Home | History | Annotate | Download | only in core
      1 /*
      2  * Copyright 2016 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 "SkRasterPipeline.h"
      9 #include "SkPM4f.h"
     10 
     11 SkRasterPipeline::SkRasterPipeline(SkArenaAlloc* alloc) : fAlloc(alloc) {
     12     this->reset();
     13 }
     14 void SkRasterPipeline::reset() {
     15     fStages      = nullptr;
     16     fNumStages   = 0;
     17     fSlotsNeeded = 1;  // We always need one extra slot for just_return().
     18 }
     19 
     20 void SkRasterPipeline::append(StockStage stage, void* ctx) {
     21     SkASSERT(stage != from_srgb);
     22     this->unchecked_append(stage, ctx);
     23 }
     24 void SkRasterPipeline::unchecked_append(StockStage stage, void* ctx) {
     25     fStages = fAlloc->make<StageList>( StageList{fStages, stage, ctx} );
     26     fNumStages   += 1;
     27     fSlotsNeeded += ctx ? 2 : 1;
     28 }
     29 
     30 void SkRasterPipeline::extend(const SkRasterPipeline& src) {
     31     if (src.empty()) {
     32         return;
     33     }
     34     auto stages = fAlloc->makeArrayDefault<StageList>(src.fNumStages);
     35 
     36     int n = src.fNumStages;
     37     const StageList* st = src.fStages;
     38     while (n --> 1) {
     39         stages[n]      = *st;
     40         stages[n].prev = &stages[n-1];
     41         st = st->prev;
     42     }
     43     stages[0]      = *st;
     44     stages[0].prev = fStages;
     45 
     46     fStages = &stages[src.fNumStages - 1];
     47     fNumStages   += src.fNumStages;
     48     fSlotsNeeded += src.fSlotsNeeded - 1;  // Don't double count just_returns().
     49 }
     50 
     51 void SkRasterPipeline::dump() const {
     52     SkDebugf("SkRasterPipeline, %d stages (in reverse)\n", fNumStages);
     53     for (auto st = fStages; st; st = st->prev) {
     54         const char* name = "";
     55         switch (st->stage) {
     56         #define M(x) case x: name = #x; break;
     57             SK_RASTER_PIPELINE_STAGES(M)
     58         #undef M
     59         }
     60         SkDebugf("\t%s\n", name);
     61     }
     62     SkDebugf("\n");
     63 }
     64 
     65 //#define TRACK_COLOR_HISTOGRAM
     66 #ifdef TRACK_COLOR_HISTOGRAM
     67     static int gBlack;
     68     static int gWhite;
     69     static int gColor;
     70     #define INC_BLACK   gBlack++
     71     #define INC_WHITE   gWhite++
     72     #define INC_COLOR   gColor++
     73 #else
     74     #define INC_BLACK
     75     #define INC_WHITE
     76     #define INC_COLOR
     77 #endif
     78 
     79 void SkRasterPipeline::append_uniform_color(SkArenaAlloc* alloc, const SkPM4f& c) {
     80     if (c.r() == 0 && c.g() == 0 && c.b() == 0 && c.a() == 1) {
     81         this->append(black_color);
     82         INC_BLACK;
     83     } else if (c.r() == 1 && c.g() == 1 && c.b() == 1 && c.a() == 1) {
     84         this->append(white_color);
     85         INC_WHITE;
     86     } else {
     87         float* storage = alloc->makeArray<float>(4);
     88         memcpy(storage, c.fVec, 4 * sizeof(float));
     89         this->append(uniform_color, storage);
     90         INC_COLOR;
     91     }
     92 
     93 #ifdef TRACK_COLOR_HISTOGRAM
     94     SkDebugf("B=%d W=%d C=%d\n", gBlack, gWhite, gColor);
     95 #endif
     96 }
     97 
     98 #undef INC_BLACK
     99 #undef INC_WHITE
    100 #undef INC_COLOR
    101 
    102 // It's pretty easy to start with sound premultiplied linear floats, pack those
    103 // to sRGB encoded bytes, then read them back to linear floats and find them not
    104 // quite premultiplied, with a color channel just a smidge greater than the alpha
    105 // channel.  This can happen basically any time we have different transfer
    106 // functions for alpha and colors... sRGB being the only one we draw into.
    107 
    108 // This is an annoying problem with no known good solution.  So apply the clamp hammer.
    109 
    110 void SkRasterPipeline::append_from_srgb(SkAlphaType at) {
    111     this->unchecked_append(from_srgb, nullptr);
    112     if (at == kPremul_SkAlphaType) {
    113         this->append(SkRasterPipeline::clamp_a);
    114     }
    115 }
    116 
    117 void SkRasterPipeline::append_from_srgb_dst(SkAlphaType at) {
    118     this->unchecked_append(from_srgb_dst, nullptr);
    119     if (at == kPremul_SkAlphaType) {
    120         this->append(SkRasterPipeline::clamp_a_dst);
    121     }
    122 }
    123 
    124 //static int gCounts[5] = { 0, 0, 0, 0, 0 };
    125 
    126 void SkRasterPipeline::append_matrix(SkArenaAlloc* alloc, const SkMatrix& matrix) {
    127     SkMatrix::TypeMask mt = matrix.getType();
    128 #if 0
    129     if (mt > 4) mt = 4;
    130     gCounts[mt] += 1;
    131     SkDebugf("matrices: %d %d %d %d %d\n",
    132              gCounts[0], gCounts[1], gCounts[2], gCounts[3], gCounts[4]);
    133 #endif
    134 
    135     // Based on a histogram of skps, we determined the following special cases were common, more
    136     // or fewer can be used if client behaviors change.
    137 
    138     if (mt == SkMatrix::kIdentity_Mask) {
    139         return;
    140     }
    141     if (mt == SkMatrix::kTranslate_Mask) {
    142         float* trans = alloc->makeArrayDefault<float>(2);
    143         trans[0] = matrix.getTranslateX();
    144         trans[1] = matrix.getTranslateY();
    145         this->append(SkRasterPipeline::matrix_translate, trans);
    146     } else if ((mt | (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) ==
    147                      (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) {
    148         float* scaleTrans = alloc->makeArrayDefault<float>(4);
    149         scaleTrans[0] = matrix.getTranslateX();
    150         scaleTrans[1] = matrix.getTranslateY();
    151         scaleTrans[2] = matrix.getScaleX();
    152         scaleTrans[3] = matrix.getScaleY();
    153         this->append(SkRasterPipeline::matrix_scale_translate, scaleTrans);
    154     } else {
    155         float* storage = alloc->makeArrayDefault<float>(9);
    156         if (matrix.asAffine(storage)) {
    157             // note: asAffine and the 2x3 stage really only need 6 entries
    158             this->append(SkRasterPipeline::matrix_2x3, storage);
    159         } else {
    160             matrix.get9(storage);
    161             this->append(SkRasterPipeline::matrix_perspective, storage);
    162         }
    163     }
    164 }
    165