mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-30 05:42:37 +00:00 
			
		
		
		
	LibGfx: Add a draw_scaled_bitmap() variant that takes a FloatRect as src_rect
Consider
    draw_scaled_bitmap({0, 0, 10, 10}, source, {0, 0, 5, 5}).
Imagine wanting to split that up into two calls, like e.g. the
compositor when redrawing the background with damage rects. You really
want to be able to say
    draw_scaled_bitmap({0, 0, 5, 10}, source, {0, 0, 2.5, 5})
but up to now you couldn't. Now you can.
This makes painting very low-res images (such as tile.png) in mode
"stretch" work much better.
			
			
This commit is contained in:
		
							parent
							
								
									7278ff6605
								
							
						
					
					
						commit
						2ec6bbd7a1
					
				
					 4 changed files with 31 additions and 20 deletions
				
			
		|  | @ -100,8 +100,8 @@ void Canvas::draw(Gfx::Painter& painter) | ||||||
| 
 | 
 | ||||||
|     auto buggie = Gfx::Bitmap::load_from_file("/res/graphics/buggie.png"); |     auto buggie = Gfx::Bitmap::load_from_file("/res/graphics/buggie.png"); | ||||||
|     painter.blit({ 25, 39 }, *buggie, { 2, 30, 62, 20 }); |     painter.blit({ 25, 39 }, *buggie, { 2, 30, 62, 20 }); | ||||||
|     painter.draw_scaled_bitmap({ 88, 39, 62 * 2, 20 * 2 }, *buggie, { 2, 30, 62, 20 }); |     painter.draw_scaled_bitmap({ 88, 39, 62 * 2, 20 * 2 }, *buggie, Gfx::IntRect { 2, 30, 62, 20 }); | ||||||
|     painter.draw_scaled_bitmap({ 202, 39, 80, 40 }, *buggie, { 2, 30, 62, 20 }); |     painter.draw_scaled_bitmap({ 202, 39, 80, 40 }, *buggie, Gfx::IntRect { 2, 30, 62, 20 }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int main(int argc, char** argv) | int main(int argc, char** argv) | ||||||
|  |  | ||||||
|  | @ -783,30 +783,33 @@ ALWAYS_INLINE static void do_draw_integer_scaled_bitmap(Gfx::Bitmap& target, con | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template<bool has_alpha_channel, typename GetPixel> | template<bool has_alpha_channel, typename GetPixel> | ||||||
| ALWAYS_INLINE static void do_draw_scaled_bitmap(Gfx::Bitmap& target, const IntRect& dst_rect, const IntRect& clipped_rect, const Gfx::Bitmap& source, const IntRect& src_rect, GetPixel get_pixel, float opacity) | ALWAYS_INLINE static void do_draw_scaled_bitmap(Gfx::Bitmap& target, const IntRect& dst_rect, const IntRect& clipped_rect, const Gfx::Bitmap& source, const FloatRect& src_rect, GetPixel get_pixel, float opacity) | ||||||
| { | { | ||||||
|     if (dst_rect == clipped_rect && !(dst_rect.width() % src_rect.width()) && !(dst_rect.height() % src_rect.height())) { |     IntRect int_src_rect = enclosing_int_rect(src_rect); | ||||||
|         int hfactor = dst_rect.width() / src_rect.width(); |     if (dst_rect == clipped_rect && int_src_rect == src_rect && !(dst_rect.width() % int_src_rect.width()) && !(dst_rect.height() % int_src_rect.height())) { | ||||||
|         int vfactor = dst_rect.height() / src_rect.height(); |         int hfactor = dst_rect.width() / int_src_rect.width(); | ||||||
|  |         int vfactor = dst_rect.height() / int_src_rect.height(); | ||||||
|         if (hfactor == 2 && vfactor == 2) |         if (hfactor == 2 && vfactor == 2) | ||||||
|             return do_draw_integer_scaled_bitmap<has_alpha_channel>(target, dst_rect, src_rect, source, 2, 2, get_pixel, opacity); |             return do_draw_integer_scaled_bitmap<has_alpha_channel>(target, dst_rect, int_src_rect, source, 2, 2, get_pixel, opacity); | ||||||
|         if (hfactor == 3 && vfactor == 3) |         if (hfactor == 3 && vfactor == 3) | ||||||
|             return do_draw_integer_scaled_bitmap<has_alpha_channel>(target, dst_rect, src_rect, source, 3, 3, get_pixel, opacity); |             return do_draw_integer_scaled_bitmap<has_alpha_channel>(target, dst_rect, int_src_rect, source, 3, 3, get_pixel, opacity); | ||||||
|         if (hfactor == 4 && vfactor == 4) |         if (hfactor == 4 && vfactor == 4) | ||||||
|             return do_draw_integer_scaled_bitmap<has_alpha_channel>(target, dst_rect, src_rect, source, 4, 4, get_pixel, opacity); |             return do_draw_integer_scaled_bitmap<has_alpha_channel>(target, dst_rect, int_src_rect, source, 4, 4, get_pixel, opacity); | ||||||
|         return do_draw_integer_scaled_bitmap<has_alpha_channel>(target, dst_rect, src_rect, source, hfactor, vfactor, get_pixel, opacity); |         return do_draw_integer_scaled_bitmap<has_alpha_channel>(target, dst_rect, int_src_rect, source, hfactor, vfactor, get_pixel, opacity); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool has_opacity = opacity != 1.0f; |     bool has_opacity = opacity != 1.0f; | ||||||
|     int hscale = (src_rect.width() << 16) / dst_rect.width(); |     int hscale = (src_rect.width() * (1 << 16)) / dst_rect.width(); | ||||||
|     int vscale = (src_rect.height() << 16) / dst_rect.height(); |     int vscale = (src_rect.height() * (1 << 16)) / dst_rect.height(); | ||||||
|  |     int src_left = src_rect.left() * (1 << 16); | ||||||
|  |     int src_top = src_rect.top() * (1 << 16); | ||||||
| 
 | 
 | ||||||
|     for (int y = clipped_rect.top(); y <= clipped_rect.bottom(); ++y) { |     for (int y = clipped_rect.top(); y <= clipped_rect.bottom(); ++y) { | ||||||
|         auto* scanline = (Color*)target.scanline(y); |         auto* scanline = (Color*)target.scanline(y); | ||||||
|         for (int x = clipped_rect.left(); x <= clipped_rect.right(); ++x) { |         for (int x = clipped_rect.left(); x <= clipped_rect.right(); ++x) { | ||||||
|             auto scaled_x = ((x - dst_rect.x()) * hscale) >> 16; |             auto scaled_x = ((x - dst_rect.x()) * hscale + src_left) >> 16; | ||||||
|             auto scaled_y = ((y - dst_rect.y()) * vscale) >> 16; |             auto scaled_y = ((y - dst_rect.y()) * vscale + src_top) >> 16; | ||||||
|             auto src_pixel = get_pixel(source, scaled_x + src_rect.left(), scaled_y + src_rect.top()); |             auto src_pixel = get_pixel(source, scaled_x, scaled_y); | ||||||
|             if (has_opacity) |             if (has_opacity) | ||||||
|                 src_pixel.set_alpha(src_pixel.alpha() * opacity); |                 src_pixel.set_alpha(src_pixel.alpha() * opacity); | ||||||
|             if constexpr (has_alpha_channel) { |             if constexpr (has_alpha_channel) { | ||||||
|  | @ -819,8 +822,14 @@ ALWAYS_INLINE static void do_draw_scaled_bitmap(Gfx::Bitmap& target, const IntRe | ||||||
| 
 | 
 | ||||||
| void Painter::draw_scaled_bitmap(const IntRect& a_dst_rect, const Gfx::Bitmap& source, const IntRect& a_src_rect, float opacity) | void Painter::draw_scaled_bitmap(const IntRect& a_dst_rect, const Gfx::Bitmap& source, const IntRect& a_src_rect, float opacity) | ||||||
| { | { | ||||||
|     if (scale() == source.scale() && a_dst_rect.size() == a_src_rect.size()) |     draw_scaled_bitmap(a_dst_rect, source, FloatRect { a_src_rect }, opacity); | ||||||
|         return blit(a_dst_rect.location(), source, a_src_rect, opacity); | } | ||||||
|  | 
 | ||||||
|  | void Painter::draw_scaled_bitmap(const IntRect& a_dst_rect, const Gfx::Bitmap& source, const FloatRect& a_src_rect, float opacity) | ||||||
|  | { | ||||||
|  |     IntRect int_src_rect = enclosing_int_rect(a_src_rect); | ||||||
|  |     if (scale() == source.scale() && a_src_rect == int_src_rect && a_dst_rect.size() == int_src_rect.size()) | ||||||
|  |         return blit(a_dst_rect.location(), source, int_src_rect, opacity); | ||||||
| 
 | 
 | ||||||
|     auto dst_rect = to_physical(a_dst_rect); |     auto dst_rect = to_physical(a_dst_rect); | ||||||
|     auto src_rect = a_src_rect * source.scale(); |     auto src_rect = a_src_rect * source.scale(); | ||||||
|  |  | ||||||
|  | @ -62,6 +62,7 @@ public: | ||||||
|     void draw_bitmap(const IntPoint&, const CharacterBitmap&, Color = Color()); |     void draw_bitmap(const IntPoint&, const CharacterBitmap&, Color = Color()); | ||||||
|     void draw_bitmap(const IntPoint&, const GlyphBitmap&, Color = Color()); |     void draw_bitmap(const IntPoint&, const GlyphBitmap&, Color = Color()); | ||||||
|     void draw_scaled_bitmap(const IntRect& dst_rect, const Gfx::Bitmap&, const IntRect& src_rect, float opacity = 1.0f); |     void draw_scaled_bitmap(const IntRect& dst_rect, const Gfx::Bitmap&, const IntRect& src_rect, float opacity = 1.0f); | ||||||
|  |     void draw_scaled_bitmap(const IntRect& dst_rect, const Gfx::Bitmap&, const FloatRect& src_rect, float opacity = 1.0f); | ||||||
|     void draw_triangle(const IntPoint&, const IntPoint&, const IntPoint&, Color); |     void draw_triangle(const IntPoint&, const IntPoint&, const IntPoint&, Color); | ||||||
|     void draw_ellipse_intersecting(const IntRect&, Color, int thickness = 1); |     void draw_ellipse_intersecting(const IntRect&, Color, int thickness = 1); | ||||||
|     void set_pixel(const IntPoint&, Color); |     void set_pixel(const IntPoint&, Color); | ||||||
|  |  | ||||||
|  | @ -262,11 +262,12 @@ void Compositor::compose() | ||||||
|             } else if (m_wallpaper_mode == WallpaperMode::Tile) { |             } else if (m_wallpaper_mode == WallpaperMode::Tile) { | ||||||
|                 painter.draw_tiled_bitmap(rect, *m_wallpaper); |                 painter.draw_tiled_bitmap(rect, *m_wallpaper); | ||||||
|             } else if (m_wallpaper_mode == WallpaperMode::Stretch) { |             } else if (m_wallpaper_mode == WallpaperMode::Stretch) { | ||||||
|                 float hscale = (float)m_wallpaper->size().width() / (float)ws.size().width(); |                 float hscale = (float)m_wallpaper->width() / (float)ws.width(); | ||||||
|                 float vscale = (float)m_wallpaper->size().height() / (float)ws.size().height(); |                 float vscale = (float)m_wallpaper->height() / (float)ws.height(); | ||||||
| 
 | 
 | ||||||
|                 // TODO: this may look ugly, we should scale to a backing bitmap and then blit
 |                 // TODO: this may look ugly, we should scale to a backing bitmap and then blit
 | ||||||
|                 painter.draw_scaled_bitmap(rect, *m_wallpaper, { rect.x() * hscale, rect.y() * vscale, rect.width() * hscale, rect.height() * vscale }); |                 auto src_rect = Gfx::FloatRect { rect.x() * hscale, rect.y() * vscale, rect.width() * hscale, rect.height() * vscale }; | ||||||
|  |                 painter.draw_scaled_bitmap(rect, *m_wallpaper, src_rect); | ||||||
|             } else { |             } else { | ||||||
|                 ASSERT_NOT_REACHED(); |                 ASSERT_NOT_REACHED(); | ||||||
|             } |             } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Nico Weber
						Nico Weber