B4A Library ScaleImageView - Pan and zoom large images

My main reason to restart using B4A was to implement on a mobile device my mapping program which covers the entire UK at various scales and requires the ability to display large map images at a specified zoom level and centered on a specified coordinate and then allow the user to drag and zoom the map.

For some strange reason if you set targetSdkVersion="xx" to more than 13 in the manifest it dramatically reduces the largest Bitmap that can be created from a file so most of my map images then no longer open in TouchImageView, jsTouchImageView and even ImageView. These all rely on opening a full resolution bitmap to display an image. It seems to be a Canvas size limitation. Also I failed to get TouchImageView to reliably pre-zoom and pre-position an image so I struggled on with jsTouchImageView and targetSdkVersion="13" and got an acceptable working version but pre-zooming and pre-positioning at an exact point was a bit problematic and not entirely accurate but usually close enough after a few heuristics. It also still used very large amounts of memory for the decoded bitmap.

Having got the app working I then started looking to try to finally solve the image viewing problems and found some source code on GitHub that was written for just this purpose. Instead of loading the entire image at once it only fully decodes from the image file the areas needed for display. As a bonus the pre-zooming and centering is very accurate and works a treat. So after a bit of difficulty getting it to compile I wrapped it, slightly enhanced to draw a centered point indication and am now extremely happy with my mapping program that can now display huge images and target SDK version 26 and later without using a vast amount of memory.

There is a ScaleImageView.htm file in the attached archive that documents the extremely simple API posted below. As always do what readme.txt says.

I can't put a large image file in the archive as it becomes too big to upload here, so find your own large image and do as Readme.txt says.
ScaleImageView
This is a custom image view designed for displaying huge images.
It includes all the standard gestures for zooming and panning images
and provides some extra useful features for animating the image position and scale.
The aim of this library is to solve some of the common problems when displaying large images in Android.
This view extends View and so inherits all the normal View methods.
This view doesn't extend ImageView and isn't intended as a general purpose replacement for it.
It is specialised for the display of photos and other large images, not the display of 9-patches,
shapes and the other types of drawable that ImageView supports.
Supported gestures are:
One finger drag to pan, Two finger pinch to zoom and double tap to zoom in and out.
Pan while zooming, seamless switch between pan and zoom and fling momentum after panning.
Quick scale (one finger zoom - quick double tap then drag)
Events Click and LongClick are provided whose co-ordinates may be accessed in the event code.
ClickViewX, ClickViewY return the position of the Click or LongClick on the view.
ClickImageX and ClickImageY return the position of the Click or LongClick on the source image.
The OnDraw event provides a Canvas that can be used to draw on the view whenever it is redrawn.
The view can draw a circle at a defined position on the original image.
The circle can either have a fixed size that is a fraction of the screen width
or a variable size that always covers the same area of the original size as it is zoomed.
The associated file, ScaleImage.jar, must be located in the Additional Libraries folder.
The line '#AdditionalJar: ScaleImage' must be added to the Main module.
This library uses code from
That code and this library are licensed under the Apache License 2.0
Copyright [2015] [Dave Morrissey]
Copyright [2018] [Andrew Graham]
Licensed under the Apache License, Version 2.0 (the "License")
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Author: Andrew Graham
Version: 2.3
  • ScaleImageView
    • Events:
      • Click 'The user has tapped on the view. Use ClickImage or ClickView for the coordinates.
      • LongClick 'The user has long pressed the view. Use ClickImage or ClickView for the coordinates.
      • OnDraw (viewcanvas As Canvas) 'The view is being redrawn. Use viewcanvas to draw on it.
    • Functions:
      • BringToFront
      • DesignerCreateView (base As Panel, lw As Label, props As Map)
      • Initialize (arg1 As String)
      • Invalidate
      • Invalidate2 (arg0 As android.graphics.Rect)
      • Invalidate3 (arg0 As Int, arg1 As Int, arg2 As Int, arg3 As Int)
      • IsInitialized As Boolean
      • Recycle
        Releases all resources the view is using and resets the state, nulling any fields that use significant memory.
        After you have called this method, the view can be re-used by setting a new image.
        Settings are remembered but state (scale and center) is forgotten.
      • RemoveView
      • RequestFocus As Boolean
      • ResetScaleAndCenter
        Zooms the image out to minimum scale and centers it on the screen according to the view's settings.
      • SendToBack
      • SetBackgroundImage (arg0 As android.graphics.Bitmap) As BitmapDrawable
      • SetColorAnimated (arg0 As Int, arg1 As Int, arg2 As Int)
      • SetLayout (arg0 As Int, arg1 As Int, arg2 As Int, arg3 As Int)
      • SetLayoutAnimated (arg0 As Int, arg1 As Int, arg2 As Int, arg3 As Int, arg4 As Int)
      • SetScaleAndCenter (scale As Float, x As Float, y As Float, duration As Int)
        Set the zoom of the displayed image and center it on the point (x,y).
        x and y are width and height factor values of the full image size between 0 and 1.
        PanLimit should be set to PAN_LIMIT_CENTER if any point is to be centered.
        Duration sets the duration of the transition in milliseconds.
        Returns False if the image is not ready and the transition was not made otherwise True.
      • SetScaleAndCenterPixels (scale As Float, x As Int, y As Int, duration As Int) As Boolean
        Set the zoom of the displayed image and center it on the point (x,y).
        x and y are full image size x and y pixel values.
        PanLimit should be set to PAN_LIMIT_CENTER if any point is to be centered.
        Duration sets the duration of the transition in milliseconds.
        Returns False if the image is not ready and the transition was not made otherwise True.
      • SetVisibleAnimated (arg0 As Int, arg1 As Boolean)
      • SourceXYtoViewXY (sourcex As Int, sourcey As Int) As Float()
        Returns a Float array containing the X and Y pixel position on the view of the specified point of the full size image.
        If the image coordinates are currently off screen, the view coordinates will also be outside the view
        Index 0 of the array contains the X coordinate and index 1 the Y coordinate.
      • ViewXYtoSourceXY (viewx As Int, viewy As Int) As Float()
        Returns a Float array containing the X and Y pixel position on the full size image of the specified point of the view.
        Index 0 of the array contains the X coordinate and index 1 the Y coordinate.
    • Properties:
      • Background As android.graphics.drawable.Drawable
      • CenterX As Float [read only]
        Gets the X pixel value of the full image point which is at the centre of the view.
        Can only be set programatically by SetScaleAndCenter or SetScaleAndCenterPixels
      • CenterY As Float [read only]
        Gets the Y pixel value of the full image point which is at the centre of the view.
        Can only be set programmatically by SetScaleAndCenter or SetScaleAndCenterPixels
      • CircleColor As Int
        Gets or sets the colour of the inner ring of the circle.
        The default is 0xff20b2aa - LightSeaGreen.
      • CircleDrawnRadius As Float [read only]
        Gets the radius of the circle in pixels as last drawn regardless of the state of EnableCircleScale.
        This can be used to position other drawn items relative to the circle.
      • CircleMinimumRadius As Float
        Gets or sets the minimum radius of the circle as a factor between 0 and 1 of the width of the view.
        This prevents the circle being drawn vanishingly small when zoomed out if EnableCircleScale is True.
        The default is 0.02.
      • CircleRadius As Float
        Gets or sets the radius of the circle as a factor between 0 and 1 of the width of the view or of the full image.
        If EnableCircleScale is True the factor is that of the full size image width.
        If EnableCircleScale is False the factor is that of the width of the view.
        The default is 0.002 which is appropriate if EnableCircleScale is True.
      • CircleWidth As Float
        Gets or sets the width of stroke used to draw the circle (not the radius).
        The value is a factor between 0 and 1 of the radius of the circle as drawn. The default is 0.2.
      • CircleX As Float
        Gets or sets the X position factor of the point on the full image at which to draw the circle.
        The default is 0.5, the centre of the image.
      • CircleXPixels As Float
        Gets or sets the X position in pixels of the point on the full image at which to draw the circle.
        The default is the centre of the image.
      • CircleY As Float
        Gets or sets the Y position factor of the point on the full image at which to draw the circle.
        The default is 0.5, the centre of the image.
      • CircleYPixels As Float
        Gets or sets the Y position in pixels of the point on the full image at which to draw the circle.
        The default is the centre of the image.
      • ClickImageX As Float [read only]
        When accessed in a Click or LongClick event gets the X pixel position on the full size image of the point clicked.
      • ClickImageY As Float [read only]
        When accessed in a Click or LongClick event gets the Y pixel position on the full size image of the point clicked.
      • ClickViewX As Float [read only]
        When accessed in a Click or LongClick event gets the X pixel position on the view of the point clicked.
      • ClickViewY As Float [read only]
        When accessed in a Click or LongClick event gets the Y pixel position on the view of the point clicked.
      • Color As Int [write only]
      • DoubleTapZoomDuration As Int [write only]
        Sets the duration of a double tap zoom animation, in milliseconds. The default is 500ms.
      • EnableCircle As Boolean
        Gets or sets whether a circle will be drawn at the image coordinates specified by CircleX and CircleY.
        The default is False.
      • EnableCircleScale As Boolean
        Gets or sets whether the size of the circle will increase and decrease when the image is zoomed.
        If True the circle will resize to bound the same features on the image whatever the zoom.
        If False the circle will maintain a fixed size on the screen of the device.
        The default is True.
      • Enabled As Boolean
      • Height As Int
      • Image As android.graphics.Bitmap [write]
        Load an existing Bitmap into the view.
        This is unsuitable for large images because it bypasses subsampling and may cause OutOfMemoryErrors.
        This is an easy way to add pan and zoom functionality to an image already in memory
      • Image As android.graphics.Bitmap [read]
        Returns a Bitmap containing the image presently displayed in the ScaleImageView
      • ImageFile As String [write only]
        Load an image from a file saved on the device file system into the view.
        This method can display JPG and PNG images of any size.
        In order to support huge images without running out of memory, a sub-sampled base layer is first loaded.
        Higher resolution tiles are loaded for the visible area as the user zooms in.
      • IsReady As Boolean [read only]
        Gets whether the view is initialised, has dimensions and will display an image.
        Returns True if the view is ready to display an image and accept touch gestures.
      • Left As Int
      • MaxZoom As Float
        Gets or set the maximum permitted zoom level of the displayed image. The default is 2.0.
        The minimum zoom level is set automatically to fit the entire image to the view.
      • Orientation As Int
        Gets or set the orientation of the image relative to the source file.
        Valid values for orientation are 0, 90, 180, 270 and -1 or one of the ORIENTATION values.
      • ORIENTATION_0 As Int [read only]
        Display the image file in its native orientation. Value is 0.
      • ORIENTATION_180 As Int [read only]
        Rotate the image 180 degrees. Value is 180.
      • ORIENTATION_270 As Int [read only]
        Rotate the image 270 degrees clockwise. Value is 270.
      • ORIENTATION_90 As Int [read only]
        Rotate the image 90 degrees clockwise. Value is 90.
      • ORIENTATION_USE_EXIF As Int [read only]
        Attempt to use EXIF information on the image to rotate it. Value is -1.
      • Padding As Int()
      • PAN_LIMIT_CENTER As Int [read only]
        Allow the image to be panned until a corner reaches the center of the screen but no further.
        Useful when you need to pan any spot on the image to the exact center of the screen.
      • PAN_LIMIT_INSIDE As Int [read only]
        Don't allow the image to be panned off screen.
        As much of the image as possible is always displayed, centered in the view when it is smaller.
      • PAN_LIMIT_OUTSIDE As Int [read only]
        Allow the image to be panned until it is just off screen, but no further.
        The edge of the image will stop when it is flush with the screen edge.
      • PanLimit As Int [write only]
        Sets the image pan limit to one of the PAN_LIMIT values.
        The default is 3 = PAN_LIMIT_CENTER.
      • Parent As Object [read only]
      • Scale As Float [read only]
        Get the current scale of the image as set by the user.
        Can only be set programmatically by SetScaleAndCenter or SetScaleAndCenterPixels.
      • SrcHeight As Int [read only]
        Get the height of the current full size image in pixels.
        Note that this does not take the applied rotation into account.
      • SrcWidth As Int [read only]
        Get the width of the current full size image in pixels.
        Note that this does not take the applied rotation into account.
      • Tag As Object
      • TileBackgroundColor As Int [write only]
        Default none. Renders a background color behind tiles. Useful when rendering a transparent PNG.
        Note: transparent PNGs require double the memory of PNGs with no alpha layer, and may cause out of memory errors.
        The default is none.
      • Top As Int
      • Visible As Boolean
      • Width As Int


EDIT: Version 2.00 now posted. ScaleImageView is now a Custom View. The API above has been updated. See Post #3 below for more details.
EDIT: Version 2.20 now posted. The API above has been updated. See Post #36 below for more details.
EDIT: Version 2.30 now posted. A readable Image property has been added to get the image presently displayed by ScaleImageView. See post #37.
 

Attachments

  • ScaleImageView_v2.30.zip
    11.4 KB · Views: 251
  • ScaleImageView_v2.20.zip
    84 KB · Views: 186
Last edited:

Caps64

Member
It is true, AND the picture moves in the screen: then now I realize what is the CenterX value, and that this property cannot be used to do what I need in my project, because I dont want that the picture moves in the screen when I swipe on it (I have to use PAN_LIMIT_INSIDE*).
In order to implement a swipe detector, SIV would need at least a Touch event. I tried to link a GestureDetector to the SIV, but it seems that GD eats all events, then as side effect the built in pinch to zoom feature does not work.

* It seems that the default value for PanLimit is PAN_LIMIT_INSIDE (not PAN_LIMIT_CENTER).
 

Rachbob

Member
Licensed User
this is a wonderful Library. but it works just with large image. could this code be modified to support samall and medium image like less than 200 ko for remote BLOB database use ?

thank you
 

agraham

Expert
Licensed User
but it works just with large image
No it doesn't, what makes you think that? It can work with any size image. In particular small images which you have already decoded and have a Bitmap for can be assigned to the Image property and panned and zoomed. Look at the popup help for ScaleImageView.Image.

Note that small images may need a large value of MaxZoom set. The default of 2 is probably a bit low for smaller images.
 
Last edited:

js486dog

Active Member
Licensed User
My main reason to restart using B4A was to implement on a mobile device my mapping program which covers the entire UK at various scales and requires the ability to display large map images at a specified zoom level and centered on a specified coordinate and then allow the user to drag and zoom the map.

For some strange reason if you set targetSdkVersion="xx" to more than 13 in the manifest it dramatically reduces the largest Bitmap that can be created from a file so most of my map images then no longer open in TouchImageView, jsTouchImageView and even ImageView. These all rely on opening a full resolution bitmap to display an image. It seems to be a Canvas size limitation. Also I failed to get TouchImageView to reliably pre-zoom and pre-position an image so I struggled on with jsTouchImageView and targetSdkVersion="13" and got an acceptable working version but pre-zooming and pre-positioning at an exact point was a bit problematic and not entirely accurate but usually close enough after a few heuristics. It also still used very large amounts of memory for the decoded bitmap.

Having got the app working I then started looking to try to finally solve the image viewing problems and found some source code on GitHub that was written for just this purpose. Instead of loading the entire image at once it only fully decodes from the image file the areas needed for display. As a bonus the pre-zooming and centering is very accurate and works a treat. So after a bit of difficulty getting it to compile I wrapped it, slightly enhanced to draw a centered point indication and am now extremely happy with my mapping program that can now display huge images and target SDK version 26 and later without using a vast amount of memory.

There is a ScaleImageView.htm file in the attached archive that documents the extremely simple API posted below. As always do what readme.txt says.

I can't put a large image file in the archive as it becomes too big to upload here, so find your own large image and do as Readme.txt says.

ScaleImageView
This is a custom image view designed for displaying huge images.
It includes all the standard gestures for zooming and panning images
and provides some extra useful features for animating the image position and scale.
The aim of this library is to solve some of the common problems when displaying large images in Android.
This view extends View and so inherits all the normal View methods.
This view doesn't extend ImageView and isn't intended as a general purpose replacement for it.
It is specialised for the display of photos and other large images, not the display of 9-patches,
shapes and the other types of drawable that ImageView supports.
Supported gestures are:
One finger drag to pan, Two finger pinch to zoom and double tap to zoom in and out.
Pan while zooming, seamless switch between pan and zoom and fling momentum after panning.
Quick scale (one finger zoom - quick double tap then drag)
Events Click and LongClick are provided whose co-ordinates may be accessed in the event code.
ClickViewX, ClickViewY return the position of the Click or LongClick on the view.
ClickImageX and ClickImageY return the position of the Click or LongClick on the source image.
The OnDraw event provides a Canvas that can be used to draw on the view whenever it is redrawn.
The view can draw a circle at a defined position on the original image.
The circle can either have a fixed size that is a fraction of the screen width
or a variable size that always covers the same area of the original size as it is zoomed.
The associated file, ScaleImage.jar, must be located in the Additional Libraries folder.
The line '#AdditionalJar: ScaleImage' must be added to the Main module.
This library uses code from
https://github.com/davemorrissey/subsampling-scale-image-view
That code and this library are licensed under the Apache License 2.0
Copyright [2015] [Dave Morrissey]
Copyright [2018] [Andrew Graham]
Licensed under the Apache License, Version 2.0 (the "License")
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Author: Andrew Graham
Version: 2.0
  • ScaleImageView
    • Events:
      • Click 'The user has tapped on the view. Use ClickImage or ClickView for the coordinates.
      • LongClick 'The user has long pressed the view. Use ClickImage or ClickView for the coordinates.
      • OnDraw (viewcanvas As Canvas) 'The view is being redrawn. Use viewcanvas to draw on it.
    • Functions:
      • BringToFront
      • DesignerCreateView (base As Panel, lw As Label, props As Map)
      • Initialize (arg1 As String)
      • Invalidate
      • Invalidate2 (arg0 As android.graphics.Rect)
      • Invalidate3 (arg0 As Int, arg1 As Int, arg2 As Int, arg3 As Int)
      • IsInitialized As Boolean
      • RemoveView
      • RequestFocus As Boolean
      • SendToBack
      • SetBackgroundImage (arg0 As android.graphics.Bitmap) As BitmapDrawable
      • SetColorAnimated (arg0 As Int, arg1 As Int, arg2 As Int)
      • SetLayout (arg0 As Int, arg1 As Int, arg2 As Int, arg3 As Int)
      • SetLayoutAnimated (arg0 As Int, arg1 As Int, arg2 As Int, arg3 As Int, arg4 As Int)
      • setMaxZoom As Float
        Gets or set the maximum permitted zoom level of the displayed image. The default is 2.0.
        The minimum zoom level is set automatically to fit the entire image to the view.
      • SetScaleAndCenter (scale As Float, x As Float, y As Float, duration As Int)
        Set the zoom of the displayed image and center it on the point (x,y).
        x and y are width and height factor values of the full image size between 0 and 1.
        PanLimit should be set to PAN_LIMIT_CENTER if any point is to be centered.
        Duration sets the duration of the transition in milliseconds.
        Returns False if the image is not ready and the transition was not made otherwise True.
      • SetScaleAndCenterPixels (scale As Float, x As Int, y As Int, duration As Int) As Boolean
        Set the zoom of the displayed image and center it on the point (x,y).
        x and y are full image size x and y pixel values.
        PanLimit should be set to PAN_LIMIT_CENTER if any point is to be centered.
        Duration sets the duration of the transition in milliseconds.
        Returns False if the image is not ready and the transition was not made otherwise True.
      • SetVisibleAnimated (arg0 As Int, arg1 As Boolean)
      • SourceXYtoViewXY (sourcex As Int, sourcey As Int) As Float()
        Returns a Float array containing the X and Y pixel position on the view of the specified point of the full size image.
        If the image coordinates are currently off screen, the view coordinates will also be outside the view
        Index 0 of the array contains the X coordinate and index 1 the Y coordinate.
      • ViewXYtoSourceXY (viewx As Int, viewy As Int) As Float()
        Returns a Float array containing the X and Y pixel position on the full size image of the specified point of the view.
        Index 0 of the array contains the X coordinate and index 1 the Y coordinate.
    • Properties:
      • Background As android.graphics.drawable.Drawable
      • CenterX As Float [read only]
        Gets the X pixel value of the full image point which is at the centre of the view.
        Can only be set programatically by SetScaleAndCenter or SetScaleAndCenterPixels
      • CenterY As Float [read only]
        Gets the Y pixel value of the full image point which is at the centre of the view.
        Can only be set programmatically by SetScaleAndCenter or SetScaleAndCenterPixels
      • CircleColor As Int
        Gets or sets the colour of the inner ring of the circle.
        The default is 0xff20b2aa - LightSeaGreen.
      • CircleDrawnRadius As Float [read only]
        Gets the radius of the circle in pixels as last drawn regardless of the state of EnableCircleScale.
        This can be used to position other drawn items relative to the circle.
      • CircleMinimumRadius As Float
        Gets or sets the minimum radius of the circle as a factor between 0 and 1 of the width of the view.
        This prevents the circle being drawn vanishingly small when zoomed out if EnableCircleScale is True.
        The default is 0.02.
      • CircleRadius As Float
        Gets or sets the radius of the circle as a factor between 0 and 1 of the width of the view or of the full image.
        If EnableCircleScale is True the factor is that of the full size image width.
        If EnableCircleScale is False the factor is that of the width of the view.
        The default is 0.002 which is appropriate if EnableCircleScale is True.
      • CircleWidth As Float
        Gets or sets the width of stroke used to draw the circle (not the radius).
        The value is a factor between 0 and 1 of the radius of the circle as drawn. The default is 0.2.
      • CircleX As Float
        Gets or sets the X position factor of the point on the full image at which to draw the circle.
        The default is 0.5, the centre of the image.
      • CircleXPixels As Float
        Gets or sets the X position in pixels of the point on the full image at which to draw the circle.
        The default is the centre of the image.
      • CircleY As Float
        Gets or sets the Y position factor of the point on the full image at which to draw the circle.
        The default is 0.5, the centre of the image.
      • CircleYPixels As Float
        Gets or sets the Y position in pixels of the point on the full image at which to draw the circle.
        The default is the centre of the image.
      • ClickImageX As Float [read only]
        When accessed in a Click or Longclickevent gets the X pixel position on the full size image of the point clicked.
      • ClickImageY As Float [read only]
        When accessed in a Click or LongClick event gets the Y pixel position on the full size image of the point clicked.
      • ClickViewX As Float [read only]
        When accessed in a Click or LongClick event gets the X pixel position on the view of the point clicked.
      • ClickViewY As Float [read only]
        When accessed in a Click or LongClick event gets the Y pixel position on the view of the point clicked.
      • Color As Int [write only]
      • EnableCircle As Boolean
        Gets or sets whether a circle will be drawn at the image coordinates specified by CircleX and CircleY.
        The default is False.
      • EnableCircleScale As Boolean
        Gets or sets whether the size of the circle will increase and decrease when the image is zoomed.
        If True the circle will resize to bound the same features on the image whatever the zoom.
        If False the circle will maintain a fixed size on the screen of the device.
        The default is True.
      • Enabled As Boolean
      • Height As Int
      • Image As android.graphics.Bitmap [write only]
        Load an existing Bitmap into the view.
        This is unsuitable for large images because it bypasses subsampling and may cause OutOfMemoryErrors.
        This is an easy way to add pan and zoom functionality to an image already in memory.
      • ImageFile As String [write only]
        Load an image from a file saved on the device file system into the view.
        This method can display JPG and PNG images of any size.
        In order to support huge images without running out of memory, a sub-sampled base layer is first loaded.
        Higher resolution tiles are loaded for the visible area as the user zooms in.
      • IsReady As Boolean [read only]
        Gets whether the view is initialised, has dimensions and will display an image.
        Returns True if the view is ready to display an image and accept touch gestures.
      • Left As Int
      • MaxZoom As Float [write only]
      • Padding As Int()
      • PAN_LIMIT_CENTER As Int [read only]
        Allow the image to be panned until a corner reaches the center of the screen but no further.
        Useful when you need to pan any spot on the image to the exact center of the screen.
      • PAN_LIMIT_INSIDE As Int [read only]
        Don't allow the image to be panned off screen.
        As much of the image as possible is always displayed, centered in the view when it is smaller.
      • PAN_LIMIT_OUTSIDE As Int [read only]
        Allow the image to be panned until it is just off screen, but no further.
        The edge of the image will stop when it is flush with the screen edge.
      • PanLimit As Int [write only]
        Sets the image pan limit to one of the PAN_LIMIT values.
        The default is 3 = PAN_LIMIT_CENTER.
      • Parent As Object [read only]
      • Scale As Float [read only]
        Get the current scale of the image as set by the user.
        Can only be set programmatically by SetScaleAndCenter or SetScaleAndCenterPixels.
      • SrcHeight As Int [read only]
        Get the height of the current full size image in pixels.
      • SrcWidth As Int [read only]
        Get the width of the current full size image in pixels.
      • Tag As Object
      • Top As Int
      • Visible As Boolean
      • Width As Int


    • EDIT: Version 2.00 now posted. ScaleImageView is now a Custom View. The API above has been updated. See Post #3 below for more details.
"agraham"
please is there a way to do transparent polygon (10% ....90%)?

I can create only entire transparent (False) or non transparent (True) polygon with:
B4X:
viewcanvas.DrawPath(Path_bod, Colors.Magenta,True, 2)
 

js486dog

Active Member
Licensed User
I think you mean filled, not transparent.

If you want a transparent colour you can define it using Colours.ARGB.
Thank you very much.
Yes Colors.ARGB is for transparent color:
B4X:
viewcanvas.DrawPath(Path_bod, Colors.ARGB(128,255,127,39),True, 2)
 

js486dog

Active Member
Licensed User
My main reason to restart using B4A was to implement on a mobile device my mapping program which covers the entire UK at various scales and requires the ability to display large map images at a specified zoom level and centered on a specified coordinate and then allow the user to drag and zoom the map.

For some strange reason if you set targetSdkVersion="xx" to more than 13 in the manifest it dramatically reduces the largest Bitmap that can be created from a file so most of my map images then no longer open in TouchImageView, jsTouchImageView and even ImageView. These all rely on opening a full resolution bitmap to display an image. It seems to be a Canvas size limitation. Also I failed to get TouchImageView to reliably pre-zoom and pre-position an image so I struggled on with jsTouchImageView and targetSdkVersion="13" and got an acceptable working version but pre-zooming and pre-positioning at an exact point was a bit problematic and not entirely accurate but usually close enough after a few heuristics. It also still used very large amounts of memory for the decoded bitmap.

Having got the app working I then started looking to try to finally solve the image viewing problems and found some source code on GitHub that was written for just this purpose. Instead of loading the entire image at once it only fully decodes from the image file the areas needed for display. As a bonus the pre-zooming and centering is very accurate and works a treat. So after a bit of difficulty getting it to compile I wrapped it, slightly enhanced to draw a centered point indication and am now extremely happy with my mapping program that can now display huge images and target SDK version 26 and later without using a vast amount of memory.

There is a ScaleImageView.htm file in the attached archive that documents the extremely simple API posted below. As always do what readme.txt says.

I can't put a large image file in the archive as it becomes too big to upload here, so find your own large image and do as Readme.txt says.

ScaleImageView
This is a custom image view designed for displaying huge images.
It includes all the standard gestures for zooming and panning images
and provides some extra useful features for animating the image position and scale.
The aim of this library is to solve some of the common problems when displaying large images in Android.
This view extends View and so inherits all the normal View methods.
This view doesn't extend ImageView and isn't intended as a general purpose replacement for it.
It is specialised for the display of photos and other large images, not the display of 9-patches,
shapes and the other types of drawable that ImageView supports.
Supported gestures are:
One finger drag to pan, Two finger pinch to zoom and double tap to zoom in and out.
Pan while zooming, seamless switch between pan and zoom and fling momentum after panning.
Quick scale (one finger zoom - quick double tap then drag)
Events Click and LongClick are provided whose co-ordinates may be accessed in the event code.
ClickViewX, ClickViewY return the position of the Click or LongClick on the view.
ClickImageX and ClickImageY return the position of the Click or LongClick on the source image.
The OnDraw event provides a Canvas that can be used to draw on the view whenever it is redrawn.
The view can draw a circle at a defined position on the original image.
The circle can either have a fixed size that is a fraction of the screen width
or a variable size that always covers the same area of the original size as it is zoomed.
The associated file, ScaleImage.jar, must be located in the Additional Libraries folder.
The line '#AdditionalJar: ScaleImage' must be added to the Main module.
This library uses code from
https://github.com/davemorrissey/subsampling-scale-image-view
That code and this library are licensed under the Apache License 2.0
Copyright [2015] [Dave Morrissey]
Copyright [2018] [Andrew Graham]
Licensed under the Apache License, Version 2.0 (the "License")
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Author: Andrew Graham
Version: 2.0
  • ScaleImageView
    • Events:
      • Click 'The user has tapped on the view. Use ClickImage or ClickView for the coordinates.
      • LongClick 'The user has long pressed the view. Use ClickImage or ClickView for the coordinates.
      • OnDraw (viewcanvas As Canvas) 'The view is being redrawn. Use viewcanvas to draw on it.
    • Functions:
      • BringToFront
      • DesignerCreateView (base As Panel, lw As Label, props As Map)
      • Initialize (arg1 As String)
      • Invalidate
      • Invalidate2 (arg0 As android.graphics.Rect)
      • Invalidate3 (arg0 As Int, arg1 As Int, arg2 As Int, arg3 As Int)
      • IsInitialized As Boolean
      • RemoveView
      • RequestFocus As Boolean
      • SendToBack
      • SetBackgroundImage (arg0 As android.graphics.Bitmap) As BitmapDrawable
      • SetColorAnimated (arg0 As Int, arg1 As Int, arg2 As Int)
      • SetLayout (arg0 As Int, arg1 As Int, arg2 As Int, arg3 As Int)
      • SetLayoutAnimated (arg0 As Int, arg1 As Int, arg2 As Int, arg3 As Int, arg4 As Int)
      • setMaxZoom As Float
        Gets or set the maximum permitted zoom level of the displayed image. The default is 2.0.
        The minimum zoom level is set automatically to fit the entire image to the view.
      • SetScaleAndCenter (scale As Float, x As Float, y As Float, duration As Int)
        Set the zoom of the displayed image and center it on the point (x,y).
        x and y are width and height factor values of the full image size between 0 and 1.
        PanLimit should be set to PAN_LIMIT_CENTER if any point is to be centered.
        Duration sets the duration of the transition in milliseconds.
        Returns False if the image is not ready and the transition was not made otherwise True.
      • SetScaleAndCenterPixels (scale As Float, x As Int, y As Int, duration As Int) As Boolean
        Set the zoom of the displayed image and center it on the point (x,y).
        x and y are full image size x and y pixel values.
        PanLimit should be set to PAN_LIMIT_CENTER if any point is to be centered.
        Duration sets the duration of the transition in milliseconds.
        Returns False if the image is not ready and the transition was not made otherwise True.
      • SetVisibleAnimated (arg0 As Int, arg1 As Boolean)
      • SourceXYtoViewXY (sourcex As Int, sourcey As Int) As Float()
        Returns a Float array containing the X and Y pixel position on the view of the specified point of the full size image.
        If the image coordinates are currently off screen, the view coordinates will also be outside the view
        Index 0 of the array contains the X coordinate and index 1 the Y coordinate.
      • ViewXYtoSourceXY (viewx As Int, viewy As Int) As Float()
        Returns a Float array containing the X and Y pixel position on the full size image of the specified point of the view.
        Index 0 of the array contains the X coordinate and index 1 the Y coordinate.
    • Properties:
      • Background As android.graphics.drawable.Drawable
      • CenterX As Float [read only]
        Gets the X pixel value of the full image point which is at the centre of the view.
        Can only be set programatically by SetScaleAndCenter or SetScaleAndCenterPixels
      • CenterY As Float [read only]
        Gets the Y pixel value of the full image point which is at the centre of the view.
        Can only be set programmatically by SetScaleAndCenter or SetScaleAndCenterPixels
      • CircleColor As Int
        Gets or sets the colour of the inner ring of the circle.
        The default is 0xff20b2aa - LightSeaGreen.
      • CircleDrawnRadius As Float [read only]
        Gets the radius of the circle in pixels as last drawn regardless of the state of EnableCircleScale.
        This can be used to position other drawn items relative to the circle.
      • CircleMinimumRadius As Float
        Gets or sets the minimum radius of the circle as a factor between 0 and 1 of the width of the view.
        This prevents the circle being drawn vanishingly small when zoomed out if EnableCircleScale is True.
        The default is 0.02.
      • CircleRadius As Float
        Gets or sets the radius of the circle as a factor between 0 and 1 of the width of the view or of the full image.
        If EnableCircleScale is True the factor is that of the full size image width.
        If EnableCircleScale is False the factor is that of the width of the view.
        The default is 0.002 which is appropriate if EnableCircleScale is True.
      • CircleWidth As Float
        Gets or sets the width of stroke used to draw the circle (not the radius).
        The value is a factor between 0 and 1 of the radius of the circle as drawn. The default is 0.2.
      • CircleX As Float
        Gets or sets the X position factor of the point on the full image at which to draw the circle.
        The default is 0.5, the centre of the image.
      • CircleXPixels As Float
        Gets or sets the X position in pixels of the point on the full image at which to draw the circle.
        The default is the centre of the image.
      • CircleY As Float
        Gets or sets the Y position factor of the point on the full image at which to draw the circle.
        The default is 0.5, the centre of the image.
      • CircleYPixels As Float
        Gets or sets the Y position in pixels of the point on the full image at which to draw the circle.
        The default is the centre of the image.
      • ClickImageX As Float [read only]
        When accessed in a Click or Longclickevent gets the X pixel position on the full size image of the point clicked.
      • ClickImageY As Float [read only]
        When accessed in a Click or LongClick event gets the Y pixel position on the full size image of the point clicked.
      • ClickViewX As Float [read only]
        When accessed in a Click or LongClick event gets the X pixel position on the view of the point clicked.
      • ClickViewY As Float [read only]
        When accessed in a Click or LongClick event gets the Y pixel position on the view of the point clicked.
      • Color As Int [write only]
      • EnableCircle As Boolean
        Gets or sets whether a circle will be drawn at the image coordinates specified by CircleX and CircleY.
        The default is False.
      • EnableCircleScale As Boolean
        Gets or sets whether the size of the circle will increase and decrease when the image is zoomed.
        If True the circle will resize to bound the same features on the image whatever the zoom.
        If False the circle will maintain a fixed size on the screen of the device.
        The default is True.
      • Enabled As Boolean
      • Height As Int
      • Image As android.graphics.Bitmap [write only]
        Load an existing Bitmap into the view.
        This is unsuitable for large images because it bypasses subsampling and may cause OutOfMemoryErrors.
        This is an easy way to add pan and zoom functionality to an image already in memory.
      • ImageFile As String [write only]
        Load an image from a file saved on the device file system into the view.
        This method can display JPG and PNG images of any size.
        In order to support huge images without running out of memory, a sub-sampled base layer is first loaded.
        Higher resolution tiles are loaded for the visible area as the user zooms in.
      • IsReady As Boolean [read only]
        Gets whether the view is initialised, has dimensions and will display an image.
        Returns True if the view is ready to display an image and accept touch gestures.
      • Left As Int
      • MaxZoom As Float [write only]
      • Padding As Int()
      • PAN_LIMIT_CENTER As Int [read only]
        Allow the image to be panned until a corner reaches the center of the screen but no further.
        Useful when you need to pan any spot on the image to the exact center of the screen.
      • PAN_LIMIT_INSIDE As Int [read only]
        Don't allow the image to be panned off screen.
        As much of the image as possible is always displayed, centered in the view when it is smaller.
      • PAN_LIMIT_OUTSIDE As Int [read only]
        Allow the image to be panned until it is just off screen, but no further.
        The edge of the image will stop when it is flush with the screen edge.
      • PanLimit As Int [write only]
        Sets the image pan limit to one of the PAN_LIMIT values.
        The default is 3 = PAN_LIMIT_CENTER.
      • Parent As Object [read only]
      • Scale As Float [read only]
        Get the current scale of the image as set by the user.
        Can only be set programmatically by SetScaleAndCenter or SetScaleAndCenterPixels.
      • SrcHeight As Int [read only]
        Get the height of the current full size image in pixels.
      • SrcWidth As Int [read only]
        Get the width of the current full size image in pixels.
      • Tag As Object
      • Top As Int
      • Visible As Boolean
      • Width As Int


    • EDIT: Version 2.00 now posted. ScaleImageView is now a Custom View. The API above has been updated. See Post #3 below for more details.
Please "Andrew".
I got a little problem.

CenterX, CenterY is Float
but
SetScaleAndCenterPixels(x and y is Int)

When I read the point position with CenterX, CenterY (as float) and after I would like to find the same position with SetScaleAndCenterPixels (x,y as Int), the result is not accurate.

Please can you change SetScaleAndCenterPixels(x and y as Float) in the source code ?
 

agraham

Expert
Licensed User
Please can you change SetScaleAndCenterPixels(x and y as Float) in the source code ?
There is no point in doing this as pixel values are inherently integer values. What inaccuracy you experiencing? Have you missed
SetScaleAndCenter (scale As Float, x As Float, y As Float, duration As Int)
In general the float values are factors of the image width and height and range from 0 to 1. The pixel values are absolute pixel values. You can transform between the two by mutiplying a float factor value by the image width or height pixel size, or by dividing a pixel value by the image width or height pixel size.
 

js486dog

Active Member
Licensed User
There is no point in doing this as pixel values are inherently integer values. What inaccuracy you experiencing? Have you missed
SetScaleAndCenter (scale As Float, x As Float, y As Float, duration As Int)
In general the float values are factors of the image width and height and range from 0 to 1. The pixel values are absolute pixel values. You can transform between the two by mutiplying a float factor value by the image width or height pixel size, or by dividing a pixel value by the image width or height pixel size.
Please look at the attached project.

When you pan map (pic1) - coordinates of center point are calculated in "OnDraw" : ('x y suradnice stredoveho bodu) and displayed in the labels: lbl_yobr, lbl_xobr

When you use Find button (pic2) and you find the same coordinates(wgs) like displayed, there are very innacurate coordinates in the labels: lbl_yobr, lbl_xobr

But try to do disable this line in Sub Button9_Click :

B4X:
'ScaleImageView1.SetScaleAndCenterPixels(ScaleImageView1.Scale,kde_lon_pix, kde_lat_pix, 1)

and coordinates in the labels: lbl_yobr, lbl_xobr are OK.
 

Attachments

  • pic1.jpg
    pic1.jpg
    17.5 KB · Views: 122
  • pic2.jpg
    pic2.jpg
    22.5 KB · Views: 120
  • w_map.zip
    101.3 KB · Views: 104
Last edited:

js486dog

Active Member
Licensed User
I suspect you may have an integer rounding problem as your map is not sufficiently detailed.
I can use the map with 70 zoom.
But why is all OK when I do disable this line in Sub Button9_Click ? :
B4X:
'ScaleImageView1.SetScaleAndCenterPixels(ScaleImageView1.Scale,kde_lon_pix, kde_lat_pix, 1)
 

js486dog

Active Member
Licensed User
Try a map with more pixels. You move the map, suffer the rounding because there aren't enough pixels and the error is reflected in your textbox co-ordinates which are updated when the map is redrawn.
Thank you very much Andrew. Now I understand how it works.
 

agraham

Expert
Licensed User
Version 2.20 of ScaleImageView has the following changes

Added Orientation, DoubleTapZoomDuration, TileBackgroundColor, Recycle and ResetScaleAndCenter.

Internal changes to resolve co-ordinate problems when an app is used as the lower or right app in split screen mode.
 

agraham

Expert
Licensed User
Version 2.30 now has a readable Image property to get the image presently displayed by ScaleImageView. Add this somewhere in the existing demo in the v2.20 archive
B4X:
    Dim b As Bitmap
    b.Initialize3(ScaleImageView1.Image)
    Dim Out As OutputStream
    Out = File.OpenOutput( File.DirRootExternal & "/Download/", "Testxv.jpg", False)
    b.WriteToStream(Out, 90, "JPEG")
    Out.Close
 
Last edited:

GeoT

Active Member
Licensed User
Hello.

I have managed to put these ScaleImageView in the Dominex's SlidingPanels, which are a class. Specifically in its ModeFullScreen format.
Is there a way to capture the Touch event and distinguish it from the SlidingPanels Touch event?
And put some indicator or event as the image of the ScaleImageView is zoomed in?
Would you Agraham want add it to the library?
Would it be feasible?

Regards and thanks.
 
Last edited:

GeoT

Active Member
Licensed User
I want to do as WhatsApp does in its activity to send several images.
Each ScaleImageView contains each image, and the SlidingPanels class changes the display from one image to another.
I want the image to be zoomed in to not be able to change the image. And when it is not, with the horizontal gesture you slide the container panel and its corresponding ScaleImageView.
But that gesture doesn't surface now.

Thanks
 
Top