From 786b02730c88fd0d202e375fc4798bc994699f43 Mon Sep 17 00:00:00 2001 From: Lenny Maiorani Date: Sat, 12 Mar 2022 11:16:30 -0700 Subject: [PATCH] LibGfx: Commonize P[BGP]M file loading contexts Much of the code in PBM, PGM, and PPM image loaders is common. The contexts are nearly identical. Instead of writing multiple contexts, write 1 with a template argument to pass in the details of the given format. --- Userland/Libraries/LibGfx/PBMLoader.cpp | 35 ++------------ Userland/Libraries/LibGfx/PBMLoader.h | 11 ++++- Userland/Libraries/LibGfx/PGMLoader.cpp | 41 ++-------------- Userland/Libraries/LibGfx/PGMLoader.h | 12 ++++- Userland/Libraries/LibGfx/PPMLoader.cpp | 41 ++-------------- Userland/Libraries/LibGfx/PPMLoader.h | 12 ++++- .../LibGfx/PortableImageLoaderCommon.h | 33 +++++++------ .../Libraries/LibGfx/PortableImageMapLoader.h | 48 +++++++++++++++++++ 8 files changed, 109 insertions(+), 124 deletions(-) create mode 100644 Userland/Libraries/LibGfx/PortableImageMapLoader.h diff --git a/Userland/Libraries/LibGfx/PBMLoader.cpp b/Userland/Libraries/LibGfx/PBMLoader.cpp index 8f2ff976b95..795437ba8fb 100644 --- a/Userland/Libraries/LibGfx/PBMLoader.cpp +++ b/Userland/Libraries/LibGfx/PBMLoader.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, Hüseyin ASLITÜRK + * Copyright (c) 2022, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause */ @@ -12,42 +13,12 @@ namespace Gfx { -struct PBMLoadingContext { - enum Type { - Unknown, - ASCII, - RAWBITS - }; - - enum State { - NotDecoded = 0, - Error, - MagicNumber, - Width, - Height, - Bitmap, - Decoded - }; - - static constexpr auto ascii_magic_number = '1'; - static constexpr auto binary_magic_number = '4'; - static constexpr auto image_type = "PBM"; - - Type type { Type::Unknown }; - State state { State::NotDecoded }; - const u8* data { nullptr }; - size_t data_size { 0 }; - size_t width { 0 }; - size_t height { 0 }; - RefPtr bitmap; -}; - static bool read_image_data(PBMLoadingContext& context, Streamer& streamer) { u8 byte; Vector color_data; - if (context.type == PBMLoadingContext::ASCII) { + if (context.type == PBMLoadingContext::Type::ASCII) { while (streamer.read(byte)) { if (byte == '0') { color_data.append(Color::White); @@ -55,7 +26,7 @@ static bool read_image_data(PBMLoadingContext& context, Streamer& streamer) color_data.append(Color::Black); } } - } else if (context.type == PBMLoadingContext::RAWBITS) { + } else if (context.type == PBMLoadingContext::Type::RAWBITS) { size_t color_index = 0; while (streamer.read(byte)) { diff --git a/Userland/Libraries/LibGfx/PBMLoader.h b/Userland/Libraries/LibGfx/PBMLoader.h index 29e9da93108..db223a11f77 100644 --- a/Userland/Libraries/LibGfx/PBMLoader.h +++ b/Userland/Libraries/LibGfx/PBMLoader.h @@ -1,16 +1,25 @@ /* * Copyright (c) 2020, Hüseyin ASLITÜRK + * Copyright (c) 2022, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once +#include #include +#include namespace Gfx { -struct PBMLoadingContext; +struct PBM { + static constexpr auto ascii_magic_number = '1'; + static constexpr auto binary_magic_number = '4'; + static constexpr StringView image_type = "PBM"; +}; + +using PBMLoadingContext = PortableImageMapLoadingContext; class PBMImageDecoderPlugin final : public ImageDecoderPlugin { public: diff --git a/Userland/Libraries/LibGfx/PGMLoader.cpp b/Userland/Libraries/LibGfx/PGMLoader.cpp index 9280f7f5284..1550673feae 100644 --- a/Userland/Libraries/LibGfx/PGMLoader.cpp +++ b/Userland/Libraries/LibGfx/PGMLoader.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, Hüseyin ASLITÜRK + * Copyright (c) 2022, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause */ @@ -12,46 +13,14 @@ namespace Gfx { -struct PGMLoadingContext { - enum Type { - Unknown, - ASCII, - RAWBITS - }; - - enum State { - NotDecoded = 0, - Error, - MagicNumber, - Width, - Height, - Maxval, - Bitmap, - Decoded - }; - - static constexpr auto ascii_magic_number = '2'; - static constexpr auto binary_magic_number = '5'; - static constexpr auto image_type = "PGM"; - - Type type { Type::Unknown }; - State state { State::NotDecoded }; - const u8* data { nullptr }; - size_t data_size { 0 }; - u16 width { 0 }; - u16 height { 0 }; - u16 max_val { 0 }; - RefPtr bitmap; -}; - static void set_adjusted_pixels(PGMLoadingContext& context, const Vector& color_data) { size_t index = 0; for (size_t y = 0; y < context.height; ++y) { for (size_t x = 0; x < context.width; ++x) { Color color = color_data.at(index); - if (context.max_val < 255) { - color = adjust_color(context.max_val, color); + if (context.format_details.max_val < 255) { + color = adjust_color(context.format_details.max_val, color); } context.bitmap->set_pixel(x, y, color); ++index; @@ -63,7 +32,7 @@ static bool read_image_data(PGMLoadingContext& context, Streamer& streamer) { Vector color_data; - if (context.type == PGMLoadingContext::ASCII) { + if (context.type == PGMLoadingContext::Type::ASCII) { u16 value; while (true) { @@ -75,7 +44,7 @@ static bool read_image_data(PGMLoadingContext& context, Streamer& streamer) color_data.append({ (u8)value, (u8)value, (u8)value }); } - } else if (context.type == PGMLoadingContext::RAWBITS) { + } else if (context.type == PGMLoadingContext::Type::RAWBITS) { u8 pixel; while (streamer.read(pixel)) { color_data.append({ pixel, pixel, pixel }); diff --git a/Userland/Libraries/LibGfx/PGMLoader.h b/Userland/Libraries/LibGfx/PGMLoader.h index ad97aa80853..805a9a92b7b 100644 --- a/Userland/Libraries/LibGfx/PGMLoader.h +++ b/Userland/Libraries/LibGfx/PGMLoader.h @@ -1,16 +1,26 @@ /* * Copyright (c) 2020, Hüseyin ASLITÜRK + * Copyright (c) 2022, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once +#include #include +#include namespace Gfx { -struct PGMLoadingContext; +struct PGM { + static constexpr auto ascii_magic_number = '2'; + static constexpr auto binary_magic_number = '5'; + static constexpr StringView image_type = "PGM"; + u16 max_val { 0 }; +}; + +using PGMLoadingContext = PortableImageMapLoadingContext; class PGMImageDecoderPlugin final : public ImageDecoderPlugin { public: diff --git a/Userland/Libraries/LibGfx/PPMLoader.cpp b/Userland/Libraries/LibGfx/PPMLoader.cpp index c05789f344e..94298690489 100644 --- a/Userland/Libraries/LibGfx/PPMLoader.cpp +++ b/Userland/Libraries/LibGfx/PPMLoader.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, Hüseyin ASLITÜRK + * Copyright (c) 2022, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause */ @@ -15,44 +16,12 @@ namespace Gfx { -struct PPMLoadingContext { - enum Type { - Unknown, - ASCII, - RAWBITS - }; - - enum State { - NotDecoded = 0, - Error, - MagicNumber, - Width, - Height, - Maxval, - Bitmap, - Decoded - }; - - static constexpr auto ascii_magic_number = '3'; - static constexpr auto binary_magic_number = '6'; - static constexpr auto image_type = "PPM"; - - Type type { Type::Unknown }; - State state { State::NotDecoded }; - const u8* data { nullptr }; - size_t data_size { 0 }; - u16 width { 0 }; - u16 height { 0 }; - u16 max_val { 0 }; - RefPtr bitmap; -}; - static bool read_image_data(PPMLoadingContext& context, Streamer& streamer) { Vector color_data; color_data.ensure_capacity(context.width * context.height); - if (context.type == PPMLoadingContext::ASCII) { + if (context.type == PPMLoadingContext::Type::ASCII) { u16 red; u16 green; u16 blue; @@ -77,11 +46,11 @@ static bool read_image_data(PPMLoadingContext& context, Streamer& streamer) break; Color color { (u8)red, (u8)green, (u8)blue }; - if (context.max_val < 255) - color = adjust_color(context.max_val, color); + if (context.format_details.max_val < 255) + color = adjust_color(context.format_details.max_val, color); color_data.append(color); } - } else if (context.type == PPMLoadingContext::RAWBITS) { + } else if (context.type == PPMLoadingContext::Type::RAWBITS) { u8 pixel[3]; while (streamer.read_bytes(pixel, 3)) { color_data.append({ pixel[0], pixel[1], pixel[2] }); diff --git a/Userland/Libraries/LibGfx/PPMLoader.h b/Userland/Libraries/LibGfx/PPMLoader.h index 19dc9e7ed8e..572e2170d3d 100644 --- a/Userland/Libraries/LibGfx/PPMLoader.h +++ b/Userland/Libraries/LibGfx/PPMLoader.h @@ -1,16 +1,26 @@ /* * Copyright (c) 2020, Hüseyin ASLITÜRK + * Copyright (c) 2022, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once +#include #include +#include namespace Gfx { -struct PPMLoadingContext; +struct PPM { + static constexpr auto ascii_magic_number = '3'; + static constexpr auto binary_magic_number = '6'; + static constexpr StringView image_type = "PPM"; + u16 max_val { 0 }; +}; + +using PPMLoadingContext = PortableImageMapLoadingContext; class PPMImageDecoderPlugin final : public ImageDecoderPlugin { public: diff --git a/Userland/Libraries/LibGfx/PortableImageLoaderCommon.h b/Userland/Libraries/LibGfx/PortableImageLoaderCommon.h index 7c9cfc75f3e..c5cb4190959 100644 --- a/Userland/Libraries/LibGfx/PortableImageLoaderCommon.h +++ b/Userland/Libraries/LibGfx/PortableImageLoaderCommon.h @@ -1,13 +1,12 @@ /* * Copyright (c) 2020, Hüseyin Aslıtürk - * Copyright (c) 2020, the SerenityOS developers. + * Copyright (c) 2020-2022, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once -#include #include #include #include @@ -82,31 +81,31 @@ static bool read_magic_number(TContext& context, Streamer& streamer) if (!context.data || context.data_size < 2) { context.state = TContext::State::Error; - dbgln_if(PORTABLE_IMAGE_LOADER_DEBUG, "There is no enough data for {}", TContext::image_type); + dbgln_if(PORTABLE_IMAGE_LOADER_DEBUG, "There is no enough data for {}", TContext::FormatDetails::image_type); return false; } u8 magic_number[2] {}; if (!streamer.read_bytes(magic_number, 2)) { context.state = TContext::State::Error; - dbgln_if(PORTABLE_IMAGE_LOADER_DEBUG, "We can't read magic number for {}", TContext::image_type); + dbgln_if(PORTABLE_IMAGE_LOADER_DEBUG, "We can't read magic number for {}", TContext::FormatDetails::image_type); return false; } - if (magic_number[0] == 'P' && magic_number[1] == TContext::ascii_magic_number) { + if (magic_number[0] == 'P' && magic_number[1] == TContext::FormatDetails::ascii_magic_number) { context.type = TContext::Type::ASCII; context.state = TContext::State::MagicNumber; return true; } - if (magic_number[0] == 'P' && magic_number[1] == TContext::binary_magic_number) { + if (magic_number[0] == 'P' && magic_number[1] == TContext::FormatDetails::binary_magic_number) { context.type = TContext::Type::RAWBITS; context.state = TContext::State::MagicNumber; return true; } context.state = TContext::State::Error; - dbgln_if(PORTABLE_IMAGE_LOADER_DEBUG, "Magic number is not valid for {}{}{}", magic_number[0], magic_number[1], TContext::image_type); + dbgln_if(PORTABLE_IMAGE_LOADER_DEBUG, "Magic number is not valid for {}{}{}", magic_number[0], magic_number[1], TContext::FormatDetails::image_type); return false; } @@ -139,7 +138,7 @@ static bool read_width(TContext& context, Streamer& streamer) return false; } - context.state = TContext::Width; + context.state = TContext::State::Width; return true; } @@ -151,25 +150,25 @@ static bool read_height(TContext& context, Streamer& streamer) return false; } - context.state = TContext::Height; + context.state = TContext::State::Height; return true; } template static bool read_max_val(TContext& context, Streamer& streamer) { - if (const bool result = read_number(streamer, &context.max_val); - !result || context.max_val == 0) { + if (const bool result = read_number(streamer, &context.format_details.max_val); + !result || context.format_details.max_val == 0) { return false; } - if (context.max_val > 255) { - dbgln_if(PORTABLE_IMAGE_LOADER_DEBUG, "We can't parse 2 byte color for {}", TContext::image_type); - context.state = TContext::Error; + if (context.format_details.max_val > 255) { + dbgln_if(PORTABLE_IMAGE_LOADER_DEBUG, "We can't parse 2 byte color for {}", TContext::FormatDetails::image_type); + context.state = TContext::State::Error; return false; } - context.state = TContext::Maxval; + context.state = TContext::State::Maxval; return true; } @@ -232,7 +231,7 @@ static bool decode(TContext& context) if (!read_whitespace(context, streamer)) return false; - if constexpr (requires { context.max_val; }) { + if constexpr (requires { context.format_details.max_val; }) { if (!read_max_val(context, streamer)) return false; @@ -266,7 +265,7 @@ static RefPtr load_from_memory(u8 const* data, size_t length, Strin { auto bitmap = load_impl(data, length); if (bitmap) - bitmap->set_mmap_name(String::formatted("Gfx::Bitmap [{}] - Decoded {}: {}", bitmap->size(), TContext::image_type, mmap_name)); + bitmap->set_mmap_name(String::formatted("Gfx::Bitmap [{}] - Decoded {}: {}", bitmap->size(), TContext::FormatDetails::image_type, mmap_name)); return bitmap; } diff --git a/Userland/Libraries/LibGfx/PortableImageMapLoader.h b/Userland/Libraries/LibGfx/PortableImageMapLoader.h new file mode 100644 index 00000000000..0a81f20dc8c --- /dev/null +++ b/Userland/Libraries/LibGfx/PortableImageMapLoader.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020, Hüseyin ASLITÜRK + * Copyright (c) 2022, the SerenityOS developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Gfx { + +template +struct PortableImageMapLoadingContext { + using FormatDetails = TFormatDetails; + + enum class Type { + Unknown, + ASCII, + RAWBITS + }; + + enum class State { + NotDecoded = 0, + Error, + MagicNumber, + Width, + Height, + Maxval, + Bitmap, + Decoded + }; + + Type type { Type::Unknown }; + State state { State::NotDecoded }; + u8 const* data { nullptr }; + size_t data_size { 0 }; + size_t width { 0 }; + size_t height { 0 }; + FormatDetails format_details {}; + RefPtr bitmap; +}; + +}