/*
* 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.h"
#include "SkCanvas.h"
#include "SkString.h"
#include "SkTypeface.h"
#include "SkTypes.h"
static const char* gFaces[] = {
"Times Roman",
"Hiragino Maru Gothic Pro",
"Papyrus",
"Helvetica",
"Courier New"
};
class TypefaceGM : public skiagm::GM {
public:
TypefaceGM() {
fFaces = new SkTypeface*[SK_ARRAY_COUNT(gFaces)];
for (size_t i = 0; i < SK_ARRAY_COUNT(gFaces); i++) {
fFaces[i] = SkTypeface::CreateFromName(gFaces[i], SkTypeface::kNormal);
}
}
virtual ~TypefaceGM() {
for (size_t i = 0; i < SK_ARRAY_COUNT(gFaces); i++) {
SkSafeUnref(fFaces[i]);
}
delete [] fFaces;
}
protected:
virtual SkString onShortName() SK_OVERRIDE {
return SkString("typeface");
}
virtual SkISize onISize() SK_OVERRIDE {
return SkISize::Make(640, 480);
}
virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
SkString text("Typefaces are fun!");
SkScalar y = 0;
SkPaint paint;
paint.setAntiAlias(true);
for (int i = 0; i < (int)SK_ARRAY_COUNT(gFaces); i++) {
this->drawWithFace(text, i, y, paint, canvas);
}
// Now go backwards
for (int i = SK_ARRAY_COUNT(gFaces) - 1; i >= 0; i--) {
this->drawWithFace(text, i, y, paint, canvas);
}
}
private:
void drawWithFace(const SkString& text, int i, SkScalar& y, SkPaint& paint,
SkCanvas* canvas) {
paint.setTypeface(fFaces[i]);
y += paint.getFontMetrics(NULL);
canvas->drawText(text.c_str(), text.size(), 0, y, paint);
}
SkTypeface** fFaces;
typedef skiagm::GM INHERITED;
};
///////////////////////////////////////////////////////////////////////////////
static void getGlyphPositions(const SkPaint& paint, const uint16_t glyphs[],
int count, SkScalar x, SkScalar y, SkPoint pos[]) {
SkASSERT(SkPaint::kGlyphID_TextEncoding == paint.getTextEncoding());
SkAutoSTMalloc<128, SkScalar> widthStorage(count);
SkScalar* widths = widthStorage.get();
paint.getTextWidths(glyphs, count * sizeof(uint16_t), widths);
for (int i = 0; i < count; ++i) {
pos[i].set(x, y);
x += widths[i];
}
}
static void applyKerning(SkPoint pos[], const int32_t adjustments[], int count,
const SkPaint& paint) {
SkScalar scale = paint.getTextSize() / paint.getTypeface()->getUnitsPerEm();
SkScalar globalAdj = 0;
for (int i = 0; i < count - 1; ++i) {
globalAdj += adjustments[i] * scale;
pos[i + 1].fX += globalAdj;
}
}
static void drawKernText(SkCanvas* canvas, const void* text, size_t len,
SkScalar x, SkScalar y, const SkPaint& paint) {
SkTypeface* face = paint.getTypeface();
if (!face) {
canvas->drawText(text, len, x, y, paint);
return;
}
SkAutoSTMalloc<128, uint16_t> glyphStorage(len);
uint16_t* glyphs = glyphStorage.get();
int glyphCount = paint.textToGlyphs(text, len, glyphs);
if (glyphCount < 1) {
return;
}
SkAutoSTMalloc<128, int32_t> adjustmentStorage(glyphCount - 1);
int32_t* adjustments = adjustmentStorage.get();
if (!face->getKerningPairAdjustments(glyphs, glyphCount, adjustments)) {
canvas->drawText(text, len, x, y, paint);
return;
}
SkPaint glyphPaint(paint);
glyphPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
SkAutoSTMalloc<128, SkPoint> posStorage(glyphCount);
SkPoint* pos = posStorage.get();
getGlyphPositions(glyphPaint, glyphs, glyphCount, x, y, pos);
applyKerning(pos, adjustments, glyphCount, glyphPaint);
canvas->drawPosText(glyphs, glyphCount * sizeof(uint16_t), pos, glyphPaint);
}
static const struct {
const char* fName;
SkTypeface::Style fStyle;
} gFaceStyles[] = {
{ "sans-serif", SkTypeface::kNormal },
{ "sans-serif", SkTypeface::kBold },
{ "sans-serif", SkTypeface::kItalic },
{ "sans-serif", SkTypeface::kBoldItalic },
{ "serif", SkTypeface::kNormal },
{ "serif", SkTypeface::kBold },
{ "serif", SkTypeface::kItalic },
{ "serif", SkTypeface::kBoldItalic },
{ "monospace", SkTypeface::kNormal },
{ "monospace", SkTypeface::kBold },
{ "monospace", SkTypeface::kItalic },
{ "monospace", SkTypeface::kBoldItalic },
};
static const int gFaceStylesCount = SK_ARRAY_COUNT(gFaceStyles);
class TypefaceStylesGM : public skiagm::GM {
SkTypeface* fFaces[gFaceStylesCount];
bool fApplyKerning;
public:
TypefaceStylesGM(bool applyKerning) : fApplyKerning(applyKerning) {
for (int i = 0; i < gFaceStylesCount; i++) {
fFaces[i] = SkTypeface::CreateFromName(gFaceStyles[i].fName,
gFaceStyles[i].fStyle);
}
}
virtual ~TypefaceStylesGM() {
for (int i = 0; i < gFaceStylesCount; i++) {
SkSafeUnref(fFaces[i]);
}
}
protected:
virtual SkString onShortName() SK_OVERRIDE {
SkString name("typefacestyles");
if (fApplyKerning) {
name.append("_kerning");
}
return name;
}
virtual SkISize onISize() SK_OVERRIDE {
return SkISize::Make(640, 480);
}
virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
SkPaint paint;
paint.setAntiAlias(true);
paint.setTextSize(SkIntToScalar(30));
const char* text = fApplyKerning ? "Type AWAY" : "Hamburgefons";
const size_t textLen = strlen(text);
SkScalar x = SkIntToScalar(10);
SkScalar dy = paint.getFontMetrics(NULL);
SkScalar y = dy;
if (fApplyKerning) {
paint.setSubpixelText(true);
} else {
paint.setLinearText(true);
}
for (int i = 0; i < gFaceStylesCount; i++) {
paint.setTypeface(fFaces[i]);
canvas->drawText(text, textLen, x, y, paint);
if (fApplyKerning) {
drawKernText(canvas, text, textLen, x + 240, y, paint);
}
y += dy;
}
}
private:
typedef skiagm::GM INHERITED;
};
///////////////////////////////////////////////////////////////////////////////
DEF_GM( return new TypefaceGM; )
DEF_GM( return new TypefaceStylesGM(false); )
DEF_GM( return new TypefaceStylesGM(true); )