1fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot/*
2fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Copyright 2015 Google Inc.
3fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot *
4fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Use of this source code is governed by a BSD-style license that can be
5fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * found in the LICENSE file.
6fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot */
7fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
8fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "GrTextUtils.h"
9fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "GrContext.h"
10fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkDrawFilter.h"
11fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkDrawProcs.h"
12fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkGlyphCache.h"
13fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkGr.h"
14fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkPaint.h"
15fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkTextBlobRunIterator.h"
16fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkTextMapStateProc.h"
17fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkTextToPathIter.h"
18fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
19fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid GrTextUtils::Paint::initFilteredColor() {
20fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // This mirrors the logic in skpaint_to_grpaint_impl for handling paint colors
21fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (fDstColorSpaceInfo->colorSpace()) {
22fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        GrColor4f filteredColor =
23fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkColorToUnpremulGrColor4f(fPaint->getColor(), *fDstColorSpaceInfo);
24fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (fPaint->getColorFilter()) {
25fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            filteredColor = GrColor4f::FromSkColor4f(
26fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                fPaint->getColorFilter()->filterColor4f(filteredColor.toSkColor4f()));
27fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
28fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fFilteredPremulColor = filteredColor.premul().toGrColor();
29fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else {
30fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkColor filteredSkColor = fPaint->getColor();
31fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (fPaint->getColorFilter()) {
32fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            filteredSkColor = fPaint->getColorFilter()->filterColor(filteredSkColor);
33fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
34fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fFilteredPremulColor = SkColorToPremulGrColor(filteredSkColor);
35fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
36fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
37fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
38fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotbool GrTextUtils::RunPaint::modifyForRun(const SkTextBlobRunIterator& run) {
39fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!fModifiedPaint.isValid()) {
40fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fModifiedPaint.init(fOriginalPaint->skPaint());
41fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fPaint = fModifiedPaint.get();
42fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else if (fFilter) {
43fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // We have to reset before applying the run because the filter could have arbitrary
44fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // changed the paint.
45fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        *fModifiedPaint.get() = fOriginalPaint->skPaint();
46fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
47fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    run.applyFontToPaint(fModifiedPaint.get());
48fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
49fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (fFilter) {
50fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (!fFilter->filter(fModifiedPaint.get(), SkDrawFilter::kText_Type)) {
51fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // A false return from filter() means we should abort the current draw.
52fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return false;
53fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
54fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // The draw filter could have changed either the paint color or color filter.
55fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        this->initFilteredColor();
56fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
57fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fModifiedPaint.get()->setFlags(FilterTextFlags(fProps, *fModifiedPaint.get()));
58fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return true;
59fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
60fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
61fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotuint32_t GrTextUtils::FilterTextFlags(const SkSurfaceProps& surfaceProps, const SkPaint& paint) {
62fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint32_t flags = paint.getFlags();
63fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
64fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!paint.isLCDRenderText() || !paint.isAntiAlias()) {
65fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return flags;
66fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
67fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
68fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (kUnknown_SkPixelGeometry == surfaceProps.pixelGeometry() || ShouldDisableLCD(paint)) {
69fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        flags &= ~SkPaint::kLCDRenderText_Flag;
70fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        flags |= SkPaint::kGenA8FromLCD_Flag;
71fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
72fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
73fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return flags;
74fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
75fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
76fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotbool GrTextUtils::ShouldDisableLCD(const SkPaint& paint) {
77fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return paint.getMaskFilter() || paint.getPathEffect() ||
78fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot           paint.isFakeBoldText() || paint.getStyle() != SkPaint::kFill_Style;
79fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
80fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
81fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid GrTextUtils::DrawBigText(GrTextUtils::Target* target,
82fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                              const GrClip& clip, const SkPaint& paint,
83fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                              const SkMatrix& viewMatrix, const char text[], size_t byteLength,
84fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                              SkScalar x, SkScalar y, const SkIRect& clipBounds) {
85fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!paint.countText(text, byteLength)) {
86fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
87fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
88fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkTextToPathIter iter(text, byteLength, paint, true);
89fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
90fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkMatrix    matrix;
91fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    matrix.setScale(iter.getPathScale(), iter.getPathScale());
92fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    matrix.postTranslate(x, y);
93fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
94fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const SkPath* iterPath;
95fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkScalar xpos, prevXPos = 0;
96fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
97fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    while (iter.next(&iterPath, &xpos)) {
98fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        matrix.postTranslate(xpos - prevXPos, 0);
99fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (iterPath) {
100fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            const SkPaint& pnt = iter.getPaint();
101fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            target->drawPath(clip, *iterPath, pnt, viewMatrix, &matrix, clipBounds);
102fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
103fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        prevXPos = xpos;
104fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
105fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
106fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
107fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid GrTextUtils::DrawBigPosText(GrTextUtils::Target* target,
108fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                 const SkSurfaceProps& props, const GrClip& clip,
109fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                 const SkPaint& origPaint, const SkMatrix& viewMatrix,
110fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                 const char text[], size_t byteLength, const SkScalar pos[],
111fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                 int scalarsPerPosition, const SkPoint& offset,
112fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                 const SkIRect& clipBounds) {
113fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!origPaint.countText(text, byteLength)) {
114fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
115fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
116fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // setup our std paint, in hopes of getting hits in the cache
117fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPaint paint(origPaint);
118fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkScalar matrixScale = paint.setupForAsPaths();
119fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
120fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkMatrix matrix;
121fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    matrix.setScale(matrixScale, matrixScale);
122fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
123fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Temporarily jam in kFill, so we only ever ask for the raw outline from the cache.
124fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    paint.setStyle(SkPaint::kFill_Style);
125fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    paint.setPathEffect(nullptr);
126fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
127fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPaint::GlyphCacheProc    glyphCacheProc = SkPaint::GetGlyphCacheProc(paint.getTextEncoding(),
128fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                                           paint.isDevKernText(),
129fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                                           true);
130fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkAutoGlyphCache           autoCache(paint, &props, nullptr);
131fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkGlyphCache*              cache = autoCache.getCache();
132fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
133fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const char*        stop = text + byteLength;
134fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkTextAlignProc    alignProc(paint.getTextAlign());
135fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkTextMapStateProc tmsProc(SkMatrix::I(), offset, scalarsPerPosition);
136fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
137fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Now restore the original settings, so we "draw" with whatever style/stroking.
138fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    paint.setStyle(origPaint.getStyle());
139fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    paint.setPathEffect(origPaint.refPathEffect());
140fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
141fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    while (text < stop) {
142fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const SkGlyph& glyph = glyphCacheProc(cache, &text);
143fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (glyph.fWidth) {
144fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            const SkPath* path = cache->findPath(glyph);
145fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (path) {
146fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkPoint tmsLoc;
147fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                tmsProc(pos, &tmsLoc);
148fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkPoint loc;
149fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                alignProc(tmsLoc, glyph, &loc);
150fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
151fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                matrix[SkMatrix::kMTransX] = loc.fX;
152fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                matrix[SkMatrix::kMTransY] = loc.fY;
153fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                target->drawPath(clip, *path, paint, viewMatrix, &matrix, clipBounds);
154fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
155fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
156fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        pos += scalarsPerPosition;
157fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
158fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
159