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
fx_skia_device.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#include "core/fxge/skia/fx_skia_device.h"
6
7#include <math.h>
8#include <stddef.h>
9#include <stdint.h>
10
11#include <algorithm>
12#include <array>
13#include <limits>
14#include <memory>
15#include <utility>
16#include <vector>
17
18#include "build/build_config.h"
19#include "core/fpdfapi/page/cpdf_expintfunc.h"
20#include "core/fpdfapi/page/cpdf_function.h"
21#include "core/fpdfapi/page/cpdf_meshstream.h"
22#include "core/fpdfapi/page/cpdf_sampledfunc.h"
23#include "core/fpdfapi/page/cpdf_shadingpattern.h"
24#include "core/fpdfapi/page/cpdf_stitchfunc.h"
25#include "core/fpdfapi/parser/cpdf_array.h"
26#include "core/fpdfapi/parser/cpdf_dictionary.h"
27#include "core/fpdfapi/parser/cpdf_stream.h"
28#include "core/fpdfapi/parser/cpdf_stream_acc.h"
29#include "core/fxcrt/cfx_bitstream.h"
30#include "core/fxcrt/check.h"
31#include "core/fxcrt/check_op.h"
32#include "core/fxcrt/data_vector.h"
33#include "core/fxcrt/fx_2d_size.h"
34#include "core/fxcrt/fx_coordinates.h"
35#include "core/fxcrt/fx_memory.h"
36#include "core/fxcrt/fx_system.h"
37#include "core/fxcrt/notreached.h"
38#include "core/fxcrt/numerics/safe_conversions.h"
39#include "core/fxcrt/ptr_util.h"
40#include "core/fxcrt/span.h"
41#include "core/fxcrt/stl_util.h"
42#include "core/fxge/agg/cfx_agg_imagerenderer.h"
43#include "core/fxge/cfx_defaultrenderdevice.h"
44#include "core/fxge/cfx_fillrenderoptions.h"
45#include "core/fxge/cfx_font.h"
46#include "core/fxge/cfx_graphstatedata.h"
47#include "core/fxge/cfx_path.h"
48#include "core/fxge/cfx_renderdevice.h"
49#include "core/fxge/cfx_substfont.h"
50#include "core/fxge/cfx_textrenderoptions.h"
51#include "core/fxge/dib/cfx_dibitmap.h"
52#include "core/fxge/dib/cstretchengine.h"
53#include "core/fxge/dib/fx_dib.h"
54#include "core/fxge/text_char_pos.h"
55#include "third_party/skia/include/core/SkBlendMode.h"
56#include "third_party/skia/include/core/SkCanvas.h"
57#include "third_party/skia/include/core/SkClipOp.h"
58#include "third_party/skia/include/core/SkColorFilter.h"
59#include "third_party/skia/include/core/SkColorPriv.h"
60#include "third_party/skia/include/core/SkColorType.h"
61#include "third_party/skia/include/core/SkImage.h"
62#include "third_party/skia/include/core/SkImageInfo.h"
63#include "third_party/skia/include/core/SkMaskFilter.h"
64#include "third_party/skia/include/core/SkPaint.h"
65#include "third_party/skia/include/core/SkPath.h"
66#include "third_party/skia/include/core/SkPathEffect.h"
67#include "third_party/skia/include/core/SkPathUtils.h"
68#include "third_party/skia/include/core/SkPixmap.h"
69#include "third_party/skia/include/core/SkRSXform.h"
70#include "third_party/skia/include/core/SkRect.h"
71#include "third_party/skia/include/core/SkRefCnt.h"
72#include "third_party/skia/include/core/SkSamplingOptions.h"
73#include "third_party/skia/include/core/SkShader.h"
74#include "third_party/skia/include/core/SkStream.h"
75#include "third_party/skia/include/core/SkSurface.h"
76#include "third_party/skia/include/core/SkTextBlob.h"
77#include "third_party/skia/include/core/SkTileMode.h"
78#include "third_party/skia/include/core/SkTypeface.h"
79#include "third_party/skia/include/effects/SkDashPathEffect.h"
80#include "third_party/skia/include/effects/SkGradientShader.h"
81#include "third_party/skia/include/pathops/SkPathOps.h"
82
83namespace {
84
85#define SHOW_SKIA_PATH 0 // set to 1 to print the path contents
87#define SHOW_SKIA_PATH_SHORTHAND 0 // set to 1 for abbreviated path contents
88#endif
89
91void DebugShowSkiaPaint(const SkPaint& paint) {
92 if (SkPaint::kFill_Style == paint.getStyle()) {
93 printf("fill 0x%08x\n", paint.getColor());
94 } else {
95 printf("stroke 0x%08x width %g\n", paint.getColor(),
96 paint.getStrokeWidth());
97 }
98}
99#endif // SHOW_SKIA_PATH
100
101void DebugShowSkiaPath(const SkPath& path) {
103#if SHOW_SKIA_PATH_SHORTHAND
104 printf(" **\n");
105#else
106 SkDynamicMemoryWStream stream;
107 path.dump(&stream, false);
108 DataVector<char> storage(stream.bytesWritten());
109 stream.copyTo(storage.data());
110 printf("%.*s", static_cast<int>(storage.size()), storage.data());
111#endif // SHOW_SKIA_PATH_SHORTHAND
112#endif // SHOW_SKIA_PATH
113}
114
115void DebugShowCanvasClip(CFX_SkiaDeviceDriver* driver, const SkCanvas* canvas) {
117 SkMatrix matrix = canvas->getTotalMatrix();
118 SkScalar m[9];
119 matrix.get9(m);
120 printf("matrix (%g,%g,%g) (%g,%g,%g) (%g,%g,%g)\n", m[0], m[1], m[2], m[3],
121 m[4], m[5], m[6], m[7], m[8]);
122 SkRect local = canvas->getLocalClipBounds();
123 SkIRect device = canvas->getDeviceClipBounds();
124
125 printf("local bounds %g %g %g %g\n", local.fLeft, local.fTop, local.fRight,
126 local.fBottom);
127 printf("device bounds %d %d %d %d\n", device.fLeft, device.fTop,
128 device.fRight, device.fBottom);
129 FX_RECT clip_box = driver->GetClipBox();
130 printf("reported bounds %d %d %d %d\n", clip_box.left, clip_box.top,
131 clip_box.right, clip_box.bottom);
132#endif // SHOW_SKIA_PATH
133}
134
135void DebugShowSkiaDrawPath(CFX_SkiaDeviceDriver* driver,
136 const SkCanvas* canvas,
137 const SkPaint& paint,
138 const SkPath& path) {
140 DebugShowSkiaPaint(paint);
141 DebugShowCanvasClip(driver, canvas);
142 DebugShowSkiaPath(path);
143 printf("\n");
144#endif // SHOW_SKIA_PATH
145}
146
147void DebugShowSkiaDrawRect(CFX_SkiaDeviceDriver* driver,
148 const SkCanvas* canvas,
149 const SkPaint& paint,
150 const SkRect& rect) {
152 DebugShowSkiaPaint(paint);
153 DebugShowCanvasClip(driver, canvas);
154 printf("rect %g %g %g %g\n", rect.fLeft, rect.fTop, rect.fRight,
155 rect.fBottom);
156#endif // SHOW_SKIA_PATH
157}
158
159void DebugValidate(const RetainPtr<CFX_DIBitmap>& bitmap) {
160#if DCHECK_IS_ON()
161 DCHECK(bitmap);
162 DCHECK(bitmap->GetBPP() == 8 || bitmap->GetBPP() == 32);
163#endif
164}
165
166SkColorType Get32BitSkColorType(bool is_rgb_byte_order) {
167 return is_rgb_byte_order ? kRGBA_8888_SkColorType : kBGRA_8888_SkColorType;
168}
169
170SkPathFillType GetAlternateOrWindingFillType(
171 const CFX_FillRenderOptions& fill_options) {
172 // TODO(thestig): This function should be able to assert
173 // fill_options.fill_type != CFX_FillRenderOptions::FillType::kNoFill.
174 return fill_options.fill_type == CFX_FillRenderOptions::FillType::kEvenOdd
175 ? SkPathFillType::kEvenOdd
176 : SkPathFillType::kWinding;
177}
178
179SkFont::Edging GetFontEdgingType(const CFX_TextRenderOptions& text_options) {
180 if (text_options.aliasing_type == CFX_TextRenderOptions::kAliasing)
181 return SkFont::Edging::kAlias;
182
183 if (text_options.aliasing_type == CFX_TextRenderOptions::kAntiAliasing)
184 return SkFont::Edging::kAntiAlias;
185
187 return SkFont::Edging::kSubpixelAntiAlias;
188}
189
190bool IsPathAPoint(const SkPath& path) {
191 if (path.isEmpty())
192 return false;
193
194 if (path.countPoints() == 1)
195 return true;
196
197 for (int i = 0; i < path.countPoints() - 1; ++i) {
198 if (path.getPoint(i) != path.getPoint(i + 1))
199 return false;
200 }
201 return true;
202}
203
204SkPath BuildPath(const CFX_Path& path) {
205 SkPath sk_path;
206 pdfium::span<const CFX_Path::Point> points = path.GetPoints();
207 for (size_t i = 0; i < points.size(); ++i) {
208 const CFX_PointF& point = points[i].m_Point;
209 CFX_Path::Point::Type point_type = points[i].m_Type;
210 if (point_type == CFX_Path::Point::Type::kMove) {
211 sk_path.moveTo(point.x, point.y);
212 } else if (point_type == CFX_Path::Point::Type::kLine) {
213 sk_path.lineTo(point.x, point.y);
214 } else if (point_type == CFX_Path::Point::Type::kBezier) {
215 const CFX_PointF& point2 = points[i + 1].m_Point;
216 const CFX_PointF& point3 = points[i + 2].m_Point;
217 sk_path.cubicTo(point.x, point.y, point2.x, point2.y, point3.x, point3.y);
218 i += 2;
219 }
220 if (points[i].m_CloseFigure)
221 sk_path.close();
222 }
223 return sk_path;
224}
225
226SkMatrix ToSkMatrix(const CFX_Matrix& m) {
227 SkMatrix skMatrix;
228 skMatrix.setAll(m.a, m.c, m.e, m.b, m.d, m.f, 0, 0, 1);
229 return skMatrix;
230}
231
232// use when pdf's y-axis points up instead of down
233SkMatrix ToFlippedSkMatrix(const CFX_Matrix& m, SkScalar flip) {
234 SkMatrix skMatrix;
235 skMatrix.setAll(m.a * flip, -m.c * flip, m.e, m.b * flip, -m.d * flip, m.f, 0,
236 0, 1);
237 return skMatrix;
238}
239
240SkBlendMode GetSkiaBlendMode(BlendMode blend_type) {
241 switch (blend_type) {
242 case BlendMode::kMultiply:
243 return SkBlendMode::kMultiply;
244 case BlendMode::kScreen:
245 return SkBlendMode::kScreen;
246 case BlendMode::kOverlay:
247 return SkBlendMode::kOverlay;
248 case BlendMode::kDarken:
249 return SkBlendMode::kDarken;
250 case BlendMode::kLighten:
251 return SkBlendMode::kLighten;
252 case BlendMode::kColorDodge:
253 return SkBlendMode::kColorDodge;
254 case BlendMode::kColorBurn:
255 return SkBlendMode::kColorBurn;
256 case BlendMode::kHardLight:
257 return SkBlendMode::kHardLight;
258 case BlendMode::kSoftLight:
259 return SkBlendMode::kSoftLight;
260 case BlendMode::kDifference:
261 return SkBlendMode::kDifference;
262 case BlendMode::kExclusion:
263 return SkBlendMode::kExclusion;
264 case BlendMode::kHue:
265 return SkBlendMode::kHue;
266 case BlendMode::kSaturation:
267 return SkBlendMode::kSaturation;
268 case BlendMode::kColor:
269 return SkBlendMode::kColor;
270 case BlendMode::kLuminosity:
271 return SkBlendMode::kLuminosity;
272 case BlendMode::kNormal:
273 return SkBlendMode::kSrcOver;
274 }
275}
276
277// Add begin & end colors into `colors` array for each gradient transition.
278//
279// `is_encode_reversed` must be set to true when the parent function of `func`
280// has an Encode array, and the matching pair of encode values for `func` are
281// in decreasing order.
282bool AddColors(const CPDF_ExpIntFunc* func,
283 DataVector<SkColor>& colors,
284 bool is_encode_reversed) {
285 if (func->InputCount() != 1) {
286 return false;
287 }
288 if (func->GetExponent() != 1) {
289 return false;
290 }
291 if (func->GetOrigOutputs() != 3) {
292 return false;
293 }
294
295 pdfium::span<const float> begin_values = func->GetBeginValues();
296 pdfium::span<const float> end_values = func->GetEndValues();
297 if (is_encode_reversed)
298 std::swap(begin_values, end_values);
299
300 colors.push_back(SkColorSetARGB(0xFF,
301 SkUnitScalarClampToByte(begin_values[0]),
302 SkUnitScalarClampToByte(begin_values[1]),
303 SkUnitScalarClampToByte(begin_values[2])));
304 colors.push_back(SkColorSetARGB(0xFF, SkUnitScalarClampToByte(end_values[0]),
305 SkUnitScalarClampToByte(end_values[1]),
306 SkUnitScalarClampToByte(end_values[2])));
307 return true;
308}
309
310uint8_t FloatToByte(float f) {
311 DCHECK(f >= 0);
312 DCHECK(f <= 1);
313 return (uint8_t)(f * 255.99f);
314}
315
316bool AddSamples(const CPDF_SampledFunc* func,
317 DataVector<SkColor>& colors,
318 DataVector<SkScalar>& pos) {
319 if (func->InputCount() != 1) {
320 return false;
321 }
322 if (func->OutputCount() != 3) { // expect rgb
323 return false;
324 }
325 if (func->GetEncodeInfo().empty()) {
326 return false;
327 }
328 const CPDF_SampledFunc::SampleEncodeInfo& encode_info =
329 func->GetEncodeInfo()[0];
330 if (encode_info.encode_min != 0) {
331 return false;
332 }
333 if (encode_info.encode_max != encode_info.sizes - 1) {
334 return false;
335 }
336 uint32_t sample_size = func->GetBitsPerSample();
337 uint32_t sample_count = encode_info.sizes;
338 if (sample_count != 1U << sample_size) {
339 return false;
340 }
341 if (func->GetSampleStream()->GetSize() < sample_count * 3 * sample_size / 8) {
342 return false;
343 }
344
345 std::array<float, 3> colors_min;
346 std::array<float, 3> colors_max;
347 for (int i = 0; i < 3; ++i) {
348 colors_min[i] = func->GetRange(i * 2);
349 colors_max[i] = func->GetRange(i * 2 + 1);
350 }
351 pdfium::span<const uint8_t> sample_data =
352 func->GetSampleStream()->GetSpan();
353 CFX_BitStream bitstream(sample_data);
354 for (uint32_t i = 0; i < sample_count; ++i) {
355 std::array<float, 3> float_colors;
356 for (uint32_t j = 0; j < 3; ++j) {
357 float sample = static_cast<float>(bitstream.GetBits(sample_size));
358 float interp = sample / (sample_count - 1);
359 float_colors[j] =
360 colors_min[j] + (colors_max[j] - colors_min[j]) * interp;
361 }
362 colors.push_back(SkPackARGB32(0xFF, FloatToByte(float_colors[0]),
363 FloatToByte(float_colors[1]),
364 FloatToByte(float_colors[2])));
365 pos.push_back(static_cast<float>(i) / (sample_count - 1));
366 }
367 return true;
368}
369
370bool AddStitching(const CPDF_StitchFunc* func,
371 DataVector<SkColor>& colors,
372 DataVector<SkScalar>& pos) {
373 float bounds_start = func->GetDomain(0);
374
375 const auto& sub_functions = func->GetSubFunctions();
376 const size_t sub_function_count = sub_functions.size();
377 for (size_t i = 0; i < sub_function_count; ++i) {
378 const CPDF_ExpIntFunc* sub_func = sub_functions[i]->ToExpIntFunc();
379 if (!sub_func)
380 return false;
381 // Check if the matching encode values are reversed
382 bool is_encode_reversed =
383 func->GetEncode(2 * i) > func->GetEncode(2 * i + 1);
384 if (!AddColors(sub_func, colors, is_encode_reversed)) {
385 return false;
386 }
387 float bounds_end =
388 i < sub_function_count - 1 ? func->GetBound(i + 1) : func->GetDomain(1);
389 pos.push_back(bounds_start);
390 pos.push_back(bounds_end);
391 bounds_start = bounds_end;
392 }
393 return true;
394}
395
396// see https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line
397SkScalar LineSide(const SkPoint& line_start,
398 const SkPoint& line_end,
399 const SkPoint& pt) {
400 return (line_end.fY - line_start.fY) * pt.fX -
401 (line_end.fX - line_start.fX) * pt.fY + line_end.fX * line_start.fY -
402 line_end.fY * line_start.fX;
403}
404
405SkPoint IntersectSides(const SkPoint& parallelPt,
406 const SkVector& paraRay,
407 const SkPoint& perpendicularPt) {
408 SkVector perpRay = {paraRay.fY, -paraRay.fX};
409 SkScalar denom = perpRay.fY * paraRay.fX - paraRay.fY * perpRay.fX;
410 if (!denom) {
411 SkPoint zeroPt = {0, 0};
412 return zeroPt;
413 }
414 SkVector ab0 = parallelPt - perpendicularPt;
415 SkScalar numerA = ab0.fY * perpRay.fX - perpRay.fY * ab0.fX;
416 numerA /= denom;
417 SkPoint result = {parallelPt.fX + paraRay.fX * numerA,
418 parallelPt.fY + paraRay.fY * numerA};
419 return result;
420}
421
422void ClipAngledGradient(pdfium::span<const SkPoint, 2> pts,
423 pdfium::span<const SkPoint, 4> rect_pts,
424 bool clip_start,
425 bool clip_end,
426 SkPath* clip) {
427 // find the corners furthest from the gradient perpendiculars
428 SkScalar minPerpDist = SK_ScalarMax;
429 SkScalar maxPerpDist = SK_ScalarMin;
430 int minPerpPtIndex = -1;
431 int maxPerpPtIndex = -1;
432 SkVector slope = pts[1] - pts[0];
433 const SkPoint start_perp[2] = {pts[0],
434 {pts[0].fX + slope.fY, pts[0].fY - slope.fX}};
435 const SkPoint end_perp[2] = {pts[1],
436 {pts[1].fX + slope.fY, pts[1].fY - slope.fX}};
437 for (int i = 0; i < 4; ++i) {
438 SkScalar sDist = LineSide(start_perp[0], start_perp[1], rect_pts[i]);
439 SkScalar eDist = LineSide(end_perp[0], end_perp[1], rect_pts[i]);
440 if (sDist * eDist <= 0) { // if the signs are different,
441 continue; // the point is inside the gradient
442 }
443 if (sDist < 0) {
444 SkScalar smaller = std::min(sDist, eDist);
445 if (minPerpDist > smaller) {
446 minPerpDist = smaller;
447 minPerpPtIndex = i;
448 }
449 } else {
450 SkScalar larger = std::max(sDist, eDist);
451 if (maxPerpDist < larger) {
452 maxPerpDist = larger;
453 maxPerpPtIndex = i;
454 }
455 }
456 }
457 if (minPerpPtIndex < 0 && maxPerpPtIndex < 0) { // nothing's outside
458 return;
459 }
460
461 // determine if negative distances are before start or after end
462 const SkPoint before_start = {pts[0].fX * 2 - pts[1].fX,
463 pts[0].fY * 2 - pts[1].fY};
464 bool before_neg = LineSide(start_perp[0], start_perp[1], before_start) < 0;
465
466 int noClipStartIndex = maxPerpPtIndex;
467 int noClipEndIndex = minPerpPtIndex;
468 if (before_neg) {
469 std::swap(noClipStartIndex, noClipEndIndex);
470 }
471 if ((!clip_start && noClipStartIndex < 0) ||
472 (!clip_end && noClipEndIndex < 0)) {
473 return;
474 }
475
476 const SkPoint& startEdgePt = clip_start ? pts[0] : rect_pts[noClipStartIndex];
477 const SkPoint& endEdgePt = clip_end ? pts[1] : rect_pts[noClipEndIndex];
478
479 // find the corners that bound the gradient
480 SkScalar minDist = SK_ScalarMax;
481 SkScalar maxDist = SK_ScalarMin;
482 int minBounds = -1;
483 int maxBounds = -1;
484 for (int i = 0; i < 4; ++i) {
485 SkScalar dist = LineSide(pts[0], pts[1], rect_pts[i]);
486 if (minDist > dist) {
487 minDist = dist;
488 minBounds = i;
489 }
490 if (maxDist < dist) {
491 maxDist = dist;
492 maxBounds = i;
493 }
494 }
495 if (minBounds < 0 || maxBounds < 0) {
496 return;
497 }
498 if (minBounds == maxBounds) {
499 return;
500 }
501 // construct a clip parallel to the gradient that goes through
502 // rect_pts[minBounds] and rect_pts[maxBounds] and perpendicular to the
503 // gradient that goes through startEdgePt, endEdgePt.
504 clip->moveTo(IntersectSides(rect_pts[minBounds], slope, startEdgePt));
505 clip->lineTo(IntersectSides(rect_pts[minBounds], slope, endEdgePt));
506 clip->lineTo(IntersectSides(rect_pts[maxBounds], slope, endEdgePt));
507 clip->lineTo(IntersectSides(rect_pts[maxBounds], slope, startEdgePt));
508}
509
510// Converts a stroking path to scanlines
511void PaintStroke(SkPaint* spaint,
512 const CFX_GraphStateData* graph_state,
513 const SkMatrix& matrix,
514 const CFX_FillRenderOptions& fill_options) {
515 SkPaint::Cap cap;
516 switch (graph_state->m_LineCap) {
517 case CFX_GraphStateData::LineCap::kRound:
518 cap = SkPaint::kRound_Cap;
519 break;
520 case CFX_GraphStateData::LineCap::kSquare:
521 cap = SkPaint::kSquare_Cap;
522 break;
523 default:
524 cap = SkPaint::kButt_Cap;
525 break;
526 }
527 SkPaint::Join join;
528 switch (graph_state->m_LineJoin) {
529 case CFX_GraphStateData::LineJoin::kRound:
530 join = SkPaint::kRound_Join;
531 break;
532 case CFX_GraphStateData::LineJoin::kBevel:
533 join = SkPaint::kBevel_Join;
534 break;
535 default:
536 join = SkPaint::kMiter_Join;
537 break;
538 }
539 SkMatrix inverse;
540 if (!matrix.invert(&inverse)) {
541 return; // give up if the matrix is degenerate, and not invertable
542 }
543 inverse.set(SkMatrix::kMTransX, 0);
544 inverse.set(SkMatrix::kMTransY, 0);
545 SkVector deviceUnits[2] = {{0, 1}, {1, 0}};
546 inverse.mapPoints(deviceUnits, std::size(deviceUnits));
547
548 float width = fill_options.zero_area
549 ? 0.0f
550 : std::max(graph_state->m_LineWidth,
551 std::min(deviceUnits[0].length(),
552 deviceUnits[1].length()));
553 if (!graph_state->m_DashArray.empty()) {
554 size_t count = (graph_state->m_DashArray.size() + 1) / 2;
555 DataVector<SkScalar> intervals(count * 2);
556 // Set dash pattern
557 for (size_t i = 0; i < count; i++) {
558 float on = graph_state->m_DashArray[i * 2];
559 if (on <= 0.000001f) {
560 on = 0.1f;
561 }
562 float off = i * 2 + 1 == graph_state->m_DashArray.size()
563 ? on
564 : graph_state->m_DashArray[i * 2 + 1];
565 off = std::max(off, 0.0f);
566 intervals[i * 2] = on;
567 intervals[i * 2 + 1] = off;
568 }
569 spaint->setPathEffect(SkDashPathEffect::Make(
570 intervals.data(), pdfium::checked_cast<int>(intervals.size()),
571 graph_state->m_DashPhase));
572 }
573 spaint->setStyle(SkPaint::kStroke_Style);
574 spaint->setAntiAlias(!fill_options.aliased_path);
575 spaint->setStrokeWidth(width);
576 spaint->setStrokeMiter(graph_state->m_MiterLimit);
577 spaint->setStrokeCap(cap);
578 spaint->setStrokeJoin(join);
579}
580
581void SetBitmapMatrix(const CFX_Matrix& m,
582 int width,
583 int height,
584 SkMatrix* skMatrix) {
585 skMatrix->setAll(m.a / width, -m.c / height, m.c + m.e, m.b / width,
586 -m.d / height, m.d + m.f, 0, 0, 1);
587}
588
589void SetBitmapPaint(bool is_mask,
590 bool anti_alias,
591 float alpha,
592 uint32_t argb,
593 BlendMode blend_type,
594 SkPaint* paint) {
595 DCHECK_GE(alpha, 0.0f);
596 DCHECK_LE(alpha, 1.0f);
597
598 if (is_mask) {
599 paint->setColor(argb);
600 } else if (alpha != 1.0f) {
601 paint->setAlphaf(alpha);
602 }
603
604 paint->setAntiAlias(anti_alias);
605 paint->setBlendMode(GetSkiaBlendMode(blend_type));
606}
607
608void SetBitmapPaintForMerge(bool is_mask,
609 bool anti_alias,
610 float alpha,
611 BlendMode blend_type,
612 SkPaint* paint) {
613 if (is_mask) {
614 paint->setColorFilter(SkColorFilters::Blend(0xFFFFFFFF, SkBlendMode::kSrc));
615 }
616
617 paint->setAlphaf(alpha);
618 paint->setAntiAlias(anti_alias);
619 paint->setBlendMode(GetSkiaBlendMode(blend_type));
620}
621
622// Makes a bitmap filled with a solid color for debugging with `SkPicture`.
623RetainPtr<CFX_DIBitmap> MakeDebugBitmap(int width, int height, uint32_t color) {
624 auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
625 // TODO(crbug.com/42271020): Consider adding support for
626 // `FXDIB_Format::kBgraPremul`
627 if (!bitmap->Create(width, height, FXDIB_Format::kBgra)) {
628 return nullptr;
629 }
630
631 bitmap->Clear(color);
632 return bitmap;
633}
634
635bool HasRSX(pdfium::span<const TextCharPos> char_pos,
636 float* scaleXPtr,
637 bool* oneAtATimePtr) {
638 bool useRSXform = false;
639 bool oneAtATime = false;
640 float scaleX = 1;
641 for (const TextCharPos& cp : char_pos) {
642 if (!cp.m_bGlyphAdjust) {
643 continue;
644 }
645 bool upright = 0 == cp.m_AdjustMatrix[1] && 0 == cp.m_AdjustMatrix[2];
646 if (cp.m_AdjustMatrix[0] != cp.m_AdjustMatrix[3]) {
647 if (upright && 1 == cp.m_AdjustMatrix[3]) {
648 if (1 == scaleX) {
649 scaleX = cp.m_AdjustMatrix[0];
650 } else if (scaleX != cp.m_AdjustMatrix[0]) {
651 oneAtATime = true;
652 }
653 } else {
654 oneAtATime = true;
655 }
656 } else if (cp.m_AdjustMatrix[1] != -cp.m_AdjustMatrix[2]) {
657 oneAtATime = true;
658 } else {
659 useRSXform = true;
660 }
661 }
662 *oneAtATimePtr = oneAtATime;
663 *scaleXPtr = oneAtATime ? 1 : scaleX;
664 return oneAtATime ? false : useRSXform;
665}
666
667} // namespace
668
669// static
670std::unique_ptr<CFX_SkiaDeviceDriver> CFX_SkiaDeviceDriver::Create(
671 RetainPtr<CFX_DIBitmap> pBitmap,
672 bool bRgbByteOrder,
673 RetainPtr<CFX_DIBitmap> pBackdropBitmap,
674 bool bGroupKnockout) {
675 auto driver = pdfium::WrapUnique(
676 new CFX_SkiaDeviceDriver(std::move(pBitmap), bRgbByteOrder,
677 std::move(pBackdropBitmap), bGroupKnockout));
678 if (!driver->m_pCanvas) {
679 return nullptr;
680 }
681
682 return driver;
683}
684
685// static
686std::unique_ptr<CFX_SkiaDeviceDriver> CFX_SkiaDeviceDriver::Create(
687 SkCanvas& canvas) {
688 auto driver = pdfium::WrapUnique(new CFX_SkiaDeviceDriver(canvas));
689 if (!driver->m_pBitmap || !driver->m_pBackdropBitmap) {
690 return nullptr;
691 }
692
693 return driver;
694}
695
696CFX_SkiaDeviceDriver::CFX_SkiaDeviceDriver(
697 RetainPtr<CFX_DIBitmap> pBitmap,
698 bool bRgbByteOrder,
699 RetainPtr<CFX_DIBitmap> pBackdropBitmap,
700 bool bGroupKnockout)
701 : m_pBitmap(std::move(pBitmap)),
702 m_pBackdropBitmap(pBackdropBitmap),
703 m_bRgbByteOrder(bRgbByteOrder),
704 m_bGroupKnockout(bGroupKnockout) {
705 SkColorType color_type;
706 const int bpp = m_pBitmap->GetBPP();
707 SkAlphaType alpha_type = kPremul_SkAlphaType;
708 if (bpp == 8) {
709 color_type = m_pBitmap->IsAlphaFormat() || m_pBitmap->IsMaskFormat()
710 ? kAlpha_8_SkColorType
711 : kGray_8_SkColorType;
712 } else if (bpp == 24) {
713 DCHECK_EQ(m_pBitmap->GetFormat(), FXDIB_Format::kBgr);
714
715 // Save the input bitmap as `m_pOriginalBitmap` and save its 32 bpp
716 // equivalent at `m_pBitmap` for Skia's internal process.
717 m_pOriginalBitmap = std::move(m_pBitmap);
718 const int width = m_pOriginalBitmap->GetWidth();
719 const int height = m_pOriginalBitmap->GetHeight();
720
721 m_pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
722 if (!m_pBitmap->Create(width, height, FXDIB_Format::kBgraPremul) ||
723 !m_pBitmap->TransferBitmap(width, height, m_pOriginalBitmap,
724 /*src_left=*/0, /*src_top=*/0)) {
725 // Skip creating SkCanvas if the 32-bpp bitmap creation fails.
726 // CFX_SkiaDeviceDriver::Create() will check for the missing `m_pCanvas`
727 // and not use `this`.
728 // Also reset `m_pOriginalBitmap` so the dtor does not try to transfer
729 // `m_pBitmap` back to `m_pOriginalBitmap`.
730 m_pOriginalBitmap.Reset();
731 return;
732 }
733
734 color_type = Get32BitSkColorType(bRgbByteOrder);
735 } else {
736 DCHECK_EQ(bpp, 32);
737 color_type = Get32BitSkColorType(bRgbByteOrder);
738 FXDIB_Format format = m_pBitmap->GetFormat();
739 if (format == FXDIB_Format::kBgrx) {
740 alpha_type = kOpaque_SkAlphaType;
741 } else if (format == FXDIB_Format::kBgra) {
742 alpha_type = kUnpremul_SkAlphaType;
743 }
744 }
745
746 SkImageInfo imageInfo = SkImageInfo::Make(
747 m_pBitmap->GetWidth(), m_pBitmap->GetHeight(), color_type, alpha_type);
748 surface_ = SkSurfaces::WrapPixels(
749 imageInfo, m_pBitmap->GetWritableBuffer().data(), m_pBitmap->GetPitch());
750 m_pCanvas = surface_->getCanvas();
751}
752
753CFX_SkiaDeviceDriver::CFX_SkiaDeviceDriver(SkCanvas& canvas)
754 : m_pCanvas(&canvas), m_bGroupKnockout(false) {
755 int width = m_pCanvas->imageInfo().width();
756 int height = m_pCanvas->imageInfo().height();
757 DCHECK_EQ(kUnknown_SkColorType, m_pCanvas->imageInfo().colorType());
758
759 constexpr uint32_t kMagenta = 0xffff00ff;
760 constexpr uint32_t kGreen = 0xff00ff00;
761 m_pBitmap = MakeDebugBitmap(width, height, kMagenta);
762 m_pBackdropBitmap = MakeDebugBitmap(width, height, kGreen);
763}
764
765CFX_SkiaDeviceDriver::~CFX_SkiaDeviceDriver() {
766 // Convert and transfer the internal processed result to the original 24 bpp
767 // bitmap provided by the render device if needed.
769}
770
771bool CFX_SkiaDeviceDriver::DrawDeviceText(
772 pdfium::span<const TextCharPos> pCharPos,
773 CFX_Font* pFont,
774 const CFX_Matrix& mtObject2Device,
775 float font_size,
776 uint32_t color,
777 const CFX_TextRenderOptions& options) {
778 // `SkTextBlob` is built from `pFont`'s font data. If `pFont` doesn't contain
779 // any font data, each text blob will have zero area to be drawn and the
780 // drawing command will be rejected. In this case, we fall back to drawing
781 // characters by their glyph bitmaps.
782 if (pFont->GetFontSpan().empty())
783 return false;
784
785 if (TryDrawText(pCharPos, pFont, mtObject2Device, font_size, color,
786 options)) {
787 return true;
788 }
789 sk_sp<SkTypeface> typeface(SkSafeRef(pFont->GetDeviceCache()));
790 SkPaint paint;
791 paint.setAntiAlias(true);
792 paint.setColor(color);
793
794 SkFont font;
795 font.setTypeface(typeface);
796 font.setEmbolden(pFont->IsSubstFontBold());
797 font.setHinting(SkFontHinting::kNone);
798 font.setSize(SkTAbs(font_size));
799 font.setSubpixel(true);
800 font.setSkewX(tanf(pFont->GetSubstFontItalicAngle() * FXSYS_PI / 180.0));
801 font.setEdging(GetFontEdgingType(options));
802
803 SkAutoCanvasRestore scoped_save_restore(m_pCanvas, /*doSave=*/true);
804 const SkScalar horizontal_flip = font_size < 0 ? -1 : 1;
805 const SkScalar vertical_flip = pFont->IsVertical() ? -1 : 1;
806 SkMatrix skMatrix = ToFlippedSkMatrix(mtObject2Device, horizontal_flip);
807 m_pCanvas->concat(skMatrix);
808 DataVector<SkPoint> positions(pCharPos.size());
809 DataVector<uint16_t> glyphs(pCharPos.size());
810
811 for (size_t index = 0; index < pCharPos.size(); ++index) {
812 const TextCharPos& cp = pCharPos[index];
813 positions[index] = {cp.m_Origin.x * horizontal_flip,
814 cp.m_Origin.y * vertical_flip};
815 glyphs[index] = static_cast<uint16_t>(cp.m_GlyphIndex);
816#if BUILDFLAG(IS_APPLE)
817 if (cp.m_ExtGID)
818 glyphs[index] = static_cast<uint16_t>(cp.m_ExtGID);
819#endif
820 }
821
822 for (size_t index = 0; index < pCharPos.size(); ++index) {
823 const TextCharPos& cp = pCharPos[index];
824 if (cp.m_bGlyphAdjust) {
825 if (0 == cp.m_AdjustMatrix[1] && 0 == cp.m_AdjustMatrix[2] &&
826 1 == cp.m_AdjustMatrix[3]) {
827 font.setScaleX(cp.m_AdjustMatrix[0]);
828 auto blob =
829 SkTextBlob::MakeFromText(&glyphs[index], sizeof(glyphs[index]),
830 font, SkTextEncoding::kGlyphID);
831 m_pCanvas->drawTextBlob(blob, positions[index].fX, positions[index].fY,
832 paint);
833 font.setScaleX(SkIntToScalar(1));
834 } else {
835 SkAutoCanvasRestore scoped_save_restore2(m_pCanvas, /*doSave=*/true);
836 SkMatrix adjust;
837 adjust.preTranslate(positions[index].fX, -positions[index].fY);
838 adjust.setScaleX(cp.m_AdjustMatrix[0]);
839 adjust.setSkewX(cp.m_AdjustMatrix[1]);
840 adjust.setSkewY(cp.m_AdjustMatrix[2]);
841 adjust.setScaleY(cp.m_AdjustMatrix[3]);
842 m_pCanvas->concat(adjust);
843 auto blob =
844 SkTextBlob::MakeFromText(&glyphs[index], sizeof(glyphs[index]),
845 font, SkTextEncoding::kGlyphID);
846 m_pCanvas->drawTextBlob(blob, 0, 0, paint);
847 }
848 } else {
849 auto blob =
850 SkTextBlob::MakeFromText(&glyphs[index], sizeof(glyphs[index]), font,
851 SkTextEncoding::kGlyphID);
852 m_pCanvas->drawTextBlob(blob, positions[index].fX, positions[index].fY,
853 paint);
854 }
855 }
856 return true;
857}
858
859// TODO(crbug.com/pdfium/1999): Merge with `DrawDeviceText()` and refactor
860// common logic.
861// TODO(crbug.com/pdfium/1774): Sometimes the thickness of the glyphs is not
862// ideal. Improve text rendering results regarding different font weight.
863bool CFX_SkiaDeviceDriver::TryDrawText(pdfium::span<const TextCharPos> char_pos,
864 const CFX_Font* pFont,
865 const CFX_Matrix& matrix,
866 float font_size,
867 uint32_t color,
868 const CFX_TextRenderOptions& options) {
869 float scaleX = 1;
870 bool oneAtATime = false;
871 bool hasRSX = HasRSX(char_pos, &scaleX, &oneAtATime);
872 if (oneAtATime) {
873 return false;
874 }
875
876 m_charDetails.SetCount(char_pos.size());
877 if (hasRSX) {
878 m_rsxform.resize(char_pos.size());
879 } else {
880 m_rsxform.resize(0);
881 }
882
883 const SkScalar horizontal_flip = font_size < 0 ? -1 : 1;
884 const SkScalar vertical_flip = pFont->IsVertical() ? -1 : 1;
885 for (size_t index = 0; index < char_pos.size(); ++index) {
886 const TextCharPos& cp = char_pos[index];
887 m_charDetails.SetPositionAt(index, {cp.m_Origin.x * horizontal_flip,
888 cp.m_Origin.y * vertical_flip});
889 m_charDetails.SetGlyphAt(index, static_cast<uint16_t>(cp.m_GlyphIndex));
890 m_charDetails.SetFontCharWidthAt(index, cp.m_FontCharWidth);
891#if BUILDFLAG(IS_APPLE)
892 if (cp.m_ExtGID) {
893 m_charDetails.SetGlyphAt(index, static_cast<uint16_t>(cp.m_ExtGID));
894 }
895#endif
896 }
897 if (hasRSX) {
898 const DataVector<SkPoint>& positions = m_charDetails.GetPositions();
899 for (size_t index = 0; index < char_pos.size(); ++index) {
900 const TextCharPos& cp = char_pos[index];
901 SkRSXform& rsxform = m_rsxform[index];
902 if (cp.m_bGlyphAdjust) {
903 rsxform.fSCos = cp.m_AdjustMatrix[0];
904 rsxform.fSSin = cp.m_AdjustMatrix[1];
905 rsxform.fTx = cp.m_AdjustMatrix[0] * positions[index].fX;
906 rsxform.fTy = -cp.m_AdjustMatrix[3] * positions[index].fY;
907 } else {
908 rsxform.fSCos = 1;
909 rsxform.fSSin = 0;
910 rsxform.fTx = positions[index].fX;
911 rsxform.fTy = positions[index].fY;
912 }
913 }
914 }
915
916 SkPaint skPaint;
917 skPaint.setAntiAlias(true);
918 skPaint.setColor(color);
919
920 SkFont font;
921 if (pFont->GetFaceRec()) { // exclude placeholder test fonts
922 font.setTypeface(sk_ref_sp(pFont->GetDeviceCache()));
923 }
924 font.setEmbolden(pFont->IsSubstFontBold());
925 font.setHinting(SkFontHinting::kNone);
926 font.setScaleX(scaleX);
927 font.setSkewX(tanf(pFont->GetSubstFontItalicAngle() * FXSYS_PI / 180.0));
928 font.setSize(SkTAbs(font_size));
929 font.setSubpixel(true);
930 font.setEdging(GetFontEdgingType(options));
931
932 SkAutoCanvasRestore scoped_save_restore(m_pCanvas, /*doSave=*/true);
933 m_pCanvas->concat(ToFlippedSkMatrix(matrix, horizontal_flip));
934
935 const DataVector<uint16_t>& glyphs = m_charDetails.GetGlyphs();
936 if (!m_rsxform.empty()) {
937 sk_sp<SkTextBlob> blob = SkTextBlob::MakeFromRSXform(
938 glyphs.data(), glyphs.size() * sizeof(uint16_t), m_rsxform.data(), font,
939 SkTextEncoding::kGlyphID);
940 m_pCanvas->drawTextBlob(blob, 0, 0, skPaint);
941 return true;
942 }
943 const DataVector<SkPoint>& positions = m_charDetails.GetPositions();
944 const DataVector<uint32_t>& widths = m_charDetails.GetFontCharWidths();
945 for (size_t i = 0; i < m_charDetails.Count(); ++i) {
946 const uint32_t font_glyph_width = pFont->GetGlyphWidth(glyphs[i]);
947 const uint32_t pdf_glyph_width = widths[i];
948 if (pdf_glyph_width > 0 && font_glyph_width > 0) {
949 // Scale the glyph from its default width `pdf_glyph_width` to the
950 // targeted width `pdf_glyph_width`.
951 font.setScaleX(scaleX * SkIntToScalar(pdf_glyph_width) /
952 font_glyph_width);
953 } else {
954 font.setScaleX(scaleX);
955 }
956 auto blob =
957 SkTextBlob::MakeFromPosText(&glyphs[i], sizeof(uint16_t), &positions[i],
958 font, SkTextEncoding::kGlyphID);
959 m_pCanvas->drawTextBlob(blob, 0, 0, skPaint);
960 }
961 return true;
962}
963
964int CFX_SkiaDeviceDriver::GetDriverType() const {
965 return 1;
966}
967
968bool CFX_SkiaDeviceDriver::MultiplyAlpha(float alpha) {
969 CHECK_GE(alpha, 0.0f);
970 CHECK_LE(alpha, 1.0f);
971
972 if (alpha != 1.0f) {
973 SkPaint paint;
974 paint.setAlphaf(alpha);
975 paint.setBlendMode(SkBlendMode::kDstIn);
976 m_pCanvas->drawPaint(paint);
977 }
978 return true;
979}
980
981bool CFX_SkiaDeviceDriver::MultiplyAlphaMask(
982 RetainPtr<const CFX_DIBitmap> mask) {
983 CHECK_EQ(FXDIB_Format::k8bppMask, mask->GetFormat());
984
985 sk_sp<SkImage> skia_mask = mask->RealizeSkImage();
986 if (!skia_mask) {
987 return false;
988 }
989 DCHECK_EQ(skia_mask->colorType(), kAlpha_8_SkColorType);
990
991 SkPaint paint;
992 paint.setBlendMode(SkBlendMode::kDstIn);
993 m_pCanvas->drawImageRect(skia_mask,
994 SkRect::Make(m_pCanvas->imageInfo().bounds()),
995 SkSamplingOptions(), &paint);
996 return true;
997}
998
999DeviceType CFX_SkiaDeviceDriver::GetDeviceType() const {
1000 return DeviceType::kDisplay;
1001}
1002
1003int CFX_SkiaDeviceDriver::GetDeviceCaps(int caps_id) const {
1004 switch (caps_id) {
1005 case FXDC_PIXEL_WIDTH:
1006 return m_pCanvas->imageInfo().width();
1007 case FXDC_PIXEL_HEIGHT:
1008 return m_pCanvas->imageInfo().height();
1009 case FXDC_BITS_PIXEL:
1010 return 32;
1011 case FXDC_HORZ_SIZE:
1012 case FXDC_VERT_SIZE:
1013 return 0;
1014 case FXDC_RENDER_CAPS:
1017 FXRC_FILLSTROKE_PATH | FXRC_SHADING | FXRC_PREMULTIPLIED_ALPHA;
1018 default:
1020 }
1021}
1022
1023void CFX_SkiaDeviceDriver::SaveState() {
1024 m_pCanvas->save();
1025}
1026
1027void CFX_SkiaDeviceDriver::RestoreState(bool bKeepSaved) {
1028 m_pCanvas->restore();
1029 if (bKeepSaved) {
1030 m_pCanvas->save();
1031 }
1032}
1033
1034bool CFX_SkiaDeviceDriver::SetClip_PathFill(
1035 const CFX_Path& path, // path info
1036 const CFX_Matrix* pObject2Device, // flips object's y-axis
1037 const CFX_FillRenderOptions& fill_options) {
1038 m_FillOptions = fill_options;
1039 const CFX_Matrix& deviceMatrix =
1040 pObject2Device ? *pObject2Device : CFX_Matrix();
1041
1042 SkPath skClipPath;
1043 if (path.GetPoints().size() == 5 || path.GetPoints().size() == 4) {
1044 std::optional<CFX_FloatRect> maybe_rectf = path.GetRect(&deviceMatrix);
1045 if (maybe_rectf.has_value()) {
1046 CFX_FloatRect& rectf = maybe_rectf.value();
1050 FX_RECT outer = rectf.GetOuterRect();
1051 // note that PDF's y-axis goes up; Skia's y-axis goes down
1052 skClipPath.addRect({(float)outer.left, (float)outer.bottom,
1053 (float)outer.right, (float)outer.top});
1054 }
1055 }
1056 if (skClipPath.isEmpty()) {
1057 skClipPath = BuildPath(path);
1058 skClipPath.setFillType(GetAlternateOrWindingFillType(fill_options));
1059 skClipPath.transform(ToSkMatrix(deviceMatrix));
1060 DebugShowSkiaPath(skClipPath);
1061 }
1062 m_pCanvas->clipPath(skClipPath, SkClipOp::kIntersect, true);
1063 DebugShowCanvasClip(this, m_pCanvas);
1064 return true;
1065}
1066
1067bool CFX_SkiaDeviceDriver::SetClip_PathStroke(
1068 const CFX_Path& path, // path info
1069 const CFX_Matrix* pObject2Device, // required transformation
1070 const CFX_GraphStateData* pGraphState // graphic state, for pen attributes
1071) {
1072 SkPath skPath = BuildPath(path);
1073 SkMatrix skMatrix = ToSkMatrix(*pObject2Device);
1074 SkPaint skPaint;
1075 PaintStroke(&skPaint, pGraphState, skMatrix, CFX_FillRenderOptions());
1076 SkPath dst_path;
1077 skpathutils::FillPathWithPaint(skPath, skPaint, &dst_path);
1078 dst_path.transform(skMatrix);
1079 m_pCanvas->clipPath(dst_path, SkClipOp::kIntersect, true);
1080 DebugShowCanvasClip(this, m_pCanvas);
1081 return true;
1082}
1083
1084bool CFX_SkiaDeviceDriver::DrawPath(const CFX_Path& path,
1085 const CFX_Matrix* pObject2Device,
1086 const CFX_GraphStateData* pGraphState,
1087 uint32_t fill_color,
1088 uint32_t stroke_color,
1089 const CFX_FillRenderOptions& fill_options) {
1090 m_FillOptions = fill_options;
1091
1092 SkPath skia_path = BuildPath(path);
1093 skia_path.setFillType(GetAlternateOrWindingFillType(fill_options));
1094
1095 SkMatrix skMatrix = pObject2Device ? ToSkMatrix(*pObject2Device) : SkMatrix();
1096 SkPaint skPaint;
1097 skPaint.setAntiAlias(!fill_options.aliased_path);
1098 if (fill_options.full_cover) {
1099 skPaint.setBlendMode(SkBlendMode::kPlus);
1100 }
1101 int stroke_alpha = FXARGB_A(stroke_color);
1102 if (stroke_alpha) {
1103 const CFX_GraphStateData& graph_state =
1104 pGraphState ? *pGraphState : CFX_GraphStateData();
1105 PaintStroke(&skPaint, &graph_state, skMatrix, fill_options);
1106 }
1107
1108 SkAutoCanvasRestore scoped_save_restore(m_pCanvas, /*doSave=*/true);
1109 m_pCanvas->concat(skMatrix);
1110 bool do_stroke = true;
1112 fill_color) {
1113 SkPath strokePath;
1114 const SkPath* fillPath = &skia_path;
1115 if (stroke_alpha) {
1116 if (m_bGroupKnockout) {
1117 skpathutils::FillPathWithPaint(skia_path, skPaint, &strokePath);
1118 if (stroke_color == fill_color &&
1119 Op(skia_path, strokePath, SkPathOp::kUnion_SkPathOp, &strokePath)) {
1120 fillPath = &strokePath;
1121 do_stroke = false;
1122 } else if (Op(skia_path, strokePath, SkPathOp::kDifference_SkPathOp,
1123 &strokePath)) {
1124 fillPath = &strokePath;
1125 }
1126 }
1127 }
1128 skPaint.setStyle(SkPaint::kFill_Style);
1129 skPaint.setColor(fill_color);
1130 DrawPathImpl(*fillPath, skPaint);
1131 }
1132 if (stroke_alpha && do_stroke) {
1133 skPaint.setStyle(SkPaint::kStroke_Style);
1134 skPaint.setColor(stroke_color);
1135 if (!skia_path.isLastContourClosed() && IsPathAPoint(skia_path)) {
1136 DCHECK_GE(skia_path.countPoints(), 1);
1137 m_pCanvas->drawPoint(skia_path.getPoint(0), skPaint);
1138 } else if (IsPathAPoint(skia_path) &&
1139 skPaint.getStrokeCap() != SkPaint::kRound_Cap) {
1140 // Do nothing. A closed 0-length closed path can be rendered only if
1141 // its line cap type is round.
1142 } else {
1143 DrawPathImpl(skia_path, skPaint);
1144 }
1145 }
1146 return true;
1147}
1148
1149bool CFX_SkiaDeviceDriver::FillRect(const FX_RECT& rect, uint32_t fill_color) {
1150 SkPaint spaint;
1151 spaint.setAntiAlias(true);
1152 spaint.setColor(fill_color);
1153 SkRect srect = SkRect::MakeLTRB(rect.left, std::min(rect.top, rect.bottom),
1154 rect.right, std::max(rect.bottom, rect.top));
1155 DebugShowSkiaDrawRect(this, m_pCanvas, spaint, srect);
1156 m_pCanvas->drawRect(srect, spaint);
1157 return true;
1158}
1159
1160bool CFX_SkiaDeviceDriver::DrawShading(const CPDF_ShadingPattern& pattern,
1161 const CFX_Matrix& matrix,
1162 const FX_RECT& clip_rect,
1163 int alpha) {
1164 const ShadingType shading_type = pattern.GetShadingType();
1165 if (shading_type != kAxialShading && shading_type != kRadialShading &&
1166 shading_type != kCoonsPatchMeshShading) {
1167 // TODO(caryclark) more types
1168 return false;
1169 }
1170 CPDF_ColorSpace::Family cs_family = pattern.GetCS()->GetFamily();
1171 if (CPDF_ColorSpace::Family::kDeviceRGB != cs_family &&
1173 return false;
1174 }
1175 const std::vector<std::unique_ptr<CPDF_Function>>& pFuncs =
1176 pattern.GetFuncs();
1177 size_t nFuncs = pFuncs.size();
1178 if (nFuncs > 1) // TODO(caryclark) remove this restriction
1179 return false;
1180 RetainPtr<const CPDF_Dictionary> pDict =
1181 pattern.GetShadingObject()->GetDict();
1182 RetainPtr<const CPDF_Array> pCoords = pDict->GetArrayFor("Coords");
1183 if (!pCoords && shading_type != kCoonsPatchMeshShading) {
1184 return false;
1185 }
1186 // TODO(caryclark) Respect Domain[0], Domain[1]. (Don't know what they do
1187 // yet.)
1188 DataVector<SkColor> sk_colors;
1189 DataVector<SkScalar> sk_pos;
1190 for (size_t j = 0; j < nFuncs; j++) {
1191 if (!pFuncs[j])
1192 continue;
1193
1194 if (const CPDF_SampledFunc* pSampledFunc = pFuncs[j]->ToSampledFunc()) {
1195 /* TODO(caryclark)
1196 Type 0 Sampled Functions in PostScript can also have an Order integer
1197 in the dictionary. PDFium doesn't appear to check for this anywhere.
1198 */
1199 if (!AddSamples(pSampledFunc, sk_colors, sk_pos)) {
1200 return false;
1201 }
1202 } else if (const CPDF_ExpIntFunc* pExpIntFuc = pFuncs[j]->ToExpIntFunc()) {
1203 if (!AddColors(pExpIntFuc, sk_colors, /*is_encode_reversed=*/false)) {
1204 return false;
1205 }
1206 sk_pos.push_back(0);
1207 sk_pos.push_back(1);
1208 } else if (const CPDF_StitchFunc* pStitchFunc = pFuncs[j]->ToStitchFunc()) {
1209 if (!AddStitching(pStitchFunc, sk_colors, sk_pos)) {
1210 return false;
1211 }
1212 } else {
1213 return false;
1214 }
1215 }
1216 RetainPtr<const CPDF_Array> pArray = pDict->GetArrayFor("Extend");
1217 bool clipStart = !pArray || !pArray->GetIntegerAt(0);
1218 bool clipEnd = !pArray || !pArray->GetIntegerAt(1);
1219 SkPaint paint;
1220 paint.setAntiAlias(true);
1221 paint.setAlpha(alpha);
1222 SkMatrix skMatrix = ToSkMatrix(matrix);
1223 SkRect skRect = SkRect::MakeLTRB(clip_rect.left, clip_rect.top,
1224 clip_rect.right, clip_rect.bottom);
1225 SkPath skClip;
1226 SkPath skPath;
1227 if (shading_type == kAxialShading) {
1228 float start_x = pCoords->GetFloatAt(0);
1229 float start_y = pCoords->GetFloatAt(1);
1230 float end_x = pCoords->GetFloatAt(2);
1231 float end_y = pCoords->GetFloatAt(3);
1232 SkPoint pts[] = {{start_x, start_y}, {end_x, end_y}};
1233 skMatrix.mapPoints(pts, std::size(pts));
1234 paint.setShader(SkGradientShader::MakeLinear(
1235 pts, sk_colors.data(), sk_pos.data(),
1236 fxcrt::CollectionSize<int>(sk_colors), SkTileMode::kClamp));
1237 if (clipStart || clipEnd) {
1238 // if the gradient is horizontal or vertical, modify the draw rectangle
1239 if (pts[0].fX == pts[1].fX) { // vertical
1240 if (pts[0].fY > pts[1].fY) {
1241 std::swap(pts[0].fY, pts[1].fY);
1242 std::swap(clipStart, clipEnd);
1243 }
1244 if (clipStart)
1245 skRect.fTop = std::max(skRect.fTop, pts[0].fY);
1246 if (clipEnd)
1247 skRect.fBottom = std::min(skRect.fBottom, pts[1].fY);
1248 } else if (pts[0].fY == pts[1].fY) { // horizontal
1249 if (pts[0].fX > pts[1].fX) {
1250 std::swap(pts[0].fX, pts[1].fX);
1251 std::swap(clipStart, clipEnd);
1252 }
1253 if (clipStart)
1254 skRect.fLeft = std::max(skRect.fLeft, pts[0].fX);
1255 if (clipEnd)
1256 skRect.fRight = std::min(skRect.fRight, pts[1].fX);
1257 } else { // if the gradient is angled and contained by the rect, clip
1258 SkPoint rectPts[4] = {{skRect.fLeft, skRect.fTop},
1259 {skRect.fRight, skRect.fTop},
1260 {skRect.fRight, skRect.fBottom},
1261 {skRect.fLeft, skRect.fBottom}};
1262 ClipAngledGradient(pts, rectPts, clipStart, clipEnd, &skClip);
1263 }
1264 }
1265 skPath.addRect(skRect);
1266 skMatrix.setIdentity();
1267 } else if (shading_type == kRadialShading) {
1268 float start_x = pCoords->GetFloatAt(0);
1269 float start_y = pCoords->GetFloatAt(1);
1270 float start_r = pCoords->GetFloatAt(2);
1271 float end_x = pCoords->GetFloatAt(3);
1272 float end_y = pCoords->GetFloatAt(4);
1273 float end_r = pCoords->GetFloatAt(5);
1274 SkPoint pts[] = {{start_x, start_y}, {end_x, end_y}};
1275
1276 paint.setShader(SkGradientShader::MakeTwoPointConical(
1277 pts[0], start_r, pts[1], end_r, sk_colors.data(), sk_pos.data(),
1278 fxcrt::CollectionSize<int>(sk_colors), SkTileMode::kClamp));
1279 if (clipStart || clipEnd) {
1280 if (clipStart && start_r)
1281 skClip.addCircle(pts[0].fX, pts[0].fY, start_r);
1282 if (clipEnd)
1283 skClip.addCircle(pts[1].fX, pts[1].fY, end_r, SkPathDirection::kCCW);
1284 else
1285 skClip.setFillType(SkPathFillType::kInverseWinding);
1286 skClip.transform(skMatrix);
1287 }
1288 SkMatrix inverse;
1289 if (!skMatrix.invert(&inverse))
1290 return false;
1291 skPath.addRect(skRect);
1292 skPath.transform(inverse);
1293 } else {
1294 CHECK_EQ(kCoonsPatchMeshShading, shading_type);
1295 RetainPtr<const CPDF_Stream> pStream = ToStream(pattern.GetShadingObject());
1296 if (!pStream)
1297 return false;
1298 CPDF_MeshStream stream(shading_type, pattern.GetFuncs(), std::move(pStream),
1299 pattern.GetCS());
1300 if (!stream.Load())
1301 return false;
1302 std::array<SkPoint, 12> cubics;
1303 std::array<SkColor, 4> colors;
1304 SkAutoCanvasRestore scoped_save_restore(m_pCanvas, /*doSave=*/true);
1305 if (!skClip.isEmpty())
1306 m_pCanvas->clipPath(skClip, SkClipOp::kIntersect, true);
1307 m_pCanvas->concat(skMatrix);
1308 while (!stream.IsEOF()) {
1309 const uint32_t flag = stream.ReadFlag();
1310 if (flag) {
1311 std::array<SkPoint, 4> temp_cubics;
1312 for (size_t i = 0; i < temp_cubics.size(); ++i) {
1313 temp_cubics[i] = cubics[(flag * 3 + i) % cubics.size()];
1314 }
1315 fxcrt::Copy(temp_cubics, cubics);
1316 SkColor temp_colors[2] = {colors[flag % 4],
1317 colors[(flag + 1) % colors.size()]};
1318 fxcrt::Copy(temp_colors, colors);
1319 }
1320 const size_t start_point = flag ? 4 : 0;
1321 for (size_t i = start_point; i < cubics.size(); ++i) {
1322 CFX_PointF point = stream.ReadCoords();
1323 cubics[i].fX = point.x;
1324 cubics[i].fY = point.y;
1325 }
1326 const size_t start_color = flag ? 2 : 0;
1327 for (size_t i = start_color; i < colors.size(); ++i) {
1328 FX_RGB_STRUCT<float> rgb = stream.ReadColor();
1329 colors[i] =
1330 SkColorSetARGB(0xFF, (U8CPU)(rgb.red * 255),
1331 (U8CPU)(rgb.green * 255), (U8CPU)(rgb.blue * 255));
1332 }
1333 m_pCanvas->drawPatch(cubics.data(), colors.data(), /*texCoords=*/nullptr,
1334 SkBlendMode::kDst, paint);
1335 }
1336 return true;
1337 }
1338 SkAutoCanvasRestore scoped_save_restore(m_pCanvas, /*doSave=*/true);
1339 if (!skClip.isEmpty())
1340 m_pCanvas->clipPath(skClip, SkClipOp::kIntersect, true);
1341 m_pCanvas->concat(skMatrix);
1342 DrawPathImpl(skPath, paint);
1343 return true;
1344}
1345
1346FX_RECT CFX_SkiaDeviceDriver::GetClipBox() const {
1347 SkIRect clip = m_pCanvas->getDeviceClipBounds();
1348 return FX_RECT(clip.fLeft, clip.fTop, clip.fRight, clip.fBottom);
1349}
1350
1351bool CFX_SkiaDeviceDriver::GetDIBits(RetainPtr<CFX_DIBitmap> bitmap,
1352 int left,
1353 int top) const {
1354 const uint8_t* input_buffer = m_pBitmap->GetBuffer().data();
1355 if (!input_buffer) {
1356 return true;
1357 }
1358
1359 uint8_t* output_buffer = bitmap->GetWritableBuffer().data();
1360 DCHECK(output_buffer);
1361
1362 SkImageInfo input_info = m_pCanvas->imageInfo();
1363 sk_sp<SkImage> input = SkImages::RasterFromPixmap(
1364 SkPixmap(input_info, input_buffer, m_pBitmap->GetPitch()),
1365 /*rasterReleaseProc=*/nullptr, /*releaseContext=*/nullptr);
1366
1367 CHECK_EQ(32, bitmap->GetBPP());
1368 SkImageInfo output_info = SkImageInfo::Make(
1369 bitmap->GetWidth(), bitmap->GetHeight(),
1370 Get32BitSkColorType(m_bRgbByteOrder),
1371 bitmap->IsPremultiplied() ? kPremul_SkAlphaType : kUnpremul_SkAlphaType);
1372 sk_sp<SkSurface> output =
1373 SkSurfaces::WrapPixels(output_info, output_buffer, bitmap->GetPitch());
1374
1375 output->getCanvas()->drawImage(input, left, top, SkSamplingOptions());
1376 return true;
1377}
1378
1379RetainPtr<const CFX_DIBitmap> CFX_SkiaDeviceDriver::GetBackDrop() const {
1380 return m_pBackdropBitmap;
1381}
1382
1383bool CFX_SkiaDeviceDriver::SetDIBits(RetainPtr<const CFX_DIBBase> bitmap,
1384 uint32_t color,
1385 const FX_RECT& src_rect,
1386 int left,
1387 int top,
1388 BlendMode blend_type) {
1389 if (m_pBitmap->GetBuffer().empty()) {
1390 return true;
1391 }
1392
1394 bitmap->GetWidth(), bitmap->GetHeight(), left, top);
1395
1396 // `bNoSmoothing` prevents linear sampling when rendering bitmaps.
1397 FXDIB_ResampleOptions sampling_options;
1398 sampling_options.bNoSmoothing = true;
1399
1400 return StartDIBitsSkia(std::move(bitmap), src_rect, /*alpha=*/1.0f, color,
1401 matrix, sampling_options, blend_type);
1402}
1403
1404bool CFX_SkiaDeviceDriver::StretchDIBits(RetainPtr<const CFX_DIBBase> bitmap,
1405 uint32_t color,
1406 int dest_left,
1407 int dest_top,
1408 int dest_width,
1409 int dest_height,
1410 const FX_RECT* pClipRect,
1411 const FXDIB_ResampleOptions& options,
1412 BlendMode blend_type) {
1413 if (m_pBitmap->GetBuffer().empty())
1414 return true;
1415
1416 CFX_Matrix matrix = CFX_RenderDevice::GetFlipMatrix(dest_width, dest_height,
1417 dest_left, dest_top);
1418 SkAutoCanvasRestore scoped_save_restore(m_pCanvas, /*doSave=*/true);
1419 SkRect skClipRect = SkRect::MakeLTRB(pClipRect->left, pClipRect->bottom,
1420 pClipRect->right, pClipRect->top);
1421 m_pCanvas->clipRect(skClipRect, SkClipOp::kIntersect, true);
1422
1423 // `bNoSmoothing` prevents linear sampling when rendering bitmaps.
1424 FXDIB_ResampleOptions sampling_options;
1425 sampling_options.bNoSmoothing = true;
1426
1427 FX_RECT rect(0, 0, bitmap->GetWidth(), bitmap->GetHeight());
1428 return StartDIBitsSkia(std::move(bitmap), rect, /*alpha=*/1.0f, color, matrix,
1429 sampling_options, blend_type);
1430}
1431
1433 RetainPtr<const CFX_DIBBase> bitmap,
1434 float alpha,
1435 uint32_t color,
1436 const CFX_Matrix& matrix,
1437 const FXDIB_ResampleOptions& options,
1438 BlendMode blend_type) {
1439 FX_RECT rect(0, 0, bitmap->GetWidth(), bitmap->GetHeight());
1440 bool success = StartDIBitsSkia(std::move(bitmap), rect, alpha, color, matrix,
1441 options, blend_type);
1442 return {success ? Result::kSuccess : Result::kFailure, nullptr};
1443}
1444
1445void CFX_DIBitmap::PreMultiply() {
1446 CHECK(CFX_DefaultRenderDevice::UseSkiaRenderer());
1447 if (GetFormat() != FXDIB_Format::kBgra) {
1448 return;
1449 }
1450
1451 void* buffer = GetWritableBuffer().data();
1452 if (!buffer) {
1453 return;
1454 }
1455
1456 SetFormat(FXDIB_Format::kBgraPremul);
1457 int height = GetHeight();
1458 int width = GetWidth();
1459 int row_bytes = GetPitch();
1460 SkImageInfo premultiplied_info =
1461 SkImageInfo::Make(width, height, kN32_SkColorType, kPremul_SkAlphaType);
1462 SkPixmap premultiplied(premultiplied_info, buffer, row_bytes);
1463 SkImageInfo unpremultiplied_info =
1464 SkImageInfo::Make(width, height, kN32_SkColorType, kUnpremul_SkAlphaType);
1465 SkPixmap unpremultiplied(unpremultiplied_info, buffer, row_bytes);
1466 unpremultiplied.readPixels(premultiplied);
1467}
1468
1469void CFX_DIBitmap::UnPreMultiply() {
1470 CHECK(CFX_DefaultRenderDevice::UseSkiaRenderer());
1471 if (GetFormat() != FXDIB_Format::kBgraPremul) {
1472 return;
1473 }
1474
1475 void* buffer = GetWritableBuffer().data();
1476 if (!buffer) {
1477 return;
1478 }
1479
1481 int height = GetHeight();
1482 int width = GetWidth();
1483 int row_bytes = GetPitch();
1484 SkImageInfo premultiplied_info =
1485 SkImageInfo::Make(width, height, kN32_SkColorType, kPremul_SkAlphaType);
1486 SkPixmap premultiplied(premultiplied_info, buffer, row_bytes);
1487 SkImageInfo unpremultiplied_info =
1488 SkImageInfo::Make(width, height, kN32_SkColorType, kUnpremul_SkAlphaType);
1489 SkPixmap unpremultiplied(unpremultiplied_info, buffer, row_bytes);
1490 premultiplied.readPixels(unpremultiplied);
1491}
1492
1493bool CFX_SkiaDeviceDriver::DrawBitsWithMask(RetainPtr<const CFX_DIBBase> bitmap,
1494 RetainPtr<const CFX_DIBBase> mask,
1495 float alpha,
1496 const CFX_Matrix& matrix,
1497 BlendMode blend_type) {
1498 DebugValidate(m_pBitmap);
1499
1500 sk_sp<SkImage> skia_source = bitmap->RealizeSkImage();
1501 if (!skia_source) {
1502 return false;
1503 }
1504
1505 DCHECK(mask->IsMaskFormat());
1506 sk_sp<SkImage> skia_mask = mask->RealizeSkImage();
1507 if (!skia_mask) {
1508 return false;
1509 }
1510 DCHECK_EQ(skia_mask->colorType(), kAlpha_8_SkColorType);
1511
1512 {
1513 SkAutoCanvasRestore scoped_save_restore(m_pCanvas, /*doSave=*/true);
1514
1515 const int src_width = bitmap->GetWidth();
1516 const int src_height = bitmap->GetHeight();
1517 SkMatrix skMatrix;
1518 SetBitmapMatrix(matrix, src_width, src_height, &skMatrix);
1519 m_pCanvas->concat(skMatrix);
1520 SkPaint paint;
1521 SetBitmapPaintForMerge(bitmap->IsMaskFormat(), !m_FillOptions.aliased_path,
1522 alpha, blend_type, &paint);
1523 sk_sp<SkShader> source_shader = skia_source->makeShader(
1524 SkTileMode::kClamp, SkTileMode::kClamp, SkSamplingOptions());
1525 sk_sp<SkShader> mask_shader = skia_mask->makeShader(
1526 SkTileMode::kClamp, SkTileMode::kClamp, SkSamplingOptions());
1527 paint.setShader(SkShaders::Blend(
1528 SkBlendMode::kSrcIn, std::move(mask_shader), std::move(source_shader)));
1529 m_pCanvas->drawRect(
1530 SkRect::MakeWH(SkIntToScalar(src_width), SkIntToScalar(src_height)),
1531 paint);
1532 }
1533
1534 DebugValidate(m_pBitmap);
1535 return true;
1536}
1537
1538bool CFX_SkiaDeviceDriver::SetBitsWithMask(RetainPtr<const CFX_DIBBase> bitmap,
1539 RetainPtr<const CFX_DIBBase> mask,
1540 int dest_left,
1541 int dest_top,
1542 float alpha,
1543 BlendMode blend_type) {
1544 if (m_pBitmap->GetBuffer().empty()) {
1545 return true;
1546 }
1547
1549 bitmap->GetWidth(), bitmap->GetHeight(), dest_left, dest_top);
1550 return DrawBitsWithMask(std::move(bitmap), std::move(mask), alpha, matrix,
1551 blend_type);
1552}
1553
1554void CFX_SkiaDeviceDriver::SetGroupKnockout(bool group_knockout) {
1555 m_bGroupKnockout = group_knockout;
1556}
1557
1558void CFX_SkiaDeviceDriver::SyncInternalBitmaps() {
1559 if (!m_pOriginalBitmap) {
1560 return;
1561 }
1562
1563 int width = m_pOriginalBitmap->GetWidth();
1564 int height = m_pOriginalBitmap->GetHeight();
1565 DCHECK_EQ(width, m_pBitmap->GetWidth());
1566 DCHECK_EQ(height, m_pBitmap->GetHeight());
1567 DCHECK_EQ(FXDIB_Format::kBgr, m_pOriginalBitmap->GetFormat());
1568 m_pOriginalBitmap->TransferBitmap(width, height, m_pBitmap, /*src_left=*/0,
1569 /*src_top=*/0);
1570}
1571
1572void CFX_SkiaDeviceDriver::Clear(uint32_t color) {
1573 m_pCanvas->clear(color);
1574}
1575
1576bool CFX_SkiaDeviceDriver::StartDIBitsSkia(RetainPtr<const CFX_DIBBase> bitmap,
1577 const FX_RECT& src_rect,
1578 float alpha,
1579 uint32_t color,
1580 const CFX_Matrix& matrix,
1581 const FXDIB_ResampleOptions& options,
1582 BlendMode blend_type) {
1583 DebugValidate(m_pBitmap);
1584
1585 sk_sp<SkImage> skia_source = bitmap->RealizeSkImage();
1586 if (!skia_source) {
1587 return false;
1588 }
1589
1590 {
1591 SkAutoCanvasRestore scoped_save_restore(m_pCanvas, /*doSave=*/true);
1592
1593 const int width = bitmap->GetWidth();
1594 const int height = bitmap->GetHeight();
1595 SkMatrix skMatrix;
1596 SetBitmapMatrix(matrix, width, height, &skMatrix);
1597 m_pCanvas->concat(skMatrix);
1598 SkPaint paint;
1599 SetBitmapPaint(bitmap->IsMaskFormat(), !m_FillOptions.aliased_path, alpha,
1600 color, blend_type, &paint);
1601
1602 bool use_interpolate_bilinear = options.bInterpolateBilinear;
1603 if (!use_interpolate_bilinear) {
1604 float dest_width = ceilf(matrix.GetXUnit());
1605 float dest_height = ceilf(matrix.GetYUnit());
1606 if (pdfium::IsValueInRangeForNumericType<int>(dest_width) &&
1607 pdfium::IsValueInRangeForNumericType<int>(dest_height)) {
1608 use_interpolate_bilinear = CStretchEngine::UseInterpolateBilinear(
1609 options, static_cast<int>(dest_width),
1610 static_cast<int>(dest_height), width, height);
1611 }
1612 }
1613 SkSamplingOptions sampling_options;
1614 if (use_interpolate_bilinear) {
1615 sampling_options =
1616 SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear);
1617 }
1618
1619 m_pCanvas->drawImageRect(
1620 skia_source,
1621 SkRect::MakeLTRB(src_rect.left, src_rect.top, src_rect.right,
1622 src_rect.bottom),
1623 SkRect::MakeWH(src_rect.Width(), src_rect.Height()), sampling_options,
1624 &paint, SkCanvas::kFast_SrcRectConstraint);
1625 }
1626
1627 DebugValidate(m_pBitmap);
1628 return true;
1629}
1630
1631void CFX_SkiaDeviceDriver::DrawPathImpl(const SkPath& path,
1632 const SkPaint& paint) {
1633 DebugShowSkiaDrawPath(this, m_pCanvas, paint, path);
1634 m_pCanvas->drawPath(path, paint);
1635}
1636
1637CFX_SkiaDeviceDriver::CharDetail::CharDetail() = default;
1638CFX_SkiaDeviceDriver::CharDetail::~CharDetail() = default;
1639
1640bool CFX_DefaultRenderDevice::AttachSkiaImpl(
1641 RetainPtr<CFX_DIBitmap> pBitmap,
1642 bool bRgbByteOrder,
1643 RetainPtr<CFX_DIBitmap> pBackdropBitmap,
1644 bool bGroupKnockout) {
1645 // FPDF_FFLDrawSkia() ends up calling this method with a deliberately null
1646 // `pBitmap`.
1647 if (!pBitmap)
1648 return false;
1649 SetBitmap(pBitmap);
1650 auto driver =
1651 CFX_SkiaDeviceDriver::Create(std::move(pBitmap), bRgbByteOrder,
1652 std::move(pBackdropBitmap), bGroupKnockout);
1653 if (!driver)
1654 return false;
1655
1656 SetDeviceDriver(std::move(driver));
1657 return true;
1658}
1659
1660bool CFX_DefaultRenderDevice::AttachCanvas(SkCanvas& canvas) {
1661 auto driver = CFX_SkiaDeviceDriver::Create(canvas);
1662 if (!driver) {
1663 return false;
1664 }
1665 SetDeviceDriver(std::move(driver));
1666 return true;
1667}
1668
1669bool CFX_DefaultRenderDevice::CreateSkia(
1670 int width,
1671 int height,
1672 FXDIB_Format format,
1673 RetainPtr<CFX_DIBitmap> pBackdropBitmap) {
1674 auto pBitmap = pdfium::MakeRetain<CFX_DIBitmap>();
1675 if (!pBitmap->Create(width, height, format))
1676 return false;
1677
1678 SetBitmap(pBitmap);
1679 auto driver = CFX_SkiaDeviceDriver::Create(std::move(pBitmap), false,
1680 std::move(pBackdropBitmap), false);
1681 if (!driver)
1682 return false;
1683
1684 SetDeviceDriver(std::move(driver));
1685 return true;
1686}
#define DCHECK_IS_ON()
Definition check.h:24
#define DCHECK
Definition check.h:33
#define DCHECK_GE(x, y)
Definition check_op.h:22
#define DCHECK_LE(x, y)
Definition check_op.h:21
#define CHECK_EQ(x, y)
Definition check_op.h:10
#define CHECK_GE(x, y)
Definition check_op.h:15
#define CHECK_LE(x, y)
Definition check_op.h:14
#define DCHECK_EQ(x, y)
Definition check_op.h:17
uint32_t GetBits(uint32_t nBits)
FXDIB_Format GetFormat() const
Definition cfx_dibbase.h:69
void SetFormat(FXDIB_Format format)
uint32_t GetPitch() const
Definition cfx_dibbase.h:67
int GetHeight() const
Definition cfx_dibbase.h:66
int GetWidth() const
Definition cfx_dibbase.h:65
constexpr CFX_FloatRect(float l, float b, float r, float t)
void Intersect(const CFX_FloatRect &other_rect)
FX_RECT GetOuterRect() const
int GetSubstFontItalicAngle() const
Definition cfx_font.cpp:178
FXFT_FaceRec * GetFaceRec() const
Definition cfx_font.h:76
bool IsVertical() const
Definition cfx_font.h:108
constexpr CFX_Matrix()=default
float GetXUnit() const
float GetYUnit() const
static CFX_Matrix GetFlipMatrix(float width, float height, float left, float top)
void SetBitmap(RetainPtr< CFX_DIBitmap > bitmap)
bool DrawBitsWithMask(RetainPtr< const CFX_DIBBase > bitmap, RetainPtr< const CFX_DIBBase > mask, float alpha, const CFX_Matrix &matrix, BlendMode blend_type)
void SyncInternalBitmaps() override
bool DrawDeviceText(pdfium::span< const TextCharPos > pCharPos, CFX_Font *pFont, const CFX_Matrix &mtObject2Device, float font_size, uint32_t color, const CFX_TextRenderOptions &options) override
bool MultiplyAlpha(float alpha) override
StartResult StartDIBits(RetainPtr< const CFX_DIBBase > bitmap, float alpha, uint32_t color, const CFX_Matrix &matrix, const FXDIB_ResampleOptions &options, BlendMode blend_type) override
bool FillRect(const FX_RECT &rect, uint32_t fill_color) override
bool DrawShading(const CPDF_ShadingPattern &pattern, const CFX_Matrix &matrix, const FX_RECT &clip_rect, int alpha) override
void SetGroupKnockout(bool group_knockout) override
DeviceType GetDeviceType() const override
void SaveState() override
bool MultiplyAlphaMask(RetainPtr< const CFX_DIBitmap > mask) override
bool SetClip_PathFill(const CFX_Path &path, const CFX_Matrix *pObject2Device, const CFX_FillRenderOptions &fill_options) override
RetainPtr< const CFX_DIBitmap > GetBackDrop() const override
bool DrawPath(const CFX_Path &path, const CFX_Matrix *pObject2Device, const CFX_GraphStateData *pGraphState, uint32_t fill_color, uint32_t stroke_color, const CFX_FillRenderOptions &fill_options) override
int GetDriverType() const override
int GetDeviceCaps(int caps_id) const override
bool SetDIBits(RetainPtr< const CFX_DIBBase > bitmap, uint32_t color, const FX_RECT &src_rect, int dest_left, int dest_top, BlendMode blend_type) override
FX_RECT GetClipBox() const override
bool StretchDIBits(RetainPtr< const CFX_DIBBase > bitmap, uint32_t color, int dest_left, int dest_top, int dest_width, int dest_height, const FX_RECT *pClipRect, const FXDIB_ResampleOptions &options, BlendMode blend_type) override
void RestoreState(bool bKeepSaved) override
bool GetDIBits(RetainPtr< CFX_DIBitmap > bitmap, int left, int top) const override
bool SetClip_PathStroke(const CFX_Path &path, const CFX_Matrix *pObject2Device, const CFX_GraphStateData *pGraphState) override
bool SetBitsWithMask(RetainPtr< const CFX_DIBBase > bitmap, RetainPtr< const CFX_DIBBase > mask, int dest_left, int dest_top, float alpha, BlendMode blend_type) override
void Clear(uint32_t color)
std::vector< RetainPtr< CPDF_Object > >::const_iterator const_iterator
Definition cpdf_array.h:29
std::map< ByteString, RetainPtr< CPDF_Object >, std::less<> > DictMap
float GetExponent() const
uint32_t GetOrigOutputs() const
uint32_t InputCount() const
float GetRange(int i) const
float GetDomain(int i) const
uint32_t OutputCount() const
uint32_t ReadFlag() const
uint32_t GetBitsPerSample() const
ShadingType GetShadingType() const
static bool UseInterpolateBilinear(const FXDIB_ResampleOptions &options, int dest_width, int dest_height, int src_width, int src_height)
bool m_bGlyphAdjust
@ kRadialShading
@ kAxialShading
@ kCoonsPatchMeshShading
CFX_PTemplate< float > CFX_PointF
BlendMode
Definition fx_dib.h:119
#define FXARGB_A(argb)
Definition fx_dib.h:196
FXDIB_Format
Definition fx_dib.h:21
#define SHOW_SKIA_PATH
#define FXSYS_PI
Definition fx_system.h:44
#define NOTREACHED_NORETURN()
Definition notreached.h:22
#define CHECK(cvref)
#define FXDC_BITS_PIXEL
#define FXDC_RENDER_CAPS
#define FXDC_PIXEL_WIDTH
#define FXRC_ALPHA_OUTPUT
#define FXDC_VERT_SIZE
#define FXDC_PIXEL_HEIGHT
#define FXDC_HORZ_SIZE
#define FXRC_ALPHA_PATH
#define FXRC_BLEND_MODE
#define FXRC_ALPHA_IMAGE
#define FXRC_GET_BITS
#define FXRC_SOFT_CLIP
int32_t bottom
int32_t right
int32_t top
int32_t left