1e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka/* 2e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Copyright (C) 2013 The Android Open Source Project 3e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * 4e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Licensed under the Apache License, Version 2.0 (the "License"); 5e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * you may not use this file except in compliance with the License. 6e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * You may obtain a copy of the License at 7e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * 8e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * http://www.apache.org/licenses/LICENSE-2.0 9e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * 10e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Unless required by applicable law or agreed to in writing, software 11e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * distributed under the License is distributed on an "AS IS" BASIS, 12e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * See the License for the specific language governing permissions and 14e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * limitations under the License. 15e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka */ 16e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 17e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkapackage com.android.photos.views; 18e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 19e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.content.Context; 20e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Bitmap; 21e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Rect; 22e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.RectF; 23e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.support.v4.util.LongSparseArray; 24e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.util.DisplayMetrics; 25e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.util.Log; 26e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.util.Pools.Pool; 27e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.util.Pools.SynchronizedPool; 28e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.view.View; 29e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.view.WindowManager; 30e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 31e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport com.android.gallery3d.common.Utils; 32e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport com.android.gallery3d.glrenderer.BasicTexture; 33e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport com.android.gallery3d.glrenderer.GLCanvas; 34e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport com.android.gallery3d.glrenderer.UploadedTexture; 35e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 36e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka/** 37e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Handles laying out, decoding, and drawing of tiles in GL 38e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka */ 39e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkapublic class TiledImageRenderer { 40e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public static final int SIZE_UNKNOWN = -1; 41e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 42e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static final String TAG = "TiledImageRenderer"; 43e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static final int UPLOAD_LIMIT = 1; 44e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 45e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka /* 46e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * This is the tile state in the CPU side. 47e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Life of a Tile: 48e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * ACTIVATED (initial state) 49e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * --> IN_QUEUE - by queueForDecode() 50e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * --> RECYCLED - by recycleTile() 51e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * IN_QUEUE --> DECODING - by decodeTile() 52e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * --> RECYCLED - by recycleTile) 53e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * DECODING --> RECYCLING - by recycleTile() 54e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * --> DECODED - by decodeTile() 55e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * --> DECODE_FAIL - by decodeTile() 56e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * RECYCLING --> RECYCLED - by decodeTile() 57e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * DECODED --> ACTIVATED - (after the decoded bitmap is uploaded) 58e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * DECODED --> RECYCLED - by recycleTile() 59e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * DECODE_FAIL -> RECYCLED - by recycleTile() 60e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * RECYCLED --> ACTIVATED - by obtainTile() 61e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka */ 62e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static final int STATE_ACTIVATED = 0x01; 63e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static final int STATE_IN_QUEUE = 0x02; 64e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static final int STATE_DECODING = 0x04; 65e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static final int STATE_DECODED = 0x08; 66e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static final int STATE_DECODE_FAIL = 0x10; 67e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static final int STATE_RECYCLING = 0x20; 68e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static final int STATE_RECYCLED = 0x40; 69e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 70e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static Pool<Bitmap> sTilePool = new SynchronizedPool<Bitmap>(64); 71e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 72e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // TILE_SIZE must be 2^N 73e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private int mTileSize; 74e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 75e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private TileSource mModel; 76e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private BasicTexture mPreview; 77e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected int mLevelCount; // cache the value of mScaledBitmaps.length 78e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 79e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // The mLevel variable indicates which level of bitmap we should use. 80e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Level 0 means the original full-sized bitmap, and a larger value means 81e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // a smaller scaled bitmap (The width and height of each scaled bitmap is 82e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // half size of the previous one). If the value is in [0, mLevelCount), we 83e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // use the bitmap in mScaledBitmaps[mLevel] for display, otherwise the value 84e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // is mLevelCount 85e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private int mLevel = 0; 86e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 87e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private int mOffsetX; 88e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private int mOffsetY; 89e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 90e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private int mUploadQuota; 91e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private boolean mRenderComplete; 92e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 93e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private final RectF mSourceRect = new RectF(); 94e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private final RectF mTargetRect = new RectF(); 95e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 96e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private final LongSparseArray<Tile> mActiveTiles = new LongSparseArray<Tile>(); 97e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 98e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // The following three queue are guarded by mQueueLock 99e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private final Object mQueueLock = new Object(); 100e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private final TileQueue mRecycledQueue = new TileQueue(); 101e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private final TileQueue mUploadQueue = new TileQueue(); 102e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private final TileQueue mDecodeQueue = new TileQueue(); 103e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 104e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // The width and height of the full-sized bitmap 105e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected int mImageWidth = SIZE_UNKNOWN; 106e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected int mImageHeight = SIZE_UNKNOWN; 107e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 108e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected int mCenterX; 109e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected int mCenterY; 110e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected float mScale; 111e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected int mRotation; 112e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 113e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private boolean mLayoutTiles; 114e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 115e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Temp variables to avoid memory allocation 116e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private final Rect mTileRange = new Rect(); 117e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private final Rect mActiveRange[] = {new Rect(), new Rect()}; 118e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 119e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private TileDecoder mTileDecoder; 120e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private boolean mBackgroundTileUploaded; 121e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 122e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private int mViewWidth, mViewHeight; 123e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private View mParent; 124e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 125e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka /** 126e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Interface for providing tiles to a {@link TiledImageRenderer} 127e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka */ 128e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public static interface TileSource { 129e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 130e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka /** 131e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * If the source does not care about the tile size, it should use 132e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * {@link TiledImageRenderer#suggestedTileSize(Context)} 133e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka */ 134e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public int getTileSize(); 135e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public int getImageWidth(); 136e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public int getImageHeight(); 137e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public int getRotation(); 138e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 139e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka /** 140e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Return a Preview image if available. This will be used as the base layer 141e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * if higher res tiles are not yet available 142e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka */ 143e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public BasicTexture getPreview(); 144e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 145e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka /** 146e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * The tile returned by this method can be specified this way: Assuming 147e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * the image size is (width, height), first take the intersection of (0, 148e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * 0) - (width, height) and (x, y) - (x + tileSize, y + tileSize). If 149e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * in extending the region, we found some part of the region is outside 150e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * the image, those pixels are filled with black. 151e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * 152e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * If level > 0, it does the same operation on a down-scaled version of 153e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * the original image (down-scaled by a factor of 2^level), but (x, y) 154e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * still refers to the coordinate on the original image. 155e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * 156e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * The method would be called by the decoder thread. 157e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka */ 158e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public Bitmap getTile(int level, int x, int y, Bitmap reuse); 159e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 160e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 161e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public static int suggestedTileSize(Context context) { 162e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return isHighResolution(context) ? 512 : 256; 163e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 164e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 165e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static boolean isHighResolution(Context context) { 166e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka DisplayMetrics metrics = new DisplayMetrics(); 167e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka WindowManager wm = (WindowManager) 168e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka context.getSystemService(Context.WINDOW_SERVICE); 169e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka wm.getDefaultDisplay().getMetrics(metrics); 170e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return metrics.heightPixels > 2048 || metrics.widthPixels > 2048; 171e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 172e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 173e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public TiledImageRenderer(View parent) { 174e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mParent = parent; 175e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTileDecoder = new TileDecoder(); 176e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTileDecoder.start(); 177e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 178e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 179e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public int getViewWidth() { 180e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return mViewWidth; 181e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 182e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 183e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public int getViewHeight() { 184e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return mViewHeight; 185e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 186e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 187e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private void invalidate() { 188e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mParent.postInvalidate(); 189e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 190e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 191e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void setModel(TileSource model, int rotation) { 192e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (mModel != model) { 193e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mModel = model; 194e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka notifyModelInvalidated(); 195e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 196e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (mRotation != rotation) { 197e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mRotation = rotation; 198e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mLayoutTiles = true; 199e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 200e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 201e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 202e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private void calculateLevelCount() { 203e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (mPreview != null) { 204e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mLevelCount = Math.max(0, Utils.ceilLog2( 205e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mImageWidth / (float) mPreview.getWidth())); 206e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else { 207e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int levels = 1; 208e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int maxDim = Math.max(mImageWidth, mImageHeight); 209e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int t = mTileSize; 210e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka while (t < maxDim) { 211e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka t <<= 1; 212e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka levels++; 213e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 214e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mLevelCount = levels; 215e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 216e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 217e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 218e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void notifyModelInvalidated() { 219e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka invalidateTiles(); 220e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (mModel == null) { 221e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mImageWidth = 0; 222e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mImageHeight = 0; 223e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mLevelCount = 0; 224e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mPreview = null; 225e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else { 226e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mImageWidth = mModel.getImageWidth(); 227e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mImageHeight = mModel.getImageHeight(); 228e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mPreview = mModel.getPreview(); 229e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTileSize = mModel.getTileSize(); 230e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka calculateLevelCount(); 231e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 232e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mLayoutTiles = true; 233e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 234e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 235e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void setViewSize(int width, int height) { 236e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mViewWidth = width; 237e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mViewHeight = height; 238e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 239e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 240e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void setPosition(int centerX, int centerY, float scale) { 241e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (mCenterX == centerX && mCenterY == centerY 242e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka && mScale == scale) { 243e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return; 244e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 245e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mCenterX = centerX; 246e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mCenterY = centerY; 247e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mScale = scale; 248e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mLayoutTiles = true; 249e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 250e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 251e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Prepare the tiles we want to use for display. 252e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // 253e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // 1. Decide the tile level we want to use for display. 254e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // 2. Decide the tile levels we want to keep as texture (in addition to 255e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // the one we use for display). 256e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // 3. Recycle unused tiles. 257e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // 4. Activate the tiles we want. 258e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private void layoutTiles() { 259e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (mViewWidth == 0 || mViewHeight == 0 || !mLayoutTiles) { 260e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return; 261e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 262e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mLayoutTiles = false; 263e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 264e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // The tile levels we want to keep as texture is in the range 265e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // [fromLevel, endLevel). 266e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int fromLevel; 267e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int endLevel; 268e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 269e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // We want to use a texture larger than or equal to the display size. 270e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mLevel = Utils.clamp(Utils.floorLog2(1f / mScale), 0, mLevelCount); 271e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 272e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // We want to keep one more tile level as texture in addition to what 273e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // we use for display. So it can be faster when the scale moves to the 274e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // next level. We choose the level closest to the current scale. 275e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (mLevel != mLevelCount) { 276e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Rect range = mTileRange; 277e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka getRange(range, mCenterX, mCenterY, mLevel, mScale, mRotation); 278e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mOffsetX = Math.round(mViewWidth / 2f + (range.left - mCenterX) * mScale); 279e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mOffsetY = Math.round(mViewHeight / 2f + (range.top - mCenterY) * mScale); 280e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka fromLevel = mScale * (1 << mLevel) > 0.75f ? mLevel - 1 : mLevel; 281e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else { 282e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Activate the tiles of the smallest two levels. 283e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka fromLevel = mLevel - 2; 284e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mOffsetX = Math.round(mViewWidth / 2f - mCenterX * mScale); 285e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mOffsetY = Math.round(mViewHeight / 2f - mCenterY * mScale); 286e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 287e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 288e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka fromLevel = Math.max(0, Math.min(fromLevel, mLevelCount - 2)); 289e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka endLevel = Math.min(fromLevel + 2, mLevelCount); 290e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 291e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Rect range[] = mActiveRange; 292e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka for (int i = fromLevel; i < endLevel; ++i) { 293e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka getRange(range[i - fromLevel], mCenterX, mCenterY, i, mRotation); 294e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 295e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 296e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // If rotation is transient, don't update the tile. 297e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (mRotation % 90 != 0) { 298e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return; 299e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 300e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 301e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka synchronized (mQueueLock) { 302e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mDecodeQueue.clean(); 303e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mUploadQueue.clean(); 304e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mBackgroundTileUploaded = false; 305e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 306e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Recycle unused tiles: if the level of the active tile is outside the 307e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // range [fromLevel, endLevel) or not in the visible range. 308e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int n = mActiveTiles.size(); 309e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka for (int i = 0; i < n; i++) { 310e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Tile tile = mActiveTiles.valueAt(i); 311e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int level = tile.mTileLevel; 312e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (level < fromLevel || level >= endLevel 313e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka || !range[level - fromLevel].contains(tile.mX, tile.mY)) { 314e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mActiveTiles.removeAt(i); 315e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka i--; 316e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka n--; 317e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka recycleTile(tile); 318e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 319e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 320e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 321e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 322e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka for (int i = fromLevel; i < endLevel; ++i) { 323e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int size = mTileSize << i; 324e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Rect r = range[i - fromLevel]; 325e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka for (int y = r.top, bottom = r.bottom; y < bottom; y += size) { 326e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka for (int x = r.left, right = r.right; x < right; x += size) { 327e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka activateTile(x, y, i); 328e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 329e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 330e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 331e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka invalidate(); 332e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 333e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 334e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private void invalidateTiles() { 335e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka synchronized (mQueueLock) { 336e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mDecodeQueue.clean(); 337e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mUploadQueue.clean(); 338e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 339e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // TODO(xx): disable decoder 340e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int n = mActiveTiles.size(); 341e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka for (int i = 0; i < n; i++) { 342e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Tile tile = mActiveTiles.valueAt(i); 343e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka recycleTile(tile); 344e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 345e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mActiveTiles.clear(); 346e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 347e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 348e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 349e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private void getRange(Rect out, int cX, int cY, int level, int rotation) { 350e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka getRange(out, cX, cY, level, 1f / (1 << (level + 1)), rotation); 351e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 352e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 353e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // If the bitmap is scaled by the given factor "scale", return the 354e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // rectangle containing visible range. The left-top coordinate returned is 355e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // aligned to the tile boundary. 356e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // 357e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // (cX, cY) is the point on the original bitmap which will be put in the 358e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // center of the ImageViewer. 359e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private void getRange(Rect out, 360e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int cX, int cY, int level, float scale, int rotation) { 361e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 362e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka double radians = Math.toRadians(-rotation); 363e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka double w = mViewWidth; 364e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka double h = mViewHeight; 365e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 366e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka double cos = Math.cos(radians); 367e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka double sin = Math.sin(radians); 368e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int width = (int) Math.ceil(Math.max( 369e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Math.abs(cos * w - sin * h), Math.abs(cos * w + sin * h))); 370e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int height = (int) Math.ceil(Math.max( 371e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Math.abs(sin * w + cos * h), Math.abs(sin * w - cos * h))); 372e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 373e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int left = (int) Math.floor(cX - width / (2f * scale)); 374e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int top = (int) Math.floor(cY - height / (2f * scale)); 375e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int right = (int) Math.ceil(left + width / scale); 376e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int bottom = (int) Math.ceil(top + height / scale); 377e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 378e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // align the rectangle to tile boundary 379e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int size = mTileSize << level; 380e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka left = Math.max(0, size * (left / size)); 381e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka top = Math.max(0, size * (top / size)); 382e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka right = Math.min(mImageWidth, right); 383e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka bottom = Math.min(mImageHeight, bottom); 384e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 385e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka out.set(left, top, right, bottom); 386e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 387e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 388e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void freeTextures() { 389e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mLayoutTiles = true; 390e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 391e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTileDecoder.finishAndWait(); 392e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka synchronized (mQueueLock) { 393e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mUploadQueue.clean(); 394e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mDecodeQueue.clean(); 395e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Tile tile = mRecycledQueue.pop(); 396e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka while (tile != null) { 397e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka tile.recycle(); 398e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka tile = mRecycledQueue.pop(); 399e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 400e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 401e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 402e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int n = mActiveTiles.size(); 403e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka for (int i = 0; i < n; i++) { 404e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Tile texture = mActiveTiles.valueAt(i); 405e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka texture.recycle(); 406e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 407e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mActiveTiles.clear(); 408e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTileRange.set(0, 0, 0, 0); 409e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 410e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka while (sTilePool.acquire() != null) {} 411e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 412e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 413e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public boolean draw(GLCanvas canvas) { 414e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka layoutTiles(); 415e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka uploadTiles(canvas); 416e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 417e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mUploadQuota = UPLOAD_LIMIT; 418e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mRenderComplete = true; 419e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 420e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int level = mLevel; 421e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int rotation = mRotation; 422e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int flags = 0; 423e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (rotation != 0) { 424e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka flags |= GLCanvas.SAVE_FLAG_MATRIX; 425e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 426e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 427e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (flags != 0) { 428e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka canvas.save(flags); 429e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (rotation != 0) { 430e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int centerX = mViewWidth / 2, centerY = mViewHeight / 2; 431e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka canvas.translate(centerX, centerY); 432e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka canvas.rotate(rotation, 0, 0, 1); 433e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka canvas.translate(-centerX, -centerY); 434e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 435e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 436e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka try { 437e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (level != mLevelCount) { 438e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int size = (mTileSize << level); 439e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float length = size * mScale; 440e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Rect r = mTileRange; 441e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 442e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka for (int ty = r.top, i = 0; ty < r.bottom; ty += size, i++) { 443e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float y = mOffsetY + i * length; 444e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka for (int tx = r.left, j = 0; tx < r.right; tx += size, j++) { 445e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float x = mOffsetX + j * length; 446e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka drawTile(canvas, tx, ty, level, x, y, length); 447e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 448e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 449e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else if (mPreview != null) { 450e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mPreview.draw(canvas, mOffsetX, mOffsetY, 451e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Math.round(mImageWidth * mScale), 452e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Math.round(mImageHeight * mScale)); 453e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 454e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } finally { 455e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (flags != 0) { 456e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka canvas.restore(); 457e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 458e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 459e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 460e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (mRenderComplete) { 461e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (!mBackgroundTileUploaded) { 462e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka uploadBackgroundTiles(canvas); 463e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 464e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else { 465e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka invalidate(); 466e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 467e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return mRenderComplete || mPreview != null; 468e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 469e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 470e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private void uploadBackgroundTiles(GLCanvas canvas) { 471e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mBackgroundTileUploaded = true; 472e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int n = mActiveTiles.size(); 473e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka for (int i = 0; i < n; i++) { 474e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Tile tile = mActiveTiles.valueAt(i); 475e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (!tile.isContentValid()) { 476e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka queueForDecode(tile); 477e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 478e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 479e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 480e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 481e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private void queueForDecode(Tile tile) { 482e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka synchronized (mQueueLock) { 483e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (tile.mTileState == STATE_ACTIVATED) { 484e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka tile.mTileState = STATE_IN_QUEUE; 485e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (mDecodeQueue.push(tile)) { 486e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mQueueLock.notifyAll(); 487e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 488e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 489e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 490e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 491e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 492e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private void decodeTile(Tile tile) { 493e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka synchronized (mQueueLock) { 494e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (tile.mTileState != STATE_IN_QUEUE) { 495e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return; 496e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 497e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka tile.mTileState = STATE_DECODING; 498e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 499e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka boolean decodeComplete = tile.decode(); 500e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka synchronized (mQueueLock) { 501e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (tile.mTileState == STATE_RECYCLING) { 502e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka tile.mTileState = STATE_RECYCLED; 503e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (tile.mDecodedTile != null) { 504e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka sTilePool.release(tile.mDecodedTile); 505e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka tile.mDecodedTile = null; 506e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 507e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mRecycledQueue.push(tile); 508e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return; 509e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 510e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka tile.mTileState = decodeComplete ? STATE_DECODED : STATE_DECODE_FAIL; 511e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (!decodeComplete) { 512e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return; 513e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 514e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mUploadQueue.push(tile); 515e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 516e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka invalidate(); 517e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 518e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 519e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private Tile obtainTile(int x, int y, int level) { 520e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka synchronized (mQueueLock) { 521e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Tile tile = mRecycledQueue.pop(); 522e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (tile != null) { 523e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka tile.mTileState = STATE_ACTIVATED; 524e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka tile.update(x, y, level); 525e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return tile; 526e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 527e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return new Tile(x, y, level); 528e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 529e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 530e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 531e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private void recycleTile(Tile tile) { 532e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka synchronized (mQueueLock) { 533e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (tile.mTileState == STATE_DECODING) { 534e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka tile.mTileState = STATE_RECYCLING; 535e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return; 536e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 537e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka tile.mTileState = STATE_RECYCLED; 538e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (tile.mDecodedTile != null) { 539e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka sTilePool.release(tile.mDecodedTile); 540e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka tile.mDecodedTile = null; 541e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 542e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mRecycledQueue.push(tile); 543e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 544e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 545e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 546e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private void activateTile(int x, int y, int level) { 547e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka long key = makeTileKey(x, y, level); 548e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Tile tile = mActiveTiles.get(key); 549e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (tile != null) { 550e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (tile.mTileState == STATE_IN_QUEUE) { 551e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka tile.mTileState = STATE_ACTIVATED; 552e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 553e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return; 554e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 555e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka tile = obtainTile(x, y, level); 556e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mActiveTiles.put(key, tile); 557e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 558e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 559e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private Tile getTile(int x, int y, int level) { 560e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return mActiveTiles.get(makeTileKey(x, y, level)); 561e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 562e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 563e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static long makeTileKey(int x, int y, int level) { 564e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka long result = x; 565e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka result = (result << 16) | y; 566e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka result = (result << 16) | level; 567e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return result; 568e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 569e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 570e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private void uploadTiles(GLCanvas canvas) { 571e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int quota = UPLOAD_LIMIT; 572e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Tile tile = null; 573e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka while (quota > 0) { 574e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka synchronized (mQueueLock) { 575e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka tile = mUploadQueue.pop(); 576e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 577e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (tile == null) { 578e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka break; 579e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 580e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (!tile.isContentValid()) { 581e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (tile.mTileState == STATE_DECODED) { 582e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka tile.updateContent(canvas); 583e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka --quota; 584e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else { 585e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Log.w(TAG, "Tile in upload queue has invalid state: " + tile.mTileState); 586e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 587e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 588e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 589e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (tile != null) { 590e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka invalidate(); 591e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 592e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 593e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 594e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Draw the tile to a square at canvas that locates at (x, y) and 595e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // has a side length of length. 596e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private void drawTile(GLCanvas canvas, 597e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int tx, int ty, int level, float x, float y, float length) { 598e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka RectF source = mSourceRect; 599e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka RectF target = mTargetRect; 600e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka target.set(x, y, x + length, y + length); 601e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka source.set(0, 0, mTileSize, mTileSize); 602e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 603e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Tile tile = getTile(tx, ty, level); 604e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (tile != null) { 605e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (!tile.isContentValid()) { 606e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (tile.mTileState == STATE_DECODED) { 607e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (mUploadQuota > 0) { 608e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka --mUploadQuota; 609e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka tile.updateContent(canvas); 610e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else { 611e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mRenderComplete = false; 612e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 613e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else if (tile.mTileState != STATE_DECODE_FAIL){ 614e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mRenderComplete = false; 615e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka queueForDecode(tile); 616e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 617e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 618e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (drawTile(tile, canvas, source, target)) { 619e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return; 620e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 621e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 622e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (mPreview != null) { 623e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int size = mTileSize << level; 624e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float scaleX = (float) mPreview.getWidth() / mImageWidth; 625e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float scaleY = (float) mPreview.getHeight() / mImageHeight; 626e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka source.set(tx * scaleX, ty * scaleY, (tx + size) * scaleX, 627e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka (ty + size) * scaleY); 628e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka canvas.drawTexture(mPreview, source, target); 629e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 630e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 631e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 632e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private boolean drawTile( 633e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Tile tile, GLCanvas canvas, RectF source, RectF target) { 634e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka while (true) { 635e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (tile.isContentValid()) { 636e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka canvas.drawTexture(tile, source, target); 637e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return true; 638e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 639e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 640e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Parent can be divided to four quads and tile is one of the four. 641e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Tile parent = tile.getParentTile(); 642e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (parent == null) { 643e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return false; 644e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 645e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (tile.mX == parent.mX) { 646e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka source.left /= 2f; 647e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka source.right /= 2f; 648e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else { 649e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka source.left = (mTileSize + source.left) / 2f; 650e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka source.right = (mTileSize + source.right) / 2f; 651e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 652e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (tile.mY == parent.mY) { 653e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka source.top /= 2f; 654e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka source.bottom /= 2f; 655e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else { 656e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka source.top = (mTileSize + source.top) / 2f; 657e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka source.bottom = (mTileSize + source.bottom) / 2f; 658e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 659e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka tile = parent; 660e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 661e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 662e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 663e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private class Tile extends UploadedTexture { 664e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public int mX; 665e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public int mY; 666e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public int mTileLevel; 667e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public Tile mNext; 668e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public Bitmap mDecodedTile; 669e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public volatile int mTileState = STATE_ACTIVATED; 670e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 671e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public Tile(int x, int y, int level) { 672e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mX = x; 673e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mY = y; 674e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTileLevel = level; 675e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 676e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 677e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 678e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected void onFreeBitmap(Bitmap bitmap) { 679e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka sTilePool.release(bitmap); 680e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 681e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 682e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka boolean decode() { 683e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Get a tile from the original image. The tile is down-scaled 684e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // by (1 << mTilelevel) from a region in the original image. 685e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka try { 686e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Bitmap reuse = sTilePool.acquire(); 687e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (reuse != null && reuse.getWidth() != mTileSize) { 688e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka reuse = null; 689e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 690e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mDecodedTile = mModel.getTile(mTileLevel, mX, mY, reuse); 691e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } catch (Throwable t) { 692e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Log.w(TAG, "fail to decode tile", t); 693e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 694e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return mDecodedTile != null; 695e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 696e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 697e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 698e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected Bitmap onGetBitmap() { 699e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Utils.assertTrue(mTileState == STATE_DECODED); 700e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 701e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // We need to override the width and height, so that we won't 702e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // draw beyond the boundaries. 703e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int rightEdge = ((mImageWidth - mX) >> mTileLevel); 704e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int bottomEdge = ((mImageHeight - mY) >> mTileLevel); 705e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka setSize(Math.min(mTileSize, rightEdge), Math.min(mTileSize, bottomEdge)); 706e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 707e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Bitmap bitmap = mDecodedTile; 708e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mDecodedTile = null; 709e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTileState = STATE_ACTIVATED; 710e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return bitmap; 711e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 712e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 713e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // We override getTextureWidth() and getTextureHeight() here, so the 714e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // texture can be re-used for different tiles regardless of the actual 715e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // size of the tile (which may be small because it is a tile at the 716e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // boundary). 717e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 718e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public int getTextureWidth() { 719e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return mTileSize; 720e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 721e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 722e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 723e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public int getTextureHeight() { 724e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return mTileSize; 725e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 726e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 727e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void update(int x, int y, int level) { 728e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mX = x; 729e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mY = y; 730e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mTileLevel = level; 731e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka invalidateContent(); 732e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 733e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 734e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public Tile getParentTile() { 735e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (mTileLevel + 1 == mLevelCount) { 736e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return null; 737e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 738e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int size = mTileSize << (mTileLevel + 1); 739e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int x = size * (mX / size); 740e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int y = size * (mY / size); 741e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return getTile(x, y, mTileLevel + 1); 742e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 743e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 744e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 745e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public String toString() { 746e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return String.format("tile(%s, %s, %s / %s)", 747e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mX / mTileSize, mY / mTileSize, mLevel, mLevelCount); 748e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 749e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 750e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 751e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static class TileQueue { 752e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private Tile mHead; 753e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 754e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public Tile pop() { 755e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Tile tile = mHead; 756e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (tile != null) { 757e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mHead = tile.mNext; 758e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 759e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return tile; 760e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 761e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 762e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public boolean push(Tile tile) { 763e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (contains(tile)) { 764e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Log.w(TAG, "Attempting to add a tile already in the queue!"); 765e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return false; 766e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 767e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka boolean wasEmpty = mHead == null; 768e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka tile.mNext = mHead; 769e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mHead = tile; 770e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return wasEmpty; 771e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 772e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 773e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private boolean contains(Tile tile) { 774e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Tile other = mHead; 775e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka while (other != null) { 776e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (other == tile) { 777e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return true; 778e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 779e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka other = other.mNext; 780e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 781e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return false; 782e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 783e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 784e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void clean() { 785e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mHead = null; 786e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 787e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 788e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 789e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private class TileDecoder extends Thread { 790e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 791e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void finishAndWait() { 792e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka interrupt(); 793e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka try { 794e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka join(); 795e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } catch (InterruptedException e) { 796e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Log.w(TAG, "Interrupted while waiting for TileDecoder thread to finish!"); 797e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 798e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 799e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 800e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private Tile waitForTile() throws InterruptedException { 801e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka synchronized (mQueueLock) { 802e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka while (true) { 803e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Tile tile = mDecodeQueue.pop(); 804e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (tile != null) { 805e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return tile; 806e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 807e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mQueueLock.wait(); 808e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 809e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 810e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 811e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 812e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 813e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void run() { 814e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka try { 815e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka while (!isInterrupted()) { 816e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Tile tile = waitForTile(); 817e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka decodeTile(tile); 818e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 819e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } catch (InterruptedException ex) { 820e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // We were finished 821e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 822e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 823e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 824e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 825e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka} 826