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