Page 2 of 3

Posted: Sat Nov 11, 2006 9:15 pm
by netmaestro
Documentation on gdiplus is notoriously scarce. Best way to get help on a command that I've found is to google the function name and you'll find examples in VB, Delphi etc. that shows you how to use it. For a complete listing of available commands, you can examine the library functions of gdiplus.dll in a loop.

For obtaining a gdiplus image from a PB image, once you have the dll open and a successful startup of gdiplus, you can execute this command:

Code: Select all

CallFunction(0, "GdipCreateBitmapFromHBITMAP", ImageID(#pbimg), 0, @*image)
and *image will point to a valid gdiplus image.

To make a homemade CatchImage for an IncludeBinary-ed image, here's a snippet from my TransparentClock program:

Code: Select all

  ; Load Clock Base Image 
  Length = ?baseend-?base 
  g1 = GlobalAlloc_(#GHND, Length) 
  *buf1 = GlobalLock_(g1) 
  CopyMemory(?base, *buf1, Length) 
  If CreateStreamOnHGlobal_(*buf1, 1, @stream1.ISTREAM) = #S_OK 
    CallFunction(0, "GdipCreateBitmapFromStream", stream1 , @*base) 
  Else 
    MessageRequester("Error","Problem creating images", #MB_ICONERROR) 
    End 
  EndIf

Posted: Sun Nov 12, 2006 3:28 am
by netmaestro
Got around to doing a speed test on CreateFileThumbnail. Time to load, resize and save 100 JPEG's from 1280*1024 to 100*75: 4.5 seconds. 100 PNG's took 7.9 seconds. Fwiw, I think these times for this quality of resampling are pretty decent.

(AMD 3800 X2, 2g Ram, NVidia 7300GT, Holley 4bbl carbs)

Posted: Sun Nov 12, 2006 12:38 pm
by dige
@netmaestro:
I guess its better to init gdi plus only once, is'nt it?

Code: Select all

    input.GdiplusStartupInput 
    input\GdiPlusVersion = 1 
    Startup( @*token, @input, #Null) 
did you have some gdi information about loseless jpeg rotation?

Posted: Sun Nov 12, 2006 4:06 pm
by wilbert

Posted: Sun Nov 12, 2006 6:27 pm
by netmaestro
@dige, I don't know what "lossless jpeg rotation" means. Could you clarify?

Posted: Sun Nov 12, 2006 11:35 pm
by mskuma
It means opening a JPEG, rotating it (e.g.) 90 degrees and saving it out without recompressing it, i.e. rearrange the data internally.

Posted: Sun Nov 12, 2006 11:53 pm
by netmaestro
Ok, thanks. Those "standard" flips and rotations are highly optimized in gdiplus for speed and quality. They are all accomplished with one command:

Code: Select all

CallFunction(0, "GdipImageRotateFlip", *image, RotationConstant)
where 'RotationConstant' is one of the following values:

Code: Select all

#RotateNoneFlipNone = 0
#Rotate90FlipNone   = 1
#Rotate180FlipNone  = 2
#Rotate270FlipNone  = 3
#RotateNoneFlipX    = 4
#Rotate90FlipX      = 5
#Rotate180FlipX     = 6
#Rotate270FlipX     = 7
#RotateNoneFlipY    = #Rotate180FlipX
#Rotate90FlipY      = #Rotate270FlipX
#Rotate180FlipY     = #RotateNoneFlipX
#Rotate270FlipY     = #Rotate90FlipX
#RotateNoneFlipXY   = #Rotate180FlipNone
#Rotate90FlipXY     = #Rotate270FlipNone
#Rotate180FlipXY    = #RotateNoneFlipNone
#Rotate270FlipXY    = #Rotate90FlipNone

Posted: Mon Nov 13, 2006 6:15 am
by netmaestro
For more interesting rotations, here's a demo program to show the method. You can use the techniques shown here to build a really high-quality needle guage, clock, dial, etc. The "wow, cool!" possibilities are unlimited and PureBasic makes it all so easy.

You need this image to run the test: http://www.lloydsplace.com/wheeldisc.png

** Right-click the link and select "Save Target As" **

Code: Select all

;========================================================
;  Program:     GDIPlus Rotation Demo
;  Author:      netmaestro
;  Date:        November 12, 2006
;  Update:      February 8, 2020
;               Updated to use prototypes as
;               CallFunction no longer supports floats
;========================================================

#background = 0 ; image# for background

CompilerIf Defined(GdiplusStartupInput, #PB_Structure) = 0
  Structure GdiplusStartupInput
    GdiPlusVersion.l
    *DebugEventCallback.Debug_Event
    SuppressBackgroundThread.l
    SuppressExternalCodecs.l
  EndStructure
CompilerEndIf 

Prototype GdiplusStartup( *token, *input, mode )
Prototype GdipCreateBitmapFromFile(filename.p-bstr, *image)
Prototype GdipCreateFromHDC( hdc, *gfx)
Prototype GdipRotateWorldTransform( *gfx, angle.f, mode)
Prototype GdipResetWorldTransform( *gfx)
Prototype GdipTranslateWorldTransform( *gfx, wmidf.f, hmidf.f, mode)
Prototype GdipDrawImageRectI( *gfx, *image, x, y, Width, Height )
Prototype GdipDeleteGraphics( *gfx )
Prototype GdipDisposeImage( *image )
Prototype GdiplusShutdown( *token )

OpenLibrary(0, "gdiplus.dll")
 
Global GdiplusStartup.GdiplusStartup                           = GetFunction( 0, "GdiplusStartup" )         
Global GdipCreateBitmapFromFile.GdipCreateBitmapFromFile       = GetFunction( 0, "GdipCreateBitmapFromFile")
Global GdipCreateFromHDC.GdipCreateFromHDC                     = GetFunction( 0, "GdipCreateFromHDC" )     
Global GdipDrawImageRectI.GdipDrawImageRectI                   = GetFunction( 0, "GdipDrawImageRectI" )     
Global GdipRotateWorldTransform.GdipRotateWorldTransform       = GetFunction( 0, "GdipRotateWorldTransform" )     
Global GdipTranslateWorldTransform.GdipTranslateWorldTransform = GetFunction( 0, "GdipTranslateWorldTransform" )     
Global GdipResetWorldTransform.GdipResetWorldTransform         = GetFunction( 0, "GdipResetWorldTransform" )
Global GdipDeleteGraphics.GdipDeleteGraphics                   = GetFunction( 0, "GdipDeleteGraphics" )     
Global GdipDisposeImage.GdipDisposeImage                       = GetFunction( 0, "GdipDisposeImage" )       
Global GdiplusShutdown.GdiplusShutdown                         = GetFunction( 0, "GdiplusShutdown" ) 

Procedure InitGDIPlus()
  input.GdiplusStartupInput
  input\GdiPlusVersion = 1
  GdiplusStartup( @*token, @input, #Null)
  ProcedureReturn *token
EndProcedure

Procedure ShutDownGDIPlus(*token)
  GdiplusShutdown(*token)
  CloseLibrary(0)
EndProcedure

Procedure RotateImage(*image, bkgimage, angle.f)
  width=ImageWidth(bkgimage)
  height=ImageHeight(bkgimage)
  wmidf.f = width/2
  hmidf.f = height/2
  wmid.l = height/2
  hmid.l = height/2 
  hdc=StartDrawing(ImageOutput(bkgimage))
    Box(0,0,width,height,GetSysColor_(#COLOR_BTNFACE))
    GdipCreateFromHDC( hdc, @*surface)
    GdipRotateWorldTransform( *surface, angle, 1)
    GdipTranslateWorldTransform( *surface, wmidf, hmidf, 1)
    GdipDrawImageRectI( *surface, *image, wmid, hmid, -width, -height)
    GdipResetWorldTransform( *surface)                                     
  StopDrawing()
  GdipDeleteGraphics( *surface) 
EndProcedure

*token = InitGDIPlus()

GdipCreateBitmapFromFile("wheeldisc.png", @*wheeldisc)
CreateImage(0,256,256,24)

OpenWindow(0,0,0,300,320,"Rotation Demo",#PB_Window_ScreenCentered|#PB_Window_SystemMenu)

ImageGadget(0,15,15,0,0,0)
TrackBarGadget(1,20,285,260,20,0,300)
SetGadgetState(1,150)

Repeat
  EventID = WaitWindowEvent(1)
  angle.f+inc.f:If angle >=360:angle=0:EndIf
  RotateImage(*wheeldisc, #background, angle)

  SetGadgetState(0,ImageID(#background))
  inc.f = (GetGadgetState(1)-150)/100
  If inc>=-0.02 And inc<=0.02:inc=0:EndIf
Until EventID = #PB_Event_CloseWindow

ShutDownGDIPlus(*token) 

Posted: Mon Nov 13, 2006 8:38 am
by dige
Very very cooool! thx netmeastro!!!

"GdipImageRotateFlip" seems not the right one for loseless
Jpeg - Rotation, because you have to load, rotate and save
the Image again...

I'll check the API Link from wilbert ... the command should
sound like "TransformJpeg90" or so ...

Posted: Mon Nov 13, 2006 5:21 pm
by netmaestro
You're most welcome. For the lossless jpeg rotation, I'm sorry it's just not a concept I'm familiar with. A person could load, flip and save images using code I've posted in this thread, but for anything more than what's here I'm afraid I'm in the dark. If you find what you're looking for, I'd appreciate your posting it. I'm always up for learning something new. It's fun, isn't it?

Posted: Tue Nov 14, 2006 8:58 am
by Hurga
Nice code, but i thought about how to do it nativly in PB. (Just my tick)

(Have no time to test the idea, so no code, only the idea)

Could that work?

load the big image into mem (with #PB_IMAGE_MEMORY)
create the thumbnail into mem

calc the x and y ratio between both images (if big = 1000x1000 and the thumbnail ist 100 x 100, the ration is 10
step through the big image (in this case take every 10th pixel)
write it to the thumbnail image.

If I find time, maybe i´ll give it a try...

Posted: Tue Nov 14, 2006 9:12 am
by dige
jpeg, png etc. are compressed images ...

Posted: Sun Jun 10, 2007 6:15 am
by Baldrick
Last week while working on a card access control system I discovered the man in charge of the system was having to manually resize all card images from his digital camera to allow the small size needed for the photo software package of this system. With a bit of sniffing around this forum I came up with this thread & have now made a little utility to quickly resize images for this man ( which he does 30 - 40 images at a time ). Some of you might find it handy so here is a download link for the source.

Click here to download source

It pretty much uses Netmaestro's 1st snippet almost unaltered which served my purposes very nicely :)

Posted: Sun Jun 10, 2007 5:53 pm
by Pantcho!!
Yeah it's nice to help people :)

I did a resize program because one of my fellow students were taking pictures of the lectures in class and has to manual resize them so i create a little code that will resize it as he wish and also the file name match to the class name.

The coolest thing is that you can code this full program in little less then 2 hours or so unlike other programming languages :)

Posted: Fri May 08, 2009 1:23 pm
by Joakim Christiansen
Hmm, can't get netmaestro's function to work here on PB 4.30.
Invalid memory access on line 111 :S

EDIT:
This helps fixing the memory bug, but the code still doesn't work.

Code: Select all

  Protected *info.ImageCodecInfo
 
  GetImageEncodersSize( @num, @Size )
  *info = AllocateMemory(Size)
  GetImageEncoders( num, Size, *info )
 
  For i = 0 To num - 1
    If *info\MimeType = encoder2use
      clsid = *info\clsid
      Break
    EndIf
    *info + SizeOf(ImageCodecInfo)
  Next

Code: Select all

;; netmaestro's gdiplus Thumbnail File creator
;
;----------------------------------------------------------------------------
;                     GDIPlus Initialization Section
;----------------------------------------------------------------------------
;
CompilerIf Defined(GdiplusStartupInput, #PB_Structure) = 0
  Structure GdiplusStartupInput
    GdiPlusVersion.l
    *DebugCallback
    SuppressBackgroundThread.l
    SuppressExternalCodecs.l
  EndStructure
CompilerEndIf 

CompilerIf Defined(EncoderParameter, #PB_Structure) = 0
  Structure EncoderParameter
    count.l
    guid.GUID
    numberOfValues.l
    type.l
    *value
  EndStructure
CompilerEndIf

CompilerIf  Defined(ImageCodecInfo, #PB_Structure) = 0
  Structure ImageCodecInfo
    clsid.CLSID
    formatID.GUID
    codecName.s
    dllName.s
    formatDescription.s
    filenameExtension.s
    mimeType.s
    flags.l
    version.l
    sigCount.l
    sigSize.l
    *sigPattern
    *sigMask
  EndStructure
CompilerEndIf

Prototype GdiplusStartup( *token, *input, mode )
Prototype GdipCreateBitmapFromFile( Filename, *image )
Prototype GdipSaveImageToFile ( *image, Filename, encoder, params)
Prototype GdipGetImageEncodersSize( *num, *Size )
Prototype GdipGetImageEncoders( *num, *Size, *info.ImageCodecInfo)
Prototype GdipGetImageThumbnail(*image, tn_xsize, tn_ysize, *tn_image, *callbackproc, *callbackdata)
Prototype GdipDisposeImage( *image )
Prototype GdiplusShutdown( *token )

OpenLibrary(0, "gdiplus.dll")

Global Startup.GdiplusStartup                        = GetFunction( 0, "GdiplusStartup" )         
Global CreateBitmapFromFile.GdipCreateBitmapFromFile = GetFunction( 0, "GdipCreateBitmapFromFile" )
Global SaveImageToFile.GdipSaveImageToFile           = GetFunction( 0, "GdipSaveImageToFile" )
Global GetImageEncodersSize.GdipGetImageEncodersSize = GetFunction( 0, "GdipGetImageEncodersSize" )
Global GetImageEncoders.GdipGetImageEncoders         = GetFunction( 0, "GdipGetImageEncoders" )
Global GetImageThumbnail.GdipGetImageThumbnail       = GetFunction( 0, "GdipGetImageThumbnail" )
Global DisposeImage.GdipDisposeImage                 = GetFunction( 0, "GdipDisposeImage" )       
Global Shutdown.GdiplusShutdown                      = GetFunction( 0, "GdiplusShutdown" ) 
;     
;-----------------------------------------------------------------------------
;                      End GDIPlus Initialization Section
;-----------------------------------------------------------------------------

Procedure StringToBStr (string$) ; By Zapman Inspired by Fr34k
  Protected Unicode$ = Space(Len(String$)* 2 + 2)
  Protected bstr_string.l
  PokeS(@Unicode$, String$, -1, #PB_Unicode)
  bstr_string = SysAllocString_(@Unicode$)
  ProcedureReturn bstr_string
EndProcedure

ProcedureDLL SaveThumbnail(*image, decoder, filename.s)
 
  Protected encoder2use.s
  Select decoder
    Case 0
      encoder2use = "image/bmp"
    Case 1
      encoder2use = "image/jpeg"
    Case 2
      encoder2use = "image/gif"
    Case 3
      encoder2use = "image/tiff"
    Case 4
      encoder2use = "image/png"
    Default
      ProcedureReturn 0
  EndSelect
 
  Dim info.ImageCodecInfo(0)
 
  GetImageEncodersSize( @num, @Size )
  ReDim info.ImageCodecInfo(Size/SizeOf(ImageCodecInfo))
  GetImageEncoders( num, Size, @info(0) )
 
  For i = 0 To num - 1
    If PeekS(@info(i)\MimeType, -1, #PB_Unicode) = encoder2use
      clsid.l = info(i)\clsid
      Break
    EndIf
  Next
 
  If clsid
    If SaveImageToFile( *image, StringToBStr(filename), clsid, 0) = 0
      ProcedureReturn 1
    Else
      ProcedureReturn 0
    EndIf
  Else
    ProcedureReturn 0
  EndIf
 
EndProcedure

ProcedureDLL CreateFileThumbnail( xsize, ysize, Filename.s )
  Select LCase(GetExtensionPart(filename))
    Case "bmp" : encoder.l = 0
    Case "jpg" : encoder.l = 1
    Case "gif" : encoder.l = 2
    Case "tif" : encoder.l = 3
    Case "png" : encoder.l = 4
    Default : encoder.l = 0
  EndSelect
  input.GdiplusStartupInput
  input\GdiPlusVersion = 1
  Startup( @*token, @input, #Null)
  CreateBitmapFromFile( StringToBStr(Filename), @*image)
  GetImageThumbnail(*image, 382,202, @*tn_image, #Null, #Null)
  SaveThumbnail( *tn_image, encoder, "tn_"+filename )   
  DisposeImage( *image )
  DisposeImage( *tn_image )
  Shutdown( *token )
EndProcedure

CreateFileThumbnail( 100, 75, OpenFileRequester("","","",0)) ; input image can be any size; parameters are for thumbnail-out size

CloseLibrary(0)