Engauge Digitizer  2
Jpeg2000.cpp
1 /******************************************************************************************************
2  * (C) 2014 markummitchell@github.com. This file is part of Engauge Digitizer, which is released *
3  * under GNU General Public License version 2 (GPLv2) or (at your option) any later version. See file *
4  * LICENSE or go to gnu.org/licenses for details. Distribution requires prior written permission. *
5  ******************************************************************************************************/
6 
7 #include "Jpeg2000.h"
8 #include "Jpeg2000Callbacks.h"
9 #include "Jpeg2000Color.h"
10 #include "Jpeg2000FormatDefs.h"
11 #include "Logger.h"
12 #include <QBuffer>
13 #include <QFile>
14 #include <QImage>
15 #include <QString>
16 
17 #define JP2_RFC3745_MAGIC "\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a"
18 #define JP2_MAGIC "\x0d\x0a\x87\x0a"
19 #define J2K_CODESTREAM_MAGIC "\xff\x4f\xff\x51"
20 
22 {
23 }
24 
25 void Jpeg2000::applyImageTweaks (opj_image_t *image) const
26 {
27  if (image->color_space == OPJ_CLRSPC_SYCC) {
28  color_sycc_to_rgb (image);
29  }
30 
31  if (image->color_space != OPJ_CLRSPC_SYCC &&
32  image->numcomps == 3 &&
33  image->comps[0].dx == image->comps[0].dy &&
34  image->comps[1].dx != 1) {
35  image->color_space = OPJ_CLRSPC_SYCC;
36  } else if (image->numcomps <= 2) {
37  image->color_space = OPJ_CLRSPC_GRAY;
38  }
39 
40  if (image->icc_profile_buf) {
41 #if defined(OPJ_HAVE_LIBLCMS1) || defined(OPJ_HAVE_LIBLCMS2)
42  color_apply_icc_profile (image);
43 #endif
44  free (image->icc_profile_buf);
45  image->icc_profile_buf = 0;
46  image->icc_profile_len = 0;
47  }
48 }
49 
50 opj_codec_t *Jpeg2000::decode (int decodeFormat) const
51 {
52  switch(decodeFormat)
53  {
54  case J2K_CFMT: /* JPEG-2000 codestream */
55  return opj_create_decompress(OPJ_CODEC_J2K);
56 
57  case JP2_CFMT: /* JPEG 2000 compressed image data */
58  return opj_create_decompress(OPJ_CODEC_JP2);
59 
60  case JPT_CFMT: /* JPEG 2000, JPIP */
61  return opj_create_decompress(OPJ_CODEC_JPT);
62 
63  default:
64  break;
65  }
66 
67  return 0;
68 }
69 
70 int Jpeg2000::getFileFormat(const char *filename) const
71 {
72  static const char *extension[] = {"pgx", "pnm", "pgm", "ppm", "bmp",
73  "tif", "raw", "rawl", "tga", "png",
74  "j2k", "jp2", "jpt", "j2c", "jpc"};
75  static const int format[] = {PGX_DFMT, PXM_DFMT, PXM_DFMT, PXM_DFMT, BMP_DFMT,
76  TIF_DFMT, RAW_DFMT, RAWL_DFMT, TGA_DFMT, PNG_DFMT,
77  J2K_CFMT, JP2_CFMT, JPT_CFMT, J2K_CFMT, J2K_CFMT};
78  const char * ext = strrchr(filename, '.');
79  if (ext == NULL) {
80  return -1;
81  }
82  ext++;
83  if (*ext) {
84  for (unsigned int i = 0; i < sizeof(format)/sizeof(*format); i++) {
85  if(strcasecmp(ext, extension[i]) == 0) {
86  return format[i];
87  }
88  }
89  }
90 
91  return -1;
92 }
93 
94 void Jpeg2000::initializeParameters (opj_dparameters_t &parameters) const
95 {
96  parameters.cp_reduce = 0;
97  parameters.cp_layer = 0;
98  parameters.cod_format = 10;
99  parameters.decod_format = 1;
100  parameters.DA_x0 = 0;
101  parameters.DA_x1 = 0;
102  parameters.DA_y0 = 0;
103  parameters.DA_y1 = 0;
104  parameters.m_verbose = 0;
105  parameters.tile_index = 0;
106  parameters.nb_tile_to_decode = 0;
107  parameters.jpwl_correct = 0;
108  parameters.jpwl_exp_comps = 0;
109  parameters.jpwl_max_tiles = 0;
110  parameters.flags = 0;
111 }
112 
113 int Jpeg2000::inputFormat(const char *filename) const
114 {
115  FILE *reader;
116  const char *s, *magic_s;
117  int ext_format, magic_format;
118  unsigned char buf[12];
119  OPJ_SIZE_T l_nb_read;
120 
121  reader = fopen(filename,
122  "rb");
123 
124  if (reader == NULL) {
125  return -2;
126  }
127 
128  memset(buf, 0, 12);
129  l_nb_read = fread(buf, 1, 12, reader);
130  fclose(reader);
131  if (l_nb_read != 12) {
132  return -1;
133  }
134 
135  ext_format = getFileFormat(filename);
136 
137  if (ext_format == JPT_CFMT) {
138  return JPT_CFMT;
139  }
140 
141  if (memcmp(buf, JP2_RFC3745_MAGIC, 12) == 0 || memcmp(buf, JP2_MAGIC, 4) == 0) {
142  magic_format = JP2_CFMT;
143  magic_s = ".jp2";
144  } else if (memcmp(buf, J2K_CODESTREAM_MAGIC, 4) == 0) {
145  magic_format = J2K_CFMT;
146  magic_s = ".j2k or .jpc or .j2c";
147  } else {
148  return -1;
149  }
150 
151  if (magic_format == ext_format) {
152  return ext_format;
153  }
154 
155  s = filename + strlen(filename) - 4;
156 
157  LOG4CPP_ERROR_S ((*mainCat)) << "Jpeg2000::inputFormat"
158  << "The extension of this file is incorrect. Found " << s
159  << ". Should be " << magic_s;
160 
161  return magic_format;
162 }
163 
164 bool Jpeg2000::invalidFileExtension (const QString &filename) const
165 {
166  const int CHARACTER_IN_EXTENSION = 3;
167 
168  bool invalid = true;
169 
170  // Look for file extension in approved list. A complication is that we probably want this
171  // comparison to be case insensitive
172  QString extensionGot = filename.right (CHARACTER_IN_EXTENSION);
173 
174  QStringList extensions = supportedFileExtensions();
175  QStringList::iterator itr;
176  for (itr = extensions.begin(); itr != extensions.end(); itr++) {
177 
178  QString extensionWanted = *itr;
179  if (QString::compare (extensionGot,
180  extensionWanted,
181  Qt::CaseInsensitive)) {
182 
183  // Found it
184  invalid = false;
185  break;
186  }
187  }
188 
189  return invalid;
190 }
191 
192 bool Jpeg2000::load (const QString &filename,
193  QImage &imageResult) const
194 {
195  LOG4CPP_INFO_S ((*mainCat)) << "Jpeg2000::load"
196  << " filename=" << filename.toLatin1().data();
197 
198  if (invalidFileExtension (filename)) {
199  return false;
200  }
201 
202  opj_dparameters_t parameters;
203  initializeParameters (parameters);
204 
205  parameters.decod_format = inputFormat (filename.toLatin1().data());
206 
207  opj_stream_t *inStream = opj_stream_create_default_file_stream (filename.toLatin1().data(), 1);
208  if (!inStream) {
209  LOG4CPP_ERROR_S ((*mainCat)) << "Jpeg2000::load encountered error opening stream";
210  return false;
211  }
212 
213  // Create decoder
214  opj_codec_t *inCodec = decode (parameters.decod_format);
215  if (!inCodec) {
216  LOG4CPP_ERROR_S ((*mainCat)) << "Jpeg2000::load encountered error creating decoding stream";
217  opj_stream_destroy (inStream);
218  return false;
219  }
220 
221  // Callbacks for local handling of errors
222  opj_set_info_handler (inCodec, infoCallback, 0);
223  opj_set_warning_handler (inCodec, warningCallback, 0);
224  opj_set_error_handler (inCodec, errorCallback, 0);
225 
226  if (!opj_setup_decoder (inCodec,
227  &parameters)) {
228  LOG4CPP_ERROR_S ((*mainCat)) << "Jpeg2000::load encountered error decoding stream";
229  opj_stream_destroy (inStream);
230  opj_destroy_codec (inCodec);
231  return false;
232  }
233 
234  // Read header and, if necessary, the JP2 boxes
235  opj_image_t *image;
236  if (!opj_read_header (inStream,
237  inCodec,
238  &image)) {
239  LOG4CPP_ERROR_S ((*mainCat)) << "Jpeg2000::load encountered error reading header";
240  opj_stream_destroy (inStream);
241  opj_destroy_codec (inCodec);
242  opj_image_destroy (image);
243  return false;
244  }
245 
246  // Get the decoded image
247  if (!(opj_decode (inCodec,
248  inStream,
249  image) &&
250  opj_end_decompress (inCodec,
251  inStream))) {
252  LOG4CPP_ERROR_S ((*mainCat)) << "Jpeg2000::load failed to decode image";
253  opj_destroy_codec (inCodec);
254  opj_stream_destroy (inStream);
255  opj_image_destroy (image);
256  return false;
257  }
258 
259  // Close the byte stream
260  opj_stream_destroy (inStream);
261 
262  applyImageTweaks (image);
263 
264  // Transform into ppm image in memory
265  bool success = true;
266  QBuffer buffer;
267  buffer.open (QBuffer::WriteOnly);
268  if (imagetopnm (image,
269  buffer)) {
270  LOG4CPP_ERROR_S ((*mainCat)) << "Jpeg2000::load failed to generate new image";
271  success = false;
272 
273  } else {
274 
275  // Intermediate file for debugging
276 // QFile file ("jpeg2000.ppm");
277 // file.open (QIODevice::WriteOnly);
278 // file.write (buffer.data());
279 // file.close ();
280 
281  // Create output
282  imageResult.loadFromData(buffer.data());
283 
284  }
285 
286  // Deallocate
287  if (inCodec) {
288  opj_destroy_codec (inCodec);
289  }
290  opj_image_destroy (image);
291 
292  return success;
293 }
294 
295 QStringList Jpeg2000::supportedFileExtensions () const
296 {
297  QStringList extensions;
298 
299  // Entries from openjpeg source code, and may not be correct. Order is unimportant since they are sorted later
300  extensions << "j2k" << "jp2" << "jpc" << "jpt";
301 
302  return extensions;
303 }
304 
306 {
307  QStringList extensions = supportedFileExtensions();
308  QStringList wildcards;
309 
310  QStringList::iterator itr;
311  for (itr = extensions.begin(); itr != extensions.end(); itr++) {
312  QString extension = *itr;
313  QString wildcard = QString ("*.%1").arg (extension);
314  wildcards << wildcard;
315  }
316 
317  return wildcards;
318 }
QStringList supportedImageWildcards() const
List the supported jpeg2000 file extensions, for filtering import files.
Definition: Jpeg2000.cpp:305
bool load(const QString &filename, QImage &image) const
Load image from jpeg2000 file.
Definition: Jpeg2000.cpp:192
Jpeg2000()
Single constructor.
Definition: Jpeg2000.cpp:21