Canvas.cpp revision c0e7a90f1f5f98e85dbeda021fac0dff79725933
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 "Typeface.h" 24 25#include <SkDrawFilter.h> 26 27namespace android { 28 29Canvas* Canvas::create_recording_canvas(int width, int height, uirenderer::RenderNode* renderNode) { 30 return new uirenderer::RecordingCanvas(width, height); 31} 32 33void Canvas::drawTextDecorations(float x, float y, float length, const SkPaint& paint) { 34 uint32_t flags; 35 SkDrawFilter* drawFilter = getDrawFilter(); 36 if (drawFilter) { 37 SkPaint paintCopy(paint); 38 drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type); 39 flags = paintCopy.getFlags(); 40 } else { 41 flags = paint.getFlags(); 42 } 43 if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) { 44 // Same values used by Skia 45 const float kStdStrikeThru_Offset = (-6.0f / 21.0f); 46 const float kStdUnderline_Offset = (1.0f / 9.0f); 47 const float kStdUnderline_Thickness = (1.0f / 18.0f); 48 49 SkScalar left = x; 50 SkScalar right = x + length; 51 float textSize = paint.getTextSize(); 52 float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f); 53 if (flags & SkPaint::kUnderlineText_Flag) { 54 SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth; 55 SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth; 56 drawRect(left, top, right, bottom, paint); 57 } 58 if (flags & SkPaint::kStrikeThruText_Flag) { 59 SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth; 60 SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth; 61 drawRect(left, top, right, bottom, paint); 62 } 63 } 64} 65 66static void simplifyPaint(int color, SkPaint* paint) { 67 paint->setColor(color); 68 paint->setShader(nullptr); 69 paint->setColorFilter(nullptr); 70 paint->setLooper(nullptr); 71 paint->setStrokeWidth(4 + 0.04 * paint->getTextSize()); 72 paint->setStrokeJoin(SkPaint::kRound_Join); 73 paint->setLooper(nullptr); 74} 75 76class DrawTextFunctor { 77public: 78 DrawTextFunctor(const minikin::Layout& layout, Canvas* canvas, uint16_t* glyphs, float* pos, 79 const SkPaint& paint, float x, float y, minikin::MinikinRect& bounds, 80 float totalAdvance) 81 : layout(layout) 82 , canvas(canvas) 83 , glyphs(glyphs) 84 , pos(pos) 85 , paint(paint) 86 , x(x) 87 , y(y) 88 , bounds(bounds) 89 , totalAdvance(totalAdvance) { 90 } 91 92 void operator()(size_t start, size_t end) { 93 if (canvas->drawTextAbsolutePos()) { 94 for (size_t i = start; i < end; i++) { 95 glyphs[i] = layout.getGlyphId(i); 96 pos[2 * i] = x + layout.getX(i); 97 pos[2 * i + 1] = y + layout.getY(i); 98 } 99 } else { 100 for (size_t i = start; i < end; i++) { 101 glyphs[i] = layout.getGlyphId(i); 102 pos[2 * i] = layout.getX(i); 103 pos[2 * i + 1] = layout.getY(i); 104 } 105 } 106 107 size_t glyphCount = end - start; 108 109 if (CC_UNLIKELY(canvas->isHighContrastText() && paint.getAlpha() != 0)) { 110 // high contrast draw path 111 int color = paint.getColor(); 112 int channelSum = SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color); 113 bool darken = channelSum < (128 * 3); 114 115 // outline 116 SkPaint outlinePaint(paint); 117 simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint); 118 outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style); 119 canvas->drawGlyphs(glyphs + start, pos + (2 * start), glyphCount, outlinePaint, x, y, 120 bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom, totalAdvance); 121 122 // inner 123 SkPaint innerPaint(paint); 124 simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, &innerPaint); 125 innerPaint.setStyle(SkPaint::kFill_Style); 126 canvas->drawGlyphs(glyphs + start, pos + (2 * start), glyphCount, innerPaint, x, y, 127 bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom, totalAdvance); 128 } else { 129 // standard draw path 130 canvas->drawGlyphs(glyphs + start, pos + (2 * start), glyphCount, paint, x, y, 131 bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom, totalAdvance); 132 } 133 } 134private: 135 const minikin::Layout& layout; 136 Canvas* canvas; 137 uint16_t* glyphs; 138 float* pos; 139 const SkPaint& paint; 140 float x; 141 float y; 142 minikin::MinikinRect& bounds; 143 float totalAdvance; 144}; 145 146void Canvas::drawText(const uint16_t* text, int start, int count, int contextCount, 147 float x, float y, int bidiFlags, const Paint& origPaint, Typeface* typeface) { 148 // minikin may modify the original paint 149 Paint paint(origPaint); 150 151 minikin::Layout layout; 152 MinikinUtils::doLayout(&layout, &paint, bidiFlags, typeface, text, start, count, contextCount); 153 154 size_t nGlyphs = layout.nGlyphs(); 155 std::unique_ptr<uint16_t[]> glyphs(new uint16_t[nGlyphs]); 156 std::unique_ptr<float[]> pos(new float[nGlyphs * 2]); 157 158 x += MinikinUtils::xOffsetForTextAlign(&paint, layout); 159 160 minikin::MinikinRect bounds; 161 layout.getBounds(&bounds); 162 if (!drawTextAbsolutePos()) { 163 bounds.offset(x, y); 164 } 165 166 // Set align to left for drawing, as we don't want individual 167 // glyphs centered or right-aligned; the offset above takes 168 // care of all alignment. 169 paint.setTextAlign(Paint::kLeft_Align); 170 171 DrawTextFunctor f(layout, this, glyphs.get(), pos.get(), 172 paint, x, y, bounds, layout.getAdvance()); 173 MinikinUtils::forFontRun(layout, &paint, f); 174} 175 176class DrawTextOnPathFunctor { 177public: 178 DrawTextOnPathFunctor(const minikin::Layout& layout, Canvas* canvas, float hOffset, 179 float vOffset, const Paint& paint, const SkPath& path) 180 : layout(layout) 181 , canvas(canvas) 182 , hOffset(hOffset) 183 , vOffset(vOffset) 184 , paint(paint) 185 , path(path) { 186 } 187 188 void operator()(size_t start, size_t end) { 189 canvas->drawLayoutOnPath(layout, hOffset, vOffset, paint, path, start, end); 190 } 191private: 192 const minikin::Layout& layout; 193 Canvas* canvas; 194 float hOffset; 195 float vOffset; 196 const Paint& paint; 197 const SkPath& path; 198}; 199 200void Canvas::drawTextOnPath(const uint16_t* text, int count, int bidiFlags, const SkPath& path, 201 float hOffset, float vOffset, const Paint& paint, Typeface* typeface) { 202 Paint paintCopy(paint); 203 minikin::Layout layout; 204 MinikinUtils::doLayout(&layout, &paintCopy, bidiFlags, typeface, text, 0, count, count); 205 hOffset += MinikinUtils::hOffsetForTextAlign(&paintCopy, layout, path); 206 207 // Set align to left for drawing, as we don't want individual 208 // glyphs centered or right-aligned; the offset above takes 209 // care of all alignment. 210 paintCopy.setTextAlign(Paint::kLeft_Align); 211 212 DrawTextOnPathFunctor f(layout, this, hOffset, vOffset, paintCopy, path); 213 MinikinUtils::forFontRun(layout, &paintCopy, f); 214} 215 216} // namespace android 217