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