mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
DirtySDK (EA's Dirty Sockets library) will be used for the LiveAPI implementation, and depends on: EABase, EAThread.
1968 lines
69 KiB
C
1968 lines
69 KiB
C
/*H********************************************************************************/
|
|
/*!
|
|
\File dirtyjpg.c
|
|
|
|
\Description
|
|
Implements JFIF/EXIF image decoding to an output 32bit buffer. This decoder
|
|
does not support progressive, lossless, or arithmetic options.
|
|
|
|
\Notes
|
|
References:
|
|
[1] http://www.w3.org/Graphics/JPEG/jfif3.pdf
|
|
[2] http://www.w3.org/Graphics/JPEG/itu-t81.pdf
|
|
[3] http://class.ece.iastate.edu/ee528/Reading%20material/JPEG_File_Format.pdf
|
|
[4] http://www.bsdg.org/SWAG/GRAPHICS/0143.PAS.html
|
|
[5] http://www.exif.org/Exif2-2.PDF
|
|
|
|
\Copyright
|
|
Copyright (c) 2006 Electronic Arts Inc.
|
|
|
|
\Version 02/23/2006 (jbrookes) First version, based on gshaefer code
|
|
*/
|
|
/********************************************************************************H*/
|
|
|
|
/*** Include files ****************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "DirtySDK/platform.h"
|
|
#include "DirtySDK/dirtysock/dirtylib.h"
|
|
#include "DirtySDK/dirtysock/dirtymem.h"
|
|
|
|
#include "DirtySDK/graph/dirtyjpg.h"
|
|
|
|
/*** Defines **********************************************************************/
|
|
|
|
#define DIRTYJPG_DEBUG (TRUE)
|
|
#define DIRTYJPG_VERBOSE (DIRTYJPG_DEBUG && FALSE)
|
|
#define DIRTYJPG_DEBUG_HUFF (DIRTYJPG_DEBUG && FALSE)
|
|
#define DIRTYJPG_DEBUG_IDCT (DIRTYJPG_DEBUG && FALSE)
|
|
|
|
#if !DIRTYJPG_DEBUG_IDCT
|
|
#define _PrintMatrix16(_pMatrix, _pTitle) {;}
|
|
#endif
|
|
|
|
#define MCU_SIZE (8*8*4) // 8x8 32-bit
|
|
#define MCU_MAXSIZE (MCU_SIZE*4*4) // max size is 4x4 blocks
|
|
|
|
// LLM constants
|
|
#define DCTSIZE 8
|
|
#define CONST_BITS 13
|
|
#define PASS1_BITS 2
|
|
#define FIX_0_298631336 ((int32_t) 2446) /* FIX(0.298631336) */
|
|
#define FIX_0_390180644 ((int32_t) 3196) /* FIX(0.390180644) */
|
|
#define FIX_0_541196100 ((int32_t) 4433) /* FIX(0.541196100) */
|
|
#define FIX_0_765366865 ((int32_t) 6270) /* FIX(0.765366865) */
|
|
#define FIX_0_899976223 ((int32_t) 7373) /* FIX(0.899976223) */
|
|
#define FIX_1_175875602 ((int32_t) 9633) /* FIX(1.175875602) */
|
|
#define FIX_1_501321110 ((int32_t) 12299) /* FIX(1.501321110) */
|
|
#define FIX_1_847759065 ((int32_t) 15137) /* FIX(1.847759065) */
|
|
#define FIX_1_961570560 ((int32_t) 16069) /* FIX(1.961570560) */
|
|
#define FIX_2_053119869 ((int32_t) 16819) /* FIX(2.053119869) */
|
|
#define FIX_2_562915447 ((int32_t) 20995) /* FIX(2.562915447) */
|
|
#define FIX_3_072711026 ((int32_t) 25172) /* FIX(3.072711026) */
|
|
|
|
/*** Type Definitions *************************************************************/
|
|
|
|
//! quantization/matrix
|
|
typedef uint16_t QuantTableT[64];
|
|
|
|
//! huffman decode table head
|
|
typedef struct HuffHeadT
|
|
{
|
|
uint16_t uShift; //!< number of shifts to normalize
|
|
uint16_t uMinCode; //!< min code in this table
|
|
uint16_t uMaxCode; //!< max code in this table
|
|
uint16_t uOffset; //!< offset to value/shift data
|
|
} HuffHeadT;
|
|
|
|
//! huffman decode table
|
|
typedef struct HuffTableT
|
|
{
|
|
HuffHeadT Head[10]; //!< table code range
|
|
uint8_t Code[512]; //!< code lookup
|
|
uint8_t Step[512]; //!< code length lookup
|
|
} HuffTableT;
|
|
|
|
//! component table
|
|
typedef struct CompTableT
|
|
{
|
|
uint16_t uMcuHorz; //!< number of 8x8 blocks horizontally in an mcu
|
|
uint16_t uMcuVert; //!< number of 8x8 blocks vertically in an mcu
|
|
uint16_t uCompHorz; //!< horizontal sampling factor
|
|
uint16_t uCompVert; //!< vertical sampling factor
|
|
uint16_t uExpHorz; //!< horizontal expansion factor
|
|
uint16_t uExpVert; //!< vertical expansion factor
|
|
|
|
uint32_t uStepHorz0; //!< horizontal step
|
|
uint32_t uStepVert0; //!< vertical step
|
|
uint32_t uStepHorz1; //!< horizontal scaled step
|
|
uint32_t uStepVert1; //!< vertical scaled stemp
|
|
uint32_t uStepHorz8; //!< horizontal step*8
|
|
uint32_t uStepVert8; //!< horizontal step*8
|
|
uint32_t uStepHorzM; //!< horizontal mcu step
|
|
uint32_t uStepVertM; //!< vertical mcu step
|
|
|
|
uint16_t uQuantNum; //!< quant table to use
|
|
|
|
HuffTableT *pACHuff; //!< pointer to current AC huffman decode table
|
|
HuffTableT *pDCHuff; //!< pointer to current DC huffman decode table
|
|
|
|
void (*pExpand)(DirtyJpgStateT *pState, struct CompTableT *pCompTable, uint32_t uOutOffset);
|
|
void (*pInvert)(DirtyJpgStateT *pState, struct CompTableT *pCompTable, uint32_t uOutOffset);
|
|
|
|
QuantTableT Matrix; //!< working matrix for decoding this component
|
|
QuantTableT Quant; //!< current quant table
|
|
|
|
} CompTableT;
|
|
|
|
//! module state
|
|
struct DirtyJpgStateT
|
|
{
|
|
// module memory group
|
|
int32_t iMemGroup; //!< module mem group id
|
|
void *pMemGroupUserData; //!< user data associated with mem group
|
|
|
|
int32_t iLastError; //!< previous error
|
|
uint32_t uLastOffset; //!< offset where decoding previously stopped
|
|
|
|
uint16_t uVersion; //!< JFIF file version
|
|
uint16_t uAspectRatio; //!< image aspect ratio
|
|
uint16_t uAspectHorz; //!< horizontal aspect ratio
|
|
uint16_t uAspectVert; //!< vertical aspect ratio
|
|
|
|
uint16_t uImageWidth; //!< width of encoded image
|
|
uint16_t uImageHeight; //!< height of encoded image
|
|
|
|
uint16_t uMcuHorz; //!< maxumum horizontal MCU, out of all components
|
|
uint16_t uMcuHorzM; //!< number of MCUs to cover image horizontall
|
|
uint16_t uMcuVert; //!< maximum vertical MCU, out of all components
|
|
uint16_t uMcuVertM; //!< number of MCUs to cover image vertically
|
|
|
|
uint32_t uBitfield; //!< current bit buffer
|
|
uint32_t uBitsAvail; //!< number of bits in bit buffer
|
|
uint32_t uBitsConsumed; //!< number of bits consumed from bitstream
|
|
uint32_t uBytesRead; //!< number of bytes read from bitstream
|
|
|
|
CompTableT CompTable[4]; //!< component tables
|
|
QuantTableT QuantTable[16]; //!< quantization tables
|
|
HuffTableT HuffTable[8]; //!< huffman decode tables
|
|
uint8_t aMCU[MCU_MAXSIZE]; //!< mcu decode buffer
|
|
|
|
const uint8_t *pCompData; //!< pointer to compressed component data
|
|
|
|
uint8_t *pImageBuf; //!< pointer to output image buffer (allocated by caller)
|
|
const uint8_t *pSrcFinal;
|
|
|
|
uint32_t uBufWidth; //!< width of buffer to decode into
|
|
uint32_t uBufHeight; //!< height of buffer to decode into
|
|
|
|
uint8_t bRestart; //!< if TRUE, a restart marker has been processed
|
|
uint8_t _pad[3];
|
|
};
|
|
|
|
/*** Variables ********************************************************************/
|
|
|
|
//! helper table to negate a value based on its bitsize
|
|
static const uint16_t _ZagAdj_adjust[] =
|
|
{
|
|
0x0000, 0x0001, 0x0003, 0x0007,
|
|
0x000f, 0x001f, 0x003f, 0x007f,
|
|
0x00ff, 0x01ff, 0x03ff, 0x07ff,
|
|
0x0fff, 0x1fff, 0x3fff, 0x7fff
|
|
};
|
|
|
|
//! jpeg zig-zag sequence table
|
|
static const uint16_t _ZagAdj_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 const int16_t _Mult07_14h[] =
|
|
{
|
|
1462, 1451, 1439, 1428, 1416, 1405, 1394, 1382, 1371, 1359, 1348, 1336, 1325, 1314, 1302, 1291,
|
|
1279, 1268, 1256, 1245, 1234, 1222, 1211, 1199, 1188, 1176, 1165, 1154, 1142, 1131, 1119, 1108,
|
|
1096, 1085, 1074, 1062, 1051, 1039, 1028, 1016, 1005, 994, 982, 971, 959, 948, 936, 925,
|
|
914, 902, 891, 879, 868, 856, 845, 834, 822, 811, 799, 788, 776, 765, 754, 742,
|
|
731, 719, 708, 697, 685, 674, 662, 651, 639, 628, 617, 605, 594, 582, 571, 559,
|
|
548, 537, 525, 514, 502, 491, 479, 468, 457, 445, 434, 422, 411, 399, 388, 377,
|
|
365, 354, 342, 331, 319, 308, 297, 285, 274, 262, 251, 239, 228, 217, 205, 194,
|
|
182, 171, 159, 148, 137, 125, 114, 102, 91, 79, 68, 57, 45, 34, 22, 11,
|
|
0, -11, -22, -34, -45, -57, -68, -79, -91, -102, -114, -125, -137, -148, -159, -171,
|
|
-182, -194, -205, -217, -228, -239, -251, -262, -274, -285, -297, -308, -319, -331, -342, -354,
|
|
-365, -377, -388, -399, -411, -422, -434, -445, -457, -468, -479, -491, -502, -514, -525, -537,
|
|
-548, -559, -571, -582, -594, -605, -617, -628, -639, -651, -662, -674, -685, -697, -708, -719,
|
|
-731, -742, -754, -765, -776, -788, -799, -811, -822, -834, -845, -856, -868, -879, -891, -902,
|
|
-914, -925, -936, -948, -959, -971, -982, -994,-1005,-1016,-1028,-1039,-1051,-1062,-1074,-1085,
|
|
-1096,-1108,-1119,-1131,-1142,-1154,-1165,-1176,-1188,-1199,-1211,-1222,-1234,-1245,-1256,-1268,
|
|
-1279,-1291,-1302,-1314,-1325,-1336,-1348,-1359,-1371,-1382,-1394,-1405,-1416,-1428,-1439,-1451
|
|
};
|
|
|
|
static const int16_t _Mult07_14l[] =
|
|
{
|
|
-179, -178, -176, -175, -173, -172, -171, -169, -168, -166, -165, -164, -162, -161, -159, -158,
|
|
-157, -155, -154, -152, -151, -150, -148, -147, -145, -144, -143, -141, -140, -138, -137, -135,
|
|
-134, -133, -131, -130, -128, -127, -126, -124, -123, -121, -120, -119, -117, -116, -114, -113,
|
|
-112, -110, -109, -107, -106, -105, -103, -102, -100, -99, -98, -96, -95, -93, -92, -91,
|
|
-89, -88, -86, -85, -84, -82, -81, -79, -78, -77, -75, -74, -72, -71, -70, -68,
|
|
-67, -65, -64, -63, -61, -60, -58, -57, -56, -54, -53, -51, -50, -49, -47, -46,
|
|
-44, -43, -42, -40, -39, -37, -36, -35, -33, -32, -30, -29, -28, -26, -25, -23,
|
|
-22, -21, -19, -18, -16, -15, -14, -12, -11, -9, -8, -7, -5, -4, -2, -1,
|
|
0, 1, 2, 4, 5, 7, 8, 9, 11, 12, 14, 15, 16, 18, 19, 21,
|
|
22, 23, 25, 26, 28, 29, 30, 32, 33, 35, 36, 37, 39, 40, 42, 43,
|
|
44, 46, 47, 49, 50, 51, 53, 54, 56, 57, 58, 60, 61, 63, 64, 65,
|
|
67, 68, 70, 71, 72, 74, 75, 77, 78, 79, 81, 82, 84, 85, 86, 88,
|
|
89, 91, 92, 93, 95, 96, 98, 99, 100, 102, 103, 105, 106, 107, 109, 110,
|
|
112, 113, 114, 116, 117, 119, 120, 121, 123, 124, 126, 127, 128, 130, 131, 133,
|
|
134, 135, 137, 138, 140, 141, 143, 144, 145, 147, 148, 150, 151, 152, 154, 155,
|
|
157, 158, 159, 161, 162, 164, 165, 166, 168, 169, 171, 172, 173, 175, 176, 178
|
|
};
|
|
|
|
static const int16_t _Mult17_03h[] =
|
|
{
|
|
-226, -225, -223, -221, -219, -217, -216, -214, -212, -210, -209, -207, -205, -203, -202, -200,
|
|
-198, -196, -194, -193, -191, -189, -187, -186, -184, -182, -180, -178, -177, -175, -173, -171,
|
|
-170, -168, -166, -164, -163, -161, -159, -157, -155, -154, -152, -150, -148, -147, -145, -143,
|
|
-141, -139, -138, -136, -134, -132, -131, -129, -127, -125, -124, -122, -120, -118, -116, -115,
|
|
-113, -111, -109, -108, -106, -104, -102, -101, -99, -97, -95, -93, -92, -90, -88, -86,
|
|
-85, -83, -81, -79, -77, -76, -74, -72, -70, -69, -67, -65, -63, -62, -60, -58,
|
|
-56, -54, -53, -51, -49, -47, -46, -44, -42, -40, -38, -37, -35, -33, -31, -30,
|
|
-28, -26, -24, -23, -21, -19, -17, -15, -14, -12, -10, -8, -7, -5, -3, -1,
|
|
0, 1, 3, 5, 7, 8, 10, 12, 14, 15, 17, 19, 21, 23, 24, 26,
|
|
28, 30, 31, 33, 35, 37, 38, 40, 42, 44, 46, 47, 49, 51, 53, 54,
|
|
56, 58, 60, 62, 63, 65, 67, 69, 70, 72, 74, 76, 77, 79, 81, 83,
|
|
85, 86, 88, 90, 92, 93, 95, 97, 99, 101, 102, 104, 106, 108, 109, 111,
|
|
113, 115, 116, 118, 120, 122, 124, 125, 127, 129, 131, 132, 134, 136, 138, 139,
|
|
141, 143, 145, 147, 148, 150, 152, 154, 155, 157, 159, 161, 163, 164, 166, 168,
|
|
170, 171, 173, 175, 177, 178, 180, 182, 184, 186, 187, 189, 191, 193, 194, 196,
|
|
198, 200, 202, 203, 205, 207, 209, 210, 212, 214, 216, 217, 219, 221, 223, 225
|
|
};
|
|
|
|
static const int16_t _Mult17_03l[] =
|
|
{
|
|
704, 699, 693, 688, 682, 677, 671, 666, 660, 655, 649, 644, 638, 633, 627, 622,
|
|
616, 611, 605, 600, 594, 589, 583, 578, 572, 567, 561, 556, 550, 545, 539, 534,
|
|
528, 523, 517, 512, 506, 501, 495, 490, 484, 479, 473, 468, 462, 457, 451, 446,
|
|
440, 434, 429, 423, 418, 412, 407, 401, 396, 390, 385, 379, 374, 368, 363, 357,
|
|
352, 346, 341, 335, 330, 324, 319, 313, 308, 302, 297, 291, 286, 280, 275, 269,
|
|
264, 258, 253, 247, 242, 236, 231, 225, 220, 214, 209, 203, 198, 192, 187, 181,
|
|
176, 170, 165, 159, 154, 148, 143, 137, 132, 126, 121, 115, 110, 104, 99, 93,
|
|
88, 82, 77, 71, 66, 60, 55, 49, 44, 38, 33, 27, 22, 16, 11, 5,
|
|
0, -5, -11, -16, -22, -27, -33, -38, -44, -49, -55, -60, -66, -71, -77, -82,
|
|
-88, -93, -99, -104, -110, -115, -121, -126, -132, -137, -143, -148, -154, -159, -165, -170,
|
|
-176, -181, -187, -192, -198, -203, -209, -214, -220, -225, -231, -236, -242, -247, -253, -258,
|
|
-264, -269, -275, -280, -286, -291, -297, -302, -308, -313, -319, -324, -330, -335, -341, -346,
|
|
-352, -357, -363, -368, -374, -379, -385, -390, -396, -401, -407, -412, -418, -423, -429, -434,
|
|
-440, -446, -451, -457, -462, -468, -473, -479, -484, -490, -495, -501, -506, -512, -517, -523,
|
|
-528, -534, -539, -545, -550, -556, -561, -567, -572, -578, -583, -589, -594, -600, -605, -611,
|
|
-616, -622, -627, -633, -638, -644, -649, -655, -660, -666, -671, -677, -682, -688, -693, -699
|
|
};
|
|
|
|
//! 8-bit table for clamping range [-256...511]->[0...255]
|
|
static const uint8_t _Ranger8[] =
|
|
{
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
|
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
|
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
|
|
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
|
|
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
|
|
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
|
|
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
|
|
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
|
|
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
|
|
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
|
|
0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
|
|
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
|
|
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
|
|
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
|
|
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
|
|
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
|
|
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
};
|
|
|
|
//! offset clamp table to zero
|
|
static const uint8_t *_pRanger8 = _Ranger8+256;
|
|
|
|
|
|
/*** Private Functions ************************************************************/
|
|
|
|
#if DIRTYJPG_DEBUG_IDCT
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _PrintMatrix16
|
|
|
|
\Description
|
|
Print 16bit 8x8 matrix to debug output.
|
|
|
|
\Input *pMatrix - pointer to matrix to print
|
|
\Input *pTitle - output title
|
|
|
|
\Version 03/09/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _PrintMatrix16(int16_t *pMatrix, const char *pTitle)
|
|
{
|
|
int32_t ctr;
|
|
|
|
NetPrintf(("%s\n", pTitle));
|
|
for (ctr = 0; ctr < 64; ctr += 8)
|
|
{
|
|
NetPrintf(("%+10d %+10d %+10d %+10d %+10d %+10d %+10d %+10d\n",
|
|
pMatrix[ctr+0], pMatrix[ctr+1], pMatrix[ctr+2], pMatrix[ctr+3],
|
|
pMatrix[ctr+4], pMatrix[ctr+5], pMatrix[ctr+6], pMatrix[ctr+7]));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
|
|
Huffman decoding routines
|
|
|
|
*/
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _ReadByte
|
|
|
|
\Description
|
|
Read a byte into the bitbuffer, handling any dle bytes.
|
|
|
|
\Input *pState - state ref
|
|
\Input *pData - compressed data base pointer
|
|
\Input *pValue - byte read
|
|
|
|
\Output
|
|
uint32_t - number of bytes consumed
|
|
|
|
\Version 02/23/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static uint32_t _ReadByte(DirtyJpgStateT *pState, const uint8_t *pData, uint8_t *pValue)
|
|
{
|
|
uint32_t uValue, uOffset=0;
|
|
|
|
// protect against reading past the end of the buffer, in which case we just return a null byte
|
|
if ((pData+uOffset) == pState->pSrcFinal)
|
|
{
|
|
*pValue = 0;
|
|
return(1);
|
|
}
|
|
|
|
// read a byte
|
|
*pValue = uValue = pData[uOffset++];
|
|
|
|
// eat marker data
|
|
while (uValue == 0xff)
|
|
{
|
|
uValue = pData[uOffset++];
|
|
pState->uBitsConsumed += 8;
|
|
|
|
// restart marker?
|
|
if ((uValue >= 0xd0) && (uValue <= 0xd7))
|
|
{
|
|
#if DIRTYJPG_DEBUG_HUFF
|
|
NetPrintf(("dirtyjpg: restart marker %d\n", uValue&0xf));
|
|
#endif
|
|
// remember that there was a restart marker
|
|
pState->bRestart = TRUE;
|
|
}
|
|
}
|
|
|
|
return(uOffset);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _ExtractBits
|
|
|
|
\Description
|
|
Read bits from bitstream.
|
|
|
|
\Input *pState - state ref
|
|
\Input *pData - compressed data base pointer
|
|
\Input uBitSize - number of bits to read
|
|
\Input *pSign - positive if leading bit of extracted data is not set, else negative
|
|
\Input bAdvance - if TRUE, advance the bit offset
|
|
|
|
\Output
|
|
uint32_t - extracted value
|
|
|
|
\Version 02/23/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static uint32_t _ExtractBits(DirtyJpgStateT *pState, const uint8_t *pData, uint32_t uBitSize, int32_t *pSign, uint8_t bAdvance)
|
|
{
|
|
uint32_t uValue, uBytesRead;
|
|
uint8_t uByte;
|
|
|
|
// load data into bit buffer
|
|
while ((pState->uBitsAvail < 16) && (pState->bRestart != TRUE))
|
|
{
|
|
uBytesRead = _ReadByte(pState, pData+pState->uBytesRead, &uByte);
|
|
pState->uBytesRead += uBytesRead;
|
|
pState->uBitfield |= uByte << (24-pState->uBitsAvail);
|
|
pState->uBitsAvail += 8;
|
|
}
|
|
|
|
// determine sign of extracted value
|
|
if (pSign)
|
|
{
|
|
*pSign = (pState->uBitfield & 0x80000000) ? -1 : 1;
|
|
}
|
|
|
|
// take top bits
|
|
uValue = pState->uBitfield >> (32-uBitSize);
|
|
if (bAdvance)
|
|
{
|
|
pState->uBitfield <<= uBitSize;
|
|
pState->uBitsAvail -= uBitSize;
|
|
pState->uBitsConsumed += uBitSize;
|
|
}
|
|
|
|
// return extracted value to caller
|
|
return(uValue);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _HuffDecode
|
|
|
|
\Description
|
|
Decode a huffman-encoded value
|
|
|
|
\Input *pState - state ref
|
|
\Input *pHuffTable - pointer to huffman table to use for decode
|
|
\Input *pData - compressed data base pointer
|
|
|
|
\Output
|
|
uint32_t - decoded value
|
|
|
|
\Version 02/23/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static uint32_t _HuffDecode(DirtyJpgStateT *pState, HuffTableT *pHuffTable, const uint8_t *pData)
|
|
{
|
|
HuffHeadT *pHuffHead;
|
|
uint32_t uValue;
|
|
|
|
// extract 16 bits, but don't advance bit offset
|
|
uValue = _ExtractBits(pState, pData, 16, NULL, FALSE);
|
|
|
|
// find table range for this value
|
|
for (pHuffHead = pHuffTable->Head; uValue >= pHuffHead->uMaxCode; pHuffHead += 1)
|
|
{
|
|
if (pHuffHead->uShift == 0)
|
|
{
|
|
NetPrintf(("dirtyjpg: decode error\n"));
|
|
return(0xffffffff);
|
|
}
|
|
}
|
|
#if DIRTYJPG_DECODE_HUFF
|
|
NetPrintf(("dirtyjpg: using range %d\n", pHuffHead-pHuffTable->Head));
|
|
#endif
|
|
|
|
// subtract mincode to get offset into table
|
|
uValue -= pHuffHead->uMinCode;
|
|
// shift down to discard extra bits
|
|
uValue >>= pHuffHead->uShift;
|
|
// offset by the start of this table into code/step buffer
|
|
uValue += pHuffHead->uOffset;
|
|
|
|
// lookup skip value and consume bits
|
|
_ExtractBits(pState, pData, pHuffTable->Step[uValue], NULL, TRUE);
|
|
|
|
#if DIRTYJPG_DEBUG_HUFF
|
|
NetPrintf(("_decode: 0x%02x->s%d,c%02x\n", uValue, pHuffTable->Step[uValue], pHuffTable->Code[uValue]));
|
|
#endif
|
|
|
|
// lookup code value
|
|
uValue = pHuffTable->Code[uValue];
|
|
|
|
// return decoded value to caller
|
|
return(uValue);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _UnpackComp
|
|
|
|
\Description
|
|
Unnpack the next sample into a component record's matrix
|
|
|
|
\Input *pState - state ref
|
|
\Input *pCompTable - pointer to component table
|
|
\Input *pData - compressed data base pointer
|
|
|
|
\Output
|
|
int32_t - negative=error, else success
|
|
|
|
\Version 02/23/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static uint32_t _UnpackComp(DirtyJpgStateT *pState, CompTableT *pCompTable, const uint8_t *pData)
|
|
{
|
|
uint16_t *pMatrix = (uint16_t *)&pCompTable->Matrix;
|
|
uint16_t *pQuant = (uint16_t *)&pCompTable->Quant;
|
|
uint32_t uCodeLen, uElemIdx, uValue=0;
|
|
int32_t iSign = 0;
|
|
|
|
// decode a token (get code length)
|
|
if ((uCodeLen = _HuffDecode(pState, pCompTable->pDCHuff, pData)) == 0xffffffff)
|
|
{
|
|
return(uCodeLen);
|
|
}
|
|
else if (uCodeLen != 0)
|
|
{
|
|
// get the raw value
|
|
uValue = _ExtractBits(pState, pData, uCodeLen, &iSign, TRUE);
|
|
// if non-negative, adjust to negative value
|
|
if (iSign > 0)
|
|
{
|
|
uValue -= _ZagAdj_adjust[uCodeLen];
|
|
}
|
|
#if DIRTYJPG_DEBUG_HUFF
|
|
NetPrintf(("dc=0x%02x\n", uValue));
|
|
#endif
|
|
// quantify the difference
|
|
uValue *= pQuant[0];
|
|
}
|
|
|
|
// accumulate and save the dc coefficient
|
|
uValue += pMatrix[0];
|
|
pMatrix[0] = uValue;
|
|
|
|
// clear the rest of the matrix
|
|
ds_memclr(&pMatrix[1], sizeof(pCompTable->Matrix)-sizeof(pMatrix[0]));
|
|
|
|
// decode ac coefficients
|
|
for (uElemIdx = 1; uElemIdx < 64; )
|
|
{
|
|
// decode ac code
|
|
if ((uValue = _HuffDecode(pState, pCompTable->pACHuff, pData)) == 0xffffffff)
|
|
{
|
|
return(uValue);
|
|
}
|
|
|
|
// get vli length
|
|
if ((uCodeLen = uValue & 0xf) == 0)
|
|
{
|
|
// end of block?
|
|
if (uValue != 0xf0)
|
|
{
|
|
return(0);
|
|
}
|
|
// skip 16 (zero skip)
|
|
uElemIdx += 16;
|
|
}
|
|
else
|
|
{
|
|
// skip count
|
|
uValue >>= 4;
|
|
// advance matrix offset (zero skip)
|
|
uElemIdx = (uElemIdx+uValue)&63;
|
|
// get the raw value and advance the bit offset
|
|
uValue = _ExtractBits(pState, pData, uCodeLen, &iSign, TRUE);
|
|
// if non-negative, adjust to negative value
|
|
if (iSign > 0)
|
|
{
|
|
uValue -= _ZagAdj_adjust[uCodeLen];
|
|
}
|
|
#if DIRTYJPG_DEBUG_HUFF
|
|
NetPrintf(("ac=0x%02x\n", uValue));
|
|
#endif
|
|
// quantify the value
|
|
uValue *= pQuant[uElemIdx];
|
|
// store the quantized value, with zag
|
|
pMatrix[_ZagAdj_zag[uElemIdx]] = uValue;
|
|
// increment index
|
|
uElemIdx += 1;
|
|
}
|
|
}
|
|
|
|
// return success
|
|
return(0);
|
|
}
|
|
|
|
|
|
/*
|
|
|
|
Image coefficient expansion routines
|
|
|
|
*/
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _Expand0
|
|
|
|
\Description
|
|
Expand buffered component scan line data (no expansion).
|
|
|
|
\Input *pState - state ref
|
|
\Input *pCompTable - pointer to component table
|
|
\Input uOutOffset - offset in mcu buffer
|
|
|
|
\Version 02/23/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _Expand0(DirtyJpgStateT *pState, CompTableT *pCompTable, uint32_t uOutOffset)
|
|
{
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _Expand3hv
|
|
|
|
\Description
|
|
Expand buffered component scan line data (2x2 special case).
|
|
|
|
\Input *pState - state ref
|
|
\Input *pCompTable - pointer to component table
|
|
\Input uOutOffset - offset in mcu buffer
|
|
|
|
\Version 02/23/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _Expand3hv(DirtyJpgStateT *pState, CompTableT *pCompTable, uint32_t uOutOffset)
|
|
{
|
|
uint32_t uStepV0 = pCompTable->uStepVert0;
|
|
uint32_t uStepVM = pCompTable->uStepVertM;
|
|
uint32_t uStepH0 = pCompTable->uStepHorz0;
|
|
uint8_t *pData = pState->aMCU + uOutOffset;
|
|
uint8_t *pEndRow, *pEndCol;
|
|
uint32_t uData;
|
|
|
|
for (pEndRow = pData + uStepVM; pData != pEndRow;)
|
|
{
|
|
for (pEndCol = pData + uStepV0; pData != pEndCol;)
|
|
{
|
|
uData = *pData; // get upper left
|
|
pData[uStepV0] = uData; // save lower-left
|
|
pData += uStepH0;
|
|
pData[0] = uData; // save upper-right
|
|
pData[uStepV0] = uData; // save lower-right
|
|
pData += uStepH0;
|
|
}
|
|
|
|
// skip extra col
|
|
pData += uStepV0;
|
|
}
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _ExpandAny
|
|
|
|
\Description
|
|
Expand buffered component scan line data (general case).
|
|
|
|
\Input *pState - state ref
|
|
\Input *pCompTable - pointer to component table
|
|
\Input uOutOffset - offset in mcu buffer
|
|
|
|
\Version 02/23/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _ExpandAny(DirtyJpgStateT *pState, CompTableT *pCompTable, uint32_t uOutOffset)
|
|
{
|
|
uint32_t uData, uExpHorz, uExpVert, uRptCnt;
|
|
uint32_t uStepV0 = pCompTable->uStepVert0;
|
|
uint32_t uStepVM = pCompTable->uStepVertM;
|
|
uint32_t uStepH0 = pCompTable->uStepHorz0;
|
|
uint8_t *pData, *pEndRow, *pEndCol;
|
|
|
|
// get zero-relative vertical expansion factor
|
|
if ((uExpVert = pCompTable->uExpVert - 1) != 0)
|
|
{
|
|
pData = pState->aMCU + uOutOffset;
|
|
for (pEndCol = pData + uStepV0; pData < pEndCol; pData += pCompTable->uStepHorz1)
|
|
{
|
|
uint8_t *pTemp = pData;
|
|
for (pEndRow = pData + uStepVM; pData < pEndRow; )
|
|
{
|
|
// get source data and index past it
|
|
uData = *pData;
|
|
pData += uStepV0;
|
|
|
|
// expand the data
|
|
for (uRptCnt = 0; uRptCnt < uExpVert; uRptCnt += 1, pData += uStepV0)
|
|
{
|
|
*pData = uData;
|
|
}
|
|
}
|
|
pData = pTemp;
|
|
}
|
|
}
|
|
|
|
// get zero-relative horizontal expansion factor
|
|
if ((uExpHorz = pCompTable->uExpHorz - 1) != 0)
|
|
{
|
|
pData = pState->aMCU + uOutOffset;
|
|
for (pEndRow = pData + uStepVM; pData < pEndRow; )
|
|
{
|
|
// get source data and index past it
|
|
uData = *pData;
|
|
pData += uStepH0;
|
|
|
|
// expand the data
|
|
for (uRptCnt = 0; uRptCnt < uExpHorz; uRptCnt += 1, pData += uStepH0)
|
|
{
|
|
*pData = uData;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _TransLLM
|
|
|
|
\Description
|
|
Perform iDCT and quantify (component record matrix -> mcu buffer).
|
|
|
|
\Input *pState - state ref
|
|
\Input *pCompTable - pointer to component table
|
|
\Input uOutOffset - offset in mcu buffer
|
|
|
|
\Version 03/03/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _TransLLM(DirtyJpgStateT *pState, CompTableT *pCompTable, uint32_t uOutOffset)
|
|
{
|
|
uint32_t uStepH1, uStepV1, uRow;
|
|
uint8_t *pDataDest;
|
|
QuantTableT WorkingMatrix;
|
|
#if DIRTYJPG_DEBUG_IDCT
|
|
QuantTableT WorkingMatrix2;
|
|
#endif
|
|
int16_t *pWorking, *pMatrix;
|
|
const uint8_t *pRanger8 = _pRanger8 + 128;
|
|
int32_t tmp0, tmp1, tmp2, tmp3;
|
|
int32_t tmp10, tmp11, tmp12, tmp13;
|
|
int32_t z1, z2, z3, z4, z5;
|
|
|
|
// point to dst data
|
|
if ((pDataDest = pState->aMCU) == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// increment to offset
|
|
pDataDest += uOutOffset;
|
|
|
|
// ref step values
|
|
uStepH1 = pCompTable->uStepHorz1;
|
|
uStepV1 = pCompTable->uStepVert1;
|
|
|
|
// debug input
|
|
_PrintMatrix16((int16_t *)&pCompTable->Matrix, "iDCT input matrix");
|
|
|
|
// do eight columns
|
|
for (uRow = 0, pWorking=(int16_t*)&WorkingMatrix, pMatrix=(int16_t*)&pCompTable->Matrix; uRow < 8; uRow++)
|
|
{
|
|
/* Due to quantization, we will usually find that many of the input
|
|
coefficients are zero, especially the AC terms. We can exploit this
|
|
by short-circuiting the IDCT calculation for any column in which all
|
|
the AC terms are zero. In that case each output is equal to the
|
|
DC coefficient (with scale factor as needed).
|
|
With typical images and quantization tables, half or more of the
|
|
column DCT calculations can be simplified this way. */
|
|
if ((pMatrix[DCTSIZE*1] | pMatrix[DCTSIZE*2] | pMatrix[DCTSIZE*3] |
|
|
pMatrix[DCTSIZE*4] | pMatrix[DCTSIZE*5] | pMatrix[DCTSIZE*6] |
|
|
pMatrix[DCTSIZE*7]) == 0)
|
|
{
|
|
/* AC terms all zero */
|
|
int16_t dcval = (int16_t) (pMatrix[DCTSIZE*0] << PASS1_BITS);
|
|
|
|
pWorking[DCTSIZE*0] = dcval;
|
|
pWorking[DCTSIZE*1] = dcval;
|
|
pWorking[DCTSIZE*2] = dcval;
|
|
pWorking[DCTSIZE*3] = dcval;
|
|
pWorking[DCTSIZE*4] = dcval;
|
|
pWorking[DCTSIZE*5] = dcval;
|
|
pWorking[DCTSIZE*6] = dcval;
|
|
pWorking[DCTSIZE*7] = dcval;
|
|
|
|
pMatrix++; // advance pointers to next column
|
|
pWorking++;
|
|
continue;
|
|
}
|
|
|
|
// Even part: reverse the even part of the forward DCT. The rotator is sqrt(2)*c(-6).
|
|
z2 = pMatrix[DCTSIZE*2];
|
|
z3 = pMatrix[DCTSIZE*6];
|
|
|
|
z1 = (z2 + z3) * FIX_0_541196100;
|
|
tmp2 = z1 + (z3 * -FIX_1_847759065);
|
|
tmp3 = z1 + (z2 * FIX_0_765366865);
|
|
|
|
z2 = pMatrix[DCTSIZE*0];
|
|
z3 = pMatrix[DCTSIZE*4];
|
|
|
|
tmp0 = (z2 + z3) << CONST_BITS;
|
|
tmp1 = (z2 - z3) << CONST_BITS;
|
|
|
|
tmp10 = tmp0 + tmp3;
|
|
tmp13 = tmp0 - tmp3;
|
|
tmp11 = tmp1 + tmp2;
|
|
tmp12 = tmp1 - tmp2;
|
|
|
|
/* Odd part per figure 8; the matrix is unitary and hence its
|
|
transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. */
|
|
tmp0 = pMatrix[DCTSIZE*7];
|
|
tmp1 = pMatrix[DCTSIZE*5];
|
|
tmp2 = pMatrix[DCTSIZE*3];
|
|
tmp3 = pMatrix[DCTSIZE*1];
|
|
|
|
z1 = tmp0 + tmp3;
|
|
z2 = tmp1 + tmp2;
|
|
z3 = tmp0 + tmp2;
|
|
z4 = tmp1 + tmp3;
|
|
z5 = (z3 + z4) * FIX_1_175875602; // sqrt(2) * c3
|
|
|
|
tmp0 = tmp0 * FIX_0_298631336; // sqrt(2) * (-c1+c3+c5-c7)
|
|
tmp1 = tmp1 * FIX_2_053119869; // sqrt(2) * ( c1+c3-c5+c7)
|
|
tmp2 = tmp2 * FIX_3_072711026; // sqrt(2) * ( c1+c3+c5-c7)
|
|
tmp3 = tmp3 * FIX_1_501321110; // sqrt(2) * ( c1+c3-c5-c7)
|
|
z1 = z1 * -FIX_0_899976223; // sqrt(2) * (c7-c3)
|
|
z2 = z2 * -FIX_2_562915447; // sqrt(2) * (-c1-c3)
|
|
z3 = z3 * -FIX_1_961570560; // sqrt(2) * (-c3-c5)
|
|
z4 = z4 * -FIX_0_390180644; // sqrt(2) * (c5-c3)
|
|
|
|
z3 += z5;
|
|
z4 += z5;
|
|
|
|
tmp0 += z1 + z3;
|
|
tmp1 += z2 + z4;
|
|
tmp2 += z2 + z3;
|
|
tmp3 += z1 + z4;
|
|
|
|
// Final output stage: inputs are tmp10..tmp13, tmp0..tmp3
|
|
pWorking[DCTSIZE*0] = (int16_t) ((tmp10 + tmp3) >> (CONST_BITS-PASS1_BITS));
|
|
pWorking[DCTSIZE*7] = (int16_t) ((tmp10 - tmp3) >> (CONST_BITS-PASS1_BITS));
|
|
pWorking[DCTSIZE*1] = (int16_t) ((tmp11 + tmp2) >> (CONST_BITS-PASS1_BITS));
|
|
pWorking[DCTSIZE*6] = (int16_t) ((tmp11 - tmp2) >> (CONST_BITS-PASS1_BITS));
|
|
pWorking[DCTSIZE*2] = (int16_t) ((tmp12 + tmp1) >> (CONST_BITS-PASS1_BITS));
|
|
pWorking[DCTSIZE*5] = (int16_t) ((tmp12 - tmp1) >> (CONST_BITS-PASS1_BITS));
|
|
pWorking[DCTSIZE*3] = (int16_t) ((tmp13 + tmp0) >> (CONST_BITS-PASS1_BITS));
|
|
pWorking[DCTSIZE*4] = (int16_t) ((tmp13 - tmp0) >> (CONST_BITS-PASS1_BITS));
|
|
|
|
pMatrix++; // advance pointers to next column
|
|
pWorking++;
|
|
}
|
|
|
|
// debug first pass
|
|
_PrintMatrix16((int16_t *)&WorkingMatrix, "iDCT pass one");
|
|
|
|
/* Pass 2: process rows from work array, store into output array.
|
|
Note that we must descale the results by a factor of 8 == 2**3,
|
|
and also undo the PASS1_BITS scaling. */
|
|
for (uRow = 0, pWorking=(int16_t*)&WorkingMatrix; uRow < DCTSIZE; uRow++, pDataDest += uStepV1)
|
|
{
|
|
// Even part: reverse the even part of the forward DCT. The rotator is sqrt(2)*c(-6).
|
|
z2 = (int32_t) pWorking[2];
|
|
z3 = (int32_t) pWorking[6];
|
|
|
|
z1 = (z2 + z3) * FIX_0_541196100;
|
|
tmp2 = z1 + (z3 * -FIX_1_847759065);
|
|
tmp3 = z1 + (z2 * FIX_0_765366865);
|
|
|
|
tmp0 = ((int32_t) pWorking[0] + (int32_t) pWorking[4]) << CONST_BITS;
|
|
tmp1 = ((int32_t) pWorking[0] - (int32_t) pWorking[4]) << CONST_BITS;
|
|
|
|
tmp10 = tmp0 + tmp3;
|
|
tmp13 = tmp0 - tmp3;
|
|
tmp11 = tmp1 + tmp2;
|
|
tmp12 = tmp1 - tmp2;
|
|
|
|
/* Odd part per figure 8; the matrix is unitary and hence its
|
|
transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. */
|
|
tmp0 = (int32_t) pWorking[7];
|
|
tmp1 = (int32_t) pWorking[5];
|
|
tmp2 = (int32_t) pWorking[3];
|
|
tmp3 = (int32_t) pWorking[1];
|
|
|
|
z1 = tmp0 + tmp3;
|
|
z2 = tmp1 + tmp2;
|
|
z3 = tmp0 + tmp2;
|
|
z4 = tmp1 + tmp3;
|
|
z5 = (z3 + z4) * FIX_1_175875602; // sqrt(2) * c3
|
|
|
|
tmp0 = tmp0 * FIX_0_298631336; // sqrt(2) * (-c1+c3+c5-c7)
|
|
tmp1 = tmp1 * FIX_2_053119869; // sqrt(2) * ( c1+c3-c5+c7)
|
|
tmp2 = tmp2 * FIX_3_072711026; // sqrt(2) * ( c1+c3+c5-c7)
|
|
tmp3 = tmp3 * FIX_1_501321110; // sqrt(2) * ( c1+c3-c5-c7)
|
|
z1 = z1 * -FIX_0_899976223; // sqrt(2) * (c7-c3)
|
|
z2 = z2 * -FIX_2_562915447; // sqrt(2) * (-c1-c3)
|
|
z3 = z3 * -FIX_1_961570560; // sqrt(2) * (-c3-c5)
|
|
z4 = z4 * -FIX_0_390180644; // sqrt(2) * (c5-c3)
|
|
|
|
z3 += z5;
|
|
z4 += z5;
|
|
|
|
tmp0 += z1 + z3;
|
|
tmp1 += z2 + z4;
|
|
tmp2 += z2 + z3;
|
|
tmp3 += z1 + z4;
|
|
|
|
#if DIRTYJPG_DEBUG_IDCT
|
|
WorkingMatrix2[0+(uRow*8)] = (((tmp10 + tmp3) >> (CONST_BITS+PASS1_BITS+3)));
|
|
WorkingMatrix2[7+(uRow*8)] = (((tmp10 - tmp3) >> (CONST_BITS+PASS1_BITS+3)));
|
|
WorkingMatrix2[1+(uRow*8)] = (((tmp11 + tmp2) >> (CONST_BITS+PASS1_BITS+3)));
|
|
WorkingMatrix2[6+(uRow*8)] = (((tmp11 - tmp2) >> (CONST_BITS+PASS1_BITS+3)));
|
|
WorkingMatrix2[2+(uRow*8)] = (((tmp12 + tmp1) >> (CONST_BITS+PASS1_BITS+3)));
|
|
WorkingMatrix2[5+(uRow*8)] = (((tmp12 - tmp1) >> (CONST_BITS+PASS1_BITS+3)));
|
|
WorkingMatrix2[3+(uRow*8)] = (((tmp13 + tmp0) >> (CONST_BITS+PASS1_BITS+3)));
|
|
WorkingMatrix2[4+(uRow*8)] = (((tmp13 - tmp0) >> (CONST_BITS+PASS1_BITS+3)));
|
|
#endif
|
|
|
|
pDataDest[0*uStepH1] = pRanger8[(((tmp10 + tmp3) >> (CONST_BITS+PASS1_BITS+3)))];
|
|
pDataDest[7*uStepH1] = pRanger8[(((tmp10 - tmp3) >> (CONST_BITS+PASS1_BITS+3)))];
|
|
pDataDest[1*uStepH1] = pRanger8[(((tmp11 + tmp2) >> (CONST_BITS+PASS1_BITS+3)))];
|
|
pDataDest[6*uStepH1] = pRanger8[(((tmp11 - tmp2) >> (CONST_BITS+PASS1_BITS+3)))];
|
|
pDataDest[2*uStepH1] = pRanger8[(((tmp12 + tmp1) >> (CONST_BITS+PASS1_BITS+3)))];
|
|
pDataDest[5*uStepH1] = pRanger8[(((tmp12 - tmp1) >> (CONST_BITS+PASS1_BITS+3)))];
|
|
pDataDest[3*uStepH1] = pRanger8[(((tmp13 + tmp0) >> (CONST_BITS+PASS1_BITS+3)))];
|
|
pDataDest[4*uStepH1] = pRanger8[(((tmp13 - tmp0) >> (CONST_BITS+PASS1_BITS+3)))];
|
|
|
|
pWorking += DCTSIZE; // advance pointer to next row
|
|
}
|
|
|
|
// debug second pass
|
|
_PrintMatrix16((int16_t *)&WorkingMatrix2, "iDCT pass two");
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _InitComp
|
|
|
|
\Description
|
|
Init component table.
|
|
|
|
\Input *pState - state ref
|
|
\Input *pFrame - pointer to SOS (start of scan) frame
|
|
|
|
\Version 03/01/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _InitComp(DirtyJpgStateT *pState, const uint8_t *pFrame)
|
|
{
|
|
CompTableT *pCompTable;
|
|
QuantTableT *pQuantTable;
|
|
uint32_t uExpFunc;
|
|
|
|
// ref comp table
|
|
pCompTable = &pState->CompTable[pFrame[0]];
|
|
|
|
// ref huffman tables
|
|
pCompTable->pDCHuff = &pState->HuffTable[0+(pFrame[1]>>4)];
|
|
pCompTable->pACHuff = &pState->HuffTable[4+(pFrame[1]&3)];
|
|
#if DIRTYJPG_DEBUG_HUFF
|
|
NetPrintf(("dirtyjpg: using dc=%d ac=%d\n", pFrame[1]>>4, pFrame[1]&3));
|
|
#endif
|
|
|
|
// reset differential encoding
|
|
pCompTable->Matrix[0] = 0;
|
|
|
|
// determine expansion function
|
|
uExpFunc = ((pCompTable->uExpVert - 1) << 2) + (pCompTable->uExpHorz - 1);
|
|
|
|
// ref expansion table
|
|
if (uExpFunc == 0)
|
|
{
|
|
pCompTable->pExpand = _Expand0;
|
|
}
|
|
else if (uExpFunc == 0x0101)
|
|
{
|
|
pCompTable->pExpand = _Expand3hv;
|
|
}
|
|
else
|
|
{
|
|
pCompTable->pExpand = _ExpandAny;
|
|
}
|
|
|
|
// get quantify table pointer
|
|
pQuantTable = &pState->QuantTable[pCompTable->uQuantNum];
|
|
|
|
// copy the quantify table
|
|
ds_memcpy_s(pCompTable->Quant, sizeof(pCompTable->Quant), pQuantTable, sizeof(*pQuantTable));
|
|
|
|
// ref inverse function
|
|
pCompTable->pInvert = _TransLLM;
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _PutColor
|
|
|
|
\Description
|
|
Convert an MCU from YCbCr to ARGB ($$tmp assume color)
|
|
|
|
\Input *pState - state ref
|
|
\Input *pCompTable - pointer to component table
|
|
\Input uDstHOff - horizontal mcu offset
|
|
\Input uDstVOff - vertical mcu offset
|
|
|
|
\Version 02/23/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _PutColor(DirtyJpgStateT *pState, CompTableT *pCompTable, uint32_t uDstHOff, uint32_t uDstVOff)
|
|
{
|
|
uint32_t uSrcH, uSrcV, uStepSrcH, uStepSrcV, uMaxSrcH, uMaxSrcV;
|
|
uint32_t uDstH, uDstV, uStepDstH, uStepDstV, uMaxDstH, uMaxDstV;
|
|
const uint8_t *pDataSrc;
|
|
uint8_t *pDataDst;
|
|
int16_t Y, Cb_hi, Cb_lo, Cr_hi, Cr_lo;
|
|
uint32_t R, G, B;
|
|
|
|
// ref source step values
|
|
uStepSrcH = pCompTable->uStepHorz0;
|
|
uMaxSrcH = pCompTable->uStepHorzM;
|
|
uStepSrcV = pCompTable->uStepVert0;
|
|
uMaxSrcV = pCompTable->uStepVertM;
|
|
|
|
// ref dest step values
|
|
uStepDstH = 4; // ARGB
|
|
uMaxDstH = uStepDstH * pState->uBufWidth;
|
|
uStepDstV = uStepDstH * pState->uBufWidth;
|
|
uMaxDstV = uStepDstV * pState->uBufHeight;
|
|
|
|
// scale mcu step values to get pixel offsets
|
|
uDstHOff *= 8*4*pState->uMcuHorz;
|
|
uDstVOff *= uMaxDstH * 8 * pState->uMcuVert;
|
|
|
|
// transform the data
|
|
for (uSrcV = 0, uDstV = uDstVOff; (uSrcV < uMaxSrcV) && (uDstV < uMaxDstV); uSrcV += uStepSrcV, uDstV += uStepDstV)
|
|
{
|
|
for (uSrcH = 0, uDstH = uDstHOff; (uSrcH < uMaxSrcH) && (uDstH < uMaxDstH); uSrcH += uStepSrcH, uDstH += uStepDstH)
|
|
{
|
|
pDataSrc = pState->aMCU + uSrcH + uSrcV;
|
|
pDataDst = pState->pImageBuf + uDstH + uDstV;
|
|
|
|
// get YCbCr components
|
|
Y = pDataSrc[0];
|
|
Cb_hi = _Mult17_03h[pDataSrc[1]]; // get 1.772(Cb-128)
|
|
Cb_lo = _Mult17_03l[pDataSrc[1]]; // get -0.34414(Cb-128)
|
|
Cr_hi = _Mult07_14h[pDataSrc[2]]; // get -0.71414(Cr-128)
|
|
Cr_lo = _Mult07_14l[pDataSrc[2]]; // get 1.402(Cr-128)
|
|
|
|
// convert to RGB
|
|
#ifdef __SNC__
|
|
R = *((uint8_t *)(_pRanger8 + Y + Cr_lo)); // Y + 1.402(Cr-128)
|
|
G = *((uint8_t *)(_pRanger8 + Y + ((Cr_hi + Cb_lo) >> 4))); // Y - 0.71414(Cr-128) - 0.34414(Cb-128)
|
|
B = *((uint8_t *)(_pRanger8 + Y + Cb_hi)); // Y + 1.772(Cb-128)
|
|
#else
|
|
R = _pRanger8[Y + Cr_lo]; // Y + 1.402(Cr-128)
|
|
G = _pRanger8[Y + ((Cr_hi + Cb_lo) >> 4)]; // Y - 0.71414(Cr-128) - 0.34414(Cb-128)
|
|
B = _pRanger8[Y + Cb_hi]; // Y + 1.772(Cb-128)
|
|
#endif
|
|
// store in output buffer
|
|
pDataDst[0] = 0xff;
|
|
pDataDst[1] = R;
|
|
pDataDst[2] = G;
|
|
pDataDst[3] = B;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _DoRST
|
|
|
|
\Description
|
|
Restart decoder after an RST marker was encountered.
|
|
|
|
\Input *pState - state ref
|
|
|
|
\Version 02/23/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _DoRST(DirtyJpgStateT *pState)
|
|
{
|
|
uint32_t uComp;
|
|
|
|
// reset bitbuffer
|
|
pState->uBitsConsumed += pState->uBitsAvail;
|
|
pState->uBitsAvail = 0;
|
|
pState->uBitfield = 0;
|
|
|
|
// reset dc for all components
|
|
for (uComp = 0; uComp < sizeof(pState->CompTable)/sizeof(pState->CompTable[0]); uComp++)
|
|
{
|
|
pState->CompTable[uComp].Matrix[0] = 0;
|
|
}
|
|
|
|
// done the restart, clear the flag
|
|
pState->bRestart = FALSE;
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _GetMCU
|
|
|
|
\Description
|
|
Decode an MCU into MCU buffer.
|
|
|
|
\Input *pState - state ref
|
|
\Input *pFrame - pointer to SOS (start of scan) component frame data
|
|
\Input uCompCount - number of components (must be 1 or 3)
|
|
|
|
\Output
|
|
uint32_t - number of bits consumed from the bitstream, or -1
|
|
|
|
\Version 02/23/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static uint32_t _GetMCU(DirtyJpgStateT *pState, const uint8_t *pFrame, uint32_t uCompCount)
|
|
{
|
|
uint32_t uCompIdx, uCompVert, uCompHorz, uOutOffset;
|
|
CompTableT *pCompTable;
|
|
|
|
// unpack and translate all components
|
|
for (uCompIdx = 0; uCompIdx < uCompCount; uCompIdx += 1)
|
|
{
|
|
// ref comp table
|
|
uint32_t uIndex = pFrame[uCompIdx*2];
|
|
if (uIndex > 3)
|
|
{
|
|
continue;
|
|
}
|
|
pCompTable = &pState->CompTable[uIndex];
|
|
|
|
// unpack and translate component
|
|
for (uOutOffset = uCompIdx, uCompVert = 0; uCompVert < pCompTable->uCompVert; uCompVert += 1)
|
|
{
|
|
// save start of row
|
|
uint32_t uTmpOffset = uOutOffset;
|
|
|
|
// unpack and translate row
|
|
for (uCompHorz = 0; uCompHorz < pCompTable->uCompHorz; uCompHorz += 1)
|
|
{
|
|
// unpack a component into record matrix
|
|
if (_UnpackComp(pState, pCompTable, pState->pCompData) == 0xffffffff)
|
|
{
|
|
return(0xffffffff);
|
|
}
|
|
|
|
// translate the component into mcu buffer
|
|
pCompTable->pInvert(pState, pCompTable, uOutOffset);
|
|
|
|
// increment to next component
|
|
uOutOffset += pCompTable->uStepHorz8;
|
|
}
|
|
|
|
// increment to next output row
|
|
uOutOffset = uTmpOffset + pCompTable->uStepVert8;
|
|
}
|
|
|
|
// expand component
|
|
pCompTable->pExpand(pState, pCompTable, uCompIdx);
|
|
}
|
|
|
|
// if there was a restart, do it now
|
|
if (pState->bRestart)
|
|
{
|
|
_DoRST(pState);
|
|
}
|
|
|
|
// return updated bit offset
|
|
return(pState->uBitsConsumed);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _ParseDQT
|
|
|
|
\Description
|
|
Parse a DQT (quantization) table.
|
|
|
|
\Input *pState - state ref
|
|
\Input *pFrame - pointer to DQT frame data
|
|
\Input *pFinal - end of DQT frame
|
|
|
|
\Output
|
|
int32_t - DIRTYJPG_ERR_*
|
|
|
|
\Version 02/23/2006 (gschaefer)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static int32_t _ParseDQT(DirtyJpgStateT *pState, const uint8_t *pFrame, const uint8_t *pFinal)
|
|
{
|
|
uint32_t uCount;
|
|
uint32_t uIndex;
|
|
uint32_t uMask = 0;
|
|
uint32_t uShift = 0;
|
|
uint32_t uSkip = 1;
|
|
|
|
// process tables
|
|
while (pFrame < pFinal)
|
|
{
|
|
// decode index
|
|
uIndex = pFrame[0] & 15;
|
|
|
|
// if 16-bit encoded, setup to shift first byte and unmask second
|
|
if (pFrame[0] >= 16)
|
|
{
|
|
uMask = 255;
|
|
uShift = 8;
|
|
uSkip = 2;
|
|
}
|
|
|
|
// skip the header
|
|
pFrame += 1;
|
|
|
|
// decode and copy the table
|
|
for (uCount = 0; uCount < 64; ++uCount)
|
|
{
|
|
pState->QuantTable[uIndex][uCount] = (pFrame[0]<<uShift)|(pFrame[1]&uMask);
|
|
pFrame += uSkip;
|
|
}
|
|
}
|
|
|
|
// return error if buffer size is wrong
|
|
return(pFrame == pFinal ? 0 : DIRTYJPG_ERR_BADDQT);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _ParseDHT
|
|
|
|
\Description
|
|
Parse a DHT (huffman) table.
|
|
|
|
\Input *pState - state ref
|
|
\Input *pFrame - pointer to DHT frame data
|
|
\Input *pFinal - end of DHT frame
|
|
|
|
\Output
|
|
int32_t - DIRTYJPG_ERR_*
|
|
|
|
\Version 02/23/2006 (gschaefer)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static int32_t _ParseDHT(DirtyJpgStateT *pState, const uint8_t *pFrame, const uint8_t *pFinal)
|
|
{
|
|
uint32_t uCount, uIndex, uOffset, uSize;
|
|
uint16_t uCode, uShift;
|
|
const uint8_t *pData;
|
|
HuffTableT *pTable;
|
|
HuffHeadT *pHead;
|
|
|
|
while (pFrame < pFinal)
|
|
{
|
|
// ensure all "should be zero bits" are really zero
|
|
if ((pFrame[0] & 0xec) != 0)
|
|
{
|
|
return(DIRTYJPG_ERR_BADDHT);
|
|
}
|
|
|
|
// point to the correct table
|
|
pTable = &pState->HuffTable[(pFrame[0]>>2) | (pFrame[0]&3)];
|
|
|
|
// offset to value/shift lookup
|
|
uOffset = 0;
|
|
|
|
// figure out max code based on first 8 huffman tables (1-8 bits consolidated)
|
|
for (uSize=1, uCode=0; uSize < 8; uSize++)
|
|
{
|
|
uCode += pFrame[uSize];
|
|
uCode += uCode;
|
|
}
|
|
uCode += pFrame[uSize];
|
|
|
|
// setup first table entry
|
|
pHead = &pTable->Head[0];
|
|
pHead->uOffset = uOffset;
|
|
// initial table is larger than the rest (based on code size)
|
|
uOffset += uCode;
|
|
pHead->uMaxCode = uCode;
|
|
pHead->uShift = uSize;
|
|
pHead += 1;
|
|
|
|
// do remaining tables
|
|
while (++uSize < 17)
|
|
{
|
|
uCode += uCode;
|
|
uCode += pFrame[uSize];
|
|
|
|
// only add header if it contains data
|
|
if (pFrame[uSize] > 0)
|
|
{
|
|
pHead->uOffset = uOffset;
|
|
// other tables are sized based on exact code counts
|
|
uOffset += pFrame[uSize];
|
|
pHead->uMaxCode = uCode;
|
|
pHead->uShift = uSize;
|
|
pHead += 1;
|
|
}
|
|
}
|
|
|
|
// terminate header list
|
|
pHead->uShift = 0;
|
|
pHead->uMaxCode = uCode*2;
|
|
pHead->uOffset = uOffset; // so we can calc length of final table
|
|
|
|
// previous max code
|
|
uCode = 0;
|
|
// start with one-bit codes
|
|
uSize = 1;
|
|
// copy value/shift data to all the tables
|
|
pData = pFrame+17;
|
|
for (pHead = &pTable->Head[0]; pHead->uShift != 0; ++pHead)
|
|
{
|
|
// change from size to shift count
|
|
uShift = pHead->uShift;
|
|
pHead->uShift = 16-uShift;
|
|
|
|
// save the previous max code as our min code
|
|
pHead->uMinCode = uCode;
|
|
// get current max code
|
|
uCode = pHead->uMaxCode;
|
|
// right align the bits
|
|
uCode <<= (16-uShift);
|
|
// save the new right-aligned max code
|
|
pHead->uMaxCode = uCode;
|
|
|
|
// fill out the data portion (walk current data offset to next data offset)
|
|
for (uCount = pHead[0].uOffset; uCount < pHead[1].uOffset;)
|
|
{
|
|
// copy over the huffman values
|
|
for (uIndex = 0; uIndex < pFrame[uSize]; ++uIndex)
|
|
{
|
|
// repeat count is variable in table 0 but always 1 for tables 1+
|
|
uint32_t uRepeat = (1 << uShift) >> uSize;
|
|
// copy the huffman value into the appropriate slots
|
|
ds_memset(pTable->Code+uCount, *pData, uRepeat);
|
|
// copy the corresponding shift counts into the matching slots
|
|
ds_memset(pTable->Step+uCount, uSize, uRepeat);
|
|
uCount += uRepeat;
|
|
pData += 1;
|
|
}
|
|
uSize += 1;
|
|
}
|
|
}
|
|
|
|
// move to end of data
|
|
pFrame = pData;
|
|
}
|
|
|
|
// make sure all data was processed
|
|
return(pFrame == pFinal ? 0 : DIRTYJPG_ERR_BADDHT);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _ParseSOF0
|
|
|
|
\Description
|
|
Parse a SOF0 (start of frame 0) frame.
|
|
|
|
\Input *pState - state ref
|
|
\Input *pFrame - pointer to sof0 frame data
|
|
\Input *pFinal - end of sof0 frame
|
|
|
|
\Output
|
|
int32_t - DIRTYJPG_ERR_*
|
|
|
|
\Version 02/23/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static int32_t _ParseSOF0(DirtyJpgStateT *pState, const uint8_t *pFrame, const uint8_t *pFinal)
|
|
{
|
|
uint32_t uComp, uCompCount;
|
|
uint32_t uHorzSampFactor, uVertSampFactor;
|
|
CompTableT *pCompTable;
|
|
const uint8_t *pComp;
|
|
|
|
// validate 8bits per component
|
|
if (pFrame[0] != 8)
|
|
{
|
|
return(DIRTYJPG_ERR_BITDEPTH);
|
|
}
|
|
|
|
pState->uImageHeight = (pFrame[1]<<8)|(pFrame[2]<<0);
|
|
pState->uImageWidth = (pFrame[3]<<8)|(pFrame[4]<<0);
|
|
uCompCount = pFrame[5];
|
|
|
|
// only support three components
|
|
if (uCompCount > 3)
|
|
{
|
|
return(DIRTYJPG_ERR_BADSOF0);
|
|
}
|
|
|
|
// parse components to find image mcu
|
|
for (pComp = pFrame+6, uComp = 0; uComp < uCompCount; uComp++, pComp += 3)
|
|
{
|
|
uVertSampFactor = pComp[1] & 0xf;
|
|
if (pState->uMcuVert < uVertSampFactor)
|
|
{
|
|
pState->uMcuVert = uVertSampFactor;
|
|
}
|
|
uHorzSampFactor = pComp[1] >> 4;
|
|
if (pState->uMcuHorz < uHorzSampFactor)
|
|
{
|
|
pState->uMcuHorz = uHorzSampFactor;
|
|
}
|
|
}
|
|
|
|
// calculate image dimensions in terms of number of mcu blocks
|
|
pState->uMcuHorzM = (pState->uImageWidth + (8*pState->uMcuHorz)-1) / (pState->uMcuHorz*8);
|
|
pState->uMcuVertM = (pState->uImageHeight + (8*pState->uMcuVert)-1) / (pState->uMcuVert*8);
|
|
|
|
// parse components to fill in comp table
|
|
for (pComp = pFrame+6, uComp = 0; uComp < uCompCount; uComp++, pComp += 3)
|
|
{
|
|
// skip invalid tabls
|
|
if (pComp[0] > 3)
|
|
{
|
|
NetPrintf(("dirtyjpg: skipping invalid comp table index\n"));
|
|
continue;
|
|
}
|
|
|
|
// ref comp table
|
|
pCompTable = &pState->CompTable[pComp[0]];
|
|
|
|
// init mcu
|
|
pCompTable->uMcuHorz = pState->uMcuHorz;
|
|
pCompTable->uMcuVert = pState->uMcuVert;
|
|
|
|
// get sampling factors
|
|
pCompTable->uCompVert = pComp[1] & 0xf;
|
|
pCompTable->uCompHorz = pComp[1] >> 4;
|
|
|
|
// compute expansion factors
|
|
pCompTable->uExpHorz = pCompTable->uMcuHorz / pCompTable->uCompHorz;
|
|
pCompTable->uExpVert = pCompTable->uMcuVert / pCompTable->uCompVert;
|
|
|
|
// calculate step based on aligned size
|
|
pCompTable->uStepHorz0 = 4;
|
|
pCompTable->uStepVert0 = pCompTable->uMcuHorz * 8 * 4;
|
|
|
|
// compute scaled sample step sizes
|
|
pCompTable->uStepHorz1 = pCompTable->uStepHorz0 * pCompTable->uExpHorz;
|
|
pCompTable->uStepVert1 = pCompTable->uStepVert0 * pCompTable->uExpVert;
|
|
|
|
// compute step * 8
|
|
pCompTable->uStepHorz8 = pCompTable->uStepHorz1 * 8;
|
|
pCompTable->uStepVert8 = pCompTable->uStepVert1 * 8;
|
|
|
|
// compute mcu step sizes
|
|
pCompTable->uStepHorzM = (pCompTable->uCompHorz * 8) * pCompTable->uStepHorz1;
|
|
pCompTable->uStepVertM = (pCompTable->uCompVert * 8) * pCompTable->uStepVert1;
|
|
|
|
// reference quantization table
|
|
pCompTable->uQuantNum = pComp[2]&0x3;
|
|
}
|
|
|
|
// index past comp table
|
|
pFrame = pComp;
|
|
|
|
return(pFrame == pFinal ? 0 : DIRTYJPG_ERR_BADSOF0);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _ParseSOS
|
|
|
|
\Description
|
|
Parse the SOS (Start of Scan) frame
|
|
|
|
\Input *pState - state ref
|
|
\Input *pFrame - pointer to SOS (start of scan) frame data
|
|
\Input *pFinal - end of frame data
|
|
|
|
\Output
|
|
uint8_t * - pointer past end of frame, or NULL if there was an error
|
|
|
|
\Version 02/23/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static const uint8_t *_ParseSOS(DirtyJpgStateT *pState, const uint8_t *pFrame, const uint8_t *pFinal)
|
|
{
|
|
uint32_t uCompIdx, uCompCount, uMcuH, uMcuV;
|
|
const uint8_t *pCompData;
|
|
|
|
// point to compressed data
|
|
pCompData = pFrame - 2;
|
|
pCompData = pCompData + ((pCompData[0] << 8) | pCompData[1]);
|
|
|
|
// save compressed data
|
|
pState->pCompData = pCompData;
|
|
|
|
// get component count and skip to next byte
|
|
uCompCount = *pFrame++;
|
|
|
|
// init component tables
|
|
for (uCompIdx = 0; uCompIdx < uCompCount; uCompIdx++)
|
|
{
|
|
_InitComp(pState, &pFrame[uCompIdx*2]);
|
|
}
|
|
|
|
// decode mcus and blit them to the output buffer
|
|
for (uMcuV = 0; uMcuV < pState->uMcuVertM; uMcuV++)
|
|
{
|
|
for (uMcuH = 0; uMcuH < pState->uMcuHorzM; uMcuH++)
|
|
{
|
|
// process an MCU
|
|
if (_GetMCU(pState, pFrame, uCompCount) == 0xffffffff)
|
|
{
|
|
return(NULL);
|
|
}
|
|
|
|
// put the colors into buffer
|
|
_PutColor(pState, &pState->CompTable[1], uMcuH, uMcuV);
|
|
}
|
|
}
|
|
|
|
// return number of bytes consumed
|
|
return(pState->pCompData + (pState->uBitsConsumed+7)/8);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _DirtyJpgValidate
|
|
|
|
\Description
|
|
Validate we have a supported jpeg file
|
|
|
|
\Input *pState - state ref
|
|
\Input *pImage - pointer to image data
|
|
\Input uLength - size of image data
|
|
|
|
\Output
|
|
int32_t - DIRTYJPG_ERR_*
|
|
|
|
\Version 07/30/2019 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static int32_t _DirtyJpgValidate(DirtyJpgStateT *pState, const uint8_t *pImage, uint32_t uLength)
|
|
{
|
|
// validate SOI header
|
|
if ((uLength < 16) || (pImage[0] != 0xff) || (pImage[1] != 0xd8))
|
|
{
|
|
return(DIRTYJPG_ERR_TOOSHORT);
|
|
}
|
|
// validate app header
|
|
if ((pImage[2] != 0xff) || ((pImage[3] != 0xe0) && (pImage[3] != 0xe1)))
|
|
{
|
|
return(DIRTYJPG_ERR_NOMAGIC);
|
|
}
|
|
// validate format marker
|
|
if (memcmp(pImage+6, "JFIF", 4) && memcmp(pImage+6, "Exif", 4))
|
|
{
|
|
return(DIRTYJPG_ERR_NOFORMAT);
|
|
}
|
|
return(DIRTYJPG_ERR_NONE);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _DirtyJpgDecodeParse
|
|
|
|
\Description
|
|
Parse the JFIF image data.
|
|
|
|
\Input *pState - state ref
|
|
\Input *pImage - pointer to image data
|
|
\Input uLength - size of image data
|
|
\Input bIdentify - identify if this is a JFIF image or not
|
|
|
|
\Output
|
|
int32_t - DIRTYJPG_ERR_*
|
|
|
|
\Version 02/23/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static int32_t _DirtyJpgDecodeParse(DirtyJpgStateT *pState, const uint8_t *pImage, uint32_t uLength, uint8_t bIdentify)
|
|
{
|
|
int32_t iError = 0;
|
|
const uint8_t *pStart=pImage, *pFinal = pImage+uLength;
|
|
uint8_t uType;
|
|
uint32_t uSize;
|
|
|
|
// save pointer past end of input
|
|
pState->pSrcFinal = pFinal;
|
|
|
|
// decode all the frames
|
|
while (pImage < pFinal)
|
|
{
|
|
// get the frame info
|
|
uType = *pImage++;
|
|
|
|
// gobble alignment byte
|
|
if (uType == 0xff)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// get size (no size for eoi)
|
|
uSize = (uType != 0xd9) ? (pImage[0]<<8)|(pImage[1]<<0) : 0;
|
|
|
|
// validate the frame size
|
|
if ((pImage+uSize) > pFinal)
|
|
{
|
|
iError = DIRTYJPG_ERR_ENDOFDATA;
|
|
break;
|
|
}
|
|
|
|
#if DIRTYJPG_VERBOSE
|
|
NetPrintf(("dirtyjpg: frame=0x%02x size=%d\n", uType, uSize));
|
|
#endif
|
|
|
|
// parse the frame
|
|
switch (uType)
|
|
{
|
|
// app0 frame
|
|
case 0xe0:
|
|
{
|
|
// verify its got JFIF header
|
|
if ((pImage[2] == 'J') && (pImage[3] == 'F') && (pImage[4] == 'I') && (pImage[5] == 'F'))
|
|
{
|
|
// extract the version and make sure we support it
|
|
pState->uVersion = (pImage[7]<<8)|(pImage[8]>>0);
|
|
if ((pState->uVersion < 0x0100) || (pState->uVersion > 0x0102))
|
|
{
|
|
iError = DIRTYJPG_ERR_BADVERS;
|
|
break;
|
|
}
|
|
|
|
// extract & save aspect info
|
|
pState->uAspectRatio = pImage[9];
|
|
pState->uAspectHorz = (pImage[10]<<8)|(pImage[11]<<0);
|
|
pState->uAspectVert = (pImage[12]<<8)|(pImage[13]<<0);
|
|
|
|
// if just doing identification, bail
|
|
if (bIdentify)
|
|
{
|
|
return(DIRTYJPG_ERR_NONE);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
// dqt frame
|
|
case 0xdb:
|
|
{
|
|
iError = _ParseDQT(pState, pImage+2, pImage+uSize);
|
|
break;
|
|
}
|
|
// dht frame
|
|
case 0xc4:
|
|
{
|
|
iError = _ParseDHT(pState, pImage+2, pImage+uSize);
|
|
break;
|
|
}
|
|
// sof0 frame (baseline jpeg)
|
|
case 0xc0:
|
|
{
|
|
iError = _ParseSOF0(pState, pImage+2, pImage+uSize);
|
|
break;
|
|
}
|
|
// sof2 frame (progressive jpeg)
|
|
case 0xc2:
|
|
NetPrintf(("dirtyjpg: warning; SOF2 frame indicates a progressively encoded image, which is not supported\n"));
|
|
iError = DIRTYJPG_ERR_NOSUPPORT;
|
|
break;
|
|
|
|
// sos (start of scan) frame
|
|
case 0xda:
|
|
{
|
|
if (pState->pImageBuf != NULL)
|
|
{
|
|
pImage = _ParseSOS(pState, pImage+2, pImage+uSize);
|
|
if (pImage == NULL)
|
|
{
|
|
iError = DIRTYJPG_ERR_DECODER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
iError = DIRTYJPG_ERR_NOBUFFER;
|
|
pState->uLastOffset = (uint32_t)(pImage-pStart);
|
|
}
|
|
break;
|
|
}
|
|
// skip "no action" frames
|
|
case 0xd9: // eoi frame
|
|
case 0xfe: // com frame
|
|
{
|
|
break;
|
|
}
|
|
|
|
default:
|
|
if ((uType & 0xf0) != 0xe0)
|
|
{
|
|
NetPrintf(("dirtyjpg: ignoring unrecognized frame type 0x%02x\n", uType));
|
|
}
|
|
#if DIRTYJPG_VERBOSE
|
|
else
|
|
{
|
|
NetPrintf(("dirtyjpg: ignoring application-specific frame type 0x%02x\n", uType));
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
// bail if we got an error
|
|
if (iError != 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// move to next record
|
|
pImage += uSize;
|
|
}
|
|
|
|
// save last error
|
|
pState->iLastError = iError;
|
|
return(iError);
|
|
}
|
|
|
|
|
|
/*** Public Functions *************************************************************/
|
|
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function DirtyJpgCreate
|
|
|
|
\Description
|
|
Create the DirtyJpg module state
|
|
|
|
\Output
|
|
DirtyJpgStateT * - pointer to new ref, or NULL if error
|
|
|
|
\Version 02/23/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
DirtyJpgStateT *DirtyJpgCreate(void)
|
|
{
|
|
DirtyJpgStateT *pState;
|
|
int32_t iMemGroup;
|
|
void *pMemGroupUserData;
|
|
|
|
// Query current mem group data
|
|
DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData);
|
|
|
|
// allocate and init module state
|
|
if ((pState = DirtyMemAlloc(sizeof(*pState), DIRTYJPG_MEMID, iMemGroup, pMemGroupUserData)) == NULL)
|
|
{
|
|
NetPrintf(("dirtyjpg: error allocating module state\n"));
|
|
return(NULL);
|
|
}
|
|
ds_memclr(pState, sizeof(*pState));
|
|
pState->iMemGroup = iMemGroup;
|
|
pState->pMemGroupUserData = pMemGroupUserData;
|
|
|
|
// return the state pointer
|
|
return(pState);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function DirtyJpgReset
|
|
|
|
\Description
|
|
Reset the DirtyJpg module state
|
|
|
|
\Input *pState - pointer to module state
|
|
|
|
\Version 02/23/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
void DirtyJpgReset(DirtyJpgStateT *pState)
|
|
{
|
|
pState->uLastOffset = 0;
|
|
|
|
pState->uBitfield = 0;
|
|
pState->uBitsAvail = 0;
|
|
pState->uBitsConsumed = 0;
|
|
pState->uBytesRead = 0;
|
|
|
|
// clear all comp tables
|
|
ds_memclr(pState->CompTable, sizeof(pState->CompTable));
|
|
|
|
// clear all quant tables
|
|
ds_memclr(pState->QuantTable, sizeof(pState->QuantTable));
|
|
|
|
// clear all huffman tables
|
|
ds_memclr(pState->HuffTable, sizeof(pState->HuffTable));
|
|
|
|
// set the image buffer to NULL
|
|
pState->pImageBuf = NULL;
|
|
|
|
pState->bRestart = FALSE;
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function DirtyJpgDestroy
|
|
|
|
\Description
|
|
Destroy the DirtyJpg module
|
|
|
|
\Input *pState - pointer to module state
|
|
|
|
\Version 02/23/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
void DirtyJpgDestroy(DirtyJpgStateT *pState)
|
|
{
|
|
DirtyMemFree(pState, DIRTYJPG_MEMID, pState->iMemGroup, pState->pMemGroupUserData);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function DirtyJpgIdentify
|
|
|
|
\Description
|
|
Identify if input image is a JPG (JFIF) image.
|
|
|
|
\Input *pState - jpg module state
|
|
\Input *pImage - pointer to image data
|
|
\Input uLength - size of image data
|
|
|
|
\Output
|
|
int32_t - TRUE if a JPG, else FALSE
|
|
|
|
\Version 03/09/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
int32_t DirtyJpgIdentify(DirtyJpgStateT *pState, const uint8_t *pImage, uint32_t uLength)
|
|
{
|
|
int32_t iResult;
|
|
|
|
// make sure it's a supported jpeg format
|
|
if ((iResult = _DirtyJpgValidate(pState, pImage, uLength)) == DIRTYJPG_ERR_NONE)
|
|
{
|
|
// run the parser
|
|
iResult = _DirtyJpgDecodeParse(pState, pImage+2, uLength-2, TRUE);
|
|
}
|
|
|
|
// return true if it's a JFIF jpg, else false
|
|
return((iResult == DIRTYJPG_ERR_NOBUFFER) || (iResult == DIRTYJPG_ERR_NONE));
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function DirtyJpgDecodeHeader
|
|
|
|
\Description
|
|
Parse JPG header.
|
|
|
|
\Input *pState - pointer to module state
|
|
\Input *pJpgHdr - pointer to jpg header
|
|
\Input *pImage - pointer to image buf
|
|
\Input uLength - size of input image
|
|
|
|
\Output
|
|
int32_t - DIRTYJPG_ERR_*
|
|
|
|
\Version 02/23/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
int32_t DirtyJpgDecodeHeader(DirtyJpgStateT *pState, DirtyJpgHdrT *pJpgHdr, const uint8_t *pImage, uint32_t uLength)
|
|
{
|
|
int32_t iResult;
|
|
|
|
// reset internal state
|
|
DirtyJpgReset(pState);
|
|
|
|
// make sure it's a supported jpeg format
|
|
if ((iResult = _DirtyJpgValidate(pState, pImage, uLength)) < 0)
|
|
{
|
|
return(iResult);
|
|
}
|
|
|
|
// run the parser -- we should get a no buffer error
|
|
if ((iResult = _DirtyJpgDecodeParse(pState, pImage+2, uLength-2, FALSE)) == DIRTYJPG_ERR_NOBUFFER)
|
|
{
|
|
// this is expected during header parsing -- save info and return no error
|
|
pJpgHdr->pImageData = pImage;
|
|
pJpgHdr->uLength = uLength;
|
|
pJpgHdr->uWidth = pState->uImageWidth;
|
|
pJpgHdr->uHeight = pState->uImageHeight;
|
|
iResult = 0;
|
|
}
|
|
return(iResult);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function DirtyJpgDecodeImage
|
|
|
|
\Description
|
|
Parse JPG image.
|
|
|
|
\Input *pState - pointer to module state
|
|
\Input *pJpgHdr - pointer to jpg header
|
|
\Input *pImageBuf - pointer to image buf
|
|
\Input iBufWidth - image buf width
|
|
\Input iBufHeight - image buf height
|
|
|
|
\Output
|
|
int32_t - DIRTYJPG_ERR_*
|
|
|
|
\Version 02/23/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
int32_t DirtyJpgDecodeImage(DirtyJpgStateT *pState, DirtyJpgHdrT *pJpgHdr, uint8_t *pImageBuf, int32_t iBufWidth, int32_t iBufHeight)
|
|
{
|
|
int32_t iError;
|
|
|
|
// make sure we are in proper state
|
|
if (pState->iLastError != DIRTYJPG_ERR_NOBUFFER)
|
|
{
|
|
return(DIRTYJPG_ERR_BADSTATE);
|
|
}
|
|
|
|
// setup the output buffer
|
|
pState->pImageBuf = pImageBuf;
|
|
pState->uBufWidth = (unsigned)iBufWidth;
|
|
pState->uBufHeight = (unsigned)iBufHeight;
|
|
|
|
// resume parsing at last spot
|
|
iError = _DirtyJpgDecodeParse(pState, pJpgHdr->pImageData + pState->uLastOffset, pJpgHdr->uLength-pState->uLastOffset, FALSE);
|
|
return(iError);
|
|
}
|