Picture shaders support perspective better
Adds a new GM that draws text in an SkPicture, transformed by a perspective matrix. When drawn directly with drawPicture() it looks fine. When drawn calculating a scale factor manually from the matrix and converting to an image (SkImage::MakeFromPicture + drawImageRect), it looks nearly indistinguishable. However, when drawing using a picture shader, the image resolution of the cached rasterization is far too low. Originally, this is because SkPictureShader only relied on SkMatrix::decomposeScale, which fails if there's perspective and the picture shader's fallback is to switch to the identity scale. When the source picture has relatively small local bounds compared to the final destination, this results in severe sampling artifacts. This CL switches the fallback to use SkMatrixPriv::DifferentialAreaScale, which is what was used to compute the manual scale factor in the GM, and is what image filters use when creating their device's coordinate space when the canvas has perspective. Change-Id: I856cd1277eb7cfcb43c766ff0d123ee1a667fe29 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/507917 Auto-Submit: Michael Ludwig <michaelludwig@google.com> Reviewed-by: Brian Osman <brianosman@google.com> Commit-Queue: Michael Ludwig <michaelludwig@google.com>
This commit is contained in:
parent
51a553d8c5
commit
ff616b89e3
@ -20,6 +20,7 @@
|
||||
#include "include/core/SkShader.h"
|
||||
#include "include/core/SkSize.h"
|
||||
#include "include/core/SkString.h"
|
||||
#include "include/core/SkTextBlob.h"
|
||||
#include "include/core/SkTileMode.h"
|
||||
#include "include/core/SkTypes.h"
|
||||
#include "tools/ToolUtils.h"
|
||||
@ -236,3 +237,74 @@ DEF_SIMPLE_GM(tiled_picture_shader, canvas, 400, 400) {
|
||||
SkFilterMode::kNearest));
|
||||
canvas->drawPaint(p);
|
||||
}
|
||||
|
||||
DEF_SIMPLE_GM(pictureshader_persp, canvas, 215, 110) {
|
||||
enum class DrawStrategy {
|
||||
kDirect,
|
||||
kPictureShader,
|
||||
};
|
||||
|
||||
auto drawPicture = [](SkCanvas* canvas, sk_sp<SkPicture> picture, DrawStrategy strategy) {
|
||||
// Only want local upper 50x50 of 'picture' before we apply decal (or clip)
|
||||
SkRect bounds = {0.f, 0.f, 50.f, 50.f};
|
||||
switch(strategy) {
|
||||
case DrawStrategy::kDirect: {
|
||||
canvas->clipRect(bounds, true);
|
||||
canvas->drawPicture(picture);
|
||||
break; }
|
||||
case DrawStrategy::kPictureShader: {
|
||||
SkPaint paint;
|
||||
paint.setShader(picture->makeShader(SkTileMode::kDecal, SkTileMode::kDecal,
|
||||
SkFilterMode::kLinear, nullptr, &bounds));
|
||||
canvas->drawRect({0.f, 0.f, 50.f, 50.f}, paint);
|
||||
break; }
|
||||
}
|
||||
};
|
||||
|
||||
auto picture = []() {
|
||||
sk_sp<SkTypeface> typeface = SkTypeface::MakeDefault();
|
||||
if (!typeface) {
|
||||
typeface = SkTypeface::MakeFromName("monospace", SkFontStyle());
|
||||
}
|
||||
SkFont font;
|
||||
font.setTypeface(typeface);
|
||||
font.setHinting(SkFontHinting::kNormal);
|
||||
font.setSize(8.f);
|
||||
|
||||
SkPaint paint;
|
||||
paint.setColor(SK_ColorGREEN);
|
||||
SkPictureRecorder recorder;
|
||||
SkCanvas* record_canvas = recorder.beginRecording({0, 0, 100, 100});
|
||||
record_canvas->drawTextBlob(SkTextBlob::MakeFromString("Hamburgefons", font),
|
||||
0, 16.f, paint);
|
||||
return recorder.finishRecordingAsPicture();
|
||||
}();
|
||||
|
||||
SkM44 m;
|
||||
m.preScale(2.f, 2.f);
|
||||
SkM44 persp = SkM44::Perspective(0.01f, 10.f, SK_ScalarPI / 3.f);
|
||||
persp.preTranslate(0.f, 5.f, -0.1f);
|
||||
persp.preConcat(SkM44::Rotate({0.f, 1.f, 0.f}, 0.008f));
|
||||
m.postConcat(persp);
|
||||
|
||||
canvas->clear(SK_ColorBLACK);
|
||||
canvas->translate(5.f, 5.f);
|
||||
for (auto strategy : { DrawStrategy::kDirect,
|
||||
DrawStrategy::kPictureShader }) {
|
||||
canvas->save();
|
||||
|
||||
SkPaint outline;
|
||||
outline.setColor(SK_ColorWHITE);
|
||||
outline.setStyle(SkPaint::kStroke_Style);
|
||||
outline.setStrokeWidth(1.f);
|
||||
canvas->drawRect({-1, -1, 101, 101}, outline);
|
||||
|
||||
canvas->clipRect({0, 0, 100, 100});
|
||||
canvas->concat(m);
|
||||
|
||||
drawPicture(canvas, picture, strategy);
|
||||
canvas->restore();
|
||||
|
||||
canvas->translate(105.f, 0.f);
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "include/private/SkImageInfoPriv.h"
|
||||
#include "src/core/SkArenaAlloc.h"
|
||||
#include "src/core/SkImagePriv.h"
|
||||
#include "src/core/SkMatrixPriv.h"
|
||||
#include "src/core/SkMatrixProvider.h"
|
||||
#include "src/core/SkMatrixUtils.h"
|
||||
#include "src/core/SkPicturePriv.h"
|
||||
@ -184,7 +185,13 @@ struct CachedImageInfo {
|
||||
SkSize size;
|
||||
// Use a rotation-invariant scale
|
||||
if (!m.decomposeScale(&size, nullptr)) {
|
||||
size = {1, 1};
|
||||
SkPoint center = {bounds.centerX(), bounds.centerY()};
|
||||
SkScalar area = SkMatrixPriv::DifferentialAreaScale(m, center);
|
||||
if (!SkScalarIsFinite(area) || SkScalarNearlyZero(area)) {
|
||||
size = {1, 1}; // ill-conditioned matrix
|
||||
} else {
|
||||
size.fWidth = size.fHeight = SkScalarSqrt(area);
|
||||
}
|
||||
}
|
||||
size.fWidth *= bounds.width();
|
||||
size.fHeight *= bounds.height();
|
||||
|
Loading…
Reference in New Issue
Block a user