openmv/src/omv/img/jpeg.c
2020-02-02 22:20:02 +01:00

1357 lines
62 KiB
C

/*
* This file is part of the OpenMV project.
*
* Copyright (c) 2013-2019 Ibrahim Abdelkader <iabdalkader@openmv.io>
* Copyright (c) 2013-2019 Kwabena W. Agyeman <kwagyeman@openmv.io>
*
* 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 <stdio.h>
#include STM32_HAL_H
#include <arm_math.h>
#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<<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];
}
}
// 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; 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)? jpeg_subsample: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);
}
void jpeg_get_mcu(image_t *img, int mcu_w, int mcu_h, int x_offs, int y_offs, int bpp, void *buf)
{
switch (bpp) {
case 0: {
uint8_t *mcu = (uint8_t*) buf;
if (y_offs+mcu_h > img->h || x_offs+mcu_w > img->w) { // clipped
for (int y=y_offs; y<y_offs+mcu_h; y++) {
for (int x=x_offs; x<x_offs+mcu_w; x++) {
if (x >= 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<y_offs+mcu_h; y++) {
for (int x=x_offs; x<x_offs+mcu_w; x++) {
if (x >= 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; y<y_offs+mcu_h; y++) {
uint8_t *pRow = &img->data[(y * img->w) + x_offs];
for (int x=x_offs; x<x_offs+mcu_w; x++) {
*mcu++ = *pRow++ - 128;
}
}
}
break;
}
case 2: {
uint16_t *mcu = (uint16_t*) buf;
for (int y=y_offs; y<y_offs+mcu_h; y++) {
for (int x=x_offs; x<x_offs+mcu_w; x++) {
if (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; y<src->h; y+=8) {
for (int x=0; x<src->w; 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; y<src->h; y+=8) {
for (int x=0; x<src->w; 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; y<src->h; y+=8) {
dy = 8;
if (y+8 > src->h) // over bottom edge
dy = src->h - y;
for (int x=0; x<src->w; 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<dy; ty++) { // rows
pRow = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src, y+ty);
pRow += x;
pY = &YDU[(ty*8)]; pU = &UDU[ty*8]; pV=&VDU[ty*8];
for (int tx=0; tx<dx; tx++) { // columns
pixel = *pRow++;
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++ = (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; y<src->h; y+=8) {
dy = 8;
if (y+8 > src->h) // over bottom edge
dy = src->h - y;
for (int x=0; x<src->w; x+=16) {
dx = 16;
if (x+16 > src->w) // over right edge
dx = src->w - x;
for (int ty=0; ty<dy; ty++) { // rows
pRow = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src, y+ty);
pRow += x;
pY = &YDU[(ty*8)]; pU = &UDU[ty*8]; pV=&VDU[ty*8];
for (int tx=0; tx<dx; tx+=2) { // column pairs
if (tx == 8) // second column of Y MCUs
pY += (64-8);
pixel = pRow[0]; // 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++ = (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; y<src->h; y+=16) {
dy = 16;
if (y+16 > src->h) // over bottom edge
dy = src->h - y;
for (int x=0; x<src->w; 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<dy; ty+=2) { // row pairs
pRow = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src, y+ty);
pRow += x;
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<dx; 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[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; y<src->h; y+=8) {
for (int x=0; x<src->w; 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; y<src->h; y+=8) {
for (int x=0; x<src->w; 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; y<src->h; y+=16) {
for (int x=0; x<src->w; 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);
}