images:jpg_to_raw_pixel_buffer
Differences
This shows you the differences between two versions of the page.
images:jpg_to_raw_pixel_buffer [2017/01/17 14:27] – created peter | images:jpg_to_raw_pixel_buffer [2019/11/29 16:28] (current) – removed peter | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Images - JPG to raw pixel buffer ====== | ||
- | <code cpp> | ||
- | // NanoJPEG -- KeyJ's Tiny Baseline JPEG Decoder | ||
- | // version 1.3.5 (2016-11-14) | ||
- | // Copyright (c) 2009-2016 Martin J. Fiedler < | ||
- | // published under the terms of the MIT license | ||
- | // | ||
- | // Permission is hereby granted, free of charge, to any person obtaining a copy | ||
- | // of this software and associated documentation files (the " | ||
- | // deal in the Software without restriction, | ||
- | // 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. | ||
- | |||
- | |||
- | /////////////////////////////////////////////////////////////////////////////// | ||
- | // DOCUMENTATION SECTION | ||
- | // read this if you want to know what this is all about // | ||
- | /////////////////////////////////////////////////////////////////////////////// | ||
- | |||
- | // INTRODUCTION | ||
- | // ============ | ||
- | // | ||
- | // This is a minimal decoder for baseline JPEG images. It accepts memory dumps | ||
- | // of JPEG files as input and generates either 8-bit grayscale or packed 24-bit | ||
- | // RGB images as output. It does not parse JFIF or Exif headers; all JPEG files | ||
- | // are assumed to be either grayscale or YCbCr. CMYK or other color spaces are | ||
- | // not supported. All YCbCr subsampling schemes with power-of-two ratios are | ||
- | // supported, as are restart intervals. Progressive or lossless JPEG is not | ||
- | // supported. | ||
- | // Summed up, NanoJPEG should be able to decode all images from digital cameras | ||
- | // and most common forms of other non-progressive JPEG images. | ||
- | // The decoder is not optimized for speed, it's optimized for simplicity and | ||
- | // small code. Image quality should be at a reasonable level. A bicubic chroma | ||
- | // upsampling filter ensures that subsampled YCbCr images are rendered in | ||
- | // decent quality. The decoder is not meant to deal with broken JPEG files in | ||
- | // a graceful manner; if anything is wrong with the bitstream, decoding will | ||
- | // simply fail. | ||
- | // The code should work with every modern C compiler without problems and | ||
- | // should not emit any warnings. It uses only (at least) 32-bit integer | ||
- | // arithmetic and is supposed to be endianness independent and 64-bit clean. | ||
- | // However, it is not thread-safe. | ||
- | |||
- | |||
- | // COMPILE-TIME CONFIGURATION | ||
- | // ========================== | ||
- | // | ||
- | // The following aspects of NanoJPEG can be controlled with preprocessor | ||
- | // defines: | ||
- | // | ||
- | // _NJ_EXAMPLE_PROGRAM | ||
- | // | ||
- | // _NJ_INCLUDE_HEADER_ONLY = Don't compile anything, just act as a header | ||
- | // file for NanoJPEG. Example: | ||
- | // # | ||
- | // # | ||
- | // int main(void) { | ||
- | // | ||
- | // // your code here | ||
- | // | ||
- | // } | ||
- | // NJ_USE_LIBC=1 | ||
- | // | ||
- | // NJ_USE_LIBC=0 | ||
- | // | ||
- | // | ||
- | // and implemented somewhere. | ||
- | // NJ_USE_WIN32=0 | ||
- | // NJ_USE_WIN32=1 | ||
- | // | ||
- | // | ||
- | // | ||
- | // | ||
- | // NJ_CHROMA_FILTER=1 | ||
- | // | ||
- | // NJ_CHROMA_FILTER=0 | ||
- | // (bad quality, but faster and less code). | ||
- | |||
- | |||
- | // API | ||
- | // === | ||
- | // | ||
- | // For API documentation, | ||
- | |||
- | |||
- | // EXAMPLE | ||
- | // ======= | ||
- | // | ||
- | // A few pages below, you can find an example program that uses NanoJPEG to | ||
- | // convert JPEG files into PGM or PPM. To compile it, use something like | ||
- | // gcc -O3 -D_NJ_EXAMPLE_PROGRAM -o nanojpeg nanojpeg.c | ||
- | // You may also add -std=c99 -Wall -Wextra -pedantic -Werror, if you want :) | ||
- | // The only thing you might need is -Wno-shift-negative-value, | ||
- | // code relies on the target machine using two's complement arithmetic, but | ||
- | // the C standard does not, even though *any* practically useful machine | ||
- | // nowadays uses two's complement. | ||
- | |||
- | |||
- | /////////////////////////////////////////////////////////////////////////////// | ||
- | // HEADER SECTION | ||
- | // copy and pase this into nanojpeg.h if you want // | ||
- | /////////////////////////////////////////////////////////////////////////////// | ||
- | |||
- | #ifndef _NANOJPEG_H | ||
- | #define _NANOJPEG_H | ||
- | |||
- | // nj_result_t: | ||
- | typedef enum _nj_result { | ||
- | NJ_OK = 0, // no error, decoding successful | ||
- | NJ_NO_JPEG, | ||
- | NJ_UNSUPPORTED, | ||
- | NJ_OUT_OF_MEM, | ||
- | NJ_INTERNAL_ERR, | ||
- | NJ_SYNTAX_ERROR, | ||
- | __NJ_FINISHED, | ||
- | } nj_result_t; | ||
- | |||
- | // njInit: Initialize NanoJPEG. | ||
- | // For safety reasons, this should be called at least one time before using | ||
- | // using any of the other NanoJPEG functions. | ||
- | void njInit(void); | ||
- | |||
- | // njDecode: Decode a JPEG image. | ||
- | // Decodes a memory dump of a JPEG file into internal buffers. | ||
- | // Parameters: | ||
- | // jpeg = The pointer to the memory dump. | ||
- | // size = The size of the JPEG file. | ||
- | // Return value: The error code in case of failure, or NJ_OK (zero) on success. | ||
- | nj_result_t njDecode(const void* jpeg, const int size); | ||
- | |||
- | // njGetWidth: Return the width (in pixels) of the most recently decoded | ||
- | // image. If njDecode() failed, the result of njGetWidth() is undefined. | ||
- | int njGetWidth(void); | ||
- | |||
- | // njGetHeight: | ||
- | // image. If njDecode() failed, the result of njGetHeight() is undefined. | ||
- | int njGetHeight(void); | ||
- | |||
- | // njIsColor: Return 1 if the most recently decoded image is a color image | ||
- | // (RGB) or 0 if it is a grayscale image. If njDecode() failed, the result | ||
- | // of njGetWidth() is undefined. | ||
- | int njIsColor(void); | ||
- | |||
- | // njGetImage: Returns the decoded image data. | ||
- | // Returns a pointer to the most recently image. The memory layout it byte- | ||
- | // oriented, top-down, without any padding between lines. Pixels of color | ||
- | // images will be stored as three consecutive bytes for the red, green and | ||
- | // blue channels. This data format is thus compatible with the PGM or PPM | ||
- | // file formats and the OpenGL texture formats GL_LUMINANCE8 or GL_RGB8. | ||
- | // If njDecode() failed, the result of njGetImage() is undefined. | ||
- | unsigned char* njGetImage(void); | ||
- | |||
- | // njGetImageSize: | ||
- | // by njGetImage(). If njDecode() failed, the result of njGetImageSize() is | ||
- | // undefined. | ||
- | int njGetImageSize(void); | ||
- | |||
- | // njDone: Uninitialize NanoJPEG. | ||
- | // Resets NanoJPEG' | ||
- | // allocated at run-time by NanoJPEG. It is still possible to decode another | ||
- | // image after a njDone() call. | ||
- | void njDone(void); | ||
- | |||
- | # | ||
- | |||
- | |||
- | /////////////////////////////////////////////////////////////////////////////// | ||
- | // CONFIGURATION SECTION | ||
- | // adjust the default settings for the NJ_ defines here // | ||
- | /////////////////////////////////////////////////////////////////////////////// | ||
- | |||
- | #ifndef NJ_USE_LIBC | ||
- | #define NJ_USE_LIBC 1 | ||
- | #endif | ||
- | |||
- | #ifndef NJ_USE_WIN32 | ||
- | #ifdef _MSC_VER | ||
- | #define NJ_USE_WIN32 (!NJ_USE_LIBC) | ||
- | #else | ||
- | #define NJ_USE_WIN32 0 | ||
- | #endif | ||
- | #endif | ||
- | |||
- | #ifndef NJ_CHROMA_FILTER | ||
- | #define NJ_CHROMA_FILTER 1 | ||
- | #endif | ||
- | |||
- | |||
- | /////////////////////////////////////////////////////////////////////////////// | ||
- | // EXAMPLE PROGRAM | ||
- | // just define _NJ_EXAMPLE_PROGRAM to compile this (requires NJ_USE_LIBC) | ||
- | /////////////////////////////////////////////////////////////////////////////// | ||
- | |||
- | # | ||
- | |||
- | #include < | ||
- | #include < | ||
- | #include < | ||
- | |||
- | int main(int argc, char* argv[]) { | ||
- | int size; | ||
- | char *buf; | ||
- | FILE *f; | ||
- | |||
- | if (argc < 2) { | ||
- | printf(" | ||
- | return 2; | ||
- | } | ||
- | f = fopen(argv[1], | ||
- | if (!f) { | ||
- | printf(" | ||
- | return 1; | ||
- | } | ||
- | fseek(f, 0, SEEK_END); | ||
- | size = (int) ftell(f); | ||
- | buf = (char*) malloc(size); | ||
- | fseek(f, 0, SEEK_SET); | ||
- | size = (int) fread(buf, 1, size, f); | ||
- | fclose(f); | ||
- | |||
- | njInit(); | ||
- | if (njDecode(buf, | ||
- | free((void*)buf); | ||
- | printf(" | ||
- | return 1; | ||
- | } | ||
- | free((void*)buf); | ||
- | |||
- | f = fopen((argc > 2) ? argv[2] : (njIsColor() ? " | ||
- | if (!f) { | ||
- | printf(" | ||
- | return 1; | ||
- | } | ||
- | fprintf(f, " | ||
- | fwrite(njGetImage(), | ||
- | fclose(f); | ||
- | njDone(); | ||
- | return 0; | ||
- | } | ||
- | |||
- | #endif | ||
- | |||
- | |||
- | /////////////////////////////////////////////////////////////////////////////// | ||
- | // IMPLEMENTATION SECTION | ||
- | // you may stop reading here // | ||
- | /////////////////////////////////////////////////////////////////////////////// | ||
- | |||
- | #ifndef _NJ_INCLUDE_HEADER_ONLY | ||
- | |||
- | #ifdef _MSC_VER | ||
- | #define NJ_INLINE static __inline | ||
- | #define NJ_FORCE_INLINE static __forceinline | ||
- | #else | ||
- | #define NJ_INLINE static inline | ||
- | #define NJ_FORCE_INLINE static inline | ||
- | #endif | ||
- | |||
- | #if NJ_USE_LIBC | ||
- | #include < | ||
- | #include < | ||
- | #define njAllocMem malloc | ||
- | #define njFreeMem | ||
- | #define njFillMem | ||
- | #define njCopyMem | ||
- | #elif NJ_USE_WIN32 | ||
- | #include < | ||
- | #define njAllocMem(size) ((void*) LocalAlloc(LMEM_FIXED, | ||
- | #define njFreeMem(block) ((void) LocalFree((HLOCAL) block)) | ||
- | NJ_INLINE void njFillMem(void* block, unsigned char value, int count) { __asm { | ||
- | mov edi, block | ||
- | mov al, value | ||
- | mov ecx, count | ||
- | rep stosb | ||
- | } } | ||
- | NJ_INLINE void njCopyMem(void* dest, const void* src, int count) { __asm { | ||
- | mov edi, dest | ||
- | mov esi, src | ||
- | mov ecx, count | ||
- | rep movsb | ||
- | } } | ||
- | #else | ||
- | extern void* njAllocMem(int size); | ||
- | extern void njFreeMem(void* block); | ||
- | extern void njFillMem(void* block, unsigned char byte, int size); | ||
- | extern void njCopyMem(void* dest, const void* src, int size); | ||
- | #endif | ||
- | |||
- | typedef struct _nj_code { | ||
- | unsigned char bits, code; | ||
- | } nj_vlc_code_t; | ||
- | |||
- | typedef struct _nj_cmp { | ||
- | int cid; | ||
- | int ssx, ssy; | ||
- | int width, height; | ||
- | int stride; | ||
- | int qtsel; | ||
- | int actabsel, dctabsel; | ||
- | int dcpred; | ||
- | unsigned char *pixels; | ||
- | } nj_component_t; | ||
- | |||
- | typedef struct _nj_ctx { | ||
- | nj_result_t error; | ||
- | const unsigned char *pos; | ||
- | int size; | ||
- | int length; | ||
- | int width, height; | ||
- | int mbwidth, mbheight; | ||
- | int mbsizex, mbsizey; | ||
- | int ncomp; | ||
- | nj_component_t comp[3]; | ||
- | int qtused, qtavail; | ||
- | unsigned char qtab[4][64]; | ||
- | nj_vlc_code_t vlctab[4][65536]; | ||
- | int buf, bufbits; | ||
- | int block[64]; | ||
- | int rstinterval; | ||
- | unsigned char *rgb; | ||
- | } nj_context_t; | ||
- | |||
- | static nj_context_t nj; | ||
- | |||
- | static const char njZZ[64] = { 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 }; | ||
- | |||
- | NJ_FORCE_INLINE unsigned char njClip(const int x) { | ||
- | return (x < 0) ? 0 : ((x > 0xFF) ? 0xFF : (unsigned char) x); | ||
- | } | ||
- | |||
- | #define W1 2841 | ||
- | #define W2 2676 | ||
- | #define W3 2408 | ||
- | #define W5 1609 | ||
- | #define W6 1108 | ||
- | #define W7 565 | ||
- | |||
- | NJ_INLINE void njRowIDCT(int* blk) { | ||
- | int x0, x1, x2, x3, x4, x5, x6, x7, x8; | ||
- | if (!((x1 = blk[4] << 11) | ||
- | | (x2 = blk[6]) | ||
- | | (x3 = blk[2]) | ||
- | | (x4 = blk[1]) | ||
- | | (x5 = blk[7]) | ||
- | | (x6 = blk[5]) | ||
- | | (x7 = blk[3]))) | ||
- | { | ||
- | blk[0] = blk[1] = blk[2] = blk[3] = blk[4] = blk[5] = blk[6] = blk[7] = blk[0] << 3; | ||
- | return; | ||
- | } | ||
- | x0 = (blk[0] << 11) + 128; | ||
- | x8 = W7 * (x4 + x5); | ||
- | x4 = x8 + (W1 - W7) * x4; | ||
- | x5 = x8 - (W1 + W7) * x5; | ||
- | x8 = W3 * (x6 + x7); | ||
- | x6 = x8 - (W3 - W5) * x6; | ||
- | x7 = x8 - (W3 + W5) * x7; | ||
- | x8 = x0 + x1; | ||
- | x0 -= x1; | ||
- | x1 = W6 * (x3 + x2); | ||
- | x2 = x1 - (W2 + W6) * x2; | ||
- | x3 = x1 + (W2 - W6) * x3; | ||
- | x1 = x4 + x6; | ||
- | x4 -= x6; | ||
- | x6 = x5 + x7; | ||
- | x5 -= x7; | ||
- | x7 = x8 + x3; | ||
- | x8 -= x3; | ||
- | x3 = x0 + x2; | ||
- | x0 -= x2; | ||
- | x2 = (181 * (x4 + x5) + 128) >> 8; | ||
- | x4 = (181 * (x4 - x5) + 128) >> 8; | ||
- | blk[0] = (x7 + x1) >> 8; | ||
- | blk[1] = (x3 + x2) >> 8; | ||
- | blk[2] = (x0 + x4) >> 8; | ||
- | blk[3] = (x8 + x6) >> 8; | ||
- | blk[4] = (x8 - x6) >> 8; | ||
- | blk[5] = (x0 - x4) >> 8; | ||
- | blk[6] = (x3 - x2) >> 8; | ||
- | blk[7] = (x7 - x1) >> 8; | ||
- | } | ||
- | |||
- | NJ_INLINE void njColIDCT(const int* blk, unsigned char *out, int stride) { | ||
- | int x0, x1, x2, x3, x4, x5, x6, x7, x8; | ||
- | if (!((x1 = blk[8*4] << 8) | ||
- | | (x2 = blk[8*6]) | ||
- | | (x3 = blk[8*2]) | ||
- | | (x4 = blk[8*1]) | ||
- | | (x5 = blk[8*7]) | ||
- | | (x6 = blk[8*5]) | ||
- | | (x7 = blk[8*3]))) | ||
- | { | ||
- | x1 = njClip(((blk[0] + 32) >> 6) + 128); | ||
- | for (x0 = 8; x0; --x0) { | ||
- | *out = (unsigned char) x1; | ||
- | out += stride; | ||
- | } | ||
- | return; | ||
- | } | ||
- | x0 = (blk[0] << 8) + 8192; | ||
- | x8 = W7 * (x4 + x5) + 4; | ||
- | x4 = (x8 + (W1 - W7) * x4) >> 3; | ||
- | x5 = (x8 - (W1 + W7) * x5) >> 3; | ||
- | x8 = W3 * (x6 + x7) + 4; | ||
- | x6 = (x8 - (W3 - W5) * x6) >> 3; | ||
- | x7 = (x8 - (W3 + W5) * x7) >> 3; | ||
- | x8 = x0 + x1; | ||
- | x0 -= x1; | ||
- | x1 = W6 * (x3 + x2) + 4; | ||
- | x2 = (x1 - (W2 + W6) * x2) >> 3; | ||
- | x3 = (x1 + (W2 - W6) * x3) >> 3; | ||
- | x1 = x4 + x6; | ||
- | x4 -= x6; | ||
- | x6 = x5 + x7; | ||
- | x5 -= x7; | ||
- | x7 = x8 + x3; | ||
- | x8 -= x3; | ||
- | x3 = x0 + x2; | ||
- | x0 -= x2; | ||
- | x2 = (181 * (x4 + x5) + 128) >> 8; | ||
- | x4 = (181 * (x4 - x5) + 128) >> 8; | ||
- | *out = njClip(((x7 + x1) >> 14) + 128); out += stride; | ||
- | *out = njClip(((x3 + x2) >> 14) + 128); out += stride; | ||
- | *out = njClip(((x0 + x4) >> 14) + 128); out += stride; | ||
- | *out = njClip(((x8 + x6) >> 14) + 128); out += stride; | ||
- | *out = njClip(((x8 - x6) >> 14) + 128); out += stride; | ||
- | *out = njClip(((x0 - x4) >> 14) + 128); out += stride; | ||
- | *out = njClip(((x3 - x2) >> 14) + 128); out += stride; | ||
- | *out = njClip(((x7 - x1) >> 14) + 128); | ||
- | } | ||
- | |||
- | #define njThrow(e) do { nj.error = e; return; } while (0) | ||
- | #define njCheckError() do { if (nj.error) return; } while (0) | ||
- | |||
- | static int njShowBits(int bits) { | ||
- | unsigned char newbyte; | ||
- | if (!bits) return 0; | ||
- | while (nj.bufbits < bits) { | ||
- | if (nj.size <= 0) { | ||
- | nj.buf = (nj.buf << 8) | 0xFF; | ||
- | nj.bufbits += 8; | ||
- | continue; | ||
- | } | ||
- | newbyte = *nj.pos++; | ||
- | nj.size--; | ||
- | nj.bufbits += 8; | ||
- | nj.buf = (nj.buf << 8) | newbyte; | ||
- | if (newbyte == 0xFF) { | ||
- | if (nj.size) { | ||
- | unsigned char marker = *nj.pos++; | ||
- | nj.size--; | ||
- | switch (marker) { | ||
- | case 0x00: | ||
- | case 0xFF: | ||
- | break; | ||
- | case 0xD9: nj.size = 0; break; | ||
- | default: | ||
- | if ((marker & 0xF8) != 0xD0) | ||
- | nj.error = NJ_SYNTAX_ERROR; | ||
- | else { | ||
- | nj.buf = (nj.buf << 8) | marker; | ||
- | nj.bufbits += 8; | ||
- | } | ||
- | } | ||
- | } else | ||
- | nj.error = NJ_SYNTAX_ERROR; | ||
- | } | ||
- | } | ||
- | return (nj.buf >> (nj.bufbits - bits)) & ((1 << bits) - 1); | ||
- | } | ||
- | |||
- | NJ_INLINE void njSkipBits(int bits) { | ||
- | if (nj.bufbits < bits) | ||
- | (void) njShowBits(bits); | ||
- | nj.bufbits -= bits; | ||
- | } | ||
- | |||
- | NJ_INLINE int njGetBits(int bits) { | ||
- | int res = njShowBits(bits); | ||
- | njSkipBits(bits); | ||
- | return res; | ||
- | } | ||
- | |||
- | NJ_INLINE void njByteAlign(void) { | ||
- | nj.bufbits &= 0xF8; | ||
- | } | ||
- | |||
- | static void njSkip(int count) { | ||
- | nj.pos += count; | ||
- | nj.size -= count; | ||
- | nj.length -= count; | ||
- | if (nj.size < 0) nj.error = NJ_SYNTAX_ERROR; | ||
- | } | ||
- | |||
- | NJ_INLINE unsigned short njDecode16(const unsigned char *pos) { | ||
- | return (pos[0] << 8) | pos[1]; | ||
- | } | ||
- | |||
- | static void njDecodeLength(void) { | ||
- | if (nj.size < 2) njThrow(NJ_SYNTAX_ERROR); | ||
- | nj.length = njDecode16(nj.pos); | ||
- | if (nj.length > nj.size) njThrow(NJ_SYNTAX_ERROR); | ||
- | njSkip(2); | ||
- | } | ||
- | |||
- | NJ_INLINE void njSkipMarker(void) { | ||
- | njDecodeLength(); | ||
- | njSkip(nj.length); | ||
- | } | ||
- | |||
- | NJ_INLINE void njDecodeSOF(void) { | ||
- | int i, ssxmax = 0, ssymax = 0; | ||
- | nj_component_t* c; | ||
- | njDecodeLength(); | ||
- | njCheckError(); | ||
- | if (nj.length < 9) njThrow(NJ_SYNTAX_ERROR); | ||
- | if (nj.pos[0] != 8) njThrow(NJ_UNSUPPORTED); | ||
- | nj.height = njDecode16(nj.pos+1); | ||
- | nj.width = njDecode16(nj.pos+3); | ||
- | if (!nj.width || !nj.height) njThrow(NJ_SYNTAX_ERROR); | ||
- | nj.ncomp = nj.pos[5]; | ||
- | njSkip(6); | ||
- | switch (nj.ncomp) { | ||
- | case 1: | ||
- | case 3: | ||
- | break; | ||
- | default: | ||
- | njThrow(NJ_UNSUPPORTED); | ||
- | } | ||
- | if (nj.length < (nj.ncomp * 3)) njThrow(NJ_SYNTAX_ERROR); | ||
- | for (i = 0, c = nj.comp; | ||
- | c->cid = nj.pos[0]; | ||
- | if (!(c-> | ||
- | if (c->ssx & (c->ssx - 1)) njThrow(NJ_UNSUPPORTED); | ||
- | if (!(c-> | ||
- | if (c->ssy & (c->ssy - 1)) njThrow(NJ_UNSUPPORTED); | ||
- | if ((c-> | ||
- | njSkip(3); | ||
- | nj.qtused |= 1 << c-> | ||
- | if (c->ssx > ssxmax) ssxmax = c->ssx; | ||
- | if (c->ssy > ssymax) ssymax = c->ssy; | ||
- | } | ||
- | if (nj.ncomp == 1) { | ||
- | c = nj.comp; | ||
- | c->ssx = c->ssy = ssxmax = ssymax = 1; | ||
- | } | ||
- | nj.mbsizex = ssxmax << 3; | ||
- | nj.mbsizey = ssymax << 3; | ||
- | nj.mbwidth = (nj.width + nj.mbsizex - 1) / nj.mbsizex; | ||
- | nj.mbheight = (nj.height + nj.mbsizey - 1) / nj.mbsizey; | ||
- | for (i = 0, c = nj.comp; | ||
- | c->width = (nj.width * c->ssx + ssxmax - 1) / ssxmax; | ||
- | c-> | ||
- | c-> | ||
- | if (((c-> | ||
- | if (!(c-> | ||
- | } | ||
- | if (nj.ncomp == 3) { | ||
- | nj.rgb = (unsigned char*) njAllocMem(nj.width * nj.height * nj.ncomp); | ||
- | if (!nj.rgb) njThrow(NJ_OUT_OF_MEM); | ||
- | } | ||
- | njSkip(nj.length); | ||
- | } | ||
- | |||
- | NJ_INLINE void njDecodeDHT(void) { | ||
- | int codelen, currcnt, remain, spread, i, j; | ||
- | nj_vlc_code_t *vlc; | ||
- | static unsigned char counts[16]; | ||
- | njDecodeLength(); | ||
- | njCheckError(); | ||
- | while (nj.length >= 17) { | ||
- | i = nj.pos[0]; | ||
- | if (i & 0xEC) njThrow(NJ_SYNTAX_ERROR); | ||
- | if (i & 0x02) njThrow(NJ_UNSUPPORTED); | ||
- | i = (i | (i >> 3)) & 3; // combined DC/AC + tableid value | ||
- | for (codelen = 1; codelen <= 16; ++codelen) | ||
- | counts[codelen - 1] = nj.pos[codelen]; | ||
- | njSkip(17); | ||
- | vlc = & | ||
- | remain = spread = 65536; | ||
- | for (codelen = 1; codelen <= 16; ++codelen) { | ||
- | spread >>= 1; | ||
- | currcnt = counts[codelen - 1]; | ||
- | if (!currcnt) continue; | ||
- | if (nj.length < currcnt) njThrow(NJ_SYNTAX_ERROR); | ||
- | remain -= currcnt << (16 - codelen); | ||
- | if (remain < 0) njThrow(NJ_SYNTAX_ERROR); | ||
- | for (i = 0; i < currcnt; | ||
- | register unsigned char code = nj.pos[i]; | ||
- | for (j = spread; | ||
- | vlc-> | ||
- | vlc-> | ||
- | ++vlc; | ||
- | } | ||
- | } | ||
- | njSkip(currcnt); | ||
- | } | ||
- | while (remain--) { | ||
- | vlc-> | ||
- | ++vlc; | ||
- | } | ||
- | } | ||
- | if (nj.length) njThrow(NJ_SYNTAX_ERROR); | ||
- | } | ||
- | |||
- | NJ_INLINE void njDecodeDQT(void) { | ||
- | int i; | ||
- | unsigned char *t; | ||
- | njDecodeLength(); | ||
- | njCheckError(); | ||
- | while (nj.length >= 65) { | ||
- | i = nj.pos[0]; | ||
- | if (i & 0xFC) njThrow(NJ_SYNTAX_ERROR); | ||
- | nj.qtavail |= 1 << i; | ||
- | t = & | ||
- | for (i = 0; i < 64; ++i) | ||
- | t[i] = nj.pos[i + 1]; | ||
- | njSkip(65); | ||
- | } | ||
- | if (nj.length) njThrow(NJ_SYNTAX_ERROR); | ||
- | } | ||
- | |||
- | NJ_INLINE void njDecodeDRI(void) { | ||
- | njDecodeLength(); | ||
- | njCheckError(); | ||
- | if (nj.length < 2) njThrow(NJ_SYNTAX_ERROR); | ||
- | nj.rstinterval = njDecode16(nj.pos); | ||
- | njSkip(nj.length); | ||
- | } | ||
- | |||
- | static int njGetVLC(nj_vlc_code_t* vlc, unsigned char* code) { | ||
- | int value = njShowBits(16); | ||
- | int bits = vlc[value].bits; | ||
- | if (!bits) { nj.error = NJ_SYNTAX_ERROR; | ||
- | njSkipBits(bits); | ||
- | value = vlc[value].code; | ||
- | if (code) *code = (unsigned char) value; | ||
- | bits = value & 15; | ||
- | if (!bits) return 0; | ||
- | value = njGetBits(bits); | ||
- | if (value < (1 << (bits - 1))) | ||
- | value += ((-1) << bits) + 1; | ||
- | return value; | ||
- | } | ||
- | |||
- | NJ_INLINE void njDecodeBlock(nj_component_t* c, unsigned char* out) { | ||
- | unsigned char code = 0; | ||
- | int value, coef = 0; | ||
- | njFillMem(nj.block, | ||
- | c-> | ||
- | nj.block[0] = (c-> | ||
- | do { | ||
- | value = njGetVLC(& | ||
- | if (!code) break; | ||
- | if (!(code & 0x0F) && (code != 0xF0)) njThrow(NJ_SYNTAX_ERROR); | ||
- | coef += (code >> 4) + 1; | ||
- | if (coef > 63) njThrow(NJ_SYNTAX_ERROR); | ||
- | nj.block[(int) njZZ[coef]] = value * nj.qtab[c-> | ||
- | } while (coef < 63); | ||
- | for (coef = 0; coef < 64; coef += 8) | ||
- | njRowIDCT(& | ||
- | for (coef = 0; coef < 8; ++coef) | ||
- | njColIDCT(& | ||
- | } | ||
- | |||
- | NJ_INLINE void njDecodeScan(void) { | ||
- | int i, mbx, mby, sbx, sby; | ||
- | int rstcount = nj.rstinterval, | ||
- | nj_component_t* c; | ||
- | njDecodeLength(); | ||
- | njCheckError(); | ||
- | if (nj.length < (4 + 2 * nj.ncomp)) njThrow(NJ_SYNTAX_ERROR); | ||
- | if (nj.pos[0] != nj.ncomp) njThrow(NJ_UNSUPPORTED); | ||
- | njSkip(1); | ||
- | for (i = 0, c = nj.comp; | ||
- | if (nj.pos[0] != c->cid) njThrow(NJ_SYNTAX_ERROR); | ||
- | if (nj.pos[1] & 0xEE) njThrow(NJ_SYNTAX_ERROR); | ||
- | c-> | ||
- | c-> | ||
- | njSkip(2); | ||
- | } | ||
- | if (nj.pos[0] || (nj.pos[1] != 63) || nj.pos[2]) njThrow(NJ_UNSUPPORTED); | ||
- | njSkip(nj.length); | ||
- | for (mbx = mby = 0;;) { | ||
- | for (i = 0, c = nj.comp; | ||
- | for (sby = 0; sby < c-> | ||
- | for (sbx = 0; sbx < c-> | ||
- | njDecodeBlock(c, | ||
- | njCheckError(); | ||
- | } | ||
- | if (++mbx >= nj.mbwidth) { | ||
- | mbx = 0; | ||
- | if (++mby >= nj.mbheight) break; | ||
- | } | ||
- | if (nj.rstinterval && !(--rstcount)) { | ||
- | njByteAlign(); | ||
- | i = njGetBits(16); | ||
- | if (((i & 0xFFF8) != 0xFFD0) || ((i & 7) != nextrst)) njThrow(NJ_SYNTAX_ERROR); | ||
- | nextrst = (nextrst + 1) & 7; | ||
- | rstcount = nj.rstinterval; | ||
- | for (i = 0; i < 3; ++i) | ||
- | nj.comp[i].dcpred = 0; | ||
- | } | ||
- | } | ||
- | nj.error = __NJ_FINISHED; | ||
- | } | ||
- | |||
- | #if NJ_CHROMA_FILTER | ||
- | |||
- | #define CF4A (-9) | ||
- | #define CF4B (111) | ||
- | #define CF4C (29) | ||
- | #define CF4D (-3) | ||
- | #define CF3A (28) | ||
- | #define CF3B (109) | ||
- | #define CF3C (-9) | ||
- | #define CF3X (104) | ||
- | #define CF3Y (27) | ||
- | #define CF3Z (-3) | ||
- | #define CF2A (139) | ||
- | #define CF2B (-11) | ||
- | #define CF(x) njClip(((x) + 64) >> 7) | ||
- | |||
- | NJ_INLINE void njUpsampleH(nj_component_t* c) { | ||
- | const int xmax = c->width - 3; | ||
- | unsigned char *out, *lin, *lout; | ||
- | int x, y; | ||
- | out = (unsigned char*) njAllocMem((c-> | ||
- | if (!out) njThrow(NJ_OUT_OF_MEM); | ||
- | lin = c-> | ||
- | lout = out; | ||
- | for (y = c-> | ||
- | lout[0] = CF(CF2A * lin[0] + CF2B * lin[1]); | ||
- | lout[1] = CF(CF3X * lin[0] + CF3Y * lin[1] + CF3Z * lin[2]); | ||
- | lout[2] = CF(CF3A * lin[0] + CF3B * lin[1] + CF3C * lin[2]); | ||
- | for (x = 0; x < xmax; ++x) { | ||
- | lout[(x << 1) + 3] = CF(CF4A * lin[x] + CF4B * lin[x + 1] + CF4C * lin[x + 2] + CF4D * lin[x + 3]); | ||
- | lout[(x << 1) + 4] = CF(CF4D * lin[x] + CF4C * lin[x + 1] + CF4B * lin[x + 2] + CF4A * lin[x + 3]); | ||
- | } | ||
- | lin += c-> | ||
- | lout += c->width << 1; | ||
- | lout[-3] = CF(CF3A * lin[-1] + CF3B * lin[-2] + CF3C * lin[-3]); | ||
- | lout[-2] = CF(CF3X * lin[-1] + CF3Y * lin[-2] + CF3Z * lin[-3]); | ||
- | lout[-1] = CF(CF2A * lin[-1] + CF2B * lin[-2]); | ||
- | } | ||
- | c->width <<= 1; | ||
- | c-> | ||
- | njFreeMem((void*)c-> | ||
- | c-> | ||
- | } | ||
- | |||
- | NJ_INLINE void njUpsampleV(nj_component_t* c) { | ||
- | const int w = c-> | ||
- | unsigned char *out, *cin, *cout; | ||
- | int x, y; | ||
- | out = (unsigned char*) njAllocMem((c-> | ||
- | if (!out) njThrow(NJ_OUT_OF_MEM); | ||
- | for (x = 0; x < w; ++x) { | ||
- | cin = & | ||
- | cout = & | ||
- | *cout = CF(CF2A * cin[0] + CF2B * cin[s1]); | ||
- | *cout = CF(CF3X * cin[0] + CF3Y * cin[s1] + CF3Z * cin[s2]); | ||
- | *cout = CF(CF3A * cin[0] + CF3B * cin[s1] + CF3C * cin[s2]); | ||
- | cin += s1; | ||
- | for (y = c-> | ||
- | *cout = CF(CF4A * cin[-s1] + CF4B * cin[0] + CF4C * cin[s1] + CF4D * cin[s2]); | ||
- | *cout = CF(CF4D * cin[-s1] + CF4C * cin[0] + CF4B * cin[s1] + CF4A * cin[s2]); | ||
- | cin += s1; | ||
- | } | ||
- | cin += s1; | ||
- | *cout = CF(CF3A * cin[0] + CF3B * cin[-s1] + CF3C * cin[-s2]); | ||
- | *cout = CF(CF3X * cin[0] + CF3Y * cin[-s1] + CF3Z * cin[-s2]); | ||
- | *cout = CF(CF2A * cin[0] + CF2B * cin[-s1]); | ||
- | } | ||
- | c-> | ||
- | c-> | ||
- | njFreeMem((void*) c-> | ||
- | c-> | ||
- | } | ||
- | |||
- | #else | ||
- | |||
- | NJ_INLINE void njUpsample(nj_component_t* c) { | ||
- | int x, y, xshift = 0, yshift = 0; | ||
- | unsigned char *out, *lin, *lout; | ||
- | while (c-> | ||
- | while (c-> | ||
- | out = (unsigned char*) njAllocMem(c-> | ||
- | if (!out) njThrow(NJ_OUT_OF_MEM); | ||
- | lin = c-> | ||
- | lout = out; | ||
- | for (y = 0; y < c-> | ||
- | lin = & | ||
- | for (x = 0; x < c-> | ||
- | lout[x] = lin[x >> xshift]; | ||
- | lout += c-> | ||
- | } | ||
- | c-> | ||
- | njFreeMem((void*) c-> | ||
- | c-> | ||
- | } | ||
- | |||
- | #endif | ||
- | |||
- | NJ_INLINE void njConvert(void) { | ||
- | int i; | ||
- | nj_component_t* c; | ||
- | for (i = 0, c = nj.comp; | ||
- | #if NJ_CHROMA_FILTER | ||
- | while ((c-> | ||
- | if (c-> | ||
- | njCheckError(); | ||
- | if (c-> | ||
- | njCheckError(); | ||
- | } | ||
- | #else | ||
- | if ((c-> | ||
- | njUpsample(c); | ||
- | #endif | ||
- | if ((c-> | ||
- | } | ||
- | if (nj.ncomp == 3) { | ||
- | // convert to RGB | ||
- | int x, yy; | ||
- | unsigned char *prgb = nj.rgb; | ||
- | const unsigned char *py = nj.comp[0].pixels; | ||
- | const unsigned char *pcb = nj.comp[1].pixels; | ||
- | const unsigned char *pcr = nj.comp[2].pixels; | ||
- | for (yy = nj.height; | ||
- | for (x = 0; x < nj.width; | ||
- | register int y = py[x] << 8; | ||
- | register int cb = pcb[x] - 128; | ||
- | register int cr = pcr[x] - 128; | ||
- | *prgb++ = njClip((y | ||
- | *prgb++ = njClip((y - 88 * cb - 183 * cr + 128) >> 8); | ||
- | *prgb++ = njClip((y + 454 * cb + 128) >> 8); | ||
- | } | ||
- | py += nj.comp[0].stride; | ||
- | pcb += nj.comp[1].stride; | ||
- | pcr += nj.comp[2].stride; | ||
- | } | ||
- | } else if (nj.comp[0].width != nj.comp[0].stride) { | ||
- | // grayscale -> only remove stride | ||
- | unsigned char *pin = & | ||
- | unsigned char *pout = & | ||
- | int y; | ||
- | for (y = nj.comp[0].height - 1; y; --y) { | ||
- | njCopyMem(pout, | ||
- | pin += nj.comp[0].stride; | ||
- | pout += nj.comp[0].width; | ||
- | } | ||
- | nj.comp[0].stride = nj.comp[0].width; | ||
- | } | ||
- | } | ||
- | |||
- | void njInit(void) { | ||
- | njFillMem(& | ||
- | } | ||
- | |||
- | void njDone(void) { | ||
- | int i; | ||
- | for (i = 0; i < 3; ++i) | ||
- | if (nj.comp[i].pixels) njFreeMem((void*) nj.comp[i].pixels); | ||
- | if (nj.rgb) njFreeMem((void*) nj.rgb); | ||
- | njInit(); | ||
- | } | ||
- | |||
- | nj_result_t njDecode(const void* jpeg, const int size) { | ||
- | njDone(); | ||
- | nj.pos = (const unsigned char*) jpeg; | ||
- | nj.size = size & 0x7FFFFFFF; | ||
- | if (nj.size < 2) return NJ_NO_JPEG; | ||
- | if ((nj.pos[0] ^ 0xFF) | (nj.pos[1] ^ 0xD8)) return NJ_NO_JPEG; | ||
- | njSkip(2); | ||
- | while (!nj.error) { | ||
- | if ((nj.size < 2) || (nj.pos[0] != 0xFF)) return NJ_SYNTAX_ERROR; | ||
- | njSkip(2); | ||
- | switch (nj.pos[-1]) { | ||
- | case 0xC0: njDecodeSOF(); | ||
- | case 0xC4: njDecodeDHT(); | ||
- | case 0xDB: njDecodeDQT(); | ||
- | case 0xDD: njDecodeDRI(); | ||
- | case 0xDA: njDecodeScan(); | ||
- | case 0xFE: njSkipMarker(); | ||
- | default: | ||
- | if ((nj.pos[-1] & 0xF0) == 0xE0) | ||
- | njSkipMarker(); | ||
- | else | ||
- | return NJ_UNSUPPORTED; | ||
- | } | ||
- | } | ||
- | if (nj.error != __NJ_FINISHED) return nj.error; | ||
- | nj.error = NJ_OK; | ||
- | njConvert(); | ||
- | return nj.error; | ||
- | } | ||
- | |||
- | int njGetWidth(void) | ||
- | int njGetHeight(void) | ||
- | int njIsColor(void) | ||
- | unsigned char* njGetImage(void) { return (nj.ncomp == 1) ? nj.comp[0].pixels : nj.rgb; } | ||
- | int njGetImageSize(void) | ||
- | |||
- | #endif // _NJ_INCLUDE_HEADER_ONLY | ||
- | </ | ||
- | |||
- | |||
- | ===== References ===== | ||
- | |||
- | http:// |
images/jpg_to_raw_pixel_buffer.1484663228.txt.gz · Last modified: 2020/07/15 09:30 (external edit)