image zoom memory problem

BerndB

Member
Licensed User
b4PPC V 6.5 optimized compilation

I am working on an app to view files related to OziExplorerCE

http://www.b4x.com/forum/share-your-creations/3654-ozi-files-viewer.html

Zooming with ImageLibEx.dll is still not really clear to me.

@Klaus:
In your scaledMap project
http://www.b4x.com/forum/chit-chat/3553-mapping.html :sign0188:
for zooming you use the dzImage DLL instead of mapDisplay.Zoom(zoom.Value) from ImageLibEx.

Is that because the increasing memory consumption with every zoom action?
That is what happens with my FS Loox 720 Win 2003 SE. and as well on the desktop.

So for zooming I use dzImage as well.
But with it I have also problems.
The jpg map inflatet to bitmap is about 1.3 MByte.
With a zoom of 160 % I get a "OutOfMemoryException"

With a zoom of 100 % i have 22 MB of free memory.
With a zoom of 120 % i have 21.6 MB of free memory.
With a zoom of 140 % i have 20.9 MB of free memory.
With a zoom of "160 %" (which didn't zoom) i have 20.9 MB of free memory as well after the error message.

The same image viewed and zoomed with other apps as "TCPMP" or "Album für Pocket PC" is no problem at all.

With this apps a zoom doesn't even change the use of memory.
As well they don't keep a copy of the original image for zooming.
(if I load the image with my app memory decreases by ~2.6 MB.
with these other apps by ~1.3 MB) (the used image is included in the above project:"Ozi Files Viewer")

:sign0085:
Are there different strategies with zooming?
Am I doing something wrong?
and does anybody know a way for me to achieve higher zoom levels without needing an other image file?


ZPktX is the zoom point (middle of screen)
B4X:
mapDisplay.New3(il1.Item(0))
drwDisplay.New2(mapDisplay.Value)    ' DrawerEx drawer object to draw onto the map image
drwMap.New1("frmMap",False)       ' drawer object to draw the part of the map image onto the form
BiExZoom.New3(mapDisplay.Value) ' BitmapEx copy for zooming
frmMap.Refresh

.
.
.

'zoom:
mapDisplay.Value=dzImage.ZoomImage(BiExZoom.Value,numZoom.Value)  
rectSrc.x = -(ZPktX -ZPktX *numZoom.Value/100)  ' keep centering on the zoom point
rectSrc.y = -(ZPktY -ZPktY *numZoom.Value/100 )
drwMap.DrawImage(mapDisplay.Value,rectSrc.Value,rectDest.Value,False)
frmMap.Refresh



Thanks a lot for any hints

Bernd
 

klaus

Expert
Licensed User
Longtime User
Hi BerndB,

I have also the Out of memory problem with bigger zoom factors on the device, but have not yet looked at it in detail. Will be done quite soon.

I use the dzImage library because I had already used it in my first Map programm. And I didn't remember that the ImageLibEx has alson a Zoom function. I remembered having seen it once, but didn't find it in the help file of the ImageLibEx library, so I used what I knew.

I have now entered the Zoom function of the ImageLibEx library in the ScaledMap programm.

Using the dzImage library or the ImageLibEx library will have no difference from the memory point of view, both generate a bigger bitmap and nothing else.

One question about your code:
What image do you have in il1.Item(0) ?
B4X:
mapDisplay.New3(il1.Item(0))

If it is the original image you don't need this extra bitmap
B4X:
BiExZoom.New3(mapDisplay.Value) ' BitmapEx copy for zooming

you can directly use:
B4X:
mapDisplay.Value=dzImage.ZoomImage(il1.Item(0),numZoom.Value)

instead of
B4X:
mapDisplay.Value=dzImage.ZoomImage(BiExZoom.Value,numZoom.Value)
This saves you 1 image !

As soon as I have some other news I will post it here.

Best regards.
 

BerndB

Member
Licensed User
Hi Klaus,

thanks for your quick response.

The code snippet is a bit simplified.
in il1.Item(0) is the original image.

but before zooming there are drawn Circles onto the image.
So image + circles are in BiExZoom.New3 to be zoomed.


Concerning memory with dzImage or ImageLibEx I found a funny thing:

In scaledMap as well as in OziFilesView I alternatively used the two zoom methods.
At first dzImage was active. (The left part of each Thumbnail.)
I quickly clicked 5 zoom steps up and then 5 zoom steps down.
This I repeated several times.

scaledMap:
B4X:
Sub Zoom(Fact)
   ZoomFact=ZoomFact*Fact

   bmpDisplay.New3(IL3.Item(0))      ' ImageLibEx 
   bmpDisplay.Zoom(ZoomFact*100)
   'bmpDisplay.Value=dzImage.ZoomImage(IL3.Item(0),ZoomFact*100) 'dzImage
   
       InitMap
   DrawElements
End Sub
(by the way: in DrawElements is done zooming as well)


OziFilesView:
B4X:
Sub numZoom_ValueChanged
   mapDisplay.New3(BiExZoom.Value)
        mapDisplay.Zoom(numZoom.Value) 
   'mapDisplay.Value= dzImage.ZoomImage(BiExZoom.Value,numZoom.Value)  
       
        rectSrc.x = -(ZPktX -ZPktX *numZoom.Value/100)  ' keep centering on the zoom point
        rectSrc.y = -(ZPktY -ZPktY *numZoom.Value/100 )

   drwMap.DrawImage(mapDisplay.Value,rectSrc.Value,rectDest.Value,False)
       frmMap.Refresh
      
End Sub

This was done on the desktop

The thumbnails are taken from the XP-taskmanager.

Using ImageLibEx you can see a increasing of used memory in both apps.

Using dzImage with OziFilesView shows increasing while zoom in
and decreasing while zoom out.
With scaledMap it shows continued increasing again.

funny, but no idea at present

cheers
Bernd
 

agraham

Expert
Licensed User
Longtime User
Try the new version 1.4 of ImageLibEx that I have just posted. It has a one line change in BitmapEx.Zoom to dispose the old pre-zoom butmap object which I missed including. All the other methods that generate a new Bitmap do it but I missed it on the Zoom method. I don't know whether it will make a difference as the .NET garbage collector might have picked it up anyway but it is worth trying.
 

klaus

Expert
Licensed User
Longtime User
Hi Andrew,

Thank's for the update.

One question,
After the creation with BitmapEx1.New3(IL1.Item(0)) ,
what is the best practice to assign a new image to a BitmapEx ?
Using
BitmapEx1.Value=IL1.Item(0) or
BitmapEx1.New3(IL1.Item(0))

Thank you in advance for your answer and Best regards.
 

klaus

Expert
Licensed User
Longtime User
Hi BerndB,

The zooming in the DrawElements routine is necessary because when I redraw the graphic elements I need to assign a new image to the form without any drawings on it and as I memorize only the original image and not the zoomed image I must zoom it before assigning it to the form.
The DrawElements routine is in fact a Redraw routine also used when I delete drawing elements and not only after zooming.

Best regards.
 

agraham

Expert
Licensed User
Longtime User
what is the best practice to assign a new image to a BitmapEx ?
The answer is - it depends!

BitmapEx1.New3 - disposes of any old Bitmap and saves a reference to the new. This is best if the old Bitmap is not in use anywhere

BitmapEx1.Value - just saves a reference to the new Bitmap. This is best if the old Bitmap is in use anywhere (like in an Image control) as disposing of it is probably not you want as the Bitmap might disappear if only a reference to the Bitmap was assigned when it was "used" rather than the contents being copied to another Bitmap.

If in doubt I would use New3 and see if any images vanish unexpectedly.
 

BerndB

Member
Licensed User
Hi Andrew,

thank you for the new version of ImageLibEx.

For the memory behavior I am sorry to say that nothing has changed.
Still with every zoom action regardless of zooming in or out, free memory is shrinking.
and still the OutOfMemoryException is coming very soon.

regards
Bernd
 

klaus

Expert
Licensed User
Longtime User
Hi Andrew,

Here is a small program that should show the problem.

On my Qtek 9090 I get an out of memory message:
- with dzImage already with a zoom factor of 2
- with ImageLibEx zoom factor 2 OK , error message with the zoom factor of 4

So for me, on my device, the ImageLibEx zoom function has a better performance than the dzImage one.

The out of memory message with the zoom factor of 4 seems normal to me because the map.jpg file represents 2 MByte as a bitmap.
And with the increase in size of the zoomed bitmap this is possible.

@BerndB
Could you make your tests with this program to see if you get the same behaviour as before.

Best regards.
 

agraham

Expert
Licensed User
Longtime User
in optimized compiled exe's BitmapEx1.Value=IL1.Item(0) generates an error !
What is the error message. I've just tried and it works fine for me on a desktop optimised compiled exe. I haven't tried a device exe but I see no reason why that should error either.
 

klaus

Expert
Licensed User
Longtime User
I noticed the problem in the ScaledMap program and not in the TestZoomMemory program.

I looked a bit further, and modified the TestZoomMemory program.
I added a button to assign the same image once again to the bitmap with bmpDisplay.Value instead of bmpDisplay.New3, and zooming it.
Clicking on the button the first time OK, but the 2nd time an error is generated even in the IDE, in the zooming line.
This doesn't happen with bmpDisplay.New3.

That's the problem I had in the ScaledMap program, and replacing bmpDisplay.Value by bmpDisplay.New3 solved the problem.

Best regards.
 

agraham

Expert
Licensed User
Longtime User
You have to remember that bitmaps are usually passed round by reference. There is a reference to a bitmap in IL1. You assign a copy of that reference to BitmapEx.Value and zoom it, in the process creating a new bitmap in bitmapEx.value. The original bitmap pointed to by the reference in BitmapEx is disposed after the zoom. As it is the same reference to the bitmap in IL1 it is that that is disposed and so is no longer available after that. Your second assignment then fails. To get a copy of a bitmap instead of a reference you can use BitmapEx.Clone which returns a reference to a new bitmap.
 

agraham

Expert
Licensed User
Longtime User
The memory problem appears to be caused by an accumulation of unreleased unmanaged memory.

A .NET bitmap may be thought of as a reference or pointer to a section of unmanaged (by .NET) memory outside the .NET framework that belongs to the OS graphics code and contains the actual bitmap data. The .NET garbage collector looks after clearing up the reference and in the process should release the unmanaged memory.

What is happening is that, despite me adhering to the .NET guidelines on disposing of graphics items within BitmapEx.Zoom, the unmanaged memory that contained the bitmap data is not being reclaimed when bitmaps within the Zoom method are Disposed. They are however recovered when the BitmapEx object is itself Disposed. I cannot explain this :confused: I assume a similar problem afflicts dzImage.

It seems as though that memory is being treated as belonging to a BitmapEx and not the Bitmap inside it. The workaround is to Dispose of the BitmapEx and recreate it when a new zoom level is required. This probably applies to all the BitmapEx methods, such as the rotates, which operate on the internal bitmap.
B4X:
If rbtImageLibEx.Checked=True Then
  bmpDisplay.Dispose
  AddObject("bmpDisplay", "BitmapEx")
  bmpDisplay.New1(AppPath & "\CapeTown.jpg")
  bmpDisplay.Zoom(ZoomFact)
 Else
  bmpDisplay.New3=dzImage.ZoomImage(IL1.Item(0),ZoomFact)
 End If
 

BerndB

Member
Licensed User
Here is an alternative to the libraries zoom functions.
And it's quite simple.

For me it looks like, that the problem is connected to the increase of
destination.width and .height (rectDest.Value) of drawImage.
B4X:
drwMap.DrawImage(mapDisplay.Value,rectSrc.Value,rectDest.Value,False)

In the following I do zooming by my own.

The destination rectangle remains at all zoom levels the same:
B4X:
rectDest.New1(0,0,ScreenW,ScreenH)

Width and height of the source rectangle changes with the zoom factor.
x and y of source rectangle is changed that way, that the zoom point keeps staying in screen middle.
B4X:
rectSrc.Width = ScreenW /(numZoom.Value/100)
  rectSrc.Height = ScreenH /(numZoom.Value/100)
  rectSrc.X = ZpktX -rectSrc.Width/2   'change rectSrc.X so that the zoom point remains
  rectSrc.Y = ZpktY -rectSrc.Height/2

No memory errors anymore.

B4X:
Sub Globals
   'Declare the global variables here.
   
   Dim mDn, mDnX, mDnY
 ' Zoom
   numZoom.Increment =20
   numZoom.Minimum =10
   numZoom.Maximum =450
   
   ScreenW= 240                ' width of the screen on the form  pixels
   ScreenH= 275'275            ' height of the screen on the form  pixels
   
   zPktX = 500    ' ZoomPoint
   zPktY = 250
   
 SrcW=ZpktX -ScreenW/2
   SrcH=ZPktY -ScreenH/2 
End Sub

Sub App_Start
       BrushEx.New1(cRed)
   NumZoom.Value =100  ' put Zoom to 100%
   mapDisplay.New3(il1.Item(0))
   drwDisplay.New2(mapDisplay.Value)      ' DrawerEx drawer object to draw onto the map image
   drwMap.New1("frmMap",False)               ' drawer object to draw the part of the map image onto the form
 
   ' RectangleEx
   rectDest.New1(0,0,ScreenW,ScreenH)   ' destination rectangle = screen area on the form
   rectSrc.New1(SrcW,SrcH,ScreenW,ScreenH)                        ' source rectangle on the map image according to the scroll positions
   ' define a circle
   rectCircle.New1(ZPktX-Round(20/2),ZPktY-Round(20/2),20,20)
 ' draw the circle on the map image
   drwDisplay.FillEllipse(brushEx.Value, rectCircle.Value) 
 ' draw the map image
   drwMap.DrawImage(mapDisplay.Value,rectSrc.Value,rectDest.Value,False)
   BiExZoom.New3(mapDisplay.Value)  '  BitmapEx copy for zooming

   frmMap.Show
End Sub

' Z O O M
Sub NumZoom_ValueChanged
  rectSrc.Width = ScreenW /(numZoom.Value/100)
  rectSrc.Height = ScreenH /(numZoom.Value/100)
  rectSrc.X = ZpktX -rectSrc.Width/2   'change rectSrc.X so that the zoom point remains
  rectSrc.Y = ZpktY -rectSrc.Height/2
      
  drwMap.DrawImage(mapDisplay.Value,rectSrc.Value,rectDest.Value,False)
  frmMap.Refresh
End Sub

Sub frmMap_MouseDown(x,y)
  mDn =1
  mDnX =x
  mDnY =y
End Sub

Sub frmMap_MouseUp(x,y)
  mDn =0
End Sub

Sub frmMap_MouseMove (x,y) 

  If mDn = 1 Then
   ZpktX = ZpktX +(mDnX -x)/(numZoom.Value/100)
   ZpktY = ZpktY +(mDnY -y)/(numZoom.Value/100)
   rectSrc.X = ZpktX -rectSrc.Width/2 
   rectSrc.Y = ZpktY -rectSrc.Height/2
         
      drwMap.DrawImage(mapDisplay.Value,rectSrc.Value,rectDest.Value,False)
      frmMap.Refresh
      mDnX =x
      mDnY =y
  End If

End Sub

cheers
Bernd
 

klaus

Expert
Licensed User
Longtime User
Hi Bernd,

I had a look at your code, it's very interesting, I tried it in the TestZoomMemory program.

For my ScaledMaps application, the first problem is that I want also to draw onto the bitmap in zoomed mode and the 2nd one is that the texts and bitmaps keep their original size independant of the zoom factor.

In your full program do you draw your drawing before any zooming ?

Best regards.
 

BerndB

Member
Licensed User
Hi Klaus,

right, in my program I just draw at the beginning with 100 %.
To draw onto the zoomed image should be possible, but its more effort then to determine place and size.

So it depends on the needs, which strategy is best.

But together with Andrews solution we have the choice ...


An advantage with stretching between source and destination is that you don't need a copy of the image for zooming, and after loading the image the memory is not affected anymore.

Thank you for your and Andrews posts.
Kind regards
Bernd
 
Top