2006-05-25

CROP IMAGES WITH GDI+

Cropping an image is a simple task for Gdiplus.
One of the ways that this can be done is using the GDI+ function gdipclonebitmapareai , that unfortunately was not included in _Gdiplus.vcx, but can be easily accessed.

Original Image



Top Left

Center



Bottom Right


Run the code below, selecting any image, and you will see the image cropped in three ways: the top-left part of the image, the bottom-right part, and the center.

LOCAL lcSource
lcSource = GETPICT()

IF EMPTY(lcSource)
RETURN
ENDIF
* Load image to GDI+
LOCAL loImage AS gpImage OF HOME() + ffc/_gdiplus.vcx
loImage = NEWOBJECT("gpImage",HOME()+"ffc/_gdiplus.vcx")
loImage.CreateFromFile(lcSource)
lnWidth = loImage.ImageWidth
lnHeight = loImage.ImageHeight

* Crop Image LOCAL loCropped as gpBitmap OF HOME() + ffc/_gdiplus.vcx

* Crop TopLeft
loCropped = Crop(loImage, 0, 0, lnWidth / 2, lnHeight /2)
loCropped.SavetoFile("crop-topleft.png","image/png")
RUN /n Explorer.exe crop-topleft.png

* Crop BottomRight
loCropped = Crop(loImage, lnWidth / 2, lnHeight /2, lnWidth /2, lnHeight /2)
loCropped.SavetoFile("crop-bottomright.png","image/png")
RUN /n Explorer.exe crop-bottomright.png

* CropCenter
loCropped = Crop(loImage, lnWidth / 4, lnHeight /4, lnWidth /2, lnHeight /2)
loCropped.SavetoFile("crop-center.png","image/png")
RUN /n Explorer.exe crop-center.png

RETURN

PROCEDURE Crop(toImage, x, y, tnWidth, tnHeight, tnPixelFormat)
IF VARTYPE(tnPixelFormat) = "l"
   tnPixelFormat = toImage.PixelFormat
ENDIF 
* gpstatus wingdipapi gdipclonebitmapareai
* (int x, int y, int width, int height, pixelformat format,
* gpbitmap *srcbitmap, gpbitmap **dstbitmap)
DECLARE LONG gdipclonebitmapareai IN gdiplus.dll ;
   LONG x, LONG y, LONG nwidth, LONG nheight, ;
   LONG pixelformat, LONG srcbitmap, LONG @dstbitmap
LOCAL lnNewBitmap
lnNewBitmap = 0
= gdipCloneBitmapAreaI(x, y, tnWidth, tnHeight, ;
tnPixelFormat, toImage.getHandle(), @lnNewBitmap)
LOCAL loNewImage
loNewImage = NEWOBJECT("gpBitmap",HOME()+"ffc/_gdiplus.vcx")
loNewImage.SetHandle(lnNewBitmap)
RETURN lonewimage
ENDPROC 


To achieve this, I created a small procedure, "crop", that receives the following parameters retrieved from msdn :

    toimage - image or bitmap object

    x - integer that specifies the x-coordinate of the upper-left corner of the rectangle that specifies the portion of this bitmap to copy.

    y - integer that specifies the y-coordinate of the upper-left corner of the rectangle that specifies the portion of this bitmap to copy.

    width - integer that specifies the width of the rectangle that specifies the portion of this bitmap to copy.

    height - integer that specifies the height of the rectangle that specifies the portion of this image to copy.

    format - optional, integer that specifies the pixel format of the new bitmap. the pixelformat data type and constants that represent various pixel formats are defined in gdiplus.h. for more information about pixel format constants, see image pixel format constants.

    return - this method returns a pointer to the new bitmap object containing the cropped image.

2006-05-16

PRINT INDIVIDUAL IMAGES

I've seen all over the web some examples showing how to print individual images directly to the printer.

I don't know why, but in some situations this approach simply denies to work. maybe, or probably a developer bug ?

One of the easiest and more secure ways to do this task is to use the native report designer, and let vfp deal with the whole process of printing.

Below there is an example that receives an image file name as a parameter, creates a report on the fly, adding an ole image object that will print the selected image.

This example is totally based in mskb 895602 "how to print pictures and how to display pictures that are stored in a blob field in visual foxpro 9.0" the adapted code should work on any version of vfp. thanks to trevor hancock and msdn ! the code is easy to understand. you can easily change the position of the image changing the hpos and vpos values. If you like the example and need more information, see "understanding and extending report structure" from the vfp help.

Save the code below as printimage.prg

To print an image, just call printimage(GETPICT())

LPARAMETERS tcImage
* tcImage = GETPICT()
*--------------------------------------------------------
* vfp code that shows how to print image files.
* code adapted from microsoft knowledge base article
* 895602. http://support.microsoft.com/kb/895602/en-us/
*
* most of the codes and comments below come from
* Trevor Hancock, from MS
*--------------------------------------------------------

LOCAL lnArea
m.lnArea = SELECT()

CREATE CURSOR ReportTemp (ImageFile c(150))
INSERT INTO ReportTemp VALUES (m.tcImage)
*-- this calls a function that makes a report programmatically.
*-- this is included here just to make sure that this sample can be run
*-- as-is, without asking the developer to manually create a report.
MakeReport()

*-- make sure that the cursor is selected,
*-- and then run the report to preview using
*-- the instance of our report listener.
SELECT ReportTemp
REPORT FORM ___ImageReport PREVIEW
DELETE FILE "___ImageReport.fr*"
SELECT (m.lnArea)

RETURN

*--------------------------------
*-- this function programmatically creates a report
*-- with an ole bound control and other fields. this is included
*-- only for demonstration purposes so this article code can stand-alone.
*-- typically, you would create your own report manually by using
*-- the report designer.
FUNCTION MakeReport
    CREATE REPORT ___ImageReport FROM ReportTemp
    *-- open the report file (frx) as a table.
    USE ___ImageReport.frx IN 0 ALIAS thereport EXCLUSIVE
    SELECT thereport
    *-- remove from the frx the auto generated fields and labels
    DELETE FROM thereport WHERE OBJTYPE = 5 AND OBJCODE = 0 && remove the labels
    DELETE FROM thereport WHERE OBJTYPE = 8 AND OBJCODE = 0 && remove the fields


    *-- add a picture/ole bound control to the report by inserting a
    *-- record with appropriate values. using an object that is based on the empty
    *-- class here and the gather name class later to insert the record makes it easier to
    *-- see which values line up to which fields (when compared to a large
    *-- sql-insert command).
    LOCAL loNewRecObj AS EMPTY
    m.loNewRecObj = NEWOBJECT( 'empty' )
    ADDPROPERTY( m.loNewRecObj, 'platform', 'windows' )
    ADDPROPERTY( m.loNewRecObj, 'uniqueid', SYS(2015) )
    ADDPROPERTY( m.loNewRecObj, 'objtype', 17 )&& "picture/ole bound control"
    ADDPROPERTY( m.loNewRecObj, 'name', 'reporttemp.imagefile' ) && the object ref to the image object.
    ADDPROPERTY( m.loNewRecObj, 'hpos', 100)
    ADDPROPERTY( m.loNewRecObj, 'vpos', 600)
    ADDPROPERTY( m.loNewRecObj, 'height', 100000)
    ADDPROPERTY( m.loNewRecObj, 'width', 100000)
    ADDPROPERTY( m.loNewRecObj, 'double', .T. ) && picture is centered in the "picture/ole bound control"
    ADDPROPERTY( m.loNewRecObj, 'supalways', .T. )
    *-- for the picture/ole bound control, the contents of the offset field specify whether
    *-- filename (0), general field name (1), or expression (2) is the source.
    ADDPROPERTY( m.loNewRecObj, 'offset', 2 )
    *-- add the picture/ole bound control record to the report.
    APPEND BLANK IN thereport
    GATHER NAME m.loNewRecObj MEMO
    *-- clean up and then close the report table.
    PACK MEMO
    USE IN SELECT( 'thereport' )
ENDFUNC