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 }