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