/*
* Copyright 2015 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 "Resources.h"
#include "SampleCode.h"
#include "SkAnimTimer.h"
#include "SkCanvas.h"
#include "SkInterpolator.h"
#include "SkGradientShader.h"
#include "SkData.h"
#include "SkPath.h"
#include "SkSurface.h"
#include "SkRandom.h"
#include "SkTime.h"
static SkSurface* make_surface(SkCanvas* canvas, const SkImageInfo& info) {
SkSurface* surface = canvas->newSurface(info);
if (!surface) {
surface = SkSurface::NewRaster(info);
}
return surface;
}
static SkShader* make_shader(const SkRect& bounds) {
#if 0
const SkPoint pts[] = {
{ bounds.left(), bounds.top() },
{ bounds.right(), bounds.bottom() },
};
const SkColor colors[] = {
SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorBLACK,
SK_ColorCYAN, SK_ColorMAGENTA, SK_ColorYELLOW,
};
return SkGradientShader::CreateLinear(pts,
colors, nullptr, SK_ARRAY_COUNT(colors),
SkShader::kClamp_TileMode);
#else
SkAutoTUnref<SkImage> image(GetResourceAsImage("mandrill_128.png"));
if (nullptr == image) {
return nullptr;
}
return image->newShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
#endif
}
#define N 128
#define ANGLE_DELTA 3
#define SCALE_DELTA (SK_Scalar1 / 32)
static SkImage* make_image() {
SkImageInfo info = SkImageInfo::MakeN32(N, N, kOpaque_SkAlphaType);
SkAutoTUnref<SkSurface> surface(SkSurface::NewRaster(info));
SkCanvas* canvas = surface->getCanvas();
canvas->drawColor(SK_ColorWHITE);
SkPath path;
path.setFillType(SkPath::kEvenOdd_FillType);
path.addRect(SkRect::MakeWH(N/2, N));
path.addRect(SkRect::MakeWH(N, N/2));
path.moveTo(0, 0); path.lineTo(N, 0); path.lineTo(0, N); path.close();
SkPaint paint;
SkSafeUnref(paint.setShader(make_shader(SkRect::MakeWH(N, N))));
canvas->drawPath(path, paint);
return surface->newImageSnapshot();
}
static SkImage* zoom_up(SkSurface* origSurf, SkImage* orig) {
const SkScalar S = 16; // amount to scale up
const int D = 2; // dimension scaling for the offscreen
// since we only view the center, don't need to produce the entire thing
SkImageInfo info = SkImageInfo::MakeN32(orig->width() * D, orig->height() * D,
kOpaque_SkAlphaType);
SkAutoTUnref<SkSurface> surface(origSurf->newSurface(info));
SkCanvas* canvas = surface->getCanvas();
canvas->drawColor(SK_ColorWHITE);
canvas->scale(S, S);
canvas->translate(-SkScalarHalf(orig->width()) * (S - D) / S,
-SkScalarHalf(orig->height()) * (S - D) / S);
canvas->drawImage(orig, 0, 0, nullptr);
if (S > 3) {
SkPaint paint;
paint.setColor(SK_ColorWHITE);
for (int i = 1; i < orig->height(); ++i) {
SkScalar y = SkIntToScalar(i);
canvas->drawLine(0, y, SkIntToScalar(orig->width()), y, paint);
}
for (int i = 1; i < orig->width(); ++i) {
SkScalar x = SkIntToScalar(i);
canvas->drawLine(x, 0, x, SkIntToScalar(orig->height()), paint);
}
}
return surface->newImageSnapshot();
}
struct AnimValue {
SkScalar fValue;
SkScalar fMin;
SkScalar fMax;
SkScalar fMod;
operator SkScalar() const { return fValue; }
void set(SkScalar value, SkScalar min, SkScalar max) {
fValue = value;
fMin = min;
fMax = max;
fMod = 0;
}
void setMod(SkScalar value, SkScalar mod) {
fValue = value;
fMin = 0;
fMax = 0;
fMod = mod;
}
SkScalar inc(SkScalar delta) {
fValue += delta;
return this->fixUp();
}
SkScalar fixUp() {
if (fMod) {
fValue = SkScalarMod(fValue, fMod);
} else {
if (fValue > fMax) {
fValue = fMax;
} else if (fValue < fMin) {
fValue = fMin;
}
}
return fValue;
}
};
static void draw_box_frame(SkCanvas* canvas, int width, int height) {
SkPaint p;
p.setStyle(SkPaint::kStroke_Style);
p.setColor(SK_ColorRED);
SkRect r = SkRect::MakeIWH(width, height);
r.inset(0.5f, 0.5f);
canvas->drawRect(r, p);
canvas->drawLine(r.left(), r.top(), r.right(), r.bottom(), p);
canvas->drawLine(r.left(), r.bottom(), r.right(), r.top(), p);
}
class FilterQualityView : public SampleView {
SkAutoTUnref<SkImage> fImage;
AnimValue fScale, fAngle;
SkSize fCell;
SkInterpolator fTrans;
SkMSec fCurrTime;
bool fShowFatBits;
public:
FilterQualityView() : fImage(make_image()), fTrans(2, 2), fShowFatBits(true) {
fCell.set(256, 256);
fScale.set(1, SK_Scalar1 / 8, 1);
fAngle.setMod(0, 360);
SkScalar values[2];
fTrans.setMirror(true);
fTrans.setReset(true);
fCurrTime = 0;
fTrans.setRepeatCount(999);
values[0] = values[1] = 0;
fTrans.setKeyFrame(0, fCurrTime, values);
values[0] = values[1] = 1;
fTrans.setKeyFrame(1, fCurrTime + 2000, values);
}
protected:
bool onQuery(SkEvent* evt) override {
if (SampleCode::TitleQ(*evt)) {
SampleCode::TitleR(evt, "FilterQuality");
return true;
}
SkUnichar uni;
if (SampleCode::CharQ(*evt, &uni)) {
switch (uni) {
case '1': fAngle.inc(-ANGLE_DELTA); this->inval(nullptr); return true;
case '2': fAngle.inc( ANGLE_DELTA); this->inval(nullptr); return true;
case '3': fScale.inc(-SCALE_DELTA); this->inval(nullptr); return true;
case '4': fScale.inc( SCALE_DELTA); this->inval(nullptr); return true;
case '5': fShowFatBits = !fShowFatBits; this->inval(nullptr); return true;
default: break;
}
}
return this->INHERITED::onQuery(evt);
}
void drawTheImage(SkCanvas* canvas, const SkISize& size, SkFilterQuality filter,
SkScalar dx, SkScalar dy) {
SkPaint paint;
paint.setAntiAlias(true);
paint.setFilterQuality(filter);
SkAutoCanvasRestore acr(canvas, true);
canvas->translate(dx, dy);
canvas->translate(SkScalarHalf(size.width()), SkScalarHalf(size.height()));
canvas->scale(fScale, fScale);
canvas->rotate(fAngle);
canvas->drawImage(fImage, -SkScalarHalf(fImage->width()), -SkScalarHalf(fImage->height()),
&paint);
if (false) {
acr.restore();
draw_box_frame(canvas, size.width(), size.height());
}
}
void drawHere(SkCanvas* canvas, SkFilterQuality filter, SkScalar dx, SkScalar dy) {
SkCanvas* origCanvas = canvas;
SkAutoCanvasRestore acr(canvas, true);
SkISize size = SkISize::Make(fImage->width(), fImage->height());
SkAutoTUnref<SkSurface> surface;
if (fShowFatBits) {
// scale up so we don't clip rotations
SkImageInfo info = SkImageInfo::MakeN32(fImage->width() * 2, fImage->height() * 2,
kOpaque_SkAlphaType);
surface.reset(make_surface(canvas, info));
canvas = surface->getCanvas();
canvas->drawColor(SK_ColorWHITE);
size.set(info.width(), info.height());
} else {
canvas->translate(SkScalarHalf(fCell.width() - fImage->width()),
SkScalarHalf(fCell.height() - fImage->height()));
}
this->drawTheImage(canvas, size, filter, dx, dy);
if (surface) {
SkAutoTUnref<SkImage> orig(surface->newImageSnapshot());
SkAutoTUnref<SkImage> zoomed(zoom_up(surface, orig));
origCanvas->drawImage(zoomed,
SkScalarHalf(fCell.width() - zoomed->width()),
SkScalarHalf(fCell.height() - zoomed->height()));
}
}
void drawBorders(SkCanvas* canvas) {
SkPaint p;
p.setStyle(SkPaint::kStroke_Style);
p.setColor(SK_ColorBLUE);
SkRect r = SkRect::MakeWH(fCell.width() * 2, fCell.height() * 2);
r.inset(SK_ScalarHalf, SK_ScalarHalf);
canvas->drawRect(r, p);
canvas->drawLine(r.left(), r.centerY(), r.right(), r.centerY(), p);
canvas->drawLine(r.centerX(), r.top(), r.centerX(), r.bottom(), p);
}
void onDrawContent(SkCanvas* canvas) override {
fCell.set(this->height() / 2, this->height() / 2);
SkScalar trans[2];
fTrans.timeToValues(fCurrTime, trans);
for (int y = 0; y < 2; ++y) {
for (int x = 0; x < 2; ++x) {
int index = y * 2 + x;
SkAutoCanvasRestore acr(canvas, true);
canvas->translate(fCell.width() * x, fCell.height() * y);
SkRect r = SkRect::MakeWH(fCell.width(), fCell.height());
r.inset(4, 4);
canvas->clipRect(r);
this->drawHere(canvas, SkFilterQuality(index), trans[0], trans[1]);
}
}
this->drawBorders(canvas);
const SkScalar textX = fCell.width() * 2 + 30;
SkPaint paint;
paint.setAntiAlias(true);
paint.setTextSize(36);
SkString str;
str.appendScalar(fScale);
canvas->drawText(str.c_str(), str.size(), textX, 100, paint);
str.reset(); str.appendScalar(fAngle);
canvas->drawText(str.c_str(), str.size(), textX, 150, paint);
str.reset(); str.appendScalar(trans[0]);
canvas->drawText(str.c_str(), str.size(), textX, 200, paint);
str.reset(); str.appendScalar(trans[1]);
canvas->drawText(str.c_str(), str.size(), textX, 250, paint);
}
bool onAnimate(const SkAnimTimer& timer) override {
fCurrTime = timer.msec();
return true;
}
virtual bool handleKey(SkKey key) {
this->inval(nullptr);
return true;
}
private:
typedef SampleView INHERITED;
};
//////////////////////////////////////////////////////////////////////////////
static SkView* MyFactory() { return new FilterQualityView; }
static SkViewRegister reg(MyFactory);