Herramientas de usuario

Herramientas del sitio


fw:dx:ddraw

DirectDraw

DirectDraw es un manejador de memoria de vídeo con el cual puedes manipular directamente, desde esta, imágenes y mostrarlas de forma eficiente por pantalla.

Inicialización y configuración

Comenzando

  • Para poder empezar a desarrollar con DirectDraw mediante C++ necesitaremos linkar la librería ddraw.lib e incluir en nuestro código la cabecera ddraw.h. Como vamos a trabajar con DirectDraw7, la última versión que se desarrolló, necesitaremos agregar también dxguid.lib.
  • El WinMain de la aplicación será el típico, podremos empezar a configurar el DDraw justo después de hacer las llamadas a ShowWindow y\o UpdateWindow.
  • Definiciones:
    • Superficie (Surface): Se refiere a zonas de memoria de vídeo, estas son las que contienen las imágenes que queremos mostrar en nuestra aplicación.
  • Tipos:
    • LPDIRECTDRAW7: Objeto DirectDraw, es un IDirectDraw. Contiene métodos útiles para:
      • Gestión del nivel cooperativo: SetCooperativeLevel o TestCooperativeLevel.
      • Gestión de modos de ventana: EnumDisplayModes, GetDisplayMode, GetMonitorFrequency, RestoreDisplayMode o SetDisplayMode.
      • Gestión de superficies: DuplicateSurface, EnumSurfaces, GetGDISurface, GetSurfaceFromDC o RestoreAllSurfaces.
      • Creación de objetos: CreateClipper, CreatePalette o CreateSurface.
    • LPDIRECTDRAWSURFACE7: Surface de DirectDraw.

Inicialización

Para configurar e iniciar la programación con DirectDraw tendremos que seguir los siguientes pasos, cada uno de ellos se basan en llamadas a funciones que devuelven una flag que si es distinta de DD_OK significa que ese paso ha fallado. DD_OK es del tipo HRESULT.

  1. Crear un objeto DirectDraw. Utilizaremos la función DirectDrawCreateEx pasándole 4 parámetros:
    • El GUID de la gráfica, esto es su identificador (si lo pasamos como NULL será el por defecto, aunque también podemos pasar las siguientes flags: DDCREATE_EMULATIONONLY, para modo emulado, y DDCREATE_HARDWAREONLY, sin emulación (DirectDraw no emula lo que no existe en hardware)).
    • Referencia al LPDIRECTDRAW7 (objeto DirectDraw) que crearemos, debe ser casteado como (void**).
    • IID_IDirectDraw7, que es un identificador de clase. :?:
    • Un valor NULL (realmente es para compativilidades futuras con objetos COM, pero no se utiliza).
  2. Configurar el modo coperativo. Esto determina cómo interactuaremos con el hardware. Para realizar esta configuración, el objeto DDraw contiene un método llamado SetCooperativeLevel al cual le pasas el HWND de la ventana y los flags de configuración, estos son:
    • DDSCL_EXCLUSIVE, que hace que únicamente DDraw se encargue de la gestión de gráficos, excluyendo GDI. Se ha de utilizar con DDSCL_FULLSCREEN.
    • DDSCL_FULLSCREEN, hace que la ventana se muestre a pantalla completa. Se ha de utilizar con DDSCL_EXCLUSIVE.
    • DDSCL_NORMAL, hace que DDraw se gestione en modo ventana.
    • DDSCL_ALLOWREBOOT, permite al usuario realizar un ctrl+alt+supr cuando ejecute tu aplicación.
  3. Configurar el modo de pantalla. Esto es indicar resolución y profundidad de color, si retorna DDERR_INVALIDMODE significa que la máquina que corre el programa no soporta este modo. Puedes utilizar el método del objeto DDraw EnumDisplayModes para ver los soportados.
#include <ddraw.h>
...
#define MSGBOX(body) MessageBox(NULL,body,"",MB_OK);
...
LPDIRECTDRAW7 lpdd;
...
if (DirectDrawCreateEx(NULL, (void**)&lpdd, IID_IDirectDraw7, NULL) != DD_OK) 
	MSGBOX("Falla la creación de DDraw!");
if (lpdd->SetCooperativeLevel (hwnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN) != DD_OK) 
	MSGBOX("Falla el cambio de modo a cooperativo!");
if (lpdd->SetDisplayMode (640, 480, 32, 0, 0) != DD_OK) 
	MSGBOX("Falla la asignación del modo de pantalla!");

Creando superficies

Existen dos tipos de superficies que una aplicación que implemente doble buffer con DDraw debe tener:

  • Superficie primaria: Es la superficie que el usuario vé.
  • Superficie no visible (backbuffer): Es la superficie que se utiliza para realizar del doble buffer, se dibuja sobre esta para luego intercambiarla por la primaria, mientras, esta no es visible.

Recuerda, una superficie es del tipo LPDIRECTDRAWSURFACE7.

LPDIRECTDRAWSURFACE7 primarySurface;
LPDIRECTDRAWSURFACE7 backSurface;

Una vez tengamos definidas los objetos correspondientes a las superficies de DDraw necesitaremos configurarlos:

  1. Definiremos un DDSURFACEDESC2 y lo configuraremos indicando su tamaño (dwSize), las flags de configuración en la propiedad dwFlags (DDSD_CAPS y DDSD_BACKBUFFERCOUNT indican que se usará backbuffer y para indicar las capacidades de la superficie una estructura DDSCAPS), si indicamos que utilizaremos un DDSCAPS también tendremos que indicar las flags para esta que es más de lo mismo para definir la superficie (recomendadas: DDSCAPS_PRIMARYSURFACE (indica que es la superfice primaria), DDSCAPS_FLIP (le dá la capacidad de flipping (intercambio de buffers)) y DDSCAPS_COMPLEX (superficie raíz)) y mediante la propiedad dwBackBufferCount indicamos que sólo tendrá un backbuffer, pero podemos poner más.
  2. Añadiremos el DDSURFACEDESC2 a la superficie principal mediante el método de esta CreateSurface.
  3. Definiremos un DDSCAPS2 para asignarselo a la superficie del BackBuffer. Lo configuraremos indicando en su propiedad dwCaps la flag DDSCAPS_BACKBUFFER.
  4. Asignaremos el anterior DDSCAPS2 a la superficie del BackBuffer y esta la asignaremos a la principal como tal mediante la función de la superficie primaria GetAttachedSurface.
DDSURFACEDESC2 ddsd;
ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX;
ddsd.dwBackBufferCount = 1;
 
if (lpdd->CreateSurface (&ddsd, &primarySurface, NULL) != DD_OK) 
	MSGBOX ("Falla la creación de la superficie principal");
 
DDSCAPS2 ddscaps;
ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
if (primarySurface->GetAttachedSurface(&ddscaps, &backSurface) != DD_OK) 
   MSGBOX ("Falla el enlace con el backbuffer");

* ZeroMemory limpia la memoria para DDSURFACEDESC2, de esa forma no tendremos problemas más tarde.

Cerrando DirectDraw

Una vez acabemos una aplicación de DDraw debemos dejarlo todo tal cual estaba, para ello seguiremos los siguientes pasos:

  1. Dejar el modo de pantalla como estaba antes llamando al método RestoreDisplayMode del objeto DirectDraw.
  2. Dejar el nivel cooperativo en normal volviendo a utilizar el método SetCooperativeLevel pero esta vez pasándole la flag DDSCL_NORMAL.
  3. Liberar la superficie primaria utilizando su método Release.
  4. Liberar las demás superficies utilizadas para cargar imágenes (método Release).
  5. Liberar el objeto DirectDraw también mediante su método Release.
lpdd->RestoreDisplayMode();
lpdd->SetCooperativeLevel(hwnd, DDSCL_NORMAL);
primarySurface->Release();
lpdd->Release();

Cuando se pierden las superficies...

Cuando se pierden las superficies… Hay que recuperarlas.
Esto ocurre, por ejemplo, cuando el usuario cambia de aplicación mediante un alt+tab o apretando a la tecla de windows. La aplicación pasa a segundo plano y al retornar GDI vuelve a quedarse con el dominio de la ventana, se han borrado las superficies! Cuando esto ocurre los métodos Flip o BltFast ya no retornan DD_OK sino DDERR_SURFACELOST. Aún así podemos recuperarlas fácilmente: únicamente se ha de llamar al método Restore de cada superficie (o si lo preferimos al RestoreAllSurfaces del objeto DDraw), pero al recuperarlas ya no tienen cargadas las imágenes, tendremos que volver a cargarlas de nuevo.

  • Cuando se vuelve a activar la ventana a la aplicación le llega el mensaje WM_ACTIVATEAPP.

Haciendo cosillas

Mostrar una imágen

Para DDraw una imágen también es una superficie, por lo que los pasos para mostrar una es cargarla y luego volcarla sobre un objeto LPDIRECTDRAWSURFACE. Algo fácil si seguimos los siguientes pasos:

  • Crearemos un objeto BITMAP desde la dirección de la imágen elegida. Las funciones para hacer esto son propias de GDI.
BITMAP bmp;
HBITMAP hBitmap = (HBITMAP)LoadImage(NULL, pathBMP, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION);
GetObject(hBitmap, sizeof(bmp), &bmp);
  • Iniciaremos la superficie donde se volcará el bitmap, primero configuramos su descripción (algo muy parecido a crear una superficie primaria pero teniendo como nuevo la indicación de que asignaremos el alto y el ancho (flags: DDSD_HEIGHT y DDSD_WIDTH)), también indicamos que es una superfie offscreen, esto es plana, esto es que no es una textura, un buffer… Sino un bitmap. Y como decimos que indicaremos el tamaño… pues indicamos también el tamaño mediante dwWidth y dwHeight.
LPDIRECTDRAWSURFACE7 bmpSurface;
DDSURFACEDESC2 ddsd;
ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
ddsd.dwWidth = bmp.bmWidth;
ddsd.dwHeight = bmp.bmHeight;
lpdd->CreateSurface(&ddsd, &bmpSurface, NULL);
  • Copiamos la imágen sobre la superficie, para ello utilizamos la función de GDI BitBlt.
HDC hdcImagen = CreateCompatibleDC(NULL);
HDC hdc; 
HBITMAP hBitmapAnt = (HBITMAP)SelectObject(hdcImagen, hBitmap); 
bmpSurface->GetDC(&hdc); 
BitBlt(hdc, 0, 0, bmp.bmWidth, bmp.bmHeight, hdcImagen, 0, 0, SRCCOPY); 
bmpSurface->ReleaseDC(hdc); 
SelectObject(hdcImagen, hBitmapAnt); 
DeleteDC(hdcImagen); 
DeleteObject(hBitmap);
  • Para volcar la superficie sobre el backbuffer debemos indicar el tamaño y posición de esta sobre el backbuffer. Para ello se utiliza la estructura RECTANGLE (también de GDI).
RECT rectOrig;
rectOrig.left = 0;
rectOrig.top = 0;
rectOrig.right = 640;
rectOrig.bottom = 480;
  • El paso final es el de volcar sobre el backbuffer la imágen y el backbuffer sobre la superficie principal. Para lo primero se utiliza el método BltFast de la superficie del backbuffer (pasándole la superficie del bmp y el rectángulo). Y para pasar del backbuffer a la principal (flipping) utilizamos el método Flip de esta.
backSurface->BltFast(0, 0, bmpSurface, &rectOrig, DDBLTFAST_WAIT);
primarySurface->Flip(backSurface, DDFLIP_WAIT);

Utilizar funciones de GDI

Una vez tenemos las superficies bien montaditas y configuraditas podemos utilizar las funciones de GDI para escribir sobre ellas, es decir, podemos escribir textos, dibujar y todas esas cosas tan monas que hacemos sobre GDI pero esta vez sobre las superficies de DDraw.

  • Tal vez queramos dibujar sobre el backbuffer y luego volcar este en la superficie principal, para ello se utiliza el método de la superficie principal Flip (sí, sí… como el saltamontes de la abeja Maya):
primarySurface->Flip(backSurface, DDFLIP_WAIT);
  • Para utilizar las funciones de GDI necesitamos tener el HDC, para ello una superficie tiene el método getHDC al cual le pasas un puntero de HDC y en él te vuelca el HDC deseado.
HDC hdc;
if (Flip(backSurface->GetDC(&hdc) == DD_OK) { 
    SetBkColor(hdc, RGB(0, 0, 255)); 
    SetTextColor(hdc, RGB(0, 0, 0)); 
    TextOut(hdc, 0, 0, "hola", lstrlen("hola")); 
    Flip(backSurface->ReleaseDC(hdc); 
}

Color Keying

El color keying es el concepto de indicar a DDraw que en toda una imágen un determinado color funcionará como transparente. De esa forma, cuando nosotros mostremos dicha imágen, las partes con ese color no se veran.
Para poder realizar el color keying es necesario un objeto del tipo DDCOLORKEY y dar a este su valor bajo (dwcolorSpaceLowvalue) y valor alto (dwcolorSpaceHighValue), que serán utilizados como segun lo asignemos a la superficie. Para ello, asignarlo, llamamos al método: SetColorKey pasandole los flags deseados y un puntero al DDCOLORKEY.

  • Flags de SetColorKey:
    • DDCKEY_COLORSPACE, indica que el objeto no contiene un solo color sino un rango.
    • DDCKEY_SRCBLT, indica que el color asignado será el utilizado como transparencia.

Por ejemplo, para asignar al verde una transparencia:

DDCOLORKEY ddck;
ddck.dwColorSpaceHighValue = 0x00FF00;
ddck.dwColorSpaceLowValue = 0x00FF00;
 
bmpSurface2->SetColorKey(DDCKEY_SRCBLT, &ddck);

No debemos olvidar ahora que al volcar la imágen sobre la superficie se ha de indicar que se tiene que tener en cuenta el color keying:

backSurface->BltFast(340, i, bmpSurface2, &rectOrig2, DDBLTFAST_WAIT | DDBLTFAST_SRCCOLORKEY);

Asignar el color key en la descripción de la superficie

Hacer esto no es tarea compleja, únicamente debemos añadir dos valores más a las propiedades del DDSURFACEDESC2, la ddckCKSrcBlt.dwColorSpaceLowValue y la ddckCKSrcBlt.dwColorSpaceHighValue correspondientes a los de un DDCOLORKEY. También tendremos que indicar mediante una flag más (DDSD_CKSRCBLT) que se tenga en cuenta este color key.

DDSURFACEDESC2 ddsd;
ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_CKSRCBLT;
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
ddsd.dwWidth = bmp.bmWidth;
ddsd.dwHeight = bmp.bmHeight;
ddsd.ddckCKSrcBlt.dwColorSpaceLowValue  = 0x00ff00;
ddsd.ddckCKSrcBlt.dwColorSpaceHighValue  = 0x00ff00;

Blitting

El blitter es la parte de la gráfica que puede manipular los datos de las imágenes, para acceder a él mediante DirectDraw se utilizan los método Blt y BltFast de una superficie. Con el primero, Blt, puedes realizar más operaciones que con BltFast pero también es más lento, veamos qué parámetros necesita:

  • Un RECT correspondiente a la región de la superficie donde se realizarán las operaciones (destino), si a este parámetro le asignas NULL la regió será toda la superficie.
  • Una superficie, el orígen de los datos (por si vas a copiar bits o lo que sea); si no necesitas ninguna porque, por ejemplo, vas a rellenar la superficie destino (en la que llamas a Blt) puedes poner un NULL.
  • Otro RECT, este indica la región de la superficie de donde se cogerán los datos (orígen).
  • Flags que indican cómo tratar la estructura DDBLTFX del siguiente parámetro. Estos pueden ser:
    • DDBLT_COLORFILL, que indica que se rellenará de un color dado en la propiedad dwFillColor del DDBLTFX.
    • DDBLT_DDFX para utilizar la propiedad dwDDFX de la DDBLFFX para indicar los efectos que se tendrán en cuenta.
    • DDBLT_DDROPS y\o DDBLT_ROP para utilizar la dwDDROPS para operaciones de raster.
    • DDBLT_KEYDEST para usar el color key de la superficie destino.
    • DDBLT_KEYSRC para utilizar el color key de la superficie orígen.
    • DDBLT_WAIT la función espera hasta que el blitter esté disponible en vez de dar un error.
    • DDBLT_ROTATIONANGLE tiene en cuenta la propiedad dwRotationAngle para indicar el ángulo de rotación.
  • El DDBLTFX es una estructura para pasar parámetros al Blt, sus propiedades más curiosas (:!: Hay problemas con la rotación):
    • dwSize: Tamaño (dado mediante el sizeof) de la estructura.
    • dwDDFX: Flags para aplicar efectos de blitting
      • Invertir: DDBLTFX_MIRRORUPDOWN o DDBLTFX_MIRRORLEFTRIGHT.
      • Rotar: DDBLTFX_ROTATE180, DDBLTFX_ROTATE270 o DDBLTFX_ROTATE90.
    • dwRotationAngle, dwROP
    • dwFillColor para rellenar de un color sólido.
    • ddckDestColorKey y ddckSrcColorKey para asignar color keys. Estos son del tipo DDCOLORKEY.
  • Por ejemplo, si queremos rellenar una suficie de azul:
DDBLTFX l;
ZeroMemory(&l, sizeof(l));
l.dwSize = sizeof(l);
l.dwFillColor = 0x0000ff;
backSurface->Blt(NULL, NULL, NULL, DDBLT_COLORFILL, &l);
  • Si queremos hacer una copia de una superficie:
backSurface->Blt(NULL, bmpSurface, NULL, DDBLT_WAIT, NULL);
  • Dibujar en la posición 50, 50 de backSurface y hasta la 100, 100 los píxels (0,0) a (100,100) de bmpSurface (aquí se hace un escalado de la imágen).
RECT rectOrig2, rectOrig3;
 
rectOrig2.left = 50;
rectOrig2.top = 50;
rectOrig2.right = 100;
rectOrig2.bottom = 100;
 
rectOrig3.left = 0;
rectOrig3.top = 0;
rectOrig3.right = 100;
rectOrig3.bottom = 100;
 
backSurface->Blt(&rectOrig2, bmpSurface, &rectOrig3, DDBLT_WAIT, NULL);
  • Pasándole un DDBLTFX
DDBLTFX l;
ZeroMemory (&l, sizeof(l));
l.dwSize = sizeof(l);
 
RECT rectOrig2;
rectOrig2.left = 0;
rectOrig2.top = 0;
rectOrig2.right = 100;
rectOrig2.bottom = 100;
 
backSurface->Blt(&rectOrig2, bmpSurface2, NULL, DDBLT_WAIT, &l);
  • Invertir en horizontal la imágen:
// Basándonos en el código anterior
l.dwDDFX = DDBLTFX_MIRRORLEFTRIGHT;
backSurface->Blt(&rectOrig2, bmpSurface2, NULL, DDBLT_WAIT | DDBLT_DDFX, &l);
  • Indicar un color key:
// Basándonos en el código anterior
DDCOLORKEY ddck;
ddck.dwColorSpaceHighValue = 0x00FF00;
ddck.dwColorSpaceLowValue = 0x00FF00;
l.ddckSrcColorkey = ddck; 
backSurface->Blt(&rectOrig2, bmpSurface2, NULL, DDBLT_WAIT | DDBLT_KEYSRC, &l);

El BltFast sirve también para copiar de una superficie a otra pero lo hace de forma más rápida que el Blt y sin aplicar efectos. Sus parámetros son los siguientes:

  • Dos integers, para indicar la posición xy de dónde se copiará la imágen (sin escalado).
  • La superficie orígen.
  • El rectángulo que indica la porción de la superficie orígen a copiar.
  • El conjunto deseado de las siguientes flags: DDBLTFAST_WAIT, DDBLTFAST_SRCCOLORKEY (utilizar color key del orígen), DDBLTFAST_DESTCOLORKEY (utilizar color key del destino) y DDBLTFAST_NOCOLORKEY (sin color key).

Crear una animación

El truco para crear una animación de sprites está en el rectángulo que indica la zona de la superficie que se dibujará. Cuando tenemos una superficie con un imágen cargada tenemos que indicar al método BltFast de la superficie donde queramos cargarla un rectángulo y la posición donde se dibujará. La gracia es que el rectángulo vaya moviendose por una gran imágen donde esten los frames de la animación del sprite pero dibujándose siempre en el mismo sitio.
Por ejemplo, imaginemos una imágen que contiene 4 dibujos de una célula, estos corresponden a una rotación donde llega hacer una vuelta completa en estos 4 dibujos\frames. La imágen es de 400×100 (donde cada célula ocupa 100×100). Modificaremos el rectángulo de la siguiente forma:

RECT rectOrig;
int y = 0;
...
rectOrig.left = y;
rectOrig.top = 0;
rectOrig.right = y + 100;
rectOrig.bottom = 100;

Y cada vez que movamos la célula haremos:

y = (y == 300) ? 0 : y + 100;

Pero siempre la dibujaremos en el mismo sitio:

backSurface->BltFast(0, 0, bmpSurface, &rectOrig, DDBLTFAST_WAIT | DDBLTFAST_SRCCOLORKEY);

Clipping

Esto es hacer que en una superficie sólo se muestre una porción, sería algo así como aplicar una máscara a esta. Para poder realizar esto necesitamos un objeto LPDIRECTDRAWCLIPPER que se crea mediante el método CreateClipper de un objeto LPDIRECTDRAW7, a este hay que pasarle 3 argumentos, pero únicamente el 2º es importante, que es un puntero a un objeto LPDIRECTDRAWCLIPPER (el primero se pasará como 0 y el tercero como NULL).

LPDIRECTDRAWCLIPPER clipper;
lpdd->CreateClipper(0, &clipper, NULL);

Una vez tengas el objeto necesitarás asignarle una lista de regiones visibles, esto se hace mediante la estructura RGNDATA, esta contiene dos elementos: Buffer (donde se guardarán los RECTs utilizados para indicar las regiones) y una estructura RGNDATAHEADER que indica cómo se debe tratar ese Buffer. Reservaremos el tamaño del buffer con malloc (y no nos olvidaremos de liberar esa porción de memoria, verdad?) ya que es un array y una vez que asignemos las regiones deberemos hacerlo de arriba abajo.
La RGNDATAHEADER contiene las siguientes propiedades útiles:

  • dwSize, el tamaño.
  • iType, el tipo de regiones, utilizaremos: RDH_RECTANGLES.
  • nCount, int, número de regiones.
  • nRgnSize, tamaño de las regiones, como utilizaremos RECTs le pasaremos algo parecido a esto: sizeof(RECT) * nCount.
  • rcBound, la superficie donde se aplicará el clipping.
LPRGNDATA lpClipList = (LPRGNDATA)malloc(sizeof(RGNDATAHEADER) + sizeof(RECT));
RECT rcClipRect = {300,300,640,480};
memcpy(lpClipList->Buffer, &rcClipRect, sizeof(RECT));
lpClipList->rdh.dwSize = sizeof(RGNDATAHEADER);
lpClipList->rdh.iType = RDH_RECTANGLES;
lpClipList->rdh.nCount = 1;
lpClipList->rdh.nRgnSize = sizeof(RECT);
lpClipList->rdh.rcBound = rcClipRect;

Una vez tengamos la ClipList tendremos que asignarla al clipper mediante el método de este SetClipList. Y para finalizar asignar el clipper a la superficie deseada con el método de la superficie SetClipper.

clipper->SetClipList(lpClipList, 0));
backSurface->SetClipper(clipper));
free(lpClipList);

:!: No funciona del todo bien

Notas

  • Puedes realizar operaciones de blitter sin tener que utilizar Blt o BltFast, para ello existen funciones de más bajo nivel y con las que tendrás acceso absoluto a los datos de una superficie. Estos son: Lock y Unlock (para bloquearla) y lPitch y lpSurface (del DDSURFACEDESC2) para acceder a ellas.
  • En vez de mirar si una función de DDraw devuelve DD_OK puedes utilizar la macro FAILED, que también comprueba el retorno:
if (FAILED(clipper->SetClipList(lpClipList, 0))) ...
  • Cuando quieras que la aplicación sea en vez de a pantalla completa en modo ventana tendrás que tener en cuenta unas cuantas cosas:
    • Es aconsejable utilizar clipping.
    • No puedes utilizar flipping con el backbuffer, si quieres implementar doble buffer tendrás que emularlo mediante otra superficie separada en modo DDSCAPS_OFFSCREENPLAIN y haciendo blitting de esta a la principal.
    • No hay que cambiar el modo de pantalla.
    • El nivel cooperativo pasa a ser únicamente DDSCL_NORMAL.

Documentos

fw/dx/ddraw.txt · Última modificación: 2020/05/09 09:25 (editor externo)