Canvas.cpp revision 79abbf22d4f672208327546661e694d837f564a9
125bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi/* 225bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi * Copyright (C) 2015 The Android Open Source Project 325bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi * 425bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi * Licensed under the Apache License, Version 2.0 (the "License"); 525bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi * you may not use this file except in compliance with the License. 625bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi * You may obtain a copy of the License at 725bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi * 825bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi * http://www.apache.org/licenses/LICENSE-2.0 925bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi * 1025bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi * Unless required by applicable law or agreed to in writing, software 1125bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi * distributed under the License is distributed on an "AS IS" BASIS, 1225bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1325bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi * See the License for the specific language governing permissions and 1425bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi * limitations under the License. 1525bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi */ 1625bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi 1725bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi#include "Canvas.h" 1825bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi 1925bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi#include "DisplayListCanvas.h" 2025bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi#include "RecordingCanvas.h" 2125bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi#include "MinikinUtils.h" 225df9d729f6c78215f287701d7e136dfc922e2bd3Hugo Benichi#include "Paint.h" 235df9d729f6c78215f287701d7e136dfc922e2bd3Hugo Benichi#include "Typeface.h" 245df9d729f6c78215f287701d7e136dfc922e2bd3Hugo Benichi 2525bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi#include <SkDrawFilter.h> 2625bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi 2725bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichinamespace android { 2825bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi 2925bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo BenichiCanvas* Canvas::create_recording_canvas(int width, int height) { 3025bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi#if HWUI_NEW_OPS 3125bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi return new uirenderer::RecordingCanvas(width, height); 3225bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi#else 3325bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi return new uirenderer::DisplayListCanvas(width, height); 3425bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi#endif 3525bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi} 3625bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi 3725bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichivoid Canvas::drawTextDecorations(float x, float y, float length, const SkPaint& paint) { 3825bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi uint32_t flags; 3925bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi SkDrawFilter* drawFilter = getDrawFilter(); 4025bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi if (drawFilter) { 4125bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi SkPaint paintCopy(paint); 4225bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type); 4325bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi flags = paintCopy.getFlags(); 4425bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi } else { 4525bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi flags = paint.getFlags(); 4625bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi } 4725bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) { 4825bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi // Same values used by Skia 4925bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi const float kStdStrikeThru_Offset = (-6.0f / 21.0f); 5025bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi const float kStdUnderline_Offset = (1.0f / 9.0f); 5125bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi const float kStdUnderline_Thickness = (1.0f / 18.0f); 5225bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi 5325bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi SkScalar left = x; 5425bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi SkScalar right = x + length; 5525bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi float textSize = paint.getTextSize(); 5625bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f); 5725bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi if (flags & SkPaint::kUnderlineText_Flag) { 5825bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth; 5925bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth; 6025bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi drawRect(left, top, right, bottom, paint); 6125bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi } 6225bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi if (flags & SkPaint::kStrikeThruText_Flag) { 6325bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth; 6425bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth; 6525bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi drawRect(left, top, right, bottom, paint); 6625bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi } 6725bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi } 6825bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi} 6925bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi 7025bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichistatic void simplifyPaint(int color, SkPaint* paint) { 7125bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi paint->setColor(color); 7225bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi paint->setShader(nullptr); 7325bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi paint->setColorFilter(nullptr); 7425bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi paint->setLooper(nullptr); 7561cbccc2bf7983b50e7a7f1fdb1858caeab6fd96Hugo Benichi paint->setStrokeWidth(4 + 0.04 * paint->getTextSize()); 7625bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi paint->setStrokeJoin(SkPaint::kRound_Join); 7725bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi paint->setLooper(nullptr); 7825bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi} 7961cbccc2bf7983b50e7a7f1fdb1858caeab6fd96Hugo Benichi 8025bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichiclass DrawTextFunctor { 8125bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichipublic: 8225bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi DrawTextFunctor(const Layout& layout, Canvas* canvas, uint16_t* glyphs, float* pos, 8361cbccc2bf7983b50e7a7f1fdb1858caeab6fd96Hugo Benichi const SkPaint& paint, float x, float y, MinikinRect& bounds, float totalAdvance) 8425bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi : layout(layout) 855df9d729f6c78215f287701d7e136dfc922e2bd3Hugo Benichi , canvas(canvas) 865df9d729f6c78215f287701d7e136dfc922e2bd3Hugo Benichi , glyphs(glyphs) 875df9d729f6c78215f287701d7e136dfc922e2bd3Hugo Benichi , pos(pos) 885df9d729f6c78215f287701d7e136dfc922e2bd3Hugo Benichi , paint(paint) 895df9d729f6c78215f287701d7e136dfc922e2bd3Hugo Benichi , x(x) 905df9d729f6c78215f287701d7e136dfc922e2bd3Hugo Benichi , y(y) 915df9d729f6c78215f287701d7e136dfc922e2bd3Hugo Benichi , bounds(bounds) 925df9d729f6c78215f287701d7e136dfc922e2bd3Hugo Benichi , totalAdvance(totalAdvance) { 935df9d729f6c78215f287701d7e136dfc922e2bd3Hugo Benichi } 945df9d729f6c78215f287701d7e136dfc922e2bd3Hugo Benichi 955df9d729f6c78215f287701d7e136dfc922e2bd3Hugo Benichi void operator()(size_t start, size_t end) { 965df9d729f6c78215f287701d7e136dfc922e2bd3Hugo Benichi if (canvas->drawTextAbsolutePos()) { 9725bf8f5d5678e747933ae9e4051daa99ddfcfaa3Hugo Benichi for (size_t i = start; i < end; i++) { 98 glyphs[i] = layout.getGlyphId(i); 99 pos[2 * i] = x + layout.getX(i); 100 pos[2 * i + 1] = y + layout.getY(i); 101 } 102 } else { 103 for (size_t i = start; i < end; i++) { 104 glyphs[i] = layout.getGlyphId(i); 105 pos[2 * i] = layout.getX(i); 106 pos[2 * i + 1] = layout.getY(i); 107 } 108 } 109 110 size_t glyphCount = end - start; 111 112 if (CC_UNLIKELY(canvas->isHighContrastText() && paint.getAlpha() != 0)) { 113 // high contrast draw path 114 int color = paint.getColor(); 115 int channelSum = SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color); 116 bool darken = channelSum < (128 * 3); 117 118 // outline 119 SkPaint outlinePaint(paint); 120 simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint); 121 outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style); 122 canvas->drawGlyphs(glyphs + start, pos + (2 * start), glyphCount, outlinePaint, x, y, 123 bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom, totalAdvance); 124 125 // inner 126 SkPaint innerPaint(paint); 127 simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, &innerPaint); 128 innerPaint.setStyle(SkPaint::kFill_Style); 129 canvas->drawGlyphs(glyphs + start, pos + (2 * start), glyphCount, innerPaint, x, y, 130 bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom, totalAdvance); 131 } else { 132 // standard draw path 133 canvas->drawGlyphs(glyphs + start, pos + (2 * start), glyphCount, paint, x, y, 134 bounds.mLeft, bounds.mTop, bounds.mRight, bounds.mBottom, totalAdvance); 135 } 136 } 137private: 138 const Layout& layout; 139 Canvas* canvas; 140 uint16_t* glyphs; 141 float* pos; 142 const SkPaint& paint; 143 float x; 144 float y; 145 MinikinRect& bounds; 146 float totalAdvance; 147}; 148 149void Canvas::drawText(const uint16_t* text, int start, int count, int contextCount, 150 float x, float y, int bidiFlags, const Paint& origPaint, Typeface* typeface) { 151 // minikin may modify the original paint 152 Paint paint(origPaint); 153 154 Layout layout; 155 MinikinUtils::doLayout(&layout, &paint, bidiFlags, typeface, text, start, count, contextCount); 156 157 size_t nGlyphs = layout.nGlyphs(); 158 std::unique_ptr<uint16_t[]> glyphs(new uint16_t[nGlyphs]); 159 std::unique_ptr<float[]> pos(new float[nGlyphs * 2]); 160 161 x += MinikinUtils::xOffsetForTextAlign(&paint, layout); 162 163 MinikinRect bounds; 164 layout.getBounds(&bounds); 165 if (!drawTextAbsolutePos()) { 166 bounds.offset(x, y); 167 } 168 169 // Set align to left for drawing, as we don't want individual 170 // glyphs centered or right-aligned; the offset above takes 171 // care of all alignment. 172 paint.setTextAlign(Paint::kLeft_Align); 173 174 DrawTextFunctor f(layout, this, glyphs.get(), pos.get(), 175 paint, x, y, bounds, layout.getAdvance()); 176 MinikinUtils::forFontRun(layout, &paint, f); 177} 178 179class DrawTextOnPathFunctor { 180public: 181 DrawTextOnPathFunctor(const Layout& layout, Canvas* canvas, float hOffset, 182 float vOffset, const Paint& paint, const SkPath& path) 183 : layout(layout) 184 , canvas(canvas) 185 , hOffset(hOffset) 186 , vOffset(vOffset) 187 , paint(paint) 188 , path(path) { 189 } 190 191 void operator()(size_t start, size_t end) { 192 uint16_t glyphs[1]; 193 for (size_t i = start; i < end; i++) { 194 glyphs[0] = layout.getGlyphId(i); 195 float x = hOffset + layout.getX(i); 196 float y = vOffset + layout.getY(i); 197 canvas->drawGlyphsOnPath(glyphs, 1, path, x, y, paint); 198 } 199 } 200private: 201 const Layout& layout; 202 Canvas* canvas; 203 float hOffset; 204 float vOffset; 205 const Paint& paint; 206 const SkPath& path; 207}; 208 209void Canvas::drawTextOnPath(const uint16_t* text, int count, int bidiFlags, const SkPath& path, 210 float hOffset, float vOffset, const Paint& paint, Typeface* typeface) { 211 Paint paintCopy(paint); 212 Layout layout; 213 MinikinUtils::doLayout(&layout, &paintCopy, bidiFlags, typeface, text, 0, count, count); 214 hOffset += MinikinUtils::hOffsetForTextAlign(&paintCopy, layout, path); 215 216 // Set align to left for drawing, as we don't want individual 217 // glyphs centered or right-aligned; the offset above takes 218 // care of all alignment. 219 paintCopy.setTextAlign(Paint::kLeft_Align); 220 221 DrawTextOnPathFunctor f(layout, this, hOffset, vOffset, paintCopy, path); 222 MinikinUtils::forFontRun(layout, &paintCopy, f); 223} 224 225} // namespace android 226