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