xref: /aosp_15_r20/external/pdfium/core/fpdfapi/render/cpdf_rendertiling.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
1 // Copyright 2020 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6 
7 #include "core/fpdfapi/render/cpdf_rendertiling.h"
8 
9 #include <limits>
10 #include <memory>
11 
12 #include "core/fpdfapi/page/cpdf_form.h"
13 #include "core/fpdfapi/page/cpdf_pageimagecache.h"
14 #include "core/fpdfapi/page/cpdf_tilingpattern.h"
15 #include "core/fpdfapi/parser/cpdf_document.h"
16 #include "core/fpdfapi/render/cpdf_rendercontext.h"
17 #include "core/fpdfapi/render/cpdf_renderoptions.h"
18 #include "core/fpdfapi/render/cpdf_renderstatus.h"
19 #include "core/fxcrt/fx_safe_types.h"
20 #include "core/fxge/cfx_defaultrenderdevice.h"
21 #include "core/fxge/dib/cfx_dibitmap.h"
22 
23 namespace {
24 
DrawPatternBitmap(CPDF_Document * pDoc,CPDF_PageImageCache * pCache,CPDF_TilingPattern * pPattern,CPDF_Form * pPatternForm,const CFX_Matrix & mtObject2Device,int width,int height,const CPDF_RenderOptions::Options & draw_options)25 RetainPtr<CFX_DIBitmap> DrawPatternBitmap(
26     CPDF_Document* pDoc,
27     CPDF_PageImageCache* pCache,
28     CPDF_TilingPattern* pPattern,
29     CPDF_Form* pPatternForm,
30     const CFX_Matrix& mtObject2Device,
31     int width,
32     int height,
33     const CPDF_RenderOptions::Options& draw_options) {
34   auto pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
35   if (!pBitmap->Create(width, height,
36                        pPattern->colored() ? FXDIB_Format::kArgb
37                                            : FXDIB_Format::k8bppMask)) {
38     return nullptr;
39   }
40   CFX_DefaultRenderDevice bitmap_device;
41   bitmap_device.AttachWithBackdropAndGroupKnockout(
42       pBitmap, /*pBackdropBitmap=*/nullptr, /*bGroupKnockout=*/true);
43   CFX_FloatRect cell_bbox =
44       pPattern->pattern_to_form().TransformRect(pPattern->bbox());
45   cell_bbox = mtObject2Device.TransformRect(cell_bbox);
46   CFX_FloatRect bitmap_rect(0.0f, 0.0f, width, height);
47   CFX_Matrix mtAdjust;
48   mtAdjust.MatchRect(bitmap_rect, cell_bbox);
49 
50   CFX_Matrix mtPattern2Bitmap = mtObject2Device * mtAdjust;
51   CPDF_RenderOptions options;
52   if (!pPattern->colored())
53     options.SetColorMode(CPDF_RenderOptions::kAlpha);
54 
55   options.GetOptions() = draw_options;
56   options.GetOptions().bForceHalftone = true;
57 
58   CPDF_RenderContext context(pDoc, nullptr, pCache);
59   context.AppendLayer(pPatternForm, mtPattern2Bitmap);
60   context.Render(&bitmap_device, nullptr, &options, nullptr);
61 
62 #if defined(_SKIA_SUPPORT_)
63   if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
64     pBitmap->UnPreMultiply();
65 #endif  // defined(_SKIA_SUPPORT_)
66   return pBitmap;
67 }
68 
69 }  // namespace
70 
71 // static
Draw(CPDF_RenderStatus * pRenderStatus,CPDF_PageObject * pPageObj,CPDF_TilingPattern * pPattern,CPDF_Form * pPatternForm,const CFX_Matrix & mtObj2Device,const FX_RECT & clip_box,bool bStroke)72 RetainPtr<CFX_DIBitmap> CPDF_RenderTiling::Draw(
73     CPDF_RenderStatus* pRenderStatus,
74     CPDF_PageObject* pPageObj,
75     CPDF_TilingPattern* pPattern,
76     CPDF_Form* pPatternForm,
77     const CFX_Matrix& mtObj2Device,
78     const FX_RECT& clip_box,
79     bool bStroke) {
80   const CFX_Matrix mtPattern2Device =
81       pPattern->pattern_to_form() * mtObj2Device;
82 
83   CFX_FloatRect cell_bbox = mtPattern2Device.TransformRect(pPattern->bbox());
84 
85   float ceil_height = std::ceil(cell_bbox.Height());
86   float ceil_width = std::ceil(cell_bbox.Width());
87 
88   // Validate the float will fit into the int when the conversion is done.
89   if (!pdfium::base::IsValueInRangeForNumericType<int>(ceil_height) ||
90       !pdfium::base::IsValueInRangeForNumericType<int>(ceil_width)) {
91     return nullptr;
92   }
93 
94   int width = static_cast<int>(ceil_width);
95   int height = static_cast<int>(ceil_height);
96   if (width <= 0)
97     width = 1;
98   if (height <= 0)
99     height = 1;
100 
101   CFX_FloatRect clip_box_p =
102       mtPattern2Device.GetInverse().TransformRect(CFX_FloatRect(clip_box));
103   int min_col = static_cast<int>(
104       ceil((clip_box_p.left - pPattern->bbox().right) / pPattern->x_step()));
105   int max_col = static_cast<int>(
106       floor((clip_box_p.right - pPattern->bbox().left) / pPattern->x_step()));
107   int min_row = static_cast<int>(
108       ceil((clip_box_p.bottom - pPattern->bbox().top) / pPattern->y_step()));
109   int max_row = static_cast<int>(
110       floor((clip_box_p.top - pPattern->bbox().bottom) / pPattern->y_step()));
111 
112   // Make sure we can fit the needed width * height into an int.
113   if (height > std::numeric_limits<int>::max() / width)
114     return nullptr;
115 
116   CFX_RenderDevice* pDevice = pRenderStatus->GetRenderDevice();
117   CPDF_RenderContext* pContext = pRenderStatus->GetContext();
118   const CPDF_RenderOptions& options = pRenderStatus->GetRenderOptions();
119   if (width > clip_box.Width() || height > clip_box.Height() ||
120       width * height > clip_box.Width() * clip_box.Height()) {
121     std::unique_ptr<CPDF_GraphicStates> pStates;
122     if (!pPattern->colored())
123       pStates = CPDF_RenderStatus::CloneObjStates(pPageObj, bStroke);
124 
125     RetainPtr<const CPDF_Dictionary> pFormResource =
126         pPatternForm->GetDict()->GetDictFor("Resources");
127     for (int col = min_col; col <= max_col; col++) {
128       for (int row = min_row; row <= max_row; row++) {
129         CFX_PointF original = mtPattern2Device.Transform(
130             CFX_PointF(col * pPattern->x_step(), row * pPattern->y_step()));
131         CFX_Matrix matrix = mtObj2Device;
132         matrix.Translate(original.x - mtPattern2Device.e,
133                          original.y - mtPattern2Device.f);
134         CFX_RenderDevice::StateRestorer restorer2(pDevice);
135         CPDF_RenderStatus status(pContext, pDevice);
136         status.SetOptions(options);
137         status.SetTransparency(pPatternForm->GetTransparency());
138         status.SetFormResource(pFormResource);
139         status.SetDropObjects(pRenderStatus->GetDropObjects());
140         status.Initialize(pRenderStatus, pStates.get());
141         status.RenderObjectList(pPatternForm, matrix);
142       }
143     }
144     return nullptr;
145   }
146 
147   bool bAligned =
148       pPattern->bbox().left == 0 && pPattern->bbox().bottom == 0 &&
149       pPattern->bbox().right == pPattern->x_step() &&
150       pPattern->bbox().top == pPattern->y_step() &&
151       (mtPattern2Device.IsScaled() || mtPattern2Device.Is90Rotated());
152   if (bAligned) {
153     int orig_x = FXSYS_roundf(mtPattern2Device.e);
154     int orig_y = FXSYS_roundf(mtPattern2Device.f);
155     min_col = (clip_box.left - orig_x) / width;
156     if (clip_box.left < orig_x)
157       min_col--;
158 
159     max_col = (clip_box.right - orig_x) / width;
160     if (clip_box.right <= orig_x)
161       max_col--;
162 
163     min_row = (clip_box.top - orig_y) / height;
164     if (clip_box.top < orig_y)
165       min_row--;
166 
167     max_row = (clip_box.bottom - orig_y) / height;
168     if (clip_box.bottom <= orig_y)
169       max_row--;
170   }
171   float left_offset = cell_bbox.left - mtPattern2Device.e;
172   float top_offset = cell_bbox.bottom - mtPattern2Device.f;
173   RetainPtr<CFX_DIBitmap> pPatternBitmap;
174   if (width * height < 16) {
175     RetainPtr<CFX_DIBitmap> pEnlargedBitmap = DrawPatternBitmap(
176         pContext->GetDocument(), pContext->GetPageCache(), pPattern,
177         pPatternForm, mtObj2Device, 8, 8, options.GetOptions());
178     pPatternBitmap = pEnlargedBitmap->StretchTo(
179         width, height, FXDIB_ResampleOptions(), nullptr);
180   } else {
181     pPatternBitmap = DrawPatternBitmap(
182         pContext->GetDocument(), pContext->GetPageCache(), pPattern,
183         pPatternForm, mtObj2Device, width, height, options.GetOptions());
184   }
185   if (!pPatternBitmap)
186     return nullptr;
187 
188   if (options.ColorModeIs(CPDF_RenderOptions::kGray))
189     pPatternBitmap->ConvertColorScale(0, 0xffffff);
190 
191   FX_ARGB fill_argb = pRenderStatus->GetFillArgb(pPageObj);
192   int clip_width = clip_box.right - clip_box.left;
193   int clip_height = clip_box.bottom - clip_box.top;
194   auto pScreen = pdfium::MakeRetain<CFX_DIBitmap>();
195   if (!pScreen->Create(clip_width, clip_height, FXDIB_Format::kArgb))
196     return nullptr;
197 
198   pdfium::span<const uint8_t> src_buf = pPatternBitmap->GetBuffer();
199   for (int col = min_col; col <= max_col; col++) {
200     for (int row = min_row; row <= max_row; row++) {
201       int start_x;
202       int start_y;
203       if (bAligned) {
204         start_x =
205             FXSYS_roundf(mtPattern2Device.e) + col * width - clip_box.left;
206         start_y =
207             FXSYS_roundf(mtPattern2Device.f) + row * height - clip_box.top;
208       } else {
209         CFX_PointF original = mtPattern2Device.Transform(
210             CFX_PointF(col * pPattern->x_step(), row * pPattern->y_step()));
211 
212         FX_SAFE_INT32 safeStartX = FXSYS_roundf(original.x + left_offset);
213         FX_SAFE_INT32 safeStartY = FXSYS_roundf(original.y + top_offset);
214 
215         safeStartX -= clip_box.left;
216         safeStartY -= clip_box.top;
217         if (!safeStartX.IsValid() || !safeStartY.IsValid())
218           return nullptr;
219 
220         start_x = safeStartX.ValueOrDie();
221         start_y = safeStartY.ValueOrDie();
222       }
223       if (width == 1 && height == 1) {
224         if (start_x < 0 || start_x >= clip_box.Width() || start_y < 0 ||
225             start_y >= clip_box.Height()) {
226           continue;
227         }
228         uint32_t* dest_buf = reinterpret_cast<uint32_t*>(
229             pScreen->GetWritableScanline(start_y).subspan(start_x * 4).data());
230         if (pPattern->colored()) {
231           const auto* src_buf32 =
232               reinterpret_cast<const uint32_t*>(src_buf.data());
233           *dest_buf = *src_buf32;
234         } else {
235           *dest_buf = (*(src_buf.data()) << 24) | (fill_argb & 0xffffff);
236         }
237       } else {
238         if (pPattern->colored()) {
239           pScreen->CompositeBitmap(start_x, start_y, width, height,
240                                    pPatternBitmap, 0, 0, BlendMode::kNormal,
241                                    nullptr, false);
242         } else {
243           pScreen->CompositeMask(start_x, start_y, width, height,
244                                  pPatternBitmap, fill_argb, 0, 0,
245                                  BlendMode::kNormal, nullptr, false);
246         }
247       }
248     }
249   }
250   return pScreen;
251 }
252