DirectX7 SetPalette always failed

866 views Asked by At

I'm learning DirectX, I want to Bind a palette to the PrimarySurface, but the process always failed. I give my code below:

#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480
#define SCREEN_BPP 32
#define MAX_COLORS_PALETTE 256

#define DDRAW_INIT_STRUCT(ddstruct) { memset(&ddstruct, 0, sizeof(ddstruct)); ddstruct.dwSize = sizeof(ddstruct); }

LPDIRECTDRAW7 lpdd = NULL;
LPDIRECTDRAWSURFACE7 lpddPrimarySurface = NULL;
LPDIRECTDRAWPALETTE lpddPalette = NULL;
PALETTEENTRY palette[256];

// Omit the unneccessary content

int GameInit()
{
    if (FAILED(DirectDrawCreateEx(NULL, (void**)&lpdd, IID_IDirectDraw7, NULL)))
        return 0;
    if (FAILED(lpdd->SetCooperativeLevel(g_GameHwnd, DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX | DDSCL_EXCLUSIVE | DDSCL_ALLOWREBOOT)))
        return 0;
    if (FAILED(lpdd->SetDisplayMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, 0, 0)))
        return 0;

    DDRAW_INIT_STRUCT(ddsd);
    ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
    ddsd.dwBackBufferCount = 1;
    ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_FLIP;

    if (FAILED(lpdd->CreateSurface(&ddsd, &lpddPrimarySurface, NULL)))
        return 0;

    ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
    if (FAILED(lpddPrimarySurface->GetAttachedSurface(&ddsd.ddsCaps, &lpddBackSurface)))
        return 0;

    memset(palette, 0, MAX_COLORS_PALETTE * sizeof(PALETTEENTRY));

    for (int index = 0; index < MAX_COLORS_PALETTE; index++)
    {
        if (index < 64)
            palette[index].peRed = index * 4;
        else if (index >= 64 && index < 128)
            palette[index].peGreen = (index - 64) * 4;
        else if (index >= 128 && index < 192)
            palette[index].peBlue = (index - 128) * 4;
        else if (index >= 192 && index < 256)
            palette[index].peRed = palette[index].peGreen = palette[index].peBlue = (index - 192) * 4;

        palette[index].peFlags = PC_NOCOLLAPSE;
    }

    if (FAILED(lpdd->CreatePalette(DDPCAPS_8BIT | DDPCAPS_ALLOW256 | DDPCAPS_INITIALIZE, palette, &lpddPalette, NULL)))
        return 0;

    **// I always failed to set palette to primary surface here....**
    if (FAILED(lpddPrimarySurface->SetPalette(lpddPalette)))
    {
        MessageBox(NULL, "Failed", NULL, MB_OK);
        return 0;
    }

    DDRAW_INIT_STRUCT(ddsd);

    if (FAILED(lpddBackSurface->Lock(NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL)))
        return 0;

    UINT *videoBuffer = (UINT*)ddsd.lpSurface;

    for (int y = 0; y < SCREEN_HEIGHT; y++)
    {
        memset((void*)videoBuffer, y % 256, SCREEN_WIDTH * sizeof(UINT));
        videoBuffer += ddsd.lPitch >> 2;
    }

    if (FAILED(lpddBackSurface->Unlock(NULL)))
        return 0;


   return 1;
}

I don't know why I always failed to SetPalette to the primary surface. I set the DisplayMode to 640*480*32, my palette is only 256-color, is this the reason? But I consult the MSDN, CreatePalette can only create 2Bit, 4Bit, 8Bit palettes. Can a 32-bit display mode be compatible with 8Bit palette? Where is the problem?

I will be grateful if someone could give me some advice. Thanks.

2

There are 2 answers

1
Retired Ninja On BEST ANSWER

Anything greater than 8-bit mode (16, 24, 32) are direct color modes and the data in the frame buffer is the actual color, so there is no palette like there would be in an 8-bit indexed color mode.

To make your example work as it is, change SCREEN_BPP to 8. The problem with that is in modern versions of Windows you may find your palette doesn't stick and it gets set back to the system palette. There are some workarounds you might try listed here: "Exclusive" DirectDraw palette isn't actually exclusive

If you want to use 32-bit mode then there is no palette and you must set the color directly rather than using the index into a palette. You could do something like this to copy the palette color into the frame buffer in your example:

if (FAILED(lpddBackSurface->Lock(NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL)))
    return 0;

UINT *videoBuffer = (UINT*)ddsd.lpSurface;

for (int y = 0; y < SCREEN_HEIGHT; y++)
{
    const PALETTEENTRY& p = palette[y%256];
    UINT color = (p.peRed << 16) | (p.peGreen << 8) | p.peBlue;
    for(int x = 0; x < SCREEN_WIDTH; ++x)
    {
        videoBuffer[x] = color;
    }
    videoBuffer += ddsd.lPitch / sizeof(UINT);
}

if (FAILED(lpddBackSurface->Unlock(NULL)))
    return 0;
0
DHarding On

@Retired Ninja who is correct and I implemented my code based on his/her comments as in the below working code to first initialize he video to use a 16:9 aspect ratio which will get you a full screen in directx 7.0 is what I tested this on:

It is true you do not need a palette when utilizing a 32Bit Per Pixel (BPP) bmp file

void Video::Initialize(void)
{

    

    TRACE("Video::Initialize invoked\n");

    
    try
    {
        hrRetVal = DirectDrawCreateEx(NULL, (void**)&pDDraw, IID_IDirectDraw7, NULL);
        if (DD_OK == hrRetVal)
        {
            hrRetVal = pDDraw->SetCooperativeLevel(hwnd,DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWREBOOT);
            

            if (DD_OK == hrRetVal)
            {
                // Lets see if the video is supported 
                hrRetVal = QueryVideoCapability();
                if (DD_OK != hrRetVal)
                {
                    Check(hrRetVal);
                }
                                
                hrRetVal = pDDraw->SetDisplayMode(1600, 900, 32, 60, 0);
                if (DD_OK != hrRetVal)
                   throw (VIDEO_EXCEPTION);
                
            }
            else
               throw (VIDEO_EXCEPTION);
        }
        else
            throw (VIDEO_EXCEPTION);
    }

    catch(int exception)
    ....
.....

Here is working code to read in the 32BPP BMP file as well.

void Bitmap::BMP32::Read(BMP32& a_bmp32, const char* file_name) // the new read that will dispaly 32BPP background
{

    TRACE("Bitmap::BMP32::Read invoked on file_name = %s", file_name);
    
    FILE* fp;
    //IMAGE an_image;
    //SURFACE a_surface;
    

    if ((fp = fopen(file_name, "rb")) != NULL)
    {

        if (fp != NULL)
        {
            TRACE("Reading the first Block of data from %s The File Type Data\n", file_name);
            /*
                uint16_t FileType{ 0x424D };
                uint32_t FileSize{ 0 };
                uint16_t Reserved1{ 0 };
                uint16_t Reserved2{ 0 };
                uint32_t PixelDataOffset{ 0 };
            */

            fread(&a_bmp32.HeaderSection.FileType, sizeof(a_bmp32.HeaderSection.FileType), 1, fp);
            fread(&a_bmp32.HeaderSection.FileSize, sizeof(a_bmp32.HeaderSection.FileSize), 1, fp);
            fread(&a_bmp32.HeaderSection.Reserved1, sizeof(a_bmp32.HeaderSection.Reserved1), 1, fp);
            fread(&a_bmp32.HeaderSection.Reserved2, sizeof(a_bmp32.HeaderSection.Reserved2), 1, fp);
            fread(&a_bmp32.HeaderSection.PixelDataOffset, sizeof(a_bmp32.HeaderSection.PixelDataOffset), 1, fp);

            TRACE("Reading in the Second Block of data from %s The Image Info Data\n", file_name);
            /*
            uint32_t HeaderSize{ 0x28 }; // the size of this structure
            uint32_t ImageWidth{ 0 };
            uint32_t ImageHeight{ 0 };
            uint16_t Planes{ 0 };
            uint16_t BitsPerPixel{ 0 };
            uint32_t Compression{ 0 };
            uint32_t ImageSize{ 0 };
            uint32_t XPixelsPerMeter{ 0 };
            uint32_t YPixelsPerMeter{ 0 };
            uint32_t TotalColors{ 0 };
            uint32_t ImportantColors{ 0 };*/

    

            fread(&a_bmp32.InfoSection.HeaderSize, sizeof(a_bmp32.InfoSection.HeaderSize), 1, fp);
            fread(&a_bmp32.InfoSection.ImageWidth, sizeof(a_bmp32.InfoSection.ImageWidth), 1, fp);  // if ImageWidth % 4 = Zero then we need to take into consideration row padding
            fread(&a_bmp32.InfoSection.ImageHeight, sizeof(a_bmp32.InfoSection.ImageHeight), 1, fp);
            fread(&a_bmp32.InfoSection.Planes, sizeof(a_bmp32.InfoSection.Planes), 1, fp);
            fread(&a_bmp32.InfoSection.BitsPerPixel, sizeof(a_bmp32.InfoSection.BitsPerPixel), 1, fp);
            fread(&a_bmp32.InfoSection.Compression, sizeof(a_bmp32.InfoSection.Compression), 1, fp);
            fread(&a_bmp32.InfoSection.ImageSize, sizeof(a_bmp32.InfoSection.ImageSize), 1, fp);
            fread(&a_bmp32.InfoSection.XPixelsPerMeter, sizeof(a_bmp32.InfoSection.XPixelsPerMeter), 1, fp);
            fread(&a_bmp32.InfoSection.YPixelsPerMeter, sizeof(a_bmp32.InfoSection.YPixelsPerMeter), 1, fp);
            fread(&a_bmp32.InfoSection.TotalColors, sizeof(a_bmp32.InfoSection.TotalColors), 1, fp);
            fread(&a_bmp32.InfoSection.ImportantColors, sizeof(a_bmp32.InfoSection.ImportantColors), 1, fp);


            TRACE("Reading in the Third Block of data from %s The Color Palette Data\n", file_name);

            /*
            uint8_t RedIntensity{ 0 };
            uint8_t GreenIntensity{ 0 };
            uint8_t BlueIntensity{ 0 };
            uint8_t Reserved{ 0 };
            */

            // note I have only tested this algorithm on 32BPP BMP images
            // in theory the ColorTableSection is skipped on 1,2,4,8 and 16BPP BMP files
            // here I skip that section only for 32BPP or 24BPP

    
            if (a_bmp32.InfoSection.BitsPerPixel < 24) {

                fread(&a_bmp32.ColorTableSection.RedIntensity, sizeof(a_bmp32.ColorTableSection.RedIntensity), 1, fp);
                fread(&a_bmp32.ColorTableSection.GreenIntensity, sizeof(a_bmp32.ColorTableSection.GreenIntensity), 1, fp);
                fread(&a_bmp32.ColorTableSection.BlueIntensity, sizeof(a_bmp32.ColorTableSection.BlueIntensity), 1, fp);
                fread(&a_bmp32.ColorTableSection.Reserved, sizeof(a_bmp32.ColorTableSection.Reserved), 1, fp);

            }
            else
            {
                TRACE("Reading in the Fourth Block of data from %s The Raw Pixel Data\n", file_name);

                // what are the dimensions of the surface that was read in
            
                
                // a_surface.surfrect.left = 0;
                // a_surface.surfrect.top = 0;
                // a_surface.surfrect.right = a_bmp32.InfoSection.ImageWidth;
                //a_surface.surfrect.bottom = a_bmp32.InfoSection.ImageHeight;

                // an_image.width = a_bmp32.InfoSection.ImageWidth;
                // an_image.height = a_bmp32.InfoSection.ImageHeight;
                // an_image.size = a_bmp32.InfoSection.ImageSize;

                // an_image.buffer = new BYTE[an_image.size];
                
                // fread(an_image.buffer, sizeof(BYTE), an_image.size, fp);
            
                a_bmp32.buffer = new BYTE[a_bmp32.InfoSection.ImageSize];
                fread(a_bmp32.buffer, sizeof(BYTE), a_bmp32.InfoSection.ImageSize, fp);
                 

            }



        }



        if (fp != NULL)
        {
            fclose(fp);
        }

    }
    else
    {
        TRACE("Bitmap::Read %s not found\n", file_name);

        char message[60];
        strcpy(message, "BMP Graphics file not found. ");
        strcat(message, file_name);
        strcat(message, "\nApplication will now terminate.");

        FatalMessageBox(message);
    }
}