1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/skia/trunk/src/pdf/SkPDFShader.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1348 @@ 1.4 + 1.5 +/* 1.6 + * Copyright 2011 Google Inc. 1.7 + * 1.8 + * Use of this source code is governed by a BSD-style license that can be 1.9 + * found in the LICENSE file. 1.10 + */ 1.11 + 1.12 + 1.13 +#include "SkPDFShader.h" 1.14 + 1.15 +#include "SkData.h" 1.16 +#include "SkPDFCatalog.h" 1.17 +#include "SkPDFDevice.h" 1.18 +#include "SkPDFFormXObject.h" 1.19 +#include "SkPDFGraphicState.h" 1.20 +#include "SkPDFResourceDict.h" 1.21 +#include "SkPDFUtils.h" 1.22 +#include "SkScalar.h" 1.23 +#include "SkStream.h" 1.24 +#include "SkTemplates.h" 1.25 +#include "SkThread.h" 1.26 +#include "SkTSet.h" 1.27 +#include "SkTypes.h" 1.28 + 1.29 +static bool inverseTransformBBox(const SkMatrix& matrix, SkRect* bbox) { 1.30 + SkMatrix inverse; 1.31 + if (!matrix.invert(&inverse)) { 1.32 + return false; 1.33 + } 1.34 + inverse.mapRect(bbox); 1.35 + return true; 1.36 +} 1.37 + 1.38 +static void unitToPointsMatrix(const SkPoint pts[2], SkMatrix* matrix) { 1.39 + SkVector vec = pts[1] - pts[0]; 1.40 + SkScalar mag = vec.length(); 1.41 + SkScalar inv = mag ? SkScalarInvert(mag) : 0; 1.42 + 1.43 + vec.scale(inv); 1.44 + matrix->setSinCos(vec.fY, vec.fX); 1.45 + matrix->preScale(mag, mag); 1.46 + matrix->postTranslate(pts[0].fX, pts[0].fY); 1.47 +} 1.48 + 1.49 +/* Assumes t + startOffset is on the stack and does a linear interpolation on t 1.50 + between startOffset and endOffset from prevColor to curColor (for each color 1.51 + component), leaving the result in component order on the stack. It assumes 1.52 + there are always 3 components per color. 1.53 + @param range endOffset - startOffset 1.54 + @param curColor[components] The current color components. 1.55 + @param prevColor[components] The previous color components. 1.56 + @param result The result ps function. 1.57 + */ 1.58 +static void interpolateColorCode(SkScalar range, SkScalar* curColor, 1.59 + SkScalar* prevColor, SkString* result) { 1.60 + SkASSERT(range != SkIntToScalar(0)); 1.61 + static const int kColorComponents = 3; 1.62 + 1.63 + // Figure out how to scale each color component. 1.64 + SkScalar multiplier[kColorComponents]; 1.65 + for (int i = 0; i < kColorComponents; i++) { 1.66 + multiplier[i] = SkScalarDiv(curColor[i] - prevColor[i], range); 1.67 + } 1.68 + 1.69 + // Calculate when we no longer need to keep a copy of the input parameter t. 1.70 + // If the last component to use t is i, then dupInput[0..i - 1] = true 1.71 + // and dupInput[i .. components] = false. 1.72 + bool dupInput[kColorComponents]; 1.73 + dupInput[kColorComponents - 1] = false; 1.74 + for (int i = kColorComponents - 2; i >= 0; i--) { 1.75 + dupInput[i] = dupInput[i + 1] || multiplier[i + 1] != 0; 1.76 + } 1.77 + 1.78 + if (!dupInput[0] && multiplier[0] == 0) { 1.79 + result->append("pop "); 1.80 + } 1.81 + 1.82 + for (int i = 0; i < kColorComponents; i++) { 1.83 + // If the next components needs t and this component will consume a 1.84 + // copy, make another copy. 1.85 + if (dupInput[i] && multiplier[i] != 0) { 1.86 + result->append("dup "); 1.87 + } 1.88 + 1.89 + if (multiplier[i] == 0) { 1.90 + result->appendScalar(prevColor[i]); 1.91 + result->append(" "); 1.92 + } else { 1.93 + if (multiplier[i] != 1) { 1.94 + result->appendScalar(multiplier[i]); 1.95 + result->append(" mul "); 1.96 + } 1.97 + if (prevColor[i] != 0) { 1.98 + result->appendScalar(prevColor[i]); 1.99 + result->append(" add "); 1.100 + } 1.101 + } 1.102 + 1.103 + if (dupInput[i]) { 1.104 + result->append("exch\n"); 1.105 + } 1.106 + } 1.107 +} 1.108 + 1.109 +/* Generate Type 4 function code to map t=[0,1) to the passed gradient, 1.110 + clamping at the edges of the range. The generated code will be of the form: 1.111 + if (t < 0) { 1.112 + return colorData[0][r,g,b]; 1.113 + } else { 1.114 + if (t < info.fColorOffsets[1]) { 1.115 + return linearinterpolation(colorData[0][r,g,b], 1.116 + colorData[1][r,g,b]); 1.117 + } else { 1.118 + if (t < info.fColorOffsets[2]) { 1.119 + return linearinterpolation(colorData[1][r,g,b], 1.120 + colorData[2][r,g,b]); 1.121 + } else { 1.122 + 1.123 + ... } else { 1.124 + return colorData[info.fColorCount - 1][r,g,b]; 1.125 + } 1.126 + ... 1.127 + } 1.128 + } 1.129 + */ 1.130 +static void gradientFunctionCode(const SkShader::GradientInfo& info, 1.131 + SkString* result) { 1.132 + /* We want to linearly interpolate from the previous color to the next. 1.133 + Scale the colors from 0..255 to 0..1 and determine the multipliers 1.134 + for interpolation. 1.135 + C{r,g,b}(t, section) = t - offset_(section-1) + t * Multiplier{r,g,b}. 1.136 + */ 1.137 + static const int kColorComponents = 3; 1.138 + typedef SkScalar ColorTuple[kColorComponents]; 1.139 + SkAutoSTMalloc<4, ColorTuple> colorDataAlloc(info.fColorCount); 1.140 + ColorTuple *colorData = colorDataAlloc.get(); 1.141 + const SkScalar scale = SkScalarInvert(SkIntToScalar(255)); 1.142 + for (int i = 0; i < info.fColorCount; i++) { 1.143 + colorData[i][0] = SkScalarMul(SkColorGetR(info.fColors[i]), scale); 1.144 + colorData[i][1] = SkScalarMul(SkColorGetG(info.fColors[i]), scale); 1.145 + colorData[i][2] = SkScalarMul(SkColorGetB(info.fColors[i]), scale); 1.146 + } 1.147 + 1.148 + // Clamp the initial color. 1.149 + result->append("dup 0 le {pop "); 1.150 + result->appendScalar(colorData[0][0]); 1.151 + result->append(" "); 1.152 + result->appendScalar(colorData[0][1]); 1.153 + result->append(" "); 1.154 + result->appendScalar(colorData[0][2]); 1.155 + result->append(" }\n"); 1.156 + 1.157 + // The gradient colors. 1.158 + int gradients = 0; 1.159 + for (int i = 1 ; i < info.fColorCount; i++) { 1.160 + if (info.fColorOffsets[i] == info.fColorOffsets[i - 1]) { 1.161 + continue; 1.162 + } 1.163 + gradients++; 1.164 + 1.165 + result->append("{dup "); 1.166 + result->appendScalar(info.fColorOffsets[i]); 1.167 + result->append(" le {"); 1.168 + if (info.fColorOffsets[i - 1] != 0) { 1.169 + result->appendScalar(info.fColorOffsets[i - 1]); 1.170 + result->append(" sub\n"); 1.171 + } 1.172 + 1.173 + interpolateColorCode(info.fColorOffsets[i] - info.fColorOffsets[i - 1], 1.174 + colorData[i], colorData[i - 1], result); 1.175 + result->append("}\n"); 1.176 + } 1.177 + 1.178 + // Clamp the final color. 1.179 + result->append("{pop "); 1.180 + result->appendScalar(colorData[info.fColorCount - 1][0]); 1.181 + result->append(" "); 1.182 + result->appendScalar(colorData[info.fColorCount - 1][1]); 1.183 + result->append(" "); 1.184 + result->appendScalar(colorData[info.fColorCount - 1][2]); 1.185 + 1.186 + for (int i = 0 ; i < gradients + 1; i++) { 1.187 + result->append("} ifelse\n"); 1.188 + } 1.189 +} 1.190 + 1.191 +/* Map a value of t on the stack into [0, 1) for Repeat or Mirror tile mode. */ 1.192 +static void tileModeCode(SkShader::TileMode mode, SkString* result) { 1.193 + if (mode == SkShader::kRepeat_TileMode) { 1.194 + result->append("dup truncate sub\n"); // Get the fractional part. 1.195 + result->append("dup 0 le {1 add} if\n"); // Map (-1,0) => (0,1) 1.196 + return; 1.197 + } 1.198 + 1.199 + if (mode == SkShader::kMirror_TileMode) { 1.200 + // Map t mod 2 into [0, 1, 1, 0]. 1.201 + // Code Stack 1.202 + result->append("abs " // Map negative to positive. 1.203 + "dup " // t.s t.s 1.204 + "truncate " // t.s t 1.205 + "dup " // t.s t t 1.206 + "cvi " // t.s t T 1.207 + "2 mod " // t.s t (i mod 2) 1.208 + "1 eq " // t.s t true|false 1.209 + "3 1 roll " // true|false t.s t 1.210 + "sub " // true|false 0.s 1.211 + "exch " // 0.s true|false 1.212 + "{1 exch sub} if\n"); // 1 - 0.s|0.s 1.213 + } 1.214 +} 1.215 + 1.216 +/** 1.217 + * Returns PS function code that applies inverse perspective 1.218 + * to a x, y point. 1.219 + * The function assumes that the stack has at least two elements, 1.220 + * and that the top 2 elements are numeric values. 1.221 + * After executing this code on a PS stack, the last 2 elements are updated 1.222 + * while the rest of the stack is preserved intact. 1.223 + * inversePerspectiveMatrix is the inverse perspective matrix. 1.224 + */ 1.225 +static SkString apply_perspective_to_coordinates( 1.226 + const SkMatrix& inversePerspectiveMatrix) { 1.227 + SkString code; 1.228 + if (!inversePerspectiveMatrix.hasPerspective()) { 1.229 + return code; 1.230 + } 1.231 + 1.232 + // Perspective matrix should be: 1.233 + // 1 0 0 1.234 + // 0 1 0 1.235 + // p0 p1 p2 1.236 + 1.237 + const SkScalar p0 = inversePerspectiveMatrix[SkMatrix::kMPersp0]; 1.238 + const SkScalar p1 = inversePerspectiveMatrix[SkMatrix::kMPersp1]; 1.239 + const SkScalar p2 = inversePerspectiveMatrix[SkMatrix::kMPersp2]; 1.240 + 1.241 + // y = y / (p2 + p0 x + p1 y) 1.242 + // x = x / (p2 + p0 x + p1 y) 1.243 + 1.244 + // Input on stack: x y 1.245 + code.append(" dup "); // x y y 1.246 + code.appendScalar(p1); // x y y p1 1.247 + code.append(" mul " // x y y*p1 1.248 + " 2 index "); // x y y*p1 x 1.249 + code.appendScalar(p0); // x y y p1 x p0 1.250 + code.append(" mul "); // x y y*p1 x*p0 1.251 + code.appendScalar(p2); // x y y p1 x*p0 p2 1.252 + code.append(" add " // x y y*p1 x*p0+p2 1.253 + "add " // x y y*p1+x*p0+p2 1.254 + "3 1 roll " // y*p1+x*p0+p2 x y 1.255 + "2 index " // z x y y*p1+x*p0+p2 1.256 + "div " // y*p1+x*p0+p2 x y/(y*p1+x*p0+p2) 1.257 + "3 1 roll " // y/(y*p1+x*p0+p2) y*p1+x*p0+p2 x 1.258 + "exch " // y/(y*p1+x*p0+p2) x y*p1+x*p0+p2 1.259 + "div " // y/(y*p1+x*p0+p2) x/(y*p1+x*p0+p2) 1.260 + "exch\n"); // x/(y*p1+x*p0+p2) y/(y*p1+x*p0+p2) 1.261 + return code; 1.262 +} 1.263 + 1.264 +static SkString linearCode(const SkShader::GradientInfo& info, 1.265 + const SkMatrix& perspectiveRemover) { 1.266 + SkString function("{"); 1.267 + 1.268 + function.append(apply_perspective_to_coordinates(perspectiveRemover)); 1.269 + 1.270 + function.append("pop\n"); // Just ditch the y value. 1.271 + tileModeCode(info.fTileMode, &function); 1.272 + gradientFunctionCode(info, &function); 1.273 + function.append("}"); 1.274 + return function; 1.275 +} 1.276 + 1.277 +static SkString radialCode(const SkShader::GradientInfo& info, 1.278 + const SkMatrix& perspectiveRemover) { 1.279 + SkString function("{"); 1.280 + 1.281 + function.append(apply_perspective_to_coordinates(perspectiveRemover)); 1.282 + 1.283 + // Find the distance from the origin. 1.284 + function.append("dup " // x y y 1.285 + "mul " // x y^2 1.286 + "exch " // y^2 x 1.287 + "dup " // y^2 x x 1.288 + "mul " // y^2 x^2 1.289 + "add " // y^2+x^2 1.290 + "sqrt\n"); // sqrt(y^2+x^2) 1.291 + 1.292 + tileModeCode(info.fTileMode, &function); 1.293 + gradientFunctionCode(info, &function); 1.294 + function.append("}"); 1.295 + return function; 1.296 +} 1.297 + 1.298 +/* The math here is all based on the description in Two_Point_Radial_Gradient, 1.299 + with one simplification, the coordinate space has been scaled so that 1.300 + Dr = 1. This means we don't need to scale the entire equation by 1/Dr^2. 1.301 + */ 1.302 +static SkString twoPointRadialCode(const SkShader::GradientInfo& info, 1.303 + const SkMatrix& perspectiveRemover) { 1.304 + SkScalar dx = info.fPoint[0].fX - info.fPoint[1].fX; 1.305 + SkScalar dy = info.fPoint[0].fY - info.fPoint[1].fY; 1.306 + SkScalar sr = info.fRadius[0]; 1.307 + SkScalar a = SkScalarMul(dx, dx) + SkScalarMul(dy, dy) - SK_Scalar1; 1.308 + bool posRoot = info.fRadius[1] > info.fRadius[0]; 1.309 + 1.310 + // We start with a stack of (x y), copy it and then consume one copy in 1.311 + // order to calculate b and the other to calculate c. 1.312 + SkString function("{"); 1.313 + 1.314 + function.append(apply_perspective_to_coordinates(perspectiveRemover)); 1.315 + 1.316 + function.append("2 copy "); 1.317 + 1.318 + // Calculate -b and b^2. 1.319 + function.appendScalar(dy); 1.320 + function.append(" mul exch "); 1.321 + function.appendScalar(dx); 1.322 + function.append(" mul add "); 1.323 + function.appendScalar(sr); 1.324 + function.append(" sub 2 mul neg dup dup mul\n"); 1.325 + 1.326 + // Calculate c 1.327 + function.append("4 2 roll dup mul exch dup mul add "); 1.328 + function.appendScalar(SkScalarMul(sr, sr)); 1.329 + function.append(" sub\n"); 1.330 + 1.331 + // Calculate the determinate 1.332 + function.appendScalar(SkScalarMul(SkIntToScalar(4), a)); 1.333 + function.append(" mul sub abs sqrt\n"); 1.334 + 1.335 + // And then the final value of t. 1.336 + if (posRoot) { 1.337 + function.append("sub "); 1.338 + } else { 1.339 + function.append("add "); 1.340 + } 1.341 + function.appendScalar(SkScalarMul(SkIntToScalar(2), a)); 1.342 + function.append(" div\n"); 1.343 + 1.344 + tileModeCode(info.fTileMode, &function); 1.345 + gradientFunctionCode(info, &function); 1.346 + function.append("}"); 1.347 + return function; 1.348 +} 1.349 + 1.350 +/* Conical gradient shader, based on the Canvas spec for radial gradients 1.351 + See: http://www.w3.org/TR/2dcontext/#dom-context-2d-createradialgradient 1.352 + */ 1.353 +static SkString twoPointConicalCode(const SkShader::GradientInfo& info, 1.354 + const SkMatrix& perspectiveRemover) { 1.355 + SkScalar dx = info.fPoint[1].fX - info.fPoint[0].fX; 1.356 + SkScalar dy = info.fPoint[1].fY - info.fPoint[0].fY; 1.357 + SkScalar r0 = info.fRadius[0]; 1.358 + SkScalar dr = info.fRadius[1] - info.fRadius[0]; 1.359 + SkScalar a = SkScalarMul(dx, dx) + SkScalarMul(dy, dy) - 1.360 + SkScalarMul(dr, dr); 1.361 + 1.362 + // First compute t, if the pixel falls outside the cone, then we'll end 1.363 + // with 'false' on the stack, otherwise we'll push 'true' with t below it 1.364 + 1.365 + // We start with a stack of (x y), copy it and then consume one copy in 1.366 + // order to calculate b and the other to calculate c. 1.367 + SkString function("{"); 1.368 + 1.369 + function.append(apply_perspective_to_coordinates(perspectiveRemover)); 1.370 + 1.371 + function.append("2 copy "); 1.372 + 1.373 + // Calculate b and b^2; b = -2 * (y * dy + x * dx + r0 * dr). 1.374 + function.appendScalar(dy); 1.375 + function.append(" mul exch "); 1.376 + function.appendScalar(dx); 1.377 + function.append(" mul add "); 1.378 + function.appendScalar(SkScalarMul(r0, dr)); 1.379 + function.append(" add -2 mul dup dup mul\n"); 1.380 + 1.381 + // c = x^2 + y^2 + radius0^2 1.382 + function.append("4 2 roll dup mul exch dup mul add "); 1.383 + function.appendScalar(SkScalarMul(r0, r0)); 1.384 + function.append(" sub dup 4 1 roll\n"); 1.385 + 1.386 + // Contents of the stack at this point: c, b, b^2, c 1.387 + 1.388 + // if a = 0, then we collapse to a simpler linear case 1.389 + if (a == 0) { 1.390 + 1.391 + // t = -c/b 1.392 + function.append("pop pop div neg dup "); 1.393 + 1.394 + // compute radius(t) 1.395 + function.appendScalar(dr); 1.396 + function.append(" mul "); 1.397 + function.appendScalar(r0); 1.398 + function.append(" add\n"); 1.399 + 1.400 + // if r(t) < 0, then it's outside the cone 1.401 + function.append("0 lt {pop false} {true} ifelse\n"); 1.402 + 1.403 + } else { 1.404 + 1.405 + // quadratic case: the Canvas spec wants the largest 1.406 + // root t for which radius(t) > 0 1.407 + 1.408 + // compute the discriminant (b^2 - 4ac) 1.409 + function.appendScalar(SkScalarMul(SkIntToScalar(4), a)); 1.410 + function.append(" mul sub dup\n"); 1.411 + 1.412 + // if d >= 0, proceed 1.413 + function.append("0 ge {\n"); 1.414 + 1.415 + // an intermediate value we'll use to compute the roots: 1.416 + // q = -0.5 * (b +/- sqrt(d)) 1.417 + function.append("sqrt exch dup 0 lt {exch -1 mul} if"); 1.418 + function.append(" add -0.5 mul dup\n"); 1.419 + 1.420 + // first root = q / a 1.421 + function.appendScalar(a); 1.422 + function.append(" div\n"); 1.423 + 1.424 + // second root = c / q 1.425 + function.append("3 1 roll div\n"); 1.426 + 1.427 + // put the larger root on top of the stack 1.428 + function.append("2 copy gt {exch} if\n"); 1.429 + 1.430 + // compute radius(t) for larger root 1.431 + function.append("dup "); 1.432 + function.appendScalar(dr); 1.433 + function.append(" mul "); 1.434 + function.appendScalar(r0); 1.435 + function.append(" add\n"); 1.436 + 1.437 + // if r(t) > 0, we have our t, pop off the smaller root and we're done 1.438 + function.append(" 0 gt {exch pop true}\n"); 1.439 + 1.440 + // otherwise, throw out the larger one and try the smaller root 1.441 + function.append("{pop dup\n"); 1.442 + function.appendScalar(dr); 1.443 + function.append(" mul "); 1.444 + function.appendScalar(r0); 1.445 + function.append(" add\n"); 1.446 + 1.447 + // if r(t) < 0, push false, otherwise the smaller root is our t 1.448 + function.append("0 le {pop false} {true} ifelse\n"); 1.449 + function.append("} ifelse\n"); 1.450 + 1.451 + // d < 0, clear the stack and push false 1.452 + function.append("} {pop pop pop false} ifelse\n"); 1.453 + } 1.454 + 1.455 + // if the pixel is in the cone, proceed to compute a color 1.456 + function.append("{"); 1.457 + tileModeCode(info.fTileMode, &function); 1.458 + gradientFunctionCode(info, &function); 1.459 + 1.460 + // otherwise, just write black 1.461 + function.append("} {0 0 0} ifelse }"); 1.462 + 1.463 + return function; 1.464 +} 1.465 + 1.466 +static SkString sweepCode(const SkShader::GradientInfo& info, 1.467 + const SkMatrix& perspectiveRemover) { 1.468 + SkString function("{exch atan 360 div\n"); 1.469 + tileModeCode(info.fTileMode, &function); 1.470 + gradientFunctionCode(info, &function); 1.471 + function.append("}"); 1.472 + return function; 1.473 +} 1.474 + 1.475 +class SkPDFShader::State { 1.476 +public: 1.477 + SkShader::GradientType fType; 1.478 + SkShader::GradientInfo fInfo; 1.479 + SkAutoFree fColorData; // This provides storage for arrays in fInfo. 1.480 + SkMatrix fCanvasTransform; 1.481 + SkMatrix fShaderTransform; 1.482 + SkIRect fBBox; 1.483 + 1.484 + SkBitmap fImage; 1.485 + uint32_t fPixelGeneration; 1.486 + SkShader::TileMode fImageTileModes[2]; 1.487 + 1.488 + State(const SkShader& shader, const SkMatrix& canvasTransform, 1.489 + const SkIRect& bbox); 1.490 + 1.491 + bool operator==(const State& b) const; 1.492 + 1.493 + SkPDFShader::State* CreateAlphaToLuminosityState() const; 1.494 + SkPDFShader::State* CreateOpaqueState() const; 1.495 + 1.496 + bool GradientHasAlpha() const; 1.497 + 1.498 +private: 1.499 + State(const State& other); 1.500 + State operator=(const State& rhs); 1.501 + void AllocateGradientInfoStorage(); 1.502 +}; 1.503 + 1.504 +class SkPDFFunctionShader : public SkPDFDict, public SkPDFShader { 1.505 + SK_DECLARE_INST_COUNT(SkPDFFunctionShader) 1.506 +public: 1.507 + explicit SkPDFFunctionShader(SkPDFShader::State* state); 1.508 + virtual ~SkPDFFunctionShader() { 1.509 + if (isValid()) { 1.510 + RemoveShader(this); 1.511 + } 1.512 + fResources.unrefAll(); 1.513 + } 1.514 + 1.515 + virtual bool isValid() { return fResources.count() > 0; } 1.516 + 1.517 + void getResources(const SkTSet<SkPDFObject*>& knownResourceObjects, 1.518 + SkTSet<SkPDFObject*>* newResourceObjects) { 1.519 + GetResourcesHelper(&fResources, 1.520 + knownResourceObjects, 1.521 + newResourceObjects); 1.522 + } 1.523 + 1.524 +private: 1.525 + static SkPDFObject* RangeObject(); 1.526 + 1.527 + SkTDArray<SkPDFObject*> fResources; 1.528 + SkAutoTDelete<const SkPDFShader::State> fState; 1.529 + 1.530 + SkPDFStream* makePSFunction(const SkString& psCode, SkPDFArray* domain); 1.531 + typedef SkPDFDict INHERITED; 1.532 +}; 1.533 + 1.534 +/** 1.535 + * A shader for PDF gradients. This encapsulates the function shader 1.536 + * inside a tiling pattern while providing a common pattern interface. 1.537 + * The encapsulation allows the use of a SMask for transparency gradients. 1.538 + */ 1.539 +class SkPDFAlphaFunctionShader : public SkPDFStream, public SkPDFShader { 1.540 +public: 1.541 + explicit SkPDFAlphaFunctionShader(SkPDFShader::State* state); 1.542 + virtual ~SkPDFAlphaFunctionShader() { 1.543 + if (isValid()) { 1.544 + RemoveShader(this); 1.545 + } 1.546 + } 1.547 + 1.548 + virtual bool isValid() { 1.549 + return fColorShader.get() != NULL; 1.550 + } 1.551 + 1.552 +private: 1.553 + SkAutoTDelete<const SkPDFShader::State> fState; 1.554 + 1.555 + SkPDFGraphicState* CreateSMaskGraphicState(); 1.556 + 1.557 + void getResources(const SkTSet<SkPDFObject*>& knownResourceObjects, 1.558 + SkTSet<SkPDFObject*>* newResourceObjects) { 1.559 + fResourceDict->getReferencedResources(knownResourceObjects, 1.560 + newResourceObjects, 1.561 + true); 1.562 + } 1.563 + 1.564 + SkAutoTUnref<SkPDFObject> fColorShader; 1.565 + SkAutoTUnref<SkPDFResourceDict> fResourceDict; 1.566 +}; 1.567 + 1.568 +class SkPDFImageShader : public SkPDFStream, public SkPDFShader { 1.569 +public: 1.570 + explicit SkPDFImageShader(SkPDFShader::State* state); 1.571 + virtual ~SkPDFImageShader() { 1.572 + if (isValid()) { 1.573 + RemoveShader(this); 1.574 + } 1.575 + fResources.unrefAll(); 1.576 + } 1.577 + 1.578 + virtual bool isValid() { return size() > 0; } 1.579 + 1.580 + void getResources(const SkTSet<SkPDFObject*>& knownResourceObjects, 1.581 + SkTSet<SkPDFObject*>* newResourceObjects) { 1.582 + GetResourcesHelper(&fResources.toArray(), 1.583 + knownResourceObjects, 1.584 + newResourceObjects); 1.585 + } 1.586 + 1.587 +private: 1.588 + SkTSet<SkPDFObject*> fResources; 1.589 + SkAutoTDelete<const SkPDFShader::State> fState; 1.590 +}; 1.591 + 1.592 +SkPDFShader::SkPDFShader() {} 1.593 + 1.594 +// static 1.595 +SkPDFObject* SkPDFShader::GetPDFShaderByState(State* inState) { 1.596 + SkPDFObject* result; 1.597 + 1.598 + SkAutoTDelete<State> shaderState(inState); 1.599 + if (shaderState.get()->fType == SkShader::kNone_GradientType && 1.600 + shaderState.get()->fImage.isNull()) { 1.601 + // TODO(vandebo) This drops SKComposeShader on the floor. We could 1.602 + // handle compose shader by pulling things up to a layer, drawing with 1.603 + // the first shader, applying the xfer mode and drawing again with the 1.604 + // second shader, then applying the layer to the original drawing. 1.605 + return NULL; 1.606 + } 1.607 + 1.608 + ShaderCanonicalEntry entry(NULL, shaderState.get()); 1.609 + int index = CanonicalShaders().find(entry); 1.610 + if (index >= 0) { 1.611 + result = CanonicalShaders()[index].fPDFShader; 1.612 + result->ref(); 1.613 + return result; 1.614 + } 1.615 + 1.616 + bool valid = false; 1.617 + // The PDFShader takes ownership of the shaderSate. 1.618 + if (shaderState.get()->fType == SkShader::kNone_GradientType) { 1.619 + SkPDFImageShader* imageShader = 1.620 + new SkPDFImageShader(shaderState.detach()); 1.621 + valid = imageShader->isValid(); 1.622 + result = imageShader; 1.623 + } else { 1.624 + if (shaderState.get()->GradientHasAlpha()) { 1.625 + SkPDFAlphaFunctionShader* gradientShader = 1.626 + SkNEW_ARGS(SkPDFAlphaFunctionShader, (shaderState.detach())); 1.627 + valid = gradientShader->isValid(); 1.628 + result = gradientShader; 1.629 + } else { 1.630 + SkPDFFunctionShader* functionShader = 1.631 + SkNEW_ARGS(SkPDFFunctionShader, (shaderState.detach())); 1.632 + valid = functionShader->isValid(); 1.633 + result = functionShader; 1.634 + } 1.635 + } 1.636 + if (!valid) { 1.637 + delete result; 1.638 + return NULL; 1.639 + } 1.640 + entry.fPDFShader = result; 1.641 + CanonicalShaders().push(entry); 1.642 + return result; // return the reference that came from new. 1.643 +} 1.644 + 1.645 +// static 1.646 +void SkPDFShader::RemoveShader(SkPDFObject* shader) { 1.647 + SkAutoMutexAcquire lock(CanonicalShadersMutex()); 1.648 + ShaderCanonicalEntry entry(shader, NULL); 1.649 + int index = CanonicalShaders().find(entry); 1.650 + SkASSERT(index >= 0); 1.651 + CanonicalShaders().removeShuffle(index); 1.652 +} 1.653 + 1.654 +// static 1.655 +SkPDFObject* SkPDFShader::GetPDFShader(const SkShader& shader, 1.656 + const SkMatrix& matrix, 1.657 + const SkIRect& surfaceBBox) { 1.658 + SkAutoMutexAcquire lock(CanonicalShadersMutex()); 1.659 + return GetPDFShaderByState( 1.660 + SkNEW_ARGS(State, (shader, matrix, surfaceBBox))); 1.661 +} 1.662 + 1.663 +// static 1.664 +SkTDArray<SkPDFShader::ShaderCanonicalEntry>& SkPDFShader::CanonicalShaders() { 1.665 + // This initialization is only thread safe with gcc. 1.666 + static SkTDArray<ShaderCanonicalEntry> gCanonicalShaders; 1.667 + return gCanonicalShaders; 1.668 +} 1.669 + 1.670 +// static 1.671 +SkBaseMutex& SkPDFShader::CanonicalShadersMutex() { 1.672 + // This initialization is only thread safe with gcc or when 1.673 + // POD-style mutex initialization is used. 1.674 + SK_DECLARE_STATIC_MUTEX(gCanonicalShadersMutex); 1.675 + return gCanonicalShadersMutex; 1.676 +} 1.677 + 1.678 +// static 1.679 +SkPDFObject* SkPDFFunctionShader::RangeObject() { 1.680 + // This initialization is only thread safe with gcc. 1.681 + static SkPDFArray* range = NULL; 1.682 + // This method is only used with CanonicalShadersMutex, so it's safe to 1.683 + // populate domain. 1.684 + if (range == NULL) { 1.685 + range = new SkPDFArray; 1.686 + range->reserve(6); 1.687 + range->appendInt(0); 1.688 + range->appendInt(1); 1.689 + range->appendInt(0); 1.690 + range->appendInt(1); 1.691 + range->appendInt(0); 1.692 + range->appendInt(1); 1.693 + } 1.694 + return range; 1.695 +} 1.696 + 1.697 +static SkPDFResourceDict* get_gradient_resource_dict( 1.698 + SkPDFObject* functionShader, 1.699 + SkPDFObject* gState) { 1.700 + SkPDFResourceDict* dict = new SkPDFResourceDict(); 1.701 + 1.702 + if (functionShader != NULL) { 1.703 + dict->insertResourceAsReference( 1.704 + SkPDFResourceDict::kPattern_ResourceType, 0, functionShader); 1.705 + } 1.706 + if (gState != NULL) { 1.707 + dict->insertResourceAsReference( 1.708 + SkPDFResourceDict::kExtGState_ResourceType, 0, gState); 1.709 + } 1.710 + 1.711 + return dict; 1.712 +} 1.713 + 1.714 +static void populate_tiling_pattern_dict(SkPDFDict* pattern, 1.715 + SkRect& bbox, SkPDFDict* resources, 1.716 + const SkMatrix& matrix) { 1.717 + const int kTiling_PatternType = 1; 1.718 + const int kColoredTilingPattern_PaintType = 1; 1.719 + const int kConstantSpacing_TilingType = 1; 1.720 + 1.721 + pattern->insertName("Type", "Pattern"); 1.722 + pattern->insertInt("PatternType", kTiling_PatternType); 1.723 + pattern->insertInt("PaintType", kColoredTilingPattern_PaintType); 1.724 + pattern->insertInt("TilingType", kConstantSpacing_TilingType); 1.725 + pattern->insert("BBox", SkPDFUtils::RectToArray(bbox))->unref(); 1.726 + pattern->insertScalar("XStep", bbox.width()); 1.727 + pattern->insertScalar("YStep", bbox.height()); 1.728 + pattern->insert("Resources", resources); 1.729 + if (!matrix.isIdentity()) { 1.730 + pattern->insert("Matrix", SkPDFUtils::MatrixToArray(matrix))->unref(); 1.731 + } 1.732 +} 1.733 + 1.734 +/** 1.735 + * Creates a content stream which fills the pattern P0 across bounds. 1.736 + * @param gsIndex A graphics state resource index to apply, or <0 if no 1.737 + * graphics state to apply. 1.738 + */ 1.739 +static SkStream* create_pattern_fill_content(int gsIndex, SkRect& bounds) { 1.740 + SkDynamicMemoryWStream content; 1.741 + if (gsIndex >= 0) { 1.742 + SkPDFUtils::ApplyGraphicState(gsIndex, &content); 1.743 + } 1.744 + SkPDFUtils::ApplyPattern(0, &content); 1.745 + SkPDFUtils::AppendRectangle(bounds, &content); 1.746 + SkPDFUtils::PaintPath(SkPaint::kFill_Style, SkPath::kEvenOdd_FillType, 1.747 + &content); 1.748 + 1.749 + return content.detachAsStream(); 1.750 +} 1.751 + 1.752 +/** 1.753 + * Creates a ExtGState with the SMask set to the luminosityShader in 1.754 + * luminosity mode. The shader pattern extends to the bbox. 1.755 + */ 1.756 +SkPDFGraphicState* SkPDFAlphaFunctionShader::CreateSMaskGraphicState() { 1.757 + SkRect bbox; 1.758 + bbox.set(fState.get()->fBBox); 1.759 + 1.760 + SkAutoTUnref<SkPDFObject> luminosityShader( 1.761 + SkPDFShader::GetPDFShaderByState( 1.762 + fState->CreateAlphaToLuminosityState())); 1.763 + 1.764 + SkAutoTUnref<SkStream> alphaStream(create_pattern_fill_content(-1, bbox)); 1.765 + 1.766 + SkAutoTUnref<SkPDFResourceDict> 1.767 + resources(get_gradient_resource_dict(luminosityShader, NULL)); 1.768 + 1.769 + SkAutoTUnref<SkPDFFormXObject> alphaMask( 1.770 + new SkPDFFormXObject(alphaStream.get(), bbox, resources.get())); 1.771 + 1.772 + return SkPDFGraphicState::GetSMaskGraphicState( 1.773 + alphaMask.get(), false, 1.774 + SkPDFGraphicState::kLuminosity_SMaskMode); 1.775 +} 1.776 + 1.777 +SkPDFAlphaFunctionShader::SkPDFAlphaFunctionShader(SkPDFShader::State* state) 1.778 + : fState(state) { 1.779 + SkRect bbox; 1.780 + bbox.set(fState.get()->fBBox); 1.781 + 1.782 + fColorShader.reset( 1.783 + SkPDFShader::GetPDFShaderByState(state->CreateOpaqueState())); 1.784 + 1.785 + // Create resource dict with alpha graphics state as G0 and 1.786 + // pattern shader as P0, then write content stream. 1.787 + SkAutoTUnref<SkPDFGraphicState> alphaGs(CreateSMaskGraphicState()); 1.788 + fResourceDict.reset( 1.789 + get_gradient_resource_dict(fColorShader.get(), alphaGs.get())); 1.790 + 1.791 + SkAutoTUnref<SkStream> colorStream( 1.792 + create_pattern_fill_content(0, bbox)); 1.793 + setData(colorStream.get()); 1.794 + 1.795 + populate_tiling_pattern_dict(this, bbox, fResourceDict.get(), 1.796 + SkMatrix::I()); 1.797 +} 1.798 + 1.799 +// Finds affine and persp such that in = affine * persp. 1.800 +// but it returns the inverse of perspective matrix. 1.801 +static bool split_perspective(const SkMatrix in, SkMatrix* affine, 1.802 + SkMatrix* perspectiveInverse) { 1.803 + const SkScalar p2 = in[SkMatrix::kMPersp2]; 1.804 + 1.805 + if (SkScalarNearlyZero(p2)) { 1.806 + return false; 1.807 + } 1.808 + 1.809 + const SkScalar zero = SkIntToScalar(0); 1.810 + const SkScalar one = SkIntToScalar(1); 1.811 + 1.812 + const SkScalar sx = in[SkMatrix::kMScaleX]; 1.813 + const SkScalar kx = in[SkMatrix::kMSkewX]; 1.814 + const SkScalar tx = in[SkMatrix::kMTransX]; 1.815 + const SkScalar ky = in[SkMatrix::kMSkewY]; 1.816 + const SkScalar sy = in[SkMatrix::kMScaleY]; 1.817 + const SkScalar ty = in[SkMatrix::kMTransY]; 1.818 + const SkScalar p0 = in[SkMatrix::kMPersp0]; 1.819 + const SkScalar p1 = in[SkMatrix::kMPersp1]; 1.820 + 1.821 + // Perspective matrix would be: 1.822 + // 1 0 0 1.823 + // 0 1 0 1.824 + // p0 p1 p2 1.825 + // But we need the inverse of persp. 1.826 + perspectiveInverse->setAll(one, zero, zero, 1.827 + zero, one, zero, 1.828 + -p0/p2, -p1/p2, 1/p2); 1.829 + 1.830 + affine->setAll(sx - p0 * tx / p2, kx - p1 * tx / p2, tx / p2, 1.831 + ky - p0 * ty / p2, sy - p1 * ty / p2, ty / p2, 1.832 + zero, zero, one); 1.833 + 1.834 + return true; 1.835 +} 1.836 + 1.837 +SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state) 1.838 + : SkPDFDict("Pattern"), 1.839 + fState(state) { 1.840 + SkString (*codeFunction)(const SkShader::GradientInfo& info, 1.841 + const SkMatrix& perspectiveRemover) = NULL; 1.842 + SkPoint transformPoints[2]; 1.843 + 1.844 + // Depending on the type of the gradient, we want to transform the 1.845 + // coordinate space in different ways. 1.846 + const SkShader::GradientInfo* info = &fState.get()->fInfo; 1.847 + transformPoints[0] = info->fPoint[0]; 1.848 + transformPoints[1] = info->fPoint[1]; 1.849 + switch (fState.get()->fType) { 1.850 + case SkShader::kLinear_GradientType: 1.851 + codeFunction = &linearCode; 1.852 + break; 1.853 + case SkShader::kRadial_GradientType: 1.854 + transformPoints[1] = transformPoints[0]; 1.855 + transformPoints[1].fX += info->fRadius[0]; 1.856 + codeFunction = &radialCode; 1.857 + break; 1.858 + case SkShader::kRadial2_GradientType: { 1.859 + // Bail out if the radii are the same. Empty fResources signals 1.860 + // an error and isValid will return false. 1.861 + if (info->fRadius[0] == info->fRadius[1]) { 1.862 + return; 1.863 + } 1.864 + transformPoints[1] = transformPoints[0]; 1.865 + SkScalar dr = info->fRadius[1] - info->fRadius[0]; 1.866 + transformPoints[1].fX += dr; 1.867 + codeFunction = &twoPointRadialCode; 1.868 + break; 1.869 + } 1.870 + case SkShader::kConical_GradientType: { 1.871 + transformPoints[1] = transformPoints[0]; 1.872 + transformPoints[1].fX += SK_Scalar1; 1.873 + codeFunction = &twoPointConicalCode; 1.874 + break; 1.875 + } 1.876 + case SkShader::kSweep_GradientType: 1.877 + transformPoints[1] = transformPoints[0]; 1.878 + transformPoints[1].fX += SK_Scalar1; 1.879 + codeFunction = &sweepCode; 1.880 + break; 1.881 + case SkShader::kColor_GradientType: 1.882 + case SkShader::kNone_GradientType: 1.883 + default: 1.884 + return; 1.885 + } 1.886 + 1.887 + // Move any scaling (assuming a unit gradient) or translation 1.888 + // (and rotation for linear gradient), of the final gradient from 1.889 + // info->fPoints to the matrix (updating bbox appropriately). Now 1.890 + // the gradient can be drawn on on the unit segment. 1.891 + SkMatrix mapperMatrix; 1.892 + unitToPointsMatrix(transformPoints, &mapperMatrix); 1.893 + 1.894 + SkMatrix finalMatrix = fState.get()->fCanvasTransform; 1.895 + finalMatrix.preConcat(fState.get()->fShaderTransform); 1.896 + finalMatrix.preConcat(mapperMatrix); 1.897 + 1.898 + // Preserves as much as posible in the final matrix, and only removes 1.899 + // the perspective. The inverse of the perspective is stored in 1.900 + // perspectiveInverseOnly matrix and has 3 useful numbers 1.901 + // (p0, p1, p2), while everything else is either 0 or 1. 1.902 + // In this way the shader will handle it eficiently, with minimal code. 1.903 + SkMatrix perspectiveInverseOnly = SkMatrix::I(); 1.904 + if (finalMatrix.hasPerspective()) { 1.905 + if (!split_perspective(finalMatrix, 1.906 + &finalMatrix, &perspectiveInverseOnly)) { 1.907 + return; 1.908 + } 1.909 + } 1.910 + 1.911 + SkRect bbox; 1.912 + bbox.set(fState.get()->fBBox); 1.913 + if (!inverseTransformBBox(finalMatrix, &bbox)) { 1.914 + return; 1.915 + } 1.916 + 1.917 + SkAutoTUnref<SkPDFArray> domain(new SkPDFArray); 1.918 + domain->reserve(4); 1.919 + domain->appendScalar(bbox.fLeft); 1.920 + domain->appendScalar(bbox.fRight); 1.921 + domain->appendScalar(bbox.fTop); 1.922 + domain->appendScalar(bbox.fBottom); 1.923 + 1.924 + SkString functionCode; 1.925 + // The two point radial gradient further references fState.get()->fInfo 1.926 + // in translating from x, y coordinates to the t parameter. So, we have 1.927 + // to transform the points and radii according to the calculated matrix. 1.928 + if (fState.get()->fType == SkShader::kRadial2_GradientType) { 1.929 + SkShader::GradientInfo twoPointRadialInfo = *info; 1.930 + SkMatrix inverseMapperMatrix; 1.931 + if (!mapperMatrix.invert(&inverseMapperMatrix)) { 1.932 + return; 1.933 + } 1.934 + inverseMapperMatrix.mapPoints(twoPointRadialInfo.fPoint, 2); 1.935 + twoPointRadialInfo.fRadius[0] = 1.936 + inverseMapperMatrix.mapRadius(info->fRadius[0]); 1.937 + twoPointRadialInfo.fRadius[1] = 1.938 + inverseMapperMatrix.mapRadius(info->fRadius[1]); 1.939 + functionCode = codeFunction(twoPointRadialInfo, perspectiveInverseOnly); 1.940 + } else { 1.941 + functionCode = codeFunction(*info, perspectiveInverseOnly); 1.942 + } 1.943 + 1.944 + SkAutoTUnref<SkPDFDict> pdfShader(new SkPDFDict); 1.945 + pdfShader->insertInt("ShadingType", 1); 1.946 + pdfShader->insertName("ColorSpace", "DeviceRGB"); 1.947 + pdfShader->insert("Domain", domain.get()); 1.948 + 1.949 + SkPDFStream* function = makePSFunction(functionCode, domain.get()); 1.950 + pdfShader->insert("Function", new SkPDFObjRef(function))->unref(); 1.951 + fResources.push(function); // Pass ownership to resource list. 1.952 + 1.953 + insertInt("PatternType", 2); 1.954 + insert("Matrix", SkPDFUtils::MatrixToArray(finalMatrix))->unref(); 1.955 + insert("Shading", pdfShader.get()); 1.956 +} 1.957 + 1.958 +SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) : fState(state) { 1.959 + fState.get()->fImage.lockPixels(); 1.960 + 1.961 + // The image shader pattern cell will be drawn into a separate device 1.962 + // in pattern cell space (no scaling on the bitmap, though there may be 1.963 + // translations so that all content is in the device, coordinates > 0). 1.964 + 1.965 + // Map clip bounds to shader space to ensure the device is large enough 1.966 + // to handle fake clamping. 1.967 + SkMatrix finalMatrix = fState.get()->fCanvasTransform; 1.968 + finalMatrix.preConcat(fState.get()->fShaderTransform); 1.969 + SkRect deviceBounds; 1.970 + deviceBounds.set(fState.get()->fBBox); 1.971 + if (!inverseTransformBBox(finalMatrix, &deviceBounds)) { 1.972 + return; 1.973 + } 1.974 + 1.975 + const SkBitmap* image = &fState.get()->fImage; 1.976 + SkRect bitmapBounds; 1.977 + image->getBounds(&bitmapBounds); 1.978 + 1.979 + // For tiling modes, the bounds should be extended to include the bitmap, 1.980 + // otherwise the bitmap gets clipped out and the shader is empty and awful. 1.981 + // For clamp modes, we're only interested in the clip region, whether 1.982 + // or not the main bitmap is in it. 1.983 + SkShader::TileMode tileModes[2]; 1.984 + tileModes[0] = fState.get()->fImageTileModes[0]; 1.985 + tileModes[1] = fState.get()->fImageTileModes[1]; 1.986 + if (tileModes[0] != SkShader::kClamp_TileMode || 1.987 + tileModes[1] != SkShader::kClamp_TileMode) { 1.988 + deviceBounds.join(bitmapBounds); 1.989 + } 1.990 + 1.991 + SkMatrix unflip; 1.992 + unflip.setTranslate(0, SkScalarRoundToScalar(deviceBounds.height())); 1.993 + unflip.preScale(SK_Scalar1, -SK_Scalar1); 1.994 + SkISize size = SkISize::Make(SkScalarRoundToInt(deviceBounds.width()), 1.995 + SkScalarRoundToInt(deviceBounds.height())); 1.996 + // TODO(edisonn): should we pass here the DCT encoder of the destination device? 1.997 + // TODO(edisonn): NYI Perspective, use SkPDFDeviceFlattener. 1.998 + SkPDFDevice pattern(size, size, unflip); 1.999 + SkCanvas canvas(&pattern); 1.1000 + 1.1001 + SkRect patternBBox; 1.1002 + image->getBounds(&patternBBox); 1.1003 + 1.1004 + // Translate the canvas so that the bitmap origin is at (0, 0). 1.1005 + canvas.translate(-deviceBounds.left(), -deviceBounds.top()); 1.1006 + patternBBox.offset(-deviceBounds.left(), -deviceBounds.top()); 1.1007 + // Undo the translation in the final matrix 1.1008 + finalMatrix.preTranslate(deviceBounds.left(), deviceBounds.top()); 1.1009 + 1.1010 + // If the bitmap is out of bounds (i.e. clamp mode where we only see the 1.1011 + // stretched sides), canvas will clip this out and the extraneous data 1.1012 + // won't be saved to the PDF. 1.1013 + canvas.drawBitmap(*image, 0, 0); 1.1014 + 1.1015 + SkScalar width = SkIntToScalar(image->width()); 1.1016 + SkScalar height = SkIntToScalar(image->height()); 1.1017 + 1.1018 + // Tiling is implied. First we handle mirroring. 1.1019 + if (tileModes[0] == SkShader::kMirror_TileMode) { 1.1020 + SkMatrix xMirror; 1.1021 + xMirror.setScale(-1, 1); 1.1022 + xMirror.postTranslate(2 * width, 0); 1.1023 + canvas.drawBitmapMatrix(*image, xMirror); 1.1024 + patternBBox.fRight += width; 1.1025 + } 1.1026 + if (tileModes[1] == SkShader::kMirror_TileMode) { 1.1027 + SkMatrix yMirror; 1.1028 + yMirror.setScale(SK_Scalar1, -SK_Scalar1); 1.1029 + yMirror.postTranslate(0, 2 * height); 1.1030 + canvas.drawBitmapMatrix(*image, yMirror); 1.1031 + patternBBox.fBottom += height; 1.1032 + } 1.1033 + if (tileModes[0] == SkShader::kMirror_TileMode && 1.1034 + tileModes[1] == SkShader::kMirror_TileMode) { 1.1035 + SkMatrix mirror; 1.1036 + mirror.setScale(-1, -1); 1.1037 + mirror.postTranslate(2 * width, 2 * height); 1.1038 + canvas.drawBitmapMatrix(*image, mirror); 1.1039 + } 1.1040 + 1.1041 + // Then handle Clamping, which requires expanding the pattern canvas to 1.1042 + // cover the entire surfaceBBox. 1.1043 + 1.1044 + // If both x and y are in clamp mode, we start by filling in the corners. 1.1045 + // (Which are just a rectangles of the corner colors.) 1.1046 + if (tileModes[0] == SkShader::kClamp_TileMode && 1.1047 + tileModes[1] == SkShader::kClamp_TileMode) { 1.1048 + SkPaint paint; 1.1049 + SkRect rect; 1.1050 + rect = SkRect::MakeLTRB(deviceBounds.left(), deviceBounds.top(), 0, 0); 1.1051 + if (!rect.isEmpty()) { 1.1052 + paint.setColor(image->getColor(0, 0)); 1.1053 + canvas.drawRect(rect, paint); 1.1054 + } 1.1055 + 1.1056 + rect = SkRect::MakeLTRB(width, deviceBounds.top(), 1.1057 + deviceBounds.right(), 0); 1.1058 + if (!rect.isEmpty()) { 1.1059 + paint.setColor(image->getColor(image->width() - 1, 0)); 1.1060 + canvas.drawRect(rect, paint); 1.1061 + } 1.1062 + 1.1063 + rect = SkRect::MakeLTRB(width, height, 1.1064 + deviceBounds.right(), deviceBounds.bottom()); 1.1065 + if (!rect.isEmpty()) { 1.1066 + paint.setColor(image->getColor(image->width() - 1, 1.1067 + image->height() - 1)); 1.1068 + canvas.drawRect(rect, paint); 1.1069 + } 1.1070 + 1.1071 + rect = SkRect::MakeLTRB(deviceBounds.left(), height, 1.1072 + 0, deviceBounds.bottom()); 1.1073 + if (!rect.isEmpty()) { 1.1074 + paint.setColor(image->getColor(0, image->height() - 1)); 1.1075 + canvas.drawRect(rect, paint); 1.1076 + } 1.1077 + } 1.1078 + 1.1079 + // Then expand the left, right, top, then bottom. 1.1080 + if (tileModes[0] == SkShader::kClamp_TileMode) { 1.1081 + SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, image->height()); 1.1082 + if (deviceBounds.left() < 0) { 1.1083 + SkBitmap left; 1.1084 + SkAssertResult(image->extractSubset(&left, subset)); 1.1085 + 1.1086 + SkMatrix leftMatrix; 1.1087 + leftMatrix.setScale(-deviceBounds.left(), 1); 1.1088 + leftMatrix.postTranslate(deviceBounds.left(), 0); 1.1089 + canvas.drawBitmapMatrix(left, leftMatrix); 1.1090 + 1.1091 + if (tileModes[1] == SkShader::kMirror_TileMode) { 1.1092 + leftMatrix.postScale(SK_Scalar1, -SK_Scalar1); 1.1093 + leftMatrix.postTranslate(0, 2 * height); 1.1094 + canvas.drawBitmapMatrix(left, leftMatrix); 1.1095 + } 1.1096 + patternBBox.fLeft = 0; 1.1097 + } 1.1098 + 1.1099 + if (deviceBounds.right() > width) { 1.1100 + SkBitmap right; 1.1101 + subset.offset(image->width() - 1, 0); 1.1102 + SkAssertResult(image->extractSubset(&right, subset)); 1.1103 + 1.1104 + SkMatrix rightMatrix; 1.1105 + rightMatrix.setScale(deviceBounds.right() - width, 1); 1.1106 + rightMatrix.postTranslate(width, 0); 1.1107 + canvas.drawBitmapMatrix(right, rightMatrix); 1.1108 + 1.1109 + if (tileModes[1] == SkShader::kMirror_TileMode) { 1.1110 + rightMatrix.postScale(SK_Scalar1, -SK_Scalar1); 1.1111 + rightMatrix.postTranslate(0, 2 * height); 1.1112 + canvas.drawBitmapMatrix(right, rightMatrix); 1.1113 + } 1.1114 + patternBBox.fRight = deviceBounds.width(); 1.1115 + } 1.1116 + } 1.1117 + 1.1118 + if (tileModes[1] == SkShader::kClamp_TileMode) { 1.1119 + SkIRect subset = SkIRect::MakeXYWH(0, 0, image->width(), 1); 1.1120 + if (deviceBounds.top() < 0) { 1.1121 + SkBitmap top; 1.1122 + SkAssertResult(image->extractSubset(&top, subset)); 1.1123 + 1.1124 + SkMatrix topMatrix; 1.1125 + topMatrix.setScale(SK_Scalar1, -deviceBounds.top()); 1.1126 + topMatrix.postTranslate(0, deviceBounds.top()); 1.1127 + canvas.drawBitmapMatrix(top, topMatrix); 1.1128 + 1.1129 + if (tileModes[0] == SkShader::kMirror_TileMode) { 1.1130 + topMatrix.postScale(-1, 1); 1.1131 + topMatrix.postTranslate(2 * width, 0); 1.1132 + canvas.drawBitmapMatrix(top, topMatrix); 1.1133 + } 1.1134 + patternBBox.fTop = 0; 1.1135 + } 1.1136 + 1.1137 + if (deviceBounds.bottom() > height) { 1.1138 + SkBitmap bottom; 1.1139 + subset.offset(0, image->height() - 1); 1.1140 + SkAssertResult(image->extractSubset(&bottom, subset)); 1.1141 + 1.1142 + SkMatrix bottomMatrix; 1.1143 + bottomMatrix.setScale(SK_Scalar1, deviceBounds.bottom() - height); 1.1144 + bottomMatrix.postTranslate(0, height); 1.1145 + canvas.drawBitmapMatrix(bottom, bottomMatrix); 1.1146 + 1.1147 + if (tileModes[0] == SkShader::kMirror_TileMode) { 1.1148 + bottomMatrix.postScale(-1, 1); 1.1149 + bottomMatrix.postTranslate(2 * width, 0); 1.1150 + canvas.drawBitmapMatrix(bottom, bottomMatrix); 1.1151 + } 1.1152 + patternBBox.fBottom = deviceBounds.height(); 1.1153 + } 1.1154 + } 1.1155 + 1.1156 + // Put the canvas into the pattern stream (fContent). 1.1157 + SkAutoTUnref<SkStream> content(pattern.content()); 1.1158 + setData(content.get()); 1.1159 + SkPDFResourceDict* resourceDict = pattern.getResourceDict(); 1.1160 + resourceDict->getReferencedResources(fResources, &fResources, false); 1.1161 + 1.1162 + populate_tiling_pattern_dict(this, patternBBox, 1.1163 + pattern.getResourceDict(), finalMatrix); 1.1164 + 1.1165 + fState.get()->fImage.unlockPixels(); 1.1166 +} 1.1167 + 1.1168 +SkPDFStream* SkPDFFunctionShader::makePSFunction(const SkString& psCode, 1.1169 + SkPDFArray* domain) { 1.1170 + SkAutoDataUnref funcData(SkData::NewWithCopy(psCode.c_str(), 1.1171 + psCode.size())); 1.1172 + SkPDFStream* result = new SkPDFStream(funcData.get()); 1.1173 + result->insertInt("FunctionType", 4); 1.1174 + result->insert("Domain", domain); 1.1175 + result->insert("Range", RangeObject()); 1.1176 + return result; 1.1177 +} 1.1178 + 1.1179 +SkPDFShader::ShaderCanonicalEntry::ShaderCanonicalEntry(SkPDFObject* pdfShader, 1.1180 + const State* state) 1.1181 + : fPDFShader(pdfShader), 1.1182 + fState(state) { 1.1183 +} 1.1184 + 1.1185 +bool SkPDFShader::ShaderCanonicalEntry::operator==( 1.1186 + const ShaderCanonicalEntry& b) const { 1.1187 + return fPDFShader == b.fPDFShader || 1.1188 + (fState != NULL && b.fState != NULL && *fState == *b.fState); 1.1189 +} 1.1190 + 1.1191 +bool SkPDFShader::State::operator==(const SkPDFShader::State& b) const { 1.1192 + if (fType != b.fType || 1.1193 + fCanvasTransform != b.fCanvasTransform || 1.1194 + fShaderTransform != b.fShaderTransform || 1.1195 + fBBox != b.fBBox) { 1.1196 + return false; 1.1197 + } 1.1198 + 1.1199 + if (fType == SkShader::kNone_GradientType) { 1.1200 + if (fPixelGeneration != b.fPixelGeneration || 1.1201 + fPixelGeneration == 0 || 1.1202 + fImageTileModes[0] != b.fImageTileModes[0] || 1.1203 + fImageTileModes[1] != b.fImageTileModes[1]) { 1.1204 + return false; 1.1205 + } 1.1206 + } else { 1.1207 + if (fInfo.fColorCount != b.fInfo.fColorCount || 1.1208 + memcmp(fInfo.fColors, b.fInfo.fColors, 1.1209 + sizeof(SkColor) * fInfo.fColorCount) != 0 || 1.1210 + memcmp(fInfo.fColorOffsets, b.fInfo.fColorOffsets, 1.1211 + sizeof(SkScalar) * fInfo.fColorCount) != 0 || 1.1212 + fInfo.fPoint[0] != b.fInfo.fPoint[0] || 1.1213 + fInfo.fTileMode != b.fInfo.fTileMode) { 1.1214 + return false; 1.1215 + } 1.1216 + 1.1217 + switch (fType) { 1.1218 + case SkShader::kLinear_GradientType: 1.1219 + if (fInfo.fPoint[1] != b.fInfo.fPoint[1]) { 1.1220 + return false; 1.1221 + } 1.1222 + break; 1.1223 + case SkShader::kRadial_GradientType: 1.1224 + if (fInfo.fRadius[0] != b.fInfo.fRadius[0]) { 1.1225 + return false; 1.1226 + } 1.1227 + break; 1.1228 + case SkShader::kRadial2_GradientType: 1.1229 + case SkShader::kConical_GradientType: 1.1230 + if (fInfo.fPoint[1] != b.fInfo.fPoint[1] || 1.1231 + fInfo.fRadius[0] != b.fInfo.fRadius[0] || 1.1232 + fInfo.fRadius[1] != b.fInfo.fRadius[1]) { 1.1233 + return false; 1.1234 + } 1.1235 + break; 1.1236 + case SkShader::kSweep_GradientType: 1.1237 + case SkShader::kNone_GradientType: 1.1238 + case SkShader::kColor_GradientType: 1.1239 + break; 1.1240 + } 1.1241 + } 1.1242 + return true; 1.1243 +} 1.1244 + 1.1245 +SkPDFShader::State::State(const SkShader& shader, 1.1246 + const SkMatrix& canvasTransform, const SkIRect& bbox) 1.1247 + : fCanvasTransform(canvasTransform), 1.1248 + fBBox(bbox), 1.1249 + fPixelGeneration(0) { 1.1250 + fInfo.fColorCount = 0; 1.1251 + fInfo.fColors = NULL; 1.1252 + fInfo.fColorOffsets = NULL; 1.1253 + fShaderTransform = shader.getLocalMatrix(); 1.1254 + fImageTileModes[0] = fImageTileModes[1] = SkShader::kClamp_TileMode; 1.1255 + 1.1256 + fType = shader.asAGradient(&fInfo); 1.1257 + 1.1258 + if (fType == SkShader::kNone_GradientType) { 1.1259 + SkShader::BitmapType bitmapType; 1.1260 + SkMatrix matrix; 1.1261 + bitmapType = shader.asABitmap(&fImage, &matrix, fImageTileModes); 1.1262 + if (bitmapType != SkShader::kDefault_BitmapType) { 1.1263 + fImage.reset(); 1.1264 + return; 1.1265 + } 1.1266 + SkASSERT(matrix.isIdentity()); 1.1267 + fPixelGeneration = fImage.getGenerationID(); 1.1268 + } else { 1.1269 + AllocateGradientInfoStorage(); 1.1270 + shader.asAGradient(&fInfo); 1.1271 + } 1.1272 +} 1.1273 + 1.1274 +SkPDFShader::State::State(const SkPDFShader::State& other) 1.1275 + : fType(other.fType), 1.1276 + fCanvasTransform(other.fCanvasTransform), 1.1277 + fShaderTransform(other.fShaderTransform), 1.1278 + fBBox(other.fBBox) 1.1279 +{ 1.1280 + // Only gradients supported for now, since that is all that is used. 1.1281 + // If needed, image state copy constructor can be added here later. 1.1282 + SkASSERT(fType != SkShader::kNone_GradientType); 1.1283 + 1.1284 + if (fType != SkShader::kNone_GradientType) { 1.1285 + fInfo = other.fInfo; 1.1286 + 1.1287 + AllocateGradientInfoStorage(); 1.1288 + for (int i = 0; i < fInfo.fColorCount; i++) { 1.1289 + fInfo.fColors[i] = other.fInfo.fColors[i]; 1.1290 + fInfo.fColorOffsets[i] = other.fInfo.fColorOffsets[i]; 1.1291 + } 1.1292 + } 1.1293 +} 1.1294 + 1.1295 +/** 1.1296 + * Create a copy of this gradient state with alpha assigned to RGB luminousity. 1.1297 + * Only valid for gradient states. 1.1298 + */ 1.1299 +SkPDFShader::State* SkPDFShader::State::CreateAlphaToLuminosityState() const { 1.1300 + SkASSERT(fType != SkShader::kNone_GradientType); 1.1301 + 1.1302 + SkPDFShader::State* newState = new SkPDFShader::State(*this); 1.1303 + 1.1304 + for (int i = 0; i < fInfo.fColorCount; i++) { 1.1305 + SkAlpha alpha = SkColorGetA(fInfo.fColors[i]); 1.1306 + newState->fInfo.fColors[i] = SkColorSetARGB(255, alpha, alpha, alpha); 1.1307 + } 1.1308 + 1.1309 + return newState; 1.1310 +} 1.1311 + 1.1312 +/** 1.1313 + * Create a copy of this gradient state with alpha set to fully opaque 1.1314 + * Only valid for gradient states. 1.1315 + */ 1.1316 +SkPDFShader::State* SkPDFShader::State::CreateOpaqueState() const { 1.1317 + SkASSERT(fType != SkShader::kNone_GradientType); 1.1318 + 1.1319 + SkPDFShader::State* newState = new SkPDFShader::State(*this); 1.1320 + for (int i = 0; i < fInfo.fColorCount; i++) { 1.1321 + newState->fInfo.fColors[i] = SkColorSetA(fInfo.fColors[i], 1.1322 + SK_AlphaOPAQUE); 1.1323 + } 1.1324 + 1.1325 + return newState; 1.1326 +} 1.1327 + 1.1328 +/** 1.1329 + * Returns true if state is a gradient and the gradient has alpha. 1.1330 + */ 1.1331 +bool SkPDFShader::State::GradientHasAlpha() const { 1.1332 + if (fType == SkShader::kNone_GradientType) { 1.1333 + return false; 1.1334 + } 1.1335 + 1.1336 + for (int i = 0; i < fInfo.fColorCount; i++) { 1.1337 + SkAlpha alpha = SkColorGetA(fInfo.fColors[i]); 1.1338 + if (alpha != SK_AlphaOPAQUE) { 1.1339 + return true; 1.1340 + } 1.1341 + } 1.1342 + return false; 1.1343 +} 1.1344 + 1.1345 +void SkPDFShader::State::AllocateGradientInfoStorage() { 1.1346 + fColorData.set(sk_malloc_throw( 1.1347 + fInfo.fColorCount * (sizeof(SkColor) + sizeof(SkScalar)))); 1.1348 + fInfo.fColors = reinterpret_cast<SkColor*>(fColorData.get()); 1.1349 + fInfo.fColorOffsets = 1.1350 + reinterpret_cast<SkScalar*>(fInfo.fColors + fInfo.fColorCount); 1.1351 +}