diff options
-rw-r--r-- | core/fpdfapi/page/cpdf_pageobjectholder.cpp | 5 | ||||
-rw-r--r-- | core/fpdfapi/page/cpdf_pageobjectholder.h | 11 | ||||
-rw-r--r-- | core/fpdfapi/page/cpdf_streamcontentparser.cpp | 10 | ||||
-rw-r--r-- | core/fpdfapi/render/cpdf_progressiverenderer.cpp | 6 | ||||
-rw-r--r-- | core/fpdfapi/render/cpdf_renderoptions.h | 1 | ||||
-rw-r--r-- | fpdfsdk/fpdfview.cpp | 207 |
6 files changed, 223 insertions, 17 deletions
diff --git a/core/fpdfapi/page/cpdf_pageobjectholder.cpp b/core/fpdfapi/page/cpdf_pageobjectholder.cpp index 974baa65a0..5f5597e5ac 100644 --- a/core/fpdfapi/page/cpdf_pageobjectholder.cpp +++ b/core/fpdfapi/page/cpdf_pageobjectholder.cpp @@ -21,7 +21,6 @@ CPDF_PageObjectHolder::CPDF_PageObjectHolder() m_pResources(nullptr), m_Transparency(0), m_bBackgroundAlphaNeeded(false), - m_bHasImageMask(false), m_ParseState(CONTENT_NOT_PARSED) {} CPDF_PageObjectHolder::~CPDF_PageObjectHolder() {} @@ -37,6 +36,10 @@ void CPDF_PageObjectHolder::ContinueParse(IFX_Pause* pPause) { } } +void CPDF_PageObjectHolder::AddImageMaskBoundingBox(const CFX_FloatRect& box) { + m_MaskBoundingBoxes.push_back(box); +} + void CPDF_PageObjectHolder::Transform(const CFX_Matrix& matrix) { for (auto& pObj : m_PageObjectList) pObj->Transform(matrix); diff --git a/core/fpdfapi/page/cpdf_pageobjectholder.h b/core/fpdfapi/page/cpdf_pageobjectholder.h index aee7617c1f..0b88a86518 100644 --- a/core/fpdfapi/page/cpdf_pageobjectholder.h +++ b/core/fpdfapi/page/cpdf_pageobjectholder.h @@ -8,6 +8,7 @@ #define CORE_FPDFAPI_PAGE_CPDF_PAGEOBJECTHOLDER_H_ #include <memory> +#include <vector> #include "core/fpdfapi/page/cpdf_pageobjectlist.h" #include "core/fxcrt/fx_coordinates.h" @@ -41,9 +42,11 @@ class CPDF_PageObjectHolder { m_bBackgroundAlphaNeeded = needed; } - bool HasImageMask() const { return m_bHasImageMask; } - void SetHasImageMask(bool value) { m_bHasImageMask = value; } - + bool HasImageMask() const { return !m_MaskBoundingBoxes.empty(); } + const std::vector<CFX_FloatRect>& GetMaskBoundingBoxes() const { + return m_MaskBoundingBoxes; + } + void AddImageMaskBoundingBox(const CFX_FloatRect& box); void Transform(const CFX_Matrix& matrix); CFX_FloatRect CalcBoundingBox() const; @@ -61,7 +64,7 @@ class CPDF_PageObjectHolder { void LoadTransInfo(); bool m_bBackgroundAlphaNeeded; - bool m_bHasImageMask; + std::vector<CFX_FloatRect> m_MaskBoundingBoxes; ParseState m_ParseState; std::unique_ptr<CPDF_ContentParser> m_pParser; CPDF_PageObjectList m_PageObjectList; diff --git a/core/fpdfapi/page/cpdf_streamcontentparser.cpp b/core/fpdfapi/page/cpdf_streamcontentparser.cpp index 30686133c2..d9f12f121b 100644 --- a/core/fpdfapi/page/cpdf_streamcontentparser.cpp +++ b/core/fpdfapi/page/cpdf_streamcontentparser.cpp @@ -735,7 +735,11 @@ void CPDF_StreamContentParser::Handle_ExecuteXObject() { CFX_ByteString name = GetString(0); if (name == m_LastImageName && m_pLastImage && m_pLastImage->GetStream() && m_pLastImage->GetStream()->GetObjNum()) { - AddImage(m_pLastImage); + CPDF_ImageObject* pObj = AddImage(m_pLastImage); + // Record the bounding box of this image, so rendering code can draw it + // properly. + if (pObj->GetImage()->IsMask()) + m_pObjectHolder->AddImageMaskBoundingBox(pObj->GetRect()); return; } @@ -757,8 +761,8 @@ void CPDF_StreamContentParser::Handle_ExecuteXObject() { m_LastImageName = name; m_pLastImage = pObj->GetImage(); - if (!m_pObjectHolder->HasImageMask()) - m_pObjectHolder->SetHasImageMask(m_pLastImage->IsMask()); + if (m_pLastImage->IsMask()) + m_pObjectHolder->AddImageMaskBoundingBox(pObj->GetRect()); } else if (type == "Form") { AddForm(pXObject); } diff --git a/core/fpdfapi/render/cpdf_progressiverenderer.cpp b/core/fpdfapi/render/cpdf_progressiverenderer.cpp index 6aa3b8a309..c3cef1fc77 100644 --- a/core/fpdfapi/render/cpdf_progressiverenderer.cpp +++ b/core/fpdfapi/render/cpdf_progressiverenderer.cpp @@ -6,6 +6,8 @@ #include "core/fpdfapi/render/cpdf_progressiverenderer.h" +#include "core/fpdfapi/page/cpdf_image.h" +#include "core/fpdfapi/page/cpdf_imageobject.h" #include "core/fpdfapi/page/cpdf_pageobject.h" #include "core/fpdfapi/page/cpdf_pageobjectholder.h" #include "core/fpdfapi/render/cpdf_pagerendercache.h" @@ -95,6 +97,10 @@ void CPDF_ProgressiveRenderer::Continue(IFX_Pause* pPause) { return; nObjsToGo = kStepLimit; } + if (pCurObj->IsImage() && pCurObj->AsImage()->GetImage()->IsMask() && + (m_pOptions->m_Flags & RENDER_BREAKFORMASKS)) { + return; + } ++iter; } if (m_pCurrentLayer->m_pObjectHolder->IsParsed()) { diff --git a/core/fpdfapi/render/cpdf_renderoptions.h b/core/fpdfapi/render/cpdf_renderoptions.h index b934941d00..ee26ccbb07 100644 --- a/core/fpdfapi/render/cpdf_renderoptions.h +++ b/core/fpdfapi/render/cpdf_renderoptions.h @@ -28,6 +28,7 @@ #define RENDER_PRINTIMAGETEXT 0x00000200 #define RENDER_OVERPRINT 0x00000400 #define RENDER_THINLINE 0x00000800 +#define RENDER_BREAKFORMASKS 0x00001000 #define RENDER_NOTEXTSMOOTH 0x10000000 #define RENDER_NOPATHSMOOTH 0x20000000 #define RENDER_NOIMAGESMOOTH 0x40000000 diff --git a/fpdfsdk/fpdfview.cpp b/fpdfsdk/fpdfview.cpp index 5010c17ceb..6f32473985 100644 --- a/fpdfsdk/fpdfview.cpp +++ b/fpdfsdk/fpdfview.cpp @@ -8,10 +8,14 @@ #include <memory> #include <utility> +#include <vector> #include "core/fpdfapi/cpdf_modulemgr.h" #include "core/fpdfapi/cpdf_pagerendercontext.h" +#include "core/fpdfapi/page/cpdf_image.h" +#include "core/fpdfapi/page/cpdf_imageobject.h" #include "core/fpdfapi/page/cpdf_page.h" +#include "core/fpdfapi/page/cpdf_pageobject.h" #include "core/fpdfapi/parser/cpdf_array.h" #include "core/fpdfapi/parser/cpdf_document.h" #include "core/fpdfapi/parser/fpdf_parser_decode.h" @@ -687,6 +691,146 @@ DLLEXPORT double STDCALL FPDF_GetPageHeight(FPDF_PAGE page) { } #if defined(_WIN32) +namespace { + +const double kEpsilonSize = 0.01f; + +void GetScaling(CPDF_Page* pPage, + int size_x, + int size_y, + int rotate, + double* scale_x, + double* scale_y) { + ASSERT(pPage); + ASSERT(scale_x); + ASSERT(scale_y); + double page_width = pPage->GetPageWidth(); + double page_height = pPage->GetPageHeight(); + if (page_width < kEpsilonSize || page_height < kEpsilonSize) + return; + + if (rotate % 2 == 0) { + *scale_x = size_x / page_width; + *scale_y = size_y / page_height; + } else { + *scale_x = size_y / page_width; + *scale_y = size_x / page_height; + } +} + +FX_RECT GetMaskDimensionsAndOffsets(CPDF_Page* pPage, + int start_x, + int start_y, + int size_x, + int size_y, + int rotate, + const CFX_FloatRect& mask_box) { + double scale_x = 0.0f; + double scale_y = 0.0f; + GetScaling(pPage, size_x, size_y, rotate, &scale_x, &scale_y); + if (scale_x < kEpsilonSize || scale_y < kEpsilonSize) + return FX_RECT(); + + // Compute sizes in page points. Round down to catch the entire bitmap. + int start_x_bm = static_cast<int>(mask_box.left * scale_x); + int start_y_bm = static_cast<int>(mask_box.bottom * scale_y); + int size_x_bm = static_cast<int>(mask_box.right * scale_x + 1.0f) - + static_cast<int>(mask_box.left * scale_x); + int size_y_bm = static_cast<int>(mask_box.top * scale_y + 1.0f) - + static_cast<int>(mask_box.bottom * scale_y); + + // Get page rotation + int page_rotation = 0; + CPDF_Dictionary* pDict = pPage->m_pFormDict; + while (pDict) { + if (pDict->KeyExist("Rotate")) { + CPDF_Object* pRotateObj = pDict->GetObjectFor("Rotate")->GetDirect(); + page_rotation = pRotateObj ? pRotateObj->GetInteger() / 90 : 0; + break; + } + if (!pDict->KeyExist("Parent")) + break; + + pDict = ToDictionary(pDict->GetObjectFor("Parent")->GetDirect()); + } + + // Compute offsets + int offset_x = 0; + int offset_y = 0; + switch ((rotate + page_rotation) % 4) { + case 0: + offset_x = start_x_bm + start_x; + offset_y = start_y + size_y - size_y_bm - start_y_bm; + break; + case 1: + offset_x = start_y_bm + start_x; + offset_y = start_x_bm + start_y; + break; + case 2: + offset_x = start_x + size_x - size_x_bm - start_x_bm; + offset_y = start_y_bm + start_y; + break; + case 3: + offset_x = start_x + size_x - size_x_bm - start_y_bm; + offset_y = start_y + size_y - size_y_bm - start_x_bm; + break; + } + return FX_RECT(offset_x, offset_y, offset_x + size_x_bm, + offset_y + size_y_bm); +} + +// Get a bitmap of just the mask section defined by |mask_box| from a full page +// bitmap |pBitmap|. +CFX_RetainPtr<CFX_DIBitmap> GetMaskBitmap(CPDF_Page* pPage, + int start_x, + int start_y, + int size_x, + int size_y, + int rotate, + CFX_RetainPtr<CFX_DIBitmap>& pSrc, + const CFX_FloatRect& mask_box, + FX_RECT* bitmap_area) { + ASSERT(bitmap_area); + *bitmap_area = GetMaskDimensionsAndOffsets(pPage, start_x, start_y, size_x, + size_y, rotate, mask_box); + if (bitmap_area->IsEmpty()) + return nullptr; + + // Create a new bitmap to transfer part of the page bitmap to. + CFX_RetainPtr<CFX_DIBitmap> pDst = pdfium::MakeRetain<CFX_DIBitmap>(); + pDst->Create(bitmap_area->Width(), bitmap_area->Height(), FXDIB_Argb); + pDst->Clear(0x00ffffff); + pDst->TransferBitmap(0, 0, bitmap_area->Width(), bitmap_area->Height(), pSrc, + bitmap_area->left, bitmap_area->top); + return pDst; +} + +void RenderBitmap(CFX_RenderDevice* device, + const CFX_RetainPtr<CFX_DIBitmap>& pSrc, + const FX_RECT& mask_area) { + int size_x_bm = mask_area.Width(); + int size_y_bm = mask_area.Height(); + if (size_x_bm == 0 || size_y_bm == 0) + return; + + // Create a new bitmap from the old one + CFX_RetainPtr<CFX_DIBitmap> pDst = pdfium::MakeRetain<CFX_DIBitmap>(); + pDst->Create(size_x_bm, size_y_bm, FXDIB_Rgb32); + pDst->Clear(0xffffffff); + pDst->CompositeBitmap(0, 0, size_x_bm, size_y_bm, pSrc, 0, 0, + FXDIB_BLEND_NORMAL, nullptr, false); + + if (device->GetDeviceCaps(FXDC_DEVICE_CLASS) == FXDC_PRINTER) { + device->StretchDIBitsWithFlagsAndBlend(pDst, mask_area.left, mask_area.top, + size_x_bm, size_y_bm, 0, + FXDIB_BLEND_NORMAL); + } else { + device->SetDIBits(pDst, mask_area.left, mask_area.top); + } +} + +} // namespace + DLLEXPORT void STDCALL FPDF_RenderPage(HDC dc, FPDF_PAGE page, int start_x, @@ -698,30 +842,74 @@ DLLEXPORT void STDCALL FPDF_RenderPage(HDC dc, CPDF_Page* pPage = CPDFPageFromFPDFPage(page); if (!pPage) return; - - CPDF_PageRenderContext* pContext = new CPDF_PageRenderContext; - pPage->SetRenderContext(pdfium::WrapUnique(pContext)); + pPage->SetRenderContext(pdfium::MakeUnique<CPDF_PageRenderContext>()); + CPDF_PageRenderContext* pContext = pPage->GetRenderContext(); CFX_RetainPtr<CFX_DIBitmap> pBitmap; - // TODO: This results in unnecessary rasterization of some PDFs due to - // HasImageMask() returning true. If any image on the page is a mask, the - // entire page gets rasterized and the spool size gets huge. + // Don't render the full page to bitmap for a mask unless there are a lot + // of masks. Full page bitmaps result in large spool sizes, so they should + // only be used when necessary. For large numbers of masks, rendering each + // individually is inefficient and unlikely to significantly improve spool + // size. const bool bNewBitmap = - pPage->BackgroundAlphaNeeded() || pPage->HasImageMask(); - if (bNewBitmap) { + pPage->BackgroundAlphaNeeded() || + (pPage->HasImageMask() && pPage->GetMaskBoundingBoxes().size() > 100); + const bool bHasMask = pPage->HasImageMask() && !bNewBitmap; + if (bNewBitmap || bHasMask) { pBitmap = pdfium::MakeRetain<CFX_DIBitmap>(); pBitmap->Create(size_x, size_y, FXDIB_Argb); pBitmap->Clear(0x00ffffff); CFX_FxgeDevice* pDevice = new CFX_FxgeDevice; pContext->m_pDevice = pdfium::WrapUnique(pDevice); pDevice->Attach(pBitmap, false, nullptr, false); + if (bHasMask) { + pContext->m_pOptions = pdfium::MakeUnique<CPDF_RenderOptions>(); + pContext->m_pOptions->m_Flags |= RENDER_BREAKFORMASKS; + } } else { pContext->m_pDevice = pdfium::MakeUnique<CFX_WindowsDevice>(dc); } + FPDF_RenderPage_Retail(pContext, page, start_x, start_y, size_x, size_y, rotate, flags, true, nullptr); - if (bNewBitmap) { + if (bHasMask) { + // Finish rendering the page to bitmap and copy the correct segments + // of the page to individual image mask bitmaps. + const std::vector<CFX_FloatRect>& mask_boxes = + pPage->GetMaskBoundingBoxes(); + std::vector<FX_RECT> bitmap_areas(mask_boxes.size()); + std::vector<CFX_RetainPtr<CFX_DIBitmap>> bitmaps(mask_boxes.size()); + for (size_t i = 0; i < mask_boxes.size(); i++) { + bitmaps[i] = + GetMaskBitmap(pPage, start_x, start_y, size_x, size_y, rotate, + pBitmap, mask_boxes[i], &bitmap_areas[i]); + pContext->m_pRenderer->Continue(nullptr); + } + + // Reset rendering context + pPage->SetRenderContext(nullptr); + + // Begin rendering to the printer. Add flag to indicate the renderer should + // pause after each image mask. + pPage->SetRenderContext(pdfium::MakeUnique<CPDF_PageRenderContext>()); + pContext = pPage->GetRenderContext(); + pContext->m_pDevice = pdfium::MakeUnique<CFX_WindowsDevice>(dc); + pContext->m_pOptions = pdfium::MakeUnique<CPDF_RenderOptions>(); + pContext->m_pOptions->m_Flags |= RENDER_BREAKFORMASKS; + FPDF_RenderPage_Retail(pContext, page, start_x, start_y, size_x, size_y, + rotate, flags, true, nullptr); + + // Render masks + for (size_t i = 0; i < mask_boxes.size(); i++) { + // Render the bitmap for the mask and free the bitmap. + if (bitmaps[i]) { // will be null if mask has zero area + RenderBitmap(pContext->m_pDevice.get(), bitmaps[i], bitmap_areas[i]); + } + // Render the next portion of page. + pContext->m_pRenderer->Continue(nullptr); + } + } else if (bNewBitmap) { CFX_WindowsDevice WinDC(dc); if (WinDC.GetDeviceCaps(FXDC_DEVICE_CLASS) == FXDC_PRINTER) { auto pDst = pdfium::MakeRetain<CFX_DIBitmap>(); @@ -735,6 +923,7 @@ DLLEXPORT void STDCALL FPDF_RenderPage(HDC dc, WinDC.SetDIBits(pBitmap, 0, 0); } } + pPage->SetRenderContext(nullptr); } #endif // defined(_WIN32) |