SkCanvas interface for drawing a patch.
Added function SkCanvas::drawPatch to the API. This function
receives the patch to draw and the paint.
Added function SkBaseDevice::drawPatch to the API. This function also receives the patch to draw and the paint.
Currently SkGpuDevice and SkBitmapDevice generate the mesh taking into
account the scale factor and call the corresponding device's drawVertices.
BUG=skia:
R=jvanverth@google.com, egdaniel@google.com, bsalomon@google.com
Author: dandov@google.com
Review URL: https://codereview.chromium.org/424663006
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index a561316..3320d18 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -2253,6 +2253,25 @@
LOOPER_END
}
+void SkCanvas::drawPatch(const SkPatch& patch, const SkPaint& paint) {
+
+ // Since a patch is always within the convex hull of the control points, we discard it when its
+ // bounding rectangle is completely outside the current clip.
+ SkRect bounds;
+ bounds.set(patch.getControlPoints(), 12);
+ if (this->quickReject(bounds)) {
+ return;
+ }
+
+ LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type, NULL)
+
+ while (iter.next()) {
+ iter.fDevice->drawPatch(iter, patch, paint);
+ }
+
+ LOOPER_END
+}
+
//////////////////////////////////////////////////////////////////////////////
// These methods are NOT virtual, and therefore must call back into virtual
// methods, rather than actually drawing themselves.
diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp
index e71c889..e71500a 100644
--- a/src/core/SkDevice.cpp
+++ b/src/core/SkDevice.cpp
@@ -6,7 +6,9 @@
*/
#include "SkDevice.h"
+#include "SkDraw.h"
#include "SkMetaData.h"
+#include "SkPatchUtils.h"
SkBaseDevice::SkBaseDevice()
: fLeakyProperties(SkDeviceProperties::MakeDefault())
@@ -77,6 +79,17 @@
this->drawPath(draw, path, paint, preMatrix, pathIsMutable);
}
+void SkBaseDevice::drawPatch(const SkDraw& draw, const SkPatch& patch, const SkPaint& paint) {
+ SkPatch::VertexData data;
+
+ SkISize lod = SkPatchUtils::GetLevelOfDetail(patch, draw.fMatrix);
+
+ // It automatically adjusts lodX and lodY in case it exceeds the number of indices.
+ patch.getVertexData(&data, lod.width(), lod.height());
+ this->drawVertices(draw, SkCanvas::kTriangles_VertexMode, data.fVertexCount, data.fPoints,
+ data.fTexCoords, data.fColors, NULL, data.fIndices, data.fIndexCount, paint);
+}
+
bool SkBaseDevice::readPixels(const SkImageInfo& info, void* dstP, size_t rowBytes, int x, int y) {
#ifdef SK_DEBUG
SkASSERT(info.width() > 0 && info.height() > 0);
diff --git a/src/core/SkPatch.cpp b/src/core/SkPatch.cpp
index acd6cb9..cc967d5 100644
--- a/src/core/SkPatch.cpp
+++ b/src/core/SkPatch.cpp
@@ -53,6 +53,21 @@
this->restart(1);
}
+ explicit FwDCubicEvaluator(SkPoint points[4]) {
+ for (int i = 0; i< 4; i++) {
+ fPoints[i] = points[i];
+ }
+
+ SkScalar cx[4], cy[4];
+ SkGetCubicCoeff(fPoints, cx, cy);
+ fCoefs[0].set(cx[0], cy[0]);
+ fCoefs[1].set(cx[1], cy[1]);
+ fCoefs[2].set(cx[2], cy[2]);
+ fCoefs[3].set(cx[3], cy[3]);
+
+ this->restart(1);
+ }
+
/**
* Restarts the forward differences evaluator to the first value of t = 0.
*/
@@ -104,14 +119,13 @@
SkPatch::SkPatch(SkPoint points[12], SkColor colors[4]) {
- for (int i = 0; i<12; i++) {
+ for (int i = 0; i < 12; i++) {
fCtrlPoints[i] = points[i];
}
+ for (int i = 0; i < 4; i++) {
+ fCornerColors[i] = colors[i];
+ }
- fCornerColors[0] = SkPreMultiplyColor(colors[0]);
- fCornerColors[1] = SkPreMultiplyColor(colors[1]);
- fCornerColors[2] = SkPreMultiplyColor(colors[2]);
- fCornerColors[3] = SkPreMultiplyColor(colors[3]);
}
uint8_t bilinear(SkScalar tx, SkScalar ty, SkScalar c00, SkScalar c10, SkScalar c01, SkScalar c11) {
@@ -120,51 +134,51 @@
return uint8_t(a * (1.f - ty) + b * ty);
}
-bool SkPatch::getVertexData(SkPatch::VertexData* data, int divisions) {
+bool SkPatch::getVertexData(SkPatch::VertexData* data, int lodX, int lodY) const {
- if (divisions < 1) {
+ if (lodX < 1 || lodY < 1) {
return false;
}
- int divX = divisions, divY = divisions;
+ // premultiply colors to avoid color bleeding.
+ SkPMColor colors[4];
+ for (int i = 0; i < 4; i++) {
+ colors[i] = SkPreMultiplyColor(fCornerColors[i]);
+ }
- data->fVertexCount = (divX + 1) * (divY + 1);
- data->fIndexCount = divX * divY * 6;
+ // number of indices is limited by size of uint16_t, so we clamp it to avoid overflow
+ data->fVertexCount = SkMin32((lodX + 1) * (lodY + 1), 65536);
+ lodX = SkMin32(lodX, 255);
+ lodY = SkMin32(lodY, 255);
+ data->fIndexCount = lodX * lodY * 6;
data->fPoints = SkNEW_ARRAY(SkPoint, data->fVertexCount);
data->fColors = SkNEW_ARRAY(uint32_t, data->fVertexCount);
data->fTexCoords = SkNEW_ARRAY(SkPoint, data->fVertexCount);
data->fIndices = SkNEW_ARRAY(uint16_t, data->fIndexCount);
- FwDCubicEvaluator fBottom(fCtrlPoints[kBottomP0_CubicCtrlPts],
- fCtrlPoints[kBottomP1_CubicCtrlPts],
- fCtrlPoints[kBottomP2_CubicCtrlPts],
- fCtrlPoints[kBottomP3_CubicCtrlPts]),
- fTop(fCtrlPoints[kTopP0_CubicCtrlPts],
- fCtrlPoints[kTopP1_CubicCtrlPts],
- fCtrlPoints[kTopP2_CubicCtrlPts],
- fCtrlPoints[kTopP2_CubicCtrlPts]),
- fLeft(fCtrlPoints[kLeftP0_CubicCtrlPts],
- fCtrlPoints[kLeftP1_CubicCtrlPts],
- fCtrlPoints[kLeftP2_CubicCtrlPts],
- fCtrlPoints[kLeftP3_CubicCtrlPts]),
- fRight(fCtrlPoints[kRightP0_CubicCtrlPts],
- fCtrlPoints[kRightP1_CubicCtrlPts],
- fCtrlPoints[kRightP2_CubicCtrlPts],
- fCtrlPoints[kRightP3_CubicCtrlPts]);
+ SkPoint pts[4];
+ this->getBottomPoints(pts);
+ FwDCubicEvaluator fBottom(pts);
+ this->getTopPoints(pts);
+ FwDCubicEvaluator fTop(pts);
+ this->getLeftPoints(pts);
+ FwDCubicEvaluator fLeft(pts);
+ this->getRightPoints(pts);
+ FwDCubicEvaluator fRight(pts);
- fBottom.restart(divX);
- fTop.restart(divX);
+ fBottom.restart(lodX);
+ fTop.restart(lodX);
SkScalar u = 0.0f;
- int stride = divY + 1;
- for (int x = 0; x <= divX; x++) {
+ int stride = lodY + 1;
+ for (int x = 0; x <= lodX; x++) {
SkPoint bottom = fBottom.next(), top = fTop.next();
- fLeft.restart(divY);
- fRight.restart(divY);
+ fLeft.restart(lodY);
+ fRight.restart(lodY);
SkScalar v = 0.f;
- for (int y = 0; y <= divY; y++) {
- int dataIndex = x * (divX + 1) + y;
+ for (int y = 0; y <= lodY; y++) {
+ int dataIndex = x * (lodY + 1) + y;
SkPoint left = fLeft.next(), right = fRight.next();
@@ -184,31 +198,31 @@
data->fPoints[dataIndex] = s0 + s1 - s2;
uint8_t a = bilinear(u, v,
- SkScalar(SkColorGetA(fCornerColors[0])),
- SkScalar(SkColorGetA(fCornerColors[1])),
- SkScalar(SkColorGetA(fCornerColors[2])),
- SkScalar(SkColorGetA(fCornerColors[3])));
+ SkScalar(SkColorGetA(colors[kTopLeft_CornerColors])),
+ SkScalar(SkColorGetA(colors[kTopRight_CornerColors])),
+ SkScalar(SkColorGetA(colors[kBottomLeft_CornerColors])),
+ SkScalar(SkColorGetA(colors[kBottomRight_CornerColors])));
uint8_t r = bilinear(u, v,
- SkScalar(SkColorGetR(fCornerColors[0])),
- SkScalar(SkColorGetR(fCornerColors[1])),
- SkScalar(SkColorGetR(fCornerColors[2])),
- SkScalar(SkColorGetR(fCornerColors[3])));
+ SkScalar(SkColorGetR(colors[kTopLeft_CornerColors])),
+ SkScalar(SkColorGetR(colors[kTopRight_CornerColors])),
+ SkScalar(SkColorGetR(colors[kBottomLeft_CornerColors])),
+ SkScalar(SkColorGetR(colors[kBottomRight_CornerColors])));
uint8_t g = bilinear(u, v,
- SkScalar(SkColorGetG(fCornerColors[0])),
- SkScalar(SkColorGetG(fCornerColors[1])),
- SkScalar(SkColorGetG(fCornerColors[2])),
- SkScalar(SkColorGetG(fCornerColors[3])));
+ SkScalar(SkColorGetG(colors[kTopLeft_CornerColors])),
+ SkScalar(SkColorGetG(colors[kTopRight_CornerColors])),
+ SkScalar(SkColorGetG(colors[kBottomLeft_CornerColors])),
+ SkScalar(SkColorGetG(colors[kBottomRight_CornerColors])));
uint8_t b = bilinear(u, v,
- SkScalar(SkColorGetB(fCornerColors[0])),
- SkScalar(SkColorGetB(fCornerColors[1])),
- SkScalar(SkColorGetB(fCornerColors[2])),
- SkScalar(SkColorGetB(fCornerColors[3])));
+ SkScalar(SkColorGetB(colors[kTopLeft_CornerColors])),
+ SkScalar(SkColorGetB(colors[kTopRight_CornerColors])),
+ SkScalar(SkColorGetB(colors[kBottomLeft_CornerColors])),
+ SkScalar(SkColorGetB(colors[kBottomRight_CornerColors])));
data->fColors[dataIndex] = SkPackARGB32(a,r,g,b);
data->fTexCoords[dataIndex] = SkPoint::Make(u, v);
- if(x < divX && y < divY) {
- int i = 6 * (x * divY + y);
+ if(x < lodX && y < lodY) {
+ int i = 6 * (x * lodY + y);
data->fIndices[i] = x * stride + y;
data->fIndices[i + 1] = x * stride + 1 + y;
data->fIndices[i + 2] = (x + 1) * stride + 1 + y;
@@ -216,9 +230,9 @@
data->fIndices[i + 4] = data->fIndices[i + 2];
data->fIndices[i + 5] = (x + 1) * stride + y;
}
- v += 1.f / divY;
+ v = SkScalarClampMax(v + 1.f / lodY, 1);
}
- u += 1.f / divX;
+ u = SkScalarClampMax(u + 1.f / lodX, 1);
}
return true;
}
diff --git a/src/utils/SkDeferredCanvas.cpp b/src/utils/SkDeferredCanvas.cpp
index 19957fd..2820bac 100644
--- a/src/utils/SkDeferredCanvas.cpp
+++ b/src/utils/SkDeferredCanvas.cpp
@@ -224,6 +224,8 @@
SkXfermode* xmode, const uint16_t indices[],
int indexCount, const SkPaint& paint) SK_OVERRIDE
{SkASSERT(0);}
+ virtual void drawPatch(const SkDraw&, const SkPatch& patch, const SkPaint& paint) SK_OVERRIDE
+ {SkASSERT(0);}
virtual void drawDevice(const SkDraw&, SkBaseDevice*, int x, int y,
const SkPaint&) SK_OVERRIDE
{SkASSERT(0);}
@@ -914,6 +916,10 @@
this->recordedDrawCommand();
}
+void SkDeferredCanvas::drawPatch(const SkPatch& patch, const SkPaint& paint) {
+ //TODO
+}
+
SkDrawFilter* SkDeferredCanvas::setDrawFilter(SkDrawFilter* filter) {
this->drawingCanvas()->setDrawFilter(filter);
this->INHERITED::setDrawFilter(filter);
diff --git a/src/utils/SkPatchUtils.cpp b/src/utils/SkPatchUtils.cpp
new file mode 100644
index 0000000..7dd0a09
--- /dev/null
+++ b/src/utils/SkPatchUtils.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkPatchUtils.h"
+
+#include "SkGeometry.h"
+
+// size in pixels of each partition per axis, adjust this knob
+static const int kPartitionSize = 30;
+
+/**
+ * Calculate the approximate arc length given a bezier curve's control points.
+ */
+static SkScalar approx_arc_length(SkPoint* points, int count) {
+ if (count < 2) {
+ return 0;
+ }
+ SkScalar arcLength = 0;
+ for (int i = 0; i < count - 1; i++) {
+ arcLength += SkPoint::Distance(points[i], points[i + 1]);
+ }
+ return arcLength;
+}
+
+SkISize SkPatchUtils::GetLevelOfDetail(const SkPatch& patch, const SkMatrix* matrix) {
+
+ SkPoint mapPts[12];
+ matrix->mapPoints(mapPts, patch.getControlPoints(), 12);
+
+ // Approximate length of each cubic.
+ SkPoint pts[4];
+ patch.getTopPoints(pts);
+ matrix->mapPoints(pts, 4);
+ SkScalar topLength = approx_arc_length(pts, 4);
+
+ patch.getBottomPoints(pts);
+ matrix->mapPoints(pts, 4);
+ SkScalar bottomLength = approx_arc_length(pts, 4);
+
+ patch.getLeftPoints(pts);
+ matrix->mapPoints(pts, 4);
+ SkScalar leftLength = approx_arc_length(pts, 4);
+
+ patch.getRightPoints(pts);
+ matrix->mapPoints(pts, 4);
+ SkScalar rightLength = approx_arc_length(pts, 4);
+
+ // Level of detail per axis, based on the larger side between top and bottom or left and right
+ int lodX = static_cast<int>(SkMaxScalar(topLength, bottomLength) / kPartitionSize);
+ int lodY = static_cast<int>(SkMaxScalar(leftLength, rightLength) / kPartitionSize);
+
+ return SkISize::Make(SkMax32(4, lodX), SkMax32(4, lodY));
+}
diff --git a/src/utils/SkPatchUtils.h b/src/utils/SkPatchUtils.h
new file mode 100644
index 0000000..f2e3b43
--- /dev/null
+++ b/src/utils/SkPatchUtils.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkPatchUtils_DEFINED
+#define SkPatchUtils_DEFINED
+
+#include "SkPatch.h"
+#include "SkMatrix.h"
+
+class SK_API SkPatchUtils {
+
+public:
+ /**
+ * Method that calculates a level of detail (number of subdivisions) for a patch in both axis.
+ */
+ static SkISize GetLevelOfDetail(const SkPatch& patch, const SkMatrix* matrix);
+};
+
+#endif