2020-01-18 09:38:21 +01:00
|
|
|
/*
|
2024-04-28 12:40:55 +02:00
|
|
|
* Copyright (c) 2018-2024, Andreas Kling <kling@serenityos.org>
|
2022-09-30 12:26:13 -05:00
|
|
|
* Copyright (c) 2022, Timothy Slater <tslater2006@gmail.com>
|
2020-01-18 09:38:21 +01:00
|
|
|
*
|
2021-04-22 01:24:48 -07:00
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
2020-01-18 09:38:21 +01:00
|
|
|
*/
|
|
|
|
|
2022-09-30 12:26:13 -05:00
|
|
|
#include <AK/Bitmap.h>
|
2023-12-16 17:49:34 +03:30
|
|
|
#include <AK/ByteString.h>
|
2020-04-15 16:55:07 +02:00
|
|
|
#include <AK/Checked.h>
|
2021-01-18 16:26:45 -05:00
|
|
|
#include <AK/LexicalPath.h>
|
2020-03-08 12:05:14 +01:00
|
|
|
#include <AK/Memory.h>
|
2023-02-08 18:28:39 +01:00
|
|
|
#include <AK/MemoryStream.h>
|
2023-01-14 19:30:07 -05:00
|
|
|
#include <LibCore/File.h>
|
2021-11-23 11:32:25 +01:00
|
|
|
#include <LibCore/MappedFile.h>
|
2023-01-20 03:27:10 +02:00
|
|
|
#include <LibCore/MimeData.h>
|
2020-06-22 21:35:22 +02:00
|
|
|
#include <LibGfx/Bitmap.h>
|
2023-03-21 14:58:06 -04:00
|
|
|
#include <LibGfx/ImageFormats/ImageDecoder.h>
|
2020-03-29 19:04:05 +02:00
|
|
|
#include <LibGfx/ShareableBitmap.h>
|
2024-07-17 19:14:16 +02:00
|
|
|
#include <LibIPC/Decoder.h>
|
|
|
|
#include <LibIPC/Encoder.h>
|
|
|
|
#include <LibIPC/File.h>
|
2021-07-24 22:49:48 +02:00
|
|
|
#include <errno.h>
|
2019-02-07 23:13:47 +01:00
|
|
|
|
2020-02-06 11:56:38 +01:00
|
|
|
namespace Gfx {
|
|
|
|
|
2020-09-12 17:17:50 +01:00
|
|
|
struct BackingStore {
|
|
|
|
void* data { nullptr };
|
|
|
|
size_t pitch { 0 };
|
|
|
|
size_t size_in_bytes { 0 };
|
|
|
|
};
|
|
|
|
|
2024-06-05 08:17:28 +02:00
|
|
|
size_t Bitmap::minimum_pitch(size_t width, BitmapFormat format)
|
2020-09-06 23:59:20 +02:00
|
|
|
{
|
|
|
|
size_t element_size;
|
|
|
|
switch (determine_storage_format(format)) {
|
2021-03-16 12:00:43 +01:00
|
|
|
case StorageFormat::BGRx8888:
|
|
|
|
case StorageFormat::BGRA8888:
|
2021-03-16 12:09:15 +01:00
|
|
|
case StorageFormat::RGBA8888:
|
2020-09-06 23:59:20 +02:00
|
|
|
element_size = 4;
|
|
|
|
break;
|
|
|
|
default:
|
2021-02-23 20:42:32 +01:00
|
|
|
VERIFY_NOT_REACHED();
|
2020-09-06 23:59:20 +02:00
|
|
|
}
|
|
|
|
|
2024-06-05 08:17:28 +02:00
|
|
|
return width * element_size;
|
2020-09-06 23:59:20 +02:00
|
|
|
}
|
|
|
|
|
2024-06-05 08:17:28 +02:00
|
|
|
static bool size_would_overflow(BitmapFormat format, IntSize size)
|
2019-02-11 09:47:10 +01:00
|
|
|
{
|
2020-04-15 11:57:24 +02:00
|
|
|
if (size.width() < 0 || size.height() < 0)
|
|
|
|
return true;
|
2020-08-30 14:18:54 +02:00
|
|
|
// This check is a bit arbitrary, but should protect us from most shenanigans:
|
2024-06-05 08:17:28 +02:00
|
|
|
if (size.width() >= INT16_MAX || size.height() >= INT16_MAX)
|
2020-08-30 14:18:54 +02:00
|
|
|
return true;
|
2020-09-06 23:59:20 +02:00
|
|
|
// In contrast, this check is absolutely necessary:
|
2024-06-05 08:17:28 +02:00
|
|
|
size_t pitch = Bitmap::minimum_pitch(size.width(), format);
|
|
|
|
return Checked<size_t>::multiplication_would_overflow(pitch, size.height());
|
2020-04-15 11:57:24 +02:00
|
|
|
}
|
|
|
|
|
2024-06-05 08:17:28 +02:00
|
|
|
ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::create(BitmapFormat format, IntSize size)
|
2020-04-15 11:57:24 +02:00
|
|
|
{
|
2024-06-05 08:17:28 +02:00
|
|
|
auto backing_store = TRY(Bitmap::allocate_backing_store(format, size));
|
|
|
|
return AK::adopt_nonnull_ref_or_enomem(new (nothrow) Bitmap(format, size, backing_store));
|
2021-01-02 16:23:04 +01:00
|
|
|
}
|
|
|
|
|
2024-06-05 08:17:28 +02:00
|
|
|
ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::create_shareable(BitmapFormat format, IntSize size)
|
2021-01-02 16:23:04 +01:00
|
|
|
{
|
2024-06-05 08:17:28 +02:00
|
|
|
if (size_would_overflow(format, size))
|
2023-01-20 20:06:05 +01:00
|
|
|
return Error::from_string_literal("Gfx::Bitmap::create_shareable size overflow");
|
2021-01-02 16:23:04 +01:00
|
|
|
|
2024-06-05 08:17:28 +02:00
|
|
|
auto const pitch = minimum_pitch(size.width(), format);
|
|
|
|
auto const data_size = size_in_bytes(pitch, size.height());
|
2021-01-16 23:14:24 +01:00
|
|
|
|
2021-11-06 11:44:05 +01:00
|
|
|
auto buffer = TRY(Core::AnonymousBuffer::create_with_size(round_up_to_power_of_two(data_size, PAGE_SIZE)));
|
2024-06-05 08:17:28 +02:00
|
|
|
auto bitmap = TRY(Bitmap::create_with_anonymous_buffer(format, buffer, size));
|
2021-11-06 11:44:05 +01:00
|
|
|
return bitmap;
|
2019-12-18 20:50:05 +01:00
|
|
|
}
|
|
|
|
|
2024-06-05 08:17:28 +02:00
|
|
|
Bitmap::Bitmap(BitmapFormat format, IntSize size, BackingStore const& backing_store)
|
2019-02-11 09:47:10 +01:00
|
|
|
: m_size(size)
|
2020-09-12 17:17:50 +01:00
|
|
|
, m_data(backing_store.data)
|
|
|
|
, m_pitch(backing_store.pitch)
|
2019-02-19 01:42:53 +01:00
|
|
|
, m_format(format)
|
2019-02-11 09:47:10 +01:00
|
|
|
{
|
2021-02-23 20:42:32 +01:00
|
|
|
VERIFY(!m_size.is_empty());
|
2024-06-05 08:17:28 +02:00
|
|
|
VERIFY(!size_would_overflow(format, size));
|
2021-02-23 20:42:32 +01:00
|
|
|
VERIFY(m_data);
|
|
|
|
VERIFY(backing_store.size_in_bytes == size_in_bytes());
|
2024-06-18 02:11:35 +03:00
|
|
|
m_destruction_callback = [data = m_data, size_in_bytes = this->size_in_bytes()] {
|
|
|
|
kfree_sized(data, size_in_bytes);
|
|
|
|
};
|
2019-02-16 12:22:00 +01:00
|
|
|
}
|
2019-01-14 20:00:42 +01:00
|
|
|
|
2024-06-18 02:11:35 +03:00
|
|
|
ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::create_wrapper(BitmapFormat format, IntSize size, size_t pitch, void* data, Function<void()>&& destruction_callback)
|
2019-01-14 20:00:42 +01:00
|
|
|
{
|
2024-06-05 08:17:28 +02:00
|
|
|
if (size_would_overflow(format, size))
|
2023-01-20 20:06:05 +01:00
|
|
|
return Error::from_string_literal("Gfx::Bitmap::create_wrapper size overflow");
|
2024-06-18 02:11:35 +03:00
|
|
|
return adopt_ref(*new Bitmap(format, size, pitch, data, move(destruction_callback)));
|
2019-01-14 20:00:42 +01:00
|
|
|
}
|
2019-01-09 03:51:34 +01:00
|
|
|
|
2024-06-05 08:17:28 +02:00
|
|
|
ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::load_from_file(StringView path, Optional<IntSize> ideal_size)
|
2021-09-07 21:22:50 +10:00
|
|
|
{
|
2023-02-16 10:51:16 -05:00
|
|
|
auto file = TRY(Core::File::open(path, Core::File::OpenMode::Read));
|
2023-07-02 22:36:35 +01:00
|
|
|
return load_from_file(move(file), path, ideal_size);
|
2021-11-14 21:42:12 +01:00
|
|
|
}
|
|
|
|
|
2023-07-02 22:36:35 +01:00
|
|
|
ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::load_from_file(NonnullOwnPtr<Core::File> file, StringView path, Optional<IntSize> ideal_size)
|
2023-01-14 19:30:07 -05:00
|
|
|
{
|
|
|
|
auto mapped_file = TRY(Core::MappedFile::map_from_file(move(file), path));
|
|
|
|
auto mime_type = Core::guess_mime_type_based_on_filename(path);
|
2023-07-02 22:36:35 +01:00
|
|
|
return load_from_bytes(mapped_file->bytes(), ideal_size, mime_type);
|
|
|
|
}
|
|
|
|
|
2023-12-16 17:49:34 +03:30
|
|
|
ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::load_from_bytes(ReadonlyBytes bytes, Optional<IntSize> ideal_size, Optional<ByteString> mine_type)
|
2023-07-02 22:36:35 +01:00
|
|
|
{
|
2024-03-04 18:07:43 -05:00
|
|
|
if (auto decoder = TRY(ImageDecoder::try_create_for_raw_bytes(bytes, mine_type))) {
|
2023-07-02 22:36:35 +01:00
|
|
|
auto frame = TRY(decoder->frame(0, ideal_size));
|
2023-01-14 19:30:07 -05:00
|
|
|
if (auto& bitmap = frame.image)
|
|
|
|
return bitmap.release_nonnull();
|
|
|
|
}
|
|
|
|
|
|
|
|
return Error::from_string_literal("Gfx::Bitmap unable to load from file");
|
|
|
|
}
|
|
|
|
|
2024-06-18 02:11:35 +03:00
|
|
|
Bitmap::Bitmap(BitmapFormat format, IntSize size, size_t pitch, void* data, Function<void()>&& destruction_callback)
|
2019-01-09 03:51:34 +01:00
|
|
|
: m_size(size)
|
2019-01-10 05:36:32 +01:00
|
|
|
, m_data(data)
|
2019-08-19 13:29:19 +02:00
|
|
|
, m_pitch(pitch)
|
2019-02-19 01:42:53 +01:00
|
|
|
, m_format(format)
|
2024-06-18 02:11:35 +03:00
|
|
|
, m_destruction_callback(move(destruction_callback))
|
2019-01-09 03:51:34 +01:00
|
|
|
{
|
2024-06-05 08:17:28 +02:00
|
|
|
VERIFY(pitch >= minimum_pitch(size.width(), format));
|
|
|
|
VERIFY(!size_would_overflow(format, size));
|
2020-09-06 23:59:20 +02:00
|
|
|
// FIXME: assert that `data` is actually long enough!
|
2019-01-09 02:06:04 +01:00
|
|
|
}
|
|
|
|
|
2024-06-05 08:17:28 +02:00
|
|
|
ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::create_with_anonymous_buffer(BitmapFormat format, Core::AnonymousBuffer buffer, IntSize size)
|
2021-01-15 12:09:37 +01:00
|
|
|
{
|
2024-06-05 08:17:28 +02:00
|
|
|
if (size_would_overflow(format, size))
|
2023-01-20 20:06:05 +01:00
|
|
|
return Error::from_string_literal("Gfx::Bitmap::create_with_anonymous_buffer size overflow");
|
2021-01-15 12:09:37 +01:00
|
|
|
|
2024-06-05 08:17:28 +02:00
|
|
|
return adopt_nonnull_ref_or_enomem(new (nothrow) Bitmap(format, move(buffer), size));
|
2022-11-25 10:56:48 +08:00
|
|
|
}
|
|
|
|
|
2024-06-05 08:17:28 +02:00
|
|
|
Bitmap::Bitmap(BitmapFormat format, Core::AnonymousBuffer buffer, IntSize size)
|
2021-01-15 12:09:37 +01:00
|
|
|
: m_size(size)
|
2021-05-24 12:24:38 +02:00
|
|
|
, m_data(buffer.data<void>())
|
2024-06-05 08:17:28 +02:00
|
|
|
, m_pitch(minimum_pitch(size.width(), format))
|
2021-01-15 12:09:37 +01:00
|
|
|
, m_format(format)
|
2021-07-07 17:33:35 +02:00
|
|
|
, m_buffer(move(buffer))
|
2021-01-15 12:09:37 +01:00
|
|
|
{
|
2024-06-05 08:17:28 +02:00
|
|
|
VERIFY(!size_would_overflow(format, size));
|
2021-01-15 12:09:37 +01:00
|
|
|
}
|
|
|
|
|
2021-11-06 11:52:35 +01:00
|
|
|
ErrorOr<NonnullRefPtr<Gfx::Bitmap>> Bitmap::clone() const
|
2020-09-12 12:20:34 +01:00
|
|
|
{
|
2024-06-05 08:17:28 +02:00
|
|
|
auto new_bitmap = TRY(Bitmap::create(format(), size()));
|
2020-09-12 12:20:34 +01:00
|
|
|
|
2021-02-23 20:42:32 +01:00
|
|
|
VERIFY(size_in_bytes() == new_bitmap->size_in_bytes());
|
2020-09-12 12:20:34 +01:00
|
|
|
memcpy(new_bitmap->scanline(0), scanline(0), size_in_bytes());
|
|
|
|
|
2021-11-06 19:30:59 +01:00
|
|
|
return new_bitmap;
|
2020-09-12 12:20:34 +01:00
|
|
|
}
|
|
|
|
|
2023-09-03 19:24:00 +01:00
|
|
|
void Bitmap::apply_mask(Gfx::Bitmap const& mask, MaskKind mask_kind)
|
|
|
|
{
|
|
|
|
VERIFY(size() == mask.size());
|
|
|
|
|
|
|
|
for (int y = 0; y < height(); y++) {
|
|
|
|
for (int x = 0; x < width(); x++) {
|
|
|
|
auto color = get_pixel(x, y);
|
|
|
|
auto mask_color = mask.get_pixel(x, y);
|
|
|
|
if (mask_kind == MaskKind::Luminance) {
|
|
|
|
color = color.with_alpha(color.alpha() * mask_color.alpha() * mask_color.luminosity() / (255 * 255));
|
|
|
|
} else {
|
|
|
|
VERIFY(mask_kind == MaskKind::Alpha);
|
|
|
|
color = color.with_alpha(color.alpha() * mask_color.alpha() / 255);
|
|
|
|
}
|
|
|
|
set_pixel(x, y, color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-06 12:26:04 +01:00
|
|
|
ErrorOr<NonnullRefPtr<Gfx::Bitmap>> Bitmap::scaled(int sx, int sy) const
|
2021-04-20 12:40:03 -07:00
|
|
|
{
|
|
|
|
VERIFY(sx >= 0 && sy >= 0);
|
|
|
|
if (sx == 1 && sy == 1)
|
2023-02-19 22:24:45 +01:00
|
|
|
return clone();
|
2021-04-20 12:40:03 -07:00
|
|
|
|
2024-06-05 08:17:28 +02:00
|
|
|
auto new_bitmap = TRY(Gfx::Bitmap::create(format(), { width() * sx, height() * sy }));
|
2021-04-20 12:40:03 -07:00
|
|
|
|
2024-06-05 08:17:28 +02:00
|
|
|
auto old_width = width();
|
|
|
|
auto old_height = height();
|
2021-04-20 12:40:03 -07:00
|
|
|
|
|
|
|
for (int y = 0; y < old_height; y++) {
|
|
|
|
for (int x = 0; x < old_width; x++) {
|
|
|
|
auto color = get_pixel(x, y);
|
|
|
|
|
|
|
|
auto base_x = x * sx;
|
|
|
|
auto base_y = y * sy;
|
|
|
|
for (int new_y = base_y; new_y < base_y + sy; new_y++) {
|
|
|
|
for (int new_x = base_x; new_x < base_x + sx; new_x++) {
|
|
|
|
new_bitmap->set_pixel(new_x, new_y, color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-06 19:30:59 +01:00
|
|
|
return new_bitmap;
|
2021-04-20 12:40:03 -07:00
|
|
|
}
|
|
|
|
|
2021-11-06 12:26:04 +01:00
|
|
|
ErrorOr<NonnullRefPtr<Gfx::Bitmap>> Bitmap::scaled(float sx, float sy) const
|
2021-04-20 12:40:03 -07:00
|
|
|
{
|
|
|
|
VERIFY(sx >= 0.0f && sy >= 0.0f);
|
|
|
|
if (floorf(sx) == sx && floorf(sy) == sy)
|
|
|
|
return scaled(static_cast<int>(sx), static_cast<int>(sy));
|
|
|
|
|
2021-05-22 00:45:32 +02:00
|
|
|
int scaled_width = (int)ceilf(sx * (float)width());
|
|
|
|
int scaled_height = (int)ceilf(sy * (float)height());
|
2023-10-18 13:29:52 -04:00
|
|
|
return scaled_to_size({ scaled_width, scaled_height });
|
|
|
|
}
|
2021-05-22 00:45:32 +02:00
|
|
|
|
2023-10-18 13:29:52 -04:00
|
|
|
// http://fourier.eng.hmc.edu/e161/lectures/resize/node3.html
|
|
|
|
ErrorOr<NonnullRefPtr<Gfx::Bitmap>> Bitmap::scaled_to_size(Gfx::IntSize size) const
|
|
|
|
{
|
2024-06-05 08:17:28 +02:00
|
|
|
auto new_bitmap = TRY(Gfx::Bitmap::create(format(), size));
|
2021-04-20 12:40:03 -07:00
|
|
|
|
2024-06-05 08:17:28 +02:00
|
|
|
auto old_width = width();
|
|
|
|
auto old_height = height();
|
|
|
|
auto new_width = new_bitmap->width();
|
|
|
|
auto new_height = new_bitmap->height();
|
2021-04-20 12:40:03 -07:00
|
|
|
|
2022-11-16 11:38:51 +01:00
|
|
|
if (old_width == 1 && old_height == 1) {
|
|
|
|
new_bitmap->fill(get_pixel(0, 0));
|
|
|
|
return new_bitmap;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (old_width > 1 && old_height > 1) {
|
|
|
|
// The interpolation goes out of bounds on the bottom- and right-most edges.
|
|
|
|
// We handle those in two specialized loops not only to make them faster, but
|
|
|
|
// also to avoid four branch checks for every pixel.
|
|
|
|
for (int y = 0; y < new_height - 1; y++) {
|
|
|
|
for (int x = 0; x < new_width - 1; x++) {
|
|
|
|
auto p = static_cast<float>(x) * static_cast<float>(old_width - 1) / static_cast<float>(new_width - 1);
|
|
|
|
auto q = static_cast<float>(y) * static_cast<float>(old_height - 1) / static_cast<float>(new_height - 1);
|
|
|
|
|
|
|
|
int i = floorf(p);
|
|
|
|
int j = floorf(q);
|
|
|
|
float u = p - static_cast<float>(i);
|
|
|
|
float v = q - static_cast<float>(j);
|
|
|
|
|
|
|
|
auto a = get_pixel(i, j);
|
|
|
|
auto b = get_pixel(i + 1, j);
|
|
|
|
auto c = get_pixel(i, j + 1);
|
|
|
|
auto d = get_pixel(i + 1, j + 1);
|
|
|
|
|
|
|
|
auto e = a.mixed_with(b, u);
|
|
|
|
auto f = c.mixed_with(d, u);
|
|
|
|
auto color = e.mixed_with(f, v);
|
|
|
|
new_bitmap->set_pixel(x, y, color);
|
|
|
|
}
|
|
|
|
}
|
2021-04-20 12:40:03 -07:00
|
|
|
|
2022-11-16 11:38:51 +01:00
|
|
|
// Bottom strip (excluding last pixel)
|
|
|
|
auto old_bottom_y = old_height - 1;
|
|
|
|
auto new_bottom_y = new_height - 1;
|
2021-04-20 12:40:03 -07:00
|
|
|
for (int x = 0; x < new_width - 1; x++) {
|
|
|
|
auto p = static_cast<float>(x) * static_cast<float>(old_width - 1) / static_cast<float>(new_width - 1);
|
|
|
|
|
2021-07-05 18:56:06 +02:00
|
|
|
int i = floorf(p);
|
2021-04-20 12:40:03 -07:00
|
|
|
float u = p - static_cast<float>(i);
|
|
|
|
|
2022-11-16 11:38:51 +01:00
|
|
|
auto a = get_pixel(i, old_bottom_y);
|
|
|
|
auto b = get_pixel(i + 1, old_bottom_y);
|
|
|
|
auto color = a.mixed_with(b, u);
|
|
|
|
new_bitmap->set_pixel(x, new_bottom_y, color);
|
2021-04-20 12:40:03 -07:00
|
|
|
}
|
|
|
|
|
2022-11-16 11:38:51 +01:00
|
|
|
// Right strip (excluding last pixel)
|
|
|
|
auto old_right_x = old_width - 1;
|
|
|
|
auto new_right_x = new_width - 1;
|
|
|
|
for (int y = 0; y < new_height - 1; y++) {
|
|
|
|
auto q = static_cast<float>(y) * static_cast<float>(old_height - 1) / static_cast<float>(new_height - 1);
|
2021-04-20 12:40:03 -07:00
|
|
|
|
2022-11-16 11:38:51 +01:00
|
|
|
int j = floorf(q);
|
|
|
|
float v = q - static_cast<float>(j);
|
2021-04-20 12:40:03 -07:00
|
|
|
|
2022-11-16 11:38:51 +01:00
|
|
|
auto c = get_pixel(old_right_x, j);
|
|
|
|
auto d = get_pixel(old_right_x, j + 1);
|
2021-04-20 12:40:03 -07:00
|
|
|
|
2022-11-16 11:38:51 +01:00
|
|
|
auto color = c.mixed_with(d, v);
|
|
|
|
new_bitmap->set_pixel(new_right_x, y, color);
|
|
|
|
}
|
2021-04-20 12:40:03 -07:00
|
|
|
|
2022-11-16 11:38:51 +01:00
|
|
|
// Bottom-right pixel
|
2024-06-05 08:17:28 +02:00
|
|
|
new_bitmap->set_pixel(new_width - 1, new_height - 1, get_pixel(width() - 1, height() - 1));
|
2022-11-16 11:38:51 +01:00
|
|
|
return new_bitmap;
|
|
|
|
} else if (old_height == 1) {
|
|
|
|
// Copy horizontal strip multiple times (excluding last pixel to out of bounds).
|
|
|
|
auto old_bottom_y = old_height - 1;
|
|
|
|
for (int x = 0; x < new_width - 1; x++) {
|
|
|
|
auto p = static_cast<float>(x) * static_cast<float>(old_width - 1) / static_cast<float>(new_width - 1);
|
|
|
|
int i = floorf(p);
|
|
|
|
float u = p - static_cast<float>(i);
|
2021-04-20 12:40:03 -07:00
|
|
|
|
2022-11-16 11:38:51 +01:00
|
|
|
auto a = get_pixel(i, old_bottom_y);
|
|
|
|
auto b = get_pixel(i + 1, old_bottom_y);
|
|
|
|
auto color = a.mixed_with(b, u);
|
|
|
|
for (int new_bottom_y = 0; new_bottom_y < new_height; new_bottom_y++) {
|
|
|
|
// Interpolate color only once and then copy into all columns.
|
|
|
|
new_bitmap->set_pixel(x, new_bottom_y, color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (int new_bottom_y = 0; new_bottom_y < new_height; new_bottom_y++) {
|
|
|
|
// Copy last pixel of horizontal strip
|
2024-06-05 08:17:28 +02:00
|
|
|
new_bitmap->set_pixel(new_width - 1, new_bottom_y, get_pixel(width() - 1, old_bottom_y));
|
2022-11-16 11:38:51 +01:00
|
|
|
}
|
|
|
|
return new_bitmap;
|
|
|
|
} else if (old_width == 1) {
|
|
|
|
// Copy vertical strip multiple times (excluding last pixel to avoid out of bounds).
|
|
|
|
auto old_right_x = old_width - 1;
|
|
|
|
for (int y = 0; y < new_height - 1; y++) {
|
|
|
|
auto q = static_cast<float>(y) * static_cast<float>(old_height - 1) / static_cast<float>(new_height - 1);
|
|
|
|
int j = floorf(q);
|
|
|
|
float v = q - static_cast<float>(j);
|
2021-04-20 12:40:03 -07:00
|
|
|
|
2022-11-16 11:38:51 +01:00
|
|
|
auto c = get_pixel(old_right_x, j);
|
|
|
|
auto d = get_pixel(old_right_x, j + 1);
|
2021-04-20 12:40:03 -07:00
|
|
|
|
2022-11-16 11:38:51 +01:00
|
|
|
auto color = c.mixed_with(d, v);
|
|
|
|
for (int new_right_x = 0; new_right_x < new_width; new_right_x++) {
|
|
|
|
// Interpolate color only once and copy into all rows.
|
|
|
|
new_bitmap->set_pixel(new_right_x, y, color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (int new_right_x = 0; new_right_x < new_width; new_right_x++) {
|
|
|
|
// Copy last pixel of vertical strip
|
2024-06-05 08:17:28 +02:00
|
|
|
new_bitmap->set_pixel(new_right_x, new_height - 1, get_pixel(old_right_x, height() - 1));
|
2022-11-16 11:38:51 +01:00
|
|
|
}
|
|
|
|
}
|
2021-11-06 19:30:59 +01:00
|
|
|
return new_bitmap;
|
2021-04-20 12:40:03 -07:00
|
|
|
}
|
|
|
|
|
2022-09-15 08:31:29 +01:00
|
|
|
ErrorOr<NonnullRefPtr<Gfx::Bitmap>> Bitmap::cropped(Gfx::IntRect crop, Optional<BitmapFormat> new_bitmap_format) const
|
2021-05-09 22:19:04 +03:00
|
|
|
{
|
2024-06-05 08:17:28 +02:00
|
|
|
auto new_bitmap = TRY(Gfx::Bitmap::create(new_bitmap_format.value_or(format()), { crop.width(), crop.height() }));
|
|
|
|
|
|
|
|
for (int y = 0; y < crop.height(); ++y) {
|
|
|
|
for (int x = 0; x < crop.width(); ++x) {
|
|
|
|
int global_x = x + crop.left();
|
|
|
|
int global_y = y + crop.top();
|
|
|
|
if (global_x >= width() || global_y >= height() || global_x < 0 || global_y < 0) {
|
2021-05-09 22:19:04 +03:00
|
|
|
new_bitmap->set_pixel(x, y, Gfx::Color::Black);
|
|
|
|
} else {
|
|
|
|
new_bitmap->set_pixel(x, y, get_pixel(global_x, global_y));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-11-06 19:30:59 +01:00
|
|
|
return new_bitmap;
|
2021-05-09 22:19:04 +03:00
|
|
|
}
|
|
|
|
|
2021-11-06 13:15:43 +01:00
|
|
|
ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::to_bitmap_backed_by_anonymous_buffer() const
|
2021-01-15 22:36:36 +01:00
|
|
|
{
|
2023-02-19 23:02:17 +01:00
|
|
|
if (m_buffer.is_valid()) {
|
|
|
|
// FIXME: The const_cast here is awkward.
|
|
|
|
return NonnullRefPtr { const_cast<Bitmap&>(*this) };
|
|
|
|
}
|
2021-11-06 13:15:43 +01:00
|
|
|
auto buffer = TRY(Core::AnonymousBuffer::create_with_size(round_up_to_power_of_two(size_in_bytes(), PAGE_SIZE)));
|
2024-06-05 08:17:28 +02:00
|
|
|
auto bitmap = TRY(Bitmap::create_with_anonymous_buffer(m_format, move(buffer), size()));
|
2021-01-15 22:36:36 +01:00
|
|
|
memcpy(bitmap->scanline(0), scanline(0), size_in_bytes());
|
|
|
|
return bitmap;
|
|
|
|
}
|
|
|
|
|
2020-02-06 11:56:38 +01:00
|
|
|
Bitmap::~Bitmap()
|
2019-01-09 02:06:04 +01:00
|
|
|
{
|
2024-06-18 02:11:35 +03:00
|
|
|
if (m_destruction_callback)
|
|
|
|
m_destruction_callback();
|
2019-01-09 03:51:34 +01:00
|
|
|
m_data = nullptr;
|
2019-01-09 02:06:04 +01:00
|
|
|
}
|
|
|
|
|
2023-06-13 19:23:38 -04:00
|
|
|
void Bitmap::strip_alpha_channel()
|
|
|
|
{
|
|
|
|
VERIFY(m_format == BitmapFormat::BGRA8888 || m_format == BitmapFormat::BGRx8888);
|
|
|
|
for (ARGB32& pixel : *this)
|
|
|
|
pixel = 0xff000000 | (pixel & 0xffffff);
|
|
|
|
m_format = BitmapFormat::BGRx8888;
|
|
|
|
}
|
|
|
|
|
2020-02-06 11:56:38 +01:00
|
|
|
void Bitmap::fill(Color color)
|
2019-06-10 19:29:50 +02:00
|
|
|
{
|
2024-06-05 08:17:28 +02:00
|
|
|
for (int y = 0; y < height(); ++y) {
|
2019-06-10 19:29:50 +02:00
|
|
|
auto* scanline = this->scanline(y);
|
2024-06-05 08:17:28 +02:00
|
|
|
fast_u32_fill(scanline, color.value(), width());
|
2019-06-10 19:29:50 +02:00
|
|
|
}
|
|
|
|
}
|
2019-12-18 20:50:05 +01:00
|
|
|
|
2021-11-06 13:15:43 +01:00
|
|
|
Gfx::ShareableBitmap Bitmap::to_shareable_bitmap() const
|
2020-03-29 19:04:05 +02:00
|
|
|
{
|
2021-11-06 13:15:43 +01:00
|
|
|
auto bitmap_or_error = to_bitmap_backed_by_anonymous_buffer();
|
|
|
|
if (bitmap_or_error.is_error())
|
2020-04-15 11:57:24 +02:00
|
|
|
return {};
|
2021-11-06 13:15:43 +01:00
|
|
|
return Gfx::ShareableBitmap { bitmap_or_error.release_value_but_fixme_should_propagate_errors(), Gfx::ShareableBitmap::ConstructWithKnownGoodBitmap };
|
2020-03-29 19:04:05 +02:00
|
|
|
}
|
|
|
|
|
2024-06-05 08:17:28 +02:00
|
|
|
ErrorOr<BackingStore> Bitmap::allocate_backing_store(BitmapFormat format, IntSize size)
|
2020-09-12 17:17:50 +01:00
|
|
|
{
|
2024-04-28 12:40:55 +02:00
|
|
|
if (size.is_empty())
|
|
|
|
return Error::from_string_literal("Gfx::Bitmap backing store size is empty");
|
|
|
|
|
2024-06-05 08:17:28 +02:00
|
|
|
if (size_would_overflow(format, size))
|
2022-07-11 17:57:32 +00:00
|
|
|
return Error::from_string_literal("Gfx::Bitmap backing store size overflow");
|
2020-09-12 17:17:50 +01:00
|
|
|
|
2024-06-05 08:17:28 +02:00
|
|
|
auto const pitch = minimum_pitch(size.width(), format);
|
|
|
|
auto const data_size_in_bytes = size_in_bytes(pitch, size.height());
|
2020-09-12 17:17:50 +01:00
|
|
|
|
2024-04-28 12:40:55 +02:00
|
|
|
void* data = kcalloc(1, data_size_in_bytes);
|
|
|
|
if (data == nullptr)
|
2021-11-06 10:34:14 +01:00
|
|
|
return Error::from_errno(errno);
|
2021-11-06 10:15:13 +01:00
|
|
|
return BackingStore { data, pitch, data_size_in_bytes };
|
2020-09-12 17:17:50 +01:00
|
|
|
}
|
|
|
|
|
2022-04-15 01:07:15 +02:00
|
|
|
bool Bitmap::visually_equals(Bitmap const& other) const
|
|
|
|
{
|
|
|
|
auto own_width = width();
|
|
|
|
auto own_height = height();
|
|
|
|
if (other.width() != own_width || other.height() != own_height)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
for (auto y = 0; y < own_height; ++y) {
|
|
|
|
for (auto x = 0; x < own_width; ++x) {
|
|
|
|
if (get_pixel(x, y) != other.get_pixel(x, y))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-02-06 11:56:38 +01:00
|
|
|
}
|
2024-07-17 19:14:16 +02:00
|
|
|
|
|
|
|
namespace IPC {
|
|
|
|
|
|
|
|
template<>
|
|
|
|
ErrorOr<void> encode(Encoder& encoder, AK::NonnullRefPtr<Gfx::Bitmap> const& bitmap)
|
|
|
|
{
|
|
|
|
Core::AnonymousBuffer buffer;
|
|
|
|
if (bitmap->anonymous_buffer().is_valid()) {
|
|
|
|
buffer = bitmap->anonymous_buffer();
|
|
|
|
} else {
|
|
|
|
buffer = MUST(Core::AnonymousBuffer::create_with_size(bitmap->size_in_bytes()));
|
|
|
|
memcpy(buffer.data<void>(), bitmap->scanline(0), bitmap->size_in_bytes());
|
|
|
|
}
|
|
|
|
TRY(encoder.encode(TRY(IPC::File::clone_fd(buffer.fd()))));
|
|
|
|
TRY(encoder.encode(static_cast<u32>(bitmap->format())));
|
|
|
|
TRY(encoder.encode(bitmap->size_in_bytes()));
|
|
|
|
TRY(encoder.encode(bitmap->pitch()));
|
|
|
|
TRY(encoder.encode(bitmap->size()));
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
template<>
|
|
|
|
ErrorOr<AK::NonnullRefPtr<Gfx::Bitmap>> decode(Decoder& decoder)
|
|
|
|
{
|
|
|
|
auto anon_file = TRY(decoder.decode<IPC::File>());
|
|
|
|
auto raw_bitmap_format = TRY(decoder.decode<u32>());
|
|
|
|
if (!Gfx::is_valid_bitmap_format(raw_bitmap_format))
|
|
|
|
return Error::from_string_literal("IPC: Invalid Gfx::ShareableBitmap format");
|
|
|
|
auto bitmap_format = static_cast<Gfx::BitmapFormat>(raw_bitmap_format);
|
|
|
|
auto size_in_bytes = TRY(decoder.decode<size_t>());
|
|
|
|
auto pitch = TRY(decoder.decode<size_t>());
|
|
|
|
auto size = TRY(decoder.decode<Gfx::IntSize>());
|
|
|
|
auto* data = TRY(Core::System::mmap(nullptr, round_up_to_power_of_two(size_in_bytes, PAGE_SIZE), PROT_READ | PROT_WRITE, MAP_SHARED, anon_file.fd(), 0));
|
|
|
|
return Gfx::Bitmap::create_wrapper(bitmap_format, size, pitch, data, [data, size_in_bytes] {
|
|
|
|
MUST(Core::System::munmap(data, size_in_bytes));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|