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