Author Topic: GDI Blitting question?  (Read 4887 times)

0 Members and 1 Guest are viewing this topic.

Offline ninogenio

  • Pentium
  • *****
  • Posts: 1668
  • Karma: 133
    • View Profile
GDI Blitting question?
« on: October 13, 2007 »
ive been messing around some more and i was wondering if storing and blitting gfx like so

pesudo code
Code: [Select]
// store sprite in an hdc to be used in game
HDC sprite,scene_dc;
HBITMAP BufferStorage;

scene_dc = GetDC( hwnd );
sprite = CreateCompatibleDC( scene_dc );
                     
BufferStorage = CreateCompatibleBitmap( scene_dc, sprite_width, spritehieght );
SelectObject( sprite, BufferStorage );
ReleaseDC( hwnd, scene_dc );

BITMAPINFO bmi;
       
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = sprite_width;
bmi.bmiHeader.biHeight = -sprite_height;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = SCREEN_BPP;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = 0;
bmi.bmiHeader.biXPelsPerMeter = 75;
bmi.bmiHeader.biYPelsPerMeter = 75;
bmi.bmiHeader.biClrUsed = 0;
bmi.bmiHeader.biClrImportant = 0;
         
SetDIBitsToDevice(sprite, 0,0, SCREEN_X,SCREEN_Y, 0,0, 0,SCREEN_Y, bitmap_ptr, &bmi,  DIB_RGB_COLORS);

and then just having to bitblit them to my offscreendc each frame instead of writing them pixel by pixel would much quicker because if it is i might start doing it this way.
« Last Edit: October 14, 2007 by ninogenio »
Challenge Trophies Won:

Offline Jim

  • Founder Member
  • DBF Aficionado
  • ********
  • Posts: 5301
  • Karma: 402
    • View Profile
Re: GDI Blitting question?
« Reply #1 on: October 14, 2007 »
Yes, pre-drawing the pixels into a DC and blitting them will be way faster.  It just gets more difficult if you want to do alpha or non-rectangular sprites.  To do alpha you can use AlphaBlend() instead of BitBlt(), but that won't work on Windows 95 (do we care?).  To do cut-outs you need a mask graphic - ideally a 1bit-per-pixel DC where there is a 0 where there's data in your image, and a 1 where you don't want to draw.  Then you do 2 blits.  A BitBlt of the mask with AND mode (SRCERASE) (this cuts out a black area where your sprite will be) and then a bitblt from your actual colour graphic with OR mode (SRCPAINT) (you need black in your graphic where you don't want to draw).  This is called a 'cookie cut', like cutting cookies out of a sheet of biscuit mixture, and is how the Amiga's blitter did it :)

Jim
Challenge Trophies Won:

Offline ninogenio

  • Pentium
  • *****
  • Posts: 1668
  • Karma: 133
    • View Profile
Re: GDI Blitting question?
« Reply #2 on: October 14, 2007 »
yeah i was looking at cookie cutting code earlier and thought it was really cool.

ive just done a quick test and the way i have it isnt quite right im trying to fill a dc with 0x00000000 and push it to my offscreendc in a clearscreen function it compiles fine but doesnt clear the pixel stuff out.

Code: [Select]
#include <windows.h>
#include <math.h>
#include <string.h>

#include "main.hpp"

HDC OffScreenDC,ClearGfx,scene_dc;
HBITMAP BufferStorage;
LRESULT CALLBACK WindowProc( HWND hwnd , UINT uMsg , WPARAM wParam , LPARAM lParam )
{
        switch (uMsg)
        {           
                case WM_CREATE:

                     scene_dc = GetDC( hwnd );
                     OffScreenDC = CreateCompatibleDC( scene_dc );
                     
                     BufferStorage = CreateCompatibleBitmap( scene_dc, SCREEN_X, SCREEN_Y );
                     SelectObject( OffScreenDC, BufferStorage );
                     
                     ClearGfx = CreateCompatibleDC( scene_dc );

                     SelectObject( ClearGfx, BufferStorage );
                     ReleaseDC( hwnd, scene_dc );
                     
                     BITMAPINFO bmi;
       
                     bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
                     bmi.bmiHeader.biWidth = SCREEN_X;
                     bmi.bmiHeader.biHeight = -SCREEN_Y;
                     bmi.bmiHeader.biPlanes = 1;
                     bmi.bmiHeader.biBitCount = SCREEN_BPP;
                     bmi.bmiHeader.biCompression = BI_RGB;
                     bmi.bmiHeader.biSizeImage = 0;
                     bmi.bmiHeader.biXPelsPerMeter = 75;
                     bmi.bmiHeader.biYPelsPerMeter = 75;
                     bmi.bmiHeader.biClrUsed = 0;
                     bmi.bmiHeader.biClrImportant = 0;
         
                     SetDIBitsToDevice(ClearGfx, 0,0, SCREEN_X,SCREEN_Y, 0,0, 0,SCREEN_Y, (const void*)0x00000000, &bmi,  DIB_RGB_COLORS);
                     //for( int y=0;y<800;y++)
                     //for( int x=0;x<600;x++)
         //COLORF is 00bbggrr
                     //SetPixel(ClearGfx,x,y,0x00000000);
                     //
                     GenioInit( OffScreenDC ,hwnd );
                     break; 
                case WM_DESTROY:
                        GenioCleanUp( OffScreenDC, hwnd );
                        DeleteDC( OffScreenDC );
                        ChangeDisplaySettings( NULL , 0 );
                        ShowCursor(1);
                        PostQuitMessage(0);
                        quit = 1;
                        break;
                case WM_KEYDOWN:
                       switch (wParam)
                       {
                              case VK_ESCAPE:
                                   PostQuitMessage(0);
                                   quit = 1;
                                   break;

                        }
                        break;
                 case WM_PAINT:
                      PAINTSTRUCT paint;
                      memset(&paint, 0, sizeof paint);
      paint.hdc = (HDC)wParam;
     
                      GetUpdateRect(hwnd, &paint.rcPaint,TRUE);
      BeginPaint(hwnd, &paint);
      GenioMain(OffScreenDC,hwnd);
      BitBlt(paint.hdc, 0, 0, SCREEN_X, SCREEN_Y, OffScreenDC, 0, 0, SRCCOPY);
      EndPaint(hwnd, &paint);
                     
                      break;
                case WM_ERASEBKGND:
         return 1;
         break;
          case WM_TIMER:
         InvalidateRect(hwnd, NULL, 0);
         break;
                default:
                        return DefWindowProc(hwnd, uMsg, wParam, lParam);
        }
        return 0;
}


int APIENTRY WinMain( HINSTANCE hInstance , HINSTANCE hPrevInstance , LPSTR lpCmdLine , int nCmdShow )
{
        WNDCLASSEX clas;
        MSG msg;
        HDC hdc;
       
        clas.cbSize = sizeof(WNDCLASSEX);
        clas.style = CS_HREDRAW | CS_VREDRAW;
        clas.lpfnWndProc = WindowProc;
        clas.cbClsExtra = 0;
        clas.cbWndExtra = 0;
        clas.hInstance = hInstance;
        clas.hIcon = NULL;
        clas.hCursor = NULL;
        clas.hbrBackground = 0;
        clas.lpszMenuName = NULL;
        clas.lpszClassName = scene_class;
        clas.hIconSm = 0;
        RegisterClassEx(&clas);
       
        DEVMODE dmScreenSettings;
        memset(&dmScreenSettings, 0, sizeof dmScreenSettings);
dmScreenSettings.dmSize = sizeof dmScreenSettings;
dmScreenSettings.dmPelsWidth = SCREEN_X;
dmScreenSettings.dmPelsHeight = SCREEN_Y;
dmScreenSettings.dmBitsPerPel = SCREEN_BPP;
dmScreenSettings.dmDisplayFrequency = 60;
dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
if (ChangeDisplaySettings (&dmScreenSettings, CDS_FULLSCREEN) == DISP_CHANGE_SUCCESSFUL) scene_window = CreateWindowEx(0, scene_class, "scene!", WS_POPUP, 0,0,SCREEN_X,SCREEN_Y, NULL, NULL, hInstance, 0);
       
        Sleep(1000);
        ShowWindow(scene_window, nCmdShow);
        UpdateWindow(scene_window);
        ShowCursor(0);
       
    SetTimer(scene_window, 1, 0, NULL);
   
        do
        {
                while (!quit && GetMessage(&msg, scene_window, 0, 0) > 0)
                {
                        TranslateMessage(&msg);
                        DispatchMessage(&msg);
                } 
                   
        } while (!quit);
       
        return 0;
}







void renderworld(HDC hdc)
{     
        BITMAPINFO bmi;
       
        bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        bmi.bmiHeader.biWidth = SCREEN_X;
        bmi.bmiHeader.biHeight = -SCREEN_Y;
        bmi.bmiHeader.biPlanes = 1;
        bmi.bmiHeader.biBitCount = SCREEN_BPP;
        bmi.bmiHeader.biCompression = BI_RGB;
        bmi.bmiHeader.biSizeImage = 0;
        bmi.bmiHeader.biXPelsPerMeter = 75;
        bmi.bmiHeader.biYPelsPerMeter = 75;
        bmi.bmiHeader.biClrUsed = 0;
        bmi.bmiHeader.biClrImportant = 0;
         
        SetDIBitsToDevice(hdc, 0,0, SCREEN_X,SCREEN_Y, 0,0, 0,SCREEN_Y, screen_ptr, &bmi,  DIB_RGB_COLORS);
               
}



void clearworld(void)
{   
     BitBlt(OffScreenDC, 0, 0, SCREEN_X, SCREEN_Y, ClearGfx, 0, 0, SRCCOPY);
}

this is the same code as my font drawing stuff where in geniomain i do this

main()
{
draw some pixel stuff
render world
clearworld
draw some text
}
« Last Edit: October 14, 2007 by ninogenio »
Challenge Trophies Won:

Offline Jim

  • Founder Member
  • DBF Aficionado
  • ********
  • Posts: 5301
  • Karma: 402
    • View Profile
Re: GDI Blitting question?
« Reply #3 on: October 14, 2007 »
First problem is here
Code: [Select]
SetDIBitsToDevice(ClearGfx, 0,0, SCREEN_X,SCREEN_Y, 0,0, 0,SCREEN_Y, (const void*)0x00000000, &bmi,  DIB_RGB_COLORS);You can't pass 0x00000000 as the pointer to the graphic and expect to get a black image.  It'll just crash as it tries to read the data from there.
Either malloc a black texture and use SetDIBits, or use SetPixel (slow), or maybe you could try doing a BitBlt from the DC to itself using BLACKNESS as the Rop?
ie.
Code: [Select]
BitBlt(dst, x, y, w, h, dst, x, y, BLACKNESS);
<edit>
Second problem
Code: [Select]
                 case WM_PAINT:
                      PAINTSTRUCT paint;
                      memset(&paint, 0, sizeof paint);
                      paint.hdc = (HDC)wParam;
                 
Go here and read the description of WM_PAINT message.
http://msdn2.microsoft.com/en-us/library/ms534901.aspx
wParam isn't set to anything, so why would you use it as the HDC?
Fortunately for you, BeginPaint() overwrites this mistake!
You don't need these two lines at all
Code: [Select]
                      memset(&paint, 0, sizeof paint);
                      paint.hdc = (HDC)wParam;
in fact, I don't think you need this line either
Code: [Select]
                      GetUpdateRect(hwnd, &paint.rcPaint,TRUE);
That function is used to detect if the window needs any painting - but you don't check the return value so it's effectively doing nothing except sending the WM_ERASEBKGND message, which we have coded to ignore!
The rcPaint RECT is nearly always going to be the whole window because we caused the WM_PAINT to get called by the InvalidateRect() call in WM_TIMER.  Anyway, you don't use the RECT information because you just redraw the entire window every time.

Jim
« Last Edit: October 14, 2007 by Jim »
Challenge Trophies Won:

Offline ninogenio

  • Pentium
  • *****
  • Posts: 1668
  • Karma: 133
    • View Profile
Re: GDI Blitting question?
« Reply #4 on: October 14, 2007 »
thanks jim that makes sense ive tryed out the setpixel but with no luck, i think i might have the clearscreen function in the wrong place but ill have to test it out.
Challenge Trophies Won:

Offline ninogenio

  • Pentium
  • *****
  • Posts: 1668
  • Karma: 133
    • View Profile
Re: GDI Blitting question?
« Reply #5 on: October 14, 2007 »
Quote
<edit>
Second problem
Code:

                 case WM_PAINT:
                      PAINTSTRUCT paint;
                      memset(&paint, 0, sizeof paint);
                      paint.hdc = (HDC)wParam;

                 
Go here and read the description of WM_PAINT message.
http://msdn2.microsoft.com/en-us/library/ms534901.aspx
wParam isn't set to anything, so why would you use it as the HDC?
Fortunately for you, BeginPaint() overwrites this mistake!
You don't need these two lines at all
Code:

                      memset(&paint, 0, sizeof paint);
                      paint.hdc = (HDC)wParam;

in fact, I don't think you need this line either
Code:

                      GetUpdateRect(hwnd, &paint.rcPaint,TRUE);

That function is used to detect if the window needs any painting - but you don't check the return value so it's effectively doing nothing except sending the WM_ERASEBKGND message, which we have coded to ignore!
The rcPaint RECT is nearly always going to be the whole window because we caused the WM_PAINT to get called by the InvalidateRect() call in WM_TIMER.  Anyway, you don't use the RECT information because you just redraw the entire window every time.

oops  :P , tbh jim ive been trying loads of stuff most of which doesnt work and thats a couple of bits i forgot to take out.

i figuared out the problem, i was trying to share bufferstorage between my two dc`s (offscreendc and cleargfx) which i found out wouldnt work as bufferstorage is attached to offscreendc till the program shuts down so i have two HBITMAP`s now one for each dc, it didnt cause any crashes it just wouldnt take color info from setdibits or setpixel.

cheers for all that help though and i runs much quicker now.

edit - oh and btw you were right this works perfectly well also and saves the need for any extra stuff at the top.

Code: [Select]
void clearworld(void)
{   
    BitBlt(OffScreenDC, 0, 0, SCREEN_X, SCREEN_Y, OffScreenDC, 0, 0, BLACKNESS);
}
« Last Edit: October 14, 2007 by ninogenio »
Challenge Trophies Won:

Offline ninogenio

  • Pentium
  • *****
  • Posts: 1668
  • Karma: 133
    • View Profile
Re: GDI Blitting question?
« Reply #6 on: October 14, 2007 »
is it possible to do a cookie cut and an alpha blit on an hdc jim? looking at it it doesnt look possible it looks like one or the other can be done.

or is there another way to mask the image while alpha blitting it?
Challenge Trophies Won:

Offline Jim

  • Founder Member
  • DBF Aficionado
  • ********
  • Posts: 5301
  • Karma: 402
    • View Profile
Re: GDI Blitting question?
« Reply #7 on: October 14, 2007 »
You don't need a mask or cookie cut when you're doing the alpha blit.  Just set the alpha channel to 0 where you don't want to see the src.

Jim
Challenge Trophies Won:

Offline ninogenio

  • Pentium
  • *****
  • Posts: 1668
  • Karma: 133
    • View Profile
Re: GDI Blitting question?
« Reply #8 on: October 14, 2007 »
ahh so its pretty much like ogl cool.

cheers!
Challenge Trophies Won: