blob: 869706e6527aee6adf5c55d2747cf083b613702c [file] [log] [blame]
/*
* Copyright 2010 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrTextContext.h"
#include "GrContext.h"
#include "SkAutoKern.h"
#include "SkGlyphCache.h"
#include "GrFontScaler.h"
GrTextContext::GrTextContext(GrContext* context, const SkDeviceProperties& properties) :
fFallbackTextContext(NULL),
fContext(context), fDeviceProperties(properties), fDrawTarget(NULL) {
}
GrTextContext::~GrTextContext() {
SkDELETE(fFallbackTextContext);
}
void GrTextContext::init(const GrPaint& grPaint, const SkPaint& skPaint) {
const GrClipData* clipData = fContext->getClip();
SkRect devConservativeBound;
clipData->fClipStack->getConservativeBounds(
-clipData->fOrigin.fX,
-clipData->fOrigin.fY,
fContext->getRenderTarget()->width(),
fContext->getRenderTarget()->height(),
&devConservativeBound);
devConservativeBound.roundOut(&fClipRect);
fDrawTarget = fContext->getTextTarget();
fPaint = grPaint;
fSkPaint = skPaint;
}
bool GrTextContext::drawText(const GrPaint& paint, const SkPaint& skPaint,
const char text[], size_t byteLength,
SkScalar x, SkScalar y) {
SkASSERT(byteLength == 0 || text != NULL);
// nothing to draw
if (text == NULL || byteLength == 0) {
return true;
}
SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc();
SkAutoGlyphCache autoCache(skPaint, &fDeviceProperties, NULL);
SkGlyphCache* cache = autoCache.getCache();
SkTArray<SkScalar> positions;
const char* textPtr = text;
SkFixed stopX = 0;
SkFixed stopY = 0;
SkFixed origin;
switch (skPaint.getTextAlign()) {
case SkPaint::kRight_Align: origin = SK_Fixed1; break;
case SkPaint::kCenter_Align: origin = SK_FixedHalf; break;
case SkPaint::kLeft_Align: origin = 0; break;
default: SkFAIL("Invalid paint origin"); return false;
}
SkAutoKern autokern;
const char* stop = text + byteLength;
while (textPtr < stop) {
// don't need x, y here, since all subpixel variants will have the
// same advance
const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0);
SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph);
positions.push_back(SkFixedToScalar(stopX + SkFixedMul_portable(origin, width)));
SkFixed height = glyph.fAdvanceY;
positions.push_back(SkFixedToScalar(stopY + SkFixedMul_portable(origin, height)));
stopX += width;
stopY += height;
}
SkASSERT(textPtr == stop);
// now adjust starting point depending on alignment
SkScalar alignX = SkFixedToScalar(stopX);
SkScalar alignY = SkFixedToScalar(stopY);
if (skPaint.getTextAlign() == SkPaint::kCenter_Align) {
alignX = SkScalarHalf(alignX);
alignY = SkScalarHalf(alignY);
} else if (skPaint.getTextAlign() == SkPaint::kLeft_Align) {
alignX = 0;
alignY = 0;
}
x -= alignX;
y -= alignY;
SkPoint offset = SkPoint::Make(x, y);
return this->drawPosText(paint, skPaint, text, byteLength, positions.begin(), 2, offset);
}
bool GrTextContext::drawPosText(const GrPaint& paint, const SkPaint& skPaint,
const char text[], size_t byteLength,
const SkScalar pos[], int scalarsPerPosition,
const SkPoint& offset) {
GrTextContext* textContext = this;
do {
if (textContext->canDraw(skPaint)) {
textContext->onDrawPosText(paint, skPaint, text, byteLength, pos, scalarsPerPosition,
offset);
return true;
}
textContext = textContext->fFallbackTextContext;
} while (textContext);
return false;
}
//*** change to output positions?
void GrTextContext::MeasureText(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc,
const char text[], size_t byteLength, SkVector* stopVector) {
SkFixed x = 0, y = 0;
const char* stop = text + byteLength;
SkAutoKern autokern;
while (text < stop) {
// don't need x, y here, since all subpixel variants will have the
// same advance
const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
x += autokern.adjust(glyph) + glyph.fAdvanceX;
y += glyph.fAdvanceY;
}
stopVector->set(SkFixedToScalar(x), SkFixedToScalar(y));
SkASSERT(text == stop);
}
static void GlyphCacheAuxProc(void* data) {
GrFontScaler* scaler = (GrFontScaler*)data;
SkSafeUnref(scaler);
}
GrFontScaler* GrTextContext::GetGrFontScaler(SkGlyphCache* cache) {
void* auxData;
GrFontScaler* scaler = NULL;
if (cache->getAuxProcData(GlyphCacheAuxProc, &auxData)) {
scaler = (GrFontScaler*)auxData;
}
if (NULL == scaler) {
scaler = SkNEW_ARGS(GrFontScaler, (cache));
cache->setAuxProc(GlyphCacheAuxProc, scaler);
}
return scaler;
}