20 Acceso directo a la memoria de v�deo

La estructura bitmap es as�:

   typedef struct BITMAP
   {
      int w, h;               - tama�o del bitmap en pixels
      int clip;               - no-cero si recortar est� activado
      int cl, cr, ct, cb;     - l�mites de recorte izquierdo, derecho, superior
                                e inferior
      int seg;                - segmento para uso con los punteros a l�nea
      unsigned char *line[];  - punteros al comienzo de cada l�nea
   } BITMAP;
Tambi�n hay otras cosas en la estructura, pero podr�an cambiar, y no deber�a usar nada excepto lo de arriba. El rect�ngulo de recorte es inclusivo arriba a la izquierda (0 permite dibujar en la posici�n 0) pero exclusivo abajo a la derecha (10 permite dibujar en la posici�n 9, pero no en la 10). F�jese que �ste no es el mismo formato que el que se usa con set_clip(), el cual toma coordenadas inclusivas para las cuatro esquinas.

Hay varios modos de conseguir acceso directo a la memoria de imagen de un bitmap, variando en complejidad dependiendo del tipo de bitmap que use.


El modo m�s simple s�lo servir� si trabaja con bitmaps de memoria (obtenidos con create_bitmap(), ficheros de datos, y ficheros de im�genes) y sub-bitmaps de bitmaps de memoria. Esto usa una tabla de punteros char, llamados 'line', la cual es parte de la estructura bitmap y contiene punteros al comienzo de cada l�nea de la imagen. Por ejemplo, una funci�n putpixel simple para un bitmap de memoria es:

   void memory_putpixel(BITMAP *bmp, int x, int y, int color)
   {
      bmp->line[y][x] = color;
   }


Para modos truecolor, es necesario especificar el tipo del puntero de l�nea, por ejemplo:

   void memory_putpixel_15_or_16_bpp(BITMAP *bmp, int x, int y, int color)
   {
      ((short *)bmp->line[y])[x] = color;
   }

   void memory_putpixel_32(BITMAP *bmp, int x, int y, int color)
   {
      ((long *)bmp->line[y])[x] = color;
   }


Si quiere escribir en la pantalla y tambi�n en bitmaps de memoria, necesita usar algunas macros auxiliares, porque la memoria de v�deo puede no ser parte de su espacio de direcciones normal. Esta simple rutina funcionar� para cualquier pantalla lineal, p.ej unos framebuffers lineales de VESA.

   void linear_screen_putpixel(BITMAP *bmp, int x, int y, int color)
   {
      bmp_select(bmp);
      bmp_write8((unsigned long)bmp->line[y]+x, color);
   }
Para los modos truecolor deber�a reemplazar bmp_write8() por bmp_write16(), bmp_write24(), o bmp_write32(), y multiplicar el desplazamiento x por el n�mero de bytes por p�xel. Por supuesto hay funciones similares para leer el valor de un pixel de un bitmap, y son bmp_read8(), bmp_read16(), bmp_read24() y bmp_read32().


Esto, sin embargo, seguir� sin funcionar en los modos de SVGA por bancos, o en plataformas como Windows, que hacen un procesado especial dentro de las funciones de cambio de banco. Para un acceso m�s flexible a los bitmaps de memoria, necesita llamar a las rutinas:


unsigned long bmp_write_line(BITMAP *bmp, int line);
Selecciona la l�nea de un bitmap en la que va a dibujar.


unsigned long bmp_read_line(BITMAP *bmp, int line);
Selecciona la l�nea de un bitmap de la que va a leer.


unsigned long bmp_unwrite_line(BITMAP *bmp);
Libera el bitmap de memoria despued de que haya acabado de trabajar con �l. S�lo necesita llamar a esta funci�n una vez al final de una operaci�n de dibujado, incluso si ha llamado a bmp_write_line() o bmp_read_line() diversas beces antes.

Estas est�n implementadas como rutinas de ensamblador en l�nea, por lo que no son tan ineficientes como podr�an parecer. Si el bitmap no necesita cambio de banco (ejemplo: es un bitmap de memoria, pantalla en modo 13h, etc), estas funciones simplemente retornan bmp->line[line].

A pesar de que los bitmaps de SVGA usan bancos, Allegro permite acceso lineal a la memoria dentro de cada scanline, por lo que s�lo necesita pasar una coordenada y a estas funciones. Varias posiciones x pueden ser obtenidas simplemente sumando la coordenada x a la direcci�n devuelta. El valor devuelto es un entero largo sin signo en vez de un puntero a caracter porque la memoria bitmap puede no estar en su segmento de datos, y necesita acceder a �l con punteros far. Por ejemplo, una funci�n putpixel usando funciones de cambio de banco es:

   void banked_putpixel(BITMAP *bmp, int x, int y, int color)
   {
      unsigned long address = bmp_write_line(bmp, y);
      bmp_select(bmp);
      bmp_write8(address+x, color);
      bmp_unwrite_line(bmp);
   }
Se dar� cuenta de que Allegro tiene funciones separadas para seleccionar los bancos de lectura y escritura. Es importante que distinga entre estos, porque en algunas tarjetas de v�deo los bancos pueden ser seleccionados individualmente, y en otros la memoria de v�deo es le�da y escrita en direcciones diferentes. No obstante, la vida nunca es tan simple como desear�amos que fuese (esto es verdad incluso cuando _no_ estamos hablando de programaci�n de gr�ficos :-) y por supuesto algunas tarjetas de v�deo s�lo pueden proveer un banco. En �stas, las funciones de lectura/escritura se comportar�n id�nticamente, por lo que no deber�a asumir que puede leer de una parte de la memoria de v�deo y escribir en otra al mismo tiempo. Puede llamar bmp_read_line(), y leer lo que quiera de la l�nea, entonces llamar bmp_write_line() con el mismo o diferente valor de l�nea, y escribir lo que quiera en ella, pero no deber�a llamar bmp_read_line() y bmp_write_line() a la vez y esperar poder leer una l�nea y escribir en otra simult�neamente. Ser�a bueno si esto fuese posible, pero si lo hace, su c�digo no funcionar� en tarjetas SVGA de un banco.


Y tambi�n est� el modo-X. Si nunca antes hab�a programado gr�ficos en este modo, probablemente no entienda esto, pero para aquellos que quieren saber c�mo Allegro trabaja con los bitmaps de pantalla del modo-X, aqu� va...

Los punteros a l�nea todav�a est�n presentes, y contienen direcciones lineales, esto es, la direcci�n con la cual accedes al primer pixel de la l�nea. Est� garantizada la alineaci�n cada cuatro pixels de las direcciones, por lo que puede fijar el plano de escritura, dividir su coordenada entre cuatro, y a�adirla al puntero de l�nea. Por ejemplo, un putpixel en modo-X es:

   void modex_putpixel(BITMAP *b, int x, int y, int color)
   {
      outportw(0x3C4, (0x100<<(x&3))|2);
      bmp_select(bmp);
      bmp_write8((unsigned long)bmp->line[y]+(x>>2), color);
   }


Ah s�: el truco de djgpp del nearptr. Personalmente, no me gusta demasiado porque desactiva la protecci�n de la memoria y no es portable a otras plataformas, pero hay mucha gente que suspira por �l porque puede dar acceso directo a la memoria de pantalla via un puntero normal de C. �Aviso: Este m�todo s�lo funcionar� con la librer�a djgpp, cuando est� usando el modo VGA 13h o un framebuffer lineal!

En su c�digo de inicializaci�n:

   #include <sys/nearptr.h>

   unsigned char *screenmemory;
   unsigned long screen_base_addr;

   __djgpp_nearptr_enable();

   __dpmi_get_segment_base_address(screen->seg, &screen_base_addr);

   screenmemory = (unsigned char *)(screen_base_addr +
                                    screen->line[0] -
                                    __djgpp_base_address);
Luego:
   void nearptr_putpixel(int x, int y, int color)
   {
      screenmemory[x + y*SCREEN_W] = color;
   }



Volver al Indice