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, &currentPos);
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