summaryrefslogtreecommitdiff
path: root/core/fpdfapi/fpdf_render
diff options
context:
space:
mode:
authorDan Sinclair <dsinclair@chromium.org>2016-03-14 13:35:12 -0400
committerDan Sinclair <dsinclair@chromium.org>2016-03-14 13:35:12 -0400
commit764ec513eecbebd12781bcc96ce81ed5e736ee92 (patch)
tree12763fde4be1f10ea1183d92185917b2b587e00b /core/fpdfapi/fpdf_render
parent97da97662417085774f75c26e535c6fbe70266ae (diff)
downloadpdfium-764ec513eecbebd12781bcc96ce81ed5e736ee92.tar.xz
Move core/src/ up to core/.
This CL moves the core/src/ files up to core/ and fixes up the include guards, includes and build files. R=tsepez@chromium.org Review URL: https://codereview.chromium.org/1800523005 .
Diffstat (limited to 'core/fpdfapi/fpdf_render')
-rw-r--r--core/fpdfapi/fpdf_render/fpdf_render.cpp1337
-rw-r--r--core/fpdfapi/fpdf_render/fpdf_render_cache.cpp334
-rw-r--r--core/fpdfapi/fpdf_render/fpdf_render_image.cpp998
-rw-r--r--core/fpdfapi/fpdf_render/fpdf_render_loadimage.cpp1654
-rw-r--r--core/fpdfapi/fpdf_render/fpdf_render_loadimage_embeddertest.cpp29
-rw-r--r--core/fpdfapi/fpdf_render/fpdf_render_pattern.cpp1207
-rw-r--r--core/fpdfapi/fpdf_render/fpdf_render_pattern_embeddertest.cpp17
-rw-r--r--core/fpdfapi/fpdf_render/fpdf_render_text.cpp787
-rw-r--r--core/fpdfapi/fpdf_render/render_int.h619
9 files changed, 6982 insertions, 0 deletions
diff --git a/core/fpdfapi/fpdf_render/fpdf_render.cpp b/core/fpdfapi/fpdf_render/fpdf_render.cpp
new file mode 100644
index 0000000000..2b82ed4cc6
--- /dev/null
+++ b/core/fpdfapi/fpdf_render/fpdf_render.cpp
@@ -0,0 +1,1337 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fpdfapi/fpdf_render/render_int.h"
+
+#include "core/fpdfapi/fpdf_page/pageint.h"
+#include "core/include/fpdfapi/cpdf_array.h"
+#include "core/include/fpdfapi/cpdf_dictionary.h"
+#include "core/include/fpdfapi/cpdf_document.h"
+#include "core/include/fpdfapi/fpdf_module.h"
+#include "core/include/fpdfapi/fpdf_render.h"
+#include "core/include/fxge/fx_ge.h"
+
+CPDF_DocRenderData::CPDF_DocRenderData(CPDF_Document* pPDFDoc)
+ : m_pPDFDoc(pPDFDoc), m_pFontCache(new CFX_FontCache) {}
+
+CPDF_DocRenderData::~CPDF_DocRenderData() {
+ Clear(TRUE);
+}
+
+void CPDF_DocRenderData::Clear(FX_BOOL bRelease) {
+ for (auto it = m_Type3FaceMap.begin(); it != m_Type3FaceMap.end();) {
+ auto curr_it = it++;
+ CPDF_CountedObject<CPDF_Type3Cache>* cache = curr_it->second;
+ if (bRelease || cache->use_count() < 2) {
+ delete cache->get();
+ delete cache;
+ m_Type3FaceMap.erase(curr_it);
+ }
+ }
+
+ for (auto it = m_TransferFuncMap.begin(); it != m_TransferFuncMap.end();) {
+ auto curr_it = it++;
+ CPDF_CountedObject<CPDF_TransferFunc>* value = curr_it->second;
+ if (bRelease || value->use_count() < 2) {
+ delete value->get();
+ delete value;
+ m_TransferFuncMap.erase(curr_it);
+ }
+ }
+
+ if (m_pFontCache) {
+ if (bRelease) {
+ delete m_pFontCache;
+ m_pFontCache = NULL;
+ } else {
+ m_pFontCache->FreeCache(FALSE);
+ }
+ }
+}
+
+CPDF_Type3Cache* CPDF_DocRenderData::GetCachedType3(CPDF_Type3Font* pFont) {
+ CPDF_CountedObject<CPDF_Type3Cache>* pCache;
+ auto it = m_Type3FaceMap.find(pFont);
+ if (it == m_Type3FaceMap.end()) {
+ CPDF_Type3Cache* pType3 = new CPDF_Type3Cache(pFont);
+ pCache = new CPDF_CountedObject<CPDF_Type3Cache>(pType3);
+ m_Type3FaceMap[pFont] = pCache;
+ } else {
+ pCache = it->second;
+ }
+ return pCache->AddRef();
+}
+
+void CPDF_DocRenderData::ReleaseCachedType3(CPDF_Type3Font* pFont) {
+ auto it = m_Type3FaceMap.find(pFont);
+ if (it != m_Type3FaceMap.end())
+ it->second->RemoveRef();
+}
+
+class CPDF_RenderModule : public IPDF_RenderModule {
+ public:
+ CPDF_RenderModule() {}
+
+ private:
+ ~CPDF_RenderModule() override {}
+
+ CPDF_DocRenderData* CreateDocData(CPDF_Document* pDoc) override;
+ void DestroyDocData(CPDF_DocRenderData* p) override;
+ void ClearDocData(CPDF_DocRenderData* p) override;
+
+ CPDF_DocRenderData* GetRenderData() override { return &m_RenderData; }
+
+ CPDF_PageRenderCache* CreatePageCache(CPDF_Page* pPage) override {
+ return new CPDF_PageRenderCache(pPage);
+ }
+
+ void DestroyPageCache(CPDF_PageRenderCache* pCache) override;
+
+ CPDF_DocRenderData m_RenderData;
+};
+
+CPDF_DocRenderData* CPDF_RenderModule::CreateDocData(CPDF_Document* pDoc) {
+ return new CPDF_DocRenderData(pDoc);
+}
+void CPDF_RenderModule::DestroyDocData(CPDF_DocRenderData* pDocData) {
+ delete pDocData;
+}
+void CPDF_RenderModule::ClearDocData(CPDF_DocRenderData* p) {
+ if (p) {
+ p->Clear(FALSE);
+ }
+}
+void CPDF_RenderModule::DestroyPageCache(CPDF_PageRenderCache* pCache) {
+ delete pCache;
+}
+
+void CPDF_ModuleMgr::InitRenderModule() {
+ m_pRenderModule.reset(new CPDF_RenderModule);
+}
+
+CPDF_RenderOptions::CPDF_RenderOptions()
+ : m_ColorMode(RENDER_COLOR_NORMAL),
+ m_Flags(RENDER_CLEARTYPE),
+ m_Interpolation(0),
+ m_AddFlags(0),
+ m_pOCContext(NULL),
+ m_dwLimitCacheSize(1024 * 1024 * 100),
+ m_HalftoneLimit(-1) {}
+FX_ARGB CPDF_RenderOptions::TranslateColor(FX_ARGB argb) const {
+ if (m_ColorMode == RENDER_COLOR_NORMAL) {
+ return argb;
+ }
+ if (m_ColorMode == RENDER_COLOR_ALPHA) {
+ return argb;
+ }
+ int a, r, g, b;
+ ArgbDecode(argb, a, r, g, b);
+ int gray = FXRGB2GRAY(r, g, b);
+ if (m_ColorMode == RENDER_COLOR_TWOCOLOR) {
+ int color = (r - gray) * (r - gray) + (g - gray) * (g - gray) +
+ (b - gray) * (b - gray);
+ if (gray < 35 && color < 20) {
+ return ArgbEncode(a, m_ForeColor);
+ }
+ if (gray > 221 && color < 20) {
+ return ArgbEncode(a, m_BackColor);
+ }
+ return argb;
+ }
+ int fr = FXSYS_GetRValue(m_ForeColor);
+ int fg = FXSYS_GetGValue(m_ForeColor);
+ int fb = FXSYS_GetBValue(m_ForeColor);
+ int br = FXSYS_GetRValue(m_BackColor);
+ int bg = FXSYS_GetGValue(m_BackColor);
+ int bb = FXSYS_GetBValue(m_BackColor);
+ r = (br - fr) * gray / 255 + fr;
+ g = (bg - fg) * gray / 255 + fg;
+ b = (bb - fb) * gray / 255 + fb;
+ return ArgbEncode(a, r, g, b);
+}
+
+// static
+int CPDF_RenderStatus::s_CurrentRecursionDepth = 0;
+
+CPDF_RenderStatus::CPDF_RenderStatus()
+ : m_pFormResource(nullptr),
+ m_pPageResource(nullptr),
+ m_pContext(nullptr),
+ m_bStopped(FALSE),
+ m_pDevice(nullptr),
+ m_pCurObj(nullptr),
+ m_pStopObj(nullptr),
+ m_HalftoneLimit(0),
+ m_bPrint(FALSE),
+ m_Transparency(0),
+ m_DitherBits(0),
+ m_bDropObjects(FALSE),
+ m_bStdCS(FALSE),
+ m_GroupFamily(0),
+ m_bLoadMask(FALSE),
+ m_pType3Char(nullptr),
+ m_T3FillColor(0),
+ m_curBlend(FXDIB_BLEND_NORMAL) {}
+
+CPDF_RenderStatus::~CPDF_RenderStatus() {}
+
+FX_BOOL CPDF_RenderStatus::Initialize(CPDF_RenderContext* pContext,
+ CFX_RenderDevice* pDevice,
+ const CFX_Matrix* pDeviceMatrix,
+ const CPDF_PageObject* pStopObj,
+ const CPDF_RenderStatus* pParentState,
+ const CPDF_GraphicStates* pInitialStates,
+ const CPDF_RenderOptions* pOptions,
+ int transparency,
+ FX_BOOL bDropObjects,
+ CPDF_Dictionary* pFormResource,
+ FX_BOOL bStdCS,
+ CPDF_Type3Char* pType3Char,
+ FX_ARGB fill_color,
+ FX_DWORD GroupFamily,
+ FX_BOOL bLoadMask) {
+ m_pContext = pContext;
+ m_pDevice = pDevice;
+ m_DitherBits = pDevice->GetDeviceCaps(FXDC_DITHER_BITS);
+ m_bPrint = m_pDevice->GetDeviceClass() != FXDC_DISPLAY;
+ if (pDeviceMatrix) {
+ m_DeviceMatrix = *pDeviceMatrix;
+ }
+ m_pStopObj = pStopObj;
+ if (pOptions) {
+ m_Options = *pOptions;
+ }
+ m_bDropObjects = bDropObjects;
+ m_bStdCS = bStdCS;
+ m_T3FillColor = fill_color;
+ m_pType3Char = pType3Char;
+ m_GroupFamily = GroupFamily;
+ m_bLoadMask = bLoadMask;
+ m_pFormResource = pFormResource;
+ m_pPageResource = m_pContext->GetPageResources();
+ if (pInitialStates && !m_pType3Char) {
+ m_InitialStates.CopyStates(*pInitialStates);
+ if (pParentState) {
+ CPDF_ColorStateData* pColorData =
+ (CPDF_ColorStateData*)(const CPDF_ColorStateData*)
+ m_InitialStates.m_ColorState;
+ CPDF_ColorStateData* pParentData =
+ (CPDF_ColorStateData*)(const CPDF_ColorStateData*)
+ pParentState->m_InitialStates.m_ColorState;
+ if (!pColorData || pColorData->m_FillColor.IsNull()) {
+ CPDF_ColorStateData* pData = m_InitialStates.m_ColorState.GetModify();
+ pData->m_FillRGB = pParentData->m_FillRGB;
+ pData->m_FillColor.Copy(&pParentData->m_FillColor);
+ }
+ if (!pColorData || pColorData->m_StrokeColor.IsNull()) {
+ CPDF_ColorStateData* pData = m_InitialStates.m_ColorState.GetModify();
+ pData->m_StrokeRGB = pParentData->m_FillRGB;
+ pData->m_StrokeColor.Copy(&pParentData->m_StrokeColor);
+ }
+ }
+ } else {
+ m_InitialStates.DefaultStates();
+ }
+ m_pObjectRenderer.reset();
+ m_Transparency = transparency;
+ return TRUE;
+}
+void CPDF_RenderStatus::RenderObjectList(
+ const CPDF_PageObjectHolder* pObjectHolder,
+ const CFX_Matrix* pObj2Device) {
+ CFX_FloatRect clip_rect(m_pDevice->GetClipBox());
+ CFX_Matrix device2object;
+ device2object.SetReverse(*pObj2Device);
+ device2object.TransformRect(clip_rect);
+
+ for (const auto& pCurObj : *pObjectHolder->GetPageObjectList()) {
+ if (pCurObj.get() == m_pStopObj) {
+ m_bStopped = TRUE;
+ return;
+ }
+ if (!pCurObj)
+ continue;
+
+ if (pCurObj->m_Left > clip_rect.right ||
+ pCurObj->m_Right < clip_rect.left ||
+ pCurObj->m_Bottom > clip_rect.top ||
+ pCurObj->m_Top < clip_rect.bottom) {
+ continue;
+ }
+ RenderSingleObject(pCurObj.get(), pObj2Device);
+ if (m_bStopped)
+ return;
+ }
+}
+void CPDF_RenderStatus::RenderSingleObject(const CPDF_PageObject* pObj,
+ const CFX_Matrix* pObj2Device) {
+ CFX_AutoRestorer<int> restorer(&s_CurrentRecursionDepth);
+ if (++s_CurrentRecursionDepth > kRenderMaxRecursionDepth) {
+ return;
+ }
+ m_pCurObj = pObj;
+ if (m_Options.m_pOCContext && pObj->m_ContentMark.NotNull()) {
+ if (!m_Options.m_pOCContext->CheckObjectVisible(pObj)) {
+ return;
+ }
+ }
+ ProcessClipPath(pObj->m_ClipPath, pObj2Device);
+ if (ProcessTransparency(pObj, pObj2Device)) {
+ return;
+ }
+ ProcessObjectNoClip(pObj, pObj2Device);
+}
+
+FX_BOOL CPDF_RenderStatus::ContinueSingleObject(const CPDF_PageObject* pObj,
+ const CFX_Matrix* pObj2Device,
+ IFX_Pause* pPause) {
+ if (m_pObjectRenderer) {
+ if (m_pObjectRenderer->Continue(pPause))
+ return TRUE;
+
+ if (!m_pObjectRenderer->m_Result)
+ DrawObjWithBackground(pObj, pObj2Device);
+ m_pObjectRenderer.reset();
+ return FALSE;
+ }
+
+ m_pCurObj = pObj;
+ if (m_Options.m_pOCContext && pObj->m_ContentMark.NotNull() &&
+ !m_Options.m_pOCContext->CheckObjectVisible(pObj)) {
+ return FALSE;
+ }
+
+ ProcessClipPath(pObj->m_ClipPath, pObj2Device);
+ if (ProcessTransparency(pObj, pObj2Device))
+ return FALSE;
+
+ if (pObj->IsImage()) {
+ m_pObjectRenderer.reset(IPDF_ObjectRenderer::Create());
+ if (!m_pObjectRenderer->Start(this, pObj, pObj2Device, FALSE)) {
+ if (!m_pObjectRenderer->m_Result)
+ DrawObjWithBackground(pObj, pObj2Device);
+ m_pObjectRenderer.reset();
+ return FALSE;
+ }
+ return ContinueSingleObject(pObj, pObj2Device, pPause);
+ }
+
+ ProcessObjectNoClip(pObj, pObj2Device);
+ return FALSE;
+}
+
+IPDF_ObjectRenderer* IPDF_ObjectRenderer::Create() {
+ return new CPDF_ImageRenderer;
+}
+
+FX_BOOL CPDF_RenderStatus::GetObjectClippedRect(const CPDF_PageObject* pObj,
+ const CFX_Matrix* pObj2Device,
+ FX_BOOL bLogical,
+ FX_RECT& rect) const {
+ rect = pObj->GetBBox(pObj2Device);
+ FX_RECT rtClip = m_pDevice->GetClipBox();
+ if (!bLogical) {
+ CFX_Matrix dCTM = m_pDevice->GetCTM();
+ FX_FLOAT a = FXSYS_fabs(dCTM.a);
+ FX_FLOAT d = FXSYS_fabs(dCTM.d);
+ if (a != 1.0f || d != 1.0f) {
+ rect.right = rect.left + (int32_t)FXSYS_ceil((FX_FLOAT)rect.Width() * a);
+ rect.bottom = rect.top + (int32_t)FXSYS_ceil((FX_FLOAT)rect.Height() * d);
+ rtClip.right =
+ rtClip.left + (int32_t)FXSYS_ceil((FX_FLOAT)rtClip.Width() * a);
+ rtClip.bottom =
+ rtClip.top + (int32_t)FXSYS_ceil((FX_FLOAT)rtClip.Height() * d);
+ }
+ }
+ rect.Intersect(rtClip);
+ return rect.IsEmpty();
+}
+void CPDF_RenderStatus::DitherObjectArea(const CPDF_PageObject* pObj,
+ const CFX_Matrix* pObj2Device) {
+ CFX_DIBitmap* pBitmap = m_pDevice->GetBitmap();
+ if (!pBitmap) {
+ return;
+ }
+ FX_RECT rect;
+ if (GetObjectClippedRect(pObj, pObj2Device, FALSE, rect)) {
+ return;
+ }
+ if (m_DitherBits == 2) {
+ static FX_ARGB pal[4] = {0, 85, 170, 255};
+ pBitmap->DitherFS(pal, 4, &rect);
+ } else if (m_DitherBits == 3) {
+ static FX_ARGB pal[8] = {0, 36, 73, 109, 146, 182, 219, 255};
+ pBitmap->DitherFS(pal, 8, &rect);
+ } else if (m_DitherBits == 4) {
+ static FX_ARGB pal[16] = {0, 17, 34, 51, 68, 85, 102, 119,
+ 136, 153, 170, 187, 204, 221, 238, 255};
+ pBitmap->DitherFS(pal, 16, &rect);
+ }
+}
+void CPDF_RenderStatus::ProcessObjectNoClip(const CPDF_PageObject* pObj,
+ const CFX_Matrix* pObj2Device) {
+ FX_BOOL bRet = FALSE;
+ switch (pObj->GetType()) {
+ case CPDF_PageObject::TEXT:
+ bRet = ProcessText(pObj->AsText(), pObj2Device, NULL);
+ break;
+ case CPDF_PageObject::PATH:
+ bRet = ProcessPath(pObj->AsPath(), pObj2Device);
+ break;
+ case CPDF_PageObject::IMAGE:
+ bRet = ProcessImage(pObj->AsImage(), pObj2Device);
+ break;
+ case CPDF_PageObject::SHADING:
+ bRet = ProcessShading(pObj->AsShading(), pObj2Device);
+ break;
+ case CPDF_PageObject::FORM:
+ bRet = ProcessForm(pObj->AsForm(), pObj2Device);
+ break;
+ }
+ if (!bRet) {
+ DrawObjWithBackground(pObj, pObj2Device);
+ }
+}
+FX_BOOL CPDF_RenderStatus::DrawObjWithBlend(const CPDF_PageObject* pObj,
+ const CFX_Matrix* pObj2Device) {
+ FX_BOOL bRet = FALSE;
+ switch (pObj->GetType()) {
+ case CPDF_PageObject::PATH:
+ bRet = ProcessPath(pObj->AsPath(), pObj2Device);
+ break;
+ case CPDF_PageObject::IMAGE:
+ bRet = ProcessImage(pObj->AsImage(), pObj2Device);
+ break;
+ case CPDF_PageObject::FORM:
+ bRet = ProcessForm(pObj->AsForm(), pObj2Device);
+ break;
+ default:
+ break;
+ }
+ return bRet;
+}
+void CPDF_RenderStatus::GetScaledMatrix(CFX_Matrix& matrix) const {
+ CFX_Matrix dCTM = m_pDevice->GetCTM();
+ matrix.a *= FXSYS_fabs(dCTM.a);
+ matrix.d *= FXSYS_fabs(dCTM.d);
+}
+void CPDF_RenderStatus::DrawObjWithBackground(const CPDF_PageObject* pObj,
+ const CFX_Matrix* pObj2Device) {
+ FX_RECT rect;
+ if (GetObjectClippedRect(pObj, pObj2Device, FALSE, rect)) {
+ return;
+ }
+ int res = 300;
+ if (pObj->IsImage() &&
+ m_pDevice->GetDeviceCaps(FXDC_DEVICE_CLASS) == FXDC_PRINTER) {
+ res = 0;
+ }
+ CPDF_ScaledRenderBuffer buffer;
+ if (!buffer.Initialize(m_pContext, m_pDevice, rect, pObj, &m_Options, res)) {
+ return;
+ }
+ CFX_Matrix matrix = *pObj2Device;
+ matrix.Concat(*buffer.GetMatrix());
+ GetScaledMatrix(matrix);
+ CPDF_Dictionary* pFormResource = NULL;
+ if (pObj->IsForm()) {
+ const CPDF_FormObject* pFormObj = pObj->AsForm();
+ if (pFormObj->m_pForm && pFormObj->m_pForm->m_pFormDict) {
+ pFormResource = pFormObj->m_pForm->m_pFormDict->GetDictBy("Resources");
+ }
+ }
+ CPDF_RenderStatus status;
+ status.Initialize(m_pContext, buffer.GetDevice(), buffer.GetMatrix(), NULL,
+ NULL, NULL, &m_Options, m_Transparency, m_bDropObjects,
+ pFormResource);
+ status.RenderSingleObject(pObj, &matrix);
+ buffer.OutputToDevice();
+}
+FX_BOOL CPDF_RenderStatus::ProcessForm(const CPDF_FormObject* pFormObj,
+ const CFX_Matrix* pObj2Device) {
+ CPDF_Dictionary* pOC = pFormObj->m_pForm->m_pFormDict->GetDictBy("OC");
+ if (pOC && m_Options.m_pOCContext &&
+ !m_Options.m_pOCContext->CheckOCGVisible(pOC)) {
+ return TRUE;
+ }
+ CFX_Matrix matrix = pFormObj->m_FormMatrix;
+ matrix.Concat(*pObj2Device);
+ CPDF_Dictionary* pResources = NULL;
+ if (pFormObj->m_pForm && pFormObj->m_pForm->m_pFormDict) {
+ pResources = pFormObj->m_pForm->m_pFormDict->GetDictBy("Resources");
+ }
+ CPDF_RenderStatus status;
+ status.Initialize(m_pContext, m_pDevice, NULL, m_pStopObj, this, pFormObj,
+ &m_Options, m_Transparency, m_bDropObjects, pResources,
+ FALSE);
+ status.m_curBlend = m_curBlend;
+ m_pDevice->SaveState();
+ status.RenderObjectList(pFormObj->m_pForm, &matrix);
+ m_bStopped = status.m_bStopped;
+ m_pDevice->RestoreState();
+ return TRUE;
+}
+FX_BOOL IsAvailableMatrix(const CFX_Matrix& matrix) {
+ if (matrix.a == 0 || matrix.d == 0) {
+ return matrix.b != 0 && matrix.c != 0;
+ }
+ if (matrix.b == 0 || matrix.c == 0) {
+ return matrix.a != 0 && matrix.d != 0;
+ }
+ return TRUE;
+}
+FX_BOOL CPDF_RenderStatus::ProcessPath(const CPDF_PathObject* pPathObj,
+ const CFX_Matrix* pObj2Device) {
+ int FillType = pPathObj->m_FillType;
+ FX_BOOL bStroke = pPathObj->m_bStroke;
+ ProcessPathPattern(pPathObj, pObj2Device, FillType, bStroke);
+ if (FillType == 0 && !bStroke) {
+ return TRUE;
+ }
+ FX_DWORD fill_argb = 0;
+ if (FillType) {
+ fill_argb = GetFillArgb(pPathObj);
+ }
+ FX_DWORD stroke_argb = 0;
+ if (bStroke) {
+ stroke_argb = GetStrokeArgb(pPathObj);
+ }
+ CFX_Matrix path_matrix = pPathObj->m_Matrix;
+ path_matrix.Concat(*pObj2Device);
+ if (!IsAvailableMatrix(path_matrix)) {
+ return TRUE;
+ }
+ if (FillType && (m_Options.m_Flags & RENDER_RECT_AA)) {
+ FillType |= FXFILL_RECT_AA;
+ }
+ if (m_Options.m_Flags & RENDER_FILL_FULLCOVER) {
+ FillType |= FXFILL_FULLCOVER;
+ }
+ if (m_Options.m_Flags & RENDER_NOPATHSMOOTH) {
+ FillType |= FXFILL_NOPATHSMOOTH;
+ }
+ if (bStroke) {
+ FillType |= FX_FILL_STROKE;
+ }
+ const CPDF_GeneralStateData* pGeneralData =
+ ((CPDF_PageObject*)pPathObj)->m_GeneralState;
+ if (pGeneralData && pGeneralData->m_StrokeAdjust) {
+ FillType |= FX_STROKE_ADJUST;
+ }
+ if (m_pType3Char) {
+ FillType |= FX_FILL_TEXT_MODE;
+ }
+ CFX_GraphStateData graphState(*pPathObj->m_GraphState);
+ if (m_Options.m_Flags & RENDER_THINLINE) {
+ graphState.m_LineWidth = 0;
+ }
+ return m_pDevice->DrawPath(pPathObj->m_Path, &path_matrix, &graphState,
+ fill_argb, stroke_argb, FillType, 0, NULL,
+ m_curBlend);
+}
+CPDF_TransferFunc* CPDF_RenderStatus::GetTransferFunc(CPDF_Object* pObj) const {
+ ASSERT(pObj);
+ CPDF_DocRenderData* pDocCache = m_pContext->GetDocument()->GetRenderData();
+ return pDocCache ? pDocCache->GetTransferFunc(pObj) : nullptr;
+}
+FX_ARGB CPDF_RenderStatus::GetFillArgb(const CPDF_PageObject* pObj,
+ FX_BOOL bType3) const {
+ CPDF_ColorStateData* pColorData =
+ (CPDF_ColorStateData*)(const CPDF_ColorStateData*)pObj->m_ColorState;
+ if (m_pType3Char && !bType3 &&
+ (!m_pType3Char->m_bColored ||
+ (m_pType3Char->m_bColored &&
+ (!pColorData || pColorData->m_FillColor.IsNull())))) {
+ return m_T3FillColor;
+ }
+ if (!pColorData || pColorData->m_FillColor.IsNull()) {
+ pColorData = (CPDF_ColorStateData*)(const CPDF_ColorStateData*)
+ m_InitialStates.m_ColorState;
+ }
+ FX_COLORREF rgb = pColorData->m_FillRGB;
+ if (rgb == (FX_DWORD)-1) {
+ return 0;
+ }
+ const CPDF_GeneralStateData* pGeneralData = pObj->m_GeneralState;
+ int alpha;
+ if (pGeneralData) {
+ alpha = (int32_t)(pGeneralData->m_FillAlpha * 255);
+ if (pGeneralData->m_pTR) {
+ if (!pGeneralData->m_pTransferFunc) {
+ ((CPDF_GeneralStateData*)pGeneralData)->m_pTransferFunc =
+ GetTransferFunc(pGeneralData->m_pTR);
+ }
+ if (pGeneralData->m_pTransferFunc) {
+ rgb = pGeneralData->m_pTransferFunc->TranslateColor(rgb);
+ }
+ }
+ } else {
+ alpha = 255;
+ }
+ return m_Options.TranslateColor(ArgbEncode(alpha, rgb));
+}
+FX_ARGB CPDF_RenderStatus::GetStrokeArgb(const CPDF_PageObject* pObj) const {
+ CPDF_ColorStateData* pColorData =
+ (CPDF_ColorStateData*)(const CPDF_ColorStateData*)pObj->m_ColorState;
+ if (m_pType3Char && (!m_pType3Char->m_bColored ||
+ (m_pType3Char->m_bColored &&
+ (!pColorData || pColorData->m_StrokeColor.IsNull())))) {
+ return m_T3FillColor;
+ }
+ if (!pColorData || pColorData->m_StrokeColor.IsNull()) {
+ pColorData = (CPDF_ColorStateData*)(const CPDF_ColorStateData*)
+ m_InitialStates.m_ColorState;
+ }
+ FX_COLORREF rgb = pColorData->m_StrokeRGB;
+ if (rgb == (FX_DWORD)-1) {
+ return 0;
+ }
+ const CPDF_GeneralStateData* pGeneralData = pObj->m_GeneralState;
+ int alpha;
+ if (pGeneralData) {
+ alpha = (int32_t)(pGeneralData->m_StrokeAlpha * 255);
+ if (pGeneralData->m_pTR) {
+ if (!pGeneralData->m_pTransferFunc) {
+ ((CPDF_GeneralStateData*)pGeneralData)->m_pTransferFunc =
+ GetTransferFunc(pGeneralData->m_pTR);
+ }
+ if (pGeneralData->m_pTransferFunc) {
+ rgb = pGeneralData->m_pTransferFunc->TranslateColor(rgb);
+ }
+ }
+ } else {
+ alpha = 255;
+ }
+ return m_Options.TranslateColor(ArgbEncode(alpha, rgb));
+}
+void CPDF_RenderStatus::ProcessClipPath(CPDF_ClipPath ClipPath,
+ const CFX_Matrix* pObj2Device) {
+ if (ClipPath.IsNull()) {
+ if (!m_LastClipPath.IsNull()) {
+ m_pDevice->RestoreState(TRUE);
+ m_LastClipPath.SetNull();
+ }
+ return;
+ }
+ if (m_LastClipPath == ClipPath)
+ return;
+
+ m_LastClipPath = ClipPath;
+ m_pDevice->RestoreState(TRUE);
+ int nClipPath = ClipPath.GetPathCount();
+ for (int i = 0; i < nClipPath; ++i) {
+ const CFX_PathData* pPathData = ClipPath.GetPath(i);
+ if (!pPathData)
+ continue;
+
+ if (pPathData->GetPointCount() == 0) {
+ CFX_PathData EmptyPath;
+ EmptyPath.AppendRect(-1, -1, 0, 0);
+ int fill_mode = FXFILL_WINDING;
+ m_pDevice->SetClip_PathFill(&EmptyPath, nullptr, fill_mode);
+ } else {
+ int ClipType = ClipPath.GetClipType(i);
+ m_pDevice->SetClip_PathFill(pPathData, pObj2Device, ClipType);
+ }
+ }
+ int textcount = ClipPath.GetTextCount();
+ if (textcount == 0)
+ return;
+
+ if (m_pDevice->GetDeviceClass() == FXDC_DISPLAY &&
+ !(m_pDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_SOFT_CLIP)) {
+ return;
+ }
+
+ std::unique_ptr<CFX_PathData> pTextClippingPath;
+ for (int i = 0; i < textcount; ++i) {
+ CPDF_TextObject* pText = ClipPath.GetText(i);
+ if (pText) {
+ if (!pTextClippingPath)
+ pTextClippingPath.reset(new CFX_PathData);
+ ProcessText(pText, pObj2Device, pTextClippingPath.get());
+ continue;
+ }
+
+ if (!pTextClippingPath)
+ continue;
+
+ int fill_mode = FXFILL_WINDING;
+ if (m_Options.m_Flags & RENDER_NOTEXTSMOOTH)
+ fill_mode |= FXFILL_NOPATHSMOOTH;
+ m_pDevice->SetClip_PathFill(pTextClippingPath.get(), nullptr, fill_mode);
+ pTextClippingPath.reset();
+ }
+}
+
+void CPDF_RenderStatus::DrawClipPath(CPDF_ClipPath ClipPath,
+ const CFX_Matrix* pObj2Device) {
+ if (ClipPath.IsNull()) {
+ return;
+ }
+ int fill_mode = 0;
+ if (m_Options.m_Flags & RENDER_NOPATHSMOOTH) {
+ fill_mode |= FXFILL_NOPATHSMOOTH;
+ }
+ int nClipPath = ClipPath.GetPathCount();
+ int i;
+ for (i = 0; i < nClipPath; i++) {
+ const CFX_PathData* pPathData = ClipPath.GetPath(i);
+ if (!pPathData) {
+ continue;
+ }
+ CFX_GraphStateData stroke_state;
+ if (m_Options.m_Flags & RENDER_THINLINE) {
+ stroke_state.m_LineWidth = 0;
+ }
+ m_pDevice->DrawPath(pPathData, pObj2Device, &stroke_state, 0, 0xffff0000,
+ fill_mode);
+ }
+}
+FX_BOOL CPDF_RenderStatus::SelectClipPath(const CPDF_PathObject* pPathObj,
+ const CFX_Matrix* pObj2Device,
+ FX_BOOL bStroke) {
+ CFX_Matrix path_matrix = pPathObj->m_Matrix;
+ path_matrix.Concat(*pObj2Device);
+ if (bStroke) {
+ CFX_GraphStateData graphState(*pPathObj->m_GraphState);
+ if (m_Options.m_Flags & RENDER_THINLINE) {
+ graphState.m_LineWidth = 0;
+ }
+ return m_pDevice->SetClip_PathStroke(pPathObj->m_Path, &path_matrix,
+ &graphState);
+ }
+ int fill_mode = pPathObj->m_FillType;
+ if (m_Options.m_Flags & RENDER_NOPATHSMOOTH) {
+ fill_mode |= FXFILL_NOPATHSMOOTH;
+ }
+ return m_pDevice->SetClip_PathFill(pPathObj->m_Path, &path_matrix, fill_mode);
+}
+FX_BOOL CPDF_RenderStatus::ProcessTransparency(const CPDF_PageObject* pPageObj,
+ const CFX_Matrix* pObj2Device) {
+ const CPDF_GeneralStateData* pGeneralState = pPageObj->m_GeneralState;
+ int blend_type =
+ pGeneralState ? pGeneralState->m_BlendType : FXDIB_BLEND_NORMAL;
+ if (blend_type == FXDIB_BLEND_UNSUPPORTED) {
+ return TRUE;
+ }
+ CPDF_Dictionary* pSMaskDict =
+ pGeneralState ? ToDictionary(pGeneralState->m_pSoftMask) : NULL;
+ if (pSMaskDict) {
+ if (pPageObj->IsImage() &&
+ pPageObj->AsImage()->m_pImage->GetDict()->KeyExist("SMask")) {
+ pSMaskDict = NULL;
+ }
+ }
+ CPDF_Dictionary* pFormResource = NULL;
+ FX_FLOAT group_alpha = 1.0f;
+ int Transparency = m_Transparency;
+ FX_BOOL bGroupTransparent = FALSE;
+ if (pPageObj->IsForm()) {
+ const CPDF_FormObject* pFormObj = pPageObj->AsForm();
+ const CPDF_GeneralStateData* pStateData =
+ pFormObj->m_GeneralState.GetObject();
+ if (pStateData) {
+ group_alpha = pStateData->m_FillAlpha;
+ }
+ Transparency = pFormObj->m_pForm->m_Transparency;
+ bGroupTransparent = !!(Transparency & PDFTRANS_ISOLATED);
+ if (pFormObj->m_pForm->m_pFormDict) {
+ pFormResource = pFormObj->m_pForm->m_pFormDict->GetDictBy("Resources");
+ }
+ }
+ FX_BOOL bTextClip = FALSE;
+ if (pPageObj->m_ClipPath.NotNull() && pPageObj->m_ClipPath.GetTextCount() &&
+ m_pDevice->GetDeviceClass() == FXDC_DISPLAY &&
+ !(m_pDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_SOFT_CLIP)) {
+ bTextClip = TRUE;
+ }
+ if ((m_Options.m_Flags & RENDER_OVERPRINT) && pPageObj->IsImage() &&
+ pGeneralState && pGeneralState->m_FillOP && pGeneralState->m_StrokeOP) {
+ CPDF_Document* pDocument = NULL;
+ CPDF_Page* pPage = NULL;
+ if (m_pContext->GetPageCache()) {
+ pPage = m_pContext->GetPageCache()->GetPage();
+ pDocument = pPage->m_pDocument;
+ } else {
+ pDocument = pPageObj->AsImage()->m_pImage->GetDocument();
+ }
+ CPDF_Dictionary* pPageResources = pPage ? pPage->m_pPageResources : NULL;
+ CPDF_Object* pCSObj =
+ pPageObj->AsImage()->m_pImage->GetStream()->GetDict()->GetElementValue(
+ "ColorSpace");
+ CPDF_ColorSpace* pColorSpace =
+ pDocument->LoadColorSpace(pCSObj, pPageResources);
+ if (pColorSpace) {
+ int format = pColorSpace->GetFamily();
+ if (format == PDFCS_DEVICECMYK || format == PDFCS_SEPARATION ||
+ format == PDFCS_DEVICEN) {
+ blend_type = FXDIB_BLEND_DARKEN;
+ }
+ pDocument->GetPageData()->ReleaseColorSpace(pCSObj);
+ }
+ }
+ if (!pSMaskDict && group_alpha == 1.0f && blend_type == FXDIB_BLEND_NORMAL &&
+ !bTextClip && !bGroupTransparent) {
+ return FALSE;
+ }
+ FX_BOOL isolated = Transparency & PDFTRANS_ISOLATED;
+ if (m_bPrint) {
+ FX_BOOL bRet = FALSE;
+ int rendCaps = m_pDevice->GetRenderCaps();
+ if (!((Transparency & PDFTRANS_ISOLATED) || pSMaskDict || bTextClip) &&
+ (rendCaps & FXRC_BLEND_MODE)) {
+ int oldBlend = m_curBlend;
+ m_curBlend = blend_type;
+ bRet = DrawObjWithBlend(pPageObj, pObj2Device);
+ m_curBlend = oldBlend;
+ }
+ if (!bRet) {
+ DrawObjWithBackground(pPageObj, pObj2Device);
+ }
+ return TRUE;
+ }
+ FX_RECT rect = pPageObj->GetBBox(pObj2Device);
+ rect.Intersect(m_pDevice->GetClipBox());
+ if (rect.IsEmpty()) {
+ return TRUE;
+ }
+ CFX_Matrix deviceCTM = m_pDevice->GetCTM();
+ FX_FLOAT scaleX = FXSYS_fabs(deviceCTM.a);
+ FX_FLOAT scaleY = FXSYS_fabs(deviceCTM.d);
+ int width = FXSYS_round((FX_FLOAT)rect.Width() * scaleX);
+ int height = FXSYS_round((FX_FLOAT)rect.Height() * scaleY);
+ CFX_FxgeDevice bitmap_device;
+ std::unique_ptr<CFX_DIBitmap> oriDevice;
+ if (!isolated && (m_pDevice->GetRenderCaps() & FXRC_GET_BITS)) {
+ oriDevice.reset(new CFX_DIBitmap);
+ if (!m_pDevice->CreateCompatibleBitmap(oriDevice.get(), width, height))
+ return TRUE;
+
+ m_pDevice->GetDIBits(oriDevice.get(), rect.left, rect.top);
+ }
+ if (!bitmap_device.Create(width, height, FXDIB_Argb, 0, oriDevice.get()))
+ return TRUE;
+
+ CFX_DIBitmap* bitmap = bitmap_device.GetBitmap();
+ bitmap->Clear(0);
+ CFX_Matrix new_matrix = *pObj2Device;
+ new_matrix.TranslateI(-rect.left, -rect.top);
+ new_matrix.Scale(scaleX, scaleY);
+ std::unique_ptr<CFX_DIBitmap> pTextMask;
+ if (bTextClip) {
+ pTextMask.reset(new CFX_DIBitmap);
+ if (!pTextMask->Create(width, height, FXDIB_8bppMask))
+ return TRUE;
+
+ pTextMask->Clear(0);
+ CFX_FxgeDevice text_device;
+ text_device.Attach(pTextMask.get());
+ for (FX_DWORD i = 0; i < pPageObj->m_ClipPath.GetTextCount(); i++) {
+ CPDF_TextObject* textobj = pPageObj->m_ClipPath.GetText(i);
+ if (!textobj) {
+ break;
+ }
+ CFX_Matrix text_matrix;
+ textobj->GetTextMatrix(&text_matrix);
+ CPDF_TextRenderer::DrawTextPath(
+ &text_device, textobj->m_nChars, textobj->m_pCharCodes,
+ textobj->m_pCharPos, textobj->m_TextState.GetFont(),
+ textobj->m_TextState.GetFontSize(), &text_matrix, &new_matrix,
+ textobj->m_GraphState, (FX_ARGB)-1, 0, NULL);
+ }
+ }
+ CPDF_RenderStatus bitmap_render;
+ bitmap_render.Initialize(m_pContext, &bitmap_device, NULL, m_pStopObj, NULL,
+ NULL, &m_Options, 0, m_bDropObjects, pFormResource,
+ TRUE);
+ bitmap_render.ProcessObjectNoClip(pPageObj, &new_matrix);
+ m_bStopped = bitmap_render.m_bStopped;
+ if (pSMaskDict) {
+ CFX_Matrix smask_matrix;
+ FXSYS_memcpy(&smask_matrix, pGeneralState->m_SMaskMatrix,
+ sizeof smask_matrix);
+ smask_matrix.Concat(*pObj2Device);
+ std::unique_ptr<CFX_DIBSource> pSMaskSource(
+ LoadSMask(pSMaskDict, &rect, &smask_matrix));
+ if (pSMaskSource)
+ bitmap->MultiplyAlpha(pSMaskSource.get());
+ }
+ if (pTextMask) {
+ bitmap->MultiplyAlpha(pTextMask.get());
+ pTextMask.reset();
+ }
+ if (Transparency & PDFTRANS_GROUP && group_alpha != 1.0f) {
+ bitmap->MultiplyAlpha((int32_t)(group_alpha * 255));
+ }
+ Transparency = m_Transparency;
+ if (pPageObj->IsForm()) {
+ Transparency |= PDFTRANS_GROUP;
+ }
+ CompositeDIBitmap(bitmap, rect.left, rect.top, 0, 255, blend_type,
+ Transparency);
+ return TRUE;
+}
+
+CFX_DIBitmap* CPDF_RenderStatus::GetBackdrop(const CPDF_PageObject* pObj,
+ const FX_RECT& rect,
+ int& left,
+ int& top,
+ FX_BOOL bBackAlphaRequired) {
+ FX_RECT bbox = rect;
+ bbox.Intersect(m_pDevice->GetClipBox());
+ left = bbox.left;
+ top = bbox.top;
+ CFX_Matrix deviceCTM = m_pDevice->GetCTM();
+ FX_FLOAT scaleX = FXSYS_fabs(deviceCTM.a);
+ FX_FLOAT scaleY = FXSYS_fabs(deviceCTM.d);
+ int width = FXSYS_round(bbox.Width() * scaleX);
+ int height = FXSYS_round(bbox.Height() * scaleY);
+ std::unique_ptr<CFX_DIBitmap> pBackdrop(new CFX_DIBitmap);
+ if (bBackAlphaRequired && !m_bDropObjects)
+ pBackdrop->Create(width, height, FXDIB_Argb);
+ else
+ m_pDevice->CreateCompatibleBitmap(pBackdrop.get(), width, height);
+
+ if (!pBackdrop->GetBuffer())
+ return nullptr;
+
+ FX_BOOL bNeedDraw;
+ if (pBackdrop->HasAlpha())
+ bNeedDraw = !(m_pDevice->GetRenderCaps() & FXRC_ALPHA_OUTPUT);
+ else
+ bNeedDraw = !(m_pDevice->GetRenderCaps() & FXRC_GET_BITS);
+
+ if (!bNeedDraw) {
+ m_pDevice->GetDIBits(pBackdrop.get(), left, top);
+ return pBackdrop.release();
+ }
+
+ CFX_Matrix FinalMatrix = m_DeviceMatrix;
+ FinalMatrix.TranslateI(-left, -top);
+ FinalMatrix.Scale(scaleX, scaleY);
+ pBackdrop->Clear(pBackdrop->HasAlpha() ? 0 : 0xffffffff);
+ CFX_FxgeDevice device;
+ device.Attach(pBackdrop.get());
+ m_pContext->Render(&device, pObj, &m_Options, &FinalMatrix);
+ return pBackdrop.release();
+}
+
+void CPDF_RenderContext::GetBackground(CFX_DIBitmap* pBuffer,
+ const CPDF_PageObject* pObj,
+ const CPDF_RenderOptions* pOptions,
+ CFX_Matrix* pFinalMatrix) {
+ CFX_FxgeDevice device;
+ device.Attach(pBuffer);
+
+ FX_RECT rect(0, 0, device.GetWidth(), device.GetHeight());
+ device.FillRect(&rect, 0xffffffff);
+ Render(&device, pObj, pOptions, pFinalMatrix);
+}
+CPDF_GraphicStates* CPDF_RenderStatus::CloneObjStates(
+ const CPDF_GraphicStates* pSrcStates,
+ FX_BOOL bStroke) {
+ if (!pSrcStates) {
+ return NULL;
+ }
+ CPDF_GraphicStates* pStates = new CPDF_GraphicStates;
+ pStates->CopyStates(*pSrcStates);
+ CPDF_Color* pObjColor = bStroke ? pSrcStates->m_ColorState.GetStrokeColor()
+ : pSrcStates->m_ColorState.GetFillColor();
+ if (!pObjColor->IsNull()) {
+ CPDF_ColorStateData* pColorData = pStates->m_ColorState.GetModify();
+ pColorData->m_FillRGB =
+ bStroke ? pSrcStates->m_ColorState.GetObject()->m_StrokeRGB
+ : pSrcStates->m_ColorState.GetObject()->m_FillRGB;
+ pColorData->m_StrokeRGB = pColorData->m_FillRGB;
+ }
+ return pStates;
+}
+
+CPDF_RenderContext::CPDF_RenderContext(CPDF_Page* pPage)
+ : m_pDocument(pPage->m_pDocument),
+ m_pPageResources(pPage->m_pPageResources),
+ m_pPageCache(pPage->GetRenderCache()),
+ m_bFirstLayer(TRUE) {}
+
+CPDF_RenderContext::CPDF_RenderContext(CPDF_Document* pDoc,
+ CPDF_PageRenderCache* pPageCache)
+ : m_pDocument(pDoc),
+ m_pPageResources(nullptr),
+ m_pPageCache(pPageCache),
+ m_bFirstLayer(TRUE) {}
+
+CPDF_RenderContext::~CPDF_RenderContext() {}
+
+void CPDF_RenderContext::AppendLayer(CPDF_PageObjectHolder* pObjectHolder,
+ const CFX_Matrix* pObject2Device) {
+ Layer* pLayer = m_Layers.AddSpace();
+ pLayer->m_pObjectHolder = pObjectHolder;
+ if (pObject2Device) {
+ pLayer->m_Matrix = *pObject2Device;
+ } else {
+ pLayer->m_Matrix.SetIdentity();
+ }
+}
+void CPDF_RenderContext::Render(CFX_RenderDevice* pDevice,
+ const CPDF_RenderOptions* pOptions,
+ const CFX_Matrix* pLastMatrix) {
+ Render(pDevice, NULL, pOptions, pLastMatrix);
+}
+void CPDF_RenderContext::Render(CFX_RenderDevice* pDevice,
+ const CPDF_PageObject* pStopObj,
+ const CPDF_RenderOptions* pOptions,
+ const CFX_Matrix* pLastMatrix) {
+ int count = m_Layers.GetSize();
+ for (int j = 0; j < count; j++) {
+ pDevice->SaveState();
+ Layer* pLayer = m_Layers.GetDataPtr(j);
+ if (pLastMatrix) {
+ CFX_Matrix FinalMatrix = pLayer->m_Matrix;
+ FinalMatrix.Concat(*pLastMatrix);
+ CPDF_RenderStatus status;
+ status.Initialize(this, pDevice, pLastMatrix, pStopObj, NULL, NULL,
+ pOptions, pLayer->m_pObjectHolder->m_Transparency,
+ FALSE, NULL);
+ status.RenderObjectList(pLayer->m_pObjectHolder, &FinalMatrix);
+ if (status.m_Options.m_Flags & RENDER_LIMITEDIMAGECACHE) {
+ m_pPageCache->CacheOptimization(status.m_Options.m_dwLimitCacheSize);
+ }
+ if (status.m_bStopped) {
+ pDevice->RestoreState();
+ break;
+ }
+ } else {
+ CPDF_RenderStatus status;
+ status.Initialize(this, pDevice, NULL, pStopObj, NULL, NULL, pOptions,
+ pLayer->m_pObjectHolder->m_Transparency, FALSE, NULL);
+ status.RenderObjectList(pLayer->m_pObjectHolder, &pLayer->m_Matrix);
+ if (status.m_Options.m_Flags & RENDER_LIMITEDIMAGECACHE) {
+ m_pPageCache->CacheOptimization(status.m_Options.m_dwLimitCacheSize);
+ }
+ if (status.m_bStopped) {
+ pDevice->RestoreState();
+ break;
+ }
+ }
+ pDevice->RestoreState();
+ }
+}
+
+CPDF_ProgressiveRenderer::CPDF_ProgressiveRenderer(
+ CPDF_RenderContext* pContext,
+ CFX_RenderDevice* pDevice,
+ const CPDF_RenderOptions* pOptions)
+ : m_Status(Ready),
+ m_pContext(pContext),
+ m_pDevice(pDevice),
+ m_pOptions(pOptions),
+ m_LayerIndex(0),
+ m_pCurrentLayer(nullptr) {}
+
+CPDF_ProgressiveRenderer::~CPDF_ProgressiveRenderer() {
+ if (m_pRenderStatus)
+ m_pDevice->RestoreState();
+}
+
+void CPDF_ProgressiveRenderer::Start(IFX_Pause* pPause) {
+ if (!m_pContext || !m_pDevice || m_Status != Ready) {
+ m_Status = Failed;
+ return;
+ }
+ m_Status = ToBeContinued;
+ Continue(pPause);
+}
+
+void CPDF_ProgressiveRenderer::Continue(IFX_Pause* pPause) {
+ while (m_Status == ToBeContinued) {
+ if (!m_pCurrentLayer) {
+ if (m_LayerIndex >= m_pContext->CountLayers()) {
+ m_Status = Done;
+ return;
+ }
+ m_pCurrentLayer = m_pContext->GetLayer(m_LayerIndex);
+ m_LastObjectRendered =
+ m_pCurrentLayer->m_pObjectHolder->GetPageObjectList()->end();
+ m_pRenderStatus.reset(new CPDF_RenderStatus());
+ m_pRenderStatus->Initialize(
+ m_pContext, m_pDevice, NULL, NULL, NULL, NULL, m_pOptions,
+ m_pCurrentLayer->m_pObjectHolder->m_Transparency, FALSE, NULL);
+ m_pDevice->SaveState();
+ m_ClipRect = CFX_FloatRect(m_pDevice->GetClipBox());
+ CFX_Matrix device2object;
+ device2object.SetReverse(m_pCurrentLayer->m_Matrix);
+ device2object.TransformRect(m_ClipRect);
+ }
+ CPDF_PageObjectList::iterator iter;
+ CPDF_PageObjectList::iterator iterEnd =
+ m_pCurrentLayer->m_pObjectHolder->GetPageObjectList()->end();
+ if (m_LastObjectRendered != iterEnd) {
+ iter = m_LastObjectRendered;
+ ++iter;
+ } else {
+ iter = m_pCurrentLayer->m_pObjectHolder->GetPageObjectList()->begin();
+ }
+ int nObjsToGo = kStepLimit;
+ while (iter != iterEnd) {
+ CPDF_PageObject* pCurObj = iter->get();
+ if (pCurObj && pCurObj->m_Left <= m_ClipRect.right &&
+ pCurObj->m_Right >= m_ClipRect.left &&
+ pCurObj->m_Bottom <= m_ClipRect.top &&
+ pCurObj->m_Top >= m_ClipRect.bottom) {
+ if (m_pRenderStatus->ContinueSingleObject(
+ pCurObj, &m_pCurrentLayer->m_Matrix, pPause)) {
+ return;
+ }
+ if (pCurObj->IsImage() &&
+ m_pRenderStatus->m_Options.m_Flags & RENDER_LIMITEDIMAGECACHE) {
+ m_pContext->GetPageCache()->CacheOptimization(
+ m_pRenderStatus->m_Options.m_dwLimitCacheSize);
+ }
+ if (pCurObj->IsForm() || pCurObj->IsShading()) {
+ nObjsToGo = 0;
+ } else {
+ --nObjsToGo;
+ }
+ }
+ m_LastObjectRendered = iter;
+ if (nObjsToGo == 0) {
+ if (pPause && pPause->NeedToPauseNow())
+ return;
+ nObjsToGo = kStepLimit;
+ }
+ ++iter;
+ }
+ if (m_pCurrentLayer->m_pObjectHolder->IsParsed()) {
+ m_pRenderStatus.reset();
+ m_pDevice->RestoreState();
+ m_pCurrentLayer = nullptr;
+ m_LayerIndex++;
+ if (pPause && pPause->NeedToPauseNow()) {
+ return;
+ }
+ } else {
+ m_pCurrentLayer->m_pObjectHolder->ContinueParse(pPause);
+ if (!m_pCurrentLayer->m_pObjectHolder->IsParsed())
+ return;
+ }
+ }
+}
+
+CPDF_TransferFunc* CPDF_DocRenderData::GetTransferFunc(CPDF_Object* pObj) {
+ if (!pObj)
+ return nullptr;
+
+ auto it = m_TransferFuncMap.find(pObj);
+ if (it != m_TransferFuncMap.end()) {
+ CPDF_CountedObject<CPDF_TransferFunc>* pTransferCounter = it->second;
+ return pTransferCounter->AddRef();
+ }
+
+ std::unique_ptr<CPDF_Function> pFuncs[3];
+ FX_BOOL bUniTransfer = TRUE;
+ FX_BOOL bIdentity = TRUE;
+ if (CPDF_Array* pArray = pObj->AsArray()) {
+ bUniTransfer = FALSE;
+ if (pArray->GetCount() < 3)
+ return nullptr;
+
+ for (FX_DWORD i = 0; i < 3; ++i) {
+ pFuncs[2 - i].reset(CPDF_Function::Load(pArray->GetElementValue(i)));
+ if (!pFuncs[2 - i])
+ return nullptr;
+ }
+ } else {
+ pFuncs[0].reset(CPDF_Function::Load(pObj));
+ if (!pFuncs[0])
+ return nullptr;
+ }
+ CPDF_TransferFunc* pTransfer = new CPDF_TransferFunc(m_pPDFDoc);
+ CPDF_CountedObject<CPDF_TransferFunc>* pTransferCounter =
+ new CPDF_CountedObject<CPDF_TransferFunc>(pTransfer);
+ m_TransferFuncMap[pObj] = pTransferCounter;
+ static const int kMaxOutputs = 16;
+ FX_FLOAT output[kMaxOutputs];
+ FXSYS_memset(output, 0, sizeof(output));
+ FX_FLOAT input;
+ int noutput;
+ for (int v = 0; v < 256; ++v) {
+ input = (FX_FLOAT)v / 255.0f;
+ if (bUniTransfer) {
+ if (pFuncs[0] && pFuncs[0]->CountOutputs() <= kMaxOutputs)
+ pFuncs[0]->Call(&input, 1, output, noutput);
+ int o = FXSYS_round(output[0] * 255);
+ if (o != v)
+ bIdentity = FALSE;
+ for (int i = 0; i < 3; ++i) {
+ pTransfer->m_Samples[i * 256 + v] = o;
+ }
+ } else {
+ for (int i = 0; i < 3; ++i) {
+ if (pFuncs[i] && pFuncs[i]->CountOutputs() <= kMaxOutputs) {
+ pFuncs[i]->Call(&input, 1, output, noutput);
+ int o = FXSYS_round(output[0] * 255);
+ if (o != v)
+ bIdentity = FALSE;
+ pTransfer->m_Samples[i * 256 + v] = o;
+ } else {
+ pTransfer->m_Samples[i * 256 + v] = v;
+ }
+ }
+ }
+ }
+
+ pTransfer->m_bIdentity = bIdentity;
+ return pTransferCounter->AddRef();
+}
+
+void CPDF_DocRenderData::ReleaseTransferFunc(CPDF_Object* pObj) {
+ auto it = m_TransferFuncMap.find(pObj);
+ if (it != m_TransferFuncMap.end())
+ it->second->RemoveRef();
+}
+
+CPDF_DeviceBuffer::CPDF_DeviceBuffer()
+ : m_pDevice(nullptr), m_pContext(nullptr), m_pObject(nullptr) {}
+
+CPDF_DeviceBuffer::~CPDF_DeviceBuffer() {}
+
+FX_BOOL CPDF_DeviceBuffer::Initialize(CPDF_RenderContext* pContext,
+ CFX_RenderDevice* pDevice,
+ FX_RECT* pRect,
+ const CPDF_PageObject* pObj,
+ int max_dpi) {
+ m_pDevice = pDevice;
+ m_pContext = pContext;
+ m_Rect = *pRect;
+ m_pObject = pObj;
+ m_Matrix.TranslateI(-pRect->left, -pRect->top);
+#if _FXM_PLATFORM_ != _FXM_PLATFORM_APPLE_
+ int horz_size = pDevice->GetDeviceCaps(FXDC_HORZ_SIZE);
+ int vert_size = pDevice->GetDeviceCaps(FXDC_VERT_SIZE);
+ if (horz_size && vert_size && max_dpi) {
+ int dpih =
+ pDevice->GetDeviceCaps(FXDC_PIXEL_WIDTH) * 254 / (horz_size * 10);
+ int dpiv =
+ pDevice->GetDeviceCaps(FXDC_PIXEL_HEIGHT) * 254 / (vert_size * 10);
+ if (dpih > max_dpi) {
+ m_Matrix.Scale((FX_FLOAT)(max_dpi) / dpih, 1.0f);
+ }
+ if (dpiv > max_dpi) {
+ m_Matrix.Scale(1.0f, (FX_FLOAT)(max_dpi) / (FX_FLOAT)dpiv);
+ }
+ }
+#endif
+ CFX_Matrix ctm = m_pDevice->GetCTM();
+ FX_FLOAT fScaleX = FXSYS_fabs(ctm.a);
+ FX_FLOAT fScaleY = FXSYS_fabs(ctm.d);
+ m_Matrix.Concat(fScaleX, 0, 0, fScaleY, 0, 0);
+ CFX_FloatRect rect(*pRect);
+ m_Matrix.TransformRect(rect);
+ FX_RECT bitmap_rect = rect.GetOutterRect();
+ m_pBitmap.reset(new CFX_DIBitmap);
+ m_pBitmap->Create(bitmap_rect.Width(), bitmap_rect.Height(), FXDIB_Argb);
+ return TRUE;
+}
+void CPDF_DeviceBuffer::OutputToDevice() {
+ if (m_pDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_GET_BITS) {
+ if (m_Matrix.a == 1.0f && m_Matrix.d == 1.0f) {
+ m_pDevice->SetDIBits(m_pBitmap.get(), m_Rect.left, m_Rect.top);
+ } else {
+ m_pDevice->StretchDIBits(m_pBitmap.get(), m_Rect.left, m_Rect.top,
+ m_Rect.Width(), m_Rect.Height());
+ }
+ } else {
+ CFX_DIBitmap buffer;
+ m_pDevice->CreateCompatibleBitmap(&buffer, m_pBitmap->GetWidth(),
+ m_pBitmap->GetHeight());
+ m_pContext->GetBackground(&buffer, m_pObject, NULL, &m_Matrix);
+ buffer.CompositeBitmap(0, 0, buffer.GetWidth(), buffer.GetHeight(),
+ m_pBitmap.get(), 0, 0);
+ m_pDevice->StretchDIBits(&buffer, m_Rect.left, m_Rect.top, m_Rect.Width(),
+ m_Rect.Height());
+ }
+}
+
+CPDF_ScaledRenderBuffer::CPDF_ScaledRenderBuffer() {}
+
+CPDF_ScaledRenderBuffer::~CPDF_ScaledRenderBuffer() {}
+
+#define _FPDFAPI_IMAGESIZE_LIMIT_ (30 * 1024 * 1024)
+FX_BOOL CPDF_ScaledRenderBuffer::Initialize(CPDF_RenderContext* pContext,
+ CFX_RenderDevice* pDevice,
+ const FX_RECT& pRect,
+ const CPDF_PageObject* pObj,
+ const CPDF_RenderOptions* pOptions,
+ int max_dpi) {
+ m_pDevice = pDevice;
+ if (m_pDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_GET_BITS) {
+ return TRUE;
+ }
+ m_pContext = pContext;
+ m_Rect = pRect;
+ m_pObject = pObj;
+ m_Matrix.TranslateI(-pRect.left, -pRect.top);
+ int horz_size = pDevice->GetDeviceCaps(FXDC_HORZ_SIZE);
+ int vert_size = pDevice->GetDeviceCaps(FXDC_VERT_SIZE);
+ if (horz_size && vert_size && max_dpi) {
+ int dpih =
+ pDevice->GetDeviceCaps(FXDC_PIXEL_WIDTH) * 254 / (horz_size * 10);
+ int dpiv =
+ pDevice->GetDeviceCaps(FXDC_PIXEL_HEIGHT) * 254 / (vert_size * 10);
+ if (dpih > max_dpi) {
+ m_Matrix.Scale((FX_FLOAT)(max_dpi) / dpih, 1.0f);
+ }
+ if (dpiv > max_dpi) {
+ m_Matrix.Scale(1.0f, (FX_FLOAT)(max_dpi) / (FX_FLOAT)dpiv);
+ }
+ }
+ m_pBitmapDevice.reset(new CFX_FxgeDevice);
+ FXDIB_Format dibFormat = FXDIB_Rgb;
+ int32_t bpp = 24;
+ if (m_pDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_ALPHA_OUTPUT) {
+ dibFormat = FXDIB_Argb;
+ bpp = 32;
+ }
+ while (1) {
+ CFX_FloatRect rect(pRect);
+ m_Matrix.TransformRect(rect);
+ FX_RECT bitmap_rect = rect.GetOutterRect();
+ int32_t iWidth = bitmap_rect.Width();
+ int32_t iHeight = bitmap_rect.Height();
+ int32_t iPitch = (iWidth * bpp + 31) / 32 * 4;
+ if (iWidth * iHeight < 1)
+ return FALSE;
+
+ if (iPitch * iHeight <= _FPDFAPI_IMAGESIZE_LIMIT_ &&
+ m_pBitmapDevice->Create(iWidth, iHeight, dibFormat)) {
+ break;
+ }
+ m_Matrix.Scale(0.5f, 0.5f);
+ }
+ m_pContext->GetBackground(m_pBitmapDevice->GetBitmap(), m_pObject, pOptions,
+ &m_Matrix);
+ return TRUE;
+}
+void CPDF_ScaledRenderBuffer::OutputToDevice() {
+ if (m_pBitmapDevice) {
+ m_pDevice->StretchDIBits(m_pBitmapDevice->GetBitmap(), m_Rect.left,
+ m_Rect.top, m_Rect.Width(), m_Rect.Height());
+ }
+}
+FX_BOOL IPDF_OCContext::CheckObjectVisible(const CPDF_PageObject* pObj) {
+ const CPDF_ContentMarkData* pData = pObj->m_ContentMark;
+ int nItems = pData->CountItems();
+ for (int i = 0; i < nItems; i++) {
+ const CPDF_ContentMarkItem& item = pData->GetItem(i);
+ if (item.GetName() == "OC" &&
+ item.GetParamType() == CPDF_ContentMarkItem::PropertiesDict &&
+ !CheckOCGVisible(item.GetParam())) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
diff --git a/core/fpdfapi/fpdf_render/fpdf_render_cache.cpp b/core/fpdfapi/fpdf_render/fpdf_render_cache.cpp
new file mode 100644
index 0000000000..f5ab7e0da5
--- /dev/null
+++ b/core/fpdfapi/fpdf_render/fpdf_render_cache.cpp
@@ -0,0 +1,334 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fpdfapi/fpdf_render/render_int.h"
+
+#include "core/fpdfapi/fpdf_page/pageint.h"
+#include "core/include/fpdfapi/cpdf_document.h"
+#include "core/include/fpdfapi/fpdf_pageobj.h"
+#include "core/include/fpdfapi/fpdf_render.h"
+#include "core/include/fxge/fx_ge.h"
+
+struct CACHEINFO {
+ FX_DWORD time;
+ CPDF_Stream* pStream;
+};
+
+extern "C" {
+static int compare(const void* data1, const void* data2) {
+ return ((CACHEINFO*)data1)->time - ((CACHEINFO*)data2)->time;
+}
+} // extern "C"
+
+CPDF_PageRenderCache::~CPDF_PageRenderCache() {
+ for (const auto& it : m_ImageCache)
+ delete it.second;
+}
+void CPDF_PageRenderCache::CacheOptimization(int32_t dwLimitCacheSize) {
+ if (m_nCacheSize <= (FX_DWORD)dwLimitCacheSize)
+ return;
+
+ size_t nCount = m_ImageCache.size();
+ CACHEINFO* pCACHEINFO = FX_Alloc(CACHEINFO, nCount);
+ size_t i = 0;
+ for (const auto& it : m_ImageCache) {
+ pCACHEINFO[i].time = it.second->GetTimeCount();
+ pCACHEINFO[i++].pStream = it.second->GetStream();
+ }
+ FXSYS_qsort(pCACHEINFO, nCount, sizeof(CACHEINFO), compare);
+ FX_DWORD nTimeCount = m_nTimeCount;
+
+ // Check if time value is about to roll over and reset all entries.
+ // The comparision is legal because FX_DWORD is an unsigned type.
+ if (nTimeCount + 1 < nTimeCount) {
+ for (i = 0; i < nCount; i++)
+ m_ImageCache[pCACHEINFO[i].pStream]->m_dwTimeCount = i;
+ m_nTimeCount = nCount;
+ }
+
+ i = 0;
+ while (i + 15 < nCount)
+ ClearImageCacheEntry(pCACHEINFO[i++].pStream);
+
+ while (i < nCount && m_nCacheSize > (FX_DWORD)dwLimitCacheSize)
+ ClearImageCacheEntry(pCACHEINFO[i++].pStream);
+
+ FX_Free(pCACHEINFO);
+}
+void CPDF_PageRenderCache::ClearImageCacheEntry(CPDF_Stream* pStream) {
+ auto it = m_ImageCache.find(pStream);
+ if (it == m_ImageCache.end())
+ return;
+
+ m_nCacheSize -= it->second->EstimateSize();
+ delete it->second;
+ m_ImageCache.erase(it);
+}
+FX_DWORD CPDF_PageRenderCache::EstimateSize() {
+ FX_DWORD dwSize = 0;
+ for (const auto& it : m_ImageCache)
+ dwSize += it.second->EstimateSize();
+
+ m_nCacheSize = dwSize;
+ return dwSize;
+}
+void CPDF_PageRenderCache::GetCachedBitmap(CPDF_Stream* pStream,
+ CFX_DIBSource*& pBitmap,
+ CFX_DIBSource*& pMask,
+ FX_DWORD& MatteColor,
+ FX_BOOL bStdCS,
+ FX_DWORD GroupFamily,
+ FX_BOOL bLoadMask,
+ CPDF_RenderStatus* pRenderStatus,
+ int32_t downsampleWidth,
+ int32_t downsampleHeight) {
+ CPDF_ImageCacheEntry* pEntry;
+ const auto it = m_ImageCache.find(pStream);
+ FX_BOOL bFound = it != m_ImageCache.end();
+ if (bFound)
+ pEntry = it->second;
+ else
+ pEntry = new CPDF_ImageCacheEntry(m_pPage->m_pDocument, pStream);
+
+ m_nTimeCount++;
+ FX_BOOL bAlreadyCached = pEntry->GetCachedBitmap(
+ pBitmap, pMask, MatteColor, m_pPage->m_pPageResources, bStdCS,
+ GroupFamily, bLoadMask, pRenderStatus, downsampleWidth, downsampleHeight);
+
+ if (!bFound)
+ m_ImageCache[pStream] = pEntry;
+
+ if (!bAlreadyCached)
+ m_nCacheSize += pEntry->EstimateSize();
+}
+FX_BOOL CPDF_PageRenderCache::StartGetCachedBitmap(
+ CPDF_Stream* pStream,
+ FX_BOOL bStdCS,
+ FX_DWORD GroupFamily,
+ FX_BOOL bLoadMask,
+ CPDF_RenderStatus* pRenderStatus,
+ int32_t downsampleWidth,
+ int32_t downsampleHeight) {
+ const auto it = m_ImageCache.find(pStream);
+ m_bCurFindCache = it != m_ImageCache.end();
+ if (m_bCurFindCache) {
+ m_pCurImageCacheEntry = it->second;
+ } else {
+ m_pCurImageCacheEntry =
+ new CPDF_ImageCacheEntry(m_pPage->m_pDocument, pStream);
+ }
+ int ret = m_pCurImageCacheEntry->StartGetCachedBitmap(
+ pRenderStatus->m_pFormResource, m_pPage->m_pPageResources, bStdCS,
+ GroupFamily, bLoadMask, pRenderStatus, downsampleWidth, downsampleHeight);
+ if (ret == 2)
+ return TRUE;
+
+ m_nTimeCount++;
+ if (!m_bCurFindCache)
+ m_ImageCache[pStream] = m_pCurImageCacheEntry;
+
+ if (!ret)
+ m_nCacheSize += m_pCurImageCacheEntry->EstimateSize();
+
+ return FALSE;
+}
+FX_BOOL CPDF_PageRenderCache::Continue(IFX_Pause* pPause) {
+ int ret = m_pCurImageCacheEntry->Continue(pPause);
+ if (ret == 2)
+ return TRUE;
+ m_nTimeCount++;
+ if (!m_bCurFindCache)
+ m_ImageCache[m_pCurImageCacheEntry->GetStream()] = m_pCurImageCacheEntry;
+ if (!ret)
+ m_nCacheSize += m_pCurImageCacheEntry->EstimateSize();
+ return FALSE;
+}
+void CPDF_PageRenderCache::ResetBitmap(CPDF_Stream* pStream,
+ const CFX_DIBitmap* pBitmap) {
+ CPDF_ImageCacheEntry* pEntry;
+ const auto it = m_ImageCache.find(pStream);
+ if (it == m_ImageCache.end()) {
+ if (!pBitmap)
+ return;
+ pEntry = new CPDF_ImageCacheEntry(m_pPage->m_pDocument, pStream);
+ m_ImageCache[pStream] = pEntry;
+ } else {
+ pEntry = it->second;
+ }
+ m_nCacheSize -= pEntry->EstimateSize();
+ pEntry->Reset(pBitmap);
+ m_nCacheSize += pEntry->EstimateSize();
+}
+CPDF_ImageCacheEntry::CPDF_ImageCacheEntry(CPDF_Document* pDoc,
+ CPDF_Stream* pStream)
+ : m_dwTimeCount(0),
+ m_pCurBitmap(NULL),
+ m_pCurMask(NULL),
+ m_MatteColor(0),
+ m_pRenderStatus(NULL),
+ m_pDocument(pDoc),
+ m_pStream(pStream),
+ m_pCachedBitmap(NULL),
+ m_pCachedMask(NULL),
+ m_dwCacheSize(0) {}
+CPDF_ImageCacheEntry::~CPDF_ImageCacheEntry() {
+ delete m_pCachedBitmap;
+ delete m_pCachedMask;
+}
+void CPDF_ImageCacheEntry::Reset(const CFX_DIBitmap* pBitmap) {
+ delete m_pCachedBitmap;
+ m_pCachedBitmap = NULL;
+ if (pBitmap) {
+ m_pCachedBitmap = pBitmap->Clone();
+ }
+ CalcSize();
+}
+void CPDF_PageRenderCache::ClearImageData() {
+ for (const auto& it : m_ImageCache)
+ it.second->ClearImageData();
+}
+void CPDF_ImageCacheEntry::ClearImageData() {
+ if (m_pCachedBitmap && !m_pCachedBitmap->GetBuffer()) {
+ ((CPDF_DIBSource*)m_pCachedBitmap)->ClearImageData();
+ }
+}
+static FX_DWORD FPDF_ImageCache_EstimateImageSize(const CFX_DIBSource* pDIB) {
+ return pDIB && pDIB->GetBuffer()
+ ? (FX_DWORD)pDIB->GetHeight() * pDIB->GetPitch() +
+ (FX_DWORD)pDIB->GetPaletteSize() * 4
+ : 0;
+}
+FX_BOOL CPDF_ImageCacheEntry::GetCachedBitmap(CFX_DIBSource*& pBitmap,
+ CFX_DIBSource*& pMask,
+ FX_DWORD& MatteColor,
+ CPDF_Dictionary* pPageResources,
+ FX_BOOL bStdCS,
+ FX_DWORD GroupFamily,
+ FX_BOOL bLoadMask,
+ CPDF_RenderStatus* pRenderStatus,
+ int32_t downsampleWidth,
+ int32_t downsampleHeight) {
+ if (m_pCachedBitmap) {
+ pBitmap = m_pCachedBitmap;
+ pMask = m_pCachedMask;
+ MatteColor = m_MatteColor;
+ return TRUE;
+ }
+ if (!pRenderStatus) {
+ return FALSE;
+ }
+ CPDF_RenderContext* pContext = pRenderStatus->GetContext();
+ CPDF_PageRenderCache* pPageRenderCache = pContext->GetPageCache();
+ m_dwTimeCount = pPageRenderCache->GetTimeCount();
+ CPDF_DIBSource* pSrc = new CPDF_DIBSource;
+ CPDF_DIBSource* pMaskSrc = NULL;
+ if (!pSrc->Load(m_pDocument, m_pStream, &pMaskSrc, &MatteColor,
+ pRenderStatus->m_pFormResource, pPageResources, bStdCS,
+ GroupFamily, bLoadMask)) {
+ delete pSrc;
+ pBitmap = NULL;
+ return FALSE;
+ }
+ m_MatteColor = MatteColor;
+ if (pSrc->GetPitch() * pSrc->GetHeight() < FPDF_HUGE_IMAGE_SIZE) {
+ m_pCachedBitmap = pSrc->Clone();
+ delete pSrc;
+ } else {
+ m_pCachedBitmap = pSrc;
+ }
+ if (pMaskSrc) {
+ m_pCachedMask = pMaskSrc->Clone();
+ delete pMaskSrc;
+ }
+
+ pBitmap = m_pCachedBitmap;
+ pMask = m_pCachedMask;
+ CalcSize();
+ return FALSE;
+}
+CFX_DIBSource* CPDF_ImageCacheEntry::DetachBitmap() {
+ CFX_DIBSource* pDIBSource = m_pCurBitmap;
+ m_pCurBitmap = NULL;
+ return pDIBSource;
+}
+CFX_DIBSource* CPDF_ImageCacheEntry::DetachMask() {
+ CFX_DIBSource* pDIBSource = m_pCurMask;
+ m_pCurMask = NULL;
+ return pDIBSource;
+}
+int CPDF_ImageCacheEntry::StartGetCachedBitmap(CPDF_Dictionary* pFormResources,
+ CPDF_Dictionary* pPageResources,
+ FX_BOOL bStdCS,
+ FX_DWORD GroupFamily,
+ FX_BOOL bLoadMask,
+ CPDF_RenderStatus* pRenderStatus,
+ int32_t downsampleWidth,
+ int32_t downsampleHeight) {
+ if (m_pCachedBitmap) {
+ m_pCurBitmap = m_pCachedBitmap;
+ m_pCurMask = m_pCachedMask;
+ return 1;
+ }
+ if (!pRenderStatus) {
+ return 0;
+ }
+ m_pRenderStatus = pRenderStatus;
+ m_pCurBitmap = new CPDF_DIBSource;
+ int ret =
+ ((CPDF_DIBSource*)m_pCurBitmap)
+ ->StartLoadDIBSource(m_pDocument, m_pStream, TRUE, pFormResources,
+ pPageResources, bStdCS, GroupFamily, bLoadMask);
+ if (ret == 2) {
+ return ret;
+ }
+ if (!ret) {
+ delete m_pCurBitmap;
+ m_pCurBitmap = NULL;
+ return 0;
+ }
+ ContinueGetCachedBitmap();
+ return 0;
+}
+void CPDF_ImageCacheEntry::ContinueGetCachedBitmap() {
+ m_MatteColor = ((CPDF_DIBSource*)m_pCurBitmap)->GetMatteColor();
+ m_pCurMask = ((CPDF_DIBSource*)m_pCurBitmap)->DetachMask();
+ CPDF_RenderContext* pContext = m_pRenderStatus->GetContext();
+ CPDF_PageRenderCache* pPageRenderCache = pContext->GetPageCache();
+ m_dwTimeCount = pPageRenderCache->GetTimeCount();
+ if (m_pCurBitmap->GetPitch() * m_pCurBitmap->GetHeight() <
+ FPDF_HUGE_IMAGE_SIZE) {
+ m_pCachedBitmap = m_pCurBitmap->Clone();
+ delete m_pCurBitmap;
+ m_pCurBitmap = NULL;
+ } else {
+ m_pCachedBitmap = m_pCurBitmap;
+ }
+ if (m_pCurMask) {
+ m_pCachedMask = m_pCurMask->Clone();
+ delete m_pCurMask;
+ m_pCurMask = NULL;
+ }
+ m_pCurBitmap = m_pCachedBitmap;
+ m_pCurMask = m_pCachedMask;
+ CalcSize();
+}
+int CPDF_ImageCacheEntry::Continue(IFX_Pause* pPause) {
+ int ret = ((CPDF_DIBSource*)m_pCurBitmap)->ContinueLoadDIBSource(pPause);
+ if (ret == 2) {
+ return ret;
+ }
+ if (!ret) {
+ delete m_pCurBitmap;
+ m_pCurBitmap = NULL;
+ return 0;
+ }
+ ContinueGetCachedBitmap();
+ return 0;
+}
+void CPDF_ImageCacheEntry::CalcSize() {
+ m_dwCacheSize = FPDF_ImageCache_EstimateImageSize(m_pCachedBitmap) +
+ FPDF_ImageCache_EstimateImageSize(m_pCachedMask);
+}
diff --git a/core/fpdfapi/fpdf_render/fpdf_render_image.cpp b/core/fpdfapi/fpdf_render/fpdf_render_image.cpp
new file mode 100644
index 0000000000..6c235a0258
--- /dev/null
+++ b/core/fpdfapi/fpdf_render/fpdf_render_image.cpp
@@ -0,0 +1,998 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fpdfapi/fpdf_render/render_int.h"
+
+#include <utility>
+#include <vector>
+
+#include "core/fpdfapi/fpdf_page/pageint.h"
+#include "core/include/fpdfapi/cpdf_array.h"
+#include "core/include/fpdfapi/cpdf_dictionary.h"
+#include "core/include/fpdfapi/cpdf_document.h"
+#include "core/include/fpdfapi/fpdf_module.h"
+#include "core/include/fpdfapi/fpdf_pageobj.h"
+#include "core/include/fpdfapi/fpdf_render.h"
+#include "core/include/fxcodec/fx_codec.h"
+#include "core/include/fxcrt/fx_safe_types.h"
+#include "core/include/fxge/fx_ge.h"
+
+FX_BOOL CPDF_RenderStatus::ProcessImage(const CPDF_ImageObject* pImageObj,
+ const CFX_Matrix* pObj2Device) {
+ CPDF_ImageRenderer render;
+ if (render.Start(this, pImageObj, pObj2Device, m_bStdCS, m_curBlend)) {
+ render.Continue(NULL);
+ }
+ return render.m_Result;
+}
+void CPDF_RenderStatus::CompositeDIBitmap(CFX_DIBitmap* pDIBitmap,
+ int left,
+ int top,
+ FX_ARGB mask_argb,
+ int bitmap_alpha,
+ int blend_mode,
+ int Transparency) {
+ if (!pDIBitmap) {
+ return;
+ }
+ FX_BOOL bIsolated = Transparency & PDFTRANS_ISOLATED;
+ FX_BOOL bGroup = Transparency & PDFTRANS_GROUP;
+ if (blend_mode == FXDIB_BLEND_NORMAL) {
+ if (!pDIBitmap->IsAlphaMask()) {
+ if (bitmap_alpha < 255) {
+ pDIBitmap->MultiplyAlpha(bitmap_alpha);
+ }
+ if (m_pDevice->SetDIBits(pDIBitmap, left, top)) {
+ return;
+ }
+ } else {
+ FX_DWORD fill_argb = m_Options.TranslateColor(mask_argb);
+ if (bitmap_alpha < 255) {
+ ((uint8_t*)&fill_argb)[3] =
+ ((uint8_t*)&fill_argb)[3] * bitmap_alpha / 255;
+ }
+ if (m_pDevice->SetBitMask(pDIBitmap, left, top, fill_argb)) {
+ return;
+ }
+ }
+ }
+ FX_BOOL bBackAlphaRequired = blend_mode && bIsolated && !m_bDropObjects;
+ FX_BOOL bGetBackGround =
+ ((m_pDevice->GetRenderCaps() & FXRC_ALPHA_OUTPUT)) ||
+ (!(m_pDevice->GetRenderCaps() & FXRC_ALPHA_OUTPUT) &&
+ (m_pDevice->GetRenderCaps() & FXRC_GET_BITS) && !bBackAlphaRequired);
+ if (bGetBackGround) {
+ if (bIsolated || !bGroup) {
+ if (pDIBitmap->IsAlphaMask()) {
+ return;
+ }
+ m_pDevice->SetDIBits(pDIBitmap, left, top, blend_mode);
+ } else {
+ FX_RECT rect(left, top, left + pDIBitmap->GetWidth(),
+ top + pDIBitmap->GetHeight());
+ rect.Intersect(m_pDevice->GetClipBox());
+ CFX_DIBitmap* pClone = NULL;
+ FX_BOOL bClone = FALSE;
+ if (m_pDevice->GetBackDrop() && m_pDevice->GetBitmap()) {
+ bClone = TRUE;
+ pClone = m_pDevice->GetBackDrop()->Clone(&rect);
+ CFX_DIBitmap* pForeBitmap = m_pDevice->GetBitmap();
+ pClone->CompositeBitmap(0, 0, pClone->GetWidth(), pClone->GetHeight(),
+ pForeBitmap, rect.left, rect.top);
+ left = left >= 0 ? 0 : left;
+ top = top >= 0 ? 0 : top;
+ if (!pDIBitmap->IsAlphaMask())
+ pClone->CompositeBitmap(0, 0, pClone->GetWidth(), pClone->GetHeight(),
+ pDIBitmap, left, top, blend_mode);
+ else
+ pClone->CompositeMask(0, 0, pClone->GetWidth(), pClone->GetHeight(),
+ pDIBitmap, mask_argb, left, top, blend_mode);
+ } else {
+ pClone = pDIBitmap;
+ }
+ if (m_pDevice->GetBackDrop()) {
+ m_pDevice->SetDIBits(pClone, rect.left, rect.top);
+ } else {
+ if (pDIBitmap->IsAlphaMask()) {
+ return;
+ }
+ m_pDevice->SetDIBits(pDIBitmap, rect.left, rect.top, blend_mode);
+ }
+ if (bClone) {
+ delete pClone;
+ }
+ }
+ return;
+ }
+ int back_left, back_top;
+ FX_RECT rect(left, top, left + pDIBitmap->GetWidth(),
+ top + pDIBitmap->GetHeight());
+ std::unique_ptr<CFX_DIBitmap> pBackdrop(
+ GetBackdrop(m_pCurObj, rect, back_left, back_top,
+ blend_mode > FXDIB_BLEND_NORMAL && bIsolated));
+ if (!pBackdrop)
+ return;
+
+ if (!pDIBitmap->IsAlphaMask()) {
+ pBackdrop->CompositeBitmap(left - back_left, top - back_top,
+ pDIBitmap->GetWidth(), pDIBitmap->GetHeight(),
+ pDIBitmap, 0, 0, blend_mode);
+ } else {
+ pBackdrop->CompositeMask(left - back_left, top - back_top,
+ pDIBitmap->GetWidth(), pDIBitmap->GetHeight(),
+ pDIBitmap, mask_argb, 0, 0, blend_mode);
+ }
+
+ std::unique_ptr<CFX_DIBitmap> pBackdrop1(new CFX_DIBitmap);
+ pBackdrop1->Create(pBackdrop->GetWidth(), pBackdrop->GetHeight(),
+ FXDIB_Rgb32);
+ pBackdrop1->Clear((FX_DWORD)-1);
+ pBackdrop1->CompositeBitmap(0, 0, pBackdrop->GetWidth(),
+ pBackdrop->GetHeight(), pBackdrop.get(), 0, 0);
+ pBackdrop = std::move(pBackdrop1);
+ m_pDevice->SetDIBits(pBackdrop.get(), back_left, back_top);
+}
+
+CPDF_TransferFunc::CPDF_TransferFunc(CPDF_Document* pDoc) : m_pPDFDoc(pDoc) {}
+
+FX_COLORREF CPDF_TransferFunc::TranslateColor(FX_COLORREF rgb) const {
+ return FXSYS_RGB(m_Samples[FXSYS_GetRValue(rgb)],
+ m_Samples[256 + FXSYS_GetGValue(rgb)],
+ m_Samples[512 + FXSYS_GetBValue(rgb)]);
+}
+
+CFX_DIBSource* CPDF_TransferFunc::TranslateImage(const CFX_DIBSource* pSrc,
+ FX_BOOL bAutoDropSrc) {
+ CPDF_DIBTransferFunc* pDest = new CPDF_DIBTransferFunc(this);
+ pDest->LoadSrc(pSrc, bAutoDropSrc);
+ return pDest;
+}
+
+CPDF_DIBTransferFunc::~CPDF_DIBTransferFunc() {}
+
+FXDIB_Format CPDF_DIBTransferFunc::GetDestFormat() {
+ if (m_pSrc->IsAlphaMask()) {
+ return FXDIB_8bppMask;
+ }
+#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
+ return (m_pSrc->HasAlpha()) ? FXDIB_Argb : FXDIB_Rgb32;
+#else
+ return (m_pSrc->HasAlpha()) ? FXDIB_Argb : FXDIB_Rgb;
+#endif
+}
+CPDF_DIBTransferFunc::CPDF_DIBTransferFunc(
+ const CPDF_TransferFunc* pTransferFunc) {
+ m_RampR = pTransferFunc->m_Samples;
+ m_RampG = &pTransferFunc->m_Samples[256];
+ m_RampB = &pTransferFunc->m_Samples[512];
+}
+void CPDF_DIBTransferFunc::TranslateScanline(uint8_t* dest_buf,
+ const uint8_t* src_buf) const {
+ int i;
+ FX_BOOL bSkip = FALSE;
+ switch (m_pSrc->GetFormat()) {
+ case FXDIB_1bppRgb: {
+ int r0 = m_RampR[0], g0 = m_RampG[0], b0 = m_RampB[0];
+ int r1 = m_RampR[255], g1 = m_RampG[255], b1 = m_RampB[255];
+ for (i = 0; i < m_Width; i++) {
+ if (src_buf[i / 8] & (1 << (7 - i % 8))) {
+ *dest_buf++ = b1;
+ *dest_buf++ = g1;
+ *dest_buf++ = r1;
+ } else {
+ *dest_buf++ = b0;
+ *dest_buf++ = g0;
+ *dest_buf++ = r0;
+ }
+#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
+ dest_buf++;
+#endif
+ }
+ break;
+ }
+ case FXDIB_1bppMask: {
+ int m0 = m_RampR[0], m1 = m_RampR[255];
+ for (i = 0; i < m_Width; i++) {
+ if (src_buf[i / 8] & (1 << (7 - i % 8))) {
+ *dest_buf++ = m1;
+ } else {
+ *dest_buf++ = m0;
+ }
+ }
+ break;
+ }
+ case FXDIB_8bppRgb: {
+ FX_ARGB* pPal = m_pSrc->GetPalette();
+ for (i = 0; i < m_Width; i++) {
+ if (pPal) {
+ FX_ARGB src_argb = pPal[*src_buf];
+ *dest_buf++ = m_RampB[FXARGB_R(src_argb)];
+ *dest_buf++ = m_RampG[FXARGB_G(src_argb)];
+ *dest_buf++ = m_RampR[FXARGB_B(src_argb)];
+ } else {
+ FX_DWORD src_byte = *src_buf;
+ *dest_buf++ = m_RampB[src_byte];
+ *dest_buf++ = m_RampG[src_byte];
+ *dest_buf++ = m_RampR[src_byte];
+ }
+ src_buf++;
+#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
+ dest_buf++;
+#endif
+ }
+ break;
+ }
+ case FXDIB_8bppMask:
+ for (i = 0; i < m_Width; i++) {
+ *dest_buf++ = m_RampR[*(src_buf++)];
+ }
+ break;
+ case FXDIB_Rgb:
+ for (i = 0; i < m_Width; i++) {
+ *dest_buf++ = m_RampB[*(src_buf++)];
+ *dest_buf++ = m_RampG[*(src_buf++)];
+ *dest_buf++ = m_RampR[*(src_buf++)];
+#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
+ dest_buf++;
+#endif
+ }
+ break;
+ case FXDIB_Rgb32:
+ bSkip = TRUE;
+ case FXDIB_Argb:
+ for (i = 0; i < m_Width; i++) {
+ *dest_buf++ = m_RampB[*(src_buf++)];
+ *dest_buf++ = m_RampG[*(src_buf++)];
+ *dest_buf++ = m_RampR[*(src_buf++)];
+ if (!bSkip) {
+ *dest_buf++ = *src_buf;
+#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
+ } else {
+ dest_buf++;
+#endif
+ }
+ src_buf++;
+ }
+ break;
+ default:
+ break;
+ }
+}
+void CPDF_DIBTransferFunc::TranslateDownSamples(uint8_t* dest_buf,
+ const uint8_t* src_buf,
+ int pixels,
+ int Bpp) const {
+ if (Bpp == 8) {
+ for (int i = 0; i < pixels; i++) {
+ *dest_buf++ = m_RampR[*(src_buf++)];
+ }
+ } else if (Bpp == 24) {
+ for (int i = 0; i < pixels; i++) {
+ *dest_buf++ = m_RampB[*(src_buf++)];
+ *dest_buf++ = m_RampG[*(src_buf++)];
+ *dest_buf++ = m_RampR[*(src_buf++)];
+ }
+ } else {
+#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
+ if (!m_pSrc->HasAlpha()) {
+ for (int i = 0; i < pixels; i++) {
+ *dest_buf++ = m_RampB[*(src_buf++)];
+ *dest_buf++ = m_RampG[*(src_buf++)];
+ *dest_buf++ = m_RampR[*(src_buf++)];
+ dest_buf++;
+ src_buf++;
+ }
+ } else {
+#endif
+ for (int i = 0; i < pixels; i++) {
+ *dest_buf++ = m_RampB[*(src_buf++)];
+ *dest_buf++ = m_RampG[*(src_buf++)];
+ *dest_buf++ = m_RampR[*(src_buf++)];
+ *dest_buf++ = *(src_buf++);
+ }
+#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
+ }
+#endif
+ }
+}
+CPDF_ImageRenderer::CPDF_ImageRenderer() {
+ m_pRenderStatus = NULL;
+ m_pImageObject = NULL;
+ m_Result = TRUE;
+ m_Status = 0;
+ m_pTransformer = NULL;
+ m_DeviceHandle = NULL;
+ m_LoadHandle = NULL;
+ m_pClone = NULL;
+ m_bStdCS = FALSE;
+ m_bPatternColor = FALSE;
+ m_BlendType = FXDIB_BLEND_NORMAL;
+ m_pPattern = NULL;
+ m_pObj2Device = NULL;
+}
+CPDF_ImageRenderer::~CPDF_ImageRenderer() {
+ delete m_pTransformer;
+ if (m_DeviceHandle) {
+ m_pRenderStatus->m_pDevice->CancelDIBits(m_DeviceHandle);
+ }
+ delete m_LoadHandle;
+ delete m_pClone;
+}
+FX_BOOL CPDF_ImageRenderer::StartLoadDIBSource() {
+ CFX_FloatRect image_rect_f = m_ImageMatrix.GetUnitRect();
+ FX_RECT image_rect = image_rect_f.GetOutterRect();
+ int dest_width = image_rect.Width();
+ int dest_height = image_rect.Height();
+ if (m_ImageMatrix.a < 0) {
+ dest_width = -dest_width;
+ }
+ if (m_ImageMatrix.d > 0) {
+ dest_height = -dest_height;
+ }
+ if (m_Loader.Start(m_pImageObject,
+ m_pRenderStatus->m_pContext->GetPageCache(), m_LoadHandle,
+ m_bStdCS, m_pRenderStatus->m_GroupFamily,
+ m_pRenderStatus->m_bLoadMask, m_pRenderStatus, dest_width,
+ dest_height)) {
+ if (m_LoadHandle) {
+ m_Status = 4;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+FX_BOOL CPDF_ImageRenderer::StartRenderDIBSource() {
+ if (!m_Loader.m_pBitmap) {
+ return FALSE;
+ }
+ m_BitmapAlpha = 255;
+ const CPDF_GeneralStateData* pGeneralState = m_pImageObject->m_GeneralState;
+ if (pGeneralState) {
+ m_BitmapAlpha = FXSYS_round(pGeneralState->m_FillAlpha * 255);
+ }
+ m_pDIBSource = m_Loader.m_pBitmap;
+ if (m_pRenderStatus->m_Options.m_ColorMode == RENDER_COLOR_ALPHA &&
+ !m_Loader.m_pMask) {
+ return StartBitmapAlpha();
+ }
+ if (pGeneralState && pGeneralState->m_pTR) {
+ if (!pGeneralState->m_pTransferFunc) {
+ ((CPDF_GeneralStateData*)pGeneralState)->m_pTransferFunc =
+ m_pRenderStatus->GetTransferFunc(pGeneralState->m_pTR);
+ }
+ if (pGeneralState->m_pTransferFunc &&
+ !pGeneralState->m_pTransferFunc->m_bIdentity) {
+ m_pDIBSource = m_Loader.m_pBitmap =
+ pGeneralState->m_pTransferFunc->TranslateImage(m_Loader.m_pBitmap,
+ !m_Loader.m_bCached);
+ if (m_Loader.m_bCached && m_Loader.m_pMask) {
+ m_Loader.m_pMask = m_Loader.m_pMask->Clone();
+ }
+ m_Loader.m_bCached = FALSE;
+ }
+ }
+ m_FillArgb = 0;
+ m_bPatternColor = FALSE;
+ m_pPattern = NULL;
+ if (m_pDIBSource->IsAlphaMask()) {
+ CPDF_Color* pColor = m_pImageObject->m_ColorState.GetFillColor();
+ if (pColor && pColor->IsPattern()) {
+ m_pPattern = pColor->GetPattern();
+ if (m_pPattern) {
+ m_bPatternColor = TRUE;
+ }
+ }
+ m_FillArgb = m_pRenderStatus->GetFillArgb(m_pImageObject);
+ } else if (m_pRenderStatus->m_Options.m_ColorMode == RENDER_COLOR_GRAY) {
+ m_pClone = m_pDIBSource->Clone();
+ m_pClone->ConvertColorScale(m_pRenderStatus->m_Options.m_BackColor,
+ m_pRenderStatus->m_Options.m_ForeColor);
+ m_pDIBSource = m_pClone;
+ }
+ m_Flags = 0;
+ if (m_pRenderStatus->m_Options.m_Flags & RENDER_FORCE_DOWNSAMPLE) {
+ m_Flags |= RENDER_FORCE_DOWNSAMPLE;
+ } else if (m_pRenderStatus->m_Options.m_Flags & RENDER_FORCE_HALFTONE) {
+ m_Flags |= RENDER_FORCE_HALFTONE;
+ }
+ if (m_pRenderStatus->m_pDevice->GetDeviceClass() != FXDC_DISPLAY) {
+ CPDF_Object* pFilters =
+ m_pImageObject->m_pImage->GetStream()->GetDict()->GetElementValue(
+ "Filter");
+ if (pFilters) {
+ if (pFilters->IsName()) {
+ CFX_ByteStringC bsDecodeType = pFilters->GetConstString();
+ if (bsDecodeType == "DCTDecode" || bsDecodeType == "JPXDecode") {
+ m_Flags |= FXRENDER_IMAGE_LOSSY;
+ }
+ } else if (CPDF_Array* pArray = pFilters->AsArray()) {
+ for (FX_DWORD i = 0; i < pArray->GetCount(); i++) {
+ CFX_ByteStringC bsDecodeType = pArray->GetConstStringAt(i);
+ if (bsDecodeType == "DCTDecode" || bsDecodeType == "JPXDecode") {
+ m_Flags |= FXRENDER_IMAGE_LOSSY;
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (m_pRenderStatus->m_Options.m_Flags & RENDER_NOIMAGESMOOTH) {
+ m_Flags |= FXDIB_NOSMOOTH;
+ } else if (m_pImageObject->m_pImage->IsInterpol()) {
+ m_Flags |= FXDIB_INTERPOL;
+ }
+ if (m_Loader.m_pMask) {
+ return DrawMaskedImage();
+ }
+ if (m_bPatternColor) {
+ return DrawPatternImage(m_pObj2Device);
+ }
+ if (m_BitmapAlpha == 255 && pGeneralState && pGeneralState->m_FillOP &&
+ pGeneralState->m_OPMode == 0 &&
+ pGeneralState->m_BlendType == FXDIB_BLEND_NORMAL &&
+ pGeneralState->m_StrokeAlpha == 1 && pGeneralState->m_FillAlpha == 1) {
+ CPDF_Document* pDocument = NULL;
+ CPDF_Page* pPage = NULL;
+ if (m_pRenderStatus->m_pContext->GetPageCache()) {
+ pPage = m_pRenderStatus->m_pContext->GetPageCache()->GetPage();
+ pDocument = pPage->m_pDocument;
+ } else {
+ pDocument = m_pImageObject->m_pImage->GetDocument();
+ }
+ CPDF_Dictionary* pPageResources = pPage ? pPage->m_pPageResources : NULL;
+ CPDF_Object* pCSObj =
+ m_pImageObject->m_pImage->GetStream()->GetDict()->GetElementValue(
+ "ColorSpace");
+ CPDF_ColorSpace* pColorSpace =
+ pDocument->LoadColorSpace(pCSObj, pPageResources);
+ if (pColorSpace) {
+ int format = pColorSpace->GetFamily();
+ if (format == PDFCS_DEVICECMYK || format == PDFCS_SEPARATION ||
+ format == PDFCS_DEVICEN) {
+ m_BlendType = FXDIB_BLEND_DARKEN;
+ }
+ pDocument->GetPageData()->ReleaseColorSpace(pCSObj);
+ }
+ }
+ return StartDIBSource();
+}
+FX_BOOL CPDF_ImageRenderer::Start(CPDF_RenderStatus* pStatus,
+ const CPDF_PageObject* pObj,
+ const CFX_Matrix* pObj2Device,
+ FX_BOOL bStdCS,
+ int blendType) {
+ m_pRenderStatus = pStatus;
+ m_bStdCS = bStdCS;
+ m_pImageObject = pObj->AsImage();
+ m_BlendType = blendType;
+ m_pObj2Device = pObj2Device;
+ CPDF_Dictionary* pOC = m_pImageObject->m_pImage->GetOC();
+ if (pOC && m_pRenderStatus->m_Options.m_pOCContext &&
+ !m_pRenderStatus->m_Options.m_pOCContext->CheckOCGVisible(pOC)) {
+ return FALSE;
+ }
+ m_ImageMatrix = m_pImageObject->m_Matrix;
+ m_ImageMatrix.Concat(*pObj2Device);
+ if (StartLoadDIBSource()) {
+ return TRUE;
+ }
+ return StartRenderDIBSource();
+}
+FX_BOOL CPDF_ImageRenderer::Start(CPDF_RenderStatus* pStatus,
+ const CFX_DIBSource* pDIBSource,
+ FX_ARGB bitmap_argb,
+ int bitmap_alpha,
+ const CFX_Matrix* pImage2Device,
+ FX_DWORD flags,
+ FX_BOOL bStdCS,
+ int blendType) {
+ m_pRenderStatus = pStatus;
+ m_pDIBSource = pDIBSource;
+ m_FillArgb = bitmap_argb;
+ m_BitmapAlpha = bitmap_alpha;
+ m_ImageMatrix = *pImage2Device;
+ m_Flags = flags;
+ m_bStdCS = bStdCS;
+ m_BlendType = blendType;
+ return StartDIBSource();
+}
+FX_BOOL CPDF_ImageRenderer::DrawPatternImage(const CFX_Matrix* pObj2Device) {
+ if (m_pRenderStatus->m_bPrint &&
+ !(m_pRenderStatus->m_pDevice->GetRenderCaps() & FXRC_BLEND_MODE)) {
+ m_Result = FALSE;
+ return FALSE;
+ }
+ FX_RECT rect = m_ImageMatrix.GetUnitRect().GetOutterRect();
+ rect.Intersect(m_pRenderStatus->m_pDevice->GetClipBox());
+ if (rect.IsEmpty()) {
+ return FALSE;
+ }
+ CFX_Matrix new_matrix = m_ImageMatrix;
+ new_matrix.TranslateI(-rect.left, -rect.top);
+ int width = rect.Width();
+ int height = rect.Height();
+ CFX_FxgeDevice bitmap_device1;
+ if (!bitmap_device1.Create(rect.Width(), rect.Height(), FXDIB_Rgb32)) {
+ return TRUE;
+ }
+ bitmap_device1.GetBitmap()->Clear(0xffffff);
+ {
+ CPDF_RenderStatus bitmap_render;
+ bitmap_render.Initialize(m_pRenderStatus->m_pContext, &bitmap_device1, NULL,
+ NULL, NULL, NULL, &m_pRenderStatus->m_Options, 0,
+ m_pRenderStatus->m_bDropObjects, NULL, TRUE);
+ CFX_Matrix patternDevice = *pObj2Device;
+ patternDevice.Translate((FX_FLOAT)-rect.left, (FX_FLOAT)-rect.top);
+ if (m_pPattern->m_PatternType == CPDF_Pattern::TILING) {
+ bitmap_render.DrawTilingPattern(
+ static_cast<CPDF_TilingPattern*>(m_pPattern), m_pImageObject,
+ &patternDevice, FALSE);
+ } else {
+ bitmap_render.DrawShadingPattern(
+ static_cast<CPDF_ShadingPattern*>(m_pPattern), m_pImageObject,
+ &patternDevice, FALSE);
+ }
+ }
+ {
+ CFX_FxgeDevice bitmap_device2;
+ if (!bitmap_device2.Create(rect.Width(), rect.Height(), FXDIB_8bppRgb)) {
+ return TRUE;
+ }
+ bitmap_device2.GetBitmap()->Clear(0);
+ CPDF_RenderStatus bitmap_render;
+ bitmap_render.Initialize(m_pRenderStatus->m_pContext, &bitmap_device2, NULL,
+ NULL, NULL, NULL, NULL, 0,
+ m_pRenderStatus->m_bDropObjects, NULL, TRUE);
+ CPDF_ImageRenderer image_render;
+ if (image_render.Start(&bitmap_render, m_pDIBSource, 0xffffffff, 255,
+ &new_matrix, m_Flags, TRUE)) {
+ image_render.Continue(NULL);
+ }
+ if (m_Loader.m_MatteColor != 0xffffffff) {
+ int matte_r = FXARGB_R(m_Loader.m_MatteColor);
+ int matte_g = FXARGB_G(m_Loader.m_MatteColor);
+ int matte_b = FXARGB_B(m_Loader.m_MatteColor);
+ for (int row = 0; row < height; row++) {
+ uint8_t* dest_scan =
+ (uint8_t*)bitmap_device1.GetBitmap()->GetScanline(row);
+ const uint8_t* mask_scan = bitmap_device2.GetBitmap()->GetScanline(row);
+ for (int col = 0; col < width; col++) {
+ int alpha = *mask_scan++;
+ if (alpha) {
+ int orig = (*dest_scan - matte_b) * 255 / alpha + matte_b;
+ if (orig < 0) {
+ orig = 0;
+ } else if (orig > 255) {
+ orig = 255;
+ }
+ *dest_scan++ = orig;
+ orig = (*dest_scan - matte_g) * 255 / alpha + matte_g;
+ if (orig < 0) {
+ orig = 0;
+ } else if (orig > 255) {
+ orig = 255;
+ }
+ *dest_scan++ = orig;
+ orig = (*dest_scan - matte_r) * 255 / alpha + matte_r;
+ if (orig < 0) {
+ orig = 0;
+ } else if (orig > 255) {
+ orig = 255;
+ }
+ *dest_scan++ = orig;
+ dest_scan++;
+ } else {
+ dest_scan += 4;
+ }
+ }
+ }
+ }
+ bitmap_device2.GetBitmap()->ConvertFormat(FXDIB_8bppMask);
+ bitmap_device1.GetBitmap()->MultiplyAlpha(bitmap_device2.GetBitmap());
+ bitmap_device1.GetBitmap()->MultiplyAlpha(255);
+ }
+ m_pRenderStatus->m_pDevice->SetDIBits(bitmap_device1.GetBitmap(), rect.left,
+ rect.top, m_BlendType);
+ return FALSE;
+}
+FX_BOOL CPDF_ImageRenderer::DrawMaskedImage() {
+ if (m_pRenderStatus->m_bPrint &&
+ !(m_pRenderStatus->m_pDevice->GetRenderCaps() & FXRC_BLEND_MODE)) {
+ m_Result = FALSE;
+ return FALSE;
+ }
+ FX_RECT rect = m_ImageMatrix.GetUnitRect().GetOutterRect();
+ rect.Intersect(m_pRenderStatus->m_pDevice->GetClipBox());
+ if (rect.IsEmpty()) {
+ return FALSE;
+ }
+ CFX_Matrix new_matrix = m_ImageMatrix;
+ new_matrix.TranslateI(-rect.left, -rect.top);
+ int width = rect.Width();
+ int height = rect.Height();
+ CFX_FxgeDevice bitmap_device1;
+ if (!bitmap_device1.Create(width, height, FXDIB_Rgb32)) {
+ return TRUE;
+ }
+ bitmap_device1.GetBitmap()->Clear(0xffffff);
+ {
+ CPDF_RenderStatus bitmap_render;
+ bitmap_render.Initialize(m_pRenderStatus->m_pContext, &bitmap_device1, NULL,
+ NULL, NULL, NULL, NULL, 0,
+ m_pRenderStatus->m_bDropObjects, NULL, TRUE);
+ CPDF_ImageRenderer image_render;
+ if (image_render.Start(&bitmap_render, m_pDIBSource, 0, 255, &new_matrix,
+ m_Flags, TRUE)) {
+ image_render.Continue(NULL);
+ }
+ }
+ {
+ CFX_FxgeDevice bitmap_device2;
+ if (!bitmap_device2.Create(width, height, FXDIB_8bppRgb)) {
+ return TRUE;
+ }
+ bitmap_device2.GetBitmap()->Clear(0);
+ CPDF_RenderStatus bitmap_render;
+ bitmap_render.Initialize(m_pRenderStatus->m_pContext, &bitmap_device2, NULL,
+ NULL, NULL, NULL, NULL, 0,
+ m_pRenderStatus->m_bDropObjects, NULL, TRUE);
+ CPDF_ImageRenderer image_render;
+ if (image_render.Start(&bitmap_render, m_Loader.m_pMask, 0xffffffff, 255,
+ &new_matrix, m_Flags, TRUE)) {
+ image_render.Continue(NULL);
+ }
+ if (m_Loader.m_MatteColor != 0xffffffff) {
+ int matte_r = FXARGB_R(m_Loader.m_MatteColor);
+ int matte_g = FXARGB_G(m_Loader.m_MatteColor);
+ int matte_b = FXARGB_B(m_Loader.m_MatteColor);
+ for (int row = 0; row < height; row++) {
+ uint8_t* dest_scan =
+ (uint8_t*)bitmap_device1.GetBitmap()->GetScanline(row);
+ const uint8_t* mask_scan = bitmap_device2.GetBitmap()->GetScanline(row);
+ for (int col = 0; col < width; col++) {
+ int alpha = *mask_scan++;
+ if (alpha) {
+ int orig = (*dest_scan - matte_b) * 255 / alpha + matte_b;
+ if (orig < 0) {
+ orig = 0;
+ } else if (orig > 255) {
+ orig = 255;
+ }
+ *dest_scan++ = orig;
+ orig = (*dest_scan - matte_g) * 255 / alpha + matte_g;
+ if (orig < 0) {
+ orig = 0;
+ } else if (orig > 255) {
+ orig = 255;
+ }
+ *dest_scan++ = orig;
+ orig = (*dest_scan - matte_r) * 255 / alpha + matte_r;
+ if (orig < 0) {
+ orig = 0;
+ } else if (orig > 255) {
+ orig = 255;
+ }
+ *dest_scan++ = orig;
+ dest_scan++;
+ } else {
+ dest_scan += 4;
+ }
+ }
+ }
+ }
+ bitmap_device2.GetBitmap()->ConvertFormat(FXDIB_8bppMask);
+ bitmap_device1.GetBitmap()->MultiplyAlpha(bitmap_device2.GetBitmap());
+ if (m_BitmapAlpha < 255) {
+ bitmap_device1.GetBitmap()->MultiplyAlpha(m_BitmapAlpha);
+ }
+ }
+ m_pRenderStatus->m_pDevice->SetDIBits(bitmap_device1.GetBitmap(), rect.left,
+ rect.top, m_BlendType);
+ return FALSE;
+}
+FX_BOOL CPDF_ImageRenderer::StartDIBSource() {
+ if (!(m_Flags & RENDER_FORCE_DOWNSAMPLE) && m_pDIBSource->GetBPP() > 1) {
+ int image_size = m_pDIBSource->GetBPP() / 8 * m_pDIBSource->GetWidth() *
+ m_pDIBSource->GetHeight();
+ if (image_size > FPDF_HUGE_IMAGE_SIZE &&
+ !(m_Flags & RENDER_FORCE_HALFTONE)) {
+ m_Flags |= RENDER_FORCE_DOWNSAMPLE;
+ }
+ }
+ if (m_pRenderStatus->m_pDevice->StartDIBits(
+ m_pDIBSource, m_BitmapAlpha, m_FillArgb, &m_ImageMatrix, m_Flags,
+ m_DeviceHandle, 0, NULL, m_BlendType)) {
+ if (m_DeviceHandle) {
+ m_Status = 3;
+ return TRUE;
+ }
+ return FALSE;
+ }
+ CFX_FloatRect image_rect_f = m_ImageMatrix.GetUnitRect();
+ FX_RECT image_rect = image_rect_f.GetOutterRect();
+ int dest_width = image_rect.Width();
+ int dest_height = image_rect.Height();
+ if ((FXSYS_fabs(m_ImageMatrix.b) >= 0.5f || m_ImageMatrix.a == 0) ||
+ (FXSYS_fabs(m_ImageMatrix.c) >= 0.5f || m_ImageMatrix.d == 0)) {
+ if (m_pRenderStatus->m_bPrint &&
+ !(m_pRenderStatus->m_pDevice->GetRenderCaps() & FXRC_BLEND_MODE)) {
+ m_Result = FALSE;
+ return FALSE;
+ }
+ FX_RECT clip_box = m_pRenderStatus->m_pDevice->GetClipBox();
+ clip_box.Intersect(image_rect);
+ m_Status = 2;
+ m_pTransformer = new CFX_ImageTransformer;
+ m_pTransformer->Start(m_pDIBSource, &m_ImageMatrix, m_Flags, &clip_box);
+ return TRUE;
+ }
+ if (m_ImageMatrix.a < 0) {
+ dest_width = -dest_width;
+ }
+ if (m_ImageMatrix.d > 0) {
+ dest_height = -dest_height;
+ }
+ int dest_left, dest_top;
+ dest_left = dest_width > 0 ? image_rect.left : image_rect.right;
+ dest_top = dest_height > 0 ? image_rect.top : image_rect.bottom;
+ if (m_pDIBSource->IsOpaqueImage() && m_BitmapAlpha == 255) {
+ if (m_pRenderStatus->m_pDevice->StretchDIBits(
+ m_pDIBSource, dest_left, dest_top, dest_width, dest_height, m_Flags,
+ NULL, m_BlendType)) {
+ return FALSE;
+ }
+ }
+ if (m_pDIBSource->IsAlphaMask()) {
+ if (m_BitmapAlpha != 255) {
+ m_FillArgb = FXARGB_MUL_ALPHA(m_FillArgb, m_BitmapAlpha);
+ }
+ if (m_pRenderStatus->m_pDevice->StretchBitMask(
+ m_pDIBSource, dest_left, dest_top, dest_width, dest_height,
+ m_FillArgb, m_Flags)) {
+ return FALSE;
+ }
+ }
+ if (m_pRenderStatus->m_bPrint &&
+ !(m_pRenderStatus->m_pDevice->GetRenderCaps() & FXRC_BLEND_MODE)) {
+ m_Result = FALSE;
+ return TRUE;
+ }
+ FX_RECT clip_box = m_pRenderStatus->m_pDevice->GetClipBox();
+ FX_RECT dest_rect = clip_box;
+ dest_rect.Intersect(image_rect);
+ FX_RECT dest_clip(
+ dest_rect.left - image_rect.left, dest_rect.top - image_rect.top,
+ dest_rect.right - image_rect.left, dest_rect.bottom - image_rect.top);
+ std::unique_ptr<CFX_DIBitmap> pStretched(
+ m_pDIBSource->StretchTo(dest_width, dest_height, m_Flags, &dest_clip));
+ if (pStretched) {
+ m_pRenderStatus->CompositeDIBitmap(pStretched.get(), dest_rect.left,
+ dest_rect.top, m_FillArgb, m_BitmapAlpha,
+ m_BlendType, FALSE);
+ }
+ return FALSE;
+}
+FX_BOOL CPDF_ImageRenderer::StartBitmapAlpha() {
+ if (m_pDIBSource->IsOpaqueImage()) {
+ CFX_PathData path;
+ path.AppendRect(0, 0, 1, 1);
+ path.Transform(&m_ImageMatrix);
+ FX_DWORD fill_color =
+ ArgbEncode(0xff, m_BitmapAlpha, m_BitmapAlpha, m_BitmapAlpha);
+ m_pRenderStatus->m_pDevice->DrawPath(&path, NULL, NULL, fill_color, 0,
+ FXFILL_WINDING);
+ } else {
+ const CFX_DIBSource* pAlphaMask = m_pDIBSource->IsAlphaMask()
+ ? m_pDIBSource
+ : m_pDIBSource->GetAlphaMask();
+ if (FXSYS_fabs(m_ImageMatrix.b) >= 0.5f ||
+ FXSYS_fabs(m_ImageMatrix.c) >= 0.5f) {
+ int left, top;
+ std::unique_ptr<CFX_DIBitmap> pTransformed(
+ pAlphaMask->TransformTo(&m_ImageMatrix, left, top));
+ if (!pTransformed)
+ return TRUE;
+
+ m_pRenderStatus->m_pDevice->SetBitMask(
+ pTransformed.get(), left, top,
+ ArgbEncode(0xff, m_BitmapAlpha, m_BitmapAlpha, m_BitmapAlpha));
+ } else {
+ CFX_FloatRect image_rect_f = m_ImageMatrix.GetUnitRect();
+ FX_RECT image_rect = image_rect_f.GetOutterRect();
+ int dest_width =
+ m_ImageMatrix.a > 0 ? image_rect.Width() : -image_rect.Width();
+ int dest_height =
+ m_ImageMatrix.d > 0 ? -image_rect.Height() : image_rect.Height();
+ int left = dest_width > 0 ? image_rect.left : image_rect.right;
+ int top = dest_height > 0 ? image_rect.top : image_rect.bottom;
+ m_pRenderStatus->m_pDevice->StretchBitMask(
+ pAlphaMask, left, top, dest_width, dest_height,
+ ArgbEncode(0xff, m_BitmapAlpha, m_BitmapAlpha, m_BitmapAlpha));
+ }
+ if (m_pDIBSource != pAlphaMask) {
+ delete pAlphaMask;
+ }
+ }
+ return FALSE;
+}
+FX_BOOL CPDF_ImageRenderer::Continue(IFX_Pause* pPause) {
+ if (m_Status == 2) {
+ if (m_pTransformer->Continue(pPause)) {
+ return TRUE;
+ }
+ CFX_DIBitmap* pBitmap = m_pTransformer->m_Storer.Detach();
+ if (!pBitmap) {
+ return FALSE;
+ }
+ if (pBitmap->IsAlphaMask()) {
+ if (m_BitmapAlpha != 255) {
+ m_FillArgb = FXARGB_MUL_ALPHA(m_FillArgb, m_BitmapAlpha);
+ }
+ m_Result = m_pRenderStatus->m_pDevice->SetBitMask(
+ pBitmap, m_pTransformer->m_ResultLeft, m_pTransformer->m_ResultTop,
+ m_FillArgb);
+ } else {
+ if (m_BitmapAlpha != 255) {
+ pBitmap->MultiplyAlpha(m_BitmapAlpha);
+ }
+ m_Result = m_pRenderStatus->m_pDevice->SetDIBits(
+ pBitmap, m_pTransformer->m_ResultLeft, m_pTransformer->m_ResultTop,
+ m_BlendType);
+ }
+ delete pBitmap;
+ return FALSE;
+ }
+ if (m_Status == 3) {
+ return m_pRenderStatus->m_pDevice->ContinueDIBits(m_DeviceHandle, pPause);
+ }
+ if (m_Status == 4) {
+ if (m_Loader.Continue(m_LoadHandle, pPause)) {
+ return TRUE;
+ }
+ if (StartRenderDIBSource()) {
+ return Continue(pPause);
+ }
+ }
+ return FALSE;
+}
+ICodec_ScanlineDecoder* FPDFAPI_CreateFlateDecoder(
+ const uint8_t* src_buf,
+ FX_DWORD src_size,
+ int width,
+ int height,
+ int nComps,
+ int bpc,
+ const CPDF_Dictionary* pParams);
+CFX_DIBitmap* CPDF_RenderStatus::LoadSMask(CPDF_Dictionary* pSMaskDict,
+ FX_RECT* pClipRect,
+ const CFX_Matrix* pMatrix) {
+ if (!pSMaskDict) {
+ return NULL;
+ }
+ int width = pClipRect->right - pClipRect->left;
+ int height = pClipRect->bottom - pClipRect->top;
+ FX_BOOL bLuminosity = FALSE;
+ bLuminosity = pSMaskDict->GetConstStringBy("S") != "Alpha";
+ CPDF_Stream* pGroup = pSMaskDict->GetStreamBy("G");
+ if (!pGroup) {
+ return NULL;
+ }
+ std::unique_ptr<CPDF_Function> pFunc;
+ CPDF_Object* pFuncObj = pSMaskDict->GetElementValue("TR");
+ if (pFuncObj && (pFuncObj->IsDictionary() || pFuncObj->IsStream()))
+ pFunc.reset(CPDF_Function::Load(pFuncObj));
+
+ CFX_Matrix matrix = *pMatrix;
+ matrix.TranslateI(-pClipRect->left, -pClipRect->top);
+ CPDF_Form form(m_pContext->GetDocument(), m_pContext->GetPageResources(),
+ pGroup);
+ form.ParseContent(NULL, NULL, NULL, NULL);
+ CFX_FxgeDevice bitmap_device;
+#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
+ if (!bitmap_device.Create(width, height,
+ bLuminosity ? FXDIB_Rgb32 : FXDIB_8bppMask)) {
+ return NULL;
+ }
+#else
+ if (!bitmap_device.Create(width, height,
+ bLuminosity ? FXDIB_Rgb : FXDIB_8bppMask)) {
+ return NULL;
+ }
+#endif
+ CFX_DIBitmap& bitmap = *bitmap_device.GetBitmap();
+ CPDF_Object* pCSObj = NULL;
+ CPDF_ColorSpace* pCS = NULL;
+ if (bLuminosity) {
+ CPDF_Array* pBC = pSMaskDict->GetArrayBy("BC");
+ FX_ARGB back_color = 0xff000000;
+ if (pBC) {
+ CPDF_Dictionary* pDict = pGroup->GetDict();
+ if (pDict && pDict->GetDictBy("Group"))
+ pCSObj = pDict->GetDictBy("Group")->GetElementValue("CS");
+ else
+ pCSObj = NULL;
+ pCS = m_pContext->GetDocument()->LoadColorSpace(pCSObj);
+ if (pCS) {
+ FX_FLOAT R, G, B;
+ FX_DWORD comps = 8;
+ if (pCS->CountComponents() > comps) {
+ comps = pCS->CountComponents();
+ }
+ CFX_FixedBufGrow<FX_FLOAT, 8> float_array(comps);
+ FX_FLOAT* pFloats = float_array;
+ FX_SAFE_DWORD num_floats = comps;
+ num_floats *= sizeof(FX_FLOAT);
+ if (!num_floats.IsValid()) {
+ return NULL;
+ }
+ FXSYS_memset(pFloats, 0, num_floats.ValueOrDie());
+ int count = pBC->GetCount() > 8 ? 8 : pBC->GetCount();
+ for (int i = 0; i < count; i++) {
+ pFloats[i] = pBC->GetNumberAt(i);
+ }
+ pCS->GetRGB(pFloats, R, G, B);
+ back_color = 0xff000000 | ((int32_t)(R * 255) << 16) |
+ ((int32_t)(G * 255) << 8) | (int32_t)(B * 255);
+ m_pContext->GetDocument()->GetPageData()->ReleaseColorSpace(pCSObj);
+ }
+ }
+ bitmap.Clear(back_color);
+ } else {
+ bitmap.Clear(0);
+ }
+ CPDF_Dictionary* pFormResource = NULL;
+ if (form.m_pFormDict) {
+ pFormResource = form.m_pFormDict->GetDictBy("Resources");
+ }
+ CPDF_RenderOptions options;
+ options.m_ColorMode = bLuminosity ? RENDER_COLOR_NORMAL : RENDER_COLOR_ALPHA;
+ CPDF_RenderStatus status;
+ status.Initialize(m_pContext, &bitmap_device, NULL, NULL, NULL, NULL,
+ &options, 0, m_bDropObjects, pFormResource, TRUE, NULL, 0,
+ pCS ? pCS->GetFamily() : 0, bLuminosity);
+ status.RenderObjectList(&form, &matrix);
+ std::unique_ptr<CFX_DIBitmap> pMask(new CFX_DIBitmap);
+ if (!pMask->Create(width, height, FXDIB_8bppMask))
+ return nullptr;
+
+ uint8_t* dest_buf = pMask->GetBuffer();
+ int dest_pitch = pMask->GetPitch();
+ uint8_t* src_buf = bitmap.GetBuffer();
+ int src_pitch = bitmap.GetPitch();
+ std::vector<uint8_t> transfers(256);
+ if (pFunc) {
+ CFX_FixedBufGrow<FX_FLOAT, 16> results(pFunc->CountOutputs());
+ for (int i = 0; i < 256; i++) {
+ FX_FLOAT input = (FX_FLOAT)i / 255.0f;
+ int nresult;
+ pFunc->Call(&input, 1, results, nresult);
+ transfers[i] = FXSYS_round(results[0] * 255);
+ }
+ } else {
+ for (int i = 0; i < 256; i++) {
+ transfers[i] = i;
+ }
+ }
+ if (bLuminosity) {
+ int Bpp = bitmap.GetBPP() / 8;
+ for (int row = 0; row < height; row++) {
+ uint8_t* dest_pos = dest_buf + row * dest_pitch;
+ uint8_t* src_pos = src_buf + row * src_pitch;
+ for (int col = 0; col < width; col++) {
+ *dest_pos++ = transfers[FXRGB2GRAY(src_pos[2], src_pos[1], *src_pos)];
+ src_pos += Bpp;
+ }
+ }
+ } else if (pFunc) {
+ int size = dest_pitch * height;
+ for (int i = 0; i < size; i++) {
+ dest_buf[i] = transfers[src_buf[i]];
+ }
+ } else {
+ FXSYS_memcpy(dest_buf, src_buf, dest_pitch * height);
+ }
+ return pMask.release();
+}
diff --git a/core/fpdfapi/fpdf_render/fpdf_render_loadimage.cpp b/core/fpdfapi/fpdf_render/fpdf_render_loadimage.cpp
new file mode 100644
index 0000000000..f1a06c4fc7
--- /dev/null
+++ b/core/fpdfapi/fpdf_render/fpdf_render_loadimage.cpp
@@ -0,0 +1,1654 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fpdfapi/fpdf_render/render_int.h"
+
+#include <algorithm>
+#include <memory>
+#include <vector>
+
+#include "core/fpdfapi/fpdf_page/pageint.h"
+#include "core/include/fpdfapi/cpdf_array.h"
+#include "core/include/fpdfapi/cpdf_dictionary.h"
+#include "core/include/fpdfapi/cpdf_document.h"
+#include "core/include/fpdfapi/fpdf_module.h"
+#include "core/include/fpdfapi/fpdf_pageobj.h"
+#include "core/include/fpdfapi/fpdf_render.h"
+#include "core/include/fxcodec/fx_codec.h"
+#include "core/include/fxcrt/fx_safe_types.h"
+#include "core/include/fxge/fx_ge.h"
+
+namespace {
+
+unsigned int GetBits8(const uint8_t* pData, uint64_t bitpos, size_t nbits) {
+ ASSERT(nbits == 1 || nbits == 2 || nbits == 4 || nbits == 8 || nbits == 16);
+ ASSERT((bitpos & (nbits - 1)) == 0);
+ unsigned int byte = pData[bitpos / 8];
+ if (nbits == 8) {
+ return byte;
+ } else if (nbits == 16) {
+ return byte * 256 + pData[bitpos / 8 + 1];
+ }
+
+ return (byte >> (8 - nbits - (bitpos % 8))) & ((1 << nbits) - 1);
+}
+
+FX_SAFE_DWORD CalculatePitch8(FX_DWORD bpc, FX_DWORD components, int width) {
+ FX_SAFE_DWORD pitch = bpc;
+ pitch *= components;
+ pitch *= width;
+ pitch += 7;
+ pitch /= 8;
+ return pitch;
+}
+
+FX_SAFE_DWORD CalculatePitch32(int bpp, int width) {
+ FX_SAFE_DWORD pitch = bpp;
+ pitch *= width;
+ pitch += 31;
+ pitch /= 32; // quantized to number of 32-bit words.
+ pitch *= 4; // and then back to bytes, (not just /8 in one step).
+ return pitch;
+}
+
+bool IsAllowedBPCValue(int bpc) {
+ return bpc == 1 || bpc == 2 || bpc == 4 || bpc == 8 || bpc == 16;
+}
+
+template <typename T>
+T ClampValue(T value, T max_value) {
+ value = std::min(value, max_value);
+ value = std::max<T>(0, value);
+ return value;
+}
+
+// Wrapper class to use with std::unique_ptr for CJPX_Decoder.
+class JpxBitMapContext {
+ public:
+ explicit JpxBitMapContext(ICodec_JpxModule* jpx_module)
+ : jpx_module_(jpx_module), decoder_(nullptr) {}
+
+ ~JpxBitMapContext() { jpx_module_->DestroyDecoder(decoder_); }
+
+ // Takes ownership of |decoder|.
+ void set_decoder(CJPX_Decoder* decoder) { decoder_ = decoder; }
+
+ CJPX_Decoder* decoder() { return decoder_; }
+
+ private:
+ ICodec_JpxModule* const jpx_module_; // Weak pointer.
+ CJPX_Decoder* decoder_; // Decoder, owned.
+
+ // Disallow evil constructors
+ JpxBitMapContext(const JpxBitMapContext&);
+ void operator=(const JpxBitMapContext&);
+};
+
+const int kMaxImageDimension = 0x01FFFF;
+
+} // namespace
+
+CFX_DIBSource* CPDF_Image::LoadDIBSource(CFX_DIBSource** ppMask,
+ FX_DWORD* pMatteColor,
+ FX_BOOL bStdCS,
+ FX_DWORD GroupFamily,
+ FX_BOOL bLoadMask) const {
+ std::unique_ptr<CPDF_DIBSource> source(new CPDF_DIBSource);
+ if (source->Load(m_pDocument, m_pStream,
+ reinterpret_cast<CPDF_DIBSource**>(ppMask), pMatteColor,
+ nullptr, nullptr, bStdCS, GroupFamily, bLoadMask)) {
+ return source.release();
+ }
+ return nullptr;
+}
+
+CFX_DIBSource* CPDF_Image::DetachBitmap() {
+ CFX_DIBSource* pBitmap = m_pDIBSource;
+ m_pDIBSource = nullptr;
+ return pBitmap;
+}
+
+CFX_DIBSource* CPDF_Image::DetachMask() {
+ CFX_DIBSource* pBitmap = m_pMask;
+ m_pMask = nullptr;
+ return pBitmap;
+}
+
+FX_BOOL CPDF_Image::StartLoadDIBSource(CPDF_Dictionary* pFormResource,
+ CPDF_Dictionary* pPageResource,
+ FX_BOOL bStdCS,
+ FX_DWORD GroupFamily,
+ FX_BOOL bLoadMask) {
+ std::unique_ptr<CPDF_DIBSource> source(new CPDF_DIBSource);
+ int ret =
+ source->StartLoadDIBSource(m_pDocument, m_pStream, TRUE, pFormResource,
+ pPageResource, bStdCS, GroupFamily, bLoadMask);
+ if (ret == 2) {
+ m_pDIBSource = source.release();
+ return TRUE;
+ }
+ if (!ret) {
+ m_pDIBSource = nullptr;
+ return FALSE;
+ }
+ m_pMask = source->DetachMask();
+ m_MatteColor = source->GetMatteColor();
+ m_pDIBSource = source.release();
+ return FALSE;
+}
+
+FX_BOOL CPDF_Image::Continue(IFX_Pause* pPause) {
+ CPDF_DIBSource* pSource = static_cast<CPDF_DIBSource*>(m_pDIBSource);
+ int ret = pSource->ContinueLoadDIBSource(pPause);
+ if (ret == 2) {
+ return TRUE;
+ }
+ if (!ret) {
+ delete m_pDIBSource;
+ m_pDIBSource = nullptr;
+ return FALSE;
+ }
+ m_pMask = pSource->DetachMask();
+ m_MatteColor = pSource->GetMatteColor();
+ return FALSE;
+}
+
+CPDF_DIBSource::CPDF_DIBSource()
+ : m_pDocument(nullptr),
+ m_pStream(nullptr),
+ m_pDict(nullptr),
+ m_pColorSpace(nullptr),
+ m_Family(0),
+ m_bpc(0),
+ m_bpc_orig(0),
+ m_nComponents(0),
+ m_GroupFamily(0),
+ m_MatteColor(0),
+ m_bLoadMask(FALSE),
+ m_bDefaultDecode(TRUE),
+ m_bImageMask(FALSE),
+ m_bDoBpcCheck(TRUE),
+ m_bColorKey(FALSE),
+ m_bHasMask(FALSE),
+ m_bStdCS(FALSE),
+ m_pCompData(nullptr),
+ m_pLineBuf(nullptr),
+ m_pMaskedLine(nullptr),
+ m_pJbig2Context(nullptr),
+ m_pMask(nullptr),
+ m_pMaskStream(nullptr),
+ m_Status(0) {}
+
+CPDF_DIBSource::~CPDF_DIBSource() {
+ FX_Free(m_pMaskedLine);
+ FX_Free(m_pLineBuf);
+ m_pCachedBitmap.reset();
+ FX_Free(m_pCompData);
+ CPDF_ColorSpace* pCS = m_pColorSpace;
+ if (pCS && m_pDocument) {
+ m_pDocument->GetPageData()->ReleaseColorSpace(pCS->GetArray());
+ }
+ if (m_pJbig2Context) {
+ ICodec_Jbig2Module* pJbig2Module = CPDF_ModuleMgr::Get()->GetJbig2Module();
+ pJbig2Module->DestroyJbig2Context(m_pJbig2Context);
+ }
+}
+
+CFX_DIBitmap* CPDF_DIBSource::GetBitmap() const {
+ return m_pCachedBitmap ? m_pCachedBitmap.get() : Clone();
+}
+
+void CPDF_DIBSource::ReleaseBitmap(CFX_DIBitmap* pBitmap) const {
+ if (pBitmap && pBitmap != m_pCachedBitmap.get()) {
+ delete pBitmap;
+ }
+}
+
+FX_BOOL CPDF_DIBSource::Load(CPDF_Document* pDoc,
+ const CPDF_Stream* pStream,
+ CPDF_DIBSource** ppMask,
+ FX_DWORD* pMatteColor,
+ CPDF_Dictionary* pFormResources,
+ CPDF_Dictionary* pPageResources,
+ FX_BOOL bStdCS,
+ FX_DWORD GroupFamily,
+ FX_BOOL bLoadMask) {
+ if (!pStream) {
+ return FALSE;
+ }
+ m_pDocument = pDoc;
+ m_pDict = pStream->GetDict();
+ if (!m_pDict) {
+ return FALSE;
+ }
+ m_pStream = pStream;
+ m_Width = m_pDict->GetIntegerBy("Width");
+ m_Height = m_pDict->GetIntegerBy("Height");
+ if (m_Width <= 0 || m_Height <= 0 || m_Width > kMaxImageDimension ||
+ m_Height > kMaxImageDimension) {
+ return FALSE;
+ }
+ m_GroupFamily = GroupFamily;
+ m_bLoadMask = bLoadMask;
+ if (!LoadColorInfo(m_pStream->GetObjNum() != 0 ? nullptr : pFormResources,
+ pPageResources)) {
+ return FALSE;
+ }
+ if (m_bDoBpcCheck && (m_bpc == 0 || m_nComponents == 0)) {
+ return FALSE;
+ }
+ FX_SAFE_DWORD src_size =
+ CalculatePitch8(m_bpc, m_nComponents, m_Width) * m_Height;
+ if (!src_size.IsValid()) {
+ return FALSE;
+ }
+ m_pStreamAcc.reset(new CPDF_StreamAcc);
+ m_pStreamAcc->LoadAllData(pStream, FALSE, src_size.ValueOrDie(), TRUE);
+ if (m_pStreamAcc->GetSize() == 0 || !m_pStreamAcc->GetData()) {
+ return FALSE;
+ }
+ if (!CreateDecoder()) {
+ return FALSE;
+ }
+ if (m_bImageMask) {
+ m_bpp = 1;
+ m_bpc = 1;
+ m_nComponents = 1;
+ m_AlphaFlag = 1;
+ } else if (m_bpc * m_nComponents == 1) {
+ m_bpp = 1;
+ } else if (m_bpc * m_nComponents <= 8) {
+ m_bpp = 8;
+ } else {
+ m_bpp = 24;
+ }
+ FX_SAFE_DWORD pitch = CalculatePitch32(m_bpp, m_Width);
+ if (!pitch.IsValid()) {
+ return FALSE;
+ }
+ m_pLineBuf = FX_Alloc(uint8_t, pitch.ValueOrDie());
+ if (m_pColorSpace && bStdCS) {
+ m_pColorSpace->EnableStdConversion(TRUE);
+ }
+ LoadPalette();
+ if (m_bColorKey) {
+ m_bpp = 32;
+ m_AlphaFlag = 2;
+ pitch = CalculatePitch32(m_bpp, m_Width);
+ if (!pitch.IsValid()) {
+ return FALSE;
+ }
+ m_pMaskedLine = FX_Alloc(uint8_t, pitch.ValueOrDie());
+ }
+ m_Pitch = pitch.ValueOrDie();
+ if (ppMask) {
+ *ppMask = LoadMask(*pMatteColor);
+ }
+ if (m_pColorSpace && bStdCS) {
+ m_pColorSpace->EnableStdConversion(FALSE);
+ }
+ return TRUE;
+}
+
+int CPDF_DIBSource::ContinueToLoadMask() {
+ if (m_bImageMask) {
+ m_bpp = 1;
+ m_bpc = 1;
+ m_nComponents = 1;
+ m_AlphaFlag = 1;
+ } else if (m_bpc * m_nComponents == 1) {
+ m_bpp = 1;
+ } else if (m_bpc * m_nComponents <= 8) {
+ m_bpp = 8;
+ } else {
+ m_bpp = 24;
+ }
+ if (!m_bpc || !m_nComponents) {
+ return 0;
+ }
+ FX_SAFE_DWORD pitch = CalculatePitch32(m_bpp, m_Width);
+ if (!pitch.IsValid()) {
+ return 0;
+ }
+ m_pLineBuf = FX_Alloc(uint8_t, pitch.ValueOrDie());
+ if (m_pColorSpace && m_bStdCS) {
+ m_pColorSpace->EnableStdConversion(TRUE);
+ }
+ LoadPalette();
+ if (m_bColorKey) {
+ m_bpp = 32;
+ m_AlphaFlag = 2;
+ pitch = CalculatePitch32(m_bpp, m_Width);
+ if (!pitch.IsValid()) {
+ return 0;
+ }
+ m_pMaskedLine = FX_Alloc(uint8_t, pitch.ValueOrDie());
+ }
+ m_Pitch = pitch.ValueOrDie();
+ return 1;
+}
+
+int CPDF_DIBSource::StartLoadDIBSource(CPDF_Document* pDoc,
+ const CPDF_Stream* pStream,
+ FX_BOOL bHasMask,
+ CPDF_Dictionary* pFormResources,
+ CPDF_Dictionary* pPageResources,
+ FX_BOOL bStdCS,
+ FX_DWORD GroupFamily,
+ FX_BOOL bLoadMask) {
+ if (!pStream) {
+ return 0;
+ }
+ m_pDocument = pDoc;
+ m_pDict = pStream->GetDict();
+ m_pStream = pStream;
+ m_bStdCS = bStdCS;
+ m_bHasMask = bHasMask;
+ m_Width = m_pDict->GetIntegerBy("Width");
+ m_Height = m_pDict->GetIntegerBy("Height");
+ if (m_Width <= 0 || m_Height <= 0 || m_Width > kMaxImageDimension ||
+ m_Height > kMaxImageDimension) {
+ return 0;
+ }
+ m_GroupFamily = GroupFamily;
+ m_bLoadMask = bLoadMask;
+ if (!LoadColorInfo(m_pStream->GetObjNum() != 0 ? nullptr : pFormResources,
+ pPageResources)) {
+ return 0;
+ }
+ if (m_bDoBpcCheck && (m_bpc == 0 || m_nComponents == 0)) {
+ return 0;
+ }
+ FX_SAFE_DWORD src_size =
+ CalculatePitch8(m_bpc, m_nComponents, m_Width) * m_Height;
+ if (!src_size.IsValid()) {
+ return 0;
+ }
+ m_pStreamAcc.reset(new CPDF_StreamAcc);
+ m_pStreamAcc->LoadAllData(pStream, FALSE, src_size.ValueOrDie(), TRUE);
+ if (m_pStreamAcc->GetSize() == 0 || !m_pStreamAcc->GetData()) {
+ return 0;
+ }
+ int ret = CreateDecoder();
+ if (!ret)
+ return ret;
+
+ if (ret != 1) {
+ if (!ContinueToLoadMask()) {
+ return 0;
+ }
+ if (m_bHasMask) {
+ StratLoadMask();
+ }
+ return ret;
+ }
+ if (!ContinueToLoadMask()) {
+ return 0;
+ }
+ if (m_bHasMask) {
+ ret = StratLoadMask();
+ }
+ if (ret == 2) {
+ return ret;
+ }
+ if (m_pColorSpace && m_bStdCS) {
+ m_pColorSpace->EnableStdConversion(FALSE);
+ }
+ return ret;
+}
+
+int CPDF_DIBSource::ContinueLoadDIBSource(IFX_Pause* pPause) {
+ FXCODEC_STATUS ret;
+ if (m_Status == 1) {
+ const CFX_ByteString& decoder = m_pStreamAcc->GetImageDecoder();
+ if (decoder == "JPXDecode") {
+ return 0;
+ }
+ ICodec_Jbig2Module* pJbig2Module = CPDF_ModuleMgr::Get()->GetJbig2Module();
+ if (!m_pJbig2Context) {
+ m_pJbig2Context = pJbig2Module->CreateJbig2Context();
+ if (m_pStreamAcc->GetImageParam()) {
+ CPDF_Stream* pGlobals =
+ m_pStreamAcc->GetImageParam()->GetStreamBy("JBIG2Globals");
+ if (pGlobals) {
+ m_pGlobalStream.reset(new CPDF_StreamAcc);
+ m_pGlobalStream->LoadAllData(pGlobals, FALSE);
+ }
+ }
+ ret = pJbig2Module->StartDecode(
+ m_pJbig2Context, m_pDocument, m_Width, m_Height, m_pStreamAcc.get(),
+ m_pGlobalStream.get(), m_pCachedBitmap->GetBuffer(),
+ m_pCachedBitmap->GetPitch(), pPause);
+ if (ret < 0) {
+ m_pCachedBitmap.reset();
+ m_pGlobalStream.reset();
+ pJbig2Module->DestroyJbig2Context(m_pJbig2Context);
+ m_pJbig2Context = nullptr;
+ return 0;
+ }
+ if (ret == FXCODEC_STATUS_DECODE_TOBECONTINUE) {
+ return 2;
+ }
+ int ret1 = 1;
+ if (m_bHasMask) {
+ ret1 = ContinueLoadMaskDIB(pPause);
+ m_Status = 2;
+ }
+ if (ret1 == 2) {
+ return ret1;
+ }
+ if (m_pColorSpace && m_bStdCS) {
+ m_pColorSpace->EnableStdConversion(FALSE);
+ }
+ return ret1;
+ }
+ FXCODEC_STATUS ret = pJbig2Module->ContinueDecode(m_pJbig2Context, pPause);
+ if (ret < 0) {
+ m_pCachedBitmap.reset();
+ m_pGlobalStream.reset();
+ pJbig2Module->DestroyJbig2Context(m_pJbig2Context);
+ m_pJbig2Context = nullptr;
+ return 0;
+ }
+ if (ret == FXCODEC_STATUS_DECODE_TOBECONTINUE) {
+ return 2;
+ }
+ int ret1 = 1;
+ if (m_bHasMask) {
+ ret1 = ContinueLoadMaskDIB(pPause);
+ m_Status = 2;
+ }
+ if (ret1 == 2) {
+ return ret1;
+ }
+ if (m_pColorSpace && m_bStdCS) {
+ m_pColorSpace->EnableStdConversion(FALSE);
+ }
+ return ret1;
+ }
+ if (m_Status == 2) {
+ return ContinueLoadMaskDIB(pPause);
+ }
+ return 0;
+}
+
+bool CPDF_DIBSource::LoadColorInfo(const CPDF_Dictionary* pFormResources,
+ const CPDF_Dictionary* pPageResources) {
+ m_bpc_orig = m_pDict->GetIntegerBy("BitsPerComponent");
+ if (m_pDict->GetIntegerBy("ImageMask"))
+ m_bImageMask = TRUE;
+
+ if (m_bImageMask || !m_pDict->KeyExist("ColorSpace")) {
+ if (!m_bImageMask) {
+ CPDF_Object* pFilter = m_pDict->GetElementValue("Filter");
+ if (pFilter) {
+ CFX_ByteString filter;
+ if (pFilter->IsName()) {
+ filter = pFilter->GetString();
+ } else if (CPDF_Array* pArray = pFilter->AsArray()) {
+ filter = pArray->GetStringAt(pArray->GetCount() - 1);
+ }
+
+ if (filter == "JPXDecode") {
+ m_bDoBpcCheck = FALSE;
+ return true;
+ }
+ }
+ }
+ m_bImageMask = TRUE;
+ m_bpc = m_nComponents = 1;
+ CPDF_Array* pDecode = m_pDict->GetArrayBy("Decode");
+ m_bDefaultDecode = !pDecode || !pDecode->GetIntegerAt(0);
+ return true;
+ }
+
+ CPDF_Object* pCSObj = m_pDict->GetElementValue("ColorSpace");
+ if (!pCSObj)
+ return false;
+
+ CPDF_DocPageData* pDocPageData = m_pDocument->GetPageData();
+ if (pFormResources)
+ m_pColorSpace = pDocPageData->GetColorSpace(pCSObj, pFormResources);
+ if (!m_pColorSpace)
+ m_pColorSpace = pDocPageData->GetColorSpace(pCSObj, pPageResources);
+ if (!m_pColorSpace)
+ return false;
+
+ m_Family = m_pColorSpace->GetFamily();
+ m_nComponents = m_pColorSpace->CountComponents();
+ if (m_Family == PDFCS_ICCBASED && pCSObj->IsName()) {
+ CFX_ByteString cs = pCSObj->GetString();
+ if (cs == "DeviceGray") {
+ m_nComponents = 1;
+ } else if (cs == "DeviceRGB") {
+ m_nComponents = 3;
+ } else if (cs == "DeviceCMYK") {
+ m_nComponents = 4;
+ }
+ }
+ ValidateDictParam();
+ m_pCompData = GetDecodeAndMaskArray(m_bDefaultDecode, m_bColorKey);
+ return !!m_pCompData;
+}
+
+DIB_COMP_DATA* CPDF_DIBSource::GetDecodeAndMaskArray(FX_BOOL& bDefaultDecode,
+ FX_BOOL& bColorKey) {
+ if (!m_pColorSpace) {
+ return nullptr;
+ }
+ DIB_COMP_DATA* pCompData = FX_Alloc(DIB_COMP_DATA, m_nComponents);
+ int max_data = (1 << m_bpc) - 1;
+ CPDF_Array* pDecode = m_pDict->GetArrayBy("Decode");
+ if (pDecode) {
+ for (FX_DWORD i = 0; i < m_nComponents; i++) {
+ pCompData[i].m_DecodeMin = pDecode->GetNumberAt(i * 2);
+ FX_FLOAT max = pDecode->GetNumberAt(i * 2 + 1);
+ pCompData[i].m_DecodeStep = (max - pCompData[i].m_DecodeMin) / max_data;
+ FX_FLOAT def_value;
+ FX_FLOAT def_min;
+ FX_FLOAT def_max;
+ m_pColorSpace->GetDefaultValue(i, def_value, def_min, def_max);
+ if (m_Family == PDFCS_INDEXED) {
+ def_max = max_data;
+ }
+ if (def_min != pCompData[i].m_DecodeMin || def_max != max) {
+ bDefaultDecode = FALSE;
+ }
+ }
+ } else {
+ for (FX_DWORD i = 0; i < m_nComponents; i++) {
+ FX_FLOAT def_value;
+ m_pColorSpace->GetDefaultValue(i, def_value, pCompData[i].m_DecodeMin,
+ pCompData[i].m_DecodeStep);
+ if (m_Family == PDFCS_INDEXED) {
+ pCompData[i].m_DecodeStep = max_data;
+ }
+ pCompData[i].m_DecodeStep =
+ (pCompData[i].m_DecodeStep - pCompData[i].m_DecodeMin) / max_data;
+ }
+ }
+ if (!m_pDict->KeyExist("SMask")) {
+ CPDF_Object* pMask = m_pDict->GetElementValue("Mask");
+ if (!pMask) {
+ return pCompData;
+ }
+ if (CPDF_Array* pArray = pMask->AsArray()) {
+ if (pArray->GetCount() >= m_nComponents * 2) {
+ for (FX_DWORD i = 0; i < m_nComponents; i++) {
+ int min_num = pArray->GetIntegerAt(i * 2);
+ int max_num = pArray->GetIntegerAt(i * 2 + 1);
+ pCompData[i].m_ColorKeyMin = std::max(min_num, 0);
+ pCompData[i].m_ColorKeyMax = std::min(max_num, max_data);
+ }
+ }
+ bColorKey = TRUE;
+ }
+ }
+ return pCompData;
+}
+
+ICodec_ScanlineDecoder* FPDFAPI_CreateFaxDecoder(
+ const uint8_t* src_buf,
+ FX_DWORD src_size,
+ int width,
+ int height,
+ const CPDF_Dictionary* pParams);
+
+ICodec_ScanlineDecoder* FPDFAPI_CreateFlateDecoder(
+ const uint8_t* src_buf,
+ FX_DWORD src_size,
+ int width,
+ int height,
+ int nComps,
+ int bpc,
+ const CPDF_Dictionary* pParams);
+
+int CPDF_DIBSource::CreateDecoder() {
+ const CFX_ByteString& decoder = m_pStreamAcc->GetImageDecoder();
+ if (decoder.IsEmpty()) {
+ return 1;
+ }
+ if (m_bDoBpcCheck && m_bpc == 0) {
+ return 0;
+ }
+ const uint8_t* src_data = m_pStreamAcc->GetData();
+ FX_DWORD src_size = m_pStreamAcc->GetSize();
+ const CPDF_Dictionary* pParams = m_pStreamAcc->GetImageParam();
+ if (decoder == "CCITTFaxDecode") {
+ m_pDecoder.reset(FPDFAPI_CreateFaxDecoder(src_data, src_size, m_Width,
+ m_Height, pParams));
+ } else if (decoder == "DCTDecode") {
+ m_pDecoder.reset(CPDF_ModuleMgr::Get()->GetJpegModule()->CreateDecoder(
+ src_data, src_size, m_Width, m_Height, m_nComponents,
+ pParams ? pParams->GetIntegerBy("ColorTransform", 1) : 1));
+ if (!m_pDecoder) {
+ FX_BOOL bTransform = FALSE;
+ int comps;
+ int bpc;
+ ICodec_JpegModule* pJpegModule = CPDF_ModuleMgr::Get()->GetJpegModule();
+ if (pJpegModule->LoadInfo(src_data, src_size, m_Width, m_Height, comps,
+ bpc, bTransform)) {
+ if (m_nComponents != static_cast<FX_DWORD>(comps)) {
+ FX_Free(m_pCompData);
+ m_nComponents = static_cast<FX_DWORD>(comps);
+ if (m_Family == PDFCS_LAB && m_nComponents != 3) {
+ m_pCompData = nullptr;
+ return 0;
+ }
+ m_pCompData = GetDecodeAndMaskArray(m_bDefaultDecode, m_bColorKey);
+ if (!m_pCompData) {
+ return 0;
+ }
+ }
+ m_bpc = bpc;
+ m_pDecoder.reset(CPDF_ModuleMgr::Get()->GetJpegModule()->CreateDecoder(
+ src_data, src_size, m_Width, m_Height, m_nComponents, bTransform));
+ }
+ }
+ } else if (decoder == "FlateDecode") {
+ m_pDecoder.reset(FPDFAPI_CreateFlateDecoder(
+ src_data, src_size, m_Width, m_Height, m_nComponents, m_bpc, pParams));
+ } else if (decoder == "JPXDecode") {
+ LoadJpxBitmap();
+ return m_pCachedBitmap ? 1 : 0;
+ } else if (decoder == "JBIG2Decode") {
+ m_pCachedBitmap.reset(new CFX_DIBitmap);
+ if (!m_pCachedBitmap->Create(
+ m_Width, m_Height, m_bImageMask ? FXDIB_1bppMask : FXDIB_1bppRgb)) {
+ m_pCachedBitmap.reset();
+ return 0;
+ }
+ m_Status = 1;
+ return 2;
+ } else if (decoder == "RunLengthDecode") {
+ m_pDecoder.reset(CPDF_ModuleMgr::Get()
+ ->GetCodecModule()
+ ->GetBasicModule()
+ ->CreateRunLengthDecoder(src_data, src_size, m_Width,
+ m_Height, m_nComponents,
+ m_bpc));
+ }
+ if (!m_pDecoder)
+ return 0;
+
+ FX_SAFE_DWORD requested_pitch =
+ CalculatePitch8(m_bpc, m_nComponents, m_Width);
+ if (!requested_pitch.IsValid()) {
+ return 0;
+ }
+ FX_SAFE_DWORD provided_pitch = CalculatePitch8(
+ m_pDecoder->GetBPC(), m_pDecoder->CountComps(), m_pDecoder->GetWidth());
+ if (!provided_pitch.IsValid()) {
+ return 0;
+ }
+ if (provided_pitch.ValueOrDie() < requested_pitch.ValueOrDie()) {
+ return 0;
+ }
+ return 1;
+}
+
+void CPDF_DIBSource::LoadJpxBitmap() {
+ ICodec_JpxModule* pJpxModule = CPDF_ModuleMgr::Get()->GetJpxModule();
+ if (!pJpxModule)
+ return;
+
+ std::unique_ptr<JpxBitMapContext> context(new JpxBitMapContext(pJpxModule));
+ context->set_decoder(pJpxModule->CreateDecoder(
+ m_pStreamAcc->GetData(), m_pStreamAcc->GetSize(), m_pColorSpace));
+ if (!context->decoder())
+ return;
+
+ FX_DWORD width = 0;
+ FX_DWORD height = 0;
+ FX_DWORD components = 0;
+ pJpxModule->GetImageInfo(context->decoder(), &width, &height, &components);
+ if (static_cast<int>(width) < m_Width || static_cast<int>(height) < m_Height)
+ return;
+
+ FX_BOOL bSwapRGB = FALSE;
+ if (m_pColorSpace) {
+ if (components != m_pColorSpace->CountComponents())
+ return;
+
+ if (m_pColorSpace == CPDF_ColorSpace::GetStockCS(PDFCS_DEVICERGB)) {
+ bSwapRGB = TRUE;
+ m_pColorSpace = nullptr;
+ }
+ } else {
+ if (components == 3) {
+ bSwapRGB = TRUE;
+ } else if (components == 4) {
+ m_pColorSpace = CPDF_ColorSpace::GetStockCS(PDFCS_DEVICECMYK);
+ }
+ m_nComponents = components;
+ }
+
+ FXDIB_Format format;
+ if (components == 1) {
+ format = FXDIB_8bppRgb;
+ } else if (components <= 3) {
+ format = FXDIB_Rgb;
+ } else if (components == 4) {
+ format = FXDIB_Rgb32;
+ } else {
+ width = (width * components + 2) / 3;
+ format = FXDIB_Rgb;
+ }
+
+ m_pCachedBitmap.reset(new CFX_DIBitmap);
+ if (!m_pCachedBitmap->Create(width, height, format)) {
+ m_pCachedBitmap.reset();
+ return;
+ }
+ m_pCachedBitmap->Clear(0xFFFFFFFF);
+ std::vector<uint8_t> output_offsets(components);
+ for (FX_DWORD i = 0; i < components; ++i)
+ output_offsets[i] = i;
+ if (bSwapRGB) {
+ output_offsets[0] = 2;
+ output_offsets[2] = 0;
+ }
+ if (!pJpxModule->Decode(context->decoder(), m_pCachedBitmap->GetBuffer(),
+ m_pCachedBitmap->GetPitch(), output_offsets)) {
+ m_pCachedBitmap.reset();
+ return;
+ }
+ if (m_pColorSpace && m_pColorSpace->GetFamily() == PDFCS_INDEXED &&
+ m_bpc < 8) {
+ int scale = 8 - m_bpc;
+ for (FX_DWORD row = 0; row < height; ++row) {
+ uint8_t* scanline =
+ const_cast<uint8_t*>(m_pCachedBitmap->GetScanline(row));
+ for (FX_DWORD col = 0; col < width; ++col) {
+ *scanline = (*scanline) >> scale;
+ ++scanline;
+ }
+ }
+ }
+ m_bpc = 8;
+}
+
+CPDF_DIBSource* CPDF_DIBSource::LoadMask(FX_DWORD& MatteColor) {
+ MatteColor = 0xFFFFFFFF;
+ CPDF_Stream* pSoftMask = m_pDict->GetStreamBy("SMask");
+ if (pSoftMask) {
+ CPDF_Array* pMatte = pSoftMask->GetDict()->GetArrayBy("Matte");
+ if (pMatte && m_pColorSpace &&
+ m_pColorSpace->CountComponents() <= m_nComponents) {
+ std::vector<FX_FLOAT> colors(m_nComponents);
+ for (FX_DWORD i = 0; i < m_nComponents; i++) {
+ colors[i] = pMatte->GetFloatAt(i);
+ }
+ FX_FLOAT R, G, B;
+ m_pColorSpace->GetRGB(colors.data(), R, G, B);
+ MatteColor = FXARGB_MAKE(0, FXSYS_round(R * 255), FXSYS_round(G * 255),
+ FXSYS_round(B * 255));
+ }
+ return LoadMaskDIB(pSoftMask);
+ }
+
+ if (CPDF_Stream* pStream = ToStream(m_pDict->GetElementValue("Mask")))
+ return LoadMaskDIB(pStream);
+
+ return nullptr;
+}
+
+int CPDF_DIBSource::StratLoadMask() {
+ m_MatteColor = 0XFFFFFFFF;
+ m_pMaskStream = m_pDict->GetStreamBy("SMask");
+ if (m_pMaskStream) {
+ CPDF_Array* pMatte = m_pMaskStream->GetDict()->GetArrayBy("Matte");
+ if (pMatte && m_pColorSpace &&
+ m_pColorSpace->CountComponents() <= m_nComponents) {
+ FX_FLOAT R, G, B;
+ std::vector<FX_FLOAT> colors(m_nComponents);
+ for (FX_DWORD i = 0; i < m_nComponents; i++) {
+ colors[i] = pMatte->GetFloatAt(i);
+ }
+ m_pColorSpace->GetRGB(colors.data(), R, G, B);
+ m_MatteColor = FXARGB_MAKE(0, FXSYS_round(R * 255), FXSYS_round(G * 255),
+ FXSYS_round(B * 255));
+ }
+ return StartLoadMaskDIB();
+ }
+
+ m_pMaskStream = ToStream(m_pDict->GetElementValue("Mask"));
+ return m_pMaskStream ? StartLoadMaskDIB() : 1;
+}
+
+int CPDF_DIBSource::ContinueLoadMaskDIB(IFX_Pause* pPause) {
+ if (!m_pMask) {
+ return 1;
+ }
+ int ret = m_pMask->ContinueLoadDIBSource(pPause);
+ if (ret == 2) {
+ return ret;
+ }
+ if (m_pColorSpace && m_bStdCS) {
+ m_pColorSpace->EnableStdConversion(FALSE);
+ }
+ if (!ret) {
+ delete m_pMask;
+ m_pMask = nullptr;
+ return ret;
+ }
+ return 1;
+}
+
+CPDF_DIBSource* CPDF_DIBSource::DetachMask() {
+ CPDF_DIBSource* pDIBSource = m_pMask;
+ m_pMask = nullptr;
+ return pDIBSource;
+}
+
+CPDF_DIBSource* CPDF_DIBSource::LoadMaskDIB(CPDF_Stream* pMask) {
+ CPDF_DIBSource* pMaskSource = new CPDF_DIBSource;
+ if (!pMaskSource->Load(m_pDocument, pMask, nullptr, nullptr, nullptr, nullptr,
+ TRUE)) {
+ delete pMaskSource;
+ return nullptr;
+ }
+ return pMaskSource;
+}
+
+int CPDF_DIBSource::StartLoadMaskDIB() {
+ m_pMask = new CPDF_DIBSource;
+ int ret = m_pMask->StartLoadDIBSource(m_pDocument, m_pMaskStream, FALSE,
+ nullptr, nullptr, TRUE);
+ if (ret == 2) {
+ if (m_Status == 0)
+ m_Status = 2;
+ return 2;
+ }
+ if (!ret) {
+ delete m_pMask;
+ m_pMask = nullptr;
+ return 1;
+ }
+ return 1;
+}
+
+void CPDF_DIBSource::LoadPalette() {
+ if (m_bpc == 0) {
+ return;
+ }
+ if (m_bpc * m_nComponents > 8) {
+ return;
+ }
+ if (!m_pColorSpace) {
+ return;
+ }
+ if (m_bpc * m_nComponents == 1) {
+ if (m_bDefaultDecode &&
+ (m_Family == PDFCS_DEVICEGRAY || m_Family == PDFCS_DEVICERGB)) {
+ return;
+ }
+ if (m_pColorSpace->CountComponents() > 3) {
+ return;
+ }
+ FX_FLOAT color_values[3];
+ color_values[0] = m_pCompData[0].m_DecodeMin;
+ color_values[1] = color_values[2] = color_values[0];
+ FX_FLOAT R = 0.0f, G = 0.0f, B = 0.0f;
+ m_pColorSpace->GetRGB(color_values, R, G, B);
+ FX_ARGB argb0 = ArgbEncode(255, FXSYS_round(R * 255), FXSYS_round(G * 255),
+ FXSYS_round(B * 255));
+ color_values[0] += m_pCompData[0].m_DecodeStep;
+ color_values[1] += m_pCompData[0].m_DecodeStep;
+ color_values[2] += m_pCompData[0].m_DecodeStep;
+ m_pColorSpace->GetRGB(color_values, R, G, B);
+ FX_ARGB argb1 = ArgbEncode(255, FXSYS_round(R * 255), FXSYS_round(G * 255),
+ FXSYS_round(B * 255));
+ if (argb0 != 0xFF000000 || argb1 != 0xFFFFFFFF) {
+ SetPaletteArgb(0, argb0);
+ SetPaletteArgb(1, argb1);
+ }
+ return;
+ }
+ if (m_pColorSpace == CPDF_ColorSpace::GetStockCS(PDFCS_DEVICEGRAY) &&
+ m_bpc == 8 && m_bDefaultDecode) {
+ } else {
+ int palette_count = 1 << (m_bpc * m_nComponents);
+ CFX_FixedBufGrow<FX_FLOAT, 16> color_values(m_nComponents);
+ FX_FLOAT* color_value = color_values;
+ for (int i = 0; i < palette_count; i++) {
+ int color_data = i;
+ for (FX_DWORD j = 0; j < m_nComponents; j++) {
+ int encoded_component = color_data % (1 << m_bpc);
+ color_data /= 1 << m_bpc;
+ color_value[j] = m_pCompData[j].m_DecodeMin +
+ m_pCompData[j].m_DecodeStep * encoded_component;
+ }
+ FX_FLOAT R = 0, G = 0, B = 0;
+ if (m_nComponents == 1 && m_Family == PDFCS_ICCBASED &&
+ m_pColorSpace->CountComponents() > 1) {
+ int nComponents = m_pColorSpace->CountComponents();
+ std::vector<FX_FLOAT> temp_buf(nComponents);
+ for (int i = 0; i < nComponents; i++) {
+ temp_buf[i] = *color_value;
+ }
+ m_pColorSpace->GetRGB(temp_buf.data(), R, G, B);
+ } else {
+ m_pColorSpace->GetRGB(color_value, R, G, B);
+ }
+ SetPaletteArgb(i, ArgbEncode(255, FXSYS_round(R * 255),
+ FXSYS_round(G * 255), FXSYS_round(B * 255)));
+ }
+ }
+}
+
+void CPDF_DIBSource::ValidateDictParam() {
+ m_bpc = m_bpc_orig;
+ CPDF_Object* pFilter = m_pDict->GetElementValue("Filter");
+ if (pFilter) {
+ if (pFilter->IsName()) {
+ CFX_ByteString filter = pFilter->GetString();
+ if (filter == "CCITTFaxDecode" || filter == "JBIG2Decode") {
+ m_bpc = 1;
+ m_nComponents = 1;
+ } else if (filter == "RunLengthDecode") {
+ if (m_bpc != 1) {
+ m_bpc = 8;
+ }
+ } else if (filter == "DCTDecode") {
+ m_bpc = 8;
+ }
+ } else if (CPDF_Array* pArray = pFilter->AsArray()) {
+ CFX_ByteString filter = pArray->GetStringAt(pArray->GetCount() - 1);
+ if (filter == "CCITTFaxDecode" || filter == "JBIG2Decode") {
+ m_bpc = 1;
+ m_nComponents = 1;
+ } else if (filter == "DCTDecode") {
+ // Previously, filter == "RunLengthDecode" was checked in the "if"
+ // statement as well, but too many documents don't conform to it.
+ m_bpc = 8;
+ }
+ }
+ }
+
+ if (!IsAllowedBPCValue(m_bpc))
+ m_bpc = 0;
+}
+
+void CPDF_DIBSource::TranslateScanline24bpp(uint8_t* dest_scan,
+ const uint8_t* src_scan) const {
+ if (m_bpc == 0) {
+ return;
+ }
+ unsigned int max_data = (1 << m_bpc) - 1;
+ if (m_bDefaultDecode) {
+ if (m_Family == PDFCS_DEVICERGB || m_Family == PDFCS_CALRGB) {
+ const uint8_t* src_pos = src_scan;
+ switch (m_bpc) {
+ case 16:
+ for (int col = 0; col < m_Width; col++) {
+ *dest_scan++ = src_pos[4];
+ *dest_scan++ = src_pos[2];
+ *dest_scan++ = *src_pos;
+ src_pos += 6;
+ }
+ break;
+ case 8:
+ for (int column = 0; column < m_Width; column++) {
+ *dest_scan++ = src_pos[2];
+ *dest_scan++ = src_pos[1];
+ *dest_scan++ = *src_pos;
+ src_pos += 3;
+ }
+ break;
+ default:
+ uint64_t src_bit_pos = 0;
+ size_t dest_byte_pos = 0;
+ for (int column = 0; column < m_Width; column++) {
+ unsigned int R = GetBits8(src_scan, src_bit_pos, m_bpc);
+ src_bit_pos += m_bpc;
+ unsigned int G = GetBits8(src_scan, src_bit_pos, m_bpc);
+ src_bit_pos += m_bpc;
+ unsigned int B = GetBits8(src_scan, src_bit_pos, m_bpc);
+ src_bit_pos += m_bpc;
+ R = std::min(R, max_data);
+ G = std::min(G, max_data);
+ B = std::min(B, max_data);
+ dest_scan[dest_byte_pos] = B * 255 / max_data;
+ dest_scan[dest_byte_pos + 1] = G * 255 / max_data;
+ dest_scan[dest_byte_pos + 2] = R * 255 / max_data;
+ dest_byte_pos += 3;
+ }
+ break;
+ }
+ return;
+ }
+ if (m_bpc == 8) {
+ if (m_nComponents == m_pColorSpace->CountComponents())
+ m_pColorSpace->TranslateImageLine(dest_scan, src_scan, m_Width, m_Width,
+ m_Height, TransMask());
+ return;
+ }
+ }
+ CFX_FixedBufGrow<FX_FLOAT, 16> color_values1(m_nComponents);
+ FX_FLOAT* color_values = color_values1;
+ FX_FLOAT R = 0.0f, G = 0.0f, B = 0.0f;
+ if (m_bpc == 8) {
+ uint64_t src_byte_pos = 0;
+ size_t dest_byte_pos = 0;
+ for (int column = 0; column < m_Width; column++) {
+ for (FX_DWORD color = 0; color < m_nComponents; color++) {
+ uint8_t data = src_scan[src_byte_pos++];
+ color_values[color] = m_pCompData[color].m_DecodeMin +
+ m_pCompData[color].m_DecodeStep * data;
+ }
+ if (TransMask()) {
+ FX_FLOAT k = 1.0f - color_values[3];
+ R = (1.0f - color_values[0]) * k;
+ G = (1.0f - color_values[1]) * k;
+ B = (1.0f - color_values[2]) * k;
+ } else {
+ m_pColorSpace->GetRGB(color_values, R, G, B);
+ }
+ R = ClampValue(R, 1.0f);
+ G = ClampValue(G, 1.0f);
+ B = ClampValue(B, 1.0f);
+ dest_scan[dest_byte_pos] = static_cast<uint8_t>(B * 255);
+ dest_scan[dest_byte_pos + 1] = static_cast<uint8_t>(G * 255);
+ dest_scan[dest_byte_pos + 2] = static_cast<uint8_t>(R * 255);
+ dest_byte_pos += 3;
+ }
+ } else {
+ uint64_t src_bit_pos = 0;
+ size_t dest_byte_pos = 0;
+ for (int column = 0; column < m_Width; column++) {
+ for (FX_DWORD color = 0; color < m_nComponents; color++) {
+ unsigned int data = GetBits8(src_scan, src_bit_pos, m_bpc);
+ color_values[color] = m_pCompData[color].m_DecodeMin +
+ m_pCompData[color].m_DecodeStep * data;
+ src_bit_pos += m_bpc;
+ }
+ if (TransMask()) {
+ FX_FLOAT k = 1.0f - color_values[3];
+ R = (1.0f - color_values[0]) * k;
+ G = (1.0f - color_values[1]) * k;
+ B = (1.0f - color_values[2]) * k;
+ } else {
+ m_pColorSpace->GetRGB(color_values, R, G, B);
+ }
+ R = ClampValue(R, 1.0f);
+ G = ClampValue(G, 1.0f);
+ B = ClampValue(B, 1.0f);
+ dest_scan[dest_byte_pos] = static_cast<uint8_t>(B * 255);
+ dest_scan[dest_byte_pos + 1] = static_cast<uint8_t>(G * 255);
+ dest_scan[dest_byte_pos + 2] = static_cast<uint8_t>(R * 255);
+ dest_byte_pos += 3;
+ }
+ }
+}
+
+uint8_t* CPDF_DIBSource::GetBuffer() const {
+ return m_pCachedBitmap ? m_pCachedBitmap->GetBuffer() : nullptr;
+}
+
+const uint8_t* CPDF_DIBSource::GetScanline(int line) const {
+ if (m_bpc == 0) {
+ return nullptr;
+ }
+ FX_SAFE_DWORD src_pitch = CalculatePitch8(m_bpc, m_nComponents, m_Width);
+ if (!src_pitch.IsValid())
+ return nullptr;
+ FX_DWORD src_pitch_value = src_pitch.ValueOrDie();
+ const uint8_t* pSrcLine = nullptr;
+ if (m_pCachedBitmap && src_pitch_value <= m_pCachedBitmap->GetPitch()) {
+ if (line >= m_pCachedBitmap->GetHeight()) {
+ line = m_pCachedBitmap->GetHeight() - 1;
+ }
+ pSrcLine = m_pCachedBitmap->GetScanline(line);
+ } else if (m_pDecoder) {
+ pSrcLine = m_pDecoder->GetScanline(line);
+ } else {
+ if (m_pStreamAcc->GetSize() >= (line + 1) * src_pitch_value) {
+ pSrcLine = m_pStreamAcc->GetData() + line * src_pitch_value;
+ }
+ }
+ if (!pSrcLine) {
+ uint8_t* pLineBuf = m_pMaskedLine ? m_pMaskedLine : m_pLineBuf;
+ FXSYS_memset(pLineBuf, 0xFF, m_Pitch);
+ return pLineBuf;
+ }
+ if (m_bpc * m_nComponents == 1) {
+ if (m_bImageMask && m_bDefaultDecode) {
+ for (FX_DWORD i = 0; i < src_pitch_value; i++) {
+ m_pLineBuf[i] = ~pSrcLine[i];
+ }
+ } else if (m_bColorKey) {
+ FX_DWORD reset_argb, set_argb;
+ reset_argb = m_pPalette ? m_pPalette[0] : 0xFF000000;
+ set_argb = m_pPalette ? m_pPalette[1] : 0xFFFFFFFF;
+ if (m_pCompData[0].m_ColorKeyMin == 0) {
+ reset_argb = 0;
+ }
+ if (m_pCompData[0].m_ColorKeyMax == 1) {
+ set_argb = 0;
+ }
+ set_argb = FXARGB_TODIB(set_argb);
+ reset_argb = FXARGB_TODIB(reset_argb);
+ FX_DWORD* dest_scan = reinterpret_cast<FX_DWORD*>(m_pMaskedLine);
+ for (int col = 0; col < m_Width; col++) {
+ if (pSrcLine[col / 8] & (1 << (7 - col % 8))) {
+ *dest_scan = set_argb;
+ } else {
+ *dest_scan = reset_argb;
+ }
+ dest_scan++;
+ }
+ return m_pMaskedLine;
+ } else {
+ FXSYS_memcpy(m_pLineBuf, pSrcLine, src_pitch_value);
+ }
+ return m_pLineBuf;
+ }
+ if (m_bpc * m_nComponents <= 8) {
+ if (m_bpc == 8) {
+ FXSYS_memcpy(m_pLineBuf, pSrcLine, src_pitch_value);
+ } else {
+ uint64_t src_bit_pos = 0;
+ for (int col = 0; col < m_Width; col++) {
+ unsigned int color_index = 0;
+ for (FX_DWORD color = 0; color < m_nComponents; color++) {
+ unsigned int data = GetBits8(pSrcLine, src_bit_pos, m_bpc);
+ color_index |= data << (color * m_bpc);
+ src_bit_pos += m_bpc;
+ }
+ m_pLineBuf[col] = color_index;
+ }
+ }
+ if (m_bColorKey) {
+ uint8_t* pDestPixel = m_pMaskedLine;
+ const uint8_t* pSrcPixel = m_pLineBuf;
+ for (int col = 0; col < m_Width; col++) {
+ uint8_t index = *pSrcPixel++;
+ if (m_pPalette) {
+ *pDestPixel++ = FXARGB_B(m_pPalette[index]);
+ *pDestPixel++ = FXARGB_G(m_pPalette[index]);
+ *pDestPixel++ = FXARGB_R(m_pPalette[index]);
+ } else {
+ *pDestPixel++ = index;
+ *pDestPixel++ = index;
+ *pDestPixel++ = index;
+ }
+ *pDestPixel = (index < m_pCompData[0].m_ColorKeyMin ||
+ index > m_pCompData[0].m_ColorKeyMax)
+ ? 0xFF
+ : 0;
+ pDestPixel++;
+ }
+ return m_pMaskedLine;
+ }
+ return m_pLineBuf;
+ }
+ if (m_bColorKey) {
+ if (m_nComponents == 3 && m_bpc == 8) {
+ uint8_t* alpha_channel = m_pMaskedLine + 3;
+ for (int col = 0; col < m_Width; col++) {
+ const uint8_t* pPixel = pSrcLine + col * 3;
+ alpha_channel[col * 4] = (pPixel[0] < m_pCompData[0].m_ColorKeyMin ||
+ pPixel[0] > m_pCompData[0].m_ColorKeyMax ||
+ pPixel[1] < m_pCompData[1].m_ColorKeyMin ||
+ pPixel[1] > m_pCompData[1].m_ColorKeyMax ||
+ pPixel[2] < m_pCompData[2].m_ColorKeyMin ||
+ pPixel[2] > m_pCompData[2].m_ColorKeyMax)
+ ? 0xFF
+ : 0;
+ }
+ } else {
+ FXSYS_memset(m_pMaskedLine, 0xFF, m_Pitch);
+ }
+ }
+ if (m_pColorSpace) {
+ TranslateScanline24bpp(m_pLineBuf, pSrcLine);
+ pSrcLine = m_pLineBuf;
+ }
+ if (m_bColorKey) {
+ const uint8_t* pSrcPixel = pSrcLine;
+ uint8_t* pDestPixel = m_pMaskedLine;
+ for (int col = 0; col < m_Width; col++) {
+ *pDestPixel++ = *pSrcPixel++;
+ *pDestPixel++ = *pSrcPixel++;
+ *pDestPixel++ = *pSrcPixel++;
+ pDestPixel++;
+ }
+ return m_pMaskedLine;
+ }
+ return pSrcLine;
+}
+
+FX_BOOL CPDF_DIBSource::SkipToScanline(int line, IFX_Pause* pPause) const {
+ return m_pDecoder && m_pDecoder->SkipToScanline(line, pPause);
+}
+
+void CPDF_DIBSource::DownSampleScanline(int line,
+ uint8_t* dest_scan,
+ int dest_bpp,
+ int dest_width,
+ FX_BOOL bFlipX,
+ int clip_left,
+ int clip_width) const {
+ if (line < 0 || !dest_scan || dest_bpp <= 0 || dest_width <= 0 ||
+ clip_left < 0 || clip_width <= 0) {
+ return;
+ }
+
+ FX_DWORD src_width = m_Width;
+ FX_SAFE_DWORD pitch = CalculatePitch8(m_bpc, m_nComponents, m_Width);
+ if (!pitch.IsValid())
+ return;
+
+ const uint8_t* pSrcLine = nullptr;
+ if (m_pCachedBitmap) {
+ pSrcLine = m_pCachedBitmap->GetScanline(line);
+ } else if (m_pDecoder) {
+ pSrcLine = m_pDecoder->GetScanline(line);
+ } else {
+ FX_DWORD src_pitch = pitch.ValueOrDie();
+ pitch *= (line + 1);
+ if (!pitch.IsValid()) {
+ return;
+ }
+
+ if (m_pStreamAcc->GetSize() >= pitch.ValueOrDie()) {
+ pSrcLine = m_pStreamAcc->GetData() + line * src_pitch;
+ }
+ }
+ int orig_Bpp = m_bpc * m_nComponents / 8;
+ int dest_Bpp = dest_bpp / 8;
+ if (!pSrcLine) {
+ FXSYS_memset(dest_scan, 0xFF, dest_Bpp * clip_width);
+ return;
+ }
+
+ FX_SAFE_INT32 max_src_x = clip_left;
+ max_src_x += clip_width - 1;
+ max_src_x *= src_width;
+ max_src_x /= dest_width;
+ if (!max_src_x.IsValid())
+ return;
+
+ if (m_bpc * m_nComponents == 1) {
+ DownSampleScanline1Bit(orig_Bpp, dest_Bpp, src_width, pSrcLine, dest_scan,
+ dest_width, bFlipX, clip_left, clip_width);
+ } else if (m_bpc * m_nComponents <= 8) {
+ DownSampleScanline8Bit(orig_Bpp, dest_Bpp, src_width, pSrcLine, dest_scan,
+ dest_width, bFlipX, clip_left, clip_width);
+ } else {
+ DownSampleScanline32Bit(orig_Bpp, dest_Bpp, src_width, pSrcLine, dest_scan,
+ dest_width, bFlipX, clip_left, clip_width);
+ }
+}
+
+void CPDF_DIBSource::DownSampleScanline1Bit(int orig_Bpp,
+ int dest_Bpp,
+ FX_DWORD src_width,
+ const uint8_t* pSrcLine,
+ uint8_t* dest_scan,
+ int dest_width,
+ FX_BOOL bFlipX,
+ int clip_left,
+ int clip_width) const {
+ FX_DWORD set_argb = (FX_DWORD)-1;
+ FX_DWORD reset_argb = 0;
+ if (m_bImageMask) {
+ if (m_bDefaultDecode) {
+ set_argb = 0;
+ reset_argb = (FX_DWORD)-1;
+ }
+ } else if (m_bColorKey) {
+ reset_argb = m_pPalette ? m_pPalette[0] : 0xFF000000;
+ set_argb = m_pPalette ? m_pPalette[1] : 0xFFFFFFFF;
+ if (m_pCompData[0].m_ColorKeyMin == 0) {
+ reset_argb = 0;
+ }
+ if (m_pCompData[0].m_ColorKeyMax == 1) {
+ set_argb = 0;
+ }
+ set_argb = FXARGB_TODIB(set_argb);
+ reset_argb = FXARGB_TODIB(reset_argb);
+ FX_DWORD* dest_scan_dword = reinterpret_cast<FX_DWORD*>(dest_scan);
+ for (int i = 0; i < clip_width; i++) {
+ FX_DWORD src_x = (clip_left + i) * src_width / dest_width;
+ if (bFlipX) {
+ src_x = src_width - src_x - 1;
+ }
+ src_x %= src_width;
+ if (pSrcLine[src_x / 8] & (1 << (7 - src_x % 8))) {
+ dest_scan_dword[i] = set_argb;
+ } else {
+ dest_scan_dword[i] = reset_argb;
+ }
+ }
+ return;
+ } else {
+ if (dest_Bpp == 1) {
+ } else if (m_pPalette) {
+ reset_argb = m_pPalette[0];
+ set_argb = m_pPalette[1];
+ }
+ }
+ for (int i = 0; i < clip_width; i++) {
+ FX_DWORD src_x = (clip_left + i) * src_width / dest_width;
+ if (bFlipX) {
+ src_x = src_width - src_x - 1;
+ }
+ src_x %= src_width;
+ int dest_pos = i * dest_Bpp;
+ if (pSrcLine[src_x / 8] & (1 << (7 - src_x % 8))) {
+ if (dest_Bpp == 1) {
+ dest_scan[dest_pos] = static_cast<uint8_t>(set_argb);
+ } else if (dest_Bpp == 3) {
+ dest_scan[dest_pos] = FXARGB_B(set_argb);
+ dest_scan[dest_pos + 1] = FXARGB_G(set_argb);
+ dest_scan[dest_pos + 2] = FXARGB_R(set_argb);
+ } else {
+ *reinterpret_cast<FX_DWORD*>(dest_scan + dest_pos) = set_argb;
+ }
+ } else {
+ if (dest_Bpp == 1) {
+ dest_scan[dest_pos] = static_cast<uint8_t>(reset_argb);
+ } else if (dest_Bpp == 3) {
+ dest_scan[dest_pos] = FXARGB_B(reset_argb);
+ dest_scan[dest_pos + 1] = FXARGB_G(reset_argb);
+ dest_scan[dest_pos + 2] = FXARGB_R(reset_argb);
+ } else {
+ *reinterpret_cast<FX_DWORD*>(dest_scan + dest_pos) = reset_argb;
+ }
+ }
+ }
+}
+
+void CPDF_DIBSource::DownSampleScanline8Bit(int orig_Bpp,
+ int dest_Bpp,
+ FX_DWORD src_width,
+ const uint8_t* pSrcLine,
+ uint8_t* dest_scan,
+ int dest_width,
+ FX_BOOL bFlipX,
+ int clip_left,
+ int clip_width) const {
+ if (m_bpc < 8) {
+ uint64_t src_bit_pos = 0;
+ for (FX_DWORD col = 0; col < src_width; col++) {
+ unsigned int color_index = 0;
+ for (FX_DWORD color = 0; color < m_nComponents; color++) {
+ unsigned int data = GetBits8(pSrcLine, src_bit_pos, m_bpc);
+ color_index |= data << (color * m_bpc);
+ src_bit_pos += m_bpc;
+ }
+ m_pLineBuf[col] = color_index;
+ }
+ pSrcLine = m_pLineBuf;
+ }
+ if (m_bColorKey) {
+ for (int i = 0; i < clip_width; i++) {
+ FX_DWORD src_x = (clip_left + i) * src_width / dest_width;
+ if (bFlipX) {
+ src_x = src_width - src_x - 1;
+ }
+ src_x %= src_width;
+ uint8_t* pDestPixel = dest_scan + i * 4;
+ uint8_t index = pSrcLine[src_x];
+ if (m_pPalette) {
+ *pDestPixel++ = FXARGB_B(m_pPalette[index]);
+ *pDestPixel++ = FXARGB_G(m_pPalette[index]);
+ *pDestPixel++ = FXARGB_R(m_pPalette[index]);
+ } else {
+ *pDestPixel++ = index;
+ *pDestPixel++ = index;
+ *pDestPixel++ = index;
+ }
+ *pDestPixel = (index < m_pCompData[0].m_ColorKeyMin ||
+ index > m_pCompData[0].m_ColorKeyMax)
+ ? 0xFF
+ : 0;
+ }
+ return;
+ }
+ for (int i = 0; i < clip_width; i++) {
+ FX_DWORD src_x = (clip_left + i) * src_width / dest_width;
+ if (bFlipX) {
+ src_x = src_width - src_x - 1;
+ }
+ src_x %= src_width;
+ uint8_t index = pSrcLine[src_x];
+ if (dest_Bpp == 1) {
+ dest_scan[i] = index;
+ } else {
+ int dest_pos = i * dest_Bpp;
+ FX_ARGB argb = m_pPalette[index];
+ dest_scan[dest_pos] = FXARGB_B(argb);
+ dest_scan[dest_pos + 1] = FXARGB_G(argb);
+ dest_scan[dest_pos + 2] = FXARGB_R(argb);
+ }
+ }
+}
+
+void CPDF_DIBSource::DownSampleScanline32Bit(int orig_Bpp,
+ int dest_Bpp,
+ FX_DWORD src_width,
+ const uint8_t* pSrcLine,
+ uint8_t* dest_scan,
+ int dest_width,
+ FX_BOOL bFlipX,
+ int clip_left,
+ int clip_width) const {
+ // last_src_x used to store the last seen src_x position which should be
+ // in [0, src_width). Set the initial value to be an invalid src_x value.
+ FX_DWORD last_src_x = src_width;
+ FX_ARGB last_argb = FXARGB_MAKE(0xFF, 0xFF, 0xFF, 0xFF);
+ FX_FLOAT unit_To8Bpc = 255.0f / ((1 << m_bpc) - 1);
+ for (int i = 0; i < clip_width; i++) {
+ int dest_x = clip_left + i;
+ FX_DWORD src_x = (bFlipX ? (dest_width - dest_x - 1) : dest_x) *
+ (int64_t)src_width / dest_width;
+ src_x %= src_width;
+
+ uint8_t* pDestPixel = dest_scan + i * dest_Bpp;
+ FX_ARGB argb;
+ if (src_x == last_src_x) {
+ argb = last_argb;
+ } else {
+ CFX_FixedBufGrow<uint8_t, 128> extracted_components(m_nComponents);
+ const uint8_t* pSrcPixel = nullptr;
+ if (m_bpc % 8 != 0) {
+ // No need to check for 32-bit overflow, as |src_x| is bounded by
+ // |src_width| and DownSampleScanline() already checked for overflow
+ // with the pitch calculation.
+ size_t num_bits = src_x * m_bpc * m_nComponents;
+ uint64_t src_bit_pos = num_bits % 8;
+ pSrcPixel = pSrcLine + num_bits / 8;
+ for (FX_DWORD j = 0; j < m_nComponents; ++j) {
+ extracted_components[j] = static_cast<uint8_t>(
+ GetBits8(pSrcPixel, src_bit_pos, m_bpc) * unit_To8Bpc);
+ src_bit_pos += m_bpc;
+ }
+ pSrcPixel = extracted_components;
+ } else {
+ pSrcPixel = pSrcLine + src_x * orig_Bpp;
+ if (m_bpc == 16) {
+ for (FX_DWORD j = 0; j < m_nComponents; ++j)
+ extracted_components[j] = pSrcPixel[j * 2];
+ pSrcPixel = extracted_components;
+ }
+ }
+
+ if (m_pColorSpace) {
+ uint8_t color[4];
+ const FX_BOOL bTransMask = TransMask();
+ if (m_bDefaultDecode) {
+ m_pColorSpace->TranslateImageLine(color, pSrcPixel, 1, 0, 0,
+ bTransMask);
+ } else {
+ for (FX_DWORD j = 0; j < m_nComponents; ++j) {
+ FX_FLOAT component_value =
+ static_cast<FX_FLOAT>(extracted_components[j]);
+ int color_value = static_cast<int>(
+ (m_pCompData[j].m_DecodeMin +
+ m_pCompData[j].m_DecodeStep * component_value) *
+ 255.0f +
+ 0.5f);
+ extracted_components[j] =
+ color_value > 255 ? 255 : (color_value < 0 ? 0 : color_value);
+ }
+ m_pColorSpace->TranslateImageLine(color, extracted_components, 1, 0,
+ 0, bTransMask);
+ }
+ argb = FXARGB_MAKE(0xFF, color[2], color[1], color[0]);
+ } else {
+ argb = FXARGB_MAKE(0xFF, pSrcPixel[2], pSrcPixel[1], pSrcPixel[0]);
+ }
+ if (m_bColorKey) {
+ int alpha = 0xFF;
+ if (m_nComponents == 3 && m_bpc == 8) {
+ alpha = (pSrcPixel[0] < m_pCompData[0].m_ColorKeyMin ||
+ pSrcPixel[0] > m_pCompData[0].m_ColorKeyMax ||
+ pSrcPixel[1] < m_pCompData[1].m_ColorKeyMin ||
+ pSrcPixel[1] > m_pCompData[1].m_ColorKeyMax ||
+ pSrcPixel[2] < m_pCompData[2].m_ColorKeyMin ||
+ pSrcPixel[2] > m_pCompData[2].m_ColorKeyMax)
+ ? 0xFF
+ : 0;
+ }
+ argb &= 0xFFFFFF;
+ argb |= alpha << 24;
+ }
+ last_src_x = src_x;
+ last_argb = argb;
+ }
+ if (dest_Bpp == 4) {
+ *reinterpret_cast<FX_DWORD*>(pDestPixel) = FXARGB_TODIB(argb);
+ } else {
+ *pDestPixel++ = FXARGB_B(argb);
+ *pDestPixel++ = FXARGB_G(argb);
+ *pDestPixel = FXARGB_R(argb);
+ }
+ }
+}
+
+FX_BOOL CPDF_DIBSource::TransMask() const {
+ return m_bLoadMask && m_GroupFamily == PDFCS_DEVICECMYK &&
+ m_Family == PDFCS_DEVICECMYK;
+}
+
+void CPDF_DIBSource::SetDownSampleSize(int dest_width, int dest_height) {
+ if (m_pDecoder) {
+ m_pDecoder->DownScale(dest_width, dest_height);
+ m_Width = m_pDecoder->GetWidth();
+ m_Height = m_pDecoder->GetHeight();
+ }
+}
+
+void CPDF_DIBSource::ClearImageData() {
+ if (m_pDecoder) {
+ m_pDecoder->ClearImageData();
+ }
+}
+
+CPDF_ImageLoaderHandle::CPDF_ImageLoaderHandle() {
+ m_pImageLoader = nullptr;
+ m_pCache = nullptr;
+ m_pImage = nullptr;
+}
+
+CPDF_ImageLoaderHandle::~CPDF_ImageLoaderHandle() {}
+
+FX_BOOL CPDF_ImageLoaderHandle::Start(CPDF_ImageLoader* pImageLoader,
+ const CPDF_ImageObject* pImage,
+ CPDF_PageRenderCache* pCache,
+ FX_BOOL bStdCS,
+ FX_DWORD GroupFamily,
+ FX_BOOL bLoadMask,
+ CPDF_RenderStatus* pRenderStatus,
+ int32_t nDownsampleWidth,
+ int32_t nDownsampleHeight) {
+ m_pImageLoader = pImageLoader;
+ m_pCache = pCache;
+ m_pImage = const_cast<CPDF_ImageObject*>(pImage);
+ m_nDownsampleWidth = nDownsampleWidth;
+ m_nDownsampleHeight = nDownsampleHeight;
+ FX_BOOL ret;
+ if (pCache) {
+ ret = pCache->StartGetCachedBitmap(pImage->m_pImage->GetStream(), bStdCS,
+ GroupFamily, bLoadMask, pRenderStatus,
+ m_nDownsampleWidth, m_nDownsampleHeight);
+ if (!ret) {
+ m_pImageLoader->m_bCached = TRUE;
+ m_pImageLoader->m_pBitmap =
+ pCache->GetCurImageCacheEntry()->DetachBitmap();
+ m_pImageLoader->m_pMask = pCache->GetCurImageCacheEntry()->DetachMask();
+ m_pImageLoader->m_MatteColor =
+ pCache->GetCurImageCacheEntry()->m_MatteColor;
+ }
+ } else {
+ ret = pImage->m_pImage->StartLoadDIBSource(pRenderStatus->m_pFormResource,
+ pRenderStatus->m_pPageResource,
+ bStdCS, GroupFamily, bLoadMask);
+ if (!ret) {
+ m_pImageLoader->m_bCached = FALSE;
+ m_pImageLoader->m_pBitmap = m_pImage->m_pImage->DetachBitmap();
+ m_pImageLoader->m_pMask = m_pImage->m_pImage->DetachMask();
+ m_pImageLoader->m_MatteColor = m_pImage->m_pImage->m_MatteColor;
+ }
+ }
+ return ret;
+}
+
+FX_BOOL CPDF_ImageLoaderHandle::Continue(IFX_Pause* pPause) {
+ FX_BOOL ret;
+ if (m_pCache) {
+ ret = m_pCache->Continue(pPause);
+ if (!ret) {
+ m_pImageLoader->m_bCached = TRUE;
+ m_pImageLoader->m_pBitmap =
+ m_pCache->GetCurImageCacheEntry()->DetachBitmap();
+ m_pImageLoader->m_pMask = m_pCache->GetCurImageCacheEntry()->DetachMask();
+ m_pImageLoader->m_MatteColor =
+ m_pCache->GetCurImageCacheEntry()->m_MatteColor;
+ }
+ } else {
+ ret = m_pImage->m_pImage->Continue(pPause);
+ if (!ret) {
+ m_pImageLoader->m_bCached = FALSE;
+ m_pImageLoader->m_pBitmap = m_pImage->m_pImage->DetachBitmap();
+ m_pImageLoader->m_pMask = m_pImage->m_pImage->DetachMask();
+ m_pImageLoader->m_MatteColor = m_pImage->m_pImage->m_MatteColor;
+ }
+ }
+ return ret;
+}
+
+FX_BOOL CPDF_ImageLoader::Start(const CPDF_ImageObject* pImage,
+ CPDF_PageRenderCache* pCache,
+ CPDF_ImageLoaderHandle*& LoadHandle,
+ FX_BOOL bStdCS,
+ FX_DWORD GroupFamily,
+ FX_BOOL bLoadMask,
+ CPDF_RenderStatus* pRenderStatus,
+ int32_t nDownsampleWidth,
+ int32_t nDownsampleHeight) {
+ m_nDownsampleWidth = nDownsampleWidth;
+ m_nDownsampleHeight = nDownsampleHeight;
+ LoadHandle = new CPDF_ImageLoaderHandle;
+ return LoadHandle->Start(this, pImage, pCache, bStdCS, GroupFamily, bLoadMask,
+ pRenderStatus, m_nDownsampleWidth,
+ m_nDownsampleHeight);
+}
+
+FX_BOOL CPDF_ImageLoader::Continue(CPDF_ImageLoaderHandle* LoadHandle,
+ IFX_Pause* pPause) {
+ return LoadHandle->Continue(pPause);
+}
+
+CPDF_ImageLoader::~CPDF_ImageLoader() {
+ if (!m_bCached) {
+ delete m_pBitmap;
+ delete m_pMask;
+ }
+}
diff --git a/core/fpdfapi/fpdf_render/fpdf_render_loadimage_embeddertest.cpp b/core/fpdfapi/fpdf_render/fpdf_render_loadimage_embeddertest.cpp
new file mode 100644
index 0000000000..427abb8e37
--- /dev/null
+++ b/core/fpdfapi/fpdf_render/fpdf_render_loadimage_embeddertest.cpp
@@ -0,0 +1,29 @@
+// Copyright 2015 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/embedder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class FPDFRenderLoadImageEmbeddertest : public EmbedderTest {};
+
+TEST_F(FPDFRenderLoadImageEmbeddertest, Bug_554151) {
+ // Test scanline downsampling with a BitsPerComponent of 4.
+ // Should not crash.
+ EXPECT_TRUE(OpenDocument("bug_554151.pdf"));
+ FPDF_PAGE page = LoadPage(0);
+ EXPECT_NE(nullptr, page);
+ FPDF_BITMAP bitmap = RenderPage(page);
+ FPDFBitmap_Destroy(bitmap);
+ UnloadPage(page);
+}
+
+TEST_F(FPDFRenderLoadImageEmbeddertest, Bug_557223) {
+ // Should not crash
+ EXPECT_TRUE(OpenDocument("bug_557223.pdf"));
+ FPDF_PAGE page = LoadPage(0);
+ EXPECT_NE(nullptr, page);
+ FPDF_BITMAP bitmap = RenderPage(page);
+ FPDFBitmap_Destroy(bitmap);
+ UnloadPage(page);
+}
diff --git a/core/fpdfapi/fpdf_render/fpdf_render_pattern.cpp b/core/fpdfapi/fpdf_render/fpdf_render_pattern.cpp
new file mode 100644
index 0000000000..5ad8db1646
--- /dev/null
+++ b/core/fpdfapi/fpdf_render/fpdf_render_pattern.cpp
@@ -0,0 +1,1207 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fpdfapi/fpdf_render/render_int.h"
+
+#include "core/fpdfapi/fpdf_page/pageint.h"
+#include "core/include/fpdfapi/cpdf_array.h"
+#include "core/include/fpdfapi/cpdf_dictionary.h"
+#include "core/include/fpdfapi/fpdf_pageobj.h"
+#include "core/include/fpdfapi/fpdf_render.h"
+#include "core/include/fxge/fx_ge.h"
+
+#define SHADING_STEPS 256
+static void DrawAxialShading(CFX_DIBitmap* pBitmap,
+ CFX_Matrix* pObject2Bitmap,
+ CPDF_Dictionary* pDict,
+ CPDF_Function** pFuncs,
+ int nFuncs,
+ CPDF_ColorSpace* pCS,
+ int alpha) {
+ ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
+ CPDF_Array* pCoords = pDict->GetArrayBy("Coords");
+ if (!pCoords) {
+ return;
+ }
+ FX_FLOAT start_x = pCoords->GetNumberAt(0);
+ FX_FLOAT start_y = pCoords->GetNumberAt(1);
+ FX_FLOAT end_x = pCoords->GetNumberAt(2);
+ FX_FLOAT end_y = pCoords->GetNumberAt(3);
+ FX_FLOAT t_min = 0, t_max = 1.0f;
+ CPDF_Array* pArray = pDict->GetArrayBy("Domain");
+ if (pArray) {
+ t_min = pArray->GetNumberAt(0);
+ t_max = pArray->GetNumberAt(1);
+ }
+ FX_BOOL bStartExtend = FALSE, bEndExtend = FALSE;
+ pArray = pDict->GetArrayBy("Extend");
+ if (pArray) {
+ bStartExtend = pArray->GetIntegerAt(0);
+ bEndExtend = pArray->GetIntegerAt(1);
+ }
+ int width = pBitmap->GetWidth();
+ int height = pBitmap->GetHeight();
+ FX_FLOAT x_span = end_x - start_x;
+ FX_FLOAT y_span = end_y - start_y;
+ FX_FLOAT axis_len_square = (x_span * x_span) + (y_span * y_span);
+ CFX_Matrix matrix;
+ matrix.SetReverse(*pObject2Bitmap);
+ int total_results = 0;
+ for (int j = 0; j < nFuncs; j++) {
+ if (pFuncs[j]) {
+ total_results += pFuncs[j]->CountOutputs();
+ }
+ }
+ if (pCS->CountComponents() > total_results) {
+ total_results = pCS->CountComponents();
+ }
+ CFX_FixedBufGrow<FX_FLOAT, 16> result_array(total_results);
+ FX_FLOAT* pResults = result_array;
+ FXSYS_memset(pResults, 0, total_results * sizeof(FX_FLOAT));
+ FX_DWORD rgb_array[SHADING_STEPS];
+ for (int i = 0; i < SHADING_STEPS; i++) {
+ FX_FLOAT input = (t_max - t_min) * i / SHADING_STEPS + t_min;
+ int offset = 0;
+ for (int j = 0; j < nFuncs; j++) {
+ if (pFuncs[j]) {
+ int nresults = 0;
+ if (pFuncs[j]->Call(&input, 1, pResults + offset, nresults)) {
+ offset += nresults;
+ }
+ }
+ }
+ FX_FLOAT R = 0.0f, G = 0.0f, B = 0.0f;
+ pCS->GetRGB(pResults, R, G, B);
+ rgb_array[i] =
+ FXARGB_TODIB(FXARGB_MAKE(alpha, FXSYS_round(R * 255),
+ FXSYS_round(G * 255), FXSYS_round(B * 255)));
+ }
+ int pitch = pBitmap->GetPitch();
+ for (int row = 0; row < height; row++) {
+ FX_DWORD* dib_buf = (FX_DWORD*)(pBitmap->GetBuffer() + row * pitch);
+ for (int column = 0; column < width; column++) {
+ FX_FLOAT x = (FX_FLOAT)column, y = (FX_FLOAT)row;
+ matrix.Transform(x, y);
+ FX_FLOAT scale = (((x - start_x) * x_span) + ((y - start_y) * y_span)) /
+ axis_len_square;
+ int index = (int32_t)(scale * (SHADING_STEPS - 1));
+ if (index < 0) {
+ if (!bStartExtend) {
+ continue;
+ }
+ index = 0;
+ } else if (index >= SHADING_STEPS) {
+ if (!bEndExtend) {
+ continue;
+ }
+ index = SHADING_STEPS - 1;
+ }
+ dib_buf[column] = rgb_array[index];
+ }
+ }
+}
+static void DrawRadialShading(CFX_DIBitmap* pBitmap,
+ CFX_Matrix* pObject2Bitmap,
+ CPDF_Dictionary* pDict,
+ CPDF_Function** pFuncs,
+ int nFuncs,
+ CPDF_ColorSpace* pCS,
+ int alpha) {
+ ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
+ CPDF_Array* pCoords = pDict->GetArrayBy("Coords");
+ if (!pCoords) {
+ return;
+ }
+ FX_FLOAT start_x = pCoords->GetNumberAt(0);
+ FX_FLOAT start_y = pCoords->GetNumberAt(1);
+ FX_FLOAT start_r = pCoords->GetNumberAt(2);
+ FX_FLOAT end_x = pCoords->GetNumberAt(3);
+ FX_FLOAT end_y = pCoords->GetNumberAt(4);
+ FX_FLOAT end_r = pCoords->GetNumberAt(5);
+ CFX_Matrix matrix;
+ matrix.SetReverse(*pObject2Bitmap);
+ FX_FLOAT t_min = 0, t_max = 1.0f;
+ CPDF_Array* pArray = pDict->GetArrayBy("Domain");
+ if (pArray) {
+ t_min = pArray->GetNumberAt(0);
+ t_max = pArray->GetNumberAt(1);
+ }
+ FX_BOOL bStartExtend = FALSE, bEndExtend = FALSE;
+ pArray = pDict->GetArrayBy("Extend");
+ if (pArray) {
+ bStartExtend = pArray->GetIntegerAt(0);
+ bEndExtend = pArray->GetIntegerAt(1);
+ }
+ int total_results = 0;
+ for (int j = 0; j < nFuncs; j++) {
+ if (pFuncs[j]) {
+ total_results += pFuncs[j]->CountOutputs();
+ }
+ }
+ if (pCS->CountComponents() > total_results) {
+ total_results = pCS->CountComponents();
+ }
+ CFX_FixedBufGrow<FX_FLOAT, 16> result_array(total_results);
+ FX_FLOAT* pResults = result_array;
+ FXSYS_memset(pResults, 0, total_results * sizeof(FX_FLOAT));
+ FX_DWORD rgb_array[SHADING_STEPS];
+ for (int i = 0; i < SHADING_STEPS; i++) {
+ FX_FLOAT input = (t_max - t_min) * i / SHADING_STEPS + t_min;
+ int offset = 0;
+ for (int j = 0; j < nFuncs; j++) {
+ if (pFuncs[j]) {
+ int nresults;
+ if (pFuncs[j]->Call(&input, 1, pResults + offset, nresults)) {
+ offset += nresults;
+ }
+ }
+ }
+ FX_FLOAT R = 0.0f, G = 0.0f, B = 0.0f;
+ pCS->GetRGB(pResults, R, G, B);
+ rgb_array[i] =
+ FXARGB_TODIB(FXARGB_MAKE(alpha, FXSYS_round(R * 255),
+ FXSYS_round(G * 255), FXSYS_round(B * 255)));
+ }
+ FX_FLOAT a = ((start_x - end_x) * (start_x - end_x)) +
+ ((start_y - end_y) * (start_y - end_y)) -
+ ((start_r - end_r) * (start_r - end_r));
+ int width = pBitmap->GetWidth();
+ int height = pBitmap->GetHeight();
+ int pitch = pBitmap->GetPitch();
+ FX_BOOL bDecreasing = FALSE;
+ if (start_r > end_r) {
+ int length = (int)FXSYS_sqrt((((start_x - end_x) * (start_x - end_x)) +
+ ((start_y - end_y) * (start_y - end_y))));
+ if (length < start_r - end_r) {
+ bDecreasing = TRUE;
+ }
+ }
+ for (int row = 0; row < height; row++) {
+ FX_DWORD* dib_buf = (FX_DWORD*)(pBitmap->GetBuffer() + row * pitch);
+ for (int column = 0; column < width; column++) {
+ FX_FLOAT x = (FX_FLOAT)column, y = (FX_FLOAT)row;
+ matrix.Transform(x, y);
+ FX_FLOAT b = -2 * (((x - start_x) * (end_x - start_x)) +
+ ((y - start_y) * (end_y - start_y)) +
+ (start_r * (end_r - start_r)));
+ FX_FLOAT c = ((x - start_x) * (x - start_x)) +
+ ((y - start_y) * (y - start_y)) - (start_r * start_r);
+ FX_FLOAT s;
+ if (a == 0) {
+ s = -c / b;
+ } else {
+ FX_FLOAT b2_4ac = (b * b) - 4 * (a * c);
+ if (b2_4ac < 0) {
+ continue;
+ }
+ FX_FLOAT root = FXSYS_sqrt(b2_4ac);
+ FX_FLOAT s1, s2;
+ if (a > 0) {
+ s1 = (-b - root) / (2 * a);
+ s2 = (-b + root) / (2 * a);
+ } else {
+ s2 = (-b - root) / (2 * a);
+ s1 = (-b + root) / (2 * a);
+ }
+ if (bDecreasing) {
+ if (s1 >= 0 || bStartExtend) {
+ s = s1;
+ } else {
+ s = s2;
+ }
+ } else {
+ if (s2 <= 1.0f || bEndExtend) {
+ s = s2;
+ } else {
+ s = s1;
+ }
+ }
+ if ((start_r + s * (end_r - start_r)) < 0) {
+ continue;
+ }
+ }
+ int index = (int32_t)(s * (SHADING_STEPS - 1));
+ if (index < 0) {
+ if (!bStartExtend) {
+ continue;
+ }
+ index = 0;
+ }
+ if (index >= SHADING_STEPS) {
+ if (!bEndExtend) {
+ continue;
+ }
+ index = SHADING_STEPS - 1;
+ }
+ dib_buf[column] = rgb_array[index];
+ }
+ }
+}
+static void DrawFuncShading(CFX_DIBitmap* pBitmap,
+ CFX_Matrix* pObject2Bitmap,
+ CPDF_Dictionary* pDict,
+ CPDF_Function** pFuncs,
+ int nFuncs,
+ CPDF_ColorSpace* pCS,
+ int alpha) {
+ ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
+ CPDF_Array* pDomain = pDict->GetArrayBy("Domain");
+ FX_FLOAT xmin = 0, ymin = 0, xmax = 1.0f, ymax = 1.0f;
+ if (pDomain) {
+ xmin = pDomain->GetNumberAt(0);
+ xmax = pDomain->GetNumberAt(1);
+ ymin = pDomain->GetNumberAt(2);
+ ymax = pDomain->GetNumberAt(3);
+ }
+ CFX_Matrix mtDomain2Target = pDict->GetMatrixBy("Matrix");
+ CFX_Matrix matrix, reverse_matrix;
+ matrix.SetReverse(*pObject2Bitmap);
+ reverse_matrix.SetReverse(mtDomain2Target);
+ matrix.Concat(reverse_matrix);
+ int width = pBitmap->GetWidth();
+ int height = pBitmap->GetHeight();
+ int pitch = pBitmap->GetPitch();
+ int total_results = 0;
+ for (int j = 0; j < nFuncs; j++) {
+ if (pFuncs[j]) {
+ total_results += pFuncs[j]->CountOutputs();
+ }
+ }
+ if (pCS->CountComponents() > total_results) {
+ total_results = pCS->CountComponents();
+ }
+ CFX_FixedBufGrow<FX_FLOAT, 16> result_array(total_results);
+ FX_FLOAT* pResults = result_array;
+ FXSYS_memset(pResults, 0, total_results * sizeof(FX_FLOAT));
+ for (int row = 0; row < height; row++) {
+ FX_DWORD* dib_buf = (FX_DWORD*)(pBitmap->GetBuffer() + row * pitch);
+ for (int column = 0; column < width; column++) {
+ FX_FLOAT x = (FX_FLOAT)column, y = (FX_FLOAT)row;
+ matrix.Transform(x, y);
+ if (x < xmin || x > xmax || y < ymin || y > ymax) {
+ continue;
+ }
+ FX_FLOAT input[2];
+ int offset = 0;
+ input[0] = x;
+ input[1] = y;
+ for (int j = 0; j < nFuncs; j++) {
+ if (pFuncs[j]) {
+ int nresults;
+ if (pFuncs[j]->Call(input, 2, pResults + offset, nresults)) {
+ offset += nresults;
+ }
+ }
+ }
+ FX_FLOAT R = 0.0f, G = 0.0f, B = 0.0f;
+ pCS->GetRGB(pResults, R, G, B);
+ dib_buf[column] = FXARGB_TODIB(FXARGB_MAKE(
+ alpha, (int32_t)(R * 255), (int32_t)(G * 255), (int32_t)(B * 255)));
+ }
+ }
+}
+FX_BOOL _GetScanlineIntersect(int y,
+ FX_FLOAT x1,
+ FX_FLOAT y1,
+ FX_FLOAT x2,
+ FX_FLOAT y2,
+ FX_FLOAT& x) {
+ if (y1 == y2) {
+ return FALSE;
+ }
+ if (y1 < y2) {
+ if (y < y1 || y > y2) {
+ return FALSE;
+ }
+ } else {
+ if (y < y2 || y > y1) {
+ return FALSE;
+ }
+ }
+ x = x1 + ((x2 - x1) * (y - y1) / (y2 - y1));
+ return TRUE;
+}
+static void DrawGouraud(CFX_DIBitmap* pBitmap,
+ int alpha,
+ CPDF_MeshVertex triangle[3]) {
+ FX_FLOAT min_y = triangle[0].y, max_y = triangle[0].y;
+ for (int i = 1; i < 3; i++) {
+ if (min_y > triangle[i].y) {
+ min_y = triangle[i].y;
+ }
+ if (max_y < triangle[i].y) {
+ max_y = triangle[i].y;
+ }
+ }
+ if (min_y == max_y) {
+ return;
+ }
+ int min_yi = (int)FXSYS_floor(min_y), max_yi = (int)FXSYS_ceil(max_y);
+ if (min_yi < 0) {
+ min_yi = 0;
+ }
+ if (max_yi >= pBitmap->GetHeight()) {
+ max_yi = pBitmap->GetHeight() - 1;
+ }
+ for (int y = min_yi; y <= max_yi; y++) {
+ int nIntersects = 0;
+ FX_FLOAT inter_x[3], r[3], g[3], b[3];
+ for (int i = 0; i < 3; i++) {
+ CPDF_MeshVertex& vertex1 = triangle[i];
+ CPDF_MeshVertex& vertex2 = triangle[(i + 1) % 3];
+ FX_BOOL bIntersect = _GetScanlineIntersect(
+ y, vertex1.x, vertex1.y, vertex2.x, vertex2.y, inter_x[nIntersects]);
+ if (!bIntersect) {
+ continue;
+ }
+
+ FX_FLOAT y_dist = (y - vertex1.y) / (vertex2.y - vertex1.y);
+ r[nIntersects] = vertex1.r + ((vertex2.r - vertex1.r) * y_dist);
+ g[nIntersects] = vertex1.g + ((vertex2.g - vertex1.g) * y_dist);
+ b[nIntersects] = vertex1.b + ((vertex2.b - vertex1.b) * y_dist);
+ nIntersects++;
+ }
+ if (nIntersects != 2) {
+ continue;
+ }
+ int min_x, max_x, start_index, end_index;
+ if (inter_x[0] < inter_x[1]) {
+ min_x = (int)FXSYS_floor(inter_x[0]);
+ max_x = (int)FXSYS_ceil(inter_x[1]);
+ start_index = 0;
+ end_index = 1;
+ } else {
+ min_x = (int)FXSYS_floor(inter_x[1]);
+ max_x = (int)FXSYS_ceil(inter_x[0]);
+ start_index = 1;
+ end_index = 0;
+ }
+ int start_x = min_x, end_x = max_x;
+ if (start_x < 0) {
+ start_x = 0;
+ }
+ if (end_x > pBitmap->GetWidth()) {
+ end_x = pBitmap->GetWidth();
+ }
+ uint8_t* dib_buf =
+ pBitmap->GetBuffer() + y * pBitmap->GetPitch() + start_x * 4;
+ FX_FLOAT r_unit = (r[end_index] - r[start_index]) / (max_x - min_x);
+ FX_FLOAT g_unit = (g[end_index] - g[start_index]) / (max_x - min_x);
+ FX_FLOAT b_unit = (b[end_index] - b[start_index]) / (max_x - min_x);
+ FX_FLOAT R = r[start_index] + (start_x - min_x) * r_unit;
+ FX_FLOAT G = g[start_index] + (start_x - min_x) * g_unit;
+ FX_FLOAT B = b[start_index] + (start_x - min_x) * b_unit;
+ for (int x = start_x; x < end_x; x++) {
+ R += r_unit;
+ G += g_unit;
+ B += b_unit;
+ FXARGB_SETDIB(dib_buf,
+ FXARGB_MAKE(alpha, (int32_t)(R * 255), (int32_t)(G * 255),
+ (int32_t)(B * 255)));
+ dib_buf += 4;
+ }
+ }
+}
+static void DrawFreeGouraudShading(CFX_DIBitmap* pBitmap,
+ CFX_Matrix* pObject2Bitmap,
+ CPDF_Stream* pShadingStream,
+ CPDF_Function** pFuncs,
+ int nFuncs,
+ CPDF_ColorSpace* pCS,
+ int alpha) {
+ ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
+
+ CPDF_MeshStream stream;
+ if (!stream.Load(pShadingStream, pFuncs, nFuncs, pCS))
+ return;
+
+ CPDF_MeshVertex triangle[3];
+ FXSYS_memset(triangle, 0, sizeof(triangle));
+
+ while (!stream.m_BitStream.IsEOF()) {
+ CPDF_MeshVertex vertex;
+ FX_DWORD flag = stream.GetVertex(vertex, pObject2Bitmap);
+ if (flag == 0) {
+ triangle[0] = vertex;
+ for (int j = 1; j < 3; j++) {
+ stream.GetVertex(triangle[j], pObject2Bitmap);
+ }
+ } else {
+ if (flag == 1) {
+ triangle[0] = triangle[1];
+ }
+ triangle[1] = triangle[2];
+ triangle[2] = vertex;
+ }
+ DrawGouraud(pBitmap, alpha, triangle);
+ }
+}
+static void DrawLatticeGouraudShading(CFX_DIBitmap* pBitmap,
+ CFX_Matrix* pObject2Bitmap,
+ CPDF_Stream* pShadingStream,
+ CPDF_Function** pFuncs,
+ int nFuncs,
+ CPDF_ColorSpace* pCS,
+ int alpha) {
+ ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
+
+ int row_verts = pShadingStream->GetDict()->GetIntegerBy("VerticesPerRow");
+ if (row_verts < 2)
+ return;
+
+ CPDF_MeshStream stream;
+ if (!stream.Load(pShadingStream, pFuncs, nFuncs, pCS))
+ return;
+
+ CPDF_MeshVertex* vertex = FX_Alloc2D(CPDF_MeshVertex, row_verts, 2);
+ if (!stream.GetVertexRow(vertex, row_verts, pObject2Bitmap)) {
+ FX_Free(vertex);
+ return;
+ }
+ int last_index = 0;
+ while (1) {
+ CPDF_MeshVertex* last_row = vertex + last_index * row_verts;
+ CPDF_MeshVertex* this_row = vertex + (1 - last_index) * row_verts;
+ if (!stream.GetVertexRow(this_row, row_verts, pObject2Bitmap)) {
+ FX_Free(vertex);
+ return;
+ }
+ CPDF_MeshVertex triangle[3];
+ for (int i = 1; i < row_verts; i++) {
+ triangle[0] = last_row[i];
+ triangle[1] = this_row[i - 1];
+ triangle[2] = last_row[i - 1];
+ DrawGouraud(pBitmap, alpha, triangle);
+ triangle[2] = this_row[i];
+ DrawGouraud(pBitmap, alpha, triangle);
+ }
+ last_index = 1 - last_index;
+ }
+ FX_Free(vertex);
+}
+struct Coon_BezierCoeff {
+ float a, b, c, d;
+ void FromPoints(float p0, float p1, float p2, float p3) {
+ a = -p0 + 3 * p1 - 3 * p2 + p3;
+ b = 3 * p0 - 6 * p1 + 3 * p2;
+ c = -3 * p0 + 3 * p1;
+ d = p0;
+ }
+ Coon_BezierCoeff first_half() {
+ Coon_BezierCoeff result;
+ result.a = a / 8;
+ result.b = b / 4;
+ result.c = c / 2;
+ result.d = d;
+ return result;
+ }
+ Coon_BezierCoeff second_half() {
+ Coon_BezierCoeff result;
+ result.a = a / 8;
+ result.b = 3 * a / 8 + b / 4;
+ result.c = 3 * a / 8 + b / 2 + c / 2;
+ result.d = a / 8 + b / 4 + c / 2 + d;
+ return result;
+ }
+ void GetPoints(float p[4]) {
+ p[0] = d;
+ p[1] = c / 3 + p[0];
+ p[2] = b / 3 - p[0] + 2 * p[1];
+ p[3] = a + p[0] - 3 * p[1] + 3 * p[2];
+ }
+ void GetPointsReverse(float p[4]) {
+ p[3] = d;
+ p[2] = c / 3 + p[3];
+ p[1] = b / 3 - p[3] + 2 * p[2];
+ p[0] = a + p[3] - 3 * p[2] + 3 * p[1];
+ }
+ void BezierInterpol(Coon_BezierCoeff& C1,
+ Coon_BezierCoeff& C2,
+ Coon_BezierCoeff& D1,
+ Coon_BezierCoeff& D2) {
+ a = (D1.a + D2.a) / 2;
+ b = (D1.b + D2.b) / 2;
+ c = (D1.c + D2.c) / 2 - (C1.a / 8 + C1.b / 4 + C1.c / 2) +
+ (C2.a / 8 + C2.b / 4) + (-C1.d + D2.d) / 2 - (C2.a + C2.b) / 2;
+ d = C1.a / 8 + C1.b / 4 + C1.c / 2 + C1.d;
+ }
+ float Distance() {
+ float dis = a + b + c;
+ return dis < 0 ? -dis : dis;
+ }
+};
+struct Coon_Bezier {
+ Coon_BezierCoeff x, y;
+ void FromPoints(float x0,
+ float y0,
+ float x1,
+ float y1,
+ float x2,
+ float y2,
+ float x3,
+ float y3) {
+ x.FromPoints(x0, x1, x2, x3);
+ y.FromPoints(y0, y1, y2, y3);
+ }
+ Coon_Bezier first_half() {
+ Coon_Bezier result;
+ result.x = x.first_half();
+ result.y = y.first_half();
+ return result;
+ }
+ Coon_Bezier second_half() {
+ Coon_Bezier result;
+ result.x = x.second_half();
+ result.y = y.second_half();
+ return result;
+ }
+ void BezierInterpol(Coon_Bezier& C1,
+ Coon_Bezier& C2,
+ Coon_Bezier& D1,
+ Coon_Bezier& D2) {
+ x.BezierInterpol(C1.x, C2.x, D1.x, D2.x);
+ y.BezierInterpol(C1.y, C2.y, D1.y, D2.y);
+ }
+ void GetPoints(FX_PATHPOINT* pPoints) {
+ float p[4];
+ int i;
+ x.GetPoints(p);
+ for (i = 0; i < 4; i++) {
+ pPoints[i].m_PointX = p[i];
+ }
+ y.GetPoints(p);
+ for (i = 0; i < 4; i++) {
+ pPoints[i].m_PointY = p[i];
+ }
+ }
+ void GetPointsReverse(FX_PATHPOINT* pPoints) {
+ float p[4];
+ int i;
+ x.GetPointsReverse(p);
+ for (i = 0; i < 4; i++) {
+ pPoints[i].m_PointX = p[i];
+ }
+ y.GetPointsReverse(p);
+ for (i = 0; i < 4; i++) {
+ pPoints[i].m_PointY = p[i];
+ }
+ }
+ float Distance() { return x.Distance() + y.Distance(); }
+};
+static int _BiInterpol(int c0,
+ int c1,
+ int c2,
+ int c3,
+ int x,
+ int y,
+ int x_scale,
+ int y_scale) {
+ int x1 = c0 + (c3 - c0) * x / x_scale;
+ int x2 = c1 + (c2 - c1) * x / x_scale;
+ return x1 + (x2 - x1) * y / y_scale;
+}
+struct Coon_Color {
+ Coon_Color() { FXSYS_memset(comp, 0, sizeof(int) * 3); }
+ int comp[3];
+ void BiInterpol(Coon_Color colors[4],
+ int x,
+ int y,
+ int x_scale,
+ int y_scale) {
+ for (int i = 0; i < 3; i++)
+ comp[i] =
+ _BiInterpol(colors[0].comp[i], colors[1].comp[i], colors[2].comp[i],
+ colors[3].comp[i], x, y, x_scale, y_scale);
+ }
+ int Distance(Coon_Color& o) {
+ int max, diff;
+ max = FXSYS_abs(comp[0] - o.comp[0]);
+ diff = FXSYS_abs(comp[1] - o.comp[1]);
+ if (max < diff) {
+ max = diff;
+ }
+ diff = FXSYS_abs(comp[2] - o.comp[2]);
+ if (max < diff) {
+ max = diff;
+ }
+ return max;
+ }
+};
+struct CPDF_PatchDrawer {
+ Coon_Color patch_colors[4];
+ int max_delta;
+ CFX_PathData path;
+ CFX_RenderDevice* pDevice;
+ int fill_mode;
+ int alpha;
+ void Draw(int x_scale,
+ int y_scale,
+ int left,
+ int bottom,
+ Coon_Bezier C1,
+ Coon_Bezier C2,
+ Coon_Bezier D1,
+ Coon_Bezier D2) {
+ FX_BOOL bSmall = C1.Distance() < 2 && C2.Distance() < 2 &&
+ D1.Distance() < 2 && D2.Distance() < 2;
+ Coon_Color div_colors[4];
+ int d_bottom, d_left, d_top, d_right;
+ div_colors[0].BiInterpol(patch_colors, left, bottom, x_scale, y_scale);
+ if (!bSmall) {
+ div_colors[1].BiInterpol(patch_colors, left, bottom + 1, x_scale,
+ y_scale);
+ div_colors[2].BiInterpol(patch_colors, left + 1, bottom + 1, x_scale,
+ y_scale);
+ div_colors[3].BiInterpol(patch_colors, left + 1, bottom, x_scale,
+ y_scale);
+ d_bottom = div_colors[3].Distance(div_colors[0]);
+ d_left = div_colors[1].Distance(div_colors[0]);
+ d_top = div_colors[1].Distance(div_colors[2]);
+ d_right = div_colors[2].Distance(div_colors[3]);
+ }
+#define COONCOLOR_THRESHOLD 4
+ if (bSmall ||
+ (d_bottom < COONCOLOR_THRESHOLD && d_left < COONCOLOR_THRESHOLD &&
+ d_top < COONCOLOR_THRESHOLD && d_right < COONCOLOR_THRESHOLD)) {
+ FX_PATHPOINT* pPoints = path.GetPoints();
+ C1.GetPoints(pPoints);
+ D2.GetPoints(pPoints + 3);
+ C2.GetPointsReverse(pPoints + 6);
+ D1.GetPointsReverse(pPoints + 9);
+ int fillFlags = FXFILL_WINDING | FXFILL_FULLCOVER;
+ if (fill_mode & RENDER_NOPATHSMOOTH) {
+ fillFlags |= FXFILL_NOPATHSMOOTH;
+ }
+ pDevice->DrawPath(
+ &path, NULL, NULL,
+ FXARGB_MAKE(alpha, div_colors[0].comp[0], div_colors[0].comp[1],
+ div_colors[0].comp[2]),
+ 0, fillFlags);
+ } else {
+ if (d_bottom < COONCOLOR_THRESHOLD && d_top < COONCOLOR_THRESHOLD) {
+ Coon_Bezier m1;
+ m1.BezierInterpol(D1, D2, C1, C2);
+ y_scale *= 2;
+ bottom *= 2;
+ Draw(x_scale, y_scale, left, bottom, C1, m1, D1.first_half(),
+ D2.first_half());
+ Draw(x_scale, y_scale, left, bottom + 1, m1, C2, D1.second_half(),
+ D2.second_half());
+ } else if (d_left < COONCOLOR_THRESHOLD &&
+ d_right < COONCOLOR_THRESHOLD) {
+ Coon_Bezier m2;
+ m2.BezierInterpol(C1, C2, D1, D2);
+ x_scale *= 2;
+ left *= 2;
+ Draw(x_scale, y_scale, left, bottom, C1.first_half(), C2.first_half(),
+ D1, m2);
+ Draw(x_scale, y_scale, left + 1, bottom, C1.second_half(),
+ C2.second_half(), m2, D2);
+ } else {
+ Coon_Bezier m1, m2;
+ m1.BezierInterpol(D1, D2, C1, C2);
+ m2.BezierInterpol(C1, C2, D1, D2);
+ Coon_Bezier m1f = m1.first_half();
+ Coon_Bezier m1s = m1.second_half();
+ Coon_Bezier m2f = m2.first_half();
+ Coon_Bezier m2s = m2.second_half();
+ x_scale *= 2;
+ y_scale *= 2;
+ left *= 2;
+ bottom *= 2;
+ Draw(x_scale, y_scale, left, bottom, C1.first_half(), m1f,
+ D1.first_half(), m2f);
+ Draw(x_scale, y_scale, left, bottom + 1, m1f, C2.first_half(),
+ D1.second_half(), m2s);
+ Draw(x_scale, y_scale, left + 1, bottom, C1.second_half(), m1s, m2f,
+ D2.first_half());
+ Draw(x_scale, y_scale, left + 1, bottom + 1, m1s, C2.second_half(), m2s,
+ D2.second_half());
+ }
+ }
+ }
+};
+
+bool _CheckCoonTensorPara(const CPDF_MeshStream& stream) {
+ bool bCoorBits = (stream.m_nCoordBits == 1 || stream.m_nCoordBits == 2 ||
+ stream.m_nCoordBits == 4 || stream.m_nCoordBits == 8 ||
+ stream.m_nCoordBits == 12 || stream.m_nCoordBits == 16 ||
+ stream.m_nCoordBits == 24 || stream.m_nCoordBits == 32);
+
+ bool bCompBits = (stream.m_nCompBits == 1 || stream.m_nCompBits == 2 ||
+ stream.m_nCompBits == 4 || stream.m_nCompBits == 8 ||
+ stream.m_nCompBits == 12 || stream.m_nCompBits == 16);
+
+ bool bFlagBits = (stream.m_nFlagBits == 2 || stream.m_nFlagBits == 4 ||
+ stream.m_nFlagBits == 8);
+
+ return bCoorBits && bCompBits && bFlagBits;
+}
+
+static void DrawCoonPatchMeshes(FX_BOOL bTensor,
+ CFX_DIBitmap* pBitmap,
+ CFX_Matrix* pObject2Bitmap,
+ CPDF_Stream* pShadingStream,
+ CPDF_Function** pFuncs,
+ int nFuncs,
+ CPDF_ColorSpace* pCS,
+ int fill_mode,
+ int alpha) {
+ ASSERT(pBitmap->GetFormat() == FXDIB_Argb);
+
+ CFX_FxgeDevice device;
+ device.Attach(pBitmap);
+ CPDF_MeshStream stream;
+ if (!stream.Load(pShadingStream, pFuncs, nFuncs, pCS))
+ return;
+ if (!_CheckCoonTensorPara(stream))
+ return;
+
+ CPDF_PatchDrawer patch;
+ patch.alpha = alpha;
+ patch.pDevice = &device;
+ patch.fill_mode = fill_mode;
+ patch.path.SetPointCount(13);
+ FX_PATHPOINT* pPoints = patch.path.GetPoints();
+ pPoints[0].m_Flag = FXPT_MOVETO;
+ for (int i = 1; i < 13; i++) {
+ pPoints[i].m_Flag = FXPT_BEZIERTO;
+ }
+ CFX_PointF coords[16];
+ int point_count = bTensor ? 16 : 12;
+ while (!stream.m_BitStream.IsEOF()) {
+ FX_DWORD flag = stream.GetFlag();
+ int iStartPoint = 0, iStartColor = 0, i = 0;
+ if (flag) {
+ iStartPoint = 4;
+ iStartColor = 2;
+ CFX_PointF tempCoords[4];
+ for (i = 0; i < 4; i++) {
+ tempCoords[i] = coords[(flag * 3 + i) % 12];
+ }
+ FXSYS_memcpy(coords, tempCoords, sizeof(tempCoords));
+ Coon_Color tempColors[2];
+ tempColors[0] = patch.patch_colors[flag];
+ tempColors[1] = patch.patch_colors[(flag + 1) % 4];
+ FXSYS_memcpy(patch.patch_colors, tempColors, sizeof(Coon_Color) * 2);
+ }
+ for (i = iStartPoint; i < point_count; i++) {
+ stream.GetCoords(coords[i].x, coords[i].y);
+ pObject2Bitmap->Transform(coords[i].x, coords[i].y);
+ }
+ for (i = iStartColor; i < 4; i++) {
+ FX_FLOAT r = 0.0f, g = 0.0f, b = 0.0f;
+ stream.GetColor(r, g, b);
+ patch.patch_colors[i].comp[0] = (int32_t)(r * 255);
+ patch.patch_colors[i].comp[1] = (int32_t)(g * 255);
+ patch.patch_colors[i].comp[2] = (int32_t)(b * 255);
+ }
+ CFX_FloatRect bbox = CFX_FloatRect::GetBBox(coords, point_count);
+ if (bbox.right <= 0 || bbox.left >= (FX_FLOAT)pBitmap->GetWidth() ||
+ bbox.top <= 0 || bbox.bottom >= (FX_FLOAT)pBitmap->GetHeight()) {
+ continue;
+ }
+ Coon_Bezier C1, C2, D1, D2;
+ C1.FromPoints(coords[0].x, coords[0].y, coords[11].x, coords[11].y,
+ coords[10].x, coords[10].y, coords[9].x, coords[9].y);
+ C2.FromPoints(coords[3].x, coords[3].y, coords[4].x, coords[4].y,
+ coords[5].x, coords[5].y, coords[6].x, coords[6].y);
+ D1.FromPoints(coords[0].x, coords[0].y, coords[1].x, coords[1].y,
+ coords[2].x, coords[2].y, coords[3].x, coords[3].y);
+ D2.FromPoints(coords[9].x, coords[9].y, coords[8].x, coords[8].y,
+ coords[7].x, coords[7].y, coords[6].x, coords[6].y);
+ patch.Draw(1, 1, 0, 0, C1, C2, D1, D2);
+ }
+}
+void CPDF_RenderStatus::DrawShading(CPDF_ShadingPattern* pPattern,
+ CFX_Matrix* pMatrix,
+ FX_RECT& clip_rect,
+ int alpha,
+ FX_BOOL bAlphaMode) {
+ CPDF_Function** pFuncs = pPattern->m_pFunctions;
+ int nFuncs = pPattern->m_nFuncs;
+ CPDF_Dictionary* pDict = pPattern->m_pShadingObj->GetDict();
+ CPDF_ColorSpace* pColorSpace = pPattern->m_pCS;
+ if (!pColorSpace) {
+ return;
+ }
+ FX_ARGB background = 0;
+ if (!pPattern->m_bShadingObj &&
+ pPattern->m_pShadingObj->GetDict()->KeyExist("Background")) {
+ CPDF_Array* pBackColor =
+ pPattern->m_pShadingObj->GetDict()->GetArrayBy("Background");
+ if (pBackColor &&
+ pBackColor->GetCount() >= pColorSpace->CountComponents()) {
+ CFX_FixedBufGrow<FX_FLOAT, 16> comps(pColorSpace->CountComponents());
+ for (int i = 0; i < pColorSpace->CountComponents(); i++) {
+ comps[i] = pBackColor->GetNumberAt(i);
+ }
+ FX_FLOAT R = 0.0f, G = 0.0f, B = 0.0f;
+ pColorSpace->GetRGB(comps, R, G, B);
+ background = ArgbEncode(255, (int32_t)(R * 255), (int32_t)(G * 255),
+ (int32_t)(B * 255));
+ }
+ }
+ if (pDict->KeyExist("BBox")) {
+ CFX_FloatRect rect = pDict->GetRectBy("BBox");
+ rect.Transform(pMatrix);
+ clip_rect.Intersect(rect.GetOutterRect());
+ }
+ CPDF_DeviceBuffer buffer;
+ buffer.Initialize(m_pContext, m_pDevice, &clip_rect, m_pCurObj, 150);
+ CFX_Matrix FinalMatrix = *pMatrix;
+ FinalMatrix.Concat(*buffer.GetMatrix());
+ CFX_DIBitmap* pBitmap = buffer.GetBitmap();
+ if (!pBitmap->GetBuffer()) {
+ return;
+ }
+ pBitmap->Clear(background);
+ int fill_mode = m_Options.m_Flags;
+ switch (pPattern->m_ShadingType) {
+ case kInvalidShading:
+ case kMaxShading:
+ return;
+ case kFunctionBasedShading:
+ DrawFuncShading(pBitmap, &FinalMatrix, pDict, pFuncs, nFuncs, pColorSpace,
+ alpha);
+ break;
+ case kAxialShading:
+ DrawAxialShading(pBitmap, &FinalMatrix, pDict, pFuncs, nFuncs,
+ pColorSpace, alpha);
+ break;
+ case kRadialShading:
+ DrawRadialShading(pBitmap, &FinalMatrix, pDict, pFuncs, nFuncs,
+ pColorSpace, alpha);
+ break;
+ case kFreeFormGouraudTriangleMeshShading: {
+ DrawFreeGouraudShading(pBitmap, &FinalMatrix,
+ ToStream(pPattern->m_pShadingObj), pFuncs, nFuncs,
+ pColorSpace, alpha);
+ } break;
+ case kLatticeFormGouraudTriangleMeshShading: {
+ DrawLatticeGouraudShading(pBitmap, &FinalMatrix,
+ ToStream(pPattern->m_pShadingObj), pFuncs,
+ nFuncs, pColorSpace, alpha);
+ } break;
+ case kCoonsPatchMeshShading:
+ case kTensorProductPatchMeshShading: {
+ DrawCoonPatchMeshes(
+ pPattern->m_ShadingType == kTensorProductPatchMeshShading, pBitmap,
+ &FinalMatrix, ToStream(pPattern->m_pShadingObj), pFuncs, nFuncs,
+ pColorSpace, fill_mode, alpha);
+ } break;
+ }
+ if (bAlphaMode) {
+ pBitmap->LoadChannel(FXDIB_Red, pBitmap, FXDIB_Alpha);
+ }
+ if (m_Options.m_ColorMode == RENDER_COLOR_GRAY) {
+ pBitmap->ConvertColorScale(m_Options.m_ForeColor, m_Options.m_BackColor);
+ }
+ buffer.OutputToDevice();
+}
+void CPDF_RenderStatus::DrawShadingPattern(CPDF_ShadingPattern* pattern,
+ const CPDF_PageObject* pPageObj,
+ const CFX_Matrix* pObj2Device,
+ FX_BOOL bStroke) {
+ if (!pattern->Load()) {
+ return;
+ }
+ m_pDevice->SaveState();
+ if (pPageObj->IsPath()) {
+ if (!SelectClipPath(pPageObj->AsPath(), pObj2Device, bStroke)) {
+ m_pDevice->RestoreState();
+ return;
+ }
+ } else if (pPageObj->IsImage()) {
+ m_pDevice->SetClip_Rect(pPageObj->GetBBox(pObj2Device));
+ } else {
+ return;
+ }
+ FX_RECT rect;
+ if (GetObjectClippedRect(pPageObj, pObj2Device, FALSE, rect)) {
+ m_pDevice->RestoreState();
+ return;
+ }
+ CFX_Matrix matrix = pattern->m_Pattern2Form;
+ matrix.Concat(*pObj2Device);
+ GetScaledMatrix(matrix);
+ int alpha = pPageObj->m_GeneralState.GetAlpha(bStroke);
+ DrawShading(pattern, &matrix, rect, alpha,
+ m_Options.m_ColorMode == RENDER_COLOR_ALPHA);
+ m_pDevice->RestoreState();
+}
+FX_BOOL CPDF_RenderStatus::ProcessShading(const CPDF_ShadingObject* pShadingObj,
+ const CFX_Matrix* pObj2Device) {
+ FX_RECT rect = pShadingObj->GetBBox(pObj2Device);
+ FX_RECT clip_box = m_pDevice->GetClipBox();
+ rect.Intersect(clip_box);
+ if (rect.IsEmpty()) {
+ return TRUE;
+ }
+ CFX_Matrix matrix = pShadingObj->m_Matrix;
+ matrix.Concat(*pObj2Device);
+ DrawShading(pShadingObj->m_pShading, &matrix, rect,
+ pShadingObj->m_GeneralState.GetAlpha(FALSE),
+ m_Options.m_ColorMode == RENDER_COLOR_ALPHA);
+ return TRUE;
+}
+static CFX_DIBitmap* DrawPatternBitmap(CPDF_Document* pDoc,
+ CPDF_PageRenderCache* pCache,
+ CPDF_TilingPattern* pPattern,
+ const CFX_Matrix* pObject2Device,
+ int width,
+ int height,
+ int flags) {
+ CFX_DIBitmap* pBitmap = new CFX_DIBitmap;
+ if (!pBitmap->Create(width, height,
+ pPattern->m_bColored ? FXDIB_Argb : FXDIB_8bppMask)) {
+ delete pBitmap;
+ return NULL;
+ }
+ CFX_FxgeDevice bitmap_device;
+ bitmap_device.Attach(pBitmap);
+ pBitmap->Clear(0);
+ CFX_FloatRect cell_bbox = pPattern->m_BBox;
+ pPattern->m_Pattern2Form.TransformRect(cell_bbox);
+ pObject2Device->TransformRect(cell_bbox);
+ CFX_FloatRect bitmap_rect(0.0f, 0.0f, (FX_FLOAT)width, (FX_FLOAT)height);
+ CFX_Matrix mtAdjust;
+ mtAdjust.MatchRect(bitmap_rect, cell_bbox);
+ CFX_Matrix mtPattern2Bitmap = *pObject2Device;
+ mtPattern2Bitmap.Concat(mtAdjust);
+ CPDF_RenderOptions options;
+ if (!pPattern->m_bColored) {
+ options.m_ColorMode = RENDER_COLOR_ALPHA;
+ }
+ flags |= RENDER_FORCE_HALFTONE;
+ options.m_Flags = flags;
+ CPDF_RenderContext context(pDoc, pCache);
+ context.AppendLayer(pPattern->m_pForm, &mtPattern2Bitmap);
+ context.Render(&bitmap_device, &options, nullptr);
+ return pBitmap;
+}
+void CPDF_RenderStatus::DrawTilingPattern(CPDF_TilingPattern* pPattern,
+ const CPDF_PageObject* pPageObj,
+ const CFX_Matrix* pObj2Device,
+ FX_BOOL bStroke) {
+ if (!pPattern->Load()) {
+ return;
+ }
+ m_pDevice->SaveState();
+ if (pPageObj->IsPath()) {
+ if (!SelectClipPath(pPageObj->AsPath(), pObj2Device, bStroke)) {
+ m_pDevice->RestoreState();
+ return;
+ }
+ } else if (pPageObj->IsImage()) {
+ m_pDevice->SetClip_Rect(pPageObj->GetBBox(pObj2Device));
+ } else {
+ return;
+ }
+ FX_RECT clip_box = m_pDevice->GetClipBox();
+ if (clip_box.IsEmpty()) {
+ m_pDevice->RestoreState();
+ return;
+ }
+ CFX_Matrix dCTM = m_pDevice->GetCTM();
+ FX_FLOAT sa = FXSYS_fabs(dCTM.a);
+ FX_FLOAT sd = FXSYS_fabs(dCTM.d);
+ clip_box.right = clip_box.left + (int32_t)FXSYS_ceil(clip_box.Width() * sa);
+ clip_box.bottom = clip_box.top + (int32_t)FXSYS_ceil(clip_box.Height() * sd);
+ CFX_Matrix mtPattern2Device = pPattern->m_Pattern2Form;
+ mtPattern2Device.Concat(*pObj2Device);
+ GetScaledMatrix(mtPattern2Device);
+ FX_BOOL bAligned = FALSE;
+ if (pPattern->m_BBox.left == 0 && pPattern->m_BBox.bottom == 0 &&
+ pPattern->m_BBox.right == pPattern->m_XStep &&
+ pPattern->m_BBox.top == pPattern->m_YStep &&
+ (mtPattern2Device.IsScaled() || mtPattern2Device.Is90Rotated())) {
+ bAligned = TRUE;
+ }
+ CFX_FloatRect cell_bbox = pPattern->m_BBox;
+ mtPattern2Device.TransformRect(cell_bbox);
+ int width = (int)FXSYS_ceil(cell_bbox.Width());
+ int height = (int)FXSYS_ceil(cell_bbox.Height());
+ if (width == 0) {
+ width = 1;
+ }
+ if (height == 0) {
+ height = 1;
+ }
+ int min_col, max_col, min_row, max_row;
+ CFX_Matrix mtDevice2Pattern;
+ mtDevice2Pattern.SetReverse(mtPattern2Device);
+ CFX_FloatRect clip_box_p(clip_box);
+ clip_box_p.Transform(&mtDevice2Pattern);
+
+ min_col = (int)FXSYS_ceil((clip_box_p.left - pPattern->m_BBox.right) /
+ pPattern->m_XStep);
+ max_col = (int)FXSYS_floor((clip_box_p.right - pPattern->m_BBox.left) /
+ pPattern->m_XStep);
+ min_row = (int)FXSYS_ceil((clip_box_p.bottom - pPattern->m_BBox.top) /
+ pPattern->m_YStep);
+ max_row = (int)FXSYS_floor((clip_box_p.top - pPattern->m_BBox.bottom) /
+ pPattern->m_YStep);
+
+ if (width > clip_box.Width() || height > clip_box.Height() ||
+ width * height > clip_box.Width() * clip_box.Height()) {
+ CPDF_GraphicStates* pStates = NULL;
+ if (!pPattern->m_bColored) {
+ pStates = CloneObjStates(pPageObj, bStroke);
+ }
+ CPDF_Dictionary* pFormResource = NULL;
+ if (pPattern->m_pForm->m_pFormDict) {
+ pFormResource = pPattern->m_pForm->m_pFormDict->GetDictBy("Resources");
+ }
+ for (int col = min_col; col <= max_col; col++)
+ for (int row = min_row; row <= max_row; row++) {
+ FX_FLOAT orig_x, orig_y;
+ orig_x = col * pPattern->m_XStep;
+ orig_y = row * pPattern->m_YStep;
+ mtPattern2Device.Transform(orig_x, orig_y);
+ CFX_Matrix matrix = *pObj2Device;
+ matrix.Translate(orig_x - mtPattern2Device.e,
+ orig_y - mtPattern2Device.f);
+ m_pDevice->SaveState();
+ CPDF_RenderStatus status;
+ status.Initialize(m_pContext, m_pDevice, NULL, NULL, this, pStates,
+ &m_Options, pPattern->m_pForm->m_Transparency,
+ m_bDropObjects, pFormResource);
+ status.RenderObjectList(pPattern->m_pForm, &matrix);
+ m_pDevice->RestoreState();
+ }
+ m_pDevice->RestoreState();
+ delete pStates;
+ return;
+ }
+ if (bAligned) {
+ int orig_x = FXSYS_round(mtPattern2Device.e);
+ int orig_y = FXSYS_round(mtPattern2Device.f);
+ min_col = (clip_box.left - orig_x) / width;
+ if (clip_box.left < orig_x) {
+ min_col--;
+ }
+ max_col = (clip_box.right - orig_x) / width;
+ if (clip_box.right <= orig_x) {
+ max_col--;
+ }
+ min_row = (clip_box.top - orig_y) / height;
+ if (clip_box.top < orig_y) {
+ min_row--;
+ }
+ max_row = (clip_box.bottom - orig_y) / height;
+ if (clip_box.bottom <= orig_y) {
+ max_row--;
+ }
+ }
+ FX_FLOAT left_offset = cell_bbox.left - mtPattern2Device.e;
+ FX_FLOAT top_offset = cell_bbox.bottom - mtPattern2Device.f;
+ CFX_DIBitmap* pPatternBitmap = NULL;
+ if (width * height < 16) {
+ CFX_DIBitmap* pEnlargedBitmap =
+ DrawPatternBitmap(m_pContext->GetDocument(), m_pContext->GetPageCache(),
+ pPattern, pObj2Device, 8, 8, m_Options.m_Flags);
+ pPatternBitmap = pEnlargedBitmap->StretchTo(width, height);
+ delete pEnlargedBitmap;
+ } else {
+ pPatternBitmap = DrawPatternBitmap(
+ m_pContext->GetDocument(), m_pContext->GetPageCache(), pPattern,
+ pObj2Device, width, height, m_Options.m_Flags);
+ }
+ if (!pPatternBitmap) {
+ m_pDevice->RestoreState();
+ return;
+ }
+ if (m_Options.m_ColorMode == RENDER_COLOR_GRAY) {
+ pPatternBitmap->ConvertColorScale(m_Options.m_ForeColor,
+ m_Options.m_BackColor);
+ }
+ FX_ARGB fill_argb = GetFillArgb(pPageObj);
+ int clip_width = clip_box.right - clip_box.left;
+ int clip_height = clip_box.bottom - clip_box.top;
+ CFX_DIBitmap screen;
+ if (!screen.Create(clip_width, clip_height, FXDIB_Argb)) {
+ return;
+ }
+ screen.Clear(0);
+ FX_DWORD* src_buf = (FX_DWORD*)pPatternBitmap->GetBuffer();
+ for (int col = min_col; col <= max_col; col++) {
+ for (int row = min_row; row <= max_row; row++) {
+ int start_x, start_y;
+ if (bAligned) {
+ start_x = FXSYS_round(mtPattern2Device.e) + col * width - clip_box.left;
+ start_y = FXSYS_round(mtPattern2Device.f) + row * height - clip_box.top;
+ } else {
+ FX_FLOAT orig_x = col * pPattern->m_XStep;
+ FX_FLOAT orig_y = row * pPattern->m_YStep;
+ mtPattern2Device.Transform(orig_x, orig_y);
+ start_x = FXSYS_round(orig_x + left_offset) - clip_box.left;
+ start_y = FXSYS_round(orig_y + top_offset) - clip_box.top;
+ }
+ if (width == 1 && height == 1) {
+ if (start_x < 0 || start_x >= clip_box.Width() || start_y < 0 ||
+ start_y >= clip_box.Height()) {
+ continue;
+ }
+ FX_DWORD* dest_buf =
+ (FX_DWORD*)(screen.GetBuffer() + screen.GetPitch() * start_y +
+ start_x * 4);
+ if (pPattern->m_bColored) {
+ *dest_buf = *src_buf;
+ } else {
+ *dest_buf = (*(uint8_t*)src_buf << 24) | (fill_argb & 0xffffff);
+ }
+ } else {
+ if (pPattern->m_bColored) {
+ screen.CompositeBitmap(start_x, start_y, width, height,
+ pPatternBitmap, 0, 0);
+ } else {
+ screen.CompositeMask(start_x, start_y, width, height, pPatternBitmap,
+ fill_argb, 0, 0);
+ }
+ }
+ }
+ }
+ CompositeDIBitmap(&screen, clip_box.left, clip_box.top, 0, 255,
+ FXDIB_BLEND_NORMAL, FALSE);
+ m_pDevice->RestoreState();
+ delete pPatternBitmap;
+}
+void CPDF_RenderStatus::DrawPathWithPattern(const CPDF_PathObject* pPathObj,
+ const CFX_Matrix* pObj2Device,
+ CPDF_Color* pColor,
+ FX_BOOL bStroke) {
+ CPDF_Pattern* pattern = pColor->GetPattern();
+ if (!pattern) {
+ return;
+ }
+ if (pattern->m_PatternType == CPDF_Pattern::TILING) {
+ DrawTilingPattern(static_cast<CPDF_TilingPattern*>(pattern), pPathObj,
+ pObj2Device, bStroke);
+ } else {
+ DrawShadingPattern(static_cast<CPDF_ShadingPattern*>(pattern), pPathObj,
+ pObj2Device, bStroke);
+ }
+}
+void CPDF_RenderStatus::ProcessPathPattern(const CPDF_PathObject* pPathObj,
+ const CFX_Matrix* pObj2Device,
+ int& filltype,
+ FX_BOOL& bStroke) {
+ if (filltype) {
+ CPDF_Color& FillColor = *pPathObj->m_ColorState.GetFillColor();
+ if (FillColor.m_pCS && FillColor.m_pCS->GetFamily() == PDFCS_PATTERN) {
+ DrawPathWithPattern(pPathObj, pObj2Device, &FillColor, FALSE);
+ filltype = 0;
+ }
+ }
+ if (bStroke) {
+ CPDF_Color& StrokeColor = *pPathObj->m_ColorState.GetStrokeColor();
+ if (StrokeColor.m_pCS && StrokeColor.m_pCS->GetFamily() == PDFCS_PATTERN) {
+ DrawPathWithPattern(pPathObj, pObj2Device, &StrokeColor, TRUE);
+ bStroke = FALSE;
+ }
+ }
+}
diff --git a/core/fpdfapi/fpdf_render/fpdf_render_pattern_embeddertest.cpp b/core/fpdfapi/fpdf_render/fpdf_render_pattern_embeddertest.cpp
new file mode 100644
index 0000000000..176c923372
--- /dev/null
+++ b/core/fpdfapi/fpdf_render/fpdf_render_pattern_embeddertest.cpp
@@ -0,0 +1,17 @@
+// Copyright 2015 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/embedder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class FPDFRenderPatternEmbeddertest : public EmbedderTest {};
+
+TEST_F(FPDFRenderPatternEmbeddertest, LoadError_547706) {
+ // Test shading where object is a dictionary instead of a stream.
+ EXPECT_TRUE(OpenDocument("bug_547706.pdf"));
+ FPDF_PAGE page = LoadPage(0);
+ FPDF_BITMAP bitmap = RenderPage(page);
+ FPDFBitmap_Destroy(bitmap);
+ UnloadPage(page);
+}
diff --git a/core/fpdfapi/fpdf_render/fpdf_render_text.cpp b/core/fpdfapi/fpdf_render/fpdf_render_text.cpp
new file mode 100644
index 0000000000..24b77ed368
--- /dev/null
+++ b/core/fpdfapi/fpdf_render/fpdf_render_text.cpp
@@ -0,0 +1,787 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fpdfapi/fpdf_render/render_int.h"
+
+#include "core/fpdfapi/fpdf_page/pageint.h"
+#include "core/include/fpdfapi/cpdf_dictionary.h"
+#include "core/include/fpdfapi/cpdf_document.h"
+#include "core/include/fpdfapi/fpdf_pageobj.h"
+#include "core/include/fpdfapi/fpdf_render.h"
+#include "core/include/fxge/fx_ge.h"
+
+CPDF_Type3Cache::~CPDF_Type3Cache() {
+ for (const auto& pair : m_SizeMap) {
+ delete pair.second;
+ }
+ m_SizeMap.clear();
+}
+CFX_GlyphBitmap* CPDF_Type3Cache::LoadGlyph(FX_DWORD charcode,
+ const CFX_Matrix* pMatrix,
+ FX_FLOAT retinaScaleX,
+ FX_FLOAT retinaScaleY) {
+ _CPDF_UniqueKeyGen keygen;
+ keygen.Generate(
+ 4, FXSYS_round(pMatrix->a * 10000), FXSYS_round(pMatrix->b * 10000),
+ FXSYS_round(pMatrix->c * 10000), FXSYS_round(pMatrix->d * 10000));
+ CFX_ByteStringC FaceGlyphsKey(keygen.m_Key, keygen.m_KeyLen);
+ CPDF_Type3Glyphs* pSizeCache;
+ auto it = m_SizeMap.find(FaceGlyphsKey);
+ if (it == m_SizeMap.end()) {
+ pSizeCache = new CPDF_Type3Glyphs;
+ m_SizeMap[FaceGlyphsKey] = pSizeCache;
+ } else {
+ pSizeCache = it->second;
+ }
+ auto it2 = pSizeCache->m_GlyphMap.find(charcode);
+ if (it2 != pSizeCache->m_GlyphMap.end())
+ return it2->second;
+
+ CFX_GlyphBitmap* pGlyphBitmap =
+ RenderGlyph(pSizeCache, charcode, pMatrix, retinaScaleX, retinaScaleY);
+ pSizeCache->m_GlyphMap[charcode] = pGlyphBitmap;
+ return pGlyphBitmap;
+}
+CPDF_Type3Glyphs::~CPDF_Type3Glyphs() {
+ for (const auto& pair : m_GlyphMap)
+ delete pair.second;
+}
+static int _AdjustBlue(FX_FLOAT pos, int& count, int blues[]) {
+ FX_FLOAT min_distance = 1000000.0f * 1.0f;
+ int closest_pos = -1;
+ for (int i = 0; i < count; i++) {
+ FX_FLOAT distance = (FX_FLOAT)FXSYS_fabs(pos - (FX_FLOAT)blues[i]);
+ if (distance < 1.0f * 80.0f / 100.0f && distance < min_distance) {
+ min_distance = distance;
+ closest_pos = i;
+ }
+ }
+ if (closest_pos >= 0) {
+ return blues[closest_pos];
+ }
+ int new_pos = FXSYS_round(pos);
+ if (count == TYPE3_MAX_BLUES) {
+ return new_pos;
+ }
+ blues[count++] = new_pos;
+ return new_pos;
+}
+void CPDF_Type3Glyphs::AdjustBlue(FX_FLOAT top,
+ FX_FLOAT bottom,
+ int& top_line,
+ int& bottom_line) {
+ top_line = _AdjustBlue(top, m_TopBlueCount, m_TopBlue);
+ bottom_line = _AdjustBlue(bottom, m_BottomBlueCount, m_BottomBlue);
+}
+
+static FX_BOOL _IsScanLine1bpp(uint8_t* pBuf, int width) {
+ int size = width / 8;
+ for (int i = 0; i < size; i++) {
+ if (pBuf[i])
+ return TRUE;
+ }
+ return (width % 8) && (pBuf[width / 8] & (0xff << (8 - width % 8)));
+}
+
+static FX_BOOL _IsScanLine8bpp(uint8_t* pBuf, int width) {
+ for (int i = 0; i < width; i++) {
+ if (pBuf[i] > 0x40)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static int _DetectFirstLastScan(const CFX_DIBitmap* pBitmap, FX_BOOL bFirst) {
+ int height = pBitmap->GetHeight(), pitch = pBitmap->GetPitch(),
+ width = pBitmap->GetWidth();
+ int bpp = pBitmap->GetBPP();
+ if (bpp > 8) {
+ width *= bpp / 8;
+ }
+ uint8_t* pBuf = pBitmap->GetBuffer();
+ int line = bFirst ? 0 : height - 1;
+ int line_step = bFirst ? 1 : -1;
+ int line_end = bFirst ? height : -1;
+ while (line != line_end) {
+ if (bpp == 1) {
+ if (_IsScanLine1bpp(pBuf + line * pitch, width)) {
+ return line;
+ }
+ } else {
+ if (_IsScanLine8bpp(pBuf + line * pitch, width)) {
+ return line;
+ }
+ }
+ line += line_step;
+ }
+ return -1;
+}
+CFX_GlyphBitmap* CPDF_Type3Cache::RenderGlyph(CPDF_Type3Glyphs* pSize,
+ FX_DWORD charcode,
+ const CFX_Matrix* pMatrix,
+ FX_FLOAT retinaScaleX,
+ FX_FLOAT retinaScaleY) {
+ const CPDF_Type3Char* pChar = m_pFont->LoadChar(charcode);
+ if (!pChar || !pChar->m_pBitmap)
+ return nullptr;
+
+ CFX_DIBitmap* pBitmap = pChar->m_pBitmap;
+ CFX_Matrix image_matrix, text_matrix;
+ image_matrix = pChar->m_ImageMatrix;
+ text_matrix.Set(pMatrix->a, pMatrix->b, pMatrix->c, pMatrix->d, 0, 0);
+ image_matrix.Concat(text_matrix);
+ CFX_DIBitmap* pResBitmap = NULL;
+ int left, top;
+ if (FXSYS_fabs(image_matrix.b) < FXSYS_fabs(image_matrix.a) / 100 &&
+ FXSYS_fabs(image_matrix.c) < FXSYS_fabs(image_matrix.d) / 100) {
+ int top_line, bottom_line;
+ top_line = _DetectFirstLastScan(pBitmap, TRUE);
+ bottom_line = _DetectFirstLastScan(pBitmap, FALSE);
+ if (top_line == 0 && bottom_line == pBitmap->GetHeight() - 1) {
+ FX_FLOAT top_y = image_matrix.d + image_matrix.f;
+ FX_FLOAT bottom_y = image_matrix.f;
+ FX_BOOL bFlipped = top_y > bottom_y;
+ if (bFlipped) {
+ FX_FLOAT temp = top_y;
+ top_y = bottom_y;
+ bottom_y = temp;
+ }
+ pSize->AdjustBlue(top_y, bottom_y, top_line, bottom_line);
+ pResBitmap = pBitmap->StretchTo(
+ (int)(FXSYS_round(image_matrix.a) * retinaScaleX),
+ (int)((bFlipped ? top_line - bottom_line : bottom_line - top_line) *
+ retinaScaleY));
+ top = top_line;
+ if (image_matrix.a < 0) {
+ image_matrix.Scale(retinaScaleX, retinaScaleY);
+ left = FXSYS_round(image_matrix.e + image_matrix.a);
+ } else {
+ left = FXSYS_round(image_matrix.e);
+ }
+ } else {
+ }
+ }
+ if (!pResBitmap) {
+ image_matrix.Scale(retinaScaleX, retinaScaleY);
+ pResBitmap = pBitmap->TransformTo(&image_matrix, left, top);
+ }
+ if (!pResBitmap) {
+ return NULL;
+ }
+ CFX_GlyphBitmap* pGlyph = new CFX_GlyphBitmap;
+ pGlyph->m_Left = left;
+ pGlyph->m_Top = -top;
+ pGlyph->m_Bitmap.TakeOver(pResBitmap);
+ delete pResBitmap;
+ return pGlyph;
+}
+void _CPDF_UniqueKeyGen::Generate(int count, ...) {
+ va_list argList;
+ va_start(argList, count);
+ for (int i = 0; i < count; i++) {
+ int p = va_arg(argList, int);
+ ((FX_DWORD*)m_Key)[i] = p;
+ }
+ va_end(argList);
+ m_KeyLen = count * sizeof(FX_DWORD);
+}
+FX_BOOL CPDF_RenderStatus::ProcessText(const CPDF_TextObject* textobj,
+ const CFX_Matrix* pObj2Device,
+ CFX_PathData* pClippingPath) {
+ if (textobj->m_nChars == 0) {
+ return TRUE;
+ }
+ int text_render_mode = textobj->m_TextState.GetObject()->m_TextMode;
+ if (text_render_mode == 3) {
+ return TRUE;
+ }
+ CPDF_Font* pFont = textobj->m_TextState.GetFont();
+ if (pFont->IsType3Font()) {
+ return ProcessType3Text(textobj, pObj2Device);
+ }
+ FX_BOOL bFill = FALSE, bStroke = FALSE, bClip = FALSE;
+ if (pClippingPath) {
+ bClip = TRUE;
+ } else {
+ switch (text_render_mode) {
+ case 0:
+ case 4:
+ bFill = TRUE;
+ break;
+ case 1:
+ case 5:
+ if (!pFont->GetFace() &&
+ !(pFont->GetSubstFont()->m_SubstFlags & FXFONT_SUBST_GLYPHPATH)) {
+ bFill = TRUE;
+ } else {
+ bStroke = TRUE;
+ }
+ break;
+ case 2:
+ case 6:
+ if (!pFont->GetFace() &&
+ !(pFont->GetSubstFont()->m_SubstFlags & FXFONT_SUBST_GLYPHPATH)) {
+ bFill = TRUE;
+ } else {
+ bFill = bStroke = TRUE;
+ }
+ break;
+ case 3:
+ case 7:
+ return TRUE;
+ default:
+ bFill = TRUE;
+ }
+ }
+ FX_ARGB stroke_argb = 0, fill_argb = 0;
+ FX_BOOL bPattern = FALSE;
+ if (bStroke) {
+ if (textobj->m_ColorState.GetStrokeColor()->IsPattern()) {
+ bPattern = TRUE;
+ } else {
+ stroke_argb = GetStrokeArgb(textobj);
+ }
+ }
+ if (bFill) {
+ if (textobj->m_ColorState.GetFillColor()->IsPattern()) {
+ bPattern = TRUE;
+ } else {
+ fill_argb = GetFillArgb(textobj);
+ }
+ }
+ CFX_Matrix text_matrix;
+ textobj->GetTextMatrix(&text_matrix);
+ if (IsAvailableMatrix(text_matrix) == FALSE) {
+ return TRUE;
+ }
+ FX_FLOAT font_size = textobj->m_TextState.GetFontSize();
+ if (bPattern) {
+ DrawTextPathWithPattern(textobj, pObj2Device, pFont, font_size,
+ &text_matrix, bFill, bStroke);
+ return TRUE;
+ }
+ if (bClip || bStroke) {
+ const CFX_Matrix* pDeviceMatrix = pObj2Device;
+ CFX_Matrix device_matrix;
+ if (bStroke) {
+ const FX_FLOAT* pCTM = textobj->m_TextState.GetObject()->m_CTM;
+ if (pCTM[0] != 1.0f || pCTM[3] != 1.0f) {
+ CFX_Matrix ctm(pCTM[0], pCTM[1], pCTM[2], pCTM[3], 0, 0);
+ text_matrix.ConcatInverse(ctm);
+ device_matrix.Copy(ctm);
+ device_matrix.Concat(*pObj2Device);
+ pDeviceMatrix = &device_matrix;
+ }
+ }
+ int flag = 0;
+ if (bStroke && bFill) {
+ flag |= FX_FILL_STROKE;
+ flag |= FX_STROKE_TEXT_MODE;
+ }
+ const CPDF_GeneralStateData* pGeneralData =
+ ((CPDF_PageObject*)textobj)->m_GeneralState;
+ if (pGeneralData && pGeneralData->m_StrokeAdjust) {
+ flag |= FX_STROKE_ADJUST;
+ }
+ if (m_Options.m_Flags & RENDER_NOTEXTSMOOTH) {
+ flag |= FXFILL_NOPATHSMOOTH;
+ }
+ return CPDF_TextRenderer::DrawTextPath(
+ m_pDevice, textobj->m_nChars, textobj->m_pCharCodes,
+ textobj->m_pCharPos, pFont, font_size, &text_matrix, pDeviceMatrix,
+ textobj->m_GraphState, fill_argb, stroke_argb, pClippingPath, flag);
+ }
+ text_matrix.Concat(*pObj2Device);
+ return CPDF_TextRenderer::DrawNormalText(
+ m_pDevice, textobj->m_nChars, textobj->m_pCharCodes, textobj->m_pCharPos,
+ pFont, font_size, &text_matrix, fill_argb, &m_Options);
+}
+CPDF_Type3Cache* CPDF_RenderStatus::GetCachedType3(CPDF_Type3Font* pFont) {
+ if (!pFont->m_pDocument) {
+ return NULL;
+ }
+ pFont->m_pDocument->GetPageData()->GetFont(pFont->GetFontDict(), FALSE);
+ return pFont->m_pDocument->GetRenderData()->GetCachedType3(pFont);
+}
+static void ReleaseCachedType3(CPDF_Type3Font* pFont) {
+ if (!pFont->m_pDocument) {
+ return;
+ }
+ pFont->m_pDocument->GetRenderData()->ReleaseCachedType3(pFont);
+ pFont->m_pDocument->GetPageData()->ReleaseFont(pFont->GetFontDict());
+}
+FX_BOOL CPDF_Type3Char::LoadBitmap(CPDF_RenderContext* pContext) {
+ if (m_pBitmap || !m_pForm) {
+ return TRUE;
+ }
+ if (m_pForm->GetPageObjectList()->size() == 1 && !m_bColored) {
+ auto& pPageObj = m_pForm->GetPageObjectList()->front();
+ if (pPageObj->IsImage()) {
+ m_ImageMatrix = pPageObj->AsImage()->m_Matrix;
+ const CFX_DIBSource* pSource =
+ pPageObj->AsImage()->m_pImage->LoadDIBSource();
+ if (pSource) {
+ m_pBitmap = pSource->Clone();
+ delete pSource;
+ }
+ delete m_pForm;
+ m_pForm = NULL;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+class CPDF_RefType3Cache {
+ public:
+ CPDF_RefType3Cache(CPDF_Type3Font* pType3Font) {
+ m_dwCount = 0;
+ m_pType3Font = pType3Font;
+ }
+ ~CPDF_RefType3Cache() {
+ while (m_dwCount--) {
+ ReleaseCachedType3(m_pType3Font);
+ }
+ }
+ FX_DWORD m_dwCount;
+ CPDF_Type3Font* m_pType3Font;
+};
+FX_BOOL CPDF_RenderStatus::ProcessType3Text(const CPDF_TextObject* textobj,
+ const CFX_Matrix* pObj2Device) {
+ CPDF_Type3Font* pType3Font = textobj->m_TextState.GetFont()->AsType3Font();
+ for (int j = 0; j < m_Type3FontCache.GetSize(); j++) {
+ if (m_Type3FontCache.GetAt(j) == pType3Font)
+ return TRUE;
+ }
+ CFX_Matrix dCTM = m_pDevice->GetCTM();
+ FX_FLOAT sa = FXSYS_fabs(dCTM.a);
+ FX_FLOAT sd = FXSYS_fabs(dCTM.d);
+ CFX_Matrix text_matrix;
+ textobj->GetTextMatrix(&text_matrix);
+ CFX_Matrix char_matrix = pType3Font->GetFontMatrix();
+ FX_FLOAT font_size = textobj->m_TextState.GetFontSize();
+ char_matrix.Scale(font_size, font_size);
+ FX_ARGB fill_argb = GetFillArgb(textobj, TRUE);
+ int fill_alpha = FXARGB_A(fill_argb);
+ int device_class = m_pDevice->GetDeviceClass();
+ FXTEXT_GLYPHPOS* pGlyphAndPos = NULL;
+ if (device_class == FXDC_DISPLAY) {
+ pGlyphAndPos = FX_Alloc(FXTEXT_GLYPHPOS, textobj->m_nChars);
+ } else if (fill_alpha < 255) {
+ return FALSE;
+ }
+ CPDF_RefType3Cache refTypeCache(pType3Font);
+ FX_DWORD* pChars = textobj->m_pCharCodes;
+ if (textobj->m_nChars == 1) {
+ pChars = (FX_DWORD*)(&textobj->m_pCharCodes);
+ }
+ for (int iChar = 0; iChar < textobj->m_nChars; iChar++) {
+ FX_DWORD charcode = pChars[iChar];
+ if (charcode == (FX_DWORD)-1) {
+ continue;
+ }
+ CPDF_Type3Char* pType3Char = pType3Font->LoadChar(charcode);
+ if (!pType3Char) {
+ continue;
+ }
+ CFX_Matrix matrix = char_matrix;
+ matrix.e += iChar ? textobj->m_pCharPos[iChar - 1] : 0;
+ matrix.Concat(text_matrix);
+ matrix.Concat(*pObj2Device);
+ if (!pType3Char->LoadBitmap(m_pContext)) {
+ if (pGlyphAndPos) {
+ for (int i = 0; i < iChar; i++) {
+ FXTEXT_GLYPHPOS& glyph = pGlyphAndPos[i];
+ if (!glyph.m_pGlyph) {
+ continue;
+ }
+ m_pDevice->SetBitMask(&glyph.m_pGlyph->m_Bitmap,
+ glyph.m_OriginX + glyph.m_pGlyph->m_Left,
+ glyph.m_OriginY - glyph.m_pGlyph->m_Top,
+ fill_argb);
+ }
+ FX_Free(pGlyphAndPos);
+ pGlyphAndPos = NULL;
+ }
+ CPDF_GraphicStates* pStates = CloneObjStates(textobj, FALSE);
+ CPDF_RenderOptions Options = m_Options;
+ Options.m_Flags |= RENDER_FORCE_HALFTONE | RENDER_RECT_AA;
+ Options.m_Flags &= ~RENDER_FORCE_DOWNSAMPLE;
+ CPDF_Dictionary* pFormResource = NULL;
+ if (pType3Char->m_pForm && pType3Char->m_pForm->m_pFormDict) {
+ pFormResource =
+ pType3Char->m_pForm->m_pFormDict->GetDictBy("Resources");
+ }
+ if (fill_alpha == 255) {
+ CPDF_RenderStatus status;
+ status.Initialize(m_pContext, m_pDevice, NULL, NULL, this, pStates,
+ &Options, pType3Char->m_pForm->m_Transparency,
+ m_bDropObjects, pFormResource, FALSE, pType3Char,
+ fill_argb);
+ status.m_Type3FontCache.Append(m_Type3FontCache);
+ status.m_Type3FontCache.Add(pType3Font);
+ m_pDevice->SaveState();
+ status.RenderObjectList(pType3Char->m_pForm, &matrix);
+ m_pDevice->RestoreState();
+ } else {
+ CFX_FloatRect rect_f = pType3Char->m_pForm->CalcBoundingBox();
+ rect_f.Transform(&matrix);
+ FX_RECT rect = rect_f.GetOutterRect();
+ CFX_FxgeDevice bitmap_device;
+ if (!bitmap_device.Create((int)(rect.Width() * sa),
+ (int)(rect.Height() * sd), FXDIB_Argb)) {
+ return TRUE;
+ }
+ bitmap_device.GetBitmap()->Clear(0);
+ CPDF_RenderStatus status;
+ status.Initialize(m_pContext, &bitmap_device, NULL, NULL, this, pStates,
+ &Options, pType3Char->m_pForm->m_Transparency,
+ m_bDropObjects, pFormResource, FALSE, pType3Char,
+ fill_argb);
+ status.m_Type3FontCache.Append(m_Type3FontCache);
+ status.m_Type3FontCache.Add(pType3Font);
+ matrix.TranslateI(-rect.left, -rect.top);
+ matrix.Scale(sa, sd);
+ status.RenderObjectList(pType3Char->m_pForm, &matrix);
+ m_pDevice->SetDIBits(bitmap_device.GetBitmap(), rect.left, rect.top);
+ }
+ delete pStates;
+ } else if (pType3Char->m_pBitmap) {
+ if (device_class == FXDC_DISPLAY) {
+ CPDF_Type3Cache* pCache = GetCachedType3(pType3Font);
+ refTypeCache.m_dwCount++;
+ CFX_GlyphBitmap* pBitmap = pCache->LoadGlyph(charcode, &matrix, sa, sd);
+ if (!pBitmap) {
+ continue;
+ }
+ int origin_x = FXSYS_round(matrix.e);
+ int origin_y = FXSYS_round(matrix.f);
+ if (pGlyphAndPos) {
+ pGlyphAndPos[iChar].m_pGlyph = pBitmap;
+ pGlyphAndPos[iChar].m_OriginX = origin_x;
+ pGlyphAndPos[iChar].m_OriginY = origin_y;
+ } else {
+ m_pDevice->SetBitMask(&pBitmap->m_Bitmap, origin_x + pBitmap->m_Left,
+ origin_y - pBitmap->m_Top, fill_argb);
+ }
+ } else {
+ CFX_Matrix image_matrix = pType3Char->m_ImageMatrix;
+ image_matrix.Concat(matrix);
+ CPDF_ImageRenderer renderer;
+ if (renderer.Start(this, pType3Char->m_pBitmap, fill_argb, 255,
+ &image_matrix, 0, FALSE)) {
+ renderer.Continue(NULL);
+ }
+ if (!renderer.m_Result) {
+ return FALSE;
+ }
+ }
+ }
+ }
+ if (pGlyphAndPos) {
+ FX_RECT rect =
+ FXGE_GetGlyphsBBox(pGlyphAndPos, textobj->m_nChars, 0, sa, sd);
+ CFX_DIBitmap bitmap;
+ if (!bitmap.Create((int)(rect.Width() * sa), (int)(rect.Height() * sd),
+ FXDIB_8bppMask)) {
+ FX_Free(pGlyphAndPos);
+ return TRUE;
+ }
+ bitmap.Clear(0);
+ for (int iChar = 0; iChar < textobj->m_nChars; iChar++) {
+ FXTEXT_GLYPHPOS& glyph = pGlyphAndPos[iChar];
+ if (!glyph.m_pGlyph) {
+ continue;
+ }
+ bitmap.TransferBitmap(
+ (int)((glyph.m_OriginX + glyph.m_pGlyph->m_Left - rect.left) * sa),
+ (int)((glyph.m_OriginY - glyph.m_pGlyph->m_Top - rect.top) * sd),
+ glyph.m_pGlyph->m_Bitmap.GetWidth(),
+ glyph.m_pGlyph->m_Bitmap.GetHeight(), &glyph.m_pGlyph->m_Bitmap, 0,
+ 0);
+ }
+ m_pDevice->SetBitMask(&bitmap, rect.left, rect.top, fill_argb);
+ FX_Free(pGlyphAndPos);
+ }
+ return TRUE;
+}
+class CPDF_CharPosList {
+ public:
+ CPDF_CharPosList();
+ ~CPDF_CharPosList();
+ void Load(int nChars,
+ FX_DWORD* pCharCodes,
+ FX_FLOAT* pCharPos,
+ CPDF_Font* pFont,
+ FX_FLOAT font_size);
+ FXTEXT_CHARPOS* m_pCharPos;
+ FX_DWORD m_nChars;
+};
+
+CPDF_CharPosList::CPDF_CharPosList() {
+ m_pCharPos = NULL;
+}
+CPDF_CharPosList::~CPDF_CharPosList() {
+ FX_Free(m_pCharPos);
+}
+void CPDF_CharPosList::Load(int nChars,
+ FX_DWORD* pCharCodes,
+ FX_FLOAT* pCharPos,
+ CPDF_Font* pFont,
+ FX_FLOAT FontSize) {
+ m_pCharPos = FX_Alloc(FXTEXT_CHARPOS, nChars);
+ m_nChars = 0;
+ CPDF_CIDFont* pCIDFont = pFont->AsCIDFont();
+ FX_BOOL bVertWriting = pCIDFont && pCIDFont->IsVertWriting();
+ for (int iChar = 0; iChar < nChars; iChar++) {
+ FX_DWORD CharCode =
+ nChars == 1 ? (FX_DWORD)(uintptr_t)pCharCodes : pCharCodes[iChar];
+ if (CharCode == (FX_DWORD)-1) {
+ continue;
+ }
+ FX_BOOL bVert = FALSE;
+ FXTEXT_CHARPOS& charpos = m_pCharPos[m_nChars++];
+ if (pCIDFont) {
+ charpos.m_bFontStyle = pCIDFont->IsFontStyleFromCharCode(CharCode);
+ }
+ charpos.m_GlyphIndex = pFont->GlyphFromCharCode(CharCode, &bVert);
+#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
+ charpos.m_ExtGID = pFont->GlyphFromCharCodeExt(CharCode);
+#endif
+ if (!pFont->IsEmbedded() && !pFont->IsCIDFont()) {
+ charpos.m_FontCharWidth = pFont->GetCharWidthF(CharCode);
+ } else {
+ charpos.m_FontCharWidth = 0;
+ }
+ charpos.m_OriginX = iChar ? pCharPos[iChar - 1] : 0;
+ charpos.m_OriginY = 0;
+ charpos.m_bGlyphAdjust = FALSE;
+ if (!pCIDFont) {
+ continue;
+ }
+ FX_WORD CID = pCIDFont->CIDFromCharCode(CharCode);
+ if (bVertWriting) {
+ charpos.m_OriginY = charpos.m_OriginX;
+ charpos.m_OriginX = 0;
+ short vx, vy;
+ pCIDFont->GetVertOrigin(CID, vx, vy);
+ charpos.m_OriginX -= FontSize * vx / 1000;
+ charpos.m_OriginY -= FontSize * vy / 1000;
+ }
+ const uint8_t* pTransform = pCIDFont->GetCIDTransform(CID);
+ if (pTransform && !bVert) {
+ charpos.m_AdjustMatrix[0] = pCIDFont->CIDTransformToFloat(pTransform[0]);
+ charpos.m_AdjustMatrix[2] = pCIDFont->CIDTransformToFloat(pTransform[2]);
+ charpos.m_AdjustMatrix[1] = pCIDFont->CIDTransformToFloat(pTransform[1]);
+ charpos.m_AdjustMatrix[3] = pCIDFont->CIDTransformToFloat(pTransform[3]);
+ charpos.m_OriginX +=
+ pCIDFont->CIDTransformToFloat(pTransform[4]) * FontSize;
+ charpos.m_OriginY +=
+ pCIDFont->CIDTransformToFloat(pTransform[5]) * FontSize;
+ charpos.m_bGlyphAdjust = TRUE;
+ }
+ }
+}
+FX_BOOL CPDF_TextRenderer::DrawTextPath(CFX_RenderDevice* pDevice,
+ int nChars,
+ FX_DWORD* pCharCodes,
+ FX_FLOAT* pCharPos,
+ CPDF_Font* pFont,
+ FX_FLOAT font_size,
+ const CFX_Matrix* pText2User,
+ const CFX_Matrix* pUser2Device,
+ const CFX_GraphStateData* pGraphState,
+ FX_ARGB fill_argb,
+ FX_ARGB stroke_argb,
+ CFX_PathData* pClippingPath,
+ int nFlag) {
+ CFX_FontCache* pCache =
+ pFont->m_pDocument ? pFont->m_pDocument->GetRenderData()->GetFontCache()
+ : NULL;
+ CPDF_CharPosList CharPosList;
+ CharPosList.Load(nChars, pCharCodes, pCharPos, pFont, font_size);
+ return pDevice->DrawTextPath(CharPosList.m_nChars, CharPosList.m_pCharPos,
+ &pFont->m_Font, pCache, font_size, pText2User,
+ pUser2Device, pGraphState, fill_argb,
+ stroke_argb, pClippingPath, nFlag);
+}
+void CPDF_TextRenderer::DrawTextString(CFX_RenderDevice* pDevice,
+ int left,
+ int top,
+ CPDF_Font* pFont,
+ int height,
+ const CFX_ByteString& str,
+ FX_ARGB argb) {
+ FX_RECT font_bbox;
+ pFont->GetFontBBox(font_bbox);
+ FX_FLOAT font_size =
+ (FX_FLOAT)height * 1000.0f / (FX_FLOAT)(font_bbox.top - font_bbox.bottom);
+ FX_FLOAT origin_x = (FX_FLOAT)left;
+ FX_FLOAT origin_y =
+ (FX_FLOAT)top + font_size * (FX_FLOAT)font_bbox.top / 1000.0f;
+ CFX_Matrix matrix(1.0f, 0, 0, -1.0f, 0, 0);
+ DrawTextString(pDevice, origin_x, origin_y, pFont, font_size, &matrix, str,
+ argb);
+}
+void CPDF_TextRenderer::DrawTextString(CFX_RenderDevice* pDevice,
+ FX_FLOAT origin_x,
+ FX_FLOAT origin_y,
+ CPDF_Font* pFont,
+ FX_FLOAT font_size,
+ const CFX_Matrix* pMatrix,
+ const CFX_ByteString& str,
+ FX_ARGB fill_argb,
+ FX_ARGB stroke_argb,
+ const CFX_GraphStateData* pGraphState,
+ const CPDF_RenderOptions* pOptions) {
+ int nChars = pFont->CountChar(str, str.GetLength());
+ if (nChars == 0) {
+ return;
+ }
+ FX_DWORD charcode;
+ int offset = 0;
+ FX_DWORD* pCharCodes;
+ FX_FLOAT* pCharPos;
+ if (nChars == 1) {
+ charcode = pFont->GetNextChar(str, str.GetLength(), offset);
+ pCharCodes = (FX_DWORD*)(uintptr_t)charcode;
+ pCharPos = NULL;
+ } else {
+ pCharCodes = FX_Alloc(FX_DWORD, nChars);
+ pCharPos = FX_Alloc(FX_FLOAT, nChars - 1);
+ FX_FLOAT cur_pos = 0;
+ for (int i = 0; i < nChars; i++) {
+ pCharCodes[i] = pFont->GetNextChar(str, str.GetLength(), offset);
+ if (i) {
+ pCharPos[i - 1] = cur_pos;
+ }
+ cur_pos += pFont->GetCharWidthF(pCharCodes[i]) * font_size / 1000;
+ }
+ }
+ CFX_Matrix matrix;
+ if (pMatrix)
+ matrix = *pMatrix;
+
+ matrix.e = origin_x;
+ matrix.f = origin_y;
+
+ if (!pFont->IsType3Font()) {
+ if (stroke_argb == 0) {
+ DrawNormalText(pDevice, nChars, pCharCodes, pCharPos, pFont, font_size,
+ &matrix, fill_argb, pOptions);
+ } else {
+ DrawTextPath(pDevice, nChars, pCharCodes, pCharPos, pFont, font_size,
+ &matrix, NULL, pGraphState, fill_argb, stroke_argb, NULL);
+ }
+ }
+
+ if (nChars > 1) {
+ FX_Free(pCharCodes);
+ FX_Free(pCharPos);
+ }
+}
+FX_BOOL CPDF_TextRenderer::DrawNormalText(CFX_RenderDevice* pDevice,
+ int nChars,
+ FX_DWORD* pCharCodes,
+ FX_FLOAT* pCharPos,
+ CPDF_Font* pFont,
+ FX_FLOAT font_size,
+ const CFX_Matrix* pText2Device,
+ FX_ARGB fill_argb,
+ const CPDF_RenderOptions* pOptions) {
+ CFX_FontCache* pCache =
+ pFont->m_pDocument ? pFont->m_pDocument->GetRenderData()->GetFontCache()
+ : NULL;
+ CPDF_CharPosList CharPosList;
+ CharPosList.Load(nChars, pCharCodes, pCharPos, pFont, font_size);
+ int FXGE_flags = 0;
+ if (pOptions) {
+ FX_DWORD dwFlags = pOptions->m_Flags;
+ if (dwFlags & RENDER_CLEARTYPE) {
+ FXGE_flags |= FXTEXT_CLEARTYPE;
+ if (dwFlags & RENDER_BGR_STRIPE) {
+ FXGE_flags |= FXTEXT_BGR_STRIPE;
+ }
+ }
+ if (dwFlags & RENDER_NOTEXTSMOOTH) {
+ FXGE_flags |= FXTEXT_NOSMOOTH;
+ }
+ if (dwFlags & RENDER_PRINTGRAPHICTEXT) {
+ FXGE_flags |= FXTEXT_PRINTGRAPHICTEXT;
+ }
+ if (dwFlags & RENDER_NO_NATIVETEXT) {
+ FXGE_flags |= FXTEXT_NO_NATIVETEXT;
+ }
+ if (dwFlags & RENDER_PRINTIMAGETEXT) {
+ FXGE_flags |= FXTEXT_PRINTIMAGETEXT;
+ }
+ } else {
+ FXGE_flags = FXTEXT_CLEARTYPE;
+ }
+ if (pFont->IsCIDFont()) {
+ FXGE_flags |= FXFONT_CIDFONT;
+ }
+ return pDevice->DrawNormalText(CharPosList.m_nChars, CharPosList.m_pCharPos,
+ &pFont->m_Font, pCache, font_size,
+ pText2Device, fill_argb, FXGE_flags);
+}
+void CPDF_RenderStatus::DrawTextPathWithPattern(const CPDF_TextObject* textobj,
+ const CFX_Matrix* pObj2Device,
+ CPDF_Font* pFont,
+ FX_FLOAT font_size,
+ const CFX_Matrix* pTextMatrix,
+ FX_BOOL bFill,
+ FX_BOOL bStroke) {
+ if (!bStroke) {
+ CPDF_PathObject path;
+ CPDF_TextObject* pCopy = textobj->Clone();
+ path.m_bStroke = FALSE;
+ path.m_FillType = FXFILL_WINDING;
+ path.m_ClipPath.AppendTexts(&pCopy, 1);
+ path.m_ColorState = textobj->m_ColorState;
+ path.m_Path.New()->AppendRect(textobj->m_Left, textobj->m_Bottom,
+ textobj->m_Right, textobj->m_Top);
+ path.m_Left = textobj->m_Left;
+ path.m_Bottom = textobj->m_Bottom;
+ path.m_Right = textobj->m_Right;
+ path.m_Top = textobj->m_Top;
+ RenderSingleObject(&path, pObj2Device);
+ return;
+ }
+ CFX_FontCache* pCache;
+ if (pFont->m_pDocument) {
+ pCache = pFont->m_pDocument->GetRenderData()->GetFontCache();
+ } else {
+ pCache = CFX_GEModule::Get()->GetFontCache();
+ }
+ CFX_FaceCache* pFaceCache = pCache->GetCachedFace(&pFont->m_Font);
+ FX_FONTCACHE_DEFINE(pCache, &pFont->m_Font);
+ CPDF_CharPosList CharPosList;
+ CharPosList.Load(textobj->m_nChars, textobj->m_pCharCodes,
+ textobj->m_pCharPos, pFont, font_size);
+ for (FX_DWORD i = 0; i < CharPosList.m_nChars; i++) {
+ FXTEXT_CHARPOS& charpos = CharPosList.m_pCharPos[i];
+ const CFX_PathData* pPath = pFaceCache->LoadGlyphPath(
+ &pFont->m_Font, charpos.m_GlyphIndex, charpos.m_FontCharWidth);
+ if (!pPath) {
+ continue;
+ }
+ CPDF_PathObject path;
+ path.m_GraphState = textobj->m_GraphState;
+ path.m_ColorState = textobj->m_ColorState;
+ CFX_Matrix matrix;
+ if (charpos.m_bGlyphAdjust)
+ matrix.Set(charpos.m_AdjustMatrix[0], charpos.m_AdjustMatrix[1],
+ charpos.m_AdjustMatrix[2], charpos.m_AdjustMatrix[3], 0, 0);
+ matrix.Concat(font_size, 0, 0, font_size, charpos.m_OriginX,
+ charpos.m_OriginY);
+ path.m_Path.New()->Append(pPath, &matrix);
+ path.m_Matrix = *pTextMatrix;
+ path.m_bStroke = bStroke;
+ path.m_FillType = bFill ? FXFILL_WINDING : 0;
+ path.CalcBoundingBox();
+ ProcessPath(&path, pObj2Device);
+ }
+}
diff --git a/core/fpdfapi/fpdf_render/render_int.h b/core/fpdfapi/fpdf_render/render_int.h
new file mode 100644
index 0000000000..c15612466c
--- /dev/null
+++ b/core/fpdfapi/fpdf_render/render_int.h
@@ -0,0 +1,619 @@
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FPDFAPI_FPDF_RENDER_RENDER_INT_H_
+#define CORE_FPDFAPI_FPDF_RENDER_RENDER_INT_H_
+
+#include <map>
+#include <memory>
+
+#include "core/include/fpdfapi/fpdf_pageobj.h"
+#include "core/include/fpdfapi/fpdf_render.h"
+
+class CFX_GlyphBitmap;
+class CFX_ImageTransformer;
+class CPDF_ImageCacheEntry;
+class CPDF_ImageLoaderHandle;
+class ICodec_ScanlineDecoder;
+
+#define TYPE3_MAX_BLUES 16
+
+class CPDF_Type3Glyphs {
+ public:
+ CPDF_Type3Glyphs() : m_TopBlueCount(0), m_BottomBlueCount(0) {}
+ ~CPDF_Type3Glyphs();
+ void AdjustBlue(FX_FLOAT top,
+ FX_FLOAT bottom,
+ int& top_line,
+ int& bottom_line);
+
+ std::map<FX_DWORD, CFX_GlyphBitmap*> m_GlyphMap;
+ int m_TopBlue[TYPE3_MAX_BLUES];
+ int m_BottomBlue[TYPE3_MAX_BLUES];
+ int m_TopBlueCount;
+ int m_BottomBlueCount;
+};
+class CPDF_Type3Cache {
+ public:
+ explicit CPDF_Type3Cache(CPDF_Type3Font* pFont) : m_pFont(pFont) {}
+ ~CPDF_Type3Cache();
+
+ CFX_GlyphBitmap* LoadGlyph(FX_DWORD charcode,
+ const CFX_Matrix* pMatrix,
+ FX_FLOAT retinaScaleX = 1.0f,
+ FX_FLOAT retinaScaleY = 1.0f);
+
+ protected:
+ CFX_GlyphBitmap* RenderGlyph(CPDF_Type3Glyphs* pSize,
+ FX_DWORD charcode,
+ const CFX_Matrix* pMatrix,
+ FX_FLOAT retinaScaleX = 1.0f,
+ FX_FLOAT retinaScaleY = 1.0f);
+ CPDF_Type3Font* const m_pFont;
+ std::map<CFX_ByteString, CPDF_Type3Glyphs*> m_SizeMap;
+};
+
+class CPDF_TransferFunc {
+ public:
+ explicit CPDF_TransferFunc(CPDF_Document* pDoc);
+
+ FX_COLORREF TranslateColor(FX_COLORREF src) const;
+ CFX_DIBSource* TranslateImage(const CFX_DIBSource* pSrc,
+ FX_BOOL bAutoDropSrc);
+
+ CPDF_Document* const m_pPDFDoc;
+ FX_BOOL m_bIdentity;
+ uint8_t m_Samples[256 * 3];
+};
+
+class CPDF_DocRenderData {
+ public:
+ CPDF_DocRenderData(CPDF_Document* pPDFDoc = NULL);
+ ~CPDF_DocRenderData();
+ CPDF_Type3Cache* GetCachedType3(CPDF_Type3Font* pFont);
+ CPDF_TransferFunc* GetTransferFunc(CPDF_Object* pObj);
+ CFX_FontCache* GetFontCache() { return m_pFontCache; }
+ void Clear(FX_BOOL bRelease = FALSE);
+ void ReleaseCachedType3(CPDF_Type3Font* pFont);
+ void ReleaseTransferFunc(CPDF_Object* pObj);
+
+ private:
+ using CPDF_Type3CacheMap =
+ std::map<CPDF_Font*, CPDF_CountedObject<CPDF_Type3Cache>*>;
+ using CPDF_TransferFuncMap =
+ std::map<CPDF_Object*, CPDF_CountedObject<CPDF_TransferFunc>*>;
+
+ CPDF_Document* m_pPDFDoc;
+ CFX_FontCache* m_pFontCache;
+ CPDF_Type3CacheMap m_Type3FaceMap;
+ CPDF_TransferFuncMap m_TransferFuncMap;
+};
+
+class IPDF_ObjectRenderer {
+ public:
+ static IPDF_ObjectRenderer* Create();
+ virtual ~IPDF_ObjectRenderer() {}
+ virtual FX_BOOL Start(CPDF_RenderStatus* pRenderStatus,
+ const CPDF_PageObject* pObj,
+ const CFX_Matrix* pObj2Device,
+ FX_BOOL bStdCS,
+ int blendType = FXDIB_BLEND_NORMAL) = 0;
+ virtual FX_BOOL Continue(IFX_Pause* pPause) = 0;
+ FX_BOOL m_Result;
+};
+
+class CPDF_RenderStatus {
+ public:
+ CPDF_RenderStatus();
+ ~CPDF_RenderStatus();
+ FX_BOOL Initialize(class CPDF_RenderContext* pContext,
+ CFX_RenderDevice* pDevice,
+ const CFX_Matrix* pDeviceMatrix,
+ const CPDF_PageObject* pStopObj,
+ const CPDF_RenderStatus* pParentStatus,
+ const CPDF_GraphicStates* pInitialStates,
+ const CPDF_RenderOptions* pOptions,
+ int transparency,
+ FX_BOOL bDropObjects,
+ CPDF_Dictionary* pFormResource = NULL,
+ FX_BOOL bStdCS = FALSE,
+ CPDF_Type3Char* pType3Char = NULL,
+ FX_ARGB fill_color = 0,
+ FX_DWORD GroupFamily = 0,
+ FX_BOOL bLoadMask = FALSE);
+ void RenderObjectList(const CPDF_PageObjectHolder* pObjectHolder,
+ const CFX_Matrix* pObj2Device);
+ void RenderSingleObject(const CPDF_PageObject* pObj,
+ const CFX_Matrix* pObj2Device);
+ FX_BOOL ContinueSingleObject(const CPDF_PageObject* pObj,
+ const CFX_Matrix* pObj2Device,
+ IFX_Pause* pPause);
+ CPDF_RenderContext* GetContext() { return m_pContext; }
+
+ CPDF_RenderOptions m_Options;
+ CPDF_Dictionary* m_pFormResource;
+ CPDF_Dictionary* m_pPageResource;
+ CFX_ArrayTemplate<CPDF_Type3Font*> m_Type3FontCache;
+
+ protected:
+ friend class CPDF_ImageRenderer;
+ friend class CPDF_RenderContext;
+ void ProcessClipPath(CPDF_ClipPath ClipPath, const CFX_Matrix* pObj2Device);
+ void DrawClipPath(CPDF_ClipPath ClipPath, const CFX_Matrix* pObj2Device);
+ FX_BOOL ProcessTransparency(const CPDF_PageObject* PageObj,
+ const CFX_Matrix* pObj2Device);
+ void ProcessObjectNoClip(const CPDF_PageObject* PageObj,
+ const CFX_Matrix* pObj2Device);
+ void DrawObjWithBackground(const CPDF_PageObject* pObj,
+ const CFX_Matrix* pObj2Device);
+ FX_BOOL DrawObjWithBlend(const CPDF_PageObject* pObj,
+ const CFX_Matrix* pObj2Device);
+ FX_BOOL ProcessPath(const CPDF_PathObject* pPathObj,
+ const CFX_Matrix* pObj2Device);
+ void ProcessPathPattern(const CPDF_PathObject* pPathObj,
+ const CFX_Matrix* pObj2Device,
+ int& filltype,
+ FX_BOOL& bStroke);
+ void DrawPathWithPattern(const CPDF_PathObject* pPathObj,
+ const CFX_Matrix* pObj2Device,
+ CPDF_Color* pColor,
+ FX_BOOL bStroke);
+ void DrawTilingPattern(CPDF_TilingPattern* pPattern,
+ const CPDF_PageObject* pPageObj,
+ const CFX_Matrix* pObj2Device,
+ FX_BOOL bStroke);
+ void DrawShadingPattern(CPDF_ShadingPattern* pPattern,
+ const CPDF_PageObject* pPageObj,
+ const CFX_Matrix* pObj2Device,
+ FX_BOOL bStroke);
+ FX_BOOL SelectClipPath(const CPDF_PathObject* pPathObj,
+ const CFX_Matrix* pObj2Device,
+ FX_BOOL bStroke);
+ FX_BOOL ProcessImage(const CPDF_ImageObject* pImageObj,
+ const CFX_Matrix* pObj2Device);
+ FX_BOOL OutputBitmapAlpha(CPDF_ImageObject* pImageObj,
+ const CFX_Matrix* pImage2Device);
+ FX_BOOL OutputImage(CPDF_ImageObject* pImageObj,
+ const CFX_Matrix* pImage2Device);
+ FX_BOOL OutputDIBSource(const CFX_DIBSource* pOutputBitmap,
+ FX_ARGB fill_argb,
+ int bitmap_alpha,
+ const CFX_Matrix* pImage2Device,
+ CPDF_ImageCacheEntry* pImageCache,
+ FX_DWORD flags);
+ void CompositeDIBitmap(CFX_DIBitmap* pDIBitmap,
+ int left,
+ int top,
+ FX_ARGB mask_argb,
+ int bitmap_alpha,
+ int blend_mode,
+ int bIsolated);
+ FX_BOOL ProcessShading(const CPDF_ShadingObject* pShadingObj,
+ const CFX_Matrix* pObj2Device);
+ void DrawShading(CPDF_ShadingPattern* pPattern,
+ CFX_Matrix* pMatrix,
+ FX_RECT& clip_rect,
+ int alpha,
+ FX_BOOL bAlphaMode);
+ FX_BOOL ProcessType3Text(const CPDF_TextObject* textobj,
+ const CFX_Matrix* pObj2Device);
+ FX_BOOL ProcessText(const CPDF_TextObject* textobj,
+ const CFX_Matrix* pObj2Device,
+ CFX_PathData* pClippingPath);
+ void DrawTextPathWithPattern(const CPDF_TextObject* textobj,
+ const CFX_Matrix* pObj2Device,
+ CPDF_Font* pFont,
+ FX_FLOAT font_size,
+ const CFX_Matrix* pTextMatrix,
+ FX_BOOL bFill,
+ FX_BOOL bStroke);
+ FX_BOOL ProcessForm(const CPDF_FormObject* pFormObj,
+ const CFX_Matrix* pObj2Device);
+ CFX_DIBitmap* GetBackdrop(const CPDF_PageObject* pObj,
+ const FX_RECT& rect,
+ int& left,
+ int& top,
+ FX_BOOL bBackAlphaRequired);
+ CFX_DIBitmap* LoadSMask(CPDF_Dictionary* pSMaskDict,
+ FX_RECT* pClipRect,
+ const CFX_Matrix* pMatrix);
+ void Init(CPDF_RenderContext* pParent);
+ static class CPDF_Type3Cache* GetCachedType3(CPDF_Type3Font* pFont);
+ static CPDF_GraphicStates* CloneObjStates(const CPDF_GraphicStates* pPathObj,
+ FX_BOOL bStroke);
+ CPDF_TransferFunc* GetTransferFunc(CPDF_Object* pObject) const;
+ FX_ARGB GetFillArgb(const CPDF_PageObject* pObj,
+ FX_BOOL bType3 = FALSE) const;
+ FX_ARGB GetStrokeArgb(const CPDF_PageObject* pObj) const;
+ CPDF_RenderContext* m_pContext;
+ FX_BOOL m_bStopped;
+ void DitherObjectArea(const CPDF_PageObject* pObj,
+ const CFX_Matrix* pObj2Device);
+ FX_BOOL GetObjectClippedRect(const CPDF_PageObject* pObj,
+ const CFX_Matrix* pObj2Device,
+ FX_BOOL bLogical,
+ FX_RECT& rect) const;
+ void GetScaledMatrix(CFX_Matrix& matrix) const;
+
+ protected:
+ static const int kRenderMaxRecursionDepth = 64;
+ static int s_CurrentRecursionDepth;
+
+ CFX_RenderDevice* m_pDevice;
+ CFX_Matrix m_DeviceMatrix;
+ CPDF_ClipPath m_LastClipPath;
+ const CPDF_PageObject* m_pCurObj;
+ const CPDF_PageObject* m_pStopObj;
+ CPDF_GraphicStates m_InitialStates;
+ int m_HalftoneLimit;
+ std::unique_ptr<IPDF_ObjectRenderer> m_pObjectRenderer;
+ FX_BOOL m_bPrint;
+ int m_Transparency;
+ int m_DitherBits;
+ FX_BOOL m_bDropObjects;
+ FX_BOOL m_bStdCS;
+ FX_DWORD m_GroupFamily;
+ FX_BOOL m_bLoadMask;
+ CPDF_Type3Char* m_pType3Char;
+ FX_ARGB m_T3FillColor;
+ int m_curBlend;
+};
+class CPDF_ImageLoader {
+ public:
+ CPDF_ImageLoader()
+ : m_pBitmap(nullptr),
+ m_pMask(nullptr),
+ m_MatteColor(0),
+ m_bCached(FALSE),
+ m_nDownsampleWidth(0),
+ m_nDownsampleHeight(0) {}
+ ~CPDF_ImageLoader();
+
+ FX_BOOL Start(const CPDF_ImageObject* pImage,
+ CPDF_PageRenderCache* pCache,
+ CPDF_ImageLoaderHandle*& LoadHandle,
+ FX_BOOL bStdCS = FALSE,
+ FX_DWORD GroupFamily = 0,
+ FX_BOOL bLoadMask = FALSE,
+ CPDF_RenderStatus* pRenderStatus = NULL,
+ int32_t nDownsampleWidth = 0,
+ int32_t nDownsampleHeight = 0);
+ FX_BOOL Continue(CPDF_ImageLoaderHandle* LoadHandle, IFX_Pause* pPause);
+
+ CFX_DIBSource* m_pBitmap;
+ CFX_DIBSource* m_pMask;
+ FX_DWORD m_MatteColor;
+ FX_BOOL m_bCached;
+
+ protected:
+ int32_t m_nDownsampleWidth;
+ int32_t m_nDownsampleHeight;
+};
+class CPDF_ImageLoaderHandle {
+ public:
+ CPDF_ImageLoaderHandle();
+ ~CPDF_ImageLoaderHandle();
+
+ FX_BOOL Start(CPDF_ImageLoader* pImageLoader,
+ const CPDF_ImageObject* pImage,
+ CPDF_PageRenderCache* pCache,
+ FX_BOOL bStdCS = FALSE,
+ FX_DWORD GroupFamily = 0,
+ FX_BOOL bLoadMask = FALSE,
+ CPDF_RenderStatus* pRenderStatus = NULL,
+ int32_t nDownsampleWidth = 0,
+ int32_t nDownsampleHeight = 0);
+ FX_BOOL Continue(IFX_Pause* pPause);
+
+ protected:
+ CPDF_ImageLoader* m_pImageLoader;
+ CPDF_PageRenderCache* m_pCache;
+ CPDF_ImageObject* m_pImage;
+ int32_t m_nDownsampleWidth;
+ int32_t m_nDownsampleHeight;
+};
+
+class CPDF_ImageRenderer : public IPDF_ObjectRenderer {
+ public:
+ CPDF_ImageRenderer();
+ ~CPDF_ImageRenderer() override;
+
+ // IPDF_ObjectRenderer
+ FX_BOOL Start(CPDF_RenderStatus* pStatus,
+ const CPDF_PageObject* pObj,
+ const CFX_Matrix* pObj2Device,
+ FX_BOOL bStdCS,
+ int blendType = FXDIB_BLEND_NORMAL) override;
+ FX_BOOL Continue(IFX_Pause* pPause) override;
+
+ FX_BOOL Start(CPDF_RenderStatus* pStatus,
+ const CFX_DIBSource* pDIBSource,
+ FX_ARGB bitmap_argb,
+ int bitmap_alpha,
+ const CFX_Matrix* pImage2Device,
+ FX_DWORD flags,
+ FX_BOOL bStdCS,
+ int blendType = FXDIB_BLEND_NORMAL);
+
+ protected:
+ CPDF_RenderStatus* m_pRenderStatus;
+ const CPDF_ImageObject* m_pImageObject;
+ int m_Status;
+ const CFX_Matrix* m_pObj2Device;
+ CFX_Matrix m_ImageMatrix;
+ CPDF_ImageLoader m_Loader;
+ const CFX_DIBSource* m_pDIBSource;
+ CFX_DIBitmap* m_pClone;
+ int m_BitmapAlpha;
+ FX_BOOL m_bPatternColor;
+ CPDF_Pattern* m_pPattern;
+ FX_ARGB m_FillArgb;
+ FX_DWORD m_Flags;
+ CFX_ImageTransformer* m_pTransformer;
+ void* m_DeviceHandle;
+ CPDF_ImageLoaderHandle* m_LoadHandle;
+ FX_BOOL m_bStdCS;
+ int m_BlendType;
+ FX_BOOL StartBitmapAlpha();
+ FX_BOOL StartDIBSource();
+ FX_BOOL StartRenderDIBSource();
+ FX_BOOL StartLoadDIBSource();
+ FX_BOOL DrawMaskedImage();
+ FX_BOOL DrawPatternImage(const CFX_Matrix* pObj2Device);
+};
+
+class CPDF_ScaledRenderBuffer {
+ public:
+ CPDF_ScaledRenderBuffer();
+ ~CPDF_ScaledRenderBuffer();
+
+ FX_BOOL Initialize(CPDF_RenderContext* pContext,
+ CFX_RenderDevice* pDevice,
+ const FX_RECT& pRect,
+ const CPDF_PageObject* pObj,
+ const CPDF_RenderOptions* pOptions = NULL,
+ int max_dpi = 0);
+ CFX_RenderDevice* GetDevice() {
+ return m_pBitmapDevice ? m_pBitmapDevice.get() : m_pDevice;
+ }
+ CFX_Matrix* GetMatrix() { return &m_Matrix; }
+ void OutputToDevice();
+
+ private:
+ CFX_RenderDevice* m_pDevice;
+ CPDF_RenderContext* m_pContext;
+ FX_RECT m_Rect;
+ const CPDF_PageObject* m_pObject;
+ std::unique_ptr<CFX_FxgeDevice> m_pBitmapDevice;
+ CFX_Matrix m_Matrix;
+};
+
+class CPDF_DeviceBuffer {
+ public:
+ CPDF_DeviceBuffer();
+ ~CPDF_DeviceBuffer();
+ FX_BOOL Initialize(CPDF_RenderContext* pContext,
+ CFX_RenderDevice* pDevice,
+ FX_RECT* pRect,
+ const CPDF_PageObject* pObj,
+ int max_dpi = 0);
+ void OutputToDevice();
+ CFX_DIBitmap* GetBitmap() const { return m_pBitmap.get(); }
+ const CFX_Matrix* GetMatrix() const { return &m_Matrix; }
+
+ private:
+ CFX_RenderDevice* m_pDevice;
+ CPDF_RenderContext* m_pContext;
+ FX_RECT m_Rect;
+ const CPDF_PageObject* m_pObject;
+ std::unique_ptr<CFX_DIBitmap> m_pBitmap;
+ CFX_Matrix m_Matrix;
+};
+
+class CPDF_ImageCacheEntry {
+ public:
+ CPDF_ImageCacheEntry(CPDF_Document* pDoc, CPDF_Stream* pStream);
+ ~CPDF_ImageCacheEntry();
+ void ClearImageData();
+ void Reset(const CFX_DIBitmap* pBitmap);
+ FX_BOOL GetCachedBitmap(CFX_DIBSource*& pBitmap,
+ CFX_DIBSource*& pMask,
+ FX_DWORD& MatteColor,
+ CPDF_Dictionary* pPageResources,
+ FX_BOOL bStdCS = FALSE,
+ FX_DWORD GroupFamily = 0,
+ FX_BOOL bLoadMask = FALSE,
+ CPDF_RenderStatus* pRenderStatus = NULL,
+ int32_t downsampleWidth = 0,
+ int32_t downsampleHeight = 0);
+ FX_DWORD EstimateSize() const { return m_dwCacheSize; }
+ FX_DWORD GetTimeCount() const { return m_dwTimeCount; }
+ CPDF_Stream* GetStream() const { return m_pStream; }
+ void SetTimeCount(FX_DWORD dwTimeCount) { m_dwTimeCount = dwTimeCount; }
+ int m_dwTimeCount;
+
+ public:
+ int StartGetCachedBitmap(CPDF_Dictionary* pFormResources,
+ CPDF_Dictionary* pPageResources,
+ FX_BOOL bStdCS = FALSE,
+ FX_DWORD GroupFamily = 0,
+ FX_BOOL bLoadMask = FALSE,
+ CPDF_RenderStatus* pRenderStatus = NULL,
+ int32_t downsampleWidth = 0,
+ int32_t downsampleHeight = 0);
+ int Continue(IFX_Pause* pPause);
+ CFX_DIBSource* DetachBitmap();
+ CFX_DIBSource* DetachMask();
+ CFX_DIBSource* m_pCurBitmap;
+ CFX_DIBSource* m_pCurMask;
+ FX_DWORD m_MatteColor;
+ CPDF_RenderStatus* m_pRenderStatus;
+
+ protected:
+ void ContinueGetCachedBitmap();
+
+ CPDF_Document* m_pDocument;
+ CPDF_Stream* m_pStream;
+ CFX_DIBSource* m_pCachedBitmap;
+ CFX_DIBSource* m_pCachedMask;
+ FX_DWORD m_dwCacheSize;
+ void CalcSize();
+};
+typedef struct {
+ FX_FLOAT m_DecodeMin;
+ FX_FLOAT m_DecodeStep;
+ int m_ColorKeyMin;
+ int m_ColorKeyMax;
+} DIB_COMP_DATA;
+
+class CPDF_DIBSource : public CFX_DIBSource {
+ public:
+ CPDF_DIBSource();
+ ~CPDF_DIBSource() override;
+
+ FX_BOOL Load(CPDF_Document* pDoc,
+ const CPDF_Stream* pStream,
+ CPDF_DIBSource** ppMask,
+ FX_DWORD* pMatteColor,
+ CPDF_Dictionary* pFormResources,
+ CPDF_Dictionary* pPageResources,
+ FX_BOOL bStdCS = FALSE,
+ FX_DWORD GroupFamily = 0,
+ FX_BOOL bLoadMask = FALSE);
+
+ // CFX_DIBSource
+ FX_BOOL SkipToScanline(int line, IFX_Pause* pPause) const override;
+ uint8_t* GetBuffer() const override;
+ const uint8_t* GetScanline(int line) const override;
+ void DownSampleScanline(int line,
+ uint8_t* dest_scan,
+ int dest_bpp,
+ int dest_width,
+ FX_BOOL bFlipX,
+ int clip_left,
+ int clip_width) const override;
+ void SetDownSampleSize(int dest_width, int dest_height) override;
+
+ CFX_DIBitmap* GetBitmap() const;
+ void ReleaseBitmap(CFX_DIBitmap* pBitmap) const;
+ void ClearImageData();
+ FX_DWORD GetMatteColor() const { return m_MatteColor; }
+
+ int StartLoadDIBSource(CPDF_Document* pDoc,
+ const CPDF_Stream* pStream,
+ FX_BOOL bHasMask,
+ CPDF_Dictionary* pFormResources,
+ CPDF_Dictionary* pPageResources,
+ FX_BOOL bStdCS = FALSE,
+ FX_DWORD GroupFamily = 0,
+ FX_BOOL bLoadMask = FALSE);
+ int ContinueLoadDIBSource(IFX_Pause* pPause);
+ int StratLoadMask();
+ int StartLoadMaskDIB();
+ int ContinueLoadMaskDIB(IFX_Pause* pPause);
+ int ContinueToLoadMask();
+ CPDF_DIBSource* DetachMask();
+
+ private:
+ bool LoadColorInfo(const CPDF_Dictionary* pFormResources,
+ const CPDF_Dictionary* pPageResources);
+ DIB_COMP_DATA* GetDecodeAndMaskArray(FX_BOOL& bDefaultDecode,
+ FX_BOOL& bColorKey);
+ CPDF_DIBSource* LoadMask(FX_DWORD& MatteColor);
+ CPDF_DIBSource* LoadMaskDIB(CPDF_Stream* pMask);
+ void LoadJpxBitmap();
+ void LoadPalette();
+ int CreateDecoder();
+ void TranslateScanline24bpp(uint8_t* dest_scan,
+ const uint8_t* src_scan) const;
+ void ValidateDictParam();
+ void DownSampleScanline1Bit(int orig_Bpp,
+ int dest_Bpp,
+ FX_DWORD src_width,
+ const uint8_t* pSrcLine,
+ uint8_t* dest_scan,
+ int dest_width,
+ FX_BOOL bFlipX,
+ int clip_left,
+ int clip_width) const;
+ void DownSampleScanline8Bit(int orig_Bpp,
+ int dest_Bpp,
+ FX_DWORD src_width,
+ const uint8_t* pSrcLine,
+ uint8_t* dest_scan,
+ int dest_width,
+ FX_BOOL bFlipX,
+ int clip_left,
+ int clip_width) const;
+ void DownSampleScanline32Bit(int orig_Bpp,
+ int dest_Bpp,
+ FX_DWORD src_width,
+ const uint8_t* pSrcLine,
+ uint8_t* dest_scan,
+ int dest_width,
+ FX_BOOL bFlipX,
+ int clip_left,
+ int clip_width) const;
+ FX_BOOL TransMask() const;
+
+ CPDF_Document* m_pDocument;
+ const CPDF_Stream* m_pStream;
+ std::unique_ptr<CPDF_StreamAcc> m_pStreamAcc;
+ const CPDF_Dictionary* m_pDict;
+ CPDF_ColorSpace* m_pColorSpace;
+ FX_DWORD m_Family;
+ FX_DWORD m_bpc;
+ FX_DWORD m_bpc_orig;
+ FX_DWORD m_nComponents;
+ FX_DWORD m_GroupFamily;
+ FX_DWORD m_MatteColor;
+ FX_BOOL m_bLoadMask;
+ FX_BOOL m_bDefaultDecode;
+ FX_BOOL m_bImageMask;
+ FX_BOOL m_bDoBpcCheck;
+ FX_BOOL m_bColorKey;
+ FX_BOOL m_bHasMask;
+ FX_BOOL m_bStdCS;
+ DIB_COMP_DATA* m_pCompData;
+ uint8_t* m_pLineBuf;
+ uint8_t* m_pMaskedLine;
+ std::unique_ptr<CFX_DIBitmap> m_pCachedBitmap;
+ std::unique_ptr<ICodec_ScanlineDecoder> m_pDecoder;
+ void* m_pJbig2Context;
+ CPDF_DIBSource* m_pMask;
+ std::unique_ptr<CPDF_StreamAcc> m_pGlobalStream;
+ CPDF_Stream* m_pMaskStream;
+ int m_Status;
+};
+
+#define FPDF_HUGE_IMAGE_SIZE 60000000
+class CPDF_DIBTransferFunc : public CFX_FilteredDIB {
+ public:
+ CPDF_DIBTransferFunc(const CPDF_TransferFunc* pTransferFunc);
+ ~CPDF_DIBTransferFunc() override;
+
+ // CFX_FilteredDIB
+ FXDIB_Format GetDestFormat() override;
+ FX_ARGB* GetDestPalette() override { return NULL; }
+ void TranslateScanline(uint8_t* dest_buf,
+ const uint8_t* src_buf) const override;
+ void TranslateDownSamples(uint8_t* dest_buf,
+ const uint8_t* src_buf,
+ int pixels,
+ int Bpp) const override;
+
+ const uint8_t* m_RampR;
+ const uint8_t* m_RampG;
+ const uint8_t* m_RampB;
+};
+
+struct _CPDF_UniqueKeyGen {
+ void Generate(int count, ...);
+ FX_CHAR m_Key[128];
+ int m_KeyLen;
+};
+
+#endif // CORE_FPDFAPI_FPDF_RENDER_RENDER_INT_H_