From 9b297c634f82479f3b6851b77f9af9e1486c4546 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Sun, 12 Mar 2023 09:05:25 -0400 Subject: [PATCH] LibGfx: Make QOIWriter use ErrorOr In addition to it now handling allocation failures, the encode() API is now consistent with PNGWriter. --- Tests/LibGL/TestRender.cpp | 2 +- Userland/Applications/PixelPaint/Image.cpp | 2 +- Userland/Demos/Mandelbrot/Mandelbrot.cpp | 2 +- Userland/Libraries/LibGfx/QOIWriter.cpp | 87 ++++++++++++---------- Userland/Libraries/LibGfx/QOIWriter.h | 19 ++--- Userland/Utilities/image.cpp | 2 +- 6 files changed, 62 insertions(+), 52 deletions(-) diff --git a/Tests/LibGL/TestRender.cpp b/Tests/LibGL/TestRender.cpp index e366c008f21..c5a54326fc0 100644 --- a/Tests/LibGL/TestRender.cpp +++ b/Tests/LibGL/TestRender.cpp @@ -35,7 +35,7 @@ static void expect_bitmap_equals_reference(Gfx::Bitmap const& bitmap, StringView if constexpr (SAVE_OUTPUT) { auto target_path = LexicalPath("/home/anon").append(reference_filename); - auto qoi_buffer = Gfx::QOIWriter::encode(bitmap); + auto qoi_buffer = MUST(Gfx::QOIWriter::encode(bitmap)); auto qoi_output_stream = MUST(Core::File::open(target_path.string(), Core::File::OpenMode::Write)); MUST(qoi_output_stream->write_entire_buffer(qoi_buffer)); } diff --git a/Userland/Applications/PixelPaint/Image.cpp b/Userland/Applications/PixelPaint/Image.cpp index ab53535f736..c6beb8f60f9 100644 --- a/Userland/Applications/PixelPaint/Image.cpp +++ b/Userland/Applications/PixelPaint/Image.cpp @@ -196,7 +196,7 @@ ErrorOr Image::export_qoi_to_file(NonnullOwnPtr stream) const { auto bitmap = TRY(compose_bitmap(Gfx::BitmapFormat::BGRA8888)); - auto encoded_data = Gfx::QOIWriter::encode(bitmap); + auto encoded_data = TRY(Gfx::QOIWriter::encode(bitmap)); TRY(stream->write_entire_buffer(encoded_data)); return {}; } diff --git a/Userland/Demos/Mandelbrot/Mandelbrot.cpp b/Userland/Demos/Mandelbrot/Mandelbrot.cpp index cf65d28df6a..c975bbaf9ce 100644 --- a/Userland/Demos/Mandelbrot/Mandelbrot.cpp +++ b/Userland/Demos/Mandelbrot/Mandelbrot.cpp @@ -380,7 +380,7 @@ ErrorOr Mandelbrot::export_image(DeprecatedString const& export_path, Imag encoded_data = TRY(Gfx::PNGWriter::encode(m_set.bitmap())); break; case ImageType::QOI: - encoded_data = Gfx::QOIWriter::encode(m_set.bitmap()); + encoded_data = TRY(Gfx::QOIWriter::encode(m_set.bitmap())); break; default: VERIFY_NOT_REACHED(); diff --git a/Userland/Libraries/LibGfx/QOIWriter.cpp b/Userland/Libraries/LibGfx/QOIWriter.cpp index 6bedd443c27..4abd119dd47 100644 --- a/Userland/Libraries/LibGfx/QOIWriter.cpp +++ b/Userland/Libraries/LibGfx/QOIWriter.cpp @@ -23,10 +23,10 @@ enum class Channels { RGBA, }; -ByteBuffer QOIWriter::encode(Bitmap const& bitmap) +ErrorOr QOIWriter::encode(Bitmap const& bitmap) { QOIWriter writer; - writer.add_header(bitmap.width(), bitmap.height(), Channels::RGBA, Colorspace::sRGB); + TRY(writer.add_header(bitmap.width(), bitmap.height(), Channels::RGBA, Colorspace::sRGB)); Color previous_pixel = { 0, 0, 0, 255 }; @@ -49,7 +49,7 @@ ByteBuffer QOIWriter::encode(Bitmap const& bitmap) // If the run reaches a maximum length of 62 or if this is the last pixel then create the chunk. if (run_length == 62 || (y == bitmap.height() - 1 && x == bitmap.width() - 1)) { - writer.add_run_chunk(run_length); + TRY(writer.add_run_chunk(run_length)); creating_run = false; } @@ -58,7 +58,7 @@ ByteBuffer QOIWriter::encode(Bitmap const& bitmap) // Run ended with the previous pixel. Create a chunk for it and continue processing this pixel. if (creating_run) { - writer.add_run_chunk(run_length); + TRY(writer.add_run_chunk(run_length)); creating_run = false; } @@ -66,7 +66,7 @@ ByteBuffer QOIWriter::encode(Bitmap const& bitmap) auto index = pixel_hash_function(pixel); auto& array_pixel = writer.running_array[index]; if (array_pixel == pixel) { - writer.add_index_chunk(index); + TRY(writer.add_index_chunk(index)); previous_pixel = pixel; continue; } @@ -84,19 +84,19 @@ ByteBuffer QOIWriter::encode(Bitmap const& bitmap) if (red_difference > -3 && red_difference < 2 && green_difference > -3 && green_difference < 2 && blue_difference > -3 && blue_difference < 2) { - writer.add_diff_chunk(red_difference, green_difference, blue_difference); + TRY(writer.add_diff_chunk(red_difference, green_difference, blue_difference)); previous_pixel = pixel; continue; } if (relative_red_difference > -9 && relative_red_difference < 8 && green_difference > -33 && green_difference < 32 && relative_blue_difference > -9 && relative_blue_difference < 8) { - writer.add_luma_chunk(relative_red_difference, green_difference, relative_blue_difference); + TRY(writer.add_luma_chunk(relative_red_difference, green_difference, relative_blue_difference)); previous_pixel = pixel; continue; } - writer.add_rgb_chunk(pixel.red(), pixel.green(), pixel.blue()); + TRY(writer.add_rgb_chunk(pixel.red(), pixel.green(), pixel.blue())); previous_pixel = pixel; continue; } @@ -104,66 +104,71 @@ ByteBuffer QOIWriter::encode(Bitmap const& bitmap) previous_pixel = pixel; // Write full color values. - writer.add_rgba_chunk(pixel.red(), pixel.green(), pixel.blue(), pixel.alpha()); + TRY(writer.add_rgba_chunk(pixel.red(), pixel.green(), pixel.blue(), pixel.alpha())); } } - writer.add_end_marker(); + TRY(writer.add_end_marker()); - return ByteBuffer::copy(writer.m_data).release_value_but_fixme_should_propagate_errors(); + return ByteBuffer::copy(writer.m_data); } -void QOIWriter::add_header(u32 width, u32 height, Channels channels = Channels::RGBA, Colorspace color_space = Colorspace::sRGB) +ErrorOr QOIWriter::add_header(u32 width, u32 height, Channels channels = Channels::RGBA, Colorspace color_space = Colorspace::sRGB) { // FIXME: Handle RGB and all linear channels. if (channels == Channels::RGB || color_space == Colorspace::Linear) TODO(); - m_data.append(qoi_magic_bytes.data(), sizeof(qoi_magic_bytes)); + TRY(m_data.try_append(qoi_magic_bytes.data(), sizeof(qoi_magic_bytes))); auto big_endian_width = AK::convert_between_host_and_big_endian(width); - m_data.append(bit_cast(&big_endian_width), sizeof(width)); + TRY(m_data.try_append(bit_cast(&big_endian_width), sizeof(width))); auto big_endian_height = AK::convert_between_host_and_big_endian(height); - m_data.append(bit_cast(&big_endian_height), sizeof(height)); + TRY(m_data.try_append(bit_cast(&big_endian_height), sizeof(height))); // Number of channels: 3 = RGB, 4 = RGBA. - m_data.append(4); + TRY(m_data.try_append(4)); // Colorspace: 0 = sRGB, 1 = all linear channels. - m_data.append(color_space == Colorspace::sRGB ? 0 : 1); + TRY(m_data.try_append(color_space == Colorspace::sRGB ? 0 : 1)); + + return {}; } -void QOIWriter::add_rgb_chunk(u8 r, u8 g, u8 b) +ErrorOr QOIWriter::add_rgb_chunk(u8 r, u8 g, u8 b) { constexpr static u8 rgb_tag = 0b1111'1110; - m_data.append(rgb_tag); - m_data.append(r); - m_data.append(g); - m_data.append(b); + TRY(m_data.try_append(rgb_tag)); + TRY(m_data.try_append(r)); + TRY(m_data.try_append(g)); + TRY(m_data.try_append(b)); + return {}; } -void QOIWriter::add_rgba_chunk(u8 r, u8 g, u8 b, u8 a) +ErrorOr QOIWriter::add_rgba_chunk(u8 r, u8 g, u8 b, u8 a) { constexpr static u8 rgba_tag = 0b1111'1111; - m_data.append(rgba_tag); - m_data.append(r); - m_data.append(g); - m_data.append(b); - m_data.append(a); + TRY(m_data.try_append(rgba_tag)); + TRY(m_data.try_append(r)); + TRY(m_data.try_append(g)); + TRY(m_data.try_append(b)); + TRY(m_data.try_append(a)); + return {}; } -void QOIWriter::add_index_chunk(unsigned int index) +ErrorOr QOIWriter::add_index_chunk(unsigned int index) { constexpr static u8 index_tag = 0b0000'0000; u8 chunk = index_tag | index; - m_data.append(chunk); + TRY(m_data.try_append(chunk)); + return {}; } -void QOIWriter::add_diff_chunk(i8 red_difference, i8 green_difference, i8 blue_difference) +ErrorOr QOIWriter::add_diff_chunk(i8 red_difference, i8 green_difference, i8 blue_difference) { constexpr static u8 diff_tag = 0b0100'0000; @@ -173,10 +178,11 @@ void QOIWriter::add_diff_chunk(i8 red_difference, i8 green_difference, i8 blue_d u8 blue = blue_difference + bias; u8 chunk = diff_tag | (red << 4) | (green << 2) | blue; - m_data.append(chunk); + TRY(m_data.try_append(chunk)); + return {}; } -void QOIWriter::add_luma_chunk(i8 relative_red_difference, i8 green_difference, i8 relative_blue_difference) +ErrorOr QOIWriter::add_luma_chunk(i8 relative_red_difference, i8 green_difference, i8 relative_blue_difference) { constexpr static u8 luma_tag = 0b1000'0000; u8 green_bias = 32; @@ -184,22 +190,25 @@ void QOIWriter::add_luma_chunk(i8 relative_red_difference, i8 green_difference, u8 chunk1 = luma_tag | (green_difference + green_bias); u8 chunk2 = ((relative_red_difference + red_blue_bias) << 4) | (relative_blue_difference + red_blue_bias); - m_data.append(chunk1); - m_data.append(chunk2); + TRY(m_data.try_append(chunk1)); + TRY(m_data.try_append(chunk2)); + return {}; } -void QOIWriter::add_run_chunk(unsigned run_length) +ErrorOr QOIWriter::add_run_chunk(unsigned run_length) { constexpr static u8 run_tag = 0b1100'0000; int bias = -1; u8 chunk = run_tag | (run_length + bias); - m_data.append(chunk); + TRY(m_data.try_append(chunk)); + return {}; } -void QOIWriter::add_end_marker() +ErrorOr QOIWriter::add_end_marker() { - m_data.append(qoi_end_marker.data(), sizeof(qoi_end_marker)); + TRY(m_data.try_append(qoi_end_marker.data(), sizeof(qoi_end_marker))); + return {}; } u32 QOIWriter::pixel_hash_function(Color pixel) diff --git a/Userland/Libraries/LibGfx/QOIWriter.h b/Userland/Libraries/LibGfx/QOIWriter.h index 3bb8b998f1c..232aad5cd14 100644 --- a/Userland/Libraries/LibGfx/QOIWriter.h +++ b/Userland/Libraries/LibGfx/QOIWriter.h @@ -6,6 +6,7 @@ #pragma once +#include #include #include @@ -16,20 +17,20 @@ enum class Channels; class QOIWriter { public: - static ByteBuffer encode(Gfx::Bitmap const&); + static ErrorOr encode(Gfx::Bitmap const&); private: QOIWriter() = default; Vector m_data; - void add_header(u32 width, u32 height, Channels, Colorspace); - void add_rgb_chunk(u8, u8, u8); - void add_rgba_chunk(u8, u8, u8, u8); - void add_index_chunk(u32 index); - void add_diff_chunk(i8 red_difference, i8 green_difference, i8 blue_difference); - void add_luma_chunk(i8 relative_red_difference, i8 green_difference, i8 relative_blue_difference); - void add_run_chunk(u32 run_length); - void add_end_marker(); + ErrorOr add_header(u32 width, u32 height, Channels, Colorspace); + ErrorOr add_rgb_chunk(u8, u8, u8); + ErrorOr add_rgba_chunk(u8, u8, u8, u8); + ErrorOr add_index_chunk(u32 index); + ErrorOr add_diff_chunk(i8 red_difference, i8 green_difference, i8 blue_difference); + ErrorOr add_luma_chunk(i8 relative_red_difference, i8 green_difference, i8 relative_blue_difference); + ErrorOr add_run_chunk(u32 run_length); + ErrorOr add_end_marker(); Array running_array; static u32 pixel_hash_function(Color pixel); diff --git a/Userland/Utilities/image.cpp b/Userland/Utilities/image.cpp index fa105303b96..e53814f9922 100644 --- a/Userland/Utilities/image.cpp +++ b/Userland/Utilities/image.cpp @@ -42,7 +42,7 @@ ErrorOr serenity_main(Main::Arguments arguments) } else if (out_path.ends_with(".png"sv, CaseSensitivity::CaseInsensitive)) { bytes = TRY(Gfx::PNGWriter::encode(*frame)); } else if (out_path.ends_with(".qoi"sv, CaseSensitivity::CaseInsensitive)) { - bytes = Gfx::QOIWriter::encode(*frame); + bytes = TRY(Gfx::QOIWriter::encode(*frame)); } else { warnln("can only write .bmp, .png, and .qoi"); return 1;