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