Add CubicResampler to makeShader
Only works on raster backend for the now. Bug: skia:10344 Change-Id: I2c82d5345ae83a2bb2744ab27e3d971c57ea989e Reviewed-on: https://skia-review.googlesource.com/c/skia/+/303271 Commit-Queue: Mike Reed <reed@google.com> Reviewed-by: Brian Salomon <bsalomon@google.com> Reviewed-by: Mike Klein <mtklein@google.com>
This commit is contained in:
parent
6241961a32
commit
3d30ca6d21
@ -6,23 +6,42 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "gm/gm.h"
|
#include "gm/gm.h"
|
||||||
#include "include/core/SkBitmap.h"
|
|
||||||
#include "include/core/SkCanvas.h"
|
#include "include/core/SkCanvas.h"
|
||||||
|
#include "include/core/SkSurface.h"
|
||||||
|
|
||||||
DEF_SIMPLE_GM(bicubic, canvas, 300, 64) {
|
DEF_SIMPLE_GM(bicubic, canvas, 300, 320) {
|
||||||
canvas->clear(SK_ColorBLACK);
|
canvas->clear(SK_ColorBLACK);
|
||||||
|
|
||||||
SkBitmap bmp;
|
auto make_img = []() {
|
||||||
bmp.allocN32Pixels(8, 1);
|
auto surf = SkSurface::MakeRasterN32Premul(7, 7);
|
||||||
bmp.eraseColor(0);
|
surf->getCanvas()->drawColor(SK_ColorBLACK);
|
||||||
*bmp.getAddr32(3, 0) = 0xFFFFFFFF;
|
|
||||||
|
|
||||||
|
SkPaint paint;
|
||||||
|
paint.setColor(SK_ColorWHITE);
|
||||||
|
surf->getCanvas()->drawLine(3.5f, 0, 3.5f, 8, paint);
|
||||||
|
return surf->makeImageSnapshot();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto img = make_img();
|
||||||
|
|
||||||
|
canvas->scale(40, 8);
|
||||||
|
for (auto q : {kNone_SkFilterQuality, kLow_SkFilterQuality, kHigh_SkFilterQuality}) {
|
||||||
|
SkPaint p;
|
||||||
|
p.setFilterQuality(q);
|
||||||
|
canvas->drawImage(img, 0, 0, &p);
|
||||||
|
canvas->translate(0, img->height() + 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
const SkRect r = SkRect::MakeIWH(img->width(), img->height());
|
||||||
SkPaint paint;
|
SkPaint paint;
|
||||||
paint.setFilterQuality(kHigh_SkFilterQuality);
|
|
||||||
|
|
||||||
for (int i = 0; i < 64; ++i) {
|
SkImage::CubicResampler cubics[] = {
|
||||||
float x = 1.0f + i/63.0f;
|
{ 0, 1.0f/2 },
|
||||||
float y = i;
|
{ 1.0f/3, 1.0f/3 },
|
||||||
canvas->drawBitmapRect(bmp, SkRect::MakeXYWH(x, y, 512, 1), &paint);
|
};
|
||||||
|
for (auto c : cubics) {
|
||||||
|
paint.setShader(img->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, c));
|
||||||
|
canvas->drawRect(r, paint);
|
||||||
|
canvas->translate(0, img->height() + 1.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include "include/core/SkFilterQuality.h"
|
#include "include/core/SkFilterQuality.h"
|
||||||
#include "include/core/SkImageEncoder.h"
|
#include "include/core/SkImageEncoder.h"
|
||||||
#include "include/core/SkImageInfo.h"
|
#include "include/core/SkImageInfo.h"
|
||||||
|
#include "include/core/SkM44.h"
|
||||||
#include "include/core/SkRefCnt.h"
|
#include "include/core/SkRefCnt.h"
|
||||||
#include "include/core/SkScalar.h"
|
#include "include/core/SkScalar.h"
|
||||||
#include "include/core/SkShader.h"
|
#include "include/core/SkShader.h"
|
||||||
@ -785,9 +786,40 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool isOpaque() const { return SkAlphaTypeIsOpaque(this->alphaType()); }
|
bool isOpaque() const { return SkAlphaTypeIsOpaque(this->alphaType()); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a shader with the specified tiling and mipmap sampling.
|
||||||
|
*/
|
||||||
sk_sp<SkShader> makeShader(SkTileMode tmx, SkTileMode tmy, const SkFilterOptions&,
|
sk_sp<SkShader> makeShader(SkTileMode tmx, SkTileMode tmy, const SkFilterOptions&,
|
||||||
const SkMatrix* localMatrix = nullptr) const;
|
const SkMatrix* localMatrix = nullptr) const;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Specify B and C (each between 0...1) to create a shader that applies the corresponding
|
||||||
|
* cubic reconstruction filter to the image.
|
||||||
|
*
|
||||||
|
* Example values:
|
||||||
|
* B = 1/3, C = 1/3 "Mitchell" filter
|
||||||
|
* B = 0, C = 1/2 "Catmull-Rom" filter
|
||||||
|
*
|
||||||
|
* See "Reconstruction Filters in Computer Graphics"
|
||||||
|
* Don P. Mitchell
|
||||||
|
* Arun N. Netravali
|
||||||
|
* 1988
|
||||||
|
* https://www.cs.utexas.edu/~fussell/courses/cs384g-fall2013/lectures/mitchell/Mitchell.pdf
|
||||||
|
*
|
||||||
|
* Desmos worksheet https://www.desmos.com/calculator/aghdpicrvr
|
||||||
|
* Nice overview https://entropymine.com/imageworsener/bicubic/
|
||||||
|
*/
|
||||||
|
struct CubicResampler {
|
||||||
|
float B, C;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a shader with the specified tiling and CubicResampler parameters.
|
||||||
|
* Returns nullptr if the resampler values are outside of [0...1]
|
||||||
|
*/
|
||||||
|
sk_sp<SkShader> makeShader(SkTileMode tmx, SkTileMode tmy, CubicResampler,
|
||||||
|
const SkMatrix* localMatrix = nullptr) const;
|
||||||
|
|
||||||
/** Creates SkShader from SkImage. SkShader dimensions are taken from SkImage. SkShader uses
|
/** Creates SkShader from SkImage. SkShader dimensions are taken from SkImage. SkShader uses
|
||||||
SkTileMode rules to fill drawn area outside SkImage. localMatrix permits
|
SkTileMode rules to fill drawn area outside SkImage. localMatrix permits
|
||||||
transforming SkImage before SkCanvas matrix is applied.
|
transforming SkImage before SkCanvas matrix is applied.
|
||||||
|
@ -79,6 +79,7 @@ public:
|
|||||||
// V76: Add filtering enum to ImageShader
|
// V76: Add filtering enum to ImageShader
|
||||||
// V77: Explicit filtering options on imageshaders
|
// V77: Explicit filtering options on imageshaders
|
||||||
// V78: Serialize skmipmap data for images that have it
|
// V78: Serialize skmipmap data for images that have it
|
||||||
|
// V79: Cubic Resampler option on imageshader
|
||||||
|
|
||||||
enum Version {
|
enum Version {
|
||||||
kMorphologyTakesScalar_Version = 74,
|
kMorphologyTakesScalar_Version = 74,
|
||||||
@ -86,10 +87,11 @@ public:
|
|||||||
kFilterEnumInImageShader_Version = 76,
|
kFilterEnumInImageShader_Version = 76,
|
||||||
kFilterOptionsInImageShader_Version = 77,
|
kFilterOptionsInImageShader_Version = 77,
|
||||||
kSerializeMipmaps_Version = 78,
|
kSerializeMipmaps_Version = 78,
|
||||||
|
kCubicResamplerImageShader_Version = 79,
|
||||||
|
|
||||||
// Only SKPs within the min/current picture version range (inclusive) can be read.
|
// Only SKPs within the min/current picture version range (inclusive) can be read.
|
||||||
kMin_Version = kMorphologyTakesScalar_Version,
|
kMin_Version = kMorphologyTakesScalar_Version,
|
||||||
kCurrent_Version = kSerializeMipmaps_Version
|
kCurrent_Version = kCubicResamplerImageShader_Version
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -142,6 +142,12 @@ sk_sp<SkShader> SkImage::makeShader(SkTileMode tmx, SkTileMode tmy,
|
|||||||
SkImageShader::FilterEnum(filtering));
|
SkImageShader::FilterEnum(filtering));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sk_sp<SkShader> SkImage::makeShader(SkTileMode tmx, SkTileMode tmy, CubicResampler cubic,
|
||||||
|
const SkMatrix* localMatrix) const {
|
||||||
|
return SkImageShader::Make(sk_ref_sp(const_cast<SkImage*>(this)), tmx, tmy, cubic,
|
||||||
|
localMatrix);
|
||||||
|
}
|
||||||
|
|
||||||
sk_sp<SkData> SkImage::encodeToData(SkEncodedImageFormat type, int quality) const {
|
sk_sp<SkData> SkImage::encodeToData(SkEncodedImageFormat type, int quality) const {
|
||||||
SkBitmap bm;
|
SkBitmap bm;
|
||||||
if (as_IB(this)->getROPixels(&bm)) {
|
if (as_IB(this)->getROPixels(&bm)) {
|
||||||
|
@ -22,6 +22,16 @@
|
|||||||
#include "src/shaders/SkBitmapProcShader.h"
|
#include "src/shaders/SkBitmapProcShader.h"
|
||||||
#include "src/shaders/SkEmptyShader.h"
|
#include "src/shaders/SkEmptyShader.h"
|
||||||
|
|
||||||
|
SkM44 SkImageShader::CubicResamplerMatrix(float B, float C) {
|
||||||
|
const float scale = 1.0f/18;
|
||||||
|
B *= scale;
|
||||||
|
C *= scale;
|
||||||
|
return SkM44( 3*B, -9*B - 18*C, 9*B + 36*C, -3*B - 18*C,
|
||||||
|
1 - 6*B, 0, -3 + 36*B + 18*C, 2 - 27*B - 18*C,
|
||||||
|
3*B, 9*B + 18*C, 3 - 45*B - 36*C, -2 + 27*B + 18*C,
|
||||||
|
0, 0, -18*C, 3*B + 18*C);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We are faster in clamp, so always use that tiling when we can.
|
* We are faster in clamp, so always use that tiling when we can.
|
||||||
*/
|
*/
|
||||||
@ -47,7 +57,8 @@ SkImageShader::SkImageShader(sk_sp<SkImage> img,
|
|||||||
, fTileModeY(optimize(tmy, fImage->height()))
|
, fTileModeY(optimize(tmy, fImage->height()))
|
||||||
, fFilterEnum(filtering)
|
, fFilterEnum(filtering)
|
||||||
, fClampAsIfUnpremul(clampAsIfUnpremul)
|
, fClampAsIfUnpremul(clampAsIfUnpremul)
|
||||||
, fFilterOptions({}) // ignored
|
, fFilterOptions({}) // ignored
|
||||||
|
, fCubic({}) // ignored
|
||||||
{
|
{
|
||||||
SkASSERT(filtering != kUseFilterOptions);
|
SkASSERT(filtering != kUseFilterOptions);
|
||||||
}
|
}
|
||||||
@ -63,6 +74,21 @@ SkImageShader::SkImageShader(sk_sp<SkImage> img,
|
|||||||
, fFilterEnum(FilterEnum::kUseFilterOptions)
|
, fFilterEnum(FilterEnum::kUseFilterOptions)
|
||||||
, fClampAsIfUnpremul(false)
|
, fClampAsIfUnpremul(false)
|
||||||
, fFilterOptions(options)
|
, fFilterOptions(options)
|
||||||
|
, fCubic({}) // ignored
|
||||||
|
{}
|
||||||
|
|
||||||
|
SkImageShader::SkImageShader(sk_sp<SkImage> img,
|
||||||
|
SkTileMode tmx, SkTileMode tmy,
|
||||||
|
SkImage::CubicResampler cubic,
|
||||||
|
const SkMatrix* localMatrix)
|
||||||
|
: INHERITED(localMatrix)
|
||||||
|
, fImage(std::move(img))
|
||||||
|
, fTileModeX(optimize(tmx, fImage->width()))
|
||||||
|
, fTileModeY(optimize(tmy, fImage->height()))
|
||||||
|
, fFilterEnum(FilterEnum::kUseCubicResampler)
|
||||||
|
, fClampAsIfUnpremul(false)
|
||||||
|
, fFilterOptions({}) // ignored
|
||||||
|
, fCubic(cubic)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
// fClampAsIfUnpremul is always false when constructed through public APIs,
|
// fClampAsIfUnpremul is always false when constructed through public APIs,
|
||||||
@ -74,13 +100,30 @@ sk_sp<SkFlattenable> SkImageShader::CreateProc(SkReadBuffer& buffer) {
|
|||||||
|
|
||||||
FilterEnum fe = kInheritFromPaint;
|
FilterEnum fe = kInheritFromPaint;
|
||||||
if (!buffer.isVersionLT(SkPicturePriv::kFilterEnumInImageShader_Version)) {
|
if (!buffer.isVersionLT(SkPicturePriv::kFilterEnumInImageShader_Version)) {
|
||||||
fe = buffer.read32LE<FilterEnum>(kUseFilterOptions);
|
fe = buffer.read32LE<FilterEnum>(kLast);
|
||||||
}
|
}
|
||||||
|
|
||||||
SkFilterOptions fo = { SkSamplingMode::kNearest, SkMipmapMode::kNone };
|
SkFilterOptions fo{ SkSamplingMode::kNearest, SkMipmapMode::kNone };
|
||||||
if (!buffer.isVersionLT(SkPicturePriv::kFilterOptionsInImageShader_Version)) {
|
SkImage::CubicResampler cubic{};
|
||||||
fo.fSampling = buffer.read32LE<SkSamplingMode>(SkSamplingMode::kLinear);
|
|
||||||
fo.fMipmap = buffer.read32LE<SkMipmapMode>(SkMipmapMode::kLinear);
|
if (buffer.isVersionLT(SkPicturePriv::kCubicResamplerImageShader_Version)) {
|
||||||
|
if (!buffer.isVersionLT(SkPicturePriv::kFilterOptionsInImageShader_Version)) {
|
||||||
|
fo.fSampling = buffer.read32LE<SkSamplingMode>(SkSamplingMode::kLinear);
|
||||||
|
fo.fMipmap = buffer.read32LE<SkMipmapMode>(SkMipmapMode::kLinear);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (fe) {
|
||||||
|
case kUseFilterOptions:
|
||||||
|
fo.fSampling = buffer.read32LE<SkSamplingMode>(SkSamplingMode::kLinear);
|
||||||
|
fo.fMipmap = buffer.read32LE<SkMipmapMode>(SkMipmapMode::kLinear);
|
||||||
|
break;
|
||||||
|
case kUseCubicResampler:
|
||||||
|
cubic.B = buffer.readScalar();
|
||||||
|
cubic.C = buffer.readScalar();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SkMatrix localMatrix;
|
SkMatrix localMatrix;
|
||||||
@ -90,17 +133,33 @@ sk_sp<SkFlattenable> SkImageShader::CreateProc(SkReadBuffer& buffer) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (fe == kUseFilterOptions)
|
switch (fe) {
|
||||||
? SkImageShader::Make(std::move(img), tmx, tmy, fo, &localMatrix)
|
case kUseFilterOptions:
|
||||||
: SkImageShader::Make(std::move(img), tmx, tmy, &localMatrix, fe);
|
return SkImageShader::Make(std::move(img), tmx, tmy, fo, &localMatrix);
|
||||||
|
case kUseCubicResampler:
|
||||||
|
return SkImageShader::Make(std::move(img), tmx, tmy, cubic, &localMatrix);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return SkImageShader::Make(std::move(img), tmx, tmy, &localMatrix, fe);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkImageShader::flatten(SkWriteBuffer& buffer) const {
|
void SkImageShader::flatten(SkWriteBuffer& buffer) const {
|
||||||
buffer.writeUInt((unsigned)fTileModeX);
|
buffer.writeUInt((unsigned)fTileModeX);
|
||||||
buffer.writeUInt((unsigned)fTileModeY);
|
buffer.writeUInt((unsigned)fTileModeY);
|
||||||
buffer.writeUInt((unsigned)fFilterEnum);
|
buffer.writeUInt((unsigned)fFilterEnum);
|
||||||
buffer.writeUInt((unsigned)fFilterOptions.fSampling);
|
switch (fFilterEnum) {
|
||||||
buffer.writeUInt((unsigned)fFilterOptions.fMipmap);
|
case kUseCubicResampler:
|
||||||
|
buffer.writeScalar(fCubic.B);
|
||||||
|
buffer.writeScalar(fCubic.C);
|
||||||
|
break;
|
||||||
|
case kUseFilterOptions:
|
||||||
|
buffer.writeUInt((unsigned)fFilterOptions.fSampling);
|
||||||
|
buffer.writeUInt((unsigned)fFilterOptions.fMipmap);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
buffer.writeMatrix(this->getLocalMatrix());
|
buffer.writeMatrix(this->getLocalMatrix());
|
||||||
buffer.writeImage(fImage.get());
|
buffer.writeImage(fImage.get());
|
||||||
SkASSERT(fClampAsIfUnpremul == false);
|
SkASSERT(fClampAsIfUnpremul == false);
|
||||||
@ -138,7 +197,8 @@ static bool legacy_shader_can_handle(const SkMatrix& inv) {
|
|||||||
|
|
||||||
SkShaderBase::Context* SkImageShader::onMakeContext(const ContextRec& rec,
|
SkShaderBase::Context* SkImageShader::onMakeContext(const ContextRec& rec,
|
||||||
SkArenaAlloc* alloc) const {
|
SkArenaAlloc* alloc) const {
|
||||||
if (fFilterEnum == kUseFilterOptions) {
|
// we only support the old SkFilterQuality setting
|
||||||
|
if (fFilterEnum > kInheritFromPaint) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,6 +294,20 @@ sk_sp<SkShader> SkImageShader::Make(sk_sp<SkImage> image,
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sk_sp<SkShader> SkImageShader::Make(sk_sp<SkImage> image, SkTileMode tmx, SkTileMode tmy,
|
||||||
|
SkImage::CubicResampler cubic, const SkMatrix* localMatrix) {
|
||||||
|
if (!(cubic.B >= 0 && cubic.B <= 1 &&
|
||||||
|
cubic.C >= 0 && cubic.C <= 1)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (!image) {
|
||||||
|
return sk_make_sp<SkEmptyShader>();
|
||||||
|
}
|
||||||
|
return sk_sp<SkShader>{
|
||||||
|
new SkImageShader(image, tmx, tmy, cubic, localMatrix)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#if SK_SUPPORT_GPU
|
#if SK_SUPPORT_GPU
|
||||||
@ -437,7 +511,9 @@ static void tweak_quality_and_inv_matrix(SkFilterQuality* quality, SkMatrix* mat
|
|||||||
bool SkImageShader::doStages(const SkStageRec& rec, SkImageStageUpdater* updater) const {
|
bool SkImageShader::doStages(const SkStageRec& rec, SkImageStageUpdater* updater) const {
|
||||||
SkFilterQuality quality;
|
SkFilterQuality quality;
|
||||||
switch (fFilterEnum) {
|
switch (fFilterEnum) {
|
||||||
case FilterEnum::kUseFilterOptions: return false; // TODO: support these in stages
|
case FilterEnum::kUseFilterOptions:
|
||||||
|
case FilterEnum::kUseCubicResampler:
|
||||||
|
return false; // TODO: support these in stages
|
||||||
case FilterEnum::kInheritFromPaint:
|
case FilterEnum::kInheritFromPaint:
|
||||||
quality = rec.fPaint.getFilterQuality();
|
quality = rec.fPaint.getFilterQuality();
|
||||||
break;
|
break;
|
||||||
@ -761,6 +837,12 @@ skvm::Color SkImageShader::onProgram(skvm::Builder* p,
|
|||||||
if (lowerWeight > 0) {
|
if (lowerWeight > 0) {
|
||||||
lower = &access->lowerLevel();
|
lower = &access->lowerLevel();
|
||||||
}
|
}
|
||||||
|
} else if (fFilterEnum == kUseCubicResampler){
|
||||||
|
auto* access = alloc->make<SkMipmapAccessor>(as_IB(fImage.get()), baseInv,
|
||||||
|
SkMipmapMode::kNone);
|
||||||
|
upper = &access->level();
|
||||||
|
upperInv = post_scale(upper->dimensions(), baseInv);
|
||||||
|
sampling = SamplingEnum::kBicubic;
|
||||||
} else {
|
} else {
|
||||||
// Convert from the filter-quality enum to our working description:
|
// Convert from the filter-quality enum to our working description:
|
||||||
// sampling : nearest, bilerp, bicubic
|
// sampling : nearest, bilerp, bicubic
|
||||||
@ -939,29 +1021,44 @@ skvm::Color SkImageShader::onProgram(skvm::Builder* p,
|
|||||||
// They're either the 16 corners of a 3x3 grid/ surrounding (x,y) at (0.5,0.5) off-center.
|
// They're either the 16 corners of a 3x3 grid/ surrounding (x,y) at (0.5,0.5) off-center.
|
||||||
skvm::F32 fx = fract(local.x + 0.5f),
|
skvm::F32 fx = fract(local.x + 0.5f),
|
||||||
fy = fract(local.y + 0.5f);
|
fy = fract(local.y + 0.5f);
|
||||||
|
skvm::F32 wx[4],
|
||||||
|
wy[4];
|
||||||
|
|
||||||
// See GrCubicEffect for details of these weights.
|
if (fFilterEnum == kUseCubicResampler) {
|
||||||
// TODO: these maybe don't seem right looking at gm/bicubic and GrBicubicEffect.
|
SkM44 weights = CubicResamplerMatrix(fCubic.B, fCubic.C);
|
||||||
auto near = [&](skvm::F32 t) {
|
|
||||||
// 1/18 + 9/18t + 27/18t^2 - 21/18t^3 == t ( t ( -21/18t + 27/18) + 9/18) + 1/18
|
auto dot = [](const skvm::F32 a[], const skvm::F32 b[]) {
|
||||||
return t * (t * (t * (-21/18.0f) + 27/18.0f) + 9/18.0f) + 1/18.0f;
|
return a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + a[3]*b[3];
|
||||||
};
|
};
|
||||||
auto far = [&](skvm::F32 t) {
|
const skvm::F32 tmpx[] = { p->splat(1.0f), fx, fx*fx, fx*fx*fx };
|
||||||
// 0/18 + 0/18*t - 6/18t^2 + 7/18t^3 == t^2 (7/18t - 6/18)
|
const skvm::F32 tmpy[] = { p->splat(1.0f), fy, fy*fy, fy*fy*fy };
|
||||||
return t * t * (t * (7/18.0f) - 6/18.0f);
|
|
||||||
};
|
for (int row = 0; row < 4; ++row) {
|
||||||
const skvm::F32 wx[] = {
|
SkV4 r = weights.row(row);
|
||||||
far (1.0f - fx),
|
skvm::F32 ru[] = {
|
||||||
near(1.0f - fx),
|
p->uniformF(uniforms->pushF(r[0])),
|
||||||
near( fx),
|
p->uniformF(uniforms->pushF(r[1])),
|
||||||
far ( fx),
|
p->uniformF(uniforms->pushF(r[2])),
|
||||||
};
|
p->uniformF(uniforms->pushF(r[3])),
|
||||||
const skvm::F32 wy[] = {
|
};
|
||||||
far (1.0f - fy),
|
wx[row] = dot(ru, tmpx);
|
||||||
near(1.0f - fy),
|
wy[row] = dot(ru, tmpy);
|
||||||
near( fy),
|
}
|
||||||
far ( fy),
|
} else { // legacy hard-coded weights
|
||||||
};
|
auto near = [&](skvm::F32 t) {
|
||||||
|
// 1/18 + 9/18t + 27/18t^2 - 21/18t^3 == t ( t ( -21/18t + 27/18) + 9/18) + 1/18
|
||||||
|
return t * (t * (t * (-21/18.0f) + 27/18.0f) + 9/18.0f) + 1/18.0f;
|
||||||
|
};
|
||||||
|
auto far = [&](skvm::F32 t) {
|
||||||
|
// 0/18 + 0/18*t - 6/18t^2 + 7/18t^3 == t^2 (7/18t - 6/18)
|
||||||
|
return t * t * (t * (7/18.0f) - 6/18.0f);
|
||||||
|
};
|
||||||
|
|
||||||
|
wx[0] = far (1.0f - fx); wy[0] = far (1.0f - fy);
|
||||||
|
wx[1] = near(1.0f - fx); wy[1] = near(1.0f - fy);
|
||||||
|
wx[2] = near( fx); wy[2] = near( fy);
|
||||||
|
wx[3] = far ( fx); wy[3] = far ( fy);
|
||||||
|
}
|
||||||
|
|
||||||
skvm::Color c;
|
skvm::Color c;
|
||||||
c.r = c.g = c.b = c.a = p->splat(0.0f);
|
c.r = c.g = c.b = c.a = p->splat(0.0f);
|
||||||
|
@ -26,6 +26,10 @@ public:
|
|||||||
kInheritFromPaint,
|
kInheritFromPaint,
|
||||||
// this signals we should use the new SkFilterOptions
|
// this signals we should use the new SkFilterOptions
|
||||||
kUseFilterOptions,
|
kUseFilterOptions,
|
||||||
|
// use fCubic and ignore FilterOptions
|
||||||
|
kUseCubicResampler,
|
||||||
|
|
||||||
|
kLast = kUseCubicResampler,
|
||||||
};
|
};
|
||||||
|
|
||||||
static sk_sp<SkShader> Make(sk_sp<SkImage>,
|
static sk_sp<SkShader> Make(sk_sp<SkImage>,
|
||||||
@ -41,12 +45,20 @@ public:
|
|||||||
const SkFilterOptions&,
|
const SkFilterOptions&,
|
||||||
const SkMatrix* localMatrix);
|
const SkMatrix* localMatrix);
|
||||||
|
|
||||||
|
static sk_sp<SkShader> Make(sk_sp<SkImage>,
|
||||||
|
SkTileMode tmx,
|
||||||
|
SkTileMode tmy,
|
||||||
|
SkImage::CubicResampler,
|
||||||
|
const SkMatrix* localMatrix);
|
||||||
|
|
||||||
bool isOpaque() const override;
|
bool isOpaque() const override;
|
||||||
|
|
||||||
#if SK_SUPPORT_GPU
|
#if SK_SUPPORT_GPU
|
||||||
std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&) const override;
|
std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&) const override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static SkM44 CubicResamplerMatrix(float B, float C);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SK_FLATTENABLE_HOOKS(SkImageShader)
|
SK_FLATTENABLE_HOOKS(SkImageShader)
|
||||||
|
|
||||||
@ -61,6 +73,11 @@ private:
|
|||||||
SkTileMode tmy,
|
SkTileMode tmy,
|
||||||
const SkFilterOptions&,
|
const SkFilterOptions&,
|
||||||
const SkMatrix* localMatrix);
|
const SkMatrix* localMatrix);
|
||||||
|
SkImageShader(sk_sp<SkImage>,
|
||||||
|
SkTileMode tmx,
|
||||||
|
SkTileMode tmy,
|
||||||
|
SkImage::CubicResampler,
|
||||||
|
const SkMatrix* localMatrix);
|
||||||
|
|
||||||
void flatten(SkWriteBuffer&) const override;
|
void flatten(SkWriteBuffer&) const override;
|
||||||
#ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
|
#ifdef SK_ENABLE_LEGACY_SHADERCONTEXT
|
||||||
@ -80,8 +97,9 @@ private:
|
|||||||
|
|
||||||
SkFilterQuality resolveFiltering(SkFilterQuality paintQuality) const {
|
SkFilterQuality resolveFiltering(SkFilterQuality paintQuality) const {
|
||||||
switch (fFilterEnum) {
|
switch (fFilterEnum) {
|
||||||
case kUseFilterOptions: return kNone_SkFilterQuality; // TODO
|
case kUseCubicResampler: return kHigh_SkFilterQuality; // TODO: handle explicitly
|
||||||
case kInheritFromPaint: return paintQuality;
|
case kUseFilterOptions: return kNone_SkFilterQuality; // TODO: handle explicitly
|
||||||
|
case kInheritFromPaint: return paintQuality;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
return (SkFilterQuality)fFilterEnum;
|
return (SkFilterQuality)fFilterEnum;
|
||||||
@ -95,6 +113,8 @@ private:
|
|||||||
|
|
||||||
// only use this if fFilterEnum == kUseFilterOptions
|
// only use this if fFilterEnum == kUseFilterOptions
|
||||||
SkFilterOptions fFilterOptions;
|
SkFilterOptions fFilterOptions;
|
||||||
|
// only use this if fFilterEnum == kUseCubicResampler
|
||||||
|
SkImage::CubicResampler fCubic;
|
||||||
|
|
||||||
friend class SkShaderBase;
|
friend class SkShaderBase;
|
||||||
typedef SkShaderBase INHERITED;
|
typedef SkShaderBase INHERITED;
|
||||||
|
@ -1471,3 +1471,33 @@ DEF_GPUTEST_FOR_ALL_CONTEXTS(ImageFlush, reporter, ctxInfo) {
|
|||||||
i2->flushAndSubmit(direct);
|
i2->flushAndSubmit(direct);
|
||||||
REPORTER_ASSERT(reporter, numSubmits() == 1);
|
REPORTER_ASSERT(reporter, numSubmits() == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "src/shaders/SkImageShader.h"
|
||||||
|
|
||||||
|
constexpr SkM44 gCentripetalCatmulRom
|
||||||
|
(0.0f/2, -1.0f/2, 2.0f/2, -1.0f/2,
|
||||||
|
2.0f/2, 0.0f/2, -5.0f/2, 3.0f/2,
|
||||||
|
0.0f/2, 1.0f/2, 4.0f/2, -3.0f/2,
|
||||||
|
0.0f/2, 0.0f/2, -1.0f/2, 1.0f/2);
|
||||||
|
|
||||||
|
constexpr SkM44 gMitchellNetravali
|
||||||
|
( 1.0f/18, -9.0f/18, 15.0f/18, -7.0f/18,
|
||||||
|
16.0f/18, 0.0f/18, -36.0f/18, 21.0f/18,
|
||||||
|
1.0f/18, 9.0f/18, 27.0f/18, -21.0f/18,
|
||||||
|
0.0f/18, 0.0f/18, -6.0f/18, 7.0f/18);
|
||||||
|
|
||||||
|
DEF_TEST(image_cubicresampler, reporter) {
|
||||||
|
auto diff = [reporter](const SkM44& a, const SkM44& b) {
|
||||||
|
const float tolerance = 0.000001f;
|
||||||
|
for (int r = 0; r < 4; ++r) {
|
||||||
|
for (int c = 0; c < 4; ++c) {
|
||||||
|
float d = std::abs(a.rc(r, c) - b.rc(r, c));
|
||||||
|
REPORTER_ASSERT(reporter, d <= tolerance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
diff(SkImageShader::CubicResamplerMatrix(1.0f/3, 1.0f/3), gMitchellNetravali);
|
||||||
|
|
||||||
|
diff(SkImageShader::CubicResamplerMatrix(0, 1.0f/2), gCentripetalCatmulRom);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user