0

I'm building an App with actionscript 3.0 in my Flash builder. This is a followup question to this question, It works but when I take the picture, the image comes out rotated to the left. how can I check which way the user is holding the phone? and then what code do I use to rotate the image to it's corresponding place?

Thanks in advanced!

EDIT: I'm using this code to rotate the image, but it seems to only rotate the image being displayed not the image file, any ideas?

var mat:Matrix = new Matrix();
mat.translate(-W/2, -H/2);
mat.rotate(Math.PI/2);
mat.translate(+W/2, +H/2);
mat.concat(myObj.transform.matrix);
myObj.transform.matrix = mat;

~Myy

Community
  • 1
  • 1
Myy
  • 18,107
  • 11
  • 37
  • 57

3 Answers3

2

You can't get stage.orientation data when you have the cameraUI view active, so here's how i solved the same issue, please note that this controller has many reusable functions, i first get the raw image data from media promise (this raw data has EXIF data that i will read to get image orientation from the camera) i then convert this byteArray to BitmapData and then resize and scale as i need. One thing to note is that if you convert the raw image data to Bitmap data you will lose EXIF data so read it before you modify the image, hope this helps and i posted it here late i know but there's no other solution in the web and maybe someone will need this sometime. Cheers

SnapshotController.as

package controllers
{

    import flash.display.BitmapData;
    import flash.display.JPEGEncoderOptions;
    import flash.display.Loader;
    import flash.display.LoaderInfo;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.IEventDispatcher;
    import flash.events.MediaEvent;
    import flash.filesystem.File;
    import flash.filesystem.FileMode;
    import flash.filesystem.FileStream;
    import flash.geom.Matrix;
    import flash.geom.Rectangle;
    import flash.media.CameraUI;
    import flash.media.MediaPromise;
    import flash.media.MediaType;
    import flash.utils.ByteArray;
    import flash.utils.IDataInput;

    import mx.utils.Base64Encoder;

    import classes.APIupload;


    public class SnapshotController
    {
        private var cameraUI:CameraUI = new CameraUI();
        private var dataSource:IDataInput; 
        private var tempDir:File = new File();
        private var imageOrientation:int = 0 ;


        public function SnapshotController()
        {

        }

        public function LaunchCameraUI():void
        {

            if( CameraUI.isSupported ) {
                trace( "Initializing camera..." ); 
                cameraUI.addEventListener( MediaEvent.COMPLETE, imageSelected ); 
                cameraUI.launch( MediaType.IMAGE ); 
            } else {
                trace( "CameraUI is not supported."); 
            }
        }

        private function imageSelected( event:MediaEvent ):void {
            trace( "Media selected..." ); 
            var imagePromise:MediaPromise = event.data; 


            dataSource = imagePromise.open(); 
            if( imagePromise.isAsync ) {
                trace( "Asynchronous media promise." ); 
                var eventSource:IEventDispatcher = dataSource as IEventDispatcher; 
                eventSource.addEventListener( Event.COMPLETE, onMediaLoaded );
            } else {
                trace( "Synchronous media promise." ); 
                readMediaData(); 
            }
        } 
        private function onMediaLoaded( event:Event ):void {
            trace("Media load complete"); 
            readMediaData();
        } 

        private function readMediaData():void { 
            var imageBytes:ByteArray = new ByteArray(); 
            dataSource.readBytes( imageBytes );

            imageOrientation = getOrientation(imageBytes);

            //saveImageFile(imageBytes);
            //Saving this byteArray will save a big file with the EXIF in it.

            var loader:Loader = new Loader();
            loader.contentLoaderInfo.addEventListener(Event.INIT, onMediaPromiseLoaded);
            loader.loadBytes(imageBytes);

        } 


        private function onMediaPromiseLoaded(e:Event):void
        {
            trace("Media file loaded");
            var base64Enc:Base64Encoder = new Base64Encoder();
            var mpLoaderInfo:LoaderInfo = e.target as LoaderInfo;
            mpLoaderInfo.removeEventListener(Event.COMPLETE, onMediaPromiseLoaded);

            var scale:Number = 0.25;
            var matrix:Matrix = new Matrix();
            matrix.scale(scale, scale);


            var jpgQuality:int = 80;


            var bmd:BitmapData = new BitmapData(mpLoaderInfo.width*scale, mpLoaderInfo.height*scale);
            bmd.draw(mpLoaderInfo.content,matrix,null,null,null,true);  

            var rotatedBMD:BitmapData = rotateBitmapData(bmd, imageOrientation);
            var bytes:ByteArray = rotatedBMD.encode(new Rectangle(0,0, rotatedBMD.width , rotatedBMD.height), new JPEGEncoderOptions(jpgQuality), bytes);




            saveImageFile(bytes); //this is the smaller file saved. it does not have EXIF data but you can write your own using AS3 eXIF class.

        } 

        private function rotateBitmapData( bitmapData:BitmapData, degree:int = 0 ) :BitmapData
        {
            var newBitmap:BitmapData;
            var matrix:Matrix = new Matrix();
            matrix.rotate( degree * (Math.PI / 180)  );

            if ( degree == 90 ) {
                newBitmap = new BitmapData( bitmapData.height, bitmapData.width, true );
                matrix.translate( bitmapData.height, 0 );
            } else if ( degree == -90 || degree == 270) {
                newBitmap = new BitmapData( bitmapData.height, bitmapData.width, true );
                matrix.translate( 0, bitmapData.width );
            } else if ( degree == 180 ) {
                newBitmap = new BitmapData( bitmapData.width, bitmapData.height, true );
                matrix.translate( bitmapData.width, bitmapData.height );
            }else if(degree == 0){
                newBitmap = new BitmapData( bitmapData.width, bitmapData.height, true );
                //matrix.translate( bitmapData.width, bitmapData.height );
            }

            newBitmap.draw( bitmapData, matrix, null, null, null, true )
            return newBitmap;
        }

        private function saveImageFile(ba:ByteArray):void{
            var now:Date = new Date(); 
            var filename:String = "IMG" + now.fullYear + now.month + now.day + now.hours + now.minutes + now.seconds + ".jpg";
            var temp:File = File.documentsDirectory.resolvePath( filename );
            var stream:FileStream = new FileStream(); 
            stream.open( temp, FileMode.WRITE ); 
            stream.writeBytes( ba ); 
            stream.close();
        }



        private function getOrientation(jpeg:ByteArray):int{
            if (jpeg == null) {
                return 0;
            }
            var offset:int = 0;
            var length:int = 0;


            while (offset + 3 < jpeg.length && (jpeg[offset++] & 0xFF) == 0xFF) {
                var marker:int = jpeg[offset] & 0xFF;

                // Check if the marker is a padding.
                if (marker == 0xFF) {
                    continue;
                }
                offset++;

                // Check if the marker is SOI or TEM.
                if (marker == 0xD8 || marker == 0x01) {
                    continue;
                }
                // Check if the marker is EOI or SOS.
                if (marker == 0xD9 || marker == 0xDA) {
                    break;
                }

                // Get the length and check if it is reasonable.
                length = pack(jpeg, offset, 2, false);
                if (length < 2 || offset + length > jpeg.length) {
                    trace("Invalid length");
                    return 0;
                }

                // Break if the marker is EXIF in APP1.
                if (marker == 0xE1 && length >= 8 &&
                    pack(jpeg, offset + 2, 4, false) == 0x45786966 &&
                    pack(jpeg, offset + 6, 2, false) == 0) {
                    offset += 8;
                    length -= 8;
                    break;
                }

                // Skip other markers.
                offset += length;
                length = 0;
            }

            if (length > 8) {
                // Identify the byte order.
                var tag:int = pack(jpeg, offset, 4, false);
                if (tag != 0x49492A00 && tag != 0x4D4D002A) {
                    trace("Invalid byte order");
                    return 0;
                }
                var littleEndian:Boolean = (tag == 0x49492A00);

                // Get the offset and check if it is reasonable.
                var count:int = pack(jpeg, offset + 4, 4, littleEndian) + 2;
                if (count < 10 || count > length) {
                    trace( "Invalid offset");
                    return 0;
                }
                offset += count;
                length -= count;

                // Get the count and go through all the elements.
                count = pack(jpeg, offset - 2, 2, littleEndian);
                while (count-- > 0 && length >= 12) {
                    // Get the tag and check if it is orientation.
                    tag = pack(jpeg, offset, 2, littleEndian);
                    if (tag == 0x0112) {
                        // We do not really care about type and count, do we?
                        var orientation:int = pack(jpeg, offset + 8, 2, littleEndian);
                        switch (orientation) {
                            case 1:
                                return 0;
                            case 3:
                                return 180;
                            case 6:
                                return 90;
                            case 8:
                                return 270;
                        }
                        trace( "Unsupported orientation");
                        return 0;
                    }
                    offset += 12;
                    length -= 12;
                }
            }

            trace( "Orientation not found");
            return 0;

        }

        private function pack(bytes:ByteArray,offset:int,length:int,
                              littleEndian:Boolean):int {
            var step:int = 1;
            if (littleEndian) {
                offset += length - 1;
                step = -1;
            }

            var value:int = 0;
            while (length-- > 0) {
                value = (value << 8) | (bytes[offset] & 0xFF);
                offset += step;
            }
            return value;
        }

    }
}
Hakim
  • 1,242
  • 1
  • 10
  • 22
1

You can use Stage.deviceOrientation or Stage.orientation* to determine which way round the phone is.

*not sure if this one works on iOS

Is it the BitmapData result itself that you want to rotate (ie create a new BitmapData with rotated image) or just rotate a Bitmap on the display list?

Edit:

Ok, heres some code to rotate a BitmapData object:

function rotateBitmapData(angle:int, source:BitmapData):BitmapData
{
    var newWidth:int = source.rect.width;
    var newHeight:int = source.rect.height;
    if (angle==90 || angle==270)
    {
        newWidth = source.rect.height;
        newHeight = source.rect.width;
    }
    var newBmd:BitmapData = new BitmapData(newWidth, newHeight, source.transparent);
    var tx:Number = 0;
    var ty:Number = 0;
    if (angle==90 || angle==180)
    {
        tx = newWidth;
}
    if (angle==180 || angle==270)
    {
        ty = newHeight;
    }
    var matrix:Matrix = new Matrix();
    matrix.createBox(1, 1, Math.PI*angle/180, tx, ty);
    newBmd.draw(source, matrix);
    return newBmd;
}

angle should be 0,90,180 or 270. It will return a new BitmapData object rotated by specified angle.

  • the data, I want to rotate the data, I edited the code to rotate the image and it works, but only in display, and I really need it to rotate the file ( data) – Myy Feb 20 '13 at 18:28
  • hey Lee, thanks so much for the info, I posted the rest of my code where I change it to a byteArray but the conversion from bitmapData to byteArray takes a little over a minute. Do you know of a faster more effective way to achieve this? – Myy Feb 21 '13 at 17:13
  • The jpeg encoder is quite slow - especially on a mobile device. Other jpeg encoders are available which may give (slightly) better results. Or you could use an asynchronous encoder so app doesnt freeze while encoding. But that would be a different question –  Feb 21 '13 at 18:15
  • excellent, I will formulate a different question and edit this, the new question is here if you could help http://stackoverflow.com/questions/15009938/jpeg-encoder-super-slow-how-to-optimize-it – Myy Feb 21 '13 at 18:44
  • The only async encoders i am aware of are my own: https://github.com/LeeBurrows/Async-Image-Encoders, but new question may attract better solutions ;) –  Feb 21 '13 at 18:56
0

You can use StageOrientationEvent.ORIENTATION_CHANGING Event :

stage.addEventListener(StageOrientationEvent.ORIENTATION_CHANGING, OrientationChangeHandler);

private function OrientationChangeHandler(e:StageOrientationEvent):void
        {
            switch (e.afterOrientation)
            {


                case StageOrientation.DEFAULT :

                break;
            case StageOrientation.ROTATED_RIGHT :

                break;
            case StageOrientation.ROTATED_LEFT :

                break;
            case StageOrientation.UPSIDE_DOWN :

                break;
            }
        }

this can help you.

Vipul
  • 431
  • 1
  • 5
  • 16
  • Hi Vipul, thanks for the answer, but I can't seem to be able to go inside the switch statement. I'm trying to rotate it with the code I posted in the edit part of the question, but it appears to only work for the image in the display, NOT the file (which is what I really want) – Myy Feb 20 '13 at 08:11