skia2/gm/stroketext.cpp
Ben Wagner b3275e1fd6 Simplify FreeType glyph paths with overlap.
With variable fonts overlapping glyph contours are now normal. However,
these do not stroke well. Simpify the glyph paths which are marked as
overlapping.

Bug: b/225044541
Change-Id: I9687e28c9274cd189f4ae8e059d8d40dd802ec41
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/521637
Reviewed-by: Herb Derby <herb@google.com>
Commit-Queue: Ben Wagner <bungeman@google.com>
2022-03-18 22:42:20 +00:00

171 lines
5.8 KiB
C++

/*
* Copyright 2014 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/SkCanvas.h"
#include "include/core/SkColor.h"
#include "include/core/SkFont.h"
#include "include/core/SkFontArguments.h"
#include "include/core/SkFontMgr.h"
#include "include/core/SkFontTypes.h"
#include "include/core/SkImageInfo.h"
#include "include/core/SkPaint.h"
#include "include/core/SkPathEffect.h"
#include "include/core/SkPoint.h"
#include "include/core/SkScalar.h"
#include "include/core/SkTextBlob.h"
#include "include/core/SkTypeface.h"
#include "include/core/SkTypes.h"
#include "include/effects/SkDashPathEffect.h"
#include "tools/Resources.h"
#include "tools/ToolUtils.h"
static void test_nulldev(SkCanvas* canvas) {
SkBitmap bm;
bm.setInfo(SkImageInfo::MakeN32Premul(30, 30));
// notice: no pixels mom! be sure we don't crash
// https://code.google.com/p/chromium/issues/detail?id=352616
SkCanvas c(bm);
SkBitmap src;
src.allocN32Pixels(10, 10);
src.eraseColor(SK_ColorRED);
// ensure we don't crash
c.writePixels(src, 0, 0);
}
static void draw_text_stroked(SkCanvas* canvas, const SkPaint& paint, const SkFont& font,
SkScalar strokeWidth) {
SkPaint p(paint);
SkPoint loc = { 20, 435 };
if (strokeWidth > 0) {
p.setStyle(SkPaint::kFill_Style);
canvas->drawSimpleText("P", 1, SkTextEncoding::kUTF8, loc.fX, loc.fY - 225, font, p);
canvas->drawTextBlob(SkTextBlob::MakeFromPosText("P", 1, &loc, font), 0, 0, p);
}
p.setColor(SK_ColorRED);
p.setStyle(SkPaint::kStroke_Style);
p.setStrokeWidth(strokeWidth);
canvas->drawSimpleText("P", 1, SkTextEncoding::kUTF8, loc.fX, loc.fY - 225, font, p);
canvas->drawTextBlob(SkTextBlob::MakeFromPosText("P", 1, &loc, font), 0, 0, p);
}
static void draw_text_set(SkCanvas* canvas, const SkPaint& paint, const SkFont& font) {
SkAutoCanvasRestore acr(canvas, true);
draw_text_stroked(canvas, paint, font, 10);
canvas->translate(200, 0);
draw_text_stroked(canvas, paint, font, 0);
const SkScalar intervals[] = { 20, 10, 5, 10 };
const SkScalar phase = 0;
canvas->translate(200, 0);
SkPaint p(paint);
p.setPathEffect(SkDashPathEffect::Make(intervals, SK_ARRAY_COUNT(intervals), phase));
draw_text_stroked(canvas, p, font, 10);
}
namespace {
enum {
kBelowThreshold_TextSize = 255,
kAboveThreshold_TextSize = 257
};
} // namespace
DEF_SIMPLE_GM(stroketext, canvas, 1200, 480) {
if (true) { test_nulldev(canvas); }
SkPaint paint;
paint.setAntiAlias(true);
SkFont font(ToolUtils::create_portable_typeface(), kBelowThreshold_TextSize);
draw_text_set(canvas, paint, font);
canvas->translate(600, 0);
font.setSize(kAboveThreshold_TextSize);
draw_text_set(canvas, paint, font);
}
DEF_SIMPLE_GM_CAN_FAIL(stroketext_native, canvas, msg, 650, 420) {
sk_sp<SkTypeface> ttf = MakeResourceAsTypeface("fonts/Stroking.ttf");
sk_sp<SkTypeface> otf = MakeResourceAsTypeface("fonts/Stroking.otf");
sk_sp<SkTypeface> overlap = []() -> sk_sp<SkTypeface>{
std::unique_ptr<SkStreamAsset> variableStream(GetResourceAsStream("fonts/Variable.ttf"));
if (!variableStream) {
return nullptr;
}
const SkFontArguments::VariationPosition::Coordinate position[] = {
{ SkSetFourByteTag('w','g','h','t'), 721.0f },
};
SkFontArguments params;
params.setVariationDesignPosition({position, SK_ARRAY_COUNT(position)});
return SkFontMgr::RefDefault()->makeFromStream(std::move(variableStream), params);
}();
if (!ttf && !otf && !overlap) {
msg->append("No support for ttf or otf.");
return skiagm::DrawResult::kSkip;
}
SkPaint p;
p.setAntiAlias(true);
p.setStyle(SkPaint::kStroke_Style);
p.setStrokeWidth(10);
p.setStrokeCap(SkPaint::kRound_Cap);
p.setStrokeJoin(SkPaint::kRound_Join);
p.setARGB(0xff, 0xbb, 0x00, 0x00);
if (ttf) {
/* Stroking.ttf is structured like:
nothing U+25CB ○ (nothing inside)
something U+25C9 ◉ (a tiny thing inside)
- off (point off / empty quad with implicit end) (before U+207B ⁻ / after U+208B ₋)
+ on (point on / empty line) (before U+207A ⁺ / after U+208A ₊)
0 off off (two implicit quads) (before U+2070 ⁰ / after U+2080 ₀)
1 off on (quad with implicit close around) (before U+00B9 ¹ / after U+2081 ₁)
2 on off (quad with implicit close) (before U+00B2 ² / after U+2082 ₂)
3 on on (empty line) (before U+00B3 ³ / after U+2083 ₃)
*/
SkFont font(ttf, 100);
canvas->drawString("○◉ ⁻₋⁺₊", 10, 100, font, p);
canvas->drawString("⁰₀¹₁²₂³₃", 10, 200, font, p);
}
if (otf) {
/* Stroking.otf is structured like:
nothing U+25CB ○
something U+25C9 ◉
0 moveto, moveto (before U+2070 ⁰) (nothing there, FreeType ignores these)
1 moveto, empty line, moveto (before U+00B9 ¹) (degenerate lineto)
3 moveto, empty cubic, moveto (before U+00B3 ³) (degenerate cubicto)
f moveto, empty flex, moveto (before U+1DA0 ᶠ) (degenerate flex)
*/
SkFont font(otf, 100);
canvas->drawString("○◉ ⁰¹³ᶠ", 10, 300, font, p);
}
if (overlap) {
/* Variable.ttf is structured like:
U+74 t (glyf outline has overlap flag)
U+167 ŧ (glyf outline does not have overlap flag)
*/
SkFont font(overlap, 100);
p.setStrokeWidth(1);
canvas->drawString("", 10, 400, font, p);
}
return skiagm::DrawResult::kOk;
}