lunes, 5 de marzo de 2012

Manipulación Y Manejo De Pixeles De Bitmap

Hola amigos! En esta ocasión aprenderemos algo muy útil y que nos hará aprender mucho. En nuestra aplicación que realizaremos en esta ocasión aprenderemos como manipular los pixeles de una imágen bitmap, aprenderemos a tomar los pixeles de una imágen personal que cargaremos en el programa y ponerla de cabeza, acostada, cambiar todas las tonalidades por un solo color, y también aprenderemos como hacer zoom a un área en específico de la imágen utilizando la función CopyRect.

Bueno pues comenzamos agregando los componentes que necesitamos en la Forma, los cuales listamos a continuación:

-1 ComboBox
-2 Image (Del mismo tamaño)
-1 Label
-7 Botones
-1 OpenPictureDialog

La Forma debe quedar de la siguiente manera, en mi caso con la imágen del dragón ya cargada en el Image1:


Ahora configuraremos varios de los componentes. El primero de ellos es el Image1, en su propiedad Picture cargamos la imágen de tipo bitmap que deseemos, en mi caso cargo la imágen de un dragón (yo he descargado varias y les recomiendo hacer lo mismo para tener variedad de imágenes y seleccionar otra cuando queramos). Luego en el ComboBox1 en su propiedad Items agregamos los siguientes:

     *AND
     *OR
     *XOR
     *NOT

Ahora vamos al archivo H y en la parte de Private declaramos las siguientes variables:

private: // User declarations
        int color, left, top, right, bottom;

Y en la parte Public declaramos los métodos Dibujar() y Zoom() que utilizaremos más delante:

public: // User declarations
        __fastcall TForm1(TComponent* Owner);
        void TForm1::Dibujar();
        void TForm1::Zoom();

Bien pues ahora comenzaremos con la codificación. Primero en el evento de creación del TForm1() agregamos la siguiente línea de código para indicar que al correr la aplicación estará seleccionada la primera opción en el ComboBox:

__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
        ComboBox1->ItemIndex = 0;
}
//---------------------------------------------------------------------------

Antes de todo abrimos el evento OnCreate de la Forma y escribimos el código siguiente que nos ayudará a reducir el parpadeo:

void __fastcall TForm1::FormCreate(TObject *Sender)
{
        Form1->Refresh();
        this->DoubleBuffered=true;
}
//---------------------------------------------------------------------------

Después, primero definimos los dos métodos que habíamos declarado anteriormente ya que los utilizaremos más adelante. Primero el método Dibujar():

void TForm1::Dibujar()
{
        Refresh();
        Form1->Canvas->DrawFocusRect(Rect(left+Image1->Left,top+Image1->Top,right+Image1->Left,bottom+Image1->Top));
}
//---------------------------------------------------------------------------

Después el método Zoom():

void TForm1::Zoom()
{
        Graphics::TBitmap *Bitmap;
        TRect rect1, rect2;
        rect1 = Rect(left,top,right,bottom);
        rect2 = Rect(430,50,750,390);
        Bitmap=Image1->Picture->Bitmap;
        Form1->Canvas->CopyRect(rect2, Bitmap->Canvas, rect1);
}
//---------------------------------------------------------------------------

Ahora escribiremos el botón que dice rojo, el cual dependiendo de la opción que esté seleccionada en el ComboBox realizará una operación de "&", "|", "!" ó "^" con el número hexadecimal "0x0000ff" para cambiar todos los colores del bitmap por tonalidades de color rojo:

void __fastcall TForm1::Button1Click(TObject *Sender)
{
        switch(ComboBox1->ItemIndex)
        {
                case 0:
                for(int x=0; x<Image1->Width; x++)
                        for(int y=0; y<Image1->Height; y++)
                        {
                                color=Image1->Canvas->Pixels[x][y];
                                Image2->Canvas->Pixels[x][y] = (TColor)color&0x0000ff;
                        }
                break;
                case 1:
                for(int x=0; x<Image1->Width; x++)
                        for(int y=0; y<Image1->Height; y++)
                        {
                                color=Image1->Canvas->Pixels[x][y];
                                Image2->Canvas->Pixels[x][y] = (TColor)color|0x0000ff;
                        }
                break;
                case 2:
                for(int x=0; x<Image1->Width; x++)
                        for(int y=0; y<Image1->Height; y++)
                        {
                                color=Image1->Canvas->Pixels[x][y];
                                Image2->Canvas->Pixels[x][y] = (TColor)color^0x0000ff;
                        }
                break;
                case 3:
                for(int x=0; x<Image1->Width; x++)
                        for(int y=0; y<Image1->Height; y++)
                        {
                                color=Image1->Canvas->Pixels[x][y];
                                Image2->Canvas->Pixels[x][y] = ~(TColor)color;
                        }
                break;
        }
}
//---------------------------------------------------------------------------

Ahora damos doble clic en el botón con la leyenda "Verde" el cual realizará una operación con el numero hexadecimal "0x00ff00" dependiendo de la opción seleccionada en el ComboBox para cambiar todos los colores del bitmap por tonalidades verdes, escribimos el siguiente código:

void __fastcall TForm1::Button2Click(TObject *Sender)
{
        switch(ComboBox1->ItemIndex)
        {
                case 0:
                for(int x=0; x<Image1->Width; x++)
                        for(int y=0; y<Image1->Height; y++)
                        {
                                color=Image1->Canvas->Pixels[x][y];
                                Image2->Canvas->Pixels[x][y] = (TColor)color&0x00ff00;//(TColor)color&255<<8;
                        }
                break;
                case 1:
                for(int x=0; x<Image1->Width; x++)
                        for(int y=0; y<Image1->Height; y++)
                        {
                                color=Image1->Canvas->Pixels[x][y];
                                Image2->Canvas->Pixels[x][y] = (TColor)color|0x00ff00;
                        }
                break;
                case 2:
                for(int x=0; x<Image1->Width; x++)
                        for(int y=0; y<Image1->Height; y++)
                        {
                                color=Image1->Canvas->Pixels[x][y];
                                Image2->Canvas->Pixels[x][y] = (TColor)color^0x00ff00;
                        }
                break;
                case 3:
                for(int x=0; x<Image1->Width; x++)
                        for(int y=0; y<Image1->Height; y++)
                        {
                                color=Image1->Canvas->Pixels[x][y];
                                Image2->Canvas->Pixels[x][y] = ~(TColor)color;
                        }
                break;
        }
}
//---------------------------------------------------------------------------

Ahora damos doble clic en el botón que dice "Azul", el cual hará exactamente lo mismo que los anteriores con la diferencia de que éste cambiará todos los colores por tonalidades azules, el código es el siguiente:

void __fastcall TForm1::Button3Click(TObject *Sender)
{
        switch(ComboBox1->ItemIndex)
        {
                case 0:
                for(int x=0; x<Image1->Width; x++)
                        for(int y=0; y<Image1->Height; y++)
                        {
                                color=Image1->Canvas->Pixels[x][y];
                                Image2->Canvas->Pixels[x][y] = (TColor)color&0xff0000;//(TColor)color&255<<16;
                        }
                break;
                case 1:
                for(int x=0; x<Image1->Width; x++)
                        for(int y=0; y<Image1->Height; y++)
                        {
                                color=Image1->Canvas->Pixels[x][y];
                                Image2->Canvas->Pixels[x][y] = (TColor)color|0xff0000;
                        }
                break;
                case 2:
                for(int x=0; x<Image1->Width; x++)
                        for(int y=0; y<Image1->Height; y++)
                        {
                                color=Image1->Canvas->Pixels[x][y];
                                Image2->Canvas->Pixels[x][y] = (TColor)color^0xff0000;
                        }
                break;
                case 3:
                for(int x=0; x<Image1->Width; x++)
                        for(int y=0; y<Image1->Height; y++)
                        {
                                color=Image1->Canvas->Pixels[x][y];
                                Image2->Canvas->Pixels[x][y] = ~(TColor)color;
                        }
                break;
        }
}
//---------------------------------------------------------------------------

Ahora en el botón "CopyBrush" escribimos el código para ejemplificar el funcionamiento de las funciones CopyBrush y CopyRect, el código lo tenemos a continuación:

void __fastcall TForm1::Button13Click(TObject *Sender)
{
        Form1->Refresh();
        Graphics::TBitmap *Bitmap;
        TRect rect1, rect2;
        rect1 = Rect(10,10,100,100);
        rect2 = Rect(450,111,550,201);//(10,111,100,201);
        Bitmap=Image1->Picture->Bitmap;
        Form1->Canvas->BrushCopy(rect2, Bitmap, rect1, (TColor)color);
        Form1->Canvas->CopyRect(rect2, Bitmap->Canvas, rect1);
}
//---------------------------------------------------------------------------

Enseguida manipularemos los pixeles de la imágen bitmap para invertirla, es decir, la dibujaremos de cabeza, el código se muestra a continuación:

void __fastcall TForm1::Button14Click(TObject *Sender)
{
        for(int x=0, x2=Image2->Width; x<Image1->Width; x++, x2--)
                for(int y=0, y2=Image2->Height; y<Image1->Height; y++, y2--)
                        Image2->Canvas->Pixels[x2][y2] = Image1->Canvas->Pixels[x][y];
}
//---------------------------------------------------------------------------

Luego lo que haremos será colocar la imágen de lado, es decir tomaremos los pixeles y dibujaremos la imágen acostada con el siguiente código:

void __fastcall TForm1::Button15Click(TObject *Sender)
{
        TRect Rect(0,0,Image2->Width,Image2->Height);
        Image2->Canvas->FillRect(Rect);
        for(int x=0, x2=Image2->Width; x<Image1->Width; x++, x2--)
                for(int y=0, y2=Image2->Height; y<Image1->Height; y++, y2--)
                        Image2->Canvas->Pixels[y2][x2] = Image1->Canvas->Pixels[x][y];
}
//---------------------------------------------------------------------------

Ahora trabajaremos para realizar el zoom a un área de la imágen, para esto utilizaremos 3 eventos. El primero de los eventos que necesitamos es el evento OnMouseDown de el Image1, en el escribimos el siguiente código:

void __fastcall TForm1::Image1MouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
        if(Shift.Contains(ssLeft))
        {
                left = X;
                top = Y;
        }
}
//---------------------------------------------------------------------------

Ahora abrimos el evento OnMouseMove de el Image1 y escribimos el código que tenemos a continuación:

void __fastcall TForm1::Image1MouseMove(TObject *Sender, TShiftState Shift,
      int X, int Y)
{
        if(Shift.Contains(ssLeft))
                Image1MouseUp(Sender, mbLeft, Shift, X, Y);
}
//---------------------------------------------------------------------------

El último de los eventos que necesitamos es el OnMouseUp de el Image1, en el cual tecleamos el siguiente código:

void __fastcall TForm1::Image1MouseUp(TObject *Sender, TMouseButton Button,
      TShiftState Shift, int X, int Y)
{
        if(Button==mbLeft)
        {
                right = X;
                bottom = Y;
                Dibujar();
                Zoom();
        }
}
//---------------------------------------------------------------------------

Con esto ya podemos dibujar un rectángulo sobre el área sobre la cual queremos realizar el zoom y esta aparecerá con el zoom realizado en la parte derecha de la Forma.

Ahora realizaremos otra tarea aprovechando el evento OnMouseDown del Image1, pero esta vez con la opción del botón clic derecho. Lo que haremos será copiar la imágen del Image1 en el Image2 pero quitando los pixeles del color al que le hayamos dado clic en la imágen original, el código es el siguiente (está debajo del código utilizado para el zoom):

void __fastcall TForm1::Image1MouseDown(TObject *Sender,
      TMouseButton Button, TShiftState Shift, int X, int Y)
{
        if(Shift.Contains(ssLeft))
        {
                left = X;
                top = Y;
        }
       if(Shift.Contains(ssRight))
        {
                Refresh();
                TColor color = Image1->Canvas->Pixels[X][Y];
                Graphics::TBitmap *Bitmap;
                TRect rect1, rect2;
                rect1 = Rect(0,0,Image1->Width,Image1->Height);
                rect2 = Rect(Image2->Left,Image2->Top,Image2->Left+Image2->Width,Image2->Height+Image2->Top);
                Bitmap=Image1->Picture->Bitmap;
                Form1->Canvas->BrushCopy(rect2, Bitmap, rect1, (TColor)color);
        }
}
//---------------------------------------------------------------------------

Finalmente codificaremos el botón "Cargar Bitmap" para tener la opción de elegir otro bitmap que tengamos en nuestra computadora y poder realizar las mismas acciones que codificamos anteriormente, el código lo tenemos enseguida:

void __fastcall TForm1::Button4Click(TObject *Sender)
{
        //Podemos seleccionar un bitmap para cargarlo en la aplicación
        Form1->Refresh();
        String name;
        if(OpenPictureDialog1->Execute())
                name = OpenPictureDialog1->FileName;
        Image1->Picture->LoadFromFile(name);
}
//---------------------------------------------------------------------------

Y eso es todo chavos, compilamos nuestra aplicación y la corremos para poder probar todas las acciones codificadas, deberíamos tener algo como lo siguiente:


Bueno chavos pues es todo por mi parte, creo que hoy aprendimos muchas cosas nuevas diferentes a las que ya sabíamos anteriormente, yo los espero en la próxima para continuar con más aplicaciones gráficas en C++ Builder. Nos vemos amigos!.