Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qjp2handler.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2016 Petroules Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:critical reason:data-parser
5
7
8#include "qimage.h"
9#include "qvariant.h"
10#include "qcolor.h"
11#include "qimagereader.h"
12#include "qnumeric.h"
13
14#include <jasper/jasper.h>
15#include <math.h> // for pow
16#include <limits>
17
18QT_BEGIN_NAMESPACE
19
20class QJp2HandlerPrivate
21{
22 Q_DECLARE_PUBLIC(QJp2Handler)
23 Q_DISABLE_COPY(QJp2HandlerPrivate)
24public:
25 int writeQuality;
26 QByteArray subType;
27 QJp2Handler *q_ptr;
28 QJp2HandlerPrivate(QJp2Handler *q_ptr);
29};
30
32
33/*
34 \class Jpeg2000JasperReader
35 \brief Jpeg2000JasperReader implements reading and writing of JPEG 2000
36 image files.
37
38 \internal
39
40 This class is designed to be used together with the an QImageIO IOHandler,
41 and it should probably not be necessary to instantiate it directly.
42
43 Internally it used the Jasper library for coding the image data.
44*/
46{
47public:
48 Jpeg2000JasperReader(QIODevice *iod, const SubFormat format = Jp2Format);
49
51
52 bool read(QImage *pImage);
53 bool write(const QImage &image, int quality);
54private:
55 typedef void (Jpeg2000JasperReader::*ScanlineFunc)(jas_seqent_t** const, uchar*);
56 typedef void (Jpeg2000JasperReader::*ScanlineFuncWrite)(jas_matrix_t**, uchar*);
57
58 void copyJasperQt(ScanlineFunc scanlinecopier);
59 void copyJasperQtGeneric();
60 void copyScanlineJasperQtRGB(jas_seqent_t ** const jasperRow, uchar *qtScanLine);
61 void copyScanlineJasperQtRGBA(jas_seqent_t ** const jasperRow, uchar *qtScanLine);
62 void copyScanlineJasperQtGray(jas_seqent_t ** const jasperRow, uchar *qtScanLine);
63 void copyScanlineJasperQtGrayA(jas_seqent_t ** const jasperRow, uchar *qtScanLine);
64
65 void copyQtJasper(const ScanlineFuncWrite scanlinecopier);
66 void copyScanlineQtJasperRGB(jas_matrix_t ** jasperRow, uchar *qtScanLine);
67 void copyScanlineQtJasperRGBA(jas_matrix_t ** jasperRow, uchar *qtScanLine);
68 void copyScanlineQtJasperColormapRGB(jas_matrix_t ** jasperRow, uchar *qtScanLine);
69 void copyScanlineQtJasperColormapRGBA(jas_matrix_t ** jasperRow, uchar *qtScanLine);
70 void copyScanlineQtJasperColormapGrayscale(jas_matrix_t ** jasperRow, uchar *qtScanLine);
71 void copyScanlineQtJasperColormapGrayscaleA(jas_matrix_t ** jasperRow, uchar *qtScanLine);
72
73 bool attemptColorspaceChange(int wantedColorSpace);
74 bool createJasperMatrix(jas_matrix_t **&matrix);
75 bool freeJasperMatrix(jas_matrix_t **matrix);
76 void printColorSpaceError();
77 jas_image_cmptparm_t createComponentMetadata(const int width, const int height);
78 jas_image_t *newRGBAImage(const int width, const int height, bool alpha);
79 jas_image_t *newGrayscaleImage(const int width, const int height, bool alpha);
80 bool decodeColorSpace(int clrspc, QString &family, QString &specific);
81 void printMetadata(jas_image_t *image);
82
83 bool jasperOk;
84
85 QIODevice *ioDevice;
86 QImage qtImage;
87 SubFormat format;
88
89 // Qt image properties
90 int qtWidth;
91 int qtHeight;
92 int qtDepth;
93 int qtNumComponents;
94
95 jas_image_t *jasper_image;
96 // jasper image properties
97 int jasNumComponents;
98 int jasComponentPrecicion[4];
99 int computedComponentWidth ;
100 int computedComponentHeight;
101 int computedComponentHorizontalSubsampling;
102 int computedComponentVerticalSubsampling;
103 int jasperColorspaceFamily;
104 // maps color to component (ex: colorComponentMapping[RED]
105 // gives the component that contains the red color)
106 int colorComponentMapping[4];
107 bool hasAlpha;
108};
109
110QJp2HandlerPrivate::QJp2HandlerPrivate(QJp2Handler *q_ptr)
111 : writeQuality(100), subType("jp2"), q_ptr(q_ptr)
112{
113}
114
115/*!
116 \class QJp2Handler
117 \brief The QJp2Handler class provides support for reading and writing
118 JPEG 2000 image files with the Qt plugin system.
119 Currently, it only supports dynamically-loaded plugins.
120
121 JPEG files comes in two subtypes: the JPEG 2000 file format (\c .jp2) and
122 the JPEG 2000 code stream format (\c .j2k, \c .jpc, or \c .j2c).
123 QJp2Handler can read and write both types.
124
125 To select a subtype, use the setOption() function with the
126 QImageIOHandler::SubType option and a parameter value of "jp2" or "j2k".
127
128 To set the image quality when writing, you can use setOption() with the
129 QImageIOHandler::Quality option, or QImageWriter::setQuality() if your are
130 using a QImageWriter object to write the image. The image quality is
131 specified as an int in the range 0 to 100. 0 means maximum compression and
132 99 means minimum compression. 100 selects lossless encoding, and this is the
133 default value.
134
135 The JPEG handler is only available as a plugin,
136 and this enables you to use normal QImage and QPixmap functions to read and
137 write images. For example:
138
139 \code
140 myLabel->setPixmap(QPixmap("myimage.jp2"));
141
142 QPixmap myPixmap;
143 myPixmap.save("myimage.jp2", "JP2");
144 \endcode
145*/
146
147/*!
148 Constructs an instance of QJp2Handler.
149*/
151 : d_ptr(new QJp2HandlerPrivate(this))
152{
153}
154
155/*!
156 Destructor for QJp2Handler.
157*/
159{
160
161}
162
163/*!
164 Verifies if some values (magic bytes) are set as expected in the
165 header of the file. If the magic bytes were found, we assume that we
166 can read the file. The function will assume that the \a iod is
167 pointing to the beginning of the JPEG 2000 header. (i.e. it will for
168 instance not seek to the beginning of a file before reading).
169
170 If \a subType is not 0, it will contain "jp2" or "j2k" upon
171 successful return.
172*/
173bool QJp2Handler::canRead(QIODevice *iod, QByteArray *subType)
174{
175 bool bCanRead = false;
176 if (iod) {
177 const QByteArray header = iod->peek(12);
178 if (header.startsWith(QByteArrayLiteral("\000\000\000\fjP \r\n\207\n"))) {
179 // Jp2 is the JPEG 2000 file format
180 bCanRead = true;
181 if (subType)
182 *subType = QByteArray("jp2");
183 } else if (header.startsWith(QByteArrayLiteral("\377\117\377\121\000"))) {
184 // J2c is the JPEG 2000 code stream
185 bCanRead = true;
186 if (subType)
187 *subType = QByteArray("j2k");
188 }
189 }
190 return bCanRead;
191}
192
193/*! \reimp
194*/
195bool QJp2Handler::canRead() const
196{
197 QByteArray subType;
198 if (canRead(device(), &subType)) {
199 setFormat(subType);
200 return true;
201 }
202 return false;
203}
204
205/*! \reimp
206*/
207bool QJp2Handler::read(QImage *image)
208{
210 return reader.read(image);
211}
212
213/*! \reimp
214*/
215bool QJp2Handler::write(const QImage &image)
216{
217 Q_D(const QJp2Handler);
218 SubFormat subFormat;
219 if (d->subType == QByteArray("jp2"))
220 subFormat = Jp2Format;
221 else
222 subFormat = J2kFormat;
223
224 Jpeg2000JasperReader writer(device(), subFormat);
225 return writer.write(image, d->writeQuality);
226}
227
228/*!
229 Get the value associated with \a option.
230 \sa setOption()
231*/
232QVariant QJp2Handler::option(ImageOption option) const
233{
234 Q_D(const QJp2Handler);
235 if (option == Quality) {
236 return QVariant(d->writeQuality);
237 } else if (option == SubType) {
238 return QVariant(d->subType);
239 }
240 return QVariant();
241}
242
243/*!
244 The JPEG 2000 handler supports two options.
245 Set \a option to QImageIOHandler::Quality to balance between quality and the
246 compression level, where \a value should be an integer in the interval
247 [0-100]. 0 is maximum compression (low quality) and 100 is lossless
248 compression (high quality).
249
250 Set \a option to QImageIOHandler::Subtype to choose the subtype,
251 where \a value should be a string equal to either "jp2" or "j2k"
252 \sa option()
253*/
254void QJp2Handler::setOption(ImageOption option, const QVariant &value)
255{
256 Q_D(QJp2Handler);
257 if (option == Quality) {
258 bool ok;
259 const int quality = value.toInt(&ok);
260 if (ok)
261 d->writeQuality = quality;
262 } else if (option == SubType) {
263 const QByteArray subTypeCandidate = value.toByteArray();
264 // Test for default Jpeg2000 file format (jp2), or stream format (j2k).
265 if (subTypeCandidate == QByteArrayLiteral("jp2") ||
266 subTypeCandidate == QByteArrayLiteral("j2k"))
267 d->subType = subTypeCandidate;
268 }
269}
270
271/*!
272 This function will return true if \a option is set to either
273 QImageIOHandler::Quality or QImageIOHandler::Subtype.
274*/
275bool QJp2Handler::supportsOption(ImageOption option) const
276{
277 return (option == Quality || option == SubType);
278}
279
280/*!
281 Automatic resource handling for a jas_image_t*.
282*/
284{
285public:
286 // Take reference to the pointer here, because the pointer
287 // may change when we change color spaces.
288 ScopedJasperImage(jas_image_t *&image):image(image) { }
289 ~ScopedJasperImage() { jas_image_destroy(image); }
290private:
291 jas_image_t *&image;
292};
293
294/*! \internal
295 Construct a Jpeg2000JasperReader using the provided \a imageIO.
296 Note that currently the jasper library is initialized in this constructor,
297 (and freed in the destructor) which means that:
298 - Only one instance of this class may exist at one time
299 - No thread safety
300*/
302 : jasperOk(true), ioDevice(iod), format(format), hasAlpha(false)
303{
304#if JAS_VERSION_MAJOR < 3
305 if (jas_init()) {
306 jasperOk = false;
307 qDebug("Jasper Library initialization failed");
308 }
309#else
310 jas_conf_clear();
311#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
312 if (QImageReader::allocationLimit() > 0)
313 jas_conf_set_max_mem_usage(qsizetype(QImageReader::allocationLimit()) * 1024 * 1024);
314#else
315 // 128MB seems to be enough.
316 jas_conf_set_max_mem_usage(128 * 1024 * 1024);
317#endif
318 if (jas_init_library()) {
319 jasperOk = false;
320 qDebug("Jasper library initialization failed");
321 }
322 if (jas_init_thread()) {
323 jas_cleanup_library();
324 jasperOk = false;
325 qDebug("Jasper thread initialization failed");
326 }
327#endif
328}
329
331{
332#if JAS_VERSION_MAJOR < 3
333 if (jasperOk)
334 jas_cleanup();
335#else
336 if (jasperOk) {
337 if (jas_cleanup_thread()) {
338 qDebug("Jasper thread cleanup failed");
339 }
340 if (jas_cleanup_library()) {
341 qDebug("Jasper library cleanup failed");
342 }
343 }
344#endif
345}
346
347/*! \internal
348 Opens the file data and attempts to decode it using the Jasper library.
349 Returns true if successful, false on failure
350*/
351bool Jpeg2000JasperReader::read(QImage *pImage)
352{
353 if (!jasperOk)
354 return false;
355
356 const auto jasCoordInIntRange = [](jas_image_coord_t v) {
357 return v >= 1 && qint64(v) <= qint64(std::numeric_limits<int>::max());
358 };
359
360 /*
361 Reading proceeds approximately as follows:
362 1. Open stream and decode using Jasper
363 2. Get image metadata
364 3. Change colorspace if necessary
365 4. Create a QImage of the appropriate type (32-bit for RGB,
366 8-bit for grayscale)
367 5. Copy image data from Jasper to the QImage
368
369 When copying the image data from the Jasper data structures to the
370 QImage, a generic copy function (copyJasperQt) iterates through the
371 scanlines and calls the provided (via the scanlineCopier argument)
372 scanline copy function for each scanline. The scanline copy function
373 selected according to image metadata such as color space and the
374 presence of an alpha channel.
375 */
376 QByteArray fileContents = ioDevice->readAll();
377 jas_stream_t *imageData = jas_stream_memopen(fileContents.data(),
378 fileContents.size());
379 jasper_image = jas_image_decode(imageData, jas_image_getfmt(imageData), 0);
380 jas_stream_close(imageData);
381 if (!jasper_image) {
382 qDebug("Jasper library can't decode Jpeg2000 image data");
383 return false;
384 }
385 ScopedJasperImage scopedImage(jasper_image);
386 //printMetadata(jasper_image);
387
388 qtWidth = jas_image_width(jasper_image);
389 qtHeight = jas_image_height(jasper_image);
390 jasNumComponents = jas_image_numcmpts(jasper_image);
391 jasperColorspaceFamily = jas_clrspc_fam(jas_image_clrspc(jasper_image));
392
393 if (jasNumComponents < 1 || jasNumComponents > 4) {
394 qDebug("Unsupported number of components (%d) in JPEG 2000 image", jasNumComponents);
395 return false;
396 }
397
398 bool needColorspaceChange = false;
399 if (jasperColorspaceFamily != JAS_CLRSPC_FAM_RGB &&
400 jasperColorspaceFamily != JAS_CLRSPC_FAM_GRAY)
401 needColorspaceChange = true;
402
403 // Get per-component data
404 int c;
405 for (c = 0; c < jasNumComponents; ++c) {
406 jasComponentPrecicion[c] = jas_image_cmptprec(jasper_image, c);
407
408 // Test for precision
409 if (jasComponentPrecicion[c] > 8 || jasComponentPrecicion[c] < 8)
410 needColorspaceChange = true;
411
412 // Test for subsampling
413 if (jas_image_cmpthstep(jasper_image, c) != 1 ||
414 jas_image_cmptvstep(jasper_image, c) != 1)
415 needColorspaceChange = true;
416
417 // Test for signed components
418 if (jas_image_cmptsgnd(jasper_image, c) != 0)
419 needColorspaceChange = true;
420 }
421
422 /*
423 If we encounter a different color space than RGB
424 (such as XYZ or YCbCr) we change that to RGB.
425 Also, if any component has "funny" metadata (such as precicion != 8 bits
426 or subsampling != 1) we also do a colorspace
427 change in order to convert it to something we can load.
428 */
429
430 bool decodeOk = true;
431 if (needColorspaceChange)
432 decodeOk = attemptColorspaceChange(JAS_CLRSPC_SRGB);
433
434 if (!decodeOk) {
435 printColorSpaceError();
436 return false;
437 }
438
439 // Image metadata may have changed, get from Jasper.
440 const jas_image_coord_t imageWidth = jas_image_width(jasper_image);
441 const jas_image_coord_t imageHeight = jas_image_height(jasper_image);
442 if (!jasCoordInIntRange(imageWidth) || !jasCoordInIntRange(imageHeight)) {
443 qDebug("The Qt JPEG 2000 reader does not support image dimensions this large");
444 return false;
445 }
446 qtWidth = imageWidth;
447 qtHeight = imageHeight;
448 jasNumComponents = jas_image_numcmpts(jasper_image);
449 jasperColorspaceFamily = jas_clrspc_fam(jas_image_clrspc(jasper_image));
450 if (jasNumComponents < 1 || jasNumComponents > 4) {
451 qDebug("Unsupported number of components (%d) in JPEG 2000 image", jasNumComponents);
452 return false;
453 }
454 for (c = 0; c < jasNumComponents; ++c) {
455 jasComponentPrecicion[c] = jas_image_cmptprec(jasper_image, c);
456 }
457
458 if (jasperColorspaceFamily != JAS_CLRSPC_FAM_RGB &&
459 jasperColorspaceFamily != JAS_CLRSPC_FAM_GRAY) {
460 qDebug("The Qt JPEG 2000 reader was unable to convert colorspace to RGB or grayscale");
461 return false;
462 }
463
464 // If a component has a subsampling factor != 1, we can't trust
465 // jas_image_height/width, so we need to figure it out ourselves
466 bool oddComponentSubsampling = false;
467 for (c = 0; c < jasNumComponents; ++c) {
468 if (jas_image_cmpthstep(jasper_image, c) != 1 ||
469 jas_image_cmptvstep(jasper_image, c) != 1) {
470 oddComponentSubsampling = true;
471 }
472 }
473
474 if (oddComponentSubsampling) {
475 // Check if all components have the same vertical/horizontal dim and
476 // subsampling
477 const jas_image_coord_t cmptWidth = jas_image_cmptwidth(jasper_image, 0);
478 const jas_image_coord_t cmptHeight = jas_image_cmptheight(jasper_image, 0);
479 const jas_image_coord_t hstep = jas_image_cmpthstep(jasper_image, 0);
480 const jas_image_coord_t vstep = jas_image_cmptvstep(jasper_image, 0);
481 if (!jasCoordInIntRange(cmptWidth) || !jasCoordInIntRange(cmptHeight) ||
482 !jasCoordInIntRange(hstep) || !jasCoordInIntRange(vstep)) {
483 qDebug("The Qt JPEG 2000 reader does not support image dimensions this large");
484 return false;
485 }
486 computedComponentWidth = cmptWidth;
487 computedComponentHeight = cmptHeight;
488 computedComponentHorizontalSubsampling = hstep;
489 computedComponentVerticalSubsampling = vstep;
490
491 for (c = 1; c < jasNumComponents; ++c) {
492 if (computedComponentWidth != jas_image_cmptwidth(jasper_image, c) ||
493 computedComponentWidth != jas_image_cmptwidth(jasper_image, c) ||
494 computedComponentHorizontalSubsampling != jas_image_cmpthstep(jasper_image, c) ||
495 computedComponentVerticalSubsampling != jas_image_cmptvstep(jasper_image, c)) {
496 qDebug("The Qt JPEG 2000 reader does not support images where "
497 "component geometry differs from image geometry");
498 return false;
499 }
500 }
501 if (qMulOverflow(computedComponentWidth, computedComponentHorizontalSubsampling, &qtWidth) ||
502 qMulOverflow(computedComponentHeight, computedComponentVerticalSubsampling, &qtHeight)) {
503 qDebug("The Qt JPEG 2000 reader does not support image dimensions this large");
504 return false;
505 }
506 }
507
508 // Sanity check each component
509 for (c = 0; c < jasNumComponents; ++c) {
510 // Test for precision
511 if (jasComponentPrecicion[c]>8 || jasComponentPrecicion[c]<8) {
512 qDebug("The Qt JPEG 2000 reader does not support components with "
513 "precision != 8");
514 decodeOk = false;
515 }
516#if 0
517 // Test the subsampling factor (space between pixels on the image grid)
518 if (oddComponentSubsampling) {
519 qDebug("The Qt JPEG 2000 reader does not support components with "
520 "a subsampling factor != 1 (yet)");
521 decodeOk = false;
522 }
523#endif
524 // Test for signed components
525 if (jas_image_cmptsgnd(jasper_image, c) != 0) {
526 qDebug("Qt JPEG 2000 reader does not support signed components");
527 decodeOk = false;
528 }
529
530 // Test for component/image geomoetry mismach.
531 // If oddComponentSubsampling, then this is already taken care of above.
532 if (!oddComponentSubsampling)
533 if (jas_image_cmpttlx(jasper_image,c) != 0 ||
534 jas_image_cmpttly(jasper_image,c) != 0 ||
535 jas_image_cmptbrx(jasper_image,c) != jas_image_brx(jasper_image) ||
536 jas_image_cmptbry(jasper_image,c) != jas_image_bry(jasper_image) ||
537 jas_image_cmptwidth (jasper_image, c) != jas_image_width (jasper_image) ||
538 jas_image_cmptheight(jasper_image, c) != jas_image_height(jasper_image )) {
539 qDebug("The Qt JPEG 2000 reader does not support images where "
540 "component geometry differs from image geometry");
541 printMetadata(jasper_image);
542 decodeOk = false;
543 }
544 }
545 if (!decodeOk)
546 return false;
547
548 // At this point, the colorspace should be either RGB or grayscale,
549 // and each component should have eight bits of precision and
550 // no unsupported geometry.
551 //printMetadata(jasper_image);
552
553 // Get color components
554 jasperColorspaceFamily = jas_clrspc_fam(jas_image_clrspc(jasper_image));
555 if (jasperColorspaceFamily == JAS_CLRSPC_FAM_RGB) {
556 if (jasNumComponents > 4)
557 qDebug("JPEG 2000 reader expected 3 or 4 components, got %d",
558 jasNumComponents);
559
560 // Set up mapping from R,G,B -> component num.
561 colorComponentMapping[0] = jas_image_getcmptbytype(jasper_image,
562 JAS_IMAGE_CT_RGB_R);
563 colorComponentMapping[1] = jas_image_getcmptbytype(jasper_image,
564 JAS_IMAGE_CT_RGB_G);
565 colorComponentMapping[2] = jas_image_getcmptbytype(jasper_image,
566 JAS_IMAGE_CT_RGB_B);
567 qtNumComponents = 3;
568 } else if (jasperColorspaceFamily == JAS_CLRSPC_FAM_GRAY) {
569 if (jasNumComponents > 2)
570 qDebug("JPEG 2000 reader expected 1 or 2 components, got %d",
571 jasNumComponents);
572 colorComponentMapping[0] = jas_image_getcmptbytype(jasper_image,
573 JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_GRAY_Y));
574 qtNumComponents = 1;
575 } else {
576 printColorSpaceError();
577 return false;
578 }
579
580 // Get alpha component if one exists. Due to the lack of test images,
581 // loading images with alpha channels is a bit untested. It works
582 // with images saved with this implementation though.
583 const int posibleAlphaComponent1 = 3;
584 const int posibleAlphaComponent2 = 48;
585
586 if (jasNumComponents == qtNumComponents + 1) {
587 colorComponentMapping[qtNumComponents] = jas_image_getcmptbytype(jasper_image, posibleAlphaComponent1);
588 if (colorComponentMapping[qtNumComponents] < 0) {
589 colorComponentMapping[qtNumComponents] = jas_image_getcmptbytype(jasper_image, posibleAlphaComponent2);
590 }
591 if (colorComponentMapping[qtNumComponents] > 0) {
592 hasAlpha = true;
593 qtNumComponents++;
594 }
595 }
596
597 // Check for missing components
598 for (c = 0; c < qtNumComponents; ++c) {
599 if (colorComponentMapping[c] < 0) {
600 qDebug("JPEG 2000 reader missing a color component");
601 return false;
602 }
603 }
604
605 // Create a QImage of the correct type
606 QImage::Format qtFormat = QImage::Format_Invalid;
607 if (jasperColorspaceFamily == JAS_CLRSPC_FAM_RGB)
608 qtFormat = hasAlpha ? QImage::Format_ARGB32 : QImage::Format_RGB32;
609 else if (jasperColorspaceFamily == JAS_CLRSPC_FAM_GRAY)
610 qtFormat = hasAlpha ? QImage::Format_ARGB32 : QImage::Format_Grayscale8;
611 if (!QImageIOHandler::allocateImage(QSize(qtWidth, qtHeight), qtFormat, &qtImage))
612 return false;
613
614 // Copy data
615 if (oddComponentSubsampling) {
616 // This is a hack really, copying of data with component subsampling
617 // != 1 doesn't fit in with the rest of the scanline copying framework.
618 copyJasperQtGeneric();
619 } else if (jasperColorspaceFamily == JAS_CLRSPC_FAM_RGB) {
620 if (hasAlpha)
621 copyJasperQt(&Jpeg2000JasperReader::copyScanlineJasperQtRGBA);
622 else
623 copyJasperQt(&Jpeg2000JasperReader::copyScanlineJasperQtRGB);
624 } else if (jasperColorspaceFamily == JAS_CLRSPC_FAM_GRAY) {
625 if (hasAlpha)
626 copyJasperQt(&Jpeg2000JasperReader::copyScanlineJasperQtGrayA);
627 else
628 copyJasperQt(&Jpeg2000JasperReader::copyScanlineJasperQtGray);
629 }
630 if (decodeOk)
631 *pImage = qtImage;
632
633 return decodeOk;
634}
635
636/*!
637 \internal
638*/
639void Jpeg2000JasperReader::copyJasperQtGeneric()
640{
641 // Create scanline data poinetrs
642 jas_matrix_t **jasperMatrix;
643 jas_seqent_t **jasperRow;
644 createJasperMatrix(jasperMatrix);
645 jasperRow = (jas_seqent_t**)malloc(jasNumComponents * sizeof(jas_seqent_t *));
646 Q_CHECK_PTR(jasperRow);
647
648 int imageY = 0;
649 for (int componentY = 0; componentY < computedComponentHeight; ++componentY) {
650 for (int c = 0; c < jasNumComponents; ++c) {
651 jas_image_readcmpt(jasper_image, colorComponentMapping[c], 0,
652 componentY, computedComponentWidth, 1,
653 jasperMatrix[c]);
654 jasperRow[c] = jas_matrix_getref(jasperMatrix[c], 0, 0);
655 }
656 for (int verticalSubsample = 0;
657 verticalSubsample < computedComponentVerticalSubsampling;
658 ++verticalSubsample) {
659 uchar *scanLineUchar = qtImage.scanLine(imageY);
660 QRgb *scanLineQRgb = reinterpret_cast<QRgb *>(scanLineUchar);
661 for (int componentX = 0; componentX < computedComponentWidth;
662 ++componentX) {
663 for (int horizontalSubsample = 0;
664 horizontalSubsample <
665 computedComponentHorizontalSubsampling;
666 ++horizontalSubsample) {
667 if (jasperColorspaceFamily == JAS_CLRSPC_FAM_RGB) {
668 if (hasAlpha) {
669 *scanLineQRgb++ = (jasperRow[3][componentX] << 24) |
670 (jasperRow[0][componentX] << 16) |
671 (jasperRow[1][componentX] << 8) |
672 jasperRow[2][componentX];
673 } else {
674 *scanLineQRgb++ = (jasperRow[0][componentX] << 16) |
675 (jasperRow[1][componentX] << 8) |
676 jasperRow[2][componentX];
677 }
678 } else if (jasperColorspaceFamily == JAS_CLRSPC_FAM_GRAY) {
679 if (hasAlpha) {
680 *scanLineQRgb++ = (jasperRow[1][componentX] << 24) |
681 (jasperRow[0][componentX] << 16) |
682 (jasperRow[0][componentX] << 8) |
683 jasperRow[0][componentX];
684 } else {
685 *scanLineUchar++ = jasperRow[0][componentX];
686 }
687 }
688 }
689 }
690 ++imageY;
691 }
692 }
693}
694
695/*!
696 \internal
697 Copies data from Jasper to QImage. The scanlineCopier parameter specifies
698 which function to use for handling each scan line.
699*/
700void Jpeg2000JasperReader::copyJasperQt(const ScanlineFunc scanlineCopier)
701{
702 // Create scanline data poinetrs
703 jas_matrix_t **jasperMatrix;
704 jas_seqent_t **jasperRow;
705
706 createJasperMatrix(jasperMatrix);
707 jasperRow = (jas_seqent_t**)malloc(jasNumComponents * sizeof(jas_seqent_t *));
708 Q_CHECK_PTR(jasperRow);
709
710 for (int scanline = 0; scanline < qtHeight; ++scanline) {
711 for (int c = 0; c < jasNumComponents; ++c) {
712 jas_image_readcmpt(jasper_image, colorComponentMapping[c], 0,
713 scanline, qtWidth, 1, jasperMatrix[c]);
714 jasperRow[c] = jas_matrix_getref(jasperMatrix[c], 0, 0);
715 }
716 (this->*scanlineCopier)(jasperRow, qtImage.scanLine(scanline));
717 }
718
719 freeJasperMatrix(jasperMatrix);
720 free(jasperRow);
721}
722
723/*!
724 \internal
725 Copies RGB data from Jasper to a 32-bit QImage.
726*/
727void Jpeg2000JasperReader::copyScanlineJasperQtRGB(
728 jas_seqent_t ** const jasperRow, uchar *qtScanLine)
729{
730 QRgb *scanLine = reinterpret_cast<QRgb *>(qtScanLine);
731 for (int c = 0; c < qtWidth; ++c) {
732 *scanLine++ = (0xFF << 24) |
733 (jasperRow[0][c] << 16) |
734 (jasperRow[1][c] << 8) |
735 jasperRow[2][c];
736 }
737}
738
739/*!
740 \internal
741 Copies RGBA data from Jasper to a 32-bit QImage.
742*/
743void Jpeg2000JasperReader::copyScanlineJasperQtRGBA(jas_seqent_t ** const jasperRow, uchar *qtScanLine)
744{
745 QRgb *scanLine = reinterpret_cast<QRgb *>(qtScanLine);
746 for (int c = 0; c < qtWidth; ++c) {
747 *scanLine++ = (jasperRow[3][c] << 24) |
748 (jasperRow[0][c] << 16) |
749 (jasperRow[1][c] << 8) |
750 jasperRow[2][c];
751 }
752}
753
754/*!
755 \internal
756 Copies data from a grayscale image to an 8-bit QImage.
757*/
758void Jpeg2000JasperReader::copyScanlineJasperQtGray(jas_seqent_t ** const jasperRow, uchar *qtScanLine)
759{
760 for (int c = 0; c < qtWidth; ++c) {
761 // *qtScanLine++ = (jasperRow[0][c] >> (jasComponentPrecicion[0] - 8));
762 *qtScanLine++ = jasperRow[0][c];
763 }
764}
765
766/*!
767 \internal
768 Copies data from a grayscale image to a 32-bit QImage.
769 Note that in this case we use an 32-bit image for grayscale data, since the
770 alpha value is per-pixel, not per-color (per-color alpha is supported by
771 8-bit QImage).
772*/
773void Jpeg2000JasperReader::copyScanlineJasperQtGrayA(jas_seqent_t ** const jasperRow, uchar *qtScanLine)
774{
775 QRgb *scanLine = reinterpret_cast<QRgb *>(qtScanLine);
776 for (int c = 0; c < qtWidth; ++c) {
777 *scanLine++ = (jasperRow[1][c] << 24) |
778 (jasperRow[0][c] << 16) |
779 (jasperRow[0][c] << 8) |
780 jasperRow[0][c];
781 }
782}
783
784/*!
785 Opens the file data and attempts to decode it using the Jasper library.
786 Returns true on success, false on failure.
787
788 32-bit and color mapped color images are encoded as RGB images,
789 color mapped grayscale images are encoded as grayscale images
790*/
791bool Jpeg2000JasperReader::write(const QImage &image, int quality)
792{
793 if (!jasperOk)
794 return false;
795
796 qtImage = image;
797
798 qtHeight = qtImage.height();
799 qtWidth = qtImage.width();
800 qtDepth = qtImage.depth();
801
802 if (qtDepth == 32) { // RGB(A)
803 jasper_image = newRGBAImage(qtWidth, qtHeight, qtImage.hasAlphaChannel());
804 if (!jasper_image)
805 return false;
806
807 if (qtImage.hasAlphaChannel())
808 copyQtJasper(&Jpeg2000JasperReader::copyScanlineQtJasperRGBA);
809 else
810 copyQtJasper(&Jpeg2000JasperReader::copyScanlineQtJasperRGB);
811 } else if (qtDepth == 8) {
812 // Color mapped grayscale
813 if (qtImage.allGray()) {
814 jasper_image = newGrayscaleImage(qtWidth, qtHeight, qtImage.hasAlphaChannel());
815 if (!jasper_image)
816 return false;
817
818 if (qtImage.hasAlphaChannel())
819 copyQtJasper(&Jpeg2000JasperReader::copyScanlineQtJasperColormapGrayscaleA);
820 else
821 copyQtJasper(&Jpeg2000JasperReader::copyScanlineQtJasperColormapGrayscale);
822 } else {
823 // Color mapped color
824 jasper_image = newRGBAImage(qtWidth, qtHeight, qtImage.hasAlphaChannel());
825 if (!jasper_image)
826 return false;
827
828 if (qtImage.hasAlphaChannel())
829 copyQtJasper(&Jpeg2000JasperReader::copyScanlineQtJasperColormapRGBA);
830 else
831 copyQtJasper(&Jpeg2000JasperReader::copyScanlineQtJasperColormapRGB);
832 }
833 } else {
834 qDebug("Unable to handle color depth %d", qtDepth);
835 return false;
836 }
837
838 int fmtid;
839 if (format == Jp2Format)
840 fmtid = jas_image_strtofmt(const_cast<char*>("jp2"));
841 else /* if (format == J2cFormat) */
842 // JasPer refers to the code stream format as jpc
843 fmtid = jas_image_strtofmt(const_cast<char*>("jpc"));
844
845 const int minQuality = 0;
846 const int maxQuality = 100;
847
848 if (quality == -1)
849 quality = 100;
850 if (quality <= minQuality)
851 quality = minQuality;
852 if (quality > maxQuality)
853 quality = maxQuality;
854
855 // Qt specifies quality as an integer in the range 0..100. Jasper specifies
856 // compression rate as an real in the range 0..1, where 1 corresponds to no
857 // compression. Computing the rate from quality is difficult, large images
858 // get better image quality than small images at the same rate. If the rate
859 // is too low, Jasper will generate a completely black image.
860 // minirate is the smallest safe rate value.
861 const double minRate = 0.001;
862
863 // maxRate specifies maximum target rate, which give the minimum amount
864 // of compression. Tests show that maxRates higer than 0.3 give no
865 // additional image quality for most images. Large images could use an even
866 // smaller maxRate value.
867 const double maxRate = 0.3;
868
869 // Set jasperRate to a value in the range minRate..maxRate. Distribute the
870 // quality steps more densely at the lower end if the rate scale.
871 const double jasperRate = minRate + pow((double(quality) / double(maxQuality)), 2) * maxRate;
872
873 // The Jasper format string contains two options:
874 // rate: rate=x
875 // lossy/lossless compression : mode=real/mode=int
876 QString jasperFormatString;
877
878 // If quality is not maxQuality, we set lossy encoding.
879 // (lossless is default)
880 if (quality != maxQuality) {
881 jasperFormatString += QLatin1String("mode=real");
882 jasperFormatString += QString(QLatin1String(" rate=%1")).arg(jasperRate);
883 }
884
885 // Open an empty jasper stream that grows automatically
886 jas_stream_t * memory_stream = jas_stream_memopen(0, 0);
887
888 // Jasper wants a non-const string.
889 char *str = qstrdup(jasperFormatString.toLatin1().constData());
890 jas_image_encode(jasper_image, memory_stream, fmtid, str);
891 delete[] str;
892 jas_stream_flush(memory_stream);
893
894 // jas_stream_t::obj_ is a void* which points to the stream implementation,
895 // e.g a file stream or a memory stream. But in our case we know that it is
896 // a memory stream since we created the object, so we just reiterpret_cast
897 // here..
898 char *buffer = reinterpret_cast<char *>(reinterpret_cast<jas_stream_memobj_t*>(memory_stream->obj_)->buf_);
899 qint64 length = jas_stream_length(memory_stream);
900 ioDevice->write(buffer, length);
901
902 jas_stream_close(memory_stream);
903 jas_image_destroy(jasper_image);
904
905 return true;
906}
907
908/*!
909 \internal
910 Copies data from qtImage to JasPer. The scanlineCopier parameter specifies
911 which function to use for handling each scan line.
912*/
913void Jpeg2000JasperReader::copyQtJasper(const ScanlineFuncWrite scanlinecopier)
914{
915 // Create jasper matrix for holding one scanline
916 jas_matrix_t **jasperMatrix;
917 createJasperMatrix(jasperMatrix);
918
919 for (int scanline = 0; scanline < qtHeight; ++scanline) {
920 (this->*scanlinecopier)(jasperMatrix, qtImage.scanLine(scanline));
921
922 // Write a scanline of data to jasper_image
923 for (int c = 0; c < jasNumComponents; ++c)
924 jas_image_writecmpt(jasper_image, c, 0, scanline, qtWidth, 1,
925 jasperMatrix[c]);
926 }
927 freeJasperMatrix(jasperMatrix);
928}
929
930/*!
931 \internal
932*/
933void Jpeg2000JasperReader::copyScanlineQtJasperRGB(jas_matrix_t ** jasperRow,
934 uchar *qtScanLine)
935{
936 QRgb *scanLineBuffer = reinterpret_cast<QRgb *>(qtScanLine);
937 for (int col = 0; col < qtWidth; ++col) {
938 jas_matrix_set(jasperRow[0], 0, col, (*scanLineBuffer & 0xFF0000) >> 16);
939 jas_matrix_set(jasperRow[1], 0, col, (*scanLineBuffer & 0x00FF00) >> 8);
940 jas_matrix_set(jasperRow[2], 0, col, *scanLineBuffer & 0x0000FF);
941 ++scanLineBuffer;
942 }
943}
944
945/*!
946 \internal
947*/
948void Jpeg2000JasperReader::copyScanlineQtJasperRGBA(jas_matrix_t ** jasperRow,
949 uchar *qtScanLine)
950{
951 QRgb *scanLineBuffer = reinterpret_cast<QRgb *>(qtScanLine);
952 for (int col = 0; col < qtWidth; ++col) {
953 jas_matrix_set(jasperRow[3], 0, col, (*scanLineBuffer & 0xFF000000) >> 24);
954 jas_matrix_set(jasperRow[0], 0, col, (*scanLineBuffer & 0x00FF0000) >> 16);
955 jas_matrix_set(jasperRow[1], 0, col, (*scanLineBuffer & 0x0000FF00) >> 8);
956 jas_matrix_set(jasperRow[2], 0, col, *scanLineBuffer & 0x000000FF);
957 ++scanLineBuffer;
958 }
959}
960
961/*!
962 \internal
963*/
964void Jpeg2000JasperReader::copyScanlineQtJasperColormapRGB(jas_matrix_t ** jasperRow,
965 uchar *qtScanLine)
966{
967 for (int col = 0; col < qtWidth; ++col) {
968 QRgb color = qtImage.color(*qtScanLine);
969 jas_matrix_set(jasperRow[0], 0, col, qRed(color));
970 jas_matrix_set(jasperRow[1], 0, col, qGreen(color));
971 jas_matrix_set(jasperRow[2], 0, col, qBlue(color));
972 ++qtScanLine;
973 }
974}
975
976/*!
977 \internal
978*/
979void Jpeg2000JasperReader::copyScanlineQtJasperColormapRGBA(jas_matrix_t ** jasperRow,
980 uchar *qtScanLine)
981{
982 for (int col = 0; col < qtWidth; ++col) {
983 QRgb color = qtImage.color(*qtScanLine);
984 jas_matrix_set(jasperRow[0], 0, col, qRed(color));
985 jas_matrix_set(jasperRow[1], 0, col, qGreen(color));
986 jas_matrix_set(jasperRow[2], 0, col, qBlue(color));
987 jas_matrix_set(jasperRow[3], 0, col, qAlpha(color));
988 ++qtScanLine;
989 }
990}
991
992/*!
993 \internal
994*/
995void Jpeg2000JasperReader::copyScanlineQtJasperColormapGrayscale(jas_matrix_t ** jasperRow,
996 uchar *qtScanLine)
997{
998 for (int col = 0; col < qtWidth; ++col) {
999 QRgb color = qtImage.color(*qtScanLine);
1000 jas_matrix_set(jasperRow[0], 0, col, qGray(color));
1001 ++qtScanLine;
1002 }
1003}
1004
1005/*!
1006 \internal
1007*/
1008void Jpeg2000JasperReader::copyScanlineQtJasperColormapGrayscaleA(jas_matrix_t ** jasperRow,
1009 uchar *qtScanLine)
1010{
1011 for (int col = 0; col < qtWidth; ++col) {
1012 QRgb color = qtImage.color(*qtScanLine);
1013 jas_matrix_set(jasperRow[0], 0, col, qGray(color));
1014 jas_matrix_set(jasperRow[1], 0, col, qAlpha(color));
1015 ++qtScanLine;
1016 }
1017}
1018
1019/*!
1020 \internal
1021 Attempts to change the color space for the image to wantedColorSpace using
1022 the JasPer library
1023*/
1024bool Jpeg2000JasperReader::attemptColorspaceChange(int wantedColorSpace)
1025{
1026 //qDebug("Attemting color space change");
1027 jas_cmprof_t *outprof;
1028 if (!(outprof = jas_cmprof_createfromclrspc(wantedColorSpace)))
1029 return false;
1030
1031 jas_image_t *newimage;
1032 if (!(newimage = jas_image_chclrspc(jasper_image, outprof,
1033 JAS_CMXFORM_INTENT_PER))) {
1034 jas_cmprof_destroy(outprof);
1035 return false;
1036 }
1037 jas_image_destroy(jasper_image);
1038 jas_cmprof_destroy(outprof);
1039 jasper_image = newimage;
1040 return true;
1041}
1042
1043/*!
1044 \internal
1045 Set up a component with parameters suitable for storing a QImage.
1046*/
1047jas_image_cmptparm_t Jpeg2000JasperReader::createComponentMetadata(
1048 const int width, const int height)
1049{
1050 jas_image_cmptparm_t param;
1051 param.tlx = 0;
1052 param.tly = 0;
1053 param.hstep = 1;
1054 param.vstep = 1;
1055 param.width = width;
1056 param.height = height;
1057 param.prec = 8;
1058 param.sgnd = 0;
1059 return param;
1060}
1061
1062/*!
1063 \internal
1064 Create a new RGB JasPer image with a possible alpha channel.
1065*/
1066jas_image_t* Jpeg2000JasperReader::newRGBAImage(const int width,
1067 const int height, bool alpha)
1068{
1069 jasNumComponents = alpha ? 4 : 3;
1070 jas_image_cmptparm_t *params = new jas_image_cmptparm_t[jasNumComponents];
1071 jas_image_cmptparm_t param = createComponentMetadata(width, height);
1072 for (int c=0; c < jasNumComponents; c++)
1073 params[c] = param;
1074 jas_image_t *newImage = jas_image_create(jasNumComponents, params,
1075 JAS_CLRSPC_SRGB);
1076
1077 if (!newImage) {
1078 delete[] params;
1079 return 0;
1080 }
1081
1082 jas_image_setcmpttype(newImage, 0, JAS_IMAGE_CT_RGB_R);
1083 jas_image_setcmpttype(newImage, 1, JAS_IMAGE_CT_RGB_G);
1084 jas_image_setcmpttype(newImage, 2, JAS_IMAGE_CT_RGB_B);
1085
1086 /*
1087 It is unclear how one stores opacity(alpha) components with JasPer,
1088 the following seems to have no effect. The opacity component gets
1089 type id 3 or 48 depending jp2 or j2c format no matter what one puts
1090 in here.
1091
1092 The symbols are defined as follows:
1093 #define JAS_IMAGE_CT_RGB_R 0
1094 #define JAS_IMAGE_CT_RGB_G 1
1095 #define JAS_IMAGE_CT_RGB_B 2
1096 #define JAS_IMAGE_CT_OPACITY 0x7FFF
1097 */
1098 if (alpha)
1099 jas_image_setcmpttype(newImage, 3, JAS_IMAGE_CT_OPACITY);
1100 delete[] params;
1101 return newImage;
1102}
1103
1104/*!
1105 \internal
1106 Create a new RGB JasPer image with a possible alpha channel.
1107*/
1108jas_image_t *Jpeg2000JasperReader::newGrayscaleImage(const int width,
1109 const int height,
1110 bool alpha)
1111{
1112 jasNumComponents = alpha ? 2 : 1;
1113 jas_image_cmptparm_t param = createComponentMetadata(width, height);
1114 jas_image_t *newImage = jas_image_create(1, &param, JAS_CLRSPC_SGRAY);
1115 if (!newImage)
1116 return 0;
1117
1118 jas_image_setcmpttype(newImage, 0, JAS_IMAGE_CT_GRAY_Y);
1119
1120 // See corresponding comment for newRGBAImage.
1121 if (alpha)
1122 jas_image_setcmpttype(newImage, 1, JAS_IMAGE_CT_OPACITY);
1123 return newImage;
1124}
1125
1126/*!
1127 \internal
1128 Allocate data structures that hold image data during transfer from the
1129 JasPer data structures to QImage.
1130*/
1131bool Jpeg2000JasperReader::createJasperMatrix(jas_matrix_t **&matrix)
1132{
1133 matrix = (jas_matrix_t**)malloc(jasNumComponents * sizeof(jas_matrix_t *));
1134 for (int c = 0; c < jasNumComponents; ++c)
1135 matrix[c] = jas_matrix_create(1, qtWidth);
1136 return true;
1137}
1138
1139/*!
1140 \internal
1141 Free data structures that hold image data during transfer from the
1142 JasPer data structures to QImage.
1143*/
1144bool Jpeg2000JasperReader::freeJasperMatrix(jas_matrix_t **matrix)
1145{
1146 for (int c = 0; c < jasNumComponents; ++c)
1147 jas_matrix_destroy(matrix[c]);
1148 free(matrix);
1149 return false;
1150}
1151
1152/*!
1153 \internal
1154*/
1155void Jpeg2000JasperReader::printColorSpaceError()
1156{
1157 QString colorspaceFamily, colorspaceSpecific;
1158 decodeColorSpace(jas_image_clrspc(jasper_image), colorspaceFamily,
1159 colorspaceSpecific);
1160 qDebug("Jpeg2000 decoder is not able to handle color space %s - %s",
1161 qPrintable(colorspaceFamily), qPrintable(colorspaceSpecific));
1162}
1163/*!
1164 \internal
1165*/
1166bool Jpeg2000JasperReader::decodeColorSpace(int clrspc, QString &family,
1167 QString &specific)
1168{
1169 int fam = jas_clrspc_fam(clrspc);
1170 int mbr = jas_clrspc_mbr(clrspc);
1171
1172 switch (fam) {
1173 case 0: family = QLatin1String("JAS_CLRSPC_FAM_UNKNOWN"); break;
1174 case 1: family = QLatin1String("JAS_CLRSPC_FAM_XYZ"); break;
1175 case 2: family = QLatin1String("JAS_CLRSPC_FAM_LAB"); break;
1176 case 3: family = QLatin1String("JAS_CLRSPC_FAM_GRAY"); break;
1177 case 4: family = QLatin1String("JAS_CLRSPC_FAM_RGB"); break;
1178 case 5: family = QLatin1String("JAS_CLRSPC_FAM_YCBCR"); break;
1179 default: family = QLatin1String("Unknown"); return false;
1180 }
1181
1182 switch (mbr) {
1183 case 0:
1184 switch (fam) {
1185 case 1: specific = QLatin1String("JAS_CLRSPC_CIEXYZ"); break;
1186 case 2: specific = QLatin1String("JAS_CLRSPC_CIELAB"); break;
1187 case 3: specific = QLatin1String("JAS_CLRSPC_SGRAY"); break;
1188 case 4: specific = QLatin1String("JAS_CLRSPC_SRGB"); break;
1189 case 5: specific = QLatin1String("JAS_CLRSPC_SYCBCR"); break;
1190 default: specific = QLatin1String("Unknown"); return false;
1191 }
1192 break;
1193 case 1:
1194 switch (fam) {
1195 case 3: specific = QLatin1String("JAS_CLRSPC_GENGRAY"); break;
1196 case 4: specific = QLatin1String("JAS_CLRSPC_GENRGB"); break;
1197 case 5: specific = QLatin1String("JAS_CLRSPC_GENYCBCR"); break;
1198 default: specific = QLatin1String("Unknown"); return false;
1199 }
1200 break;
1201 default:
1202 return false;
1203 }
1204 return true;
1205}
1206/*!
1207 \internal
1208*/
1209void Jpeg2000JasperReader::printMetadata(jas_image_t *image)
1210{
1211#ifndef QT_NO_DEBUG
1212 // jas_image_cmptparm_t param
1213 qDebug("Image width: %ld", long(jas_image_width(image)));
1214 qDebug("Image height: %ld", long(jas_image_height(image)));
1215 qDebug("Coordinates on reference grid: (%ld,%ld) (%ld,%ld)",
1216 long(jas_image_tlx(image)), long(jas_image_tly(image)),
1217 long(jas_image_brx(image)), long(jas_image_bry(image)));
1218 qDebug("Number of image components: %d", jas_image_numcmpts(image));
1219
1220 QString colorspaceFamily;
1221 QString colorspaceSpecific;
1222 decodeColorSpace(jas_image_clrspc(image), colorspaceFamily, colorspaceSpecific);
1223 qDebug("Color model (space): %d, %s - %s", jas_image_clrspc(image),
1224 qPrintable(colorspaceFamily), qPrintable(colorspaceSpecific));
1225
1226 qDebug("Component metadata:");
1227
1228 for (int c = 0; c < static_cast<int>(jas_image_numcmpts(image)); ++c) {
1229 qDebug("Component %d:", c);
1230 qDebug(" Component type: %ld", long(jas_image_cmpttype(image, c)));
1231 qDebug(" Width: %ld", long(jas_image_cmptwidth(image, c)));
1232 qDebug(" Height: %ld", long(jas_image_cmptheight(image, c)));
1233 qDebug(" Signedness: %d", jas_image_cmptsgnd(image, c));
1234 qDebug(" Precision: %d", jas_image_cmptprec(image, c));
1235 qDebug(" Horizontal subsampling factor: %ld",long(jas_image_cmpthstep(image, c)));
1236 qDebug(" Vertical subsampling factor: %ld", long(jas_image_cmptvstep(image, c)));
1237 qDebug(" Coordinates on reference grid: (%ld,%ld) (%ld,%ld)",
1238 long(jas_image_cmpttlx(image, c)), long(jas_image_cmpttly(image, c)),
1239 long(jas_image_cmptbrx(image, c)), long(jas_image_cmptbry(image, c)));
1240 }
1241#else
1242 Q_UNUSED(image);
1243#endif
1244}
1245
1246QT_END_NAMESPACE
bool read(QImage *pImage)
Jpeg2000JasperReader(QIODevice *iod, const SubFormat format=Jp2Format)
bool write(const QImage &image, int quality)
Opens the file data and attempts to decode it using the Jasper library.
QIODevice * device() const
Returns the device currently assigned to QImageReader, or \nullptr if no device has been assigned.
The QJp2Handler class provides support for reading and writing JPEG 2000 image files with the Qt plug...
~QJp2Handler()
Destructor for QJp2Handler.
QJp2Handler()
Constructs an instance of QJp2Handler.
static bool canRead(QIODevice *iod, QByteArray *subType)
Verifies if some values (magic bytes) are set as expected in the header of the file.
bool write(const QImage &image) override
\reimp
bool read(QImage *image) override
\reimp
bool canRead() const override
\reimp
Automatic resource handling for a jas_image_t*.
ScopedJasperImage(jas_image_t *&image)
SubFormat
@ Jp2Format
@ J2kFormat