Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(63)

Side by Side Diff: src/pdf/SkPDFShader.cpp

Issue 26389006: PDF: support perspective in simple shaders. (this version does not work well with tilling) (Closed) Base URL: https://skia.googlecode.com/svn/trunk
Patch Set: previous upload failed Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « src/pdf/SkPDFDevice.cpp ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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 static SkString linearCode(const SkShader::GradientInfo& info) { 213 /**
214 SkString function("{pop\n"); // Just ditch the y value. 214 * Returns PS function code that would apply perspective to a x, y point.
vandebo (ex-Chrome) 2013/10/21 19:23:19 nit: would apply -> applies
edisonn 2013/10/23 19:45:18 Done.
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
vandebo (ex-Chrome) 2013/10/21 19:23:19 nit: ececuting -> executing
edisonn 2013/10/23 19:45:18 Done.
218 * while the rest of the stack is preserved intact.
219 * xy2xy is the inverse perspective matrix.
vandebo (ex-Chrome) 2013/10/21 19:23:19 nit: update or remove
edisonn 2013/10/23 19:45:18 Done.
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.
215 tileModeCode(info.fTileMode, &function); 269 tileModeCode(info.fTileMode, &function);
216 gradientFunctionCode(info, &function); 270 gradientFunctionCode(info, &function);
217 function.append("}"); 271 function.append("}");
218 return function; 272 return function;
219 } 273 }
220 274
221 static SkString radialCode(const SkShader::GradientInfo& info) { 275 static SkString radialCode(const SkShader::GradientInfo& info,
276 const SkMatrix& perspectiveRemover) {
222 SkString function("{"); 277 SkString function("{");
278
279 function.append(apply_perspective_to_coordinates(perspectiveRemover));
280
223 // Find the distance from the origin. 281 // Find the distance from the origin.
224 function.append("dup " // x y y 282 function.append("dup " // x y y
225 "mul " // x y^2 283 "mul " // x y^2
226 "exch " // y^2 x 284 "exch " // y^2 x
227 "dup " // y^2 x x 285 "dup " // y^2 x x
228 "mul " // y^2 x^2 286 "mul " // y^2 x^2
229 "add " // y^2+x^2 287 "add " // y^2+x^2
230 "sqrt\n"); // sqrt(y^2+x^2) 288 "sqrt\n"); // sqrt(y^2+x^2)
231 289
232 tileModeCode(info.fTileMode, &function); 290 tileModeCode(info.fTileMode, &function);
233 gradientFunctionCode(info, &function); 291 gradientFunctionCode(info, &function);
234 function.append("}"); 292 function.append("}");
235 return function; 293 return function;
236 } 294 }
237 295
238 /* The math here is all based on the description in Two_Point_Radial_Gradient, 296 /* The math here is all based on the description in Two_Point_Radial_Gradient,
239 with one simplification, the coordinate space has been scaled so that 297 with one simplification, the coordinate space has been scaled so that
240 Dr = 1. This means we don't need to scale the entire equation by 1/Dr^2. 298 Dr = 1. This means we don't need to scale the entire equation by 1/Dr^2.
241 */ 299 */
242 static SkString twoPointRadialCode(const SkShader::GradientInfo& info) { 300 static SkString twoPointRadialCode(const SkShader::GradientInfo& info,
301 const SkMatrix& perspectiveRemover) {
243 SkScalar dx = info.fPoint[0].fX - info.fPoint[1].fX; 302 SkScalar dx = info.fPoint[0].fX - info.fPoint[1].fX;
244 SkScalar dy = info.fPoint[0].fY - info.fPoint[1].fY; 303 SkScalar dy = info.fPoint[0].fY - info.fPoint[1].fY;
245 SkScalar sr = info.fRadius[0]; 304 SkScalar sr = info.fRadius[0];
246 SkScalar a = SkScalarMul(dx, dx) + SkScalarMul(dy, dy) - SK_Scalar1; 305 SkScalar a = SkScalarMul(dx, dx) + SkScalarMul(dy, dy) - SK_Scalar1;
247 bool posRoot = info.fRadius[1] > info.fRadius[0]; 306 bool posRoot = info.fRadius[1] > info.fRadius[0];
248 307
249 // We start with a stack of (x y), copy it and then consume one copy in 308 // We start with a stack of (x y), copy it and then consume one copy in
250 // order to calculate b and the other to calculate c. 309 // order to calculate b and the other to calculate c.
251 SkString function("{"); 310 SkString function("{");
311
312 function.append(apply_perspective_to_coordinates(perspectiveRemover));
313
252 function.append("2 copy "); 314 function.append("2 copy ");
253 315
254 // Calculate -b and b^2. 316 // Calculate -b and b^2.
255 function.appendScalar(dy); 317 function.appendScalar(dy);
256 function.append(" mul exch "); 318 function.append(" mul exch ");
257 function.appendScalar(dx); 319 function.appendScalar(dx);
258 function.append(" mul add "); 320 function.append(" mul add ");
259 function.appendScalar(sr); 321 function.appendScalar(sr);
260 function.append(" sub 2 mul neg dup dup mul\n"); 322 function.append(" sub 2 mul neg dup dup mul\n");
261 323
(...skipping 17 matching lines...) Expand all
279 341
280 tileModeCode(info.fTileMode, &function); 342 tileModeCode(info.fTileMode, &function);
281 gradientFunctionCode(info, &function); 343 gradientFunctionCode(info, &function);
282 function.append("}"); 344 function.append("}");
283 return function; 345 return function;
284 } 346 }
285 347
286 /* Conical gradient shader, based on the Canvas spec for radial gradients 348 /* Conical gradient shader, based on the Canvas spec for radial gradients
287 See: http://www.w3.org/TR/2dcontext/#dom-context-2d-createradialgradient 349 See: http://www.w3.org/TR/2dcontext/#dom-context-2d-createradialgradient
288 */ 350 */
289 static SkString twoPointConicalCode(const SkShader::GradientInfo& info) { 351 static SkString twoPointConicalCode(const SkShader::GradientInfo& info,
352 const SkMatrix& perspectiveRemover) {
290 SkScalar dx = info.fPoint[1].fX - info.fPoint[0].fX; 353 SkScalar dx = info.fPoint[1].fX - info.fPoint[0].fX;
291 SkScalar dy = info.fPoint[1].fY - info.fPoint[0].fY; 354 SkScalar dy = info.fPoint[1].fY - info.fPoint[0].fY;
292 SkScalar r0 = info.fRadius[0]; 355 SkScalar r0 = info.fRadius[0];
293 SkScalar dr = info.fRadius[1] - info.fRadius[0]; 356 SkScalar dr = info.fRadius[1] - info.fRadius[0];
294 SkScalar a = SkScalarMul(dx, dx) + SkScalarMul(dy, dy) - 357 SkScalar a = SkScalarMul(dx, dx) + SkScalarMul(dy, dy) -
295 SkScalarMul(dr, dr); 358 SkScalarMul(dr, dr);
296 359
297 // First compute t, if the pixel falls outside the cone, then we'll end 360 // First compute t, if the pixel falls outside the cone, then we'll end
298 // with 'false' on the stack, otherwise we'll push 'true' with t below it 361 // with 'false' on the stack, otherwise we'll push 'true' with t below it
299 362
300 // We start with a stack of (x y), copy it and then consume one copy in 363 // We start with a stack of (x y), copy it and then consume one copy in
301 // order to calculate b and the other to calculate c. 364 // order to calculate b and the other to calculate c.
302 SkString function("{"); 365 SkString function("{");
366
367 function.append(apply_perspective_to_coordinates(perspectiveRemover));
368
303 function.append("2 copy "); 369 function.append("2 copy ");
304 370
305 // Calculate b and b^2; b = -2 * (y * dy + x * dx + r0 * dr). 371 // Calculate b and b^2; b = -2 * (y * dy + x * dx + r0 * dr).
306 function.appendScalar(dy); 372 function.appendScalar(dy);
307 function.append(" mul exch "); 373 function.append(" mul exch ");
308 function.appendScalar(dx); 374 function.appendScalar(dx);
309 function.append(" mul add "); 375 function.append(" mul add ");
310 function.appendScalar(SkScalarMul(r0, dr)); 376 function.appendScalar(SkScalarMul(r0, dr));
311 function.append(" add -2 mul dup dup mul\n"); 377 function.append(" add -2 mul dup dup mul\n");
312 378
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
388 function.append("{"); 454 function.append("{");
389 tileModeCode(info.fTileMode, &function); 455 tileModeCode(info.fTileMode, &function);
390 gradientFunctionCode(info, &function); 456 gradientFunctionCode(info, &function);
391 457
392 // otherwise, just write black 458 // otherwise, just write black
393 function.append("} {0 0 0} ifelse }"); 459 function.append("} {0 0 0} ifelse }");
394 460
395 return function; 461 return function;
396 } 462 }
397 463
398 static SkString sweepCode(const SkShader::GradientInfo& info) { 464 static SkString sweepCode(const SkShader::GradientInfo& info,
465 const SkMatrix& perspectiveRemover) {
399 SkString function("{exch atan 360 div\n"); 466 SkString function("{exch atan 360 div\n");
400 tileModeCode(info.fTileMode, &function); 467 tileModeCode(info.fTileMode, &function);
401 gradientFunctionCode(info, &function); 468 gradientFunctionCode(info, &function);
402 function.append("}"); 469 function.append("}");
403 return function; 470 return function;
404 } 471 }
405 472
406 class SkPDFShader::State { 473 class SkPDFShader::State {
407 public: 474 public:
408 SkShader::GradientType fType; 475 SkShader::GradientType fType;
(...skipping 309 matching lines...) Expand 10 before | Expand all | Expand 10 after
718 get_gradient_resource_dict(fColorShader.get(), alphaGs.get())); 785 get_gradient_resource_dict(fColorShader.get(), alphaGs.get()));
719 786
720 SkAutoTUnref<SkStream> colorStream( 787 SkAutoTUnref<SkStream> colorStream(
721 create_pattern_fill_content(0, bbox)); 788 create_pattern_fill_content(0, bbox));
722 setData(colorStream.get()); 789 setData(colorStream.get());
723 790
724 populate_tiling_pattern_dict(this, bbox, fResourceDict.get(), 791 populate_tiling_pattern_dict(this, bbox, fResourceDict.get(),
725 SkMatrix::I()); 792 SkMatrix::I());
726 } 793 }
727 794
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
728 SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state) 833 SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state)
729 : SkPDFDict("Pattern"), 834 : SkPDFDict("Pattern"),
730 fState(state) { 835 fState(state) {
731 SkString (*codeFunction)(const SkShader::GradientInfo& info) = NULL; 836 SkString (*codeFunction)(const SkShader::GradientInfo& info,
837 const SkMatrix& perspectiveRemover) = NULL;
732 SkPoint transformPoints[2]; 838 SkPoint transformPoints[2];
733 839
734 // Depending on the type of the gradient, we want to transform the 840 // Depending on the type of the gradient, we want to transform the
735 // coordinate space in different ways. 841 // coordinate space in different ways.
736 const SkShader::GradientInfo* info = &fState.get()->fInfo; 842 const SkShader::GradientInfo* info = &fState.get()->fInfo;
737 transformPoints[0] = info->fPoint[0]; 843 transformPoints[0] = info->fPoint[0];
738 transformPoints[1] = info->fPoint[1]; 844 transformPoints[1] = info->fPoint[1];
739 switch (fState.get()->fType) { 845 switch (fState.get()->fType) {
740 case SkShader::kLinear_GradientType: 846 case SkShader::kLinear_GradientType:
741 codeFunction = &linearCode; 847 codeFunction = &linearCode;
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
773 default: 879 default:
774 return; 880 return;
775 } 881 }
776 882
777 // Move any scaling (assuming a unit gradient) or translation 883 // Move any scaling (assuming a unit gradient) or translation
778 // (and rotation for linear gradient), of the final gradient from 884 // (and rotation for linear gradient), of the final gradient from
779 // info->fPoints to the matrix (updating bbox appropriately). Now 885 // info->fPoints to the matrix (updating bbox appropriately). Now
780 // the gradient can be drawn on on the unit segment. 886 // the gradient can be drawn on on the unit segment.
781 SkMatrix mapperMatrix; 887 SkMatrix mapperMatrix;
782 unitToPointsMatrix(transformPoints, &mapperMatrix); 888 unitToPointsMatrix(transformPoints, &mapperMatrix);
889
890 SkMatrix perspectiveInverseOnly = SkMatrix::I();
891
783 SkMatrix finalMatrix = fState.get()->fCanvasTransform; 892 SkMatrix finalMatrix = fState.get()->fCanvasTransform;
784 finalMatrix.preConcat(fState.get()->fShaderTransform); 893 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
785 finalMatrix.preConcat(mapperMatrix); 907 finalMatrix.preConcat(mapperMatrix);
786 908
787 SkRect bbox; 909 SkRect bbox;
788 bbox.set(fState.get()->fBBox); 910 bbox.set(fState.get()->fBBox);
789 if (!inverseTransformBBox(finalMatrix, &bbox)) { 911 if (!inverseTransformBBox(finalMatrix, &bbox)) {
790 return; 912 return;
791 } 913 }
792 914
793 SkAutoTUnref<SkPDFArray> domain(new SkPDFArray); 915 SkAutoTUnref<SkPDFArray> domain(new SkPDFArray);
794 domain->reserve(4); 916 domain->reserve(4);
(...skipping 10 matching lines...) Expand all
805 SkShader::GradientInfo twoPointRadialInfo = *info; 927 SkShader::GradientInfo twoPointRadialInfo = *info;
806 SkMatrix inverseMapperMatrix; 928 SkMatrix inverseMapperMatrix;
807 if (!mapperMatrix.invert(&inverseMapperMatrix)) { 929 if (!mapperMatrix.invert(&inverseMapperMatrix)) {
808 return; 930 return;
809 } 931 }
810 inverseMapperMatrix.mapPoints(twoPointRadialInfo.fPoint, 2); 932 inverseMapperMatrix.mapPoints(twoPointRadialInfo.fPoint, 2);
811 twoPointRadialInfo.fRadius[0] = 933 twoPointRadialInfo.fRadius[0] =
812 inverseMapperMatrix.mapRadius(info->fRadius[0]); 934 inverseMapperMatrix.mapRadius(info->fRadius[0]);
813 twoPointRadialInfo.fRadius[1] = 935 twoPointRadialInfo.fRadius[1] =
814 inverseMapperMatrix.mapRadius(info->fRadius[1]); 936 inverseMapperMatrix.mapRadius(info->fRadius[1]);
815 functionCode = codeFunction(twoPointRadialInfo); 937 functionCode = codeFunction(twoPointRadialInfo, perspectiveInverseOnly);
816 } else { 938 } else {
817 functionCode = codeFunction(*info); 939 functionCode = codeFunction(*info, perspectiveInverseOnly);
818 } 940 }
819 941
820 SkAutoTUnref<SkPDFDict> pdfShader(new SkPDFDict); 942 SkAutoTUnref<SkPDFDict> pdfShader(new SkPDFDict);
821 pdfShader->insertInt("ShadingType", 1); 943 pdfShader->insertInt("ShadingType", 1);
822 pdfShader->insertName("ColorSpace", "DeviceRGB"); 944 pdfShader->insertName("ColorSpace", "DeviceRGB");
823 pdfShader->insert("Domain", domain.get()); 945 pdfShader->insert("Domain", domain.get());
824 946
825 SkPDFStream* function = makePSFunction(functionCode, domain.get()); 947 SkPDFStream* function = makePSFunction(functionCode, domain.get());
826 pdfShader->insert("Function", new SkPDFObjRef(function))->unref(); 948 pdfShader->insert("Function", new SkPDFObjRef(function))->unref();
827 fResources.push(function); // Pass ownership to resource list. 949 fResources.push(function); // Pass ownership to resource list.
(...skipping 390 matching lines...) Expand 10 before | Expand all | Expand 10 after
1218 return false; 1340 return false;
1219 } 1341 }
1220 1342
1221 void SkPDFShader::State::AllocateGradientInfoStorage() { 1343 void SkPDFShader::State::AllocateGradientInfoStorage() {
1222 fColorData.set(sk_malloc_throw( 1344 fColorData.set(sk_malloc_throw(
1223 fInfo.fColorCount * (sizeof(SkColor) + sizeof(SkScalar)))); 1345 fInfo.fColorCount * (sizeof(SkColor) + sizeof(SkScalar))));
1224 fInfo.fColors = reinterpret_cast<SkColor*>(fColorData.get()); 1346 fInfo.fColors = reinterpret_cast<SkColor*>(fColorData.get());
1225 fInfo.fColorOffsets = 1347 fInfo.fColorOffsets =
1226 reinterpret_cast<SkScalar*>(fInfo.fColors + fInfo.fColorCount); 1348 reinterpret_cast<SkScalar*>(fInfo.fColors + fInfo.fColorCount);
1227 } 1349 }
OLDNEW
« no previous file with comments | « src/pdf/SkPDFDevice.cpp ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698