2015-06-24 17:29:17 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2015 Google Inc.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
|
|
* found in the LICENSE file.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "gm.h"
|
2017-01-11 17:44:43 +00:00
|
|
|
|
|
|
|
#include "SkAutoMalloc.h"
|
2015-06-24 17:29:17 +00:00
|
|
|
#include "SkCanvas.h"
|
|
|
|
#include "SkRSXform.h"
|
|
|
|
#include "SkSurface.h"
|
2018-12-20 22:10:27 +00:00
|
|
|
#include "SkTextBlob.h"
|
2018-01-02 20:40:29 +00:00
|
|
|
#include "sk_tool_utils.h"
|
2015-06-24 17:29:17 +00:00
|
|
|
|
|
|
|
class DrawAtlasGM : public skiagm::GM {
|
2016-03-17 17:51:11 +00:00
|
|
|
static sk_sp<SkImage> MakeAtlas(SkCanvas* caller, const SkRect& target) {
|
2015-06-24 17:29:17 +00:00
|
|
|
SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
|
2018-09-05 18:41:40 +00:00
|
|
|
auto surface(sk_tool_utils::makeSurface(caller, info));
|
2015-06-24 17:29:17 +00:00
|
|
|
SkCanvas* canvas = surface->getCanvas();
|
|
|
|
// draw red everywhere, but we don't expect to see it in the draw, testing the notion
|
|
|
|
// that drawAtlas draws a subset-region of the atlas.
|
|
|
|
canvas->clear(SK_ColorRED);
|
|
|
|
|
|
|
|
SkPaint paint;
|
2016-10-06 00:33:02 +00:00
|
|
|
paint.setBlendMode(SkBlendMode::kClear);
|
2015-06-24 17:29:17 +00:00
|
|
|
SkRect r(target);
|
|
|
|
r.inset(-1, -1);
|
|
|
|
// zero out a place (with a 1-pixel border) to land our drawing.
|
|
|
|
canvas->drawRect(r, paint);
|
2016-10-06 00:33:02 +00:00
|
|
|
paint.setBlendMode(SkBlendMode::kSrcOver);
|
2015-06-24 17:29:17 +00:00
|
|
|
paint.setColor(SK_ColorBLUE);
|
|
|
|
paint.setAntiAlias(true);
|
|
|
|
canvas->drawOval(target, paint);
|
2016-03-17 17:51:11 +00:00
|
|
|
return surface->makeImageSnapshot();
|
2015-06-24 17:29:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
DrawAtlasGM() {}
|
2016-03-29 16:03:52 +00:00
|
|
|
|
2015-06-24 17:29:17 +00:00
|
|
|
protected:
|
2016-03-29 16:03:52 +00:00
|
|
|
|
2015-06-24 17:29:17 +00:00
|
|
|
SkString onShortName() override {
|
|
|
|
return SkString("draw-atlas");
|
|
|
|
}
|
2016-03-29 16:03:52 +00:00
|
|
|
|
2015-06-24 17:29:17 +00:00
|
|
|
SkISize onISize() override {
|
|
|
|
return SkISize::Make(640, 480);
|
|
|
|
}
|
2016-03-29 16:03:52 +00:00
|
|
|
|
2015-06-24 17:29:17 +00:00
|
|
|
void onDraw(SkCanvas* canvas) override {
|
|
|
|
const SkRect target = { 50, 50, 80, 90 };
|
2016-09-29 20:43:49 +00:00
|
|
|
auto atlas = MakeAtlas(canvas, target);
|
2015-06-24 17:29:17 +00:00
|
|
|
|
|
|
|
const struct {
|
|
|
|
SkScalar fScale;
|
|
|
|
SkScalar fDegrees;
|
|
|
|
SkScalar fTx;
|
|
|
|
SkScalar fTy;
|
2016-03-29 16:03:52 +00:00
|
|
|
|
2015-06-24 17:29:17 +00:00
|
|
|
void apply(SkRSXform* xform) const {
|
|
|
|
const SkScalar rad = SkDegreesToRadians(fDegrees);
|
|
|
|
xform->fSCos = fScale * SkScalarCos(rad);
|
|
|
|
xform->fSSin = fScale * SkScalarSin(rad);
|
|
|
|
xform->fTx = fTx;
|
|
|
|
xform->fTy = fTy;
|
|
|
|
}
|
|
|
|
} rec[] = {
|
|
|
|
{ 1, 0, 10, 10 }, // just translate
|
|
|
|
{ 2, 0, 110, 10 }, // scale + translate
|
|
|
|
{ 1, 30, 210, 10 }, // rotate + translate
|
|
|
|
{ 2, -30, 310, 30 }, // scale + rotate + translate
|
|
|
|
};
|
|
|
|
|
|
|
|
const int N = SK_ARRAY_COUNT(rec);
|
|
|
|
SkRSXform xform[N];
|
|
|
|
SkRect tex[N];
|
|
|
|
SkColor colors[N];
|
|
|
|
|
|
|
|
for (int i = 0; i < N; ++i) {
|
|
|
|
rec[i].apply(&xform[i]);
|
|
|
|
tex[i] = target;
|
|
|
|
colors[i] = 0x80FF0000 + (i * 40 * 256);
|
|
|
|
}
|
|
|
|
|
|
|
|
SkPaint paint;
|
|
|
|
paint.setFilterQuality(kLow_SkFilterQuality);
|
|
|
|
paint.setAntiAlias(true);
|
|
|
|
|
2016-09-29 20:43:49 +00:00
|
|
|
canvas->drawAtlas(atlas.get(), xform, tex, N, nullptr, &paint);
|
2015-06-24 17:29:17 +00:00
|
|
|
canvas->translate(0, 100);
|
2016-10-28 19:42:34 +00:00
|
|
|
canvas->drawAtlas(atlas.get(), xform, tex, colors, N, SkBlendMode::kSrcIn, nullptr, &paint);
|
2015-06-24 17:29:17 +00:00
|
|
|
}
|
2016-03-29 16:03:52 +00:00
|
|
|
|
2015-06-24 17:29:17 +00:00
|
|
|
private:
|
|
|
|
typedef GM INHERITED;
|
|
|
|
};
|
|
|
|
DEF_GM( return new DrawAtlasGM; )
|
2016-07-07 19:47:17 +00:00
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2018-12-04 22:35:56 +00:00
|
|
|
#include "SkFont.h"
|
|
|
|
#include "SkFontPriv.h"
|
2016-07-07 19:47:17 +00:00
|
|
|
#include "SkPath.h"
|
|
|
|
#include "SkPathMeasure.h"
|
|
|
|
|
2018-01-29 18:33:06 +00:00
|
|
|
static void draw_text_on_path(SkCanvas* canvas, const void* text, size_t length,
|
2018-12-20 22:10:27 +00:00
|
|
|
const SkPoint xy[], const SkPath& path, const SkFont& font, const SkPaint& paint,
|
2018-10-01 16:16:59 +00:00
|
|
|
float baseline_offset) {
|
2016-07-07 19:47:17 +00:00
|
|
|
SkPathMeasure meas(path, false);
|
|
|
|
|
2018-12-04 22:35:56 +00:00
|
|
|
int count = font.countText(text, length, kUTF8_SkTextEncoding);
|
2016-07-12 22:06:33 +00:00
|
|
|
size_t size = count * (sizeof(SkRSXform) + sizeof(SkScalar));
|
|
|
|
SkAutoSMalloc<512> storage(size);
|
|
|
|
SkRSXform* xform = (SkRSXform*)storage.get();
|
|
|
|
SkScalar* widths = (SkScalar*)(xform + count);
|
|
|
|
|
|
|
|
// Compute a conservative bounds so we can cull the draw
|
2018-12-04 22:35:56 +00:00
|
|
|
const SkRect fontb = SkFontPriv::GetFontBounds(font);
|
|
|
|
const SkScalar max = SkTMax(SkTMax(SkScalarAbs(fontb.fLeft), SkScalarAbs(fontb.fRight)),
|
|
|
|
SkTMax(SkScalarAbs(fontb.fTop), SkScalarAbs(fontb.fBottom)));
|
2016-07-12 22:06:33 +00:00
|
|
|
const SkRect bounds = path.getBounds().makeOutset(max, max);
|
|
|
|
|
2018-12-20 22:10:27 +00:00
|
|
|
SkAutoTArray<SkGlyphID> glyphs(count);
|
|
|
|
font.textToGlyphs(text, length, kUTF8_SkTextEncoding, glyphs.get(), count);
|
|
|
|
font.getWidths(glyphs.get(), count, widths);
|
2018-01-29 18:33:06 +00:00
|
|
|
|
2018-10-01 16:16:59 +00:00
|
|
|
for (int i = 0; i < count; ++i) {
|
|
|
|
// we want to position each character on the center of its advance
|
|
|
|
const SkScalar offset = SkScalarHalf(widths[i]);
|
|
|
|
SkPoint pos;
|
|
|
|
SkVector tan;
|
|
|
|
if (!meas.getPosTan(xy[i].x() + offset, &pos, &tan)) {
|
|
|
|
pos = xy[i];
|
|
|
|
tan.set(1, 0);
|
2018-01-29 18:33:06 +00:00
|
|
|
}
|
2018-10-01 16:16:59 +00:00
|
|
|
pos += SkVector::Make(-tan.fY, tan.fX) * baseline_offset;
|
2018-01-29 18:33:06 +00:00
|
|
|
|
2018-10-01 16:16:59 +00:00
|
|
|
xform[i].fSCos = tan.x();
|
|
|
|
xform[i].fSSin = tan.y();
|
|
|
|
xform[i].fTx = pos.x() - tan.y() * xy[i].y() - tan.x() * offset;
|
|
|
|
xform[i].fTy = pos.y() + tan.x() * xy[i].y() - tan.y() * offset;
|
2018-01-29 18:33:06 +00:00
|
|
|
}
|
2016-07-12 22:06:33 +00:00
|
|
|
|
2018-12-20 22:10:27 +00:00
|
|
|
canvas->drawTextBlob(SkTextBlob::MakeFromRSXform(glyphs.get(), count * sizeof(SkGlyphID),
|
|
|
|
&xform[0], font, kGlyphID_SkTextEncoding),
|
|
|
|
0, 0, paint);
|
2018-10-01 16:16:59 +00:00
|
|
|
|
2016-07-12 22:06:33 +00:00
|
|
|
if (true) {
|
|
|
|
SkPaint p;
|
|
|
|
p.setStyle(SkPaint::kStroke_Style);
|
|
|
|
canvas->drawRect(bounds, p);
|
|
|
|
}
|
2016-07-07 19:47:17 +00:00
|
|
|
}
|
|
|
|
|
2018-03-15 17:37:08 +00:00
|
|
|
#include "SkGradientShader.h"
|
|
|
|
static sk_sp<SkShader> make_shader() {
|
|
|
|
SkPoint pts[2] = {{0, 0}, {220, 0}};
|
|
|
|
SkColor colors[2] = {SK_ColorRED, SK_ColorBLUE};
|
|
|
|
return SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkShader::kMirror_TileMode);
|
|
|
|
}
|
|
|
|
|
2018-10-01 16:16:59 +00:00
|
|
|
static void drawTextPath(SkCanvas* canvas, bool doStroke) {
|
2016-07-08 10:31:09 +00:00
|
|
|
const char text0[] = "ABCDFGHJKLMNOPQRSTUVWXYZ";
|
|
|
|
const int N = sizeof(text0) - 1;
|
2016-07-07 19:47:17 +00:00
|
|
|
SkPoint pos[N];
|
|
|
|
|
2018-12-20 22:10:27 +00:00
|
|
|
SkFont font;
|
|
|
|
font.setSize(100);
|
|
|
|
|
2016-07-12 22:06:33 +00:00
|
|
|
SkPaint paint;
|
2018-03-15 17:37:08 +00:00
|
|
|
paint.setShader(make_shader());
|
2016-07-12 22:06:33 +00:00
|
|
|
paint.setAntiAlias(true);
|
2018-01-29 19:32:38 +00:00
|
|
|
if (doStroke) {
|
|
|
|
paint.setStyle(SkPaint::kStroke_Style);
|
|
|
|
paint.setStrokeWidth(2.25f);
|
|
|
|
paint.setStrokeJoin(SkPaint::kRound_Join);
|
|
|
|
}
|
2016-07-07 19:47:17 +00:00
|
|
|
|
2016-07-12 22:06:33 +00:00
|
|
|
SkScalar x = 0;
|
2016-07-07 19:47:17 +00:00
|
|
|
for (int i = 0; i < N; ++i) {
|
2016-07-12 22:06:33 +00:00
|
|
|
pos[i].set(x, 0);
|
2018-12-20 22:10:27 +00:00
|
|
|
x += font.measureText(&text0[i], 1, kUTF8_SkTextEncoding, nullptr, &paint);
|
2016-07-07 19:47:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SkPath path;
|
2018-01-29 18:33:06 +00:00
|
|
|
const float baseline_offset = -5;
|
|
|
|
|
|
|
|
const SkPath::Direction dirs[] = {
|
|
|
|
SkPath::kCW_Direction, SkPath::kCCW_Direction,
|
|
|
|
};
|
|
|
|
for (auto d : dirs) {
|
|
|
|
path.reset();
|
|
|
|
path.addOval(SkRect::MakeXYWH(160, 160, 540, 540), d);
|
2018-12-20 22:10:27 +00:00
|
|
|
draw_text_on_path(canvas, text0, N, pos, path, font, paint, baseline_offset);
|
2018-01-29 18:33:06 +00:00
|
|
|
}
|
2016-07-07 19:47:17 +00:00
|
|
|
|
2018-01-29 19:32:38 +00:00
|
|
|
paint.reset();
|
2016-07-07 19:47:17 +00:00
|
|
|
paint.setStyle(SkPaint::kStroke_Style);
|
|
|
|
canvas->drawPath(path, paint);
|
|
|
|
}
|
|
|
|
|
2018-10-01 16:16:59 +00:00
|
|
|
DEF_SIMPLE_GM(drawTextRSXform, canvas, 430, 860) {
|
2018-01-29 18:33:06 +00:00
|
|
|
canvas->scale(0.5f, 0.5f);
|
2018-01-29 19:32:38 +00:00
|
|
|
const bool doStroke[] = { false, true };
|
|
|
|
for (auto st : doStroke) {
|
2018-10-01 16:16:59 +00:00
|
|
|
drawTextPath(canvas, st);
|
2018-01-29 19:32:38 +00:00
|
|
|
canvas->translate(0, 860);
|
|
|
|
}
|
2018-01-29 18:33:06 +00:00
|
|
|
}
|
|
|
|
|
2018-12-20 20:24:21 +00:00
|
|
|
// Exercise xform blob and its bounds
|
|
|
|
DEF_SIMPLE_GM(blob_rsxform, canvas, 500, 100) {
|
|
|
|
SkFont font;
|
|
|
|
font.setTypeface(sk_tool_utils::create_portable_typeface());
|
|
|
|
font.setSize(50);
|
|
|
|
|
|
|
|
const char text[] = "CrazyXform";
|
|
|
|
constexpr size_t len = sizeof(text) - 1;
|
|
|
|
|
|
|
|
SkRSXform xforms[len];
|
|
|
|
SkScalar scale = 1;
|
|
|
|
SkScalar x = 0, y = 0;
|
|
|
|
for (size_t i = 0; i < len; ++i) {
|
|
|
|
scale = SkScalarSin(i * SK_ScalarPI / (len-1)) * 0.75f + 0.5f;
|
|
|
|
xforms[i] = SkRSXform::Make(scale, 0, x, y);
|
|
|
|
x += 50 * scale;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto blob = SkTextBlob::MakeFromRSXform(text, len, xforms, font);
|
|
|
|
|
|
|
|
SkPoint offset = { 20, 70 };
|
|
|
|
SkPaint paint;
|
|
|
|
paint.setColor(0xFFCCCCCC);
|
|
|
|
canvas->drawRect(blob->bounds().makeOffset(offset.fX, offset.fY), paint);
|
|
|
|
paint.setColor(SK_ColorBLACK);
|
|
|
|
canvas->drawTextBlob(blob, offset.fX, offset.fY, paint);
|
|
|
|
}
|
|
|
|
|
2017-04-28 14:02:47 +00:00
|
|
|
#include "Resources.h"
|
|
|
|
#include "SkColorFilter.h"
|
|
|
|
#include "SkVertices.h"
|
|
|
|
|
|
|
|
static sk_sp<SkVertices> make_vertices(sk_sp<SkImage> image, const SkRect& r,
|
|
|
|
SkColor color) {
|
|
|
|
SkPoint pos[4];
|
|
|
|
r.toQuad(pos);
|
|
|
|
SkColor colors[4] = { color, color, color, color };
|
|
|
|
return SkVertices::MakeCopy(SkVertices::kTriangleFan_VertexMode, 4,
|
|
|
|
pos, pos, colors);
|
|
|
|
}
|
2016-07-07 19:47:17 +00:00
|
|
|
|
2017-04-28 14:02:47 +00:00
|
|
|
/*
|
|
|
|
* drawAtlas and drawVertices have several things in common:
|
|
|
|
* - can create compound "shaders", combining texture and colors
|
|
|
|
* - these are combined via an explicit blendmode
|
|
|
|
* - like drawImage, they only respect parts of the paint
|
|
|
|
* - colorfilter, imagefilter, blendmode, alpha
|
|
|
|
*
|
|
|
|
* This GM produces a series of pairs of images (atlas | vertices).
|
|
|
|
* Each pair should look the same, and each set shows a different combination
|
|
|
|
* of alpha | colorFilter | mode
|
|
|
|
*/
|
|
|
|
DEF_SIMPLE_GM(compare_atlas_vertices, canvas, 560, 585) {
|
|
|
|
const SkRect tex = SkRect::MakeWH(128, 128);
|
|
|
|
const SkRSXform xform = SkRSXform::Make(1, 0, 0, 0);
|
|
|
|
const SkColor color = 0x884488CC;
|
|
|
|
|
2017-12-08 15:21:31 +00:00
|
|
|
auto image = GetResourceAsImage("images/mandrill_128.png");
|
2017-04-28 14:02:47 +00:00
|
|
|
auto verts = make_vertices(image, tex, color);
|
|
|
|
const sk_sp<SkColorFilter> filters[] = {
|
|
|
|
nullptr,
|
|
|
|
SkColorFilter::MakeModeFilter(0xFF00FF88, SkBlendMode::kModulate),
|
|
|
|
};
|
|
|
|
const SkBlendMode modes[] = {
|
|
|
|
SkBlendMode::kSrcOver,
|
|
|
|
SkBlendMode::kPlus,
|
|
|
|
};
|
|
|
|
|
|
|
|
canvas->translate(10, 10);
|
|
|
|
SkPaint paint;
|
|
|
|
for (SkBlendMode mode : modes) {
|
2019-02-15 21:13:57 +00:00
|
|
|
for (float alpha : { 1.0f, 0.5f }) {
|
|
|
|
paint.setAlphaf(alpha);
|
2017-04-28 14:02:47 +00:00
|
|
|
canvas->save();
|
|
|
|
for (auto cf : filters) {
|
|
|
|
paint.setColorFilter(cf);
|
|
|
|
canvas->drawAtlas(image, &xform, &tex, &color, 1,
|
|
|
|
mode, &tex, &paint);
|
|
|
|
canvas->translate(128, 0);
|
2017-04-28 15:12:19 +00:00
|
|
|
paint.setShader(image->makeShader());
|
2017-04-28 14:02:47 +00:00
|
|
|
canvas->drawVertices(verts, mode, paint);
|
|
|
|
paint.setShader(nullptr);
|
|
|
|
canvas->translate(145, 0);
|
|
|
|
}
|
|
|
|
canvas->restore();
|
|
|
|
canvas->translate(0, 145);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|