diff --git a/Meta/Lagom/Fuzzers/FuzzQOILoader.cpp b/Meta/Lagom/Fuzzers/FuzzQOILoader.cpp deleted file mode 100644 index e1096adf422..00000000000 --- a/Meta/Lagom/Fuzzers/FuzzQOILoader.cpp +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * 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::QOIImageDecoderPlugin::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 550bf19b646..9098f5b2948 100644 --- a/Meta/Lagom/Fuzzers/fuzzers.cmake +++ b/Meta/Lagom/Fuzzers/fuzzers.cmake @@ -33,7 +33,6 @@ set(FUZZER_TARGETS Poly1305 PPMLoader QOALoader - QOILoader RegexECMA262 RegexPosixBasic RegexPosixExtended @@ -98,7 +97,6 @@ set(FUZZER_DEPENDENCIES_PNGLoader LibGfx) set(FUZZER_DEPENDENCIES_Poly1305 LibCrypto) set(FUZZER_DEPENDENCIES_PPMLoader LibGfx) set(FUZZER_DEPENDENCIES_QOALoader LibAudio) -set(FUZZER_DEPENDENCIES_QOILoader LibGfx) set(FUZZER_DEPENDENCIES_RegexECMA262 LibRegex) set(FUZZER_DEPENDENCIES_RegexPosixBasic LibRegex) set(FUZZER_DEPENDENCIES_RegexPosixExtended LibRegex) diff --git a/Meta/gn/secondary/Userland/Libraries/LibGfx/BUILD.gn b/Meta/gn/secondary/Userland/Libraries/LibGfx/BUILD.gn index f7b915a8110..472f80f4ec7 100644 --- a/Meta/gn/secondary/Userland/Libraries/LibGfx/BUILD.gn +++ b/Meta/gn/secondary/Userland/Libraries/LibGfx/BUILD.gn @@ -82,8 +82,6 @@ shared_library("LibGfx") { "ImageFormats/PPMLoader.cpp", "ImageFormats/PortableFormatWriter.cpp", "ImageFormats/QMArithmeticDecoder.cpp", - "ImageFormats/QOILoader.cpp", - "ImageFormats/QOIWriter.cpp", "ImageFormats/TGALoader.cpp", "ImageFormats/TIFFLoader.cpp", "ImageFormats/TinyVGLoader.cpp", diff --git a/Tests/LibGfx/TestImageWriter.cpp b/Tests/LibGfx/TestImageWriter.cpp index c99d5833aae..c284fff6324 100644 --- a/Tests/LibGfx/TestImageWriter.cpp +++ b/Tests/LibGfx/TestImageWriter.cpp @@ -18,8 +18,6 @@ #include #include #include -#include -#include #include #include #include @@ -174,12 +172,6 @@ TEST_CASE(test_png) TRY_OR_FAIL((test_roundtrip(TRY_OR_FAIL(create_test_rgba_bitmap())))); } -TEST_CASE(test_qoi) -{ - TRY_OR_FAIL((test_roundtrip(TRY_OR_FAIL(create_test_rgb_bitmap())))); - TRY_OR_FAIL((test_roundtrip(TRY_OR_FAIL(create_test_rgba_bitmap())))); -} - TEST_CASE(test_webp) { TRY_OR_FAIL((test_roundtrip(TRY_OR_FAIL(create_test_rgb_bitmap())))); diff --git a/Userland/Libraries/LibCore/MimeData.cpp b/Userland/Libraries/LibCore/MimeData.cpp index ff7068ba5ed..27b6dcf1e0a 100644 --- a/Userland/Libraries/LibCore/MimeData.cpp +++ b/Userland/Libraries/LibCore/MimeData.cpp @@ -133,7 +133,6 @@ static Array const s_registered_mime_type = { MimeType { .name = "image/x-portable-bitmap"sv, .common_extensions = { ".pbm"sv }, .description = "PBM image data"sv, .magic_bytes = Vector { 0x50, 0x31, 0x0A } }, MimeType { .name = "image/x-portable-graymap"sv, .common_extensions = { ".pgm"sv }, .description = "PGM image data"sv, .magic_bytes = Vector { 0x50, 0x32, 0x0A } }, MimeType { .name = "image/x-portable-pixmap"sv, .common_extensions = { ".ppm"sv }, .description = "PPM image data"sv, .magic_bytes = Vector { 0x50, 0x33, 0x0A } }, - MimeType { .name = "image/x-qoi"sv, .common_extensions = { ".qoi"sv }, .description = "QOI image data"sv, .magic_bytes = Vector { 'q', 'o', 'i', 'f' } }, MimeType { .name = "image/x-targa"sv, .common_extensions = { ".tga"sv }, .description = "Targa image data"sv }, MimeType { .name = "text/css"sv, .common_extensions = { ".css"sv }, .description = "Cascading Style Sheet"sv }, diff --git a/Userland/Libraries/LibGfx/CMakeLists.txt b/Userland/Libraries/LibGfx/CMakeLists.txt index 1f06aa50bf8..00b0977a729 100644 --- a/Userland/Libraries/LibGfx/CMakeLists.txt +++ b/Userland/Libraries/LibGfx/CMakeLists.txt @@ -60,8 +60,6 @@ set(SOURCES ImageFormats/PAMLoader.cpp ImageFormats/PPMLoader.cpp ImageFormats/QMArithmeticDecoder.cpp - ImageFormats/QOILoader.cpp - ImageFormats/QOIWriter.cpp ImageFormats/TGALoader.cpp ImageFormats/TIFFLoader.cpp ImageFormats/TinyVGLoader.cpp diff --git a/Userland/Libraries/LibGfx/ImageFormats/ImageDecoder.cpp b/Userland/Libraries/LibGfx/ImageFormats/ImageDecoder.cpp index 8a24bdafa38..34e98705b24 100644 --- a/Userland/Libraries/LibGfx/ImageFormats/ImageDecoder.cpp +++ b/Userland/Libraries/LibGfx/ImageFormats/ImageDecoder.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -50,7 +49,6 @@ static ErrorOr> probe_and_sniff_for_appropriate_plugi { PGMImageDecoderPlugin::sniff, PGMImageDecoderPlugin::create }, { PNGImageDecoderPlugin::sniff, PNGImageDecoderPlugin::create }, { PPMImageDecoderPlugin::sniff, PPMImageDecoderPlugin::create }, - { QOIImageDecoderPlugin::sniff, QOIImageDecoderPlugin::create }, { TIFFImageDecoderPlugin::sniff, TIFFImageDecoderPlugin::create }, { TinyVGImageDecoderPlugin::sniff, TinyVGImageDecoderPlugin::create }, { WebPImageDecoderPlugin::sniff, WebPImageDecoderPlugin::create }, diff --git a/Userland/Libraries/LibGfx/ImageFormats/QOILoader.cpp b/Userland/Libraries/LibGfx/ImageFormats/QOILoader.cpp deleted file mode 100644 index 8ca74428879..00000000000 --- a/Userland/Libraries/LibGfx/ImageFormats/QOILoader.cpp +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright (c) 2021, Linus Groh - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Gfx { - -static constexpr auto QOI_MAGIC = "qoif"sv; -static constexpr u8 QOI_OP_RGB = 0b11111110; -static constexpr u8 QOI_OP_RGBA = 0b11111111; -static constexpr u8 QOI_OP_INDEX = 0b00000000; -static constexpr u8 QOI_OP_DIFF = 0b01000000; -static constexpr u8 QOI_OP_LUMA = 0b10000000; -static constexpr u8 QOI_OP_RUN = 0b11000000; -static constexpr u8 QOI_MASK_2 = 0b11000000; -static constexpr u8 END_MARKER[] = { 0, 0, 0, 0, 0, 0, 0, 1 }; - -static ErrorOr decode_qoi_header(Stream& stream) -{ - auto header = TRY(stream.read_value()); - if (StringView { header.magic, array_size(header.magic) } != QOI_MAGIC) - return Error::from_string_literal("Invalid QOI image: incorrect header magic"); - header.width = AK::convert_between_host_and_big_endian(header.width); - header.height = AK::convert_between_host_and_big_endian(header.height); - return header; -} - -static ErrorOr decode_qoi_op_rgb(Stream& stream, u8 first_byte, Color pixel) -{ - VERIFY(first_byte == QOI_OP_RGB); - u8 bytes[3]; - TRY(stream.read_until_filled({ &bytes, array_size(bytes) })); - - // The alpha value remains unchanged from the previous pixel. - return Color { bytes[0], bytes[1], bytes[2], pixel.alpha() }; -} - -static ErrorOr decode_qoi_op_rgba(Stream& stream, u8 first_byte) -{ - VERIFY(first_byte == QOI_OP_RGBA); - u8 bytes[4]; - TRY(stream.read_until_filled({ &bytes, array_size(bytes) })); - return Color { bytes[0], bytes[1], bytes[2], bytes[3] }; -} - -static ErrorOr decode_qoi_op_index(Stream&, u8 first_byte) -{ - VERIFY((first_byte & QOI_MASK_2) == QOI_OP_INDEX); - u8 index = first_byte & ~QOI_MASK_2; - VERIFY(index <= 63); - return index; -} - -static ErrorOr decode_qoi_op_diff(Stream&, u8 first_byte, Color pixel) -{ - VERIFY((first_byte & QOI_MASK_2) == QOI_OP_DIFF); - u8 dr = (first_byte & 0b00110000) >> 4; - u8 dg = (first_byte & 0b00001100) >> 2; - u8 db = (first_byte & 0b00000011); - VERIFY(dr <= 3 && dg <= 3 && db <= 3); - - // Values are stored as unsigned integers with a bias of 2. - return Color { - static_cast(pixel.red() + static_cast(dr - 2)), - static_cast(pixel.green() + static_cast(dg - 2)), - static_cast(pixel.blue() + static_cast(db - 2)), - pixel.alpha(), - }; -} - -static ErrorOr decode_qoi_op_luma(Stream& stream, u8 first_byte, Color pixel) -{ - VERIFY((first_byte & QOI_MASK_2) == QOI_OP_LUMA); - auto byte = TRY(stream.read_value()); - u8 diff_green = (first_byte & ~QOI_MASK_2); - u8 dr_dg = (byte & 0b11110000) >> 4; - u8 db_dg = (byte & 0b00001111); - - // Values are stored as unsigned integers with a bias of 32 for the green channel and a bias of 8 for the red and blue channel. - return Color { - static_cast(pixel.red() + static_cast((diff_green - 32) + (dr_dg - 8))), - static_cast(pixel.green() + static_cast(diff_green - 32)), - static_cast(pixel.blue() + static_cast((diff_green - 32) + (db_dg - 8))), - pixel.alpha(), - }; -} - -static ErrorOr decode_qoi_op_run(Stream&, u8 first_byte) -{ - VERIFY((first_byte & QOI_MASK_2) == QOI_OP_RUN); - u8 run = first_byte & ~QOI_MASK_2; - - // The run-length is stored with a bias of -1. - run += 1; - - // Note that the run-lengths 63 and 64 (b111110 and b111111) are illegal as they are occupied by the QOI_OP_RGB and QOI_OP_RGBA tags. - if (run == QOI_OP_RGB || run == QOI_OP_RGBA) - return Error::from_string_literal("Invalid QOI image: illegal run length"); - - VERIFY(run >= 1 && run <= 62); - return run; -} - -static ErrorOr decode_qoi_end_marker(Stream& stream) -{ - u8 bytes[array_size(END_MARKER)]; - TRY(stream.read_until_filled({ &bytes, array_size(bytes) })); - if (!stream.is_eof()) - return Error::from_string_literal("Invalid QOI image: expected end of stream but more bytes are available"); - if (memcmp(&END_MARKER, &bytes, array_size(bytes)) != 0) - return Error::from_string_literal("Invalid QOI image: incorrect end marker"); - return {}; -} - -static ErrorOr> decode_qoi_image(Stream& stream, u32 width, u32 height) -{ - // FIXME: Why is Gfx::Bitmap's size signed? Makes no sense whatsoever. - if (width > NumericLimits::max()) - return Error::from_string_literal("Cannot create bitmap for QOI image of valid size, width exceeds maximum Gfx::Bitmap width"); - if (height > NumericLimits::max()) - return Error::from_string_literal("Cannot create bitmap for QOI image of valid size, height exceeds maximum Gfx::Bitmap height"); - - auto bitmap = TRY(Bitmap::create(BitmapFormat::BGRA8888, { width, height })); - - u8 run = 0; - Color pixel = { 0, 0, 0, 255 }; - Color previous_pixels[64] {}; - - for (u32 y = 0; y < height; ++y) { - for (u32 x = 0; x < width; ++x) { - if (run > 0) - --run; - if (run == 0) { - auto first_byte = TRY(stream.read_value()); - if (first_byte == QOI_OP_RGB) - pixel = TRY(decode_qoi_op_rgb(stream, first_byte, pixel)); - else if (first_byte == QOI_OP_RGBA) - pixel = TRY(decode_qoi_op_rgba(stream, first_byte)); - else if ((first_byte & QOI_MASK_2) == QOI_OP_INDEX) - pixel = previous_pixels[TRY(decode_qoi_op_index(stream, first_byte))]; - else if ((first_byte & QOI_MASK_2) == QOI_OP_DIFF) - pixel = TRY(decode_qoi_op_diff(stream, first_byte, pixel)); - else if ((first_byte & QOI_MASK_2) == QOI_OP_LUMA) - pixel = TRY(decode_qoi_op_luma(stream, first_byte, pixel)); - else if ((first_byte & QOI_MASK_2) == QOI_OP_RUN) - run = TRY(decode_qoi_op_run(stream, first_byte)); - else - return Error::from_string_literal("Invalid QOI image: unknown chunk tag"); - } - auto index_position = (pixel.red() * 3 + pixel.green() * 5 + pixel.blue() * 7 + pixel.alpha() * 11) % 64; - previous_pixels[index_position] = pixel; - bitmap->set_pixel(x, y, pixel); - } - } - TRY(decode_qoi_end_marker(stream)); - return { move(bitmap) }; -} - -QOIImageDecoderPlugin::QOIImageDecoderPlugin(NonnullOwnPtr stream) -{ - m_context = make(); - m_context->stream = move(stream); -} - -IntSize QOIImageDecoderPlugin::size() -{ - return { m_context->header.width, m_context->header.height }; -} - -bool QOIImageDecoderPlugin::sniff(ReadonlyBytes data) -{ - FixedMemoryStream stream { { data.data(), data.size() } }; - return !decode_qoi_header(stream).is_error(); -} - -ErrorOr> QOIImageDecoderPlugin::create(ReadonlyBytes data) -{ - auto stream = TRY(try_make(data)); - auto plugin = TRY(adopt_nonnull_own_or_enomem(new (nothrow) QOIImageDecoderPlugin(move(stream)))); - TRY(plugin->decode_header_and_update_context()); - return plugin; -} - -ErrorOr QOIImageDecoderPlugin::frame(size_t index, Optional) -{ - if (index > 0) - return Error::from_string_literal("Invalid frame index"); - - // No one should try to decode the frame again after an error was already returned. - VERIFY(m_context->state != QOILoadingContext::State::Error); - - if (m_context->state == QOILoadingContext::State::HeaderDecoded) - TRY(decode_image_and_update_context()); - - VERIFY(m_context->state == QOILoadingContext::State::ImageDecoded); - VERIFY(m_context->bitmap); - return ImageFrameDescriptor { m_context->bitmap, 0 }; -} - -ErrorOr QOIImageDecoderPlugin::decode_header_and_update_context() -{ - VERIFY(m_context->state < QOILoadingContext::State::HeaderDecoded); - m_context->header = TRY(decode_qoi_header(*m_context->stream)); - m_context->state = QOILoadingContext::State::HeaderDecoded; - return {}; -} - -ErrorOr QOIImageDecoderPlugin::decode_image_and_update_context() -{ - VERIFY(m_context->state < QOILoadingContext::State::ImageDecoded); - auto error_or_bitmap = decode_qoi_image(*m_context->stream, m_context->header.width, m_context->header.height); - if (error_or_bitmap.is_error()) { - m_context->state = QOILoadingContext::State::Error; - return error_or_bitmap.release_error(); - } - m_context->state = QOILoadingContext::State::ImageDecoded; - m_context->bitmap = error_or_bitmap.release_value(); - return {}; -} - -} diff --git a/Userland/Libraries/LibGfx/ImageFormats/QOILoader.h b/Userland/Libraries/LibGfx/ImageFormats/QOILoader.h deleted file mode 100644 index c1908ebdbf3..00000000000 --- a/Userland/Libraries/LibGfx/ImageFormats/QOILoader.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2021, Linus Groh - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Gfx { - -// Decoder for the "Quite OK Image" format (v1.0). -// https://qoiformat.org/qoi-specification.pdf - -struct [[gnu::packed]] QOIHeader { - char magic[4]; - u32 width; - u32 height; - u8 channels; - u8 colorspace; -}; - -struct QOILoadingContext { - enum class State { - NotDecoded = 0, - HeaderDecoded, - ImageDecoded, - Error, - }; - State state { State::NotDecoded }; - OwnPtr stream {}; - QOIHeader header {}; - RefPtr bitmap; -}; - -class QOIImageDecoderPlugin final : public ImageDecoderPlugin { -public: - static bool sniff(ReadonlyBytes); - static ErrorOr> create(ReadonlyBytes); - - virtual ~QOIImageDecoderPlugin() override = default; - - virtual IntSize size() override; - - virtual ErrorOr frame(size_t index, Optional ideal_size = {}) override; - -private: - ErrorOr decode_header_and_update_context(); - ErrorOr decode_image_and_update_context(); - - QOIImageDecoderPlugin(NonnullOwnPtr); - - OwnPtr m_context; -}; - -} - -template<> -struct AK::Traits : public DefaultTraits { - static constexpr bool is_trivially_serializable() { return true; } -}; diff --git a/Userland/Libraries/LibGfx/ImageFormats/QOIWriter.cpp b/Userland/Libraries/LibGfx/ImageFormats/QOIWriter.cpp deleted file mode 100644 index 9e6159b620c..00000000000 --- a/Userland/Libraries/LibGfx/ImageFormats/QOIWriter.cpp +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (c) 2022, Olivier De Cannière - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "QOIWriter.h" -#include - -namespace Gfx { - -static constexpr Array qoi_magic_bytes = { 'q', 'o', 'i', 'f' }; -static constexpr Array qoi_end_marker = { 0, 0, 0, 0, 0, 0, 0, 1 }; - -enum class Colorspace { - sRGB, - Linear, -}; - -enum class Channels { - RGB, - RGBA, -}; - -ErrorOr QOIWriter::encode(Bitmap const& bitmap) -{ - QOIWriter writer; - TRY(writer.add_header(bitmap.width(), bitmap.height(), Channels::RGBA, Colorspace::sRGB)); - - Color previous_pixel = { 0, 0, 0, 255 }; - - bool creating_run = false; - int run_length = 0; - - for (auto y = 0; y < bitmap.height(); y++) { - for (auto x = 0; x < bitmap.width(); x++) { - auto pixel = bitmap.get_pixel(x, y); - - // Check for at most 62 consecutive identical pixels. - if (pixel == previous_pixel) { - if (!creating_run) { - creating_run = true; - run_length = 0; - writer.insert_into_running_array(pixel); - } - - run_length++; - - // If the run reaches a maximum length of 62 or if this is the last pixel then create the chunk. - if (run_length == 62 || (y == bitmap.height() - 1 && x == bitmap.width() - 1)) { - TRY(writer.add_run_chunk(run_length)); - creating_run = false; - } - - continue; - } - - // Run ended with the previous pixel. Create a chunk for it and continue processing this pixel. - if (creating_run) { - TRY(writer.add_run_chunk(run_length)); - creating_run = false; - } - - // Check if the pixel matches a pixel in the running array. - auto index = pixel_hash_function(pixel); - auto& array_pixel = writer.running_array[index]; - if (array_pixel == pixel) { - TRY(writer.add_index_chunk(index)); - previous_pixel = pixel; - continue; - } - - writer.running_array[index] = pixel; - - // Check if pixel can be expressed as a difference of the previous pixel. - if (pixel.alpha() == previous_pixel.alpha()) { - int red_difference = pixel.red() - previous_pixel.red(); - int green_difference = pixel.green() - previous_pixel.green(); - int blue_difference = pixel.blue() - previous_pixel.blue(); - int relative_red_difference = red_difference - green_difference; - int relative_blue_difference = blue_difference - green_difference; - - if (red_difference > -3 && red_difference < 2 - && green_difference > -3 && green_difference < 2 - && blue_difference > -3 && blue_difference < 2) { - TRY(writer.add_diff_chunk(red_difference, green_difference, blue_difference)); - previous_pixel = pixel; - continue; - } - if (relative_red_difference > -9 && relative_red_difference < 8 - && green_difference > -33 && green_difference < 32 - && relative_blue_difference > -9 && relative_blue_difference < 8) { - TRY(writer.add_luma_chunk(relative_red_difference, green_difference, relative_blue_difference)); - previous_pixel = pixel; - continue; - } - - TRY(writer.add_rgb_chunk(pixel.red(), pixel.green(), pixel.blue())); - previous_pixel = pixel; - continue; - } - - previous_pixel = pixel; - - // Write full color values. - TRY(writer.add_rgba_chunk(pixel.red(), pixel.green(), pixel.blue(), pixel.alpha())); - } - } - - TRY(writer.add_end_marker()); - - return ByteBuffer::copy(writer.m_data); -} - -ErrorOr QOIWriter::add_header(u32 width, u32 height, Channels channels = Channels::RGBA, Colorspace color_space = Colorspace::sRGB) -{ - // FIXME: Handle RGB and all linear channels. - if (channels == Channels::RGB || color_space == Colorspace::Linear) - TODO(); - - TRY(m_data.try_append(qoi_magic_bytes.data(), sizeof(qoi_magic_bytes))); - - auto big_endian_width = AK::convert_between_host_and_big_endian(width); - TRY(m_data.try_append(bit_cast(&big_endian_width), sizeof(width))); - - auto big_endian_height = AK::convert_between_host_and_big_endian(height); - TRY(m_data.try_append(bit_cast(&big_endian_height), sizeof(height))); - - // Number of channels: 3 = RGB, 4 = RGBA. - TRY(m_data.try_append(4)); - - // Colorspace: 0 = sRGB, 1 = all linear channels. - TRY(m_data.try_append(color_space == Colorspace::sRGB ? 0 : 1)); - - return {}; -} - -ErrorOr QOIWriter::add_rgb_chunk(u8 r, u8 g, u8 b) -{ - constexpr static u8 rgb_tag = 0b1111'1110; - - TRY(m_data.try_append(rgb_tag)); - TRY(m_data.try_append(r)); - TRY(m_data.try_append(g)); - TRY(m_data.try_append(b)); - return {}; -} - -ErrorOr QOIWriter::add_rgba_chunk(u8 r, u8 g, u8 b, u8 a) -{ - constexpr static u8 rgba_tag = 0b1111'1111; - - TRY(m_data.try_append(rgba_tag)); - TRY(m_data.try_append(r)); - TRY(m_data.try_append(g)); - TRY(m_data.try_append(b)); - TRY(m_data.try_append(a)); - return {}; -} - -ErrorOr QOIWriter::add_index_chunk(unsigned int index) -{ - constexpr static u8 index_tag = 0b0000'0000; - - u8 chunk = index_tag | index; - TRY(m_data.try_append(chunk)); - return {}; -} - -ErrorOr QOIWriter::add_diff_chunk(i8 red_difference, i8 green_difference, i8 blue_difference) -{ - constexpr static u8 diff_tag = 0b0100'0000; - - u8 bias = 2; - u8 red = red_difference + bias; - u8 green = green_difference + bias; - u8 blue = blue_difference + bias; - - u8 chunk = diff_tag | (red << 4) | (green << 2) | blue; - TRY(m_data.try_append(chunk)); - return {}; -} - -ErrorOr QOIWriter::add_luma_chunk(i8 relative_red_difference, i8 green_difference, i8 relative_blue_difference) -{ - constexpr static u8 luma_tag = 0b1000'0000; - u8 green_bias = 32; - u8 red_blue_bias = 8; - - u8 chunk1 = luma_tag | (green_difference + green_bias); - u8 chunk2 = ((relative_red_difference + red_blue_bias) << 4) | (relative_blue_difference + red_blue_bias); - TRY(m_data.try_append(chunk1)); - TRY(m_data.try_append(chunk2)); - return {}; -} - -ErrorOr QOIWriter::add_run_chunk(unsigned run_length) -{ - constexpr static u8 run_tag = 0b1100'0000; - int bias = -1; - - u8 chunk = run_tag | (run_length + bias); - TRY(m_data.try_append(chunk)); - return {}; -} - -ErrorOr QOIWriter::add_end_marker() -{ - TRY(m_data.try_append(qoi_end_marker.data(), sizeof(qoi_end_marker))); - return {}; -} - -u32 QOIWriter::pixel_hash_function(Color pixel) -{ - return (pixel.red() * 3 + pixel.green() * 5 + pixel.blue() * 7 + pixel.alpha() * 11) % 64; -} - -void QOIWriter::insert_into_running_array(Color pixel) -{ - auto index = pixel_hash_function(pixel); - running_array[index] = pixel; -} - -} diff --git a/Userland/Libraries/LibGfx/ImageFormats/QOIWriter.h b/Userland/Libraries/LibGfx/ImageFormats/QOIWriter.h deleted file mode 100644 index 232aad5cd14..00000000000 --- a/Userland/Libraries/LibGfx/ImageFormats/QOIWriter.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2022, Olivier De Cannière - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Gfx { - -enum class Colorspace; -enum class Channels; - -class QOIWriter { -public: - static ErrorOr encode(Gfx::Bitmap const&); - -private: - QOIWriter() = default; - - Vector m_data; - ErrorOr add_header(u32 width, u32 height, Channels, Colorspace); - ErrorOr add_rgb_chunk(u8, u8, u8); - ErrorOr add_rgba_chunk(u8, u8, u8, u8); - ErrorOr add_index_chunk(u32 index); - ErrorOr add_diff_chunk(i8 red_difference, i8 green_difference, i8 blue_difference); - ErrorOr add_luma_chunk(i8 relative_red_difference, i8 green_difference, i8 relative_blue_difference); - ErrorOr add_run_chunk(u32 run_length); - ErrorOr add_end_marker(); - - Array running_array; - static u32 pixel_hash_function(Color pixel); - void insert_into_running_array(Color pixel); -}; - -} diff --git a/Userland/Libraries/LibWeb/Loader/Resource.cpp b/Userland/Libraries/LibWeb/Loader/Resource.cpp index 817cd12406e..3d54f3c5b34 100644 --- a/Userland/Libraries/LibWeb/Loader/Resource.cpp +++ b/Userland/Libraries/LibWeb/Loader/Resource.cpp @@ -97,11 +97,6 @@ void Resource::did_load(Badge, ReadonlyBytes data, HTTP::HeaderM if (content_type.has_value()) { dbgln_if(RESOURCE_DEBUG, "Content-Type header: '{}'", content_type.value()); m_mime_type = mime_type_from_content_type(content_type.value()); - // FIXME: "The Quite OK Image Format" doesn't have an official mime type yet, - // and servers like nginx will send a generic octet-stream mime type instead. - // Let's use image/x-qoi for now, which is also what our Core::MimeData uses & would guess. - if (m_mime_type == "application/octet-stream" && url().serialize_path().ends_with(".qoi"sv)) - m_mime_type = "image/x-qoi"; } else { auto content_type_options = headers.get("X-Content-Type-Options"); if (content_type_options.value_or("").equals_ignoring_ascii_case("nosniff"sv)) { diff --git a/Userland/Utilities/image.cpp b/Userland/Utilities/image.cpp index 09623f15663..e434f610842 100644 --- a/Userland/Utilities/image.cpp +++ b/Userland/Utilities/image.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include @@ -186,10 +185,8 @@ static ErrorOr save_image(LoadedImage& image, StringView out_path, bool pp bytes = TRY(Gfx::BMPWriter::encode(*frame, { .icc_data = image.icc_data })); } else if (out_path.ends_with(".png"sv, CaseSensitivity::CaseInsensitive)) { bytes = TRY(Gfx::PNGWriter::encode(*frame, { .icc_data = image.icc_data })); - } else if (out_path.ends_with(".qoi"sv, CaseSensitivity::CaseInsensitive)) { - bytes = TRY(Gfx::QOIWriter::encode(*frame)); } else { - return Error::from_string_view("can only write .bmp, .gif, .jpg, .png, .ppm, .qoi, and .webp"sv); + return Error::from_string_view("can only write .bmp, .gif, .jpg, .png, .ppm, and .webp"sv); } TRY(TRY(stream())->write_until_depleted(bytes));