lilimg 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/ext/nanojpeg.h ADDED
@@ -0,0 +1,928 @@
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 pase 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
+ #include "jo_gif.h"
209
+
210
+ int main(int argc, char* argv[]) {
211
+ int size;
212
+ char *buf;
213
+ FILE *f;
214
+
215
+ if (argc < 2) {
216
+ printf("Usage: %s <input.jpg> [<output.ppm>]\n", argv[0]);
217
+ return 2;
218
+ }
219
+ f = fopen(argv[1], "rb");
220
+ if (!f) {
221
+ printf("Error opening the input file.\n");
222
+ return 1;
223
+ }
224
+ fseek(f, 0, SEEK_END);
225
+ size = (int) ftell(f);
226
+ buf = (char*) malloc(size);
227
+ fseek(f, 0, SEEK_SET);
228
+ size = (int) fread(buf, 1, size, f);
229
+ fclose(f);
230
+
231
+ njInit();
232
+ if (njDecode(buf, size)) {
233
+ free((void*)buf);
234
+ printf("Error decoding the input file.\n");
235
+ return 1;
236
+ }
237
+ free((void*)buf);
238
+
239
+ unsigned char * pixels = njGetImage();
240
+
241
+ // width/height | the same for every frame
242
+ // repeat | 0 = loop forever, 1 = loop once, etc...
243
+ // palSize | must be power of 2 - 1. so, 255 not 256.
244
+ jo_gif_t gif = jo_gif_start("foo.gif", njGetWidth(), njGetHeight(), 0, 3);
245
+ jo_gif_frame(&gif, pixels, 0, 0);
246
+ jo_gif_end(&gif);
247
+
248
+ // f = fopen((argc > 2) ? argv[2] : (njIsColor() ? "nanojpeg_out.ppm" : "nanojpeg_out.pgm"), "wb");
249
+ // if (!f) {
250
+ // printf("Error opening the output file.\n");
251
+ // return 1;
252
+ // }
253
+
254
+ // fprintf(f, "P%d\n%d %d\n255\n", njIsColor() ? 6 : 5, njGetWidth(), njGetHeight());
255
+ // fwrite(njGetImage(), 1, njGetImageSize(), f);
256
+ // fclose(f);
257
+
258
+ njDone();
259
+ return 0;
260
+ }
261
+
262
+ #endif
263
+
264
+
265
+ ///////////////////////////////////////////////////////////////////////////////
266
+ // IMPLEMENTATION SECTION //
267
+ // you may stop reading here //
268
+ ///////////////////////////////////////////////////////////////////////////////
269
+
270
+ #ifndef _NJ_INCLUDE_HEADER_ONLY
271
+
272
+ #ifdef _MSC_VER
273
+ #define NJ_INLINE static __inline
274
+ #define NJ_FORCE_INLINE static __forceinline
275
+ #else
276
+ #define NJ_INLINE static inline
277
+ #define NJ_FORCE_INLINE static inline
278
+ #endif
279
+
280
+ #if NJ_USE_LIBC
281
+ #include <stdlib.h>
282
+ #include <string.h>
283
+ #define njAllocMem malloc
284
+ #define njFreeMem free
285
+ #define njFillMem memset
286
+ #define njCopyMem memcpy
287
+ #elif NJ_USE_WIN32
288
+ #include <windows.h>
289
+ #define njAllocMem(size) ((void*) LocalAlloc(LMEM_FIXED, (SIZE_T)(size)))
290
+ #define njFreeMem(block) ((void) LocalFree((HLOCAL) block))
291
+ NJ_INLINE void njFillMem(void* block, unsigned char value, int count) { __asm {
292
+ mov edi, block
293
+ mov al, value
294
+ mov ecx, count
295
+ rep stosb
296
+ } }
297
+ NJ_INLINE void njCopyMem(void* dest, const void* src, int count) { __asm {
298
+ mov edi, dest
299
+ mov esi, src
300
+ mov ecx, count
301
+ rep movsb
302
+ } }
303
+ #else
304
+ extern void* njAllocMem(int size);
305
+ extern void njFreeMem(void* block);
306
+ extern void njFillMem(void* block, unsigned char byte, int size);
307
+ extern void njCopyMem(void* dest, const void* src, int size);
308
+ #endif
309
+
310
+ typedef struct _nj_code {
311
+ unsigned char bits, code;
312
+ } nj_vlc_code_t;
313
+
314
+ typedef struct _nj_cmp {
315
+ int cid;
316
+ int ssx, ssy;
317
+ int width, height;
318
+ int stride;
319
+ int qtsel;
320
+ int actabsel, dctabsel;
321
+ int dcpred;
322
+ unsigned char *pixels;
323
+ } nj_component_t;
324
+
325
+ typedef struct _nj_ctx {
326
+ nj_result_t error;
327
+ const unsigned char *pos;
328
+ int size;
329
+ int length;
330
+ int width, height;
331
+ int mbwidth, mbheight;
332
+ int mbsizex, mbsizey;
333
+ int ncomp;
334
+ nj_component_t comp[3];
335
+ int qtused, qtavail;
336
+ unsigned char qtab[4][64];
337
+ nj_vlc_code_t vlctab[4][65536];
338
+ int buf, bufbits;
339
+ int block[64];
340
+ int rstinterval;
341
+ unsigned char *rgb;
342
+ } nj_context_t;
343
+
344
+ static nj_context_t nj;
345
+
346
+ static const char njZZ[64] = { 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18,
347
+ 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35,
348
+ 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45,
349
+ 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 };
350
+
351
+ NJ_FORCE_INLINE unsigned char njClip(const int x) {
352
+ return (x < 0) ? 0 : ((x > 0xFF) ? 0xFF : (unsigned char) x);
353
+ }
354
+
355
+ #define W1 2841
356
+ #define W2 2676
357
+ #define W3 2408
358
+ #define W5 1609
359
+ #define W6 1108
360
+ #define W7 565
361
+
362
+ NJ_INLINE void njRowIDCT(int* blk) {
363
+ int x0, x1, x2, x3, x4, x5, x6, x7, x8;
364
+ if (!((x1 = blk[4] << 11)
365
+ | (x2 = blk[6])
366
+ | (x3 = blk[2])
367
+ | (x4 = blk[1])
368
+ | (x5 = blk[7])
369
+ | (x6 = blk[5])
370
+ | (x7 = blk[3])))
371
+ {
372
+ blk[0] = blk[1] = blk[2] = blk[3] = blk[4] = blk[5] = blk[6] = blk[7] = blk[0] << 3;
373
+ return;
374
+ }
375
+ x0 = (blk[0] << 11) + 128;
376
+ x8 = W7 * (x4 + x5);
377
+ x4 = x8 + (W1 - W7) * x4;
378
+ x5 = x8 - (W1 + W7) * x5;
379
+ x8 = W3 * (x6 + x7);
380
+ x6 = x8 - (W3 - W5) * x6;
381
+ x7 = x8 - (W3 + W5) * x7;
382
+ x8 = x0 + x1;
383
+ x0 -= x1;
384
+ x1 = W6 * (x3 + x2);
385
+ x2 = x1 - (W2 + W6) * x2;
386
+ x3 = x1 + (W2 - W6) * x3;
387
+ x1 = x4 + x6;
388
+ x4 -= x6;
389
+ x6 = x5 + x7;
390
+ x5 -= x7;
391
+ x7 = x8 + x3;
392
+ x8 -= x3;
393
+ x3 = x0 + x2;
394
+ x0 -= x2;
395
+ x2 = (181 * (x4 + x5) + 128) >> 8;
396
+ x4 = (181 * (x4 - x5) + 128) >> 8;
397
+ blk[0] = (x7 + x1) >> 8;
398
+ blk[1] = (x3 + x2) >> 8;
399
+ blk[2] = (x0 + x4) >> 8;
400
+ blk[3] = (x8 + x6) >> 8;
401
+ blk[4] = (x8 - x6) >> 8;
402
+ blk[5] = (x0 - x4) >> 8;
403
+ blk[6] = (x3 - x2) >> 8;
404
+ blk[7] = (x7 - x1) >> 8;
405
+ }
406
+
407
+ NJ_INLINE void njColIDCT(const int* blk, unsigned char *out, int stride) {
408
+ int x0, x1, x2, x3, x4, x5, x6, x7, x8;
409
+ if (!((x1 = blk[8*4] << 8)
410
+ | (x2 = blk[8*6])
411
+ | (x3 = blk[8*2])
412
+ | (x4 = blk[8*1])
413
+ | (x5 = blk[8*7])
414
+ | (x6 = blk[8*5])
415
+ | (x7 = blk[8*3])))
416
+ {
417
+ x1 = njClip(((blk[0] + 32) >> 6) + 128);
418
+ for (x0 = 8; x0; --x0) {
419
+ *out = (unsigned char) x1;
420
+ out += stride;
421
+ }
422
+ return;
423
+ }
424
+ x0 = (blk[0] << 8) + 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
+
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 = (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 = (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 = (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
+
496
+ NJ_INLINE void njSkipBits(int bits) {
497
+ if (nj.bufbits < bits)
498
+ (void) njShowBits(bits);
499
+ nj.bufbits -= bits;
500
+ }
501
+
502
+ NJ_INLINE int njGetBits(int bits) {
503
+ int res = njShowBits(bits);
504
+ njSkipBits(bits);
505
+ return res;
506
+ }
507
+
508
+ NJ_INLINE void njByteAlign(void) {
509
+ nj.bufbits &= 0xF8;
510
+ }
511
+
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
+
519
+ NJ_INLINE unsigned short njDecode16(const unsigned char *pos) {
520
+ return (pos[0] << 8) | pos[1];
521
+ }
522
+
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
+
530
+ NJ_INLINE void njSkipMarker(void) {
531
+ njDecodeLength();
532
+ njSkip(nj.length);
533
+ }
534
+
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 = ssxmax << 3;
572
+ nj.mbsizey = 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 * 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 * 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
+
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 -= currcnt << (16 - codelen);
611
+ if (remain < 0) njThrow(NJ_SYNTAX_ERROR);
612
+ for (i = 0; i < currcnt; ++i) {
613
+ register 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
+
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
+
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
+
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 += ((-1) << bits) + 1;
667
+ return value;
668
+ }
669
+
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
+
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[((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
+
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
+
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
+
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
+
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
+ register int y = py[x] << 8;
856
+ register int cb = pcb[x] - 128;
857
+ register 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
+
880
+ void njInit(void) {
881
+ njFillMem(&nj, 0, sizeof(nj_context_t));
882
+ }
883
+
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
+
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
+
922
+ int njGetWidth(void) { return nj.width; }
923
+ int njGetHeight(void) { return nj.height; }
924
+ int njIsColor(void) { return (nj.ncomp != 1); }
925
+ unsigned char* njGetImage(void) { return (nj.ncomp == 1) ? nj.comp[0].pixels : nj.rgb; }
926
+ int njGetImageSize(void) { return nj.width * nj.height * nj.ncomp; }
927
+
928
+ #endif // _NJ_INCLUDE_HEADER_ONLY