/* * Copyright 2013 Hannes Janetzek * * This file is part of the OpenScienceMap project (http://www.opensciencemap.org). * * 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.renderer.bucket; import static org.oscim.backend.GLAdapter.gl; import static org.oscim.renderer.MapRenderer.MAX_INDICES; import static org.oscim.renderer.MapRenderer.bindQuadIndicesVBO; import java.nio.ShortBuffer; import org.oscim.backend.GL; import org.oscim.backend.canvas.Bitmap; import org.oscim.renderer.GLShader; import org.oscim.renderer.GLState; import org.oscim.renderer.GLViewport; import org.oscim.renderer.MapRenderer; import org.oscim.renderer.bucket.TextureItem.TexturePool; /** * Renderer for a single bitmap, width and height must be power of 2. */ public class BitmapBucket extends TextureBucket { // TODO share layers.vbo() between BitmapTileLayers // static final Logger log = LoggerFactory.getLogger(BitmapLayer.class); private Bitmap mBitmap; private final boolean mReuseBitmap; private final short[] mVertices; private int mWidth, mHeight; /** * @param reuseBitmap false if the Bitmap should be disposed * after loading to texture. */ public BitmapBucket(boolean reuseBitmap) { super(RenderBucket.BITMAP); mReuseBitmap = reuseBitmap; mVertices = new short[24]; // used for size calculation of Layers buffer. numVertices = 4; } /** * w/h sets also target dimension to render the bitmap. */ public void setBitmap(Bitmap bitmap, int w, int h) { setBitmap(bitmap, w, h, null); } public void setBitmap(Bitmap bitmap, int w, int h, TexturePool pool) { mWidth = w; mHeight = h; mBitmap = bitmap; if (textures == null) { if (pool == null) textures = new TextureItem(mBitmap); else { textures = pool.get(mBitmap); } } TextureItem t = textures; t.indices = TextureBucket.INDICES_PER_SPRITE; } private void setVertices(ShortBuffer vboData) { short[] buf = mVertices; short w = (short) (mWidth * MapRenderer.COORD_SCALE); short h = (short) (mHeight * MapRenderer.COORD_SCALE); short texMin = 0; short texMax = 1; // putSprite(buf, pos, tx, ty, x1, y1, x2, y2, u1, v1, u2, v2); int pos = 0; // top-left buf[pos++] = 0; buf[pos++] = 0; buf[pos++] = -1; buf[pos++] = -1; buf[pos++] = texMin; buf[pos++] = texMin; // bot-left buf[pos++] = 0; buf[pos++] = h; buf[pos++] = -1; buf[pos++] = -1; buf[pos++] = texMin; buf[pos++] = texMax; // top-right buf[pos++] = w; buf[pos++] = 0; buf[pos++] = -1; buf[pos++] = -1; buf[pos++] = texMax; buf[pos++] = texMin; // bot-right buf[pos++] = w; buf[pos++] = h; buf[pos++] = -1; buf[pos++] = -1; buf[pos++] = texMax; buf[pos++] = texMax; this.vertexOffset = vboData.position() * 2; vboData.put(buf); } @Override protected void compile(ShortBuffer vboData, ShortBuffer iboData) { if (mBitmap == null) return; setVertices(vboData); textures.upload(); if (!mReuseBitmap) { mBitmap.recycle(); mBitmap = null; textures.bitmap = null; } } @Override protected void clear() { // release textures and vertexItems super.clear(); if (mBitmap == null) return; if (!mReuseBitmap) mBitmap.recycle(); mBitmap = null; //textures.bitmap = null; //textures.dispose(); //TextureItem.pool.releaseTexture(textures); //textures = null; } static class Shader extends GLShader { int uMVP, uAlpha, aPos, aTexCoord; Shader(String shaderFile) { if (!create(shaderFile)) return; uMVP = getUniform("u_mvp"); uAlpha = getUniform("u_alpha"); aPos = getAttrib("vertex"); aTexCoord = getAttrib("tex_coord"); } @Override public boolean useProgram() { if (super.useProgram()) { GLState.enableVertexArrays(aPos, aTexCoord); return true; } return false; } } public static final class Renderer { public final static int INDICES_PER_SPRITE = 6; final static int VERTICES_PER_SPRITE = 4; final static int SHORTS_PER_VERTICE = 6; static Shader shader; static void init() { shader = new Shader("texture_alpha"); } public static RenderBucket draw(RenderBucket b, GLViewport v, float scale, float alpha) { GLState.blend(true); Shader s = shader; s.useProgram(); TextureBucket tb = (TextureBucket) b; gl.uniform1f(s.uAlpha, alpha); v.mvp.setAsUniform(s.uMVP); bindQuadIndicesVBO(); for (TextureItem t = tb.textures; t != null; t = t.next) { t.bind(); for (int i = 0; i < t.indices; i += MAX_INDICES) { /* to.offset * (24(shorts) * * 2(short-bytes) / 6(indices) == 8) */ int off = (t.offset + i) * 8 + tb.vertexOffset; gl.vertexAttribPointer(s.aPos, 2, GL.SHORT, false, 12, off); gl.vertexAttribPointer(s.aTexCoord, 2, GL.SHORT, false, 12, off + 8); int numIndices = t.indices - i; if (numIndices > MAX_INDICES) numIndices = MAX_INDICES; gl.drawElements(GL.TRIANGLES, numIndices, GL.UNSIGNED_SHORT, 0); } } return b.next; } } }