I was pretty disappointed with my first attempts, so I wrote this little application to see just how fast one image could be dragged across another. I quickly learned two things - one, it was my code that made the original application slow, two - the combination of GDIPlus and PBCC is pretty impressive.
This is nothing like a finished application - just some quick and dirty code to investigate the technique. I did find that with high quality there was noticeable flicker, so I made the quality a global variable and "turned it down" during the drag operation, then redrew at high quailty when the left mouse button was released.
If you want to try it out, you will need the application and include file (below) and two image files - JPGs are fine, photograhpic images are fine - just change the code in the PBMAIN to refer to your files.
include file picodgip.inc
application:
This is nothing like a finished application - just some quick and dirty code to investigate the technique. I did find that with high quality there was noticeable flicker, so I made the quality a global variable and "turned it down" during the drag operation, then redrew at high quailty when the left mouse button was released.
If you want to try it out, you will need the application and include file (below) and two image files - JPGs are fine, photograhpic images are fine - just change the code in the PBMAIN to refer to your files.
include file picodgip.inc
Code:
'-------------------------------------------------------------------------------- ' picoGDIP.inc placed in public domain by Chris Holbrook May 2008 ' ' based on code by Messrs Terrier, Schullian, Walker & others, thanks to Simon Morgan for debugging. ' modified Jan 2008: globals no longer used ' Dec 2008: some redundant code removed '-------------------------------------------------------------------------------- 'GDIP CONSTANTS %QualityModeHigh = 2 ' Best rendering quality %UnitPixel = 2 ' Each unit is one device pixel. '-------------------------------------------------------------------------------- ' pico-GDIP.inc ' '----------------------------------------------------------------------------------------------------------------------- ' type GdiplusStartupInput GdiplusVersion as dword '// Must be 1 DebugEventCallback as dword '// Ignored on free builds SuppressBackgroundThread as long '// FALSE unless you're prepared to call '// the hook/unhook functions properly SuppressExternalCodecs as long '// FALSE unless you want GDI+ only to use '// its internal image codecs. end type ' '----------------------------------------------------------------------------------------------------------------------- ' type GdiplusStartupOutput ' // The following 2 fields are NULL if SuppressBackgroundThread is FALSE. ' // Otherwise, they are functions which must be called appropriately to ' // replace the background thread. ' // ' // These should be called on the application's main message loop - i.e. ' // a message loop which is active for the lifetime of GDI+. ' // "NotificationHook" should be called before starting the loop, ' // and "NotificationUnhook" should be called after the loop ends. NotificationHook as dword NotificationUnhook as dword end type ' '----------------------------------------------------------------------------------------------------------------------- type ImageCodecInfo ClassID as guid '// CLSID. Codec identifier FormatID as guid '// GUID. File format identifier CodecName as dword '// WCHAR*. Pointer to a null-terminated string '// that contains the codec name DllName as dword '// WCHAR*. Pointer to a null-terminated string '// that contains the path name of the DLL in '// which the codec resides. If the codec is not '// a DLL, this pointer is NULL FormatDescription as dword '// WCHAR*. Pointer to a null-terminated string '// that contains the name of the format used by the codec FilenameExtension as dword '// WCHAR*. Pointer to a null-terminated string '// that contains all file-name extensions associated '// with the codec. The extensions are separated with semicolons. MimeType as dword '// WCHAR*. Pointer to a null-terminated string '// that contains the mime type of the codec Flags as dword '// Combination of flags from the ImageCodecFlags enumeration Version as dword '// Integer that indicates the version of the codec SigCount as dword '// Integer that indicates the number of signatures '// used by the file format associated with the codec SigSize as dword '// Integer that indicates the number of bytes of each signature SigPattern as dword '// BYTE*. Pointer to an array of bytes that contains '// the pattern for each signature SigMask as dword '// BYTE*. Pointer to an array of bytes that contains '// the mask for each signature end type '----------------------------------------------------------------------------------------------------------------------- ' declare function GdipSetInterpolationMode lib "gdiplus.dll" alias "GdipSetInterpolationMode" (byval graphics as long, byval interpolation as long) as long declare function GdipDrawImageRectI lib "gdiplus.dll" alias "GdipDrawImageRectI" (byval graphics as long, byval nImage as long, byval x as long, byval y as long, byval nWidth as long, byval Height as long) as long declare function GdipGetImageWidth lib "gdiplus.dll" alias "GdipGetImageWidth" (byval nImage as long, nWidth as long) as long declare function GdipGetImageHeight lib "gdiplus.dll" alias "GdipGetImageHeight" (byval nImage as long, Height as long) as long declare function GdipDeleteGraphics lib "gdiplus.dll" alias "GdipDeleteGraphics" (byval graphics as long) as long declare function GdipGraphicsClear lib "gdiplus.dll" alias "GdipGraphicsClear" (byval graphics as long, byval lColor as long) as long declare function GdipCreateFromHWND lib "gdiplus.dll" alias "GdipCreateFromHWND" (byval hwnd as long, graphics as long) as long declare function GdipCreateFromHDC lib "gdiplus.dll" alias "GdipCreateFromHDC" (byval hdc as long, graphics as long) as long declare function GdipGetImagePixelFormat lib "gdiplus.dll" alias "GdipGetImagePixelFormat" _ (byval nImage as long, PixelFormat as long) as long declare function GdipGetImageDimension lib "gdiplus.dll" alias "GdipGetImageDimension" _ (byval nImage as long, nWidth as single, Height as single) as long declare function GdipGetImageGraphicsContext lib "gdiplus.dll" alias "GdipGetImageGraphicsContext" _ (byval nImage as long, graphics as long) as long declare function GdiplusStartup lib "GDIPLUS.DLL" alias "GdiplusStartup" _ (token as dword, inputbuf as GdiplusStartupInput, outputbuf as GdiplusStartupOutput) as long declare sub GdiplusShutdown lib "GDIPLUS.DLL" alias "GdiplusShutdown" _ (byval token as dword) declare function GdipLoadImageFromFile lib "GDIPLUS.DLL" alias "GdipLoadImageFromFile" _ (byval flname as string, lpImage as dword) as long declare function GdipDisposeImage lib "GDIPLUS.DLL" alias "GdipDisposeImage" _ (byval lpImage as dword) as long declare function GdipCreateBitmapFromScan0 lib "gdiplus.dll" alias "GdipCreateBitmapFromScan0" _ (byval nWidth as long, byval Height as long, byval stride as long, byval PixelFormat as long, _ scan0 as any, nBitmap as long) as long declare function GdipGetInterpolationMode lib "gdiplus.dll" alias "GdipGetInterpolationMode" _ (byval graphics as long, interpolation as long) as long declare function GdipDrawImageRectRectI lib "gdiplus.dll" alias "GdipDrawImageRectRectI" _ (byval graphics as long, byval nImage as long, byval dstx as long, byval dsty as long, _ byval dstwidth as long, byval dstheight as long, byval srcx as long, byval srcy as long, _ byval srcwidth as long, byval srcheight as long, byval srcUnit as long, _ optional byval imageAttributes as long, optional byval pCALLBACK as long, _ optional byval callbackData as long) as long declare sub skIconise (byval xPic as long, byval yPic as long, _ picture dimensions byval xCell as long, byval yCell as long, _ FRAME dimensions byref xOfs as long, byref yOfs as long, _ calc'd offset in frame byref xSize as long, byref ySize as long) ' thumbnail dimensions ' '----------------------------------------------------------------------------------------------------------------------- ' write resized image to file and/or window. function ResizeImage (picpath as string, hW as dword, maxwidth as dword, maxheight as dword) as long local origwidth as single local origheight as single local lPixelFormat as long local s as string local extn as string local imagestyle as string local lresult, OfsX, OfsY, Tx, Ty as long local hbitmap, himage, graphics as dword local encoderCLSID as GUIDAPI GDIPLoadImageFromFile ucode$(picPath), hImage ' if hImage = 0 then ' return image handle value of zero to indicate failure exit function end if ' GdipGetImageDimension hImage, origwidth, origheight ' skIconise ( origwidth, origheight, maxwidth, maxheight, ofsX, ofsY, tX, tY) ' GdipGetImagePixelFormat hImage, lPixelFormat ' create right-size bitmap GdipCreateBitmapFromScan0 tX, tY, 0, lPixelFormat, byval 0&, hBitmap ' get graphics handle to bitmap GdipGetImageGraphicsContext hBitmap, graphics GdipSetInterpolationMode graphics, %QualityModeHigh ' drop image into graphics object GdipDrawImageRectRectI graphics, hImage, 0, 0, tX, tY, 0, 0, origwidth, origheight, %UnitPixel ' get rid of GDI objects call GdipDeleteGraphics(graphics) deleteobject hbitmap function = himage end function ' '------------------------------------------------------------------------------------------------------- ' Computes location and size to stretch a bitmap preserving its aspect. ' sub skIconise (byval xPic as long, byval yPic as long, _ picture dimensions byval xCell as long, byval yCell as long, _ FRAME dimensions byref xOfs as long, byref yOfs as long, _ calc'd offset in frame byref xSize as long, byref ySize as long) export ' thumbnail dimensions local scale as single if xPIC& then scale! = xCell& / xPic& if scale! > 1 then scale! = 1 xSize& = xPic& * scale!: ySize& = yPic& * scale! ' In case Height > 150 compute new scale factor if ySize& > yCell& then if yPic& then scale! = yCell& / yPic& xSize& = xPic& * scale!: ySize& = yPic& * scale! end if xOfs& = (xCell& - xSize&) \ 2 yOfs& = (yCell& - ySize&) \ 2 end sub
Code:
'to investigate speed of GDIPlus image handling on a GW ' Chris Holbrook 13 Dec 2008 #compile exe #dim all #include once "win32api.inc" #include once "picogdip.inc" global GrDialogProc as long ' used in subclassing the grapic control to get mouse msgs global GrOldProc as long ' used in subclassing the grapic control to get mouse msgs global currentrect, mainrect as rect global hWin, hGW as dword global gdip_image, gdip_mainimage as dword global gdip_quality as long '-------------------------------------------------------------------------------- function GrProc(byval hWnd as dword, byval wMsg as dword, byval wParam as dword, byval lParam as long) as long static startpt, endpt as pointAPI static htimer as long select case wMsg ' case %wm_mousemove if wparam and %MK_LBUTTON then ' we're dragging gdip_quality = 1 endpt.X = lo(word,lParam) endpt.Y = hi(word,lParam) drawit(startpt, endpt) startpt = endPT end if ' case %wm_lbuttondown startpt.X = lo(word,lParam) startpt.Y = lo(word,lParam) ' case %wm_lbuttonup gdip_quality = 2 drawit(startpt, endpt) startpt.X = 0: startpt.Y = 0 ' end select function = CallWindowProc(GrOldProc, hWnd, wMsg, wParam, lParam) end function '--------------------------------------------------------------------- sub ShowImage ( r as rect, gdip_image as dword) local hdc, hG as dword local tx, ty, framex, framey, ofsx, ofsy as long local wide, high as single global gdip_quality as long graphic get dc to hDC frameX = r.nRight - r.nLeft frameY = r.nBottom - r.nTop GdipGetImageDimension gdip_Image, wide, high ' calc coordinates to centre image in frame, retaining original proportion skIconise ( wide, high, frameX, frameY, ofsX, ofsY, tX, tY) if GdipCreatefromHDC ( hDC, hG) = 0 then if hG <> 0 then ' can also set smoothing mode, etc here GdipSetInterpolationMode(hG, gdip_quality) '%QualityModeLow =1 %qualitymodehigh = 2 '? r.nleft, r.ntop, r.nright, r.nbottom '? frameX, frameY, ofsX, ofsY, tX, tY GdipDrawImageRectI(hG, gdip_image, r.nleft + 2, _ r.ntop + 2, _ tX - 2*2, tY - 2 * 2 ) if hG <> 0 then gdipdeletegraphics hG end if end if end sub '---------------------------------------------------------------------- sub drawit ( startpt as pointapi, endpt as pointapi) local colour as long local r as rect colour = %black offsetrect currentrect, endpt.X - startpt.X , endpt.Y - startpt.Y ? currentrect.nleft, max(70, currentrect.ntop),currentrect.nright, currentrect.nbottom graphic attach hwin, 0, redraw graphic box (0, 70) - (500, 500), 0, -2, -1, 0 showimage (mainrect, gdip_mainimage) r = currentrect r.ntop = max(70, r.ntop) r.nbottom = max(150, r.nbottom) showimage (r, gdip_image) graphic redraw end sub '----------------------------------------------------------------------------------- function pbmain () as long local skey as string local PICPATH as string static nstatus as long static token as dword static StartupInput as GdiplusStartupInput StartupInput.GdiplusVersion = 1 nStatus = GdiplusStartup(token, StartupInput, byval %NULL) if nStatus then ? "Error initializing GDI+", %mb_applmodal, "Warning" exit function end if ' Create and show a Graphic window on screen graphic window "", 450, 130, 500, 500 to hWin graphic attach hWin, 0, redraw hGW = GetWindow(hWin, %GW_CHILD) ' Retrieve hWnd of graphic window ' Subclass the Graphic control GrOldProc = SetWindowLong(hGW, %GWL_WNDPROC, codeptr(GrProc)) setrect mainrect, 0, 100, 500, 400 setrect currentrect, 100, 70, 250, 250 gdip_image = resizeimage ( "test.jpg", HWIN, _ currentrect.nright - currentrect.nleft, _ currentrect.nbottom - currentrect.ntop ) gdip_mainimage = resizeimage ( "pulled.jpg", HWIN, _ mainrect.nright - mainrect.nleft, _ mainrect.nbottom - mainrect.ntop ) showimage(mainrect, gdip_mainimage) showimage(currentrect, gdip_image) 'lastpt = gMousePt graphic print "to investigate speed of GDIPlus image handling on a GW by Chris Holbrook 13-DEC-2008" graphic print "drag an image on a GRAPHIC WINDOW" graphic print "start with cursor in the graphic window(this one)" graphic print "Use the mouse with left button down to drag it" graphic print "<Esc> to exit" graphic redraw ' main program loop ' wait for the punter to press ESC then exit ' let the subclass proc do the work do skey = "" graphic inkey$ to skey if skey$ = $esc then exit loop sleep 50 loop SetWindowLong(hGW, %GWL_WNDPROC, GrOldProc) ' remove subclassing graphic window end GdiplusShutdown token end function