mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 16:32:45 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			132 lines
		
	
	
	
		
			4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			132 lines
		
	
	
	
		
			4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org>
 | |
|  *
 | |
|  * SPDX-License-Identifier: BSD-2-Clause
 | |
|  */
 | |
| 
 | |
| #include "Layer.h"
 | |
| #include "Image.h"
 | |
| #include "Selection.h"
 | |
| #include <AK/RefPtr.h>
 | |
| #include <AK/Try.h>
 | |
| #include <LibGfx/Bitmap.h>
 | |
| 
 | |
| namespace PixelPaint {
 | |
| 
 | |
| ErrorOr<NonnullRefPtr<Layer>> Layer::try_create_with_size(Image& image, Gfx::IntSize const& size, String name)
 | |
| {
 | |
|     VERIFY(!size.is_empty());
 | |
| 
 | |
|     if (size.width() > 16384 || size.height() > 16384)
 | |
|         return Error::from_string_literal("Layer size too large"sv);
 | |
| 
 | |
|     auto bitmap = TRY(Gfx::Bitmap::try_create(Gfx::BitmapFormat::BGRA8888, size));
 | |
|     return adopt_nonnull_ref_or_enomem(new (nothrow) Layer(image, move(bitmap), move(name)));
 | |
| }
 | |
| 
 | |
| ErrorOr<NonnullRefPtr<Layer>> Layer::try_create_with_bitmap(Image& image, NonnullRefPtr<Gfx::Bitmap> bitmap, String name)
 | |
| {
 | |
|     VERIFY(!bitmap->size().is_empty());
 | |
| 
 | |
|     if (bitmap->size().width() > 16384 || bitmap->size().height() > 16384)
 | |
|         return Error::from_string_literal("Layer size too large"sv);
 | |
| 
 | |
|     return adopt_nonnull_ref_or_enomem(new (nothrow) Layer(image, bitmap, move(name)));
 | |
| }
 | |
| 
 | |
| ErrorOr<NonnullRefPtr<Layer>> Layer::try_create_snapshot(Image& image, Layer const& layer)
 | |
| {
 | |
|     auto bitmap = TRY(layer.bitmap().clone());
 | |
|     auto snapshot = TRY(try_create_with_bitmap(image, move(bitmap), layer.name()));
 | |
| 
 | |
|     /*
 | |
|         We set these properties directly because calling the setters might
 | |
|         notify the image of an update on the newly created layer, but this
 | |
|         layer has not yet been added to the image.
 | |
|     */
 | |
|     snapshot->m_opacity_percent = layer.opacity_percent();
 | |
|     snapshot->m_visible = layer.is_visible();
 | |
| 
 | |
|     snapshot->set_selected(layer.is_selected());
 | |
|     snapshot->set_location(layer.location());
 | |
| 
 | |
|     return snapshot;
 | |
| }
 | |
| 
 | |
| Layer::Layer(Image& image, NonnullRefPtr<Gfx::Bitmap> bitmap, String name)
 | |
|     : m_image(image)
 | |
|     , m_name(move(name))
 | |
|     , m_bitmap(move(bitmap))
 | |
| {
 | |
| }
 | |
| 
 | |
| void Layer::did_modify_bitmap(Gfx::IntRect const& rect)
 | |
| {
 | |
|     m_image.layer_did_modify_bitmap({}, *this, rect);
 | |
| }
 | |
| 
 | |
| void Layer::set_visible(bool visible)
 | |
| {
 | |
|     if (m_visible == visible)
 | |
|         return;
 | |
|     m_visible = visible;
 | |
|     m_image.layer_did_modify_properties({}, *this);
 | |
| }
 | |
| 
 | |
| void Layer::set_opacity_percent(int opacity_percent)
 | |
| {
 | |
|     if (m_opacity_percent == opacity_percent)
 | |
|         return;
 | |
|     m_opacity_percent = opacity_percent;
 | |
|     m_image.layer_did_modify_properties({}, *this);
 | |
| }
 | |
| 
 | |
| void Layer::set_name(String name)
 | |
| {
 | |
|     if (m_name == name)
 | |
|         return;
 | |
|     m_name = move(name);
 | |
|     m_image.layer_did_modify_properties({}, *this);
 | |
| }
 | |
| 
 | |
| RefPtr<Gfx::Bitmap> Layer::try_copy_bitmap(Selection const& selection) const
 | |
| {
 | |
|     if (selection.is_empty()) {
 | |
|         return {};
 | |
|     }
 | |
|     auto selection_rect = selection.bounding_rect();
 | |
| 
 | |
|     auto bitmap_or_error = Gfx::Bitmap::try_create(Gfx::BitmapFormat::BGRA8888, selection_rect.size());
 | |
|     if (bitmap_or_error.is_error())
 | |
|         return nullptr;
 | |
|     auto result = bitmap_or_error.release_value_but_fixme_should_propagate_errors();
 | |
|     VERIFY(result->has_alpha_channel());
 | |
| 
 | |
|     for (int y = selection_rect.top(); y <= selection_rect.bottom(); y++) {
 | |
|         for (int x = selection_rect.left(); x <= selection_rect.right(); x++) {
 | |
| 
 | |
|             Gfx::IntPoint image_point { x, y };
 | |
|             auto layer_point = image_point - m_location;
 | |
|             auto result_point = image_point - selection_rect.top_left();
 | |
| 
 | |
|             if (!m_bitmap->physical_rect().contains(layer_point)) {
 | |
|                 result->set_pixel(result_point, Gfx::Color::Transparent);
 | |
|                 continue;
 | |
|             }
 | |
| 
 | |
|             auto pixel = m_bitmap->get_pixel(layer_point);
 | |
| 
 | |
|             // Widen to int before multiplying to avoid overflow issues
 | |
|             auto pixel_alpha = static_cast<int>(pixel.alpha());
 | |
|             auto selection_alpha = static_cast<int>(selection.get_selection_alpha(image_point));
 | |
|             auto new_alpha = (pixel_alpha * selection_alpha) / 0xFF;
 | |
|             pixel.set_alpha(static_cast<u8>(clamp(new_alpha, 0, 0xFF)));
 | |
| 
 | |
|             result->set_pixel(result_point, pixel);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return result;
 | |
| }
 | |
| 
 | |
| }
 | 
