From 47ad1d3617fd22242af0bb369f06c9d34e8b8031 Mon Sep 17 00:00:00 2001 From: Hannes Janetzek Date: Fri, 12 Apr 2013 04:32:01 +0200 Subject: [PATCH] start of TextureAtlas --- src/org/oscim/renderer/TextureAtlas.java | 212 ++++++++++++++++++ .../oscim/renderer/overlays/AtlasTest.java | 86 +++++++ 2 files changed, 298 insertions(+) create mode 100644 src/org/oscim/renderer/TextureAtlas.java create mode 100644 src/org/oscim/renderer/overlays/AtlasTest.java diff --git a/src/org/oscim/renderer/TextureAtlas.java b/src/org/oscim/renderer/TextureAtlas.java new file mode 100644 index 00000000..861eeb83 --- /dev/null +++ b/src/org/oscim/renderer/TextureAtlas.java @@ -0,0 +1,212 @@ +/* + * Copyright 2013 Hannes Janetzek + * + * 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 . + */ + +// ported from: +/* ============================================================================ + * Freetype GL - A C OpenGL Freetype engine + * Platform: Any + * WWW: http://code.google.com/p/freetype-gl/ + * ---------------------------------------------------------------------------- + * Copyright 2011,2012 Nicolas P. Rougier. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY NICOLAS P. ROUGIER ''AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL NICOLAS P. ROUGIER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are + * those of the authors and should not be interpreted as representing official + * policies, either expressed or implied, of Nicolas P. Rougier. + * ============================================================================ + * + * This source is based on the article by Jukka Jylanki : + * "A Thousand Ways to Pack the Bin - A Practical Approach to + * Two-Dimensional Rectangle Bin Packing", February 27, 2010. + * + * More precisely, this is an implementation of the Skyline Bottom-Left + * algorithm based on C++ sources provided by Jukka Jylanki at: + * http://clb.demon.fi/files/RectangleBinPack/ + * + * ============================================================================ + */ +package org.oscim.renderer; + +import org.oscim.utils.pool.Inlist; + +import android.graphics.Bitmap; + +public class TextureAtlas { + /** Allocated slots */ + Slot mSlots; + Rect mRects; + + /** Width (in pixels) of the underlying texture */ + final int mWidth; + + /** Height (in pixels) of the underlying texture */ + final int mHeight; + + /** Depth (in bytes) of the underlying texture */ + final int mDepth; + + /** Allocated surface size */ + int mUsed; + + /** Texture identity (OpenGL) */ + int mId; + + /** Atlas data */ + Bitmap mData; + + class Slot extends Inlist { + int x, y, w; + + public Slot(int x, int y, int w) { + this.x = x; + this.y = y; + this.w = w; + } + } + + public static class Rect extends Inlist { + public int x, y, w, h; + } + + private TextureAtlas(int width, int height, int depth) { + mWidth = width; + mHeight = height; + mDepth = depth; + mSlots = new Slot(1, 1, width - 2); + } + + public Rect getRegion(int width, int height) { + int y, bestHeight, bestWidth; + Slot slot, prev; + Rect r = new Rect(); + r.w = width; + r.h = height; + + bestHeight = Integer.MAX_VALUE; + bestWidth = Integer.MAX_VALUE; + + Slot bestSlot = null; + + for (slot = mSlots; slot != null; slot = slot.next) { + // fit width + if ((slot.x + width) > (mWidth - 1)) + continue; + + // fit height + y = slot.y; + int widthLeft = width; + + Slot fit = slot; + while (widthLeft > 0) { + if (fit.y > y) + y = fit.y; + + if ((y + height) > (mHeight - 1)) { + y = -1; + break; + } + widthLeft -= fit.w; + + fit = fit.next; + } + + if (y < 0) + continue; + + int h = y + height; + if ((h < bestHeight) || ((h == bestHeight) && (slot.w < bestWidth))) { + bestHeight = h; + bestSlot = slot; + bestWidth = slot.w; + r.x = slot.x; + r.y = y; + } + } + + if (bestSlot == null) + return null; + + Slot curSlot = new Slot(r.x, r.y + height, width); + mSlots = Inlist.prependRelative(mSlots, curSlot, bestSlot); + + for (prev = curSlot; prev.next != null;) { + slot = prev.next; + + int shrink = (prev.x + prev.w) - slot.x; + + if (shrink <= 0) + break; + + slot.x += shrink; + slot.w -= shrink; + if (slot.w > 0) + break; + + // erease slot + prev.next = slot.next; + } + + // merge + for (slot = mSlots; slot.next != null;) { + Slot next = slot.next; + + if (slot.y == next.y) { + slot.w += next.w; + + // erease 'next' slot + slot.next = next.next; + } else { + slot = next; + } + } + + mUsed += width * height; + + mRects = Inlist.push(mRects, r); + return r; + } + + public void clear() { + mRects = null; + mSlots = new Slot(1, 1, mWidth - 2); + } + + public static TextureAtlas create(int width, int height, int depth) { + if (!(depth == 1 || depth == 3 || depth == 4)) + throw new IllegalArgumentException("invalid depth"); + + return new TextureAtlas(width, height, depth); + } +} diff --git a/src/org/oscim/renderer/overlays/AtlasTest.java b/src/org/oscim/renderer/overlays/AtlasTest.java new file mode 100644 index 00000000..40d9fb10 --- /dev/null +++ b/src/org/oscim/renderer/overlays/AtlasTest.java @@ -0,0 +1,86 @@ +package org.oscim.renderer.overlays; + +import java.util.Arrays; + +import org.oscim.core.MapPosition; +import org.oscim.graphics.Color; +import org.oscim.graphics.Paint.Cap; +import org.oscim.renderer.GLRenderer.Matrices; +import org.oscim.renderer.TextureAtlas; +import org.oscim.renderer.TextureAtlas.Rect; +import org.oscim.renderer.layer.LineLayer; +import org.oscim.theme.renderinstruction.Line; +import org.oscim.view.MapView; + +import android.util.Log; + +public class AtlasTest extends BasicOverlay { + + public AtlasTest(MapView mapView) { + super(mapView); + + TextureAtlas mAtlas = TextureAtlas.create(2048, 2048, 1); + + LineLayer ll = layers.getLineLayer(0); + ll.line = new Line(Color.BLUE, 3, Cap.BUTT); + ll.width = 1.5f; + + LineLayer ll2 = layers.getLineLayer(1); + ll2.line = new Line(Color.RED, 3, Cap.BUTT); + ll2.width = 1.5f; + + float[] points = new float[10]; + + for (int i = 0; i < 400; i++) { + int w = (int) (20 + Math.random() * 256); + int h = (int) (20 + Math.random() * 56); + Rect r = mAtlas.getRegion(w, h); + if (r == null) { + Log.d("...", "no space left"); + continue; + } + points[0] = r.x; + points[1] = r.y; + points[2] = r.x + r.w; + points[3] = r.y; + points[4] = r.x + r.w; + points[5] = r.y + r.h; + points[6] = r.x; + points[7] = r.y + r.h; + points[8] = r.x; + points[9] = r.y; + ll.addLine(points, 10, false); + + r.x += 2; + r.y += 2; + points[0] = r.x; + points[1] = r.y; + points[2] = r.x + w; + points[3] = r.y; + points[4] = r.x + w; + points[5] = r.y + h; + points[6] = r.x; + points[7] = r.y + h; + points[8] = r.x; + points[9] = r.y; + + Log.d("...", "add region: " + Arrays.toString(points)); + + ll2.addLine(points, 10, false); + + } + this.newData = true; + } + + boolean initial = true; + + @Override + public void update(MapPosition pos, boolean positionChanged, + boolean tilesChanged, Matrices m) { + + if (initial) { + mMapPosition.copy(pos); + initial = false; + } + } +}