?? gfx.cpp
字號:
//------------------------------------------------------------------------------
// Name: Gfx.cpp
// Desc: Holds all the graphics related functions. This includes drawing
// scanlines, directx stuff, etc.
//------------------------------------------------------------------------------
#include "Gfx.h"
#include "Palette.h"
#include "AttributeTable.h"
// Global array of colors.
BYTE abyColors[NUMBER_OF_COLORS][4];
// DirectDraw Surfaces used for the graphics engine.
LPDIRECTDRAW7 lpDD;
LPDIRECTDRAWSURFACE7 lpddsPrimary;
LPDIRECTDRAWSURFACE7 lpddsBack;
LPDIRECTDRAWSURFACE7 lpddsScreen;
LPDIRECTDRAWCLIPPER lpClipper;
RECT rcScreen;
BYTE* pSurfaceMemory; // Pointer to our locked memory to draw our pixels on.
LONG lBytesPerPixel; // Number of bytes per pixel.
LONG FourMinusBPP; // 4 - Number of bytes per pixel (to speed up putpixel loop).
LONG lSurfacePitch; // The pitch of the scanline use to move to the next line.
//------------------------------------------------------------------------------
// Name: CreateDirectDraw()
// Desc: Creates DirectDraw and all the surfaces and sets up the colors
// array with the paletted nes colors.
//------------------------------------------------------------------------------
HRESULT CreateDirectDraw(HWND hwnd)
{
DDSURFACEDESC2 ddsd;
DDPIXELFORMAT DDpf; // DirectDraw pixel format structure.
HRESULT ddrval;
RECT rcClient;
// Create the main DirectDraw object.
ddrval = DirectDrawCreateEx(NULL, (VOID**)&lpDD, IID_IDirectDraw7, NULL);
if(ddrval != DD_OK)
return FALSE;
// Using DDSCL_NORMAL means we will coexist with GDI.
ddrval = lpDD->SetCooperativeLevel(hwnd, DDSCL_NORMAL);
if(ddrval != DD_OK)
{
lpDD->Release();
return FALSE;
}
memset(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
// The primary surface is not a page flipping surface.
ddrval = lpDD->CreateSurface(&ddsd, &lpddsPrimary, NULL);
if (ddrval != DD_OK)
{
lpDD->Release();
return FALSE;
}
// Create a clipper to ensure that our drawing stays inside our window.
ddrval = lpDD->CreateClipper(0, &lpClipper, NULL);
if (ddrval != DD_OK)
{
lpddsPrimary->Release();
lpDD->Release();
return FALSE;
}
// Setting it to our hwnd gives the clipper the coordinates from our window.
ddrval = lpClipper->SetHWnd(0, hwnd);
if (ddrval != DD_OK)
{
lpClipper-> Release();
lpddsPrimary->Release();
lpDD->Release();
return FALSE;
}
// Attach the clipper to the primary surface.
ddrval = lpddsPrimary->SetClipper(lpClipper);
if (ddrval != DD_OK)
{
lpClipper-> Release();
lpddsPrimary->Release();
lpDD->Release();
return FALSE;
}
GetClientRect(hwnd, &rcClient);
memset(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
ddsd.dwWidth = rcClient.right - rcClient.left;
ddsd.dwHeight = rcClient.bottom - rcClient.top;
// Create the backbuffer separately.
ddrval = lpDD->CreateSurface(&ddsd, &lpddsBack, NULL);
if (ddrval != DD_OK)
{
lpClipper-> Release();
lpddsPrimary->Release();
lpDD->Release();
return FALSE;
}
ddsd.dwWidth = SCREEN_WIDTH;
ddsd.dwHeight = SCREEN_HEIGHT;
// Create the offscreen surface for drawing our screen to.
ddrval = lpDD->CreateSurface(&ddsd, &lpddsScreen, NULL);
if (ddrval != DD_OK)
{
lpClipper-> Release();
lpddsPrimary->Release();
lpddsBack->Release();
lpDD->Release();
return FALSE;
}
// Fill the DDpf structure and get the number of bytes per pixel.
ZeroMemory(&DDpf, sizeof(DDpf));
DDpf.dwSize = sizeof(DDpf);
lpddsScreen->GetPixelFormat(&DDpf);
// Save the number of bytes per pixel and 4 - the number of bytes
// per pixel to speed things up when drawing pixels.
lBytesPerPixel = DDpf.dwRGBBitCount / 8;
FourMinusBPP = 4 - lBytesPerPixel;
// Compile all the pixels.
for (int i = 0; i < NUMBER_OF_COLORS; i++)
CompilePixel(lpddsScreen, i, abyNESPalette[(3*i)], abyNESPalette[(3*i)+1], abyNESPalette[(3*i)+2]);
return TRUE;
} // end CreateDirectDraw()
//------------------------------------------------------------------------------
// Name: DestroyDirectDraw()
// Desc: Cleans up everything that was initialized for DirectDdraw.
//------------------------------------------------------------------------------
HRESULT DestroyDirectDraw()
{
if(lpDD != NULL)
{
if (lpddsPrimary != NULL)
{
lpddsPrimary->Release();
lpddsPrimary = NULL;
}
if (lpddsBack != NULL)
{
lpddsBack->Release();
lpddsBack = NULL;
}
if (lpddsScreen != NULL)
{
lpddsScreen->Release();
lpddsScreen = NULL;
}
if (lpClipper != NULL)
{
lpClipper->Release();
lpClipper = NULL;
}
lpDD->Release();
lpDD = NULL;
}
return DD_OK;
} // end DestroyDirectDraw()
//------------------------------------------------------------------------------
// Name: Flip()
// Desc: Blts the nes screen surface to the back surface and then flips
// the back surface and the primary if in full screen mode. If in
// windowed mode, it blts the back surface to the primary surface
// instead of fliping them.
//------------------------------------------------------------------------------
VOID Flip()
{
//lpddsBack->Blt(NULL, lpddsScreen, NULL, DDBLT_WAIT, NULL);
//lpddsPrimary->Blt(NULL, lpddsBack, NULL, DDBLT_WAIT, NULL);
lpddsPrimary->Blt(&rcScreen, lpddsScreen, NULL, DDBLT_WAIT, NULL);
} // end Flip()
//------------------------------------------------------------------------------
// Name: BeginDrawing()
// Desc: Clears the surface to the background color, locks the surface and
// sets up the pointer to the video memory.
//------------------------------------------------------------------------------
HRESULT BeginDrawing()
{
HRESULT ddrval;
DDSURFACEDESC2 ddsdLockedSurf; // Surface description to lock our surface.
// Clear to the background color which is located in bits 7-5 in reg $2001
// when bit 0 is set, otherwise is the first entry in the background palette.
//BYTE byColorIndex = ((CPU.Memory[0x2001] & 0xE0) >> 5) * 3;
//ClearScreen(RGB(abyNESPalette[byColorIndex], 0, 0));
ClearScreen(RGB(0, 0, 0));
// Lock the surface.
ddsdLockedSurf.dwSize = sizeof(DDSURFACEDESC2);
ddrval = lpddsScreen->Lock(NULL, &ddsdLockedSurf, DDLOCK_WAIT | DDLOCK_NOSYSLOCK, NULL);
if (ddrval == DD_OK)
{
// Initialize the pointer to surface memory to point to the
// beginning of our scanline.
pSurfaceMemory = (BYTE*)ddsdLockedSurf.lpSurface;
// Save the pitch of the surface for later use in other functions.
lSurfacePitch = ddsdLockedSurf.lPitch;
}
return ddrval;
} // end BeginDrawing()
//------------------------------------------------------------------------------
// Name: EndDrawing()
// Desc: Unlocks the drawing surface and flips the surfaces.
//------------------------------------------------------------------------------
VOID EndDrawing()
{
// Unlock the drawing surface.
lpddsScreen->Unlock(NULL);
// Flip the surfaces to display what we've drawn.
Flip();
} // end EndDrawing()
//------------------------------------------------------------------------------
// Name: DrawScanline()
// Desc: The main routine for drawing a scanline. Locks and unlocks the
// surface and calls the scanline drawing routing for the type
// of mirroring.
//------------------------------------------------------------------------------
VOID DrawScanline()
{
// Save the pointer to video memory so we don't permanently modify it.
BYTE* pTemp = pSurfaceMemory;
// Now move the pointer to surface memory to the correct scanline.
pSurfaceMemory += (lSurfacePitch * wScanline);
// Draw our scanline if its enabled.
if (CPU.Memory[0x2001] & 0x08)
DrawScanlineH();
// Draw all the sprites that are on the scanline if they are enabled.
//if (CPU.Memory[0x2001] & 0x10)
// DrawScanlineSprites();
// Restore our pointer to memory.
pSurfaceMemory = pTemp;
} // end DrawScanline()
//------------------------------------------------------------------------------
// Name: DrawScanlineH()
// Desc: Draws a scanline using horizontal mirroring.
//------------------------------------------------------------------------------
VOID DrawScanlineH()
{
BYTE* pabyNameTables;
BYTE* pbyNameTableTile;
BYTE* pbyAttributeTable;
BYTE* pbyCurPatternTableAddr;
BYTE byCounter = 8;
BYTE byUpperColor = 0;
WORD wTileNum = 0;
WORD wBegTileNum = 0;
WORD relx = byHScroll[0];
WORD x = 0;
DWORD byScanlineMOD8 = wScanline % 8;
__asm
{
// Save the registers
pushad
// The pointer to the current nametable tile will always
// start with the first name table.
// Get the bits that determine what nametable to use.
xor ebx, ebx
mov bl, [CPU.Memory+02000h]
and bl, 03
// Get the address of the name table and save it for later.
lea eax, [PPU.apbyNameTables+ebx*4]
mov pabyNameTables, eax
// Get the pointer to the current name table and store
// it in the variables for later. Also, store the
// the attribute table.
mov eax, [eax]
mov pbyNameTableTile, eax
mov pbyAttributeTable, eax
add eax, 03C0h
mov pbyAttributeTable, eax
// Get the address of the background pattern table
xor ebx, ebx
mov bl, BYTE PTR [CPU.Memory+02000h] // Address of the PPU control register #1
and bl, PATTERN_TABLE_ADDRESS
shr bx, 2 // Which pattern table (0,1)
mov ebx, [PPU.apbyPatternTables+ebx] // Move the address of the pattern table array into ebx
mov pbyCurPatternTableAddr, ebx // Save the address of the pattern table for later
//--------------------------------------------------------
// Get the address of the tile number in the ppu memory
// by (TILENUM * 16) + PATTERNTABLE (Yoshi)
//--------------------------------------------------------
and eax, 000000FFh
mov ax, wScanline
mov cx, relx
// TILENUM = ((Scanline / 8) * 32) + (HScroll / 8)
shr ax, 3 // Scanline / 8
shl ax, 5 // Multiply by 32
// Save the beginning tile number so we don't have
// to recalculate it when we change name tables.
mov wBegTileNum, ax
shr cx, 3 // HScroll / 8
add ax, cx // ax=TILENUM
// Save the tile number so we can increment it.
mov wTileNum, ax
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -