summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibGfx
diff options
context:
space:
mode:
authorMatthew Olsson <matthewcolsson@gmail.com>2021-04-20 12:40:03 -0700
committerAndreas Kling <kling@serenityos.org>2021-05-02 22:48:06 +0200
commit790908ffc3a77dc140b301ee1765b614a94a65d3 (patch)
tree74bc80e3b1f48e6c7a05b893be4c8e47dfbf4288 /Userland/Libraries/LibGfx
parent4233b69ecf6811181d0dd5d746cfeca8bdd57432 (diff)
downloadserenity-790908ffc3a77dc140b301ee1765b614a94a65d3.zip
LibGfx: Add scaling methods to Bitmap
Diffstat (limited to 'Userland/Libraries/LibGfx')
-rw-r--r--Userland/Libraries/LibGfx/Bitmap.cpp109
-rw-r--r--Userland/Libraries/LibGfx/Bitmap.h2
2 files changed, 111 insertions, 0 deletions
diff --git a/Userland/Libraries/LibGfx/Bitmap.cpp b/Userland/Libraries/LibGfx/Bitmap.cpp
index 201e334a0b..b3be385944 100644
--- a/Userland/Libraries/LibGfx/Bitmap.cpp
+++ b/Userland/Libraries/LibGfx/Bitmap.cpp
@@ -389,6 +389,115 @@ RefPtr<Gfx::Bitmap> Bitmap::flipped(Gfx::Orientation orientation) const
return new_bitmap;
}
+RefPtr<Gfx::Bitmap> Bitmap::scaled(int sx, int sy) const
+{
+ VERIFY(sx >= 0 && sy >= 0);
+ if (sx == 1 && sy == 1)
+ return this;
+
+ auto new_bitmap = Gfx::Bitmap::create(format(), { width() * sx, height() * sy }, scale());
+ if (!new_bitmap)
+ return nullptr;
+
+ auto old_width = physical_width();
+ auto old_height = physical_height();
+
+ 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);
+ }
+ }
+ }
+ }
+
+ return new_bitmap;
+}
+
+// http://fourier.eng.hmc.edu/e161/lectures/resize/node3.html
+RefPtr<Gfx::Bitmap> Bitmap::scaled(float sx, float sy) const
+{
+ VERIFY(sx >= 0.0f && sy >= 0.0f);
+ if (floorf(sx) == sx && floorf(sy) == sy)
+ return scaled(static_cast<int>(sx), static_cast<int>(sy));
+
+ auto new_bitmap = Gfx::Bitmap::create(format(), { width() * sx, height() * sy }, scale());
+ if (!new_bitmap)
+ return nullptr;
+
+ auto old_width = physical_width();
+ auto old_height = physical_height();
+ auto new_width = new_bitmap->physical_width();
+ auto new_height = new_bitmap->physical_height();
+
+ // 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 = floor(p);
+ int j = floor(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.interpolate(b, u);
+ auto f = c.interpolate(d, u);
+ auto color = e.interpolate(f, v);
+ new_bitmap->set_pixel(x, y, color);
+ }
+ }
+
+ // Bottom strip (excluding last pixel)
+ auto old_bottom_y = old_height - 1;
+ auto new_bottom_y = new_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 = floor(p);
+ float u = p - static_cast<float>(i);
+
+ auto a = get_pixel(i, old_bottom_y);
+ auto b = get_pixel(i + 1, old_bottom_y);
+ auto color = a.interpolate(b, u);
+ new_bitmap->set_pixel(x, new_bottom_y, color);
+ }
+
+ // 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);
+
+ int j = floor(q);
+ float v = q - static_cast<float>(j);
+
+ auto c = get_pixel(old_right_x, j);
+ auto d = get_pixel(old_right_x, j + 1);
+
+ auto color = c.interpolate(d, v);
+ new_bitmap->set_pixel(new_right_x, y, color);
+ }
+
+ // Bottom-right pixel
+ new_bitmap->set_pixel(new_width - 1, new_height - 1, get_pixel(physical_width() - 1, physical_height() - 1));
+
+ return new_bitmap;
+}
+
#ifdef __serenity__
RefPtr<Bitmap> Bitmap::to_bitmap_backed_by_anon_fd() const
{
diff --git a/Userland/Libraries/LibGfx/Bitmap.h b/Userland/Libraries/LibGfx/Bitmap.h
index 572a526cc2..be920a64ee 100644
--- a/Userland/Libraries/LibGfx/Bitmap.h
+++ b/Userland/Libraries/LibGfx/Bitmap.h
@@ -115,6 +115,8 @@ public:
RefPtr<Gfx::Bitmap> rotated(Gfx::RotationDirection) const;
RefPtr<Gfx::Bitmap> flipped(Gfx::Orientation) const;
+ RefPtr<Gfx::Bitmap> scaled(int sx, int sy) const;
+ RefPtr<Gfx::Bitmap> scaled(float sx, float sy) const;
RefPtr<Bitmap> to_bitmap_backed_by_anon_fd() const;
ByteBuffer serialize_to_byte_buffer() const;