1 // ==========================================================
2 // MNG / JNG helpers
3 //
4 // Design and implementation by
5 // - Herv� Drolon (drolon@infonie.fr)
6 //
7 // This file is part of FreeImage 3
8 //
9 // COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
10 // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
11 // THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
12 // OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
13 // CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
14 // THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
15 // SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
16 // PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
17 // THIS DISCLAIMER.
18 //
19 // Use at your own risk!
20 // ==========================================================
21
22 #include "FreeImage.h"
23 #include "Utilities.h"
24
25 /**
26 References
27 http://www.libpng.org/pub/mng/spec/jng.html
28 http://www.w3.org/TR/PNG/
29 http://libpng.org/pub/mng/spec/
30 */
31
32 // --------------------------------------------------------------------------
33
34 #define MNG_INCLUDE_JNG
35
36 #ifdef MNG_INCLUDE_JNG
37 #define MNG_COLORTYPE_JPEGGRAY 8 /* JHDR */
38 #define MNG_COLORTYPE_JPEGCOLOR 10
39 #define MNG_COLORTYPE_JPEGGRAYA 12
40 #define MNG_COLORTYPE_JPEGCOLORA 14
41
42 #define MNG_BITDEPTH_JPEG8 8 /* JHDR */
43 #define MNG_BITDEPTH_JPEG12 12
44 #define MNG_BITDEPTH_JPEG8AND12 20
45
46 #define MNG_COMPRESSION_BASELINEJPEG 8 /* JHDR */
47
48 #define MNG_INTERLACE_SEQUENTIAL 0 /* JHDR */
49 #define MNG_INTERLACE_PROGRESSIVE 8
50 #endif /* MNG_INCLUDE_JNG */
51
52 // --------------------------------------------------------------------------
53
54 #define JNG_SUPPORTED
55
56 /** Size of a JDAT chunk on writing */
57 const DWORD JPEG_CHUNK_SIZE = 8192;
58
59 /** PNG signature */
60 static const BYTE g_png_signature[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
61 /** JNG signature */
62 static const BYTE g_jng_signature[8] = { 139, 74, 78, 71, 13, 10, 26, 10 };
63
64 // --------------------------------------------------------------------------
65
66 /** Chunk type converted to enum */
67 enum eChunckType {
68 UNKNOWN_CHUNCK,
69 MHDR,
70 BACK,
71 BASI,
72 CLIP,
73 CLON,
74 DEFI,
75 DHDR,
76 DISC,
77 ENDL,
78 FRAM,
79 IEND,
80 IHDR,
81 JHDR,
82 LOOP,
83 MAGN,
84 MEND,
85 MOVE,
86 PAST,
87 PLTE,
88 SAVE,
89 SEEK,
90 SHOW,
91 TERM,
92 bKGD,
93 cHRM,
94 gAMA,
95 iCCP,
96 nEED,
97 pHYg,
98 vpAg,
99 pHYs,
100 sBIT,
101 sRGB,
102 tRNS,
103 IDAT,
104 JDAT,
105 JDAA,
106 JdAA,
107 JSEP,
108 oFFs,
109 hIST,
110 iTXt,
111 sPLT,
112 sTER,
113 tEXt,
114 tIME,
115 zTXt
116 };
117
118 /**
119 Helper for map<key, value> where value is a pointer to a string.
120 Used to store tEXt metadata.
121 */
122 typedef std::map<std::string, std::string> tEXtMAP;
123
124 // --------------------------------------------------------------------------
125
126 /*
127 Constant strings for known chunk types. If you need to add a chunk,
128 add a string holding the name here. To make the code more
129 portable, we use ASCII numbers like this, not characters.
130 */
131
132 static BYTE mng_MHDR[5]={ 77, 72, 68, 82, (BYTE) '\0'};
133 static BYTE mng_BACK[5]={ 66, 65, 67, 75, (BYTE) '\0'};
134 static BYTE mng_BASI[5]={ 66, 65, 83, 73, (BYTE) '\0'};
135 static BYTE mng_CLIP[5]={ 67, 76, 73, 80, (BYTE) '\0'};
136 static BYTE mng_CLON[5]={ 67, 76, 79, 78, (BYTE) '\0'};
137 static BYTE mng_DEFI[5]={ 68, 69, 70, 73, (BYTE) '\0'};
138 static BYTE mng_DHDR[5]={ 68, 72, 68, 82, (BYTE) '\0'};
139 static BYTE mng_DISC[5]={ 68, 73, 83, 67, (BYTE) '\0'};
140 static BYTE mng_ENDL[5]={ 69, 78, 68, 76, (BYTE) '\0'};
141 static BYTE mng_FRAM[5]={ 70, 82, 65, 77, (BYTE) '\0'};
142 static BYTE mng_IEND[5]={ 73, 69, 78, 68, (BYTE) '\0'};
143 static BYTE mng_IHDR[5]={ 73, 72, 68, 82, (BYTE) '\0'};
144 static BYTE mng_JHDR[5]={ 74, 72, 68, 82, (BYTE) '\0'};
145 static BYTE mng_LOOP[5]={ 76, 79, 79, 80, (BYTE) '\0'};
146 static BYTE mng_MAGN[5]={ 77, 65, 71, 78, (BYTE) '\0'};
147 static BYTE mng_MEND[5]={ 77, 69, 78, 68, (BYTE) '\0'};
148 static BYTE mng_MOVE[5]={ 77, 79, 86, 69, (BYTE) '\0'};
149 static BYTE mng_PAST[5]={ 80, 65, 83, 84, (BYTE) '\0'};
150 static BYTE mng_PLTE[5]={ 80, 76, 84, 69, (BYTE) '\0'};
151 static BYTE mng_SAVE[5]={ 83, 65, 86, 69, (BYTE) '\0'};
152 static BYTE mng_SEEK[5]={ 83, 69, 69, 75, (BYTE) '\0'};
153 static BYTE mng_SHOW[5]={ 83, 72, 79, 87, (BYTE) '\0'};
154 static BYTE mng_TERM[5]={ 84, 69, 82, 77, (BYTE) '\0'};
155 static BYTE mng_bKGD[5]={ 98, 75, 71, 68, (BYTE) '\0'};
156 static BYTE mng_cHRM[5]={ 99, 72, 82, 77, (BYTE) '\0'};
157 static BYTE mng_gAMA[5]={103, 65, 77, 65, (BYTE) '\0'};
158 static BYTE mng_iCCP[5]={105, 67, 67, 80, (BYTE) '\0'};
159 static BYTE mng_nEED[5]={110, 69, 69, 68, (BYTE) '\0'};
160 static BYTE mng_pHYg[5]={112, 72, 89, 103, (BYTE) '\0'};
161 static BYTE mng_vpAg[5]={118, 112, 65, 103, (BYTE) '\0'};
162 static BYTE mng_pHYs[5]={112, 72, 89, 115, (BYTE) '\0'};
163 static BYTE mng_sBIT[5]={115, 66, 73, 84, (BYTE) '\0'};
164 static BYTE mng_sRGB[5]={115, 82, 71, 66, (BYTE) '\0'};
165 static BYTE mng_tRNS[5]={116, 82, 78, 83, (BYTE) '\0'};
166
167 #if defined(JNG_SUPPORTED)
168 static BYTE mng_IDAT[5]={ 73, 68, 65, 84, (BYTE) '\0'};
169 static BYTE mng_JDAT[5]={ 74, 68, 65, 84, (BYTE) '\0'};
170 static BYTE mng_JDAA[5]={ 74, 68, 65, 65, (BYTE) '\0'};
171 static BYTE mng_JdAA[5]={ 74, 100, 65, 65, (BYTE) '\0'};
172 static BYTE mng_JSEP[5]={ 74, 83, 69, 80, (BYTE) '\0'};
173 static BYTE mng_oFFs[5]={111, 70, 70, 115, (BYTE) '\0'};
174 #endif
175
176 static BYTE mng_hIST[5]={104, 73, 83, 84, (BYTE) '\0'};
177 static BYTE mng_iTXt[5]={105, 84, 88, 116, (BYTE) '\0'};
178 static BYTE mng_sPLT[5]={115, 80, 76, 84, (BYTE) '\0'};
179 static BYTE mng_sTER[5]={115, 84, 69, 82, (BYTE) '\0'};
180 static BYTE mng_tEXt[5]={116, 69, 88, 116, (BYTE) '\0'};
181 static BYTE mng_tIME[5]={116, 73, 77, 69, (BYTE) '\0'};
182 static BYTE mng_zTXt[5]={122, 84, 88, 116, (BYTE) '\0'};
183
184
185 // --------------------------------------------------------------------------
186
187 /**
188 Convert a chunk name to a unique ID
189 */
190 static eChunckType
mng_GetChunckType(const BYTE * mChunkName)191 mng_GetChunckType(const BYTE *mChunkName) {
192 if(memcmp(mChunkName, mng_MHDR, 4) == 0) {
193 return MHDR;
194 }
195 if(memcmp(mChunkName, mng_LOOP, 4) == 0) {
196 return LOOP;
197 }
198 if(memcmp(mChunkName, mng_DEFI, 4) == 0) {
199 return DEFI;
200 }
201 if(memcmp(mChunkName, mng_PLTE, 4) == 0) {
202 return PLTE;
203 }
204 if(memcmp(mChunkName, mng_tRNS, 4) == 0) {
205 return tRNS;
206 }
207 if(memcmp(mChunkName, mng_IHDR, 4) == 0) {
208 return IHDR;
209 }
210 if(memcmp(mChunkName, mng_JHDR, 4) == 0) {
211 return JHDR;
212 }
213 if(memcmp(mChunkName, mng_MEND, 4) == 0) {
214 return MEND;
215 }
216 if(memcmp(mChunkName, mng_IEND, 4) == 0) {
217 return IEND;
218 }
219 if(memcmp(mChunkName, mng_JDAT, 4) == 0) {
220 return JDAT;
221 }
222 if(memcmp(mChunkName, mng_IDAT, 4) == 0) {
223 return IDAT;
224 }
225 if(memcmp(mChunkName, mng_JDAA, 4) == 0) {
226 return JDAA;
227 }
228 if(memcmp(mChunkName, mng_gAMA, 4) == 0) {
229 return gAMA;
230 }
231 if(memcmp(mChunkName, mng_pHYs, 4) == 0) {
232 return pHYs;
233 }
234 if(memcmp(mChunkName, mng_bKGD, 4) == 0) {
235 return bKGD;
236 }
237 if(memcmp(mChunkName, mng_tEXt, 4) == 0) {
238 return tEXt;
239 }
240
241 return UNKNOWN_CHUNCK;
242 }
243
244 inline void
mng_SwapShort(WORD * sp)245 mng_SwapShort(WORD *sp) {
246 #ifndef FREEIMAGE_BIGENDIAN
247 SwapShort(sp);
248 #endif
249 }
250
251 inline void
mng_SwapLong(DWORD * lp)252 mng_SwapLong(DWORD *lp) {
253 #ifndef FREEIMAGE_BIGENDIAN
254 SwapLong(lp);
255 #endif
256 }
257
258 /**
259 Returns the size, in bytes, of a FreeImageIO stream, from the current position.
260 */
261 static long
mng_LOF(FreeImageIO * io,fi_handle handle)262 mng_LOF(FreeImageIO *io, fi_handle handle) {
263 long start_pos = io->tell_proc(handle);
264 io->seek_proc(handle, 0, SEEK_END);
265 long file_length = io->tell_proc(handle);
266 io->seek_proc(handle, start_pos, SEEK_SET);
267 return file_length;
268 }
269
270 /**
271 Count the number of bytes in a PNG stream, from IHDR to IEND.
272 If successful, the stream position, as given by io->tell_proc(handle),
273 should be the end of the PNG stream at the return of the function.
274 @param io
275 @param handle
276 @param inPos
277 @param m_TotalBytesOfChunks
278 @return Returns TRUE if successful, returns FALSE otherwise
279 */
280 static BOOL
mng_CountPNGChunks(FreeImageIO * io,fi_handle handle,long inPos,unsigned * m_TotalBytesOfChunks)281 mng_CountPNGChunks(FreeImageIO *io, fi_handle handle, long inPos, unsigned *m_TotalBytesOfChunks) {
282 long mLOF;
283 long mPos;
284 BOOL mEnd = FALSE;
285 DWORD mLength = 0;
286 BYTE mChunkName[5];
287
288 *m_TotalBytesOfChunks = 0;
289
290 // get the length of the file
291 mLOF = mng_LOF(io, handle);
292
293 // go to the start of the file
294 io->seek_proc(handle, inPos, SEEK_SET);
295
296 try {
297 // parse chunks
298 while(mEnd == FALSE) {
299 // chunk length
300 mPos = io->tell_proc(handle);
301 if(mPos + 4 > mLOF) {
302 throw(1);
303 }
304 io->read_proc(&mLength, 1, 4, handle);
305 mng_SwapLong(&mLength);
306 // chunk name
307 mPos = io->tell_proc(handle);
308 if(mPos + 4 > mLOF) {
309 throw(1);
310 }
311 io->read_proc(&mChunkName[0], 1, 4, handle);
312 mChunkName[4] = '\0';
313
314 // go to next chunk
315 mPos = io->tell_proc(handle);
316 // 4 = size of the CRC
317 if(mPos + (long)mLength + 4 > mLOF) {
318 throw(1);
319 }
320 io->seek_proc(handle, mLength + 4, SEEK_CUR);
321
322 switch( mng_GetChunckType(mChunkName) ) {
323 case IHDR:
324 if(mLength != 13) {
325 throw(1);
326 }
327 break;
328
329 case IEND:
330 mEnd = TRUE;
331 // the length below includes 4 bytes CRC, but no bytes for Length
332 *m_TotalBytesOfChunks = io->tell_proc(handle) - inPos;
333 break;
334
335 case UNKNOWN_CHUNCK:
336 default:
337 break;
338 }
339
340 } // while(!mEnd)
341
342 return TRUE;
343
344 } catch(int) {
345 return FALSE;
346 }
347 }
348
349 /**
350 Retrieve the position of a chunk in a PNG stream
351 @param hPngMemory PNG stream handle
352 @param chunk_name Name of the chunk to be found
353 @param offset Start of the search in the stream
354 @param start_pos [returned value] Start position of the chunk
355 @param next_pos [returned value] Start position of the next chunk
356 @return Returns TRUE if successful, returns FALSE otherwise
357 */
358 static BOOL
mng_FindChunk(FIMEMORY * hPngMemory,BYTE * chunk_name,long offset,DWORD * start_pos,DWORD * next_pos)359 mng_FindChunk(FIMEMORY *hPngMemory, BYTE *chunk_name, long offset, DWORD *start_pos, DWORD *next_pos) {
360 DWORD mLength = 0;
361
362 BYTE *data = NULL;
363 DWORD size_in_bytes = 0;
364
365 *start_pos = 0;
366 *next_pos = 0;
367
368 // get a pointer to the stream buffer
369 FreeImage_AcquireMemory(hPngMemory, &data, &size_in_bytes);
370 if(!(data && size_in_bytes) || (size_in_bytes < 20) || (size_in_bytes - offset < 20)) {
371 // not enough space to read a signature(8 bytes) + a chunk(at least 12 bytes)
372 return FALSE;
373 }
374
375 try {
376
377 // skip the signature and/or any following chunk(s)
378 DWORD chunk_pos = offset;
379
380 while(1) {
381 // get chunk length
382 if(chunk_pos + 4 > size_in_bytes) {
383 break;
384 }
385
386 memcpy(&mLength, &data[chunk_pos], 4);
387 mng_SwapLong(&mLength);
388 chunk_pos += 4;
389
390 const DWORD next_chunk_pos = chunk_pos + 4 + mLength + 4;
391 if(next_chunk_pos > size_in_bytes) {
392 break;
393 }
394
395 // get chunk name
396 if(memcmp(&data[chunk_pos], chunk_name, 4) == 0) {
397 chunk_pos -= 4; // found chunk
398 *start_pos = chunk_pos;
399 *next_pos = next_chunk_pos;
400 return TRUE;
401 }
402
403 chunk_pos = next_chunk_pos;
404 }
405
406 return FALSE;
407
408 } catch(int) {
409 return FALSE;
410 }
411 }
412
413 /**
414 Remove a chunk located at (start_pos, next_pos) in the PNG stream
415 @param hPngMemory PNG stream handle
416 @param start_pos Start position of the chunk
417 @param next_pos Start position of the next chunk
418 @return Returns TRUE if successfull, returns FALSE otherwise
419 */
420 static BOOL
mng_CopyRemoveChunks(FIMEMORY * hPngMemory,DWORD start_pos,DWORD next_pos)421 mng_CopyRemoveChunks(FIMEMORY *hPngMemory, DWORD start_pos, DWORD next_pos) {
422 BYTE *data = NULL;
423 DWORD size_in_bytes = 0;
424
425 // length of the chunk to remove
426 DWORD chunk_length = next_pos - start_pos;
427 if(chunk_length == 0) {
428 return TRUE;
429 }
430
431 // get a pointer to the stream buffer
432 FreeImage_AcquireMemory(hPngMemory, &data, &size_in_bytes);
433 if(!(data && size_in_bytes) || (size_in_bytes < 20) || (chunk_length >= size_in_bytes)) {
434 // not enough space to read a signature(8 bytes) + a chunk(at least 12 bytes)
435 return FALSE;
436 }
437
438 // new file length
439 unsigned buffer_size = size_in_bytes + chunk_length;
440
441 BYTE *buffer = (BYTE*)malloc(buffer_size * sizeof(BYTE));
442 if(!buffer) {
443 return FALSE;
444 }
445 memcpy(&buffer[0], &data[0], start_pos);
446 memcpy(&buffer[start_pos], &data[next_pos], size_in_bytes - next_pos);
447
448 // seek to the start of the stream
449 FreeImage_SeekMemory(hPngMemory, 0, SEEK_SET);
450 // re-write the stream
451 FreeImage_WriteMemory(buffer, 1, buffer_size, hPngMemory);
452
453 free(buffer);
454
455 return TRUE;
456 }
457
458 /**
459 Insert a chunk just before the inNextChunkName chunk
460 @param hPngMemory PNG stream handle
461 @param start_pos Start position of the inNextChunkName chunk
462 @param next_pos Start position of the next chunk
463 @return Returns TRUE if successfull, returns FALSE otherwise
464 */
465 static BOOL
mng_CopyInsertChunks(FIMEMORY * hPngMemory,BYTE * inNextChunkName,BYTE * inInsertChunk,DWORD inChunkLength,DWORD start_pos,DWORD next_pos)466 mng_CopyInsertChunks(FIMEMORY *hPngMemory, BYTE *inNextChunkName, BYTE *inInsertChunk, DWORD inChunkLength, DWORD start_pos, DWORD next_pos) {
467 BYTE *data = NULL;
468 DWORD size_in_bytes = 0;
469
470 // length of the chunk to check
471 DWORD chunk_length = next_pos - start_pos;
472 if(chunk_length == 0) {
473 return TRUE;
474 }
475
476 // get a pointer to the stream buffer
477 FreeImage_AcquireMemory(hPngMemory, &data, &size_in_bytes);
478 if(!(data && size_in_bytes) || (size_in_bytes < 20) || (chunk_length >= size_in_bytes)) {
479 // not enough space to read a signature(8 bytes) + a chunk(at least 12 bytes)
480 return FALSE;
481 }
482
483 // new file length
484 unsigned buffer_size = inChunkLength + size_in_bytes;
485
486 BYTE *buffer = (BYTE*)malloc(buffer_size * sizeof(BYTE));
487 if(!buffer) {
488 return FALSE;
489 }
490 unsigned p = 0;
491 memcpy(&buffer[p], &data[0], start_pos);
492 p += start_pos;
493 memcpy(&buffer[p], inInsertChunk, inChunkLength);
494 p += inChunkLength;
495 memcpy(&buffer[p], &data[start_pos], size_in_bytes - start_pos);
496
497 // seek to the start of the stream
498 FreeImage_SeekMemory(hPngMemory, 0, SEEK_SET);
499 // re-write the stream
500 FreeImage_WriteMemory(buffer, 1, buffer_size, hPngMemory);
501
502 free(buffer);
503
504 return TRUE;
505 }
506
507 static BOOL
mng_RemoveChunk(FIMEMORY * hPngMemory,BYTE * chunk_name)508 mng_RemoveChunk(FIMEMORY *hPngMemory, BYTE *chunk_name) {
509 BOOL bResult = FALSE;
510
511 DWORD start_pos = 0;
512 DWORD next_pos = 0;
513
514 bResult = mng_FindChunk(hPngMemory, chunk_name, 8, &start_pos, &next_pos);
515 if(!bResult) {
516 return FALSE;
517 }
518
519 bResult = mng_CopyRemoveChunks(hPngMemory, start_pos, next_pos);
520 if(!bResult) {
521 return FALSE;
522 }
523
524 return TRUE;
525 }
526
527 static BOOL
mng_InsertChunk(FIMEMORY * hPngMemory,BYTE * inNextChunkName,BYTE * inInsertChunk,unsigned chunk_length)528 mng_InsertChunk(FIMEMORY *hPngMemory, BYTE *inNextChunkName, BYTE *inInsertChunk, unsigned chunk_length) {
529 BOOL bResult = FALSE;
530
531 DWORD start_pos = 0;
532 DWORD next_pos = 0;
533
534 bResult = mng_FindChunk(hPngMemory, inNextChunkName, 8, &start_pos, &next_pos);
535 if(!bResult) {
536 return FALSE;
537 }
538
539 bResult = mng_CopyInsertChunks(hPngMemory, inNextChunkName, inInsertChunk, chunk_length, start_pos, next_pos);
540 if(!bResult) {
541 return FALSE;
542 }
543
544 return TRUE;
545 }
546
547 static FIBITMAP*
mng_LoadFromMemoryHandle(FIMEMORY * hmem,int flags=0)548 mng_LoadFromMemoryHandle(FIMEMORY *hmem, int flags = 0) {
549 long offset = 0;
550 FIBITMAP *dib = NULL;
551
552 if(hmem) {
553 // seek to the start of the stream
554 FreeImage_SeekMemory(hmem, offset, SEEK_SET);
555
556 // check the file signature and deduce its format
557 // (the second argument is currently not used by FreeImage)
558 FREE_IMAGE_FORMAT fif = FreeImage_GetFileTypeFromMemory(hmem, 0);
559 if(fif != FIF_UNKNOWN) {
560 dib = FreeImage_LoadFromMemory(fif, hmem, flags);
561 }
562 }
563
564 return dib;
565 }
566
567 /**
568 Write a chunk in a PNG stream from the current position.
569 @param chunk_name Name of the chunk
570 @param chunk_data Chunk array
571 @param length Chunk length
572 @param hPngMemory PNG stream handle
573 */
574 static void
mng_WriteChunk(BYTE * chunk_name,BYTE * chunk_data,DWORD length,FIMEMORY * hPngMemory)575 mng_WriteChunk(BYTE *chunk_name, BYTE *chunk_data, DWORD length, FIMEMORY *hPngMemory) {
576 DWORD crc_file = 0;
577 // write a PNG chunk ...
578 // - length
579 mng_SwapLong(&length);
580 FreeImage_WriteMemory(&length, 1, 4, hPngMemory);
581 mng_SwapLong(&length);
582 // - chunk name
583 FreeImage_WriteMemory(chunk_name, 1, 4, hPngMemory);
584 if(chunk_data && length) {
585 // - chunk data
586 FreeImage_WriteMemory(chunk_data, 1, length, hPngMemory);
587 // - crc
588 crc_file = FreeImage_ZLibCRC32(0, chunk_name, 4);
589 crc_file = FreeImage_ZLibCRC32(crc_file, chunk_data, length);
590 mng_SwapLong(&crc_file);
591 FreeImage_WriteMemory(&crc_file, 1, 4, hPngMemory);
592 } else {
593 // - crc
594 crc_file = FreeImage_ZLibCRC32(0, chunk_name, 4);
595 mng_SwapLong(&crc_file);
596 FreeImage_WriteMemory(&crc_file, 1, 4, hPngMemory);
597 }
598
599 }
600
601 /**
602 Wrap a IDAT chunk as a PNG stream.
603 The stream has the structure { g_png_signature, IHDR, IDAT, IEND }
604 The image is assumed to be a greyscale image.
605
606 @param jng_width Image width
607 @param jng_height Image height
608 @param jng_alpha_sample_depth Bits per pixel
609 @param mChunk PNG grayscale IDAT format
610 @param mLength IDAT chunk length
611 @param hPngMemory Output memory stream
612 */
613 static void
mng_WritePNGStream(DWORD jng_width,DWORD jng_height,BYTE jng_alpha_sample_depth,BYTE * mChunk,DWORD mLength,FIMEMORY * hPngMemory)614 mng_WritePNGStream(DWORD jng_width, DWORD jng_height, BYTE jng_alpha_sample_depth, BYTE *mChunk, DWORD mLength, FIMEMORY *hPngMemory) {
615 // PNG grayscale IDAT format
616
617 BYTE data[14];
618
619 // wrap the IDAT chunk as a PNG stream
620
621 // write PNG file signature
622 FreeImage_WriteMemory(g_png_signature, 1, 8, hPngMemory);
623
624 // write a IHDR chunk ...
625 /*
626 The IHDR chunk must appear FIRST. It contains:
627 Width: 4 bytes
628 Height: 4 bytes
629 Bit depth: 1 byte
630 Color type: 1 byte
631 Compression method: 1 byte
632 Filter method: 1 byte
633 Interlace method: 1 byte
634 */
635 // - chunk data
636 mng_SwapLong(&jng_width);
637 mng_SwapLong(&jng_height);
638 memcpy(&data[0], &jng_width, 4);
639 memcpy(&data[4], &jng_height, 4);
640 mng_SwapLong(&jng_width);
641 mng_SwapLong(&jng_height);
642 data[8] = jng_alpha_sample_depth;
643 data[9] = 0; // color_type gray (jng_color_type)
644 data[10] = 0; // compression method 0 (jng_alpha_compression_method)
645 data[11] = 0; // filter_method 0 (jng_alpha_filter_method)
646 data[12] = 0; // interlace_method 0 (jng_alpha_interlace_method)
647
648 mng_WriteChunk(mng_IHDR, &data[0], 13, hPngMemory);
649
650 // write a IDAT chunk ...
651 mng_WriteChunk(mng_IDAT, mChunk, mLength, hPngMemory);
652
653 // write a IEND chunk ...
654 mng_WriteChunk(mng_IEND, NULL, 0, hPngMemory);
655
656 }
657
658 // --------------------------------------------------------------------------
659
660 /**
661 Build and set a FITAG whose type is FIDT_ASCII.
662 The tag must be destroyed by the caller using FreeImage_DeleteTag.
663 @param model Metadata model to be filled
664 @param dib Image to be filled
665 @param key Tag key
666 @param value Tag value
667 @return Returns TRUE if successful, returns FALSE otherwise
668 */
669 static BOOL
mng_SetKeyValue(FREE_IMAGE_MDMODEL model,FIBITMAP * dib,const char * key,const char * value)670 mng_SetKeyValue(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, const char *value) {
671 if(!dib || !key || !value) {
672 return FALSE;
673 }
674 // create a tag
675 FITAG *tag = FreeImage_CreateTag();
676 if(tag) {
677 BOOL bSuccess = TRUE;
678 // fill the tag
679 DWORD tag_length = (DWORD)(strlen(value) + 1);
680 bSuccess &= FreeImage_SetTagKey(tag, key);
681 bSuccess &= FreeImage_SetTagLength(tag, tag_length);
682 bSuccess &= FreeImage_SetTagCount(tag, tag_length);
683 bSuccess &= FreeImage_SetTagType(tag, FIDT_ASCII);
684 bSuccess &= FreeImage_SetTagValue(tag, value);
685 if(bSuccess) {
686 // set the tag
687 FreeImage_SetMetadata(model, dib, FreeImage_GetTagKey(tag), tag);
688 }
689 FreeImage_DeleteTag(tag);
690 return bSuccess;
691 }
692
693 return FALSE;
694 }
695
696 /**
697 Read a tEXt chunk and extract the key/value pair.
698 @param key_value_pair [returned value] Array of key/value pairs
699 @param mChunk Chunk data
700 @param mLength Chunk length
701 @return Returns TRUE if successful, returns FALSE otherwise
702 */
703 static BOOL
mng_SetMetadata_tEXt(tEXtMAP & key_value_pair,const BYTE * mChunk,DWORD mLength)704 mng_SetMetadata_tEXt(tEXtMAP &key_value_pair, const BYTE *mChunk, DWORD mLength) {
705 std::string key;
706 std::string value;
707 BYTE *buffer = (BYTE*)malloc(mLength * sizeof(BYTE));
708 if(!buffer) {
709 return FALSE;
710 }
711 DWORD pos = 0;
712
713 memset(buffer, 0, mLength * sizeof(BYTE));
714
715 for(DWORD i = 0; i < mLength; i++) {
716 buffer[pos++] = mChunk[i];
717 if(mChunk[i] == '\0') {
718 if(key.size() == 0) {
719 key = (char*)buffer;
720 pos = 0;
721 memset(buffer, 0, mLength * sizeof(BYTE));
722 } else {
723 break;
724 }
725 }
726 }
727 value = (char*)buffer;
728 free(buffer);
729
730 key_value_pair[key] = value;
731
732 return TRUE;
733 }
734
735 // --------------------------------------------------------------------------
736
737 /**
738 Load a FIBITMAP from a MNG or a JNG stream
739 @param format_id ID of the caller
740 @param io Stream i/o functions
741 @param handle Stream handle
742 @param Offset Start of the first chunk
743 @param flags Loading flags
744 @return Returns a dib if successful, returns NULL otherwise
745 */
746 FIBITMAP*
mng_ReadChunks(int format_id,FreeImageIO * io,fi_handle handle,long Offset,int flags=0)747 mng_ReadChunks(int format_id, FreeImageIO *io, fi_handle handle, long Offset, int flags = 0) {
748 DWORD mLength = 0;
749 BYTE mChunkName[5];
750 BYTE *mChunk = NULL;
751 DWORD crc_file;
752 long LastOffset;
753 long mOrigPos;
754 BYTE *PLTE_file_chunk = NULL; // whole PLTE chunk (lentgh, name, array, crc)
755 DWORD PLTE_file_size = 0; // size of PLTE chunk
756
757 BOOL m_HasGlobalPalette = FALSE; // may turn to TRUE in PLTE chunk
758 unsigned m_TotalBytesOfChunks = 0;
759 FIBITMAP *dib = NULL;
760 FIBITMAP *dib_alpha = NULL;
761
762 FIMEMORY *hJpegMemory = NULL;
763 FIMEMORY *hPngMemory = NULL;
764 FIMEMORY *hIDATMemory = NULL;
765
766 // ---
767 DWORD jng_width = 0;
768 DWORD jng_height = 0;
769 BYTE jng_color_type = 0;
770 BYTE jng_image_sample_depth = 0;
771 BYTE jng_image_compression_method = 0;
772
773 BYTE jng_alpha_sample_depth = 0;
774 BYTE jng_alpha_compression_method = 0;
775 BYTE jng_alpha_filter_method = 0;
776 BYTE jng_alpha_interlace_method = 0;
777
778 DWORD mng_frame_width = 0;
779 DWORD mng_frame_height = 0;
780 DWORD mng_ticks_per_second = 0;
781 DWORD mng_nominal_layer_count = 0;
782 DWORD mng_nominal_frame_count = 0;
783 DWORD mng_nominal_play_time = 0;
784 DWORD mng_simplicity_profile = 0;
785
786
787 DWORD res_x = 2835; // 72 dpi
788 DWORD res_y = 2835; // 72 dpi
789 RGBQUAD rgbBkColor = {0, 0, 0, 0};
790 WORD bk_red, bk_green, bk_blue;
791 BOOL hasBkColor = FALSE;
792 BOOL mHasIDAT = FALSE;
793
794 tEXtMAP key_value_pair;
795
796 // ---
797
798 BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
799
800 // get the file size
801 const long mLOF = mng_LOF(io, handle);
802 // go to the first chunk
803 io->seek_proc(handle, Offset, SEEK_SET);
804
805 try {
806 BOOL mEnd = FALSE;
807
808 while(mEnd == FALSE) {
809 // start of the chunk
810 LastOffset = io->tell_proc(handle);
811 // read length
812 mLength = 0;
813 io->read_proc(&mLength, 1, sizeof(mLength), handle);
814 mng_SwapLong(&mLength);
815 // read name
816 io->read_proc(&mChunkName[0], 1, 4, handle);
817 mChunkName[4] = '\0';
818
819 if(mLength > 0) {
820 mChunk = (BYTE*)realloc(mChunk, mLength);
821 if(!mChunk) {
822 FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: out of memory", mChunkName);
823 throw (const char*)NULL;
824 }
825 Offset = io->tell_proc(handle);
826 if(Offset + (long)mLength > mLOF) {
827 FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: unexpected end of file", mChunkName);
828 throw (const char*)NULL;
829 }
830 // read chunk
831 io->read_proc(mChunk, 1, mLength, handle);
832 }
833 // read crc
834 io->read_proc(&crc_file, 1, sizeof(crc_file), handle);
835 mng_SwapLong(&crc_file);
836 // check crc
837 DWORD crc_check = FreeImage_ZLibCRC32(0, &mChunkName[0], 4);
838 crc_check = FreeImage_ZLibCRC32(crc_check, mChunk, mLength);
839 if(crc_check != crc_file) {
840 FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: bad CRC", mChunkName);
841 throw (const char*)NULL;
842 }
843
844 switch( mng_GetChunckType(mChunkName) ) {
845 case MHDR:
846 // The MHDR chunk is always first in all MNG datastreams except for those
847 // that consist of a single PNG or JNG datastream with a PNG or JNG signature.
848 if(mLength == 28) {
849 memcpy(&mng_frame_width, &mChunk[0], 4);
850 memcpy(&mng_frame_height, &mChunk[4], 4);
851 memcpy(&mng_ticks_per_second, &mChunk[8], 4);
852 memcpy(&mng_nominal_layer_count, &mChunk[12], 4);
853 memcpy(&mng_nominal_frame_count, &mChunk[16], 4);
854 memcpy(&mng_nominal_play_time, &mChunk[20], 4);
855 memcpy(&mng_simplicity_profile, &mChunk[24], 4);
856
857 mng_SwapLong(&mng_frame_width);
858 mng_SwapLong(&mng_frame_height);
859 mng_SwapLong(&mng_ticks_per_second);
860 mng_SwapLong(&mng_nominal_layer_count);
861 mng_SwapLong(&mng_nominal_frame_count);
862 mng_SwapLong(&mng_nominal_play_time);
863 mng_SwapLong(&mng_simplicity_profile);
864
865 } else {
866 FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: size is %d instead of 28", mChunkName, mLength);
867 }
868 break;
869
870 case MEND:
871 mEnd = TRUE;
872 break;
873
874 case LOOP:
875 case ENDL:
876 break;
877 case DEFI:
878 break;
879 case SAVE:
880 case SEEK:
881 case TERM:
882 break;
883 case BACK:
884 break;
885
886 // Global "PLTE" and "tRNS" (if any). PNG "PLTE" will be of 0 byte, as it uses global data.
887 case PLTE: // Global
888 m_HasGlobalPalette = TRUE;
889 PLTE_file_size = mLength + 12; // (lentgh, name, array, crc) = (4, 4, mLength, 4)
890 PLTE_file_chunk = (BYTE*)realloc(PLTE_file_chunk, PLTE_file_size);
891 if(!PLTE_file_chunk) {
892 FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: out of memory", mChunkName);
893 throw (const char*)NULL;
894 } else {
895 mOrigPos = io->tell_proc(handle);
896 // seek to the start of the chunk
897 io->seek_proc(handle, LastOffset, SEEK_SET);
898 // load the whole chunk
899 io->read_proc(PLTE_file_chunk, 1, PLTE_file_size, handle);
900 // go to the start of the next chunk
901 io->seek_proc(handle, mOrigPos, SEEK_SET);
902 }
903 break;
904
905 case tRNS: // Global
906 break;
907
908 case IHDR:
909 Offset = LastOffset;
910 // parse the PNG file and get its file size
911 if(mng_CountPNGChunks(io, handle, Offset, &m_TotalBytesOfChunks) == FALSE) {
912 // reach an unexpected end of file
913 mEnd = TRUE;
914 FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: unexpected end of PNG file", mChunkName);
915 break;
916 }
917
918 // wrap the { IHDR, ..., IEND } chunks as a PNG stream
919 if(hPngMemory == NULL) {
920 hPngMemory = FreeImage_OpenMemory();
921 }
922
923 mOrigPos = io->tell_proc(handle);
924
925 // write PNG file signature
926 FreeImage_SeekMemory(hPngMemory, 0, SEEK_SET);
927 FreeImage_WriteMemory(g_png_signature, 1, 8, hPngMemory);
928
929 mChunk = (BYTE*)realloc(mChunk, m_TotalBytesOfChunks);
930 if(!mChunk) {
931 FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: out of memory", mChunkName);
932 throw (const char*)NULL;
933 }
934
935 // on calling CountPNGChunks earlier, we were in Offset pos,
936 // go back there
937 io->seek_proc(handle, Offset, SEEK_SET);
938 io->read_proc(mChunk, 1, m_TotalBytesOfChunks, handle);
939 // Put back to original pos
940 io->seek_proc(handle, mOrigPos, SEEK_SET);
941 // write the PNG chunks
942 FreeImage_WriteMemory(mChunk, 1, m_TotalBytesOfChunks, hPngMemory);
943
944 // plug in global PLTE if local PLTE exists
945 if(m_HasGlobalPalette) {
946 // ensure we remove some local chunks, so that global
947 // "PLTE" can be inserted right before "IDAT".
948 mng_RemoveChunk(hPngMemory, mng_PLTE);
949 mng_RemoveChunk(hPngMemory, mng_tRNS);
950 mng_RemoveChunk(hPngMemory, mng_bKGD);
951 // insert global "PLTE" chunk in its entirety before "IDAT"
952 mng_InsertChunk(hPngMemory, mng_IDAT, PLTE_file_chunk, PLTE_file_size);
953 }
954
955 if(dib) FreeImage_Unload(dib);
956 dib = mng_LoadFromMemoryHandle(hPngMemory, flags);
957
958 // stop after the first image
959 mEnd = TRUE;
960 break;
961
962 case JHDR:
963 if(mLength == 16) {
964 memcpy(&jng_width, &mChunk[0], 4);
965 memcpy(&jng_height, &mChunk[4], 4);
966 mng_SwapLong(&jng_width);
967 mng_SwapLong(&jng_height);
968
969 jng_color_type = mChunk[8];
970 jng_image_sample_depth = mChunk[9];
971 jng_image_compression_method = mChunk[10];
972 //BYTE jng_image_interlace_method = mChunk[11]; // for debug only
973
974 jng_alpha_sample_depth = mChunk[12];
975 jng_alpha_compression_method = mChunk[13];
976 jng_alpha_filter_method = mChunk[14];
977 jng_alpha_interlace_method = mChunk[15];
978 } else {
979 FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: invalid chunk length", mChunkName);
980 throw (const char*)NULL;
981 }
982 break;
983
984 case JDAT:
985 if(hJpegMemory == NULL) {
986 hJpegMemory = FreeImage_OpenMemory();
987 }
988 // as there may be several JDAT chunks, concatenate them
989 FreeImage_WriteMemory(mChunk, 1, mLength, hJpegMemory);
990 break;
991
992 case IDAT:
993 if(!header_only && (jng_alpha_compression_method == 0)) {
994 // PNG grayscale IDAT format
995 if(hIDATMemory == NULL) {
996 hIDATMemory = FreeImage_OpenMemory();
997 mHasIDAT = TRUE;
998 }
999 // as there may be several IDAT chunks, concatenate them
1000 FreeImage_WriteMemory(mChunk, 1, mLength, hIDATMemory);
1001 }
1002 break;
1003
1004 case IEND:
1005 if(!hJpegMemory) {
1006 mEnd = TRUE;
1007 break;
1008 }
1009 // load the JPEG
1010 if(dib) {
1011 FreeImage_Unload(dib);
1012 }
1013 dib = mng_LoadFromMemoryHandle(hJpegMemory, flags);
1014
1015 // load the PNG alpha layer
1016 if(mHasIDAT) {
1017 BYTE *data = NULL;
1018 DWORD size_in_bytes = 0;
1019
1020 // get a pointer to the IDAT buffer
1021 FreeImage_AcquireMemory(hIDATMemory, &data, &size_in_bytes);
1022 if(data && size_in_bytes) {
1023 // wrap the IDAT chunk as a PNG stream
1024 if(hPngMemory == NULL) {
1025 hPngMemory = FreeImage_OpenMemory();
1026 }
1027 mng_WritePNGStream(jng_width, jng_height, jng_alpha_sample_depth, data, size_in_bytes, hPngMemory);
1028 // load the PNG
1029 if(dib_alpha) {
1030 FreeImage_Unload(dib_alpha);
1031 }
1032 dib_alpha = mng_LoadFromMemoryHandle(hPngMemory, flags);
1033 }
1034 }
1035 // stop the parsing
1036 mEnd = TRUE;
1037 break;
1038
1039 case JDAA:
1040 break;
1041
1042 case gAMA:
1043 break;
1044
1045 case pHYs:
1046 // unit is pixels per meter
1047 memcpy(&res_x, &mChunk[0], 4);
1048 mng_SwapLong(&res_x);
1049 memcpy(&res_y, &mChunk[4], 4);
1050 mng_SwapLong(&res_y);
1051 break;
1052
1053 case bKGD:
1054 memcpy(&bk_red, &mChunk[0], 2);
1055 mng_SwapShort(&bk_red);
1056 rgbBkColor.rgbRed = (BYTE)bk_red;
1057 memcpy(&bk_green, &mChunk[2], 2);
1058 mng_SwapShort(&bk_green);
1059 rgbBkColor.rgbGreen = (BYTE)bk_green;
1060 memcpy(&bk_blue, &mChunk[4], 2);
1061 mng_SwapShort(&bk_blue);
1062 rgbBkColor.rgbBlue = (BYTE)bk_blue;
1063 hasBkColor = TRUE;
1064 break;
1065
1066 case tEXt:
1067 mng_SetMetadata_tEXt(key_value_pair, mChunk, mLength);
1068 break;
1069
1070 case UNKNOWN_CHUNCK:
1071 default:
1072 break;
1073
1074
1075 } // switch( GetChunckType )
1076 } // while(!mEnd)
1077
1078 FreeImage_CloseMemory(hJpegMemory);
1079 FreeImage_CloseMemory(hPngMemory);
1080 FreeImage_CloseMemory(hIDATMemory);
1081 free(mChunk);
1082 free(PLTE_file_chunk);
1083
1084 // convert to 32-bit if a transparent layer is available
1085 if(!header_only && dib_alpha) {
1086 FIBITMAP *dst = FreeImage_ConvertTo32Bits(dib);
1087 if((FreeImage_GetBPP(dib_alpha) == 8) && (FreeImage_GetImageType(dib_alpha) == FIT_BITMAP)) {
1088 FreeImage_SetChannel(dst, dib_alpha, FICC_ALPHA);
1089 } else {
1090 FIBITMAP *dst_alpha = FreeImage_ConvertTo8Bits(dib_alpha);
1091 FreeImage_SetChannel(dst, dst_alpha, FICC_ALPHA);
1092 FreeImage_Unload(dst_alpha);
1093 }
1094 FreeImage_Unload(dib);
1095 dib = dst;
1096 }
1097 FreeImage_Unload(dib_alpha);
1098
1099 if(dib) {
1100 // set metadata
1101 FreeImage_SetDotsPerMeterX(dib, res_x);
1102 FreeImage_SetDotsPerMeterY(dib, res_y);
1103 if(hasBkColor) {
1104 FreeImage_SetBackgroundColor(dib, &rgbBkColor);
1105 }
1106 if(key_value_pair.size()) {
1107 for(tEXtMAP::iterator j = key_value_pair.begin(); j != key_value_pair.end(); j++) {
1108 std::string key = (*j).first;
1109 std::string value = (*j).second;
1110 mng_SetKeyValue(FIMD_COMMENTS, dib, key.c_str(), value.c_str());
1111 }
1112 }
1113 }
1114
1115 return dib;
1116
1117 } catch(const char *text) {
1118 FreeImage_CloseMemory(hJpegMemory);
1119 FreeImage_CloseMemory(hPngMemory);
1120 FreeImage_CloseMemory(hIDATMemory);
1121 free(mChunk);
1122 free(PLTE_file_chunk);
1123 FreeImage_Unload(dib);
1124 FreeImage_Unload(dib_alpha);
1125 if(text) {
1126 FreeImage_OutputMessageProc(format_id, text);
1127 }
1128 return NULL;
1129 }
1130 }
1131
1132 // --------------------------------------------------------------------------
1133
1134 /**
1135 Write a FIBITMAP to a JNG stream
1136 @param format_id ID of the caller
1137 @param io Stream i/o functions
1138 @param dib Image to be saved
1139 @param handle Stream handle
1140 @param flags Saving flags
1141 @return Returns TRUE if successful, returns FALSE otherwise
1142 */
1143 BOOL
mng_WriteJNG(int format_id,FreeImageIO * io,FIBITMAP * dib,fi_handle handle,int flags)1144 mng_WriteJNG(int format_id, FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int flags) {
1145 DWORD jng_width = 0;
1146 DWORD jng_height = 0;
1147 BYTE jng_color_type = 0;
1148 BYTE jng_image_sample_depth = 8;
1149 BYTE jng_image_compression_method = 8; // 8: ISO-10918-1 Huffman-coded baseline JPEG.
1150 BYTE jng_image_interlace_method = 0;
1151
1152 BYTE jng_alpha_sample_depth = 0;
1153 BYTE jng_alpha_compression_method = 0;
1154 BYTE jng_alpha_filter_method = 0;
1155 BYTE jng_alpha_interlace_method = 0;
1156
1157 BYTE buffer[16];
1158
1159 FIMEMORY *hJngMemory = NULL;
1160 FIMEMORY *hJpegMemory = NULL;
1161 FIMEMORY *hPngMemory = NULL;
1162
1163 FIBITMAP *dib_rgb = NULL;
1164 FIBITMAP *dib_alpha = NULL;
1165
1166 if(!dib || (FreeImage_GetImageType(dib) != FIT_BITMAP)) {
1167 return FALSE;
1168 }
1169
1170 unsigned bpp = FreeImage_GetBPP(dib);
1171
1172 switch(bpp) {
1173 case 8:
1174 if(FreeImage_GetColorType(dib) == FIC_MINISBLACK) {
1175 dib_rgb = dib;
1176 jng_color_type = MNG_COLORTYPE_JPEGGRAY;
1177 } else {
1178 // JPEG plugin will convert other types (FIC_MINISWHITE, FIC_PALETTE) to 24-bit on the fly
1179 //dib_rgb = FreeImage_ConvertTo24Bits(dib);
1180 dib_rgb = dib;
1181 jng_color_type = MNG_COLORTYPE_JPEGCOLOR;
1182
1183 }
1184 break;
1185 case 24:
1186 dib_rgb = dib;
1187 jng_color_type = MNG_COLORTYPE_JPEGCOLOR;
1188 break;
1189 case 32:
1190 dib_rgb = FreeImage_ConvertTo24Bits(dib);
1191 jng_color_type = MNG_COLORTYPE_JPEGCOLORA;
1192 jng_alpha_sample_depth = 8;
1193 break;
1194 default:
1195 return FALSE;
1196 }
1197
1198 jng_width = (DWORD)FreeImage_GetWidth(dib);
1199 jng_height = (DWORD)FreeImage_GetHeight(dib);
1200
1201 try {
1202 hJngMemory = FreeImage_OpenMemory();
1203
1204 // --- write JNG file signature ---
1205 FreeImage_WriteMemory(g_jng_signature, 1, 8, hJngMemory);
1206
1207 // --- write a JHDR chunk ---
1208 SwapLong(&jng_width);
1209 SwapLong(&jng_height);
1210 memcpy(&buffer[0], &jng_width, 4);
1211 memcpy(&buffer[4], &jng_height, 4);
1212 SwapLong(&jng_width);
1213 SwapLong(&jng_height);
1214 buffer[8] = jng_color_type;
1215 buffer[9] = jng_image_sample_depth;
1216 buffer[10] = jng_image_compression_method;
1217 buffer[11] = jng_image_interlace_method;
1218 buffer[12] = jng_alpha_sample_depth;
1219 buffer[13] = jng_alpha_compression_method;
1220 buffer[14] = jng_alpha_filter_method;
1221 buffer[15] = jng_alpha_interlace_method;
1222 mng_WriteChunk(mng_JHDR, &buffer[0], 16, hJngMemory);
1223
1224 // --- write a sequence of JDAT chunks ---
1225 hJpegMemory = FreeImage_OpenMemory();
1226 flags |= JPEG_BASELINE;
1227 if(!FreeImage_SaveToMemory(FIF_JPEG, dib_rgb, hJpegMemory, flags)) {
1228 throw (const char*)NULL;
1229 }
1230 if(dib_rgb != dib) {
1231 FreeImage_Unload(dib_rgb);
1232 dib_rgb = NULL;
1233 }
1234 {
1235 BYTE *jpeg_data = NULL;
1236 DWORD size_in_bytes = 0;
1237
1238 // get a pointer to the stream buffer
1239 FreeImage_AcquireMemory(hJpegMemory, &jpeg_data, &size_in_bytes);
1240 // write chunks
1241 for(DWORD k = 0; k < size_in_bytes;) {
1242 DWORD bytes_left = size_in_bytes - k;
1243 DWORD chunk_size = MIN(JPEG_CHUNK_SIZE, bytes_left);
1244 mng_WriteChunk(mng_JDAT, &jpeg_data[k], chunk_size, hJngMemory);
1245 k += chunk_size;
1246 }
1247 }
1248 FreeImage_CloseMemory(hJpegMemory);
1249 hJpegMemory = NULL;
1250
1251 // --- write alpha layer as a sequence of IDAT chunk ---
1252 if((bpp == 32) && (jng_color_type == MNG_COLORTYPE_JPEGCOLORA)) {
1253 dib_alpha = FreeImage_GetChannel(dib, FICC_ALPHA);
1254
1255 hPngMemory = FreeImage_OpenMemory();
1256 if(!FreeImage_SaveToMemory(FIF_PNG, dib_alpha, hPngMemory, PNG_DEFAULT)) {
1257 throw (const char*)NULL;
1258 }
1259 FreeImage_Unload(dib_alpha);
1260 dib_alpha = NULL;
1261 // get the IDAT chunk
1262 {
1263 BOOL bResult = FALSE;
1264 DWORD start_pos = 0;
1265 DWORD next_pos = 0;
1266 long offset = 8;
1267
1268 do {
1269 // find the next IDAT chunk from 'offset' position
1270 bResult = mng_FindChunk(hPngMemory, mng_IDAT, offset, &start_pos, &next_pos);
1271 if(!bResult) break;
1272
1273 BYTE *png_data = NULL;
1274 DWORD size_in_bytes = 0;
1275
1276 // get a pointer to the stream buffer
1277 FreeImage_AcquireMemory(hPngMemory, &png_data, &size_in_bytes);
1278 // write the IDAT chunk
1279 mng_WriteChunk(mng_IDAT, &png_data[start_pos+8], next_pos - start_pos - 12, hJngMemory);
1280
1281 offset = next_pos;
1282
1283 } while(bResult);
1284 }
1285
1286 FreeImage_CloseMemory(hPngMemory);
1287 hPngMemory = NULL;
1288 }
1289
1290 // --- write a IEND chunk ---
1291 mng_WriteChunk(mng_IEND, NULL, 0, hJngMemory);
1292
1293 // write the JNG on output stream
1294 {
1295 BYTE *jng_data = NULL;
1296 DWORD size_in_bytes = 0;
1297 FreeImage_AcquireMemory(hJngMemory, &jng_data, &size_in_bytes);
1298 io->write_proc(jng_data, 1, size_in_bytes, handle);
1299 }
1300
1301 FreeImage_CloseMemory(hJngMemory);
1302 FreeImage_CloseMemory(hJpegMemory);
1303 FreeImage_CloseMemory(hPngMemory);
1304
1305 return TRUE;
1306
1307 } catch(const char *text) {
1308 FreeImage_CloseMemory(hJngMemory);
1309 FreeImage_CloseMemory(hJpegMemory);
1310 FreeImage_CloseMemory(hPngMemory);
1311 if(dib_rgb && (dib_rgb != dib)) {
1312 FreeImage_Unload(dib_rgb);
1313 }
1314 FreeImage_Unload(dib_alpha);
1315 if(text) {
1316 FreeImage_OutputMessageProc(format_id, text);
1317 }
1318 return FALSE;
1319 }
1320 }
1321