Home | History | Annotate | Download | only in gpu
      1 /*
      2  * Copyright 2010 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "GrTextContext.h"
      9 #include "GrContext.h"
     10 #include "GrDrawTarget.h"
     11 #include "GrFontScaler.h"
     12 
     13 #include "SkAutoKern.h"
     14 #include "SkDrawFilter.h"
     15 #include "SkDrawProcs.h"
     16 #include "SkGlyphCache.h"
     17 #include "SkGpuDevice.h"
     18 #include "SkTextBlob.h"
     19 #include "SkTextMapStateProc.h"
     20 #include "SkTextToPathIter.h"
     21 
     22 GrTextContext::GrTextContext(GrContext* context, SkGpuDevice* gpuDevice,
     23                              const SkDeviceProperties& properties)
     24     : fFallbackTextContext(NULL)
     25     , fContext(context)
     26     , fGpuDevice(gpuDevice)
     27     , fDeviceProperties(properties)
     28     , fDrawTarget(NULL) {
     29 }
     30 
     31 GrTextContext::~GrTextContext() {
     32     SkDELETE(fFallbackTextContext);
     33 }
     34 
     35 void GrTextContext::init(GrRenderTarget* rt, const GrClip& clip, const GrPaint& grPaint,
     36                          const SkPaint& skPaint, const SkIRect& regionClipBounds) {
     37     fClip = clip;
     38 
     39     fRenderTarget.reset(SkRef(rt));
     40 
     41     fRegionClipBounds = regionClipBounds;
     42     fClip.getConservativeBounds(fRenderTarget->width(), fRenderTarget->height(), &fClipRect);
     43 
     44     fDrawTarget = fContext->getTextTarget();
     45 
     46     fPaint = grPaint;
     47     fSkPaint = skPaint;
     48 }
     49 
     50 void GrTextContext::drawText(GrRenderTarget* rt, const GrClip& clip, const GrPaint& paint,
     51                              const SkPaint& skPaint, const SkMatrix& viewMatrix,
     52                              const char text[], size_t byteLength,
     53                              SkScalar x, SkScalar y, const SkIRect& clipBounds) {
     54     if (!fContext->getTextTarget()) {
     55         return;
     56     }
     57 
     58     GrTextContext* textContext = this;
     59     do {
     60         if (textContext->canDraw(rt, clip, paint, skPaint, viewMatrix)) {
     61             textContext->onDrawText(rt, clip, paint, skPaint, viewMatrix, text, byteLength, x, y,
     62                                     clipBounds);
     63             return;
     64         }
     65         textContext = textContext->fFallbackTextContext;
     66     } while (textContext);
     67 
     68     // fall back to drawing as a path
     69     SkASSERT(fGpuDevice);
     70     this->drawTextAsPath(skPaint, viewMatrix, text, byteLength, x, y, clipBounds);
     71 }
     72 
     73 void GrTextContext::drawPosText(GrRenderTarget* rt, const GrClip& clip, const GrPaint& paint,
     74                                 const SkPaint& skPaint, const SkMatrix& viewMatrix,
     75                                 const char text[], size_t byteLength,
     76                                 const SkScalar pos[], int scalarsPerPosition,
     77                                 const SkPoint& offset, const SkIRect& clipBounds) {
     78     if (!fContext->getTextTarget()) {
     79         return;
     80     }
     81 
     82     GrTextContext* textContext = this;
     83     do {
     84         if (textContext->canDraw(rt, clip, paint, skPaint, viewMatrix)) {
     85             textContext->onDrawPosText(rt, clip, paint, skPaint, viewMatrix, text, byteLength, pos,
     86                                        scalarsPerPosition, offset, clipBounds);
     87             return;
     88         }
     89         textContext = textContext->fFallbackTextContext;
     90     } while (textContext);
     91 
     92     // fall back to drawing as a path
     93     SkASSERT(fGpuDevice);
     94     this->drawPosTextAsPath(skPaint, viewMatrix, text, byteLength, pos, scalarsPerPosition, offset,
     95                             clipBounds);
     96 }
     97 
     98 void GrTextContext::drawTextBlob(GrRenderTarget* rt, const GrClip& clip, const SkPaint& skPaint,
     99                                  const SkMatrix& viewMatrix, const SkTextBlob* blob,
    100                                  SkScalar x, SkScalar y,
    101                                  SkDrawFilter* drawFilter, const SkIRect& clipBounds) {
    102     SkPaint runPaint = skPaint;
    103 
    104     SkTextBlob::RunIterator it(blob);
    105     for (;!it.done(); it.next()) {
    106         size_t textLen = it.glyphCount() * sizeof(uint16_t);
    107         const SkPoint& offset = it.offset();
    108         // applyFontToPaint() always overwrites the exact same attributes,
    109         // so it is safe to not re-seed the paint for this reason.
    110         it.applyFontToPaint(&runPaint);
    111 
    112         if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
    113             // A false return from filter() means we should abort the current draw.
    114             runPaint = skPaint;
    115             continue;
    116         }
    117 
    118         runPaint.setFlags(fGpuDevice->filterTextFlags(runPaint));
    119 
    120         GrPaint grPaint;
    121         if (!SkPaint2GrPaint(fContext, fRenderTarget, runPaint, viewMatrix, true, &grPaint)) {
    122             return;
    123         }
    124 
    125         switch (it.positioning()) {
    126         case SkTextBlob::kDefault_Positioning:
    127             this->drawText(rt, clip, grPaint, runPaint, viewMatrix, (const char *)it.glyphs(),
    128                            textLen, x + offset.x(), y + offset.y(), clipBounds);
    129             break;
    130         case SkTextBlob::kHorizontal_Positioning:
    131             this->drawPosText(rt, clip, grPaint, runPaint, viewMatrix, (const char*)it.glyphs(),
    132                               textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()), clipBounds);
    133             break;
    134         case SkTextBlob::kFull_Positioning:
    135             this->drawPosText(rt, clip, grPaint, runPaint, viewMatrix, (const char*)it.glyphs(),
    136                               textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds);
    137             break;
    138         default:
    139             SkFAIL("unhandled positioning mode");
    140         }
    141 
    142         if (drawFilter) {
    143             // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
    144             runPaint = skPaint;
    145         }
    146     }
    147 }
    148 
    149 void GrTextContext::drawTextAsPath(const SkPaint& skPaint, const SkMatrix& viewMatrix,
    150                                    const char text[], size_t byteLength, SkScalar x, SkScalar y,
    151                                    const SkIRect& clipBounds) {
    152     SkTextToPathIter iter(text, byteLength, skPaint, true);
    153 
    154     SkMatrix    matrix;
    155     matrix.setScale(iter.getPathScale(), iter.getPathScale());
    156     matrix.postTranslate(x, y);
    157 
    158     const SkPath* iterPath;
    159     SkScalar xpos, prevXPos = 0;
    160 
    161     while (iter.next(&iterPath, &xpos)) {
    162         matrix.postTranslate(xpos - prevXPos, 0);
    163         if (iterPath) {
    164             const SkPaint& pnt = iter.getPaint();
    165             fGpuDevice->internalDrawPath(*iterPath, pnt, viewMatrix, &matrix, clipBounds, false);
    166         }
    167         prevXPos = xpos;
    168     }
    169 }
    170 
    171 void GrTextContext::drawPosTextAsPath(const SkPaint& origPaint, const SkMatrix& viewMatrix,
    172                                       const char text[], size_t byteLength,
    173                                       const SkScalar pos[], int scalarsPerPosition,
    174                                       const SkPoint& offset, const SkIRect& clipBounds) {
    175     // setup our std paint, in hopes of getting hits in the cache
    176     SkPaint paint(origPaint);
    177     SkScalar matrixScale = paint.setupForAsPaths();
    178 
    179     SkMatrix matrix;
    180     matrix.setScale(matrixScale, matrixScale);
    181 
    182     // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache.
    183     paint.setStyle(SkPaint::kFill_Style);
    184     paint.setPathEffect(NULL);
    185 
    186     SkDrawCacheProc     glyphCacheProc = paint.getDrawCacheProc();
    187     SkAutoGlyphCache    autoCache(paint, NULL, NULL);
    188     SkGlyphCache*       cache = autoCache.getCache();
    189 
    190     const char*        stop = text + byteLength;
    191     SkTextAlignProc    alignProc(paint.getTextAlign());
    192     SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
    193 
    194     // Now restore the original settings, so we "draw" with whatever style/stroking.
    195     paint.setStyle(origPaint.getStyle());
    196     paint.setPathEffect(origPaint.getPathEffect());
    197 
    198     while (text < stop) {
    199         const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
    200         if (glyph.fWidth) {
    201             const SkPath* path = cache->findPath(glyph);
    202             if (path) {
    203                 SkPoint tmsLoc;
    204                 tmsProc(pos, &tmsLoc);
    205                 SkPoint loc;
    206                 alignProc(tmsLoc, glyph, &loc);
    207 
    208                 matrix[SkMatrix::kMTransX] = loc.fX;
    209                 matrix[SkMatrix::kMTransY] = loc.fY;
    210                 fGpuDevice->internalDrawPath(*path, paint, viewMatrix, &matrix, clipBounds, false);
    211             }
    212         }
    213         pos += scalarsPerPosition;
    214     }
    215 }
    216 
    217 //*** change to output positions?
    218 int GrTextContext::MeasureText(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc,
    219                                 const char text[], size_t byteLength, SkVector* stopVector) {
    220     SkFixed     x = 0, y = 0;
    221     const char* stop = text + byteLength;
    222 
    223     SkAutoKern  autokern;
    224 
    225     int numGlyphs = 0;
    226     while (text < stop) {
    227         // don't need x, y here, since all subpixel variants will have the
    228         // same advance
    229         const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
    230 
    231         x += autokern.adjust(glyph) + glyph.fAdvanceX;
    232         y += glyph.fAdvanceY;
    233         ++numGlyphs;
    234     }
    235     stopVector->set(SkFixedToScalar(x), SkFixedToScalar(y));
    236 
    237     SkASSERT(text == stop);
    238 
    239     return numGlyphs;
    240 }
    241 
    242 static void GlyphCacheAuxProc(void* data) {
    243     GrFontScaler* scaler = (GrFontScaler*)data;
    244     SkSafeUnref(scaler);
    245 }
    246 
    247 GrFontScaler* GrTextContext::GetGrFontScaler(SkGlyphCache* cache) {
    248     void* auxData;
    249     GrFontScaler* scaler = NULL;
    250 
    251     if (cache->getAuxProcData(GlyphCacheAuxProc, &auxData)) {
    252         scaler = (GrFontScaler*)auxData;
    253     }
    254     if (NULL == scaler) {
    255         scaler = SkNEW_ARGS(GrFontScaler, (cache));
    256         cache->setAuxProc(GlyphCacheAuxProc, scaler);
    257     }
    258 
    259     return scaler;
    260 }
    261