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