| /* |
| * Copyright 2012 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/SkBitmap.h" |
| #include "include/core/SkBlurTypes.h" |
| #include "include/core/SkCanvas.h" |
| #include "include/core/SkColor.h" |
| #include "include/core/SkFont.h" |
| #include "include/core/SkMaskFilter.h" |
| #include "include/core/SkPaint.h" |
| #include "include/core/SkPath.h" |
| #include "include/core/SkPixmap.h" |
| #include "include/core/SkPoint.h" |
| #include "include/core/SkRRect.h" |
| #include "include/core/SkRect.h" |
| #include "include/core/SkScalar.h" |
| #include "include/core/SkSize.h" |
| #include "include/core/SkString.h" |
| #include "include/core/SkTypes.h" |
| #include "include/effects/SkGradientShader.h" |
| #include "tools/ToolUtils.h" |
| #include "tools/fonts/FontToolUtils.h" |
| |
| typedef void (*InsetProc)(const SkRRect&, SkScalar dx, SkScalar dy, SkRRect*); |
| |
| static void inset0(const SkRRect& src, SkScalar dx, SkScalar dy, SkRRect* dst) { |
| SkRect r = src.rect(); |
| |
| r.inset(dx, dy); |
| if (r.isEmpty()) { |
| dst->setEmpty(); |
| return; |
| } |
| |
| SkVector radii[4]; |
| for (int i = 0; i < 4; ++i) { |
| radii[i] = src.radii((SkRRect::Corner)i); |
| } |
| for (int i = 0; i < 4; ++i) { |
| radii[i].fX -= dx; |
| radii[i].fY -= dy; |
| } |
| dst->setRectRadii(r, radii); |
| } |
| |
| static void inset1(const SkRRect& src, SkScalar dx, SkScalar dy, SkRRect* dst) { |
| SkRect r = src.rect(); |
| |
| r.inset(dx, dy); |
| if (r.isEmpty()) { |
| dst->setEmpty(); |
| return; |
| } |
| |
| SkVector radii[4]; |
| for (int i = 0; i < 4; ++i) { |
| radii[i] = src.radii((SkRRect::Corner)i); |
| } |
| dst->setRectRadii(r, radii); |
| } |
| |
| static void inset2(const SkRRect& src, SkScalar dx, SkScalar dy, SkRRect* dst) { |
| SkRect r = src.rect(); |
| |
| r.inset(dx, dy); |
| if (r.isEmpty()) { |
| dst->setEmpty(); |
| return; |
| } |
| |
| SkVector radii[4]; |
| for (int i = 0; i < 4; ++i) { |
| radii[i] = src.radii((SkRRect::Corner)i); |
| } |
| for (int i = 0; i < 4; ++i) { |
| if (radii[i].fX) { |
| radii[i].fX -= dx; |
| } |
| if (radii[i].fY) { |
| radii[i].fY -= dy; |
| } |
| } |
| dst->setRectRadii(r, radii); |
| } |
| |
| static SkScalar prop(SkScalar radius, SkScalar newSize, SkScalar oldSize) { |
| return newSize * radius / oldSize; |
| } |
| |
| static void inset3(const SkRRect& src, SkScalar dx, SkScalar dy, SkRRect* dst) { |
| SkRect r = src.rect(); |
| |
| r.inset(dx, dy); |
| if (r.isEmpty()) { |
| dst->setEmpty(); |
| return; |
| } |
| |
| SkVector radii[4]; |
| for (int i = 0; i < 4; ++i) { |
| radii[i] = src.radii((SkRRect::Corner)i); |
| } |
| for (int i = 0; i < 4; ++i) { |
| radii[i].fX = prop(radii[i].fX, r.width(), src.rect().width()); |
| radii[i].fY = prop(radii[i].fY, r.height(), src.rect().height()); |
| } |
| dst->setRectRadii(r, radii); |
| } |
| |
| static void draw_rrect_color(SkCanvas* canvas, const SkRRect& rrect) { |
| SkPaint paint; |
| paint.setAntiAlias(true); |
| paint.setStyle(SkPaint::kStroke_Style); |
| |
| if (rrect.isRect()) { |
| paint.setColor(SK_ColorRED); |
| } else if (rrect.isOval()) { |
| paint.setColor(ToolUtils::color_to_565(0xFF008800)); |
| } else if (rrect.isSimple()) { |
| paint.setColor(SK_ColorBLUE); |
| } else { |
| paint.setColor(SK_ColorBLACK); |
| } |
| canvas->drawRRect(rrect, paint); |
| } |
| |
| static void drawrr(SkCanvas* canvas, const SkRRect& rrect, InsetProc proc) { |
| SkRRect rr; |
| for (SkScalar d = -30; d <= 30; d += 5) { |
| proc(rrect, d, d, &rr); |
| draw_rrect_color(canvas, rr); |
| } |
| } |
| |
| class RRectGM : public skiagm::GM { |
| public: |
| RRectGM() {} |
| |
| protected: |
| SkString getName() const override { return SkString("rrect"); } |
| |
| SkISize getISize() override { return SkISize::Make(820, 710); } |
| |
| void onDraw(SkCanvas* canvas) override { |
| constexpr InsetProc insetProcs[] = { |
| inset0, inset1, inset2, inset3 |
| }; |
| |
| SkRRect rrect[4]; |
| SkRect r = { 0, 0, 120, 100 }; |
| SkVector radii[4] = { |
| { 0, 0 }, { 30, 1 }, { 10, 40 }, { 40, 40 } |
| }; |
| |
| rrect[0].setRect(r); |
| rrect[1].setOval(r); |
| rrect[2].setRectXY(r, 20, 20); |
| rrect[3].setRectRadii(r, radii); |
| |
| canvas->translate(50.5f, 50.5f); |
| for (size_t j = 0; j < std::size(insetProcs); ++j) { |
| canvas->save(); |
| for (size_t i = 0; i < std::size(rrect); ++i) { |
| drawrr(canvas, rrect[i], insetProcs[j]); |
| canvas->translate(200, 0); |
| } |
| canvas->restore(); |
| canvas->translate(0, 170); |
| } |
| } |
| |
| private: |
| using INHERITED = GM; |
| }; |
| |
| DEF_GM( return new RRectGM; ) |
| |
| class RRectBlurGM : public skiagm::GM { |
| public: |
| RRectBlurGM() {} |
| |
| protected: |
| SkString getName() const override { return SkString("rrect_blurs"); } |
| |
| static constexpr int kWidth = 300; |
| static constexpr int kHeight = 400; |
| // how much to exagerate the diffs |
| static constexpr int kDiffMaginification = 16; |
| static constexpr bool kPrintDiffMetrics = false; |
| |
| SkISize getISize() override { return SkISize::Make(kWidth, kHeight); } |
| |
| static void draw_blurry_rrect( |
| SkCanvas* canvas, int cellY, sk_sp<SkMaskFilter> mf, SkColor color, const SkRRect& rr) { |
| const int kCellSize = 100; |
| SkPaint rrectPaint; |
| rrectPaint.setColor(color); |
| rrectPaint.setMaskFilter(mf); |
| |
| const int paddingX = (kCellSize - rr.width()) / 2; |
| const int paddingY = (kCellSize - rr.height()) / 2; |
| const SkRRect left = rr.makeOffset(paddingX, paddingY + cellY); |
| canvas->drawRRect(left, rrectPaint); |
| |
| const SkRRect right = rr.makeOffset(2 * kCellSize + paddingX, paddingY + cellY); |
| SkPath rightPath; |
| rightPath.addRRect(right); |
| canvas->drawPath(rightPath, rrectPaint); |
| |
| // In an ideal world, there would be no diffs at all between the two drawing |
| // methods. The point of this gm is to show those differences and allow us to |
| // measure the differences. |
| SkBitmap leftBitmap; |
| leftBitmap.allocPixels(SkImageInfo::MakeN32Premul(kCellSize, kCellSize)); |
| SkImageInfo infoLeft = leftBitmap.info(); |
| if (!canvas->readPixels(infoLeft, |
| leftBitmap.pixmap().writable_addr(), |
| infoLeft.minRowBytes(), |
| 0, |
| cellY)) { |
| return; |
| } |
| |
| SkBitmap rightBitmap; |
| rightBitmap.allocPixels(SkImageInfo::MakeN32Premul(kCellSize, kCellSize)); |
| SkImageInfo infoRight = rightBitmap.info(); |
| if (!canvas->readPixels(infoRight, |
| rightBitmap.pixmap().writable_addr(), |
| infoRight.minRowBytes(), |
| 2 * kCellSize, |
| cellY)) { |
| return; |
| } |
| |
| int diffPixels = 0; |
| SkBitmap diffBitmap; |
| diffBitmap.allocPixels(SkImageInfo::MakeN32Premul(kCellSize, kCellSize)); |
| for (int y = 0; y < kCellSize; ++y) { |
| for (int x = 0; x < kCellSize; ++x) { |
| SkColor leftColor = leftBitmap.getColor(x, y); |
| SkColor rightColor = rightBitmap.getColor(x, y); |
| // Add up the diffs in the 4 channels, then treat that as how bright |
| // to draw the diff |
| int diff = abs((int)(SkColorGetA(leftColor) - SkColorGetA(rightColor))) + |
| abs((int)(SkColorGetR(leftColor) - SkColorGetR(rightColor))) + |
| abs((int)(SkColorGetG(leftColor) - SkColorGetG(rightColor))) + |
| abs((int)(SkColorGetB(leftColor) - SkColorGetB(rightColor))); |
| SkASSERT(diff >= 0); |
| const U8CPU grey = std::min(diff * kDiffMaginification, 255); |
| if (grey > 0) { |
| diffPixels++; |
| } |
| *diffBitmap.pixmap().writable_addr32(x, y) = SkColorSetARGB(0xFF, grey, grey, grey); |
| } |
| } |
| if (kPrintDiffMetrics) { |
| SkDebugf("%d pixels diff\n", diffPixels); |
| } |
| |
| canvas->writePixels(diffBitmap, kCellSize, cellY); |
| } |
| |
| void onDraw(SkCanvas* canvas) override { |
| // Because of the read/write pixels, this doesn't draw right if viewer zooms in. |
| canvas->resetMatrix(); |
| canvas->clear(SK_ColorDKGRAY); |
| |
| draw_blurry_rrect(canvas, 0, |
| SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 1.0f, false /*=respectCTM*/), |
| SK_ColorWHITE, |
| SkRRect::MakeRectXY(SkRect::MakeWH(50, 50), 10, 15)); |
| |
| draw_blurry_rrect(canvas, 100, |
| SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 0.5f, false /*=respectCTM*/), |
| SK_ColorYELLOW, |
| SkRRect::MakeRectXY(SkRect::MakeWH(60, 80), 3.1f, 1.5f)); |
| |
| SkRRect rr; |
| rr.setNinePatch(SkRect::MakeWH(70, 80), |
| 5, // left |
| 10, // top |
| 13, // right |
| 7); // bottom |
| draw_blurry_rrect(canvas, 200, |
| SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 2.5f, false /*=respectCTM*/), |
| SkColorSetARGB(255, 200, 100, 30), |
| rr); |
| |
| SkVector radii[4] = {{0, 0}, {20, 1}, {10, 30}, {30, 30}}; |
| rr.setRectRadii(SkRect::MakeWH(90, 90), radii); |
| draw_blurry_rrect(canvas, 300, |
| SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 1.1f, false /*=respectCTM*/), |
| SkColorSetARGB(255, 35, 120, 220), |
| rr); |
| |
| // labels after to avoid contaminating the diffs |
| SkPaint labelPaint; |
| labelPaint.setColor(SK_ColorWHITE); |
| labelPaint.setAntiAlias(true); |
| SkFont font = ToolUtils::DefaultPortableFont(); |
| canvas->drawString("drawRRect", 15, 15, font, labelPaint); |
| canvas->drawString("diff", 140, 15, font, labelPaint); |
| canvas->drawString("drawPath", 220, 15, font, labelPaint); |
| canvas->drawLine(100, 0, 100, kHeight, labelPaint); |
| canvas->drawLine(200, 0, 200, kHeight, labelPaint); |
| canvas->drawLine(0, 100, kWidth, 100, labelPaint); |
| canvas->drawLine(0, 200, kWidth, 200, labelPaint); |
| canvas->drawLine(0, 300, kWidth, 300, labelPaint); |
| } |
| }; |
| |
| DEF_GM(return new RRectBlurGM;) |