summaryrefslogtreecommitdiffstats
path: root/android/source/src/java/org/mozilla/gecko/gfx/SingleTileLayer.java
blob: 0bc271678368a60ad6d0e0591df8cfdb26016d7f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.gecko.gfx;

import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.graphics.RegionIterator;
import android.opengl.GLES20;

import java.nio.FloatBuffer;

/**
 * Encapsulates the logic needed to draw a single textured tile.
 *
 * TODO: Repeating textures really should be their own type of layer.
 */
public class SingleTileLayer extends TileLayer {
    private static final String LOGTAG = "GeckoSingleTileLayer";

    private Rect mMask;

    // To avoid excessive GC, declare some objects here that would otherwise
    // be created and destroyed frequently during draw().
    private final RectF mBounds;
    private final RectF mTextureBounds;
    private final RectF mViewport;
    private final Rect mIntBounds;
    private final Rect mSubRect;
    private final RectF mSubRectF;
    private final Region mMaskedBounds;
    private final Rect mCropRect;
    private final RectF mObjRectF;
    private final float[] mCoords;

    public SingleTileLayer(CairoImage image) {
        this(false, image);
    }

    public SingleTileLayer(boolean repeat, CairoImage image) {
        this(image, repeat ? TileLayer.PaintMode.REPEAT : TileLayer.PaintMode.NORMAL);
    }

    public SingleTileLayer(CairoImage image, TileLayer.PaintMode paintMode) {
        super(image, paintMode);

        mBounds = new RectF();
        mTextureBounds = new RectF();
        mViewport = new RectF();
        mIntBounds = new Rect();
        mSubRect = new Rect();
        mSubRectF = new RectF();
        mMaskedBounds = new Region();
        mCropRect = new Rect();
        mObjRectF = new RectF();
        mCoords = new float[20];
    }

    /**
     * Set an area to mask out when rendering.
     */
    public void setMask(Rect aMaskRect) {
        mMask = aMaskRect;
    }

    @Override
    public void draw(RenderContext context) {
        // mTextureIDs may be null here during startup if Layer.java's draw method
        // failed to acquire the transaction lock and call performUpdates.
        if (!initialized())
            return;

        mViewport.set(context.viewport);

        if (repeats()) {
            // If we're repeating, we want to adjust the texture bounds so that
            // the texture repeats the correct number of times when drawn at
            // the size of the viewport.
            mBounds.set(getBounds(context));
            mTextureBounds.set(0.0f, 0.0f, mBounds.width(), mBounds.height());
            mBounds.set(0.0f, 0.0f, mViewport.width(), mViewport.height());
        } else if (stretches()) {
            // If we're stretching, we just want the bounds and texture bounds
            // to fit to the page.
            mBounds.set(context.pageRect);
            mTextureBounds.set(mBounds);
        } else {
            mBounds.set(getBounds(context));
            mTextureBounds.set(mBounds);
        }

        mBounds.roundOut(mIntBounds);
        mMaskedBounds.set(mIntBounds);
        if (mMask != null) {
            mMaskedBounds.op(mMask, Region.Op.DIFFERENCE);
            if (mMaskedBounds.isEmpty())
                return;
        }

        // XXX Possible optimisation here, form this array so we can draw it in
        //     a single call.
        RegionIterator i = new RegionIterator(mMaskedBounds);
        while (i.next(mSubRect)) {
            // Compensate for rounding errors at the edge of the tile caused by
            // the roundOut above
            mSubRectF.set(Math.max(mBounds.left, (float)mSubRect.left),
                          Math.max(mBounds.top, (float)mSubRect.top),
                          Math.min(mBounds.right, (float)mSubRect.right),
                          Math.min(mBounds.bottom, (float)mSubRect.bottom));

            // This is the left/top/right/bottom of the rect, relative to the
            // bottom-left of the layer, to use for texture coordinates.
            mCropRect.set(Math.round(mSubRectF.left - mBounds.left),
                          Math.round(mBounds.bottom - mSubRectF.top),
                          Math.round(mSubRectF.right - mBounds.left),
                          Math.round(mBounds.bottom - mSubRectF.bottom));

            mObjRectF.set(mSubRectF.left - mViewport.left,
                          mViewport.bottom - mSubRectF.bottom,
                          mSubRectF.right - mViewport.left,
                          mViewport.bottom - mSubRectF.top);

            fillRectCoordBuffer(mCoords, mObjRectF, mViewport.width(), mViewport.height(),
                                mCropRect, mTextureBounds.width(), mTextureBounds.height());

            FloatBuffer coordBuffer = context.coordBuffer;
            int positionHandle = context.positionHandle;
            int textureHandle = context.textureHandle;

            GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getTextureID());

            // Make sure we are at position zero in the buffer
            coordBuffer.position(0);
            coordBuffer.put(mCoords);

            // Unbind any the current array buffer so we can use client side buffers
            GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);

            // Vertex coordinates are x,y,z starting at position 0 into the buffer.
            coordBuffer.position(0);
            GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20, coordBuffer);

            // Texture coordinates are texture_x, texture_y starting at position 3 into the buffer.
            coordBuffer.position(3);
            GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20, coordBuffer);
            GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
        }
    }
}