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 "SkPDFTypes.h" 17#include "SkPDFUtils.h" 18#include "SkScalar.h" 19#include "SkStream.h" 20#include "SkTemplates.h" 21#include "SkThread.h" 22#include "SkTypes.h" 23 24static bool transformBBox(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->preTranslate(pts[0].fX, pts[0].fY); 41 matrix->preScale(mag, mag); 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. 47 @param range endOffset - startOffset 48 @param curColor[components] The current color components. 49 @param prevColor[components] The previous color components. 50 @param result The result ps function. 51 */ 52static void interpolateColorCode(SkScalar range, SkScalar* curColor, 53 SkScalar* prevColor, int components, 54 SkString* result) { 55 // Figure out how to scale each color component. 56 SkAutoSTMalloc<4, SkScalar> multiplierAlloc(components); 57 SkScalar *multiplier = multiplierAlloc.get(); 58 for (int i = 0; i < components; i++) { 59 multiplier[i] = SkScalarDiv(curColor[i] - prevColor[i], range); 60 } 61 62 // Calculate when we no longer need to keep a copy of the input parameter t. 63 // If the last component to use t is i, then dupInput[0..i - 1] = true 64 // and dupInput[i .. components] = false. 65 SkAutoSTMalloc<4, bool> dupInputAlloc(components); 66 bool *dupInput = dupInputAlloc.get(); 67 dupInput[components - 1] = false; 68 for (int i = components - 2; i >= 0; i--) { 69 dupInput[i] = dupInput[i + 1] || multiplier[i + 1] != 0; 70 } 71 72 if (!dupInput[0] && multiplier[0] == 0) { 73 result->append("pop "); 74 } 75 76 for (int i = 0; i < components; i++) { 77 // If the next components needs t and this component will consume a 78 // copy, make another copy. 79 if (dupInput[i] && multiplier[i] != 0) { 80 result->append("dup "); 81 } 82 83 if (multiplier[i] == 0) { 84 result->appendScalar(prevColor[i]); 85 result->append(" "); 86 } else { 87 if (multiplier[i] != 1) { 88 result->appendScalar(multiplier[i]); 89 result->append(" mul "); 90 } 91 if (prevColor[i] != 0) { 92 result->appendScalar(prevColor[i]); 93 result->append(" add "); 94 } 95 } 96 97 if (dupInput[i]) { 98 result->append("exch\n"); 99 } 100 } 101} 102 103/* Generate Type 4 function code to map t=[0,1) to the passed gradient, 104 clamping at the edges of the range. The generated code will be of the form: 105 if (t < 0) { 106 return colorData[0][r,g,b]; 107 } else { 108 if (t < info.fColorOffsets[1]) { 109 return linearinterpolation(colorData[0][r,g,b], 110 colorData[1][r,g,b]); 111 } else { 112 if (t < info.fColorOffsets[2]) { 113 return linearinterpolation(colorData[1][r,g,b], 114 colorData[2][r,g,b]); 115 } else { 116 117 ... } else { 118 return colorData[info.fColorCount - 1][r,g,b]; 119 } 120 ... 121 } 122 } 123 */ 124static void gradientFunctionCode(const SkShader::GradientInfo& info, 125 SkString* result) { 126 /* We want to linearly interpolate from the previous color to the next. 127 Scale the colors from 0..255 to 0..1 and determine the multipliers 128 for interpolation. 129 C{r,g,b}(t, section) = t - offset_(section-1) + t * Multiplier{r,g,b}. 130 */ 131 static const int kColorComponents = 3; 132 typedef SkScalar ColorTuple[kColorComponents]; 133 SkAutoSTMalloc<4, ColorTuple> colorDataAlloc(info.fColorCount); 134 ColorTuple *colorData = colorDataAlloc.get(); 135 const SkScalar scale = SkScalarInvert(SkIntToScalar(255)); 136 for (int i = 0; i < info.fColorCount; i++) { 137 colorData[i][0] = SkScalarMul(SkColorGetR(info.fColors[i]), scale); 138 colorData[i][1] = SkScalarMul(SkColorGetG(info.fColors[i]), scale); 139 colorData[i][2] = SkScalarMul(SkColorGetB(info.fColors[i]), scale); 140 } 141 142 // Clamp the initial color. 143 result->append("dup 0 le {pop "); 144 result->appendScalar(colorData[0][0]); 145 result->append(" "); 146 result->appendScalar(colorData[0][1]); 147 result->append(" "); 148 result->appendScalar(colorData[0][2]); 149 result->append(" }\n"); 150 151 // The gradient colors. 152 for (int i = 1 ; i < info.fColorCount; i++) { 153 result->append("{dup "); 154 result->appendScalar(info.fColorOffsets[i]); 155 result->append(" le {"); 156 if (info.fColorOffsets[i - 1] != 0) { 157 result->appendScalar(info.fColorOffsets[i - 1]); 158 result->append(" sub\n"); 159 } 160 161 interpolateColorCode(info.fColorOffsets[i] - info.fColorOffsets[i - 1], 162 colorData[i], colorData[i - 1], kColorComponents, 163 result); 164 result->append("}\n"); 165 } 166 167 // Clamp the final color. 168 result->append("{pop "); 169 result->appendScalar(colorData[info.fColorCount - 1][0]); 170 result->append(" "); 171 result->appendScalar(colorData[info.fColorCount - 1][1]); 172 result->append(" "); 173 result->appendScalar(colorData[info.fColorCount - 1][2]); 174 175 for (int i = 0 ; i < info.fColorCount; i++) { 176 result->append("} ifelse\n"); 177 } 178} 179 180/* Map a value of t on the stack into [0, 1) for Repeat or Mirror tile mode. */ 181static void tileModeCode(SkShader::TileMode mode, SkString* result) { 182 if (mode == SkShader::kRepeat_TileMode) { 183 result->append("dup truncate sub\n"); // Get the fractional part. 184 result->append("dup 0 le {1 add} if\n"); // Map (-1,0) => (0,1) 185 return; 186 } 187 188 if (mode == SkShader::kMirror_TileMode) { 189 // Map t mod 2 into [0, 1, 1, 0]. 190 // Code Stack 191 result->append("abs " // Map negative to positive. 192 "dup " // t.s t.s 193 "truncate " // t.s t 194 "dup " // t.s t t 195 "cvi " // t.s t T 196 "2 mod " // t.s t (i mod 2) 197 "1 eq " // t.s t true|false 198 "3 1 roll " // true|false t.s t 199 "sub " // true|false 0.s 200 "exch " // 0.s true|false 201 "{1 exch sub} if\n"); // 1 - 0.s|0.s 202 } 203} 204 205static SkString linearCode(const SkShader::GradientInfo& info) { 206 SkString function("{pop\n"); // Just ditch the y value. 207 tileModeCode(info.fTileMode, &function); 208 gradientFunctionCode(info, &function); 209 function.append("}"); 210 return function; 211} 212 213static SkString radialCode(const SkShader::GradientInfo& info) { 214 SkString function("{"); 215 // Find the distance from the origin. 216 function.append("dup " // x y y 217 "mul " // x y^2 218 "exch " // y^2 x 219 "dup " // y^2 x x 220 "mul " // y^2 x^2 221 "add " // y^2+x^2 222 "sqrt\n"); // sqrt(y^2+x^2) 223 224 tileModeCode(info.fTileMode, &function); 225 gradientFunctionCode(info, &function); 226 function.append("}"); 227 return function; 228} 229 230/* The math here is all based on the description in Two_Point_Radial_Gradient, 231 with one simplification, the coordinate space has been scaled so that 232 Dr = 1. This means we don't need to scale the entire equation by 1/Dr^2. 233 */ 234static SkString twoPointRadialCode(const SkShader::GradientInfo& info) { 235 SkScalar dx = info.fPoint[0].fX - info.fPoint[1].fX; 236 SkScalar dy = info.fPoint[0].fY - info.fPoint[1].fY; 237 SkScalar sr = info.fRadius[0]; 238 SkScalar a = SkScalarMul(dx, dx) + SkScalarMul(dy, dy) - SK_Scalar1; 239 bool posRoot = info.fRadius[1] > info.fRadius[0]; 240 241 // We start with a stack of (x y), copy it and then consume one copy in 242 // order to calculate b and the other to calculate c. 243 SkString function("{"); 244 function.append("2 copy "); 245 246 // Calculate -b and b^2. 247 function.appendScalar(dy); 248 function.append(" mul exch "); 249 function.appendScalar(dx); 250 function.append(" mul add "); 251 function.appendScalar(sr); 252 function.append(" sub 2 mul neg dup dup mul\n"); 253 254 // Calculate c 255 function.append("4 2 roll dup mul exch dup mul add "); 256 function.appendScalar(SkScalarMul(sr, sr)); 257 function.append(" sub\n"); 258 259 // Calculate the determinate 260 function.appendScalar(SkScalarMul(SkIntToScalar(4), a)); 261 function.append(" mul sub abs sqrt\n"); 262 263 // And then the final value of t. 264 if (posRoot) { 265 function.append("sub "); 266 } else { 267 function.append("add "); 268 } 269 function.appendScalar(SkScalarMul(SkIntToScalar(2), a)); 270 function.append(" div\n"); 271 272 tileModeCode(info.fTileMode, &function); 273 gradientFunctionCode(info, &function); 274 function.append("}"); 275 return function; 276} 277 278/* Conical gradient shader, based on the Canvas spec for radial gradients 279 See: http://www.w3.org/TR/2dcontext/#dom-context-2d-createradialgradient 280 */ 281static SkString twoPointConicalCode(const SkShader::GradientInfo& info) { 282 SkScalar dx = info.fPoint[1].fX - info.fPoint[0].fX; 283 SkScalar dy = info.fPoint[1].fY - info.fPoint[0].fY; 284 SkScalar r0 = info.fRadius[0]; 285 SkScalar dr = info.fRadius[1] - info.fRadius[0]; 286 SkScalar a = SkScalarMul(dx, dx) + SkScalarMul(dy, dy) - 287 SkScalarMul(dr, dr); 288 289 // First compute t, if the pixel falls outside the cone, then we'll end 290 // with 'false' on the stack, otherwise we'll push 'true' with t below it 291 292 // We start with a stack of (x y), copy it and then consume one copy in 293 // order to calculate b and the other to calculate c. 294 SkString function("{"); 295 function.append("2 copy "); 296 297 // Calculate b and b^2; b = -2 * (y * dy + x * dx + r0 * dr). 298 function.appendScalar(dy); 299 function.append(" mul exch "); 300 function.appendScalar(dx); 301 function.append(" mul add "); 302 function.appendScalar(SkScalarMul(r0, dr)); 303 function.append(" add -2 mul dup dup mul\n"); 304 305 // c = x^2 + y^2 + radius0^2 306 function.append("4 2 roll dup mul exch dup mul add "); 307 function.appendScalar(SkScalarMul(r0, r0)); 308 function.append(" sub dup 4 1 roll\n"); 309 310 // Contents of the stack at this point: c, b, b^2, c 311 312 // if a = 0, then we collapse to a simpler linear case 313 if (a == 0) { 314 315 // t = -c/b 316 function.append("pop pop div neg dup "); 317 318 // compute radius(t) 319 function.appendScalar(dr); 320 function.append(" mul "); 321 function.appendScalar(r0); 322 function.append(" add\n"); 323 324 // if r(t) < 0, then it's outside the cone 325 function.append("0 lt {pop false} {true} ifelse\n"); 326 327 } else { 328 329 // quadratic case: the Canvas spec wants the largest 330 // root t for which radius(t) > 0 331 332 // compute the discriminant (b^2 - 4ac) 333 function.appendScalar(SkScalarMul(SkIntToScalar(4), a)); 334 function.append(" mul sub dup\n"); 335 336 // if d >= 0, proceed 337 function.append("0 ge {\n"); 338 339 // an intermediate value we'll use to compute the roots: 340 // q = -0.5 * (b +/- sqrt(d)) 341 function.append("sqrt exch dup 0 lt {exch -1 mul} if"); 342 function.append(" add -0.5 mul dup\n"); 343 344 // first root = q / a 345 function.appendScalar(a); 346 function.append(" div\n"); 347 348 // second root = c / q 349 function.append("3 1 roll div\n"); 350 351 // put the larger root on top of the stack 352 function.append("2 copy gt {exch} if\n"); 353 354 // compute radius(t) for larger root 355 function.append("dup "); 356 function.appendScalar(dr); 357 function.append(" mul "); 358 function.appendScalar(r0); 359 function.append(" add\n"); 360 361 // if r(t) > 0, we have our t, pop off the smaller root and we're done 362 function.append(" 0 gt {exch pop true}\n"); 363 364 // otherwise, throw out the larger one and try the smaller root 365 function.append("{pop dup\n"); 366 function.appendScalar(dr); 367 function.append(" mul "); 368 function.appendScalar(r0); 369 function.append(" add\n"); 370 371 // if r(t) < 0, push false, otherwise the smaller root is our t 372 function.append("0 le {pop false} {true} ifelse\n"); 373 function.append("} ifelse\n"); 374 375 // d < 0, clear the stack and push false 376 function.append("} {pop pop pop false} ifelse\n"); 377 } 378 379 // if the pixel is in the cone, proceed to compute a color 380 function.append("{"); 381 tileModeCode(info.fTileMode, &function); 382 gradientFunctionCode(info, &function); 383 384 // otherwise, just write black 385 function.append("} {0 0 0} ifelse }"); 386 387 return function; 388} 389 390static SkString sweepCode(const SkShader::GradientInfo& info) { 391 SkString function("{exch atan 360 div\n"); 392 tileModeCode(info.fTileMode, &function); 393 gradientFunctionCode(info, &function); 394 function.append("}"); 395 return function; 396} 397 398class SkPDFShader::State { 399public: 400 SkShader::GradientType fType; 401 SkShader::GradientInfo fInfo; 402 SkAutoFree fColorData; 403 SkMatrix fCanvasTransform; 404 SkMatrix fShaderTransform; 405 SkIRect fBBox; 406 407 SkBitmap fImage; 408 uint32_t fPixelGeneration; 409 SkShader::TileMode fImageTileModes[2]; 410 411 explicit State(const SkShader& shader, const SkMatrix& canvasTransform, 412 const SkIRect& bbox); 413 bool operator==(const State& b) const; 414}; 415 416class SkPDFFunctionShader : public SkPDFDict, public SkPDFShader { 417public: 418 explicit SkPDFFunctionShader(SkPDFShader::State* state); 419 virtual ~SkPDFFunctionShader() { 420 if (isValid()) { 421 RemoveShader(this); 422 } 423 fResources.unrefAll(); 424 } 425 426 virtual bool isValid() { return fResources.count() > 0; } 427 428 void getResources(SkTDArray<SkPDFObject*>* resourceList) { 429 GetResourcesHelper(&fResources, resourceList); 430 } 431 432private: 433 static SkPDFObject* RangeObject(); 434 435 SkTDArray<SkPDFObject*> fResources; 436 SkAutoTDelete<const SkPDFShader::State> fState; 437 438 SkPDFStream* makePSFunction(const SkString& psCode, SkPDFArray* domain); 439}; 440 441class SkPDFImageShader : public SkPDFStream, public SkPDFShader { 442public: 443 explicit SkPDFImageShader(SkPDFShader::State* state); 444 virtual ~SkPDFImageShader() { 445 RemoveShader(this); 446 fResources.unrefAll(); 447 } 448 449 virtual bool isValid() { return size() > 0; } 450 451 void getResources(SkTDArray<SkPDFObject*>* resourceList) { 452 GetResourcesHelper(&fResources, resourceList); 453 } 454 455private: 456 SkTDArray<SkPDFObject*> fResources; 457 SkAutoTDelete<const SkPDFShader::State> fState; 458}; 459 460SkPDFShader::SkPDFShader() {} 461 462// static 463void SkPDFShader::RemoveShader(SkPDFObject* shader) { 464 SkAutoMutexAcquire lock(CanonicalShadersMutex()); 465 ShaderCanonicalEntry entry(shader, NULL); 466 int index = CanonicalShaders().find(entry); 467 SkASSERT(index >= 0); 468 CanonicalShaders().removeShuffle(index); 469} 470 471// static 472SkPDFObject* SkPDFShader::GetPDFShader(const SkShader& shader, 473 const SkMatrix& matrix, 474 const SkIRect& surfaceBBox) { 475 SkPDFObject* result; 476 SkAutoMutexAcquire lock(CanonicalShadersMutex()); 477 SkAutoTDelete<State> shaderState(new State(shader, matrix, surfaceBBox)); 478 if (shaderState.get()->fType == SkShader::kNone_GradientType && 479 shaderState.get()->fImage.isNull()) { 480 // TODO(vandebo) This drops SKComposeShader on the floor. We could 481 // handle compose shader by pulling things up to a layer, drawing with 482 // the first shader, applying the xfer mode and drawing again with the 483 // second shader, then applying the layer to the original drawing. 484 return NULL; 485 } 486 487 ShaderCanonicalEntry entry(NULL, shaderState.get()); 488 int index = CanonicalShaders().find(entry); 489 if (index >= 0) { 490 result = CanonicalShaders()[index].fPDFShader; 491 result->ref(); 492 return result; 493 } 494 495 bool valid = false; 496 // The PDFShader takes ownership of the shaderSate. 497 if (shaderState.get()->fType == SkShader::kNone_GradientType) { 498 SkPDFImageShader* imageShader = 499 new SkPDFImageShader(shaderState.detach()); 500 valid = imageShader->isValid(); 501 result = imageShader; 502 } else { 503 SkPDFFunctionShader* functionShader = 504 new SkPDFFunctionShader(shaderState.detach()); 505 valid = functionShader->isValid(); 506 result = functionShader; 507 } 508 if (!valid) { 509 delete result; 510 return NULL; 511 } 512 entry.fPDFShader = result; 513 CanonicalShaders().push(entry); 514 return result; // return the reference that came from new. 515} 516 517// static 518SkTDArray<SkPDFShader::ShaderCanonicalEntry>& SkPDFShader::CanonicalShaders() { 519 // This initialization is only thread safe with gcc. 520 static SkTDArray<ShaderCanonicalEntry> gCanonicalShaders; 521 return gCanonicalShaders; 522} 523 524// static 525SkBaseMutex& SkPDFShader::CanonicalShadersMutex() { 526 // This initialization is only thread safe with gcc or when 527 // POD-style mutex initialization is used. 528 SK_DECLARE_STATIC_MUTEX(gCanonicalShadersMutex); 529 return gCanonicalShadersMutex; 530} 531 532// static 533SkPDFObject* SkPDFFunctionShader::RangeObject() { 534 // This initialization is only thread safe with gcc. 535 static SkPDFArray* range = NULL; 536 // This method is only used with CanonicalShadersMutex, so it's safe to 537 // populate domain. 538 if (range == NULL) { 539 range = new SkPDFArray; 540 range->reserve(6); 541 range->appendInt(0); 542 range->appendInt(1); 543 range->appendInt(0); 544 range->appendInt(1); 545 range->appendInt(0); 546 range->appendInt(1); 547 } 548 return range; 549} 550 551SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state) 552 : SkPDFDict("Pattern"), 553 fState(state) { 554 SkString (*codeFunction)(const SkShader::GradientInfo& info) = NULL; 555 SkPoint transformPoints[2]; 556 557 // Depending on the type of the gradient, we want to transform the 558 // coordinate space in different ways. 559 const SkShader::GradientInfo* info = &fState.get()->fInfo; 560 transformPoints[0] = info->fPoint[0]; 561 transformPoints[1] = info->fPoint[1]; 562 switch (fState.get()->fType) { 563 case SkShader::kLinear_GradientType: 564 codeFunction = &linearCode; 565 break; 566 case SkShader::kRadial_GradientType: 567 transformPoints[1] = transformPoints[0]; 568 transformPoints[1].fX += info->fRadius[0]; 569 codeFunction = &radialCode; 570 break; 571 case SkShader::kRadial2_GradientType: { 572 // Bail out if the radii are the same. Empty fResources signals 573 // an error and isValid will return false. 574 if (info->fRadius[0] == info->fRadius[1]) { 575 return; 576 } 577 transformPoints[1] = transformPoints[0]; 578 SkScalar dr = info->fRadius[1] - info->fRadius[0]; 579 transformPoints[1].fX += dr; 580 codeFunction = &twoPointRadialCode; 581 break; 582 } 583 case SkShader::kConical_GradientType: { 584 transformPoints[1] = transformPoints[0]; 585 transformPoints[1].fX += SK_Scalar1; 586 codeFunction = &twoPointConicalCode; 587 break; 588 } 589 case SkShader::kSweep_GradientType: 590 transformPoints[1] = transformPoints[0]; 591 transformPoints[1].fX += SK_Scalar1; 592 codeFunction = &sweepCode; 593 break; 594 case SkShader::kColor_GradientType: 595 case SkShader::kNone_GradientType: 596 default: 597 return; 598 } 599 600 // Move any scaling (assuming a unit gradient) or translation 601 // (and rotation for linear gradient), of the final gradient from 602 // info->fPoints to the matrix (updating bbox appropriately). Now 603 // the gradient can be drawn on on the unit segment. 604 SkMatrix mapperMatrix; 605 unitToPointsMatrix(transformPoints, &mapperMatrix); 606 SkMatrix finalMatrix = fState.get()->fCanvasTransform; 607 finalMatrix.preConcat(mapperMatrix); 608 finalMatrix.preConcat(fState.get()->fShaderTransform); 609 SkRect bbox; 610 bbox.set(fState.get()->fBBox); 611 if (!transformBBox(finalMatrix, &bbox)) { 612 return; 613 } 614 615 SkAutoTUnref<SkPDFArray> domain(new SkPDFArray); 616 domain->reserve(4); 617 domain->appendScalar(bbox.fLeft); 618 domain->appendScalar(bbox.fRight); 619 domain->appendScalar(bbox.fTop); 620 domain->appendScalar(bbox.fBottom); 621 622 SkString functionCode; 623 // The two point radial gradient further references fState.get()->fInfo 624 // in translating from x, y coordinates to the t parameter. So, we have 625 // to transform the points and radii according to the calculated matrix. 626 if (fState.get()->fType == SkShader::kRadial2_GradientType) { 627 SkShader::GradientInfo twoPointRadialInfo = *info; 628 SkMatrix inverseMapperMatrix; 629 if (!mapperMatrix.invert(&inverseMapperMatrix)) { 630 return; 631 } 632 inverseMapperMatrix.mapPoints(twoPointRadialInfo.fPoint, 2); 633 twoPointRadialInfo.fRadius[0] = 634 inverseMapperMatrix.mapRadius(info->fRadius[0]); 635 twoPointRadialInfo.fRadius[1] = 636 inverseMapperMatrix.mapRadius(info->fRadius[1]); 637 functionCode = codeFunction(twoPointRadialInfo); 638 } else { 639 functionCode = codeFunction(*info); 640 } 641 642 SkAutoTUnref<SkPDFDict> pdfShader(new SkPDFDict); 643 pdfShader->insertInt("ShadingType", 1); 644 pdfShader->insertName("ColorSpace", "DeviceRGB"); 645 pdfShader->insert("Domain", domain.get()); 646 647 SkPDFStream* function = makePSFunction(functionCode, domain.get()); 648 pdfShader->insert("Function", new SkPDFObjRef(function))->unref(); 649 fResources.push(function); // Pass ownership to resource list. 650 651 insertInt("PatternType", 2); 652 insert("Matrix", SkPDFUtils::MatrixToArray(finalMatrix))->unref(); 653 insert("Shading", pdfShader.get()); 654} 655 656SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) : fState(state) { 657 fState.get()->fImage.lockPixels(); 658 659 SkMatrix finalMatrix = fState.get()->fCanvasTransform; 660 finalMatrix.preConcat(fState.get()->fShaderTransform); 661 SkRect surfaceBBox; 662 surfaceBBox.set(fState.get()->fBBox); 663 if (!transformBBox(finalMatrix, &surfaceBBox)) { 664 return; 665 } 666 667 SkMatrix unflip; 668 unflip.setTranslate(0, SkScalarRoundToScalar(surfaceBBox.height())); 669 unflip.preScale(SK_Scalar1, -SK_Scalar1); 670 SkISize size = SkISize::Make(SkScalarRound(surfaceBBox.width()), 671 SkScalarRound(surfaceBBox.height())); 672 SkPDFDevice pattern(size, size, unflip); 673 SkCanvas canvas(&pattern); 674 canvas.translate(-surfaceBBox.fLeft, -surfaceBBox.fTop); 675 finalMatrix.preTranslate(surfaceBBox.fLeft, surfaceBBox.fTop); 676 677 const SkBitmap* image = &fState.get()->fImage; 678 SkScalar width = SkIntToScalar(image->width()); 679 SkScalar height = SkIntToScalar(image->height()); 680 SkShader::TileMode tileModes[2]; 681 tileModes[0] = fState.get()->fImageTileModes[0]; 682 tileModes[1] = fState.get()->fImageTileModes[1]; 683 684 canvas.drawBitmap(*image, 0, 0); 685 SkRect patternBBox = SkRect::MakeXYWH(-surfaceBBox.fLeft, -surfaceBBox.fTop, 686 width, height); 687 688 // Tiling is implied. First we handle mirroring. 689 if (tileModes[0] == SkShader::kMirror_TileMode) { 690 SkMatrix xMirror; 691 xMirror.setScale(-1, 1); 692 xMirror.postTranslate(2 * width, 0); 693 canvas.drawBitmapMatrix(*image, xMirror); 694 patternBBox.fRight += width; 695 } 696 if (tileModes[1] == SkShader::kMirror_TileMode) { 697 SkMatrix yMirror; 698 yMirror.setScale(SK_Scalar1, -SK_Scalar1); 699 yMirror.postTranslate(0, 2 * height); 700 canvas.drawBitmapMatrix(*image, yMirror); 701 patternBBox.fBottom += height; 702 } 703 if (tileModes[0] == SkShader::kMirror_TileMode && 704 tileModes[1] == SkShader::kMirror_TileMode) { 705 SkMatrix mirror; 706 mirror.setScale(-1, -1); 707 mirror.postTranslate(2 * width, 2 * height); 708 canvas.drawBitmapMatrix(*image, mirror); 709 } 710 711 // Then handle Clamping, which requires expanding the pattern canvas to 712 // cover the entire surfaceBBox. 713 714 // If both x and y are in clamp mode, we start by filling in the corners. 715 // (Which are just a rectangles of the corner colors.) 716 if (tileModes[0] == SkShader::kClamp_TileMode && 717 tileModes[1] == SkShader::kClamp_TileMode) { 718 SkPaint paint; 719 SkRect rect; 720 rect = SkRect::MakeLTRB(surfaceBBox.fLeft, surfaceBBox.fTop, 0, 0); 721 if (!rect.isEmpty()) { 722 paint.setColor(image->getColor(0, 0)); 723 canvas.drawRect(rect, paint); 724 } 725 726 rect = SkRect::MakeLTRB(width, surfaceBBox.fTop, surfaceBBox.fRight, 0); 727 if (!rect.isEmpty()) { 728 paint.setColor(image->getColor(image->width() - 1, 0)); 729 canvas.drawRect(rect, paint); 730 } 731 732 rect = SkRect::MakeLTRB(width, height, surfaceBBox.fRight, 733 surfaceBBox.fBottom); 734 if (!rect.isEmpty()) { 735 paint.setColor(image->getColor(image->width() - 1, 736 image->height() - 1)); 737 canvas.drawRect(rect, paint); 738 } 739 740 rect = SkRect::MakeLTRB(surfaceBBox.fLeft, height, 0, 741 surfaceBBox.fBottom); 742 if (!rect.isEmpty()) { 743 paint.setColor(image->getColor(0, image->height() - 1)); 744 canvas.drawRect(rect, paint); 745 } 746 } 747 748 // Then expand the left, right, top, then bottom. 749 if (tileModes[0] == SkShader::kClamp_TileMode) { 750 SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, image->height()); 751 if (surfaceBBox.fLeft < 0) { 752 SkBitmap left; 753 SkAssertResult(image->extractSubset(&left, subset)); 754 755 SkMatrix leftMatrix; 756 leftMatrix.setScale(-surfaceBBox.fLeft, 1); 757 leftMatrix.postTranslate(surfaceBBox.fLeft, 0); 758 canvas.drawBitmapMatrix(left, leftMatrix); 759 760 if (tileModes[1] == SkShader::kMirror_TileMode) { 761 leftMatrix.postScale(SK_Scalar1, -SK_Scalar1); 762 leftMatrix.postTranslate(0, 2 * height); 763 canvas.drawBitmapMatrix(left, leftMatrix); 764 } 765 patternBBox.fLeft = 0; 766 } 767 768 if (surfaceBBox.fRight > width) { 769 SkBitmap right; 770 subset.offset(image->width() - 1, 0); 771 SkAssertResult(image->extractSubset(&right, subset)); 772 773 SkMatrix rightMatrix; 774 rightMatrix.setScale(surfaceBBox.fRight - width, 1); 775 rightMatrix.postTranslate(width, 0); 776 canvas.drawBitmapMatrix(right, rightMatrix); 777 778 if (tileModes[1] == SkShader::kMirror_TileMode) { 779 rightMatrix.postScale(SK_Scalar1, -SK_Scalar1); 780 rightMatrix.postTranslate(0, 2 * height); 781 canvas.drawBitmapMatrix(right, rightMatrix); 782 } 783 patternBBox.fRight = surfaceBBox.width(); 784 } 785 } 786 787 if (tileModes[1] == SkShader::kClamp_TileMode) { 788 SkIRect subset = SkIRect::MakeXYWH(0, 0, image->width(), 1); 789 if (surfaceBBox.fTop < 0) { 790 SkBitmap top; 791 SkAssertResult(image->extractSubset(&top, subset)); 792 793 SkMatrix topMatrix; 794 topMatrix.setScale(SK_Scalar1, -surfaceBBox.fTop); 795 topMatrix.postTranslate(0, surfaceBBox.fTop); 796 canvas.drawBitmapMatrix(top, topMatrix); 797 798 if (tileModes[0] == SkShader::kMirror_TileMode) { 799 topMatrix.postScale(-1, 1); 800 topMatrix.postTranslate(2 * width, 0); 801 canvas.drawBitmapMatrix(top, topMatrix); 802 } 803 patternBBox.fTop = 0; 804 } 805 806 if (surfaceBBox.fBottom > height) { 807 SkBitmap bottom; 808 subset.offset(0, image->height() - 1); 809 SkAssertResult(image->extractSubset(&bottom, subset)); 810 811 SkMatrix bottomMatrix; 812 bottomMatrix.setScale(SK_Scalar1, surfaceBBox.fBottom - height); 813 bottomMatrix.postTranslate(0, height); 814 canvas.drawBitmapMatrix(bottom, bottomMatrix); 815 816 if (tileModes[0] == SkShader::kMirror_TileMode) { 817 bottomMatrix.postScale(-1, 1); 818 bottomMatrix.postTranslate(2 * width, 0); 819 canvas.drawBitmapMatrix(bottom, bottomMatrix); 820 } 821 patternBBox.fBottom = surfaceBBox.height(); 822 } 823 } 824 825 SkAutoTUnref<SkPDFArray> patternBBoxArray(new SkPDFArray); 826 patternBBoxArray->reserve(4); 827 patternBBoxArray->appendScalar(patternBBox.fLeft); 828 patternBBoxArray->appendScalar(patternBBox.fTop); 829 patternBBoxArray->appendScalar(patternBBox.fRight); 830 patternBBoxArray->appendScalar(patternBBox.fBottom); 831 832 // Put the canvas into the pattern stream (fContent). 833 SkAutoTUnref<SkStream> content(pattern.content()); 834 setData(content.get()); 835 pattern.getResources(&fResources, false); 836 837 insertName("Type", "Pattern"); 838 insertInt("PatternType", 1); 839 insertInt("PaintType", 1); 840 insertInt("TilingType", 1); 841 insert("BBox", patternBBoxArray.get()); 842 insertScalar("XStep", patternBBox.width()); 843 insertScalar("YStep", patternBBox.height()); 844 insert("Resources", pattern.getResourceDict()); 845 insert("Matrix", SkPDFUtils::MatrixToArray(finalMatrix))->unref(); 846 847 fState.get()->fImage.unlockPixels(); 848} 849 850SkPDFStream* SkPDFFunctionShader::makePSFunction(const SkString& psCode, 851 SkPDFArray* domain) { 852 SkAutoDataUnref funcData(SkData::NewWithCopy(psCode.c_str(), 853 psCode.size())); 854 SkPDFStream* result = new SkPDFStream(funcData.get()); 855 result->insertInt("FunctionType", 4); 856 result->insert("Domain", domain); 857 result->insert("Range", RangeObject()); 858 return result; 859} 860 861SkPDFShader::ShaderCanonicalEntry::ShaderCanonicalEntry(SkPDFObject* pdfShader, 862 const State* state) 863 : fPDFShader(pdfShader), 864 fState(state) { 865} 866 867bool SkPDFShader::ShaderCanonicalEntry::operator==( 868 const ShaderCanonicalEntry& b) const { 869 return fPDFShader == b.fPDFShader || 870 (fState != NULL && b.fState != NULL && *fState == *b.fState); 871} 872 873bool SkPDFShader::State::operator==(const SkPDFShader::State& b) const { 874 if (fType != b.fType || 875 fCanvasTransform != b.fCanvasTransform || 876 fShaderTransform != b.fShaderTransform || 877 fBBox != b.fBBox) { 878 return false; 879 } 880 881 if (fType == SkShader::kNone_GradientType) { 882 if (fPixelGeneration != b.fPixelGeneration || 883 fPixelGeneration == 0 || 884 fImageTileModes[0] != b.fImageTileModes[0] || 885 fImageTileModes[1] != b.fImageTileModes[1]) { 886 return false; 887 } 888 } else { 889 if (fInfo.fColorCount != b.fInfo.fColorCount || 890 memcmp(fInfo.fColors, b.fInfo.fColors, 891 sizeof(SkColor) * fInfo.fColorCount) != 0 || 892 memcmp(fInfo.fColorOffsets, b.fInfo.fColorOffsets, 893 sizeof(SkScalar) * fInfo.fColorCount) != 0 || 894 fInfo.fPoint[0] != b.fInfo.fPoint[0] || 895 fInfo.fTileMode != b.fInfo.fTileMode) { 896 return false; 897 } 898 899 switch (fType) { 900 case SkShader::kLinear_GradientType: 901 if (fInfo.fPoint[1] != b.fInfo.fPoint[1]) { 902 return false; 903 } 904 break; 905 case SkShader::kRadial_GradientType: 906 if (fInfo.fRadius[0] != b.fInfo.fRadius[0]) { 907 return false; 908 } 909 break; 910 case SkShader::kRadial2_GradientType: 911 case SkShader::kConical_GradientType: 912 if (fInfo.fPoint[1] != b.fInfo.fPoint[1] || 913 fInfo.fRadius[0] != b.fInfo.fRadius[0] || 914 fInfo.fRadius[1] != b.fInfo.fRadius[1]) { 915 return false; 916 } 917 break; 918 case SkShader::kSweep_GradientType: 919 case SkShader::kNone_GradientType: 920 case SkShader::kColor_GradientType: 921 break; 922 } 923 } 924 return true; 925} 926 927SkPDFShader::State::State(const SkShader& shader, 928 const SkMatrix& canvasTransform, const SkIRect& bbox) 929 : fCanvasTransform(canvasTransform), 930 fBBox(bbox), 931 fPixelGeneration(0) { 932 fInfo.fColorCount = 0; 933 fInfo.fColors = NULL; 934 fInfo.fColorOffsets = NULL; 935 fShaderTransform = shader.getLocalMatrix(); 936 fImageTileModes[0] = fImageTileModes[1] = SkShader::kClamp_TileMode; 937 938 fType = shader.asAGradient(&fInfo); 939 940 if (fType == SkShader::kNone_GradientType) { 941 SkShader::BitmapType bitmapType; 942 SkMatrix matrix; 943 bitmapType = shader.asABitmap(&fImage, &matrix, fImageTileModes); 944 if (bitmapType != SkShader::kDefault_BitmapType) { 945 fImage.reset(); 946 return; 947 } 948 SkASSERT(matrix.isIdentity()); 949 fPixelGeneration = fImage.getGenerationID(); 950 } else { 951 fColorData.set(sk_malloc_throw( 952 fInfo.fColorCount * (sizeof(SkColor) + sizeof(SkScalar)))); 953 fInfo.fColors = reinterpret_cast<SkColor*>(fColorData.get()); 954 fInfo.fColorOffsets = 955 reinterpret_cast<SkScalar*>(fInfo.fColors + fInfo.fColorCount); 956 shader.asAGradient(&fInfo); 957 } 958} 959