TileSurfaceImage类
protected void assembleTiles(RenderContext rc) {
// TODO
// The need to create Tiles with a defined image source couples the need to determine a tile's visibility with
// he need to know its image source. Decoupling the two would mean we only need to know the image source when
// the texture is actually requested Could the tile-based operations done here be implicit on level/row/column,
// or use transient pooled tile objects not tied to an image source?
if (this.topLevelTiles.isEmpty()) {
this.createTopLevelTiles();
}
for (int idx = 0, len = this.topLevelTiles.size(); idx < len; idx++) {
this.addTileOrDescendants(rc, (ImageTile) this.topLevelTiles.get(idx));
}
}
protected void addTileOrDescendants(RenderContext rc, ImageTile tile) {
if (!tile.intersectsSector(this.levelSet.sector) || !tile.intersectsFrustum(rc, rc.frustum)) {
return; // ignore the tile and its descendants if it's not needed or not visible
}
if (tile.level.isLastLevel() || !tile.mustSubdivide(rc, this.detailControl)) {
this.addTile(rc, tile);
return; // use the tile if it does not need to be subdivided
}
ImageTile currentAncestorTile = this.ancestorTile;
Texture currentAncestorTexture = this.ancestorTexture;
ImageSource tileImageSource = tile.getImageSource();
if (tileImageSource != null) { // tile has an image source; its level is not empty
Texture tileTexture = rc.getTexture(tileImageSource);
if (tileTexture != null) { // tile has a texture; use it as a fallback tile for descendants
this.ancestorTile = tile;
this.ancestorTexture = tileTexture;
}
}
for (Tile child : tile.subdivideToCache(this.tileFactory, this.tileCache, 4)) { // each tile has a cached size of 1
this.addTileOrDescendants(rc, (ImageTile) child); // recursively process the tile's children
}
this.ancestorTile = currentAncestorTile; // restore the last fallback tile, even if it was null
this.ancestorTexture = currentAncestorTexture;
}
Tile类
public Tile[] subdivideToCache(TileFactory tileFactory, LruMemoryCache<String, Tile[]> cache, int cacheSize) {
if (tileFactory == null) {
throw new IllegalArgumentException(
Logger.logMessage(Logger.ERROR, "Tile", "subdivideToCache", "missingTileFactory"));
}
if (cache == null) {
throw new IllegalArgumentException(
Logger.logMessage(Logger.ERROR, "Tile", "subdivideToCache", "missingCache"));
}
Tile[] children = cache.get(this.tileKey);
if (children == null) {
children = this.subdivide(tileFactory);
if (children != null) {
cache.put(this.tileKey, children, cacheSize);
}
}
return children;
}
public Tile[] subdivide(TileFactory tileFactory) {
if (tileFactory == null) {
throw new IllegalArgumentException(
Logger.logMessage(Logger.ERROR, "Tile", "subdivide", "missingTileFactory"));
}
Level childLevel = this.level.nextLevel();
if (childLevel == null) {
return null;
}
Tile[] children = new Tile[4];
double latMin = this.sector.minLatitude();
double lonMin = this.sector.minLongitude();
double latMid = this.sector.centroidLatitude();
double lonMid = this.sector.centroidLongitude();
double childDelta = this.level.tileDelta * 0.5;
int childRow = 2 * this.row;
int childCol = 2 * this.column;
Sector childSector = new Sector(latMin, lonMin, childDelta, childDelta);
children[0] = tileFactory.createTile(childSector, childLevel, childRow, childCol); // Southwest
childRow = 2 * this.row;
childCol = 2 * this.column + 1;
childSector = new Sector(latMin, lonMid, childDelta, childDelta);
children[1] = tileFactory.createTile(childSector, childLevel, childRow, childCol); // Southeast
childRow = 2 * this.row + 1;
childCol = 2 * this.column;
childSector = new Sector(latMid, lonMin, childDelta, childDelta);
children[2] = tileFactory.createTile(childSector, childLevel, childRow, childCol); // Northwest
childRow = 2 * this.row + 1;
childCol = 2 * this.column + 1;
childSector = new Sector(latMid, lonMid, childDelta, childDelta);
children[3] = tileFactory.createTile(childSector, childLevel, childRow, childCol); // Northeast
return children;
}
这里想说明问题是:
if (tile.level.isLastLevel() || !tile.mustSubdivide(rc, this.detailControl)) {
this.addTile(rc, tile);
return; // use the tile if it does not need to be subdivided
}
就是对当前的现实状态进行判断,以决定是否直接加载当前切片还是需要进一步切分,如果需要继续划分,就不加载,继续向下切分子切片,并递归调用addTileOrDescendants,继续判断切片是否还需切分,如果不需要继续切分,就addTile
protected void addTile(RenderContext rc, ImageTile tile) {
ImageSource imageSource = tile.getImageSource();
if (imageSource == null) {
return; // no image source indicates an empty level or an image missing from the tiled data store
}
Texture texture = rc.getTexture(imageSource); // try to get the texture from the cache
if (texture == null) {
texture = rc.retrieveTexture(imageSource, this.imageOptions); // puts retrieved textures in the cache
}
if (texture != null) { // use the tile's own texture
Pool<DrawableSurfaceTexture> pool = rc.getDrawablePool(DrawableSurfaceTexture.class);
Drawable drawable = DrawableSurfaceTexture.obtain(pool).set(this.activeProgram, tile.sector, texture, texture.getTexCoordTransform());
rc.offerSurfaceDrawable(drawable, 0 /*z-order*/);
} else if (this.ancestorTile != null) { // use the ancestor tile's texture, transformed to fill the tile sector
this.ancestorTexCoordMatrix.set(this.ancestorTexture.getTexCoordTransform());
this.ancestorTexCoordMatrix.multiplyByTileTransform(tile.sector, this.ancestorTile.sector);
Pool<DrawableSurfaceTexture> pool = rc.getDrawablePool(DrawableSurfaceTexture.class);
Drawable drawable = DrawableSurfaceTexture.obtain(pool).set(this.activeProgram, tile.sector, this.ancestorTexture, this.ancestorTexCoordMatrix);
rc.offerSurfaceDrawable(drawable, 0 /*z-order*/);
}
}
此时,就需要发送请求来获取对应的切片了!
RenderresourceCachelei
public Texture retrieveTexture(ImageSource imageSource, ImageOptions options) {
if (imageSource == null) {
return null; // a null image source corresponds to a null texture
}
// Bitmap image sources are already in memory, so a texture may be created and put into the cache immediately.
if (imageSource.isBitmap()) {
Texture texture = this.createTexture(imageSource, options, imageSource.asBitmap());
this.put(imageSource, texture, texture.getByteCount());
return texture;
}
// All other image sources must be retrieved from disk or network and must be retrieved on a separate thread.
// This includes bitmap factory image sources, since we cannot make any guarantees about what a bitmap factory
// implementation may do. First look for the image in the image retrieval cache, removing it and creating a
// corresponding texture if found.
Bitmap bitmap = this.imageRetrieverCache.remove(imageSource);
if (bitmap != null) {
Texture texture = this.createTexture(imageSource, options, bitmap);
this.put(imageSource, texture, texture.getByteCount());
return texture;
}
// The image must be retrieved on a separate thread. Request the image source and return null to indicate that
// the texture is not in memory. The image is added to the image retrieval cache upon successful retrieval. It's
// then expected that a subsequent render frame will result in another call to retrieveTexture, in which case
// the image will be found in the image retrieval cache.
if (imageSource.isUrl()) {
this.urlImageRetriever.retrieve(imageSource, options, this);
} else {
this.imageRetriever.retrieve(imageSource, options, this);
}
return null;
}
Retriever类:
public void retrieve(K key, O options, Callback<K, O, V> callback) {
if (key == null) {
throw new IllegalArgumentException(
Logger.logMessage(Logger.ERROR, "Retriever", "retrieve", "missingKey"));
}
if (callback == null) {
throw new IllegalArgumentException(
Logger.logMessage(Logger.ERROR, "Retriever", "retrieve", "missingCallback"));
}
AsyncTask<K, O, V> task = this.obtainAsyncTask(key, options, callback);
if (task == null) { // too many async tasks running, or a task for 'key' is already running
callback.retrievalRejected(this, key);
return;
}
try {
WorldWind.taskService().execute(task);
} catch (RejectedExecutionException ignored) { // singleton task service is full
this.recycleAsyncTask(task);
callback.retrievalRejected(this, key);
}
}