Accueil > Silverlight, Silverlight 3 > [SL3] : JPG thumbnail d’une image JPG ou PNG en Silverlight 3

[SL3] : JPG thumbnail d’une image JPG ou PNG en Silverlight 3

Update 22.11.2009 : Avec cette technique vous pouvez aussi réaliser une miniature de fichiers PNG mais la sortie sera un fichier jpg.  Modifiez la ligne par : ofd.Filter = « Fichiers image|*.jpg;*.jpeg;*.png »;

La version actuelle de silverlight ne propose pas (nativement) de composants permettant l’encodage d’images comme on pourrait le faire en WPF. Cela dit certains projets permettent à présent de réaliser cela mais la tache n’est pas forcement aisée. Cette technique peut être utilisé afin de compresser et retailler une image avant de l’envoyer au serveur (via un service WCF par exemple), les gains sont multiples : 

  • utilisation de la bande passante réduite au minimum
  • réactivité de l’application
  • meilleur disponibilité de la plateforme de service et donc des serveurs

Ayant été confronté à ce problème récemment, je vous propose mon retour sur expérience dans le domaine ! 

Pré requis

Librairie FJCore (permettant d’encoder des fichiers en JPG) que vous pourrez trouver ici 

La classe ImageHelpers !

Voici ma classe utilitaire permettant de compresser et retailler facilement une image provenant d’un stream (exemple d’un OpenFileDialog). Il est question ici d’une classe qui a fait son apparition dans Silverlight 3 (connue en WPF) : WritableBitmap. La clé de la réussite résulte dans le rendu du controle image dans un WritableBitmap et de convertir celui-ci via la librairie FJCore afin d’obtenir des données encodées au format JPG. 

		/// <summary>
		/// Compresse et réduit une image JPG
		/// </summary>
		/// <param name="stream">Stream de l'image à compresser</param>
		/// <param name="maxSize">Taille maximun de l'image compressée</param>
		/// <param name="quality">Qualité de compression JGP (0 - 100)</param>
		/// <returns>Tableau de byte contenant l'image compressée</returns>
		public static byte[] CompressImage(Stream stream, Int32 maxSize, Int32 quality)
		{
			BitmapImage imageSource = null;
			Int32 width = 1;
			Int32 height = 1;

			//Création d'un BitmapImage depuis le Stream
			imageSource = new BitmapImage();
			imageSource.CreateOptions = BitmapCreateOptions.DelayCreation;
			imageSource.SetSource(stream);

			//Calcul de la largeur et de la hauteur
			if (imageSource.PixelWidth > imageSource.PixelHeight)
			{
				width = imageSource.PixelWidth * maxSize / imageSource.PixelHeight;
				height = maxSize;
			}
			else
			{
				width = maxSize;
				height = imageSource.PixelHeight * maxSize / imageSource.PixelWidth;
			}

			//Création du WritableBitmap de sortir
			WriteableBitmap w = new WriteableBitmap(width, height);

			//image qui va nous servir générer le thumbnail
			Image i = new Image();
			i.Stretch = Stretch.Uniform;
			i.Source = imageSource;
			i.Height = height;
			i.Width = width;

			//on effectue un rendu de l'image dans le WritableBitmap ! et on obtient l'image miniature !
			w.Render(i, null);
			w.Invalidate();

			//on convertie le WritableBitmap
			return ConvertToBytes(w, quality);
		}

Ensuite il faut convertir le WritableBitmap en raster et compresser celui-ci en flux JPG (entrent en scene la librairie FJCore) et ainsi récupérer les bytes de la miniature ! 


		/// <summary>
		/// Obtient les bytes correspondant à un WritableBitmap dans un format JPG
		/// </summary>
		/// <param name="bitmap">Le bitmap à traduire en bytes</param>
		/// <param name="quality">Qualité de compression JGP (0 - 100)</param>
		/// <returns>Tableau de byte contenant l'image compressée</returns>
		private static byte[] ConvertToBytes(WriteableBitmap bitmap, Int32 quality)
		{
			int width = bitmap.PixelWidth;
			int height = bitmap.PixelHeight;
			int bands = 3;
			byte[] jpgBinaryData = null;
			byte[][,] raster = new byte[bands][,];

			//Génération du raster de l'image
			for (int i = 0; i < bands; i++)
			{
				raster[i] = new byte[width, height];
			}

			for (int row = 0; row < height; row++)
			{
				for (int column = 0; column < width; column++)
				{
					int pixel = bitmap.Pixels[width * row + column];
					raster[0][column, row] = (byte)(pixel >> 16);
					raster[1][column, row] = (byte)(pixel >> 8);
					raster[2][column, row] = (byte)pixel;
				}
			}

			//Creation d'une image FJCore
			FluxJpeg.Core.ColorModel model = new FluxJpeg.Core.ColorModel
			{
				colorspace = FluxJpeg.Core.ColorSpace.RGB
			};

			FluxJpeg.Core.Image img = new FluxJpeg.Core.Image(model, raster);

			//encodage de l'image en JPG et compression de celle ci
			using (MemoryStream stream = new MemoryStream())
			{
				JpegEncoder encoder = new JpegEncoder(img, quality, stream);
				encoder.Encode();

				stream.Seek(0, SeekOrigin.Begin);
				jpgBinaryData = new Byte[stream.Length];
				stream.Read(jpgBinaryData, 0, (int)stream.Length);
			}

			//on renvoie les bytes de l'imgae compressée
			return jpgBinaryData;
		}

Voici le code source complet : 

using System;
using System.Windows.Controls;
using System.Windows.Media;
using System.IO;
using System.Windows.Media.Imaging;
using FluxJpeg.Core.Encoder;

namespace SilverlightThumbnails
{
	public class ImageHelpers
	{

		/// <summary>
		/// Compresse et réduit une image JPG
		/// </summary>
		/// <param name="stream">Stream de l'image à compresser</param>
		/// <param name="maxSize">Taille maximun de l'image compressée</param>
		/// <param name="quality">Qualité de compression JGP (0 - 100)</param>
		/// <returns>Tableau de byte contenant l'image compressée</returns>
		public static byte[] CompressImage(Stream stream, Int32 maxSize, Int32 quality)
		{
			BitmapImage imageSource = null;
			Int32 width = 1;
			Int32 height = 1;

			//Création d'un BitmapImage depuis le Stream
			imageSource = new BitmapImage();
			imageSource.CreateOptions = BitmapCreateOptions.DelayCreation;
			imageSource.SetSource(stream);

			//Calcul de la largeur et de la hauteur
			if (imageSource.PixelWidth > imageSource.PixelHeight)
			{
				width = imageSource.PixelWidth * maxSize / imageSource.PixelHeight;
				height = maxSize;
			}
			else
			{
				width = maxSize;
				height = imageSource.PixelHeight * maxSize / imageSource.PixelWidth;
			}

			//Création du WritableBitmap de sortir
			WriteableBitmap w = new WriteableBitmap(width, height);

			//image qui va nous servir générer le thumbnail
			Image i = new Image();
			i.Stretch = Stretch.Uniform;
			i.Source = imageSource;
			i.Height = height;
			i.Width = width;

			//on effectue un rendu de l'image dans le WritableBitmap ! et on obtient l'image miniature !
			w.Render(i, null);
			w.Invalidate();

			//on convertie le WritableBitmap
			return ConvertToBytes(w, quality);
		}

		/// <summary>
		/// Obtient les bytes correspondant à un WritableBitmap dans un format JPG
		/// </summary>
		/// <param name="bitmap">Le bitmap à traduire en bytes</param>
		/// <param name="quality">Qualité de compression JGP (0 - 100)</param>
		/// <returns>Tableau de byte contenant l'image compressée</returns>
		private static byte[] ConvertToBytes(WriteableBitmap bitmap, Int32 quality)
		{
			int width = bitmap.PixelWidth;
			int height = bitmap.PixelHeight;
			int bands = 3;
			byte[] jpgBinaryData = null;
			byte[][,] raster = new byte[bands][,];

			//Génération du raster de l'image
			for (int i = 0; i < bands; i++)
			{
				raster[i] = new byte[width, height];
			}

			for (int row = 0; row < height; row++)
			{
				for (int column = 0; column < width; column++)
				{
					int pixel = bitmap.Pixels[width * row + column];
					raster[0][column, row] = (byte)(pixel >> 16);
					raster[1][column, row] = (byte)(pixel >> 8);
					raster[2][column, row] = (byte)pixel;
				}
			}

			//Creation d'une image FJCore
			FluxJpeg.Core.ColorModel model = new FluxJpeg.Core.ColorModel
			{
				colorspace = FluxJpeg.Core.ColorSpace.RGB
			};

			FluxJpeg.Core.Image img = new FluxJpeg.Core.Image(model, raster);

			//encodage de l'image en JPG et compression de celle ci
			using (MemoryStream stream = new MemoryStream())
			{
				JpegEncoder encoder = new JpegEncoder(img, quality, stream);
				encoder.Encode();

				stream.Seek(0, SeekOrigin.Begin);
				jpgBinaryData = new Byte[stream.Length];
				stream.Read(jpgBinaryData, 0, (int)stream.Length);
			}

			//on renvoie les bytes de l'imgae compressée
			return jpgBinaryData;
		}

	}
}

Exemple d’utilisation

Code XAML

<UserControl x:Class="SilverlightThumbnails.MainPage"
			 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
			 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
	<Grid x:Name="LayoutRoot">
		<StackPanel HorizontalAlignment="Left"
					VerticalAlignment="Top">
			<Button x:Name="OpenButton"
					Content="Parcourir..."
					Click="OpenClick" />
			<Button x:Name="SaveButton"
					IsEnabled="False"
					Content="Sauvegarder..."
					Click="SaveClick" />
		</StackPanel>
	</Grid>
</UserControl>

Code behind


using System.Windows;
using System.Windows.Controls;
using System.IO;

namespace SilverlightThumbnails
{
	public partial class MainPage : UserControl
	{
		byte[] compressedImage = null;

		public MainPage()
		{
			InitializeComponent();
		}

		private void OpenClick(object sender, RoutedEventArgs e)
		{
			OpenFileDialog ofd = new OpenFileDialog();
			ofd.Filter = "Fichiers image|*.jpg;*.jpeg";
			ofd.ShowDialog();

			compressedImage = null;

			if (ofd.File != null)
			{
				using (Stream imageStream = ofd.File.OpenRead())
				{
					compressedImage = ImageHelpers.CompressImage(imageStream, 400, 90);
				}
			}

			if (compressedImage != null)
			{
				MessageBox.Show("Image compressée !");
				SaveButton.IsEnabled = true;
			}
		}

		private void SaveClick(object sender, RoutedEventArgs e)
		{
			if (compressedImage != null)
			{
				OpenButton.IsEnabled = false;

				SaveFileDialog sfd = new SaveFileDialog();
				sfd.Filter = "Fichiers image|*.jpg;*.jpeg";
				sfd.DefaultExt = ".jpg";

				sfd.ShowDialog();

				using (Stream fs = (Stream)sfd.OpenFile())
				{
					fs.Write(compressedImage, 0, compressedImage.Length);
				}

				SaveButton.IsEnabled = false;
				OpenButton.IsEnabled = true;

				MessageBox.Show("Image enregistrée !");
			}
		}
	}
}

Résultat

 Image originale  Image compressée  Code source complet du tutorial SilverlightThumbnails.zip 

 

Publicités
Catégories :Silverlight, Silverlight 3
  1. Aucun commentaire pour l’instant.
  1. No trackbacks yet.

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion /  Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion /  Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion /  Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion /  Changer )

w

Connexion à %s

%d blogueurs aiment cette page :