9dc0b9e2f8
We can't remove the loopers themselves, as they are still used by android and chrome (they just don't ever pass them to skia). Eventually each of those clients will resolve this, but for now we just keep the classes (and tests) in skia. Bug: skia:4783 Change-Id: I5f507e6bb82280f2bc7c0b21eebe59c287aa9265 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/230579 Reviewed-by: Florin Malita <fmalita@chromium.org> Commit-Queue: Mike Reed <reed@google.com>
277 lines
10 KiB
C++
277 lines
10 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/SkBlendMode.h"
|
|
#include "include/core/SkBlurTypes.h"
|
|
#include "include/core/SkCanvas.h"
|
|
#include "include/core/SkColor.h"
|
|
#include "include/core/SkColorFilter.h"
|
|
#include "include/core/SkDrawLooper.h"
|
|
#include "include/core/SkFont.h"
|
|
#include "include/core/SkFontTypes.h"
|
|
#include "include/core/SkMaskFilter.h"
|
|
#include "include/core/SkMatrix.h"
|
|
#include "include/core/SkPaint.h"
|
|
#include "include/core/SkPath.h"
|
|
#include "include/core/SkPathEffect.h"
|
|
#include "include/core/SkPoint.h"
|
|
#include "include/core/SkRect.h"
|
|
#include "include/core/SkRefCnt.h"
|
|
#include "include/core/SkScalar.h"
|
|
#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/SkTypeface.h"
|
|
#include "include/core/SkTypes.h"
|
|
#include "include/effects/Sk2DPathEffect.h"
|
|
#include "include/effects/SkColorMatrixFilter.h"
|
|
#include "include/effects/SkGradientShader.h"
|
|
#include "include/effects/SkLayerDrawLooper.h"
|
|
#include "include/private/SkTArray.h"
|
|
#include "include/private/SkTDArray.h"
|
|
#include "src/core/SkBlurMask.h"
|
|
#include "tools/ToolUtils.h"
|
|
|
|
#include <string.h>
|
|
|
|
namespace skiagm {
|
|
|
|
constexpr int kWidth = 1250;
|
|
constexpr int kHeight = 700;
|
|
|
|
// Unlike the variant in ToolUtils, this version positions the glyphs on a diagonal
|
|
static void add_to_text_blob(SkTextBlobBuilder* builder, const char* text, const SkFont& font,
|
|
SkScalar x, SkScalar y) {
|
|
SkTDArray<uint16_t> glyphs;
|
|
|
|
size_t len = strlen(text);
|
|
glyphs.append(font.countText(text, len, SkTextEncoding::kUTF8));
|
|
font.textToGlyphs(text, len, SkTextEncoding::kUTF8, glyphs.begin(), glyphs.count());
|
|
|
|
const SkScalar advanceX = font.getSize() * 0.85f;
|
|
const SkScalar advanceY = font.getSize() * 1.5f;
|
|
|
|
SkTDArray<SkScalar> pos;
|
|
for (unsigned i = 0; i < len; ++i) {
|
|
*pos.append() = x + i * advanceX;
|
|
*pos.append() = y + i * (advanceY / len);
|
|
}
|
|
const SkTextBlobBuilder::RunBuffer& run = builder->allocRunPos(font, glyphs.count());
|
|
memcpy(run.glyphs, glyphs.begin(), glyphs.count() * sizeof(uint16_t));
|
|
memcpy(run.pos, pos.begin(), len * sizeof(SkScalar) * 2);
|
|
}
|
|
|
|
typedef void (*LooperProc)(SkPaint*);
|
|
|
|
struct LooperSettings {
|
|
SkBlendMode fMode;
|
|
SkColor fColor;
|
|
SkPaint::Style fStyle;
|
|
SkScalar fWidth;
|
|
SkScalar fOffset;
|
|
SkScalar fSkewX;
|
|
bool fEffect;
|
|
};
|
|
|
|
static void mask_filter(SkPaint* paint) {
|
|
paint->setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle,
|
|
SkBlurMask::ConvertRadiusToSigma(3.f)));
|
|
}
|
|
|
|
static sk_sp<SkPathEffect> make_tile_effect() {
|
|
SkMatrix m;
|
|
m.setScale(1.f, 1.f);
|
|
|
|
SkPath path;
|
|
path.addCircle(0, 0, SkIntToScalar(5));
|
|
|
|
return SkPath2DPathEffect::Make(m, path);
|
|
}
|
|
|
|
static void path_effect(SkPaint* paint) {
|
|
paint->setPathEffect(make_tile_effect());
|
|
}
|
|
|
|
static sk_sp<SkShader> make_shader(const SkRect& bounds) {
|
|
const SkPoint pts[] = {
|
|
{ bounds.left(), bounds.top() },
|
|
{ bounds.right(), bounds.bottom() },
|
|
};
|
|
const SkColor colors[] = {
|
|
SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorBLACK,
|
|
SK_ColorCYAN, SK_ColorMAGENTA, SK_ColorYELLOW,
|
|
};
|
|
return SkGradientShader::MakeLinear(pts, colors, nullptr, SK_ARRAY_COUNT(colors),
|
|
SkTileMode::kClamp);
|
|
}
|
|
|
|
static void color_filter(SkPaint* paint) {
|
|
SkRect r;
|
|
r.setWH(SkIntToScalar(kWidth), 50);
|
|
paint->setShader(make_shader(r));
|
|
paint->setColorFilter(SkColorMatrixFilter::MakeLightingFilter(0xF0F0F0, 0));
|
|
}
|
|
|
|
static void kitchen_sink(SkPaint* paint) {
|
|
color_filter(paint);
|
|
path_effect(paint);
|
|
mask_filter(paint);
|
|
|
|
}
|
|
|
|
static sk_sp<SkDrawLooper> setupLooper(SkLayerDrawLooper::BitFlags bits,
|
|
LooperProc proc,
|
|
const LooperSettings settings[],
|
|
size_t size) {
|
|
SkLayerDrawLooper::Builder looperBuilder;
|
|
|
|
SkLayerDrawLooper::LayerInfo info;
|
|
info.fPaintBits = bits;
|
|
|
|
info.fColorMode = SkBlendMode::kSrc;
|
|
|
|
for (size_t i = 0; i < size; i++) {
|
|
info.fOffset.set(settings[i].fOffset, settings[i].fOffset);
|
|
SkPaint* paint = looperBuilder.addLayer(info);
|
|
paint->setBlendMode(settings[i].fMode);
|
|
paint->setColor(settings[i].fColor);
|
|
paint->setStyle(settings[i].fStyle);
|
|
paint->setStrokeWidth(settings[i].fWidth);
|
|
if (settings[i].fEffect) {
|
|
(*proc)(paint);
|
|
}
|
|
}
|
|
return looperBuilder.detach();
|
|
}
|
|
|
|
class TextBlobLooperGM : public GM {
|
|
public:
|
|
TextBlobLooperGM() {}
|
|
|
|
protected:
|
|
void onOnceBeforeDraw() override {
|
|
SkTextBlobBuilder builder;
|
|
|
|
// LCD
|
|
SkFont font;
|
|
font.setSize(32);
|
|
const char* text = "The quick brown fox jumps over the lazy dog";
|
|
font.setSubpixel(true);
|
|
font.setEdging(SkFont::Edging::kSubpixelAntiAlias);
|
|
font.setTypeface(ToolUtils::create_portable_typeface());
|
|
add_to_text_blob(&builder, text, font, 0, 0);
|
|
fBlob = builder.make();
|
|
|
|
// create a looper which sandwhiches an effect in two normal draws
|
|
LooperSettings looperSandwhich[] = {
|
|
{ SkBlendMode::kSrc, SK_ColorMAGENTA, SkPaint::kFill_Style, 0, 0, 0, false },
|
|
{ SkBlendMode::kSrcOver, 0x88000000, SkPaint::kFill_Style, 0, 10.f, 0, true },
|
|
{ SkBlendMode::kSrcOver, 0x50FF00FF, SkPaint::kFill_Style, 0, 20.f, 0, false },
|
|
};
|
|
|
|
LooperSettings compound[] = {
|
|
{ SkBlendMode::kSrc, SK_ColorWHITE, SkPaint::kStroke_Style, 1.f * 3/4, 0, 0, false },
|
|
{ SkBlendMode::kSrc, SK_ColorRED, SkPaint::kStroke_Style, 4.f, 0, 0, false },
|
|
{ SkBlendMode::kSrc, SK_ColorBLUE, SkPaint::kFill_Style, 0, 0, 0, false },
|
|
{ SkBlendMode::kSrcOver, 0x88000000, SkPaint::kFill_Style, 0, 10.f, 0, true }
|
|
};
|
|
|
|
LooperSettings xfermode[] = {
|
|
{ SkBlendMode::kDifference, SK_ColorWHITE, SkPaint::kFill_Style, 0, 0, 0, false },
|
|
{ SkBlendMode::kSrcOver, 0xFF000000, SkPaint::kFill_Style, 0, 1.f, 0, true },
|
|
{ SkBlendMode::kSrcOver, 0x50FF00FF, SkPaint::kFill_Style, 0, 2.f, 0, false },
|
|
};
|
|
|
|
// NOTE, this should be ignored by textblobs
|
|
LooperSettings skew[] = {
|
|
{ SkBlendMode::kSrc, SK_ColorRED, SkPaint::kFill_Style, 0, 0, -1.f, false },
|
|
{ SkBlendMode::kSrc, SK_ColorGREEN, SkPaint::kFill_Style, 0, 10.f, -1.f, false },
|
|
{ SkBlendMode::kSrc, SK_ColorBLUE, SkPaint::kFill_Style, 0, 20.f, -1.f, false },
|
|
};
|
|
|
|
LooperSettings kitchenSink[] = {
|
|
{ SkBlendMode::kSrc, SK_ColorWHITE, SkPaint::kStroke_Style, 1.f * 3/4, 0, 0, false },
|
|
{ SkBlendMode::kSrc, SK_ColorBLACK, SkPaint::kFill_Style, 0, 0, 0, false },
|
|
{ SkBlendMode::kDifference, SK_ColorWHITE, SkPaint::kFill_Style, 1.f, 10.f, 0, false },
|
|
{ SkBlendMode::kSrc, SK_ColorWHITE, SkPaint::kFill_Style, 0, 10.f, 0, true },
|
|
{ SkBlendMode::kSrcOver, 0x50FF00FF, SkPaint::kFill_Style, 0, 20.f, 0, false },
|
|
};
|
|
|
|
fLoopers.push_back(setupLooper(SkLayerDrawLooper::kMaskFilter_Bit |
|
|
SkLayerDrawLooper::kXfermode_Bit |
|
|
SkLayerDrawLooper::kStyle_Bit, &mask_filter,
|
|
compound, SK_ARRAY_COUNT(compound)));
|
|
fLoopers.push_back(setupLooper(SkLayerDrawLooper::kPathEffect_Bit |
|
|
SkLayerDrawLooper::kXfermode_Bit, &path_effect,
|
|
looperSandwhich, SK_ARRAY_COUNT(looperSandwhich)));
|
|
fLoopers.push_back(setupLooper(SkLayerDrawLooper::kShader_Bit |
|
|
SkLayerDrawLooper::kColorFilter_Bit |
|
|
SkLayerDrawLooper::kXfermode_Bit, &color_filter,
|
|
looperSandwhich, SK_ARRAY_COUNT(looperSandwhich)));
|
|
fLoopers.push_back(setupLooper(SkLayerDrawLooper::kShader_Bit |
|
|
SkLayerDrawLooper::kColorFilter_Bit |
|
|
SkLayerDrawLooper::kXfermode_Bit, &color_filter,
|
|
xfermode, SK_ARRAY_COUNT(xfermode)));
|
|
fLoopers.push_back(setupLooper(0, nullptr, skew, SK_ARRAY_COUNT(skew)));
|
|
fLoopers.push_back(setupLooper(SkLayerDrawLooper::kMaskFilter_Bit |
|
|
SkLayerDrawLooper::kShader_Bit |
|
|
SkLayerDrawLooper::kColorFilter_Bit |
|
|
SkLayerDrawLooper::kPathEffect_Bit |
|
|
SkLayerDrawLooper::kStyle_Bit |
|
|
SkLayerDrawLooper::kXfermode_Bit, &kitchen_sink,
|
|
kitchenSink, SK_ARRAY_COUNT(kitchenSink)));
|
|
|
|
// Test we respect overrides
|
|
fLoopers.push_back(setupLooper(0, &kitchen_sink,
|
|
kitchenSink, SK_ARRAY_COUNT(kitchenSink)));
|
|
}
|
|
|
|
SkString onShortName() override {
|
|
return SkString("textbloblooper");
|
|
}
|
|
|
|
SkISize onISize() override {
|
|
return SkISize::Make(kWidth, kHeight);
|
|
}
|
|
|
|
void onDraw(SkCanvas* canvas) override {
|
|
|
|
canvas->drawColor(SK_ColorGRAY);
|
|
|
|
SkPaint paint;
|
|
canvas->translate(10, 40);
|
|
|
|
SkRect bounds = fBlob->bounds();
|
|
|
|
int y = 0;
|
|
for (int looper = 0; looper < fLoopers.count(); looper++) {
|
|
SkTextBlob* b = fBlob.get();
|
|
canvas->save();
|
|
canvas->translate(0, SkIntToScalar(y));
|
|
fLoopers[looper]->apply(canvas, paint, [b](SkCanvas* c, const SkPaint& p) {
|
|
c->drawTextBlob(b, 0, 0, p);
|
|
});
|
|
canvas->restore();
|
|
y += SkScalarFloorToInt(bounds.height());
|
|
}
|
|
}
|
|
|
|
private:
|
|
sk_sp<SkTextBlob> fBlob;
|
|
SkTArray<sk_sp<SkDrawLooper>> fLoopers;
|
|
|
|
typedef GM INHERITED;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
DEF_GM(return new TextBlobLooperGM;)
|
|
}
|