mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 09:02:43 +00:00 
			
		
		
		
	LibGfx+icc: Add ICCProfile support for parametricCurveType and print it
With this, we can parse all types required in v4 "Three-component matrix-based Input profiles".
This commit is contained in:
		
							parent
							
								
									67f718aa3f
								
							
						
					
					
						commit
						fa00d94e82
					
				
					 3 changed files with 172 additions and 2 deletions
				
			
		|  | @ -635,6 +635,58 @@ ErrorOr<NonnullRefPtr<MultiLocalizedUnicodeTagData>> MultiLocalizedUnicodeTagDat | ||||||
|     return adopt_ref(*new MultiLocalizedUnicodeTagData(offset, size, move(records))); |     return adopt_ref(*new MultiLocalizedUnicodeTagData(offset, size, move(records))); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | unsigned ParametricCurveTagData::parameter_count(FunctionType function_type) | ||||||
|  | { | ||||||
|  |     switch (function_type) { | ||||||
|  |     case FunctionType::Type0: | ||||||
|  |         return 1; | ||||||
|  |     case FunctionType::Type1: | ||||||
|  |         return 3; | ||||||
|  |     case FunctionType::Type2: | ||||||
|  |         return 4; | ||||||
|  |     case FunctionType::Type3: | ||||||
|  |         return 5; | ||||||
|  |     case FunctionType::Type4: | ||||||
|  |         return 7; | ||||||
|  |     } | ||||||
|  |     VERIFY_NOT_REACHED(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ErrorOr<NonnullRefPtr<ParametricCurveTagData>> ParametricCurveTagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size) | ||||||
|  | { | ||||||
|  |     // ICC v4, 10.18 parametricCurveType
 | ||||||
|  |     VERIFY(tag_type(bytes) == Type); | ||||||
|  |     TRY(check_reserved(bytes)); | ||||||
|  | 
 | ||||||
|  |     // "The parametricCurveType describes a one-dimensional curve by specifying one of a predefined set of functions
 | ||||||
|  |     //  using the parameters."
 | ||||||
|  | 
 | ||||||
|  |     if (bytes.size() < 2 * sizeof(u32) + 2 * sizeof(u16)) | ||||||
|  |         return Error::from_string_literal("ICC::Profile: parametricCurveType has not enough data"); | ||||||
|  | 
 | ||||||
|  |     u16 raw_function_type = *bit_cast<BigEndian<u16> const*>(bytes.data() + 8); | ||||||
|  |     u16 reserved = *bit_cast<BigEndian<u16> const*>(bytes.data() + 10); | ||||||
|  |     if (reserved != 0) | ||||||
|  |         return Error::from_string_literal("ICC::Profile: parametricCurveType reserved u16 after function type not 0"); | ||||||
|  | 
 | ||||||
|  |     if (raw_function_type > 4) | ||||||
|  |         return Error::from_string_literal("ICC::Profile: parametricCurveType unknown function type"); | ||||||
|  | 
 | ||||||
|  |     FunctionType function_type = (FunctionType)raw_function_type; | ||||||
|  |     unsigned count = parameter_count(function_type); | ||||||
|  | 
 | ||||||
|  |     if (bytes.size() < 2 * sizeof(u32) + 2 * sizeof(u16) + count * sizeof(s15Fixed16Number)) | ||||||
|  |         return Error::from_string_literal("ICC::Profile: parametricCurveType has not enough data for parameters"); | ||||||
|  | 
 | ||||||
|  |     BigEndian<s15Fixed16Number> const* raw_parameters = bit_cast<BigEndian<s15Fixed16Number> const*>(bytes.data() + 12); | ||||||
|  |     Array<S15Fixed16, 7> parameters; | ||||||
|  |     parameters.fill(0); | ||||||
|  |     for (unsigned i = 0; i < count; ++i) | ||||||
|  |         parameters[i] = S15Fixed16::create_raw(raw_parameters[i]); | ||||||
|  | 
 | ||||||
|  |     return adopt_ref(*new ParametricCurveTagData(offset, size, function_type, move(parameters))); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| ErrorOr<NonnullRefPtr<S15Fixed16ArrayTagData>> S15Fixed16ArrayTagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size) | ErrorOr<NonnullRefPtr<S15Fixed16ArrayTagData>> S15Fixed16ArrayTagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size) | ||||||
| { | { | ||||||
|     // ICC v4, 10.22 s15Fixed16ArrayType
 |     // ICC v4, 10.22 s15Fixed16ArrayType
 | ||||||
|  | @ -897,6 +949,8 @@ ErrorOr<NonnullRefPtr<TagData>> Profile::read_tag(ReadonlyBytes bytes, Detail::T | ||||||
|         return CurveTagData::from_bytes(tag_bytes, entry.offset_to_beginning_of_tag_data_element, entry.size_of_tag_data_element); |         return CurveTagData::from_bytes(tag_bytes, entry.offset_to_beginning_of_tag_data_element, entry.size_of_tag_data_element); | ||||||
|     case MultiLocalizedUnicodeTagData::Type: |     case MultiLocalizedUnicodeTagData::Type: | ||||||
|         return MultiLocalizedUnicodeTagData::from_bytes(tag_bytes, entry.offset_to_beginning_of_tag_data_element, entry.size_of_tag_data_element); |         return MultiLocalizedUnicodeTagData::from_bytes(tag_bytes, entry.offset_to_beginning_of_tag_data_element, entry.size_of_tag_data_element); | ||||||
|  |     case ParametricCurveTagData::Type: | ||||||
|  |         return ParametricCurveTagData::from_bytes(tag_bytes, entry.offset_to_beginning_of_tag_data_element, entry.size_of_tag_data_element); | ||||||
|     case S15Fixed16ArrayTagData::Type: |     case S15Fixed16ArrayTagData::Type: | ||||||
|         return S15Fixed16ArrayTagData::from_bytes(tag_bytes, entry.offset_to_beginning_of_tag_data_element, entry.size_of_tag_data_element); |         return S15Fixed16ArrayTagData::from_bytes(tag_bytes, entry.offset_to_beginning_of_tag_data_element, entry.size_of_tag_data_element); | ||||||
|     case TextDescriptionTagData::Type: |     case TextDescriptionTagData::Type: | ||||||
|  |  | ||||||
|  | @ -224,6 +224,8 @@ private: | ||||||
|     u64 m_bits = 0; |     u64 m_bits = 0; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | using S15Fixed16 = FixedPoint<16, i32>; | ||||||
|  | 
 | ||||||
| struct XYZ { | struct XYZ { | ||||||
|     double x { 0 }; |     double x { 0 }; | ||||||
|     double y { 0 }; |     double y { 0 }; | ||||||
|  | @ -313,13 +315,100 @@ private: | ||||||
|     Vector<Record> m_records; |     Vector<Record> m_records; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | // ICC v4, 10.18 parametricCurveType
 | ||||||
|  | class ParametricCurveTagData : public TagData { | ||||||
|  | public: | ||||||
|  |     // Table 68 — parametricCurveType function type encoding
 | ||||||
|  |     enum class FunctionType { | ||||||
|  |         // Y = X**g
 | ||||||
|  |         Type0, | ||||||
|  | 
 | ||||||
|  |         // Y = (a*X + b)**g       if X >= -b/a
 | ||||||
|  |         //   = 0                  else
 | ||||||
|  |         Type1, | ||||||
|  |         CIE_122_1966 = Type1, | ||||||
|  | 
 | ||||||
|  |         // Y = (a*X + b)**g + c   if X >= -b/a
 | ||||||
|  |         //   = c                  else
 | ||||||
|  |         Type2, | ||||||
|  |         IEC_61966_1 = Type2, | ||||||
|  | 
 | ||||||
|  |         // Y = (a*X + b)**g       if X >= d
 | ||||||
|  |         //   =  c*X               else
 | ||||||
|  |         Type3, | ||||||
|  |         IEC_61966_2_1 = Type3, | ||||||
|  |         sRGB = Type3, | ||||||
|  | 
 | ||||||
|  |         // Y = (a*X + b)**g + e   if X >= d
 | ||||||
|  |         //   =  c*X + f           else
 | ||||||
|  |         Type4, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     // "The domain and range of each function shall be [0,0 1,0]. Any function value outside the range shall be clipped
 | ||||||
|  |     //  to the range of the function."
 | ||||||
|  |     // "NOTE 1 The parameters selected for a parametric curve can result in complex or undefined values for the input range
 | ||||||
|  |     //  used. This can occur, for example, if d < -b/a. In such cases the behaviour of the curve is undefined."
 | ||||||
|  | 
 | ||||||
|  |     static constexpr TagTypeSignature Type { 0x70617261 }; // 'para'
 | ||||||
|  | 
 | ||||||
|  |     static ErrorOr<NonnullRefPtr<ParametricCurveTagData>> from_bytes(ReadonlyBytes, u32 offset, u32 size); | ||||||
|  | 
 | ||||||
|  |     ParametricCurveTagData(u32 offset, u32 size, FunctionType function_type, Array<S15Fixed16, 7> parameters) | ||||||
|  |         : TagData(offset, size, Type) | ||||||
|  |         , m_function_type(function_type) | ||||||
|  |         , m_parameters(move(parameters)) | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     FunctionType function_type() const { return m_function_type; } | ||||||
|  | 
 | ||||||
|  |     static unsigned parameter_count(FunctionType); | ||||||
|  | 
 | ||||||
|  |     S15Fixed16 g() const { return m_parameters[0]; } | ||||||
|  |     S15Fixed16 a() const | ||||||
|  |     { | ||||||
|  |         VERIFY(function_type() >= FunctionType::Type1); | ||||||
|  |         return m_parameters[1]; | ||||||
|  |     } | ||||||
|  |     S15Fixed16 b() const | ||||||
|  |     { | ||||||
|  |         VERIFY(function_type() >= FunctionType::Type1); | ||||||
|  |         return m_parameters[2]; | ||||||
|  |     } | ||||||
|  |     S15Fixed16 c() const | ||||||
|  |     { | ||||||
|  |         VERIFY(function_type() >= FunctionType::Type2); | ||||||
|  |         return m_parameters[3]; | ||||||
|  |     } | ||||||
|  |     S15Fixed16 d() const | ||||||
|  |     { | ||||||
|  |         VERIFY(function_type() >= FunctionType::Type3); | ||||||
|  |         return m_parameters[4]; | ||||||
|  |     } | ||||||
|  |     S15Fixed16 e() const | ||||||
|  |     { | ||||||
|  |         VERIFY(function_type() >= FunctionType::Type4); | ||||||
|  |         return m_parameters[5]; | ||||||
|  |     } | ||||||
|  |     S15Fixed16 f() const | ||||||
|  |     { | ||||||
|  |         VERIFY(function_type() >= FunctionType::Type4); | ||||||
|  |         return m_parameters[6]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     FunctionType m_function_type; | ||||||
|  | 
 | ||||||
|  |     // Contains, in this order, g a b c d e f.
 | ||||||
|  |     // Not all FunctionTypes use all parameters.
 | ||||||
|  |     Array<S15Fixed16, 7> m_parameters; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| // ICC v4, 10.22 s15Fixed16ArrayType
 | // ICC v4, 10.22 s15Fixed16ArrayType
 | ||||||
| class S15Fixed16ArrayTagData : public TagData { | class S15Fixed16ArrayTagData : public TagData { | ||||||
| public: | public: | ||||||
|     static constexpr TagTypeSignature Type { 0x73663332 }; // 'sf32'
 |     static constexpr TagTypeSignature Type { 0x73663332 }; // 'sf32'
 | ||||||
| 
 | 
 | ||||||
|     using S15Fixed16 = FixedPoint<16, i32>; |  | ||||||
| 
 |  | ||||||
|     static ErrorOr<NonnullRefPtr<S15Fixed16ArrayTagData>> from_bytes(ReadonlyBytes, u32 offset, u32 size); |     static ErrorOr<NonnullRefPtr<S15Fixed16ArrayTagData>> from_bytes(ReadonlyBytes, u32 offset, u32 size); | ||||||
| 
 | 
 | ||||||
|     S15Fixed16ArrayTagData(u32 offset, u32 size, Vector<S15Fixed16, 9> values) |     S15Fixed16ArrayTagData(u32 offset, u32 size, Vector<S15Fixed16, 9> values) | ||||||
|  |  | ||||||
|  | @ -112,6 +112,33 @@ ErrorOr<int> serenity_main(Main::Arguments arguments) | ||||||
|                     record.iso_3166_1_country_code >> 8, record.iso_3166_1_country_code & 0xff, |                     record.iso_3166_1_country_code >> 8, record.iso_3166_1_country_code & 0xff, | ||||||
|                     record.text); |                     record.text); | ||||||
|             } |             } | ||||||
|  |         } else if (tag_data->type() == Gfx::ICC::ParametricCurveTagData::Type) { | ||||||
|  |             auto& parametric_curve = static_cast<Gfx::ICC::ParametricCurveTagData&>(*tag_data); | ||||||
|  |             switch (parametric_curve.function_type()) { | ||||||
|  |             case Gfx::ICC::ParametricCurveTagData::FunctionType::Type0: | ||||||
|  |                 outln("  Y = X**{}", parametric_curve.g()); | ||||||
|  |                 break; | ||||||
|  |             case Gfx::ICC::ParametricCurveTagData::FunctionType::Type1: | ||||||
|  |                 outln("  Y = ({}*X + {})**{}   if X >= -{}/{}", | ||||||
|  |                     parametric_curve.a(), parametric_curve.b(), parametric_curve.g(), parametric_curve.b(), parametric_curve.a()); | ||||||
|  |                 outln("  Y = 0                                else"); | ||||||
|  |                 break; | ||||||
|  |             case Gfx::ICC::ParametricCurveTagData::FunctionType::Type2: | ||||||
|  |                 outln("  Y = ({}*X + {})**{} + {}   if X >= -{}/{}", | ||||||
|  |                     parametric_curve.a(), parametric_curve.b(), parametric_curve.g(), parametric_curve.c(), parametric_curve.b(), parametric_curve.a()); | ||||||
|  |                 outln("  Y =  {}                                    else", parametric_curve.c()); | ||||||
|  |                 break; | ||||||
|  |             case Gfx::ICC::ParametricCurveTagData::FunctionType::Type3: | ||||||
|  |                 outln("  Y = ({}*X + {})**{}   if X >= {}", | ||||||
|  |                     parametric_curve.a(), parametric_curve.b(), parametric_curve.g(), parametric_curve.d()); | ||||||
|  |                 outln("  Y =  {}*X                        else", parametric_curve.c()); | ||||||
|  |                 break; | ||||||
|  |             case Gfx::ICC::ParametricCurveTagData::FunctionType::Type4: | ||||||
|  |                 outln("  Y = ({}*X + {})**{} + {}   if X >= {}", | ||||||
|  |                     parametric_curve.a(), parametric_curve.b(), parametric_curve.g(), parametric_curve.e(), parametric_curve.d()); | ||||||
|  |                 outln("  Y =  {}*X + {}                             else", parametric_curve.c(), parametric_curve.f()); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|         } else if (tag_data->type() == Gfx::ICC::S15Fixed16ArrayTagData::Type) { |         } else if (tag_data->type() == Gfx::ICC::S15Fixed16ArrayTagData::Type) { | ||||||
|             // This tag can contain arbitrarily many fixed-point numbers, but in practice it's
 |             // This tag can contain arbitrarily many fixed-point numbers, but in practice it's
 | ||||||
|             // exclusively used for the 'chad' tag, where it always contains 9 values that
 |             // exclusively used for the 'chad' tag, where it always contains 9 values that
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Nico Weber
						Nico Weber