1/* 2 * Copyright 2011 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 "SkCanvas.h" 9#include "SkColorShader.h" 10#include "SkGradientShader.h" 11#include "SkShader.h" 12#include "SkTemplates.h" 13#include "Test.h" 14 15// https://code.google.com/p/chromium/issues/detail?id=448299 16// Giant (inverse) matrix causes overflow when converting/computing using 32.32 17// Before the fix, we would assert (and then crash). 18static void test_big_grad(skiatest::Reporter* reporter) { 19 const SkColor colors[] = { SK_ColorRED, SK_ColorBLUE }; 20 const SkPoint pts[] = {{ 15, 14.7112684f }, { 0.709064007f, 12.6108112f }}; 21 SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL, 2, SkShader::kClamp_TileMode); 22 SkPaint paint; 23 paint.setShader(s)->unref(); 24 25 SkBitmap bm; 26 bm.allocN32Pixels(2000, 1); 27 SkCanvas c(bm); 28 29 const SkScalar affine[] = { 30 1.06608627e-06f, 4.26434525e-07f, 6.2855f, 2.6611f, 273.4393f, 244.0046f 31 }; 32 SkMatrix matrix; 33 matrix.setAffine(affine); 34 c.concat(matrix); 35 36 c.drawPaint(paint); 37} 38 39struct GradRec { 40 int fColorCount; 41 const SkColor* fColors; 42 const SkScalar* fPos; 43 const SkPoint* fPoint; // 2 44 const SkScalar* fRadius; // 2 45 SkShader::TileMode fTileMode; 46 47 void gradCheck(skiatest::Reporter* reporter, SkShader* shader, 48 SkShader::GradientInfo* info, 49 SkShader::GradientType gt) const { 50 SkAutoTMalloc<SkColor> colorStorage(fColorCount); 51 SkAutoTMalloc<SkScalar> posStorage(fColorCount); 52 53 info->fColorCount = fColorCount; 54 info->fColors = colorStorage; 55 info->fColorOffsets = posStorage.get(); 56 REPORTER_ASSERT(reporter, shader->asAGradient(info) == gt); 57 58 REPORTER_ASSERT(reporter, info->fColorCount == fColorCount); 59 REPORTER_ASSERT(reporter, 60 !memcmp(info->fColors, fColors, fColorCount * sizeof(SkColor))); 61 REPORTER_ASSERT(reporter, 62 !memcmp(info->fColorOffsets, fPos, fColorCount * sizeof(SkScalar))); 63 REPORTER_ASSERT(reporter, fTileMode == info->fTileMode); 64 } 65}; 66 67 68static void none_gradproc(skiatest::Reporter* reporter, const GradRec&) { 69 SkAutoTUnref<SkShader> s(SkShader::CreateEmptyShader()); 70 REPORTER_ASSERT(reporter, SkShader::kNone_GradientType == s->asAGradient(NULL)); 71} 72 73static void color_gradproc(skiatest::Reporter* reporter, const GradRec& rec) { 74 SkAutoTUnref<SkShader> s(new SkColorShader(rec.fColors[0])); 75 REPORTER_ASSERT(reporter, SkShader::kColor_GradientType == s->asAGradient(NULL)); 76 77 SkShader::GradientInfo info; 78 info.fColors = NULL; 79 info.fColorCount = 0; 80 s->asAGradient(&info); 81 REPORTER_ASSERT(reporter, 1 == info.fColorCount); 82} 83 84static void linear_gradproc(skiatest::Reporter* reporter, const GradRec& rec) { 85 SkAutoTUnref<SkShader> s(SkGradientShader::CreateLinear(rec.fPoint, 86 rec.fColors, 87 rec.fPos, 88 rec.fColorCount, 89 rec.fTileMode)); 90 91 SkShader::GradientInfo info; 92 rec.gradCheck(reporter, s, &info, SkShader::kLinear_GradientType); 93 REPORTER_ASSERT(reporter, !memcmp(info.fPoint, rec.fPoint, 2 * sizeof(SkPoint))); 94} 95 96static void radial_gradproc(skiatest::Reporter* reporter, const GradRec& rec) { 97 SkAutoTUnref<SkShader> s(SkGradientShader::CreateRadial(rec.fPoint[0], 98 rec.fRadius[0], 99 rec.fColors, 100 rec.fPos, 101 rec.fColorCount, 102 rec.fTileMode)); 103 104 SkShader::GradientInfo info; 105 rec.gradCheck(reporter, s, &info, SkShader::kRadial_GradientType); 106 REPORTER_ASSERT(reporter, info.fPoint[0] == rec.fPoint[0]); 107 REPORTER_ASSERT(reporter, info.fRadius[0] == rec.fRadius[0]); 108} 109 110static void sweep_gradproc(skiatest::Reporter* reporter, const GradRec& rec) { 111 SkAutoTUnref<SkShader> s(SkGradientShader::CreateSweep(rec.fPoint[0].fX, 112 rec.fPoint[0].fY, 113 rec.fColors, 114 rec.fPos, 115 rec.fColorCount)); 116 117 SkShader::GradientInfo info; 118 rec.gradCheck(reporter, s, &info, SkShader::kSweep_GradientType); 119 REPORTER_ASSERT(reporter, info.fPoint[0] == rec.fPoint[0]); 120} 121 122static void conical_gradproc(skiatest::Reporter* reporter, const GradRec& rec) { 123 SkAutoTUnref<SkShader> s(SkGradientShader::CreateTwoPointConical(rec.fPoint[0], 124 rec.fRadius[0], 125 rec.fPoint[1], 126 rec.fRadius[1], 127 rec.fColors, 128 rec.fPos, 129 rec.fColorCount, 130 rec.fTileMode)); 131 132 SkShader::GradientInfo info; 133 rec.gradCheck(reporter, s, &info, SkShader::kConical_GradientType); 134 REPORTER_ASSERT(reporter, !memcmp(info.fPoint, rec.fPoint, 2 * sizeof(SkPoint))); 135 REPORTER_ASSERT(reporter, !memcmp(info.fRadius, rec.fRadius, 2 * sizeof(SkScalar))); 136} 137 138// Ensure that repeated color gradients behave like drawing a single color 139static void TestConstantGradient(skiatest::Reporter*) { 140 const SkPoint pts[] = { 141 { 0, 0 }, 142 { SkIntToScalar(10), 0 } 143 }; 144 SkColor colors[] = { SK_ColorBLUE, SK_ColorBLUE }; 145 const SkScalar pos[] = { 0, SK_Scalar1 }; 146 SkAutoTUnref<SkShader> s(SkGradientShader::CreateLinear(pts, 147 colors, 148 pos, 149 2, 150 SkShader::kClamp_TileMode)); 151 SkBitmap outBitmap; 152 outBitmap.allocN32Pixels(10, 1); 153 SkPaint paint; 154 paint.setShader(s.get()); 155 SkCanvas canvas(outBitmap); 156 canvas.drawPaint(paint); 157 SkAutoLockPixels alp(outBitmap); 158 for (int i = 0; i < 10; i++) { 159 // The following is commented out because it currently fails 160 // Related bug: https://code.google.com/p/skia/issues/detail?id=1098 161 162 // REPORTER_ASSERT(reporter, SK_ColorBLUE == outBitmap.getColor(i, 0)); 163 } 164} 165 166typedef void (*GradProc)(skiatest::Reporter* reporter, const GradRec&); 167 168static void TestGradientShaders(skiatest::Reporter* reporter) { 169 static const SkColor gColors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE }; 170 static const SkScalar gPos[] = { 0, SK_ScalarHalf, SK_Scalar1 }; 171 static const SkPoint gPts[] = { 172 { 0, 0 }, 173 { SkIntToScalar(10), SkIntToScalar(20) } 174 }; 175 static const SkScalar gRad[] = { SkIntToScalar(1), SkIntToScalar(2) }; 176 177 GradRec rec; 178 rec.fColorCount = SK_ARRAY_COUNT(gColors); 179 rec.fColors = gColors; 180 rec.fPos = gPos; 181 rec.fPoint = gPts; 182 rec.fRadius = gRad; 183 rec.fTileMode = SkShader::kClamp_TileMode; 184 185 static const GradProc gProcs[] = { 186 none_gradproc, 187 color_gradproc, 188 linear_gradproc, 189 radial_gradproc, 190 sweep_gradproc, 191 conical_gradproc, 192 }; 193 194 for (size_t i = 0; i < SK_ARRAY_COUNT(gProcs); ++i) { 195 gProcs[i](reporter, rec); 196 } 197} 198 199DEF_TEST(Gradient, reporter) { 200 TestGradientShaders(reporter); 201 TestConstantGradient(reporter); 202 test_big_grad(reporter); 203} 204