Tutorial 25: Simple Bitmap

En este tutorial, aprenderemos como usar bitmaps en nuestros programas. Para ser exactos, Aprenderemos a desplegar in bitmap en el área cliente de una ventana. Baja el ejemplo.

Teoría

Los bitmaps pueden ser vistos como pinturas almacenadas en la computadora. Hay muchos formatos de pinturas usados con los computadores pero Windows sólo soporta como nativos los archivos Gráficos de Bitmap de Windows (.bmp). Los bitmaps a que me referiré en este tutorial son archivos gráficos de Windows. La manera más fácil de usar un bitmap es emplearlo como recurso. Hay dos maneras de hacer eso. Puedes incluir el bitmap en el archivo de definición del recurso (.rc) de la siguiente manera:
 

#define IDB_MYBITMAP   100
IDB_MYBITMAP  BITMAP  "c:\project\example.bmp"

Este método usa una constante para representar el bitmap. La primera línea crea una constante llamada IDB_MYBITMAP que tiene el valor de 100. Usaremos esta etiqueta para referirnos al bitmap en el programa. La siguiente línea declara un recurso bitmap. Dice al compilador de recursos dónde encontrar el actual archivo bmp.

El otro método usa un nombre para representar el bitmap de la siguiente manera:

MyBitMap  BITMAP "c:\project\example.bmp"

Este método requiere que te refieras al bitmap en tu programa usando la cadena "MyBitMap" en vez de un valor.

Cualquiera de estos métodos trabaja bien siempre que sepas cuál estás usando.

Ahora que ponemos el bitmap en el archivo de recurso, podemos continuar con los pasos a seguir para desplegarlo en el área cliente de nuestra ventana.

  1. Llamar a LoadBitmap para obtener el manejador [handle] al bitmap. LoadBitmap tiene la siguiente definición:

      LoadBitmap proto hInstance:HINSTANCE, lpBitmapName:LPSTR


    Esta función regresa un manejador al bitmap. hInstance es el manejador de instancia de nuestro programa. lpBitmapName es un puntero a la cadena con el nombre del bitmap (en caso de que uses el segundo método para referirte al bitmap). Si usas una constante para referirte al bitmap (como IDB_MYBITMAP), puedes poner este valor aquí. (En el ejemplo de arriba sería 100). Un pequeño ejemplo, en orden:

       
      Primer Método:

      .386
      .model flat, stdcall
      ................
      .const
      IDB_MYBITMAP    equ 100
      ...............
      .data?
      hInstance  dd ?
      ..............
      .code
      .............
          invoke GetModuleHandle,NULL
          mov hInstance,eax
      ............
          invoke LoadBitmap,hInstance,IDB_MYBITMAP
      ...........

      Segundo Método:

      .386
      .model flat, stdcall
      ................
      .data
      BitmapName  db "MyBitMap",0
      ...............
      .data?
      hInstance  dd ?
      ..............
      .code
      .............
          invoke GetModuleHandle,NULL
          mov hInstance,eax
      ............
          invoke LoadBitmap,hInstance,addr BitmapName
      ...........

  1. Obtener un manejador al contexto del dispositivo (DC). Puedes obtener este manejador llamando a BeginPaint en respuesta al mensaje WM_PAINT o llamando a GetDC en algún lado.
  2. Crear un contexto de dispositivo de memoria que tenga el mismo atributo que el contexto de dispositivo que obtuvimos. La idea aquí es crear un tipo de superficie oculta para dibujo [hidden drawing surface] sobre la cual podamos dibujar el bitmap. Cuando terminamos con al operación, copiamos el contenido de la hidden drawing surface al contexto de dispositivo actual en una llamada a función. Es un ejemplo de técnica de doble-buffer usada para desplegar con rapidez imágenes sobre la pantalla. Puedes crear esta superficie oculta para dibujo [hidden drawing surface] llamando a CreateCompatibleDC.

      CreateCompatibleDC  proto  hdc:HDC


    Si esta función tiene éxito, regresa el manejador del contexto de dispositivo de memoria en eax. hdc es el manejador al contexto de dispositivo con que quieres que el DC de memoria sea compatible.

  1. Ahora que obtuviste una superficie oculta para dibujo, puedes dibujar sobre ella seleccionando el bitmap dentro de ella. Esto se hace llamando a SelectObject con el manejador al DC de memoria como primer parámetro y el manejador al bitmap como segundo parámetro. SelectObject tiene la siguiente definición:

      SelectObject   proto  hdc:HDC, hGdiObject:DWORD

  1. El bitmap ahora es dibujado sobre el contexto de dispositivo de memoria. Todo lo que necesitamos hacer aquí es copiarlo al dispositivo de despliegue actual, realnmente el verdadero contexto de dispositivo. Hay varias funciones que pueden realizar esta operación tales como BitBlt y StretchBlt. BitBlt copia el contenido de un DC a otro de manera que es más rápido mientras que StretchBlt pueda estrechra o comprimir el bitmap para fijar el área de salida. Usaremos aquí BitBlt por simplicidad. BitBlt tiene la siguiente definición:

      BitBlt  proto  hdcDest:DWORD, nxDest:DWORD, nyDest:DWORD, nWidth:DWORD, nHeight:DWORD, hdcSrc:DWORD, nxSrc:DWORD, nySrc:DWORD, dwROP:DWORD
       

    hdcDest es el manejador del contexto de dispositivo que sirve como destino de la operación de transferencia del bitmap
    nxDest, nyDest son las coordenadas de la esquina superior izquierda del área de salida
    nWidth, nHeight son el ancho y la altura del área de salida
    hdcSrc es el manejador del contexto de dispositivo que sirve como fuente de la operación de transferencia del bitmap
    nxSrc, nySrc son las coordenadas de la esquina superior izquierda del rectángulo de origen.
    dwROP es el código de la operación-raster (de ahí las siglas ROP) que gobierna cómo combinar los datos de los colores del bitmap con los datos de los colores existentes en el área de salida para alcanzar el resultado final. Muchas veces, sólo quieres sobre-escribir los datos de colores existentes con los nuevos.

  1. Cuando ya hayas hecho lo que ibas a hacer con el bitmap, suprímelo con una llamada a la API DeleteObject.

¡Eso es todo! Para recapitular, necesitas poner el bitmap dentro del guión de recursos. Luego cárgalo desde el recurso con LoadBitmap. Obtendrás un manejador al bitmap. Luego obtienes el manejador al contexto de dispositivo del área sobre el cual quieres pintar el bitmap. Luego creas un contexto de dispositivo de memoria compatible con el contexto de dispositivo que obtuviste. Selecciona el bitmap dentro del DC de memoria y luego copia el contenido de la memoria del DC al verdadero DC.

Código de Ejemplo:

.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\gdi32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\gdi32.lib

WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
IDB_MAIN   equ 1

.data
ClassName db "SimpleWin32ASMBitmapClass",0
AppName  db "Win32ASM Simple Bitmap Example",0

.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hBitmap dd ?

.code
start:
 invoke GetModuleHandle, NULL
 mov    hInstance,eax
 invoke GetCommandLine
 mov    CommandLine,eax
 invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
 invoke ExitProcess,eax

WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
 LOCAL wc:WNDCLASSEX
 LOCAL msg:MSG
 LOCAL hwnd:HWND
 mov   wc.cbSize,SIZEOF WNDCLASSEX
 mov   wc.style, CS_HREDRAW or CS_VREDRAW
 mov   wc.lpfnWndProc, OFFSET WndProc
 mov   wc.cbClsExtra,NULL
 mov   wc.cbWndExtra,NULL
 push  hInstance
 pop   wc.hInstance
 mov   wc.hbrBackground,COLOR_WINDOW+1
 mov   wc.lpszMenuName,NULL
 mov   wc.lpszClassName,OFFSET ClassName
 invoke LoadIcon,NULL,IDI_APPLICATION
 mov   wc.hIcon,eax
 mov   wc.hIconSm,eax
 invoke LoadCursor,NULL,IDC_ARROW
 mov   wc.hCursor,eax
 invoke RegisterClassEx, addr wc
 INVOKE CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
           WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
           CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\
           hInst,NULL
 mov   hwnd,eax
 invoke ShowWindow, hwnd,SW_SHOWNORMAL
 invoke UpdateWindow, hwnd
 .while TRUE
  invoke GetMessage, ADDR msg,NULL,0,0
  .break .if (!eax)
  invoke TranslateMessage, ADDR msg
  invoke DispatchMessage, ADDR msg
 .endw
 mov     eax,msg.wParam
 ret
WinMain endp

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
   LOCAL ps:PAINTSTRUCT
   LOCAL hdc:HDC
   LOCAL hMemDC:HDC
   LOCAL rect:RECT
   .if uMsg==WM_CREATE
      invoke LoadBitmap,hInstance,IDB_MAIN
      mov hBitmap,eax
   .elseif uMsg==WM_PAINT
      invoke BeginPaint,hWnd,addr ps
      mov    hdc,eax
      invoke CreateCompatibleDC,hdc
      mov    hMemDC,eax
      invoke SelectObject,hMemDC,hBitmap
      invoke GetClientRect,hWnd,addr rect
      invoke BitBlt,hdc,0,0,rect.right,rect.bottom,hMemDC,0,0,SRCCOPY
      invoke DeleteDC,hMemDC
      invoke EndPaint,hWnd,addr ps
 .elseif uMsg==WM_DESTROY
  invoke DeleteObject,hBitmap
  invoke PostQuitMessage,NULL
 .ELSE
  invoke DefWindowProc,hWnd,uMsg,wParam,lParam
  ret
 .ENDIF
 xor eax,eax
 ret
WndProc endp
end start

;---------------------------------------------------------------------
;                            El guión de recursos
;---------------------------------------------------------------------
#define IDB_MAIN 1
IDB_MAIN BITMAP "tweety78.bmp"

Análisis:

No hay mucho que analizar en este tutorial ;)
 

#define IDB_MAIN 1
IDB_MAIN BITMAP "tweety78.bmp"

Definir una constante llamada IDB_MAIN, asignando 1 como su valor. Y luego usar esa constante como el identificador del recurso del bitmap. El archivo bitmap a ser incluido en el recuros "tweety78.bmp" que reside en la misma carpeta como guión de recursos.

   .if uMsg==WM_CREATE
      invoke LoadBitmap,hInstance,IDB_MAIN
      mov hBitmap,eax

En respuesta a WM_CREATE, llamamos a LoadBitmap para cargar al bitmap desde el recurso, pasando el identificador del recurso del bitmap como segundo parámetro a la API. Obtenemos el manejador al bitmap cuando regresa la función.

Ahora que el bitmap está cargado, podemos pintarlo en el área cliente de nuestra ventana principal.

   .elseif uMsg==WM_PAINT
      invoke BeginPaint,hWnd,addr ps
      mov    hdc,eax
      invoke CreateCompatibleDC,hdc
      mov    hMemDC,eax
      invoke SelectObject,hMemDC,hBitmap
      invoke GetClientRect,hWnd,addr rect
      invoke BitBlt,hdc,0,0,rect.right,rect.bottom,hMemDC,0,0,SRCCOPY
      invoke DeleteDC,hMemDC
      invoke EndPaint,hWnd,addr ps

Elegimos pintar el bitmap en respuesta al mensaje WM_PAINT. Primero llamamos a BeginPaint para obtener el manejador al contexto de dispositivo. Luego creamos un DC de memoria compatible con CreateCompatibleDC. Lo siguiente es seleccionar el bitmap dentro del DC de memoria con SelectObject. Determinamos la dimensión del área cliente con GetClientRect. Ahora podemos desplegar el bitmap en el área cliente llamando a BitBlt que copia el bitmap desde el DC de memoria al DC real. Cuando la imagen ya esté hecha, ya no tenemos necesidad del DC de memoria, así que lo suprimimos con DeleteDC. Terminamos la sección de pintura con EndPaint.

 .elseif uMsg==WM_DESTROY
  invoke DeleteObject,hBitmap
  invoke PostQuitMessage,NULL

Cuando no necesitemos ya el bitmap, lo suprimimos [delete] con DeleteObject


Index

Next

[Iczelion's Win32 Assembly Homepage]

n u M I T_o r's   Programming Page

Este tutorial, original de Iczelion, ha sido traducido por:   n u M I T_o r

www.000webhost.com