1 // ==========================================================
2 // JPEG XR Loader & Writer
3 //
4 // Design and implementation by
5 // - Herve 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 #include "../Metadata/FreeImageTag.h"
25
26 #include "../LibJXR/jxrgluelib/JXRGlue.h"
27
28 // ==========================================================
29 // Plugin Interface
30 // ==========================================================
31
32 static int s_format_id;
33
34 // ==========================================================
35 // FreeImageIO interface (I/O streaming functions)
36 // ==========================================================
37
38 /**
39 JXR wrapper for FreeImage I/O handle
40 */
41 typedef struct tagFreeImageJXRIO {
42 FreeImageIO *io;
43 fi_handle handle;
44 } FreeImageJXRIO;
45
46 static ERR
_jxr_io_Read(WMPStream * pWS,void * pv,size_t cb)47 _jxr_io_Read(WMPStream* pWS, void* pv, size_t cb) {
48 FreeImageJXRIO *fio = (FreeImageJXRIO*)pWS->state.pvObj;
49 return (fio->io->read_proc(pv, (unsigned)cb, 1, fio->handle) == 1) ? WMP_errSuccess : WMP_errFileIO;
50 }
51
52 static ERR
_jxr_io_Write(WMPStream * pWS,const void * pv,size_t cb)53 _jxr_io_Write(WMPStream* pWS, const void* pv, size_t cb) {
54 FreeImageJXRIO *fio = (FreeImageJXRIO*)pWS->state.pvObj;
55 if(0 != cb) {
56 return (fio->io->write_proc((void*)pv, (unsigned)cb, 1, fio->handle) == 1) ? WMP_errSuccess : WMP_errFileIO;
57 }
58 return WMP_errFileIO;
59 }
60
61 static ERR
_jxr_io_SetPos(WMPStream * pWS,size_t offPos)62 _jxr_io_SetPos(WMPStream* pWS, size_t offPos) {
63 FreeImageJXRIO *fio = (FreeImageJXRIO*)pWS->state.pvObj;
64 return (fio->io->seek_proc(fio->handle, (long)offPos, SEEK_SET) == 0) ? WMP_errSuccess : WMP_errFileIO;
65 }
66
67 static ERR
_jxr_io_GetPos(WMPStream * pWS,size_t * poffPos)68 _jxr_io_GetPos(WMPStream* pWS, size_t* poffPos) {
69 FreeImageJXRIO *fio = (FreeImageJXRIO*)pWS->state.pvObj;
70 long lOff = fio->io->tell_proc(fio->handle);
71 if(lOff == -1) {
72 return WMP_errFileIO;
73 }
74 *poffPos = (size_t)lOff;
75 return WMP_errSuccess;
76 }
77
78 static Bool
_jxr_io_EOS(WMPStream * pWS)79 _jxr_io_EOS(WMPStream* pWS) {
80 FreeImageJXRIO *fio = (FreeImageJXRIO*)pWS->state.pvObj;
81 long currentPos = fio->io->tell_proc(fio->handle);
82 fio->io->seek_proc(fio->handle, 0, SEEK_END);
83 long fileRemaining = fio->io->tell_proc(fio->handle) - currentPos;
84 fio->io->seek_proc(fio->handle, currentPos, SEEK_SET);
85 return (fileRemaining > 0);
86 }
87
88 static ERR
_jxr_io_Close(WMPStream ** ppWS)89 _jxr_io_Close(WMPStream** ppWS) {
90 WMPStream *pWS = *ppWS;
91 // HACK : we use fMem to avoid a stream destruction by the library
92 // because FreeImage MUST HAVE the ownership of the stream
93 // see _jxr_io_Create
94 if(pWS && pWS->fMem) {
95 free(pWS);
96 *ppWS = NULL;
97 }
98 return WMP_errSuccess;
99 }
100
101 static ERR
_jxr_io_Create(WMPStream ** ppWS,FreeImageJXRIO * jxr_io)102 _jxr_io_Create(WMPStream **ppWS, FreeImageJXRIO *jxr_io) {
103 *ppWS = (WMPStream*)calloc(1, sizeof(**ppWS));
104 if(*ppWS) {
105 WMPStream *pWS = *ppWS;
106
107 pWS->state.pvObj = jxr_io;
108 pWS->Close = _jxr_io_Close;
109 pWS->EOS = _jxr_io_EOS;
110 pWS->Read = _jxr_io_Read;
111 pWS->Write = _jxr_io_Write;
112 pWS->SetPos = _jxr_io_SetPos;
113 pWS->GetPos = _jxr_io_GetPos;
114
115 // HACK : we use fMem to avoid a stream destruction by the library
116 // because FreeImage MUST HAVE the ownership of the stream
117 // see _jxr_io_Close
118 pWS->fMem = FALSE;
119
120 return WMP_errSuccess;
121 }
122 return WMP_errOutOfMemory;
123 }
124
125 // ==========================================================
126 // JPEG XR Error handling
127 // ==========================================================
128
129 static const char*
JXR_ErrorMessage(const int error)130 JXR_ErrorMessage(const int error) {
131 switch(error) {
132 case WMP_errNotYetImplemented:
133 case WMP_errAbstractMethod:
134 return "Not yet implemented";
135 case WMP_errOutOfMemory:
136 return "Out of memory";
137 case WMP_errFileIO:
138 return "File I/O error";
139 case WMP_errBufferOverflow:
140 return "Buffer overflow";
141 case WMP_errInvalidParameter:
142 return "Invalid parameter";
143 case WMP_errInvalidArgument:
144 return "Invalid argument";
145 case WMP_errUnsupportedFormat:
146 return "Unsupported format";
147 case WMP_errIncorrectCodecVersion:
148 return "Incorrect codec version";
149 case WMP_errIndexNotFound:
150 return "Format converter: Index not found";
151 case WMP_errOutOfSequence:
152 return "Metadata: Out of sequence";
153 case WMP_errMustBeMultipleOf16LinesUntilLastCall:
154 return "Must be multiple of 16 lines until last call";
155 case WMP_errPlanarAlphaBandedEncRequiresTempFile:
156 return "Planar alpha banded encoder requires temp files";
157 case WMP_errAlphaModeCannotBeTranscoded:
158 return "Alpha mode cannot be transcoded";
159 case WMP_errIncorrectCodecSubVersion:
160 return "Incorrect codec subversion";
161 case WMP_errFail:
162 case WMP_errNotInitialized:
163 default:
164 return "Invalid instruction - please contact the FreeImage team";
165 }
166 }
167
168 // ==========================================================
169 // Helper functions & macro
170 // ==========================================================
171
172 #define JXR_CHECK(error_code) \
173 if(error_code < 0) { \
174 const char *error_message = JXR_ErrorMessage(error_code); \
175 throw error_message; \
176 }
177
178 // --------------------------------------------------------------------------
179
180 /**
181 Input conversions natively understood by FreeImage
182 @see GetNativePixelFormat
183 */
184 typedef struct tagJXRInputConversion {
185 BITDEPTH_BITS bdBitDepth;
186 U32 cbitUnit;
187 FREE_IMAGE_TYPE image_type;
188 unsigned red_mask;
189 unsigned green_mask;
190 unsigned blue_mask;
191 } JXRInputConversion;
192
193 /**
194 Conversion table for native FreeImage formats
195 @see GetNativePixelFormat
196 */
197 static JXRInputConversion s_FreeImagePixelInfo[] = {
198 // 1-bit bitmap
199 { BD_1, 1, FIT_BITMAP, 0, 0, 0 },
200 // 8-, 24-, 32-bit bitmap
201 { BD_8, 8, FIT_BITMAP, 0, 0, 0 },
202 { BD_8, 24, FIT_BITMAP, 0, 0, 0 },
203 { BD_8, 32, FIT_BITMAP, 0, 0, 0 },
204 // 16-bit RGB 565
205 { BD_565, 16, FIT_BITMAP, FI16_565_RED_MASK, FI16_565_GREEN_MASK, FI16_565_BLUE_MASK },
206 // 16-bit RGB 555
207 { BD_5, 16, FIT_BITMAP, FI16_555_RED_MASK, FI16_555_GREEN_MASK, FI16_555_BLUE_MASK },
208 // 16-bit greyscale, RGB16, RGBA16 bitmap
209 { BD_16, 16, FIT_UINT16, 0, 0, 0 },
210 { BD_16, 48, FIT_RGB16, 0, 0, 0 },
211 { BD_16, 64, FIT_RGBA16, 0, 0, 0 },
212 // 32-bit float, RGBF, RGBAF bitmap
213 { BD_32F, 32, FIT_FLOAT, 0, 0, 0 },
214 { BD_32F, 96, FIT_RGBF, 0, 0, 0 },
215 { BD_32F, 128, FIT_RGBAF, 0, 0, 0 }
216 };
217
218 /**
219 Scan input pixelInfo specifications and return the equivalent FreeImage info for loading
220 @param pixelInfo Image specifications
221 @param out_guid_format (returned value) output pixel format
222 @param image_type (returned value) Image type
223 @param bpp (returned value) Image bit depth
224 @param red_mask (returned value) RGB mask
225 @param green_mask (returned value) RGB mask
226 @param blue_mask (returned value) RGB mask
227 @return Returns WMP_errSuccess if successful, returns WMP_errFail otherwise
228 @see GetInputPixelFormat
229 */
230 static ERR
GetNativePixelFormat(const PKPixelInfo * pixelInfo,PKPixelFormatGUID * out_guid_format,FREE_IMAGE_TYPE * image_type,unsigned * bpp,unsigned * red_mask,unsigned * green_mask,unsigned * blue_mask)231 GetNativePixelFormat(const PKPixelInfo *pixelInfo, PKPixelFormatGUID *out_guid_format, FREE_IMAGE_TYPE *image_type, unsigned *bpp, unsigned *red_mask, unsigned *green_mask, unsigned *blue_mask) {
232 const unsigned s_FreeImagePixelInfoSize = (unsigned)sizeof(s_FreeImagePixelInfo) / sizeof(*(s_FreeImagePixelInfo));
233
234 for(unsigned i = 0; i < s_FreeImagePixelInfoSize; i++) {
235 if(pixelInfo->bdBitDepth == s_FreeImagePixelInfo[i].bdBitDepth) {
236 if(pixelInfo->cbitUnit == s_FreeImagePixelInfo[i].cbitUnit) {
237 // found ! now get dst image format specifications
238 memcpy(out_guid_format, pixelInfo->pGUIDPixFmt, sizeof(PKPixelFormatGUID));
239 *image_type = s_FreeImagePixelInfo[i].image_type;
240 *bpp = s_FreeImagePixelInfo[i].cbitUnit;
241 *red_mask = s_FreeImagePixelInfo[i].red_mask;
242 *green_mask = s_FreeImagePixelInfo[i].green_mask;
243 *blue_mask = s_FreeImagePixelInfo[i].blue_mask;
244 return WMP_errSuccess;
245 }
246 }
247 }
248
249 // not found : need pixel format conversion
250 return WMP_errFail;
251 }
252
253 /**
254 Scan input file guid format and return the equivalent FreeImage info & target guid format for loading
255 @param pDecoder Decoder handle
256 @param guid_format (returned value) Output pixel format
257 @param image_type (returned value) Image type
258 @param bpp (returned value) Image bit depth
259 @param red_mask (returned value) RGB mask
260 @param green_mask (returned value) RGB mask
261 @param blue_mask (returned value) RGB mask
262 @return Returns TRUE if successful, returns FALSE otherwise
263 */
264 static ERR
GetInputPixelFormat(PKImageDecode * pDecoder,PKPixelFormatGUID * guid_format,FREE_IMAGE_TYPE * image_type,unsigned * bpp,unsigned * red_mask,unsigned * green_mask,unsigned * blue_mask)265 GetInputPixelFormat(PKImageDecode *pDecoder, PKPixelFormatGUID *guid_format, FREE_IMAGE_TYPE *image_type, unsigned *bpp, unsigned *red_mask, unsigned *green_mask, unsigned *blue_mask) {
266 ERR error_code = 0; // error code as returned by the interface
267 PKPixelInfo pixelInfo; // image specifications
268
269 try {
270 // get input file pixel format ...
271 PKPixelFormatGUID pguidSourcePF;
272 error_code = pDecoder->GetPixelFormat(pDecoder, &pguidSourcePF);
273 JXR_CHECK(error_code);
274 pixelInfo.pGUIDPixFmt = &pguidSourcePF;
275 // ... check for a supported format and get the format specifications
276 error_code = PixelFormatLookup(&pixelInfo, LOOKUP_FORWARD);
277 JXR_CHECK(error_code);
278
279 // search for an equivalent native FreeImage format
280 error_code = GetNativePixelFormat(&pixelInfo, guid_format, image_type, bpp, red_mask, green_mask, blue_mask);
281
282 if(error_code != WMP_errSuccess) {
283 // try to find a suitable conversion function ...
284 const PKPixelFormatGUID *ppguidTargetPF = NULL; // target pixel format
285 unsigned iIndex = 0; // first available conversion function
286 do {
287 error_code = PKFormatConverter_EnumConversions(&pguidSourcePF, iIndex, &ppguidTargetPF);
288 if(error_code == WMP_errSuccess) {
289 // found a conversion function, is the converted format a native FreeImage format ?
290 pixelInfo.pGUIDPixFmt = ppguidTargetPF;
291 error_code = PixelFormatLookup(&pixelInfo, LOOKUP_FORWARD);
292 JXR_CHECK(error_code);
293 error_code = GetNativePixelFormat(&pixelInfo, guid_format, image_type, bpp, red_mask, green_mask, blue_mask);
294 if(error_code == WMP_errSuccess) {
295 break;
296 }
297 }
298 // try next conversion function
299 iIndex++;
300 } while(error_code != WMP_errIndexNotFound);
301
302 }
303
304 return (error_code == WMP_errSuccess) ? WMP_errSuccess : WMP_errUnsupportedFormat;
305
306 } catch(...) {
307 return error_code;
308 }
309 }
310
311 // --------------------------------------------------------------------------
312
313 /**
314 Scan input dib format and return the equivalent PKPixelFormatGUID format for saving
315 @param dib Image to be saved
316 @param guid_format (returned value) GUID format
317 @param bHasAlpha (returned value) TRUE if an alpha layer is present
318 @return Returns TRUE if successful, returns FALSE otherwise
319 */
320 static ERR
GetOutputPixelFormat(FIBITMAP * dib,PKPixelFormatGUID * guid_format,BOOL * bHasAlpha)321 GetOutputPixelFormat(FIBITMAP *dib, PKPixelFormatGUID *guid_format, BOOL *bHasAlpha) {
322 const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
323 const unsigned bpp = FreeImage_GetBPP(dib);
324 const FREE_IMAGE_COLOR_TYPE color_type = FreeImage_GetColorType(dib);
325
326 *guid_format = GUID_PKPixelFormatDontCare;
327 *bHasAlpha = FALSE;
328
329 switch(image_type) {
330 case FIT_BITMAP: // standard image : 1-, 4-, 8-, 16-, 24-, 32-bit
331 switch(bpp) {
332 case 1:
333 // assume FIC_MINISBLACK
334 if(color_type == FIC_MINISBLACK) {
335 *guid_format = GUID_PKPixelFormatBlackWhite;
336 }
337 break;
338 case 8:
339 // assume FIC_MINISBLACK
340 if(color_type == FIC_MINISBLACK) {
341 *guid_format = GUID_PKPixelFormat8bppGray;
342 }
343 break;
344 case 16:
345 if ((FreeImage_GetRedMask(dib) == FI16_565_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_565_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_565_BLUE_MASK)) {
346 *guid_format = GUID_PKPixelFormat16bppRGB565;
347 } else {
348 // includes case where all the masks are 0
349 *guid_format = GUID_PKPixelFormat16bppRGB555;
350 }
351 break;
352 #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
353 case 24:
354 *guid_format = GUID_PKPixelFormat24bppBGR;
355 break;
356 case 32:
357 *guid_format = GUID_PKPixelFormat32bppBGRA;
358 *bHasAlpha = TRUE;
359 break;
360 #elif FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
361 case 24:
362 *guid_format = GUID_PKPixelFormat24bppRGB;
363 break;
364 case 32:
365 *guid_format = GUID_PKPixelFormat32bppRGBA;
366 *bHasAlpha = TRUE;
367 break;
368 #endif
369 case 4:
370 default:
371 // not supported
372 break;
373 }
374 break;
375 case FIT_UINT16: // array of unsigned short : unsigned 16-bit
376 *guid_format = GUID_PKPixelFormat16bppGray;
377 break;
378 case FIT_FLOAT: // array of float : 32-bit IEEE floating point
379 *guid_format = GUID_PKPixelFormat32bppGrayFloat;
380 break;
381 case FIT_RGB16: // 48-bit RGB image : 3 x 16-bit
382 *guid_format = GUID_PKPixelFormat48bppRGB;
383 break;
384 case FIT_RGBA16: // 64-bit RGBA image : 4 x 16-bit
385 *guid_format = GUID_PKPixelFormat64bppRGBA;
386 *bHasAlpha = TRUE;
387 break;
388 case FIT_RGBF: // 96-bit RGB float image : 3 x 32-bit IEEE floating point
389 *guid_format = GUID_PKPixelFormat96bppRGBFloat;
390 break;
391 case FIT_RGBAF: // 128-bit RGBA float image : 4 x 32-bit IEEE floating point
392 *guid_format = GUID_PKPixelFormat128bppRGBAFloat;
393 *bHasAlpha = TRUE;
394 break;
395
396 case FIT_INT16: // array of short : signed 16-bit
397 case FIT_UINT32: // array of unsigned long : unsigned 32-bit
398 case FIT_INT32: // array of long : signed 32-bit
399 case FIT_DOUBLE: // array of double : 64-bit IEEE floating point
400 case FIT_COMPLEX: // array of FICOMPLEX : 2 x 64-bit IEEE floating point
401
402 default:
403 // unsupported format
404 break;
405 }
406
407 return (*guid_format != GUID_PKPixelFormatDontCare) ? WMP_errSuccess : WMP_errUnsupportedFormat;
408 }
409
410 // ==========================================================
411 // Metadata loading
412 // ==========================================================
413
414 /**
415 Read a JPEG-XR IFD as a buffer
416 @see ReadMetadata
417 */
418 static ERR
ReadProfile(WMPStream * pStream,unsigned cbByteCount,unsigned uOffset,BYTE ** ppbProfile)419 ReadProfile(WMPStream* pStream, unsigned cbByteCount, unsigned uOffset, BYTE **ppbProfile) {
420 // (re-)allocate profile buffer
421 BYTE *pbProfile = *ppbProfile;
422 pbProfile = (BYTE*)realloc(pbProfile, cbByteCount);
423 if(!pbProfile) {
424 return WMP_errOutOfMemory;
425 }
426 // read the profile
427 if(WMP_errSuccess == pStream->SetPos(pStream, uOffset)) {
428 if(WMP_errSuccess == pStream->Read(pStream, pbProfile, cbByteCount)) {
429 *ppbProfile = pbProfile;
430 return WMP_errSuccess;
431 }
432 }
433 return WMP_errFileIO;
434 }
435
436 /**
437 Convert a DPKPROPVARIANT to a FITAG, then store the tag as FIMD_EXIF_MAIN
438 @see ReadDescriptiveMetadata
439 */
440 static BOOL
ReadPropVariant(WORD tag_id,const DPKPROPVARIANT & varSrc,FIBITMAP * dib)441 ReadPropVariant(WORD tag_id, const DPKPROPVARIANT & varSrc, FIBITMAP *dib) {
442 DWORD dwSize;
443
444 if(varSrc.vt == DPKVT_EMPTY) {
445 return FALSE;
446 }
447
448 // get the tag key
449 TagLib& s = TagLib::instance();
450 const char *key = s.getTagFieldName(TagLib::EXIF_MAIN, tag_id, NULL);
451 if(!key) {
452 return FALSE;
453 }
454
455 // create a tag
456 FITAG *tag = FreeImage_CreateTag();
457 if(tag) {
458 // set tag ID
459 FreeImage_SetTagID(tag, tag_id);
460 // set tag type, count, length and value
461 switch (varSrc.vt) {
462 case DPKVT_LPSTR:
463 FreeImage_SetTagType(tag, FIDT_ASCII);
464 dwSize = (DWORD)strlen(varSrc.VT.pszVal) + 1;
465 FreeImage_SetTagCount(tag, dwSize);
466 FreeImage_SetTagLength(tag, dwSize);
467 FreeImage_SetTagValue(tag, varSrc.VT.pszVal);
468 break;
469
470 case DPKVT_LPWSTR:
471 FreeImage_SetTagType(tag, FIDT_UNDEFINED);
472 dwSize = (DWORD)(sizeof(U16) * (wcslen((wchar_t *) varSrc.VT.pwszVal) + 1)); // +1 for NULL term
473 FreeImage_SetTagCount(tag, dwSize);
474 FreeImage_SetTagLength(tag, dwSize);
475 FreeImage_SetTagValue(tag, varSrc.VT.pwszVal);
476 break;
477
478 case DPKVT_UI2:
479 FreeImage_SetTagType(tag, FIDT_SHORT);
480 FreeImage_SetTagCount(tag, 1);
481 FreeImage_SetTagLength(tag, 2);
482 FreeImage_SetTagValue(tag, &varSrc.VT.uiVal);
483 break;
484
485 case DPKVT_UI4:
486 FreeImage_SetTagType(tag, FIDT_LONG);
487 FreeImage_SetTagCount(tag, 1);
488 FreeImage_SetTagLength(tag, 4);
489 FreeImage_SetTagValue(tag, &varSrc.VT.ulVal);
490 break;
491
492 default:
493 assert(FALSE); // This case is not handled
494 break;
495 }
496 // get the tag desctiption
497 const char *description = s.getTagDescription(TagLib::EXIF_MAIN, tag_id);
498 FreeImage_SetTagDescription(tag, description);
499
500 // store the tag
501 FreeImage_SetMetadata(FIMD_EXIF_MAIN, dib, key, tag);
502
503 FreeImage_DeleteTag(tag);
504 }
505 return TRUE;
506 }
507
508 /**
509 Read JPEG-XR descriptive metadata and store as EXIF_MAIN metadata
510 @see ReadPropVariant
511 */
512 static ERR
ReadDescriptiveMetadata(PKImageDecode * pID,FIBITMAP * dib)513 ReadDescriptiveMetadata(PKImageDecode *pID, FIBITMAP *dib) {
514 // get Exif TIFF metadata
515 const DESCRIPTIVEMETADATA *pDescMetadata = &pID->WMP.sDescMetadata;
516 // convert metadata to FITAG and store into the EXIF_MAIN metadata model
517 ReadPropVariant(WMP_tagImageDescription, pDescMetadata->pvarImageDescription, dib);
518 ReadPropVariant(WMP_tagCameraMake, pDescMetadata->pvarCameraMake, dib);
519 ReadPropVariant(WMP_tagCameraModel, pDescMetadata->pvarCameraModel, dib);
520 ReadPropVariant(WMP_tagSoftware, pDescMetadata->pvarSoftware, dib);
521 ReadPropVariant(WMP_tagDateTime, pDescMetadata->pvarDateTime, dib);
522 ReadPropVariant(WMP_tagArtist, pDescMetadata->pvarArtist, dib);
523 ReadPropVariant(WMP_tagCopyright, pDescMetadata->pvarCopyright, dib);
524 ReadPropVariant(WMP_tagRatingStars, pDescMetadata->pvarRatingStars, dib);
525 ReadPropVariant(WMP_tagRatingValue, pDescMetadata->pvarRatingValue, dib);
526 ReadPropVariant(WMP_tagCaption, pDescMetadata->pvarCaption, dib);
527 ReadPropVariant(WMP_tagDocumentName, pDescMetadata->pvarDocumentName, dib);
528 ReadPropVariant(WMP_tagPageName, pDescMetadata->pvarPageName, dib);
529 ReadPropVariant(WMP_tagPageNumber, pDescMetadata->pvarPageNumber, dib);
530 ReadPropVariant(WMP_tagHostComputer, pDescMetadata->pvarHostComputer, dib);
531 return WMP_errSuccess;
532 }
533
534 /**
535 Read ICC, XMP, Exif, Exif-GPS, IPTC, descriptive (i.e. Exif-TIFF) metadata
536 @see ReadProfile, ReadDescriptiveMetadata
537 */
538 static ERR
ReadMetadata(PKImageDecode * pID,FIBITMAP * dib)539 ReadMetadata(PKImageDecode *pID, FIBITMAP *dib) {
540 ERR error_code = 0; // error code as returned by the interface
541 size_t currentPos = 0; // current stream position
542
543 WMPStream *pStream = pID->pStream;
544 WmpDEMisc *wmiDEMisc = &pID->WMP.wmiDEMisc;
545 BYTE *pbProfile = NULL;
546
547 try {
548 // save current position
549 error_code = pStream->GetPos(pStream, ¤tPos);
550 JXR_CHECK(error_code);
551
552 // ICC profile
553 if(0 != wmiDEMisc->uColorProfileByteCount) {
554 unsigned cbByteCount = wmiDEMisc->uColorProfileByteCount;
555 unsigned uOffset = wmiDEMisc->uColorProfileOffset;
556 error_code = ReadProfile(pStream, cbByteCount, uOffset, &pbProfile);
557 JXR_CHECK(error_code);
558 FreeImage_CreateICCProfile(dib, pbProfile, cbByteCount);
559 }
560
561 // XMP metadata
562 if(0 != wmiDEMisc->uXMPMetadataByteCount) {
563 unsigned cbByteCount = wmiDEMisc->uXMPMetadataByteCount;
564 unsigned uOffset = wmiDEMisc->uXMPMetadataOffset;
565 error_code = ReadProfile(pStream, cbByteCount, uOffset, &pbProfile);
566 JXR_CHECK(error_code);
567 // store the tag as XMP
568 FITAG *tag = FreeImage_CreateTag();
569 if(tag) {
570 FreeImage_SetTagLength(tag, cbByteCount);
571 FreeImage_SetTagCount(tag, cbByteCount);
572 FreeImage_SetTagType(tag, FIDT_ASCII);
573 FreeImage_SetTagValue(tag, pbProfile);
574 FreeImage_SetTagKey(tag, g_TagLib_XMPFieldName);
575 FreeImage_SetMetadata(FIMD_XMP, dib, FreeImage_GetTagKey(tag), tag);
576 FreeImage_DeleteTag(tag);
577 }
578 }
579
580 // IPTC metadata
581 if(0 != wmiDEMisc->uIPTCNAAMetadataByteCount) {
582 unsigned cbByteCount = wmiDEMisc->uIPTCNAAMetadataByteCount;
583 unsigned uOffset = wmiDEMisc->uIPTCNAAMetadataOffset;
584 error_code = ReadProfile(pStream, cbByteCount, uOffset, &pbProfile);
585 JXR_CHECK(error_code);
586 // decode the IPTC profile
587 read_iptc_profile(dib, pbProfile, cbByteCount);
588 }
589
590 // Exif metadata
591 if(0 != wmiDEMisc->uEXIFMetadataByteCount) {
592 unsigned cbByteCount = wmiDEMisc->uEXIFMetadataByteCount;
593 unsigned uOffset = wmiDEMisc->uEXIFMetadataOffset;
594 error_code = ReadProfile(pStream, cbByteCount, uOffset, &pbProfile);
595 JXR_CHECK(error_code);
596 // decode the Exif profile
597 jpegxr_read_exif_profile(dib, pbProfile, cbByteCount, uOffset);
598 }
599
600 // Exif-GPS metadata
601 if(0 != wmiDEMisc->uGPSInfoMetadataByteCount) {
602 unsigned cbByteCount = wmiDEMisc->uGPSInfoMetadataByteCount;
603 unsigned uOffset = wmiDEMisc->uGPSInfoMetadataOffset;
604 error_code = ReadProfile(pStream, cbByteCount, uOffset, &pbProfile);
605 JXR_CHECK(error_code);
606 // decode the Exif-GPS profile
607 jpegxr_read_exif_gps_profile(dib, pbProfile, cbByteCount, uOffset);
608 }
609
610 // free profile buffer
611 free(pbProfile);
612 // restore initial position
613 error_code = pID->pStream->SetPos(pID->pStream, currentPos);
614 JXR_CHECK(error_code);
615
616 // as a LAST STEP, read descriptive metadata
617 // these metadata overwrite possible identical Exif-TIFF metadata
618 // that could have been read inside the Exif IFD
619
620 return ReadDescriptiveMetadata(pID, dib);
621
622 } catch(...) {
623 // free profile buffer
624 free(pbProfile);
625 if(currentPos) {
626 // restore initial position
627 pStream->SetPos(pStream, currentPos);
628 }
629 return error_code;
630 }
631 }
632
633 // ==========================================================
634 // Metadata saving
635 // ==========================================================
636
637 /**
638 Convert a FITAG (coming from FIMD_EXIF_MAIN) to a DPKPROPVARIANT.
639 No allocation is needed here, the function just copy pointers when needed.
640 @see WriteDescriptiveMetadata
641 */
642 static BOOL
WritePropVariant(FIBITMAP * dib,WORD tag_id,DPKPROPVARIANT & varDst)643 WritePropVariant(FIBITMAP *dib, WORD tag_id, DPKPROPVARIANT & varDst) {
644 FITAG *tag = NULL;
645
646 TagLib& s = TagLib::instance();
647
648 // clear output DPKPROPVARIANT
649 varDst.vt = DPKVT_EMPTY;
650
651 // given the tag id, get the tag key
652 const char *key = s.getTagFieldName(TagLib::EXIF_MAIN, tag_id, NULL);
653 // then, get the tag info
654 if(!FreeImage_GetMetadata(FIMD_EXIF_MAIN, dib, key, &tag)) {
655 return FALSE;
656 }
657
658 // set the tag value
659 switch(FreeImage_GetTagType(tag)) {
660 case FIDT_ASCII:
661 varDst.vt = DPKVT_LPSTR;
662 varDst.VT.pszVal = (char*)FreeImage_GetTagValue(tag);
663 break;
664 case FIDT_BYTE:
665 case FIDT_UNDEFINED:
666 varDst.vt = DPKVT_LPWSTR;
667 varDst.VT.pwszVal = (U16*)FreeImage_GetTagValue(tag);
668 break;
669 case FIDT_SHORT:
670 varDst.vt = DPKVT_UI2;
671 varDst.VT.uiVal = *((U16*)FreeImage_GetTagValue(tag));
672 break;
673 case FIDT_LONG:
674 varDst.vt = DPKVT_UI4;
675 varDst.VT.ulVal = *((U32*)FreeImage_GetTagValue(tag));
676 break;
677 default:
678 break;
679 }
680
681 return TRUE;
682 }
683
684 /**
685 Write EXIF_MAIN metadata to JPEG-XR descriptive metadata
686 @see WritePropVariant
687 */
688 static ERR
WriteDescriptiveMetadata(PKImageEncode * pIE,FIBITMAP * dib)689 WriteDescriptiveMetadata(PKImageEncode *pIE, FIBITMAP *dib) {
690 ERR error_code = 0; // error code as returned by the interface
691 DESCRIPTIVEMETADATA DescMetadata;
692
693 // fill the DESCRIPTIVEMETADATA structure (use pointers to arrays when needed)
694 WritePropVariant(dib, WMP_tagImageDescription, DescMetadata.pvarImageDescription);
695 WritePropVariant(dib, WMP_tagCameraMake, DescMetadata.pvarCameraMake);
696 WritePropVariant(dib, WMP_tagCameraModel, DescMetadata.pvarCameraModel);
697 WritePropVariant(dib, WMP_tagSoftware, DescMetadata.pvarSoftware);
698 WritePropVariant(dib, WMP_tagDateTime, DescMetadata.pvarDateTime);
699 WritePropVariant(dib, WMP_tagArtist, DescMetadata.pvarArtist);
700 WritePropVariant(dib, WMP_tagCopyright, DescMetadata.pvarCopyright);
701 WritePropVariant(dib, WMP_tagRatingStars, DescMetadata.pvarRatingStars);
702 WritePropVariant(dib, WMP_tagRatingValue, DescMetadata.pvarRatingValue);
703 WritePropVariant(dib, WMP_tagCaption, DescMetadata.pvarCaption);
704 WritePropVariant(dib, WMP_tagDocumentName, DescMetadata.pvarDocumentName);
705 WritePropVariant(dib, WMP_tagPageName, DescMetadata.pvarPageName);
706 WritePropVariant(dib, WMP_tagPageNumber, DescMetadata.pvarPageNumber);
707 WritePropVariant(dib, WMP_tagHostComputer, DescMetadata.pvarHostComputer);
708
709 // copy the structure to the encoder
710 error_code = pIE->SetDescriptiveMetadata(pIE, &DescMetadata);
711
712 // no need to free anything here
713 return error_code;
714 }
715
716 /**
717 Write ICC, XMP, Exif, Exif-GPS, IPTC, descriptive (i.e. Exif-TIFF) metadata
718 */
719 static ERR
WriteMetadata(PKImageEncode * pIE,FIBITMAP * dib)720 WriteMetadata(PKImageEncode *pIE, FIBITMAP *dib) {
721 ERR error_code = 0; // error code as returned by the interface
722 BYTE *profile = NULL;
723 unsigned profile_size = 0;
724
725 try {
726 // write ICC profile
727 {
728 FIICCPROFILE *iccProfile = FreeImage_GetICCProfile(dib);
729 if(iccProfile->data) {
730 error_code = pIE->SetColorContext(pIE, (U8*)iccProfile->data, iccProfile->size);
731 JXR_CHECK(error_code);
732 }
733 }
734
735 // write descriptive metadata
736 if(FreeImage_GetMetadataCount(FIMD_EXIF_MAIN, dib)) {
737 error_code = WriteDescriptiveMetadata(pIE, dib);
738 JXR_CHECK(error_code);
739 }
740
741 // write IPTC metadata
742 if(FreeImage_GetMetadataCount(FIMD_IPTC, dib)) {
743 // create a binary profile
744 if(write_iptc_profile(dib, &profile, &profile_size)) {
745 // write the profile
746 error_code = PKImageEncode_SetIPTCNAAMetadata_WMP(pIE, profile, profile_size);
747 JXR_CHECK(error_code);
748 // release profile
749 free(profile);
750 profile = NULL;
751 }
752 }
753
754 // write XMP metadata
755 {
756 FITAG *tag_xmp = NULL;
757 if(FreeImage_GetMetadata(FIMD_XMP, dib, g_TagLib_XMPFieldName, &tag_xmp)) {
758 error_code = PKImageEncode_SetXMPMetadata_WMP(pIE, (BYTE*)FreeImage_GetTagValue(tag_xmp), FreeImage_GetTagLength(tag_xmp));
759 JXR_CHECK(error_code);
760 }
761 }
762
763 // write Exif metadata
764 {
765 if(tiff_get_ifd_profile(dib, FIMD_EXIF_EXIF, &profile, &profile_size)) {
766 error_code = PKImageEncode_SetEXIFMetadata_WMP(pIE, profile, profile_size);
767 JXR_CHECK(error_code);
768 // release profile
769 free(profile);
770 profile = NULL;
771 }
772 }
773
774 // write Exif GPS metadata
775 {
776 if(tiff_get_ifd_profile(dib, FIMD_EXIF_GPS, &profile, &profile_size)) {
777 error_code = PKImageEncode_SetGPSInfoMetadata_WMP(pIE, profile, profile_size);
778 JXR_CHECK(error_code);
779 // release profile
780 free(profile);
781 profile = NULL;
782 }
783 }
784
785 return WMP_errSuccess;
786
787 } catch(...) {
788 free(profile);
789 return error_code;
790 }
791 }
792
793
794
795 // ==========================================================
796 // Quantization tables (Y, U, V, YHP, UHP, VHP),
797 // optimized for PSNR
798 // ==========================================================
799
800 static const int DPK_QPS_420[11][6] = { // for 8 bit only
801 { 66, 65, 70, 72, 72, 77 },
802 { 59, 58, 63, 64, 63, 68 },
803 { 52, 51, 57, 56, 56, 61 },
804 { 48, 48, 54, 51, 50, 55 },
805 { 43, 44, 48, 46, 46, 49 },
806 { 37, 37, 42, 38, 38, 43 },
807 { 26, 28, 31, 27, 28, 31 },
808 { 16, 17, 22, 16, 17, 21 },
809 { 10, 11, 13, 10, 10, 13 },
810 { 5, 5, 6, 5, 5, 6 },
811 { 2, 2, 3, 2, 2, 2 }
812 };
813
814 static const int DPK_QPS_8[12][6] = {
815 { 67, 79, 86, 72, 90, 98 },
816 { 59, 74, 80, 64, 83, 89 },
817 { 53, 68, 75, 57, 76, 83 },
818 { 49, 64, 71, 53, 70, 77 },
819 { 45, 60, 67, 48, 67, 74 },
820 { 40, 56, 62, 42, 59, 66 },
821 { 33, 49, 55, 35, 51, 58 },
822 { 27, 44, 49, 28, 45, 50 },
823 { 20, 36, 42, 20, 38, 44 },
824 { 13, 27, 34, 13, 28, 34 },
825 { 7, 17, 21, 8, 17, 21 }, // Photoshop 100%
826 { 2, 5, 6, 2, 5, 6 }
827 };
828
829 static const int DPK_QPS_16[11][6] = {
830 { 197, 203, 210, 202, 207, 213 },
831 { 174, 188, 193, 180, 189, 196 },
832 { 152, 167, 173, 156, 169, 174 },
833 { 135, 152, 157, 137, 153, 158 },
834 { 119, 137, 141, 119, 138, 142 },
835 { 102, 120, 125, 100, 120, 124 },
836 { 82, 98, 104, 79, 98, 103 },
837 { 60, 76, 81, 58, 76, 81 },
838 { 39, 52, 58, 36, 52, 58 },
839 { 16, 27, 33, 14, 27, 33 },
840 { 5, 8, 9, 4, 7, 8 }
841 };
842
843 static const int DPK_QPS_16f[11][6] = {
844 { 148, 177, 171, 165, 187, 191 },
845 { 133, 155, 153, 147, 172, 181 },
846 { 114, 133, 138, 130, 157, 167 },
847 { 97, 118, 120, 109, 137, 144 },
848 { 76, 98, 103, 85, 115, 121 },
849 { 63, 86, 91, 62, 96, 99 },
850 { 46, 68, 71, 43, 73, 75 },
851 { 29, 48, 52, 27, 48, 51 },
852 { 16, 30, 35, 14, 29, 34 },
853 { 8, 14, 17, 7, 13, 17 },
854 { 3, 5, 7, 3, 5, 6 }
855 };
856
857 static const int DPK_QPS_32f[11][6] = {
858 { 194, 206, 209, 204, 211, 217 },
859 { 175, 187, 196, 186, 193, 205 },
860 { 157, 170, 177, 167, 180, 190 },
861 { 133, 152, 156, 144, 163, 168 },
862 { 116, 138, 142, 117, 143, 148 },
863 { 98, 120, 123, 96, 123, 126 },
864 { 80, 99, 102, 78, 99, 102 },
865 { 65, 79, 84, 63, 79, 84 },
866 { 48, 61, 67, 45, 60, 66 },
867 { 27, 41, 46, 24, 40, 45 },
868 { 3, 22, 24, 2, 21, 22 }
869 };
870
871 // ==========================================================
872 // Plugin Implementation
873 // ==========================================================
874
875 static const char * DLL_CALLCONV
Format()876 Format() {
877 return "JPEG-XR";
878 }
879
880 static const char * DLL_CALLCONV
Description()881 Description() {
882 return "JPEG XR image format";
883 }
884
885 static const char * DLL_CALLCONV
Extension()886 Extension() {
887 return "jxr,wdp,hdp";
888 }
889
890 static const char * DLL_CALLCONV
RegExpr()891 RegExpr() {
892 return NULL;
893 }
894
895 static const char * DLL_CALLCONV
MimeType()896 MimeType() {
897 return "image/vnd.ms-photo";
898 }
899
900 static BOOL DLL_CALLCONV
Validate(FreeImageIO * io,fi_handle handle)901 Validate(FreeImageIO *io, fi_handle handle) {
902 BYTE jxr_signature[3] = { 0x49, 0x49, 0xBC };
903 BYTE signature[3] = { 0, 0, 0 };
904
905 io->read_proc(&signature, 1, 3, handle);
906
907 return (memcmp(jxr_signature, signature, 3) == 0);
908 }
909
910 static BOOL DLL_CALLCONV
SupportsExportDepth(int depth)911 SupportsExportDepth(int depth) {
912 return (
913 (depth == 1) ||
914 (depth == 8) ||
915 (depth == 16) ||
916 (depth == 24) ||
917 (depth == 32)
918 );
919 }
920
921 static BOOL DLL_CALLCONV
SupportsExportType(FREE_IMAGE_TYPE type)922 SupportsExportType(FREE_IMAGE_TYPE type) {
923 return (
924 (type == FIT_BITMAP) ||
925 (type == FIT_UINT16) ||
926 (type == FIT_RGB16) ||
927 (type == FIT_RGBA16) ||
928 (type == FIT_FLOAT) ||
929 (type == FIT_RGBF) ||
930 (type == FIT_RGBAF)
931 );
932 }
933
934 static BOOL DLL_CALLCONV
SupportsICCProfiles()935 SupportsICCProfiles() {
936 return TRUE;
937 }
938
939 static BOOL DLL_CALLCONV
SupportsNoPixels()940 SupportsNoPixels() {
941 return TRUE;
942 }
943
944 // ==========================================================
945 // Open & Close
946 // ==========================================================
947
948 static void * DLL_CALLCONV
Open(FreeImageIO * io,fi_handle handle,BOOL read)949 Open(FreeImageIO *io, fi_handle handle, BOOL read) {
950 WMPStream *pStream = NULL; // stream interface
951 if(io && handle) {
952 // allocate the FreeImageIO stream wrapper
953 FreeImageJXRIO *jxr_io = (FreeImageJXRIO*)malloc(sizeof(FreeImageJXRIO));
954 if(jxr_io) {
955 jxr_io->io = io;
956 jxr_io->handle = handle;
957 // create a JXR stream wrapper
958 if(_jxr_io_Create(&pStream, jxr_io) != WMP_errSuccess) {
959 free(jxr_io);
960 return NULL;
961 }
962 }
963 }
964 return pStream;
965 }
966
967 static void DLL_CALLCONV
Close(FreeImageIO * io,fi_handle handle,void * data)968 Close(FreeImageIO *io, fi_handle handle, void *data) {
969 WMPStream *pStream = (WMPStream*)data;
970 if(pStream) {
971 // free the FreeImageIO stream wrapper
972 FreeImageJXRIO *jxr_io = (FreeImageJXRIO*)pStream->state.pvObj;
973 free(jxr_io);
974 // free the JXR stream wrapper
975 pStream->fMem = TRUE;
976 _jxr_io_Close(&pStream);
977 }
978 }
979
980 // ==========================================================
981 // Load
982 // ==========================================================
983
984 /**
985 Set decoder parameters
986 @param pDecoder Decoder handle
987 @param flags FreeImage load flags
988 */
989 static void
SetDecoderParameters(PKImageDecode * pDecoder,int flags)990 SetDecoderParameters(PKImageDecode *pDecoder, int flags) {
991 // load image & alpha for formats with alpha
992 pDecoder->WMP.wmiSCP.uAlphaMode = 2;
993 // more options to come ...
994 }
995
996 /**
997 Copy or convert & copy decoded pixels into the dib
998 @param pDecoder Decoder handle
999 @param out_guid_format Target guid format
1000 @param dib Output dib
1001 @param width Image width
1002 @param height Image height
1003 @return Returns 0 if successful, returns ERR otherwise
1004 */
1005 static ERR
CopyPixels(PKImageDecode * pDecoder,PKPixelFormatGUID out_guid_format,FIBITMAP * dib,int width,int height)1006 CopyPixels(PKImageDecode *pDecoder, PKPixelFormatGUID out_guid_format, FIBITMAP *dib, int width, int height) {
1007 PKFormatConverter *pConverter = NULL; // pixel format converter
1008 ERR error_code = 0; // error code as returned by the interface
1009 BYTE *pb = NULL; // local buffer used for pixel format conversion
1010
1011 // image dimensions
1012 const PKRect rect = {0, 0, width, height};
1013
1014 try {
1015 // get input file pixel format ...
1016 PKPixelFormatGUID in_guid_format;
1017 error_code = pDecoder->GetPixelFormat(pDecoder, &in_guid_format);
1018 JXR_CHECK(error_code);
1019
1020 // is a format conversion needed ?
1021
1022 if(IsEqualGUID(out_guid_format, in_guid_format)) {
1023 // no conversion, load bytes "as is" ...
1024
1025 // get a pointer to dst pixel data
1026 BYTE *dib_bits = FreeImage_GetBits(dib);
1027
1028 // get dst pitch (count of BYTE for stride)
1029 const unsigned cbStride = FreeImage_GetPitch(dib);
1030
1031 // decode and copy bits to dst array
1032 error_code = pDecoder->Copy(pDecoder, &rect, dib_bits, cbStride);
1033 JXR_CHECK(error_code);
1034 }
1035 else {
1036 // we need to use the conversion API ...
1037
1038 // allocate the pixel format converter
1039 error_code = PKCodecFactory_CreateFormatConverter(&pConverter);
1040 JXR_CHECK(error_code);
1041
1042 // set the conversion function
1043 error_code = pConverter->Initialize(pConverter, pDecoder, NULL, out_guid_format);
1044 JXR_CHECK(error_code);
1045
1046 // get the maximum stride
1047 unsigned cbStride = 0;
1048 {
1049 PKPixelInfo pPIFrom;
1050 PKPixelInfo pPITo;
1051
1052 pPIFrom.pGUIDPixFmt = &in_guid_format;
1053 error_code = PixelFormatLookup(&pPIFrom, LOOKUP_FORWARD);
1054 JXR_CHECK(error_code);
1055
1056 pPITo.pGUIDPixFmt = &out_guid_format;
1057 error_code = PixelFormatLookup(&pPITo, LOOKUP_FORWARD);
1058 JXR_CHECK(error_code);
1059
1060 unsigned cbStrideFrom = ((pPIFrom.cbitUnit + 7) >> 3) * width;
1061 unsigned cbStrideTo = ((pPITo.cbitUnit + 7) >> 3) * width;
1062 cbStride = MAX(cbStrideFrom, cbStrideTo);
1063 }
1064
1065 // allocate a local decoder / encoder buffer
1066 error_code = PKAllocAligned((void **) &pb, cbStride * height, 128);
1067 JXR_CHECK(error_code);
1068
1069 // copy / convert pixels
1070 error_code = pConverter->Copy(pConverter, &rect, pb, cbStride);
1071 JXR_CHECK(error_code);
1072
1073 // now copy pixels into the dib
1074 const size_t line_size = FreeImage_GetLine(dib);
1075 for(int y = 0; y < height; y++) {
1076 BYTE *src_bits = (BYTE*)(pb + y * cbStride);
1077 BYTE *dst_bits = (BYTE*)FreeImage_GetScanLine(dib, y);
1078 memcpy(dst_bits, src_bits, line_size);
1079 }
1080
1081 // free the local buffer
1082 PKFreeAligned((void **) &pb);
1083
1084 // free the pixel format converter
1085 PKFormatConverter_Release(&pConverter);
1086 }
1087
1088 // FreeImage DIB are upside-down relative to usual graphic conventions
1089 FreeImage_FlipVertical(dib);
1090
1091 // post-processing ...
1092 // -------------------
1093
1094 // swap RGB as needed
1095
1096 #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
1097 if(IsEqualGUID(out_guid_format, GUID_PKPixelFormat24bppRGB) || IsEqualGUID(out_guid_format, GUID_PKPixelFormat32bppRGB)) {
1098 SwapRedBlue32(dib);
1099 }
1100 #elif FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB
1101 if(IsEqualGUID(out_guid_format, GUID_PKPixelFormat24bppBGR) || IsEqualGUID(out_guid_format, GUID_PKPixelFormat32bppBGR)) {
1102 SwapRedBlue32(dib);
1103 }
1104 #endif
1105
1106 return WMP_errSuccess;
1107
1108 } catch(...) {
1109 // free the local buffer
1110 PKFreeAligned((void **) &pb);
1111 // free the pixel format converter
1112 PKFormatConverter_Release(&pConverter);
1113
1114 return error_code;
1115 }
1116 }
1117
1118 // --------------------------------------------------------------------------
1119
1120 static FIBITMAP * DLL_CALLCONV
Load(FreeImageIO * io,fi_handle handle,int page,int flags,void * data)1121 Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
1122 PKImageDecode *pDecoder = NULL; // decoder interface
1123 ERR error_code = 0; // error code as returned by the interface
1124 PKPixelFormatGUID guid_format; // loaded pixel format (== input file pixel format if no conversion needed)
1125
1126 FREE_IMAGE_TYPE image_type = FIT_UNKNOWN; // input image type
1127 unsigned bpp = 0; // input image bit depth
1128 FIBITMAP *dib = NULL;
1129
1130 // get the I/O stream wrapper
1131 WMPStream *pDecodeStream = (WMPStream*)data;
1132
1133 if(!handle || !pDecodeStream) {
1134 return NULL;
1135 }
1136
1137 BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
1138
1139 try {
1140 int width, height; // image dimensions (in pixels)
1141
1142 // create a JXR decoder interface and initialize function pointers with *_WMP functions
1143 error_code = PKImageDecode_Create_WMP(&pDecoder);
1144 JXR_CHECK(error_code);
1145
1146 // attach the stream to the decoder ...
1147 // ... then read the image container and the metadata
1148 error_code = pDecoder->Initialize(pDecoder, pDecodeStream);
1149 JXR_CHECK(error_code);
1150
1151 // set decoder parameters
1152 SetDecoderParameters(pDecoder, flags);
1153
1154 // get dst image format specifications
1155 unsigned red_mask = 0, green_mask = 0, blue_mask = 0;
1156 error_code = GetInputPixelFormat(pDecoder, &guid_format, &image_type, &bpp, &red_mask, &green_mask, &blue_mask);
1157 JXR_CHECK(error_code);
1158
1159 // get image dimensions
1160 pDecoder->GetSize(pDecoder, &width, &height);
1161
1162 // allocate dst image
1163 {
1164 dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, bpp, red_mask, green_mask, blue_mask);
1165 if(!dib) {
1166 throw FI_MSG_ERROR_DIB_MEMORY;
1167 }
1168 if(FreeImage_GetBPP(dib) == 1) {
1169 // BD_1 - build a FIC_MINISBLACK palette
1170 RGBQUAD *pal = FreeImage_GetPalette(dib);
1171 pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0;
1172 pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255;
1173 }
1174 }
1175
1176 // get image resolution
1177 {
1178 float resX, resY; // image resolution (in dots per inch)
1179 // convert from English units, i.e. dots per inch to universal units, i.e. dots per meter
1180 pDecoder->GetResolution(pDecoder, &resX, &resY);
1181 FreeImage_SetDotsPerMeterX(dib, (unsigned)(resX / 0.0254F + 0.5F));
1182 FreeImage_SetDotsPerMeterY(dib, (unsigned)(resY / 0.0254F + 0.5F));
1183 }
1184
1185 // get metadata & ICC profile
1186 error_code = ReadMetadata(pDecoder, dib);
1187 JXR_CHECK(error_code);
1188
1189 if(header_only) {
1190 // header only mode ...
1191
1192 // free the decoder
1193 pDecoder->Release(&pDecoder);
1194 assert(pDecoder == NULL);
1195
1196 return dib;
1197 }
1198
1199 // copy pixels into the dib, perform pixel conversion if needed
1200 error_code = CopyPixels(pDecoder, guid_format, dib, width, height);
1201 JXR_CHECK(error_code);
1202
1203 // free the decoder
1204 pDecoder->Release(&pDecoder);
1205 assert(pDecoder == NULL);
1206
1207 return dib;
1208
1209 } catch (const char *message) {
1210 // unload the dib
1211 FreeImage_Unload(dib);
1212 // free the decoder
1213 pDecoder->Release(&pDecoder);
1214
1215 if(NULL != message) {
1216 FreeImage_OutputMessageProc(s_format_id, message);
1217 }
1218 }
1219
1220 return NULL;
1221 }
1222
1223 // ==========================================================
1224 // Save
1225 // ==========================================================
1226
1227 /**
1228 Configure compression parameters
1229
1230 ImageQuality Q (BD==1) Q (BD==8) Q (BD==16) Q (BD==32F) Subsample Overlap
1231 [0.0, 0.4] 8-IQ*5 (see table) (see table) (see table) 4:4:4 2
1232 (0.4, 0.8) 8-IQ*5 (see table) (see table) (see table) 4:4:4 1
1233 [0.8, 1.0) 8-IQ*5 (see table) (see table) (see table) 4:4:4 1
1234 [1.0, 1.0] 1 1 1 1 4:4:4 0
1235
1236 @param wmiSCP Encoder parameters
1237 @param pixelInfo Image specifications
1238 @param fltImageQuality Image output quality in [0..1), 1 means lossless
1239 */
1240 static void
SetCompression(CWMIStrCodecParam * wmiSCP,const PKPixelInfo * pixelInfo,float fltImageQuality)1241 SetCompression(CWMIStrCodecParam *wmiSCP, const PKPixelInfo *pixelInfo, float fltImageQuality) {
1242 if(fltImageQuality < 1.0F) {
1243 // overlap
1244 if(fltImageQuality >= 0.5F) {
1245 wmiSCP->olOverlap = OL_ONE;
1246 } else {
1247 wmiSCP->olOverlap = OL_TWO;
1248 }
1249 // chroma sub-sampling
1250 if(fltImageQuality >= 0.5F || pixelInfo->uBitsPerSample > 8) {
1251 wmiSCP->cfColorFormat = YUV_444;
1252 } else {
1253 wmiSCP->cfColorFormat = YUV_420;
1254 }
1255
1256 // bit depth
1257 if(pixelInfo->bdBitDepth == BD_1) {
1258 wmiSCP->uiDefaultQPIndex = (U8)(8 - 5.0F * fltImageQuality + 0.5F);
1259 }
1260 else {
1261 // remap [0.8, 0.866, 0.933, 1.0] to [0.8, 0.9, 1.0, 1.1]
1262 // to use 8-bit DPK QP table (0.933 == Photoshop JPEG 100)
1263 if(fltImageQuality > 0.8F && pixelInfo->bdBitDepth == BD_8 && wmiSCP->cfColorFormat != YUV_420 && wmiSCP->cfColorFormat != YUV_422) {
1264 fltImageQuality = 0.8F + (fltImageQuality - 0.8F) * 1.5F;
1265 }
1266
1267 const int qi = (int) (10.0F * fltImageQuality);
1268 const float qf = 10.0F * fltImageQuality - (float)qi;
1269
1270 const int *pQPs =
1271 (wmiSCP->cfColorFormat == YUV_420 || wmiSCP->cfColorFormat == YUV_422) ?
1272 DPK_QPS_420[qi] :
1273 (pixelInfo->bdBitDepth == BD_8 ? DPK_QPS_8[qi] :
1274 (pixelInfo->bdBitDepth == BD_16 ? DPK_QPS_16[qi] :
1275 (pixelInfo->bdBitDepth == BD_16F ? DPK_QPS_16f[qi] :
1276 DPK_QPS_32f[qi])));
1277
1278 wmiSCP->uiDefaultQPIndex = (U8) (0.5F + (float) pQPs[0] * (1.0F - qf) + (float) (pQPs + 6)[0] * qf);
1279 wmiSCP->uiDefaultQPIndexU = (U8) (0.5F + (float) pQPs[1] * (1.0F - qf) + (float) (pQPs + 6)[1] * qf);
1280 wmiSCP->uiDefaultQPIndexV = (U8) (0.5F + (float) pQPs[2] * (1.0F - qf) + (float) (pQPs + 6)[2] * qf);
1281 wmiSCP->uiDefaultQPIndexYHP = (U8) (0.5F + (float) pQPs[3] * (1.0F - qf) + (float) (pQPs + 6)[3] * qf);
1282 wmiSCP->uiDefaultQPIndexUHP = (U8) (0.5F + (float) pQPs[4] * (1.0F - qf) + (float) (pQPs + 6)[4] * qf);
1283 wmiSCP->uiDefaultQPIndexVHP = (U8) (0.5F + (float) pQPs[5] * (1.0F - qf) + (float) (pQPs + 6)[5] * qf);
1284 }
1285 } // fltImageQuality < 1.0F
1286 else {
1287 // lossless mode
1288 wmiSCP->uiDefaultQPIndex = 1;
1289 }
1290 }
1291
1292 /**
1293 Set encoder parameters
1294 @param wmiSCP Encoder parameters
1295 @param pixelInfo Image specifications
1296 @param flags FreeImage save flags
1297 @param bHasAlpha TRUE if an alpha layer is present
1298 */
1299 static void
SetEncoderParameters(CWMIStrCodecParam * wmiSCP,const PKPixelInfo * pixelInfo,int flags,BOOL bHasAlpha)1300 SetEncoderParameters(CWMIStrCodecParam *wmiSCP, const PKPixelInfo *pixelInfo, int flags, BOOL bHasAlpha) {
1301 float fltImageQuality = 1.0F;
1302
1303 // all values have been set to zero by the API
1304 // update default values for some attributes
1305 wmiSCP->cfColorFormat = YUV_444; // color format
1306 wmiSCP->bdBitDepth = BD_LONG; // internal bit depth
1307 wmiSCP->bfBitstreamFormat = SPATIAL; // compressed image data in spatial order
1308 wmiSCP->bProgressiveMode = FALSE; // sequential mode
1309 wmiSCP->olOverlap = OL_ONE; // single level overlap processing
1310 wmiSCP->cNumOfSliceMinus1H = 0; // # of horizontal slices
1311 wmiSCP->cNumOfSliceMinus1V = 0; // # of vertical slices
1312 wmiSCP->sbSubband = SB_ALL; // keep all subbands
1313 wmiSCP->uAlphaMode = 0; // 0:no alpha 1: alpha only else: something + alpha
1314 wmiSCP->uiDefaultQPIndex = 1; // quantization for grey or rgb layer(s), 1: lossless
1315 wmiSCP->uiDefaultQPIndexAlpha = 1; // quantization for alpha layer, 1: lossless
1316
1317 // process the flags
1318 // -----------------
1319
1320 // progressive mode
1321 if((flags & JXR_PROGRESSIVE) == JXR_PROGRESSIVE) {
1322 // turn on progressive mode (instead of sequential mode)
1323 wmiSCP->bProgressiveMode = TRUE;
1324 }
1325
1326 // quality in [0.01 - 1.0), 1.0 means lossless - default is 0.80
1327 int quality = flags & 0x7F;
1328 if(quality == 0) {
1329 // defaut to 0.80
1330 fltImageQuality = 0.8F;
1331 } else if((flags & JXR_LOSSLESS) == JXR_LOSSLESS) {
1332 fltImageQuality = 1.0F;
1333 } else {
1334 quality = (quality >= 100) ? 100 : quality;
1335 fltImageQuality = quality / 100.0F;
1336 }
1337 SetCompression(wmiSCP, pixelInfo, fltImageQuality);
1338
1339 // alpha compression
1340 if(bHasAlpha) {
1341 wmiSCP->uAlphaMode = 2; // encode with a planar alpha channel
1342 }
1343 }
1344
1345 // --------------------------------------------------------------------------
1346
1347 static BOOL DLL_CALLCONV
Save(FreeImageIO * io,FIBITMAP * dib,fi_handle handle,int page,int flags,void * data)1348 Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
1349 BOOL bIsFlipped = FALSE; // FreeImage DIB are upside-down relative to usual graphic conventions
1350 PKPixelFormatGUID guid_format; // image format
1351 PKPixelInfo pixelInfo; // image specifications
1352 BOOL bHasAlpha = FALSE; // is alpha layer present ?
1353
1354 PKImageEncode *pEncoder = NULL; // encoder interface
1355 ERR error_code = 0; // error code as returned by the interface
1356
1357 // get the I/O stream wrapper
1358 WMPStream *pEncodeStream = (WMPStream*)data;
1359
1360 if(!dib || !handle || !pEncodeStream) {
1361 return FALSE;
1362 }
1363
1364 try {
1365 // get image dimensions
1366 unsigned width = FreeImage_GetWidth(dib);
1367 unsigned height = FreeImage_GetHeight(dib);
1368
1369 // check JPEG-XR limits
1370 if((width < MB_WIDTH_PIXEL) || (height < MB_HEIGHT_PIXEL)) {
1371 FreeImage_OutputMessageProc(s_format_id, "Unsupported image size: width x height = %d x %d", width, height);
1372 throw (const char*)NULL;
1373 }
1374
1375 // get output pixel format
1376 error_code = GetOutputPixelFormat(dib, &guid_format, &bHasAlpha);
1377 JXR_CHECK(error_code);
1378 pixelInfo.pGUIDPixFmt = &guid_format;
1379 error_code = PixelFormatLookup(&pixelInfo, LOOKUP_FORWARD);
1380 JXR_CHECK(error_code);
1381
1382 // create a JXR encoder interface and initialize function pointers with *_WMP functions
1383 error_code = PKImageEncode_Create_WMP(&pEncoder);
1384 JXR_CHECK(error_code);
1385
1386 // attach the stream to the encoder and set all encoder parameters to zero ...
1387 error_code = pEncoder->Initialize(pEncoder, pEncodeStream, &pEncoder->WMP.wmiSCP, sizeof(CWMIStrCodecParam));
1388 JXR_CHECK(error_code);
1389
1390 // ... then configure the encoder
1391 SetEncoderParameters(&pEncoder->WMP.wmiSCP, &pixelInfo, flags, bHasAlpha);
1392
1393 // set pixel format
1394 pEncoder->SetPixelFormat(pEncoder, guid_format);
1395
1396 // set image size
1397 pEncoder->SetSize(pEncoder, width, height);
1398
1399 // set resolution (convert from universal units to English units)
1400 float resX = (float)(unsigned)(0.5F + 0.0254F * FreeImage_GetDotsPerMeterX(dib));
1401 float resY = (float)(unsigned)(0.5F + 0.0254F * FreeImage_GetDotsPerMeterY(dib));
1402 pEncoder->SetResolution(pEncoder, resX, resY);
1403
1404 // set metadata
1405 WriteMetadata(pEncoder, dib);
1406
1407 // write metadata & pixels
1408 // -----------------------
1409
1410 // dib coordinates are upside-down relative to usual conventions
1411 bIsFlipped = FreeImage_FlipVertical(dib);
1412
1413 // get a pointer to dst pixel data
1414 BYTE *dib_bits = FreeImage_GetBits(dib);
1415
1416 // get dst pitch (count of BYTE for stride)
1417 const unsigned cbStride = FreeImage_GetPitch(dib);
1418
1419 // write metadata + pixels on output
1420 error_code = pEncoder->WritePixels(pEncoder, height, dib_bits, cbStride);
1421 JXR_CHECK(error_code);
1422
1423 // recover dib coordinates
1424 FreeImage_FlipVertical(dib);
1425
1426 // free the encoder
1427 pEncoder->Release(&pEncoder);
1428 assert(pEncoder == NULL);
1429
1430 return TRUE;
1431
1432 } catch (const char *message) {
1433 if(bIsFlipped) {
1434 // recover dib coordinates
1435 FreeImage_FlipVertical(dib);
1436 }
1437 if(pEncoder) {
1438 // free the encoder
1439 pEncoder->Release(&pEncoder);
1440 assert(pEncoder == NULL);
1441 }
1442 if(NULL != message) {
1443 FreeImage_OutputMessageProc(s_format_id, message);
1444 }
1445 }
1446
1447 return FALSE;
1448 }
1449
1450 // ==========================================================
1451 // Init
1452 // ==========================================================
1453
1454 void DLL_CALLCONV
InitJXR(Plugin * plugin,int format_id)1455 InitJXR(Plugin *plugin, int format_id) {
1456 s_format_id = format_id;
1457
1458 plugin->format_proc = Format;
1459 plugin->description_proc = Description;
1460 plugin->extension_proc = Extension;
1461 plugin->regexpr_proc = RegExpr;
1462 plugin->open_proc = Open;
1463 plugin->close_proc = Close;
1464 plugin->pagecount_proc = NULL;
1465 plugin->pagecapability_proc = NULL;
1466 plugin->load_proc = Load;
1467 plugin->save_proc = Save;
1468 plugin->validate_proc = Validate;
1469 plugin->mime_proc = MimeType;
1470 plugin->supports_export_bpp_proc = SupportsExportDepth;
1471 plugin->supports_export_type_proc = SupportsExportType;
1472 plugin->supports_icc_profiles_proc = SupportsICCProfiles;
1473 plugin->supports_no_pixels_proc = SupportsNoPixels;
1474 }
1475
1476