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 SK_WARN_UNUSED_RESULT
     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:
     74     SkTriColorShader(bool isOpaque) : fIsOpaque(isOpaque) {}
     75 
     76     Matrix43* getMatrix43() { return &fM43; }
     77 
     78     bool isOpaque() const override { return fIsOpaque; }
     79 
     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:
     86     Context* onMakeContext(const ContextRec& rec, SkArenaAlloc* alloc) const override {
     87         return nullptr;
     88     }
     89     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
    103 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
    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 //
    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 
    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 
    176 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