Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
5#include "qjp2handler_p.h"
6
7#include "qimage.h"
8#include "qvariant.h"
9#include "qcolor.h"
10#include "qimagereader.h"
11
12#include <jasper/jasper.h>
13#include <math.h> // for pow
14
16
27
29
30/*
31 \class Jpeg2000JasperReader
32 \brief Jpeg2000JasperReader implements reading and writing of JPEG 2000
33 image files.
34
35 \internal
36
37 This class is designed to be used together with the an QImageIO IOHandler,
38 and it should probably not be necessary to instantiate it directly.
39
40 Internally it used the Jasper library for coding the image data.
41*/
43{
44public:
46
48
49 bool read(QImage *pImage);
50 bool write(const QImage &image, int quality);
51private:
52 typedef void (Jpeg2000JasperReader::*ScanlineFunc)(jas_seqent_t** const, uchar*);
53 typedef void (Jpeg2000JasperReader::*ScanlineFuncWrite)(jas_matrix_t**, uchar*);
54
55 void copyJasperQt(ScanlineFunc scanlinecopier);
56 void copyJasperQtGeneric();
57 void copyScanlineJasperQtRGB(jas_seqent_t ** const jasperRow, uchar *qtScanLine);
58 void copyScanlineJasperQtRGBA(jas_seqent_t ** const jasperRow, uchar *qtScanLine);
59 void copyScanlineJasperQtGray(jas_seqent_t ** const jasperRow, uchar *qtScanLine);
60 void copyScanlineJasperQtGrayA(jas_seqent_t ** const jasperRow, uchar *qtScanLine);
61
62 void copyQtJasper(const ScanlineFuncWrite scanlinecopier);
63 void copyScanlineQtJasperRGB(jas_matrix_t ** jasperRow, uchar *qtScanLine);
64 void copyScanlineQtJasperRGBA(jas_matrix_t ** jasperRow, uchar *qtScanLine);
65 void copyScanlineQtJasperColormapRGB(jas_matrix_t ** jasperRow, uchar *qtScanLine);
66 void copyScanlineQtJasperColormapRGBA(jas_matrix_t ** jasperRow, uchar *qtScanLine);
67 void copyScanlineQtJasperColormapGrayscale(jas_matrix_t ** jasperRow, uchar *qtScanLine);
68 void copyScanlineQtJasperColormapGrayscaleA(jas_matrix_t ** jasperRow, uchar *qtScanLine);
69
70 bool attemptColorspaceChange(int wantedColorSpace);
71 bool createJasperMatrix(jas_matrix_t **&matrix);
72 bool freeJasperMatrix(jas_matrix_t **matrix);
73 void printColorSpaceError();
74 jas_image_cmptparm_t createComponentMetadata(const int width, const int height);
75 jas_image_t *newRGBAImage(const int width, const int height, bool alpha);
76 jas_image_t *newGrayscaleImage(const int width, const int height, bool alpha);
77 bool decodeColorSpace(int clrspc, QString &family, QString &specific);
78 void printMetadata(jas_image_t *image);
79
80 bool jasperOk;
81
82 QIODevice *ioDevice;
83 QImage qtImage;
85
86 // Qt image properties
87 int qtWidth;
88 int qtHeight;
89 int qtDepth;
90 int qtNumComponents;
91
92 jas_image_t *jasper_image;
93 // jasper image properties
94 int jasNumComponents;
95 int jasComponentPrecicion[4];
96 int computedComponentWidth ;
97 int computedComponentHeight;
98 int computedComponentHorizontalSubsampling;
99 int computedComponentVerticalSubsampling;
100 int jasperColorspaceFamily;
101 // maps color to component (ex: colorComponentMapping[RED]
102 // gives the component that contains the red color)
103 int colorComponentMapping[4];
104 bool hasAlpha;
105};
106
108 : writeQuality(100), subType("jp2"), q_ptr(q_ptr)
109{
110}
111
151
159
171{
172 bool bCanRead = false;
173 if (iod) {
174 const QByteArray header = iod->peek(12);
175 if (header.startsWith(QByteArrayLiteral("\000\000\000\fjP \r\n\207\n"))) {
176 // Jp2 is the JPEG 2000 file format
177 bCanRead = true;
178 if (subType)
179 *subType = QByteArray("jp2");
180 } else if (header.startsWith(QByteArrayLiteral("\377\117\377\121\000"))) {
181 // J2c is the JPEG 2000 code stream
182 bCanRead = true;
183 if (subType)
184 *subType = QByteArray("j2k");
185 }
186 }
187 return bCanRead;
188}
189
193{
194 QByteArray subType;
195 if (canRead(device(), &subType)) {
196 setFormat(subType);
197 return true;
198 }
199 return false;
200}
201
205{
207 return reader.read(image);
208}
209
213{
214 Q_D(const QJp2Handler);
215 SubFormat subFormat;
216 if (d->subType == QByteArray("jp2"))
217 subFormat = Jp2Format;
218 else
219 subFormat = J2kFormat;
220
221 Jpeg2000JasperReader writer(device(), subFormat);
222 return writer.write(image, d->writeQuality);
223}
224
230{
231 Q_D(const QJp2Handler);
232 if (option == Quality) {
233 return QVariant(d->writeQuality);
234 } else if (option == SubType) {
235 return QVariant(d->subType);
236 }
237 return QVariant();
238}
239
252{
253 Q_D(QJp2Handler);
254 if (option == Quality) {
255 bool ok;
256 const int quality = value.toInt(&ok);
257 if (ok)
258 d->writeQuality = quality;
259 } else if (option == SubType) {
260 const QByteArray subTypeCandidate = value.toByteArray();
261 // Test for default Jpeg2000 file format (jp2), or stream format (j2k).
262 if (subTypeCandidate == QByteArrayLiteral("jp2") ||
263 subTypeCandidate == QByteArrayLiteral("j2k"))
264 d->subType = subTypeCandidate;
265 }
266}
267
273{
274 return (option == Quality || option == SubType);
275}
276
281{
282public:
283 // Take reference to the pointer here, because the pointer
284 // may change when we change color spaces.
285 ScopedJasperImage(jas_image_t *&image):image(image) { }
286 ~ScopedJasperImage() { jas_image_destroy(image); }
287private:
288 jas_image_t *&image;
289};
290
299 : jasperOk(true), ioDevice(iod), format(format), hasAlpha(false)
300{
301#if JAS_VERSION_MAJOR < 3
302 if (jas_init()) {
303 jasperOk = false;
304 qDebug("Jasper Library initialization failed");
305 }
306#else
307 jas_conf_clear();
308#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
310 jas_conf_set_max_mem_usage(qsizetype(QImageReader::allocationLimit()) * 1024 * 1024);
311#else
312 // 128MB seems to be enough.
313 jas_conf_set_max_mem_usage(128 * 1024 * 1024);
314#endif
315 if (jas_init_library()) {
316 jasperOk = false;
317 qDebug("Jasper library initialization failed");
318 }
319 if (jas_init_thread()) {
320 jas_cleanup_library();
321 jasperOk = false;
322 qDebug("Jasper thread initialization failed");
323 }
324#endif
325}
326
328{
329#if JAS_VERSION_MAJOR < 3
330 if (jasperOk)
331 jas_cleanup();
332#else
333 if (jasperOk) {
334 if (jas_cleanup_thread()) {
335 qDebug("Jasper thread cleanup failed");
336 }
337 if (jas_cleanup_library()) {
338 qDebug("Jasper library cleanup failed");
339 }
340 }
341#endif
342}
343
349{
350 if (!jasperOk)
351 return false;
352
353 /*
354 Reading proceeds approximately as follows:
355 1. Open stream and decode using Jasper
356 2. Get image metadata
357 3. Change colorspace if necessary
358 4. Create a QImage of the appropriate type (32-bit for RGB,
359 8-bit for grayscale)
360 5. Copy image data from Jasper to the QImage
361
362 When copying the image data from the Jasper data structures to the
363 QImage, a generic copy function (copyJasperQt) iterates through the
364 scanlines and calls the provided (via the scanlineCopier argument)
365 scanline copy function for each scanline. The scanline copy function
366 selected according to image metadata such as color space and the
367 presence of an alpha channel.
368 */
369 QByteArray fileContents = ioDevice->readAll();
370 jas_stream_t *imageData = jas_stream_memopen(fileContents.data(),
371 fileContents.size());
372 jasper_image = jas_image_decode(imageData, jas_image_getfmt(imageData), 0);
373 jas_stream_close(imageData);
374 if (!jasper_image) {
375 qDebug("Jasper library can't decode Jpeg2000 image data");
376 return false;
377 }
378 ScopedJasperImage scopedImage(jasper_image);
379 //printMetadata(jasper_image);
380
381 qtWidth = jas_image_width(jasper_image);
382 qtHeight = jas_image_height(jasper_image);
383 jasNumComponents = jas_image_numcmpts(jasper_image);
384 jasperColorspaceFamily = jas_clrspc_fam(jas_image_clrspc(jasper_image));
385
386 bool needColorspaceChange = false;
387 if (jasperColorspaceFamily != JAS_CLRSPC_FAM_RGB &&
388 jasperColorspaceFamily != JAS_CLRSPC_FAM_GRAY)
389 needColorspaceChange = true;
390
391 // Get per-component data
392 int c;
393 for (c = 0; c < jasNumComponents; ++c) {
394 jasComponentPrecicion[c] = jas_image_cmptprec(jasper_image, c);
395
396 // Test for precision
397 if (jasComponentPrecicion[c] > 8 || jasComponentPrecicion[c] < 8)
398 needColorspaceChange = true;
399
400 // Test for subsampling
401 if (jas_image_cmpthstep(jasper_image, c) != 1 ||
402 jas_image_cmptvstep(jasper_image, c) != 1)
403 needColorspaceChange = true;
404
405 // Test for signed components
406 if (jas_image_cmptsgnd(jasper_image, c) != 0)
407 needColorspaceChange = true;
408 }
409
410 /*
411 If we encounter a different color space than RGB
412 (such as XYZ or YCbCr) we change that to RGB.
413 Also, if any component has "funny" metadata (such as precicion != 8 bits
414 or subsampling != 1) we also do a colorspace
415 change in order to convert it to something we can load.
416 */
417
418 bool decodeOk = true;
419 if (needColorspaceChange)
420 decodeOk = attemptColorspaceChange(JAS_CLRSPC_SRGB);
421
422 if (!decodeOk) {
423 printColorSpaceError();
424 return false;
425 }
426
427 // Image metadata may have changed, get from Jasper.
428 qtWidth = jas_image_width(jasper_image);
429 qtHeight = jas_image_height(jasper_image);
430 jasNumComponents = jas_image_numcmpts(jasper_image);
431 jasperColorspaceFamily = jas_clrspc_fam(jas_image_clrspc(jasper_image));
432 for (c = 0; c < jasNumComponents; ++c) {
433 jasComponentPrecicion[c] = jas_image_cmptprec(jasper_image, c);
434 }
435
436 if (jasperColorspaceFamily != JAS_CLRSPC_FAM_RGB &&
437 jasperColorspaceFamily != JAS_CLRSPC_FAM_GRAY) {
438 qDebug("The Qt JPEG 2000 reader was unable to convert colorspace to RGB or grayscale");
439 return false;
440 }
441
442 // If a component has a subsampling factor != 1, we can't trust
443 // jas_image_height/width, so we need to figure it out ourselves
444 bool oddComponentSubsampling = false;
445 for (c = 0; c < jasNumComponents; ++c) {
446 if (jas_image_cmpthstep(jasper_image, c) != 1 ||
447 jas_image_cmptvstep(jasper_image, c) != 1) {
448 oddComponentSubsampling = true;
449 }
450 }
451
452 if (oddComponentSubsampling) {
453 // Check if all components have the same vertical/horizontal dim and
454 // subsampling
455 computedComponentWidth = jas_image_cmptwidth(jasper_image, 0);
456 computedComponentHeight = jas_image_cmptheight(jasper_image, 0);
457 computedComponentHorizontalSubsampling = jas_image_cmpthstep(jasper_image, 0);
458 computedComponentVerticalSubsampling = jas_image_cmptvstep(jasper_image, 0);
459
460 for (c = 1; c < jasNumComponents; ++c) {
461 if (computedComponentWidth != jas_image_cmptwidth(jasper_image, c) ||
462 computedComponentWidth != jas_image_cmptwidth(jasper_image, c) ||
463 computedComponentHorizontalSubsampling != jas_image_cmpthstep(jasper_image, c) ||
464 computedComponentVerticalSubsampling != jas_image_cmptvstep(jasper_image, c)) {
465 qDebug("The Qt JPEG 2000 reader does not support images where "
466 "component geometry differs from image geometry");
467 return false;
468 }
469 }
470 qtWidth = computedComponentWidth * computedComponentHorizontalSubsampling;
471 qtHeight = computedComponentHeight * computedComponentVerticalSubsampling;
472 }
473
474 // Sanity check each component
475 for (c = 0; c < jasNumComponents; ++c) {
476 // Test for precision
477 if (jasComponentPrecicion[c]>8 || jasComponentPrecicion[c]<8) {
478 qDebug("The Qt JPEG 2000 reader does not support components with "
479 "precision != 8");
480 decodeOk = false;
481 }
482#if 0
483 // Test the subsampling factor (space between pixels on the image grid)
484 if (oddComponentSubsampling) {
485 qDebug("The Qt JPEG 2000 reader does not support components with "
486 "a subsampling factor != 1 (yet)");
487 decodeOk = false;
488 }
489#endif
490 // Test for signed components
491 if (jas_image_cmptsgnd(jasper_image, c) != 0) {
492 qDebug("Qt JPEG 2000 reader does not support signed components");
493 decodeOk = false;
494 }
495
496 // Test for component/image geomoetry mismach.
497 // If oddComponentSubsampling, then this is already taken care of above.
498 if (!oddComponentSubsampling)
499 if (jas_image_cmpttlx(jasper_image,c) != 0 ||
500 jas_image_cmpttly(jasper_image,c) != 0 ||
501 jas_image_cmptbrx(jasper_image,c) != jas_image_brx(jasper_image) ||
502 jas_image_cmptbry(jasper_image,c) != jas_image_bry(jasper_image) ||
503 jas_image_cmptwidth (jasper_image, c) != jas_image_width (jasper_image) ||
504 jas_image_cmptheight(jasper_image, c) != jas_image_height(jasper_image )) {
505 qDebug("The Qt JPEG 2000 reader does not support images where "
506 "component geometry differs from image geometry");
507 printMetadata(jasper_image);
508 decodeOk = false;
509 }
510 }
511 if (!decodeOk)
512 return false;
513
514 // At this point, the colorspace should be either RGB or grayscale,
515 // and each component should have eight bits of precision and
516 // no unsupported geometry.
517 //printMetadata(jasper_image);
518
519 // Get color components
520 jasperColorspaceFamily = jas_clrspc_fam(jas_image_clrspc(jasper_image));
521 if (jasperColorspaceFamily == JAS_CLRSPC_FAM_RGB) {
522 if (jasNumComponents > 4)
523 qDebug("JPEG 2000 reader expected 3 or 4 components, got %d",
524 jasNumComponents);
525
526 // Set up mapping from R,G,B -> component num.
527 colorComponentMapping[0] = jas_image_getcmptbytype(jasper_image,
528 JAS_IMAGE_CT_RGB_R);
529 colorComponentMapping[1] = jas_image_getcmptbytype(jasper_image,
530 JAS_IMAGE_CT_RGB_G);
531 colorComponentMapping[2] = jas_image_getcmptbytype(jasper_image,
532 JAS_IMAGE_CT_RGB_B);
533 qtNumComponents = 3;
534 } else if (jasperColorspaceFamily == JAS_CLRSPC_FAM_GRAY) {
535 if (jasNumComponents > 2)
536 qDebug("JPEG 2000 reader expected 1 or 2 components, got %d",
537 jasNumComponents);
538 colorComponentMapping[0] = jas_image_getcmptbytype(jasper_image,
539 JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_GRAY_Y));
540 qtNumComponents = 1;
541 } else {
542 printColorSpaceError();
543 return false;
544 }
545
546 // Get alpha component if one exists. Due to the lack of test images,
547 // loading images with alpha channels is a bit untested. It works
548 // with images saved with this implementation though.
549 const int posibleAlphaComponent1 = 3;
550 const int posibleAlphaComponent2 = 48;
551
552 if (jasNumComponents == qtNumComponents + 1) {
553 colorComponentMapping[qtNumComponents] = jas_image_getcmptbytype(jasper_image, posibleAlphaComponent1);
554 if (colorComponentMapping[qtNumComponents] < 0) {
555 colorComponentMapping[qtNumComponents] = jas_image_getcmptbytype(jasper_image, posibleAlphaComponent2);
556 }
557 if (colorComponentMapping[qtNumComponents] > 0) {
558 hasAlpha = true;
559 qtNumComponents++;
560 }
561 }
562
563 // Check for missing components
564 for (c = 0; c < qtNumComponents; ++c) {
565 if (colorComponentMapping[c] < 0) {
566 qDebug("JPEG 2000 reader missing a color component");
567 return false;
568 }
569 }
570
571 // Create a QImage of the correct type
573 if (jasperColorspaceFamily == JAS_CLRSPC_FAM_RGB)
574 qtFormat = hasAlpha ? QImage::Format_ARGB32 : QImage::Format_RGB32;
575 else if (jasperColorspaceFamily == JAS_CLRSPC_FAM_GRAY)
576 qtFormat = hasAlpha ? QImage::Format_ARGB32 : QImage::Format_Grayscale8;
577 if (!QImageIOHandler::allocateImage(QSize(qtWidth, qtHeight), qtFormat, &qtImage))
578 return false;
579
580 // Copy data
581 if (oddComponentSubsampling) {
582 // This is a hack really, copying of data with component subsampling
583 // != 1 doesn't fit in with the rest of the scanline copying framework.
584 copyJasperQtGeneric();
585 } else if (jasperColorspaceFamily == JAS_CLRSPC_FAM_RGB) {
586 if (hasAlpha)
587 copyJasperQt(&Jpeg2000JasperReader::copyScanlineJasperQtRGBA);
588 else
589 copyJasperQt(&Jpeg2000JasperReader::copyScanlineJasperQtRGB);
590 } else if (jasperColorspaceFamily == JAS_CLRSPC_FAM_GRAY) {
591 if (hasAlpha)
592 copyJasperQt(&Jpeg2000JasperReader::copyScanlineJasperQtGrayA);
593 else
594 copyJasperQt(&Jpeg2000JasperReader::copyScanlineJasperQtGray);
595 }
596 if (decodeOk)
597 *pImage = qtImage;
598
599 return decodeOk;
600}
601
605void Jpeg2000JasperReader::copyJasperQtGeneric()
606{
607 // Create scanline data poinetrs
608 jas_matrix_t **jasperMatrix;
609 jas_seqent_t **jasperRow;
610 createJasperMatrix(jasperMatrix);
611 jasperRow = (jas_seqent_t**)malloc(jasNumComponents * sizeof(jas_seqent_t *));
612 Q_CHECK_PTR(jasperRow);
613
614 int imageY = 0;
615 for (int componentY = 0; componentY < computedComponentHeight; ++componentY) {
616 for (int c = 0; c < jasNumComponents; ++c) {
617 jas_image_readcmpt(jasper_image, colorComponentMapping[c], 0,
618 componentY, computedComponentWidth, 1,
619 jasperMatrix[c]);
620 jasperRow[c] = jas_matrix_getref(jasperMatrix[c], 0, 0);
621 }
622 for (int verticalSubsample = 0;
623 verticalSubsample < computedComponentVerticalSubsampling;
624 ++verticalSubsample) {
625 uchar *scanLineUchar = qtImage.scanLine(imageY);
626 QRgb *scanLineQRgb = reinterpret_cast<QRgb *>(scanLineUchar);
627 for (int componentX = 0; componentX < computedComponentWidth;
628 ++componentX) {
629 for (int horizontalSubsample = 0;
630 horizontalSubsample <
631 computedComponentHorizontalSubsampling;
632 ++horizontalSubsample) {
633 if (jasperColorspaceFamily == JAS_CLRSPC_FAM_RGB) {
634 if (hasAlpha) {
635 *scanLineQRgb++ = (jasperRow[3][componentX] << 24) |
636 (jasperRow[0][componentX] << 16) |
637 (jasperRow[1][componentX] << 8) |
638 jasperRow[2][componentX];
639 } else {
640 *scanLineQRgb++ = (jasperRow[0][componentX] << 16) |
641 (jasperRow[1][componentX] << 8) |
642 jasperRow[2][componentX];
643 }
644 } else if (jasperColorspaceFamily == JAS_CLRSPC_FAM_GRAY) {
645 if (hasAlpha) {
646 *scanLineQRgb++ = (jasperRow[1][componentX] << 24) |
647 (jasperRow[0][componentX] << 16) |
648 (jasperRow[0][componentX] << 8) |
649 jasperRow[0][componentX];
650 } else {
651 *scanLineUchar++ = jasperRow[0][componentX];
652 }
653 }
654 }
655 }
656 ++imageY;
657 }
658 }
659}
660
666void Jpeg2000JasperReader::copyJasperQt(const ScanlineFunc scanlineCopier)
667{
668 // Create scanline data poinetrs
669 jas_matrix_t **jasperMatrix;
670 jas_seqent_t **jasperRow;
671
672 createJasperMatrix(jasperMatrix);
673 jasperRow = (jas_seqent_t**)malloc(jasNumComponents * sizeof(jas_seqent_t *));
674 Q_CHECK_PTR(jasperRow);
675
676 for (int scanline = 0; scanline < qtHeight; ++scanline) {
677 for (int c = 0; c < jasNumComponents; ++c) {
678 jas_image_readcmpt(jasper_image, colorComponentMapping[c], 0,
679 scanline, qtWidth, 1, jasperMatrix[c]);
680 jasperRow[c] = jas_matrix_getref(jasperMatrix[c], 0, 0);
681 }
682 (this->*scanlineCopier)(jasperRow, qtImage.scanLine(scanline));
683 }
684
685 freeJasperMatrix(jasperMatrix);
686 free(jasperRow);
687}
688
693void Jpeg2000JasperReader::copyScanlineJasperQtRGB(
694 jas_seqent_t ** const jasperRow, uchar *qtScanLine)
695{
696 QRgb *scanLine = reinterpret_cast<QRgb *>(qtScanLine);
697 for (int c = 0; c < qtWidth; ++c) {
698 *scanLine++ = (0xFF << 24) |
699 (jasperRow[0][c] << 16) |
700 (jasperRow[1][c] << 8) |
701 jasperRow[2][c];
702 }
703}
704
709void Jpeg2000JasperReader::copyScanlineJasperQtRGBA(jas_seqent_t ** const jasperRow, uchar *qtScanLine)
710{
711 QRgb *scanLine = reinterpret_cast<QRgb *>(qtScanLine);
712 for (int c = 0; c < qtWidth; ++c) {
713 *scanLine++ = (jasperRow[3][c] << 24) |
714 (jasperRow[0][c] << 16) |
715 (jasperRow[1][c] << 8) |
716 jasperRow[2][c];
717 }
718}
719
724void Jpeg2000JasperReader::copyScanlineJasperQtGray(jas_seqent_t ** const jasperRow, uchar *qtScanLine)
725{
726 for (int c = 0; c < qtWidth; ++c) {
727 // *qtScanLine++ = (jasperRow[0][c] >> (jasComponentPrecicion[0] - 8));
728 *qtScanLine++ = jasperRow[0][c];
729 }
730}
731
739void Jpeg2000JasperReader::copyScanlineJasperQtGrayA(jas_seqent_t ** const jasperRow, uchar *qtScanLine)
740{
741 QRgb *scanLine = reinterpret_cast<QRgb *>(qtScanLine);
742 for (int c = 0; c < qtWidth; ++c) {
743 *scanLine++ = (jasperRow[1][c] << 24) |
744 (jasperRow[0][c] << 16) |
745 (jasperRow[0][c] << 8) |
746 jasperRow[0][c];
747 }
748}
749
757bool Jpeg2000JasperReader::write(const QImage &image, int quality)
758{
759 if (!jasperOk)
760 return false;
761
762 qtImage = image;
763
764 qtHeight = qtImage.height();
765 qtWidth = qtImage.width();
766 qtDepth = qtImage.depth();
767
768 if (qtDepth == 32) { // RGB(A)
769 jasper_image = newRGBAImage(qtWidth, qtHeight, qtImage.hasAlphaChannel());
770 if (!jasper_image)
771 return false;
772
773 if (qtImage.hasAlphaChannel())
774 copyQtJasper(&Jpeg2000JasperReader::copyScanlineQtJasperRGBA);
775 else
776 copyQtJasper(&Jpeg2000JasperReader::copyScanlineQtJasperRGB);
777 } else if (qtDepth == 8) {
778 // Color mapped grayscale
779 if (qtImage.allGray()) {
780 jasper_image = newGrayscaleImage(qtWidth, qtHeight, qtImage.hasAlphaChannel());
781 if (!jasper_image)
782 return false;
783
784 if (qtImage.hasAlphaChannel())
785 copyQtJasper(&Jpeg2000JasperReader::copyScanlineQtJasperColormapGrayscaleA);
786 else
787 copyQtJasper(&Jpeg2000JasperReader::copyScanlineQtJasperColormapGrayscale);
788 } else {
789 // Color mapped color
790 jasper_image = newRGBAImage(qtWidth, qtHeight, qtImage.hasAlphaChannel());
791 if (!jasper_image)
792 return false;
793
794 if (qtImage.hasAlphaChannel())
795 copyQtJasper(&Jpeg2000JasperReader::copyScanlineQtJasperColormapRGBA);
796 else
797 copyQtJasper(&Jpeg2000JasperReader::copyScanlineQtJasperColormapRGB);
798 }
799 } else {
800 qDebug("Unable to handle color depth %d", qtDepth);
801 return false;
802 }
803
804 int fmtid;
805 if (format == Jp2Format)
806 fmtid = jas_image_strtofmt(const_cast<char*>("jp2"));
807 else /* if (format == J2cFormat) */
808 // JasPer refers to the code stream format as jpc
809 fmtid = jas_image_strtofmt(const_cast<char*>("jpc"));
810
811 const int minQuality = 0;
812 const int maxQuality = 100;
813
814 if (quality == -1)
815 quality = 100;
816 if (quality <= minQuality)
817 quality = minQuality;
818 if (quality > maxQuality)
819 quality = maxQuality;
820
821 // Qt specifies quality as an integer in the range 0..100. Jasper specifies
822 // compression rate as an real in the range 0..1, where 1 corresponds to no
823 // compression. Computing the rate from quality is difficult, large images
824 // get better image quality than small images at the same rate. If the rate
825 // is too low, Jasper will generate a completely black image.
826 // minirate is the smallest safe rate value.
827 const double minRate = 0.001;
828
829 // maxRate specifies maximum target rate, which give the minimum amount
830 // of compression. Tests show that maxRates higer than 0.3 give no
831 // additional image quality for most images. Large images could use an even
832 // smaller maxRate value.
833 const double maxRate = 0.3;
834
835 // Set jasperRate to a value in the range minRate..maxRate. Distribute the
836 // quality steps more densely at the lower end if the rate scale.
837 const double jasperRate = minRate + pow((double(quality) / double(maxQuality)), 2) * maxRate;
838
839 // The Jasper format string contains two options:
840 // rate: rate=x
841 // lossy/lossless compression : mode=real/mode=int
842 QString jasperFormatString;
843
844 // If quality is not maxQuality, we set lossy encoding.
845 // (lossless is default)
846 if (quality != maxQuality) {
847 jasperFormatString += QLatin1String("mode=real");
848 jasperFormatString += QString(QLatin1String(" rate=%1")).arg(jasperRate);
849 }
850
851 // Open an empty jasper stream that grows automatically
852 jas_stream_t * memory_stream = jas_stream_memopen(0, 0);
853
854 // Jasper wants a non-const string.
855 char *str = qstrdup(jasperFormatString.toLatin1().constData());
856 jas_image_encode(jasper_image, memory_stream, fmtid, str);
857 delete[] str;
858 jas_stream_flush(memory_stream);
859
860 // jas_stream_t::obj_ is a void* which points to the stream implementation,
861 // e.g a file stream or a memory stream. But in our case we know that it is
862 // a memory stream since we created the object, so we just reiterpret_cast
863 // here..
864 char *buffer = reinterpret_cast<char *>(reinterpret_cast<jas_stream_memobj_t*>(memory_stream->obj_)->buf_);
865 qint64 length = jas_stream_length(memory_stream);
866 ioDevice->write(buffer, length);
867
868 jas_stream_close(memory_stream);
869 jas_image_destroy(jasper_image);
870
871 return true;
872}
873
879void Jpeg2000JasperReader::copyQtJasper(const ScanlineFuncWrite scanlinecopier)
880{
881 // Create jasper matrix for holding one scanline
882 jas_matrix_t **jasperMatrix;
883 createJasperMatrix(jasperMatrix);
884
885 for (int scanline = 0; scanline < qtHeight; ++scanline) {
886 (this->*scanlinecopier)(jasperMatrix, qtImage.scanLine(scanline));
887
888 // Write a scanline of data to jasper_image
889 for (int c = 0; c < jasNumComponents; ++c)
890 jas_image_writecmpt(jasper_image, c, 0, scanline, qtWidth, 1,
891 jasperMatrix[c]);
892 }
893 freeJasperMatrix(jasperMatrix);
894}
895
899void Jpeg2000JasperReader::copyScanlineQtJasperRGB(jas_matrix_t ** jasperRow,
900 uchar *qtScanLine)
901{
902 QRgb *scanLineBuffer = reinterpret_cast<QRgb *>(qtScanLine);
903 for (int col = 0; col < qtWidth; ++col) {
904 jas_matrix_set(jasperRow[0], 0, col, (*scanLineBuffer & 0xFF0000) >> 16);
905 jas_matrix_set(jasperRow[1], 0, col, (*scanLineBuffer & 0x00FF00) >> 8);
906 jas_matrix_set(jasperRow[2], 0, col, *scanLineBuffer & 0x0000FF);
907 ++scanLineBuffer;
908 }
909}
910
914void Jpeg2000JasperReader::copyScanlineQtJasperRGBA(jas_matrix_t ** jasperRow,
915 uchar *qtScanLine)
916{
917 QRgb *scanLineBuffer = reinterpret_cast<QRgb *>(qtScanLine);
918 for (int col = 0; col < qtWidth; ++col) {
919 jas_matrix_set(jasperRow[3], 0, col, (*scanLineBuffer & 0xFF000000) >> 24);
920 jas_matrix_set(jasperRow[0], 0, col, (*scanLineBuffer & 0x00FF0000) >> 16);
921 jas_matrix_set(jasperRow[1], 0, col, (*scanLineBuffer & 0x0000FF00) >> 8);
922 jas_matrix_set(jasperRow[2], 0, col, *scanLineBuffer & 0x000000FF);
923 ++scanLineBuffer;
924 }
925}
926
930void Jpeg2000JasperReader::copyScanlineQtJasperColormapRGB(jas_matrix_t ** jasperRow,
931 uchar *qtScanLine)
932{
933 for (int col = 0; col < qtWidth; ++col) {
934 QRgb color = qtImage.color(*qtScanLine);
935 jas_matrix_set(jasperRow[0], 0, col, qRed(color));
936 jas_matrix_set(jasperRow[1], 0, col, qGreen(color));
937 jas_matrix_set(jasperRow[2], 0, col, qBlue(color));
938 ++qtScanLine;
939 }
940}
941
945void Jpeg2000JasperReader::copyScanlineQtJasperColormapRGBA(jas_matrix_t ** jasperRow,
946 uchar *qtScanLine)
947{
948 for (int col = 0; col < qtWidth; ++col) {
949 QRgb color = qtImage.color(*qtScanLine);
950 jas_matrix_set(jasperRow[0], 0, col, qRed(color));
951 jas_matrix_set(jasperRow[1], 0, col, qGreen(color));
952 jas_matrix_set(jasperRow[2], 0, col, qBlue(color));
953 jas_matrix_set(jasperRow[3], 0, col, qAlpha(color));
954 ++qtScanLine;
955 }
956}
957
961void Jpeg2000JasperReader::copyScanlineQtJasperColormapGrayscale(jas_matrix_t ** jasperRow,
962 uchar *qtScanLine)
963{
964 for (int col = 0; col < qtWidth; ++col) {
965 QRgb color = qtImage.color(*qtScanLine);
966 jas_matrix_set(jasperRow[0], 0, col, qGray(color));
967 ++qtScanLine;
968 }
969}
970
974void Jpeg2000JasperReader::copyScanlineQtJasperColormapGrayscaleA(jas_matrix_t ** jasperRow,
975 uchar *qtScanLine)
976{
977 for (int col = 0; col < qtWidth; ++col) {
978 QRgb color = qtImage.color(*qtScanLine);
979 jas_matrix_set(jasperRow[0], 0, col, qGray(color));
980 jas_matrix_set(jasperRow[1], 0, col, qAlpha(color));
981 ++qtScanLine;
982 }
983}
984
990bool Jpeg2000JasperReader::attemptColorspaceChange(int wantedColorSpace)
991{
992 //qDebug("Attemting color space change");
993 jas_cmprof_t *outprof;
994 if (!(outprof = jas_cmprof_createfromclrspc(wantedColorSpace)))
995 return false;
996
997 jas_image_t *newimage;
998 if (!(newimage = jas_image_chclrspc(jasper_image, outprof,
999 JAS_CMXFORM_INTENT_PER))) {
1000 jas_cmprof_destroy(outprof);
1001 return false;
1002 }
1003 jas_image_destroy(jasper_image);
1004 jas_cmprof_destroy(outprof);
1005 jasper_image = newimage;
1006 return true;
1007}
1008
1013jas_image_cmptparm_t Jpeg2000JasperReader::createComponentMetadata(
1014 const int width, const int height)
1015{
1016 jas_image_cmptparm_t param;
1017 param.tlx = 0;
1018 param.tly = 0;
1019 param.hstep = 1;
1020 param.vstep = 1;
1021 param.width = width;
1022 param.height = height;
1023 param.prec = 8;
1024 param.sgnd = 0;
1025 return param;
1026}
1027
1032jas_image_t* Jpeg2000JasperReader::newRGBAImage(const int width,
1033 const int height, bool alpha)
1034{
1035 jasNumComponents = alpha ? 4 : 3;
1036 jas_image_cmptparm_t *params = new jas_image_cmptparm_t[jasNumComponents];
1037 jas_image_cmptparm_t param = createComponentMetadata(width, height);
1038 for (int c=0; c < jasNumComponents; c++)
1039 params[c] = param;
1040 jas_image_t *newImage = jas_image_create(jasNumComponents, params,
1041 JAS_CLRSPC_SRGB);
1042
1043 if (!newImage) {
1044 delete[] params;
1045 return 0;
1046 }
1047
1048 jas_image_setcmpttype(newImage, 0, JAS_IMAGE_CT_RGB_R);
1049 jas_image_setcmpttype(newImage, 1, JAS_IMAGE_CT_RGB_G);
1050 jas_image_setcmpttype(newImage, 2, JAS_IMAGE_CT_RGB_B);
1051
1052 /*
1053 It is unclear how one stores opacity(alpha) components with JasPer,
1054 the following seems to have no effect. The opacity component gets
1055 type id 3 or 48 depending jp2 or j2c format no matter what one puts
1056 in here.
1057
1058 The symbols are defined as follows:
1059 #define JAS_IMAGE_CT_RGB_R 0
1060 #define JAS_IMAGE_CT_RGB_G 1
1061 #define JAS_IMAGE_CT_RGB_B 2
1062 #define JAS_IMAGE_CT_OPACITY 0x7FFF
1063 */
1064 if (alpha)
1065 jas_image_setcmpttype(newImage, 3, JAS_IMAGE_CT_OPACITY);
1066 delete[] params;
1067 return newImage;
1068}
1069
1074jas_image_t *Jpeg2000JasperReader::newGrayscaleImage(const int width,
1075 const int height,
1076 bool alpha)
1077{
1078 jasNumComponents = alpha ? 2 : 1;
1079 jas_image_cmptparm_t param = createComponentMetadata(width, height);
1080 jas_image_t *newImage = jas_image_create(1, &param, JAS_CLRSPC_SGRAY);
1081 if (!newImage)
1082 return 0;
1083
1084 jas_image_setcmpttype(newImage, 0, JAS_IMAGE_CT_GRAY_Y);
1085
1086 // See corresponding comment for newRGBAImage.
1087 if (alpha)
1088 jas_image_setcmpttype(newImage, 1, JAS_IMAGE_CT_OPACITY);
1089 return newImage;
1090}
1091
1097bool Jpeg2000JasperReader::createJasperMatrix(jas_matrix_t **&matrix)
1098{
1099 matrix = (jas_matrix_t**)malloc(jasNumComponents * sizeof(jas_matrix_t *));
1100 for (int c = 0; c < jasNumComponents; ++c)
1101 matrix[c] = jas_matrix_create(1, qtWidth);
1102 return true;
1103}
1104
1110bool Jpeg2000JasperReader::freeJasperMatrix(jas_matrix_t **matrix)
1111{
1112 for (int c = 0; c < jasNumComponents; ++c)
1113 jas_matrix_destroy(matrix[c]);
1114 free(matrix);
1115 return false;
1116}
1117
1121void Jpeg2000JasperReader::printColorSpaceError()
1122{
1123 QString colorspaceFamily, colorspaceSpecific;
1124 decodeColorSpace(jas_image_clrspc(jasper_image), colorspaceFamily,
1125 colorspaceSpecific);
1126 qDebug("Jpeg2000 decoder is not able to handle color space %s - %s",
1127 qPrintable(colorspaceFamily), qPrintable(colorspaceSpecific));
1128}
1132bool Jpeg2000JasperReader::decodeColorSpace(int clrspc, QString &family,
1133 QString &specific)
1134{
1135 int fam = jas_clrspc_fam(clrspc);
1136 int mbr = jas_clrspc_mbr(clrspc);
1137
1138 switch (fam) {
1139 case 0: family = QLatin1String("JAS_CLRSPC_FAM_UNKNOWN"); break;
1140 case 1: family = QLatin1String("JAS_CLRSPC_FAM_XYZ"); break;
1141 case 2: family = QLatin1String("JAS_CLRSPC_FAM_LAB"); break;
1142 case 3: family = QLatin1String("JAS_CLRSPC_FAM_GRAY"); break;
1143 case 4: family = QLatin1String("JAS_CLRSPC_FAM_RGB"); break;
1144 case 5: family = QLatin1String("JAS_CLRSPC_FAM_YCBCR"); break;
1145 default: family = QLatin1String("Unknown"); return false;
1146 }
1147
1148 switch (mbr) {
1149 case 0:
1150 switch (fam) {
1151 case 1: specific = QLatin1String("JAS_CLRSPC_CIEXYZ"); break;
1152 case 2: specific = QLatin1String("JAS_CLRSPC_CIELAB"); break;
1153 case 3: specific = QLatin1String("JAS_CLRSPC_SGRAY"); break;
1154 case 4: specific = QLatin1String("JAS_CLRSPC_SRGB"); break;
1155 case 5: specific = QLatin1String("JAS_CLRSPC_SYCBCR"); break;
1156 default: specific = QLatin1String("Unknown"); return false;
1157 }
1158 break;
1159 case 1:
1160 switch (fam) {
1161 case 3: specific = QLatin1String("JAS_CLRSPC_GENGRAY"); break;
1162 case 4: specific = QLatin1String("JAS_CLRSPC_GENRGB"); break;
1163 case 5: specific = QLatin1String("JAS_CLRSPC_GENYCBCR"); break;
1164 default: specific = QLatin1String("Unknown"); return false;
1165 }
1166 break;
1167 default:
1168 return false;
1169 }
1170 return true;
1171}
1175void Jpeg2000JasperReader::printMetadata(jas_image_t *image)
1176{
1177#ifndef QT_NO_DEBUG
1178 // jas_image_cmptparm_t param
1179 qDebug("Image width: %ld", long(jas_image_width(image)));
1180 qDebug("Image height: %ld", long(jas_image_height(image)));
1181 qDebug("Coordinates on reference grid: (%ld,%ld) (%ld,%ld)",
1182 long(jas_image_tlx(image)), long(jas_image_tly(image)),
1183 long(jas_image_brx(image)), long(jas_image_bry(image)));
1184 qDebug("Number of image components: %d", jas_image_numcmpts(image));
1185
1186 QString colorspaceFamily;
1187 QString colorspaceSpecific;
1188 decodeColorSpace(jas_image_clrspc(image), colorspaceFamily, colorspaceSpecific);
1189 qDebug("Color model (space): %d, %s - %s", jas_image_clrspc(image),
1190 qPrintable(colorspaceFamily), qPrintable(colorspaceSpecific));
1191
1192 qDebug("Component metadata:");
1193
1194 for (int c = 0; c < static_cast<int>(jas_image_numcmpts(image)); ++c) {
1195 qDebug("Component %d:", c);
1196 qDebug(" Component type: %ld", long(jas_image_cmpttype(image, c)));
1197 qDebug(" Width: %ld", long(jas_image_cmptwidth(image, c)));
1198 qDebug(" Height: %ld", long(jas_image_cmptheight(image, c)));
1199 qDebug(" Signedness: %d", jas_image_cmptsgnd(image, c));
1200 qDebug(" Precision: %d", jas_image_cmptprec(image, c));
1201 qDebug(" Horizontal subsampling factor: %ld",long(jas_image_cmpthstep(image, c)));
1202 qDebug(" Vertical subsampling factor: %ld", long(jas_image_cmptvstep(image, c)));
1203 qDebug(" Coordinates on reference grid: (%ld,%ld) (%ld,%ld)",
1204 long(jas_image_cmpttlx(image, c)), long(jas_image_cmpttly(image, c)),
1205 long(jas_image_cmptbrx(image, c)), long(jas_image_cmptbry(image, c)));
1206 }
1207#else
1208 Q_UNUSED(image);
1209#endif
1210}
1211
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.
\inmodule QtCore
Definition qbytearray.h:57
\inmodule QtCore \reentrant
Definition qiodevice.h:34
QByteArray readAll()
Reads all remaining data from the device, and returns it as a byte array.
qint64 write(const char *data, qint64 len)
Writes at most maxSize bytes of data from data to the device.
qint64 peek(char *data, qint64 maxlen)
ImageOption
This enum describes the different options supported by QImageIOHandler.
static bool allocateImage(QSize size, QImage::Format format, QImage *image)
QIODevice * device() const
Returns the device currently assigned to the QImageIOHandler.
void setFormat(const QByteArray &format)
Sets the format of the QImageIOHandler to format.
static int allocationLimit()
QImage read()
Reads an image from the device.
\inmodule QtGui
Definition qimage.h:37
bool hasAlphaChannel() const
Returns true if the image has a format that respects the alpha channel, otherwise returns false.
Definition qimage.cpp:4589
uchar * scanLine(int)
Returns a pointer to the pixel data at the scanline with index i.
Definition qimage.cpp:1637
bool allGray() const
Returns true if all the colors in the image are shades of gray (i.e.
Definition qimage.cpp:2872
QRgb color(int i) const
Returns the color in the color table at index i.
Definition qimage.cpp:1577
int width() const
Returns the width of the image.
int height() const
Returns the height of the image.
Format
The following image formats are available in Qt.
Definition qimage.h:41
@ Format_RGB32
Definition qimage.h:46
@ Format_Invalid
Definition qimage.h:42
@ Format_ARGB32
Definition qimage.h:47
@ Format_Grayscale8
Definition qimage.h:66
int depth() const
QJp2Handler * q_ptr
QJp2HandlerPrivate(QJp2Handler *q_ptr)
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.
void setOption(ImageOption option, const QVariant &value) override
The JPEG 2000 handler supports two options.
bool write(const QImage &image) override
\reimp
bool read(QImage *image) override
\reimp
bool supportsOption(ImageOption option) const override
This function will return true if option is set to either QImageIOHandler::Quality or QImageIOHandler...
QVariant option(ImageOption option) const override
Get the value associated with option.
bool canRead() const override
\reimp
\inmodule QtCore
Definition qsize.h:25
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8870
\inmodule QtCore
Definition qvariant.h:65
Automatic resource handling for a jas_image_t*.
ScopedJasperImage(jas_image_t *&image)
#define this
Definition dialogs.cpp:9
QString str
[2]
Combined button and popup list for selecting options.
Definition image.cpp:4
#define QByteArrayLiteral(str)
Definition qbytearray.h:52
Q_CORE_EXPORT char * qstrdup(const char *)
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
static QString header(const QString &name)
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
SubFormat
@ Jp2Format
@ J2kFormat
#define qDebug
[1]
Definition qlogging.h:164
GLint GLsizei GLsizei height
GLenum GLuint GLenum GLsizei length
GLenum GLuint buffer
GLint GLsizei width
GLuint color
[2]
GLenum const GLint * param
GLint GLsizei GLsizei GLenum format
void ** params
const GLubyte * c
GLuint GLenum matrix
GLuint GLenum option
GLfloat GLfloat GLfloat alpha
Definition qopenglext.h:418
static bool hasAlpha(const QImage &image)
QT_BEGIN_NAMESPACE typedef unsigned int QRgb
Definition qrgb.h:13
constexpr int qRed(QRgb rgb)
Definition qrgb.h:18
constexpr int qGreen(QRgb rgb)
Definition qrgb.h:21
constexpr int qGray(int r, int g, int b)
Definition qrgb.h:36
constexpr int qBlue(QRgb rgb)
Definition qrgb.h:24
constexpr int qAlpha(QRgb rgb)
Definition qrgb.h:27
#define qPrintable(string)
Definition qstring.h:1531
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define Q_UNUSED(x)
unsigned char uchar
Definition qtypes.h:32
ptrdiff_t qsizetype
Definition qtypes.h:165
long long qint64
Definition qtypes.h:60
Q_CHECK_PTR(a=new int[80])
QByteArray imageData
[15]