mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 07:57:47 +00:00
LibGfx: Read embedded ICC profiles in .bmp files
Also add a spec link and slightly more BMP_DEBUG output.
This commit is contained in:
parent
f6503577f6
commit
89a8a198b2
1 changed files with 92 additions and 2 deletions
|
@ -20,6 +20,7 @@ const u8 bmp_header_size = 14;
|
||||||
const u32 color_palette_limit = 1024;
|
const u32 color_palette_limit = 1024;
|
||||||
|
|
||||||
// Compression flags
|
// Compression flags
|
||||||
|
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wmf/4e588f70-bd92-4a6f-b77f-35d0feaf7a57
|
||||||
struct Compression {
|
struct Compression {
|
||||||
enum : u32 {
|
enum : u32 {
|
||||||
RGB = 0,
|
RGB = 0,
|
||||||
|
@ -89,6 +90,33 @@ struct Formatter<Gfx::Endpoint<T>> : Formatter<StringView> {
|
||||||
|
|
||||||
namespace Gfx {
|
namespace Gfx {
|
||||||
|
|
||||||
|
// CALIBRATED_RGB, sRGB, WINDOWS_COLOR_SPACE values are from
|
||||||
|
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wmf/eb4bbd50-b3ce-4917-895c-be31f214797f
|
||||||
|
// PROFILE_LINKED, PROFILE_EMBEDDED values are from
|
||||||
|
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wmf/3c289fe1-c42e-42f6-b125-4b5fc49a2b20
|
||||||
|
struct ColorSpace {
|
||||||
|
enum : u32 {
|
||||||
|
// "This value implies that endpoints and gamma values are given in the appropriate fields" in DIBV4.
|
||||||
|
// The only valid value in v4 bmps.
|
||||||
|
CALIBRATED_RGB = 0,
|
||||||
|
|
||||||
|
// "Specifies that the bitmap is in sRGB color space."
|
||||||
|
sRGB = 0x73524742, // 'sRGB'
|
||||||
|
|
||||||
|
// "This value indicates that the bitmap is in the system default color space, sRGB."
|
||||||
|
WINDOWS_COLOR_SPACE = 0x57696E20, // 'Win '
|
||||||
|
|
||||||
|
// "This value indicates that bV5ProfileData points to the file name of the profile to use
|
||||||
|
// (gamma and endpoints values are ignored)."
|
||||||
|
LINKED = 0x4C494E4B, // 'LINK'
|
||||||
|
|
||||||
|
// "This value indicates that bV5ProfileData points to a memory buffer that contains the profile to be used
|
||||||
|
// (gamma and endpoints values are ignored)."
|
||||||
|
EMBEDDED = 0x4D424544, // 'MBED'
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapv4header
|
||||||
struct DIBV4 {
|
struct DIBV4 {
|
||||||
u32 color_space { 0 };
|
u32 color_space { 0 };
|
||||||
Endpoint<i32> red_endpoint { 0, 0, 0 };
|
Endpoint<i32> red_endpoint { 0, 0, 0 };
|
||||||
|
@ -97,6 +125,44 @@ struct DIBV4 {
|
||||||
Endpoint<u32> gamma_endpoint { 0, 0, 0 };
|
Endpoint<u32> gamma_endpoint { 0, 0, 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wmf/9fec0834-607d-427d-abd5-ab240fb0db38
|
||||||
|
struct GamutMappingIntent {
|
||||||
|
enum : u32 {
|
||||||
|
// "Specifies that the white point SHOULD be maintained.
|
||||||
|
// Typically used when logical colors MUST be matched to their nearest physical color in the destination color gamut.
|
||||||
|
//
|
||||||
|
// Intent: Match
|
||||||
|
//
|
||||||
|
// ICC name: Absolute Colorimetric"
|
||||||
|
ABS_COLORIMETRIC = 8,
|
||||||
|
|
||||||
|
// "Specifies that saturation SHOULD be maintained.
|
||||||
|
// Typically used for business charts and other situations in which dithering is not required.
|
||||||
|
//
|
||||||
|
// Intent: Graphic
|
||||||
|
//
|
||||||
|
// ICC name: Saturation"
|
||||||
|
BUSINESS = 1,
|
||||||
|
|
||||||
|
// "Specifies that a colorimetric match SHOULD be maintained.
|
||||||
|
// Typically used for graphic designs and named colors.
|
||||||
|
//
|
||||||
|
// Intent: Proof
|
||||||
|
//
|
||||||
|
// ICC name: Relative Colorimetric"
|
||||||
|
GRAPHICS = 2,
|
||||||
|
|
||||||
|
// "Specifies that contrast SHOULD be maintained.
|
||||||
|
// Typically used for photographs and natural images.
|
||||||
|
//
|
||||||
|
// Intent: Picture
|
||||||
|
//
|
||||||
|
// ICC name: Perceptual"
|
||||||
|
IMAGES = 4,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapv5header
|
||||||
struct DIBV5 {
|
struct DIBV5 {
|
||||||
u32 intent { 0 };
|
u32 intent { 0 };
|
||||||
u32 profile_data { 0 };
|
u32 profile_data { 0 };
|
||||||
|
@ -119,7 +185,7 @@ enum class DIBType {
|
||||||
V2,
|
V2,
|
||||||
V3,
|
V3,
|
||||||
V4,
|
V4,
|
||||||
V5
|
V5,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BMPLoadingContext {
|
struct BMPLoadingContext {
|
||||||
|
@ -696,9 +762,12 @@ static bool decode_bmp_v3_dib(BMPLoadingContext& context, InputStreamer& streame
|
||||||
if ((context.dib.core.bpp == 32 && mask != 0) || context.dib.core.bpp == 16) {
|
if ((context.dib.core.bpp == 32 && mask != 0) || context.dib.core.bpp == 16) {
|
||||||
context.dib.info.masks.append(mask);
|
context.dib.info.masks.append(mask);
|
||||||
dbgln_if(BMP_DEBUG, "BMP alpha mask: {:#08x}", mask);
|
dbgln_if(BMP_DEBUG, "BMP alpha mask: {:#08x}", mask);
|
||||||
|
} else {
|
||||||
|
dbgln_if(BMP_DEBUG, "BMP alpha mask (ignored): {:#08x}", mask);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
streamer.drop_bytes(4);
|
streamer.drop_bytes(4);
|
||||||
|
dbgln_if(BMP_DEBUG, "BMP alpha mask skipped");
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -1494,7 +1563,28 @@ ErrorOr<ImageFrameDescriptor> BMPImageDecoderPlugin::frame(size_t index)
|
||||||
|
|
||||||
ErrorOr<Optional<ReadonlyBytes>> BMPImageDecoderPlugin::icc_data()
|
ErrorOr<Optional<ReadonlyBytes>> BMPImageDecoderPlugin::icc_data()
|
||||||
{
|
{
|
||||||
return OptionalNone {};
|
TRY(decode_bmp_dib(*m_context));
|
||||||
|
|
||||||
|
if (m_context->dib_type != DIBType::V5)
|
||||||
|
return OptionalNone {};
|
||||||
|
|
||||||
|
// FIXME: For LINKED, return data from the linked file?
|
||||||
|
// FIXME: For sRGB and WINDOWS_COLOR_SPACE, return an sRGB profile somehow.
|
||||||
|
// FIXME: For CALIBRATED_RGB, do something with v4.{red_endpoint,green_endpoint,blue_endpoint,gamma_endpoint}
|
||||||
|
if (m_context->dib.v4.color_space != ColorSpace::EMBEDDED)
|
||||||
|
return OptionalNone {};
|
||||||
|
|
||||||
|
auto const& v5 = m_context->dib.v5;
|
||||||
|
if (!v5.profile_data || !v5.profile_size)
|
||||||
|
return OptionalNone {};
|
||||||
|
|
||||||
|
// FIXME: Do something with v5.intent (which has a GamutMappingIntent value).
|
||||||
|
|
||||||
|
u8 header_size = m_context->is_included_in_ico ? 0 : bmp_header_size;
|
||||||
|
if (v5.profile_data + header_size + v5.profile_size > m_context->file_size)
|
||||||
|
return Error::from_string_literal("BMPImageDecoderPlugin: ICC profile data out of bounds");
|
||||||
|
|
||||||
|
return ReadonlyBytes { m_context->file_bytes + header_size + v5.profile_data, v5.profile_size };
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue