mirror of
https://libwebsockets.org/repo/libwebsockets
synced 2024-11-24 01:39:33 +00:00
2757 lines
54 KiB
C
2757 lines
54 KiB
C
/*
|
|
* lws jpeg
|
|
*
|
|
* Copyright (C) 2019 - 2022 Andy Green <andy@warmcat.com>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to
|
|
* deal in the Software without restriction, including without limitation the
|
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
* sell copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
* IN THE SOFTWARE.
|
|
*
|
|
* Based on public domain original with notice -->
|
|
*
|
|
* picojpeg.c v1.1 - Public domain, Rich Geldreich <richgel99@gmail.com>
|
|
* Nov. 27, 2010 - Initial release
|
|
* Feb. 9, 2013 - Added H1V2/H2V1 support, cleaned up macros, signed shift fixes
|
|
* Also integrated and tested changes from Chris Phoenix <cphoenix@gmail.com>.
|
|
*
|
|
* https://github.com/richgel999/picojpeg
|
|
*
|
|
* This version is rewritten for lws, changing the whole approach to decode on
|
|
* demand to issue a line of output at a time, statefully. This version is
|
|
* licensed MIT.
|
|
*
|
|
* Rasterization works into an 8 or 16-line buffer on Y, 444, 422 and 420 MCU
|
|
* layouts.
|
|
*/
|
|
|
|
#include <private-lib-core.h>
|
|
|
|
#define jpeg_loglevel LLL_NOTICE
|
|
#if (_LWS_ENABLED_LOGS & jpeg_loglevel)
|
|
#define lwsl_jpeg(...) _lws_log(jpeg_loglevel, __VA_ARGS__)
|
|
#else
|
|
#define lwsl_jpeg(...)
|
|
#endif
|
|
|
|
#define MARKER_SCAN_LIMIT 1536
|
|
|
|
/*
|
|
* Set to 1 if right shifts on signed ints are always unsigned (logical) shifts
|
|
* When 1, arithmetic right shifts will be emulated by using a logical shift
|
|
* with special case code to ensure the sign bit is replicated.
|
|
*/
|
|
|
|
#define PJPG_RIGHT_SHIFT_IS_ALWAYS_UNSIGNED 0
|
|
|
|
typedef enum {
|
|
LWSJDS_FIND_SOI_INIT1,
|
|
LWSJDS_FIND_SOI_INIT2,
|
|
LWSJDS_FIND_SOI,
|
|
LWSJDS_FIND_SOF1,
|
|
LWSJDS_FIND_SOF2,
|
|
LWSJDS_INIT_FRAME,
|
|
LWSJDS_INIT_SCAN,
|
|
LWSJDS_DECODE_MCU,
|
|
|
|
} lws_jpeg_decode_state_t;
|
|
|
|
// Scan types
|
|
typedef enum
|
|
{
|
|
PJPG_GRAYSCALE,
|
|
PJPG_YH1V1,
|
|
PJPG_YH2V1,
|
|
PJPG_YH1V2,
|
|
PJPG_YH2V2
|
|
} pjpeg_scan_type_t;
|
|
|
|
#if PJPG_RIGHT_SHIFT_IS_ALWAYS_UNSIGNED
|
|
static int16_t replicateSignBit16(int8_t n)
|
|
{
|
|
switch (n)
|
|
{
|
|
case 0: return 0x0000;
|
|
case 1: return 0x8000;
|
|
case 2: return 0xC000;
|
|
case 3: return 0xE000;
|
|
case 4: return 0xF000;
|
|
case 5: return 0xF800;
|
|
case 6: return 0xFC00;
|
|
case 7: return 0xFE00;
|
|
case 8: return 0xFF00;
|
|
case 9: return 0xFF80;
|
|
case 10: return 0xFFC0;
|
|
case 11: return 0xFFE0;
|
|
case 12: return 0xFFF0;
|
|
case 13: return 0xFFF8;
|
|
case 14: return 0xFFFC;
|
|
case 15: return 0xFFFE;
|
|
default: return 0xFFFF;
|
|
}
|
|
}
|
|
static LWS_INLINE int16_t arithmeticRightShiftN16(int16_t x, int8_t n)
|
|
{
|
|
int16_t r = (uint16_t)x >> (uint8_t)n;
|
|
if (x < 0)
|
|
r |= replicateSignBit16(n);
|
|
return r;
|
|
}
|
|
static LWS_INLINE long arithmeticRightShift8L(long x)
|
|
{
|
|
long r = (unsigned long)x >> 8U;
|
|
if (x < 0)
|
|
r |= ~(~(unsigned long)0U >> 8U);
|
|
return r;
|
|
}
|
|
#define PJPG_ARITH_SHIFT_RIGHT_N_16(x, n) arithmeticRightShiftN16(x, n)
|
|
#define PJPG_ARITH_SHIFT_RIGHT_8_L(x) arithmeticRightShift8L(x)
|
|
#else
|
|
#define PJPG_ARITH_SHIFT_RIGHT_N_16(x, n) ((x) >> (n))
|
|
#define PJPG_ARITH_SHIFT_RIGHT_8_L(x) ((x) >> 8)
|
|
#endif
|
|
|
|
#define PJPG_MAX_WIDTH 16384
|
|
#define PJPG_MAX_HEIGHT 16384
|
|
#define PJPG_MAXCOMPSINSCAN 3
|
|
|
|
enum {
|
|
PJM_SOF0 = 0xC0,
|
|
PJM_SOF1 = 0xC1,
|
|
PJM_SOF2 = 0xC2,
|
|
PJM_SOF3 = 0xC3,
|
|
|
|
PJM_SOF5 = 0xC5,
|
|
PJM_SOF6 = 0xC6,
|
|
PJM_SOF7 = 0xC7,
|
|
|
|
PJM_JPG = 0xC8,
|
|
PJM_SOF9 = 0xC9,
|
|
PJM_SOF10 = 0xCA,
|
|
PJM_SOF11 = 0xCB,
|
|
|
|
PJM_SOF13 = 0xCD,
|
|
PJM_SOF14 = 0xCE,
|
|
PJM_SOF15 = 0xCF,
|
|
|
|
PJM_DHT = 0xC4,
|
|
|
|
PJM_DAC = 0xCC,
|
|
|
|
PJM_RST0 = 0xD0,
|
|
PJM_RST1 = 0xD1,
|
|
PJM_RST2 = 0xD2,
|
|
PJM_RST3 = 0xD3,
|
|
PJM_RST4 = 0xD4,
|
|
PJM_RST5 = 0xD5,
|
|
PJM_RST6 = 0xD6,
|
|
PJM_RST7 = 0xD7,
|
|
|
|
PJM_SOI = 0xD8,
|
|
PJM_EOI = 0xD9,
|
|
PJM_SOS = 0xDA,
|
|
PJM_DQT = 0xDB,
|
|
PJM_DNL = 0xDC,
|
|
PJM_DRI = 0xDD,
|
|
PJM_DHP = 0xDE,
|
|
PJM_EXP = 0xDF,
|
|
|
|
PJM_APP0 = 0xE0,
|
|
PJM_APP15 = 0xEF,
|
|
|
|
PJM_JPG0 = 0xF0,
|
|
PJM_JPG13 = 0xFD,
|
|
PJM_COM = 0xFE,
|
|
|
|
PJM_TEM = 0x01,
|
|
|
|
PJM_ERROR = 0x100,
|
|
|
|
RST0 = 0xD0
|
|
};
|
|
|
|
typedef struct huff_table {
|
|
uint16_t min_code[16];
|
|
uint16_t max_code[16];
|
|
uint8_t value[16];
|
|
} huff_table_t;
|
|
|
|
typedef struct lws_jpeg {
|
|
|
|
pjpeg_scan_type_t scan_type;
|
|
|
|
const uint8_t *inbuf;
|
|
uint8_t *lines;
|
|
size_t insize;
|
|
|
|
lws_jpeg_decode_state_t dstate;
|
|
|
|
int16_t coeffs[8 * 8];
|
|
int16_t quant0[8 * 8];
|
|
int16_t quant1[8 * 8];
|
|
int16_t last_dc[3];
|
|
uint16_t bits;
|
|
uint16_t image_width;
|
|
uint16_t image_height;
|
|
uint16_t restart_interval;
|
|
uint16_t restart_num;
|
|
uint16_t restarts_left;
|
|
uint16_t mcu_max_row;
|
|
uint16_t mcu_max_col;
|
|
|
|
uint16_t mcu_ofs_x;
|
|
uint16_t mcu_ofs_y;
|
|
|
|
uint16_t mcu_count_left_x;
|
|
uint16_t mcu_count_left_y;
|
|
|
|
huff_table_t huff_tab0;
|
|
huff_table_t huff_tab1;
|
|
huff_table_t huff_tab2;
|
|
huff_table_t huff_tab3;
|
|
|
|
uint8_t mcu_buf_R[256];
|
|
uint8_t mcu_buf_G[256];
|
|
uint8_t mcu_buf_B[256];
|
|
|
|
uint8_t huff_val0[16];
|
|
uint8_t huff_val1[16];
|
|
uint8_t huff_val2[256];
|
|
uint8_t huff_val3[256];
|
|
|
|
uint8_t mcu_org_id[6];
|
|
uint8_t comp_id[3];
|
|
uint8_t comp_h_samp[3];
|
|
uint8_t comp_v_samp[3];
|
|
uint8_t comp_quant[3];
|
|
|
|
uint8_t comp_scan_count;
|
|
uint8_t comp_list[3];
|
|
uint8_t comp_dc[3]; // 0,1
|
|
uint8_t comp_ac[3]; // 0,1
|
|
|
|
uint8_t mcu_max_blocks;
|
|
uint8_t mcu_max_size_x;
|
|
uint8_t mcu_max_size_y;
|
|
|
|
uint8_t stash[2];
|
|
uint8_t stashc;
|
|
uint8_t ringy;
|
|
|
|
uint8_t huff_valid;
|
|
uint8_t quant_valid;
|
|
|
|
uint8_t seen_eoi;
|
|
|
|
uint8_t bits_left;
|
|
|
|
uint8_t frame_comps;
|
|
|
|
uint8_t ff_skip;
|
|
char hold_at_metadata;
|
|
|
|
/* interruptible fine states */
|
|
uint16_t fs_hd_code; /* huff_decode() */
|
|
uint16_t fs_emit_budget; /* lws_jpeg_emit_next_line */
|
|
uint16_t fs_pm_skip_budget;
|
|
uint16_t fs_pm_count;
|
|
uint16_t fs_pm_temp;
|
|
uint16_t fs_sos_left;
|
|
uint16_t fs_sof_left;
|
|
uint16_t fs_ir_i;
|
|
uint8_t fs_gb16; /* get_bits16() */
|
|
uint8_t fs_hd; /* huff_decode() */
|
|
uint8_t fs_hd_i; /* huff_decode() */
|
|
uint8_t fs_emit_lc;
|
|
uint8_t fs_emit_tc;
|
|
uint8_t fs_emit_c;
|
|
uint8_t fs_pm_s1;
|
|
uint8_t fs_pm_c;
|
|
uint8_t fs_pm_skip;
|
|
uint8_t fs_pm_bits[16];
|
|
uint8_t fs_pm_i;
|
|
uint8_t fs_pm_n;
|
|
uint8_t fs_pm_have_n;
|
|
uint8_t fs_pm_ti;
|
|
uint8_t fs_sos_phase;
|
|
uint8_t fs_sos_phase_loop;
|
|
uint8_t fs_sos_i;
|
|
uint8_t fs_sos_cc;
|
|
uint8_t fs_sos_c;
|
|
uint8_t fs_mcu_phase;
|
|
uint8_t fs_mcu_phase_loop;
|
|
uint8_t fs_mcu_mb;
|
|
uint8_t fs_mcu_k;
|
|
uint8_t fs_mcu_s;
|
|
uint8_t fs_sof_phase;
|
|
uint8_t fs_sof_i;
|
|
uint8_t fs_ir_phase;
|
|
uint8_t fs_is_phase;
|
|
|
|
} lws_jpeg_t;
|
|
|
|
static const int8_t ZAG[] = { 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18,
|
|
11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27,
|
|
20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57,
|
|
50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51,
|
|
58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61,
|
|
54, 47, 55, 62, 63, };
|
|
|
|
static LWS_INLINE lws_stateful_ret_t
|
|
get_char(lws_jpeg_t *j, uint8_t *c)
|
|
{
|
|
if (j->stashc) {
|
|
*c = j->stash[0];
|
|
j->stash[0] = j->stash[1];
|
|
j->stashc--;
|
|
return LWS_SRET_OK;
|
|
}
|
|
|
|
if (!j->insize)
|
|
return LWS_SRET_WANT_INPUT;
|
|
|
|
*c = *j->inbuf++;
|
|
j->insize--;
|
|
|
|
return LWS_SRET_OK;
|
|
}
|
|
|
|
static lws_stateful_ret_t
|
|
get_octet(lws_jpeg_t *j, uint8_t *c, uint8_t ffcheck)
|
|
{
|
|
lws_stateful_ret_t r;
|
|
uint8_t c1;
|
|
|
|
if (!j->ff_skip) {
|
|
r = get_char(j, c);
|
|
if (r)
|
|
return r;
|
|
}
|
|
|
|
if (ffcheck && (j->ff_skip || *c == 0xff)) {
|
|
j->ff_skip = 1;
|
|
r = get_char(j, &c1);
|
|
if (r)
|
|
return r;
|
|
j->ff_skip = 0;
|
|
if (c1) {
|
|
if (c1 == PJM_EOI) {
|
|
j->seen_eoi = 1;
|
|
return LWS_SRET_OK;
|
|
}
|
|
lwsl_jpeg("%s: nonzero stuffed 0x%02X\n", __func__, c1);
|
|
return LWS_SRET_FATAL + 1;
|
|
}
|
|
|
|
*c = 0xff;
|
|
}
|
|
|
|
return LWS_SRET_OK;
|
|
}
|
|
|
|
static lws_stateful_ret_t
|
|
get_bits8(lws_jpeg_t *j, uint8_t *v, uint8_t numBits, uint8_t ffcheck)
|
|
{
|
|
uint8_t origBits = numBits, c = 0;
|
|
uint16_t ret = j->bits;
|
|
lws_stateful_ret_t r;
|
|
|
|
if (j->bits_left < numBits) {
|
|
|
|
r = get_octet(j, &c, ffcheck);
|
|
if (r)
|
|
return r;
|
|
|
|
j->bits = (uint16_t)(j->bits << j->bits_left);
|
|
j->bits = (uint16_t)(j->bits | c);
|
|
j->bits = (uint16_t)(j->bits << (numBits - j->bits_left));
|
|
|
|
j->bits_left = (uint8_t)(8 - (numBits - j->bits_left));
|
|
} else {
|
|
j->bits_left = (uint8_t) (j->bits_left - numBits);
|
|
j->bits = (uint16_t)(j->bits << numBits);
|
|
}
|
|
|
|
*v = (uint8_t)(ret >> (16 - origBits));
|
|
|
|
return LWS_SRET_OK;
|
|
}
|
|
|
|
static lws_stateful_ret_t
|
|
get_bits16(lws_jpeg_t *j, uint16_t *v, uint8_t numBits, uint8_t ffcheck)
|
|
{
|
|
uint8_t origBits = numBits, c = 0;
|
|
uint16_t ret = j->bits;
|
|
lws_stateful_ret_t r;
|
|
|
|
assert(numBits > 8); /* otherwise, use get_bits8 */
|
|
numBits = (uint8_t)(numBits - 8);
|
|
|
|
if (!j->fs_gb16) { /* if not interrupted in second part */
|
|
|
|
r = get_octet(j, &c, ffcheck);
|
|
if (r)
|
|
return r;
|
|
|
|
j->bits = (uint16_t)(j->bits << j->bits_left);
|
|
j->bits = (uint16_t)(j->bits | c);
|
|
j->bits = (uint16_t)(j->bits << (8 - j->bits_left));
|
|
}
|
|
|
|
ret = (uint16_t)((ret & 0xff00) | (j->bits >> 8));
|
|
|
|
if (j->bits_left < numBits) {
|
|
|
|
j->fs_gb16 = 1; /* so we skip to here if retrying */
|
|
r = get_octet(j, &c, ffcheck);
|
|
if (r)
|
|
return r;
|
|
|
|
j->fs_gb16 = 0; /* cancel skip to here flag */
|
|
|
|
j->bits = (uint16_t)(j->bits << j->bits_left);
|
|
j->bits = (uint16_t)(j->bits | c);
|
|
j->bits = (uint16_t)(j->bits << (numBits - j->bits_left));
|
|
j->bits_left = (uint8_t)(8 - (numBits - j->bits_left));
|
|
} else {
|
|
j->bits_left = (uint8_t) (j->bits_left - numBits);
|
|
j->bits = (uint16_t)(j->bits << numBits);
|
|
}
|
|
|
|
*v = (uint16_t)(ret >> (16 - origBits));
|
|
|
|
return LWS_SRET_OK;
|
|
}
|
|
|
|
static LWS_INLINE lws_stateful_ret_t
|
|
get_bit(lws_jpeg_t *j, uint16_t *v)
|
|
{
|
|
lws_stateful_ret_t r;
|
|
uint16_t ret = 0;
|
|
uint8_t c = 0;
|
|
|
|
if (j->bits & 0x8000)
|
|
ret = 1;
|
|
|
|
if (!j->bits_left) {
|
|
r = get_octet(j, &c, 1);
|
|
if (r)
|
|
return r;
|
|
|
|
j->bits = (uint16_t)(j->bits | c);
|
|
j->bits_left = (uint8_t)(j->bits_left + 8);
|
|
}
|
|
|
|
j->bits_left--;
|
|
j->bits = (uint16_t)(j->bits << 1);
|
|
|
|
*v = ret;
|
|
|
|
return LWS_SRET_OK;
|
|
}
|
|
|
|
static uint16_t
|
|
get_extend_test(uint8_t i)
|
|
{
|
|
if (!i || i > 15)
|
|
return 0;
|
|
|
|
return (uint16_t)(1 << (i - 1));
|
|
}
|
|
|
|
static int16_t
|
|
get_extend_offset(uint8_t i)
|
|
{
|
|
if (!i || i > 15)
|
|
return 0;
|
|
|
|
return (int16_t)((int16_t)(0xffffffff << i) + 1);
|
|
}
|
|
|
|
static LWS_INLINE int16_t
|
|
huff_extend(uint16_t x, uint8_t s)
|
|
{
|
|
return (int16_t)(((x < get_extend_test(s)) ?
|
|
x + get_extend_offset(s) : x));
|
|
}
|
|
|
|
static LWS_INLINE lws_stateful_ret_t
|
|
huff_decode(lws_jpeg_t *j, uint8_t *v, const huff_table_t *ht, const uint8_t *p)
|
|
{
|
|
lws_stateful_ret_t r;
|
|
uint16_t c;
|
|
|
|
if (!j->fs_hd) {
|
|
r = get_bit(j, &j->fs_hd_code);
|
|
if (r)
|
|
return r;
|
|
if (j->seen_eoi)
|
|
return LWS_SRET_OK;
|
|
j->fs_hd = 1;
|
|
j->fs_hd_i = 0;
|
|
}
|
|
|
|
for (;;) {
|
|
uint16_t maxCode;
|
|
|
|
if (j->fs_hd_i == 16) {
|
|
j->fs_hd = 0;
|
|
*v = 0;
|
|
return LWS_SRET_OK;
|
|
}
|
|
|
|
maxCode = ht->max_code[j->fs_hd_i];
|
|
if ((j->fs_hd_code <= maxCode) && (maxCode != 0xFFFF))
|
|
break;
|
|
|
|
r = get_bit(j, &c);
|
|
if (r)
|
|
return r;
|
|
|
|
if (j->seen_eoi)
|
|
return LWS_SRET_OK;
|
|
|
|
j->fs_hd_i++;
|
|
j->fs_hd_code = (uint16_t)((j->fs_hd_code << 1) | c);
|
|
}
|
|
|
|
j->fs_hd = 0;
|
|
|
|
*v = p[(ht->value[j->fs_hd_i] +
|
|
(j->fs_hd_code - ht->min_code[j->fs_hd_i]))];
|
|
|
|
return LWS_SRET_OK;
|
|
}
|
|
|
|
static void
|
|
huffCreate(const uint8_t *pBits, huff_table_t *ht)
|
|
{
|
|
uint8_t i = 0;
|
|
uint8_t jj = 0;
|
|
|
|
uint16_t code = 0;
|
|
|
|
for (;;) {
|
|
uint8_t num = pBits[i];
|
|
|
|
if (!num) {
|
|
ht->min_code[i] = 0x0000;
|
|
ht->max_code[i] = 0xFFFF;
|
|
ht->value[i] = 0;
|
|
} else {
|
|
ht->min_code[i] = code;
|
|
ht->max_code[i] = (uint16_t)(code + num - 1);
|
|
ht->value[i] = jj;
|
|
|
|
jj = (uint8_t) (jj + num);
|
|
|
|
code = (uint16_t) (code + num);
|
|
}
|
|
|
|
code = (uint16_t)(code << 1);
|
|
|
|
i++;
|
|
if (i > 15)
|
|
break;
|
|
}
|
|
}
|
|
|
|
static huff_table_t *
|
|
get_huff_table(lws_jpeg_t *j, uint8_t index)
|
|
{
|
|
// 0-1 = DC
|
|
// 2-3 = AC
|
|
switch (index) {
|
|
case 0:
|
|
return &j->huff_tab0;
|
|
case 1:
|
|
return &j->huff_tab1;
|
|
case 2:
|
|
return &j->huff_tab2;
|
|
case 3:
|
|
return &j->huff_tab3;
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static uint8_t *
|
|
get_huff_value(lws_jpeg_t *j, uint8_t index)
|
|
{
|
|
// 0-1 = DC
|
|
// 2-3 = AC
|
|
switch (index) {
|
|
case 0:
|
|
return j->huff_val0;
|
|
case 1:
|
|
return j->huff_val1;
|
|
case 2:
|
|
return j->huff_val2;
|
|
case 3:
|
|
return j->huff_val3;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static uint16_t
|
|
getMaxHuffCodes(uint8_t index)
|
|
{
|
|
return (index < 2) ? 12 : 255;
|
|
}
|
|
|
|
static void createWinogradQuant(lws_jpeg_t *j, int16_t *pq);
|
|
|
|
|
|
static lws_stateful_ret_t
|
|
read_sof_marker(lws_jpeg_t *j)
|
|
{
|
|
lws_stateful_ret_t r;
|
|
uint8_t c;
|
|
|
|
switch (j->fs_sof_phase) {
|
|
case 0:
|
|
r = get_bits16(j, &j->fs_sof_left, 16, 0);
|
|
if (r)
|
|
return r;
|
|
|
|
j->fs_sof_phase++;
|
|
|
|
/* fallthru */
|
|
|
|
case 1:
|
|
r = get_bits8(j, &c, 8, 0);
|
|
if (r)
|
|
return r;
|
|
|
|
if (c != 8) {
|
|
lwsl_jpeg("%s: required 8\n", __func__);
|
|
return LWS_SRET_FATAL + 2;
|
|
}
|
|
|
|
j->fs_sof_phase++;
|
|
|
|
/* fallthru */
|
|
|
|
case 2:
|
|
r = get_bits16(j, &j->image_height, 16, 0);
|
|
if (r)
|
|
return r;
|
|
|
|
if ((!j->image_height) || (j->image_height > PJPG_MAX_HEIGHT)) {
|
|
lwsl_jpeg("%s: image height range\n", __func__);
|
|
return LWS_SRET_FATAL + 3;
|
|
}
|
|
|
|
j->fs_sof_phase++;
|
|
|
|
/* fallthru */
|
|
|
|
case 3:
|
|
r = get_bits16(j, &j->image_width, 16, 0);
|
|
if (r)
|
|
return r;
|
|
|
|
if ((!j->image_width) || (j->image_width > PJPG_MAX_WIDTH)) {
|
|
lwsl_jpeg("%s: image width range\n", __func__);
|
|
return LWS_SRET_FATAL + 4;
|
|
}
|
|
|
|
lwsl_warn("%s: %d x %d\n", __func__, j->image_width, j->image_height);
|
|
|
|
j->fs_sof_phase++;
|
|
|
|
/* fallthru */
|
|
|
|
case 4:
|
|
r = get_bits8(j, &j->frame_comps, 8, 0);
|
|
if (r)
|
|
return r;
|
|
|
|
if (j->frame_comps > 3) {
|
|
lwsl_jpeg("%s: too many comps\n", __func__);
|
|
return LWS_SRET_FATAL + 5;
|
|
}
|
|
|
|
if (j->fs_sof_left !=
|
|
(j->frame_comps + j->frame_comps + j->frame_comps + 8)) {
|
|
lwsl_jpeg("%s: unexpected soft_left\n", __func__);
|
|
return LWS_SRET_FATAL + 6;
|
|
}
|
|
|
|
j->fs_sof_i = 0;
|
|
|
|
j->fs_sof_phase++;
|
|
|
|
/* fallthru */
|
|
|
|
default:
|
|
|
|
while (j->fs_sof_i < j->frame_comps) {
|
|
switch (j->fs_sof_phase) {
|
|
case 5:
|
|
r = get_bits8(j, &j->comp_id[j->fs_sof_i], 8, 0);
|
|
if (r)
|
|
return r;
|
|
|
|
j->fs_sof_phase++;
|
|
|
|
/* fallthru */
|
|
|
|
case 6:
|
|
r = get_bits8(j, &j->comp_h_samp[j->fs_sof_i], 4, 0);
|
|
if (r)
|
|
return r;
|
|
|
|
j->fs_sof_phase++;
|
|
|
|
/* fallthru */
|
|
|
|
case 7:
|
|
r = get_bits8(j, &j->comp_v_samp[j->fs_sof_i], 4, 0);
|
|
if (r)
|
|
return r;
|
|
|
|
j->fs_sof_phase++;
|
|
|
|
/* fallthru */
|
|
|
|
case 8:
|
|
r = get_bits8(j, &j->comp_quant[j->fs_sof_i], 8, 0);
|
|
if (r)
|
|
return r;
|
|
|
|
if (j->comp_quant[j->fs_sof_i] > 1) {
|
|
lwsl_jpeg("%s: comp_quant > 1\n", __func__);
|
|
return LWS_SRET_FATAL + 7;
|
|
}
|
|
break;
|
|
} /* loop switch */
|
|
|
|
j->fs_sof_phase = 5;
|
|
j->fs_sof_i++;
|
|
} /* while */
|
|
|
|
} /* switch */
|
|
|
|
return LWS_SRET_OK;
|
|
}
|
|
|
|
// Read a start of scan (SOS) marker.
|
|
static lws_stateful_ret_t
|
|
read_sos_marker(lws_jpeg_t *j)
|
|
{
|
|
lws_stateful_ret_t r;
|
|
uint8_t c;
|
|
|
|
switch (j->fs_sos_phase) {
|
|
case 0:
|
|
r = get_bits16(j, &j->fs_sos_left, 16, 0);
|
|
if (r)
|
|
return r;
|
|
|
|
j->fs_sos_i = 0;
|
|
j->fs_sos_phase++;
|
|
|
|
/* fallthru */
|
|
|
|
case 1:
|
|
r = get_bits8(j, &j->comp_scan_count, 8, 0);
|
|
if (r)
|
|
return r;
|
|
|
|
j->fs_sos_left = (uint16_t)(j->fs_sos_left - 3);
|
|
|
|
if ((j->fs_sos_left !=
|
|
(j->comp_scan_count + j->comp_scan_count + 3)) ||
|
|
(j->comp_scan_count < 1) ||
|
|
(j->comp_scan_count > PJPG_MAXCOMPSINSCAN)) {
|
|
lwsl_jpeg("%s: scan comps limit\n", __func__);
|
|
return LWS_SRET_FATAL + 8;
|
|
}
|
|
|
|
j->fs_sos_phase++;
|
|
j->fs_sos_phase_loop = 0;
|
|
|
|
/* fallthru */
|
|
|
|
case 2:
|
|
while (j->fs_sos_i < j->comp_scan_count) {
|
|
switch (j->fs_sos_phase_loop) {
|
|
case 0:
|
|
r = get_bits8(j, &j->fs_sos_cc, 8, 0);
|
|
if (r)
|
|
return r;
|
|
j->fs_sos_phase_loop++;
|
|
|
|
/* fallthru */
|
|
|
|
case 1:
|
|
r = get_bits8(j, &j->fs_sos_c, 8, 0);
|
|
if (r)
|
|
return r;
|
|
|
|
j->fs_sos_left = (uint16_t)(j->fs_sos_left - 2);
|
|
|
|
for (c = 0; c < j->frame_comps; c++)
|
|
if (j->fs_sos_cc == j->comp_id[c])
|
|
break;
|
|
|
|
if (c >= j->frame_comps) {
|
|
lwsl_jpeg("%s: SOS comps\n", __func__);
|
|
return LWS_SRET_FATAL + 9;
|
|
}
|
|
|
|
j->comp_list[j->fs_sos_i] = c;
|
|
j->comp_dc[c] = (j->fs_sos_c >> 4) & 15;
|
|
j->comp_ac[c] = (j->fs_sos_c & 15);
|
|
|
|
break;
|
|
}
|
|
|
|
j->fs_sos_i++;
|
|
j->fs_sos_phase_loop = 0;
|
|
}
|
|
|
|
j->fs_sos_phase++;
|
|
|
|
/* fallthru */
|
|
|
|
case 3:
|
|
r = get_bits8(j, &c, 8, 0);
|
|
if (r)
|
|
return r;
|
|
|
|
j->fs_sos_phase++;
|
|
|
|
/* fallthru */
|
|
|
|
case 4:
|
|
r = get_bits8(j, &c, 8, 0);
|
|
if (r)
|
|
return r;
|
|
|
|
j->fs_sos_phase++;
|
|
|
|
/* fallthru */
|
|
|
|
case 5:
|
|
r = get_bits8(j, &c, 4, 0);
|
|
if (r)
|
|
return r;
|
|
|
|
j->fs_sos_phase++;
|
|
|
|
/* fallthru */
|
|
|
|
case 6:
|
|
r = get_bits8(j, &c, 4, 0);
|
|
if (r)
|
|
return r;
|
|
|
|
j->fs_sos_left = (uint16_t)(j->fs_sos_left - 3);
|
|
|
|
j->fs_sos_phase++;
|
|
|
|
/* fallthru */
|
|
|
|
case 7:
|
|
while (j->fs_sos_left) {
|
|
r = get_bits8(j, &c, 8, 0);
|
|
if (r)
|
|
return r;
|
|
|
|
j->fs_sos_left--;
|
|
}
|
|
|
|
j->fs_sos_phase = 0;
|
|
|
|
return LWS_SRET_OK;
|
|
}
|
|
|
|
lwsl_jpeg("%s: SOS marker fail\n", __func__);
|
|
|
|
return LWS_SRET_FATAL + 10;
|
|
}
|
|
|
|
// Process markers. Returns when an SOFx, SOI, EOI, or SOS marker is
|
|
// encountered.
|
|
static lws_stateful_ret_t
|
|
process_markers(lws_jpeg_t *j, uint8_t *pMarker)
|
|
{
|
|
lws_stateful_ret_t r;
|
|
uint16_t w;
|
|
uint8_t c;
|
|
|
|
do {
|
|
if (j->fs_pm_s1 < 2) {
|
|
do {
|
|
if (j->fs_pm_s1 == 0) {
|
|
do {
|
|
r = get_bits8(j, &j->fs_pm_c, 8, 0);
|
|
if (r)
|
|
return r;
|
|
|
|
} while (j->fs_pm_c != 0xFF);
|
|
|
|
j->fs_pm_s1 = 1;
|
|
}
|
|
|
|
do {
|
|
r = get_bits8(j, &j->fs_pm_c, 8, 0);
|
|
if (r)
|
|
return r;
|
|
|
|
} while (j->fs_pm_c == 0xFF);
|
|
|
|
} while (!j->fs_pm_c);
|
|
|
|
j->fs_pm_skip = 0;
|
|
j->fs_pm_i = 0;
|
|
j->fs_pm_s1 = 2;
|
|
}
|
|
|
|
switch (j->fs_pm_c) {
|
|
case PJM_SOF0:
|
|
case PJM_SOF1:
|
|
case PJM_SOF2:
|
|
case PJM_SOF3:
|
|
case PJM_SOF5:
|
|
case PJM_SOF6:
|
|
case PJM_SOF7:
|
|
// case PJM_JPG:
|
|
case PJM_SOF9:
|
|
case PJM_SOF10:
|
|
case PJM_SOF11:
|
|
case PJM_SOF13:
|
|
case PJM_SOF14:
|
|
case PJM_SOF15:
|
|
case PJM_SOI:
|
|
case PJM_EOI:
|
|
case PJM_SOS:
|
|
*pMarker = j->fs_pm_c;
|
|
|
|
goto exit_ok;
|
|
|
|
case PJM_DHT:
|
|
if (!j->fs_pm_skip) { /* step zero */
|
|
r = get_bits16(j, &j->fs_pm_skip_budget, 16, 0);
|
|
if (r)
|
|
return r;
|
|
|
|
if (j->fs_pm_skip_budget < 2) {
|
|
lwsl_jpeg("%s: inadequate skip\n",
|
|
__func__);
|
|
return LWS_SRET_FATAL + 11;
|
|
}
|
|
|
|
j->fs_pm_skip_budget = (uint16_t)(
|
|
j->fs_pm_skip_budget - 2);
|
|
j->fs_pm_skip = 1;
|
|
j->fs_pm_i = 0;
|
|
}
|
|
|
|
while (j->fs_pm_skip_budget) {
|
|
uint8_t index;
|
|
uint16_t totalRead;
|
|
huff_table_t *ht;
|
|
uint8_t *p;
|
|
|
|
switch (j->fs_pm_skip) {
|
|
case 1:
|
|
r = get_bits8(j, &index, 8, 0);
|
|
if (r)
|
|
return r;
|
|
|
|
if (((index & 0x0f) > 1) ||
|
|
((index & 0xf0) > 0x10)) {
|
|
lwsl_jpeg("%s: idx range\n", __func__);
|
|
return LWS_SRET_FATAL + 12;
|
|
}
|
|
|
|
j->fs_pm_ti = (uint8_t)
|
|
(((index >> 3) & 2) + (index & 1));
|
|
j->huff_valid = (uint8_t)(j->huff_valid |
|
|
(1 << j->fs_pm_ti));
|
|
j->fs_pm_count = 0;
|
|
j->fs_pm_i = 0;
|
|
j->fs_pm_skip = 2;
|
|
|
|
/* fallthru */
|
|
|
|
case 2:
|
|
while (j->fs_pm_i <= 15) {
|
|
r = get_bits8(j, &j->fs_pm_bits[
|
|
j->fs_pm_i], 8, 0);
|
|
if (r)
|
|
return r;
|
|
j->fs_pm_count =
|
|
(uint16_t)(
|
|
j->fs_pm_count +
|
|
j->fs_pm_bits[j->fs_pm_i]);
|
|
j->fs_pm_i++;
|
|
}
|
|
|
|
if (j->fs_pm_count >
|
|
getMaxHuffCodes(j->fs_pm_ti)) {
|
|
lwsl_jpeg("%s: huff count\n", __func__);
|
|
return LWS_SRET_FATAL + 13;
|
|
}
|
|
|
|
j->fs_pm_i = 0;
|
|
j->fs_pm_skip = 3;
|
|
|
|
/* fallthru */
|
|
|
|
case 3:
|
|
ht = get_huff_table(j, j->fs_pm_ti);
|
|
p = get_huff_value(j, j->fs_pm_ti);
|
|
|
|
while (j->fs_pm_i < j->fs_pm_count) {
|
|
r = get_bits8(j, &p[j->fs_pm_i], 8, 0);
|
|
if (r)
|
|
return r;
|
|
|
|
j->fs_pm_i++;
|
|
}
|
|
|
|
totalRead = (uint16_t)(1 + 16 +
|
|
j->fs_pm_count);
|
|
|
|
if (j->fs_pm_skip_budget < totalRead) {
|
|
lwsl_jpeg("%s: read budget\n",
|
|
__func__);
|
|
return LWS_SRET_FATAL + 14;
|
|
}
|
|
|
|
j->fs_pm_skip_budget = (uint16_t)
|
|
(j->fs_pm_skip_budget - totalRead);
|
|
|
|
huffCreate(j->fs_pm_bits, ht);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
/* No arithmetic coding support */
|
|
case PJM_DAC:
|
|
lwsl_jpeg("%s: arithmetic coding not supported\n",
|
|
__func__);
|
|
|
|
return LWS_SRET_FATAL;
|
|
|
|
case PJM_DQT:
|
|
switch (j->fs_pm_skip) {
|
|
case 0:
|
|
r = get_bits16(j, &j->fs_pm_skip_budget, 16, 0);
|
|
if (r)
|
|
return r;
|
|
|
|
if (j->fs_pm_skip_budget < 2) {
|
|
lwsl_jpeg("%s: inadequate DQT skip\n",
|
|
__func__);
|
|
|
|
return LWS_SRET_FATAL + 15;
|
|
}
|
|
|
|
j->fs_pm_skip_budget = (uint16_t)
|
|
(j->fs_pm_skip_budget - 2);
|
|
j->fs_pm_skip = 1;
|
|
j->fs_pm_have_n = 0;
|
|
|
|
/* fallthru */
|
|
|
|
case 1:
|
|
while (j->fs_pm_skip_budget) {
|
|
uint16_t totalRead;
|
|
|
|
if (!j->fs_pm_have_n) {
|
|
r = get_bits8(j, &j->fs_pm_n, 8, 0);
|
|
if (r)
|
|
return r;
|
|
if ((j->fs_pm_n & 0xf) > 1) {
|
|
lwsl_jpeg("%s: PM n too big\n",
|
|
__func__);
|
|
return LWS_SRET_FATAL + 16;
|
|
}
|
|
|
|
j->quant_valid = (uint8_t)(
|
|
j->quant_valid |
|
|
((j->fs_pm_n & 0xf) ? 2 : 1));
|
|
|
|
j->fs_pm_i = 0;
|
|
j->fs_pm_have_n = 1;
|
|
}
|
|
|
|
// read quantization entries, in zag order
|
|
while (j->fs_pm_i < 64) {
|
|
switch (j->fs_pm_have_n) {
|
|
case 1:
|
|
r = get_bits8(j, &c, 8, 0);
|
|
if (r)
|
|
return r;
|
|
|
|
j->fs_pm_temp = (uint16_t)c;
|
|
|
|
j->fs_pm_have_n++;
|
|
|
|
/* fallthru */
|
|
|
|
case 2:
|
|
if (j->fs_pm_n >> 4) {
|
|
r = get_bits8(j, &c, 8, 0);
|
|
if (r)
|
|
return r;
|
|
j->fs_pm_temp =
|
|
(uint16_t)(
|
|
(j->fs_pm_temp << 8) + c);
|
|
}
|
|
|
|
if (j->fs_pm_n & 0xf)
|
|
j->quant1[j->fs_pm_i] =
|
|
(int16_t)j->fs_pm_temp;
|
|
else
|
|
j->quant0[j->fs_pm_i] =
|
|
(int16_t)j->fs_pm_temp;
|
|
break;
|
|
}
|
|
|
|
j->fs_pm_i++;
|
|
j->fs_pm_have_n = 1;
|
|
|
|
} /* 64 zags */
|
|
|
|
j->fs_pm_have_n = 0;
|
|
|
|
createWinogradQuant(j,
|
|
(j->fs_pm_n & 0xf) ?
|
|
j->quant1 : j->quant0);
|
|
|
|
totalRead = 64 + 1;
|
|
|
|
if (j->fs_pm_n >> 4)
|
|
totalRead = (uint16_t)(totalRead + 64);
|
|
|
|
if (j->fs_pm_skip_budget < totalRead) {
|
|
lwsl_jpeg("%s: DQT: skip budget"
|
|
" underflow\n", __func__);
|
|
return LWS_SRET_FATAL + 17;
|
|
}
|
|
|
|
j->fs_pm_skip_budget = (uint16_t)
|
|
(j->fs_pm_skip_budget - totalRead);
|
|
} /* while skip_budget / left */
|
|
|
|
j->fs_pm_skip = 0;
|
|
break;
|
|
} /* DQT phase separation */
|
|
break;
|
|
|
|
case PJM_DRI:
|
|
switch (j->fs_pm_i) {
|
|
case 0:
|
|
r = get_bits16(j, &w, 16, 0);
|
|
if (r)
|
|
return r;
|
|
if (w != 4) {
|
|
lwsl_jpeg("%s: DRI wrong val\n", __func__);
|
|
return LWS_SRET_FATAL + 18;
|
|
}
|
|
|
|
j->fs_pm_i = 1;
|
|
|
|
/* fallthru */
|
|
|
|
case 1:
|
|
r = get_bits16(j, &j->restart_interval, 16, 0);
|
|
if (r)
|
|
return r;
|
|
|
|
break;
|
|
}
|
|
break;
|
|
|
|
//case PJM_APP0: /* no need to read the JFIF marker */
|
|
|
|
case PJM_JPG:
|
|
case PJM_RST0: /* no parameters */
|
|
case PJM_RST1:
|
|
case PJM_RST2:
|
|
case PJM_RST3:
|
|
case PJM_RST4:
|
|
case PJM_RST5:
|
|
case PJM_RST6:
|
|
case PJM_RST7:
|
|
case PJM_TEM:
|
|
lwsl_jpeg("%s: bad MCU type\n", __func__);
|
|
|
|
return LWS_SRET_FATAL;
|
|
|
|
default: /* must be DNL, DHP, EXP, APPn, JPGn, COM, or RESn or APP0
|
|
* Used to skip unrecognized markers.
|
|
*/
|
|
|
|
if (!j->fs_pm_skip) {
|
|
r = get_bits16(j, &j->fs_pm_skip_budget, 16, 0);
|
|
if (r)
|
|
return r;
|
|
if (j->fs_pm_skip_budget < 2) {
|
|
lwsl_jpeg("%s: inadequate skip 3\n",
|
|
__func__);
|
|
|
|
return LWS_SRET_FATAL + 19;
|
|
}
|
|
|
|
j->fs_pm_skip_budget = (uint16_t)
|
|
(j->fs_pm_skip_budget - 2);
|
|
j->fs_pm_skip = 1;
|
|
}
|
|
|
|
while (j->fs_pm_skip_budget) {
|
|
uint8_t c;
|
|
r = get_bits8(j, &c, 8, 0);
|
|
if (r)
|
|
return r;
|
|
j->fs_pm_skip_budget--;
|
|
}
|
|
break;
|
|
} /* switch */
|
|
|
|
j->fs_pm_s1 = 0; /* do next_marker() flow next loop */
|
|
|
|
} while(1);
|
|
|
|
exit_ok:
|
|
j->fs_pm_s1 = 0;
|
|
|
|
return LWS_SRET_OK;
|
|
}
|
|
|
|
// Restart interval processing.
|
|
static lws_stateful_ret_t
|
|
interval_restart(lws_jpeg_t *j)
|
|
{
|
|
lws_stateful_ret_t r;
|
|
uint8_t c;
|
|
|
|
switch (j->fs_ir_phase) {
|
|
case 0:
|
|
while (j->fs_ir_i < MARKER_SCAN_LIMIT) {
|
|
r = get_char(j, &c);
|
|
if (r)
|
|
return r;
|
|
|
|
if (c == 0xFF)
|
|
break;
|
|
|
|
j->fs_ir_i++;
|
|
}
|
|
|
|
if (j->fs_ir_i == MARKER_SCAN_LIMIT) {
|
|
/* we judge it unreasonable */
|
|
lwsl_jpeg("%s: scan limit exceeded\n", __func__);
|
|
|
|
return LWS_SRET_FATAL;
|
|
}
|
|
|
|
j->fs_ir_phase++;
|
|
|
|
/* fallthru */
|
|
|
|
case 1:
|
|
while (j->fs_ir_i < MARKER_SCAN_LIMIT) {
|
|
r = get_char(j, &c);
|
|
if (r)
|
|
return r;
|
|
|
|
if (c != 0xFF)
|
|
break;
|
|
j->fs_ir_i++;
|
|
}
|
|
|
|
if (j->fs_ir_i == MARKER_SCAN_LIMIT) {
|
|
/* we judge it unreasonable */
|
|
lwsl_jpeg("%s: scan limit exceeded 2\n", __func__);
|
|
|
|
return LWS_SRET_FATAL + 20;
|
|
}
|
|
|
|
/* Is it the expected marker? If not, something bad happened. */
|
|
if (c != (j->restart_num + PJM_RST0)) {
|
|
lwsl_jpeg("%s: unexpected marker\n", __func__);
|
|
|
|
return LWS_SRET_FATAL + 21;
|
|
}
|
|
|
|
/* Reset each component's DC prediction values. */
|
|
j->last_dc[0] = 0;
|
|
j->last_dc[1] = 0;
|
|
j->last_dc[2] = 0;
|
|
|
|
j->restarts_left = j->restart_interval;
|
|
|
|
j->restart_num = (j->restart_num + 1) & 7;
|
|
|
|
j->bits_left = 8;
|
|
|
|
j->fs_ir_phase++;
|
|
|
|
/* fallthru */
|
|
|
|
case 2:
|
|
r = get_bits8(j, &c, 8, 1);
|
|
if (r)
|
|
return r;
|
|
j->fs_ir_phase++;
|
|
|
|
/* fallthru */
|
|
|
|
case 3:
|
|
r = get_bits8(j, &c, 8, 1);
|
|
if (r)
|
|
return r;
|
|
|
|
j->fs_ir_phase = 0;
|
|
break;
|
|
}
|
|
|
|
return LWS_SRET_OK;
|
|
}
|
|
|
|
static lws_stateful_ret_t
|
|
check_huff_tables(lws_jpeg_t *j)
|
|
{
|
|
uint8_t i;
|
|
|
|
for (i = 0; i < j->comp_scan_count; i++) {
|
|
uint8_t compDCTab = j->comp_dc[j->comp_list[i]];
|
|
uint8_t compACTab = (uint8_t)(j->comp_ac[j->comp_list[i]] + 2);
|
|
|
|
if (((j->huff_valid & (1 << compDCTab)) == 0) ||
|
|
((j->huff_valid & (1 << compACTab)) == 0)) {
|
|
lwsl_jpeg("%s: invalid hufftable\n", __func__);
|
|
|
|
return LWS_SRET_FATAL;
|
|
}
|
|
}
|
|
|
|
return LWS_SRET_OK;
|
|
}
|
|
|
|
static lws_stateful_ret_t
|
|
check_quant_tables(lws_jpeg_t *j)
|
|
{
|
|
uint8_t i;
|
|
|
|
for (i = 0; i < j->comp_scan_count; i++) {
|
|
uint8_t compqMask = j->comp_quant[j->comp_list[i]] ? 2 : 1;
|
|
|
|
if ((j->quant_valid & compqMask) == 0) {
|
|
lwsl_jpeg("%s: invalid quant table\n", __func__);
|
|
|
|
return LWS_SRET_FATAL + 22;
|
|
}
|
|
}
|
|
|
|
return LWS_SRET_OK;
|
|
}
|
|
|
|
static lws_stateful_ret_t
|
|
init_scan(lws_jpeg_t *j)
|
|
{
|
|
lws_stateful_ret_t r;
|
|
uint8_t c;
|
|
|
|
switch (j->fs_is_phase) {
|
|
case 0:
|
|
r = process_markers(j, &c);
|
|
if (r)
|
|
return r;
|
|
|
|
if (c == PJM_EOI) {
|
|
lwsl_jpeg("%s: scan reached EOI\n", __func__);
|
|
|
|
return LWS_SRET_FATAL + 23;
|
|
}
|
|
|
|
if (c != PJM_SOS) {
|
|
lwsl_jpeg("%s: not SOS\n", __func__);
|
|
|
|
return LWS_SRET_FATAL + 24;
|
|
}
|
|
|
|
j->fs_is_phase++;
|
|
|
|
/* fallthru */
|
|
|
|
case 1:
|
|
r = read_sos_marker(j);
|
|
if (r)
|
|
return r;
|
|
|
|
j->fs_is_phase++;
|
|
|
|
/* fallthru */
|
|
|
|
case 2:
|
|
r = check_huff_tables(j);
|
|
if (r)
|
|
return r;
|
|
|
|
r = check_quant_tables(j);
|
|
if (r)
|
|
return r;
|
|
|
|
j->last_dc[0] = 0;
|
|
j->last_dc[1] = 0;
|
|
j->last_dc[2] = 0;
|
|
|
|
if (j->restart_interval) {
|
|
j->restarts_left = j->restart_interval;
|
|
j->restart_num = 0;
|
|
}
|
|
|
|
if (j->bits_left > 0)
|
|
j->stash[j->stashc++] = (uint8_t)j->bits;
|
|
|
|
j->stash[j->stashc++] = (uint8_t) (j->bits >> 8);
|
|
j->bits_left = 8;
|
|
|
|
j->fs_is_phase++;
|
|
|
|
/* fallthru */
|
|
|
|
case 3:
|
|
r = get_bits8(j, &c, 8, 1);
|
|
if (r)
|
|
return r;
|
|
j->fs_is_phase++;
|
|
|
|
/* fallthru */
|
|
|
|
case 4:
|
|
r = get_bits8(j, &c, 8, 1);
|
|
if (r)
|
|
return r;
|
|
break;
|
|
}
|
|
|
|
j->fs_is_phase = 0;
|
|
|
|
return LWS_SRET_OK;
|
|
}
|
|
|
|
static lws_stateful_ret_t
|
|
init_frame(lws_jpeg_t *j)
|
|
{
|
|
switch (j->frame_comps) {
|
|
case 1:
|
|
if ((j->comp_h_samp[0] != 1) || (j->comp_v_samp[0] != 1)) {
|
|
lwsl_jpeg("%s: samps not 1\n", __func__);
|
|
|
|
return LWS_SRET_FATAL + 25;
|
|
}
|
|
|
|
j->scan_type = PJPG_GRAYSCALE;
|
|
|
|
j->mcu_max_blocks = 1;
|
|
j->mcu_org_id[0] = 0;
|
|
|
|
j->mcu_max_size_x = 8;
|
|
j->mcu_max_size_y = 8;
|
|
break;
|
|
|
|
case 3:
|
|
if (((j->comp_h_samp[1] != 1) || (j->comp_v_samp[1] != 1)) ||
|
|
((j->comp_h_samp[2] != 1) || (j->comp_v_samp[2] != 1))) {
|
|
lwsl_jpeg("%s: samps not 1 (2)\n", __func__);
|
|
|
|
return LWS_SRET_FATAL + 26;
|
|
}
|
|
|
|
if ((j->comp_h_samp[0] == 1) && (j->comp_v_samp[0] == 1)) {
|
|
j->scan_type = PJPG_YH1V1;
|
|
|
|
j->mcu_max_blocks = 3;
|
|
j->mcu_org_id[0] = 0;
|
|
j->mcu_org_id[1] = 1;
|
|
j->mcu_org_id[2] = 2;
|
|
|
|
j->mcu_max_size_x = 8;
|
|
j->mcu_max_size_y = 8;
|
|
break;
|
|
}
|
|
|
|
if ((j->comp_h_samp[0] == 1) && (j->comp_v_samp[0] == 2)) {
|
|
j->scan_type = PJPG_YH1V2;
|
|
|
|
j->mcu_max_blocks = 4;
|
|
j->mcu_org_id[0] = 0;
|
|
j->mcu_org_id[1] = 0;
|
|
j->mcu_org_id[2] = 1;
|
|
j->mcu_org_id[3] = 2;
|
|
|
|
j->mcu_max_size_x = 8;
|
|
j->mcu_max_size_y = 16;
|
|
|
|
break;
|
|
}
|
|
|
|
if ((j->comp_h_samp[0] == 2) && (j->comp_v_samp[0] == 1)) {
|
|
j->scan_type = PJPG_YH2V1;
|
|
|
|
j->mcu_max_blocks = 4;
|
|
j->mcu_org_id[0] = 0;
|
|
j->mcu_org_id[1] = 0;
|
|
j->mcu_org_id[2] = 1;
|
|
j->mcu_org_id[3] = 2;
|
|
|
|
j->mcu_max_size_x = 16;
|
|
j->mcu_max_size_y = 8;
|
|
|
|
break;
|
|
}
|
|
|
|
if ((j->comp_h_samp[0] == 2) && (j->comp_v_samp[0] == 2)) {
|
|
j->scan_type = PJPG_YH2V2;
|
|
|
|
j->mcu_max_blocks = 6;
|
|
j->mcu_org_id[0] = 0;
|
|
j->mcu_org_id[1] = 0;
|
|
j->mcu_org_id[2] = 0;
|
|
j->mcu_org_id[3] = 0;
|
|
j->mcu_org_id[4] = 1;
|
|
j->mcu_org_id[5] = 2;
|
|
|
|
j->mcu_max_size_x = 16;
|
|
j->mcu_max_size_y = 16;
|
|
|
|
break;
|
|
}
|
|
|
|
/* fallthru */
|
|
|
|
default:
|
|
lwsl_jpeg("%s: unknown chroma scheme\n", __func__);
|
|
|
|
return LWS_SRET_FATAL;
|
|
}
|
|
|
|
j->mcu_max_row = (uint16_t)
|
|
((j->image_width + (j->mcu_max_size_x - 1)) >>
|
|
((j->mcu_max_size_x == 8) ? 3 : 4));
|
|
j->mcu_max_col = (uint16_t)
|
|
((j->image_height + (j->mcu_max_size_y - 1)) >>
|
|
((j->mcu_max_size_y == 8) ? 3 : 4));
|
|
|
|
j->mcu_count_left_x = j->mcu_max_row;
|
|
j->mcu_count_left_y = j->mcu_max_col;
|
|
|
|
return LWS_SRET_OK;
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
// Winograd IDCT: 5 multiplies per row/col, up to 80 muls for the 2D IDCT
|
|
|
|
#define PJPG_DCT_SCALE_BITS 7
|
|
|
|
#define PJPG_DCT_SCALE (1U << PJPG_DCT_SCALE_BITS)
|
|
|
|
#define PJPG_DESCALE(x) PJPG_ARITH_SHIFT_RIGHT_N_16(((x) + \
|
|
(1 << (PJPG_DCT_SCALE_BITS - 1))), PJPG_DCT_SCALE_BITS)
|
|
|
|
#define PJPG_WFIX(x) ((x) * PJPG_DCT_SCALE + 0.5f)
|
|
|
|
#define PJPG_WINOGRAD_QUANT_SCALE_BITS 10
|
|
|
|
const uint8_t winograd[] = { 128, 178, 178, 167, 246, 167, 151, 232, 232,
|
|
151, 128, 209, 219, 209, 128, 101, 178, 197, 197, 178, 101, 69,
|
|
139, 167, 177, 167, 139, 69, 35, 96, 131, 151, 151, 131, 96, 35,
|
|
49, 91, 118, 128, 118, 91, 49, 46, 81, 101, 101, 81, 46, 42, 69,
|
|
79, 69, 42, 35, 54, 54, 35, 28, 37, 28, 19, 19, 10, };
|
|
|
|
// Multiply quantization matrix by the Winograd IDCT scale factors
|
|
static void
|
|
createWinogradQuant(lws_jpeg_t *j, int16_t *pq)
|
|
{
|
|
uint8_t i;
|
|
|
|
for (i = 0; i < 64; i++) {
|
|
long x = pq[i];
|
|
|
|
x *= winograd[i];
|
|
pq[i] = (int16_t)((x + (1 << (PJPG_WINOGRAD_QUANT_SCALE_BITS -
|
|
PJPG_DCT_SCALE_BITS - 1))) >>
|
|
(PJPG_WINOGRAD_QUANT_SCALE_BITS -
|
|
PJPG_DCT_SCALE_BITS));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* These multiply helper functions are the 4 types of signed multiplies needed
|
|
* by the Winograd IDCT.
|
|
* A smart C compiler will optimize them to use 16x8 = 24 bit muls, if not you
|
|
* may need to tweak these functions or drop to CPU specific inline assembly.
|
|
*/
|
|
|
|
// 1/cos(4*pi/16)
|
|
// 362, 256+106
|
|
static LWS_INLINE int16_t
|
|
imul_b1_b3(int16_t w)
|
|
{
|
|
long x = (w * 362L);
|
|
|
|
x += 128L;
|
|
return (int16_t) (PJPG_ARITH_SHIFT_RIGHT_8_L(x));
|
|
}
|
|
|
|
// 1/cos(6*pi/16)
|
|
// 669, 256+256+157
|
|
static LWS_INLINE int16_t
|
|
imul_b2(int16_t w)
|
|
{
|
|
long x = (w * 669L);
|
|
|
|
x += 128L;
|
|
return (int16_t) (PJPG_ARITH_SHIFT_RIGHT_8_L(x));
|
|
}
|
|
|
|
// 1/cos(2*pi/16)
|
|
// 277, 256+21
|
|
static LWS_INLINE int16_t
|
|
imul_b4(int16_t w)
|
|
{
|
|
long x = (w * 277L);
|
|
|
|
x += 128L;
|
|
return (int16_t) (PJPG_ARITH_SHIFT_RIGHT_8_L(x));
|
|
}
|
|
|
|
// 1/(cos(2*pi/16) + cos(6*pi/16))
|
|
// 196, 196
|
|
static LWS_INLINE int16_t
|
|
imul_b5(int16_t w)
|
|
{
|
|
long x = (w * 196L);
|
|
|
|
x += 128L;
|
|
return (int16_t) (PJPG_ARITH_SHIFT_RIGHT_8_L(x));
|
|
}
|
|
|
|
static LWS_INLINE uint8_t
|
|
clamp(int16_t s)
|
|
{
|
|
if (s < 0)
|
|
return 0;
|
|
|
|
if (s > 255)
|
|
return 255;
|
|
|
|
return (uint8_t)s;
|
|
}
|
|
|
|
static void
|
|
idct_rows(lws_jpeg_t *j)
|
|
{
|
|
int16_t *ps = j->coeffs;
|
|
uint8_t i;
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
if (!(ps[1] | ps[2] | ps[3] | ps[4] | ps[5] | ps[6] | ps[7])) {
|
|
/*
|
|
* Short circuit the 1D IDCT if only the DC component
|
|
* is non-zero
|
|
*/
|
|
int16_t src0 = *ps;
|
|
|
|
*(ps + 1) = src0;
|
|
*(ps + 2) = src0;
|
|
*(ps + 3) = src0;
|
|
*(ps + 4) = src0;
|
|
*(ps + 5) = src0;
|
|
*(ps + 6) = src0;
|
|
*(ps + 7) = src0;
|
|
ps += 8;
|
|
continue;
|
|
}
|
|
|
|
int16_t src4 = *(ps + 5);
|
|
int16_t src7 = *(ps + 3);
|
|
int16_t x4 = (int16_t)(src4 - src7);
|
|
int16_t x7 = (int16_t)(src4 + src7);
|
|
|
|
int16_t src5 = *(ps + 1);
|
|
int16_t src6 = *(ps + 7);
|
|
int16_t x5 = (int16_t)(src5 + src6);
|
|
int16_t x6 = (int16_t)(src5 - src6);
|
|
|
|
int16_t tmp1 = (int16_t)(imul_b5((int16_t)(x4 - x6)));
|
|
int16_t stg26 = (int16_t)(imul_b4(x6) - tmp1);
|
|
|
|
int16_t x24 = (int16_t)(tmp1 - imul_b2(x4));
|
|
|
|
int16_t x15 = (int16_t)(x5 - x7);
|
|
int16_t x17 = (int16_t)(x5 + x7);
|
|
|
|
int16_t tmp2 = (int16_t)(stg26 - x17);
|
|
int16_t tmp3 = (int16_t)(imul_b1_b3(x15) - tmp2);
|
|
int16_t x44 = (int16_t)(tmp3 + x24);
|
|
|
|
int16_t src0 = *(ps + 0);
|
|
int16_t src1 = *(ps + 4);
|
|
int16_t x30 = (int16_t)(src0 + src1);
|
|
int16_t x31 = (int16_t)(src0 - src1);
|
|
|
|
int16_t src2 = *(ps + 2);
|
|
int16_t src3 = *(ps + 6);
|
|
int16_t x12 = (int16_t)(src2 - src3);
|
|
int16_t x13 = (int16_t)(src2 + src3);
|
|
|
|
int16_t x32 = (int16_t)(imul_b1_b3(x12) - x13);
|
|
|
|
int16_t x40 = (int16_t)(x30 + x13);
|
|
int16_t x43 = (int16_t)(x30 - x13);
|
|
int16_t x41 = (int16_t)(x31 + x32);
|
|
int16_t x42 = (int16_t)(x31 - x32);
|
|
|
|
*(ps + 0) = (int16_t)(x40 + x17);
|
|
*(ps + 1) = (int16_t)(x41 + tmp2);
|
|
*(ps + 2) = (int16_t)(x42 + tmp3);
|
|
*(ps + 3) = (int16_t)(x43 - x44);
|
|
*(ps + 4) = (int16_t)(x43 + x44);
|
|
*(ps + 5) = (int16_t)(x42 - tmp3);
|
|
*(ps + 6) = (int16_t)(x41 - tmp2);
|
|
*(ps + 7) = (int16_t)(x40 - x17);
|
|
|
|
ps += 8;
|
|
}
|
|
}
|
|
|
|
static void
|
|
idct_cols(lws_jpeg_t *j)
|
|
{
|
|
int16_t *ps = j->coeffs;
|
|
uint8_t i;
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
if (!(ps[1 * 8] | ps[2 * 8] | ps[3 * 8] | ps[4 * 8]
|
|
| ps[5 * 8] | ps[6 * 8] | ps[7 * 8])) {
|
|
/*
|
|
* Short circuit the 1D IDCT if only the DC component
|
|
* is non-zero
|
|
*/
|
|
uint8_t c = clamp((int16_t)(PJPG_DESCALE(*ps) + 128));
|
|
*(ps + 0 * 8) = c;
|
|
*(ps + 1 * 8) = c;
|
|
*(ps + 2 * 8) = c;
|
|
*(ps + 3 * 8) = c;
|
|
*(ps + 4 * 8) = c;
|
|
*(ps + 5 * 8) = c;
|
|
*(ps + 6 * 8) = c;
|
|
*(ps + 7 * 8) = c;
|
|
ps++;
|
|
continue;
|
|
}
|
|
|
|
int16_t src4 = *(ps + 5 * 8);
|
|
int16_t src7 = *(ps + 3 * 8);
|
|
int16_t x4 = (int16_t)(src4 - src7);
|
|
int16_t x7 = (int16_t)(src4 + src7);
|
|
|
|
int16_t src5 = *(ps + 1 * 8);
|
|
int16_t src6 = *(ps + 7 * 8);
|
|
int16_t x5 = (int16_t)(src5 + src6);
|
|
int16_t x6 = (int16_t)(src5 - src6);
|
|
|
|
int16_t tmp1 = (int16_t)(imul_b5((int16_t)(x4 - x6)));
|
|
int16_t stg26 = (int16_t)(imul_b4(x6) - tmp1);
|
|
|
|
int16_t x24 = (int16_t)(tmp1 - imul_b2(x4));
|
|
|
|
int16_t x15 = (int16_t)(x5 - x7);
|
|
int16_t x17 = (int16_t)(x5 + x7);
|
|
|
|
int16_t tmp2 = (int16_t)(stg26 - x17);
|
|
int16_t tmp3 = (int16_t)(imul_b1_b3(x15) - tmp2);
|
|
int16_t x44 = (int16_t)(tmp3 + x24);
|
|
|
|
int16_t src0 = *(ps + 0 * 8);
|
|
int16_t src1 = *(ps + 4 * 8);
|
|
int16_t x30 = (int16_t)(src0 + src1);
|
|
int16_t x31 = (int16_t)(src0 - src1);
|
|
|
|
int16_t src2 = *(ps + 2 * 8);
|
|
int16_t src3 = *(ps + 6 * 8);
|
|
int16_t x12 = (int16_t)(src2 - src3);
|
|
int16_t x13 = (int16_t)(src2 + src3);
|
|
|
|
int16_t x32 = (int16_t)(imul_b1_b3(x12) - x13);
|
|
|
|
int16_t x40 = (int16_t)(x30 + x13);
|
|
int16_t x43 = (int16_t)(x30 - x13);
|
|
int16_t x41 = (int16_t)(x31 + x32);
|
|
int16_t x42 = (int16_t)(x31 - x32);
|
|
|
|
// descale, convert to unsigned and clamp to 8-bit
|
|
*(ps + 0 * 8) = clamp((int16_t)(PJPG_DESCALE(x40 + x17) + 128));
|
|
*(ps + 1 * 8) = clamp((int16_t)(PJPG_DESCALE(x41 + tmp2) + 128));
|
|
*(ps + 2 * 8) = clamp((int16_t)(PJPG_DESCALE(x42 + tmp3) + 128));
|
|
*(ps + 3 * 8) = clamp((int16_t)(PJPG_DESCALE(x43 - x44) + 128));
|
|
*(ps + 4 * 8) = clamp((int16_t)(PJPG_DESCALE(x43 + x44) + 128));
|
|
*(ps + 5 * 8) = clamp((int16_t)(PJPG_DESCALE(x42 - tmp3) + 128));
|
|
*(ps + 6 * 8) = clamp((int16_t)(PJPG_DESCALE(x41 - tmp2) + 128));
|
|
*(ps + 7 * 8) = clamp((int16_t)(PJPG_DESCALE(x40 - x17) + 128));
|
|
|
|
ps++;
|
|
}
|
|
}
|
|
|
|
static LWS_INLINE uint8_t
|
|
add_clamp(uint8_t a, int16_t b)
|
|
{
|
|
b = (int16_t)(a + b);
|
|
|
|
if (b > 255)
|
|
return 255;
|
|
|
|
if (b < 0)
|
|
return 0;
|
|
|
|
return (uint8_t)b;
|
|
}
|
|
|
|
static LWS_INLINE uint8_t
|
|
sub_clamp(uint8_t a, int16_t b)
|
|
{
|
|
b = (int16_t)(a - b);
|
|
|
|
if (b > 255)
|
|
return 255;
|
|
|
|
if (b < 0)
|
|
return 0;
|
|
|
|
return (uint8_t)b;
|
|
}
|
|
|
|
// 103/256
|
|
//R = Y + 1.402 (Cr-128)
|
|
// 88/256, 183/256
|
|
//G = Y - 0.34414 (Cb-128) - 0.71414 (Cr-128)
|
|
// 198/256
|
|
//B = Y + 1.772 (Cb-128)
|
|
|
|
// Cb upsample and accumulate, 4x4 to 8x8
|
|
static void
|
|
upsample_cb(lws_jpeg_t *j, uint8_t src_ofs, uint8_t dst_ofs)
|
|
{
|
|
// Cb - affects G and B
|
|
uint8_t x, y;
|
|
int16_t *ps = j->coeffs + src_ofs;
|
|
uint8_t *pg = j->mcu_buf_G + dst_ofs;
|
|
uint8_t *pb = j->mcu_buf_B + dst_ofs;
|
|
|
|
for (y = 0; y < 4; y++) {
|
|
for (x = 0; x < 4; x++) {
|
|
uint8_t cb = (uint8_t) *ps++;
|
|
int16_t cbG, cbB;
|
|
|
|
cbG = (int16_t)(((cb * 88U) >> 8U) - 44U);
|
|
pg[0] = sub_clamp(pg[0], cbG);
|
|
pg[1] = sub_clamp(pg[1], cbG);
|
|
pg[8] = sub_clamp(pg[8], cbG);
|
|
pg[9] = sub_clamp(pg[9], cbG);
|
|
|
|
cbB = (int16_t)((cb + ((cb * 198U) >> 8U)) - 227U);
|
|
pb[0] = add_clamp(pb[0], cbB);
|
|
pb[1] = add_clamp(pb[1], cbB);
|
|
pb[8] = add_clamp(pb[8], cbB);
|
|
pb[9] = add_clamp(pb[9], cbB);
|
|
|
|
pg += 2;
|
|
pb += 2;
|
|
}
|
|
|
|
ps = ps - 4 + 8;
|
|
pg = pg - 8 + 16;
|
|
pb = pb - 8 + 16;
|
|
}
|
|
}
|
|
|
|
// Cb upsample and accumulate, 4x8 to 8x8
|
|
static void
|
|
upsample_cbh(lws_jpeg_t *j, uint8_t src_ofs, uint8_t dst_ofs)
|
|
{
|
|
// Cb - affects G and B
|
|
int16_t *ps = j->coeffs + src_ofs;
|
|
uint8_t *pg = j->mcu_buf_G + dst_ofs;
|
|
uint8_t *pb = j->mcu_buf_B + dst_ofs;
|
|
uint8_t x, y;
|
|
|
|
for (y = 0; y < 8; y++) {
|
|
for (x = 0; x < 4; x++) {
|
|
uint8_t cb = (uint8_t) *ps++;
|
|
int16_t cbG, cbB;
|
|
|
|
cbG = (int16_t)(((cb * 88U) >> 8U) - 44U);
|
|
pg[0] = sub_clamp(pg[0], cbG);
|
|
pg[1] = sub_clamp(pg[1], cbG);
|
|
|
|
cbB = (int16_t)((cb + ((cb * 198U) >> 8U)) - 227U);
|
|
pb[0] = add_clamp(pb[0], cbB);
|
|
pb[1] = add_clamp(pb[1], cbB);
|
|
|
|
pg += 2;
|
|
pb += 2;
|
|
}
|
|
|
|
ps = ps - 4 + 8;
|
|
}
|
|
}
|
|
|
|
// Cb upsample and accumulate, 8x4 to 8x8
|
|
static void
|
|
upsample_cbv(lws_jpeg_t *j, uint8_t src_ofs, uint8_t dst_ofs)
|
|
{
|
|
// Cb - affects G and B
|
|
int16_t *ps = j->coeffs + src_ofs;
|
|
uint8_t *pg = j->mcu_buf_G + dst_ofs;
|
|
uint8_t *pb = j->mcu_buf_B + dst_ofs;
|
|
uint8_t x, y;
|
|
|
|
for (y = 0; y < 4; y++) {
|
|
for (x = 0; x < 8; x++) {
|
|
uint8_t cb = (uint8_t) *ps++;
|
|
int16_t cbG, cbB;
|
|
|
|
cbG = (int16_t)(((cb * 88U) >> 8U) - 44U);
|
|
pg[0] = sub_clamp(pg[0], cbG);
|
|
pg[8] = sub_clamp(pg[8], cbG);
|
|
|
|
cbB = (int16_t)((cb + ((cb * 198U) >> 8U)) - 227U);
|
|
pb[0] = add_clamp(pb[0], cbB);
|
|
pb[8] = add_clamp(pb[8], cbB);
|
|
|
|
++pg;
|
|
++pb;
|
|
}
|
|
|
|
pg = pg - 8 + 16;
|
|
pb = pb - 8 + 16;
|
|
}
|
|
}
|
|
|
|
// 103/256
|
|
//R = Y + 1.402 (Cr-128)
|
|
// 88/256, 183/256
|
|
//G = Y - 0.34414 (Cb-128) - 0.71414 (Cr-128)
|
|
// 198/256
|
|
//B = Y + 1.772 (Cb-128)
|
|
|
|
// Cr upsample and accumulate, 4x4 to 8x8
|
|
static void
|
|
upsample_cr(lws_jpeg_t *j, uint8_t src_ofs, uint8_t dst_ofs)
|
|
{
|
|
// Cr - affects R and G
|
|
uint8_t x, y;
|
|
int16_t *ps = j->coeffs + src_ofs;
|
|
uint8_t *pr = j->mcu_buf_R + dst_ofs;
|
|
uint8_t *pg = j->mcu_buf_G + dst_ofs;
|
|
|
|
for (y = 0; y < 4; y++) {
|
|
for (x = 0; x < 4; x++) {
|
|
uint8_t cr = (uint8_t) *ps++;
|
|
int16_t crR, crG;
|
|
|
|
crR = (int16_t)((cr + ((cr * 103U) >> 8U)) - 179);
|
|
pr[0] = add_clamp(pr[0], crR);
|
|
pr[1] = add_clamp(pr[1], crR);
|
|
pr[8] = add_clamp(pr[8], crR);
|
|
pr[9] = add_clamp(pr[9], crR);
|
|
|
|
crG = (int16_t)(((cr * 183U) >> 8U) - 91);
|
|
pg[0] = sub_clamp(pg[0], crG);
|
|
pg[1] = sub_clamp(pg[1], crG);
|
|
pg[8] = sub_clamp(pg[8], crG);
|
|
pg[9] = sub_clamp(pg[9], crG);
|
|
|
|
pr += 2;
|
|
pg += 2;
|
|
}
|
|
|
|
ps = ps - 4 + 8;
|
|
pr = pr - 8 + 16;
|
|
pg = pg - 8 + 16;
|
|
}
|
|
}
|
|
|
|
// Cr upsample and accumulate, 4x8 to 8x8
|
|
static void
|
|
upsample_crh(lws_jpeg_t *j, uint8_t src_ofs, uint8_t dst_ofs)
|
|
{
|
|
// Cr - affects R and G
|
|
uint8_t x, y;
|
|
int16_t *ps = j->coeffs + src_ofs;
|
|
uint8_t *pr = j->mcu_buf_R + dst_ofs;
|
|
uint8_t *pg = j->mcu_buf_G + dst_ofs;
|
|
|
|
for (y = 0; y < 8; y++) {
|
|
for (x = 0; x < 4; x++) {
|
|
uint8_t cr = (uint8_t) *ps++;
|
|
int16_t crR, crG;
|
|
|
|
crR = (int16_t)((cr + ((cr * 103U) >> 8U)) - 179);
|
|
pr[0] = add_clamp(pr[0], crR);
|
|
pr[1] = add_clamp(pr[1], crR);
|
|
|
|
crG = (int16_t)(((cr * 183U) >> 8U) - 91);
|
|
pg[0] = sub_clamp(pg[0], crG);
|
|
pg[1] = sub_clamp(pg[1], crG);
|
|
|
|
pr += 2;
|
|
pg += 2;
|
|
}
|
|
|
|
ps = ps - 4 + 8;
|
|
}
|
|
}
|
|
|
|
// Cr upsample and accumulate, 8x4 to 8x8
|
|
static void
|
|
upsample_crv(lws_jpeg_t *j, uint8_t src_ofs, uint8_t dst_ofs)
|
|
{
|
|
// Cr - affects R and G
|
|
uint8_t x, y;
|
|
int16_t *ps = j->coeffs + src_ofs;
|
|
uint8_t *pr = j->mcu_buf_R + dst_ofs;
|
|
uint8_t *pg = j->mcu_buf_G + dst_ofs;
|
|
|
|
for (y = 0; y < 4; y++) {
|
|
for (x = 0; x < 8; x++) {
|
|
uint8_t cr = (uint8_t) *ps++;
|
|
int16_t crR, crG;
|
|
|
|
crR = (int16_t)((cr + ((cr * 103U) >> 8U)) - 179);
|
|
pr[0] = add_clamp(pr[0], crR);
|
|
pr[8] = add_clamp(pr[8], crR);
|
|
|
|
crG = (int16_t)(((cr * 183U) >> 8U) - 91);
|
|
pg[0] = sub_clamp(pg[0], crG);
|
|
pg[8] = sub_clamp(pg[8], crG);
|
|
|
|
++pr;
|
|
++pg;
|
|
}
|
|
|
|
pr = pr - 8 + 16;
|
|
pg = pg - 8 + 16;
|
|
}
|
|
}
|
|
|
|
// Convert Y to RGB
|
|
static void
|
|
copy_y(lws_jpeg_t *j, uint8_t dst_ofs)
|
|
{
|
|
uint8_t i;
|
|
uint8_t *pRDst = j->mcu_buf_R + dst_ofs;
|
|
uint8_t *pGDst = j->mcu_buf_G + dst_ofs;
|
|
uint8_t *pBDst = j->mcu_buf_B + dst_ofs;
|
|
int16_t *ps = j->coeffs;
|
|
|
|
for (i = 64; i > 0; i--) {
|
|
uint8_t c = (uint8_t) *ps++;
|
|
|
|
*pRDst++ = c;
|
|
*pGDst++ = c;
|
|
*pBDst++ = c;
|
|
}
|
|
}
|
|
|
|
// Cb convert to RGB and accumulate
|
|
static void
|
|
convert_cb(lws_jpeg_t *j, uint8_t dst_ofs)
|
|
{
|
|
uint8_t i;
|
|
uint8_t *pg = j->mcu_buf_G + dst_ofs;
|
|
uint8_t *pb = j->mcu_buf_B + dst_ofs;
|
|
int16_t *ps = j->coeffs;
|
|
|
|
for (i = 64; i > 0; i--) {
|
|
uint8_t cb = (uint8_t) *ps++;
|
|
int16_t cbG, cbB;
|
|
|
|
cbG = (int16_t)(((cb * 88U) >> 8U) - 44U);
|
|
*pg = sub_clamp(pg[0], cbG);
|
|
pg++;
|
|
|
|
cbB = (int16_t)((cb + ((cb * 198U) >> 8U)) - 227U);
|
|
*pb = add_clamp(pb[0], cbB);
|
|
pb++;
|
|
}
|
|
}
|
|
|
|
// Cr convert to RGB and accumulate
|
|
static void
|
|
convert_cr(lws_jpeg_t *j, uint8_t dst_ofs)
|
|
{
|
|
uint8_t i;
|
|
uint8_t *pr = j->mcu_buf_R + dst_ofs;
|
|
uint8_t *pg = j->mcu_buf_G + dst_ofs;
|
|
int16_t *ps = j->coeffs;
|
|
|
|
for (i = 64; i > 0; i--) {
|
|
uint8_t cr = (uint8_t) *ps++;
|
|
int16_t crR, crG;
|
|
|
|
crR = (int16_t)((cr + ((cr * 103U) >> 8U)) - 179);
|
|
*pr = add_clamp(pr[0], crR);
|
|
pr++;
|
|
|
|
crG = (int16_t)(((cr * 183U) >> 8U) - 91);
|
|
*pg = sub_clamp(pg[0], crG);
|
|
pg++;
|
|
}
|
|
}
|
|
|
|
static void
|
|
transform_block(lws_jpeg_t *j, uint8_t mb)
|
|
{
|
|
idct_rows(j);
|
|
idct_cols(j);
|
|
|
|
switch (j->scan_type) {
|
|
case PJPG_GRAYSCALE:
|
|
// MCU size: 1, 1 block per MCU
|
|
copy_y(j, 0);
|
|
break;
|
|
|
|
case PJPG_YH1V1:
|
|
// MCU size: 8x8, 3 blocks per MCU
|
|
switch (mb) {
|
|
case 0:
|
|
copy_y(j, 0);
|
|
break;
|
|
|
|
case 1:
|
|
convert_cb(j, 0);
|
|
break;
|
|
|
|
case 2:
|
|
convert_cr(j, 0);
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case PJPG_YH1V2:
|
|
// MCU size: 8x16, 4 blocks per MCU
|
|
switch (mb) {
|
|
case 0:
|
|
copy_y(j, 0);
|
|
break;
|
|
|
|
case 1:
|
|
copy_y(j, 128);
|
|
break;
|
|
|
|
case 2:
|
|
upsample_cbv(j, 0, 0);
|
|
upsample_cbv(j, 4 * 8, 128);
|
|
break;
|
|
|
|
case 3:
|
|
upsample_crv(j, 0, 0);
|
|
upsample_crv(j, 4 * 8, 128);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case PJPG_YH2V1:
|
|
// MCU size: 16x8, 4 blocks per MCU
|
|
switch (mb) {
|
|
case 0:
|
|
copy_y(j, 0);
|
|
break;
|
|
|
|
case 1:
|
|
copy_y(j, 64);
|
|
break;
|
|
|
|
case 2:
|
|
upsample_cbh(j, 0, 0);
|
|
upsample_cbh(j, 4, 64);
|
|
break;
|
|
|
|
case 3:
|
|
upsample_crh(j, 0, 0);
|
|
upsample_crh(j, 4, 64);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case PJPG_YH2V2:
|
|
// MCU size: 16x16, 6 blocks per MCU
|
|
switch (mb) {
|
|
case 0:
|
|
copy_y(j, 0);
|
|
break;
|
|
|
|
case 1:
|
|
copy_y(j, 64);
|
|
break;
|
|
|
|
case 2:
|
|
copy_y(j, 128);
|
|
break;
|
|
|
|
case 3:
|
|
copy_y(j, 192);
|
|
break;
|
|
|
|
case 4:
|
|
upsample_cb(j, 0, 0);
|
|
upsample_cb(j, 4, 64);
|
|
upsample_cb(j, 4 * 8, 128);
|
|
upsample_cb(j, 4 + 4 * 8, 192);
|
|
break;
|
|
|
|
case 5:
|
|
upsample_cr(j, 0, 0);
|
|
upsample_cr(j, 4, 64);
|
|
upsample_cr(j, 4 * 8, 128);
|
|
upsample_cr(j, 4 + 4 * 8, 192);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static lws_stateful_ret_t
|
|
lws_jpeg_mcu_next(lws_jpeg_t *j)
|
|
{
|
|
unsigned int x, y, row_pitch = (unsigned int)(j->frame_comps *
|
|
j->image_width);
|
|
lws_stateful_ret_t r;
|
|
|
|
if (!j->fs_mcu_phase) {
|
|
if (j->restart_interval) {
|
|
if (j->restarts_left == 0) {
|
|
lwsl_err("%s: process_restart\n", __func__);
|
|
r = interval_restart(j);
|
|
if (r)
|
|
return r;
|
|
}
|
|
j->restarts_left--;
|
|
}
|
|
|
|
j->fs_mcu_mb = 0;
|
|
j->fs_mcu_phase++;
|
|
}
|
|
|
|
while (j->fs_mcu_mb < j->mcu_max_blocks) {
|
|
uint8_t id = j->mcu_org_id[j->fs_mcu_mb];
|
|
uint8_t compDCTab = j->comp_dc[id];
|
|
uint8_t compq = j->comp_quant[id];
|
|
const int16_t *pQ = compq ? j->quant1 : j->quant0;
|
|
uint8_t nexb, compACTab, c;
|
|
uint16_t xr;
|
|
int16_t dc;
|
|
uint8_t s;
|
|
|
|
switch (j->fs_mcu_phase) {
|
|
case 1:
|
|
r = huff_decode(j, &j->fs_mcu_s, compDCTab ?
|
|
&j->huff_tab1 : &j->huff_tab0,
|
|
compDCTab ?
|
|
j->huff_val1 : j->huff_val0);
|
|
if (r)
|
|
return r;
|
|
|
|
if (j->seen_eoi)
|
|
return LWS_SRET_OK;
|
|
|
|
j->fs_mcu_phase++;
|
|
|
|
/* fallthru */
|
|
|
|
case 2:
|
|
xr = 0;
|
|
nexb = j->fs_mcu_s & 0xf;
|
|
if (nexb) {
|
|
if (nexb > 8)
|
|
r = get_bits16(j, &xr, nexb, 1);
|
|
else {
|
|
c = 0;
|
|
r = get_bits8(j, &c, nexb, 1);
|
|
xr = c;
|
|
}
|
|
|
|
if (r)
|
|
return r;
|
|
}
|
|
|
|
dc = (int16_t)(huff_extend(xr, j->fs_mcu_s) +
|
|
j->last_dc[id]);
|
|
j->last_dc[id] = (int16_t)dc;
|
|
j->coeffs[0] = (int16_t)(dc * pQ[0]);
|
|
|
|
j->fs_mcu_k = 1;
|
|
j->fs_mcu_phase_loop = 0;
|
|
j->fs_mcu_phase++;
|
|
|
|
/* fallthru */
|
|
|
|
case 3:
|
|
compACTab = j->comp_ac[id];
|
|
|
|
/* Decode and dequantize AC coefficients */
|
|
while (j->fs_mcu_k < 64) {
|
|
uint16_t exb;
|
|
|
|
if (!j->fs_mcu_phase_loop) {
|
|
r = huff_decode(j, &j->fs_mcu_s,
|
|
compACTab ?
|
|
&j->huff_tab3 : &j->huff_tab2,
|
|
compACTab ?
|
|
j->huff_val3 : j->huff_val2);
|
|
if (j->seen_eoi)
|
|
return LWS_SRET_OK;
|
|
if (r)
|
|
return r;
|
|
|
|
j->fs_mcu_phase_loop = 1;
|
|
}
|
|
|
|
exb = 0;
|
|
nexb = j->fs_mcu_s & 0xf;
|
|
if (nexb) {
|
|
if (nexb > 8)
|
|
r = get_bits16(j, &exb, nexb, 1);
|
|
else {
|
|
c = 0;
|
|
r = get_bits8(j, &c, nexb, 1);
|
|
exb = (uint16_t)c;
|
|
}
|
|
if (r)
|
|
return r;
|
|
}
|
|
|
|
xr = (j->fs_mcu_s >> 4) & 0xf;
|
|
s = j->fs_mcu_s & 15;
|
|
|
|
if (s) {
|
|
if (xr) {
|
|
if ((j->fs_mcu_k + xr) > 63) {
|
|
lwsl_jpeg("%s: k oflow\n",
|
|
__func__);
|
|
|
|
return LWS_SRET_FATAL;
|
|
}
|
|
|
|
while (xr--)
|
|
j->coeffs[(int)ZAG[
|
|
(unsigned int)
|
|
j->fs_mcu_k++]] = 0;
|
|
}
|
|
|
|
j->coeffs[(int)ZAG[(unsigned int)
|
|
j->fs_mcu_k]] = (int16_t)(
|
|
huff_extend(exb, s) *
|
|
pQ[(unsigned int)j->fs_mcu_k]);
|
|
} else {
|
|
if (xr != 15)
|
|
break; /* early loop exit */
|
|
|
|
if (((unsigned int)j->fs_mcu_k + 16) > 64) {
|
|
lwsl_jpeg("%s: k > 64\n", __func__);
|
|
return LWS_SRET_FATAL;
|
|
}
|
|
|
|
for (xr = 16; xr > 0; xr--)
|
|
j->coeffs[(int)ZAG[(unsigned int)
|
|
j->fs_mcu_k++]] = 0;
|
|
|
|
j->fs_mcu_k--;
|
|
}
|
|
|
|
j->fs_mcu_phase_loop = 0;
|
|
j->fs_mcu_k++;
|
|
} /* while k < 64 */
|
|
|
|
while (j->fs_mcu_k < 64)
|
|
j->coeffs[(int)ZAG[(unsigned int)
|
|
j->fs_mcu_k++]] = 0;
|
|
|
|
transform_block(j, j->fs_mcu_mb);
|
|
|
|
break;
|
|
} /* switch */
|
|
|
|
j->fs_mcu_phase = 1;
|
|
j->fs_mcu_mb++;
|
|
} /* while mb */
|
|
|
|
/*
|
|
* Place the MCB into the allocated, MCU-height pixel buffer
|
|
*/
|
|
|
|
uint8_t *dr = j->lines + (j->mcu_ofs_x * j->mcu_max_size_x *
|
|
j->frame_comps);
|
|
|
|
for (y = 0; y < j->mcu_max_size_y; y += 8) {
|
|
unsigned int by_limit = (unsigned int)((unsigned int)j->image_height -
|
|
(unsigned int)((unsigned int)j->mcu_ofs_y *
|
|
(unsigned int)j->mcu_max_size_y +
|
|
(unsigned int)y));
|
|
|
|
if (by_limit > 8)
|
|
by_limit = 8;
|
|
|
|
for (x = 0; x < j->mcu_max_size_x; x += 8) {
|
|
uint8_t *db = dr + (x * j->frame_comps);
|
|
uint8_t src_ofs = (uint8_t)((x * 8U) + (y * 16U));
|
|
const uint8_t *pSrcR = j->mcu_buf_R + src_ofs;
|
|
const uint8_t *pSrcG = j->mcu_buf_G + src_ofs;
|
|
const uint8_t *pSrcB = j->mcu_buf_B + src_ofs;
|
|
unsigned int bx_limit = (unsigned int)(
|
|
(unsigned int)j->image_width -
|
|
(unsigned int)((unsigned int)j->mcu_ofs_x *
|
|
(unsigned int)j->mcu_max_size_x +
|
|
(unsigned int)x));
|
|
unsigned int bx, by;
|
|
|
|
if (bx_limit > 8)
|
|
bx_limit = 8;
|
|
|
|
if (j->scan_type == PJPG_GRAYSCALE) {
|
|
for (by = 0; by < by_limit; by++) {
|
|
uint8_t *pDst = db;
|
|
|
|
for (bx = 0; bx < bx_limit; bx++)
|
|
*pDst++ = *pSrcR++;
|
|
|
|
pSrcR += (8 - bx_limit);
|
|
|
|
db += row_pitch;
|
|
}
|
|
} else {
|
|
for (by = 0; by < by_limit; by++) {
|
|
uint8_t *pDst = db;
|
|
|
|
for (bx = 0; bx < bx_limit; bx++) {
|
|
pDst[0] = *pSrcR++;
|
|
pDst[1] = *pSrcG++;
|
|
pDst[2] = *pSrcB++;
|
|
pDst += 3;
|
|
}
|
|
|
|
pSrcR += (8 - bx_limit);
|
|
pSrcG += (8 - bx_limit);
|
|
pSrcB += (8 - bx_limit);
|
|
|
|
db += row_pitch;
|
|
}
|
|
}
|
|
} /* x */
|
|
|
|
dr += (row_pitch * 8);
|
|
} /* y */
|
|
|
|
if (j->mcu_ofs_x++ == j->mcu_max_row - 1) {
|
|
j->mcu_ofs_x = 0;
|
|
j->mcu_ofs_y++;
|
|
}
|
|
|
|
j->fs_mcu_phase = 0;
|
|
|
|
return LWS_SRET_OK;
|
|
}
|
|
|
|
lws_jpeg_t *
|
|
lws_jpeg_new(void)
|
|
{
|
|
lws_jpeg_t *j = lws_zalloc(sizeof(*j), __func__);
|
|
|
|
if (!j)
|
|
return NULL;
|
|
|
|
return j;
|
|
}
|
|
|
|
void
|
|
lws_jpeg_free(lws_jpeg_t **j)
|
|
{
|
|
lws_free_set_NULL((*j)->lines);
|
|
lws_free_set_NULL(*j);
|
|
}
|
|
|
|
lws_stateful_ret_t
|
|
lws_jpeg_emit_next_line(lws_jpeg_t *j, const uint8_t **ppix,
|
|
const uint8_t **buf, size_t *size, char hold_at_metadata)
|
|
{
|
|
lws_stateful_ret_t r = 0;
|
|
size_t mcu_buf_len;
|
|
|
|
j->inbuf = *buf;
|
|
j->insize = *size;
|
|
j->hold_at_metadata = hold_at_metadata;
|
|
|
|
do {
|
|
switch (j->dstate) {
|
|
|
|
case LWSJDS_FIND_SOI_INIT1:
|
|
j->fs_emit_budget = 4096;
|
|
r = get_bits8(j, &j->fs_emit_lc, 8, 0);
|
|
if (r)
|
|
goto fin;
|
|
j->dstate++;
|
|
|
|
/* fallthru */
|
|
|
|
case LWSJDS_FIND_SOI_INIT2:
|
|
r = get_bits8(j, &j->fs_emit_tc, 8, 0);
|
|
if (r)
|
|
goto fin;
|
|
|
|
if ((j->fs_emit_lc == 0xFF) &&
|
|
(j->fs_emit_tc == PJM_SOI)) {
|
|
j->dstate = LWSJDS_FIND_SOI;
|
|
break;
|
|
}
|
|
|
|
j->dstate++;
|
|
|
|
/* fallthru */
|
|
|
|
case LWSJDS_FIND_SOI:
|
|
|
|
for (;;) {
|
|
|
|
j->fs_emit_lc = j->fs_emit_tc;
|
|
r = get_bits8(j, &j->fs_emit_tc, 8, 0);
|
|
if (r)
|
|
goto fin;
|
|
|
|
if (--j->fs_emit_budget == 0) {
|
|
lwsl_jpeg("%s: SOI emit budget gone\n",
|
|
__func__);
|
|
|
|
return LWS_SRET_FATAL + 28;
|
|
}
|
|
|
|
if (j->fs_emit_lc == 0xFF) {
|
|
if (j->fs_emit_tc == PJM_SOI)
|
|
break;
|
|
if (j->fs_emit_tc == PJM_EOI) {
|
|
lwsl_jpeg("%s: SOI reached EOI\n",
|
|
__func__);
|
|
|
|
return LWS_SRET_FATAL + 29;
|
|
}
|
|
lwsl_jpeg("%s: skipping 0x%02x\n", __func__, j->fs_emit_lc);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check the next character after marker:
|
|
* if it's not 0xFF, it can't be the start of the
|
|
* next marker, so the file is bad
|
|
*/
|
|
|
|
j->fs_emit_tc = (uint8_t)((j->bits >> 8) & 0xFF);
|
|
|
|
if (j->fs_emit_tc != 0xFF) {
|
|
lwsl_jpeg("%s: not marker\n", __func__);
|
|
|
|
return LWS_SRET_FATAL + 30;
|
|
}
|
|
|
|
j->dstate = LWSJDS_FIND_SOF1;
|
|
|
|
/* fallthru */
|
|
|
|
case LWSJDS_FIND_SOF1:
|
|
|
|
r = process_markers(j, &j->fs_emit_c);
|
|
if (r)
|
|
goto fin;
|
|
|
|
if (j->fs_emit_c == PJM_SOF2) {
|
|
lwsl_warn("%s: progressive JPEG not supported\n", __func__);
|
|
return LWS_SRET_FATAL + 31;
|
|
}
|
|
|
|
if (j->fs_emit_c != PJM_SOF0) {
|
|
lwsl_jpeg("%s: not SOF0 (%d)\n", __func__, (int)j->fs_emit_c);
|
|
|
|
return LWS_SRET_FATAL + 31;
|
|
}
|
|
|
|
j->dstate++;
|
|
|
|
/* fallthru */
|
|
|
|
case LWSJDS_FIND_SOF2:
|
|
|
|
r = read_sof_marker(j);
|
|
if (r)
|
|
goto fin;
|
|
|
|
j->dstate++;
|
|
|
|
/* fallthru */
|
|
|
|
case LWSJDS_INIT_FRAME:
|
|
|
|
r = init_frame(j);
|
|
if (r)
|
|
goto fin;
|
|
|
|
j->dstate++;
|
|
|
|
/* fallthru */
|
|
|
|
case LWSJDS_INIT_SCAN:
|
|
|
|
r = init_scan(j);
|
|
if (r)
|
|
goto fin;
|
|
|
|
if (j->hold_at_metadata)
|
|
return LWS_SRET_AWAIT_RETRY;
|
|
|
|
/*
|
|
* 8, or 16 lines of 24-bpp according to MCU height
|
|
*/
|
|
/*
|
|
* row_pitch = (size_t)j->image_witdh * j->frame_comps
|
|
*
|
|
* max dr
|
|
* j->lines + (size_t)(j->mcu_max_row * j->mcu_max_size_x * j->frame_comps) + (size_t)(row_pitch * j->mcu_max_size_y)
|
|
*
|
|
* max db
|
|
* max dr + (size_t)(j->mcu_max_size_x * j->frame_comps) + (size_t)(by_limit * row_pitch)
|
|
*
|
|
* max pDst
|
|
* max db + (size_t)(bx_limit * 3)
|
|
*
|
|
* max by_limit and bx_limit = 8
|
|
*/
|
|
mcu_buf_len = (size_t)(j->mcu_max_row * j->mcu_max_size_x * j->frame_comps)
|
|
+ (size_t)(j->image_width * j->frame_comps * j->mcu_max_size_y)
|
|
+ (size_t)(j->mcu_max_size_x * j->frame_comps)
|
|
+ (size_t)(8 * j->frame_comps * j->image_width)
|
|
+ (size_t)(8 * 3);
|
|
|
|
j->lines = lws_zalloc(mcu_buf_len, __func__);
|
|
if (!j->lines) {
|
|
lwsl_jpeg("%s: OOM\n", __func__);
|
|
return LWS_SRET_FATAL + 32;
|
|
}
|
|
|
|
j->dstate++;
|
|
|
|
/* fallthru */
|
|
|
|
case LWSJDS_DECODE_MCU:
|
|
|
|
/*
|
|
* Once we started dumping the line buffer, continue
|
|
* until we cleared the prepared MCU height
|
|
*/
|
|
if (j->ringy & (j->mcu_max_size_y - 1))
|
|
goto intra;
|
|
|
|
if (j->seen_eoi) {
|
|
r = LWS_SRET_OK;
|
|
goto intra;
|
|
}
|
|
|
|
r = lws_jpeg_mcu_next(j);
|
|
if (j->seen_eoi) {
|
|
r = LWS_SRET_OK;
|
|
goto intra;
|
|
}
|
|
if (r)
|
|
goto fin;
|
|
|
|
j->mcu_count_left_x--;
|
|
if (!j->mcu_count_left_x) {
|
|
j->mcu_count_left_y--;
|
|
|
|
if (j->mcu_count_left_y > 0)
|
|
j->mcu_count_left_x = j->mcu_max_row;
|
|
|
|
if (!j->mcu_count_left_x && !j->mcu_count_left_y) {
|
|
lwsl_notice("%s: seems finished2\n", __func__);
|
|
r = LWS_SRET_NO_FURTHER_IN;
|
|
goto intra;
|
|
}
|
|
|
|
goto intra;
|
|
}
|
|
break;
|
|
}
|
|
} while (1);
|
|
|
|
intra:
|
|
*ppix = j->lines + (((j->ringy++) & (j->mcu_max_size_y - 1)) *
|
|
j->frame_comps * j->image_width);
|
|
|
|
r |= LWS_SRET_WANT_OUTPUT;
|
|
|
|
fin:
|
|
*buf = j->inbuf;
|
|
*size = j->insize;
|
|
|
|
return r;
|
|
}
|
|
|
|
unsigned int
|
|
lws_jpeg_get_width(const lws_jpeg_t *j)
|
|
{
|
|
return j->image_width;
|
|
}
|
|
|
|
unsigned int
|
|
lws_jpeg_get_height(const lws_jpeg_t *j)
|
|
{
|
|
return j->image_height;
|
|
}
|
|
|
|
unsigned int
|
|
lws_jpeg_get_bpp(const lws_jpeg_t *j)
|
|
{
|
|
return j->scan_type == PJPG_GRAYSCALE ? 8 : 24;
|
|
}
|
|
|
|
unsigned int
|
|
lws_jpeg_get_bitdepth(const lws_jpeg_t *j)
|
|
{
|
|
return 8;
|
|
}
|
|
|
|
unsigned int
|
|
lws_jpeg_get_components(const lws_jpeg_t *j)
|
|
{
|
|
return j->scan_type == PJPG_GRAYSCALE ? 1 : 3;
|
|
}
|
|
|
|
unsigned int
|
|
lws_jpeg_get_pixelsize(const lws_jpeg_t *j)
|
|
{
|
|
return j->scan_type == PJPG_GRAYSCALE ? 8 : 24;
|
|
}
|