Intro

J'ai créé ce blog pour partager avec vous une de mes passions : le développement informatique. J'essaierais de vous faire profiter de mes découvertes et surtout de publier régulièrement des articles. N'hésitez pas à apporter vos commentaires!

dimanche 1 mai 2011

Flex 4 : ByteArray BitmapImage

Lorsqu’on a une application flex qui communique avec un serveur, il est fréquent qu’on reçoive des images sous forme de tableau de bytes.
Je souhaitais pouvoir utiliser les nouvelles fonctionnalités du BitmapImage(l'option fillmode notamment), malheureusement ce dernier n'accepte pas de ByteArray comme source pour le binding (alors qu'un mx:Image ou la version 4.5 du BitmapImage le supporte parfaitement). C’est pourquoi j’ai décidé de créer un BitmapImage customisé. Ce dernier permet aussi, si on le souhaite, de redimensionner l’image de manière à ce qu’elle soit adaptée à la taille du conteneur.


import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Loader;
    import flash.display.LoaderInfo;
    import flash.events.Event;
    import flash.utils.ByteArray;

    import spark.primitives.BitmapImage;

    public class ByteArrayBitmapImage extends BitmapImage
    {
        public function ByteArrayBitmapImage()
        {
            super();
        }

        // The byteArray to load
        private var _currentByteArray:ByteArray;
        // The maximum width for the bitmap
        private var _maxContainerHeight:Number = -1;
        // The maximum height for the bitmap
        private var _maxContainerWidth:Number = -1;

        [Bindable]
        public function get maxContainerWidth():Number
        {
            return _maxContainerWidth;
        }

        public function set maxContainerWidth(value:Number):void
        {
            _maxContainerWidth = value;
        }

        [Bindable]
        public function get maxContainerHeight():Number
        {
            return _maxContainerHeight;
        }

        public function set maxContainerHeight(value:Number):void
        {
            _maxContainerHeight = value;
        }

        [Bindable]
        override public function set source(value:Object):void
        {
            var byteArray:ByteArray = null;
            if (value && value is ByteArray && (byteArray = ByteArray(value)).length >
                0)
            {
                _currentByteArray = byteArray;
                // we set cursor at the beginning
                byteArray.position = 0;
                var img_loader:Loader = new Loader();
                img_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, imageLoadComplete, false, 0, true);
                img_loader.loadBytes(byteArray);
            }
            else
            {
                // default behavior
                _currentByteArray = null;
                super.source = value;
            }
        }

        protected function imageLoadComplete(e:Event):void
        {
            e.stopImmediatePropagation();
            var contentLoaderInfo:LoaderInfo = LoaderInfo(e.currentTarget)
            contentLoaderInfo.removeEventListener(Event.COMPLETE, imageLoadComplete);
            var bm:Bitmap = Bitmap(contentLoaderInfo.content);
            bm.smoothing = true;
            resizeImageIfNecessary(bm);
            super.source = bm;
        }

        protected function resizeImageIfNecessary(bmd:Bitmap):void
        {
            if ((_maxContainerWidth == -1) || (_maxContainerHeight == -1))
                return; // no need to resize

            var imageRapport:Number;
            if (bmd.width >= bmd.height)
            {
                if (bmd.width > _maxContainerWidth)
                {
                    imageRapport = Number(bmd.width) / Number(bmd.height);
                    bmd.width = _maxContainerWidth;
                    bmd.height = _maxContainerHeight / imageRapport;
                }
            }
            else
            {
                if (bmd.height > _maxContainerHeight)
                {
                    imageRapport = Number(bmd.height) / Number(bmd.width);
                    bmd.height = _maxContainerHeight;
                    bmd.width = _maxContainerWidth / imageRapport;
                }
            }
        }
    }

Cependant je n’ai pas mis en place le redimensionnement automatique de l’image lorsque les limites du conteneur sont modifiées. Pour y remédier, il suffit d’appeler la méthode resizeImageIfNecessary(bmd:Bitmap) dans les 2 mutateurs.
Et voilà vous pouvez utiliser votre composant dans vos fichiers mxml :
<common:bytearraybitmapimage height="@{model.test.imageHeight}" id="test" maxcontainerheight="{maxImageHeight}" maxcontainerwidth="{maxImageWidth}" source="{model.test.imageContent}" width="@{model.test.imageWidth}" />

4 commentaires:

bo a dit…

Cool! Est-ce que je peux le reutiliser en Flex 3 avec quelques modificqtions mineurs?

_TomTom_ a dit…

Le soucis est que cette classe hérite d'un composant spark (BitmapImage) qui est présent qu'à partir de la version 4 du SDK Flex. En Flex 3, le composant mx:Image accepte un ByteArray comme source pour le binding. De plus si tu veux maintenir les proportions de ton image il te suffit d'utiliser la propriété maintainAspectRatio="true".

~lD~ a dit…

éhé à garder sous la main !

Par contre, question lisibilité, il y a une utilité à garder les getter et setter ? cf Article adobe : Why we don't have to use getters and setters

_TomTom_ a dit…

En fait je voulais rajouter du comportement lorsqu'on modifie la taille max du conteneur, mais je ne l'ai pas mis en place ni même testé :). Je le modifierai quand ce sera fait ;)