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