src/java.desktop/share/native/common/java2d/opengl/OGLRenderQueue.c
changeset 47216 71c04702a3d5
parent 26751 70bac69b37c9
child 56230 489867818774
child 58324 0aba35254e00
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.desktop/share/native/common/java2d/opengl/OGLRenderQueue.c	Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,861 @@
+/*
+ * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef HEADLESS
+
+#include <stdlib.h>
+
+#include "sun_java2d_pipe_BufferedOpCodes.h"
+
+#include "jlong.h"
+#include "OGLBlitLoops.h"
+#include "OGLBufImgOps.h"
+#include "OGLContext.h"
+#include "OGLMaskBlit.h"
+#include "OGLMaskFill.h"
+#include "OGLPaints.h"
+#include "OGLRenderQueue.h"
+#include "OGLRenderer.h"
+#include "OGLSurfaceData.h"
+#include "OGLTextRenderer.h"
+#include "OGLVertexCache.h"
+
+/**
+ * Used to track whether we are in a series of a simple primitive operations
+ * or texturing operations.  This variable should be controlled only via
+ * the INIT/CHECK/RESET_PREVIOUS_OP() macros.  See the
+ * OGLRenderQueue_CheckPreviousOp() method below for more information.
+ */
+jint previousOp;
+
+/**
+ * References to the "current" context and destination surface.
+ */
+static OGLContext *oglc = NULL;
+static OGLSDOps *dstOps = NULL;
+
+/**
+ * The following methods are implemented in the windowing system (i.e. GLX
+ * and WGL) source files.
+ */
+extern OGLContext *OGLSD_SetScratchSurface(JNIEnv *env, jlong pConfigInfo);
+extern void OGLGC_DestroyOGLGraphicsConfig(jlong pConfigInfo);
+extern void OGLSD_SwapBuffers(JNIEnv *env, jlong window);
+extern void OGLSD_Flush(JNIEnv *env);
+
+JNIEXPORT void JNICALL
+Java_sun_java2d_opengl_OGLRenderQueue_flushBuffer
+    (JNIEnv *env, jobject oglrq,
+     jlong buf, jint limit)
+{
+    jboolean sync = JNI_FALSE;
+    unsigned char *b, *end;
+
+    J2dTraceLn1(J2D_TRACE_INFO,
+                "OGLRenderQueue_flushBuffer: limit=%d", limit);
+
+    b = (unsigned char *)jlong_to_ptr(buf);
+    if (b == NULL) {
+        J2dRlsTraceLn(J2D_TRACE_ERROR,
+            "OGLRenderQueue_flushBuffer: cannot get direct buffer address");
+        return;
+    }
+
+    INIT_PREVIOUS_OP();
+    end = b + limit;
+
+    while (b < end) {
+        jint opcode = NEXT_INT(b);
+
+        J2dTraceLn2(J2D_TRACE_VERBOSE,
+                    "OGLRenderQueue_flushBuffer: opcode=%d, rem=%d",
+                    opcode, (end-b));
+
+        switch (opcode) {
+
+        // draw ops
+        case sun_java2d_pipe_BufferedOpCodes_DRAW_LINE:
+            {
+                jint x1 = NEXT_INT(b);
+                jint y1 = NEXT_INT(b);
+                jint x2 = NEXT_INT(b);
+                jint y2 = NEXT_INT(b);
+                OGLRenderer_DrawLine(oglc, x1, y1, x2, y2);
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_DRAW_RECT:
+            {
+                jint x = NEXT_INT(b);
+                jint y = NEXT_INT(b);
+                jint w = NEXT_INT(b);
+                jint h = NEXT_INT(b);
+                OGLRenderer_DrawRect(oglc, x, y, w, h);
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_DRAW_POLY:
+            {
+                jint nPoints      = NEXT_INT(b);
+                jboolean isClosed = NEXT_BOOLEAN(b);
+                jint transX       = NEXT_INT(b);
+                jint transY       = NEXT_INT(b);
+                jint *xPoints = (jint *)b;
+                jint *yPoints = ((jint *)b) + nPoints;
+                OGLRenderer_DrawPoly(oglc, nPoints, isClosed,
+                                     transX, transY,
+                                     xPoints, yPoints);
+                SKIP_BYTES(b, nPoints * BYTES_PER_POLY_POINT);
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_DRAW_PIXEL:
+            {
+                jint x = NEXT_INT(b);
+                jint y = NEXT_INT(b);
+                // Note that we could use GL_POINTS here, but the common
+                // use case for DRAW_PIXEL is when rendering a Path2D,
+                // which will consist of a mix of DRAW_PIXEL and DRAW_LINE
+                // calls.  So to improve batching we use GL_LINES here,
+                // even though it requires an extra vertex per pixel.
+                CONTINUE_IF_NULL(oglc);
+                CHECK_PREVIOUS_OP(GL_LINES);
+                j2d_glVertex2i(x, y);
+                j2d_glVertex2i(x+1, y+1);
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_DRAW_SCANLINES:
+            {
+                jint count = NEXT_INT(b);
+                OGLRenderer_DrawScanlines(oglc, count, (jint *)b);
+                SKIP_BYTES(b, count * BYTES_PER_SCANLINE);
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_DRAW_PARALLELOGRAM:
+            {
+                jfloat x11 = NEXT_FLOAT(b);
+                jfloat y11 = NEXT_FLOAT(b);
+                jfloat dx21 = NEXT_FLOAT(b);
+                jfloat dy21 = NEXT_FLOAT(b);
+                jfloat dx12 = NEXT_FLOAT(b);
+                jfloat dy12 = NEXT_FLOAT(b);
+                jfloat lwr21 = NEXT_FLOAT(b);
+                jfloat lwr12 = NEXT_FLOAT(b);
+                OGLRenderer_DrawParallelogram(oglc,
+                                              x11, y11,
+                                              dx21, dy21,
+                                              dx12, dy12,
+                                              lwr21, lwr12);
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_DRAW_AAPARALLELOGRAM:
+            {
+                jfloat x11 = NEXT_FLOAT(b);
+                jfloat y11 = NEXT_FLOAT(b);
+                jfloat dx21 = NEXT_FLOAT(b);
+                jfloat dy21 = NEXT_FLOAT(b);
+                jfloat dx12 = NEXT_FLOAT(b);
+                jfloat dy12 = NEXT_FLOAT(b);
+                jfloat lwr21 = NEXT_FLOAT(b);
+                jfloat lwr12 = NEXT_FLOAT(b);
+                OGLRenderer_DrawAAParallelogram(oglc, dstOps,
+                                                x11, y11,
+                                                dx21, dy21,
+                                                dx12, dy12,
+                                                lwr21, lwr12);
+            }
+            break;
+
+        // fill ops
+        case sun_java2d_pipe_BufferedOpCodes_FILL_RECT:
+            {
+                jint x = NEXT_INT(b);
+                jint y = NEXT_INT(b);
+                jint w = NEXT_INT(b);
+                jint h = NEXT_INT(b);
+                OGLRenderer_FillRect(oglc, x, y, w, h);
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_FILL_SPANS:
+            {
+                jint count = NEXT_INT(b);
+                OGLRenderer_FillSpans(oglc, count, (jint *)b);
+                SKIP_BYTES(b, count * BYTES_PER_SPAN);
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_FILL_PARALLELOGRAM:
+            {
+                jfloat x11 = NEXT_FLOAT(b);
+                jfloat y11 = NEXT_FLOAT(b);
+                jfloat dx21 = NEXT_FLOAT(b);
+                jfloat dy21 = NEXT_FLOAT(b);
+                jfloat dx12 = NEXT_FLOAT(b);
+                jfloat dy12 = NEXT_FLOAT(b);
+                OGLRenderer_FillParallelogram(oglc,
+                                              x11, y11,
+                                              dx21, dy21,
+                                              dx12, dy12);
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_FILL_AAPARALLELOGRAM:
+            {
+                jfloat x11 = NEXT_FLOAT(b);
+                jfloat y11 = NEXT_FLOAT(b);
+                jfloat dx21 = NEXT_FLOAT(b);
+                jfloat dy21 = NEXT_FLOAT(b);
+                jfloat dx12 = NEXT_FLOAT(b);
+                jfloat dy12 = NEXT_FLOAT(b);
+                OGLRenderer_FillAAParallelogram(oglc, dstOps,
+                                                x11, y11,
+                                                dx21, dy21,
+                                                dx12, dy12);
+            }
+            break;
+
+        // text-related ops
+        case sun_java2d_pipe_BufferedOpCodes_DRAW_GLYPH_LIST:
+            {
+                jint numGlyphs        = NEXT_INT(b);
+                jint packedParams     = NEXT_INT(b);
+                jfloat glyphListOrigX = NEXT_FLOAT(b);
+                jfloat glyphListOrigY = NEXT_FLOAT(b);
+                jboolean usePositions = EXTRACT_BOOLEAN(packedParams,
+                                                        OFFSET_POSITIONS);
+                jboolean subPixPos    = EXTRACT_BOOLEAN(packedParams,
+                                                        OFFSET_SUBPIXPOS);
+                jboolean rgbOrder     = EXTRACT_BOOLEAN(packedParams,
+                                                        OFFSET_RGBORDER);
+                jint lcdContrast      = EXTRACT_BYTE(packedParams,
+                                                     OFFSET_CONTRAST);
+                unsigned char *images = b;
+                unsigned char *positions;
+                jint bytesPerGlyph;
+                if (usePositions) {
+                    positions = (b + numGlyphs * BYTES_PER_GLYPH_IMAGE);
+                    bytesPerGlyph = BYTES_PER_POSITIONED_GLYPH;
+                } else {
+                    positions = NULL;
+                    bytesPerGlyph = BYTES_PER_GLYPH_IMAGE;
+                }
+                OGLTR_DrawGlyphList(env, oglc, dstOps,
+                                    numGlyphs, usePositions,
+                                    subPixPos, rgbOrder, lcdContrast,
+                                    glyphListOrigX, glyphListOrigY,
+                                    images, positions);
+                SKIP_BYTES(b, numGlyphs * bytesPerGlyph);
+            }
+            break;
+
+        // copy-related ops
+        case sun_java2d_pipe_BufferedOpCodes_COPY_AREA:
+            {
+                jint x  = NEXT_INT(b);
+                jint y  = NEXT_INT(b);
+                jint w  = NEXT_INT(b);
+                jint h  = NEXT_INT(b);
+                jint dx = NEXT_INT(b);
+                jint dy = NEXT_INT(b);
+                OGLBlitLoops_CopyArea(env, oglc, dstOps,
+                                      x, y, w, h, dx, dy);
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_BLIT:
+            {
+                jint packedParams = NEXT_INT(b);
+                jint sx1          = NEXT_INT(b);
+                jint sy1          = NEXT_INT(b);
+                jint sx2          = NEXT_INT(b);
+                jint sy2          = NEXT_INT(b);
+                jdouble dx1       = NEXT_DOUBLE(b);
+                jdouble dy1       = NEXT_DOUBLE(b);
+                jdouble dx2       = NEXT_DOUBLE(b);
+                jdouble dy2       = NEXT_DOUBLE(b);
+                jlong pSrc        = NEXT_LONG(b);
+                jlong pDst        = NEXT_LONG(b);
+                jint hint         = EXTRACT_BYTE(packedParams, OFFSET_HINT);
+                jboolean texture  = EXTRACT_BOOLEAN(packedParams,
+                                                    OFFSET_TEXTURE);
+                jboolean rtt      = EXTRACT_BOOLEAN(packedParams,
+                                                    OFFSET_RTT);
+                jboolean xform    = EXTRACT_BOOLEAN(packedParams,
+                                                    OFFSET_XFORM);
+                jboolean isoblit  = EXTRACT_BOOLEAN(packedParams,
+                                                    OFFSET_ISOBLIT);
+                if (isoblit) {
+                    OGLBlitLoops_IsoBlit(env, oglc, pSrc, pDst,
+                                         xform, hint, texture, rtt,
+                                         sx1, sy1, sx2, sy2,
+                                         dx1, dy1, dx2, dy2);
+                } else {
+                    jint srctype = EXTRACT_BYTE(packedParams, OFFSET_SRCTYPE);
+                    OGLBlitLoops_Blit(env, oglc, pSrc, pDst,
+                                      xform, hint, srctype, texture,
+                                      sx1, sy1, sx2, sy2,
+                                      dx1, dy1, dx2, dy2);
+                }
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_SURFACE_TO_SW_BLIT:
+            {
+                jint sx      = NEXT_INT(b);
+                jint sy      = NEXT_INT(b);
+                jint dx      = NEXT_INT(b);
+                jint dy      = NEXT_INT(b);
+                jint w       = NEXT_INT(b);
+                jint h       = NEXT_INT(b);
+                jint dsttype = NEXT_INT(b);
+                jlong pSrc   = NEXT_LONG(b);
+                jlong pDst   = NEXT_LONG(b);
+                OGLBlitLoops_SurfaceToSwBlit(env, oglc,
+                                             pSrc, pDst, dsttype,
+                                             sx, sy, dx, dy, w, h);
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_MASK_FILL:
+            {
+                jint x        = NEXT_INT(b);
+                jint y        = NEXT_INT(b);
+                jint w        = NEXT_INT(b);
+                jint h        = NEXT_INT(b);
+                jint maskoff  = NEXT_INT(b);
+                jint maskscan = NEXT_INT(b);
+                jint masklen  = NEXT_INT(b);
+                unsigned char *pMask = (masklen > 0) ? b : NULL;
+                OGLMaskFill_MaskFill(oglc, x, y, w, h,
+                                     maskoff, maskscan, masklen, pMask);
+                SKIP_BYTES(b, masklen);
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_MASK_BLIT:
+            {
+                jint dstx     = NEXT_INT(b);
+                jint dsty     = NEXT_INT(b);
+                jint width    = NEXT_INT(b);
+                jint height   = NEXT_INT(b);
+                jint masklen  = width * height * sizeof(jint);
+                OGLMaskBlit_MaskBlit(env, oglc,
+                                     dstx, dsty, width, height, b);
+                SKIP_BYTES(b, masklen);
+            }
+            break;
+
+        // state-related ops
+        case sun_java2d_pipe_BufferedOpCodes_SET_RECT_CLIP:
+            {
+                jint x1 = NEXT_INT(b);
+                jint y1 = NEXT_INT(b);
+                jint x2 = NEXT_INT(b);
+                jint y2 = NEXT_INT(b);
+                OGLContext_SetRectClip(oglc, dstOps, x1, y1, x2, y2);
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_BEGIN_SHAPE_CLIP:
+            {
+                OGLContext_BeginShapeClip(oglc);
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_SET_SHAPE_CLIP_SPANS:
+            {
+                jint count = NEXT_INT(b);
+                OGLRenderer_FillSpans(oglc, count, (jint *)b);
+                SKIP_BYTES(b, count * BYTES_PER_SPAN);
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_END_SHAPE_CLIP:
+            {
+                OGLContext_EndShapeClip(oglc, dstOps);
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_RESET_CLIP:
+            {
+                OGLContext_ResetClip(oglc);
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_SET_ALPHA_COMPOSITE:
+            {
+                jint rule         = NEXT_INT(b);
+                jfloat extraAlpha = NEXT_FLOAT(b);
+                jint flags        = NEXT_INT(b);
+                OGLContext_SetAlphaComposite(oglc, rule, extraAlpha, flags);
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_SET_XOR_COMPOSITE:
+            {
+                jint xorPixel = NEXT_INT(b);
+                OGLContext_SetXorComposite(oglc, xorPixel);
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_RESET_COMPOSITE:
+            {
+                OGLContext_ResetComposite(oglc);
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_SET_TRANSFORM:
+            {
+                jdouble m00 = NEXT_DOUBLE(b);
+                jdouble m10 = NEXT_DOUBLE(b);
+                jdouble m01 = NEXT_DOUBLE(b);
+                jdouble m11 = NEXT_DOUBLE(b);
+                jdouble m02 = NEXT_DOUBLE(b);
+                jdouble m12 = NEXT_DOUBLE(b);
+                OGLContext_SetTransform(oglc, m00, m10, m01, m11, m02, m12);
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_RESET_TRANSFORM:
+            {
+                OGLContext_ResetTransform(oglc);
+            }
+            break;
+
+        // context-related ops
+        case sun_java2d_pipe_BufferedOpCodes_SET_SURFACES:
+            {
+                jlong pSrc = NEXT_LONG(b);
+                jlong pDst = NEXT_LONG(b);
+                if (oglc != NULL) {
+                    RESET_PREVIOUS_OP();
+                }
+                oglc = OGLContext_SetSurfaces(env, pSrc, pDst);
+                dstOps = (OGLSDOps *)jlong_to_ptr(pDst);
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_SET_SCRATCH_SURFACE:
+            {
+                jlong pConfigInfo = NEXT_LONG(b);
+                if (oglc != NULL) {
+                    RESET_PREVIOUS_OP();
+                }
+                oglc = OGLSD_SetScratchSurface(env, pConfigInfo);
+                dstOps = NULL;
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_FLUSH_SURFACE:
+            {
+                jlong pData = NEXT_LONG(b);
+                OGLSDOps *oglsdo = (OGLSDOps *)jlong_to_ptr(pData);
+                if (oglsdo != NULL) {
+                    CONTINUE_IF_NULL(oglc);
+                    RESET_PREVIOUS_OP();
+                    OGLSD_Delete(env, oglsdo);
+                }
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_DISPOSE_SURFACE:
+            {
+                jlong pData = NEXT_LONG(b);
+                OGLSDOps *oglsdo = (OGLSDOps *)jlong_to_ptr(pData);
+                if (oglsdo != NULL) {
+                    CONTINUE_IF_NULL(oglc);
+                    RESET_PREVIOUS_OP();
+                    OGLSD_Delete(env, oglsdo);
+                    if (oglsdo->privOps != NULL) {
+                        free(oglsdo->privOps);
+                    }
+                }
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_DISPOSE_CONFIG:
+            {
+                jlong pConfigInfo = NEXT_LONG(b);
+                CONTINUE_IF_NULL(oglc);
+                RESET_PREVIOUS_OP();
+                OGLGC_DestroyOGLGraphicsConfig(pConfigInfo);
+
+                // the previous method will call glX/wglMakeCurrent(None),
+                // so we should nullify the current oglc and dstOps to avoid
+                // calling glFlush() (or similar) while no context is current
+                oglc = NULL;
+                dstOps = NULL;
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_INVALIDATE_CONTEXT:
+            {
+                // flush just in case there are any pending operations in
+                // the hardware pipe
+                if (oglc != NULL) {
+                    RESET_PREVIOUS_OP();
+                    j2d_glFlush();
+                }
+
+                // invalidate the references to the current context and
+                // destination surface that are maintained at the native level
+                oglc = NULL;
+                dstOps = NULL;
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_SAVE_STATE:
+            {
+                j2d_glPushAttrib(GL_ALL_ATTRIB_BITS);
+                j2d_glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
+                j2d_glMatrixMode(GL_MODELVIEW);
+                j2d_glPushMatrix();
+                j2d_glMatrixMode(GL_PROJECTION);
+                j2d_glPushMatrix();
+                j2d_glMatrixMode(GL_TEXTURE);
+                j2d_glPushMatrix();
+            }
+            break;
+
+        case sun_java2d_pipe_BufferedOpCodes_RESTORE_STATE:
+            {
+                j2d_glPopAttrib();
+                j2d_glPopClientAttrib();
+                j2d_glMatrixMode(GL_MODELVIEW);
+                j2d_glPopMatrix();
+                j2d_glMatrixMode(GL_PROJECTION);
+                j2d_glPopMatrix();
+                j2d_glMatrixMode(GL_TEXTURE);
+                j2d_glPopMatrix();
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_SYNC:
+            {
+                sync = JNI_TRUE;
+            }
+            break;
+
+        // multibuffering ops
+        case sun_java2d_pipe_BufferedOpCodes_SWAP_BUFFERS:
+            {
+                jlong window = NEXT_LONG(b);
+                if (oglc != NULL) {
+                    RESET_PREVIOUS_OP();
+                }
+                OGLSD_SwapBuffers(env, window);
+            }
+            break;
+
+        // special no-op (mainly used for achieving 8-byte alignment)
+        case sun_java2d_pipe_BufferedOpCodes_NOOP:
+            break;
+
+        // paint-related ops
+        case sun_java2d_pipe_BufferedOpCodes_RESET_PAINT:
+            {
+                OGLPaints_ResetPaint(oglc);
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_SET_COLOR:
+            {
+                jint pixel = NEXT_INT(b);
+                OGLPaints_SetColor(oglc, pixel);
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_SET_GRADIENT_PAINT:
+            {
+                jboolean useMask= NEXT_BOOLEAN(b);
+                jboolean cyclic = NEXT_BOOLEAN(b);
+                jdouble p0      = NEXT_DOUBLE(b);
+                jdouble p1      = NEXT_DOUBLE(b);
+                jdouble p3      = NEXT_DOUBLE(b);
+                jint pixel1     = NEXT_INT(b);
+                jint pixel2     = NEXT_INT(b);
+                OGLPaints_SetGradientPaint(oglc, useMask, cyclic,
+                                           p0, p1, p3,
+                                           pixel1, pixel2);
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_SET_LINEAR_GRADIENT_PAINT:
+            {
+                jboolean useMask = NEXT_BOOLEAN(b);
+                jboolean linear  = NEXT_BOOLEAN(b);
+                jint cycleMethod = NEXT_INT(b);
+                jint numStops    = NEXT_INT(b);
+                jfloat p0        = NEXT_FLOAT(b);
+                jfloat p1        = NEXT_FLOAT(b);
+                jfloat p3        = NEXT_FLOAT(b);
+                void *fractions, *pixels;
+                fractions = b; SKIP_BYTES(b, numStops * sizeof(jfloat));
+                pixels    = b; SKIP_BYTES(b, numStops * sizeof(jint));
+                OGLPaints_SetLinearGradientPaint(oglc, dstOps,
+                                                 useMask, linear,
+                                                 cycleMethod, numStops,
+                                                 p0, p1, p3,
+                                                 fractions, pixels);
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_SET_RADIAL_GRADIENT_PAINT:
+            {
+                jboolean useMask = NEXT_BOOLEAN(b);
+                jboolean linear  = NEXT_BOOLEAN(b);
+                jint numStops    = NEXT_INT(b);
+                jint cycleMethod = NEXT_INT(b);
+                jfloat m00       = NEXT_FLOAT(b);
+                jfloat m01       = NEXT_FLOAT(b);
+                jfloat m02       = NEXT_FLOAT(b);
+                jfloat m10       = NEXT_FLOAT(b);
+                jfloat m11       = NEXT_FLOAT(b);
+                jfloat m12       = NEXT_FLOAT(b);
+                jfloat focusX    = NEXT_FLOAT(b);
+                void *fractions, *pixels;
+                fractions = b; SKIP_BYTES(b, numStops * sizeof(jfloat));
+                pixels    = b; SKIP_BYTES(b, numStops * sizeof(jint));
+                OGLPaints_SetRadialGradientPaint(oglc, dstOps,
+                                                 useMask, linear,
+                                                 cycleMethod, numStops,
+                                                 m00, m01, m02,
+                                                 m10, m11, m12,
+                                                 focusX,
+                                                 fractions, pixels);
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_SET_TEXTURE_PAINT:
+            {
+                jboolean useMask= NEXT_BOOLEAN(b);
+                jboolean filter = NEXT_BOOLEAN(b);
+                jlong pSrc      = NEXT_LONG(b);
+                jdouble xp0     = NEXT_DOUBLE(b);
+                jdouble xp1     = NEXT_DOUBLE(b);
+                jdouble xp3     = NEXT_DOUBLE(b);
+                jdouble yp0     = NEXT_DOUBLE(b);
+                jdouble yp1     = NEXT_DOUBLE(b);
+                jdouble yp3     = NEXT_DOUBLE(b);
+                OGLPaints_SetTexturePaint(oglc, useMask, pSrc, filter,
+                                          xp0, xp1, xp3,
+                                          yp0, yp1, yp3);
+            }
+            break;
+
+        // BufferedImageOp-related ops
+        case sun_java2d_pipe_BufferedOpCodes_ENABLE_CONVOLVE_OP:
+            {
+                jlong pSrc        = NEXT_LONG(b);
+                jboolean edgeZero = NEXT_BOOLEAN(b);
+                jint kernelWidth  = NEXT_INT(b);
+                jint kernelHeight = NEXT_INT(b);
+                OGLBufImgOps_EnableConvolveOp(oglc, pSrc, edgeZero,
+                                              kernelWidth, kernelHeight, b);
+                SKIP_BYTES(b, kernelWidth * kernelHeight * sizeof(jfloat));
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_DISABLE_CONVOLVE_OP:
+            {
+                OGLBufImgOps_DisableConvolveOp(oglc);
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_ENABLE_RESCALE_OP:
+            {
+                jlong pSrc          = NEXT_LONG(b);
+                jboolean nonPremult = NEXT_BOOLEAN(b);
+                jint numFactors     = 4;
+                unsigned char *scaleFactors = b;
+                unsigned char *offsets = (b + numFactors * sizeof(jfloat));
+                OGLBufImgOps_EnableRescaleOp(oglc, pSrc, nonPremult,
+                                             scaleFactors, offsets);
+                SKIP_BYTES(b, numFactors * sizeof(jfloat) * 2);
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_DISABLE_RESCALE_OP:
+            {
+                OGLBufImgOps_DisableRescaleOp(oglc);
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_ENABLE_LOOKUP_OP:
+            {
+                jlong pSrc          = NEXT_LONG(b);
+                jboolean nonPremult = NEXT_BOOLEAN(b);
+                jboolean shortData  = NEXT_BOOLEAN(b);
+                jint numBands       = NEXT_INT(b);
+                jint bandLength     = NEXT_INT(b);
+                jint offset         = NEXT_INT(b);
+                jint bytesPerElem = shortData ? sizeof(jshort):sizeof(jbyte);
+                void *tableValues = b;
+                OGLBufImgOps_EnableLookupOp(oglc, pSrc, nonPremult, shortData,
+                                            numBands, bandLength, offset,
+                                            tableValues);
+                SKIP_BYTES(b, numBands * bandLength * bytesPerElem);
+            }
+            break;
+        case sun_java2d_pipe_BufferedOpCodes_DISABLE_LOOKUP_OP:
+            {
+                OGLBufImgOps_DisableLookupOp(oglc);
+            }
+            break;
+
+        default:
+            J2dRlsTraceLn1(J2D_TRACE_ERROR,
+                "OGLRenderQueue_flushBuffer: invalid opcode=%d", opcode);
+            if (oglc != NULL) {
+                RESET_PREVIOUS_OP();
+            }
+            return;
+        }
+    }
+
+    if (oglc != NULL) {
+        RESET_PREVIOUS_OP();
+        if (sync) {
+            j2d_glFinish();
+        } else {
+            j2d_glFlush();
+        }
+        OGLSD_Flush(env);
+    }
+}
+
+/**
+ * Returns a pointer to the "current" context, as set by the last SET_SURFACES
+ * or SET_SCRATCH_SURFACE operation.
+ */
+OGLContext *
+OGLRenderQueue_GetCurrentContext()
+{
+    return oglc;
+}
+
+/**
+ * Returns a pointer to the "current" destination surface, as set by the last
+ * SET_SURFACES operation.
+ */
+OGLSDOps *
+OGLRenderQueue_GetCurrentDestination()
+{
+    return dstOps;
+}
+
+/**
+ * Used to track whether we are within a series of simple primitive operations
+ * or texturing operations.  The op parameter determines the nature of the
+ * operation that is to follow.  Valid values for this op parameter are:
+ *
+ *     GL_QUADS
+ *     GL_LINES
+ *     GL_LINE_LOOP
+ *     GL_LINE_STRIP
+ *     (basically any of the valid parameters for glBegin())
+ *
+ *     GL_TEXTURE_2D
+ *     GL_TEXTURE_RECTANGLE_ARB
+ *
+ *     OGL_STATE_RESET
+ *     OGL_STATE_CHANGE
+ *     OGL_STATE_MASK_OP
+ *     OGL_STATE_GLYPH_OP
+ *
+ * Note that the above constants are guaranteed to be unique values.  The
+ * last few are defined to be negative values to differentiate them from
+ * the core GL* constants, which are defined to be non-negative.
+ *
+ * For simple primitives, this method allows us to batch similar primitives
+ * within the same glBegin()/glEnd() pair.  For example, if we have 100
+ * consecutive FILL_RECT operations, we only have to call glBegin(GL_QUADS)
+ * for the first op, and then subsequent operations will consist only of
+ * glVertex*() calls, which helps improve performance.  The glEnd() call
+ * only needs to be issued before an operation that cannot happen within a
+ * glBegin()/glEnd() pair (e.g. updating the clip), or one that requires a
+ * different primitive mode (e.g. GL_LINES).
+ *
+ * For operations that involve texturing, this method helps us to avoid
+ * calling glEnable(GL_TEXTURE_2D) and glDisable(GL_TEXTURE_2D) around each
+ * operation.  For example, if we have an alternating series of ISO_BLIT
+ * and MASK_BLIT operations (both of which involve texturing), we need
+ * only to call glEnable(GL_TEXTURE_2D) before the first ISO_BLIT operation.
+ * The glDisable(GL_TEXTURE_2D) call only needs to be issued before an
+ * operation that cannot (or should not) happen while texturing is enabled
+ * (e.g. a context change, or a simple primitive operation like GL_QUADS).
+ */
+void
+OGLRenderQueue_CheckPreviousOp(jint op)
+{
+    if (previousOp == op) {
+        // The op is the same as last time, so we can return immediately.
+        return;
+    }
+
+    J2dTraceLn1(J2D_TRACE_VERBOSE,
+                "OGLRenderQueue_CheckPreviousOp: new op=%d", op);
+
+    switch (previousOp) {
+    case GL_TEXTURE_2D:
+    case GL_TEXTURE_RECTANGLE_ARB:
+        if (op == OGL_STATE_CHANGE) {
+            // Optimization: Certain state changes (those marked as
+            // OGL_STATE_CHANGE) are allowed while texturing is enabled.
+            // In this case, we can allow previousOp to remain as it is and
+            // then return early.
+            return;
+        } else {
+            // Otherwise, op must be a primitive operation, or a reset, so
+            // we will disable texturing.
+            j2d_glDisable(previousOp);
+            // This next step of binding to zero should not be strictly
+            // necessary, but on some older Nvidia boards (e.g. GeForce 2)
+            // problems will arise if GL_TEXTURE_2D and
+            // GL_TEXTURE_RECTANGLE_ARB are bound at the same time, so we
+            // will do this just to be safe.
+            j2d_glBindTexture(previousOp, 0);
+        }
+        break;
+    case OGL_STATE_MASK_OP:
+        OGLVertexCache_DisableMaskCache(oglc);
+        break;
+    case OGL_STATE_GLYPH_OP:
+        OGLTR_DisableGlyphVertexCache(oglc);
+        break;
+    case OGL_STATE_PGRAM_OP:
+        OGLRenderer_DisableAAParallelogramProgram();
+        break;
+    case OGL_STATE_RESET:
+    case OGL_STATE_CHANGE:
+        // No-op
+        break;
+    default:
+        // In this case, op must be one of:
+        //     - the start of a different primitive type (glBegin())
+        //     - a texturing operation
+        //     - a state change (not allowed within glBegin()/glEnd() pairs)
+        //     - a reset
+        // so we must first complete the previous primitive operation.
+        j2d_glEnd();
+        break;
+    }
+
+    switch (op) {
+    case GL_TEXTURE_2D:
+    case GL_TEXTURE_RECTANGLE_ARB:
+        // We are starting a texturing operation, so enable texturing.
+        j2d_glEnable(op);
+        break;
+    case OGL_STATE_MASK_OP:
+        OGLVertexCache_EnableMaskCache(oglc);
+        break;
+    case OGL_STATE_GLYPH_OP:
+        OGLTR_EnableGlyphVertexCache(oglc);
+        break;
+    case OGL_STATE_PGRAM_OP:
+        OGLRenderer_EnableAAParallelogramProgram();
+        break;
+    case OGL_STATE_RESET:
+    case OGL_STATE_CHANGE:
+        // No-op
+        break;
+    default:
+        // We are starting a primitive operation, so call glBegin() with
+        // the given primitive type.
+        j2d_glBegin(op);
+        break;
+    }
+
+    previousOp = op;
+}
+
+#endif /* !HEADLESS */