IonicWind Software

IWBasic => GUI Central => Topic started by: LarryMc on April 30, 2009, 01:29:49 PM

Title: Saving DC to bitmap
Post by: LarryMc on April 30, 2009, 01:29:49 PM
I'm trying to adapt the bitmap save code for a EBasic window so it will work with my custom control
In my WM_PAINT handler I'm creating a compatible dc (for buffering) and writing all my "stuff" to it.

The line that updates the screen is (px is set with BeginPaint):_BitBlt(px, 0, 0, rc.right,rc.bottom, hdcMem, 0, 0, SRCCOPY)

Right after that line of code, I want to save what was displayed in the control to an external bitmap.
So I'm calling doprint(px,rc)
The file is being created with 121 bytes (which I figure is just a header)

sub doprint(hdc:uint,rc:winrect)
string filename=getstartpath+"testing.bmp"
def hdcWin,hdcComp,hbitmap,hbitmapold,quadsize:int
def info:BITMAPINFOHEADER
def fileheader:BITMAPFILEHEADER
def lpbits,lpbminfo:MEMORY
def fileb:BFILE
ZeroMemory(info,len(info))
ZeroMemory(fileheader,len(fileheader))
hdcComp = _CreateCompatibleDC(hdc)
hbitmap = _CreateCompatibleBitmap(hdc,rc.right,rc.bottom)
hbitmapold = _SelectObject(hdcComp,hbitmap)
_BitBlt(hdcComp,0,0,rc.right,rc.bottom,hdc,0,0,0x00CC0020)
_SelectObject(hdcComp,hbitmapold)
_DeleteDC(hdcComp)
quadsize = CreateInfoStructure(hbitmap,info)
REM allocate memory and get the 'bits' of the bitmap
allocmem lpbits,1,info.biSizeImage
allocmem lpbminfo,1,len(info) + quadsize
writemem lpbminfo,1,info
GetDIBits(hdcComp,hbitmap,0,info.biHeight,lpbits,lpbminfo,0)
REM open a binary file and write the header,color data and bitmap bits
if(openfile(fileb,filename,"W") = 0)
fileheader.bfType = 0x4d42
fileheader.bfSize = len(fileheader) + info.biSize + quadsize + info.biSizeImage
fileheader.bfOffBits = len(fileheader) + info.biSize + quadsize
write fileb,fileheader
write fileb,lpbminfo
write fileb,lpbits
closefile fileb
endif
freemem lpbminfo
freemem lpbits
_DeleteObject(hbitmap)
return
endsub

SUB CreateInfoStructure(hbitmap:uint,info:BITMAPINFOHEADER)
def bmp:BITMAP
def mem:MEMORY
def cClrBits:WORD
def quadsize:int : quadsize = 0
Allocmem mem,1,len(bmp)

GetObjectA(hbitmap,len(bmp),mem)
readmem mem,1,bmp
freemem mem
cClrBits = bmp.bmPlanes * bmp.bmBitsPixel
    if (cClrBits = 1)
        cClrBits = 1
    else
if (cClrBits <= 4)
      cClrBits = 4
    else
if (cClrBits <= 8)
        cClrBits = 8
    else
if (cClrBits <= 16)
        cClrBits = 16
    else
if (cClrBits <= 24)
        cClrBits = 24
    else
        cClrBits = 32
endif
endif
endif
endif
endif
    info.biSize = len(info)
    info.biWidth = bmp.bmWidth
    info.biHeight = bmp.bmHeight
    info.biPlanes = bmp.bmPlanes
    info.biBitCount = bmp.bmBitsPixel
    if (cClrBits < 24)
        info.biClrUsed = 2^cClrBits
endif
info.biCompression = 0
info.biSizeImage = (info.biWidth + 7.0) / 8.0 * info.biHeight * cClrBits
if(cClrBits < 24)
quadsize = 4 * (2^cClrBits)
endif
RETURN quadsize
ENDSUB


Don't know what to juggle to fix it.

Larry
Title: Re: Saving DC to bitmap
Post by: sapero on April 30, 2009, 02:01:00 PM
I have another solution, maybe easier (designed for bitmaps with width and height multiple by two)$include "windowssdk.inc"
'$define SNAP_NONCLIENT

SaveScreenshot(FindWindow(0, "Emergence BASIC - [SaveScreenshot.eba]"), "desktop.bmp", FALSE)


sub SaveScreenshot(HWND hwnd, LPTSTR path, BOOL fNonClient)

BITMAPINFOHEADER bmiHeader
BITMAPFILEHEADER filehdr
WINRECT rc

ZeroMemory(&bmiHeader, len(bmiHeader))

bmiHeader.biSize = len(BITMAPINFOHEADER)

if (fNonClient)
GetWindowRect(hwnd, &rc)
HDC hdcSource = GetWindowDC(hwnd)
else
GetClientRect(hwnd, &rc)
hdcSource = GetDC(hwnd)
endif

bmiHeader.biWidth    = rc.right - rc.left ' target bitmap size
bmiHeader.biHeight   = rc.bottom - rc.top
bmiHeader.biPlanes   = 1
bmiHeader.biBitCount = 24


pointer pBits ' 24bit: RGBTRIPLE*   32bit:RGBQUAD*
HBITMAP hbmImage = CreateDIBSection(hdcSource, &bmiHeader, DIB_RGB_COLORS, &pBits, 0, 0)

HDC hdcMemory = CreateCompatibleDC(hdcSource)
hbmImage = SelectObject(hdcMemory, hbmImage)

BitBlt(hdcMemory,0,0, bmiHeader.biWidth, bmiHeader.biHeight, hdcSource, 0, 0, SRCCOPY | CAPTUREBLT)

hbmImage = SelectObject(hdcMemory, hbmImage)
DeleteDC(hdcMemory)
ReleaseDC(hwnd, hdcSource)

HANDLE hFile = CreateFile(path, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0)

DWORD bilen = bmiHeader.biWidth * bmiHeader.biHeight * bmiHeader.biBitCount/8
DWORD bytes

filehdr.bfType      = 0x4d42 'MB'
filehdr.bfOffBits   = len(BITMAPFILEHEADER) + len(BITMAPINFOHEADER)
filehdr.bfSize      = len(BITMAPFILEHEADER) + len(BITMAPINFOHEADER) + bilen
filehdr.bfReserved1 = 0
filehdr.bfReserved2 = 0

WriteFile(hFile, &filehdr, len(filehdr), &bytes, NULL)
WriteFile(hFile, &bmiHeader, len(bmiHeader), &bytes, NULL)
WriteFile(hFile, pBits, bilen, &bytes, NULL)
CloseHandle(hFile)

DeleteObject(hbmImage)
endsub
Title: Re: Saving DC to bitmap
Post by: LarryMc on April 30, 2009, 04:40:51 PM
I got a ton errors.
In the program I'm working on I don't use your sdk.inc.
I use Paul's windows.inc

Got it to compile and create a bmp file but it only output 40 bytes; no actual image.

TYPE BITMAPFILEHEADER
DEF bfType:WORD
DEF bfSize:INT
DEF bfReserved1:WORD
DEF bfReserved2:WORD
DEF bfOffBits:INT
ENDTYPE

TYPE SECURITY_ATTRIBUTES
DEF nLength:INT
DEF lpSecurityDescriptor:INT
DEF bInheritHandle:INT
ENDTYPE

TYPE OVERLAPPED
DEF ternal:INT
DEF ternalHigh:INT
DEF offset:INT
DEF OffsetHigh:INT
DEF hEvent:INT
ENDTYPE
CONST CAPTUREBLT = &H40000000
CONST GENERIC_WRITE = &H40000000
CONST CREATE_ALWAYS = 2

DECLARE "user32.dll",GetWindowDC(hwnd:INT),INT
DECLARE "gdi32.dll",CreateDIBSection(hDC:INT, pBitmapInfo:BITMAPINFO, un:INT, lplpVoid:INT, handle:INT, dw:INT),INT

sub SaveScreenshot(hwnd:uint, path:string, fNonClient:int)

BITMAPINFOHEADER bmiHeader
BITMAPFILEHEADER filehdr
WINRECT rc
OVERLAPPED ol
ZeroMemory(ol, len(ol))
ZeroMemory(bmiHeader, len(bmiHeader))

bmiHeader.biSize = len(BITMAPINFOHEADER)

if (fNonClient)
_GetWindowRect(hwnd, rc)
int hdcSource = GetWindowDC(hwnd)
else
_GetClientRect(hwnd, rc)
hdcSource = _GetDC(hwnd)
endif

bmiHeader.biWidth    = rc.right - rc.left ' target bitmap size
bmiHeader.biHeight   = rc.bottom - rc.top
bmiHeader.biPlanes   = 1
bmiHeader.biBitCount = 24


pointer pBits ' 24bit: RGBTRIPLE*   32bit:RGBQUAD*
uint hbmImage = CreateDIBSection(hdcSource, bmiHeader, DIB_RGB_COLORS, pBits, 0, 0)

uint hdcMemory = _CreateCompatibleDC(hdcSource)
hbmImage = _SelectObject(hdcMemory, hbmImage)

_BitBlt(hdcMemory,0,0, bmiHeader.biWidth, bmiHeader.biHeight, hdcSource, 0, 0, SRCCOPY | CAPTUREBLT)

hbmImage = _SelectObject(hdcMemory, hbmImage)
_DeleteDC(hdcMemory)
_ReleaseDC(hwnd, hdcSource)

int hFile = _CreateFile(path, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0)

int bilen = bmiHeader.biWidth * bmiHeader.biHeight * bmiHeader.biBitCount/8
pointer bytes=null

filehdr.bfType      = 0x4d42 'MB'
filehdr.bfOffBits   = len(BITMAPFILEHEADER) + len(BITMAPINFOHEADER)
filehdr.bfSize      = len(BITMAPFILEHEADER) + len(BITMAPINFOHEADER) + bilen
filehdr.bfReserved1 = 0
filehdr.bfReserved2 = 0

_WriteFile(hFile, filehdr, len(filehdr), bytes, ol)
_WriteFile(hFile, bmiHeader, len(bmiHeader), bytes, ol)
_WriteFile(hFile, pBits, bilen, bytes, ol)
_CloseHandle(hFile)

_DeleteObject(hbmImage)
endsub


Larry
Title: Re: Saving DC to bitmap
Post by: Ionic Wind Support Team on April 30, 2009, 08:37:25 PM
Larry,
Get the structures from the draw.eba sample, specifically SaveBitmap.inc which is included with your installation.

TYPE BITMAPFILEHEADER

should be

TYPE BITMAPFILEHEADER,1

Because it requires 1 byte packing.

Paul.
Title: Re: Saving DC to bitmap
Post by: sapero on May 01, 2009, 12:35:07 AM
Larry, it creates 40 byte file if the hwnd is invalid. Do you remember win.hwnd ?
By the way here is the port for windows.inc:$include "windows.inc"

SaveScreenshot(_GetDesktopWindow(), "desktop.bmp", FALSE)

TYPE _BITMAPFILEHEADER,2
DEF bfType AS WORD
DEF bfSize AS INT
DEF bfReserved1 AS WORD
DEF bfReserved2 AS WORD
DEF bfOffBits AS INT
ENDTYPE

sub SaveScreenshot(int hwnd, string path, int fNonClient)

BITMAPINFOHEADER bmiHeader
_BITMAPFILEHEADER filehdr
WINRECT rc

if (!_IsWindow(hwnd)) then return _MessageBeep(MB_ICONHAND)
_RtlZeroMemory(&bmiHeader, len(bmiHeader))

bmiHeader.biSize = len(bmiHeader)

if (fNonClient)
_GetWindowRect(hwnd, rc)
int hdcSource = _GetWindowDC(hwnd)
else
_GetClientRect(hwnd, rc)
hdcSource = _GetDC(hwnd)
endif

bmiHeader.biWidth    = rc.right - rc.left ' target bitmap size
bmiHeader.biHeight   = rc.bottom - rc.top
bmiHeader.biPlanes   = 1
bmiHeader.biBitCount = 24


pointer pBits ' 24bit: RGBTRIPLE*   32bit:RGBQUAD*
int hbmImage = _CreateDIBSection(hdcSource, bmiHeader, DIB_RGB_COLORS, &pBits, 0, 0)

int hdcMemory = _CreateCompatibleDC(hdcSource)
hbmImage = _SelectObject(hdcMemory, hbmImage)

_BitBlt(hdcMemory,0,0, bmiHeader.biWidth, bmiHeader.biHeight, hdcSource, 0, 0, SRCCOPY)

hbmImage = _SelectObject(hdcMemory, hbmImage)
_DeleteDC(hdcMemory)
_ReleaseDC(hwnd, hdcSource)

BFILE hFile
Openfile hFile, path, "W"

'int bilen = bmiHeader.biWidth * bmiHeader.biHeight * bmiHeader.biBitCount/8
BITMAP bm
_GetObject(hbmImage, len(bm), bm)
int bilen = bm.bmWidthBytes * bm.bmHeight

filehdr.bfType      = 0x4d42 'MB'
filehdr.bfOffBits   = len(filehdr) + len(bmiHeader)
filehdr.bfSize      = len(filehdr) + len(bmiHeader) + bilen
filehdr.bfReserved1 = 0
filehdr.bfReserved2 = 0

__write hFile, *<string>&filehdr, len(filehdr)
__write hFile, *<string>&bmiHeader, len(bmiHeader)
__write hFile, *<string>pBits, bilen
CloseFile hFile

_DeleteObject(hbmImage)
endsub
Title: Re: Saving DC to bitmap
Post by: LarryMc on May 01, 2009, 04:56:50 AM
Sapero

I now get a bitmap but had/have 2 issues.

1. I had to comment out ImageFilter(hbmImage) or it wouldn't compile.
    update: I see from your post to fasecero that the above was just a typo here.
2. The saved bitmap will load into MSPaint and it looks fine.
    If I go to thumbnail view in windows explorer the bitmap isn't displayed; just the generic thumbnail for a bitmap.
    If I try to preview it in explorer it just displays "Drawing failed." in the center of the window.

All my other bitmaps display just fine.

Larry
Title: Re: Saving DC to bitmap
Post by: sapero on May 01, 2009, 05:38:02 AM
Yes, the ImageFilter was from another thread, code updated
I have modified the code two times, first added _BITMAPFILEHEADER definition, then changed len(struct name) to len(variable name) because there was still a reference to the inproperly declared BITMAPFILEHEADER (windows.inc). But it always displayed fine in totalcommander preivew tool (F3 key).
The images it produces now are correct.
Title: Re: Saving DC to bitmap
Post by: LarryMc on May 01, 2009, 05:52:20 AM
I updated with your revisions and still have the same problem as #2 above.

ok with MSPaint
ok with irfanwiew
won't open in MS PictureIt 2002

Larry
Title: Re: Saving DC to bitmap
Post by: sapero on May 01, 2009, 06:21:46 AM
Ok updated again, the original code was designed for bitmaps with width and height multiple by two. Changed bilen variable:
BITMAP bm
_GetObject(hbmImage, len(bm), bm)
int bilen = bm.bmWidthBytes * bm.bmHeight


294*295, 295*295, and 295*294 bitmap now opens in pic/fax viewer and office picture viewer.
Title: Re: Saving DC to bitmap
Post by: LarryMc on May 01, 2009, 06:23:56 AM
Sapero

It works perfect now.

Thank you so very much, as always!

Larry