summaryrefslogtreecommitdiffstats
path: root/android/source/src/java/org/mozilla/gecko/gfx/NinePatchTileLayer.java
blob: 99f203961a7469e6976d7271d2497202e9126f9d (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
/* -*- 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.RectF;
import android.opengl.GLES20;

import java.nio.FloatBuffer;

/**
 * Encapsulates the logic needed to draw a nine-patch bitmap using OpenGL ES.
 *
 * For more information on nine-patch bitmaps, see the following document:
 *   http://developer.android.com/guide/topics/graphics/2d-graphics.html#nine-patch
 */
public class NinePatchTileLayer extends TileLayer {
    private static final int PATCH_SIZE = 16;
    private static final int TEXTURE_SIZE = 64;

    public NinePatchTileLayer(CairoImage image) {
        super(image, PaintMode.NORMAL);
    }

    @Override
    public void draw(RenderContext context) {
        if (!initialized())
            return;

        GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
        GLES20.glEnable(GLES20.GL_BLEND);

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getTextureID());
        drawPatches(context);
    }

    private void drawPatches(RenderContext context) {
        /*
         * We divide the nine-patch bitmap up as follows:
         *
         *    +---+---+---+
         *    | 0 | 1 | 2 |
         *    +---+---+---+
         *    | 3 |   | 4 |
         *    +---+---+---+
         *    | 5 | 6 | 7 |
         *    +---+---+---+
         */

        // page is the rect of the "missing" center spot in the picture above
        RectF page = context.pageRect;

        drawPatch(context, 0, PATCH_SIZE * 3,                                              /* 0 */
                  page.left - PATCH_SIZE, page.top - PATCH_SIZE, PATCH_SIZE, PATCH_SIZE);
        drawPatch(context, PATCH_SIZE, PATCH_SIZE * 3,                                     /* 1 */
                  page.left, page.top - PATCH_SIZE, page.width(), PATCH_SIZE);
        drawPatch(context, PATCH_SIZE * 2, PATCH_SIZE * 3,                                 /* 2 */
                  page.right, page.top - PATCH_SIZE, PATCH_SIZE, PATCH_SIZE);
        drawPatch(context, 0, PATCH_SIZE * 2,                                              /* 3 */
                  page.left - PATCH_SIZE, page.top, PATCH_SIZE, page.height());
        drawPatch(context, PATCH_SIZE * 2, PATCH_SIZE * 2,                                 /* 4 */
                  page.right, page.top, PATCH_SIZE, page.height());
        drawPatch(context, 0, PATCH_SIZE,                                                  /* 5 */
                  page.left - PATCH_SIZE, page.bottom, PATCH_SIZE, PATCH_SIZE);
        drawPatch(context, PATCH_SIZE, PATCH_SIZE,                                         /* 6 */
                  page.left, page.bottom, page.width(), PATCH_SIZE);
        drawPatch(context, PATCH_SIZE * 2, PATCH_SIZE,                                     /* 7 */
                  page.right, page.bottom, PATCH_SIZE, PATCH_SIZE);
    }

    private void drawPatch(RenderContext context, int textureX, int textureY,
                           float tileX, float tileY, float tileWidth, float tileHeight) {
        RectF viewport = context.viewport;
        float viewportHeight = viewport.height();
        float drawX = tileX - viewport.left;
        float drawY = viewportHeight - (tileY + tileHeight - viewport.top);

        float[] coords = {
            //x, y, z, texture_x, texture_y
            drawX/viewport.width(), drawY/viewport.height(), 0,
            textureX/(float)TEXTURE_SIZE, textureY/(float)TEXTURE_SIZE,

            drawX/viewport.width(), (drawY+tileHeight)/viewport.height(), 0,
            textureX/(float)TEXTURE_SIZE, (textureY+PATCH_SIZE)/(float)TEXTURE_SIZE,

            (drawX+tileWidth)/viewport.width(), drawY/viewport.height(), 0,
            (textureX+PATCH_SIZE)/(float)TEXTURE_SIZE, textureY/(float)TEXTURE_SIZE,

            (drawX+tileWidth)/viewport.width(), (drawY+tileHeight)/viewport.height(), 0,
            (textureX+PATCH_SIZE)/(float)TEXTURE_SIZE, (textureY+PATCH_SIZE)/(float)TEXTURE_SIZE

        };

        // Get the buffer and handles from the context
        FloatBuffer coordBuffer = context.coordBuffer;
        int positionHandle = context.positionHandle;
        int textureHandle = context.textureHandle;

        // Make sure we are at position zero in the buffer in case other draw methods did not clean
        // up after themselves
        coordBuffer.position(0);
        coordBuffer.put(coords);

        // 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.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
                               GLES20.GL_CLAMP_TO_EDGE);
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
                               GLES20.GL_CLAMP_TO_EDGE);

        // Use bilinear filtering for both magnification and minimization of the texture. This
        // applies only to the shadow layer so we do not incur a high overhead.
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
                               GLES20.GL_LINEAR);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
                               GLES20.GL_LINEAR);

        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
    }
}