From 3c1be9879f2fe10e77f6fd5523cf9c56a797ff6f Mon Sep 17 00:00:00 2001 From: Lucas CHOLLET Date: Mon, 31 Jul 2023 23:17:33 -0400 Subject: [PATCH] LibGfx: Add `ExifOrientedBitmap` The idea behind this class is to provide an abstraction for decoders. It allows them to use the class as if it was a normal `Bitmap`, however, under the hood, this class will honor a given orientation, as specified by the Exif standard. This class is introduced to be used within the JPEG XL decoder, but it should be possible to use it for every Exif-compatible format. --- .../LibGfx/ImageFormats/ExifOrientedBitmap.h | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 Userland/Libraries/LibGfx/ImageFormats/ExifOrientedBitmap.h diff --git a/Userland/Libraries/LibGfx/ImageFormats/ExifOrientedBitmap.h b/Userland/Libraries/LibGfx/ImageFormats/ExifOrientedBitmap.h new file mode 100644 index 0000000000..66722ed1db --- /dev/null +++ b/Userland/Libraries/LibGfx/ImageFormats/ExifOrientedBitmap.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2023, Lucas Chollet + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Gfx { +class ExifOrientedBitmap { +public: + // In the EXIF 3.0 specification, 4.6.5.1.6. Orientation + enum class Orientation { + None = 1, + FlipHorizontally = 2, + Rotate180 = 3, + FlipVertically = 4, + Rotate90ClockwiseThenFlipHorizontally = 5, + Rotate90Clockwise = 6, + FlipHorizontallyThenRotate90Clockwise = 7, + Rotate90CounterClockwise = 8, + }; + + template + static ErrorOr create(BitmapFormat format, IntSize size, Orientation orientation) + { + auto bitmap = TRY(Bitmap::create(format, oriented_size(size, orientation))); + return ExifOrientedBitmap(move(bitmap), size, orientation); + } + + void set_pixel(u32 x, u32 y, Color color) + { + auto const new_position = oriented_position(IntPoint(x, y)); + m_bitmap->set_pixel(new_position, color); + } + + NonnullRefPtr& bitmap() + { + return m_bitmap; + } + +private: + ExifOrientedBitmap(NonnullRefPtr bitmap, IntSize size, Orientation orientation) + : m_bitmap(move(bitmap)) + , m_orientation(orientation) + , m_width(size.width()) + , m_height(size.height()) + { + } + + static IntSize oriented_size(IntSize size, Orientation orientation) + { + switch (orientation) { + + case Orientation::None: + case Orientation::FlipHorizontally: + case Orientation::Rotate180: + case Orientation::FlipVertically: + return size; + case Orientation::Rotate90ClockwiseThenFlipHorizontally: + case Orientation::Rotate90Clockwise: + case Orientation::FlipHorizontallyThenRotate90Clockwise: + case Orientation::Rotate90CounterClockwise: + return { size.height(), size.width() }; + } + VERIFY_NOT_REACHED(); + } + + IntPoint oriented_position(IntPoint point) + { + auto const flip_horizontally = [this](IntPoint point) { + return IntPoint(m_width - point.x() - 1, point.y()); + }; + + auto const rotate_90_clockwise = [this](IntPoint point) { + return IntPoint(m_height - point.y() - 1, point.x()); + }; + + switch (m_orientation) { + case Orientation::None: + return point; + case Orientation::FlipHorizontally: + return flip_horizontally(point); + case Orientation::Rotate180: + return IntPoint(m_width - point.x() - 1, m_height - point.y() - 1); + case Orientation::FlipVertically: + return IntPoint(point.x(), m_height - point.y() - 1); + case Orientation::Rotate90ClockwiseThenFlipHorizontally: + return flip_horizontally(rotate_90_clockwise(point)); + case Orientation::Rotate90Clockwise: + return rotate_90_clockwise(point); + case Orientation::FlipHorizontallyThenRotate90Clockwise: + return rotate_90_clockwise(flip_horizontally(point)); + case Orientation::Rotate90CounterClockwise: + return IntPoint(point.y(), m_width - point.x() - 1); + } + VERIFY_NOT_REACHED(); + } + + NonnullRefPtr m_bitmap; + Orientation m_orientation; + + u32 m_width {}; + u32 m_height {}; +}; +}