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

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.
This commit is contained in:
Lenny Maiorani 2022-03-12 11:16:30 -07:00 committed by Andreas Kling
parent 5c21f963ff
commit 786b02730c
8 changed files with 109 additions and 124 deletions

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (c) 2020, Hüseyin ASLITÜRK <asliturk@hotmail.com> * Copyright (c) 2020, Hüseyin ASLITÜRK <asliturk@hotmail.com>
* Copyright (c) 2022, the SerenityOS developers.
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
@ -12,42 +13,12 @@
namespace Gfx { 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<Gfx::Bitmap> bitmap;
};
static bool read_image_data(PBMLoadingContext& context, Streamer& streamer) static bool read_image_data(PBMLoadingContext& context, Streamer& streamer)
{ {
u8 byte; u8 byte;
Vector<Gfx::Color> color_data; Vector<Gfx::Color> color_data;
if (context.type == PBMLoadingContext::ASCII) { if (context.type == PBMLoadingContext::Type::ASCII) {
while (streamer.read(byte)) { while (streamer.read(byte)) {
if (byte == '0') { if (byte == '0') {
color_data.append(Color::White); color_data.append(Color::White);
@ -55,7 +26,7 @@ static bool read_image_data(PBMLoadingContext& context, Streamer& streamer)
color_data.append(Color::Black); color_data.append(Color::Black);
} }
} }
} else if (context.type == PBMLoadingContext::RAWBITS) { } else if (context.type == PBMLoadingContext::Type::RAWBITS) {
size_t color_index = 0; size_t color_index = 0;
while (streamer.read(byte)) { while (streamer.read(byte)) {

View file

@ -1,16 +1,25 @@
/* /*
* Copyright (c) 2020, Hüseyin ASLITÜRK <asliturk@hotmail.com> * Copyright (c) 2020, Hüseyin ASLITÜRK <asliturk@hotmail.com>
* Copyright (c) 2022, the SerenityOS developers.
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#pragma once #pragma once
#include <AK/StringView.h>
#include <LibGfx/ImageDecoder.h> #include <LibGfx/ImageDecoder.h>
#include <LibGfx/PortableImageMapLoader.h>
namespace Gfx { 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<PBM>;
class PBMImageDecoderPlugin final : public ImageDecoderPlugin { class PBMImageDecoderPlugin final : public ImageDecoderPlugin {
public: public:

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (c) 2020, Hüseyin ASLITÜRK <asliturk@hotmail.com> * Copyright (c) 2020, Hüseyin ASLITÜRK <asliturk@hotmail.com>
* Copyright (c) 2022, the SerenityOS developers.
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
@ -12,46 +13,14 @@
namespace Gfx { 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<Gfx::Bitmap> bitmap;
};
static void set_adjusted_pixels(PGMLoadingContext& context, const Vector<Gfx::Color>& color_data) static void set_adjusted_pixels(PGMLoadingContext& context, const Vector<Gfx::Color>& color_data)
{ {
size_t index = 0; size_t index = 0;
for (size_t y = 0; y < context.height; ++y) { for (size_t y = 0; y < context.height; ++y) {
for (size_t x = 0; x < context.width; ++x) { for (size_t x = 0; x < context.width; ++x) {
Color color = color_data.at(index); Color color = color_data.at(index);
if (context.max_val < 255) { if (context.format_details.max_val < 255) {
color = adjust_color(context.max_val, color); color = adjust_color(context.format_details.max_val, color);
} }
context.bitmap->set_pixel(x, y, color); context.bitmap->set_pixel(x, y, color);
++index; ++index;
@ -63,7 +32,7 @@ static bool read_image_data(PGMLoadingContext& context, Streamer& streamer)
{ {
Vector<Gfx::Color> color_data; Vector<Gfx::Color> color_data;
if (context.type == PGMLoadingContext::ASCII) { if (context.type == PGMLoadingContext::Type::ASCII) {
u16 value; u16 value;
while (true) { while (true) {
@ -75,7 +44,7 @@ static bool read_image_data(PGMLoadingContext& context, Streamer& streamer)
color_data.append({ (u8)value, (u8)value, (u8)value }); color_data.append({ (u8)value, (u8)value, (u8)value });
} }
} else if (context.type == PGMLoadingContext::RAWBITS) { } else if (context.type == PGMLoadingContext::Type::RAWBITS) {
u8 pixel; u8 pixel;
while (streamer.read(pixel)) { while (streamer.read(pixel)) {
color_data.append({ pixel, pixel, pixel }); color_data.append({ pixel, pixel, pixel });

View file

@ -1,16 +1,26 @@
/* /*
* Copyright (c) 2020, Hüseyin ASLITÜRK <asliturk@hotmail.com> * Copyright (c) 2020, Hüseyin ASLITÜRK <asliturk@hotmail.com>
* Copyright (c) 2022, the SerenityOS developers.
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#pragma once #pragma once
#include <AK/StringView.h>
#include <LibGfx/ImageDecoder.h> #include <LibGfx/ImageDecoder.h>
#include <LibGfx/PortableImageMapLoader.h>
namespace Gfx { 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<PGM>;
class PGMImageDecoderPlugin final : public ImageDecoderPlugin { class PGMImageDecoderPlugin final : public ImageDecoderPlugin {
public: public:

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (c) 2020, Hüseyin ASLITÜRK <asliturk@hotmail.com> * Copyright (c) 2020, Hüseyin ASLITÜRK <asliturk@hotmail.com>
* Copyright (c) 2022, the SerenityOS developers.
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
@ -15,44 +16,12 @@
namespace Gfx { 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<Gfx::Bitmap> bitmap;
};
static bool read_image_data(PPMLoadingContext& context, Streamer& streamer) static bool read_image_data(PPMLoadingContext& context, Streamer& streamer)
{ {
Vector<Gfx::Color> color_data; Vector<Gfx::Color> color_data;
color_data.ensure_capacity(context.width * context.height); color_data.ensure_capacity(context.width * context.height);
if (context.type == PPMLoadingContext::ASCII) { if (context.type == PPMLoadingContext::Type::ASCII) {
u16 red; u16 red;
u16 green; u16 green;
u16 blue; u16 blue;
@ -77,11 +46,11 @@ static bool read_image_data(PPMLoadingContext& context, Streamer& streamer)
break; break;
Color color { (u8)red, (u8)green, (u8)blue }; Color color { (u8)red, (u8)green, (u8)blue };
if (context.max_val < 255) if (context.format_details.max_val < 255)
color = adjust_color(context.max_val, color); color = adjust_color(context.format_details.max_val, color);
color_data.append(color); color_data.append(color);
} }
} else if (context.type == PPMLoadingContext::RAWBITS) { } else if (context.type == PPMLoadingContext::Type::RAWBITS) {
u8 pixel[3]; u8 pixel[3];
while (streamer.read_bytes(pixel, 3)) { while (streamer.read_bytes(pixel, 3)) {
color_data.append({ pixel[0], pixel[1], pixel[2] }); color_data.append({ pixel[0], pixel[1], pixel[2] });

View file

@ -1,16 +1,26 @@
/* /*
* Copyright (c) 2020, Hüseyin ASLITÜRK <asliturk@hotmail.com> * Copyright (c) 2020, Hüseyin ASLITÜRK <asliturk@hotmail.com>
* Copyright (c) 2022, the SerenityOS developers.
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#pragma once #pragma once
#include <AK/StringView.h>
#include <LibGfx/ImageDecoder.h> #include <LibGfx/ImageDecoder.h>
#include <LibGfx/PortableImageMapLoader.h>
namespace Gfx { 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<PPM>;
class PPMImageDecoderPlugin final : public ImageDecoderPlugin { class PPMImageDecoderPlugin final : public ImageDecoderPlugin {
public: public:

View file

@ -1,13 +1,12 @@
/* /*
* Copyright (c) 2020, Hüseyin Aslıtürk <asliturk@hotmail.com> * Copyright (c) 2020, Hüseyin Aslıtürk <asliturk@hotmail.com>
* Copyright (c) 2020, the SerenityOS developers. * Copyright (c) 2020-2022, the SerenityOS developers.
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#pragma once #pragma once
#include <AK/Array.h>
#include <AK/Debug.h> #include <AK/Debug.h>
#include <AK/Endian.h> #include <AK/Endian.h>
#include <AK/ScopeGuard.h> #include <AK/ScopeGuard.h>
@ -82,31 +81,31 @@ static bool read_magic_number(TContext& context, Streamer& streamer)
if (!context.data || context.data_size < 2) { if (!context.data || context.data_size < 2) {
context.state = TContext::State::Error; 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; return false;
} }
u8 magic_number[2] {}; u8 magic_number[2] {};
if (!streamer.read_bytes(magic_number, 2)) { if (!streamer.read_bytes(magic_number, 2)) {
context.state = TContext::State::Error; 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; 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.type = TContext::Type::ASCII;
context.state = TContext::State::MagicNumber; context.state = TContext::State::MagicNumber;
return true; 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.type = TContext::Type::RAWBITS;
context.state = TContext::State::MagicNumber; context.state = TContext::State::MagicNumber;
return true; return true;
} }
context.state = TContext::State::Error; 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; return false;
} }
@ -139,7 +138,7 @@ static bool read_width(TContext& context, Streamer& streamer)
return false; return false;
} }
context.state = TContext::Width; context.state = TContext::State::Width;
return true; return true;
} }
@ -151,25 +150,25 @@ static bool read_height(TContext& context, Streamer& streamer)
return false; return false;
} }
context.state = TContext::Height; context.state = TContext::State::Height;
return true; return true;
} }
template<typename TContext> template<typename TContext>
static bool read_max_val(TContext& context, Streamer& streamer) static bool read_max_val(TContext& context, Streamer& streamer)
{ {
if (const bool result = read_number(streamer, &context.max_val); if (const bool result = read_number(streamer, &context.format_details.max_val);
!result || context.max_val == 0) { !result || context.format_details.max_val == 0) {
return false; return false;
} }
if (context.max_val > 255) { if (context.format_details.max_val > 255) {
dbgln_if(PORTABLE_IMAGE_LOADER_DEBUG, "We can't parse 2 byte color for {}", TContext::image_type); dbgln_if(PORTABLE_IMAGE_LOADER_DEBUG, "We can't parse 2 byte color for {}", TContext::FormatDetails::image_type);
context.state = TContext::Error; context.state = TContext::State::Error;
return false; return false;
} }
context.state = TContext::Maxval; context.state = TContext::State::Maxval;
return true; return true;
} }
@ -232,7 +231,7 @@ static bool decode(TContext& context)
if (!read_whitespace(context, streamer)) if (!read_whitespace(context, streamer))
return false; return false;
if constexpr (requires { context.max_val; }) { if constexpr (requires { context.format_details.max_val; }) {
if (!read_max_val(context, streamer)) if (!read_max_val(context, streamer))
return false; return false;
@ -266,7 +265,7 @@ static RefPtr<Gfx::Bitmap> load_from_memory(u8 const* data, size_t length, Strin
{ {
auto bitmap = load_impl<TContext>(data, length); auto bitmap = load_impl<TContext>(data, length);
if (bitmap) 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; return bitmap;
} }

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2020, Hüseyin ASLITÜRK <asliturk@hotmail.com>
* Copyright (c) 2022, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/RefPtr.h>
#include <AK/StringView.h>
#include <AK/Types.h>
#include <LibGfx/Bitmap.h>
namespace Gfx {
template<class TFormatDetails>
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<Gfx::Bitmap> bitmap;
};
}