xref: /aosp_15_r20/external/pdfium/xfa/fgas/graphics/cfgas_gegraphics.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
1 // Copyright 2014 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 "xfa/fgas/graphics/cfgas_gegraphics.h"
8 
9 #include <math.h>
10 
11 #include <iterator>
12 #include <memory>
13 #include <utility>
14 
15 #include "core/fxcrt/fx_system.h"
16 #include "core/fxcrt/span_util.h"
17 #include "core/fxge/cfx_defaultrenderdevice.h"
18 #include "core/fxge/cfx_renderdevice.h"
19 #include "core/fxge/cfx_unicodeencoding.h"
20 #include "core/fxge/dib/cfx_dibitmap.h"
21 #include "third_party/base/check.h"
22 #include "xfa/fgas/graphics/cfgas_gecolor.h"
23 #include "xfa/fgas/graphics/cfgas_gepath.h"
24 #include "xfa/fgas/graphics/cfgas_gepattern.h"
25 #include "xfa/fgas/graphics/cfgas_geshading.h"
26 
27 namespace {
28 
29 struct FX_HATCHDATA {
30   int32_t width;
31   int32_t height;
32   uint8_t maskBits[64];
33 };
34 
35 const FX_HATCHDATA kHatchBitmapData[] = {
36     {16,  // Horizontal
37      16,
38      {
39          0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
40          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
41          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
42          0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
43          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
44          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
45      }},
46     {16,  // Vertical
47      16,
48      {
49          0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00,
50          0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80,
51          0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80,
52          0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
53          0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00,
54          0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
55      }},
56     {16,  // ForwardDiagonal
57      16,
58      {
59          0x80, 0x80, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x20, 0x20, 0x00,
60          0x00, 0x10, 0x10, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x04, 0x04,
61          0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x80,
62          0x80, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00,
63          0x10, 0x10, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x04, 0x04, 0x00,
64          0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
65      }},
66     {16,  // BackwardDiagonal
67      16,
68      {
69          0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x04, 0x04, 0x00,
70          0x00, 0x08, 0x08, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x20, 0x20,
71          0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x01,
72          0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00,
73          0x08, 0x08, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x20, 0x20, 0x00,
74          0x00, 0x40, 0x40, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
75      }},
76     {16,  // Cross
77      16,
78      {
79          0xff, 0xff, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00,
80          0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80,
81          0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0xff,
82          0xff, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
83          0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00,
84          0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
85      }},
86     {16,  // DiagonalCross
87      16,
88      {
89          0x81, 0x81, 0x00, 0x00, 0x42, 0x42, 0x00, 0x00, 0x24, 0x24, 0x00,
90          0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x24, 0x24,
91          0x00, 0x00, 0x42, 0x42, 0x00, 0x00, 0x81, 0x81, 0x00, 0x00, 0x81,
92          0x81, 0x00, 0x00, 0x42, 0x42, 0x00, 0x00, 0x24, 0x24, 0x00, 0x00,
93          0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x24, 0x24, 0x00,
94          0x00, 0x42, 0x42, 0x00, 0x00, 0x81, 0x81, 0x00, 0x00,
95      }},
96 };
97 
98 const FX_HATCHDATA kHatchPlaceHolder = {
99     0,
100     0,
101     {
102         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
103         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
104         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
105         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
106         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
107         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
108     }};
109 
GetHatchBitmapData(size_t index)110 const FX_HATCHDATA& GetHatchBitmapData(size_t index) {
111   return index < std::size(kHatchBitmapData) ? kHatchBitmapData[index]
112                                              : kHatchPlaceHolder;
113 }
114 
115 }  // namespace
116 
CFGAS_GEGraphics(CFX_RenderDevice * renderDevice)117 CFGAS_GEGraphics::CFGAS_GEGraphics(CFX_RenderDevice* renderDevice)
118     : m_renderDevice(renderDevice) {
119   DCHECK(m_renderDevice);
120 }
121 
122 CFGAS_GEGraphics::~CFGAS_GEGraphics() = default;
123 
SaveGraphState()124 void CFGAS_GEGraphics::SaveGraphState() {
125   m_renderDevice->SaveState();
126   m_infoStack.push_back(std::make_unique<TInfo>(m_info));
127 }
128 
RestoreGraphState()129 void CFGAS_GEGraphics::RestoreGraphState() {
130   m_renderDevice->RestoreState(false);
131   CHECK(!m_infoStack.empty());
132   m_info = *m_infoStack.back();
133   m_infoStack.pop_back();
134   return;
135 }
136 
SetLineCap(CFX_GraphStateData::LineCap lineCap)137 void CFGAS_GEGraphics::SetLineCap(CFX_GraphStateData::LineCap lineCap) {
138   m_info.graphState.m_LineCap = lineCap;
139 }
140 
SetLineDash(float dashPhase,pdfium::span<const float> dashArray)141 void CFGAS_GEGraphics::SetLineDash(float dashPhase,
142                                    pdfium::span<const float> dashArray) {
143   DCHECK(!dashArray.empty());
144   float scale = m_info.isActOnDash ? m_info.graphState.m_LineWidth : 1.0;
145   m_info.graphState.m_DashPhase = dashPhase;
146   m_info.graphState.m_DashArray.resize(dashArray.size());
147   for (size_t i = 0; i < dashArray.size(); ++i)
148     m_info.graphState.m_DashArray[i] = dashArray[i] * scale;
149 }
150 
SetSolidLineDash()151 void CFGAS_GEGraphics::SetSolidLineDash() {
152   m_info.graphState.m_DashArray.clear();
153 }
154 
SetLineWidth(float lineWidth)155 void CFGAS_GEGraphics::SetLineWidth(float lineWidth) {
156   m_info.graphState.m_LineWidth = lineWidth;
157 }
158 
EnableActOnDash()159 void CFGAS_GEGraphics::EnableActOnDash() {
160   m_info.isActOnDash = true;
161 }
162 
SetStrokeColor(const CFGAS_GEColor & color)163 void CFGAS_GEGraphics::SetStrokeColor(const CFGAS_GEColor& color) {
164   m_info.strokeColor = color;
165 }
166 
SetFillColor(const CFGAS_GEColor & color)167 void CFGAS_GEGraphics::SetFillColor(const CFGAS_GEColor& color) {
168   m_info.fillColor = color;
169 }
170 
StrokePath(const CFGAS_GEPath & path,const CFX_Matrix & matrix)171 void CFGAS_GEGraphics::StrokePath(const CFGAS_GEPath& path,
172                                   const CFX_Matrix& matrix) {
173   RenderDeviceStrokePath(path, matrix);
174 }
175 
FillPath(const CFGAS_GEPath & path,CFX_FillRenderOptions::FillType fill_type,const CFX_Matrix & matrix)176 void CFGAS_GEGraphics::FillPath(const CFGAS_GEPath& path,
177                                 CFX_FillRenderOptions::FillType fill_type,
178                                 const CFX_Matrix& matrix) {
179   RenderDeviceFillPath(path, fill_type, matrix);
180 }
181 
ConcatMatrix(const CFX_Matrix & matrix)182 void CFGAS_GEGraphics::ConcatMatrix(const CFX_Matrix& matrix) {
183   m_info.CTM.Concat(matrix);
184 }
185 
GetMatrix() const186 const CFX_Matrix* CFGAS_GEGraphics::GetMatrix() const {
187   return &m_info.CTM;
188 }
189 
GetClipRect() const190 CFX_RectF CFGAS_GEGraphics::GetClipRect() const {
191   FX_RECT r = m_renderDevice->GetClipBox();
192   return CFX_RectF(r.left, r.top, r.Width(), r.Height());
193 }
194 
SetClipRect(const CFX_RectF & rect)195 void CFGAS_GEGraphics::SetClipRect(const CFX_RectF& rect) {
196   m_renderDevice->SetClip_Rect(
197       FX_RECT(FXSYS_roundf(rect.left), FXSYS_roundf(rect.top),
198               FXSYS_roundf(rect.right()), FXSYS_roundf(rect.bottom())));
199 }
200 
GetRenderDevice()201 CFX_RenderDevice* CFGAS_GEGraphics::GetRenderDevice() {
202   return m_renderDevice;
203 }
204 
RenderDeviceStrokePath(const CFGAS_GEPath & path,const CFX_Matrix & matrix)205 void CFGAS_GEGraphics::RenderDeviceStrokePath(const CFGAS_GEPath& path,
206                                               const CFX_Matrix& matrix) {
207   if (m_info.strokeColor.GetType() != CFGAS_GEColor::Solid)
208     return;
209 
210   CFX_Matrix m = m_info.CTM;
211   m.Concat(matrix);
212   m_renderDevice->DrawPath(path.GetPath(), &m, &m_info.graphState, 0x0,
213                            m_info.strokeColor.GetArgb(),
214                            CFX_FillRenderOptions());
215 }
216 
RenderDeviceFillPath(const CFGAS_GEPath & path,CFX_FillRenderOptions::FillType fill_type,const CFX_Matrix & matrix)217 void CFGAS_GEGraphics::RenderDeviceFillPath(
218     const CFGAS_GEPath& path,
219     CFX_FillRenderOptions::FillType fill_type,
220     const CFX_Matrix& matrix) {
221   CFX_Matrix m = m_info.CTM;
222   m.Concat(matrix);
223 
224   const CFX_FillRenderOptions fill_options(fill_type);
225   switch (m_info.fillColor.GetType()) {
226     case CFGAS_GEColor::Solid:
227       m_renderDevice->DrawPath(path.GetPath(), &m, &m_info.graphState,
228                                m_info.fillColor.GetArgb(), 0x0, fill_options);
229       return;
230     case CFGAS_GEColor::Pattern:
231       FillPathWithPattern(path, fill_options, m);
232       return;
233     case CFGAS_GEColor::Shading:
234       FillPathWithShading(path, fill_options, m);
235       return;
236     default:
237       return;
238   }
239 }
240 
FillPathWithPattern(const CFGAS_GEPath & path,const CFX_FillRenderOptions & fill_options,const CFX_Matrix & matrix)241 void CFGAS_GEGraphics::FillPathWithPattern(
242     const CFGAS_GEPath& path,
243     const CFX_FillRenderOptions& fill_options,
244     const CFX_Matrix& matrix) {
245   RetainPtr<CFX_DIBitmap> bitmap = m_renderDevice->GetBitmap();
246   int32_t width = bitmap->GetWidth();
247   int32_t height = bitmap->GetHeight();
248   auto bmp = pdfium::MakeRetain<CFX_DIBitmap>();
249   bmp->Create(width, height, FXDIB_Format::kArgb);
250   m_renderDevice->GetDIBits(bmp, 0, 0);
251 
252   CFGAS_GEPattern::HatchStyle hatchStyle =
253       m_info.fillColor.GetPattern()->GetHatchStyle();
254   const FX_HATCHDATA& data =
255       GetHatchBitmapData(static_cast<size_t>(hatchStyle));
256 
257   auto mask = pdfium::MakeRetain<CFX_DIBitmap>();
258   mask->Create(data.width, data.height, FXDIB_Format::k1bppMask);
259   fxcrt::spancpy(
260       mask->GetWritableBuffer(),
261       pdfium::make_span(data.maskBits).first(mask->GetPitch() * data.height));
262   const CFX_FloatRect rectf =
263       matrix.TransformRect(path.GetPath().GetBoundingBox());
264   const FX_RECT rect = rectf.ToRoundedFxRect();
265 
266   CFX_DefaultRenderDevice device;
267   device.Attach(bmp);
268   device.FillRect(rect, m_info.fillColor.GetPattern()->GetBackArgb());
269   for (int32_t j = rect.bottom; j < rect.top; j += mask->GetHeight()) {
270     for (int32_t i = rect.left; i < rect.right; i += mask->GetWidth()) {
271       device.SetBitMask(mask, i, j,
272                         m_info.fillColor.GetPattern()->GetForeArgb());
273     }
274   }
275   CFX_RenderDevice::StateRestorer restorer(m_renderDevice);
276   m_renderDevice->SetClip_PathFill(path.GetPath(), &matrix, fill_options);
277   SetDIBitsWithMatrix(std::move(bmp), CFX_Matrix());
278 }
279 
FillPathWithShading(const CFGAS_GEPath & path,const CFX_FillRenderOptions & fill_options,const CFX_Matrix & matrix)280 void CFGAS_GEGraphics::FillPathWithShading(
281     const CFGAS_GEPath& path,
282     const CFX_FillRenderOptions& fill_options,
283     const CFX_Matrix& matrix) {
284   RetainPtr<CFX_DIBitmap> bitmap = m_renderDevice->GetBitmap();
285   int32_t width = bitmap->GetWidth();
286   int32_t height = bitmap->GetHeight();
287   float start_x = m_info.fillColor.GetShading()->GetBeginPoint().x;
288   float start_y = m_info.fillColor.GetShading()->GetBeginPoint().y;
289   float end_x = m_info.fillColor.GetShading()->GetEndPoint().x;
290   float end_y = m_info.fillColor.GetShading()->GetEndPoint().y;
291   auto bmp = pdfium::MakeRetain<CFX_DIBitmap>();
292   bmp->Create(width, height, FXDIB_Format::kArgb);
293   m_renderDevice->GetDIBits(bmp, 0, 0);
294   bool result = false;
295   switch (m_info.fillColor.GetShading()->GetType()) {
296     case CFGAS_GEShading::Type::kAxial: {
297       float x_span = end_x - start_x;
298       float y_span = end_y - start_y;
299       float axis_len_square = (x_span * x_span) + (y_span * y_span);
300       for (int32_t row = 0; row < height; row++) {
301         uint32_t* dib_buf =
302             reinterpret_cast<uint32_t*>(bmp->GetWritableScanline(row).data());
303         for (int32_t column = 0; column < width; column++) {
304           float scale = 0.0f;
305           if (axis_len_square) {
306             float y = static_cast<float>(row);
307             float x = static_cast<float>(column);
308             scale = (((x - start_x) * x_span) + ((y - start_y) * y_span)) /
309                     axis_len_square;
310             if (isnan(scale) || scale < 0.0f) {
311               if (!m_info.fillColor.GetShading()->IsExtendedBegin())
312                 continue;
313               scale = 0.0f;
314             } else if (scale > 1.0f) {
315               if (!m_info.fillColor.GetShading()->IsExtendedEnd())
316                 continue;
317               scale = 1.0f;
318             }
319           }
320           int32_t index =
321               static_cast<int32_t>(scale * (CFGAS_GEShading::kSteps - 1));
322           dib_buf[column] = m_info.fillColor.GetShading()->GetArgb(index);
323         }
324       }
325       result = true;
326       break;
327     }
328     case CFGAS_GEShading::Type::kRadial: {
329       float start_r = m_info.fillColor.GetShading()->GetBeginRadius();
330       float end_r = m_info.fillColor.GetShading()->GetEndRadius();
331       float a = ((start_x - end_x) * (start_x - end_x)) +
332                 ((start_y - end_y) * (start_y - end_y)) -
333                 ((start_r - end_r) * (start_r - end_r));
334       for (int32_t row = 0; row < height; row++) {
335         uint32_t* dib_buf =
336             reinterpret_cast<uint32_t*>(bmp->GetWritableScanline(row).data());
337         for (int32_t column = 0; column < width; column++) {
338           float x = (float)(column);
339           float y = (float)(row);
340           float b = -2 * (((x - start_x) * (end_x - start_x)) +
341                           ((y - start_y) * (end_y - start_y)) +
342                           (start_r * (end_r - start_r)));
343           float c = ((x - start_x) * (x - start_x)) +
344                     ((y - start_y) * (y - start_y)) - (start_r * start_r);
345           float s;
346           if (a == 0) {
347             s = -c / b;
348           } else {
349             float b2_4ac = (b * b) - 4 * (a * c);
350             if (b2_4ac < 0) {
351               continue;
352             }
353             float root = (sqrt(b2_4ac));
354             float s1;
355             float s2;
356             if (a > 0) {
357               s1 = (-b - root) / (2 * a);
358               s2 = (-b + root) / (2 * a);
359             } else {
360               s2 = (-b - root) / (2 * a);
361               s1 = (-b + root) / (2 * a);
362             }
363             if (s2 <= 1.0f || m_info.fillColor.GetShading()->IsExtendedEnd()) {
364               s = (s2);
365             } else {
366               s = (s1);
367             }
368             if ((start_r) + s * (end_r - start_r) < 0) {
369               continue;
370             }
371           }
372           if (isnan(s) || s < 0.0f) {
373             if (!m_info.fillColor.GetShading()->IsExtendedBegin())
374               continue;
375             s = 0.0f;
376           }
377           if (s > 1.0f) {
378             if (!m_info.fillColor.GetShading()->IsExtendedEnd())
379               continue;
380             s = 1.0f;
381           }
382           int index = static_cast<int32_t>(s * (CFGAS_GEShading::kSteps - 1));
383           dib_buf[column] = m_info.fillColor.GetShading()->GetArgb(index);
384         }
385       }
386       result = true;
387       break;
388     }
389   }
390   if (result) {
391     CFX_RenderDevice::StateRestorer restorer(m_renderDevice);
392     m_renderDevice->SetClip_PathFill(path.GetPath(), &matrix, fill_options);
393     SetDIBitsWithMatrix(std::move(bmp), matrix);
394   }
395 }
396 
SetDIBitsWithMatrix(RetainPtr<CFX_DIBBase> source,const CFX_Matrix & matrix)397 void CFGAS_GEGraphics::SetDIBitsWithMatrix(RetainPtr<CFX_DIBBase> source,
398                                            const CFX_Matrix& matrix) {
399   if (matrix.IsIdentity()) {
400     m_renderDevice->SetDIBits(source, 0, 0);
401   } else {
402     CFX_Matrix m((float)source->GetWidth(), 0, 0, (float)source->GetHeight(), 0,
403                  0);
404     m.Concat(matrix);
405     int32_t left;
406     int32_t top;
407     RetainPtr<CFX_DIBitmap> bmp1 = source->FlipImage(false, true);
408     RetainPtr<CFX_DIBitmap> bmp2 = bmp1->TransformTo(m, &left, &top);
409     m_renderDevice->SetDIBits(bmp2, left, top);
410   }
411 }
412 
413 CFGAS_GEGraphics::TInfo::TInfo() = default;
414 
TInfo(const TInfo & info)415 CFGAS_GEGraphics::TInfo::TInfo(const TInfo& info)
416     : graphState(info.graphState),
417       CTM(info.CTM),
418       isActOnDash(info.isActOnDash),
419       strokeColor(info.strokeColor),
420       fillColor(info.fillColor) {}
421 
operator =(const TInfo & other)422 CFGAS_GEGraphics::TInfo& CFGAS_GEGraphics::TInfo::operator=(
423     const TInfo& other) {
424   graphState = other.graphState;
425   CTM = other.CTM;
426   isActOnDash = other.isActOnDash;
427   strokeColor = other.strokeColor;
428   fillColor = other.fillColor;
429   return *this;
430 }
431 
StateRestorer(CFGAS_GEGraphics * graphics)432 CFGAS_GEGraphics::StateRestorer::StateRestorer(CFGAS_GEGraphics* graphics)
433     : graphics_(graphics) {
434   graphics_->SaveGraphState();
435 }
436 
~StateRestorer()437 CFGAS_GEGraphics::StateRestorer::~StateRestorer() {
438   graphics_->RestoreGraphState();
439 }
440