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 {}; +}; +}