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