summaryrefslogtreecommitdiff
path: root/libs/hwui/SkiaCanvasProxy.cpp
diff options
context:
space:
mode:
author Derek Sollenberger <djsollen@google.com> 2014-12-16 08:37:20 -0500
committer Derek Sollenberger <djsollen@google.com> 2015-01-30 12:56:37 -0500
commit1db141f93c4fe79a4669440c3d14f63bc87b2e34 (patch)
tree674930c62207ed50620d51b67448cffe3d01b43a /libs/hwui/SkiaCanvasProxy.cpp
parent728dace14d83e87e777ceaaf3dfd031cf3502f80 (diff)
Create proxy between Skia's SkCanvas and the framework Canvas.
This enables Picture.java to be replayed into HWUI in addition to extending the Skia testing suite to HWUI. Bug: 19011232 Change-Id: Id27ac03eec817b0784763e62ab8413a07b3b8cb2
Diffstat (limited to 'libs/hwui/SkiaCanvasProxy.cpp')
-rw-r--r--libs/hwui/SkiaCanvasProxy.cpp347
1 files changed, 347 insertions, 0 deletions
diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp
new file mode 100644
index 000000000000..de5f91c4ad8b
--- /dev/null
+++ b/libs/hwui/SkiaCanvasProxy.cpp
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkiaCanvasProxy.h"
+
+#include <cutils/log.h>
+#include <SkPatchUtils.h>
+
+namespace android {
+namespace uirenderer {
+
+SkiaCanvasProxy::SkiaCanvasProxy(Canvas* canvas)
+ : INHERITED(canvas->width(), canvas->height())
+ , mCanvas(canvas) {}
+
+void SkiaCanvasProxy::onDrawPaint(const SkPaint& paint) {
+ mCanvas->drawPaint(paint);
+}
+
+void SkiaCanvasProxy::onDrawPoints(PointMode pointMode, size_t count, const SkPoint pts[],
+ const SkPaint& paint) {
+ // convert the SkPoints into floats
+ SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats);
+ const size_t floatCount = count << 1;
+ const float* floatArray = &pts[0].fX;
+
+ switch (pointMode) {
+ case kPoints_PointMode: {
+ mCanvas->drawPoints(floatArray, floatCount, paint);
+ break;
+ }
+ case kLines_PointMode: {
+ mCanvas->drawLines(floatArray, floatCount, paint);
+ break;
+ }
+ case kPolygon_PointMode: {
+ SkPaint strokedPaint(paint);
+ strokedPaint.setStyle(SkPaint::kStroke_Style);
+
+ SkPath path;
+ for (size_t i = 0; i < count - 1; i++) {
+ path.moveTo(pts[i]);
+ path.lineTo(pts[i+1]);
+ this->drawPath(path, strokedPaint);
+ path.rewind();
+ }
+ break;
+ }
+ default:
+ LOG_ALWAYS_FATAL("Unknown point type");
+ }
+}
+
+void SkiaCanvasProxy::onDrawOval(const SkRect& rect, const SkPaint& paint) {
+ mCanvas->drawOval(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint);
+}
+
+void SkiaCanvasProxy::onDrawRect(const SkRect& rect, const SkPaint& paint) {
+ mCanvas->drawRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint);
+}
+
+void SkiaCanvasProxy::onDrawRRect(const SkRRect& roundRect, const SkPaint& paint) {
+ if (!roundRect.isComplex()) {
+ const SkRect& rect = roundRect.rect();
+ SkVector radii = roundRect.getSimpleRadii();
+ mCanvas->drawRoundRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom,
+ radii.fX, radii.fY, paint);
+ } else {
+ SkPath path;
+ path.addRRect(roundRect);
+ mCanvas->drawPath(path, paint);
+ }
+}
+
+void SkiaCanvasProxy::onDrawPath(const SkPath& path, const SkPaint& paint) {
+ mCanvas->drawPath(path, paint);
+}
+
+void SkiaCanvasProxy::onDrawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
+ const SkPaint* paint) {
+ mCanvas->drawBitmap(bitmap, left, top, paint);
+}
+
+void SkiaCanvasProxy::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* srcPtr,
+ const SkRect& dst, const SkPaint* paint, DrawBitmapRectFlags) {
+ SkRect src = (srcPtr) ? *srcPtr : SkRect::MakeWH(bitmap.width(), bitmap.height());
+ mCanvas->drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom,
+ dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint);
+}
+
+void SkiaCanvasProxy::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
+ const SkRect& dst, const SkPaint*) {
+ //TODO make nine-patch drawing a method on Canvas.h
+ SkDEBUGFAIL("SkiaCanvasProxy::onDrawBitmapNine is not yet supported");
+}
+
+void SkiaCanvasProxy::onDrawSprite(const SkBitmap& bitmap, int left, int top,
+ const SkPaint* paint) {
+ mCanvas->save(SkCanvas::kMatrixClip_SaveFlag);
+ mCanvas->setMatrix(SkMatrix::I());
+ mCanvas->drawBitmap(bitmap, left, top, paint);
+ mCanvas->restore();
+}
+
+void SkiaCanvasProxy::onDrawVertices(VertexMode mode, int vertexCount, const SkPoint vertices[],
+ const SkPoint texs[], const SkColor colors[], SkXfermode*, const uint16_t indices[],
+ int indexCount, const SkPaint& paint) {
+ // convert the SkPoints into floats
+ SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats);
+ const int floatCount = vertexCount << 1;
+ const float* vArray = &vertices[0].fX;
+ const float* tArray = (texs) ? &texs[0].fX : NULL;
+ const int* cArray = (colors) ? (int*)colors : NULL;
+ mCanvas->drawVertices(mode, floatCount, vArray, tArray, cArray, indices, indexCount, paint);
+}
+
+SkSurface* SkiaCanvasProxy::onNewSurface(const SkImageInfo&, const SkSurfaceProps&) {
+ SkDEBUGFAIL("SkiaCanvasProxy::onNewSurface is not supported");
+ return NULL;
+}
+
+void SkiaCanvasProxy::willSave() {
+ mCanvas->save(SkCanvas::kMatrixClip_SaveFlag);
+}
+
+SkCanvas::SaveLayerStrategy SkiaCanvasProxy::willSaveLayer(const SkRect* rectPtr,
+ const SkPaint* paint, SaveFlags flags) {
+ SkRect rect;
+ if (rectPtr) {
+ rect = *rectPtr;
+ } else if(!mCanvas->getClipBounds(&rect)) {
+ rect = SkRect::MakeEmpty();
+ }
+ mCanvas->saveLayer(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint, flags);
+ return SkCanvas::kNoLayer_SaveLayerStrategy;
+}
+
+void SkiaCanvasProxy::willRestore() {
+ mCanvas->restore();
+}
+
+void SkiaCanvasProxy::didConcat(const SkMatrix& matrix) {
+ mCanvas->concat(matrix);
+}
+
+void SkiaCanvasProxy::didSetMatrix(const SkMatrix& matrix) {
+ mCanvas->setMatrix(matrix);
+}
+
+void SkiaCanvasProxy::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
+ const SkPaint& paint) {
+ SkPath path;
+ path.addRRect(outer);
+ path.addRRect(inner);
+ path.setFillType(SkPath::kEvenOdd_FillType);
+ this->drawPath(path, paint);
+}
+
+/**
+ * Utility class that converts the incoming text & paint from the given encoding
+ * into glyphIDs.
+ */
+class GlyphIDConverter {
+public:
+ GlyphIDConverter(const void* text, size_t byteLength, const SkPaint& origPaint) {
+ paint = origPaint;
+ if (paint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding) {
+ glyphIDs = (uint16_t*)text;
+ count = byteLength >> 1;
+ } else {
+ storage.reset(byteLength); // ensures space for one glyph per ID given UTF8 encoding.
+ glyphIDs = storage.get();
+ count = paint.textToGlyphs(text, byteLength, storage.get());
+ paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ }
+ }
+
+ SkPaint paint;
+ uint16_t* glyphIDs;
+ int count;
+private:
+ SkAutoSTMalloc<32, uint16_t> storage;
+};
+
+void SkiaCanvasProxy::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
+ const SkPaint& origPaint) {
+ // convert to glyphIDs if necessary
+ GlyphIDConverter glyphs(text, byteLength, origPaint);
+
+ // compute the glyph positions
+ SkAutoSTMalloc<32, SkPoint> pointStorage(glyphs.count);
+ SkAutoSTMalloc<32, SkScalar> glyphWidths(glyphs.count);
+ glyphs.paint.getTextWidths(glyphs.glyphIDs, glyphs.count << 1, glyphWidths.get());
+
+ // compute conservative bounds
+ // NOTE: We could call the faster paint.getFontBounds for a less accurate,
+ // but even more conservative bounds if this is too slow.
+ SkRect bounds;
+ glyphs.paint.measureText(glyphs.glyphIDs, glyphs.count << 1, &bounds);
+
+ // adjust for non-left alignment
+ if (glyphs.paint.getTextAlign() != SkPaint::kLeft_Align) {
+ SkScalar stop = 0;
+ for (int i = 0; i < glyphs.count; i++) {
+ stop += glyphWidths[i];
+ }
+ if (glyphs.paint.getTextAlign() == SkPaint::kCenter_Align) {
+ stop = SkScalarHalf(stop);
+ }
+ if (glyphs.paint.isVerticalText()) {
+ y -= stop;
+ } else {
+ x -= stop;
+ }
+ }
+
+ // setup the first glyph position and adjust bounds if needed
+ if (mCanvas->drawTextAbsolutePos()) {
+ bounds.offset(x,y);
+ pointStorage[0].set(x, y);
+ } else {
+ pointStorage[0].set(0, 0);
+ }
+
+ // setup the remaining glyph positions
+ if (glyphs.paint.isVerticalText()) {
+ for (int i = 1; i < glyphs.count; i++) {
+ pointStorage[i].set(x, glyphWidths[i-1] + pointStorage[i-1].fY);
+ }
+ } else {
+ for (int i = 1; i < glyphs.count; i++) {
+ pointStorage[i].set(glyphWidths[i-1] + pointStorage[i-1].fX, y);
+ }
+ }
+
+ SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats);
+ mCanvas->drawText(glyphs.glyphIDs, &pointStorage[0].fX, glyphs.count, glyphs.paint,
+ x, y, bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, 0);
+}
+
+void SkiaCanvasProxy::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
+ const SkPaint& origPaint) {
+ // convert to glyphIDs if necessary
+ GlyphIDConverter glyphs(text, byteLength, origPaint);
+
+ // convert to relative positions if necessary
+ int x, y;
+ const SkPoint* posArray;
+ SkAutoSTMalloc<32, SkPoint> pointStorage;
+ if (mCanvas->drawTextAbsolutePos()) {
+ x = 0;
+ y = 0;
+ posArray = pos;
+ } else {
+ x = pos[0].fX;
+ y = pos[0].fY;
+ posArray = pointStorage.reset(glyphs.count);
+ for (int i = 0; i < glyphs.count; i++) {
+ pointStorage[i].fX = pos[i].fX- x;
+ pointStorage[i].fY = pos[i].fY- y;
+ }
+ }
+
+ // compute conservative bounds
+ // NOTE: We could call the faster paint.getFontBounds for a less accurate,
+ // but even more conservative bounds if this is too slow.
+ SkRect bounds;
+ glyphs.paint.measureText(glyphs.glyphIDs, glyphs.count << 1, &bounds);
+
+ SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats);
+ mCanvas->drawText(glyphs.glyphIDs, &posArray[0].fX, glyphs.count, glyphs.paint, x, y,
+ bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, 0);
+}
+
+void SkiaCanvasProxy::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
+ SkScalar constY, const SkPaint& paint) {
+ const size_t pointCount = byteLength >> 1;
+ SkAutoSTMalloc<32, SkPoint> storage(pointCount);
+ SkPoint* pts = storage.get();
+ for (size_t i = 0; i < pointCount; i++) {
+ pts[i].set(xpos[i], constY);
+ }
+ this->onDrawPosText(text, byteLength, pts, paint);
+}
+
+void SkiaCanvasProxy::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
+ const SkMatrix* matrix, const SkPaint& origPaint) {
+ // convert to glyphIDs if necessary
+ GlyphIDConverter glyphs(text, byteLength, origPaint);
+ mCanvas->drawTextOnPath(glyphs.glyphIDs, glyphs.count, path, 0, 0, glyphs.paint);
+}
+
+void SkiaCanvasProxy::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
+ const SkPaint& paint) {
+ SkDEBUGFAIL("SkiaCanvasProxy::onDrawTextBlob is not supported");
+}
+
+void SkiaCanvasProxy::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
+ const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
+ SkPatchUtils::VertexData data;
+
+ SkMatrix matrix;
+ mCanvas->getMatrix(&matrix);
+ SkISize lod = SkPatchUtils::GetLevelOfDetail(cubics, &matrix);
+
+ // It automatically adjusts lodX and lodY in case it exceeds the number of indices.
+ // If it fails to generate the vertices, then we do not draw.
+ if (SkPatchUtils::getVertexData(&data, cubics, colors, texCoords, lod.width(), lod.height())) {
+ this->drawVertices(SkCanvas::kTriangles_VertexMode, data.fVertexCount, data.fPoints,
+ data.fTexCoords, data.fColors, xmode, data.fIndices, data.fIndexCount,
+ paint);
+ }
+}
+
+void SkiaCanvasProxy::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle) {
+ mCanvas->clipRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, op);
+}
+
+void SkiaCanvasProxy::onClipRRect(const SkRRect& roundRect, SkRegion::Op op, ClipEdgeStyle) {
+ SkPath path;
+ path.addRRect(roundRect);
+ mCanvas->clipPath(&path, op);
+}
+
+void SkiaCanvasProxy::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle) {
+ mCanvas->clipPath(&path, op);
+}
+
+void SkiaCanvasProxy::onClipRegion(const SkRegion& region, SkRegion::Op op) {
+ mCanvas->clipRegion(&region, op);
+}
+
+}; // namespace uirenderer
+}; // namespace android