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/cfx_graphics.h"
8
9#include <memory>
10
11#include "core/fxge/cfx_fxgedevice.h"
12#include "core/fxge/cfx_gemodule.h"
13#include "core/fxge/cfx_renderdevice.h"
14#include "core/fxge/cfx_unicodeencoding.h"
15#include "third_party/base/ptr_util.h"
16#include "xfa/fxgraphics/cfx_color.h"
17#include "xfa/fxgraphics/cfx_path.h"
18#include "xfa/fxgraphics/cfx_pattern.h"
19#include "xfa/fxgraphics/cfx_shading.h"
20
21namespace {
22
23enum {
24  FX_CONTEXT_None = 0,
25  FX_CONTEXT_Device,
26};
27
28#define FX_HATCHSTYLE_Total 53
29
30struct FX_HATCHDATA {
31  int32_t width;
32  int32_t height;
33  uint8_t maskBits[64];
34};
35
36const 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
101CFX_Graphics::CFX_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
108CFX_Graphics::~CFX_Graphics() {}
109
110void CFX_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
118void CFX_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
131void CFX_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
137void CFX_Graphics::SetLineDash(FX_FLOAT dashPhase,
138                               FX_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    FX_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
157void CFX_Graphics::SetLineDash(FX_DashStyle dashStyle) {
158  if (m_type == FX_CONTEXT_Device && m_renderDevice)
159    RenderDeviceSetLineDash(dashStyle);
160}
161
162void CFX_Graphics::SetLineWidth(FX_FLOAT lineWidth, bool isActOnDash) {
163  if (m_type == FX_CONTEXT_Device && m_renderDevice) {
164    m_info.graphState.m_LineWidth = lineWidth;
165    m_info.isActOnDash = isActOnDash;
166  }
167}
168
169void CFX_Graphics::SetStrokeColor(CFX_Color* color) {
170  if (!color)
171    return;
172  if (m_type == FX_CONTEXT_Device && m_renderDevice) {
173    m_info.strokeColor = color;
174  }
175}
176
177void CFX_Graphics::SetFillColor(CFX_Color* color) {
178  if (!color)
179    return;
180  if (m_type == FX_CONTEXT_Device && m_renderDevice) {
181    m_info.fillColor = color;
182  }
183}
184
185void CFX_Graphics::StrokePath(CFX_Path* path, CFX_Matrix* matrix) {
186  if (!path)
187    return;
188  if (m_type == FX_CONTEXT_Device && m_renderDevice)
189    RenderDeviceStrokePath(path, matrix);
190}
191
192void CFX_Graphics::FillPath(CFX_Path* path,
193                            FX_FillMode fillMode,
194                            CFX_Matrix* matrix) {
195  if (!path)
196    return;
197  if (m_type == FX_CONTEXT_Device && m_renderDevice)
198    RenderDeviceFillPath(path, fillMode, matrix);
199}
200
201void CFX_Graphics::StretchImage(CFX_DIBSource* source,
202                                const CFX_RectF& rect,
203                                CFX_Matrix* matrix) {
204  if (!source)
205    return;
206  if (m_type == FX_CONTEXT_Device && m_renderDevice)
207    RenderDeviceStretchImage(source, rect, matrix);
208}
209
210void CFX_Graphics::ConcatMatrix(const CFX_Matrix* matrix) {
211  if (!matrix)
212    return;
213  if (m_type == FX_CONTEXT_Device && m_renderDevice) {
214    m_info.CTM.Concat(*matrix);
215  }
216}
217
218CFX_Matrix* CFX_Graphics::GetMatrix() {
219  if (m_type == FX_CONTEXT_Device && m_renderDevice)
220    return &m_info.CTM;
221  return nullptr;
222}
223
224CFX_RectF CFX_Graphics::GetClipRect() const {
225  if (m_type != FX_CONTEXT_Device || !m_renderDevice)
226    return CFX_RectF();
227
228  FX_RECT r = m_renderDevice->GetClipBox();
229  return CFX_Rect(r.left, r.top, r.Width(), r.Height()).As<FX_FLOAT>();
230}
231
232void CFX_Graphics::SetClipRect(const CFX_RectF& rect) {
233  if (m_type == FX_CONTEXT_Device && m_renderDevice) {
234    m_renderDevice->SetClip_Rect(
235        FX_RECT(FXSYS_round(rect.left), FXSYS_round(rect.top),
236                FXSYS_round(rect.right()), FXSYS_round(rect.bottom())));
237  }
238}
239
240CFX_RenderDevice* CFX_Graphics::GetRenderDevice() {
241  return m_renderDevice;
242}
243
244void CFX_Graphics::RenderDeviceSetLineDash(FX_DashStyle dashStyle) {
245  switch (dashStyle) {
246    case FX_DASHSTYLE_Solid: {
247      m_info.graphState.SetDashCount(0);
248      return;
249    }
250    case FX_DASHSTYLE_Dash: {
251      FX_FLOAT dashArray[] = {3, 1};
252      SetLineDash(0, dashArray, 2);
253      return;
254    }
255    case FX_DASHSTYLE_Dot: {
256      FX_FLOAT dashArray[] = {1, 1};
257      SetLineDash(0, dashArray, 2);
258      return;
259    }
260    case FX_DASHSTYLE_DashDot: {
261      FX_FLOAT dashArray[] = {3, 1, 1, 1};
262      SetLineDash(0, dashArray, 4);
263      return;
264    }
265    case FX_DASHSTYLE_DashDotDot: {
266      FX_FLOAT dashArray[] = {4, 1, 2, 1, 2, 1};
267      SetLineDash(0, dashArray, 6);
268      return;
269    }
270    default:
271      return;
272  }
273}
274
275void CFX_Graphics::RenderDeviceStrokePath(CFX_Path* path, CFX_Matrix* matrix) {
276  if (!m_info.strokeColor)
277    return;
278  CFX_Matrix m(m_info.CTM.a, m_info.CTM.b, m_info.CTM.c, m_info.CTM.d,
279               m_info.CTM.e, m_info.CTM.f);
280  if (matrix) {
281    m.Concat(*matrix);
282  }
283  switch (m_info.strokeColor->m_type) {
284    case FX_COLOR_Solid: {
285      m_renderDevice->DrawPath(path->GetPathData(), &m, &m_info.graphState, 0x0,
286                               m_info.strokeColor->m_info.argb, 0);
287      return;
288    }
289    default:
290      return;
291  }
292}
293
294void CFX_Graphics::RenderDeviceFillPath(CFX_Path* path,
295                                        FX_FillMode fillMode,
296                                        CFX_Matrix* matrix) {
297  if (!m_info.fillColor)
298    return;
299  CFX_Matrix m(m_info.CTM.a, m_info.CTM.b, m_info.CTM.c, m_info.CTM.d,
300               m_info.CTM.e, m_info.CTM.f);
301  if (matrix) {
302    m.Concat(*matrix);
303  }
304  switch (m_info.fillColor->m_type) {
305    case FX_COLOR_Solid: {
306      m_renderDevice->DrawPath(path->GetPathData(), &m, &m_info.graphState,
307                               m_info.fillColor->m_info.argb, 0x0, fillMode);
308      return;
309    }
310    case FX_COLOR_Pattern:
311      FillPathWithPattern(path, fillMode, &m);
312      return;
313    case FX_COLOR_Shading:
314      FillPathWithShading(path, fillMode, &m);
315      return;
316    default:
317      return;
318  }
319}
320
321void CFX_Graphics::RenderDeviceStretchImage(CFX_DIBSource* source,
322                                            const CFX_RectF& rect,
323                                            CFX_Matrix* matrix) {
324  CFX_Matrix m1(m_info.CTM.a, m_info.CTM.b, m_info.CTM.c, m_info.CTM.d,
325                m_info.CTM.e, m_info.CTM.f);
326  if (matrix) {
327    m1.Concat(*matrix);
328  }
329  std::unique_ptr<CFX_DIBitmap> bmp1 =
330      source->StretchTo((int32_t)rect.Width(), (int32_t)rect.Height());
331  CFX_Matrix m2(rect.Width(), 0.0, 0.0, rect.Height(), rect.left, rect.top);
332  m2.Concat(m1);
333
334  int32_t left;
335  int32_t top;
336  std::unique_ptr<CFX_DIBitmap> bmp2 = bmp1->FlipImage(false, true);
337  std::unique_ptr<CFX_DIBitmap> bmp3 = bmp2->TransformTo(&m2, left, top);
338  CFX_RectF r = GetClipRect();
339  CFX_DIBitmap* bitmap = m_renderDevice->GetBitmap();
340  bitmap->CompositeBitmap(FXSYS_round(r.left), FXSYS_round(r.top),
341                          FXSYS_round(r.Width()), FXSYS_round(r.Height()),
342                          bmp3.get(), FXSYS_round(r.left - left),
343                          FXSYS_round(r.top - top));
344}
345
346void CFX_Graphics::FillPathWithPattern(CFX_Path* path,
347                                       FX_FillMode fillMode,
348                                       CFX_Matrix* matrix) {
349  CFX_Pattern* pattern = m_info.fillColor->m_info.pattern;
350  CFX_DIBitmap* bitmap = m_renderDevice->GetBitmap();
351  int32_t width = bitmap->GetWidth();
352  int32_t height = bitmap->GetHeight();
353  CFX_DIBitmap bmp;
354  bmp.Create(width, height, FXDIB_Argb);
355  m_renderDevice->GetDIBits(&bmp, 0, 0);
356
357  FX_HatchStyle hatchStyle = m_info.fillColor->m_info.pattern->m_hatchStyle;
358  const FX_HATCHDATA& data = hatchBitmapData[static_cast<int>(hatchStyle)];
359
360  CFX_DIBitmap mask;
361  mask.Create(data.width, data.height, FXDIB_1bppMask);
362  FXSYS_memcpy(mask.GetBuffer(), data.maskBits, mask.GetPitch() * data.height);
363  CFX_FloatRect rectf = path->GetPathData()->GetBoundingBox();
364  if (matrix)
365    matrix->TransformRect(rectf);
366
367  FX_RECT rect(FXSYS_round(rectf.left), FXSYS_round(rectf.top),
368               FXSYS_round(rectf.right), FXSYS_round(rectf.bottom));
369  CFX_FxgeDevice device;
370  device.Attach(&bmp, false, nullptr, false);
371  device.FillRect(&rect, m_info.fillColor->m_info.pattern->m_backArgb);
372  for (int32_t j = rect.bottom; j < rect.top; j += mask.GetHeight()) {
373    for (int32_t i = rect.left; i < rect.right; i += mask.GetWidth()) {
374      device.SetBitMask(&mask, i, j,
375                        m_info.fillColor->m_info.pattern->m_foreArgb);
376    }
377  }
378
379  m_renderDevice->SaveState();
380  m_renderDevice->SetClip_PathFill(path->GetPathData(), matrix, fillMode);
381  SetDIBitsWithMatrix(&bmp, &pattern->m_matrix);
382  m_renderDevice->RestoreState(false);
383}
384
385void CFX_Graphics::FillPathWithShading(CFX_Path* path,
386                                       FX_FillMode fillMode,
387                                       CFX_Matrix* matrix) {
388  CFX_DIBitmap* bitmap = m_renderDevice->GetBitmap();
389  int32_t width = bitmap->GetWidth();
390  int32_t height = bitmap->GetHeight();
391  FX_FLOAT start_x = m_info.fillColor->m_shading->m_beginPoint.x;
392  FX_FLOAT start_y = m_info.fillColor->m_shading->m_beginPoint.y;
393  FX_FLOAT end_x = m_info.fillColor->m_shading->m_endPoint.x;
394  FX_FLOAT end_y = m_info.fillColor->m_shading->m_endPoint.y;
395  CFX_DIBitmap bmp;
396  bmp.Create(width, height, FXDIB_Argb);
397  m_renderDevice->GetDIBits(&bmp, 0, 0);
398  int32_t pitch = bmp.GetPitch();
399  bool result = false;
400  switch (m_info.fillColor->m_shading->m_type) {
401    case FX_SHADING_Axial: {
402      FX_FLOAT x_span = end_x - start_x;
403      FX_FLOAT y_span = end_y - start_y;
404      FX_FLOAT axis_len_square = (x_span * x_span) + (y_span * y_span);
405      for (int32_t row = 0; row < height; row++) {
406        uint32_t* dib_buf = (uint32_t*)(bmp.GetBuffer() + row * pitch);
407        for (int32_t column = 0; column < width; column++) {
408          FX_FLOAT x = (FX_FLOAT)(column);
409          FX_FLOAT y = (FX_FLOAT)(row);
410          FX_FLOAT scale =
411              (((x - start_x) * x_span) + ((y - start_y) * y_span)) /
412              axis_len_square;
413          if (scale < 0) {
414            if (!m_info.fillColor->m_shading->m_isExtendedBegin) {
415              continue;
416            }
417            scale = 0;
418          } else if (scale > 1.0f) {
419            if (!m_info.fillColor->m_shading->m_isExtendedEnd) {
420              continue;
421            }
422            scale = 1.0f;
423          }
424          int32_t index = (int32_t)(scale * (FX_SHADING_Steps - 1));
425          dib_buf[column] = m_info.fillColor->m_shading->m_argbArray[index];
426        }
427      }
428      result = true;
429      break;
430    }
431    case FX_SHADING_Radial: {
432      FX_FLOAT start_r = m_info.fillColor->m_shading->m_beginRadius;
433      FX_FLOAT end_r = m_info.fillColor->m_shading->m_endRadius;
434      FX_FLOAT a = ((start_x - end_x) * (start_x - end_x)) +
435                   ((start_y - end_y) * (start_y - end_y)) -
436                   ((start_r - end_r) * (start_r - end_r));
437      for (int32_t row = 0; row < height; row++) {
438        uint32_t* dib_buf = (uint32_t*)(bmp.GetBuffer() + row * pitch);
439        for (int32_t column = 0; column < width; column++) {
440          FX_FLOAT x = (FX_FLOAT)(column);
441          FX_FLOAT y = (FX_FLOAT)(row);
442          FX_FLOAT b = -2 * (((x - start_x) * (end_x - start_x)) +
443                             ((y - start_y) * (end_y - start_y)) +
444                             (start_r * (end_r - start_r)));
445          FX_FLOAT c = ((x - start_x) * (x - start_x)) +
446                       ((y - start_y) * (y - start_y)) - (start_r * start_r);
447          FX_FLOAT s;
448          if (a == 0) {
449            s = -c / b;
450          } else {
451            FX_FLOAT b2_4ac = (b * b) - 4 * (a * c);
452            if (b2_4ac < 0) {
453              continue;
454            }
455            FX_FLOAT root = (FXSYS_sqrt(b2_4ac));
456            FX_FLOAT s1, s2;
457            if (a > 0) {
458              s1 = (-b - root) / (2 * a);
459              s2 = (-b + root) / (2 * a);
460            } else {
461              s2 = (-b - root) / (2 * a);
462              s1 = (-b + root) / (2 * a);
463            }
464            if (s2 <= 1.0f || m_info.fillColor->m_shading->m_isExtendedEnd) {
465              s = (s2);
466            } else {
467              s = (s1);
468            }
469            if ((start_r) + s * (end_r - start_r) < 0) {
470              continue;
471            }
472          }
473          if (s < 0) {
474            if (!m_info.fillColor->m_shading->m_isExtendedBegin) {
475              continue;
476            }
477            s = 0;
478          }
479          if (s > 1.0f) {
480            if (!m_info.fillColor->m_shading->m_isExtendedEnd) {
481              continue;
482            }
483            s = 1.0f;
484          }
485          int index = (int32_t)(s * (FX_SHADING_Steps - 1));
486          dib_buf[column] = m_info.fillColor->m_shading->m_argbArray[index];
487        }
488      }
489      result = true;
490      break;
491    }
492    default: {
493      result = false;
494      break;
495    }
496  }
497  if (result) {
498    m_renderDevice->SaveState();
499    m_renderDevice->SetClip_PathFill(path->GetPathData(), matrix, fillMode);
500    SetDIBitsWithMatrix(&bmp, matrix);
501    m_renderDevice->RestoreState(false);
502  }
503}
504
505void CFX_Graphics::SetDIBitsWithMatrix(CFX_DIBSource* source,
506                                       CFX_Matrix* matrix) {
507  if (matrix->IsIdentity()) {
508    m_renderDevice->SetDIBits(source, 0, 0);
509  } else {
510    CFX_Matrix m((FX_FLOAT)source->GetWidth(), 0, 0,
511                 (FX_FLOAT)source->GetHeight(), 0, 0);
512    m.Concat(*matrix);
513    int32_t left;
514    int32_t top;
515    std::unique_ptr<CFX_DIBitmap> bmp1 = source->FlipImage(false, true);
516    std::unique_ptr<CFX_DIBitmap> bmp2 = bmp1->TransformTo(&m, left, top);
517    m_renderDevice->SetDIBits(bmp2.get(), left, top);
518  }
519}
520
521CFX_Graphics::TInfo::TInfo()
522    : isActOnDash(false), strokeColor(nullptr), fillColor(nullptr) {}
523
524CFX_Graphics::TInfo::TInfo(const TInfo& info)
525    : graphState(info.graphState),
526      CTM(info.CTM),
527      isActOnDash(info.isActOnDash),
528      strokeColor(info.strokeColor),
529      fillColor(info.fillColor) {}
530
531CFX_Graphics::TInfo& CFX_Graphics::TInfo::operator=(const TInfo& other) {
532  graphState.Copy(other.graphState);
533  CTM = other.CTM;
534  isActOnDash = other.isActOnDash;
535  strokeColor = other.strokeColor;
536  fillColor = other.fillColor;
537  return *this;
538}
539