gfx/skia/trunk/src/pdf/SkPDFShader.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/skia/trunk/src/pdf/SkPDFShader.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1348 @@
     1.4 +
     1.5 +/*
     1.6 + * Copyright 2011 Google Inc.
     1.7 + *
     1.8 + * Use of this source code is governed by a BSD-style license that can be
     1.9 + * found in the LICENSE file.
    1.10 + */
    1.11 +
    1.12 +
    1.13 +#include "SkPDFShader.h"
    1.14 +
    1.15 +#include "SkData.h"
    1.16 +#include "SkPDFCatalog.h"
    1.17 +#include "SkPDFDevice.h"
    1.18 +#include "SkPDFFormXObject.h"
    1.19 +#include "SkPDFGraphicState.h"
    1.20 +#include "SkPDFResourceDict.h"
    1.21 +#include "SkPDFUtils.h"
    1.22 +#include "SkScalar.h"
    1.23 +#include "SkStream.h"
    1.24 +#include "SkTemplates.h"
    1.25 +#include "SkThread.h"
    1.26 +#include "SkTSet.h"
    1.27 +#include "SkTypes.h"
    1.28 +
    1.29 +static bool inverseTransformBBox(const SkMatrix& matrix, SkRect* bbox) {
    1.30 +    SkMatrix inverse;
    1.31 +    if (!matrix.invert(&inverse)) {
    1.32 +        return false;
    1.33 +    }
    1.34 +    inverse.mapRect(bbox);
    1.35 +    return true;
    1.36 +}
    1.37 +
    1.38 +static void unitToPointsMatrix(const SkPoint pts[2], SkMatrix* matrix) {
    1.39 +    SkVector    vec = pts[1] - pts[0];
    1.40 +    SkScalar    mag = vec.length();
    1.41 +    SkScalar    inv = mag ? SkScalarInvert(mag) : 0;
    1.42 +
    1.43 +    vec.scale(inv);
    1.44 +    matrix->setSinCos(vec.fY, vec.fX);
    1.45 +    matrix->preScale(mag, mag);
    1.46 +    matrix->postTranslate(pts[0].fX, pts[0].fY);
    1.47 +}
    1.48 +
    1.49 +/* Assumes t + startOffset is on the stack and does a linear interpolation on t
    1.50 +   between startOffset and endOffset from prevColor to curColor (for each color
    1.51 +   component), leaving the result in component order on the stack. It assumes
    1.52 +   there are always 3 components per color.
    1.53 +   @param range                  endOffset - startOffset
    1.54 +   @param curColor[components]   The current color components.
    1.55 +   @param prevColor[components]  The previous color components.
    1.56 +   @param result                 The result ps function.
    1.57 + */
    1.58 +static void interpolateColorCode(SkScalar range, SkScalar* curColor,
    1.59 +                                 SkScalar* prevColor, SkString* result) {
    1.60 +    SkASSERT(range != SkIntToScalar(0));
    1.61 +    static const int kColorComponents = 3;
    1.62 +
    1.63 +    // Figure out how to scale each color component.
    1.64 +    SkScalar multiplier[kColorComponents];
    1.65 +    for (int i = 0; i < kColorComponents; i++) {
    1.66 +        multiplier[i] = SkScalarDiv(curColor[i] - prevColor[i], range);
    1.67 +    }
    1.68 +
    1.69 +    // Calculate when we no longer need to keep a copy of the input parameter t.
    1.70 +    // If the last component to use t is i, then dupInput[0..i - 1] = true
    1.71 +    // and dupInput[i .. components] = false.
    1.72 +    bool dupInput[kColorComponents];
    1.73 +    dupInput[kColorComponents - 1] = false;
    1.74 +    for (int i = kColorComponents - 2; i >= 0; i--) {
    1.75 +        dupInput[i] = dupInput[i + 1] || multiplier[i + 1] != 0;
    1.76 +    }
    1.77 +
    1.78 +    if (!dupInput[0] && multiplier[0] == 0) {
    1.79 +        result->append("pop ");
    1.80 +    }
    1.81 +
    1.82 +    for (int i = 0; i < kColorComponents; i++) {
    1.83 +        // If the next components needs t and this component will consume a
    1.84 +        // copy, make another copy.
    1.85 +        if (dupInput[i] && multiplier[i] != 0) {
    1.86 +            result->append("dup ");
    1.87 +        }
    1.88 +
    1.89 +        if (multiplier[i] == 0) {
    1.90 +            result->appendScalar(prevColor[i]);
    1.91 +            result->append(" ");
    1.92 +        } else {
    1.93 +            if (multiplier[i] != 1) {
    1.94 +                result->appendScalar(multiplier[i]);
    1.95 +                result->append(" mul ");
    1.96 +            }
    1.97 +            if (prevColor[i] != 0) {
    1.98 +                result->appendScalar(prevColor[i]);
    1.99 +                result->append(" add ");
   1.100 +            }
   1.101 +        }
   1.102 +
   1.103 +        if (dupInput[i]) {
   1.104 +            result->append("exch\n");
   1.105 +        }
   1.106 +    }
   1.107 +}
   1.108 +
   1.109 +/* Generate Type 4 function code to map t=[0,1) to the passed gradient,
   1.110 +   clamping at the edges of the range.  The generated code will be of the form:
   1.111 +       if (t < 0) {
   1.112 +           return colorData[0][r,g,b];
   1.113 +       } else {
   1.114 +           if (t < info.fColorOffsets[1]) {
   1.115 +               return linearinterpolation(colorData[0][r,g,b],
   1.116 +                                          colorData[1][r,g,b]);
   1.117 +           } else {
   1.118 +               if (t < info.fColorOffsets[2]) {
   1.119 +                   return linearinterpolation(colorData[1][r,g,b],
   1.120 +                                              colorData[2][r,g,b]);
   1.121 +               } else {
   1.122 +
   1.123 +                ...    } else {
   1.124 +                           return colorData[info.fColorCount - 1][r,g,b];
   1.125 +                       }
   1.126 +                ...
   1.127 +           }
   1.128 +       }
   1.129 + */
   1.130 +static void gradientFunctionCode(const SkShader::GradientInfo& info,
   1.131 +                                 SkString* result) {
   1.132 +    /* We want to linearly interpolate from the previous color to the next.
   1.133 +       Scale the colors from 0..255 to 0..1 and determine the multipliers
   1.134 +       for interpolation.
   1.135 +       C{r,g,b}(t, section) = t - offset_(section-1) + t * Multiplier{r,g,b}.
   1.136 +     */
   1.137 +    static const int kColorComponents = 3;
   1.138 +    typedef SkScalar ColorTuple[kColorComponents];
   1.139 +    SkAutoSTMalloc<4, ColorTuple> colorDataAlloc(info.fColorCount);
   1.140 +    ColorTuple *colorData = colorDataAlloc.get();
   1.141 +    const SkScalar scale = SkScalarInvert(SkIntToScalar(255));
   1.142 +    for (int i = 0; i < info.fColorCount; i++) {
   1.143 +        colorData[i][0] = SkScalarMul(SkColorGetR(info.fColors[i]), scale);
   1.144 +        colorData[i][1] = SkScalarMul(SkColorGetG(info.fColors[i]), scale);
   1.145 +        colorData[i][2] = SkScalarMul(SkColorGetB(info.fColors[i]), scale);
   1.146 +    }
   1.147 +
   1.148 +    // Clamp the initial color.
   1.149 +    result->append("dup 0 le {pop ");
   1.150 +    result->appendScalar(colorData[0][0]);
   1.151 +    result->append(" ");
   1.152 +    result->appendScalar(colorData[0][1]);
   1.153 +    result->append(" ");
   1.154 +    result->appendScalar(colorData[0][2]);
   1.155 +    result->append(" }\n");
   1.156 +
   1.157 +    // The gradient colors.
   1.158 +    int gradients = 0;
   1.159 +    for (int i = 1 ; i < info.fColorCount; i++) {
   1.160 +        if (info.fColorOffsets[i] == info.fColorOffsets[i - 1]) {
   1.161 +            continue;
   1.162 +        }
   1.163 +        gradients++;
   1.164 +
   1.165 +        result->append("{dup ");
   1.166 +        result->appendScalar(info.fColorOffsets[i]);
   1.167 +        result->append(" le {");
   1.168 +        if (info.fColorOffsets[i - 1] != 0) {
   1.169 +            result->appendScalar(info.fColorOffsets[i - 1]);
   1.170 +            result->append(" sub\n");
   1.171 +        }
   1.172 +
   1.173 +        interpolateColorCode(info.fColorOffsets[i] - info.fColorOffsets[i - 1],
   1.174 +                             colorData[i], colorData[i - 1], result);
   1.175 +        result->append("}\n");
   1.176 +    }
   1.177 +
   1.178 +    // Clamp the final color.
   1.179 +    result->append("{pop ");
   1.180 +    result->appendScalar(colorData[info.fColorCount - 1][0]);
   1.181 +    result->append(" ");
   1.182 +    result->appendScalar(colorData[info.fColorCount - 1][1]);
   1.183 +    result->append(" ");
   1.184 +    result->appendScalar(colorData[info.fColorCount - 1][2]);
   1.185 +
   1.186 +    for (int i = 0 ; i < gradients + 1; i++) {
   1.187 +        result->append("} ifelse\n");
   1.188 +    }
   1.189 +}
   1.190 +
   1.191 +/* Map a value of t on the stack into [0, 1) for Repeat or Mirror tile mode. */
   1.192 +static void tileModeCode(SkShader::TileMode mode, SkString* result) {
   1.193 +    if (mode == SkShader::kRepeat_TileMode) {
   1.194 +        result->append("dup truncate sub\n");  // Get the fractional part.
   1.195 +        result->append("dup 0 le {1 add} if\n");  // Map (-1,0) => (0,1)
   1.196 +        return;
   1.197 +    }
   1.198 +
   1.199 +    if (mode == SkShader::kMirror_TileMode) {
   1.200 +        // Map t mod 2 into [0, 1, 1, 0].
   1.201 +        //               Code                     Stack
   1.202 +        result->append("abs "                 // Map negative to positive.
   1.203 +                       "dup "                 // t.s t.s
   1.204 +                       "truncate "            // t.s t
   1.205 +                       "dup "                 // t.s t t
   1.206 +                       "cvi "                 // t.s t T
   1.207 +                       "2 mod "               // t.s t (i mod 2)
   1.208 +                       "1 eq "                // t.s t true|false
   1.209 +                       "3 1 roll "            // true|false t.s t
   1.210 +                       "sub "                 // true|false 0.s
   1.211 +                       "exch "                // 0.s true|false
   1.212 +                       "{1 exch sub} if\n");  // 1 - 0.s|0.s
   1.213 +    }
   1.214 +}
   1.215 +
   1.216 +/**
   1.217 + *  Returns PS function code that applies inverse perspective
   1.218 + *  to a x, y point.
   1.219 + *  The function assumes that the stack has at least two elements,
   1.220 + *  and that the top 2 elements are numeric values.
   1.221 + *  After executing this code on a PS stack, the last 2 elements are updated
   1.222 + *  while the rest of the stack is preserved intact.
   1.223 + *  inversePerspectiveMatrix is the inverse perspective matrix.
   1.224 + */
   1.225 +static SkString apply_perspective_to_coordinates(
   1.226 +        const SkMatrix& inversePerspectiveMatrix) {
   1.227 +    SkString code;
   1.228 +    if (!inversePerspectiveMatrix.hasPerspective()) {
   1.229 +        return code;
   1.230 +    }
   1.231 +
   1.232 +    // Perspective matrix should be:
   1.233 +    // 1   0  0
   1.234 +    // 0   1  0
   1.235 +    // p0 p1 p2
   1.236 +
   1.237 +    const SkScalar p0 = inversePerspectiveMatrix[SkMatrix::kMPersp0];
   1.238 +    const SkScalar p1 = inversePerspectiveMatrix[SkMatrix::kMPersp1];
   1.239 +    const SkScalar p2 = inversePerspectiveMatrix[SkMatrix::kMPersp2];
   1.240 +
   1.241 +    // y = y / (p2 + p0 x + p1 y)
   1.242 +    // x = x / (p2 + p0 x + p1 y)
   1.243 +
   1.244 +    // Input on stack: x y
   1.245 +    code.append(" dup ");               // x y y
   1.246 +    code.appendScalar(p1);              // x y y p1
   1.247 +    code.append(" mul "                 // x y y*p1
   1.248 +                " 2 index ");           // x y y*p1 x
   1.249 +    code.appendScalar(p0);              // x y y p1 x p0
   1.250 +    code.append(" mul ");               // x y y*p1 x*p0
   1.251 +    code.appendScalar(p2);              // x y y p1 x*p0 p2
   1.252 +    code.append(" add "                 // x y y*p1 x*p0+p2
   1.253 +                "add "                  // x y y*p1+x*p0+p2
   1.254 +                "3 1 roll "             // y*p1+x*p0+p2 x y
   1.255 +                "2 index "              // z x y y*p1+x*p0+p2
   1.256 +                "div "                  // y*p1+x*p0+p2 x y/(y*p1+x*p0+p2)
   1.257 +                "3 1 roll "             // y/(y*p1+x*p0+p2) y*p1+x*p0+p2 x
   1.258 +                "exch "                 // y/(y*p1+x*p0+p2) x y*p1+x*p0+p2
   1.259 +                "div "                  // y/(y*p1+x*p0+p2) x/(y*p1+x*p0+p2)
   1.260 +                "exch\n");              // x/(y*p1+x*p0+p2) y/(y*p1+x*p0+p2)
   1.261 +    return code;
   1.262 +}
   1.263 +
   1.264 +static SkString linearCode(const SkShader::GradientInfo& info,
   1.265 +                           const SkMatrix& perspectiveRemover) {
   1.266 +    SkString function("{");
   1.267 +
   1.268 +    function.append(apply_perspective_to_coordinates(perspectiveRemover));
   1.269 +
   1.270 +    function.append("pop\n");  // Just ditch the y value.
   1.271 +    tileModeCode(info.fTileMode, &function);
   1.272 +    gradientFunctionCode(info, &function);
   1.273 +    function.append("}");
   1.274 +    return function;
   1.275 +}
   1.276 +
   1.277 +static SkString radialCode(const SkShader::GradientInfo& info,
   1.278 +                           const SkMatrix& perspectiveRemover) {
   1.279 +    SkString function("{");
   1.280 +
   1.281 +    function.append(apply_perspective_to_coordinates(perspectiveRemover));
   1.282 +
   1.283 +    // Find the distance from the origin.
   1.284 +    function.append("dup "      // x y y
   1.285 +                    "mul "      // x y^2
   1.286 +                    "exch "     // y^2 x
   1.287 +                    "dup "      // y^2 x x
   1.288 +                    "mul "      // y^2 x^2
   1.289 +                    "add "      // y^2+x^2
   1.290 +                    "sqrt\n");  // sqrt(y^2+x^2)
   1.291 +
   1.292 +    tileModeCode(info.fTileMode, &function);
   1.293 +    gradientFunctionCode(info, &function);
   1.294 +    function.append("}");
   1.295 +    return function;
   1.296 +}
   1.297 +
   1.298 +/* The math here is all based on the description in Two_Point_Radial_Gradient,
   1.299 +   with one simplification, the coordinate space has been scaled so that
   1.300 +   Dr = 1.  This means we don't need to scale the entire equation by 1/Dr^2.
   1.301 + */
   1.302 +static SkString twoPointRadialCode(const SkShader::GradientInfo& info,
   1.303 +                                   const SkMatrix& perspectiveRemover) {
   1.304 +    SkScalar dx = info.fPoint[0].fX - info.fPoint[1].fX;
   1.305 +    SkScalar dy = info.fPoint[0].fY - info.fPoint[1].fY;
   1.306 +    SkScalar sr = info.fRadius[0];
   1.307 +    SkScalar a = SkScalarMul(dx, dx) + SkScalarMul(dy, dy) - SK_Scalar1;
   1.308 +    bool posRoot = info.fRadius[1] > info.fRadius[0];
   1.309 +
   1.310 +    // We start with a stack of (x y), copy it and then consume one copy in
   1.311 +    // order to calculate b and the other to calculate c.
   1.312 +    SkString function("{");
   1.313 +
   1.314 +    function.append(apply_perspective_to_coordinates(perspectiveRemover));
   1.315 +
   1.316 +    function.append("2 copy ");
   1.317 +
   1.318 +    // Calculate -b and b^2.
   1.319 +    function.appendScalar(dy);
   1.320 +    function.append(" mul exch ");
   1.321 +    function.appendScalar(dx);
   1.322 +    function.append(" mul add ");
   1.323 +    function.appendScalar(sr);
   1.324 +    function.append(" sub 2 mul neg dup dup mul\n");
   1.325 +
   1.326 +    // Calculate c
   1.327 +    function.append("4 2 roll dup mul exch dup mul add ");
   1.328 +    function.appendScalar(SkScalarMul(sr, sr));
   1.329 +    function.append(" sub\n");
   1.330 +
   1.331 +    // Calculate the determinate
   1.332 +    function.appendScalar(SkScalarMul(SkIntToScalar(4), a));
   1.333 +    function.append(" mul sub abs sqrt\n");
   1.334 +
   1.335 +    // And then the final value of t.
   1.336 +    if (posRoot) {
   1.337 +        function.append("sub ");
   1.338 +    } else {
   1.339 +        function.append("add ");
   1.340 +    }
   1.341 +    function.appendScalar(SkScalarMul(SkIntToScalar(2), a));
   1.342 +    function.append(" div\n");
   1.343 +
   1.344 +    tileModeCode(info.fTileMode, &function);
   1.345 +    gradientFunctionCode(info, &function);
   1.346 +    function.append("}");
   1.347 +    return function;
   1.348 +}
   1.349 +
   1.350 +/* Conical gradient shader, based on the Canvas spec for radial gradients
   1.351 +   See: http://www.w3.org/TR/2dcontext/#dom-context-2d-createradialgradient
   1.352 + */
   1.353 +static SkString twoPointConicalCode(const SkShader::GradientInfo& info,
   1.354 +                                    const SkMatrix& perspectiveRemover) {
   1.355 +    SkScalar dx = info.fPoint[1].fX - info.fPoint[0].fX;
   1.356 +    SkScalar dy = info.fPoint[1].fY - info.fPoint[0].fY;
   1.357 +    SkScalar r0 = info.fRadius[0];
   1.358 +    SkScalar dr = info.fRadius[1] - info.fRadius[0];
   1.359 +    SkScalar a = SkScalarMul(dx, dx) + SkScalarMul(dy, dy) -
   1.360 +                 SkScalarMul(dr, dr);
   1.361 +
   1.362 +    // First compute t, if the pixel falls outside the cone, then we'll end
   1.363 +    // with 'false' on the stack, otherwise we'll push 'true' with t below it
   1.364 +
   1.365 +    // We start with a stack of (x y), copy it and then consume one copy in
   1.366 +    // order to calculate b and the other to calculate c.
   1.367 +    SkString function("{");
   1.368 +
   1.369 +    function.append(apply_perspective_to_coordinates(perspectiveRemover));
   1.370 +
   1.371 +    function.append("2 copy ");
   1.372 +
   1.373 +    // Calculate b and b^2; b = -2 * (y * dy + x * dx + r0 * dr).
   1.374 +    function.appendScalar(dy);
   1.375 +    function.append(" mul exch ");
   1.376 +    function.appendScalar(dx);
   1.377 +    function.append(" mul add ");
   1.378 +    function.appendScalar(SkScalarMul(r0, dr));
   1.379 +    function.append(" add -2 mul dup dup mul\n");
   1.380 +
   1.381 +    // c = x^2 + y^2 + radius0^2
   1.382 +    function.append("4 2 roll dup mul exch dup mul add ");
   1.383 +    function.appendScalar(SkScalarMul(r0, r0));
   1.384 +    function.append(" sub dup 4 1 roll\n");
   1.385 +
   1.386 +    // Contents of the stack at this point: c, b, b^2, c
   1.387 +
   1.388 +    // if a = 0, then we collapse to a simpler linear case
   1.389 +    if (a == 0) {
   1.390 +
   1.391 +        // t = -c/b
   1.392 +        function.append("pop pop div neg dup ");
   1.393 +
   1.394 +        // compute radius(t)
   1.395 +        function.appendScalar(dr);
   1.396 +        function.append(" mul ");
   1.397 +        function.appendScalar(r0);
   1.398 +        function.append(" add\n");
   1.399 +
   1.400 +        // if r(t) < 0, then it's outside the cone
   1.401 +        function.append("0 lt {pop false} {true} ifelse\n");
   1.402 +
   1.403 +    } else {
   1.404 +
   1.405 +        // quadratic case: the Canvas spec wants the largest
   1.406 +        // root t for which radius(t) > 0
   1.407 +
   1.408 +        // compute the discriminant (b^2 - 4ac)
   1.409 +        function.appendScalar(SkScalarMul(SkIntToScalar(4), a));
   1.410 +        function.append(" mul sub dup\n");
   1.411 +
   1.412 +        // if d >= 0, proceed
   1.413 +        function.append("0 ge {\n");
   1.414 +
   1.415 +        // an intermediate value we'll use to compute the roots:
   1.416 +        // q = -0.5 * (b +/- sqrt(d))
   1.417 +        function.append("sqrt exch dup 0 lt {exch -1 mul} if");
   1.418 +        function.append(" add -0.5 mul dup\n");
   1.419 +
   1.420 +        // first root = q / a
   1.421 +        function.appendScalar(a);
   1.422 +        function.append(" div\n");
   1.423 +
   1.424 +        // second root = c / q
   1.425 +        function.append("3 1 roll div\n");
   1.426 +
   1.427 +        // put the larger root on top of the stack
   1.428 +        function.append("2 copy gt {exch} if\n");
   1.429 +
   1.430 +        // compute radius(t) for larger root
   1.431 +        function.append("dup ");
   1.432 +        function.appendScalar(dr);
   1.433 +        function.append(" mul ");
   1.434 +        function.appendScalar(r0);
   1.435 +        function.append(" add\n");
   1.436 +
   1.437 +        // if r(t) > 0, we have our t, pop off the smaller root and we're done
   1.438 +        function.append(" 0 gt {exch pop true}\n");
   1.439 +
   1.440 +        // otherwise, throw out the larger one and try the smaller root
   1.441 +        function.append("{pop dup\n");
   1.442 +        function.appendScalar(dr);
   1.443 +        function.append(" mul ");
   1.444 +        function.appendScalar(r0);
   1.445 +        function.append(" add\n");
   1.446 +
   1.447 +        // if r(t) < 0, push false, otherwise the smaller root is our t
   1.448 +        function.append("0 le {pop false} {true} ifelse\n");
   1.449 +        function.append("} ifelse\n");
   1.450 +
   1.451 +        // d < 0, clear the stack and push false
   1.452 +        function.append("} {pop pop pop false} ifelse\n");
   1.453 +    }
   1.454 +
   1.455 +    // if the pixel is in the cone, proceed to compute a color
   1.456 +    function.append("{");
   1.457 +    tileModeCode(info.fTileMode, &function);
   1.458 +    gradientFunctionCode(info, &function);
   1.459 +
   1.460 +    // otherwise, just write black
   1.461 +    function.append("} {0 0 0} ifelse }");
   1.462 +
   1.463 +    return function;
   1.464 +}
   1.465 +
   1.466 +static SkString sweepCode(const SkShader::GradientInfo& info,
   1.467 +                          const SkMatrix& perspectiveRemover) {
   1.468 +    SkString function("{exch atan 360 div\n");
   1.469 +    tileModeCode(info.fTileMode, &function);
   1.470 +    gradientFunctionCode(info, &function);
   1.471 +    function.append("}");
   1.472 +    return function;
   1.473 +}
   1.474 +
   1.475 +class SkPDFShader::State {
   1.476 +public:
   1.477 +    SkShader::GradientType fType;
   1.478 +    SkShader::GradientInfo fInfo;
   1.479 +    SkAutoFree fColorData;    // This provides storage for arrays in fInfo.
   1.480 +    SkMatrix fCanvasTransform;
   1.481 +    SkMatrix fShaderTransform;
   1.482 +    SkIRect fBBox;
   1.483 +
   1.484 +    SkBitmap fImage;
   1.485 +    uint32_t fPixelGeneration;
   1.486 +    SkShader::TileMode fImageTileModes[2];
   1.487 +
   1.488 +    State(const SkShader& shader, const SkMatrix& canvasTransform,
   1.489 +          const SkIRect& bbox);
   1.490 +
   1.491 +    bool operator==(const State& b) const;
   1.492 +
   1.493 +    SkPDFShader::State* CreateAlphaToLuminosityState() const;
   1.494 +    SkPDFShader::State* CreateOpaqueState() const;
   1.495 +
   1.496 +    bool GradientHasAlpha() const;
   1.497 +
   1.498 +private:
   1.499 +    State(const State& other);
   1.500 +    State operator=(const State& rhs);
   1.501 +    void AllocateGradientInfoStorage();
   1.502 +};
   1.503 +
   1.504 +class SkPDFFunctionShader : public SkPDFDict, public SkPDFShader {
   1.505 +    SK_DECLARE_INST_COUNT(SkPDFFunctionShader)
   1.506 +public:
   1.507 +    explicit SkPDFFunctionShader(SkPDFShader::State* state);
   1.508 +    virtual ~SkPDFFunctionShader() {
   1.509 +        if (isValid()) {
   1.510 +            RemoveShader(this);
   1.511 +        }
   1.512 +        fResources.unrefAll();
   1.513 +    }
   1.514 +
   1.515 +    virtual bool isValid() { return fResources.count() > 0; }
   1.516 +
   1.517 +    void getResources(const SkTSet<SkPDFObject*>& knownResourceObjects,
   1.518 +                      SkTSet<SkPDFObject*>* newResourceObjects) {
   1.519 +        GetResourcesHelper(&fResources,
   1.520 +                           knownResourceObjects,
   1.521 +                           newResourceObjects);
   1.522 +    }
   1.523 +
   1.524 +private:
   1.525 +    static SkPDFObject* RangeObject();
   1.526 +
   1.527 +    SkTDArray<SkPDFObject*> fResources;
   1.528 +    SkAutoTDelete<const SkPDFShader::State> fState;
   1.529 +
   1.530 +    SkPDFStream* makePSFunction(const SkString& psCode, SkPDFArray* domain);
   1.531 +    typedef SkPDFDict INHERITED;
   1.532 +};
   1.533 +
   1.534 +/**
   1.535 + * A shader for PDF gradients. This encapsulates the function shader
   1.536 + * inside a tiling pattern while providing a common pattern interface.
   1.537 + * The encapsulation allows the use of a SMask for transparency gradients.
   1.538 + */
   1.539 +class SkPDFAlphaFunctionShader : public SkPDFStream, public SkPDFShader {
   1.540 +public:
   1.541 +    explicit SkPDFAlphaFunctionShader(SkPDFShader::State* state);
   1.542 +    virtual ~SkPDFAlphaFunctionShader() {
   1.543 +        if (isValid()) {
   1.544 +            RemoveShader(this);
   1.545 +        }
   1.546 +    }
   1.547 +
   1.548 +    virtual bool isValid() {
   1.549 +        return fColorShader.get() != NULL;
   1.550 +    }
   1.551 +
   1.552 +private:
   1.553 +    SkAutoTDelete<const SkPDFShader::State> fState;
   1.554 +
   1.555 +    SkPDFGraphicState* CreateSMaskGraphicState();
   1.556 +
   1.557 +    void getResources(const SkTSet<SkPDFObject*>& knownResourceObjects,
   1.558 +                      SkTSet<SkPDFObject*>* newResourceObjects) {
   1.559 +        fResourceDict->getReferencedResources(knownResourceObjects,
   1.560 +                                              newResourceObjects,
   1.561 +                                              true);
   1.562 +    }
   1.563 +
   1.564 +    SkAutoTUnref<SkPDFObject> fColorShader;
   1.565 +    SkAutoTUnref<SkPDFResourceDict> fResourceDict;
   1.566 +};
   1.567 +
   1.568 +class SkPDFImageShader : public SkPDFStream, public SkPDFShader {
   1.569 +public:
   1.570 +    explicit SkPDFImageShader(SkPDFShader::State* state);
   1.571 +    virtual ~SkPDFImageShader() {
   1.572 +        if (isValid()) {
   1.573 +            RemoveShader(this);
   1.574 +        }
   1.575 +        fResources.unrefAll();
   1.576 +    }
   1.577 +
   1.578 +    virtual bool isValid() { return size() > 0; }
   1.579 +
   1.580 +    void getResources(const SkTSet<SkPDFObject*>& knownResourceObjects,
   1.581 +                      SkTSet<SkPDFObject*>* newResourceObjects) {
   1.582 +        GetResourcesHelper(&fResources.toArray(),
   1.583 +                           knownResourceObjects,
   1.584 +                           newResourceObjects);
   1.585 +    }
   1.586 +
   1.587 +private:
   1.588 +    SkTSet<SkPDFObject*> fResources;
   1.589 +    SkAutoTDelete<const SkPDFShader::State> fState;
   1.590 +};
   1.591 +
   1.592 +SkPDFShader::SkPDFShader() {}
   1.593 +
   1.594 +// static
   1.595 +SkPDFObject* SkPDFShader::GetPDFShaderByState(State* inState) {
   1.596 +    SkPDFObject* result;
   1.597 +
   1.598 +    SkAutoTDelete<State> shaderState(inState);
   1.599 +    if (shaderState.get()->fType == SkShader::kNone_GradientType &&
   1.600 +            shaderState.get()->fImage.isNull()) {
   1.601 +        // TODO(vandebo) This drops SKComposeShader on the floor.  We could
   1.602 +        // handle compose shader by pulling things up to a layer, drawing with
   1.603 +        // the first shader, applying the xfer mode and drawing again with the
   1.604 +        // second shader, then applying the layer to the original drawing.
   1.605 +        return NULL;
   1.606 +    }
   1.607 +
   1.608 +    ShaderCanonicalEntry entry(NULL, shaderState.get());
   1.609 +    int index = CanonicalShaders().find(entry);
   1.610 +    if (index >= 0) {
   1.611 +        result = CanonicalShaders()[index].fPDFShader;
   1.612 +        result->ref();
   1.613 +        return result;
   1.614 +    }
   1.615 +
   1.616 +    bool valid = false;
   1.617 +    // The PDFShader takes ownership of the shaderSate.
   1.618 +    if (shaderState.get()->fType == SkShader::kNone_GradientType) {
   1.619 +        SkPDFImageShader* imageShader =
   1.620 +            new SkPDFImageShader(shaderState.detach());
   1.621 +        valid = imageShader->isValid();
   1.622 +        result = imageShader;
   1.623 +    } else {
   1.624 +        if (shaderState.get()->GradientHasAlpha()) {
   1.625 +            SkPDFAlphaFunctionShader* gradientShader =
   1.626 +                SkNEW_ARGS(SkPDFAlphaFunctionShader, (shaderState.detach()));
   1.627 +            valid = gradientShader->isValid();
   1.628 +            result = gradientShader;
   1.629 +        } else {
   1.630 +            SkPDFFunctionShader* functionShader =
   1.631 +                SkNEW_ARGS(SkPDFFunctionShader, (shaderState.detach()));
   1.632 +            valid = functionShader->isValid();
   1.633 +            result = functionShader;
   1.634 +        }
   1.635 +    }
   1.636 +    if (!valid) {
   1.637 +        delete result;
   1.638 +        return NULL;
   1.639 +    }
   1.640 +    entry.fPDFShader = result;
   1.641 +    CanonicalShaders().push(entry);
   1.642 +    return result;  // return the reference that came from new.
   1.643 +}
   1.644 +
   1.645 +// static
   1.646 +void SkPDFShader::RemoveShader(SkPDFObject* shader) {
   1.647 +    SkAutoMutexAcquire lock(CanonicalShadersMutex());
   1.648 +    ShaderCanonicalEntry entry(shader, NULL);
   1.649 +    int index = CanonicalShaders().find(entry);
   1.650 +    SkASSERT(index >= 0);
   1.651 +    CanonicalShaders().removeShuffle(index);
   1.652 +}
   1.653 +
   1.654 +// static
   1.655 +SkPDFObject* SkPDFShader::GetPDFShader(const SkShader& shader,
   1.656 +                                       const SkMatrix& matrix,
   1.657 +                                       const SkIRect& surfaceBBox) {
   1.658 +    SkAutoMutexAcquire lock(CanonicalShadersMutex());
   1.659 +    return GetPDFShaderByState(
   1.660 +            SkNEW_ARGS(State, (shader, matrix, surfaceBBox)));
   1.661 +}
   1.662 +
   1.663 +// static
   1.664 +SkTDArray<SkPDFShader::ShaderCanonicalEntry>& SkPDFShader::CanonicalShaders() {
   1.665 +    // This initialization is only thread safe with gcc.
   1.666 +    static SkTDArray<ShaderCanonicalEntry> gCanonicalShaders;
   1.667 +    return gCanonicalShaders;
   1.668 +}
   1.669 +
   1.670 +// static
   1.671 +SkBaseMutex& SkPDFShader::CanonicalShadersMutex() {
   1.672 +    // This initialization is only thread safe with gcc or when
   1.673 +    // POD-style mutex initialization is used.
   1.674 +    SK_DECLARE_STATIC_MUTEX(gCanonicalShadersMutex);
   1.675 +    return gCanonicalShadersMutex;
   1.676 +}
   1.677 +
   1.678 +// static
   1.679 +SkPDFObject* SkPDFFunctionShader::RangeObject() {
   1.680 +    // This initialization is only thread safe with gcc.
   1.681 +    static SkPDFArray* range = NULL;
   1.682 +    // This method is only used with CanonicalShadersMutex, so it's safe to
   1.683 +    // populate domain.
   1.684 +    if (range == NULL) {
   1.685 +        range = new SkPDFArray;
   1.686 +        range->reserve(6);
   1.687 +        range->appendInt(0);
   1.688 +        range->appendInt(1);
   1.689 +        range->appendInt(0);
   1.690 +        range->appendInt(1);
   1.691 +        range->appendInt(0);
   1.692 +        range->appendInt(1);
   1.693 +    }
   1.694 +    return range;
   1.695 +}
   1.696 +
   1.697 +static SkPDFResourceDict* get_gradient_resource_dict(
   1.698 +        SkPDFObject* functionShader,
   1.699 +        SkPDFObject* gState) {
   1.700 +    SkPDFResourceDict* dict = new SkPDFResourceDict();
   1.701 +
   1.702 +    if (functionShader != NULL) {
   1.703 +        dict->insertResourceAsReference(
   1.704 +                SkPDFResourceDict::kPattern_ResourceType, 0, functionShader);
   1.705 +    }
   1.706 +    if (gState != NULL) {
   1.707 +        dict->insertResourceAsReference(
   1.708 +                SkPDFResourceDict::kExtGState_ResourceType, 0, gState);
   1.709 +    }
   1.710 +
   1.711 +    return dict;
   1.712 +}
   1.713 +
   1.714 +static void populate_tiling_pattern_dict(SkPDFDict* pattern,
   1.715 +                                      SkRect& bbox, SkPDFDict* resources,
   1.716 +                                      const SkMatrix& matrix) {
   1.717 +    const int kTiling_PatternType = 1;
   1.718 +    const int kColoredTilingPattern_PaintType = 1;
   1.719 +    const int kConstantSpacing_TilingType = 1;
   1.720 +
   1.721 +    pattern->insertName("Type", "Pattern");
   1.722 +    pattern->insertInt("PatternType", kTiling_PatternType);
   1.723 +    pattern->insertInt("PaintType", kColoredTilingPattern_PaintType);
   1.724 +    pattern->insertInt("TilingType", kConstantSpacing_TilingType);
   1.725 +    pattern->insert("BBox", SkPDFUtils::RectToArray(bbox))->unref();
   1.726 +    pattern->insertScalar("XStep", bbox.width());
   1.727 +    pattern->insertScalar("YStep", bbox.height());
   1.728 +    pattern->insert("Resources", resources);
   1.729 +    if (!matrix.isIdentity()) {
   1.730 +        pattern->insert("Matrix", SkPDFUtils::MatrixToArray(matrix))->unref();
   1.731 +    }
   1.732 +}
   1.733 +
   1.734 +/**
   1.735 + * Creates a content stream which fills the pattern P0 across bounds.
   1.736 + * @param gsIndex A graphics state resource index to apply, or <0 if no
   1.737 + * graphics state to apply.
   1.738 + */
   1.739 +static SkStream* create_pattern_fill_content(int gsIndex, SkRect& bounds) {
   1.740 +    SkDynamicMemoryWStream content;
   1.741 +    if (gsIndex >= 0) {
   1.742 +        SkPDFUtils::ApplyGraphicState(gsIndex, &content);
   1.743 +    }
   1.744 +    SkPDFUtils::ApplyPattern(0, &content);
   1.745 +    SkPDFUtils::AppendRectangle(bounds, &content);
   1.746 +    SkPDFUtils::PaintPath(SkPaint::kFill_Style, SkPath::kEvenOdd_FillType,
   1.747 +                          &content);
   1.748 +
   1.749 +    return content.detachAsStream();
   1.750 +}
   1.751 +
   1.752 +/**
   1.753 + * Creates a ExtGState with the SMask set to the luminosityShader in
   1.754 + * luminosity mode. The shader pattern extends to the bbox.
   1.755 + */
   1.756 +SkPDFGraphicState* SkPDFAlphaFunctionShader::CreateSMaskGraphicState() {
   1.757 +    SkRect bbox;
   1.758 +    bbox.set(fState.get()->fBBox);
   1.759 +
   1.760 +    SkAutoTUnref<SkPDFObject> luminosityShader(
   1.761 +            SkPDFShader::GetPDFShaderByState(
   1.762 +                 fState->CreateAlphaToLuminosityState()));
   1.763 +
   1.764 +    SkAutoTUnref<SkStream> alphaStream(create_pattern_fill_content(-1, bbox));
   1.765 +
   1.766 +    SkAutoTUnref<SkPDFResourceDict>
   1.767 +        resources(get_gradient_resource_dict(luminosityShader, NULL));
   1.768 +
   1.769 +    SkAutoTUnref<SkPDFFormXObject> alphaMask(
   1.770 +            new SkPDFFormXObject(alphaStream.get(), bbox, resources.get()));
   1.771 +
   1.772 +    return SkPDFGraphicState::GetSMaskGraphicState(
   1.773 +            alphaMask.get(), false,
   1.774 +            SkPDFGraphicState::kLuminosity_SMaskMode);
   1.775 +}
   1.776 +
   1.777 +SkPDFAlphaFunctionShader::SkPDFAlphaFunctionShader(SkPDFShader::State* state)
   1.778 +        : fState(state) {
   1.779 +    SkRect bbox;
   1.780 +    bbox.set(fState.get()->fBBox);
   1.781 +
   1.782 +    fColorShader.reset(
   1.783 +            SkPDFShader::GetPDFShaderByState(state->CreateOpaqueState()));
   1.784 +
   1.785 +    // Create resource dict with alpha graphics state as G0 and
   1.786 +    // pattern shader as P0, then write content stream.
   1.787 +    SkAutoTUnref<SkPDFGraphicState> alphaGs(CreateSMaskGraphicState());
   1.788 +    fResourceDict.reset(
   1.789 +            get_gradient_resource_dict(fColorShader.get(), alphaGs.get()));
   1.790 +
   1.791 +    SkAutoTUnref<SkStream> colorStream(
   1.792 +            create_pattern_fill_content(0, bbox));
   1.793 +    setData(colorStream.get());
   1.794 +
   1.795 +    populate_tiling_pattern_dict(this, bbox, fResourceDict.get(),
   1.796 +                                 SkMatrix::I());
   1.797 +}
   1.798 +
   1.799 +// Finds affine and persp such that in = affine * persp.
   1.800 +// but it returns the inverse of perspective matrix.
   1.801 +static bool split_perspective(const SkMatrix in, SkMatrix* affine,
   1.802 +                              SkMatrix* perspectiveInverse) {
   1.803 +    const SkScalar p2 = in[SkMatrix::kMPersp2];
   1.804 +
   1.805 +    if (SkScalarNearlyZero(p2)) {
   1.806 +        return false;
   1.807 +    }
   1.808 +
   1.809 +    const SkScalar zero = SkIntToScalar(0);
   1.810 +    const SkScalar one = SkIntToScalar(1);
   1.811 +
   1.812 +    const SkScalar sx = in[SkMatrix::kMScaleX];
   1.813 +    const SkScalar kx = in[SkMatrix::kMSkewX];
   1.814 +    const SkScalar tx = in[SkMatrix::kMTransX];
   1.815 +    const SkScalar ky = in[SkMatrix::kMSkewY];
   1.816 +    const SkScalar sy = in[SkMatrix::kMScaleY];
   1.817 +    const SkScalar ty = in[SkMatrix::kMTransY];
   1.818 +    const SkScalar p0 = in[SkMatrix::kMPersp0];
   1.819 +    const SkScalar p1 = in[SkMatrix::kMPersp1];
   1.820 +
   1.821 +    // Perspective matrix would be:
   1.822 +    // 1  0  0
   1.823 +    // 0  1  0
   1.824 +    // p0 p1 p2
   1.825 +    // But we need the inverse of persp.
   1.826 +    perspectiveInverse->setAll(one,          zero,       zero,
   1.827 +                               zero,         one,        zero,
   1.828 +                               -p0/p2,     -p1/p2,     1/p2);
   1.829 +
   1.830 +    affine->setAll(sx - p0 * tx / p2,       kx - p1 * tx / p2,      tx / p2,
   1.831 +                   ky - p0 * ty / p2,       sy - p1 * ty / p2,      ty / p2,
   1.832 +                   zero,                    zero,                   one);
   1.833 +
   1.834 +    return true;
   1.835 +}
   1.836 +
   1.837 +SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state)
   1.838 +        : SkPDFDict("Pattern"),
   1.839 +          fState(state) {
   1.840 +    SkString (*codeFunction)(const SkShader::GradientInfo& info,
   1.841 +                             const SkMatrix& perspectiveRemover) = NULL;
   1.842 +    SkPoint transformPoints[2];
   1.843 +
   1.844 +    // Depending on the type of the gradient, we want to transform the
   1.845 +    // coordinate space in different ways.
   1.846 +    const SkShader::GradientInfo* info = &fState.get()->fInfo;
   1.847 +    transformPoints[0] = info->fPoint[0];
   1.848 +    transformPoints[1] = info->fPoint[1];
   1.849 +    switch (fState.get()->fType) {
   1.850 +        case SkShader::kLinear_GradientType:
   1.851 +            codeFunction = &linearCode;
   1.852 +            break;
   1.853 +        case SkShader::kRadial_GradientType:
   1.854 +            transformPoints[1] = transformPoints[0];
   1.855 +            transformPoints[1].fX += info->fRadius[0];
   1.856 +            codeFunction = &radialCode;
   1.857 +            break;
   1.858 +        case SkShader::kRadial2_GradientType: {
   1.859 +            // Bail out if the radii are the same.  Empty fResources signals
   1.860 +            // an error and isValid will return false.
   1.861 +            if (info->fRadius[0] == info->fRadius[1]) {
   1.862 +                return;
   1.863 +            }
   1.864 +            transformPoints[1] = transformPoints[0];
   1.865 +            SkScalar dr = info->fRadius[1] - info->fRadius[0];
   1.866 +            transformPoints[1].fX += dr;
   1.867 +            codeFunction = &twoPointRadialCode;
   1.868 +            break;
   1.869 +        }
   1.870 +        case SkShader::kConical_GradientType: {
   1.871 +            transformPoints[1] = transformPoints[0];
   1.872 +            transformPoints[1].fX += SK_Scalar1;
   1.873 +            codeFunction = &twoPointConicalCode;
   1.874 +            break;
   1.875 +        }
   1.876 +        case SkShader::kSweep_GradientType:
   1.877 +            transformPoints[1] = transformPoints[0];
   1.878 +            transformPoints[1].fX += SK_Scalar1;
   1.879 +            codeFunction = &sweepCode;
   1.880 +            break;
   1.881 +        case SkShader::kColor_GradientType:
   1.882 +        case SkShader::kNone_GradientType:
   1.883 +        default:
   1.884 +            return;
   1.885 +    }
   1.886 +
   1.887 +    // Move any scaling (assuming a unit gradient) or translation
   1.888 +    // (and rotation for linear gradient), of the final gradient from
   1.889 +    // info->fPoints to the matrix (updating bbox appropriately).  Now
   1.890 +    // the gradient can be drawn on on the unit segment.
   1.891 +    SkMatrix mapperMatrix;
   1.892 +    unitToPointsMatrix(transformPoints, &mapperMatrix);
   1.893 +
   1.894 +    SkMatrix finalMatrix = fState.get()->fCanvasTransform;
   1.895 +    finalMatrix.preConcat(fState.get()->fShaderTransform);
   1.896 +    finalMatrix.preConcat(mapperMatrix);
   1.897 +
   1.898 +    // Preserves as much as posible in the final matrix, and only removes
   1.899 +    // the perspective. The inverse of the perspective is stored in
   1.900 +    // perspectiveInverseOnly matrix and has 3 useful numbers
   1.901 +    // (p0, p1, p2), while everything else is either 0 or 1.
   1.902 +    // In this way the shader will handle it eficiently, with minimal code.
   1.903 +    SkMatrix perspectiveInverseOnly = SkMatrix::I();
   1.904 +    if (finalMatrix.hasPerspective()) {
   1.905 +        if (!split_perspective(finalMatrix,
   1.906 +                               &finalMatrix, &perspectiveInverseOnly)) {
   1.907 +            return;
   1.908 +        }
   1.909 +    }
   1.910 +
   1.911 +    SkRect bbox;
   1.912 +    bbox.set(fState.get()->fBBox);
   1.913 +    if (!inverseTransformBBox(finalMatrix, &bbox)) {
   1.914 +        return;
   1.915 +    }
   1.916 +
   1.917 +    SkAutoTUnref<SkPDFArray> domain(new SkPDFArray);
   1.918 +    domain->reserve(4);
   1.919 +    domain->appendScalar(bbox.fLeft);
   1.920 +    domain->appendScalar(bbox.fRight);
   1.921 +    domain->appendScalar(bbox.fTop);
   1.922 +    domain->appendScalar(bbox.fBottom);
   1.923 +
   1.924 +    SkString functionCode;
   1.925 +    // The two point radial gradient further references fState.get()->fInfo
   1.926 +    // in translating from x, y coordinates to the t parameter. So, we have
   1.927 +    // to transform the points and radii according to the calculated matrix.
   1.928 +    if (fState.get()->fType == SkShader::kRadial2_GradientType) {
   1.929 +        SkShader::GradientInfo twoPointRadialInfo = *info;
   1.930 +        SkMatrix inverseMapperMatrix;
   1.931 +        if (!mapperMatrix.invert(&inverseMapperMatrix)) {
   1.932 +            return;
   1.933 +        }
   1.934 +        inverseMapperMatrix.mapPoints(twoPointRadialInfo.fPoint, 2);
   1.935 +        twoPointRadialInfo.fRadius[0] =
   1.936 +            inverseMapperMatrix.mapRadius(info->fRadius[0]);
   1.937 +        twoPointRadialInfo.fRadius[1] =
   1.938 +            inverseMapperMatrix.mapRadius(info->fRadius[1]);
   1.939 +        functionCode = codeFunction(twoPointRadialInfo, perspectiveInverseOnly);
   1.940 +    } else {
   1.941 +        functionCode = codeFunction(*info, perspectiveInverseOnly);
   1.942 +    }
   1.943 +
   1.944 +    SkAutoTUnref<SkPDFDict> pdfShader(new SkPDFDict);
   1.945 +    pdfShader->insertInt("ShadingType", 1);
   1.946 +    pdfShader->insertName("ColorSpace", "DeviceRGB");
   1.947 +    pdfShader->insert("Domain", domain.get());
   1.948 +
   1.949 +    SkPDFStream* function = makePSFunction(functionCode, domain.get());
   1.950 +    pdfShader->insert("Function", new SkPDFObjRef(function))->unref();
   1.951 +    fResources.push(function);  // Pass ownership to resource list.
   1.952 +
   1.953 +    insertInt("PatternType", 2);
   1.954 +    insert("Matrix", SkPDFUtils::MatrixToArray(finalMatrix))->unref();
   1.955 +    insert("Shading", pdfShader.get());
   1.956 +}
   1.957 +
   1.958 +SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) : fState(state) {
   1.959 +    fState.get()->fImage.lockPixels();
   1.960 +
   1.961 +    // The image shader pattern cell will be drawn into a separate device
   1.962 +    // in pattern cell space (no scaling on the bitmap, though there may be
   1.963 +    // translations so that all content is in the device, coordinates > 0).
   1.964 +
   1.965 +    // Map clip bounds to shader space to ensure the device is large enough
   1.966 +    // to handle fake clamping.
   1.967 +    SkMatrix finalMatrix = fState.get()->fCanvasTransform;
   1.968 +    finalMatrix.preConcat(fState.get()->fShaderTransform);
   1.969 +    SkRect deviceBounds;
   1.970 +    deviceBounds.set(fState.get()->fBBox);
   1.971 +    if (!inverseTransformBBox(finalMatrix, &deviceBounds)) {
   1.972 +        return;
   1.973 +    }
   1.974 +
   1.975 +    const SkBitmap* image = &fState.get()->fImage;
   1.976 +    SkRect bitmapBounds;
   1.977 +    image->getBounds(&bitmapBounds);
   1.978 +
   1.979 +    // For tiling modes, the bounds should be extended to include the bitmap,
   1.980 +    // otherwise the bitmap gets clipped out and the shader is empty and awful.
   1.981 +    // For clamp modes, we're only interested in the clip region, whether
   1.982 +    // or not the main bitmap is in it.
   1.983 +    SkShader::TileMode tileModes[2];
   1.984 +    tileModes[0] = fState.get()->fImageTileModes[0];
   1.985 +    tileModes[1] = fState.get()->fImageTileModes[1];
   1.986 +    if (tileModes[0] != SkShader::kClamp_TileMode ||
   1.987 +            tileModes[1] != SkShader::kClamp_TileMode) {
   1.988 +        deviceBounds.join(bitmapBounds);
   1.989 +    }
   1.990 +
   1.991 +    SkMatrix unflip;
   1.992 +    unflip.setTranslate(0, SkScalarRoundToScalar(deviceBounds.height()));
   1.993 +    unflip.preScale(SK_Scalar1, -SK_Scalar1);
   1.994 +    SkISize size = SkISize::Make(SkScalarRoundToInt(deviceBounds.width()),
   1.995 +                                 SkScalarRoundToInt(deviceBounds.height()));
   1.996 +    // TODO(edisonn): should we pass here the DCT encoder of the destination device?
   1.997 +    // TODO(edisonn): NYI Perspective, use SkPDFDeviceFlattener.
   1.998 +    SkPDFDevice pattern(size, size, unflip);
   1.999 +    SkCanvas canvas(&pattern);
  1.1000 +
  1.1001 +    SkRect patternBBox;
  1.1002 +    image->getBounds(&patternBBox);
  1.1003 +
  1.1004 +    // Translate the canvas so that the bitmap origin is at (0, 0).
  1.1005 +    canvas.translate(-deviceBounds.left(), -deviceBounds.top());
  1.1006 +    patternBBox.offset(-deviceBounds.left(), -deviceBounds.top());
  1.1007 +    // Undo the translation in the final matrix
  1.1008 +    finalMatrix.preTranslate(deviceBounds.left(), deviceBounds.top());
  1.1009 +
  1.1010 +    // If the bitmap is out of bounds (i.e. clamp mode where we only see the
  1.1011 +    // stretched sides), canvas will clip this out and the extraneous data
  1.1012 +    // won't be saved to the PDF.
  1.1013 +    canvas.drawBitmap(*image, 0, 0);
  1.1014 +
  1.1015 +    SkScalar width = SkIntToScalar(image->width());
  1.1016 +    SkScalar height = SkIntToScalar(image->height());
  1.1017 +
  1.1018 +    // Tiling is implied.  First we handle mirroring.
  1.1019 +    if (tileModes[0] == SkShader::kMirror_TileMode) {
  1.1020 +        SkMatrix xMirror;
  1.1021 +        xMirror.setScale(-1, 1);
  1.1022 +        xMirror.postTranslate(2 * width, 0);
  1.1023 +        canvas.drawBitmapMatrix(*image, xMirror);
  1.1024 +        patternBBox.fRight += width;
  1.1025 +    }
  1.1026 +    if (tileModes[1] == SkShader::kMirror_TileMode) {
  1.1027 +        SkMatrix yMirror;
  1.1028 +        yMirror.setScale(SK_Scalar1, -SK_Scalar1);
  1.1029 +        yMirror.postTranslate(0, 2 * height);
  1.1030 +        canvas.drawBitmapMatrix(*image, yMirror);
  1.1031 +        patternBBox.fBottom += height;
  1.1032 +    }
  1.1033 +    if (tileModes[0] == SkShader::kMirror_TileMode &&
  1.1034 +            tileModes[1] == SkShader::kMirror_TileMode) {
  1.1035 +        SkMatrix mirror;
  1.1036 +        mirror.setScale(-1, -1);
  1.1037 +        mirror.postTranslate(2 * width, 2 * height);
  1.1038 +        canvas.drawBitmapMatrix(*image, mirror);
  1.1039 +    }
  1.1040 +
  1.1041 +    // Then handle Clamping, which requires expanding the pattern canvas to
  1.1042 +    // cover the entire surfaceBBox.
  1.1043 +
  1.1044 +    // If both x and y are in clamp mode, we start by filling in the corners.
  1.1045 +    // (Which are just a rectangles of the corner colors.)
  1.1046 +    if (tileModes[0] == SkShader::kClamp_TileMode &&
  1.1047 +            tileModes[1] == SkShader::kClamp_TileMode) {
  1.1048 +        SkPaint paint;
  1.1049 +        SkRect rect;
  1.1050 +        rect = SkRect::MakeLTRB(deviceBounds.left(), deviceBounds.top(), 0, 0);
  1.1051 +        if (!rect.isEmpty()) {
  1.1052 +            paint.setColor(image->getColor(0, 0));
  1.1053 +            canvas.drawRect(rect, paint);
  1.1054 +        }
  1.1055 +
  1.1056 +        rect = SkRect::MakeLTRB(width, deviceBounds.top(),
  1.1057 +                                deviceBounds.right(), 0);
  1.1058 +        if (!rect.isEmpty()) {
  1.1059 +            paint.setColor(image->getColor(image->width() - 1, 0));
  1.1060 +            canvas.drawRect(rect, paint);
  1.1061 +        }
  1.1062 +
  1.1063 +        rect = SkRect::MakeLTRB(width, height,
  1.1064 +                                deviceBounds.right(), deviceBounds.bottom());
  1.1065 +        if (!rect.isEmpty()) {
  1.1066 +            paint.setColor(image->getColor(image->width() - 1,
  1.1067 +                                           image->height() - 1));
  1.1068 +            canvas.drawRect(rect, paint);
  1.1069 +        }
  1.1070 +
  1.1071 +        rect = SkRect::MakeLTRB(deviceBounds.left(), height,
  1.1072 +                                0, deviceBounds.bottom());
  1.1073 +        if (!rect.isEmpty()) {
  1.1074 +            paint.setColor(image->getColor(0, image->height() - 1));
  1.1075 +            canvas.drawRect(rect, paint);
  1.1076 +        }
  1.1077 +    }
  1.1078 +
  1.1079 +    // Then expand the left, right, top, then bottom.
  1.1080 +    if (tileModes[0] == SkShader::kClamp_TileMode) {
  1.1081 +        SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, image->height());
  1.1082 +        if (deviceBounds.left() < 0) {
  1.1083 +            SkBitmap left;
  1.1084 +            SkAssertResult(image->extractSubset(&left, subset));
  1.1085 +
  1.1086 +            SkMatrix leftMatrix;
  1.1087 +            leftMatrix.setScale(-deviceBounds.left(), 1);
  1.1088 +            leftMatrix.postTranslate(deviceBounds.left(), 0);
  1.1089 +            canvas.drawBitmapMatrix(left, leftMatrix);
  1.1090 +
  1.1091 +            if (tileModes[1] == SkShader::kMirror_TileMode) {
  1.1092 +                leftMatrix.postScale(SK_Scalar1, -SK_Scalar1);
  1.1093 +                leftMatrix.postTranslate(0, 2 * height);
  1.1094 +                canvas.drawBitmapMatrix(left, leftMatrix);
  1.1095 +            }
  1.1096 +            patternBBox.fLeft = 0;
  1.1097 +        }
  1.1098 +
  1.1099 +        if (deviceBounds.right() > width) {
  1.1100 +            SkBitmap right;
  1.1101 +            subset.offset(image->width() - 1, 0);
  1.1102 +            SkAssertResult(image->extractSubset(&right, subset));
  1.1103 +
  1.1104 +            SkMatrix rightMatrix;
  1.1105 +            rightMatrix.setScale(deviceBounds.right() - width, 1);
  1.1106 +            rightMatrix.postTranslate(width, 0);
  1.1107 +            canvas.drawBitmapMatrix(right, rightMatrix);
  1.1108 +
  1.1109 +            if (tileModes[1] == SkShader::kMirror_TileMode) {
  1.1110 +                rightMatrix.postScale(SK_Scalar1, -SK_Scalar1);
  1.1111 +                rightMatrix.postTranslate(0, 2 * height);
  1.1112 +                canvas.drawBitmapMatrix(right, rightMatrix);
  1.1113 +            }
  1.1114 +            patternBBox.fRight = deviceBounds.width();
  1.1115 +        }
  1.1116 +    }
  1.1117 +
  1.1118 +    if (tileModes[1] == SkShader::kClamp_TileMode) {
  1.1119 +        SkIRect subset = SkIRect::MakeXYWH(0, 0, image->width(), 1);
  1.1120 +        if (deviceBounds.top() < 0) {
  1.1121 +            SkBitmap top;
  1.1122 +            SkAssertResult(image->extractSubset(&top, subset));
  1.1123 +
  1.1124 +            SkMatrix topMatrix;
  1.1125 +            topMatrix.setScale(SK_Scalar1, -deviceBounds.top());
  1.1126 +            topMatrix.postTranslate(0, deviceBounds.top());
  1.1127 +            canvas.drawBitmapMatrix(top, topMatrix);
  1.1128 +
  1.1129 +            if (tileModes[0] == SkShader::kMirror_TileMode) {
  1.1130 +                topMatrix.postScale(-1, 1);
  1.1131 +                topMatrix.postTranslate(2 * width, 0);
  1.1132 +                canvas.drawBitmapMatrix(top, topMatrix);
  1.1133 +            }
  1.1134 +            patternBBox.fTop = 0;
  1.1135 +        }
  1.1136 +
  1.1137 +        if (deviceBounds.bottom() > height) {
  1.1138 +            SkBitmap bottom;
  1.1139 +            subset.offset(0, image->height() - 1);
  1.1140 +            SkAssertResult(image->extractSubset(&bottom, subset));
  1.1141 +
  1.1142 +            SkMatrix bottomMatrix;
  1.1143 +            bottomMatrix.setScale(SK_Scalar1, deviceBounds.bottom() - height);
  1.1144 +            bottomMatrix.postTranslate(0, height);
  1.1145 +            canvas.drawBitmapMatrix(bottom, bottomMatrix);
  1.1146 +
  1.1147 +            if (tileModes[0] == SkShader::kMirror_TileMode) {
  1.1148 +                bottomMatrix.postScale(-1, 1);
  1.1149 +                bottomMatrix.postTranslate(2 * width, 0);
  1.1150 +                canvas.drawBitmapMatrix(bottom, bottomMatrix);
  1.1151 +            }
  1.1152 +            patternBBox.fBottom = deviceBounds.height();
  1.1153 +        }
  1.1154 +    }
  1.1155 +
  1.1156 +    // Put the canvas into the pattern stream (fContent).
  1.1157 +    SkAutoTUnref<SkStream> content(pattern.content());
  1.1158 +    setData(content.get());
  1.1159 +    SkPDFResourceDict* resourceDict = pattern.getResourceDict();
  1.1160 +    resourceDict->getReferencedResources(fResources, &fResources, false);
  1.1161 +
  1.1162 +    populate_tiling_pattern_dict(this, patternBBox,
  1.1163 +                                 pattern.getResourceDict(), finalMatrix);
  1.1164 +
  1.1165 +    fState.get()->fImage.unlockPixels();
  1.1166 +}
  1.1167 +
  1.1168 +SkPDFStream* SkPDFFunctionShader::makePSFunction(const SkString& psCode,
  1.1169 +                                                 SkPDFArray* domain) {
  1.1170 +    SkAutoDataUnref funcData(SkData::NewWithCopy(psCode.c_str(),
  1.1171 +                                                 psCode.size()));
  1.1172 +    SkPDFStream* result = new SkPDFStream(funcData.get());
  1.1173 +    result->insertInt("FunctionType", 4);
  1.1174 +    result->insert("Domain", domain);
  1.1175 +    result->insert("Range", RangeObject());
  1.1176 +    return result;
  1.1177 +}
  1.1178 +
  1.1179 +SkPDFShader::ShaderCanonicalEntry::ShaderCanonicalEntry(SkPDFObject* pdfShader,
  1.1180 +                                                        const State* state)
  1.1181 +    : fPDFShader(pdfShader),
  1.1182 +      fState(state) {
  1.1183 +}
  1.1184 +
  1.1185 +bool SkPDFShader::ShaderCanonicalEntry::operator==(
  1.1186 +        const ShaderCanonicalEntry& b) const {
  1.1187 +    return fPDFShader == b.fPDFShader ||
  1.1188 +           (fState != NULL && b.fState != NULL && *fState == *b.fState);
  1.1189 +}
  1.1190 +
  1.1191 +bool SkPDFShader::State::operator==(const SkPDFShader::State& b) const {
  1.1192 +    if (fType != b.fType ||
  1.1193 +            fCanvasTransform != b.fCanvasTransform ||
  1.1194 +            fShaderTransform != b.fShaderTransform ||
  1.1195 +            fBBox != b.fBBox) {
  1.1196 +        return false;
  1.1197 +    }
  1.1198 +
  1.1199 +    if (fType == SkShader::kNone_GradientType) {
  1.1200 +        if (fPixelGeneration != b.fPixelGeneration ||
  1.1201 +                fPixelGeneration == 0 ||
  1.1202 +                fImageTileModes[0] != b.fImageTileModes[0] ||
  1.1203 +                fImageTileModes[1] != b.fImageTileModes[1]) {
  1.1204 +            return false;
  1.1205 +        }
  1.1206 +    } else {
  1.1207 +        if (fInfo.fColorCount != b.fInfo.fColorCount ||
  1.1208 +                memcmp(fInfo.fColors, b.fInfo.fColors,
  1.1209 +                       sizeof(SkColor) * fInfo.fColorCount) != 0 ||
  1.1210 +                memcmp(fInfo.fColorOffsets, b.fInfo.fColorOffsets,
  1.1211 +                       sizeof(SkScalar) * fInfo.fColorCount) != 0 ||
  1.1212 +                fInfo.fPoint[0] != b.fInfo.fPoint[0] ||
  1.1213 +                fInfo.fTileMode != b.fInfo.fTileMode) {
  1.1214 +            return false;
  1.1215 +        }
  1.1216 +
  1.1217 +        switch (fType) {
  1.1218 +            case SkShader::kLinear_GradientType:
  1.1219 +                if (fInfo.fPoint[1] != b.fInfo.fPoint[1]) {
  1.1220 +                    return false;
  1.1221 +                }
  1.1222 +                break;
  1.1223 +            case SkShader::kRadial_GradientType:
  1.1224 +                if (fInfo.fRadius[0] != b.fInfo.fRadius[0]) {
  1.1225 +                    return false;
  1.1226 +                }
  1.1227 +                break;
  1.1228 +            case SkShader::kRadial2_GradientType:
  1.1229 +            case SkShader::kConical_GradientType:
  1.1230 +                if (fInfo.fPoint[1] != b.fInfo.fPoint[1] ||
  1.1231 +                        fInfo.fRadius[0] != b.fInfo.fRadius[0] ||
  1.1232 +                        fInfo.fRadius[1] != b.fInfo.fRadius[1]) {
  1.1233 +                    return false;
  1.1234 +                }
  1.1235 +                break;
  1.1236 +            case SkShader::kSweep_GradientType:
  1.1237 +            case SkShader::kNone_GradientType:
  1.1238 +            case SkShader::kColor_GradientType:
  1.1239 +                break;
  1.1240 +        }
  1.1241 +    }
  1.1242 +    return true;
  1.1243 +}
  1.1244 +
  1.1245 +SkPDFShader::State::State(const SkShader& shader,
  1.1246 +                          const SkMatrix& canvasTransform, const SkIRect& bbox)
  1.1247 +        : fCanvasTransform(canvasTransform),
  1.1248 +          fBBox(bbox),
  1.1249 +          fPixelGeneration(0) {
  1.1250 +    fInfo.fColorCount = 0;
  1.1251 +    fInfo.fColors = NULL;
  1.1252 +    fInfo.fColorOffsets = NULL;
  1.1253 +    fShaderTransform = shader.getLocalMatrix();
  1.1254 +    fImageTileModes[0] = fImageTileModes[1] = SkShader::kClamp_TileMode;
  1.1255 +
  1.1256 +    fType = shader.asAGradient(&fInfo);
  1.1257 +
  1.1258 +    if (fType == SkShader::kNone_GradientType) {
  1.1259 +        SkShader::BitmapType bitmapType;
  1.1260 +        SkMatrix matrix;
  1.1261 +        bitmapType = shader.asABitmap(&fImage, &matrix, fImageTileModes);
  1.1262 +        if (bitmapType != SkShader::kDefault_BitmapType) {
  1.1263 +            fImage.reset();
  1.1264 +            return;
  1.1265 +        }
  1.1266 +        SkASSERT(matrix.isIdentity());
  1.1267 +        fPixelGeneration = fImage.getGenerationID();
  1.1268 +    } else {
  1.1269 +        AllocateGradientInfoStorage();
  1.1270 +        shader.asAGradient(&fInfo);
  1.1271 +    }
  1.1272 +}
  1.1273 +
  1.1274 +SkPDFShader::State::State(const SkPDFShader::State& other)
  1.1275 +  : fType(other.fType),
  1.1276 +    fCanvasTransform(other.fCanvasTransform),
  1.1277 +    fShaderTransform(other.fShaderTransform),
  1.1278 +    fBBox(other.fBBox)
  1.1279 +{
  1.1280 +    // Only gradients supported for now, since that is all that is used.
  1.1281 +    // If needed, image state copy constructor can be added here later.
  1.1282 +    SkASSERT(fType != SkShader::kNone_GradientType);
  1.1283 +
  1.1284 +    if (fType != SkShader::kNone_GradientType) {
  1.1285 +        fInfo = other.fInfo;
  1.1286 +
  1.1287 +        AllocateGradientInfoStorage();
  1.1288 +        for (int i = 0; i < fInfo.fColorCount; i++) {
  1.1289 +            fInfo.fColors[i] = other.fInfo.fColors[i];
  1.1290 +            fInfo.fColorOffsets[i] = other.fInfo.fColorOffsets[i];
  1.1291 +        }
  1.1292 +    }
  1.1293 +}
  1.1294 +
  1.1295 +/**
  1.1296 + * Create a copy of this gradient state with alpha assigned to RGB luminousity.
  1.1297 + * Only valid for gradient states.
  1.1298 + */
  1.1299 +SkPDFShader::State* SkPDFShader::State::CreateAlphaToLuminosityState() const {
  1.1300 +    SkASSERT(fType != SkShader::kNone_GradientType);
  1.1301 +
  1.1302 +    SkPDFShader::State* newState = new SkPDFShader::State(*this);
  1.1303 +
  1.1304 +    for (int i = 0; i < fInfo.fColorCount; i++) {
  1.1305 +        SkAlpha alpha = SkColorGetA(fInfo.fColors[i]);
  1.1306 +        newState->fInfo.fColors[i] = SkColorSetARGB(255, alpha, alpha, alpha);
  1.1307 +    }
  1.1308 +
  1.1309 +    return newState;
  1.1310 +}
  1.1311 +
  1.1312 +/**
  1.1313 + * Create a copy of this gradient state with alpha set to fully opaque
  1.1314 + * Only valid for gradient states.
  1.1315 + */
  1.1316 +SkPDFShader::State* SkPDFShader::State::CreateOpaqueState() const {
  1.1317 +    SkASSERT(fType != SkShader::kNone_GradientType);
  1.1318 +
  1.1319 +    SkPDFShader::State* newState = new SkPDFShader::State(*this);
  1.1320 +    for (int i = 0; i < fInfo.fColorCount; i++) {
  1.1321 +        newState->fInfo.fColors[i] = SkColorSetA(fInfo.fColors[i],
  1.1322 +                                                 SK_AlphaOPAQUE);
  1.1323 +    }
  1.1324 +
  1.1325 +    return newState;
  1.1326 +}
  1.1327 +
  1.1328 +/**
  1.1329 + * Returns true if state is a gradient and the gradient has alpha.
  1.1330 + */
  1.1331 +bool SkPDFShader::State::GradientHasAlpha() const {
  1.1332 +    if (fType == SkShader::kNone_GradientType) {
  1.1333 +        return false;
  1.1334 +    }
  1.1335 +
  1.1336 +    for (int i = 0; i < fInfo.fColorCount; i++) {
  1.1337 +        SkAlpha alpha = SkColorGetA(fInfo.fColors[i]);
  1.1338 +        if (alpha != SK_AlphaOPAQUE) {
  1.1339 +            return true;
  1.1340 +        }
  1.1341 +    }
  1.1342 +    return false;
  1.1343 +}
  1.1344 +
  1.1345 +void SkPDFShader::State::AllocateGradientInfoStorage() {
  1.1346 +    fColorData.set(sk_malloc_throw(
  1.1347 +               fInfo.fColorCount * (sizeof(SkColor) + sizeof(SkScalar))));
  1.1348 +    fInfo.fColors = reinterpret_cast<SkColor*>(fColorData.get());
  1.1349 +    fInfo.fColorOffsets =
  1.1350 +            reinterpret_cast<SkScalar*>(fInfo.fColors + fInfo.fColorCount);
  1.1351 +}

mercurial