blob: b421c60bfd17ee48009204ec7f33f2ab02f47853 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
reed@android.comd2abab62009-10-20 21:27:15 +00008#include "SampleCode.h"
9#include "SkView.h"
10#include "SkCanvas.h"
11#include "SkGradientShader.h"
12#include "SkPath.h"
13#include "SkRegion.h"
14#include "SkShader.h"
15#include "SkUtils.h"
16#include "SkImageDecoder.h"
17
reed@android.com3f2025f2009-10-29 17:37:56 +000018#include "SkBlurMaskFilter.h"
19#include "SkTableMaskFilter.h"
20
reed@android.com2736a692010-01-28 21:24:01 +000021#define kNearlyZero (SK_Scalar1 / 8092)
22
reed@android.com3f2025f2009-10-29 17:37:56 +000023static void test_bigblur(SkCanvas* canvas) {
24 canvas->drawColor(SK_ColorBLACK);
25
26 SkBitmap orig, mask;
27 SkImageDecoder::DecodeFile("/skimages/app_icon.png", &orig);
28
29 SkMaskFilter* mf = SkBlurMaskFilter::Create(8, SkBlurMaskFilter::kNormal_BlurStyle);
30 SkPaint paint;
31 paint.setMaskFilter(mf)->unref();
32 SkIPoint offset;
33 orig.extractAlpha(&mask, &paint, &offset);
34
35 paint.setColor(0xFFBB8800);
36 paint.setColor(SK_ColorWHITE);
37
38 int i;
39 canvas->save();
40 float gamma = 0.8;
41 for (i = 0; i < 5; i++) {
42 paint.setMaskFilter(SkTableMaskFilter::CreateGamma(gamma))->unref();
43 canvas->drawBitmap(mask, 0, 0, &paint);
44 paint.setMaskFilter(NULL);
45 canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint);
46 gamma -= 0.1;
47 canvas->translate(120, 0);
48 }
49 canvas->restore();
50 canvas->translate(0, 160);
51
52 for (i = 0; i < 5; i++) {
53 paint.setMaskFilter(SkTableMaskFilter::CreateClip(i*30, 255 - 20))->unref();
54 canvas->drawBitmap(mask, 0, 0, &paint);
55 paint.setMaskFilter(NULL);
56 canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint);
57 canvas->translate(120, 0);
58 }
59
60#if 0
61 paint.setColor(0xFFFFFFFF);
62 canvas->drawBitmap(mask, 0, 0, &paint);
63 paint.setMaskFilter(NULL);
64 canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint);
65
66 canvas->translate(120, 0);
67
68 canvas->drawBitmap(mask, 0, 0, &paint);
69 canvas->drawBitmap(mask, 0, 0, &paint);
70 canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint);
71
72 canvas->translate(120, 0);
73
74 canvas->drawBitmap(mask, 0, 0, &paint);
75 canvas->drawBitmap(mask, 0, 0, &paint);
76 canvas->drawBitmap(mask, 0, 0, &paint);
77 canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint);
78
79 canvas->translate(120, 0);
80
81 canvas->drawBitmap(mask, 0, 0, &paint);
82 canvas->drawBitmap(mask, 0, 0, &paint);
83 canvas->drawBitmap(mask, 0, 0, &paint);
84 canvas->drawBitmap(mask, 0, 0, &paint);
85 canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint);
86
87 canvas->translate(120, 0);
88
89 canvas->drawBitmap(mask, 0, 0, &paint);
90 canvas->drawBitmap(mask, 0, 0, &paint);
91 canvas->drawBitmap(mask, 0, 0, &paint);
92 canvas->drawBitmap(mask, 0, 0, &paint);
93 canvas->drawBitmap(mask, 0, 0, &paint);
94 canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint);
95#endif
96}
97
reed@android.com2ee7c642009-10-28 14:25:34 +000098#include "SkMeshUtils.h"
99
reed@android.com4408cca2009-10-27 02:24:03 +0000100static SkPoint SkMakePoint(SkScalar x, SkScalar y) {
101 SkPoint pt;
102 pt.set(x, y);
103 return pt;
104}
105
106static SkPoint SkPointInterp(const SkPoint& a, const SkPoint& b, SkScalar t) {
107 return SkMakePoint(SkScalarInterp(a.fX, b.fX, t),
108 SkScalarInterp(a.fY, b.fY, t));
109}
110
111#include "SkBoundaryPatch.h"
112
reed@android.com4408cca2009-10-27 02:24:03 +0000113static void set_cubic(SkPoint pts[4], SkScalar x0, SkScalar y0,
114 SkScalar x3, SkScalar y3, SkScalar scale = 1) {
115 SkPoint tmp, tmp2;
116
117 pts[0].set(x0, y0);
118 pts[3].set(x3, y3);
119
120 tmp = SkPointInterp(pts[0], pts[3], SK_Scalar1/3);
121 tmp2 = pts[0] - tmp;
122 tmp2.rotateCW();
123 tmp2.scale(scale);
124 pts[1] = tmp + tmp2;
125
126 tmp = SkPointInterp(pts[0], pts[3], 2*SK_Scalar1/3);
127 tmp2 = pts[3] - tmp;
128 tmp2.rotateCW();
129 tmp2.scale(scale);
130 pts[2] = tmp + tmp2;
131}
132
reed@android.com4408cca2009-10-27 02:24:03 +0000133static void test_patch(SkCanvas* canvas, const SkBitmap& bm, SkScalar scale) {
reed@android.com2ee7c642009-10-28 14:25:34 +0000134 SkCubicBoundary cubic;
135 set_cubic(cubic.fPts + 0, 0, 0, 100, 0, scale);
136 set_cubic(cubic.fPts + 3, 100, 0, 100, 100, scale);
137 set_cubic(cubic.fPts + 6, 100, 100, 0, 100, -scale);
138 set_cubic(cubic.fPts + 9, 0, 100, 0, 0, 0);
reed@android.com4408cca2009-10-27 02:24:03 +0000139
reed@android.com4408cca2009-10-27 02:24:03 +0000140 SkBoundaryPatch patch;
reed@android.com2ee7c642009-10-28 14:25:34 +0000141 patch.setBoundary(&cubic);
142
143 const int Rows = 16;
144 const int Cols = 16;
reed@android.com4408cca2009-10-27 02:24:03 +0000145 SkPoint pts[Rows * Cols];
reed@android.com2ee7c642009-10-28 14:25:34 +0000146 patch.evalPatch(pts, Rows, Cols);
reed@android.com4408cca2009-10-27 02:24:03 +0000147
148 SkPaint paint;
149 paint.setAntiAlias(true);
reed@android.com2ee7c642009-10-28 14:25:34 +0000150 paint.setFilterBitmap(true);
reed@android.com4408cca2009-10-27 02:24:03 +0000151 paint.setStrokeWidth(1);
152 paint.setStrokeCap(SkPaint::kRound_Cap);
reed@android.com2ee7c642009-10-28 14:25:34 +0000153
reed@android.com4408cca2009-10-27 02:24:03 +0000154 canvas->translate(50, 50);
155 canvas->scale(3, 3);
reed@android.com2ee7c642009-10-28 14:25:34 +0000156
157 SkMeshUtils::Draw(canvas, bm, Rows, Cols, pts, NULL, paint);
158}
reed@android.com4408cca2009-10-27 02:24:03 +0000159
reed@android.com2ee7c642009-10-28 14:25:34 +0000160static void test_drag(SkCanvas* canvas, const SkBitmap& bm,
161 const SkPoint& p0, const SkPoint& p1) {
162 SkCubicBoundary cubic;
163 set_cubic(cubic.fPts + 0, 0, 0, 100, 0, 0);
164 set_cubic(cubic.fPts + 3, 100, 0, 100, 100, 0);
165 set_cubic(cubic.fPts + 6, 100, 100, 0, 100, 0);
166 set_cubic(cubic.fPts + 9, 0, 100, 0, 0, 0);
167
168#if 0
169 cubic.fPts[1] += p1 - p0;
170 cubic.fPts[2] += p1 - p0;
171#else
172 SkScalar dx = p1.fX - p0.fX;
173 if (dx > 0) dx = 0;
174 SkScalar dy = p1.fY - p0.fY;
175 if (dy > 0) dy = 0;
176
177 cubic.fPts[1].fY += dy;
178 cubic.fPts[2].fY += dy;
179 cubic.fPts[10].fX += dx;
180 cubic.fPts[11].fX += dx;
181#endif
182
183 SkBoundaryPatch patch;
184 patch.setBoundary(&cubic);
185
186 const int Rows = 16;
187 const int Cols = 16;
188 SkPoint pts[Rows * Cols];
189 patch.evalPatch(pts, Rows, Cols);
190
191 SkPaint paint;
192 paint.setAntiAlias(true);
193 paint.setFilterBitmap(true);
194 paint.setStrokeWidth(1);
195 paint.setStrokeCap(SkPaint::kRound_Cap);
196
197 canvas->translate(50, 50);
198 canvas->scale(3, 3);
199
200 SkAutoCanvasRestore acr(canvas, true);
201
202 SkRect r = { 0, 0, 100, 100 };
203 canvas->clipRect(r);
204 SkMeshUtils::Draw(canvas, bm, Rows, Cols, pts, NULL, paint);
reed@android.com4408cca2009-10-27 02:24:03 +0000205}
reed@android.com80b4ebe2009-10-21 19:41:10 +0000206
207///////////////////////////////////////////////////////////////////////////////
208
reed@android.comd2abab62009-10-20 21:27:15 +0000209class Mesh {
210public:
211 Mesh();
212 ~Mesh();
213
214 Mesh& operator=(const Mesh& src);
215
216 void init(const SkRect& bounds, int rows, int cols,
217 const SkRect& texture);
218
reed@android.com80b4ebe2009-10-21 19:41:10 +0000219 const SkRect& bounds() const { return fBounds; }
220
reed@android.comd2abab62009-10-20 21:27:15 +0000221 int rows() const { return fRows; }
222 int cols() const { return fCols; }
223 SkPoint& pt(int row, int col) {
224 return fPts[row * (fRows + 1) + col];
225 }
226
227 void draw(SkCanvas*, const SkPaint&);
228 void drawWireframe(SkCanvas* canvas, const SkPaint& paint);
229
230private:
reed@android.com80b4ebe2009-10-21 19:41:10 +0000231 SkRect fBounds;
reed@android.comd2abab62009-10-20 21:27:15 +0000232 int fRows, fCols;
233 SkPoint* fPts;
234 SkPoint* fTex; // just points into fPts, not separately allocated
235 int fCount;
236 uint16_t* fIndices;
237 int fIndexCount;
238};
239
240Mesh::Mesh() : fPts(NULL), fCount(0), fIndices(NULL), fIndexCount(0) {}
241
242Mesh::~Mesh() {
243 delete[] fPts;
244 delete[] fIndices;
245}
246
247Mesh& Mesh::operator=(const Mesh& src) {
248 delete[] fPts;
249 delete[] fIndices;
250
reed@android.com80b4ebe2009-10-21 19:41:10 +0000251 fBounds = src.fBounds;
reed@android.comd2abab62009-10-20 21:27:15 +0000252 fRows = src.fRows;
253 fCols = src.fCols;
254
255 fCount = src.fCount;
256 fPts = new SkPoint[fCount * 2];
257 fTex = fPts + fCount;
258 memcpy(fPts, src.fPts, fCount * 2 * sizeof(SkPoint));
259
260 delete[] fIndices;
261 fIndexCount = src.fIndexCount;
262 fIndices = new uint16_t[fIndexCount];
263 memcpy(fIndices, src.fIndices, fIndexCount * sizeof(uint16_t));
264
265 return *this;
266}
267
268void Mesh::init(const SkRect& bounds, int rows, int cols,
269 const SkRect& texture) {
270 SkASSERT(rows > 0 && cols > 0);
271
reed@android.com80b4ebe2009-10-21 19:41:10 +0000272 fBounds = bounds;
reed@android.comd2abab62009-10-20 21:27:15 +0000273 fRows = rows;
274 fCols = cols;
275
276 delete[] fPts;
277 fCount = (rows + 1) * (cols + 1);
278 fPts = new SkPoint[fCount * 2];
279 fTex = fPts + fCount;
280
281 delete[] fIndices;
282 fIndexCount = rows * cols * 6;
283 fIndices = new uint16_t[fIndexCount];
284
285 SkPoint* pts = fPts;
286 const SkScalar dx = bounds.width() / rows;
287 const SkScalar dy = bounds.height() / cols;
288 SkPoint* tex = fTex;
289 const SkScalar dtx = texture.width() / rows;
290 const SkScalar dty = texture.height() / cols;
291 uint16_t* idx = fIndices;
292 int index = 0;
293 for (int y = 0; y <= cols; y++) {
294 for (int x = 0; x <= rows; x++) {
295 pts->set(bounds.fLeft + x*dx, bounds.fTop + y*dy);
296 pts += 1;
297 tex->set(texture.fLeft + x*dtx, texture.fTop + y*dty);
298 tex += 1;
299
300 if (y < cols && x < rows) {
301 *idx++ = index;
302 *idx++ = index + rows + 1;
303 *idx++ = index + 1;
304
305 *idx++ = index + 1;
306 *idx++ = index + rows + 1;
307 *idx++ = index + rows + 2;
308
309 index += 1;
310 }
311 }
312 index += 1;
313 }
314}
315
316void Mesh::draw(SkCanvas* canvas, const SkPaint& paint) {
317 canvas->drawVertices(SkCanvas::kTriangles_VertexMode, fCount,
318 fPts, fTex, NULL, NULL, fIndices, fIndexCount,
319 paint);
320}
321
322void Mesh::drawWireframe(SkCanvas* canvas, const SkPaint& paint) {
323 canvas->drawVertices(SkCanvas::kTriangles_VertexMode, fCount,
324 fPts, NULL, NULL, NULL, fIndices, fIndexCount,
325 paint);
326}
327
328///////////////////////////////////////////////////////////////////////////////
329
330class WarpView : public SkView {
331 Mesh fMesh, fOrig;
332 SkBitmap fBitmap;
reed@android.com2736a692010-01-28 21:24:01 +0000333 SkMatrix fMatrix, fInverse;
reed@android.comd2abab62009-10-20 21:27:15 +0000334public:
335 WarpView() {
336 SkBitmap bm;
reed@android.com7d970c72010-04-22 16:07:49 +0000337// SkImageDecoder::DecodeFile("/skimages/marker.png", &bm);
reed@android.com2736a692010-01-28 21:24:01 +0000338 SkImageDecoder::DecodeFile("/skimages/logo.gif", &bm);
reed@android.com2ee7c642009-10-28 14:25:34 +0000339 // SkImageDecoder::DecodeFile("/beach_shot.JPG", &bm);
reed@android.com4408cca2009-10-27 02:24:03 +0000340 fBitmap = bm;
reed@android.comd2abab62009-10-20 21:27:15 +0000341
342 SkRect bounds, texture;
343 texture.set(0, 0, SkIntToScalar(fBitmap.width()),
344 SkIntToScalar(fBitmap.height()));
345 bounds = texture;
346
347// fMesh.init(bounds, fBitmap.width() / 40, fBitmap.height() / 40, texture);
reed@android.com2736a692010-01-28 21:24:01 +0000348 fMesh.init(bounds, fBitmap.width()/16, fBitmap.height()/16, texture);
reed@android.comd2abab62009-10-20 21:27:15 +0000349 fOrig = fMesh;
reed@android.com2ee7c642009-10-28 14:25:34 +0000350
351 fP0.set(0, 0);
352 fP1 = fP0;
reed@android.com2736a692010-01-28 21:24:01 +0000353
354 fMatrix.setScale(2, 2);
355 fMatrix.invert(&fInverse);
reed@android.comd2abab62009-10-20 21:27:15 +0000356 }
357
358protected:
359 // overrides from SkEventSink
360 virtual bool onQuery(SkEvent* evt) {
361 if (SampleCode::TitleQ(*evt)) {
362 SampleCode::TitleR(evt, "Warp");
363 return true;
364 }
365 return this->INHERITED::onQuery(evt);
366 }
367
reed@android.com2736a692010-01-28 21:24:01 +0000368 static SkPoint apply_warp(const SkVector& drag, SkScalar dragLength,
369 const SkPoint& dragStart, const SkPoint& dragCurr,
370 const SkPoint& orig) {
371 SkVector delta = orig - dragCurr;
372 SkScalar length = SkPoint::Normalize(&delta);
373 if (length <= kNearlyZero) {
374 return orig;
375 }
376
377 const SkScalar period = 20;
378 const SkScalar mag = dragLength / 3;
379
380 SkScalar d = length / (period);
381 d = mag * SkScalarSin(d) / d;
382 SkScalar dx = delta.fX * d;
383 SkScalar dy = delta.fY * d;
384 SkScalar px = orig.fX + dx;
385 SkScalar py = orig.fY + dy;
386 return SkPoint::Make(px, py);
387 }
388
389 static SkPoint apply_warp2(const SkVector& drag, SkScalar dragLength,
390 const SkPoint& dragStart, const SkPoint& dragCurr,
391 const SkPoint& orig) {
392 SkVector delta = orig - dragCurr;
393 SkScalar length = SkPoint::Normalize(&delta);
394 if (length <= kNearlyZero) {
395 return orig;
396 }
397
reed@android.com7d970c72010-04-22 16:07:49 +0000398 const SkScalar period = 10 + dragLength/4;
reed@android.com2736a692010-01-28 21:24:01 +0000399 const SkScalar mag = dragLength / 3;
400
401 SkScalar d = length / (period);
402 if (d > SK_ScalarPI) {
403 d = SK_ScalarPI;
404 }
405
406 d = -mag * SkScalarSin(d);
407
408 SkScalar dx = delta.fX * d;
409 SkScalar dy = delta.fY * d;
410 SkScalar px = orig.fX + dx;
411 SkScalar py = orig.fY + dy;
412 return SkPoint::Make(px, py);
413 }
414
415 typedef SkPoint (*WarpProc)(const SkVector& drag, SkScalar dragLength,
416 const SkPoint& dragStart, const SkPoint& dragCurr,
417 const SkPoint& orig);
418
reed@android.comd2abab62009-10-20 21:27:15 +0000419 void warp(const SkPoint& p0, const SkPoint& p1) {
reed@android.com2736a692010-01-28 21:24:01 +0000420 WarpProc proc = apply_warp2;
421 SkPoint delta = p1 - p0;
422 SkScalar length = SkPoint::Normalize(&delta);
423 for (int y = 0; y < fMesh.rows(); y++) {
424 for (int x = 0; x < fMesh.cols(); x++) {
425 fMesh.pt(x, y) = proc(delta, length, p0, p1, fOrig.pt(x, y));
426 }
427 }
reed@android.com2ee7c642009-10-28 14:25:34 +0000428 fP0 = p0;
429 fP1 = p1;
reed@android.comd2abab62009-10-20 21:27:15 +0000430 }
431
432 virtual void onDraw(SkCanvas* canvas) {
reed@android.com3f2025f2009-10-29 17:37:56 +0000433 canvas->drawColor(SK_ColorLTGRAY);
434 // test_bigblur(canvas); return;
reed@android.comd2abab62009-10-20 21:27:15 +0000435
reed@android.com2736a692010-01-28 21:24:01 +0000436 canvas->concat(fMatrix);
437
reed@android.comd2abab62009-10-20 21:27:15 +0000438 SkPaint paint;
439 paint.setFilterBitmap(true);
440 paint.setShader(SkShader::CreateBitmapShader(fBitmap,
441 SkShader::kClamp_TileMode,
442 SkShader::kClamp_TileMode))->unref();
reed@android.com2736a692010-01-28 21:24:01 +0000443 fMesh.draw(canvas, paint); //return;
reed@android.comd2abab62009-10-20 21:27:15 +0000444
445 paint.setShader(NULL);
446 paint.setColor(SK_ColorRED);
reed@android.com2736a692010-01-28 21:24:01 +0000447 fMesh.draw(canvas, paint);
reed@android.com2ee7c642009-10-28 14:25:34 +0000448
reed@android.com2736a692010-01-28 21:24:01 +0000449 // test_drag(canvas, fBitmap, fP0, fP1);
reed@android.comd2abab62009-10-20 21:27:15 +0000450 }
451
452 virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
453 return new Click(this);
454 }
455
456 virtual bool onClick(Click* click) {
reed@android.com2736a692010-01-28 21:24:01 +0000457 SkPoint pts[2] = { click->fOrig, click->fCurr };
458 fInverse.mapPoints(pts, 2);
459 this->warp(pts[0], pts[1]);
reed@android.comd2abab62009-10-20 21:27:15 +0000460 this->inval(NULL);
461 return true;
462 }
463
464private:
465 SkIRect fBase, fRect;
reed@android.com2ee7c642009-10-28 14:25:34 +0000466 SkPoint fP0, fP1;
reed@android.comd2abab62009-10-20 21:27:15 +0000467 typedef SkView INHERITED;
468};
469
470//////////////////////////////////////////////////////////////////////////////
471
472static SkView* MyFactory() { return new WarpView; }
473static SkViewRegister reg(MyFactory);
474