/*
* Copyright (C) 2016 Peng fei Pan <sky@xiaopan.me>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.xiaopan.sketch.feature.large;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.Rect;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import me.xiaopan.sketch.Configuration;
import me.xiaopan.sketch.SLogType;
import me.xiaopan.sketch.Sketch;
import me.xiaopan.sketch.SLog;
import me.xiaopan.sketch.SketchMonitor;
import me.xiaopan.sketch.cache.BitmapPool;
import me.xiaopan.sketch.feature.ImageSizeCalculator;
import me.xiaopan.sketch.util.ObjectPool;
import me.xiaopan.sketch.util.SketchUtils;
/**
* 碎片管理器
*/
// TODO: 2016/12/17 优化碎片计算规则,尽量保证每块碎片的尺寸都是一样的,这样就能充分利用inBitmap功能减少内存分配提高流畅度
class TileManager {
private static final String NAME = "TileManager";
int tiles = 3; // 碎片基数,例如碎片基数是3时,就将绘制区域分割成一个(3+1)x(3+1)=16个方块
Rect visibleRect = new Rect(); // 可见区域,当前用户真正能看见的区域
Rect drawRect = new Rect(); // 绘制区域,可见区域加大一圈就是绘制区域,为的是提前将四周加载出来,用户缓慢滑动时可直接看到
Rect decodeRect = new Rect(); // 解码区域,真正需要解码的区域,是以绘制区域为基础,滑动时哪边不够了就在扩展哪边,解码区域一定比绘制区域大
Rect drawSrcRect = new Rect();
Rect decodeSrcRect = new Rect();
List<Tile> tileList = new LinkedList<Tile>();
LargeImageViewer.OnTileChangedListener onTileChangedListener;
private Context context;
private BitmapPool bitmapPool;
private LargeImageViewer largeImageViewer;
private ObjectPool<Tile> tilePool = new ObjectPool<Tile>(new ObjectPool.ObjectFactory<Tile>() {
@Override
public Tile newObject() {
return new Tile();
}
}, 60);
private ObjectPool<Rect> rectPool = new ObjectPool<Rect>(new ObjectPool.ObjectFactory<Rect>() {
@Override
public Rect newObject() {
return new Rect();
}
}, 20);
TileManager(Context context, LargeImageViewer largeImageViewer) {
context = context.getApplicationContext();
this.context = context;
this.bitmapPool = Sketch.with(context).getConfiguration().getBitmapPool();
this.largeImageViewer = largeImageViewer;
}
void update(Rect newVisibleRect, Point previewDrawableSize, Point imageViewSize, Point imageSize, boolean zooming) {
if (zooming) {
SLog.w(SLogType.LARGE, NAME, "zooming. newVisibleRect=%s, tiles=%d",
newVisibleRect.toShortString(), tileList.size());
return;
}
// 过滤掉重复的刷新
if (visibleRect.equals(newVisibleRect)) {
SLog.w(SLogType.LARGE, NAME, "visible rect no changed. update. newVisibleRect=%s, oldVisibleRect=%s",
newVisibleRect.toShortString(), visibleRect.toShortString());
return;
}
visibleRect.set(newVisibleRect);
final int viewWidth = imageViewSize.x;
final int viewHeight = imageViewSize.y;
final int previewImageWidth = previewDrawableSize.x;
final int previewImageHeight = previewDrawableSize.y;
final int imageWidth = imageSize.x;
final int imageHeight = imageSize.y;
// 原始图和预览图对比的缩放比例
final float originWidthScale = (float) imageWidth / previewImageWidth;
final float originHeightScale = (float) imageHeight / previewImageHeight;
// 计算绘制区域时,每边应该增加的量
final int drawWidthAdd = (int) ((float) newVisibleRect.width() / tiles / 2);
final int drawHeightAdd = (int) ((float) newVisibleRect.height() / tiles / 2);
// 将显示区域加大一圈,计算出绘制区域,宽高各增加一个平均值
// 为的是提前将四周加载出来,用户缓慢滑动的时候可以提前看到四周的图像
Rect newDrawRect = rectPool.get();
newDrawRect.left = Math.max(0, newVisibleRect.left - drawWidthAdd);
newDrawRect.top = Math.max(0, newVisibleRect.top - drawHeightAdd);
newDrawRect.right = Math.min(previewImageWidth, newVisibleRect.right + drawWidthAdd);
newDrawRect.bottom = Math.min(previewImageHeight, newVisibleRect.bottom + drawHeightAdd);
if (newDrawRect.isEmpty()) {
SLog.e(SLogType.LARGE, NAME, "newDrawRect is empty. %s", newDrawRect.toShortString());
return;
}
// 计算碎片的尺寸
final int finalTiles = tiles + 1;
final int tileWidth = newDrawRect.width() / finalTiles;
final int tileHeight = newDrawRect.height() / finalTiles;
if (tileWidth <= 0 || tileHeight <= 0) {
SLog.e(SLogType.LARGE, NAME, "tileWidth or tileHeight exception. %dx%d", tileWidth, tileHeight);
return;
}
// 根据碎片尺寸修剪drawRect,使其正好能整除碎片
if (newDrawRect.right < previewImageWidth) {
newDrawRect.right = newDrawRect.left + (finalTiles * tileWidth);
} else if (newDrawRect.left > 0) {
newDrawRect.left = newDrawRect.right - (finalTiles * tileWidth);
}
if (newDrawRect.bottom < previewImageHeight) {
newDrawRect.bottom = newDrawRect.top + (finalTiles * tileHeight);
} else if (newDrawRect.top > 0) {
newDrawRect.top = newDrawRect.bottom - (finalTiles * tileHeight);
}
Rect newDrawSrcRect = rectPool.get();
calculateSrcRect(newDrawSrcRect, newDrawRect, imageWidth, imageHeight, originWidthScale, originHeightScale);
int inSampleSize = calculateInSampleSize(newDrawSrcRect.width(), newDrawSrcRect.height(), viewWidth, viewHeight);
SLog.i(SLogType.LARGE, NAME, "update start. newVisibleRect=%s, newDrawRect=%s, oldDecodeRect=%s, inSampleSize=%d, scale=%s, lastScale=%s, tiles=%d",
newVisibleRect.toShortString(), newDrawRect.toShortString(), decodeRect.toShortString(),
inSampleSize, largeImageViewer.getZoomScale(), largeImageViewer.getLastZoomScale(), tileList.size());
// 根据上一次绘制区域的和新绘制区域的差异计算出最终的绘制区域
Rect newDecodeRect = rectPool.get();
calculateTilesDecodeRect(newDecodeRect, newDrawRect, drawWidthAdd, drawHeightAdd,
tileWidth, tileHeight, previewImageWidth, previewImageHeight);
Rect newDecodeSrcRect = rectPool.get();
calculateSrcRect(newDecodeSrcRect, newDecodeRect, imageWidth, imageHeight,
originWidthScale, originHeightScale);
if (!newDecodeRect.isEmpty()) {
// 如果最终绘制区域跟上一次没有变化就不继续了
if (!newDecodeRect.equals(decodeRect)) {
// 回收那些已经超出绘制区域的碎片
recycleTiles(tileList, newDecodeRect);
// 找出所有的空白区域,然后一个一个加载
List<Rect> emptyRectList = findEmptyRect(newDecodeRect, tileList);
if (emptyRectList != null && emptyRectList.size() > 0) {
loadTiles(emptyRectList, tileWidth, tileHeight, imageWidth, imageHeight,
originWidthScale, originHeightScale, inSampleSize, newDecodeRect);
} else {
SLog.d(SLogType.LARGE, NAME, "not found empty rect");
}
if (onTileChangedListener != null) {
onTileChangedListener.onTileChanged(largeImageViewer);
}
SLog.e(SLogType.LARGE, NAME, "update finished, newDecodeRect=%s, tiles=%d",
newDecodeRect.toShortString(), tileList.size());
} else {
SLog.e(SLogType.LARGE, NAME, "update finished draw rect no change");
}
} else {
SLog.e(SLogType.LARGE, NAME, "update finished. final draw rect is empty. newDecodeRect=%s",
newDecodeRect.toShortString());
}
drawRect.set(newDrawRect);
drawSrcRect.set(newDrawSrcRect);
decodeRect.set(newDecodeRect);
decodeSrcRect.set(newDecodeSrcRect);
newDrawRect.setEmpty();
newDrawSrcRect.setEmpty();
newDecodeRect.setEmpty();
newDecodeSrcRect.setEmpty();
rectPool.put(newDrawRect);
rectPool.put(newDrawSrcRect);
rectPool.put(newDecodeRect);
rectPool.put(newDecodeSrcRect);
}
/**
* 计算绘制区域在完整图片中对应的区域,重点是各用各的缩放比例(这很重要),因为宽或高的比例可能不一样
*/
private void calculateSrcRect(Rect srcRect, Rect drawRect, int imageWidth, int imageHeight,
float originWidthScale, float originHeightScale) {
srcRect.left = Math.max(0, Math.round(drawRect.left * originWidthScale));
srcRect.top = Math.max(0, Math.round(drawRect.top * originHeightScale));
srcRect.right = Math.min(imageWidth, Math.round(drawRect.right * originWidthScale));
srcRect.bottom = Math.min(imageHeight, Math.round(drawRect.bottom * originHeightScale));
}
/**
* 计算解码时的缩放比例
*/
private int calculateInSampleSize(int srcWidth, int srcHeight, int viewWidth, int viewHeight) {
// 由于绘制区域比显示区域大了一圈,因此targetSize也得大一圈
float targetSizeScale = ((float) tiles / 10) + 1;
int targetWidth = Math.round(viewWidth * targetSizeScale);
int targetHeight = Math.round(viewHeight * targetSizeScale);
ImageSizeCalculator imageSizeCalculator = Sketch.with(context).getConfiguration().getImageSizeCalculator();
return imageSizeCalculator.calculateInSampleSize(srcWidth, srcHeight, targetWidth, targetHeight, false);
}
/**
* 在上一个绘制区域的基础上计算出根据新的绘制区域,计算出最终的绘制区域
*/
private void calculateTilesDecodeRect(Rect newDecodeRect, Rect newDrawRect,
int drawWidthAdd, int drawHeightAdd,
int drawTileWidth, int drawTileHeight,
int maxDrawWidth, int maxDrawHeight) {
// 缩放比例已改变或者这是第一次就直接用新的绘制区域
if (largeImageViewer.getZoomScale() != largeImageViewer.getLastZoomScale() || decodeRect.isEmpty()) {
newDecodeRect.set(newDrawRect);
return;
}
// 以上一次的绘制区域为基础
newDecodeRect.set(decodeRect);
int leftAndRightEdge = Math.round(drawWidthAdd * 0.8f);
int topAndBottomEdge = Math.round(drawHeightAdd * 0.8f);
int leftSpace = Math.abs(newDrawRect.left - newDecodeRect.left);
int topSpace = Math.abs(newDrawRect.top - newDecodeRect.top);
int rightSpace = Math.abs(newDrawRect.right - newDecodeRect.right);
int bottomSpace = Math.abs(newDrawRect.bottom - newDecodeRect.bottom);
// 左边需要加一列
if (newDrawRect.left < newDecodeRect.left) {
if (newDrawRect.left == 0) {
// 如果已经到边了,管它还差多少,直接顶到边
newDecodeRect.left = 0;
if (SLogType.LARGE.isEnabled()) {
SLog.d(SLogType.LARGE, NAME, "decode rect left to 0, newDecodeRect=%s", newDecodeRect.toShortString());
}
} else if (leftSpace > leftAndRightEdge || newDecodeRect.left - drawTileWidth <= 0) {
// 如果间距比较大或者再加一个碎片的宽度就到边了就扩展
// 由于间距可能会大于一个碎片的宽度,因此要循环不停的加
while (newDecodeRect.left > newDrawRect.left) {
newDecodeRect.left = Math.max(0, newDecodeRect.left - drawTileWidth);
if (SLogType.LARGE.isEnabled()) {
SLog.d(SLogType.LARGE, NAME, "decode rect left expand %d, newDecodeRect=%s",
drawTileWidth, newDecodeRect.toShortString());
}
}
}
}
// 顶部需要加一行
if (newDrawRect.top < newDecodeRect.top) {
if (newDrawRect.top == 0) {
// 如果已经到边了,管它还差多少,直接顶到边
newDecodeRect.top = 0;
if (SLogType.LARGE.isEnabled()) {
SLog.d(SLogType.LARGE, NAME, "decode rect top to 0, newDecodeRect=%s", newDecodeRect.toShortString());
}
} else if (topSpace > topAndBottomEdge || newDecodeRect.top - drawTileHeight <= 0) {
// 如果间距比较大或者再加一个碎片的高度就到边了就扩展
// 由于间距可能会大于一个碎片的高度,因此要循环不停的加
while (newDecodeRect.top > newDrawRect.top) {
newDecodeRect.top = Math.max(0, newDecodeRect.top - drawTileHeight);
if (SLogType.LARGE.isEnabled()) {
SLog.d(SLogType.LARGE, NAME, "decode rect top expand %d, newDecodeRect=%s",
drawTileHeight, newDecodeRect.toShortString());
}
}
}
}
// 右边需要加一列
if (newDrawRect.right > newDecodeRect.right) {
if (newDrawRect.right == maxDrawWidth) {
// 如果已经到边了,管它还差多少,直接顶到边
newDecodeRect.right = maxDrawWidth;
if (SLogType.LARGE.isEnabled()) {
SLog.d(SLogType.LARGE, NAME, "decode rect right to %d, newDecodeRect=%s",
maxDrawWidth, newDecodeRect.toShortString());
}
} else if (rightSpace > leftAndRightEdge || newDecodeRect.right + drawTileWidth >= maxDrawWidth) {
// 如果间距比较大或者再加一个碎片的宽度就到边了就扩展
// 由于间距可能会大于一个碎片的宽度,因此要循环不停的加
while (newDecodeRect.right < newDrawRect.right) {
newDecodeRect.right = Math.min(maxDrawWidth, newDecodeRect.right + drawTileWidth);
if (SLogType.LARGE.isEnabled()) {
SLog.d(SLogType.LARGE, NAME, "decode rect right expand %d, newDecodeRect=%s",
drawTileWidth, newDecodeRect.toShortString());
}
}
}
}
// 底部需要加一行
if (newDrawRect.bottom > newDecodeRect.bottom) {
if (newDrawRect.bottom > maxDrawHeight) {
// 如果已经到边了,管它还差多少,直接顶到边
newDecodeRect.bottom = maxDrawHeight;
if (SLogType.LARGE.isEnabled()) {
SLog.d(SLogType.LARGE, NAME, "decode rect bottom to %d, newDecodeRect=%s",
maxDrawHeight, newDecodeRect.toShortString());
}
} else if (bottomSpace > topAndBottomEdge || newDecodeRect.bottom + drawTileHeight >= maxDrawHeight) {
// 如果间距比较大或者再加一个碎片的高度就到边了就扩展
// 由于间距可能会大于一个碎片的高度,因此要循环不停的加
while (newDecodeRect.bottom < newDrawRect.bottom) {
newDecodeRect.bottom = Math.min(maxDrawHeight, newDecodeRect.bottom + drawTileHeight);
if (SLogType.LARGE.isEnabled()) {
SLog.d(SLogType.LARGE, NAME, "decode rect bottom expand %d, newDecodeRect=%s",
drawTileHeight, newDecodeRect.toShortString());
}
}
}
}
// 前面把四周给加大了一圈,这里要把多余的剪掉
while (newDecodeRect.left + drawTileWidth < newDrawRect.left ||
newDecodeRect.top + drawTileHeight < newDrawRect.top ||
newDecodeRect.right - drawTileWidth > newDrawRect.right ||
newDecodeRect.bottom - drawTileHeight > newDrawRect.bottom) {
if (newDecodeRect.left + drawTileWidth < newDrawRect.left) {
newDecodeRect.left += drawTileWidth;
if (SLogType.LARGE.isEnabled()) {
SLog.d(SLogType.LARGE, NAME, "decode rect left reduced %d, newDecodeRect=%s",
drawTileWidth, newDecodeRect.toShortString());
}
}
if (newDecodeRect.top + drawTileHeight < newDrawRect.top) {
newDecodeRect.top += drawTileHeight;
if (SLogType.LARGE.isEnabled()) {
SLog.d(SLogType.LARGE, NAME, "decode rect top reduced %d, newDecodeRect=%s",
drawTileHeight, newDecodeRect.toShortString());
}
}
if (newDecodeRect.right - drawTileWidth > newDrawRect.right) {
newDecodeRect.right -= drawTileWidth;
if (SLogType.LARGE.isEnabled()) {
SLog.d(SLogType.LARGE, NAME, "decode rect right reduced %d, newDecodeRect=%s",
drawTileWidth, newDecodeRect.toShortString());
}
}
if (newDecodeRect.bottom - drawTileHeight > newDrawRect.bottom) {
newDecodeRect.bottom -= drawTileHeight;
if (SLogType.LARGE.isEnabled()) {
SLog.d(SLogType.LARGE, NAME, "decode rect bottom reduced %d, newDecodeRect=%s",
drawTileHeight, newDecodeRect.toShortString());
}
}
}
}
/**
* 去重
*/
private boolean canLoad(int left, int top, int right, int bottom) {
for (Tile drawTile : tileList) {
if (drawTile.drawRect.left == left &&
drawTile.drawRect.top == top &&
drawTile.drawRect.right == right &&
drawTile.drawRect.bottom == bottom) {
return false;
}
}
return true;
}
/**
* 假如有一个矩形,并且已知这个矩形中的N个碎片,那么要找出所有的空白碎片(不可用的碎片会从已知列表中删除)
*
* @param rect 那个矩形
* @param tileList 已知碎片
* @return 所有空白的碎片
*/
private List<Rect> findEmptyRect(Rect rect, List<Tile> tileList) {
if (rect.isEmpty()) {
return null;
}
List<Rect> emptyRectList = null;
if (tileList == null || tileList.size() == 0) {
Rect fullRect = rectPool.get();
fullRect.set(rect);
emptyRectList = new LinkedList<Rect>();
emptyRectList.add(fullRect);
return emptyRectList;
}
// 按离左上角的距离排序
Comparator<Tile> tileComparator = new Comparator<Tile>() {
@Override
public int compare(Tile o1, Tile o2) {
// 同一行比较left,不同行比较top
if ((o1.drawRect.top <= o2.drawRect.top && o1.drawRect.bottom >= o2.drawRect.bottom)
|| (o1.drawRect.top >= o2.drawRect.top && o1.drawRect.bottom <= o2.drawRect.bottom)) {
return o1.drawRect.left - o2.drawRect.left;
} else {
return o1.drawRect.top - o2.drawRect.top;
}
}
};
try {
Collections.sort(tileList, tileComparator);
} catch (IllegalArgumentException e) {
e.printStackTrace();
/**
* Java7的排序算法在检测到A>B, B>C, 但是A<=C的时候就会抛出异常,这里的处理办法是暂时改用旧版的排序算法再次排序
*/
Configuration configuration = Sketch.with(context).getConfiguration();
SketchMonitor sketchMonitor = configuration.getMonitor();
sketchMonitor.onTileSortError(e, tileList, false);
System.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
try {
Collections.sort(tileList, tileComparator);
} catch (IllegalArgumentException e2) {
e2.printStackTrace();
sketchMonitor.onTileSortError(e, tileList, true);
}
System.setProperty("java.util.Arrays.useLegacyMergeSort", "false");
}
int left = rect.left, top = rect.top, right = 0, bottom = -1;
Tile lastRect = null;
Tile childRect;
Iterator<Tile> rectIterator = tileList.iterator();
while (rectIterator.hasNext()) {
childRect = rectIterator.next();
boolean newLine = lastRect == null || (childRect.drawRect.top >= bottom);
if (newLine) {
// 首先要处理上一行的最后一个
if (lastRect != null) {
if (lastRect.drawRect.right < rect.right) {
Rect rightEmptyRect = rectPool.get();
rightEmptyRect.set(lastRect.drawRect.right, top, rect.right, bottom);
if (emptyRectList == null) {
emptyRectList = new LinkedList<Rect>();
}
emptyRectList.add(rightEmptyRect);
}
}
// 然后要更新top和bottom
top = bottom != -1 ? bottom : top;
bottom = childRect.drawRect.bottom;
// 左边有空隙
if (childRect.drawRect.left > left) {
Rect leftEmptyRect = rectPool.get();
leftEmptyRect.set(left, childRect.drawRect.top, childRect.drawRect.left, childRect.drawRect.bottom);
if (emptyRectList == null) {
emptyRectList = new LinkedList<Rect>();
}
emptyRectList.add(leftEmptyRect);
}
// 顶部有空隙
if (childRect.drawRect.top > top) {
Rect topEmptyRect = rectPool.get();
topEmptyRect.set(left, top, childRect.drawRect.right, childRect.drawRect.top);
if (emptyRectList == null) {
emptyRectList = new LinkedList<Rect>();
}
emptyRectList.add(topEmptyRect);
}
right = childRect.drawRect.right;
lastRect = childRect;
} else {
boolean available = childRect.drawRect.bottom == lastRect.drawRect.bottom;
if (available) {
// 左边有空隙
if (childRect.drawRect.left > right) {
Rect leftEmptyRect = rectPool.get();
leftEmptyRect.set(right, top, childRect.drawRect.left, bottom);
if (emptyRectList == null) {
emptyRectList = new LinkedList<Rect>();
}
emptyRectList.add(leftEmptyRect);
}
// 顶部有空隙
if (childRect.drawRect.top > top) {
Rect topEmptyRect = rectPool.get();
topEmptyRect.set(childRect.drawRect.left, top, childRect.drawRect.right, childRect.drawRect.top);
if (emptyRectList == null) {
emptyRectList = new LinkedList<Rect>();
}
emptyRectList.add(topEmptyRect);
}
right = childRect.drawRect.right;
lastRect = childRect;
} else {
rectIterator.remove();
}
}
}
// 最后的结尾处理
if (right < rect.right) {
Rect rightEmptyRect = rectPool.get();
rightEmptyRect.set(right, top, rect.right, bottom);
if (emptyRectList == null) {
emptyRectList = new LinkedList<Rect>();
}
emptyRectList.add(rightEmptyRect);
}
if (bottom < rect.bottom) {
Rect bottomEmptyRect = rectPool.get();
bottomEmptyRect.set(rect.left, bottom, rect.right, rect.bottom);
if (emptyRectList == null) {
emptyRectList = new LinkedList<Rect>();
}
emptyRectList.add(bottomEmptyRect);
}
return emptyRectList;
}
/**
* 回收哪些已经超出绘制区域的碎片
*/
private void recycleTiles(List<Tile> tileList, Rect drawRect) {
Tile tile;
Iterator<Tile> tileIterator = tileList.iterator();
while (tileIterator.hasNext()) {
tile = tileIterator.next();
// 缩放比例已经变了或者这个碎片已经跟当前显示区域毫无交集,那么就可以回收这个碎片了
if (largeImageViewer.getZoomScale() != tile.scale || !SketchUtils.isCross(tile.drawRect, drawRect)) {
if (!tile.isEmpty()) {
if (SLogType.LARGE.isEnabled()) {
SLog.d(SLogType.LARGE, NAME, "recycle tile. tile=%s", tile.getInfo());
}
tileIterator.remove();
tile.clean(bitmapPool);
tilePool.put(tile);
} else {
if (SLogType.LARGE.isEnabled()) {
SLog.w(SLogType.LARGE, NAME, "recycle loading tile and refresh key. tile=%s", tile.getInfo());
}
tile.refreshKey();
tileIterator.remove();
}
}
}
}
private void loadTiles(List<Rect> emptyRectList, int tileWidth, int tileHeight,
int imageWidth, int imageHeight, float originWidthScale, float originHeightScale,
int inSampleSize, Rect newDecodeRect) {
for (Rect emptyRect : emptyRectList) {
if (SLogType.LARGE.isEnabled()) {
SLog.d(SLogType.LARGE, NAME, "load emptyRect=%s", emptyRect.toShortString());
}
int tileLeft = emptyRect.left, tileTop = emptyRect.top, tileRight = 0, tileBottom = 0;
while (Math.round(tileRight) < emptyRect.right || Math.round(tileBottom) < emptyRect.bottom) {
tileRight = Math.min(tileLeft + tileWidth, emptyRect.right);
tileBottom = Math.min(tileTop + tileHeight, emptyRect.bottom);
if (canLoad(tileLeft, tileTop, tileRight, tileBottom)) {
Tile loadTile = tilePool.get();
loadTile.drawRect.set(tileLeft, tileTop, tileRight, tileBottom);
loadTile.inSampleSize = inSampleSize;
loadTile.scale = largeImageViewer.getZoomScale();
calculateSrcRect(loadTile.srcRect, loadTile.drawRect, imageWidth, imageHeight, originWidthScale, originHeightScale);
tileList.add(loadTile);
if (SLogType.LARGE.isEnabled()) {
SLog.d(SLogType.LARGE, NAME, "submit and refresh key. newDecodeRect=%s, tile=%s",
newDecodeRect.toShortString(), loadTile.getInfo());
}
loadTile.refreshKey();
largeImageViewer.getTileDecoder().decodeTile(loadTile);
} else {
if (SLogType.LARGE.isEnabled()) {
SLog.w(SLogType.LARGE, NAME, "repeated tile. tileDrawRect=%d, %d, %d, %d",
Math.round(tileLeft), Math.round(tileTop), Math.round(tileRight), Math.round(tileBottom));
}
}
if (Math.round(tileRight) >= emptyRect.right) {
tileLeft = emptyRect.left;
tileTop = tileBottom;
} else {
tileLeft = tileRight;
}
}
emptyRect.setEmpty();
rectPool.put(emptyRect);
}
}
void decodeCompleted(Tile tile, Bitmap bitmap, int useTime) {
if (SLogType.LARGE.isEnabled()) {
String bitmapConfig = bitmap.getConfig() != null ? bitmap.getConfig().name() : null;
SLog.i(SLogType.LARGE, NAME, "decode completed. useTime=%dms, tile=%s, bitmap=%dx%d(%s), tiles=%d",
useTime, tile.getInfo(), bitmap.getWidth(), bitmap.getHeight(), bitmapConfig, tileList.size());
}
tile.bitmap = bitmap;
tile.bitmapDrawSrcRect.set(0, 0, bitmap.getWidth(), bitmap.getHeight());
tile.decoder = null;
largeImageViewer.invalidateView();
if (onTileChangedListener != null) {
onTileChangedListener.onTileChanged(largeImageViewer);
}
}
void decodeError(Tile tile, DecodeHandler.DecodeErrorException exception) {
if (SLogType.LARGE.isEnabled()) {
SLog.w(SLogType.LARGE, NAME, "decode failed. %s. tile=%s, tiles=%d",
exception.getCauseMessage(), tile.getInfo(), tileList.size());
}
tileList.remove(tile);
tile.clean(bitmapPool);
tilePool.put(tile);
}
void clean(String why) {
for (Tile tile : tileList) {
tile.refreshKey();
tile.clean(bitmapPool);
tilePool.put(tile);
if (SLogType.LARGE.isEnabled()) {
SLog.w(SLogType.LARGE, NAME, "clean tile and refresh key. %s. tile=%s", why, tile.getInfo());
}
}
tileList.clear();
visibleRect.setEmpty();
drawRect.setEmpty();
drawSrcRect.setEmpty();
decodeRect.setEmpty();
decodeSrcRect.setEmpty();
}
void recycle(@SuppressWarnings("UnusedParameters") String why) {
clean(why);
tilePool.clear();
rectPool.clear();
}
}