diff options
author | Matthew Olsson <matthewcolsson@gmail.com> | 2021-04-20 12:40:03 -0700 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-05-02 22:48:06 +0200 |
commit | 790908ffc3a77dc140b301ee1765b614a94a65d3 (patch) | |
tree | 74bc80e3b1f48e6c7a05b893be4c8e47dfbf4288 /Userland/Libraries/LibGfx | |
parent | 4233b69ecf6811181d0dd5d746cfeca8bdd57432 (diff) | |
download | serenity-790908ffc3a77dc140b301ee1765b614a94a65d3.zip |
LibGfx: Add scaling methods to Bitmap
Diffstat (limited to 'Userland/Libraries/LibGfx')
-rw-r--r-- | Userland/Libraries/LibGfx/Bitmap.cpp | 109 | ||||
-rw-r--r-- | Userland/Libraries/LibGfx/Bitmap.h | 2 |
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; |