/* * This file is part of the OpenMV project. * * Copyright (c) 2013-2019 Ibrahim Abdelkader * Copyright (c) 2013-2019 Kwabena W. Agyeman * * This work is licensed under the MIT license, see the file LICENSE for details. * * 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 #include STM32_HAL_H #include #include "xalloc.h" #include "fb_alloc.h" #include "ff_wrapper.h" #include "imlib.h" #include "omv_boardconfig.h" #define TIME_JPEG (0) // Expand 4 bits to 32 for binary to grayscale; process 4 pixels at a time const uint32_t u32Expand[16] = {0x0, 0xff, 0xff00, 0xffff, 0xff0000, 0xff00ff, 0xffff00, 0xffffff, 0xff000000, 0xff0000ff, 0xff00ff00, 0xff00ffff, 0xffff0000, 0xffff00ff, 0xffffff00, 0xffffffff}; #if (OMV_HARDWARE_JPEG == 1) #define MCU_W (8) #define MCU_H (8) #define JPEG_444_GS_MCU_SIZE (64) #define JPEG_444_YCBCR_MCU_SIZE (192) #define JPEG_422_YCBCR_MCU_SIZE (256) #define JPEG_420_YCBCR_MCU_SIZE (384) typedef struct _jpeg_enc { int img_w; int img_h; int img_bpp; int mcu_row; int mcu_size; int out_size; int x_offset; int y_offset; bool overflow; image_t *img; union { uint8_t *pixels8; uint16_t *pixels16; }; } jpeg_enc_t; static uint8_t mcubuf[512]; static jpeg_enc_t jpeg_enc; static uint8_t *get_mcu() { uint8_t *Y0 = mcubuf; uint8_t *CB = mcubuf + 64; uint8_t *CR = mcubuf + 128; int r, g, b; // to separate RGB565 into R8,G8,B8 int dx=MCU_W, dy=MCU_H; // width and height of MCU can be truncated if we're at bottom or right edge // Copy 8x8 MCUs switch (jpeg_enc.img_bpp) { case 0: { if (jpeg_enc.x_offset+dx > jpeg_enc.img_w) dx = jpeg_enc.img_w - jpeg_enc.x_offset; // fewer than 8 wide if (jpeg_enc.y_offset+dy > jpeg_enc.img_h) dy = jpeg_enc.img_h - jpeg_enc.y_offset; // fewer than 8 tall if (dx != MCU_W || dy != MCU_H) { // edge case (bottom or right), memset(Y0, 0, 64); // all empty spots will be 0 for (int y=jpeg_enc.y_offset; y<(jpeg_enc.y_offset + dy); y++) { for (int x=jpeg_enc.x_offset; x<(jpeg_enc.x_offset + dx); x++) { *Y0++ = COLOR_BINARY_TO_GRAYSCALE(IMAGE_GET_BINARY_PIXEL(jpeg_enc.img, x, y)); } } } else { // full sized (8x8) MCU int iPitch = ((jpeg_enc.img->w + 31) >> 3) & 0xfffc; // dword align uint8_t u8Pixels; uint32_t *d32 = (uint32_t *)Y0; for (int y=jpeg_enc.y_offset; y<(jpeg_enc.y_offset + 8); y++) { // read 8 binary pixels in one shot int index = (y * iPitch) + (jpeg_enc.x_offset>>3); // get byte offset uint8_t *s = &jpeg_enc.img->data[index]; u8Pixels = s[0]; // get 8 binary pixels (1 byte) *d32++ = u32Expand[u8Pixels & 0xf]; // first 4 pixels *d32++ = u32Expand[u8Pixels >> 4]; // second 4 pixels } // for y } // full MCU } break; case 1: { uint32_t *s32, *d32; if (jpeg_enc.x_offset+dx > jpeg_enc.img_w) dx = jpeg_enc.img_w - jpeg_enc.x_offset; // fewer than 8 wide if (jpeg_enc.y_offset+dy > jpeg_enc.img_h) dy = jpeg_enc.img_h - jpeg_enc.y_offset; // fewer than 8 tall if (dx != MCU_W || dy != MCU_H) // partial MCU, fill with 0's to start memset(Y0, 0, 64); for (int y=jpeg_enc.y_offset; y<(jpeg_enc.y_offset + dy); y++) { if (dx != MCU_W) { for (int x=jpeg_enc.x_offset; x<(jpeg_enc.x_offset + dx); x++) { *Y0++ = jpeg_enc.pixels8[y * jpeg_enc.img_w + x]; } Y0 += (MCU_W - dx); } else { // full 8x8 s32 = (uint32_t *)&jpeg_enc.pixels8[(y * jpeg_enc.img_w) + jpeg_enc.x_offset]; d32 = (uint32_t *)Y0; d32[0] = s32[0]; d32[1] = s32[1]; // copy 8 pixels Y0 += 8; } } } break; case 2: { uint16_t *pPixels, pixel; if (jpeg_enc.x_offset+dx > jpeg_enc.img_w) dx = jpeg_enc.img_w - jpeg_enc.x_offset; // fewer than 8 wide if (jpeg_enc.y_offset+dy > jpeg_enc.img_h) dy = jpeg_enc.img_h - jpeg_enc.y_offset; // fewer than 8 tall if (dx != MCU_W || dy != MCU_H) // partial MCU, fill with 0's to start memset(mcubuf, 0, 192); // faster than using a per pixel conditional statement for (int y=jpeg_enc.y_offset, idx=0; y<(jpeg_enc.y_offset + dy); y++) { pPixels = &jpeg_enc.pixels16[(y * jpeg_enc.img_w) + jpeg_enc.x_offset]; for (int x=jpeg_enc.x_offset; x<(jpeg_enc.x_offset + dx); x++, idx++) { pixel = *pPixels++; // get RGB565 pixel r = rb528_table[(pixel >> 3) & 0x1f]; // extract R8/G8/B8 g = g628_table[((pixel & 7) << 3) | (pixel >> 13)]; b = rb528_table[(pixel >> 8) & 0x1f]; // faster to keep all calculations in integer math with 15-bit fractions Y0[idx] = (uint8_t)(((r * 9770) + (g * 19182) + (b * 3736)) >> 15); // .299*r + .587*g + .114*b CB[idx] = (uint8_t)(((b << 14) - (r * 5529) - (g * 10855)) >> 15) -128; // -0.168736*r + -0.331264*g + 0.5*b CR[idx] = (uint8_t)(((r << 14) - (g * 13682) - (b * 2664)) >> 15) -128; // 0.5*r + -0.418688*g + -0.081312*b } idx += (MCU_W - dx); // increment the dest pointer properly for partial MCUs (output width is always 8) } break; } case 3: { uint16_t pixel, rgbbuf[64]; if (jpeg_enc.x_offset + 8 >= jpeg_enc.img_w || jpeg_enc.y_offset + 8 >= jpeg_enc.img_h || jpeg_enc.x_offset == 0 || jpeg_enc.y_offset == 0) { // use slow method on edges // Bayer to rgb565 takes care of zero padding. imlib_bayer_to_rgb565(jpeg_enc.img, 8, 8, jpeg_enc.x_offset, jpeg_enc.y_offset, rgbbuf); for (int y=0, idx=0; y<8; y++) { for (int x=0; x<8; x++, idx++) { pixel = rgbbuf[idx]; r = rb528_table[(pixel >> 3) & 0x1f]; g = g628_table[((pixel & 7) << 3) | (pixel >> 13)]; b = rb528_table[(pixel >> 8) & 0x1f]; // faster to keep all calculations in integer math with 15-bit fractions Y0[idx] = (uint8_t)(((r * 9770) + (g * 19182) + (b * 3736)) >> 15); // .299*r + .587*g + .114*b CB[idx] = (uint8_t)(((b << 14) - (r * 5529) - (g * 10855)) >> 15) -128; // -0.168736*r + -0.331264*g + 0.5*b CR[idx] = (uint8_t)(((r << 14) - (g * 13682) - (b * 2664)) >> 15) -128; // 0.5*r + -0.418688*g + -0.081312*b } // for x } // for y } else { // use faster method for center part uint8_t *s; int pitch = jpeg_enc.img->w; // keep in local var for (int y=0, idx=0; y<8; y++) { s = (uint8_t*)jpeg_enc.img->pixels; s += (jpeg_enc.y_offset+y)*jpeg_enc.img->w + jpeg_enc.x_offset; for (int x=0; x<8; x++, idx++, s++) { if ((y & 1) == 0) { // even rows if ((x & 1) == 0) { // even cols b = s[0]; g = s[-1] + s[1] + s[-pitch] + s[pitch]; r = s[-1-pitch] + s[1-pitch] + s[pitch-1] + s[pitch+1]; g >>= 2; r >>= 2; } else { // odd cols g = s[0]; b = s[-1] + s[1]; r = s[-pitch] + s[pitch]; b >>= 1; r >>= 1; } } else { // odd rows if ((x & 1) == 0) { // even cols g = s[0]; r = s[-1] + s[1]; b = s[-pitch] + s[pitch]; r >>= 1; b >>= 1; } else { // odd cols r = s[0]; g = s[-1] + s[1] + s[-pitch] + s[pitch]; b = s[-1-pitch] + s[1-pitch] + s[pitch-1] + s[pitch+1]; g >>= 2; b >>= 2; } } // faster to keep all calculations in integer math with 15-bit fractions Y0[idx] = (uint8_t)(((r * 9770) + (g * 19182) + (b * 3736)) >> 15); // .299*r + .587*g + .114*b CB[idx] = (uint8_t)(((b << 14) - (r * 5529) - (g * 10855)) >> 15) -128; // -0.168736*r + -0.331264*g + 0.5*b CR[idx] = (uint8_t)(((r << 14) - (g * 13682) - (b * 2664)) >> 15) -128; // 0.5*r + -0.418688*g + -0.081312*b } // for x } // for y } // fast vs slow method break; } } jpeg_enc.x_offset += MCU_W; if (jpeg_enc.x_offset == (jpeg_enc.mcu_row * MCU_W)) { jpeg_enc.x_offset = 0; jpeg_enc.y_offset += MCU_H; } return mcubuf; } void HAL_JPEG_GetDataCallback(JPEG_HandleTypeDef *hjpeg, uint32_t NbDecodedData) { HAL_JPEG_Pause(hjpeg, JPEG_PAUSE_RESUME_INPUT); if ((hjpeg->JpegOutCount+1024) > hjpeg->OutDataLength) { // JPEG buffer overflow. jpeg_enc.overflow = true; HAL_JPEG_Abort(hjpeg); HAL_JPEG_ConfigInputBuffer(hjpeg, NULL, 0); } else if (jpeg_enc.y_offset == jpeg_enc.img_h) { // Compression is done. HAL_JPEG_ConfigInputBuffer(hjpeg, NULL, 0); HAL_JPEG_Resume(hjpeg, JPEG_PAUSE_RESUME_INPUT); } else { // Set the next MCU. HAL_JPEG_ConfigInputBuffer(hjpeg, get_mcu(), jpeg_enc.mcu_size); HAL_JPEG_Resume(hjpeg, JPEG_PAUSE_RESUME_INPUT); } } void HAL_JPEG_DataReadyCallback (JPEG_HandleTypeDef *hjpeg, uint8_t *pDataOut, uint32_t OutDataLength) { jpeg_enc.out_size = OutDataLength; } void HAL_JPEG_ErrorCallback(JPEG_HandleTypeDef *hjpeg) { printf("JPEG decode/encode error\n"); } bool jpeg_compress(image_t *src, image_t *dst, int quality, bool realloc) { #if (TIME_JPEG==1) uint32_t start = HAL_GetTick(); #endif // Init the HAL JPEG driver JPEG_HandleTypeDef JPEG_Handle = {0}; JPEG_Handle.Instance = JPEG; HAL_JPEG_Init(&JPEG_Handle); uint32_t pad_w = src->w; if (pad_w % 8 != 0) { pad_w += (8 - (pad_w % 8)); } jpeg_enc.img = src; jpeg_enc.img_w = src->w; jpeg_enc.img_h = src->h; jpeg_enc.img_bpp = src->bpp; jpeg_enc.mcu_row = pad_w / MCU_W; jpeg_enc.out_size = 0; jpeg_enc.x_offset = 0; jpeg_enc.y_offset = 0; jpeg_enc.overflow = false; jpeg_enc.pixels8 = (uint8_t *) src->pixels; jpeg_enc.pixels16 = (uint16_t*) src->pixels; JPEG_ConfTypeDef JPEG_Info; JPEG_Info.ImageWidth = src->w; JPEG_Info.ImageHeight = src->h; JPEG_Info.ImageQuality = quality; switch (src->bpp) { case 0: case 1: jpeg_enc.mcu_size = JPEG_444_GS_MCU_SIZE; JPEG_Info.ColorSpace = JPEG_GRAYSCALE_COLORSPACE; JPEG_Info.ChromaSubsampling = JPEG_444_SUBSAMPLING; break; case 2: case 3: jpeg_enc.mcu_size = JPEG_444_YCBCR_MCU_SIZE; JPEG_Info.ColorSpace = JPEG_YCBCR_COLORSPACE; JPEG_Info.ChromaSubsampling = JPEG_444_SUBSAMPLING; break; } if (HAL_JPEG_ConfigEncoding(&JPEG_Handle, &JPEG_Info) != HAL_OK) { // Initialization error return true; } // NOTE: output buffer size is stored in dst->bpp if (HAL_JPEG_Encode(&JPEG_Handle, get_mcu(), jpeg_enc.mcu_size, dst->pixels, dst->bpp, 3000) != HAL_OK) { // Initialization error return true; } // Set output size dst->bpp = jpeg_enc.out_size; #if (TIME_JPEG==1) printf("time: %lums\n", HAL_GetTick() - start); #endif HAL_JPEG_DeInit(&JPEG_Handle); return jpeg_enc.overflow; } #else // 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; int bitc, bitb; 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}, }; 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 = xrealloc(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 = xrealloc(jpeg_buf->buf, jpeg_buf->length); } memcpy(jpeg_buf->buf+jpeg_buf->idx, data, size); jpeg_buf->idx += size; } static void jpeg_writeBits(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 void jpeg_calcBits(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<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]; } } // Encode DC int diff = DUQ[0] - DC; if (diff == 0) { jpeg_writeBits(jpeg_buf, HTDC[0]); } else { uint16_t bits[2]; jpeg_calcBits(diff, bits); jpeg_writeBits(jpeg_buf, HTDC[bits[1]]); jpeg_writeBits(jpeg_buf, bits); } // Encode ACs if(end0pos == 0) { jpeg_writeBits(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_writeBits(jpeg_buf, M16zeroes); nrzeroes &= 15; } uint16_t bits[2]; jpeg_calcBits(DUQ[i], bits); jpeg_writeBits(jpeg_buf, HTAC[(nrzeroes<<4)+bits[1]]); jpeg_writeBits(jpeg_buf, bits); } if(end0pos != 63) { jpeg_writeBits(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_subsample_t jpeg_subsample) { // 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; i0)}, 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 img->h || x_offs+mcu_w > img->w) { // clipped for (int y=y_offs; y= img->w || y >= img->h) { *mcu++ = 0; } else { *mcu++ = COLOR_BINARY_TO_GRAYSCALE(IMAGE_GET_BINARY_PIXEL(img, x, y)) - 128; } } } } // clipped else { int iPitch = ((img->w + 31) >> 3) & 0xfffc; // dword align uint8_t u8Pixels; uint32_t *d32 = (uint32_t *)mcu; for (int y=y_offs; y<(y_offs + 8); y++) { // read 8 binary pixels in one shot int index = (y * iPitch) + (x_offs>>3); // get byte offset uint8_t *s = &img->data[index]; u8Pixels = s[0]; // get 8 binary pixels (1 byte) *d32++ = u32Expand[u8Pixels & 0xf]; // first 4 pixels *d32++ = u32Expand[u8Pixels >> 4]; // second 4 pixels } // for y } // not clipped break; } case 1: { uint8_t *mcu = (uint8_t*) buf; //memset(mcu, 0, 64); if (y_offs+mcu_h > img->h || x_offs+mcu_w > img->w) { // truncated MCU for (int y=y_offs; y= img->w || y >= img->h) { *mcu++ = 0; } else { *mcu++ = IMAGE_GET_GRAYSCALE_PIXEL(img, x, y) - 128; } } } } // needs to be clipped else // no need to check bounds per pixel { for (int y=y_offs; ydata[(y * img->w) + x_offs]; for (int x=x_offs; x= img->w || y >= img->h) { *mcu++ = 0; } else { *mcu++ = IMAGE_GET_RGB565_PIXEL(img, x, y); } } } break; } default: break; } } bool jpeg_compress(image_t *src, image_t *dst, int quality, bool realloc) { int DCY=0, DCU=0, DCV=0; #if (TIME_JPEG==1) uint32_t start = HAL_GetTick(); #endif // JPEG buffer jpeg_buf_t jpeg_buf = { .idx =0, .buf = dst->pixels, .length = dst->bpp, .bitc = 0, .bitb = 0, .realloc = realloc, .overflow = false, }; // Initialize quantization tables jpeg_init(quality); jpeg_subsample_t jpeg_subsample; if (quality >= 60) { jpeg_subsample = JPEG_SUBSAMPLE_1x1; } else if (quality > 35) { jpeg_subsample = JPEG_SUBSAMPLE_2x1; } else { // <= 35 jpeg_subsample = JPEG_SUBSAMPLE_2x2; } // Write JPEG headers if (src->bpp == 3) { // BAYER // Will be converted to RGB565 jpeg_write_headers(&jpeg_buf, src->w, src->h, 2, jpeg_subsample); } else { jpeg_write_headers(&jpeg_buf, src->w, src->h, (src->bpp == 0) ? 1 : src->bpp, jpeg_subsample); } // Encode 8x8 macroblocks if (src->bpp == 0) { int8_t YDU[64]; // Copy 8x8 MCUs for (int y=0; yh; y+=8) { for (int x=0; xw; x+=8) { jpeg_get_mcu(src, 8, 8, x, y, src->bpp, YDU); DCY = jpeg_processDU(&jpeg_buf, YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); } if (jpeg_buf.overflow) { goto jpeg_overflow; } } } else if (src->bpp == 1) { int8_t YDU[64]; // Copy 8x8 MCUs for (int y=0; yh; y+=8) { for (int x=0; xw; x+=8) { jpeg_get_mcu(src, 8, 8, x, y, src->bpp, YDU); DCY = jpeg_processDU(&jpeg_buf, YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); } if (jpeg_buf.overflow) { goto jpeg_overflow; } } } else if (src->bpp == 2) {// TODO assuming RGB565 switch (jpeg_subsample) { case JPEG_SUBSAMPLE_1x1: { uint16_t pixel, *pRow;; int dx, dy; int r, g, b; // to separate RGB565 into R8,G8,B8 int8_t YDU[64], UDU[64], VDU[64]; int8_t *pY, *pU, *pV; for (int y=0; yh; y+=8) { dy = 8; if (y+8 > src->h) // over bottom edge dy = src->h - y; for (int x=0; xw; x+=8) { dx = 8; if (x+8 > src->w) // over right edge, reduce capture size dx = src->w - x; if (dx != 8 || dy != 8) { // fill unused portion with 0 memset(YDU,0,sizeof(YDU)); memset(UDU,0,sizeof(UDU)); memset(VDU,0,sizeof(VDU)); } for (int ty=0; ty> 3) & 0x1f]; // extract R8/G8/B8 g = g628_table[((pixel & 7) << 3) | (pixel >> 13)]; b = rb528_table[(pixel >> 8) & 0x1f]; // faster to keep all calculations in integer math with 15-bit fractions *pY++ = (uint8_t)(((r * 9770) + (g * 19182) + (b * 3736)) >> 15) -128; // .299*r + .587*g + .114*b *pU++ = (uint8_t)(((b << 14) - (r * 5529) - (g * 10855)) >> 15); // -0.168736*r + -0.331264*g + 0.5*b *pV++ = (uint8_t)(((r << 14) - (g * 13682) - (b * 2664)) >> 15); // 0.5*r + -0.418688*g + -0.081312*b } // for tx } // for ty DCY = jpeg_processDU(&jpeg_buf, YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); 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) { goto jpeg_overflow; } } break; } case JPEG_SUBSAMPLE_2x1: { uint16_t pixel, *pRow; int dx, dy; int r, g, b; // to separate RGB565 into R8,G8,B8 int8_t YDU[128], UDU[64], VDU[64]; int8_t *pY, *pU, *pV; for (int y=0; yh; y+=8) { dy = 8; if (y+8 > src->h) // over bottom edge dy = src->h - y; for (int x=0; xw; x+=16) { dx = 16; if (x+16 > src->w) // over right edge dx = src->w - x; for (int ty=0; ty> 3) & 0x1f]; // extract R8/G8/B8 g = g628_table[((pixel & 7) << 3) | (pixel >> 13)]; b = rb528_table[(pixel >> 8) & 0x1f]; // faster to keep all calculations in integer math with 15-bit fractions pY[0] = (uint8_t)(((r * 9770) + (g * 19182) + (b * 3736)) >> 15) -128; // .299*r + .587*g + .114*b *pU++ = (uint8_t)(((b << 14) - (r * 5529) - (g * 10855)) >> 15); // -0.168736*r + -0.331264*g + 0.5*b *pV++ = (uint8_t)(((r << 14) - (g * 13682) - (b * 2664)) >> 15); // 0.5*r + -0.418688*g + -0.081312*b pixel = pRow[1]; // right r = rb528_table[(pixel >> 3) & 0x1f]; // extract R8/G8/B8 g = g628_table[((pixel & 7) << 3) | (pixel >> 13)]; b = rb528_table[(pixel >> 8) & 0x1f]; // faster to keep all calculations in integer math with 15-bit fractions pY[1] = (uint8_t)(((r * 9770) + (g * 19182) + (b * 3736)) >> 15)-128; // .299*r + .587*g + .114*b pY += 2; pRow += 2; } // for tx } // for ty DCY = jpeg_processDU(&jpeg_buf, YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); DCY = jpeg_processDU(&jpeg_buf, YDU+64, fdtbl_Y, DCY, YDC_HT, YAC_HT); 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) { goto jpeg_overflow; } } break; } case JPEG_SUBSAMPLE_2x2: { uint16_t pixel, *pRow; int dx, dy; int r, g, b; // to separate RGB565 into R8,G8,B8 int8_t YDU[256], UDU[64], VDU[64]; int8_t *pY, *pU, *pV; for (int y=0; yh; y+=16) { dy = 16; if (y+16 > src->h) // over bottom edge dy = src->h - y; for (int x=0; xw; x+=16) { dx = 16; if (x+16 > src->w) // over right edge, reduce capture size dx = src->w - x; if (dx != 16 || dy != 16) { // fill unused portion with 0 memset(YDU,0,sizeof(YDU)); memset(UDU,0,sizeof(UDU)); memset(VDU,0,sizeof(VDU)); } for (int ty=0; ty= 8) // second row of Y MCUs pY += (128 - 64); for (int tx=0; tx> 3) & 0x1f]; // extract R8/G8/B8 g = g628_table[((pixel & 7) << 3) | (pixel >> 13)]; b = rb528_table[(pixel >> 8) & 0x1f]; // faster to keep all calculations in integer math with 15-bit fractions pY[0] = (uint8_t)(((r * 9770) + (g * 19182) + (b * 3736)) >> 15) -128; // .299*r + .587*g + .114*b pU[0] = (uint8_t)(((b << 14) - (r * 5529) - (g * 10855)) >> 15); // -0.168736*r + -0.331264*g + 0.5*b pV[0] = (uint8_t)(((r << 14) - (g * 13682) - (b * 2664)) >> 15); // 0.5*r + -0.418688*g + -0.081312*b pixel = pRow[1]; // top right r = rb528_table[(pixel >> 3) & 0x1f]; // extract R8/G8/B8 g = g628_table[((pixel & 7) << 3) | (pixel >> 13)]; b = rb528_table[(pixel >> 8) & 0x1f]; // faster to keep all calculations in integer math with 15-bit fractions pY[1] = (uint8_t)(((r * 9770) + (g * 19182) + (b * 3736)) >> 15)-128; // .299*r + .587*g + .114*b pixel = pRow[src->w]; // bottom left r = rb528_table[(pixel >> 3) & 0x1f]; // extract R8/G8/B8 g = g628_table[((pixel & 7) << 3) | (pixel >> 13)]; b = rb528_table[(pixel >> 8) & 0x1f]; // faster to keep all calculations in integer math with 15-bit fractions pY[8] = (uint8_t)(((r * 9770) + (g * 19182) + (b * 3736)) >> 15)-128; // .299*r + .587*g + .114*b pixel = pRow[1+src->w]; // bottom right r = rb528_table[(pixel >> 3) & 0x1f]; // extract R8/G8/B8 g = g628_table[((pixel & 7) << 3) | (pixel >> 13)]; b = rb528_table[(pixel >> 8) & 0x1f]; // faster to keep all calculations in integer math with 15-bit fractions pY[9] = (uint8_t)(((r * 9770) + (g * 19182) + (b * 3736)) >> 15)-128; // .299*r + .587*g + .114*b pY += 2; pU++; pV++; pRow += 2; } // for tx } // for ty DCY = jpeg_processDU(&jpeg_buf, YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); DCY = jpeg_processDU(&jpeg_buf, YDU+64, fdtbl_Y, DCY, YDC_HT, YAC_HT); DCY = jpeg_processDU(&jpeg_buf, YDU+128, fdtbl_Y, DCY, YDC_HT, YAC_HT); DCY = jpeg_processDU(&jpeg_buf, YDU+192, fdtbl_Y, DCY, YDC_HT, YAC_HT); 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) { goto jpeg_overflow; } } break; } } } else if (src->bpp == 3) { //RAW/BAYER switch (jpeg_subsample) { case JPEG_SUBSAMPLE_1x1: { int8_t YDU[64], UDU[64], VDU[64]; uint16_t pixel, rgbbuf[64]; int r, g, b; for (int y=0; yh; y+=8) { for (int x=0; xw; x+=8) { imlib_bayer_to_rgb565(src, 8, 8, x, y, rgbbuf); for (int ty=0, idx=0; ty<8; ty++, idx+=8) { for (int tx=0; tx<8; tx++) { pixel = rgbbuf[idx+tx]; r = rb528_table[(pixel >> 3) & 0x1f]; // extract R8/G8/B8 g = g628_table[((pixel & 7) << 3) | (pixel >> 13)]; b = rb528_table[(pixel >> 8) & 0x1f]; // faster to keep all calculations in integer math with 15-bit fractions YDU[idx+tx] = (uint8_t)(((r * 9770) + (g * 19182) + (b * 3736)) >> 15) -128; // .299*r + .587*g + .114*b UDU[idx+tx] = (uint8_t)(((b << 14) - (r * 5529) - (g * 10855)) >> 15); // -0.168736*r + -0.331264*g + 0.5*b VDU[idx+tx] = (uint8_t)(((r << 14) - (g * 13682) - (b * 2664)) >> 15); // 0.5*r + -0.418688*g + -0.081312*b } // for tx } // for ty DCY = jpeg_processDU(&jpeg_buf, YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); 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) { goto jpeg_overflow; } } break; } case JPEG_SUBSAMPLE_2x1: { uint16_t pixel, rgbbuf[128]; int8_t YDU[128], UDU[64], VDU[64]; int r, g, b, idx, ofs; for (int y=0; yh; y+=8) { for (int x=0; xw; x+=16) { imlib_bayer_to_rgb565(src, 16, 8, x, y, rgbbuf); for (int ty=0; ty<8; ty++) { idx = ty*8; ofs = ty*16; for (int tx=0; tx<8; tx++) { if (tx == 4) idx += (64-8); // right MCU pixel = rgbbuf[ofs+tx*2]; r = rb528_table[(pixel >> 3) & 0x1f]; // extract R8/G8/B8 g = g628_table[((pixel & 7) << 3) | (pixel >> 13)]; b = rb528_table[(pixel >> 8) & 0x1f]; // faster to keep all calculations in integer math with 15-bit fractions YDU[idx+tx*2] = (uint8_t)(((r * 9770) + (g * 19182) + (b * 3736)) >> 15) -128; // .299*r + .587*g + .114*b UDU[(ty*8)+tx] = (uint8_t)(((b << 14) - (r * 5529) - (g * 10855)) >> 15); // -0.168736*r + -0.331264*g + 0.5*b VDU[(ty*8)+tx] = (uint8_t)(((r << 14) - (g * 13682) - (b * 2664)) >> 15); // 0.5*r + -0.418688*g + -0.081312*b pixel = rgbbuf[ofs+tx*2+1]; r = rb528_table[(pixel >> 3) & 0x1f]; // extract R8/G8/B8 g = g628_table[((pixel & 7) << 3) | (pixel >> 13)]; b = rb528_table[(pixel >> 8) & 0x1f]; // faster to keep all calculations in integer math with 15-bit fractions YDU[idx+tx*2+1] = (uint8_t)(((r * 9770) + (g * 19182) + (b * 3736)) >> 15) -128; // .299*r + .587*g + .114*b } // for tx } // for ty DCY = jpeg_processDU(&jpeg_buf, YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); DCY = jpeg_processDU(&jpeg_buf, YDU+64, fdtbl_Y, DCY, YDC_HT, YAC_HT); 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) { goto jpeg_overflow; } } break; } case JPEG_SUBSAMPLE_2x2: { uint16_t pixel, *pRow, rgbbuf[256]; int8_t YDU[256], UDU[64], VDU[64]; int8_t *pY, *pU, *pV; int r, g, b; for (int y=0; yh; y+=16) { for (int x=0; xw; x+=16) { imlib_bayer_to_rgb565(src, 16, 16, x, y, rgbbuf); for (int ty=0; ty<16; ty+=2) { // row pairs pRow = &rgbbuf[ty*16]; pY = &YDU[(ty*8)]; pU = &UDU[ty*4]; pV=&VDU[ty*4]; if (ty >= 8) // second row of Y MCUs pY += (128 - 64); for (int tx=0; tx<16; tx+=2) { // column pairs if (tx == 8) // second column of Y MCUs pY += (64-8); pixel = pRow[0]; // top left r = rb528_table[(pixel >> 3) & 0x1f]; // extract R8/G8/B8 g = g628_table[((pixel & 7) << 3) | (pixel >> 13)]; b = rb528_table[(pixel >> 8) & 0x1f]; // faster to keep all calculations in integer math with 15-bit fractions pY[0] = (uint8_t)(((r * 9770) + (g * 19182) + (b * 3736)) >> 15) -128; // .299*r + .587*g + .114*b pU[0] = (uint8_t)(((b << 14) - (r * 5529) - (g * 10855)) >> 15); // -0.168736*r + -0.331264*g + 0.5*b pV[0] = (uint8_t)(((r << 14) - (g * 13682) - (b * 2664)) >> 15); // 0.5*r + -0.418688*g + -0.081312*b pixel = pRow[1]; // top right r = rb528_table[(pixel >> 3) & 0x1f]; // extract R8/G8/B8 g = g628_table[((pixel & 7) << 3) | (pixel >> 13)]; b = rb528_table[(pixel >> 8) & 0x1f]; // faster to keep all calculations in integer math with 15-bit fractions pY[1] = (uint8_t)(((r * 9770) + (g * 19182) + (b * 3736)) >> 15)-128; // .299*r + .587*g + .114*b pixel = pRow[16]; // bottom left r = rb528_table[(pixel >> 3) & 0x1f]; // extract R8/G8/B8 g = g628_table[((pixel & 7) << 3) | (pixel >> 13)]; b = rb528_table[(pixel >> 8) & 0x1f]; // faster to keep all calculations in integer math with 15-bit fractions pY[8] = (uint8_t)(((r * 9770) + (g * 19182) + (b * 3736)) >> 15)-128; // .299*r + .587*g + .114*b pixel = pRow[17]; // bottom right r = rb528_table[(pixel >> 3) & 0x1f]; // extract R8/G8/B8 g = g628_table[((pixel & 7) << 3) | (pixel >> 13)]; b = rb528_table[(pixel >> 8) & 0x1f]; // faster to keep all calculations in integer math with 15-bit fractions pY[9] = (uint8_t)(((r * 9770) + (g * 19182) + (b * 3736)) >> 15)-128; // .299*r + .587*g + .114*b pY += 2; pU++; pV++; pRow += 2; } // for tx } // for ty DCY = jpeg_processDU(&jpeg_buf, YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); DCY = jpeg_processDU(&jpeg_buf, YDU+64, fdtbl_Y, DCY, YDC_HT, YAC_HT); DCY = jpeg_processDU(&jpeg_buf, YDU+128, fdtbl_Y, DCY, YDC_HT, YAC_HT); DCY = jpeg_processDU(&jpeg_buf, YDU+192, fdtbl_Y, DCY, YDC_HT, YAC_HT); 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) { goto jpeg_overflow; } } break; } } } // Do the bit alignment of the EOI marker static const uint16_t fillBits[] = {0x7F, 7}; jpeg_writeBits(&jpeg_buf, fillBits); // EOI jpeg_put_char(&jpeg_buf, 0xFF); jpeg_put_char(&jpeg_buf, 0xD9); dst->bpp = jpeg_buf.idx; dst->data = jpeg_buf.buf; #if (TIME_JPEG==1) printf("time: %lums\n", HAL_GetTick() - start); #endif jpeg_overflow: return jpeg_buf.overflow; } #endif //defined OMV_HARDWARE_JPEG // This function inits the geometry values of an image. void jpeg_read_geometry(FIL *fp, image_t *img, const char *path) { for (;;) { uint16_t header; read_word(fp, &header); header = IM_SWAP16(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; read_word(fp, &size); size = IM_SWAP16(size); if (((0xFFC0 <= header) && (header <= 0xFFC3)) || ((0xFFC5 <= header) && (header <= 0xFFC7)) || ((0xFFC9 <= header) && (header <= 0xFFCB)) || ((0xFFCD <= header) && (header <= 0xFFCF))) { read_byte_ignore(fp); uint16_t width; read_word(fp, &width); width = IM_SWAP16(width); uint16_t height; read_word(fp, &height); height = IM_SWAP16(height); img->w = width; img->h = height; img->bpp = f_size(fp); return; } else { file_seek(fp, f_tell(fp) + size - 2); } } else { ff_file_corrupted(fp); } } } // This function reads the pixel values of an image. void jpeg_read_pixels(FIL *fp, image_t *img) { file_seek(fp, 0); read_data(fp, img->pixels, img->bpp); } void jpeg_read(image_t *img, const char *path) { FIL fp; file_read_open(&fp, path); // Do not use file_buffer_on() here. jpeg_read_geometry(&fp, img, path); if (!img->pixels) img->pixels = xalloc(img->bpp); jpeg_read_pixels(&fp, img); // Do not use file_buffer_off() here. file_close(&fp); } void jpeg_write(image_t *img, const char *path, int quality) { FIL fp; file_write_open(&fp, path); if (IM_IS_JPEG(img)) { write_data(&fp, img->pixels, img->bpp); } else { uint32_t size; uint8_t *buffer = fb_alloc_all(&size, FB_ALLOC_PREFER_SIZE); image_t out = { .w=img->w, .h=img->h, .bpp=size, .pixels=buffer }; // 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); write_data(&fp, out.pixels, out.bpp); fb_free(); } file_close(&fp); }