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))); | ||||
| } | ||||
| 
 | ||||
| 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) | ||||
| { | ||||
|     // 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); | ||||
|     case MultiLocalizedUnicodeTagData::Type: | ||||
|         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: | ||||
|         return S15Fixed16ArrayTagData::from_bytes(tag_bytes, entry.offset_to_beginning_of_tag_data_element, entry.size_of_tag_data_element); | ||||
|     case TextDescriptionTagData::Type: | ||||
|  |  | |||
|  | @ -224,6 +224,8 @@ private: | |||
|     u64 m_bits = 0; | ||||
| }; | ||||
| 
 | ||||
| using S15Fixed16 = FixedPoint<16, i32>; | ||||
| 
 | ||||
| struct XYZ { | ||||
|     double x { 0 }; | ||||
|     double y { 0 }; | ||||
|  | @ -313,13 +315,100 @@ private: | |||
|     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
 | ||||
| class S15Fixed16ArrayTagData : public TagData { | ||||
| public: | ||||
|     static constexpr TagTypeSignature Type { 0x73663332 }; // 'sf32'
 | ||||
| 
 | ||||
|     using S15Fixed16 = FixedPoint<16, i32>; | ||||
| 
 | ||||
|     static ErrorOr<NonnullRefPtr<S15Fixed16ArrayTagData>> from_bytes(ReadonlyBytes, u32 offset, u32 size); | ||||
| 
 | ||||
|     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.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) { | ||||
|             // 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
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Nico Weber
						Nico Weber