Coverage Report

Created: 2026-06-09 06:12

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