Path: blob/master/src/java.desktop/share/native/common/java2d/opengl/OGLBlitLoops.c
41159 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;182183scalex = ((GLfloat)(dx2-dx1)) / (sx2-sx1);184scaley = ((GLfloat)(dy2-dy1)) / (sy2-sy1);185186if (oglc->extraAlpha != 1.0f) {187OGLContext_SetExtraAlpha(oglc->extraAlpha);188}189if (!pf->hasAlpha) {190// if the source surface does not have an alpha channel,191// we need to ensure that the alpha values are forced to192// the current extra alpha value (see OGLContext_SetExtraAlpha()193// for more information)194j2d_glPixelTransferf(GL_ALPHA_SCALE, 0.0f);195j2d_glPixelTransferf(GL_ALPHA_BIAS, oglc->extraAlpha);196}197198// This is a rather intriguing (yet totally valid) hack... If we were to199// specify a raster position that is outside the surface bounds, the raster200// position would be invalid and nothing would be rendered. However, we201// can use a widely known trick to move the raster position outside the202// surface bounds while maintaining its status as valid. The following203// call to glBitmap() renders a no-op bitmap, but offsets the current204// raster position from (0,0) to the desired location of (dx1,-dy1)...205j2d_glRasterPos2i(0, 0);206j2d_glBitmap(0, 0, 0, 0, (GLfloat)dx1, (GLfloat)-dy1, NULL);207208j2d_glPixelZoom(scalex, -scaley);209210GLvoid *pSrc = PtrCoord(srcInfo->rasBase, sx1, srcInfo->pixelStride,211sy1, srcInfo->scanStride);212213// in case pixel stride is not a multiple of scanline stride the copy214// has to be done line by line (see 6207877)215if (srcInfo->scanStride % srcInfo->pixelStride != 0) {216jint width = sx2-sx1;217jint height = sy2-sy1;218while (height > 0) {219j2d_glDrawPixels(width, 1, pf->format, pf->type, pSrc);220j2d_glBitmap(0, 0, 0, 0, (GLfloat)0, (GLfloat)-scaley, NULL);221pSrc = PtrAddBytes(pSrc, srcInfo->scanStride);222height--;223}224} else {225j2d_glDrawPixels(sx2-sx1, sy2-sy1, pf->format, pf->type, pSrc);226}227228j2d_glPixelZoom(1.0, 1.0);229230if (oglc->extraAlpha != 1.0f) {231OGLContext_SetExtraAlpha(1.0f);232}233if (!pf->hasAlpha) {234// restore scale/bias to their original values235j2d_glPixelTransferf(GL_ALPHA_SCALE, 1.0f);236j2d_glPixelTransferf(GL_ALPHA_BIAS, 0.0f);237}238}239240/**241* Inner loop used for copying a source system memory ("Sw") surface or242* OpenGL "Surface" to a destination OpenGL "Surface", using an OpenGL texture243* tile as an intermediate surface. This method is invoked from244* OGLBlitLoops_Blit() for "Sw" surfaces and OGLBlitLoops_IsoBlit() for245* "Surface" surfaces.246*247* This method is used to transform the source surface into the destination.248* Pixel rectangles cannot be arbitrarily transformed (without the249* GL_EXT_pixel_transform extension, which is not supported on most modern250* hardware). However, texture mapped quads do respect the GL_MODELVIEW251* transform matrix, so we use textures here to perform the transform252* operation. This method uses a tile-based approach in which a small253* subregion of the source surface is copied into a cached texture tile. The254* texture tile is then mapped into the appropriate location in the255* destination surface.256*257* REMIND: this only works well using GL_NEAREST for the filtering mode258* (GL_LINEAR causes visible stitching problems between tiles,259* but this can be fixed by making use of texture borders)260*/261static void262OGLBlitToSurfaceViaTexture(OGLContext *oglc, SurfaceDataRasInfo *srcInfo,263OGLPixelFormat *pf, OGLSDOps *srcOps,264jboolean swsurface, jint hint,265jint sx1, jint sy1, jint sx2, jint sy2,266jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)267{268GLdouble tx1, ty1, tx2, ty2;269GLdouble dx, dy, dw, dh, cdw, cdh;270jint tw, th;271jint sx, sy, sw, sh;272GLint glhint = (hint == OGLSD_XFORM_BILINEAR) ? GL_LINEAR : GL_NEAREST;273jboolean adjustAlpha = (pf != NULL && !pf->hasAlpha);274jboolean slowPath;275276if (oglc->blitTextureID == 0) {277if (!OGLContext_InitBlitTileTexture(oglc)) {278J2dRlsTraceLn(J2D_TRACE_ERROR,279"OGLBlitToSurfaceViaTexture: could not init blit tile");280return;281}282}283284tx1 = 0.0f;285ty1 = 0.0f;286tw = OGLC_BLIT_TILE_SIZE;287th = OGLC_BLIT_TILE_SIZE;288cdw = (dx2-dx1) / (((GLdouble)(sx2-sx1)) / OGLC_BLIT_TILE_SIZE);289cdh = (dy2-dy1) / (((GLdouble)(sy2-sy1)) / OGLC_BLIT_TILE_SIZE);290291j2d_glEnable(GL_TEXTURE_2D);292j2d_glBindTexture(GL_TEXTURE_2D, oglc->blitTextureID);293OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE);294j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, glhint);295j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, glhint);296297if (adjustAlpha) {298// if the source surface does not have an alpha channel,299// we need to ensure that the alpha values are forced to 1.0f300j2d_glPixelTransferf(GL_ALPHA_SCALE, 0.0f);301j2d_glPixelTransferf(GL_ALPHA_BIAS, 1.0f);302}303304// in case pixel stride is not a multiple of scanline stride the copy305// has to be done line by line (see 6207877)306slowPath = srcInfo->scanStride % srcInfo->pixelStride != 0;307308for (sy = sy1, dy = dy1; sy < sy2; sy += th, dy += cdh) {309sh = ((sy + th) > sy2) ? (sy2 - sy) : th;310dh = ((dy + cdh) > dy2) ? (dy2 - dy) : cdh;311312for (sx = sx1, dx = dx1; sx < sx2; sx += tw, dx += cdw) {313sw = ((sx + tw) > sx2) ? (sx2 - sx) : tw;314dw = ((dx + cdw) > dx2) ? (dx2 - dx) : cdw;315316tx2 = ((GLdouble)sw) / tw;317ty2 = ((GLdouble)sh) / th;318319if (swsurface) {320GLvoid *pSrc = PtrCoord(srcInfo->rasBase,321sx, srcInfo->pixelStride,322sy, srcInfo->scanStride);323if (slowPath) {324jint tmph = sh;325while (tmph > 0) {326j2d_glTexSubImage2D(GL_TEXTURE_2D, 0,3270, sh - tmph, sw, 1,328pf->format, pf->type,329pSrc);330pSrc = PtrAddBytes(pSrc, srcInfo->scanStride);331tmph--;332}333} else {334j2d_glTexSubImage2D(GL_TEXTURE_2D, 0,3350, 0, sw, sh,336pf->format, pf->type,337pSrc);338}339340// the texture image is "right side up", so we align the341// upper-left texture corner with the upper-left quad corner342j2d_glBegin(GL_QUADS);343j2d_glTexCoord2d(tx1, ty1); j2d_glVertex2d(dx, dy);344j2d_glTexCoord2d(tx2, ty1); j2d_glVertex2d(dx + dw, dy);345j2d_glTexCoord2d(tx2, ty2); j2d_glVertex2d(dx + dw, dy + dh);346j2d_glTexCoord2d(tx1, ty2); j2d_glVertex2d(dx, dy + dh);347j2d_glEnd();348} else {349// this accounts for lower-left origin of the source region350jint newsx = srcOps->xOffset + sx;351jint newsy = srcOps->yOffset + srcOps->height - (sy + sh);352j2d_glCopyTexSubImage2D(GL_TEXTURE_2D, 0,3530, 0, newsx, newsy, sw, sh);354355// the texture image is "upside down" after the last step, so356// we align the bottom-left texture corner with the upper-left357// quad corner (and vice versa) to effectively flip the358// texture image359j2d_glBegin(GL_QUADS);360j2d_glTexCoord2d(tx1, ty2); j2d_glVertex2d(dx, dy);361j2d_glTexCoord2d(tx2, ty2); j2d_glVertex2d(dx + dw, dy);362j2d_glTexCoord2d(tx2, ty1); j2d_glVertex2d(dx + dw, dy + dh);363j2d_glTexCoord2d(tx1, ty1); j2d_glVertex2d(dx, dy + dh);364j2d_glEnd();365}366}367}368369if (adjustAlpha) {370// restore scale/bias to their original values371j2d_glPixelTransferf(GL_ALPHA_SCALE, 1.0f);372j2d_glPixelTransferf(GL_ALPHA_BIAS, 0.0f);373}374375j2d_glDisable(GL_TEXTURE_2D);376}377378/**379* Inner loop used for copying a source system memory ("Sw") surface to a380* destination OpenGL "Texture". This method is invoked from381* 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(). Note385* that this method is only capable of copying the source surface into the386* destination surface (i.e. no scaling or general transform is allowed).387* This restriction should not be an issue as this method is only used388* currently to cache a static system memory image into an OpenGL texture in389* a hidden-acceleration situation.390*/391static void392OGLBlitSwToTexture(SurfaceDataRasInfo *srcInfo, OGLPixelFormat *pf,393OGLSDOps *dstOps,394jint dx1, jint dy1, jint dx2, jint dy2)395{396jboolean adjustAlpha = (pf != NULL && !pf->hasAlpha);397j2d_glBindTexture(dstOps->textureTarget, dstOps->textureID);398399if (adjustAlpha) {400// if the source surface does not have an alpha channel,401// we need to ensure that the alpha values are forced to 1.0f402j2d_glPixelTransferf(GL_ALPHA_SCALE, 0.0f);403j2d_glPixelTransferf(GL_ALPHA_BIAS, 1.0f);404}405406// in case pixel stride is not a multiple of scanline stride the copy407// has to be done line by line (see 6207877)408if (srcInfo->scanStride % srcInfo->pixelStride != 0) {409jint width = dx2 - dx1;410jint height = dy2 - dy1;411GLvoid *pSrc = srcInfo->rasBase;412413while (height > 0) {414j2d_glTexSubImage2D(dstOps->textureTarget, 0,415dx1, dy2 - height, width, 1,416pf->format, pf->type, pSrc);417pSrc = PtrAddBytes(pSrc, srcInfo->scanStride);418height--;419}420} else {421j2d_glTexSubImage2D(dstOps->textureTarget, 0,422dx1, dy1, dx2-dx1, dy2-dy1,423pf->format, pf->type, srcInfo->rasBase);424}425if (adjustAlpha) {426// restore scale/bias to their original values427j2d_glPixelTransferf(GL_ALPHA_SCALE, 1.0f);428j2d_glPixelTransferf(GL_ALPHA_BIAS, 0.0f);429}430}431432/**433* General blit method for copying a native OpenGL surface (of type "Surface"434* or "Texture") to another OpenGL "Surface". If texture is JNI_TRUE, this435* method will invoke the Texture->Surface inner loop; otherwise, one of the436* Surface->Surface inner loops will be invoked, depending on the transform437* state.438*439* REMIND: we can trick these blit methods into doing XOR simply by passing440* in the (pixel ^ xorpixel) as the pixel value and preceding the441* blit with a fillrect...442*/443void444OGLBlitLoops_IsoBlit(JNIEnv *env,445OGLContext *oglc, jlong pSrcOps, jlong pDstOps,446jboolean xform, jint hint,447jboolean texture, jboolean rtt,448jint sx1, jint sy1, jint sx2, jint sy2,449jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)450{451OGLSDOps *srcOps = (OGLSDOps *)jlong_to_ptr(pSrcOps);452OGLSDOps *dstOps = (OGLSDOps *)jlong_to_ptr(pDstOps);453SurfaceDataRasInfo srcInfo;454jint sw = sx2 - sx1;455jint sh = sy2 - sy1;456jdouble dw = dx2 - dx1;457jdouble dh = dy2 - dy1;458459J2dTraceLn(J2D_TRACE_INFO, "OGLBlitLoops_IsoBlit");460461if (sw <= 0 || sh <= 0 || dw <= 0 || dh <= 0) {462J2dTraceLn(J2D_TRACE_WARNING,463"OGLBlitLoops_IsoBlit: invalid dimensions");464return;465}466467RETURN_IF_NULL(srcOps);468RETURN_IF_NULL(dstOps);469RETURN_IF_NULL(oglc);470471srcInfo.bounds.x1 = sx1;472srcInfo.bounds.y1 = sy1;473srcInfo.bounds.x2 = sx2;474srcInfo.bounds.y2 = sy2;475476SurfaceData_IntersectBoundsXYXY(&srcInfo.bounds,4770, 0, srcOps->width, srcOps->height);478479if (srcInfo.bounds.x2 > srcInfo.bounds.x1 &&480srcInfo.bounds.y2 > srcInfo.bounds.y1)481{482if (srcInfo.bounds.x1 != sx1) {483dx1 += (srcInfo.bounds.x1 - sx1) * (dw / sw);484sx1 = srcInfo.bounds.x1;485}486if (srcInfo.bounds.y1 != sy1) {487dy1 += (srcInfo.bounds.y1 - sy1) * (dh / sh);488sy1 = srcInfo.bounds.y1;489}490if (srcInfo.bounds.x2 != sx2) {491dx2 += (srcInfo.bounds.x2 - sx2) * (dw / sw);492sx2 = srcInfo.bounds.x2;493}494if (srcInfo.bounds.y2 != sy2) {495dy2 += (srcInfo.bounds.y2 - sy2) * (dh / sh);496sy2 = srcInfo.bounds.y2;497}498499J2dTraceLn2(J2D_TRACE_VERBOSE, " texture=%d hint=%d", texture, hint);500J2dTraceLn4(J2D_TRACE_VERBOSE, " sx1=%d sy1=%d sx2=%d sy2=%d",501sx1, sy1, sx2, sy2);502J2dTraceLn4(J2D_TRACE_VERBOSE, " dx1=%f dy1=%f dx2=%f dy2=%f",503dx1, dy1, dx2, dy2);504505if (texture) {506GLint glhint = (hint == OGLSD_XFORM_BILINEAR) ? GL_LINEAR :507GL_NEAREST;508CHECK_PREVIOUS_OP(srcOps->textureTarget);509OGLBlitTextureToSurface(oglc, srcOps, dstOps, rtt, glhint,510sx1, sy1, sx2, sy2,511dx1, dy1, dx2, dy2);512} else {513jboolean viaTexture;514if (xform) {515// we must use the via-texture codepath when there is a xform516viaTexture = JNI_TRUE;517} else {518// look at the vendor to see which codepath is faster519// (this has been empirically determined; see 5020009)520switch (OGLC_GET_VENDOR(oglc)) {521case OGLC_VENDOR_NVIDIA:522// the via-texture codepath tends to be faster when523// there is either a simple scale OR an extra alpha524viaTexture =525(sx2-sx1) != (jint)(dx2-dx1) ||526(sy2-sy1) != (jint)(dy2-dy1) ||527oglc->extraAlpha != 1.0f;528break;529530case OGLC_VENDOR_ATI:531// the via-texture codepath tends to be faster only when532// there is an extra alpha involved (scaling or not)533viaTexture = (oglc->extraAlpha != 1.0f);534break;535536default:537// just use the glCopyPixels() codepath538viaTexture = JNI_FALSE;539break;540}541}542543RESET_PREVIOUS_OP();544if (viaTexture) {545OGLBlitToSurfaceViaTexture(oglc, &srcInfo, NULL, srcOps,546JNI_FALSE, hint,547sx1, sy1, sx2, sy2,548dx1, dy1, dx2, dy2);549} else {550OGLBlitSurfaceToSurface(oglc, srcOps, dstOps,551sx1, sy1, sx2, sy2,552dx1, dy1, dx2, dy2);553}554}555}556}557558/**559* General blit method for copying a system memory ("Sw") surface to a native560* OpenGL surface (of type "Surface" or "Texture"). If texture is JNI_TRUE,561* this method will invoke the Sw->Texture inner loop; otherwise, one of the562* Sw->Surface inner loops will be invoked, depending on the transform state.563*/564void565OGLBlitLoops_Blit(JNIEnv *env,566OGLContext *oglc, jlong pSrcOps, jlong pDstOps,567jboolean xform, jint hint,568jint srctype, jboolean texture,569jint sx1, jint sy1, jint sx2, jint sy2,570jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)571{572SurfaceDataOps *srcOps = (SurfaceDataOps *)jlong_to_ptr(pSrcOps);573OGLSDOps *dstOps = (OGLSDOps *)jlong_to_ptr(pDstOps);574SurfaceDataRasInfo srcInfo;575OGLPixelFormat pf = PixelFormats[srctype];576jint sw = sx2 - sx1;577jint sh = sy2 - sy1;578jdouble dw = dx2 - dx1;579jdouble dh = dy2 - dy1;580581J2dTraceLn(J2D_TRACE_INFO, "OGLBlitLoops_Blit");582583if (sw <= 0 || sh <= 0 || dw <= 0 || dh <= 0 || srctype < 0) {584J2dTraceLn(J2D_TRACE_WARNING,585"OGLBlitLoops_Blit: invalid dimensions or srctype");586return;587}588589RETURN_IF_NULL(srcOps);590RETURN_IF_NULL(dstOps);591RETURN_IF_NULL(oglc);592RESET_PREVIOUS_OP();593594srcInfo.bounds.x1 = sx1;595srcInfo.bounds.y1 = sy1;596srcInfo.bounds.x2 = sx2;597srcInfo.bounds.y2 = sy2;598599if (srcOps->Lock(env, srcOps, &srcInfo, SD_LOCK_READ) != SD_SUCCESS) {600J2dTraceLn(J2D_TRACE_WARNING,601"OGLBlitLoops_Blit: could not acquire lock");602return;603}604605if (srcInfo.bounds.x2 > srcInfo.bounds.x1 &&606srcInfo.bounds.y2 > srcInfo.bounds.y1)607{608srcOps->GetRasInfo(env, srcOps, &srcInfo);609if (srcInfo.rasBase) {610if (srcInfo.bounds.x1 != sx1) {611dx1 += (srcInfo.bounds.x1 - sx1) * (dw / sw);612sx1 = srcInfo.bounds.x1;613}614if (srcInfo.bounds.y1 != sy1) {615dy1 += (srcInfo.bounds.y1 - sy1) * (dh / sh);616sy1 = srcInfo.bounds.y1;617}618if (srcInfo.bounds.x2 != sx2) {619dx2 += (srcInfo.bounds.x2 - sx2) * (dw / sw);620sx2 = srcInfo.bounds.x2;621}622if (srcInfo.bounds.y2 != sy2) {623dy2 += (srcInfo.bounds.y2 - sy2) * (dh / sh);624sy2 = srcInfo.bounds.y2;625}626627J2dTraceLn3(J2D_TRACE_VERBOSE, " texture=%d srctype=%d hint=%d",628texture, srctype, hint);629J2dTraceLn4(J2D_TRACE_VERBOSE, " sx1=%d sy1=%d sx2=%d sy2=%d",630sx1, sy1, sx2, sy2);631J2dTraceLn4(J2D_TRACE_VERBOSE, " dx1=%f dy1=%f dx2=%f dy2=%f",632dx1, dy1, dx2, dy2);633634// Note: we will calculate x/y positions in the raster manually635j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);636j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);637j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH,638srcInfo.scanStride / srcInfo.pixelStride);639j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, pf.alignment);640641if (texture) {642// These coordinates will always be integers since we643// only ever do a straight copy from sw to texture.644// Thus these casts are "safe" - no loss of precision.645OGLBlitSwToTexture(&srcInfo, &pf, dstOps,646(jint)dx1, (jint)dy1, (jint)dx2, (jint)dy2);647} else {648jboolean viaTexture;649if (xform) {650// we must use the via-texture codepath when there651// is a xform652viaTexture = JNI_TRUE;653} else {654// look at the vendor to see which codepath is faster655// (this has been empirically determined; see 5020009)656switch (OGLC_GET_VENDOR(oglc)) {657case OGLC_VENDOR_NVIDIA:658// the via-texture codepath tends to be faster when659// there is either a simple scale OR an extra alpha660viaTexture =661(sx2-sx1) != (jint)(dx2-dx1) ||662(sy2-sy1) != (jint)(dy2-dy1) ||663oglc->extraAlpha != 1.0f;664break;665#ifdef MACOSX666case OGLC_VENDOR_ATI:667// see 8024461668viaTexture = JNI_TRUE;669break;670#endif671case OGLC_VENDOR_INTEL:672viaTexture = JNI_TRUE;673break;674default:675// just use the glDrawPixels() codepath676viaTexture = JNI_FALSE;677break;678}679}680681if (viaTexture) {682OGLBlitToSurfaceViaTexture(oglc, &srcInfo, &pf, NULL,683JNI_TRUE, hint,684sx1, sy1, sx2, sy2,685dx1, dy1, dx2, dy2);686} else {687OGLBlitSwToSurface(oglc, &srcInfo, &pf,688sx1, sy1, sx2, sy2,689dx1, dy1, dx2, dy2);690}691}692693j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);694j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 4);695}696SurfaceData_InvokeRelease(env, srcOps, &srcInfo);697}698SurfaceData_InvokeUnlock(env, srcOps, &srcInfo);699}700701/**702* This method makes vertical flip of the provided area of Surface and convert703* pixel's data from argbPre to argb format if requested.704*/705void flip(void *pDst, juint w, juint h, jint scanStride, jboolean convert) {706const size_t clippedStride = 4 * w;707void *tempRow = (h > 1 && !convert) ? malloc(clippedStride) : NULL;708juint i = 0;709juint step = 0;710// vertical flip and convert argbpre to argb if necessary711for (; i < h / 2; ++i) {712juint *r1 = PtrPixelsRow(pDst, i, scanStride);713juint *r2 = PtrPixelsRow(pDst, h - i - 1, scanStride);714if (tempRow) {715// fast path716memcpy(tempRow, r1, clippedStride);717memcpy(r1, r2, clippedStride);718memcpy(r2, tempRow, clippedStride);719} else {720// slow path721for (step = 0; step < w; ++step) {722juint tmp = r1[step];723if (convert) {724LoadIntArgbPreTo1IntArgb(r2, 0, step, r1[step]);725LoadIntArgbPreTo1IntArgb(&tmp, 0, 0, r2[step]);726} else {727r1[step] = r2[step];728r2[step] = tmp;729}730}731}732}733// convert the middle line if necessary734if (convert && h % 2) {735juint *r1 = PtrPixelsRow(pDst, i, scanStride);736for (step = 0; step < w; ++step) {737LoadIntArgbPreTo1IntArgb(r1, 0, step, r1[step]);738}739}740if (tempRow) {741free(tempRow);742}743}744745/**746* Specialized blit method for copying a native OpenGL "Surface" (pbuffer,747* window, etc.) to a system memory ("Sw") surface.748*/749void750OGLBlitLoops_SurfaceToSwBlit(JNIEnv *env, OGLContext *oglc,751jlong pSrcOps, jlong pDstOps, jint dsttype,752jint srcx, jint srcy, jint dstx, jint dsty,753jint width, jint height)754{755OGLSDOps *srcOps = (OGLSDOps *)jlong_to_ptr(pSrcOps);756SurfaceDataOps *dstOps = (SurfaceDataOps *)jlong_to_ptr(pDstOps);757SurfaceDataRasInfo srcInfo, dstInfo;758OGLPixelFormat pf = PixelFormats[dsttype];759760J2dTraceLn(J2D_TRACE_INFO, "OGLBlitLoops_SurfaceToSwBlit");761762if (width <= 0 || height <= 0) {763J2dTraceLn(J2D_TRACE_WARNING,764"OGLBlitLoops_SurfaceToSwBlit: dimensions are non-positive");765return;766}767768RETURN_IF_NULL(srcOps);769RETURN_IF_NULL(dstOps);770RETURN_IF_NULL(oglc);771RESET_PREVIOUS_OP();772773srcInfo.bounds.x1 = srcx;774srcInfo.bounds.y1 = srcy;775srcInfo.bounds.x2 = srcx + width;776srcInfo.bounds.y2 = srcy + height;777dstInfo.bounds.x1 = dstx;778dstInfo.bounds.y1 = dsty;779dstInfo.bounds.x2 = dstx + width;780dstInfo.bounds.y2 = dsty + height;781782if (dstOps->Lock(env, dstOps, &dstInfo, SD_LOCK_WRITE) != SD_SUCCESS) {783J2dTraceLn(J2D_TRACE_WARNING,784"OGLBlitLoops_SurfaceToSwBlit: could not acquire dst lock");785return;786}787788SurfaceData_IntersectBoundsXYXY(&srcInfo.bounds,7890, 0, srcOps->width, srcOps->height);790SurfaceData_IntersectBlitBounds(&dstInfo.bounds, &srcInfo.bounds,791srcx - dstx, srcy - dsty);792793if (srcInfo.bounds.x2 > srcInfo.bounds.x1 &&794srcInfo.bounds.y2 > srcInfo.bounds.y1)795{796dstOps->GetRasInfo(env, dstOps, &dstInfo);797if (dstInfo.rasBase) {798void *pDst = dstInfo.rasBase;799800srcx = srcInfo.bounds.x1;801srcy = srcInfo.bounds.y1;802dstx = dstInfo.bounds.x1;803dsty = dstInfo.bounds.y1;804width = srcInfo.bounds.x2 - srcInfo.bounds.x1;805height = srcInfo.bounds.y2 - srcInfo.bounds.y1;806807pDst = PtrAddBytes(pDst, dstx * dstInfo.pixelStride);808pDst = PtrPixelsRow(pDst, dsty, dstInfo.scanStride);809810j2d_glPixelStorei(GL_PACK_ROW_LENGTH,811dstInfo.scanStride / dstInfo.pixelStride);812j2d_glPixelStorei(GL_PACK_ALIGNMENT, pf.alignment);813#ifdef MACOSX814if (srcOps->isOpaque) {815// For some reason Apple's OpenGL implementation will816// read back zero values from the alpha channel of an817// opaque surface when using glReadPixels(), so here we818// force the resulting pixels to be fully opaque.819j2d_glPixelTransferf(GL_ALPHA_BIAS, 1.0);820}821#endif822823J2dTraceLn4(J2D_TRACE_VERBOSE, " sx=%d sy=%d w=%d h=%d",824srcx, srcy, width, height);825J2dTraceLn2(J2D_TRACE_VERBOSE, " dx=%d dy=%d",826dstx, dsty);827828// this accounts for lower-left origin of the source region829srcx = srcOps->xOffset + srcx;830srcy = srcOps->yOffset + srcOps->height - srcy - height;831832// Note that glReadPixels() is extremely slow!833// So we call it only once and flip the image using memcpy.834j2d_glReadPixels(srcx, srcy, width, height,835pf.format, pf.type, pDst);836// It was checked above that width and height are positive.837flip(pDst, (juint) width, (juint) height, dstInfo.scanStride,838!pf.isPremult && !srcOps->isOpaque);839#ifdef MACOSX840if (srcOps->isOpaque) {841j2d_glPixelTransferf(GL_ALPHA_BIAS, 0.0);842}843#endif844j2d_glPixelStorei(GL_PACK_ROW_LENGTH, 0);845j2d_glPixelStorei(GL_PACK_ALIGNMENT, 4);846}847SurfaceData_InvokeRelease(env, dstOps, &dstInfo);848}849SurfaceData_InvokeUnlock(env, dstOps, &dstInfo);850}851852void853OGLBlitLoops_CopyArea(JNIEnv *env,854OGLContext *oglc, OGLSDOps *dstOps,855jint x, jint y, jint width, jint height,856jint dx, jint dy)857{858SurfaceDataBounds srcBounds, dstBounds;859860J2dTraceLn(J2D_TRACE_INFO, "OGLBlitLoops_CopyArea");861862RETURN_IF_NULL(oglc);863RETURN_IF_NULL(dstOps);864RESET_PREVIOUS_OP();865866J2dTraceLn4(J2D_TRACE_VERBOSE, " x=%d y=%d w=%d h=%d",867x, y, width, height);868J2dTraceLn2(J2D_TRACE_VERBOSE, " dx=%d dy=%d",869dx, dy);870871srcBounds.x1 = x;872srcBounds.y1 = y;873srcBounds.x2 = srcBounds.x1 + width;874srcBounds.y2 = srcBounds.y1 + height;875dstBounds.x1 = x + dx;876dstBounds.y1 = y + dy;877dstBounds.x2 = dstBounds.x1 + width;878dstBounds.y2 = dstBounds.y1 + height;879880// 6430601: manually clip src/dst parameters to work around881// some bugs in Sun's and Apple's OpenGL implementations882// (it's a good idea to restrict the source parameters anyway, since883// passing out of range parameters to glCopyPixels() will result in884// an OpenGL error)885SurfaceData_IntersectBoundsXYXY(&srcBounds,8860, 0, dstOps->width, dstOps->height);887SurfaceData_IntersectBoundsXYXY(&dstBounds,8880, 0, dstOps->width, dstOps->height);889SurfaceData_IntersectBlitBounds(&dstBounds, &srcBounds, -dx, -dy);890891if (dstBounds.x1 < dstBounds.x2 && dstBounds.y1 < dstBounds.y2) {892#ifdef MACOSX893if (dstOps->isOpaque) {894// For some reason Apple's OpenGL implementation will fail895// to render glCopyPixels() when the src/dst rectangles are896// overlapping and glColorMask() has disabled writes to the897// alpha channel. The workaround is to temporarily re-enable898// the alpha channel during the glCopyPixels() operation.899j2d_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);900}901#endif902903OGLBlitSurfaceToSurface(oglc, dstOps, dstOps,904srcBounds.x1, srcBounds.y1,905srcBounds.x2, srcBounds.y2,906dstBounds.x1, dstBounds.y1,907dstBounds.x2, dstBounds.y2);908#ifdef MACOSX909if (dstOps->isOpaque) {910j2d_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);911}912#endif913}914}915916#endif /* !HEADLESS */917918919