1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 02:17:34 +00:00

LibGfx: Add a WebPLoadingContext::error() helper

No behavior change.

(Well, technically, this now correctly sets the state to Error
if the first chunk is neither of 'VP8 ', 'VP8L', 'VP8X'. But no
*interesting* behavior change.)
This commit is contained in:
Nico Weber 2023-02-24 14:12:19 -05:00 committed by Linus Groh
parent 76b71fcb75
commit 65d41f8ed9

View file

@ -68,6 +68,13 @@ struct WebPLoadingContext {
RefPtr<Gfx::Bitmap> bitmap; RefPtr<Gfx::Bitmap> bitmap;
Optional<ReadonlyBytes> icc_data; Optional<ReadonlyBytes> icc_data;
template<size_t N>
[[nodiscard]] class Error error(char const (&string_literal)[N])
{
state = WebPLoadingContext::State::Error;
return Error::from_string_literal(string_literal);
}
}; };
// https://developers.google.com/speed/webp/docs/riff_container#webp_file_header // https://developers.google.com/speed/webp/docs/riff_container#webp_file_header
@ -76,31 +83,23 @@ static ErrorOr<void> decode_webp_header(WebPLoadingContext& context)
if (context.state >= WebPLoadingContext::HeaderDecoded) if (context.state >= WebPLoadingContext::HeaderDecoded)
return {}; return {};
if (context.data.size() < sizeof(WebPFileHeader)) { if (context.data.size() < sizeof(WebPFileHeader))
context.state = WebPLoadingContext::State::Error; return context.error("Missing WebP header");
return Error::from_string_literal("Missing WebP header");
}
auto& header = *bit_cast<WebPFileHeader const*>(context.data.data()); auto& header = *bit_cast<WebPFileHeader const*>(context.data.data());
if (header.riff != FourCC("RIFF") || header.webp != FourCC("WEBP")) { if (header.riff != FourCC("RIFF") || header.webp != FourCC("WEBP"))
context.state = WebPLoadingContext::State::Error; return context.error("Invalid WebP header");
return Error::from_string_literal("Invalid WebP header");
}
// "File Size: [...] The size of the file in bytes starting at offset 8. The maximum value of this field is 2^32 minus 10 bytes." // "File Size: [...] The size of the file in bytes starting at offset 8. The maximum value of this field is 2^32 minus 10 bytes."
u32 const maximum_webp_file_size = 0xffff'ffff - 9; u32 const maximum_webp_file_size = 0xffff'ffff - 9;
if (header.file_size > maximum_webp_file_size) { if (header.file_size > maximum_webp_file_size)
context.state = WebPLoadingContext::State::Error; return context.error("WebP header file size over maximum");
return Error::from_string_literal("WebP header file size over maximum");
}
// "The file size in the header is the total size of the chunks that follow plus 4 bytes for the 'WEBP' FourCC. // "The file size in the header is the total size of the chunks that follow plus 4 bytes for the 'WEBP' FourCC.
// The file SHOULD NOT contain any data after the data specified by File Size. // The file SHOULD NOT contain any data after the data specified by File Size.
// Readers MAY parse such files, ignoring the trailing data." // Readers MAY parse such files, ignoring the trailing data."
if (context.data.size() - 8 < header.file_size) { if (context.data.size() - 8 < header.file_size)
context.state = WebPLoadingContext::State::Error; return context.error("WebP data too small for size in header");
return Error::from_string_literal("WebP data too small for size in header");
}
if (context.data.size() - 8 > header.file_size) { if (context.data.size() - 8 > header.file_size) {
dbgln_if(WEBP_DEBUG, "WebP has {} bytes of data, but header needs only {}. Trimming.", context.data.size(), header.file_size + 8); dbgln_if(WEBP_DEBUG, "WebP has {} bytes of data, but header needs only {}. Trimming.", context.data.size(), header.file_size + 8);
context.data = context.data.trim(header.file_size + 8); context.data = context.data.trim(header.file_size + 8);
@ -113,18 +112,14 @@ static ErrorOr<void> decode_webp_header(WebPLoadingContext& context)
// https://developers.google.com/speed/webp/docs/riff_container#riff_file_format // https://developers.google.com/speed/webp/docs/riff_container#riff_file_format
static ErrorOr<Chunk> decode_webp_chunk_header(WebPLoadingContext& context, ReadonlyBytes chunks) static ErrorOr<Chunk> decode_webp_chunk_header(WebPLoadingContext& context, ReadonlyBytes chunks)
{ {
if (chunks.size() < sizeof(ChunkHeader)) { if (chunks.size() < sizeof(ChunkHeader))
context.state = WebPLoadingContext::State::Error; return context.error("Not enough data for WebP chunk header");
return Error::from_string_literal("Not enough data for WebP chunk header");
}
auto const& header = *bit_cast<ChunkHeader const*>(chunks.data()); auto const& header = *bit_cast<ChunkHeader const*>(chunks.data());
dbgln_if(WEBP_DEBUG, "chunk {} size {}", header.chunk_type, header.chunk_size); dbgln_if(WEBP_DEBUG, "chunk {} size {}", header.chunk_type, header.chunk_size);
if (chunks.size() < sizeof(ChunkHeader) + header.chunk_size) { if (chunks.size() < sizeof(ChunkHeader) + header.chunk_size)
context.state = WebPLoadingContext::State::Error; return context.error("Not enough data for WebP chunk");
return Error::from_string_literal("Not enough data for WebP chunk");
}
return Chunk { header.chunk_type, { chunks.data() + sizeof(ChunkHeader), header.chunk_size } }; return Chunk { header.chunk_type, { chunks.data() + sizeof(ChunkHeader), header.chunk_size } };
} }
@ -141,14 +136,10 @@ static ErrorOr<Chunk> decode_webp_advance_chunk(WebPLoadingContext& context, Rea
chunks = chunks.slice(sizeof(ChunkHeader) + chunk.data.size()); chunks = chunks.slice(sizeof(ChunkHeader) + chunk.data.size());
if (chunk.data.size() % 2 != 0) { if (chunk.data.size() % 2 != 0) {
if (chunks.is_empty()) { if (chunks.is_empty())
context.state = WebPLoadingContext::State::Error; return context.error("Missing data for padding byte");
return Error::from_string_literal("Missing data for padding byte"); if (*chunks.data() != 0)
} return context.error("Padding byte is not 0");
if (*chunks.data() != 0) {
context.state = WebPLoadingContext::State::Error;
return Error::from_string_literal("Padding byte is not 0");
}
chunks = chunks.slice(1); chunks = chunks.slice(1);
} }
@ -225,7 +216,7 @@ static ErrorOr<void> decode_webp_chunks(WebPLoadingContext& context)
if (first_chunk.type == FourCC("VP8X")) if (first_chunk.type == FourCC("VP8X"))
return decode_webp_extended(context, first_chunk, chunks); return decode_webp_extended(context, first_chunk, chunks);
return Error::from_string_literal("WebPImageDecoderPlugin: Invalid first chunk type"); return context.error("WebPImageDecoderPlugin: Invalid first chunk type");
} }
WebPImageDecoderPlugin::WebPImageDecoderPlugin(ReadonlyBytes data, OwnPtr<WebPLoadingContext> context) WebPImageDecoderPlugin::WebPImageDecoderPlugin(ReadonlyBytes data, OwnPtr<WebPLoadingContext> context)