blob: 0ef18aaeb2aaed2de32345d076f441befecc56e1 [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 "GrBlurUtils.h"
#include "GrContext.h"
#include "GrDrawContext.h"
#include "GrFontScaler.h"
#include "SkAutoKern.h"
#include "SkDrawFilter.h"
#include "SkDrawProcs.h"
#include "SkGlyphCache.h"
#include "SkGpuDevice.h"
#include "SkGrPriv.h"
#include "SkTextBlobRunIterator.h"
#include "SkTextMapStateProc.h"
#include "SkTextToPathIter.h"
GrTextContext::GrTextContext(GrContext* context, const SkSurfaceProps& surfaceProps)
: fFallbackTextContext(nullptr)
, fContext(context)
, fSurfaceProps(surfaceProps) {
}
GrTextContext::~GrTextContext() {
delete fFallbackTextContext;
}
void GrTextContext::drawText(GrDrawContext* dc, GrRenderTarget* rt,
const GrClip& clip, const GrPaint& paint,
const SkPaint& skPaint, const SkMatrix& viewMatrix,
const char text[], size_t byteLength,
SkScalar x, SkScalar y, const SkIRect& clipBounds) {
if (fContext->abandoned()) {
return;
}
GrTextContext* textContext = this;
do {
if (textContext->canDraw(rt, clip, paint, skPaint, viewMatrix)) {
textContext->onDrawText(dc, rt, clip, paint, skPaint, viewMatrix,
text, byteLength, x, y, clipBounds);
return;
}
textContext = textContext->fFallbackTextContext;
} while (textContext);
// fall back to drawing as a path
this->drawTextAsPath(dc, rt, clip, skPaint, viewMatrix,
text, byteLength, x, y, clipBounds);
}
void GrTextContext::drawPosText(GrDrawContext* dc, GrRenderTarget* rt,
const GrClip& clip, const GrPaint& paint,
const SkPaint& skPaint, const SkMatrix& viewMatrix,
const char text[], size_t byteLength,
const SkScalar pos[], int scalarsPerPosition,
const SkPoint& offset, const SkIRect& clipBounds) {
if (fContext->abandoned()) {
return;
}
GrTextContext* textContext = this;
do {
if (textContext->canDraw(rt, clip, paint, skPaint, viewMatrix)) {
textContext->onDrawPosText(dc, rt, clip, paint, skPaint, viewMatrix,
text, byteLength, pos,
scalarsPerPosition, offset, clipBounds);
return;
}
textContext = textContext->fFallbackTextContext;
} while (textContext);
// fall back to drawing as a path
this->drawPosTextAsPath(dc, rt, clip, skPaint, viewMatrix, text, byteLength, pos,
scalarsPerPosition, offset, clipBounds);
}
bool GrTextContext::ShouldDisableLCD(const SkPaint& paint) {
if (!SkXfermode::AsMode(paint.getXfermode(), nullptr) ||
paint.getMaskFilter() ||
paint.getRasterizer() ||
paint.getPathEffect() ||
paint.isFakeBoldText() ||
paint.getStyle() != SkPaint::kFill_Style)
{
return true;
}
return false;
}
uint32_t GrTextContext::FilterTextFlags(const SkSurfaceProps& surfaceProps, const SkPaint& paint) {
uint32_t flags = paint.getFlags();
if (!paint.isLCDRenderText() || !paint.isAntiAlias()) {
return flags;
}
if (kUnknown_SkPixelGeometry == surfaceProps.pixelGeometry() || ShouldDisableLCD(paint)) {
flags &= ~SkPaint::kLCDRenderText_Flag;
flags |= SkPaint::kGenA8FromLCD_Flag;
}
return flags;
}
void GrTextContext::drawTextBlob(GrDrawContext* dc, GrRenderTarget* rt,
const GrClip& clip, const SkPaint& skPaint,
const SkMatrix& viewMatrix, const SkTextBlob* blob,
SkScalar x, SkScalar y,
SkDrawFilter* drawFilter, const SkIRect& clipBounds) {
SkPaint runPaint = skPaint;
SkTextBlobRunIterator it(blob);
for (;!it.done(); it.next()) {
size_t textLen = it.glyphCount() * sizeof(uint16_t);
const SkPoint& offset = it.offset();
// applyFontToPaint() always overwrites the exact same attributes,
// so it is safe to not re-seed the paint for this reason.
it.applyFontToPaint(&runPaint);
if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
// A false return from filter() means we should abort the current draw.
runPaint = skPaint;
continue;
}
runPaint.setFlags(FilterTextFlags(fSurfaceProps, runPaint));
GrPaint grPaint;
if (!SkPaintToGrPaint(fContext, runPaint, viewMatrix, &grPaint)) {
return;
}
switch (it.positioning()) {
case SkTextBlob::kDefault_Positioning:
this->drawText(dc, rt, clip, grPaint, runPaint, viewMatrix, (const char *)it.glyphs(),
textLen, x + offset.x(), y + offset.y(), clipBounds);
break;
case SkTextBlob::kHorizontal_Positioning:
this->drawPosText(dc, rt, clip, grPaint, runPaint, viewMatrix, (const char*)it.glyphs(),
textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()), clipBounds);
break;
case SkTextBlob::kFull_Positioning:
this->drawPosText(dc, rt, clip, grPaint, runPaint, viewMatrix, (const char*)it.glyphs(),
textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds);
break;
default:
SkFAIL("unhandled positioning mode");
}
if (drawFilter) {
// A draw filter may change the paint arbitrarily, so we must re-seed in this case.
runPaint = skPaint;
}
}
}
void GrTextContext::drawTextAsPath(GrDrawContext* dc, GrRenderTarget* rt,
const GrClip& clip,
const SkPaint& skPaint, const SkMatrix& viewMatrix,
const char text[], size_t byteLength, SkScalar x, SkScalar y,
const SkIRect& clipBounds) {
SkTextToPathIter iter(text, byteLength, skPaint, true);
SkMatrix matrix;
matrix.setScale(iter.getPathScale(), iter.getPathScale());
matrix.postTranslate(x, y);
const SkPath* iterPath;
SkScalar xpos, prevXPos = 0;
while (iter.next(&iterPath, &xpos)) {
matrix.postTranslate(xpos - prevXPos, 0);
if (iterPath) {
const SkPaint& pnt = iter.getPaint();
GrBlurUtils::drawPathWithMaskFilter(fContext, dc, rt, clip, *iterPath,
pnt, viewMatrix, &matrix, clipBounds, false);
}
prevXPos = xpos;
}
}
void GrTextContext::drawPosTextAsPath(GrDrawContext* dc, GrRenderTarget* rt,
const GrClip& clip,
const SkPaint& origPaint, const SkMatrix& viewMatrix,
const char text[], size_t byteLength,
const SkScalar pos[], int scalarsPerPosition,
const SkPoint& offset, const SkIRect& clipBounds) {
// setup our std paint, in hopes of getting hits in the cache
SkPaint paint(origPaint);
SkScalar matrixScale = paint.setupForAsPaths();
SkMatrix matrix;
matrix.setScale(matrixScale, matrixScale);
// Temporarily jam in kFill, so we only ever ask for the raw outline from the cache.
paint.setStyle(SkPaint::kFill_Style);
paint.setPathEffect(nullptr);
SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc();
SkAutoGlyphCache autoCache(paint, &fSurfaceProps, nullptr);
SkGlyphCache* cache = autoCache.getCache();
const char* stop = text + byteLength;
SkTextAlignProc alignProc(paint.getTextAlign());
SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
// Now restore the original settings, so we "draw" with whatever style/stroking.
paint.setStyle(origPaint.getStyle());
paint.setPathEffect(origPaint.getPathEffect());
while (text < stop) {
const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
if (glyph.fWidth) {
const SkPath* path = cache->findPath(glyph);
if (path) {
SkPoint tmsLoc;
tmsProc(pos, &tmsLoc);
SkPoint loc;
alignProc(tmsLoc, glyph, &loc);
matrix[SkMatrix::kMTransX] = loc.fX;
matrix[SkMatrix::kMTransY] = loc.fY;
GrBlurUtils::drawPathWithMaskFilter(fContext, dc, rt, clip, *path, paint,
viewMatrix, &matrix, clipBounds, false);
}
}
pos += scalarsPerPosition;
}
}
// *** change to output positions?
int 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;
int numGlyphs = 0;
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;
++numGlyphs;
}
stopVector->set(SkFixedToScalar(x), SkFixedToScalar(y));
SkASSERT(text == stop);
return numGlyphs;
}
static void GlyphCacheAuxProc(void* data) {
GrFontScaler* scaler = (GrFontScaler*)data;
SkSafeUnref(scaler);
}
GrFontScaler* GrTextContext::GetGrFontScaler(SkGlyphCache* cache) {
void* auxData;
GrFontScaler* scaler = nullptr;
if (cache->getAuxProcData(GlyphCacheAuxProc, &auxData)) {
scaler = (GrFontScaler*)auxData;
}
if (nullptr == scaler) {
scaler = new GrFontScaler(cache);
cache->setAuxProc(GlyphCacheAuxProc, scaler);
}
return scaler;
}