skia2/gm/textblob.cpp
kkinnunen b4a797f3aa Move font loading in gm tests and benches out of constructors
Constructing the gm tests and benches causes many calls to font loads.
This is visible as profiling samples in fontconfig and freetype on Linux
for all profiling runs of nanobench. This complicates analysis of
test-cases that are suspected of being slow due to font-related issues.

Move the font loading to GM::onOnceBeforeDraw and Benchmark::onPreDraw.
This way the code is not executed if the testcase does not match the
nanobench --match filter. This way the samples in font-related code are
more easy to identify as legitimate occurances caused by the testcase.

This should not cause differences in timings, because:
* Benchmark::preDraw / onPreDraw is defined to be run outside the timer
* GM::runAsBench is not enabled for any of the modified testcases. Also
  nanobench untimed warmup round should run the onOnceBeforeDraw.
  (and there are other GM::runAsBench gms already doing loading in
   onOnceBeforeDraw).

Changes the behavior:
In TextBench:
Before, the test would report two different gms with the same name if
the color emoji font was not loaded successfully.
After, the test always reports all tests as individual names.

Generally:
The errors from loading fonts now print inbetween each testcase, as
opposed to printing during construction phase. Sample output:
( 143/145 MB  1872) 14.7ms	8888 gm  quadclosepathResource /fonts/Funkster.ttf not a valid font.
( 160/160 MB  1831) 575µs	8888 gm  surfacenewResource /fonts/Funkster.ttf not a valid font.
( 163/165 MB  1816) 12.5ms	8888 gm  linepathResource /fonts/Funkster.ttf not a valid font.
( 263/411 MB  1493) 118ms	8888 gm  typefacestyles_kerningResource /fonts/Funkster.ttf not a valid font.
( 374/411 MB  1231) 7.16ms	565 gm  getpostextpathResource /fonts/Funkster.ttf not a valid font.
( 323/411 MB  1179) 4.92ms	565 gm  stringartResource /fonts/Funkster.ttf not a valid font.
( 347/493 MB   917) 191ms	565 gm  patch_gridResource /fonts/Funkster.ttf not a valid font.
( 375/493 MB   857) 23.9ms	gpu gm  clipdrawdrawCannot render path (0)
( 393/493 MB   706) 2.91ms	unit test  ParsePath------ png error IEND: CRC error
( 394/493 MB   584) 166ms	gpu gm  hairmodesResource /fonts/Funkster.ttf not a valid font.
Resource /fonts/Funkster.ttf not a valid font.
Resource /fonts/Funkster.ttf not a valid font.
...

Review URL: https://codereview.chromium.org/1144023002
2015-05-21 06:15:28 -07:00

190 lines
6.7 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.h"
#include "SkCanvas.h"
#include "SkPoint.h"
#include "SkTextBlob.h"
#include "SkTDArray.h"
namespace {
enum Pos {
kDefault_Pos = 0,
kScalar_Pos = 1,
kPoint_Pos = 2,
};
const struct BlobCfg {
unsigned count;
Pos pos;
SkScalar scale;
} blobConfigs[][3][3] = {
{
{ { 1024, kDefault_Pos, 1 }, { 0, kDefault_Pos, 0 }, { 0, kDefault_Pos, 0 } },
{ { 1024, kScalar_Pos, 1 }, { 0, kScalar_Pos, 0 }, { 0, kScalar_Pos, 0 } },
{ { 1024, kPoint_Pos, 1 }, { 0, kPoint_Pos, 0 }, { 0, kPoint_Pos, 0 } },
},
{
{ { 4, kDefault_Pos, 1 }, { 4, kDefault_Pos, 1 }, { 4, kDefault_Pos, 1 } },
{ { 4, kScalar_Pos, 1 }, { 4, kScalar_Pos, 1 }, { 4, kScalar_Pos, 1 } },
{ { 4, kPoint_Pos, 1 }, { 4, kPoint_Pos, 1 }, { 4, kPoint_Pos, 1 } },
},
{
{ { 4, kDefault_Pos, 1 }, { 4, kDefault_Pos, 1 }, { 4, kScalar_Pos, 1 } },
{ { 4, kScalar_Pos, 1 }, { 4, kScalar_Pos, 1 }, { 4, kPoint_Pos, 1 } },
{ { 4, kPoint_Pos, 1 }, { 4, kPoint_Pos, 1 }, { 4, kDefault_Pos, 1 } },
},
{
{ { 4, kDefault_Pos, 1 }, { 4, kScalar_Pos, 1 }, { 4, kPoint_Pos, 1 } },
{ { 4, kScalar_Pos, 1 }, { 4, kPoint_Pos, 1 }, { 4, kDefault_Pos, 1 } },
{ { 4, kPoint_Pos, 1 }, { 4, kDefault_Pos, 1 }, { 4, kScalar_Pos, 1 } },
},
{
{ { 4, kDefault_Pos, .75f }, { 4, kDefault_Pos, 1 }, { 4, kScalar_Pos, 1.25f } },
{ { 4, kScalar_Pos, .75f }, { 4, kScalar_Pos, 1 }, { 4, kPoint_Pos, 1.25f } },
{ { 4, kPoint_Pos, .75f }, { 4, kPoint_Pos, 1 }, { 4, kDefault_Pos, 1.25f } },
},
{
{ { 4, kDefault_Pos, 1 }, { 4, kScalar_Pos, .75f }, { 4, kPoint_Pos, 1.25f } },
{ { 4, kScalar_Pos, 1 }, { 4, kPoint_Pos, .75f }, { 4, kDefault_Pos, 1.25f } },
{ { 4, kPoint_Pos, 1 }, { 4, kDefault_Pos, .75f }, { 4, kScalar_Pos, 1.25f } },
},
};
const SkScalar kFontSize = 16;
}
class TextBlobGM : public skiagm::GM {
public:
TextBlobGM(const char* txt)
: fText(txt) {
}
protected:
void onOnceBeforeDraw() override {
fTypeface.reset(sk_tool_utils::create_portable_typeface("Times", SkTypeface::kNormal));
SkPaint p;
p.setTypeface(fTypeface);
size_t txtLen = strlen(fText);
int glyphCount = p.textToGlyphs(fText, txtLen, NULL);
fGlyphs.append(glyphCount);
p.textToGlyphs(fText, txtLen, fGlyphs.begin());
}
SkString onShortName() override {
return SkString("textblob");
}
SkISize onISize() override {
return SkISize::Make(640, 480);
}
void onDraw(SkCanvas* canvas) override {
for (unsigned b = 0; b < SK_ARRAY_COUNT(blobConfigs); ++b) {
SkAutoTUnref<const SkTextBlob> blob(this->makeBlob(b));
SkPaint p;
SkPoint offset = SkPoint::Make(SkIntToScalar(10 + 300 * (b % 2)),
SkIntToScalar(20 + 150 * (b / 2)));
canvas->drawTextBlob(blob, offset.x(), offset.y(), p);
p.setColor(SK_ColorBLUE);
p.setStyle(SkPaint::kStroke_Style);
SkRect box = blob->bounds();
box.offset(offset);
canvas->drawRect(box, p);
}
}
private:
const SkTextBlob* makeBlob(unsigned blobIndex) {
SkTextBlobBuilder builder;
SkPaint font;
font.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
font.setAntiAlias(true);
font.setSubpixelText(true);
font.setTypeface(fTypeface);
for (unsigned l = 0; l < SK_ARRAY_COUNT(blobConfigs[blobIndex]); ++l) {
unsigned currentGlyph = 0;
for (unsigned c = 0; c < SK_ARRAY_COUNT(blobConfigs[blobIndex][l]); ++c) {
const BlobCfg* cfg = &blobConfigs[blobIndex][l][c];
unsigned count = cfg->count;
if (count > fGlyphs.count() - currentGlyph) {
count = fGlyphs.count() - currentGlyph;
}
if (0 == count) {
break;
}
font.setTextSize(kFontSize * cfg->scale);
const SkScalar advanceX = font.getTextSize() * 0.85f;
const SkScalar advanceY = font.getTextSize() * 1.5f;
SkPoint offset = SkPoint::Make(currentGlyph * advanceX + c * advanceX,
advanceY * l);
switch (cfg->pos) {
case kDefault_Pos: {
const SkTextBlobBuilder::RunBuffer& buf = builder.allocRun(font, count,
offset.x(),
offset.y());
memcpy(buf.glyphs, fGlyphs.begin() + currentGlyph, count * sizeof(uint16_t));
} break;
case kScalar_Pos: {
const SkTextBlobBuilder::RunBuffer& buf = builder.allocRunPosH(font, count,
offset.y());
SkTDArray<SkScalar> pos;
for (unsigned i = 0; i < count; ++i) {
*pos.append() = offset.x() + i * advanceX;
}
memcpy(buf.glyphs, fGlyphs.begin() + currentGlyph, count * sizeof(uint16_t));
memcpy(buf.pos, pos.begin(), count * sizeof(SkScalar));
} break;
case kPoint_Pos: {
const SkTextBlobBuilder::RunBuffer& buf = builder.allocRunPos(font, count);
SkTDArray<SkScalar> pos;
for (unsigned i = 0; i < count; ++i) {
*pos.append() = offset.x() + i * advanceX;
*pos.append() = offset.y() + i * (advanceY / count);
}
memcpy(buf.glyphs, fGlyphs.begin() + currentGlyph, count * sizeof(uint16_t));
memcpy(buf.pos, pos.begin(), count * sizeof(SkScalar) * 2);
} break;
default:
SkFAIL("unhandled pos value");
}
currentGlyph += count;
}
}
return builder.build();
}
SkTDArray<uint16_t> fGlyphs;
SkAutoTUnref<SkTypeface> fTypeface;
const char* fText;
typedef skiagm::GM INHERITED;
};
DEF_GM( return SkNEW_ARGS(TextBlobGM, ("hamburgefons")); )