LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/pdf - SkPDFShader.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 712 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 33 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright 2011 Google Inc.
       3             :  *
       4             :  * Use of this source code is governed by a BSD-style license that can be
       5             :  * found in the LICENSE file.
       6             :  */
       7             : 
       8             : 
       9             : #include "SkPDFShader.h"
      10             : 
      11             : #include "SkData.h"
      12             : #include "SkPDFCanon.h"
      13             : #include "SkPDFDevice.h"
      14             : #include "SkPDFDocument.h"
      15             : #include "SkPDFFormXObject.h"
      16             : #include "SkPDFGraphicState.h"
      17             : #include "SkPDFResourceDict.h"
      18             : #include "SkPDFUtils.h"
      19             : #include "SkScalar.h"
      20             : #include "SkStream.h"
      21             : #include "SkTemplates.h"
      22             : 
      23           0 : static bool inverse_transform_bbox(const SkMatrix& matrix, SkRect* bbox) {
      24             :     SkMatrix inverse;
      25           0 :     if (!matrix.invert(&inverse)) {
      26           0 :         return false;
      27             :     }
      28           0 :     inverse.mapRect(bbox);
      29           0 :     return true;
      30             : }
      31             : 
      32           0 : static void unitToPointsMatrix(const SkPoint pts[2], SkMatrix* matrix) {
      33           0 :     SkVector    vec = pts[1] - pts[0];
      34           0 :     SkScalar    mag = vec.length();
      35           0 :     SkScalar    inv = mag ? SkScalarInvert(mag) : 0;
      36             : 
      37           0 :     vec.scale(inv);
      38           0 :     matrix->setSinCos(vec.fY, vec.fX);
      39           0 :     matrix->preScale(mag, mag);
      40           0 :     matrix->postTranslate(pts[0].fX, pts[0].fY);
      41           0 : }
      42             : 
      43             : static const int kColorComponents = 3;
      44             : typedef uint8_t ColorTuple[kColorComponents];
      45             : 
      46             : /* Assumes t + startOffset is on the stack and does a linear interpolation on t
      47             :    between startOffset and endOffset from prevColor to curColor (for each color
      48             :    component), leaving the result in component order on the stack. It assumes
      49             :    there are always 3 components per color.
      50             :    @param range                  endOffset - startOffset
      51             :    @param curColor[components]   The current color components.
      52             :    @param prevColor[components]  The previous color components.
      53             :    @param result                 The result ps function.
      54             :  */
      55           0 : static void interpolateColorCode(SkScalar range, const ColorTuple& curColor,
      56             :                                  const ColorTuple& prevColor,
      57             :                                  SkDynamicMemoryWStream* result) {
      58           0 :     SkASSERT(range != SkIntToScalar(0));
      59             : 
      60             :     // Figure out how to scale each color component.
      61             :     SkScalar multiplier[kColorComponents];
      62           0 :     for (int i = 0; i < kColorComponents; i++) {
      63             :         static const SkScalar kColorScale = SkScalarInvert(255);
      64           0 :         multiplier[i] = kColorScale * (curColor[i] - prevColor[i]) / range;
      65             :     }
      66             : 
      67             :     // Calculate when we no longer need to keep a copy of the input parameter t.
      68             :     // If the last component to use t is i, then dupInput[0..i - 1] = true
      69             :     // and dupInput[i .. components] = false.
      70             :     bool dupInput[kColorComponents];
      71           0 :     dupInput[kColorComponents - 1] = false;
      72           0 :     for (int i = kColorComponents - 2; i >= 0; i--) {
      73           0 :         dupInput[i] = dupInput[i + 1] || multiplier[i + 1] != 0;
      74             :     }
      75             : 
      76           0 :     if (!dupInput[0] && multiplier[0] == 0) {
      77           0 :         result->writeText("pop ");
      78             :     }
      79             : 
      80           0 :     for (int i = 0; i < kColorComponents; i++) {
      81             :         // If the next components needs t and this component will consume a
      82             :         // copy, make another copy.
      83           0 :         if (dupInput[i] && multiplier[i] != 0) {
      84           0 :             result->writeText("dup ");
      85             :         }
      86             : 
      87           0 :         if (multiplier[i] == 0) {
      88           0 :             SkPDFUtils::AppendColorComponent(prevColor[i], result);
      89           0 :             result->writeText(" ");
      90             :         } else {
      91           0 :             if (multiplier[i] != 1) {
      92           0 :                 SkPDFUtils::AppendScalar(multiplier[i], result);
      93           0 :                 result->writeText(" mul ");
      94             :             }
      95           0 :             if (prevColor[i] != 0) {
      96           0 :                 SkPDFUtils::AppendColorComponent(prevColor[i], result);
      97           0 :                 result->writeText(" add ");
      98             :             }
      99             :         }
     100             : 
     101           0 :         if (dupInput[i]) {
     102           0 :             result->writeText("exch\n");
     103             :         }
     104             :     }
     105           0 : }
     106             : 
     107             : /* Generate Type 4 function code to map t=[0,1) to the passed gradient,
     108             :    clamping at the edges of the range.  The generated code will be of the form:
     109             :        if (t < 0) {
     110             :            return colorData[0][r,g,b];
     111             :        } else {
     112             :            if (t < info.fColorOffsets[1]) {
     113             :                return linearinterpolation(colorData[0][r,g,b],
     114             :                                           colorData[1][r,g,b]);
     115             :            } else {
     116             :                if (t < info.fColorOffsets[2]) {
     117             :                    return linearinterpolation(colorData[1][r,g,b],
     118             :                                               colorData[2][r,g,b]);
     119             :                } else {
     120             : 
     121             :                 ...    } else {
     122             :                            return colorData[info.fColorCount - 1][r,g,b];
     123             :                        }
     124             :                 ...
     125             :            }
     126             :        }
     127             :  */
     128           0 : static void gradientFunctionCode(const SkShader::GradientInfo& info,
     129             :                                  SkDynamicMemoryWStream* result) {
     130             :     /* We want to linearly interpolate from the previous color to the next.
     131             :        Scale the colors from 0..255 to 0..1 and determine the multipliers
     132             :        for interpolation.
     133             :        C{r,g,b}(t, section) = t - offset_(section-1) + t * Multiplier{r,g,b}.
     134             :      */
     135             : 
     136           0 :     SkAutoSTMalloc<4, ColorTuple> colorDataAlloc(info.fColorCount);
     137           0 :     ColorTuple *colorData = colorDataAlloc.get();
     138           0 :     for (int i = 0; i < info.fColorCount; i++) {
     139           0 :         colorData[i][0] = SkColorGetR(info.fColors[i]);
     140           0 :         colorData[i][1] = SkColorGetG(info.fColors[i]);
     141           0 :         colorData[i][2] = SkColorGetB(info.fColors[i]);
     142             :     }
     143             : 
     144             :     // Clamp the initial color.
     145           0 :     result->writeText("dup 0 le {pop ");
     146           0 :     SkPDFUtils::AppendColorComponent(colorData[0][0], result);
     147           0 :     result->writeText(" ");
     148           0 :     SkPDFUtils::AppendColorComponent(colorData[0][1], result);
     149           0 :     result->writeText(" ");
     150           0 :     SkPDFUtils::AppendColorComponent(colorData[0][2], result);
     151           0 :     result->writeText(" }\n");
     152             : 
     153             :     // The gradient colors.
     154           0 :     int gradients = 0;
     155           0 :     for (int i = 1 ; i < info.fColorCount; i++) {
     156           0 :         if (info.fColorOffsets[i] == info.fColorOffsets[i - 1]) {
     157           0 :             continue;
     158             :         }
     159           0 :         gradients++;
     160             : 
     161           0 :         result->writeText("{dup ");
     162           0 :         SkPDFUtils::AppendScalar(info.fColorOffsets[i], result);
     163           0 :         result->writeText(" le {");
     164           0 :         if (info.fColorOffsets[i - 1] != 0) {
     165           0 :             SkPDFUtils::AppendScalar(info.fColorOffsets[i - 1], result);
     166           0 :             result->writeText(" sub\n");
     167             :         }
     168             : 
     169           0 :         interpolateColorCode(info.fColorOffsets[i] - info.fColorOffsets[i - 1],
     170           0 :                              colorData[i], colorData[i - 1], result);
     171           0 :         result->writeText("}\n");
     172             :     }
     173             : 
     174             :     // Clamp the final color.
     175           0 :     result->writeText("{pop ");
     176           0 :     SkPDFUtils::AppendColorComponent(colorData[info.fColorCount - 1][0], result);
     177           0 :     result->writeText(" ");
     178           0 :     SkPDFUtils::AppendColorComponent(colorData[info.fColorCount - 1][1], result);
     179           0 :     result->writeText(" ");
     180           0 :     SkPDFUtils::AppendColorComponent(colorData[info.fColorCount - 1][2], result);
     181             : 
     182           0 :     for (int i = 0 ; i < gradients + 1; i++) {
     183           0 :         result->writeText("} ifelse\n");
     184             :     }
     185           0 : }
     186             : 
     187           0 : static sk_sp<SkPDFDict> createInterpolationFunction(const ColorTuple& color1,
     188             :                                                     const ColorTuple& color2) {
     189           0 :     auto retval = sk_make_sp<SkPDFDict>();
     190             : 
     191           0 :     auto c0 = sk_make_sp<SkPDFArray>();
     192           0 :     c0->appendColorComponent(color1[0]);
     193           0 :     c0->appendColorComponent(color1[1]);
     194           0 :     c0->appendColorComponent(color1[2]);
     195           0 :     retval->insertObject("C0", std::move(c0));
     196             : 
     197           0 :     auto c1 = sk_make_sp<SkPDFArray>();
     198           0 :     c1->appendColorComponent(color2[0]);
     199           0 :     c1->appendColorComponent(color2[1]);
     200           0 :     c1->appendColorComponent(color2[2]);
     201           0 :     retval->insertObject("C1", std::move(c1));
     202             : 
     203           0 :     auto domain = sk_make_sp<SkPDFArray>();
     204           0 :     domain->appendScalar(0);
     205           0 :     domain->appendScalar(1.0f);
     206           0 :     retval->insertObject("Domain", std::move(domain));
     207             : 
     208           0 :     retval->insertInt("FunctionType", 2);
     209           0 :     retval->insertScalar("N", 1.0f);
     210             : 
     211           0 :     return retval;
     212             : }
     213             : 
     214           0 : static sk_sp<SkPDFDict> gradientStitchCode(const SkShader::GradientInfo& info) {
     215           0 :     auto retval = sk_make_sp<SkPDFDict>();
     216             : 
     217             :     // normalize color stops
     218           0 :     int colorCount = info.fColorCount;
     219           0 :     SkTDArray<SkColor>    colors(info.fColors, colorCount);
     220           0 :     SkTDArray<SkScalar>   colorOffsets(info.fColorOffsets, colorCount);
     221             : 
     222           0 :     int i = 1;
     223           0 :     while (i < colorCount - 1) {
     224             :         // ensure stops are in order
     225           0 :         if (colorOffsets[i - 1] > colorOffsets[i]) {
     226           0 :             colorOffsets[i] = colorOffsets[i - 1];
     227             :         }
     228             : 
     229             :         // remove points that are between 2 coincident points
     230           0 :         if ((colorOffsets[i - 1] == colorOffsets[i]) && (colorOffsets[i] == colorOffsets[i + 1])) {
     231           0 :             colorCount -= 1;
     232           0 :             colors.remove(i);
     233           0 :             colorOffsets.remove(i);
     234             :         } else {
     235           0 :             i++;
     236             :         }
     237             :     }
     238             :     // find coincident points and slightly move them over
     239           0 :     for (i = 1; i < colorCount - 1; i++) {
     240           0 :         if (colorOffsets[i - 1] == colorOffsets[i]) {
     241           0 :             colorOffsets[i] += 0.00001f;
     242             :         }
     243             :     }
     244             :     // check if last 2 stops coincide
     245           0 :     if (colorOffsets[i - 1] == colorOffsets[i]) {
     246           0 :         colorOffsets[i - 1] -= 0.00001f;
     247             :     }
     248             : 
     249           0 :     SkAutoSTMalloc<4, ColorTuple> colorDataAlloc(colorCount);
     250           0 :     ColorTuple *colorData = colorDataAlloc.get();
     251           0 :     for (int i = 0; i < colorCount; i++) {
     252           0 :         colorData[i][0] = SkColorGetR(colors[i]);
     253           0 :         colorData[i][1] = SkColorGetG(colors[i]);
     254           0 :         colorData[i][2] = SkColorGetB(colors[i]);
     255             :     }
     256             : 
     257             :     // no need for a stitch function if there are only 2 stops.
     258           0 :     if (colorCount == 2)
     259           0 :         return createInterpolationFunction(colorData[0], colorData[1]);
     260             : 
     261           0 :     auto encode = sk_make_sp<SkPDFArray>();
     262           0 :     auto bounds = sk_make_sp<SkPDFArray>();
     263           0 :     auto functions = sk_make_sp<SkPDFArray>();
     264             : 
     265           0 :     auto domain = sk_make_sp<SkPDFArray>();
     266           0 :     domain->appendScalar(0);
     267           0 :     domain->appendScalar(1.0f);
     268           0 :     retval->insertObject("Domain", std::move(domain));
     269           0 :     retval->insertInt("FunctionType", 3);
     270             : 
     271           0 :     for (int i = 1; i < colorCount; i++) {
     272           0 :         if (i > 1) {
     273           0 :             bounds->appendScalar(colorOffsets[i-1]);
     274             :         }
     275             : 
     276           0 :         encode->appendScalar(0);
     277           0 :         encode->appendScalar(1.0f);
     278             :     
     279           0 :         functions->appendObject(createInterpolationFunction(colorData[i-1], colorData[i]));
     280             :     }
     281             : 
     282           0 :     retval->insertObject("Encode", std::move(encode));
     283           0 :     retval->insertObject("Bounds", std::move(bounds));
     284           0 :     retval->insertObject("Functions", std::move(functions));
     285             : 
     286           0 :     return retval;
     287             : }
     288             : 
     289             : /* Map a value of t on the stack into [0, 1) for Repeat or Mirror tile mode. */
     290           0 : static void tileModeCode(SkShader::TileMode mode,
     291             :                          SkDynamicMemoryWStream* result) {
     292           0 :     if (mode == SkShader::kRepeat_TileMode) {
     293           0 :         result->writeText("dup truncate sub\n");  // Get the fractional part.
     294           0 :         result->writeText("dup 0 le {1 add} if\n");  // Map (-1,0) => (0,1)
     295           0 :         return;
     296             :     }
     297             : 
     298           0 :     if (mode == SkShader::kMirror_TileMode) {
     299             :         // Map t mod 2 into [0, 1, 1, 0].
     300             :         //               Code                     Stack
     301           0 :         result->writeText("abs "                 // Map negative to positive.
     302             :                           "dup "                 // t.s t.s
     303             :                           "truncate "            // t.s t
     304             :                           "dup "                 // t.s t t
     305             :                           "cvi "                 // t.s t T
     306             :                           "2 mod "               // t.s t (i mod 2)
     307             :                           "1 eq "                // t.s t true|false
     308             :                           "3 1 roll "            // true|false t.s t
     309             :                           "sub "                 // true|false 0.s
     310             :                           "exch "                // 0.s true|false
     311           0 :                           "{1 exch sub} if\n");  // 1 - 0.s|0.s
     312             :     }
     313             : }
     314             : 
     315             : /**
     316             :  *  Returns PS function code that applies inverse perspective
     317             :  *  to a x, y point.
     318             :  *  The function assumes that the stack has at least two elements,
     319             :  *  and that the top 2 elements are numeric values.
     320             :  *  After executing this code on a PS stack, the last 2 elements are updated
     321             :  *  while the rest of the stack is preserved intact.
     322             :  *  inversePerspectiveMatrix is the inverse perspective matrix.
     323             :  */
     324           0 : static void apply_perspective_to_coordinates(
     325             :         const SkMatrix& inversePerspectiveMatrix,
     326             :         SkDynamicMemoryWStream* code) {
     327           0 :     if (!inversePerspectiveMatrix.hasPerspective()) {
     328           0 :         return;
     329             :     }
     330             : 
     331             :     // Perspective matrix should be:
     332             :     // 1   0  0
     333             :     // 0   1  0
     334             :     // p0 p1 p2
     335             : 
     336           0 :     const SkScalar p0 = inversePerspectiveMatrix[SkMatrix::kMPersp0];
     337           0 :     const SkScalar p1 = inversePerspectiveMatrix[SkMatrix::kMPersp1];
     338           0 :     const SkScalar p2 = inversePerspectiveMatrix[SkMatrix::kMPersp2];
     339             : 
     340             :     // y = y / (p2 + p0 x + p1 y)
     341             :     // x = x / (p2 + p0 x + p1 y)
     342             : 
     343             :     // Input on stack: x y
     344           0 :     code->writeText(" dup ");             // x y y
     345           0 :     SkPDFUtils::AppendScalar(p1, code);   // x y y p1
     346           0 :     code->writeText(" mul "               // x y y*p1
     347           0 :                     " 2 index ");         // x y y*p1 x
     348           0 :     SkPDFUtils::AppendScalar(p0, code);   // x y y p1 x p0
     349           0 :     code->writeText(" mul ");             // x y y*p1 x*p0
     350           0 :     SkPDFUtils::AppendScalar(p2, code);   // x y y p1 x*p0 p2
     351           0 :     code->writeText(" add "               // x y y*p1 x*p0+p2
     352             :                     "add "                // x y y*p1+x*p0+p2
     353             :                     "3 1 roll "           // y*p1+x*p0+p2 x y
     354             :                     "2 index "            // z x y y*p1+x*p0+p2
     355             :                     "div "                // y*p1+x*p0+p2 x y/(y*p1+x*p0+p2)
     356             :                     "3 1 roll "           // y/(y*p1+x*p0+p2) y*p1+x*p0+p2 x
     357             :                     "exch "               // y/(y*p1+x*p0+p2) x y*p1+x*p0+p2
     358             :                     "div "                // y/(y*p1+x*p0+p2) x/(y*p1+x*p0+p2)
     359           0 :                     "exch\n");            // x/(y*p1+x*p0+p2) y/(y*p1+x*p0+p2)
     360             : }
     361             : 
     362           0 : static void linearCode(const SkShader::GradientInfo& info,
     363             :                        const SkMatrix& perspectiveRemover,
     364             :                        SkDynamicMemoryWStream* function) {
     365           0 :     function->writeText("{");
     366             : 
     367           0 :     apply_perspective_to_coordinates(perspectiveRemover, function);
     368             : 
     369           0 :     function->writeText("pop\n");  // Just ditch the y value.
     370           0 :     tileModeCode(info.fTileMode, function);
     371           0 :     gradientFunctionCode(info, function);
     372           0 :     function->writeText("}");
     373           0 : }
     374             : 
     375           0 : static void radialCode(const SkShader::GradientInfo& info,
     376             :                        const SkMatrix& perspectiveRemover,
     377             :                        SkDynamicMemoryWStream* function) {
     378           0 :     function->writeText("{");
     379             : 
     380           0 :     apply_perspective_to_coordinates(perspectiveRemover, function);
     381             : 
     382             :     // Find the distance from the origin.
     383           0 :     function->writeText("dup "      // x y y
     384             :                     "mul "      // x y^2
     385             :                     "exch "     // y^2 x
     386             :                     "dup "      // y^2 x x
     387             :                     "mul "      // y^2 x^2
     388             :                     "add "      // y^2+x^2
     389           0 :                     "sqrt\n");  // sqrt(y^2+x^2)
     390             : 
     391           0 :     tileModeCode(info.fTileMode, function);
     392           0 :     gradientFunctionCode(info, function);
     393           0 :     function->writeText("}");
     394           0 : }
     395             : 
     396             : /* Conical gradient shader, based on the Canvas spec for radial gradients
     397             :    See: http://www.w3.org/TR/2dcontext/#dom-context-2d-createradialgradient
     398             :  */
     399           0 : static void twoPointConicalCode(const SkShader::GradientInfo& info,
     400             :                                 const SkMatrix& perspectiveRemover,
     401             :                                 SkDynamicMemoryWStream* function) {
     402           0 :     SkScalar dx = info.fPoint[1].fX - info.fPoint[0].fX;
     403           0 :     SkScalar dy = info.fPoint[1].fY - info.fPoint[0].fY;
     404           0 :     SkScalar r0 = info.fRadius[0];
     405           0 :     SkScalar dr = info.fRadius[1] - info.fRadius[0];
     406           0 :     SkScalar a = dx * dx + dy * dy - dr * dr;
     407             : 
     408             :     // First compute t, if the pixel falls outside the cone, then we'll end
     409             :     // with 'false' on the stack, otherwise we'll push 'true' with t below it
     410             : 
     411             :     // We start with a stack of (x y), copy it and then consume one copy in
     412             :     // order to calculate b and the other to calculate c.
     413           0 :     function->writeText("{");
     414             : 
     415           0 :     apply_perspective_to_coordinates(perspectiveRemover, function);
     416             : 
     417           0 :     function->writeText("2 copy ");
     418             : 
     419             :     // Calculate b and b^2; b = -2 * (y * dy + x * dx + r0 * dr).
     420           0 :     SkPDFUtils::AppendScalar(dy, function);
     421           0 :     function->writeText(" mul exch ");
     422           0 :     SkPDFUtils::AppendScalar(dx, function);
     423           0 :     function->writeText(" mul add ");
     424           0 :     SkPDFUtils::AppendScalar(r0 * dr, function);
     425           0 :     function->writeText(" add -2 mul dup dup mul\n");
     426             : 
     427             :     // c = x^2 + y^2 + radius0^2
     428           0 :     function->writeText("4 2 roll dup mul exch dup mul add ");
     429           0 :     SkPDFUtils::AppendScalar(r0 * r0, function);
     430           0 :     function->writeText(" sub dup 4 1 roll\n");
     431             : 
     432             :     // Contents of the stack at this point: c, b, b^2, c
     433             : 
     434             :     // if a = 0, then we collapse to a simpler linear case
     435           0 :     if (a == 0) {
     436             : 
     437             :         // t = -c/b
     438           0 :         function->writeText("pop pop div neg dup ");
     439             : 
     440             :         // compute radius(t)
     441           0 :         SkPDFUtils::AppendScalar(dr, function);
     442           0 :         function->writeText(" mul ");
     443           0 :         SkPDFUtils::AppendScalar(r0, function);
     444           0 :         function->writeText(" add\n");
     445             : 
     446             :         // if r(t) < 0, then it's outside the cone
     447           0 :         function->writeText("0 lt {pop false} {true} ifelse\n");
     448             : 
     449             :     } else {
     450             : 
     451             :         // quadratic case: the Canvas spec wants the largest
     452             :         // root t for which radius(t) > 0
     453             : 
     454             :         // compute the discriminant (b^2 - 4ac)
     455           0 :         SkPDFUtils::AppendScalar(a * 4, function);
     456           0 :         function->writeText(" mul sub dup\n");
     457             : 
     458             :         // if d >= 0, proceed
     459           0 :         function->writeText("0 ge {\n");
     460             : 
     461             :         // an intermediate value we'll use to compute the roots:
     462             :         // q = -0.5 * (b +/- sqrt(d))
     463           0 :         function->writeText("sqrt exch dup 0 lt {exch -1 mul} if");
     464           0 :         function->writeText(" add -0.5 mul dup\n");
     465             : 
     466             :         // first root = q / a
     467           0 :         SkPDFUtils::AppendScalar(a, function);
     468           0 :         function->writeText(" div\n");
     469             : 
     470             :         // second root = c / q
     471           0 :         function->writeText("3 1 roll div\n");
     472             : 
     473             :         // put the larger root on top of the stack
     474           0 :         function->writeText("2 copy gt {exch} if\n");
     475             : 
     476             :         // compute radius(t) for larger root
     477           0 :         function->writeText("dup ");
     478           0 :         SkPDFUtils::AppendScalar(dr, function);
     479           0 :         function->writeText(" mul ");
     480           0 :         SkPDFUtils::AppendScalar(r0, function);
     481           0 :         function->writeText(" add\n");
     482             : 
     483             :         // if r(t) > 0, we have our t, pop off the smaller root and we're done
     484           0 :         function->writeText(" 0 gt {exch pop true}\n");
     485             : 
     486             :         // otherwise, throw out the larger one and try the smaller root
     487           0 :         function->writeText("{pop dup\n");
     488           0 :         SkPDFUtils::AppendScalar(dr, function);
     489           0 :         function->writeText(" mul ");
     490           0 :         SkPDFUtils::AppendScalar(r0, function);
     491           0 :         function->writeText(" add\n");
     492             : 
     493             :         // if r(t) < 0, push false, otherwise the smaller root is our t
     494           0 :         function->writeText("0 le {pop false} {true} ifelse\n");
     495           0 :         function->writeText("} ifelse\n");
     496             : 
     497             :         // d < 0, clear the stack and push false
     498           0 :         function->writeText("} {pop pop pop false} ifelse\n");
     499             :     }
     500             : 
     501             :     // if the pixel is in the cone, proceed to compute a color
     502           0 :     function->writeText("{");
     503           0 :     tileModeCode(info.fTileMode, function);
     504           0 :     gradientFunctionCode(info, function);
     505             : 
     506             :     // otherwise, just write black
     507           0 :     function->writeText("} {0 0 0} ifelse }");
     508           0 : }
     509             : 
     510           0 : static void sweepCode(const SkShader::GradientInfo& info,
     511             :                           const SkMatrix& perspectiveRemover,
     512             :                           SkDynamicMemoryWStream* function) {
     513           0 :     function->writeText("{exch atan 360 div\n");
     514           0 :     tileModeCode(info.fTileMode, function);
     515           0 :     gradientFunctionCode(info, function);
     516           0 :     function->writeText("}");
     517           0 : }
     518             : 
     519           0 : static void drawBitmapMatrix(SkCanvas* canvas, const SkBitmap& bm, const SkMatrix& matrix) {
     520           0 :     SkAutoCanvasRestore acr(canvas, true);
     521           0 :     canvas->concat(matrix);
     522           0 :     canvas->drawBitmap(bm, 0, 0);
     523           0 : }
     524             : 
     525             : ////////////////////////////////////////////////////////////////////////////////
     526             : 
     527             : static sk_sp<SkPDFStream> make_alpha_function_shader(SkPDFDocument* doc,
     528             :                                                      SkScalar dpi,
     529             :                                                      const SkPDFShader::State& state);
     530             : static sk_sp<SkPDFDict> make_function_shader(SkPDFCanon* canon,
     531             :                                              const SkPDFShader::State& state);
     532             : 
     533             : static sk_sp<SkPDFStream> make_image_shader(SkPDFDocument* doc,
     534             :                                             SkScalar dpi,
     535             :                                             const SkPDFShader::State& state,
     536             :                                             SkBitmap image);
     537             : 
     538           0 : static sk_sp<SkPDFObject> get_pdf_shader_by_state(
     539             :         SkPDFDocument* doc,
     540             :         SkScalar dpi,
     541             :         SkPDFShader::State state,
     542             :         SkBitmap image) {
     543           0 :     SkPDFCanon* canon = doc->canon();
     544           0 :     if (state.fType == SkShader::kNone_GradientType && image.isNull()) {
     545             :         // TODO(vandebo) This drops SKComposeShader on the floor.  We could
     546             :         // handle compose shader by pulling things up to a layer, drawing with
     547             :         // the first shader, applying the xfer mode and drawing again with the
     548             :         // second shader, then applying the layer to the original drawing.
     549           0 :         return nullptr;
     550           0 :     } else if (state.fType == SkShader::kNone_GradientType) {
     551           0 :         sk_sp<SkPDFObject> shader = canon->findImageShader(state);
     552           0 :         if (!shader) {
     553           0 :             shader = make_image_shader(doc, dpi, state, std::move(image));
     554           0 :             canon->addImageShader(shader, std::move(state));
     555             :         }
     556           0 :         return shader;
     557           0 :     } else if (state.GradientHasAlpha()) {
     558           0 :         sk_sp<SkPDFObject> shader = canon->findAlphaShader(state);
     559           0 :         if (!shader) {
     560           0 :             shader = make_alpha_function_shader(doc, dpi, state);
     561           0 :             canon->addAlphaShader(shader, std::move(state));
     562             :         }
     563           0 :         return shader;
     564             :     } else {
     565           0 :         sk_sp<SkPDFObject> shader = canon->findFunctionShader(state);
     566           0 :         if (!shader) {
     567           0 :             shader = make_function_shader(canon, state);
     568           0 :             canon->addFunctionShader(shader, std::move(state));
     569             :         }
     570           0 :         return shader;
     571             :     }
     572             : }
     573             : 
     574           0 : sk_sp<SkPDFObject> SkPDFShader::GetPDFShader(SkPDFDocument* doc,
     575             :                                              SkScalar dpi,
     576             :                                              SkShader* shader,
     577             :                                              const SkMatrix& matrix,
     578             :                                              const SkIRect& surfaceBBox,
     579             :                                              SkScalar rasterScale) {
     580           0 :     if (surfaceBBox.isEmpty()) {
     581           0 :         return nullptr;
     582             :     }
     583           0 :     SkBitmap image;
     584           0 :     State state(shader, matrix, surfaceBBox, rasterScale, &image);
     585             :     return get_pdf_shader_by_state(
     586           0 :             doc, dpi, std::move(state), std::move(image));
     587             : }
     588             : 
     589           0 : static sk_sp<SkPDFDict> get_gradient_resource_dict(
     590             :         SkPDFObject* functionShader,
     591             :         SkPDFObject* gState) {
     592           0 :     SkTDArray<SkPDFObject*> patterns;
     593           0 :     if (functionShader) {
     594           0 :         patterns.push(functionShader);
     595             :     }
     596           0 :     SkTDArray<SkPDFObject*> graphicStates;
     597           0 :     if (gState) {
     598           0 :         graphicStates.push(gState);
     599             :     }
     600           0 :     return SkPDFResourceDict::Make(&graphicStates, &patterns, nullptr, nullptr);
     601             : }
     602             : 
     603           0 : static void populate_tiling_pattern_dict(SkPDFDict* pattern,
     604             :                                          SkRect& bbox,
     605             :                                          sk_sp<SkPDFDict> resources,
     606             :                                          const SkMatrix& matrix) {
     607           0 :     const int kTiling_PatternType = 1;
     608           0 :     const int kColoredTilingPattern_PaintType = 1;
     609           0 :     const int kConstantSpacing_TilingType = 1;
     610             : 
     611           0 :     pattern->insertName("Type", "Pattern");
     612           0 :     pattern->insertInt("PatternType", kTiling_PatternType);
     613           0 :     pattern->insertInt("PaintType", kColoredTilingPattern_PaintType);
     614           0 :     pattern->insertInt("TilingType", kConstantSpacing_TilingType);
     615           0 :     pattern->insertObject("BBox", SkPDFUtils::RectToArray(bbox));
     616           0 :     pattern->insertScalar("XStep", bbox.width());
     617           0 :     pattern->insertScalar("YStep", bbox.height());
     618           0 :     pattern->insertObject("Resources", std::move(resources));
     619           0 :     if (!matrix.isIdentity()) {
     620           0 :         pattern->insertObject("Matrix", SkPDFUtils::MatrixToArray(matrix));
     621             :     }
     622           0 : }
     623             : 
     624             : /**
     625             :  * Creates a content stream which fills the pattern P0 across bounds.
     626             :  * @param gsIndex A graphics state resource index to apply, or <0 if no
     627             :  * graphics state to apply.
     628             :  */
     629           0 : static std::unique_ptr<SkStreamAsset> create_pattern_fill_content(
     630             :         int gsIndex, SkRect& bounds) {
     631           0 :     SkDynamicMemoryWStream content;
     632           0 :     if (gsIndex >= 0) {
     633           0 :         SkPDFUtils::ApplyGraphicState(gsIndex, &content);
     634             :     }
     635           0 :     SkPDFUtils::ApplyPattern(0, &content);
     636           0 :     SkPDFUtils::AppendRectangle(bounds, &content);
     637             :     SkPDFUtils::PaintPath(SkPaint::kFill_Style, SkPath::kEvenOdd_FillType,
     638           0 :                           &content);
     639             : 
     640           0 :     return std::unique_ptr<SkStreamAsset>(content.detachAsStream());
     641             : }
     642             : 
     643             : /**
     644             :  * Creates a ExtGState with the SMask set to the luminosityShader in
     645             :  * luminosity mode. The shader pattern extends to the bbox.
     646             :  */
     647           0 : static sk_sp<SkPDFObject> create_smask_graphic_state(
     648             :         SkPDFDocument* doc, SkScalar dpi, const SkPDFShader::State& state) {
     649             :     SkRect bbox;
     650           0 :     bbox.set(state.fBBox);
     651             : 
     652             :     sk_sp<SkPDFObject> luminosityShader(
     653           0 :             get_pdf_shader_by_state(doc, dpi, state.MakeAlphaToLuminosityState(),
     654           0 :                                     SkBitmap()));
     655             : 
     656           0 :     std::unique_ptr<SkStreamAsset> alphaStream(create_pattern_fill_content(-1, bbox));
     657             : 
     658             :     sk_sp<SkPDFDict> resources =
     659           0 :         get_gradient_resource_dict(luminosityShader.get(), nullptr);
     660             : 
     661             :     sk_sp<SkPDFObject> alphaMask =
     662           0 :         SkPDFMakeFormXObject(std::move(alphaStream),
     663           0 :                              SkPDFUtils::RectToArray(bbox),
     664           0 :                              std::move(resources),
     665             :                              SkMatrix::I(),
     666           0 :                              "DeviceRGB");
     667           0 :     return SkPDFGraphicState::GetSMaskGraphicState(
     668           0 :             std::move(alphaMask), false,
     669           0 :             SkPDFGraphicState::kLuminosity_SMaskMode, doc->canon());
     670             : }
     671             : 
     672           0 : static sk_sp<SkPDFStream> make_alpha_function_shader(SkPDFDocument* doc,
     673             :                                                      SkScalar dpi,
     674             :                                                      const SkPDFShader::State& state) {
     675             :     SkRect bbox;
     676           0 :     bbox.set(state.fBBox);
     677             : 
     678           0 :     SkPDFShader::State opaqueState(state.MakeOpaqueState());
     679             : 
     680             :     sk_sp<SkPDFObject> colorShader(
     681           0 :             get_pdf_shader_by_state(doc, dpi, std::move(opaqueState), SkBitmap()));
     682           0 :     if (!colorShader) {
     683           0 :         return nullptr;
     684             :     }
     685             : 
     686             :     // Create resource dict with alpha graphics state as G0 and
     687             :     // pattern shader as P0, then write content stream.
     688           0 :     sk_sp<SkPDFObject> alphaGs = create_smask_graphic_state(doc, dpi, state);
     689             : 
     690             :     sk_sp<SkPDFDict> resourceDict =
     691           0 :             get_gradient_resource_dict(colorShader.get(), alphaGs.get());
     692             : 
     693             :     std::unique_ptr<SkStreamAsset> colorStream(
     694           0 :             create_pattern_fill_content(0, bbox));
     695           0 :     auto alphaFunctionShader = sk_make_sp<SkPDFStream>(std::move(colorStream));
     696             : 
     697           0 :     populate_tiling_pattern_dict(alphaFunctionShader->dict(), bbox,
     698           0 :                                  std::move(resourceDict), SkMatrix::I()); 
     699           0 :     return alphaFunctionShader;
     700             : }
     701             : 
     702             : // Finds affine and persp such that in = affine * persp.
     703             : // but it returns the inverse of perspective matrix.
     704           0 : static bool split_perspective(const SkMatrix in, SkMatrix* affine,
     705             :                               SkMatrix* perspectiveInverse) {
     706           0 :     const SkScalar p2 = in[SkMatrix::kMPersp2];
     707             : 
     708           0 :     if (SkScalarNearlyZero(p2)) {
     709           0 :         return false;
     710             :     }
     711             : 
     712           0 :     const SkScalar zero = SkIntToScalar(0);
     713           0 :     const SkScalar one = SkIntToScalar(1);
     714             : 
     715           0 :     const SkScalar sx = in[SkMatrix::kMScaleX];
     716           0 :     const SkScalar kx = in[SkMatrix::kMSkewX];
     717           0 :     const SkScalar tx = in[SkMatrix::kMTransX];
     718           0 :     const SkScalar ky = in[SkMatrix::kMSkewY];
     719           0 :     const SkScalar sy = in[SkMatrix::kMScaleY];
     720           0 :     const SkScalar ty = in[SkMatrix::kMTransY];
     721           0 :     const SkScalar p0 = in[SkMatrix::kMPersp0];
     722           0 :     const SkScalar p1 = in[SkMatrix::kMPersp1];
     723             : 
     724             :     // Perspective matrix would be:
     725             :     // 1  0  0
     726             :     // 0  1  0
     727             :     // p0 p1 p2
     728             :     // But we need the inverse of persp.
     729           0 :     perspectiveInverse->setAll(one,          zero,       zero,
     730             :                                zero,         one,        zero,
     731           0 :                                -p0/p2,     -p1/p2,     1/p2);
     732             : 
     733           0 :     affine->setAll(sx - p0 * tx / p2,       kx - p1 * tx / p2,      tx / p2,
     734           0 :                    ky - p0 * ty / p2,       sy - p1 * ty / p2,      ty / p2,
     735           0 :                    zero,                    zero,                   one);
     736             : 
     737           0 :     return true;
     738             : }
     739             : 
     740           0 : sk_sp<SkPDFArray> SkPDFShader::MakeRangeObject() {
     741           0 :     auto range = sk_make_sp<SkPDFArray>();
     742           0 :     range->reserve(6);
     743           0 :     range->appendInt(0);
     744           0 :     range->appendInt(1);
     745           0 :     range->appendInt(0);
     746           0 :     range->appendInt(1);
     747           0 :     range->appendInt(0);
     748           0 :     range->appendInt(1);
     749           0 :     return range;
     750             : }
     751             : 
     752           0 : static sk_sp<SkPDFStream> make_ps_function(
     753             :         std::unique_ptr<SkStreamAsset> psCode,
     754             :         sk_sp<SkPDFArray> domain,
     755             :         sk_sp<SkPDFObject> range) {
     756           0 :     auto result = sk_make_sp<SkPDFStream>(std::move(psCode));
     757           0 :     result->dict()->insertInt("FunctionType", 4);
     758           0 :     result->dict()->insertObject("Domain", std::move(domain));
     759           0 :     result->dict()->insertObject("Range", std::move(range));
     760           0 :     return result;
     761             : }
     762             : 
     763             : // catch cases where the inner just touches the outer circle
     764             : // and make the inner circle just inside the outer one to match raster
     765           0 : static void FixUpRadius(const SkPoint& p1, SkScalar& r1, const SkPoint& p2, SkScalar& r2) {
     766             :     // detect touching circles
     767           0 :     SkScalar distance = SkPoint::Distance(p1, p2);
     768           0 :     SkScalar subtractRadii = fabs(r1 - r2);
     769           0 :     if (fabs(distance - subtractRadii) < 0.002f) {
     770           0 :         if (r1 > r2) {
     771           0 :             r1 += 0.002f;
     772             :         } else {
     773           0 :             r2 += 0.002f;
     774             :         }
     775             :     }
     776           0 : }
     777             : 
     778           0 : static sk_sp<SkPDFDict> make_function_shader(SkPDFCanon* canon,
     779             :                                              const SkPDFShader::State& state) {
     780             :     void (*codeFunction)(const SkShader::GradientInfo& info,
     781             :                          const SkMatrix& perspectiveRemover,
     782           0 :                          SkDynamicMemoryWStream* function) = nullptr;
     783             :     SkPoint transformPoints[2];
     784           0 :     const SkShader::GradientInfo* info = &state.fInfo;
     785           0 :     SkMatrix finalMatrix = state.fCanvasTransform;
     786           0 :     finalMatrix.preConcat(state.fShaderTransform);
     787             : 
     788           0 :     bool doStitchFunctions = (state.fType == SkShader::kLinear_GradientType ||
     789           0 :                               state.fType == SkShader::kRadial_GradientType ||
     790           0 :                               state.fType == SkShader::kConical_GradientType) &&
     791           0 :                              info->fTileMode == SkShader::kClamp_TileMode &&
     792           0 :                              !finalMatrix.hasPerspective();
     793             : 
     794           0 :     auto domain = sk_make_sp<SkPDFArray>();
     795             : 
     796           0 :     int32_t shadingType = 1;
     797           0 :     auto pdfShader = sk_make_sp<SkPDFDict>();
     798             :     // The two point radial gradient further references
     799             :     // state.fInfo
     800             :     // in translating from x, y coordinates to the t parameter. So, we have
     801             :     // to transform the points and radii according to the calculated matrix.
     802           0 :     if (doStitchFunctions) {
     803           0 :         pdfShader->insertObject("Function", gradientStitchCode(*info));
     804           0 :         shadingType = (state.fType == SkShader::kLinear_GradientType) ? 2 : 3;
     805             : 
     806           0 :         auto extend = sk_make_sp<SkPDFArray>();
     807           0 :         extend->reserve(2);
     808           0 :         extend->appendBool(true);
     809           0 :         extend->appendBool(true);
     810           0 :         pdfShader->insertObject("Extend", std::move(extend));
     811             : 
     812           0 :         auto coords = sk_make_sp<SkPDFArray>();
     813           0 :         if (state.fType == SkShader::kConical_GradientType) {
     814           0 :             coords->reserve(6);
     815           0 :             SkScalar r1 = info->fRadius[0];
     816           0 :             SkScalar r2 = info->fRadius[1];
     817           0 :             SkPoint pt1 = info->fPoint[0];
     818           0 :             SkPoint pt2 = info->fPoint[1];
     819           0 :             FixUpRadius(pt1, r1, pt2, r2);
     820             : 
     821           0 :             coords->appendScalar(pt1.fX);
     822           0 :             coords->appendScalar(pt1.fY);
     823           0 :             coords->appendScalar(r1);
     824             : 
     825           0 :             coords->appendScalar(pt2.fX);
     826           0 :             coords->appendScalar(pt2.fY);
     827           0 :             coords->appendScalar(r2);
     828           0 :         } else if (state.fType == SkShader::kRadial_GradientType) {
     829           0 :             coords->reserve(6);
     830           0 :             const SkPoint& pt1 = info->fPoint[0];
     831             : 
     832           0 :             coords->appendScalar(pt1.fX);
     833           0 :             coords->appendScalar(pt1.fY);
     834           0 :             coords->appendScalar(0);
     835             : 
     836           0 :             coords->appendScalar(pt1.fX);
     837           0 :             coords->appendScalar(pt1.fY);
     838           0 :             coords->appendScalar(info->fRadius[0]);
     839             :         } else {
     840           0 :             coords->reserve(4);
     841           0 :             const SkPoint& pt1 = info->fPoint[0];
     842           0 :             const SkPoint& pt2 = info->fPoint[1];
     843             : 
     844           0 :             coords->appendScalar(pt1.fX);
     845           0 :             coords->appendScalar(pt1.fY);
     846             : 
     847           0 :             coords->appendScalar(pt2.fX);
     848           0 :             coords->appendScalar(pt2.fY);
     849             :         }
     850             : 
     851           0 :         pdfShader->insertObject("Coords", std::move(coords));
     852             :     } else {
     853             :         // Depending on the type of the gradient, we want to transform the
     854             :         // coordinate space in different ways.
     855           0 :         transformPoints[0] = info->fPoint[0];
     856           0 :         transformPoints[1] = info->fPoint[1];
     857           0 :         switch (state.fType) {
     858             :             case SkShader::kLinear_GradientType:
     859           0 :                 codeFunction = &linearCode;
     860           0 :                 break;
     861             :             case SkShader::kRadial_GradientType:
     862           0 :                 transformPoints[1] = transformPoints[0];
     863           0 :                 transformPoints[1].fX += info->fRadius[0];
     864           0 :                 codeFunction = &radialCode;
     865           0 :                 break;
     866             :             case SkShader::kConical_GradientType: {
     867           0 :                 transformPoints[1] = transformPoints[0];
     868           0 :                 transformPoints[1].fX += SK_Scalar1;
     869           0 :                 codeFunction = &twoPointConicalCode;
     870           0 :                 break;
     871             :             }
     872             :             case SkShader::kSweep_GradientType:
     873           0 :                 transformPoints[1] = transformPoints[0];
     874           0 :                 transformPoints[1].fX += SK_Scalar1;
     875           0 :                 codeFunction = &sweepCode;
     876           0 :                 break;
     877             :             case SkShader::kColor_GradientType:
     878             :             case SkShader::kNone_GradientType:
     879             :             default:
     880           0 :                 return nullptr;
     881             :         }
     882             : 
     883             :         // Move any scaling (assuming a unit gradient) or translation
     884             :         // (and rotation for linear gradient), of the final gradient from
     885             :         // info->fPoints to the matrix (updating bbox appropriately).  Now
     886             :         // the gradient can be drawn on on the unit segment.
     887             :         SkMatrix mapperMatrix;
     888           0 :         unitToPointsMatrix(transformPoints, &mapperMatrix);
     889             : 
     890           0 :         finalMatrix.preConcat(mapperMatrix);
     891             : 
     892             :         // Preserves as much as posible in the final matrix, and only removes
     893             :         // the perspective. The inverse of the perspective is stored in
     894             :         // perspectiveInverseOnly matrix and has 3 useful numbers
     895             :         // (p0, p1, p2), while everything else is either 0 or 1.
     896             :         // In this way the shader will handle it eficiently, with minimal code.
     897           0 :         SkMatrix perspectiveInverseOnly = SkMatrix::I();
     898           0 :         if (finalMatrix.hasPerspective()) {
     899           0 :             if (!split_perspective(finalMatrix,
     900             :                                    &finalMatrix, &perspectiveInverseOnly)) {
     901           0 :                 return nullptr;
     902             :             }
     903             :         }
     904             : 
     905             :         SkRect bbox;
     906           0 :         bbox.set(state.fBBox);
     907           0 :         if (!inverse_transform_bbox(finalMatrix, &bbox)) {
     908           0 :             return nullptr;
     909             :         }
     910           0 :         domain->reserve(4);
     911           0 :         domain->appendScalar(bbox.fLeft);
     912           0 :         domain->appendScalar(bbox.fRight);
     913           0 :         domain->appendScalar(bbox.fTop);
     914           0 :         domain->appendScalar(bbox.fBottom);
     915             :         
     916           0 :         SkDynamicMemoryWStream functionCode;
     917             :         
     918           0 :         if (state.fType == SkShader::kConical_GradientType) {
     919           0 :             SkShader::GradientInfo twoPointRadialInfo = *info;
     920             :             SkMatrix inverseMapperMatrix;
     921           0 :             if (!mapperMatrix.invert(&inverseMapperMatrix)) {
     922           0 :                 return nullptr;
     923             :             }
     924           0 :             inverseMapperMatrix.mapPoints(twoPointRadialInfo.fPoint, 2);
     925           0 :             twoPointRadialInfo.fRadius[0] =
     926           0 :                 inverseMapperMatrix.mapRadius(info->fRadius[0]);
     927           0 :             twoPointRadialInfo.fRadius[1] =
     928           0 :                 inverseMapperMatrix.mapRadius(info->fRadius[1]);
     929           0 :             codeFunction(twoPointRadialInfo, perspectiveInverseOnly, &functionCode);
     930             :         } else {
     931           0 :             codeFunction(*info, perspectiveInverseOnly, &functionCode);
     932             :         }
     933             :         
     934           0 :         pdfShader->insertObject("Domain", domain);
     935             : 
     936             :         // Call canon->makeRangeObject() instead of
     937             :         // SkPDFShader::MakeRangeObject() so that the canon can
     938             :         // deduplicate.
     939             :         std::unique_ptr<SkStreamAsset> functionStream(
     940           0 :                 functionCode.detachAsStream());
     941           0 :         sk_sp<SkPDFStream> function = make_ps_function(std::move(functionStream),
     942           0 :                                                        std::move(domain),
     943           0 :                                                        canon->makeRangeObject());
     944           0 :         pdfShader->insertObjRef("Function", std::move(function));
     945             :     }
     946             : 
     947           0 :     pdfShader->insertInt("ShadingType", shadingType);
     948           0 :     pdfShader->insertName("ColorSpace", "DeviceRGB");
     949             : 
     950           0 :     auto pdfFunctionShader = sk_make_sp<SkPDFDict>("Pattern");
     951           0 :     pdfFunctionShader->insertInt("PatternType", 2);
     952           0 :     pdfFunctionShader->insertObject("Matrix",
     953           0 :                                     SkPDFUtils::MatrixToArray(finalMatrix));
     954           0 :     pdfFunctionShader->insertObject("Shading", std::move(pdfShader));
     955             : 
     956           0 :     return pdfFunctionShader;
     957             : }
     958             : 
     959           0 : static sk_sp<SkPDFStream> make_image_shader(SkPDFDocument* doc,
     960             :                                             SkScalar dpi,
     961             :                                             const SkPDFShader::State& state,
     962             :                                             SkBitmap image) {
     963           0 :     SkASSERT(state.fBitmapKey ==
     964             :              (SkBitmapKey{image.getSubset(), image.getGenerationID()}));
     965           0 :     SkAutoLockPixels SkAutoLockPixels(image);
     966             : 
     967             :     // The image shader pattern cell will be drawn into a separate device
     968             :     // in pattern cell space (no scaling on the bitmap, though there may be
     969             :     // translations so that all content is in the device, coordinates > 0).
     970             : 
     971             :     // Map clip bounds to shader space to ensure the device is large enough
     972             :     // to handle fake clamping.
     973           0 :     SkMatrix finalMatrix = state.fCanvasTransform;
     974           0 :     finalMatrix.preConcat(state.fShaderTransform);
     975             :     SkRect deviceBounds;
     976           0 :     deviceBounds.set(state.fBBox);
     977           0 :     if (!inverse_transform_bbox(finalMatrix, &deviceBounds)) {
     978           0 :         return nullptr;
     979             :     }
     980             : 
     981             :     SkRect bitmapBounds;
     982           0 :     image.getBounds(&bitmapBounds);
     983             : 
     984             :     // For tiling modes, the bounds should be extended to include the bitmap,
     985             :     // otherwise the bitmap gets clipped out and the shader is empty and awful.
     986             :     // For clamp modes, we're only interested in the clip region, whether
     987             :     // or not the main bitmap is in it.
     988             :     SkShader::TileMode tileModes[2];
     989           0 :     tileModes[0] = state.fImageTileModes[0];
     990           0 :     tileModes[1] = state.fImageTileModes[1];
     991           0 :     if (tileModes[0] != SkShader::kClamp_TileMode ||
     992           0 :             tileModes[1] != SkShader::kClamp_TileMode) {
     993           0 :         deviceBounds.join(bitmapBounds);
     994             :     }
     995             : 
     996           0 :     SkISize size = SkISize::Make(SkScalarRoundToInt(deviceBounds.width()),
     997           0 :                                  SkScalarRoundToInt(deviceBounds.height()));
     998             :     sk_sp<SkPDFDevice> patternDevice(
     999           0 :             SkPDFDevice::CreateUnflipped(size, dpi, doc));
    1000           0 :     SkCanvas canvas(patternDevice.get());
    1001             : 
    1002             :     SkRect patternBBox;
    1003           0 :     image.getBounds(&patternBBox);
    1004             : 
    1005             :     // Translate the canvas so that the bitmap origin is at (0, 0).
    1006           0 :     canvas.translate(-deviceBounds.left(), -deviceBounds.top());
    1007           0 :     patternBBox.offset(-deviceBounds.left(), -deviceBounds.top());
    1008             :     // Undo the translation in the final matrix
    1009           0 :     finalMatrix.preTranslate(deviceBounds.left(), deviceBounds.top());
    1010             : 
    1011             :     // If the bitmap is out of bounds (i.e. clamp mode where we only see the
    1012             :     // stretched sides), canvas will clip this out and the extraneous data
    1013             :     // won't be saved to the PDF.
    1014           0 :     canvas.drawBitmap(image, 0, 0);
    1015             : 
    1016           0 :     SkScalar width = SkIntToScalar(image.width());
    1017           0 :     SkScalar height = SkIntToScalar(image.height());
    1018             : 
    1019             :     // Tiling is implied.  First we handle mirroring.
    1020           0 :     if (tileModes[0] == SkShader::kMirror_TileMode) {
    1021             :         SkMatrix xMirror;
    1022           0 :         xMirror.setScale(-1, 1);
    1023           0 :         xMirror.postTranslate(2 * width, 0);
    1024           0 :         drawBitmapMatrix(&canvas, image, xMirror);
    1025           0 :         patternBBox.fRight += width;
    1026             :     }
    1027           0 :     if (tileModes[1] == SkShader::kMirror_TileMode) {
    1028             :         SkMatrix yMirror;
    1029           0 :         yMirror.setScale(SK_Scalar1, -SK_Scalar1);
    1030           0 :         yMirror.postTranslate(0, 2 * height);
    1031           0 :         drawBitmapMatrix(&canvas, image, yMirror);
    1032           0 :         patternBBox.fBottom += height;
    1033             :     }
    1034           0 :     if (tileModes[0] == SkShader::kMirror_TileMode &&
    1035           0 :             tileModes[1] == SkShader::kMirror_TileMode) {
    1036             :         SkMatrix mirror;
    1037           0 :         mirror.setScale(-1, -1);
    1038           0 :         mirror.postTranslate(2 * width, 2 * height);
    1039           0 :         drawBitmapMatrix(&canvas, image, mirror);
    1040             :     }
    1041             : 
    1042             :     // Then handle Clamping, which requires expanding the pattern canvas to
    1043             :     // cover the entire surfaceBBox.
    1044             : 
    1045             :     // If both x and y are in clamp mode, we start by filling in the corners.
    1046             :     // (Which are just a rectangles of the corner colors.)
    1047           0 :     if (tileModes[0] == SkShader::kClamp_TileMode &&
    1048           0 :             tileModes[1] == SkShader::kClamp_TileMode) {
    1049           0 :         SkPaint paint;
    1050             :         SkRect rect;
    1051           0 :         rect = SkRect::MakeLTRB(deviceBounds.left(), deviceBounds.top(), 0, 0);
    1052           0 :         if (!rect.isEmpty()) {
    1053           0 :             paint.setColor(image.getColor(0, 0));
    1054           0 :             canvas.drawRect(rect, paint);
    1055             :         }
    1056             : 
    1057             :         rect = SkRect::MakeLTRB(width, deviceBounds.top(),
    1058           0 :                                 deviceBounds.right(), 0);
    1059           0 :         if (!rect.isEmpty()) {
    1060           0 :             paint.setColor(image.getColor(image.width() - 1, 0));
    1061           0 :             canvas.drawRect(rect, paint);
    1062             :         }
    1063             : 
    1064             :         rect = SkRect::MakeLTRB(width, height,
    1065           0 :                                 deviceBounds.right(), deviceBounds.bottom());
    1066           0 :         if (!rect.isEmpty()) {
    1067           0 :             paint.setColor(image.getColor(image.width() - 1,
    1068           0 :                                            image.height() - 1));
    1069           0 :             canvas.drawRect(rect, paint);
    1070             :         }
    1071             : 
    1072             :         rect = SkRect::MakeLTRB(deviceBounds.left(), height,
    1073           0 :                                 0, deviceBounds.bottom());
    1074           0 :         if (!rect.isEmpty()) {
    1075           0 :             paint.setColor(image.getColor(0, image.height() - 1));
    1076           0 :             canvas.drawRect(rect, paint);
    1077             :         }
    1078             :     }
    1079             : 
    1080             :     // Then expand the left, right, top, then bottom.
    1081           0 :     if (tileModes[0] == SkShader::kClamp_TileMode) {
    1082           0 :         SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, image.height());
    1083           0 :         if (deviceBounds.left() < 0) {
    1084           0 :             SkBitmap left;
    1085           0 :             SkAssertResult(image.extractSubset(&left, subset));
    1086             : 
    1087             :             SkMatrix leftMatrix;
    1088           0 :             leftMatrix.setScale(-deviceBounds.left(), 1);
    1089           0 :             leftMatrix.postTranslate(deviceBounds.left(), 0);
    1090           0 :             drawBitmapMatrix(&canvas, left, leftMatrix);
    1091             : 
    1092           0 :             if (tileModes[1] == SkShader::kMirror_TileMode) {
    1093           0 :                 leftMatrix.postScale(SK_Scalar1, -SK_Scalar1);
    1094           0 :                 leftMatrix.postTranslate(0, 2 * height);
    1095           0 :                 drawBitmapMatrix(&canvas, left, leftMatrix);
    1096             :             }
    1097           0 :             patternBBox.fLeft = 0;
    1098             :         }
    1099             : 
    1100           0 :         if (deviceBounds.right() > width) {
    1101           0 :             SkBitmap right;
    1102           0 :             subset.offset(image.width() - 1, 0);
    1103           0 :             SkAssertResult(image.extractSubset(&right, subset));
    1104             : 
    1105             :             SkMatrix rightMatrix;
    1106           0 :             rightMatrix.setScale(deviceBounds.right() - width, 1);
    1107           0 :             rightMatrix.postTranslate(width, 0);
    1108           0 :             drawBitmapMatrix(&canvas, right, rightMatrix);
    1109             : 
    1110           0 :             if (tileModes[1] == SkShader::kMirror_TileMode) {
    1111           0 :                 rightMatrix.postScale(SK_Scalar1, -SK_Scalar1);
    1112           0 :                 rightMatrix.postTranslate(0, 2 * height);
    1113           0 :                 drawBitmapMatrix(&canvas, right, rightMatrix);
    1114             :             }
    1115           0 :             patternBBox.fRight = deviceBounds.width();
    1116             :         }
    1117             :     }
    1118             : 
    1119           0 :     if (tileModes[1] == SkShader::kClamp_TileMode) {
    1120           0 :         SkIRect subset = SkIRect::MakeXYWH(0, 0, image.width(), 1);
    1121           0 :         if (deviceBounds.top() < 0) {
    1122           0 :             SkBitmap top;
    1123           0 :             SkAssertResult(image.extractSubset(&top, subset));
    1124             : 
    1125             :             SkMatrix topMatrix;
    1126           0 :             topMatrix.setScale(SK_Scalar1, -deviceBounds.top());
    1127           0 :             topMatrix.postTranslate(0, deviceBounds.top());
    1128           0 :             drawBitmapMatrix(&canvas, top, topMatrix);
    1129             : 
    1130           0 :             if (tileModes[0] == SkShader::kMirror_TileMode) {
    1131           0 :                 topMatrix.postScale(-1, 1);
    1132           0 :                 topMatrix.postTranslate(2 * width, 0);
    1133           0 :                 drawBitmapMatrix(&canvas, top, topMatrix);
    1134             :             }
    1135           0 :             patternBBox.fTop = 0;
    1136             :         }
    1137             : 
    1138           0 :         if (deviceBounds.bottom() > height) {
    1139           0 :             SkBitmap bottom;
    1140           0 :             subset.offset(0, image.height() - 1);
    1141           0 :             SkAssertResult(image.extractSubset(&bottom, subset));
    1142             : 
    1143             :             SkMatrix bottomMatrix;
    1144           0 :             bottomMatrix.setScale(SK_Scalar1, deviceBounds.bottom() - height);
    1145           0 :             bottomMatrix.postTranslate(0, height);
    1146           0 :             drawBitmapMatrix(&canvas, bottom, bottomMatrix);
    1147             : 
    1148           0 :             if (tileModes[0] == SkShader::kMirror_TileMode) {
    1149           0 :                 bottomMatrix.postScale(-1, 1);
    1150           0 :                 bottomMatrix.postTranslate(2 * width, 0);
    1151           0 :                 drawBitmapMatrix(&canvas, bottom, bottomMatrix);
    1152             :             }
    1153           0 :             patternBBox.fBottom = deviceBounds.height();
    1154             :         }
    1155             :     }
    1156             : 
    1157           0 :     auto imageShader = sk_make_sp<SkPDFStream>(patternDevice->content());
    1158           0 :     populate_tiling_pattern_dict(imageShader->dict(), patternBBox,
    1159           0 :                                  patternDevice->makeResourceDict(), finalMatrix);
    1160           0 :     return imageShader;
    1161             : }
    1162             : 
    1163           0 : bool SkPDFShader::State::operator==(const SkPDFShader::State& b) const {
    1164           0 :     if (fType != b.fType ||
    1165           0 :             fCanvasTransform != b.fCanvasTransform ||
    1166           0 :             fShaderTransform != b.fShaderTransform ||
    1167           0 :             fBBox != b.fBBox) {
    1168           0 :         return false;
    1169             :     }
    1170             : 
    1171           0 :     if (fType == SkShader::kNone_GradientType) {
    1172           0 :         if (fBitmapKey != b.fBitmapKey ||
    1173           0 :                 fBitmapKey.fID == 0 ||
    1174           0 :                 fImageTileModes[0] != b.fImageTileModes[0] ||
    1175           0 :                 fImageTileModes[1] != b.fImageTileModes[1]) {
    1176           0 :             return false;
    1177             :         }
    1178             :     } else {
    1179           0 :         if (fInfo.fColorCount != b.fInfo.fColorCount ||
    1180           0 :                 memcmp(fInfo.fColors, b.fInfo.fColors,
    1181           0 :                        sizeof(SkColor) * fInfo.fColorCount) != 0 ||
    1182           0 :                 memcmp(fInfo.fColorOffsets, b.fInfo.fColorOffsets,
    1183           0 :                        sizeof(SkScalar) * fInfo.fColorCount) != 0 ||
    1184           0 :                 fInfo.fPoint[0] != b.fInfo.fPoint[0] ||
    1185           0 :                 fInfo.fTileMode != b.fInfo.fTileMode) {
    1186           0 :             return false;
    1187             :         }
    1188             : 
    1189           0 :         switch (fType) {
    1190             :             case SkShader::kLinear_GradientType:
    1191           0 :                 if (fInfo.fPoint[1] != b.fInfo.fPoint[1]) {
    1192           0 :                     return false;
    1193             :                 }
    1194           0 :                 break;
    1195             :             case SkShader::kRadial_GradientType:
    1196           0 :                 if (fInfo.fRadius[0] != b.fInfo.fRadius[0]) {
    1197           0 :                     return false;
    1198             :                 }
    1199           0 :                 break;
    1200             :             case SkShader::kConical_GradientType:
    1201           0 :                 if (fInfo.fPoint[1] != b.fInfo.fPoint[1] ||
    1202           0 :                         fInfo.fRadius[0] != b.fInfo.fRadius[0] ||
    1203           0 :                         fInfo.fRadius[1] != b.fInfo.fRadius[1]) {
    1204           0 :                     return false;
    1205             :                 }
    1206           0 :                 break;
    1207             :             case SkShader::kSweep_GradientType:
    1208             :             case SkShader::kNone_GradientType:
    1209             :             case SkShader::kColor_GradientType:
    1210           0 :                 break;
    1211             :         }
    1212             :     }
    1213           0 :     return true;
    1214             : }
    1215             : 
    1216           0 : SkPDFShader::State::State(SkShader* shader, const SkMatrix& canvasTransform,
    1217             :                           const SkIRect& bbox, SkScalar rasterScale,
    1218           0 :                           SkBitmap* imageDst)
    1219             :         : fType(SkShader::kNone_GradientType)
    1220             :         , fInfo{0, nullptr, nullptr, {{0.0f, 0.0f}, {0.0f, 0.0f}},
    1221             :                 {0.0f, 0.0f}, SkShader::kClamp_TileMode, 0}
    1222             :         , fCanvasTransform(canvasTransform)
    1223           0 :         , fShaderTransform{SkMatrix::I()}
    1224             :         , fBBox(bbox)
    1225             :         , fBitmapKey{{0, 0, 0, 0}, 0}
    1226             :         , fImageTileModes{SkShader::kClamp_TileMode,
    1227           0 :                           SkShader::kClamp_TileMode} {
    1228           0 :     SkASSERT(imageDst);
    1229           0 :     fInfo.fColorCount = 0;
    1230           0 :     fInfo.fColors = nullptr;
    1231           0 :     fInfo.fColorOffsets = nullptr;
    1232           0 :     fImageTileModes[0] = fImageTileModes[1] = SkShader::kClamp_TileMode;
    1233           0 :     fType = shader->asAGradient(&fInfo);
    1234             : 
    1235           0 :     if (fType != SkShader::kNone_GradientType) {
    1236           0 :         fBitmapKey = SkBitmapKey{{0, 0, 0, 0}, 0};
    1237           0 :         fShaderTransform = shader->getLocalMatrix();
    1238           0 :         this->allocateGradientInfoStorage();
    1239           0 :         shader->asAGradient(&fInfo);
    1240           0 :         return;
    1241             :     }
    1242           0 :     if (SkImage* skimg = shader->isAImage(&fShaderTransform, fImageTileModes)) {
    1243             :         // TODO(halcanary): delay converting to bitmap.
    1244           0 :         if (skimg->asLegacyBitmap(imageDst, SkImage::kRO_LegacyBitmapMode)) {
    1245           0 :             fBitmapKey = SkBitmapKey{imageDst->getSubset(), imageDst->getGenerationID()};
    1246           0 :             return;
    1247             :         }
    1248             :     }
    1249           0 :     fShaderTransform = shader->getLocalMatrix();
    1250             :     // Generic fallback for unsupported shaders:
    1251             :     //  * allocate a bbox-sized bitmap
    1252             :     //  * shade the whole area
    1253             :     //  * use the result as a bitmap shader
    1254             : 
    1255             :     // bbox is in device space. While that's exactly what we
    1256             :     // want for sizing our bitmap, we need to map it into
    1257             :     // shader space for adjustments (to match
    1258             :     // MakeImageShader's behavior).
    1259           0 :     SkRect shaderRect = SkRect::Make(bbox);
    1260           0 :     if (!inverse_transform_bbox(canvasTransform, &shaderRect)) {
    1261           0 :         imageDst->reset();
    1262           0 :         return;
    1263             :     }
    1264             : 
    1265             :     // Clamp the bitmap size to about 1M pixels
    1266             :     static const SkScalar kMaxBitmapArea = 1024 * 1024;
    1267           0 :     SkScalar bitmapArea = rasterScale * bbox.width() * rasterScale * bbox.height();
    1268           0 :     if (bitmapArea > kMaxBitmapArea) {
    1269           0 :         rasterScale *= SkScalarSqrt(kMaxBitmapArea / bitmapArea);
    1270             :     }
    1271             : 
    1272           0 :     SkISize size = {SkScalarRoundToInt(rasterScale * bbox.width()),
    1273           0 :                     SkScalarRoundToInt(rasterScale * bbox.height())};
    1274           0 :     SkSize scale = {SkIntToScalar(size.width()) / shaderRect.width(),
    1275           0 :                     SkIntToScalar(size.height()) / shaderRect.height()};
    1276             : 
    1277           0 :     imageDst->allocN32Pixels(size.width(), size.height());
    1278           0 :     imageDst->eraseColor(SK_ColorTRANSPARENT);
    1279             : 
    1280           0 :     SkPaint p;
    1281           0 :     p.setShader(sk_ref_sp(shader));
    1282             : 
    1283           0 :     SkCanvas canvas(*imageDst);
    1284           0 :     canvas.scale(scale.width(), scale.height());
    1285           0 :     canvas.translate(-shaderRect.x(), -shaderRect.y());
    1286           0 :     canvas.drawPaint(p);
    1287             : 
    1288           0 :     fShaderTransform.setTranslate(shaderRect.x(), shaderRect.y());
    1289           0 :     fShaderTransform.preScale(1 / scale.width(), 1 / scale.height());
    1290           0 :     fBitmapKey = SkBitmapKey{imageDst->getSubset(), imageDst->getGenerationID()};
    1291             : }
    1292             : 
    1293           0 : SkPDFShader::State::State(const SkPDFShader::State& other)
    1294           0 :   : fType(other.fType),
    1295             :     fCanvasTransform(other.fCanvasTransform),
    1296             :     fShaderTransform(other.fShaderTransform),
    1297           0 :     fBBox(other.fBBox)
    1298             : {
    1299             :     // Only gradients supported for now, since that is all that is used.
    1300             :     // If needed, image state copy constructor can be added here later.
    1301           0 :     SkASSERT(fType != SkShader::kNone_GradientType);
    1302             : 
    1303           0 :     if (fType != SkShader::kNone_GradientType) {
    1304           0 :         fInfo = other.fInfo;
    1305             : 
    1306           0 :         this->allocateGradientInfoStorage();
    1307           0 :         for (int i = 0; i < fInfo.fColorCount; i++) {
    1308           0 :             fInfo.fColors[i] = other.fInfo.fColors[i];
    1309           0 :             fInfo.fColorOffsets[i] = other.fInfo.fColorOffsets[i];
    1310             :         }
    1311             :     }
    1312           0 : }
    1313             : 
    1314             : /**
    1315             :  * Create a copy of this gradient state with alpha assigned to RGB luminousity.
    1316             :  * Only valid for gradient states.
    1317             :  */
    1318           0 : SkPDFShader::State SkPDFShader::State::MakeAlphaToLuminosityState() const {
    1319           0 :     SkASSERT(fBitmapKey == (SkBitmapKey{{0, 0, 0, 0}, 0}));
    1320           0 :     SkASSERT(fType != SkShader::kNone_GradientType);
    1321             : 
    1322           0 :     SkPDFShader::State newState(*this);
    1323             : 
    1324           0 :     for (int i = 0; i < fInfo.fColorCount; i++) {
    1325           0 :         SkAlpha alpha = SkColorGetA(fInfo.fColors[i]);
    1326           0 :         newState.fInfo.fColors[i] = SkColorSetARGB(255, alpha, alpha, alpha);
    1327             :     }
    1328             : 
    1329           0 :     return newState;
    1330             : }
    1331             : 
    1332             : /**
    1333             :  * Create a copy of this gradient state with alpha set to fully opaque
    1334             :  * Only valid for gradient states.
    1335             :  */
    1336           0 : SkPDFShader::State SkPDFShader::State::MakeOpaqueState() const {
    1337           0 :     SkASSERT(fBitmapKey == (SkBitmapKey{{0, 0, 0, 0}, 0}));
    1338           0 :     SkASSERT(fType != SkShader::kNone_GradientType);
    1339             : 
    1340           0 :     SkPDFShader::State newState(*this);
    1341           0 :     for (int i = 0; i < fInfo.fColorCount; i++) {
    1342           0 :         newState.fInfo.fColors[i] = SkColorSetA(fInfo.fColors[i],
    1343             :                                                  SK_AlphaOPAQUE);
    1344             :     }
    1345             : 
    1346           0 :     return newState;
    1347             : }
    1348             : 
    1349             : /**
    1350             :  * Returns true if state is a gradient and the gradient has alpha.
    1351             :  */
    1352           0 : bool SkPDFShader::State::GradientHasAlpha() const {
    1353           0 :     if (fType == SkShader::kNone_GradientType) {
    1354           0 :         return false;
    1355             :     }
    1356             : 
    1357           0 :     for (int i = 0; i < fInfo.fColorCount; i++) {
    1358           0 :         SkAlpha alpha = SkColorGetA(fInfo.fColors[i]);
    1359           0 :         if (alpha != SK_AlphaOPAQUE) {
    1360           0 :             return true;
    1361             :         }
    1362             :     }
    1363           0 :     return false;
    1364             : }
    1365             : 
    1366           0 : void SkPDFShader::State::allocateGradientInfoStorage() {
    1367           0 :     fColors.reset(new SkColor[fInfo.fColorCount]);
    1368           0 :     fStops.reset(new SkScalar[fInfo.fColorCount]);
    1369           0 :     fInfo.fColors = fColors.get();
    1370           0 :     fInfo.fColorOffsets = fStops.get();
    1371           0 : }

Generated by: LCOV version 1.13