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