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