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

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