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 */