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 "SkSurface.h" 13 #include "SkTemplates.h" 14 #include "Test.h" 15 16 // https://code.google.com/p/chromium/issues/detail?id=448299 17 // Giant (inverse) matrix causes overflow when converting/computing using 32.32 18 // Before the fix, we would assert (and then crash). 19 static void test_big_grad(skiatest::Reporter* reporter) { 20 const SkColor colors[] = { SK_ColorRED, SK_ColorBLUE }; 21 const SkPoint pts[] = {{ 15, 14.7112684f }, { 0.709064007f, 12.6108112f }}; 22 SkShader* s = SkGradientShader::CreateLinear(pts, colors, nullptr, 2, SkShader::kClamp_TileMode); 23 SkPaint paint; 24 paint.setShader(s)->unref(); 25 26 SkBitmap bm; 27 bm.allocN32Pixels(2000, 1); 28 SkCanvas c(bm); 29 30 const SkScalar affine[] = { 31 1.06608627e-06f, 4.26434525e-07f, 6.2855f, 2.6611f, 273.4393f, 244.0046f 32 }; 33 SkMatrix matrix; 34 matrix.setAffine(affine); 35 c.concat(matrix); 36 37 c.drawPaint(paint); 38 } 39 40 struct GradRec { 41 int fColorCount; 42 const SkColor* fColors; 43 const SkScalar* fPos; 44 const SkPoint* fPoint; // 2 45 const SkScalar* fRadius; // 2 46 SkShader::TileMode fTileMode; 47 48 void gradCheck(skiatest::Reporter* reporter, SkShader* shader, 49 SkShader::GradientInfo* info, 50 SkShader::GradientType gt) const { 51 SkAutoTMalloc<SkColor> colorStorage(fColorCount); 52 SkAutoTMalloc<SkScalar> posStorage(fColorCount); 53 54 info->fColorCount = fColorCount; 55 info->fColors = colorStorage; 56 info->fColorOffsets = posStorage.get(); 57 REPORTER_ASSERT(reporter, shader->asAGradient(info) == gt); 58 59 REPORTER_ASSERT(reporter, info->fColorCount == fColorCount); 60 REPORTER_ASSERT(reporter, 61 !memcmp(info->fColors, fColors, fColorCount * sizeof(SkColor))); 62 REPORTER_ASSERT(reporter, 63 !memcmp(info->fColorOffsets, fPos, fColorCount * sizeof(SkScalar))); 64 REPORTER_ASSERT(reporter, fTileMode == info->fTileMode); 65 } 66 }; 67 68 69 static void none_gradproc(skiatest::Reporter* reporter, const GradRec&) { 70 SkAutoTUnref<SkShader> s(SkShader::CreateEmptyShader()); 71 REPORTER_ASSERT(reporter, SkShader::kNone_GradientType == s->asAGradient(nullptr)); 72 } 73 74 static void color_gradproc(skiatest::Reporter* reporter, const GradRec& rec) { 75 SkAutoTUnref<SkShader> s(new SkColorShader(rec.fColors[0])); 76 REPORTER_ASSERT(reporter, SkShader::kColor_GradientType == s->asAGradient(nullptr)); 77 78 SkShader::GradientInfo info; 79 info.fColors = nullptr; 80 info.fColorCount = 0; 81 s->asAGradient(&info); 82 REPORTER_ASSERT(reporter, 1 == info.fColorCount); 83 } 84 85 static void linear_gradproc(skiatest::Reporter* reporter, const GradRec& rec) { 86 SkAutoTUnref<SkShader> s(SkGradientShader::CreateLinear(rec.fPoint, 87 rec.fColors, 88 rec.fPos, 89 rec.fColorCount, 90 rec.fTileMode)); 91 92 SkShader::GradientInfo info; 93 rec.gradCheck(reporter, s, &info, SkShader::kLinear_GradientType); 94 REPORTER_ASSERT(reporter, !memcmp(info.fPoint, rec.fPoint, 2 * sizeof(SkPoint))); 95 } 96 97 static void radial_gradproc(skiatest::Reporter* reporter, const GradRec& rec) { 98 SkAutoTUnref<SkShader> s(SkGradientShader::CreateRadial(rec.fPoint[0], 99 rec.fRadius[0], 100 rec.fColors, 101 rec.fPos, 102 rec.fColorCount, 103 rec.fTileMode)); 104 105 SkShader::GradientInfo info; 106 rec.gradCheck(reporter, s, &info, SkShader::kRadial_GradientType); 107 REPORTER_ASSERT(reporter, info.fPoint[0] == rec.fPoint[0]); 108 REPORTER_ASSERT(reporter, info.fRadius[0] == rec.fRadius[0]); 109 } 110 111 static void sweep_gradproc(skiatest::Reporter* reporter, const GradRec& rec) { 112 SkAutoTUnref<SkShader> s(SkGradientShader::CreateSweep(rec.fPoint[0].fX, 113 rec.fPoint[0].fY, 114 rec.fColors, 115 rec.fPos, 116 rec.fColorCount)); 117 118 SkShader::GradientInfo info; 119 rec.gradCheck(reporter, s, &info, SkShader::kSweep_GradientType); 120 REPORTER_ASSERT(reporter, info.fPoint[0] == rec.fPoint[0]); 121 } 122 123 static void conical_gradproc(skiatest::Reporter* reporter, const GradRec& rec) { 124 SkAutoTUnref<SkShader> s(SkGradientShader::CreateTwoPointConical(rec.fPoint[0], 125 rec.fRadius[0], 126 rec.fPoint[1], 127 rec.fRadius[1], 128 rec.fColors, 129 rec.fPos, 130 rec.fColorCount, 131 rec.fTileMode)); 132 133 SkShader::GradientInfo info; 134 rec.gradCheck(reporter, s, &info, SkShader::kConical_GradientType); 135 REPORTER_ASSERT(reporter, !memcmp(info.fPoint, rec.fPoint, 2 * sizeof(SkPoint))); 136 REPORTER_ASSERT(reporter, !memcmp(info.fRadius, rec.fRadius, 2 * sizeof(SkScalar))); 137 } 138 139 // Ensure that repeated color gradients behave like drawing a single color 140 static void TestConstantGradient(skiatest::Reporter*) { 141 const SkPoint pts[] = { 142 { 0, 0 }, 143 { SkIntToScalar(10), 0 } 144 }; 145 SkColor colors[] = { SK_ColorBLUE, SK_ColorBLUE }; 146 const SkScalar pos[] = { 0, SK_Scalar1 }; 147 SkAutoTUnref<SkShader> s(SkGradientShader::CreateLinear(pts, 148 colors, 149 pos, 150 2, 151 SkShader::kClamp_TileMode)); 152 SkBitmap outBitmap; 153 outBitmap.allocN32Pixels(10, 1); 154 SkPaint paint; 155 paint.setShader(s.get()); 156 SkCanvas canvas(outBitmap); 157 canvas.drawPaint(paint); 158 SkAutoLockPixels alp(outBitmap); 159 for (int i = 0; i < 10; i++) { 160 // The following is commented out because it currently fails 161 // Related bug: https://code.google.com/p/skia/issues/detail?id=1098 162 163 // REPORTER_ASSERT(reporter, SK_ColorBLUE == outBitmap.getColor(i, 0)); 164 } 165 } 166 167 typedef void (*GradProc)(skiatest::Reporter* reporter, const GradRec&); 168 169 static void TestGradientShaders(skiatest::Reporter* reporter) { 170 static const SkColor gColors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE }; 171 static const SkScalar gPos[] = { 0, SK_ScalarHalf, SK_Scalar1 }; 172 static const SkPoint gPts[] = { 173 { 0, 0 }, 174 { SkIntToScalar(10), SkIntToScalar(20) } 175 }; 176 static const SkScalar gRad[] = { SkIntToScalar(1), SkIntToScalar(2) }; 177 178 GradRec rec; 179 rec.fColorCount = SK_ARRAY_COUNT(gColors); 180 rec.fColors = gColors; 181 rec.fPos = gPos; 182 rec.fPoint = gPts; 183 rec.fRadius = gRad; 184 rec.fTileMode = SkShader::kClamp_TileMode; 185 186 static const GradProc gProcs[] = { 187 none_gradproc, 188 color_gradproc, 189 linear_gradproc, 190 radial_gradproc, 191 sweep_gradproc, 192 conical_gradproc, 193 }; 194 195 for (size_t i = 0; i < SK_ARRAY_COUNT(gProcs); ++i) { 196 gProcs[i](reporter, rec); 197 } 198 } 199 200 static void test_nearly_vertical(skiatest::Reporter* reporter) { 201 SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(200, 200)); 202 203 const SkPoint pts[] = {{ 100, 50 }, { 100.0001f, 50000 }}; 204 const SkColor colors[] = { SK_ColorBLACK, SK_ColorWHITE }; 205 const SkScalar pos[] = { 0, 1 }; 206 SkAutoTUnref<SkShader> gradient( 207 SkGradientShader::CreateLinear(pts, colors, pos, 2, SkShader::kClamp_TileMode)); 208 209 SkPaint paint; 210 paint.setShader(gradient); 211 212 surface->getCanvas()->drawPaint(paint); 213 } 214 215 // A linear gradient interval can, due to numerical imprecision (likely in the divide) 216 // finish an interval with the final fx not landing outside of [p0...p1]. 217 // The old code had an assert which this test triggered. 218 // We now explicitly clamp the resulting fx value. 219 static void test_linear_fuzz(skiatest::Reporter* reporter) { 220 SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(1300, 630)); 221 222 const SkPoint pts[] = {{ 179.5f, -179.5f }, { 1074.5f, 715.5f }}; 223 const SkColor colors[] = { SK_ColorBLACK, SK_ColorWHITE, SK_ColorBLACK, SK_ColorWHITE }; 224 const SkScalar pos[] = {0, 0.200000003f, 0.800000012f, 1 }; 225 226 227 SkAutoTUnref<SkShader> gradient( 228 SkGradientShader::CreateLinear(pts, colors, pos, 4, SkShader::kClamp_TileMode)); 229 230 SkPaint paint; 231 paint.setShader(gradient); 232 SkRect r = {0, 83, 1254, 620}; 233 surface->getCanvas()->drawRect(r, paint); 234 } 235 236 DEF_TEST(Gradient, reporter) { 237 TestGradientShaders(reporter); 238 TestConstantGradient(reporter); 239 test_big_grad(reporter); 240 test_nearly_vertical(reporter); 241 test_linear_fuzz(reporter); 242 } 243