How to use GdipCreateBitmapFromGdiDib from GDI+ [WORKAROUND]

Just starting out? Need help? Post your questions and find answers here.
coder14
Enthusiast
Enthusiast
Posts: 327
Joined: Tue Jun 21, 2011 10:39 am

How to use GdipCreateBitmapFromGdiDib from GDI+ [WORKAROUND]

Post by coder14 »

Can someone help me with an example to use this function please:

Code: Select all

GdipCreateBitmapFromGdiDib(@gdiBitmapInfo.BitmapInfo, *gdiBitmapData, @*bitmap)
I am trying to use it with a DIB that I get from EZTWAIN.dll according this this example:
http://www.purebasic.fr/english/viewtop ... 12&t=42010

I think I can replace the first param with the DIB but I don't know what the second param is.

Thank you. :)
Last edited by coder14 on Sun Jan 08, 2017 5:50 am, edited 2 times in total.
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: How to use GdipCreateBitmapFromGdiDib from GDI+

Post by netmaestro »

Consider the following code:

Code: Select all

dc = CreateDC_("DISPLAY",0,0,0)
ndc = CreateCompatibleDC_(0)

bmi.BITMAPINFO
bmi\bmiHeader\biSize     = SizeOf(BITMAPINFOHEADER) 
bmi\bmiHeader\biWidth    = 400
bmi\bmiHeader\biHeight   = -400 ; Otherwise colors in the buffer will be backwards, "bottom-up" which we don't want
bmi\bmiHeader\biPlanes   = 1 
bmi\bmiHeader\biBitCount = 32 
bmi\bmiHeader\biCompression = #BI_RGB 

hImage = CreateDIBSection_(ndc, bmi, #DIB_RGB_COLORS, @*pvBits, 0, 0)

DeleteDC_(dc)
DeleteDC_(ndc)

GetObject_(hImage, SizeOf(BITMAP), @bmp.BITMAP)

Debug bmp\bmBits 
Debug *pvbits

DeleteObject_(bmp)
You would code that prototype as follows:

Code: Select all

GdipCreateBitmapFromGdiDib(@bmi, bmp\bmBits, @*bitmap)
The key here is GetObject_(), which allows you access to the pointer to the bits.
If you're inside a StartDrawing() block for a PB image, you can alternatively use DrawingBuffer() to get the pointer.
BERESHEIT
coder14
Enthusiast
Enthusiast
Posts: 327
Joined: Tue Jun 21, 2011 10:39 am

Re: How to use GdipCreateBitmapFromGdiDib from GDI+

Post by coder14 »

netmaestro wrote:Consider the following code:

Code: Select all

dc = CreateDC_("DISPLAY",0,0,0)
ndc = CreateCompatibleDC_(0)

bmi.BITMAPINFO
bmi\bmiHeader\biSize     = SizeOf(BITMAPINFOHEADER) 
bmi\bmiHeader\biWidth    = 400
bmi\bmiHeader\biHeight   = -400 ; Otherwise colors in the buffer will be backwards, "bottom-up" which we don't want
bmi\bmiHeader\biPlanes   = 1 
bmi\bmiHeader\biBitCount = 32 
bmi\bmiHeader\biCompression = #BI_RGB 

hImage = CreateDIBSection_(ndc, bmi, #DIB_RGB_COLORS, @*pvBits, 0, 0)

DeleteDC_(dc)
DeleteDC_(ndc)

GetObject_(hImage, SizeOf(BITMAP), @bmp.BITMAP)

Debug bmp\bmBits 
Debug *pvbits

DeleteObject_(bmp)
You would code that prototype as follows:

Code: Select all

GdipCreateBitmapFromGdiDib(@bmi, bmp\bmBits, @*bitmap)
The key here is GetObject_(), which allows you access to the pointer to the bits.
If you're inside a StartDrawing() block for a PB image, you can alternatively use DrawingBuffer() to get the pointer.
Thanks netmaestro! I'm slowly getting some idea.

But your code creates a new DIB and BMInfo. How do I get the BMInfo of an already created DIB?

I tried modifying your code with:

Code: Select all

GetDIBits_(dc, hDIB, 0, 1, NULL, @bmi, #DIB_RGB_COLORS)
GdipCreateBitmapFromGdiDib(@bmi, hDIB, @bmp)  ;imported from gdiplus.dll
SetGadgetState(0, bmp)  ;image gadget
but it did not work. :oops:
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: How to use GdipCreateBitmapFromGdiDib from GDI+

Post by netmaestro »

Actually you probably don't want to use that API anyway. The problem with it is that it will do fine for 24bit images with no alpha channel but as soon as you want to use a 32bit image with alpha channel you find that GdipCreateBitmapFromGdiDib won't copy the alpha layer. Your background will be black and there really isn't a good way to correct this. Luckily you have an alternative. You need the BITMAPINFO object and a pointer to the colorbits, right? There's another gdiplus command that doesn't need the BITMAPINFO stuff, just the pointer to the colorbits and some easily-gotten metrics and it willl preserve the alpha channel: GdipCreateBitmapFromScan0. So I'll show you how to use that one instead:

Code: Select all

#MatrixOrderPrepend          = 0 
#MatrixOrderAppend           = 1 

#PixelFormat32bppARGB        = $26200A

Import "gdiplus.lib"
  GdiplusStartup(a,b,c)
  GdiplusShutdown(a)
  GdipDrawImageRectI(a,b,c,d,e,f)
  GdipDisposeImage(a) 
  GdipCreateFromHDC(a,b)
  GdipDeleteGraphics(a)
  GdipReleaseDC(a,b)
  GdipCreateBitmapFromScan0(a,b,c,d,e,f)
  GdipRotateWorldTransform(a, b.f, c)
  GdipTranslateWorldTransform(a, b.f, c.f, d)
  GdipResetWorldTransform(a)
EndImport

Structure GdiplusStartupInput
  GdiPlusVersion.l
  *DebugEventCallback.DEBUG_EVENT
  SuppressBackgroundThread.l
  SuppressExternalCodecs.l
EndStructure

CreateImage(1, 256, 256, 32, #PB_Image_Transparent)

StartDrawing(ImageOutput(1))
DrawingMode(#PB_2DDrawing_AllChannels)
Box(56,56,144,144,RGBA(0,0,210,255))
StopDrawing()

; Initialize GDIPlus
input.GdiplusStartupInput\GdiPlusVersion = 1
GdiplusStartup(@*token, @input, #Null)

; Create our bitmap
GetObject_(ImageID(1), SizeOf(BITMAP), @bmp.BITMAP)
GdipCreateBitmapFromScan0(256, 256, bmp\bmWidthBytes, #PixelFormat32bppARGB, bmp\bmBits, @*gdip_bitmap)

; Now let's have some fun with it:
OpenWindow(0,0,0,256,256,"GdipCreateBitmapFromScan0 Demo",#PB_Window_ScreenCentered|#PB_Window_SystemMenu)
AddWindowTimer(0,1,5)
CanvasGadget(0,0,0,256,256)
angle.f = 0
Repeat
  EventID = WaitWindowEvent() 
  Select EventID
    Case #PB_Event_Timer
      CreateImage(0, 256, 256, 32, #PB_Image_Transparent)
      hDC = StartDrawing(CanvasOutput(0))
      Box(0,0,256,256,GetSysColor_(#COLOR_3DFACE))
      GdipCreateFromHDC(hDC, @*gfxcontext)
      GdipRotateWorldTransform(*gfxcontext, angle, #MatrixOrderPrepend)
      GdipTranslateWorldTransform(*gfxcontext, 128, 128, #MatrixOrderAppend)
      GdipDrawImageRectI(*gfxcontext, *gdip_bitmap, 128.0, 128.0, -256, -256)
      GdipReleaseDC(*gfxcontext, hDC)
      GdipDeleteGraphics(*gfxcontext)
      StopDrawing()
      angle+0.1:If angle>=360:angle=0:EndIf
  EndSelect
  
Until EventID = #PB_Event_CloseWindow

GdiplusShutdown(*token)
BERESHEIT
coder14
Enthusiast
Enthusiast
Posts: 327
Joined: Tue Jun 21, 2011 10:39 am

Re: How to use GdipCreateBitmapFromGdiDib from GDI+

Post by coder14 »

netmaestro wrote:Actually you probably don't want to use that API anyway. The problem with it is that it will do fine for 24bit images with no alpha channel but as soon as you want to use a 32bit image with alpha channel you find that GdipCreateBitmapFromGdiDib won't copy the alpha layer. Your background will be black and there really isn't a good way to correct this. Luckily you have an alternative. You need the BITMAPINFO object and a pointer to the colorbits, right? There's another gdiplus command that doesn't need the BITMAPINFO stuff, just the pointer to the colorbits and some easily-gotten metrics and it willl preserve the alpha channel: GdipCreateBitmapFromScan0. So I'll show you how to use that one instead:

Code: Select all

#MatrixOrderPrepend          = 0 
#MatrixOrderAppend           = 1 

#PixelFormat32bppARGB        = $26200A

Import "gdiplus.lib"
  GdiplusStartup(a,b,c)
  GdiplusShutdown(a)
  GdipDrawImageRectI(a,b,c,d,e,f)
  GdipDisposeImage(a) 
  GdipCreateFromHDC(a,b)
  GdipDeleteGraphics(a)
  GdipReleaseDC(a,b)
  GdipCreateBitmapFromScan0(a,b,c,d,e,f)
  GdipRotateWorldTransform(a, b.f, c)
  GdipTranslateWorldTransform(a, b.f, c.f, d)
  GdipResetWorldTransform(a)
EndImport

Structure GdiplusStartupInput
  GdiPlusVersion.l
  *DebugEventCallback.DEBUG_EVENT
  SuppressBackgroundThread.l
  SuppressExternalCodecs.l
EndStructure

CreateImage(1, 256, 256, 32, #PB_Image_Transparent)

StartDrawing(ImageOutput(1))
DrawingMode(#PB_2DDrawing_AllChannels)
Box(56,56,144,144,RGBA(0,0,210,255))
StopDrawing()

; Initialize GDIPlus
input.GdiplusStartupInput\GdiPlusVersion = 1
GdiplusStartup(@*token, @input, #Null)

; Create our bitmap
GetObject_(ImageID(1), SizeOf(BITMAP), @bmp.BITMAP)
GdipCreateBitmapFromScan0(256, 256, bmp\bmWidthBytes, #PixelFormat32bppARGB, bmp\bmBits, @*gdip_bitmap)

; Now let's have some fun with it:
OpenWindow(0,0,0,256,256,"GdipCreateBitmapFromScan0 Demo",#PB_Window_ScreenCentered|#PB_Window_SystemMenu)
AddWindowTimer(0,1,5)
CanvasGadget(0,0,0,256,256)
angle.f = 0
Repeat
  EventID = WaitWindowEvent() 
  Select EventID
    Case #PB_Event_Timer
      CreateImage(0, 256, 256, 32, #PB_Image_Transparent)
      hDC = StartDrawing(CanvasOutput(0))
      Box(0,0,256,256,GetSysColor_(#COLOR_3DFACE))
      GdipCreateFromHDC(hDC, @*gfxcontext)
      GdipRotateWorldTransform(*gfxcontext, angle, #MatrixOrderPrepend)
      GdipTranslateWorldTransform(*gfxcontext, 128, 128, #MatrixOrderAppend)
      GdipDrawImageRectI(*gfxcontext, *gdip_bitmap, 128.0, 128.0, -256, -256)
      GdipReleaseDC(*gfxcontext, hDC)
      GdipDeleteGraphics(*gfxcontext)
      StopDrawing()
      angle+0.1:If angle>=360:angle=0:EndIf
  EndSelect
  
Until EventID = #PB_Event_CloseWindow

GdiplusShutdown(*token)
Thank you netmaestro for the great code. :D So nice! :D

I am trying to figure out where to insert the DIB that I want to convert to bitmap? My intention is to use the DIB that is returned by EZTW32.dll. harkon wrote a routine in EZTwain.pbi to do that but it sometimes hiccups.

http://www.purebasic.fr/english/viewtop ... 12&t=42010

So I thought that this can do the job. :P
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: How to use GdipCreateBitmapFromGdiDib from GDI+

Post by netmaestro »

I don't know the TWAIN stuff, sorry. I only know gdiplus because I'm a veteran of many battles against it with no losses recorded. (ok, a few concussions resulting in intermittent dementia and large patches of hair gone from my scalp, but NO LOSSES) ...cost/benefit analysis? I'm afraid to even look...
BERESHEIT
coder14
Enthusiast
Enthusiast
Posts: 327
Joined: Tue Jun 21, 2011 10:39 am

Re: How to use GdipCreateBitmapFromGdiDib from GDI+

Post by coder14 »

netmaestro wrote:I don't know the TWAIN stuff, sorry. I only know gdiplus because I'm a veteran of many battles against it with no losses recorded. (ok, a few concussions resulting in intermittent dementia and large patches of hair gone from my scalp, but NO LOSSES) ...cost/benefit analysis? I'm afraid to even look...
:lol: :lol: :lol:

I doubt it! :D

But will the DIB be the same format? :?:
User avatar
TI-994A
Addict
Addict
Posts: 2741
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Re: How to use GdipCreateBitmapFromGdiDib from GDI+

Post by TI-994A »

coder14 wrote:I am trying to use it with a DIB that I get from EZTWAIN.dll...
An adapted version of harkon's code (working, though not optimised):

Code: Select all

Procedure displayBMP(hDIB)
  
  Define *pDIB.BITMAPINFOHEADER = GlobalLock_(hDIB)
  Define bmpFH.BITMAPFILEHEADER
  
  bitCount = *pDIB\biBitCount
  clrsUsed = *pDIB\biClrUsed
  
  If clrsUsed = 0 And bitCount <= 8
    clrsUsed = 1 << bitCount
  EndIf
  
  If *pDIB\biCompression = #BI_RGB
    bytesPerRow = (((*pDIB\biWidth * bitCount) + 31) / 32) * 4
    *pDIB\biSizeImage = bytesPerRow * *pDIB\biHeight
  ElseIf *pDIB\biSizeImage = 0
    ProcedureReturn #False
  EndIf
  
  With bmpFH
    \bfType = $4d42   ;ASCII for 'BM'
    \bfReserved1 = 0
    \bfReserved2 = 0
    \bfOffBits = SizeOf(BITMAPFILEHEADER) +
                 SizeOf(BITMAPINFOHEADER) +
                 SizeOf(RGBQUAD) * clrsUsed
    \bfSize = \bfOffBits + *pDIB\biSizeImage  
  EndWith 
  
  If bitCount < 24
    If clrsUsed = 0 And bitCount <= 8
      palletteSize = (1<<bitCount) * 4
    EndIf
  EndIf  
  
  bfhSize = SizeOf(bmpFH)
  
  bmpOffset = bfhSize + *pDIB\biSize + palletteSize
  bmpSize = bmpOffset + *pDIB\biSizeImage  
  
  *bmpBuffer = AllocateMemory(bmpSize)
  CopyMemory(@bmpFH, *bmpBuffer, bfhSize)
  CopyMemory(*pDIB, *bmpBuffer + bfhSize, bmpSize - bfhSize)
  imgNo = CatchImage(#PB_Any, *bmpBuffer, bmpSize)  
  SetGadgetState(#imgGadgetNo, ImageID(imgNo))
  
  GlobalUnlock_(hDIB)
  
  FreeImage(imgNo)
  FreeMemory(*bmpBuffer)  
  
  ProcedureReturn #True
  
EndProcedure
The example displays the captured bitmap (imgNo) onto an image gadget (#imgGadgetNo). However, it could also be saved to file using any of PureBasic's image encoders.

It resolves the flipped image issue with greyscale bitmaps, although pixel-type 3 is still not resulting in an RGB image, as per TWAIN specifications.

Tested with EZTW32.dll running PureBasic v5.31 (x86) on Windows 8.1.

Hope it helps. :wink:
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel :D
coder14
Enthusiast
Enthusiast
Posts: 327
Joined: Tue Jun 21, 2011 10:39 am

Re: How to use GdipCreateBitmapFromGdiDib from GDI+

Post by coder14 »

TI-994A wrote:
coder14 wrote:I am trying to use it with a DIB that I get from EZTWAIN.dll...
An adapted version of harkon's code (working, though not optimised):

Code: Select all

Procedure displayBMP(hDIB)
  
  Define *pDIB.BITMAPINFOHEADER = GlobalLock_(hDIB)
  Define bmpFH.BITMAPFILEHEADER
  
  bitCount = *pDIB\biBitCount
  clrsUsed = *pDIB\biClrUsed
  
  If clrsUsed = 0 And bitCount <= 8
    clrsUsed = 1 << bitCount
  EndIf
  
  If *pDIB\biCompression = #BI_RGB
    bytesPerRow = (((*pDIB\biWidth * bitCount) + 31) / 32) * 4
    *pDIB\biSizeImage = bytesPerRow * *pDIB\biHeight
  ElseIf *pDIB\biSizeImage = 0
    ProcedureReturn #False
  EndIf
  
  With bmpFH
    \bfType = $4d42   ;ASCII for 'BM'
    \bfReserved1 = 0
    \bfReserved2 = 0
    \bfOffBits = SizeOf(BITMAPFILEHEADER) +
                 SizeOf(BITMAPINFOHEADER) +
                 SizeOf(RGBQUAD) * clrsUsed
    \bfSize = \bfOffBits + *pDIB\biSizeImage  
  EndWith 
  
  If bitCount < 24
    If clrsUsed = 0 And bitCount <= 8
      palletteSize = (1<<bitCount) * 4
    EndIf
  EndIf  
  
  bfhSize = SizeOf(bmpFH)
  
  bmpOffset = bfhSize + *pDIB\biSize + palletteSize
  bmpSize = bmpOffset + *pDIB\biSizeImage  
  
  *bmpBuffer = AllocateMemory(bmpSize)
  CopyMemory(@bmpFH, *bmpBuffer, bfhSize)
  CopyMemory(*pDIB, *bmpBuffer + bfhSize, bmpSize - bfhSize)
  imgNo = CatchImage(#PB_Any, *bmpBuffer, bmpSize)  
  SetGadgetState(#imgGadgetNo, ImageID(imgNo))
  
  GlobalUnlock_(hDIB)
  
  FreeImage(imgNo)
  FreeMemory(*bmpBuffer)  
  
  ProcedureReturn #True
  
EndProcedure
The example displays the captured bitmap (imgNo) onto an image gadget (#imgGadgetNo). However, it could also be saved to file using any of PureBasic's image encoders.

It resolves the flipped image issue with greyscale bitmaps, although pixel-type 3 is still not resulting in an RGB image, as per TWAIN specifications.

Tested with EZTW32.dll running PureBasic v5.31 (x86) on Windows 8.1.

Hope it helps. :wink:
THANK YOU TI-994A!!! :D :D :D You are a genius!

I know it can be displayed but just did not know how. This is the best because no new image is created besides poking the existing DIB into memory and catching the image. I can display it and save it! It works for color and monochrome scans but the pixel-type mismatch still exist. 3 should be RGB.

Thank you netmaestro for the many many solutions. You are a gdiplus hero! :D But the problme is I am not. :oops: I modified your codes many many times but just could not get the right combination. Finally it is harkon's hkTWAIN_WriteNativeToJPG code that did it.

Thanks TI-994A for cleaning it up for me. :D

PS: Why do you say "not optimized"? :?:
Post Reply