Home | History | Annotate | Download | only in hwui
      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