Announcement

Collapse
No announcement yet.

speed of GDIPlus images on a GRAPHIC WINDOW

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • speed of GDIPlus images on a GRAPHIC WINDOW

    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
    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
    application:
    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
Working...
X