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