1 /*
2 Copyright (c) 2017-2021 Timur Gafarov
3 
4 Boost Software License - Version 1.0 - August 17th, 2003
5 
6 Permission is hereby granted, free of charge, to any person or organization
7 obtaining a copy of the software and accompanying documentation covered by
8 this license (the "Software") to use, reproduce, display, distribute,
9 execute, and transmit the Software, and to prepare derivative works of the
10 Software, and to permit third-parties to whom the Software is furnished to
11 do so, all subject to the following:
12 
13 The copyright notices in the Software and this entire statement, including
14 the above license grant, this restriction and the following disclaimer,
15 must be included in all copies of the Software, in whole or in part, and
16 all derivative works of the Software, unless such copies or derivative
17 works are solely in the form of machine-executable object code generated by
18 a source language processor.
19 
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 DEALINGS IN THE SOFTWARE.
27 */
28 
29 /**
30  * Animated images
31  *
32  * Copyright: Timur Gafarov 2017-2021.
33  * License: $(LINK2 boost.org/LICENSE_1_0.txt, Boost License 1.0).
34  * Authors: Timur Gafarov
35  */
36 module dlib.image.animation;
37 
38 import dlib.core.memory;
39 import dlib.image.image;
40 
41 /**
42  * Animated image interface 
43  */
44 interface SuperAnimatedImage: SuperImage
45 {
46     @property size_t frameSize();
47     @property uint numFrames();
48     @property uint currentFrame();
49     @property void currentFrame(uint f);
50     uint advanceFrame();
51 }
52 
53 /**
54  * Extension of standard Image that handles animation
55  *
56  * Description:
57  * AnimatedImage can store more than one frame of pixel data.
58  * Current frame can be switched with currentFrame property.
59  * All usual image operations work on current frame, so
60  * you can use this class with any existing dlib.image
61  * functionality: save to file, apply filters, etc.
62  */
63 class AnimatedImage(IntegerPixelFormat fmt): Image!(fmt), SuperAnimatedImage
64 {
65     protected:
66     uint _numFrames;
67     uint _currentFrame = 0;
68     size_t _frameSize;
69 
70     public:
71     this(uint w, uint h, uint frames)
72     {
73         _numFrames = frames;
74         super(w, h);
75         _frameSize = _width * _height * _pixelSize;
76     }
77 
78     override @property SuperImage dup()
79     {
80         auto res = new AnimatedImage!(fmt)(_width, _height, _numFrames);
81         res.data[] = data[];
82         return res;
83     }
84 
85     override SuperImage createSameFormat(uint w, uint h)
86     {
87         return new AnimatedImage!(fmt)(w, h, _numFrames);
88     }
89 
90     @property size_t frameSize()
91     {
92         return _frameSize;
93     }
94 
95     @property uint numFrames()
96     {
97         return _numFrames;
98     }
99 
100     @property uint currentFrame()
101     {
102         return _currentFrame;
103     }
104 
105     @property void currentFrame(uint f)
106     {
107         if (f >= _numFrames)
108             _currentFrame = _numFrames - 1;
109         else
110             _currentFrame = f;
111     }
112 
113     uint advanceFrame()
114     {
115         currentFrame = currentFrame + 1;
116         return _currentFrame;
117     }
118 
119     override @property ubyte[] data()
120     {
121         if (_currentFrame >= _numFrames)
122             _currentFrame = _numFrames - 1;
123         size_t offset = _frameSize * _currentFrame;
124         return _data[offset..offset+_frameSize];
125     }
126 
127     protected override void allocateData()
128     {
129         _data = new ubyte[_width * _height * _pixelSize * _numFrames];
130     }
131 }
132 
133 /// Specialization of AnimatedImage for 8-bit luminance pixel format
134 alias AnimatedImageL8 = AnimatedImage!(IntegerPixelFormat.L8);
135 /// Specialization of AnimatedImage for 8-bit luminance-alpha pixel format
136 alias AnimatedImageLA8 = AnimatedImage!(IntegerPixelFormat.LA8);
137 /// Specialization of AnimatedImage for 8-bit RGB pixel format
138 alias AnimatedImageRGB8 = AnimatedImage!(IntegerPixelFormat.RGB8);
139 /// Specialization of AnimatedImage for 8-bit RGBA pixel format
140 alias AnimatedImageRGBA8 = AnimatedImage!(IntegerPixelFormat.RGBA8);
141 
142 /// Specialization of AnimatedImage for 16-bit luminance pixel format
143 alias AnimatedImageL16 = AnimatedImage!(IntegerPixelFormat.L16);
144 /// Specialization of AnimatedImage for 16-bit luminance-alpha pixel format
145 alias AnimatedImageLA16 = AnimatedImage!(IntegerPixelFormat.LA16);
146 /// Specialization of AnimatedImage for 16-bit RGB pixel format
147 alias AnimatedImageRGB16 = AnimatedImage!(IntegerPixelFormat.RGB16);
148 /// Specialization of AnimatedImage for 16-bit RGBA pixel format
149 alias AnimatedImageRGBA16 = AnimatedImage!(IntegerPixelFormat.RGBA16);
150 
151 /**
152  * Factory class for animated images
153  */
154 class AnimatedImageFactory: SuperImageFactory
155 {
156     SuperImage createImage(uint w, uint h, uint channels, uint bitDepth, uint numFrames = 1)
157     {
158         return animatedImage(w, h, channels, bitDepth, numFrames);
159     }
160 }
161 
162 private AnimatedImageFactory _defaultAnimatedImageFactory;
163 
164 /**
165  * Get default AnimatedImageFactory singleton
166  */
167 AnimatedImageFactory animatedImageFactory()
168 {
169     if (!_defaultAnimatedImageFactory)
170         _defaultAnimatedImageFactory = new AnimatedImageFactory();
171     return _defaultAnimatedImageFactory;
172 }
173 
174 /**
175  * Factory function for animated images
176  */
177 SuperImage animatedImage(uint w, uint h, uint channels, uint bitDepth, uint numFrames = 1)
178 in
179 {
180     assert(channels > 0 && channels <= 4);
181     assert(bitDepth == 8 || bitDepth == 16);
182 }
183 do
184 {
185     switch(channels)
186     {
187         case 1:
188         {
189             if (bitDepth == 8)
190                 return new AnimatedImageL8(w, h, numFrames);
191             else
192                 return new AnimatedImageL16(w, h, numFrames);
193         }
194         case 2:
195         {
196             if (bitDepth == 8)
197                 return new AnimatedImageLA8(w, h, numFrames);
198             else
199                 return new AnimatedImageLA16(w, h, numFrames);
200         }
201         case 3:
202         {
203             if (bitDepth == 8)
204                 return new AnimatedImageRGB8(w, h, numFrames);
205             else
206                 return new AnimatedImageRGB16(w, h, numFrames);
207         }
208         case 4:
209         {
210             if (bitDepth == 8)
211                 return new AnimatedImageRGBA8(w, h, numFrames);
212             else
213                 return new AnimatedImageRGBA16(w, h, numFrames);
214         }
215         default:
216             assert(0);
217     }
218 }
219 
220 /**
221  * AnimatedImage that uses dlib.core.memory instead of GC
222  */
223 class UnmanagedAnimatedImage(IntegerPixelFormat fmt): AnimatedImage!(fmt)
224 {
225     override @property SuperImage dup()
226     {
227         auto res = New!(UnmanagedAnimatedImage!(fmt))(_width, _height, _numFrames);
228         res.data[] = data[];
229         return res;
230     }
231 
232     override SuperImage createSameFormat(uint w, uint h)
233     {
234         return New!(UnmanagedAnimatedImage!(fmt))(w, h, _numFrames);
235     }
236 
237     this(uint w, uint h, uint frames)
238     {
239         super(w, h, frames);
240     }
241 
242     ~this()
243     {
244         Delete(_data);
245     }
246 
247     protected override void allocateData()
248     {
249         _data = New!(ubyte[])(_width * _height * _pixelSize * _numFrames);
250     }
251 
252     override void free()
253     {
254         Delete(this);
255     }
256 }
257 
258 /// Specialization of UnmanagedAnimatedImage for 8-bit luminance pixel format
259 alias UnmanagedAnimatedImageL8 = UnmanagedAnimatedImage!(IntegerPixelFormat.L8);
260 /// Specialization of UnmanagedAnimatedImage for 8-bit luminance-alpha pixel format
261 alias UnmanagedAnimatedImageLA8 = UnmanagedAnimatedImage!(IntegerPixelFormat.LA8);
262 /// Specialization of UnmanagedAnimatedImage for 8-bit RGB pixel format
263 alias UnmanagedAnimatedImageRGB8 = UnmanagedAnimatedImage!(IntegerPixelFormat.RGB8);
264 /// Specialization of UnmanagedAnimatedImage for 8-bit RGBA pixel format
265 alias UnmanagedAnimatedImageRGBA8 = UnmanagedAnimatedImage!(IntegerPixelFormat.RGBA8);
266 
267 /// Specialization of UnmanagedAnimatedImage for 16-bit luminance pixel format
268 alias UnmanagedAnimatedImageL16 = UnmanagedAnimatedImage!(IntegerPixelFormat.L16);
269 /// Specialization of UnmanagedAnimatedImage for 16-bit luminance-alpha pixel format
270 alias UnmanagedAnimatedImageLA16 = UnmanagedAnimatedImage!(IntegerPixelFormat.LA16);
271 /// Specialization of UnmanagedAnimatedImage for 16-bit RGB pixel format
272 alias UnmanagedAnimatedImageRGB16 = UnmanagedAnimatedImage!(IntegerPixelFormat.RGB16);
273 /// Specialization of UnmanagedAnimatedImage for 16-bit RGBA pixel format
274 alias UnmanagedAnimatedImageRGBA16 = UnmanagedAnimatedImage!(IntegerPixelFormat.RGBA16);
275 
276 /**
277  * Factory class for UnmanagedAnimatedImage
278  */
279 class UnmanagedAnimatedImageFactory: SuperImageFactory
280 {
281     SuperImage createImage(uint w, uint h, uint channels, uint bitDepth, uint numFrames = 1)
282     {
283         return unmanagedAnimatedImage(w, h, channels, bitDepth, numFrames);
284     }
285 }
286 
287 /**
288  * Factory function for UnmanagedAnimatedImage
289  */
290 SuperImage unmanagedAnimatedImage(uint w, uint h, uint channels, uint bitDepth, uint numFrames = 1)
291 in
292 {
293     assert(channels > 0 && channels <= 4);
294     assert(bitDepth == 8 || bitDepth == 16);
295 }
296 do
297 {
298     switch(channels)
299     {
300         case 1:
301         {
302             if (bitDepth == 8)
303                 return New!UnmanagedAnimatedImageL8(w, h, numFrames);
304             else
305                 return New!UnmanagedAnimatedImageL16(w, h, numFrames);
306         }
307         case 2:
308         {
309             if (bitDepth == 8)
310                 return New!UnmanagedAnimatedImageLA8(w, h, numFrames);
311             else
312                 return New!UnmanagedAnimatedImageLA16(w, h, numFrames);
313         }
314         case 3:
315         {
316             if (bitDepth == 8)
317                 return New!UnmanagedAnimatedImageRGB8(w, h, numFrames);
318             else
319                 return New!UnmanagedAnimatedImageRGB16(w, h, numFrames);
320         }
321         case 4:
322         {
323             if (bitDepth == 8)
324                 return New!UnmanagedAnimatedImageRGBA8(w, h, numFrames);
325             else
326                 return New!UnmanagedAnimatedImageRGBA16(w, h, numFrames);
327         }
328         default:
329             assert(0);
330     }
331 }