Don't use sprite-blit if cubic sampling
Need this to land first: https://chromium-review.googlesource.com/c/chromium/src/+/2597643 Bug: skia:7650 Change-Id: Idebecfc8a5922a937fd46ccff1eea4df53f6b0cd Reviewed-on: https://skia-review.googlesource.com/c/skia/+/345170 Reviewed-by: Mike Klein <mtklein@google.com> Reviewed-by: Florin Malita <fmalita@google.com> Commit-Queue: Mike Reed <reed@google.com>
This commit is contained in:
parent
88883c4ca2
commit
6aea078802
@ -238,6 +238,7 @@ tests_sources = [
|
||||
"$_tests/SVGDeviceTest.cpp",
|
||||
"$_tests/SafeMathTest.cpp",
|
||||
"$_tests/SamplePatternDictionaryTest.cpp",
|
||||
"$_tests/SamplingTest.cpp",
|
||||
"$_tests/ScalarTest.cpp",
|
||||
"$_tests/ScaleToSidesTest.cpp",
|
||||
"$_tests/SerialProcsTest.cpp",
|
||||
|
@ -2708,7 +2708,8 @@ private:
|
||||
/**
|
||||
* Returns true if the paint's imagefilter can be invoked directly, without needed a layer.
|
||||
*/
|
||||
bool canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint&);
|
||||
bool canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkSamplingOptions&,
|
||||
const SkPaint&);
|
||||
|
||||
/**
|
||||
* Returns true if the clip (for any active layer) contains antialiasing.
|
||||
|
@ -2205,13 +2205,14 @@ void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
|
||||
this->topDevice()->drawPath(path, layer.paint());
|
||||
}
|
||||
|
||||
bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) {
|
||||
bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h,
|
||||
const SkSamplingOptions& sampling, const SkPaint& paint) {
|
||||
if (!paint.getImageFilter()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const SkMatrix& ctm = this->getTotalMatrix();
|
||||
if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) {
|
||||
if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), sampling, paint)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2257,8 +2258,11 @@ void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const S
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: this should be passed in directly by the client
|
||||
const SkSamplingOptions sampling(paint ? paint->getFilterQuality() : kNone_SkFilterQuality);
|
||||
|
||||
if (realPaint.getImageFilter() &&
|
||||
this->canDrawBitmapAsSprite(x, y, image->width(), image->height(), realPaint) &&
|
||||
this->canDrawBitmapAsSprite(x, y, image->width(), image->height(), sampling, realPaint) &&
|
||||
!image_to_color_filter(&realPaint)) {
|
||||
// Evaluate the image filter directly on the input image and then draw the result, instead
|
||||
// of first drawing the image to a temporary layer and filtering.
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "src/core/SkPathPriv.h"
|
||||
#include "src/core/SkRasterClip.h"
|
||||
#include "src/core/SkRectPriv.h"
|
||||
#include "src/core/SkSamplingPriv.h"
|
||||
#include "src/core/SkScan.h"
|
||||
#include "src/core/SkStroke.h"
|
||||
#include "src/core/SkTLazy.h"
|
||||
@ -37,13 +38,6 @@
|
||||
|
||||
#include <utility>
|
||||
|
||||
static bool allows_sprite(const SkSamplingOptions& sampling) {
|
||||
// Legacy behavior is to ignore sampling if there is no matrix, but new behavior
|
||||
// should respect cubic, and not draw as sprite. Need to rebaseline test images
|
||||
// to respect this...
|
||||
// return !sampling.useCubic
|
||||
return true;
|
||||
}
|
||||
static SkPaint make_paint_with_image(const SkPaint& origPaint, const SkBitmap& bitmap,
|
||||
const SkSamplingOptions& sampling,
|
||||
SkMatrix* matrix = nullptr) {
|
||||
@ -983,7 +977,8 @@ void SkDraw::drawBitmapAsMask(const SkBitmap& bitmap, const SkSamplingOptions& s
|
||||
}
|
||||
|
||||
SkMatrix ctm = fMatrixProvider->localToDevice();
|
||||
if (allows_sprite(sampling) && SkTreatAsSprite(ctm, bitmap.dimensions(), paint)) {
|
||||
if (SkTreatAsSprite(ctm, bitmap.dimensions(), sampling, paint))
|
||||
{
|
||||
int ix = SkScalarRoundToInt(ctm.getTranslateX());
|
||||
int iy = SkScalarRoundToInt(ctm.getTranslateY());
|
||||
|
||||
@ -1101,8 +1096,7 @@ void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix,
|
||||
}
|
||||
|
||||
if (bitmap.colorType() != kAlpha_8_SkColorType
|
||||
&& allows_sprite(sampling)
|
||||
&& SkTreatAsSprite(matrix, bitmap.dimensions(), *paint)) {
|
||||
&& SkTreatAsSprite(matrix, bitmap.dimensions(), sampling, *paint)) {
|
||||
//
|
||||
// It is safe to call lock pixels now, since we know the matrix is
|
||||
// (more or less) identity.
|
||||
|
@ -1628,8 +1628,14 @@ void SkMatrix::dump() const {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "src/core/SkMatrixUtils.h"
|
||||
#include "src/core/SkSamplingPriv.h"
|
||||
|
||||
bool SkTreatAsSprite(const SkMatrix& mat, const SkISize& size, const SkSamplingOptions& sampling,
|
||||
const SkPaint& paint) {
|
||||
if (!SkSamplingPriv::NoChangeWithIdentityMatrix(sampling)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SkTreatAsSprite(const SkMatrix& mat, const SkISize& size, const SkPaint& paint) {
|
||||
// Our path aa is 2-bits, and our rect aa is 8, so we could use 8,
|
||||
// but in practice 4 seems enough (still looks smooth) and allows
|
||||
// more slightly fractional cases to fall into the fast (sprite) case.
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
class SkMatrix;
|
||||
class SkPaint;
|
||||
struct SkSamplingOptions;
|
||||
|
||||
/**
|
||||
* Given a matrix, size and paint, return true if the computed dst-rect would
|
||||
@ -22,7 +23,8 @@ class SkPaint;
|
||||
*
|
||||
* The src-rect is defined to be { 0, 0, size.width(), size.height() }
|
||||
*/
|
||||
bool SkTreatAsSprite(const SkMatrix&, const SkISize& size, const SkPaint& paint);
|
||||
bool SkTreatAsSprite(const SkMatrix&, const SkISize& size, const SkSamplingOptions&,
|
||||
const SkPaint&);
|
||||
|
||||
/** Decomposes the upper-left 2x2 of the matrix into a rotation (represented by
|
||||
the cosine and sine of the rotation angle), followed by a non-uniform scale,
|
||||
|
29
src/core/SkSamplingPriv.h
Normal file
29
src/core/SkSamplingPriv.h
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2020 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkSamplingPriv_DEFINED
|
||||
#define SkSamplingPriv_DEFINED
|
||||
|
||||
#include "include/core/SkSamplingOptions.h"
|
||||
|
||||
class SkSamplingPriv {
|
||||
public:
|
||||
// Returns true if the sampling can be ignored when the CTM is identity.
|
||||
static bool NoChangeWithIdentityMatrix(const SkSamplingOptions& sampling) {
|
||||
#ifdef SK_SUPPORT_LEGACY_SPRITE_IGNORE_HQ
|
||||
// Legacy behavior is to ignore sampling if there is identity matrix, even with cubic
|
||||
// reampling.
|
||||
return true;
|
||||
#else
|
||||
// If B == 0, the cubic resampler should have no effect for identity matrices
|
||||
// https://entropymine.com/imageworsener/bicubic/
|
||||
return !sampling.useCubic || sampling.cubic.B == 0;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
@ -54,12 +54,14 @@ static void test_treatAsSprite(skiatest::Reporter* reporter) {
|
||||
SkPaint aaPaint;
|
||||
aaPaint.setAntiAlias(true);
|
||||
|
||||
const SkSamplingOptions sampling;
|
||||
|
||||
// assert: translate-only no-aa can always be treated as sprite
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
rand_matrix(&mat, rand, SkMatrix::kTranslate_Mask);
|
||||
for (int j = 0; j < 1000; ++j) {
|
||||
rand_size(&size, rand);
|
||||
REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, size, noaaPaint));
|
||||
REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, size, sampling, noaaPaint));
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,8 +70,8 @@ static void test_treatAsSprite(skiatest::Reporter* reporter) {
|
||||
rand_matrix(&mat, rand, SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask);
|
||||
for (int j = 0; j < 1000; ++j) {
|
||||
rand_size(&size, rand);
|
||||
REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, size, noaaPaint));
|
||||
REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, size, aaPaint));
|
||||
REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, size, sampling, noaaPaint));
|
||||
REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, size, sampling, aaPaint));
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,33 +79,33 @@ static void test_treatAsSprite(skiatest::Reporter* reporter) {
|
||||
|
||||
const SkScalar tooMuchSubpixel = 100.1f;
|
||||
mat.setTranslate(tooMuchSubpixel, 0);
|
||||
REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, size, aaPaint));
|
||||
REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, size, sampling, aaPaint));
|
||||
mat.setTranslate(0, tooMuchSubpixel);
|
||||
REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, size, aaPaint));
|
||||
REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, size, sampling, aaPaint));
|
||||
|
||||
const SkScalar tinySubPixel = 100.02f;
|
||||
mat.setTranslate(tinySubPixel, 0);
|
||||
REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, size, aaPaint));
|
||||
REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, size, sampling, aaPaint));
|
||||
mat.setTranslate(0, tinySubPixel);
|
||||
REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, size, aaPaint));
|
||||
REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, size, sampling, aaPaint));
|
||||
|
||||
const SkScalar twoThirds = SK_Scalar1 * 2 / 3;
|
||||
const SkScalar bigScale = (size.width() + twoThirds) / size.width();
|
||||
mat.setScale(bigScale, bigScale);
|
||||
REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, size, noaaPaint));
|
||||
REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, size, aaPaint));
|
||||
REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, size, sampling, noaaPaint));
|
||||
REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, size, sampling, aaPaint));
|
||||
|
||||
const SkScalar oneThird = SK_Scalar1 / 3;
|
||||
const SkScalar smallScale = (size.width() + oneThird) / size.width();
|
||||
mat.setScale(smallScale, smallScale);
|
||||
REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, size, noaaPaint));
|
||||
REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, size, aaPaint));
|
||||
REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, size, sampling, noaaPaint));
|
||||
REPORTER_ASSERT(reporter, !SkTreatAsSprite(mat, size, sampling, aaPaint));
|
||||
|
||||
const SkScalar oneFortyth = SK_Scalar1 / 40;
|
||||
const SkScalar tinyScale = (size.width() + oneFortyth) / size.width();
|
||||
mat.setScale(tinyScale, tinyScale);
|
||||
REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, size, noaaPaint));
|
||||
REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, size, aaPaint));
|
||||
REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, size, sampling, noaaPaint));
|
||||
REPORTER_ASSERT(reporter, SkTreatAsSprite(mat, size, sampling, aaPaint));
|
||||
}
|
||||
|
||||
static void test_wacky_bitmapshader(skiatest::Reporter* reporter,
|
||||
|
57
tests/SamplingTest.cpp
Normal file
57
tests/SamplingTest.cpp
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "include/core/SkCanvas.h"
|
||||
#include "include/core/SkSurface.h"
|
||||
#include "include/utils/SkRandom.h"
|
||||
#include "src/core/SkSamplingPriv.h"
|
||||
#include "tests/Test.h"
|
||||
#include "tools/Resources.h"
|
||||
#include "tools/ToolUtils.h"
|
||||
|
||||
// In general, sampling under identity matrix should not affect the pixels. However,
|
||||
// cubic resampling when B != 0 is expected to change pixels.
|
||||
//
|
||||
DEF_TEST(sampling_with_identity_matrix, r) {
|
||||
const char* names[] = {
|
||||
"images/mandrill_128.png", "images/color_wheel.jpg",
|
||||
};
|
||||
|
||||
SkRandom rand;
|
||||
for (auto name : names) {
|
||||
auto src = GetResourceAsImage(name);
|
||||
auto surf = SkSurface::MakeRasterN32Premul(src->width(), src->height());
|
||||
auto canvas = surf->getCanvas();
|
||||
|
||||
auto dotest = [&](const SkSamplingOptions& sampling, bool expect_same) {
|
||||
canvas->clear(0);
|
||||
canvas->drawImage(src.get(), 0, 0, sampling, nullptr);
|
||||
auto dst = surf->makeImageSnapshot();
|
||||
|
||||
REPORTER_ASSERT(r, SkSamplingPriv::NoChangeWithIdentityMatrix(sampling) == expect_same);
|
||||
REPORTER_ASSERT(r, ToolUtils::equal_pixels(src.get(), dst.get()) == expect_same);
|
||||
};
|
||||
|
||||
// Exercise all non-cubics -- expecting no changes
|
||||
for (auto m : {SkMipmapMode::kNone, SkMipmapMode::kNearest, SkMipmapMode::kLinear}) {
|
||||
for (auto f : {SkFilterMode::kNearest, SkFilterMode::kLinear}) {
|
||||
dotest(SkSamplingOptions(f, m), true);
|
||||
}
|
||||
}
|
||||
|
||||
// Exercise cubic variants with B zero and non-zero
|
||||
constexpr int N = 30; // try a bunch of random values
|
||||
for (int i = 0; i < N; ++i) {
|
||||
float C = rand.nextF();
|
||||
dotest(SkSamplingOptions({0, C}), true);
|
||||
|
||||
float B = rand.nextF() * 0.9f + 0.05f; // non-zero but still within (0,,,1]
|
||||
SkASSERT(B != 0);
|
||||
dotest(SkSamplingOptions({B, C}), false);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user