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 "SkiaCanvasProxy.h"
     18 
     19 #include <memory>
     20 
     21 #include <log/log.h>
     22 
     23 #include "hwui/Bitmap.h"
     24 #include <SkLatticeIter.h>
     25 #include <SkPatchUtils.h>
     26 #include <SkPaint.h>
     27 #include <SkPath.h>
     28 #include <SkPixelRef.h>
     29 #include <SkRect.h>
     30 #include <SkRRect.h>
     31 #include <SkRSXform.h>
     32 #include <SkSurface.h>
     33 #include <SkTextBlobRunIterator.h>
     34 #include <SkVertices.h>
     35 
     36 namespace android {
     37 namespace uirenderer {
     38 
     39 SkiaCanvasProxy::SkiaCanvasProxy(Canvas* canvas, bool filterHwuiCalls)
     40         : INHERITED(canvas->width(), canvas->height())
     41         , mCanvas(canvas)
     42         , mFilterHwuiCalls(filterHwuiCalls) {}
     43 
     44 void SkiaCanvasProxy::onDrawPaint(const SkPaint& paint) {
     45     mCanvas->drawPaint(paint);
     46 }
     47 
     48 void SkiaCanvasProxy::onDrawPoints(PointMode pointMode, size_t count, const SkPoint pts[],
     49         const SkPaint& paint) {
     50     if (!pts || count == 0) {
     51         return;
     52     }
     53 
     54     // convert the SkPoints into floats
     55     static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats");
     56     const size_t floatCount = count << 1;
     57     const float* floatArray = &pts[0].fX;
     58 
     59     switch (pointMode) {
     60         case kPoints_PointMode: {
     61             mCanvas->drawPoints(floatArray, floatCount, paint);
     62             break;
     63         }
     64         case kLines_PointMode: {
     65             mCanvas->drawLines(floatArray, floatCount, paint);
     66             break;
     67         }
     68         case kPolygon_PointMode: {
     69             SkPaint strokedPaint(paint);
     70             strokedPaint.setStyle(SkPaint::kStroke_Style);
     71 
     72             SkPath path;
     73             for (size_t i = 0; i < count - 1; i++) {
     74                 path.moveTo(pts[i]);
     75                 path.lineTo(pts[i+1]);
     76                 this->drawPath(path, strokedPaint);
     77                 path.rewind();
     78             }
     79             break;
     80         }
     81         default:
     82             LOG_ALWAYS_FATAL("Unknown point type");
     83     }
     84 }
     85 
     86 void SkiaCanvasProxy::onDrawOval(const SkRect& rect, const SkPaint& paint) {
     87     mCanvas->drawOval(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint);
     88 }
     89 
     90 void SkiaCanvasProxy::onDrawRect(const SkRect& rect, const SkPaint& paint) {
     91     mCanvas->drawRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint);
     92 }
     93 
     94 void SkiaCanvasProxy::onDrawRRect(const SkRRect& roundRect, const SkPaint& paint) {
     95     if (!roundRect.isComplex()) {
     96         const SkRect& rect = roundRect.rect();
     97         SkVector radii = roundRect.getSimpleRadii();
     98         mCanvas->drawRoundRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom,
     99                                radii.fX, radii.fY, paint);
    100     } else {
    101         SkPath path;
    102         path.addRRect(roundRect);
    103         mCanvas->drawPath(path, paint);
    104     }
    105 }
    106 
    107 void SkiaCanvasProxy::onDrawArc(const SkRect& rect, SkScalar startAngle, SkScalar sweepAngle,
    108                                 bool useCenter, const SkPaint& paint) {
    109     mCanvas->drawArc(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom,
    110                      startAngle, sweepAngle, useCenter, paint);
    111 }
    112 
    113 void SkiaCanvasProxy::onDrawPath(const SkPath& path, const SkPaint& paint) {
    114     mCanvas->drawPath(path, paint);
    115 }
    116 
    117 void SkiaCanvasProxy::onDrawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
    118         const SkPaint* paint) {
    119     sk_sp<Bitmap> hwuiBitmap = Bitmap::createFrom(bitmap.info(), *bitmap.pixelRef());
    120     // HWUI doesn't support extractSubset(), so convert any subsetted bitmap into
    121     // a drawBitmapRect(); pass through an un-subsetted bitmap.
    122     if (hwuiBitmap && bitmap.dimensions() != hwuiBitmap->info().dimensions()) {
    123         SkIPoint origin = bitmap.pixelRefOrigin();
    124         mCanvas->drawBitmap(*hwuiBitmap, origin.fX, origin.fY,
    125                             origin.fX + bitmap.dimensions().width(),
    126                             origin.fY + bitmap.dimensions().height(),
    127                             left, top,
    128                             left + bitmap.dimensions().width(),
    129                             top + bitmap.dimensions().height(),
    130                             paint);
    131     } else {
    132         mCanvas->drawBitmap(*hwuiBitmap, left, top, paint);
    133     }
    134 }
    135 
    136 void SkiaCanvasProxy::onDrawBitmapRect(const SkBitmap& skBitmap, const SkRect* srcPtr,
    137         const SkRect& dst, const SkPaint* paint, SrcRectConstraint) {
    138     SkRect src = (srcPtr) ? *srcPtr : SkRect::MakeWH(skBitmap.width(), skBitmap.height());
    139     // TODO: if bitmap is a subset, do we need to add pixelRefOrigin to src?
    140    Bitmap* bitmap = reinterpret_cast<Bitmap*>(skBitmap.pixelRef());
    141    mCanvas->drawBitmap(*bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom,
    142                         dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint);
    143 }
    144 
    145 void SkiaCanvasProxy::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
    146         const SkRect& dst, const SkPaint*) {
    147     //TODO make nine-patch drawing a method on Canvas.h
    148     SkDEBUGFAIL("SkiaCanvasProxy::onDrawBitmapNine is not yet supported");
    149 }
    150 
    151 void SkiaCanvasProxy::onDrawImage(const SkImage* image, SkScalar left, SkScalar top,
    152         const SkPaint* paint) {
    153     SkBitmap skiaBitmap;
    154     if (image->asLegacyBitmap(&skiaBitmap, SkImage::kRO_LegacyBitmapMode)) {
    155         onDrawBitmap(skiaBitmap, left, top, paint);
    156     }
    157 }
    158 
    159 void SkiaCanvasProxy::onDrawImageRect(const SkImage* image, const SkRect* srcPtr, const SkRect& dst,
    160         const SkPaint* paint, SrcRectConstraint constraint) {
    161     SkBitmap skiaBitmap;
    162     if (image->asLegacyBitmap(&skiaBitmap, SkImage::kRO_LegacyBitmapMode)) {
    163         sk_sp<Bitmap> bitmap = Bitmap::createFrom(skiaBitmap.info(), *skiaBitmap.pixelRef());
    164         SkRect src = (srcPtr) ? *srcPtr : SkRect::MakeWH(image->width(), image->height());
    165         mCanvas->drawBitmap(*bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom,
    166                 dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint);
    167     }
    168 }
    169 
    170 void SkiaCanvasProxy::onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst,
    171         const SkPaint*) {
    172     SkDEBUGFAIL("SkiaCanvasProxy::onDrawImageNine is not yet supported");
    173 }
    174 
    175 void SkiaCanvasProxy::onDrawImageLattice(const SkImage* image, const Lattice& lattice,
    176         const SkRect& dst, const SkPaint* paint) {
    177     SkLatticeIter iter(lattice, dst);
    178     SkRect srcR, dstR;
    179     while (iter.next(&srcR, &dstR)) {
    180         onDrawImageRect(image, &srcR, dstR, paint, SkCanvas::kStrict_SrcRectConstraint);
    181     }
    182 }
    183 
    184 void SkiaCanvasProxy::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode,
    185         const SkPaint& paint) {
    186     // TODO: should we pass through blendmode
    187     if (mFilterHwuiCalls) {
    188         return;
    189     }
    190     // convert the SkPoints into floats
    191     static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats");
    192     const int floatCount = vertices->vertexCount() << 1;
    193     const float* vArray = (const float*)vertices->positions();
    194     const float* tArray = (const float*)vertices->texCoords();
    195     const int* cArray = (const int*)vertices->colors();
    196     mCanvas->drawVertices(vertices->mode(), floatCount, vArray, tArray, cArray,
    197             vertices->indices(), vertices->indexCount(), paint);
    198 }
    199 
    200 sk_sp<SkSurface> SkiaCanvasProxy::onNewSurface(const SkImageInfo&, const SkSurfaceProps&) {
    201     SkDEBUGFAIL("SkiaCanvasProxy::onNewSurface is not supported");
    202     return NULL;
    203 }
    204 
    205 void SkiaCanvasProxy::willSave() {
    206     mCanvas->save(android::SaveFlags::MatrixClip);
    207 }
    208 
    209 static inline SaveFlags::Flags saveFlags(SkCanvas::SaveLayerFlags layerFlags) {
    210     SaveFlags::Flags saveFlags = 0;
    211 
    212     if (!(layerFlags & SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag)) {
    213         saveFlags |= SaveFlags::ClipToLayer;
    214     }
    215 
    216     if (!(layerFlags & SkCanvas::kIsOpaque_SaveLayerFlag)) {
    217         saveFlags |= SaveFlags::HasAlphaLayer;
    218     }
    219 
    220     return saveFlags;
    221 }
    222 
    223 SkCanvas::SaveLayerStrategy SkiaCanvasProxy::getSaveLayerStrategy(const SaveLayerRec& saveLayerRec) {
    224     SkRect rect;
    225     if (saveLayerRec.fBounds) {
    226         rect = *saveLayerRec.fBounds;
    227     } else if (!mCanvas->getClipBounds(&rect)) {
    228         rect = SkRect::MakeEmpty();
    229     }
    230     mCanvas->saveLayer(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, saveLayerRec.fPaint,
    231                        saveFlags(saveLayerRec.fSaveLayerFlags));
    232     return SkCanvas::kNoLayer_SaveLayerStrategy;
    233 }
    234 
    235 void SkiaCanvasProxy::willRestore() {
    236     mCanvas->restore();
    237 }
    238 
    239 void SkiaCanvasProxy::didConcat(const SkMatrix& matrix) {
    240     mCanvas->concat(matrix);
    241 }
    242 
    243 void SkiaCanvasProxy::didSetMatrix(const SkMatrix& matrix) {
    244     mCanvas->setMatrix(matrix);
    245 }
    246 
    247 void SkiaCanvasProxy::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
    248         const SkPaint& paint) {
    249     SkPath path;
    250     path.addRRect(outer);
    251     path.addRRect(inner);
    252     path.setFillType(SkPath::kEvenOdd_FillType);
    253     this->drawPath(path, paint);
    254 }
    255 
    256 /**
    257  * Utility class that converts the incoming text & paint from the given encoding
    258  * into glyphIDs.
    259  */
    260 class GlyphIDConverter {
    261 public:
    262     GlyphIDConverter(const void* text, size_t byteLength, const SkPaint& origPaint) {
    263         paint = origPaint;
    264         if (paint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding) {
    265             glyphIDs = (uint16_t*)text;
    266             count = byteLength >> 1;
    267         } else {
    268              // ensure space for one glyph per ID given UTF8 encoding.
    269             storage.reset(new uint16_t[byteLength]);
    270             glyphIDs = storage.get();
    271             count = paint.textToGlyphs(text, byteLength, storage.get());
    272             paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
    273         }
    274     }
    275 
    276     SkPaint paint;
    277     uint16_t* glyphIDs;
    278     int count;
    279 private:
    280     std::unique_ptr<uint16_t[]> storage;
    281 };
    282 
    283 void SkiaCanvasProxy::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
    284         const SkPaint& origPaint) {
    285     // convert to glyphIDs if necessary
    286     GlyphIDConverter glyphs(text, byteLength, origPaint);
    287 
    288     // compute the glyph positions
    289     std::unique_ptr<SkPoint[]> pointStorage(new SkPoint[glyphs.count]);
    290     std::unique_ptr<SkScalar[]> glyphWidths(new SkScalar[glyphs.count]);
    291     glyphs.paint.getTextWidths(glyphs.glyphIDs, glyphs.count << 1, glyphWidths.get());
    292 
    293     // compute conservative bounds
    294     // NOTE: We could call the faster paint.getFontBounds for a less accurate,
    295     //       but even more conservative bounds if this  is too slow.
    296     SkRect bounds;
    297     glyphs.paint.measureText(glyphs.glyphIDs, glyphs.count << 1, &bounds);
    298 
    299     // adjust for non-left alignment
    300     if (glyphs.paint.getTextAlign() != SkPaint::kLeft_Align) {
    301         SkScalar stop = 0;
    302         for (int i = 0; i < glyphs.count; i++) {
    303             stop += glyphWidths[i];
    304         }
    305         if (glyphs.paint.getTextAlign() == SkPaint::kCenter_Align) {
    306             stop = SkScalarHalf(stop);
    307         }
    308         if (glyphs.paint.isVerticalText()) {
    309             y -= stop;
    310         } else {
    311             x -= stop;
    312         }
    313     }
    314 
    315     // setup the first glyph position and adjust bounds if needed
    316     int xBaseline = 0;
    317     int yBaseline = 0;
    318     if (mCanvas->drawTextAbsolutePos()) {
    319         bounds.offset(x,y);
    320         xBaseline = x;
    321         yBaseline = y;
    322     }
    323     pointStorage[0].set(xBaseline, yBaseline);
    324 
    325     // setup the remaining glyph positions
    326     if (glyphs.paint.isVerticalText()) {
    327         for (int i = 1; i < glyphs.count; i++) {
    328             pointStorage[i].set(xBaseline, glyphWidths[i-1] + pointStorage[i-1].fY);
    329         }
    330     } else {
    331         for (int i = 1; i < glyphs.count; i++) {
    332             pointStorage[i].set(glyphWidths[i-1] + pointStorage[i-1].fX, yBaseline);
    333         }
    334     }
    335 
    336     static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats");
    337     mCanvas->drawGlyphs(glyphs.glyphIDs, &pointStorage[0].fX, glyphs.count, glyphs.paint,
    338                       x, y, bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, 0);
    339 }
    340 
    341 void SkiaCanvasProxy::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
    342         const SkPaint& origPaint) {
    343     // convert to glyphIDs if necessary
    344     GlyphIDConverter glyphs(text, byteLength, origPaint);
    345 
    346     // convert to relative positions if necessary
    347     int x, y;
    348     const SkPoint* posArray;
    349     std::unique_ptr<SkPoint[]> pointStorage;
    350     if (mCanvas->drawTextAbsolutePos()) {
    351         x = 0;
    352         y = 0;
    353         posArray = pos;
    354     } else {
    355         x = pos[0].fX;
    356         y = pos[0].fY;
    357         pointStorage.reset(new SkPoint[glyphs.count]);
    358         for (int i = 0; i < glyphs.count; i++) {
    359             pointStorage[i].fX = pos[i].fX - x;
    360             pointStorage[i].fY = pos[i].fY - y;
    361         }
    362         posArray = pointStorage.get();
    363     }
    364 
    365     // Compute conservative bounds.  If the content has already been processed
    366     // by Minikin then it had already computed these bounds.  Unfortunately,
    367     // there is no way to capture those bounds as part of the Skia drawPosText
    368     // API so we need to do that computation again here.
    369     SkRect bounds = SkRect::MakeEmpty();
    370     for (int i = 0; i < glyphs.count; i++) {
    371         SkRect glyphBounds = SkRect::MakeEmpty();
    372         glyphs.paint.measureText(&glyphs.glyphIDs[i], sizeof(uint16_t), &glyphBounds);
    373         glyphBounds.offset(pos[i].fX, pos[i].fY);
    374         bounds.join(glyphBounds);
    375     }
    376 
    377     static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats");
    378     mCanvas->drawGlyphs(glyphs.glyphIDs, &posArray[0].fX, glyphs.count, glyphs.paint, x, y,
    379                       bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, 0);
    380 }
    381 
    382 void SkiaCanvasProxy::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
    383         SkScalar constY, const SkPaint& paint) {
    384     const size_t pointCount = byteLength >> 1;
    385     std::unique_ptr<SkPoint[]> pts(new SkPoint[pointCount]);
    386     for (size_t i = 0; i < pointCount; i++) {
    387         pts[i].set(xpos[i], constY);
    388     }
    389     this->onDrawPosText(text, byteLength, pts.get(), paint);
    390 }
    391 
    392 void SkiaCanvasProxy::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
    393         const SkMatrix* matrix, const SkPaint& origPaint) {
    394     SkDEBUGFAIL("SkiaCanvasProxy::onDrawTextOnPath is not supported");
    395 }
    396 
    397 void SkiaCanvasProxy::onDrawTextRSXform(const void* text, size_t byteLength,
    398         const SkRSXform xform[], const SkRect* cullRect, const SkPaint& paint) {
    399     GlyphIDConverter glyphs(text, byteLength, paint); // Just get count
    400     SkMatrix localM, currM, origM;
    401     mCanvas->getMatrix(&currM);
    402     origM = currM;
    403     for (int i = 0; i < glyphs.count; i++) {
    404         localM.setRSXform(*xform++);
    405         currM.setConcat(origM, localM);
    406         mCanvas->setMatrix(currM);
    407         this->onDrawText((char*)text + (byteLength / glyphs.count * i),
    408                          byteLength / glyphs.count, 0, 0, paint);
    409     }
    410     mCanvas->setMatrix(origM);
    411 }
    412 
    413 
    414 void SkiaCanvasProxy::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
    415         const SkPaint& paint) {
    416     SkPaint runPaint = paint;
    417 
    418      SkTextBlobRunIterator it(blob);
    419      for (;!it.done(); it.next()) {
    420          size_t textLen = it.glyphCount() * sizeof(uint16_t);
    421          const SkPoint& offset = it.offset();
    422          // applyFontToPaint() always overwrites the exact same attributes,
    423          // so it is safe to not re-seed the paint for this reason.
    424          it.applyFontToPaint(&runPaint);
    425 
    426          switch (it.positioning()) {
    427          case SkTextBlob::kDefault_Positioning:
    428              this->drawText(it.glyphs(), textLen, x + offset.x(), y + offset.y(), runPaint);
    429              break;
    430          case SkTextBlob::kHorizontal_Positioning: {
    431              std::unique_ptr<SkPoint[]> pts(new SkPoint[it.glyphCount()]);
    432              for (size_t i = 0; i < it.glyphCount(); i++) {
    433                  pts[i].set(x + offset.x() + it.pos()[i], y + offset.y());
    434              }
    435              this->drawPosText(it.glyphs(), textLen, pts.get(), runPaint);
    436              break;
    437          }
    438          case SkTextBlob::kFull_Positioning: {
    439              std::unique_ptr<SkPoint[]> pts(new SkPoint[it.glyphCount()]);
    440              for (size_t i = 0; i < it.glyphCount(); i++) {
    441                  const size_t xIndex = i*2;
    442                  const size_t yIndex = xIndex + 1;
    443                  pts[i].set(x + offset.x() + it.pos()[xIndex], y + offset.y() + it.pos()[yIndex]);
    444              }
    445              this->drawPosText(it.glyphs(), textLen, pts.get(), runPaint);
    446              break;
    447          }
    448          default:
    449              SkFAIL("unhandled positioning mode");
    450          }
    451      }
    452 }
    453 
    454 void SkiaCanvasProxy::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
    455         const SkPoint texCoords[4], SkBlendMode bmode, const SkPaint& paint) {
    456     if (mFilterHwuiCalls) {
    457         return;
    458     }
    459     SkPatchUtils::VertexData data;
    460 
    461     SkMatrix matrix;
    462     mCanvas->getMatrix(&matrix);
    463     SkISize lod = SkPatchUtils::GetLevelOfDetail(cubics, &matrix);
    464 
    465     // It automatically adjusts lodX and lodY in case it exceeds the number of indices.
    466     // If it fails to generate the vertices, then we do not draw.
    467     if (SkPatchUtils::getVertexData(&data, cubics, colors, texCoords, lod.width(), lod.height())) {
    468         this->drawVertices(SkCanvas::kTriangles_VertexMode, data.fVertexCount, data.fPoints,
    469                            data.fTexCoords, data.fColors, bmode, data.fIndices, data.fIndexCount,
    470                            paint);
    471     }
    472 }
    473 
    474 void SkiaCanvasProxy::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle) {
    475     mCanvas->clipRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, op);
    476 }
    477 
    478 void SkiaCanvasProxy::onClipRRect(const SkRRect& roundRect, SkClipOp op, ClipEdgeStyle) {
    479     SkPath path;
    480     path.addRRect(roundRect);
    481     mCanvas->clipPath(&path, op);
    482 }
    483 
    484 void SkiaCanvasProxy::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle) {
    485     mCanvas->clipPath(&path, op);
    486 }
    487 
    488 }; // namespace uirenderer
    489 }; // namespace android
    490