Announcement

Collapse
No announcement yet.

challenged by GDI+

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

  • challenged by GDI+

    A couple of years ago I cobbled together (mostly from examples here) enough GDI+ code to allow basic picture-handling and it has served my purpose until today (see code below). Now I need to extend it so as to capture the image - somehow - after it has been read from a JPG/PNG/BMP/GIF/TIF/ICO file, so that I can store it into a database. It would be childs play to read the file and store a copy of that in the database, but then to redisplay it I would have to write it out again, which is a bit daft.

    So the challenge is a) to store it (I need a pointer and a length for this) and b) to restore the stored object via a GDI+ function in order to redisplay the picture.

    Because the initial window size could be small, storing and reusing the initial bitmap might not be a good idea.

    Any ideas?

    Code:
    '--------------------------------------------------------------------------------
    ' MiniGDIP.inc  placed in public domain by Chris Holbrook June 2006
    '
    ' based on code by Messrs Roca, Terrier, Schullian, Walker & others, thanks to Simon Morgan for debugging.
    ' modified Jan 2008: globals no longer used
    '--------------------------------------------------------------------------------
    'GDIP CONSTANTS
    %QualityModeHigh = 2       ' Best rendering quality
    %UnitPixel       = 2 ' Each unit is one device pixel.
    '--------------------------------------------------------------------------------
    '
    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                                                                                    bitblt
    '
    '-----------------------------------------------------------------------------------------------------------------------
    '
    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 GdipCreateBitmapFromHBITMAP LIB "gdiplus.dll" ALIAS "GdipCreateBitmapFromHBITMAP" (BYVAL hbm AS LONG, BYVAL hpal AS LONG, nBitmap 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 GdipGetImageEncodersSize LIB "GDIPLUS.DLL" ALIAS "GdipGetImageEncodersSize" _
                (numEncoders AS DWORD, nSize AS DWORD) AS LONG
    DECLARE FUNCTION GdipGetImageEncoders LIB "GDIPLUS.DLL" ALIAS "GdipGetImageEncoders" _
                (BYVAL numEncoders AS DWORD, BYVAL nSize AS DWORD, BYVAL lpEncoders AS DWORD) AS LONG
    DECLARE FUNCTION GdipSaveImageToFile LIB "GDIPLUS.DLL" ALIAS "GdipSaveImageToFile" _
                (BYVAL lpImage AS DWORD, BYVAL flname AS STRING, clsidEncoder AS GUID, _
                 OPTIONAL BYVAL EncoderParams 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 FUNCTION GdipGetSmoothingMode LIB "gdiplus.dll" ALIAS "GdipGetSmoothingMode" _
                (BYVAL graphics AS LONG, SmoothingMd 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
    '
    '-----------------------------------------------------------------------------------------------------------------------
    '
    FUNCTION ReadUnicodeString (BYVAL lp AS DWORD) AS STRING
       LOCAL p AS BYTE PTR, s AS STRING
       p = lp                             '// Pointer to the string
       IF p = %Null THEN EXIT FUNCTION    '// Null pointer
       WHILE CHR$(@p) <> $NUL
          s = s + CHR$(@p)
          p = p + 2                       '// Unicode strings require two bytes per character
       WEND
       FUNCTION = s
    END FUNCTION
    '
    '-----------------------------------------------------------------------------------------------------------------------
    '
    ' ==========================================================================
    ' GetEncoderClsid
    ' The function GetEncoderClsid in the following example receives the MIME
    ' type of an encoder and returns the class identifier (CLSID) of that encoder.
    ' The MIME types of the encoders built into GDI+ are as follows:
    '   image/bmp
    '   image/jpeg
    '   image/gif
    '   image/tiff
    '   image/png
    ' ==========================================================================
    FUNCTION GetEncoderClsid (BYVAL sMimeType AS STRING) AS STRING
       DIM pImageCodecInfo AS ImageCodecInfo PTR
       LOCAL numEncoders AS DWORD, nSize AS DWORD
       LOCAL lRslt AS LONG, i AS LONG, x AS LONG
       LOCAL p AS BYTE PTR, s AS STRING
       LOCAL nSigCount AS LONG, nSigSize AS LONG
       sMimeType = UCASE$(sMimeType)
       lRslt = GdipGetImageEncodersSize(numEncoders, nSize)
       REDIM buffer(nSize - 1) AS BYTE
       pImageCodecInfo = VARPTR(buffer(0))
       lRslt = GdipGetImageEncoders(numEncoders, nSize, pImageCodecInfo)
       IF lRslt = 0 THEN
          FOR i = 1 TO numEncoders
             IF INSTR(UCASE$(ReadUnicodeString(@pImageCodecInfo.MimeType)), sMimeType) THEN
                FUNCTION = GUIDTXT$(@pImageCodecInfo.ClassID)
                EXIT FOR
             END IF
             INCR pImageCodecInfo       '// Increments pointer
          NEXT
       END IF
    END FUNCTION
    '
    '-----------------------------------------------------------------------------------------------------------------------
    ' write resized image to file and/or window. If filename is blank, file will not be created.
    '                                            If window handle is null, window will not be painted.
    FUNCTION ResizeImage (picpath AS STRING, destpath 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 EncoderClSID AS GUID
        LOCAL extn AS STRING
        LOCAL imagestyle AS STRING
        LOCAL OfsX, OfsY, Tx, Ty AS LONG
        LOCAL hbitmap, himage, graphics AS DWORD
        '
    
        IF destpath <> "" THEN ' if destpath is not specified we still load the global image
            extn = UCASE$(PARSE$(destpath,".",-1))
            SELECT CASE CONST$ extn
                 CASE "BMP"         : imagestyle = "image/bmp"
                 CASE "EMF"         : imagestyle = "image/x-emf"
                 CASE "GIF"         : imagestyle = "image/gif"
                 CASE "ICO"         : imagestyle = "image/x-icon"
                 CASE "JPG", "JPEG" : imagestyle = "image/jpeg"
                 CASE "PNG"         : imagestyle = "image/png"
                 CASE "TIF", "TIFF" : imagestyle = "image/tiff"
                 CASE "WMF"         : imagestyle = "image/x-wmf"
                 CASE ELSE
                     ' return image handle value of zero to indicate failure
                      EXIT FUNCTION
            END SELECT
        END IF
        '
        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
        ' save resized image to file if output filename is not null
        IF destpath <> "" THEN
            s = GetEncoderClsid(imagestyle)
            EncoderCLSID = GUID$(s)
            GdipSaveImageToFile hbitmap, UCODE$(DestPath), EncoderCLSID, %NULL
        END IF
        ' get rid of unwanted 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

  • #2
    Since I first looked for GDI+ info a couple of years ago a lot more stuff has appeared on the www. For strictly PB people Jose Roca's forums are a good place to look.

    So far it looks as if half my question has been answered by an example of Jose Roca's using GdipCreateBitmapFromStream. I can modify this to load the image which I store in my database.

    Haven't yet worked out how to save the image though. The mechanics of getting it in and out of the blob field are not a problem, just how to get from a valid image handle to a memory address and byte count to create the stored form of the image.

    Comment


    • #3
      The stream is an IStream interface.
      Fairly simple to use..
      A simple 'gather all data into a buffer' situation.
      hellobasic

      Comment


      • #4
        Following examples by Jose Roca and Kev Peel, I have discovered that the GDI+ bitmap functions return bitmap handles which are not compatible with the GDI ones. Fortunately there are conversion functions in GDI+, so the bitmap can eventually be manipulated using GetDIBits, for example.

        From my testing code - sorry it's not compilable but I'm in a hurry!

        Code:
            ' create a memory DC
            hDC = CreateCompatibleDC ( 0 )
            ' Create a GDIP+ Bitmap object from a JPEG file.
            lresult = GdipCreateBitmapFromFile(UCODE$(gsPicPath), pBitmap)
            ' get a GDI bitmap handle for it
            lresult = GdipCreateHBITMAPFromBitmap(pBitmap, hbitmap, BYVAL -1)
            ' get bitmap stats into the BMINFO structure
            bitbuffer = STRING$(SIZEOF(BITMAPINFOHEADER), $NUL)
            pBMINFO = STRPTR(bitbuffer)
            @pBMINFO.biSize = SIZEOF(BITMAPINFOHEADER)
        
            lresult = GetDIBits(hDC, hBitMap, 0, 0, BYVAL %NULL, BYVAL pBMINFO, %DIB_RGB_COLORS)
        
            ?  "result " + STR$(lresult) + $CRLF + _
               "bitmap " + STR$(hbitmap) + $CRLF + _
               "size   " + STR$(@pBMINFO.bisize) + $CRLF + _
               "width  " + STR$(@pBMINFO.biwidth) + $CRLF + _
               "height " + STR$(@pBMINFO.biheight) + $CRLF + _
               "planes " + STR$(@pBMINFO.biplanes) + $CRLF + _
               "bits per pixel               " + STR$(@pBMINFO.bibitcount) + $CRLF + _
               "compression                  " + STR$(@pBMINFO.bicompression) + $CRLF + _
               "size of image(bytes)         " + STR$(@pBMINFO.bisizeimage) + $CRLF + _
               "pixels per meter horizontal  " + STR$(@pBMINFO.biXPelsPerMeter) + $CRLF + _
               "pixels per meter vertical    " + STR$(@pBMINFO.biYPelsPerMeter) + $CRLF + _
               "no of color indices used     " + STR$(@pBMINFO.biClrUsed) + $CRLF + _
               "no of important color indices" + STR$(@pBMINFO.biClrImportant)

        Comment


        • #5
          Originally posted by Edwin Knoppert View Post
          The stream is an IStream interface.
          Fairly simple to use..
          A simple 'gather all data into a buffer' situation.
          Is the format of the image saved by GdiPSaveImageToStream the same as the image format on disc, I wonder? Or is it a generic format?

          If the image is saved to a stream, the length is not returned*, any ideas how to derive the length (size) of the image written to a stream?

          Good compilable example code by Messrs Terrier & Roca for reading from an Istream using GdipLoadImageFromStream.

          * most GDIplus functions appear to return either zero for success, or an error code. I have posted an application including an error code list (it's from Jose Roca's forum) on the Source Code forum.
          Last edited by Chris Holbrook; 2 Aug 2008, 05:06 PM. Reason: added links

          Comment


          • #6
            If you save it to disk as a stream, then you will have to load it using GdipLoadImageFromStream. This function returns an Image handle, that you can use with all the functions that accept such and handle, like GdipGetImageWidth and GdipGetImageHeight.
            Forum: http://www.jose.it-berater.org/smfforum/index.php

            Comment


            • #7
              Originally posted by José Roca View Post
              If you save it to disk as a stream...
              There are the functions GdipSaveImageToStream and GdipSaveImageToFile. Or do you mean that there is a function to write a stream to a file? Could not find it in the structured storage stuff.

              (later) OK, I've found IStream_Stat, which gives cbsize, so can use the stream as a buffer, then

              Code:
              FILE            <<<<<<< APP >>>>>>>       DB      <<<<<<<< APP >>>>>>>
              image file -> image handle -> istream -> blob -> istream -> image handle
                                  |                                           |
                                  +-------------------------------------------+
                                                       |
                                                   show image

              Comment


              • #8
                Or do you mean that there is a function to write a stream to a file? Could not find it in the structured storage stuff.
                No, you have to open or create an storage file with StgCreateStorageEx, create an stream in it with IStorage_CreateStream and write the bytes in it. Later, you can reopen the storage file and get a pointer to the stream with IStorage_OpenStream, pointer that you can pass to GdipLoadImageFromStream to load the stream as a GDI+ image.
                Forum: http://www.jose.it-berater.org/smfforum/index.php

                Comment

                Working...
                X