Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/native/sun/java2d/opengl/OGLBlitLoops.c
38918 views
/*1* Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#ifndef HEADLESS2627#include <jni.h>28#include <jlong.h>2930#include "SurfaceData.h"31#include "OGLBlitLoops.h"32#include "OGLRenderQueue.h"33#include "OGLSurfaceData.h"34#include "GraphicsPrimitiveMgr.h"3536#include <stdlib.h> // malloc37#include <string.h> // memcpy38#include "IntArgbPre.h"3940extern OGLPixelFormat PixelFormats[];4142/**43* Inner loop used for copying a source OpenGL "Surface" (window, pbuffer,44* etc.) to a destination OpenGL "Surface". Note that the same surface can45* 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 as47* OGLBlitLoops_CopyArea().48*49* The standard glCopyPixels() mechanism is used to copy the source region50* into the destination region. If the regions have different dimensions,51* the source will be scaled into the destination as appropriate (only52* nearest neighbor filtering will be applied for simple scale operations).53*/54static void55OGLBlitSurfaceToSurface(OGLContext *oglc, OGLSDOps *srcOps, OGLSDOps *dstOps,56jint sx1, jint sy1, jint sx2, jint sy2,57jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)58{59GLfloat scalex, scaley;60jint srcw = sx2 - sx1;61jint srch = sy2 - sy1;6263scalex = ((GLfloat)(dx2-dx1)) / srcw;64scaley = ((GLfloat)(dy2-dy1)) / srch;6566// the following lines account for the fact that glCopyPixels() copies a67// region whose lower-left corner is at (x,y), but the source parameters68// (sx1,sy1) we are given here point to the upper-left corner of the69// source region... so here we play with the sy1 and dy1 parameters so70// that they point to the lower-left corners of the regions...71sx1 = srcOps->xOffset + sx1;72sy1 = srcOps->yOffset + srcOps->height - sy2;73dy1 = dy2;7475if (oglc->extraAlpha != 1.0f) {76OGLContext_SetExtraAlpha(oglc->extraAlpha);77}7879// see OGLBlitSwToSurface() for more info on the following two lines80j2d_glRasterPos2i(0, 0);81j2d_glBitmap(0, 0, 0, 0, (GLfloat)dx1, (GLfloat)-dy1, NULL);8283if (scalex == 1.0f && scaley == 1.0f) {84j2d_glCopyPixels(sx1, sy1, srcw, srch, GL_COLOR);85} else {86j2d_glPixelZoom(scalex, scaley);87j2d_glCopyPixels(sx1, sy1, srcw, srch, GL_COLOR);88j2d_glPixelZoom(1.0f, 1.0f);89}9091if (oglc->extraAlpha != 1.0f) {92OGLContext_SetExtraAlpha(1.0f);93}94}9596/**97* Inner loop used for copying a source OpenGL "Texture" to a destination98* OpenGL "Surface". This method is invoked from OGLBlitLoops_IsoBlit().99*100* This method will copy, scale, or transform the source texture into the101* destination depending on the transform state, as established in102* and OGLContext_SetTransform(). If the source texture is103* transformed in any way when rendered into the destination, the filtering104* method applied is determined by the hint parameter (can be GL_NEAREST or105* GL_LINEAR).106*/107static void108OGLBlitTextureToSurface(OGLContext *oglc,109OGLSDOps *srcOps, OGLSDOps *dstOps,110jboolean rtt, jint hint,111jint sx1, jint sy1, jint sx2, jint sy2,112jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)113{114GLdouble tx1, ty1, tx2, ty2;115116if (rtt) {117/*118* The source is a render-to-texture surface. These surfaces differ119* from regular texture objects in that the bottom scanline (of120* the actual image content) coincides with the top edge of the121* texture object. Therefore, we need to adjust the sy1/sy2122* coordinates relative to the top scanline of the image content.123*124* In texture coordinates, the top-left corner of the image content125* would be at:126* (0.0, (imgHeight/texHeight))127* while the bottom-right corner corresponds to:128* ((imgWidth/texWidth), 0.0)129*/130sy1 = srcOps->height - sy1;131sy2 = srcOps->height - sy2;132}133134if (srcOps->textureTarget == GL_TEXTURE_RECTANGLE_ARB) {135// The GL_ARB_texture_rectangle extension requires that we specify136// texture coordinates in the range [0,srcw] and [0,srch] instead of137// [0,1] as we would normally do in the case of GL_TEXTURE_2D138tx1 = (GLdouble)sx1;139ty1 = (GLdouble)sy1;140tx2 = (GLdouble)sx2;141ty2 = (GLdouble)sy2;142} else {143// Otherwise we need to convert the source bounds into the range [0,1]144tx1 = ((GLdouble)sx1) / srcOps->textureWidth;145ty1 = ((GLdouble)sy1) / srcOps->textureHeight;146tx2 = ((GLdouble)sx2) / srcOps->textureWidth;147ty2 = ((GLdouble)sy2) / srcOps->textureHeight;148}149150// Note that we call CHECK_PREVIOUS_OP(texTarget) in IsoBlit(), which151// will call glEnable(texTarget) as necessary.152j2d_glBindTexture(srcOps->textureTarget, srcOps->textureID);153OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE);154OGLSD_UPDATE_TEXTURE_FILTER(srcOps, hint);155156j2d_glBegin(GL_QUADS);157j2d_glTexCoord2d(tx1, ty1); j2d_glVertex2d(dx1, dy1);158j2d_glTexCoord2d(tx2, ty1); j2d_glVertex2d(dx2, dy1);159j2d_glTexCoord2d(tx2, ty2); j2d_glVertex2d(dx2, dy2);160j2d_glTexCoord2d(tx1, ty2); j2d_glVertex2d(dx1, dy2);161j2d_glEnd();162}163164/**165* Inner loop used for copying a source system memory ("Sw") surface to a166* destination OpenGL "Surface". This method is invoked from167* OGLBlitLoops_Blit().168*169* The standard glDrawPixels() mechanism is used to copy the source region170* into the destination region. If the regions have different171* dimensions, the source will be scaled into the destination172* as appropriate (only nearest neighbor filtering will be applied for simple173* scale operations).174*/175static void176OGLBlitSwToSurface(OGLContext *oglc, SurfaceDataRasInfo *srcInfo,177OGLPixelFormat *pf,178jint sx1, jint sy1, jint sx2, jint sy2,179jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)180{181GLfloat scalex, scaley;182GLvoid *pSrc;183184scalex = ((GLfloat)(dx2-dx1)) / (sx2-sx1);185scaley = ((GLfloat)(dy2-dy1)) / (sy2-sy1);186187if (oglc->extraAlpha != 1.0f) {188OGLContext_SetExtraAlpha(oglc->extraAlpha);189}190if (!pf->hasAlpha) {191// if the source surface does not have an alpha channel,192// we need to ensure that the alpha values are forced to193// the current extra alpha value (see OGLContext_SetExtraAlpha()194// for more information)195j2d_glPixelTransferf(GL_ALPHA_SCALE, 0.0f);196j2d_glPixelTransferf(GL_ALPHA_BIAS, oglc->extraAlpha);197}198199// This is a rather intriguing (yet totally valid) hack... If we were to200// specify a raster position that is outside the surface bounds, the raster201// position would be invalid and nothing would be rendered. However, we202// can use a widely known trick to move the raster position outside the203// surface bounds while maintaining its status as valid. The following204// call to glBitmap() renders a no-op bitmap, but offsets the current205// raster position from (0,0) to the desired location of (dx1,-dy1)...206j2d_glRasterPos2i(0, 0);207j2d_glBitmap(0, 0, 0, 0, (GLfloat)dx1, (GLfloat)-dy1, NULL);208209j2d_glPixelZoom(scalex, -scaley);210211pSrc = PtrCoord(srcInfo->rasBase, sx1, srcInfo->pixelStride,212sy1, srcInfo->scanStride);213214// in case pixel stride is not a multiple of scanline stride the copy215// has to be done line by line (see 6207877)216if (srcInfo->scanStride % srcInfo->pixelStride != 0) {217jint width = sx2-sx1;218jint height = sy2-sy1;219while (height > 0) {220j2d_glDrawPixels(width, 1, pf->format, pf->type, pSrc);221j2d_glBitmap(0, 0, 0, 0, (GLfloat)0, (GLfloat)-scaley, NULL);222pSrc = PtrAddBytes(pSrc, srcInfo->scanStride);223height--;224}225} else {226j2d_glDrawPixels(sx2-sx1, sy2-sy1, pf->format, pf->type, pSrc);227}228229j2d_glPixelZoom(1.0, 1.0);230231if (oglc->extraAlpha != 1.0f) {232OGLContext_SetExtraAlpha(1.0f);233}234if (!pf->hasAlpha) {235// restore scale/bias to their original values236j2d_glPixelTransferf(GL_ALPHA_SCALE, 1.0f);237j2d_glPixelTransferf(GL_ALPHA_BIAS, 0.0f);238}239}240241/**242* Inner loop used for copying a source system memory ("Sw") surface or243* OpenGL "Surface" to a destination OpenGL "Surface", using an OpenGL texture244* tile as an intermediate surface. This method is invoked from245* OGLBlitLoops_Blit() for "Sw" surfaces and OGLBlitLoops_IsoBlit() for246* "Surface" surfaces.247*248* This method is used to transform the source surface into the destination.249* Pixel rectangles cannot be arbitrarily transformed (without the250* GL_EXT_pixel_transform extension, which is not supported on most modern251* hardware). However, texture mapped quads do respect the GL_MODELVIEW252* transform matrix, so we use textures here to perform the transform253* operation. This method uses a tile-based approach in which a small254* subregion of the source surface is copied into a cached texture tile. The255* texture tile is then mapped into the appropriate location in the256* destination surface.257*258* REMIND: this only works well using GL_NEAREST for the filtering mode259* (GL_LINEAR causes visible stitching problems between tiles,260* but this can be fixed by making use of texture borders)261*/262static void263OGLBlitToSurfaceViaTexture(OGLContext *oglc, SurfaceDataRasInfo *srcInfo,264OGLPixelFormat *pf, OGLSDOps *srcOps,265jboolean swsurface, jint hint,266jint sx1, jint sy1, jint sx2, jint sy2,267jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)268{269GLdouble tx1, ty1, tx2, ty2;270GLdouble dx, dy, dw, dh, cdw, cdh;271jint tw, th;272jint sx, sy, sw, sh;273GLint glhint = (hint == OGLSD_XFORM_BILINEAR) ? GL_LINEAR : GL_NEAREST;274jboolean adjustAlpha = (pf != NULL && !pf->hasAlpha);275jboolean slowPath;276277if (oglc->blitTextureID == 0) {278if (!OGLContext_InitBlitTileTexture(oglc)) {279J2dRlsTraceLn(J2D_TRACE_ERROR,280"OGLBlitToSurfaceViaTexture: could not init blit tile");281return;282}283}284285tx1 = 0.0f;286ty1 = 0.0f;287tw = OGLC_BLIT_TILE_SIZE;288th = OGLC_BLIT_TILE_SIZE;289cdw = (dx2-dx1) / (((GLdouble)(sx2-sx1)) / OGLC_BLIT_TILE_SIZE);290cdh = (dy2-dy1) / (((GLdouble)(sy2-sy1)) / OGLC_BLIT_TILE_SIZE);291292j2d_glEnable(GL_TEXTURE_2D);293j2d_glBindTexture(GL_TEXTURE_2D, oglc->blitTextureID);294OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE);295j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, glhint);296j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, glhint);297298if (adjustAlpha) {299// if the source surface does not have an alpha channel,300// we need to ensure that the alpha values are forced to 1.0f301j2d_glPixelTransferf(GL_ALPHA_SCALE, 0.0f);302j2d_glPixelTransferf(GL_ALPHA_BIAS, 1.0f);303}304305// in case pixel stride is not a multiple of scanline stride the copy306// has to be done line by line (see 6207877)307slowPath = srcInfo->scanStride % srcInfo->pixelStride != 0;308309for (sy = sy1, dy = dy1; sy < sy2; sy += th, dy += cdh) {310sh = ((sy + th) > sy2) ? (sy2 - sy) : th;311dh = ((dy + cdh) > dy2) ? (dy2 - dy) : cdh;312313for (sx = sx1, dx = dx1; sx < sx2; sx += tw, dx += cdw) {314sw = ((sx + tw) > sx2) ? (sx2 - sx) : tw;315dw = ((dx + cdw) > dx2) ? (dx2 - dx) : cdw;316317tx2 = ((GLdouble)sw) / tw;318ty2 = ((GLdouble)sh) / th;319320if (swsurface) {321GLvoid *pSrc = PtrCoord(srcInfo->rasBase,322sx, srcInfo->pixelStride,323sy, srcInfo->scanStride);324if (slowPath) {325jint tmph = sh;326while (tmph > 0) {327j2d_glTexSubImage2D(GL_TEXTURE_2D, 0,3280, sh - tmph, sw, 1,329pf->format, pf->type,330pSrc);331pSrc = PtrAddBytes(pSrc, srcInfo->scanStride);332tmph--;333}334} else {335j2d_glTexSubImage2D(GL_TEXTURE_2D, 0,3360, 0, sw, sh,337pf->format, pf->type,338pSrc);339}340341// the texture image is "right side up", so we align the342// upper-left texture corner with the upper-left quad corner343j2d_glBegin(GL_QUADS);344j2d_glTexCoord2d(tx1, ty1); j2d_glVertex2d(dx, dy);345j2d_glTexCoord2d(tx2, ty1); j2d_glVertex2d(dx + dw, dy);346j2d_glTexCoord2d(tx2, ty2); j2d_glVertex2d(dx + dw, dy + dh);347j2d_glTexCoord2d(tx1, ty2); j2d_glVertex2d(dx, dy + dh);348j2d_glEnd();349} else {350// this accounts for lower-left origin of the source region351jint newsx = srcOps->xOffset + sx;352jint newsy = srcOps->yOffset + srcOps->height - (sy + sh);353j2d_glCopyTexSubImage2D(GL_TEXTURE_2D, 0,3540, 0, newsx, newsy, sw, sh);355356// the texture image is "upside down" after the last step, so357// we align the bottom-left texture corner with the upper-left358// quad corner (and vice versa) to effectively flip the359// texture image360j2d_glBegin(GL_QUADS);361j2d_glTexCoord2d(tx1, ty2); j2d_glVertex2d(dx, dy);362j2d_glTexCoord2d(tx2, ty2); j2d_glVertex2d(dx + dw, dy);363j2d_glTexCoord2d(tx2, ty1); j2d_glVertex2d(dx + dw, dy + dh);364j2d_glTexCoord2d(tx1, ty1); j2d_glVertex2d(dx, dy + dh);365j2d_glEnd();366}367}368}369370if (adjustAlpha) {371// restore scale/bias to their original values372j2d_glPixelTransferf(GL_ALPHA_SCALE, 1.0f);373j2d_glPixelTransferf(GL_ALPHA_BIAS, 0.0f);374}375376j2d_glDisable(GL_TEXTURE_2D);377}378379/**380* Inner loop used for copying a source system memory ("Sw") surface to a381* destination OpenGL "Texture". This method is invoked from382* OGLBlitLoops_Blit().383*384* The source surface is effectively loaded into the OpenGL texture object,385* which must have already been initialized by OGLSD_initTexture(). Note386* that this method is only capable of copying the source surface into the387* destination surface (i.e. no scaling or general transform is allowed).388* This restriction should not be an issue as this method is only used389* currently to cache a static system memory image into an OpenGL texture in390* a hidden-acceleration situation.391*/392static void393OGLBlitSwToTexture(SurfaceDataRasInfo *srcInfo, OGLPixelFormat *pf,394OGLSDOps *dstOps,395jint dx1, jint dy1, jint dx2, jint dy2)396{397jboolean adjustAlpha = (pf != NULL && !pf->hasAlpha);398j2d_glBindTexture(dstOps->textureTarget, dstOps->textureID);399400if (adjustAlpha) {401// if the source surface does not have an alpha channel,402// we need to ensure that the alpha values are forced to 1.0f403j2d_glPixelTransferf(GL_ALPHA_SCALE, 0.0f);404j2d_glPixelTransferf(GL_ALPHA_BIAS, 1.0f);405}406407// in case pixel stride is not a multiple of scanline stride the copy408// has to be done line by line (see 6207877)409if (srcInfo->scanStride % srcInfo->pixelStride != 0) {410jint width = dx2 - dx1;411jint height = dy2 - dy1;412GLvoid *pSrc = srcInfo->rasBase;413414while (height > 0) {415j2d_glTexSubImage2D(dstOps->textureTarget, 0,416dx1, dy2 - height, width, 1,417pf->format, pf->type, pSrc);418pSrc = PtrAddBytes(pSrc, srcInfo->scanStride);419height--;420}421} else {422j2d_glTexSubImage2D(dstOps->textureTarget, 0,423dx1, dy1, dx2-dx1, dy2-dy1,424pf->format, pf->type, srcInfo->rasBase);425}426if (adjustAlpha) {427// restore scale/bias to their original values428j2d_glPixelTransferf(GL_ALPHA_SCALE, 1.0f);429j2d_glPixelTransferf(GL_ALPHA_BIAS, 0.0f);430}431}432433/**434* General blit method for copying a native OpenGL surface (of type "Surface"435* or "Texture") to another OpenGL "Surface". If texture is JNI_TRUE, this436* method will invoke the Texture->Surface inner loop; otherwise, one of the437* Surface->Surface inner loops will be invoked, depending on the transform438* state.439*440* REMIND: we can trick these blit methods into doing XOR simply by passing441* in the (pixel ^ xorpixel) as the pixel value and preceding the442* blit with a fillrect...443*/444void445OGLBlitLoops_IsoBlit(JNIEnv *env,446OGLContext *oglc, jlong pSrcOps, jlong pDstOps,447jboolean xform, jint hint,448jboolean texture, jboolean rtt,449jint sx1, jint sy1, jint sx2, jint sy2,450jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)451{452OGLSDOps *srcOps = (OGLSDOps *)jlong_to_ptr(pSrcOps);453OGLSDOps *dstOps = (OGLSDOps *)jlong_to_ptr(pDstOps);454SurfaceDataRasInfo srcInfo;455jint sw = sx2 - sx1;456jint sh = sy2 - sy1;457jdouble dw = dx2 - dx1;458jdouble dh = dy2 - dy1;459460J2dTraceLn(J2D_TRACE_INFO, "OGLBlitLoops_IsoBlit");461462if (sw <= 0 || sh <= 0 || dw <= 0 || dh <= 0) {463J2dTraceLn(J2D_TRACE_WARNING,464"OGLBlitLoops_IsoBlit: invalid dimensions");465return;466}467468RETURN_IF_NULL(srcOps);469RETURN_IF_NULL(dstOps);470RETURN_IF_NULL(oglc);471472srcInfo.bounds.x1 = sx1;473srcInfo.bounds.y1 = sy1;474srcInfo.bounds.x2 = sx2;475srcInfo.bounds.y2 = sy2;476477SurfaceData_IntersectBoundsXYXY(&srcInfo.bounds,4780, 0, srcOps->width, srcOps->height);479480if (srcInfo.bounds.x2 > srcInfo.bounds.x1 &&481srcInfo.bounds.y2 > srcInfo.bounds.y1)482{483if (srcInfo.bounds.x1 != sx1) {484dx1 += (srcInfo.bounds.x1 - sx1) * (dw / sw);485sx1 = srcInfo.bounds.x1;486}487if (srcInfo.bounds.y1 != sy1) {488dy1 += (srcInfo.bounds.y1 - sy1) * (dh / sh);489sy1 = srcInfo.bounds.y1;490}491if (srcInfo.bounds.x2 != sx2) {492dx2 += (srcInfo.bounds.x2 - sx2) * (dw / sw);493sx2 = srcInfo.bounds.x2;494}495if (srcInfo.bounds.y2 != sy2) {496dy2 += (srcInfo.bounds.y2 - sy2) * (dh / sh);497sy2 = srcInfo.bounds.y2;498}499500J2dTraceLn2(J2D_TRACE_VERBOSE, " texture=%d hint=%d", texture, hint);501J2dTraceLn4(J2D_TRACE_VERBOSE, " sx1=%d sy1=%d sx2=%d sy2=%d",502sx1, sy1, sx2, sy2);503J2dTraceLn4(J2D_TRACE_VERBOSE, " dx1=%f dy1=%f dx2=%f dy2=%f",504dx1, dy1, dx2, dy2);505506if (texture) {507GLint glhint = (hint == OGLSD_XFORM_BILINEAR) ? GL_LINEAR :508GL_NEAREST;509CHECK_PREVIOUS_OP(srcOps->textureTarget);510OGLBlitTextureToSurface(oglc, srcOps, dstOps, rtt, glhint,511sx1, sy1, sx2, sy2,512dx1, dy1, dx2, dy2);513} else {514jboolean viaTexture;515if (xform) {516// we must use the via-texture codepath when there is a xform517viaTexture = JNI_TRUE;518} else {519// look at the vendor to see which codepath is faster520// (this has been empirically determined; see 5020009)521switch (OGLC_GET_VENDOR(oglc)) {522case OGLC_VENDOR_NVIDIA:523// the via-texture codepath tends to be faster when524// there is either a simple scale OR an extra alpha525viaTexture =526(sx2-sx1) != (jint)(dx2-dx1) ||527(sy2-sy1) != (jint)(dy2-dy1) ||528oglc->extraAlpha != 1.0f;529break;530531case OGLC_VENDOR_ATI:532// the via-texture codepath tends to be faster only when533// there is an extra alpha involved (scaling or not)534viaTexture = (oglc->extraAlpha != 1.0f);535break;536537default:538// just use the glCopyPixels() codepath539viaTexture = JNI_FALSE;540break;541}542}543544RESET_PREVIOUS_OP();545if (viaTexture) {546OGLBlitToSurfaceViaTexture(oglc, &srcInfo, NULL, srcOps,547JNI_FALSE, hint,548sx1, sy1, sx2, sy2,549dx1, dy1, dx2, dy2);550} else {551OGLBlitSurfaceToSurface(oglc, srcOps, dstOps,552sx1, sy1, sx2, sy2,553dx1, dy1, dx2, dy2);554}555}556}557}558559/**560* General blit method for copying a system memory ("Sw") surface to a native561* OpenGL surface (of type "Surface" or "Texture"). If texture is JNI_TRUE,562* this method will invoke the Sw->Texture inner loop; otherwise, one of the563* Sw->Surface inner loops will be invoked, depending on the transform state.564*/565void566OGLBlitLoops_Blit(JNIEnv *env,567OGLContext *oglc, jlong pSrcOps, jlong pDstOps,568jboolean xform, jint hint,569jint srctype, jboolean texture,570jint sx1, jint sy1, jint sx2, jint sy2,571jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)572{573SurfaceDataOps *srcOps = (SurfaceDataOps *)jlong_to_ptr(pSrcOps);574OGLSDOps *dstOps = (OGLSDOps *)jlong_to_ptr(pDstOps);575SurfaceDataRasInfo srcInfo;576OGLPixelFormat pf = PixelFormats[srctype];577jint sw = sx2 - sx1;578jint sh = sy2 - sy1;579jdouble dw = dx2 - dx1;580jdouble dh = dy2 - dy1;581582J2dTraceLn(J2D_TRACE_INFO, "OGLBlitLoops_Blit");583584if (sw <= 0 || sh <= 0 || dw <= 0 || dh <= 0 || srctype < 0) {585J2dTraceLn(J2D_TRACE_WARNING,586"OGLBlitLoops_Blit: invalid dimensions or srctype");587return;588}589590RETURN_IF_NULL(srcOps);591RETURN_IF_NULL(dstOps);592RETURN_IF_NULL(oglc);593RESET_PREVIOUS_OP();594595srcInfo.bounds.x1 = sx1;596srcInfo.bounds.y1 = sy1;597srcInfo.bounds.x2 = sx2;598srcInfo.bounds.y2 = sy2;599600if (srcOps->Lock(env, srcOps, &srcInfo, SD_LOCK_READ) != SD_SUCCESS) {601J2dTraceLn(J2D_TRACE_WARNING,602"OGLBlitLoops_Blit: could not acquire lock");603return;604}605606if (srcInfo.bounds.x2 > srcInfo.bounds.x1 &&607srcInfo.bounds.y2 > srcInfo.bounds.y1)608{609srcOps->GetRasInfo(env, srcOps, &srcInfo);610if (srcInfo.rasBase) {611if (srcInfo.bounds.x1 != sx1) {612dx1 += (srcInfo.bounds.x1 - sx1) * (dw / sw);613sx1 = srcInfo.bounds.x1;614}615if (srcInfo.bounds.y1 != sy1) {616dy1 += (srcInfo.bounds.y1 - sy1) * (dh / sh);617sy1 = srcInfo.bounds.y1;618}619if (srcInfo.bounds.x2 != sx2) {620dx2 += (srcInfo.bounds.x2 - sx2) * (dw / sw);621sx2 = srcInfo.bounds.x2;622}623if (srcInfo.bounds.y2 != sy2) {624dy2 += (srcInfo.bounds.y2 - sy2) * (dh / sh);625sy2 = srcInfo.bounds.y2;626}627628J2dTraceLn3(J2D_TRACE_VERBOSE, " texture=%d srctype=%d hint=%d",629texture, srctype, hint);630J2dTraceLn4(J2D_TRACE_VERBOSE, " sx1=%d sy1=%d sx2=%d sy2=%d",631sx1, sy1, sx2, sy2);632J2dTraceLn4(J2D_TRACE_VERBOSE, " dx1=%f dy1=%f dx2=%f dy2=%f",633dx1, dy1, dx2, dy2);634635// Note: we will calculate x/y positions in the raster manually636j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);637j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);638j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH,639srcInfo.scanStride / srcInfo.pixelStride);640j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, pf.alignment);641642if (texture) {643// These coordinates will always be integers since we644// only ever do a straight copy from sw to texture.645// Thus these casts are "safe" - no loss of precision.646OGLBlitSwToTexture(&srcInfo, &pf, dstOps,647(jint)dx1, (jint)dy1, (jint)dx2, (jint)dy2);648} else {649jboolean viaTexture;650if (xform) {651// we must use the via-texture codepath when there652// is a xform653viaTexture = JNI_TRUE;654} else {655// look at the vendor to see which codepath is faster656// (this has been empirically determined; see 5020009)657switch (OGLC_GET_VENDOR(oglc)) {658case OGLC_VENDOR_NVIDIA:659// the via-texture codepath tends to be faster when660// there is either a simple scale OR an extra alpha661viaTexture =662(sx2-sx1) != (jint)(dx2-dx1) ||663(sy2-sy1) != (jint)(dy2-dy1) ||664oglc->extraAlpha != 1.0f;665break;666#ifdef MACOSX667case OGLC_VENDOR_ATI:668// see 8024461669viaTexture = JNI_TRUE;670break;671#endif672case OGLC_VENDOR_INTEL:673viaTexture = JNI_TRUE;674break;675default:676// just use the glDrawPixels() codepath677viaTexture = JNI_FALSE;678break;679}680}681682if (viaTexture) {683OGLBlitToSurfaceViaTexture(oglc, &srcInfo, &pf, NULL,684JNI_TRUE, hint,685sx1, sy1, sx2, sy2,686dx1, dy1, dx2, dy2);687} else {688OGLBlitSwToSurface(oglc, &srcInfo, &pf,689sx1, sy1, sx2, sy2,690dx1, dy1, dx2, dy2);691}692}693694j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);695j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 4);696}697SurfaceData_InvokeRelease(env, srcOps, &srcInfo);698}699SurfaceData_InvokeUnlock(env, srcOps, &srcInfo);700}701702/**703* This method makes vertical flip of the provided area of Surface and convert704* pixel's data from argbPre to argb format if requested.705*/706void flip(void *pDst, juint w, juint h, jint scanStride, jboolean convert) {707const size_t clippedStride = 4 * w;708void *tempRow = (h > 1 && !convert) ? malloc(clippedStride) : NULL;709juint i = 0;710juint step = 0;711// vertical flip and convert argbpre to argb if necessary712for (; i < h / 2; ++i) {713juint *r1 = PtrPixelsRow(pDst, i, scanStride);714juint *r2 = PtrPixelsRow(pDst, h - i - 1, scanStride);715if (tempRow) {716// fast path717memcpy(tempRow, r1, clippedStride);718memcpy(r1, r2, clippedStride);719memcpy(r2, tempRow, clippedStride);720} else {721// slow path722for (step = 0; step < w; ++step) {723juint tmp = r1[step];724if (convert) {725LoadIntArgbPreTo1IntArgb(r2, 0, step, r1[step]);726LoadIntArgbPreTo1IntArgb(&tmp, 0, 0, r2[step]);727} else {728r1[step] = r2[step];729r2[step] = tmp;730}731}732}733}734// convert the middle line if necessary735if (convert && h % 2) {736juint *r1 = PtrPixelsRow(pDst, i, scanStride);737for (step = 0; step < w; ++step) {738LoadIntArgbPreTo1IntArgb(r1, 0, step, r1[step]);739}740}741if (tempRow) {742free(tempRow);743}744}745746/**747* Specialized blit method for copying a native OpenGL "Surface" (pbuffer,748* window, etc.) to a system memory ("Sw") surface.749*/750void751OGLBlitLoops_SurfaceToSwBlit(JNIEnv *env, OGLContext *oglc,752jlong pSrcOps, jlong pDstOps, jint dsttype,753jint srcx, jint srcy, jint dstx, jint dsty,754jint width, jint height)755{756OGLSDOps *srcOps = (OGLSDOps *)jlong_to_ptr(pSrcOps);757SurfaceDataOps *dstOps = (SurfaceDataOps *)jlong_to_ptr(pDstOps);758SurfaceDataRasInfo srcInfo, dstInfo;759OGLPixelFormat pf = PixelFormats[dsttype];760761J2dTraceLn(J2D_TRACE_INFO, "OGLBlitLoops_SurfaceToSwBlit");762763if (width <= 0 || height <= 0) {764J2dTraceLn(J2D_TRACE_WARNING,765"OGLBlitLoops_SurfaceToSwBlit: dimensions are non-positive");766return;767}768769RETURN_IF_NULL(srcOps);770RETURN_IF_NULL(dstOps);771RETURN_IF_NULL(oglc);772RESET_PREVIOUS_OP();773774srcInfo.bounds.x1 = srcx;775srcInfo.bounds.y1 = srcy;776srcInfo.bounds.x2 = srcx + width;777srcInfo.bounds.y2 = srcy + height;778dstInfo.bounds.x1 = dstx;779dstInfo.bounds.y1 = dsty;780dstInfo.bounds.x2 = dstx + width;781dstInfo.bounds.y2 = dsty + height;782783if (dstOps->Lock(env, dstOps, &dstInfo, SD_LOCK_WRITE) != SD_SUCCESS) {784J2dTraceLn(J2D_TRACE_WARNING,785"OGLBlitLoops_SurfaceToSwBlit: could not acquire dst lock");786return;787}788789SurfaceData_IntersectBoundsXYXY(&srcInfo.bounds,7900, 0, srcOps->width, srcOps->height);791SurfaceData_IntersectBlitBounds(&dstInfo.bounds, &srcInfo.bounds,792srcx - dstx, srcy - dsty);793794if (srcInfo.bounds.x2 > srcInfo.bounds.x1 &&795srcInfo.bounds.y2 > srcInfo.bounds.y1)796{797dstOps->GetRasInfo(env, dstOps, &dstInfo);798if (dstInfo.rasBase) {799void *pDst = dstInfo.rasBase;800801srcx = srcInfo.bounds.x1;802srcy = srcInfo.bounds.y1;803dstx = dstInfo.bounds.x1;804dsty = dstInfo.bounds.y1;805width = srcInfo.bounds.x2 - srcInfo.bounds.x1;806height = srcInfo.bounds.y2 - srcInfo.bounds.y1;807808pDst = PtrAddBytes(pDst, dstx * dstInfo.pixelStride);809pDst = PtrPixelsRow(pDst, dsty, dstInfo.scanStride);810811j2d_glPixelStorei(GL_PACK_ROW_LENGTH,812dstInfo.scanStride / dstInfo.pixelStride);813j2d_glPixelStorei(GL_PACK_ALIGNMENT, pf.alignment);814#ifdef MACOSX815if (srcOps->isOpaque) {816// For some reason Apple's OpenGL implementation will817// read back zero values from the alpha channel of an818// opaque surface when using glReadPixels(), so here we819// force the resulting pixels to be fully opaque.820j2d_glPixelTransferf(GL_ALPHA_BIAS, 1.0);821}822#endif823824J2dTraceLn4(J2D_TRACE_VERBOSE, " sx=%d sy=%d w=%d h=%d",825srcx, srcy, width, height);826J2dTraceLn2(J2D_TRACE_VERBOSE, " dx=%d dy=%d",827dstx, dsty);828829// this accounts for lower-left origin of the source region830srcx = srcOps->xOffset + srcx;831srcy = srcOps->yOffset + srcOps->height - srcy - height;832833// Note that glReadPixels() is extremely slow!834// So we call it only once and flip the image using memcpy.835j2d_glReadPixels(srcx, srcy, width, height,836pf.format, pf.type, pDst);837// It was checked above that width and height are positive.838flip(pDst, (juint) width, (juint) height, dstInfo.scanStride,839!pf.isPremult && !srcOps->isOpaque);840#ifdef MACOSX841if (srcOps->isOpaque) {842j2d_glPixelTransferf(GL_ALPHA_BIAS, 0.0);843}844#endif845j2d_glPixelStorei(GL_PACK_ROW_LENGTH, 0);846j2d_glPixelStorei(GL_PACK_ALIGNMENT, 4);847}848SurfaceData_InvokeRelease(env, dstOps, &dstInfo);849}850SurfaceData_InvokeUnlock(env, dstOps, &dstInfo);851}852853void854OGLBlitLoops_CopyArea(JNIEnv *env,855OGLContext *oglc, OGLSDOps *dstOps,856jint x, jint y, jint width, jint height,857jint dx, jint dy)858{859SurfaceDataBounds srcBounds, dstBounds;860861J2dTraceLn(J2D_TRACE_INFO, "OGLBlitLoops_CopyArea");862863RETURN_IF_NULL(oglc);864RETURN_IF_NULL(dstOps);865RESET_PREVIOUS_OP();866867J2dTraceLn4(J2D_TRACE_VERBOSE, " x=%d y=%d w=%d h=%d",868x, y, width, height);869J2dTraceLn2(J2D_TRACE_VERBOSE, " dx=%d dy=%d",870dx, dy);871872srcBounds.x1 = x;873srcBounds.y1 = y;874srcBounds.x2 = srcBounds.x1 + width;875srcBounds.y2 = srcBounds.y1 + height;876dstBounds.x1 = x + dx;877dstBounds.y1 = y + dy;878dstBounds.x2 = dstBounds.x1 + width;879dstBounds.y2 = dstBounds.y1 + height;880881// 6430601: manually clip src/dst parameters to work around882// some bugs in Sun's and Apple's OpenGL implementations883// (it's a good idea to restrict the source parameters anyway, since884// passing out of range parameters to glCopyPixels() will result in885// an OpenGL error)886SurfaceData_IntersectBoundsXYXY(&srcBounds,8870, 0, dstOps->width, dstOps->height);888SurfaceData_IntersectBoundsXYXY(&dstBounds,8890, 0, dstOps->width, dstOps->height);890SurfaceData_IntersectBlitBounds(&dstBounds, &srcBounds, -dx, -dy);891892if (dstBounds.x1 < dstBounds.x2 && dstBounds.y1 < dstBounds.y2) {893#ifdef MACOSX894if (dstOps->isOpaque) {895// For some reason Apple's OpenGL implementation will fail896// to render glCopyPixels() when the src/dst rectangles are897// overlapping and glColorMask() has disabled writes to the898// alpha channel. The workaround is to temporarily re-enable899// the alpha channel during the glCopyPixels() operation.900j2d_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);901}902#endif903904OGLBlitSurfaceToSurface(oglc, dstOps, dstOps,905srcBounds.x1, srcBounds.y1,906srcBounds.x2, srcBounds.y2,907dstBounds.x1, dstBounds.y1,908dstBounds.x2, dstBounds.y2);909#ifdef MACOSX910if (dstOps->isOpaque) {911j2d_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);912}913#endif914}915}916917#endif /* !HEADLESS */918919920