1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "Canvas.h" 18 19#include "RecordingCanvas.h" 20#include "RenderNode.h" 21#include "MinikinUtils.h" 22#include "Paint.h" 23#include "Properties.h" 24#include "pipeline/skia/SkiaRecordingCanvas.h" 25#include "Typeface.h" 26 27#include <SkDrawFilter.h> 28 29namespace android { 30 31Canvas* Canvas::create_recording_canvas(int width, int height, uirenderer::RenderNode* renderNode) { 32 if (uirenderer::Properties::isSkiaEnabled()) { 33 return new uirenderer::skiapipeline::SkiaRecordingCanvas(renderNode, width, height); 34 } 35 return new uirenderer::RecordingCanvas(width, height); 36} 37 38void Canvas::drawTextDecorations(float x, float y, float length, const SkPaint& paint) { 39 uint32_t flags; 40 SkDrawFilter* drawFilter = getDrawFilter(); 41 if (drawFilter) { 42 SkPaint paintCopy(paint); 43 drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type); 44 flags = paintCopy.getFlags(); 45 } else { 46 flags = paint.getFlags(); 47 } 48 if (flags & (SkPaint::kUnderlineText_ReserveFlag | SkPaint::kStrikeThruText_ReserveFlag)) { 49 // Same values used by Skia 50 const float kStdStrikeThru_Offset = (-6.0f / 21.0f); 51 const float kStdUnderline_Offset = (1.0f / 9.0f); 52 const float kStdUnderline_Thickness = (1.0f / 18.0f); 53 54 SkScalar left = x; 55 SkScalar right = x + length; 56 float textSize = paint.getTextSize(); 57 float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f); 58 if (flags & SkPaint::kUnderlineText_ReserveFlag) { 59 SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth; 60 SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth; 61 drawRect(left, top, right, bottom, paint); 62 } 63 if (flags & SkPaint::kStrikeThruText_ReserveFlag) { 64 SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth; 65 SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth; 66 drawRect(left, top, right, bottom, paint); 67 } 68 } 69} 70 71static void simplifyPaint(int color, SkPaint* paint) { 72 paint->setColor(color); 73 paint->setShader(nullptr); 74 paint->setColorFilter(nullptr); 75 paint->setLooper(nullptr); 76 paint->setStrokeWidth(4 + 0.04 * paint->getTextSize()); 77 paint->setStrokeJoin(SkPaint::kRound_Join); 78 paint->setLooper(nullptr); 79} 80 81class DrawTextFunctor { 82public: 83 DrawTextFunctor(const minikin::Layout& layout, Canvas* canvas, uint16_t* glyphs, float* pos, 84 const SkPaint& paint, float x, float y, minikin::MinikinRect& bounds, 85 float totalAdvance) 86 : layout(layout) 87 , canvas(canvas) 88 , glyphs(glyphs) 89 , pos(pos) 90 , paint(paint) 91 , x(x) 92 , y(y) 93 , bounds(bounds) 94 , totalAdvance(totalAdvance) { 95 } 96 97 void operator()(size_t start, size_t end) { 98 if (canvas->drawTextAbsolutePos()) { 99 for (size_t i = start; i < end; i++) { 100 glyphs[i] = layout.getGlyphId(i); 101 pos[2 * i] = x + layout.getX(i); 102 pos[2 * i + 1] = y + layout.getY(i); 103 } 104 } else { 105 for (size_t i = start; i < end; i++) { 106 glyphs[i] = layout.getGlyphId(i); 107 pos[2 * i] = layout.getX(i); 108 pos[2 * i + 1] = layout.getY(i); 109 } 110 } 111 112 size_t glyphCount = end - start; 113 114 if (CC_UNLIKELY(canvas->isHighContrastText() && paint.getAlpha() != 0)) { 115 // high contrast draw path 116 int color = paint.getColor(); 117 int channelSum = SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color); 118 bool darken = channelSum < (128 * 3); 119 120 // outline 121 SkPaint outlinePaint(paint); 122 simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint); 123 outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style); 124 canvas->drawGlyphs(glyphs + start, pos + (2 * start), glyphCount, outlinePaint, x, y, 125 bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom, totalAdvance); 126 127 // inner 128 SkPaint innerPaint(paint); 129 simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, &innerPaint); 130 innerPaint.setStyle(SkPaint::kFill_Style); 131 canvas->drawGlyphs(glyphs + start, pos + (2 * start), glyphCount, innerPaint, x, y, 132 bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom, totalAdvance); 133 } else { 134 // standard draw path 135 canvas->drawGlyphs(glyphs + start, pos + (2 * start), glyphCount, paint, x, y, 136 bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom, totalAdvance); 137 } 138 } 139private: 140 const minikin::Layout& layout; 141 Canvas* canvas; 142 uint16_t* glyphs; 143 float* pos; 144 const SkPaint& paint; 145 float x; 146 float y; 147 minikin::MinikinRect& bounds; 148 float totalAdvance; 149}; 150 151void Canvas::drawText(const uint16_t* text, int start, int count, int contextCount, 152 float x, float y, int bidiFlags, const Paint& origPaint, Typeface* typeface) { 153 // minikin may modify the original paint 154 Paint paint(origPaint); 155 156 minikin::Layout layout = MinikinUtils::doLayout( 157 &paint, bidiFlags, typeface, text, start, count, contextCount); 158 159 size_t nGlyphs = layout.nGlyphs(); 160 std::unique_ptr<uint16_t[]> glyphs(new uint16_t[nGlyphs]); 161 std::unique_ptr<float[]> pos(new float[nGlyphs * 2]); 162 163 x += MinikinUtils::xOffsetForTextAlign(&paint, layout); 164 165 minikin::MinikinRect bounds; 166 layout.getBounds(&bounds); 167 if (!drawTextAbsolutePos()) { 168 bounds.offset(x, y); 169 } 170 171 // Set align to left for drawing, as we don't want individual 172 // glyphs centered or right-aligned; the offset above takes 173 // care of all alignment. 174 paint.setTextAlign(Paint::kLeft_Align); 175 176 DrawTextFunctor f(layout, this, glyphs.get(), pos.get(), 177 paint, x, y, bounds, layout.getAdvance()); 178 MinikinUtils::forFontRun(layout, &paint, f); 179} 180 181class DrawTextOnPathFunctor { 182public: 183 DrawTextOnPathFunctor(const minikin::Layout& layout, Canvas* canvas, float hOffset, 184 float vOffset, const Paint& paint, const SkPath& path) 185 : layout(layout) 186 , canvas(canvas) 187 , hOffset(hOffset) 188 , vOffset(vOffset) 189 , paint(paint) 190 , path(path) { 191 } 192 193 void operator()(size_t start, size_t end) { 194 canvas->drawLayoutOnPath(layout, hOffset, vOffset, paint, path, start, end); 195 } 196private: 197 const minikin::Layout& layout; 198 Canvas* canvas; 199 float hOffset; 200 float vOffset; 201 const Paint& paint; 202 const SkPath& path; 203}; 204 205void Canvas::drawTextOnPath(const uint16_t* text, int count, int bidiFlags, const SkPath& path, 206 float hOffset, float vOffset, const Paint& paint, Typeface* typeface) { 207 Paint paintCopy(paint); 208 minikin::Layout layout = MinikinUtils::doLayout( 209 &paintCopy, bidiFlags, typeface, text, 0, count, count); 210 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