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