Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.desktop/share/native/common/java2d/opengl/OGLBlitLoops.c
41159 views
1
/*
2
* Copyright (c) 2003, 2019, 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
41
extern 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
*/
55
static void
56
OGLBlitSurfaceToSurface(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
*/
108
static void
109
OGLBlitTextureToSurface(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
*/
176
static void
177
OGLBlitSwToSurface(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
GLvoid *pSrc = PtrCoord(srcInfo->rasBase, sx1, srcInfo->pixelStride,
212
sy1, srcInfo->scanStride);
213
214
// in case pixel stride is not a multiple of scanline stride the copy
215
// has to be done line by line (see 6207877)
216
if (srcInfo->scanStride % srcInfo->pixelStride != 0) {
217
jint width = sx2-sx1;
218
jint height = sy2-sy1;
219
while (height > 0) {
220
j2d_glDrawPixels(width, 1, pf->format, pf->type, pSrc);
221
j2d_glBitmap(0, 0, 0, 0, (GLfloat)0, (GLfloat)-scaley, NULL);
222
pSrc = PtrAddBytes(pSrc, srcInfo->scanStride);
223
height--;
224
}
225
} else {
226
j2d_glDrawPixels(sx2-sx1, sy2-sy1, pf->format, pf->type, pSrc);
227
}
228
229
j2d_glPixelZoom(1.0, 1.0);
230
231
if (oglc->extraAlpha != 1.0f) {
232
OGLContext_SetExtraAlpha(1.0f);
233
}
234
if (!pf->hasAlpha) {
235
// restore scale/bias to their original values
236
j2d_glPixelTransferf(GL_ALPHA_SCALE, 1.0f);
237
j2d_glPixelTransferf(GL_ALPHA_BIAS, 0.0f);
238
}
239
}
240
241
/**
242
* Inner loop used for copying a source system memory ("Sw") surface or
243
* OpenGL "Surface" to a destination OpenGL "Surface", using an OpenGL texture
244
* tile as an intermediate surface. This method is invoked from
245
* OGLBlitLoops_Blit() for "Sw" surfaces and OGLBlitLoops_IsoBlit() for
246
* "Surface" surfaces.
247
*
248
* This method is used to transform the source surface into the destination.
249
* Pixel rectangles cannot be arbitrarily transformed (without the
250
* GL_EXT_pixel_transform extension, which is not supported on most modern
251
* hardware). However, texture mapped quads do respect the GL_MODELVIEW
252
* transform matrix, so we use textures here to perform the transform
253
* operation. This method uses a tile-based approach in which a small
254
* subregion of the source surface is copied into a cached texture tile. The
255
* texture tile is then mapped into the appropriate location in the
256
* destination surface.
257
*
258
* REMIND: this only works well using GL_NEAREST for the filtering mode
259
* (GL_LINEAR causes visible stitching problems between tiles,
260
* but this can be fixed by making use of texture borders)
261
*/
262
static void
263
OGLBlitToSurfaceViaTexture(OGLContext *oglc, SurfaceDataRasInfo *srcInfo,
264
OGLPixelFormat *pf, OGLSDOps *srcOps,
265
jboolean swsurface, jint hint,
266
jint sx1, jint sy1, jint sx2, jint sy2,
267
jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)
268
{
269
GLdouble tx1, ty1, tx2, ty2;
270
GLdouble dx, dy, dw, dh, cdw, cdh;
271
jint tw, th;
272
jint sx, sy, sw, sh;
273
GLint glhint = (hint == OGLSD_XFORM_BILINEAR) ? GL_LINEAR : GL_NEAREST;
274
jboolean adjustAlpha = (pf != NULL && !pf->hasAlpha);
275
jboolean slowPath;
276
277
if (oglc->blitTextureID == 0) {
278
if (!OGLContext_InitBlitTileTexture(oglc)) {
279
J2dRlsTraceLn(J2D_TRACE_ERROR,
280
"OGLBlitToSurfaceViaTexture: could not init blit tile");
281
return;
282
}
283
}
284
285
tx1 = 0.0f;
286
ty1 = 0.0f;
287
tw = OGLC_BLIT_TILE_SIZE;
288
th = OGLC_BLIT_TILE_SIZE;
289
cdw = (dx2-dx1) / (((GLdouble)(sx2-sx1)) / OGLC_BLIT_TILE_SIZE);
290
cdh = (dy2-dy1) / (((GLdouble)(sy2-sy1)) / OGLC_BLIT_TILE_SIZE);
291
292
j2d_glEnable(GL_TEXTURE_2D);
293
j2d_glBindTexture(GL_TEXTURE_2D, oglc->blitTextureID);
294
OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE);
295
j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, glhint);
296
j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, glhint);
297
298
if (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.0f
301
j2d_glPixelTransferf(GL_ALPHA_SCALE, 0.0f);
302
j2d_glPixelTransferf(GL_ALPHA_BIAS, 1.0f);
303
}
304
305
// in case pixel stride is not a multiple of scanline stride the copy
306
// has to be done line by line (see 6207877)
307
slowPath = srcInfo->scanStride % srcInfo->pixelStride != 0;
308
309
for (sy = sy1, dy = dy1; sy < sy2; sy += th, dy += cdh) {
310
sh = ((sy + th) > sy2) ? (sy2 - sy) : th;
311
dh = ((dy + cdh) > dy2) ? (dy2 - dy) : cdh;
312
313
for (sx = sx1, dx = dx1; sx < sx2; sx += tw, dx += cdw) {
314
sw = ((sx + tw) > sx2) ? (sx2 - sx) : tw;
315
dw = ((dx + cdw) > dx2) ? (dx2 - dx) : cdw;
316
317
tx2 = ((GLdouble)sw) / tw;
318
ty2 = ((GLdouble)sh) / th;
319
320
if (swsurface) {
321
GLvoid *pSrc = PtrCoord(srcInfo->rasBase,
322
sx, srcInfo->pixelStride,
323
sy, srcInfo->scanStride);
324
if (slowPath) {
325
jint tmph = sh;
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_glTexSubImage2D(GL_TEXTURE_2D, 0,
336
0, 0, sw, sh,
337
pf->format, pf->type,
338
pSrc);
339
}
340
341
// the texture image is "right side up", so we align the
342
// upper-left texture corner with the upper-left quad corner
343
j2d_glBegin(GL_QUADS);
344
j2d_glTexCoord2d(tx1, ty1); j2d_glVertex2d(dx, dy);
345
j2d_glTexCoord2d(tx2, ty1); j2d_glVertex2d(dx + dw, dy);
346
j2d_glTexCoord2d(tx2, ty2); j2d_glVertex2d(dx + dw, dy + dh);
347
j2d_glTexCoord2d(tx1, ty2); j2d_glVertex2d(dx, dy + dh);
348
j2d_glEnd();
349
} else {
350
// this accounts for lower-left origin of the source region
351
jint newsx = srcOps->xOffset + sx;
352
jint newsy = srcOps->yOffset + srcOps->height - (sy + sh);
353
j2d_glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
354
0, 0, newsx, newsy, sw, sh);
355
356
// the texture image is "upside down" after the last step, so
357
// we align the bottom-left texture corner with the upper-left
358
// quad corner (and vice versa) to effectively flip the
359
// texture image
360
j2d_glBegin(GL_QUADS);
361
j2d_glTexCoord2d(tx1, ty2); j2d_glVertex2d(dx, dy);
362
j2d_glTexCoord2d(tx2, ty2); j2d_glVertex2d(dx + dw, dy);
363
j2d_glTexCoord2d(tx2, ty1); j2d_glVertex2d(dx + dw, dy + dh);
364
j2d_glTexCoord2d(tx1, ty1); j2d_glVertex2d(dx, dy + dh);
365
j2d_glEnd();
366
}
367
}
368
}
369
370
if (adjustAlpha) {
371
// restore scale/bias to their original values
372
j2d_glPixelTransferf(GL_ALPHA_SCALE, 1.0f);
373
j2d_glPixelTransferf(GL_ALPHA_BIAS, 0.0f);
374
}
375
376
j2d_glDisable(GL_TEXTURE_2D);
377
}
378
379
/**
380
* Inner loop used for copying a source system memory ("Sw") surface to a
381
* destination OpenGL "Texture". This method is invoked from
382
* 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(). Note
386
* that this method is only capable of copying the source surface into the
387
* destination surface (i.e. no scaling or general transform is allowed).
388
* This restriction should not be an issue as this method is only used
389
* currently to cache a static system memory image into an OpenGL texture in
390
* a hidden-acceleration situation.
391
*/
392
static void
393
OGLBlitSwToTexture(SurfaceDataRasInfo *srcInfo, OGLPixelFormat *pf,
394
OGLSDOps *dstOps,
395
jint dx1, jint dy1, jint dx2, jint dy2)
396
{
397
jboolean adjustAlpha = (pf != NULL && !pf->hasAlpha);
398
j2d_glBindTexture(dstOps->textureTarget, dstOps->textureID);
399
400
if (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.0f
403
j2d_glPixelTransferf(GL_ALPHA_SCALE, 0.0f);
404
j2d_glPixelTransferf(GL_ALPHA_BIAS, 1.0f);
405
}
406
407
// in case pixel stride is not a multiple of scanline stride the copy
408
// has to be done line by line (see 6207877)
409
if (srcInfo->scanStride % srcInfo->pixelStride != 0) {
410
jint width = dx2 - dx1;
411
jint height = dy2 - dy1;
412
GLvoid *pSrc = srcInfo->rasBase;
413
414
while (height > 0) {
415
j2d_glTexSubImage2D(dstOps->textureTarget, 0,
416
dx1, dy2 - height, width, 1,
417
pf->format, pf->type, pSrc);
418
pSrc = PtrAddBytes(pSrc, srcInfo->scanStride);
419
height--;
420
}
421
} else {
422
j2d_glTexSubImage2D(dstOps->textureTarget, 0,
423
dx1, dy1, dx2-dx1, dy2-dy1,
424
pf->format, pf->type, srcInfo->rasBase);
425
}
426
if (adjustAlpha) {
427
// restore scale/bias to their original values
428
j2d_glPixelTransferf(GL_ALPHA_SCALE, 1.0f);
429
j2d_glPixelTransferf(GL_ALPHA_BIAS, 0.0f);
430
}
431
}
432
433
/**
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, this
436
* method will invoke the Texture->Surface inner loop; otherwise, one of the
437
* Surface->Surface inner loops will be invoked, depending on the transform
438
* state.
439
*
440
* REMIND: we can trick these blit methods into doing XOR simply by passing
441
* in the (pixel ^ xorpixel) as the pixel value and preceding the
442
* blit with a fillrect...
443
*/
444
void
445
OGLBlitLoops_IsoBlit(JNIEnv *env,
446
OGLContext *oglc, jlong pSrcOps, jlong pDstOps,
447
jboolean xform, jint hint,
448
jboolean texture, jboolean rtt,
449
jint sx1, jint sy1, jint sx2, jint sy2,
450
jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)
451
{
452
OGLSDOps *srcOps = (OGLSDOps *)jlong_to_ptr(pSrcOps);
453
OGLSDOps *dstOps = (OGLSDOps *)jlong_to_ptr(pDstOps);
454
SurfaceDataRasInfo srcInfo;
455
jint sw = sx2 - sx1;
456
jint sh = sy2 - sy1;
457
jdouble dw = dx2 - dx1;
458
jdouble dh = dy2 - dy1;
459
460
J2dTraceLn(J2D_TRACE_INFO, "OGLBlitLoops_IsoBlit");
461
462
if (sw <= 0 || sh <= 0 || dw <= 0 || dh <= 0) {
463
J2dTraceLn(J2D_TRACE_WARNING,
464
"OGLBlitLoops_IsoBlit: invalid dimensions");
465
return;
466
}
467
468
RETURN_IF_NULL(srcOps);
469
RETURN_IF_NULL(dstOps);
470
RETURN_IF_NULL(oglc);
471
472
srcInfo.bounds.x1 = sx1;
473
srcInfo.bounds.y1 = sy1;
474
srcInfo.bounds.x2 = sx2;
475
srcInfo.bounds.y2 = sy2;
476
477
SurfaceData_IntersectBoundsXYXY(&srcInfo.bounds,
478
0, 0, srcOps->width, srcOps->height);
479
480
if (srcInfo.bounds.x2 > srcInfo.bounds.x1 &&
481
srcInfo.bounds.y2 > srcInfo.bounds.y1)
482
{
483
if (srcInfo.bounds.x1 != sx1) {
484
dx1 += (srcInfo.bounds.x1 - sx1) * (dw / sw);
485
sx1 = srcInfo.bounds.x1;
486
}
487
if (srcInfo.bounds.y1 != sy1) {
488
dy1 += (srcInfo.bounds.y1 - sy1) * (dh / sh);
489
sy1 = srcInfo.bounds.y1;
490
}
491
if (srcInfo.bounds.x2 != sx2) {
492
dx2 += (srcInfo.bounds.x2 - sx2) * (dw / sw);
493
sx2 = srcInfo.bounds.x2;
494
}
495
if (srcInfo.bounds.y2 != sy2) {
496
dy2 += (srcInfo.bounds.y2 - sy2) * (dh / sh);
497
sy2 = srcInfo.bounds.y2;
498
}
499
500
J2dTraceLn2(J2D_TRACE_VERBOSE, " texture=%d hint=%d", texture, hint);
501
J2dTraceLn4(J2D_TRACE_VERBOSE, " sx1=%d sy1=%d sx2=%d sy2=%d",
502
sx1, sy1, sx2, sy2);
503
J2dTraceLn4(J2D_TRACE_VERBOSE, " dx1=%f dy1=%f dx2=%f dy2=%f",
504
dx1, dy1, dx2, dy2);
505
506
if (texture) {
507
GLint glhint = (hint == OGLSD_XFORM_BILINEAR) ? GL_LINEAR :
508
GL_NEAREST;
509
CHECK_PREVIOUS_OP(srcOps->textureTarget);
510
OGLBlitTextureToSurface(oglc, srcOps, dstOps, rtt, glhint,
511
sx1, sy1, sx2, sy2,
512
dx1, dy1, dx2, dy2);
513
} else {
514
jboolean viaTexture;
515
if (xform) {
516
// we must use the via-texture codepath when there is a xform
517
viaTexture = JNI_TRUE;
518
} else {
519
// look at the vendor to see which codepath is faster
520
// (this has been empirically determined; see 5020009)
521
switch (OGLC_GET_VENDOR(oglc)) {
522
case OGLC_VENDOR_NVIDIA:
523
// the via-texture codepath tends to be faster when
524
// there is either a simple scale OR an extra alpha
525
viaTexture =
526
(sx2-sx1) != (jint)(dx2-dx1) ||
527
(sy2-sy1) != (jint)(dy2-dy1) ||
528
oglc->extraAlpha != 1.0f;
529
break;
530
531
case OGLC_VENDOR_ATI:
532
// the via-texture codepath tends to be faster only when
533
// there is an extra alpha involved (scaling or not)
534
viaTexture = (oglc->extraAlpha != 1.0f);
535
break;
536
537
default:
538
// just use the glCopyPixels() codepath
539
viaTexture = JNI_FALSE;
540
break;
541
}
542
}
543
544
RESET_PREVIOUS_OP();
545
if (viaTexture) {
546
OGLBlitToSurfaceViaTexture(oglc, &srcInfo, NULL, srcOps,
547
JNI_FALSE, hint,
548
sx1, sy1, sx2, sy2,
549
dx1, dy1, dx2, dy2);
550
} else {
551
OGLBlitSurfaceToSurface(oglc, srcOps, dstOps,
552
sx1, sy1, sx2, sy2,
553
dx1, dy1, dx2, dy2);
554
}
555
}
556
}
557
}
558
559
/**
560
* General blit method for copying a system memory ("Sw") surface to a native
561
* 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 the
563
* Sw->Surface inner loops will be invoked, depending on the transform state.
564
*/
565
void
566
OGLBlitLoops_Blit(JNIEnv *env,
567
OGLContext *oglc, jlong pSrcOps, jlong pDstOps,
568
jboolean xform, jint hint,
569
jint srctype, jboolean texture,
570
jint sx1, jint sy1, jint sx2, jint sy2,
571
jdouble dx1, jdouble dy1, jdouble dx2, jdouble dy2)
572
{
573
SurfaceDataOps *srcOps = (SurfaceDataOps *)jlong_to_ptr(pSrcOps);
574
OGLSDOps *dstOps = (OGLSDOps *)jlong_to_ptr(pDstOps);
575
SurfaceDataRasInfo srcInfo;
576
OGLPixelFormat pf = PixelFormats[srctype];
577
jint sw = sx2 - sx1;
578
jint sh = sy2 - sy1;
579
jdouble dw = dx2 - dx1;
580
jdouble dh = dy2 - dy1;
581
582
J2dTraceLn(J2D_TRACE_INFO, "OGLBlitLoops_Blit");
583
584
if (sw <= 0 || sh <= 0 || dw <= 0 || dh <= 0 || srctype < 0) {
585
J2dTraceLn(J2D_TRACE_WARNING,
586
"OGLBlitLoops_Blit: invalid dimensions or srctype");
587
return;
588
}
589
590
RETURN_IF_NULL(srcOps);
591
RETURN_IF_NULL(dstOps);
592
RETURN_IF_NULL(oglc);
593
RESET_PREVIOUS_OP();
594
595
srcInfo.bounds.x1 = sx1;
596
srcInfo.bounds.y1 = sy1;
597
srcInfo.bounds.x2 = sx2;
598
srcInfo.bounds.y2 = sy2;
599
600
if (srcOps->Lock(env, srcOps, &srcInfo, SD_LOCK_READ) != SD_SUCCESS) {
601
J2dTraceLn(J2D_TRACE_WARNING,
602
"OGLBlitLoops_Blit: could not acquire lock");
603
return;
604
}
605
606
if (srcInfo.bounds.x2 > srcInfo.bounds.x1 &&
607
srcInfo.bounds.y2 > srcInfo.bounds.y1)
608
{
609
srcOps->GetRasInfo(env, srcOps, &srcInfo);
610
if (srcInfo.rasBase) {
611
if (srcInfo.bounds.x1 != sx1) {
612
dx1 += (srcInfo.bounds.x1 - sx1) * (dw / sw);
613
sx1 = srcInfo.bounds.x1;
614
}
615
if (srcInfo.bounds.y1 != sy1) {
616
dy1 += (srcInfo.bounds.y1 - sy1) * (dh / sh);
617
sy1 = srcInfo.bounds.y1;
618
}
619
if (srcInfo.bounds.x2 != sx2) {
620
dx2 += (srcInfo.bounds.x2 - sx2) * (dw / sw);
621
sx2 = srcInfo.bounds.x2;
622
}
623
if (srcInfo.bounds.y2 != sy2) {
624
dy2 += (srcInfo.bounds.y2 - sy2) * (dh / sh);
625
sy2 = srcInfo.bounds.y2;
626
}
627
628
J2dTraceLn3(J2D_TRACE_VERBOSE, " texture=%d srctype=%d hint=%d",
629
texture, srctype, hint);
630
J2dTraceLn4(J2D_TRACE_VERBOSE, " sx1=%d sy1=%d sx2=%d sy2=%d",
631
sx1, sy1, sx2, sy2);
632
J2dTraceLn4(J2D_TRACE_VERBOSE, " dx1=%f dy1=%f dx2=%f dy2=%f",
633
dx1, dy1, dx2, dy2);
634
635
// Note: we will calculate x/y positions in the raster manually
636
j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
637
j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
638
j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH,
639
srcInfo.scanStride / srcInfo.pixelStride);
640
j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, pf.alignment);
641
642
if (texture) {
643
// These coordinates will always be integers since we
644
// only ever do a straight copy from sw to texture.
645
// Thus these casts are "safe" - no loss of precision.
646
OGLBlitSwToTexture(&srcInfo, &pf, dstOps,
647
(jint)dx1, (jint)dy1, (jint)dx2, (jint)dy2);
648
} else {
649
jboolean viaTexture;
650
if (xform) {
651
// we must use the via-texture codepath when there
652
// is a xform
653
viaTexture = JNI_TRUE;
654
} else {
655
// look at the vendor to see which codepath is faster
656
// (this has been empirically determined; see 5020009)
657
switch (OGLC_GET_VENDOR(oglc)) {
658
case OGLC_VENDOR_NVIDIA:
659
// the via-texture codepath tends to be faster when
660
// there is either a simple scale OR an extra alpha
661
viaTexture =
662
(sx2-sx1) != (jint)(dx2-dx1) ||
663
(sy2-sy1) != (jint)(dy2-dy1) ||
664
oglc->extraAlpha != 1.0f;
665
break;
666
#ifdef MACOSX
667
case OGLC_VENDOR_ATI:
668
// see 8024461
669
viaTexture = JNI_TRUE;
670
break;
671
#endif
672
case OGLC_VENDOR_INTEL:
673
viaTexture = JNI_TRUE;
674
break;
675
default:
676
// just use the glDrawPixels() codepath
677
viaTexture = JNI_FALSE;
678
break;
679
}
680
}
681
682
if (viaTexture) {
683
OGLBlitToSurfaceViaTexture(oglc, &srcInfo, &pf, NULL,
684
JNI_TRUE, hint,
685
sx1, sy1, sx2, sy2,
686
dx1, dy1, dx2, dy2);
687
} else {
688
OGLBlitSwToSurface(oglc, &srcInfo, &pf,
689
sx1, sy1, sx2, sy2,
690
dx1, dy1, dx2, dy2);
691
}
692
}
693
694
j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
695
j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
696
}
697
SurfaceData_InvokeRelease(env, srcOps, &srcInfo);
698
}
699
SurfaceData_InvokeUnlock(env, srcOps, &srcInfo);
700
}
701
702
/**
703
* This method makes vertical flip of the provided area of Surface and convert
704
* pixel's data from argbPre to argb format if requested.
705
*/
706
void flip(void *pDst, juint w, juint h, jint scanStride, jboolean convert) {
707
const size_t clippedStride = 4 * w;
708
void *tempRow = (h > 1 && !convert) ? malloc(clippedStride) : NULL;
709
juint i = 0;
710
juint step = 0;
711
// vertical flip and convert argbpre to argb if necessary
712
for (; i < h / 2; ++i) {
713
juint *r1 = PtrPixelsRow(pDst, i, scanStride);
714
juint *r2 = PtrPixelsRow(pDst, h - i - 1, scanStride);
715
if (tempRow) {
716
// fast path
717
memcpy(tempRow, r1, clippedStride);
718
memcpy(r1, r2, clippedStride);
719
memcpy(r2, tempRow, clippedStride);
720
} else {
721
// slow path
722
for (step = 0; step < w; ++step) {
723
juint tmp = r1[step];
724
if (convert) {
725
LoadIntArgbPreTo1IntArgb(r2, 0, step, r1[step]);
726
LoadIntArgbPreTo1IntArgb(&tmp, 0, 0, r2[step]);
727
} else {
728
r1[step] = r2[step];
729
r2[step] = tmp;
730
}
731
}
732
}
733
}
734
// convert the middle line if necessary
735
if (convert && h % 2) {
736
juint *r1 = PtrPixelsRow(pDst, i, scanStride);
737
for (step = 0; step < w; ++step) {
738
LoadIntArgbPreTo1IntArgb(r1, 0, step, r1[step]);
739
}
740
}
741
if (tempRow) {
742
free(tempRow);
743
}
744
}
745
746
/**
747
* Specialized blit method for copying a native OpenGL "Surface" (pbuffer,
748
* window, etc.) to a system memory ("Sw") surface.
749
*/
750
void
751
OGLBlitLoops_SurfaceToSwBlit(JNIEnv *env, OGLContext *oglc,
752
jlong pSrcOps, jlong pDstOps, jint dsttype,
753
jint srcx, jint srcy, jint dstx, jint dsty,
754
jint width, jint height)
755
{
756
OGLSDOps *srcOps = (OGLSDOps *)jlong_to_ptr(pSrcOps);
757
SurfaceDataOps *dstOps = (SurfaceDataOps *)jlong_to_ptr(pDstOps);
758
SurfaceDataRasInfo srcInfo, dstInfo;
759
OGLPixelFormat pf = PixelFormats[dsttype];
760
761
J2dTraceLn(J2D_TRACE_INFO, "OGLBlitLoops_SurfaceToSwBlit");
762
763
if (width <= 0 || height <= 0) {
764
J2dTraceLn(J2D_TRACE_WARNING,
765
"OGLBlitLoops_SurfaceToSwBlit: dimensions are non-positive");
766
return;
767
}
768
769
RETURN_IF_NULL(srcOps);
770
RETURN_IF_NULL(dstOps);
771
RETURN_IF_NULL(oglc);
772
RESET_PREVIOUS_OP();
773
774
srcInfo.bounds.x1 = srcx;
775
srcInfo.bounds.y1 = srcy;
776
srcInfo.bounds.x2 = srcx + width;
777
srcInfo.bounds.y2 = srcy + height;
778
dstInfo.bounds.x1 = dstx;
779
dstInfo.bounds.y1 = dsty;
780
dstInfo.bounds.x2 = dstx + width;
781
dstInfo.bounds.y2 = dsty + height;
782
783
if (dstOps->Lock(env, dstOps, &dstInfo, SD_LOCK_WRITE) != SD_SUCCESS) {
784
J2dTraceLn(J2D_TRACE_WARNING,
785
"OGLBlitLoops_SurfaceToSwBlit: could not acquire dst lock");
786
return;
787
}
788
789
SurfaceData_IntersectBoundsXYXY(&srcInfo.bounds,
790
0, 0, srcOps->width, srcOps->height);
791
SurfaceData_IntersectBlitBounds(&dstInfo.bounds, &srcInfo.bounds,
792
srcx - dstx, srcy - dsty);
793
794
if (srcInfo.bounds.x2 > srcInfo.bounds.x1 &&
795
srcInfo.bounds.y2 > srcInfo.bounds.y1)
796
{
797
dstOps->GetRasInfo(env, dstOps, &dstInfo);
798
if (dstInfo.rasBase) {
799
void *pDst = dstInfo.rasBase;
800
801
srcx = srcInfo.bounds.x1;
802
srcy = srcInfo.bounds.y1;
803
dstx = dstInfo.bounds.x1;
804
dsty = dstInfo.bounds.y1;
805
width = srcInfo.bounds.x2 - srcInfo.bounds.x1;
806
height = srcInfo.bounds.y2 - srcInfo.bounds.y1;
807
808
pDst = PtrAddBytes(pDst, dstx * dstInfo.pixelStride);
809
pDst = PtrPixelsRow(pDst, dsty, dstInfo.scanStride);
810
811
j2d_glPixelStorei(GL_PACK_ROW_LENGTH,
812
dstInfo.scanStride / dstInfo.pixelStride);
813
j2d_glPixelStorei(GL_PACK_ALIGNMENT, pf.alignment);
814
#ifdef MACOSX
815
if (srcOps->isOpaque) {
816
// For some reason Apple's OpenGL implementation will
817
// read back zero values from the alpha channel of an
818
// opaque surface when using glReadPixels(), so here we
819
// force the resulting pixels to be fully opaque.
820
j2d_glPixelTransferf(GL_ALPHA_BIAS, 1.0);
821
}
822
#endif
823
824
J2dTraceLn4(J2D_TRACE_VERBOSE, " sx=%d sy=%d w=%d h=%d",
825
srcx, srcy, width, height);
826
J2dTraceLn2(J2D_TRACE_VERBOSE, " dx=%d dy=%d",
827
dstx, dsty);
828
829
// this accounts for lower-left origin of the source region
830
srcx = srcOps->xOffset + srcx;
831
srcy = srcOps->yOffset + srcOps->height - srcy - height;
832
833
// Note that glReadPixels() is extremely slow!
834
// So we call it only once and flip the image using memcpy.
835
j2d_glReadPixels(srcx, srcy, width, height,
836
pf.format, pf.type, pDst);
837
// It was checked above that width and height are positive.
838
flip(pDst, (juint) width, (juint) height, dstInfo.scanStride,
839
!pf.isPremult && !srcOps->isOpaque);
840
#ifdef MACOSX
841
if (srcOps->isOpaque) {
842
j2d_glPixelTransferf(GL_ALPHA_BIAS, 0.0);
843
}
844
#endif
845
j2d_glPixelStorei(GL_PACK_ROW_LENGTH, 0);
846
j2d_glPixelStorei(GL_PACK_ALIGNMENT, 4);
847
}
848
SurfaceData_InvokeRelease(env, dstOps, &dstInfo);
849
}
850
SurfaceData_InvokeUnlock(env, dstOps, &dstInfo);
851
}
852
853
void
854
OGLBlitLoops_CopyArea(JNIEnv *env,
855
OGLContext *oglc, OGLSDOps *dstOps,
856
jint x, jint y, jint width, jint height,
857
jint dx, jint dy)
858
{
859
SurfaceDataBounds srcBounds, dstBounds;
860
861
J2dTraceLn(J2D_TRACE_INFO, "OGLBlitLoops_CopyArea");
862
863
RETURN_IF_NULL(oglc);
864
RETURN_IF_NULL(dstOps);
865
RESET_PREVIOUS_OP();
866
867
J2dTraceLn4(J2D_TRACE_VERBOSE, " x=%d y=%d w=%d h=%d",
868
x, y, width, height);
869
J2dTraceLn2(J2D_TRACE_VERBOSE, " dx=%d dy=%d",
870
dx, dy);
871
872
srcBounds.x1 = x;
873
srcBounds.y1 = y;
874
srcBounds.x2 = srcBounds.x1 + width;
875
srcBounds.y2 = srcBounds.y1 + height;
876
dstBounds.x1 = x + dx;
877
dstBounds.y1 = y + dy;
878
dstBounds.x2 = dstBounds.x1 + width;
879
dstBounds.y2 = dstBounds.y1 + height;
880
881
// 6430601: manually clip src/dst parameters to work around
882
// some bugs in Sun's and Apple's OpenGL implementations
883
// (it's a good idea to restrict the source parameters anyway, since
884
// passing out of range parameters to glCopyPixels() will result in
885
// an OpenGL error)
886
SurfaceData_IntersectBoundsXYXY(&srcBounds,
887
0, 0, dstOps->width, dstOps->height);
888
SurfaceData_IntersectBoundsXYXY(&dstBounds,
889
0, 0, dstOps->width, dstOps->height);
890
SurfaceData_IntersectBlitBounds(&dstBounds, &srcBounds, -dx, -dy);
891
892
if (dstBounds.x1 < dstBounds.x2 && dstBounds.y1 < dstBounds.y2) {
893
#ifdef MACOSX
894
if (dstOps->isOpaque) {
895
// For some reason Apple's OpenGL implementation will fail
896
// to render glCopyPixels() when the src/dst rectangles are
897
// overlapping and glColorMask() has disabled writes to the
898
// alpha channel. The workaround is to temporarily re-enable
899
// the alpha channel during the glCopyPixels() operation.
900
j2d_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
901
}
902
#endif
903
904
OGLBlitSurfaceToSurface(oglc, dstOps, dstOps,
905
srcBounds.x1, srcBounds.y1,
906
srcBounds.x2, srcBounds.y2,
907
dstBounds.x1, dstBounds.y1,
908
dstBounds.x2, dstBounds.y2);
909
#ifdef MACOSX
910
if (dstOps->isOpaque) {
911
j2d_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
912
}
913
#endif
914
}
915
}
916
917
#endif /* !HEADLESS */
918
919