1ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com
2da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org/*
3ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Copyright 2011 Google Inc.
4da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org *
5ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Use of this source code is governed by a BSD-style license that can be
6ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * found in the LICENSE file.
7da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org */
8da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
9ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com
10da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org#include "SkPDFShader.h"
11da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
12421d6443fbd3a913dfa32b6492c4a2969bc6314bvandebo@chromium.org#include "SkData.h"
13fb62b3d423fa34c672df42f47017dbef087d19e9halcanary#include "SkPDFCanon.h"
14da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org#include "SkPDFDevice.h"
1593a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org#include "SkPDFFormXObject.h"
1693a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org#include "SkPDFGraphicState.h"
1747401354074549d8591da7fa115241766d3ee3d2commit-bot@chromium.org#include "SkPDFResourceDict.h"
18da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org#include "SkPDFUtils.h"
19da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org#include "SkScalar.h"
20da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org#include "SkStream.h"
21316338a4eb54b544409e3a98d97ea0a829aef706twiz@google.com#include "SkTemplates.h"
22da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org#include "SkTypes.h"
23da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
247b4d4c7a4668f771641aaed88b83d22d5efe6873fmalitastatic bool inverse_transform_bbox(const SkMatrix& matrix, SkRect* bbox) {
25da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    SkMatrix inverse;
26b054990307b7338e599a12d9af10eb2058b94051vandebo@chromium.org    if (!matrix.invert(&inverse)) {
27386dfc049baa400c13e4e98727d4c04d0242b7b8vandebo@chromium.org        return false;
28b054990307b7338e599a12d9af10eb2058b94051vandebo@chromium.org    }
29da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    inverse.mapRect(bbox);
30386dfc049baa400c13e4e98727d4c04d0242b7b8vandebo@chromium.org    return true;
31da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org}
32da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
33da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.orgstatic void unitToPointsMatrix(const SkPoint pts[2], SkMatrix* matrix) {
34da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    SkVector    vec = pts[1] - pts[0];
35da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    SkScalar    mag = vec.length();
36da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    SkScalar    inv = mag ? SkScalarInvert(mag) : 0;
37da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
38da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    vec.scale(inv);
39da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    matrix->setSinCos(vec.fY, vec.fX);
40da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    matrix->preScale(mag, mag);
41ace2269edfed9dfd3097597f1698921aaddf80a1commit-bot@chromium.org    matrix->postTranslate(pts[0].fX, pts[0].fY);
42da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org}
43da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
44da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org/* Assumes t + startOffset is on the stack and does a linear interpolation on t
45da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org   between startOffset and endOffset from prevColor to curColor (for each color
4653648ab14e2f6a3cf07bd36b0964db8a0664b46adjsollen@google.com   component), leaving the result in component order on the stack. It assumes
4753648ab14e2f6a3cf07bd36b0964db8a0664b46adjsollen@google.com   there are always 3 components per color.
48da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org   @param range                  endOffset - startOffset
49da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org   @param curColor[components]   The current color components.
50da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org   @param prevColor[components]  The previous color components.
51da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org   @param result                 The result ps function.
52da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org */
53da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.orgstatic void interpolateColorCode(SkScalar range, SkScalar* curColor,
5453648ab14e2f6a3cf07bd36b0964db8a0664b46adjsollen@google.com                                 SkScalar* prevColor, SkString* result) {
558135323f70b38e465761ab3e9817183750e3864dedisonn@google.com    SkASSERT(range != SkIntToScalar(0));
5653648ab14e2f6a3cf07bd36b0964db8a0664b46adjsollen@google.com    static const int kColorComponents = 3;
5753648ab14e2f6a3cf07bd36b0964db8a0664b46adjsollen@google.com
58da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    // Figure out how to scale each color component.
5953648ab14e2f6a3cf07bd36b0964db8a0664b46adjsollen@google.com    SkScalar multiplier[kColorComponents];
6053648ab14e2f6a3cf07bd36b0964db8a0664b46adjsollen@google.com    for (int i = 0; i < kColorComponents; i++) {
6180ea19ca4bdd68c1493666a5fe7e4ce9d43ded8breed        multiplier[i] = (curColor[i] - prevColor[i]) / range;
62da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    }
63da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
64da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    // Calculate when we no longer need to keep a copy of the input parameter t.
65da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    // If the last component to use t is i, then dupInput[0..i - 1] = true
66da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    // and dupInput[i .. components] = false.
6753648ab14e2f6a3cf07bd36b0964db8a0664b46adjsollen@google.com    bool dupInput[kColorComponents];
6853648ab14e2f6a3cf07bd36b0964db8a0664b46adjsollen@google.com    dupInput[kColorComponents - 1] = false;
6953648ab14e2f6a3cf07bd36b0964db8a0664b46adjsollen@google.com    for (int i = kColorComponents - 2; i >= 0; i--) {
70da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        dupInput[i] = dupInput[i + 1] || multiplier[i + 1] != 0;
71da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    }
72da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
73da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    if (!dupInput[0] && multiplier[0] == 0) {
74da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        result->append("pop ");
75da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    }
76da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
7753648ab14e2f6a3cf07bd36b0964db8a0664b46adjsollen@google.com    for (int i = 0; i < kColorComponents; i++) {
78b862204352f1a6028e0bfafd25df1cbca114208bvandebo@chromium.org        // If the next components needs t and this component will consume a
79b862204352f1a6028e0bfafd25df1cbca114208bvandebo@chromium.org        // copy, make another copy.
80b862204352f1a6028e0bfafd25df1cbca114208bvandebo@chromium.org        if (dupInput[i] && multiplier[i] != 0) {
81da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            result->append("dup ");
82da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        }
83da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
84da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        if (multiplier[i] == 0) {
85da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            result->appendScalar(prevColor[i]);
86da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            result->append(" ");
87da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        } else {
88da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            if (multiplier[i] != 1) {
89da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                result->appendScalar(multiplier[i]);
90da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                result->append(" mul ");
91da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            }
92da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            if (prevColor[i] != 0) {
93da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                result->appendScalar(prevColor[i]);
94da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                result->append(" add ");
95da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            }
96da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        }
97da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
98da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        if (dupInput[i]) {
99da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            result->append("exch\n");
100da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        }
101da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    }
102da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org}
103da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
104da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org/* Generate Type 4 function code to map t=[0,1) to the passed gradient,
105da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org   clamping at the edges of the range.  The generated code will be of the form:
106da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org       if (t < 0) {
107da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org           return colorData[0][r,g,b];
108da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org       } else {
109da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org           if (t < info.fColorOffsets[1]) {
110da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org               return linearinterpolation(colorData[0][r,g,b],
111da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                                          colorData[1][r,g,b]);
112da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org           } else {
113da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org               if (t < info.fColorOffsets[2]) {
114da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                   return linearinterpolation(colorData[1][r,g,b],
115da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                                              colorData[2][r,g,b]);
116da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org               } else {
117da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
118da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                ...    } else {
119da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                           return colorData[info.fColorCount - 1][r,g,b];
120da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                       }
121da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                ...
122da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org           }
123da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org       }
124da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org */
125da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.orgstatic void gradientFunctionCode(const SkShader::GradientInfo& info,
126da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                                 SkString* result) {
127da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    /* We want to linearly interpolate from the previous color to the next.
128da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org       Scale the colors from 0..255 to 0..1 and determine the multipliers
129da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org       for interpolation.
130da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org       C{r,g,b}(t, section) = t - offset_(section-1) + t * Multiplier{r,g,b}.
131da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org     */
132da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    static const int kColorComponents = 3;
133316338a4eb54b544409e3a98d97ea0a829aef706twiz@google.com    typedef SkScalar ColorTuple[kColorComponents];
134316338a4eb54b544409e3a98d97ea0a829aef706twiz@google.com    SkAutoSTMalloc<4, ColorTuple> colorDataAlloc(info.fColorCount);
135316338a4eb54b544409e3a98d97ea0a829aef706twiz@google.com    ColorTuple *colorData = colorDataAlloc.get();
136da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    const SkScalar scale = SkScalarInvert(SkIntToScalar(255));
137da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    for (int i = 0; i < info.fColorCount; i++) {
138da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        colorData[i][0] = SkScalarMul(SkColorGetR(info.fColors[i]), scale);
139da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        colorData[i][1] = SkScalarMul(SkColorGetG(info.fColors[i]), scale);
140da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        colorData[i][2] = SkScalarMul(SkColorGetB(info.fColors[i]), scale);
141da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    }
142da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
143da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    // Clamp the initial color.
144da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    result->append("dup 0 le {pop ");
145da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    result->appendScalar(colorData[0][0]);
146da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    result->append(" ");
147da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    result->appendScalar(colorData[0][1]);
148da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    result->append(" ");
149da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    result->appendScalar(colorData[0][2]);
150da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    result->append(" }\n");
151da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
152da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    // The gradient colors.
1538135323f70b38e465761ab3e9817183750e3864dedisonn@google.com    int gradients = 0;
154da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    for (int i = 1 ; i < info.fColorCount; i++) {
1558135323f70b38e465761ab3e9817183750e3864dedisonn@google.com        if (info.fColorOffsets[i] == info.fColorOffsets[i - 1]) {
1568135323f70b38e465761ab3e9817183750e3864dedisonn@google.com            continue;
1578135323f70b38e465761ab3e9817183750e3864dedisonn@google.com        }
1588135323f70b38e465761ab3e9817183750e3864dedisonn@google.com        gradients++;
1598135323f70b38e465761ab3e9817183750e3864dedisonn@google.com
160da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        result->append("{dup ");
161da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        result->appendScalar(info.fColorOffsets[i]);
162da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        result->append(" le {");
163da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        if (info.fColorOffsets[i - 1] != 0) {
164da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            result->appendScalar(info.fColorOffsets[i - 1]);
165da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            result->append(" sub\n");
166da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        }
167da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
168da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        interpolateColorCode(info.fColorOffsets[i] - info.fColorOffsets[i - 1],
16953648ab14e2f6a3cf07bd36b0964db8a0664b46adjsollen@google.com                             colorData[i], colorData[i - 1], result);
170da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        result->append("}\n");
171da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    }
172da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
173da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    // Clamp the final color.
174da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    result->append("{pop ");
175da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    result->appendScalar(colorData[info.fColorCount - 1][0]);
176da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    result->append(" ");
177da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    result->appendScalar(colorData[info.fColorCount - 1][1]);
178da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    result->append(" ");
179da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    result->appendScalar(colorData[info.fColorCount - 1][2]);
180da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
1818135323f70b38e465761ab3e9817183750e3864dedisonn@google.com    for (int i = 0 ; i < gradients + 1; i++) {
182da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        result->append("} ifelse\n");
183da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    }
184da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org}
185da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
186da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org/* Map a value of t on the stack into [0, 1) for Repeat or Mirror tile mode. */
187da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.orgstatic void tileModeCode(SkShader::TileMode mode, SkString* result) {
188da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    if (mode == SkShader::kRepeat_TileMode) {
189da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        result->append("dup truncate sub\n");  // Get the fractional part.
190da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        result->append("dup 0 le {1 add} if\n");  // Map (-1,0) => (0,1)
191da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        return;
192da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    }
193da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
194da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    if (mode == SkShader::kMirror_TileMode) {
195da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        // Map t mod 2 into [0, 1, 1, 0].
196da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        //               Code                     Stack
197da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        result->append("abs "                 // Map negative to positive.
198da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                       "dup "                 // t.s t.s
199da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                       "truncate "            // t.s t
200da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                       "dup "                 // t.s t t
201da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                       "cvi "                 // t.s t T
202da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                       "2 mod "               // t.s t (i mod 2)
203da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                       "1 eq "                // t.s t true|false
204da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                       "3 1 roll "            // true|false t.s t
205da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                       "sub "                 // true|false 0.s
206da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                       "exch "                // 0.s true|false
207da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                       "{1 exch sub} if\n");  // 1 - 0.s|0.s
208da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    }
209da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org}
210da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
21183d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com/**
21283d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com *  Returns PS function code that applies inverse perspective
21383d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com *  to a x, y point.
21483d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com *  The function assumes that the stack has at least two elements,
21583d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com *  and that the top 2 elements are numeric values.
21683d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com *  After executing this code on a PS stack, the last 2 elements are updated
21783d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com *  while the rest of the stack is preserved intact.
21883d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com *  inversePerspectiveMatrix is the inverse perspective matrix.
21983d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com */
22083d8eda890def8c119794deeec6244c67da83ac8edisonn@google.comstatic SkString apply_perspective_to_coordinates(
22183d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com        const SkMatrix& inversePerspectiveMatrix) {
22283d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    SkString code;
22383d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    if (!inversePerspectiveMatrix.hasPerspective()) {
22483d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com        return code;
22583d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    }
22683d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com
22783d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    // Perspective matrix should be:
22883d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    // 1   0  0
22983d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    // 0   1  0
23083d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    // p0 p1 p2
23183d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com
23283d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    const SkScalar p0 = inversePerspectiveMatrix[SkMatrix::kMPersp0];
23383d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    const SkScalar p1 = inversePerspectiveMatrix[SkMatrix::kMPersp1];
23483d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    const SkScalar p2 = inversePerspectiveMatrix[SkMatrix::kMPersp2];
23583d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com
23683d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    // y = y / (p2 + p0 x + p1 y)
23783d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    // x = x / (p2 + p0 x + p1 y)
23883d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com
23983d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    // Input on stack: x y
24083d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    code.append(" dup ");               // x y y
24183d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    code.appendScalar(p1);              // x y y p1
24283d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    code.append(" mul "                 // x y y*p1
24383d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com                " 2 index ");           // x y y*p1 x
24483d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    code.appendScalar(p0);              // x y y p1 x p0
24583d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    code.append(" mul ");               // x y y*p1 x*p0
24683d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    code.appendScalar(p2);              // x y y p1 x*p0 p2
24783d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    code.append(" add "                 // x y y*p1 x*p0+p2
24883d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com                "add "                  // x y y*p1+x*p0+p2
24983d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com                "3 1 roll "             // y*p1+x*p0+p2 x y
25083d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com                "2 index "              // z x y y*p1+x*p0+p2
25183d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com                "div "                  // y*p1+x*p0+p2 x y/(y*p1+x*p0+p2)
25283d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com                "3 1 roll "             // y/(y*p1+x*p0+p2) y*p1+x*p0+p2 x
25383d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com                "exch "                 // y/(y*p1+x*p0+p2) x y*p1+x*p0+p2
25483d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com                "div "                  // y/(y*p1+x*p0+p2) x/(y*p1+x*p0+p2)
25583d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com                "exch\n");              // x/(y*p1+x*p0+p2) y/(y*p1+x*p0+p2)
25683d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    return code;
25783d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com}
25883d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com
25983d8eda890def8c119794deeec6244c67da83ac8edisonn@google.comstatic SkString linearCode(const SkShader::GradientInfo& info,
26083d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com                           const SkMatrix& perspectiveRemover) {
26183d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    SkString function("{");
26283d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com
26383d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    function.append(apply_perspective_to_coordinates(perspectiveRemover));
26483d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com
26583d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    function.append("pop\n");  // Just ditch the y value.
266da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    tileModeCode(info.fTileMode, &function);
267da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    gradientFunctionCode(info, &function);
268da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    function.append("}");
269da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    return function;
270da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org}
271da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
27283d8eda890def8c119794deeec6244c67da83ac8edisonn@google.comstatic SkString radialCode(const SkShader::GradientInfo& info,
27383d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com                           const SkMatrix& perspectiveRemover) {
274da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    SkString function("{");
27583d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com
27683d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    function.append(apply_perspective_to_coordinates(perspectiveRemover));
27783d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com
278da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    // Find the distance from the origin.
279da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    function.append("dup "      // x y y
280da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                    "mul "      // x y^2
281da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                    "exch "     // y^2 x
282da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                    "dup "      // y^2 x x
283da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                    "mul "      // y^2 x^2
284da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                    "add "      // y^2+x^2
285da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                    "sqrt\n");  // sqrt(y^2+x^2)
286da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
287da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    tileModeCode(info.fTileMode, &function);
288da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    gradientFunctionCode(info, &function);
289da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    function.append("}");
290da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    return function;
291da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org}
292da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
2936219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com/* Conical gradient shader, based on the Canvas spec for radial gradients
294d6176b0dcacb124539e0cfd051e6d93a9782f020rmistry@google.com   See: http://www.w3.org/TR/2dcontext/#dom-context-2d-createradialgradient
2956219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com */
29683d8eda890def8c119794deeec6244c67da83ac8edisonn@google.comstatic SkString twoPointConicalCode(const SkShader::GradientInfo& info,
29783d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com                                    const SkMatrix& perspectiveRemover) {
2986219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com    SkScalar dx = info.fPoint[1].fX - info.fPoint[0].fX;
2996219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com    SkScalar dy = info.fPoint[1].fY - info.fPoint[0].fY;
3006219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com    SkScalar r0 = info.fRadius[0];
3016219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com    SkScalar dr = info.fRadius[1] - info.fRadius[0];
302d6176b0dcacb124539e0cfd051e6d93a9782f020rmistry@google.com    SkScalar a = SkScalarMul(dx, dx) + SkScalarMul(dy, dy) -
3036219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com                 SkScalarMul(dr, dr);
3046219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com
3056219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com    // First compute t, if the pixel falls outside the cone, then we'll end
3066219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com    // with 'false' on the stack, otherwise we'll push 'true' with t below it
3076219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com
3086219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com    // We start with a stack of (x y), copy it and then consume one copy in
3096219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com    // order to calculate b and the other to calculate c.
3106219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com    SkString function("{");
31183d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com
31283d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    function.append(apply_perspective_to_coordinates(perspectiveRemover));
31383d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com
3146219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com    function.append("2 copy ");
3156219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com
3166219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com    // Calculate b and b^2; b = -2 * (y * dy + x * dx + r0 * dr).
3176219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com    function.appendScalar(dy);
3186219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com    function.append(" mul exch ");
3196219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com    function.appendScalar(dx);
3206219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com    function.append(" mul add ");
3216219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com    function.appendScalar(SkScalarMul(r0, dr));
3226219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com    function.append(" add -2 mul dup dup mul\n");
3236219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com
3246219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com    // c = x^2 + y^2 + radius0^2
3256219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com    function.append("4 2 roll dup mul exch dup mul add ");
3266219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com    function.appendScalar(SkScalarMul(r0, r0));
3276219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com    function.append(" sub dup 4 1 roll\n");
3286219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com
3296219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com    // Contents of the stack at this point: c, b, b^2, c
3306219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com
3316219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com    // if a = 0, then we collapse to a simpler linear case
3326219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com    if (a == 0) {
3336219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com
3346219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        // t = -c/b
3356219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        function.append("pop pop div neg dup ");
3366219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com
3376219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        // compute radius(t)
3386219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        function.appendScalar(dr);
3396219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        function.append(" mul ");
3406219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        function.appendScalar(r0);
3416219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        function.append(" add\n");
3426219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com
3436219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        // if r(t) < 0, then it's outside the cone
344ab2fe82840f37e92e7c45a36cac725c6fd2512c7rileya@google.com        function.append("0 lt {pop false} {true} ifelse\n");
3456219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com
3466219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com    } else {
3476219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com
3486219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        // quadratic case: the Canvas spec wants the largest
3496219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        // root t for which radius(t) > 0
350d6176b0dcacb124539e0cfd051e6d93a9782f020rmistry@google.com
3516219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        // compute the discriminant (b^2 - 4ac)
3526219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        function.appendScalar(SkScalarMul(SkIntToScalar(4), a));
3536219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        function.append(" mul sub dup\n");
3546219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com
3556219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        // if d >= 0, proceed
3566219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        function.append("0 ge {\n");
3576219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com
3586219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        // an intermediate value we'll use to compute the roots:
3596219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        // q = -0.5 * (b +/- sqrt(d))
3606219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        function.append("sqrt exch dup 0 lt {exch -1 mul} if");
3616219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        function.append(" add -0.5 mul dup\n");
3626219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com
3636219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        // first root = q / a
3646219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        function.appendScalar(a);
3656219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        function.append(" div\n");
3666219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com
3676219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        // second root = c / q
3686219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        function.append("3 1 roll div\n");
3696219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com
3706219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        // put the larger root on top of the stack
3716219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        function.append("2 copy gt {exch} if\n");
3726219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com
3736219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        // compute radius(t) for larger root
3746219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        function.append("dup ");
3756219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        function.appendScalar(dr);
3766219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        function.append(" mul ");
3776219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        function.appendScalar(r0);
3786219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        function.append(" add\n");
3796219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com
3806219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        // if r(t) > 0, we have our t, pop off the smaller root and we're done
3816219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        function.append(" 0 gt {exch pop true}\n");
3826219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com
3836219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        // otherwise, throw out the larger one and try the smaller root
3846219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        function.append("{pop dup\n");
3856219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        function.appendScalar(dr);
3866219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        function.append(" mul ");
3876219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        function.appendScalar(r0);
3886219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        function.append(" add\n");
3896219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com
3906219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        // if r(t) < 0, push false, otherwise the smaller root is our t
391ab2fe82840f37e92e7c45a36cac725c6fd2512c7rileya@google.com        function.append("0 le {pop false} {true} ifelse\n");
3926219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        function.append("} ifelse\n");
3936219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com
3946219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        // d < 0, clear the stack and push false
395ab2fe82840f37e92e7c45a36cac725c6fd2512c7rileya@google.com        function.append("} {pop pop pop false} ifelse\n");
3966219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com    }
3976219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com
3986219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com    // if the pixel is in the cone, proceed to compute a color
3996219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com    function.append("{");
4006219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com    tileModeCode(info.fTileMode, &function);
4016219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com    gradientFunctionCode(info, &function);
4026219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com
4036219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com    // otherwise, just write black
4046219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com    function.append("} {0 0 0} ifelse }");
4056219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com
4066219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com    return function;
4076219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com}
4086219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com
40983d8eda890def8c119794deeec6244c67da83ac8edisonn@google.comstatic SkString sweepCode(const SkShader::GradientInfo& info,
41083d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com                          const SkMatrix& perspectiveRemover) {
411da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    SkString function("{exch atan 360 div\n");
412da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    tileModeCode(info.fTileMode, &function);
413da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    gradientFunctionCode(info, &function);
414da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    function.append("}");
415da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    return function;
416da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org}
417da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
418c54d8db4d169ea5f2af2a9a2349df007bd428475Florin Malitastatic void drawBitmapMatrix(SkCanvas* canvas, const SkBitmap& bm, const SkMatrix& matrix) {
419c54d8db4d169ea5f2af2a9a2349df007bd428475Florin Malita    SkAutoCanvasRestore acr(canvas, true);
420c54d8db4d169ea5f2af2a9a2349df007bd428475Florin Malita    canvas->concat(matrix);
421c54d8db4d169ea5f2af2a9a2349df007bd428475Florin Malita    canvas->drawBitmap(bm, 0, 0);
422c54d8db4d169ea5f2af2a9a2349df007bd428475Florin Malita}
423c54d8db4d169ea5f2af2a9a2349df007bd428475Florin Malita
424421d6443fbd3a913dfa32b6492c4a2969bc6314bvandebo@chromium.orgclass SkPDFShader::State {
425421d6443fbd3a913dfa32b6492c4a2969bc6314bvandebo@chromium.orgpublic:
426421d6443fbd3a913dfa32b6492c4a2969bc6314bvandebo@chromium.org    SkShader::GradientType fType;
427421d6443fbd3a913dfa32b6492c4a2969bc6314bvandebo@chromium.org    SkShader::GradientInfo fInfo;
42893a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    SkAutoFree fColorData;    // This provides storage for arrays in fInfo.
429421d6443fbd3a913dfa32b6492c4a2969bc6314bvandebo@chromium.org    SkMatrix fCanvasTransform;
430421d6443fbd3a913dfa32b6492c4a2969bc6314bvandebo@chromium.org    SkMatrix fShaderTransform;
431421d6443fbd3a913dfa32b6492c4a2969bc6314bvandebo@chromium.org    SkIRect fBBox;
432421d6443fbd3a913dfa32b6492c4a2969bc6314bvandebo@chromium.org
433421d6443fbd3a913dfa32b6492c4a2969bc6314bvandebo@chromium.org    SkBitmap fImage;
434421d6443fbd3a913dfa32b6492c4a2969bc6314bvandebo@chromium.org    uint32_t fPixelGeneration;
435421d6443fbd3a913dfa32b6492c4a2969bc6314bvandebo@chromium.org    SkShader::TileMode fImageTileModes[2];
436421d6443fbd3a913dfa32b6492c4a2969bc6314bvandebo@chromium.org
43793a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    State(const SkShader& shader, const SkMatrix& canvasTransform,
438c3796c7a74e103d9b367ad9449fcdacfa20d83e1fmalita          const SkIRect& bbox, SkScalar rasterScale);
43993a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
440421d6443fbd3a913dfa32b6492c4a2969bc6314bvandebo@chromium.org    bool operator==(const State& b) const;
44193a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
44293a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    SkPDFShader::State* CreateAlphaToLuminosityState() const;
44393a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    SkPDFShader::State* CreateOpaqueState() const;
44493a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
44593a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    bool GradientHasAlpha() const;
44693a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
44793a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.orgprivate:
44893a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    State(const State& other);
44993a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    State operator=(const State& rhs);
45093a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    void AllocateGradientInfoStorage();
451421d6443fbd3a913dfa32b6492c4a2969bc6314bvandebo@chromium.org};
452421d6443fbd3a913dfa32b6492c4a2969bc6314bvandebo@chromium.org
453530ea8e24bc88f2d7973c35a703f18c1dafb56dchalcanary////////////////////////////////////////////////////////////////////////////////
454da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
4552e3f9d8a9309686eeb4c76ccfde5800da87a68b3halcanarySkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state)
4562e3f9d8a9309686eeb4c76ccfde5800da87a68b3halcanary    : SkPDFDict("Pattern"), fShaderState(state) {}
457da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
458547019e1a671b1b30746e69b9647b201500e1504halcanarySkPDFFunctionShader::~SkPDFFunctionShader() {}
459bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary
460530ea8e24bc88f2d7973c35a703f18c1dafb56dchalcanarybool SkPDFFunctionShader::equals(const SkPDFShader::State& state) const {
461530ea8e24bc88f2d7973c35a703f18c1dafb56dchalcanary    return state == *fShaderState;
462530ea8e24bc88f2d7973c35a703f18c1dafb56dchalcanary}
46393a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
464530ea8e24bc88f2d7973c35a703f18c1dafb56dchalcanary////////////////////////////////////////////////////////////////////////////////
46593a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
4662e3f9d8a9309686eeb4c76ccfde5800da87a68b3halcanarySkPDFAlphaFunctionShader::SkPDFAlphaFunctionShader(SkPDFShader::State* state)
4672e3f9d8a9309686eeb4c76ccfde5800da87a68b3halcanary    : fShaderState(state) {}
46893a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
469530ea8e24bc88f2d7973c35a703f18c1dafb56dchalcanarybool SkPDFAlphaFunctionShader::equals(const SkPDFShader::State& state) const {
470530ea8e24bc88f2d7973c35a703f18c1dafb56dchalcanary    return state == *fShaderState;
471530ea8e24bc88f2d7973c35a703f18c1dafb56dchalcanary}
472bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary
4732e3f9d8a9309686eeb4c76ccfde5800da87a68b3halcanarySkPDFAlphaFunctionShader::~SkPDFAlphaFunctionShader() {}
474421d6443fbd3a913dfa32b6492c4a2969bc6314bvandebo@chromium.org
475530ea8e24bc88f2d7973c35a703f18c1dafb56dchalcanary////////////////////////////////////////////////////////////////////////////////
476421d6443fbd3a913dfa32b6492c4a2969bc6314bvandebo@chromium.org
4772e3f9d8a9309686eeb4c76ccfde5800da87a68b3halcanarySkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state)
4782e3f9d8a9309686eeb4c76ccfde5800da87a68b3halcanary    : fShaderState(state) {}
479421d6443fbd3a913dfa32b6492c4a2969bc6314bvandebo@chromium.org
480530ea8e24bc88f2d7973c35a703f18c1dafb56dchalcanarybool SkPDFImageShader::equals(const SkPDFShader::State& state) const {
481530ea8e24bc88f2d7973c35a703f18c1dafb56dchalcanary    return state == *fShaderState;
482530ea8e24bc88f2d7973c35a703f18c1dafb56dchalcanary}
483fb62b3d423fa34c672df42f47017dbef087d19e9halcanary
484547019e1a671b1b30746e69b9647b201500e1504halcanarySkPDFImageShader::~SkPDFImageShader() {}
485fb62b3d423fa34c672df42f47017dbef087d19e9halcanary
486530ea8e24bc88f2d7973c35a703f18c1dafb56dchalcanary////////////////////////////////////////////////////////////////////////////////
487530ea8e24bc88f2d7973c35a703f18c1dafb56dchalcanary
488530ea8e24bc88f2d7973c35a703f18c1dafb56dchalcanarystatic SkPDFObject* get_pdf_shader_by_state(
489792c80f5a7b66e75d42664ccb298f31962c6654chalcanary        SkPDFCanon* canon,
490792c80f5a7b66e75d42664ccb298f31962c6654chalcanary        SkScalar dpi,
491bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary        SkAutoTDelete<SkPDFShader::State>* autoState) {
492530ea8e24bc88f2d7973c35a703f18c1dafb56dchalcanary    const SkPDFShader::State& state = **autoState;
493bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    if (state.fType == SkShader::kNone_GradientType && state.fImage.isNull()) {
494da6c569334b5971e55dc95cb9bf85853d068e999vandebo@chromium.org        // TODO(vandebo) This drops SKComposeShader on the floor.  We could
495da6c569334b5971e55dc95cb9bf85853d068e999vandebo@chromium.org        // handle compose shader by pulling things up to a layer, drawing with
496da6c569334b5971e55dc95cb9bf85853d068e999vandebo@chromium.org        // the first shader, applying the xfer mode and drawing again with the
497da6c569334b5971e55dc95cb9bf85853d068e999vandebo@chromium.org        // second shader, then applying the layer to the original drawing.
498da6c569334b5971e55dc95cb9bf85853d068e999vandebo@chromium.org        return NULL;
499530ea8e24bc88f2d7973c35a703f18c1dafb56dchalcanary    } else if (state.fType == SkShader::kNone_GradientType) {
500792c80f5a7b66e75d42664ccb298f31962c6654chalcanary        SkPDFObject* shader = canon->findImageShader(state);
501792c80f5a7b66e75d42664ccb298f31962c6654chalcanary        return shader ? SkRef(shader)
502792c80f5a7b66e75d42664ccb298f31962c6654chalcanary                      : SkPDFImageShader::Create(canon, dpi, autoState);
503bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    } else if (state.GradientHasAlpha()) {
504792c80f5a7b66e75d42664ccb298f31962c6654chalcanary        SkPDFObject* shader = canon->findAlphaShader(state);
505530ea8e24bc88f2d7973c35a703f18c1dafb56dchalcanary        return shader ? SkRef(shader)
506792c80f5a7b66e75d42664ccb298f31962c6654chalcanary                      : SkPDFAlphaFunctionShader::Create(canon, dpi, autoState);
507421d6443fbd3a913dfa32b6492c4a2969bc6314bvandebo@chromium.org    } else {
508792c80f5a7b66e75d42664ccb298f31962c6654chalcanary        SkPDFObject* shader = canon->findFunctionShader(state);
509792c80f5a7b66e75d42664ccb298f31962c6654chalcanary        return shader ? SkRef(shader)
510792c80f5a7b66e75d42664ccb298f31962c6654chalcanary                      : SkPDFFunctionShader::Create(canon, autoState);
511386dfc049baa400c13e4e98727d4c04d0242b7b8vandebo@chromium.org    }
51293a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org}
51393a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
51493a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org// static
515792c80f5a7b66e75d42664ccb298f31962c6654chalcanarySkPDFObject* SkPDFShader::GetPDFShader(SkPDFCanon* canon,
516792c80f5a7b66e75d42664ccb298f31962c6654chalcanary                                       SkScalar dpi,
517792c80f5a7b66e75d42664ccb298f31962c6654chalcanary                                       const SkShader& shader,
51893a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org                                       const SkMatrix& matrix,
519c3796c7a74e103d9b367ad9449fcdacfa20d83e1fmalita                                       const SkIRect& surfaceBBox,
520c3796c7a74e103d9b367ad9449fcdacfa20d83e1fmalita                                       SkScalar rasterScale) {
521bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    SkAutoTDelete<SkPDFShader::State> state(
522c3796c7a74e103d9b367ad9449fcdacfa20d83e1fmalita            SkNEW_ARGS(State, (shader, matrix, surfaceBBox, rasterScale)));
523792c80f5a7b66e75d42664ccb298f31962c6654chalcanary    return get_pdf_shader_by_state(canon, dpi, &state);
524da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org}
525da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
5262b86155b42c2493ff0c558ce105a464769962274halcanarystatic SkPDFDict* get_gradient_resource_dict(
52793a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org        SkPDFObject* functionShader,
52893a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org        SkPDFObject* gState) {
5292b86155b42c2493ff0c558ce105a464769962274halcanary    SkTDArray<SkPDFObject*> patterns;
5302b86155b42c2493ff0c558ce105a464769962274halcanary    if (functionShader) {
5312b86155b42c2493ff0c558ce105a464769962274halcanary        patterns.push(functionShader);
53293a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    }
5332b86155b42c2493ff0c558ce105a464769962274halcanary    SkTDArray<SkPDFObject*> graphicStates;
5342b86155b42c2493ff0c558ce105a464769962274halcanary    if (gState) {
5352b86155b42c2493ff0c558ce105a464769962274halcanary        graphicStates.push(gState);
53693a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    }
5372b86155b42c2493ff0c558ce105a464769962274halcanary    return SkPDFResourceDict::Create(&graphicStates, &patterns, NULL, NULL);
53893a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org}
53993a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
54093a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.orgstatic void populate_tiling_pattern_dict(SkPDFDict* pattern,
541547019e1a671b1b30746e69b9647b201500e1504halcanary                                         SkRect& bbox,
542547019e1a671b1b30746e69b9647b201500e1504halcanary                                         SkPDFDict* resources,
543547019e1a671b1b30746e69b9647b201500e1504halcanary                                         const SkMatrix& matrix) {
54493a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    const int kTiling_PatternType = 1;
54593a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    const int kColoredTilingPattern_PaintType = 1;
54693a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    const int kConstantSpacing_TilingType = 1;
54793a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
54893a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    pattern->insertName("Type", "Pattern");
54993a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    pattern->insertInt("PatternType", kTiling_PatternType);
55093a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    pattern->insertInt("PaintType", kColoredTilingPattern_PaintType);
55193a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    pattern->insertInt("TilingType", kConstantSpacing_TilingType);
552a25b3371a7209105e6c05f7bbb53d19e2b14498bhalcanary    pattern->insertObject("BBox", SkPDFUtils::RectToArray(bbox));
55393a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    pattern->insertScalar("XStep", bbox.width());
55493a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    pattern->insertScalar("YStep", bbox.height());
555a25b3371a7209105e6c05f7bbb53d19e2b14498bhalcanary    pattern->insertObject("Resources", SkRef(resources));
55693a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    if (!matrix.isIdentity()) {
557a25b3371a7209105e6c05f7bbb53d19e2b14498bhalcanary        pattern->insertObject("Matrix", SkPDFUtils::MatrixToArray(matrix));
55893a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    }
55993a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org}
56093a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
56193a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org/**
56293a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org * Creates a content stream which fills the pattern P0 across bounds.
56393a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org * @param gsIndex A graphics state resource index to apply, or <0 if no
56493a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org * graphics state to apply.
56593a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org */
56693a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.orgstatic SkStream* create_pattern_fill_content(int gsIndex, SkRect& bounds) {
56793a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    SkDynamicMemoryWStream content;
56893a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    if (gsIndex >= 0) {
56993a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org        SkPDFUtils::ApplyGraphicState(gsIndex, &content);
57093a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    }
57193a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    SkPDFUtils::ApplyPattern(0, &content);
57293a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    SkPDFUtils::AppendRectangle(bounds, &content);
57393a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    SkPDFUtils::PaintPath(SkPaint::kFill_Style, SkPath::kEvenOdd_FillType,
57493a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org                          &content);
57593a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
57693a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    return content.detachAsStream();
57793a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org}
57893a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
57993a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org/**
58093a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org * Creates a ExtGState with the SMask set to the luminosityShader in
58193a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org * luminosity mode. The shader pattern extends to the bbox.
58293a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org */
583be27a118c277af23377d38e9b3bfd3fcc276114fhalcanarystatic SkPDFObject* create_smask_graphic_state(
584792c80f5a7b66e75d42664ccb298f31962c6654chalcanary        SkPDFCanon* canon, SkScalar dpi, const SkPDFShader::State& state) {
58593a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    SkRect bbox;
586bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    bbox.set(state.fBBox);
58793a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
588bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    SkAutoTDelete<SkPDFShader::State> alphaToLuminosityState(
589bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary            state.CreateAlphaToLuminosityState());
590bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    SkAutoTUnref<SkPDFObject> luminosityShader(
591792c80f5a7b66e75d42664ccb298f31962c6654chalcanary            get_pdf_shader_by_state(canon, dpi, &alphaToLuminosityState));
59293a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
593a1193e4b0e34a7e4e1bd33e9708d7341679f8321scroggo    SkAutoTDelete<SkStream> alphaStream(create_pattern_fill_content(-1, bbox));
59493a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
5952b86155b42c2493ff0c558ce105a464769962274halcanary    SkAutoTUnref<SkPDFDict>
59693a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org        resources(get_gradient_resource_dict(luminosityShader, NULL));
59793a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
59893a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    SkAutoTUnref<SkPDFFormXObject> alphaMask(
59993a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org            new SkPDFFormXObject(alphaStream.get(), bbox, resources.get()));
60093a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
60193a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    return SkPDFGraphicState::GetSMaskGraphicState(
60293a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org            alphaMask.get(), false,
60393a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org            SkPDFGraphicState::kLuminosity_SMaskMode);
60493a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org}
60593a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
606530ea8e24bc88f2d7973c35a703f18c1dafb56dchalcanarySkPDFAlphaFunctionShader* SkPDFAlphaFunctionShader::Create(
607792c80f5a7b66e75d42664ccb298f31962c6654chalcanary        SkPDFCanon* canon,
608792c80f5a7b66e75d42664ccb298f31962c6654chalcanary        SkScalar dpi,
609bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary        SkAutoTDelete<SkPDFShader::State>* autoState) {
610bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    const SkPDFShader::State& state = **autoState;
61193a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    SkRect bbox;
612bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    bbox.set(state.fBBox);
61393a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
614bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    SkAutoTDelete<SkPDFShader::State> opaqueState(state.CreateOpaqueState());
615bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary
616547019e1a671b1b30746e69b9647b201500e1504halcanary    SkAutoTUnref<SkPDFObject> colorShader(
617547019e1a671b1b30746e69b9647b201500e1504halcanary            get_pdf_shader_by_state(canon, dpi, &opaqueState));
618bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    if (!colorShader) {
619bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary        return NULL;
620bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    }
62193a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
62293a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    // Create resource dict with alpha graphics state as G0 and
62393a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    // pattern shader as P0, then write content stream.
624be27a118c277af23377d38e9b3bfd3fcc276114fhalcanary    SkAutoTUnref<SkPDFObject> alphaGs(
625792c80f5a7b66e75d42664ccb298f31962c6654chalcanary            create_smask_graphic_state(canon, dpi, state));
626bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary
627bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    SkPDFAlphaFunctionShader* alphaFunctionShader =
6282e3f9d8a9309686eeb4c76ccfde5800da87a68b3halcanary            SkNEW_ARGS(SkPDFAlphaFunctionShader, (autoState->detach()));
629bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary
6302b86155b42c2493ff0c558ce105a464769962274halcanary    SkAutoTUnref<SkPDFDict> resourceDict(
631547019e1a671b1b30746e69b9647b201500e1504halcanary            get_gradient_resource_dict(colorShader.get(), alphaGs.get()));
63293a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
633a1193e4b0e34a7e4e1bd33e9708d7341679f8321scroggo    SkAutoTDelete<SkStream> colorStream(
63493a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org            create_pattern_fill_content(0, bbox));
635bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    alphaFunctionShader->setData(colorStream.get());
63693a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
637547019e1a671b1b30746e69b9647b201500e1504halcanary    populate_tiling_pattern_dict(alphaFunctionShader, bbox, resourceDict.get(),
63893a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org                                 SkMatrix::I());
639792c80f5a7b66e75d42664ccb298f31962c6654chalcanary    canon->addAlphaShader(alphaFunctionShader);
640bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    return alphaFunctionShader;
64193a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org}
64293a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
64383d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com// Finds affine and persp such that in = affine * persp.
64483d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com// but it returns the inverse of perspective matrix.
64583d8eda890def8c119794deeec6244c67da83ac8edisonn@google.comstatic bool split_perspective(const SkMatrix in, SkMatrix* affine,
64683d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com                              SkMatrix* perspectiveInverse) {
64783d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    const SkScalar p2 = in[SkMatrix::kMPersp2];
64883d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com
64983d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    if (SkScalarNearlyZero(p2)) {
65083d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com        return false;
65183d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    }
65283d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com
65383d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    const SkScalar zero = SkIntToScalar(0);
65483d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    const SkScalar one = SkIntToScalar(1);
65583d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com
65683d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    const SkScalar sx = in[SkMatrix::kMScaleX];
65783d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    const SkScalar kx = in[SkMatrix::kMSkewX];
65883d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    const SkScalar tx = in[SkMatrix::kMTransX];
65983d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    const SkScalar ky = in[SkMatrix::kMSkewY];
66083d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    const SkScalar sy = in[SkMatrix::kMScaleY];
66183d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    const SkScalar ty = in[SkMatrix::kMTransY];
66283d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    const SkScalar p0 = in[SkMatrix::kMPersp0];
66383d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    const SkScalar p1 = in[SkMatrix::kMPersp1];
66483d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com
66583d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    // Perspective matrix would be:
66683d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    // 1  0  0
66783d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    // 0  1  0
66883d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    // p0 p1 p2
66983d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    // But we need the inverse of persp.
67083d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    perspectiveInverse->setAll(one,          zero,       zero,
67183d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com                               zero,         one,        zero,
67283d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com                               -p0/p2,     -p1/p2,     1/p2);
67383d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com
67483d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    affine->setAll(sx - p0 * tx / p2,       kx - p1 * tx / p2,      tx / p2,
67583d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com                   ky - p0 * ty / p2,       sy - p1 * ty / p2,      ty / p2,
67683d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com                   zero,                    zero,                   one);
67783d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com
67883d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    return true;
67983d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com}
68083d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com
681bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanarynamespace {
682bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanarySkPDFObject* create_range_object() {
683bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    SkPDFArray* range = SkNEW(SkPDFArray);
684bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    range->reserve(6);
685bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    range->appendInt(0);
686bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    range->appendInt(1);
687bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    range->appendInt(0);
688bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    range->appendInt(1);
689bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    range->appendInt(0);
690bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    range->appendInt(1);
691bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    return range;
692bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary}
693bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary
694bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanarytemplate <typename T> void unref(T* ptr) { ptr->unref();}
695bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary}  // namespace
696bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary
697bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanarySK_DECLARE_STATIC_LAZY_PTR(SkPDFObject, rangeObject,
698bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary                           create_range_object, unref<SkPDFObject>);
699bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary
700bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanarystatic SkPDFStream* make_ps_function(const SkString& psCode,
701bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary                                     SkPDFArray* domain) {
702bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    SkAutoDataUnref funcData(
703bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary            SkData::NewWithCopy(psCode.c_str(), psCode.size()));
704bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    SkPDFStream* result = SkNEW_ARGS(SkPDFStream, (funcData.get()));
705bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    result->insertInt("FunctionType", 4);
706a25b3371a7209105e6c05f7bbb53d19e2b14498bhalcanary    result->insertObject("Domain", SkRef(domain));
707a25b3371a7209105e6c05f7bbb53d19e2b14498bhalcanary    result->insertObject("Range", SkRef(rangeObject.get()));
708bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    return result;
709bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary}
710bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary
711530ea8e24bc88f2d7973c35a703f18c1dafb56dchalcanarySkPDFFunctionShader* SkPDFFunctionShader::Create(
712792c80f5a7b66e75d42664ccb298f31962c6654chalcanary        SkPDFCanon* canon, SkAutoTDelete<SkPDFShader::State>* autoState) {
713bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    const SkPDFShader::State& state = **autoState;
714bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary
71583d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    SkString (*codeFunction)(const SkShader::GradientInfo& info,
71683d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com                             const SkMatrix& perspectiveRemover) = NULL;
717da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    SkPoint transformPoints[2];
718da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
719da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    // Depending on the type of the gradient, we want to transform the
720da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    // coordinate space in different ways.
721bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    const SkShader::GradientInfo* info = &state.fInfo;
722da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    transformPoints[0] = info->fPoint[0];
723da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    transformPoints[1] = info->fPoint[1];
724bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    switch (state.fType) {
725da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        case SkShader::kLinear_GradientType:
726da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            codeFunction = &linearCode;
727da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            break;
728da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        case SkShader::kRadial_GradientType:
729da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            transformPoints[1] = transformPoints[0];
730da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            transformPoints[1].fX += info->fRadius[0];
731da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            codeFunction = &radialCode;
732da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            break;
7336219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        case SkShader::kConical_GradientType: {
7346219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com            transformPoints[1] = transformPoints[0];
7356219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com            transformPoints[1].fX += SK_Scalar1;
7366219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com            codeFunction = &twoPointConicalCode;
7376219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com            break;
7386219728eec0819a2a28b4439d8553a70b9c232f6rileya@google.com        }
739da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        case SkShader::kSweep_GradientType:
740da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            transformPoints[1] = transformPoints[0];
741c39c8674c94dba8b1ffe938cd99c825320cc1475vandebo@chromium.org            transformPoints[1].fX += SK_Scalar1;
742da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            codeFunction = &sweepCode;
743da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            break;
744da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        case SkShader::kColor_GradientType:
745da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        case SkShader::kNone_GradientType:
746020798af6780ba29156f4daec87c29ae9e4f4a12vandebo@chromium.org        default:
747bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary            return NULL;
748da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    }
749da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
750da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    // Move any scaling (assuming a unit gradient) or translation
751da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    // (and rotation for linear gradient), of the final gradient from
752da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    // info->fPoints to the matrix (updating bbox appropriately).  Now
753da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    // the gradient can be drawn on on the unit segment.
754da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    SkMatrix mapperMatrix;
755da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    unitToPointsMatrix(transformPoints, &mapperMatrix);
75683d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com
757bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    SkMatrix finalMatrix = state.fCanvasTransform;
758bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    finalMatrix.preConcat(state.fShaderTransform);
759ace2269edfed9dfd3097597f1698921aaddf80a1commit-bot@chromium.org    finalMatrix.preConcat(mapperMatrix);
760ace2269edfed9dfd3097597f1698921aaddf80a1commit-bot@chromium.org
76183d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    // Preserves as much as posible in the final matrix, and only removes
76283d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    // the perspective. The inverse of the perspective is stored in
76383d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    // perspectiveInverseOnly matrix and has 3 useful numbers
76483d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    // (p0, p1, p2), while everything else is either 0 or 1.
76583d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    // In this way the shader will handle it eficiently, with minimal code.
76683d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    SkMatrix perspectiveInverseOnly = SkMatrix::I();
76783d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    if (finalMatrix.hasPerspective()) {
76883d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com        if (!split_perspective(finalMatrix,
76983d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com                               &finalMatrix, &perspectiveInverseOnly)) {
770bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary            return NULL;
77183d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com        }
77283d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com    }
77383d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com
774da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    SkRect bbox;
775bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    bbox.set(state.fBBox);
7767b4d4c7a4668f771641aaed88b83d22d5efe6873fmalita    if (!inverse_transform_bbox(finalMatrix, &bbox)) {
777bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary        return NULL;
778386dfc049baa400c13e4e98727d4c04d0242b7b8vandebo@chromium.org    }
779da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
780d96d17b9c113ac694138224249ff2ce643e961ddvandebo@chromium.org    SkAutoTUnref<SkPDFArray> domain(new SkPDFArray);
781da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    domain->reserve(4);
782c789cf1c6f3947283aeb34acb5a22e6204f8f6f5reed@google.com    domain->appendScalar(bbox.fLeft);
783c789cf1c6f3947283aeb34acb5a22e6204f8f6f5reed@google.com    domain->appendScalar(bbox.fRight);
784c789cf1c6f3947283aeb34acb5a22e6204f8f6f5reed@google.com    domain->appendScalar(bbox.fTop);
785c789cf1c6f3947283aeb34acb5a22e6204f8f6f5reed@google.com    domain->appendScalar(bbox.fBottom);
786da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
787da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    SkString functionCode;
788fb62b3d423fa34c672df42f47017dbef087d19e9halcanary    // The two point radial gradient further references
789bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    // state.fInfo
790da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    // in translating from x, y coordinates to the t parameter. So, we have
791da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    // to transform the points and radii according to the calculated matrix.
79271a6cbfc585959738dc0b375603696ca7f60605freed    if (state.fType == SkShader::kConical_GradientType) {
793da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        SkShader::GradientInfo twoPointRadialInfo = *info;
794da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        SkMatrix inverseMapperMatrix;
795b054990307b7338e599a12d9af10eb2058b94051vandebo@chromium.org        if (!mapperMatrix.invert(&inverseMapperMatrix)) {
796bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary            return NULL;
797b054990307b7338e599a12d9af10eb2058b94051vandebo@chromium.org        }
798da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        inverseMapperMatrix.mapPoints(twoPointRadialInfo.fPoint, 2);
799da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        twoPointRadialInfo.fRadius[0] =
800da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            inverseMapperMatrix.mapRadius(info->fRadius[0]);
801da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        twoPointRadialInfo.fRadius[1] =
802da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            inverseMapperMatrix.mapRadius(info->fRadius[1]);
80383d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com        functionCode = codeFunction(twoPointRadialInfo, perspectiveInverseOnly);
804da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    } else {
80583d8eda890def8c119794deeec6244c67da83ac8edisonn@google.com        functionCode = codeFunction(*info, perspectiveInverseOnly);
806da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    }
807da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
808d96d17b9c113ac694138224249ff2ce643e961ddvandebo@chromium.org    SkAutoTUnref<SkPDFDict> pdfShader(new SkPDFDict);
809c789cf1c6f3947283aeb34acb5a22e6204f8f6f5reed@google.com    pdfShader->insertInt("ShadingType", 1);
810c789cf1c6f3947283aeb34acb5a22e6204f8f6f5reed@google.com    pdfShader->insertName("ColorSpace", "DeviceRGB");
811a25b3371a7209105e6c05f7bbb53d19e2b14498bhalcanary    pdfShader->insertObject("Domain", SkRef(domain.get()));
812d96d17b9c113ac694138224249ff2ce643e961ddvandebo@chromium.org
813f5b17fbd9406ea276d62886a072872dbd48ca58ahalcanary    SkAutoTUnref<SkPDFStream> function(
814f5b17fbd9406ea276d62886a072872dbd48ca58ahalcanary            make_ps_function(functionCode, domain.get()));
815a25b3371a7209105e6c05f7bbb53d19e2b14498bhalcanary    pdfShader->insertObjRef("Function", function.detach());
816bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary
817bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    SkPDFFunctionShader* pdfFunctionShader =
8182e3f9d8a9309686eeb4c76ccfde5800da87a68b3halcanary            SkNEW_ARGS(SkPDFFunctionShader, (autoState->detach()));
819bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary
820bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    pdfFunctionShader->insertInt("PatternType", 2);
821a25b3371a7209105e6c05f7bbb53d19e2b14498bhalcanary    pdfFunctionShader->insertObject("Matrix",
822a25b3371a7209105e6c05f7bbb53d19e2b14498bhalcanary                                    SkPDFUtils::MatrixToArray(finalMatrix));
823a25b3371a7209105e6c05f7bbb53d19e2b14498bhalcanary    pdfFunctionShader->insertObject("Shading", pdfShader.detach());
824bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary
825792c80f5a7b66e75d42664ccb298f31962c6654chalcanary    canon->addFunctionShader(pdfFunctionShader);
826bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    return pdfFunctionShader;
827da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org}
828da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
829530ea8e24bc88f2d7973c35a703f18c1dafb56dchalcanarySkPDFImageShader* SkPDFImageShader::Create(
830792c80f5a7b66e75d42664ccb298f31962c6654chalcanary        SkPDFCanon* canon,
831792c80f5a7b66e75d42664ccb298f31962c6654chalcanary        SkScalar dpi,
832bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary        SkAutoTDelete<SkPDFShader::State>* autoState) {
833bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    const SkPDFShader::State& state = **autoState;
834bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary
835bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    state.fImage.lockPixels();
836da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
837e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org    // The image shader pattern cell will be drawn into a separate device
838e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org    // in pattern cell space (no scaling on the bitmap, though there may be
839e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org    // translations so that all content is in the device, coordinates > 0).
840e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org
841e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org    // Map clip bounds to shader space to ensure the device is large enough
842e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org    // to handle fake clamping.
843bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    SkMatrix finalMatrix = state.fCanvasTransform;
844bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    finalMatrix.preConcat(state.fShaderTransform);
845e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org    SkRect deviceBounds;
846bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    deviceBounds.set(state.fBBox);
8477b4d4c7a4668f771641aaed88b83d22d5efe6873fmalita    if (!inverse_transform_bbox(finalMatrix, &deviceBounds)) {
848bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary        return NULL;
849386dfc049baa400c13e4e98727d4c04d0242b7b8vandebo@chromium.org    }
850da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
851bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    const SkBitmap* image = &state.fImage;
852e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org    SkRect bitmapBounds;
853e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org    image->getBounds(&bitmapBounds);
854e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org
855e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org    // For tiling modes, the bounds should be extended to include the bitmap,
856e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org    // otherwise the bitmap gets clipped out and the shader is empty and awful.
857e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org    // For clamp modes, we're only interested in the clip region, whether
858e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org    // or not the main bitmap is in it.
859e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org    SkShader::TileMode tileModes[2];
860bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    tileModes[0] = state.fImageTileModes[0];
861bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    tileModes[1] = state.fImageTileModes[1];
862e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org    if (tileModes[0] != SkShader::kClamp_TileMode ||
863e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org            tileModes[1] != SkShader::kClamp_TileMode) {
864e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org        deviceBounds.join(bitmapBounds);
865e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org    }
866e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org
867e1ca705cac4b946993f6cbf798e2a0ba27e739f3reed@google.com    SkISize size = SkISize::Make(SkScalarRoundToInt(deviceBounds.width()),
868e1ca705cac4b946993f6cbf798e2a0ba27e739f3reed@google.com                                 SkScalarRoundToInt(deviceBounds.height()));
869a1f1ee98a1f6d0770f6243270ca2f0e6c92efabahalcanary    SkAutoTUnref<SkPDFDevice> patternDevice(
870792c80f5a7b66e75d42664ccb298f31962c6654chalcanary            SkPDFDevice::CreateUnflipped(size, dpi, canon));
871a1f1ee98a1f6d0770f6243270ca2f0e6c92efabahalcanary    SkCanvas canvas(patternDevice.get());
872da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
873e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org    SkRect patternBBox;
874e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org    image->getBounds(&patternBBox);
875da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
876e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org    // Translate the canvas so that the bitmap origin is at (0, 0).
877e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org    canvas.translate(-deviceBounds.left(), -deviceBounds.top());
878e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org    patternBBox.offset(-deviceBounds.left(), -deviceBounds.top());
879e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org    // Undo the translation in the final matrix
880e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org    finalMatrix.preTranslate(deviceBounds.left(), deviceBounds.top());
881e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org
882e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org    // If the bitmap is out of bounds (i.e. clamp mode where we only see the
883e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org    // stretched sides), canvas will clip this out and the extraneous data
884e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org    // won't be saved to the PDF.
885da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    canvas.drawBitmap(*image, 0, 0);
886e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org
887e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org    SkScalar width = SkIntToScalar(image->width());
888e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org    SkScalar height = SkIntToScalar(image->height());
889da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
890da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    // Tiling is implied.  First we handle mirroring.
891da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    if (tileModes[0] == SkShader::kMirror_TileMode) {
892da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        SkMatrix xMirror;
893da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        xMirror.setScale(-1, 1);
894da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        xMirror.postTranslate(2 * width, 0);
895c54d8db4d169ea5f2af2a9a2349df007bd428475Florin Malita        drawBitmapMatrix(&canvas, *image, xMirror);
896da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        patternBBox.fRight += width;
897da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    }
898da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    if (tileModes[1] == SkShader::kMirror_TileMode) {
899da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        SkMatrix yMirror;
900663515bc59325092c4e47f5189782bd6fcd0586avandebo@chromium.org        yMirror.setScale(SK_Scalar1, -SK_Scalar1);
901da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        yMirror.postTranslate(0, 2 * height);
902c54d8db4d169ea5f2af2a9a2349df007bd428475Florin Malita        drawBitmapMatrix(&canvas, *image, yMirror);
903da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        patternBBox.fBottom += height;
904da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    }
905da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    if (tileModes[0] == SkShader::kMirror_TileMode &&
906da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            tileModes[1] == SkShader::kMirror_TileMode) {
907da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        SkMatrix mirror;
908da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        mirror.setScale(-1, -1);
909da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        mirror.postTranslate(2 * width, 2 * height);
910c54d8db4d169ea5f2af2a9a2349df007bd428475Florin Malita        drawBitmapMatrix(&canvas, *image, mirror);
911da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    }
912da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
913da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    // Then handle Clamping, which requires expanding the pattern canvas to
914da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    // cover the entire surfaceBBox.
915da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
916da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    // If both x and y are in clamp mode, we start by filling in the corners.
917da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    // (Which are just a rectangles of the corner colors.)
918da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    if (tileModes[0] == SkShader::kClamp_TileMode &&
919da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            tileModes[1] == SkShader::kClamp_TileMode) {
920da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        SkPaint paint;
921da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        SkRect rect;
922e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org        rect = SkRect::MakeLTRB(deviceBounds.left(), deviceBounds.top(), 0, 0);
923da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        if (!rect.isEmpty()) {
924da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            paint.setColor(image->getColor(0, 0));
925da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            canvas.drawRect(rect, paint);
926da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        }
927da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
928e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org        rect = SkRect::MakeLTRB(width, deviceBounds.top(),
929e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org                                deviceBounds.right(), 0);
930da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        if (!rect.isEmpty()) {
93154ff85c79d5a59ad9978a91b1aec1177e54104c5vandebo@chromium.org            paint.setColor(image->getColor(image->width() - 1, 0));
932da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            canvas.drawRect(rect, paint);
933da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        }
934da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
935e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org        rect = SkRect::MakeLTRB(width, height,
936e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org                                deviceBounds.right(), deviceBounds.bottom());
937da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        if (!rect.isEmpty()) {
93854ff85c79d5a59ad9978a91b1aec1177e54104c5vandebo@chromium.org            paint.setColor(image->getColor(image->width() - 1,
93954ff85c79d5a59ad9978a91b1aec1177e54104c5vandebo@chromium.org                                           image->height() - 1));
940da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            canvas.drawRect(rect, paint);
941da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        }
942da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
943e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org        rect = SkRect::MakeLTRB(deviceBounds.left(), height,
944e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org                                0, deviceBounds.bottom());
945da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        if (!rect.isEmpty()) {
94654ff85c79d5a59ad9978a91b1aec1177e54104c5vandebo@chromium.org            paint.setColor(image->getColor(0, image->height() - 1));
947da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            canvas.drawRect(rect, paint);
948da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        }
949da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    }
950da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
951da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    // Then expand the left, right, top, then bottom.
952da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    if (tileModes[0] == SkShader::kClamp_TileMode) {
95354ff85c79d5a59ad9978a91b1aec1177e54104c5vandebo@chromium.org        SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, image->height());
954e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org        if (deviceBounds.left() < 0) {
955da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            SkBitmap left;
956da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            SkAssertResult(image->extractSubset(&left, subset));
957da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
958da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            SkMatrix leftMatrix;
959e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org            leftMatrix.setScale(-deviceBounds.left(), 1);
960e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org            leftMatrix.postTranslate(deviceBounds.left(), 0);
961c54d8db4d169ea5f2af2a9a2349df007bd428475Florin Malita            drawBitmapMatrix(&canvas, left, leftMatrix);
962da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
963da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            if (tileModes[1] == SkShader::kMirror_TileMode) {
964663515bc59325092c4e47f5189782bd6fcd0586avandebo@chromium.org                leftMatrix.postScale(SK_Scalar1, -SK_Scalar1);
965da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                leftMatrix.postTranslate(0, 2 * height);
966c54d8db4d169ea5f2af2a9a2349df007bd428475Florin Malita                drawBitmapMatrix(&canvas, left, leftMatrix);
967da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            }
968be2048a371813259c46fc2260d53ccadc4ea8133vandebo@chromium.org            patternBBox.fLeft = 0;
969da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        }
970da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
971e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org        if (deviceBounds.right() > width) {
972da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            SkBitmap right;
97354ff85c79d5a59ad9978a91b1aec1177e54104c5vandebo@chromium.org            subset.offset(image->width() - 1, 0);
974da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            SkAssertResult(image->extractSubset(&right, subset));
975da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
976da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            SkMatrix rightMatrix;
977e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org            rightMatrix.setScale(deviceBounds.right() - width, 1);
978da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            rightMatrix.postTranslate(width, 0);
979c54d8db4d169ea5f2af2a9a2349df007bd428475Florin Malita            drawBitmapMatrix(&canvas, right, rightMatrix);
980da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
981da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            if (tileModes[1] == SkShader::kMirror_TileMode) {
982663515bc59325092c4e47f5189782bd6fcd0586avandebo@chromium.org                rightMatrix.postScale(SK_Scalar1, -SK_Scalar1);
983da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                rightMatrix.postTranslate(0, 2 * height);
984c54d8db4d169ea5f2af2a9a2349df007bd428475Florin Malita                drawBitmapMatrix(&canvas, right, rightMatrix);
985da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            }
986e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org            patternBBox.fRight = deviceBounds.width();
987da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        }
988da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    }
989da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
990da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    if (tileModes[1] == SkShader::kClamp_TileMode) {
99154ff85c79d5a59ad9978a91b1aec1177e54104c5vandebo@chromium.org        SkIRect subset = SkIRect::MakeXYWH(0, 0, image->width(), 1);
992e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org        if (deviceBounds.top() < 0) {
993da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            SkBitmap top;
994da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            SkAssertResult(image->extractSubset(&top, subset));
995da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
996da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            SkMatrix topMatrix;
997e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org            topMatrix.setScale(SK_Scalar1, -deviceBounds.top());
998e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org            topMatrix.postTranslate(0, deviceBounds.top());
999c54d8db4d169ea5f2af2a9a2349df007bd428475Florin Malita            drawBitmapMatrix(&canvas, top, topMatrix);
1000da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
1001da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            if (tileModes[0] == SkShader::kMirror_TileMode) {
1002da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                topMatrix.postScale(-1, 1);
1003da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                topMatrix.postTranslate(2 * width, 0);
1004c54d8db4d169ea5f2af2a9a2349df007bd428475Florin Malita                drawBitmapMatrix(&canvas, top, topMatrix);
1005da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            }
1006be2048a371813259c46fc2260d53ccadc4ea8133vandebo@chromium.org            patternBBox.fTop = 0;
1007da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        }
1008da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
1009e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org        if (deviceBounds.bottom() > height) {
1010da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            SkBitmap bottom;
101154ff85c79d5a59ad9978a91b1aec1177e54104c5vandebo@chromium.org            subset.offset(0, image->height() - 1);
1012da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            SkAssertResult(image->extractSubset(&bottom, subset));
1013da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
1014da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            SkMatrix bottomMatrix;
1015e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org            bottomMatrix.setScale(SK_Scalar1, deviceBounds.bottom() - height);
1016da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            bottomMatrix.postTranslate(0, height);
1017c54d8db4d169ea5f2af2a9a2349df007bd428475Florin Malita            drawBitmapMatrix(&canvas, bottom, bottomMatrix);
1018da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
1019da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            if (tileModes[0] == SkShader::kMirror_TileMode) {
1020da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                bottomMatrix.postScale(-1, 1);
1021da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                bottomMatrix.postTranslate(2 * width, 0);
1022c54d8db4d169ea5f2af2a9a2349df007bd428475Florin Malita                drawBitmapMatrix(&canvas, bottom, bottomMatrix);
1023da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            }
1024e324cc69be2be62a76cef52ba3562771af02f315commit-bot@chromium.org            patternBBox.fBottom = deviceBounds.height();
1025da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        }
1026da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    }
1027da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
1028da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    // Put the canvas into the pattern stream (fContent).
1029334fcbc167237f02058cb508cb5f51b718141461halcanary    SkAutoTDelete<SkStreamAsset> content(patternDevice->content());
1030bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary
1031bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    SkPDFImageShader* imageShader =
10322e3f9d8a9309686eeb4c76ccfde5800da87a68b3halcanary            SkNEW_ARGS(SkPDFImageShader, (autoState->detach()));
1033bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    imageShader->setData(content.get());
1034bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary
10352b86155b42c2493ff0c558ce105a464769962274halcanary    SkAutoTUnref<SkPDFDict> resourceDict(
10366d622703e578eddc64ab4e3340d0ab0033268799halcanary            patternDevice->createResourceDict());
1037792c80f5a7b66e75d42664ccb298f31962c6654chalcanary    populate_tiling_pattern_dict(imageShader, patternBBox,
10386d622703e578eddc64ab4e3340d0ab0033268799halcanary                                 resourceDict.get(), finalMatrix);
1039da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
1040bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    imageShader->fShaderState->fImage.unlockPixels();
1041da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
1042792c80f5a7b66e75d42664ccb298f31962c6654chalcanary    canon->addImageShader(imageShader);
1043bc59ac6b12bbded2117fe3aa9643b2d138e5dddahalcanary    return imageShader;
1044da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org}
1045da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
1046da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.orgbool SkPDFShader::State::operator==(const SkPDFShader::State& b) const {
1047da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    if (fType != b.fType ||
1048da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            fCanvasTransform != b.fCanvasTransform ||
1049da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            fShaderTransform != b.fShaderTransform ||
1050da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            fBBox != b.fBBox) {
1051da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        return false;
1052da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    }
1053da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
1054da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    if (fType == SkShader::kNone_GradientType) {
1055da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        if (fPixelGeneration != b.fPixelGeneration ||
1056da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                fPixelGeneration == 0 ||
1057da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                fImageTileModes[0] != b.fImageTileModes[0] ||
1058da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                fImageTileModes[1] != b.fImageTileModes[1]) {
1059da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            return false;
1060da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        }
1061da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    } else {
1062da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        if (fInfo.fColorCount != b.fInfo.fColorCount ||
1063da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                memcmp(fInfo.fColors, b.fInfo.fColors,
1064da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                       sizeof(SkColor) * fInfo.fColorCount) != 0 ||
1065da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                memcmp(fInfo.fColorOffsets, b.fInfo.fColorOffsets,
1066da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                       sizeof(SkScalar) * fInfo.fColorCount) != 0 ||
1067da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                fInfo.fPoint[0] != b.fInfo.fPoint[0] ||
1068da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                fInfo.fTileMode != b.fInfo.fTileMode) {
1069da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            return false;
1070da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        }
1071da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
1072da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        switch (fType) {
1073da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            case SkShader::kLinear_GradientType:
1074da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                if (fInfo.fPoint[1] != b.fInfo.fPoint[1]) {
1075da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                    return false;
1076da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                }
1077da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                break;
1078da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            case SkShader::kRadial_GradientType:
1079da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                if (fInfo.fRadius[0] != b.fInfo.fRadius[0]) {
1080da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                    return false;
1081da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                }
1082da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                break;
10834908533aef9830dc1c18552c305ed6ccb286f4f2reed@google.com            case SkShader::kConical_GradientType:
1084da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                if (fInfo.fPoint[1] != b.fInfo.fPoint[1] ||
1085da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                        fInfo.fRadius[0] != b.fInfo.fRadius[0] ||
1086da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                        fInfo.fRadius[1] != b.fInfo.fRadius[1]) {
1087da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                    return false;
1088da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                }
1089da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                break;
1090da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            case SkShader::kSweep_GradientType:
1091da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            case SkShader::kNone_GradientType:
1092da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org            case SkShader::kColor_GradientType:
1093da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org                break;
1094da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        }
1095da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    }
1096da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    return true;
1097da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org}
1098da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
1099c3796c7a74e103d9b367ad9449fcdacfa20d83e1fmalitaSkPDFShader::State::State(const SkShader& shader, const SkMatrix& canvasTransform,
1100c3796c7a74e103d9b367ad9449fcdacfa20d83e1fmalita                          const SkIRect& bbox, SkScalar rasterScale)
1101da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        : fCanvasTransform(canvasTransform),
1102e1bc274295ec57cb3d3f01aaa8abff3b49c76c73vandebo@chromium.org          fBBox(bbox),
1103e1bc274295ec57cb3d3f01aaa8abff3b49c76c73vandebo@chromium.org          fPixelGeneration(0) {
1104da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    fInfo.fColorCount = 0;
1105da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    fInfo.fColors = NULL;
1106da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    fInfo.fColorOffsets = NULL;
1107f94b3a4cebd4adab09c40ebe23c02a615e10c394bsalomon@google.com    fShaderTransform = shader.getLocalMatrix();
1108e1bc274295ec57cb3d3f01aaa8abff3b49c76c73vandebo@chromium.org    fImageTileModes[0] = fImageTileModes[1] = SkShader::kClamp_TileMode;
1109da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
1110da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    fType = shader.asAGradient(&fInfo);
1111da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org
1112da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    if (fType == SkShader::kNone_GradientType) {
1113da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        SkShader::BitmapType bitmapType;
1114da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        SkMatrix matrix;
111591f319c5dc4493384f0a52aaeef3dcc311ef6ed0rileya@google.com        bitmapType = shader.asABitmap(&fImage, &matrix, fImageTileModes);
1116da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        if (bitmapType != SkShader::kDefault_BitmapType) {
1117c3796c7a74e103d9b367ad9449fcdacfa20d83e1fmalita            // Generic fallback for unsupported shaders:
1118c3796c7a74e103d9b367ad9449fcdacfa20d83e1fmalita            //  * allocate a bbox-sized bitmap
1119c3796c7a74e103d9b367ad9449fcdacfa20d83e1fmalita            //  * shade the whole area
1120c3796c7a74e103d9b367ad9449fcdacfa20d83e1fmalita            //  * use the result as a bitmap shader
1121c3796c7a74e103d9b367ad9449fcdacfa20d83e1fmalita
11227b4d4c7a4668f771641aaed88b83d22d5efe6873fmalita            // bbox is in device space. While that's exactly what we want for sizing our bitmap,
11237b4d4c7a4668f771641aaed88b83d22d5efe6873fmalita            // we need to map it into shader space for adjustments (to match
11247b4d4c7a4668f771641aaed88b83d22d5efe6873fmalita            // SkPDFImageShader::Create's behavior).
11257b4d4c7a4668f771641aaed88b83d22d5efe6873fmalita            SkRect shaderRect = SkRect::Make(bbox);
11267b4d4c7a4668f771641aaed88b83d22d5efe6873fmalita            if (!inverse_transform_bbox(canvasTransform, &shaderRect)) {
11277b4d4c7a4668f771641aaed88b83d22d5efe6873fmalita                fImage.reset();
11287b4d4c7a4668f771641aaed88b83d22d5efe6873fmalita                return;
11297b4d4c7a4668f771641aaed88b83d22d5efe6873fmalita            }
11307b4d4c7a4668f771641aaed88b83d22d5efe6873fmalita
1131c3796c7a74e103d9b367ad9449fcdacfa20d83e1fmalita            // Clamp the bitmap size to about 1M pixels
1132c3796c7a74e103d9b367ad9449fcdacfa20d83e1fmalita            static const SkScalar kMaxBitmapArea = 1024 * 1024;
1133c3796c7a74e103d9b367ad9449fcdacfa20d83e1fmalita            SkScalar bitmapArea = rasterScale * bbox.width() * rasterScale * bbox.height();
1134c3796c7a74e103d9b367ad9449fcdacfa20d83e1fmalita            if (bitmapArea > kMaxBitmapArea) {
113580ea19ca4bdd68c1493666a5fe7e4ce9d43ded8breed                rasterScale *= SkScalarSqrt(kMaxBitmapArea / bitmapArea);
1136c3796c7a74e103d9b367ad9449fcdacfa20d83e1fmalita            }
1137c3796c7a74e103d9b367ad9449fcdacfa20d83e1fmalita
1138c3796c7a74e103d9b367ad9449fcdacfa20d83e1fmalita            SkISize size = SkISize::Make(SkScalarRoundToInt(rasterScale * bbox.width()),
1139c3796c7a74e103d9b367ad9449fcdacfa20d83e1fmalita                                         SkScalarRoundToInt(rasterScale * bbox.height()));
11407b4d4c7a4668f771641aaed88b83d22d5efe6873fmalita            SkSize scale = SkSize::Make(SkIntToScalar(size.width()) / shaderRect.width(),
11417b4d4c7a4668f771641aaed88b83d22d5efe6873fmalita                                        SkIntToScalar(size.height()) / shaderRect.height());
1142c3796c7a74e103d9b367ad9449fcdacfa20d83e1fmalita
1143c3796c7a74e103d9b367ad9449fcdacfa20d83e1fmalita            fImage.allocN32Pixels(size.width(), size.height());
1144c3796c7a74e103d9b367ad9449fcdacfa20d83e1fmalita            fImage.eraseColor(SK_ColorTRANSPARENT);
1145c3796c7a74e103d9b367ad9449fcdacfa20d83e1fmalita
1146c3796c7a74e103d9b367ad9449fcdacfa20d83e1fmalita            SkPaint p;
1147c3796c7a74e103d9b367ad9449fcdacfa20d83e1fmalita            p.setShader(const_cast<SkShader*>(&shader));
1148c3796c7a74e103d9b367ad9449fcdacfa20d83e1fmalita
1149c3796c7a74e103d9b367ad9449fcdacfa20d83e1fmalita            SkCanvas canvas(fImage);
1150c3796c7a74e103d9b367ad9449fcdacfa20d83e1fmalita            canvas.scale(scale.width(), scale.height());
11517b4d4c7a4668f771641aaed88b83d22d5efe6873fmalita            canvas.translate(-shaderRect.x(), -shaderRect.y());
1152c3796c7a74e103d9b367ad9449fcdacfa20d83e1fmalita            canvas.drawPaint(p);
1153c3796c7a74e103d9b367ad9449fcdacfa20d83e1fmalita
11547b4d4c7a4668f771641aaed88b83d22d5efe6873fmalita            fShaderTransform.setTranslate(shaderRect.x(), shaderRect.y());
1155c3796c7a74e103d9b367ad9449fcdacfa20d83e1fmalita            fShaderTransform.preScale(1 / scale.width(), 1 / scale.height());
1156c3796c7a74e103d9b367ad9449fcdacfa20d83e1fmalita        } else {
1157c3796c7a74e103d9b367ad9449fcdacfa20d83e1fmalita            SkASSERT(matrix.isIdentity());
1158da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        }
1159da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        fPixelGeneration = fImage.getGenerationID();
1160da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    } else {
116193a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org        AllocateGradientInfoStorage();
1162da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org        shader.asAGradient(&fInfo);
1163da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org    }
1164da912d61ede86dd3dfa8f645c6f3977f2183812bvandebo@chromium.org}
116593a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
116693a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.orgSkPDFShader::State::State(const SkPDFShader::State& other)
116793a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org  : fType(other.fType),
116893a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    fCanvasTransform(other.fCanvasTransform),
116993a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    fShaderTransform(other.fShaderTransform),
117093a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    fBBox(other.fBBox)
117193a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org{
117293a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    // Only gradients supported for now, since that is all that is used.
117393a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    // If needed, image state copy constructor can be added here later.
117493a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    SkASSERT(fType != SkShader::kNone_GradientType);
117593a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
117693a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    if (fType != SkShader::kNone_GradientType) {
117793a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org        fInfo = other.fInfo;
117893a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
117993a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org        AllocateGradientInfoStorage();
118093a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org        for (int i = 0; i < fInfo.fColorCount; i++) {
118193a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org            fInfo.fColors[i] = other.fInfo.fColors[i];
118293a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org            fInfo.fColorOffsets[i] = other.fInfo.fColorOffsets[i];
118393a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org        }
118493a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    }
118593a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org}
118693a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
118793a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org/**
118893a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org * Create a copy of this gradient state with alpha assigned to RGB luminousity.
118993a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org * Only valid for gradient states.
119093a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org */
119193a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.orgSkPDFShader::State* SkPDFShader::State::CreateAlphaToLuminosityState() const {
119293a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    SkASSERT(fType != SkShader::kNone_GradientType);
119393a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
119493a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    SkPDFShader::State* newState = new SkPDFShader::State(*this);
119593a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
119693a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    for (int i = 0; i < fInfo.fColorCount; i++) {
119793a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org        SkAlpha alpha = SkColorGetA(fInfo.fColors[i]);
119893a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org        newState->fInfo.fColors[i] = SkColorSetARGB(255, alpha, alpha, alpha);
119993a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    }
120093a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
120193a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    return newState;
120293a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org}
120393a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
120493a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org/**
120593a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org * Create a copy of this gradient state with alpha set to fully opaque
120693a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org * Only valid for gradient states.
120793a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org */
120893a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.orgSkPDFShader::State* SkPDFShader::State::CreateOpaqueState() const {
120993a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    SkASSERT(fType != SkShader::kNone_GradientType);
121093a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
121193a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    SkPDFShader::State* newState = new SkPDFShader::State(*this);
121293a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    for (int i = 0; i < fInfo.fColorCount; i++) {
121393a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org        newState->fInfo.fColors[i] = SkColorSetA(fInfo.fColors[i],
121493a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org                                                 SK_AlphaOPAQUE);
121593a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    }
121693a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
121793a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    return newState;
121893a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org}
121993a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
122093a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org/**
122193a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org * Returns true if state is a gradient and the gradient has alpha.
122293a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org */
122393a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.orgbool SkPDFShader::State::GradientHasAlpha() const {
122493a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    if (fType == SkShader::kNone_GradientType) {
122593a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org        return false;
122693a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    }
122793a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
122893a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    for (int i = 0; i < fInfo.fColorCount; i++) {
122993a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org        SkAlpha alpha = SkColorGetA(fInfo.fColors[i]);
123093a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org        if (alpha != SK_AlphaOPAQUE) {
123193a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org            return true;
123293a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org        }
123393a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    }
123493a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    return false;
123593a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org}
123693a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org
123793a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.orgvoid SkPDFShader::State::AllocateGradientInfoStorage() {
123893a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    fColorData.set(sk_malloc_throw(
123993a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org               fInfo.fColorCount * (sizeof(SkColor) + sizeof(SkScalar))));
124093a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    fInfo.fColors = reinterpret_cast<SkColor*>(fColorData.get());
124193a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org    fInfo.fColorOffsets =
124293a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org            reinterpret_cast<SkScalar*>(fInfo.fColors + fInfo.fColorCount);
124393a2e213441c75033b04365c7d68c8d3887288accommit-bot@chromium.org}
1244