blob: ba07467dfcad0f116ae8ed468f9575293d5aedb6 [file] [log] [blame]
mtklein281b33f2016-07-12 15:01:26 -07001/*
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"
Mike Reedc91e3872017-07-05 14:12:37 -04009#include "SkPM4f.h"
mtklein281b33f2016-07-12 15:01:26 -070010
Mike Kleinb24704d2017-05-24 07:53:00 -040011SkRasterPipeline::SkRasterPipeline(SkArenaAlloc* alloc) : fAlloc(alloc) {
12 this->reset();
Mike Klein1859f692017-05-22 08:28:45 -040013}
Mike Kleinb24704d2017-05-24 07:53:00 -040014void SkRasterPipeline::reset() {
15 fStages = nullptr;
Mike Klein9fff1112017-05-24 11:37:52 -040016 fNumStages = 0;
Mike Kleinb24704d2017-05-24 07:53:00 -040017 fSlotsNeeded = 1; // We always need one extra slot for just_return().
Mike Klein159db0a2017-07-25 12:02:13 -040018 fClamped = true;
Mike Klein1859f692017-05-22 08:28:45 -040019}
mtklein281b33f2016-07-12 15:01:26 -070020
Mike Klein26bea5d2016-10-05 10:36:38 -040021void SkRasterPipeline::append(StockStage stage, void* ctx) {
Mike Klein16776df2017-08-03 10:22:42 -040022 SkASSERT(stage != from_srgb); // Please use append_from_srgb().
23 SkASSERT(stage != uniform_color); // Please use append_constant_color().
Mike Kleinb24704d2017-05-24 07:53:00 -040024 this->unchecked_append(stage, ctx);
25}
26void SkRasterPipeline::unchecked_append(StockStage stage, void* ctx) {
27 fStages = fAlloc->make<StageList>( StageList{fStages, stage, ctx} );
Mike Klein9fff1112017-05-24 11:37:52 -040028 fNumStages += 1;
Mike Kleinb24704d2017-05-24 07:53:00 -040029 fSlotsNeeded += ctx ? 2 : 1;
Mike Kleinfa9f2412016-09-29 13:40:01 -040030}
31
mtklein9a5c47f2016-07-22 11:05:04 -070032void SkRasterPipeline::extend(const SkRasterPipeline& src) {
Mike Klein9fff1112017-05-24 11:37:52 -040033 if (src.empty()) {
Mike Kleinb24704d2017-05-24 07:53:00 -040034 return;
35 }
Mike Klein9fff1112017-05-24 11:37:52 -040036 auto stages = fAlloc->makeArrayDefault<StageList>(src.fNumStages);
37
38 int n = src.fNumStages;
39 const StageList* st = src.fStages;
40 while (n --> 1) {
41 stages[n] = *st;
42 stages[n].prev = &stages[n-1];
43 st = st->prev;
44 }
45 stages[0] = *st;
46 stages[0].prev = fStages;
47
48 fStages = &stages[src.fNumStages - 1];
49 fNumStages += src.fNumStages;
50 fSlotsNeeded += src.fSlotsNeeded - 1; // Don't double count just_returns().
Mike Klein159db0a2017-07-25 12:02:13 -040051 fClamped = fClamped && src.fClamped;
mtklein9a5c47f2016-07-22 11:05:04 -070052}
53
Mike Klein3928c6b2016-11-15 16:18:38 -050054void SkRasterPipeline::dump() const {
Mike Klein9fff1112017-05-24 11:37:52 -040055 SkDebugf("SkRasterPipeline, %d stages (in reverse)\n", fNumStages);
Mike Kleinb24704d2017-05-24 07:53:00 -040056 for (auto st = fStages; st; st = st->prev) {
Mike Klein3928c6b2016-11-15 16:18:38 -050057 const char* name = "";
Mike Kleinb24704d2017-05-24 07:53:00 -040058 switch (st->stage) {
Mike Klein3928c6b2016-11-15 16:18:38 -050059 #define M(x) case x: name = #x; break;
60 SK_RASTER_PIPELINE_STAGES(M)
61 #undef M
62 }
Mike Kleinbf49d612016-11-30 20:54:37 +000063 SkDebugf("\t%s\n", name);
Mike Klein3928c6b2016-11-15 16:18:38 -050064 }
65 SkDebugf("\n");
66}
Mike Kleind37d5d92016-12-14 13:38:24 +000067
Mike Reedc91e3872017-07-05 14:12:37 -040068//#define TRACK_COLOR_HISTOGRAM
69#ifdef TRACK_COLOR_HISTOGRAM
70 static int gBlack;
71 static int gWhite;
72 static int gColor;
73 #define INC_BLACK gBlack++
74 #define INC_WHITE gWhite++
75 #define INC_COLOR gColor++
76#else
77 #define INC_BLACK
78 #define INC_WHITE
79 #define INC_COLOR
80#endif
81
Mike Klein16776df2017-08-03 10:22:42 -040082void SkRasterPipeline::append_constant_color(SkArenaAlloc* alloc, const float rgba[4]) {
83 if (rgba[0] == 0 && rgba[1] == 0 && rgba[2] == 0 && rgba[3] == 1) {
Mike Reedc91e3872017-07-05 14:12:37 -040084 this->append(black_color);
85 INC_BLACK;
Mike Klein16776df2017-08-03 10:22:42 -040086 } else if (rgba[0] == 1 && rgba[1] == 1 && rgba[2] == 1 && rgba[3] == 1) {
Mike Reedc91e3872017-07-05 14:12:37 -040087 this->append(white_color);
88 INC_WHITE;
89 } else {
90 float* storage = alloc->makeArray<float>(4);
Mike Klein16776df2017-08-03 10:22:42 -040091 memcpy(storage, rgba, 4 * sizeof(float));
92 this->unchecked_append(uniform_color, storage);
Mike Reedc91e3872017-07-05 14:12:37 -040093 INC_COLOR;
94 }
95
96#ifdef TRACK_COLOR_HISTOGRAM
97 SkDebugf("B=%d W=%d C=%d\n", gBlack, gWhite, gColor);
98#endif
99}
100
101#undef INC_BLACK
102#undef INC_WHITE
103#undef INC_COLOR
104
Mike Kleind37d5d92016-12-14 13:38:24 +0000105// It's pretty easy to start with sound premultiplied linear floats, pack those
106// to sRGB encoded bytes, then read them back to linear floats and find them not
107// quite premultiplied, with a color channel just a smidge greater than the alpha
108// channel. This can happen basically any time we have different transfer
109// functions for alpha and colors... sRGB being the only one we draw into.
110
111// This is an annoying problem with no known good solution. So apply the clamp hammer.
112
113void SkRasterPipeline::append_from_srgb(SkAlphaType at) {
Mike Kleinb24704d2017-05-24 07:53:00 -0400114 this->unchecked_append(from_srgb, nullptr);
Mike Kleind37d5d92016-12-14 13:38:24 +0000115 if (at == kPremul_SkAlphaType) {
116 this->append(SkRasterPipeline::clamp_a);
117 }
118}
Mike Reed279091e2017-06-27 16:58:00 -0400119
120void SkRasterPipeline::append_from_srgb_dst(SkAlphaType at) {
121 this->unchecked_append(from_srgb_dst, nullptr);
122 if (at == kPremul_SkAlphaType) {
123 this->append(SkRasterPipeline::clamp_a_dst);
124 }
125}
Mike Reed6b59bf42017-07-03 21:26:44 -0400126
Mike Reed7aad8cc2017-07-05 12:33:06 -0400127//static int gCounts[5] = { 0, 0, 0, 0, 0 };
128
Mike Reed6b59bf42017-07-03 21:26:44 -0400129void SkRasterPipeline::append_matrix(SkArenaAlloc* alloc, const SkMatrix& matrix) {
Mike Reed7aad8cc2017-07-05 12:33:06 -0400130 SkMatrix::TypeMask mt = matrix.getType();
131#if 0
132 if (mt > 4) mt = 4;
133 gCounts[mt] += 1;
134 SkDebugf("matrices: %d %d %d %d %d\n",
135 gCounts[0], gCounts[1], gCounts[2], gCounts[3], gCounts[4]);
136#endif
137
138 // Based on a histogram of skps, we determined the following special cases were common, more
139 // or fewer can be used if client behaviors change.
140
141 if (mt == SkMatrix::kIdentity_Mask) {
142 return;
143 }
144 if (mt == SkMatrix::kTranslate_Mask) {
145 float* trans = alloc->makeArrayDefault<float>(2);
146 trans[0] = matrix.getTranslateX();
147 trans[1] = matrix.getTranslateY();
148 this->append(SkRasterPipeline::matrix_translate, trans);
149 } else if ((mt | (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) ==
150 (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) {
151 float* scaleTrans = alloc->makeArrayDefault<float>(4);
152 scaleTrans[0] = matrix.getTranslateX();
153 scaleTrans[1] = matrix.getTranslateY();
154 scaleTrans[2] = matrix.getScaleX();
155 scaleTrans[3] = matrix.getScaleY();
156 this->append(SkRasterPipeline::matrix_scale_translate, scaleTrans);
Mike Reed6b59bf42017-07-03 21:26:44 -0400157 } else {
Mike Reed7aad8cc2017-07-05 12:33:06 -0400158 float* storage = alloc->makeArrayDefault<float>(9);
159 if (matrix.asAffine(storage)) {
160 // note: asAffine and the 2x3 stage really only need 6 entries
161 this->append(SkRasterPipeline::matrix_2x3, storage);
162 } else {
163 matrix.get9(storage);
164 this->append(SkRasterPipeline::matrix_perspective, storage);
165 }
Mike Reed6b59bf42017-07-03 21:26:44 -0400166 }
167}
Mike Klein159db0a2017-07-25 12:02:13 -0400168
169void SkRasterPipeline::clamp_if_unclamped(SkAlphaType alphaType) {
170 if (!fClamped) {
171 this->append(SkRasterPipeline::clamp_0);
172 this->append(alphaType == kPremul_SkAlphaType ? SkRasterPipeline::clamp_a
173 : SkRasterPipeline::clamp_1);
174 fClamped = true;
175 }
176}