1 2/* 3 * Copyright 2011 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 10#include "SkPDFShader.h" 11 12#include "SkData.h" 13#include "SkPDFCanon.h" 14#include "SkPDFDevice.h" 15#include "SkPDFFormXObject.h" 16#include "SkPDFGraphicState.h" 17#include "SkPDFResourceDict.h" 18#include "SkPDFUtils.h" 19#include "SkScalar.h" 20#include "SkStream.h" 21#include "SkTemplates.h" 22#include "SkTypes.h" 23 24static bool inverse_transform_bbox(const SkMatrix& matrix, SkRect* bbox) { 25 SkMatrix inverse; 26 if (!matrix.invert(&inverse)) { 27 return false; 28 } 29 inverse.mapRect(bbox); 30 return true; 31} 32 33static void unitToPointsMatrix(const SkPoint pts[2], SkMatrix* matrix) { 34 SkVector vec = pts[1] - pts[0]; 35 SkScalar mag = vec.length(); 36 SkScalar inv = mag ? SkScalarInvert(mag) : 0; 37 38 vec.scale(inv); 39 matrix->setSinCos(vec.fY, vec.fX); 40 matrix->preScale(mag, mag); 41 matrix->postTranslate(pts[0].fX, pts[0].fY); 42} 43 44/* Assumes t + startOffset is on the stack and does a linear interpolation on t 45 between startOffset and endOffset from prevColor to curColor (for each color 46 component), leaving the result in component order on the stack. It assumes 47 there are always 3 components per color. 48 @param range endOffset - startOffset 49 @param curColor[components] The current color components. 50 @param prevColor[components] The previous color components. 51 @param result The result ps function. 52 */ 53static void interpolateColorCode(SkScalar range, SkScalar* curColor, 54 SkScalar* prevColor, SkString* result) { 55 SkASSERT(range != SkIntToScalar(0)); 56 static const int kColorComponents = 3; 57 58 // Figure out how to scale each color component. 59 SkScalar multiplier[kColorComponents]; 60 for (int i = 0; i < kColorComponents; i++) { 61 multiplier[i] = (curColor[i] - prevColor[i]) / range; 62 } 63 64 // Calculate when we no longer need to keep a copy of the input parameter t. 65 // If the last component to use t is i, then dupInput[0..i - 1] = true 66 // and dupInput[i .. components] = false. 67 bool dupInput[kColorComponents]; 68 dupInput[kColorComponents - 1] = false; 69 for (int i = kColorComponents - 2; i >= 0; i--) { 70 dupInput[i] = dupInput[i + 1] || multiplier[i + 1] != 0; 71 } 72 73 if (!dupInput[0] && multiplier[0] == 0) { 74 result->append("pop "); 75 } 76 77 for (int i = 0; i < kColorComponents; i++) { 78 // If the next components needs t and this component will consume a 79 // copy, make another copy. 80 if (dupInput[i] && multiplier[i] != 0) { 81 result->append("dup "); 82 } 83 84 if (multiplier[i] == 0) { 85 result->appendScalar(prevColor[i]); 86 result->append(" "); 87 } else { 88 if (multiplier[i] != 1) { 89 result->appendScalar(multiplier[i]); 90 result->append(" mul "); 91 } 92 if (prevColor[i] != 0) { 93 result->appendScalar(prevColor[i]); 94 result->append(" add "); 95 } 96 } 97 98 if (dupInput[i]) { 99 result->append("exch\n"); 100 } 101 } 102} 103 104/* Generate Type 4 function code to map t=[0,1) to the passed gradient, 105 clamping at the edges of the range. The generated code will be of the form: 106 if (t < 0) { 107 return colorData[0][r,g,b]; 108 } else { 109 if (t < info.fColorOffsets[1]) { 110 return linearinterpolation(colorData[0][r,g,b], 111 colorData[1][r,g,b]); 112 } else { 113 if (t < info.fColorOffsets[2]) { 114 return linearinterpolation(colorData[1][r,g,b], 115 colorData[2][r,g,b]); 116 } else { 117 118 ... } else { 119 return colorData[info.fColorCount - 1][r,g,b]; 120 } 121 ... 122 } 123 } 124 */ 125static void gradientFunctionCode(const SkShader::GradientInfo& info, 126 SkString* result) { 127 /* We want to linearly interpolate from the previous color to the next. 128 Scale the colors from 0..255 to 0..1 and determine the multipliers 129 for interpolation. 130 C{r,g,b}(t, section) = t - offset_(section-1) + t * Multiplier{r,g,b}. 131 */ 132 static const int kColorComponents = 3; 133 typedef SkScalar ColorTuple[kColorComponents]; 134 SkAutoSTMalloc<4, ColorTuple> colorDataAlloc(info.fColorCount); 135 ColorTuple *colorData = colorDataAlloc.get(); 136 const SkScalar scale = SkScalarInvert(SkIntToScalar(255)); 137 for (int i = 0; i < info.fColorCount; i++) { 138 colorData[i][0] = SkScalarMul(SkColorGetR(info.fColors[i]), scale); 139 colorData[i][1] = SkScalarMul(SkColorGetG(info.fColors[i]), scale); 140 colorData[i][2] = SkScalarMul(SkColorGetB(info.fColors[i]), scale); 141 } 142 143 // Clamp the initial color. 144 result->append("dup 0 le {pop "); 145 result->appendScalar(colorData[0][0]); 146 result->append(" "); 147 result->appendScalar(colorData[0][1]); 148 result->append(" "); 149 result->appendScalar(colorData[0][2]); 150 result->append(" }\n"); 151 152 // The gradient colors. 153 int gradients = 0; 154 for (int i = 1 ; i < info.fColorCount; i++) { 155 if (info.fColorOffsets[i] == info.fColorOffsets[i - 1]) { 156 continue; 157 } 158 gradients++; 159 160 result->append("{dup "); 161 result->appendScalar(info.fColorOffsets[i]); 162 result->append(" le {"); 163 if (info.fColorOffsets[i - 1] != 0) { 164 result->appendScalar(info.fColorOffsets[i - 1]); 165 result->append(" sub\n"); 166 } 167 168 interpolateColorCode(info.fColorOffsets[i] - info.fColorOffsets[i - 1], 169 colorData[i], colorData[i - 1], result); 170 result->append("}\n"); 171 } 172 173 // Clamp the final color. 174 result->append("{pop "); 175 result->appendScalar(colorData[info.fColorCount - 1][0]); 176 result->append(" "); 177 result->appendScalar(colorData[info.fColorCount - 1][1]); 178 result->append(" "); 179 result->appendScalar(colorData[info.fColorCount - 1][2]); 180 181 for (int i = 0 ; i < gradients + 1; i++) { 182 result->append("} ifelse\n"); 183 } 184} 185 186/* Map a value of t on the stack into [0, 1) for Repeat or Mirror tile mode. */ 187static void tileModeCode(SkShader::TileMode mode, SkString* result) { 188 if (mode == SkShader::kRepeat_TileMode) { 189 result->append("dup truncate sub\n"); // Get the fractional part. 190 result->append("dup 0 le {1 add} if\n"); // Map (-1,0) => (0,1) 191 return; 192 } 193 194 if (mode == SkShader::kMirror_TileMode) { 195 // Map t mod 2 into [0, 1, 1, 0]. 196 // Code Stack 197 result->append("abs " // Map negative to positive. 198 "dup " // t.s t.s 199 "truncate " // t.s t 200 "dup " // t.s t t 201 "cvi " // t.s t T 202 "2 mod " // t.s t (i mod 2) 203 "1 eq " // t.s t true|false 204 "3 1 roll " // true|false t.s t 205 "sub " // true|false 0.s 206 "exch " // 0.s true|false 207 "{1 exch sub} if\n"); // 1 - 0.s|0.s 208 } 209} 210 211/** 212 * Returns PS function code that applies inverse perspective 213 * to a x, y point. 214 * The function assumes that the stack has at least two elements, 215 * and that the top 2 elements are numeric values. 216 * After executing this code on a PS stack, the last 2 elements are updated 217 * while the rest of the stack is preserved intact. 218 * inversePerspectiveMatrix is the inverse perspective matrix. 219 */ 220static SkString apply_perspective_to_coordinates( 221 const SkMatrix& inversePerspectiveMatrix) { 222 SkString code; 223 if (!inversePerspectiveMatrix.hasPerspective()) { 224 return code; 225 } 226 227 // Perspective matrix should be: 228 // 1 0 0 229 // 0 1 0 230 // p0 p1 p2 231 232 const SkScalar p0 = inversePerspectiveMatrix[SkMatrix::kMPersp0]; 233 const SkScalar p1 = inversePerspectiveMatrix[SkMatrix::kMPersp1]; 234 const SkScalar p2 = inversePerspectiveMatrix[SkMatrix::kMPersp2]; 235 236 // y = y / (p2 + p0 x + p1 y) 237 // x = x / (p2 + p0 x + p1 y) 238 239 // Input on stack: x y 240 code.append(" dup "); // x y y 241 code.appendScalar(p1); // x y y p1 242 code.append(" mul " // x y y*p1 243 " 2 index "); // x y y*p1 x 244 code.appendScalar(p0); // x y y p1 x p0 245 code.append(" mul "); // x y y*p1 x*p0 246 code.appendScalar(p2); // x y y p1 x*p0 p2 247 code.append(" add " // x y y*p1 x*p0+p2 248 "add " // x y y*p1+x*p0+p2 249 "3 1 roll " // y*p1+x*p0+p2 x y 250 "2 index " // z x y y*p1+x*p0+p2 251 "div " // y*p1+x*p0+p2 x y/(y*p1+x*p0+p2) 252 "3 1 roll " // y/(y*p1+x*p0+p2) y*p1+x*p0+p2 x 253 "exch " // y/(y*p1+x*p0+p2) x y*p1+x*p0+p2 254 "div " // y/(y*p1+x*p0+p2) x/(y*p1+x*p0+p2) 255 "exch\n"); // x/(y*p1+x*p0+p2) y/(y*p1+x*p0+p2) 256 return code; 257} 258 259static SkString linearCode(const SkShader::GradientInfo& info, 260 const SkMatrix& perspectiveRemover) { 261 SkString function("{"); 262 263 function.append(apply_perspective_to_coordinates(perspectiveRemover)); 264 265 function.append("pop\n"); // Just ditch the y value. 266 tileModeCode(info.fTileMode, &function); 267 gradientFunctionCode(info, &function); 268 function.append("}"); 269 return function; 270} 271 272static SkString radialCode(const SkShader::GradientInfo& info, 273 const SkMatrix& perspectiveRemover) { 274 SkString function("{"); 275 276 function.append(apply_perspective_to_coordinates(perspectiveRemover)); 277 278 // Find the distance from the origin. 279 function.append("dup " // x y y 280 "mul " // x y^2 281 "exch " // y^2 x 282 "dup " // y^2 x x 283 "mul " // y^2 x^2 284 "add " // y^2+x^2 285 "sqrt\n"); // sqrt(y^2+x^2) 286 287 tileModeCode(info.fTileMode, &function); 288 gradientFunctionCode(info, &function); 289 function.append("}"); 290 return function; 291} 292 293/* Conical gradient shader, based on the Canvas spec for radial gradients 294 See: http://www.w3.org/TR/2dcontext/#dom-context-2d-createradialgradient 295 */ 296static SkString twoPointConicalCode(const SkShader::GradientInfo& info, 297 const SkMatrix& perspectiveRemover) { 298 SkScalar dx = info.fPoint[1].fX - info.fPoint[0].fX; 299 SkScalar dy = info.fPoint[1].fY - info.fPoint[0].fY; 300 SkScalar r0 = info.fRadius[0]; 301 SkScalar dr = info.fRadius[1] - info.fRadius[0]; 302 SkScalar a = SkScalarMul(dx, dx) + SkScalarMul(dy, dy) - 303 SkScalarMul(dr, dr); 304 305 // First compute t, if the pixel falls outside the cone, then we'll end 306 // with 'false' on the stack, otherwise we'll push 'true' with t below it 307 308 // We start with a stack of (x y), copy it and then consume one copy in 309 // order to calculate b and the other to calculate c. 310 SkString function("{"); 311 312 function.append(apply_perspective_to_coordinates(perspectiveRemover)); 313 314 function.append("2 copy "); 315 316 // Calculate b and b^2; b = -2 * (y * dy + x * dx + r0 * dr). 317 function.appendScalar(dy); 318 function.append(" mul exch "); 319 function.appendScalar(dx); 320 function.append(" mul add "); 321 function.appendScalar(SkScalarMul(r0, dr)); 322 function.append(" add -2 mul dup dup mul\n"); 323 324 // c = x^2 + y^2 + radius0^2 325 function.append("4 2 roll dup mul exch dup mul add "); 326 function.appendScalar(SkScalarMul(r0, r0)); 327 function.append(" sub dup 4 1 roll\n"); 328 329 // Contents of the stack at this point: c, b, b^2, c 330 331 // if a = 0, then we collapse to a simpler linear case 332 if (a == 0) { 333 334 // t = -c/b 335 function.append("pop pop div neg dup "); 336 337 // compute radius(t) 338 function.appendScalar(dr); 339 function.append(" mul "); 340 function.appendScalar(r0); 341 function.append(" add\n"); 342 343 // if r(t) < 0, then it's outside the cone 344 function.append("0 lt {pop false} {true} ifelse\n"); 345 346 } else { 347 348 // quadratic case: the Canvas spec wants the largest 349 // root t for which radius(t) > 0 350 351 // compute the discriminant (b^2 - 4ac) 352 function.appendScalar(SkScalarMul(SkIntToScalar(4), a)); 353 function.append(" mul sub dup\n"); 354 355 // if d >= 0, proceed 356 function.append("0 ge {\n"); 357 358 // an intermediate value we'll use to compute the roots: 359 // q = -0.5 * (b +/- sqrt(d)) 360 function.append("sqrt exch dup 0 lt {exch -1 mul} if"); 361 function.append(" add -0.5 mul dup\n"); 362 363 // first root = q / a 364 function.appendScalar(a); 365 function.append(" div\n"); 366 367 // second root = c / q 368 function.append("3 1 roll div\n"); 369 370 // put the larger root on top of the stack 371 function.append("2 copy gt {exch} if\n"); 372 373 // compute radius(t) for larger root 374 function.append("dup "); 375 function.appendScalar(dr); 376 function.append(" mul "); 377 function.appendScalar(r0); 378 function.append(" add\n"); 379 380 // if r(t) > 0, we have our t, pop off the smaller root and we're done 381 function.append(" 0 gt {exch pop true}\n"); 382 383 // otherwise, throw out the larger one and try the smaller root 384 function.append("{pop dup\n"); 385 function.appendScalar(dr); 386 function.append(" mul "); 387 function.appendScalar(r0); 388 function.append(" add\n"); 389 390 // if r(t) < 0, push false, otherwise the smaller root is our t 391 function.append("0 le {pop false} {true} ifelse\n"); 392 function.append("} ifelse\n"); 393 394 // d < 0, clear the stack and push false 395 function.append("} {pop pop pop false} ifelse\n"); 396 } 397 398 // if the pixel is in the cone, proceed to compute a color 399 function.append("{"); 400 tileModeCode(info.fTileMode, &function); 401 gradientFunctionCode(info, &function); 402 403 // otherwise, just write black 404 function.append("} {0 0 0} ifelse }"); 405 406 return function; 407} 408 409static SkString sweepCode(const SkShader::GradientInfo& info, 410 const SkMatrix& perspectiveRemover) { 411 SkString function("{exch atan 360 div\n"); 412 tileModeCode(info.fTileMode, &function); 413 gradientFunctionCode(info, &function); 414 function.append("}"); 415 return function; 416} 417 418static void drawBitmapMatrix(SkCanvas* canvas, const SkBitmap& bm, const SkMatrix& matrix) { 419 SkAutoCanvasRestore acr(canvas, true); 420 canvas->concat(matrix); 421 canvas->drawBitmap(bm, 0, 0); 422} 423 424class SkPDFShader::State { 425public: 426 SkShader::GradientType fType; 427 SkShader::GradientInfo fInfo; 428 SkAutoFree fColorData; // This provides storage for arrays in fInfo. 429 SkMatrix fCanvasTransform; 430 SkMatrix fShaderTransform; 431 SkIRect fBBox; 432 433 SkBitmap fImage; 434 uint32_t fPixelGeneration; 435 SkShader::TileMode fImageTileModes[2]; 436 437 State(const SkShader& shader, const SkMatrix& canvasTransform, 438 const SkIRect& bbox, SkScalar rasterScale); 439 440 bool operator==(const State& b) const; 441 442 SkPDFShader::State* CreateAlphaToLuminosityState() const; 443 SkPDFShader::State* CreateOpaqueState() const; 444 445 bool GradientHasAlpha() const; 446 447private: 448 State(const State& other); 449 State operator=(const State& rhs); 450 void AllocateGradientInfoStorage(); 451}; 452 453//////////////////////////////////////////////////////////////////////////////// 454 455SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state) 456 : SkPDFDict("Pattern"), fShaderState(state) {} 457 458SkPDFFunctionShader::~SkPDFFunctionShader() {} 459 460bool SkPDFFunctionShader::equals(const SkPDFShader::State& state) const { 461 return state == *fShaderState; 462} 463 464//////////////////////////////////////////////////////////////////////////////// 465 466SkPDFAlphaFunctionShader::SkPDFAlphaFunctionShader(SkPDFShader::State* state) 467 : fShaderState(state) {} 468 469bool SkPDFAlphaFunctionShader::equals(const SkPDFShader::State& state) const { 470 return state == *fShaderState; 471} 472 473SkPDFAlphaFunctionShader::~SkPDFAlphaFunctionShader() {} 474 475//////////////////////////////////////////////////////////////////////////////// 476 477SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) 478 : fShaderState(state) {} 479 480bool SkPDFImageShader::equals(const SkPDFShader::State& state) const { 481 return state == *fShaderState; 482} 483 484SkPDFImageShader::~SkPDFImageShader() {} 485 486//////////////////////////////////////////////////////////////////////////////// 487 488static SkPDFObject* get_pdf_shader_by_state( 489 SkPDFCanon* canon, 490 SkScalar dpi, 491 SkAutoTDelete<SkPDFShader::State>* autoState) { 492 const SkPDFShader::State& state = **autoState; 493 if (state.fType == SkShader::kNone_GradientType && state.fImage.isNull()) { 494 // TODO(vandebo) This drops SKComposeShader on the floor. We could 495 // handle compose shader by pulling things up to a layer, drawing with 496 // the first shader, applying the xfer mode and drawing again with the 497 // second shader, then applying the layer to the original drawing. 498 return NULL; 499 } else if (state.fType == SkShader::kNone_GradientType) { 500 SkPDFObject* shader = canon->findImageShader(state); 501 return shader ? SkRef(shader) 502 : SkPDFImageShader::Create(canon, dpi, autoState); 503 } else if (state.GradientHasAlpha()) { 504 SkPDFObject* shader = canon->findAlphaShader(state); 505 return shader ? SkRef(shader) 506 : SkPDFAlphaFunctionShader::Create(canon, dpi, autoState); 507 } else { 508 SkPDFObject* shader = canon->findFunctionShader(state); 509 return shader ? SkRef(shader) 510 : SkPDFFunctionShader::Create(canon, autoState); 511 } 512} 513 514// static 515SkPDFObject* SkPDFShader::GetPDFShader(SkPDFCanon* canon, 516 SkScalar dpi, 517 const SkShader& shader, 518 const SkMatrix& matrix, 519 const SkIRect& surfaceBBox, 520 SkScalar rasterScale) { 521 SkAutoTDelete<SkPDFShader::State> state( 522 SkNEW_ARGS(State, (shader, matrix, surfaceBBox, rasterScale))); 523 return get_pdf_shader_by_state(canon, dpi, &state); 524} 525 526static SkPDFDict* get_gradient_resource_dict( 527 SkPDFObject* functionShader, 528 SkPDFObject* gState) { 529 SkTDArray<SkPDFObject*> patterns; 530 if (functionShader) { 531 patterns.push(functionShader); 532 } 533 SkTDArray<SkPDFObject*> graphicStates; 534 if (gState) { 535 graphicStates.push(gState); 536 } 537 return SkPDFResourceDict::Create(&graphicStates, &patterns, NULL, NULL); 538} 539 540static void populate_tiling_pattern_dict(SkPDFDict* pattern, 541 SkRect& bbox, 542 SkPDFDict* resources, 543 const SkMatrix& matrix) { 544 const int kTiling_PatternType = 1; 545 const int kColoredTilingPattern_PaintType = 1; 546 const int kConstantSpacing_TilingType = 1; 547 548 pattern->insertName("Type", "Pattern"); 549 pattern->insertInt("PatternType", kTiling_PatternType); 550 pattern->insertInt("PaintType", kColoredTilingPattern_PaintType); 551 pattern->insertInt("TilingType", kConstantSpacing_TilingType); 552 pattern->insertObject("BBox", SkPDFUtils::RectToArray(bbox)); 553 pattern->insertScalar("XStep", bbox.width()); 554 pattern->insertScalar("YStep", bbox.height()); 555 pattern->insertObject("Resources", SkRef(resources)); 556 if (!matrix.isIdentity()) { 557 pattern->insertObject("Matrix", SkPDFUtils::MatrixToArray(matrix)); 558 } 559} 560 561/** 562 * Creates a content stream which fills the pattern P0 across bounds. 563 * @param gsIndex A graphics state resource index to apply, or <0 if no 564 * graphics state to apply. 565 */ 566static SkStream* create_pattern_fill_content(int gsIndex, SkRect& bounds) { 567 SkDynamicMemoryWStream content; 568 if (gsIndex >= 0) { 569 SkPDFUtils::ApplyGraphicState(gsIndex, &content); 570 } 571 SkPDFUtils::ApplyPattern(0, &content); 572 SkPDFUtils::AppendRectangle(bounds, &content); 573 SkPDFUtils::PaintPath(SkPaint::kFill_Style, SkPath::kEvenOdd_FillType, 574 &content); 575 576 return content.detachAsStream(); 577} 578 579/** 580 * Creates a ExtGState with the SMask set to the luminosityShader in 581 * luminosity mode. The shader pattern extends to the bbox. 582 */ 583static SkPDFObject* create_smask_graphic_state( 584 SkPDFCanon* canon, SkScalar dpi, const SkPDFShader::State& state) { 585 SkRect bbox; 586 bbox.set(state.fBBox); 587 588 SkAutoTDelete<SkPDFShader::State> alphaToLuminosityState( 589 state.CreateAlphaToLuminosityState()); 590 SkAutoTUnref<SkPDFObject> luminosityShader( 591 get_pdf_shader_by_state(canon, dpi, &alphaToLuminosityState)); 592 593 SkAutoTDelete<SkStream> alphaStream(create_pattern_fill_content(-1, bbox)); 594 595 SkAutoTUnref<SkPDFDict> 596 resources(get_gradient_resource_dict(luminosityShader, NULL)); 597 598 SkAutoTUnref<SkPDFFormXObject> alphaMask( 599 new SkPDFFormXObject(alphaStream.get(), bbox, resources.get())); 600 601 return SkPDFGraphicState::GetSMaskGraphicState( 602 alphaMask.get(), false, 603 SkPDFGraphicState::kLuminosity_SMaskMode); 604} 605 606SkPDFAlphaFunctionShader* SkPDFAlphaFunctionShader::Create( 607 SkPDFCanon* canon, 608 SkScalar dpi, 609 SkAutoTDelete<SkPDFShader::State>* autoState) { 610 const SkPDFShader::State& state = **autoState; 611 SkRect bbox; 612 bbox.set(state.fBBox); 613 614 SkAutoTDelete<SkPDFShader::State> opaqueState(state.CreateOpaqueState()); 615 616 SkAutoTUnref<SkPDFObject> colorShader( 617 get_pdf_shader_by_state(canon, dpi, &opaqueState)); 618 if (!colorShader) { 619 return NULL; 620 } 621 622 // Create resource dict with alpha graphics state as G0 and 623 // pattern shader as P0, then write content stream. 624 SkAutoTUnref<SkPDFObject> alphaGs( 625 create_smask_graphic_state(canon, dpi, state)); 626 627 SkPDFAlphaFunctionShader* alphaFunctionShader = 628 SkNEW_ARGS(SkPDFAlphaFunctionShader, (autoState->detach())); 629 630 SkAutoTUnref<SkPDFDict> resourceDict( 631 get_gradient_resource_dict(colorShader.get(), alphaGs.get())); 632 633 SkAutoTDelete<SkStream> colorStream( 634 create_pattern_fill_content(0, bbox)); 635 alphaFunctionShader->setData(colorStream.get()); 636 637 populate_tiling_pattern_dict(alphaFunctionShader, bbox, resourceDict.get(), 638 SkMatrix::I()); 639 canon->addAlphaShader(alphaFunctionShader); 640 return alphaFunctionShader; 641} 642 643// Finds affine and persp such that in = affine * persp. 644// but it returns the inverse of perspective matrix. 645static bool split_perspective(const SkMatrix in, SkMatrix* affine, 646 SkMatrix* perspectiveInverse) { 647 const SkScalar p2 = in[SkMatrix::kMPersp2]; 648 649 if (SkScalarNearlyZero(p2)) { 650 return false; 651 } 652 653 const SkScalar zero = SkIntToScalar(0); 654 const SkScalar one = SkIntToScalar(1); 655 656 const SkScalar sx = in[SkMatrix::kMScaleX]; 657 const SkScalar kx = in[SkMatrix::kMSkewX]; 658 const SkScalar tx = in[SkMatrix::kMTransX]; 659 const SkScalar ky = in[SkMatrix::kMSkewY]; 660 const SkScalar sy = in[SkMatrix::kMScaleY]; 661 const SkScalar ty = in[SkMatrix::kMTransY]; 662 const SkScalar p0 = in[SkMatrix::kMPersp0]; 663 const SkScalar p1 = in[SkMatrix::kMPersp1]; 664 665 // Perspective matrix would be: 666 // 1 0 0 667 // 0 1 0 668 // p0 p1 p2 669 // But we need the inverse of persp. 670 perspectiveInverse->setAll(one, zero, zero, 671 zero, one, zero, 672 -p0/p2, -p1/p2, 1/p2); 673 674 affine->setAll(sx - p0 * tx / p2, kx - p1 * tx / p2, tx / p2, 675 ky - p0 * ty / p2, sy - p1 * ty / p2, ty / p2, 676 zero, zero, one); 677 678 return true; 679} 680 681namespace { 682SkPDFObject* create_range_object() { 683 SkPDFArray* range = SkNEW(SkPDFArray); 684 range->reserve(6); 685 range->appendInt(0); 686 range->appendInt(1); 687 range->appendInt(0); 688 range->appendInt(1); 689 range->appendInt(0); 690 range->appendInt(1); 691 return range; 692} 693 694template <typename T> void unref(T* ptr) { ptr->unref();} 695} // namespace 696 697SK_DECLARE_STATIC_LAZY_PTR(SkPDFObject, rangeObject, 698 create_range_object, unref<SkPDFObject>); 699 700static SkPDFStream* make_ps_function(const SkString& psCode, 701 SkPDFArray* domain) { 702 SkAutoDataUnref funcData( 703 SkData::NewWithCopy(psCode.c_str(), psCode.size())); 704 SkPDFStream* result = SkNEW_ARGS(SkPDFStream, (funcData.get())); 705 result->insertInt("FunctionType", 4); 706 result->insertObject("Domain", SkRef(domain)); 707 result->insertObject("Range", SkRef(rangeObject.get())); 708 return result; 709} 710 711SkPDFFunctionShader* SkPDFFunctionShader::Create( 712 SkPDFCanon* canon, SkAutoTDelete<SkPDFShader::State>* autoState) { 713 const SkPDFShader::State& state = **autoState; 714 715 SkString (*codeFunction)(const SkShader::GradientInfo& info, 716 const SkMatrix& perspectiveRemover) = NULL; 717 SkPoint transformPoints[2]; 718 719 // Depending on the type of the gradient, we want to transform the 720 // coordinate space in different ways. 721 const SkShader::GradientInfo* info = &state.fInfo; 722 transformPoints[0] = info->fPoint[0]; 723 transformPoints[1] = info->fPoint[1]; 724 switch (state.fType) { 725 case SkShader::kLinear_GradientType: 726 codeFunction = &linearCode; 727 break; 728 case SkShader::kRadial_GradientType: 729 transformPoints[1] = transformPoints[0]; 730 transformPoints[1].fX += info->fRadius[0]; 731 codeFunction = &radialCode; 732 break; 733 case SkShader::kConical_GradientType: { 734 transformPoints[1] = transformPoints[0]; 735 transformPoints[1].fX += SK_Scalar1; 736 codeFunction = &twoPointConicalCode; 737 break; 738 } 739 case SkShader::kSweep_GradientType: 740 transformPoints[1] = transformPoints[0]; 741 transformPoints[1].fX += SK_Scalar1; 742 codeFunction = &sweepCode; 743 break; 744 case SkShader::kColor_GradientType: 745 case SkShader::kNone_GradientType: 746 default: 747 return NULL; 748 } 749 750 // Move any scaling (assuming a unit gradient) or translation 751 // (and rotation for linear gradient), of the final gradient from 752 // info->fPoints to the matrix (updating bbox appropriately). Now 753 // the gradient can be drawn on on the unit segment. 754 SkMatrix mapperMatrix; 755 unitToPointsMatrix(transformPoints, &mapperMatrix); 756 757 SkMatrix finalMatrix = state.fCanvasTransform; 758 finalMatrix.preConcat(state.fShaderTransform); 759 finalMatrix.preConcat(mapperMatrix); 760 761 // Preserves as much as posible in the final matrix, and only removes 762 // the perspective. The inverse of the perspective is stored in 763 // perspectiveInverseOnly matrix and has 3 useful numbers 764 // (p0, p1, p2), while everything else is either 0 or 1. 765 // In this way the shader will handle it eficiently, with minimal code. 766 SkMatrix perspectiveInverseOnly = SkMatrix::I(); 767 if (finalMatrix.hasPerspective()) { 768 if (!split_perspective(finalMatrix, 769 &finalMatrix, &perspectiveInverseOnly)) { 770 return NULL; 771 } 772 } 773 774 SkRect bbox; 775 bbox.set(state.fBBox); 776 if (!inverse_transform_bbox(finalMatrix, &bbox)) { 777 return NULL; 778 } 779 780 SkAutoTUnref<SkPDFArray> domain(new SkPDFArray); 781 domain->reserve(4); 782 domain->appendScalar(bbox.fLeft); 783 domain->appendScalar(bbox.fRight); 784 domain->appendScalar(bbox.fTop); 785 domain->appendScalar(bbox.fBottom); 786 787 SkString functionCode; 788 // The two point radial gradient further references 789 // state.fInfo 790 // in translating from x, y coordinates to the t parameter. So, we have 791 // to transform the points and radii according to the calculated matrix. 792 if (state.fType == SkShader::kConical_GradientType) { 793 SkShader::GradientInfo twoPointRadialInfo = *info; 794 SkMatrix inverseMapperMatrix; 795 if (!mapperMatrix.invert(&inverseMapperMatrix)) { 796 return NULL; 797 } 798 inverseMapperMatrix.mapPoints(twoPointRadialInfo.fPoint, 2); 799 twoPointRadialInfo.fRadius[0] = 800 inverseMapperMatrix.mapRadius(info->fRadius[0]); 801 twoPointRadialInfo.fRadius[1] = 802 inverseMapperMatrix.mapRadius(info->fRadius[1]); 803 functionCode = codeFunction(twoPointRadialInfo, perspectiveInverseOnly); 804 } else { 805 functionCode = codeFunction(*info, perspectiveInverseOnly); 806 } 807 808 SkAutoTUnref<SkPDFDict> pdfShader(new SkPDFDict); 809 pdfShader->insertInt("ShadingType", 1); 810 pdfShader->insertName("ColorSpace", "DeviceRGB"); 811 pdfShader->insertObject("Domain", SkRef(domain.get())); 812 813 SkAutoTUnref<SkPDFStream> function( 814 make_ps_function(functionCode, domain.get())); 815 pdfShader->insertObjRef("Function", function.detach()); 816 817 SkPDFFunctionShader* pdfFunctionShader = 818 SkNEW_ARGS(SkPDFFunctionShader, (autoState->detach())); 819 820 pdfFunctionShader->insertInt("PatternType", 2); 821 pdfFunctionShader->insertObject("Matrix", 822 SkPDFUtils::MatrixToArray(finalMatrix)); 823 pdfFunctionShader->insertObject("Shading", pdfShader.detach()); 824 825 canon->addFunctionShader(pdfFunctionShader); 826 return pdfFunctionShader; 827} 828 829SkPDFImageShader* SkPDFImageShader::Create( 830 SkPDFCanon* canon, 831 SkScalar dpi, 832 SkAutoTDelete<SkPDFShader::State>* autoState) { 833 const SkPDFShader::State& state = **autoState; 834 835 state.fImage.lockPixels(); 836 837 // The image shader pattern cell will be drawn into a separate device 838 // in pattern cell space (no scaling on the bitmap, though there may be 839 // translations so that all content is in the device, coordinates > 0). 840 841 // Map clip bounds to shader space to ensure the device is large enough 842 // to handle fake clamping. 843 SkMatrix finalMatrix = state.fCanvasTransform; 844 finalMatrix.preConcat(state.fShaderTransform); 845 SkRect deviceBounds; 846 deviceBounds.set(state.fBBox); 847 if (!inverse_transform_bbox(finalMatrix, &deviceBounds)) { 848 return NULL; 849 } 850 851 const SkBitmap* image = &state.fImage; 852 SkRect bitmapBounds; 853 image->getBounds(&bitmapBounds); 854 855 // For tiling modes, the bounds should be extended to include the bitmap, 856 // otherwise the bitmap gets clipped out and the shader is empty and awful. 857 // For clamp modes, we're only interested in the clip region, whether 858 // or not the main bitmap is in it. 859 SkShader::TileMode tileModes[2]; 860 tileModes[0] = state.fImageTileModes[0]; 861 tileModes[1] = state.fImageTileModes[1]; 862 if (tileModes[0] != SkShader::kClamp_TileMode || 863 tileModes[1] != SkShader::kClamp_TileMode) { 864 deviceBounds.join(bitmapBounds); 865 } 866 867 SkISize size = SkISize::Make(SkScalarRoundToInt(deviceBounds.width()), 868 SkScalarRoundToInt(deviceBounds.height())); 869 SkAutoTUnref<SkPDFDevice> patternDevice( 870 SkPDFDevice::CreateUnflipped(size, dpi, canon)); 871 SkCanvas canvas(patternDevice.get()); 872 873 SkRect patternBBox; 874 image->getBounds(&patternBBox); 875 876 // Translate the canvas so that the bitmap origin is at (0, 0). 877 canvas.translate(-deviceBounds.left(), -deviceBounds.top()); 878 patternBBox.offset(-deviceBounds.left(), -deviceBounds.top()); 879 // Undo the translation in the final matrix 880 finalMatrix.preTranslate(deviceBounds.left(), deviceBounds.top()); 881 882 // If the bitmap is out of bounds (i.e. clamp mode where we only see the 883 // stretched sides), canvas will clip this out and the extraneous data 884 // won't be saved to the PDF. 885 canvas.drawBitmap(*image, 0, 0); 886 887 SkScalar width = SkIntToScalar(image->width()); 888 SkScalar height = SkIntToScalar(image->height()); 889 890 // Tiling is implied. First we handle mirroring. 891 if (tileModes[0] == SkShader::kMirror_TileMode) { 892 SkMatrix xMirror; 893 xMirror.setScale(-1, 1); 894 xMirror.postTranslate(2 * width, 0); 895 drawBitmapMatrix(&canvas, *image, xMirror); 896 patternBBox.fRight += width; 897 } 898 if (tileModes[1] == SkShader::kMirror_TileMode) { 899 SkMatrix yMirror; 900 yMirror.setScale(SK_Scalar1, -SK_Scalar1); 901 yMirror.postTranslate(0, 2 * height); 902 drawBitmapMatrix(&canvas, *image, yMirror); 903 patternBBox.fBottom += height; 904 } 905 if (tileModes[0] == SkShader::kMirror_TileMode && 906 tileModes[1] == SkShader::kMirror_TileMode) { 907 SkMatrix mirror; 908 mirror.setScale(-1, -1); 909 mirror.postTranslate(2 * width, 2 * height); 910 drawBitmapMatrix(&canvas, *image, mirror); 911 } 912 913 // Then handle Clamping, which requires expanding the pattern canvas to 914 // cover the entire surfaceBBox. 915 916 // If both x and y are in clamp mode, we start by filling in the corners. 917 // (Which are just a rectangles of the corner colors.) 918 if (tileModes[0] == SkShader::kClamp_TileMode && 919 tileModes[1] == SkShader::kClamp_TileMode) { 920 SkPaint paint; 921 SkRect rect; 922 rect = SkRect::MakeLTRB(deviceBounds.left(), deviceBounds.top(), 0, 0); 923 if (!rect.isEmpty()) { 924 paint.setColor(image->getColor(0, 0)); 925 canvas.drawRect(rect, paint); 926 } 927 928 rect = SkRect::MakeLTRB(width, deviceBounds.top(), 929 deviceBounds.right(), 0); 930 if (!rect.isEmpty()) { 931 paint.setColor(image->getColor(image->width() - 1, 0)); 932 canvas.drawRect(rect, paint); 933 } 934 935 rect = SkRect::MakeLTRB(width, height, 936 deviceBounds.right(), deviceBounds.bottom()); 937 if (!rect.isEmpty()) { 938 paint.setColor(image->getColor(image->width() - 1, 939 image->height() - 1)); 940 canvas.drawRect(rect, paint); 941 } 942 943 rect = SkRect::MakeLTRB(deviceBounds.left(), height, 944 0, deviceBounds.bottom()); 945 if (!rect.isEmpty()) { 946 paint.setColor(image->getColor(0, image->height() - 1)); 947 canvas.drawRect(rect, paint); 948 } 949 } 950 951 // Then expand the left, right, top, then bottom. 952 if (tileModes[0] == SkShader::kClamp_TileMode) { 953 SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, image->height()); 954 if (deviceBounds.left() < 0) { 955 SkBitmap left; 956 SkAssertResult(image->extractSubset(&left, subset)); 957 958 SkMatrix leftMatrix; 959 leftMatrix.setScale(-deviceBounds.left(), 1); 960 leftMatrix.postTranslate(deviceBounds.left(), 0); 961 drawBitmapMatrix(&canvas, left, leftMatrix); 962 963 if (tileModes[1] == SkShader::kMirror_TileMode) { 964 leftMatrix.postScale(SK_Scalar1, -SK_Scalar1); 965 leftMatrix.postTranslate(0, 2 * height); 966 drawBitmapMatrix(&canvas, left, leftMatrix); 967 } 968 patternBBox.fLeft = 0; 969 } 970 971 if (deviceBounds.right() > width) { 972 SkBitmap right; 973 subset.offset(image->width() - 1, 0); 974 SkAssertResult(image->extractSubset(&right, subset)); 975 976 SkMatrix rightMatrix; 977 rightMatrix.setScale(deviceBounds.right() - width, 1); 978 rightMatrix.postTranslate(width, 0); 979 drawBitmapMatrix(&canvas, right, rightMatrix); 980 981 if (tileModes[1] == SkShader::kMirror_TileMode) { 982 rightMatrix.postScale(SK_Scalar1, -SK_Scalar1); 983 rightMatrix.postTranslate(0, 2 * height); 984 drawBitmapMatrix(&canvas, right, rightMatrix); 985 } 986 patternBBox.fRight = deviceBounds.width(); 987 } 988 } 989 990 if (tileModes[1] == SkShader::kClamp_TileMode) { 991 SkIRect subset = SkIRect::MakeXYWH(0, 0, image->width(), 1); 992 if (deviceBounds.top() < 0) { 993 SkBitmap top; 994 SkAssertResult(image->extractSubset(&top, subset)); 995 996 SkMatrix topMatrix; 997 topMatrix.setScale(SK_Scalar1, -deviceBounds.top()); 998 topMatrix.postTranslate(0, deviceBounds.top()); 999 drawBitmapMatrix(&canvas, top, topMatrix); 1000 1001 if (tileModes[0] == SkShader::kMirror_TileMode) { 1002 topMatrix.postScale(-1, 1); 1003 topMatrix.postTranslate(2 * width, 0); 1004 drawBitmapMatrix(&canvas, top, topMatrix); 1005 } 1006 patternBBox.fTop = 0; 1007 } 1008 1009 if (deviceBounds.bottom() > height) { 1010 SkBitmap bottom; 1011 subset.offset(0, image->height() - 1); 1012 SkAssertResult(image->extractSubset(&bottom, subset)); 1013 1014 SkMatrix bottomMatrix; 1015 bottomMatrix.setScale(SK_Scalar1, deviceBounds.bottom() - height); 1016 bottomMatrix.postTranslate(0, height); 1017 drawBitmapMatrix(&canvas, bottom, bottomMatrix); 1018 1019 if (tileModes[0] == SkShader::kMirror_TileMode) { 1020 bottomMatrix.postScale(-1, 1); 1021 bottomMatrix.postTranslate(2 * width, 0); 1022 drawBitmapMatrix(&canvas, bottom, bottomMatrix); 1023 } 1024 patternBBox.fBottom = deviceBounds.height(); 1025 } 1026 } 1027 1028 // Put the canvas into the pattern stream (fContent). 1029 SkAutoTDelete<SkStreamAsset> content(patternDevice->content()); 1030 1031 SkPDFImageShader* imageShader = 1032 SkNEW_ARGS(SkPDFImageShader, (autoState->detach())); 1033 imageShader->setData(content.get()); 1034 1035 SkAutoTUnref<SkPDFDict> resourceDict( 1036 patternDevice->createResourceDict()); 1037 populate_tiling_pattern_dict(imageShader, patternBBox, 1038 resourceDict.get(), finalMatrix); 1039 1040 imageShader->fShaderState->fImage.unlockPixels(); 1041 1042 canon->addImageShader(imageShader); 1043 return imageShader; 1044} 1045 1046bool SkPDFShader::State::operator==(const SkPDFShader::State& b) const { 1047 if (fType != b.fType || 1048 fCanvasTransform != b.fCanvasTransform || 1049 fShaderTransform != b.fShaderTransform || 1050 fBBox != b.fBBox) { 1051 return false; 1052 } 1053 1054 if (fType == SkShader::kNone_GradientType) { 1055 if (fPixelGeneration != b.fPixelGeneration || 1056 fPixelGeneration == 0 || 1057 fImageTileModes[0] != b.fImageTileModes[0] || 1058 fImageTileModes[1] != b.fImageTileModes[1]) { 1059 return false; 1060 } 1061 } else { 1062 if (fInfo.fColorCount != b.fInfo.fColorCount || 1063 memcmp(fInfo.fColors, b.fInfo.fColors, 1064 sizeof(SkColor) * fInfo.fColorCount) != 0 || 1065 memcmp(fInfo.fColorOffsets, b.fInfo.fColorOffsets, 1066 sizeof(SkScalar) * fInfo.fColorCount) != 0 || 1067 fInfo.fPoint[0] != b.fInfo.fPoint[0] || 1068 fInfo.fTileMode != b.fInfo.fTileMode) { 1069 return false; 1070 } 1071 1072 switch (fType) { 1073 case SkShader::kLinear_GradientType: 1074 if (fInfo.fPoint[1] != b.fInfo.fPoint[1]) { 1075 return false; 1076 } 1077 break; 1078 case SkShader::kRadial_GradientType: 1079 if (fInfo.fRadius[0] != b.fInfo.fRadius[0]) { 1080 return false; 1081 } 1082 break; 1083 case SkShader::kConical_GradientType: 1084 if (fInfo.fPoint[1] != b.fInfo.fPoint[1] || 1085 fInfo.fRadius[0] != b.fInfo.fRadius[0] || 1086 fInfo.fRadius[1] != b.fInfo.fRadius[1]) { 1087 return false; 1088 } 1089 break; 1090 case SkShader::kSweep_GradientType: 1091 case SkShader::kNone_GradientType: 1092 case SkShader::kColor_GradientType: 1093 break; 1094 } 1095 } 1096 return true; 1097} 1098 1099SkPDFShader::State::State(const SkShader& shader, const SkMatrix& canvasTransform, 1100 const SkIRect& bbox, SkScalar rasterScale) 1101 : fCanvasTransform(canvasTransform), 1102 fBBox(bbox), 1103 fPixelGeneration(0) { 1104 fInfo.fColorCount = 0; 1105 fInfo.fColors = NULL; 1106 fInfo.fColorOffsets = NULL; 1107 fShaderTransform = shader.getLocalMatrix(); 1108 fImageTileModes[0] = fImageTileModes[1] = SkShader::kClamp_TileMode; 1109 1110 fType = shader.asAGradient(&fInfo); 1111 1112 if (fType == SkShader::kNone_GradientType) { 1113 SkShader::BitmapType bitmapType; 1114 SkMatrix matrix; 1115 bitmapType = shader.asABitmap(&fImage, &matrix, fImageTileModes); 1116 if (bitmapType != SkShader::kDefault_BitmapType) { 1117 // Generic fallback for unsupported shaders: 1118 // * allocate a bbox-sized bitmap 1119 // * shade the whole area 1120 // * use the result as a bitmap shader 1121 1122 // bbox is in device space. While that's exactly what we want for sizing our bitmap, 1123 // we need to map it into shader space for adjustments (to match 1124 // SkPDFImageShader::Create's behavior). 1125 SkRect shaderRect = SkRect::Make(bbox); 1126 if (!inverse_transform_bbox(canvasTransform, &shaderRect)) { 1127 fImage.reset(); 1128 return; 1129 } 1130 1131 // Clamp the bitmap size to about 1M pixels 1132 static const SkScalar kMaxBitmapArea = 1024 * 1024; 1133 SkScalar bitmapArea = rasterScale * bbox.width() * rasterScale * bbox.height(); 1134 if (bitmapArea > kMaxBitmapArea) { 1135 rasterScale *= SkScalarSqrt(kMaxBitmapArea / bitmapArea); 1136 } 1137 1138 SkISize size = SkISize::Make(SkScalarRoundToInt(rasterScale * bbox.width()), 1139 SkScalarRoundToInt(rasterScale * bbox.height())); 1140 SkSize scale = SkSize::Make(SkIntToScalar(size.width()) / shaderRect.width(), 1141 SkIntToScalar(size.height()) / shaderRect.height()); 1142 1143 fImage.allocN32Pixels(size.width(), size.height()); 1144 fImage.eraseColor(SK_ColorTRANSPARENT); 1145 1146 SkPaint p; 1147 p.setShader(const_cast<SkShader*>(&shader)); 1148 1149 SkCanvas canvas(fImage); 1150 canvas.scale(scale.width(), scale.height()); 1151 canvas.translate(-shaderRect.x(), -shaderRect.y()); 1152 canvas.drawPaint(p); 1153 1154 fShaderTransform.setTranslate(shaderRect.x(), shaderRect.y()); 1155 fShaderTransform.preScale(1 / scale.width(), 1 / scale.height()); 1156 } else { 1157 SkASSERT(matrix.isIdentity()); 1158 } 1159 fPixelGeneration = fImage.getGenerationID(); 1160 } else { 1161 AllocateGradientInfoStorage(); 1162 shader.asAGradient(&fInfo); 1163 } 1164} 1165 1166SkPDFShader::State::State(const SkPDFShader::State& other) 1167 : fType(other.fType), 1168 fCanvasTransform(other.fCanvasTransform), 1169 fShaderTransform(other.fShaderTransform), 1170 fBBox(other.fBBox) 1171{ 1172 // Only gradients supported for now, since that is all that is used. 1173 // If needed, image state copy constructor can be added here later. 1174 SkASSERT(fType != SkShader::kNone_GradientType); 1175 1176 if (fType != SkShader::kNone_GradientType) { 1177 fInfo = other.fInfo; 1178 1179 AllocateGradientInfoStorage(); 1180 for (int i = 0; i < fInfo.fColorCount; i++) { 1181 fInfo.fColors[i] = other.fInfo.fColors[i]; 1182 fInfo.fColorOffsets[i] = other.fInfo.fColorOffsets[i]; 1183 } 1184 } 1185} 1186 1187/** 1188 * Create a copy of this gradient state with alpha assigned to RGB luminousity. 1189 * Only valid for gradient states. 1190 */ 1191SkPDFShader::State* SkPDFShader::State::CreateAlphaToLuminosityState() const { 1192 SkASSERT(fType != SkShader::kNone_GradientType); 1193 1194 SkPDFShader::State* newState = new SkPDFShader::State(*this); 1195 1196 for (int i = 0; i < fInfo.fColorCount; i++) { 1197 SkAlpha alpha = SkColorGetA(fInfo.fColors[i]); 1198 newState->fInfo.fColors[i] = SkColorSetARGB(255, alpha, alpha, alpha); 1199 } 1200 1201 return newState; 1202} 1203 1204/** 1205 * Create a copy of this gradient state with alpha set to fully opaque 1206 * Only valid for gradient states. 1207 */ 1208SkPDFShader::State* SkPDFShader::State::CreateOpaqueState() const { 1209 SkASSERT(fType != SkShader::kNone_GradientType); 1210 1211 SkPDFShader::State* newState = new SkPDFShader::State(*this); 1212 for (int i = 0; i < fInfo.fColorCount; i++) { 1213 newState->fInfo.fColors[i] = SkColorSetA(fInfo.fColors[i], 1214 SK_AlphaOPAQUE); 1215 } 1216 1217 return newState; 1218} 1219 1220/** 1221 * Returns true if state is a gradient and the gradient has alpha. 1222 */ 1223bool SkPDFShader::State::GradientHasAlpha() const { 1224 if (fType == SkShader::kNone_GradientType) { 1225 return false; 1226 } 1227 1228 for (int i = 0; i < fInfo.fColorCount; i++) { 1229 SkAlpha alpha = SkColorGetA(fInfo.fColors[i]); 1230 if (alpha != SK_AlphaOPAQUE) { 1231 return true; 1232 } 1233 } 1234 return false; 1235} 1236 1237void SkPDFShader::State::AllocateGradientInfoStorage() { 1238 fColorData.set(sk_malloc_throw( 1239 fInfo.fColorCount * (sizeof(SkColor) + sizeof(SkScalar)))); 1240 fInfo.fColors = reinterpret_cast<SkColor*>(fColorData.get()); 1241 fInfo.fColorOffsets = 1242 reinterpret_cast<SkScalar*>(fInfo.fColors + fInfo.fColorCount); 1243} 1244