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)