1f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka/*
2f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * Copyright (C) 2013 The Android Open Source Project
3f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka *
4f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * Licensed under the Apache License, Version 2.0 (the "License");
5f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * you may not use this file except in compliance with the License.
6f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * You may obtain a copy of the License at
7f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka *
8f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka *      http://www.apache.org/licenses/LICENSE-2.0
9f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka *
10f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * Unless required by applicable law or agreed to in writing, software
11f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * distributed under the License is distributed on an "AS IS" BASIS,
12f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * See the License for the specific language governing permissions and
14f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * limitations under the License.
15f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka */
16f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
17f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkapackage com.android.photos;
18f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
19f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.annotation.TargetApi;
20f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.content.Context;
21f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.content.res.Resources;
22f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.graphics.Bitmap;
23f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.graphics.BitmapFactory;
24f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.graphics.BitmapRegionDecoder;
25f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.graphics.Canvas;
26f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.graphics.Paint;
27f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.graphics.Rect;
28f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.net.Uri;
2978776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyalimport android.opengl.GLUtils;
30f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.os.Build;
31f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport android.util.Log;
32f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
335bfa1defa156cde58a9c95ba3f5b7ae4e8ec7a96Sunny Goyalimport com.android.gallery3d.common.ExifOrientation;
34f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport com.android.gallery3d.common.Utils;
35f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport com.android.gallery3d.glrenderer.BasicTexture;
36f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport com.android.gallery3d.glrenderer.BitmapTexture;
37f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport com.android.photos.views.TiledImageRenderer;
38841a502864d307341945eae62569b92cada93e75Sunny Goyalimport com.android.wallpaperpicker.common.InputStreamProvider;
39f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
40841a502864d307341945eae62569b92cada93e75Sunny Goyalimport java.io.File;
41f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport java.io.IOException;
42f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaimport java.io.InputStream;
43f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
44f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkainterface SimpleBitmapRegionDecoder {
45f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    int getWidth();
46f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    int getHeight();
47f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    Bitmap decodeRegion(Rect wantRegion, BitmapFactory.Options options);
48f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka}
49f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
50f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaclass SimpleBitmapRegionDecoderWrapper implements SimpleBitmapRegionDecoder {
51f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    BitmapRegionDecoder mDecoder;
52f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    private SimpleBitmapRegionDecoderWrapper(BitmapRegionDecoder decoder) {
53f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        mDecoder = decoder;
54f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
55841a502864d307341945eae62569b92cada93e75Sunny Goyal
56f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    public static SimpleBitmapRegionDecoderWrapper newInstance(
57f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            InputStream is, boolean isShareable) {
58f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        try {
59f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            BitmapRegionDecoder d = BitmapRegionDecoder.newInstance(is, isShareable);
60f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            if (d != null) {
61f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                return new SimpleBitmapRegionDecoderWrapper(d);
62f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            }
63f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        } catch (IOException e) {
64f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            Log.w("BitmapRegionTileSource", "getting decoder failed", e);
65f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            return null;
66f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
67f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        return null;
68f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
69f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    public int getWidth() {
70f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        return mDecoder.getWidth();
71f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
72f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    public int getHeight() {
73f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        return mDecoder.getHeight();
74f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
75f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    public Bitmap decodeRegion(Rect wantRegion, BitmapFactory.Options options) {
76f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        return mDecoder.decodeRegion(wantRegion, options);
77f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
78f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka}
79f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
80f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkaclass DumbBitmapRegionDecoder implements SimpleBitmapRegionDecoder {
81f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    Bitmap mBuffer;
82f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    Canvas mTempCanvas;
83f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    Paint mTempPaint;
84f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    private DumbBitmapRegionDecoder(Bitmap b) {
85f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        mBuffer = b;
86f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
87f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    public static DumbBitmapRegionDecoder newInstance(InputStream is) {
88f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        Bitmap b = BitmapFactory.decodeStream(is);
89f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        if (b != null) {
90f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            return new DumbBitmapRegionDecoder(b);
91f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
92f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        return null;
93f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
94f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    public int getWidth() {
95f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        return mBuffer.getWidth();
96f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
97f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    public int getHeight() {
98f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        return mBuffer.getHeight();
99f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
100f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    public Bitmap decodeRegion(Rect wantRegion, BitmapFactory.Options options) {
101f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        if (mTempCanvas == null) {
102f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mTempCanvas = new Canvas();
103f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mTempPaint = new Paint();
104f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mTempPaint.setFilterBitmap(true);
105f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
106f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        int sampleSize = Math.max(options.inSampleSize, 1);
107f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        Bitmap newBitmap = Bitmap.createBitmap(
108f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                wantRegion.width() / sampleSize,
109f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                wantRegion.height() / sampleSize,
110f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                Bitmap.Config.ARGB_8888);
111f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        mTempCanvas.setBitmap(newBitmap);
112f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        mTempCanvas.save();
113f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        mTempCanvas.scale(1f / sampleSize, 1f / sampleSize);
114f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        mTempCanvas.drawBitmap(mBuffer, -wantRegion.left, -wantRegion.top, mTempPaint);
115f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        mTempCanvas.restore();
116f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        mTempCanvas.setBitmap(null);
117f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        return newBitmap;
118f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
119f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka}
120f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
121f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka/**
122f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * A {@link com.android.photos.views.TiledImageRenderer.TileSource} using
123f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka * {@link BitmapRegionDecoder} to wrap a local file
124f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka */
125f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)
126f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurkapublic class BitmapRegionTileSource implements TiledImageRenderer.TileSource {
127f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
128f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    private static final String TAG = "BitmapRegionTileSource";
129f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
130f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    private static final int GL_SIZE_LIMIT = 2048;
131f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    // This must be no larger than half the size of the GL_SIZE_LIMIT
132f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    // due to decodePreview being allowed to be up to 2x the size of the target
13378776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyal    private static final int MAX_PREVIEW_SIZE = GL_SIZE_LIMIT / 2;
134f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
135f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    public static abstract class BitmapSource {
136f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        private SimpleBitmapRegionDecoder mDecoder;
137f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        private Bitmap mPreview;
138f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        private int mRotation;
139f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        public enum State { NOT_LOADED, LOADED, ERROR_LOADING };
140f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        private State mState = State.NOT_LOADED;
14178776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyal
142371bf2f71613dbe646555190789f5d30037fa084Tony Wickham        /** Returns whether loading was successful. */
1432a218b718e5f498a284341532178c84fe3850e63Sunny Goyal        public boolean loadInBackground(InBitmapProvider bitmapProvider) {
1445bfa1defa156cde58a9c95ba3f5b7ae4e8ec7a96Sunny Goyal            mRotation = getExifRotation();
145f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mDecoder = loadBitmapRegionDecoder();
146f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            if (mDecoder == null) {
147f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                mState = State.ERROR_LOADING;
148f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                return false;
149f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            } else {
150f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                int width = mDecoder.getWidth();
151f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                int height = mDecoder.getHeight();
152f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
15378776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyal                BitmapFactory.Options opts = new BitmapFactory.Options();
15478776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyal                opts.inPreferredConfig = Bitmap.Config.ARGB_8888;
15578776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyal                opts.inPreferQualityOverSpeed = true;
1562a218b718e5f498a284341532178c84fe3850e63Sunny Goyal
15778776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyal                float scale = (float) MAX_PREVIEW_SIZE / Math.max(width, height);
158841a502864d307341945eae62569b92cada93e75Sunny Goyal                opts.inSampleSize = Utils.computeSampleSizeLarger(scale);
15978776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyal                opts.inJustDecodeBounds = false;
16078776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyal                opts.inMutable = true;
16178776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyal
16278776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyal                if (bitmapProvider != null) {
16378776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyal                    int expectedPixles = (width / opts.inSampleSize) * (height / opts.inSampleSize);
16478776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyal                    Bitmap reusableBitmap = bitmapProvider.forPixelCount(expectedPixles);
16578776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyal                    if (reusableBitmap != null) {
16678776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyal                        // Try loading with reusable bitmap
16778776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyal                        opts.inBitmap = reusableBitmap;
16878776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyal                        try {
16978776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyal                            mPreview = loadPreviewBitmap(opts);
17078776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyal                        } catch (IllegalArgumentException e) {
171371bf2f71613dbe646555190789f5d30037fa084Tony Wickham                            Log.d(TAG, "Unable to reuse bitmap", e);
17278776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyal                            opts.inBitmap = null;
17378776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyal                            mPreview = null;
1742a218b718e5f498a284341532178c84fe3850e63Sunny Goyal                        }
1752a218b718e5f498a284341532178c84fe3850e63Sunny Goyal                    }
176f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                }
17778776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyal                if (mPreview == null) {
17878776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyal                    mPreview = loadPreviewBitmap(opts);
17978776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyal                }
180371bf2f71613dbe646555190789f5d30037fa084Tony Wickham                if (mPreview == null) {
181371bf2f71613dbe646555190789f5d30037fa084Tony Wickham                    mState = State.ERROR_LOADING;
182371bf2f71613dbe646555190789f5d30037fa084Tony Wickham                    return false;
183371bf2f71613dbe646555190789f5d30037fa084Tony Wickham                }
18478776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyal
18578776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyal                // Verify that the bitmap can be used on GL surface
18678776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyal                try {
18778776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyal                    GLUtils.getInternalFormat(mPreview);
18878776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyal                    GLUtils.getType(mPreview);
18978776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyal                    mState = State.LOADED;
19078776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyal                } catch (IllegalArgumentException e) {
19178776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyal                    Log.d(TAG, "Image cannot be rendered on a GL surface", e);
19278776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyal                    mState = State.ERROR_LOADING;
19378776d77ca661361dcdc6f84d740a95b51e28cffSunny Goyal                }
194371bf2f71613dbe646555190789f5d30037fa084Tony Wickham                return mState == State.LOADED;
195f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            }
196f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
197f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
198f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        public State getLoadingState() {
199f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            return mState;
200f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
201f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
202f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        public SimpleBitmapRegionDecoder getBitmapRegionDecoder() {
203f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            return mDecoder;
204f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
205f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
206f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        public Bitmap getPreviewBitmap() {
207f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            return mPreview;
208f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
209f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
210f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        public int getRotation() {
211f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            return mRotation;
212f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
213f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
2145bfa1defa156cde58a9c95ba3f5b7ae4e8ec7a96Sunny Goyal        public abstract int getExifRotation();
215f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        public abstract SimpleBitmapRegionDecoder loadBitmapRegionDecoder();
216f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        public abstract Bitmap loadPreviewBitmap(BitmapFactory.Options options);
2172a218b718e5f498a284341532178c84fe3850e63Sunny Goyal
2182a218b718e5f498a284341532178c84fe3850e63Sunny Goyal        public interface InBitmapProvider {
2192a218b718e5f498a284341532178c84fe3850e63Sunny Goyal            Bitmap forPixelCount(int count);
2202a218b718e5f498a284341532178c84fe3850e63Sunny Goyal        }
221f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
222f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
223841a502864d307341945eae62569b92cada93e75Sunny Goyal    public static class InputStreamSource extends BitmapSource {
224841a502864d307341945eae62569b92cada93e75Sunny Goyal        private final InputStreamProvider mStreamProvider;
225841a502864d307341945eae62569b92cada93e75Sunny Goyal        private final Context mContext;
226841a502864d307341945eae62569b92cada93e75Sunny Goyal
227841a502864d307341945eae62569b92cada93e75Sunny Goyal        public InputStreamSource(Context context, Uri uri) {
228841a502864d307341945eae62569b92cada93e75Sunny Goyal            this(InputStreamProvider.fromUri(context, uri), context);
229f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
2305bfa1defa156cde58a9c95ba3f5b7ae4e8ec7a96Sunny Goyal
231841a502864d307341945eae62569b92cada93e75Sunny Goyal        public InputStreamSource(Resources res, int resId, Context context) {
232841a502864d307341945eae62569b92cada93e75Sunny Goyal            this(InputStreamProvider.fromResource(res, resId), context);
233f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
234f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
235841a502864d307341945eae62569b92cada93e75Sunny Goyal        public InputStreamSource(InputStreamProvider streamProvider, Context context) {
236841a502864d307341945eae62569b92cada93e75Sunny Goyal            mStreamProvider = streamProvider;
237f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mContext = context;
238f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
239841a502864d307341945eae62569b92cada93e75Sunny Goyal
240f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        @Override
241f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        public SimpleBitmapRegionDecoder loadBitmapRegionDecoder() {
242f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            try {
243841a502864d307341945eae62569b92cada93e75Sunny Goyal                InputStream is = mStreamProvider.newStreamNotNull();
244f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                SimpleBitmapRegionDecoder regionDecoder =
245f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                        SimpleBitmapRegionDecoderWrapper.newInstance(is, false);
246f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                Utils.closeSilently(is);
247f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                if (regionDecoder == null) {
248841a502864d307341945eae62569b92cada93e75Sunny Goyal                    is = mStreamProvider.newStreamNotNull();
249f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    regionDecoder = DumbBitmapRegionDecoder.newInstance(is);
250f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    Utils.closeSilently(is);
251f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                }
252f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                return regionDecoder;
253841a502864d307341945eae62569b92cada93e75Sunny Goyal            } catch (IOException e) {
254841a502864d307341945eae62569b92cada93e75Sunny Goyal                Log.e("InputStreamSource", "Failed to load stream", e);
255f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                return null;
256f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            }
257f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
258841a502864d307341945eae62569b92cada93e75Sunny Goyal
259841a502864d307341945eae62569b92cada93e75Sunny Goyal        @Override
260841a502864d307341945eae62569b92cada93e75Sunny Goyal        public int getExifRotation() {
261841a502864d307341945eae62569b92cada93e75Sunny Goyal            return mStreamProvider.getRotationFromExif(mContext);
262841a502864d307341945eae62569b92cada93e75Sunny Goyal        }
263841a502864d307341945eae62569b92cada93e75Sunny Goyal
264f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        @Override
265f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        public Bitmap loadPreviewBitmap(BitmapFactory.Options options) {
266f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            try {
267841a502864d307341945eae62569b92cada93e75Sunny Goyal                InputStream is = mStreamProvider.newStreamNotNull();
268f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                Bitmap b = BitmapFactory.decodeStream(is, null, options);
269f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                Utils.closeSilently(is);
270f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                return b;
271841a502864d307341945eae62569b92cada93e75Sunny Goyal            } catch (IOException | OutOfMemoryError e) {
272841a502864d307341945eae62569b92cada93e75Sunny Goyal                Log.e("InputStreamSource", "Failed to load stream", e);
273f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                return null;
274f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            }
275f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
276f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
277f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
278841a502864d307341945eae62569b92cada93e75Sunny Goyal    public static class FilePathBitmapSource extends InputStreamSource {
279841a502864d307341945eae62569b92cada93e75Sunny Goyal        private String mPath;
280841a502864d307341945eae62569b92cada93e75Sunny Goyal        public FilePathBitmapSource(File file, Context context) {
281841a502864d307341945eae62569b92cada93e75Sunny Goyal            super(context, Uri.fromFile(file));
282841a502864d307341945eae62569b92cada93e75Sunny Goyal            mPath = file.getAbsolutePath();
283f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
2845bfa1defa156cde58a9c95ba3f5b7ae4e8ec7a96Sunny Goyal
285f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        @Override
2865bfa1defa156cde58a9c95ba3f5b7ae4e8ec7a96Sunny Goyal        public int getExifRotation() {
287841a502864d307341945eae62569b92cada93e75Sunny Goyal            return ExifOrientation.readRotation(mPath);
288f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
289f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
290f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
291f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    SimpleBitmapRegionDecoder mDecoder;
292f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    int mWidth;
293f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    int mHeight;
294f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    int mTileSize;
295f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    private BasicTexture mPreview;
296f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    private final int mRotation;
297f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
298f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    // For use only by getTile
299f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    private Rect mWantRegion = new Rect();
300f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    private BitmapFactory.Options mOptions;
301f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
3022a218b718e5f498a284341532178c84fe3850e63Sunny Goyal    public BitmapRegionTileSource(Context context, BitmapSource source, byte[] tempStorage) {
303f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        mTileSize = TiledImageRenderer.suggestedTileSize(context);
304f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        mRotation = source.getRotation();
305f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        mDecoder = source.getBitmapRegionDecoder();
306f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        if (mDecoder != null) {
307f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mWidth = mDecoder.getWidth();
308f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mHeight = mDecoder.getHeight();
309f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mOptions = new BitmapFactory.Options();
310f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
311f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            mOptions.inPreferQualityOverSpeed = true;
3122a218b718e5f498a284341532178c84fe3850e63Sunny Goyal            mOptions.inTempStorage = tempStorage;
3132a218b718e5f498a284341532178c84fe3850e63Sunny Goyal
3142a218b718e5f498a284341532178c84fe3850e63Sunny Goyal            Bitmap preview = source.getPreviewBitmap();
3152a218b718e5f498a284341532178c84fe3850e63Sunny Goyal            if (preview != null &&
3162a218b718e5f498a284341532178c84fe3850e63Sunny Goyal                    preview.getWidth() <= GL_SIZE_LIMIT && preview.getHeight() <= GL_SIZE_LIMIT) {
317f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                    mPreview = new BitmapTexture(preview);
3182a218b718e5f498a284341532178c84fe3850e63Sunny Goyal            } else {
3192a218b718e5f498a284341532178c84fe3850e63Sunny Goyal                Log.w(TAG, String.format(
3202a218b718e5f498a284341532178c84fe3850e63Sunny Goyal                        "Failed to create preview of apropriate size! "
3212a218b718e5f498a284341532178c84fe3850e63Sunny Goyal                        + " in: %dx%d, out: %dx%d",
3222a218b718e5f498a284341532178c84fe3850e63Sunny Goyal                        mWidth, mHeight,
323371bf2f71613dbe646555190789f5d30037fa084Tony Wickham                        preview == null ? -1 : preview.getWidth(),
324371bf2f71613dbe646555190789f5d30037fa084Tony Wickham                        preview == null ? -1 : preview.getHeight()));
325f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            }
326f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
327f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
328f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
3292a218b718e5f498a284341532178c84fe3850e63Sunny Goyal    public Bitmap getBitmap() {
3302a218b718e5f498a284341532178c84fe3850e63Sunny Goyal        return mPreview instanceof BitmapTexture ? ((BitmapTexture) mPreview).getBitmap() : null;
3312a218b718e5f498a284341532178c84fe3850e63Sunny Goyal    }
3322a218b718e5f498a284341532178c84fe3850e63Sunny Goyal
333f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    @Override
334f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    public int getTileSize() {
335f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        return mTileSize;
336f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
337f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
338f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    @Override
339f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    public int getImageWidth() {
340f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        return mWidth;
341f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
342f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
343f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    @Override
344f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    public int getImageHeight() {
345f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        return mHeight;
346f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
347f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
348f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    @Override
349f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    public BasicTexture getPreview() {
350f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        return mPreview;
351f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
352f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
353f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    @Override
354f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    public int getRotation() {
355f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        return mRotation;
356f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
357f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
358f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    @Override
359f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    public Bitmap getTile(int level, int x, int y, Bitmap bitmap) {
360f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        int tileSize = getTileSize();
361f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        int t = tileSize << level;
362f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        mWantRegion.set(x, y, x + t, y + t);
363f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
364f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        if (bitmap == null) {
365f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            bitmap = Bitmap.createBitmap(tileSize, tileSize, Bitmap.Config.ARGB_8888);
366f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
367f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
368f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        mOptions.inSampleSize = (1 << level);
369f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        mOptions.inBitmap = bitmap;
370f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
371f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        try {
372f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            bitmap = mDecoder.decodeRegion(mWantRegion, mOptions);
373f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        } finally {
374f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            if (mOptions.inBitmap != bitmap && mOptions.inBitmap != null) {
375f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka                mOptions.inBitmap = null;
376f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            }
377f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
378f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka
379f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        if (bitmap == null) {
380f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka            Log.w("BitmapRegionTileSource", "fail in decoding region");
381f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        }
382f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka        return bitmap;
383f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka    }
384f1b220be2b401f203cfb490f2e78af32700ef4bbMichael Jurka}
385