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 "DisplayListCanvas.h"
     20 #include "RecordingCanvas.h"
     21 #include "MinikinUtils.h"
     22 #include "Paint.h"
     23 #include "Typeface.h"
     24 
     25 #include <SkDrawFilter.h>
     26 
     27 namespace android {
     28 
     29 Canvas* 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 
     37 void 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 
     70 static 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 
     80 class DrawTextFunctor {
     81 public:
     82     DrawTextFunctor(const Layout& layout, Canvas* canvas, uint16_t* glyphs, float* pos,
     83             const SkPaint& paint, float x, float y, MinikinRect& bounds, float totalAdvance)
     84         : layout(layout)
     85         , canvas(canvas)
     86         , glyphs(glyphs)
     87         , pos(pos)
     88         , paint(paint)
     89         , x(x)
     90         , y(y)
     91         , bounds(bounds)
     92         , totalAdvance(totalAdvance) {
     93     }
     94 
     95     void operator()(size_t start, size_t end) {
     96         if (canvas->drawTextAbsolutePos()) {
     97             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     }
    137 private:
    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 
    149 void 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 
    179 class DrawTextOnPathFunctor {
    180 public:
    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     }
    200 private:
    201     const Layout& layout;
    202     Canvas* canvas;
    203     float hOffset;
    204     float vOffset;
    205     const Paint& paint;
    206     const SkPath& path;
    207 };
    208 
    209 void 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