1/**
2 * Copyright (c) 2019-2021 Alecaddd (https://alecaddd.com)
3 *
4 * This file is part of Akira.
5 *
6 * Akira is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10
11 * Akira is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15
16 * You should have received a copy of the GNU General Public License
17 * along with Akira. If not, see <https://www.gnu.org/licenses/>.
18 *
19 * Authored by: Alessandro "Alecaddd" Castellani <castellani.ale@gmail.com>
20 */
21
22using Akira.Lib.Components;
23
24/**
25 * Generate a simple Image item.
26 */
27public class Akira.Lib.Items.CanvasImage : Goo.CanvasImage, Akira.Lib.Items.CanvasItem {
28    public Gee.ArrayList<Component> components { get; set; }
29
30    public Items.CanvasArtboard? artboard { get; set; }
31
32    // CanvasImage unique attributes.
33    public Lib.Managers.ImageManager manager { get; set; }
34    private Gdk.Pixbuf original_pixbuf;
35
36    public CanvasImage (
37        double _x,
38        double _y,
39        Lib.Managers.ImageManager _manager,
40        Goo.CanvasItem? _parent,
41        Items.CanvasArtboard? _artboard
42    ) {
43        parent = _artboard != null ? _artboard : _parent;
44        artboard = _artboard;
45
46        // Set the ImageManager.
47        manager = _manager;
48
49        // Create the image item.
50        x = y = 0;
51        width = height = 1;
52        scale_to_fit = true;
53        init_position (this, _x, _y);
54
55        // Add the newly created item to the Canvas or Artboard.
56        parent.add_child (this, -1);
57
58        // Initialize the imported image.
59        init_pixbuf ();
60
61        // Force the generation of the item bounds on creation.
62        Goo.CanvasBounds bounds;
63        this.get_bounds (out bounds);
64
65        // Add all the components that this item uses.
66        components = new Gee.ArrayList<Component> ();
67        components.add (new Name (this));
68        components.add (new Coordinates (this));
69        components.add (new Opacity (this));
70        components.add (new Rotation (this));
71        components.add (new Size (this));
72        components.add (new Flipped (this));
73        components.add (new Layer ());
74
75        check_add_to_artboard (this);
76
77        ((Lib.Canvas) canvas).window.event_bus.detect_image_size_change.connect (check_resize_pixbuf);
78    }
79
80    private void init_pixbuf () {
81        // Load the pixbuf at full resolution to properly define the size ratio of the item.
82        manager.get_pixbuf.begin (-1, -1, (obj, res) => {
83            try {
84                original_pixbuf = manager.get_pixbuf.end (res);
85                pixbuf = original_pixbuf;
86
87                // Define the item's size based on the images size.
88                size.width = original_pixbuf.width;
89                size.height = original_pixbuf.height;
90                // Imported images should have their size ratio locked by default.
91                // Change the locked attribute after the size has been defined to let
92                // the Size component properly calculate the correct size ratio.
93                size.locked = true;
94
95                // Reset the size to a 2px initial value after the size ratio was properly defined
96                // in order to allow the user to decide the initial image size. We use 2px in order
97                // to avoid issues when dividing by the ratio in case of narrow images.
98                size.width = 2;
99            } catch (Error e) {
100                warning (e.message);
101                ((Lib.Canvas) canvas).window.event_bus.canvas_notification (e.message);
102            }
103        });
104    }
105
106    /**
107     * Trigger the pixbuf resampling.
108     */
109    public void check_resize_pixbuf () {
110        // Interrupt if this image isn't part of the selection.
111        if (!layer.selected) {
112            return;
113        }
114
115        // Interrupt if the size of the image didn't change.
116        if (size.width == manager.pixbuf.get_width () && size.height == manager.pixbuf.get_height ()) {
117            return;
118        }
119
120        resize_pixbuf ((int) size.width, (int) size.height);
121    }
122
123    /**
124     * Resample the pixbuf size.
125     *
126     * @param {int} w - The new width.
127     * @param {int} h - The new height.
128     * @param {bool} update - If the updated pixbuf size should be applied to the CanvasItem.
129     */
130    public void resize_pixbuf (int w, int h, bool update = false) {
131        manager.get_pixbuf.begin (w, h, (obj, res) => {
132            try {
133                var _pixbuf = manager.get_pixbuf.end (res);
134                pixbuf = _pixbuf;
135                if (update) {
136                    width = _pixbuf.get_width ();
137                    height = _pixbuf.get_height ();
138                }
139            } catch (Error e) {
140                warning (e.message);
141                ((Lib.Canvas) canvas).window.event_bus.canvas_notification (e.message);
142            }
143        });
144    }
145}
146