terça-feira, setembro 03, 2013

Manipulando imagens em .Net c#

Uma das coisas que me decepciona muito no .Net é a falta do senso de comunidade.
Calma aí quem for dizer que há comunidade no universo .Net. Se você pensou isso todo empolgado, entre para o mundo Java e verás a gritante diferença!

Mas mesmo chorando a falta de boas listas e grupos de usuários como temos em Java, ainda há outra coisa que me decepciona muito que é a falta de bons componentes gratuitos para fazer determinadas coisas.

Se em Java você despacha para Pai Google “como fazer tal coisa em Java” e encontra 4 milhões de posts/artigos/papers/tutoriais e mais uns 1,5K componentes gratuitos para fazer tal coisa, em .Net...
Quando tu achas alguma coisa é um componente pago ou alguns poucos posts no stackoverflow.com.

Então eu estava querendo fazer algumas coisas com imagens e me debrucei no balcão de informações Google.com a procura de respostas para:

Recuperar um array de bytes a partir de um arquivo.
Gerar uma String base 64 deste array de bytes.
Redimensionar uma imagem via código.

Primeiro de tudo deixe-me explicar porque disso tudo.

Eu estou gravando imagens em um banco de dados direto do código c# da minha aplicação. Mas não quero gravar o binário do arquivo da imagem no banco e depois ter que recuperar o binário, jogar para algum lado interpretar isso para eu ter uma tag <img /> com o source apontando para um programa que vai ler o binário e me devolver um response do tipo image/{png, jpg, bmp}.

Então eu pego uma imagem, leio os bytes dela e transformo esses bytes em uma String de base 64 que eu posso utilizar diretamente na tag <img />:

<img alt="Embedded Image" src="data:image/jpg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD//gA8Q1... />

Então criei uma classe para a famigerada namespace útil, chamada ImageUtil:

using System; 
using System.Drawing; 
using System.Drawing.Drawing2D; 
using System.Drawing.Imaging; 
using System.IO; 
 
namespace com.blogspot.jeanjmichel.util 

    /// <summary> 
    /// This class summarizes all possible operations applicable to an image. 
    /// </summary> 
    public class ImageUtil 
    { 
        /// <summary> 
        /// This method convert an image's bytes array to a based 64 String. 
        /// </summary> 
        /// <param name="bytesArray">Image's bytes in an array.</param> 
        /// <returns>Based 64 String.</returns> 
        public static String ConvertArrayOfBytesToStringBase64(byte[] bytesArray) 
        { 
            return System.Convert.ToBase64String(bytesArray, 0, bytesArray.Length); 
        } 
 
        /// <summary> 
        /// This method loads an image to an array of bytes. 
        /// </summary> 
        /// <param name="fileFullPath">Full path (path + file name) of the image.</param> 
        /// <returns>Image's bytes in an array.</returns> 
        public static byte[] ConvertImageToArrayOfBytes(String fileFullPath) 
        { 
            FileStream fS = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read); 
             
            byte[] imgBytes = new byte[fS.Length]; 
            fS.Read(imgBytes, 0, (int)fS.Length); 
             
            fS.Close(); 
             
            return imgBytes; 
        } 
 
        /// <summary> 
        /// This method returns the extension of the image's file. 
        /// </summary> 
        /// <param name="fileFullPath">Full path (path + file name) of the image.</param> 
        /// <returns>The extension of the image's file.</returns> 
        public static String GetImageExtension(String fileFullPath) 
        { 
            return Path.GetExtension(fileFullPath); 
        } 
 
        /// <summary> 
        /// This method generates an HTML <img /> tag with the embedded image data. 
        /// </summary> 
        /// <param name="fileExtension">The extension of the image's file.</param> 
        /// <param name="imgInBase64">The image's bytes in a based 64 String.</param> 
        /// <returns>An HTML <img /> tag with embedded image data.</returns> 
        public static String GenerateHTMLImgTag(String fileExtension, String imgInBase64) 
        { 
            return "<img alt=\"Embedded ImageUtil\" src=\"data:image/" + fileExtension + ";base64," + imgInBase64 + "\" />"; 
        } 
 
        /// <summary> 
        /// This method resizes the image to a specified size. 
        /// </summary> 
        /// <param name="inFileFullPath">Full path (path + file name) of the original image.</param> 
        /// <param name="outFileFullPath">Full path (path + file name) of the resized image.</param> 
        /// <param name="newImagSize">Wanted size.</param> 
        /// <returns></returns> 
        public static Boolean ResizeImageTo(String inFileFullPath, String outFileFullPath, Size newImagSize) 
        { 
            System.Drawing.Image originalImage = new Bitmap(inFileFullPath); 
 
            int originalWidth = originalImage.Width; 
            int originalHeight = originalImage.Height; 
 
            if (originalWidth > newImagSize.Width || originalHeight > newImagSize.Height) 
            { 
                float nPercent = 0; 
                float nPercentW = 0; 
                float nPercentH = 0; 
                 
                nPercentW = ((float)newImagSize.Width / (float)originalWidth); 
                nPercentH = ((float)newImagSize.Height / (float)originalHeight); 
                 
                if (nPercentH < nPercentW) 
                    nPercent = nPercentH; 
                else 
                    nPercent = nPercentW; 
                 
                int destWidth = (int)(originalWidth * nPercent); 
                int destHeight = (int)(originalHeight * nPercent); 
                 
                Bitmap b = new Bitmap(destWidth, destHeight); 
                Graphics g = Graphics.FromImage(b); 
                g.InterpolationMode = InterpolationMode.HighQualityBicubic; 
                 
                g.DrawImage(originalImage, 0, 0, destWidth, destHeight); 
                g.Dispose(); 
                b.Save(outFileFullPath, ImageFormat.Jpeg); 
                 
                b.Dispose(); 
 
                return true; 
            } 
             
            return false; 
        } 
    } 


Essa classe tem métodos para retornar o array de bytes da imagem, a String base 64, gerar a tag HTML utilizando está String base 64 e ainda posso redimensiona a imagem.

Rodando um teste com a classe, onde eu tenho uma imagem em resolução 720x540 e irei:

Gerar um arquivo com a tag HTML embutindo os dados da imagem no source;
Gerar uma cópia da imagem em resolução de até 335x180 para utilizar neste post;
E gerar um arquivo com a tag HTML embutindo os dados da imagem redimensionada no source.

Eis a classe teste:

using System; 
using System.IO; 
using com.blogspot.jeanjmichel.util; 
using System.Drawing; 
 
namespace com.blogspot.jeanjmichel 

    public class Program 
    { 
        static void Main(string[] args) 
        { 
            String imagesDirectoryPath = @"C:\\WorkingWithImages\images\"; 
 
            String originalImageName = "Anita.jpg"; 
            String originalImageFullPath = imagesDirectoryPath + originalImageName; 
            String originalImageExtension = ImageUtil.GetImageExtension(imagesDirectoryPath + originalImageName).Replace(".", "").ToLower(); 
            String originalImageOutTextFile = imagesDirectoryPath + originalImageName.Replace("." + originalImageExtension, ".txt"); 
             
            String resizedImageName = originalImageName.Replace("." + originalImageExtension, " - resized.jpg"); 
            String resizedImageFullPath = imagesDirectoryPath + resizedImageName; 
            String resizedImageOutTextFile = imagesDirectoryPath + resizedImageName.Replace(".jpg", ".txt"); 
             
            Console.WriteLine("Generating HTML <img /> tag with embedded image data..."); 
             
            byte[] originalImageBytes = ImageUtil.ConvertImageToArrayOfBytes(originalImageFullPath); 
            String originalImageInStringBase64 = ImageUtil.ConvertArrayOfBytesToStringBase64(originalImageBytes); 
            StreamWriter txtFile = new StreamWriter(originalImageOutTextFile); 
            txtFile.Write(ImageUtil.GenerateHTMLImgTag(originalImageExtension, originalImageInStringBase64)); 
            txtFile.Flush(); 
            txtFile.Dispose(); 
             
            Console.WriteLine("Done!"); 
             
            Console.WriteLine("Resizing the image to 335x180 resolution..."); 
             
            if (ImageUtil.ResizeImageTo(originalImageFullPath, resizedImageFullPath, new Size(640, 480))) 
            { 
                Console.WriteLine("Done!"); 
                Console.WriteLine("Generating HTML <img /> tag with embedded resized image data ..."); 
                 
                byte[] resizedImageBytes = ImageUtil.ConvertImageToArrayOfBytes(resizedImageFullPath); 
                String resizedImageInStringBase64 = ImageUtil.ConvertArrayOfBytesToStringBase64(resizedImageBytes); 
                StreamWriter txtFile2 = new StreamWriter(resizedImageOutTextFile); 
                txtFile2.Write(ImageUtil.GenerateHTMLImgTag("jpg", resizedImageInStringBase64)); 
                txtFile2.Flush(); 
                txtFile2.Dispose(); 
 
                Console.WriteLine("Done!"); 
            } 
            else 
            { 
                Console.WriteLine("The image is smaller than the new resolution!"); 
            } 
             
            Console.ReadKey(); 
        } 
    } 


E a saída no console:

C:\WorkingWithImages\bin\Debug>WorkingWithImages.exe
Generating HTML <img /> tag with embedded image data...
Done!
Resizing the image to 335x180 resolution...
Done!
Generating HTML <img /> tag with embedded resized image data ...
Done!


Pronto, a imagem foi redimensionada e salva em disco, além de ter sido gerados dois arquivos txt com as tags HTML <img /> no mesmo diretório de imagens.

Original:

Embedded ImageUtil

Redimensionada:

Embedded ImageUtil

Está é a Anita, minha labradora ;)

Bom proveito para quem mais for útil ou possa interessar manipular imagens em c#. Lembrando que o código está no Git Hub também.

Nenhum comentário: