Hur man skivar en videofilm

 

I denna guide ska jag gå igenom hur man kan skiva upp en spelande videofilm i Flash med hjälp av ActionScript 3.0. Klicka här för att se ett exempel där detta används i ett spel.

För att vi ska kunna bearbeta en film är det viktigt att förstå hur en spelfilm är uppbyggd. En film består av en serie stillbilder som visas i snabb följd. Detta ger då upphov till en illusion av rörelse. Varje stillbild kallar vi i fortsättningen för en bildruta i filmen. När vi ska skiva upp filmen kommer vi att göra det individuellt för varje bildruta. Eftersom varje bildruta är en vanlig stillbild kan vi börja med att beskriva hur man skivar upp en enskild bild och sen enkelt ta steget över till att skiva upp en film som består av flera stillbilder.

En bild i ActionScript representeras av klassen Bitmap. Ett objekt av typen Bitmap håller en referens till ett objekt av typen BitmapData som innehåller pixelinformationen om vad som ska visas i bilden. I vårt exempel börjar vi med att skapa ett nytt objekt av typen BitmapData som har bredden width och höjden height och ger detta objekt till en Bitmap för att kunna visa informationen som en bild.

var tBitmap:Bitmap = new Bitmap();
var tBD:BitmapData = new BitmapData(width, height);
tBitmap.bitmapData = tBD;

För att kunna fånga en del av en filmruta använder vi oss av metoden draw() hos klassen BitmapData.

public function draw(source:IBitmapDrawable, matrix:Matrix = null, colorTransform:ColorTransform = null, blendMode:String = null, clipRect:Rectangle = null, smoothing:Boolean = false):void

Den första parametern till denna metod är source. Det är denna som kommer att ritas av och placeras i BitmapData-objektet som metoden anropas på. Förutom source kommer vi även att skicka med parametrarna matrix och clipRect. Tillsammans definierar dessa två vilken del av source som vi vill klippa ut och rita av i BitmapData-objektet. Av parametrarna är matrix en matris som definierar hur bitmapens koordinater ska skalas, roteras och förflyttas. I vårt fall är vi bara intresserade av förflyttningen för att kunna definierar vilken del av filmen som ska klippas ut. För att ange hur stor del av filmen som ska klippas ut så skickar vi med clipRect som är en rektangel vars höjd och bredd definierar hur stor del av bildrutan som ska kopieras.

var tBitmap:Bitmap = new Bitmap();
var tBD:BitmapData = new BitmapData(width, height);
var tM:Matrix = new Matrix();
tM.translate(x, y);
var tR:Rectangle = new Rectangle(0, 0, width, heigh);
tBD.draw(source, tM, null, null, tR);
tBitmap.bitmapData = tBD;

Koden ovan kommer att rita av den delen av source som finns inom en rektangel med bredden width och höjden hight och vars övre vänstra hörn är placerat i koordinaten (x, y).

Bild som visar placering och storlek på utklippet

Så här långt har vi bara klippt ut en del av en filmruta, vill man klippa upp en hel filmruta i flera delar så får man upprepa koden ovan för varje del man vill klippa filmrutan i. I exemplet nedan klipps filmrutan upp i nio delar. Tre horisontalt och tre vertikalt.

Bild som visar hur filmrutan delas upp i nio lika stora delar

protected function slice(rSource:DisplayObject):void
{
   var tTilesOneDirection:uint = 3;
   var tWidth:Number = rSource.width / tTilesOneDirection;
   var tHeight:Number = rSource.height / tTilesOneDirection;
   var tBitmap:Bitmap;
   var tBD:BitmapData = new BitmapData(tWidth, tHeight, false); 
   var tM:Matrix = new Matrix();
   var tR:Rectangle = new Rectangle(0, 0, tWidth, tHeight);
   var i:uint;
   var j:uint;
   for (i = 0; i < pTilesOneDirection; i++)
   {
      for (j = 0; j < pTilesOneDirection; j++)
      { 
         tM = new Matrix();
         tM.translate(-i * tWidth, -j * tHeight);
         tBD.draw(rSource, tM, null, null, tR);
         tBitmap = new Bitmap();
         tBitmap.bitmapData = tBD.clone();
         addChild(tBitmap);
      }
   }
   tBD.dispose();
}

I koden ovan börjar vi med att definierar hur många delar filmrutan ska klippas upp i. När vi har definerat det kan vi räkna ut bredd och höjd på varje del genom att ta filmrutans bredd och höjd delat med antalet delar i varje ledd.

Därefter skapar vi en ny Bitmap, BitmapData, Matrix och Rectangle som i tidigare exempel. Dock väntar vi med att sätta förflyttningen i matrisen. Denna kommer vi istället att sätta inne i looparna eftersom förflyttningen kommer att vara olika för de olika delarna som filmrutan klipps upp i. Vi skapar två olika for-loopar, den första för att klippa filmrutan i x-led och den andra för att göra det i y-led. Inuti looparna sätter vi förflyttningen i matrisen beroende på vilken del av filmrutan vi vill klippa ut. Vilken del som är aktuell defineras av i och j. För att beräkna vad vi ska sätta förflyttningen till så multiplicerar vi i med bredden och j med höjden för en utklippt bit. På så sätt får vi fram koordinaten för det övre vänstra hörnet.

När vi har satt förflyttningen i matrisen kan vi anropa metoden draw på samma sätt som tidigare. Eftersom vi klipper upp filmrutan i nio olika delar måste vi skapa en ny Bitmap för varje del. Till denna Bitmap, tBitmap, skickar vi en referens till en kopia av den utklippta BitmapData-objektet, tBD. Orsaken till att vi skapar en kopia är att vi använder samma BitmapData genom hela loopen. Eftersom tBitmap får en referens till en BitmapData så kommer alla tBitmap att visat samma sak om vi inte gör en kopia, eftersom alla tBitmap i så fall har en referens till samma BitmapData. Skapar vi istället en kopia så håller alla tBitmap en referens till var sin BitmapData som visar hur tBD såg ut när kopian skapades.

Till sist anropar vi disposetBD för att frigöra det minne som den tar upp. För att koden ska visa något på skärmen måste man naturligtvis lägga till varje tBitmap till displaylistan med addChild(tBitmap).

Svårare än så här är det inte att klippa sönder en bildruta i mindre bitar. När vi nu vet hur man gör det för en enskild bildruta är det bara att upprepa det för varje bildruta i hela filmen. Enklast gör man det genom att lyssna på onEnterFrame-eventet för filmen och köra koden på varje ny filmruta. Ska man göra detta på varje bildruta kan det vara lämpligt att spara alla tBitmap utanför metoden och återanvända dem för varje bildruta i filmen. Då behöver man inte skapa nya för varje bildruta utan bara ändra informationen i dess BitmapData istället.

Letar ni efter en erfaren frilans flash konsult? Tveka inte att höra av er till mig här. Vill ni veta mer om vad jag har att erbjuda som frilansande flashutvecklare så hittar ni den informationen här.