1 /**
2 * Copyright © Novelate 2020
3 * License: MIT (https://github.com/Novelate/NovelateEngine/blob/master/LICENSE)
4 * Author: Jacob Jensen (bausshf)
5 * Website: https://novelate.com/
6 * ------
7 * Novelate is a free and open-source visual novel engine and framework written in the D programming language.
8 * It can be used freely for both personal and commercial projects.
9 * ------
10 * Module Description:
11 * An image component allows for images to be rendered with a set of extra features and flexibility such as fading etc.
12 */
13 module novelate.ui.imagecomponent;
14 
15 import std.conv : to;
16 
17 import novelate.ui.component;
18 import novelate.buildstate;
19 
20 /// Wrapepr around an image component.
21 final class ImageComponent : Component
22 {
23   private:
24   /// The image.
25   ExternalImage _image;
26   /// Boolean determining whether the image is full-screen or not.
27   bool _fullScreen;
28   /// The alpha channel.
29   ubyte _alpha;
30   /// The current fade-in amount.
31   ubyte _fadingIn;
32   /// The current fade-out amount.
33   ubyte _fadingOut;
34 
35   public:
36   final:
37   /**
38   * Creates a new image component.
39   * Params:
40   *   path = The path of the image.
41   */
42   this(string path)
43   {
44     _image = new ExternalImage();
45     _image.loadFromFile(path);
46     _image.position = FloatVector(0, 0);
47 
48     _alpha = 255;
49 
50     auto bounds = _image.bounds;
51 
52     super(bounds.x, bounds.y);
53   }
54 
55   @property
56   {
57     /// Gets a boolean determining whether the image is full-screen or not.
58     bool fullScreen() { return _fullScreen; }
59 
60     /// Sets a boolean determining whether the image is full-screen or not.
61     void fullScreen(bool isFullScreen)
62     {
63       _fullScreen = isFullScreen;
64     }
65     /// Gets the alpha channel.
66     ubyte alpha() { return _alpha; }
67     /// Sets the alpha channel.
68     void alpha(ubyte newAlpha)
69     {
70       _alpha = newAlpha;
71 
72       _image.color = Paint(255, 255, 255, _alpha);
73     }
74   }
75 
76   /// A handler for when the image has faded out.
77   void delegate() fadedOut;
78   /// A handler for when the image has faded in.
79   void delegate() fadedIn;
80   /// A handler for when the image is fading out.
81   void delegate(ubyte) fadingOut;
82   /// A handler for when the image is fading in.
83   void delegate(ubyte) fadingIn;
84 
85   /**
86   * Fades out the image.
87   * Params:
88   *   speed = The speed at which the image will fade.
89   */
90   void fadeOut(ubyte speed)
91   {
92     if (_fadingOut)
93     {
94       return;
95     }
96 
97     _fadingIn = 0;
98     _fadingOut = speed;
99     alpha = 255;
100   }
101 
102   /**
103   * Fades in the image.
104   * Params:
105   *   speed = The speed at which the image will fade.
106   */
107   void fadeIn(ubyte speed)
108   {
109     if (_fadingIn)
110     {
111       return;
112     }
113 
114     _fadingOut = 0;
115     _fadingIn = speed;
116     alpha = 0;
117   }
118 
119   /// See: Component.render()
120   override void render(ExternalWindow window)
121   {
122     if (_image)
123     {
124       ptrdiff_t oldAlpha = cast(ptrdiff_t)_alpha;
125 
126       if (_fadingIn && oldAlpha < 255)
127       {
128         oldAlpha += _fadingIn;
129 
130         if (fadingIn)
131         {
132           fadingIn(cast(ubyte)oldAlpha);
133         }
134 
135         if (oldAlpha >= 255)
136         {
137           oldAlpha = 255;
138 
139           if (fadedIn)
140           {
141             fadedIn();
142           }
143         }
144 
145         alpha = cast(ubyte)oldAlpha;
146       }
147       else if (_fadingOut && _alpha > 0)
148       {
149         oldAlpha -= _fadingOut;
150 
151         if (fadingOut)
152         {
153           fadingOut(cast(ubyte)oldAlpha);
154         }
155 
156         if (oldAlpha <= 0)
157         {
158           oldAlpha = 0;
159 
160           if (fadedOut)
161           {
162             fadedOut();
163           }
164         }
165 
166         alpha = cast(ubyte)oldAlpha;
167       }
168 
169       _image.draw(window);
170     }
171   }
172 
173   /// See: Component.refresh()
174   override void refresh(size_t width, size_t height)
175   {
176     if (!_fullScreen)
177     {
178       return;
179     }
180 
181     auto bounds = _image.bounds;
182 
183     _image.scale =
184     FloatVector
185     (
186       cast(int)width / bounds.x,
187       cast(int)height / bounds.y
188     );
189   }
190 
191   /// See: Component.updateSize()
192   override void updateSize()
193   {
194     if (_fullScreen)
195     {
196       return;
197     }
198 
199     auto bounds = _image.bounds;
200 
201     _image.scale =
202     FloatVector
203     (
204       cast(int)super.width / bounds.x,
205       cast(int)super.height / bounds.y
206     );
207   }
208 
209   /// See: Component.fitToSize()
210   void fitToSize(double maxWidth, double maxHeight, bool enlarge = false)
211   {
212     import std.math : fmin, round;
213 
214     auto src = super.size;
215 
216     maxWidth = enlarge ? cast(double)maxWidth : fmin(maxWidth, cast(double)src.x);
217     maxHeight = enlarge ? cast(double)maxHeight : fmin(maxHeight, cast(double)src.y);
218 
219     double rnd = fmin(maxWidth / cast(double)src.x, maxHeight / cast(double)src.y);
220 
221     auto newWidth = round(src.x * rnd);
222     auto newHeight = round(src.y * rnd);
223 
224     super.size = FloatVector(newWidth, newHeight);
225   }
226 
227   /// See: Component.updatePosition()
228   override void updatePosition()
229   {
230     _image.position = super.position;
231   }
232 
233   static if (isManualMemory)
234   {
235     /// See: Component.clean()
236     override void clean()
237     {
238       if (_image)
239       {
240         _image.clean();
241       }
242     }
243   }
244 }