Canvas.cpp revision 5e00c7ce063116c11315639f0035aca8ad73e8cc
10c1bc742181ded4930842b46e9507372f0b1b963James Dong/* 20c1bc742181ded4930842b46e9507372f0b1b963James Dong * Copyright (C) 2015 The Android Open Source Project 30c1bc742181ded4930842b46e9507372f0b1b963James Dong * 40c1bc742181ded4930842b46e9507372f0b1b963James Dong * Licensed under the Apache License, Version 2.0 (the "License"); 50c1bc742181ded4930842b46e9507372f0b1b963James Dong * you may not use this file except in compliance with the License. 60c1bc742181ded4930842b46e9507372f0b1b963James Dong * You may obtain a copy of the License at 70c1bc742181ded4930842b46e9507372f0b1b963James Dong * 80c1bc742181ded4930842b46e9507372f0b1b963James Dong * http://www.apache.org/licenses/LICENSE-2.0 90c1bc742181ded4930842b46e9507372f0b1b963James Dong * 100c1bc742181ded4930842b46e9507372f0b1b963James Dong * Unless required by applicable law or agreed to in writing, software 110c1bc742181ded4930842b46e9507372f0b1b963James Dong * distributed under the License is distributed on an "AS IS" BASIS, 120c1bc742181ded4930842b46e9507372f0b1b963James Dong * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 130c1bc742181ded4930842b46e9507372f0b1b963James Dong * See the License for the specific language governing permissions and 140c1bc742181ded4930842b46e9507372f0b1b963James Dong * limitations under the License. 150c1bc742181ded4930842b46e9507372f0b1b963James Dong */ 160c1bc742181ded4930842b46e9507372f0b1b963James Dong 170c1bc742181ded4930842b46e9507372f0b1b963James Dong#include "Canvas.h" 180c1bc742181ded4930842b46e9507372f0b1b963James Dong 190c1bc742181ded4930842b46e9507372f0b1b963James Dong#include "RecordingCanvas.h" 200c1bc742181ded4930842b46e9507372f0b1b963James Dong#include "MinikinUtils.h" 210c1bc742181ded4930842b46e9507372f0b1b963James Dong#include "Paint.h" 220c1bc742181ded4930842b46e9507372f0b1b963James Dong#include "Typeface.h" 230c1bc742181ded4930842b46e9507372f0b1b963James Dong 240c1bc742181ded4930842b46e9507372f0b1b963James Dong#include <SkDrawFilter.h> 250c1bc742181ded4930842b46e9507372f0b1b963James Dong 260c1bc742181ded4930842b46e9507372f0b1b963James Dongnamespace android { 270c1bc742181ded4930842b46e9507372f0b1b963James Dong 280c1bc742181ded4930842b46e9507372f0b1b963James DongCanvas* Canvas::create_recording_canvas(int width, int height) { 290c1bc742181ded4930842b46e9507372f0b1b963James Dong return new uirenderer::RecordingCanvas(width, height); 300c1bc742181ded4930842b46e9507372f0b1b963James Dong} 310c1bc742181ded4930842b46e9507372f0b1b963James Dong 320c1bc742181ded4930842b46e9507372f0b1b963James Dongvoid Canvas::drawTextDecorations(float x, float y, float length, const SkPaint& paint) { 330c1bc742181ded4930842b46e9507372f0b1b963James Dong uint32_t flags; 340c1bc742181ded4930842b46e9507372f0b1b963James Dong SkDrawFilter* drawFilter = getDrawFilter(); 35db43b34c3428e480f8c4c66e7e88f4001f37f91eMark Salyzyn if (drawFilter) { 360c1bc742181ded4930842b46e9507372f0b1b963James Dong SkPaint paintCopy(paint); 370c1bc742181ded4930842b46e9507372f0b1b963James Dong drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type); 380c1bc742181ded4930842b46e9507372f0b1b963James Dong flags = paintCopy.getFlags(); 390c1bc742181ded4930842b46e9507372f0b1b963James Dong } else { 400c1bc742181ded4930842b46e9507372f0b1b963James Dong flags = paint.getFlags(); 410c1bc742181ded4930842b46e9507372f0b1b963James Dong } 420c1bc742181ded4930842b46e9507372f0b1b963James Dong if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) { 430c1bc742181ded4930842b46e9507372f0b1b963James Dong // Same values used by Skia 440c1bc742181ded4930842b46e9507372f0b1b963James Dong const float kStdStrikeThru_Offset = (-6.0f / 21.0f); 450c1bc742181ded4930842b46e9507372f0b1b963James Dong const float kStdUnderline_Offset = (1.0f / 9.0f); 460c1bc742181ded4930842b46e9507372f0b1b963James Dong const float kStdUnderline_Thickness = (1.0f / 18.0f); 470c1bc742181ded4930842b46e9507372f0b1b963James Dong 480c1bc742181ded4930842b46e9507372f0b1b963James Dong SkScalar left = x; 490c1bc742181ded4930842b46e9507372f0b1b963James Dong SkScalar right = x + length; 500c1bc742181ded4930842b46e9507372f0b1b963James Dong float textSize = paint.getTextSize(); 510c1bc742181ded4930842b46e9507372f0b1b963James Dong float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f); 520c1bc742181ded4930842b46e9507372f0b1b963James Dong if (flags & SkPaint::kUnderlineText_Flag) { 530c1bc742181ded4930842b46e9507372f0b1b963James Dong SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth; 540c1bc742181ded4930842b46e9507372f0b1b963James Dong SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth; 550c1bc742181ded4930842b46e9507372f0b1b963James Dong drawRect(left, top, right, bottom, paint); 560c1bc742181ded4930842b46e9507372f0b1b963James Dong } 570c1bc742181ded4930842b46e9507372f0b1b963James Dong if (flags & SkPaint::kStrikeThruText_Flag) { 580c1bc742181ded4930842b46e9507372f0b1b963James Dong SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth; 590c1bc742181ded4930842b46e9507372f0b1b963James Dong SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth; 600c1bc742181ded4930842b46e9507372f0b1b963James Dong drawRect(left, top, right, bottom, paint); 610c1bc742181ded4930842b46e9507372f0b1b963James Dong } 620c1bc742181ded4930842b46e9507372f0b1b963James Dong } 630c1bc742181ded4930842b46e9507372f0b1b963James Dong} 640c1bc742181ded4930842b46e9507372f0b1b963James Dong 650c1bc742181ded4930842b46e9507372f0b1b963James Dongstatic void simplifyPaint(int color, SkPaint* paint) { 660c1bc742181ded4930842b46e9507372f0b1b963James Dong paint->setColor(color); 670c1bc742181ded4930842b46e9507372f0b1b963James Dong paint->setShader(nullptr); 680c1bc742181ded4930842b46e9507372f0b1b963James Dong paint->setColorFilter(nullptr); 690c1bc742181ded4930842b46e9507372f0b1b963James Dong paint->setLooper(nullptr); 700c1bc742181ded4930842b46e9507372f0b1b963James Dong paint->setStrokeWidth(4 + 0.04 * paint->getTextSize()); 710c1bc742181ded4930842b46e9507372f0b1b963James Dong paint->setStrokeJoin(SkPaint::kRound_Join); 720c1bc742181ded4930842b46e9507372f0b1b963James Dong paint->setLooper(nullptr); 730c1bc742181ded4930842b46e9507372f0b1b963James Dong} 740c1bc742181ded4930842b46e9507372f0b1b963James Dong 750c1bc742181ded4930842b46e9507372f0b1b963James Dongclass DrawTextFunctor { 760c1bc742181ded4930842b46e9507372f0b1b963James Dongpublic: 770c1bc742181ded4930842b46e9507372f0b1b963James Dong DrawTextFunctor(const minikin::Layout& layout, Canvas* canvas, uint16_t* glyphs, float* pos, 780c1bc742181ded4930842b46e9507372f0b1b963James Dong const SkPaint& paint, float x, float y, minikin::MinikinRect& bounds, 79db43b34c3428e480f8c4c66e7e88f4001f37f91eMark Salyzyn float totalAdvance) 800c1bc742181ded4930842b46e9507372f0b1b963James Dong : layout(layout) 810c1bc742181ded4930842b46e9507372f0b1b963James Dong , canvas(canvas) 82db43b34c3428e480f8c4c66e7e88f4001f37f91eMark Salyzyn , glyphs(glyphs) 830c1bc742181ded4930842b46e9507372f0b1b963James Dong , pos(pos) 840c1bc742181ded4930842b46e9507372f0b1b963James Dong , paint(paint) 85db43b34c3428e480f8c4c66e7e88f4001f37f91eMark Salyzyn , x(x) 860c1bc742181ded4930842b46e9507372f0b1b963James Dong , y(y) 870c1bc742181ded4930842b46e9507372f0b1b963James Dong , bounds(bounds) 88db43b34c3428e480f8c4c66e7e88f4001f37f91eMark Salyzyn , totalAdvance(totalAdvance) { 890c1bc742181ded4930842b46e9507372f0b1b963James Dong } 900c1bc742181ded4930842b46e9507372f0b1b963James Dong 91db43b34c3428e480f8c4c66e7e88f4001f37f91eMark Salyzyn void operator()(size_t start, size_t end) { 920c1bc742181ded4930842b46e9507372f0b1b963James Dong if (canvas->drawTextAbsolutePos()) { 930c1bc742181ded4930842b46e9507372f0b1b963James Dong for (size_t i = start; i < end; i++) { 94db43b34c3428e480f8c4c66e7e88f4001f37f91eMark Salyzyn glyphs[i] = layout.getGlyphId(i); 950c1bc742181ded4930842b46e9507372f0b1b963James Dong pos[2 * i] = x + layout.getX(i); 960c1bc742181ded4930842b46e9507372f0b1b963James Dong pos[2 * i + 1] = y + layout.getY(i); 970c1bc742181ded4930842b46e9507372f0b1b963James Dong } 980c1bc742181ded4930842b46e9507372f0b1b963James Dong } else { 990c1bc742181ded4930842b46e9507372f0b1b963James Dong for (size_t i = start; i < end; i++) { 1000c1bc742181ded4930842b46e9507372f0b1b963James Dong glyphs[i] = layout.getGlyphId(i); 1010c1bc742181ded4930842b46e9507372f0b1b963James Dong pos[2 * i] = layout.getX(i); 1020c1bc742181ded4930842b46e9507372f0b1b963James Dong pos[2 * i + 1] = layout.getY(i); 1030c1bc742181ded4930842b46e9507372f0b1b963James Dong } 1040c1bc742181ded4930842b46e9507372f0b1b963James Dong } 105db43b34c3428e480f8c4c66e7e88f4001f37f91eMark Salyzyn 106db43b34c3428e480f8c4c66e7e88f4001f37f91eMark Salyzyn size_t glyphCount = end - start; 1070c1bc742181ded4930842b46e9507372f0b1b963James Dong 1080c1bc742181ded4930842b46e9507372f0b1b963James Dong if (CC_UNLIKELY(canvas->isHighContrastText() && paint.getAlpha() != 0)) { 1090c1bc742181ded4930842b46e9507372f0b1b963James Dong // high contrast draw path 1100c1bc742181ded4930842b46e9507372f0b1b963James Dong int color = paint.getColor(); 1110c1bc742181ded4930842b46e9507372f0b1b963James Dong int channelSum = SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color); 1120c1bc742181ded4930842b46e9507372f0b1b963James Dong bool darken = channelSum < (128 * 3); 1130c1bc742181ded4930842b46e9507372f0b1b963James Dong 1140c1bc742181ded4930842b46e9507372f0b1b963James Dong // outline 1150c1bc742181ded4930842b46e9507372f0b1b963James Dong SkPaint outlinePaint(paint); 1160c1bc742181ded4930842b46e9507372f0b1b963James Dong simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint); 1170c1bc742181ded4930842b46e9507372f0b1b963James Dong outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style); 1180c1bc742181ded4930842b46e9507372f0b1b963James Dong canvas->drawGlyphs(glyphs + start, pos + (2 * start), glyphCount, outlinePaint, x, y, 1190c1bc742181ded4930842b46e9507372f0b1b963James Dong bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom, totalAdvance); 1200c1bc742181ded4930842b46e9507372f0b1b963James Dong 1210c1bc742181ded4930842b46e9507372f0b1b963James Dong // inner 1220c1bc742181ded4930842b46e9507372f0b1b963James Dong SkPaint innerPaint(paint); 1230c1bc742181ded4930842b46e9507372f0b1b963James Dong simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, &innerPaint); 1240c1bc742181ded4930842b46e9507372f0b1b963James Dong innerPaint.setStyle(SkPaint::kFill_Style); 1250c1bc742181ded4930842b46e9507372f0b1b963James Dong canvas->drawGlyphs(glyphs + start, pos + (2 * start), glyphCount, innerPaint, x, y, 1260c1bc742181ded4930842b46e9507372f0b1b963James Dong bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom, totalAdvance); 1270c1bc742181ded4930842b46e9507372f0b1b963James Dong } else { 1280c1bc742181ded4930842b46e9507372f0b1b963James Dong // standard draw path 1290c1bc742181ded4930842b46e9507372f0b1b963James Dong canvas->drawGlyphs(glyphs + start, pos + (2 * start), glyphCount, paint, x, y, 1300c1bc742181ded4930842b46e9507372f0b1b963James Dong bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom, totalAdvance); 1310c1bc742181ded4930842b46e9507372f0b1b963James Dong } 1320c1bc742181ded4930842b46e9507372f0b1b963James Dong } 1330c1bc742181ded4930842b46e9507372f0b1b963James Dongprivate: 1340c1bc742181ded4930842b46e9507372f0b1b963James Dong const minikin::Layout& layout; 1350c1bc742181ded4930842b46e9507372f0b1b963James Dong Canvas* canvas; 1360c1bc742181ded4930842b46e9507372f0b1b963James Dong uint16_t* glyphs; 1370c1bc742181ded4930842b46e9507372f0b1b963James Dong float* pos; 1380c1bc742181ded4930842b46e9507372f0b1b963James Dong const SkPaint& paint; 1390c1bc742181ded4930842b46e9507372f0b1b963James Dong float x; 1400c1bc742181ded4930842b46e9507372f0b1b963James Dong float y; 1410c1bc742181ded4930842b46e9507372f0b1b963James Dong minikin::MinikinRect& bounds; 1420c1bc742181ded4930842b46e9507372f0b1b963James Dong float totalAdvance; 1430c1bc742181ded4930842b46e9507372f0b1b963James Dong}; 1440c1bc742181ded4930842b46e9507372f0b1b963James Dong 1450c1bc742181ded4930842b46e9507372f0b1b963James Dongvoid Canvas::drawText(const uint16_t* text, int start, int count, int contextCount, 1460c1bc742181ded4930842b46e9507372f0b1b963James Dong float x, float y, int bidiFlags, const Paint& origPaint, Typeface* typeface) { 1470c1bc742181ded4930842b46e9507372f0b1b963James Dong // minikin may modify the original paint 1480c1bc742181ded4930842b46e9507372f0b1b963James Dong Paint paint(origPaint); 1490c1bc742181ded4930842b46e9507372f0b1b963James Dong 1500c1bc742181ded4930842b46e9507372f0b1b963James Dong minikin::Layout layout; 1510c1bc742181ded4930842b46e9507372f0b1b963James Dong MinikinUtils::doLayout(&layout, &paint, bidiFlags, typeface, text, start, count, contextCount); 1520c1bc742181ded4930842b46e9507372f0b1b963James Dong 1530c1bc742181ded4930842b46e9507372f0b1b963James Dong size_t nGlyphs = layout.nGlyphs(); 1540c1bc742181ded4930842b46e9507372f0b1b963James Dong std::unique_ptr<uint16_t[]> glyphs(new uint16_t[nGlyphs]); 1550c1bc742181ded4930842b46e9507372f0b1b963James Dong std::unique_ptr<float[]> pos(new float[nGlyphs * 2]); 1560c1bc742181ded4930842b46e9507372f0b1b963James Dong 1570c1bc742181ded4930842b46e9507372f0b1b963James Dong x += MinikinUtils::xOffsetForTextAlign(&paint, layout); 1580c1bc742181ded4930842b46e9507372f0b1b963James Dong 1590c1bc742181ded4930842b46e9507372f0b1b963James Dong minikin::MinikinRect bounds; 1600c1bc742181ded4930842b46e9507372f0b1b963James Dong layout.getBounds(&bounds); 1610c1bc742181ded4930842b46e9507372f0b1b963James Dong if (!drawTextAbsolutePos()) { 1620c1bc742181ded4930842b46e9507372f0b1b963James Dong bounds.offset(x, y); 1630c1bc742181ded4930842b46e9507372f0b1b963James Dong } 1640c1bc742181ded4930842b46e9507372f0b1b963James Dong 1650c1bc742181ded4930842b46e9507372f0b1b963James Dong // Set align to left for drawing, as we don't want individual 1660c1bc742181ded4930842b46e9507372f0b1b963James Dong // glyphs centered or right-aligned; the offset above takes 1670c1bc742181ded4930842b46e9507372f0b1b963James Dong // care of all alignment. 1680c1bc742181ded4930842b46e9507372f0b1b963James Dong paint.setTextAlign(Paint::kLeft_Align); 1690c1bc742181ded4930842b46e9507372f0b1b963James Dong 1700c1bc742181ded4930842b46e9507372f0b1b963James Dong DrawTextFunctor f(layout, this, glyphs.get(), pos.get(), 1710c1bc742181ded4930842b46e9507372f0b1b963James Dong paint, x, y, bounds, layout.getAdvance()); 1720c1bc742181ded4930842b46e9507372f0b1b963James Dong MinikinUtils::forFontRun(layout, &paint, f); 1730c1bc742181ded4930842b46e9507372f0b1b963James Dong} 1740c1bc742181ded4930842b46e9507372f0b1b963James Dong 1750c1bc742181ded4930842b46e9507372f0b1b963James Dongclass DrawTextOnPathFunctor { 1760c1bc742181ded4930842b46e9507372f0b1b963James Dongpublic: 1770c1bc742181ded4930842b46e9507372f0b1b963James Dong DrawTextOnPathFunctor(const minikin::Layout& layout, Canvas* canvas, float hOffset, 1780c1bc742181ded4930842b46e9507372f0b1b963James Dong float vOffset, const Paint& paint, const SkPath& path) 1790c1bc742181ded4930842b46e9507372f0b1b963James Dong : layout(layout) 1800c1bc742181ded4930842b46e9507372f0b1b963James Dong , canvas(canvas) 1810c1bc742181ded4930842b46e9507372f0b1b963James Dong , hOffset(hOffset) 1820c1bc742181ded4930842b46e9507372f0b1b963James Dong , vOffset(vOffset) 1830c1bc742181ded4930842b46e9507372f0b1b963James Dong , paint(paint) 1840c1bc742181ded4930842b46e9507372f0b1b963James Dong , path(path) { 1850c1bc742181ded4930842b46e9507372f0b1b963James Dong } 1860c1bc742181ded4930842b46e9507372f0b1b963James Dong 1870c1bc742181ded4930842b46e9507372f0b1b963James Dong void operator()(size_t start, size_t end) { 1880c1bc742181ded4930842b46e9507372f0b1b963James Dong uint16_t glyphs[1]; 1890c1bc742181ded4930842b46e9507372f0b1b963James Dong for (size_t i = start; i < end; i++) { 1900c1bc742181ded4930842b46e9507372f0b1b963James Dong glyphs[0] = layout.getGlyphId(i); 1910c1bc742181ded4930842b46e9507372f0b1b963James Dong float x = hOffset + layout.getX(i); 1920c1bc742181ded4930842b46e9507372f0b1b963James Dong float y = vOffset + layout.getY(i); 1930c1bc742181ded4930842b46e9507372f0b1b963James Dong canvas->drawGlyphsOnPath(glyphs, 1, path, x, y, paint); 1940c1bc742181ded4930842b46e9507372f0b1b963James Dong } 1950c1bc742181ded4930842b46e9507372f0b1b963James Dong } 1960c1bc742181ded4930842b46e9507372f0b1b963James Dongprivate: 1970c1bc742181ded4930842b46e9507372f0b1b963James Dong const minikin::Layout& layout; 1980c1bc742181ded4930842b46e9507372f0b1b963James Dong Canvas* canvas; 1990c1bc742181ded4930842b46e9507372f0b1b963James Dong float hOffset; 2000c1bc742181ded4930842b46e9507372f0b1b963James Dong float vOffset; 2010c1bc742181ded4930842b46e9507372f0b1b963James Dong const Paint& paint; 2020c1bc742181ded4930842b46e9507372f0b1b963James Dong const SkPath& path; 2030c1bc742181ded4930842b46e9507372f0b1b963James Dong}; 2040c1bc742181ded4930842b46e9507372f0b1b963James Dong 2050c1bc742181ded4930842b46e9507372f0b1b963James Dongvoid Canvas::drawTextOnPath(const uint16_t* text, int count, int bidiFlags, const SkPath& path, 2060c1bc742181ded4930842b46e9507372f0b1b963James Dong float hOffset, float vOffset, const Paint& paint, Typeface* typeface) { 2070c1bc742181ded4930842b46e9507372f0b1b963James Dong Paint paintCopy(paint); 2080c1bc742181ded4930842b46e9507372f0b1b963James Dong minikin::Layout layout; 2090c1bc742181ded4930842b46e9507372f0b1b963James Dong MinikinUtils::doLayout(&layout, &paintCopy, bidiFlags, typeface, text, 0, count, count); 2100c1bc742181ded4930842b46e9507372f0b1b963James Dong hOffset += MinikinUtils::hOffsetForTextAlign(&paintCopy, layout, path); 211 212 // Set align to left for drawing, as we don't want individual 213 // glyphs centered or right-aligned; the offset above takes 214 // care of all alignment. 215 paintCopy.setTextAlign(Paint::kLeft_Align); 216 217 DrawTextOnPathFunctor f(layout, this, hOffset, vOffset, paintCopy, path); 218 MinikinUtils::forFontRun(layout, &paintCopy, f); 219} 220 221} // namespace android 222