| /* |
| * 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; |
| } |