mirror of
				https://github.com/JakubMelka/PDF4QT.git
				synced 2025-06-05 21:59:17 +02:00 
			
		
		
		
	Soft masks for JPEG 2000
This commit is contained in:
		| @@ -141,10 +141,6 @@ public: | |||||||
|     /// Returns number of color channels |     /// Returns number of color channels | ||||||
|     unsigned int getColorChannels() const { return m_components; } |     unsigned int getColorChannels() const { return m_components; } | ||||||
|  |  | ||||||
|     // TODO: Implement alpha channel functionality |  | ||||||
|     /// Returns number of alpha transparency channels (0 or 1) |  | ||||||
|     unsigned int getAlphaChannels() const { return 0; } |  | ||||||
|  |  | ||||||
|     bool isValid() const { return m_width && m_height && m_components && m_bitsPerComponent; } |     bool isValid() const { return m_width && m_height && m_components && m_bitsPerComponent; } | ||||||
|  |  | ||||||
|     const unsigned char* getRow(unsigned int rowIndex) const; |     const unsigned char* getRow(unsigned int rowIndex) const; | ||||||
|   | |||||||
| @@ -66,21 +66,12 @@ PDFImage PDFImage::createImage(const PDFDocument* document, | |||||||
|         throw PDFException(PDFTranslationContext::tr("Image has not data.")); |         throw PDFException(PDFTranslationContext::tr("Image has not data.")); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // TODO: Implement SMaskInData |  | ||||||
|  |  | ||||||
|     for (const char* notImplementedKey : { "SMaskInData" }) |  | ||||||
|     { |  | ||||||
|         if (dictionary->hasKey(notImplementedKey)) |  | ||||||
|         { |  | ||||||
|             throw PDFRendererException(RenderErrorType::NotImplemented, PDFTranslationContext::tr("Not implemented image property '%2'.").arg(QString::fromLatin1(notImplementedKey))); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     PDFImageData::MaskingType maskingType = PDFImageData::MaskingType::None; |     PDFImageData::MaskingType maskingType = PDFImageData::MaskingType::None; | ||||||
|     std::vector<PDFInteger> mask; |     std::vector<PDFInteger> mask; | ||||||
|     std::vector<PDFReal> decode = loader.readNumberArrayFromDictionary(dictionary, "Decode"); |     std::vector<PDFReal> decode = loader.readNumberArrayFromDictionary(dictionary, "Decode"); | ||||||
|     bool imageMask = loader.readBooleanFromDictionary(dictionary, "ImageMask", false); |     bool imageMask = loader.readBooleanFromDictionary(dictionary, "ImageMask", false); | ||||||
|     std::vector<PDFReal> matte = loader.readNumberArrayFromDictionary(dictionary, "Matte"); |     std::vector<PDFReal> matte = loader.readNumberArrayFromDictionary(dictionary, "Matte"); | ||||||
|  |     PDFInteger sMaskInData = loader.readIntegerFromDictionary(dictionary, "SMaskInData", 0); | ||||||
|  |  | ||||||
|     if (isSoftMask && (imageMask || dictionary->hasKey("Mask") || dictionary->hasKey("SMask"))) |     if (isSoftMask && (imageMask || dictionary->hasKey("Mask") || dictionary->hasKey("SMask"))) | ||||||
|     { |     { | ||||||
| @@ -447,11 +438,39 @@ PDFImage PDFImage::createImage(const PDFDocument* document, | |||||||
|             // If we have a valid image, then adjust it |             // If we have a valid image, then adjust it | ||||||
|             if (jpegImage) |             if (jpegImage) | ||||||
|             { |             { | ||||||
|  |                 // This image type can have color space defined in the data (definition of color space in PDF | ||||||
|  |                 // is only optional). So, if we doesn't have a color space, then we must determine it from the data. | ||||||
|  |                 if (!image.m_colorSpace) | ||||||
|  |                 { | ||||||
|  |                     switch (jpegImage->color_space) | ||||||
|  |                     { | ||||||
|  |                         case OPJ_CLRSPC_SRGB: | ||||||
|  |                             image.m_colorSpace.reset(new PDFDeviceRGBColorSpace()); | ||||||
|  |                             break; | ||||||
|  |  | ||||||
|  |                         case OPJ_CLRSPC_GRAY: | ||||||
|  |                             image.m_colorSpace.reset(new PDFDeviceGrayColorSpace()); | ||||||
|  |                             break; | ||||||
|  |  | ||||||
|  |                         case OPJ_CLRSPC_CMYK: | ||||||
|  |                             image.m_colorSpace.reset(new PDFDeviceCMYKColorSpace()); | ||||||
|  |                             break; | ||||||
|  |  | ||||||
|  |                         default: | ||||||
|  |                             imageData.errors.push_back(PDFRenderError(RenderErrorType::Error, PDFTranslationContext::tr("Unknown color space for JPEG 2000 image."))); | ||||||
|  |                             break; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |  | ||||||
|                 // First we must check, if all components are valid (i.e has same width/height/precision) |                 // First we must check, if all components are valid (i.e has same width/height/precision) | ||||||
|  |  | ||||||
|  |                 std::vector<OPJ_UINT32> ordinaryComponents; | ||||||
|  |                 std::vector<OPJ_UINT32> alphaComponents; | ||||||
|  |  | ||||||
|                 bool valid = true; |                 bool valid = true; | ||||||
|                 const OPJ_UINT32 componentCount = jpegImage->numcomps; |                 const OPJ_UINT32 componentCount = jpegImage->numcomps; | ||||||
|                 for (OPJ_UINT32 i = 1; i < componentCount; ++i) |                 ordinaryComponents.reserve(componentCount); | ||||||
|  |                 for (OPJ_UINT32 i = 0; i < componentCount; ++i) | ||||||
|                 { |                 { | ||||||
|                     if (jpegImage->comps[0].w != jpegImage->comps[i].w || |                     if (jpegImage->comps[0].w != jpegImage->comps[i].w || | ||||||
|                         jpegImage->comps[0].h != jpegImage->comps[i].h || |                         jpegImage->comps[0].h != jpegImage->comps[i].h || | ||||||
| @@ -461,12 +480,37 @@ PDFImage PDFImage::createImage(const PDFDocument* document, | |||||||
|                         valid = false; |                         valid = false; | ||||||
|                         break; |                         break; | ||||||
|                     } |                     } | ||||||
|  |                     else | ||||||
|  |                     { | ||||||
|  |                         // Fill in ordinary component, or alpha component | ||||||
|  |                         if (!jpegImage->comps[i].alpha) | ||||||
|  |                         { | ||||||
|  |                             ordinaryComponents.push_back(i); | ||||||
|  |                         } | ||||||
|  |                         else | ||||||
|  |                         { | ||||||
|  |                             alphaComponents.push_back(i); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 // TODO: Include alpha channel functionality - mask in image |  | ||||||
|  |  | ||||||
|                 if (valid) |                 if (valid) | ||||||
|                 { |                 { | ||||||
|  |                     const size_t colorSpaceComponentCount = image.m_colorSpace->getColorComponentCount(); | ||||||
|  |                     const bool hasAlphaChannel = !alphaComponents.empty(); | ||||||
|  |  | ||||||
|  |                     if (colorSpaceComponentCount < ordinaryComponents.size()) | ||||||
|  |                     { | ||||||
|  |                         // We have too much ordinary components | ||||||
|  |                         imageData.errors.push_back(PDFRenderError(RenderErrorType::Warning, PDFTranslationContext::tr("JPEG 2000 image has too much non-alpha channels. Ignoring %1 channels.").arg(ordinaryComponents.size() - colorSpaceComponentCount))); | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     if (alphaComponents.size() > 1) | ||||||
|  |                     { | ||||||
|  |                         // We support only one alpha channel component | ||||||
|  |                         imageData.errors.push_back(PDFRenderError(RenderErrorType::Warning, PDFTranslationContext::tr("JPEG 2000 image has too much alpha channels. Ignoring %1 alpha channels.").arg(alphaComponents.size() - 1))); | ||||||
|  |                     } | ||||||
|  |  | ||||||
|                     const OPJ_UINT32 w = jpegImage->comps[0].w; |                     const OPJ_UINT32 w = jpegImage->comps[0].w; | ||||||
|                     const OPJ_UINT32 h = jpegImage->comps[0].h; |                     const OPJ_UINT32 h = jpegImage->comps[0].h; | ||||||
|                     const OPJ_UINT32 prec = jpegImage->comps[0].prec; |                     const OPJ_UINT32 prec = jpegImage->comps[0].prec; | ||||||
| @@ -501,7 +545,8 @@ PDFImage PDFImage::createImage(const PDFDocument* document, | |||||||
|                     }; |                     }; | ||||||
|  |  | ||||||
|                     // Variables for image data. We convert all components to the 8-bit format |                     // Variables for image data. We convert all components to the 8-bit format | ||||||
|                     unsigned int components = jpegImage->numcomps; |                     const size_t ordinaryComponentCount = ordinaryComponents.size(); | ||||||
|  |                     unsigned int components = static_cast<unsigned int>(qMin(ordinaryComponentCount, colorSpaceComponentCount)); | ||||||
|                     unsigned int bitsPerComponent = 8; |                     unsigned int bitsPerComponent = 8; | ||||||
|                     unsigned int width = w; |                     unsigned int width = w; | ||||||
|                     unsigned int height = h; |                     unsigned int height = h; | ||||||
| @@ -517,13 +562,40 @@ PDFImage PDFImage::createImage(const PDFDocument* document, | |||||||
|                                 int index = stride * row + col * components + componentIndex; |                                 int index = stride * row + col * components + componentIndex; | ||||||
|                                 Q_ASSERT(index < imageDataBuffer.size()); |                                 Q_ASSERT(index < imageDataBuffer.size()); | ||||||
|  |  | ||||||
|                                 imageDataBuffer[index] = transformValue(jpegImage->comps[componentIndex].data[w * row + col]); |                                 imageDataBuffer[index] = transformValue(jpegImage->comps[ordinaryComponents[componentIndex]].data[w * row + col]); | ||||||
|                             } |                             } | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|  |  | ||||||
|                     image.m_imageData = PDFImageData(components, bitsPerComponent, width, height, stride, maskingType, qMove(imageDataBuffer), qMove(mask), qMove(decode), qMove(matte)); |                     image.m_imageData = PDFImageData(components, bitsPerComponent, width, height, stride, maskingType, qMove(imageDataBuffer), qMove(mask), qMove(decode), qMove(matte)); | ||||||
|                     valid = image.m_imageData.isValid(); |                     valid = image.m_imageData.isValid(); | ||||||
|  |  | ||||||
|  |                     // Handle the alpha channel buffer - create soft mask. If SMaskInData equals to 1, then alpha channel is used. | ||||||
|  |                     // If SMaskInData equals to 2, then premultiplied alpha channel is used. | ||||||
|  |                     if (hasAlphaChannel && (sMaskInData == 1 || sMaskInData == 2)) | ||||||
|  |                     { | ||||||
|  |                         const int alphaStride = w; | ||||||
|  |                         QByteArray alphaDataBuffer(width * height, 0); | ||||||
|  |                         const OPJ_UINT32 alphaComponentIndex = alphaComponents.front(); | ||||||
|  |                         for (unsigned int row = 0; row < h; ++row) | ||||||
|  |                         { | ||||||
|  |                             for (unsigned int col = 0; col < w; ++col) | ||||||
|  |                             { | ||||||
|  |                                 int index = alphaStride * row + col; | ||||||
|  |                                 Q_ASSERT(index < alphaDataBuffer.size()); | ||||||
|  |  | ||||||
|  |                                 alphaDataBuffer[index] = transformValue(jpegImage->comps[alphaComponentIndex].data[w * row + col]); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |  | ||||||
|  |                         if (sMaskInData == 2) | ||||||
|  |                         { | ||||||
|  |                             matte.resize(ordinaryComponentCount, 0.0); | ||||||
|  |                         } | ||||||
|  |  | ||||||
|  |                         image.m_softMask = PDFImageData(1, bitsPerComponent, width, height, alphaStride, PDFImageData::MaskingType::None, qMove(alphaDataBuffer), { }, { }, qMove(matte)); | ||||||
|  |                         image.m_imageData.setMaskingType(PDFImageData::MaskingType::SoftMask); | ||||||
|  |                     } | ||||||
|                 } |                 } | ||||||
|                 else |                 else | ||||||
|                 { |                 { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user