Ir al índice de .NET Cómo... en .NET
 

Trocear una imagen (2)

Indicando dinámicamente en cuantos trozos (columnas y filas)

 
Publicado el 18/Ene/2007
Actualizado el 18/Ene/2007
Autor: Guillermo 'guille' Som

En este artículo te muestro cómo trocear una imagen en otras más pequeñas, de forma que todas juntas forman la imagen original, útil para, por ejemplo hacer un puzzle de imágenes o crear mapas, etc. En esta ocasión, el número de filas y columnas las puedes indicar en tiempo de ejecución. Como de costumbre, con código tanto para Visual Basic como para C# (válido para .NET 1.1 y superior).


Introducción:

Pues eso, siguiendo con el mismo tema que en la primera parte, en esta ocasión te muestro cómo trocear una imagen en los trozos que indiques durante la ejecución del programa, de esa forma, puedes indicar cuantas columnas y cuantas filas quieres usar, y también le he puesto para que se adapte al tamaño de la ventana, de forma que se vea más grande si así lo quieres. Además de poner una una opción para que se pueda ver automáticamente el nuevo tamaño sin necesidad de volver a pulsar en el botón trocear.

Esta es la captura de la aplicación en tiempo de ejecución:

Figura 1. La aplicación en tiempo de ejecución
Figura 1. La aplicación en tiempo de ejecución

 

Como puedes comprobar, puedes usar la imagen que quieras, y si te gusta la que incluye el programa, que es la que está en la captura de la primera parte, simplemente borra el contenido de la caja de textos con la imagen a trocear y al pulsar la tecla Intro se mostrará nuevamente la imagen original.

 

No me voy a enrollar en esta ocasión ya que en el otro artículo prácticamente te lo expliqué todo paso a paso. Solo decirte que utilizo valores de configuración del usuario para almacenar las columnas, el fichero y esas cosillas, ni que decir tiene que no me he complicado la vida y en esta ocasión he usado cosas que solo tiene la versión 2005 de los dos lenguajes, aunque si quitas la parte de configuración, el código te valdrá lo mismo para Visual Studio 2003.

Nota:
Si quieres usar el código mostrado en este artículo en Visual Studio 2003 debes quitar todo lo que hace referencia a My.Settings y My.Resouces en Visual Basic y Properties.Settings y Properties.Resources en C#.

Sí, ya se que este es diferente y que seguramente necesitaría que te lo "desglosara", pero... mejor que mires los comentarios y así vas habituándote a entender el código... ;-)))

Sí, ya se que no es una excusa, pero... je, je... ¿a que lo parecía?

Pues eso... ahí abajo tienes el código de Visual Basic y el de C#, para que te entretengas... y si no quieres entretenerte, también te pongo un fichero .rar para que te bajes los proyectos para Visual Basic 2005 y Visual C# 2005, es decir, que los puedes usar con las versiones Express de Visual Studio 2005 o con las versiones "normales".

 

Para un próximo artículo, (si no se te olvida, que ya nos vamos conociendo) le añadiré un botón para poder guardar las imágenes generadas... pero mientras tanto, puedes ir probándolo por tu cuenta. Como pista te diré que el objeto Image tiene una propiedad que te facilitará la tarea.

 

 

¡Que lo trocees dinámicamente bien!

Nos vemos.
Guillermo

P.S.
El código incluido en el ZIP tiene algunas cosillas que no muestro aquí, por ejemplo, una barra de progreso (aunque no consigo que llegue al 100%, je, je) y también un Timer para que le de tiempo al formulario a mostrarse, ya que si has indicado muchos trozos tardará en mostrarse, porque al iniciarse, primero trocea la imagen. Ese mismo temporizador lo uso para poner a cero la barra de progreso.

 


Código para Visual Basic.NET (VB.NET)El código para Visual Basic .NET (casi cualquier versión)

'------------------------------------------------------------------------------
' Trocear una imagen                                                (17/Ene/07)
' Las imágenes de destino pueden cambiar de tamaño
'
' ©Guillermo 'guille' Som, 2007
'------------------------------------------------------------------------------
Option Strict On

Imports Microsoft.VisualBasic
Imports vb = Microsoft.VisualBasic
Imports System
Imports System.Windows.Forms
Imports System.Drawing

Public Class fTamañoDinamico

    Private ficImagen As String
    ' El número de dimensiones, por ejemplo 3x3 = 9 trozos
    Private nColumnas As Integer = 3
    Private nFilas As Integer = 3
    ' Para que no se produzca el evento Resize mientras se inicia
    Private iniciando As Boolean = True
    ' Constante con el número máximo de filas y columnas
    Private Const maxTrozos As Integer = 50

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) _
                Handles MyBase.Load
        ' Asignar los valores de la configuración
        ficImagen = My.Settings.Fichero
        nColumnas = My.Settings.Columnas
        nFilas = My.Settings.Filas
        Me.txtColumnas.Text = nColumnas.ToString
        Me.txtFilas.Text = nFilas.ToString
        Me.chkAjustarAuto.Checked = My.Settings.AjustarAuto
        Me.txtImagen.Text = ficImagen
        ' Si no existe la imagen, dejamos la mía
        If System.IO.File.Exists(ficImagen) Then
            Me.picMain.Image = Image.FromFile(ficImagen)
        Else
            Me.picMain.Image = My.Resources.Guille28sep06_1003_240x308
        End If
        ' Mostrar los trozos
        trocearImagen(nColumnas, nFilas)
        iniciando = False
    End Sub

    Private Sub btnExaminar_Click(ByVal sender As Object, ByVal e As EventArgs) _
                Handles btnExaminar.Click
        Dim oFD As New OpenFileDialog
        oFD.Title = "Seleccionar imagen"
        oFD.Filter = "Imágenes|*.jpg;*.gif;*.png;*.bmp|Todos (*.*)|*.*"
        oFD.FileName = Me.txtImagen.Text
        If oFD.ShowDialog = Windows.Forms.DialogResult.OK Then
            Me.txtImagen.Text = oFD.FileName
            Me.picMain.Image = Image.FromFile(Me.txtImagen.Text)
            My.Settings.Fichero = Me.txtImagen.Text
        End If
    End Sub

    Private Sub chkAjustarAuto_CheckedChanged(ByVal sender As Object, ByVal e As EventArgs) _
                Handles chkAjustarAuto.CheckedChanged
        My.Settings.AjustarAuto = chkAjustarAuto.Checked
    End Sub

    Private Sub txtImagen_KeyPress(ByVal sender As Object, ByVal e As KeyPressEventArgs) _
                Handles txtImagen.KeyPress
        If e.KeyChar = ChrW(13) Then
            ficImagen = Me.txtImagen.Text
            My.Settings.Fichero = ""
            If String.IsNullOrEmpty(ficImagen) Then
                Me.picMain.Image = My.Resources.Guille28sep06_1003_240x308
            End If
        End If
    End Sub

    Private Sub btnTrocear_Click(ByVal sender As System.Object, ByVal e As EventArgs) _
                Handles btnTrocear.Click
        ' Trocear una imagen en trozos más pequeños
        ' Asignar el tamño según las columnas y filas indicadas
        ' Comprobar que son datos correctos
        Dim novalido As Boolean = False
        If vb.IsNumeric(txtColumnas.Text) = False Then
            novalido = True
        Else
            nColumnas = CInt(txtColumnas.Text)
            If nColumnas < 1 OrElse nColumnas > maxTrozos Then
                novalido = True
            End If
        End If
        If novalido Then
            MessageBox.Show("Debes indicar un número de 1 a " & maxTrozos & " en las columnas")
            txtColumnas.Focus()
            Exit Sub
        End If
        If vb.IsNumeric(txtFilas.Text) = False Then
            novalido = True
        Else
            nFilas = CInt(txtFilas.Text)
            If nFilas < 1 OrElse nFilas > maxTrozos Then
                novalido = True
            End If
        End If
        If novalido Then
            MessageBox.Show("Debes indicar un número de 1 a " & maxTrozos & " en las filas")
            txtFilas.Focus()
            Exit Sub
        End If
        ' Guardar los valores en la configuración
        My.Settings.Columnas = nColumnas
        My.Settings.Filas = nFilas
        trocearImagen(nColumnas, nFilas)
    End Sub

    Private Sub gbImagenes_Resize(ByVal sender As Object, ByVal e As EventArgs) _
                Handles gbImagenes.Resize
        If iniciando Then Exit Sub
        ' El número de filas y de columnas será
        ' el usado la última vez al pulsar en trocear
        If Me.chkAjustarAuto.Checked Then
            trocearImagen(nColumnas, nFilas)
        End If
    End Sub

    Private Sub trocearImagen(ByVal columnas As Integer, ByVal filas As Integer)
        ' Separación entre la imagen y el borde del contenedor
        Const sepX As Integer = 6
        Const sepY As Integer = 16
        ' La separación entre cada trozo
        Const sep As Integer = 2
        ' El tamaño de cada imagen será:
        ' Ancho del groupBox \ columnas - columnas
        ' Alto del groupBox \ filas - filas
        ' Al usar columnas y filas, se separa más de la cuenta o menos de lo deseado
        ' Pero dejando 2 para el ancho y (filas\2) para el alto parece que está bien
        Dim tamW As Integer = (Me.gbImagenes.ClientSize.Width - sepX) \ columnas - 2
        Dim tamH As Integer = (Me.gbImagenes.ClientSize.Height - sepY) \ filas - 2
        ' El tamaño proporcional del ancho y alto
        ' correspondientes a los trozos a usar
        Dim tamTrozoW As Integer = Me.picMain.Image.Width \ columnas
        Dim tamTrozoH As Integer = Me.picMain.Image.Height \ filas
        ' El rectángulo de cada nuevo trozo
        Dim rectDest As New Rectangle(0, 0, tamW, tamH)
        ' Estas variables se usan en el bucle
        Dim bmpDest As Bitmap
        Dim g As Graphics
        Dim rectOri As Rectangle
        '
        ' Array con el número de pictures necesarias
        'Dim trozos(0 To (columnas * filas - 1)) As PictureBox
        Dim trozos(columnas * filas - 1) As PictureBox
        ' Eliminamos las imágenes que tuviera el groupBox
        Me.gbImagenes.Controls.Clear()
        ' Para contar cada columna y fila
        Dim c, f As Integer
        ' La posición X e Y en la imagen original
        Dim pX, pY As Integer
        For i As Integer = 0 To trozos.Length - 1
            ' El trozo de la imagen original
            rectOri = New Rectangle(pX, pY, tamTrozoW, tamTrozoH)
            ' La imagen de destino
            bmpDest = New Bitmap(tamW, tamH)
            g = Graphics.FromImage(bmpDest)
            ' Obtenemos un trozo de la imagen original
            ' y lo dibujamos en la imagen de destino
            g.DrawImage(Me.picMain.Image, rectDest, rectOri, GraphicsUnit.Pixel)
            '
            ' Asignamos la nueva imagen al picture correspondiente
            ' creamos el picture y lo asignamos al groupBox
            trozos(i) = New PictureBox
            Me.gbImagenes.Controls.Add(trozos(i))
            ' Asignamos el tamaño
            trozos(i).Size = New Size(tamW, tamH)
            ' Posicionamos el picture
            trozos(i).Location = New Point(tamW * c + (sep * c) + sepX, _
                                           tamH * f + (sep * f) + sepY)
            ' Asignar la imagen creada
            trozos(i).Image = bmpDest
            '
            ' Calculamos la posición del próximo trozo
            c += 1
            pX += tamTrozoW
            ' Cuando hayamos recorrido las columnas,
            ' pasamos a la siguiente fila
            If c >= columnas Then
                c = 0
                f += 1
                pX = 0
                pY += tamTrozoH
            End If
        Next
    End Sub
End Class

 


Código para C Sharp (C#)El código para C# (casi cualquier versión)

//-----------------------------------------------------------------------------
// Trocear una imagen                                               (17/Ene/07)
// Las imágenes de destino pueden cambiar de tamaño
//
// ©Guillermo 'guille' Som, 2007
//-----------------------------------------------------------------------------
using System;
using System.Windows.Forms;
using System.Drawing;

namespace trocearImagen_cs
{

public partial class fTamañoDinamico : Form
{
    public fTamañoDinamico()
    {
        InitializeComponent();
    }

    private string ficImagen;
    // El número de dimensiones, por ejemplo 3x3 = 9 trozos
    private int nColumnas = 3;
    private int nFilas = 3;
    // Para que no se produzca el evento Resize mientras se inicia
    private bool iniciando = true;
    // Constante con el número máximo de filas y columnas
    const int maxTrozos = 50;

    Properties.Settings mySettings = new trocearImagen_cs.Properties.Settings();

    private void fTamañoDinamico_Load(object sender, EventArgs e)
    {
        // Asignar los valores de la configuración
        ficImagen = mySettings.Fichero;
        nColumnas = mySettings.Columnas;
        nFilas = mySettings.Filas;
        this.txtColumnas.Text = nColumnas.ToString();
        this.txtFilas.Text = nFilas.ToString();
        this.chkAjustarAuto.Checked = mySettings.AjustarAuto;
        this.txtImagen.Text = ficImagen;
        // Si no existe la imagen, dejamos la mía
        if (System.IO.File.Exists(ficImagen))
        {
            this.picMain.Image = Image.FromFile(ficImagen);
        }
        else
        {
            this.picMain.Image = Properties.Resources.Guille28sep06_1003_240x308;
        }
        // Mostrar los trozos
        trocearImagen(nColumnas, nFilas);
        iniciando = false;
    }
    
    private void fTamañoDinamico_FormClosing(object sender, FormClosingEventArgs e)
    {
        // C# no guarda automáticamente los cambios    
        mySettings.Save();
    }

    private void btnExaminar_Click(object sender, EventArgs e)
    {
        OpenFileDialog oFD = new OpenFileDialog();
        oFD.Title = "Seleccionar imagen";
        oFD.Filter = "Imágenes|*.jpg;*.gif;*.png;*.bmp|Todos (*.*)|*.*";
        oFD.FileName = this.txtImagen.Text;
        if (oFD.ShowDialog() == DialogResult.OK)
        {
            this.txtImagen.Text = oFD.FileName;
            this.picMain.Image = Image.FromFile(this.txtImagen.Text);
            mySettings.Fichero = this.txtImagen.Text;
        }
    }

    private void chkAjustarAuto_CheckedChanged(object sender, EventArgs e)
    {
        mySettings.AjustarAuto = chkAjustarAuto.Checked;
    }

    private void txtImagen_KeyPress(object sender, KeyPressEventArgs e)
    {
        if (e.KeyChar == (char)13)
        {
            ficImagen = this.txtImagen.Text;
            mySettings.Fichero = "";
            if (String.IsNullOrEmpty(ficImagen))
            {
                this.picMain.Image = Properties.Resources.Guille28sep06_1003_240x308;
            }
        }
    }

    private void btnTrocear_Click(System.Object sender, EventArgs e)
    {
        // Trocear una imagen en trozos más pequeños
        // Asignar el tamño según las columnas y filas indicadas
        // Comprobar que son datos correctos
        bool novalido = false;
        try
        {
            nColumnas = Convert.ToInt32(txtColumnas.Text);
            if (nColumnas < 1 || nColumnas > maxTrozos)
            {
                novalido = true;
            }
        }
        catch 
        {
            novalido = true;
        }
        if (novalido)
        {
            MessageBox.Show("Debes indicar un número entero de 1 a " + 
                            maxTrozos + " en las columnas");
            txtColumnas.Focus();
            return;
        }
        try
        {
            nFilas = Convert.ToInt32(txtFilas.Text);
            if (nFilas < 1 || nFilas > maxTrozos)
            {
                novalido = true;
            }
        }
        catch
        {
            novalido = true;
        }
        if (novalido)
        {
            MessageBox.Show("Debes indicar un número entero de 1 a " + 
                            maxTrozos + " en las filas");
            txtFilas.Focus();
            return;
        }
        // Guardar los valores en la configuración
        mySettings.Columnas = nColumnas;
        mySettings.Filas = nFilas;
        trocearImagen(nColumnas, nFilas);
    }

    private void gbImagenes_Resize(object sender, EventArgs e)
    {
        if (iniciando) return;
        // El número de filas y de columnas será
        // el usado la última vez al pulsar en trocear
        if (this.chkAjustarAuto.Checked)
        {
            trocearImagen(nColumnas, nFilas);
        }
    }

    private void trocearImagen(int columnas, int filas)
    {
        // Separación entre la imagen y el borde del contenedor
        const int sepX = 6;
        const int sepY = 16;
        // La separación entre cada trozo
        const int sep = 2;
        // El tamaño de cada imagen será:
        // Ancho del groupBox \ columnas - columnas
        // Alto del groupBox \ filas - filas
        // Al usar columnas y filas, se separa más de la cuenta o menos de lo deseado
        // Pero dejando 2 para el ancho y (filas\2) para el alto parece que está bien
        int tamW = (this.gbImagenes.ClientSize.Width - sepX) / columnas - 2;
        int tamH = (this.gbImagenes.ClientSize.Height - sepY) / filas - 2;
        // El tamaño proporcional del ancho y alto
        // correspondientes a los trozos a usar
        int tamTrozoW = this.picMain.Image.Width / columnas;
        int tamTrozoH = this.picMain.Image.Height / filas;
        // El rectángulo de cada nuevo trozo
        Rectangle rectDest = new Rectangle(0, 0, tamW, tamH);
        // Estas variables se usan en el bucle
        Bitmap bmpDest;
        Graphics g;
        Rectangle rectOri;
        //
        // Array con el número de pictures necesarias
        PictureBox[] trozos = new PictureBox[columnas * filas];
        // Eliminamos las imágenes que tuviera el groupBox
        this.gbImagenes.Controls.Clear();
        // Para contar cada columna y fila
        int c = 0, f = 0;
        // La posición X e Y en la imagen original
        int pX = 0, pY = 0;
        for (int i = 0; i < trozos.Length; i++)
        {
            // El trozo de la imagen original
            rectOri = new Rectangle(pX, pY, tamTrozoW, tamTrozoH);
            // La imagen de destino
            bmpDest = new Bitmap(tamW, tamH);
            g = Graphics.FromImage(bmpDest);
            // Obtenemos un trozo de la imagen original
            // y lo dibujamos en la imagen de destino
            g.DrawImage(this.picMain.Image, rectDest, rectOri, GraphicsUnit.Pixel);
            //
            // Asignamos la nueva imagen al picture correspondiente
            // creamos el picture y lo asignamos al groupBox
            trozos[i] = new PictureBox();
            this.gbImagenes.Controls.Add(trozos[i]);
            // Asignamos el tamaño
            trozos[i].Size = new Size(tamW, tamH);
            // Posicionamos el picture
            trozos[i].Location = new Point(tamW * c + (sep * c) + sepX, 
                                           tamH * f + (sep * f) + sepY);
            // Asignar la imagen creada
            trozos[i].Image = bmpDest;
            //
            // Calculamos la posición del próximo trozo
            c += 1;
            pX += tamTrozoW;
            // Cuando hayamos recorrido las columnas,
            // pasamos a la siguiente fila
            if (c >= columnas)
            {
                c = 0;
                f += 1;
                pX = 0;
                pY += tamTrozoH;
            }
        }
    }
}


}

 


Espacios de nombres usados en el código de este artículo:

System.Windows.Forms
System.Drawing
 

Compatibilidad:

Este código funciona con cualquier versión de .NET salvo por lo indicado de las configuraciones, particularmente en Visual Basic.
Windows Vista ready! ;-)))

 


Código de ejemplo (comprimido):

 

Fichero con el código de ejemplo: trocear_imagen_src.rar - 56.40 KB

Contiene los proyectos de VB y C#, sin binarios, solo el código.

(MD5 checksum: 476E6200DDAD54D35CF16B9DC9D187E5)

 


Ir al índice principal de el Guille

Valid XHTML 1.0 Transitional ¡CSS Válido!