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