11cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 20b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger/* 31cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger * Copyright 2011 Google Inc. 40b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger * 51cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger * Use of this source code is governed by a BSD-style license that can be 61cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger * found in the LICENSE file. 70b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger */ 80b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 91cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 100b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger#include "SkPDFShader.h" 110b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 120b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger#include "SkCanvas.h" 131cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger#include "SkData.h" 140b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger#include "SkPDFCatalog.h" 150b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger#include "SkPDFDevice.h" 160b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger#include "SkPDFTypes.h" 170b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger#include "SkPDFUtils.h" 180b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger#include "SkScalar.h" 190b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger#include "SkStream.h" 200b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger#include "SkTemplates.h" 210b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger#include "SkThread.h" 220b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger#include "SkTypes.h" 230b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 240b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenbergerstatic void transformBBox(const SkMatrix& matrix, SkRect* bbox) { 250b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkMatrix inverse; 260b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger inverse.reset(); 270b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger matrix.invert(&inverse); 280b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger inverse.mapRect(bbox); 290b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger} 300b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 310b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenbergerstatic void unitToPointsMatrix(const SkPoint pts[2], SkMatrix* matrix) { 320b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkVector vec = pts[1] - pts[0]; 330b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkScalar mag = vec.length(); 340b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkScalar inv = mag ? SkScalarInvert(mag) : 0; 350b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 360b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger vec.scale(inv); 370b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger matrix->setSinCos(vec.fY, vec.fX); 380b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger matrix->preTranslate(pts[0].fX, pts[0].fY); 390b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger matrix->preScale(mag, mag); 400b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger} 410b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 420b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger/* Assumes t + startOffset is on the stack and does a linear interpolation on t 430b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger between startOffset and endOffset from prevColor to curColor (for each color 440b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger component), leaving the result in component order on the stack. 450b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger @param range endOffset - startOffset 460b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger @param curColor[components] The current color components. 470b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger @param prevColor[components] The previous color components. 480b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger @param result The result ps function. 490b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger */ 500b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenbergerstatic void interpolateColorCode(SkScalar range, SkScalar* curColor, 510b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkScalar* prevColor, int components, 520b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkString* result) { 530b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // Figure out how to scale each color component. 540b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkAutoSTMalloc<4, SkScalar> multiplierAlloc(components); 550b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkScalar *multiplier = multiplierAlloc.get(); 560b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger for (int i = 0; i < components; i++) { 570b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger multiplier[i] = SkScalarDiv(curColor[i] - prevColor[i], range); 580b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 590b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 600b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // Calculate when we no longer need to keep a copy of the input parameter t. 610b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // If the last component to use t is i, then dupInput[0..i - 1] = true 620b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // and dupInput[i .. components] = false. 630b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkAutoSTMalloc<4, bool> dupInputAlloc(components); 640b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger bool *dupInput = dupInputAlloc.get(); 650b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger dupInput[components - 1] = false; 660b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger for (int i = components - 2; i >= 0; i--) { 670b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger dupInput[i] = dupInput[i + 1] || multiplier[i + 1] != 0; 680b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 690b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 700b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (!dupInput[0] && multiplier[0] == 0) { 710b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->append("pop "); 720b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 730b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 740b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger for (int i = 0; i < components; i++) { 750b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // If the next components needs t, make a copy. 760b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (dupInput[i]) { 770b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->append("dup "); 780b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 790b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 800b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (multiplier[i] == 0) { 810b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->appendScalar(prevColor[i]); 820b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->append(" "); 830b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } else { 840b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (multiplier[i] != 1) { 850b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->appendScalar(multiplier[i]); 860b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->append(" mul "); 870b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 880b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (prevColor[i] != 0) { 890b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->appendScalar(prevColor[i]); 900b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->append(" add "); 910b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 920b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 930b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 940b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (dupInput[i]) { 950b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->append("exch\n"); 960b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 970b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 980b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger} 990b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 1000b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger/* Generate Type 4 function code to map t=[0,1) to the passed gradient, 1010b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger clamping at the edges of the range. The generated code will be of the form: 1020b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (t < 0) { 1030b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger return colorData[0][r,g,b]; 1040b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } else { 1050b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (t < info.fColorOffsets[1]) { 1060b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger return linearinterpolation(colorData[0][r,g,b], 1070b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger colorData[1][r,g,b]); 1080b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } else { 1090b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (t < info.fColorOffsets[2]) { 1100b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger return linearinterpolation(colorData[1][r,g,b], 1110b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger colorData[2][r,g,b]); 1120b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } else { 1130b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 1140b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger ... } else { 1150b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger return colorData[info.fColorCount - 1][r,g,b]; 1160b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 1170b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger ... 1180b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 1190b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 1200b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger */ 1210b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenbergerstatic void gradientFunctionCode(const SkShader::GradientInfo& info, 1220b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkString* result) { 1230b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger /* We want to linearly interpolate from the previous color to the next. 1240b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger Scale the colors from 0..255 to 0..1 and determine the multipliers 1250b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger for interpolation. 1260b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger C{r,g,b}(t, section) = t - offset_(section-1) + t * Multiplier{r,g,b}. 1270b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger */ 1280b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger static const int kColorComponents = 3; 1290b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger typedef SkScalar ColorTuple[kColorComponents]; 1300b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkAutoSTMalloc<4, ColorTuple> colorDataAlloc(info.fColorCount); 1310b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger ColorTuple *colorData = colorDataAlloc.get(); 1320b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger const SkScalar scale = SkScalarInvert(SkIntToScalar(255)); 1330b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger for (int i = 0; i < info.fColorCount; i++) { 1340b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger colorData[i][0] = SkScalarMul(SkColorGetR(info.fColors[i]), scale); 1350b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger colorData[i][1] = SkScalarMul(SkColorGetG(info.fColors[i]), scale); 1360b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger colorData[i][2] = SkScalarMul(SkColorGetB(info.fColors[i]), scale); 1370b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 1380b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 1390b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // Clamp the initial color. 1400b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->append("dup 0 le {pop "); 1410b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->appendScalar(colorData[0][0]); 1420b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->append(" "); 1430b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->appendScalar(colorData[0][1]); 1440b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->append(" "); 1450b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->appendScalar(colorData[0][2]); 1460b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->append(" }\n"); 1470b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 1480b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // The gradient colors. 1490b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger for (int i = 1 ; i < info.fColorCount; i++) { 1500b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->append("{dup "); 1510b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->appendScalar(info.fColorOffsets[i]); 1520b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->append(" le {"); 1530b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (info.fColorOffsets[i - 1] != 0) { 1540b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->appendScalar(info.fColorOffsets[i - 1]); 1550b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->append(" sub\n"); 1560b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 1570b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 1580b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger interpolateColorCode(info.fColorOffsets[i] - info.fColorOffsets[i - 1], 1590b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger colorData[i], colorData[i - 1], kColorComponents, 1600b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result); 1610b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->append("}\n"); 1620b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 1630b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 1640b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // Clamp the final color. 1650b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->append("{pop "); 1660b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->appendScalar(colorData[info.fColorCount - 1][0]); 1670b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->append(" "); 1680b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->appendScalar(colorData[info.fColorCount - 1][1]); 1690b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->append(" "); 1700b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->appendScalar(colorData[info.fColorCount - 1][2]); 1710b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 1720b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger for (int i = 0 ; i < info.fColorCount; i++) { 1730b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->append("} ifelse\n"); 1740b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 1750b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger} 1760b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 1770b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger/* Map a value of t on the stack into [0, 1) for Repeat or Mirror tile mode. */ 1780b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenbergerstatic void tileModeCode(SkShader::TileMode mode, SkString* result) { 1790b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (mode == SkShader::kRepeat_TileMode) { 1800b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->append("dup truncate sub\n"); // Get the fractional part. 1810b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->append("dup 0 le {1 add} if\n"); // Map (-1,0) => (0,1) 1820b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger return; 1830b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 1840b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 1850b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (mode == SkShader::kMirror_TileMode) { 1860b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // Map t mod 2 into [0, 1, 1, 0]. 1870b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // Code Stack 1880b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->append("abs " // Map negative to positive. 1890b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger "dup " // t.s t.s 1900b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger "truncate " // t.s t 1910b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger "dup " // t.s t t 1920b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger "cvi " // t.s t T 1930b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger "2 mod " // t.s t (i mod 2) 1940b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger "1 eq " // t.s t true|false 1950b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger "3 1 roll " // true|false t.s t 1960b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger "sub " // true|false 0.s 1970b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger "exch " // 0.s true|false 1980b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger "{1 exch sub} if\n"); // 1 - 0.s|0.s 1990b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 2000b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger} 2010b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 2020b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenbergerstatic SkString linearCode(const SkShader::GradientInfo& info) { 2031cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkString function("{pop\n"); // Just ditch the y value. 2040b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger tileModeCode(info.fTileMode, &function); 2050b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger gradientFunctionCode(info, &function); 2060b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger function.append("}"); 2070b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger return function; 2080b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger} 2090b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 2100b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenbergerstatic SkString radialCode(const SkShader::GradientInfo& info) { 2110b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkString function("{"); 2120b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // Find the distance from the origin. 2130b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger function.append("dup " // x y y 2140b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger "mul " // x y^2 2150b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger "exch " // y^2 x 2160b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger "dup " // y^2 x x 2170b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger "mul " // y^2 x^2 2180b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger "add " // y^2+x^2 2190b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger "sqrt\n"); // sqrt(y^2+x^2) 2200b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 2210b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger tileModeCode(info.fTileMode, &function); 2220b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger gradientFunctionCode(info, &function); 2230b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger function.append("}"); 2240b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger return function; 2250b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger} 2260b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 2270b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger/* The math here is all based on the description in Two_Point_Radial_Gradient, 2280b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger with one simplification, the coordinate space has been scaled so that 2290b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger Dr = 1. This means we don't need to scale the entire equation by 1/Dr^2. 2300b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger */ 2310b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenbergerstatic SkString twoPointRadialCode(const SkShader::GradientInfo& info) { 2320b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkScalar dx = info.fPoint[0].fX - info.fPoint[1].fX; 2330b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkScalar dy = info.fPoint[0].fY - info.fPoint[1].fY; 2340b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkScalar sr = info.fRadius[0]; 2350b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkScalar a = SkScalarMul(dx, dx) + SkScalarMul(dy, dy) - SK_Scalar1; 2360b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger bool posRoot = info.fRadius[1] > info.fRadius[0]; 2370b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 2380b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // We start with a stack of (x y), copy it and then consume one copy in 2390b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // order to calculate b and the other to calculate c. 2400b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkString function("{"); 2410b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger function.append("2 copy "); 2420b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 2430b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // Calculate -b and b^2. 2440b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger function.appendScalar(dy); 2450b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger function.append(" mul exch "); 2460b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger function.appendScalar(dx); 2470b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger function.append(" mul add "); 2480b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger function.appendScalar(sr); 2490b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger function.append(" sub 2 mul neg dup dup mul\n"); 2500b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 2510b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // Calculate c 2520b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger function.append("4 2 roll dup mul exch dup mul add "); 2530b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger function.appendScalar(SkScalarMul(sr, sr)); 2540b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger function.append(" sub\n"); 2550b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 2560b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // Calculate the determinate 2570b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger function.appendScalar(SkScalarMul(SkIntToScalar(4), a)); 2580b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger function.append(" mul sub abs sqrt\n"); 2590b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 2600b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // And then the final value of t. 2610b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (posRoot) { 2620b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger function.append("sub "); 2630b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } else { 2640b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger function.append("add "); 2650b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 2660b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger function.appendScalar(SkScalarMul(SkIntToScalar(2), a)); 2670b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger function.append(" div\n"); 2680b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 2690b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger tileModeCode(info.fTileMode, &function); 2700b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger gradientFunctionCode(info, &function); 2710b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger function.append("}"); 2720b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger return function; 2730b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger} 2740b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 2750b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenbergerstatic SkString sweepCode(const SkShader::GradientInfo& info) { 2760b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkString function("{exch atan 360 div\n"); 2770b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger tileModeCode(info.fTileMode, &function); 2780b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger gradientFunctionCode(info, &function); 2790b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger function.append("}"); 2800b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger return function; 2810b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger} 2820b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 2831cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergerclass SkPDFShader::State { 2841cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergerpublic: 2851cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkShader::GradientType fType; 2861cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkShader::GradientInfo fInfo; 2871cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkAutoFree fColorData; 2881cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkMatrix fCanvasTransform; 2891cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkMatrix fShaderTransform; 2901cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkIRect fBBox; 2911cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 2921cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkBitmap fImage; 2931cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger uint32_t fPixelGeneration; 2941cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkShader::TileMode fImageTileModes[2]; 2951cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 2961cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger explicit State(const SkShader& shader, const SkMatrix& canvasTransform, 2971cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger const SkIRect& bbox); 2981cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger bool operator==(const State& b) const; 2991cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger}; 3001cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 3011cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergerclass SkPDFFunctionShader : public SkPDFDict, public SkPDFShader { 3021cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergerpublic: 3031cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger explicit SkPDFFunctionShader(SkPDFShader::State* state); 3041cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger ~SkPDFFunctionShader() { 3051cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger if (isValid()) { 3061cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger RemoveShader(this); 3071cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 3081cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger fResources.unrefAll(); 3091cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 3100b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 3111cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger bool isValid() { return fResources.count() > 0; } 3120b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 3131cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger void getResources(SkTDArray<SkPDFObject*>* resourceList) { 3141cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GetResourcesHelper(&fResources, resourceList); 3151cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 3160b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 3171cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergerprivate: 3181cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger static SkPDFObject* RangeObject(); 3190b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 3201cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkTDArray<SkPDFObject*> fResources; 3211cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkAutoTDelete<const SkPDFShader::State> fState; 3221cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 3231cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkPDFStream* makePSFunction(const SkString& psCode, SkPDFArray* domain); 3241cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger}; 3250b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 3261cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergerclass SkPDFImageShader : public SkPDFStream, public SkPDFShader { 3271cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergerpublic: 3281cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger explicit SkPDFImageShader(SkPDFShader::State* state); 3291cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger ~SkPDFImageShader() { 3301cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger RemoveShader(this); 3311cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger fResources.unrefAll(); 3320b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 3331cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 3341cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger void getResources(SkTDArray<SkPDFObject*>* resourceList) { 3351cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger GetResourcesHelper(&fResources, resourceList); 3361cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 3371cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 3381cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergerprivate: 3391cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkTDArray<SkPDFObject*> fResources; 3401cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkAutoTDelete<const SkPDFShader::State> fState; 3411cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger}; 3421cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 3431cab2921ab279367f8206cdadc9259d12e603548Derek SollenbergerSkPDFShader::SkPDFShader() {} 3441cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 3451cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger// static 3461cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergervoid SkPDFShader::RemoveShader(SkPDFObject* shader) { 3471cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkAutoMutexAcquire lock(CanonicalShadersMutex()); 3481cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger ShaderCanonicalEntry entry(shader, NULL); 3491cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger int index = CanonicalShaders().find(entry); 3501cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkASSERT(index >= 0); 3511cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger CanonicalShaders().removeShuffle(index); 3520b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger} 3530b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 3540b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger// static 3551cab2921ab279367f8206cdadc9259d12e603548Derek SollenbergerSkPDFObject* SkPDFShader::GetPDFShader(const SkShader& shader, 3560b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger const SkMatrix& matrix, 3570b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger const SkIRect& surfaceBBox) { 3581cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkPDFObject* result; 3591cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkAutoMutexAcquire lock(CanonicalShadersMutex()); 3600b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkAutoTDelete<State> shaderState(new State(shader, matrix, surfaceBBox)); 3610b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 3620b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger ShaderCanonicalEntry entry(NULL, shaderState.get()); 3631cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger int index = CanonicalShaders().find(entry); 3640b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (index >= 0) { 3651cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger result = CanonicalShaders()[index].fPDFShader; 3660b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->ref(); 3670b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger return result; 3680b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 3690b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // The PDFShader takes ownership of the shaderSate. 3701cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger if (shaderState.get()->fType == SkShader::kNone_GradientType) { 3711cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger result = new SkPDFImageShader(shaderState.detach()); 3721cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } else { 3731cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkPDFFunctionShader* functionShader = 3741cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger new SkPDFFunctionShader(shaderState.detach()); 3751cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger if (!functionShader->isValid()) { 3761cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger delete functionShader; 3771cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger return NULL; 3781cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger } 3791cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger result = functionShader; 3800b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 3811cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger entry.fPDFShader = result; 3821cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger CanonicalShaders().push(entry); 3831cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger return result; // return the reference that came from new. 3840b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger} 3850b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 3860b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger// static 3871cab2921ab279367f8206cdadc9259d12e603548Derek SollenbergerSkTDArray<SkPDFShader::ShaderCanonicalEntry>& SkPDFShader::CanonicalShaders() { 3880b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // This initialization is only thread safe with gcc. 3890b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger static SkTDArray<ShaderCanonicalEntry> gCanonicalShaders; 3900b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger return gCanonicalShaders; 3910b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger} 3920b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 3930b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger// static 3944f1dae40e24d57d647db01443b8bf2410514b8b5Derek SollenbergerSkBaseMutex& SkPDFShader::CanonicalShadersMutex() { 3954f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger // This initialization is only thread safe with gcc or when 3964f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger // POD-style mutex initialization is used. 3974f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger SK_DECLARE_STATIC_MUTEX(gCanonicalShadersMutex); 3980b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger return gCanonicalShadersMutex; 3990b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger} 4000b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 4010b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger// static 4021cab2921ab279367f8206cdadc9259d12e603548Derek SollenbergerSkPDFObject* SkPDFFunctionShader::RangeObject() { 4030b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // This initialization is only thread safe with gcc. 4040b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger static SkPDFArray* range = NULL; 4051cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // This method is only used with CanonicalShadersMutex, so it's safe to 4060b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // populate domain. 4070b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (range == NULL) { 4080b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger range = new SkPDFArray; 4090b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger range->reserve(6); 4101cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger range->appendInt(0); 4111cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger range->appendInt(1); 4121cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger range->appendInt(0); 4131cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger range->appendInt(1); 4141cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger range->appendInt(0); 4151cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger range->appendInt(1); 4160b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 4170b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger return range; 4180b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger} 4190b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 4201cab2921ab279367f8206cdadc9259d12e603548Derek SollenbergerSkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state) 4211cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger : SkPDFDict("Pattern"), 4221cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger fState(state) { 4230b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkString (*codeFunction)(const SkShader::GradientInfo& info) = NULL; 4240b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkPoint transformPoints[2]; 4250b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 4260b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // Depending on the type of the gradient, we want to transform the 4270b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // coordinate space in different ways. 4280b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger const SkShader::GradientInfo* info = &fState.get()->fInfo; 4290b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger transformPoints[0] = info->fPoint[0]; 4300b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger transformPoints[1] = info->fPoint[1]; 4310b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger switch (fState.get()->fType) { 4320b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger case SkShader::kLinear_GradientType: 4330b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger codeFunction = &linearCode; 4340b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger break; 4350b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger case SkShader::kRadial_GradientType: 4360b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger transformPoints[1] = transformPoints[0]; 4370b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger transformPoints[1].fX += info->fRadius[0]; 4380b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger codeFunction = &radialCode; 4390b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger break; 4400b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger case SkShader::kRadial2_GradientType: { 4411cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // Bail out if the radii are the same. Empty fResources signals 4421cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger // an error and isValid will return false. 4430b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (info->fRadius[0] == info->fRadius[1]) { 4440b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger return; 4450b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 4460b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger transformPoints[1] = transformPoints[0]; 4470b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkScalar dr = info->fRadius[1] - info->fRadius[0]; 4480b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger transformPoints[1].fX += dr; 4490b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger codeFunction = &twoPointRadialCode; 4500b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger break; 4510b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 4520b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger case SkShader::kSweep_GradientType: 4530b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger transformPoints[1] = transformPoints[0]; 4540b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger transformPoints[1].fX += 1; 4550b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger codeFunction = &sweepCode; 4560b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger break; 4570b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger case SkShader::kColor_GradientType: 4580b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger case SkShader::kNone_GradientType: 4591cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger default: 4600b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger return; 4610b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 4620b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 4630b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // Move any scaling (assuming a unit gradient) or translation 4640b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // (and rotation for linear gradient), of the final gradient from 4650b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // info->fPoints to the matrix (updating bbox appropriately). Now 4660b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // the gradient can be drawn on on the unit segment. 4670b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkMatrix mapperMatrix; 4680b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger unitToPointsMatrix(transformPoints, &mapperMatrix); 4690b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkMatrix finalMatrix = fState.get()->fCanvasTransform; 4700b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger finalMatrix.preConcat(mapperMatrix); 4710b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger finalMatrix.preConcat(fState.get()->fShaderTransform); 4720b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkRect bbox; 4730b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger bbox.set(fState.get()->fBBox); 4740b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger transformBBox(finalMatrix, &bbox); 4750b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 4760b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkRefPtr<SkPDFArray> domain = new SkPDFArray; 4770b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger domain->unref(); // SkRefPtr and new both took a reference. 4780b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger domain->reserve(4); 4791cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger domain->appendScalar(bbox.fLeft); 4801cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger domain->appendScalar(bbox.fRight); 4811cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger domain->appendScalar(bbox.fTop); 4821cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger domain->appendScalar(bbox.fBottom); 4830b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 4840b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkString functionCode; 4850b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // The two point radial gradient further references fState.get()->fInfo 4860b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // in translating from x, y coordinates to the t parameter. So, we have 4870b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // to transform the points and radii according to the calculated matrix. 4880b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (fState.get()->fType == SkShader::kRadial2_GradientType) { 4890b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkShader::GradientInfo twoPointRadialInfo = *info; 4900b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkMatrix inverseMapperMatrix; 4910b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger mapperMatrix.invert(&inverseMapperMatrix); 4920b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger inverseMapperMatrix.mapPoints(twoPointRadialInfo.fPoint, 2); 4930b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger twoPointRadialInfo.fRadius[0] = 4940b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger inverseMapperMatrix.mapRadius(info->fRadius[0]); 4950b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger twoPointRadialInfo.fRadius[1] = 4960b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger inverseMapperMatrix.mapRadius(info->fRadius[1]); 4970b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger functionCode = codeFunction(twoPointRadialInfo); 4980b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } else { 4990b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger functionCode = codeFunction(*info); 5000b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 5010b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 5020b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkRefPtr<SkPDFStream> function = makePSFunction(functionCode, domain.get()); 5030b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // Pass one reference to fResources, SkRefPtr and new both took a reference. 5040b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger fResources.push(function.get()); 5050b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 5060b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkRefPtr<SkPDFDict> pdfShader = new SkPDFDict; 5070b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger pdfShader->unref(); // SkRefPtr and new both took a reference. 5081cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger pdfShader->insertInt("ShadingType", 1); 5091cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger pdfShader->insertName("ColorSpace", "DeviceRGB"); 5100b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger pdfShader->insert("Domain", domain.get()); 5110b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger pdfShader->insert("Function", new SkPDFObjRef(function.get()))->unref(); 5120b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 5131cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger insertInt("PatternType", 2); 5141cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger insert("Matrix", SkPDFUtils::MatrixToArray(finalMatrix))->unref(); 5151cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger insert("Shading", pdfShader.get()); 5160b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger} 5170b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 5181cab2921ab279367f8206cdadc9259d12e603548Derek SollenbergerSkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) : fState(state) { 5190b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger fState.get()->fImage.lockPixels(); 5200b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 5210b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkMatrix finalMatrix = fState.get()->fCanvasTransform; 5220b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger finalMatrix.preConcat(fState.get()->fShaderTransform); 5230b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkRect surfaceBBox; 5240b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger surfaceBBox.set(fState.get()->fBBox); 5250b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger transformBBox(finalMatrix, &surfaceBBox); 5260b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 5270b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkMatrix unflip; 5280b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger unflip.setTranslate(0, SkScalarRound(surfaceBBox.height())); 5291cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger unflip.preScale(SK_Scalar1, -SK_Scalar1); 5300b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkISize size = SkISize::Make(SkScalarRound(surfaceBBox.width()), 5310b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkScalarRound(surfaceBBox.height())); 5320b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkPDFDevice pattern(size, size, unflip); 5330b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkCanvas canvas(&pattern); 5340b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger canvas.translate(-surfaceBBox.fLeft, -surfaceBBox.fTop); 5350b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger finalMatrix.preTranslate(surfaceBBox.fLeft, surfaceBBox.fTop); 5360b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 5370b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger const SkBitmap* image = &fState.get()->fImage; 5380b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger int width = image->width(); 5390b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger int height = image->height(); 5400b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkShader::TileMode tileModes[2]; 5410b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger tileModes[0] = fState.get()->fImageTileModes[0]; 5420b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger tileModes[1] = fState.get()->fImageTileModes[1]; 5430b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 5440b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger canvas.drawBitmap(*image, 0, 0); 5450b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkRect patternBBox = SkRect::MakeXYWH(-surfaceBBox.fLeft, -surfaceBBox.fTop, 5460b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger width, height); 5470b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 5480b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // Tiling is implied. First we handle mirroring. 5490b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (tileModes[0] == SkShader::kMirror_TileMode) { 5500b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkMatrix xMirror; 5510b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger xMirror.setScale(-1, 1); 5520b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger xMirror.postTranslate(2 * width, 0); 5530b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger canvas.drawBitmapMatrix(*image, xMirror); 5540b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger patternBBox.fRight += width; 5550b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 5560b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (tileModes[1] == SkShader::kMirror_TileMode) { 5570b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkMatrix yMirror; 5581cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger yMirror.setScale(SK_Scalar1, -SK_Scalar1); 5590b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger yMirror.postTranslate(0, 2 * height); 5600b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger canvas.drawBitmapMatrix(*image, yMirror); 5610b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger patternBBox.fBottom += height; 5620b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 5630b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (tileModes[0] == SkShader::kMirror_TileMode && 5640b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger tileModes[1] == SkShader::kMirror_TileMode) { 5650b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkMatrix mirror; 5660b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger mirror.setScale(-1, -1); 5670b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger mirror.postTranslate(2 * width, 2 * height); 5680b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger canvas.drawBitmapMatrix(*image, mirror); 5690b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 5700b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 5710b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // Then handle Clamping, which requires expanding the pattern canvas to 5720b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // cover the entire surfaceBBox. 5730b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 5740b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // If both x and y are in clamp mode, we start by filling in the corners. 5750b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // (Which are just a rectangles of the corner colors.) 5760b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (tileModes[0] == SkShader::kClamp_TileMode && 5770b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger tileModes[1] == SkShader::kClamp_TileMode) { 5780b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkPaint paint; 5790b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkRect rect; 5800b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger rect = SkRect::MakeLTRB(surfaceBBox.fLeft, surfaceBBox.fTop, 0, 0); 5810b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (!rect.isEmpty()) { 5820b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger paint.setColor(image->getColor(0, 0)); 5830b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger canvas.drawRect(rect, paint); 5840b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 5850b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 5860b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger rect = SkRect::MakeLTRB(width, surfaceBBox.fTop, surfaceBBox.fRight, 0); 5870b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (!rect.isEmpty()) { 5880b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger paint.setColor(image->getColor(width - 1, 0)); 5890b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger canvas.drawRect(rect, paint); 5900b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 5910b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 5920b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger rect = SkRect::MakeLTRB(width, height, surfaceBBox.fRight, 5930b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger surfaceBBox.fBottom); 5940b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (!rect.isEmpty()) { 5950b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger paint.setColor(image->getColor(width - 1, height - 1)); 5960b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger canvas.drawRect(rect, paint); 5970b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 5980b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 5990b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger rect = SkRect::MakeLTRB(surfaceBBox.fLeft, height, 0, 6000b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger surfaceBBox.fBottom); 6010b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (!rect.isEmpty()) { 6020b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger paint.setColor(image->getColor(0, height - 1)); 6030b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger canvas.drawRect(rect, paint); 6040b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 6050b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 6060b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 6070b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // Then expand the left, right, top, then bottom. 6080b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (tileModes[0] == SkShader::kClamp_TileMode) { 6090b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, height); 6100b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (surfaceBBox.fLeft < 0) { 6110b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkBitmap left; 6120b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkAssertResult(image->extractSubset(&left, subset)); 6130b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 6140b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkMatrix leftMatrix; 6150b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger leftMatrix.setScale(-surfaceBBox.fLeft, 1); 6160b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger leftMatrix.postTranslate(surfaceBBox.fLeft, 0); 6170b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger canvas.drawBitmapMatrix(left, leftMatrix); 6180b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 6190b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (tileModes[1] == SkShader::kMirror_TileMode) { 6201cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger leftMatrix.postScale(SK_Scalar1, -SK_Scalar1); 6210b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger leftMatrix.postTranslate(0, 2 * height); 6220b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger canvas.drawBitmapMatrix(left, leftMatrix); 6230b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 6240b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger patternBBox.fLeft = 0; 6250b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 6260b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 6270b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (surfaceBBox.fRight > width) { 6280b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkBitmap right; 6290b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger subset.offset(width - 1, 0); 6300b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkAssertResult(image->extractSubset(&right, subset)); 6310b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 6320b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkMatrix rightMatrix; 6330b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger rightMatrix.setScale(surfaceBBox.fRight - width, 1); 6340b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger rightMatrix.postTranslate(width, 0); 6350b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger canvas.drawBitmapMatrix(right, rightMatrix); 6360b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 6370b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (tileModes[1] == SkShader::kMirror_TileMode) { 6381cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger rightMatrix.postScale(SK_Scalar1, -SK_Scalar1); 6390b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger rightMatrix.postTranslate(0, 2 * height); 6400b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger canvas.drawBitmapMatrix(right, rightMatrix); 6410b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 6420b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger patternBBox.fRight = surfaceBBox.width(); 6430b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 6440b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 6450b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 6460b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (tileModes[1] == SkShader::kClamp_TileMode) { 6470b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkIRect subset = SkIRect::MakeXYWH(0, 0, width, 1); 6480b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (surfaceBBox.fTop < 0) { 6490b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkBitmap top; 6500b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkAssertResult(image->extractSubset(&top, subset)); 6510b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 6520b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkMatrix topMatrix; 6531cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger topMatrix.setScale(SK_Scalar1, -surfaceBBox.fTop); 6540b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger topMatrix.postTranslate(0, surfaceBBox.fTop); 6550b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger canvas.drawBitmapMatrix(top, topMatrix); 6560b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 6570b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (tileModes[0] == SkShader::kMirror_TileMode) { 6580b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger topMatrix.postScale(-1, 1); 6590b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger topMatrix.postTranslate(2 * width, 0); 6600b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger canvas.drawBitmapMatrix(top, topMatrix); 6610b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 6620b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger patternBBox.fTop = 0; 6630b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 6640b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 6650b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (surfaceBBox.fBottom > height) { 6660b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkBitmap bottom; 6670b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger subset.offset(0, height - 1); 6680b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkAssertResult(image->extractSubset(&bottom, subset)); 6690b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 6700b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkMatrix bottomMatrix; 6711cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger bottomMatrix.setScale(SK_Scalar1, surfaceBBox.fBottom - height); 6720b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger bottomMatrix.postTranslate(0, height); 6730b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger canvas.drawBitmapMatrix(bottom, bottomMatrix); 6740b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 6750b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (tileModes[0] == SkShader::kMirror_TileMode) { 6760b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger bottomMatrix.postScale(-1, 1); 6770b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger bottomMatrix.postTranslate(2 * width, 0); 6780b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger canvas.drawBitmapMatrix(bottom, bottomMatrix); 6790b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 6800b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger patternBBox.fBottom = surfaceBBox.height(); 6810b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 6820b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 6830b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 6840b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkRefPtr<SkPDFArray> patternBBoxArray = new SkPDFArray; 6850b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger patternBBoxArray->unref(); // SkRefPtr and new both took a reference. 6860b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger patternBBoxArray->reserve(4); 6871cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger patternBBoxArray->appendScalar(patternBBox.fLeft); 6881cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger patternBBoxArray->appendScalar(patternBBox.fTop); 6891cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger patternBBoxArray->appendScalar(patternBBox.fRight); 6901cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger patternBBoxArray->appendScalar(patternBBox.fBottom); 6910b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 6920b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger // Put the canvas into the pattern stream (fContent). 6930b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkRefPtr<SkStream> content = pattern.content(); 6940b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger content->unref(); // SkRefPtr and content() both took a reference. 6950b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger pattern.getResources(&fResources); 6960b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 6971cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger setData(content.get()); 6981cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger insertName("Type", "Pattern"); 6991cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger insertInt("PatternType", 1); 7001cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger insertInt("PaintType", 1); 7011cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger insertInt("TilingType", 1); 7021cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger insert("BBox", patternBBoxArray.get()); 7031cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger insertScalar("XStep", patternBBox.width()); 7041cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger insertScalar("YStep", patternBBox.height()); 7051cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger insert("Resources", pattern.getResourceDict()); 7061cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger insert("Matrix", SkPDFUtils::MatrixToArray(finalMatrix))->unref(); 7070b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 7080b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger fState.get()->fImage.unlockPixels(); 7090b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger} 7100b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 7111cab2921ab279367f8206cdadc9259d12e603548Derek SollenbergerSkPDFStream* SkPDFFunctionShader::makePSFunction(const SkString& psCode, 7121cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkPDFArray* domain) { 7131cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkAutoDataUnref funcData(SkData::NewWithCopy(psCode.c_str(), 7141cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger psCode.size())); 7151cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger SkPDFStream* result = new SkPDFStream(funcData.get()); 7161cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger result->insertInt("FunctionType", 4); 7170b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger result->insert("Domain", domain); 7181cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger result->insert("Range", RangeObject()); 7190b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger return result; 7200b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger} 7210b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 7221cab2921ab279367f8206cdadc9259d12e603548Derek SollenbergerSkPDFShader::ShaderCanonicalEntry::ShaderCanonicalEntry(SkPDFObject* pdfShader, 7231cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger const State* state) 7241cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger : fPDFShader(pdfShader), 7251cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger fState(state) { 7261cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger} 7271cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 7281cab2921ab279367f8206cdadc9259d12e603548Derek Sollenbergerbool SkPDFShader::ShaderCanonicalEntry::operator==( 7291cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger const ShaderCanonicalEntry& b) const { 7301cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger return fPDFShader == b.fPDFShader || 7311cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger (fState != NULL && b.fState != NULL && *fState == *b.fState); 7321cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger} 7331cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger 7340b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenbergerbool SkPDFShader::State::operator==(const SkPDFShader::State& b) const { 7350b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (fType != b.fType || 7360b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger fCanvasTransform != b.fCanvasTransform || 7370b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger fShaderTransform != b.fShaderTransform || 7380b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger fBBox != b.fBBox) { 7390b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger return false; 7400b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 7410b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 7420b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (fType == SkShader::kNone_GradientType) { 7430b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (fPixelGeneration != b.fPixelGeneration || 7440b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger fPixelGeneration == 0 || 7450b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger fImageTileModes[0] != b.fImageTileModes[0] || 7460b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger fImageTileModes[1] != b.fImageTileModes[1]) { 7470b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger return false; 7480b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 7490b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } else { 7500b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (fInfo.fColorCount != b.fInfo.fColorCount || 7510b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger memcmp(fInfo.fColors, b.fInfo.fColors, 7520b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger sizeof(SkColor) * fInfo.fColorCount) != 0 || 7530b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger memcmp(fInfo.fColorOffsets, b.fInfo.fColorOffsets, 7540b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger sizeof(SkScalar) * fInfo.fColorCount) != 0 || 7550b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger fInfo.fPoint[0] != b.fInfo.fPoint[0] || 7560b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger fInfo.fTileMode != b.fInfo.fTileMode) { 7570b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger return false; 7580b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 7590b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 7600b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger switch (fType) { 7610b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger case SkShader::kLinear_GradientType: 7620b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (fInfo.fPoint[1] != b.fInfo.fPoint[1]) { 7630b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger return false; 7640b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 7650b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger break; 7660b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger case SkShader::kRadial_GradientType: 7670b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (fInfo.fRadius[0] != b.fInfo.fRadius[0]) { 7680b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger return false; 7690b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 7700b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger break; 7710b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger case SkShader::kRadial2_GradientType: 7720b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (fInfo.fPoint[1] != b.fInfo.fPoint[1] || 7730b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger fInfo.fRadius[0] != b.fInfo.fRadius[0] || 7740b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger fInfo.fRadius[1] != b.fInfo.fRadius[1]) { 7750b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger return false; 7760b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 7770b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger break; 7780b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger case SkShader::kSweep_GradientType: 7790b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger case SkShader::kNone_GradientType: 7800b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger case SkShader::kColor_GradientType: 7810b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger break; 7820b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 7830b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 7840b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger return true; 7850b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger} 7860b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 7870b15698a8c76bb8abc1b555c1d91892669b4118fDerek SollenbergerSkPDFShader::State::State(const SkShader& shader, 7880b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger const SkMatrix& canvasTransform, const SkIRect& bbox) 7890b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger : fCanvasTransform(canvasTransform), 7901cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger fBBox(bbox), 7911cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger fPixelGeneration(0) { 7920b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger fInfo.fColorCount = 0; 7930b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger fInfo.fColors = NULL; 7940b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger fInfo.fColorOffsets = NULL; 7950b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger shader.getLocalMatrix(&fShaderTransform); 7961cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger fImageTileModes[0] = fImageTileModes[1] = SkShader::kClamp_TileMode; 7970b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 7980b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger fType = shader.asAGradient(&fInfo); 7990b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger 8000b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (fType == SkShader::kNone_GradientType) { 8010b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkShader::BitmapType bitmapType; 8020b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkMatrix matrix; 8030b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger bitmapType = shader.asABitmap(&fImage, &matrix, fImageTileModes, NULL); 8040b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger if (bitmapType != SkShader::kDefault_BitmapType) { 8050b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger fImage.reset(); 8060b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger return; 8070b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 8080b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger SkASSERT(matrix.isIdentity()); 8090b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger fPixelGeneration = fImage.getGenerationID(); 8100b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } else { 8110b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger fColorData.set(sk_malloc_throw( 8120b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger fInfo.fColorCount * (sizeof(SkColor) + sizeof(SkScalar)))); 8131cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger fInfo.fColors = reinterpret_cast<SkColor*>(fColorData.get()); 8141cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger fInfo.fColorOffsets = 8151cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger reinterpret_cast<SkScalar*>(fInfo.fColors + fInfo.fColorCount); 8160b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger shader.asAGradient(&fInfo); 8170b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger } 8180b15698a8c76bb8abc1b555c1d91892669b4118fDerek Sollenberger} 819