skia2/gm/thinrects.cpp
Michael Ludwig 575c921357 Account for hairline mode in rect op bounds for clipping
Disables the preApply() clip check for rects that are subpixel since the
bounds will not be correct, and avoids pre-applying clips to rects if
the result would turn into a subpixel draw.

Also disables hairline mode for rects that are not fully anti-aliased,
which showed up in the fast_constraint GMs where we'd tile an image and
the bottom/right edges were actually subpixel. Since the objective of
per-edge AA is for tiling, we don't want those subpixel edges to be
outset more than the original geometry would have been in a non-tiling
case.

Bug: skia:12164, chromium:1210170
Change-Id: I3bca4fc3e63b6f86f1da1ea7a5e5873d221755f7
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/426437
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
2021-07-13 17:33:13 +00:00

180 lines
5.7 KiB
C++

/*
* Copyright 2013 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/SkColor.h"
#include "include/core/SkImage.h"
#include "include/core/SkPaint.h"
#include "include/core/SkRRect.h"
#include "include/core/SkRect.h"
#include "include/core/SkSize.h"
#include "include/core/SkString.h"
#include "include/core/SkSurface.h"
#include "include/core/SkTypes.h"
namespace skiagm {
// Draw various width thin rects at 1/8 horizontal pixel increments
class ThinRectsGM : public GM {
public:
ThinRectsGM(bool round) : fRound(round) {
this->setBGColor(0xFF000000);
}
protected:
SkString onShortName() override {
return SkString(fRound ? "thinroundrects" : "thinrects");
}
SkISize onISize() override {
return SkISize::Make(240, 320);
}
void onDraw(SkCanvas* canvas) override {
SkPaint white;
white.setColor(SK_ColorWHITE);
white.setAntiAlias(true);
SkPaint green;
green.setColor(SK_ColorGREEN);
green.setAntiAlias(true);
for (int i = 0; i < 8; ++i) {
canvas->save();
canvas->translate(i*0.125f, i*40.0f);
this->drawVertRects(canvas, white);
canvas->translate(40.0f, 0.0f);
this->drawVertRects(canvas, green);
canvas->restore();
canvas->save();
canvas->translate(80.0f, i*40.0f + i*0.125f);
this->drawHorizRects(canvas, white);
canvas->translate(40.0f, 0.0f);
this->drawHorizRects(canvas, green);
canvas->restore();
canvas->save();
canvas->translate(160.0f + i*0.125f,
i*40.0f + i*0.125f);
this->drawSquares(canvas, white);
canvas->translate(40.0f, 0.0f);
this->drawSquares(canvas, green);
canvas->restore();
}
}
private:
void drawVertRects(SkCanvas* canvas, const SkPaint& p) {
constexpr SkRect vertRects[] = {
{ 1, 1, 5.0f, 21 }, // 4 pix wide
{ 8, 1, 10.0f, 21 }, // 2 pix wide
{ 13, 1, 14.0f, 21 }, // 1 pix wide
{ 17, 1, 17.5f, 21 }, // 1/2 pix wide
{ 21, 1, 21.25f, 21 }, // 1/4 pix wide
{ 25, 1, 25.125f, 21 }, // 1/8 pix wide
{ 29, 1, 29.0f, 21 } // 0 pix wide
};
static constexpr SkVector radii[4] = {{1/32.f, 2/32.f}, {3/32.f, 1/32.f}, {2/32.f, 3/32.f},
{1/32.f, 3/32.f}};
SkRRect rrect;
for (size_t j = 0; j < SK_ARRAY_COUNT(vertRects); ++j) {
if (fRound) {
rrect.setRectRadii(vertRects[j], radii);
canvas->drawRRect(rrect, p);
} else {
canvas->drawRect(vertRects[j], p);
}
}
}
void drawHorizRects(SkCanvas* canvas, const SkPaint& p) {
constexpr SkRect horizRects[] = {
{ 1, 1, 21, 5.0f }, // 4 pix high
{ 1, 8, 21, 10.0f }, // 2 pix high
{ 1, 13, 21, 14.0f }, // 1 pix high
{ 1, 17, 21, 17.5f }, // 1/2 pix high
{ 1, 21, 21, 21.25f }, // 1/4 pix high
{ 1, 25, 21, 25.125f }, // 1/8 pix high
{ 1, 29, 21, 29.0f } // 0 pix high
};
SkRRect rrect;
for (size_t j = 0; j < SK_ARRAY_COUNT(horizRects); ++j) {
if (fRound) {
rrect.setNinePatch(horizRects[j], 1/32.f, 2/32.f, 3/32.f, 4/32.f);
canvas->drawRRect(rrect, p);
} else {
canvas->drawRect(horizRects[j], p);
}
}
}
void drawSquares(SkCanvas* canvas, const SkPaint& p) {
constexpr SkRect squares[] = {
{ 1, 1, 5.0f, 5.0f }, // 4 pix
{ 8, 8, 10.0f, 10.0f }, // 2 pix
{ 13, 13, 14.0f, 14.0f }, // 1 pix
{ 17, 17, 17.5f, 17.5f }, // 1/2 pix
{ 21, 21, 21.25f, 21.25f }, // 1/4 pix
{ 25, 25, 25.125f, 25.125f }, // 1/8 pix
{ 29, 29, 29.0f, 29.0f } // 0 pix
};
SkRRect rrect;
for (size_t j = 0; j < SK_ARRAY_COUNT(squares); ++j) {
if (fRound) {
rrect.setRectXY(squares[j], 1/32.f, 2/32.f);
canvas->drawRRect(rrect, p);
} else {
canvas->drawRect(squares[j], p);
}
}
}
const bool fRound;
using INHERITED = GM;
};
//////////////////////////////////////////////////////////////////////////////
DEF_GM( return new ThinRectsGM(false); )
DEF_GM( return new ThinRectsGM(true); )
} // namespace skiagm
DEF_SIMPLE_GM_CAN_FAIL(clipped_thinrect, canvas, errorMsg, 256, 256) {
auto zoomed = canvas->makeSurface(canvas->imageInfo().makeWH(10, 10));
if (!zoomed) {
errorMsg->printf("makeSurface not supported");
return skiagm::DrawResult::kSkip;
}
auto zoomedCanvas = zoomed->getCanvas();
SkPaint p;
p.setColor(SK_ColorRED);
p.setAntiAlias(true);
p.setStyle(SkPaint::kFill_Style);
zoomedCanvas->save();
zoomedCanvas->clipRect(SkRect::MakeXYWH(0, 5, 256, 10), true /*doAntialias*/);
zoomedCanvas->drawRect(SkRect::MakeXYWH(0, 0, 100, 5.5), p);
zoomedCanvas->restore();
// Zoom-in. Should see one line of red representing zoomed in 1/2px coverage and *not*
// two lines of varying coverage from hairline rendering.
auto img = zoomed->makeImageSnapshot();
canvas->drawImageRect(img, SkRect::MakeXYWH(0, 10, 200, 200), SkSamplingOptions());
return skiagm::DrawResult::kOk;
}