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