Announcement

Collapse
No announcement yet.

SaveBmp - any colordepth

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • SaveBmp - any colordepth

    Hi there,

    snippet below is a port from MSDN. It already works.
    But i'm a bit unhappy with 2 things :
    Had to change my win32api.inc ( GetDIBits ).
    Is it possible to declare a function a second time
    outside win32api.inc ?

    Handling 16 bit Bitmaps differs a lot from the MSDN sample.
    Is it ok ?

    Basic code :
    Code:
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    ' GENERATED BY ManageIncludes 09.04.2001
    ' file : paintbx2.bas
    ' extracts function BMPsave ( BYVAL hBmp AS LONG, BYVAL hPalette AS LONG, sFileName AS STRING, flag AS LONG ) AS LONG
    '          function MemCopyD( BYVAL src AS LONG, BYVAL dst AS LONG, BYVAL ln AS LONG) AS LONG
    '          sub      BMPsetPalette( BYVAL hDC AS LONG, BYVAL hPalette AS LONG )
    ' from     E:\pbdll60\datalink\controls\inc\paintbox.bas
    
    
    SUB BMPsetPalette( BYVAL hDC AS LONG, BYVAL hPalette AS LONG )
        IF ISFALSE hPalette THEN EXIT SUB
        SelectPalette  hDC, hPalette, %FALSE
        RealizePalette hDC
    END SUB
    
    FUNCTION MemCopyD(BYVAL src AS LONG, _
                      BYVAL dst AS LONG, _
                      BYVAL ln AS LONG) AS LONG
        #REGISTER NONE
          ! cld
          ! mov esi, src
          ! mov edi, dst
          ! mov ecx, ln
          ! shr ecx, 2
          ! rep movsd
          ! mov ecx, ln
          ! and ecx, 3
          ! rep movsb
        FUNCTION = 0
    END FUNCTION
    
    
    ''*****************************Public*Routine******************************\
    ' function BmpSave
    ' requires changes in win32api.inc :
    ' old :
    ' DECLARE FUNCTION GetDIBits LIB "GDI32.DLL" ALIAS "GetDIBits" (BYVAL aHDC AS LONG, BYVAL hBitmap AS LONG, BYVAL nStartScan AS LONG, BYVAL nNumScans AS LONG, lpBits AS ANY, lpBI AS BITMAPINFO, BYVAL wUsage AS LONG) AS LONG
    ' new :
    ' DECLARE FUNCTION GetDIBits LIB "GDI32.DLL" ALIAS "GetDIBits" (BYVAL aHDC AS LONG, BYVAL hBitmap AS LONG, BYVAL nStartScan AS LONG, BYVAL nNumScans AS LONG, BYVAL lpBits AS LONG, BYVAL lpBI AS LONG, BYVAL wUsage AS LONG) AS LONG
    '**************************************************************************/
    
    FUNCTION BmpSave ( BYVAL hBmp AS LONG, BYVAL hPalette AS LONG, BYVAL flag AS LONG sFileName AS STRING ) AS LONG
    
        LOCAL hFile   AS LONG
        LOCAL hTmpBmp AS LONG
        LOCAL hBmpOld AS LONG
        LOCAL bfh     AS BITMAPFILEHEADER
        LOCAL pbmi    AS LONG ''
        LOCAL bmi     AS BITMAPINFO
        LOCAL sizBMI  AS LONG
        LOCAL hdc     AS LONG
        LOCAL Success AS LONG
        LOCAL pBits   AS LONG
    
        IF ISFALSE hBmp     THEN EXIT FUNCTION
        IF ISFALSE hPalette THEN EXIT FUNCTION
    
        hdc = getDC ( %hWnd_Desktop )
        IF ISFALSE hdc THEN EXIT FUNCTION
    
        BMPsetPalette hDC, hPalette
        Success = %true
        ''
        '' LET the graphics engine TO retrieve the dimension of the bitmap FOR us
        '' GetDIBits uses the SIZE TO determine IF it's BITMAPCOREINFO or BITMAPINFO
        '' IF BitCount != 0, color table will be retrieved
        bmi.bmiHeader.biSize     = SIZEOF(bmi.bmiHeader)  '' GDI need this TO work
        bmi.bmiHeader.biBitCount = 0                      '' don't get the color table
        IF ISFALSE GetDIBits (hDC, hBmp, 0, 0, 0, VARPTR( bmi ), %DIB_RGB_COLORS) THEN
           ReleaseDC %hWnd_DESKTOP, hDC
           EXIT FUNCTION
        END IF
    
        '' Now that we know the SIZE of the IMAGE, alloc enough memory TO retrieve
        '' the actual bits
        pBits = GlobalAlloc( %GMEM_FIXED, bmi.bmiHeader.biSizeImage )
        IF ISFALSE pBits THEN
           ReleaseDC %hWnd_DESKTOP, hDC
           EXIT FUNCTION
        END IF
    
        '' Note: 24 bits per pixel has no COLOR table.  So, we don't have to
        '' allocate memory FOR retrieving that.  Otherwise, we do.
        SELECT CASE bmi.bmiHeader.biBitCount             '' has COLOR table
            CASE 24   : sizBMI = LEN(BITMAPINFOHEADER)
            CASE 16   : sizBMI = LEN( BITMAPINFO )
            CASE 32   : sizBMI = LEN(BITMAPINFOHEADER) + LEN(RGBQUAD) * 3
            CASE ELSE
                 LOCAL i AS LONG
                 i = 1
                 SHIFT LEFT i, bmi.bmiHeader.biBitCount
                 sizBMI = LEN(BITMAPINFOHEADER) + 4 * i
        END SELECT
        ''
        '' Allocate memory FOR BITMAPINFOHEADER
        '' and COLOR table IF it is NOT 24bpp...
        ''
        pbmi = GlobalAlloc( %GMEM_FIXED OR %GMEM_ZEROINIT, sizBMI )
        IF ISFALSE pbmi THEN Success = %FALSE : GOTO ErrExit1
        '' Now that we've a bigger chunk of memory, let's copy the Bitmap
        '' info header DATA over
        memCopyD VARPTR(bmi), pbmi, LEN ( BITMAPINFOHEADER )
        ''
        '' fill IN the info FOR the BitmapFileHeader
        ''
        bfh.bfType    = CVWRD("BM")
        bfh.bfSize    = LEN(bfh) + LEN(BITMAPINFOHEADER) + sizBMI + bmi.bmiHeader.biSizeImage
        bfh.bfOffBits = LEN(bfh) + sizBMI
    
        '' Bitmap can't be selected into a DC when calling GetDIBits
        '' Assume that the hDC is the DC where the bitmap would have been selected
        '' IF indeed it has been selected
        ''
        hTmpBmp = CreateCompatibleBitmap( hDC, bmi.bmiHeader.biWidth, bmi.bmiHeader.biHeight )
        IF ISFALSE hTmpBmp THEN Success = %FALSE : GOTO ErrExit3
    
        hBmpOld = SelectObject(hDC, hTmpBmp)
        IF ISFALSE GetDIBits(hDC, hBmp, 0, bmi.bmiHeader.biHeight, pBits, pbmi, %DIB_RGB_COLORS) THEN _
           Success = %FALSE : GOTO ErrExit4
        ''
        '' LET's open the file and get ready for writing
        ''
        hFile = FREEFILE
        OPEN sFileName FOR OUTPUT AS hFile
        IF ERR THEN Success = %FALSE : GOTO ErrExit4
        PRINT #hfile, PEEK$( VARPTR( bfh ), LEN (bfh) ) & _           ' BitmapFileHeader
                      PEEK$( pbmi, sizBMI ) & _                       ' BitmapInfoHeader AND COLOR table, IF ANY
                      PEEK$( pBits, bmi.bmiHeader.biSizeImage )       ' WRITE the bits also
        IF ERR THEN Success = %FALSE : GOTO ErrExit4
    
    
    ErrExit4:
        SelectObject hDC, hBmpOld
        DeleteObject hTmpBmp
    
    ErrExit3:
        CLOSE hFile
    
    ErrExit2:
        GlobalFree pbmi
    
    ErrExit1:
        GlobalFree pBits
        ReleaseDC %hWnd_DESKTOP, hDC
        FUNCTION = Success
    
    END FUNCTION

    Microsoft's C source :
    Code:
    /******************************Module*Header*******************************\
    * Module Name: savebmp.c
    *
    *
    * Created: 06-Jan-1992 10:59:36
    *
    * Copyright (C) 1993-1996 Microsoft Corporation
    *
    * Contains the main routine, SaveBitmapFile, for saving a DDB into file
    * in DIB format.
    *
    * Dependencies:
    *
    *   (#defines)
    *   (#includes)
    *       #include <windows.h>
    *       #include "jtypes.h"
    *
    \**************************************************************************/
    #include <windows.h>
    #include "julia.h"
    
    extern HPALETTE ghPal;
    extern HWND ghwndMain;
    extern char   gtext[256];
    BOOL SaveBitmapFile(HDC, HBITMAP, PSTR);
    // void ErrorOut(char errstring[30]);
    
    /******************************Public*Routine******************************\
    * SaveBitmapFile
    *
    *
    * Effects: Save pInfo->hBmpSaved into disk specified by pszFileName
    *
    * Warnings: assumes hBmpSaved is not selected into window's DC other than
    *           pInfo->hwnd's DC
    *
    \**************************************************************************/
    
    BOOL SaveBitmapFile(HDC hDC, HBITMAP hBmp, PSTR pszFileName)
    {
        int         hFile;
        OFSTRUCT    ofReOpenBuff;
        HBITMAP     hTmpBmp, hBmpOld;
        BOOL        bSuccess;
        BITMAPFILEHEADER    bfh;
        PBITMAPINFO pbmi;
        PBYTE       pBits;
        BITMAPINFO  bmi;
        PBYTE pjTmp, pjTmpBmi;
        ULONG sizBMI;
    
    
        bSuccess = TRUE;
    #if 0
        if (ghPal) {
            SelectPalette(hDC, ghPal, FALSE);
            RealizePalette(hDC);
        }
    #endif
        if (!hBmp) {
            MessageBox(ghwndMain,
    			       GetStringRes (IDS_NO_BITMAP_TO_SAVE),
    				   NULL, MB_OK);
            return FALSE;
        }
    
        //
        // Let the graphics engine to retrieve the dimension of the bitmap for us
        // GetDIBits uses the size to determine if it's BITMAPCOREINFO or BITMAPINFO
        // if BitCount != 0, color table will be retrieved
        //
        bmi.bmiHeader.biSize = 0x28;              // GDI need this to work
        bmi.bmiHeader.biBitCount = 0;             // don't get the color table
        if ((GetDIBits(hDC, hBmp, 0, 0, (LPSTR)NULL, &bmi, DIB_RGB_COLORS)) == 0) {
           return FALSE;
        }
    
        //
        // Now that we know the size of the image, alloc enough memory to retrieve
        // the actual bits
        //
        if ((pBits = (PBYTE)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,
                    bmi.bmiHeader.biSizeImage)) == NULL) {
            return FALSE;
        }
    
        //
        // Note: 24 bits per pixel has no color table.  So, we don't have to
        // allocate memory for retrieving that.  Otherwise, we do.
        //
        pbmi = &bmi;                                      // assume no color table
    
        switch (bmi.bmiHeader.biBitCount) {
            case 24:                                      // has color table
                sizBMI = sizeof(BITMAPINFOHEADER);
                break;
            case 16:
            case 32:
                sizBMI = sizeof(BITMAPINFOHEADER)+sizeof(DWORD)*3;
                break;
            default:
                sizBMI = sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*(1<<bmi.bmiHeader.biBitCount);
                break;
    
        }
    
        //
        // Allocate memory for color table if it is not 24bpp...
        //
        if (sizBMI != sizeof(BITMAPINFOHEADER)) {
            ULONG       sizTmp;
            //
            // I need more memory for the color table
            //
            if ((pbmi = (PBITMAPINFO)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizBMI )) == NULL) {
                bSuccess = FALSE;
                goto ErrExit1;
            }
            //
            // Now that we've a bigger chunk of memory, let's copy the Bitmap
            // info header data over
            //
            pjTmp = (PBYTE)pbmi;
            pjTmpBmi = (PBYTE)&bmi;
            sizTmp = sizeof(BITMAPINFOHEADER);
    #ifdef __LCC__
    		memcpy(pjTmp,pjTmpBmi,sizTmp);
    #else
            while(sizTmp--)
            {
                *(((PBYTE)pjTmp)++) = *((pjTmpBmi)++);
            }
    #endif
        }
    
        //
        // Let's open the file and get ready for writing
        //
        if ((hFile = OpenFile(pszFileName, (LPOFSTRUCT)&ofReOpenBuff,
                     OF_CREATE | OF_WRITE)) == -1) {
            MessageBox(ghwndMain, GetStringRes(IDS_FILE_OPEN_FAILED),
    				   NULL, MB_OK);
            goto ErrExit2;
        }
    
        //
        // But first, fill in the info for the BitmapFileHeader
        //
        bfh.bfType = 0x4D42;                            // 'BM'
        bfh.bfSize = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+sizBMI+
            pbmi->bmiHeader.biSizeImage;
        bfh.bfReserved1 =
        bfh.bfReserved2 = 0;
        bfh.bfOffBits = sizeof(BITMAPFILEHEADER)+sizBMI;
    
        //
        // Write out the file header now
        //
        if (_lwrite(hFile, (LPSTR)&bfh, sizeof(BITMAPFILEHEADER)) == -1) {
            bSuccess = FALSE;
            goto ErrExit3;
        }
    
        //
        // Bitmap can't be selected into a DC when calling GetDIBits
        // Assume that the hDC is the DC where the bitmap would have been selected
        // if indeed it has been selected
        //
        if (hTmpBmp = CreateCompatibleBitmap(hDC, pbmi->bmiHeader.biWidth, pbmi->bmiHeader.biHeight)) {
            hBmpOld = SelectObject(hDC, hTmpBmp);
            if ((GetDIBits(hDC, hBmp, 0, pbmi->bmiHeader.biHeight, (LPSTR)pBits, pbmi, DIB_RGB_COLORS))==0){
                bSuccess = FALSE;
                goto ErrExit4;
            }
        } else {
            MessageBox(ghwndMain,
    			GetStringRes (IDS_BITMAP_NOT_CREATED),
    			NULL, MB_OK);
            bSuccess = FALSE;
            goto ErrExit3;
        }
    
        //
        // Now write out the BitmapInfoHeader and color table, if any
        //
        if (_lwrite(hFile, (LPSTR)pbmi, sizBMI) == -1) {
            bSuccess = FALSE;
            goto ErrExit4;
        }
    
        //
        // write the bits also
        //
        if (_lwrite(hFile, (LPSTR)pBits, pbmi->bmiHeader.biSizeImage) == -1) {
            bSuccess = FALSE;
            goto ErrExit4;
        }
    
    
    ErrExit4:
        SelectObject(hDC, hBmpOld);
        DeleteObject(hTmpBmp);
    ErrExit3:
        _lclose(hFile);
    ErrExit2:
        GlobalFree(pbmi);
    ErrExit1:
        GlobalFree(pBits);
        return bSuccess;
    }
    
    
    #if 0
    /************************************************************************
     * void ErrorOut(char errstring[30])
     *
     * Purpose: Print out an meainful error code by means of
     *	  GetLastError and printf
     *
     * Inputs:  errstring - the action that failed, passed by the
     *		      calling proc.
     *
     * Returns: none
     *
     * Calls:   GetLastError
     *
     * History:
     * 09-13-91 Pete Grey   Created.
     *
    \************************************************************************/
    
    
    void ErrorOut(char errstring[30])
    {
    DWORD Error;
    CHAR  str[80];
    
    Error= GetLastError();
    wsprintf((LPSTR) str, "Error on %s = %d\n", errstring, Error);
    OutputDebugString(str);
    }
    
    #endif



    ------------------

  • #2
    Ralph:
    > Had to change my win32api.inc ( GetDIBits ).

    I was able to compile, using current inc - in one place I used long variable (pbits = 0), in another - ByVal

    > Handling 16 bit Bitmaps differs a lot from the MSDN sample. Is it ok ?

    I believe that MSDN simply illustrates GetDIBits. Never saw 16-bit BMP and don't think that to have matter with DDB is a good idea.
    Code, which you refered, maybe useful, for example, to save part of the screen as BMP.

    Much easy (and clear) to create DIB section, to select it into memory hDC and to copy from screen DC to memory DC (= to fill DIB section).
    For video regimes 16bits+ there are reasons to use 24bits DIB-sections only.
    If video regime is 256 colors it's possible to create 8-bits DIB sections also (retrieve a palette from screen DC).



    ------------------
    E-MAIL: [email protected]

    Comment


    • #3
      Semen

      you are right. Followed the sample to close.
      The lines :
      Code:
      hTmpBmp = CreateCompatibleBitmap( hDC, bmi.bmiHeader.biWidth, bmi.bmiHeader.biHeight )
      IF ISFALSE hTmpBmp THEN Success = %FALSE : GOTO ErrExit3    
      hBmpOld = SelectObject(hDC, hTmpBmp)
      ..
      ..
      ErrExit4:    
        ..
        DeleteObject hTmpBmp
      are useless. But how did you manage the Api call to GetDIBits
      without changing win32api.inc ? pbmi changes size depending on
      resolution.

      Ralph


      ------------------

      Comment

      Working...
      X