Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | |
michael@0 | 2 | /* |
michael@0 | 3 | * Copyright 2012 Google Inc. |
michael@0 | 4 | * |
michael@0 | 5 | * Use of this source code is governed by a BSD-style license that can be |
michael@0 | 6 | * found in the LICENSE file. |
michael@0 | 7 | */ |
michael@0 | 8 | |
michael@0 | 9 | #include "SkSweepGradient.h" |
michael@0 | 10 | |
michael@0 | 11 | SkSweepGradient::SkSweepGradient(SkScalar cx, SkScalar cy, |
michael@0 | 12 | const Descriptor& desc) |
michael@0 | 13 | : SkGradientShaderBase(desc) |
michael@0 | 14 | , fCenter(SkPoint::Make(cx, cy)) |
michael@0 | 15 | { |
michael@0 | 16 | fPtsToUnit.setTranslate(-cx, -cy); |
michael@0 | 17 | |
michael@0 | 18 | // overwrite the tilemode to a canonical value (since sweep ignores it) |
michael@0 | 19 | fTileMode = SkShader::kClamp_TileMode; |
michael@0 | 20 | } |
michael@0 | 21 | |
michael@0 | 22 | SkShader::BitmapType SkSweepGradient::asABitmap(SkBitmap* bitmap, |
michael@0 | 23 | SkMatrix* matrix, SkShader::TileMode* xy) const { |
michael@0 | 24 | if (bitmap) { |
michael@0 | 25 | this->getGradientTableBitmap(bitmap); |
michael@0 | 26 | } |
michael@0 | 27 | if (matrix) { |
michael@0 | 28 | *matrix = fPtsToUnit; |
michael@0 | 29 | } |
michael@0 | 30 | if (xy) { |
michael@0 | 31 | xy[0] = fTileMode; |
michael@0 | 32 | xy[1] = kClamp_TileMode; |
michael@0 | 33 | } |
michael@0 | 34 | return kSweep_BitmapType; |
michael@0 | 35 | } |
michael@0 | 36 | |
michael@0 | 37 | SkShader::GradientType SkSweepGradient::asAGradient(GradientInfo* info) const { |
michael@0 | 38 | if (info) { |
michael@0 | 39 | commonAsAGradient(info); |
michael@0 | 40 | info->fPoint[0] = fCenter; |
michael@0 | 41 | } |
michael@0 | 42 | return kSweep_GradientType; |
michael@0 | 43 | } |
michael@0 | 44 | |
michael@0 | 45 | SkSweepGradient::SkSweepGradient(SkReadBuffer& buffer) |
michael@0 | 46 | : INHERITED(buffer), |
michael@0 | 47 | fCenter(buffer.readPoint()) { |
michael@0 | 48 | } |
michael@0 | 49 | |
michael@0 | 50 | void SkSweepGradient::flatten(SkWriteBuffer& buffer) const { |
michael@0 | 51 | this->INHERITED::flatten(buffer); |
michael@0 | 52 | buffer.writePoint(fCenter); |
michael@0 | 53 | } |
michael@0 | 54 | |
michael@0 | 55 | // returns angle in a circle [0..2PI) -> [0..255] |
michael@0 | 56 | static unsigned SkATan2_255(float y, float x) { |
michael@0 | 57 | // static const float g255Over2PI = 255 / (2 * SK_ScalarPI); |
michael@0 | 58 | static const float g255Over2PI = 40.584510488433314f; |
michael@0 | 59 | |
michael@0 | 60 | float result = sk_float_atan2(y, x); |
michael@0 | 61 | if (result < 0) { |
michael@0 | 62 | result += 2 * SK_ScalarPI; |
michael@0 | 63 | } |
michael@0 | 64 | SkASSERT(result >= 0); |
michael@0 | 65 | // since our value is always >= 0, we can cast to int, which is faster than |
michael@0 | 66 | // calling floorf() |
michael@0 | 67 | int ir = (int)(result * g255Over2PI); |
michael@0 | 68 | SkASSERT(ir >= 0 && ir <= 255); |
michael@0 | 69 | return ir; |
michael@0 | 70 | } |
michael@0 | 71 | |
michael@0 | 72 | void SkSweepGradient::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC, |
michael@0 | 73 | int count) { |
michael@0 | 74 | SkMatrix::MapXYProc proc = fDstToIndexProc; |
michael@0 | 75 | const SkMatrix& matrix = fDstToIndex; |
michael@0 | 76 | const SkPMColor* SK_RESTRICT cache = this->getCache32(); |
michael@0 | 77 | int toggle = init_dither_toggle(x, y); |
michael@0 | 78 | SkPoint srcPt; |
michael@0 | 79 | |
michael@0 | 80 | if (fDstToIndexClass != kPerspective_MatrixClass) { |
michael@0 | 81 | proc(matrix, SkIntToScalar(x) + SK_ScalarHalf, |
michael@0 | 82 | SkIntToScalar(y) + SK_ScalarHalf, &srcPt); |
michael@0 | 83 | SkScalar dx, fx = srcPt.fX; |
michael@0 | 84 | SkScalar dy, fy = srcPt.fY; |
michael@0 | 85 | |
michael@0 | 86 | if (fDstToIndexClass == kFixedStepInX_MatrixClass) { |
michael@0 | 87 | SkFixed storage[2]; |
michael@0 | 88 | (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf, |
michael@0 | 89 | &storage[0], &storage[1]); |
michael@0 | 90 | dx = SkFixedToScalar(storage[0]); |
michael@0 | 91 | dy = SkFixedToScalar(storage[1]); |
michael@0 | 92 | } else { |
michael@0 | 93 | SkASSERT(fDstToIndexClass == kLinear_MatrixClass); |
michael@0 | 94 | dx = matrix.getScaleX(); |
michael@0 | 95 | dy = matrix.getSkewY(); |
michael@0 | 96 | } |
michael@0 | 97 | |
michael@0 | 98 | for (; count > 0; --count) { |
michael@0 | 99 | *dstC++ = cache[toggle + SkATan2_255(fy, fx)]; |
michael@0 | 100 | fx += dx; |
michael@0 | 101 | fy += dy; |
michael@0 | 102 | toggle = next_dither_toggle(toggle); |
michael@0 | 103 | } |
michael@0 | 104 | } else { // perspective case |
michael@0 | 105 | for (int stop = x + count; x < stop; x++) { |
michael@0 | 106 | proc(matrix, SkIntToScalar(x) + SK_ScalarHalf, |
michael@0 | 107 | SkIntToScalar(y) + SK_ScalarHalf, &srcPt); |
michael@0 | 108 | *dstC++ = cache[toggle + SkATan2_255(srcPt.fY, srcPt.fX)]; |
michael@0 | 109 | toggle = next_dither_toggle(toggle); |
michael@0 | 110 | } |
michael@0 | 111 | } |
michael@0 | 112 | } |
michael@0 | 113 | |
michael@0 | 114 | void SkSweepGradient::shadeSpan16(int x, int y, uint16_t* SK_RESTRICT dstC, |
michael@0 | 115 | int count) { |
michael@0 | 116 | SkMatrix::MapXYProc proc = fDstToIndexProc; |
michael@0 | 117 | const SkMatrix& matrix = fDstToIndex; |
michael@0 | 118 | const uint16_t* SK_RESTRICT cache = this->getCache16(); |
michael@0 | 119 | int toggle = init_dither_toggle16(x, y); |
michael@0 | 120 | SkPoint srcPt; |
michael@0 | 121 | |
michael@0 | 122 | if (fDstToIndexClass != kPerspective_MatrixClass) { |
michael@0 | 123 | proc(matrix, SkIntToScalar(x) + SK_ScalarHalf, |
michael@0 | 124 | SkIntToScalar(y) + SK_ScalarHalf, &srcPt); |
michael@0 | 125 | SkScalar dx, fx = srcPt.fX; |
michael@0 | 126 | SkScalar dy, fy = srcPt.fY; |
michael@0 | 127 | |
michael@0 | 128 | if (fDstToIndexClass == kFixedStepInX_MatrixClass) { |
michael@0 | 129 | SkFixed storage[2]; |
michael@0 | 130 | (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf, |
michael@0 | 131 | &storage[0], &storage[1]); |
michael@0 | 132 | dx = SkFixedToScalar(storage[0]); |
michael@0 | 133 | dy = SkFixedToScalar(storage[1]); |
michael@0 | 134 | } else { |
michael@0 | 135 | SkASSERT(fDstToIndexClass == kLinear_MatrixClass); |
michael@0 | 136 | dx = matrix.getScaleX(); |
michael@0 | 137 | dy = matrix.getSkewY(); |
michael@0 | 138 | } |
michael@0 | 139 | |
michael@0 | 140 | for (; count > 0; --count) { |
michael@0 | 141 | int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits); |
michael@0 | 142 | *dstC++ = cache[toggle + index]; |
michael@0 | 143 | toggle = next_dither_toggle16(toggle); |
michael@0 | 144 | fx += dx; |
michael@0 | 145 | fy += dy; |
michael@0 | 146 | } |
michael@0 | 147 | } else { // perspective case |
michael@0 | 148 | for (int stop = x + count; x < stop; x++) { |
michael@0 | 149 | proc(matrix, SkIntToScalar(x) + SK_ScalarHalf, |
michael@0 | 150 | SkIntToScalar(y) + SK_ScalarHalf, &srcPt); |
michael@0 | 151 | |
michael@0 | 152 | int index = SkATan2_255(srcPt.fY, srcPt.fX); |
michael@0 | 153 | index >>= (8 - kCache16Bits); |
michael@0 | 154 | *dstC++ = cache[toggle + index]; |
michael@0 | 155 | toggle = next_dither_toggle16(toggle); |
michael@0 | 156 | } |
michael@0 | 157 | } |
michael@0 | 158 | } |
michael@0 | 159 | |
michael@0 | 160 | ///////////////////////////////////////////////////////////////////// |
michael@0 | 161 | |
michael@0 | 162 | #if SK_SUPPORT_GPU |
michael@0 | 163 | |
michael@0 | 164 | #include "GrTBackendEffectFactory.h" |
michael@0 | 165 | |
michael@0 | 166 | class GrGLSweepGradient : public GrGLGradientEffect { |
michael@0 | 167 | public: |
michael@0 | 168 | |
michael@0 | 169 | GrGLSweepGradient(const GrBackendEffectFactory& factory, |
michael@0 | 170 | const GrDrawEffect&) : INHERITED (factory) { } |
michael@0 | 171 | virtual ~GrGLSweepGradient() { } |
michael@0 | 172 | |
michael@0 | 173 | virtual void emitCode(GrGLShaderBuilder*, |
michael@0 | 174 | const GrDrawEffect&, |
michael@0 | 175 | EffectKey, |
michael@0 | 176 | const char* outputColor, |
michael@0 | 177 | const char* inputColor, |
michael@0 | 178 | const TransformedCoordsArray&, |
michael@0 | 179 | const TextureSamplerArray&) SK_OVERRIDE; |
michael@0 | 180 | |
michael@0 | 181 | static EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) { |
michael@0 | 182 | return GenBaseGradientKey(drawEffect); |
michael@0 | 183 | } |
michael@0 | 184 | |
michael@0 | 185 | private: |
michael@0 | 186 | |
michael@0 | 187 | typedef GrGLGradientEffect INHERITED; |
michael@0 | 188 | |
michael@0 | 189 | }; |
michael@0 | 190 | |
michael@0 | 191 | ///////////////////////////////////////////////////////////////////// |
michael@0 | 192 | |
michael@0 | 193 | class GrSweepGradient : public GrGradientEffect { |
michael@0 | 194 | public: |
michael@0 | 195 | static GrEffectRef* Create(GrContext* ctx, |
michael@0 | 196 | const SkSweepGradient& shader, |
michael@0 | 197 | const SkMatrix& matrix) { |
michael@0 | 198 | AutoEffectUnref effect(SkNEW_ARGS(GrSweepGradient, (ctx, shader, matrix))); |
michael@0 | 199 | return CreateEffectRef(effect); |
michael@0 | 200 | } |
michael@0 | 201 | virtual ~GrSweepGradient() { } |
michael@0 | 202 | |
michael@0 | 203 | static const char* Name() { return "Sweep Gradient"; } |
michael@0 | 204 | virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { |
michael@0 | 205 | return GrTBackendEffectFactory<GrSweepGradient>::getInstance(); |
michael@0 | 206 | } |
michael@0 | 207 | |
michael@0 | 208 | typedef GrGLSweepGradient GLEffect; |
michael@0 | 209 | |
michael@0 | 210 | private: |
michael@0 | 211 | GrSweepGradient(GrContext* ctx, |
michael@0 | 212 | const SkSweepGradient& shader, |
michael@0 | 213 | const SkMatrix& matrix) |
michael@0 | 214 | : INHERITED(ctx, shader, matrix, SkShader::kClamp_TileMode) { } |
michael@0 | 215 | GR_DECLARE_EFFECT_TEST; |
michael@0 | 216 | |
michael@0 | 217 | typedef GrGradientEffect INHERITED; |
michael@0 | 218 | }; |
michael@0 | 219 | |
michael@0 | 220 | ///////////////////////////////////////////////////////////////////// |
michael@0 | 221 | |
michael@0 | 222 | GR_DEFINE_EFFECT_TEST(GrSweepGradient); |
michael@0 | 223 | |
michael@0 | 224 | GrEffectRef* GrSweepGradient::TestCreate(SkRandom* random, |
michael@0 | 225 | GrContext* context, |
michael@0 | 226 | const GrDrawTargetCaps&, |
michael@0 | 227 | GrTexture**) { |
michael@0 | 228 | SkPoint center = {random->nextUScalar1(), random->nextUScalar1()}; |
michael@0 | 229 | |
michael@0 | 230 | SkColor colors[kMaxRandomGradientColors]; |
michael@0 | 231 | SkScalar stopsArray[kMaxRandomGradientColors]; |
michael@0 | 232 | SkScalar* stops = stopsArray; |
michael@0 | 233 | SkShader::TileMode tmIgnored; |
michael@0 | 234 | int colorCount = RandomGradientParams(random, colors, &stops, &tmIgnored); |
michael@0 | 235 | SkAutoTUnref<SkShader> shader(SkGradientShader::CreateSweep(center.fX, center.fY, |
michael@0 | 236 | colors, stops, colorCount)); |
michael@0 | 237 | SkPaint paint; |
michael@0 | 238 | return shader->asNewEffect(context, paint); |
michael@0 | 239 | } |
michael@0 | 240 | |
michael@0 | 241 | ///////////////////////////////////////////////////////////////////// |
michael@0 | 242 | |
michael@0 | 243 | void GrGLSweepGradient::emitCode(GrGLShaderBuilder* builder, |
michael@0 | 244 | const GrDrawEffect&, |
michael@0 | 245 | EffectKey key, |
michael@0 | 246 | const char* outputColor, |
michael@0 | 247 | const char* inputColor, |
michael@0 | 248 | const TransformedCoordsArray& coords, |
michael@0 | 249 | const TextureSamplerArray& samplers) { |
michael@0 | 250 | this->emitUniforms(builder, key); |
michael@0 | 251 | SkString coords2D = builder->ensureFSCoords2D(coords, 0); |
michael@0 | 252 | const GrGLContextInfo ctxInfo = builder->ctxInfo(); |
michael@0 | 253 | SkString t; |
michael@0 | 254 | // 0.1591549430918 is 1/(2*pi), used since atan returns values [-pi, pi] |
michael@0 | 255 | // On Intel GPU there is an issue where it reads the second arguement to atan "- %s.x" as an int |
michael@0 | 256 | // thus must us -1.0 * %s.x to work correctly |
michael@0 | 257 | if (kIntel_GrGLVendor != ctxInfo.vendor()){ |
michael@0 | 258 | t.printf("atan(- %s.y, - %s.x) * 0.1591549430918 + 0.5", |
michael@0 | 259 | coords2D.c_str(), coords2D.c_str()); |
michael@0 | 260 | } else { |
michael@0 | 261 | t.printf("atan(- %s.y, -1.0 * %s.x) * 0.1591549430918 + 0.5", |
michael@0 | 262 | coords2D.c_str(), coords2D.c_str()); |
michael@0 | 263 | } |
michael@0 | 264 | this->emitColor(builder, t.c_str(), key, |
michael@0 | 265 | outputColor, inputColor, samplers); |
michael@0 | 266 | } |
michael@0 | 267 | |
michael@0 | 268 | ///////////////////////////////////////////////////////////////////// |
michael@0 | 269 | |
michael@0 | 270 | GrEffectRef* SkSweepGradient::asNewEffect(GrContext* context, const SkPaint&) const { |
michael@0 | 271 | SkMatrix matrix; |
michael@0 | 272 | if (!this->getLocalMatrix().invert(&matrix)) { |
michael@0 | 273 | return NULL; |
michael@0 | 274 | } |
michael@0 | 275 | matrix.postConcat(fPtsToUnit); |
michael@0 | 276 | return GrSweepGradient::Create(context, *this, matrix); |
michael@0 | 277 | } |
michael@0 | 278 | |
michael@0 | 279 | #else |
michael@0 | 280 | |
michael@0 | 281 | GrEffectRef* SkSweepGradient::asNewEffect(GrContext*, const SkPaint&) const { |
michael@0 | 282 | SkDEBUGFAIL("Should not call in GPU-less build"); |
michael@0 | 283 | return NULL; |
michael@0 | 284 | } |
michael@0 | 285 | |
michael@0 | 286 | #endif |
michael@0 | 287 | |
michael@0 | 288 | #ifndef SK_IGNORE_TO_STRING |
michael@0 | 289 | void SkSweepGradient::toString(SkString* str) const { |
michael@0 | 290 | str->append("SkSweepGradient: ("); |
michael@0 | 291 | |
michael@0 | 292 | str->append("center: ("); |
michael@0 | 293 | str->appendScalar(fCenter.fX); |
michael@0 | 294 | str->append(", "); |
michael@0 | 295 | str->appendScalar(fCenter.fY); |
michael@0 | 296 | str->append(") "); |
michael@0 | 297 | |
michael@0 | 298 | this->INHERITED::toString(str); |
michael@0 | 299 | |
michael@0 | 300 | str->append(")"); |
michael@0 | 301 | } |
michael@0 | 302 | #endif |