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

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