• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <memory>
10 
11 #include "core/fxge/cfx_defaultrenderdevice.h"
12 #include "core/fxge/cfx_renderdevice.h"
13 #include "core/fxge/cfx_unicodeencoding.h"
14 #include "core/fxge/dib/cfx_dibitmap.h"
15 #include "third_party/base/ptr_util.h"
16 #include "xfa/fxgraphics/cxfa_gecolor.h"
17 #include "xfa/fxgraphics/cxfa_gepath.h"
18 #include "xfa/fxgraphics/cxfa_gepattern.h"
19 #include "xfa/fxgraphics/cxfa_geshading.h"
20 
21 namespace {
22 
23 enum {
24   FX_CONTEXT_None = 0,
25   FX_CONTEXT_Device,
26 };
27 
28 #define FX_HATCHSTYLE_Total 53
29 
30 struct FX_HATCHDATA {
31   int32_t width;
32   int32_t height;
33   uint8_t maskBits[64];
34 };
35 
36 const FX_HATCHDATA hatchBitmapData[FX_HATCHSTYLE_Total] = {
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 }  // namespace
100 
CXFA_Graphics(CFX_RenderDevice * renderDevice)101 CXFA_Graphics::CXFA_Graphics(CFX_RenderDevice* renderDevice)
102     : m_type(FX_CONTEXT_None), m_renderDevice(renderDevice) {
103   if (!renderDevice)
104     return;
105   m_type = FX_CONTEXT_Device;
106 }
107 
~CXFA_Graphics()108 CXFA_Graphics::~CXFA_Graphics() {}
109 
SaveGraphState()110 void CXFA_Graphics::SaveGraphState() {
111   if (m_type != FX_CONTEXT_Device || !m_renderDevice)
112     return;
113 
114   m_renderDevice->SaveState();
115   m_infoStack.push_back(pdfium::MakeUnique<TInfo>(m_info));
116 }
117 
RestoreGraphState()118 void CXFA_Graphics::RestoreGraphState() {
119   if (m_type != FX_CONTEXT_Device || !m_renderDevice)
120     return;
121 
122   m_renderDevice->RestoreState(false);
123   if (m_infoStack.empty() || !m_infoStack.back())
124     return;
125 
126   m_info = *m_infoStack.back();
127   m_infoStack.pop_back();
128   return;
129 }
130 
SetLineCap(CFX_GraphStateData::LineCap lineCap)131 void CXFA_Graphics::SetLineCap(CFX_GraphStateData::LineCap lineCap) {
132   if (m_type == FX_CONTEXT_Device && m_renderDevice) {
133     m_info.graphState.m_LineCap = lineCap;
134   }
135 }
136 
SetLineDash(float dashPhase,float * dashArray,int32_t dashCount)137 void CXFA_Graphics::SetLineDash(float dashPhase,
138                                 float* dashArray,
139                                 int32_t dashCount) {
140   if (dashCount > 0 && !dashArray)
141     return;
142 
143   dashCount = dashCount < 0 ? 0 : dashCount;
144   if (m_type == FX_CONTEXT_Device && m_renderDevice) {
145     float scale = 1.0;
146     if (m_info.isActOnDash) {
147       scale = m_info.graphState.m_LineWidth;
148     }
149     m_info.graphState.m_DashPhase = dashPhase;
150     m_info.graphState.SetDashCount(dashCount);
151     for (int32_t i = 0; i < dashCount; i++) {
152       m_info.graphState.m_DashArray[i] = dashArray[i] * scale;
153     }
154   }
155 }
156 
SetSolidLineDash()157 void CXFA_Graphics::SetSolidLineDash() {
158   if (m_type == FX_CONTEXT_Device && m_renderDevice)
159     m_info.graphState.SetDashCount(0);
160 }
161 
SetLineWidth(float lineWidth)162 void CXFA_Graphics::SetLineWidth(float lineWidth) {
163   if (m_type == FX_CONTEXT_Device && m_renderDevice)
164     m_info.graphState.m_LineWidth = lineWidth;
165 }
166 
EnableActOnDash()167 void CXFA_Graphics::EnableActOnDash() {
168   if (m_type == FX_CONTEXT_Device && m_renderDevice)
169     m_info.isActOnDash = true;
170 }
171 
SetStrokeColor(const CXFA_GEColor & color)172 void CXFA_Graphics::SetStrokeColor(const CXFA_GEColor& color) {
173   if (m_type == FX_CONTEXT_Device && m_renderDevice)
174     m_info.strokeColor = color;
175 }
176 
SetFillColor(const CXFA_GEColor & color)177 void CXFA_Graphics::SetFillColor(const CXFA_GEColor& color) {
178   if (m_type == FX_CONTEXT_Device && m_renderDevice)
179     m_info.fillColor = color;
180 }
181 
StrokePath(CXFA_GEPath * path,const CFX_Matrix * matrix)182 void CXFA_Graphics::StrokePath(CXFA_GEPath* path, const CFX_Matrix* matrix) {
183   if (!path)
184     return;
185   if (m_type == FX_CONTEXT_Device && m_renderDevice)
186     RenderDeviceStrokePath(path, matrix);
187 }
188 
FillPath(CXFA_GEPath * path,FX_FillMode fillMode,const CFX_Matrix * matrix)189 void CXFA_Graphics::FillPath(CXFA_GEPath* path,
190                              FX_FillMode fillMode,
191                              const CFX_Matrix* matrix) {
192   if (!path)
193     return;
194   if (m_type == FX_CONTEXT_Device && m_renderDevice)
195     RenderDeviceFillPath(path, fillMode, matrix);
196 }
197 
ConcatMatrix(const CFX_Matrix * matrix)198 void CXFA_Graphics::ConcatMatrix(const CFX_Matrix* matrix) {
199   if (!matrix)
200     return;
201   if (m_type == FX_CONTEXT_Device && m_renderDevice) {
202     m_info.CTM.Concat(*matrix);
203   }
204 }
205 
GetMatrix() const206 const CFX_Matrix* CXFA_Graphics::GetMatrix() const {
207   if (m_type == FX_CONTEXT_Device && m_renderDevice)
208     return &m_info.CTM;
209   return nullptr;
210 }
211 
GetClipRect() const212 CFX_RectF CXFA_Graphics::GetClipRect() const {
213   if (m_type != FX_CONTEXT_Device || !m_renderDevice)
214     return CFX_RectF();
215 
216   FX_RECT r = m_renderDevice->GetClipBox();
217   return CFX_Rect(r.left, r.top, r.Width(), r.Height()).As<float>();
218 }
219 
SetClipRect(const CFX_RectF & rect)220 void CXFA_Graphics::SetClipRect(const CFX_RectF& rect) {
221   if (m_type == FX_CONTEXT_Device && m_renderDevice) {
222     m_renderDevice->SetClip_Rect(
223         FX_RECT(FXSYS_round(rect.left), FXSYS_round(rect.top),
224                 FXSYS_round(rect.right()), FXSYS_round(rect.bottom())));
225   }
226 }
227 
GetRenderDevice()228 CFX_RenderDevice* CXFA_Graphics::GetRenderDevice() {
229   return m_renderDevice;
230 }
231 
RenderDeviceStrokePath(const CXFA_GEPath * path,const CFX_Matrix * matrix)232 void CXFA_Graphics::RenderDeviceStrokePath(const CXFA_GEPath* path,
233                                            const CFX_Matrix* matrix) {
234   if (m_info.strokeColor.GetType() != CXFA_GEColor::Solid)
235     return;
236 
237   CFX_Matrix m = m_info.CTM;
238   if (matrix)
239     m.Concat(*matrix);
240 
241   m_renderDevice->DrawPath(path->GetPathData(), &m, &m_info.graphState, 0x0,
242                            m_info.strokeColor.GetArgb(), 0);
243 }
244 
RenderDeviceFillPath(const CXFA_GEPath * path,FX_FillMode fillMode,const CFX_Matrix * matrix)245 void CXFA_Graphics::RenderDeviceFillPath(const CXFA_GEPath* path,
246                                          FX_FillMode fillMode,
247                                          const CFX_Matrix* matrix) {
248   CFX_Matrix m = m_info.CTM;
249   if (matrix)
250     m.Concat(*matrix);
251 
252   switch (m_info.fillColor.GetType()) {
253     case CXFA_GEColor::Solid:
254       m_renderDevice->DrawPath(path->GetPathData(), &m, &m_info.graphState,
255                                m_info.fillColor.GetArgb(), 0x0, fillMode);
256       return;
257     case CXFA_GEColor::Pattern:
258       FillPathWithPattern(path, fillMode, m);
259       return;
260     case CXFA_GEColor::Shading:
261       FillPathWithShading(path, fillMode, m);
262       return;
263     default:
264       return;
265   }
266 }
267 
FillPathWithPattern(const CXFA_GEPath * path,FX_FillMode fillMode,const CFX_Matrix & matrix)268 void CXFA_Graphics::FillPathWithPattern(const CXFA_GEPath* path,
269                                         FX_FillMode fillMode,
270                                         const CFX_Matrix& matrix) {
271   CXFA_GEPattern* pattern = m_info.fillColor.GetPattern();
272   RetainPtr<CFX_DIBitmap> bitmap = m_renderDevice->GetBitmap();
273   int32_t width = bitmap->GetWidth();
274   int32_t height = bitmap->GetHeight();
275   auto bmp = pdfium::MakeRetain<CFX_DIBitmap>();
276   bmp->Create(width, height, FXDIB_Argb);
277   m_renderDevice->GetDIBits(bmp, 0, 0);
278 
279   FX_HatchStyle hatchStyle = m_info.fillColor.GetPattern()->m_hatchStyle;
280   const FX_HATCHDATA& data = hatchBitmapData[static_cast<int>(hatchStyle)];
281 
282   auto mask = pdfium::MakeRetain<CFX_DIBitmap>();
283   mask->Create(data.width, data.height, FXDIB_1bppMask);
284   memcpy(mask->GetBuffer(), data.maskBits, mask->GetPitch() * data.height);
285   CFX_FloatRect rectf =
286       matrix.TransformRect(path->GetPathData()->GetBoundingBox());
287 
288   FX_RECT rect(FXSYS_round(rectf.left), FXSYS_round(rectf.top),
289                FXSYS_round(rectf.right), FXSYS_round(rectf.bottom));
290   CFX_DefaultRenderDevice device;
291   device.Attach(bmp, false, nullptr, false);
292   device.FillRect(&rect, m_info.fillColor.GetPattern()->m_backArgb);
293   for (int32_t j = rect.bottom; j < rect.top; j += mask->GetHeight()) {
294     for (int32_t i = rect.left; i < rect.right; i += mask->GetWidth())
295       device.SetBitMask(mask, i, j, m_info.fillColor.GetPattern()->m_foreArgb);
296   }
297   CFX_RenderDevice::StateRestorer restorer(m_renderDevice);
298   m_renderDevice->SetClip_PathFill(path->GetPathData(), &matrix, fillMode);
299   SetDIBitsWithMatrix(bmp, pattern->m_matrix);
300 }
301 
FillPathWithShading(const CXFA_GEPath * path,FX_FillMode fillMode,const CFX_Matrix & matrix)302 void CXFA_Graphics::FillPathWithShading(const CXFA_GEPath* path,
303                                         FX_FillMode fillMode,
304                                         const CFX_Matrix& matrix) {
305   RetainPtr<CFX_DIBitmap> bitmap = m_renderDevice->GetBitmap();
306   int32_t width = bitmap->GetWidth();
307   int32_t height = bitmap->GetHeight();
308   float start_x = m_info.fillColor.GetShading()->m_beginPoint.x;
309   float start_y = m_info.fillColor.GetShading()->m_beginPoint.y;
310   float end_x = m_info.fillColor.GetShading()->m_endPoint.x;
311   float end_y = m_info.fillColor.GetShading()->m_endPoint.y;
312   auto bmp = pdfium::MakeRetain<CFX_DIBitmap>();
313   bmp->Create(width, height, FXDIB_Argb);
314   m_renderDevice->GetDIBits(bmp, 0, 0);
315   int32_t pitch = bmp->GetPitch();
316   bool result = false;
317   switch (m_info.fillColor.GetShading()->m_type) {
318     case FX_SHADING_Axial: {
319       float x_span = end_x - start_x;
320       float y_span = end_y - start_y;
321       float axis_len_square = (x_span * x_span) + (y_span * y_span);
322       for (int32_t row = 0; row < height; row++) {
323         uint32_t* dib_buf = (uint32_t*)(bmp->GetBuffer() + row * pitch);
324         for (int32_t column = 0; column < width; column++) {
325           float x = (float)(column);
326           float y = (float)(row);
327           float scale = (((x - start_x) * x_span) + ((y - start_y) * y_span)) /
328                         axis_len_square;
329           if (scale < 0) {
330             if (!m_info.fillColor.GetShading()->m_isExtendedBegin) {
331               continue;
332             }
333             scale = 0;
334           } else if (scale > 1.0f) {
335             if (!m_info.fillColor.GetShading()->m_isExtendedEnd) {
336               continue;
337             }
338             scale = 1.0f;
339           }
340           int32_t index = (int32_t)(scale * (FX_SHADING_Steps - 1));
341           dib_buf[column] = m_info.fillColor.GetShading()->m_argbArray[index];
342         }
343       }
344       result = true;
345       break;
346     }
347     case FX_SHADING_Radial: {
348       float start_r = m_info.fillColor.GetShading()->m_beginRadius;
349       float end_r = m_info.fillColor.GetShading()->m_endRadius;
350       float a = ((start_x - end_x) * (start_x - end_x)) +
351                 ((start_y - end_y) * (start_y - end_y)) -
352                 ((start_r - end_r) * (start_r - end_r));
353       for (int32_t row = 0; row < height; row++) {
354         uint32_t* dib_buf = (uint32_t*)(bmp->GetBuffer() + row * pitch);
355         for (int32_t column = 0; column < width; column++) {
356           float x = (float)(column);
357           float y = (float)(row);
358           float b = -2 * (((x - start_x) * (end_x - start_x)) +
359                           ((y - start_y) * (end_y - start_y)) +
360                           (start_r * (end_r - start_r)));
361           float c = ((x - start_x) * (x - start_x)) +
362                     ((y - start_y) * (y - start_y)) - (start_r * start_r);
363           float s;
364           if (a == 0) {
365             s = -c / b;
366           } else {
367             float b2_4ac = (b * b) - 4 * (a * c);
368             if (b2_4ac < 0) {
369               continue;
370             }
371             float root = (sqrt(b2_4ac));
372             float s1, s2;
373             if (a > 0) {
374               s1 = (-b - root) / (2 * a);
375               s2 = (-b + root) / (2 * a);
376             } else {
377               s2 = (-b - root) / (2 * a);
378               s1 = (-b + root) / (2 * a);
379             }
380             if (s2 <= 1.0f || m_info.fillColor.GetShading()->m_isExtendedEnd) {
381               s = (s2);
382             } else {
383               s = (s1);
384             }
385             if ((start_r) + s * (end_r - start_r) < 0) {
386               continue;
387             }
388           }
389           if (s < 0) {
390             if (!m_info.fillColor.GetShading()->m_isExtendedBegin) {
391               continue;
392             }
393             s = 0;
394           }
395           if (s > 1.0f) {
396             if (!m_info.fillColor.GetShading()->m_isExtendedEnd) {
397               continue;
398             }
399             s = 1.0f;
400           }
401           int index = (int32_t)(s * (FX_SHADING_Steps - 1));
402           dib_buf[column] = m_info.fillColor.GetShading()->m_argbArray[index];
403         }
404       }
405       result = true;
406       break;
407     }
408     default: {
409       result = false;
410       break;
411     }
412   }
413   if (result) {
414     CFX_RenderDevice::StateRestorer restorer(m_renderDevice);
415     m_renderDevice->SetClip_PathFill(path->GetPathData(), &matrix, fillMode);
416     SetDIBitsWithMatrix(bmp, matrix);
417   }
418 }
419 
SetDIBitsWithMatrix(const RetainPtr<CFX_DIBSource> & source,const CFX_Matrix & matrix)420 void CXFA_Graphics::SetDIBitsWithMatrix(const RetainPtr<CFX_DIBSource>& source,
421                                         const CFX_Matrix& matrix) {
422   if (matrix.IsIdentity()) {
423     m_renderDevice->SetDIBits(source, 0, 0);
424   } else {
425     CFX_Matrix m((float)source->GetWidth(), 0, 0, (float)source->GetHeight(), 0,
426                  0);
427     m.Concat(matrix);
428     int32_t left;
429     int32_t top;
430     RetainPtr<CFX_DIBitmap> bmp1 = source->FlipImage(false, true);
431     RetainPtr<CFX_DIBitmap> bmp2 = bmp1->TransformTo(&m, &left, &top);
432     m_renderDevice->SetDIBits(bmp2, left, top);
433   }
434 }
435 
TInfo()436 CXFA_Graphics::TInfo::TInfo()
437     : isActOnDash(false), strokeColor(nullptr), fillColor(nullptr) {}
438 
TInfo(const TInfo & info)439 CXFA_Graphics::TInfo::TInfo(const TInfo& info)
440     : graphState(info.graphState),
441       CTM(info.CTM),
442       isActOnDash(info.isActOnDash),
443       strokeColor(info.strokeColor),
444       fillColor(info.fillColor) {}
445 
operator =(const TInfo & other)446 CXFA_Graphics::TInfo& CXFA_Graphics::TInfo::operator=(const TInfo& other) {
447   graphState.Copy(other.graphState);
448   CTM = other.CTM;
449   isActOnDash = other.isActOnDash;
450   strokeColor = other.strokeColor;
451   fillColor = other.fillColor;
452   return *this;
453 }
454