speed up A8 by creating a new entry-point in SkDraw that blits the path's coverage directly into an A8 target, regardless of the previous pixel values.

R=bsalomon@google.com, mtklein@google.com

Review URL: https://codereview.chromium.org/56453002

git-svn-id: http://skia.googlecode.com/svn/trunk@12118 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
reed@google.com 2013-11-04 20:10:33 +00:00
parent 8d8bcf9f88
commit ac9d306a92
11 changed files with 199 additions and 25 deletions

70
bench/CoverageBench.cpp Normal file
View File

@ -0,0 +1,70 @@
/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkBenchmark.h"
#include "SkBitmap.h"
#include "SkCanvas.h"
#include "SkColorPriv.h"
#include "SkDraw.h"
#include "SkMatrix.h"
#include "SkPath.h"
#include "SkRasterClip.h"
class DrawPathBench : public SkBenchmark {
SkPaint fPaint;
SkString fName;
SkPath fPath;
SkRasterClip fRC;
SkBitmap fBitmap;
SkMatrix fIdentity;
SkDraw fDraw;
bool fDrawCoverage;
public:
DrawPathBench(bool drawCoverage) : fDrawCoverage(drawCoverage) {
fPaint.setAntiAlias(true);
fName.printf("draw_coverage_%s", drawCoverage ? "true" : "false");
fPath.moveTo(0, 0);
fPath.quadTo(500, 0, 500, 500);
fPath.quadTo(250, 0, 0, 500);
fBitmap.setConfig(SkBitmap::kA8_Config, 500, 500);
fBitmap.allocPixels();
fIdentity.setIdentity();
fRC.setRect(fPath.getBounds().round());
fDraw.fBitmap = &fBitmap;
fDraw.fMatrix = &fIdentity;
fDraw.fRC = &fRC;
}
protected:
virtual const char* onGetName() SK_OVERRIDE {
return fName.c_str();
}
virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
if (fDrawCoverage) {
for (int i = 0; i < this->getLoops(); ++i) {
fDraw.drawPathCoverage(fPath, fPaint);
}
} else {
for (int i = 0; i < this->getLoops(); ++i) {
fDraw.drawPath(fPath, fPaint);
}
}
}
private:
typedef SkBenchmark INHERITED;
};
///////////////////////////////////////////////////////////////////////////////
DEF_BENCH( return new DrawPathBench(false) )
DEF_BENCH( return new DrawPathBench(true) )

View File

@ -18,6 +18,7 @@
'../bench/ChromeBench.cpp',
'../bench/CmapBench.cpp',
'../bench/ColorFilterBench.cpp',
'../bench/CoverageBench.cpp',
'../bench/DashBench.cpp',
'../bench/DecodeBench.cpp',
'../bench/DeferredCanvasBench.cpp',

View File

@ -43,8 +43,15 @@ public:
* affect the geometry/rasterization, then the pre matrix can just be
* pre-concated with the current matrix.
*/
void drawPath(const SkPath& srcPath, const SkPaint&,
const SkMatrix* prePathMatrix, bool pathIsMutable) const;
void drawPath(const SkPath& path, const SkPaint& paint,
const SkMatrix* prePathMatrix, bool pathIsMutable) const {
this->drawPath(path, paint, prePathMatrix, pathIsMutable, false);
}
void drawPath(const SkPath& path, const SkPaint& paint) const {
this->drawPath(path, paint, NULL, false, false);
}
void drawBitmap(const SkBitmap&, const SkMatrix&, const SkPaint&) const;
void drawSprite(const SkBitmap&, int x, int y, const SkPaint&) const;
void drawText(const char text[], size_t byteLength, SkScalar x,
@ -65,8 +72,14 @@ public:
const uint16_t indices[], int ptCount,
const SkPaint& paint) const;
void drawPath(const SkPath& src, const SkPaint& paint) const {
this->drawPath(src, paint, NULL, false);
/**
* Overwrite the target with the path's coverage (i.e. its mask).
* Will overwrite the entire device, so it need not be zero'd first.
*
* Only device A8 is supported right now.
*/
void drawPathCoverage(const SkPath& src, const SkPaint& paint) const {
this->drawPath(src, paint, NULL, false, true);
}
/** Helper function that creates a mask from a path and an optional maskfilter.
@ -107,6 +120,9 @@ private:
void drawDevMask(const SkMask& mask, const SkPaint&) const;
void drawBitmapAsMask(const SkBitmap&, const SkPaint&) const;
void drawPath(const SkPath&, const SkPaint&, const SkMatrix* preMatrix,
bool pathIsMutable, bool drawCoverage) const;
/**
* Return the current clip bounds, in local coordinates, with slop to account
* for antialiasing or hairlines (i.e. device-bounds outset by 1, and then

View File

@ -730,7 +730,7 @@ struct SK_API SkRect {
/**
* Set the dst rectangle by rounding this rectangle's coordinates to their
* nearest integer values using SkScalarRound.
* nearest integer values using SkScalarRoundToInt.
*/
void round(SkIRect* dst) const {
SkASSERT(dst);
@ -772,6 +772,15 @@ struct SK_API SkRect {
SkScalarFloorToInt(fRight), SkScalarFloorToInt(fBottom));
}
/**
* Return a new SkIRect which is contains the rounded coordinates of this
* rect using SkScalarRoundToInt.
*/
SkIRect round() const {
SkIRect ir;
this->round(&ir);
return ir;
}
/**
* Swap top/bottom or left/right if there are flipped (i.e. if width()

View File

@ -850,14 +850,16 @@ static XferInterp interpret_xfermode(const SkPaint& paint, SkXfermode* xfer,
SkBlitter* SkBlitter::Choose(const SkBitmap& device,
const SkMatrix& matrix,
const SkPaint& origPaint,
void* storage, size_t storageSize) {
void* storage, size_t storageSize,
bool drawCoverage) {
SkASSERT(storageSize == 0 || storage != NULL);
SkBlitter* blitter = NULL;
// which check, in case we're being called by a client with a dummy device
// (e.g. they have a bounder that always aborts the draw)
if (SkBitmap::kNo_Config == device.config()) {
if (SkBitmap::kNo_Config == device.config() ||
(drawCoverage && (SkBitmap::kA8_Config != device.config()))) {
SK_PLACEMENT_NEW(blitter, SkNullBlitter, storage, storageSize);
return blitter;
}
@ -940,6 +942,7 @@ SkBlitter* SkBlitter::Choose(const SkBitmap& device,
return blitter;
}
switch (device.config()) {
case SkBitmap::kA1_Config:
SK_PLACEMENT_NEW_ARGS(blitter, SkA1_Blitter,
@ -947,7 +950,12 @@ SkBlitter* SkBlitter::Choose(const SkBitmap& device,
break;
case SkBitmap::kA8_Config:
if (shader) {
if (drawCoverage) {
SkASSERT(NULL == shader);
SkASSERT(NULL == paint->getXfermode());
SK_PLACEMENT_NEW_ARGS(blitter, SkA8_Coverage_Blitter,
storage, storageSize, (device, *paint));
} else if (shader) {
SK_PLACEMENT_NEW_ARGS(blitter, SkA8_Shader_Blitter,
storage, storageSize, (device, *paint));
} else {

View File

@ -76,7 +76,8 @@ public:
static SkBlitter* Choose(const SkBitmap& device,
const SkMatrix& matrix,
const SkPaint& paint,
void* storage, size_t storageSize);
void* storage, size_t storageSize,
bool drawCoverage = false);
static SkBlitter* ChooseSprite(const SkBitmap& device,
const SkPaint&,

View File

@ -347,3 +347,56 @@ void SkA8_Shader_Blitter::blitMask(const SkMask& mask, const SkIRect& clip) {
alpha += mask.fRowBytes;
}
}
///////////////////////////////////////////////////////////////////////////////
SkA8_Coverage_Blitter::SkA8_Coverage_Blitter(const SkBitmap& device,
const SkPaint& paint) : SkRasterBlitter(device) {
SkASSERT(NULL == paint.getShader());
SkASSERT(NULL == paint.getXfermode());
SkASSERT(NULL == paint.getColorFilter());
}
void SkA8_Coverage_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[],
const int16_t runs[]) {
SkASSERT(0 == x);
uint8_t* device = fDevice.getAddr8(x, y);
SkDEBUGCODE(int totalCount = 0;)
for (;;) {
int count = runs[0];
SkASSERT(count >= 0);
if (count == 0) {
return;
}
memset(device, antialias[0], count);
runs += count;
antialias += count;
device += count;
SkDEBUGCODE(totalCount += count;)
}
SkASSERT(fDevice.width() == totalCount);
}
void SkA8_Coverage_Blitter::blitH(int x, int y, int width) {
sk_throw();
}
void SkA8_Coverage_Blitter::blitV(int x, int y, int height, SkAlpha alpha) {
sk_throw();
}
void SkA8_Coverage_Blitter::blitRect(int x, int y, int width, int height) {
sk_throw();
}
void SkA8_Coverage_Blitter::blitMask(const SkMask&, const SkIRect&) {
sk_throw();
}
const SkBitmap* SkA8_Coverage_Blitter::justAnOpaqueColor(uint32_t*) {
sk_throw();
return NULL;
}

View File

@ -40,6 +40,17 @@ private:
///////////////////////////////////////////////////////////////////////////////
class SkA8_Coverage_Blitter : public SkRasterBlitter {
public:
SkA8_Coverage_Blitter(const SkBitmap& device, const SkPaint& paint);
virtual void blitH(int x, int y, int width) SK_OVERRIDE;
virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) SK_OVERRIDE;
virtual void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE;
virtual void blitRect(int x, int y, int width, int height) SK_OVERRIDE;
virtual void blitMask(const SkMask&, const SkIRect&) SK_OVERRIDE;
virtual const SkBitmap* justAnOpaqueColor(uint32_t*) SK_OVERRIDE;
};
class SkA8_Blitter : public SkRasterBlitter {
public:
SkA8_Blitter(const SkBitmap& device, const SkPaint& paint);

View File

@ -59,9 +59,9 @@ public:
fBlitter = NULL;
}
SkAutoBlitterChoose(const SkBitmap& device, const SkMatrix& matrix,
const SkPaint& paint) {
const SkPaint& paint, bool drawCoverage = false) {
fBlitter = SkBlitter::Choose(device, matrix, paint,
fStorage, sizeof(fStorage));
fStorage, sizeof(fStorage), drawCoverage);
}
~SkAutoBlitterChoose();
@ -1023,7 +1023,8 @@ bool SkDrawTreatAsHairline(const SkPaint& paint, const SkMatrix& matrix,
}
void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& origPaint,
const SkMatrix* prePathMatrix, bool pathIsMutable) const {
const SkMatrix* prePathMatrix, bool pathIsMutable,
bool drawCoverage) const {
SkDEBUGCODE(this->validate();)
// nothing to draw
@ -1112,7 +1113,7 @@ void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& origPaint,
// transform the path into device space
pathPtr->transform(*matrix, devPathPtr);
SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, *paint);
SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, *paint, drawCoverage);
if (paint->getMaskFilter()) {
SkPaint::Style style = doFill ? SkPaint::kFill_Style :

View File

@ -73,20 +73,21 @@ void GrSWMaskHelper::draw(const SkPath& path, const SkStrokeRec& stroke, SkRegio
paint.setStrokeWidth(stroke.getWidth());
}
}
SkXfermode* mode = SkXfermode::Create(op_to_mode(op));
paint.setXfermode(mode);
paint.setAntiAlias(antiAlias);
paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha));
fDraw.drawPath(path, paint);
SkSafeUnref(mode);
if (SkRegion::kReplace_Op == op && 0xFF == alpha) {
SkASSERT(0xFF == paint.getAlpha());
fDraw.drawPathCoverage(path, paint);
} else {
paint.setXfermodeMode(op_to_mode(op));
paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha));
fDraw.drawPath(path, paint);
}
}
bool GrSWMaskHelper::init(const SkIRect& resultBounds,
const SkMatrix* matrix) {
const SkMatrix* matrix,
bool zeroPixels) {
if (NULL != matrix) {
fMatrix = *matrix;
} else {
@ -103,7 +104,9 @@ bool GrSWMaskHelper::init(const SkIRect& resultBounds,
if (!fBM.allocPixels()) {
return false;
}
sk_bzero(fBM.getPixels(), fBM.getSafeSize());
if (zeroPixels) {
sk_bzero(fBM.getPixels(), fBM.getSafeSize());
}
sk_bzero(&fDraw, sizeof(fDraw));
fRasterClip.setRect(bounds);
@ -163,7 +166,7 @@ GrTexture* GrSWMaskHelper::DrawPathMaskToTexture(GrContext* context,
GrSWMaskHelper helper(context);
if (!helper.init(resultBounds, matrix)) {
if (!helper.init(resultBounds, matrix, false)) {
return NULL;
}

View File

@ -48,7 +48,8 @@ public:
// may be accumulated in the helper during creation, "resultBounds"
// allows the caller to specify the region of interest - to limit the
// amount of work.
bool init(const SkIRect& resultBounds, const SkMatrix* matrix);
bool init(const SkIRect& resultBounds, const SkMatrix* matrix,
bool zeroPixels = true);
// Draw a single rect into the accumulation bitmap using the specified op
void draw(const SkRect& rect, SkRegion::Op op,