--- /dev/null
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkPaint.h"
+#include "SkUnitMappers.h"
+#include "SkCubicInterval.h"
+
+class UnitMapperView : public SkView {
+ SkPoint fPts[4];
+ SkMatrix fMatrix;
+public:
+ UnitMapperView() {
+ fPts[0].set(0, 0);
+ fPts[1].set(SK_Scalar1 / 3, SK_Scalar1 / 3);
+ fPts[2].set(SK_Scalar1 * 2 / 3, SK_Scalar1 * 2 / 3);
+ fPts[3].set(SK_Scalar1, SK_Scalar1);
+
+ fMatrix.setScale(SK_Scalar1 * 200, -SK_Scalar1 * 200);
+ fMatrix.postTranslate(SkIntToScalar(100), SkIntToScalar(300));
+ }
+
+protected:
+ // overrides from SkEventSink
+ virtual bool onQuery(SkEvent* evt) {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "UnitMapper");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ void drawBG(SkCanvas* canvas) {
+ canvas->drawColor(SK_ColorWHITE);
+ }
+
+ virtual void onDraw(SkCanvas* canvas) {
+ this->drawBG(canvas);
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setColor(0xFF8888FF);
+
+ SkRect r = { 0, 0, SK_Scalar1, SK_Scalar1 };
+
+ canvas->concat(fMatrix);
+ canvas->drawRect(r, paint);
+
+ paint.setColor(SK_ColorBLACK);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(0);
+ paint.setStrokeCap(SkPaint::kRound_Cap);
+
+ SkPath path;
+ path.moveTo(fPts[0]);
+ path.cubicTo(fPts[1], fPts[2], fPts[3]);
+ canvas->drawPath(path, paint);
+
+ paint.setColor(SK_ColorRED);
+ paint.setStrokeWidth(0);
+ canvas->drawLine(0, 0, SK_Scalar1, SK_Scalar1, paint);
+
+ paint.setColor(SK_ColorBLUE);
+ paint.setStrokeWidth(SK_Scalar1 / 60);
+ for (int i = 0; i < 50; i++) {
+ SkScalar x = i * SK_Scalar1 / 49;
+ canvas->drawPoint(x, SkEvalCubicInterval(&fPts[1], x), paint);
+ }
+
+ paint.setStrokeWidth(SK_Scalar1 / 20);
+ paint.setColor(SK_ColorGREEN);
+ canvas->drawPoints(SkCanvas::kPoints_PointMode, 2, &fPts[1], paint);
+ }
+
+ SkPoint invertPt(SkScalar x, SkScalar y) {
+ SkPoint pt;
+ SkMatrix m;
+ fMatrix.invert(&m);
+ m.mapXY(x, y, &pt);
+ return pt;
+ }
+
+ SkPoint* hittest(SkScalar x, SkScalar y) {
+ SkPoint target = { x, y };
+ SkPoint pts[2] = { fPts[1], fPts[2] };
+ fMatrix.mapPoints(pts, 2);
+ for (int i = 0; i < 2; i++) {
+ if (SkPoint::Distance(pts[i], target) < SkIntToScalar(4)) {
+ return &fPts[i + 1];
+ }
+ }
+ return NULL;
+ }
+
+ virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+ fDragPt = hittest(x, y);
+ return fDragPt ? new Click(this) : NULL;
+ }
+
+ virtual bool onClick(Click* click) {
+ if (fDragPt) {
+ *fDragPt = invertPt(click->fCurr.fX, click->fCurr.fY);
+ this->inval(NULL);
+ return true;
+ }
+ return false;
+ }
+
+private:
+ SkPoint* fDragPt;
+
+ typedef SkView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new UnitMapperView; }
+static SkViewRegister reg(MyFactory);
+
--- /dev/null
+#include "SkCubicInterval.h"
+
+static SkScalar eval_cubic(SkScalar c1, SkScalar c2, SkScalar c3,
+ SkScalar t) {
+ return SkScalarMul(SkScalarMul(SkScalarMul(c3, t) + c2, t) + c1, t);
+}
+
+static SkScalar find_cubic_t(SkScalar c1, SkScalar c2, SkScalar c3,
+ SkScalar targetX) {
+ SkScalar minT = 0;
+ SkScalar maxT = SK_Scalar1;
+ SkScalar t;
+
+ for (;;) {
+ t = SkScalarAve(minT, maxT);
+ SkScalar x = eval_cubic(c1, c2, c3, t);
+ if (SkScalarNearlyZero(x - targetX)) {
+ break;
+ }
+ // subdivide the range and try again
+ if (x < targetX) {
+ minT = t;
+ } else {
+ maxT = t;
+ }
+ }
+ return t;
+}
+
+/*
+ a(1-t)^3 + 3bt(1-t)^2 + 3ct^2(1-t) + dt^3
+ a: [0, 0]
+ d: [1, 1]
+
+ 3bt - 6bt^2 + 3bt^3 + 3ct^2 - 3ct^3 + t^3
+ C1 = t^1: 3b
+ C2 = t^2: 3c - 6b
+ C3 = t^3: 3b - 3c + 1
+
+ ((C3*t + C2)*t + C1)*t
+ */
+SkScalar SkEvalCubicInterval(SkScalar x1, SkScalar y1,
+ SkScalar x2, SkScalar y2,
+ SkScalar unitX) {
+ x1 = SkScalarPin(x1, 0, SK_Scalar1);
+ x2 = SkScalarPin(x2, 0, SK_Scalar1);
+ unitX = SkScalarPin(unitX, 0, SK_Scalar1);
+
+ // First compute our coefficients in X
+ x1 *= 3;
+ x2 *= 3;
+
+ // now search for t given unitX
+ SkScalar t = find_cubic_t(x1, x2 - 2*x1, x1 - x2 + SK_Scalar1, unitX);
+
+ // now evaluate the cubic in Y
+ y1 *= 3;
+ y2 *= 3;
+ return eval_cubic(y1, y2 - 2*y1, y1 - y2 + SK_Scalar1, t);
+}
+