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 "xfa/fgas/graphics/cfgas_gecolor.h"
23 #include "xfa/fgas/graphics/cfgas_gepath.h"
24 #include "xfa/fgas/graphics/cfgas_gepattern.h"
25 #include "xfa/fgas/graphics/cfgas_geshading.h"
26
27 namespace {
28
29 struct FX_HATCHDATA {
30 int32_t width;
31 int32_t height;
32 uint8_t maskBits[64];
33 };
34
35 const FX_HATCHDATA kHatchBitmapData[] = {
36 {16, // Horizontal
37 16,
38 {
39 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
40 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
41 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
42 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
43 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
44 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
45 }},
46 {16, // Vertical
47 16,
48 {
49 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00,
50 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80,
51 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80,
52 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
53 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00,
54 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
55 }},
56 {16, // ForwardDiagonal
57 16,
58 {
59 0x80, 0x80, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x20, 0x20, 0x00,
60 0x00, 0x10, 0x10, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x04, 0x04,
61 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x80,
62 0x80, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00,
63 0x10, 0x10, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x04, 0x04, 0x00,
64 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
65 }},
66 {16, // BackwardDiagonal
67 16,
68 {
69 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x04, 0x04, 0x00,
70 0x00, 0x08, 0x08, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x20, 0x20,
71 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x01,
72 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00,
73 0x08, 0x08, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x20, 0x20, 0x00,
74 0x00, 0x40, 0x40, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
75 }},
76 {16, // Cross
77 16,
78 {
79 0xff, 0xff, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00,
80 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80,
81 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0xff,
82 0xff, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
83 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00,
84 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
85 }},
86 {16, // DiagonalCross
87 16,
88 {
89 0x81, 0x81, 0x00, 0x00, 0x42, 0x42, 0x00, 0x00, 0x24, 0x24, 0x00,
90 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x24, 0x24,
91 0x00, 0x00, 0x42, 0x42, 0x00, 0x00, 0x81, 0x81, 0x00, 0x00, 0x81,
92 0x81, 0x00, 0x00, 0x42, 0x42, 0x00, 0x00, 0x24, 0x24, 0x00, 0x00,
93 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x24, 0x24, 0x00,
94 0x00, 0x42, 0x42, 0x00, 0x00, 0x81, 0x81, 0x00, 0x00,
95 }},
96 };
97
98 const FX_HATCHDATA kHatchPlaceHolder = {
99 0,
100 0,
101 {
102 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
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,
108 }};
109
GetHatchBitmapData(size_t index)110 const FX_HATCHDATA& GetHatchBitmapData(size_t index) {
111 return index < std::size(kHatchBitmapData) ? kHatchBitmapData[index]
112 : kHatchPlaceHolder;
113 }
114
115 } // namespace
116
CFGAS_GEGraphics(CFX_RenderDevice * renderDevice)117 CFGAS_GEGraphics::CFGAS_GEGraphics(CFX_RenderDevice* renderDevice)
118 : m_renderDevice(renderDevice) {
119 DCHECK(m_renderDevice);
120 }
121
122 CFGAS_GEGraphics::~CFGAS_GEGraphics() = default;
123
SaveGraphState()124 void CFGAS_GEGraphics::SaveGraphState() {
125 m_renderDevice->SaveState();
126 m_infoStack.push_back(std::make_unique<TInfo>(m_info));
127 }
128
RestoreGraphState()129 void CFGAS_GEGraphics::RestoreGraphState() {
130 m_renderDevice->RestoreState(false);
131 CHECK(!m_infoStack.empty());
132 m_info = *m_infoStack.back();
133 m_infoStack.pop_back();
134 return;
135 }
136
SetLineCap(CFX_GraphStateData::LineCap lineCap)137 void CFGAS_GEGraphics::SetLineCap(CFX_GraphStateData::LineCap lineCap) {
138 m_info.graphState.m_LineCap = lineCap;
139 }
140
SetLineDash(float dashPhase,pdfium::span<const float> dashArray)141 void CFGAS_GEGraphics::SetLineDash(float dashPhase,
142 pdfium::span<const float> dashArray) {
143 DCHECK(!dashArray.empty());
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(dashArray.size());
147 for (size_t i = 0; i < dashArray.size(); ++i)
148 m_info.graphState.m_DashArray[i] = dashArray[i] * scale;
149 }
150
SetSolidLineDash()151 void CFGAS_GEGraphics::SetSolidLineDash() {
152 m_info.graphState.m_DashArray.clear();
153 }
154
SetLineWidth(float lineWidth)155 void CFGAS_GEGraphics::SetLineWidth(float lineWidth) {
156 m_info.graphState.m_LineWidth = lineWidth;
157 }
158
EnableActOnDash()159 void CFGAS_GEGraphics::EnableActOnDash() {
160 m_info.isActOnDash = true;
161 }
162
SetStrokeColor(const CFGAS_GEColor & color)163 void CFGAS_GEGraphics::SetStrokeColor(const CFGAS_GEColor& color) {
164 m_info.strokeColor = color;
165 }
166
SetFillColor(const CFGAS_GEColor & color)167 void CFGAS_GEGraphics::SetFillColor(const CFGAS_GEColor& color) {
168 m_info.fillColor = color;
169 }
170
StrokePath(const CFGAS_GEPath & path,const CFX_Matrix & matrix)171 void CFGAS_GEGraphics::StrokePath(const CFGAS_GEPath& path,
172 const CFX_Matrix& matrix) {
173 RenderDeviceStrokePath(path, matrix);
174 }
175
FillPath(const CFGAS_GEPath & path,CFX_FillRenderOptions::FillType fill_type,const CFX_Matrix & matrix)176 void CFGAS_GEGraphics::FillPath(const CFGAS_GEPath& path,
177 CFX_FillRenderOptions::FillType fill_type,
178 const CFX_Matrix& matrix) {
179 RenderDeviceFillPath(path, fill_type, matrix);
180 }
181
ConcatMatrix(const CFX_Matrix & matrix)182 void CFGAS_GEGraphics::ConcatMatrix(const CFX_Matrix& matrix) {
183 m_info.CTM.Concat(matrix);
184 }
185
GetMatrix() const186 const CFX_Matrix* CFGAS_GEGraphics::GetMatrix() const {
187 return &m_info.CTM;
188 }
189
GetClipRect() const190 CFX_RectF CFGAS_GEGraphics::GetClipRect() const {
191 FX_RECT r = m_renderDevice->GetClipBox();
192 return CFX_RectF(r.left, r.top, r.Width(), r.Height());
193 }
194
SetClipRect(const CFX_RectF & rect)195 void CFGAS_GEGraphics::SetClipRect(const CFX_RectF& rect) {
196 m_renderDevice->SetClip_Rect(
197 FX_RECT(FXSYS_roundf(rect.left), FXSYS_roundf(rect.top),
198 FXSYS_roundf(rect.right()), FXSYS_roundf(rect.bottom())));
199 }
200
GetRenderDevice()201 CFX_RenderDevice* CFGAS_GEGraphics::GetRenderDevice() {
202 return m_renderDevice;
203 }
204
RenderDeviceStrokePath(const CFGAS_GEPath & path,const CFX_Matrix & matrix)205 void CFGAS_GEGraphics::RenderDeviceStrokePath(const CFGAS_GEPath& path,
206 const CFX_Matrix& matrix) {
207 if (m_info.strokeColor.GetType() != CFGAS_GEColor::Solid)
208 return;
209
210 CFX_Matrix m = m_info.CTM;
211 m.Concat(matrix);
212 m_renderDevice->DrawPath(path.GetPath(), &m, &m_info.graphState, 0x0,
213 m_info.strokeColor.GetArgb(),
214 CFX_FillRenderOptions());
215 }
216
RenderDeviceFillPath(const CFGAS_GEPath & path,CFX_FillRenderOptions::FillType fill_type,const CFX_Matrix & matrix)217 void CFGAS_GEGraphics::RenderDeviceFillPath(
218 const CFGAS_GEPath& path,
219 CFX_FillRenderOptions::FillType fill_type,
220 const CFX_Matrix& matrix) {
221 CFX_Matrix m = m_info.CTM;
222 m.Concat(matrix);
223
224 const CFX_FillRenderOptions fill_options(fill_type);
225 switch (m_info.fillColor.GetType()) {
226 case CFGAS_GEColor::Solid:
227 m_renderDevice->DrawPath(path.GetPath(), &m, &m_info.graphState,
228 m_info.fillColor.GetArgb(), 0x0, fill_options);
229 return;
230 case CFGAS_GEColor::Pattern:
231 FillPathWithPattern(path, fill_options, m);
232 return;
233 case CFGAS_GEColor::Shading:
234 FillPathWithShading(path, fill_options, m);
235 return;
236 default:
237 return;
238 }
239 }
240
FillPathWithPattern(const CFGAS_GEPath & path,const CFX_FillRenderOptions & fill_options,const CFX_Matrix & matrix)241 void CFGAS_GEGraphics::FillPathWithPattern(
242 const CFGAS_GEPath& path,
243 const CFX_FillRenderOptions& fill_options,
244 const CFX_Matrix& matrix) {
245 RetainPtr<CFX_DIBitmap> bitmap = m_renderDevice->GetBitmap();
246 int32_t width = bitmap->GetWidth();
247 int32_t height = bitmap->GetHeight();
248 auto bmp = pdfium::MakeRetain<CFX_DIBitmap>();
249 bmp->Create(width, height, FXDIB_Format::kArgb);
250 m_renderDevice->GetDIBits(bmp, 0, 0);
251
252 CFGAS_GEPattern::HatchStyle hatchStyle =
253 m_info.fillColor.GetPattern()->GetHatchStyle();
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_Format::k1bppMask);
259 fxcrt::spancpy(
260 mask->GetWritableBuffer(),
261 pdfium::make_span(data.maskBits).first(mask->GetPitch() * data.height));
262 const CFX_FloatRect rectf =
263 matrix.TransformRect(path.GetPath().GetBoundingBox());
264 const FX_RECT rect = rectf.ToRoundedFxRect();
265
266 CFX_DefaultRenderDevice device;
267 device.Attach(bmp);
268 device.FillRect(rect, m_info.fillColor.GetPattern()->GetBackArgb());
269 for (int32_t j = rect.bottom; j < rect.top; j += mask->GetHeight()) {
270 for (int32_t i = rect.left; i < rect.right; i += mask->GetWidth()) {
271 device.SetBitMask(mask, i, j,
272 m_info.fillColor.GetPattern()->GetForeArgb());
273 }
274 }
275 CFX_RenderDevice::StateRestorer restorer(m_renderDevice);
276 m_renderDevice->SetClip_PathFill(path.GetPath(), &matrix, fill_options);
277 SetDIBitsWithMatrix(std::move(bmp), CFX_Matrix());
278 }
279
FillPathWithShading(const CFGAS_GEPath & path,const CFX_FillRenderOptions & fill_options,const CFX_Matrix & matrix)280 void CFGAS_GEGraphics::FillPathWithShading(
281 const CFGAS_GEPath& path,
282 const CFX_FillRenderOptions& fill_options,
283 const CFX_Matrix& matrix) {
284 RetainPtr<CFX_DIBitmap> bitmap = m_renderDevice->GetBitmap();
285 int32_t width = bitmap->GetWidth();
286 int32_t height = bitmap->GetHeight();
287 float start_x = m_info.fillColor.GetShading()->GetBeginPoint().x;
288 float start_y = m_info.fillColor.GetShading()->GetBeginPoint().y;
289 float end_x = m_info.fillColor.GetShading()->GetEndPoint().x;
290 float end_y = m_info.fillColor.GetShading()->GetEndPoint().y;
291 auto bmp = pdfium::MakeRetain<CFX_DIBitmap>();
292 bmp->Create(width, height, FXDIB_Format::kArgb);
293 m_renderDevice->GetDIBits(bmp, 0, 0);
294 bool result = false;
295 switch (m_info.fillColor.GetShading()->GetType()) {
296 case CFGAS_GEShading::Type::kAxial: {
297 float x_span = end_x - start_x;
298 float y_span = end_y - start_y;
299 float axis_len_square = (x_span * x_span) + (y_span * y_span);
300 for (int32_t row = 0; row < height; row++) {
301 uint32_t* dib_buf =
302 reinterpret_cast<uint32_t*>(bmp->GetWritableScanline(row).data());
303 for (int32_t column = 0; column < width; column++) {
304 float scale = 0.0f;
305 if (axis_len_square) {
306 float y = static_cast<float>(row);
307 float x = static_cast<float>(column);
308 scale = (((x - start_x) * x_span) + ((y - start_y) * y_span)) /
309 axis_len_square;
310 if (isnan(scale) || scale < 0.0f) {
311 if (!m_info.fillColor.GetShading()->IsExtendedBegin())
312 continue;
313 scale = 0.0f;
314 } else if (scale > 1.0f) {
315 if (!m_info.fillColor.GetShading()->IsExtendedEnd())
316 continue;
317 scale = 1.0f;
318 }
319 }
320 int32_t index =
321 static_cast<int32_t>(scale * (CFGAS_GEShading::kSteps - 1));
322 dib_buf[column] = m_info.fillColor.GetShading()->GetArgb(index);
323 }
324 }
325 result = true;
326 break;
327 }
328 case CFGAS_GEShading::Type::kRadial: {
329 float start_r = m_info.fillColor.GetShading()->GetBeginRadius();
330 float end_r = m_info.fillColor.GetShading()->GetEndRadius();
331 float a = ((start_x - end_x) * (start_x - end_x)) +
332 ((start_y - end_y) * (start_y - end_y)) -
333 ((start_r - end_r) * (start_r - end_r));
334 for (int32_t row = 0; row < height; row++) {
335 uint32_t* dib_buf =
336 reinterpret_cast<uint32_t*>(bmp->GetWritableScanline(row).data());
337 for (int32_t column = 0; column < width; column++) {
338 float x = (float)(column);
339 float y = (float)(row);
340 float b = -2 * (((x - start_x) * (end_x - start_x)) +
341 ((y - start_y) * (end_y - start_y)) +
342 (start_r * (end_r - start_r)));
343 float c = ((x - start_x) * (x - start_x)) +
344 ((y - start_y) * (y - start_y)) - (start_r * start_r);
345 float s;
346 if (a == 0) {
347 s = -c / b;
348 } else {
349 float b2_4ac = (b * b) - 4 * (a * c);
350 if (b2_4ac < 0) {
351 continue;
352 }
353 float root = (sqrt(b2_4ac));
354 float s1;
355 float s2;
356 if (a > 0) {
357 s1 = (-b - root) / (2 * a);
358 s2 = (-b + root) / (2 * a);
359 } else {
360 s2 = (-b - root) / (2 * a);
361 s1 = (-b + root) / (2 * a);
362 }
363 if (s2 <= 1.0f || m_info.fillColor.GetShading()->IsExtendedEnd()) {
364 s = (s2);
365 } else {
366 s = (s1);
367 }
368 if ((start_r) + s * (end_r - start_r) < 0) {
369 continue;
370 }
371 }
372 if (isnan(s) || s < 0.0f) {
373 if (!m_info.fillColor.GetShading()->IsExtendedBegin())
374 continue;
375 s = 0.0f;
376 }
377 if (s > 1.0f) {
378 if (!m_info.fillColor.GetShading()->IsExtendedEnd())
379 continue;
380 s = 1.0f;
381 }
382 int index = static_cast<int32_t>(s * (CFGAS_GEShading::kSteps - 1));
383 dib_buf[column] = m_info.fillColor.GetShading()->GetArgb(index);
384 }
385 }
386 result = true;
387 break;
388 }
389 }
390 if (result) {
391 CFX_RenderDevice::StateRestorer restorer(m_renderDevice);
392 m_renderDevice->SetClip_PathFill(path.GetPath(), &matrix, fill_options);
393 SetDIBitsWithMatrix(std::move(bmp), matrix);
394 }
395 }
396
SetDIBitsWithMatrix(RetainPtr<CFX_DIBBase> source,const CFX_Matrix & matrix)397 void CFGAS_GEGraphics::SetDIBitsWithMatrix(RetainPtr<CFX_DIBBase> source,
398 const CFX_Matrix& matrix) {
399 if (matrix.IsIdentity()) {
400 m_renderDevice->SetDIBits(source, 0, 0);
401 } else {
402 CFX_Matrix m((float)source->GetWidth(), 0, 0, (float)source->GetHeight(), 0,
403 0);
404 m.Concat(matrix);
405 int32_t left;
406 int32_t top;
407 RetainPtr<CFX_DIBitmap> bmp1 = source->FlipImage(false, true);
408 RetainPtr<CFX_DIBitmap> bmp2 = bmp1->TransformTo(m, &left, &top);
409 m_renderDevice->SetDIBits(bmp2, left, top);
410 }
411 }
412
413 CFGAS_GEGraphics::TInfo::TInfo() = default;
414
TInfo(const TInfo & info)415 CFGAS_GEGraphics::TInfo::TInfo(const TInfo& info)
416 : graphState(info.graphState),
417 CTM(info.CTM),
418 isActOnDash(info.isActOnDash),
419 strokeColor(info.strokeColor),
420 fillColor(info.fillColor) {}
421
operator =(const TInfo & other)422 CFGAS_GEGraphics::TInfo& CFGAS_GEGraphics::TInfo::operator=(
423 const TInfo& other) {
424 graphState = other.graphState;
425 CTM = other.CTM;
426 isActOnDash = other.isActOnDash;
427 strokeColor = other.strokeColor;
428 fillColor = other.fillColor;
429 return *this;
430 }
431
StateRestorer(CFGAS_GEGraphics * graphics)432 CFGAS_GEGraphics::StateRestorer::StateRestorer(CFGAS_GEGraphics* graphics)
433 : graphics_(graphics) {
434 graphics_->SaveGraphState();
435 }
436
~StateRestorer()437 CFGAS_GEGraphics::StateRestorer::~StateRestorer() {
438 graphics_->RestoreGraphState();
439 }
440