/* * SPDX-License-Identifier: MIT * * Copyright (C) 2013-2024 OpenMV, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * * Minimalistic JPEG baseline encoder. * Ported from public domain JPEG writer by Jon Olick - http://jonolick.com * DCT implementation is based on Arai, Agui, and Nakajima's algorithm for scaled DCT. */ #include "imlib.h" #include "file_utils.h" // Expand 4 bits to 32 for binary to grayscale - process 4 pixels at a time #if (OMV_JPEG_CODEC_ENABLE == 1) #define JPEG_BINARY_0 0x00 #define JPEG_BINARY_1 0xFF static const uint32_t jpeg_expand[16] = { 0x00000000, 0x000000ff, 0x0000ff00, 0x0000ffff, 0x00ff0000, 0x00ff00ff, 0x00ffff00, 0x00ffffff, 0xff000000, 0xff0000ff, 0xff00ff00, 0xff00ffff, 0xffff0000, 0xffff00ff, 0xffffff00, 0xffffffff }; #else #define JPEG_BINARY_0 0x80 #define JPEG_BINARY_1 0x7F static const uint32_t jpeg_expand[16] = { 0x80808080, 0x8080807f, 0x80807f80, 0x80807f7f, 0x807f8080, 0x807f807f, 0x807f7f80, 0x807f7f7f, 0x7f808080, 0x7f80807f, 0x7f807f80, 0x7f807f7f, 0x7f7f8080, 0x7f7f807f, 0x7f7f7f80, 0x7f7f7f7f }; #endif void jpeg_get_mcu(image_t *src, int x_offset, int y_offset, int dx, int dy, int8_t *Y0, int8_t *CB, int8_t *CR) { switch (src->pixfmt) { case PIXFORMAT_BINARY: { if ((dx != JPEG_MCU_W) || (dy != JPEG_MCU_H)) { // partial MCU, fill with 0's to start memset(Y0, 0, JPEG_444_GS_MCU_SIZE); } for (int y = y_offset, yy = y + dy; y < yy; y++) { uint32_t *rp = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(src, y); uint8_t pixels = rp[x_offset >> UINT32_T_SHIFT] >> (x_offset & UINT32_T_MASK); if (dx == JPEG_MCU_W) { *((uint32_t *) Y0) = jpeg_expand[pixels & 0xf]; *(((uint32_t *) Y0) + 1) = jpeg_expand[pixels >> 4]; } else if (dx >= 4) { *((uint32_t *) Y0) = jpeg_expand[pixels & 0xf]; if (dx >= 6) { *(((uint16_t *) Y0) + 2) = jpeg_expand[pixels >> 4]; if (dx & 1) { Y0[6] = (pixels & 0x40) ? JPEG_BINARY_1 : JPEG_BINARY_0; } } else if (dx & 1) { Y0[4] = (pixels & 0x10) ? JPEG_BINARY_1 : JPEG_BINARY_0; } } else if (dx >= 2) { *((uint16_t *) Y0) = jpeg_expand[pixels & 0x3]; if (dx & 1) { Y0[2] = (pixels & 0x4) ? JPEG_BINARY_1 : JPEG_BINARY_0; } } else { *Y0 = (pixels & 0x1) ? JPEG_BINARY_1 : JPEG_BINARY_0; } Y0 += JPEG_MCU_W; } break; } case PIXFORMAT_GRAYSCALE: { if ((dx != JPEG_MCU_W) || (dy != JPEG_MCU_H)) { // partial MCU, fill with 0's to start memset(Y0, 0, JPEG_444_GS_MCU_SIZE); } for (int y = y_offset, yy = y + dy; y < yy; y++) { uint8_t *rp = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(src, y) + x_offset; #if (OMV_JPEG_CODEC_ENABLE == 0) if (dx == JPEG_MCU_W) { *((uint32_t *) Y0) = *((uint32_t *) rp) ^ 0x80808080; *(((uint32_t *) Y0) + 1) = *(((uint32_t *) rp) + 1) ^ 0x80808080; } else if (dx >= 4) { *((uint32_t *) Y0) = *((uint32_t *) rp) ^ 0x80808080; if (dx >= 6) { *(((uint16_t *) Y0) + 2) = *(((uint16_t *) rp) + 2) ^ 0x8080; if (dx & 1) { Y0[6] = rp[6] ^ 0x80; } } else if (dx & 1) { Y0[4] = rp[4] ^ 0x80; } } else if (dx >= 2) { *((uint16_t *) Y0) = *((uint16_t *) rp) ^ 0x8080; if (dx & 1) { Y0[2] = rp[2] ^ 0x80; } } else{ *Y0 = *rp ^ 0x80; } #else if (dx == JPEG_MCU_W) { *((uint32_t *) Y0) = *((uint32_t *) rp); *(((uint32_t *) Y0) + 1) = *(((uint32_t *) rp) + 1); } else if (dx >= 4) { *((uint32_t *) Y0) = *((uint32_t *) rp); if (dx >= 6) { *(((uint16_t *) Y0) + 2) = *(((uint16_t *) rp) + 2); if (dx & 1) { Y0[6] = rp[6]; } } else if (dx & 1) { Y0[4] = rp[4]; } } else if (dx >= 2) { *((uint16_t *) Y0) = *((uint16_t *) rp); if (dx & 1) { Y0[2] = rp[2]; } } else{ *Y0 = *rp; } #endif Y0 += JPEG_MCU_W; } break; } case PIXFORMAT_RGB565: { if ((dx != JPEG_MCU_W) || (dy != JPEG_MCU_H)) { // partial MCU, fill with 0's to start memset(Y0, 0, JPEG_444_GS_MCU_SIZE); memset(CB, 0, JPEG_444_GS_MCU_SIZE); memset(CR, 0, JPEG_444_GS_MCU_SIZE); } for (int y = y_offset, yy = y + dy, index = 0; y < yy; y++) { uint32_t *rp = (uint32_t *) (IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src, y) + x_offset); for (int x = 0, xx = dx - 1; x < xx; x += 2, index += 2) { int pixels = *rp++; int r_pixels = ((pixels >> 8) & 0xf800f8) | ((pixels >> 13) & 0x70007); int g_pixels = ((pixels >> 3) & 0xfc00fc) | ((pixels >> 9) & 0x30003); int b_pixels = ((pixels << 3) & 0xf800f8) | ((pixels >> 2) & 0x70007); int y = ((r_pixels * 38) + (g_pixels * 75) + (b_pixels * 15)) >> 7; #if (OMV_JPEG_CODEC_ENABLE == 0) y ^= 0x800080; #endif Y0[index] = y, Y0[index + 1] = y >> 16; int u = __SSUB16(b_pixels * 64, (r_pixels * 21) + (g_pixels * 43)) >> 7; #if (OMV_JPEG_CODEC_ENABLE == 1) u ^= 0x800080; #endif CB[index] = u, CB[index + 1] = u >> 16; int v = __SSUB16(r_pixels * 64, (g_pixels * 54) + (b_pixels * 10)) >> 7; #if (OMV_JPEG_CODEC_ENABLE == 1) v ^= 0x800080; #endif CR[index] = v, CR[index + 1] = v >> 16; } if (dx & 1) { int pixel = *((uint16_t *) rp); int r = COLOR_RGB565_TO_R8(pixel); int g = COLOR_RGB565_TO_G8(pixel); int b = COLOR_RGB565_TO_B8(pixel); int y0 = COLOR_RGB888_TO_Y(r, g, b); #if (OMV_JPEG_CODEC_ENABLE == 0) y0 ^= 0x80; #endif Y0[index] = y0; int cb = COLOR_RGB888_TO_U(r, g, b); #if (OMV_JPEG_CODEC_ENABLE == 1) cb ^= 0x80; #endif CB[index] = cb; int cr = COLOR_RGB888_TO_V(r, g, b); #if (OMV_JPEG_CODEC_ENABLE == 1) cr ^= 0x80; #endif CR[index++] = cr; } index += JPEG_MCU_W - dx; } break; } case PIXFORMAT_YUV_ANY: { if ((dx != JPEG_MCU_W) || (dy != JPEG_MCU_H)) { // partial MCU, fill with 0's to start memset(Y0, 0, JPEG_444_GS_MCU_SIZE); memset(CB, 0, JPEG_444_GS_MCU_SIZE); memset(CR, 0, JPEG_444_GS_MCU_SIZE); } int shift = (src->pixfmt == PIXFORMAT_YUV422) ? 24 : 8; for (int y = y_offset, yy = y + dy, index = 0; y < yy; y++) { uint32_t *rp = (uint32_t *) (IMAGE_COMPUTE_YUV_PIXEL_ROW_PTR(src, y) + x_offset); for (int x = 0, xx = dx - 1; x < xx; x += 2, index += 2) { int pixels = *rp++; #if (OMV_JPEG_CODEC_ENABLE == 0) pixels ^= 0x80808080; #endif Y0[index] = pixels, Y0[index + 1] = pixels >> 16; int cb = pixels >> shift; CB[index] = cb, CB[index + 1] = cb; int cr = pixels >> (32 - shift); CR[index] = cr, CR[index + 1] = cr; } if (dx & 1) { int pixel = *((uint16_t *) rp); #if (OMV_JPEG_CODEC_ENABLE == 0) pixel ^= 0x8080; #endif Y0[index] = pixel; if (index % JPEG_MCU_W) { if (shift == 8) { CR[index] = CR[index - 1]; CB[index++] = pixel >> 8; } else { CB[index] = CB[index - 1]; CR[index++] = pixel >> 8; } } else { if (shift == 8) { CB[index] = pixel >> 8; #if (OMV_JPEG_CODEC_ENABLE == 0) CR[index++] = 0; #else CR[index++] = 0x80; #endif } else { #if (OMV_JPEG_CODEC_ENABLE == 0) CB[index] = 0; #else CB[index] = 0x80; #endif CR[index++] = pixel >> 8; } } } index += JPEG_MCU_W - dx; } break; } case PIXFORMAT_BAYER_ANY: { if ((dx != JPEG_MCU_W) || (dy != JPEG_MCU_H)) { // partial MCU, fill with 0's to start memset(Y0, 0, JPEG_444_GS_MCU_SIZE); memset(CB, 0, JPEG_444_GS_MCU_SIZE); memset(CR, 0, JPEG_444_GS_MCU_SIZE); } rectangle_t roi = { .x = x_offset, .y = y_offset, .w = dx, .h = dy }; imlib_debayer_ycbcr(src, &roi, Y0, CB, CR); break; } } } #if (OMV_JPEG_CODEC_ENABLE == 0) // Software JPEG implementation. #define FIX_0_382683433 ((int32_t) 98) #define FIX_0_541196100 ((int32_t) 139) #define FIX_0_707106781 ((int32_t) 181) #define FIX_1_306562965 ((int32_t) 334) #define DESCALE(x, y) (x >> y) #define MULTIPLY(x, y) DESCALE((x) * (y), 8) typedef struct { int idx; int length; uint8_t *buf; uint32_t bitb; uint32_t bitc; bool realloc; bool overflow; } jpeg_buf_t; // Quantization tables static float fdtbl_Y[64], fdtbl_UV[64]; static uint8_t YTable[64], UVTable[64]; static const uint8_t s_jpeg_ZigZag[] = { 0, 1, 5, 6, 14, 15, 27, 28, 2, 4, 7, 13, 16, 26, 29, 42, 3, 8, 12, 17, 25, 30, 41, 43, 9, 11, 18, 24, 31, 40, 44, 53, 10, 19, 23, 32, 39, 45, 52, 54, 20, 22, 33, 38, 46, 51, 55, 60, 21, 34, 37, 47, 50, 56, 59, 61, 35, 36, 48, 49, 57, 58, 62, 63 }; static const uint8_t YQT[] = { 16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55, 14, 13, 16, 24, 40, 57, 69, 56, 14, 17, 22, 29, 51, 87, 80, 62, 18, 22, 37, 56, 68, 109, 103, 77, 24, 35, 55, 64, 81, 104, 113, 92, 49, 64, 78, 87, 103, 121, 120, 101, 72, 92, 95, 98, 112, 100, 103, 99 }; static const uint8_t UVQT[] = { 17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99, 99, 99, 99, 24, 26, 56, 99, 99, 99, 99, 99, 47, 66, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99 }; static const float aasf[] = { 1.0f, 1.387039845f, 1.306562965f, 1.175875602f, 1.0f, 0.785694958f, 0.541196100f, 0.275899379f }; static const uint8_t std_dc_luminance_nrcodes[] = {0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}; static const uint8_t std_dc_luminance_values[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; static const uint8_t std_ac_luminance_nrcodes[] = {0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d}; static const uint8_t std_ac_luminance_values[] = { 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa }; static const uint8_t std_dc_chrominance_nrcodes[] = {0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}; static const uint8_t std_dc_chrominance_values[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; static const uint8_t std_ac_chrominance_nrcodes[] = {0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77}; static const uint8_t std_ac_chrominance_values[] = { 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa }; // Huffman tables static const uint16_t YDC_HT[12][2] = { {0, 2}, {2, 3}, {3, 3}, {4, 3}, {5, 3}, {6, 3}, {14, 4}, {30, 5}, {62, 6}, {126, 7}, {254, 8}, {510, 9}, }; static const uint16_t UVDC_HT[12][2] = { {0, 2}, {1, 2}, {2, 2}, {6, 3}, {14, 4}, {30, 5}, {62, 6}, {126, 7}, {254, 8}, {510, 9}, {1022, 10}, {2046, 11}, }; static const uint16_t YAC_HT[256][2] = { {0x000A, 0x0004}, {0x0000, 0x0002}, {0x0001, 0x0002}, {0x0004, 0x0003}, {0x000B, 0x0004}, {0x001A, 0x0005}, {0x0078, 0x0007}, {0x00F8, 0x0008}, {0x03F6, 0x000A}, {0xFF82, 0x0010}, {0xFF83, 0x0010}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x000C, 0x0004}, {0x001B, 0x0005}, {0x0079, 0x0007}, {0x01F6, 0x0009}, {0x07F6, 0x000B}, {0xFF84, 0x0010}, {0xFF85, 0x0010}, {0xFF86, 0x0010}, {0xFF87, 0x0010}, {0xFF88, 0x0010}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x001C, 0x0005}, {0x00F9, 0x0008}, {0x03F7, 0x000A}, {0x0FF4, 0x000C}, {0xFF89, 0x0010}, {0xFF8A, 0x0010}, {0xFF8B, 0x0010}, {0xFF8C, 0x0010}, {0xFF8D, 0x0010}, {0xFF8E, 0x0010}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x003A, 0x0006}, {0x01F7, 0x0009}, {0x0FF5, 0x000C}, {0xFF8F, 0x0010}, {0xFF90, 0x0010}, {0xFF91, 0x0010}, {0xFF92, 0x0010}, {0xFF93, 0x0010}, {0xFF94, 0x0010}, {0xFF95, 0x0010}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x003B, 0x0006}, {0x03F8, 0x000A}, {0xFF96, 0x0010}, {0xFF97, 0x0010}, {0xFF98, 0x0010}, {0xFF99, 0x0010}, {0xFF9A, 0x0010}, {0xFF9B, 0x0010}, {0xFF9C, 0x0010}, {0xFF9D, 0x0010}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x007A, 0x0007}, {0x07F7, 0x000B}, {0xFF9E, 0x0010}, {0xFF9F, 0x0010}, {0xFFA0, 0x0010}, {0xFFA1, 0x0010}, {0xFFA2, 0x0010}, {0xFFA3, 0x0010}, {0xFFA4, 0x0010}, {0xFFA5, 0x0010}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x007B, 0x0007}, {0x0FF6, 0x000C}, {0xFFA6, 0x0010}, {0xFFA7, 0x0010}, {0xFFA8, 0x0010}, {0xFFA9, 0x0010}, {0xFFAA, 0x0010}, {0xFFAB, 0x0010}, {0xFFAC, 0x0010}, {0xFFAD, 0x0010}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x00FA, 0x0008}, {0x0FF7, 0x000C}, {0xFFAE, 0x0010}, {0xFFAF, 0x0010}, {0xFFB0, 0x0010}, {0xFFB1, 0x0010}, {0xFFB2, 0x0010}, {0xFFB3, 0x0010}, {0xFFB4, 0x0010}, {0xFFB5, 0x0010}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x01F8, 0x0009}, {0x7FC0, 0x000F}, {0xFFB6, 0x0010}, {0xFFB7, 0x0010}, {0xFFB8, 0x0010}, {0xFFB9, 0x0010}, {0xFFBA, 0x0010}, {0xFFBB, 0x0010}, {0xFFBC, 0x0010}, {0xFFBD, 0x0010}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x01F9, 0x0009}, {0xFFBE, 0x0010}, {0xFFBF, 0x0010}, {0xFFC0, 0x0010}, {0xFFC1, 0x0010}, {0xFFC2, 0x0010}, {0xFFC3, 0x0010}, {0xFFC4, 0x0010}, {0xFFC5, 0x0010}, {0xFFC6, 0x0010}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x01FA, 0x0009}, {0xFFC7, 0x0010}, {0xFFC8, 0x0010}, {0xFFC9, 0x0010}, {0xFFCA, 0x0010}, {0xFFCB, 0x0010}, {0xFFCC, 0x0010}, {0xFFCD, 0x0010}, {0xFFCE, 0x0010}, {0xFFCF, 0x0010}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x03F9, 0x000A}, {0xFFD0, 0x0010}, {0xFFD1, 0x0010}, {0xFFD2, 0x0010}, {0xFFD3, 0x0010}, {0xFFD4, 0x0010}, {0xFFD5, 0x0010}, {0xFFD6, 0x0010}, {0xFFD7, 0x0010}, {0xFFD8, 0x0010}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x03FA, 0x000A}, {0xFFD9, 0x0010}, {0xFFDA, 0x0010}, {0xFFDB, 0x0010}, {0xFFDC, 0x0010}, {0xFFDD, 0x0010}, {0xFFDE, 0x0010}, {0xFFDF, 0x0010}, {0xFFE0, 0x0010}, {0xFFE1, 0x0010}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x07F8, 0x000B}, {0xFFE2, 0x0010}, {0xFFE3, 0x0010}, {0xFFE4, 0x0010}, {0xFFE5, 0x0010}, {0xFFE6, 0x0010}, {0xFFE7, 0x0010}, {0xFFE8, 0x0010}, {0xFFE9, 0x0010}, {0xFFEA, 0x0010}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0xFFEB, 0x0010}, {0xFFEC, 0x0010}, {0xFFED, 0x0010}, {0xFFEE, 0x0010}, {0xFFEF, 0x0010}, {0xFFF0, 0x0010}, {0xFFF1, 0x0010}, {0xFFF2, 0x0010}, {0xFFF3, 0x0010}, {0xFFF4, 0x0010}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x07F9, 0x000B}, {0xFFF5, 0x0010}, {0xFFF6, 0x0010}, {0xFFF7, 0x0010}, {0xFFF8, 0x0010}, {0xFFF9, 0x0010}, {0xFFFA, 0x0010}, {0xFFFB, 0x0010}, {0xFFFC, 0x0010}, {0xFFFD, 0x0010}, {0xFFFE, 0x0010}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, }; static const uint16_t UVAC_HT[256][2] = { {0x0000, 0x0002}, {0x0001, 0x0002}, {0x0004, 0x0003}, {0x000A, 0x0004}, {0x0018, 0x0005}, {0x0019, 0x0005}, {0x0038, 0x0006}, {0x0078, 0x0007}, {0x01F4, 0x0009}, {0x03F6, 0x000A}, {0x0FF4, 0x000C}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x000B, 0x0004}, {0x0039, 0x0006}, {0x00F6, 0x0008}, {0x01F5, 0x0009}, {0x07F6, 0x000B}, {0x0FF5, 0x000C}, {0xFF88, 0x0010}, {0xFF89, 0x0010}, {0xFF8A, 0x0010}, {0xFF8B, 0x0010}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x001A, 0x0005}, {0x00F7, 0x0008}, {0x03F7, 0x000A}, {0x0FF6, 0x000C}, {0x7FC2, 0x000F}, {0xFF8C, 0x0010}, {0xFF8D, 0x0010}, {0xFF8E, 0x0010}, {0xFF8F, 0x0010}, {0xFF90, 0x0010}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x001B, 0x0005}, {0x00F8, 0x0008}, {0x03F8, 0x000A}, {0x0FF7, 0x000C}, {0xFF91, 0x0010}, {0xFF92, 0x0010}, {0xFF93, 0x0010}, {0xFF94, 0x0010}, {0xFF95, 0x0010}, {0xFF96, 0x0010}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x003A, 0x0006}, {0x01F6, 0x0009}, {0xFF97, 0x0010}, {0xFF98, 0x0010}, {0xFF99, 0x0010}, {0xFF9A, 0x0010}, {0xFF9B, 0x0010}, {0xFF9C, 0x0010}, {0xFF9D, 0x0010}, {0xFF9E, 0x0010}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x003B, 0x0006}, {0x03F9, 0x000A}, {0xFF9F, 0x0010}, {0xFFA0, 0x0010}, {0xFFA1, 0x0010}, {0xFFA2, 0x0010}, {0xFFA3, 0x0010}, {0xFFA4, 0x0010}, {0xFFA5, 0x0010}, {0xFFA6, 0x0010}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0079, 0x0007}, {0x07F7, 0x000B}, {0xFFA7, 0x0010}, {0xFFA8, 0x0010}, {0xFFA9, 0x0010}, {0xFFAA, 0x0010}, {0xFFAB, 0x0010}, {0xFFAC, 0x0010}, {0xFFAD, 0x0010}, {0xFFAE, 0x0010}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x007A, 0x0007}, {0x07F8, 0x000B}, {0xFFAF, 0x0010}, {0xFFB0, 0x0010}, {0xFFB1, 0x0010}, {0xFFB2, 0x0010}, {0xFFB3, 0x0010}, {0xFFB4, 0x0010}, {0xFFB5, 0x0010}, {0xFFB6, 0x0010}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x00F9, 0x0008}, {0xFFB7, 0x0010}, {0xFFB8, 0x0010}, {0xFFB9, 0x0010}, {0xFFBA, 0x0010}, {0xFFBB, 0x0010}, {0xFFBC, 0x0010}, {0xFFBD, 0x0010}, {0xFFBE, 0x0010}, {0xFFBF, 0x0010}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x01F7, 0x0009}, {0xFFC0, 0x0010}, {0xFFC1, 0x0010}, {0xFFC2, 0x0010}, {0xFFC3, 0x0010}, {0xFFC4, 0x0010}, {0xFFC5, 0x0010}, {0xFFC6, 0x0010}, {0xFFC7, 0x0010}, {0xFFC8, 0x0010}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x01F8, 0x0009}, {0xFFC9, 0x0010}, {0xFFCA, 0x0010}, {0xFFCB, 0x0010}, {0xFFCC, 0x0010}, {0xFFCD, 0x0010}, {0xFFCE, 0x0010}, {0xFFCF, 0x0010}, {0xFFD0, 0x0010}, {0xFFD1, 0x0010}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x01F9, 0x0009}, {0xFFD2, 0x0010}, {0xFFD3, 0x0010}, {0xFFD4, 0x0010}, {0xFFD5, 0x0010}, {0xFFD6, 0x0010}, {0xFFD7, 0x0010}, {0xFFD8, 0x0010}, {0xFFD9, 0x0010}, {0xFFDA, 0x0010}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x01FA, 0x0009}, {0xFFDB, 0x0010}, {0xFFDC, 0x0010}, {0xFFDD, 0x0010}, {0xFFDE, 0x0010}, {0xFFDF, 0x0010}, {0xFFE0, 0x0010}, {0xFFE1, 0x0010}, {0xFFE2, 0x0010}, {0xFFE3, 0x0010}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x07F9, 0x000B}, {0xFFE4, 0x0010}, {0xFFE5, 0x0010}, {0xFFE6, 0x0010}, {0xFFE7, 0x0010}, {0xFFE8, 0x0010}, {0xFFE9, 0x0010}, {0xFFEA, 0x0010}, {0xFFEB, 0x0010}, {0xFFEC, 0x0010}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x3FE0, 0x000E}, {0xFFED, 0x0010}, {0xFFEE, 0x0010}, {0xFFEF, 0x0010}, {0xFFF0, 0x0010}, {0xFFF1, 0x0010}, {0xFFF2, 0x0010}, {0xFFF3, 0x0010}, {0xFFF4, 0x0010}, {0xFFF5, 0x0010}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x03FA, 0x000A}, {0x7FC3, 0x000F}, {0xFFF6, 0x0010}, {0xFFF7, 0x0010}, {0xFFF8, 0x0010}, {0xFFF9, 0x0010}, {0xFFFA, 0x0010}, {0xFFFB, 0x0010}, {0xFFFC, 0x0010}, {0xFFFD, 0x0010}, {0xFFFE, 0x0010}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, {0x0000, 0x0000}, }; // Check if the output buffer is nearly full and allocate more space // if needed. If realloc is disabled, return true to halt the encoding. static int jpeg_check_highwater(jpeg_buf_t *jpeg_buf) { if ((jpeg_buf->idx + 1) >= jpeg_buf->length - 256) { if (jpeg_buf->realloc == false) { // Can't realloc buffer jpeg_buf->overflow = true; return 1; } jpeg_buf->length += 1024; jpeg_buf->buf = m_realloc(jpeg_buf->buf, jpeg_buf->length); } return 0; } static void jpeg_put_char(jpeg_buf_t *jpeg_buf, char c) { if ((jpeg_buf->idx + 1) >= jpeg_buf->length) { if (jpeg_buf->realloc == false) { // Can't realloc buffer jpeg_buf->overflow = true; return; } jpeg_buf->length += 1024; jpeg_buf->buf = m_realloc(jpeg_buf->buf, jpeg_buf->length); } jpeg_buf->buf[jpeg_buf->idx++] = c; } static void jpeg_put_bytes(jpeg_buf_t *jpeg_buf, const void *data, int size) { if ((jpeg_buf->idx + size) >= jpeg_buf->length) { if (jpeg_buf->realloc == false) { // Can't realloc buffer jpeg_buf->overflow = true; return; } jpeg_buf->length += 1024; jpeg_buf->buf = m_realloc(jpeg_buf->buf, jpeg_buf->length); } memcpy(jpeg_buf->buf + jpeg_buf->idx, data, size); jpeg_buf->idx += size; } static inline void jpeg_write_bits(jpeg_buf_t *jpeg_buf, const uint16_t *bs) { jpeg_buf->bitc += bs[1]; jpeg_buf->bitb |= bs[0] << (24 - jpeg_buf->bitc); while (jpeg_buf->bitc > 7) { uint8_t c = (jpeg_buf->bitb >> 16) & 255; jpeg_put_char(jpeg_buf, c); if (c == 255) { jpeg_put_char(jpeg_buf, 0); } jpeg_buf->bitb <<= 8; jpeg_buf->bitc -= 8; } } //Huffman-encoded magnitude value static inline void jpeg_calc_bits(int val, uint16_t bits[2]) { int t1 = val; if (val < 0) { t1 = -val; val = val - 1; } bits[1] = 32 - __CLZ(t1); bits[0] = val & ((1 << bits[1]) - 1); } static int jpeg_processDU(jpeg_buf_t *jpeg_buf, int8_t *CDU, float *fdtbl, int DC, const uint16_t (*HTDC)[2], const uint16_t (*HTAC)[2]) { int DU[64]; int DUQ[64]; int z1, z2, z3, z4, z5, z11, z13; int t0, t1, t2, t3, t4, t5, t6, t7, t10, t11, t12, t13; const uint16_t EOB[2] = { HTAC[0x00][0], HTAC[0x00][1] }; const uint16_t M16zeroes[2] = { HTAC[0xF0][0], HTAC[0xF0][1] }; // DCT rows for (int i = 8, *p = DU; i > 0; i--, p += 8, CDU += 8) { t0 = CDU[0] + CDU[7]; t1 = CDU[1] + CDU[6]; t2 = CDU[2] + CDU[5]; t3 = CDU[3] + CDU[4]; t7 = CDU[0] - CDU[7]; t6 = CDU[1] - CDU[6]; t5 = CDU[2] - CDU[5]; t4 = CDU[3] - CDU[4]; // Even part t10 = t0 + t3; t13 = t0 - t3; t11 = t1 + t2; t12 = t1 - t2; z1 = MULTIPLY(t12 + t13, FIX_0_707106781); // c4 p[0] = t10 + t11; p[4] = t10 - t11; p[2] = t13 + z1; p[6] = t13 - z1; // Odd part t10 = t4 + t5;// phase 2 t11 = t5 + t6; t12 = t6 + t7; // The rotator is modified from fig 4-8 to avoid extra negations. z5 = MULTIPLY(t10 - t12, FIX_0_382683433); // c6 z2 = MULTIPLY(t10, FIX_0_541196100) + z5; // 1.306562965f-c6 z4 = MULTIPLY(t12, FIX_1_306562965) + z5; // 1.306562965f+c6 z3 = MULTIPLY(t11, FIX_0_707106781); // c4 z11 = t7 + z3; // phase 5 z13 = t7 - z3; p[5] = z13 + z2;// phase 6 p[3] = z13 - z2; p[1] = z11 + z4; p[7] = z11 - z4; } // DCT columns for (int i = 8, *p = DU; i > 0; i--, p++) { t0 = p[0] + p[56]; t1 = p[8] + p[48]; t2 = p[16] + p[40]; t3 = p[24] + p[32]; t7 = p[0] - p[56]; t6 = p[8] - p[48]; t5 = p[16] - p[40]; t4 = p[24] - p[32]; // Even part t10 = t0 + t3; // phase 2 t13 = t0 - t3; t11 = t1 + t2; t12 = t1 - t2; z1 = MULTIPLY(t12 + t13, FIX_0_707106781); // c4 p[0] = t10 + t11; // phase 3 p[32] = t10 - t11; p[16] = t13 + z1; // phase 5 p[48] = t13 - z1; // Odd part t10 = t4 + t5; // phase 2 t11 = t5 + t6; t12 = t6 + t7; // The rotator is modified from fig 4-8 to avoid extra negations. z5 = MULTIPLY(t10 - t12, FIX_0_382683433); // c6 z2 = MULTIPLY(t10, FIX_0_541196100) + z5; // 1.306562965f-c6 z4 = MULTIPLY(t12, FIX_1_306562965) + z5; // 1.306562965f+c6 z3 = MULTIPLY(t11, FIX_0_707106781); // c4 z11 = t7 + z3; // phase 5 z13 = t7 - z3; p[40] = z13 + z2;// phase 6 p[24] = z13 - z2; p[8] = z11 + z4; p[56] = z11 - z4; } // first non-zero element in reverse order int end0pos = 0; // Quantize/descale/zigzag the coefficients for (int i = 0; i < 64; ++i) { DUQ[s_jpeg_ZigZag[i]] = fast_roundf(DU[i] * fdtbl[i]); if (s_jpeg_ZigZag[i] > end0pos && DUQ[s_jpeg_ZigZag[i]]) { end0pos = s_jpeg_ZigZag[i]; } } if (jpeg_check_highwater(jpeg_buf)) { // check if we're getting close to the end of the buffer return 0; // stop encoding, we've run out of space } // Encode DC int diff = DUQ[0] - DC; if (diff == 0) { jpeg_write_bits(jpeg_buf, HTDC[0]); } else { uint16_t bits[2]; jpeg_calc_bits(diff, bits); jpeg_write_bits(jpeg_buf, HTDC[bits[1]]); jpeg_write_bits(jpeg_buf, bits); } // Encode ACs if (end0pos == 0) { jpeg_write_bits(jpeg_buf, EOB); return DUQ[0]; } for (int i = 1; i <= end0pos; ++i) { int startpos = i; for (; DUQ[i] == 0 && i <= end0pos ; ++i) { } int nrzeroes = i - startpos; if (nrzeroes >= 16) { int lng = nrzeroes >> 4; for (int nrmarker = 1; nrmarker <= lng; ++nrmarker) { jpeg_write_bits(jpeg_buf, M16zeroes); } nrzeroes &= 15; } uint16_t bits[2]; jpeg_calc_bits(DUQ[i], bits); jpeg_write_bits(jpeg_buf, HTAC[(nrzeroes << 4) + bits[1]]); jpeg_write_bits(jpeg_buf, bits); } if (end0pos != 63) { jpeg_write_bits(jpeg_buf, EOB); } return DUQ[0]; } static void jpeg_init(int quality) { static int q = 0; quality = quality < 50 ? 5000 / quality : 200 - quality * 2; // If quality changed, update quantization matrix if (q != quality) { q = quality; for (int i = 0; i < 64; ++i) { int yti = (YQT[i] * quality + 50) / 100; YTable[s_jpeg_ZigZag[i]] = yti < 1 ? 1 : yti > 255 ? 255 : yti; int uvti = (UVQT[i] * quality + 50) / 100; UVTable[s_jpeg_ZigZag[i]] = uvti < 1 ? 1 : uvti > 255 ? 255 : uvti; } for (int r = 0, k = 0; r < 8; ++r) { for (int c = 0; c < 8; ++c, ++k) { fdtbl_Y[k] = 1.0f / (aasf[r] * aasf[c] * YTable [s_jpeg_ZigZag[k]] * 8.0f); fdtbl_UV[k] = 1.0f / (aasf[r] * aasf[c] * UVTable[s_jpeg_ZigZag[k]] * 8.0f); } } } } static void jpeg_write_headers(jpeg_buf_t *jpeg_buf, int w, int h, int bpp, jpeg_subsampling_t subsampling) { // Number of components (1 or 3) uint8_t nr_comp = (bpp == 1)? 1 : 3; // JPEG headers uint8_t m_soi[] = { 0xFF, 0xD8 // SOI }; uint8_t m_app0[] = { 0xFF, 0xE0, // APP0 0x00, 0x10, 'J', 'F', 'I', 'F', 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00 }; uint8_t m_dqt[] = { 0xFF, 0xDB, // DQT (bpp * 65 + 2) >> 8, // Header length MSB (bpp * 65 + 2) & 0xFF, // Header length LSB }; uint8_t m_sof0[] = { 0xFF, 0xC0, // SOF0 (nr_comp * 3 + 8) >> 8, // Header length MSB (nr_comp * 3 + 8) & 0xFF, // Header length LSB 0x08, // Bits per sample h >> 8, h & 0xFF, // Height w >> 8, w & 0xFF, // Width nr_comp, // Number of components }; uint8_t m_dht[] = { 0xFF, 0xC4, // DHT (bpp * 208 + 2) >> 8, // Header length MSB (bpp * 208 + 2) & 0xFF, // Header length LSB }; uint8_t m_sos[] = { 0xFF, 0xDA, // SOS (nr_comp * 2 + 6) >> 8, // Header length MSB (nr_comp * 2 + 6) & 0xFF, // Header length LSB nr_comp, // Number of components }; // Write SOI marker jpeg_put_bytes(jpeg_buf, m_soi, sizeof(m_soi)); // Write APP0 marker jpeg_put_bytes(jpeg_buf, m_app0, sizeof(m_app0)); // Write DQT marker jpeg_put_bytes(jpeg_buf, m_dqt, sizeof(m_dqt)); // Write Y quantization table (index, table) jpeg_put_char(jpeg_buf, 0); jpeg_put_bytes(jpeg_buf, YTable, sizeof(YTable)); if (bpp > 1) { // Write UV quantization table (index, table) jpeg_put_char(jpeg_buf, 1); jpeg_put_bytes(jpeg_buf, UVTable, sizeof(UVTable)); } // Write SOF0 marker jpeg_put_bytes(jpeg_buf, m_sof0, sizeof(m_sof0)); for (int i = 0; i < nr_comp; i++) { // Component ID, HV sampling, q table idx jpeg_put_bytes(jpeg_buf, (uint8_t [3]) {i + 1, (i == 0 && bpp == 2)? subsampling:0x11, (i > 0)}, 3); } // Write DHT marker jpeg_put_bytes(jpeg_buf, m_dht, sizeof(m_dht)); // Write DHT-YDC jpeg_put_char(jpeg_buf, 0x00); jpeg_put_bytes(jpeg_buf, std_dc_luminance_nrcodes + 1, sizeof(std_dc_luminance_nrcodes) - 1); jpeg_put_bytes(jpeg_buf, std_dc_luminance_values, sizeof(std_dc_luminance_values)); // Write DHT-YAC jpeg_put_char(jpeg_buf, 0x10); jpeg_put_bytes(jpeg_buf, std_ac_luminance_nrcodes + 1, sizeof(std_ac_luminance_nrcodes) - 1); jpeg_put_bytes(jpeg_buf, std_ac_luminance_values, sizeof(std_ac_luminance_values)); if (bpp > 1) { // Write DHT-UDC jpeg_put_char(jpeg_buf, 0x01); jpeg_put_bytes(jpeg_buf, std_dc_chrominance_nrcodes + 1, sizeof(std_dc_chrominance_nrcodes) - 1); jpeg_put_bytes(jpeg_buf, std_dc_chrominance_values, sizeof(std_dc_chrominance_values)); // Write DHT-UAC jpeg_put_char(jpeg_buf, 0x11); jpeg_put_bytes(jpeg_buf, std_ac_chrominance_nrcodes + 1, sizeof(std_ac_chrominance_nrcodes) - 1); jpeg_put_bytes(jpeg_buf, std_ac_chrominance_values, sizeof(std_ac_chrominance_values)); } // Write SOS marker jpeg_put_bytes(jpeg_buf, m_sos, sizeof(m_sos)); for (int i = 0; i < nr_comp; i++) { jpeg_put_bytes(jpeg_buf, (uint8_t [2]) {i + 1, (i == 0)? 0x00:0x11}, 2); } // Spectral selection jpeg_put_bytes(jpeg_buf, (uint8_t [3]) {0x00, 0x3F, 0x0}, 3); } bool jpeg_compress(image_t *src, image_t *dst, int quality, bool realloc, jpeg_subsampling_t subsampling) { if (!dst->data) { uint32_t size = 0; dst->data = fb_alloc_all(&size, FB_ALLOC_PREFER_SIZE | FB_ALLOC_CACHE_ALIGN); dst->size = IMLIB_IMAGE_MAX_SIZE(size); } if (src->is_compressed) { return true; } // JPEG buffer jpeg_buf_t jpeg_buf = { .idx = 0, .buf = dst->pixels, .length = dst->size, .bitc = 0, .bitb = 0, .realloc = realloc, .overflow = false, }; // Initialize quantization tables jpeg_init(quality); if (src->is_color) { if (subsampling == JPEG_SUBSAMPLING_AUTO) { if (quality <= 35) { subsampling = JPEG_SUBSAMPLING_420; } else if (quality < 60) { subsampling = JPEG_SUBSAMPLING_422; } else { subsampling = JPEG_SUBSAMPLING_444; } } } else { subsampling = JPEG_SUBSAMPLING_444; } jpeg_write_headers(&jpeg_buf, src->w, src->h, src->is_color ? 2 : 1, subsampling); int DCY = 0, DCU = 0, DCV = 0; switch (subsampling) { // Quiet GCC compiler warning (this is never reached) case JPEG_SUBSAMPLING_AUTO: { break; } case JPEG_SUBSAMPLING_444: { int8_t YDU[JPEG_444_GS_MCU_SIZE]; int8_t UDU[JPEG_444_GS_MCU_SIZE]; int8_t VDU[JPEG_444_GS_MCU_SIZE]; for (int y_offset = 0; y_offset < src->h; y_offset += JPEG_MCU_H) { int dy = IM_MIN(JPEG_MCU_H, src->h - y_offset); for (int x_offset = 0; x_offset < src->w; x_offset += JPEG_MCU_W) { int dx = IM_MIN(JPEG_MCU_W, src->w - x_offset); jpeg_get_mcu(src, x_offset, y_offset, dx, dy, YDU, UDU, VDU); DCY = jpeg_processDU(&jpeg_buf, YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); if (src->is_color) { DCU = jpeg_processDU(&jpeg_buf, UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); DCV = jpeg_processDU(&jpeg_buf, VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); } } if (jpeg_buf.overflow) { return true; } } break; } case JPEG_SUBSAMPLING_422: { // color only int8_t YDU[JPEG_444_GS_MCU_SIZE * 2]; int8_t UDU[JPEG_444_GS_MCU_SIZE * 2]; int8_t VDU[JPEG_444_GS_MCU_SIZE * 2]; int8_t UDU_avg[JPEG_444_GS_MCU_SIZE]; int8_t VDU_avg[JPEG_444_GS_MCU_SIZE]; for (int y_offset = 0; y_offset < src->h; y_offset += JPEG_MCU_H) { int dy = IM_MIN(JPEG_MCU_H, src->h - y_offset); for (int x_offset = 0; x_offset < src->w; ) { for (int i = 0; i < (JPEG_444_GS_MCU_SIZE * 2); i += JPEG_444_GS_MCU_SIZE, x_offset += JPEG_MCU_W) { int dx = IM_MIN(JPEG_MCU_W, src->w - x_offset); if (dx > 0) { jpeg_get_mcu(src, x_offset, y_offset, dx, dy, YDU + i, UDU + i, VDU + i); } else { memset(YDU + i, 0, JPEG_444_GS_MCU_SIZE); memset(UDU + i, 0, JPEG_444_GS_MCU_SIZE); memset(VDU + i, 0, JPEG_444_GS_MCU_SIZE); } DCY = jpeg_processDU(&jpeg_buf, YDU + i, fdtbl_Y, DCY, YDC_HT, YAC_HT); } // horizontal subsampling of U & V #if defined(ARM_MATH_DSP) uint32_t *UDUp0 = (uint32_t *) UDU; uint32_t *VDUp0 = (uint32_t *) VDU; uint32_t *UDUp1 = (uint32_t *) (UDU + JPEG_444_GS_MCU_SIZE); uint32_t *VDUp1 = (uint32_t *) (VDU + JPEG_444_GS_MCU_SIZE); #else int8_t *UDUp0 = UDU; int8_t *VDUp0 = VDU; int8_t *UDUp1 = UDUp0 + JPEG_444_GS_MCU_SIZE; int8_t *VDUp1 = VDUp0 + JPEG_444_GS_MCU_SIZE; #endif for (int j = 0; j < JPEG_444_GS_MCU_SIZE; j += JPEG_MCU_W) { #if defined(ARM_MATH_DSP) uint32_t UDUp0_3210 = *UDUp0++; uint32_t UDUp0_avg_32_10 = __SHADD8(UDUp0_3210, __UXTB16_RORn(UDUp0_3210, 8)); UDU_avg[j] = UDUp0_avg_32_10; UDU_avg[j + 1] = UDUp0_avg_32_10 >> 16; uint32_t UDUp0_7654 = *UDUp0++; uint32_t UDUp0_avg_76_54 = __SHADD8(UDUp0_7654, __UXTB16_RORn(UDUp0_7654, 8)); UDU_avg[j + 2] = UDUp0_avg_76_54; UDU_avg[j + 3] = UDUp0_avg_76_54 >> 16; uint32_t UDUp1_3210 = *UDUp1++; uint32_t UDUp1_avg_32_10 = __SHADD8(UDUp1_3210, __UXTB16_RORn(UDUp1_3210, 8)); UDU_avg[j + 4] = UDUp1_avg_32_10; UDU_avg[j + 5] = UDUp1_avg_32_10 >> 16; uint32_t UDUp1_7654 = *UDUp1++; uint32_t UDUp1_avg_76_54 = __SHADD8(UDUp1_7654, __UXTB16_RORn(UDUp1_7654, 8)); UDU_avg[j + 6] = UDUp1_avg_76_54; UDU_avg[j + 7] = UDUp1_avg_76_54 >> 16; uint32_t VDUp0_3210 = *VDUp0++; uint32_t VDUp0_avg_32_10 = __SHADD8(VDUp0_3210, __UXTB16_RORn(VDUp0_3210, 8)); VDU_avg[j] = VDUp0_avg_32_10; VDU_avg[j + 1] = VDUp0_avg_32_10 >> 16; uint32_t VDUp0_7654 = *VDUp0++; uint32_t VDUp0_avg_76_54 = __SHADD8(VDUp0_7654, __UXTB16_RORn(VDUp0_7654, 8)); VDU_avg[j + 2] = VDUp0_avg_76_54; VDU_avg[j + 3] = VDUp0_avg_76_54 >> 16; uint32_t VDUp1_3210 = *VDUp1++; uint32_t VDUp1_avg_32_10 = __SHADD8(VDUp1_3210, __UXTB16_RORn(VDUp1_3210, 8)); VDU_avg[j + 4] = VDUp1_avg_32_10; VDU_avg[j + 5] = VDUp1_avg_32_10 >> 16; uint32_t VDUp1_7654 = *VDUp1++; uint32_t VDUp1_avg_76_54 = __SHADD8(VDUp1_7654, __UXTB16_RORn(VDUp1_7654, 8)); VDU_avg[j + 6] = VDUp1_avg_76_54; VDU_avg[j + 7] = VDUp1_avg_76_54 >> 16; #else for (int i = 0; i < JPEG_MCU_W; i += 2) { UDU_avg[j + (i / 2)] = (UDUp0[i] + UDUp0[i + 1]) / 2; VDU_avg[j + (i / 2)] = (VDUp0[i] + VDUp0[i + 1]) / 2; UDU_avg[j + (i / 2) + (JPEG_MCU_W / 2)] = (UDUp1[i] + UDUp1[i + 1]) / 2; VDU_avg[j + (i / 2) + (JPEG_MCU_W / 2)] = (VDUp1[i] + VDUp1[i + 1]) / 2; } UDUp0 += JPEG_MCU_W; VDUp0 += JPEG_MCU_W; UDUp1 += JPEG_MCU_W; VDUp1 += JPEG_MCU_W; #endif } DCU = jpeg_processDU(&jpeg_buf, UDU_avg, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); DCV = jpeg_processDU(&jpeg_buf, VDU_avg, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); } if (jpeg_buf.overflow) { return true; } } break; } case JPEG_SUBSAMPLING_420: { // color only int8_t YDU[JPEG_444_GS_MCU_SIZE * 4]; int8_t UDU[JPEG_444_GS_MCU_SIZE * 4]; int8_t VDU[JPEG_444_GS_MCU_SIZE * 4]; int8_t UDU_avg[JPEG_444_GS_MCU_SIZE]; int8_t VDU_avg[JPEG_444_GS_MCU_SIZE]; for (int y_offset = 0; y_offset < src->h; ) { for (int x_offset = 0; x_offset < src->w; ) { for (int j = 0; j < (JPEG_444_GS_MCU_SIZE * 4); j += (JPEG_444_GS_MCU_SIZE * 2), y_offset += JPEG_MCU_H) { int dy = IM_MIN(JPEG_MCU_H, src->h - y_offset); for (int i = 0; i < (JPEG_444_GS_MCU_SIZE * 2); i += JPEG_444_GS_MCU_SIZE, x_offset += JPEG_MCU_W) { int dx = IM_MIN(JPEG_MCU_W, src->w - x_offset); if ((dx > 0) && (dy > 0)) { jpeg_get_mcu(src, x_offset, y_offset, dx, dy, YDU + i + j, UDU + i + j, VDU + i + j); } else { memset(YDU + i + j, 0, JPEG_444_GS_MCU_SIZE); memset(UDU + i + j, 0, JPEG_444_GS_MCU_SIZE); memset(VDU + i + j, 0, JPEG_444_GS_MCU_SIZE); } DCY = jpeg_processDU(&jpeg_buf, YDU + i + j, fdtbl_Y, DCY, YDC_HT, YAC_HT); } // Reset back two columns. x_offset -= (JPEG_MCU_W * 2); } // Advance to the next columns. x_offset += (JPEG_MCU_W * 2); // Reset back two rows. y_offset -= (JPEG_MCU_H * 2); // horizontal and vertical subsampling of U & V #if defined(ARM_MATH_DSP) uint32_t *UDUp = (uint32_t *) UDU; uint32_t *VDUp = (uint32_t *) VDU; #else int8_t *UDUp0 = UDU; int8_t *VDUp0 = VDU; int8_t *UDUp1 = UDUp0 + JPEG_444_GS_MCU_SIZE; int8_t *VDUp1 = VDUp0 + JPEG_444_GS_MCU_SIZE; int8_t *UDUp2 = UDUp1 + JPEG_444_GS_MCU_SIZE; int8_t *VDUp2 = VDUp1 + JPEG_444_GS_MCU_SIZE; int8_t *UDUp3 = UDUp2 + JPEG_444_GS_MCU_SIZE; int8_t *VDUp3 = VDUp2 + JPEG_444_GS_MCU_SIZE; #endif for (int j = 0, k = JPEG_444_GS_MCU_SIZE / 2; k < JPEG_444_GS_MCU_SIZE; j += JPEG_MCU_W, k += JPEG_MCU_W) { #if defined(ARM_MATH_DSP) for (int i = 0; i < 4; i++) { int index = ((i & 2) ? k : j) + ((i & 1) * 4); uint32_t UDU_r0_3210 = UDUp[i * 16]; uint32_t UDU_r0_avg_32_10 = __SHADD8(UDU_r0_3210, __UXTB16_RORn(UDU_r0_3210, 8)); uint32_t UDU_r0_7654 = UDUp[(i * 16) + 1]; uint32_t UDU_r0_avg_76_54 = __SHADD8(UDU_r0_7654, __UXTB16_RORn(UDU_r0_7654, 8)); uint32_t UDU_r1_3210 = UDUp[(i * 16) + 2]; uint32_t UDU_r1_avg_32_10 = __SHADD8(UDU_r1_3210, __UXTB16_RORn(UDU_r1_3210, 8)); uint32_t UDU_r1_7654 = UDUp[(i * 16) + 3]; uint32_t UDU_r1_avg_76_54 = __SHADD8(UDU_r1_7654, __UXTB16_RORn(UDU_r1_7654, 8)); uint32_t UDU_r0_r1_avg_32_10 = __SHADD8(UDU_r0_avg_32_10, UDU_r1_avg_32_10); UDU_avg[index] = UDU_r0_r1_avg_32_10; UDU_avg[index + 1] = UDU_r0_r1_avg_32_10 >> 16; uint32_t UDU_r0_r1_avg_76_54 = __SHADD8(UDU_r0_avg_76_54, UDU_r1_avg_76_54); UDU_avg[index + 2] = UDU_r0_r1_avg_76_54; UDU_avg[index + 3] = UDU_r0_r1_avg_76_54 >> 16; uint32_t VDU_r0_3210 = VDUp[i * 16]; uint32_t VDU_r0_avg_32_10 = __SHADD8(VDU_r0_3210, __UXTB16_RORn(VDU_r0_3210, 8)); uint32_t VDU_r0_7654 = VDUp[(i * 16) + 1]; uint32_t VDU_r0_avg_76_54 = __SHADD8(VDU_r0_7654, __UXTB16_RORn(VDU_r0_7654, 8)); uint32_t VDU_r1_3210 = VDUp[(i * 16) + 2]; uint32_t VDU_r1_avg_32_10 = __SHADD8(VDU_r1_3210, __UXTB16_RORn(VDU_r1_3210, 8)); uint32_t VDU_r1_7654 = VDUp[(i * 16) + 3]; uint32_t VDU_r1_avg_76_54 = __SHADD8(VDU_r1_7654, __UXTB16_RORn(VDU_r1_7654, 8)); uint32_t VDU_r0_r1_avg_32_10 = __SHADD8(VDU_r0_avg_32_10, VDU_r1_avg_32_10); VDU_avg[index] = VDU_r0_r1_avg_32_10; VDU_avg[index + 1] = VDU_r0_r1_avg_32_10 >> 16; uint32_t VDU_r0_r1_avg_76_54 = __SHADD8(VDU_r0_avg_76_54, VDU_r1_avg_76_54); VDU_avg[index + 2] = VDU_r0_r1_avg_76_54; VDU_avg[index + 3] = VDU_r0_r1_avg_76_54 >> 16; } UDUp += 4; VDUp += 4; #else for (int i = 0; i < JPEG_MCU_W; i += 2) { UDU_avg[j + (i / 2)] = (UDUp0[i] + UDUp0[i + 1] + UDUp0[i + JPEG_MCU_W] + UDUp0[i + 1 + JPEG_MCU_W]) / 4; VDU_avg[j + (i / 2)] = (VDUp0[i] + VDUp0[i + 1] + VDUp0[i + JPEG_MCU_W] + VDUp0[i + 1 + JPEG_MCU_W]) / 4; UDU_avg[j + (i / 2) + (JPEG_MCU_W / 2)] = (UDUp1[i] + UDUp1[i + 1] + UDUp1[i + JPEG_MCU_W] + UDUp1[i + 1 + JPEG_MCU_W]) / 4; VDU_avg[j + (i / 2) + (JPEG_MCU_W / 2)] = (VDUp1[i] + VDUp1[i + 1] + VDUp1[i + JPEG_MCU_W] + VDUp1[i + 1 + JPEG_MCU_W]) / 4; UDU_avg[k + (i / 2)] = (UDUp2[i] + UDUp2[i + 1] + UDUp2[i + JPEG_MCU_W] + UDUp2[i + 1 + JPEG_MCU_W]) / 4; VDU_avg[k + (i / 2)] = (VDUp2[i] + VDUp2[i + 1] + VDUp2[i + JPEG_MCU_W] + VDUp2[i + 1 + JPEG_MCU_W]) / 4; UDU_avg[k + (i / 2) + (JPEG_MCU_W / 2)] = (UDUp3[i] + UDUp3[i + 1] + UDUp3[i + JPEG_MCU_W] + UDUp3[i + 1 + JPEG_MCU_W]) / 4; VDU_avg[k + (i / 2) + (JPEG_MCU_W / 2)] = (VDUp3[i] + VDUp3[i + 1] + VDUp3[i + JPEG_MCU_W] + VDUp3[i + 1 + JPEG_MCU_W]) / 4; } UDUp0 += JPEG_MCU_W * 2; VDUp0 += JPEG_MCU_W * 2; UDUp1 += JPEG_MCU_W * 2; VDUp1 += JPEG_MCU_W * 2; UDUp2 += JPEG_MCU_W * 2; VDUp2 += JPEG_MCU_W * 2; UDUp3 += JPEG_MCU_W * 2; VDUp3 += JPEG_MCU_W * 2; #endif } DCU = jpeg_processDU(&jpeg_buf, UDU_avg, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); DCV = jpeg_processDU(&jpeg_buf, VDU_avg, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); } if (jpeg_buf.overflow) { return true; } // Advance to the next rows. y_offset += (JPEG_MCU_H * 2); } break; } } // Do the bit alignment of the EOI marker jpeg_write_bits(&jpeg_buf, (const uint16_t []) {0x7F, 7}); // EOI jpeg_put_char(&jpeg_buf, 0xFF); jpeg_put_char(&jpeg_buf, 0xD9); dst->size = jpeg_buf.idx; dst->data = jpeg_buf.buf; return false; } #endif // (OMV_JPEG_CODEC_ENABLE == 0) bool jpeg_is_valid(image_t *img) { uint8_t *p = img->data, *p_end = img->data + img->size; while (p < p_end) { uint16_t header = (p[0] << 8) | p[1]; p += sizeof(uint16_t); if ((0xFFD0 <= header) && (header <= 0xFFD9)) { continue; } else if (0xFFDA == header) { // Start-of-Scan (no more jpeg headers left). return true; } else if (((0xFFC0 <= header) && (header <= 0xFFCF)) || ((0xFFDB <= header) && (header <= 0xFFDF)) || ((0xFFE0 <= header) && (header <= 0xFFEF)) || ((0xFFF0 <= header) && (header <= 0xFFFE))) { uint16_t size = (p[0] << 8) | p[1]; p += sizeof(uint16_t); if (((0xFFC1 <= header) && (header <= 0xFFC3)) || ((0xFFC5 <= header) && (header <= 0xFFC7)) || ((0xFFC9 <= header) && (header <= 0xFFCB)) || ((0xFFCD <= header) && (header <= 0xFFCF))) { // Non-baseline jpeg. return false; } else { p += size - sizeof(uint16_t); } } else { // Invalid JPEG return false; } } return false; } int jpeg_clean_trailing_bytes(int size, uint8_t *data) { while ((size > 1) && ((data[size - 2] != 0xFF) || (data[size - 1] != 0xD9))) { size -= 1; } return size; } #if defined(IMLIB_ENABLE_IMAGE_FILE_IO) // This function inits the geometry values of an image. void jpeg_read_geometry(file_t *fp, image_t *img, const char *path, jpg_read_settings_t *rs) { for (;;) { uint16_t header; file_read(fp, &header, 2); header = __REV16(header); if ((0xFFD0 <= header) && (header <= 0xFFD9)) { continue; } else if (((0xFFC0 <= header) && (header <= 0xFFCF)) || ((0xFFDA <= header) && (header <= 0xFFDF)) || ((0xFFE0 <= header) && (header <= 0xFFEF)) || ((0xFFF0 <= header) && (header <= 0xFFFE))) { uint16_t size; file_read(fp, &size, 2); size = __REV16(size); if (((0xFFC0 <= header) && (header <= 0xFFC3)) || ((0xFFC5 <= header) && (header <= 0xFFC7)) || ((0xFFC9 <= header) && (header <= 0xFFCB)) || ((0xFFCD <= header) && (header <= 0xFFCF))) { file_read(fp, NULL, 1); uint16_t height; file_read(fp, &height, 2); height = __REV16(height); uint16_t width; file_read(fp, &width, 2); width = __REV16(width); rs->jpg_w = width; rs->jpg_h = height; rs->jpg_size = IMLIB_IMAGE_MAX_SIZE(file_size(fp)); img->w = rs->jpg_w; img->h = rs->jpg_h; img->size = rs->jpg_size; img->pixfmt = PIXFORMAT_JPEG; return; } else { file_seek(fp, file_tell(fp) + size - 2); } } else { file_raise_corrupted(fp); } } } // This function reads the pixel values of an image. void jpeg_read_pixels(file_t *fp, image_t *img) { file_seek(fp, 0); file_read(fp, img->pixels, img->size); } void jpeg_read(image_t *img, const char *path) { file_t fp; jpg_read_settings_t rs; // Do not use file buffering here. file_open(&fp, path, false, FA_READ | FA_OPEN_EXISTING); jpeg_read_geometry(&fp, img, path, &rs); if (!img->pixels) { image_alloc(img, img->size); } jpeg_read_pixels(&fp, img); file_close(&fp); } void jpeg_write(image_t *img, const char *path, int quality) { file_t fp; file_open(&fp, path, false, FA_WRITE | FA_CREATE_ALWAYS); if (IM_IS_JPEG(img)) { file_write(&fp, img->pixels, img->size); } else { // alloc in jpeg compress image_t out = { .w = img->w, .h = img->h, .pixfmt = PIXFORMAT_JPEG, .size = 0, .pixels = NULL }; // When jpeg_compress needs more memory than in currently allocated it // will try to realloc. MP will detect that the pointer is outside of // the heap and return NULL which will cause an out of memory error. jpeg_compress(img, &out, quality, false, JPEG_SUBSAMPLING_AUTO); file_write(&fp, out.pixels, out.size); fb_free(); // frees alloc in jpeg_compress() } file_close(&fp); } #endif //IMLIB_ENABLE_IMAGE_FILE_IO)