michael@0: /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: package org.mozilla.gecko.gfx; michael@0: michael@0: import android.graphics.Rect; michael@0: import android.graphics.RectF; michael@0: import android.graphics.Region; michael@0: import android.graphics.RegionIterator; michael@0: import android.opengl.GLES20; michael@0: michael@0: import java.nio.FloatBuffer; michael@0: michael@0: /** michael@0: * Encapsulates the logic needed to draw a single textured tile. michael@0: * michael@0: * TODO: Repeating textures really should be their own type of layer. michael@0: */ michael@0: public class SingleTileLayer extends TileLayer { michael@0: private static final String LOGTAG = "GeckoSingleTileLayer"; michael@0: michael@0: private Rect mMask; michael@0: michael@0: // To avoid excessive GC, declare some objects here that would otherwise michael@0: // be created and destroyed frequently during draw(). michael@0: private final RectF mBounds; michael@0: private final RectF mTextureBounds; michael@0: private final RectF mViewport; michael@0: private final Rect mIntBounds; michael@0: private final Rect mSubRect; michael@0: private final RectF mSubRectF; michael@0: private final Region mMaskedBounds; michael@0: private final Rect mCropRect; michael@0: private final RectF mObjRectF; michael@0: private final float[] mCoords; michael@0: michael@0: public SingleTileLayer(CairoImage image) { michael@0: this(false, image); michael@0: } michael@0: michael@0: public SingleTileLayer(boolean repeat, CairoImage image) { michael@0: this(image, repeat ? TileLayer.PaintMode.REPEAT : TileLayer.PaintMode.NORMAL); michael@0: } michael@0: michael@0: public SingleTileLayer(CairoImage image, TileLayer.PaintMode paintMode) { michael@0: super(image, paintMode); michael@0: michael@0: mBounds = new RectF(); michael@0: mTextureBounds = new RectF(); michael@0: mViewport = new RectF(); michael@0: mIntBounds = new Rect(); michael@0: mSubRect = new Rect(); michael@0: mSubRectF = new RectF(); michael@0: mMaskedBounds = new Region(); michael@0: mCropRect = new Rect(); michael@0: mObjRectF = new RectF(); michael@0: mCoords = new float[20]; michael@0: } michael@0: michael@0: /** michael@0: * Set an area to mask out when rendering. michael@0: */ michael@0: public void setMask(Rect aMaskRect) { michael@0: mMask = aMaskRect; michael@0: } michael@0: michael@0: @Override michael@0: public void draw(RenderContext context) { michael@0: // mTextureIDs may be null here during startup if Layer.java's draw method michael@0: // failed to acquire the transaction lock and call performUpdates. michael@0: if (!initialized()) michael@0: return; michael@0: michael@0: mViewport.set(context.viewport); michael@0: michael@0: if (repeats()) { michael@0: // If we're repeating, we want to adjust the texture bounds so that michael@0: // the texture repeats the correct number of times when drawn at michael@0: // the size of the viewport. michael@0: mBounds.set(getBounds(context)); michael@0: mTextureBounds.set(0.0f, 0.0f, mBounds.width(), mBounds.height()); michael@0: mBounds.set(0.0f, 0.0f, mViewport.width(), mViewport.height()); michael@0: } else if (stretches()) { michael@0: // If we're stretching, we just want the bounds and texture bounds michael@0: // to fit to the page. michael@0: mBounds.set(context.pageRect); michael@0: mTextureBounds.set(mBounds); michael@0: } else { michael@0: mBounds.set(getBounds(context)); michael@0: mTextureBounds.set(mBounds); michael@0: } michael@0: michael@0: mBounds.roundOut(mIntBounds); michael@0: mMaskedBounds.set(mIntBounds); michael@0: if (mMask != null) { michael@0: mMaskedBounds.op(mMask, Region.Op.DIFFERENCE); michael@0: if (mMaskedBounds.isEmpty()) michael@0: return; michael@0: } michael@0: michael@0: // XXX Possible optimisation here, form this array so we can draw it in michael@0: // a single call. michael@0: RegionIterator i = new RegionIterator(mMaskedBounds); michael@0: while (i.next(mSubRect)) { michael@0: // Compensate for rounding errors at the edge of the tile caused by michael@0: // the roundOut above michael@0: mSubRectF.set(Math.max(mBounds.left, (float)mSubRect.left), michael@0: Math.max(mBounds.top, (float)mSubRect.top), michael@0: Math.min(mBounds.right, (float)mSubRect.right), michael@0: Math.min(mBounds.bottom, (float)mSubRect.bottom)); michael@0: michael@0: // This is the left/top/right/bottom of the rect, relative to the michael@0: // bottom-left of the layer, to use for texture coordinates. michael@0: mCropRect.set(Math.round(mSubRectF.left - mBounds.left), michael@0: Math.round(mBounds.bottom - mSubRectF.top), michael@0: Math.round(mSubRectF.right - mBounds.left), michael@0: Math.round(mBounds.bottom - mSubRectF.bottom)); michael@0: michael@0: mObjRectF.set(mSubRectF.left - mViewport.left, michael@0: mViewport.bottom - mSubRectF.bottom, michael@0: mSubRectF.right - mViewport.left, michael@0: mViewport.bottom - mSubRectF.top); michael@0: michael@0: fillRectCoordBuffer(mCoords, mObjRectF, mViewport.width(), mViewport.height(), michael@0: mCropRect, mTextureBounds.width(), mTextureBounds.height()); michael@0: michael@0: FloatBuffer coordBuffer = context.coordBuffer; michael@0: int positionHandle = context.positionHandle; michael@0: int textureHandle = context.textureHandle; michael@0: michael@0: GLES20.glActiveTexture(GLES20.GL_TEXTURE0); michael@0: GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getTextureID()); michael@0: michael@0: // Make sure we are at position zero in the buffer michael@0: coordBuffer.position(0); michael@0: coordBuffer.put(mCoords); michael@0: michael@0: // Unbind any the current array buffer so we can use client side buffers michael@0: GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); michael@0: michael@0: // Vertex coordinates are x,y,z starting at position 0 into the buffer. michael@0: coordBuffer.position(0); michael@0: GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20, coordBuffer); michael@0: michael@0: // Texture coordinates are texture_x, texture_y starting at position 3 into the buffer. michael@0: coordBuffer.position(3); michael@0: GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20, coordBuffer); michael@0: GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); michael@0: } michael@0: } michael@0: }