skia2/samplecode/SampleFilterQuality.cpp
bungeman fbd87d611a Missing resources crash SampleFilterQuality.
The FilterQuality slide segfaults if resources cannot be loaded.
This is particularly noticeable with an Android build, as by
default resources are not available.

Review URL: https://codereview.chromium.org/1422763004
2015-11-10 13:29:44 -08:00

325 lines
10 KiB
C++

/*
* 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);