Enable crop and zooming in on your digital photograph display form


I wanted to add the ability to zoom into a portion of a photograph on my photo viewer. Below is some code that processes the MouseUp, MouseDown and MouseMove events to add a yellow dotted-dash selection rectangle using a Shape control that the mouse can resize over the desired portion of a picture to crop/zoom.


 


Using the image control with Stretch set to Isometric means to keep the image’s original proportions (so skinny people stay skinny). That means that if the image control’s aspect ratio (width/height) is different from that of the original picture, either the top/bottom edges or the left/right edges of the image surface will be blank. Thus, calculating the crop rectangle gets a little complicated.


 


An additional complication arises when the ShowWindow is set to show the form as a top level form. The hWnd needed to draw the image is the client window of the form. WindowFromPoint is called to get the window from an x/y position.


 


The DrawImagePortionScaled routine of _gdiplus.vcx is called to do the scaling and drawing.


 


Now I can zoom in on that book on the bookcase in the background of the photo to read the title and author!


 


PUBLIC x


x=NEWOBJECT("pform")


 


DEFINE CLASS pform as form


      left=00


      width=600


      height=300


      ShowWindow=1


      allowoutput=.f.


      ADD OBJECT img as myimage


      PROCEDURE init


            this.img.visible=1


            this.img.picture="d:\kids.jpg"


            this.img.height=thisform.height


            this.img.width=thisform.width


            this.visible=1


            DECLARE integer ValidateRect IN WIN32API integer, integer


ENDDEFINE


 


DEFINE CLASS myimage as Image


      stretch=1


      nState=0


      PROCEDURE mousemove(nBut,nShift,x,y)


            IF this.nState=1


                  IF x > thisform.dr.Left


                        thisform.dr.width=x - thisform.dr.Left


                  ENDIF


                  IF y > thisform.dr.Top


                        thisform.dr.height = y -thisform.dr.Top


                  ENDIF


            ENDIF


      PROCEDURE mousedown(nBut,nShift,x,y)


            IF this.nState>0


                  thisform.dr.visible=0


                  this.picture=this.picture


                  this.nState=0


                  RETURN


            ENDIF


            IF VARTYPE(thisform.dr)='U'


                  thisform.AddObject("dr","myshape")


            ENDIF


            thisform.dr.top=y


            thisform.dr.left = x


            thisform.dr.height=1


            thisform.dr.width=1


            thisform.dr.visible=1


            this.nState=1


      PROCEDURE mouseup(nBut,nShift,x,y)


            IF this.nState#1 


                  RETURN


            ENDIF


            this.nState =2


            thisform.dr.visible=.f.


            thisform.img.picture=thisform.img.picture &&paint over sel rect


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


            LOCAL oGraphics as gpGraphics OF (HOME()+"ffc\_gdiplus")


            LOCAL oRectDest as gpRectangle OF (HOME()+"ffc\_gdiplus")


            LOCAL oImage as gpImage OF (HOME()+"ffc\_gdiplus")


            oImage=CREATEOBJECT("gpImage",thisform.img.picture)


            oGraphics=CREATEOBJECT("gpgraphics")


            wRatio=oImage.ImageHeight/oImage.ImageWidth     && 1704/2272 .75


            pRatio=thisform.img.Height/thisform.img.Width   && 300/600 .5


            *Must compensate for Stretch=1 (Isometric)


            IF wRatio>pratio


                  wwidth=thisform.img.height/wRatio


                  wleft=thisform.img.left+(thisform.img.width - wwidth)/2


                  wtop=thisform.img.top


                  wheight=thisform.img.height


                  oRectSrc=CREATEOBJECT("gpRectangle",;


                        (thisform.dr.left-wleft)*oImage.ImageWidth/wWidth,;


                        (thisform.dr.top - thisform.img.top) *oImage.ImageHeight/thisform.img.height,;


                        thisform.dr.width*oImage.ImageWidth/wWidth,;


                        thisform.dr.Height * oImage.ImageHeight/thisform.img.height)


            ELSE


                  wwidth=thisform.img.width


                  wleft=thisform.img.left


                  wheight=thisform.img.width*wRatio


                  wTop=thisform.img.top+(thisform.img.height-wheight)/2


                  oRectSrc=CREATEOBJECT("gpRectangle",;


                        thisform.dr.left * oImage.ImageWidth/thisform.img.width,;


                        (thisform.dr.top-wtop) *oImage.ImageHeight/wheight,;


                        thisform.dr.width * oImage.ImageWidth/thisform.img.width,;


                        thisform.dr.Height *oImage.ImageHeight/wheight)


            ENDIF


            oRectDest=CREATEOBJECT("gpRectangle",wleft,wtop,wwidth,wHeight)


            hWnd=SYS(2327, SYS(2325, SYS(2326,THISFORM.hwnd))) && From Craig Boyd


            oGraphics.CreateFromHWND(hWnd)


            oGraphics.DrawImagePortionScaled(oImage,oRectDest,oRectSrc,2)


            ValidateRect(hWnd,0)


ENDDEFINE


DEFINE CLASS myshape as Shape


      bordercolor=65535


      BackStyle=0 && transparent


      BorderStyle=4


ENDDEFINE


 


 


 


 


 


 


 

Comments (22)

  1. Dawa Tsering says:

    Calvin,

    It’s not working when I select an area on the photo. It just flashes and goes back to original image.

    p.s. this code solves the code copying and pasting from your post:

    _cliptext = STRTRAN(_cliptext,CHR(13)+CHR(13),CHR(13))

  2. Craig Boyd says:

    Hi Calvin,

    Nice use of GDI+ and a decent example of how to create a rubberband selection feature. As for the Child window of a Visual FoxPro TopLevel form, I detailed this in an FoxTalk 2.0 article that might be of interest to you, a couple of other solutions for getting the child window’s hwnd and a link to the GDI+ article…

    *********************************

    *!* Using only Visual FoxPro 9

    *********************************

    Local lnHwnd

    lnHwnd = SYS(2327, SYS(2325, SYS(2326,THISFORM.hwnd)))

    *********************************

    *!* Using GetWindow API function

    *********************************

    #DEFINE GW_CHILD 5

    DECLARE LONG GetWindowLong IN User32 LONG HWND, INTEGER nIndex

    Local lnHwnd

    lnHwnd = IIF(THISFORM.SHOWWINDOW = 2, GetWindow(THISFORM.HWND,GW_CHILD), THISFORM.HWND)

    Here’s the link for the "GDI+ on VFP 9 Forms" article (the real link is about a mile long, so I’m giving you a tinyurl that points to it)

    http://tinyurl.com/bpas8

  3. Craig Boyd says:

    Hi Dawa,

    The reason Calvin’s example doesn’t appear to work is that he hasn’t hooked the paint event in order to facilitate visual persistence of the zoomed in portion of the image. It is painting the zoomed in portion when the following line is run:

    oGraphics.DrawImagePortionScaled(oImage,oRectDest,oRectSrc,2)

    However, immediately after that Visual FoxPro repaints the form and the image is lost (so quick it is hard to even detect that it happened). The fix is to to use the Visula FoxPro 9 BindEvent function to hook the WM_PAINT messages being sent to the window and render the portion selected in a custom method. If you read the article that I linked in my comment to Calvin you can see how this is accomplished.

  4. CalvinH says:

    If it doesn’t work for you, try a larger JPG: apparently it only works with JPGs with my kids in them (about 1 Meg). It’s a timing issue: it is working with the smaller JPGs (you might see it flicker) but then a WM_PAINT message is processed which refreshes the original JPG.

  5. CalvinH says:

    I’ve modified the code to call ValidateRect so that the WM_PAINT won’t overwrite the zoomed in area. It’s also using Craig’s suggestion about using the SYS functions.

    Thanks for the helpful explanation and links Craig. I wrote those SYS functions years ago. I hope you find them “decent” too<g>

  6. Craig Boyd says:

    Calvin,

    >>I wrote those SYS functions years ago. I hope you find them “decent” too<g>

    Absolutely, I find them very useful. I included that nested Sys() line in my GDI+ article too… problem was that it didn’t occur to me until the article was almost going to press that I could nest them like that to get the child window hwnd, otherwise I would have probably used them instead of the API call. As it was, I only had time to put them in the code as a comment.

    I really appreciate all the time and features you’ve put into Visual FoxPro… and since we’re talking about GDI+ here, I’ll also say that I appreciate the work you did with Walter Nicholls. (I was green with envy!<g>)

    As for the rubberband selection… I’ve already got a project that I could use it in… so it’s definitely decent.

  7. In my last post Enable crop and zooming in on your digital photograph display form&amp;nbsp; there is code…

  8. Dawa Tsering says:

    Thank you Craig for the explaination!

    Calvin, if use ScrollBars = 3, then the croping doesn’t work. Also when you single click on it, it goes back to the original image. I noticed this when tried to zoom futher.

    Add the following code in the INIT event:

    <pre>

    this.ScrollBars = 3 this.AddObject("btn","commandbutton")

    this.btn.Left = 500

    this.btn.Width = 500

    this.btn.Height= 500

    this.btn.Visible = .t.

    </pre>

  9. After having read a post on Calvin Hsia’s weblog, I decided to try my hand at cr

  10. Craig Boyd says:

    Calvin,

    I created a class based on your idea here for a rubberband selection. If you haven’t seen it yet, you can at:

    http://www.sweetpotatosoftware.com/SPSBlog/PermaLink,guid,de3b099f-bca6-471e-89bf-ae094b823780.aspx

    Keep up the great Visual FoxPro related blog entries and ideas!

  11. Good job and great design!,Good job and great design!

  12. ... says:

    mmm.. nice design, I must say..

  13. ... says:

    Lo trovo piuttosto impressionante. Lavoro grande fatto..)

  14. ... says:

    Stupore! ho una sensibilit molto buona circa il vostro luogo!!!!

  15. ... says:

    9 su 10! Ottenerlo! Siete buoni!

  16. ... says:

    L’information interessante que vous avez! I’am allant revenir bientot.

  17. I received a customer question: I have looked all over the web and still searching, and found your blog.

  18. I received a customer question: I have looked all over the web and still searching, and found your blog

  19. Today’s digital cameras take pictures with much higher resolution than many computer screens. My Canon

Skip to main content