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