@@ -941,6 +941,265 @@ PDFErrorOr<void> LatticeFormGouraudShading::draw(Gfx::Painter&, Gfx::AffineTrans
941
941
return Error::rendering_unsupported_error (" Cannot draw lattice-form gouraud-shaded triangle meshes yet" );
942
942
}
943
943
944
+ class CoonsPatchShading final : public Shading {
945
+ public:
946
+ static PDFErrorOr<NonnullRefPtr<CoonsPatchShading>> create (Document*, NonnullRefPtr<StreamObject>, CommonEntries);
947
+
948
+ virtual PDFErrorOr<void > draw (Gfx::Painter&, Gfx::AffineTransform const &) override ;
949
+
950
+ private:
951
+ using FunctionsType = Variant<Empty, NonnullRefPtr<Function>, Vector<NonnullRefPtr<Function>>>;
952
+
953
+ // Indexes into m_patch_data.
954
+ struct CoonsPatch {
955
+ u32 control_points[12 ];
956
+ u32 colors[4 ];
957
+ };
958
+
959
+ CoonsPatchShading (CommonEntries common_entries, Vector<float > patch_data, Vector<CoonsPatch> patches, FunctionsType functions)
960
+ : m_common_entries(move(common_entries))
961
+ , m_patch_data(move(patch_data))
962
+ , m_patches(move(patches))
963
+ , m_functions(move(functions))
964
+ {
965
+ }
966
+
967
+ CommonEntries m_common_entries;
968
+
969
+ // Interleaved x0, y0, x1, y1, ..., x11, y11, c0, c1, c2, c3, ...
970
+ // (For flags 1-3, only 8 coordinates and 2 colors.)
971
+ Vector<float > m_patch_data;
972
+ Vector<CoonsPatch> m_patches;
973
+ FunctionsType m_functions;
974
+ };
975
+
976
+ PDFErrorOr<NonnullRefPtr<CoonsPatchShading>> CoonsPatchShading::create (Document* document, NonnullRefPtr<StreamObject> shading_stream, CommonEntries common_entries)
977
+ {
978
+ auto shading_dict = shading_stream->dict ();
979
+
980
+ // TABLE 4.34 Additional entries specific to a type 6 shading dictionary
981
+ // "(Required) The number of bits used to represent each geometric coordi-
982
+ // nate. Valid values are 1, 2, 4, 8, 12, 16, 24, and 32."
983
+ int bits_per_coordinate = TRY (document->resolve (shading_dict->get_value (CommonNames::BitsPerCoordinate))).to_int ();
984
+ if (!first_is_one_of (bits_per_coordinate, 1 , 2 , 4 , 8 , 12 , 16 , 24 , 32 ))
985
+ return Error::malformed_error (" BitsPerCoordinate invalid" );
986
+
987
+ // "(Required) The number of bits used to represent each color component.
988
+ // Valid values are 1, 2, 4, 8, 12, and 16."
989
+ int bits_per_component = TRY (document->resolve (shading_dict->get_value (CommonNames::BitsPerComponent))).to_int ();
990
+ if (!first_is_one_of (bits_per_component, 1 , 2 , 4 , 8 , 12 , 16 ))
991
+ return Error::malformed_error (" BitsPerComponent invalid" );
992
+
993
+ // "(Required) The number of bits used to represent the edge flag for each
994
+ // patch (see below). Valid values of BitsPerFlag are 2, 4, and 8, but only the
995
+ // least significant 2 bits in each flag value are used. Valid values for the edge
996
+ // flag are 0, 1, 2, and 3."
997
+ int bits_per_flag = TRY (document->resolve (shading_dict->get_value (CommonNames::BitsPerFlag))).to_int ();
998
+ if (!first_is_one_of (bits_per_flag, 2 , 4 , 8 ))
999
+ return Error::malformed_error (" BitsPerFlag invalid" );
1000
+
1001
+ // "(Required) An array of numbers specifying how to map vertex coordinates
1002
+ // and color components into the appropriate ranges of values. The decoding
1003
+ // method is similar to that used in image dictionaries (see “Decode Arrays”
1004
+ // on page 344). The ranges are specified as follows:
1005
+ //
1006
+ // [ xmin xmax ymin ymax c1,min c1,max … cn,min cn,max ]
1007
+ //
1008
+ // Note that only one pair of c values should be specified if a Function entry
1009
+ // is present."
1010
+ auto decode_array = TRY (shading_dict->get_array (document, CommonNames::Decode));
1011
+ size_t number_of_components = static_cast <size_t >(shading_dict->contains (CommonNames::Function) ? 1 : common_entries.color_space ->number_of_components ());
1012
+ if (decode_array->size () != 4 + 2 * number_of_components)
1013
+ return Error::malformed_error (" Decode array must have 4 + 2 * number of components elements" );
1014
+ Vector<float > decode;
1015
+ decode.resize (decode_array->size ());
1016
+ for (size_t i = 0 ; i < decode_array->size (); ++i)
1017
+ decode[i] = decode_array->at (i).to_float ();
1018
+
1019
+ // "(Optional) A 1-in, n-out function or an array of n 1-in, 1-out functions
1020
+ // (where n is the number of color components in the shading dictionary’s
1021
+ // color space). If this entry is present, the color data for each vertex must be
1022
+ // specified by a single parametric variable rather than by n separate color
1023
+ // components. The designated function(s) are called with each interpolated
1024
+ // value of the parametric variable to determine the actual color at each
1025
+ // point. Each input value is forced into the range interval specified for the
1026
+ // corresponding color component in the shading dictionary’s Decode array.
1027
+ // Each function’s domain must be a superset of that interval. If the value re-
1028
+ // turned by the function for a given color component is out of range, it is
1029
+ // adjusted to the nearest valid value.
1030
+ // This entry may not be used with an Indexed color space."
1031
+ FunctionsType functions;
1032
+ if (shading_dict->contains (CommonNames::Function)) {
1033
+ if (common_entries.color_space ->family () == ColorSpaceFamily::Indexed)
1034
+ return Error::malformed_error (" Function cannot be used with Indexed color space" );
1035
+
1036
+ functions = TRY ([&]() -> PDFErrorOr<FunctionsType> {
1037
+ auto function_object = TRY (shading_dict->get_object (document, CommonNames::Function));
1038
+ if (function_object->is <ArrayObject>()) {
1039
+ auto function_array = function_object->cast <ArrayObject>();
1040
+ Vector<NonnullRefPtr<Function>> functions_vector;
1041
+ if (function_array->size () != static_cast <size_t >(common_entries.color_space ->number_of_components ()))
1042
+ return Error::malformed_error (" Function array must have as many elements as color space has components" );
1043
+ for (size_t i = 0 ; i < function_array->size (); ++i) {
1044
+ auto function = TRY (Function::create (document, TRY (document->resolve_to <Object>(function_array->at (i)))));
1045
+ if (TRY (function->evaluate (to_array ({ decode[4 ] }))).size () != 1 )
1046
+ return Error::malformed_error (" Function must have 1 output component" );
1047
+ TRY (functions_vector.try_append (move (function)));
1048
+ }
1049
+ return functions_vector;
1050
+ }
1051
+ auto function = TRY (Function::create (document, function_object));
1052
+ if (TRY (function->evaluate (to_array ({ decode[0 ] }))).size () != static_cast <size_t >(common_entries.color_space ->number_of_components ()))
1053
+ return Error::malformed_error (" Function must have as many output components as color space" );
1054
+ return function;
1055
+ }());
1056
+ }
1057
+
1058
+ // See "Type 6 Shadings (Coons Patch Meshes)" in the PDF 1.7 spec for a description of the stream contents.
1059
+ auto stream = FixedMemoryStream { shading_stream->bytes () };
1060
+ BigEndianInputBitStream bitstream { MaybeOwned { stream } };
1061
+
1062
+ Vector<float > patch_data;
1063
+ Vector<CoonsPatch> patches;
1064
+
1065
+ auto read_point = [&]() -> ErrorOr<void > {
1066
+ u32 x = TRY (bitstream.read_bits <u32>(bits_per_coordinate));
1067
+ u32 y = TRY (bitstream.read_bits <u32>(bits_per_coordinate));
1068
+ TRY (patch_data.try_append (mix (decode[0 ], decode[1 ], x / (powf (2 .0f , bits_per_coordinate) - 1 ))));
1069
+ TRY (patch_data.try_append (mix (decode[2 ], decode[3 ], y / (powf (2 .0f , bits_per_coordinate) - 1 ))));
1070
+ return {};
1071
+ };
1072
+
1073
+ auto read_points = [&](u32 n) -> ErrorOr<void > {
1074
+ for (u32 i = 0 ; i < n; ++i)
1075
+ TRY (read_point ());
1076
+ return {};
1077
+ };
1078
+
1079
+ auto read_color = [&]() -> ErrorOr<void > {
1080
+ for (size_t i = 0 ; i < number_of_components; ++i) {
1081
+ u16 color = TRY (bitstream.read_bits <u16>(bits_per_component));
1082
+ TRY (patch_data.try_append (mix (decode[4 + 2 * i], decode[4 + 2 * i + 1 ], color / (powf (2 .0f , bits_per_component) - 1 ))));
1083
+ }
1084
+ return {};
1085
+ };
1086
+
1087
+ auto read_colors = [&](u32 n) -> ErrorOr<void > {
1088
+ for (u32 i = 0 ; i < n; ++i)
1089
+ TRY (read_color ());
1090
+ return {};
1091
+ };
1092
+
1093
+ while (!bitstream.is_eof ()) {
1094
+ u8 flag = TRY (bitstream.read_bits <u8>(bits_per_flag));
1095
+
1096
+ int n = patch_data.size ();
1097
+ CoonsPatch patch;
1098
+
1099
+ // "TABLE 4.35 Data values in a Coons patch mesh"
1100
+ switch (flag) {
1101
+ case 0 :
1102
+ // "x1 y1 x2 y2 x3 y3 x4 y4 x5 y5 x6 y6 x7 y7 x8 y8 x9 y9 x10 y10 x11 y11 x12 y12 c1 c2 c3 c4
1103
+ // New patch; no implicit values"
1104
+ TRY (patch_data.try_ensure_capacity (patch_data.size () + 12 * 2 + 4 + number_of_components));
1105
+ TRY (read_points (12 ));
1106
+ TRY (read_colors (4 ));
1107
+ for (int i = 0 ; i < 12 ; ++i)
1108
+ patch.control_points [i] = n + 2 * i;
1109
+ for (int i = 0 ; i < 4 ; ++i)
1110
+ patch.colors [i] = n + 24 + number_of_components * i;
1111
+ break ;
1112
+ case 1 :
1113
+ if (patches.is_empty ())
1114
+ return Error::malformed_error (" Edge flag 1 without preceding patch" );
1115
+ // "x5 y5 x6 y6 x7 y7 x8 y8 x9 y9 x10 y10 x11 y11 x12 y12 c3 c4
1116
+ // Implicit values:
1117
+ // (x1, y1) = (x4, y4) previous
1118
+ // (x2, y2) = (x5, y5) previous
1119
+ // (x3, y3) = (x6, y6) previous
1120
+ // (x4, y4) = (x7, y7) previous
1121
+ // c1 = c2 previous
1122
+ // c2 = c3 previous"
1123
+ TRY (patch_data.try_ensure_capacity (patch_data.size () + 8 * 2 + 2 + number_of_components));
1124
+ TRY (read_points (8 ));
1125
+ TRY (read_colors (2 ));
1126
+ patch.control_points [0 ] = patches.last ().control_points [3 ];
1127
+ patch.control_points [1 ] = patches.last ().control_points [4 ];
1128
+ patch.control_points [2 ] = patches.last ().control_points [5 ];
1129
+ patch.control_points [3 ] = patches.last ().control_points [6 ];
1130
+ for (int i = 0 ; i < 8 ; ++i)
1131
+ patch.control_points [i + 4 ] = n + 2 * i;
1132
+ patch.colors [0 ] = patches.last ().colors [1 ];
1133
+ patch.colors [1 ] = patches.last ().colors [2 ];
1134
+ for (int i = 0 ; i < 2 ; ++i)
1135
+ patch.colors [i + 2 ] = n + 16 + number_of_components * i;
1136
+ break ;
1137
+ case 2 :
1138
+ if (patches.is_empty ())
1139
+ return Error::malformed_error (" Edge flag 2 without preceding patch" );
1140
+ // "x5 y5 x6 y6 x7 y7 x8 y8 x9 y9 x10 y10 x11 y11 x12 y12 c3 c4
1141
+ // Implicit values:
1142
+ // (x1, y1) = (x7, y7) previous
1143
+ // (x2, y2) = (x8, y8) previous
1144
+ // (x3, y3) = (x9, y9) previous
1145
+ // (x4, y4) = (x10, y10) previous
1146
+ // c1 = c3 previous
1147
+ // c2 = c4 previous"
1148
+ TRY (patch_data.try_ensure_capacity (patch_data.size () + 8 * 2 + 2 + number_of_components));
1149
+ TRY (read_points (8 ));
1150
+ TRY (read_colors (2 ));
1151
+ patch.control_points [0 ] = patches.last ().control_points [6 ];
1152
+ patch.control_points [1 ] = patches.last ().control_points [7 ];
1153
+ patch.control_points [2 ] = patches.last ().control_points [8 ];
1154
+ patch.control_points [3 ] = patches.last ().control_points [9 ];
1155
+ for (int i = 0 ; i < 8 ; ++i)
1156
+ patch.control_points [i + 4 ] = n + 2 * i;
1157
+ patch.colors [0 ] = patches.last ().colors [2 ];
1158
+ patch.colors [1 ] = patches.last ().colors [3 ];
1159
+ for (int i = 0 ; i < 2 ; ++i)
1160
+ patch.colors [i + 2 ] = n + 16 + number_of_components * i;
1161
+ break ;
1162
+ case 3 :
1163
+ if (patches.is_empty ())
1164
+ return Error::malformed_error (" Edge flag 3 without preceding patch" );
1165
+ // "x5 y5 x6 y6 x7 y7 x8 y8 x9 y9 x10 y10 x11 y11 x12 y12 c3 c4
1166
+ // Implicit values:
1167
+ // (x1, y1) = (x10, y10) previous
1168
+ // (x2, y2) = (x11, y11) previous
1169
+ // (x3, y3) = (x12, y12) previous
1170
+ // (x4, y4) = (x1, y1) previous
1171
+ // c1 = c4 previous
1172
+ // c2 = c1 previous"
1173
+ TRY (patch_data.try_ensure_capacity (patch_data.size () + 8 * 2 + 2 + number_of_components));
1174
+ TRY (read_points (8 ));
1175
+ TRY (read_colors (2 ));
1176
+ patch.control_points [0 ] = patches.last ().control_points [9 ];
1177
+ patch.control_points [1 ] = patches.last ().control_points [10 ];
1178
+ patch.control_points [2 ] = patches.last ().control_points [11 ];
1179
+ patch.control_points [3 ] = patches.last ().control_points [0 ];
1180
+ for (int i = 0 ; i < 8 ; ++i)
1181
+ patch.control_points [i + 4 ] = n + 2 * i;
1182
+ patch.colors [0 ] = patches.last ().colors [3 ];
1183
+ patch.colors [1 ] = patches.last ().colors [0 ];
1184
+ for (int i = 0 ; i < 2 ; ++i)
1185
+ patch.colors [i + 2 ] = n + 16 + number_of_components * i;
1186
+ break ;
1187
+ default :
1188
+ return Error::malformed_error (" Invalid edge flag" );
1189
+ }
1190
+
1191
+ TRY (patches.try_append (patch));
1192
+ bitstream.align_to_byte_boundary ();
1193
+ }
1194
+
1195
+ return adopt_ref (*new CoonsPatchShading (move (common_entries), move (patch_data), move (patches), move (functions)));
1196
+ }
1197
+
1198
+ PDFErrorOr<void > CoonsPatchShading::draw (Gfx::Painter&, Gfx::AffineTransform const &)
1199
+ {
1200
+ return Error::rendering_unsupported_error (" Cannot draw coons path mesh shadings yet" );
1201
+ }
1202
+
944
1203
}
945
1204
946
1205
PDFErrorOr<NonnullRefPtr<Shading>> Shading::create (Document* document, NonnullRefPtr<Object> shading_dict_or_stream, Renderer& renderer)
@@ -982,7 +1241,9 @@ PDFErrorOr<NonnullRefPtr<Shading>> Shading::create(Document* document, NonnullRe
982
1241
return Error::malformed_error (" Lattice-form Gouraud-shaded triangle mesh stream has wrong type" );
983
1242
return LatticeFormGouraudShading::create (document, shading_dict_or_stream->cast <StreamObject>(), move (common_entries));
984
1243
case 6 :
985
- return Error::rendering_unsupported_error (" Coons patch mesh not yet implemented" );
1244
+ if (!shading_dict_or_stream->is <StreamObject>())
1245
+ return Error::malformed_error (" Coons patch mesh stream has wrong type" );
1246
+ return CoonsPatchShading::create (document, shading_dict_or_stream->cast <StreamObject>(), move (common_entries));
986
1247
case 7 :
987
1248
return Error::rendering_unsupported_error (" Tensor-product patch mesh not yet implemented" );
988
1249
}
0 commit comments