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