Draw text or graphics directly onto a JPG file

Here’s some code to manipulate photos using the GDIPlus class library that ships with VFP 9. It creates a GPImage object out of a JPG, then creates a GPGraphics object, draws some text and an ellipse on that object, then saves it to a new filename. The new jpg is then drawn in VFP.

I use similar code to date/timestamp my photos.

To run the code, copy a JPG (preferably a digital photo) to c:\kids.jpg, or alter the code appropriately.

Colors for GDI are 256 levels of Red, Green and Blue, and 256 levels of Alpha (transparency). Thus 0xF0102030 means ARGB where Alpha=0xf0, Red is 0x10, Green is 0x20 and Blue is 0x30

The ellipse is drawn with a gradient brush.

GDIPlus is not initialized in the multithreaded runtime, so GdiplusStartup and GdiplusShutdown must be called explicitly. My next blog post will call this code in the runtime.

CLEAR

WriteJpg("c:\kids.jpg","Fox Rox!") && Change input JPG filename

SET CLASSLIB TO HOME()+"ffc\_gdiplus"

oImage=CREATEOBJECT("gpimage","c:\t.jpg")

oGraphics=CREATEOBJECT("gpgraphics")

oGraphics.CreateFromHWND(_screen.HWnd)

orect= CREATEOBJECT("gprectangle",0,0,800,600)

oGraphics.DrawImageScaled(oImage,oRect)

PROCEDURE WriteJpg(cFilename,cNewText)

      TRY

            gdipToken=0

            IF _vfp.StartMode=0

                  SET CLASSLIB TO HOME()+"ffc\_gdiplus"

            ELSE

                  SET CLASSLIB TO c:\_gdiplus

                  IF _vfp.StartMode = 5

                        DECLARE INTEGER GdiplusStartup in gdiplus integer @ lpToken,string @ GdiplusStartupInput_lpInput,integer ptr_lpOutput

                        DECLARE INTEGER GdiplusShutdown in gdiplus integer lToken

                        gdipToken=0

                        gdipStartupStruct=CHR(1)+REPLICATE(CHR(0),4*4-1)

                        GdiplusStartup(@gdipToken,@gdipStartupStruct,0)

                  ENDIF

            ENDIF

           

            LOCAL oGraphics as gpgraphics

            LOCAL oBrush as gpsolidbrush

            LOCAL orect as gprectangle

            LOCAL oImage as gpimage

            oImage=CREATEOBJECT("gpimage",cFilename)

            oGraphics=CREATEOBJECT("gpgraphics")

            ltrim("next local")

            IF !oGraphics.CreateFromImage(oImage)

                  THROW "CreateFromImage "+cFilename

            ENDIF

            oGraphics.DrawStringA(cNewText+iif(_vfp.startmode=0,' Design ',' Runtime ')+TRANSFORM(DATETIME()),;

                  CREATEOBJECT("gpfont","Verdana",26,1,3),;

                  CREATEOBJECT("gprectangle",0,0,880,425),;

                  CREATEOBJECT("gpstringformat"),;

                  CREATEOBJECT("gpsolidbrush",0xcff00060)) && change the solidbrush color

            DECLARE integer GdipCreateLineBrushFromRect ;

                  IN gdiplus.dll string,integer,integer,integer, integer, integer @

            nlBrush=0

            orect= CREATEOBJECT("gprectangle",0,0,100,30)

            GdipCreateLineBrushFromRect(oRect.GdipRectF,;

                  0xffff0000,0xff00ff00,3,1,@nlBrush)

            oBrush=CREATEOBJECT("gphatchbrush",4)

            oBrush.SetHandle(nlBrush)

            oGraphics.FillEllipse(oBrush,;

                  CREATEOBJECT("gprectangle",400,600,680,325))

            IF !oImage.SaveToFile("c:\t.jpg","image/jpeg")

                  THROW "error saving"

            ENDIF

      CATCH TO oexcept

            DECLARE integer MessageBoxA IN WIN32API integer, string,string,integer

      MessageBoxA(0,oexcept.UserValue+":"+oexcept.message+":"+oexcept.details+":"+TRANSFORM(oExcept.lineno),"",0)

      ENDTRY

      IF gdipToken>0

            GdiplusShutdown(gdipToken)

      ENDIF

RETURN