LibGfx/PortableFormat: Port to Stream
Each one of `[PBM, PGM, PPM]Loader` used yet another stream-like relic. This patch ports all of them to `AK::Stream`.
This commit is contained in:
parent
b9574c180e
commit
7cafd7d177
Notes:
sideshowbarker
2024-07-17 00:53:02 +09:00
Author: https://github.com/LucasChollet Commit: https://github.com/SerenityOS/serenity/commit/7cafd7d177 Pull-request: https://github.com/SerenityOS/serenity/pull/17831 Reviewed-by: https://github.com/kleinesfilmroellchen ✅ Reviewed-by: https://github.com/nico
8 changed files with 88 additions and 79 deletions
|
@ -6,15 +6,13 @@
|
|||
*/
|
||||
|
||||
#include "PBMLoader.h"
|
||||
#include "AK/Endian.h"
|
||||
#include "PortableImageLoaderCommon.h"
|
||||
#include "Userland/Libraries/LibGfx/Streamer.h"
|
||||
#include <string.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
bool read_image_data(PBMLoadingContext& context, Streamer& streamer)
|
||||
bool read_image_data(PBMLoadingContext& context)
|
||||
{
|
||||
auto& stream = *context.stream;
|
||||
Vector<Gfx::Color> color_data;
|
||||
|
||||
auto const context_size = context.width * context.height;
|
||||
|
@ -22,9 +20,10 @@ bool read_image_data(PBMLoadingContext& context, Streamer& streamer)
|
|||
|
||||
if (context.type == PBMLoadingContext::Type::ASCII) {
|
||||
for (u64 i = 0; i < context_size; ++i) {
|
||||
u8 byte;
|
||||
if (!streamer.read(byte))
|
||||
auto byte_or_error = stream.read_value<u8>();
|
||||
if (byte_or_error.is_error())
|
||||
return false;
|
||||
auto const byte = byte_or_error.value();
|
||||
if (byte == '0')
|
||||
color_data[i] = Color::White;
|
||||
else if (byte == '1')
|
||||
|
@ -34,11 +33,12 @@ bool read_image_data(PBMLoadingContext& context, Streamer& streamer)
|
|||
}
|
||||
} else if (context.type == PBMLoadingContext::Type::RAWBITS) {
|
||||
for (u64 color_index = 0; color_index < context_size;) {
|
||||
u8 byte;
|
||||
if (!streamer.read(byte))
|
||||
auto byte_or_error = stream.read_value<u8>();
|
||||
if (byte_or_error.is_error())
|
||||
return false;
|
||||
auto byte = byte_or_error.value();
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int val = byte & 0x80;
|
||||
auto const val = byte & 0x80;
|
||||
|
||||
if (val == 0)
|
||||
color_data[color_index] = Color::White;
|
||||
|
|
|
@ -22,5 +22,5 @@ struct PBM {
|
|||
using PBMLoadingContext = PortableImageMapLoadingContext<PBM>;
|
||||
using PBMImageDecoderPlugin = PortableImageDecoderPlugin<PBMLoadingContext>;
|
||||
|
||||
bool read_image_data(PBMLoadingContext& context, Streamer& streamer);
|
||||
bool read_image_data(PBMLoadingContext& context);
|
||||
}
|
||||
|
|
|
@ -5,11 +5,8 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Endian.h>
|
||||
#include <LibGfx/ImageFormats/PGMLoader.h>
|
||||
#include <LibGfx/ImageFormats/PortableImageLoaderCommon.h>
|
||||
#include <LibGfx/Streamer.h>
|
||||
#include <string.h>
|
||||
#include "PGMLoader.h"
|
||||
#include "PortableImageLoaderCommon.h"
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
@ -28,8 +25,9 @@ static void set_adjusted_pixels(PGMLoadingContext& context, Vector<Gfx::Color> c
|
|||
}
|
||||
}
|
||||
|
||||
bool read_image_data(PGMLoadingContext& context, Streamer& streamer)
|
||||
bool read_image_data(PGMLoadingContext& context)
|
||||
{
|
||||
auto& stream = *context.stream;
|
||||
Vector<Gfx::Color> color_data;
|
||||
auto const context_size = context.width * context.height;
|
||||
|
||||
|
@ -37,21 +35,22 @@ bool read_image_data(PGMLoadingContext& context, Streamer& streamer)
|
|||
|
||||
if (context.type == PGMLoadingContext::Type::ASCII) {
|
||||
for (u64 i = 0; i < context_size; ++i) {
|
||||
auto number_or_error = read_number(streamer);
|
||||
auto number_or_error = read_number(stream);
|
||||
if (number_or_error.is_error())
|
||||
return false;
|
||||
auto value = number_or_error.value();
|
||||
|
||||
if (read_whitespace(context, streamer).is_error())
|
||||
if (read_whitespace(context).is_error())
|
||||
return false;
|
||||
|
||||
color_data[i] = { (u8)value, (u8)value, (u8)value };
|
||||
}
|
||||
} else if (context.type == PGMLoadingContext::Type::RAWBITS) {
|
||||
for (u64 i = 0; i < context_size; ++i) {
|
||||
u8 pixel;
|
||||
if (!streamer.read(pixel))
|
||||
auto pixel_or_error = stream.read_value<u8>();
|
||||
if (pixel_or_error.is_error())
|
||||
return false;
|
||||
auto const pixel = pixel_or_error.value();
|
||||
color_data[i] = { pixel, pixel, pixel };
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,5 +23,5 @@ struct PGM {
|
|||
using PGMLoadingContext = PortableImageMapLoadingContext<PGM>;
|
||||
using PGMImageDecoderPlugin = PortableImageDecoderPlugin<PGMLoadingContext>;
|
||||
|
||||
bool read_image_data(PGMLoadingContext& context, Streamer& streamer);
|
||||
bool read_image_data(PGMLoadingContext& context);
|
||||
}
|
||||
|
|
|
@ -7,42 +7,38 @@
|
|||
|
||||
#include "PPMLoader.h"
|
||||
#include "PortableImageLoaderCommon.h"
|
||||
#include <AK/Endian.h>
|
||||
#include <AK/LexicalPath.h>
|
||||
#include <AK/ScopeGuard.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <LibGfx/Streamer.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
bool read_image_data(PPMLoadingContext& context, Streamer& streamer)
|
||||
bool read_image_data(PPMLoadingContext& context)
|
||||
{
|
||||
Vector<Gfx::Color> color_data;
|
||||
auto const context_size = context.width * context.height;
|
||||
color_data.resize(context_size);
|
||||
|
||||
auto& stream = *context.stream;
|
||||
|
||||
if (context.type == PPMLoadingContext::Type::ASCII) {
|
||||
for (u64 i = 0; i < context_size; ++i) {
|
||||
auto const red_or_error = read_number(streamer);
|
||||
auto const red_or_error = read_number(stream);
|
||||
if (red_or_error.is_error())
|
||||
return false;
|
||||
|
||||
if (read_whitespace(context, streamer).is_error())
|
||||
if (read_whitespace(context).is_error())
|
||||
return false;
|
||||
|
||||
auto const green_or_error = read_number(streamer);
|
||||
auto const green_or_error = read_number(stream);
|
||||
if (green_or_error.is_error())
|
||||
return false;
|
||||
|
||||
if (read_whitespace(context, streamer).is_error())
|
||||
if (read_whitespace(context).is_error())
|
||||
return false;
|
||||
|
||||
auto const blue_or_error = read_number(streamer);
|
||||
auto const blue_or_error = read_number(stream);
|
||||
if (blue_or_error.is_error())
|
||||
return false;
|
||||
|
||||
if (read_whitespace(context, streamer).is_error())
|
||||
if (read_whitespace(context).is_error())
|
||||
return false;
|
||||
|
||||
Color color { (u8)red_or_error.value(), (u8)green_or_error.value(), (u8)blue_or_error.value() };
|
||||
|
@ -52,8 +48,11 @@ bool read_image_data(PPMLoadingContext& context, Streamer& streamer)
|
|||
}
|
||||
} else if (context.type == PPMLoadingContext::Type::RAWBITS) {
|
||||
for (u64 i = 0; i < context_size; ++i) {
|
||||
u8 pixel[3];
|
||||
if (!streamer.read_bytes(pixel, 3))
|
||||
Array<u8, 3> pixel;
|
||||
Bytes buffer { pixel };
|
||||
|
||||
auto const result = stream.read_until_filled(buffer);
|
||||
if (result.is_error())
|
||||
return false;
|
||||
color_data[i] = { pixel[0], pixel[1], pixel[2] };
|
||||
}
|
||||
|
|
|
@ -23,5 +23,5 @@ struct PPM {
|
|||
using PPMLoadingContext = PortableImageMapLoadingContext<PPM>;
|
||||
using PPMImageDecoderPlugin = PortableImageDecoderPlugin<PPMLoadingContext>;
|
||||
|
||||
bool read_image_data(PPMLoadingContext& context, Streamer& streamer);
|
||||
bool read_image_data(PPMLoadingContext& context);
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/Color.h>
|
||||
#include <LibGfx/ImageFormats/ImageDecoder.h>
|
||||
#include <LibGfx/Streamer.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
|
@ -31,14 +30,14 @@ static constexpr Color adjust_color(u16 max_val, Color color)
|
|||
return color;
|
||||
}
|
||||
|
||||
static inline ErrorOr<u16> read_number(Streamer& streamer)
|
||||
static inline ErrorOr<u16> read_number(SeekableStream& stream)
|
||||
{
|
||||
u8 byte {};
|
||||
StringBuilder sb {};
|
||||
u8 byte {};
|
||||
|
||||
while (streamer.read(byte)) {
|
||||
for (auto buffer = TRY(stream.read_some({ &byte, 1 })); !buffer.is_empty(); buffer = TRY(stream.read_some({ &byte, 1 }))) {
|
||||
if (byte == ' ' || byte == '\t' || byte == '\n' || byte == '\r') {
|
||||
streamer.step_back();
|
||||
TRY(stream.seek(-1, SeekMode::FromCurrentPosition));
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -53,12 +52,13 @@ static inline ErrorOr<u16> read_number(Streamer& streamer)
|
|||
}
|
||||
|
||||
template<typename TContext>
|
||||
static ErrorOr<void> read_comment([[maybe_unused]] TContext& context, Streamer& streamer)
|
||||
static ErrorOr<void> read_comment(TContext& context)
|
||||
{
|
||||
auto& stream = *context.stream;
|
||||
bool is_first_char = true;
|
||||
u8 byte {};
|
||||
|
||||
while (streamer.read(byte)) {
|
||||
while ((byte = TRY(stream.template read_value<u8>()))) {
|
||||
if (is_first_char) {
|
||||
if (byte != '#')
|
||||
return Error::from_string_literal("Can't read comment from stream");
|
||||
|
@ -72,20 +72,20 @@ static ErrorOr<void> read_comment([[maybe_unused]] TContext& context, Streamer&
|
|||
}
|
||||
|
||||
template<typename TContext>
|
||||
static bool read_magic_number(TContext& context, Streamer& streamer)
|
||||
static bool read_magic_number(TContext& context)
|
||||
{
|
||||
if (context.state >= TContext::State::MagicNumber) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!context.data || context.data_size < 2) {
|
||||
if (context.stream->size().release_value_but_fixme_should_propagate_errors() < 2) {
|
||||
context.state = TContext::State::Error;
|
||||
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)) {
|
||||
Array<u8, 2> magic_number {};
|
||||
if (context.stream->read_until_filled(Bytes { magic_number }).is_error()) {
|
||||
context.state = TContext::State::Error;
|
||||
dbgln_if(PORTABLE_IMAGE_LOADER_DEBUG, "We can't read magic number for {}", TContext::FormatDetails::image_type);
|
||||
return false;
|
||||
|
@ -109,19 +109,25 @@ static bool read_magic_number(TContext& context, Streamer& streamer)
|
|||
}
|
||||
|
||||
template<typename TContext>
|
||||
static ErrorOr<void> read_whitespace(TContext& context, Streamer& streamer)
|
||||
static ErrorOr<void> read_whitespace(TContext& context)
|
||||
{
|
||||
auto& stream = *context.stream;
|
||||
bool is_first_char = true;
|
||||
u8 byte {};
|
||||
|
||||
while (streamer.read(byte)) {
|
||||
while (true) {
|
||||
auto byte_or_error = stream.template read_value<u8>();
|
||||
// Nothing went wrong if we reached eof while reading a comment.
|
||||
if (byte_or_error.is_error())
|
||||
return {};
|
||||
auto const byte = byte_or_error.value();
|
||||
|
||||
if (byte == '#') {
|
||||
streamer.step_back();
|
||||
TRY(read_comment(context, streamer));
|
||||
stream.seek(-1, SeekMode::FromCurrentPosition).release_value_but_fixme_should_propagate_errors();
|
||||
TRY(read_comment(context));
|
||||
continue;
|
||||
}
|
||||
if (byte != ' ' && byte != '\t' && byte != '\n' && byte != '\r') {
|
||||
streamer.step_back();
|
||||
stream.seek(-1, SeekMode::FromCurrentPosition).release_value_but_fixme_should_propagate_errors();
|
||||
if (is_first_char)
|
||||
return Error::from_string_literal("Can't read whitespace from stream");
|
||||
break;
|
||||
|
@ -135,25 +141,25 @@ static ErrorOr<void> read_whitespace(TContext& context, Streamer& streamer)
|
|||
}
|
||||
|
||||
template<typename TContext>
|
||||
static ErrorOr<void> read_width(TContext& context, Streamer& streamer)
|
||||
static ErrorOr<void> read_width(TContext& context)
|
||||
{
|
||||
context.width = TRY(read_number(streamer));
|
||||
context.width = TRY(read_number(*context.stream));
|
||||
context.state = TContext::State::Width;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename TContext>
|
||||
static ErrorOr<void> read_height(TContext& context, Streamer& streamer)
|
||||
static ErrorOr<void> read_height(TContext& context)
|
||||
{
|
||||
context.height = TRY(read_number(streamer));
|
||||
context.height = TRY(read_number(*context.stream));
|
||||
context.state = TContext::State::Height;
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename TContext>
|
||||
static ErrorOr<void> read_max_val(TContext& context, Streamer& streamer)
|
||||
static ErrorOr<void> read_max_val(TContext& context)
|
||||
{
|
||||
context.format_details.max_val = TRY(read_number(streamer));
|
||||
context.format_details.max_val = TRY(read_number(*context.stream));
|
||||
|
||||
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);
|
||||
|
@ -199,21 +205,19 @@ static bool decode(TContext& context)
|
|||
context.state = TContext::State::Error;
|
||||
});
|
||||
|
||||
Streamer streamer(context.data, context.data_size);
|
||||
|
||||
if (!read_magic_number(context, streamer))
|
||||
if (!read_magic_number(context))
|
||||
return false;
|
||||
|
||||
if (read_whitespace(context, streamer).is_error())
|
||||
if (read_whitespace(context).is_error())
|
||||
return false;
|
||||
|
||||
if (read_width(context, streamer).is_error())
|
||||
if (read_width(context).is_error())
|
||||
return false;
|
||||
|
||||
if (read_whitespace(context, streamer).is_error())
|
||||
if (read_whitespace(context).is_error())
|
||||
return false;
|
||||
|
||||
if (read_height(context, streamer).is_error())
|
||||
if (read_height(context).is_error())
|
||||
return false;
|
||||
|
||||
if (context.width > maximum_width_for_decoded_images || context.height > maximum_height_for_decoded_images) {
|
||||
|
@ -221,18 +225,18 @@ static bool decode(TContext& context)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (read_whitespace(context, streamer).is_error())
|
||||
if (read_whitespace(context).is_error())
|
||||
return false;
|
||||
|
||||
if constexpr (requires { context.format_details.max_val; }) {
|
||||
if (read_max_val(context, streamer).is_error())
|
||||
if (read_max_val(context).is_error())
|
||||
return false;
|
||||
|
||||
if (read_whitespace(context, streamer).is_error())
|
||||
if (read_whitespace(context).is_error())
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!read_image_data(context, streamer))
|
||||
if (!read_image_data(context))
|
||||
return false;
|
||||
|
||||
error_guard.disarm();
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/BufferedStream.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <AK/RefPtr.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <AK/Types.h>
|
||||
|
@ -38,12 +40,18 @@ struct PortableImageMapLoadingContext {
|
|||
|
||||
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;
|
||||
|
||||
NonnullOwnPtr<SeekableStream> stream;
|
||||
|
||||
PortableImageMapLoadingContext(NonnullOwnPtr<SeekableStream> stream)
|
||||
: stream(move(stream))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TContext>
|
||||
|
@ -67,17 +75,15 @@ public:
|
|||
virtual ErrorOr<Optional<ReadonlyBytes>> icc_data() override;
|
||||
|
||||
private:
|
||||
PortableImageDecoderPlugin(u8 const*, size_t);
|
||||
PortableImageDecoderPlugin(NonnullOwnPtr<SeekableStream> stream);
|
||||
|
||||
OwnPtr<TContext> m_context;
|
||||
};
|
||||
|
||||
template<typename TContext>
|
||||
PortableImageDecoderPlugin<TContext>::PortableImageDecoderPlugin(u8 const* data, size_t size)
|
||||
PortableImageDecoderPlugin<TContext>::PortableImageDecoderPlugin(NonnullOwnPtr<SeekableStream> stream)
|
||||
{
|
||||
m_context = make<TContext>();
|
||||
m_context->data = data;
|
||||
m_context->data_size = size;
|
||||
m_context = make<TContext>(move(stream));
|
||||
}
|
||||
|
||||
template<typename TContext>
|
||||
|
@ -114,7 +120,8 @@ bool PortableImageDecoderPlugin<TContext>::set_nonvolatile(bool& was_purged)
|
|||
template<typename TContext>
|
||||
ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> PortableImageDecoderPlugin<TContext>::create(ReadonlyBytes data)
|
||||
{
|
||||
return adopt_nonnull_own_or_enomem(new (nothrow) PortableImageDecoderPlugin<TContext>(data.data(), data.size()));
|
||||
auto stream = TRY(try_make<FixedMemoryStream>(data));
|
||||
return adopt_nonnull_own_or_enomem(new (nothrow) PortableImageDecoderPlugin<TContext>(move(stream)));
|
||||
}
|
||||
|
||||
template<typename TContext>
|
||||
|
|
Loading…
Add table
Reference in a new issue