• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     SkPixmap pixmap;
155     if (image->peekPixels(&pixmap) && skiaBitmap.installPixels(pixmap)) {
156         onDrawBitmap(skiaBitmap, left, top, paint);
157     }
158 }
159 
onDrawImageRect(const SkImage * image,const SkRect * srcPtr,const SkRect & dst,const SkPaint * paint,SrcRectConstraint constraint)160 void SkiaCanvasProxy::onDrawImageRect(const SkImage* image, const SkRect* srcPtr, const SkRect& dst,
161         const SkPaint* paint, SrcRectConstraint constraint) {
162     SkBitmap skiaBitmap;
163     SkPixmap pixmap;
164     if (image->peekPixels(&pixmap) && skiaBitmap.installPixels(pixmap)) {
165         sk_sp<Bitmap> bitmap = Bitmap::createFrom(skiaBitmap.info(), *skiaBitmap.pixelRef());
166         SkRect src = (srcPtr) ? *srcPtr : SkRect::MakeWH(image->width(), image->height());
167         mCanvas->drawBitmap(*bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom,
168                 dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint);
169     }
170 }
171 
onDrawImageNine(const SkImage *,const SkIRect & center,const SkRect & dst,const SkPaint *)172 void SkiaCanvasProxy::onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst,
173         const SkPaint*) {
174     SkDEBUGFAIL("SkiaCanvasProxy::onDrawImageNine is not yet supported");
175 }
176 
onDrawImageLattice(const SkImage * image,const Lattice & lattice,const SkRect & dst,const SkPaint * paint)177 void SkiaCanvasProxy::onDrawImageLattice(const SkImage* image, const Lattice& lattice,
178         const SkRect& dst, const SkPaint* paint) {
179     SkLatticeIter iter(lattice, dst);
180     SkRect srcR, dstR;
181     while (iter.next(&srcR, &dstR)) {
182         onDrawImageRect(image, &srcR, dstR, paint, SkCanvas::kStrict_SrcRectConstraint);
183     }
184 }
185 
onDrawVerticesObject(const SkVertices * vertices,SkBlendMode bmode,const SkPaint & paint)186 void SkiaCanvasProxy::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode,
187         const SkPaint& paint) {
188     if (mFilterHwuiCalls) {
189         return;
190     }
191     mCanvas->drawVertices(vertices, bmode, paint);
192 }
193 
onNewSurface(const SkImageInfo &,const SkSurfaceProps &)194 sk_sp<SkSurface> SkiaCanvasProxy::onNewSurface(const SkImageInfo&, const SkSurfaceProps&) {
195     SkDEBUGFAIL("SkiaCanvasProxy::onNewSurface is not supported");
196     return NULL;
197 }
198 
willSave()199 void SkiaCanvasProxy::willSave() {
200     mCanvas->save(android::SaveFlags::MatrixClip);
201 }
202 
saveFlags(SkCanvas::SaveLayerFlags layerFlags)203 static inline SaveFlags::Flags saveFlags(SkCanvas::SaveLayerFlags layerFlags) {
204     SaveFlags::Flags saveFlags = 0;
205 
206     if (!(layerFlags & SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag)) {
207         saveFlags |= SaveFlags::ClipToLayer;
208     }
209 
210     if (!(layerFlags & SkCanvas::kIsOpaque_SaveLayerFlag)) {
211         saveFlags |= SaveFlags::HasAlphaLayer;
212     }
213 
214     return saveFlags;
215 }
216 
getSaveLayerStrategy(const SaveLayerRec & saveLayerRec)217 SkCanvas::SaveLayerStrategy SkiaCanvasProxy::getSaveLayerStrategy(const SaveLayerRec& saveLayerRec) {
218     SkRect rect;
219     if (saveLayerRec.fBounds) {
220         rect = *saveLayerRec.fBounds;
221     } else if (!mCanvas->getClipBounds(&rect)) {
222         rect = SkRect::MakeEmpty();
223     }
224     mCanvas->saveLayer(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, saveLayerRec.fPaint,
225                        saveFlags(saveLayerRec.fSaveLayerFlags));
226     return SkCanvas::kNoLayer_SaveLayerStrategy;
227 }
228 
willRestore()229 void SkiaCanvasProxy::willRestore() {
230     mCanvas->restore();
231 }
232 
didConcat(const SkMatrix & matrix)233 void SkiaCanvasProxy::didConcat(const SkMatrix& matrix) {
234     mCanvas->concat(matrix);
235 }
236 
didSetMatrix(const SkMatrix & matrix)237 void SkiaCanvasProxy::didSetMatrix(const SkMatrix& matrix) {
238     mCanvas->setMatrix(matrix);
239 }
240 
onDrawDRRect(const SkRRect & outer,const SkRRect & inner,const SkPaint & paint)241 void SkiaCanvasProxy::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
242         const SkPaint& paint) {
243     SkPath path;
244     path.addRRect(outer);
245     path.addRRect(inner);
246     path.setFillType(SkPath::kEvenOdd_FillType);
247     this->drawPath(path, paint);
248 }
249 
250 /**
251  * Utility class that converts the incoming text & paint from the given encoding
252  * into glyphIDs.
253  */
254 class GlyphIDConverter {
255 public:
GlyphIDConverter(const void * text,size_t byteLength,const SkPaint & origPaint)256     GlyphIDConverter(const void* text, size_t byteLength, const SkPaint& origPaint) {
257         paint = origPaint;
258         if (paint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding) {
259             glyphIDs = (uint16_t*)text;
260             count = byteLength >> 1;
261         } else {
262              // ensure space for one glyph per ID given UTF8 encoding.
263             storage.reset(new uint16_t[byteLength]);
264             glyphIDs = storage.get();
265             count = paint.textToGlyphs(text, byteLength, storage.get());
266             paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
267         }
268     }
269 
270     SkPaint paint;
271     uint16_t* glyphIDs;
272     int count;
273 private:
274     std::unique_ptr<uint16_t[]> storage;
275 };
276 
onDrawText(const void * text,size_t byteLength,SkScalar x,SkScalar y,const SkPaint & origPaint)277 void SkiaCanvasProxy::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
278         const SkPaint& origPaint) {
279     // convert to glyphIDs if necessary
280     GlyphIDConverter glyphs(text, byteLength, origPaint);
281 
282     // compute the glyph positions
283     std::unique_ptr<SkScalar[]> glyphWidths(new SkScalar[glyphs.count]);
284     glyphs.paint.getTextWidths(glyphs.glyphIDs, glyphs.count << 1, glyphWidths.get());
285 
286     // compute conservative bounds
287     // NOTE: We could call the faster paint.getFontBounds for a less accurate,
288     //       but even more conservative bounds if this  is too slow.
289     SkRect bounds;
290     glyphs.paint.measureText(glyphs.glyphIDs, glyphs.count << 1, &bounds);
291 
292     // adjust for non-left alignment
293     if (glyphs.paint.getTextAlign() != SkPaint::kLeft_Align) {
294         SkScalar stop = 0;
295         for (int i = 0; i < glyphs.count; i++) {
296             stop += glyphWidths[i];
297         }
298         if (glyphs.paint.getTextAlign() == SkPaint::kCenter_Align) {
299             stop = SkScalarHalf(stop);
300         }
301         if (glyphs.paint.isVerticalText()) {
302             y -= stop;
303         } else {
304             x -= stop;
305         }
306     }
307 
308     // setup the first glyph position and adjust bounds if needed
309     int xBaseline = 0;
310     int yBaseline = 0;
311     if (mCanvas->drawTextAbsolutePos()) {
312         bounds.offset(x,y);
313         xBaseline = x;
314         yBaseline = y;
315     }
316 
317     static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats");
318     auto glyphFunc = [&] (uint16_t* text, float* positions) {
319         memcpy(text, glyphs.glyphIDs, glyphs.count*sizeof(uint16_t));
320         size_t posIndex = 0;
321         // setup the first glyph position
322         positions[posIndex++] = xBaseline;
323         positions[posIndex++] = yBaseline;
324         // setup the remaining glyph positions
325         if (glyphs.paint.isVerticalText()) {
326             float yPosition = yBaseline;
327             for (int i = 1; i < glyphs.count; i++) {
328                 positions[posIndex++] = xBaseline;
329                 yPosition += glyphWidths[i-1];
330                 positions[posIndex++] = yPosition;
331             }
332         } else {
333             float xPosition = xBaseline;
334             for (int i = 1; i < glyphs.count; i++) {
335                 xPosition += glyphWidths[i-1];
336                 positions[posIndex++] = xPosition;
337                 positions[posIndex++] = yBaseline;
338             }
339         }
340     };
341     mCanvas->drawGlyphs(glyphFunc, glyphs.count, glyphs.paint, x, y, bounds.fLeft, bounds.fTop,
342             bounds.fRight, bounds.fBottom, 0);
343 }
344 
onDrawPosText(const void * text,size_t byteLength,const SkPoint pos[],const SkPaint & origPaint)345 void SkiaCanvasProxy::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
346         const SkPaint& origPaint) {
347     // convert to glyphIDs if necessary
348     GlyphIDConverter glyphs(text, byteLength, origPaint);
349 
350     // convert to relative positions if necessary
351     int x, y;
352     if (mCanvas->drawTextAbsolutePos()) {
353         x = 0;
354         y = 0;
355     } else {
356         x = pos[0].fX;
357         y = pos[0].fY;
358     }
359 
360     // Compute conservative bounds.  If the content has already been processed
361     // by Minikin then it had already computed these bounds.  Unfortunately,
362     // there is no way to capture those bounds as part of the Skia drawPosText
363     // API so we need to do that computation again here.
364     SkRect bounds = SkRect::MakeEmpty();
365     for (int i = 0; i < glyphs.count; i++) {
366         SkRect glyphBounds = SkRect::MakeEmpty();
367         glyphs.paint.measureText(&glyphs.glyphIDs[i], sizeof(uint16_t), &glyphBounds);
368         glyphBounds.offset(pos[i].fX, pos[i].fY);
369         bounds.join(glyphBounds);
370     }
371 
372     static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats");
373     auto glyphFunc = [&] (uint16_t* text, float* positions) {
374         memcpy(text, glyphs.glyphIDs, glyphs.count*sizeof(uint16_t));
375         if (mCanvas->drawTextAbsolutePos()) {
376             memcpy(positions, pos, 2*glyphs.count*sizeof(float));
377         } else {
378             for (int i = 0, posIndex = 0; i < glyphs.count; i++) {
379                 positions[posIndex++] = pos[i].fX - x;
380                 positions[posIndex++] = pos[i].fY - y;
381             }
382         }
383     };
384     mCanvas->drawGlyphs(glyphFunc, glyphs.count, glyphs.paint, x, y, bounds.fLeft, bounds.fTop,
385             bounds.fRight, bounds.fBottom, 0);
386 }
387 
onDrawPosTextH(const void * text,size_t byteLength,const SkScalar xpos[],SkScalar constY,const SkPaint & paint)388 void SkiaCanvasProxy::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
389         SkScalar constY, const SkPaint& paint) {
390     const size_t pointCount = byteLength >> 1;
391     std::unique_ptr<SkPoint[]> pts(new SkPoint[pointCount]);
392     for (size_t i = 0; i < pointCount; i++) {
393         pts[i].set(xpos[i], constY);
394     }
395     this->onDrawPosText(text, byteLength, pts.get(), paint);
396 }
397 
onDrawTextOnPath(const void * text,size_t byteLength,const SkPath & path,const SkMatrix * matrix,const SkPaint & origPaint)398 void SkiaCanvasProxy::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
399         const SkMatrix* matrix, const SkPaint& origPaint) {
400     SkDEBUGFAIL("SkiaCanvasProxy::onDrawTextOnPath is not supported");
401 }
402 
onDrawTextRSXform(const void * text,size_t byteLength,const SkRSXform xform[],const SkRect * cullRect,const SkPaint & paint)403 void SkiaCanvasProxy::onDrawTextRSXform(const void* text, size_t byteLength,
404         const SkRSXform xform[], const SkRect* cullRect, const SkPaint& paint) {
405     GlyphIDConverter glyphs(text, byteLength, paint); // Just get count
406     SkMatrix localM, currM, origM;
407     mCanvas->getMatrix(&currM);
408     origM = currM;
409     for (int i = 0; i < glyphs.count; i++) {
410         localM.setRSXform(*xform++);
411         currM.setConcat(origM, localM);
412         mCanvas->setMatrix(currM);
413         this->onDrawText((char*)text + (byteLength / glyphs.count * i),
414                          byteLength / glyphs.count, 0, 0, paint);
415     }
416     mCanvas->setMatrix(origM);
417 }
418 
419 
onDrawTextBlob(const SkTextBlob * blob,SkScalar x,SkScalar y,const SkPaint & paint)420 void SkiaCanvasProxy::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
421         const SkPaint& paint) {
422     SkPaint runPaint = paint;
423 
424      SkTextBlobRunIterator it(blob);
425      for (;!it.done(); it.next()) {
426          size_t textLen = it.glyphCount() * sizeof(uint16_t);
427          const SkPoint& offset = it.offset();
428          // applyFontToPaint() always overwrites the exact same attributes,
429          // so it is safe to not re-seed the paint for this reason.
430          it.applyFontToPaint(&runPaint);
431 
432          switch (it.positioning()) {
433          case SkTextBlob::kDefault_Positioning:
434              this->drawText(it.glyphs(), textLen, x + offset.x(), y + offset.y(), runPaint);
435              break;
436          case SkTextBlob::kHorizontal_Positioning: {
437              std::unique_ptr<SkPoint[]> pts(new SkPoint[it.glyphCount()]);
438              for (size_t i = 0; i < it.glyphCount(); i++) {
439                  pts[i].set(x + offset.x() + it.pos()[i], y + offset.y());
440              }
441              this->drawPosText(it.glyphs(), textLen, pts.get(), runPaint);
442              break;
443          }
444          case SkTextBlob::kFull_Positioning: {
445              std::unique_ptr<SkPoint[]> pts(new SkPoint[it.glyphCount()]);
446              for (size_t i = 0; i < it.glyphCount(); i++) {
447                  const size_t xIndex = i*2;
448                  const size_t yIndex = xIndex + 1;
449                  pts[i].set(x + offset.x() + it.pos()[xIndex], y + offset.y() + it.pos()[yIndex]);
450              }
451              this->drawPosText(it.glyphs(), textLen, pts.get(), runPaint);
452              break;
453          }
454          default:
455              SkFAIL("unhandled positioning mode");
456          }
457      }
458 }
459 
onDrawPatch(const SkPoint cubics[12],const SkColor colors[4],const SkPoint texCoords[4],SkBlendMode bmode,const SkPaint & paint)460 void SkiaCanvasProxy::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
461         const SkPoint texCoords[4], SkBlendMode bmode, const SkPaint& paint) {
462     if (mFilterHwuiCalls) {
463         return;
464     }
465     SkMatrix matrix;
466     mCanvas->getMatrix(&matrix);
467     SkISize lod = SkPatchUtils::GetLevelOfDetail(cubics, &matrix);
468 
469     mCanvas->drawVertices(SkPatchUtils::MakeVertices(cubics, colors, texCoords,
470                                                      lod.width(), lod.height()).get(),
471                           bmode, paint);
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