blob: a7b25d7ef9bb509cb3c3fa0e54354f3c58dfbdc9 [file] [log] [blame]
/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkRasterPipeline.h"
#include "SkPM4f.h"
SkRasterPipeline::SkRasterPipeline(SkArenaAlloc* alloc) : fAlloc(alloc) {
this->reset();
}
void SkRasterPipeline::reset() {
fStages = nullptr;
fNumStages = 0;
fSlotsNeeded = 1; // We always need one extra slot for just_return().
}
void SkRasterPipeline::append(StockStage stage, void* ctx) {
SkASSERT(stage != from_srgb);
this->unchecked_append(stage, ctx);
}
void SkRasterPipeline::unchecked_append(StockStage stage, void* ctx) {
fStages = fAlloc->make<StageList>( StageList{fStages, stage, ctx} );
fNumStages += 1;
fSlotsNeeded += ctx ? 2 : 1;
}
void SkRasterPipeline::extend(const SkRasterPipeline& src) {
if (src.empty()) {
return;
}
auto stages = fAlloc->makeArrayDefault<StageList>(src.fNumStages);
int n = src.fNumStages;
const StageList* st = src.fStages;
while (n --> 1) {
stages[n] = *st;
stages[n].prev = &stages[n-1];
st = st->prev;
}
stages[0] = *st;
stages[0].prev = fStages;
fStages = &stages[src.fNumStages - 1];
fNumStages += src.fNumStages;
fSlotsNeeded += src.fSlotsNeeded - 1; // Don't double count just_returns().
}
void SkRasterPipeline::dump() const {
SkDebugf("SkRasterPipeline, %d stages (in reverse)\n", fNumStages);
for (auto st = fStages; st; st = st->prev) {
const char* name = "";
switch (st->stage) {
#define M(x) case x: name = #x; break;
SK_RASTER_PIPELINE_STAGES(M)
#undef M
}
SkDebugf("\t%s\n", name);
}
SkDebugf("\n");
}
//#define TRACK_COLOR_HISTOGRAM
#ifdef TRACK_COLOR_HISTOGRAM
static int gBlack;
static int gWhite;
static int gColor;
#define INC_BLACK gBlack++
#define INC_WHITE gWhite++
#define INC_COLOR gColor++
#else
#define INC_BLACK
#define INC_WHITE
#define INC_COLOR
#endif
void SkRasterPipeline::append_uniform_color(SkArenaAlloc* alloc, const SkPM4f& c) {
if (c.r() == 0 && c.g() == 0 && c.b() == 0 && c.a() == 1) {
this->append(black_color);
INC_BLACK;
} else if (c.r() == 1 && c.g() == 1 && c.b() == 1 && c.a() == 1) {
this->append(white_color);
INC_WHITE;
} else {
float* storage = alloc->makeArray<float>(4);
memcpy(storage, c.fVec, 4 * sizeof(float));
this->append(uniform_color, storage);
INC_COLOR;
}
#ifdef TRACK_COLOR_HISTOGRAM
SkDebugf("B=%d W=%d C=%d\n", gBlack, gWhite, gColor);
#endif
}
#undef INC_BLACK
#undef INC_WHITE
#undef INC_COLOR
// It's pretty easy to start with sound premultiplied linear floats, pack those
// to sRGB encoded bytes, then read them back to linear floats and find them not
// quite premultiplied, with a color channel just a smidge greater than the alpha
// channel. This can happen basically any time we have different transfer
// functions for alpha and colors... sRGB being the only one we draw into.
// This is an annoying problem with no known good solution. So apply the clamp hammer.
void SkRasterPipeline::append_from_srgb(SkAlphaType at) {
this->unchecked_append(from_srgb, nullptr);
if (at == kPremul_SkAlphaType) {
this->append(SkRasterPipeline::clamp_a);
}
}
void SkRasterPipeline::append_from_srgb_dst(SkAlphaType at) {
this->unchecked_append(from_srgb_dst, nullptr);
if (at == kPremul_SkAlphaType) {
this->append(SkRasterPipeline::clamp_a_dst);
}
}
//static int gCounts[5] = { 0, 0, 0, 0, 0 };
void SkRasterPipeline::append_matrix(SkArenaAlloc* alloc, const SkMatrix& matrix) {
SkMatrix::TypeMask mt = matrix.getType();
#if 0
if (mt > 4) mt = 4;
gCounts[mt] += 1;
SkDebugf("matrices: %d %d %d %d %d\n",
gCounts[0], gCounts[1], gCounts[2], gCounts[3], gCounts[4]);
#endif
// Based on a histogram of skps, we determined the following special cases were common, more
// or fewer can be used if client behaviors change.
if (mt == SkMatrix::kIdentity_Mask) {
return;
}
if (mt == SkMatrix::kTranslate_Mask) {
float* trans = alloc->makeArrayDefault<float>(2);
trans[0] = matrix.getTranslateX();
trans[1] = matrix.getTranslateY();
this->append(SkRasterPipeline::matrix_translate, trans);
} else if ((mt | (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) ==
(SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) {
float* scaleTrans = alloc->makeArrayDefault<float>(4);
scaleTrans[0] = matrix.getTranslateX();
scaleTrans[1] = matrix.getTranslateY();
scaleTrans[2] = matrix.getScaleX();
scaleTrans[3] = matrix.getScaleY();
this->append(SkRasterPipeline::matrix_scale_translate, scaleTrans);
} else {
float* storage = alloc->makeArrayDefault<float>(9);
if (matrix.asAffine(storage)) {
// note: asAffine and the 2x3 stage really only need 6 entries
this->append(SkRasterPipeline::matrix_2x3, storage);
} else {
matrix.get9(storage);
this->append(SkRasterPipeline::matrix_perspective, storage);
}
}
}