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