lilimg 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/Rakefile +1 -0
- data/example/build.sh +2 -0
- data/example/label.jpg +0 -0
- data/example/test.c +86 -0
- data/example/test.rb +7 -0
- data/ext/extconf.rb +7 -0
- data/ext/jo_gif.h +427 -0
- data/ext/lilimg.c +71 -0
- data/ext/nanojpeg.h +928 -0
- data/lilimg.gemspec +17 -0
- metadata +54 -0
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
|