Xamarin.Forms Resim İşlemleri

Merhaba,

Genelde geliştirdiğimiz mobil uygulamalarda resim işlemleri bazen beklediğimizden fazla zamanımızı alabiliyor ve son kullanıcı deneyimlerini olumsuz etkileyebiliyor.

Bunlar; Web servis’e gönderilen resmin boyutu, kalitesi, yön hizalaması gibi durumlar olabiliyor. Bu maddeleri çoğaltabiliriz. Hepimiz biliyoruz ki, resim ne kadar büyük ölçekli (kalite vs) gönderilirse son kullanıcıya o kadar yüklenmiş oluruz. Internet tüketimi, işlemin sonlanmasının beklenmesi gibi sayabiliriz.

“Ben resmi çekip gönderirim ve bu tarz işlemler servis tarafında yapılmalıdır.” gibi durumlarla da karışlaşabiliyoruz. Açıkçası ben bu duruma pek katılamayacağım. Ufak bir senaryoyla bunu tartışabiliriz.

iPhone7 ile çekilmiş bir resim ortalama 3-5mb arası olduğunu kabul edersek. (Daha fazla olabileceğini hepimiz biliyoruz.) Bu resmi hiç işlemeden servise gönderip, yukarıda saydığımız maddeleri de serviste yaptığımızı kabul edelim. Bu karar bize bazı sorunlar oluşturabilir. Son kullanıcının bulunduğu yeri ve internet çekim tahmin etmemiz çok zor. Bu yüzden geliştirdiğimiz uygulamalarda en kötü senaryoları düşünüp, oluşan şartlara göre geliştirmelerimizi yapmalıyız.

Geliştirdiğim uygulamalarda bu tarz işlemleri mobil platformların yetenekleri doğrultusunda mobil tarafta yapmayı tercih ediyorum. Android ve iOS platformların bu konular için gerçekten çok kullanışlı sınıfları mevcut. Bu sınıfları kullarak çok hızlı ve zahmetsiz bir şekilde resim işlemleri için kendimizi en kötü senaryoya hazırlayabiliriz. Önemli olan hangi sınıfı hangi ihtiyaçta kullanacağımızı kararlaştırmak.

Xamarin.Forms projesi açıp, media plugin‘i kuralım. Xamarin Forms üzerinde media plugin kullanımını link üzerinden inceleyebilirsiniz.

Yukarıda belirttiğim gibi Android ve iOS platformların kendi yeteneklerini kullanacağımız için Xamarin Forms DependencyService‘i kullanacağım.

Ortak bir interface hazırlayarak kodlamaya başlayabiliriz.

 public interface IMediaService
 {
     byte[] ResizeImage(byte[] imageData, float width, float height);
     byte[] ResizeImage(string imagePath, float width, float height);
 }

İstersek bytearray veya çektiğimiz resmin cihaz üzerindeki path’ini kullanarak bu işlemi yapabiliriz.

Android için;

class MediaService : IMediaService
    {
        public byte[] ResizeImage(byte[] imageData, float width, float height)
        {
            try
            {
                BitmapFactory.Options options = new BitmapFactory.Options()
                {
                    InPurgeable = true,
                };
                Bitmap originalImage = BitmapFactory.DecodeByteArray(imageData, 0, imageData.Length, options);
                return CreateImage(originalImage, width, height);
            }
            catch (System.Exception ex)
            {
                return imageData;
            }
        }

        public byte[] ResizeImage(string imagePath, float width, float height)
        {
            try
            {
                BitmapFactory.Options options = new BitmapFactory.Options()
                {
                    InPurgeable = true,
                };
                Bitmap originalImage = BitmapFactory.DecodeFile(imagePath, options);
                return CreateImage(originalImage, width, height);
            }
            catch (System.Exception ex)
            {
                return new byte[0];
            }
        }

        private byte[] CreateImage(Bitmap bitmap, float width, float height)
        {
            float newHeight = 0;
            float newWidth = 0;

            var originalHeight = bitmap.Height;
            var originalWidth = bitmap.Width;

            if (originalHeight > originalWidth)
            {
                newHeight = height;
                float ratio = originalHeight / height;
                newWidth = originalWidth / ratio;
            }
            else
            {
                newWidth = width;
                float ratio = originalWidth / width;
                newHeight = originalHeight / ratio;
            }

            Bitmap resizedImage = Bitmap.CreateScaledBitmap(bitmap, (int)newWidth, (int)newHeight, true);
            bitmap.Recycle();

            using (MemoryStream ms = new MemoryStream())
            {
                resizedImage.Compress(Bitmap.CompressFormat.Png, 100, ms);
                resizedImage.Recycle();
                return ms.ToArray();
            }
        }
    }

iOS için;

class MediaService : IMediaService
    {
        public byte[] ResizeImage(byte[] imageData, float width, float height)
        {
            UIImage originalImage = ImageFromByteArray(imageData);
            return CreateImage(originalImage, width, height);
        }

        public byte[] ResizeImage(string imagePath, float width, float height)
        {
            UIImage originalImage = ImageFormPath(imagePath);
            return CreateImage(originalImage, width, height);
        }

        private UIImage ImageFromByteArray(byte[] data)
        {
            if (data == null)
                return null;

            return new UIImage(Foundation.NSData.FromArray(data));
        }

        private UIImage ImageFormPath(string path)
        {
            if (String.IsNullOrEmpty(path))
                return null;

            return new UIImage(path);
        }

        private byte[] CreateImage(UIImage image, float width, float height)
        {
            var originalHeight = image.Size.Height;
            var originalWidth = image.Size.Width;

            nfloat newHeight = 0;
            nfloat newWidth = 0;

            if (originalHeight > originalWidth)
            {
                newHeight = height;
                nfloat ratio = originalHeight / height;
                newWidth = originalWidth / ratio;
            }
            else
            {
                newWidth = width;
                nfloat ratio = originalWidth / width;
                newHeight = originalHeight / ratio;
            }

            width = (float)newWidth;
            height = (float)newHeight;

            UIGraphics.BeginImageContext(new SizeF(width, height));
            image.Draw(new RectangleF(0, 0, width, height));
            var resizedImage = UIGraphics.GetImageFromCurrentImageContext();
            UIGraphics.EndImageContext();

            var bytesImagen = resizedImage.AsJPEG().ToArray();
            resizedImage.Dispose();
            return bytesImagen;
        }
    }

Burada dikkat etmemiz gereken nokta, her iki platform için CreateImage metodu.

Çektiğimiz resmin width ve height değerlerini alarak, ufakta bir ratio kontrolu ve hesaplaması ile resim bizim verdiğimiz width ve height değerleriyle yeniden boyutlandırılıyor ve ölçeklendiriliyor. Bu işlemlerin sonunda geriye .png formatında 100 quality değerinde yeni bytearray dönüyor.

Kullanım için;

var resizeFile = DependencyService.Get<IMediaService>().ResizeImage(file.Path, 300, 300);

Kaynak kodları github üzerinden inceleyebilirsiniz.

Yiğit

Xamarin Developer, Consultant & Architect. Community Leader and Director of Xamarin Türkiye

Post A Reply