1 /*
   2  * Copyright (c) 2003, 2006, 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 <jni.h>
  29 #include <jlong.h>
  30 
  31 #include "SurfaceData.h"
  32 #include "OGLBlitLoops.h"
  33 #include "OGLRenderQueue.h"
  34 #include "OGLSurfaceData.h"
  35 #include "GraphicsPrimitiveMgr.h"
  36 
  37 extern OGLPixelFormat PixelFormats[];
  38 
  39 /**
  40  * Inner loop used for copying a source OpenGL "Surface" (window, pbuffer,
  41  * etc.) to a destination OpenGL "Surface".  Note that the same surface can
  42  * be used as both the source and destination, as is the case in a copyArea()
  43  * operation.  This method is invoked from OGLBlitLoops_IsoBlit() as well as
  44  * OGLBlitLoops_CopyArea().
  45  *
  46  * The standard glCopyPixels() mechanism is used to copy the source region
  47  * into the destination region.  If the regions have different dimensions,
  48  * the source will be scaled into the destination as appropriate (only
  49  * nearest neighbor filtering will be applied for simple scale operations).
  50  */
  51 static void
  52 OGLBlitSurfaceToSurface(OGLContext *oglc, OGLSDOps *srcOps, OGLSDOps *dstOps,
  53                         jint sx1, jint sy1, jint sx2, jint sy2,
  54                         jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)
  55 {
  56     GLfloat scalex, scaley;
  57     jint srcw = sx2 - sx1;
  58     jint srch = sy2 - sy1;
  59 
  60     scalex = ((GLfloat)(dx2-dx1)) / srcw;
  61     scaley = ((GLfloat)(dy2-dy1)) / srch;
  62 
  63     // the following lines account for the fact that glCopyPixels() copies a
  64     // region whose lower-left corner is at (x,y), but the source parameters
  65     // (sx1,sy1) we are given here point to the upper-left corner of the
  66     // source region... so here we play with the sy1 and dy1 parameters so
  67     // that they point to the lower-left corners of the regions...
  68     sx1 = srcOps->xOffset + sx1;
  69     sy1 = srcOps->yOffset + srcOps->height - sy2;
  70     dy1 = dy2;
  71 
  72     if (oglc->extraAlpha != 1.0f) {
  73         OGLContext_SetExtraAlpha(oglc->extraAlpha);
  74     }
  75 
  76     // see OGLBlitSwToSurface() for more info on the following two lines
  77     j2d_glRasterPos2i(0, 0);
  78     j2d_glBitmap(0, 0, 0, 0, (GLfloat)dx1, (GLfloat)-dy1, NULL);
  79 
  80     if (scalex == 1.0f && scaley == 1.0f) {
  81         j2d_glCopyPixels(sx1, sy1, srcw, srch, GL_COLOR);
  82     } else {
  83         j2d_glPixelZoom(scalex, scaley);
  84         j2d_glCopyPixels(sx1, sy1, srcw, srch, GL_COLOR);
  85         j2d_glPixelZoom(1.0f, 1.0f);
  86     }
  87 
  88     if (oglc->extraAlpha != 1.0f) {
  89         OGLContext_SetExtraAlpha(1.0f);
  90     }
  91 }
  92 
  93 /**
  94  * Inner loop used for copying a source OpenGL "Texture" to a destination
  95  * OpenGL "Surface".  This method is invoked from OGLBlitLoops_IsoBlit().
  96  *
  97  * This method will copy, scale, or transform the source texture into the
  98  * destination depending on the transform state, as established in
  99  * and OGLContext_SetTransform().  If the source texture is
 100  * transformed in any way when rendered into the destination, the filtering
 101  * method applied is determined by the hint parameter (can be GL_NEAREST or
 102  * GL_LINEAR).
 103  */
 104 static void
 105 OGLBlitTextureToSurface(OGLContext *oglc,
 106                         OGLSDOps *srcOps, OGLSDOps *dstOps,
 107                         jboolean rtt, jint hint,
 108                         jint sx1, jint sy1, jint sx2, jint sy2,
 109                         jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)
 110 {
 111     GLdouble tx1, ty1, tx2, ty2;
 112 
 113     if (rtt) {
 114         /*
 115          * The source is a render-to-texture surface.  These surfaces differ
 116          * from regular texture objects in that the bottom scanline (of
 117          * the actual image content) coincides with the top edge of the
 118          * texture object.  Therefore, we need to adjust the sy1/sy2
 119          * coordinates relative to the top scanline of the image content.
 120          *
 121          * In texture coordinates, the top-left corner of the image content
 122          * would be at:
 123          *     (0.0, (imgHeight/texHeight))
 124          * while the bottom-right corner corresponds to:
 125          *     ((imgWidth/texWidth), 0.0)
 126          */
 127         sy1 = srcOps->height - sy1;
 128         sy2 = srcOps->height - sy2;
 129     }
 130 
 131     if (srcOps->textureTarget == GL_TEXTURE_RECTANGLE_ARB) {
 132         // The GL_ARB_texture_rectangle extension requires that we specify
 133         // texture coordinates in the range [0,srcw] and [0,srch] instead of
 134         // [0,1] as we would normally do in the case of GL_TEXTURE_2D
 135         tx1 = (GLdouble)sx1;
 136         ty1 = (GLdouble)sy1;
 137         tx2 = (GLdouble)sx2;
 138         ty2 = (GLdouble)sy2;
 139     } else {
 140         // Otherwise we need to convert the source bounds into the range [0,1]
 141         tx1 = ((GLdouble)sx1) / srcOps->textureWidth;
 142         ty1 = ((GLdouble)sy1) / srcOps->textureHeight;
 143         tx2 = ((GLdouble)sx2) / srcOps->textureWidth;
 144         ty2 = ((GLdouble)sy2) / srcOps->textureHeight;
 145     }
 146 
 147     // Note that we call CHECK_PREVIOUS_OP(texTarget) in IsoBlit(), which
 148     // will call glEnable(texTarget) as necessary.
 149     j2d_glBindTexture(srcOps->textureTarget, srcOps->textureID);
 150     OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE);
 151     OGLSD_UPDATE_TEXTURE_FILTER(srcOps, hint);
 152 
 153     j2d_glBegin(GL_QUADS);
 154     j2d_glTexCoord2d(tx1, ty1); j2d_glVertex2d(dx1, dy1);
 155     j2d_glTexCoord2d(tx2, ty1); j2d_glVertex2d(dx2, dy1);
 156     j2d_glTexCoord2d(tx2, ty2); j2d_glVertex2d(dx2, dy2);
 157     j2d_glTexCoord2d(tx1, ty2); j2d_glVertex2d(dx1, dy2);
 158     j2d_glEnd();
 159 }
 160 
 161 /**
 162  * Inner loop used for copying a source system memory ("Sw") surface to a
 163  * destination OpenGL "Surface".  This method is invoked from
 164  * OGLBlitLoops_Blit().
 165  *
 166  * The standard glDrawPixels() mechanism is used to copy the source region
 167  * into the destination region.  If the regions have different
 168  * dimensions, the source will be scaled into the destination
 169  * as appropriate (only nearest neighbor filtering will be applied for simple
 170  * scale operations).
 171  */
 172 static void
 173 OGLBlitSwToSurface(OGLContext *oglc, SurfaceDataRasInfo *srcInfo,
 174                    OGLPixelFormat *pf,
 175                    jint sx1, jint sy1, jint sx2, jint sy2,
 176                    jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)
 177 {
 178     GLfloat scalex, scaley;
 179 
 180     scalex = ((GLfloat)(dx2-dx1)) / (sx2-sx1);
 181     scaley = ((GLfloat)(dy2-dy1)) / (sy2-sy1);
 182 
 183     if (oglc->extraAlpha != 1.0f) {
 184         OGLContext_SetExtraAlpha(oglc->extraAlpha);
 185     }
 186     if (!pf->hasAlpha) {
 187         // if the source surface does not have an alpha channel,
 188         // we need to ensure that the alpha values are forced to
 189         // the current extra alpha value (see OGLContext_SetExtraAlpha()
 190         // for more information)
 191         j2d_glPixelTransferf(GL_ALPHA_SCALE, 0.0f);
 192         j2d_glPixelTransferf(GL_ALPHA_BIAS, oglc->extraAlpha);
 193     }
 194 
 195     // This is a rather intriguing (yet totally valid) hack... If we were to
 196     // specify a raster position that is outside the surface bounds, the raster
 197     // position would be invalid and nothing would be rendered.  However, we
 198     // can use a widely known trick to move the raster position outside the
 199     // surface bounds while maintaining its status as valid.  The following
 200     // call to glBitmap() renders a no-op bitmap, but offsets the current
 201     // raster position from (0,0) to the desired location of (dx1,-dy1)...
 202     j2d_glRasterPos2i(0, 0);
 203     j2d_glBitmap(0, 0, 0, 0, (GLfloat)dx1, (GLfloat)-dy1, NULL);
 204 
 205     j2d_glPixelZoom(scalex, -scaley);
 206 
 207     // in case pixel stride is not a multiple of scanline stride the copy
 208     // has to be done line by line (see 6207877)
 209     if (srcInfo->scanStride % srcInfo->pixelStride != 0) {
 210         jint width = sx2-sx1;
 211         jint height = sy2-sy1;
 212         GLvoid *pSrc = srcInfo->rasBase;
 213 
 214         while (height > 0) {
 215             j2d_glDrawPixels(width, 1, pf->format, pf->type, pSrc);
 216             j2d_glBitmap(0, 0, 0, 0, (GLfloat)0, (GLfloat)-1, NULL);
 217             pSrc = PtrAddBytes(pSrc, srcInfo->scanStride);
 218             height--;
 219         }
 220     } else {
 221         j2d_glDrawPixels(sx2-sx1, sy2-sy1, pf->format, pf->type, srcInfo->rasBase);
 222     }
 223 
 224     j2d_glPixelZoom(1.0, 1.0);
 225 
 226     if (oglc->extraAlpha != 1.0f) {
 227         OGLContext_SetExtraAlpha(1.0f);
 228     }
 229     if (!pf->hasAlpha) {
 230         // restore scale/bias to their original values
 231         j2d_glPixelTransferf(GL_ALPHA_SCALE, 1.0f);
 232         j2d_glPixelTransferf(GL_ALPHA_BIAS, 0.0f);
 233     }
 234 }
 235 
 236 /**
 237  * Inner loop used for copying a source system memory ("Sw") surface or
 238  * OpenGL "Surface" to a destination OpenGL "Surface", using an OpenGL texture
 239  * tile as an intermediate surface.  This method is invoked from
 240  * OGLBlitLoops_Blit() for "Sw" surfaces and OGLBlitLoops_IsoBlit() for
 241  * "Surface" surfaces.
 242  *
 243  * This method is used to transform the source surface into the destination.
 244  * Pixel rectangles cannot be arbitrarily transformed (without the
 245  * GL_EXT_pixel_transform extension, which is not supported on most modern
 246  * hardware).  However, texture mapped quads do respect the GL_MODELVIEW
 247  * transform matrix, so we use textures here to perform the transform
 248  * operation.  This method uses a tile-based approach in which a small
 249  * subregion of the source surface is copied into a cached texture tile.  The
 250  * texture tile is then mapped into the appropriate location in the
 251  * destination surface.
 252  *
 253  * REMIND: this only works well using GL_NEAREST for the filtering mode
 254  *         (GL_LINEAR causes visible stitching problems between tiles,
 255  *         but this can be fixed by making use of texture borders)
 256  */
 257 static void
 258 OGLBlitToSurfaceViaTexture(OGLContext *oglc, SurfaceDataRasInfo *srcInfo,
 259                            OGLPixelFormat *pf, OGLSDOps *srcOps,
 260                            jboolean swsurface, jint hint,
 261                            jint sx1, jint sy1, jint sx2, jint sy2,
 262                            jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)
 263 {
 264     GLdouble tx1, ty1, tx2, ty2;
 265     GLdouble dx, dy, dw, dh, cdw, cdh;
 266     jint tw, th;
 267     jint sx, sy, sw, sh;
 268     GLint glhint = (hint == OGLSD_XFORM_BILINEAR) ? GL_LINEAR : GL_NEAREST;
 269     jboolean adjustAlpha = (pf != NULL && !pf->hasAlpha);
 270     jboolean slowPath;
 271 
 272     if (oglc->blitTextureID == 0) {
 273         if (!OGLContext_InitBlitTileTexture(oglc)) {
 274             J2dRlsTraceLn(J2D_TRACE_ERROR,
 275                 "OGLBlitToSurfaceViaTexture: could not init blit tile");
 276             return;
 277         }
 278     }
 279 
 280     tx1 = 0.0f;
 281     ty1 = 0.0f;
 282     tw = OGLC_BLIT_TILE_SIZE;
 283     th = OGLC_BLIT_TILE_SIZE;
 284     cdw = (dx2-dx1) / (((GLdouble)(sx2-sx1)) / OGLC_BLIT_TILE_SIZE);
 285     cdh = (dy2-dy1) / (((GLdouble)(sy2-sy1)) / OGLC_BLIT_TILE_SIZE);
 286 
 287     j2d_glEnable(GL_TEXTURE_2D);
 288     j2d_glBindTexture(GL_TEXTURE_2D, oglc->blitTextureID);
 289     OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE);
 290     j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, glhint);
 291     j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, glhint);
 292 
 293     if (adjustAlpha) {
 294         // if the source surface does not have an alpha channel,
 295         // we need to ensure that the alpha values are forced to 1.0f
 296         j2d_glPixelTransferf(GL_ALPHA_SCALE, 0.0f);
 297         j2d_glPixelTransferf(GL_ALPHA_BIAS, 1.0f);
 298     }
 299 
 300     // in case pixel stride is not a multiple of scanline stride the copy
 301     // has to be done line by line (see 6207877)
 302     slowPath = srcInfo->scanStride % srcInfo->pixelStride != 0;
 303 
 304     for (sy = sy1, dy = dy1; sy < sy2; sy += th, dy += cdh) {
 305         sh = ((sy + th) > sy2) ? (sy2 - sy) : th;
 306         dh = ((dy + cdh) > dy2) ? (dy2 - dy) : cdh;
 307 
 308         for (sx = sx1, dx = dx1; sx < sx2; sx += tw, dx += cdw) {
 309             sw = ((sx + tw) > sx2) ? (sx2 - sx) : tw;
 310             dw = ((dx + cdw) > dx2) ? (dx2 - dx) : cdw;
 311 
 312             tx2 = ((GLdouble)sw) / tw;
 313             ty2 = ((GLdouble)sh) / th;
 314 
 315             if (swsurface) {
 316                 if (slowPath) {
 317                     jint tmph = sh;
 318                     GLvoid *pSrc = PtrCoord(srcInfo->rasBase,
 319                                             sx, srcInfo->pixelStride,
 320                                             sy, srcInfo->scanStride);
 321 
 322                     while (tmph > 0) {
 323                         j2d_glTexSubImage2D(GL_TEXTURE_2D, 0,
 324                                             0, sh - tmph, sw, 1,
 325                                             pf->format, pf->type,
 326                                             pSrc);
 327                         pSrc = PtrAddBytes(pSrc, srcInfo->scanStride);
 328                         tmph--;
 329                     }
 330                 } else {
 331                     j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, sx);
 332                     j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, sy);
 333 
 334                     j2d_glTexSubImage2D(GL_TEXTURE_2D, 0,
 335                                         0, 0, sw, sh,
 336                                         pf->format, pf->type,
 337                                         srcInfo->rasBase);
 338                 }
 339 
 340                 // the texture image is "right side up", so we align the
 341                 // upper-left texture corner with the upper-left quad corner
 342                 j2d_glBegin(GL_QUADS);
 343                 j2d_glTexCoord2d(tx1, ty1); j2d_glVertex2d(dx, dy);
 344                 j2d_glTexCoord2d(tx2, ty1); j2d_glVertex2d(dx + dw, dy);
 345                 j2d_glTexCoord2d(tx2, ty2); j2d_glVertex2d(dx + dw, dy + dh);
 346                 j2d_glTexCoord2d(tx1, ty2); j2d_glVertex2d(dx, dy + dh);
 347                 j2d_glEnd();
 348             } else {
 349                 // this accounts for lower-left origin of the source region
 350                 jint newsx = srcOps->xOffset + sx;
 351                 jint newsy = srcOps->yOffset + srcOps->height - (sy + sh);
 352                 j2d_glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
 353                                         0, 0, newsx, newsy, sw, sh);
 354 
 355                 // the texture image is "upside down" after the last step, so
 356                 // we align the bottom-left texture corner with the upper-left
 357                 // quad corner (and vice versa) to effectively flip the
 358                 // texture image
 359                 j2d_glBegin(GL_QUADS);
 360                 j2d_glTexCoord2d(tx1, ty2); j2d_glVertex2d(dx, dy);
 361                 j2d_glTexCoord2d(tx2, ty2); j2d_glVertex2d(dx + dw, dy);
 362                 j2d_glTexCoord2d(tx2, ty1); j2d_glVertex2d(dx + dw, dy + dh);
 363                 j2d_glTexCoord2d(tx1, ty1); j2d_glVertex2d(dx, dy + dh);
 364                 j2d_glEnd();
 365             }
 366         }
 367     }
 368 
 369     if (adjustAlpha) {
 370         // restore scale/bias to their original values
 371         j2d_glPixelTransferf(GL_ALPHA_SCALE, 1.0f);
 372         j2d_glPixelTransferf(GL_ALPHA_BIAS, 0.0f);
 373     }
 374 
 375     j2d_glDisable(GL_TEXTURE_2D);
 376 }
 377 
 378 /**
 379  * Inner loop used for copying a source system memory ("Sw") surface to a
 380  * destination OpenGL "Texture".  This method is invoked from
 381  * OGLBlitLoops_Blit().
 382  *
 383  * The source surface is effectively loaded into the OpenGL texture object,
 384  * which must have already been initialized by OGLSD_initTexture().  Note
 385  * that this method is only capable of copying the source surface into the
 386  * destination surface (i.e. no scaling or general transform is allowed).
 387  * This restriction should not be an issue as this method is only used
 388  * currently to cache a static system memory image into an OpenGL texture in
 389  * a hidden-acceleration situation.
 390  */
 391 static void
 392 OGLBlitSwToTexture(OGLContext *oglc, SurfaceDataRasInfo *srcInfo,
 393                    OGLPixelFormat *pf, OGLSDOps *dstOps,
 394                    jint dx1, jint dy1, jint dx2, jint dy2)
 395 {
 396     j2d_glBindTexture(dstOps->textureTarget, dstOps->textureID);
 397 
 398     if (oglc->extraAlpha != 1.0f) {
 399         OGLContext_SetExtraAlpha(oglc->extraAlpha);
 400     }
 401     if (!pf->hasAlpha) {
 402         // if the source surface does not have an alpha channel,
 403         // we need to ensure that the alpha values are forced to
 404         // the current extra alpha value (see OGLContext_SetExtraAlpha()
 405         // for more information)
 406         j2d_glPixelTransferf(GL_ALPHA_SCALE, 0.0f);
 407         j2d_glPixelTransferf(GL_ALPHA_BIAS, oglc->extraAlpha);
 408     }
 409 
 410     // in case pixel stride is not a multiple of scanline stride the copy
 411     // has to be done line by line (see 6207877)
 412     if (srcInfo->scanStride % srcInfo->pixelStride != 0) {
 413         jint width = dx2 - dx1;
 414         jint height = dy2 - dy1;
 415         GLvoid *pSrc = srcInfo->rasBase;
 416 
 417         while (height > 0) {
 418             j2d_glTexSubImage2D(dstOps->textureTarget, 0,
 419                                 dx1, dy2 - height, width, 1,
 420                                 pf->format, pf->type, pSrc);
 421             pSrc = PtrAddBytes(pSrc, srcInfo->scanStride);
 422             height--;
 423         }
 424     } else {
 425         j2d_glTexSubImage2D(dstOps->textureTarget, 0,
 426                             dx1, dy1, dx2-dx1, dy2-dy1,
 427                             pf->format, pf->type, srcInfo->rasBase);
 428     }
 429     if (oglc->extraAlpha != 1.0f) {
 430         OGLContext_SetExtraAlpha(1.0f);
 431     }
 432     if (!pf->hasAlpha) {
 433         // restore scale/bias to their original values
 434         j2d_glPixelTransferf(GL_ALPHA_SCALE, 1.0f);
 435         j2d_glPixelTransferf(GL_ALPHA_BIAS, 0.0f);
 436     }
 437 }
 438 
 439 /**
 440  * General blit method for copying a native OpenGL surface (of type "Surface"
 441  * or "Texture") to another OpenGL "Surface".  If texture is JNI_TRUE, this
 442  * method will invoke the Texture->Surface inner loop; otherwise, one of the
 443  * Surface->Surface inner loops will be invoked, depending on the transform
 444  * state.
 445  *
 446  * REMIND: we can trick these blit methods into doing XOR simply by passing
 447  *         in the (pixel ^ xorpixel) as the pixel value and preceding the
 448  *         blit with a fillrect...
 449  */
 450 void
 451 OGLBlitLoops_IsoBlit(JNIEnv *env,
 452                      OGLContext *oglc, jlong pSrcOps, jlong pDstOps,
 453                      jboolean xform, jint hint,
 454                      jboolean texture, jboolean rtt,
 455                      jint sx1, jint sy1, jint sx2, jint sy2,
 456                      jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)
 457 {
 458     OGLSDOps *srcOps = (OGLSDOps *)jlong_to_ptr(pSrcOps);
 459     OGLSDOps *dstOps = (OGLSDOps *)jlong_to_ptr(pDstOps);
 460     SurfaceDataRasInfo srcInfo;
 461     jint sw    = sx2 - sx1;
 462     jint sh    = sy2 - sy1;
 463     jdouble dw = dx2 - dx1;
 464     jdouble dh = dy2 - dy1;
 465 
 466     J2dTraceLn(J2D_TRACE_INFO, "OGLBlitLoops_IsoBlit");
 467 
 468     if (sw <= 0 || sh <= 0 || dw <= 0 || dh <= 0) {
 469         J2dTraceLn(J2D_TRACE_WARNING,
 470                    "OGLBlitLoops_IsoBlit: invalid dimensions");
 471         return;
 472     }
 473 
 474     RETURN_IF_NULL(srcOps);
 475     RETURN_IF_NULL(dstOps);
 476     RETURN_IF_NULL(oglc);
 477 
 478     srcInfo.bounds.x1 = sx1;
 479     srcInfo.bounds.y1 = sy1;
 480     srcInfo.bounds.x2 = sx2;
 481     srcInfo.bounds.y2 = sy2;
 482 
 483     SurfaceData_IntersectBoundsXYXY(&srcInfo.bounds,
 484                                     0, 0, srcOps->width, srcOps->height);
 485 
 486     if (srcInfo.bounds.x2 > srcInfo.bounds.x1 &&
 487         srcInfo.bounds.y2 > srcInfo.bounds.y1)
 488     {
 489         if (srcInfo.bounds.x1 != sx1) {
 490             dx1 += (srcInfo.bounds.x1 - sx1) * (dw / sw);
 491             sx1 = srcInfo.bounds.x1;
 492         }
 493         if (srcInfo.bounds.y1 != sy1) {
 494             dy1 += (srcInfo.bounds.y1 - sy1) * (dh / sh);
 495             sy1 = srcInfo.bounds.y1;
 496         }
 497         if (srcInfo.bounds.x2 != sx2) {
 498             dx2 += (srcInfo.bounds.x2 - sx2) * (dw / sw);
 499             sx2 = srcInfo.bounds.x2;
 500         }
 501         if (srcInfo.bounds.y2 != sy2) {
 502             dy2 += (srcInfo.bounds.y2 - sy2) * (dh / sh);
 503             sy2 = srcInfo.bounds.y2;
 504         }
 505 
 506         J2dTraceLn2(J2D_TRACE_VERBOSE, "  texture=%d hint=%d", texture, hint);
 507         J2dTraceLn4(J2D_TRACE_VERBOSE, "  sx1=%d sy1=%d sx2=%d sy2=%d",
 508                     sx1, sy1, sx2, sy2);
 509         J2dTraceLn4(J2D_TRACE_VERBOSE, "  dx1=%f dy1=%f dx2=%f dy2=%f",
 510                     dx1, dy1, dx2, dy2);
 511 
 512         if (texture) {
 513             GLint glhint = (hint == OGLSD_XFORM_BILINEAR) ? GL_LINEAR :
 514                                                             GL_NEAREST;
 515             CHECK_PREVIOUS_OP(srcOps->textureTarget);
 516             OGLBlitTextureToSurface(oglc, srcOps, dstOps, rtt, glhint,
 517                                     sx1, sy1, sx2, sy2,
 518                                     dx1, dy1, dx2, dy2);
 519         } else {
 520             jboolean viaTexture;
 521             if (xform) {
 522                 // we must use the via-texture codepath when there is a xform
 523                 viaTexture = JNI_TRUE;
 524             } else {
 525                 // look at the vendor to see which codepath is faster
 526                 // (this has been empirically determined; see 5020009)
 527                 switch (OGLC_GET_VENDOR(oglc)) {
 528                 case OGLC_VENDOR_NVIDIA:
 529                     // the via-texture codepath tends to be faster when
 530                     // there is either a simple scale OR an extra alpha
 531                     viaTexture =
 532                         (sx2-sx1) != (jint)(dx2-dx1) ||
 533                         (sy2-sy1) != (jint)(dy2-dy1) ||
 534                         oglc->extraAlpha != 1.0f;
 535                     break;
 536 
 537                 case OGLC_VENDOR_ATI:
 538                     // the via-texture codepath tends to be faster only when
 539                     // there is an extra alpha involved (scaling or not)
 540                     viaTexture = (oglc->extraAlpha != 1.0f);
 541                     break;
 542 
 543                 default:
 544                     // just use the glCopyPixels() codepath
 545                     viaTexture = JNI_FALSE;
 546                     break;
 547                 }
 548             }
 549 
 550             RESET_PREVIOUS_OP();
 551             if (viaTexture) {
 552                 OGLBlitToSurfaceViaTexture(oglc, &srcInfo, NULL, srcOps,
 553                                            JNI_FALSE, hint,
 554                                            sx1, sy1, sx2, sy2,
 555                                            dx1, dy1, dx2, dy2);
 556             } else {
 557                 OGLBlitSurfaceToSurface(oglc, srcOps, dstOps,
 558                                         sx1, sy1, sx2, sy2,
 559                                         dx1, dy1, dx2, dy2);
 560             }
 561         }
 562     }
 563 }
 564 
 565 /**
 566  * General blit method for copying a system memory ("Sw") surface to a native
 567  * OpenGL surface (of type "Surface" or "Texture").  If texture is JNI_TRUE,
 568  * this method will invoke the Sw->Texture inner loop; otherwise, one of the
 569  * Sw->Surface inner loops will be invoked, depending on the transform state.
 570  */
 571 void
 572 OGLBlitLoops_Blit(JNIEnv *env,
 573                   OGLContext *oglc, jlong pSrcOps, jlong pDstOps,
 574                   jboolean xform, jint hint,
 575                   jint srctype, jboolean texture,
 576                   jint sx1, jint sy1, jint sx2, jint sy2,
 577                   jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)
 578 {
 579     SurfaceDataOps *srcOps = (SurfaceDataOps *)jlong_to_ptr(pSrcOps);
 580     OGLSDOps *dstOps = (OGLSDOps *)jlong_to_ptr(pDstOps);
 581     SurfaceDataRasInfo srcInfo;
 582     OGLPixelFormat pf = PixelFormats[srctype];
 583     jint sw    = sx2 - sx1;
 584     jint sh    = sy2 - sy1;
 585     jdouble dw = dx2 - dx1;
 586     jdouble dh = dy2 - dy1;
 587 
 588     J2dTraceLn(J2D_TRACE_INFO, "OGLBlitLoops_Blit");
 589 
 590     if (sw <= 0 || sh <= 0 || dw <= 0 || dh <= 0 || srctype < 0) {
 591         J2dTraceLn(J2D_TRACE_WARNING,
 592                    "OGLBlitLoops_Blit: invalid dimensions or srctype");
 593         return;
 594     }
 595 
 596     RETURN_IF_NULL(srcOps);
 597     RETURN_IF_NULL(dstOps);
 598     RETURN_IF_NULL(oglc);
 599     RESET_PREVIOUS_OP();
 600 
 601     srcInfo.bounds.x1 = sx1;
 602     srcInfo.bounds.y1 = sy1;
 603     srcInfo.bounds.x2 = sx2;
 604     srcInfo.bounds.y2 = sy2;
 605 
 606     if (srcOps->Lock(env, srcOps, &srcInfo, SD_LOCK_READ) != SD_SUCCESS) {
 607         J2dTraceLn(J2D_TRACE_WARNING,
 608                    "OGLBlitLoops_Blit: could not acquire lock");
 609         return;
 610     }
 611 
 612     if (srcInfo.bounds.x2 > srcInfo.bounds.x1 &&
 613         srcInfo.bounds.y2 > srcInfo.bounds.y1)
 614     {
 615         srcOps->GetRasInfo(env, srcOps, &srcInfo);
 616         if (srcInfo.rasBase) {
 617             if (srcInfo.bounds.x1 != sx1) {
 618                 dx1 += (srcInfo.bounds.x1 - sx1) * (dw / sw);
 619                 sx1 = srcInfo.bounds.x1;
 620             }
 621             if (srcInfo.bounds.y1 != sy1) {
 622                 dy1 += (srcInfo.bounds.y1 - sy1) * (dh / sh);
 623                 sy1 = srcInfo.bounds.y1;
 624             }
 625             if (srcInfo.bounds.x2 != sx2) {
 626                 dx2 += (srcInfo.bounds.x2 - sx2) * (dw / sw);
 627                 sx2 = srcInfo.bounds.x2;
 628             }
 629             if (srcInfo.bounds.y2 != sy2) {
 630                 dy2 += (srcInfo.bounds.y2 - sy2) * (dh / sh);
 631                 sy2 = srcInfo.bounds.y2;
 632             }
 633 
 634             J2dTraceLn3(J2D_TRACE_VERBOSE, "  texture=%d srctype=%d hint=%d",
 635                         texture, srctype, hint);
 636             J2dTraceLn4(J2D_TRACE_VERBOSE, "  sx1=%d sy1=%d sx2=%d sy2=%d",
 637                         sx1, sy1, sx2, sy2);
 638             J2dTraceLn4(J2D_TRACE_VERBOSE, "  dx1=%f dy1=%f dx2=%f dy2=%f",
 639                         dx1, dy1, dx2, dy2);
 640 
 641             j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, sx1);
 642             j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, sy1);
 643             j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH,
 644                               srcInfo.scanStride / srcInfo.pixelStride);
 645             j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, pf.alignment);
 646 
 647             if (texture) {
 648                 // These coordinates will always be integers since we
 649                 // only ever do a straight copy from sw to texture.
 650                 // Thus these casts are "safe" - no loss of precision.
 651                 OGLBlitSwToTexture(oglc, &srcInfo, &pf, dstOps,
 652                                    (jint)dx1, (jint)dy1, (jint)dx2, (jint)dy2);
 653             } else {
 654                 jboolean viaTexture;
 655                 if (xform) {
 656                     // we must use the via-texture codepath when there
 657                     // is a xform
 658                     viaTexture = JNI_TRUE;
 659                 } else {
 660                     // look at the vendor to see which codepath is faster
 661                     // (this has been empirically determined; see 5020009)
 662                     switch (OGLC_GET_VENDOR(oglc)) {
 663                     case OGLC_VENDOR_NVIDIA:
 664                         // the via-texture codepath tends to be faster when
 665                         // there is either a simple scale OR an extra alpha
 666                         viaTexture =
 667                             (sx2-sx1) != (jint)(dx2-dx1) ||
 668                             (sy2-sy1) != (jint)(dy2-dy1) ||
 669                             oglc->extraAlpha != 1.0f;
 670                         break;
 671 
 672                     default:
 673                         // just use the glDrawPixels() codepath
 674                         viaTexture = JNI_FALSE;
 675                         break;
 676                     }
 677                 }
 678 
 679                 if (viaTexture) {
 680                     OGLBlitToSurfaceViaTexture(oglc, &srcInfo, &pf, NULL,
 681                                                JNI_TRUE, hint,
 682                                                sx1, sy1, sx2, sy2,
 683                                                dx1, dy1, dx2, dy2);
 684                 } else {
 685                     OGLBlitSwToSurface(oglc, &srcInfo, &pf,
 686                                        sx1, sy1, sx2, sy2,
 687                                        dx1, dy1, dx2, dy2);
 688                 }
 689             }
 690 
 691             j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
 692             j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
 693             j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
 694             j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
 695         }
 696         SurfaceData_InvokeRelease(env, srcOps, &srcInfo);
 697     }
 698     SurfaceData_InvokeUnlock(env, srcOps, &srcInfo);
 699 }
 700 
 701 /**
 702  * Specialized blit method for copying a native OpenGL "Surface" (pbuffer,
 703  * window, etc.) to a system memory ("Sw") surface.
 704  */
 705 void
 706 OGLBlitLoops_SurfaceToSwBlit(JNIEnv *env, OGLContext *oglc,
 707                              jlong pSrcOps, jlong pDstOps, jint dsttype,
 708                              jint srcx, jint srcy, jint dstx, jint dsty,
 709                              jint width, jint height)
 710 {
 711     OGLSDOps *srcOps = (OGLSDOps *)jlong_to_ptr(pSrcOps);
 712     SurfaceDataOps *dstOps = (SurfaceDataOps *)jlong_to_ptr(pDstOps);
 713     SurfaceDataRasInfo srcInfo, dstInfo;
 714     OGLPixelFormat pf = PixelFormats[dsttype];
 715 
 716     J2dTraceLn(J2D_TRACE_INFO, "OGLBlitLoops_SurfaceToSwBlit");
 717 
 718     if (width <= 0 || height <= 0) {
 719         J2dTraceLn(J2D_TRACE_WARNING,
 720             "OGLBlitLoops_SurfaceToSwBlit: dimensions are non-positive");
 721         return;
 722     }
 723 
 724     RETURN_IF_NULL(srcOps);
 725     RETURN_IF_NULL(dstOps);
 726     RETURN_IF_NULL(oglc);
 727     RESET_PREVIOUS_OP();
 728 
 729     srcInfo.bounds.x1 = srcx;
 730     srcInfo.bounds.y1 = srcy;
 731     srcInfo.bounds.x2 = srcx + width;
 732     srcInfo.bounds.y2 = srcy + height;
 733     dstInfo.bounds.x1 = dstx;
 734     dstInfo.bounds.y1 = dsty;
 735     dstInfo.bounds.x2 = dstx + width;
 736     dstInfo.bounds.y2 = dsty + height;
 737 
 738     if (dstOps->Lock(env, dstOps, &dstInfo, SD_LOCK_WRITE) != SD_SUCCESS) {
 739         J2dTraceLn(J2D_TRACE_WARNING,
 740             "OGLBlitLoops_SurfaceToSwBlit: could not acquire dst lock");
 741         return;
 742     }
 743 
 744     SurfaceData_IntersectBoundsXYXY(&srcInfo.bounds,
 745                                     0, 0, srcOps->width, srcOps->height);
 746     SurfaceData_IntersectBlitBounds(&dstInfo.bounds, &srcInfo.bounds,
 747                                     srcx - dstx, srcy - dsty);
 748 
 749     if (srcInfo.bounds.x2 > srcInfo.bounds.x1 &&
 750         srcInfo.bounds.y2 > srcInfo.bounds.y1)
 751     {
 752         dstOps->GetRasInfo(env, dstOps, &dstInfo);
 753         if (dstInfo.rasBase) {
 754             void *pDst = dstInfo.rasBase;
 755 
 756             srcx = srcInfo.bounds.x1;
 757             srcy = srcInfo.bounds.y1;
 758             dstx = dstInfo.bounds.x1;
 759             dsty = dstInfo.bounds.y1;
 760             width = srcInfo.bounds.x2 - srcInfo.bounds.x1;
 761             height = srcInfo.bounds.y2 - srcInfo.bounds.y1;
 762 
 763             j2d_glPixelStorei(GL_PACK_SKIP_PIXELS, dstx);
 764             j2d_glPixelStorei(GL_PACK_ROW_LENGTH,
 765                               dstInfo.scanStride / dstInfo.pixelStride);
 766             j2d_glPixelStorei(GL_PACK_ALIGNMENT, pf.alignment);
 767 #ifdef MACOSX
 768             if (srcOps->isOpaque) {
 769                 // For some reason Apple's OpenGL implementation will
 770                 // read back zero values from the alpha channel of an
 771                 // opaque surface when using glReadPixels(), so here we
 772                 // force the resulting pixels to be fully opaque.
 773                 j2d_glPixelTransferf(GL_ALPHA_BIAS, 1.0);
 774             }
 775 #endif
 776 
 777             J2dTraceLn4(J2D_TRACE_VERBOSE, "  sx=%d sy=%d w=%d h=%d",
 778                         srcx, srcy, width, height);
 779             J2dTraceLn2(J2D_TRACE_VERBOSE, "  dx=%d dy=%d",
 780                         dstx, dsty);
 781 
 782             // this accounts for lower-left origin of the source region
 783             srcx = srcOps->xOffset + srcx;
 784             srcy = srcOps->yOffset + srcOps->height - (srcy + 1);
 785 
 786             // we must read one scanline at a time because there is no way
 787             // to read starting at the top-left corner of the source region
 788             while (height > 0) {
 789                 j2d_glPixelStorei(GL_PACK_SKIP_ROWS, dsty);
 790                 j2d_glReadPixels(srcx, srcy, width, 1,
 791                                  pf.format, pf.type, pDst);
 792                 srcy--;
 793                 dsty++;
 794                 height--;
 795             }
 796 
 797 #ifdef MACOSX
 798             if (srcOps->isOpaque) {
 799                 j2d_glPixelTransferf(GL_ALPHA_BIAS, 0.0);
 800             }
 801 #endif
 802 
 803             j2d_glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
 804             j2d_glPixelStorei(GL_PACK_SKIP_ROWS, 0);
 805             j2d_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
 806             j2d_glPixelStorei(GL_PACK_ALIGNMENT, 4);
 807         }
 808         SurfaceData_InvokeRelease(env, dstOps, &dstInfo);
 809     }
 810     SurfaceData_InvokeUnlock(env, dstOps, &dstInfo);
 811 }
 812 
 813 void
 814 OGLBlitLoops_CopyArea(JNIEnv *env,
 815                       OGLContext *oglc, OGLSDOps *dstOps,
 816                       jint x, jint y, jint width, jint height,
 817                       jint dx, jint dy)
 818 {
 819     SurfaceDataBounds srcBounds, dstBounds;
 820 
 821     J2dTraceLn(J2D_TRACE_INFO, "OGLBlitLoops_CopyArea");
 822 
 823     RETURN_IF_NULL(oglc);
 824     RETURN_IF_NULL(dstOps);
 825     RESET_PREVIOUS_OP();
 826 
 827     J2dTraceLn4(J2D_TRACE_VERBOSE, "  x=%d y=%d w=%d h=%d",
 828                 x, y, width, height);
 829     J2dTraceLn2(J2D_TRACE_VERBOSE, "  dx=%d dy=%d",
 830                 dx, dy);
 831 
 832     srcBounds.x1 = x;
 833     srcBounds.y1 = y;
 834     srcBounds.x2 = srcBounds.x1 + width;
 835     srcBounds.y2 = srcBounds.y1 + height;
 836     dstBounds.x1 = x + dx;
 837     dstBounds.y1 = y + dy;
 838     dstBounds.x2 = dstBounds.x1 + width;
 839     dstBounds.y2 = dstBounds.y1 + height;
 840 
 841     // 6430601: manually clip src/dst parameters to work around
 842     // some bugs in Sun's and Apple's OpenGL implementations
 843     // (it's a good idea to restrict the source parameters anyway, since
 844     // passing out of range parameters to glCopyPixels() will result in
 845     // an OpenGL error)
 846     SurfaceData_IntersectBoundsXYXY(&srcBounds,
 847                                     0, 0, dstOps->width, dstOps->height);
 848     SurfaceData_IntersectBoundsXYXY(&dstBounds,
 849                                     0, 0, dstOps->width, dstOps->height);
 850     SurfaceData_IntersectBlitBounds(&dstBounds, &srcBounds, -dx, -dy);
 851 
 852     if (dstBounds.x1 < dstBounds.x2 && dstBounds.y1 < dstBounds.y2) {
 853 #ifdef MACOSX
 854         if (dstOps->isOpaque) {
 855             // For some reason Apple's OpenGL implementation will fail
 856             // to render glCopyPixels() when the src/dst rectangles are
 857             // overlapping and glColorMask() has disabled writes to the
 858             // alpha channel.  The workaround is to temporarily re-enable
 859             // the alpha channel during the glCopyPixels() operation.
 860             j2d_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
 861         }
 862 #endif
 863 
 864         OGLBlitSurfaceToSurface(oglc, dstOps, dstOps,
 865                                 srcBounds.x1, srcBounds.y1,
 866                                 srcBounds.x2, srcBounds.y2,
 867                                 dstBounds.x1, dstBounds.y1,
 868                                 dstBounds.x2, dstBounds.y2);
 869 #ifdef MACOSX
 870         if (dstOps->isOpaque) {
 871             j2d_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
 872         }
 873 #endif
 874     }
 875 }
 876 
 877 #endif /* !HEADLESS */