1/*
2 * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26#ifndef HEADLESS
27
28#include <stdlib.h>
29#include <string.h>
30
31#include "sun_java2d_SunGraphics2D.h"
32
33#include "OGLPaints.h"
34#include "OGLVertexCache.h"
35
36typedef struct _J2DVertex {
37 jfloat tx, ty;
38 jubyte r, g, b, a;
39 jfloat dx, dy;
40} J2DVertex;
41
42static J2DVertex *vertexCache = NULL;
43static jint vertexCacheIndex = 0;
44
45static GLuint maskCacheTexID = 0;
46static jint maskCacheIndex = 0;
47
48#define OGLVC_ADD_VERTEX(TX, TY, R, G, B, A, DX, DY) \
49 do { \
50 J2DVertex *v = &vertexCache[vertexCacheIndex++]; \
51 v->tx = TX; \
52 v->ty = TY; \
53 v->r = R; \
54 v->g = G; \
55 v->b = B; \
56 v->a = A; \
57 v->dx = DX; \
58 v->dy = DY; \
59 } while (0)
60
61#define OGLVC_ADD_QUAD(TX1, TY1, TX2, TY2, DX1, DY1, DX2, DY2, R, G, B, A) \
62 do { \
63 OGLVC_ADD_VERTEX(TX1, TY1, R, G, B, A, DX1, DY1); \
64 OGLVC_ADD_VERTEX(TX2, TY1, R, G, B, A, DX2, DY1); \
65 OGLVC_ADD_VERTEX(TX2, TY2, R, G, B, A, DX2, DY2); \
66 OGLVC_ADD_VERTEX(TX1, TY2, R, G, B, A, DX1, DY2); \
67 } while (0)
68
69jboolean
70OGLVertexCache_InitVertexCache(OGLContext *oglc)
71{
72 J2dTraceLn(J2D_TRACE_INFO, "OGLVertexCache_InitVertexCache");
73
74 if (vertexCache == NULL) {
75 vertexCache = (J2DVertex *)malloc(OGLVC_MAX_INDEX * sizeof(J2DVertex));
76 if (vertexCache == NULL) {
77 return JNI_FALSE;
78 }
79 }
80
81 if (!oglc->vertexCacheEnabled) {
82 j2d_glTexCoordPointer(2, GL_FLOAT,
83 sizeof(J2DVertex), vertexCache);
84 j2d_glColorPointer(4, GL_UNSIGNED_BYTE,
85 sizeof(J2DVertex), ((jfloat *)vertexCache) + 2);
86 j2d_glVertexPointer(2, GL_FLOAT,
87 sizeof(J2DVertex), ((jfloat *)vertexCache) + 3);
88
89 j2d_glEnableClientState(GL_TEXTURE_COORD_ARRAY);
90 j2d_glEnableClientState(GL_COLOR_ARRAY);
91 j2d_glEnableClientState(GL_VERTEX_ARRAY);
92
93 oglc->vertexCacheEnabled = JNI_TRUE;
94 }
95
96 return JNI_TRUE;
97}
98
99void
100OGLVertexCache_FlushVertexCache()
101{
102 J2dTraceLn(J2D_TRACE_INFO, "OGLVertexCache_FlushVertexCache");
103
104 if (vertexCacheIndex > 0) {
105 j2d_glDrawArrays(GL_QUADS, 0, vertexCacheIndex);
106 }
107 vertexCacheIndex = 0;
108}
109
110/**
111 * This method is somewhat hacky, but necessary for the foreseeable future.
112 * The problem is the way OpenGL handles color values in vertex arrays. When
113 * a vertex in a vertex array contains a color, and then the vertex array
114 * is rendered via glDrawArrays(), the global OpenGL color state is actually
115 * modified each time a vertex is rendered. This means that after all
116 * vertices have been flushed, the global OpenGL color state will be set to
117 * the color of the most recently rendered element in the vertex array.
118 *
119 * The reason this is a problem for us is that we do not want to flush the
120 * vertex array (in the case of mask/glyph operations) or issue a glEnd()
121 * (in the case of non-antialiased primitives) everytime the current color
122 * changes, which would defeat any benefit from batching in the first place.
123 * We handle this in practice by not calling CHECK/RESET_PREVIOUS_OP() when
124 * the simple color state is changing in OGLPaints_SetColor(). This is
125 * problematic for vertex caching because we may end up with the following
126 * situation, for example:
127 * SET_COLOR (orange)
128 * MASK_FILL
129 * MASK_FILL
130 * SET_COLOR (blue; remember, this won't cause a flush)
131 * FILL_RECT (this will cause the vertex array to be flushed)
132 *
133 * In this case, we would actually end up rendering an orange FILL_RECT,
134 * not a blue one as intended, because flushing the vertex cache flush would
135 * override the color state from the most recent SET_COLOR call.
136 *
137 * Long story short, the easiest way to resolve this problem is to call
138 * this method just after disabling the mask/glyph cache, which will ensure
139 * that the appropriate color state is restored.
140 */
141void
142OGLVertexCache_RestoreColorState(OGLContext *oglc)
143{
144 if (oglc->paintState == sun_java2d_SunGraphics2D_PAINT_ALPHACOLOR) {
145 OGLPaints_SetColor(oglc, oglc->pixel);
146 }
147}
148
149static jboolean
150OGLVertexCache_InitMaskCache()
151{
152 J2dTraceLn(J2D_TRACE_INFO, "OGLVertexCache_InitMaskCache");
153
154 maskCacheTexID =
155 OGLContext_CreateBlitTexture(GL_INTENSITY8, GL_LUMINANCE,
156 OGLVC_MASK_CACHE_WIDTH_IN_TEXELS,
157 OGLVC_MASK_CACHE_HEIGHT_IN_TEXELS);
158
159 // init special fully opaque tile in the upper-right corner of
160 // the mask cache texture
161 {
162 GLubyte allOnes[OGLVC_MASK_CACHE_TILE_SIZE];
163 memset(allOnes, 0xff, OGLVC_MASK_CACHE_TILE_SIZE);
164 j2d_glTexSubImage2D(GL_TEXTURE_2D, 0,
165 OGLVC_MASK_CACHE_SPECIAL_TILE_X,
166 OGLVC_MASK_CACHE_SPECIAL_TILE_Y,
167 OGLVC_MASK_CACHE_TILE_WIDTH,
168 OGLVC_MASK_CACHE_TILE_HEIGHT,
169 GL_LUMINANCE, GL_UNSIGNED_BYTE, allOnes);
170 }
171
172 return JNI_TRUE;
173}
174
175void
176OGLVertexCache_EnableMaskCache(OGLContext *oglc)
177{
178 J2dTraceLn(J2D_TRACE_INFO, "OGLVertexCache_EnableMaskCache");
179
180 if (!OGLVertexCache_InitVertexCache(oglc)) {
181 return;
182 }
183
184 if (maskCacheTexID == 0) {
185 if (!OGLVertexCache_InitMaskCache()) {
186 return;
187 }
188 }
189
190 j2d_glEnable(GL_TEXTURE_2D);
191 j2d_glBindTexture(GL_TEXTURE_2D, maskCacheTexID);
192 OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE);
193 j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
194}
195
196void
197OGLVertexCache_DisableMaskCache(OGLContext *oglc)
198{
199 J2dTraceLn(J2D_TRACE_INFO, "OGLVertexCache_DisableMaskCache");
200
201 OGLVertexCache_FlushVertexCache();
202 OGLVertexCache_RestoreColorState(oglc);
203
204 j2d_glDisable(GL_TEXTURE_2D);
205 j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
206 j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
207 j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
208 j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
209
210 maskCacheIndex = 0;
211}
212
213void
214OGLVertexCache_AddMaskQuad(OGLContext *oglc,
215 jint srcx, jint srcy,
216 jint dstx, jint dsty,
217 jint width, jint height,
218 jint maskscan, void *mask)
219{
220 jfloat tx1, ty1, tx2, ty2;
221 jfloat dx1, dy1, dx2, dy2;
222
223 J2dTraceLn1(J2D_TRACE_INFO, "OGLVertexCache_AddMaskQuad: %d",
224 maskCacheIndex);
225
226 if (maskCacheIndex >= OGLVC_MASK_CACHE_MAX_INDEX ||
227 vertexCacheIndex >= OGLVC_MAX_INDEX)
228 {
229 OGLVertexCache_FlushVertexCache();
230 maskCacheIndex = 0;
231 }
232
233 if (mask != NULL) {
234 jint texx = OGLVC_MASK_CACHE_TILE_WIDTH *
235 (maskCacheIndex % OGLVC_MASK_CACHE_WIDTH_IN_TILES);
236 jint texy = OGLVC_MASK_CACHE_TILE_HEIGHT *
237 (maskCacheIndex / OGLVC_MASK_CACHE_WIDTH_IN_TILES);
238
239 // update the source pointer offsets
240 j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, srcx);
241 j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, srcy);
242 j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, maskscan);
243
244 // copy alpha mask into texture tile
245 j2d_glTexSubImage2D(GL_TEXTURE_2D, 0,
246 texx, texy, width, height,
247 GL_LUMINANCE, GL_UNSIGNED_BYTE, mask);
248
249 tx1 = ((jfloat)texx) / OGLVC_MASK_CACHE_WIDTH_IN_TEXELS;
250 ty1 = ((jfloat)texy) / OGLVC_MASK_CACHE_HEIGHT_IN_TEXELS;
251
252 maskCacheIndex++;
253 } else {
254 // use special fully opaque tile
255 tx1 = ((jfloat)OGLVC_MASK_CACHE_SPECIAL_TILE_X) /
256 OGLVC_MASK_CACHE_WIDTH_IN_TEXELS;
257 ty1 = ((jfloat)OGLVC_MASK_CACHE_SPECIAL_TILE_Y) /
258 OGLVC_MASK_CACHE_HEIGHT_IN_TEXELS;
259 }
260
261 tx2 = tx1 + (((jfloat)width) / OGLVC_MASK_CACHE_WIDTH_IN_TEXELS);
262 ty2 = ty1 + (((jfloat)height) / OGLVC_MASK_CACHE_HEIGHT_IN_TEXELS);
263
264 dx1 = (jfloat)dstx;
265 dy1 = (jfloat)dsty;
266 dx2 = dx1 + width;
267 dy2 = dy1 + height;
268
269 OGLVC_ADD_QUAD(tx1, ty1, tx2, ty2,
270 dx1, dy1, dx2, dy2,
271 oglc->r, oglc->g, oglc->b, oglc->a);
272}
273
274void
275OGLVertexCache_AddGlyphQuad(OGLContext *oglc,
276 jfloat tx1, jfloat ty1, jfloat tx2, jfloat ty2,
277 jfloat dx1, jfloat dy1, jfloat dx2, jfloat dy2)
278{
279 J2dTraceLn(J2D_TRACE_INFO, "OGLVertexCache_AddGlyphQuad");
280
281 if (vertexCacheIndex >= OGLVC_MAX_INDEX) {
282 OGLVertexCache_FlushVertexCache();
283 }
284
285 OGLVC_ADD_QUAD(tx1, ty1, tx2, ty2,
286 dx1, dy1, dx2, dy2,
287 oglc->r, oglc->g, oglc->b, oglc->a);
288}
289
290#endif /* !HEADLESS */
291