Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
cfgas_gegraphics.cpp
Go to the documentation of this file.
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 <array>
12#include <iterator>
13#include <memory>
14#include <utility>
15
16#include "core/fxcrt/check.h"
17#include "core/fxcrt/fx_system.h"
18#include "core/fxcrt/span_util.h"
19#include "core/fxcrt/stl_util.h"
20#include "core/fxge/cfx_defaultrenderdevice.h"
21#include "core/fxge/cfx_renderdevice.h"
22#include "core/fxge/cfx_unicodeencoding.h"
23#include "core/fxge/dib/cfx_dibitmap.h"
24#include "xfa/fgas/graphics/cfgas_gecolor.h"
25#include "xfa/fgas/graphics/cfgas_gepath.h"
26#include "xfa/fgas/graphics/cfgas_gepattern.h"
27#include "xfa/fgas/graphics/cfgas_geshading.h"
28
29namespace {
30
31struct FX_HATCHDATA {
32 int32_t width;
33 int32_t height;
34 uint8_t maskBits[64];
35};
36
37constexpr auto kHatchBitmapData = fxcrt::ToArray<const FX_HATCHDATA>({
38 {16, // Horizontal
39 16,
40 {
41 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
42 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
43 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
44 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
45 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
46 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
47 }},
48 {16, // Vertical
49 16,
50 {
51 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00,
52 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80,
53 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80,
54 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
55 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00,
56 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
57 }},
58 {16, // ForwardDiagonal
59 16,
60 {
61 0x80, 0x80, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x20, 0x20, 0x00,
62 0x00, 0x10, 0x10, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x04, 0x04,
63 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x80,
64 0x80, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00,
65 0x10, 0x10, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x04, 0x04, 0x00,
66 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
67 }},
68 {16, // BackwardDiagonal
69 16,
70 {
71 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x04, 0x04, 0x00,
72 0x00, 0x08, 0x08, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x20, 0x20,
73 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x01,
74 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00,
75 0x08, 0x08, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x20, 0x20, 0x00,
76 0x00, 0x40, 0x40, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
77 }},
78 {16, // Cross
79 16,
80 {
81 0xff, 0xff, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00,
82 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80,
83 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0xff,
84 0xff, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
85 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00,
86 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
87 }},
88 {16, // DiagonalCross
89 16,
90 {
91 0x81, 0x81, 0x00, 0x00, 0x42, 0x42, 0x00, 0x00, 0x24, 0x24, 0x00,
92 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x24, 0x24,
93 0x00, 0x00, 0x42, 0x42, 0x00, 0x00, 0x81, 0x81, 0x00, 0x00, 0x81,
94 0x81, 0x00, 0x00, 0x42, 0x42, 0x00, 0x00, 0x24, 0x24, 0x00, 0x00,
95 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x24, 0x24, 0x00,
96 0x00, 0x42, 0x42, 0x00, 0x00, 0x81, 0x81, 0x00, 0x00,
97 }},
98});
99
100const FX_HATCHDATA kHatchPlaceHolder = {
101 0,
102 0,
103 {
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, 0x00, 0x00,
109 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
110 }};
111
112const FX_HATCHDATA& GetHatchBitmapData(size_t index) {
113 return index < std::size(kHatchBitmapData) ? kHatchBitmapData[index]
114 : kHatchPlaceHolder;
115}
116
117} // namespace
118
121 DCHECK(m_renderDevice);
122}
123
125
126void CFGAS_GEGraphics::SaveGraphState() {
127 m_renderDevice->SaveState();
128 m_infoStack.push_back(std::make_unique<TInfo>(m_info));
129}
130
131void CFGAS_GEGraphics::RestoreGraphState() {
132 m_renderDevice->RestoreState(false);
133 CHECK(!m_infoStack.empty());
134 m_info = *m_infoStack.back();
135 m_infoStack.pop_back();
136 return;
137}
138
140 m_info.graphState.m_LineCap = lineCap;
141}
142
143void CFGAS_GEGraphics::SetLineDash(float dashPhase,
144 pdfium::span<const float> dashArray) {
145 DCHECK(!dashArray.empty());
146 float scale = m_info.isActOnDash ? m_info.graphState.m_LineWidth : 1.0;
147 m_info.graphState.m_DashPhase = dashPhase;
148 m_info.graphState.m_DashArray.resize(dashArray.size());
149 for (size_t i = 0; i < dashArray.size(); ++i)
150 m_info.graphState.m_DashArray[i] = dashArray[i] * scale;
151}
152
154 m_info.graphState.m_DashArray.clear();
155}
156
157void CFGAS_GEGraphics::SetLineWidth(float lineWidth) {
158 m_info.graphState.m_LineWidth = lineWidth;
159}
160
162 m_info.isActOnDash = true;
163}
164
166 m_info.strokeColor = color;
167}
168
170 m_info.fillColor = color;
171}
172
173void CFGAS_GEGraphics::StrokePath(const CFGAS_GEPath& path,
174 const CFX_Matrix& matrix) {
175 RenderDeviceStrokePath(path, matrix);
176}
177
178void CFGAS_GEGraphics::FillPath(const CFGAS_GEPath& path,
180 const CFX_Matrix& matrix) {
181 RenderDeviceFillPath(path, fill_type, matrix);
182}
183
185 m_info.CTM.Concat(matrix);
186}
187
189 return &m_info.CTM;
190}
191
193 FX_RECT r = m_renderDevice->GetClipBox();
195}
196
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
204 return m_renderDevice;
205}
206
207void CFGAS_GEGraphics::RenderDeviceStrokePath(const CFGAS_GEPath& path,
208 const CFX_Matrix& matrix) {
209 if (m_info.strokeColor.GetType() != CFGAS_GEColor::Solid)
210 return;
211
212 CFX_Matrix m = m_info.CTM;
213 m.Concat(matrix);
214 m_renderDevice->DrawPath(path.GetPath(), &m, &m_info.graphState, 0x0,
215 m_info.strokeColor.GetArgb(),
216 CFX_FillRenderOptions());
217}
218
219void CFGAS_GEGraphics::RenderDeviceFillPath(
220 const CFGAS_GEPath& path,
222 const CFX_Matrix& matrix) {
223 CFX_Matrix m = m_info.CTM;
224 m.Concat(matrix);
225
226 const CFX_FillRenderOptions fill_options(fill_type);
227 switch (m_info.fillColor.GetType()) {
228 case CFGAS_GEColor::Solid:
229 m_renderDevice->DrawPath(path.GetPath(), &m, &m_info.graphState,
230 m_info.fillColor.GetArgb(), 0x0, fill_options);
231 return;
233 FillPathWithPattern(path, fill_options, m);
234 return;
236 FillPathWithShading(path, fill_options, m);
237 return;
238 default:
239 return;
240 }
241}
242
243void CFGAS_GEGraphics::FillPathWithPattern(
244 const CFGAS_GEPath& path,
245 const CFX_FillRenderOptions& fill_options,
246 const CFX_Matrix& matrix) {
247 RetainPtr<const CFX_DIBitmap> bitmap = m_renderDevice->GetBitmap();
248 int32_t width = bitmap->GetWidth();
249 int32_t height = bitmap->GetHeight();
250 auto bmp = pdfium::MakeRetain<CFX_DIBitmap>();
251 // TODO(crbug.com/355630556): Consider adding support for
252 // `FXDIB_Format::kBgraPremul`
253 CHECK(bmp->Create(width, height, FXDIB_Format::kBgra));
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 CHECK(mask->Create(data.width, data.height, FXDIB_Format::k1bppMask));
263 fxcrt::Copy(
264 pdfium::make_span(data.maskBits).first(mask->GetPitch() * data.height),
265 mask->GetWritableBuffer());
266 const CFX_FloatRect rectf =
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
284void CFGAS_GEGraphics::FillPathWithShading(
285 const CFGAS_GEPath& path,
286 const CFX_FillRenderOptions& fill_options,
287 const CFX_Matrix& matrix) {
288 RetainPtr<const 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 // TODO(crbug.com/355630556): Consider adding support for
297 // `FXDIB_Format::kBgraPremul`
298 CHECK(bmp->Create(width, height, FXDIB_Format::kBgra));
299 m_renderDevice->GetDIBits(bmp, 0, 0);
300 bool result = false;
301 switch (m_info.fillColor.GetShading()->GetType()) {
302 case CFGAS_GEShading::Type::kAxial: {
303 float x_span = end_x - start_x;
304 float y_span = end_y - start_y;
305 float axis_len_square = (x_span * x_span) + (y_span * y_span);
306 for (int32_t row = 0; row < height; row++) {
307 auto dib_buf = bmp->GetWritableScanlineAs<uint32_t>(row);
308 for (int32_t column = 0; column < width; column++) {
309 float scale = 0.0f;
310 if (axis_len_square) {
311 float y = static_cast<float>(row);
312 float x = static_cast<float>(column);
313 scale = (((x - start_x) * x_span) + ((y - start_y) * y_span)) /
314 axis_len_square;
315 if (isnan(scale) || scale < 0.0f) {
316 if (!m_info.fillColor.GetShading()->IsExtendedBegin())
317 continue;
318 scale = 0.0f;
319 } else if (scale > 1.0f) {
320 if (!m_info.fillColor.GetShading()->IsExtendedEnd())
321 continue;
322 scale = 1.0f;
323 }
324 }
325 dib_buf[column] = m_info.fillColor.GetShading()->GetArgb(scale);
326 }
327 }
328 result = true;
329 break;
330 }
331 case CFGAS_GEShading::Type::kRadial: {
332 float start_r = m_info.fillColor.GetShading()->GetBeginRadius();
333 float end_r = m_info.fillColor.GetShading()->GetEndRadius();
334 float a = ((start_x - end_x) * (start_x - end_x)) +
335 ((start_y - end_y) * (start_y - end_y)) -
336 ((start_r - end_r) * (start_r - end_r));
337 for (int32_t row = 0; row < height; row++) {
338 auto dib_buf = bmp->GetWritableScanlineAs<uint32_t>(row);
339 for (int32_t column = 0; column < width; column++) {
340 float x = (float)(column);
341 float y = (float)(row);
342 float b = -2 * (((x - start_x) * (end_x - start_x)) +
343 ((y - start_y) * (end_y - start_y)) +
344 (start_r * (end_r - start_r)));
345 float c = ((x - start_x) * (x - start_x)) +
346 ((y - start_y) * (y - start_y)) - (start_r * start_r);
347 float s;
348 if (a == 0) {
349 s = -c / b;
350 } else {
351 float b2_4ac = (b * b) - 4 * (a * c);
352 if (b2_4ac < 0) {
353 continue;
354 }
355 float root = (sqrt(b2_4ac));
356 float s1;
357 float s2;
358 if (a > 0) {
359 s1 = (-b - root) / (2 * a);
360 s2 = (-b + root) / (2 * a);
361 } else {
362 s2 = (-b - root) / (2 * a);
363 s1 = (-b + root) / (2 * a);
364 }
365 if (s2 <= 1.0f || m_info.fillColor.GetShading()->IsExtendedEnd()) {
366 s = (s2);
367 } else {
368 s = (s1);
369 }
370 if ((start_r) + s * (end_r - start_r) < 0) {
371 continue;
372 }
373 }
374 if (isnan(s) || s < 0.0f) {
375 if (!m_info.fillColor.GetShading()->IsExtendedBegin())
376 continue;
377 s = 0.0f;
378 }
379 if (s > 1.0f) {
380 if (!m_info.fillColor.GetShading()->IsExtendedEnd())
381 continue;
382 s = 1.0f;
383 }
384 dib_buf[column] = m_info.fillColor.GetShading()->GetArgb(s);
385 }
386 }
387 result = true;
388 break;
389 }
390 }
391 if (result) {
392 CFX_RenderDevice::StateRestorer restorer(m_renderDevice);
393 m_renderDevice->SetClip_PathFill(path.GetPath(), &matrix, fill_options);
394 SetDIBitsWithMatrix(std::move(bmp), matrix);
395 }
396}
397
398void CFGAS_GEGraphics::SetDIBitsWithMatrix(RetainPtr<CFX_DIBBase> source,
399 const CFX_Matrix& matrix) {
400 if (matrix.IsIdentity()) {
401 m_renderDevice->SetDIBits(source, 0, 0);
402 } else {
403 CFX_Matrix m((float)source->GetWidth(), 0, 0, (float)source->GetHeight(), 0,
404 0);
405 m.Concat(matrix);
406 int32_t left;
407 int32_t top;
408 RetainPtr<CFX_DIBitmap> bmp1 = source->FlipImage(false, true);
409 RetainPtr<CFX_DIBitmap> bmp2 = bmp1->TransformTo(m, &left, &top);
410 m_renderDevice->SetDIBits(bmp2, left, top);
411 }
412}
413
414CFGAS_GEGraphics::TInfo::TInfo() = default;
415
416CFGAS_GEGraphics::TInfo::TInfo(const TInfo& info) = default;
417
418CFGAS_GEGraphics::TInfo& CFGAS_GEGraphics::TInfo::operator=(
419 const TInfo& other) = default;
420
423 graphics_->SaveGraphState();
424}
425
427 graphics_->RestoreGraphState();
428}
#define DCHECK
Definition check.h:33
StateRestorer(CFGAS_GEGraphics *graphics)
void FillPath(const CFGAS_GEPath &path, CFX_FillRenderOptions::FillType fill_type, const CFX_Matrix &matrix)
void SetLineDash(float dashPhase, pdfium::span< const float > dashArray)
void SetLineWidth(float lineWidth)
CFX_RenderDevice * GetRenderDevice()
CFGAS_GEGraphics(CFX_RenderDevice *renderDevice)
void SetClipRect(const CFX_RectF &rect)
void SetStrokeColor(const CFGAS_GEColor &color)
void ConcatMatrix(const CFX_Matrix &matrix)
const CFX_Matrix * GetMatrix() const
CFX_RectF GetClipRect() const
void SetLineCap(CFX_GraphStateData::LineCap lineCap)
void SetFillColor(const CFGAS_GEColor &color)
void StrokePath(const CFGAS_GEPath &path, const CFX_Matrix &matrix)
const CFX_Path & GetPath() const
bool Attach(RetainPtr< CFX_DIBitmap > pBitmap)
FX_RECT ToRoundedFxRect() const
constexpr CFX_Matrix()=default
CFX_FloatRect TransformRect(const CFX_FloatRect &rect) const
constexpr CFX_Matrix(float a1, float b1, float c1, float d1, float e1, float f1)
bool IsIdentity() const
void Concat(const CFX_Matrix &right)
CFX_FloatRect GetBoundingBox() const
Definition cfx_path.cpp:323
constexpr CFX_RectF(float dst_left, float dst_top, float dst_width, float dst_height)
FXDIB_Format
Definition fx_dib.h:21
#define CHECK(cvref)
constexpr CFX_FillRenderOptions(FillType fill_type)
int Height() const
int32_t bottom
int32_t right
int Width() const
int32_t top
int32_t left