SkiaCanvasProxy.cpp revision 806a6f07a37c16b37631d8707dd1f2b41276fafc
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 <cutils/log.h> 20#include <SkPatchUtils.h> 21 22namespace android { 23namespace uirenderer { 24 25SkiaCanvasProxy::SkiaCanvasProxy(Canvas* canvas) 26 : INHERITED(canvas->width(), canvas->height()) 27 , mCanvas(canvas) {} 28 29void SkiaCanvasProxy::onDrawPaint(const SkPaint& paint) { 30 mCanvas->drawPaint(paint); 31} 32 33void SkiaCanvasProxy::onDrawPoints(PointMode pointMode, size_t count, const SkPoint pts[], 34 const SkPaint& paint) { 35 // convert the SkPoints into floats 36 SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats); 37 const size_t floatCount = count << 1; 38 const float* floatArray = &pts[0].fX; 39 40 switch (pointMode) { 41 case kPoints_PointMode: { 42 mCanvas->drawPoints(floatArray, floatCount, paint); 43 break; 44 } 45 case kLines_PointMode: { 46 mCanvas->drawLines(floatArray, floatCount, paint); 47 break; 48 } 49 case kPolygon_PointMode: { 50 SkPaint strokedPaint(paint); 51 strokedPaint.setStyle(SkPaint::kStroke_Style); 52 53 SkPath path; 54 for (size_t i = 0; i < count - 1; i++) { 55 path.moveTo(pts[i]); 56 path.lineTo(pts[i+1]); 57 this->drawPath(path, strokedPaint); 58 path.rewind(); 59 } 60 break; 61 } 62 default: 63 LOG_ALWAYS_FATAL("Unknown point type"); 64 } 65} 66 67void SkiaCanvasProxy::onDrawOval(const SkRect& rect, const SkPaint& paint) { 68 mCanvas->drawOval(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint); 69} 70 71void SkiaCanvasProxy::onDrawRect(const SkRect& rect, const SkPaint& paint) { 72 mCanvas->drawRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint); 73} 74 75void SkiaCanvasProxy::onDrawRRect(const SkRRect& roundRect, const SkPaint& paint) { 76 if (!roundRect.isComplex()) { 77 const SkRect& rect = roundRect.rect(); 78 SkVector radii = roundRect.getSimpleRadii(); 79 mCanvas->drawRoundRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, 80 radii.fX, radii.fY, paint); 81 } else { 82 SkPath path; 83 path.addRRect(roundRect); 84 mCanvas->drawPath(path, paint); 85 } 86} 87 88void SkiaCanvasProxy::onDrawPath(const SkPath& path, const SkPaint& paint) { 89 mCanvas->drawPath(path, paint); 90} 91 92void SkiaCanvasProxy::onDrawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top, 93 const SkPaint* paint) { 94 mCanvas->drawBitmap(bitmap, left, top, paint); 95} 96 97void SkiaCanvasProxy::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* srcPtr, 98 const SkRect& dst, const SkPaint* paint, DrawBitmapRectFlags) { 99 SkRect src = (srcPtr) ? *srcPtr : SkRect::MakeWH(bitmap.width(), bitmap.height()); 100 mCanvas->drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom, 101 dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint); 102} 103 104void SkiaCanvasProxy::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, 105 const SkRect& dst, const SkPaint*) { 106 //TODO make nine-patch drawing a method on Canvas.h 107 SkDEBUGFAIL("SkiaCanvasProxy::onDrawBitmapNine is not yet supported"); 108} 109 110void SkiaCanvasProxy::onDrawSprite(const SkBitmap& bitmap, int left, int top, 111 const SkPaint* paint) { 112 mCanvas->save(SkCanvas::kMatrixClip_SaveFlag); 113 mCanvas->setMatrix(SkMatrix::I()); 114 mCanvas->drawBitmap(bitmap, left, top, paint); 115 mCanvas->restore(); 116} 117 118void SkiaCanvasProxy::onDrawVertices(VertexMode mode, int vertexCount, const SkPoint vertices[], 119 const SkPoint texs[], const SkColor colors[], SkXfermode*, const uint16_t indices[], 120 int indexCount, const SkPaint& paint) { 121 // convert the SkPoints into floats 122 SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats); 123 const int floatCount = vertexCount << 1; 124 const float* vArray = &vertices[0].fX; 125 const float* tArray = (texs) ? &texs[0].fX : NULL; 126 const int* cArray = (colors) ? (int*)colors : NULL; 127 mCanvas->drawVertices(mode, floatCount, vArray, tArray, cArray, indices, indexCount, paint); 128} 129 130SkSurface* SkiaCanvasProxy::onNewSurface(const SkImageInfo&, const SkSurfaceProps&) { 131 SkDEBUGFAIL("SkiaCanvasProxy::onNewSurface is not supported"); 132 return NULL; 133} 134 135void SkiaCanvasProxy::willSave() { 136 mCanvas->save(SkCanvas::kMatrixClip_SaveFlag); 137} 138 139SkCanvas::SaveLayerStrategy SkiaCanvasProxy::willSaveLayer(const SkRect* rectPtr, 140 const SkPaint* paint, SaveFlags flags) { 141 SkRect rect; 142 if (rectPtr) { 143 rect = *rectPtr; 144 } else if(!mCanvas->getClipBounds(&rect)) { 145 rect = SkRect::MakeEmpty(); 146 } 147 mCanvas->saveLayer(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint, flags); 148 return SkCanvas::kNoLayer_SaveLayerStrategy; 149} 150 151void SkiaCanvasProxy::willRestore() { 152 mCanvas->restore(); 153} 154 155void SkiaCanvasProxy::didConcat(const SkMatrix& matrix) { 156 mCanvas->concat(matrix); 157} 158 159void SkiaCanvasProxy::didSetMatrix(const SkMatrix& matrix) { 160 mCanvas->setMatrix(matrix); 161} 162 163void SkiaCanvasProxy::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, 164 const SkPaint& paint) { 165 SkPath path; 166 path.addRRect(outer); 167 path.addRRect(inner); 168 path.setFillType(SkPath::kEvenOdd_FillType); 169 this->drawPath(path, paint); 170} 171 172/** 173 * Utility class that converts the incoming text & paint from the given encoding 174 * into glyphIDs. 175 */ 176class GlyphIDConverter { 177public: 178 GlyphIDConverter(const void* text, size_t byteLength, const SkPaint& origPaint) { 179 paint = origPaint; 180 if (paint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding) { 181 glyphIDs = (uint16_t*)text; 182 count = byteLength >> 1; 183 } else { 184 storage.reset(byteLength); // ensures space for one glyph per ID given UTF8 encoding. 185 glyphIDs = storage.get(); 186 count = paint.textToGlyphs(text, byteLength, storage.get()); 187 paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); 188 } 189 } 190 191 SkPaint paint; 192 uint16_t* glyphIDs; 193 int count; 194private: 195 SkAutoSTMalloc<32, uint16_t> storage; 196}; 197 198void SkiaCanvasProxy::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, 199 const SkPaint& origPaint) { 200 // convert to glyphIDs if necessary 201 GlyphIDConverter glyphs(text, byteLength, origPaint); 202 203 // compute the glyph positions 204 SkAutoSTMalloc<32, SkPoint> pointStorage(glyphs.count); 205 SkAutoSTMalloc<32, SkScalar> glyphWidths(glyphs.count); 206 glyphs.paint.getTextWidths(glyphs.glyphIDs, glyphs.count << 1, glyphWidths.get()); 207 208 // compute conservative bounds 209 // NOTE: We could call the faster paint.getFontBounds for a less accurate, 210 // but even more conservative bounds if this is too slow. 211 SkRect bounds; 212 glyphs.paint.measureText(glyphs.glyphIDs, glyphs.count << 1, &bounds); 213 214 // adjust for non-left alignment 215 if (glyphs.paint.getTextAlign() != SkPaint::kLeft_Align) { 216 SkScalar stop = 0; 217 for (int i = 0; i < glyphs.count; i++) { 218 stop += glyphWidths[i]; 219 } 220 if (glyphs.paint.getTextAlign() == SkPaint::kCenter_Align) { 221 stop = SkScalarHalf(stop); 222 } 223 if (glyphs.paint.isVerticalText()) { 224 y -= stop; 225 } else { 226 x -= stop; 227 } 228 } 229 230 // setup the first glyph position and adjust bounds if needed 231 int xBaseline = 0; 232 int yBaseline = 0; 233 if (mCanvas->drawTextAbsolutePos()) { 234 bounds.offset(x,y); 235 xBaseline = x; 236 yBaseline = y; 237 } 238 pointStorage[0].set(xBaseline, yBaseline); 239 240 // setup the remaining glyph positions 241 if (glyphs.paint.isVerticalText()) { 242 for (int i = 1; i < glyphs.count; i++) { 243 pointStorage[i].set(xBaseline, glyphWidths[i-1] + pointStorage[i-1].fY); 244 } 245 } else { 246 for (int i = 1; i < glyphs.count; i++) { 247 pointStorage[i].set(glyphWidths[i-1] + pointStorage[i-1].fX, yBaseline); 248 } 249 } 250 251 SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats); 252 mCanvas->drawText(glyphs.glyphIDs, &pointStorage[0].fX, glyphs.count, glyphs.paint, 253 x, y, bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, 0); 254} 255 256void SkiaCanvasProxy::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[], 257 const SkPaint& origPaint) { 258 // convert to glyphIDs if necessary 259 GlyphIDConverter glyphs(text, byteLength, origPaint); 260 261 // convert to relative positions if necessary 262 int x, y; 263 const SkPoint* posArray; 264 SkAutoSTMalloc<32, SkPoint> pointStorage; 265 if (mCanvas->drawTextAbsolutePos()) { 266 x = 0; 267 y = 0; 268 posArray = pos; 269 } else { 270 x = pos[0].fX; 271 y = pos[0].fY; 272 posArray = pointStorage.reset(glyphs.count); 273 for (int i = 0; i < glyphs.count; i++) { 274 pointStorage[i].fX = pos[i].fX- x; 275 pointStorage[i].fY = pos[i].fY- y; 276 } 277 } 278 279 // compute conservative bounds 280 // NOTE: We could call the faster paint.getFontBounds for a less accurate, 281 // but even more conservative bounds if this is too slow. 282 SkRect bounds; 283 glyphs.paint.measureText(glyphs.glyphIDs, glyphs.count << 1, &bounds); 284 285 SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats); 286 mCanvas->drawText(glyphs.glyphIDs, &posArray[0].fX, glyphs.count, glyphs.paint, x, y, 287 bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, 0); 288} 289 290void SkiaCanvasProxy::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[], 291 SkScalar constY, const SkPaint& paint) { 292 const size_t pointCount = byteLength >> 1; 293 SkAutoSTMalloc<32, SkPoint> storage(pointCount); 294 SkPoint* pts = storage.get(); 295 for (size_t i = 0; i < pointCount; i++) { 296 pts[i].set(xpos[i], constY); 297 } 298 this->onDrawPosText(text, byteLength, pts, paint); 299} 300 301void SkiaCanvasProxy::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path, 302 const SkMatrix* matrix, const SkPaint& origPaint) { 303 // convert to glyphIDs if necessary 304 GlyphIDConverter glyphs(text, byteLength, origPaint); 305 mCanvas->drawTextOnPath(glyphs.glyphIDs, glyphs.count, path, 0, 0, glyphs.paint); 306} 307 308void SkiaCanvasProxy::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, 309 const SkPaint& paint) { 310 SkDEBUGFAIL("SkiaCanvasProxy::onDrawTextBlob is not supported"); 311} 312 313void SkiaCanvasProxy::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], 314 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) { 315 SkPatchUtils::VertexData data; 316 317 SkMatrix matrix; 318 mCanvas->getMatrix(&matrix); 319 SkISize lod = SkPatchUtils::GetLevelOfDetail(cubics, &matrix); 320 321 // It automatically adjusts lodX and lodY in case it exceeds the number of indices. 322 // If it fails to generate the vertices, then we do not draw. 323 if (SkPatchUtils::getVertexData(&data, cubics, colors, texCoords, lod.width(), lod.height())) { 324 this->drawVertices(SkCanvas::kTriangles_VertexMode, data.fVertexCount, data.fPoints, 325 data.fTexCoords, data.fColors, xmode, data.fIndices, data.fIndexCount, 326 paint); 327 } 328} 329 330void SkiaCanvasProxy::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle) { 331 mCanvas->clipRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, op); 332} 333 334void SkiaCanvasProxy::onClipRRect(const SkRRect& roundRect, SkRegion::Op op, ClipEdgeStyle) { 335 SkPath path; 336 path.addRRect(roundRect); 337 mCanvas->clipPath(&path, op); 338} 339 340void SkiaCanvasProxy::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle) { 341 mCanvas->clipPath(&path, op); 342} 343 344void SkiaCanvasProxy::onClipRegion(const SkRegion& region, SkRegion::Op op) { 345 mCanvas->clipRegion(®ion, op); 346} 347 348}; // namespace uirenderer 349}; // namespace android 350