1f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin/*
2f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Copyright (C) 2010 The Android Open Source Project
3f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *
4f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Licensed under the Apache License, Version 2.0 (the "License");
5f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * you may not use this file except in compliance with the License.
6f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * You may obtain a copy of the License at
7f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *
8f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *      http://www.apache.org/licenses/LICENSE-2.0
9f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *
10f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Unless required by applicable law or agreed to in writing, software
11f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * distributed under the License is distributed on an "AS IS" BASIS,
12f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * See the License for the specific language governing permissions and
14f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * limitations under the License.
15f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin */
16f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
17f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpackage com.android.gallery3d.ui;
18f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
19ed97d58f2028938aeb3e49c10ac598cc3e7a28c6Owen Linimport android.annotation.TargetApi;
20f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.graphics.Bitmap;
21f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.graphics.Bitmap.Config;
22f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.graphics.BitmapFactory;
23f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.graphics.BitmapRegionDecoder;
2430aa92093126f00b46543ae3b5d1035a3270f240Owen Linimport android.graphics.Canvas;
25f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.graphics.Rect;
26f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
2730aa92093126f00b46543ae3b5d1035a3270f240Owen Linimport com.android.gallery3d.common.ApiHelper;
281b2cfc142830875991dd190f163ad14af66d9e11Owen Linimport com.android.gallery3d.common.Utils;
29f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescuimport com.android.photos.data.GalleryBitmapPool;
301b2cfc142830875991dd190f163ad14af66d9e11Owen Lin
316dc3866ac10cc6bf38e73f098fe1988d1a6eecd4Your Namepublic class TileImageViewAdapter implements TileImageView.TileSource {
321b2cfc142830875991dd190f163ad14af66d9e11Owen Lin    private static final String TAG = "TileImageViewAdapter";
3315b351a22d02e89d882fc9fe32b3f4c512080e0aChih-Chung Chang    protected ScreenNail mScreenNail;
34b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang    protected boolean mOwnScreenNail;
35f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    protected BitmapRegionDecoder mRegionDecoder;
36f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    protected int mImageWidth;
37f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    protected int mImageHeight;
38f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    protected int mLevelCount;
39f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
40f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public TileImageViewAdapter() {
41f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
42f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
43f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public synchronized void clear() {
44030f8dad6aefc42d0af39bc1b93f370937d3e2abOwen Lin        mScreenNail = null;
45f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mImageWidth = 0;
46f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mImageHeight = 0;
47f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mLevelCount = 0;
48f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mRegionDecoder = null;
49f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
50f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
51030f8dad6aefc42d0af39bc1b93f370937d3e2abOwen Lin    // Caller is responsible to recycle the ScreenNail
5215b351a22d02e89d882fc9fe32b3f4c512080e0aChih-Chung Chang    public synchronized void setScreenNail(
5315b351a22d02e89d882fc9fe32b3f4c512080e0aChih-Chung Chang            ScreenNail screenNail, int width, int height) {
54b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang        Utils.checkNotNull(screenNail);
55030f8dad6aefc42d0af39bc1b93f370937d3e2abOwen Lin        mScreenNail = screenNail;
56f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mImageWidth = width;
57f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mImageHeight = height;
58f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mRegionDecoder = null;
59f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mLevelCount = 0;
60f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
61f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
62f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public synchronized void setRegionDecoder(BitmapRegionDecoder decoder) {
63f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mRegionDecoder = Utils.checkNotNull(decoder);
64f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mImageWidth = decoder.getWidth();
65f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mImageHeight = decoder.getHeight();
66f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mLevelCount = calculateLevelCount();
67f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
68f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
69f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int calculateLevelCount() {
70f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return Math.max(0, Utils.ceilLog2(
7115b351a22d02e89d882fc9fe32b3f4c512080e0aChih-Chung Chang                (float) mImageWidth / mScreenNail.getWidth()));
72f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
73f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
74c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin    // Gets a sub image on a rectangle of the current photo. For example,
75c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin    // getTile(1, 50, 50, 100, 3, pool) means to get the region located
76c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin    // at (50, 50) with sample level 1 (ie, down sampled by 2^1) and the
77c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin    // target tile size (after sampling) 100 with border 3.
78c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin    //
79c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin    // From this spec, we can infer the actual tile size to be
80c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin    // 100 + 3x2 = 106, and the size of the region to be extracted from the
81c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin    // photo to be 200 with border 6.
82c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin    //
83c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin    // As a result, we should decode region (50-6, 50-6, 250+6, 250+6) or
84c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin    // (44, 44, 256, 256) from the original photo and down sample it to 106.
85ed97d58f2028938aeb3e49c10ac598cc3e7a28c6Owen Lin    @TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB)
86f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
87f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu    public Bitmap getTile(int level, int x, int y, int tileSize) {
8830aa92093126f00b46543ae3b5d1035a3270f240Owen Lin        if (!ApiHelper.HAS_REUSING_BITMAP_IN_BITMAP_REGION_DECODER) {
896dc3866ac10cc6bf38e73f098fe1988d1a6eecd4Your Name            return getTileWithoutReusingBitmap(level, x, y, tileSize);
9030aa92093126f00b46543ae3b5d1035a3270f240Owen Lin        }
9130aa92093126f00b46543ae3b5d1035a3270f240Owen Lin
92c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin        int t = tileSize << level;
93c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin
946dc3866ac10cc6bf38e73f098fe1988d1a6eecd4Your Name        Rect wantRegion = new Rect(x, y, x + t, y + t);
95f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
96c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin        boolean needClear;
976fc8d72ce0814a70c74be71fb9d0775b8d7ca768Ray Chen        BitmapRegionDecoder regionDecoder = null;
98c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin
996fc8d72ce0814a70c74be71fb9d0775b8d7ca768Ray Chen        synchronized (this) {
1006fc8d72ce0814a70c74be71fb9d0775b8d7ca768Ray Chen            regionDecoder = mRegionDecoder;
1016fc8d72ce0814a70c74be71fb9d0775b8d7ca768Ray Chen            if (regionDecoder == null) return null;
102c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin
103c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin            // We need to clear a reused bitmap, if wantRegion is not fully
104c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin            // within the image.
105c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin            needClear = !new Rect(0, 0, mImageWidth, mImageHeight)
106c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin                    .contains(wantRegion);
1076fc8d72ce0814a70c74be71fb9d0775b8d7ca768Ray Chen        }
1086fc8d72ce0814a70c74be71fb9d0775b8d7ca768Ray Chen
109f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu        Bitmap bitmap = GalleryBitmapPool.getInstance().get(tileSize, tileSize);
110c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin        if (bitmap != null) {
111c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin            if (needClear) bitmap.eraseColor(0);
112c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin        } else {
1136dc3866ac10cc6bf38e73f098fe1988d1a6eecd4Your Name            bitmap = Bitmap.createBitmap(tileSize, tileSize, Config.ARGB_8888);
114c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin        }
115f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
116f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        BitmapFactory.Options options = new BitmapFactory.Options();
117f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        options.inPreferredConfig = Config.ARGB_8888;
118f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        options.inPreferQualityOverSpeed = true;
119f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        options.inSampleSize =  (1 << level);
120c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin        options.inBitmap = bitmap;
121c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin
122c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin        try {
123c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin            // In CropImage, we may call the decodeRegion() concurrently.
124c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin            synchronized (regionDecoder) {
125c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin                bitmap = regionDecoder.decodeRegion(wantRegion, options);
126c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin            }
127c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin        } finally {
128c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin            if (options.inBitmap != bitmap && options.inBitmap != null) {
129f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu                GalleryBitmapPool.getInstance().put(options.inBitmap);
130c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin                options.inBitmap = null;
131c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin            }
132f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
133f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1341b2cfc142830875991dd190f163ad14af66d9e11Owen Lin        if (bitmap == null) {
1351b2cfc142830875991dd190f163ad14af66d9e11Owen Lin            Log.w(TAG, "fail in decoding region");
136cb4fb7c19f20405fb5e08513e6297dffce824118Chih-Chung Chang        }
137c2c0b01fbbf243eee1a11c33ba6dd1ce918a9ce5Owen Lin        return bitmap;
138f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
139f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
14030aa92093126f00b46543ae3b5d1035a3270f240Owen Lin    private Bitmap getTileWithoutReusingBitmap(
1416dc3866ac10cc6bf38e73f098fe1988d1a6eecd4Your Name            int level, int x, int y, int tileSize) {
14230aa92093126f00b46543ae3b5d1035a3270f240Owen Lin        int t = tileSize << level;
1436dc3866ac10cc6bf38e73f098fe1988d1a6eecd4Your Name        Rect wantRegion = new Rect(x, y, x + t, y + t);
14430aa92093126f00b46543ae3b5d1035a3270f240Owen Lin
14530aa92093126f00b46543ae3b5d1035a3270f240Owen Lin        BitmapRegionDecoder regionDecoder;
14630aa92093126f00b46543ae3b5d1035a3270f240Owen Lin        Rect overlapRegion;
14730aa92093126f00b46543ae3b5d1035a3270f240Owen Lin
14830aa92093126f00b46543ae3b5d1035a3270f240Owen Lin        synchronized (this) {
14930aa92093126f00b46543ae3b5d1035a3270f240Owen Lin            regionDecoder = mRegionDecoder;
15030aa92093126f00b46543ae3b5d1035a3270f240Owen Lin            if (regionDecoder == null) return null;
15130aa92093126f00b46543ae3b5d1035a3270f240Owen Lin            overlapRegion = new Rect(0, 0, mImageWidth, mImageHeight);
15230aa92093126f00b46543ae3b5d1035a3270f240Owen Lin            Utils.assertTrue(overlapRegion.intersect(wantRegion));
15330aa92093126f00b46543ae3b5d1035a3270f240Owen Lin        }
15430aa92093126f00b46543ae3b5d1035a3270f240Owen Lin
15530aa92093126f00b46543ae3b5d1035a3270f240Owen Lin        BitmapFactory.Options options = new BitmapFactory.Options();
15630aa92093126f00b46543ae3b5d1035a3270f240Owen Lin        options.inPreferredConfig = Config.ARGB_8888;
15730aa92093126f00b46543ae3b5d1035a3270f240Owen Lin        options.inPreferQualityOverSpeed = true;
15830aa92093126f00b46543ae3b5d1035a3270f240Owen Lin        options.inSampleSize =  (1 << level);
15930aa92093126f00b46543ae3b5d1035a3270f240Owen Lin        Bitmap bitmap = null;
16030aa92093126f00b46543ae3b5d1035a3270f240Owen Lin
16130aa92093126f00b46543ae3b5d1035a3270f240Owen Lin        // In CropImage, we may call the decodeRegion() concurrently.
16230aa92093126f00b46543ae3b5d1035a3270f240Owen Lin        synchronized (regionDecoder) {
16330aa92093126f00b46543ae3b5d1035a3270f240Owen Lin            bitmap = regionDecoder.decodeRegion(overlapRegion, options);
16430aa92093126f00b46543ae3b5d1035a3270f240Owen Lin        }
16530aa92093126f00b46543ae3b5d1035a3270f240Owen Lin
16630aa92093126f00b46543ae3b5d1035a3270f240Owen Lin        if (bitmap == null) {
16730aa92093126f00b46543ae3b5d1035a3270f240Owen Lin            Log.w(TAG, "fail in decoding region");
16830aa92093126f00b46543ae3b5d1035a3270f240Owen Lin        }
16930aa92093126f00b46543ae3b5d1035a3270f240Owen Lin
17030aa92093126f00b46543ae3b5d1035a3270f240Owen Lin        if (wantRegion.equals(overlapRegion)) return bitmap;
17130aa92093126f00b46543ae3b5d1035a3270f240Owen Lin
1726dc3866ac10cc6bf38e73f098fe1988d1a6eecd4Your Name        Bitmap result = Bitmap.createBitmap(tileSize, tileSize, Config.ARGB_8888);
17330aa92093126f00b46543ae3b5d1035a3270f240Owen Lin        Canvas canvas = new Canvas(result);
17430aa92093126f00b46543ae3b5d1035a3270f240Owen Lin        canvas.drawBitmap(bitmap,
17530aa92093126f00b46543ae3b5d1035a3270f240Owen Lin                (overlapRegion.left - wantRegion.left) >> level,
17630aa92093126f00b46543ae3b5d1035a3270f240Owen Lin                (overlapRegion.top - wantRegion.top) >> level, null);
17730aa92093126f00b46543ae3b5d1035a3270f240Owen Lin        return result;
17830aa92093126f00b46543ae3b5d1035a3270f240Owen Lin    }
17930aa92093126f00b46543ae3b5d1035a3270f240Owen Lin
18030aa92093126f00b46543ae3b5d1035a3270f240Owen Lin
181f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
18215b351a22d02e89d882fc9fe32b3f4c512080e0aChih-Chung Chang    public ScreenNail getScreenNail() {
18315b351a22d02e89d882fc9fe32b3f4c512080e0aChih-Chung Chang        return mScreenNail;
184f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
185f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
186f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
187f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public int getImageHeight() {
188f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return mImageHeight;
189f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
190f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
191f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
192f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public int getImageWidth() {
193f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return mImageWidth;
194f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
195f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
196f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
197f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public int getLevelCount() {
198f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return mLevelCount;
199f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
200f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin}
201