Retry cleaning up SkLinearBitmapPipeline.
This is mostly dead code. In order to make it truly dead, we need to opt drawing unpremul images into SkRasterPipelineBlitter. They had been handled by SkLinearBitmapPipeline, but can't be draw by SkBitmapProcLegacyShader. Drawing unpremul images is tested by the GM all_variants_8888, which gave us trouble last time around (serialize-8888 drew right, 8888 wrong) but now draws fine. I think this was probably also the root of the revert, drawing some unpremul image in Chrome's tests somewhere. Change-Id: I453f9df44ade807316935921cbae82961e2f08aa Reviewed-on: https://skia-review.googlesource.com/24862 Reviewed-by: Florin Malita <fmalita@chromium.org> Commit-Queue: Mike Klein <mtklein@chromium.org>
This commit is contained in:
parent
edfe3dfb47
commit
0fddb2d7c1
@ -1,202 +0,0 @@
|
||||
/*
|
||||
* 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 <tuple>
|
||||
|
||||
#include "Benchmark.h"
|
||||
#include "Resources.h"
|
||||
#include "SkCpu.h"
|
||||
#include "SkImage.h"
|
||||
#include "SkImage_Base.h"
|
||||
#include "SkNx.h"
|
||||
#include "SkOpts.h"
|
||||
#include "SkPM4fPriv.h"
|
||||
#include "SkString.h"
|
||||
|
||||
#define INNER_LOOPS 10
|
||||
|
||||
static inline void brute_srcover_srgb_srgb_1(uint32_t* dst, uint32_t src) {
|
||||
auto d = Sk4f_fromS32(*dst),
|
||||
s = Sk4f_fromS32( src);
|
||||
*dst = Sk4f_toS32(s + d * (1.0f - s[3]));
|
||||
}
|
||||
|
||||
static inline void srcover_srgb_srgb_1(uint32_t* dst, uint32_t src) {
|
||||
if (src >= 0xFF000000) {
|
||||
*dst = src;
|
||||
return;
|
||||
}
|
||||
brute_srcover_srgb_srgb_1(dst, src);
|
||||
}
|
||||
|
||||
static void brute_force_srcover_srgb_srgb(
|
||||
uint32_t* dst, const uint32_t* const src, int ndst, const int nsrc) {
|
||||
while (ndst > 0) {
|
||||
int n = SkTMin(ndst, nsrc);
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
brute_srcover_srgb_srgb_1(dst++, src[i]);
|
||||
}
|
||||
ndst -= n;
|
||||
}
|
||||
}
|
||||
|
||||
static void trivial_srcover_srgb_srgb(
|
||||
uint32_t* dst, const uint32_t* const src, int ndst, const int nsrc) {
|
||||
while (ndst > 0) {
|
||||
int n = SkTMin(ndst, nsrc);
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
srcover_srgb_srgb_1(dst++, src[i]);
|
||||
}
|
||||
ndst -= n;
|
||||
}
|
||||
}
|
||||
|
||||
static void best_non_simd_srcover_srgb_srgb(
|
||||
uint32_t* dst, const uint32_t* const src, int ndst, const int nsrc) {
|
||||
uint64_t* ddst = reinterpret_cast<uint64_t*>(dst);
|
||||
|
||||
auto srcover_srgb_srgb_2 = [](uint32_t* dst, const uint32_t* src) {
|
||||
srcover_srgb_srgb_1(dst++, *src++);
|
||||
srcover_srgb_srgb_1(dst, *src);
|
||||
};
|
||||
|
||||
while (ndst >0) {
|
||||
int count = SkTMin(ndst, nsrc);
|
||||
ndst -= count;
|
||||
const uint64_t* dsrc = reinterpret_cast<const uint64_t*>(src);
|
||||
const uint64_t* end = dsrc + (count >> 1);
|
||||
do {
|
||||
if ((~*dsrc & 0xFF000000FF000000) == 0) {
|
||||
do {
|
||||
*ddst++ = *dsrc++;
|
||||
} while (dsrc < end && (~*dsrc & 0xFF000000FF000000) == 0);
|
||||
} else if ((*dsrc & 0xFF000000FF000000) == 0) {
|
||||
do {
|
||||
dsrc++;
|
||||
ddst++;
|
||||
} while (dsrc < end && (*dsrc & 0xFF000000FF000000) == 0);
|
||||
} else {
|
||||
srcover_srgb_srgb_2(reinterpret_cast<uint32_t*>(ddst++),
|
||||
reinterpret_cast<const uint32_t*>(dsrc++));
|
||||
}
|
||||
} while (dsrc < end);
|
||||
|
||||
if ((count & 1) != 0) {
|
||||
uint32_t s1;
|
||||
memcpy(&s1, dsrc, 4);
|
||||
srcover_srgb_srgb_1(reinterpret_cast<uint32_t*>(ddst), s1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SrcOverVSkOptsBruteForce {
|
||||
public:
|
||||
static SkString Name() { return SkString{"VSkOptsBruteForce"}; }
|
||||
static void BlendN(uint32_t* dst, const uint32_t* src, int count) {
|
||||
brute_force_srcover_srgb_srgb(dst, src, count, count);
|
||||
}
|
||||
};
|
||||
|
||||
class SrcOverVSkOptsTrivial {
|
||||
public:
|
||||
static SkString Name() { return SkString{"VSkOptsTrivial"}; }
|
||||
static void BlendN(uint32_t* dst, const uint32_t* src, int count) {
|
||||
trivial_srcover_srgb_srgb(dst, src, count, count);
|
||||
}
|
||||
};
|
||||
|
||||
class SrcOverVSkOptsNonSimdCore {
|
||||
public:
|
||||
static SkString Name() { return SkString{"VSkOptsNonSimdCore"}; }
|
||||
static void BlendN(uint32_t* dst, const uint32_t* src, int count) {
|
||||
best_non_simd_srcover_srgb_srgb(dst, src, count, count);
|
||||
}
|
||||
};
|
||||
|
||||
class SrcOverVSkOptsDefault {
|
||||
public:
|
||||
static SkString Name() { return SkString{"VSkOptsDefault"}; }
|
||||
static void BlendN(uint32_t* dst, const uint32_t* src, int count) {
|
||||
SkOpts::srcover_srgb_srgb(dst, src, count, count);
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename Blender>
|
||||
class LinearSrcOverBench : public Benchmark {
|
||||
public:
|
||||
LinearSrcOverBench(const char* fileName) : fFileName(fileName) {
|
||||
fName = "LinearSrcOver_";
|
||||
fName.append(fileName);
|
||||
fName.append(Blender::Name());
|
||||
}
|
||||
|
||||
protected:
|
||||
bool isSuitableFor(Backend backend) override { return backend == kNonRendering_Backend; }
|
||||
const char* onGetName() override { return fName.c_str(); }
|
||||
|
||||
void onPreDraw(SkCanvas*) override {
|
||||
if (!fPixmap.addr()) {
|
||||
sk_sp<SkImage> image = GetResourceAsImage(fFileName.c_str());
|
||||
SkBitmap bm;
|
||||
SkColorSpace* legacyColorSpace = nullptr;
|
||||
if (!as_IB(image)->getROPixels(&bm, legacyColorSpace)) {
|
||||
SkFAIL("Could not read resource");
|
||||
}
|
||||
bm.peekPixels(&fPixmap);
|
||||
fCount = fPixmap.rowBytesAsPixels();
|
||||
fDst.reset(fCount);
|
||||
sk_bzero(fDst.get(), fPixmap.rowBytes());
|
||||
}
|
||||
}
|
||||
|
||||
void onDraw(int loops, SkCanvas*) override {
|
||||
SkASSERT(fPixmap.colorType() == kN32_SkColorType);
|
||||
|
||||
const int width = fPixmap.rowBytesAsPixels();
|
||||
|
||||
for (int i = 0; i < loops * INNER_LOOPS; ++i) {
|
||||
const uint32_t* src = fPixmap.addr32();
|
||||
for (int y = 0; y < fPixmap.height(); y++) {
|
||||
Blender::BlendN(fDst.get(), src, width);
|
||||
src += width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void onPostDraw(SkCanvas*) override {
|
||||
// Make sure the compiler does not optimize away the operation.
|
||||
volatile uint32_t v = 0;
|
||||
for (int i = 0; i < fCount; i++) {
|
||||
v ^= fDst[i];
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
int fCount;
|
||||
SkAutoTArray<uint32_t> fDst;
|
||||
SkString fFileName;
|
||||
SkString fName;
|
||||
SkPixmap fPixmap;
|
||||
|
||||
typedef Benchmark INHERITED;
|
||||
};
|
||||
|
||||
#define BENCHES(fileName) \
|
||||
DEF_BENCH( return new LinearSrcOverBench<SrcOverVSkOptsBruteForce>(fileName); ) \
|
||||
DEF_BENCH( return new LinearSrcOverBench<SrcOverVSkOptsTrivial>(fileName); ) \
|
||||
DEF_BENCH( return new LinearSrcOverBench<SrcOverVSkOptsNonSimdCore>(fileName); ) \
|
||||
DEF_BENCH( return new LinearSrcOverBench<SrcOverVSkOptsDefault>(fileName); )
|
||||
|
||||
BENCHES("yellow_rose.png")
|
||||
BENCHES("baby_tux.png")
|
||||
BENCHES("plane.png")
|
||||
BENCHES("mandrill_512.png")
|
||||
BENCHES("iconstrip.png")
|
@ -1,354 +0,0 @@
|
||||
/*
|
||||
* 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 <memory>
|
||||
#include "Benchmark.h"
|
||||
|
||||
#include "SkArenaAlloc.h"
|
||||
#include "SkBitmapProcShader.h"
|
||||
#include "SkColor.h"
|
||||
#include "SkArenaAlloc.h"
|
||||
#include "SkImage.h"
|
||||
#include "SkLinearBitmapPipeline.h"
|
||||
#include "SkPM4f.h"
|
||||
#include "SkShaderBase.h"
|
||||
|
||||
struct CommonBitmapFPBenchmark : public Benchmark {
|
||||
CommonBitmapFPBenchmark(
|
||||
SkISize srcSize,
|
||||
bool isSRGB,
|
||||
SkMatrix m,
|
||||
bool useBilerp,
|
||||
SkShader::TileMode xTile,
|
||||
SkShader::TileMode yTile)
|
||||
: fIsSRGB(isSRGB)
|
||||
, fM{m}
|
||||
, fUseBilerp{useBilerp}
|
||||
, fXTile{xTile}
|
||||
, fYTile{yTile} {
|
||||
fSrcSize = srcSize;
|
||||
}
|
||||
|
||||
static SkString tileName(const char* pre, SkShader::TileMode mode) {
|
||||
SkString name{pre};
|
||||
switch (mode) {
|
||||
case SkShader::kClamp_TileMode:
|
||||
name.append("Clamp");
|
||||
return name;
|
||||
case SkShader::kRepeat_TileMode:
|
||||
name.append("Repeat");
|
||||
return name;
|
||||
case SkShader::kMirror_TileMode:
|
||||
name.append("Mirror");
|
||||
return name;
|
||||
default:
|
||||
name.append("Unknown");
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
const char* onGetName() override {
|
||||
fName.set("SkBitmapFP");
|
||||
if (fM.getType() & SkMatrix::kPerspective_Mask) {
|
||||
fName.append("Perspective");
|
||||
} else if (fM.getType() & SkMatrix::kAffine_Mask) {
|
||||
fName.append("Affine");
|
||||
} else if (fM.getType() & SkMatrix::kScale_Mask) {
|
||||
fName.append("Scale");
|
||||
} else if (fM.getType() & SkMatrix::kTranslate_Mask) {
|
||||
fName.append("Translate");
|
||||
} else {
|
||||
fName.append("Identity");
|
||||
}
|
||||
|
||||
fName.append(tileName("X", fXTile));
|
||||
fName.append(tileName("Y", fYTile));
|
||||
|
||||
if (fUseBilerp) {
|
||||
fName.append("Filter");
|
||||
} else {
|
||||
fName.append("Nearest");
|
||||
}
|
||||
|
||||
fName.appendf("%s", BaseName().c_str());
|
||||
|
||||
return fName.c_str();
|
||||
}
|
||||
|
||||
void onPreDraw(SkCanvas*) override {
|
||||
int width = fSrcSize.fWidth;
|
||||
int height = fSrcSize.fHeight;
|
||||
fBitmap.reset(new uint32_t[width * height]);
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
fBitmap[y * width + x] = (y << 8) + x + (128<<24);
|
||||
}
|
||||
}
|
||||
|
||||
bool trash = fM.invert(&fInvert);
|
||||
sk_ignore_unused_variable(trash);
|
||||
|
||||
fInfo = SkImageInfo::MakeN32Premul(width, height, fIsSRGB ?
|
||||
SkColorSpace::MakeSRGB() : nullptr);
|
||||
}
|
||||
|
||||
bool isSuitableFor(Backend backend) override {
|
||||
return backend == kNonRendering_Backend;
|
||||
}
|
||||
|
||||
virtual SkString BaseName() = 0;
|
||||
|
||||
SkString fName;
|
||||
SkISize fSrcSize;
|
||||
bool fIsSRGB;
|
||||
SkMatrix fM;
|
||||
SkMatrix fInvert;
|
||||
bool fUseBilerp;
|
||||
SkShader::TileMode fXTile;
|
||||
SkShader::TileMode fYTile;
|
||||
SkImageInfo fInfo;
|
||||
std::unique_ptr<uint32_t[]> fBitmap;
|
||||
};
|
||||
|
||||
struct SkBitmapFPGeneral final : public CommonBitmapFPBenchmark {
|
||||
SkBitmapFPGeneral(
|
||||
SkISize srcSize,
|
||||
bool isSRGB,
|
||||
SkMatrix m,
|
||||
bool useBilerp,
|
||||
SkShader::TileMode xTile,
|
||||
SkShader::TileMode yTile)
|
||||
: CommonBitmapFPBenchmark(srcSize, isSRGB, m, useBilerp, xTile, yTile) { }
|
||||
|
||||
SkString BaseName() override {
|
||||
SkString name;
|
||||
if (fInfo.gammaCloseToSRGB()) {
|
||||
name.set("sRGB");
|
||||
} else {
|
||||
name.set("Linr");
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
void onDraw(int loops, SkCanvas*) override {
|
||||
int width = fSrcSize.fWidth;
|
||||
int height = fSrcSize.fHeight;
|
||||
|
||||
SkAutoTMalloc<SkPM4f> FPbuffer(width*height);
|
||||
|
||||
SkFilterQuality filterQuality;
|
||||
if (fUseBilerp) {
|
||||
filterQuality = SkFilterQuality::kLow_SkFilterQuality;
|
||||
} else {
|
||||
filterQuality = SkFilterQuality::kNone_SkFilterQuality;
|
||||
}
|
||||
|
||||
SkPixmap srcPixmap{fInfo, fBitmap.get(), static_cast<size_t>(4 * width)};
|
||||
|
||||
|
||||
SkSTArenaAlloc<600> allocator(512);
|
||||
SkLinearBitmapPipeline pipeline{
|
||||
fInvert, filterQuality, fXTile, fYTile, SK_ColorBLACK, srcPixmap, &allocator};
|
||||
|
||||
int count = 100;
|
||||
|
||||
for (int n = 0; n < 1000*loops; n++) {
|
||||
pipeline.shadeSpan4f(3, 6, FPbuffer, count);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct SkBitmapFPOrigShader : public CommonBitmapFPBenchmark {
|
||||
SkBitmapFPOrigShader(
|
||||
SkISize srcSize,
|
||||
bool isSRGB,
|
||||
SkMatrix m,
|
||||
bool useBilerp,
|
||||
SkShader::TileMode xTile,
|
||||
SkShader::TileMode yTile)
|
||||
: CommonBitmapFPBenchmark(srcSize, isSRGB, m, useBilerp, xTile, yTile) { }
|
||||
|
||||
SkString BaseName() override {
|
||||
SkString name{"Orig"};
|
||||
return name;
|
||||
}
|
||||
|
||||
void onPreDraw(SkCanvas* c) override {
|
||||
CommonBitmapFPBenchmark::onPreDraw(c);
|
||||
|
||||
fImage = SkImage::MakeRasterCopy(
|
||||
SkPixmap(fInfo, fBitmap.get(), sizeof(SkPMColor) * fSrcSize.fWidth));
|
||||
fPaint.setShader(fImage->makeShader(fXTile, fYTile));
|
||||
if (fUseBilerp) {
|
||||
fPaint.setFilterQuality(SkFilterQuality::kLow_SkFilterQuality);
|
||||
} else {
|
||||
fPaint.setFilterQuality(SkFilterQuality::kNone_SkFilterQuality);
|
||||
}
|
||||
}
|
||||
|
||||
void onPostDraw(SkCanvas*) override {
|
||||
|
||||
}
|
||||
|
||||
void onDraw(int loops, SkCanvas*) override {
|
||||
if (as_SB(fPaint.getShader())->isRasterPipelineOnly()) {
|
||||
return;
|
||||
}
|
||||
int width = fSrcSize.fWidth;
|
||||
int height = fSrcSize.fHeight;
|
||||
|
||||
SkAutoTMalloc<SkPMColor> buffer4b(width*height);
|
||||
|
||||
SkArenaAlloc alloc{0};
|
||||
const SkShaderBase::ContextRec rec(fPaint, fM, nullptr,
|
||||
SkShaderBase::ContextRec::kPMColor_DstType,
|
||||
nullptr);
|
||||
SkShaderBase::Context* ctx = as_SB(fPaint.getShader())->makeContext(rec, &alloc);
|
||||
|
||||
int count = 100;
|
||||
|
||||
for (int n = 0; n < 1000*loops; n++) {
|
||||
ctx->shadeSpan(3, 6, buffer4b, count);
|
||||
}
|
||||
}
|
||||
SkPaint fPaint;
|
||||
sk_sp<SkImage> fImage;
|
||||
};
|
||||
|
||||
const bool gSRGB = true;
|
||||
const bool gLinearRGB = false;
|
||||
static SkISize srcSize = SkISize::Make(120, 100);
|
||||
static SkMatrix mI = SkMatrix::I();
|
||||
DEF_BENCH(return new SkBitmapFPGeneral(
|
||||
srcSize, gSRGB, mI, false,
|
||||
SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);)
|
||||
|
||||
DEF_BENCH(return new SkBitmapFPGeneral(
|
||||
srcSize, gLinearRGB, mI, false,
|
||||
SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);)
|
||||
|
||||
DEF_BENCH(return new SkBitmapFPOrigShader(
|
||||
srcSize, gLinearRGB, mI, false,
|
||||
SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);)
|
||||
|
||||
DEF_BENCH(return new SkBitmapFPGeneral(
|
||||
srcSize, gSRGB, mI, true,
|
||||
SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);)
|
||||
|
||||
DEF_BENCH(return new SkBitmapFPGeneral(
|
||||
srcSize, gLinearRGB, mI, true,
|
||||
SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);)
|
||||
|
||||
DEF_BENCH(return new SkBitmapFPOrigShader(
|
||||
srcSize, gLinearRGB, mI, true,
|
||||
SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);)
|
||||
|
||||
static SkMatrix mS = SkMatrix::MakeScale(2.7f, 2.7f);
|
||||
DEF_BENCH(return new SkBitmapFPGeneral(
|
||||
srcSize, gSRGB, mS, false,
|
||||
SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);)
|
||||
|
||||
DEF_BENCH(return new SkBitmapFPGeneral(
|
||||
srcSize, gLinearRGB, mS, false,
|
||||
SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);)
|
||||
|
||||
DEF_BENCH(return new SkBitmapFPOrigShader(
|
||||
srcSize, gLinearRGB, mS, false,
|
||||
SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);)
|
||||
|
||||
DEF_BENCH(return new SkBitmapFPGeneral(
|
||||
srcSize, gSRGB, mS, true,
|
||||
SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);)
|
||||
|
||||
DEF_BENCH(return new SkBitmapFPGeneral(
|
||||
srcSize, gLinearRGB, mS, true,
|
||||
SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);)
|
||||
|
||||
DEF_BENCH(return new SkBitmapFPOrigShader(
|
||||
srcSize, gLinearRGB, mS, true,
|
||||
SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);)
|
||||
|
||||
// Repeat
|
||||
DEF_BENCH(return new SkBitmapFPGeneral(
|
||||
srcSize, gSRGB, mS, false,
|
||||
SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode);)
|
||||
|
||||
DEF_BENCH(return new SkBitmapFPGeneral(
|
||||
srcSize, gLinearRGB, mS, false,
|
||||
SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode);)
|
||||
|
||||
DEF_BENCH(return new SkBitmapFPOrigShader(
|
||||
srcSize, gLinearRGB, mS, false,
|
||||
SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode);)
|
||||
|
||||
DEF_BENCH(return new SkBitmapFPGeneral(
|
||||
srcSize, gSRGB, mS, true,
|
||||
SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode);)
|
||||
|
||||
DEF_BENCH(return new SkBitmapFPGeneral(
|
||||
srcSize, gLinearRGB, mS, true,
|
||||
SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode);)
|
||||
|
||||
DEF_BENCH(return new SkBitmapFPOrigShader(
|
||||
srcSize, gLinearRGB, mS, true,
|
||||
SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode);)
|
||||
|
||||
static SkMatrix rotate(SkScalar r) {
|
||||
SkMatrix m;
|
||||
m.setRotate(30);
|
||||
return m;
|
||||
}
|
||||
|
||||
static SkMatrix mR = rotate(30);
|
||||
DEF_BENCH(return new SkBitmapFPGeneral(
|
||||
srcSize, gSRGB, mR, false,
|
||||
SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);)
|
||||
|
||||
DEF_BENCH(return new SkBitmapFPGeneral(
|
||||
srcSize, gLinearRGB, mR, false,
|
||||
SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);)
|
||||
|
||||
DEF_BENCH(return new SkBitmapFPOrigShader(
|
||||
srcSize, gLinearRGB, mR, false,
|
||||
SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);)
|
||||
|
||||
DEF_BENCH(return new SkBitmapFPGeneral(
|
||||
srcSize, gSRGB, mR, true,
|
||||
SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);)
|
||||
|
||||
DEF_BENCH(return new SkBitmapFPGeneral(
|
||||
srcSize, gLinearRGB, mR, true,
|
||||
SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);)
|
||||
|
||||
DEF_BENCH(return new SkBitmapFPOrigShader(
|
||||
srcSize, gLinearRGB, mR, true,
|
||||
SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);)
|
||||
|
||||
// Repeat
|
||||
DEF_BENCH(return new SkBitmapFPGeneral(
|
||||
srcSize, gSRGB, mR, false,
|
||||
SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode);)
|
||||
|
||||
DEF_BENCH(return new SkBitmapFPGeneral(
|
||||
srcSize, gLinearRGB, mR, false,
|
||||
SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode);)
|
||||
|
||||
DEF_BENCH(return new SkBitmapFPOrigShader(
|
||||
srcSize, gLinearRGB, mR, false,
|
||||
SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode);)
|
||||
|
||||
DEF_BENCH(return new SkBitmapFPGeneral(
|
||||
srcSize, gSRGB, mR, true,
|
||||
SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode);)
|
||||
|
||||
DEF_BENCH(return new SkBitmapFPGeneral(
|
||||
srcSize, gLinearRGB, mR, true,
|
||||
SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode);)
|
||||
|
||||
DEF_BENCH(return new SkBitmapFPOrigShader(
|
||||
srcSize, gLinearRGB, mR, true,
|
||||
SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode);)
|
@ -107,9 +107,7 @@ bench_sources = [
|
||||
"$_bench/ShadowBench.cpp",
|
||||
"$_bench/ShapesBench.cpp",
|
||||
"$_bench/Sk4fBench.cpp",
|
||||
"$_bench/SkBlend_optsBench.cpp",
|
||||
"$_bench/SkGlyphCacheBench.cpp",
|
||||
"$_bench/SkLinearBitmapPipelineBench.cpp",
|
||||
"$_bench/SKPAnimationBench.cpp",
|
||||
"$_bench/SKPBench.cpp",
|
||||
"$_bench/SkRasterPipelineBench.cpp",
|
||||
|
@ -158,12 +158,6 @@ skia_core_sources = [
|
||||
"$_src/core/SkImageCacherator.h",
|
||||
"$_src/core/SkImageGenerator.cpp",
|
||||
"$_src/core/SkLights.cpp",
|
||||
"$_src/core/SkLinearBitmapPipeline.cpp",
|
||||
"$_src/core/SkLinearBitmapPipeline.h",
|
||||
"$_src/core/SkLinearBitmapPipeline_core.h",
|
||||
"$_src/core/SkLinearBitmapPipeline_matrix.h",
|
||||
"$_src/core/SkLinearBitmapPipeline_tile.h",
|
||||
"$_src/core/SkLinearBitmapPipeline_sample.h",
|
||||
"$_src/core/SkLineClipper.cpp",
|
||||
"$_src/core/SkLiteDL.cpp",
|
||||
"$_src/core/SkLiteRecorder.cpp",
|
||||
|
@ -201,7 +201,6 @@ tests_sources = [
|
||||
"$_tests/SizeTest.cpp",
|
||||
"$_tests/Sk4x4fTest.cpp",
|
||||
"$_tests/SkBase64Test.cpp",
|
||||
"$_tests/SkBlend_optsTest.cpp",
|
||||
"$_tests/skbug5221.cpp",
|
||||
"$_tests/skbug6389.cpp",
|
||||
"$_tests/skbug6653.cpp",
|
||||
@ -209,7 +208,6 @@ tests_sources = [
|
||||
"$_tests/SkDOMTest.cpp",
|
||||
"$_tests/SkFixed15Test.cpp",
|
||||
"$_tests/SkImageTest.cpp",
|
||||
"$_tests/SkLinearBitmapPipelineTest.cpp",
|
||||
"$_tests/SkLiteDLTest.cpp",
|
||||
"$_tests/SkNxTest.cpp",
|
||||
"$_tests/SkPEGTest.cpp",
|
||||
|
@ -1,669 +0,0 @@
|
||||
/*
|
||||
* 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 "SkLinearBitmapPipeline.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <tuple>
|
||||
|
||||
#include "SkArenaAlloc.h"
|
||||
#include "SkLinearBitmapPipeline_core.h"
|
||||
#include "SkLinearBitmapPipeline_matrix.h"
|
||||
#include "SkLinearBitmapPipeline_tile.h"
|
||||
#include "SkLinearBitmapPipeline_sample.h"
|
||||
#include "SkNx.h"
|
||||
#include "SkOpts.h"
|
||||
#include "SkPM4f.h"
|
||||
|
||||
namespace {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Matrix Stage
|
||||
// PointProcessor uses a strategy to help complete the work of the different stages. The strategy
|
||||
// must implement the following methods:
|
||||
// * processPoints(xs, ys) - must mutate the xs and ys for the stage.
|
||||
// * maybeProcessSpan(span, next) - This represents a horizontal series of pixels
|
||||
// to work over.
|
||||
// span - encapsulation of span.
|
||||
// next - a pointer to the next stage.
|
||||
// maybeProcessSpan - returns false if it can not process the span and needs to fallback to
|
||||
// point lists for processing.
|
||||
template<typename Strategy, typename Next>
|
||||
class MatrixStage final : public SkLinearBitmapPipeline::PointProcessorInterface {
|
||||
public:
|
||||
template <typename... Args>
|
||||
MatrixStage(Next* next, Args&&... args)
|
||||
: fNext{next}
|
||||
, fStrategy{std::forward<Args>(args)...}{ }
|
||||
|
||||
MatrixStage(Next* next, MatrixStage* stage)
|
||||
: fNext{next}
|
||||
, fStrategy{stage->fStrategy} { }
|
||||
|
||||
void SK_VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override {
|
||||
fStrategy.processPoints(&xs, &ys);
|
||||
fNext->pointListFew(n, xs, ys);
|
||||
}
|
||||
|
||||
void SK_VECTORCALL pointList4(Sk4s xs, Sk4s ys) override {
|
||||
fStrategy.processPoints(&xs, &ys);
|
||||
fNext->pointList4(xs, ys);
|
||||
}
|
||||
|
||||
// The span you pass must not be empty.
|
||||
void pointSpan(Span span) override {
|
||||
SkASSERT(!span.isEmpty());
|
||||
if (!fStrategy.maybeProcessSpan(span, fNext)) {
|
||||
span_fallback(span, this);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Next* const fNext;
|
||||
Strategy fStrategy;
|
||||
};
|
||||
|
||||
template <typename Next = SkLinearBitmapPipeline::PointProcessorInterface>
|
||||
using TranslateMatrix = MatrixStage<TranslateMatrixStrategy, Next>;
|
||||
|
||||
template <typename Next = SkLinearBitmapPipeline::PointProcessorInterface>
|
||||
using ScaleMatrix = MatrixStage<ScaleMatrixStrategy, Next>;
|
||||
|
||||
template <typename Next = SkLinearBitmapPipeline::PointProcessorInterface>
|
||||
using AffineMatrix = MatrixStage<AffineMatrixStrategy, Next>;
|
||||
|
||||
template <typename Next = SkLinearBitmapPipeline::PointProcessorInterface>
|
||||
using PerspectiveMatrix = MatrixStage<PerspectiveMatrixStrategy, Next>;
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Tile Stage
|
||||
|
||||
template<typename XStrategy, typename YStrategy, typename Next>
|
||||
class CombinedTileStage final : public SkLinearBitmapPipeline::PointProcessorInterface {
|
||||
public:
|
||||
CombinedTileStage(Next* next, SkISize dimensions)
|
||||
: fNext{next}
|
||||
, fXStrategy{dimensions.width()}
|
||||
, fYStrategy{dimensions.height()}{ }
|
||||
|
||||
CombinedTileStage(Next* next, CombinedTileStage* stage)
|
||||
: fNext{next}
|
||||
, fXStrategy{stage->fXStrategy}
|
||||
, fYStrategy{stage->fYStrategy} { }
|
||||
|
||||
void SK_VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override {
|
||||
fXStrategy.tileXPoints(&xs);
|
||||
fYStrategy.tileYPoints(&ys);
|
||||
fNext->pointListFew(n, xs, ys);
|
||||
}
|
||||
|
||||
void SK_VECTORCALL pointList4(Sk4s xs, Sk4s ys) override {
|
||||
fXStrategy.tileXPoints(&xs);
|
||||
fYStrategy.tileYPoints(&ys);
|
||||
fNext->pointList4(xs, ys);
|
||||
}
|
||||
|
||||
// The span you pass must not be empty.
|
||||
void pointSpan(Span span) override {
|
||||
SkASSERT(!span.isEmpty());
|
||||
SkPoint start; SkScalar length; int count;
|
||||
std::tie(start, length, count) = span;
|
||||
|
||||
if (span.count() == 1) {
|
||||
// DANGER:
|
||||
// The explicit casts from float to Sk4f are not usually necessary, but are here to
|
||||
// work around an MSVC 2015u2 c++ code generation bug. This is tracked using skia bug
|
||||
// 5566.
|
||||
this->pointListFew(1, Sk4f{span.startX()}, Sk4f{span.startY()});
|
||||
return;
|
||||
}
|
||||
|
||||
SkScalar x = X(start);
|
||||
SkScalar y = fYStrategy.tileY(Y(start));
|
||||
Span yAdjustedSpan{{x, y}, length, count};
|
||||
|
||||
if (!fXStrategy.maybeProcessSpan(yAdjustedSpan, fNext)) {
|
||||
span_fallback(span, this);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Next* const fNext;
|
||||
XStrategy fXStrategy;
|
||||
YStrategy fYStrategy;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Specialized Samplers
|
||||
|
||||
// RGBA8888UnitRepeatSrc - A sampler that takes advantage of the fact the the src and destination
|
||||
// are the same format and do not need in transformations in pixel space. Therefore, there is no
|
||||
// need to convert them to HiFi pixel format.
|
||||
class RGBA8888UnitRepeatSrc final : public SkLinearBitmapPipeline::SampleProcessorInterface,
|
||||
public SkLinearBitmapPipeline::DestinationInterface {
|
||||
public:
|
||||
RGBA8888UnitRepeatSrc(const uint32_t* src, int32_t width)
|
||||
: fSrc{src}, fWidth{width} { }
|
||||
|
||||
void SK_VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override {
|
||||
SkASSERT(fDest + n <= fEnd);
|
||||
// At this point xs and ys should be >= 0, so trunc is the same as floor.
|
||||
Sk4i iXs = SkNx_cast<int>(xs);
|
||||
Sk4i iYs = SkNx_cast<int>(ys);
|
||||
|
||||
if (n >= 1) *fDest++ = *this->pixelAddress(iXs[0], iYs[0]);
|
||||
if (n >= 2) *fDest++ = *this->pixelAddress(iXs[1], iYs[1]);
|
||||
if (n >= 3) *fDest++ = *this->pixelAddress(iXs[2], iYs[2]);
|
||||
}
|
||||
|
||||
void SK_VECTORCALL pointList4(Sk4s xs, Sk4s ys) override {
|
||||
SkASSERT(fDest + 4 <= fEnd);
|
||||
Sk4i iXs = SkNx_cast<int>(xs);
|
||||
Sk4i iYs = SkNx_cast<int>(ys);
|
||||
*fDest++ = *this->pixelAddress(iXs[0], iYs[0]);
|
||||
*fDest++ = *this->pixelAddress(iXs[1], iYs[1]);
|
||||
*fDest++ = *this->pixelAddress(iXs[2], iYs[2]);
|
||||
*fDest++ = *this->pixelAddress(iXs[3], iYs[3]);
|
||||
}
|
||||
|
||||
void pointSpan(Span span) override {
|
||||
SkASSERT(fDest + span.count() <= fEnd);
|
||||
if (span.length() != 0.0f) {
|
||||
int32_t x = SkScalarTruncToInt(span.startX());
|
||||
int32_t y = SkScalarTruncToInt(span.startY());
|
||||
const uint32_t* src = this->pixelAddress(x, y);
|
||||
memmove(fDest, src, span.count() * sizeof(uint32_t));
|
||||
fDest += span.count();
|
||||
}
|
||||
}
|
||||
|
||||
void repeatSpan(Span span, int32_t repeatCount) override {
|
||||
SkASSERT(fDest + span.count() * repeatCount <= fEnd);
|
||||
|
||||
int32_t x = SkScalarTruncToInt(span.startX());
|
||||
int32_t y = SkScalarTruncToInt(span.startY());
|
||||
const uint32_t* src = this->pixelAddress(x, y);
|
||||
uint32_t* dest = fDest;
|
||||
while (repeatCount --> 0) {
|
||||
memmove(dest, src, span.count() * sizeof(uint32_t));
|
||||
dest += span.count();
|
||||
}
|
||||
fDest = dest;
|
||||
}
|
||||
|
||||
void setDestination(void* dst, int count) override {
|
||||
fDest = static_cast<uint32_t*>(dst);
|
||||
fEnd = fDest + count;
|
||||
}
|
||||
|
||||
private:
|
||||
const uint32_t* pixelAddress(int32_t x, int32_t y) {
|
||||
return &fSrc[fWidth * y + x];
|
||||
}
|
||||
const uint32_t* const fSrc;
|
||||
const int32_t fWidth;
|
||||
uint32_t* fDest;
|
||||
uint32_t* fEnd;
|
||||
};
|
||||
|
||||
// RGBA8888UnitRepeatSrc - A sampler that takes advantage of the fact the the src and destination
|
||||
// are the same format and do not need in transformations in pixel space. Therefore, there is no
|
||||
// need to convert them to HiFi pixel format.
|
||||
class RGBA8888UnitRepeatSrcOver final : public SkLinearBitmapPipeline::SampleProcessorInterface,
|
||||
public SkLinearBitmapPipeline::DestinationInterface {
|
||||
public:
|
||||
RGBA8888UnitRepeatSrcOver(const uint32_t* src, int32_t width)
|
||||
: fSrc{src}, fWidth{width} { }
|
||||
|
||||
void SK_VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override {
|
||||
SkASSERT(fDest + n <= fEnd);
|
||||
// At this point xs and ys should be >= 0, so trunc is the same as floor.
|
||||
Sk4i iXs = SkNx_cast<int>(xs);
|
||||
Sk4i iYs = SkNx_cast<int>(ys);
|
||||
|
||||
if (n >= 1) blendPixelAt(iXs[0], iYs[0]);
|
||||
if (n >= 2) blendPixelAt(iXs[1], iYs[1]);
|
||||
if (n >= 3) blendPixelAt(iXs[2], iYs[2]);
|
||||
}
|
||||
|
||||
void SK_VECTORCALL pointList4(Sk4s xs, Sk4s ys) override {
|
||||
SkASSERT(fDest + 4 <= fEnd);
|
||||
Sk4i iXs = SkNx_cast<int>(xs);
|
||||
Sk4i iYs = SkNx_cast<int>(ys);
|
||||
blendPixelAt(iXs[0], iYs[0]);
|
||||
blendPixelAt(iXs[1], iYs[1]);
|
||||
blendPixelAt(iXs[2], iYs[2]);
|
||||
blendPixelAt(iXs[3], iYs[3]);
|
||||
}
|
||||
|
||||
void pointSpan(Span span) override {
|
||||
if (span.length() != 0.0f) {
|
||||
this->repeatSpan(span, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void repeatSpan(Span span, int32_t repeatCount) override {
|
||||
SkASSERT(fDest + span.count() * repeatCount <= fEnd);
|
||||
SkASSERT(span.count() > 0);
|
||||
SkASSERT(repeatCount > 0);
|
||||
|
||||
int32_t x = (int32_t)span.startX();
|
||||
int32_t y = (int32_t)span.startY();
|
||||
const uint32_t* beginSpan = this->pixelAddress(x, y);
|
||||
|
||||
SkOpts::srcover_srgb_srgb(fDest, beginSpan, span.count() * repeatCount, span.count());
|
||||
|
||||
fDest += span.count() * repeatCount;
|
||||
|
||||
SkASSERT(fDest <= fEnd);
|
||||
}
|
||||
|
||||
void setDestination(void* dst, int count) override {
|
||||
SkASSERT(count > 0);
|
||||
fDest = static_cast<uint32_t*>(dst);
|
||||
fEnd = fDest + count;
|
||||
}
|
||||
|
||||
private:
|
||||
const uint32_t* pixelAddress(int32_t x, int32_t y) {
|
||||
return &fSrc[fWidth * y + x];
|
||||
}
|
||||
|
||||
void blendPixelAt(int32_t x, int32_t y) {
|
||||
const uint32_t* src = this->pixelAddress(x, y);
|
||||
SkOpts::srcover_srgb_srgb(fDest, src, 1, 1);
|
||||
fDest += 1;
|
||||
}
|
||||
|
||||
const uint32_t* const fSrc;
|
||||
const int32_t fWidth;
|
||||
uint32_t* fDest;
|
||||
uint32_t* fEnd;
|
||||
};
|
||||
|
||||
using Blender = SkLinearBitmapPipeline::BlendProcessorInterface;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Pixel Blender Stage
|
||||
template <SkAlphaType alphaType>
|
||||
class SrcFPPixel final : public Blender {
|
||||
public:
|
||||
SrcFPPixel(float postAlpha) : fPostAlpha{postAlpha} { }
|
||||
SrcFPPixel(const SrcFPPixel& Blender) : fPostAlpha(Blender.fPostAlpha) {}
|
||||
void SK_VECTORCALL blendPixel(Sk4f pixel) override {
|
||||
SkASSERT(fDst + 1 <= fEnd );
|
||||
this->srcPixel(fDst, pixel, 0);
|
||||
fDst += 1;
|
||||
}
|
||||
|
||||
void SK_VECTORCALL blend4Pixels(Sk4f p0, Sk4f p1, Sk4f p2, Sk4f p3) override {
|
||||
SkASSERT(fDst + 4 <= fEnd);
|
||||
SkPM4f* dst = fDst;
|
||||
this->srcPixel(dst, p0, 0);
|
||||
this->srcPixel(dst, p1, 1);
|
||||
this->srcPixel(dst, p2, 2);
|
||||
this->srcPixel(dst, p3, 3);
|
||||
fDst += 4;
|
||||
}
|
||||
|
||||
void setDestination(void* dst, int count) override {
|
||||
fDst = static_cast<SkPM4f*>(dst);
|
||||
fEnd = fDst + count;
|
||||
}
|
||||
|
||||
private:
|
||||
void SK_VECTORCALL srcPixel(SkPM4f* dst, Sk4f pixel, int index) {
|
||||
check_pixel(pixel);
|
||||
|
||||
Sk4f newPixel = pixel;
|
||||
if (alphaType == kUnpremul_SkAlphaType) {
|
||||
newPixel = Premultiply(pixel);
|
||||
}
|
||||
newPixel = newPixel * fPostAlpha;
|
||||
newPixel.store(dst + index);
|
||||
}
|
||||
static Sk4f SK_VECTORCALL Premultiply(Sk4f pixel) {
|
||||
float alpha = pixel[3];
|
||||
return pixel * Sk4f{alpha, alpha, alpha, 1.0f};
|
||||
}
|
||||
|
||||
SkPM4f* fDst;
|
||||
SkPM4f* fEnd;
|
||||
float fPostAlpha;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// SkLinearBitmapPipeline
|
||||
SkLinearBitmapPipeline::~SkLinearBitmapPipeline() {}
|
||||
|
||||
SkLinearBitmapPipeline::SkLinearBitmapPipeline(
|
||||
const SkMatrix& inverse,
|
||||
SkFilterQuality filterQuality,
|
||||
SkShader::TileMode xTile, SkShader::TileMode yTile,
|
||||
SkColor paintColor,
|
||||
const SkPixmap& srcPixmap,
|
||||
SkArenaAlloc* allocator)
|
||||
{
|
||||
SkISize dimensions = srcPixmap.info().dimensions();
|
||||
const SkImageInfo& srcImageInfo = srcPixmap.info();
|
||||
|
||||
SkMatrix adjustedInverse = inverse;
|
||||
if (filterQuality == kNone_SkFilterQuality) {
|
||||
if (inverse.getScaleX() >= 0.0f) {
|
||||
adjustedInverse.setTranslateX(
|
||||
nextafterf(inverse.getTranslateX(), std::floor(inverse.getTranslateX())));
|
||||
}
|
||||
if (inverse.getScaleY() >= 0.0f) {
|
||||
adjustedInverse.setTranslateY(
|
||||
nextafterf(inverse.getTranslateY(), std::floor(inverse.getTranslateY())));
|
||||
}
|
||||
}
|
||||
|
||||
SkScalar dx = adjustedInverse.getScaleX();
|
||||
|
||||
// If it is an index 8 color type, the sampler converts to unpremul for better fidelity.
|
||||
SkAlphaType alphaType = srcImageInfo.alphaType();
|
||||
|
||||
float postAlpha = SkColorGetA(paintColor) * (1.0f / 255.0f);
|
||||
// As the stages are built, the chooser function may skip a stage. For example, with the
|
||||
// identity matrix, the matrix stage is skipped, and the tilerStage is the first stage.
|
||||
auto blenderStage = this->chooseBlenderForShading(alphaType, postAlpha, allocator);
|
||||
auto samplerStage = this->chooseSampler(
|
||||
blenderStage, filterQuality, xTile, yTile, srcPixmap, paintColor, allocator);
|
||||
auto tilerStage = this->chooseTiler(
|
||||
samplerStage, dimensions, xTile, yTile, filterQuality, dx, allocator);
|
||||
fFirstStage = this->chooseMatrix(tilerStage, adjustedInverse, allocator);
|
||||
fLastStage = blenderStage;
|
||||
}
|
||||
|
||||
SkLinearBitmapPipeline::SkLinearBitmapPipeline(
|
||||
const SkLinearBitmapPipeline& pipeline,
|
||||
const SkPixmap& srcPixmap,
|
||||
SkBlendMode mode,
|
||||
const SkImageInfo& dstInfo,
|
||||
SkArenaAlloc* allocator)
|
||||
{
|
||||
SkASSERT(mode == SkBlendMode::kSrc || mode == SkBlendMode::kSrcOver);
|
||||
SkASSERT(srcPixmap.info().colorType() == dstInfo.colorType()
|
||||
&& srcPixmap.info().colorType() == kRGBA_8888_SkColorType);
|
||||
|
||||
SampleProcessorInterface* sampleStage;
|
||||
if (mode == SkBlendMode::kSrc) {
|
||||
auto sampler = allocator->make<RGBA8888UnitRepeatSrc>(
|
||||
srcPixmap.writable_addr32(0, 0), srcPixmap.rowBytes() / 4);
|
||||
sampleStage = sampler;
|
||||
fLastStage = sampler;
|
||||
} else {
|
||||
auto sampler = allocator->make<RGBA8888UnitRepeatSrcOver>(
|
||||
srcPixmap.writable_addr32(0, 0), srcPixmap.rowBytes() / 4);
|
||||
sampleStage = sampler;
|
||||
fLastStage = sampler;
|
||||
}
|
||||
|
||||
auto tilerStage = pipeline.fTileStageCloner(sampleStage, allocator);
|
||||
auto matrixStage = pipeline.fMatrixStageCloner(tilerStage, allocator);
|
||||
fFirstStage = matrixStage;
|
||||
}
|
||||
|
||||
void SkLinearBitmapPipeline::shadeSpan4f(int x, int y, SkPM4f* dst, int count) {
|
||||
SkASSERT(count > 0);
|
||||
this->blitSpan(x, y, dst, count);
|
||||
}
|
||||
|
||||
void SkLinearBitmapPipeline::blitSpan(int x, int y, void* dst, int count) {
|
||||
SkASSERT(count > 0);
|
||||
fLastStage->setDestination(dst, count);
|
||||
|
||||
// The count and length arguments start out in a precise relation in order to keep the
|
||||
// math correct through the different stages. Count is the number of pixel to produce.
|
||||
// Since the code samples at pixel centers, length is the distance from the center of the
|
||||
// first pixel to the center of the last pixel. This implies that length is count-1.
|
||||
fFirstStage->pointSpan(Span{{x + 0.5f, y + 0.5f}, count - 1.0f, count});
|
||||
}
|
||||
|
||||
SkLinearBitmapPipeline::PointProcessorInterface*
|
||||
SkLinearBitmapPipeline::chooseMatrix(
|
||||
PointProcessorInterface* next,
|
||||
const SkMatrix& inverse,
|
||||
SkArenaAlloc* allocator)
|
||||
{
|
||||
if (inverse.hasPerspective()) {
|
||||
auto matrixStage = allocator->make<PerspectiveMatrix<>>(
|
||||
next,
|
||||
SkVector{inverse.getTranslateX(), inverse.getTranslateY()},
|
||||
SkVector{inverse.getScaleX(), inverse.getScaleY()},
|
||||
SkVector{inverse.getSkewX(), inverse.getSkewY()},
|
||||
SkVector{inverse.getPerspX(), inverse.getPerspY()},
|
||||
inverse.get(SkMatrix::kMPersp2));
|
||||
fMatrixStageCloner =
|
||||
[matrixStage](PointProcessorInterface* cloneNext, SkArenaAlloc* memory) {
|
||||
return memory->make<PerspectiveMatrix<>>(cloneNext, matrixStage);
|
||||
};
|
||||
return matrixStage;
|
||||
} else if (inverse.getSkewX() != 0.0f || inverse.getSkewY() != 0.0f) {
|
||||
auto matrixStage = allocator->make<AffineMatrix<>>(
|
||||
next,
|
||||
SkVector{inverse.getTranslateX(), inverse.getTranslateY()},
|
||||
SkVector{inverse.getScaleX(), inverse.getScaleY()},
|
||||
SkVector{inverse.getSkewX(), inverse.getSkewY()});
|
||||
fMatrixStageCloner =
|
||||
[matrixStage](PointProcessorInterface* cloneNext, SkArenaAlloc* memory) {
|
||||
return memory->make<AffineMatrix<>>(cloneNext, matrixStage);
|
||||
};
|
||||
return matrixStage;
|
||||
} else if (inverse.getScaleX() != 1.0f || inverse.getScaleY() != 1.0f) {
|
||||
auto matrixStage = allocator->make<ScaleMatrix<>>(
|
||||
next,
|
||||
SkVector{inverse.getTranslateX(), inverse.getTranslateY()},
|
||||
SkVector{inverse.getScaleX(), inverse.getScaleY()});
|
||||
fMatrixStageCloner =
|
||||
[matrixStage](PointProcessorInterface* cloneNext, SkArenaAlloc* memory) {
|
||||
return memory->make<ScaleMatrix<>>(cloneNext, matrixStage);
|
||||
};
|
||||
return matrixStage;
|
||||
} else if (inverse.getTranslateX() != 0.0f || inverse.getTranslateY() != 0.0f) {
|
||||
auto matrixStage = allocator->make<TranslateMatrix<>>(
|
||||
next,
|
||||
SkVector{inverse.getTranslateX(), inverse.getTranslateY()});
|
||||
fMatrixStageCloner =
|
||||
[matrixStage](PointProcessorInterface* cloneNext, SkArenaAlloc* memory) {
|
||||
return memory->make<TranslateMatrix<>>(cloneNext, matrixStage);
|
||||
};
|
||||
return matrixStage;
|
||||
} else {
|
||||
fMatrixStageCloner = [](PointProcessorInterface* cloneNext, SkArenaAlloc* memory) {
|
||||
return cloneNext;
|
||||
};
|
||||
return next;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Tiler>
|
||||
SkLinearBitmapPipeline::PointProcessorInterface* SkLinearBitmapPipeline::createTiler(
|
||||
SampleProcessorInterface* next,
|
||||
SkISize dimensions,
|
||||
SkArenaAlloc* allocator)
|
||||
{
|
||||
auto tilerStage = allocator->make<Tiler>(next, dimensions);
|
||||
fTileStageCloner =
|
||||
[tilerStage](SampleProcessorInterface* cloneNext,
|
||||
SkArenaAlloc* memory) -> PointProcessorInterface* {
|
||||
return memory->make<Tiler>(cloneNext, tilerStage);
|
||||
};
|
||||
return tilerStage;
|
||||
}
|
||||
|
||||
template <typename XStrategy>
|
||||
SkLinearBitmapPipeline::PointProcessorInterface* SkLinearBitmapPipeline::chooseTilerYMode(
|
||||
SampleProcessorInterface* next,
|
||||
SkShader::TileMode yMode,
|
||||
SkISize dimensions,
|
||||
SkArenaAlloc* allocator)
|
||||
{
|
||||
switch (yMode) {
|
||||
case SkShader::kClamp_TileMode: {
|
||||
using Tiler = CombinedTileStage<XStrategy, YClampStrategy, SampleProcessorInterface>;
|
||||
return this->createTiler<Tiler>(next, dimensions, allocator);
|
||||
}
|
||||
case SkShader::kRepeat_TileMode: {
|
||||
using Tiler = CombinedTileStage<XStrategy, YRepeatStrategy, SampleProcessorInterface>;
|
||||
return this->createTiler<Tiler>(next, dimensions, allocator);
|
||||
}
|
||||
case SkShader::kMirror_TileMode: {
|
||||
using Tiler = CombinedTileStage<XStrategy, YMirrorStrategy, SampleProcessorInterface>;
|
||||
return this->createTiler<Tiler>(next, dimensions, allocator);
|
||||
}
|
||||
}
|
||||
|
||||
// Should never get here.
|
||||
SkFAIL("Not all Y tile cases covered.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SkLinearBitmapPipeline::PointProcessorInterface* SkLinearBitmapPipeline::chooseTiler(
|
||||
SampleProcessorInterface* next,
|
||||
SkISize dimensions,
|
||||
SkShader::TileMode xMode,
|
||||
SkShader::TileMode yMode,
|
||||
SkFilterQuality filterQuality,
|
||||
SkScalar dx,
|
||||
SkArenaAlloc* allocator)
|
||||
{
|
||||
switch (xMode) {
|
||||
case SkShader::kClamp_TileMode:
|
||||
return this->chooseTilerYMode<XClampStrategy>(next, yMode, dimensions, allocator);
|
||||
case SkShader::kRepeat_TileMode:
|
||||
if (dx == 1.0f && filterQuality == kNone_SkFilterQuality) {
|
||||
return this->chooseTilerYMode<XRepeatUnitScaleStrategy>(
|
||||
next, yMode, dimensions, allocator);
|
||||
} else {
|
||||
return this->chooseTilerYMode<XRepeatStrategy>(
|
||||
next, yMode, dimensions, allocator);
|
||||
}
|
||||
case SkShader::kMirror_TileMode:
|
||||
return this->chooseTilerYMode<XMirrorStrategy>(next, yMode, dimensions, allocator);
|
||||
}
|
||||
|
||||
// Should never get here.
|
||||
SkFAIL("Not all X tile cases covered.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <SkColorType colorType>
|
||||
SkLinearBitmapPipeline::PixelAccessorInterface*
|
||||
SkLinearBitmapPipeline::chooseSpecificAccessor(
|
||||
const SkPixmap& srcPixmap,
|
||||
SkArenaAlloc* allocator)
|
||||
{
|
||||
if (srcPixmap.info().gammaCloseToSRGB()) {
|
||||
using Accessor = PixelAccessor<colorType, kSRGB_SkGammaType>;
|
||||
return allocator->make<Accessor>(srcPixmap);
|
||||
} else {
|
||||
using Accessor = PixelAccessor<colorType, kLinear_SkGammaType>;
|
||||
return allocator->make<Accessor>(srcPixmap);
|
||||
}
|
||||
}
|
||||
|
||||
SkLinearBitmapPipeline::PixelAccessorInterface* SkLinearBitmapPipeline::choosePixelAccessor(
|
||||
const SkPixmap& srcPixmap,
|
||||
const SkColor A8TintColor,
|
||||
SkArenaAlloc* allocator)
|
||||
{
|
||||
const SkImageInfo& imageInfo = srcPixmap.info();
|
||||
|
||||
switch (imageInfo.colorType()) {
|
||||
case kAlpha_8_SkColorType: {
|
||||
using Accessor = PixelAccessor<kAlpha_8_SkColorType, kLinear_SkGammaType>;
|
||||
return allocator->make<Accessor>(srcPixmap, A8TintColor);
|
||||
}
|
||||
case kARGB_4444_SkColorType:
|
||||
return this->chooseSpecificAccessor<kARGB_4444_SkColorType>(srcPixmap, allocator);
|
||||
case kRGB_565_SkColorType:
|
||||
return this->chooseSpecificAccessor<kRGB_565_SkColorType>(srcPixmap, allocator);
|
||||
case kRGBA_8888_SkColorType:
|
||||
return this->chooseSpecificAccessor<kRGBA_8888_SkColorType>(srcPixmap, allocator);
|
||||
case kBGRA_8888_SkColorType:
|
||||
return this->chooseSpecificAccessor<kBGRA_8888_SkColorType>(srcPixmap, allocator);
|
||||
case kGray_8_SkColorType:
|
||||
return this->chooseSpecificAccessor<kGray_8_SkColorType>(srcPixmap, allocator);
|
||||
case kRGBA_F16_SkColorType: {
|
||||
using Accessor = PixelAccessor<kRGBA_F16_SkColorType, kLinear_SkGammaType>;
|
||||
return allocator->make<Accessor>(srcPixmap);
|
||||
}
|
||||
default:
|
||||
// Should never get here.
|
||||
SkFAIL("Pixel source not supported.");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
SkLinearBitmapPipeline::SampleProcessorInterface* SkLinearBitmapPipeline::chooseSampler(
|
||||
Blender* next,
|
||||
SkFilterQuality filterQuality,
|
||||
SkShader::TileMode xTile, SkShader::TileMode yTile,
|
||||
const SkPixmap& srcPixmap,
|
||||
const SkColor A8TintColor,
|
||||
SkArenaAlloc* allocator)
|
||||
{
|
||||
const SkImageInfo& imageInfo = srcPixmap.info();
|
||||
SkISize dimensions = imageInfo.dimensions();
|
||||
|
||||
// Special case samplers with fully expanded templates
|
||||
if (imageInfo.gammaCloseToSRGB()) {
|
||||
if (filterQuality == kNone_SkFilterQuality) {
|
||||
switch (imageInfo.colorType()) {
|
||||
case kN32_SkColorType: {
|
||||
using Sampler =
|
||||
NearestNeighborSampler<
|
||||
PixelAccessor<kN32_SkColorType, kSRGB_SkGammaType>, Blender>;
|
||||
return allocator->make<Sampler>(next, srcPixmap);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (imageInfo.colorType()) {
|
||||
case kN32_SkColorType: {
|
||||
using Sampler =
|
||||
BilerpSampler<
|
||||
PixelAccessor<kN32_SkColorType, kSRGB_SkGammaType>, Blender>;
|
||||
return allocator->make<Sampler>(next, dimensions, xTile, yTile, srcPixmap);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto pixelAccessor = this->choosePixelAccessor(srcPixmap, A8TintColor, allocator);
|
||||
// General cases.
|
||||
if (filterQuality == kNone_SkFilterQuality) {
|
||||
using Sampler = NearestNeighborSampler<PixelAccessorShim, Blender>;
|
||||
return allocator->make<Sampler>(next, pixelAccessor);
|
||||
} else {
|
||||
using Sampler = BilerpSampler<PixelAccessorShim, Blender>;
|
||||
return allocator->make<Sampler>(next, dimensions, xTile, yTile, pixelAccessor);
|
||||
}
|
||||
}
|
||||
|
||||
Blender* SkLinearBitmapPipeline::chooseBlenderForShading(
|
||||
SkAlphaType alphaType,
|
||||
float postAlpha,
|
||||
SkArenaAlloc* allocator)
|
||||
{
|
||||
if (alphaType == kUnpremul_SkAlphaType) {
|
||||
return allocator->make<SrcFPPixel<kUnpremul_SkAlphaType>>(postAlpha);
|
||||
} else {
|
||||
// kOpaque_SkAlphaType is treated the same as kPremul_SkAlphaType
|
||||
return allocator->make<SrcFPPixel<kPremul_SkAlphaType>>(postAlpha);
|
||||
}
|
||||
}
|
@ -1,112 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkLinearBitmapPipeline_DEFINED
|
||||
#define SkLinearBitmapPipeline_DEFINED
|
||||
|
||||
#include "SkArenaAlloc.h"
|
||||
#include "SkColor.h"
|
||||
#include "SkImageInfo.h"
|
||||
#include "SkMatrix.h"
|
||||
#include "SkShader.h"
|
||||
|
||||
class SkEmbeddableLinearPipeline;
|
||||
|
||||
enum SkGammaType {
|
||||
kLinear_SkGammaType,
|
||||
kSRGB_SkGammaType,
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// SkLinearBitmapPipeline - encapsulates all the machinery for doing floating point pixel
|
||||
// processing in a linear color space.
|
||||
// Note: this class has unusual alignment requirements due to its use of SIMD instructions. The
|
||||
// class SkEmbeddableLinearPipeline below manages these requirements.
|
||||
class SkLinearBitmapPipeline {
|
||||
public:
|
||||
SkLinearBitmapPipeline(
|
||||
const SkMatrix& inverse,
|
||||
SkFilterQuality filterQuality,
|
||||
SkShader::TileMode xTile, SkShader::TileMode yTile,
|
||||
SkColor paintColor,
|
||||
const SkPixmap& srcPixmap,
|
||||
SkArenaAlloc* allocator);
|
||||
|
||||
SkLinearBitmapPipeline(
|
||||
const SkLinearBitmapPipeline& pipeline,
|
||||
const SkPixmap& srcPixmap,
|
||||
SkBlendMode,
|
||||
const SkImageInfo& dstInfo,
|
||||
SkArenaAlloc* allocator);
|
||||
|
||||
~SkLinearBitmapPipeline();
|
||||
|
||||
void shadeSpan4f(int x, int y, SkPM4f* dst, int count);
|
||||
void blitSpan(int32_t x, int32_t y, void* dst, int count);
|
||||
|
||||
class PointProcessorInterface;
|
||||
class SampleProcessorInterface;
|
||||
class BlendProcessorInterface;
|
||||
class DestinationInterface;
|
||||
class PixelAccessorInterface;
|
||||
|
||||
using MatrixCloner =
|
||||
std::function<PointProcessorInterface* (PointProcessorInterface*, SkArenaAlloc*)>;
|
||||
using TilerCloner =
|
||||
std::function<PointProcessorInterface* (SampleProcessorInterface*, SkArenaAlloc*)>;
|
||||
|
||||
PointProcessorInterface* chooseMatrix(
|
||||
PointProcessorInterface* next,
|
||||
const SkMatrix& inverse,
|
||||
SkArenaAlloc* allocator);
|
||||
|
||||
template <typename Tiler>
|
||||
PointProcessorInterface* createTiler(SampleProcessorInterface* next, SkISize dimensions,
|
||||
SkArenaAlloc* allocator);
|
||||
|
||||
template <typename XStrategy>
|
||||
PointProcessorInterface* chooseTilerYMode(
|
||||
SampleProcessorInterface* next, SkShader::TileMode yMode, SkISize dimensions,
|
||||
SkArenaAlloc* allocator);
|
||||
|
||||
PointProcessorInterface* chooseTiler(
|
||||
SampleProcessorInterface* next,
|
||||
SkISize dimensions,
|
||||
SkShader::TileMode xMode, SkShader::TileMode yMode,
|
||||
SkFilterQuality filterQuality,
|
||||
SkScalar dx,
|
||||
SkArenaAlloc* allocator);
|
||||
|
||||
template <SkColorType colorType>
|
||||
PixelAccessorInterface* chooseSpecificAccessor(const SkPixmap& srcPixmap,
|
||||
SkArenaAlloc* allocator);
|
||||
|
||||
PixelAccessorInterface* choosePixelAccessor(
|
||||
const SkPixmap& srcPixmap,
|
||||
const SkColor A8TintColor,
|
||||
SkArenaAlloc* allocator);
|
||||
|
||||
SampleProcessorInterface* chooseSampler(
|
||||
BlendProcessorInterface* next,
|
||||
SkFilterQuality filterQuality,
|
||||
SkShader::TileMode xTile, SkShader::TileMode yTile,
|
||||
const SkPixmap& srcPixmap,
|
||||
const SkColor A8TintColor,
|
||||
SkArenaAlloc* allocator);
|
||||
|
||||
BlendProcessorInterface* chooseBlenderForShading(
|
||||
SkAlphaType alphaType,
|
||||
float postAlpha,
|
||||
SkArenaAlloc* allocator);
|
||||
|
||||
PointProcessorInterface* fFirstStage;
|
||||
MatrixCloner fMatrixStageCloner;
|
||||
TilerCloner fTileStageCloner;
|
||||
DestinationInterface* fLastStage;
|
||||
};
|
||||
|
||||
#endif // SkLinearBitmapPipeline_DEFINED
|
@ -1,255 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkLinearBitmapPipeline_core_DEFINED
|
||||
#define SkLinearBitmapPipeline_core_DEFINED
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include "SkNx.h"
|
||||
|
||||
// New bilerp strategy:
|
||||
// Pass through on bilerpList4 and bilerpListFew (analogs to pointList), introduce bilerpEdge
|
||||
// which takes 4 points. If the sample spans an edge, then break it into a bilerpEdge. Bilerp
|
||||
// span then becomes a normal span except in special cases where an extra Y is given. The bilerp
|
||||
// need to stay single point calculations until the tile layer.
|
||||
// TODO:
|
||||
// - edge span predicate.
|
||||
// - introduce new point API
|
||||
// - Add tile for new api.
|
||||
|
||||
namespace {
|
||||
struct X {
|
||||
explicit X(SkScalar val) : fVal{val} { }
|
||||
explicit X(SkPoint pt) : fVal{pt.fX} { }
|
||||
explicit X(SkSize s) : fVal{s.fWidth} { }
|
||||
explicit X(SkISize s) : fVal((SkScalar)s.fWidth) { }
|
||||
operator SkScalar () const {return fVal;}
|
||||
private:
|
||||
SkScalar fVal;
|
||||
};
|
||||
|
||||
struct Y {
|
||||
explicit Y(SkScalar val) : fVal{val} { }
|
||||
explicit Y(SkPoint pt) : fVal{pt.fY} { }
|
||||
explicit Y(SkSize s) : fVal{s.fHeight} { }
|
||||
explicit Y(SkISize s) : fVal((SkScalar)s.fHeight) { }
|
||||
operator SkScalar () const {return fVal;}
|
||||
private:
|
||||
SkScalar fVal;
|
||||
};
|
||||
|
||||
// The Span class enables efficient processing horizontal spans of pixels.
|
||||
// * start - the point where to start the span.
|
||||
// * length - the number of pixels to traverse in source space.
|
||||
// * count - the number of pixels to produce in destination space.
|
||||
// Both start and length are mapped through the inversion matrix to produce values in source
|
||||
// space. After the matrix operation, the tilers may break the spans up into smaller spans.
|
||||
// The tilers can produce spans that seem nonsensical.
|
||||
// * The clamp tiler can create spans with length of 0. This indicates to copy an edge pixel out
|
||||
// to the edge of the destination scan.
|
||||
// * The mirror tiler can produce spans with negative length. This indicates that the source
|
||||
// should be traversed in the opposite direction to the destination pixels.
|
||||
class Span {
|
||||
public:
|
||||
Span(SkPoint start, SkScalar length, int count)
|
||||
: fStart(start)
|
||||
, fLength(length)
|
||||
, fCount{count} {
|
||||
SkASSERT(std::isfinite(length));
|
||||
}
|
||||
|
||||
operator std::tuple<SkPoint&, SkScalar&, int&>() {
|
||||
return std::tie(fStart, fLength, fCount);
|
||||
}
|
||||
|
||||
bool isEmpty() const { return 0 == fCount; }
|
||||
void clear() { fCount = 0; }
|
||||
int count() const { return fCount; }
|
||||
SkScalar length() const { return fLength; }
|
||||
SkScalar startX() const { return X(fStart); }
|
||||
SkScalar endX() const { return this->startX() + this->length(); }
|
||||
SkScalar startY() const { return Y(fStart); }
|
||||
Span emptySpan() { return Span{{0.0, 0.0}, 0.0f, 0}; }
|
||||
|
||||
bool completelyWithin(SkScalar xMin, SkScalar xMax) const {
|
||||
SkScalar sMin, sMax;
|
||||
std::tie(sMin, sMax) = std::minmax(startX(), endX());
|
||||
return xMin <= sMin && sMax < xMax;
|
||||
}
|
||||
|
||||
void offset(SkScalar offsetX) {
|
||||
fStart.offset(offsetX, 0.0f);
|
||||
}
|
||||
|
||||
Span breakAt(SkScalar breakX, SkScalar dx) {
|
||||
SkASSERT(std::isfinite(breakX));
|
||||
SkASSERT(std::isfinite(dx));
|
||||
SkASSERT(dx != 0.0f);
|
||||
|
||||
if (this->isEmpty()) {
|
||||
return this->emptySpan();
|
||||
}
|
||||
|
||||
int dxSteps = SkScalarFloorToInt((breakX - this->startX()) / dx);
|
||||
|
||||
if (dxSteps < 0) {
|
||||
// The span is wholly after breakX.
|
||||
return this->emptySpan();
|
||||
} else if (dxSteps >= fCount) {
|
||||
// The span is wholly before breakX.
|
||||
Span answer = *this;
|
||||
this->clear();
|
||||
return answer;
|
||||
}
|
||||
|
||||
// Calculate the values for the span to cleave off.
|
||||
SkScalar newLength = dxSteps * dx;
|
||||
|
||||
// If the last (or first if count = 1) sample lands directly on the boundary. Include it
|
||||
// when dx < 0 and exclude it when dx > 0.
|
||||
// Reasoning:
|
||||
// dx > 0: The sample point on the boundary is part of the next span because the entire
|
||||
// pixel is after the boundary.
|
||||
// dx < 0: The sample point on the boundary is part of the current span because the
|
||||
// entire pixel is before the boundary.
|
||||
if (this->startX() + newLength == breakX && dx > 0) {
|
||||
if (dxSteps > 0) {
|
||||
dxSteps -= 1;
|
||||
newLength -= dx;
|
||||
} else {
|
||||
return this->emptySpan();
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate new span parameters
|
||||
SkPoint newStart = fStart;
|
||||
int newCount = dxSteps + 1;
|
||||
SkASSERT(newCount > 0);
|
||||
|
||||
// Update this span to reflect the break.
|
||||
SkScalar lengthToStart = newLength + dx;
|
||||
fLength -= lengthToStart;
|
||||
fCount -= newCount;
|
||||
fStart = {this->startX() + lengthToStart, Y(fStart)};
|
||||
|
||||
return Span{newStart, newLength, newCount};
|
||||
}
|
||||
|
||||
void clampToSinglePixel(SkPoint pixel) {
|
||||
fStart = pixel;
|
||||
fLength = 0.0f;
|
||||
}
|
||||
|
||||
private:
|
||||
SkPoint fStart;
|
||||
SkScalar fLength;
|
||||
int fCount;
|
||||
};
|
||||
|
||||
template<typename Stage>
|
||||
void span_fallback(Span span, Stage* stage) {
|
||||
SkPoint start;
|
||||
SkScalar length;
|
||||
int count;
|
||||
std::tie(start, length, count) = span;
|
||||
Sk4f startXs{X(start)};
|
||||
Sk4f ys{Y(start)};
|
||||
Sk4f mults = {0.0f, 1.0f, 2.0f, 3.0f};
|
||||
|
||||
// Initializing this is not needed, but some compilers can't figure this out.
|
||||
Sk4s dXs{0.0f};
|
||||
if (count > 1) {
|
||||
SkScalar dx = length / (count - 1);
|
||||
dXs = Sk4f{dx};
|
||||
}
|
||||
|
||||
// Instead of using xs = xs + dx every round, this uses xs = i * dx + X(start). This
|
||||
// eliminates the rounding error for the sum.
|
||||
Sk4f xs = startXs + mults * dXs;
|
||||
while (count >= 4) {
|
||||
stage->pointList4(xs, ys);
|
||||
|
||||
mults += Sk4f{4.0f};
|
||||
xs = mults * dXs + startXs;
|
||||
count -= 4;
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
stage->pointListFew(count, xs, ys);
|
||||
}
|
||||
}
|
||||
|
||||
inline Sk4f SK_VECTORCALL check_pixel(const Sk4f& pixel) {
|
||||
SkASSERTF(0.0f <= pixel[0] && pixel[0] <= 1.0f, "pixel[0]: %f", pixel[0]);
|
||||
SkASSERTF(0.0f <= pixel[1] && pixel[1] <= 1.0f, "pixel[1]: %f", pixel[1]);
|
||||
SkASSERTF(0.0f <= pixel[2] && pixel[2] <= 1.0f, "pixel[2]: %f", pixel[2]);
|
||||
SkASSERTF(0.0f <= pixel[3] && pixel[3] <= 1.0f, "pixel[3]: %f", pixel[3]);
|
||||
return pixel;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class SkLinearBitmapPipeline::PointProcessorInterface {
|
||||
public:
|
||||
virtual ~PointProcessorInterface() { }
|
||||
// Take the first n (where 0 < n && n < 4) items from xs and ys and sample those points. For
|
||||
// nearest neighbor, that means just taking the floor xs and ys. For bilerp, this means
|
||||
// to expand the bilerp filter around the point and sample using that filter.
|
||||
virtual void SK_VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) = 0;
|
||||
// Same as pointListFew, but n = 4.
|
||||
virtual void SK_VECTORCALL pointList4(Sk4s xs, Sk4s ys) = 0;
|
||||
// A span is a compact form of sample points that are obtained by mapping points from
|
||||
// destination space to source space. This is used for horizontal lines only, and is mainly
|
||||
// used to take advantage of memory coherence for horizontal spans.
|
||||
virtual void pointSpan(Span span) = 0;
|
||||
};
|
||||
|
||||
class SkLinearBitmapPipeline::SampleProcessorInterface
|
||||
: public SkLinearBitmapPipeline::PointProcessorInterface {
|
||||
public:
|
||||
// Used for nearest neighbor when scale factor is 1.0. The span can just be repeated with no
|
||||
// edge pixel alignment problems. This is for handling a very common case.
|
||||
virtual void repeatSpan(Span span, int32_t repeatCount) = 0;
|
||||
};
|
||||
|
||||
class SkLinearBitmapPipeline::DestinationInterface {
|
||||
public:
|
||||
virtual ~DestinationInterface() { }
|
||||
// Count is normally not needed, but in these early stages of development it is useful to
|
||||
// check bounds.
|
||||
// TODO(herb): 4/6/2016 - remove count when code is stable.
|
||||
virtual void setDestination(void* dst, int count) = 0;
|
||||
};
|
||||
|
||||
class SkLinearBitmapPipeline::BlendProcessorInterface
|
||||
: public SkLinearBitmapPipeline::DestinationInterface {
|
||||
public:
|
||||
virtual void SK_VECTORCALL blendPixel(Sk4f pixel0) = 0;
|
||||
virtual void SK_VECTORCALL blend4Pixels(Sk4f p0, Sk4f p1, Sk4f p2, Sk4f p3) = 0;
|
||||
};
|
||||
|
||||
class SkLinearBitmapPipeline::PixelAccessorInterface {
|
||||
public:
|
||||
virtual ~PixelAccessorInterface() { }
|
||||
virtual void SK_VECTORCALL getFewPixels(
|
||||
int n, Sk4i xs, Sk4i ys, Sk4f* px0, Sk4f* px1, Sk4f* px2) const = 0;
|
||||
|
||||
virtual void SK_VECTORCALL get4Pixels(
|
||||
Sk4i xs, Sk4i ys, Sk4f* px0, Sk4f* px1, Sk4f* px2, Sk4f* px3) const = 0;
|
||||
|
||||
virtual void get4Pixels(
|
||||
const void* src, int index, Sk4f* px0, Sk4f* px1, Sk4f* px2, Sk4f* px3) const = 0;
|
||||
|
||||
virtual Sk4f getPixelFromRow(const void* row, int index) const = 0;
|
||||
|
||||
virtual Sk4f getPixelAt(int index) const = 0;
|
||||
|
||||
virtual const void* row(int y) const = 0;
|
||||
};
|
||||
|
||||
#endif // SkLinearBitmapPipeline_core_DEFINED
|
@ -1,118 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkLinearBitmapPipeline_matrix_DEFINED
|
||||
#define SkLinearBitmapPipeline_matrix_DEFINED
|
||||
|
||||
#include "SkLinearBitmapPipeline_core.h"
|
||||
|
||||
namespace {
|
||||
class TranslateMatrixStrategy {
|
||||
public:
|
||||
TranslateMatrixStrategy(SkVector offset)
|
||||
: fXOffset{X(offset)}
|
||||
, fYOffset{Y(offset)} { }
|
||||
|
||||
void processPoints(Sk4s* xs, Sk4s* ys) const {
|
||||
*xs = *xs + fXOffset;
|
||||
*ys = *ys + fYOffset;
|
||||
}
|
||||
|
||||
template <typename Next>
|
||||
bool maybeProcessSpan(Span span, Next* next) const {
|
||||
SkPoint start; SkScalar length; int count;
|
||||
std::tie(start, length, count) = span;
|
||||
next->pointSpan(Span{start + SkPoint{fXOffset, fYOffset}, length, count});
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
const SkScalar fXOffset, fYOffset;
|
||||
};
|
||||
|
||||
class ScaleMatrixStrategy {
|
||||
public:
|
||||
ScaleMatrixStrategy(SkVector offset, SkVector scale)
|
||||
: fXOffset{X(offset)}, fYOffset{Y(offset)}
|
||||
, fXScale{X(scale)}, fYScale{Y(scale)} { }
|
||||
void processPoints(Sk4s* xs, Sk4s* ys) const {
|
||||
*xs = *xs * fXScale + fXOffset;
|
||||
*ys = *ys * fYScale + fYOffset;
|
||||
}
|
||||
|
||||
template <typename Next>
|
||||
bool maybeProcessSpan(Span span, Next* next) const {
|
||||
SkPoint start; SkScalar length; int count;
|
||||
std::tie(start, length, count) = span;
|
||||
SkPoint newStart =
|
||||
SkPoint{X(start) * fXScale + fXOffset, Y(start) * fYScale + fYOffset};
|
||||
SkScalar newLength = length * fXScale;
|
||||
next->pointSpan(Span{newStart, newLength, count});
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
const SkScalar fXOffset, fYOffset;
|
||||
const SkScalar fXScale, fYScale;
|
||||
};
|
||||
|
||||
class AffineMatrixStrategy {
|
||||
public:
|
||||
AffineMatrixStrategy(SkVector offset, SkVector scale, SkVector skew)
|
||||
: fXOffset{X(offset)}, fYOffset{Y(offset)}
|
||||
, fXScale{X(scale)}, fYScale{Y(scale)}
|
||||
, fXSkew{X(skew)}, fYSkew{Y(skew)} { }
|
||||
void processPoints(Sk4s* xs, Sk4s* ys) const {
|
||||
Sk4s newXs = fXScale * *xs + fXSkew * *ys + fXOffset;
|
||||
Sk4s newYs = fYSkew * *xs + fYScale * *ys + fYOffset;
|
||||
|
||||
*xs = newXs;
|
||||
*ys = newYs;
|
||||
}
|
||||
|
||||
template <typename Next>
|
||||
bool maybeProcessSpan(Span span, Next* next) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
const SkScalar fXOffset, fYOffset;
|
||||
const SkScalar fXScale, fYScale;
|
||||
const SkScalar fXSkew, fYSkew;
|
||||
};
|
||||
|
||||
class PerspectiveMatrixStrategy {
|
||||
public:
|
||||
PerspectiveMatrixStrategy(SkVector offset, SkVector scale, SkVector skew,
|
||||
SkVector zSkew, SkScalar zOffset)
|
||||
: fXOffset{X(offset)}, fYOffset{Y(offset)}, fZOffset{zOffset}
|
||||
, fXScale{X(scale)}, fYScale{Y(scale)}
|
||||
, fXSkew{X(skew)}, fYSkew{Y(skew)}, fZXSkew{X(zSkew)}, fZYSkew{Y(zSkew)} { }
|
||||
void processPoints(Sk4s* xs, Sk4s* ys) const {
|
||||
Sk4s newXs = fXScale * *xs + fXSkew * *ys + fXOffset;
|
||||
Sk4s newYs = fYSkew * *xs + fYScale * *ys + fYOffset;
|
||||
Sk4s newZs = fZXSkew * *xs + fZYSkew * *ys + fZOffset;
|
||||
|
||||
*xs = newXs / newZs;
|
||||
*ys = newYs / newZs;
|
||||
}
|
||||
|
||||
template <typename Next>
|
||||
bool maybeProcessSpan(Span span, Next* next) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
const SkScalar fXOffset, fYOffset, fZOffset;
|
||||
const SkScalar fXScale, fYScale;
|
||||
const SkScalar fXSkew, fYSkew, fZXSkew, fZYSkew;
|
||||
};
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif // SkLinearBitmapPipeline_matrix_DEFINED
|
File diff suppressed because it is too large
Load Diff
@ -1,412 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkLinearBitmapPipeline_tile_DEFINED
|
||||
#define SkLinearBitmapPipeline_tile_DEFINED
|
||||
|
||||
#include "SkLinearBitmapPipeline_core.h"
|
||||
#include "SkPM4f.h"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
namespace {
|
||||
|
||||
void assertTiled(const Sk4s& vs, SkScalar vMax) {
|
||||
SkASSERT(0 <= vs[0] && vs[0] < vMax);
|
||||
SkASSERT(0 <= vs[1] && vs[1] < vMax);
|
||||
SkASSERT(0 <= vs[2] && vs[2] < vMax);
|
||||
SkASSERT(0 <= vs[3] && vs[3] < vMax);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clamp in the X direction.
|
||||
* Observations:
|
||||
* * sample pointer border - if the sample point is <= 0.5 or >= Max - 0.5 then the pixel
|
||||
* value should be a border color. For this case, create the span using clampToSinglePixel.
|
||||
*/
|
||||
class XClampStrategy {
|
||||
public:
|
||||
XClampStrategy(int32_t max)
|
||||
: fXMaxPixel{SkScalar(max - SK_ScalarHalf)}
|
||||
, fXMax{SkScalar(max)} { }
|
||||
|
||||
void tileXPoints(Sk4s* xs) {
|
||||
*xs = Sk4s::Min(Sk4s::Max(*xs, SK_ScalarHalf), fXMaxPixel);
|
||||
assertTiled(*xs, fXMax);
|
||||
}
|
||||
|
||||
template<typename Next>
|
||||
bool maybeProcessSpan(Span originalSpan, Next* next) {
|
||||
SkASSERT(!originalSpan.isEmpty());
|
||||
SkPoint start; SkScalar length; int count;
|
||||
std::tie(start, length, count) = originalSpan;
|
||||
SkScalar x = X(start);
|
||||
SkScalar y = Y(start);
|
||||
Span span{{x, y}, length, count};
|
||||
|
||||
if (span.completelyWithin(0.0f, fXMax)) {
|
||||
next->pointSpan(span);
|
||||
return true;
|
||||
}
|
||||
if (1 == count || 0.0f == length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SkScalar dx = length / (count - 1);
|
||||
|
||||
// A B C
|
||||
// +-------+-------+-------++-------+-------+-------+ +-------+-------++------
|
||||
// | *---*|---*---|*---*--||-*---*-|---*---|*---...| |--*---*|---*---||*---*....
|
||||
// | | | || | | | ... | | ||
|
||||
// | | | || | | | | | ||
|
||||
// +-------+-------+-------++-------+-------+-------+ +-------+-------++------
|
||||
// ^ ^
|
||||
// | xMin xMax-1 | xMax
|
||||
//
|
||||
// *---*---*---... - track of samples. * = sample
|
||||
//
|
||||
// +-+ ||
|
||||
// | | - pixels in source space. || - tile border.
|
||||
// +-+ ||
|
||||
//
|
||||
// The length from A to B is the length in source space or 4 * dx or (count - 1) * dx
|
||||
// where dx is the distance between samples. There are 5 destination pixels
|
||||
// corresponding to 5 samples specified in the A, B span. The distance from A to the next
|
||||
// span starting at C is 5 * dx, so count * dx.
|
||||
// Remember, count is the number of pixels needed for the destination and the number of
|
||||
// samples.
|
||||
// Overall Strategy:
|
||||
// * Under - for portions of the span < xMin, take the color at pixel {xMin, y} and use it
|
||||
// to fill in the 5 pixel sampled from A to B.
|
||||
// * Middle - for the portion of the span between xMin and xMax sample normally.
|
||||
// * Over - for the portion of the span > xMax, take the color at pixel {xMax-1, y} and
|
||||
// use it to fill in the rest of the destination pixels.
|
||||
if (dx >= 0) {
|
||||
Span leftClamped = span.breakAt(SK_ScalarHalf, dx);
|
||||
if (!leftClamped.isEmpty()) {
|
||||
leftClamped.clampToSinglePixel({SK_ScalarHalf, y});
|
||||
next->pointSpan(leftClamped);
|
||||
}
|
||||
Span center = span.breakAt(fXMax, dx);
|
||||
if (!center.isEmpty()) {
|
||||
next->pointSpan(center);
|
||||
}
|
||||
if (!span.isEmpty()) {
|
||||
span.clampToSinglePixel({fXMaxPixel, y});
|
||||
next->pointSpan(span);
|
||||
}
|
||||
} else {
|
||||
Span rightClamped = span.breakAt(fXMax, dx);
|
||||
if (!rightClamped.isEmpty()) {
|
||||
rightClamped.clampToSinglePixel({fXMaxPixel, y});
|
||||
next->pointSpan(rightClamped);
|
||||
}
|
||||
Span center = span.breakAt(SK_ScalarHalf, dx);
|
||||
if (!center.isEmpty()) {
|
||||
next->pointSpan(center);
|
||||
}
|
||||
if (!span.isEmpty()) {
|
||||
span.clampToSinglePixel({SK_ScalarHalf, y});
|
||||
next->pointSpan(span);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
const SkScalar fXMaxPixel;
|
||||
const SkScalar fXMax;
|
||||
};
|
||||
|
||||
class YClampStrategy {
|
||||
public:
|
||||
YClampStrategy(int32_t max)
|
||||
: fYMaxPixel{SkScalar(max) - SK_ScalarHalf} { }
|
||||
|
||||
void tileYPoints(Sk4s* ys) {
|
||||
*ys = Sk4s::Min(Sk4s::Max(*ys, SK_ScalarHalf), fYMaxPixel);
|
||||
assertTiled(*ys, fYMaxPixel + SK_ScalarHalf);
|
||||
}
|
||||
|
||||
SkScalar tileY(SkScalar y) {
|
||||
Sk4f ys{y};
|
||||
tileYPoints(&ys);
|
||||
return ys[0];
|
||||
}
|
||||
|
||||
private:
|
||||
const SkScalar fYMaxPixel;
|
||||
};
|
||||
|
||||
SkScalar tile_mod(SkScalar x, SkScalar base, SkScalar cap) {
|
||||
// When x is a negative number *very* close to zero, the difference becomes 0 - (-base) = base
|
||||
// which is an out of bound value. The min() corrects these problematic values.
|
||||
return std::min(x - SkScalarFloorToScalar(x / base) * base, cap);
|
||||
}
|
||||
|
||||
class XRepeatStrategy {
|
||||
public:
|
||||
XRepeatStrategy(int32_t max)
|
||||
: fXMax{SkScalar(max)}
|
||||
, fXCap{SkScalar(nextafterf(SkScalar(max), 0.0f))}
|
||||
, fXInvMax{1.0f / SkScalar(max)} { }
|
||||
|
||||
void tileXPoints(Sk4s* xs) {
|
||||
Sk4s divX = *xs * fXInvMax;
|
||||
Sk4s modX = *xs - divX.floor() * fXMax;
|
||||
*xs = Sk4s::Min(fXCap, modX);
|
||||
assertTiled(*xs, fXMax);
|
||||
}
|
||||
|
||||
template<typename Next>
|
||||
bool maybeProcessSpan(Span originalSpan, Next* next) {
|
||||
SkASSERT(!originalSpan.isEmpty());
|
||||
SkPoint start; SkScalar length; int count;
|
||||
std::tie(start, length, count) = originalSpan;
|
||||
// Make x and y in range on the tile.
|
||||
SkScalar x = tile_mod(X(start), fXMax, fXCap);
|
||||
SkScalar y = Y(start);
|
||||
SkScalar dx = length / (count - 1);
|
||||
|
||||
// No need trying to go fast because the steps are larger than a tile or there is one point.
|
||||
if (SkScalarAbs(dx) >= fXMax || count <= 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// A B C D Z
|
||||
// +-------+-------+-------++-------+-------+-------++ +-------+-------++------
|
||||
// | | *---|*---*--||-*---*-|---*---|*---*--|| |--*---*| ||
|
||||
// | | | || | | || ... | | ||
|
||||
// | | | || | | || | | ||
|
||||
// +-------+-------+-------++-------+-------+-------++ +-------+-------++------
|
||||
// ^^ ^^ ^^
|
||||
// xMax || xMin xMax || xMin xMax || xMin
|
||||
//
|
||||
// *---*---*---... - track of samples. * = sample
|
||||
//
|
||||
// +-+ ||
|
||||
// | | - pixels in source space. || - tile border.
|
||||
// +-+ ||
|
||||
//
|
||||
//
|
||||
// The given span starts at A and continues on through several tiles to sample point Z.
|
||||
// The idea is to break this into several spans one on each tile the entire span
|
||||
// intersects. The A to B span only covers a partial tile and has a count of 3 and the
|
||||
// distance from A to B is (count - 1) * dx or 2 * dx. The distance from A to the start of
|
||||
// the next span is count * dx or 3 * dx. Span C to D covers an entire tile has a count
|
||||
// of 5 and a length of 4 * dx. Remember, count is the number of pixels needed for the
|
||||
// destination and the number of samples.
|
||||
//
|
||||
// Overall Strategy:
|
||||
// While the span hangs over the edge of the tile, draw the span covering the tile then
|
||||
// slide the span over to the next tile.
|
||||
|
||||
// The guard could have been count > 0, but then a bunch of math would be done in the
|
||||
// common case.
|
||||
|
||||
Span span({x, y}, length, count);
|
||||
if (dx > 0) {
|
||||
while (!span.isEmpty() && span.endX() >= fXMax) {
|
||||
Span toDraw = span.breakAt(fXMax, dx);
|
||||
next->pointSpan(toDraw);
|
||||
span.offset(-fXMax);
|
||||
}
|
||||
} else {
|
||||
while (!span.isEmpty() && span.endX() < 0.0f) {
|
||||
Span toDraw = span.breakAt(0.0f, dx);
|
||||
next->pointSpan(toDraw);
|
||||
span.offset(fXMax);
|
||||
}
|
||||
}
|
||||
|
||||
// All on a single tile.
|
||||
if (!span.isEmpty()) {
|
||||
next->pointSpan(span);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
const SkScalar fXMax;
|
||||
const SkScalar fXCap;
|
||||
const SkScalar fXInvMax;
|
||||
};
|
||||
|
||||
// The XRepeatUnitScaleStrategy exploits the situation where dx = 1.0. The main advantage is that
|
||||
// the relationship between the sample points and the source pixels does not change from tile to
|
||||
// repeated tile. This allows the tiler to calculate the span once and re-use it for each
|
||||
// repeated tile. This is later exploited by some samplers to avoid converting pixels to linear
|
||||
// space allowing the use of memmove to place pixel in the destination.
|
||||
class XRepeatUnitScaleStrategy {
|
||||
public:
|
||||
XRepeatUnitScaleStrategy(int32_t max)
|
||||
: fXMax{SkScalar(max)}
|
||||
, fXCap{SkScalar(nextafterf(SkScalar(max), 0.0f))}
|
||||
, fXInvMax{1.0f / SkScalar(max)} { }
|
||||
|
||||
void tileXPoints(Sk4s* xs) {
|
||||
Sk4s divX = *xs * fXInvMax;
|
||||
Sk4s modX = *xs - divX.floor() * fXMax;
|
||||
*xs = Sk4s::Min(fXCap, modX);
|
||||
assertTiled(*xs, fXMax);
|
||||
}
|
||||
|
||||
template<typename Next>
|
||||
bool maybeProcessSpan(Span originalSpan, Next* next) {
|
||||
SkASSERT(!originalSpan.isEmpty());
|
||||
SkPoint start; SkScalar length; int count;
|
||||
std::tie(start, length, count) = originalSpan;
|
||||
// Make x and y in range on the tile.
|
||||
SkScalar x = tile_mod(X(start), fXMax, fXCap);
|
||||
SkScalar y = Y(start);
|
||||
|
||||
// No need trying to go fast because the steps are larger than a tile or there is one point.
|
||||
if (fXMax == 1 || count <= 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// x should be on the tile.
|
||||
SkASSERT(0.0f <= x && x < fXMax);
|
||||
Span span({x, y}, length, count);
|
||||
|
||||
if (SkScalarFloorToScalar(x) != 0.0f) {
|
||||
Span toDraw = span.breakAt(fXMax, 1.0f);
|
||||
SkASSERT(0.0f <= toDraw.startX() && toDraw.endX() < fXMax);
|
||||
next->pointSpan(toDraw);
|
||||
span.offset(-fXMax);
|
||||
}
|
||||
|
||||
// All of the span could have been on the first tile. If so, then no work to do.
|
||||
if (span.isEmpty()) return true;
|
||||
|
||||
// At this point the span should be aligned to zero.
|
||||
SkASSERT(SkScalarFloorToScalar(span.startX()) == 0.0f);
|
||||
|
||||
// Note: The span length has an unintuitive relation to the tile width. The tile width is
|
||||
// a half open interval [tb, te), but the span is a closed interval [sb, se]. In order to
|
||||
// compare the two, you need to convert the span to a half open interval. This is done by
|
||||
// adding dx to se. So, the span becomes: [sb, se + dx). Hence the + 1.0f below.
|
||||
SkScalar div = (span.length() + 1.0f) / fXMax;
|
||||
int32_t repeatCount = SkScalarFloorToInt(div);
|
||||
Span repeatableSpan{{0.0f, y}, fXMax - 1.0f, SkScalarFloorToInt(fXMax)};
|
||||
|
||||
// Repeat the center section.
|
||||
SkASSERT(0.0f <= repeatableSpan.startX() && repeatableSpan.endX() < fXMax);
|
||||
if (repeatCount > 0) {
|
||||
next->repeatSpan(repeatableSpan, repeatCount);
|
||||
}
|
||||
|
||||
// Calculate the advance past the center portion.
|
||||
SkScalar advance = SkScalar(repeatCount) * fXMax;
|
||||
|
||||
// There may be some of the span left over.
|
||||
span.breakAt(advance, 1.0f);
|
||||
|
||||
// All on a single tile.
|
||||
if (!span.isEmpty()) {
|
||||
span.offset(-advance);
|
||||
SkASSERT(0.0f <= span.startX() && span.endX() < fXMax);
|
||||
next->pointSpan(span);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
const SkScalar fXMax;
|
||||
const SkScalar fXCap;
|
||||
const SkScalar fXInvMax;
|
||||
};
|
||||
|
||||
class YRepeatStrategy {
|
||||
public:
|
||||
YRepeatStrategy(int32_t max)
|
||||
: fYMax{SkScalar(max)}
|
||||
, fYCap{SkScalar(nextafterf(SkScalar(max), 0.0f))}
|
||||
, fYsInvMax{1.0f / SkScalar(max)} { }
|
||||
|
||||
void tileYPoints(Sk4s* ys) {
|
||||
Sk4s divY = *ys * fYsInvMax;
|
||||
Sk4s modY = *ys - divY.floor() * fYMax;
|
||||
*ys = Sk4s::Min(fYCap, modY);
|
||||
assertTiled(*ys, fYMax);
|
||||
}
|
||||
|
||||
SkScalar tileY(SkScalar y) {
|
||||
SkScalar answer = tile_mod(y, fYMax, fYCap);
|
||||
SkASSERT(0 <= answer && answer < fYMax);
|
||||
return answer;
|
||||
}
|
||||
|
||||
private:
|
||||
const SkScalar fYMax;
|
||||
const SkScalar fYCap;
|
||||
const SkScalar fYsInvMax;
|
||||
};
|
||||
// max = 40
|
||||
// mq2[x_] := Abs[(x - 40) - Floor[(x - 40)/80] * 80 - 40]
|
||||
class XMirrorStrategy {
|
||||
public:
|
||||
XMirrorStrategy(int32_t max)
|
||||
: fXMax{SkScalar(max)}
|
||||
, fXCap{SkScalar(nextafterf(SkScalar(max), 0.0f))}
|
||||
, fXDoubleInvMax{1.0f / (2.0f * SkScalar(max))} { }
|
||||
|
||||
void tileXPoints(Sk4s* xs) {
|
||||
Sk4f bias = *xs - fXMax;
|
||||
Sk4f div = bias * fXDoubleInvMax;
|
||||
Sk4f mod = bias - div.floor() * 2.0f * fXMax;
|
||||
Sk4f unbias = mod - fXMax;
|
||||
*xs = Sk4f::Min(unbias.abs(), fXCap);
|
||||
assertTiled(*xs, fXMax);
|
||||
}
|
||||
|
||||
template <typename Next>
|
||||
bool maybeProcessSpan(Span originalSpan, Next* next) { return false; }
|
||||
|
||||
private:
|
||||
SkScalar fXMax;
|
||||
SkScalar fXCap;
|
||||
SkScalar fXDoubleInvMax;
|
||||
};
|
||||
|
||||
class YMirrorStrategy {
|
||||
public:
|
||||
YMirrorStrategy(int32_t max)
|
||||
: fYMax{SkScalar(max)}
|
||||
, fYCap{nextafterf(SkScalar(max), 0.0f)}
|
||||
, fYDoubleInvMax{1.0f / (2.0f * SkScalar(max))} { }
|
||||
|
||||
void tileYPoints(Sk4s* ys) {
|
||||
Sk4f bias = *ys - fYMax;
|
||||
Sk4f div = bias * fYDoubleInvMax;
|
||||
Sk4f mod = bias - div.floor() * 2.0f * fYMax;
|
||||
Sk4f unbias = mod - fYMax;
|
||||
*ys = Sk4f::Min(unbias.abs(), fYCap);
|
||||
assertTiled(*ys, fYMax);
|
||||
}
|
||||
|
||||
SkScalar tileY(SkScalar y) {
|
||||
SkScalar bias = y - fYMax;
|
||||
SkScalar div = bias * fYDoubleInvMax;
|
||||
SkScalar mod = bias - SkScalarFloorToScalar(div) * 2.0f * fYMax;
|
||||
SkScalar unbias = mod - fYMax;
|
||||
SkScalar answer = SkMinScalar(SkScalarAbs(unbias), fYCap);
|
||||
SkASSERT(0 <= answer && answer < fYMax);
|
||||
return answer;
|
||||
}
|
||||
|
||||
private:
|
||||
SkScalar fYMax;
|
||||
SkScalar fYCap;
|
||||
SkScalar fYDoubleInvMax;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
#endif // SkLinearBitmapPipeline_tile_DEFINED
|
@ -36,7 +36,6 @@
|
||||
#define SK_OPTS_NS portable
|
||||
#endif
|
||||
|
||||
#include "SkBlend_opts.h"
|
||||
#include "SkBlitMask_opts.h"
|
||||
#include "SkBlitRow_opts.h"
|
||||
#include "SkBlurImageFilter_opts.h"
|
||||
@ -79,8 +78,6 @@ namespace SkOpts {
|
||||
DEFINE_DEFAULT(inverted_CMYK_to_RGB1);
|
||||
DEFINE_DEFAULT(inverted_CMYK_to_BGR1);
|
||||
|
||||
DEFINE_DEFAULT(srcover_srgb_srgb);
|
||||
|
||||
DEFINE_DEFAULT(memset16);
|
||||
DEFINE_DEFAULT(memset32);
|
||||
DEFINE_DEFAULT(memset64);
|
||||
|
@ -48,10 +48,6 @@ namespace SkOpts {
|
||||
inverted_CMYK_to_RGB1, // i.e. convert color space
|
||||
inverted_CMYK_to_BGR1; // i.e. convert color space
|
||||
|
||||
// Blend ndst src pixels over dst, where both src and dst point to sRGB pixels (RGBA or BGRA).
|
||||
// If nsrc < ndst, we loop over src to create a pattern.
|
||||
extern void (*srcover_srgb_srgb)(uint32_t* dst, const uint32_t* src, int ndst, int nsrc);
|
||||
|
||||
extern void (*memset16)(uint16_t[], uint16_t, int);
|
||||
extern void (*memset32)(uint32_t[], uint32_t, int);
|
||||
extern void (*memset64)(uint64_t[], uint64_t, int);
|
||||
|
@ -1,98 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
/*
|
||||
ninja -C out/Release dm nanobench ; and ./out/Release/dm --match Blend_opts ; and ./out/Release/nanobench --samples 300 --nompd --match LinearSrcOver -q
|
||||
*/
|
||||
|
||||
#ifndef SkBlend_opts_DEFINED
|
||||
#define SkBlend_opts_DEFINED
|
||||
|
||||
#include "SkNx.h"
|
||||
#include "SkPM4fPriv.h"
|
||||
|
||||
#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE41
|
||||
#include <immintrin.h>
|
||||
#endif
|
||||
|
||||
namespace SK_OPTS_NS {
|
||||
|
||||
static inline void srcover_srgb_srgb_1(uint32_t* dst, uint32_t src) {
|
||||
if (src >= 0xFF000000) {
|
||||
*dst = src;
|
||||
return;
|
||||
}
|
||||
auto d = Sk4f_fromS32(*dst),
|
||||
s = Sk4f_fromS32( src);
|
||||
*dst = Sk4f_toS32(s + d * (1.0f - s[3]));
|
||||
}
|
||||
|
||||
static inline void srcover_srgb_srgb_4(uint32_t* dst, const uint32_t* src) {
|
||||
srcover_srgb_srgb_1(dst++, *src++);
|
||||
srcover_srgb_srgb_1(dst++, *src++);
|
||||
srcover_srgb_srgb_1(dst++, *src++);
|
||||
srcover_srgb_srgb_1(dst , *src );
|
||||
}
|
||||
|
||||
#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE41
|
||||
|
||||
static inline __m128i load(const uint32_t* p) {
|
||||
return _mm_loadu_si128(reinterpret_cast<const __m128i*>(p));
|
||||
}
|
||||
|
||||
static inline void store(uint32_t* p, __m128i v) {
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i*>(p), v);
|
||||
}
|
||||
|
||||
static void srcover_srgb_srgb(
|
||||
uint32_t* dst, const uint32_t* const srcStart, int ndst, const int nsrc) {
|
||||
const __m128i alphaMask = _mm_set1_epi32(0xFF000000);
|
||||
while (ndst > 0) {
|
||||
int count = SkTMin(ndst, nsrc);
|
||||
ndst -= count;
|
||||
const uint32_t* src = srcStart;
|
||||
const uint32_t* end = dst + (count & ~3);
|
||||
|
||||
while (dst < end) {
|
||||
__m128i pixels = load(src);
|
||||
|
||||
if (_mm_testc_si128(pixels, alphaMask)) {
|
||||
store(dst, pixels);
|
||||
} else if (!_mm_testz_si128(pixels, alphaMask)) {
|
||||
srcover_srgb_srgb_4(dst, src);
|
||||
}
|
||||
|
||||
dst += 4;
|
||||
src += 4;
|
||||
}
|
||||
|
||||
count = count & 3;
|
||||
while (count-- > 0) {
|
||||
srcover_srgb_srgb_1(dst++, *src++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void srcover_srgb_srgb(
|
||||
uint32_t* dst, const uint32_t* const src, int ndst, const int nsrc) {
|
||||
while (ndst > 0) {
|
||||
int n = SkTMin(ndst, nsrc);
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
srcover_srgb_srgb_1(dst++, src[i]);
|
||||
}
|
||||
ndst -= n;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace SK_OPTS_NS
|
||||
|
||||
#endif//SkBlend_opts_DEFINED
|
@ -10,14 +10,12 @@
|
||||
#define SK_OPTS_NS sse41
|
||||
#include "SkBlurImageFilter_opts.h"
|
||||
#include "SkBlitRow_opts.h"
|
||||
#include "SkBlend_opts.h"
|
||||
|
||||
namespace SkOpts {
|
||||
void Init_sse41() {
|
||||
box_blur_xx = sse41::box_blur_xx;
|
||||
box_blur_xy = sse41::box_blur_xy;
|
||||
box_blur_yx = sse41::box_blur_yx;
|
||||
srcover_srgb_srgb = sse41::srcover_srgb_srgb;
|
||||
blit_row_s32a_opaque = sse41::blit_row_s32a_opaque;
|
||||
}
|
||||
}
|
||||
|
@ -100,79 +100,6 @@ private:
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#include "SkLinearBitmapPipeline.h"
|
||||
#include "SkPM4f.h"
|
||||
|
||||
class LinearPipelineContext : public BitmapProcInfoContext {
|
||||
public:
|
||||
LinearPipelineContext(const SkShaderBase& shader, const SkShaderBase::ContextRec& rec,
|
||||
SkBitmapProcInfo* info, SkArenaAlloc* alloc)
|
||||
: INHERITED(shader, rec, info), fAllocator{alloc}
|
||||
{
|
||||
// Save things off in case we need to build a blitter pipeline.
|
||||
fSrcPixmap = info->fPixmap;
|
||||
fAlpha = SkColorGetA(info->fPaintColor) / 255.0f;
|
||||
fFilterQuality = info->fFilterQuality;
|
||||
fMatrixTypeMask = info->fRealInvMatrix.getType();
|
||||
|
||||
fShaderPipeline = alloc->make<SkLinearBitmapPipeline>(
|
||||
info->fRealInvMatrix, info->fFilterQuality,
|
||||
info->fTileModeX, info->fTileModeY,
|
||||
info->fPaintColor,
|
||||
info->fPixmap,
|
||||
fAllocator);
|
||||
}
|
||||
|
||||
void shadeSpan4f(int x, int y, SkPM4f dstC[], int count) override {
|
||||
fShaderPipeline->shadeSpan4f(x, y, dstC, count);
|
||||
}
|
||||
|
||||
void shadeSpan(int x, int y, SkPMColor dstC[], int count) override {
|
||||
const int N = 128;
|
||||
SkPM4f tmp[N];
|
||||
|
||||
while (count > 0) {
|
||||
const int n = SkTMin(count, N);
|
||||
fShaderPipeline->shadeSpan4f(x, y, tmp, n);
|
||||
// now convert to SkPMColor
|
||||
for (int i = 0; i < n; ++i) {
|
||||
dstC[i] = Sk4f_toL32(tmp[i].to4f_pmorder());
|
||||
}
|
||||
dstC += n;
|
||||
x += n;
|
||||
count -= n;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// Store the allocator from the context creation incase we are asked to build a blitter.
|
||||
SkArenaAlloc* fAllocator;
|
||||
SkLinearBitmapPipeline* fShaderPipeline;
|
||||
SkPixmap fSrcPixmap;
|
||||
float fAlpha;
|
||||
SkMatrix::TypeMask fMatrixTypeMask;
|
||||
SkFilterQuality fFilterQuality;
|
||||
|
||||
typedef BitmapProcInfoContext INHERITED;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static bool choose_linear_pipeline(const SkShaderBase::ContextRec& rec, const SkImageInfo& srcInfo) {
|
||||
// If we get here, we can reasonably use either context, respect the caller's preference
|
||||
//
|
||||
bool needsPremul = srcInfo.alphaType() == kUnpremul_SkAlphaType;
|
||||
bool needsSwizzle = srcInfo.bytesPerPixel() == 4 && srcInfo.colorType() != kN32_SkColorType;
|
||||
return SkShaderBase::ContextRec::kPM4f_DstType == rec.fPreferredDstType
|
||||
|| needsPremul || needsSwizzle;
|
||||
}
|
||||
|
||||
size_t SkBitmapProcLegacyShader::ContextSize(const ContextRec& rec, const SkImageInfo& srcInfo) {
|
||||
size_t size0 = sizeof(BitmapProcShaderContext) + sizeof(SkBitmapProcState);
|
||||
size_t size1 = sizeof(LinearPipelineContext) + sizeof(SkBitmapProcInfo);
|
||||
size_t s = SkTMax(size0, size1);
|
||||
return s;
|
||||
}
|
||||
|
||||
SkShaderBase::Context* SkBitmapProcLegacyShader::MakeContext(
|
||||
const SkShaderBase& shader, TileMode tmx, TileMode tmy,
|
||||
@ -184,21 +111,9 @@ SkShaderBase::Context* SkBitmapProcLegacyShader::MakeContext(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Decide if we can/want to use the new linear pipeline
|
||||
bool useLinearPipeline = choose_linear_pipeline(rec, provider.info());
|
||||
|
||||
if (useLinearPipeline) {
|
||||
SkBitmapProcInfo* info = alloc->make<SkBitmapProcInfo>(provider, tmx, tmy);
|
||||
if (!info->init(totalInverse, *rec.fPaint)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return alloc->make<LinearPipelineContext>(shader, rec, info, alloc);
|
||||
} else {
|
||||
SkBitmapProcState* state = alloc->make<SkBitmapProcState>(provider, tmx, tmy);
|
||||
if (!state->setup(totalInverse, *rec.fPaint)) {
|
||||
return nullptr;
|
||||
}
|
||||
return alloc->make<BitmapProcShaderContext>(shader, rec, state);
|
||||
SkBitmapProcState* state = alloc->make<SkBitmapProcState>(provider, tmx, tmy);
|
||||
if (!state->setup(totalInverse, *rec.fPaint)) {
|
||||
return nullptr;
|
||||
}
|
||||
return alloc->make<BitmapProcShaderContext>(shader, rec, state);
|
||||
}
|
||||
|
@ -16,7 +16,6 @@ class SkBitmapProcLegacyShader : public SkShaderBase {
|
||||
private:
|
||||
friend class SkImageShader;
|
||||
|
||||
static size_t ContextSize(const ContextRec&, const SkImageInfo& srcInfo);
|
||||
static Context* MakeContext(const SkShaderBase&, TileMode tmx, TileMode tmy,
|
||||
const SkBitmapProvider&, const ContextRec&, SkArenaAlloc* alloc);
|
||||
|
||||
|
@ -61,11 +61,14 @@ bool SkImageShader::isOpaque() const {
|
||||
return fImage->isOpaque();
|
||||
}
|
||||
|
||||
bool SkImageShader::IsRasterPipelineOnly(SkColorType ct, SkShader::TileMode tx,
|
||||
SkShader::TileMode ty) {
|
||||
bool SkImageShader::IsRasterPipelineOnly(SkColorType ct, SkAlphaType at,
|
||||
SkShader::TileMode tx, SkShader::TileMode ty) {
|
||||
if (ct != kN32_SkColorType) {
|
||||
return true;
|
||||
}
|
||||
if (at == kUnpremul_SkAlphaType) {
|
||||
return true;
|
||||
}
|
||||
#ifndef SK_SUPPORT_LEGACY_TILED_BITMAPS
|
||||
if (tx != ty) {
|
||||
return true;
|
||||
@ -76,7 +79,8 @@ bool SkImageShader::IsRasterPipelineOnly(SkColorType ct, SkShader::TileMode tx,
|
||||
|
||||
bool SkImageShader::onIsRasterPipelineOnly() const {
|
||||
SkBitmapProvider provider(fImage.get(), nullptr);
|
||||
return IsRasterPipelineOnly(provider.info().colorType(), fTileModeX, fTileModeY);
|
||||
return IsRasterPipelineOnly(provider.info().colorType(), provider.info().alphaType(),
|
||||
fTileModeX, fTileModeY);
|
||||
}
|
||||
|
||||
SkShaderBase::Context* SkImageShader::onMakeContext(const ContextRec& rec,
|
||||
|
@ -29,7 +29,8 @@ public:
|
||||
|
||||
SkImageShader(sk_sp<SkImage>, TileMode tx, TileMode ty, const SkMatrix* localMatrix);
|
||||
|
||||
static bool IsRasterPipelineOnly(SkColorType, SkShader::TileMode tx, SkShader::TileMode ty);
|
||||
static bool IsRasterPipelineOnly(SkColorType, SkAlphaType,
|
||||
SkShader::TileMode tx, SkShader::TileMode ty);
|
||||
|
||||
protected:
|
||||
void flatten(SkWriteBuffer&) const override;
|
||||
|
@ -292,7 +292,7 @@ sk_sp<SkShader> SkPictureShader::refBitmapShader(const SkMatrix& viewMatrix, con
|
||||
}
|
||||
|
||||
bool SkPictureShader::onIsRasterPipelineOnly() const {
|
||||
return SkImageShader::IsRasterPipelineOnly(kN32_SkColorType, fTmx, fTmy);
|
||||
return SkImageShader::IsRasterPipelineOnly(kN32_SkColorType, kPremul_SkAlphaType, fTmx, fTmy);
|
||||
}
|
||||
|
||||
bool SkPictureShader::onAppendStages(SkRasterPipeline* p, SkColorSpace* cs, SkArenaAlloc* alloc,
|
||||
|
@ -1,107 +0,0 @@
|
||||
/*
|
||||
* 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 <string>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include "Resources.h"
|
||||
#include "SkCpu.h"
|
||||
#include "SkImage.h"
|
||||
#include "SkImage_Base.h"
|
||||
#include "SkOpts.h"
|
||||
#include "SkPM4fPriv.h"
|
||||
#include "SkNx.h"
|
||||
#include "Test.h"
|
||||
|
||||
typedef void (*Blender)(uint32_t* dst, const uint32_t* const srcStart, int ndst, const int nsrc);
|
||||
|
||||
static inline void srcover_srgb_srgb_1(uint32_t* dst, uint32_t src) {
|
||||
auto d = Sk4f_fromS32(*dst),
|
||||
s = Sk4f_fromS32( src);
|
||||
*dst = Sk4f_toS32(s + d * (1.0f - s[3]));
|
||||
}
|
||||
|
||||
static void brute_force_srcover_srgb_srgb(
|
||||
uint32_t* dst, const uint32_t* const src, int ndst, const int nsrc) {
|
||||
while (ndst > 0) {
|
||||
int n = SkTMin(ndst, nsrc);
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
srcover_srgb_srgb_1(dst++, src[i]);
|
||||
}
|
||||
ndst -= n;
|
||||
}
|
||||
}
|
||||
|
||||
static SkString mismatch_message(std::string resourceName, int x, int y,
|
||||
uint32_t src, uint32_t good, uint32_t bad) {
|
||||
return SkStringPrintf(
|
||||
"%s - missmatch at %d, %d src: %08x good: %08x bad: %08x",
|
||||
resourceName.c_str(), x, y, src, good, bad);
|
||||
}
|
||||
|
||||
static void test_blender(std::string resourceName, skiatest::Reporter* reporter) {
|
||||
std::string fileName = resourceName + ".png";
|
||||
sk_sp<SkImage> image = GetResourceAsImage(fileName.c_str());
|
||||
if (image == nullptr) {
|
||||
ERRORF(reporter, "image is NULL");
|
||||
return;
|
||||
}
|
||||
SkBitmap bm;
|
||||
sk_sp<SkColorSpace> srgbColorSpace = SkColorSpace::MakeSRGB();
|
||||
if (!as_IB(image)->getROPixels(&bm, srgbColorSpace.get())) {
|
||||
ERRORF(reporter, "Could not read resource");
|
||||
return;
|
||||
}
|
||||
|
||||
SkPixmap pixmap;
|
||||
bm.peekPixels(&pixmap);
|
||||
SkASSERTF(pixmap.colorType() == kN32_SkColorType, "colorType: %d", pixmap.colorType());
|
||||
SkASSERT(pixmap.alphaType() != kUnpremul_SkAlphaType);
|
||||
const uint32_t* src = pixmap.addr32();
|
||||
const int width = pixmap.rowBytesAsPixels();
|
||||
SkASSERT(width > 0);
|
||||
SkASSERT(width < 4000);
|
||||
SkAutoTArray<uint32_t> correctDst(width);
|
||||
SkAutoTArray<uint32_t> testDst(width);
|
||||
|
||||
for (int y = 0; y < pixmap.height(); y++) {
|
||||
// TODO: zero is not the most interesting dst to test srcover...
|
||||
sk_bzero(correctDst.get(), width * sizeof(uint32_t));
|
||||
sk_bzero(testDst.get(), width * sizeof(uint32_t));
|
||||
brute_force_srcover_srgb_srgb(correctDst.get(), src, width, width);
|
||||
SkOpts:: srcover_srgb_srgb( testDst.get(), src, width, width);
|
||||
for (int x = 0; x < width; x++) {
|
||||
REPORTER_ASSERT_MESSAGE(
|
||||
reporter, correctDst[x] == testDst[x],
|
||||
mismatch_message(resourceName, x, y, src[x], correctDst[x], testDst[x]));
|
||||
if (correctDst[x] != testDst[x]) break;
|
||||
}
|
||||
src += width;
|
||||
}
|
||||
}
|
||||
|
||||
DEF_TEST(SkBlend_optsCheck, reporter) {
|
||||
std::vector<std::string> testResources = {
|
||||
"yellow_rose", "baby_tux", "plane", "mandrill_512", "iconstrip"
|
||||
};
|
||||
|
||||
for (auto& resourceName : testResources) {
|
||||
test_blender(resourceName, reporter);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_TEST(SkBlend_optsSqrtCheck, reporter) {
|
||||
for (int c = 0; c < 256; c++) {
|
||||
Sk4f i{(float)c};
|
||||
Sk4f ii = i * i;
|
||||
Sk4f s = ii.sqrt() + 0.5f;
|
||||
Sk4f sf = s.floor();
|
||||
REPORTER_ASSERT_MESSAGE(
|
||||
reporter, i[0] == sf[0], SkStringPrintf("i: %f, s: %f", i[0], sf[0]));
|
||||
}
|
||||
}
|
@ -1,257 +0,0 @@
|
||||
/*
|
||||
* 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 <algorithm>
|
||||
#include <array>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include "SkLinearBitmapPipeline.h"
|
||||
#include "SkColor.h"
|
||||
#include "SkNx.h"
|
||||
#include "SkPoint.h"
|
||||
#include "SkPM4f.h"
|
||||
#include "Test.h"
|
||||
#include "SkLinearBitmapPipeline_tile.h"
|
||||
|
||||
|
||||
DEF_TEST(LBPBilerpEdge, reporter) {
|
||||
|
||||
}
|
||||
|
||||
static SkString dump(SkScalar cut, Span prefix, Span remainder) {
|
||||
SkPoint prefixStart; SkScalar prefixLen; int prefixCount;
|
||||
std::tie(prefixStart, prefixLen, prefixCount) = prefix;
|
||||
SkPoint remainderStart; SkScalar remainderLen; int remainderCount;
|
||||
std::tie(remainderStart, remainderLen, remainderCount) = remainder;
|
||||
return SkStringPrintf("cut: %f prefix: (%f, %f), %f, %d - remainder: (%f, %f), %f, %d",
|
||||
cut,
|
||||
prefixStart.fX, prefixStart.fY, prefixLen, prefixCount,
|
||||
remainderStart.fX, remainderStart.fY, remainderLen, remainderCount);
|
||||
}
|
||||
|
||||
static void check_span_result(
|
||||
skiatest::Reporter* reporter,
|
||||
Span span, SkScalar dx, SkScalar cut, SkPoint start, SkScalar len, int count) {
|
||||
SkPoint originalStart; SkScalar originalLen; int originalCount;
|
||||
std::tie(originalStart, originalLen, originalCount) = span;
|
||||
|
||||
Span prefix = span.breakAt(cut, dx);
|
||||
|
||||
SkPoint prefixStart; SkScalar prefixLen; int prefixCount;
|
||||
std::tie(prefixStart, prefixLen, prefixCount) = prefix;
|
||||
|
||||
REPORTER_ASSERT_MESSAGE(reporter, prefixStart == start, dump(cut, prefix, span));
|
||||
REPORTER_ASSERT_MESSAGE(reporter, prefixLen == len, dump(cut, prefix, span));
|
||||
REPORTER_ASSERT_MESSAGE(reporter, prefixCount == count, dump(cut, prefix, span));
|
||||
SkPoint expectedRemainderStart;
|
||||
SkScalar expectedRemainderLen;
|
||||
int expectedRemainderCount;
|
||||
if (prefix.isEmpty()) {
|
||||
expectedRemainderStart = originalStart;
|
||||
expectedRemainderLen = originalLen;
|
||||
expectedRemainderCount = originalCount;
|
||||
} else {
|
||||
expectedRemainderStart = SkPoint::Make(originalStart.fX + prefixLen + dx, originalStart.fY);
|
||||
expectedRemainderLen = originalLen - prefixLen - dx;
|
||||
expectedRemainderCount = originalCount - prefixCount;
|
||||
}
|
||||
|
||||
if (!span.isEmpty()) {
|
||||
SkPoint remainderStart;
|
||||
SkScalar remainderLen;
|
||||
int remainderCount;
|
||||
std::tie(remainderStart, remainderLen, remainderCount) = span;
|
||||
// Remainder span
|
||||
REPORTER_ASSERT_MESSAGE(reporter, expectedRemainderStart == remainderStart,
|
||||
dump(cut, prefix, span));
|
||||
REPORTER_ASSERT_MESSAGE(reporter,
|
||||
expectedRemainderLen == remainderLen,
|
||||
dump(cut, prefix, span));
|
||||
REPORTER_ASSERT_MESSAGE(reporter,
|
||||
expectedRemainderCount == remainderCount,
|
||||
dump(cut, prefix, span));
|
||||
}
|
||||
}
|
||||
|
||||
DEF_TEST(LBPSpanOps, reporter) {
|
||||
{
|
||||
SkScalar dx = 1.0f;
|
||||
SkPoint start = SkPoint::Make(-5, -5);
|
||||
Span span{start, 9.0f, 10};
|
||||
check_span_result(reporter, span, dx, 0.0f, start, 4.0f, 5);
|
||||
check_span_result(reporter, span, dx, -6.0f, SkPoint::Make(0, 0), 0.0f, 0);
|
||||
check_span_result(reporter, span, dx, -5.0f, SkPoint::Make(0, 0), 0.0f, 0);
|
||||
check_span_result(reporter, span, dx, -4.0f, SkPoint::Make(-5, -5), 0.0f, 1);
|
||||
check_span_result(reporter, span, dx, 4.0f, SkPoint::Make(-5, -5), 8.0f, 9);
|
||||
check_span_result(reporter, span, dx, 5.0f, SkPoint::Make(-5, -5), 9.0f, 10);
|
||||
check_span_result(reporter, span, dx, 6.0f, SkPoint::Make(-5, -5), 9.0f, 10);
|
||||
}
|
||||
{
|
||||
SkScalar dx = -1.0f;
|
||||
SkPoint start = SkPoint::Make(5, 5);
|
||||
Span span{start, -9.0f, 10};
|
||||
check_span_result(reporter, span, dx, 0.0f, start, -5.0f, 6);
|
||||
check_span_result(reporter, span, dx, -6.0f, SkPoint::Make(5, 5), -9.0f, 10);
|
||||
check_span_result(reporter, span, dx, -5.0f, SkPoint::Make(5, 5), -9.0f, 10);
|
||||
check_span_result(reporter, span, dx, -4.0f, SkPoint::Make(5, 5), -9.0f, 10);
|
||||
check_span_result(reporter, span, dx, 4.0f, SkPoint::Make(5, 5), -1.0f, 2);
|
||||
check_span_result(reporter, span, dx, 5.0f, SkPoint::Make(5, 5), 0.0f, 1);
|
||||
check_span_result(reporter, span, dx, 6.0f, SkPoint::Make(0, 0), 0.0f, 0);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_TEST(LBPBilerpSpanOps, reporter) {
|
||||
|
||||
}
|
||||
|
||||
template <typename XTiler, typename YTiler>
|
||||
static bool compare_tiler_case(
|
||||
XTiler& xTiler, YTiler& yTiler, Span span, skiatest::Reporter* reporter) {
|
||||
Span originalSpan = span;
|
||||
std::vector<SkPoint> listPoints;
|
||||
std::vector<SkPoint> spanPoints;
|
||||
struct Sink {
|
||||
void SK_VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) {
|
||||
SkASSERT(0 < n && n < 4);
|
||||
if (n >= 1) storePoint({xs[0], ys[0]});
|
||||
if (n >= 2) storePoint({xs[1], ys[1]});
|
||||
if (n >= 3) storePoint({xs[2], ys[2]});
|
||||
}
|
||||
|
||||
void SK_VECTORCALL pointList4(Sk4s xs, Sk4s ys) {
|
||||
storePoint({xs[0], ys[0]});
|
||||
storePoint({xs[1], ys[1]});
|
||||
storePoint({xs[2], ys[2]});
|
||||
storePoint({xs[3], ys[3]});
|
||||
}
|
||||
|
||||
void pointSpan(Span span) {
|
||||
span_fallback(span, this);
|
||||
}
|
||||
|
||||
void storePoint(SkPoint pt) {
|
||||
fPoints->push_back({SkScalarFloorToScalar(X(pt)), SkScalarFloorToScalar(Y(pt))});
|
||||
}
|
||||
|
||||
std::vector<SkPoint>* fPoints;
|
||||
};
|
||||
|
||||
Sink listSink = {&listPoints};
|
||||
Sink spanSink = {&spanPoints};
|
||||
|
||||
SkPoint start; SkScalar length; int count;
|
||||
std::tie(start, length, count) = span;
|
||||
|
||||
SkScalar dx = length / (count - 1);
|
||||
Sk4f xs = Sk4f{X(start)} + Sk4f{0.0f, dx, 2 * dx, 3 * dx};
|
||||
Sk4f ys = Sk4f{Y(start)};
|
||||
while (count >= 4) {
|
||||
Sk4f txs = xs;
|
||||
Sk4f tys = ys;
|
||||
xTiler.tileXPoints(&txs);
|
||||
yTiler.tileYPoints(&tys);
|
||||
listSink.pointList4(txs, tys);
|
||||
xs = xs + 4.0f * dx;
|
||||
count -= 4;
|
||||
}
|
||||
if (count > 0) {
|
||||
xTiler.tileXPoints(&xs);
|
||||
yTiler.tileYPoints(&ys);
|
||||
listSink.pointListFew(count, xs, ys);
|
||||
}
|
||||
|
||||
std::tie(start, length, count) = originalSpan;
|
||||
SkScalar x = X(start);
|
||||
SkScalar y = yTiler.tileY(Y(start));
|
||||
Span yAdjustedSpan{{x, y}, length, count};
|
||||
|
||||
bool handledSpan = xTiler.maybeProcessSpan(yAdjustedSpan, &spanSink);
|
||||
if (handledSpan) {
|
||||
auto firstNotTheSame = std::mismatch(
|
||||
listPoints.begin(), listPoints.end(), spanPoints.begin());
|
||||
if (firstNotTheSame.first != listSink.fPoints->end()) {
|
||||
auto element = std::distance(listPoints.begin(), firstNotTheSame.first);
|
||||
SkASSERT(element >= 0);
|
||||
std::tie(start, length, count) = originalSpan;
|
||||
ERRORF(reporter, "Span: {%f, %f}, %f, %d", start.fX, start.fY, length, count);
|
||||
ERRORF(reporter, "Size points: %d, size span: %d",
|
||||
listPoints.size(), spanPoints.size());
|
||||
if ((unsigned)element >= spanPoints.size()) {
|
||||
ERRORF(reporter, "Size points: %d, size span: %d",
|
||||
listPoints.size(), spanPoints.size());
|
||||
// Mismatch off the end
|
||||
ERRORF(reporter,
|
||||
"The mismatch is at position %d and has value %f, %f - it is off the end "
|
||||
"of the other.",
|
||||
element, X(*firstNotTheSame.first), Y(*firstNotTheSame.first));
|
||||
} else {
|
||||
ERRORF(reporter,
|
||||
"Mismatch at %d - points: %f, %f - span: %f, %f",
|
||||
element, listPoints[element].fX, listPoints[element].fY,
|
||||
spanPoints[element].fX, spanPoints[element].fY);
|
||||
}
|
||||
SkFAIL("aha");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename XTiler, typename YTiler>
|
||||
static bool compare_tiler_spans(int width, int height, skiatest::Reporter* reporter) {
|
||||
XTiler xTiler{width};
|
||||
YTiler yTiler{height};
|
||||
INFOF(reporter, "w: %d, h: %d \n", width, height);
|
||||
std::array<int, 8> interestingX {{-5, -1, 0, 1, width - 1, width, width + 1, width + 5}};
|
||||
std::array<int, 8> interestingY {{-5, -1, 0, 1, height - 1, height, height + 1, height + 5}};
|
||||
std::array<int, 6> interestingCount {{1, 2, 3, 4, 5, 10}};
|
||||
std::array<SkScalar, 7> interestingScale {{0.0f, 1.0f, 0.5f, 2.1f, -2.1f, -1.0f, -0.5f}};
|
||||
for (auto scale : interestingScale) {
|
||||
for (auto startX : interestingX) {
|
||||
for (auto count : interestingCount) {
|
||||
for (auto y : interestingY) {
|
||||
Span span{
|
||||
SkPoint::Make((SkScalar)startX, (SkScalar)y), (count-1.0f) * scale, count};
|
||||
if (!compare_tiler_case(xTiler, yTiler, span, reporter)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename XTiler, typename YTiler>
|
||||
static void test_tiler(skiatest::Reporter* reporter) {
|
||||
std::array<int, 6> interestingSize {{1, 2, 3, 4, 5, 10}};
|
||||
for (auto width : interestingSize) {
|
||||
for (auto height : interestingSize) {
|
||||
if (!compare_tiler_spans<XTiler, YTiler>(width, height, reporter)) { return; }
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
DEF_TEST(LBPStrategyClampTile, reporter) {
|
||||
#if 0
|
||||
ClampStrategy tiler{SkSize::Make(1, 1)};
|
||||
Span span{SkPoint::Make(0, -5), 1.0f, 2};
|
||||
compare_tiler_case<ClampStrategy>(tiler, span, reporter);
|
||||
#else
|
||||
test_tiler<XClampStrategy, YClampStrategy>(reporter);
|
||||
#endif
|
||||
}
|
||||
|
||||
DEF_TEST(LBPStrategyRepeatTile, reporter) {
|
||||
#if 0
|
||||
RepeatStrategy tiler{SkSize::Make(3, 1)};
|
||||
Span span{SkPoint::Make(-5, -5), 20 * 2.1f, 100};
|
||||
compare_tiler_case<RepeatStrategy>(tiler, span, reporter);
|
||||
#else
|
||||
test_tiler<XRepeatStrategy, YRepeatStrategy>(reporter);
|
||||
#endif
|
||||
}
|
||||
*/
|
Loading…
Reference in New Issue
Block a user