Support /SMaskInData in JPEG2000 images.
In CPDF_DIB::LoadJpxBitmap(), detect when /SMaskInData is set and
extract the inline SMask. Then in CPDF_DIB::StartLoadMask(), load the
extracted data if it exists.
Bug: pdfium:1469
Change-Id: I4b57bca3c46e7cf0468891dc2f18aa3a881ce24a
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/67331
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fpdfapi/page/cpdf_dib.cpp b/core/fpdfapi/page/cpdf_dib.cpp
index 2d9919d..1c51732 100644
--- a/core/fpdfapi/page/cpdf_dib.cpp
+++ b/core/fpdfapi/page/cpdf_dib.cpp
@@ -18,6 +18,8 @@
#include "core/fpdfapi/parser/cpdf_array.h"
#include "core/fpdfapi/parser/cpdf_dictionary.h"
#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_name.h"
+#include "core/fpdfapi/parser/cpdf_number.h"
#include "core/fpdfapi/parser/cpdf_stream.h"
#include "core/fpdfapi/parser/cpdf_stream_acc.h"
#include "core/fpdfapi/parser/fpdf_parser_decode.h"
@@ -151,6 +153,10 @@
CPDF_DIB::~CPDF_DIB() = default;
+CPDF_DIB::JpxSMaskInlineData::JpxSMaskInlineData() = default;
+
+CPDF_DIB::JpxSMaskInlineData::~JpxSMaskInlineData() = default;
+
bool CPDF_DIB::Load(CPDF_Document* pDoc, const CPDF_Stream* pStream) {
if (!pStream)
return false;
@@ -703,15 +709,42 @@
if (!rgb_bitmap->Create(image_info.width, image_info.height, FXDIB_Rgb))
return nullptr;
- // TODO(thestig): Is there existing code that does this already?
- // TODO(crbug.com/pdfium/1469): Handle alpha channel.
- for (uint32_t row = 0; row < image_info.height; ++row) {
- const uint8_t* src = result_bitmap->GetScanline(row);
- uint8_t* dest = rgb_bitmap->GetWritableScanline(row);
- for (uint32_t col = 0; col < image_info.width; ++col) {
- memcpy(dest, src, 3);
- src += 4;
- dest += 3;
+ if (m_pDict->GetIntegerFor("SMaskInData") == 1) {
+ // TODO(thestig): Acrobat does not support "/SMaskInData 1" combined with
+ // filters. Check for that and fail early.
+ DCHECK(m_JpxInlineData.data.empty());
+ m_JpxInlineData.width = image_info.width;
+ m_JpxInlineData.height = image_info.height;
+ m_JpxInlineData.data.reserve(image_info.width * image_info.height);
+ for (uint32_t row = 0; row < image_info.height; ++row) {
+ const uint8_t* src = result_bitmap->GetScanline(row);
+ uint8_t* dest = rgb_bitmap->GetWritableScanline(row);
+ for (uint32_t col = 0; col < image_info.width; ++col) {
+ uint8_t a = src[3];
+ m_JpxInlineData.data.push_back(a);
+ uint8_t na = 255 - a;
+ uint8_t b = (src[0] * a + 255 * na) / 255;
+ uint8_t g = (src[1] * a + 255 * na) / 255;
+ uint8_t r = (src[2] * a + 255 * na) / 255;
+ dest[0] = b;
+ dest[1] = g;
+ dest[2] = r;
+ src += 4;
+ dest += 3;
+ }
+ }
+ } else {
+ // TODO(thestig): Is there existing code that does this already?
+ for (uint32_t row = 0; row < image_info.height; ++row) {
+ const uint8_t* src = result_bitmap->GetScanline(row);
+ uint8_t* dest = rgb_bitmap->GetWritableScanline(row);
+ for (uint32_t col = 0; col < image_info.width; ++col) {
+ dest[0] = src[0];
+ dest[1] = src[1];
+ dest[2] = src[2];
+ src += 4;
+ dest += 3;
+ }
}
}
result_bitmap = std::move(rgb_bitmap);
@@ -732,6 +765,21 @@
CPDF_DIB::LoadState CPDF_DIB::StartLoadMask() {
m_MatteColor = 0XFFFFFFFF;
+
+ if (!m_JpxInlineData.data.empty()) {
+ auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+ dict->SetNewFor<CPDF_Name>("Type", "XObject");
+ dict->SetNewFor<CPDF_Name>("Subtype", "Image");
+ dict->SetNewFor<CPDF_Name>("ColorSpace", "DeviceGray");
+ dict->SetNewFor<CPDF_Number>("Width", m_JpxInlineData.width);
+ dict->SetNewFor<CPDF_Number>("Height", m_JpxInlineData.height);
+ dict->SetNewFor<CPDF_Number>("BitsPerComponent", 8);
+
+ auto mask_in_data = pdfium::MakeRetain<CPDF_Stream>();
+ mask_in_data->InitStream(m_JpxInlineData.data, dict);
+ return StartLoadMaskDIB(std::move(mask_in_data));
+ }
+
RetainPtr<const CPDF_Stream> mask(m_pDict->GetStreamFor("SMask"));
if (!mask) {
mask.Reset(ToStream(m_pDict->GetDirectObjectFor("Mask")));
diff --git a/core/fpdfapi/page/cpdf_dib.h b/core/fpdfapi/page/cpdf_dib.h
index 6960cea..1226394 100644
--- a/core/fpdfapi/page/cpdf_dib.h
+++ b/core/fpdfapi/page/cpdf_dib.h
@@ -79,6 +79,15 @@
CPDF_DIB();
~CPDF_DIB() override;
+ struct JpxSMaskInlineData {
+ JpxSMaskInlineData();
+ ~JpxSMaskInlineData();
+
+ int width;
+ int height;
+ std::vector<uint8_t> data;
+ };
+
LoadState StartLoadMask();
LoadState StartLoadMaskDIB(RetainPtr<const CPDF_Stream> mask);
bool ContinueToLoadMask();
@@ -153,6 +162,7 @@
RetainPtr<CPDF_DIB> m_pMask;
RetainPtr<CPDF_StreamAcc> m_pGlobalAcc;
std::unique_ptr<fxcodec::ScanlineDecoder> m_pDecoder;
+ JpxSMaskInlineData m_JpxInlineData;
// Must come after |m_pCachedBitmap|.
std::unique_ptr<fxcodec::Jbig2Context> m_pJbig2Context;
diff --git a/testing/SUPPRESSIONS b/testing/SUPPRESSIONS
index f1270f8..ae9cec0 100644
--- a/testing/SUPPRESSIONS
+++ b/testing/SUPPRESSIONS
@@ -343,9 +343,6 @@
# TODO(pdfium:1466): Remove after associated bug is fixed
bug_1449.in linux * *
-# TODO(pdfium:1469): Remove after associated bug is fixed
-bug_1469.in * * *
-
# xfa_specific
# TODO(pdfium:1107): Remove after associated bug is fixed