Prevent outset Ws from going negative
Bug: skia:9028 Change-Id: I8e3d37050d3fce7602eee62ae911eae756e603a2 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/211100 Commit-Queue: Michael Ludwig <michaelludwig@google.com> Reviewed-by: Brian Osman <brianosman@google.com>
This commit is contained in:
parent
d1701ba884
commit
784184ab93
159
gm/postercircle.cpp
Normal file
159
gm/postercircle.cpp
Normal file
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright 2019 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "gm/gm.h"
|
||||
#include "include/core/SkCanvas.h"
|
||||
#include "include/core/SkFont.h"
|
||||
#include "include/core/SkImage.h"
|
||||
#include "include/core/SkRRect.h"
|
||||
#include "include/core/SkSurface.h"
|
||||
#include "tools/timer/AnimTimer.h"
|
||||
|
||||
// Mimics https://output.jsbin.com/falefice/1/quiet?CC_POSTER_CIRCLE, which can't be captured as
|
||||
// an SKP due to many 3D layers being composited post-SKP capture.
|
||||
// See skbug.com/9028
|
||||
class PosterCircleGM : public skiagm::GM {
|
||||
public:
|
||||
PosterCircleGM() : fTime(0.f) {}
|
||||
|
||||
protected:
|
||||
|
||||
SkString onShortName() override {
|
||||
return SkString("poster_circle");
|
||||
}
|
||||
|
||||
SkISize onISize() override {
|
||||
return SkISize::Make(kStageWidth, kStageHeight + 50);
|
||||
}
|
||||
|
||||
bool onAnimate(const AnimTimer& timer) override {
|
||||
#if 0
|
||||
if (timer.isRunning()) {
|
||||
// Use a fixed timestep
|
||||
fTime += 5e-4f;
|
||||
}
|
||||
#else
|
||||
fTime = timer.scaled(0.5f);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void onOnceBeforeDraw() override {
|
||||
SkFont font;
|
||||
font.setEdging(SkFont::Edging::kAntiAlias);
|
||||
font.setEmbolden(true);
|
||||
font.setSize(24.f);
|
||||
|
||||
sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(kPosterSize, kPosterSize);
|
||||
for (int i = 0; i < kNumAngles; ++i) {
|
||||
SkCanvas* canvas = surface->getCanvas();
|
||||
|
||||
SkPaint fillPaint;
|
||||
fillPaint.setAntiAlias(true);
|
||||
fillPaint.setColor(i % 2 == 0 ? SkColorSetRGB(0x99, 0x5C, 0x7F)
|
||||
: SkColorSetRGB(0x83, 0x5A, 0x99));
|
||||
canvas->drawRRect(SkRRect::MakeRectXY(SkRect::MakeWH(kPosterSize, kPosterSize),
|
||||
10.f, 10.f), fillPaint);
|
||||
|
||||
SkString label;
|
||||
label.printf("%d", i);
|
||||
SkRect labelBounds;
|
||||
font.measureText(label.c_str(), label.size(), kUTF8_SkTextEncoding, &labelBounds);
|
||||
SkScalar labelX = 0.5f * kPosterSize - 0.5f * labelBounds.width();
|
||||
SkScalar labelY = 0.5f * kPosterSize + 0.5f * labelBounds.height();
|
||||
|
||||
|
||||
SkPaint labelPaint;
|
||||
labelPaint.setAntiAlias(true);
|
||||
canvas->drawString(label, labelX, labelY, font, labelPaint);
|
||||
|
||||
fPosterImages[i] = surface->makeImageSnapshot();
|
||||
}
|
||||
}
|
||||
|
||||
void onDraw(SkCanvas* canvas) override {
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/perspective
|
||||
// for projection matrix when --webkit-perspective: 800px is used.
|
||||
SkMatrix44 proj(SkMatrix44::kIdentity_Constructor);
|
||||
proj.set(3, 2, -1.f / 800.f);
|
||||
|
||||
for (int pass = 0; pass < 2; ++pass) {
|
||||
// Want to draw 90 to 270 first (the back), then 270 to 90 (the front), but do all 3
|
||||
// rings backsides, then their frontsides since the front projections overlap across
|
||||
// rings. Note: we skip the poster circle's x axis rotation because that complicates the
|
||||
// back-to-front drawing order and it isn't necessary to trigger draws aligned with Z.
|
||||
bool drawFront = pass > 0;
|
||||
|
||||
for (int y = 0; y < 3; ++y) {
|
||||
float ringY = (y - 1) * (kPosterSize + 10.f);
|
||||
for (int i = 0; i < kNumAngles; ++i) {
|
||||
// Add an extra 45 degree rotation, which triggers the bug by aligning some of
|
||||
// the posters with the z axis.
|
||||
SkScalar yDuration = 5.f - y;
|
||||
SkScalar yRotation = SkScalarMod(kAngleStep * i +
|
||||
360.f * SkScalarMod(fTime / yDuration, yDuration), 360.f);
|
||||
// These rotation limits were chosen manually to line up with current projection
|
||||
static constexpr SkScalar kBackMinAngle = 70.f;
|
||||
static constexpr SkScalar kBackMaxAngle = 290.f;
|
||||
if (drawFront) {
|
||||
if (yRotation >= kBackMinAngle && yRotation <= kBackMaxAngle) {
|
||||
// Back portion during a front draw
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (yRotation < kBackMinAngle || yRotation > kBackMaxAngle) {
|
||||
// Front portion during a back draw
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
canvas->save();
|
||||
|
||||
// Matrix matches transform: rotateY(<angle>deg) translateZ(200px); nested in an
|
||||
// element with the perspective projection matrix above.
|
||||
SkMatrix44 model;
|
||||
// No post/preRotate, so start with rotation matrix and adjust from there
|
||||
model.setRotateAboutUnit(0.f, 1.f, 0.f, SkDegreesToRadians(yRotation));
|
||||
model.preTranslate(0.f, 0.f, kRingRadius); // *before* rotation
|
||||
model.postTranslate(0.f, ringY, 0.f); // *after* rotation
|
||||
model.postConcat(proj);
|
||||
model.postTranslate(0.5f * kStageWidth, 0.5f * kStageHeight + 25, 0.f);
|
||||
|
||||
// Flatten the 4x4 matrix by discarding the 3rd row and column
|
||||
canvas->concat(SkMatrix::MakeAll(
|
||||
model.get(0, 0), model.get(0, 1), model.get(0, 3),
|
||||
model.get(1, 0), model.get(1, 1), model.get(1, 3),
|
||||
model.get(3, 0), model.get(3, 1), model.get(3, 3)));
|
||||
|
||||
SkRect poster = SkRect::MakeLTRB(-0.5f * kPosterSize, -0.5f * kPosterSize,
|
||||
0.5f * kPosterSize, 0.5f * kPosterSize);
|
||||
SkPaint fillPaint;
|
||||
fillPaint.setAntiAlias(true);
|
||||
fillPaint.setAlphaf(0.7f);
|
||||
fillPaint.setFilterQuality(kLow_SkFilterQuality);
|
||||
canvas->drawImageRect(fPosterImages[i], poster, &fillPaint);
|
||||
|
||||
canvas->restore();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static const int kAngleStep = 30;
|
||||
static const int kNumAngles = 12; // 0 through 330 degrees
|
||||
|
||||
static const int kStageWidth = 600;
|
||||
static const int kStageHeight = 400;
|
||||
static const int kRingRadius = 200;
|
||||
static const int kPosterSize = 100;
|
||||
|
||||
sk_sp<SkImage> fPosterImages[kNumAngles];
|
||||
SkScalar fTime;
|
||||
};
|
||||
|
||||
DEF_GM(return new PosterCircleGM();)
|
@ -275,6 +275,7 @@ gm_sources = [
|
||||
"$_gm/poly2poly.cpp",
|
||||
"$_gm/polygons.cpp",
|
||||
"$_gm/polygonoffset.cpp",
|
||||
"$_gm/postercircle.cpp",
|
||||
"$_gm/quadpaths.cpp",
|
||||
"$_gm/radial_gradient_precision.cpp",
|
||||
"$_gm/readpixels.cpp",
|
||||
|
@ -258,6 +258,19 @@ static void outset_projected_vertices(const V4f& x2d, const V4f& y2d,
|
||||
V4f(0.f)) / denom; /* !B */
|
||||
}
|
||||
|
||||
V4f newW = quad->fW + a * e1w + b * e2w;
|
||||
// If newW < 0, scale a and b such that the point reaches the infinity plane instead of crossing
|
||||
// This breaks orthogonality of inset/outsets, but GPUs don't handle negative Ws well so this
|
||||
// is far less visually disturbing (likely not noticeable since it's at extreme perspective).
|
||||
// The alternative correction (multiply xyw by -1) has the disadvantage of changing how local
|
||||
// coordinates would be interpolated.
|
||||
static const float kMinW = 1e-6f;
|
||||
if (any(newW < 0.f)) {
|
||||
V4f scale = if_then_else(newW < kMinW, (kMinW - quad->fW) / (newW - quad->fW), V4f(1.f));
|
||||
a *= scale;
|
||||
b *= scale;
|
||||
}
|
||||
|
||||
quad->fX += a * e1x + b * e2x;
|
||||
quad->fY += a * e1y + b * e2y;
|
||||
quad->fW += a * e1w + b * e2w;
|
||||
|
Loading…
Reference in New Issue
Block a user