diff --git a/vtm/src/org/oscim/utils/TextureAtlasUtils.java b/vtm/src/org/oscim/utils/TextureAtlasUtils.java new file mode 100644 index 00000000..88b9bf92 --- /dev/null +++ b/vtm/src/org/oscim/utils/TextureAtlasUtils.java @@ -0,0 +1,176 @@ +/* + * Copyright 2017 Longri + * + * 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 . + */ +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 java.util.*; + +/** + * Created by Longri on 26.01.2017. + */ +public class TextureAtlasUtils { + + private final static int MAX_ATLAS_SIZE = 1024; + private final static int PAD = 2; + + /** + * Create a Map from Map! + *
+ * The List contains the generated TextureAtlas object, for disposing if no longer needed!
+ * With tha param disposeBitmap, all Bitmaps will released!
+ * With parameter flipY, the Atlas TextureItem will flipped over Y. (Is needed by iOS)
+ * @param inputMap Map input Map with all Bitmaps, from which the regions are to be created + * @param outputMap Map contains all generated TextureRegions + * @param atlasList List contains all created TextureAtlases + * @param disposeBitmaps boolean (will recycle all Bitmap's) + * @param flipY boolean (set True with iOS) + */ + public static void createTextureRegions(final Map inputMap, Map outputMap, + List atlasList, boolean disposeBitmaps, boolean flipY) { + + // step 1: sort inputMap by Bitmap size + List> list = + new LinkedList>(inputMap.entrySet()); + + Collections.sort(list, new Comparator>() { + public int compare(Map.Entry o1, Map.Entry 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 sortedByValues = new LinkedHashMap(); + for (Map.Entry entry : list) { + sortedByValues.put(entry.getKey(), entry.getValue()); + } + + + //step 2: calculate Atlas count and size + int completePixel = PAD * PAD; + for (Map.Entry entry : sortedByValues.entrySet()) { + completePixel += (((Bitmap) entry.getValue()).getWidth() + PAD) + * (((Bitmap) entry.getValue()).getHeight() + PAD); + } + 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; + + + //step 3: replace value object with object that holds the Bitmap and a Rectangle + for (Map.Entry 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); + } + + + //step 4: calculate Regions(rectangles) and split to atlases + List atlases = new ArrayList(); + atlases.add(new AtlasElement()); + int atlasIndex = 0; + int maxLineHeight = PAD; + for (Map.Entry 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 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 entry : atlas.map.entrySet()) { + textureAtlas.addTextureRegion(entry.getKey(), ((BmpRectangleObject) entry.getValue()).rec); + outputMap.put(entry.getKey(), textureAtlas.getTextureRegion(entry.getKey())); + } + } + + if (disposeBitmaps) { + for (Bitmap bmp : inputMap.values()) { + bmp.recycle(); + } + inputMap.clear(); + } + } + + private static class BmpRectangleObject { + private Bitmap bitmap; + private TextureAtlas.Rect rec; + } + + private static class AtlasElement { + int width = 0, height = 0; + Map map = new LinkedHashMap(); + } +}