OLD | NEW |
1 | 1 |
2 /* | 2 /* |
3 * Copyright 2011 Google Inc. | 3 * Copyright 2011 Google Inc. |
4 * | 4 * |
5 * Use of this source code is governed by a BSD-style license that can be | 5 * Use of this source code is governed by a BSD-style license that can be |
6 * found in the LICENSE file. | 6 * found in the LICENSE file. |
7 */ | 7 */ |
8 | 8 |
9 | 9 |
10 #include "SkPDFShader.h" | 10 #include "SkPDFShader.h" |
(...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
203 "cvi " // t.s t T | 203 "cvi " // t.s t T |
204 "2 mod " // t.s t (i mod 2) | 204 "2 mod " // t.s t (i mod 2) |
205 "1 eq " // t.s t true|false | 205 "1 eq " // t.s t true|false |
206 "3 1 roll " // true|false t.s t | 206 "3 1 roll " // true|false t.s t |
207 "sub " // true|false 0.s | 207 "sub " // true|false 0.s |
208 "exch " // 0.s true|false | 208 "exch " // 0.s true|false |
209 "{1 exch sub} if\n"); // 1 - 0.s|0.s | 209 "{1 exch sub} if\n"); // 1 - 0.s|0.s |
210 } | 210 } |
211 } | 211 } |
212 | 212 |
213 /** | 213 static SkString linearCode(const SkShader::GradientInfo& info) { |
214 * Returns PS function code that would apply perspective to a x, y point. | 214 SkString function("{pop\n"); // Just ditch the y value. |
215 * The function assumes that the stack has at least two elements, | |
216 * and that the top 2 elements are numeric values. | |
217 * After ececuting this code on a PS stack, the last 2 elements are updated | |
218 * while the rest of the stack is preserved intact. | |
219 * xy2xy is the inverse perspective matrix. | |
220 */ | |
221 static SkString apply_perspective_to_coordinates( | |
222 const SkMatrix& inversePerspectiveMatrix) { | |
223 SkString code; | |
224 if (!inversePerspectiveMatrix.hasPerspective()) { | |
225 return code; | |
226 } | |
227 | |
228 // Perspective matrix should be: | |
229 // 1 0 0 | |
230 // 0 1 0 | |
231 // p0 p1 p2 | |
232 | |
233 SkScalar p0 = inversePerspectiveMatrix[SkMatrix::kMPersp0]; | |
234 SkScalar p1 = inversePerspectiveMatrix[SkMatrix::kMPersp1]; | |
235 SkScalar p2 = inversePerspectiveMatrix[SkMatrix::kMPersp2]; | |
236 | |
237 // y = y / (p2 + p0 x + p1 y) | |
238 // x = x / (p2 + p0 x + p1 y) | |
239 | |
240 // Input on stack: x y | |
241 code.append("dup "); // x y y | |
242 code.appendScalar(p1); // x y y p1 | |
243 code.append(" mul " // x y y*p1 | |
244 " 2 index "); // x y y*p1 x | |
245 code.appendScalar(p0); // x y y p1 x p0 | |
246 code.append(" mul "); // x y y*p1 x*p0 | |
247 code.appendScalar(p2); // x y y p1 x*p0 p2 | |
248 code.append("add " // x y y*p1 x*p0+p2 | |
249 "add " // x y y*p1+x*p0+p2 | |
250 "1 index " // x y y*p1+x*p0+p2 y | |
251 "1 index " // x y y*p1+x*p0+p2 y y*p1+x*p0+p2 | |
252 "div " // x y y*p1+x*p0+p2 y/(y*p1+x*p0+p2) | |
253 "4 1 roll " // y/(y*p1+x*p0+p2) x y y*p1+x*p0+p2 | |
254 "exch " // y/(y*p1+x*p0+p2) x y*p1+x*p0+p2 y | |
255 "pop " // y/(y*p1+x*p0+p2) x y*p1+x*p0+p2 | |
256 "div " // y/(y*p1+x*p0+p2) x/(y*p1+x*p0+p2) | |
257 "exch\n"); // x/(y*p1+x*p0+p2) y/(y*p1+x*p0+p2) | |
258 | |
259 return code; | |
260 } | |
261 | |
262 static SkString linearCode(const SkShader::GradientInfo& info, | |
263 const SkMatrix& perspectiveRemover) { | |
264 SkString function("{"); | |
265 | |
266 function.append(apply_perspective_to_coordinates(perspectiveRemover)); | |
267 | |
268 function.append("pop\n"); // Just ditch the y value. | |
269 tileModeCode(info.fTileMode, &function); | 215 tileModeCode(info.fTileMode, &function); |
270 gradientFunctionCode(info, &function); | 216 gradientFunctionCode(info, &function); |
271 function.append("}"); | 217 function.append("}"); |
272 return function; | 218 return function; |
273 } | 219 } |
274 | 220 |
275 static SkString radialCode(const SkShader::GradientInfo& info, | 221 static SkString radialCode(const SkShader::GradientInfo& info) { |
276 const SkMatrix& perspectiveRemover) { | |
277 SkString function("{"); | 222 SkString function("{"); |
278 | |
279 function.append(apply_perspective_to_coordinates(perspectiveRemover)); | |
280 | |
281 // Find the distance from the origin. | 223 // Find the distance from the origin. |
282 function.append("dup " // x y y | 224 function.append("dup " // x y y |
283 "mul " // x y^2 | 225 "mul " // x y^2 |
284 "exch " // y^2 x | 226 "exch " // y^2 x |
285 "dup " // y^2 x x | 227 "dup " // y^2 x x |
286 "mul " // y^2 x^2 | 228 "mul " // y^2 x^2 |
287 "add " // y^2+x^2 | 229 "add " // y^2+x^2 |
288 "sqrt\n"); // sqrt(y^2+x^2) | 230 "sqrt\n"); // sqrt(y^2+x^2) |
289 | 231 |
290 tileModeCode(info.fTileMode, &function); | 232 tileModeCode(info.fTileMode, &function); |
291 gradientFunctionCode(info, &function); | 233 gradientFunctionCode(info, &function); |
292 function.append("}"); | 234 function.append("}"); |
293 return function; | 235 return function; |
294 } | 236 } |
295 | 237 |
296 /* The math here is all based on the description in Two_Point_Radial_Gradient, | 238 /* The math here is all based on the description in Two_Point_Radial_Gradient, |
297 with one simplification, the coordinate space has been scaled so that | 239 with one simplification, the coordinate space has been scaled so that |
298 Dr = 1. This means we don't need to scale the entire equation by 1/Dr^2. | 240 Dr = 1. This means we don't need to scale the entire equation by 1/Dr^2. |
299 */ | 241 */ |
300 static SkString twoPointRadialCode(const SkShader::GradientInfo& info, | 242 static SkString twoPointRadialCode(const SkShader::GradientInfo& info) { |
301 const SkMatrix& perspectiveRemover) { | |
302 SkScalar dx = info.fPoint[0].fX - info.fPoint[1].fX; | 243 SkScalar dx = info.fPoint[0].fX - info.fPoint[1].fX; |
303 SkScalar dy = info.fPoint[0].fY - info.fPoint[1].fY; | 244 SkScalar dy = info.fPoint[0].fY - info.fPoint[1].fY; |
304 SkScalar sr = info.fRadius[0]; | 245 SkScalar sr = info.fRadius[0]; |
305 SkScalar a = SkScalarMul(dx, dx) + SkScalarMul(dy, dy) - SK_Scalar1; | 246 SkScalar a = SkScalarMul(dx, dx) + SkScalarMul(dy, dy) - SK_Scalar1; |
306 bool posRoot = info.fRadius[1] > info.fRadius[0]; | 247 bool posRoot = info.fRadius[1] > info.fRadius[0]; |
307 | 248 |
308 // We start with a stack of (x y), copy it and then consume one copy in | 249 // We start with a stack of (x y), copy it and then consume one copy in |
309 // order to calculate b and the other to calculate c. | 250 // order to calculate b and the other to calculate c. |
310 SkString function("{"); | 251 SkString function("{"); |
311 | |
312 function.append(apply_perspective_to_coordinates(perspectiveRemover)); | |
313 | |
314 function.append("2 copy "); | 252 function.append("2 copy "); |
315 | 253 |
316 // Calculate -b and b^2. | 254 // Calculate -b and b^2. |
317 function.appendScalar(dy); | 255 function.appendScalar(dy); |
318 function.append(" mul exch "); | 256 function.append(" mul exch "); |
319 function.appendScalar(dx); | 257 function.appendScalar(dx); |
320 function.append(" mul add "); | 258 function.append(" mul add "); |
321 function.appendScalar(sr); | 259 function.appendScalar(sr); |
322 function.append(" sub 2 mul neg dup dup mul\n"); | 260 function.append(" sub 2 mul neg dup dup mul\n"); |
323 | 261 |
(...skipping 17 matching lines...) Expand all Loading... |
341 | 279 |
342 tileModeCode(info.fTileMode, &function); | 280 tileModeCode(info.fTileMode, &function); |
343 gradientFunctionCode(info, &function); | 281 gradientFunctionCode(info, &function); |
344 function.append("}"); | 282 function.append("}"); |
345 return function; | 283 return function; |
346 } | 284 } |
347 | 285 |
348 /* Conical gradient shader, based on the Canvas spec for radial gradients | 286 /* Conical gradient shader, based on the Canvas spec for radial gradients |
349 See: http://www.w3.org/TR/2dcontext/#dom-context-2d-createradialgradient | 287 See: http://www.w3.org/TR/2dcontext/#dom-context-2d-createradialgradient |
350 */ | 288 */ |
351 static SkString twoPointConicalCode(const SkShader::GradientInfo& info, | 289 static SkString twoPointConicalCode(const SkShader::GradientInfo& info) { |
352 const SkMatrix& perspectiveRemover) { | |
353 SkScalar dx = info.fPoint[1].fX - info.fPoint[0].fX; | 290 SkScalar dx = info.fPoint[1].fX - info.fPoint[0].fX; |
354 SkScalar dy = info.fPoint[1].fY - info.fPoint[0].fY; | 291 SkScalar dy = info.fPoint[1].fY - info.fPoint[0].fY; |
355 SkScalar r0 = info.fRadius[0]; | 292 SkScalar r0 = info.fRadius[0]; |
356 SkScalar dr = info.fRadius[1] - info.fRadius[0]; | 293 SkScalar dr = info.fRadius[1] - info.fRadius[0]; |
357 SkScalar a = SkScalarMul(dx, dx) + SkScalarMul(dy, dy) - | 294 SkScalar a = SkScalarMul(dx, dx) + SkScalarMul(dy, dy) - |
358 SkScalarMul(dr, dr); | 295 SkScalarMul(dr, dr); |
359 | 296 |
360 // First compute t, if the pixel falls outside the cone, then we'll end | 297 // First compute t, if the pixel falls outside the cone, then we'll end |
361 // with 'false' on the stack, otherwise we'll push 'true' with t below it | 298 // with 'false' on the stack, otherwise we'll push 'true' with t below it |
362 | 299 |
363 // We start with a stack of (x y), copy it and then consume one copy in | 300 // We start with a stack of (x y), copy it and then consume one copy in |
364 // order to calculate b and the other to calculate c. | 301 // order to calculate b and the other to calculate c. |
365 SkString function("{"); | 302 SkString function("{"); |
366 | |
367 function.append(apply_perspective_to_coordinates(perspectiveRemover)); | |
368 | |
369 function.append("2 copy "); | 303 function.append("2 copy "); |
370 | 304 |
371 // Calculate b and b^2; b = -2 * (y * dy + x * dx + r0 * dr). | 305 // Calculate b and b^2; b = -2 * (y * dy + x * dx + r0 * dr). |
372 function.appendScalar(dy); | 306 function.appendScalar(dy); |
373 function.append(" mul exch "); | 307 function.append(" mul exch "); |
374 function.appendScalar(dx); | 308 function.appendScalar(dx); |
375 function.append(" mul add "); | 309 function.append(" mul add "); |
376 function.appendScalar(SkScalarMul(r0, dr)); | 310 function.appendScalar(SkScalarMul(r0, dr)); |
377 function.append(" add -2 mul dup dup mul\n"); | 311 function.append(" add -2 mul dup dup mul\n"); |
378 | 312 |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
454 function.append("{"); | 388 function.append("{"); |
455 tileModeCode(info.fTileMode, &function); | 389 tileModeCode(info.fTileMode, &function); |
456 gradientFunctionCode(info, &function); | 390 gradientFunctionCode(info, &function); |
457 | 391 |
458 // otherwise, just write black | 392 // otherwise, just write black |
459 function.append("} {0 0 0} ifelse }"); | 393 function.append("} {0 0 0} ifelse }"); |
460 | 394 |
461 return function; | 395 return function; |
462 } | 396 } |
463 | 397 |
464 static SkString sweepCode(const SkShader::GradientInfo& info, | 398 static SkString sweepCode(const SkShader::GradientInfo& info) { |
465 const SkMatrix& perspectiveRemover) { | |
466 SkString function("{exch atan 360 div\n"); | 399 SkString function("{exch atan 360 div\n"); |
467 tileModeCode(info.fTileMode, &function); | 400 tileModeCode(info.fTileMode, &function); |
468 gradientFunctionCode(info, &function); | 401 gradientFunctionCode(info, &function); |
469 function.append("}"); | 402 function.append("}"); |
470 return function; | 403 return function; |
471 } | 404 } |
472 | 405 |
473 class SkPDFShader::State { | 406 class SkPDFShader::State { |
474 public: | 407 public: |
475 SkShader::GradientType fType; | 408 SkShader::GradientType fType; |
(...skipping 309 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
785 get_gradient_resource_dict(fColorShader.get(), alphaGs.get())); | 718 get_gradient_resource_dict(fColorShader.get(), alphaGs.get())); |
786 | 719 |
787 SkAutoTUnref<SkStream> colorStream( | 720 SkAutoTUnref<SkStream> colorStream( |
788 create_pattern_fill_content(0, bbox)); | 721 create_pattern_fill_content(0, bbox)); |
789 setData(colorStream.get()); | 722 setData(colorStream.get()); |
790 | 723 |
791 populate_tiling_pattern_dict(this, bbox, fResourceDict.get(), | 724 populate_tiling_pattern_dict(this, bbox, fResourceDict.get(), |
792 SkMatrix::I()); | 725 SkMatrix::I()); |
793 } | 726 } |
794 | 727 |
795 // Finds affine and persp such that in = affine * persp. | |
796 // but it returns the inverse of perspective matrix. | |
797 static bool split_perspective(const SkMatrix in, SkMatrix* affine, | |
798 SkMatrix* perspectiveInverse) { | |
799 const SkScalar p2 = in[SkMatrix::kMPersp2]; | |
800 | |
801 if (SkScalarNearlyZero(p2)) { | |
802 return false; | |
803 } | |
804 | |
805 const SkScalar zero = SkIntToScalar(0); | |
806 const SkScalar one = SkIntToScalar(1); | |
807 | |
808 const SkScalar sx = in[SkMatrix::kMScaleX]; | |
809 const SkScalar kx = in[SkMatrix::kMSkewX]; | |
810 const SkScalar tx = in[SkMatrix::kMTransX]; | |
811 const SkScalar ky = in[SkMatrix::kMSkewY]; | |
812 const SkScalar sy = in[SkMatrix::kMScaleY]; | |
813 const SkScalar ty = in[SkMatrix::kMTransY]; | |
814 const SkScalar p0 = in[SkMatrix::kMPersp0]; | |
815 const SkScalar p1 = in[SkMatrix::kMPersp1]; | |
816 | |
817 // Perspective matrix would be: | |
818 // 1 0 0 | |
819 // 0 1 0 | |
820 // p0 p1 p2 | |
821 // But we need the inverse of persp. | |
822 perspectiveInverse->setAll(one, zero, zero, | |
823 zero, one, zero, | |
824 -p0 / p2, -p1/p2, 1/p2); | |
825 | |
826 affine->setAll(sx - p0 * tx / p2, kx - p1 * tx / p2, tx / p2, | |
827 ky - p0 * ty / p2, sy - p1 * ty / p2, ty / p2, | |
828 zero, zero, one); | |
829 | |
830 return true; | |
831 } | |
832 | |
833 SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state) | 728 SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state) |
834 : SkPDFDict("Pattern"), | 729 : SkPDFDict("Pattern"), |
835 fState(state) { | 730 fState(state) { |
836 SkString (*codeFunction)(const SkShader::GradientInfo& info, | 731 SkString (*codeFunction)(const SkShader::GradientInfo& info) = NULL; |
837 const SkMatrix& perspectiveRemover) = NULL; | |
838 SkPoint transformPoints[2]; | 732 SkPoint transformPoints[2]; |
839 | 733 |
840 // Depending on the type of the gradient, we want to transform the | 734 // Depending on the type of the gradient, we want to transform the |
841 // coordinate space in different ways. | 735 // coordinate space in different ways. |
842 const SkShader::GradientInfo* info = &fState.get()->fInfo; | 736 const SkShader::GradientInfo* info = &fState.get()->fInfo; |
843 transformPoints[0] = info->fPoint[0]; | 737 transformPoints[0] = info->fPoint[0]; |
844 transformPoints[1] = info->fPoint[1]; | 738 transformPoints[1] = info->fPoint[1]; |
845 switch (fState.get()->fType) { | 739 switch (fState.get()->fType) { |
846 case SkShader::kLinear_GradientType: | 740 case SkShader::kLinear_GradientType: |
847 codeFunction = &linearCode; | 741 codeFunction = &linearCode; |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
879 default: | 773 default: |
880 return; | 774 return; |
881 } | 775 } |
882 | 776 |
883 // Move any scaling (assuming a unit gradient) or translation | 777 // Move any scaling (assuming a unit gradient) or translation |
884 // (and rotation for linear gradient), of the final gradient from | 778 // (and rotation for linear gradient), of the final gradient from |
885 // info->fPoints to the matrix (updating bbox appropriately). Now | 779 // info->fPoints to the matrix (updating bbox appropriately). Now |
886 // the gradient can be drawn on on the unit segment. | 780 // the gradient can be drawn on on the unit segment. |
887 SkMatrix mapperMatrix; | 781 SkMatrix mapperMatrix; |
888 unitToPointsMatrix(transformPoints, &mapperMatrix); | 782 unitToPointsMatrix(transformPoints, &mapperMatrix); |
889 | |
890 SkMatrix perspectiveInverseOnly = SkMatrix::I(); | |
891 | |
892 SkMatrix finalMatrix = fState.get()->fCanvasTransform; | 783 SkMatrix finalMatrix = fState.get()->fCanvasTransform; |
893 finalMatrix.preConcat(fState.get()->fShaderTransform); | 784 finalMatrix.preConcat(fState.get()->fShaderTransform); |
894 | |
895 // Preserves as much as posible in the final matrix, and only removes | |
896 // the perspective. The inverse of the perspective is stored in | |
897 // perspectiveInverseOnly matrix and has 3 useful numbers | |
898 // (p0, p1, p2), while everything else is either 0 or 1. | |
899 // In this way the shader will handle it eficiently, with minimal code. | |
900 if (finalMatrix.hasPerspective()) { | |
901 if (!split_perspective(finalMatrix, | |
902 &finalMatrix, &perspectiveInverseOnly)) { | |
903 return; | |
904 } | |
905 } | |
906 | |
907 finalMatrix.preConcat(mapperMatrix); | 785 finalMatrix.preConcat(mapperMatrix); |
908 | 786 |
909 SkRect bbox; | 787 SkRect bbox; |
910 bbox.set(fState.get()->fBBox); | 788 bbox.set(fState.get()->fBBox); |
911 if (!inverseTransformBBox(finalMatrix, &bbox)) { | 789 if (!inverseTransformBBox(finalMatrix, &bbox)) { |
912 return; | 790 return; |
913 } | 791 } |
914 | 792 |
915 SkAutoTUnref<SkPDFArray> domain(new SkPDFArray); | 793 SkAutoTUnref<SkPDFArray> domain(new SkPDFArray); |
916 domain->reserve(4); | 794 domain->reserve(4); |
(...skipping 10 matching lines...) Expand all Loading... |
927 SkShader::GradientInfo twoPointRadialInfo = *info; | 805 SkShader::GradientInfo twoPointRadialInfo = *info; |
928 SkMatrix inverseMapperMatrix; | 806 SkMatrix inverseMapperMatrix; |
929 if (!mapperMatrix.invert(&inverseMapperMatrix)) { | 807 if (!mapperMatrix.invert(&inverseMapperMatrix)) { |
930 return; | 808 return; |
931 } | 809 } |
932 inverseMapperMatrix.mapPoints(twoPointRadialInfo.fPoint, 2); | 810 inverseMapperMatrix.mapPoints(twoPointRadialInfo.fPoint, 2); |
933 twoPointRadialInfo.fRadius[0] = | 811 twoPointRadialInfo.fRadius[0] = |
934 inverseMapperMatrix.mapRadius(info->fRadius[0]); | 812 inverseMapperMatrix.mapRadius(info->fRadius[0]); |
935 twoPointRadialInfo.fRadius[1] = | 813 twoPointRadialInfo.fRadius[1] = |
936 inverseMapperMatrix.mapRadius(info->fRadius[1]); | 814 inverseMapperMatrix.mapRadius(info->fRadius[1]); |
937 functionCode = codeFunction(twoPointRadialInfo, perspectiveInverseOnly); | 815 functionCode = codeFunction(twoPointRadialInfo); |
938 } else { | 816 } else { |
939 functionCode = codeFunction(*info, perspectiveInverseOnly); | 817 functionCode = codeFunction(*info); |
940 } | 818 } |
941 | 819 |
942 SkAutoTUnref<SkPDFDict> pdfShader(new SkPDFDict); | 820 SkAutoTUnref<SkPDFDict> pdfShader(new SkPDFDict); |
943 pdfShader->insertInt("ShadingType", 1); | 821 pdfShader->insertInt("ShadingType", 1); |
944 pdfShader->insertName("ColorSpace", "DeviceRGB"); | 822 pdfShader->insertName("ColorSpace", "DeviceRGB"); |
945 pdfShader->insert("Domain", domain.get()); | 823 pdfShader->insert("Domain", domain.get()); |
946 | 824 |
947 SkPDFStream* function = makePSFunction(functionCode, domain.get()); | 825 SkPDFStream* function = makePSFunction(functionCode, domain.get()); |
948 pdfShader->insert("Function", new SkPDFObjRef(function))->unref(); | 826 pdfShader->insert("Function", new SkPDFObjRef(function))->unref(); |
949 fResources.push(function); // Pass ownership to resource list. | 827 fResources.push(function); // Pass ownership to resource list. |
(...skipping 390 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1340 return false; | 1218 return false; |
1341 } | 1219 } |
1342 | 1220 |
1343 void SkPDFShader::State::AllocateGradientInfoStorage() { | 1221 void SkPDFShader::State::AllocateGradientInfoStorage() { |
1344 fColorData.set(sk_malloc_throw( | 1222 fColorData.set(sk_malloc_throw( |
1345 fInfo.fColorCount * (sizeof(SkColor) + sizeof(SkScalar)))); | 1223 fInfo.fColorCount * (sizeof(SkColor) + sizeof(SkScalar)))); |
1346 fInfo.fColors = reinterpret_cast<SkColor*>(fColorData.get()); | 1224 fInfo.fColors = reinterpret_cast<SkColor*>(fColorData.get()); |
1347 fInfo.fColorOffsets = | 1225 fInfo.fColorOffsets = |
1348 reinterpret_cast<SkScalar*>(fInfo.fColors + fInfo.fColorCount); | 1226 reinterpret_cast<SkScalar*>(fInfo.fColors + fInfo.fColorCount); |
1349 } | 1227 } |
OLD | NEW |