diff options
Diffstat (limited to 'Userland/Libraries/LibGfx/Bitmap.h')
-rw-r--r-- | Userland/Libraries/LibGfx/Bitmap.h | 336 |
1 files changed, 336 insertions, 0 deletions
diff --git a/Userland/Libraries/LibGfx/Bitmap.h b/Userland/Libraries/LibGfx/Bitmap.h new file mode 100644 index 0000000000..4d0b236f57 --- /dev/null +++ b/Userland/Libraries/LibGfx/Bitmap.h @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <AK/Forward.h> +#include <AK/RefCounted.h> +#include <AK/RefPtr.h> +#include <LibGfx/Color.h> +#include <LibGfx/Forward.h> +#include <LibGfx/Rect.h> + +#define ENUMERATE_IMAGE_FORMATS \ + __ENUMERATE_IMAGE_FORMAT(pbm, ".pbm") \ + __ENUMERATE_IMAGE_FORMAT(pgm, ".pgm") \ + __ENUMERATE_IMAGE_FORMAT(png, ".png") \ + __ENUMERATE_IMAGE_FORMAT(ppm, ".ppm") \ + __ENUMERATE_IMAGE_FORMAT(gif, ".gif") \ + __ENUMERATE_IMAGE_FORMAT(bmp, ".bmp") \ + __ENUMERATE_IMAGE_FORMAT(ico, ".ico") \ + __ENUMERATE_IMAGE_FORMAT(jpg, ".jpg") \ + __ENUMERATE_IMAGE_FORMAT(jpg, ".jpeg") + +namespace Gfx { + +enum class BitmapFormat { + Invalid, + Indexed1, + Indexed2, + Indexed4, + Indexed8, + RGB32, + RGBA32, +}; + +enum class StorageFormat { + Indexed8, + RGB32, + RGBA32, +}; + +static StorageFormat determine_storage_format(BitmapFormat format) +{ + switch (format) { + case BitmapFormat::RGB32: + return StorageFormat::RGB32; + case BitmapFormat::RGBA32: + return StorageFormat::RGBA32; + case BitmapFormat::Indexed1: + case BitmapFormat::Indexed2: + case BitmapFormat::Indexed4: + case BitmapFormat::Indexed8: + return StorageFormat::Indexed8; + default: + ASSERT_NOT_REACHED(); + } +} + +struct BackingStore; + +enum RotationDirection { + Left, + Right +}; + +class Bitmap : public RefCounted<Bitmap> { +public: + static RefPtr<Bitmap> create(BitmapFormat, const IntSize&); + static RefPtr<Bitmap> create_shareable(BitmapFormat, const IntSize&); + static RefPtr<Bitmap> create_purgeable(BitmapFormat, const IntSize&); + static RefPtr<Bitmap> create_wrapper(BitmapFormat, const IntSize&, size_t pitch, void*); + static RefPtr<Bitmap> load_from_file(const StringView& path); + static RefPtr<Bitmap> create_with_shared_buffer(BitmapFormat, NonnullRefPtr<SharedBuffer>&&, const IntSize&); + static RefPtr<Bitmap> create_with_shared_buffer(BitmapFormat, NonnullRefPtr<SharedBuffer>&&, const IntSize&, const Vector<RGBA32>& palette); + static RefPtr<Bitmap> create_from_serialized_byte_buffer(ByteBuffer&& buffer); + static bool is_path_a_supported_image_format(const StringView& path) + { +#define __ENUMERATE_IMAGE_FORMAT(Name, Ext) \ + if (path.ends_with(Ext, CaseSensitivity::CaseInsensitive)) \ + return true; + ENUMERATE_IMAGE_FORMATS +#undef __ENUMERATE_IMAGE_FORMAT + + return false; + } + + RefPtr<Gfx::Bitmap> clone() const; + + RefPtr<Gfx::Bitmap> rotated(Gfx::RotationDirection) const; + RefPtr<Gfx::Bitmap> flipped(Gfx::Orientation) const; + RefPtr<Bitmap> to_bitmap_backed_by_shared_buffer() const; + ByteBuffer serialize_to_byte_buffer() const; + + ShareableBitmap to_shareable_bitmap(pid_t peer_pid = -1) const; + + ~Bitmap(); + + u8* scanline_u8(int y); + const u8* scanline_u8(int y) const; + RGBA32* scanline(int y); + const RGBA32* scanline(int y) const; + + IntRect rect() const { return { {}, m_size }; } + IntSize size() const { return m_size; } + int width() const { return m_size.width(); } + int height() const { return m_size.height(); } + size_t pitch() const { return m_pitch; } + int shbuf_id() const; + + SharedBuffer* shared_buffer() { return m_shared_buffer.ptr(); } + const SharedBuffer* shared_buffer() const { return m_shared_buffer.ptr(); } + + ALWAYS_INLINE bool is_indexed() const + { + return is_indexed(m_format); + } + + ALWAYS_INLINE static bool is_indexed(BitmapFormat format) + { + return format == BitmapFormat::Indexed8 || format == BitmapFormat::Indexed4 + || format == BitmapFormat::Indexed2 || format == BitmapFormat::Indexed1; + } + + static size_t palette_size(BitmapFormat format) + { + switch (format) { + case BitmapFormat::Indexed1: + return 2; + case BitmapFormat::Indexed2: + return 4; + case BitmapFormat::Indexed4: + return 16; + case BitmapFormat::Indexed8: + return 256; + default: + return 0; + } + } + + Vector<RGBA32> palette_to_vector() const; + + static unsigned bpp_for_format(BitmapFormat format) + { + switch (format) { + case BitmapFormat::Indexed1: + return 1; + case BitmapFormat::Indexed2: + return 2; + case BitmapFormat::Indexed4: + return 4; + case BitmapFormat::Indexed8: + return 8; + case BitmapFormat::RGB32: + case BitmapFormat::RGBA32: + return 32; + default: + ASSERT_NOT_REACHED(); + case BitmapFormat::Invalid: + return 0; + } + } + + static size_t minimum_pitch(size_t width, BitmapFormat); + + unsigned bpp() const + { + return bpp_for_format(m_format); + } + + void fill(Color); + + bool has_alpha_channel() const { return m_format == BitmapFormat::RGBA32; } + BitmapFormat format() const { return m_format; } + + void set_mmap_name(const StringView&); + + static constexpr size_t size_in_bytes(size_t pitch, int height) { return pitch * height; } + size_t size_in_bytes() const { return size_in_bytes(m_pitch, height()); } + + Color palette_color(u8 index) const { return Color::from_rgba(m_palette[index]); } + void set_palette_color(u8 index, Color color) { m_palette[index] = color.value(); } + + template<StorageFormat> + Color get_pixel(int x, int y) const; + Color get_pixel(int x, int y) const; + Color get_pixel(const IntPoint& position) const + { + return get_pixel(position.x(), position.y()); + } + + template<StorageFormat> + void set_pixel(int x, int y, Color); + void set_pixel(int x, int y, Color); + void set_pixel(const IntPoint& position, Color color) + { + set_pixel(position.x(), position.y(), color); + } + + bool is_purgeable() const { return m_purgeable; } + bool is_volatile() const { return m_volatile; } + void set_volatile(); + [[nodiscard]] bool set_nonvolatile(); + +private: + enum class Purgeable { + No, + Yes + }; + Bitmap(BitmapFormat, const IntSize&, Purgeable, const BackingStore&); + Bitmap(BitmapFormat, const IntSize&, size_t pitch, void*); + Bitmap(BitmapFormat, NonnullRefPtr<SharedBuffer>&&, const IntSize&, const Vector<RGBA32>& palette); + + static Optional<BackingStore> allocate_backing_store(BitmapFormat, const IntSize&, Purgeable); + + void allocate_palette_from_format(BitmapFormat, const Vector<RGBA32>& source_palette); + + IntSize m_size; + void* m_data { nullptr }; + RGBA32* m_palette { nullptr }; + size_t m_pitch { 0 }; + BitmapFormat m_format { BitmapFormat::Invalid }; + bool m_needs_munmap { false }; + bool m_purgeable { false }; + bool m_volatile { false }; + RefPtr<SharedBuffer> m_shared_buffer; +}; + +inline u8* Bitmap::scanline_u8(int y) +{ + ASSERT(y >= 0 && y < height()); + return reinterpret_cast<u8*>(m_data) + (y * m_pitch); +} + +inline const u8* Bitmap::scanline_u8(int y) const +{ + ASSERT(y >= 0 && y < height()); + return reinterpret_cast<const u8*>(m_data) + (y * m_pitch); +} + +inline RGBA32* Bitmap::scanline(int y) +{ + return reinterpret_cast<RGBA32*>(scanline_u8(y)); +} + +inline const RGBA32* Bitmap::scanline(int y) const +{ + return reinterpret_cast<const RGBA32*>(scanline_u8(y)); +} + +template<> +inline Color Bitmap::get_pixel<StorageFormat::RGB32>(int x, int y) const +{ + ASSERT(x >= 0 && x < width()); + return Color::from_rgb(scanline(y)[x]); +} + +template<> +inline Color Bitmap::get_pixel<StorageFormat::RGBA32>(int x, int y) const +{ + ASSERT(x >= 0 && x < width()); + return Color::from_rgba(scanline(y)[x]); +} + +template<> +inline Color Bitmap::get_pixel<StorageFormat::Indexed8>(int x, int y) const +{ + ASSERT(x >= 0 && x < width()); + return Color::from_rgb(m_palette[scanline_u8(y)[x]]); +} + +inline Color Bitmap::get_pixel(int x, int y) const +{ + switch (determine_storage_format(m_format)) { + case StorageFormat::RGB32: + return get_pixel<StorageFormat::RGB32>(x, y); + case StorageFormat::RGBA32: + return get_pixel<StorageFormat::RGBA32>(x, y); + case StorageFormat::Indexed8: + return get_pixel<StorageFormat::Indexed8>(x, y); + default: + ASSERT_NOT_REACHED(); + } +} + +template<> +inline void Bitmap::set_pixel<StorageFormat::RGB32>(int x, int y, Color color) +{ + ASSERT(x >= 0 && x < width()); + scanline(y)[x] = color.value(); +} +template<> +inline void Bitmap::set_pixel<StorageFormat::RGBA32>(int x, int y, Color color) +{ + ASSERT(x >= 0 && x < width()); + scanline(y)[x] = color.value(); // drop alpha +} +inline void Bitmap::set_pixel(int x, int y, Color color) +{ + switch (determine_storage_format(m_format)) { + case StorageFormat::RGB32: + set_pixel<StorageFormat::RGB32>(x, y, color); + break; + case StorageFormat::RGBA32: + set_pixel<StorageFormat::RGBA32>(x, y, color); + break; + case StorageFormat::Indexed8: + ASSERT_NOT_REACHED(); + default: + ASSERT_NOT_REACHED(); + } +} + +} |