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