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 
SkiaCanvasProxy(Canvas * canvas,bool filterHwuiCalls)39 SkiaCanvasProxy::SkiaCanvasProxy(Canvas* canvas, bool filterHwuiCalls)
40         : INHERITED(canvas->width(), canvas->height())
41         , mCanvas(canvas)
42         , mFilterHwuiCalls(filterHwuiCalls) {}
43 
onDrawPaint(const SkPaint & paint)44 void SkiaCanvasProxy::onDrawPaint(const SkPaint& paint) {
45     mCanvas->drawPaint(paint);
46 }
47 
onDrawPoints(PointMode pointMode,size_t count,const SkPoint pts[],const SkPaint & paint)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 
onDrawOval(const SkRect & rect,const SkPaint & paint)86 void SkiaCanvasProxy::onDrawOval(const SkRect& rect, const SkPaint& paint) {
87     mCanvas->drawOval(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint);
88 }
89 
onDrawRect(const SkRect & rect,const SkPaint & paint)90 void SkiaCanvasProxy::onDrawRect(const SkRect& rect, const SkPaint& paint) {
91     mCanvas->drawRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint);
92 }
93 
onDrawRRect(const SkRRect & roundRect,const SkPaint & paint)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 
onDrawArc(const SkRect & rect,SkScalar startAngle,SkScalar sweepAngle,bool useCenter,const SkPaint & paint)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 
onDrawPath(const SkPath & path,const SkPaint & paint)113 void SkiaCanvasProxy::onDrawPath(const SkPath& path, const SkPaint& paint) {
114     mCanvas->drawPath(path, paint);
115 }
116 
onDrawBitmap(const SkBitmap & bitmap,SkScalar left,SkScalar top,const SkPaint * paint)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 
onDrawBitmapRect(const SkBitmap & skBitmap,const SkRect * srcPtr,const SkRect & dst,const SkPaint * paint,SrcRectConstraint)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 
onDrawBitmapNine(const SkBitmap & bitmap,const SkIRect & center,const SkRect & dst,const SkPaint *)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 
onDrawImage(const SkImage * image,SkScalar left,SkScalar top,const SkPaint * paint)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 
onDrawImageRect(const SkImage * image,const SkRect * srcPtr,const SkRect & dst,const SkPaint * paint,SrcRectConstraint constraint)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 
onDrawImageNine(const SkImage *,const SkIRect & center,const SkRect & dst,const SkPaint *)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 
onDrawImageLattice(const SkImage * image,const Lattice & lattice,const SkRect & dst,const SkPaint * paint)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 
onDrawVerticesObject(const SkVertices * vertices,SkBlendMode bmode,const SkPaint & paint)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 
onNewSurface(const SkImageInfo &,const SkSurfaceProps &)200 sk_sp<SkSurface> SkiaCanvasProxy::onNewSurface(const SkImageInfo&, const SkSurfaceProps&) {
201     SkDEBUGFAIL("SkiaCanvasProxy::onNewSurface is not supported");
202     return NULL;
203 }
204 
willSave()205 void SkiaCanvasProxy::willSave() {
206     mCanvas->save(android::SaveFlags::MatrixClip);
207 }
208 
saveFlags(SkCanvas::SaveLayerFlags layerFlags)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 
getSaveLayerStrategy(const SaveLayerRec & saveLayerRec)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 
willRestore()235 void SkiaCanvasProxy::willRestore() {
236     mCanvas->restore();
237 }
238 
didConcat(const SkMatrix & matrix)239 void SkiaCanvasProxy::didConcat(const SkMatrix& matrix) {
240     mCanvas->concat(matrix);
241 }
242 
didSetMatrix(const SkMatrix & matrix)243 void SkiaCanvasProxy::didSetMatrix(const SkMatrix& matrix) {
244     mCanvas->setMatrix(matrix);
245 }
246 
onDrawDRRect(const SkRRect & outer,const SkRRect & inner,const SkPaint & paint)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:
GlyphIDConverter(const void * text,size_t byteLength,const SkPaint & origPaint)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 
onDrawText(const void * text,size_t byteLength,SkScalar x,SkScalar y,const SkPaint & origPaint)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 
onDrawPosText(const void * text,size_t byteLength,const SkPoint pos[],const SkPaint & origPaint)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 
onDrawPosTextH(const void * text,size_t byteLength,const SkScalar xpos[],SkScalar constY,const SkPaint & paint)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 
onDrawTextOnPath(const void * text,size_t byteLength,const SkPath & path,const SkMatrix * matrix,const SkPaint & origPaint)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 
onDrawTextRSXform(const void * text,size_t byteLength,const SkRSXform xform[],const SkRect * cullRect,const SkPaint & paint)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 
onDrawTextBlob(const SkTextBlob * blob,SkScalar x,SkScalar y,const SkPaint & paint)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 
onDrawPatch(const SkPoint cubics[12],const SkColor colors[4],const SkPoint texCoords[4],SkBlendMode bmode,const SkPaint & paint)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 
onClipRect(const SkRect & rect,SkClipOp op,ClipEdgeStyle)474 void SkiaCanvasProxy::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle) {
475     mCanvas->clipRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, op);
476 }
477 
onClipRRect(const SkRRect & roundRect,SkClipOp op,ClipEdgeStyle)478 void SkiaCanvasProxy::onClipRRect(const SkRRect& roundRect, SkClipOp op, ClipEdgeStyle) {
479     SkPath path;
480     path.addRRect(roundRect);
481     mCanvas->clipPath(&path, op);
482 }
483 
onClipPath(const SkPath & path,SkClipOp op,ClipEdgeStyle)484 void SkiaCanvasProxy::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle) {
485     mCanvas->clipPath(&path, op);
486 }
487 
488 }; // namespace uirenderer
489 }; // namespace android
490