stage version of vertices
This CL, just to limit its size/complexity, only handles colors but not textures. Future CLs will cover everything. Performance is pretty exciting. Its faster than the old code-path, and when we fix a bug in pathutils to preserve opaqueness, it gets a lot faster (8 -> 5) Bug: skia: Change-Id: I4113060e25fe25fe4e6a0ea59bd4fa5e33abc668 Reviewed-on: https://skia-review.googlesource.com/17276 Commit-Queue: Mike Reed <reed@google.com> Reviewed-by: Mike Klein <mtklein@chromium.org> Reviewed-by: Florin Malita <fmalita@chromium.org>
This commit is contained in:
parent
985febffa2
commit
0264095cfd
@ -205,5 +205,9 @@ SkBlitter* SkBlitter_ChooseD565(const SkPixmap& device, const SkPaint& paint,
|
||||
// Returns nullptr if no SkRasterPipeline blitter can be constructed for this paint.
|
||||
SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap&, const SkPaint&, const SkMatrix& ctm,
|
||||
SkArenaAlloc*);
|
||||
SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap&, const SkPaint&, const SkMatrix& ctm,
|
||||
const SkRasterPipeline& shaderPipeline,
|
||||
bool shader_is_opaque, bool shader_wants_dither,
|
||||
SkArenaAlloc*);
|
||||
|
||||
#endif
|
||||
|
@ -10,13 +10,19 @@
|
||||
#include "SkColorShader.h"
|
||||
#include "SkDraw.h"
|
||||
#include "SkNx.h"
|
||||
#include "SkPM4f.h"
|
||||
#include "SkPM4fPriv.h"
|
||||
#include "SkRasterClip.h"
|
||||
#include "SkScan.h"
|
||||
#include "SkShader.h"
|
||||
#include "SkString.h"
|
||||
#include "SkVertState.h"
|
||||
|
||||
#include "SkRasterPipeline.h"
|
||||
#include "SkArenaAlloc.h"
|
||||
#include "SkCoreBlitters.h"
|
||||
#include "SkColorSpaceXform.h"
|
||||
#include "SkColorSpace_Base.h"
|
||||
|
||||
struct Matrix43 {
|
||||
float fMat[12]; // column major
|
||||
|
||||
@ -356,9 +362,67 @@ namespace {
|
||||
|
||||
return alloc->makeSkSp<SkColorShader>(SkUnPreMultiply::PMColorToColor(pmColor));
|
||||
}
|
||||
|
||||
} // anonymous ns
|
||||
|
||||
static bool update_tricolor_matrix(const SkMatrix& ctmInv,
|
||||
const SkPoint pts[], const SkPM4f colors[],
|
||||
int index0, int index1, int index2, Matrix43* result) {
|
||||
SkMatrix m, im;
|
||||
m.reset();
|
||||
m.set(0, pts[index1].fX - pts[index0].fX);
|
||||
m.set(1, pts[index2].fX - pts[index0].fX);
|
||||
m.set(2, pts[index0].fX);
|
||||
m.set(3, pts[index1].fY - pts[index0].fY);
|
||||
m.set(4, pts[index2].fY - pts[index0].fY);
|
||||
m.set(5, pts[index0].fY);
|
||||
if (!m.invert(&im)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SkMatrix dstToUnit;
|
||||
dstToUnit.setConcat(im, ctmInv);
|
||||
|
||||
Sk4f c0 = colors[index0].to4f(),
|
||||
c1 = colors[index1].to4f(),
|
||||
c2 = colors[index2].to4f();
|
||||
|
||||
Matrix43 colorm;
|
||||
(c1 - c0).store(&colorm.fMat[0]);
|
||||
(c2 - c0).store(&colorm.fMat[4]);
|
||||
c0.store(&colorm.fMat[8]);
|
||||
result->setConcat(colorm, dstToUnit);
|
||||
return true;
|
||||
}
|
||||
|
||||
static SkPM4f* convert_colors(const SkColor src[], int count, SkColorSpace* deviceCS,
|
||||
SkArenaAlloc* alloc) {
|
||||
SkPM4f* dst = alloc->makeArray<SkPM4f>(count);
|
||||
if (!deviceCS) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
dst[i] = SkPM4f_from_SkColor(src[i], nullptr);
|
||||
}
|
||||
} else {
|
||||
// For now, we want premul to happen on the colors before interplation. If we later want
|
||||
// to apply it after the interp, pass kUnpremul here.
|
||||
SkAlphaType alphaVerb = kPremul_SkAlphaType;
|
||||
auto srcCS = SkColorSpace::MakeSRGB();
|
||||
auto dstCS = as_CSB(deviceCS)->makeLinearGamma();
|
||||
SkColorSpaceXform::New(srcCS.get(),
|
||||
dstCS.get())->apply(SkColorSpaceXform::kRGBA_F32_ColorFormat, dst,
|
||||
SkColorSpaceXform::kBGRA_8888_ColorFormat, src,
|
||||
count, alphaVerb);
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
static bool compute_is_opaque(const SkColor colors[], int count) {
|
||||
uint32_t c = ~0;
|
||||
for (int i = 0; i < count; ++i) {
|
||||
c &= colors[i];
|
||||
}
|
||||
return SkColorGetA(c) == 0xFF;
|
||||
}
|
||||
|
||||
void SkDraw::drawVertices(SkVertices::VertexMode vmode, int count,
|
||||
const SkPoint vertices[], const SkPoint textures[],
|
||||
const SkColor colors[], SkBlendMode bmode,
|
||||
@ -370,6 +434,10 @@ void SkDraw::drawVertices(SkVertices::VertexMode vmode, int count,
|
||||
if (count < 3 || (indices && indexCount < 3) || fRC->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
SkMatrix ctmInv;
|
||||
if (!fMatrix->invert(&ctmInv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// transform out vertices into device coordinates
|
||||
SkAutoSTMalloc<16, SkPoint> storage(count);
|
||||
@ -387,6 +455,53 @@ void SkDraw::drawVertices(SkVertices::VertexMode vmode, int count,
|
||||
Thus for texture drawing, we need both texture[] and a shader.
|
||||
*/
|
||||
|
||||
if (colors && !textures) {
|
||||
char arenaStorage[4096];
|
||||
SkArenaAlloc alloc(arenaStorage, sizeof(storage));
|
||||
Matrix43 matrix43;
|
||||
SkRasterPipeline shaderPipeline;
|
||||
|
||||
// Convert the SkColors into float colors. The conversion depends on some conditions:
|
||||
// - If the pixmap has a dst colorspace, we have to be "color-correct".
|
||||
// Do we map into dst-colorspace before or after we interpolate?
|
||||
// - We have to decide when to apply per-color alpha (before or after we interpolate)
|
||||
//
|
||||
// For now, we will take a simple approach, but recognize this is just a start:
|
||||
// - convert colors into dst colorspace before interpolation (matches gradients)
|
||||
// - apply per-color alpha before interpolation (matches old version of vertices)
|
||||
//
|
||||
SkPM4f* dstColors = convert_colors(colors, count, fDst.colorSpace(), &alloc);
|
||||
|
||||
shaderPipeline.append(SkRasterPipeline::matrix_4x3, &matrix43);
|
||||
// In theory we should never need to clamp. However, either due to imprecision in our
|
||||
// matrix43, or the scan converter passing us pixel centers that in fact are not within
|
||||
// the triangle, we do see occasional (slightly) out-of-range values, so we add these
|
||||
// clamp stages. It would be nice to find a way to detect when these are not needed.
|
||||
shaderPipeline.append(SkRasterPipeline::clamp_0);
|
||||
shaderPipeline.append(SkRasterPipeline::clamp_a);
|
||||
|
||||
bool is_opaque = compute_is_opaque(colors, count),
|
||||
wants_dither = paint.isDither();
|
||||
auto blitter = SkCreateRasterPipelineBlitter(fDst, paint, *fMatrix, shaderPipeline,
|
||||
is_opaque, wants_dither, &alloc);
|
||||
SkASSERT(!blitter->isNullBlitter());
|
||||
|
||||
// setup our state and function pointer for iterating triangles
|
||||
VertState state(count, indices, indexCount);
|
||||
VertState::Proc vertProc = state.chooseProc(vmode);
|
||||
|
||||
while (vertProc(&state)) {
|
||||
SkPoint tmp[] = {
|
||||
devVerts[state.f0], devVerts[state.f1], devVerts[state.f2]
|
||||
};
|
||||
if (update_tricolor_matrix(ctmInv, vertices, dstColors, state.f0, state.f1, state.f2,
|
||||
&matrix43)) {
|
||||
SkScan::FillTriangle(tmp, *fRC, blitter);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto triShader = sk_make_sp<SkTriColorShader>();
|
||||
SkPaint p(paint);
|
||||
|
||||
|
@ -82,7 +82,7 @@
|
||||
M(exclusion) M(hardlight) M(lighten) M(overlay) M(softlight) \
|
||||
M(hue) M(saturation) M(color) M(luminosity) \
|
||||
M(luminance_to_alpha) \
|
||||
M(matrix_2x3) M(matrix_3x4) M(matrix_4x5) \
|
||||
M(matrix_2x3) M(matrix_3x4) M(matrix_4x5) M(matrix_4x3) \
|
||||
M(matrix_perspective) \
|
||||
M(parametric_r) M(parametric_g) M(parametric_b) \
|
||||
M(parametric_a) \
|
||||
|
@ -22,6 +22,10 @@ class SkRasterPipelineBlitter : public SkBlitter {
|
||||
public:
|
||||
static SkBlitter* Create(const SkPixmap&, const SkPaint&, const SkMatrix& ctm,
|
||||
SkArenaAlloc*);
|
||||
static SkBlitter* Create(const SkPixmap&, const SkPaint&, const SkMatrix& ctm,
|
||||
const SkRasterPipeline& shaderPipeline,
|
||||
bool is_opaque, bool wants_dither,
|
||||
SkArenaAlloc*);
|
||||
|
||||
SkRasterPipelineBlitter(SkPixmap dst, SkBlendMode blend, SkPM4f paintColor)
|
||||
: fDst(dst)
|
||||
@ -38,6 +42,9 @@ public:
|
||||
// blits using something like a SkRasterPipeline::runFew() method.
|
||||
|
||||
private:
|
||||
void finishBuilding(const SkPaint& paint, const SkMatrix& ctm, bool is_oapque,
|
||||
bool is_constant, bool wants_dither, SkArenaAlloc* alloc);
|
||||
|
||||
void append_load_d(SkRasterPipeline*) const;
|
||||
void append_blend (SkRasterPipeline*) const;
|
||||
void maybe_clamp (SkRasterPipeline*) const;
|
||||
@ -85,48 +92,100 @@ SkBlitter* SkRasterPipelineBlitter::Create(const SkPixmap& dst,
|
||||
paint.getBlendMode(),
|
||||
SkPM4f_from_SkColor(paint.getColor(), dst.colorSpace()));
|
||||
|
||||
|
||||
SkBlendMode* blend = &blitter->fBlend;
|
||||
SkPM4f* paintColor = &blitter->fPaintColor;
|
||||
SkRasterPipeline* pipeline = &blitter->fShader;
|
||||
SkShader* shader = paint.getShader();
|
||||
|
||||
SkShader* shader = paint.getShader();
|
||||
SkColorFilter* colorFilter = paint.getColorFilter();
|
||||
bool is_opaque = paintColor->a() == 1.0f,
|
||||
is_constant = true,
|
||||
wants_dither = false;
|
||||
|
||||
// TODO: Think more about under what conditions we dither:
|
||||
// - if we're drawing anything into 565 and the user has asked us to dither, or
|
||||
// - if we're drawing a gradient into 565 or 8888.
|
||||
if ((paint.isDither() && dst.info().colorType() == kRGB_565_SkColorType) ||
|
||||
(shader && shader->asAGradient(nullptr) >= SkShader::kLinear_GradientType)) {
|
||||
switch (dst.info().colorType()) {
|
||||
default: blitter->fDitherCtx.rate = 0.0f; break;
|
||||
case kRGB_565_SkColorType: blitter->fDitherCtx.rate = 1/63.0f; break;
|
||||
case kRGBA_8888_SkColorType:
|
||||
case kBGRA_8888_SkColorType: blitter->fDitherCtx.rate = 1/255.0f; break;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_opaque = paintColor->a() == 1.0f,
|
||||
is_constant = blitter->fDitherCtx.rate == 0.0f;
|
||||
if (shader) {
|
||||
pipeline->append(SkRasterPipeline::seed_shader, &blitter->fCurrentY);
|
||||
if (!shader->appendStages(pipeline, dst.colorSpace(), alloc, ctm, paint)) {
|
||||
// When a shader fails to append stages, it means it has vetoed drawing entirely.
|
||||
return alloc->make<SkNullBlitter>();
|
||||
}
|
||||
if (!is_opaque) {
|
||||
pipeline->append(SkRasterPipeline::scale_1_float,
|
||||
&paintColor->fVec[SkPM4f::A]);
|
||||
}
|
||||
|
||||
is_opaque = is_opaque && shader->isOpaque();
|
||||
is_constant = is_constant && shader->isConstant();
|
||||
if (!is_opaque) {
|
||||
pipeline->append(SkRasterPipeline::scale_1_float, &paintColor->fVec[SkPM4f::A]);
|
||||
}
|
||||
is_opaque = is_opaque && shader->isOpaque();
|
||||
is_constant = shader->isConstant();
|
||||
wants_dither = shader->asAGradient(nullptr) >= SkShader::kLinear_GradientType;
|
||||
|
||||
} else {
|
||||
pipeline->append(SkRasterPipeline::constant_color, paintColor);
|
||||
}
|
||||
|
||||
blitter->finishBuilding(paint, ctm, is_opaque, is_constant, wants_dither, alloc);
|
||||
return blitter;
|
||||
}
|
||||
|
||||
SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap& dst,
|
||||
const SkPaint& paint,
|
||||
const SkMatrix& ctm,
|
||||
const SkRasterPipeline& shaderPipeline,
|
||||
bool is_opaque, bool wants_dither,
|
||||
SkArenaAlloc* alloc) {
|
||||
return SkRasterPipelineBlitter::Create(dst, paint, ctm, shaderPipeline,
|
||||
is_opaque, wants_dither, alloc);
|
||||
}
|
||||
|
||||
SkBlitter* SkRasterPipelineBlitter::Create(const SkPixmap& dst,
|
||||
const SkPaint& paint,
|
||||
const SkMatrix& ctm,
|
||||
const SkRasterPipeline& shaderPipeline,
|
||||
bool is_opaque, bool wants_dither,
|
||||
SkArenaAlloc* alloc) {
|
||||
auto blitter = alloc->make<SkRasterPipelineBlitter>(
|
||||
dst,
|
||||
paint.getBlendMode(),
|
||||
SkPM4f_from_SkColor(paint.getColor(), dst.colorSpace()));
|
||||
|
||||
bool is_constant = false; // we figure a custom shaderPipeline is never constant
|
||||
SkPM4f* paintColor = &blitter->fPaintColor;
|
||||
SkRasterPipeline* pipeline = &blitter->fShader;
|
||||
|
||||
pipeline->append(SkRasterPipeline::seed_shader, &blitter->fCurrentY);
|
||||
pipeline->extend(shaderPipeline);
|
||||
|
||||
if (paintColor->a() != 1.0f) {
|
||||
pipeline->append(SkRasterPipeline::scale_1_float, &paintColor->fVec[SkPM4f::A]);
|
||||
is_opaque = false;
|
||||
}
|
||||
|
||||
blitter->finishBuilding(paint, ctm, is_opaque, is_constant, wants_dither, alloc);
|
||||
return blitter;
|
||||
}
|
||||
|
||||
void SkRasterPipelineBlitter::finishBuilding(const SkPaint& paint, const SkMatrix& ctm,
|
||||
bool is_opaque, bool is_constant, bool wants_dither,
|
||||
SkArenaAlloc* alloc) {
|
||||
SkBlendMode* blend = &fBlend;
|
||||
SkPM4f* paintColor = &fPaintColor;
|
||||
SkRasterPipeline* pipeline = &fShader;
|
||||
SkColorFilter* colorFilter = paint.getColorFilter();
|
||||
|
||||
SkASSERT(fDitherCtx.rate == 0);
|
||||
if ((paint.isDither() && fDst.info().colorType() == kRGB_565_SkColorType) || wants_dither) {
|
||||
switch (fDst.info().colorType()) {
|
||||
case kRGB_565_SkColorType:
|
||||
fDitherCtx.rate = 1/63.0f;
|
||||
is_constant = false;
|
||||
break;
|
||||
case kRGBA_8888_SkColorType:
|
||||
case kBGRA_8888_SkColorType:
|
||||
fDitherCtx.rate = 1/255.0f;
|
||||
is_constant = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (colorFilter) {
|
||||
colorFilter->appendStages(pipeline, dst.colorSpace(), alloc, is_opaque);
|
||||
colorFilter->appendStages(pipeline, fDst.colorSpace(), alloc, is_opaque);
|
||||
is_opaque = is_opaque && (colorFilter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag);
|
||||
}
|
||||
|
||||
@ -147,14 +206,12 @@ SkBlitter* SkRasterPipelineBlitter::Create(const SkPixmap& dst,
|
||||
if (is_constant && *blend == SkBlendMode::kSrc) {
|
||||
SkRasterPipeline p;
|
||||
p.extend(*pipeline);
|
||||
blitter->fDstPtr = &blitter->fMemsetColor;
|
||||
blitter->append_store(&p);
|
||||
fDstPtr = &fMemsetColor;
|
||||
this->append_store(&p);
|
||||
p.run(0,1);
|
||||
|
||||
blitter->fCanMemsetInBlitH = true;
|
||||
fCanMemsetInBlitH = true;
|
||||
}
|
||||
|
||||
return blitter;
|
||||
}
|
||||
|
||||
void SkRasterPipelineBlitter::append_load_d(SkRasterPipeline* p) const {
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1047,6 +1047,16 @@ STAGE(matrix_4x5) {
|
||||
b = B;
|
||||
a = A;
|
||||
}
|
||||
STAGE(matrix_4x3) {
|
||||
auto m = (const float*)ctx;
|
||||
auto X = r,
|
||||
Y = g;
|
||||
|
||||
r = mad(X, m[0], mad(Y, m[4], m[ 8]));
|
||||
g = mad(X, m[1], mad(Y, m[5], m[ 9]));
|
||||
b = mad(X, m[2], mad(Y, m[6], m[10]));
|
||||
a = mad(X, m[3], mad(Y, m[7], m[11]));
|
||||
}
|
||||
STAGE(matrix_perspective) {
|
||||
// N.B. Unlike the other matrix_ stages, this matrix is row-major.
|
||||
auto m = (const float*)ctx;
|
||||
|
Loading…
Reference in New Issue
Block a user