/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "gm.h"
#include "SkColorPriv.h"
#include "SkGeometry.h"
#include "SkShader.h"
#define WIRE_FRAME_WIDTH 1.1f
static void tesselate(const SkPath& src, SkPath* dst) {
SkPath::Iter iter(src, true);
SkPoint pts[4];
SkPath::Verb verb;
while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
switch (verb) {
case SkPath::kMove_Verb:
dst->moveTo(pts[0]);
break;
case SkPath::kLine_Verb:
dst->lineTo(pts[1]);
break;
case SkPath::kQuad_Verb: {
SkPoint p;
for (int i = 1; i <= 8; ++i) {
SkEvalQuadAt(pts, i / 8.0f, &p, NULL);
dst->lineTo(p);
}
} break;
case SkPath::kCubic_Verb: {
SkPoint p;
for (int i = 1; i <= 8; ++i) {
SkEvalCubicAt(pts, i / 8.0f, &p, NULL, NULL);
dst->lineTo(p);
}
} break;
}
}
}
static void setFade(SkPaint* paint, bool showGL) {
paint->setAlpha(showGL ? 0x66 : 0xFF);
}
static void setGLFrame(SkPaint* paint) {
paint->setColor(0xFFFF0000);
paint->setStyle(SkPaint::kStroke_Style);
paint->setAntiAlias(true);
paint->setStrokeWidth(WIRE_FRAME_WIDTH);
}
static void show_mesh(SkCanvas* canvas, const SkRect& r) {
SkPaint paint;
setGLFrame(&paint);
canvas->drawRect(r, paint);
canvas->drawLine(r.fLeft, r.fTop, r.fRight, r.fBottom, paint);
}
static void drawLine(SkCanvas* canvas, const SkPoint& p0, const SkPoint& p1,
const SkPaint& paint) {
canvas->drawLine(p0.fX, p0.fY, p1.fX, p1.fY, paint);
}
static void show_mesh(SkCanvas* canvas, const SkPoint pts[],
const uint16_t indices[], int count) {
SkPaint paint;
setGLFrame(&paint);
for (int i = 0; i < count - 2; ++i) {
drawLine(canvas, pts[indices[i]], pts[indices[i+1]], paint);
drawLine(canvas, pts[indices[i+1]], pts[indices[i+2]], paint);
drawLine(canvas, pts[indices[i+2]], pts[indices[i]], paint);
}
}
static void show_glframe(SkCanvas* canvas, const SkPath& path) {
SkPaint paint;
setGLFrame(&paint);
canvas->drawPath(path, paint);
}
static void show_mesh_between(SkCanvas* canvas, const SkPath& p0, const SkPath& p1) {
SkPath d0, d1;
tesselate(p0, &d0);
tesselate(p1, &d1);
SkPoint pts0[256*2], pts1[256];
int count = d0.getPoints(pts0, SK_ARRAY_COUNT(pts0));
int count1 = d1.getPoints(pts1, SK_ARRAY_COUNT(pts1));
SkASSERT(count == count1);
memcpy(&pts0[count], pts1, count * sizeof(SkPoint));
uint16_t indices[256*6];
uint16_t* ndx = indices;
for (int i = 0; i < count; ++i) {
*ndx++ = i;
*ndx++ = i + count;
}
*ndx++ = 0;
show_mesh(canvas, pts0, indices, ndx - indices);
}
static void show_fan(SkCanvas* canvas, const SkPath& path, SkScalar cx, SkScalar cy) {
SkPaint paint;
setGLFrame(&paint);
canvas->drawPath(path, paint);
SkPoint pts[256];
int count = path.getPoints(pts, SK_ARRAY_COUNT(pts));
for (int i = 0; i < count; ++i) {
canvas->drawLine(pts[i].fX, pts[i].fY, cx, cy, paint);
}
}
///////////////////////////////////////////////////////////////////////////////
typedef void (*DrawProc)(SkCanvas* canvas, bool showGL, int flags);
static void draw_line(SkCanvas* canvas, bool showGL, int flags) {
SkPaint paint;
paint.setAntiAlias(true);
if (showGL) {
setGLFrame(&paint);
}
canvas->drawLine(50, 50, 400, 100, paint);
paint.setColor(SK_ColorBLACK);
canvas->rotate(40);
setFade(&paint, showGL);
paint.setStrokeWidth(40);
canvas->drawLine(100, 50, 450, 50, paint);
if (showGL) {
show_mesh(canvas, SkRect::MakeLTRB(100, 50-20, 450, 50+20));
}
}
static void draw_rect(SkCanvas* canvas, bool showGL, int flags) {
SkPaint paint;
paint.setAntiAlias(true);
SkRect r = SkRect::MakeLTRB(50, 70, 250, 370);
setFade(&paint, showGL);
canvas->drawRect(r, paint);
if (showGL) {
show_mesh(canvas, r);
}
canvas->translate(320, 0);
paint.setStyle(SkPaint::kStroke_Style);
paint.setStrokeWidth(25);
canvas->drawRect(r, paint);
if (showGL) {
SkScalar rad = paint.getStrokeWidth() / 2;
SkPoint pts[8];
r.outset(rad, rad);
r.toQuad(&pts[0]);
r.inset(rad*2, rad*2);
r.toQuad(&pts[4]);
const uint16_t indices[] = {
0, 4, 1, 5, 2, 6, 3, 7, 0, 4
};
show_mesh(canvas, pts, indices, SK_ARRAY_COUNT(indices));
}
}
static void draw_oval(SkCanvas* canvas, bool showGL, int flags) {
SkPaint paint;
paint.setAntiAlias(true);
SkRect r = SkRect::MakeLTRB(50, 70, 250, 370);
setFade(&paint, showGL);
canvas->drawOval(r, paint);
if (showGL) {
switch (flags) {
case 0: {
SkPath path;
path.addOval(r);
show_glframe(canvas, path);
} break;
case 1:
case 3: {
SkPath src, dst;
src.addOval(r);
tesselate(src, &dst);
show_fan(canvas, dst, r.centerX(), r.centerY());
} break;
case 2: {
SkPaint p(paint);
show_mesh(canvas, r);
setGLFrame(&p);
paint.setStyle(SkPaint::kFill_Style);
canvas->drawCircle(r.centerX(), r.centerY(), 3, p);
} break;
}
}
canvas->translate(320, 0);
paint.setStyle(SkPaint::kStroke_Style);
paint.setStrokeWidth(25);
canvas->drawOval(r, paint);
if (showGL) {
switch (flags) {
case 0: {
SkPath path;
SkScalar rad = paint.getStrokeWidth() / 2;
r.outset(rad, rad);
path.addOval(r);
r.inset(rad*2, rad*2);
path.addOval(r);
show_glframe(canvas, path);
} break;
case 1: {
SkPath path0, path1;
SkScalar rad = paint.getStrokeWidth() / 2;
r.outset(rad, rad);
path0.addOval(r);
r.inset(rad*2, rad*2);
path1.addOval(r);
show_mesh_between(canvas, path0, path1);
} break;
case 2: {
SkPath path;
path.addOval(r);
show_glframe(canvas, path);
SkScalar rad = paint.getStrokeWidth() / 2;
r.outset(rad, rad);
show_mesh(canvas, r);
} break;
case 3: {
SkScalar rad = paint.getStrokeWidth() / 2;
r.outset(rad, rad);
SkPaint paint;
paint.setAlpha(0x33);
canvas->drawRect(r, paint);
show_mesh(canvas, r);
} break;
}
}
}
#include "SkImageDecoder.h"
static void draw_image(SkCanvas* canvas, bool showGL, int flags) {
SkPaint paint;
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
setFade(&paint, showGL);
static SkBitmap* gBM;
if (NULL == gBM) {
gBM = new SkBitmap;
SkImageDecoder::DecodeFile("/skimages/startrek.png", gBM);
}
SkRect r = SkRect::MakeWH(gBM->width(), gBM->height());
canvas->save();
canvas->translate(30, 30);
canvas->scale(0.8f, 0.8f);
canvas->drawBitmap(*gBM, 0, 0, &paint);
if (showGL) {
show_mesh(canvas, r);
}
canvas->restore();
canvas->translate(210, 290);
canvas->rotate(-35);
canvas->drawBitmap(*gBM, 0, 0, &paint);
if (showGL) {
show_mesh(canvas, r);
}
}
static void draw_text(SkCanvas* canvas, bool showGL, int flags) {
SkPaint paint;
paint.setAntiAlias(true);
paint.setLCDRenderText(true);
const char text[] = "Graphics at Google";
size_t len = strlen(text);
setFade(&paint, showGL);
canvas->translate(40, 50);
for (int i = 0; i < 10; ++i) {
paint.setTextSize(12 + i * 3);
canvas->drawText(text, len, 0, 0, paint);
if (showGL) {
SkRect bounds[256];
SkScalar widths[256];
int count = paint.getTextWidths(text, len, widths, bounds);
SkScalar adv = 0;
for (int j = 0; j < count; ++j) {
bounds[j].offset(adv, 0);
show_mesh(canvas, bounds[j]);
adv += widths[j];
}
}
canvas->translate(0, paint.getTextSize() * 3 / 2);
}
}
static const struct {
DrawProc fProc;
const char* fName;
} gRec[] = {
{ draw_line, "Lines" },
{ draw_rect, "Rects" },
{ draw_oval, "Ovals" },
{ draw_image, "Images" },
{ draw_text, "Text" },
};
class TalkGM : public skiagm::GM {
DrawProc fProc;
SkString fName;
bool fShowGL;
int fFlags;
public:
TalkGM(int index, bool showGL, int flags = 0) {
fProc = gRec[index].fProc;
fName.set(gRec[index].fName);
if (showGL) {
fName.append("-gl");
}
fShowGL = showGL;
fFlags = flags;
}
protected:
virtual SkString onShortName() {
return fName;
}
virtual SkISize onISize() {
return SkISize::Make(640, 480);
}
virtual void onDraw(SkCanvas* canvas) {
SkISize size = canvas->getDeviceSize();
SkRect dst = SkRect::MakeWH(size.width(), size.height());
SkRect src = SkRect::MakeWH(640, 480);
SkMatrix matrix;
matrix.setRectToRect(src, dst, SkMatrix::kCenter_ScaleToFit);
canvas->concat(matrix);
fProc(canvas, fShowGL, fFlags);
}
virtual uint32_t onGetFlags() const SK_OVERRIDE {
return kSkipPDF_Flag | kSkipPicture_Flag | kSkipPipe_Flag | kSkipTiled_Flag;
}
private:
typedef skiagm::GM INHERITED;
};
//////////////////////////////////////////////////////////////////////////////
#define GM_CONCAT(X,Y) GM_CONCAT_IMPL(X,Y)
#define GM_CONCAT_IMPL(X,Y) X##Y
#define FACTORY_NAME GM_CONCAT(Factory, __LINE__)
#define REGISTRY_NAME GM_CONCAT(gReg, __LINE__)
#define ADD_GM(Class, args) \
static skiagm::GM* FACTORY_NAME(void*) { return new Class args; } \
static skiagm::GMRegistry REGISTRY_NAME(FACTORY_NAME);
ADD_GM(TalkGM, (0, false))
ADD_GM(TalkGM, (0, true))
ADD_GM(TalkGM, (1, false))
ADD_GM(TalkGM, (1, true))
ADD_GM(TalkGM, (2, false))
ADD_GM(TalkGM, (2, true))
ADD_GM(TalkGM, (2, true, 1))
ADD_GM(TalkGM, (2, true, 2))
ADD_GM(TalkGM, (2, true, 3))
ADD_GM(TalkGM, (3, false))
ADD_GM(TalkGM, (3, true))
ADD_GM(TalkGM, (4, false))
ADD_GM(TalkGM, (4, true))
//static GM* MyFactory(void*) { return new TalkGM(0, false); }
//static GMRegistry reg(MyFactory);