1 //
2 // C++ Implementation: kenburnseffect
3 //
4 // Description:
5 //
6 //
7 // Author: Yorn <yorn@gmx.net>, (C) 2009
8 //
9 // Copyright: See COPYING file that comes with this distribution
10 //
11 //
12 #include "kenburnseffect.h"
13
14 #include <iostream>
15 #include <cmath>
16 #include <cstring>
17 #include <cstdlib>
18
19 #include "pictureResize.h"
20 #include "pictureBlend.h"
21 #include "log.h"
22 #include "effectorVisitor.h"
23
KenBurnsEffect()24 KenBurnsEffect::KenBurnsEffect() :
25 Effector(), state(unconfigured)
26 {
27 }
28
~KenBurnsEffect()29 KenBurnsEffect::~KenBurnsEffect()
30 {
31 }
32
configure(KenBurnsEffect::KenBurnsConfig & _config)33 void KenBurnsEffect::configure(KenBurnsEffect::KenBurnsConfig& _config)
34 {
35
36 config = _config;
37
38 stepX = ((config.endpointX - config.startpointX) * 1.0)
39 / (config.sequenceLength * 1.0);
40 stepY = ((config.endpointY - config.startpointY) * 1.0)
41 / (config.sequenceLength * 1.0);
42 stepZoom = ((config.zoomEnd - config.zoomStart) * 1.0)
43 / (config.sequenceLength * 1.0);
44
45 actX = config.startpointX;
46 actY = config.startpointY;
47 actZoom = config.zoomStart;
48
49 blackPlane = RGBPlane(config.outputWidth, config.outputHeight);
50 presentationPlane = config.origPlane;
51
52 /* blank the plane */
53 uint32 planesize = config.outputWidth * config.outputHeight * 4; // 3 Colors + Alpha channel
54 memset(blackPlane->plane, 0x00, planesize);
55
56 frameCounter = 0;
57
58 if (config.first)
59 state = presentation;
60 else
61 state = blindIn;
62
63 }
64
doBlindIn(RGBPlane & plane)65 void KenBurnsEffect::doBlindIn(RGBPlane& plane)
66 {
67
68 logger.debug() << " --- Position " << actX <<" x "<< actY <<" "<<( 1.0/actZoom);
69
70 // get the picture to be fade in
71 plane = PictureResize::subframe(presentationPlane, config.outputWidth,
72 config.outputHeight, actX, actY, 1.0 / actZoom);
73
74 // calculate the next fader picture
75 plane = PictureBlend::crossfade(blackPlane, plane, (frameCounter * 1.0)
76 / (config.blindLength * 1.0));
77
78 // let the fading go on
79 actX += stepX;
80 actY += stepY;
81 actZoom += stepZoom;
82
83 frameCounter++;
84 if (frameCounter > config.blindLength) {
85 state = presentation;
86 }
87 }
88
doPresentation(RGBPlane & plane)89 void KenBurnsEffect::doPresentation(RGBPlane& plane)
90 {
91 logger.debug() << " --- Position " << actX <<" x "<< actY <<" "<<( 1.0/actZoom);
92
93 // get the picture to be fade in
94 plane = PictureResize::subframe(presentationPlane, config.outputWidth,
95 config.outputHeight, actX, actY, 1.0 / actZoom);
96
97 // let the fading go on
98 actX += stepX;
99 actY += stepY;
100 actZoom += stepZoom;
101
102 frameCounter++;
103 if (frameCounter > (config.sequenceLength - config.blindLength)) {
104 if (!config.last) {
105 state = blindOut;
106 } else {
107 if (frameCounter >= config.sequenceLength) {
108 state = unavailable;
109 }
110 }
111 }
112
113 }
114
doBlindOut(RGBPlane & plane)115 void KenBurnsEffect::doBlindOut(RGBPlane & plane)
116 {
117 #ifdef DEBUG
118 logger.debug() << " --- Position " << actX <<" x "<< actY <<" "<<( 1.0/actZoom);
119 #endif
120 // get the picture to be fade in
121 plane = PictureResize::subframe(presentationPlane, config.outputWidth,
122 config.outputHeight, actX, actY, 1.0 / actZoom);
123
124 // calculate the next fader picture
125 plane = PictureBlend::crossfade(blackPlane, plane, ((config.sequenceLength
126 - frameCounter) * 1.0) / (config.blindLength * 1.0));
127
128 // let the fading go on
129 actX += stepX;
130 actY += stepY;
131 actZoom += stepZoom;
132
133 frameCounter++;
134 if (frameCounter >= config.sequenceLength) {
135 state = unavailable;
136 }
137
138 }
139
available()140 bool KenBurnsEffect::available()
141 {
142 return ((state != unavailable) && (state != unconfigured));
143 }
144
accept(EffectorVisitor & visitor) const145 void KenBurnsEffect::accept(EffectorVisitor& visitor) const
146 {
147 visitor.visit(*this);
148 }
149
operator >>(RGBPlane & plane)150 Effector & KenBurnsEffect::operator >>(RGBPlane & plane)
151 {
152 switch (state) {
153
154 case blindIn: {
155 doBlindIn(plane);
156 break;
157 }
158
159 case blindOut: {
160 doBlindOut(plane);
161 break;
162 }
163
164 case presentation: {
165 doPresentation(plane);
166 break;
167 }
168
169 default: {
170 logger.error() << "KenBurnsEffect: no frame available\n";
171 break;
172 }
173 }
174 return(*this);
175 }
176
createKBconfigRandom(RGBPlane & plane,uint32 pictureWidth,uint32 pictureHeight,uint32 frameWidth,uint32 frameHeight,uint32 sequenceLength,uint32 blindLength)177 KenBurnsEffect::KenBurnsConfig KenBurnsEffect::createKBconfigRandom(
178 RGBPlane& plane, uint32 pictureWidth, uint32 pictureHeight,
179 uint32 frameWidth, uint32 frameHeight, uint32 sequenceLength,
180 uint32 blindLength)
181 {
182
183 KenBurnsConfig config;
184
185 config.origPlane = plane;
186 config.outputWidth = frameWidth;
187 config.outputHeight = frameHeight;
188 config.sequenceLength = sequenceLength;
189 config.blindLength = blindLength;
190
191 float maxZoomfactor;
192 if ((pictureWidth * 1.0) / (frameWidth * 1.0) * (frameHeight * 1.0)
193 < (pictureHeight * 1.0))
194 maxZoomfactor = (pictureWidth * 1.0) / (frameWidth * 1.0);
195 else
196 maxZoomfactor = (pictureHeight * 1.0) / (frameHeight * 1.0);
197
198 config.zoomStart = maxZoomfactor * 0.75 + rand() * (maxZoomfactor * 0.25)
199 / (RAND_MAX * 1.0);
200 config.zoomEnd = maxZoomfactor * 0.75 + rand() * (maxZoomfactor * 0.25)
201 / (RAND_MAX * 1.0);
202
203 #ifdef DEBUG
204 logger.debug() << "Zooming ("<<maxZoomfactor<<") from factor "<<config.zoomStart<<" to "<<config.zoomEnd<<std::endl;
205 #endif
206
207 float availableXStart = pictureWidth - frameWidth * config.zoomStart;
208 float availableYStart = pictureHeight - frameHeight * config.zoomStart;
209
210 float availableXEnd = pictureWidth - frameWidth * config.zoomEnd;
211 float availableYEnd = pictureHeight - frameHeight * config.zoomEnd;
212
213 if ((availableXStart < 0) || (availableYStart < 0) || (availableXEnd < 0) || (availableYEnd < 0)) {
214 logger.error()<< "KenBurnsSequence: picture too small\n";
215 // what should we do on error?
216 }
217
218 float availLengthSqr(powf((availableXStart - availableXEnd), 2.0) + powf(
219 (availableYStart - availableYEnd), 2.0));
220 float lengthSqr;
221
222 do {
223 config.startpointX = (((float) rand()) * availableXStart) / (RAND_MAX
224 * 1.0);
225 config.startpointY = (((float) rand()) * availableYStart) / (RAND_MAX
226 * 1.0);
227
228 config.endpointX = (((float) rand()) * availableXEnd)
229 / (RAND_MAX * 1.0);
230 config.endpointY = (((float) rand()) * availableYEnd)
231 / (RAND_MAX * 1.0);
232
233 // calculate walklength
234 float lengthX = fabs(config.startpointX - config.endpointX);
235 float lengthY = fabs(config.startpointY - config.endpointY);
236
237 lengthSqr = powf(lengthX, 2.0) + powf(lengthY, 2.0);
238
239 } while (lengthSqr < availLengthSqr / 4.0);
240
241 return (config);
242 }
243
createKBconfigPredefine(RGBPlane & plane,uint32 pictureWidth,uint32 pictureHeight,uint32 frameWidth,uint32 frameHeight,uint32 sequenceLength,uint32 blindLength,uint32 predefine)244 KenBurnsEffect::KenBurnsConfig KenBurnsEffect::createKBconfigPredefine(
245 RGBPlane& plane, uint32 pictureWidth, uint32 pictureHeight,
246 uint32 frameWidth, uint32 frameHeight, uint32 sequenceLength,
247 uint32 blindLength, uint32 predefine)
248 {
249
250 KenBurnsConfig config;
251
252 config.origPlane = plane;
253 config.outputWidth = frameWidth;
254 config.outputHeight = frameHeight;
255 config.sequenceLength = sequenceLength;
256 config.blindLength = blindLength;
257
258 float maxZoomfactor;
259 if ((pictureWidth * 1.0) / (frameWidth * 1.0) * (frameHeight * 1.0)
260 < (pictureHeight * 1.0))
261 maxZoomfactor = (pictureWidth * 1.0) / (frameWidth * 1.0);
262 else
263 maxZoomfactor = (pictureHeight * 1.0) / (frameHeight * 1.0);
264
265 if (predefine < 5) {
266 config.zoomStart = maxZoomfactor * 0.9;//maxZoomfactor*0.75+rand()*(maxZoomfactor*0.25)/(RAND_MAX*1.0);
267 config.zoomEnd = maxZoomfactor * 0.9;//maxZoomfactor*0.75+rand()*(maxZoomfactor*0.25)/(RAND_MAX*1.0);
268 } else {
269 if (predefine < 9) {
270 config.zoomStart = maxZoomfactor * 0.8;//maxZoomfactor*0.75+rand()*(maxZoomfactor*0.25)/(RAND_MAX*1.0);
271 config.zoomEnd = maxZoomfactor * 0.9;//maxZoomfactor*0.75+rand()*(maxZoomfactor*0.25)/(RAND_MAX*1.0);
272 predefine -= 4;
273 } else {
274 if (predefine < 13) {
275 config.zoomStart = maxZoomfactor * 0.9;//maxZoomfactor*0.75+rand()*(maxZoomfactor*0.25)/(RAND_MAX*1.0);
276 config.zoomEnd = maxZoomfactor * 0.8;//maxZoomfactor*0.75+rand()*(maxZoomfactor*0.25)/(RAND_MAX*1.0);
277 predefine -= 8;
278 } else {
279 logger.error() << "Predefine No. <" << predefine
280 << "> not available\n";
281 exit(-1);
282 }
283 }
284 }
285
286 #ifdef DEBUG
287 logger.error()<< "Zooming (" << maxZoomfactor << ") from factor "
288 << config.zoomStart << " to " << config.zoomEnd << std::endl;
289 #endif
290
291 float availableXStart = pictureWidth - frameWidth * config.zoomStart;
292 float availableYStart = pictureHeight - frameHeight * config.zoomStart;
293
294 float availableXEnd = pictureWidth - frameWidth * config.zoomEnd;
295 float availableYEnd = pictureHeight - frameHeight * config.zoomEnd;
296
297 if ((availableXStart < 0) || (availableYStart < 0) || (availableXEnd < 0)
298 || (availableYEnd < 0)) {
299 logger.error() << "KenBurnsSequence: picture to small\n";
300 // was machen bei einem Fehler?
301 }
302
303 float availLengthSqr(powf((availableXStart - availableXEnd), 2.0) + powf(
304 (availableYStart - availableYEnd), 2.0));
305 float lengthSqr;
306
307 // do {
308
309 switch (predefine) {
310 case 1: {
311 config.startpointX = 0;
312 config.startpointY = 0;
313
314 config.endpointX = availableXEnd;
315 config.endpointY = availableYEnd;
316 break;
317 }
318 case 2: {
319 config.startpointX = availableXStart;
320 config.startpointY = 0;
321
322 config.endpointX = 0;
323 config.endpointY = availableYEnd;
324 break;
325 }
326 case 3: {
327 config.startpointX = availableXStart;
328 config.startpointY = availableYStart;
329
330 config.endpointX = 0;
331 config.endpointY = 0;
332 break;
333 }
334 case 4: {
335 config.startpointX = 0;
336 config.startpointY = availableYStart;
337
338 config.endpointX = availableXEnd;
339 config.endpointY = 0;
340 break;
341 }
342 }
343
344 // calculate walklength
345 float lengthX = fabs(config.startpointX - config.endpointX);
346 float lengthY = fabs(config.startpointY - config.endpointY);
347
348 lengthSqr = powf(lengthX, 2.0) + powf(lengthY, 2.0);
349
350 // } while (false); //lengthSqr < availLengthSqr/4.0);
351
352 return (config);
353 }
354