ImageFilterTest.cpp revision 5788faaa2ac4203827c68006b669e277d441e2e4
1/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkBitmap.h"
9#include "SkBitmapDevice.h"
10#include "SkBitmapSource.h"
11#include "SkBlurImageFilter.h"
12#include "SkCanvas.h"
13#include "SkColorFilterImageFilter.h"
14#include "SkColorMatrixFilter.h"
15#include "SkComposeImageFilter.h"
16#include "SkDeviceImageFilterProxy.h"
17#include "SkDisplacementMapEffect.h"
18#include "SkDropShadowImageFilter.h"
19#include "SkFlattenableSerialization.h"
20#include "SkGradientShader.h"
21#include "SkLightingImageFilter.h"
22#include "SkMatrixConvolutionImageFilter.h"
23#include "SkMatrixImageFilter.h"
24#include "SkMergeImageFilter.h"
25#include "SkMorphologyImageFilter.h"
26#include "SkOffsetImageFilter.h"
27#include "SkPerlinNoiseShader.h"
28#include "SkPicture.h"
29#include "SkPictureImageFilter.h"
30#include "SkPictureRecorder.h"
31#include "SkReadBuffer.h"
32#include "SkRect.h"
33#include "SkRectShaderImageFilter.h"
34#include "SkTileImageFilter.h"
35#include "SkXfermodeImageFilter.h"
36#include "Test.h"
37
38#if SK_SUPPORT_GPU
39#include "GrContextFactory.h"
40#include "SkGpuDevice.h"
41#endif
42
43static const int kBitmapSize = 4;
44
45namespace {
46
47class MatrixTestImageFilter : public SkImageFilter {
48public:
49    MatrixTestImageFilter(skiatest::Reporter* reporter, const SkMatrix& expectedMatrix)
50      : SkImageFilter(0, NULL), fReporter(reporter), fExpectedMatrix(expectedMatrix) {
51    }
52
53    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context& ctx,
54                               SkBitmap* result, SkIPoint* offset) const SK_OVERRIDE {
55        REPORTER_ASSERT(fReporter, ctx.ctm() == fExpectedMatrix);
56        return true;
57    }
58
59    SK_TO_STRING_OVERRIDE()
60    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(MatrixTestImageFilter)
61
62protected:
63    void flatten(SkWriteBuffer& buffer) const SK_OVERRIDE {
64        this->INHERITED::flatten(buffer);
65        buffer.writeFunctionPtr(fReporter);
66        buffer.writeMatrix(fExpectedMatrix);
67    }
68
69private:
70    skiatest::Reporter* fReporter;
71    SkMatrix fExpectedMatrix;
72
73    typedef SkImageFilter INHERITED;
74};
75
76}
77
78SkFlattenable* MatrixTestImageFilter::CreateProc(SkReadBuffer& buffer) {
79    SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
80    skiatest::Reporter* reporter = (skiatest::Reporter*)buffer.readFunctionPtr();
81    SkMatrix matrix;
82    buffer.readMatrix(&matrix);
83    return SkNEW_ARGS(MatrixTestImageFilter, (reporter, matrix));
84}
85
86#ifndef SK_IGNORE_TO_STRING
87void MatrixTestImageFilter::toString(SkString* str) const {
88    str->appendf("MatrixTestImageFilter: (");
89    str->append(")");
90}
91#endif
92
93static void make_small_bitmap(SkBitmap& bitmap) {
94    bitmap.allocN32Pixels(kBitmapSize, kBitmapSize);
95    SkCanvas canvas(bitmap);
96    canvas.clear(0x00000000);
97    SkPaint darkPaint;
98    darkPaint.setColor(0xFF804020);
99    SkPaint lightPaint;
100    lightPaint.setColor(0xFF244484);
101    const int i = kBitmapSize / 4;
102    for (int y = 0; y < kBitmapSize; y += i) {
103        for (int x = 0; x < kBitmapSize; x += i) {
104            canvas.save();
105            canvas.translate(SkIntToScalar(x), SkIntToScalar(y));
106            canvas.drawRect(SkRect::MakeXYWH(0, 0,
107                                             SkIntToScalar(i),
108                                             SkIntToScalar(i)), darkPaint);
109            canvas.drawRect(SkRect::MakeXYWH(SkIntToScalar(i),
110                                             0,
111                                             SkIntToScalar(i),
112                                             SkIntToScalar(i)), lightPaint);
113            canvas.drawRect(SkRect::MakeXYWH(0,
114                                             SkIntToScalar(i),
115                                             SkIntToScalar(i),
116                                             SkIntToScalar(i)), lightPaint);
117            canvas.drawRect(SkRect::MakeXYWH(SkIntToScalar(i),
118                                             SkIntToScalar(i),
119                                             SkIntToScalar(i),
120                                             SkIntToScalar(i)), darkPaint);
121            canvas.restore();
122        }
123    }
124}
125
126static SkImageFilter* make_scale(float amount, SkImageFilter* input = NULL) {
127    SkScalar s = amount;
128    SkScalar matrix[20] = { s, 0, 0, 0, 0,
129                            0, s, 0, 0, 0,
130                            0, 0, s, 0, 0,
131                            0, 0, 0, s, 0 };
132    SkAutoTUnref<SkColorFilter> filter(SkColorMatrixFilter::Create(matrix));
133    return SkColorFilterImageFilter::Create(filter, input);
134}
135
136static SkImageFilter* make_grayscale(SkImageFilter* input = NULL, const SkImageFilter::CropRect* cropRect = NULL) {
137    SkScalar matrix[20];
138    memset(matrix, 0, 20 * sizeof(SkScalar));
139    matrix[0] = matrix[5] = matrix[10] = 0.2126f;
140    matrix[1] = matrix[6] = matrix[11] = 0.7152f;
141    matrix[2] = matrix[7] = matrix[12] = 0.0722f;
142    matrix[18] = 1.0f;
143    SkAutoTUnref<SkColorFilter> filter(SkColorMatrixFilter::Create(matrix));
144    return SkColorFilterImageFilter::Create(filter, input, cropRect);
145}
146
147DEF_TEST(ImageFilter, reporter) {
148    {
149        // Check that two non-clipping color matrices concatenate into a single filter.
150        SkAutoTUnref<SkImageFilter> halfBrightness(make_scale(0.5f));
151        SkAutoTUnref<SkImageFilter> quarterBrightness(make_scale(0.5f, halfBrightness));
152        REPORTER_ASSERT(reporter, NULL == quarterBrightness->getInput(0));
153    }
154
155    {
156        // Check that a clipping color matrix followed by a grayscale does not concatenate into a single filter.
157        SkAutoTUnref<SkImageFilter> doubleBrightness(make_scale(2.0f));
158        SkAutoTUnref<SkImageFilter> halfBrightness(make_scale(0.5f, doubleBrightness));
159        REPORTER_ASSERT(reporter, halfBrightness->getInput(0));
160    }
161
162    {
163        // Check that a color filter image filter without a crop rect can be
164        // expressed as a color filter.
165        SkAutoTUnref<SkImageFilter> gray(make_grayscale());
166        REPORTER_ASSERT(reporter, true == gray->asColorFilter(NULL));
167    }
168
169    {
170        // Check that a color filter image filter with a crop rect cannot
171        // be expressed as a color filter.
172        SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(0, 0, 100, 100));
173        SkAutoTUnref<SkImageFilter> grayWithCrop(make_grayscale(NULL, &cropRect));
174        REPORTER_ASSERT(reporter, false == grayWithCrop->asColorFilter(NULL));
175    }
176
177    {
178        // Check that two non-commutative matrices are concatenated in
179        // the correct order.
180        SkScalar blueToRedMatrix[20] = { 0 };
181        blueToRedMatrix[2] = blueToRedMatrix[18] = SK_Scalar1;
182        SkScalar redToGreenMatrix[20] = { 0 };
183        redToGreenMatrix[5] = redToGreenMatrix[18] = SK_Scalar1;
184        SkAutoTUnref<SkColorFilter> blueToRed(SkColorMatrixFilter::Create(blueToRedMatrix));
185        SkAutoTUnref<SkImageFilter> filter1(SkColorFilterImageFilter::Create(blueToRed.get()));
186        SkAutoTUnref<SkColorFilter> redToGreen(SkColorMatrixFilter::Create(redToGreenMatrix));
187        SkAutoTUnref<SkImageFilter> filter2(SkColorFilterImageFilter::Create(redToGreen.get(), filter1.get()));
188
189        SkBitmap result;
190        result.allocN32Pixels(kBitmapSize, kBitmapSize);
191
192        SkPaint paint;
193        paint.setColor(SK_ColorBLUE);
194        paint.setImageFilter(filter2.get());
195        SkCanvas canvas(result);
196        canvas.clear(0x0);
197        SkRect rect = SkRect::Make(SkIRect::MakeWH(kBitmapSize, kBitmapSize));
198        canvas.drawRect(rect, paint);
199        uint32_t pixel = *result.getAddr32(0, 0);
200        // The result here should be green, since we have effectively shifted blue to green.
201        REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
202    }
203
204    {
205        // Tests pass by not asserting
206        SkBitmap bitmap, result;
207        make_small_bitmap(bitmap);
208        result.allocN32Pixels(kBitmapSize, kBitmapSize);
209
210        {
211            // This tests for :
212            // 1 ) location at (0,0,1)
213            SkPoint3 location(0, 0, SK_Scalar1);
214            // 2 ) location and target at same value
215            SkPoint3 target(location.fX, location.fY, location.fZ);
216            // 3 ) large negative specular exponent value
217            SkScalar specularExponent = -1000;
218
219            SkAutoTUnref<SkImageFilter> bmSrc(SkBitmapSource::Create(bitmap));
220            SkPaint paint;
221            paint.setImageFilter(SkLightingImageFilter::CreateSpotLitSpecular(
222                    location, target, specularExponent, 180,
223                    0xFFFFFFFF, SK_Scalar1, SK_Scalar1, SK_Scalar1,
224                    bmSrc))->unref();
225            SkCanvas canvas(result);
226            SkRect r = SkRect::MakeWH(SkIntToScalar(kBitmapSize),
227                                      SkIntToScalar(kBitmapSize));
228            canvas.drawRect(r, paint);
229        }
230    }
231}
232
233static void test_crop_rects(SkBaseDevice* device, skiatest::Reporter* reporter) {
234    // Check that all filters offset to their absolute crop rect,
235    // unaffected by the input crop rect.
236    // Tests pass by not asserting.
237    SkBitmap bitmap;
238    bitmap.allocN32Pixels(100, 100);
239    bitmap.eraseARGB(0, 0, 0, 0);
240    SkDeviceImageFilterProxy proxy(device, SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType));
241
242    SkImageFilter::CropRect inputCropRect(SkRect::MakeXYWH(8, 13, 80, 80));
243    SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(20, 30, 60, 60));
244    SkAutoTUnref<SkImageFilter> input(make_grayscale(NULL, &inputCropRect));
245
246    SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(SK_ColorRED, SkXfermode::kSrcIn_Mode));
247    SkPoint3 location(0, 0, SK_Scalar1);
248    SkPoint3 target(SK_Scalar1, SK_Scalar1, SK_Scalar1);
249    SkScalar kernel[9] = {
250        SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
251        SkIntToScalar( 1), SkIntToScalar(-7), SkIntToScalar( 1),
252        SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
253    };
254    SkISize kernelSize = SkISize::Make(3, 3);
255    SkScalar gain = SK_Scalar1, bias = 0;
256
257    SkImageFilter* filters[] = {
258        SkColorFilterImageFilter::Create(cf.get(), input.get(), &cropRect),
259        SkDisplacementMapEffect::Create(SkDisplacementMapEffect::kR_ChannelSelectorType,
260                                        SkDisplacementMapEffect::kB_ChannelSelectorType,
261                                        40.0f, input.get(), input.get(), &cropRect),
262        SkBlurImageFilter::Create(SK_Scalar1, SK_Scalar1, input.get(), &cropRect),
263        SkDropShadowImageFilter::Create(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1,
264            SK_ColorGREEN, SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode,
265            input.get(), &cropRect, 0),
266        SkLightingImageFilter::CreatePointLitDiffuse(location, SK_ColorGREEN, 0, 0, input.get(), &cropRect),
267        SkLightingImageFilter::CreatePointLitSpecular(location, SK_ColorGREEN, 0, 0, 0, input.get(), &cropRect),
268        SkMatrixConvolutionImageFilter::Create(kernelSize, kernel, gain, bias, SkIPoint::Make(1, 1), SkMatrixConvolutionImageFilter::kRepeat_TileMode, false, input.get(), &cropRect),
269        SkMergeImageFilter::Create(input.get(), input.get(), SkXfermode::kSrcOver_Mode, &cropRect),
270        SkOffsetImageFilter::Create(SK_Scalar1, SK_Scalar1, input.get(), &cropRect),
271        SkOffsetImageFilter::Create(SK_Scalar1, SK_Scalar1, input.get(), &cropRect),
272        SkDilateImageFilter::Create(3, 2, input.get(), &cropRect),
273        SkErodeImageFilter::Create(2, 3, input.get(), &cropRect),
274        SkTileImageFilter::Create(inputCropRect.rect(), cropRect.rect(), input.get()),
275        SkXfermodeImageFilter::Create(SkXfermode::Create(SkXfermode::kSrcOver_Mode), input.get(), input.get(), &cropRect),
276    };
277
278    for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) {
279        SkImageFilter* filter = filters[i];
280        SkBitmap result;
281        SkIPoint offset;
282        SkString str;
283        str.printf("filter %d", static_cast<int>(i));
284        SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeLargest(), NULL);
285        REPORTER_ASSERT_MESSAGE(reporter, filter->filterImage(&proxy, bitmap, ctx,
286                                &result, &offset), str.c_str());
287        REPORTER_ASSERT_MESSAGE(reporter, offset.fX == 20 && offset.fY == 30, str.c_str());
288    }
289
290    for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) {
291        SkSafeUnref(filters[i]);
292    }
293}
294
295static SkBitmap make_gradient_circle(int width, int height) {
296    SkBitmap bitmap;
297    SkScalar x = SkIntToScalar(width / 2);
298    SkScalar y = SkIntToScalar(height / 2);
299    SkScalar radius = SkMinScalar(x, y) * 0.8f;
300    bitmap.allocN32Pixels(width, height);
301    SkCanvas canvas(bitmap);
302    canvas.clear(0x00000000);
303    SkColor colors[2];
304    colors[0] = SK_ColorWHITE;
305    colors[1] = SK_ColorBLACK;
306    SkAutoTUnref<SkShader> shader(
307        SkGradientShader::CreateRadial(SkPoint::Make(x, y), radius, colors, NULL, 2,
308                                       SkShader::kClamp_TileMode)
309    );
310    SkPaint paint;
311    paint.setShader(shader);
312    canvas.drawCircle(x, y, radius, paint);
313    return bitmap;
314}
315
316static void test_negative_blur_sigma(SkBaseDevice* device, skiatest::Reporter* reporter) {
317    // Check that SkBlurImageFilter will accept a negative sigma, either in
318    // the given arguments or after CTM application.
319    int width = 32, height = 32;
320    SkDeviceImageFilterProxy proxy(device, SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType));
321    SkScalar five = SkIntToScalar(5);
322
323    SkAutoTUnref<SkBlurImageFilter> positiveFilter(
324        SkBlurImageFilter::Create(five, five)
325    );
326
327    SkAutoTUnref<SkBlurImageFilter> negativeFilter(
328        SkBlurImageFilter::Create(-five, five)
329    );
330
331    SkBitmap gradient = make_gradient_circle(width, height);
332    SkBitmap positiveResult1, negativeResult1;
333    SkBitmap positiveResult2, negativeResult2;
334    SkIPoint offset;
335    SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeLargest(), NULL);
336    positiveFilter->filterImage(&proxy, gradient, ctx, &positiveResult1, &offset);
337    negativeFilter->filterImage(&proxy, gradient, ctx, &negativeResult1, &offset);
338    SkMatrix negativeScale;
339    negativeScale.setScale(-SK_Scalar1, SK_Scalar1);
340    SkImageFilter::Context negativeCTX(negativeScale, SkIRect::MakeLargest(), NULL);
341    positiveFilter->filterImage(&proxy, gradient, negativeCTX, &negativeResult2, &offset);
342    negativeFilter->filterImage(&proxy, gradient, negativeCTX, &positiveResult2, &offset);
343    SkAutoLockPixels lockP1(positiveResult1);
344    SkAutoLockPixels lockP2(positiveResult2);
345    SkAutoLockPixels lockN1(negativeResult1);
346    SkAutoLockPixels lockN2(negativeResult2);
347    for (int y = 0; y < height; y++) {
348        int diffs = memcmp(positiveResult1.getAddr32(0, y), negativeResult1.getAddr32(0, y), positiveResult1.rowBytes());
349        REPORTER_ASSERT(reporter, !diffs);
350        if (diffs) {
351            break;
352        }
353        diffs = memcmp(positiveResult1.getAddr32(0, y), negativeResult2.getAddr32(0, y), positiveResult1.rowBytes());
354        REPORTER_ASSERT(reporter, !diffs);
355        if (diffs) {
356            break;
357        }
358        diffs = memcmp(positiveResult1.getAddr32(0, y), positiveResult2.getAddr32(0, y), positiveResult1.rowBytes());
359        REPORTER_ASSERT(reporter, !diffs);
360        if (diffs) {
361            break;
362        }
363    }
364}
365
366DEF_TEST(TestNegativeBlurSigma, reporter) {
367    SkBitmap temp;
368    temp.allocN32Pixels(100, 100);
369    SkBitmapDevice device(temp);
370    test_negative_blur_sigma(&device, reporter);
371}
372
373DEF_TEST(ImageFilterDrawTiled, reporter) {
374    // Check that all filters when drawn tiled (with subsequent clip rects) exactly
375    // match the same filters drawn with a single full-canvas bitmap draw.
376    // Tests pass by not asserting.
377
378    SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(SK_ColorRED, SkXfermode::kSrcIn_Mode));
379    SkPoint3 location(0, 0, SK_Scalar1);
380    SkPoint3 target(SK_Scalar1, SK_Scalar1, SK_Scalar1);
381    SkScalar kernel[9] = {
382        SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
383        SkIntToScalar( 1), SkIntToScalar(-7), SkIntToScalar( 1),
384        SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
385    };
386    SkISize kernelSize = SkISize::Make(3, 3);
387    SkScalar gain = SK_Scalar1, bias = 0;
388    SkScalar five = SkIntToScalar(5);
389
390    SkAutoTUnref<SkImageFilter> gradient_source(SkBitmapSource::Create(make_gradient_circle(64, 64)));
391    SkAutoTUnref<SkImageFilter> blur(SkBlurImageFilter::Create(five, five));
392    SkMatrix matrix;
393
394    matrix.setTranslate(SK_Scalar1, SK_Scalar1);
395    matrix.postRotate(SkIntToScalar(45), SK_Scalar1, SK_Scalar1);
396
397    SkRTreeFactory factory;
398    SkPictureRecorder recorder;
399    SkCanvas* recordingCanvas = recorder.beginRecording(64, 64, &factory, 0);
400
401    SkPaint greenPaint;
402    greenPaint.setColor(SK_ColorGREEN);
403    recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeXYWH(10, 10, 30, 20)), greenPaint);
404    SkAutoTUnref<SkPicture> picture(recorder.endRecording());
405    SkAutoTUnref<SkImageFilter> pictureFilter(SkPictureImageFilter::Create(picture.get()));
406    SkAutoTUnref<SkShader> shader(SkPerlinNoiseShader::CreateTurbulence(SK_Scalar1, SK_Scalar1, 1, 0));
407
408    SkAutoTUnref<SkImageFilter> rectShaderFilter(SkRectShaderImageFilter::Create(shader.get()));
409
410    struct {
411        const char*    fName;
412        SkImageFilter* fFilter;
413    } filters[] = {
414        { "color filter", SkColorFilterImageFilter::Create(cf.get()) },
415        { "displacement map", SkDisplacementMapEffect::Create(
416              SkDisplacementMapEffect::kR_ChannelSelectorType,
417              SkDisplacementMapEffect::kB_ChannelSelectorType,
418              20.0f, gradient_source.get()) },
419        { "blur", SkBlurImageFilter::Create(SK_Scalar1, SK_Scalar1) },
420        { "drop shadow", SkDropShadowImageFilter::Create(
421              SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_ColorGREEN,
422              SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode) },
423        { "diffuse lighting", SkLightingImageFilter::CreatePointLitDiffuse(
424              location, SK_ColorGREEN, 0, 0) },
425        { "specular lighting",
426              SkLightingImageFilter::CreatePointLitSpecular(location, SK_ColorGREEN, 0, 0, 0) },
427        { "matrix convolution",
428              SkMatrixConvolutionImageFilter::Create(
429                  kernelSize, kernel, gain, bias, SkIPoint::Make(1, 1),
430                  SkMatrixConvolutionImageFilter::kRepeat_TileMode, false) },
431        { "merge", SkMergeImageFilter::Create(NULL, NULL, SkXfermode::kSrcOver_Mode) },
432        { "offset", SkOffsetImageFilter::Create(SK_Scalar1, SK_Scalar1) },
433        { "dilate", SkDilateImageFilter::Create(3, 2) },
434        { "erode", SkErodeImageFilter::Create(2, 3) },
435        { "tile", SkTileImageFilter::Create(SkRect::MakeXYWH(0, 0, 50, 50),
436                                            SkRect::MakeXYWH(0, 0, 100, 100), NULL) },
437        { "matrix", SkMatrixImageFilter::Create(matrix, SkPaint::kLow_FilterLevel) },
438        { "blur and offset", SkOffsetImageFilter::Create(five, five, blur.get()) },
439        { "picture and blur", SkBlurImageFilter::Create(five, five, pictureFilter.get()) },
440        { "rect shader and blur", SkBlurImageFilter::Create(five, five, rectShaderFilter.get()) },
441    };
442
443    SkBitmap untiledResult, tiledResult;
444    int width = 64, height = 64;
445    untiledResult.allocN32Pixels(width, height);
446    tiledResult.allocN32Pixels(width, height);
447    SkCanvas tiledCanvas(tiledResult);
448    SkCanvas untiledCanvas(untiledResult);
449    int tileSize = 8;
450
451    for (int scale = 1; scale <= 2; ++scale) {
452        for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) {
453            tiledCanvas.clear(0);
454            untiledCanvas.clear(0);
455            SkPaint paint;
456            paint.setImageFilter(filters[i].fFilter);
457            paint.setTextSize(SkIntToScalar(height));
458            paint.setColor(SK_ColorWHITE);
459            SkString str;
460            const char* text = "ABC";
461            SkScalar ypos = SkIntToScalar(height);
462            untiledCanvas.save();
463            untiledCanvas.scale(SkIntToScalar(scale), SkIntToScalar(scale));
464            untiledCanvas.drawText(text, strlen(text), 0, ypos, paint);
465            untiledCanvas.restore();
466            for (int y = 0; y < height; y += tileSize) {
467                for (int x = 0; x < width; x += tileSize) {
468                    tiledCanvas.save();
469                    tiledCanvas.clipRect(SkRect::Make(SkIRect::MakeXYWH(x, y, tileSize, tileSize)));
470                    tiledCanvas.scale(SkIntToScalar(scale), SkIntToScalar(scale));
471                    tiledCanvas.drawText(text, strlen(text), 0, ypos, paint);
472                    tiledCanvas.restore();
473                }
474            }
475            untiledCanvas.flush();
476            tiledCanvas.flush();
477            for (int y = 0; y < height; y++) {
478                int diffs = memcmp(untiledResult.getAddr32(0, y), tiledResult.getAddr32(0, y), untiledResult.rowBytes());
479                REPORTER_ASSERT_MESSAGE(reporter, !diffs, filters[i].fName);
480                if (diffs) {
481                    break;
482                }
483            }
484        }
485    }
486
487    for (size_t i = 0; i < SK_ARRAY_COUNT(filters); ++i) {
488        SkSafeUnref(filters[i].fFilter);
489    }
490}
491
492static void draw_saveLayer_picture(int width, int height, int tileSize,
493                                   SkBBHFactory* factory, SkBitmap* result) {
494
495    SkMatrix matrix;
496    matrix.setTranslate(SkIntToScalar(50), 0);
497
498    SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(SK_ColorWHITE, SkXfermode::kSrc_Mode));
499    SkAutoTUnref<SkImageFilter> cfif(SkColorFilterImageFilter::Create(cf.get()));
500    SkAutoTUnref<SkImageFilter> imageFilter(SkMatrixImageFilter::Create(matrix, SkPaint::kNone_FilterLevel, cfif.get()));
501
502    SkPaint paint;
503    paint.setImageFilter(imageFilter.get());
504    SkPictureRecorder recorder;
505    SkRect bounds = SkRect::Make(SkIRect::MakeXYWH(0, 0, 50, 50));
506    SkCanvas* recordingCanvas = recorder.beginRecording(SkIntToScalar(width),
507                                                        SkIntToScalar(height),
508                                                        factory, 0);
509    recordingCanvas->translate(-55, 0);
510    recordingCanvas->saveLayer(&bounds, &paint);
511    recordingCanvas->restore();
512    SkAutoTUnref<SkPicture> picture1(recorder.endRecording());
513
514    result->allocN32Pixels(width, height);
515    SkCanvas canvas(*result);
516    canvas.clear(0);
517    canvas.clipRect(SkRect::Make(SkIRect::MakeWH(tileSize, tileSize)));
518    canvas.drawPicture(picture1.get());
519}
520
521DEF_TEST(ImageFilterDrawMatrixBBH, reporter) {
522    // Check that matrix filter when drawn tiled with BBH exactly
523    // matches the same thing drawn without BBH.
524    // Tests pass by not asserting.
525
526    const int width = 200, height = 200;
527    const int tileSize = 100;
528    SkBitmap result1, result2;
529    SkRTreeFactory factory;
530
531    draw_saveLayer_picture(width, height, tileSize, &factory, &result1);
532    draw_saveLayer_picture(width, height, tileSize, NULL, &result2);
533
534    for (int y = 0; y < height; y++) {
535        int diffs = memcmp(result1.getAddr32(0, y), result2.getAddr32(0, y), result1.rowBytes());
536        REPORTER_ASSERT(reporter, !diffs);
537        if (diffs) {
538            break;
539        }
540    }
541}
542
543static SkImageFilter* makeBlur(SkImageFilter* input = NULL) {
544    return SkBlurImageFilter::Create(SK_Scalar1, SK_Scalar1, input);
545}
546
547static SkImageFilter* makeDropShadow(SkImageFilter* input = NULL) {
548    return SkDropShadowImageFilter::Create(
549        SkIntToScalar(100), SkIntToScalar(100),
550        SkIntToScalar(10), SkIntToScalar(10),
551        SK_ColorBLUE, SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode,
552        input, NULL, 0);
553}
554
555DEF_TEST(ImageFilterBlurThenShadowBounds, reporter) {
556    SkAutoTUnref<SkImageFilter> filter1(makeBlur());
557    SkAutoTUnref<SkImageFilter> filter2(makeDropShadow(filter1.get()));
558
559    SkIRect bounds = SkIRect::MakeXYWH(0, 0, 100, 100);
560    SkIRect expectedBounds = SkIRect::MakeXYWH(-133, -133, 236, 236);
561    filter2->filterBounds(bounds, SkMatrix::I(), &bounds);
562
563    REPORTER_ASSERT(reporter, bounds == expectedBounds);
564}
565
566DEF_TEST(ImageFilterShadowThenBlurBounds, reporter) {
567    SkAutoTUnref<SkImageFilter> filter1(makeDropShadow());
568    SkAutoTUnref<SkImageFilter> filter2(makeBlur(filter1.get()));
569
570    SkIRect bounds = SkIRect::MakeXYWH(0, 0, 100, 100);
571    SkIRect expectedBounds = SkIRect::MakeXYWH(-133, -133, 236, 236);
572    filter2->filterBounds(bounds, SkMatrix::I(), &bounds);
573
574    REPORTER_ASSERT(reporter, bounds == expectedBounds);
575}
576
577DEF_TEST(ImageFilterDilateThenBlurBounds, reporter) {
578    SkAutoTUnref<SkImageFilter> filter1(SkDilateImageFilter::Create(2, 2));
579    SkAutoTUnref<SkImageFilter> filter2(makeDropShadow(filter1.get()));
580
581    SkIRect bounds = SkIRect::MakeXYWH(0, 0, 100, 100);
582    SkIRect expectedBounds = SkIRect::MakeXYWH(-132, -132, 234, 234);
583    filter2->filterBounds(bounds, SkMatrix::I(), &bounds);
584
585    REPORTER_ASSERT(reporter, bounds == expectedBounds);
586}
587
588DEF_TEST(ImageFilterComposedBlurFastBounds, reporter) {
589    SkAutoTUnref<SkImageFilter> filter1(makeBlur());
590    SkAutoTUnref<SkImageFilter> filter2(makeBlur());
591    SkAutoTUnref<SkImageFilter> composedFilter(SkComposeImageFilter::Create(filter1.get(), filter2.get()));
592
593    SkRect boundsSrc = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100));
594    SkRect expectedBounds = SkRect::MakeXYWH(
595        SkIntToScalar(-6), SkIntToScalar(-6), SkIntToScalar(112), SkIntToScalar(112));
596    SkRect boundsDst = SkRect::MakeEmpty();
597    composedFilter->computeFastBounds(boundsSrc, &boundsDst);
598
599    REPORTER_ASSERT(reporter, boundsDst == expectedBounds);
600}
601
602static void draw_blurred_rect(SkCanvas* canvas) {
603    SkAutoTUnref<SkImageFilter> filter(SkBlurImageFilter::Create(SkIntToScalar(8), 0));
604    SkPaint filterPaint;
605    filterPaint.setColor(SK_ColorWHITE);
606    filterPaint.setImageFilter(filter);
607    canvas->saveLayer(NULL, &filterPaint);
608    SkPaint whitePaint;
609    whitePaint.setColor(SK_ColorWHITE);
610    canvas->drawRect(SkRect::Make(SkIRect::MakeWH(4, 4)), whitePaint);
611    canvas->restore();
612}
613
614static void draw_picture_clipped(SkCanvas* canvas, const SkRect& clipRect, const SkPicture* picture) {
615    canvas->save();
616    canvas->clipRect(clipRect);
617    canvas->drawPicture(picture);
618    canvas->restore();
619}
620
621DEF_TEST(ImageFilterDrawTiledBlurRTree, reporter) {
622    // Check that the blur filter when recorded with RTree acceleration,
623    // and drawn tiled (with subsequent clip rects) exactly
624    // matches the same filter drawn with without RTree acceleration.
625    // This tests that the "bleed" from the blur into the otherwise-blank
626    // tiles is correctly rendered.
627    // Tests pass by not asserting.
628
629    int width = 16, height = 8;
630    SkBitmap result1, result2;
631    result1.allocN32Pixels(width, height);
632    result2.allocN32Pixels(width, height);
633    SkCanvas canvas1(result1);
634    SkCanvas canvas2(result2);
635    int tileSize = 8;
636
637    canvas1.clear(0);
638    canvas2.clear(0);
639
640    SkRTreeFactory factory;
641
642    SkPictureRecorder recorder1, recorder2;
643    // The only difference between these two pictures is that one has RTree aceleration.
644    SkCanvas* recordingCanvas1 = recorder1.beginRecording(SkIntToScalar(width),
645                                                          SkIntToScalar(height),
646                                                          NULL, 0);
647    SkCanvas* recordingCanvas2 = recorder2.beginRecording(SkIntToScalar(width),
648                                                          SkIntToScalar(height),
649                                                          &factory, 0);
650    draw_blurred_rect(recordingCanvas1);
651    draw_blurred_rect(recordingCanvas2);
652    SkAutoTUnref<SkPicture> picture1(recorder1.endRecording());
653    SkAutoTUnref<SkPicture> picture2(recorder2.endRecording());
654    for (int y = 0; y < height; y += tileSize) {
655        for (int x = 0; x < width; x += tileSize) {
656            SkRect tileRect = SkRect::Make(SkIRect::MakeXYWH(x, y, tileSize, tileSize));
657            draw_picture_clipped(&canvas1, tileRect, picture1);
658            draw_picture_clipped(&canvas2, tileRect, picture2);
659        }
660    }
661    for (int y = 0; y < height; y++) {
662        int diffs = memcmp(result1.getAddr32(0, y), result2.getAddr32(0, y), result1.rowBytes());
663        REPORTER_ASSERT(reporter, !diffs);
664        if (diffs) {
665            break;
666        }
667    }
668}
669
670DEF_TEST(ImageFilterMatrixConvolution, reporter) {
671    // Check that a 1x3 filter does not cause a spurious assert.
672    SkScalar kernel[3] = {
673        SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
674    };
675    SkISize kernelSize = SkISize::Make(1, 3);
676    SkScalar gain = SK_Scalar1, bias = 0;
677    SkIPoint kernelOffset = SkIPoint::Make(0, 0);
678
679    SkAutoTUnref<SkImageFilter> filter(
680        SkMatrixConvolutionImageFilter::Create(
681            kernelSize, kernel, gain, bias, kernelOffset,
682            SkMatrixConvolutionImageFilter::kRepeat_TileMode, false));
683
684    SkBitmap result;
685    int width = 16, height = 16;
686    result.allocN32Pixels(width, height);
687    SkCanvas canvas(result);
688    canvas.clear(0);
689
690    SkPaint paint;
691    paint.setImageFilter(filter);
692    SkRect rect = SkRect::Make(SkIRect::MakeWH(width, height));
693    canvas.drawRect(rect, paint);
694}
695
696DEF_TEST(ImageFilterMatrixConvolutionBorder, reporter) {
697    // Check that a filter with borders outside the target bounds
698    // does not crash.
699    SkScalar kernel[3] = {
700        0, 0, 0,
701    };
702    SkISize kernelSize = SkISize::Make(3, 1);
703    SkScalar gain = SK_Scalar1, bias = 0;
704    SkIPoint kernelOffset = SkIPoint::Make(2, 0);
705
706    SkAutoTUnref<SkImageFilter> filter(
707        SkMatrixConvolutionImageFilter::Create(
708            kernelSize, kernel, gain, bias, kernelOffset,
709            SkMatrixConvolutionImageFilter::kClamp_TileMode, true));
710
711    SkBitmap result;
712
713    int width = 10, height = 10;
714    result.allocN32Pixels(width, height);
715    SkCanvas canvas(result);
716    canvas.clear(0);
717
718    SkPaint filterPaint;
719    filterPaint.setImageFilter(filter);
720    SkRect bounds = SkRect::MakeWH(1, 10);
721    SkRect rect = SkRect::Make(SkIRect::MakeWH(width, height));
722    SkPaint rectPaint;
723    canvas.saveLayer(&bounds, &filterPaint);
724    canvas.drawRect(rect, rectPaint);
725    canvas.restore();
726}
727
728DEF_TEST(ImageFilterCropRect, reporter) {
729    SkBitmap temp;
730    temp.allocN32Pixels(100, 100);
731    SkBitmapDevice device(temp);
732    test_crop_rects(&device, reporter);
733}
734
735DEF_TEST(ImageFilterMatrix, reporter) {
736    SkBitmap temp;
737    temp.allocN32Pixels(100, 100);
738    SkBitmapDevice device(temp);
739    SkCanvas canvas(&device);
740    canvas.scale(SkIntToScalar(2), SkIntToScalar(2));
741
742    SkMatrix expectedMatrix = canvas.getTotalMatrix();
743
744    SkRTreeFactory factory;
745    SkPictureRecorder recorder;
746    SkCanvas* recordingCanvas = recorder.beginRecording(100, 100, &factory, 0);
747
748    SkPaint paint;
749    SkAutoTUnref<MatrixTestImageFilter> imageFilter(
750        new MatrixTestImageFilter(reporter, expectedMatrix));
751    paint.setImageFilter(imageFilter.get());
752    recordingCanvas->saveLayer(NULL, &paint);
753    SkPaint solidPaint;
754    solidPaint.setColor(0xFFFFFFFF);
755    recordingCanvas->save();
756    recordingCanvas->scale(SkIntToScalar(10), SkIntToScalar(10));
757    recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(100, 100)), solidPaint);
758    recordingCanvas->restore(); // scale
759    recordingCanvas->restore(); // saveLayer
760    SkAutoTUnref<SkPicture> picture(recorder.endRecording());
761
762    canvas.drawPicture(picture);
763}
764
765DEF_TEST(ImageFilterCrossProcessPictureImageFilter, reporter) {
766    SkRTreeFactory factory;
767    SkPictureRecorder recorder;
768    SkCanvas* recordingCanvas = recorder.beginRecording(1, 1, &factory, 0);
769
770    // Create an SkPicture which simply draws a green 1x1 rectangle.
771    SkPaint greenPaint;
772    greenPaint.setColor(SK_ColorGREEN);
773    recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), greenPaint);
774    SkAutoTUnref<SkPicture> picture(recorder.endRecording());
775
776    // Wrap that SkPicture in an SkPictureImageFilter.
777    SkAutoTUnref<SkImageFilter> imageFilter(
778        SkPictureImageFilter::Create(picture.get()));
779
780    // Check that SkPictureImageFilter successfully serializes its contained
781    // SkPicture when not in cross-process mode.
782    SkPaint paint;
783    paint.setImageFilter(imageFilter.get());
784    SkPictureRecorder outerRecorder;
785    SkCanvas* outerCanvas = outerRecorder.beginRecording(1, 1, &factory, 0);
786    SkPaint redPaintWithFilter;
787    redPaintWithFilter.setColor(SK_ColorRED);
788    redPaintWithFilter.setImageFilter(imageFilter.get());
789    outerCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), redPaintWithFilter);
790    SkAutoTUnref<SkPicture> outerPicture(outerRecorder.endRecording());
791
792    SkBitmap bitmap;
793    bitmap.allocN32Pixels(1, 1);
794    SkBitmapDevice device(bitmap);
795    SkCanvas canvas(&device);
796
797    // The result here should be green, since the filter replaces the primitive's red interior.
798    canvas.clear(0x0);
799    canvas.drawPicture(outerPicture);
800    uint32_t pixel = *bitmap.getAddr32(0, 0);
801    REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
802
803    // Check that, for now, SkPictureImageFilter does not serialize or
804    // deserialize its contained picture when the filter is serialized
805    // cross-process. Do this by "laundering" it through SkValidatingReadBuffer.
806    SkAutoTUnref<SkData> data(SkValidatingSerializeFlattenable(imageFilter.get()));
807    SkAutoTUnref<SkFlattenable> flattenable(SkValidatingDeserializeFlattenable(
808        data->data(), data->size(), SkImageFilter::GetFlattenableType()));
809    SkImageFilter* unflattenedFilter = static_cast<SkImageFilter*>(flattenable.get());
810
811    redPaintWithFilter.setImageFilter(unflattenedFilter);
812    SkPictureRecorder crossProcessRecorder;
813    SkCanvas* crossProcessCanvas = crossProcessRecorder.beginRecording(1, 1, &factory, 0);
814    crossProcessCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), redPaintWithFilter);
815    SkAutoTUnref<SkPicture> crossProcessPicture(crossProcessRecorder.endRecording());
816
817    canvas.clear(0x0);
818    canvas.drawPicture(crossProcessPicture);
819    pixel = *bitmap.getAddr32(0, 0);
820#ifdef SK_DISALLOW_CROSSPROCESS_PICTUREIMAGEFILTERS
821    // The result here should not be green, since the filter draws nothing.
822    REPORTER_ASSERT(reporter, pixel != SK_ColorGREEN);
823#else
824    REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
825#endif
826}
827
828DEF_TEST(ImageFilterClippedPictureImageFilter, reporter) {
829    SkRTreeFactory factory;
830    SkPictureRecorder recorder;
831    SkCanvas* recordingCanvas = recorder.beginRecording(1, 1, &factory, 0);
832
833    // Create an SkPicture which simply draws a green 1x1 rectangle.
834    SkPaint greenPaint;
835    greenPaint.setColor(SK_ColorGREEN);
836    recordingCanvas->drawRect(SkRect::Make(SkIRect::MakeWH(1, 1)), greenPaint);
837    SkAutoTUnref<SkPicture> picture(recorder.endRecording());
838
839    SkAutoTUnref<SkImageFilter> imageFilter(
840        SkPictureImageFilter::Create(picture.get()));
841
842    SkBitmap result;
843    SkIPoint offset;
844    SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeXYWH(1, 1, 1, 1), NULL);
845    SkBitmap bitmap;
846    bitmap.allocN32Pixels(2, 2);
847    SkBitmapDevice device(bitmap);
848    SkDeviceImageFilterProxy proxy(&device, SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType));
849    REPORTER_ASSERT(reporter, !imageFilter->filterImage(&proxy, bitmap, ctx, &result, &offset));
850}
851
852DEF_TEST(ImageFilterEmptySaveLayer, reporter) {
853    // Even when there's an empty saveLayer()/restore(), ensure that an image
854    // filter or color filter which affects transparent black still draws.
855
856    SkBitmap bitmap;
857    bitmap.allocN32Pixels(10, 10);
858    SkBitmapDevice device(bitmap);
859    SkCanvas canvas(&device);
860
861    SkRTreeFactory factory;
862    SkPictureRecorder recorder;
863
864    SkAutoTUnref<SkColorFilter> green(
865        SkColorFilter::CreateModeFilter(SK_ColorGREEN, SkXfermode::kSrc_Mode));
866    SkAutoTUnref<SkColorFilterImageFilter> imageFilter(
867        SkColorFilterImageFilter::Create(green.get()));
868    SkPaint imageFilterPaint;
869    imageFilterPaint.setImageFilter(imageFilter.get());
870    SkPaint colorFilterPaint;
871    colorFilterPaint.setColorFilter(green.get());
872
873    SkRect bounds = SkRect::MakeWH(10, 10);
874
875    SkCanvas* recordingCanvas = recorder.beginRecording(10, 10, &factory, 0);
876    recordingCanvas->saveLayer(&bounds, &imageFilterPaint);
877    recordingCanvas->restore();
878    SkAutoTUnref<SkPicture> picture(recorder.endRecording());
879
880    canvas.clear(0);
881    canvas.drawPicture(picture);
882    uint32_t pixel = *bitmap.getAddr32(0, 0);
883    REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
884
885    recordingCanvas = recorder.beginRecording(10, 10, &factory, 0);
886    recordingCanvas->saveLayer(NULL, &imageFilterPaint);
887    recordingCanvas->restore();
888    SkAutoTUnref<SkPicture> picture2(recorder.endRecording());
889
890    canvas.clear(0);
891    canvas.drawPicture(picture2);
892    pixel = *bitmap.getAddr32(0, 0);
893    REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
894
895    recordingCanvas = recorder.beginRecording(10, 10, &factory, 0);
896    recordingCanvas->saveLayer(&bounds, &colorFilterPaint);
897    recordingCanvas->restore();
898    SkAutoTUnref<SkPicture> picture3(recorder.endRecording());
899
900    canvas.clear(0);
901    canvas.drawPicture(picture3);
902    pixel = *bitmap.getAddr32(0, 0);
903    REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
904}
905
906static void test_huge_blur(SkBaseDevice* device, skiatest::Reporter* reporter) {
907    SkCanvas canvas(device);
908
909    SkBitmap bitmap;
910    bitmap.allocN32Pixels(100, 100);
911    bitmap.eraseARGB(0, 0, 0, 0);
912
913    // Check that a blur with an insane radius does not crash or assert.
914    SkAutoTUnref<SkImageFilter> blur(SkBlurImageFilter::Create(SkIntToScalar(1<<30), SkIntToScalar(1<<30)));
915
916    SkPaint paint;
917    paint.setImageFilter(blur);
918    canvas.drawSprite(bitmap, 0, 0, &paint);
919}
920
921DEF_TEST(HugeBlurImageFilter, reporter) {
922    SkBitmap temp;
923    temp.allocN32Pixels(100, 100);
924    SkBitmapDevice device(temp);
925    test_huge_blur(&device, reporter);
926}
927
928DEF_TEST(MatrixConvolutionSanityTest, reporter) {
929    SkScalar kernel[1] = { 0 };
930    SkScalar gain = SK_Scalar1, bias = 0;
931    SkIPoint kernelOffset = SkIPoint::Make(1, 1);
932
933    // Check that an enormous (non-allocatable) kernel gives a NULL filter.
934    SkAutoTUnref<SkImageFilter> conv(SkMatrixConvolutionImageFilter::Create(
935        SkISize::Make(1<<30, 1<<30),
936        kernel,
937        gain,
938        bias,
939        kernelOffset,
940        SkMatrixConvolutionImageFilter::kRepeat_TileMode,
941        false));
942
943    REPORTER_ASSERT(reporter, NULL == conv.get());
944
945    // Check that a NULL kernel gives a NULL filter.
946    conv.reset(SkMatrixConvolutionImageFilter::Create(
947        SkISize::Make(1, 1),
948        NULL,
949        gain,
950        bias,
951        kernelOffset,
952        SkMatrixConvolutionImageFilter::kRepeat_TileMode,
953        false));
954
955    REPORTER_ASSERT(reporter, NULL == conv.get());
956
957    // Check that a kernel width < 1 gives a NULL filter.
958    conv.reset(SkMatrixConvolutionImageFilter::Create(
959        SkISize::Make(0, 1),
960        kernel,
961        gain,
962        bias,
963        kernelOffset,
964        SkMatrixConvolutionImageFilter::kRepeat_TileMode,
965        false));
966
967    REPORTER_ASSERT(reporter, NULL == conv.get());
968
969    // Check that kernel height < 1 gives a NULL filter.
970    conv.reset(SkMatrixConvolutionImageFilter::Create(
971        SkISize::Make(1, -1),
972        kernel,
973        gain,
974        bias,
975        kernelOffset,
976        SkMatrixConvolutionImageFilter::kRepeat_TileMode,
977        false));
978
979    REPORTER_ASSERT(reporter, NULL == conv.get());
980}
981
982static void test_xfermode_cropped_input(SkBaseDevice* device, skiatest::Reporter* reporter) {
983    SkCanvas canvas(device);
984    canvas.clear(0);
985
986    SkBitmap bitmap;
987    bitmap.allocN32Pixels(1, 1);
988    bitmap.eraseARGB(255, 255, 255, 255);
989
990    SkAutoTUnref<SkColorFilter> green(
991        SkColorFilter::CreateModeFilter(SK_ColorGREEN, SkXfermode::kSrcIn_Mode));
992    SkAutoTUnref<SkColorFilterImageFilter> greenFilter(
993        SkColorFilterImageFilter::Create(green.get()));
994    SkImageFilter::CropRect cropRect(SkRect::MakeEmpty());
995    SkAutoTUnref<SkColorFilterImageFilter> croppedOut(
996        SkColorFilterImageFilter::Create(green.get(), NULL, &cropRect));
997
998    // Check that an xfermode image filter whose input has been cropped out still draws the other
999    // input. Also check that drawing with both inputs cropped out doesn't cause a GPU warning.
1000    SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrcOver_Mode);
1001    SkAutoTUnref<SkImageFilter> xfermodeNoFg(
1002        SkXfermodeImageFilter::Create(mode, greenFilter, croppedOut));
1003    SkAutoTUnref<SkImageFilter> xfermodeNoBg(
1004        SkXfermodeImageFilter::Create(mode, croppedOut, greenFilter));
1005    SkAutoTUnref<SkImageFilter> xfermodeNoFgNoBg(
1006        SkXfermodeImageFilter::Create(mode, croppedOut, croppedOut));
1007
1008    SkPaint paint;
1009    paint.setImageFilter(xfermodeNoFg);
1010    canvas.drawSprite(bitmap, 0, 0, &paint);
1011
1012    uint32_t pixel;
1013    SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
1014    canvas.readPixels(info, &pixel, 4, 0, 0);
1015    REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
1016
1017    paint.setImageFilter(xfermodeNoBg);
1018    canvas.drawSprite(bitmap, 0, 0, &paint);
1019    canvas.readPixels(info, &pixel, 4, 0, 0);
1020    REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
1021
1022    paint.setImageFilter(xfermodeNoFgNoBg);
1023    canvas.drawSprite(bitmap, 0, 0, &paint);
1024    canvas.readPixels(info, &pixel, 4, 0, 0);
1025    REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
1026}
1027
1028DEF_TEST(ImageFilterNestedSaveLayer, reporter) {
1029    SkBitmap temp;
1030    temp.allocN32Pixels(50, 50);
1031    SkBitmapDevice device(temp);
1032    SkCanvas canvas(&device);
1033    canvas.clear(0x0);
1034
1035    SkBitmap bitmap;
1036    bitmap.allocN32Pixels(10, 10);
1037    bitmap.eraseColor(SK_ColorGREEN);
1038
1039    SkMatrix matrix;
1040    matrix.setScale(SkIntToScalar(2), SkIntToScalar(2));
1041    matrix.postTranslate(SkIntToScalar(-20), SkIntToScalar(-20));
1042    SkAutoTUnref<SkImageFilter> matrixFilter(
1043        SkMatrixImageFilter::Create(matrix, SkPaint::kLow_FilterLevel));
1044
1045    // Test that saveLayer() with a filter nested inside another saveLayer() applies the
1046    // correct offset to the filter matrix.
1047    SkRect bounds1 = SkRect::MakeXYWH(10, 10, 30, 30);
1048    canvas.saveLayer(&bounds1, NULL);
1049    SkPaint filterPaint;
1050    filterPaint.setImageFilter(matrixFilter);
1051    SkRect bounds2 = SkRect::MakeXYWH(20, 20, 10, 10);
1052    canvas.saveLayer(&bounds2, &filterPaint);
1053    SkPaint greenPaint;
1054    greenPaint.setColor(SK_ColorGREEN);
1055    canvas.drawRect(bounds2, greenPaint);
1056    canvas.restore();
1057    canvas.restore();
1058    SkPaint strokePaint;
1059    strokePaint.setStyle(SkPaint::kStroke_Style);
1060    strokePaint.setColor(SK_ColorRED);
1061
1062    SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
1063    uint32_t pixel;
1064    canvas.readPixels(info, &pixel, 4, 25, 25);
1065    REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
1066
1067    // Test that drawSprite() with a filter nested inside a saveLayer() applies the
1068    // correct offset to the filter matrix.
1069    canvas.clear(0x0);
1070    canvas.readPixels(info, &pixel, 4, 25, 25);
1071    canvas.saveLayer(&bounds1, NULL);
1072    canvas.drawSprite(bitmap, 20, 20, &filterPaint);
1073    canvas.restore();
1074
1075    canvas.readPixels(info, &pixel, 4, 25, 25);
1076    REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN);
1077}
1078
1079DEF_TEST(XfermodeImageFilterCroppedInput, reporter) {
1080    SkBitmap temp;
1081    temp.allocN32Pixels(100, 100);
1082    SkBitmapDevice device(temp);
1083    test_xfermode_cropped_input(&device, reporter);
1084}
1085
1086DEF_TEST(ComposedImageFilterOffset, reporter) {
1087    SkBitmap bitmap;
1088    bitmap.allocN32Pixels(100, 100);
1089    bitmap.eraseARGB(0, 0, 0, 0);
1090    SkBitmapDevice device(bitmap);
1091    SkDeviceImageFilterProxy proxy(&device, SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType));
1092
1093    SkImageFilter::CropRect cropRect(SkRect::MakeXYWH(1, 0, 20, 20));
1094    SkAutoTUnref<SkImageFilter> offsetFilter(SkOffsetImageFilter::Create(0, 0, NULL, &cropRect));
1095    SkAutoTUnref<SkImageFilter> composedFilter(SkComposeImageFilter::Create(makeBlur(), offsetFilter.get()));
1096    SkBitmap result;
1097    SkIPoint offset;
1098    SkImageFilter::Context ctx(SkMatrix::I(), SkIRect::MakeLargest(), NULL);
1099    REPORTER_ASSERT(reporter, composedFilter->filterImage(&proxy, bitmap, ctx, &result, &offset));
1100    REPORTER_ASSERT(reporter, offset.fX == 1 && offset.fY == 0);
1101}
1102
1103#if SK_SUPPORT_GPU
1104const SkSurfaceProps gProps = SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType);
1105
1106DEF_GPUTEST(ImageFilterCropRectGPU, reporter, factory) {
1107    GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(0));
1108    SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context,
1109                                                         SkSurface::kNo_Budgeted,
1110                                                         SkImageInfo::MakeN32Premul(100, 100),
1111                                                         0,
1112                                                         &gProps));
1113    test_crop_rects(device, reporter);
1114}
1115
1116DEF_GPUTEST(HugeBlurImageFilterGPU, reporter, factory) {
1117    GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(0));
1118    SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context,
1119                                                         SkSurface::kNo_Budgeted,
1120                                                         SkImageInfo::MakeN32Premul(100, 100),
1121                                                         0,
1122                                                         &gProps));
1123    test_huge_blur(device, reporter);
1124}
1125
1126DEF_GPUTEST(XfermodeImageFilterCroppedInputGPU, reporter, factory) {
1127    GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(0));
1128    SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context,
1129                                                         SkSurface::kNo_Budgeted,
1130                                                         SkImageInfo::MakeN32Premul(1, 1),
1131                                                         0,
1132                                                         &gProps));
1133    test_xfermode_cropped_input(device, reporter);
1134}
1135
1136DEF_GPUTEST(TestNegativeBlurSigmaGPU, reporter, factory) {
1137    GrContext* context = factory->get(static_cast<GrContextFactory::GLContextType>(0));
1138    SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(context,
1139                                                         SkSurface::kNo_Budgeted,
1140                                                         SkImageInfo::MakeN32Premul(1, 1),
1141                                                         0,
1142                                                         &gProps));
1143    test_negative_blur_sigma(device, reporter);
1144}
1145#endif
1146