diff --git a/Base/res/html/misc/targasuite_files/buggie-bottom-left-compressed.tga b/Base/res/html/misc/targasuite_files/buggie-bottom-left-compressed.tga deleted file mode 100644 index 187d49bec5c..00000000000 Binary files a/Base/res/html/misc/targasuite_files/buggie-bottom-left-compressed.tga and /dev/null differ diff --git a/Base/res/html/misc/targasuite_files/buggie-bottom-left-uncompressed.tga b/Base/res/html/misc/targasuite_files/buggie-bottom-left-uncompressed.tga deleted file mode 100644 index 5f0a8aaf615..00000000000 Binary files a/Base/res/html/misc/targasuite_files/buggie-bottom-left-uncompressed.tga and /dev/null differ diff --git a/Base/res/html/misc/targasuite_files/buggie-top-left-compressed.tga b/Base/res/html/misc/targasuite_files/buggie-top-left-compressed.tga deleted file mode 100644 index 45cbda95210..00000000000 Binary files a/Base/res/html/misc/targasuite_files/buggie-top-left-compressed.tga and /dev/null differ diff --git a/Base/res/html/misc/targasuite_files/buggie-top-left-uncompressed.tga b/Base/res/html/misc/targasuite_files/buggie-top-left-uncompressed.tga deleted file mode 100644 index 8952ffaf368..00000000000 Binary files a/Base/res/html/misc/targasuite_files/buggie-top-left-uncompressed.tga and /dev/null differ diff --git a/Meta/Lagom/Fuzzers/FuzzTGALoader.cpp b/Meta/Lagom/Fuzzers/FuzzTGALoader.cpp deleted file mode 100644 index 8d458bde1ca..00000000000 --- a/Meta/Lagom/Fuzzers/FuzzTGALoader.cpp +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2022, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size) -{ - AK::set_debug_enabled(false); - auto decoder_or_error = Gfx::TGAImageDecoderPlugin::create({ data, size }); - if (decoder_or_error.is_error()) - return 0; - auto decoder = decoder_or_error.release_value(); - (void)decoder->frame(0); - return 0; -} diff --git a/Meta/Lagom/Fuzzers/fuzzers.cmake b/Meta/Lagom/Fuzzers/fuzzers.cmake index b0926e05e22..2ce2378c3f6 100644 --- a/Meta/Lagom/Fuzzers/fuzzers.cmake +++ b/Meta/Lagom/Fuzzers/fuzzers.cmake @@ -38,7 +38,6 @@ set(FUZZER_TARGETS SHA512 Tar TextDecoder - TGALoader TIFFLoader TTF TinyVGLoader @@ -97,7 +96,6 @@ set(FUZZER_DEPENDENCIES_SHA384 LibCrypto) set(FUZZER_DEPENDENCIES_SHA512 LibCrypto) set(FUZZER_DEPENDENCIES_Tar LibArchive) set(FUZZER_DEPENDENCIES_TextDecoder LibTextCodec) -set(FUZZER_DEPENDENCIES_TGALoader LibGfx) set(FUZZER_DEPENDENCIES_TIFFLoader LibGfx) set(FUZZER_DEPENDENCIES_TTF LibGfx) set(FUZZER_DEPENDENCIES_TinyVGLoader LibGfx) diff --git a/Meta/gn/secondary/Userland/Libraries/LibGfx/BUILD.gn b/Meta/gn/secondary/Userland/Libraries/LibGfx/BUILD.gn index 081003e780f..b1f74899548 100644 --- a/Meta/gn/secondary/Userland/Libraries/LibGfx/BUILD.gn +++ b/Meta/gn/secondary/Userland/Libraries/LibGfx/BUILD.gn @@ -76,7 +76,6 @@ shared_library("LibGfx") { "ImageFormats/PNGLoader.cpp", "ImageFormats/PNGWriter.cpp", "ImageFormats/QMArithmeticDecoder.cpp", - "ImageFormats/TGALoader.cpp", "ImageFormats/TIFFLoader.cpp", "ImageFormats/TinyVGLoader.cpp", "ImageFormats/WebPLoader.cpp", diff --git a/Tests/LibGfx/TestImageDecoder.cpp b/Tests/LibGfx/TestImageDecoder.cpp index 73ca916136a..fa861ce9047 100644 --- a/Tests/LibGfx/TestImageDecoder.cpp +++ b/Tests/LibGfx/TestImageDecoder.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -726,42 +725,6 @@ TEST_CASE(test_png_malformed_frame) } } -TEST_CASE(test_targa_bottom_left) -{ - auto file = TRY_OR_FAIL(Core::MappedFile::map(TEST_INPUT("tga/buggie-bottom-left-uncompressed.tga"sv))); - EXPECT(Gfx::TGAImageDecoderPlugin::validate_before_create(file->bytes())); - auto plugin_decoder = TRY_OR_FAIL(Gfx::TGAImageDecoderPlugin::create(file->bytes())); - - TRY_OR_FAIL(expect_single_frame(*plugin_decoder)); -} - -TEST_CASE(test_targa_top_left) -{ - auto file = TRY_OR_FAIL(Core::MappedFile::map(TEST_INPUT("tga/buggie-top-left-uncompressed.tga"sv))); - EXPECT(Gfx::TGAImageDecoderPlugin::validate_before_create(file->bytes())); - auto plugin_decoder = TRY_OR_FAIL(Gfx::TGAImageDecoderPlugin::create(file->bytes())); - - TRY_OR_FAIL(expect_single_frame(*plugin_decoder)); -} - -TEST_CASE(test_targa_bottom_left_compressed) -{ - auto file = TRY_OR_FAIL(Core::MappedFile::map(TEST_INPUT("tga/buggie-bottom-left-compressed.tga"sv))); - EXPECT(Gfx::TGAImageDecoderPlugin::validate_before_create(file->bytes())); - auto plugin_decoder = TRY_OR_FAIL(Gfx::TGAImageDecoderPlugin::create(file->bytes())); - - TRY_OR_FAIL(expect_single_frame(*plugin_decoder)); -} - -TEST_CASE(test_targa_top_left_compressed) -{ - auto file = TRY_OR_FAIL(Core::MappedFile::map(TEST_INPUT("tga/buggie-top-left-compressed.tga"sv))); - EXPECT(Gfx::TGAImageDecoderPlugin::validate_before_create(file->bytes())); - auto plugin_decoder = TRY_OR_FAIL(Gfx::TGAImageDecoderPlugin::create(file->bytes())); - - TRY_OR_FAIL(expect_single_frame(*plugin_decoder)); -} - TEST_CASE(test_tiff_uncompressed) { auto file = TRY_OR_FAIL(Core::MappedFile::map(TEST_INPUT("tiff/uncompressed.tiff"sv))); diff --git a/Tests/LibGfx/test-inputs/tga/buggie-bottom-left-compressed.tga b/Tests/LibGfx/test-inputs/tga/buggie-bottom-left-compressed.tga deleted file mode 100644 index 187d49bec5c..00000000000 Binary files a/Tests/LibGfx/test-inputs/tga/buggie-bottom-left-compressed.tga and /dev/null differ diff --git a/Tests/LibGfx/test-inputs/tga/buggie-bottom-left-uncompressed.tga b/Tests/LibGfx/test-inputs/tga/buggie-bottom-left-uncompressed.tga deleted file mode 100644 index 5f0a8aaf615..00000000000 Binary files a/Tests/LibGfx/test-inputs/tga/buggie-bottom-left-uncompressed.tga and /dev/null differ diff --git a/Tests/LibGfx/test-inputs/tga/buggie-top-left-compressed.tga b/Tests/LibGfx/test-inputs/tga/buggie-top-left-compressed.tga deleted file mode 100644 index 45cbda95210..00000000000 Binary files a/Tests/LibGfx/test-inputs/tga/buggie-top-left-compressed.tga and /dev/null differ diff --git a/Tests/LibGfx/test-inputs/tga/buggie-top-left-uncompressed.tga b/Tests/LibGfx/test-inputs/tga/buggie-top-left-uncompressed.tga deleted file mode 100644 index 8952ffaf368..00000000000 Binary files a/Tests/LibGfx/test-inputs/tga/buggie-top-left-uncompressed.tga and /dev/null differ diff --git a/Userland/Libraries/LibGfx/CMakeLists.txt b/Userland/Libraries/LibGfx/CMakeLists.txt index df5692165d8..8281f492005 100644 --- a/Userland/Libraries/LibGfx/CMakeLists.txt +++ b/Userland/Libraries/LibGfx/CMakeLists.txt @@ -54,7 +54,6 @@ set(SOURCES ImageFormats/PNGLoader.cpp ImageFormats/PNGWriter.cpp ImageFormats/QMArithmeticDecoder.cpp - ImageFormats/TGALoader.cpp ImageFormats/TIFFLoader.cpp ImageFormats/TinyVGLoader.cpp ImageFormats/WebPLoader.cpp diff --git a/Userland/Libraries/LibGfx/ImageFormats/ImageDecoder.cpp b/Userland/Libraries/LibGfx/ImageFormats/ImageDecoder.cpp index 7669745854f..68cf1e77cd9 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/ImageDecoder.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/ImageDecoder.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -53,39 +52,11 @@ static ErrorOr> probe_and_sniff_for_appropriate_plugi return OwnPtr {}; } -static ErrorOr> probe_and_sniff_for_appropriate_plugin_with_known_mime_type(StringView mime_type, ReadonlyBytes bytes) -{ - struct ImagePluginWithMIMETypeInitializer { - bool (*validate_before_create)(ReadonlyBytes) = nullptr; - ErrorOr> (*create)(ReadonlyBytes) = nullptr; - StringView mime_type; - }; - - static constexpr ImagePluginWithMIMETypeInitializer s_initializers_with_mime_type[] = { - { TGAImageDecoderPlugin::validate_before_create, TGAImageDecoderPlugin::create, "image/x-targa"sv }, - }; - - for (auto& plugin : s_initializers_with_mime_type) { - if (plugin.mime_type != mime_type) - continue; - auto validation_result = plugin.validate_before_create(bytes); - if (!validation_result) - continue; - return TRY(plugin.create(bytes)); - } - return OwnPtr {}; -} - -ErrorOr> ImageDecoder::try_create_for_raw_bytes(ReadonlyBytes bytes, Optional mime_type) +ErrorOr> ImageDecoder::try_create_for_raw_bytes(ReadonlyBytes bytes, [[maybe_unused]] Optional mime_type) { if (auto plugin = TRY(probe_and_sniff_for_appropriate_plugin(bytes)); plugin) return adopt_ref_if_nonnull(new (nothrow) ImageDecoder(plugin.release_nonnull())); - if (mime_type.has_value()) { - if (OwnPtr plugin = TRY(probe_and_sniff_for_appropriate_plugin_with_known_mime_type(mime_type.value(), bytes)); plugin) - return adopt_ref_if_nonnull(new (nothrow) ImageDecoder(plugin.release_nonnull())); - } - return RefPtr {}; } diff --git a/Userland/Libraries/LibGfx/ImageFormats/TGALoader.cpp b/Userland/Libraries/LibGfx/ImageFormats/TGALoader.cpp deleted file mode 100644 index 5e7aff85df7..00000000000 --- a/Userland/Libraries/LibGfx/ImageFormats/TGALoader.cpp +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Copyright (c) 2022, Tom Needham <06needhamt@gmail.com> - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Gfx { - -enum TGADataType : u8 { - None = 0, - UncompressedColorMapped = 1, - UncompressedRGB = 2, - UncompressedBlackAndWhite = 3, - RunLengthEncodedColorMapped = 9, - RunLengthEncodedRGB = 10, - CompressedBlackAndWhite = 11, - CompressedColorMapped = 32, - CompressedColorMappedFourPass = 33 -}; - -struct [[gnu::packed]] TGAHeader { - u8 id_length; - u8 color_map_type; - TGADataType data_type_code; - i16 color_map_origin; - i16 color_map_length; - u8 color_map_depth; - i16 x_origin; - i16 y_origin; - u16 width; - u16 height; - u8 bits_per_pixel; - u8 image_descriptor; -}; -static_assert(sizeof(TGAHeader) == 18); - -} - -template<> -struct AK::Traits : public DefaultTraits { - static constexpr bool is_trivially_serializable() { return true; } -}; - -namespace Gfx { - -struct TGALoadingContext { - TGALoadingContext(ReadonlyBytes bytes, FixedMemoryStream stream) - : bytes(bytes) - , stream(move(stream)) - { - } - ReadonlyBytes bytes; - FixedMemoryStream stream; - TGAHeader header {}; - RefPtr bitmap; -}; - -TGAImageDecoderPlugin::TGAImageDecoderPlugin(NonnullOwnPtr context) - : m_context(move(context)) -{ -} - -TGAImageDecoderPlugin::~TGAImageDecoderPlugin() = default; - -IntSize TGAImageDecoderPlugin::size() -{ - return IntSize { m_context->header.width, m_context->header.height }; -} - -static ErrorOr ensure_header_validity(TGAHeader const& header, size_t whole_image_stream_size) -{ - if ((header.bits_per_pixel % 8) != 0 || header.bits_per_pixel < 8 || header.bits_per_pixel > 32) - return Error::from_string_literal("Invalid bit depth"); - auto bytes_remaining = whole_image_stream_size - sizeof(TGAHeader); - if (header.data_type_code == TGADataType::UncompressedRGB && bytes_remaining < static_cast(header.width) * header.height * (header.bits_per_pixel / 8)) - return Error::from_string_literal("Not enough data to read an image with the expected size"); - return {}; -} - -ErrorOr TGAImageDecoderPlugin::decode_tga_header() -{ - m_context->header = TRY(m_context->stream.read_value()); - TRY(ensure_header_validity(m_context->header, m_context->bytes.size())); - return {}; -} - -bool TGAImageDecoderPlugin::validate_before_create(ReadonlyBytes data) -{ - FixedMemoryStream stream { data }; - auto header_or_err = stream.read_value(); - if (header_or_err.is_error()) - return false; - return !ensure_header_validity(header_or_err.release_value(), data.size()).is_error(); -} - -ErrorOr> TGAImageDecoderPlugin::create(ReadonlyBytes data) -{ - FixedMemoryStream stream { data }; - auto context = TRY(adopt_nonnull_own_or_enomem(new (nothrow) TGALoadingContext(data, move(stream)))); - auto plugin = TRY(adopt_nonnull_own_or_enomem(new (nothrow) TGAImageDecoderPlugin(move(context)))); - TRY(plugin->decode_tga_header()); - return plugin; -} - -static ErrorOr read_pixel_from_stream(Stream& stream, size_t bytes_size) -{ - // NOTE: We support 24-bit color pixels and 32-bit color pixels - VERIFY(bytes_size == 3 || bytes_size == 4); - if (bytes_size == 3) { - Array raw; - TRY(stream.read_until_filled(raw.span())); - return Color(raw[2], raw[1], raw[0]).value(); - } - return stream.read_value(); -} - -struct TGAPixelPacketHeader { - bool raw { false }; - u8 pixels_count { 0 }; -}; - -static ErrorOr read_pixel_packet_header(Stream& stream) -{ - auto const pixel_packet_header = TRY(stream.read_value()); - bool pixels_raw_in_packet = !(pixel_packet_header & 0x80); - u8 pixels_count_in_packet = (pixel_packet_header & 0x7f); - // NOTE: Run-length-encoded/Raw pixel packets cannot encode zero pixels, - // so value 0 stands for 1 pixel, 1 stands for 2, etc... - pixels_count_in_packet++; - VERIFY(pixels_count_in_packet > 0); - return TGAPixelPacketHeader { pixels_raw_in_packet, pixels_count_in_packet }; -} - -ErrorOr TGAImageDecoderPlugin::frame(size_t index, Optional) -{ - auto bits_per_pixel = m_context->header.bits_per_pixel; - auto color_map = m_context->header.color_map_type; - auto data_type = m_context->header.data_type_code; - auto width = m_context->header.width; - auto height = m_context->header.height; - auto x_origin = m_context->header.x_origin; - auto y_origin = m_context->header.y_origin; - - if (index != 0) - return Error::from_string_literal("TGAImageDecoderPlugin: frame index must be 0"); - - if (color_map > 1) - return Error::from_string_literal("TGAImageDecoderPlugin: Invalid color map type"); - - if (m_context->bitmap) - return ImageFrameDescriptor { m_context->bitmap, 0 }; - - RefPtr bitmap; - switch (bits_per_pixel) { - case 24: - bitmap = TRY(Bitmap::create(BitmapFormat::BGRx8888, { m_context->header.width, m_context->header.height })); - break; - - case 32: - bitmap = TRY(Bitmap::create(BitmapFormat::BGRA8888, { m_context->header.width, m_context->header.height })); - break; - - default: - // FIXME: Implement other TGA bit depths - return Error::from_string_literal("TGAImageDecoderPlugin: Can only handle 24 and 32 bits per pixel"); - } - - // FIXME: Try to understand the Image origin (instead of X and Y origin coordinates) - // based on the Image descriptor, Field 5.6, bits 4 and 5. - - // NOTE: If Y origin is set to a negative number, just assume the generating software - // meant that we start with Y origin at the top height of the picture. - // At least this is the observed behavior when generating some pictures in GIMP. - if (y_origin < 0) - y_origin = height; - if (y_origin != 0 && y_origin != height) - return Error::from_string_literal("TGAImageDecoderPlugin: Can only handle Y origin which is 0 or the entire height"); - if (x_origin != 0 && x_origin != width) - return Error::from_string_literal("TGAImageDecoderPlugin: Can only handle X origin which is 0 or the entire width"); - - VERIFY((bits_per_pixel % 8) == 0); - auto bytes_per_pixel = bits_per_pixel / 8; - - switch (data_type) { - case TGADataType::UncompressedRGB: { - for (int row = 0; row < height; ++row) { - for (int col = 0; col < width; ++col) { - auto actual_row = row; - if (y_origin < height) - actual_row = height - 1 - row; - auto actual_col = col; - if (x_origin > width) - actual_col = width - 1 - col; - bitmap->scanline(actual_row)[actual_col] = TRY(read_pixel_from_stream(m_context->stream, bytes_per_pixel)); - } - } - break; - } - - case TGADataType::RunLengthEncodedRGB: { - size_t pixel_index = 0; - size_t pixel_count = height * width; - while (pixel_index < pixel_count) { - auto pixel_packet_header = TRY(read_pixel_packet_header(m_context->stream)); - VERIFY(pixel_packet_header.pixels_count > 0); - - auto pixel = TRY(read_pixel_from_stream(m_context->stream, bytes_per_pixel)); - auto max_pixel_index = min(pixel_index + pixel_packet_header.pixels_count, pixel_count); - for (size_t current_pixel_index = pixel_index; current_pixel_index < max_pixel_index; ++current_pixel_index) { - int row = current_pixel_index / width; - int col = current_pixel_index % width; - auto actual_row = row; - if (y_origin < height) - actual_row = height - 1 - row; - auto actual_col = col; - if (x_origin > width) - actual_col = width - 1 - col; - bitmap->scanline(actual_row)[actual_col] = pixel; - if (pixel_packet_header.raw && (current_pixel_index + 1) < max_pixel_index) - pixel = TRY(read_pixel_from_stream(m_context->stream, bytes_per_pixel)); - } - pixel_index += pixel_packet_header.pixels_count; - } - break; - } - default: - // FIXME: Implement other TGA data types - return Error::from_string_literal("TGAImageDecoderPlugin: Can currently only handle the UncompressedRGB or CompressedRGB data type"); - } - - m_context->bitmap = bitmap; - return ImageFrameDescriptor { m_context->bitmap, 0 }; -} - -} diff --git a/Userland/Libraries/LibGfx/ImageFormats/TGALoader.h b/Userland/Libraries/LibGfx/ImageFormats/TGALoader.h deleted file mode 100644 index ad0899cf159..00000000000 --- a/Userland/Libraries/LibGfx/ImageFormats/TGALoader.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2022, Tom Needham <06needhamt@gmail.com> - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Gfx { - -struct TGALoadingContext; - -class TGAImageDecoderPlugin final : public ImageDecoderPlugin { -public: - static bool validate_before_create(ReadonlyBytes); - static ErrorOr> create(ReadonlyBytes); - - virtual ~TGAImageDecoderPlugin() override; - - virtual IntSize size() override; - - virtual ErrorOr frame(size_t index, Optional ideal_size = {}) override; - -private: - TGAImageDecoderPlugin(NonnullOwnPtr); - - ErrorOr decode_tga_header(); - NonnullOwnPtr m_context; -}; - -}