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