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