Skip to content

Commit 32e4e04

Browse files
committed
LibPDF: Read coons patch mesh shading dict entries
We now create an CoonsPatchShading object and read all required parameters and build objects for them, but we don't use them for any rendering yet.
1 parent 1d10f1a commit 32e4e04

File tree

1 file changed

+262
-1
lines changed

1 file changed

+262
-1
lines changed

Userland/Libraries/LibPDF/Shading.cpp

+262-1
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,265 @@ PDFErrorOr<void> LatticeFormGouraudShading::draw(Gfx::Painter&, Gfx::AffineTrans
941941
return Error::rendering_unsupported_error("Cannot draw lattice-form gouraud-shaded triangle meshes yet");
942942
}
943943

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+
9441203
}
9451204

9461205
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
9821241
return Error::malformed_error("Lattice-form Gouraud-shaded triangle mesh stream has wrong type");
9831242
return LatticeFormGouraudShading::create(document, shading_dict_or_stream->cast<StreamObject>(), move(common_entries));
9841243
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));
9861247
case 7:
9871248
return Error::rendering_unsupported_error("Tensor-product patch mesh not yet implemented");
9881249
}

0 commit comments

Comments
 (0)