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