1 /*
2 * Copyright 2024 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "src/gpu/graphite/RasterPathUtils.h"
9
10 #include "include/core/SkStrokeRec.h"
11 #include "include/private/base/SkFixed.h"
12 #include "src/base/SkFloatBits.h"
13 #include "src/core/SkBlitter_A8.h"
14 #include "src/gpu/graphite/geom/Shape.h"
15 #include "src/gpu/graphite/geom/Transform_graphite.h"
16
17 namespace skgpu::graphite {
18
init(SkISize pixmapSize)19 bool RasterMaskHelper::init(SkISize pixmapSize) {
20 if (!fPixels) {
21 return false;
22 }
23
24 // Allocate pixmap if needed
25 if (!fPixels->addr()) {
26 const SkImageInfo bmImageInfo = SkImageInfo::MakeA8(pixmapSize);
27 if (!fPixels->tryAlloc(bmImageInfo)) {
28 return false;
29 }
30 fPixels->erase(0);
31 } else if (fPixels->dimensions() != pixmapSize) {
32 return false;
33 }
34
35 fDraw.fBlitterChooser = SkA8Blitter_Choose;
36 fDraw.fDst = *fPixels;
37 fDraw.fRC = &fRasterClip;
38 return true;
39 }
40
drawShape(const Shape & shape,const Transform & transform,const SkStrokeRec & strokeRec,const SkIRect & resultBounds)41 void RasterMaskHelper::drawShape(const Shape& shape,
42 const Transform& transform,
43 const SkStrokeRec& strokeRec,
44 const SkIRect& resultBounds) {
45 fRasterClip.setRect(resultBounds);
46
47 SkPaint paint;
48 paint.setBlendMode(SkBlendMode::kSrc); // "Replace" mode
49 paint.setAntiAlias(true);
50 // SkPaint's color is unpremul so this will produce alpha in every channel.
51 paint.setColor(SK_ColorWHITE);
52 strokeRec.applyToPaint(&paint);
53
54 SkMatrix translatedMatrix = SkMatrix(transform);
55 // The atlas transform of the shape is the linear-components (scale, rotation, skew) of
56 // `localToDevice` translated by the top-left offset of the resultBounds.
57 // We will need to translate draws so the bound's UL corner is at the origin
58 translatedMatrix.postTranslate(resultBounds.x(), resultBounds.y());
59
60 fDraw.fCTM = &translatedMatrix;
61 SkPath path = shape.asPath();
62 if (path.isInverseFillType()) {
63 // The shader will handle the inverse fill in this case
64 path.toggleInverseFillType();
65 }
66 fDraw.drawPathCoverage(path, paint);
67 }
68
GeneratePathMaskKey(const Shape & shape,const Transform & transform,const SkStrokeRec & strokeRec,skvx::half2 maskSize)69 skgpu::UniqueKey GeneratePathMaskKey(const Shape& shape,
70 const Transform& transform,
71 const SkStrokeRec& strokeRec,
72 skvx::half2 maskSize) {
73 skgpu::UniqueKey maskKey;
74 {
75 static const skgpu::UniqueKey::Domain kDomain = skgpu::UniqueKey::GenerateDomain();
76 int styleKeySize = 6;
77 if (!strokeRec.isHairlineStyle() && !strokeRec.isFillStyle()) {
78 // Add space for width and miter if needed
79 styleKeySize += 2;
80 }
81 skgpu::UniqueKey::Builder builder(&maskKey, kDomain, styleKeySize + shape.keySize(),
82 "Raster Path Mask");
83 builder[0] = maskSize.x() | (maskSize.y() << 16);
84
85 // We require the upper left 2x2 of the matrix to match exactly for a cache hit.
86 SkMatrix mat = transform.matrix().asM33();
87 SkScalar sx = mat.get(SkMatrix::kMScaleX);
88 SkScalar sy = mat.get(SkMatrix::kMScaleY);
89 SkScalar kx = mat.get(SkMatrix::kMSkewX);
90 SkScalar ky = mat.get(SkMatrix::kMSkewY);
91 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
92 // Fractional translate does not affect caching on Android. This is done for better cache
93 // hit ratio and speed and is matching HWUI behavior, which didn't consider the matrix
94 // at all when caching paths.
95 SkFixed fracX = 0;
96 SkFixed fracY = 0;
97 #else
98 SkScalar tx = mat.get(SkMatrix::kMTransX);
99 SkScalar ty = mat.get(SkMatrix::kMTransY);
100 // Allow 8 bits each in x and y of subpixel positioning.
101 SkFixed fracX = SkScalarToFixed(SkScalarFraction(tx)) & 0x0000FF00;
102 SkFixed fracY = SkScalarToFixed(SkScalarFraction(ty)) & 0x0000FF00;
103 #endif
104 builder[1] = SkFloat2Bits(sx);
105 builder[2] = SkFloat2Bits(sy);
106 builder[3] = SkFloat2Bits(kx);
107 builder[4] = SkFloat2Bits(ky);
108 // FracX and fracY are &ed with 0x0000ff00, so need to shift one down to fill 16 bits.
109 uint32_t fracBits = fracX | (fracY >> 8);
110 // Distinguish between path styles. For anything but fill, we also need to include
111 // the cap. (SW grows hairlines by 0.5 pixel with round and square caps). For stroke
112 // or fill-and-stroke we need to include the join, width, and miter.
113 static_assert(SkStrokeRec::kStyleCount <= (1 << 2));
114 static_assert(SkPaint::kCapCount <= (1 << 2));
115 static_assert(SkPaint::kJoinCount <= (1 << 2));
116 uint32_t styleBits = strokeRec.getStyle();
117 if (!strokeRec.isFillStyle()) {
118 styleBits |= (strokeRec.getCap() << 2);
119 }
120 if (!strokeRec.isHairlineStyle() && !strokeRec.isFillStyle()) {
121 styleBits |= (strokeRec.getJoin() << 4);
122 builder[5] = SkFloat2Bits(strokeRec.getWidth());
123 builder[6] = SkFloat2Bits(strokeRec.getMiter());
124 }
125 builder[styleKeySize-1] = fracBits | (styleBits << 16);
126 shape.writeKey(&builder[styleKeySize], /*includeInverted=*/false);
127 }
128 return maskKey;
129 }
130
131 } // namespace skgpu::graphite
132