moagrius/TileView

StreamProvider asked to provide data for rows and columns out of bounds with to small images

tva-TIS opened this issue · 5 comments

I don't quite know if this really is an issue or belongs to the kind of exceptions StreamProviders seem to be allowed to throw.

I've written a custom StreamProvider that provides data that has been prepared dynamically. Since I don't have control over which image is displayed in my (probably special) usecase I had some images in the TileView that have lower resolution than the TileView covers. The StreamProvider is then asked to provide data for rows and columns that dont exist, as start and end row and column are calculated by the viewport scaling.
Example:
One image that i tested had dimensions of 190x266 pixels and the formula I chose to dynamically determine the TileSize chose a TileSize of 14. At this setup the Provider can Provide 190 / 14 = 13, 5 ~ 14 columns and 266 / 14 = 19 rows. But as the viewport on the device has a multiple of the dimensions the provider is aked to provide 45 columns and 63 rows.

As I said I don't now if this requires a change in the source code, but i thought that this case might appear should at least be brought to the authors attention.

Sorry, I've read your example a couple of times and I'm not at all clear on the issue. What specifically is happening? Maybe you could post your project somewhere with STR...

I'm afraid I probably cannot share the whole project, but i used a project that only had the following java file (and the layout file for the tileView) modified to test my hypothesis of the correlation of the image size with the NullpointerExceptions.
MainActivity.zip

The following code examples however are from the real project.

My StreamProvider looks like this:

    private class BitmapStreamProvider implements StreamProvider {
        @Override
        public InputStream getStream(int column, int row, Context context, Object data) {

            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            try {
                ((SparseArray<SparseArray<Bitmap>>) data).get(column).get(row).compress(Bitmap.CompressFormat.PNG, 0, bos);
            } catch (NullPointerException e){
                Log.w(TAG, "Invalid row or column requested, column " + column + " and row " + row, e);
                return null;
            }
            byte[] bitmapdata = bos.toByteArray();
            return new ByteArrayInputStream(bitmapdata);
        }
    }

And the tileView is initialized as following:

new TileView.Builder(tileView)
                .defineZoomLevel(bits)
                .setTileSize(tileSize)
                .setSize(originalBitmap.getWidth(), originalBitmap.getHeight())
                .setStreamProvider(new BitmapStreamProvider())
                .installPlugin(new MarkerPlugin(context))
                .build();

... with bits being the two-dimensional SparseArray with originalBitmap sliced down to tileSize width and height.
So in my example originalBitmap would now be an image of size 190*266 pixels. The tileSize is calculated by tileSize = (int) Math.sqrt(Math.sqrt((originalBitmap.getWidth() * originalBitmap.getHeight()))); to have larger tileSize for larger images. For the example image the tileSize is 14. The algorithm cutting the bitmaps fills bits with 14 SparseArrays each containing 19 bitmaps which would fit the calculations from my first comment. In populateTileGridFromViewport however (assuming no scale for now), the tileView calculates to go over 64 (0-64) rows and 49 (0-49) columns. tileSize stays 14 as getScale() and mCurrentDetail.getSample() both return 1. rows.start and columns.start both are set to 0 as we want to start in the top left corner of the tiles. rows.end and columns.end however use viewport values which are not null, on my developement device viewport.bottom is 896, and viewport.right is 688 which are the layout dimensions of the tileView. When calculated through the tileSize these values will result in a row count that is bigger than the amount of tiles that could be made from the image as long as the image has smaller dimensions than the viewport, because both have been divided by the tileSize, which will lead to NullPointerExceptions in the StreamProvider.

As I said: I'm both aware that the StreamProviders are allowed to throw Exceptions and your library usually displays quite large images, but thought this should be to attention anyway, even if its only for someone wandering about the occuring NullPointerExceptions.

do you see the problem in the math where the grid is computed?

I will close this issue now in favor of #531, centralizing the knowledge of the problem . If the solution created there will not help in my usecase (which is unlikely) I'm gonna reopen it again.

Duplicate of #531

There seem to be further issues with images that do not cover the Viewport of the TileView, even when applying the current change suggestions of #531 (comment).
Discovered so far:

  • When setting MaximumScale/ScaleLimits the onClickListeners of the markers aren't called anymore or markers may be invisible entirely
  • Image is not correctly vertically centered and first appears at the top of the TileView until touched when in MinimumScaleMode Contain
  • Setting a MinimumScaleMode does not have an effect unless MaximumScale/ScaleLimits were set

I will see how far I can dig into the issues myself, but won't open separate issues for know as I don't know how far these issues are connected to each other or if they may be fixed in additional changes within #531