diff options
author | ericLemanissier <eric.lemanissier@gmail.com> | 2022-12-22 07:59:56 +0100 |
---|---|---|
committer | Tim Flynn <trflynn89@pm.me> | 2022-12-23 16:48:30 -0500 |
commit | a6d710612fb72eaa251f6f63a2e7db37ec3cfe35 (patch) | |
tree | 59e204f6c8c12c5970ea6d198a61b8912cdbab6c /Userland | |
parent | 8455d58a4453f785252edc75e70b957d3c87186f (diff) | |
download | serenity-a6d710612fb72eaa251f6f63a2e7db37ec3cfe35.zip |
LibGfx: BMPLoader: Propagate errors properly
Use our normal error propagation mechanism instead of returning booleans
Diffstat (limited to 'Userland')
-rw-r--r-- | Userland/Libraries/LibGfx/BMPLoader.cpp | 201 |
1 files changed, 91 insertions, 110 deletions
diff --git a/Userland/Libraries/LibGfx/BMPLoader.cpp b/Userland/Libraries/LibGfx/BMPLoader.cpp index 3c6f8f26ef..ece02245ed 100644 --- a/Userland/Libraries/LibGfx/BMPLoader.cpp +++ b/Userland/Libraries/LibGfx/BMPLoader.cpp @@ -8,7 +8,9 @@ #include <AK/BuiltinWrappers.h> #include <AK/Debug.h> #include <AK/DeprecatedString.h> +#include <AK/Error.h> #include <AK/Function.h> +#include <AK/Try.h> #include <AK/Vector.h> #include <LibGfx/BMPLoader.h> @@ -425,18 +427,18 @@ static bool set_dib_bitmasks(BMPLoadingContext& context, InputStreamer& streamer return true; } -static bool decode_bmp_header(BMPLoadingContext& context) +static ErrorOr<void> decode_bmp_header(BMPLoadingContext& context) { if (context.state == BMPLoadingContext::State::Error) - return false; + return Error::from_string_literal("Error before starting decode_bmp_header"); if (context.state >= BMPLoadingContext::State::HeaderDecoded) - return true; + return {}; if (!context.file_bytes || context.file_size < bmp_header_size) { dbgln_if(BMP_DEBUG, "Missing BMP header"); context.state = BMPLoadingContext::State::Error; - return false; + return Error::from_string_literal("Missing BMP header"); } InputStreamer streamer(context.file_bytes, bmp_header_size); @@ -445,7 +447,7 @@ static bool decode_bmp_header(BMPLoadingContext& context) if (header != 0x4d42) { dbgln_if(BMP_DEBUG, "BMP has invalid magic header number: {:#04x}", header); context.state = BMPLoadingContext::State::Error; - return false; + return Error::from_string_literal("BMP has invalid magic header number"); } // The reported size of the file in the header is actually not important @@ -466,11 +468,11 @@ static bool decode_bmp_header(BMPLoadingContext& context) if (context.data_offset >= context.file_size) { dbgln_if(BMP_DEBUG, "BMP data offset is beyond file end?!"); - return false; + return Error::from_string_literal("BMP data offset is beyond file end"); } context.state = BMPLoadingContext::State::HeaderDecoded; - return true; + return {}; } static bool decode_bmp_core_dib(BMPLoadingContext& context, InputStreamer& streamer) @@ -744,35 +746,35 @@ static bool decode_bmp_v5_dib(BMPLoadingContext& context, InputStreamer& streame return true; } -static bool decode_bmp_dib(BMPLoadingContext& context) +static ErrorOr<void> decode_bmp_dib(BMPLoadingContext& context) { if (context.state == BMPLoadingContext::State::Error) - return false; + return Error::from_string_literal("Error before starting decode_bmp_dib"); if (context.state >= BMPLoadingContext::State::DIBDecoded) - return true; + return {}; - if (!context.is_included_in_ico && context.state < BMPLoadingContext::State::HeaderDecoded && !decode_bmp_header(context)) - return false; + if (!context.is_included_in_ico && context.state < BMPLoadingContext::State::HeaderDecoded) + TRY(decode_bmp_header(context)); u8 header_size = context.is_included_in_ico ? 0 : bmp_header_size; if (!context.is_included_in_ico && context.file_size < (u8)(header_size + 4)) - return false; + return Error::from_string_literal("File size too short"); if (context.is_included_in_ico && context.file_size < 4) - return false; + return Error::from_string_literal("File size too short"); InputStreamer streamer(context.file_bytes + (context.is_included_in_ico ? 0 : header_size), 4); u32 dib_size = streamer.read_u32(); if (context.file_size < header_size + dib_size) - return false; + return Error::from_string_literal("File size too short"); if (!context.is_included_in_ico && (context.data_offset < header_size + dib_size)) { dbgln("Shenanigans! BMP pixel data and header usually don't overlap."); - return false; + return Error::from_string_literal("BMP pixel data and header usually don't overlap"); } // NOTE: If this is a headless BMP (embedded on ICO files), then we can only infer the data_offset after we know the data table size. @@ -843,7 +845,7 @@ static bool decode_bmp_dib(BMPLoadingContext& context) if (error) { dbgln("BMP has an invalid DIB"); context.state = BMPLoadingContext::State::Error; - return false; + return Error::from_string_literal("BMP has an invalid DIB"); } // NOTE: If this is a headless BMP (included on ICOns), the data_offset is set based on the number_of_palette_colors found on the DIB header @@ -860,23 +862,23 @@ static bool decode_bmp_dib(BMPLoadingContext& context) context.state = BMPLoadingContext::State::DIBDecoded; - return true; + return {}; } -static bool decode_bmp_color_table(BMPLoadingContext& context) +static ErrorOr<void> decode_bmp_color_table(BMPLoadingContext& context) { if (context.state == BMPLoadingContext::State::Error) - return false; + return Error::from_string_literal("Error before starting decode_bmp_color_table"); - if (context.state < BMPLoadingContext::State::DIBDecoded && !decode_bmp_dib(context)) - return false; + if (context.state < BMPLoadingContext::State::DIBDecoded) + TRY(decode_bmp_dib(context)); if (context.state >= BMPLoadingContext::State::ColorTableDecoded) - return true; + return {}; if (context.dib.core.bpp > 8) { context.state = BMPLoadingContext::State::ColorTableDecoded; - return true; + return {}; } auto bytes_per_color = context.dib_type == DIBType::Core ? 3 : 4; @@ -905,18 +907,18 @@ static bool decode_bmp_color_table(BMPLoadingContext& context) for (u32 i = 0; !streamer.at_end() && i < max_colors; ++i) { if (bytes_per_color == 4) { if (!streamer.has_u32()) - return false; + return Error::from_string_literal("Cannot read 32 bits"); context.color_table.append(streamer.read_u32()); } else { if (!streamer.has_u24()) - return false; + return Error::from_string_literal("Cannot read 24 bits"); context.color_table.append(streamer.read_u24()); } } context.state = BMPLoadingContext::State::ColorTableDecoded; - return true; + return {}; } struct RLEState { @@ -927,13 +929,13 @@ struct RLEState { }; }; -static bool uncompress_bmp_rle_data(BMPLoadingContext& context, ByteBuffer& buffer) +static ErrorOr<void> uncompress_bmp_rle_data(BMPLoadingContext& context, ByteBuffer& buffer) { // RLE-compressed images cannot be stored top-down if (context.dib.core.height < 0) { dbgln_if(BMP_DEBUG, "BMP is top-down and RLE compressed"); context.state = BMPLoadingContext::State::Error; - return false; + return Error::from_string_literal("BMP is top-down and RLE compressed"); } InputStreamer streamer(context.file_bytes + context.data_offset, context.file_size - context.data_offset); @@ -958,20 +960,20 @@ static bool uncompress_bmp_rle_data(BMPLoadingContext& context, ByteBuffer& buff } if (buffer_size > 300 * MiB) { dbgln("Suspiciously large amount of RLE data"); - return false; + return Error::from_string_literal("Suspiciously large amount of RLE data"); } auto buffer_result = ByteBuffer::create_zeroed(buffer_size); if (buffer_result.is_error()) { dbgln("Not enough memory for buffer allocation"); - return false; + return buffer_result.release_error(); } buffer = buffer_result.release_value(); // Avoid as many if statements as possible by pulling out // compression-dependent actions into separate lambdas Function<u32()> get_buffer_index; - Function<bool(u32, bool)> set_byte; - Function<Optional<u32>()> read_byte; + Function<ErrorOr<void>(u32, bool)> set_byte; + Function<ErrorOr<u32>()> read_byte; if (compression == Compression::RLE8) { get_buffer_index = [&]() -> u32 { return row * total_columns + column; }; @@ -982,7 +984,7 @@ static bool uncompress_bmp_rle_data(BMPLoadingContext& context, ByteBuffer& buff } if (compression == Compression::RLE8) { - set_byte = [&](u32 color, bool) -> bool { + set_byte = [&](u32 color, bool) -> ErrorOr<void> { if (column >= total_columns) { column = 0; row++; @@ -990,14 +992,14 @@ static bool uncompress_bmp_rle_data(BMPLoadingContext& context, ByteBuffer& buff auto index = get_buffer_index(); if (index >= buffer.size()) { dbgln("BMP has badly-formatted RLE data"); - return false; + return Error::from_string_literal("BMP has badly-formatted RLE data"); } buffer[index] = color; column++; - return true; + return {}; }; } else if (compression == Compression::RLE24) { - set_byte = [&](u32 color, bool) -> bool { + set_byte = [&](u32 color, bool) -> ErrorOr<void> { if (column >= total_columns) { column = 0; row++; @@ -1005,14 +1007,14 @@ static bool uncompress_bmp_rle_data(BMPLoadingContext& context, ByteBuffer& buff auto index = get_buffer_index(); if (index + 3 >= buffer.size()) { dbgln("BMP has badly-formatted RLE data"); - return false; + return Error::from_string_literal("BMP has badly-formatted RLE data"); } ((u32&)buffer[index]) = color; column++; - return true; + return {}; }; } else { - set_byte = [&](u32 byte, bool rle4_set_second_nibble) -> bool { + set_byte = [&](u32 byte, bool rle4_set_second_nibble) -> ErrorOr<void> { if (column >= total_columns) { column = 0; row++; @@ -1021,7 +1023,7 @@ static bool uncompress_bmp_rle_data(BMPLoadingContext& context, ByteBuffer& buff u32 index = get_buffer_index(); if (index >= buffer.size() || (rle4_set_second_nibble && index + 1 >= buffer.size())) { dbgln("BMP has badly-formatted RLE data"); - return false; + return Error::from_string_literal("BMP has badly-formatted RLE data"); } if (column % 2) { @@ -1040,23 +1042,23 @@ static bool uncompress_bmp_rle_data(BMPLoadingContext& context, ByteBuffer& buff } column++; - return true; + return {}; }; } if (compression == Compression::RLE24) { - read_byte = [&]() -> Optional<u32> { + read_byte = [&]() -> ErrorOr<u32> { if (!streamer.has_u24()) { dbgln("BMP has badly-formatted RLE data"); - return {}; + return Error::from_string_literal("BMP has badly-formatted RLE data"); } return streamer.read_u24(); }; } else { - read_byte = [&]() -> Optional<u32> { + read_byte = [&]() -> ErrorOr<u32> { if (!streamer.has_u8()) { dbgln("BMP has badly-formatted RLE data"); - return {}; + return Error::from_string_literal("BMP has badly-formatted RLE data"); } return streamer.read_u8(); }; @@ -1068,7 +1070,7 @@ static bool uncompress_bmp_rle_data(BMPLoadingContext& context, ByteBuffer& buff switch (currently_consuming) { case RLEState::PixelCount: if (!streamer.has_u8()) - return false; + return Error::from_string_literal("Cannot read 8 bits"); byte = streamer.read_u8(); if (!byte) { currently_consuming = RLEState::Meta; @@ -1077,28 +1079,22 @@ static bool uncompress_bmp_rle_data(BMPLoadingContext& context, ByteBuffer& buff currently_consuming = RLEState::PixelValue; } break; - case RLEState::PixelValue: { - auto result = read_byte(); - if (!result.has_value()) - return false; - byte = result.value(); + case RLEState::PixelValue: + byte = TRY(read_byte()); for (u16 i = 0; i < pixel_count; ++i) { if (compression != Compression::RLE4) { - if (!set_byte(byte, true)) - return false; + TRY(set_byte(byte, true)); } else { - if (!set_byte(byte, i != pixel_count - 1)) - return false; + TRY(set_byte(byte, i != pixel_count - 1)); i++; } } currently_consuming = RLEState::PixelCount; break; - } case RLEState::Meta: if (!streamer.has_u8()) - return false; + return Error::from_string_literal("Cannot read 8 bits"); byte = streamer.read_u8(); if (!byte) { column = 0; @@ -1107,13 +1103,13 @@ static bool uncompress_bmp_rle_data(BMPLoadingContext& context, ByteBuffer& buff continue; } if (byte == 1) - return true; + return {}; if (byte == 2) { if (!streamer.has_u8()) - return false; + return Error::from_string_literal("Cannot read 8 bits"); u8 offset_x = streamer.read_u8(); if (!streamer.has_u8()) - return false; + return Error::from_string_literal("Cannot read 8 bits"); u8 offset_y = streamer.read_u8(); column += offset_x; if (column >= total_columns) { @@ -1130,12 +1126,8 @@ static bool uncompress_bmp_rle_data(BMPLoadingContext& context, ByteBuffer& buff i16 i = byte; while (i >= 1) { - auto result = read_byte(); - if (!result.has_value()) - return false; - byte = result.value(); - if (!set_byte(byte, i != 1)) - return false; + byte = TRY(read_byte()); + TRY(set_byte(byte, i != 1)); i--; if (compression == Compression::RLE4) i--; @@ -1145,13 +1137,13 @@ static bool uncompress_bmp_rle_data(BMPLoadingContext& context, ByteBuffer& buff if (compression != Compression::RLE4) { if (pixel_count % 2) { if (!streamer.has_u8()) - return false; + return Error::from_string_literal("Cannot read 8 bits"); byte = streamer.read_u8(); } } else { if (((pixel_count + 1) / 2) % 2) { if (!streamer.has_u8()) - return false; + return Error::from_string_literal("Cannot read 8 bits"); byte = streamer.read_u8(); } } @@ -1163,13 +1155,13 @@ static bool uncompress_bmp_rle_data(BMPLoadingContext& context, ByteBuffer& buff VERIFY_NOT_REACHED(); } -static bool decode_bmp_pixel_data(BMPLoadingContext& context) +static ErrorOr<void> decode_bmp_pixel_data(BMPLoadingContext& context) { if (context.state == BMPLoadingContext::State::Error) - return false; + return Error::from_string_literal("Error before starting decode_bmp_pixel_data"); - if (context.state <= BMPLoadingContext::State::ColorTableDecoded && !decode_bmp_color_table(context)) - return false; + if (context.state <= BMPLoadingContext::State::ColorTableDecoded) + TRY(decode_bmp_color_table(context)); const u16 bits_per_pixel = context.dib.core.bpp; @@ -1207,52 +1199,45 @@ static bool decode_bmp_pixel_data(BMPLoadingContext& context) if (format == BitmapFormat::Invalid) { dbgln("BMP has invalid bpp of {}", bits_per_pixel); context.state = BMPLoadingContext::State::Error; - return false; + return Error::from_string_literal("BMP has invalid bpp"); } const u32 width = abs(context.dib.core.width); const u32 height = !context.is_included_in_ico ? context.dib.core.height : (context.dib.core.height / 2); - auto bitmap_or_error = Bitmap::try_create(format, { static_cast<int>(width), static_cast<int>(height) }); - if (bitmap_or_error.is_error()) { - // FIXME: Propagate the *real* error. - return false; - } - - context.bitmap = bitmap_or_error.release_value_but_fixme_should_propagate_errors(); + context.bitmap = TRY(Bitmap::try_create(format, { static_cast<int>(width), static_cast<int>(height) })); ByteBuffer rle_buffer; ReadonlyBytes bytes { context.file_bytes + context.data_offset, context.file_size - context.data_offset }; if (context.dib.info.compression == Compression::RLE4 || context.dib.info.compression == Compression::RLE8 || context.dib.info.compression == Compression::RLE24) { - if (!uncompress_bmp_rle_data(context, rle_buffer)) - return false; + TRY(uncompress_bmp_rle_data(context, rle_buffer)); bytes = rle_buffer.bytes(); } InputStreamer streamer(bytes.data(), bytes.size()); - auto process_row_padding = [&](const u8 consumed) -> bool { + auto process_row_padding = [&](const u8 consumed) -> ErrorOr<void> { // Calculate padding u8 remaining = consumed % 4; u8 bytes_to_drop = remaining == 0 ? 0 : 4 - remaining; if (streamer.remaining() < bytes_to_drop) - return false; + return Error::from_string_literal("Not enough bytes available to drop"); streamer.drop_bytes(bytes_to_drop); - return true; + return {}; }; - auto process_row = [&](u32 row) -> bool { + auto process_row = [&](u32 row) -> ErrorOr<void> { u32 space_remaining_before_consuming_row = streamer.remaining(); for (u32 column = 0; column < width;) { switch (bits_per_pixel) { case 1: { if (!streamer.has_u8()) - return false; + return Error::from_string_literal("Cannot read 8 bits"); u8 byte = streamer.read_u8(); u8 mask = 8; while (column < width && mask > 0) { @@ -1269,7 +1254,7 @@ static bool decode_bmp_pixel_data(BMPLoadingContext& context) } case 2: { if (!streamer.has_u8()) - return false; + return Error::from_string_literal("Cannot read 8 bits"); u8 byte = streamer.read_u8(); u8 mask = 8; while (column < width && mask > 0) { @@ -1286,7 +1271,7 @@ static bool decode_bmp_pixel_data(BMPLoadingContext& context) } case 4: { if (!streamer.has_u8()) { - return false; + return Error::from_string_literal("Cannot read 8 bits"); } u8 byte = streamer.read_u8(); @@ -1309,7 +1294,7 @@ static bool decode_bmp_pixel_data(BMPLoadingContext& context) } case 8: { if (!streamer.has_u8()) - return false; + return Error::from_string_literal("Cannot read 8 bits"); u8 byte = streamer.read_u8(); if (context.is_included_in_ico) { @@ -1322,19 +1307,19 @@ static bool decode_bmp_pixel_data(BMPLoadingContext& context) } case 16: { if (!streamer.has_u16()) - return false; + return Error::from_string_literal("Cannot read 16 bits"); context.bitmap->scanline(row)[column++] = int_to_scaled_rgb(context, streamer.read_u16()); break; } case 24: { if (!streamer.has_u24()) - return false; + return Error::from_string_literal("Cannot read 24 bits"); context.bitmap->scanline(row)[column++] = streamer.read_u24(); break; } case 32: if (!streamer.has_u32()) - return false; + return Error::from_string_literal("Cannot read 32 bits"); if (context.dib.info.masks.is_empty()) { context.bitmap->scanline(row)[column++] = streamer.read_u32(); } else { @@ -1349,12 +1334,12 @@ static bool decode_bmp_pixel_data(BMPLoadingContext& context) return process_row_padding(consumed); }; - auto process_mask_row = [&](u32 row) -> bool { + auto process_mask_row = [&](u32 row) -> ErrorOr<void> { u32 space_remaining_before_consuming_row = streamer.remaining(); for (u32 column = 0; column < width;) { if (!streamer.has_u8()) - return false; + return Error::from_string_literal("Cannot read 8 bits"); u8 byte = streamer.read_u8(); u8 mask = 8; @@ -1383,27 +1368,23 @@ static bool decode_bmp_pixel_data(BMPLoadingContext& context) if (context.dib.core.height < 0) { // BMP is stored top-down for (u32 row = 0; row < height; ++row) { - if (!process_row(row)) - return false; + TRY(process_row(row)); } if (context.is_included_in_ico) { for (u32 row = 0; row < height; ++row) { - if (!process_mask_row(row)) - return false; + TRY(process_mask_row(row)); } } } else { // BMP is stored bottom-up for (i32 row = height - 1; row >= 0; --row) { - if (!process_row(row)) - return false; + TRY(process_row(row)); } if (context.is_included_in_ico) { for (i32 row = height - 1; row >= 0; --row) { - if (!process_mask_row(row)) - return false; + TRY(process_mask_row(row)); } } } @@ -1416,7 +1397,7 @@ static bool decode_bmp_pixel_data(BMPLoadingContext& context) context.state = BMPLoadingContext::State::PixelDataDecoded; - return true; + return {}; } BMPImageDecoderPlugin::BMPImageDecoderPlugin(u8 const* data, size_t data_size, bool is_included_in_ico) @@ -1434,7 +1415,7 @@ IntSize BMPImageDecoderPlugin::size() if (m_context->state == BMPLoadingContext::State::Error) return {}; - if (m_context->state < BMPLoadingContext::State::DIBDecoded && !decode_bmp_dib(*m_context)) + if (m_context->state < BMPLoadingContext::State::DIBDecoded && decode_bmp_dib(*m_context).is_error()) return {}; return { m_context->dib.core.width, abs(m_context->dib.core.height) }; @@ -1455,12 +1436,12 @@ bool BMPImageDecoderPlugin::set_nonvolatile(bool& was_purged) bool BMPImageDecoderPlugin::sniff() { - return decode_bmp_header(*m_context); + return !decode_bmp_header(*m_context).is_error(); } bool BMPImageDecoderPlugin::sniff_dib() { - return decode_bmp_dib(*m_context); + return !decode_bmp_dib(*m_context).is_error(); } bool BMPImageDecoderPlugin::is_animated() @@ -1486,8 +1467,8 @@ ErrorOr<ImageFrameDescriptor> BMPImageDecoderPlugin::frame(size_t index) if (m_context->state == BMPLoadingContext::State::Error) return Error::from_string_literal("BMPImageDecoderPlugin: Decoding failed"); - if (m_context->state < BMPLoadingContext::State::PixelDataDecoded && !decode_bmp_pixel_data(*m_context)) - return Error::from_string_literal("BMPImageDecoderPlugin: Decoding failed"); + if (m_context->state < BMPLoadingContext::State::PixelDataDecoded) + TRY(decode_bmp_pixel_data(*m_context)); VERIFY(m_context->bitmap); return ImageFrameDescriptor { m_context->bitmap, 0 }; |