Modificar los pixeles en un BitmapImage utilizando WriteableBitmap | C# | WinRT

Avanzado

Otros artículos de manipulación de imágenes en WinRT

Continuando con la serie dedicada a los fundamentos de manipulación de imágenes en WinRT y dando continuidad al anterior artículo, en esta oportunidad les contare como hacer lo mismo que con BitmapEncoder pero con menos de la mitad de esfuerzo y complejidad, estoy hablando de utilizar un objeto WriteableBitmap el cual internamente hace todo lo requerido.

Por practicidad y teniendo en consideración a los lectores nuevos de la serie, el inicio de este post es identico al del articulo anterior, diferenciandose solo desde la segunda parte.

En un post anterior vimos como acceder directamente a los pixeles de una imagen, esto en resumen es así:

 var localFolder = Package.Current.InstalledLocation;  
var folder = await localFolder.GetFolderAsync("Assets");  
var imgfile = await folder.GetFileAsync("conejo.bmp");

var filestream = await imgfile.OpenReadAsync();  
var decoder = await BitmapDecoder.CreateAsync(filestream);  
var pxDataProvider = await decoder.GetPixelDataAsync();  
byte[] pxData = pxDataProvider.DetachPixelData();  

Para una explicación detallada revisar el post anterior.

Esto nos permite acceder a la información de pixeles en forma de un array de bytes. Una vez modificada la información del array de bytes básicamente tenemos un nuevo Bitmap, así que hay que asignar este byte[] a un objeto tipo BitmapImage.

A diferencia del post anterior de la serie, en esta oportundad no haré uso de BitmapEncoder sino de una clase que ahorra mucho trabajo WriteableBitmap.

Para hacer uso de WriteableBitmap basta con conocer el tamaño de la imagen a generar.

 WriteableBitmap wbmp = new WriteableBitmap((int)decoder.PixelWidth, (int)decoder.PixelHeight);

Ahora lo importante es escribir el arreglo de bytes, esto lo hacemos accediendo a la propiedad PixelBuffer la cual es de tipo IBuffer la mala noticia es que esta interfaz no expone absolutamente nada útil para llevar a cabo nuestra tarea.

Desde luego siemrpe hay algo que hacer, agregando el siguiente namespace tenemos acceso a una serie de métodos de extensión que valga decir son unos salvavidas de primera:

 using System.Runtime.InteropServices.WindowsRuntime;

Puntualmente haremos uso del método de extensión AsStream el cual como ya habran deducido, convierte el buffer en un stream en el que , por supuesto, podemos escribir arrays de bytes:

 var tgtstream = wbmp.PixelBuffer.AsStream();
        await tgtstream.WriteAsync(pxData, 0, pxData.Length);

Para finalmente asignar el WriteableBitmap como ImageSource del control.

 imgBitmap.Source = wbmp;

Imagen cargada

Este es el código completo con un pequeño improvement:

 var localFolder = Package.Current.InstalledLocation;

//var imgfile = await (await localFolder.GetFolderAsync("Assets")).GetFileAsync("animal.jpg");
var imgfile = await (await localFolder.GetFolderAsync("Assets")).GetFileAsync("conejo.bmp");

var filestream = await imgfile.OpenReadAsync();  
var decoder = await BitmapDecoder.CreateAsync(filestream);  
var pxDataProvider = await decoder.GetPixelDataAsync();  
byte[] pxData = pxDataProvider.DetachPixelData();

//hacer algo con los pixeles
for (int i = 0; i < pxData.Length; i += 4)  
{
}

WriteableBitmap wbmp = new WriteableBitmap((int)decoder.PixelWidth, (int)decoder.PixelHeight);

using (var tgtstream = wbmp.PixelBuffer.AsStream())  
{
    await tgtstream.WriteAsync(pxData, 0, pxData.Length);
    await tgtstream.FlushAsync(); 
}

imgBitmap.Source = wbmp;