1 /*
   2  * Copyright (c) 2003, 2014, 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 #include <stdlib.h> // malloc
  38 #include <string.h> // memcpy
  39 #include "IntArgbPre.h"
  40 
  41 extern OGLPixelFormat PixelFormats[];
  42 
  43 /**
  44  * Inner loop used for copying a source OpenGL "Surface" (window, pbuffer,
  45  * etc.) to a destination OpenGL "Surface".  Note that the same surface can
  46  * be used as both the source and destination, as is the case in a copyArea()
  47  * operation.  This method is invoked from OGLBlitLoops_IsoBlit() as well as
  48  * OGLBlitLoops_CopyArea().
  49  *
  50  * The standard glCopyPixels() mechanism is used to copy the source region
  51  * into the destination region.  If the regions have different dimensions,
  52  * the source will be scaled into the destination as appropriate (only
  53  * nearest neighbor filtering will be applied for simple scale operations).
  54  */
  55 static void
  56 OGLBlitSurfaceToSurface(OGLContext *oglc, OGLSDOps *srcOps, OGLSDOps *dstOps,
  57                         jint sx1, jint sy1, jint sx2, jint sy2,
  58                         jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)
  59 {
  60     GLfloat scalex, scaley;
  61     jint srcw = sx2 - sx1;
  62     jint srch = sy2 - sy1;
  63 
  64     scalex = ((GLfloat)(dx2-dx1)) / srcw;
  65     scaley = ((GLfloat)(dy2-dy1)) / srch;
  66 
  67     // the following lines account for the fact that glCopyPixels() copies a
  68     // region whose lower-left corner is at (x,y), but the source parameters
  69     // (sx1,sy1) we are given here point to the upper-left corner of the
  70     // source region... so here we play with the sy1 and dy1 parameters so
  71     // that they point to the lower-left corners of the regions...
  72     sx1 = srcOps->xOffset + sx1;
  73     sy1 = srcOps->yOffset + srcOps->height - sy2;
  74     dy1 = dy2;
  75 
  76     if (oglc->extraAlpha != 1.0f) {
  77         OGLContext_SetExtraAlpha(oglc->extraAlpha);
  78     }
  79 
  80     // see OGLBlitSwToSurface() for more info on the following two lines
  81     j2d_glRasterPos2i(0, 0);
  82     j2d_glBitmap(0, 0, 0, 0, (GLfloat)dx1, (GLfloat)-dy1, NULL);
  83 
  84     if (scalex == 1.0f && scaley == 1.0f) {
  85         j2d_glCopyPixels(sx1, sy1, srcw, srch, GL_COLOR);
  86     } else {
  87         j2d_glPixelZoom(scalex, scaley);
  88         j2d_glCopyPixels(sx1, sy1, srcw, srch, GL_COLOR);
  89         j2d_glPixelZoom(1.0f, 1.0f);
  90     }
  91 
  92     if (oglc->extraAlpha != 1.0f) {
  93         OGLContext_SetExtraAlpha(1.0f);
  94     }
  95 }
  96 
  97 /**
  98  * Inner loop used for copying a source OpenGL "Texture" to a destination
  99  * OpenGL "Surface".  This method is invoked from OGLBlitLoops_IsoBlit().
 100  *
 101  * This method will copy, scale, or transform the source texture into the
 102  * destination depending on the transform state, as established in
 103  * and OGLContext_SetTransform().  If the source texture is
 104  * transformed in any way when rendered into the destination, the filtering
 105  * method applied is determined by the hint parameter (can be GL_NEAREST or
 106  * GL_LINEAR).
 107  */
 108 static void
 109 OGLBlitTextureToSurface(OGLContext *oglc,
 110                         OGLSDOps *srcOps, OGLSDOps *dstOps,
 111                         jboolean rtt, jint hint,
 112                         jint sx1, jint sy1, jint sx2, jint sy2,
 113                         jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)
 114 {
 115     GLdouble tx1, ty1, tx2, ty2;
 116 
 117     if (rtt) {
 118         /*
 119          * The source is a render-to-texture surface.  These surfaces differ
 120          * from regular texture objects in that the bottom scanline (of
 121          * the actual image content) coincides with the top edge of the
 122          * texture object.  Therefore, we need to adjust the sy1/sy2
 123          * coordinates relative to the top scanline of the image content.
 124          *
 125          * In texture coordinates, the top-left corner of the image content
 126          * would be at:
 127          *     (0.0, (imgHeight/texHeight))
 128          * while the bottom-right corner corresponds to:
 129          *     ((imgWidth/texWidth), 0.0)
 130          */
 131         sy1 = srcOps->height - sy1;
 132         sy2 = srcOps->height - sy2;
 133     }
 134 
 135     if (srcOps->textureTarget == GL_TEXTURE_RECTANGLE_ARB) {
 136         // The GL_ARB_texture_rectangle extension requires that we specify
 137         // texture coordinates in the range [0,srcw] and [0,srch] instead of
 138         // [0,1] as we would normally do in the case of GL_TEXTURE_2D
 139         tx1 = (GLdouble)sx1;
 140         ty1 = (GLdouble)sy1;
 141         tx2 = (GLdouble)sx2;
 142         ty2 = (GLdouble)sy2;
 143     } else {
 144         // Otherwise we need to convert the source bounds into the range [0,1]
 145         tx1 = ((GLdouble)sx1) / srcOps->textureWidth;
 146         ty1 = ((GLdouble)sy1) / srcOps->textureHeight;
 147         tx2 = ((GLdouble)sx2) / srcOps->textureWidth;
 148         ty2 = ((GLdouble)sy2) / srcOps->textureHeight;
 149     }
 150 
 151     // Note that we call CHECK_PREVIOUS_OP(texTarget) in IsoBlit(), which
 152     // will call glEnable(texTarget) as necessary.
 153     j2d_glBindTexture(srcOps->textureTarget, srcOps->textureID);
 154     OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE);
 155     OGLSD_UPDATE_TEXTURE_FILTER(srcOps, hint);
 156 
 157     j2d_glBegin(GL_QUADS);
 158     j2d_glTexCoord2d(tx1, ty1); j2d_glVertex2d(dx1, dy1);
 159     j2d_glTexCoord2d(tx2, ty1); j2d_glVertex2d(dx2, dy1);
 160     j2d_glTexCoord2d(tx2, ty2); j2d_glVertex2d(dx2, dy2);
 161     j2d_glTexCoord2d(tx1, ty2); j2d_glVertex2d(dx1, dy2);
 162     j2d_glEnd();
 163 }
 164 
 165 /**
 166  * Inner loop used for copying a source system memory ("Sw") surface to a
 167  * destination OpenGL "Surface".  This method is invoked from
 168  * OGLBlitLoops_Blit().
 169  *
 170  * The standard glDrawPixels() mechanism is used to copy the source region
 171  * into the destination region.  If the regions have different
 172  * dimensions, the source will be scaled into the destination
 173  * as appropriate (only nearest neighbor filtering will be applied for simple
 174  * scale operations).
 175  */
 176 static void
 177 OGLBlitSwToSurface(OGLContext *oglc, SurfaceDataRasInfo *srcInfo,
 178                    OGLPixelFormat *pf,
 179                    jint sx1, jint sy1, jint sx2, jint sy2,
 180                    jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)
 181 {
 182     GLfloat scalex, scaley;
 183 
 184     scalex = ((GLfloat)(dx2-dx1)) / (sx2-sx1);
 185     scaley = ((GLfloat)(dy2-dy1)) / (sy2-sy1);
 186 
 187     if (oglc->extraAlpha != 1.0f) {
 188         OGLContext_SetExtraAlpha(oglc->extraAlpha);
 189     }
 190     if (!pf->hasAlpha) {
 191         // if the source surface does not have an alpha channel,
 192         // we need to ensure that the alpha values are forced to
 193         // the current extra alpha value (see OGLContext_SetExtraAlpha()
 194         // for more information)
 195         j2d_glPixelTransferf(GL_ALPHA_SCALE, 0.0f);
 196         j2d_glPixelTransferf(GL_ALPHA_BIAS, oglc->extraAlpha);
 197     }
 198 
 199     // This is a rather intriguing (yet totally valid) hack... If we were to
 200     // specify a raster position that is outside the surface bounds, the raster
 201     // position would be invalid and nothing would be rendered.  However, we
 202     // can use a widely known trick to move the raster position outside the
 203     // surface bounds while maintaining its status as valid.  The following
 204     // call to glBitmap() renders a no-op bitmap, but offsets the current
 205     // raster position from (0,0) to the desired location of (dx1,-dy1)...
 206     j2d_glRasterPos2i(0, 0);
 207     j2d_glBitmap(0, 0, 0, 0, (GLfloat)dx1, (GLfloat)-dy1, NULL);
 208 
 209     j2d_glPixelZoom(scalex, -scaley);
 210 
 211     // in case pixel stride is not a multiple of scanline stride the copy
 212     // has to be done line by line (see 6207877)
 213     if (srcInfo->scanStride % srcInfo->pixelStride != 0) {
 214         jint width = sx2-sx1;
 215         jint height = sy2-sy1;
 216         GLvoid *pSrc = srcInfo->rasBase;
 217 
 218         while (height > 0) {
 219             j2d_glDrawPixels(width, 1, pf->format, pf->type, pSrc);
 220             j2d_glBitmap(0, 0, 0, 0, (GLfloat)0, (GLfloat)-1, NULL);
 221             pSrc = PtrAddBytes(pSrc, srcInfo->scanStride);
 222             height--;
 223         }
 224     } else {
 225         j2d_glDrawPixels(sx2-sx1, sy2-sy1, pf->format, pf->type, srcInfo->rasBase);
 226     }
 227 
 228     j2d_glPixelZoom(1.0, 1.0);
 229 
 230     if (oglc->extraAlpha != 1.0f) {
 231         OGLContext_SetExtraAlpha(1.0f);
 232     }
 233     if (!pf->hasAlpha) {
 234         // restore scale/bias to their original values
 235         j2d_glPixelTransferf(GL_ALPHA_SCALE, 1.0f);
 236         j2d_glPixelTransferf(GL_ALPHA_BIAS, 0.0f);
 237     }
 238 }
 239 
 240 /**
 241  * Inner loop used for copying a source system memory ("Sw") surface or
 242  * OpenGL "Surface" to a destination OpenGL "Surface", using an OpenGL texture
 243  * tile as an intermediate surface.  This method is invoked from
 244  * OGLBlitLoops_Blit() for "Sw" surfaces and OGLBlitLoops_IsoBlit() for
 245  * "Surface" surfaces.
 246  *
 247  * This method is used to transform the source surface into the destination.
 248  * Pixel rectangles cannot be arbitrarily transformed (without the
 249  * GL_EXT_pixel_transform extension, which is not supported on most modern
 250  * hardware).  However, texture mapped quads do respect the GL_MODELVIEW
 251  * transform matrix, so we use textures here to perform the transform
 252  * operation.  This method uses a tile-based approach in which a small
 253  * subregion of the source surface is copied into a cached texture tile.  The
 254  * texture tile is then mapped into the appropriate location in the
 255  * destination surface.
 256  *
 257  * REMIND: this only works well using GL_NEAREST for the filtering mode
 258  *         (GL_LINEAR causes visible stitching problems between tiles,
 259  *         but this can be fixed by making use of texture borders)
 260  */
 261 static void
 262 OGLBlitToSurfaceViaTexture(OGLContext *oglc, SurfaceDataRasInfo *srcInfo,
 263                            OGLPixelFormat *pf, OGLSDOps *srcOps,
 264                            jboolean swsurface, jint hint,
 265                            jint sx1, jint sy1, jint sx2, jint sy2,
 266                            jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)
 267 {
 268     GLdouble tx1, ty1, tx2, ty2;
 269     GLdouble dx, dy, dw, dh, cdw, cdh;
 270     jint tw, th;
 271     jint sx, sy, sw, sh;
 272     GLint glhint = (hint == OGLSD_XFORM_BILINEAR) ? GL_LINEAR : GL_NEAREST;
 273     jboolean adjustAlpha = (pf != NULL && !pf->hasAlpha);
 274     jboolean slowPath;
 275 
 276     if (oglc->blitTextureID == 0) {
 277         if (!OGLContext_InitBlitTileTexture(oglc)) {
 278             J2dRlsTraceLn(J2D_TRACE_ERROR,
 279                 "OGLBlitToSurfaceViaTexture: could not init blit tile");
 280             return;
 281         }
 282     }
 283 
 284     tx1 = 0.0f;
 285     ty1 = 0.0f;
 286     tw = OGLC_BLIT_TILE_SIZE;
 287     th = OGLC_BLIT_TILE_SIZE;
 288     cdw = (dx2-dx1) / (((GLdouble)(sx2-sx1)) / OGLC_BLIT_TILE_SIZE);
 289     cdh = (dy2-dy1) / (((GLdouble)(sy2-sy1)) / OGLC_BLIT_TILE_SIZE);
 290 
 291     j2d_glEnable(GL_TEXTURE_2D);
 292     j2d_glBindTexture(GL_TEXTURE_2D, oglc->blitTextureID);
 293     OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE);
 294     j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, glhint);
 295     j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, glhint);
 296 
 297     if (adjustAlpha) {
 298         // if the source surface does not have an alpha channel,
 299         // we need to ensure that the alpha values are forced to 1.0f
 300         j2d_glPixelTransferf(GL_ALPHA_SCALE, 0.0f);
 301         j2d_glPixelTransferf(GL_ALPHA_BIAS, 1.0f);
 302     }
 303 
 304     // in case pixel stride is not a multiple of scanline stride the copy
 305     // has to be done line by line (see 6207877)
 306     slowPath = srcInfo->scanStride % srcInfo->pixelStride != 0;
 307 
 308     for (sy = sy1, dy = dy1; sy < sy2; sy += th, dy += cdh) {
 309         sh = ((sy + th) > sy2) ? (sy2 - sy) : th;
 310         dh = ((dy + cdh) > dy2) ? (dy2 - dy) : cdh;
 311 
 312         for (sx = sx1, dx = dx1; sx < sx2; sx += tw, dx += cdw) {
 313             sw = ((sx + tw) > sx2) ? (sx2 - sx) : tw;
 314             dw = ((dx + cdw) > dx2) ? (dx2 - dx) : cdw;
 315 
 316             tx2 = ((GLdouble)sw) / tw;
 317             ty2 = ((GLdouble)sh) / th;
 318 
 319             if (swsurface) {
 320                 if (slowPath) {
 321                     jint tmph = sh;
 322                     GLvoid *pSrc = PtrCoord(srcInfo->rasBase,
 323                                             sx, srcInfo->pixelStride,
 324                                             sy, srcInfo->scanStride);
 325 
 326                     while (tmph > 0) {
 327                         j2d_glTexSubImage2D(GL_TEXTURE_2D, 0,
 328                                             0, sh - tmph, sw, 1,
 329                                             pf->format, pf->type,
 330                                             pSrc);
 331                         pSrc = PtrAddBytes(pSrc, srcInfo->scanStride);
 332                         tmph--;
 333                     }
 334                 } else {
 335                     j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, sx);
 336                     j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, sy);
 337 
 338                     j2d_glTexSubImage2D(GL_TEXTURE_2D, 0,
 339                                         0, 0, sw, sh,
 340                                         pf->format, pf->type,
 341                                         srcInfo->rasBase);
 342 
 343                     j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
 344                     j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
 345                 }
 346 
 347                 // the texture image is "right side up", so we align the
 348                 // upper-left texture corner with the upper-left quad corner
 349                 j2d_glBegin(GL_QUADS);
 350                 j2d_glTexCoord2d(tx1, ty1); j2d_glVertex2d(dx, dy);
 351                 j2d_glTexCoord2d(tx2, ty1); j2d_glVertex2d(dx + dw, dy);
 352                 j2d_glTexCoord2d(tx2, ty2); j2d_glVertex2d(dx + dw, dy + dh);
 353                 j2d_glTexCoord2d(tx1, ty2); j2d_glVertex2d(dx, dy + dh);
 354                 j2d_glEnd();
 355             } else {
 356                 // this accounts for lower-left origin of the source region
 357                 jint newsx = srcOps->xOffset + sx;
 358                 jint newsy = srcOps->yOffset + srcOps->height - (sy + sh);
 359                 j2d_glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
 360                                         0, 0, newsx, newsy, sw, sh);
 361 
 362                 // the texture image is "upside down" after the last step, so
 363                 // we align the bottom-left texture corner with the upper-left
 364                 // quad corner (and vice versa) to effectively flip the
 365                 // texture image
 366                 j2d_glBegin(GL_QUADS);
 367                 j2d_glTexCoord2d(tx1, ty2); j2d_glVertex2d(dx, dy);
 368                 j2d_glTexCoord2d(tx2, ty2); j2d_glVertex2d(dx + dw, dy);
 369                 j2d_glTexCoord2d(tx2, ty1); j2d_glVertex2d(dx + dw, dy + dh);
 370                 j2d_glTexCoord2d(tx1, ty1); j2d_glVertex2d(dx, dy + dh);
 371                 j2d_glEnd();
 372             }
 373         }
 374     }
 375 
 376     if (adjustAlpha) {
 377         // restore scale/bias to their original values
 378         j2d_glPixelTransferf(GL_ALPHA_SCALE, 1.0f);
 379         j2d_glPixelTransferf(GL_ALPHA_BIAS, 0.0f);
 380     }
 381 
 382     j2d_glDisable(GL_TEXTURE_2D);
 383 }
 384 
 385 /**
 386  * Inner loop used for copying a source system memory ("Sw") surface to a
 387  * destination OpenGL "Texture".  This method is invoked from
 388  * OGLBlitLoops_Blit().
 389  *
 390  * The source surface is effectively loaded into the OpenGL texture object,
 391  * which must have already been initialized by OGLSD_initTexture().  Note
 392  * that this method is only capable of copying the source surface into the
 393  * destination surface (i.e. no scaling or general transform is allowed).
 394  * This restriction should not be an issue as this method is only used
 395  * currently to cache a static system memory image into an OpenGL texture in
 396  * a hidden-acceleration situation.
 397  */
 398 static void
 399 OGLBlitSwToTexture(SurfaceDataRasInfo *srcInfo, OGLPixelFormat *pf,
 400                    OGLSDOps *dstOps,
 401                    jint dx1, jint dy1, jint dx2, jint dy2)
 402 {
 403     jboolean adjustAlpha = (pf != NULL && !pf->hasAlpha);
 404     j2d_glBindTexture(dstOps->textureTarget, dstOps->textureID);
 405 
 406     if (adjustAlpha) {
 407         // if the source surface does not have an alpha channel,
 408         // we need to ensure that the alpha values are forced to 1.0f
 409         j2d_glPixelTransferf(GL_ALPHA_SCALE, 0.0f);
 410         j2d_glPixelTransferf(GL_ALPHA_BIAS, 1.0f);
 411     }
 412 
 413     // in case pixel stride is not a multiple of scanline stride the copy
 414     // has to be done line by line (see 6207877)
 415     if (srcInfo->scanStride % srcInfo->pixelStride != 0) {
 416         jint width = dx2 - dx1;
 417         jint height = dy2 - dy1;
 418         GLvoid *pSrc = srcInfo->rasBase;
 419 
 420         while (height > 0) {
 421             j2d_glTexSubImage2D(dstOps->textureTarget, 0,
 422                                 dx1, dy2 - height, width, 1,
 423                                 pf->format, pf->type, pSrc);
 424             pSrc = PtrAddBytes(pSrc, srcInfo->scanStride);
 425             height--;
 426         }
 427     } else {
 428         j2d_glTexSubImage2D(dstOps->textureTarget, 0,
 429                             dx1, dy1, dx2-dx1, dy2-dy1,
 430                             pf->format, pf->type, srcInfo->rasBase);
 431     }
 432     if (adjustAlpha) {
 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(&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 #ifdef MACOSX
 672                     case OGLC_VENDOR_ATI:
 673                         // see 8024461
 674                         viaTexture = JNI_TRUE;
 675                         break;
 676 #endif
 677                     default:
 678                         // just use the glDrawPixels() codepath
 679                         viaTexture = JNI_FALSE;
 680                         break;
 681                     }
 682                 }
 683 
 684                 if (viaTexture) {
 685                     OGLBlitToSurfaceViaTexture(oglc, &srcInfo, &pf, NULL,
 686                                                JNI_TRUE, hint,
 687                                                sx1, sy1, sx2, sy2,
 688                                                dx1, dy1, dx2, dy2);
 689                 } else {
 690                     OGLBlitSwToSurface(oglc, &srcInfo, &pf,
 691                                        sx1, sy1, sx2, sy2,
 692                                        dx1, dy1, dx2, dy2);
 693                 }
 694             }
 695 
 696             j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
 697             j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
 698             j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
 699             j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
 700         }
 701         SurfaceData_InvokeRelease(env, srcOps, &srcInfo);
 702     }
 703     SurfaceData_InvokeUnlock(env, srcOps, &srcInfo);
 704 }
 705 
 706 /**
 707  * This method makes vertical flip of the provided area of Surface and convert
 708  * pixel's data from argbPre to argb format if requested.
 709  */
 710 void flip(void *pDst, juint w, juint h, jint scanStride, jboolean convert) {
 711     const size_t clippedStride = 4 * w;
 712     void *tempRow = (h > 1 && !convert) ? malloc(clippedStride) : NULL;
 713     juint i = 0;
 714     juint step = 0;
 715     // vertical flip and convert argbpre to argb if necessary
 716     for (; i < h / 2; ++i) {
 717         juint *r1 = PtrAddBytes(pDst, (i * scanStride));
 718         juint *r2 = PtrAddBytes(pDst, (h - i - 1) * scanStride);
 719         if (tempRow) {
 720             // fast path
 721             memcpy(tempRow, r1, clippedStride);
 722             memcpy(r1, r2, clippedStride);
 723             memcpy(r2, tempRow, clippedStride);
 724         } else {
 725             // slow path
 726             for (step = 0; step < w; ++step) {
 727                 juint tmp = r1[step];
 728                 if (convert) {
 729                     LoadIntArgbPreTo1IntArgb(r2, 0, step, r1[step]);
 730                     LoadIntArgbPreTo1IntArgb(&tmp, 0, 0, r2[step]);
 731                 } else {
 732                     r1[step] = r2[step];
 733                     r2[step] = tmp;
 734                 }
 735             }
 736         }
 737     }
 738     // convert the middle line if necessary
 739     if (convert && h % 2) {
 740         juint *r1 = PtrAddBytes(pDst, (i * scanStride));
 741         for (step = 0; step < w; ++step) {
 742             LoadIntArgbPreTo1IntArgb(r1, 0, step, r1[step]);
 743         }
 744     }
 745     if (tempRow) {
 746         free(tempRow);
 747     }
 748 }
 749 
 750 /**
 751  * Specialized blit method for copying a native OpenGL "Surface" (pbuffer,
 752  * window, etc.) to a system memory ("Sw") surface.
 753  */
 754 void
 755 OGLBlitLoops_SurfaceToSwBlit(JNIEnv *env, OGLContext *oglc,
 756                              jlong pSrcOps, jlong pDstOps, jint dsttype,
 757                              jint srcx, jint srcy, jint dstx, jint dsty,
 758                              jint width, jint height)
 759 {
 760     OGLSDOps *srcOps = (OGLSDOps *)jlong_to_ptr(pSrcOps);
 761     SurfaceDataOps *dstOps = (SurfaceDataOps *)jlong_to_ptr(pDstOps);
 762     SurfaceDataRasInfo srcInfo, dstInfo;
 763     OGLPixelFormat pf = PixelFormats[dsttype];
 764 
 765     J2dTraceLn(J2D_TRACE_INFO, "OGLBlitLoops_SurfaceToSwBlit");
 766 
 767     if (width <= 0 || height <= 0) {
 768         J2dTraceLn(J2D_TRACE_WARNING,
 769             "OGLBlitLoops_SurfaceToSwBlit: dimensions are non-positive");
 770         return;
 771     }
 772 
 773     RETURN_IF_NULL(srcOps);
 774     RETURN_IF_NULL(dstOps);
 775     RETURN_IF_NULL(oglc);
 776     RESET_PREVIOUS_OP();
 777 
 778     srcInfo.bounds.x1 = srcx;
 779     srcInfo.bounds.y1 = srcy;
 780     srcInfo.bounds.x2 = srcx + width;
 781     srcInfo.bounds.y2 = srcy + height;
 782     dstInfo.bounds.x1 = dstx;
 783     dstInfo.bounds.y1 = dsty;
 784     dstInfo.bounds.x2 = dstx + width;
 785     dstInfo.bounds.y2 = dsty + height;
 786 
 787     if (dstOps->Lock(env, dstOps, &dstInfo, SD_LOCK_WRITE) != SD_SUCCESS) {
 788         J2dTraceLn(J2D_TRACE_WARNING,
 789             "OGLBlitLoops_SurfaceToSwBlit: could not acquire dst lock");
 790         return;
 791     }
 792 
 793     SurfaceData_IntersectBoundsXYXY(&srcInfo.bounds,
 794                                     0, 0, srcOps->width, srcOps->height);
 795     SurfaceData_IntersectBlitBounds(&dstInfo.bounds, &srcInfo.bounds,
 796                                     srcx - dstx, srcy - dsty);
 797 
 798     if (srcInfo.bounds.x2 > srcInfo.bounds.x1 &&
 799         srcInfo.bounds.y2 > srcInfo.bounds.y1)
 800     {
 801         dstOps->GetRasInfo(env, dstOps, &dstInfo);
 802         if (dstInfo.rasBase) {
 803             void *pDst = dstInfo.rasBase;
 804 
 805             srcx = srcInfo.bounds.x1;
 806             srcy = srcInfo.bounds.y1;
 807             dstx = dstInfo.bounds.x1;
 808             dsty = dstInfo.bounds.y1;
 809             width = srcInfo.bounds.x2 - srcInfo.bounds.x1;
 810             height = srcInfo.bounds.y2 - srcInfo.bounds.y1;
 811 
 812             pDst = PtrAddBytes(pDst, dstx * dstInfo.pixelStride);
 813             pDst = PtrAddBytes(pDst, dsty * dstInfo.scanStride);
 814 
 815             j2d_glPixelStorei(GL_PACK_ROW_LENGTH,
 816                               dstInfo.scanStride / dstInfo.pixelStride);
 817             j2d_glPixelStorei(GL_PACK_ALIGNMENT, pf.alignment);
 818 #ifdef MACOSX
 819             if (srcOps->isOpaque) {
 820                 // For some reason Apple's OpenGL implementation will
 821                 // read back zero values from the alpha channel of an
 822                 // opaque surface when using glReadPixels(), so here we
 823                 // force the resulting pixels to be fully opaque.
 824                 j2d_glPixelTransferf(GL_ALPHA_BIAS, 1.0);
 825             }
 826 #endif
 827 
 828             J2dTraceLn4(J2D_TRACE_VERBOSE, "  sx=%d sy=%d w=%d h=%d",
 829                         srcx, srcy, width, height);
 830             J2dTraceLn2(J2D_TRACE_VERBOSE, "  dx=%d dy=%d",
 831                         dstx, dsty);
 832 
 833             // this accounts for lower-left origin of the source region
 834             srcx = srcOps->xOffset + srcx;
 835             srcy = srcOps->yOffset + srcOps->height - srcy - height;
 836 
 837             // Note that glReadPixels() is extremely slow!
 838             // So we call it only once and flip the image using memcpy.
 839             j2d_glReadPixels(srcx, srcy, width, height,
 840                              pf.format, pf.type, pDst);
 841             // It was checked above that width and height are positive.
 842             flip(pDst, (juint) width, (juint) height, dstInfo.scanStride,
 843                  !pf.isPremult && !srcOps->isOpaque);
 844 #ifdef MACOSX
 845             if (srcOps->isOpaque) {
 846                 j2d_glPixelTransferf(GL_ALPHA_BIAS, 0.0);
 847             }
 848 #endif
 849             j2d_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
 850             j2d_glPixelStorei(GL_PACK_ALIGNMENT, 4);
 851         }
 852         SurfaceData_InvokeRelease(env, dstOps, &dstInfo);
 853     }
 854     SurfaceData_InvokeUnlock(env, dstOps, &dstInfo);
 855 }
 856 
 857 void
 858 OGLBlitLoops_CopyArea(JNIEnv *env,
 859                       OGLContext *oglc, OGLSDOps *dstOps,
 860                       jint x, jint y, jint width, jint height,
 861                       jint dx, jint dy)
 862 {
 863     SurfaceDataBounds srcBounds, dstBounds;
 864 
 865     J2dTraceLn(J2D_TRACE_INFO, "OGLBlitLoops_CopyArea");
 866 
 867     RETURN_IF_NULL(oglc);
 868     RETURN_IF_NULL(dstOps);
 869     RESET_PREVIOUS_OP();
 870 
 871     J2dTraceLn4(J2D_TRACE_VERBOSE, "  x=%d y=%d w=%d h=%d",
 872                 x, y, width, height);
 873     J2dTraceLn2(J2D_TRACE_VERBOSE, "  dx=%d dy=%d",
 874                 dx, dy);
 875 
 876     srcBounds.x1 = x;
 877     srcBounds.y1 = y;
 878     srcBounds.x2 = srcBounds.x1 + width;
 879     srcBounds.y2 = srcBounds.y1 + height;
 880     dstBounds.x1 = x + dx;
 881     dstBounds.y1 = y + dy;
 882     dstBounds.x2 = dstBounds.x1 + width;
 883     dstBounds.y2 = dstBounds.y1 + height;
 884 
 885     // 6430601: manually clip src/dst parameters to work around
 886     // some bugs in Sun's and Apple's OpenGL implementations
 887     // (it's a good idea to restrict the source parameters anyway, since
 888     // passing out of range parameters to glCopyPixels() will result in
 889     // an OpenGL error)
 890     SurfaceData_IntersectBoundsXYXY(&srcBounds,
 891                                     0, 0, dstOps->width, dstOps->height);
 892     SurfaceData_IntersectBoundsXYXY(&dstBounds,
 893                                     0, 0, dstOps->width, dstOps->height);
 894     SurfaceData_IntersectBlitBounds(&dstBounds, &srcBounds, -dx, -dy);
 895 
 896     if (dstBounds.x1 < dstBounds.x2 && dstBounds.y1 < dstBounds.y2) {
 897 #ifdef MACOSX
 898         if (dstOps->isOpaque) {
 899             // For some reason Apple's OpenGL implementation will fail
 900             // to render glCopyPixels() when the src/dst rectangles are
 901             // overlapping and glColorMask() has disabled writes to the
 902             // alpha channel.  The workaround is to temporarily re-enable
 903             // the alpha channel during the glCopyPixels() operation.
 904             j2d_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
 905         }
 906 #endif
 907 
 908         OGLBlitSurfaceToSurface(oglc, dstOps, dstOps,
 909                                 srcBounds.x1, srcBounds.y1,
 910                                 srcBounds.x2, srcBounds.y2,
 911                                 dstBounds.x1, dstBounds.y1,
 912                                 dstBounds.x2, dstBounds.y2);
 913 #ifdef MACOSX
 914         if (dstOps->isOpaque) {
 915             j2d_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
 916         }
 917 #endif
 918     }
 919 }
 920 
 921 #endif /* !HEADLESS */