blob: c7adad94909d9cd84e389b1f8df69b6bf75a68c6 [file] [log] [blame]
reed@android.comd2abab62009-10-20 21:27:15 +00001#include "SampleCode.h"
2#include "SkView.h"
3#include "SkCanvas.h"
4#include "SkGradientShader.h"
5#include "SkPath.h"
6#include "SkRegion.h"
7#include "SkShader.h"
8#include "SkUtils.h"
9#include "SkImageDecoder.h"
10
reed@android.com4408cca2009-10-27 02:24:03 +000011static SkPoint SkMakePoint(SkScalar x, SkScalar y) {
12 SkPoint pt;
13 pt.set(x, y);
14 return pt;
15}
16
17static SkPoint SkPointInterp(const SkPoint& a, const SkPoint& b, SkScalar t) {
18 return SkMakePoint(SkScalarInterp(a.fX, b.fX, t),
19 SkScalarInterp(a.fY, b.fY, t));
20}
21
22#include "SkBoundaryPatch.h"
23
24static void set_pts(SkPoint pts[], int R, int C, SkBoundaryPatch* patch) {
25 SkScalar invR = SkScalarInvert(SkIntToScalar(R - 1));
26 SkScalar invC = SkScalarInvert(SkIntToScalar(C - 1));
27
28 for (int y = 0; y < C; y++) {
29 SkScalar yy = y * invC;
30 for (int x = 0; x < R; x++) {
31 *pts++ = patch->evaluate(x * invR, yy);
32 }
33 }
34}
35
36static void set_cubic(SkPoint pts[4], SkScalar x0, SkScalar y0,
37 SkScalar x3, SkScalar y3, SkScalar scale = 1) {
38 SkPoint tmp, tmp2;
39
40 pts[0].set(x0, y0);
41 pts[3].set(x3, y3);
42
43 tmp = SkPointInterp(pts[0], pts[3], SK_Scalar1/3);
44 tmp2 = pts[0] - tmp;
45 tmp2.rotateCW();
46 tmp2.scale(scale);
47 pts[1] = tmp + tmp2;
48
49 tmp = SkPointInterp(pts[0], pts[3], 2*SK_Scalar1/3);
50 tmp2 = pts[3] - tmp;
51 tmp2.rotateCW();
52 tmp2.scale(scale);
53 pts[2] = tmp + tmp2;
54}
55
56static void draw_texture(SkCanvas* canvas, const SkPoint verts[], int R, int C,
57 const SkBitmap& texture) {
58 int vertCount = R * C;
59 const int rows = R - 1;
60 const int cols = C - 1;
61 int idxCount = rows * cols * 6;
62
63 SkAutoTArray<SkPoint> texStorage(vertCount);
64 SkPoint* tex = texStorage.get();
65 SkAutoTArray<uint16_t> idxStorage(idxCount);
66 uint16_t* idx = idxStorage.get();
67
68
69 const SkScalar dtx = texture.width() / rows;
70 const SkScalar dty = texture.height() / cols;
71 int index = 0;
72 for (int y = 0; y <= cols; y++) {
73 for (int x = 0; x <= rows; x++) {
74 tex->set(x*dtx, y*dty);
75 tex += 1;
76
77 if (y < cols && x < rows) {
78 *idx++ = index;
79 *idx++ = index + rows + 1;
80 *idx++ = index + 1;
81
82 *idx++ = index + 1;
83 *idx++ = index + rows + 1;
84 *idx++ = index + rows + 2;
85
86 index += 1;
87 }
88 }
89 index += 1;
90 }
91
92 SkPaint paint;
93 paint.setShader(SkShader::CreateBitmapShader(texture,
94 SkShader::kClamp_TileMode,
95 SkShader::kClamp_TileMode))->unref();
96
97 canvas->drawVertices(SkCanvas::kTriangles_VertexMode, vertCount, verts,
98 texStorage.get(), NULL, NULL, idxStorage.get(),
99 idxCount, paint);
100}
101
102static void test_patch(SkCanvas* canvas, const SkBitmap& bm, SkScalar scale) {
103 SkCubicBoundaryCurve L, T, R, B;
104
105 set_cubic(L.fPts, 0, 0, 0, 100, scale);
106 set_cubic(T.fPts, 0, 0, 100, 0, scale);
107 set_cubic(R.fPts, 100, 0, 100, 100, -scale);
108 set_cubic(B.fPts, 0, 100, 100, 100, 0);
109
110 SkBoundaryPatch patch;
111 patch.setCurve(SkBoundaryPatch::kLeft, &L);
112 patch.setCurve(SkBoundaryPatch::kTop, &T);
113 patch.setCurve(SkBoundaryPatch::kRight, &R);
114 patch.setCurve(SkBoundaryPatch::kBottom, &B);
115
116 const int Rows = 25;
117 const int Cols = 25;
118 SkPoint pts[Rows * Cols];
119 set_pts(pts, Rows, Cols, &patch);
120
121 SkPaint paint;
122 paint.setAntiAlias(true);
123 paint.setStrokeWidth(1);
124 paint.setStrokeCap(SkPaint::kRound_Cap);
125
126 canvas->translate(50, 50);
127 canvas->scale(3, 3);
128
129 draw_texture(canvas, pts, Rows, Cols, bm);
130// canvas->drawPoints(SkCanvas::kPoints_PointMode, SK_ARRAY_COUNT(pts),
131// pts, paint);
132}
reed@android.com80b4ebe2009-10-21 19:41:10 +0000133
134///////////////////////////////////////////////////////////////////////////////
135
reed@android.comd2abab62009-10-20 21:27:15 +0000136class Mesh {
137public:
138 Mesh();
139 ~Mesh();
140
141 Mesh& operator=(const Mesh& src);
142
143 void init(const SkRect& bounds, int rows, int cols,
144 const SkRect& texture);
145
reed@android.com80b4ebe2009-10-21 19:41:10 +0000146 const SkRect& bounds() const { return fBounds; }
147
reed@android.comd2abab62009-10-20 21:27:15 +0000148 int rows() const { return fRows; }
149 int cols() const { return fCols; }
150 SkPoint& pt(int row, int col) {
151 return fPts[row * (fRows + 1) + col];
152 }
153
154 void draw(SkCanvas*, const SkPaint&);
155 void drawWireframe(SkCanvas* canvas, const SkPaint& paint);
156
157private:
reed@android.com80b4ebe2009-10-21 19:41:10 +0000158 SkRect fBounds;
reed@android.comd2abab62009-10-20 21:27:15 +0000159 int fRows, fCols;
160 SkPoint* fPts;
161 SkPoint* fTex; // just points into fPts, not separately allocated
162 int fCount;
163 uint16_t* fIndices;
164 int fIndexCount;
165};
166
167Mesh::Mesh() : fPts(NULL), fCount(0), fIndices(NULL), fIndexCount(0) {}
168
169Mesh::~Mesh() {
170 delete[] fPts;
171 delete[] fIndices;
172}
173
174Mesh& Mesh::operator=(const Mesh& src) {
175 delete[] fPts;
176 delete[] fIndices;
177
reed@android.com80b4ebe2009-10-21 19:41:10 +0000178 fBounds = src.fBounds;
reed@android.comd2abab62009-10-20 21:27:15 +0000179 fRows = src.fRows;
180 fCols = src.fCols;
181
182 fCount = src.fCount;
183 fPts = new SkPoint[fCount * 2];
184 fTex = fPts + fCount;
185 memcpy(fPts, src.fPts, fCount * 2 * sizeof(SkPoint));
186
187 delete[] fIndices;
188 fIndexCount = src.fIndexCount;
189 fIndices = new uint16_t[fIndexCount];
190 memcpy(fIndices, src.fIndices, fIndexCount * sizeof(uint16_t));
191
192 return *this;
193}
194
195void Mesh::init(const SkRect& bounds, int rows, int cols,
196 const SkRect& texture) {
197 SkASSERT(rows > 0 && cols > 0);
198
reed@android.com80b4ebe2009-10-21 19:41:10 +0000199 fBounds = bounds;
reed@android.comd2abab62009-10-20 21:27:15 +0000200 fRows = rows;
201 fCols = cols;
202
203 delete[] fPts;
204 fCount = (rows + 1) * (cols + 1);
205 fPts = new SkPoint[fCount * 2];
206 fTex = fPts + fCount;
207
208 delete[] fIndices;
209 fIndexCount = rows * cols * 6;
210 fIndices = new uint16_t[fIndexCount];
211
212 SkPoint* pts = fPts;
213 const SkScalar dx = bounds.width() / rows;
214 const SkScalar dy = bounds.height() / cols;
215 SkPoint* tex = fTex;
216 const SkScalar dtx = texture.width() / rows;
217 const SkScalar dty = texture.height() / cols;
218 uint16_t* idx = fIndices;
219 int index = 0;
220 for (int y = 0; y <= cols; y++) {
221 for (int x = 0; x <= rows; x++) {
222 pts->set(bounds.fLeft + x*dx, bounds.fTop + y*dy);
223 pts += 1;
224 tex->set(texture.fLeft + x*dtx, texture.fTop + y*dty);
225 tex += 1;
226
227 if (y < cols && x < rows) {
228 *idx++ = index;
229 *idx++ = index + rows + 1;
230 *idx++ = index + 1;
231
232 *idx++ = index + 1;
233 *idx++ = index + rows + 1;
234 *idx++ = index + rows + 2;
235
236 index += 1;
237 }
238 }
239 index += 1;
240 }
241}
242
243void Mesh::draw(SkCanvas* canvas, const SkPaint& paint) {
244 canvas->drawVertices(SkCanvas::kTriangles_VertexMode, fCount,
245 fPts, fTex, NULL, NULL, fIndices, fIndexCount,
246 paint);
247}
248
249void Mesh::drawWireframe(SkCanvas* canvas, const SkPaint& paint) {
250 canvas->drawVertices(SkCanvas::kTriangles_VertexMode, fCount,
251 fPts, NULL, NULL, NULL, fIndices, fIndexCount,
252 paint);
253}
254
255///////////////////////////////////////////////////////////////////////////////
256
reed@android.com4408cca2009-10-27 02:24:03 +0000257static SkScalar gScale = 0;
258static SkScalar gDScale = 0.01;
259
reed@android.comd2abab62009-10-20 21:27:15 +0000260class WarpView : public SkView {
261 Mesh fMesh, fOrig;
262 SkBitmap fBitmap;
263public:
264 WarpView() {
265 SkBitmap bm;
reed@android.com4408cca2009-10-27 02:24:03 +0000266 // SkImageDecoder::DecodeFile("/skimages/beach.jpg", &bm);
267 SkImageDecoder::DecodeFile("/beach_shot.JPG", &bm);
268 fBitmap = bm;
reed@android.comd2abab62009-10-20 21:27:15 +0000269
270 SkRect bounds, texture;
271 texture.set(0, 0, SkIntToScalar(fBitmap.width()),
272 SkIntToScalar(fBitmap.height()));
273 bounds = texture;
274
275// fMesh.init(bounds, fBitmap.width() / 40, fBitmap.height() / 40, texture);
reed@android.com80b4ebe2009-10-21 19:41:10 +0000276 fMesh.init(bounds, 30, 30, texture);
reed@android.comd2abab62009-10-20 21:27:15 +0000277 fOrig = fMesh;
278 }
279
280protected:
281 // overrides from SkEventSink
282 virtual bool onQuery(SkEvent* evt) {
283 if (SampleCode::TitleQ(*evt)) {
284 SampleCode::TitleR(evt, "Warp");
285 return true;
286 }
287 return this->INHERITED::onQuery(evt);
288 }
289
reed@android.com80b4ebe2009-10-21 19:41:10 +0000290 static SkPoint make_pt(SkScalar x, SkScalar y) {
291 SkPoint pt;
292 pt.set(x, y);
293 return pt;
294 }
295
296 static SkScalar mapx0(SkScalar min, SkScalar max, SkScalar x0, SkScalar x1,
297 SkScalar x) {
298 if (x < x0) {
299 SkASSERT(x0 > min);
300 return x1 - SkScalarMulDiv(x1 - min, x0 - x, x0 - min);
301 } else {
302 SkASSERT(max > x0);
303 return x1 + SkScalarMulDiv(max - x1, x - x0, max - x0);
304 }
305 }
306
307 static SkScalar mapx1(SkScalar min, SkScalar max, SkScalar x0, SkScalar x1,
308 SkScalar x) {
309 SkScalar newx;
310 if (x < x0) {
311 SkASSERT(x0 > min);
312 newx = x1 - SkScalarMulDiv(x1 - min, x0 - x, x0 - min);
313 } else {
314 SkASSERT(max > x0);
315 newx = x1 + SkScalarMulDiv(max - x1, x - x0, max - x0);
316 }
317 return x + (newx - x) * 0.5f;
318 }
319
320 static SkPoint mappt(const SkRect& r, const SkPoint& p0, const SkPoint& p1,
321 const SkPoint& pt) {
322 return make_pt(mapx0(r.fLeft, r.fRight, p0.fX, p1.fX, pt.fX),
323 mapx0(r.fTop, r.fBottom, p0.fY, p1.fY, pt.fY));
324 }
325
reed@android.comd2abab62009-10-20 21:27:15 +0000326 void warp(const SkPoint& p0, const SkPoint& p1) {
reed@android.com80b4ebe2009-10-21 19:41:10 +0000327 const SkRect& bounds = fOrig.bounds();
reed@android.comd2abab62009-10-20 21:27:15 +0000328 int rows = fMesh.rows();
329 int cols = fMesh.cols();
330
reed@android.com80b4ebe2009-10-21 19:41:10 +0000331 SkRect r = bounds;
332 r.inset(bounds.width() / 256, bounds.height() / 256);
333 if (r.contains(p0)) {
334 for (int y = 1; y < cols; y++) {
335 for (int x = 1; x < rows; x++) {
336 fMesh.pt(x, y) = mappt(bounds, p0, p1, fOrig.pt(x, y));
337 }
reed@android.comd2abab62009-10-20 21:27:15 +0000338 }
339 }
340 }
341
342 virtual void onDraw(SkCanvas* canvas) {
343 canvas->drawColor(SK_ColorGRAY);
344
345 SkPaint paint;
346 paint.setFilterBitmap(true);
347 paint.setShader(SkShader::CreateBitmapShader(fBitmap,
348 SkShader::kClamp_TileMode,
349 SkShader::kClamp_TileMode))->unref();
reed@android.com4408cca2009-10-27 02:24:03 +0000350 // fMesh.draw(canvas, paint);
reed@android.comd2abab62009-10-20 21:27:15 +0000351
352 paint.setShader(NULL);
353 paint.setColor(SK_ColorRED);
reed@android.com80b4ebe2009-10-21 19:41:10 +0000354 // fMesh.draw(canvas, paint);
reed@android.com4408cca2009-10-27 02:24:03 +0000355
356 test_patch(canvas, fBitmap, gScale);
357 gScale += gDScale;
358 if (gScale > 2) {
359 gDScale = -gDScale;
360 } else if (gScale < -2) {
361 gDScale = -gDScale;
362 }
363 this->inval(NULL);
reed@android.comd2abab62009-10-20 21:27:15 +0000364 }
365
366 virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
367 return new Click(this);
368 }
369
370 virtual bool onClick(Click* click) {
371 this->warp(click->fOrig, click->fCurr);
372 this->inval(NULL);
373 return true;
374 }
375
376private:
377 SkIRect fBase, fRect;
378
379 typedef SkView INHERITED;
380};
381
382//////////////////////////////////////////////////////////////////////////////
383
384static SkView* MyFactory() { return new WarpView; }
385static SkViewRegister reg(MyFactory);
386