Articles Archive
Articles Search
Director Wiki
 

Zooming In and Out of an Image

October 10, 2001
by Will Turnage

Dear Multimedia Handyman,

I have a bit of a big question. I was wondering if it is possible to program some sort of loop (zoom) application in director, that can zoom in and out of images. Any help is most welcome.

Yves Peeters

Yves-

If you want to create an application that can zoom in and out of images, then you can do this using just two behaviors. Take a look at this movie:

To create this effect in your own movie, all you need are two behaviors. The first behavior will control whether the cursor is the zoom in or zoom out cursor, and the second behavior will use imaging lingo to display a zoomed in part of an image.

The first behavior to display the proper cursor is pretty easy. You want the zoom-in cursor to appear when you roll the cursor over the image, however, if the SHIFT key is being held down, then you want the cursor to change to the zoom-out cursor. The key in this behavior is to use the mouseWithin handler which continually executes as you roll over the image. Here's the code for the behavior.

property pSprRef

on beginSprite me

  pSprRef = sprite (me.spriteNum)
  pSprRef.cursor = 302

end

on mouseWithin me

  if the shiftDown then
    if (pSprRef.cursor <> 303) then
      pSprRef.cursor = 303
    end if
  else
    if (pSprRef.cursor <> 302) then
      pSprRef.cursor = 302
    end if
  end if

end

This behavior begins by making a reference to its own sprite and storing that reference in a variable (pSprRef). Next, you set the sprite's cursor to 302, which is the number for the zoom-in cursor.

Now you create a mouseWithin function that will execute when the mouse is rolling over the sprite on the screen. In this mouseWithin handler, the first thing you do is check whether or not the SHIFT key is being held down. If it is, then you check to see if the sprite's current cursor is set to 303, which is the number for the zoom-out cursor. If the sprite's cursor is not set to 303, your code sets the cursor to 303. Finally, if the SHIFT key is not held down, you check to see if the sprite's current cursor is set to 302. If it's not, then it sets the sprite's cursor to the zoom-in cursor. It's very important that you check the status of the cursor before changing it. Constantly setting the cursor can cause it to flicker.

Now that you have this behavior in place, you're ready to write the code that handles zooming in and out of an image. The way this behavior works is that it uses two bitmaps. The first bitmap is the full image you want to zoom and zoom out of. This image can be located anywhere in your cast and can be any size. The second image is a different bitmap that will just be used to display the specific part of the image that you're looking at any given point in time. The first step is to create some variables you'll need and initialize them.

on beginSprite me
  pSprRef = sprite (me.spriteNum)
  pMainImage = member ("rockies").image.duplicate ()
  pMagnification = 1
  pImgSourceRect = pMainImage.rect
  pBlankImage = image (400, 300, 32)
  pBlankImage.fill (pBlankImage.rect, color (#rgb, 0, 0, 0))
  me.resetPict ()
end

When the behavior begins, it first creates a variable containing a reference to its sprite. Then it creates an image object containing the full image you are zooming in and out of. Next, a variable named pMagnification defines a number representing how much you've zoomed into the picture. By default, the magnification will be set to one. The next variable you create is called pImgSourceRect which will store a rect that tells you which part of your image is being displayed in the sprite on screen. Its initial value is equal to the rect of the full, zoomed-out image. Finally, you create a blank image object to display the zoomed-in part of the image. This image is filled with the color black so that if the image runs off of the screen, you just see a black background. The last step, once you have initialized all of your variables, is to call the resetPict function.

on resetPict me

  tempImage = pBlankImage.duplicate ()
  tempImage.copyPixels (pMainImage, tempImage.rect, pImgSourceRect)
  pSprRef.member.image = tempImage

end

The resetPict function first creates a duplicate of the blank image and stores it in a local variable. Next, it copies a section of your original image, specified by the variable pImgSourceRect, and pastes it into the temporary image object. Finally, it displays the image object, by setting the image of the member of the sprite that this behavior is attached to.

So now you've initialized all of your variables, and you're ready to zoom in to (or out of) the image displayed on screen. When the user clicks the mouse, this is what happens:

on mouseDown me

  if pSprRef.cursor = 302 then
    pMagnification = min (pMagnification * 2.0, 32.0)
  else
    pMagnification = max (1.0, pMagnification / 2.0)
  end if

  
  if pMagnification = 1 then
    
    pImgSourceRect = pMainImage.rect
    
  else
    
    tempRect = pMainImage.rect / pMagnification
    tempClick = the mouseLoc - point (pSprRef.left, pSprRef.top)
    
    offsetPercentH = tempClick.locH / float (pBlankImage.rect.width)
    offsetPercentV = tempClick.locV / float (pBlankImage.rect.height)
    
    absLocH = (offsetPercentH * pImgSourceRect.width) + pImgSourceRect[1]
    absLocV = (offsetPercentV * pImgSourceRect.height) + pImgSourceRect[2]
    
    newOffsetH = absLocH - (tempRect.width * offsetPercentH)
    newOffsetV = absLocV - (tempRect.height * offsetPercentV)
    
    pImgSourceRect = tempRect.offset (newOffsetH, newOffsetV)
    
  end if

  me.resetPict ()

end

When you click the mouse on the image, the first thing the code checks the cursor of the sprite to see whether you are zooming in or out. If you are zooming in, it multiplies the magnification by 2, up to a maximum of 32. If you are zooming out, then it divides the magnification by 2, making sure that the magnification doesn't get less than 1.

Next comes the complicated part, calculating the zoomed portion of the image to display. If the magnification is set to 1, this is easy because you will just display the full image on screen. However, if the magnification is greater than 1, you have to do some calculations based on where the user clicked.

You start by dividing the rect of your image by the magnification factor. This will give you the exact size of the area of your original image that will be blown up and displayed on screen. Now, you have to calculate where this rect needs to be located within your image.

The key step is to use the location where the user clicks the mouse. Your first step is to find the mouse's location relative to the edges of the displayed image. Next, you use those percentages to calculate the absolute location in your main image that the user clicked on. Finally, you use those percentages again to recalculate the location of the new rect using the point the user clicked as your center reference point .

The first step is to calculate where the user clicked the image relative to the image itself. You do this by taking the mouse's location when the user clicked and subtracting from it the location of your sprite's upper left corner. Then you calculate the percentage of how far to the right and how far down in your image you actually clicked. This is done by dividing the clicked location by the entire height and length of the on-screen image.

Once you've found the relative location of the user click on screen, you need to calculate where that click corresponds in your full image. You do this by taking the relative percentage you just calculated, and multiplying it by the width or height of the pImgSourceRect, which contains the absolute location of the image displayed on-screen. Keep in mind that you also need to offset these values by the upper left corner of the pImgSourceRect for an exact fit.

Now that you have the absolute location in your main image of where the user clicked, the final step is to take that location and calculate a new rect of what will be shown to the user. This is done by taking your relative percentages and the new sized rect, and calculating the offset of the rect with the absolute location you clicked on. Finally, you take this newly-calculated rect and store that value into pImgSourceRect. At this point, you're done with all the math, and you can just call the resetPict function to display the proper portion of the image on the screen.

A sample movie is available for download in Macintosh (1.1MB) or Windows (950k) format.

Will Turnage is a multimedia programmer based in New York City. In addition to his weekly role as Director Online's Multimedia Handyman, he is also the Technology Director for Sony Music's Client Side Technologies Group. You can read more about his work at http://will.turnage.com/.

Copyright 1997-2017, Director Online. Article content copyright by respective authors.