skia2/samplecode/SampleFilterFuzz.cpp
commit-bot@chromium.org cac5fd597f Factory methods for heap-allocated SkImageFilter objects.
This is part of an effort to ensure that all SkPaint effects can only be
allocated on the heap.

This patch makes the constructors of SkImageFilter and its subclasses non-public
and instead provides factory methods for creating these objects on the heap. We
temporarily keep constructor of publicly visible classes public behind a flag.

BUG=skia:2187
R=scroggo@google.com, mtklein@chromium.org, reed@google.com, senorblanco@google.com, senorblanco@chromium.org, bsalomon@google.com, sugoi@chromium.org, zork@chromium.org

Author: dominikg@chromium.org

Review URL: https://codereview.chromium.org/182983003

git-svn-id: http://skia.googlecode.com/svn/trunk@13718 2bbb7eff-a529-9590-31e7-b0007b416f81
2014-03-10 10:51:58 +00:00

437 lines
14 KiB
C++

/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SampleCode.h"
#include "SkBicubicImageFilter.h"
#include "SkBitmapDevice.h"
#include "SkBitmapSource.h"
#include "SkBlurImageFilter.h"
#include "SkCanvas.h"
#include "SkColorFilter.h"
#include "SkColorFilterImageFilter.h"
#include "SkComposeImageFilter.h"
#include "SkData.h"
#include "SkDisplacementMapEffect.h"
#include "SkDropShadowImageFilter.h"
#include "SkFlattenableSerialization.h"
#include "SkLightingImageFilter.h"
#include "SkMagnifierImageFilter.h"
#include "SkMergeImageFilter.h"
#include "SkMorphologyImageFilter.h"
#include "SkOffsetImageFilter.h"
#include "SkPerlinNoiseShader.h"
#include "SkPictureImageFilter.h"
#include "SkRandom.h"
#include "SkRectShaderImageFilter.h"
#include "SkTileImageFilter.h"
#include "SkView.h"
#include "SkXfermodeImageFilter.h"
#include <stdio.h>
#include <time.h>
//#define SK_ADD_RANDOM_BIT_FLIPS
//#define SK_FUZZER_IS_VERBOSE
static const uint32_t kSeed = (uint32_t)(time(NULL));
static SkRandom gRand(kSeed);
static bool return_large = false;
static bool return_undef = false;
static const int kBitmapSize = 24;
static int R(float x) {
return (int)floor(SkScalarToFloat(gRand.nextUScalar1()) * x);
}
#if defined _WIN32
#pragma warning ( push )
// we are intentionally causing an overflow here
// (warning C4756: overflow in constant arithmetic)
#pragma warning ( disable : 4756 )
#endif
static float huge() {
double d = 1e100;
float f = (float)d;
return f;
}
#if defined _WIN32
#pragma warning ( pop )
#endif
static float make_number(bool positiveOnly) {
float f = positiveOnly ? 1.0f : 0.0f;
float v = f;
int sel;
if (return_large) sel = R(6); else sel = R(4);
if (!return_undef && sel == 0) sel = 1;
if (R(2) == 1) v = (float)(R(100)+f); else
switch (sel) {
case 0: break;
case 1: v = f; break;
case 2: v = 0.000001f; break;
case 3: v = 10000.0f; break;
case 4: v = 2000000000.0f; break;
case 5: v = huge(); break;
}
if (!positiveOnly && (R(4) == 1)) v = -v;
return v;
}
static SkScalar make_scalar(bool positiveOnly = false) {
return make_number(positiveOnly);
}
static SkRect make_rect() {
return SkRect::MakeWH(SkIntToScalar(R(static_cast<float>(kBitmapSize))),
SkIntToScalar(R(static_cast<float>(kBitmapSize))));
}
static SkXfermode::Mode make_xfermode() {
return static_cast<SkXfermode::Mode>(R(SkXfermode::kLastMode+1));
}
static SkColor make_color() {
return (R(2) == 1) ? 0xFFC0F0A0 : 0xFF000090;
}
static SkPoint3 make_point() {
return SkPoint3(make_scalar(), make_scalar(), make_scalar(true));
}
static SkDisplacementMapEffect::ChannelSelectorType make_channel_selector_type() {
return static_cast<SkDisplacementMapEffect::ChannelSelectorType>(R(4)+1);
}
static bool valid_for_raster_canvas(const SkImageInfo& info) {
switch (info.colorType()) {
case kAlpha_8_SkColorType:
case kRGB_565_SkColorType:
return true;
case kPMColor_SkColorType:
return kPremul_SkAlphaType == info.alphaType() ||
kOpaque_SkAlphaType == info.alphaType();
default:
break;
}
return false;
}
static SkColorType rand_colortype() {
return (SkColorType)R(kLastEnum_SkColorType + 1);
}
static void rand_bitmap_for_canvas(SkBitmap* bitmap) {
SkImageInfo info;
do {
info = SkImageInfo::Make(kBitmapSize, kBitmapSize, rand_colortype(),
kPremul_SkAlphaType);
} while (!valid_for_raster_canvas(info) || !bitmap->allocPixels(info));
}
static void make_g_bitmap(SkBitmap& bitmap) {
rand_bitmap_for_canvas(&bitmap);
SkCanvas canvas(bitmap);
canvas.clear(0x00000000);
SkPaint paint;
paint.setAntiAlias(true);
paint.setColor(0xFF884422);
paint.setTextSize(SkIntToScalar(kBitmapSize/2));
const char* str = "g";
canvas.drawText(str, strlen(str), SkIntToScalar(kBitmapSize/8),
SkIntToScalar(kBitmapSize/4), paint);
}
static void make_checkerboard_bitmap(SkBitmap& bitmap) {
rand_bitmap_for_canvas(&bitmap);
SkCanvas canvas(bitmap);
canvas.clear(0x00000000);
SkPaint darkPaint;
darkPaint.setColor(0xFF804020);
SkPaint lightPaint;
lightPaint.setColor(0xFF244484);
const int i = kBitmapSize / 8;
const SkScalar f = SkIntToScalar(i);
for (int y = 0; y < kBitmapSize; y += i) {
for (int x = 0; x < kBitmapSize; x += i) {
canvas.save();
canvas.translate(SkIntToScalar(x), SkIntToScalar(y));
canvas.drawRect(SkRect::MakeXYWH(0, 0, f, f), darkPaint);
canvas.drawRect(SkRect::MakeXYWH(f, 0, f, f), lightPaint);
canvas.drawRect(SkRect::MakeXYWH(0, f, f, f), lightPaint);
canvas.drawRect(SkRect::MakeXYWH(f, f, f, f), darkPaint);
canvas.restore();
}
}
}
static const SkBitmap& make_bitmap() {
static SkBitmap bitmap[2];
static bool initialized = false;
if (!initialized) {
make_g_bitmap(bitmap[0]);
make_checkerboard_bitmap(bitmap[1]);
initialized = true;
}
return bitmap[R(2)];
}
#ifdef SK_ALLOW_PICTUREIMAGEFILTER_SERIALIZATION
static void drawSomething(SkCanvas* canvas) {
SkPaint paint;
canvas->save();
canvas->scale(0.5f, 0.5f);
canvas->drawBitmap(make_bitmap(), 0, 0, NULL);
canvas->restore();
const char beforeStr[] = "before circle";
const char afterStr[] = "after circle";
paint.setAntiAlias(true);
paint.setColor(SK_ColorRED);
canvas->drawData(beforeStr, sizeof(beforeStr));
canvas->drawCircle(SkIntToScalar(kBitmapSize/2), SkIntToScalar(kBitmapSize/2), SkIntToScalar(kBitmapSize/3), paint);
canvas->drawData(afterStr, sizeof(afterStr));
paint.setColor(SK_ColorBLACK);
paint.setTextSize(SkIntToScalar(kBitmapSize/3));
canvas->drawText("Picture", 7, SkIntToScalar(kBitmapSize/2), SkIntToScalar(kBitmapSize/4), paint);
}
#endif
static SkImageFilter* make_image_filter(bool canBeNull = true) {
SkImageFilter* filter = 0;
// Add a 1 in 3 chance to get a NULL input
if (canBeNull && (R(3) == 1)) { return filter; }
enum { BICUBIC, MERGE, COLOR, BLUR, MAGNIFIER, XFERMODE, OFFSET, COMPOSE,
DISTANT_LIGHT, POINT_LIGHT, SPOT_LIGHT, NOISE, DROP_SHADOW,
MORPHOLOGY, BITMAP, DISPLACE, TILE, PICTURE, NUM_FILTERS };
switch (R(NUM_FILTERS)) {
case BICUBIC:
// Scale is set to 1 here so that it can fit in the DAG without resizing the output
filter = SkBicubicImageFilter::CreateMitchell(SkSize::Make(1, 1), make_image_filter());
break;
case MERGE:
filter = SkMergeImageFilter::Create(make_image_filter(), make_image_filter(), make_xfermode());
break;
case COLOR:
{
SkAutoTUnref<SkColorFilter> cf((R(2) == 1) ?
SkColorFilter::CreateModeFilter(make_color(), make_xfermode()) :
SkColorFilter::CreateLightingFilter(make_color(), make_color()));
filter = cf.get() ? SkColorFilterImageFilter::Create(cf, make_image_filter()) : 0;
}
break;
case BLUR:
filter = SkBlurImageFilter::Create(make_scalar(true), make_scalar(true), make_image_filter());
break;
case MAGNIFIER:
filter = SkMagnifierImageFilter::Create(make_rect(), make_scalar(true));
break;
case XFERMODE:
{
SkAutoTUnref<SkXfermode> mode(SkXfermode::Create(make_xfermode()));
filter = SkXfermodeImageFilter::Create(mode, make_image_filter(), make_image_filter());
}
break;
case OFFSET:
filter = SkOffsetImageFilter::Create(make_scalar(), make_scalar(), make_image_filter());
break;
case COMPOSE:
filter = SkComposeImageFilter::Create(make_image_filter(), make_image_filter());
break;
case DISTANT_LIGHT:
filter = (R(2) == 1) ?
SkLightingImageFilter::CreateDistantLitDiffuse(make_point(),
make_color(), make_scalar(), make_scalar(), make_image_filter()) :
SkLightingImageFilter::CreateDistantLitSpecular(make_point(),
make_color(), make_scalar(), make_scalar(), SkIntToScalar(R(10)),
make_image_filter());
break;
case POINT_LIGHT:
filter = (R(2) == 1) ?
SkLightingImageFilter::CreatePointLitDiffuse(make_point(),
make_color(), make_scalar(), make_scalar(), make_image_filter()) :
SkLightingImageFilter::CreatePointLitSpecular(make_point(),
make_color(), make_scalar(), make_scalar(), SkIntToScalar(R(10)),
make_image_filter());
break;
case SPOT_LIGHT:
filter = (R(2) == 1) ?
SkLightingImageFilter::CreateSpotLitDiffuse(SkPoint3(0, 0, 0),
make_point(), make_scalar(), make_scalar(), make_color(),
make_scalar(), make_scalar(), make_image_filter()) :
SkLightingImageFilter::CreateSpotLitSpecular(SkPoint3(0, 0, 0),
make_point(), make_scalar(), make_scalar(), make_color(),
make_scalar(), make_scalar(), SkIntToScalar(R(10)), make_image_filter());
break;
case NOISE:
{
SkAutoTUnref<SkShader> shader((R(2) == 1) ?
SkPerlinNoiseShader::CreateFractalNoise(
make_scalar(true), make_scalar(true), R(10.0f), make_scalar()) :
SkPerlinNoiseShader::CreateTubulence(
make_scalar(true), make_scalar(true), R(10.0f), make_scalar()));
SkImageFilter::CropRect cropR(SkRect::MakeWH(SkIntToScalar(kBitmapSize),
SkIntToScalar(kBitmapSize)));
filter = SkRectShaderImageFilter::Create(shader, &cropR);
}
break;
case DROP_SHADOW:
filter = SkDropShadowImageFilter::Create(make_scalar(), make_scalar(),
make_scalar(true), make_color(), make_image_filter());
break;
case MORPHOLOGY:
if (R(2) == 1) {
filter = SkDilateImageFilter::Create(R(static_cast<float>(kBitmapSize)),
R(static_cast<float>(kBitmapSize)), make_image_filter());
} else {
filter = SkErodeImageFilter::Create(R(static_cast<float>(kBitmapSize)),
R(static_cast<float>(kBitmapSize)), make_image_filter());
}
break;
case BITMAP:
if (R(2) == 1) {
filter = SkBitmapSource::Create(make_bitmap(), make_rect(), make_rect());
} else {
filter = SkBitmapSource::Create(make_bitmap());
}
break;
case DISPLACE:
filter = SkDisplacementMapEffect::Create(make_channel_selector_type(),
make_channel_selector_type(), make_scalar(),
make_image_filter(false), make_image_filter());
break;
case TILE:
filter = SkTileImageFilter::Create(make_rect(), make_rect(), make_image_filter(false));
break;
case PICTURE:
{
SkPicture* pict = NULL;
#ifdef SK_ALLOW_PICTUREIMAGEFILTER_SERIALIZATION
pict = new SkPicture;
SkAutoUnref aur(pict);
drawSomething(pict->beginRecording(kBitmapSize, kBitmapSize));
pict->endRecording();
#endif
filter = SkPictureImageFilter::Create(pict, make_rect());
}
break;
default:
break;
}
return (filter || canBeNull) ? filter : make_image_filter(canBeNull);
}
static SkImageFilter* make_serialized_image_filter() {
SkAutoTUnref<SkImageFilter> filter(make_image_filter(false));
SkAutoTUnref<SkData> data(SkValidatingSerializeFlattenable(filter));
const unsigned char* ptr = static_cast<const unsigned char*>(data->data());
size_t len = data->size();
#ifdef SK_ADD_RANDOM_BIT_FLIPS
unsigned char* p = const_cast<unsigned char*>(ptr);
for (size_t i = 0; i < len; ++i, ++p) {
if (R(250) == 1) { // 0.4% of the time, flip a bit or byte
if (R(10) == 1) { // Then 10% of the time, change a whole byte
switch(R(3)) {
case 0:
*p ^= 0xFF; // Flip entire byte
break;
case 1:
*p = 0xFF; // Set all bits to 1
break;
case 2:
*p = 0x00; // Set all bits to 0
break;
}
} else {
*p ^= (1 << R(8));
}
}
}
#endif // SK_ADD_RANDOM_BIT_FLIPS
SkFlattenable* flattenable = SkValidatingDeserializeFlattenable(ptr, len,
SkImageFilter::GetFlattenableType());
return static_cast<SkImageFilter*>(flattenable);
}
static void drawClippedBitmap(SkCanvas* canvas, int x, int y, const SkPaint& paint) {
canvas->save();
canvas->clipRect(SkRect::MakeXYWH(SkIntToScalar(x), SkIntToScalar(y),
SkIntToScalar(kBitmapSize), SkIntToScalar(kBitmapSize)));
canvas->drawBitmap(make_bitmap(), SkIntToScalar(x), SkIntToScalar(y), &paint);
canvas->restore();
}
static void do_fuzz(SkCanvas* canvas) {
SkImageFilter* filter = make_serialized_image_filter();
#ifdef SK_FUZZER_IS_VERBOSE
static uint32_t numFilters = 0;
static uint32_t numValidFilters = 0;
if (0 == numFilters) {
printf("Fuzzing with %u\n", kSeed);
}
numFilters++;
if (NULL != filter) {
numValidFilters++;
}
printf("Filter no : %u. Valid filters so far : %u\r", numFilters, numValidFilters);
fflush(stdout);
#endif
SkPaint paint;
SkSafeUnref(paint.setImageFilter(filter));
drawClippedBitmap(canvas, 0, 0, paint);
}
//////////////////////////////////////////////////////////////////////////////
class ImageFilterFuzzView : public SampleView {
public:
ImageFilterFuzzView() {
this->setBGColor(0xFFDDDDDD);
}
protected:
// overrides from SkEventSink
virtual bool onQuery(SkEvent* evt) {
if (SampleCode::TitleQ(*evt)) {
SampleCode::TitleR(evt, "ImageFilterFuzzer");
return true;
}
return this->INHERITED::onQuery(evt);
}
void drawBG(SkCanvas* canvas) {
canvas->drawColor(0xFFDDDDDD);
}
virtual void onDrawContent(SkCanvas* canvas) {
do_fuzz(canvas);
this->inval(0);
}
private:
typedef SkView INHERITED;
};
//////////////////////////////////////////////////////////////////////////////
static SkView* MyFactory() { return new ImageFilterFuzzView; }
static SkViewRegister reg(MyFactory);