March 28, 2024, 12:27:26 PM

News:

IonicWind Snippit Manager 2.xx Released!  Install it on a memory stick and take it with you!  With or without IWBasic!


Saving DC to bitmap

Started by LarryMc, April 30, 2009, 01:29:49 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

LarryMc

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
LarryMc
Larry McCaughn :)
Author of IWB+, Custom Button Designer library, Custom Chart Designer library, Snippet Manager, IWGrid control library, LM_Image control library

sapero

April 30, 2009, 02:01:00 PM #1 Last Edit: May 01, 2009, 06:17:55 AM by sapero
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

LarryMc

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
LarryMc
Larry McCaughn :)
Author of IWB+, Custom Button Designer library, Custom Chart Designer library, Snippet Manager, IWGrid control library, LM_Image control library

Ionic Wind Support Team

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.
Ionic Wind Support Team

sapero

May 01, 2009, 12:35:07 AM #4 Last Edit: May 01, 2009, 06:19:18 AM by sapero
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

LarryMc

May 01, 2009, 04:56:50 AM #5 Last Edit: May 01, 2009, 05:02:25 AM by Larry McCaughn
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
LarryMc
Larry McCaughn :)
Author of IWB+, Custom Button Designer library, Custom Chart Designer library, Snippet Manager, IWGrid control library, LM_Image control library

sapero

May 01, 2009, 05:38:02 AM #6 Last Edit: May 01, 2009, 05:41:50 AM by sapero
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.

LarryMc

May 01, 2009, 05:52:20 AM #7 Last Edit: May 01, 2009, 06:01:04 AM by Larry McCaughn
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
LarryMc
Larry McCaughn :)
Author of IWB+, Custom Button Designer library, Custom Chart Designer library, Snippet Manager, IWGrid control library, LM_Image control library

sapero

May 01, 2009, 06:21:46 AM #8 Last Edit: May 01, 2009, 06:25:58 AM by sapero
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.

LarryMc

Sapero

It works perfect now.

Thank you so very much, as always!

Larry
LarryMc
Larry McCaughn :)
Author of IWB+, Custom Button Designer library, Custom Chart Designer library, Snippet Manager, IWGrid control library, LM_Image control library