• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2017 Google Inc.
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 "SkArenaAlloc.h"
9 #include "SkAutoBlitterChoose.h"
10 #include "SkComposeShader.h"
11 #include "SkDraw.h"
12 #include "SkNx.h"
13 #include "SkPM4fPriv.h"
14 #include "SkRasterClip.h"
15 #include "SkScan.h"
16 #include "SkShaderBase.h"
17 #include "SkString.h"
18 #include "SkVertState.h"
19 
20 #include "SkArenaAlloc.h"
21 #include "SkCoreBlitters.h"
22 #include "SkColorSpaceXform.h"
23 
24 struct Matrix43 {
25     float fMat[12];    // column major
26 
mapMatrix4327     Sk4f map(float x, float y) const {
28         return Sk4f::Load(&fMat[0]) * x + Sk4f::Load(&fMat[4]) * y + Sk4f::Load(&fMat[8]);
29     }
30 
setConcatMatrix4331     void setConcat(const Matrix43& a, const SkMatrix& b) {
32         fMat[ 0] = a.dot(0, b.getScaleX(), b.getSkewY());
33         fMat[ 1] = a.dot(1, b.getScaleX(), b.getSkewY());
34         fMat[ 2] = a.dot(2, b.getScaleX(), b.getSkewY());
35         fMat[ 3] = a.dot(3, b.getScaleX(), b.getSkewY());
36 
37         fMat[ 4] = a.dot(0, b.getSkewX(), b.getScaleY());
38         fMat[ 5] = a.dot(1, b.getSkewX(), b.getScaleY());
39         fMat[ 6] = a.dot(2, b.getSkewX(), b.getScaleY());
40         fMat[ 7] = a.dot(3, b.getSkewX(), b.getScaleY());
41 
42         fMat[ 8] = a.dot(0, b.getTranslateX(), b.getTranslateY()) + a.fMat[ 8];
43         fMat[ 9] = a.dot(1, b.getTranslateX(), b.getTranslateY()) + a.fMat[ 9];
44         fMat[10] = a.dot(2, b.getTranslateX(), b.getTranslateY()) + a.fMat[10];
45         fMat[11] = a.dot(3, b.getTranslateX(), b.getTranslateY()) + a.fMat[11];
46     }
47 
48 private:
dotMatrix4349     float dot(int index, float x, float y) const {
50         return fMat[index + 0] * x + fMat[index + 4] * y;
51     }
52 };
53 
ChooseHairProc(bool doAntiAlias)54 static SkScan::HairRCProc ChooseHairProc(bool doAntiAlias) {
55     return doAntiAlias ? SkScan::AntiHairLine : SkScan::HairLine;
56 }
57 
58 static bool SK_WARN_UNUSED_RESULT
texture_to_matrix(const VertState & state,const SkPoint verts[],const SkPoint texs[],SkMatrix * matrix)59 texture_to_matrix(const VertState& state, const SkPoint verts[], const SkPoint texs[],
60                   SkMatrix* matrix) {
61     SkPoint src[3], dst[3];
62 
63     src[0] = texs[state.f0];
64     src[1] = texs[state.f1];
65     src[2] = texs[state.f2];
66     dst[0] = verts[state.f0];
67     dst[1] = verts[state.f1];
68     dst[2] = verts[state.f2];
69     return matrix->setPolyToPoly(src, dst, 3);
70 }
71 
72 class SkTriColorShader : public SkShaderBase {
73 public:
SkTriColorShader(bool isOpaque)74     SkTriColorShader(bool isOpaque) : fIsOpaque(isOpaque) {}
75 
getMatrix43()76     Matrix43* getMatrix43() { return &fM43; }
77 
isOpaque() const78     bool isOpaque() const override { return fIsOpaque; }
79 
SK_TO_STRING_OVERRIDE()80     SK_TO_STRING_OVERRIDE()
81 
82     // For serialization.  This will never be called.
83     Factory getFactory() const override { SK_ABORT("not reached"); return nullptr; }
84 
85 protected:
onMakeContext(const ContextRec & rec,SkArenaAlloc * alloc) const86     Context* onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc) const override {
87         return nullptr;
88     }
onAppendStages(const StageRec & rec) const89     bool onAppendStages(const StageRec& rec) const override {
90         rec.fPipeline->append_seed_shader();
91         rec.fPipeline->append(SkRasterPipeline::matrix_4x3, &fM43);
92         return true;
93     }
94 
95 private:
96     Matrix43 fM43;
97     const bool fIsOpaque;
98 
99     typedef SkShaderBase INHERITED;
100 };
101 
102 #ifndef SK_IGNORE_TO_STRING
toString(SkString * str) const103 void SkTriColorShader::toString(SkString* str) const {
104     str->append("SkTriColorShader: (");
105 
106     this->INHERITED::toString(str);
107 
108     str->append(")");
109 }
110 #endif
111 
112 static bool SK_WARN_UNUSED_RESULT
update_tricolor_matrix(const SkMatrix & ctmInv,const SkPoint pts[],const SkPM4f colors[],int index0,int index1,int index2,Matrix43 * result)113 update_tricolor_matrix(const SkMatrix& ctmInv, const SkPoint pts[], const SkPM4f colors[],
114                        int index0, int index1, int index2, Matrix43* result) {
115     SkMatrix m, im;
116     m.reset();
117     m.set(0, pts[index1].fX - pts[index0].fX);
118     m.set(1, pts[index2].fX - pts[index0].fX);
119     m.set(2, pts[index0].fX);
120     m.set(3, pts[index1].fY - pts[index0].fY);
121     m.set(4, pts[index2].fY - pts[index0].fY);
122     m.set(5, pts[index0].fY);
123     if (!m.invert(&im)) {
124         return false;
125     }
126 
127     SkMatrix dstToUnit;
128     dstToUnit.setConcat(im, ctmInv);
129 
130     Sk4f c0 = colors[index0].to4f(),
131          c1 = colors[index1].to4f(),
132          c2 = colors[index2].to4f();
133 
134     Matrix43 colorm;
135     (c1 - c0).store(&colorm.fMat[0]);
136     (c2 - c0).store(&colorm.fMat[4]);
137     c0.store(&colorm.fMat[8]);
138     result->setConcat(colorm, dstToUnit);
139     return true;
140 }
141 
142 // Convert the SkColors into float colors. The conversion depends on some conditions:
143 // - If the pixmap has a dst colorspace, we have to be "color-correct".
144 //   Do we map into dst-colorspace before or after we interpolate?
145 // - We have to decide when to apply per-color alpha (before or after we interpolate)
146 //
147 // For now, we will take a simple approach, but recognize this is just a start:
148 // - convert colors into dst colorspace before interpolation (matches gradients)
149 // - apply per-color alpha before interpolation (matches old version of vertices)
150 //
convert_colors(const SkColor src[],int count,SkColorSpace * deviceCS,SkArenaAlloc * alloc)151 static SkPM4f* convert_colors(const SkColor src[], int count, SkColorSpace* deviceCS,
152                               SkArenaAlloc* alloc) {
153     SkPM4f* dst = alloc->makeArray<SkPM4f>(count);
154     if (!deviceCS) {
155         for (int i = 0; i < count; ++i) {
156             dst[i] = SkPM4f_from_SkColor(src[i], nullptr);
157         }
158     } else {
159         auto srcCS = SkColorSpace::MakeSRGB();
160         auto dstCS = deviceCS->makeLinearGamma();
161         SkColorSpaceXform::Apply(dstCS.get(), SkColorSpaceXform::kRGBA_F32_ColorFormat, dst,
162                                  srcCS.get(), SkColorSpaceXform::kBGRA_8888_ColorFormat, src,
163                                  count, SkColorSpaceXform::kPremul_AlphaOp);
164     }
165     return dst;
166 }
167 
compute_is_opaque(const SkColor colors[],int count)168 static bool compute_is_opaque(const SkColor colors[], int count) {
169     uint32_t c = ~0;
170     for (int i = 0; i < count; ++i) {
171         c &= colors[i];
172     }
173     return SkColorGetA(c) == 0xFF;
174 }
175 
drawVertices(SkVertices::VertexMode vmode,int count,const SkPoint vertices[],const SkPoint textures[],const SkColor colors[],SkBlendMode bmode,const uint16_t indices[],int indexCount,const SkPaint & paint) const176 void SkDraw::drawVertices(SkVertices::VertexMode vmode, int count,
177                           const SkPoint vertices[], const SkPoint textures[],
178                           const SkColor colors[], SkBlendMode bmode,
179                           const uint16_t indices[], int indexCount,
180                           const SkPaint& paint) const {
181     SkASSERT(0 == count || vertices);
182 
183     // abort early if there is nothing to draw
184     if (count < 3 || (indices && indexCount < 3) || fRC->isEmpty()) {
185         return;
186     }
187     SkMatrix ctmInv;
188     if (!fMatrix->invert(&ctmInv)) {
189         return;
190     }
191 
192     // make textures and shader mutually consistent
193     SkShader* shader = paint.getShader();
194     if (!(shader && textures)) {
195         shader = nullptr;
196         textures = nullptr;
197     }
198 
199     // We can simplify things for certain blendmodes. This is for speed, and SkComposeShader
200     // itself insists we don't pass kSrc or kDst to it.
201     //
202     if (colors && textures) {
203         switch (bmode) {
204             case SkBlendMode::kSrc:
205                 colors = nullptr;
206                 break;
207             case SkBlendMode::kDst:
208                 textures = nullptr;
209                 break;
210             default: break;
211         }
212     }
213 
214     // we don't use the shader if there are no textures
215     if (!textures) {
216         shader = nullptr;
217     }
218 
219     constexpr size_t defCount = 16;
220     constexpr size_t outerSize = sizeof(SkTriColorShader) +
221                                  sizeof(SkComposeShader) +
222                                  (sizeof(SkPoint) + sizeof(SkPM4f)) * defCount;
223     SkSTArenaAlloc<outerSize> outerAlloc;
224 
225     SkPoint* devVerts = outerAlloc.makeArray<SkPoint>(count);
226     fMatrix->mapPoints(devVerts, vertices, count);
227 
228     VertState       state(count, indices, indexCount);
229     VertState::Proc vertProc = state.chooseProc(vmode);
230 
231     if (colors || textures) {
232         SkPM4f*     dstColors = nullptr;
233         Matrix43*   matrix43 = nullptr;
234 
235         if (colors) {
236             dstColors = convert_colors(colors, count, fDst.colorSpace(), &outerAlloc);
237 
238             SkTriColorShader* triShader = outerAlloc.make<SkTriColorShader>(
239                                                                 compute_is_opaque(colors, count));
240             matrix43 = triShader->getMatrix43();
241             if (shader) {
242                 shader = outerAlloc.make<SkComposeShader>(sk_ref_sp(triShader), sk_ref_sp(shader),
243                                                           bmode, 1);
244             } else {
245                 shader = triShader;
246             }
247         }
248 
249         SkPaint p(paint);
250         p.setShader(sk_ref_sp(shader));
251 
252         if (!textures) {    // only tricolor shader
253             SkASSERT(matrix43);
254             auto blitter = SkCreateRasterPipelineBlitter(fDst, p, *fMatrix, &outerAlloc);
255             while (vertProc(&state)) {
256                 if (!update_tricolor_matrix(ctmInv, vertices, dstColors,
257                                             state.f0, state.f1, state.f2,
258                                             matrix43)) {
259                     continue;
260                 }
261 
262                 SkPoint tmp[] = {
263                     devVerts[state.f0], devVerts[state.f1], devVerts[state.f2]
264                 };
265                 SkScan::FillTriangle(tmp, *fRC, blitter);
266             }
267         } else {
268             while (vertProc(&state)) {
269                 SkSTArenaAlloc<2048> innerAlloc;
270 
271                 const SkMatrix* ctm = fMatrix;
272                 SkMatrix tmpCtm;
273                 if (textures) {
274                     SkMatrix localM;
275                     if (!texture_to_matrix(state, vertices, textures, &localM)) {
276                         continue;
277                     }
278                     tmpCtm = SkMatrix::Concat(*fMatrix, localM);
279                     ctm = &tmpCtm;
280                 }
281 
282                 if (matrix43 && !update_tricolor_matrix(ctmInv, vertices, dstColors,
283                                                         state.f0, state.f1, state.f2,
284                                                         matrix43)) {
285                     continue;
286                 }
287 
288                 SkPoint tmp[] = {
289                     devVerts[state.f0], devVerts[state.f1], devVerts[state.f2]
290                 };
291                 auto blitter = SkCreateRasterPipelineBlitter(fDst, p, *ctm, &innerAlloc);
292                 SkScan::FillTriangle(tmp, *fRC, blitter);
293             }
294         }
295     } else {
296         // no colors[] and no texture, stroke hairlines with paint's color.
297         SkPaint p;
298         p.setStyle(SkPaint::kStroke_Style);
299         SkAutoBlitterChoose blitter(fDst, *fMatrix, p);
300         // Abort early if we failed to create a shader context.
301         if (blitter->isNullBlitter()) {
302             return;
303         }
304         SkScan::HairRCProc hairProc = ChooseHairProc(paint.isAntiAlias());
305         const SkRasterClip& clip = *fRC;
306         while (vertProc(&state)) {
307             SkPoint array[] = {
308                 devVerts[state.f0], devVerts[state.f1], devVerts[state.f2], devVerts[state.f0]
309             };
310             hairProc(array, 4, clip, blitter.get());
311         }
312     }
313 }
314