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

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

Issue 1925233003: SkPDF: Use type 2/3 shading for gradient shaders (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: addressed hal's comment Created 4 years, 7 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
« no previous file with comments | « no previous file | 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 * Copyright 2011 Google Inc. 2 * Copyright 2011 Google Inc.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 8
9 #include "SkPDFShader.h" 9 #include "SkPDFShader.h"
10 10
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after
115 colorData[2][r,g,b]); 115 colorData[2][r,g,b]);
116 } else { 116 } else {
117 117
118 ... } else { 118 ... } else {
119 return colorData[info.fColorCount - 1][r,g,b]; 119 return colorData[info.fColorCount - 1][r,g,b];
120 } 120 }
121 ... 121 ...
122 } 122 }
123 } 123 }
124 */ 124 */
125 static const int kColorComponents = 3;
126 typedef SkScalar ColorTuple[kColorComponents];
125 static void gradientFunctionCode(const SkShader::GradientInfo& info, 127 static void gradientFunctionCode(const SkShader::GradientInfo& info,
126 SkDynamicMemoryWStream* result) { 128 SkDynamicMemoryWStream* result) {
127 /* We want to linearly interpolate from the previous color to the next. 129 /* We want to linearly interpolate from the previous color to the next.
128 Scale the colors from 0..255 to 0..1 and determine the multipliers 130 Scale the colors from 0..255 to 0..1 and determine the multipliers
129 for interpolation. 131 for interpolation.
130 C{r,g,b}(t, section) = t - offset_(section-1) + t * Multiplier{r,g,b}. 132 C{r,g,b}(t, section) = t - offset_(section-1) + t * Multiplier{r,g,b}.
131 */ 133 */
132 static const int kColorComponents = 3; 134
133 typedef SkScalar ColorTuple[kColorComponents];
134 SkAutoSTMalloc<4, ColorTuple> colorDataAlloc(info.fColorCount); 135 SkAutoSTMalloc<4, ColorTuple> colorDataAlloc(info.fColorCount);
135 ColorTuple *colorData = colorDataAlloc.get(); 136 ColorTuple *colorData = colorDataAlloc.get();
136 const SkScalar scale = SkScalarInvert(SkIntToScalar(255)); 137 const SkScalar scale = SkScalarInvert(SkIntToScalar(255));
137 for (int i = 0; i < info.fColorCount; i++) { 138 for (int i = 0; i < info.fColorCount; i++) {
138 colorData[i][0] = SkScalarMul(SkColorGetR(info.fColors[i]), scale); 139 colorData[i][0] = SkScalarMul(SkColorGetR(info.fColors[i]), scale);
139 colorData[i][1] = SkScalarMul(SkColorGetG(info.fColors[i]), scale); 140 colorData[i][1] = SkScalarMul(SkColorGetG(info.fColors[i]), scale);
140 colorData[i][2] = SkScalarMul(SkColorGetB(info.fColors[i]), scale); 141 colorData[i][2] = SkScalarMul(SkColorGetB(info.fColors[i]), scale);
141 } 142 }
142 143
143 // Clamp the initial color. 144 // Clamp the initial color.
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
176 result->writeText(" "); 177 result->writeText(" ");
177 SkPDFUtils::AppendScalar(colorData[info.fColorCount - 1][1], result); 178 SkPDFUtils::AppendScalar(colorData[info.fColorCount - 1][1], result);
178 result->writeText(" "); 179 result->writeText(" ");
179 SkPDFUtils::AppendScalar(colorData[info.fColorCount - 1][2], result); 180 SkPDFUtils::AppendScalar(colorData[info.fColorCount - 1][2], result);
180 181
181 for (int i = 0 ; i < gradients + 1; i++) { 182 for (int i = 0 ; i < gradients + 1; i++) {
182 result->writeText("} ifelse\n"); 183 result->writeText("} ifelse\n");
183 } 184 }
184 } 185 }
185 186
187 static sk_sp<SkPDFDict> createInterpolationFunction(const ColorTuple& color1,
188 const ColorTuple& color2 ) {
189 auto retval = sk_make_sp<SkPDFDict>();
190
191 auto c0 = sk_make_sp<SkPDFArray>();
192 c0->appendScalar(color1[0]);
193 c0->appendScalar(color1[1]);
194 c0->appendScalar(color1[2]);
195 retval->insertObject("C0", std::move(c0));
196
197 auto c1 = sk_make_sp<SkPDFArray>();
198 c1->appendScalar(color2[0]);
199 c1->appendScalar(color2[1]);
200 c1->appendScalar(color2[2]);
201 retval->insertObject("C1", std::move(c1));
202
203 auto domain = sk_make_sp<SkPDFArray>();
204 domain->appendScalar(0);
205 domain->appendScalar(1.0f);
206 retval->insertObject("Domain", std::move(domain));
207
208 retval->insertInt("FunctionType", 2);
209 retval->insertScalar("N", 1.0f);
210
211 return retval;
212 }
213
214 static sk_sp<SkPDFDict> gradientStitchCode(const SkShader::GradientInfo& info) {
215 auto retval = sk_make_sp<SkPDFDict>();
216
217 SkAutoSTMalloc<4, ColorTuple> colorDataAlloc(info.fColorCount);
218 ColorTuple *colorData = colorDataAlloc.get();
219 const SkScalar scale = SkScalarInvert(SkIntToScalar(255));
220 for (int i = 0; i < info.fColorCount; i++) {
221 colorData[i][0] = SkScalarMul(SkColorGetR(info.fColors[i]), scale);
222 colorData[i][1] = SkScalarMul(SkColorGetG(info.fColors[i]), scale);
223 colorData[i][2] = SkScalarMul(SkColorGetB(info.fColors[i]), scale);
224 }
225
226 // no need for a stitch function if there are only 2 stops.
227 if (info.fColorCount == 2)
228 return createInterpolationFunction(colorData[0], colorData[1]);
229
230 auto encode = sk_make_sp<SkPDFArray>();
231 auto bounds = sk_make_sp<SkPDFArray>();
232 auto functions = sk_make_sp<SkPDFArray>();
233
234 auto domain = sk_make_sp<SkPDFArray>();
235 domain->appendScalar(0);
236 domain->appendScalar(1.0f);
237 retval->insertObject("Domain", std::move(domain));
238 retval->insertInt("FunctionType", 3);
239
240 for (int i = 1; i < info.fColorCount; i++) {
241 SkScalar offset = 0;
242 if (functions->size() > 0) {
243 offset = info.fColorOffsets[i-1];
244 if (offset >= 0.99998f) {
245 bounds->appendScalar(1.0f);
246 } else if (offset == 0) {// if a colorstop has offset 0, remove prev ious stops.
hal.canary 2016/05/04 17:42:59 does this ever happen? offsets [0,0,1,1] with a c
247 encode = sk_make_sp<SkPDFArray>();
248 bounds = sk_make_sp<SkPDFArray>();
249 functions = sk_make_sp<SkPDFArray>();
250 } else {
251 bounds->appendScalar(offset);
252 }
253 }
254
255 encode->appendScalar(0);
256 encode->appendScalar(1.0f);
257
258 functions->appendObject(createInterpolationFunction(colorData[i-1], colo rData[i]));
259
260 if (offset >= 0.99998f) { // stop at colorstop that has 100% offset. ign ore later ones.
261 break;
262 }
263 }
264
265 retval->insertObject("Encode", std::move(encode));
266 retval->insertObject("Bounds", std::move(bounds));
267 retval->insertObject("Functions", std::move(functions));
268
269 return retval;
270 }
271
272
186 /* Map a value of t on the stack into [0, 1) for Repeat or Mirror tile mode. */ 273 /* Map a value of t on the stack into [0, 1) for Repeat or Mirror tile mode. */
187 static void tileModeCode(SkShader::TileMode mode, 274 static void tileModeCode(SkShader::TileMode mode,
188 SkDynamicMemoryWStream* result) { 275 SkDynamicMemoryWStream* result) {
189 if (mode == SkShader::kRepeat_TileMode) { 276 if (mode == SkShader::kRepeat_TileMode) {
190 result->writeText("dup truncate sub\n"); // Get the fractional part. 277 result->writeText("dup truncate sub\n"); // Get the fractional part.
191 result->writeText("dup 0 le {1 add} if\n"); // Map (-1,0) => (0,1) 278 result->writeText("dup 0 le {1 add} if\n"); // Map (-1,0) => (0,1)
192 return; 279 return;
193 } 280 }
194 281
195 if (mode == SkShader::kMirror_TileMode) { 282 if (mode == SkShader::kMirror_TileMode) {
(...skipping 510 matching lines...) Expand 10 before | Expand all | Expand 10 after
706 } 793 }
707 794
708 SkPDFFunctionShader* SkPDFFunctionShader::Create( 795 SkPDFFunctionShader* SkPDFFunctionShader::Create(
709 SkPDFCanon* canon, std::unique_ptr<SkPDFShader::State>* autoState) { 796 SkPDFCanon* canon, std::unique_ptr<SkPDFShader::State>* autoState) {
710 const SkPDFShader::State& state = **autoState; 797 const SkPDFShader::State& state = **autoState;
711 798
712 void (*codeFunction)(const SkShader::GradientInfo& info, 799 void (*codeFunction)(const SkShader::GradientInfo& info,
713 const SkMatrix& perspectiveRemover, 800 const SkMatrix& perspectiveRemover,
714 SkDynamicMemoryWStream* function) = nullptr; 801 SkDynamicMemoryWStream* function) = nullptr;
715 SkPoint transformPoints[2]; 802 SkPoint transformPoints[2];
803 const SkShader::GradientInfo* info = &state.fInfo;
716 804
717 // Depending on the type of the gradient, we want to transform the 805 bool doStitchFunctions = (state.fType == SkShader::kLinear_GradientType ||
718 // coordinate space in different ways. 806 state.fType == SkShader::kRadial_GradientType ||
719 const SkShader::GradientInfo* info = &state.fInfo; 807 state.fType == SkShader::kConical_GradientType) &&
720 transformPoints[0] = info->fPoint[0]; 808 info->fTileMode == SkShader::kClamp_TileMode;
721 transformPoints[1] = info->fPoint[1];
722 switch (state.fType) {
723 case SkShader::kLinear_GradientType:
724 codeFunction = &linearCode;
725 break;
726 case SkShader::kRadial_GradientType:
727 transformPoints[1] = transformPoints[0];
728 transformPoints[1].fX += info->fRadius[0];
729 codeFunction = &radialCode;
730 break;
731 case SkShader::kConical_GradientType: {
732 transformPoints[1] = transformPoints[0];
733 transformPoints[1].fX += SK_Scalar1;
734 codeFunction = &twoPointConicalCode;
735 break;
736 }
737 case SkShader::kSweep_GradientType:
738 transformPoints[1] = transformPoints[0];
739 transformPoints[1].fX += SK_Scalar1;
740 codeFunction = &sweepCode;
741 break;
742 case SkShader::kColor_GradientType:
743 case SkShader::kNone_GradientType:
744 default:
745 return nullptr;
746 }
747
748 // Move any scaling (assuming a unit gradient) or translation
749 // (and rotation for linear gradient), of the final gradient from
750 // info->fPoints to the matrix (updating bbox appropriately). Now
751 // the gradient can be drawn on on the unit segment.
752 SkMatrix mapperMatrix;
753 unitToPointsMatrix(transformPoints, &mapperMatrix);
754 809
755 SkMatrix finalMatrix = state.fCanvasTransform; 810 SkMatrix finalMatrix = state.fCanvasTransform;
756 finalMatrix.preConcat(state.fShaderTransform); 811 finalMatrix.preConcat(state.fShaderTransform);
757 finalMatrix.preConcat(mapperMatrix);
758
759 // Preserves as much as posible in the final matrix, and only removes
760 // the perspective. The inverse of the perspective is stored in
761 // perspectiveInverseOnly matrix and has 3 useful numbers
762 // (p0, p1, p2), while everything else is either 0 or 1.
763 // In this way the shader will handle it eficiently, with minimal code.
764 SkMatrix perspectiveInverseOnly = SkMatrix::I();
765 if (finalMatrix.hasPerspective()) {
766 if (!split_perspective(finalMatrix,
767 &finalMatrix, &perspectiveInverseOnly)) {
768 return nullptr;
769 }
770 }
771
772 SkRect bbox;
773 bbox.set(state.fBBox);
774 if (!inverse_transform_bbox(finalMatrix, &bbox)) {
775 return nullptr;
776 }
777 812
778 auto domain = sk_make_sp<SkPDFArray>(); 813 auto domain = sk_make_sp<SkPDFArray>();
779 domain->reserve(4);
780 domain->appendScalar(bbox.fLeft);
781 domain->appendScalar(bbox.fRight);
782 domain->appendScalar(bbox.fTop);
783 domain->appendScalar(bbox.fBottom);
784 814
785 SkDynamicMemoryWStream functionCode; 815 int32_t shadingType = 1;
816 auto pdfShader = sk_make_sp<SkPDFDict>();
786 // The two point radial gradient further references 817 // The two point radial gradient further references
787 // state.fInfo 818 // state.fInfo
788 // in translating from x, y coordinates to the t parameter. So, we have 819 // in translating from x, y coordinates to the t parameter. So, we have
789 // to transform the points and radii according to the calculated matrix. 820 // to transform the points and radii according to the calculated matrix.
790 if (state.fType == SkShader::kConical_GradientType) { 821 if (doStitchFunctions) {
791 SkShader::GradientInfo twoPointRadialInfo = *info; 822 pdfShader->insertObject("Function", std::move(gradientStitchCode(*info)) );
hal.canary 2016/05/04 12:57:17 don't need the std::move here, it is already a r-v
792 SkMatrix inverseMapperMatrix; 823 shadingType = (state.fType == SkShader::kLinear_GradientType) ? 2 : 3;
793 if (!mapperMatrix.invert(&inverseMapperMatrix)) { 824
825 auto extend = sk_make_sp<SkPDFArray>();
826 extend->reserve(2);
827 extend->appendBool(true);
828 extend->appendBool(true);
829 pdfShader->insertObject("Extend", std::move(extend));
830
831 auto coords = sk_make_sp<SkPDFArray>();
832 if (state.fType == SkShader::kConical_GradientType) {
833 coords->reserve(6);
834 coords->appendScalar(info->fPoint[0].fX);
835 coords->appendScalar(info->fPoint[0].fY);
836 coords->appendScalar(info->fRadius[0]);
837
838 coords->appendScalar(info->fPoint[1].fX);
839 coords->appendScalar(info->fPoint[1].fY);
840 coords->appendScalar(info->fRadius[1]);
841 } else if (state.fType == SkShader::kRadial_GradientType) {
842 coords->reserve(6);
843 coords->appendScalar(info->fPoint[0].fX);
844 coords->appendScalar(info->fPoint[0].fY);
845 coords->appendScalar(0);
846
847 coords->appendScalar(info->fPoint[0].fX);
848 coords->appendScalar(info->fPoint[0].fY);
849 coords->appendScalar(info->fRadius[0]);
850 } else {
851 coords->reserve(4);
852 coords->appendScalar(info->fPoint[0].fX);
853 coords->appendScalar(info->fPoint[0].fY);
854
855 coords->appendScalar(info->fPoint[1].fX);
856 coords->appendScalar(info->fPoint[1].fY);
857 }
858
859 pdfShader->insertObject("Coords", std::move(coords));
860 } else {
861 // Depending on the type of the gradient, we want to transform the
862 // coordinate space in different ways.
863 transformPoints[0] = info->fPoint[0];
864 transformPoints[1] = info->fPoint[1];
865 switch (state.fType) {
866 case SkShader::kLinear_GradientType:
867 codeFunction = &linearCode;
868 break;
869 case SkShader::kRadial_GradientType:
870 transformPoints[1] = transformPoints[0];
871 transformPoints[1].fX += info->fRadius[0];
872 codeFunction = &radialCode;
873 break;
874 case SkShader::kConical_GradientType: {
875 transformPoints[1] = transformPoints[0];
876 transformPoints[1].fX += SK_Scalar1;
877 codeFunction = &twoPointConicalCode;
878 break;
879 }
880 case SkShader::kSweep_GradientType:
881 transformPoints[1] = transformPoints[0];
882 transformPoints[1].fX += SK_Scalar1;
883 codeFunction = &sweepCode;
884 break;
885 case SkShader::kColor_GradientType:
886 case SkShader::kNone_GradientType:
887 default:
888 return nullptr;
889 }
890
891 // Move any scaling (assuming a unit gradient) or translation
892 // (and rotation for linear gradient), of the final gradient from
893 // info->fPoints to the matrix (updating bbox appropriately). Now
894 // the gradient can be drawn on on the unit segment.
895 SkMatrix mapperMatrix;
896 unitToPointsMatrix(transformPoints, &mapperMatrix);
897
898 finalMatrix.preConcat(mapperMatrix);
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 SkMatrix perspectiveInverseOnly = SkMatrix::I();
906 if (finalMatrix.hasPerspective()) {
907 if (!split_perspective(finalMatrix,
908 &finalMatrix, &perspectiveInverseOnly)) {
909 return nullptr;
910 }
911 }
912
913 SkRect bbox;
914 bbox.set(state.fBBox);
915 if (!inverse_transform_bbox(finalMatrix, &bbox)) {
794 return nullptr; 916 return nullptr;
795 } 917 }
796 inverseMapperMatrix.mapPoints(twoPointRadialInfo.fPoint, 2); 918 domain->reserve(4);
797 twoPointRadialInfo.fRadius[0] = 919 domain->appendScalar(bbox.fLeft);
798 inverseMapperMatrix.mapRadius(info->fRadius[0]); 920 domain->appendScalar(bbox.fRight);
799 twoPointRadialInfo.fRadius[1] = 921 domain->appendScalar(bbox.fTop);
800 inverseMapperMatrix.mapRadius(info->fRadius[1]); 922 domain->appendScalar(bbox.fBottom);
801 codeFunction(twoPointRadialInfo, perspectiveInverseOnly, &functionCode); 923
802 } else { 924 SkDynamicMemoryWStream functionCode;
803 codeFunction(*info, perspectiveInverseOnly, &functionCode); 925
926 if (state.fType == SkShader::kConical_GradientType) {
927 SkShader::GradientInfo twoPointRadialInfo = *info;
928 SkMatrix inverseMapperMatrix;
929 if (!mapperMatrix.invert(&inverseMapperMatrix)) {
930 return nullptr;
931 }
932 inverseMapperMatrix.mapPoints(twoPointRadialInfo.fPoint, 2);
933 twoPointRadialInfo.fRadius[0] =
934 inverseMapperMatrix.mapRadius(info->fRadius[0]);
935 twoPointRadialInfo.fRadius[1] =
936 inverseMapperMatrix.mapRadius(info->fRadius[1]);
937 codeFunction(twoPointRadialInfo, perspectiveInverseOnly, &functionCo de);
938 } else {
939 codeFunction(*info, perspectiveInverseOnly, &functionCode);
940 }
941
942 pdfShader->insertObject("Domain", sk_ref_sp(domain.get()));
943
944 // Call canon->makeRangeObject() instead of
945 // SkPDFShader::MakeRangeObject() so that the canon can
946 // deduplicate.
947 std::unique_ptr<SkStreamAsset> functionStream(
948 functionCode.detachAsStream());
949 auto function = make_ps_function(std::move(functionStream), domain.get() ,
950 canon->makeRangeObject());
951 pdfShader->insertObjRef("Function", std::move(function));
804 } 952 }
805 953
806 auto pdfShader = sk_make_sp<SkPDFDict>(); 954 pdfShader->insertInt("ShadingType", shadingType);
807 pdfShader->insertInt("ShadingType", 1);
808 pdfShader->insertName("ColorSpace", "DeviceRGB"); 955 pdfShader->insertName("ColorSpace", "DeviceRGB");
809 pdfShader->insertObject("Domain", sk_ref_sp(domain.get()));
810
811 // Call canon->makeRangeObject() instead of
812 // SkPDFShader::MakeRangeObject() so that the canon can
813 // deduplicate.
814 std::unique_ptr<SkStreamAsset> functionStream(
815 functionCode.detachAsStream());
816 auto function = make_ps_function(std::move(functionStream), domain.get(),
817 canon->makeRangeObject());
818 pdfShader->insertObjRef("Function", std::move(function));
819 956
820 sk_sp<SkPDFFunctionShader> pdfFunctionShader( 957 sk_sp<SkPDFFunctionShader> pdfFunctionShader(
821 new SkPDFFunctionShader(autoState->release())); 958 new SkPDFFunctionShader(autoState->release()));
822 pdfFunctionShader->insertInt("PatternType", 2); 959 pdfFunctionShader->insertInt("PatternType", 2);
823 pdfFunctionShader->insertObject("Matrix", 960 pdfFunctionShader->insertObject("Matrix",
824 SkPDFUtils::MatrixToArray(finalMatrix)); 961 SkPDFUtils::MatrixToArray(finalMatrix));
825 pdfFunctionShader->insertObject("Shading", std::move(pdfShader)); 962 pdfFunctionShader->insertObject("Shading", std::move(pdfShader));
826 963
827 canon->addFunctionShader(pdfFunctionShader.get()); 964 canon->addFunctionShader(pdfFunctionShader.get());
828 return pdfFunctionShader.release(); 965 return pdfFunctionShader.release();
(...skipping 402 matching lines...) Expand 10 before | Expand all | Expand 10 after
1231 return false; 1368 return false;
1232 } 1369 }
1233 1370
1234 void SkPDFShader::State::AllocateGradientInfoStorage() { 1371 void SkPDFShader::State::AllocateGradientInfoStorage() {
1235 fColorData.set(sk_malloc_throw( 1372 fColorData.set(sk_malloc_throw(
1236 fInfo.fColorCount * (sizeof(SkColor) + sizeof(SkScalar)))); 1373 fInfo.fColorCount * (sizeof(SkColor) + sizeof(SkScalar))));
1237 fInfo.fColors = reinterpret_cast<SkColor*>(fColorData.get()); 1374 fInfo.fColors = reinterpret_cast<SkColor*>(fColorData.get());
1238 fInfo.fColorOffsets = 1375 fInfo.fColorOffsets =
1239 reinterpret_cast<SkScalar*>(fInfo.fColors + fInfo.fColorCount); 1376 reinterpret_cast<SkScalar*>(fInfo.fColors + fInfo.fColorCount);
1240 } 1377 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698