diff --git a/CMakeLists.txt b/CMakeLists.txt index 382d794146e..ab2c11300e6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,7 +71,7 @@ if (ALL_THE_DEBUG_MACROS) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DMALLOC_DEBUG -DMASTERPTY_DEBUG -DMBR_DEBUG -DMEMORY_DEBUG -DMENU_DEBUG -DMINIMIZE_ANIMATION_DEBUG -DMM_DEBUG -DMOVE_DEBUG -DMULTIPROCESSOR_DEBUG") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNETWORK_TASK_DEBUG -DNT_DEBUG") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOBJECT_DEBUG -DOCCLUSIONS_DEBUG -DOFFD_DEBUG") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DPAGE_FAULT_DEBUG -DPARSER_DEBUG -DPATA_DEBUG -DPATA_DEVICE_DEBUG -DPATH_DEBUG -DPCI_DEBUG -DPNG_DEBUG -DPPM_DEBUG -DPROCESS_DEBUG -DPROCFS_DEBUG -DPS2MOUSE_DEBUG -DPTHREAD_DEBUG -DPTMX_DEBUG") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DPAGE_FAULT_DEBUG -DPARSER_DEBUG -DPATA_DEBUG -DPATA_DEVICE_DEBUG -DPATH_DEBUG -DPCI_DEBUG -DPNG_DEBUG -DPORTABLE_IMAGE_LOADER_DEBUG -DPROCESS_DEBUG -DPROCFS_DEBUG -DPS2MOUSE_DEBUG -DPTHREAD_DEBUG -DPTMX_DEBUG") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DREACHABLE_DEBUG -DREGEX_DEBUG -DRESIZE_DEBUG -DRESOURCE_DEBUG -DROUTING_DEBUG -DRTL8139_DEBUG") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSAFE_SYSCALL_DEBUG -DSB16_DEBUG -DSCHEDULER_DEBUG -DSCHEDULER_RUNNABLE_DEBUG -DSELECTION_DEBUG -DSERVICE_DEBUG -DSHARED_BUFFER_DEBUG -DSH_DEBUG -DSIGNAL_DEBUG -DSLAVEPTY_DEBUG -DSMP_DEBUG -DSOCKET_DEBUG -DSYSTEM_MENU_DEBUG -DSYSTEMSERVER_DEBUG -DSTORAGE_DEVICE_DEBUG") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTCP_DEBUG -DTCP_SOCKET_DEBUG -DTERMCAP_DEBUG -DTERMINAL_DEBUG -DTHREAD_DEBUG -DTLS_DEBUG -DTTY_DEBUG") diff --git a/Libraries/LibGfx/Color.h b/Libraries/LibGfx/Color.h index 595d1bf9d9b..0187a2e6b72 100644 --- a/Libraries/LibGfx/Color.h +++ b/Libraries/LibGfx/Color.h @@ -101,19 +101,19 @@ public: m_value |= value << 24; } - void set_red(u8 value) + constexpr void set_red(u8 value) { m_value &= 0xff00ffff; m_value |= value << 16; } - void set_green(u8 value) + constexpr void set_green(u8 value) { m_value &= 0xffff00ff; m_value |= value << 8; } - void set_blue(u8 value) + constexpr void set_blue(u8 value) { m_value &= 0xffffff00; m_value |= value; diff --git a/Libraries/LibGfx/PBMLoader.cpp b/Libraries/LibGfx/PBMLoader.cpp index 7e5396808de..487cb9ff022 100644 --- a/Libraries/LibGfx/PBMLoader.cpp +++ b/Libraries/LibGfx/PBMLoader.cpp @@ -25,6 +25,7 @@ */ #include "PBMLoader.h" +#include "PortableImageLoaderCommon.h" #include "Streamer.h" #include #include @@ -37,8 +38,8 @@ namespace Gfx { struct PBMLoadingContext { enum Type { Unknown, - P1_ASCII, - P4_RAWBITS + ASCII, + RAWBITS }; enum State { @@ -51,6 +52,10 @@ struct PBMLoadingContext { 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 }; @@ -60,138 +65,12 @@ struct PBMLoadingContext { RefPtr bitmap; }; -static int read_number(Streamer& streamer) -{ - u8 byte; - StringBuilder sb; - - while (streamer.read(byte)) { - if (byte == ' ' || byte == '\t' || byte == '\n' || byte == '\r') { - streamer.step_back(); - break; - } - - sb.append(byte); - } - - return sb.to_string().to_uint().value_or(0); -} - -static bool read_comment([[maybe_unused]] PBMLoadingContext& context, Streamer& streamer) -{ - bool exist = false; - u8 byte; - - while (streamer.read(byte)) { - switch (byte) { - case '#': { - exist = true; - break; - } - case '\t': - case '\n': { - return exist; - } - default: - break; - } - } - - return exist; -} - -static bool read_magic_number(PBMLoadingContext& context, Streamer& streamer) -{ - if (context.state >= PBMLoadingContext::MagicNumber) - return true; - - if (!context.data || context.data_size < 2) { - context.state = PBMLoadingContext::State::Error; - dbg() << "There is no enough data."; - return false; - } - - u8 magic_number[2]; - if (!streamer.read_bytes(magic_number, 2)) { - context.state = PBMLoadingContext::State::Error; - dbg() << "We can't read magic number."; - return false; - } - - if (magic_number[0] == 'P' && magic_number[1] == '1') { - context.type = PBMLoadingContext::P1_ASCII; - context.state = PBMLoadingContext::MagicNumber; - return true; - } - - if (magic_number[0] == 'P' && magic_number[1] == '4') { - context.type = PBMLoadingContext::P4_RAWBITS; - context.state = PBMLoadingContext::MagicNumber; - return true; - } - - context.state = PBMLoadingContext::State::Error; - dbg() << "Magic number is not valid." << (char)magic_number[0] << (char)magic_number[1]; - return false; -} - -static bool read_white_space(PBMLoadingContext& context, Streamer& streamer) -{ - bool exist = false; - u8 byte; - - while (streamer.read(byte)) { - switch (byte) { - case ' ': - case '\t': - case '\n': - case '\r': { - exist = true; - break; - } - case '#': { - streamer.step_back(); - read_comment(context, streamer); - break; - } - default: { - streamer.step_back(); - return exist; - } - } - } - - return exist; -} - -static bool read_width(PBMLoadingContext& context, Streamer& streamer) -{ - context.width = read_number(streamer); - if (context.width == 0) { - return false; - } - - context.state = PBMLoadingContext::Width; - return true; -} - -static bool read_height(PBMLoadingContext& context, Streamer& streamer) -{ - context.height = read_number(streamer); - if (context.height == 0) { - return false; - } - - context.state = PBMLoadingContext::Height; - return true; -} - static bool read_image_data(PBMLoadingContext& context, Streamer& streamer) { u8 byte; Vector color_data; - if (context.type == PBMLoadingContext::P1_ASCII) { + if (context.type == PBMLoadingContext::ASCII) { while (streamer.read(byte)) { if (byte == '0') { color_data.append(Color::White); @@ -199,7 +78,7 @@ static bool read_image_data(PBMLoadingContext& context, Streamer& streamer) color_data.append(Color::Black); } } - } else if (context.type == PBMLoadingContext::P4_RAWBITS) { + } else if (context.type == PBMLoadingContext::RAWBITS) { size_t color_index = 0; while (streamer.read(byte)) { @@ -222,83 +101,24 @@ static bool read_image_data(PBMLoadingContext& context, Streamer& streamer) } } - context.bitmap = Bitmap::create_purgeable(BitmapFormat::RGB32, { context.width, context.height }); - if (!context.bitmap) { - context.state = PBMLoadingContext::State::Error; + if (!create_bitmap(context)) { return false; } - size_t index = 0; - for (int y = 0; y < context.height; ++y) { - for (int x = 0; x < context.width; ++x) { - context.bitmap->set_pixel(x, y, color_data.at(index)); - index++; - } - } + set_pixels(context, color_data); context.state = PBMLoadingContext::State::Bitmap; return true; } -static bool decode_pbm(PBMLoadingContext& context) -{ - if (context.state >= PBMLoadingContext::State::Decoded) - return true; - - Streamer streamer(context.data, context.data_size); - - if (!read_magic_number(context, streamer)) - return false; - if (!read_white_space(context, streamer)) - return false; - - if (!read_width(context, streamer)) - return false; - - if (!read_white_space(context, streamer)) - return false; - - if (!read_height(context, streamer)) - return false; - - if (!read_white_space(context, streamer)) - return false; - - if (!read_image_data(context, streamer)) - return false; - - context.state = PBMLoadingContext::State::Decoded; - return true; -} - -static RefPtr load_pbm_impl(const u8* data, size_t data_size) -{ - PBMLoadingContext context; - context.data = data; - context.data_size = data_size; - - if (!decode_pbm(context)) - return nullptr; - - return context.bitmap; -} - RefPtr load_pbm(const StringView& path) { - MappedFile mapped_file(path); - if (!mapped_file.is_valid()) { - return nullptr; - } - - auto bitmap = load_pbm_impl((const u8*)mapped_file.data(), mapped_file.size()); - if (bitmap) - bitmap->set_mmap_name(String::format("Gfx::Bitmap [%dx%d] - Decoded PBM: %s", bitmap->width(), bitmap->height(), LexicalPath::canonicalized_path(path).characters())); - return bitmap; + return load(path); } RefPtr load_pbm_from_memory(const u8* data, size_t length) { - auto bitmap = load_pbm_impl(data, length); + auto bitmap = load_impl(data, length); if (bitmap) bitmap->set_mmap_name(String::format("Gfx::Bitmap [%dx%d] - Decoded PBM: ", bitmap->width(), bitmap->height())); return bitmap; @@ -321,7 +141,7 @@ IntSize PBMImageDecoderPlugin::size() return {}; if (m_context->state < PBMLoadingContext::State::Decoded) { - bool success = decode_pbm(*m_context); + bool success = decode(*m_context); if (!success) return {}; } @@ -335,7 +155,7 @@ RefPtr PBMImageDecoderPlugin::bitmap() return nullptr; if (m_context->state < PBMLoadingContext::State::Decoded) { - bool success = decode_pbm(*m_context); + bool success = decode(*m_context); if (!success) return nullptr; } diff --git a/Libraries/LibGfx/PGMLoader.cpp b/Libraries/LibGfx/PGMLoader.cpp index b83773c81d6..8fb94b86e2a 100644 --- a/Libraries/LibGfx/PGMLoader.cpp +++ b/Libraries/LibGfx/PGMLoader.cpp @@ -25,6 +25,7 @@ */ #include "PGMLoader.h" +#include "PortableImageLoaderCommon.h" #include "Streamer.h" #include #include @@ -37,8 +38,8 @@ namespace Gfx { struct PGMLoadingContext { enum Type { Unknown, - P2_ASCII, - P5_RAWBITS + ASCII, + RAWBITS }; enum State { @@ -52,6 +53,10 @@ struct PGMLoadingContext { 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 }; @@ -62,169 +67,11 @@ struct PGMLoadingContext { RefPtr bitmap; }; -ALWAYS_INLINE static Color adjust_color(u16 max_val, Color& color) -{ - color.set_red((color.red() * 255) / max_val); - color.set_green((color.green() * 255) / max_val); - color.set_blue((color.blue() * 255) / max_val); - - return color; -} - -static bool read_number(Streamer& streamer, u16* value) -{ - u8 byte; - StringBuilder sb; - - while (streamer.read(byte)) { - if (byte == ' ' || byte == '\t' || byte == '\n' || byte == '\r') { - streamer.step_back(); - break; - } - - sb.append(byte); - } - - auto opt_value = sb.to_string().to_uint(); - if (!opt_value.has_value()) { - return false; - } - - *value = (u16)opt_value.value(); - return true; -} - -static bool read_comment([[maybe_unused]] PGMLoadingContext& context, Streamer& streamer) -{ - bool exist = false; - u8 byte; - - while (streamer.read(byte)) { - switch (byte) { - case '#': { - exist = true; - break; - } - case '\t': - case '\n': { - return exist; - } - default: - break; - } - } - - return exist; -} - -static bool read_magic_number(PGMLoadingContext& context, Streamer& streamer) -{ - if (context.state >= PGMLoadingContext::MagicNumber) - return true; - - if (!context.data || context.data_size < 2) { - context.state = PGMLoadingContext::State::Error; - dbg() << "There is no enough data."; - return false; - } - - u8 magic_number[2]; - if (!streamer.read_bytes(magic_number, 2)) { - context.state = PGMLoadingContext::State::Error; - dbg() << "We can't read magic number."; - return false; - } - - if (magic_number[0] == 'P' && magic_number[1] == '2') { - context.type = PGMLoadingContext::P2_ASCII; - context.state = PGMLoadingContext::MagicNumber; - return true; - } - - if (magic_number[0] == 'P' && magic_number[1] == '5') { - context.type = PGMLoadingContext::P5_RAWBITS; - context.state = PGMLoadingContext::MagicNumber; - return true; - } - - context.state = PGMLoadingContext::State::Error; - dbg() << "Magic number is not valid:" << (char)magic_number[0] << (char)magic_number[1]; - return false; -} - -static bool read_white_space(PGMLoadingContext& context, Streamer& streamer) -{ - bool exist = false; - u8 byte; - - while (streamer.read(byte)) { - switch (byte) { - case ' ': - case '\t': - case '\n': - case '\r': { - exist = true; - break; - } - case '#': { - streamer.step_back(); - read_comment(context, streamer); - break; - } - default: { - streamer.step_back(); - return exist; - } - } - } - - return exist; -} - -static bool read_width(PGMLoadingContext& context, Streamer& streamer) -{ - bool result = read_number(streamer, &context.width); - if (!result || context.width == 0) { - return false; - } - - context.state = PGMLoadingContext::Width; - return true; -} - -static bool read_height(PGMLoadingContext& context, Streamer& streamer) -{ - bool result = read_number(streamer, &context.height); - if (!result || context.height == 0) { - return false; - } - - context.state = PGMLoadingContext::Height; - return true; -} - -static bool read_max_val(PGMLoadingContext& context, Streamer& streamer) -{ - bool result = read_number(streamer, &context.max_val); - if (!result || context.max_val == 0) { - return false; - } - - if (context.max_val > 255) { - dbg() << "We can't pars 2 byte color."; - context.state = PGMLoadingContext::Error; - return false; - } - - context.state = PGMLoadingContext::Maxval; - return true; -} - static bool read_image_data(PGMLoadingContext& context, Streamer& streamer) { Vector color_data; - if (context.type == PGMLoadingContext::P2_ASCII) { + if (context.type == PGMLoadingContext::ASCII) { u16 value; while (true) { @@ -236,100 +83,31 @@ static bool read_image_data(PGMLoadingContext& context, Streamer& streamer) color_data.append({ (u8)value, (u8)value, (u8)value }); } - } else if (context.type == PGMLoadingContext::P5_RAWBITS) { + } else if (context.type == PGMLoadingContext::RAWBITS) { u8 pixel; while (streamer.read(pixel)) { color_data.append({ pixel, pixel, pixel }); } } - context.bitmap = Bitmap::create_purgeable(BitmapFormat::RGB32, { context.width, context.height }); - if (!context.bitmap) { - context.state = PGMLoadingContext::State::Error; + if (!create_bitmap(context)) { return false; } - size_t index = 0; - for (int y = 0; y < context.height; ++y) { - for (int x = 0; x < context.width; ++x) { - Color color = color_data.at(index); - if (context.max_val < 255) - color = adjust_color(context.max_val, color); - context.bitmap->set_pixel(x, y, color); - index++; - } - } + set_adjusted_pixels(context, color_data); context.state = PGMLoadingContext::State::Bitmap; return true; } -static bool decode_pgm(PGMLoadingContext& context) -{ - if (context.state >= PGMLoadingContext::State::Decoded) - return true; - - Streamer streamer(context.data, context.data_size); - - if (!read_magic_number(context, streamer)) - return false; - - if (!read_white_space(context, streamer)) - return false; - - if (!read_width(context, streamer)) - return false; - - if (!read_white_space(context, streamer)) - return false; - - if (!read_height(context, streamer)) - return false; - - if (!read_white_space(context, streamer)) - return false; - - if (!read_max_val(context, streamer)) - return false; - - if (!read_white_space(context, streamer)) - return false; - - if (!read_image_data(context, streamer)) - return false; - - context.state = PGMLoadingContext::State::Decoded; - return true; -} - -static RefPtr load_pgm_impl(const u8* data, size_t data_size) -{ - PGMLoadingContext context; - context.data = data; - context.data_size = data_size; - - if (!decode_pgm(context)) - return nullptr; - - return context.bitmap; -} - RefPtr load_pgm(const StringView& path) { - MappedFile mapped_file(path); - if (!mapped_file.is_valid()) { - return nullptr; - } - - auto bitmap = load_pgm_impl((const u8*)mapped_file.data(), mapped_file.size()); - if (bitmap) - bitmap->set_mmap_name(String::format("Gfx::Bitmap [%dx%d] - Decoded PGM: %s", bitmap->width(), bitmap->height(), LexicalPath::canonicalized_path(path).characters())); - return bitmap; + return load(path); } RefPtr load_pgm_from_memory(const u8* data, size_t length) { - auto bitmap = load_pgm_impl(data, length); + auto bitmap = load_impl(data, length); if (bitmap) bitmap->set_mmap_name(String::format("Gfx::Bitmap [%dx%d] - Decoded PGM: ", bitmap->width(), bitmap->height())); return bitmap; @@ -352,7 +130,7 @@ IntSize PGMImageDecoderPlugin::size() return {}; if (m_context->state < PGMLoadingContext::State::Decoded) { - bool success = decode_pgm(*m_context); + bool success = decode(*m_context); if (!success) return {}; } @@ -366,7 +144,7 @@ RefPtr PGMImageDecoderPlugin::bitmap() return nullptr; if (m_context->state < PGMLoadingContext::State::Decoded) { - bool success = decode_pgm(*m_context); + bool success = decode(*m_context); if (!success) return nullptr; } diff --git a/Libraries/LibGfx/PPMLoader.cpp b/Libraries/LibGfx/PPMLoader.cpp index a082054663d..2fb2c1c909d 100644 --- a/Libraries/LibGfx/PPMLoader.cpp +++ b/Libraries/LibGfx/PPMLoader.cpp @@ -25,6 +25,7 @@ */ #include "PPMLoader.h" +#include "PortableImageLoaderCommon.h" #include "Streamer.h" #include #include @@ -33,15 +34,13 @@ #include #include -//#define PPM_DEBUG - namespace Gfx { struct PPMLoadingContext { enum Type { Unknown, - P3_ASCII, - P6_RAWBITS + ASCII, + RAWBITS }; enum State { @@ -55,6 +54,10 @@ struct PPMLoadingContext { 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 }; @@ -65,178 +68,12 @@ struct PPMLoadingContext { RefPtr bitmap; }; -ALWAYS_INLINE static Color adjust_color(u16 max_val, Color& color) -{ - color.set_red((color.red() * 255) / max_val); - color.set_green((color.green() * 255) / max_val); - color.set_blue((color.blue() * 255) / max_val); - - return color; -} - -static bool read_number(Streamer& streamer, u16* value) -{ - u8 byte; - StringBuilder sb; - - while (streamer.read(byte)) { - if (byte == ' ' || byte == '\t' || byte == '\n' || byte == '\r') { - streamer.step_back(); - break; - } - - sb.append(byte); - } - - auto opt_value = sb.to_string().to_uint(); - if (!opt_value.has_value()) { - return false; - } - - *value = (u16)opt_value.value(); - return true; -} - -static bool read_comment([[maybe_unused]] PPMLoadingContext& context, Streamer& streamer) -{ - bool exist = false; - u8 byte; - - while (streamer.read(byte)) { - switch (byte) { - case '#': { - exist = true; - break; - } - case '\t': - case '\n': { - return exist; - } - default: - break; - } - } - - return exist; -} - -static bool read_magic_number(PPMLoadingContext& context, Streamer& streamer) -{ - if (context.state >= PPMLoadingContext::MagicNumber) - return true; - - if (!context.data || context.data_size < 2) { - context.state = PPMLoadingContext::State::Error; -#ifdef PPM_DEBUG - dbg() << "There is not enough data."; -#endif - return false; - } - - u8 magic_number[2]; - if (!streamer.read_bytes(magic_number, 2)) { - context.state = PPMLoadingContext::State::Error; -#ifdef PPM_DEBUG - dbg() << "We can't read magic number."; -#endif - return false; - } - - if (magic_number[0] == 'P' && magic_number[1] == '3') { - context.type = PPMLoadingContext::P3_ASCII; - context.state = PPMLoadingContext::MagicNumber; - return true; - } - - if (magic_number[0] == 'P' && magic_number[1] == '6') { - context.type = PPMLoadingContext::P6_RAWBITS; - context.state = PPMLoadingContext::MagicNumber; - return true; - } - - context.state = PPMLoadingContext::State::Error; -#ifdef PPM_DEBUG - dbg() << "Magic number is not valid." << (char)magic_number[0] << (char)magic_number[1]; -#endif - return false; -} - -static bool read_white_space(PPMLoadingContext& context, Streamer& streamer) -{ - bool exist = false; - u8 byte; - - while (streamer.read(byte)) { - switch (byte) { - case ' ': - case '\t': - case '\n': - case '\r': { - exist = true; - break; - } - case '#': { - streamer.step_back(); - read_comment(context, streamer); - break; - } - default: { - streamer.step_back(); - return exist; - } - } - } - - return exist; -} - -static bool read_width(PPMLoadingContext& context, Streamer& streamer) -{ - bool result = read_number(streamer, &context.width); - if (!result || context.width == 0) { - return false; - } - - context.state = PPMLoadingContext::Width; - return true; -} - -static bool read_height(PPMLoadingContext& context, Streamer& streamer) -{ - bool result = read_number(streamer, &context.height); - if (!result || context.height == 0) { - return false; - } - - context.state = PPMLoadingContext::Height; - return true; -} - -static bool read_max_val(PPMLoadingContext& context, Streamer& streamer) -{ - bool result = read_number(streamer, &context.max_val); - if (!result || context.max_val == 0) { - return false; - } - - if (context.max_val > 255) { -#ifdef PPM_DEBUG - dbg() << "We can't parse 2 byte color."; -#endif - context.state = PPMLoadingContext::Error; - return false; - } - - context.state = PPMLoadingContext::Maxval; - return true; -} - static bool read_image_data(PPMLoadingContext& context, Streamer& streamer) { Vector color_data; color_data.ensure_capacity(context.width * context.height); - if (context.type == PPMLoadingContext::P3_ASCII) { + if (context.type == PPMLoadingContext::ASCII) { u16 red; u16 green; u16 blue; @@ -265,7 +102,7 @@ static bool read_image_data(PPMLoadingContext& context, Streamer& streamer) color = adjust_color(context.max_val, color); color_data.append(color); } - } else if (context.type == PPMLoadingContext::P6_RAWBITS) { + } else if (context.type == PPMLoadingContext::RAWBITS) { u8 pixel[3]; while (streamer.read_bytes(pixel, 3)) { color_data.append({ pixel[0], pixel[1], pixel[2] }); @@ -275,95 +112,24 @@ static bool read_image_data(PPMLoadingContext& context, Streamer& streamer) if (context.width * context.height != color_data.size()) return false; - context.bitmap = Bitmap::create_purgeable(BitmapFormat::RGB32, { context.width, context.height }); - if (!context.bitmap) { - context.state = PPMLoadingContext::State::Error; + if (!create_bitmap(context)) { return false; } - size_t index = 0; - for (int y = 0; y < context.height; ++y) { - for (int x = 0; x < context.width; ++x) { - context.bitmap->set_pixel(x, y, color_data.at(index)); - index++; - } - } + set_pixels(context, color_data); context.state = PPMLoadingContext::State::Bitmap; return true; } -static bool decode_ppm(PPMLoadingContext& context) -{ - if (context.state >= PPMLoadingContext::State::Decoded) - return true; - - auto error_guard = ArmedScopeGuard([&] { - context.state = PPMLoadingContext::State::Error; - }); - - Streamer streamer(context.data, context.data_size); - - if (!read_magic_number(context, streamer)) - return false; - - if (!read_white_space(context, streamer)) - return false; - - if (!read_width(context, streamer)) - return false; - - if (!read_white_space(context, streamer)) - return false; - - if (!read_height(context, streamer)) - return false; - - if (!read_white_space(context, streamer)) - return false; - - if (!read_max_val(context, streamer)) - return false; - - if (!read_white_space(context, streamer)) - return false; - - if (!read_image_data(context, streamer)) - return false; - - error_guard.disarm(); - context.state = PPMLoadingContext::State::Decoded; - return true; -} - -static RefPtr load_ppm_impl(const u8* data, size_t data_size) -{ - PPMLoadingContext context; - context.data = data; - context.data_size = data_size; - - if (!decode_ppm(context)) - return nullptr; - - return context.bitmap; -} - RefPtr load_ppm(const StringView& path) { - MappedFile mapped_file(path); - if (!mapped_file.is_valid()) { - return nullptr; - } - - auto bitmap = load_ppm_impl((const u8*)mapped_file.data(), mapped_file.size()); - if (bitmap) - bitmap->set_mmap_name(String::format("Gfx::Bitmap [%dx%d] - Decoded PPM: %s", bitmap->width(), bitmap->height(), LexicalPath::canonicalized_path(path).characters())); - return bitmap; + return load(path); } RefPtr load_ppm_from_memory(const u8* data, size_t length) { - auto bitmap = load_ppm_impl(data, length); + auto bitmap = load_impl(data, length); if (bitmap) bitmap->set_mmap_name(String::format("Gfx::Bitmap [%dx%d] - Decoded PPM: ", bitmap->width(), bitmap->height())); return bitmap; @@ -386,7 +152,7 @@ IntSize PPMImageDecoderPlugin::size() return {}; if (m_context->state < PPMLoadingContext::State::Decoded) { - bool success = decode_ppm(*m_context); + bool success = decode(*m_context); if (!success) return {}; } @@ -400,7 +166,7 @@ RefPtr PPMImageDecoderPlugin::bitmap() return nullptr; if (m_context->state < PPMLoadingContext::State::Decoded) { - bool success = decode_ppm(*m_context); + bool success = decode(*m_context); if (!success) return nullptr; } diff --git a/Libraries/LibGfx/PortableImageLoaderCommon.h b/Libraries/LibGfx/PortableImageLoaderCommon.h new file mode 100644 index 00000000000..a95c2127c5a --- /dev/null +++ b/Libraries/LibGfx/PortableImageLoaderCommon.h @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2020, the SerenityOS developers, Hüseyin ASLITÜRK + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "Streamer.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define PORTABLE_IMAGE_LOADER_DEBUG + +namespace Gfx { + +static constexpr Color adjust_color(u16 max_val, Color color) +{ + color.set_red((color.red() * 255) / max_val); + color.set_green((color.green() * 255) / max_val); + color.set_blue((color.blue() * 255) / max_val); + + return color; +} + +template +static bool read_number(Streamer& streamer, TValue* value) +{ + u8 byte {}; + StringBuilder sb {}; + + while (streamer.read(byte)) { + if (byte == ' ' || byte == '\t' || byte == '\n' || byte == '\r') { + streamer.step_back(); + break; + } + + sb.append(byte); + } + + const auto opt_value = sb.to_string().to_uint(); + if (!opt_value.has_value()) { + *value = 0; + return false; + } + + *value = static_cast(opt_value.value()); + return true; +} + +template +static bool read_comment([[maybe_unused]] TContext& context, Streamer& streamer) +{ + bool exist = false; + u8 byte {}; + + while (streamer.read(byte)) { + if (byte == '#') { + exist = true; + } else if (byte == '\t' || byte == '\n') { + return exist; + } + } + + return exist; +} + +template +static bool read_magic_number(TContext& context, Streamer& streamer) +{ + if (context.state >= TContext::State::MagicNumber) { + return true; + } + + if (!context.data || context.data_size < 2) { + context.state = TContext::State::Error; +#ifdef PORTABLE_IMAGE_LOADER_DEBUG + dbg() << "There is no enough data for " << TContext::image_type; +#endif + return false; + } + + u8 magic_number[2] {}; + if (!streamer.read_bytes(magic_number, 2)) { + context.state = TContext::State::Error; +#ifdef PORTABLE_IMAGE_LOADER_DEBUG + dbg() << "We can't read magic number for " << TContext::image_type; +#endif + return false; + } + + if (magic_number[0] == 'P' && magic_number[1] == TContext::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) { + context.type = TContext::Type::RAWBITS; + context.state = TContext::State::MagicNumber; + return true; + } + + context.state = TContext::State::Error; +#ifdef PORTABLE_IMAGE_LOADER_DEBUG + dbg() << "Magic number is not valid for " + << magic_number[0] << magic_number[1] << TContext::image_type; +#endif + return false; +} + +template +static bool read_white_space(TContext& context, Streamer& streamer) +{ + bool exist = false; + u8 byte {}; + + while (streamer.read(byte)) { + if (byte == ' ' || byte == '\t' || byte == '\n' || byte == '\r') { + exist = true; + } else if (byte == '#') { + streamer.step_back(); + read_comment(context, streamer); + } else { + streamer.step_back(); + return exist; + } + } + + return exist; +} + +template +static bool read_width(TContext& context, Streamer& streamer) +{ + if (const bool result = read_number(streamer, &context.width); + !result || context.width == 0) { + return false; + } + + context.state = TContext::Width; + return true; +} + +template +static bool read_height(TContext& context, Streamer& streamer) +{ + if (const bool result = read_number(streamer, &context.height); + !result || context.height == 0) { + return false; + } + + context.state = TContext::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) { + return false; + } + + if (context.max_val > 255) { +#ifdef PORTABLE_IMAGE_LOADER_DEBUG + dbg() << "We can't parse 2 byte color for " << TContext::image_type; +#endif + context.state = TContext::Error; + return false; + } + + context.state = TContext::Maxval; + return true; +} + +template +static bool create_bitmap(TContext& context) +{ + context.bitmap = Bitmap::create_purgeable(BitmapFormat::RGB32, { context.width, context.height }); + if (!context.bitmap) { + context.state = TContext::State::Error; + return false; + } + return true; +} + +template +static void set_adjusted_pixels(TContext& context, const AK::Vector& color_data) +{ + size_t index = 0; + for (int y = 0; y < context.height; ++y) { + for (int x = 0; x < context.width; ++x) { + Color color = color_data.at(index); + if (context.max_val < 255) { + color = adjust_color(context.max_val, color); + } + context.bitmap->set_pixel(x, y, color); + index++; + } + } +} + +template +static void set_pixels(TContext& context, const AK::Vector& color_data) +{ + size_t index = 0; + for (int y = 0; y < context.height; ++y) { + for (int x = 0; x < context.width; ++x) { + context.bitmap->set_pixel(x, y, color_data.at(index)); + index++; + } + } +} + +template +static bool decode(TContext& context) +{ + if (context.state >= TContext::State::Decoded) + return true; + + auto error_guard = ArmedScopeGuard([&] { + context.state = TContext::State::Error; + }); + + Streamer streamer(context.data, context.data_size); + + if (!read_magic_number(context, streamer)) + return false; + + if (!read_white_space(context, streamer)) + return false; + + if (!read_width(context, streamer)) + return false; + + if (!read_white_space(context, streamer)) + return false; + + if (!read_height(context, streamer)) + return false; + + if (!read_white_space(context, streamer)) + return false; + + if constexpr (requires { context.max_val; }) { + if (!read_max_val(context, streamer)) + return false; + + if (!read_white_space(context, streamer)) + return false; + } + + if (!read_image_data(context, streamer)) + return false; + + error_guard.disarm(); + context.state = TContext::State::Decoded; + return true; +} + +template +static RefPtr load_impl(const u8* data, size_t data_size) +{ + TContext context {}; + context.data = data; + context.data_size = data_size; + + if (!decode(context)) { + return nullptr; + } + return context.bitmap; +} +template +static RefPtr load(const StringView& path) +{ + MappedFile mapped_file(path); + if (!mapped_file.is_valid()) { + return nullptr; + } + + auto bitmap = load_impl((const u8*)mapped_file.data(), mapped_file.size()); + if (bitmap) + bitmap->set_mmap_name(String::format("Gfx::Bitmap [%dx%d] - Decoded %s: %s", + bitmap->width(), bitmap->height(), + TContext::image_type, + LexicalPath::canonicalized_path(path).characters())); + return bitmap; +} + +}