Texture atlas: use bitmap packer based on libGdx PixmapPacker (#304)
This commit is contained in:
parent
29322acf87
commit
23d34fa00a
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2013 Hannes Janetzek
|
||||
* Copyright 2017 Longri
|
||||
*
|
||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
||||
*
|
||||
@ -68,6 +69,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class TextureAtlas extends Inlist<TextureAtlas> {
|
||||
static final Logger log = LoggerFactory.getLogger(TextureAtlas.class);
|
||||
@ -239,6 +241,10 @@ public class TextureAtlas extends Inlist<TextureAtlas> {
|
||||
return r;
|
||||
}
|
||||
|
||||
public Map<Object, TextureRegion> getRegions() {
|
||||
return mRegions;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
mRects = null;
|
||||
mSlots = new Slot(1, 1, mWidth - 2);
|
||||
|
332
vtm/src/org/oscim/utils/BitmapPacker.java
Executable file
332
vtm/src/org/oscim/utils/BitmapPacker.java
Executable file
@ -0,0 +1,332 @@
|
||||
/*
|
||||
* Copyright 2017 Longri
|
||||
*
|
||||
* Based on PixmapPacker from LibGdx converted to use VTM Bitmaps without any LibGdx dependencies:
|
||||
* https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/g2d/PixmapPacker.java
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.utils;
|
||||
|
||||
import org.oscim.backend.CanvasAdapter;
|
||||
import org.oscim.backend.canvas.Bitmap;
|
||||
import org.oscim.backend.canvas.Canvas;
|
||||
import org.oscim.backend.canvas.Color;
|
||||
import org.oscim.renderer.atlas.TextureAtlas;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class BitmapPacker {
|
||||
|
||||
private final int atlasWidth, atlasHeight;
|
||||
private final int padding;
|
||||
private final PackStrategy packStrategy;
|
||||
private final boolean flipY;
|
||||
|
||||
private final List<PackerAtlasItem> packerAtlasItems = new ArrayList<>();
|
||||
|
||||
public BitmapPacker(int atlasWidth, int atlasHeight, int padding, boolean flipY) {
|
||||
this(atlasWidth, atlasHeight, padding, new GuillotineStrategy(), flipY);
|
||||
}
|
||||
|
||||
public BitmapPacker(int atlasWidth, int atlasHeight, int padding, PackStrategy packStrategy,
|
||||
boolean flipY) {
|
||||
this.atlasWidth = atlasWidth;
|
||||
this.atlasHeight = atlasHeight;
|
||||
this.padding = padding;
|
||||
this.packStrategy = packStrategy;
|
||||
this.flipY = flipY;
|
||||
}
|
||||
|
||||
public synchronized Rect add(Object key, Bitmap image) {
|
||||
Rect rect = new Rect(0, 0, image.getWidth(), image.getHeight());
|
||||
if (rect.width > atlasWidth || rect.height > atlasHeight) {
|
||||
if (key == null)
|
||||
throw new RuntimeException("PackerAtlasItem size too small for Bitmap.");
|
||||
throw new RuntimeException("PackerAtlasItem size too small for Bitmap: " + key);
|
||||
}
|
||||
|
||||
PackerAtlasItem packerAtlasItem = packStrategy.pack(this, key, rect);
|
||||
if (key != null) {
|
||||
packerAtlasItem.rects.put(key, rect);
|
||||
packerAtlasItem.addedRects.add(key);
|
||||
}
|
||||
|
||||
int rectX = rect.x, rectY = rect.y, rectWidth = rect.width, rectHeight = rect.height;
|
||||
|
||||
packerAtlasItem.drawBitmap(image, rectX,
|
||||
flipY ? packerAtlasItem.image.getHeight() - rectY : rectY);
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
public synchronized PackerAtlasItem getAtlasItem(int index) {
|
||||
return packerAtlasItems.get(index);
|
||||
}
|
||||
|
||||
public int getAtlasCount() {
|
||||
return packerAtlasItems.size();
|
||||
}
|
||||
|
||||
public static class PackerAtlasItem {
|
||||
HashMap<Object, Rect> rects = new HashMap<>();
|
||||
final Bitmap image;
|
||||
final Canvas canvas;
|
||||
final ArrayList<Object> addedRects = new ArrayList<>();
|
||||
|
||||
PackerAtlasItem(BitmapPacker packer) {
|
||||
image = CanvasAdapter.newBitmap(packer.atlasWidth, packer.atlasHeight, 0);
|
||||
canvas = CanvasAdapter.newCanvas();
|
||||
canvas.setBitmap(this.image);
|
||||
canvas.fillColor(Color.TRANSPARENT);
|
||||
}
|
||||
|
||||
public TextureAtlas getAtlas() {
|
||||
TextureAtlas atlas = new TextureAtlas(image);
|
||||
//add regions
|
||||
for (Map.Entry<Object, Rect> entry : rects.entrySet()) {
|
||||
atlas.addTextureRegion(entry.getKey(), entry.getValue().getAtlasRect());
|
||||
}
|
||||
return atlas;
|
||||
}
|
||||
|
||||
void drawBitmap(Bitmap image, int x, int y) {
|
||||
canvas.drawBitmap(image, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
public interface PackStrategy {
|
||||
void sort(ArrayList<Bitmap> images);
|
||||
|
||||
PackerAtlasItem pack(BitmapPacker packer, Object key, Rect rect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does bin packing by inserting to the right or below previously packed rectangles.
|
||||
* This is good at packing arbitrarily sized images.
|
||||
*
|
||||
* @author mzechner
|
||||
* @author Nathan Sweet
|
||||
* @author Rob Rendell
|
||||
*/
|
||||
public static class GuillotineStrategy implements PackStrategy {
|
||||
Comparator<Bitmap> comparator;
|
||||
|
||||
public void sort(ArrayList<Bitmap> Bitmaps) {
|
||||
if (comparator == null) {
|
||||
comparator = new Comparator<Bitmap>() {
|
||||
public int compare(Bitmap o1, Bitmap o2) {
|
||||
return Math.max(o1.getWidth(), o1.getHeight()) - Math.max(o2.getWidth(), o2.getHeight());
|
||||
}
|
||||
};
|
||||
}
|
||||
Collections.sort(Bitmaps, comparator);
|
||||
}
|
||||
|
||||
public PackerAtlasItem pack(BitmapPacker packer, Object key, Rect rect) {
|
||||
GuillotineAtlasItem atlasItem;
|
||||
if (packer.packerAtlasItems.size() == 0) {
|
||||
// Add a atlas item if empty.
|
||||
atlasItem = new GuillotineAtlasItem(packer);
|
||||
packer.packerAtlasItems.add(atlasItem);
|
||||
} else {
|
||||
// Always try to pack into the last atlas item.
|
||||
atlasItem = (GuillotineAtlasItem) packer.packerAtlasItems.get(packer.packerAtlasItems.size() - 1);
|
||||
}
|
||||
|
||||
int padding = packer.padding;
|
||||
rect.width += padding;
|
||||
rect.height += padding;
|
||||
Node node = insert(atlasItem.root, rect);
|
||||
if (node == null) {
|
||||
// Didn't fit, pack into a new atlas item.
|
||||
atlasItem = new GuillotineAtlasItem(packer);
|
||||
packer.packerAtlasItems.add(atlasItem);
|
||||
node = insert(atlasItem.root, rect);
|
||||
}
|
||||
node.full = true;
|
||||
rect.set(node.rect.x, node.rect.y, node.rect.width - padding, node.rect.height - padding);
|
||||
return atlasItem;
|
||||
}
|
||||
|
||||
private Node insert(Node node, Rect rect) {
|
||||
if (!node.full && node.leftChild != null && node.rightChild != null) {
|
||||
Node newNode = insert(node.leftChild, rect);
|
||||
if (newNode == null) newNode = insert(node.rightChild, rect);
|
||||
return newNode;
|
||||
} else {
|
||||
if (node.full) return null;
|
||||
if (node.rect.width == rect.width && node.rect.height == rect.height) return node;
|
||||
if (node.rect.width < rect.width || node.rect.height < rect.height) return null;
|
||||
|
||||
node.leftChild = new Node();
|
||||
node.rightChild = new Node();
|
||||
|
||||
int deltaWidth = node.rect.width - rect.width;
|
||||
int deltaHeight = node.rect.height - rect.height;
|
||||
if (deltaWidth > deltaHeight) {
|
||||
node.leftChild.rect.x = node.rect.x;
|
||||
node.leftChild.rect.y = node.rect.y;
|
||||
node.leftChild.rect.width = rect.width;
|
||||
node.leftChild.rect.height = node.rect.height;
|
||||
|
||||
node.rightChild.rect.x = node.rect.x + rect.width;
|
||||
node.rightChild.rect.y = node.rect.y;
|
||||
node.rightChild.rect.width = node.rect.width - rect.width;
|
||||
node.rightChild.rect.height = node.rect.height;
|
||||
} else {
|
||||
node.leftChild.rect.x = node.rect.x;
|
||||
node.leftChild.rect.y = node.rect.y;
|
||||
node.leftChild.rect.width = node.rect.width;
|
||||
node.leftChild.rect.height = rect.height;
|
||||
|
||||
node.rightChild.rect.x = node.rect.x;
|
||||
node.rightChild.rect.y = node.rect.y + rect.height;
|
||||
node.rightChild.rect.width = node.rect.width;
|
||||
node.rightChild.rect.height = node.rect.height - rect.height;
|
||||
}
|
||||
|
||||
return insert(node.leftChild, rect);
|
||||
}
|
||||
}
|
||||
|
||||
static final class Node {
|
||||
Node leftChild;
|
||||
Node rightChild;
|
||||
final Rect rect = new Rect();
|
||||
boolean full;
|
||||
}
|
||||
|
||||
static class GuillotineAtlasItem extends PackerAtlasItem {
|
||||
Node root;
|
||||
|
||||
GuillotineAtlasItem(BitmapPacker packer) {
|
||||
super(packer);
|
||||
root = new Node();
|
||||
root.rect.x = packer.padding;
|
||||
root.rect.y = packer.padding;
|
||||
root.rect.width = packer.atlasWidth - packer.padding * 2;
|
||||
root.rect.height = packer.atlasHeight - packer.padding * 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does bin packing by inserting in rows. This is good at packing images that have similar heights.
|
||||
*
|
||||
* @author Nathan Sweet
|
||||
*/
|
||||
public static class SkylineStrategy implements PackStrategy {
|
||||
Comparator<Bitmap> comparator;
|
||||
|
||||
public void sort(ArrayList<Bitmap> images) {
|
||||
if (comparator == null) {
|
||||
comparator = new Comparator<Bitmap>() {
|
||||
public int compare(Bitmap o1, Bitmap o2) {
|
||||
return o1.getHeight() - o2.getHeight();
|
||||
}
|
||||
};
|
||||
}
|
||||
Collections.sort(images, comparator);
|
||||
}
|
||||
|
||||
public PackerAtlasItem pack(BitmapPacker packer, Object key, Rect rect) {
|
||||
int padding = packer.padding;
|
||||
int atlasWidth = packer.atlasWidth - padding * 2, atlasHeight = packer.atlasHeight - padding * 2;
|
||||
int rectWidth = rect.width + padding, rectHeight = rect.height + padding;
|
||||
for (int i = 0, n = packer.packerAtlasItems.size(); i < n; i++) {
|
||||
SkylineAtlasItem atlasItem = (SkylineAtlasItem) packer.packerAtlasItems.get(i);
|
||||
SkylineAtlasItem.Row bestRow = null;
|
||||
// Fit in any row before the last.
|
||||
for (int ii = 0, nn = atlasItem.rows.size() - 1; ii < nn; ii++) {
|
||||
SkylineAtlasItem.Row row = atlasItem.rows.get(ii);
|
||||
if (row.x + rectWidth >= atlasWidth) continue;
|
||||
if (row.y + rectHeight >= atlasHeight) continue;
|
||||
if (rectHeight > row.height) continue;
|
||||
if (bestRow == null || row.height < bestRow.height) bestRow = row;
|
||||
}
|
||||
if (bestRow == null) {
|
||||
// Fit in last row, increasing height.
|
||||
SkylineAtlasItem.Row row = atlasItem.rows.get(atlasItem.rows.size() - 1);
|
||||
if (row.y + rectHeight >= atlasHeight) continue;
|
||||
if (row.x + rectWidth < atlasWidth) {
|
||||
row.height = Math.max(row.height, rectHeight);
|
||||
bestRow = row;
|
||||
} else {
|
||||
// Fit in new row.
|
||||
bestRow = new SkylineAtlasItem.Row();
|
||||
bestRow.y = row.y + row.height;
|
||||
bestRow.height = rectHeight;
|
||||
if (bestRow.y + bestRow.height > atlasHeight) continue;
|
||||
atlasItem.rows.add(bestRow);
|
||||
}
|
||||
}
|
||||
|
||||
rect.x = bestRow.x;
|
||||
rect.y = bestRow.y;
|
||||
bestRow.x += rectWidth;
|
||||
return atlasItem;
|
||||
}
|
||||
// Fit in new atlas item.
|
||||
SkylineAtlasItem atlasItem = new SkylineAtlasItem(packer);
|
||||
packer.packerAtlasItems.add(atlasItem);
|
||||
SkylineAtlasItem.Row row = new SkylineAtlasItem.Row();
|
||||
row.x = padding + rectWidth;
|
||||
row.y = padding;
|
||||
row.height = rectHeight;
|
||||
atlasItem.rows.add(row);
|
||||
rect.x = padding;
|
||||
rect.y = padding;
|
||||
return atlasItem;
|
||||
}
|
||||
|
||||
static class SkylineAtlasItem extends PackerAtlasItem {
|
||||
ArrayList<Row> rows = new ArrayList<>();
|
||||
|
||||
SkylineAtlasItem(BitmapPacker packer) {
|
||||
super(packer);
|
||||
|
||||
}
|
||||
|
||||
static class Row {
|
||||
int x, y, height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class Rect {
|
||||
int x, y, width, height;
|
||||
|
||||
Rect() {
|
||||
}
|
||||
|
||||
Rect(int x, int y, int width, int height) {
|
||||
this.set(x, y, width, height);
|
||||
}
|
||||
|
||||
void set(int x, int y, int width, int height) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
TextureAtlas.Rect getAtlasRect() {
|
||||
return new TextureAtlas.Rect(x, y, width, height);
|
||||
}
|
||||
}
|
||||
}
|
@ -14,150 +14,67 @@
|
||||
*/
|
||||
package org.oscim.utils;
|
||||
|
||||
import org.oscim.backend.CanvasAdapter;
|
||||
import org.oscim.backend.canvas.Bitmap;
|
||||
import org.oscim.backend.canvas.Canvas;
|
||||
import org.oscim.renderer.atlas.TextureAtlas;
|
||||
import org.oscim.renderer.atlas.TextureRegion;
|
||||
import org.oscim.utils.math.MathUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class TextureAtlasUtils {
|
||||
|
||||
private final static int MAX_ATLAS_SIZE = 1024;
|
||||
private final static int PAD = 2;
|
||||
private static final int MAX_ATLAS_SIZE = 2048;
|
||||
private static final int PAD = 2;
|
||||
|
||||
/**
|
||||
* Create a Map<Object, TextureRegion> from Map<Object, Bitmap>!
|
||||
* <br/>
|
||||
* The List<TextureAtlas> contains the generated TextureAtlas object, for disposing if no longer needed!<br/>
|
||||
* With tha param disposeBitmap, all Bitmaps will released!<br/>
|
||||
* With parameter flipY, the Atlas TextureItem will flipped over Y. (Is needed by iOS)<br/>
|
||||
* Create atlas texture regions from bitmaps.
|
||||
*
|
||||
* @param inputMap Map<Object, Bitmap> input Map with all Bitmaps, from which the regions are to be created
|
||||
* @param outputMap Map<Object, TextureRegion> contains all generated TextureRegions
|
||||
* @param atlasList List<TextureAtlas> contains all created TextureAtlases
|
||||
* @param disposeBitmaps boolean (will recycle all Bitmap's)
|
||||
* @param flipY boolean (set True with iOS)
|
||||
* @param inputMap input bitmaps
|
||||
* @param outputMap generated texture regions
|
||||
* @param atlasList created texture atlases
|
||||
* @param disposeBitmaps recycle input bitmaps
|
||||
* @param flipY texture items flip over y (needed on iOS)
|
||||
*/
|
||||
public static void createTextureRegions(final Map<Object, Bitmap> inputMap, Map<Object, TextureRegion> outputMap,
|
||||
List<TextureAtlas> atlasList, boolean disposeBitmaps, boolean flipY) {
|
||||
|
||||
// step 1: sort inputMap by Bitmap size
|
||||
List<Map.Entry<Object, Bitmap>> list =
|
||||
new LinkedList<>(inputMap.entrySet());
|
||||
|
||||
Collections.sort(list, new Comparator<Map.Entry<Object, Bitmap>>() {
|
||||
public int compare(Map.Entry<Object, Bitmap> o1, Map.Entry<Object, Bitmap> o2) {
|
||||
int width1 = o1.getValue().getWidth();
|
||||
int width2 = o2.getValue().getWidth();
|
||||
int height1 = o1.getValue().getHeight();
|
||||
int height2 = o2.getValue().getHeight();
|
||||
return Math.max(width2, height2) - Math.max(width1, height1);
|
||||
}
|
||||
});
|
||||
|
||||
Map<Object, Object> sortedByValues = new LinkedHashMap<>();
|
||||
for (Map.Entry<Object, Bitmap> entry : list) {
|
||||
sortedByValues.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
|
||||
//step 2: calculate Atlas count and size
|
||||
public static void createTextureRegions(final Map<Object, Bitmap> inputMap,
|
||||
Map<Object, TextureRegion> outputMap,
|
||||
List<TextureAtlas> atlasList, boolean disposeBitmaps,
|
||||
boolean flipY) {
|
||||
// calculate atlas size
|
||||
int completePixel = PAD * PAD;
|
||||
for (Map.Entry<Object, Object> entry : sortedByValues.entrySet()) {
|
||||
completePixel += (((Bitmap) entry.getValue()).getWidth() + PAD)
|
||||
* (((Bitmap) entry.getValue()).getHeight() + PAD);
|
||||
int minHeight = Integer.MAX_VALUE;
|
||||
int maxHeight = Integer.MIN_VALUE;
|
||||
for (Map.Entry<Object, Bitmap> entry : inputMap.entrySet()) {
|
||||
int height = entry.getValue().getHeight();
|
||||
completePixel += (entry.getValue().getWidth() + PAD) * (height + PAD);
|
||||
|
||||
minHeight = Math.min(minHeight, height);
|
||||
maxHeight = Math.max(maxHeight, height);
|
||||
}
|
||||
|
||||
BitmapPacker.PackStrategy strategy = maxHeight - minHeight < 50
|
||||
? new BitmapPacker.SkylineStrategy()
|
||||
: new BitmapPacker.GuillotineStrategy();
|
||||
completePixel *= 1.2; // add estimated blank pixels
|
||||
int atlasWidth = (int) Math.sqrt(completePixel);
|
||||
int atlasCount = (atlasWidth / MAX_ATLAS_SIZE) + 1;
|
||||
if (atlasCount > 1) atlasWidth = MAX_ATLAS_SIZE;
|
||||
// next power of two
|
||||
atlasWidth = MathUtils.nextPowerOfTwo(MathUtils.nextPowerOfTwo(atlasWidth) + 1);
|
||||
// limit to max
|
||||
atlasWidth = Math.min(MAX_ATLAS_SIZE, atlasWidth);
|
||||
|
||||
BitmapPacker bitmapPacker = new BitmapPacker(atlasWidth, atlasWidth, PAD, strategy, flipY);
|
||||
|
||||
//step 3: replace value object with object that holds the Bitmap and a Rectangle
|
||||
for (Map.Entry<Object, Object> entry : sortedByValues.entrySet()) {
|
||||
BmpRectangleObject newObject = new BmpRectangleObject();
|
||||
newObject.bitmap = (Bitmap) entry.getValue();
|
||||
newObject.rec = new TextureAtlas.Rect(0, 0,
|
||||
newObject.bitmap.getWidth(), newObject.bitmap.getHeight());
|
||||
entry.setValue(newObject);
|
||||
for (Map.Entry<Object, Bitmap> entry : inputMap.entrySet()) {
|
||||
completePixel += (entry.getValue().getWidth() + PAD)
|
||||
* (entry.getValue().getHeight() + PAD);
|
||||
bitmapPacker.add(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
|
||||
//step 4: calculate Regions(rectangles) and split to atlases
|
||||
List<AtlasElement> atlases = new ArrayList<>();
|
||||
atlases.add(new AtlasElement());
|
||||
int atlasIndex = 0;
|
||||
int maxLineHeight = PAD;
|
||||
for (Map.Entry<Object, Object> entry : sortedByValues.entrySet()) {
|
||||
BmpRectangleObject obj = (BmpRectangleObject) entry.getValue();
|
||||
AtlasElement atlas = atlases.get(atlasIndex);
|
||||
if ((atlas.width + obj.rec.w + PAD) > atlasWidth) {
|
||||
// not enough space, try next line
|
||||
if ((atlas.height + maxLineHeight + obj.rec.h + PAD) > MAX_ATLAS_SIZE) {
|
||||
// not enough space, take new Atlas
|
||||
atlas.width = atlasWidth;
|
||||
atlas.height += PAD + maxLineHeight;
|
||||
atlasIndex++;
|
||||
atlas = new AtlasElement();
|
||||
atlases.add(atlas);
|
||||
|
||||
obj.rec.x = PAD + atlas.width;
|
||||
obj.rec.y = PAD + atlas.height;
|
||||
atlas.width += obj.rec.w + PAD;
|
||||
maxLineHeight = obj.rec.h + PAD;
|
||||
} else {
|
||||
// new line
|
||||
atlas.width = 0;
|
||||
atlas.height += PAD + maxLineHeight;
|
||||
obj.rec.x = PAD + atlas.width;
|
||||
obj.rec.y = PAD + atlas.height;
|
||||
atlas.width += obj.rec.w + PAD;
|
||||
}
|
||||
} else {
|
||||
// new row
|
||||
obj.rec.x = PAD + atlas.width;
|
||||
obj.rec.y = PAD + atlas.height;
|
||||
atlas.width += obj.rec.w + PAD;
|
||||
maxLineHeight = Math.max(maxLineHeight, obj.rec.h + PAD);
|
||||
}
|
||||
atlas.map.put(entry.getKey(), obj);
|
||||
}
|
||||
AtlasElement lastAtlas = atlases.get(atlases.size() - 1);
|
||||
lastAtlas.width = atlasWidth;
|
||||
lastAtlas.height += maxLineHeight;
|
||||
|
||||
|
||||
//step 5: create TextureAtlases and there TextureRegions
|
||||
for (AtlasElement atlas : atlases) {
|
||||
Bitmap atlasBitmap = CanvasAdapter.newBitmap(atlas.width, atlas.height, 0);
|
||||
Canvas canvas = CanvasAdapter.newCanvas();
|
||||
canvas.setBitmap(atlasBitmap);
|
||||
|
||||
// draw regions into texture
|
||||
for (Map.Entry<Object, Object> entry : atlas.map.entrySet()) {
|
||||
BmpRectangleObject obj = (BmpRectangleObject) entry.getValue();
|
||||
if (obj.rec.x + obj.rec.w > atlasBitmap.getWidth() ||
|
||||
obj.rec.y + obj.rec.h > atlasBitmap.getHeight()) {
|
||||
throw new RuntimeException("atlas region outside of textureRegion");
|
||||
}
|
||||
canvas.drawBitmap(obj.bitmap, obj.rec.x, flipY ? atlas.height - obj.rec.y - obj.rec.h : obj.rec.y);
|
||||
}
|
||||
TextureAtlas textureAtlas = new TextureAtlas(atlasBitmap);
|
||||
atlasList.add(textureAtlas);
|
||||
|
||||
//register regions and put there into outputMap
|
||||
for (Map.Entry<Object, Object> entry : atlas.map.entrySet()) {
|
||||
textureAtlas.addTextureRegion(entry.getKey(), ((BmpRectangleObject) entry.getValue()).rec);
|
||||
outputMap.put(entry.getKey(), textureAtlas.getTextureRegion(entry.getKey()));
|
||||
}
|
||||
for (int i = 0, n = bitmapPacker.getAtlasCount(); i < n; i++) {
|
||||
BitmapPacker.PackerAtlasItem packerAtlasItem = bitmapPacker.getAtlasItem(i);
|
||||
TextureAtlas atlas = packerAtlasItem.getAtlas();
|
||||
atlasList.add(atlas);
|
||||
outputMap.putAll(atlas.getRegions());
|
||||
}
|
||||
|
||||
if (disposeBitmaps) {
|
||||
@ -167,14 +84,4 @@ public class TextureAtlasUtils {
|
||||
inputMap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private static class BmpRectangleObject {
|
||||
private Bitmap bitmap;
|
||||
private TextureAtlas.Rect rec;
|
||||
}
|
||||
|
||||
private static class AtlasElement {
|
||||
int width = 0, height = 0;
|
||||
Map<Object, Object> map = new LinkedHashMap<>();
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user