1 // NanoJPEG -- KeyJ's Tiny Baseline JPEG Decoder
2 // version 1.3.5 (2016-11-14)
3 // Copyright (c) 2009-2016 Martin J. Fiedler <martin.fiedler@gmx.net>
4 // published under the terms of the MIT license
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a copy
7 // of this software and associated documentation files (the "Software"), to
8 // deal in the Software without restriction, including without limitation the
9 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 // sell copies of the Software, and to permit persons to whom the Software is
11 // furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 // DEALINGS IN THE SOFTWARE.
23
24
25 ///////////////////////////////////////////////////////////////////////////////
26 // DOCUMENTATION SECTION //
27 // read this if you want to know what this is all about //
28 ///////////////////////////////////////////////////////////////////////////////
29
30 // INTRODUCTION
31 // ============
32 //
33 // This is a minimal decoder for baseline JPEG images. It accepts memory dumps
34 // of JPEG files as input and generates either 8-bit grayscale or packed 24-bit
35 // RGB images as output. It does not parse JFIF or Exif headers; all JPEG files
36 // are assumed to be either grayscale or YCbCr. CMYK or other color spaces are
37 // not supported. All YCbCr subsampling schemes with power-of-two ratios are
38 // supported, as are restart intervals. Progressive or lossless JPEG is not
39 // supported.
40 // Summed up, NanoJPEG should be able to decode all images from digital cameras
41 // and most common forms of other non-progressive JPEG images.
42 // The decoder is not optimized for speed, it's optimized for simplicity and
43 // small code. Image quality should be at a reasonable level. A bicubic chroma
44 // upsampling filter ensures that subsampled YCbCr images are rendered in
45 // decent quality. The decoder is not meant to deal with broken JPEG files in
46 // a graceful manner; if anything is wrong with the bitstream, decoding will
47 // simply fail.
48 // The code should work with every modern C compiler without problems and
49 // should not emit any warnings. It uses only (at least) 32-bit integer
50 // arithmetic and is supposed to be endianness independent and 64-bit clean.
51 // However, it is not thread-safe.
52
53
54 // COMPILE-TIME CONFIGURATION
55 // ==========================
56 //
57 // The following aspects of NanoJPEG can be controlled with preprocessor
58 // defines:
59 //
60 // _NJ_EXAMPLE_PROGRAM = Compile a main() function with an example
61 // program.
62 // _NJ_INCLUDE_HEADER_ONLY = Don't compile anything, just act as a header
63 // file for NanoJPEG. Example:
64 // #define _NJ_INCLUDE_HEADER_ONLY
65 // #include "nanojpeg.c"
66 // int main(void) {
67 // njInit();
68 // // your code here
69 // njDone();
70 // }
71 // NJ_USE_LIBC=1 = Use the malloc(), free(), memset() and memcpy()
72 // functions from the standard C library (default).
73 // NJ_USE_LIBC=0 = Don't use the standard C library. In this mode,
74 // external functions njAlloc(), njFreeMem(),
75 // njFillMem() and njCopyMem() need to be defined
76 // and implemented somewhere.
77 // NJ_USE_WIN32=0 = Normal mode (default).
78 // NJ_USE_WIN32=1 = If compiling with MSVC for Win32 and
79 // NJ_USE_LIBC=0, NanoJPEG will use its own
80 // implementations of the required C library
81 // functions (default if compiling with MSVC and
82 // NJ_USE_LIBC=0).
83 // NJ_CHROMA_FILTER=1 = Use the bicubic chroma upsampling filter
84 // (default).
85 // NJ_CHROMA_FILTER=0 = Use simple pixel repetition for chroma upsampling
86 // (bad quality, but faster and less code).
87
88
89 // API
90 // ===
91 //
92 // For API documentation, read the "header section" below.
93
94
95 // EXAMPLE
96 // =======
97 //
98 // A few pages below, you can find an example program that uses NanoJPEG to
99 // convert JPEG files into PGM or PPM. To compile it, use something like
100 // gcc -O3 -D_NJ_EXAMPLE_PROGRAM -o nanojpeg nanojpeg.c
101 // You may also add -std=c99 -Wall -Wextra -pedantic -Werror, if you want :)
102 // The only thing you might need is -Wno-shift-negative-value, because this
103 // code relies on the target machine using two's complement arithmetic, but
104 // the C standard does not, even though *any* practically useful machine
105 // nowadays uses two's complement.
106
107
108 ///////////////////////////////////////////////////////////////////////////////
109 // HEADER SECTION //
110 // copy and paste this into nanojpeg.h if you want //
111 ///////////////////////////////////////////////////////////////////////////////
112
113 #ifndef _NANOJPEG_H
114 #define _NANOJPEG_H
115
116 // nj_result_t: Result codes for njDecode().
117 typedef enum _nj_result {
118 NJ_OK = 0, // no error, decoding successful
119 NJ_NO_JPEG, // not a JPEG file
120 NJ_UNSUPPORTED, // unsupported format
121 NJ_OUT_OF_MEM, // out of memory
122 NJ_INTERNAL_ERR, // internal error
123 NJ_SYNTAX_ERROR, // syntax error
124 __NJ_FINISHED, // used internally, will never be reported
125 } nj_result_t;
126
127 // njInit: Initialize NanoJPEG.
128 // For safety reasons, this should be called at least one time before using
129 // using any of the other NanoJPEG functions.
130 void njInit(void);
131
132 // njDecode: Decode a JPEG image.
133 // Decodes a memory dump of a JPEG file into internal buffers.
134 // Parameters:
135 // jpeg = The pointer to the memory dump.
136 // size = The size of the JPEG file.
137 // Return value: The error code in case of failure, or NJ_OK (zero) on success.
138 nj_result_t njDecode(const void* jpeg, const int size);
139
140 // njGetWidth: Return the width (in pixels) of the most recently decoded
141 // image. If njDecode() failed, the result of njGetWidth() is undefined.
142 int njGetWidth(void);
143
144 // njGetHeight: Return the height (in pixels) of the most recently decoded
145 // image. If njDecode() failed, the result of njGetHeight() is undefined.
146 int njGetHeight(void);
147
148 // njIsColor: Return 1 if the most recently decoded image is a color image
149 // (RGB) or 0 if it is a grayscale image. If njDecode() failed, the result
150 // of njGetWidth() is undefined.
151 int njIsColor(void);
152
153 // njGetImage: Returns the decoded image data.
154 // Returns a pointer to the most recently image. The memory layout it byte-
155 // oriented, top-down, without any padding between lines. Pixels of color
156 // images will be stored as three consecutive bytes for the red, green and
157 // blue channels. This data format is thus compatible with the PGM or PPM
158 // file formats and the OpenGL texture formats GL_LUMINANCE8 or GL_RGB8.
159 // If njDecode() failed, the result of njGetImage() is undefined.
160 unsigned char* njGetImage(void);
161
162 // njGetImageSize: Returns the size (in bytes) of the image data returned
163 // by njGetImage(). If njDecode() failed, the result of njGetImageSize() is
164 // undefined.
165 int njGetImageSize(void);
166
167 // njDone: Uninitialize NanoJPEG.
168 // Resets NanoJPEG's internal state and frees all memory that has been
169 // allocated at run-time by NanoJPEG. It is still possible to decode another
170 // image after a njDone() call.
171 void njDone(void);
172
173 #endif//_NANOJPEG_H
174
175
176 ///////////////////////////////////////////////////////////////////////////////
177 // CONFIGURATION SECTION //
178 // adjust the default settings for the NJ_ defines here //
179 ///////////////////////////////////////////////////////////////////////////////
180
181 #ifndef NJ_USE_LIBC
182 #define NJ_USE_LIBC 1
183 #endif
184
185 #ifndef NJ_USE_WIN32
186 #ifdef _MSC_VER
187 #define NJ_USE_WIN32 (!NJ_USE_LIBC)
188 #else
189 #define NJ_USE_WIN32 0
190 #endif
191 #endif
192
193 #ifndef NJ_CHROMA_FILTER
194 #define NJ_CHROMA_FILTER 1
195 #endif
196
197
198 ///////////////////////////////////////////////////////////////////////////////
199 // EXAMPLE PROGRAM //
200 // just define _NJ_EXAMPLE_PROGRAM to compile this (requires NJ_USE_LIBC) //
201 ///////////////////////////////////////////////////////////////////////////////
202
203 #ifdef _NJ_EXAMPLE_PROGRAM
204
205 #include <stdio.h>
206 #include <stdlib.h>
207 #include <string.h>
208
main(int argc,char * argv[])209 int main(int argc, char* argv[]) {
210 int size;
211 char *buf;
212 FILE *f;
213
214 if (argc < 2) {
215 printf("Usage: %s <input.jpg> [<output.ppm>]\n", argv[0]);
216 return 2;
217 }
218 f = fopen(argv[1], "rb");
219 if (!f) {
220 printf("Error opening the input file.\n");
221 return 1;
222 }
223 fseek(f, 0, SEEK_END);
224 size = (int) ftell(f);
225 buf = (char*) malloc(size);
226 fseek(f, 0, SEEK_SET);
227 size = (int) fread(buf, 1, size, f);
228 fclose(f);
229
230 njInit();
231 if (njDecode(buf, size)) {
232 free((void*)buf);
233 printf("Error decoding the input file.\n");
234 return 1;
235 }
236 free((void*)buf);
237
238 f = fopen((argc > 2) ? argv[2] : (njIsColor() ? "nanojpeg_out.ppm" : "nanojpeg_out.pgm"), "wb");
239 if (!f) {
240 printf("Error opening the output file.\n");
241 return 1;
242 }
243 fprintf(f, "P%d\n%d %d\n255\n", njIsColor() ? 6 : 5, njGetWidth(), njGetHeight());
244 fwrite(njGetImage(), 1, njGetImageSize(), f);
245 fclose(f);
246 njDone();
247 return 0;
248 }
249
250 #endif
251
252
253 ///////////////////////////////////////////////////////////////////////////////
254 // IMPLEMENTATION SECTION //
255 // you may stop reading here //
256 ///////////////////////////////////////////////////////////////////////////////
257
258 #ifndef _NJ_INCLUDE_HEADER_ONLY
259
260 #ifdef _MSC_VER
261 #define NJ_INLINE static __inline
262 #define NJ_FORCE_INLINE static __forceinline
263 #else
264 #define NJ_INLINE static inline
265 #define NJ_FORCE_INLINE static inline
266 #endif
267
268 #if NJ_USE_LIBC
269 #include <stdlib.h>
270 #include <string.h>
271 #define njAllocMem malloc
272 #define njFreeMem free
273 #define njFillMem memset
274 #define njCopyMem memcpy
275 #elif NJ_USE_WIN32
276 #include <windows.h>
277 #define njAllocMem(size) ((void*) LocalAlloc(LMEM_FIXED, (SIZE_T)(size)))
278 #define njFreeMem(block) ((void) LocalFree((HLOCAL) block))
njFillMem(void * block,unsigned char value,int count)279 NJ_INLINE void njFillMem(void* block, unsigned char value, int count) { __asm {
280 mov edi, block
281 mov al, value
282 mov ecx, count
283 rep stosb
284 } }
njCopyMem(void * dest,const void * src,int count)285 NJ_INLINE void njCopyMem(void* dest, const void* src, int count) { __asm {
286 mov edi, dest
287 mov esi, src
288 mov ecx, count
289 rep movsb
290 } }
291 #else
292 extern void* njAllocMem(int size);
293 extern void njFreeMem(void* block);
294 extern void njFillMem(void* block, unsigned char byte, int size);
295 extern void njCopyMem(void* dest, const void* src, int size);
296 #endif
297
298 typedef struct _nj_code {
299 unsigned char bits, code;
300 } nj_vlc_code_t;
301
302 typedef struct _nj_cmp {
303 int cid;
304 int ssx, ssy;
305 int width, height;
306 int stride;
307 int qtsel;
308 int actabsel, dctabsel;
309 int dcpred;
310 unsigned char *pixels;
311 } nj_component_t;
312
313 typedef struct _nj_ctx {
314 nj_result_t error;
315 const unsigned char *pos;
316 int size;
317 int length;
318 int width, height;
319 int mbwidth, mbheight;
320 int mbsizex, mbsizey;
321 int ncomp;
322 nj_component_t comp[3];
323 int qtused, qtavail;
324 unsigned char qtab[4][64];
325 nj_vlc_code_t vlctab[4][65536];
326 int buf, bufbits;
327 int block[64];
328 int rstinterval;
329 unsigned char *rgb;
330 } nj_context_t;
331
332 static nj_context_t nj;
333
334 static const char njZZ[64] = { 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18,
335 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35,
336 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45,
337 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 };
338
njClip(const int x)339 NJ_FORCE_INLINE unsigned char njClip(const int x) {
340 return (x < 0) ? 0 : ((x > 0xFF) ? 0xFF : (unsigned char) x);
341 }
342
343 #define W1 2841
344 #define W2 2676
345 #define W3 2408
346 #define W5 1609
347 #define W6 1108
348 #define W7 565
349
350 #ifdef USING_R
shiftLeft(int i,int p)351 NJ_INLINE int shiftLeft(int i, int p) {
352 unsigned u = *(unsigned *)(&i);
353 u <<= p;
354 return int(u);
355 }
356 #else
357 #define shiftLeft(i,p) (i) << (p)
358 #endif
359
njRowIDCT(int * blk)360 NJ_INLINE void njRowIDCT(int* blk) {
361 int x0, x1, x2, x3, x4, x5, x6, x7, x8;
362 if (!((x1 = shiftLeft(blk[4], 11))
363 | (x2 = blk[6])
364 | (x3 = blk[2])
365 | (x4 = blk[1])
366 | (x5 = blk[7])
367 | (x6 = blk[5])
368 | (x7 = blk[3])))
369 {
370 blk[0] = blk[1] = blk[2] = blk[3] = blk[4] = blk[5] = blk[6] = blk[7] = shiftLeft(blk[0], 3);
371 return;
372 }
373 x0 = shiftLeft(blk[0], 11);
374 x0 += 128;
375 x8 = W7 * (x4 + x5);
376 x4 = x8 + (W1 - W7) * x4;
377 x5 = x8 - (W1 + W7) * x5;
378 x8 = W3 * (x6 + x7);
379 x6 = x8 - (W3 - W5) * x6;
380 x7 = x8 - (W3 + W5) * x7;
381 x8 = x0 + x1;
382 x0 -= x1;
383 x1 = W6 * (x3 + x2);
384 x2 = x1 - (W2 + W6) * x2;
385 x3 = x1 + (W2 - W6) * x3;
386 x1 = x4 + x6;
387 x4 -= x6;
388 x6 = x5 + x7;
389 x5 -= x7;
390 x7 = x8 + x3;
391 x8 -= x3;
392 x3 = x0 + x2;
393 x0 -= x2;
394 x2 = (181 * (x4 + x5) + 128) >> 8;
395 x4 = (181 * (x4 - x5) + 128) >> 8;
396 blk[0] = (x7 + x1) >> 8;
397 blk[1] = (x3 + x2) >> 8;
398 blk[2] = (x0 + x4) >> 8;
399 blk[3] = (x8 + x6) >> 8;
400 blk[4] = (x8 - x6) >> 8;
401 blk[5] = (x0 - x4) >> 8;
402 blk[6] = (x3 - x2) >> 8;
403 blk[7] = (x7 - x1) >> 8;
404 }
405
njColIDCT(const int * blk,unsigned char * out,int stride)406 NJ_INLINE void njColIDCT(const int* blk, unsigned char *out, int stride) {
407 int x0, x1, x2, x3, x4, x5, x6, x7, x8;
408 if (!((x1 = shiftLeft(blk[8*4], 8))
409 | (x2 = blk[8*6])
410 | (x3 = blk[8*2])
411 | (x4 = blk[8*1])
412 | (x5 = blk[8*7])
413 | (x6 = blk[8*5])
414 | (x7 = blk[8*3])))
415 {
416 x1 = njClip(((blk[0] + 32) >> 6) + 128);
417 for (x0 = 8; x0; --x0) {
418 *out = (unsigned char) x1;
419 out += stride;
420 }
421 return;
422 }
423 x0 = shiftLeft(blk[0], 8);
424 x0 += 8192;
425 x8 = W7 * (x4 + x5) + 4;
426 x4 = (x8 + (W1 - W7) * x4) >> 3;
427 x5 = (x8 - (W1 + W7) * x5) >> 3;
428 x8 = W3 * (x6 + x7) + 4;
429 x6 = (x8 - (W3 - W5) * x6) >> 3;
430 x7 = (x8 - (W3 + W5) * x7) >> 3;
431 x8 = x0 + x1;
432 x0 -= x1;
433 x1 = W6 * (x3 + x2) + 4;
434 x2 = (x1 - (W2 + W6) * x2) >> 3;
435 x3 = (x1 + (W2 - W6) * x3) >> 3;
436 x1 = x4 + x6;
437 x4 -= x6;
438 x6 = x5 + x7;
439 x5 -= x7;
440 x7 = x8 + x3;
441 x8 -= x3;
442 x3 = x0 + x2;
443 x0 -= x2;
444 x2 = (181 * (x4 + x5) + 128) >> 8;
445 x4 = (181 * (x4 - x5) + 128) >> 8;
446 *out = njClip(((x7 + x1) >> 14) + 128); out += stride;
447 *out = njClip(((x3 + x2) >> 14) + 128); out += stride;
448 *out = njClip(((x0 + x4) >> 14) + 128); out += stride;
449 *out = njClip(((x8 + x6) >> 14) + 128); out += stride;
450 *out = njClip(((x8 - x6) >> 14) + 128); out += stride;
451 *out = njClip(((x0 - x4) >> 14) + 128); out += stride;
452 *out = njClip(((x3 - x2) >> 14) + 128); out += stride;
453 *out = njClip(((x7 - x1) >> 14) + 128);
454 }
455
456 #define njThrow(e) do { nj.error = e; return; } while (0)
457 #define njCheckError() do { if (nj.error) return; } while (0)
458
njShowBits(int bits)459 static int njShowBits(int bits) {
460 unsigned char newbyte;
461 if (!bits) return 0;
462 while (nj.bufbits < bits) {
463 if (nj.size <= 0) {
464 nj.buf = shiftLeft(nj.buf, 8) | 0xFF;
465 nj.bufbits += 8;
466 continue;
467 }
468 newbyte = *nj.pos++;
469 nj.size--;
470 nj.bufbits += 8;
471 nj.buf = shiftLeft(nj.buf, 8) | newbyte;
472 if (newbyte == 0xFF) {
473 if (nj.size) {
474 unsigned char marker = *nj.pos++;
475 nj.size--;
476 switch (marker) {
477 case 0x00:
478 case 0xFF:
479 break;
480 case 0xD9: nj.size = 0; break;
481 default:
482 if ((marker & 0xF8) != 0xD0)
483 nj.error = NJ_SYNTAX_ERROR;
484 else {
485 nj.buf = shiftLeft(nj.buf, 8) | marker;
486 nj.bufbits += 8;
487 }
488 }
489 } else
490 nj.error = NJ_SYNTAX_ERROR;
491 }
492 }
493 return (nj.buf >> (nj.bufbits - bits)) & ((1 << bits) - 1);
494 }
495
njSkipBits(int bits)496 NJ_INLINE void njSkipBits(int bits) {
497 if (nj.bufbits < bits)
498 (void) njShowBits(bits);
499 nj.bufbits -= bits;
500 }
501
njGetBits(int bits)502 NJ_INLINE int njGetBits(int bits) {
503 int res = njShowBits(bits);
504 njSkipBits(bits);
505 return res;
506 }
507
njByteAlign(void)508 NJ_INLINE void njByteAlign(void) {
509 nj.bufbits &= 0xF8;
510 }
511
njSkip(int count)512 static void njSkip(int count) {
513 nj.pos += count;
514 nj.size -= count;
515 nj.length -= count;
516 if (nj.size < 0) nj.error = NJ_SYNTAX_ERROR;
517 }
518
njDecode16(const unsigned char * pos)519 NJ_INLINE unsigned short njDecode16(const unsigned char *pos) {
520 return (pos[0] << 8) | pos[1];
521 }
522
njDecodeLength(void)523 static void njDecodeLength(void) {
524 if (nj.size < 2) njThrow(NJ_SYNTAX_ERROR);
525 nj.length = njDecode16(nj.pos);
526 if (nj.length > nj.size) njThrow(NJ_SYNTAX_ERROR);
527 njSkip(2);
528 }
529
njSkipMarker(void)530 NJ_INLINE void njSkipMarker(void) {
531 njDecodeLength();
532 njSkip(nj.length);
533 }
534
njDecodeSOF(void)535 NJ_INLINE void njDecodeSOF(void) {
536 int i, ssxmax = 0, ssymax = 0;
537 nj_component_t* c;
538 njDecodeLength();
539 njCheckError();
540 if (nj.length < 9) njThrow(NJ_SYNTAX_ERROR);
541 if (nj.pos[0] != 8) njThrow(NJ_UNSUPPORTED);
542 nj.height = njDecode16(nj.pos+1);
543 nj.width = njDecode16(nj.pos+3);
544 if (!nj.width || !nj.height) njThrow(NJ_SYNTAX_ERROR);
545 nj.ncomp = nj.pos[5];
546 njSkip(6);
547 switch (nj.ncomp) {
548 case 1:
549 case 3:
550 break;
551 default:
552 njThrow(NJ_UNSUPPORTED);
553 }
554 if (nj.length < (nj.ncomp * 3)) njThrow(NJ_SYNTAX_ERROR);
555 for (i = 0, c = nj.comp; i < nj.ncomp; ++i, ++c) {
556 c->cid = nj.pos[0];
557 if (!(c->ssx = nj.pos[1] >> 4)) njThrow(NJ_SYNTAX_ERROR);
558 if (c->ssx & (c->ssx - 1)) njThrow(NJ_UNSUPPORTED); // non-power of two
559 if (!(c->ssy = nj.pos[1] & 15)) njThrow(NJ_SYNTAX_ERROR);
560 if (c->ssy & (c->ssy - 1)) njThrow(NJ_UNSUPPORTED); // non-power of two
561 if ((c->qtsel = nj.pos[2]) & 0xFC) njThrow(NJ_SYNTAX_ERROR);
562 njSkip(3);
563 nj.qtused |= 1 << c->qtsel;
564 if (c->ssx > ssxmax) ssxmax = c->ssx;
565 if (c->ssy > ssymax) ssymax = c->ssy;
566 }
567 if (nj.ncomp == 1) {
568 c = nj.comp;
569 c->ssx = c->ssy = ssxmax = ssymax = 1;
570 }
571 nj.mbsizex = shiftLeft(ssxmax, 3);
572 nj.mbsizey = shiftLeft(ssymax, 3);
573 nj.mbwidth = (nj.width + nj.mbsizex - 1) / nj.mbsizex;
574 nj.mbheight = (nj.height + nj.mbsizey - 1) / nj.mbsizey;
575 for (i = 0, c = nj.comp; i < nj.ncomp; ++i, ++c) {
576 c->width = (nj.width * c->ssx + ssxmax - 1) / ssxmax;
577 c->height = (nj.height * c->ssy + ssymax - 1) / ssymax;
578 c->stride = nj.mbwidth * shiftLeft(c->ssx, 3);
579 if (((c->width < 3) && (c->ssx != ssxmax)) || ((c->height < 3) && (c->ssy != ssymax))) njThrow(NJ_UNSUPPORTED);
580 if (!(c->pixels = (unsigned char*) njAllocMem(c->stride * nj.mbheight * shiftLeft(c->ssy, 3)))) njThrow(NJ_OUT_OF_MEM);
581 }
582 if (nj.ncomp == 3) {
583 nj.rgb = (unsigned char*) njAllocMem(nj.width * nj.height * nj.ncomp);
584 if (!nj.rgb) njThrow(NJ_OUT_OF_MEM);
585 }
586 njSkip(nj.length);
587 }
588
njDecodeDHT(void)589 NJ_INLINE void njDecodeDHT(void) {
590 int codelen, currcnt, remain, spread, i, j;
591 nj_vlc_code_t *vlc;
592 static unsigned char counts[16];
593 njDecodeLength();
594 njCheckError();
595 while (nj.length >= 17) {
596 i = nj.pos[0];
597 if (i & 0xEC) njThrow(NJ_SYNTAX_ERROR);
598 if (i & 0x02) njThrow(NJ_UNSUPPORTED);
599 i = (i | (i >> 3)) & 3; // combined DC/AC + tableid value
600 for (codelen = 1; codelen <= 16; ++codelen)
601 counts[codelen - 1] = nj.pos[codelen];
602 njSkip(17);
603 vlc = &nj.vlctab[i][0];
604 remain = spread = 65536;
605 for (codelen = 1; codelen <= 16; ++codelen) {
606 spread >>= 1;
607 currcnt = counts[codelen - 1];
608 if (!currcnt) continue;
609 if (nj.length < currcnt) njThrow(NJ_SYNTAX_ERROR);
610 remain -= shiftLeft(currcnt, 16 - codelen);
611 if (remain < 0) njThrow(NJ_SYNTAX_ERROR);
612 for (i = 0; i < currcnt; ++i) {
613 unsigned char code = nj.pos[i];
614 for (j = spread; j; --j) {
615 vlc->bits = (unsigned char) codelen;
616 vlc->code = code;
617 ++vlc;
618 }
619 }
620 njSkip(currcnt);
621 }
622 while (remain--) {
623 vlc->bits = 0;
624 ++vlc;
625 }
626 }
627 if (nj.length) njThrow(NJ_SYNTAX_ERROR);
628 }
629
njDecodeDQT(void)630 NJ_INLINE void njDecodeDQT(void) {
631 int i;
632 unsigned char *t;
633 njDecodeLength();
634 njCheckError();
635 while (nj.length >= 65) {
636 i = nj.pos[0];
637 if (i & 0xFC) njThrow(NJ_SYNTAX_ERROR);
638 nj.qtavail |= 1 << i;
639 t = &nj.qtab[i][0];
640 for (i = 0; i < 64; ++i)
641 t[i] = nj.pos[i + 1];
642 njSkip(65);
643 }
644 if (nj.length) njThrow(NJ_SYNTAX_ERROR);
645 }
646
njDecodeDRI(void)647 NJ_INLINE void njDecodeDRI(void) {
648 njDecodeLength();
649 njCheckError();
650 if (nj.length < 2) njThrow(NJ_SYNTAX_ERROR);
651 nj.rstinterval = njDecode16(nj.pos);
652 njSkip(nj.length);
653 }
654
njGetVLC(nj_vlc_code_t * vlc,unsigned char * code)655 static int njGetVLC(nj_vlc_code_t* vlc, unsigned char* code) {
656 int value = njShowBits(16);
657 int bits = vlc[value].bits;
658 if (!bits) { nj.error = NJ_SYNTAX_ERROR; return 0; }
659 njSkipBits(bits);
660 value = vlc[value].code;
661 if (code) *code = (unsigned char) value;
662 bits = value & 15;
663 if (!bits) return 0;
664 value = njGetBits(bits);
665 if (value < (1 << (bits - 1)))
666 value += (shiftLeft(-1, bits)) + 1;
667 return value;
668 }
669
njDecodeBlock(nj_component_t * c,unsigned char * out)670 NJ_INLINE void njDecodeBlock(nj_component_t* c, unsigned char* out) {
671 unsigned char code = 0;
672 int value, coef = 0;
673 njFillMem(nj.block, 0, sizeof(nj.block));
674 c->dcpred += njGetVLC(&nj.vlctab[c->dctabsel][0], NULL);
675 nj.block[0] = (c->dcpred) * nj.qtab[c->qtsel][0];
676 do {
677 value = njGetVLC(&nj.vlctab[c->actabsel][0], &code);
678 if (!code) break; // EOB
679 if (!(code & 0x0F) && (code != 0xF0)) njThrow(NJ_SYNTAX_ERROR);
680 coef += (code >> 4) + 1;
681 if (coef > 63) njThrow(NJ_SYNTAX_ERROR);
682 nj.block[(int) njZZ[coef]] = value * nj.qtab[c->qtsel][coef];
683 } while (coef < 63);
684 for (coef = 0; coef < 64; coef += 8)
685 njRowIDCT(&nj.block[coef]);
686 for (coef = 0; coef < 8; ++coef)
687 njColIDCT(&nj.block[coef], &out[coef], c->stride);
688 }
689
njDecodeScan(void)690 NJ_INLINE void njDecodeScan(void) {
691 int i, mbx, mby, sbx, sby;
692 int rstcount = nj.rstinterval, nextrst = 0;
693 nj_component_t* c;
694 njDecodeLength();
695 njCheckError();
696 if (nj.length < (4 + 2 * nj.ncomp)) njThrow(NJ_SYNTAX_ERROR);
697 if (nj.pos[0] != nj.ncomp) njThrow(NJ_UNSUPPORTED);
698 njSkip(1);
699 for (i = 0, c = nj.comp; i < nj.ncomp; ++i, ++c) {
700 if (nj.pos[0] != c->cid) njThrow(NJ_SYNTAX_ERROR);
701 if (nj.pos[1] & 0xEE) njThrow(NJ_SYNTAX_ERROR);
702 c->dctabsel = nj.pos[1] >> 4;
703 c->actabsel = (nj.pos[1] & 1) | 2;
704 njSkip(2);
705 }
706 if (nj.pos[0] || (nj.pos[1] != 63) || nj.pos[2]) njThrow(NJ_UNSUPPORTED);
707 njSkip(nj.length);
708 for (mbx = mby = 0;;) {
709 for (i = 0, c = nj.comp; i < nj.ncomp; ++i, ++c)
710 for (sby = 0; sby < c->ssy; ++sby)
711 for (sbx = 0; sbx < c->ssx; ++sbx) {
712 njDecodeBlock(c, &c->pixels[shiftLeft((mby * c->ssy + sby) * c->stride + mbx * c->ssx + sbx, 3)]);
713 njCheckError();
714 }
715 if (++mbx >= nj.mbwidth) {
716 mbx = 0;
717 if (++mby >= nj.mbheight) break;
718 }
719 if (nj.rstinterval && !(--rstcount)) {
720 njByteAlign();
721 i = njGetBits(16);
722 if (((i & 0xFFF8) != 0xFFD0) || ((i & 7) != nextrst)) njThrow(NJ_SYNTAX_ERROR);
723 nextrst = (nextrst + 1) & 7;
724 rstcount = nj.rstinterval;
725 for (i = 0; i < 3; ++i)
726 nj.comp[i].dcpred = 0;
727 }
728 }
729 nj.error = __NJ_FINISHED;
730 }
731
732 #if NJ_CHROMA_FILTER
733
734 #define CF4A (-9)
735 #define CF4B (111)
736 #define CF4C (29)
737 #define CF4D (-3)
738 #define CF3A (28)
739 #define CF3B (109)
740 #define CF3C (-9)
741 #define CF3X (104)
742 #define CF3Y (27)
743 #define CF3Z (-3)
744 #define CF2A (139)
745 #define CF2B (-11)
746 #define CF(x) njClip(((x) + 64) >> 7)
747
njUpsampleH(nj_component_t * c)748 NJ_INLINE void njUpsampleH(nj_component_t* c) {
749 const int xmax = c->width - 3;
750 unsigned char *out, *lin, *lout;
751 int x, y;
752 out = (unsigned char*) njAllocMem((c->width * c->height) << 1);
753 if (!out) njThrow(NJ_OUT_OF_MEM);
754 lin = c->pixels;
755 lout = out;
756 for (y = c->height; y; --y) {
757 lout[0] = CF(CF2A * lin[0] + CF2B * lin[1]);
758 lout[1] = CF(CF3X * lin[0] + CF3Y * lin[1] + CF3Z * lin[2]);
759 lout[2] = CF(CF3A * lin[0] + CF3B * lin[1] + CF3C * lin[2]);
760 for (x = 0; x < xmax; ++x) {
761 lout[(x << 1) + 3] = CF(CF4A * lin[x] + CF4B * lin[x + 1] + CF4C * lin[x + 2] + CF4D * lin[x + 3]);
762 lout[(x << 1) + 4] = CF(CF4D * lin[x] + CF4C * lin[x + 1] + CF4B * lin[x + 2] + CF4A * lin[x + 3]);
763 }
764 lin += c->stride;
765 lout += c->width << 1;
766 lout[-3] = CF(CF3A * lin[-1] + CF3B * lin[-2] + CF3C * lin[-3]);
767 lout[-2] = CF(CF3X * lin[-1] + CF3Y * lin[-2] + CF3Z * lin[-3]);
768 lout[-1] = CF(CF2A * lin[-1] + CF2B * lin[-2]);
769 }
770 c->width <<= 1;
771 c->stride = c->width;
772 njFreeMem((void*)c->pixels);
773 c->pixels = out;
774 }
775
njUpsampleV(nj_component_t * c)776 NJ_INLINE void njUpsampleV(nj_component_t* c) {
777 const int w = c->width, s1 = c->stride, s2 = s1 + s1;
778 unsigned char *out, *cin, *cout;
779 int x, y;
780 out = (unsigned char*) njAllocMem((c->width * c->height) << 1);
781 if (!out) njThrow(NJ_OUT_OF_MEM);
782 for (x = 0; x < w; ++x) {
783 cin = &c->pixels[x];
784 cout = &out[x];
785 *cout = CF(CF2A * cin[0] + CF2B * cin[s1]); cout += w;
786 *cout = CF(CF3X * cin[0] + CF3Y * cin[s1] + CF3Z * cin[s2]); cout += w;
787 *cout = CF(CF3A * cin[0] + CF3B * cin[s1] + CF3C * cin[s2]); cout += w;
788 cin += s1;
789 for (y = c->height - 3; y; --y) {
790 *cout = CF(CF4A * cin[-s1] + CF4B * cin[0] + CF4C * cin[s1] + CF4D * cin[s2]); cout += w;
791 *cout = CF(CF4D * cin[-s1] + CF4C * cin[0] + CF4B * cin[s1] + CF4A * cin[s2]); cout += w;
792 cin += s1;
793 }
794 cin += s1;
795 *cout = CF(CF3A * cin[0] + CF3B * cin[-s1] + CF3C * cin[-s2]); cout += w;
796 *cout = CF(CF3X * cin[0] + CF3Y * cin[-s1] + CF3Z * cin[-s2]); cout += w;
797 *cout = CF(CF2A * cin[0] + CF2B * cin[-s1]);
798 }
799 c->height <<= 1;
800 c->stride = c->width;
801 njFreeMem((void*) c->pixels);
802 c->pixels = out;
803 }
804
805 #else
806
njUpsample(nj_component_t * c)807 NJ_INLINE void njUpsample(nj_component_t* c) {
808 int x, y, xshift = 0, yshift = 0;
809 unsigned char *out, *lin, *lout;
810 while (c->width < nj.width) { c->width <<= 1; ++xshift; }
811 while (c->height < nj.height) { c->height <<= 1; ++yshift; }
812 out = (unsigned char*) njAllocMem(c->width * c->height);
813 if (!out) njThrow(NJ_OUT_OF_MEM);
814 lin = c->pixels;
815 lout = out;
816 for (y = 0; y < c->height; ++y) {
817 lin = &c->pixels[(y >> yshift) * c->stride];
818 for (x = 0; x < c->width; ++x)
819 lout[x] = lin[x >> xshift];
820 lout += c->width;
821 }
822 c->stride = c->width;
823 njFreeMem((void*) c->pixels);
824 c->pixels = out;
825 }
826
827 #endif
828
njConvert(void)829 NJ_INLINE void njConvert(void) {
830 int i;
831 nj_component_t* c;
832 for (i = 0, c = nj.comp; i < nj.ncomp; ++i, ++c) {
833 #if NJ_CHROMA_FILTER
834 while ((c->width < nj.width) || (c->height < nj.height)) {
835 if (c->width < nj.width) njUpsampleH(c);
836 njCheckError();
837 if (c->height < nj.height) njUpsampleV(c);
838 njCheckError();
839 }
840 #else
841 if ((c->width < nj.width) || (c->height < nj.height))
842 njUpsample(c);
843 #endif
844 if ((c->width < nj.width) || (c->height < nj.height)) njThrow(NJ_INTERNAL_ERR);
845 }
846 if (nj.ncomp == 3) {
847 // convert to RGB
848 int x, yy;
849 unsigned char *prgb = nj.rgb;
850 const unsigned char *py = nj.comp[0].pixels;
851 const unsigned char *pcb = nj.comp[1].pixels;
852 const unsigned char *pcr = nj.comp[2].pixels;
853 for (yy = nj.height; yy; --yy) {
854 for (x = 0; x < nj.width; ++x) {
855 int y = py[x] << 8;
856 int cb = pcb[x] - 128;
857 int cr = pcr[x] - 128;
858 *prgb++ = njClip((y + 359 * cr + 128) >> 8);
859 *prgb++ = njClip((y - 88 * cb - 183 * cr + 128) >> 8);
860 *prgb++ = njClip((y + 454 * cb + 128) >> 8);
861 }
862 py += nj.comp[0].stride;
863 pcb += nj.comp[1].stride;
864 pcr += nj.comp[2].stride;
865 }
866 } else if (nj.comp[0].width != nj.comp[0].stride) {
867 // grayscale -> only remove stride
868 unsigned char *pin = &nj.comp[0].pixels[nj.comp[0].stride];
869 unsigned char *pout = &nj.comp[0].pixels[nj.comp[0].width];
870 int y;
871 for (y = nj.comp[0].height - 1; y; --y) {
872 njCopyMem(pout, pin, nj.comp[0].width);
873 pin += nj.comp[0].stride;
874 pout += nj.comp[0].width;
875 }
876 nj.comp[0].stride = nj.comp[0].width;
877 }
878 }
879
njInit(void)880 void njInit(void) {
881 njFillMem(&nj, 0, sizeof(nj_context_t));
882 }
883
njDone(void)884 void njDone(void) {
885 int i;
886 for (i = 0; i < 3; ++i)
887 if (nj.comp[i].pixels) njFreeMem((void*) nj.comp[i].pixels);
888 if (nj.rgb) njFreeMem((void*) nj.rgb);
889 njInit();
890 }
891
njDecode(const void * jpeg,const int size)892 nj_result_t njDecode(const void* jpeg, const int size) {
893 njDone();
894 nj.pos = (const unsigned char*) jpeg;
895 nj.size = size & 0x7FFFFFFF;
896 if (nj.size < 2) return NJ_NO_JPEG;
897 if ((nj.pos[0] ^ 0xFF) | (nj.pos[1] ^ 0xD8)) return NJ_NO_JPEG;
898 njSkip(2);
899 while (!nj.error) {
900 if ((nj.size < 2) || (nj.pos[0] != 0xFF)) return NJ_SYNTAX_ERROR;
901 njSkip(2);
902 switch (nj.pos[-1]) {
903 case 0xC0: njDecodeSOF(); break;
904 case 0xC4: njDecodeDHT(); break;
905 case 0xDB: njDecodeDQT(); break;
906 case 0xDD: njDecodeDRI(); break;
907 case 0xDA: njDecodeScan(); break;
908 case 0xFE: njSkipMarker(); break;
909 default:
910 if ((nj.pos[-1] & 0xF0) == 0xE0)
911 njSkipMarker();
912 else
913 return NJ_UNSUPPORTED;
914 }
915 }
916 if (nj.error != __NJ_FINISHED) return nj.error;
917 nj.error = NJ_OK;
918 njConvert();
919 return nj.error;
920 }
921
njGetWidth(void)922 int njGetWidth(void) { return nj.width; }
njGetHeight(void)923 int njGetHeight(void) { return nj.height; }
njIsColor(void)924 int njIsColor(void) { return (nj.ncomp != 1); }
njGetImage(void)925 unsigned char* njGetImage(void) { return (nj.ncomp == 1) ? nj.comp[0].pixels : nj.rgb; }
njGetImageSize(void)926 int njGetImageSize(void) { return nj.width * nj.height * nj.ncomp; }
927
928 #endif // _NJ_INCLUDE_HEADER_ONLY