1/*
2 * Copyright (c) 2003, 2008, 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 <jlong.h>
29#include <jni_util.h>
30#include <math.h>
31
32#include "sun_java2d_opengl_OGLRenderer.h"
33
34#include "OGLRenderer.h"
35#include "OGLRenderQueue.h"
36#include "OGLSurfaceData.h"
37
38/**
39 * Note: Some of the methods in this file apply a "magic number"
40 * translation to line segments. The OpenGL specification lays out the
41 * "diamond exit rule" for line rasterization, but it is loose enough to
42 * allow for a wide range of line rendering hardware. (It appears that
43 * some hardware, such as the Nvidia GeForce2 series, does not even meet
44 * the spec in all cases.) As such it is difficult to find a mapping
45 * between the Java2D and OpenGL line specs that works consistently across
46 * all hardware combinations.
47 *
48 * Therefore the "magic numbers" you see here have been empirically derived
49 * after testing on a variety of graphics hardware in order to find some
50 * reasonable middle ground between the two specifications. The general
51 * approach is to apply a fractional translation to vertices so that they
52 * hit pixel centers and therefore touch the same pixels as in our other
53 * pipelines. Emphasis was placed on finding values so that OGL lines with
54 * a slope of +/- 1 hit all the same pixels as our other (software) loops.
55 * The stepping in other diagonal lines rendered with OGL may deviate
56 * slightly from those rendered with our software loops, but the most
57 * important thing is that these magic numbers ensure that all OGL lines
58 * hit the same endpoints as our software loops.
59 *
60 * If you find it necessary to change any of these magic numbers in the
61 * future, just be sure that you test the changes across a variety of
62 * hardware to ensure consistent rendering everywhere.
63 */
64
65void
66OGLRenderer_DrawLine(OGLContext *oglc, jint x1, jint y1, jint x2, jint y2)
67{
68 J2dTraceLn(J2D_TRACE_INFO, "OGLRenderer_DrawLine");
69
70 RETURN_IF_NULL(oglc);
71
72 CHECK_PREVIOUS_OP(GL_LINES);
73
74 if (y1 == y2) {
75 // horizontal
76 GLfloat fx1 = (GLfloat)x1;
77 GLfloat fx2 = (GLfloat)x2;
78 GLfloat fy = ((GLfloat)y1) + 0.2f;
79
80 if (x1 > x2) {
81 GLfloat t = fx1; fx1 = fx2; fx2 = t;
82 }
83
84 j2d_glVertex2f(fx1+0.2f, fy);
85 j2d_glVertex2f(fx2+1.2f, fy);
86 } else if (x1 == x2) {
87 // vertical
88 GLfloat fx = ((GLfloat)x1) + 0.2f;
89 GLfloat fy1 = (GLfloat)y1;
90 GLfloat fy2 = (GLfloat)y2;
91
92 if (y1 > y2) {
93 GLfloat t = fy1; fy1 = fy2; fy2 = t;
94 }
95
96 j2d_glVertex2f(fx, fy1+0.2f);
97 j2d_glVertex2f(fx, fy2+1.2f);
98 } else {
99 // diagonal
100 GLfloat fx1 = (GLfloat)x1;
101 GLfloat fy1 = (GLfloat)y1;
102 GLfloat fx2 = (GLfloat)x2;
103 GLfloat fy2 = (GLfloat)y2;
104
105 if (x1 < x2) {
106 fx1 += 0.2f;
107 fx2 += 1.0f;
108 } else {
109 fx1 += 0.8f;
110 fx2 -= 0.2f;
111 }
112
113 if (y1 < y2) {
114 fy1 += 0.2f;
115 fy2 += 1.0f;
116 } else {
117 fy1 += 0.8f;
118 fy2 -= 0.2f;
119 }
120
121 j2d_glVertex2f(fx1, fy1);
122 j2d_glVertex2f(fx2, fy2);
123 }
124}
125
126void
127OGLRenderer_DrawRect(OGLContext *oglc, jint x, jint y, jint w, jint h)
128{
129 J2dTraceLn(J2D_TRACE_INFO, "OGLRenderer_DrawRect");
130
131 if (w < 0 || h < 0) {
132 return;
133 }
134
135 RETURN_IF_NULL(oglc);
136
137 if (w < 2 || h < 2) {
138 // If one dimension is less than 2 then there is no
139 // gap in the middle - draw a solid filled rectangle.
140 CHECK_PREVIOUS_OP(GL_QUADS);
141 GLRECT_BODY_XYWH(x, y, w+1, h+1);
142 } else {
143 GLfloat fx1 = ((GLfloat)x) + 0.2f;
144 GLfloat fy1 = ((GLfloat)y) + 0.2f;
145 GLfloat fx2 = fx1 + ((GLfloat)w);
146 GLfloat fy2 = fy1 + ((GLfloat)h);
147
148 // Avoid drawing the endpoints twice.
149 // Also prefer including the endpoints in the
150 // horizontal sections which draw pixels faster.
151
152 CHECK_PREVIOUS_OP(GL_LINES);
153 // top
154 j2d_glVertex2f(fx1, fy1);
155 j2d_glVertex2f(fx2+1.0f, fy1);
156 // right
157 j2d_glVertex2f(fx2, fy1+1.0f);
158 j2d_glVertex2f(fx2, fy2);
159 // bottom
160 j2d_glVertex2f(fx1, fy2);
161 j2d_glVertex2f(fx2+1.0f, fy2);
162 // left
163 j2d_glVertex2f(fx1, fy1+1.0f);
164 j2d_glVertex2f(fx1, fy2);
165 }
166}
167
168void
169OGLRenderer_DrawPoly(OGLContext *oglc,
170 jint nPoints, jint isClosed,
171 jint transX, jint transY,
172 jint *xPoints, jint *yPoints)
173{
174 jboolean isEmpty = JNI_TRUE;
175 jint mx, my;
176 jint i;
177
178 J2dTraceLn(J2D_TRACE_INFO, "OGLRenderer_DrawPoly");
179
180 if (xPoints == NULL || yPoints == NULL) {
181 J2dRlsTraceLn(J2D_TRACE_ERROR,
182 "OGLRenderer_DrawPoly: points array is null");
183 return;
184 }
185
186 RETURN_IF_NULL(oglc);
187
188 // Note that BufferedRenderPipe.drawPoly() has already rejected polys
189 // with nPoints<2, so we can be certain here that we have nPoints>=2.
190
191 mx = xPoints[0];
192 my = yPoints[0];
193
194 CHECK_PREVIOUS_OP(GL_LINE_STRIP);
195 for (i = 0; i < nPoints; i++) {
196 jint x = xPoints[i];
197 jint y = yPoints[i];
198
199 isEmpty = isEmpty && (x == mx && y == my);
200
201 // Translate each vertex by a fraction so that we hit pixel centers.
202 j2d_glVertex2f((GLfloat)(x + transX) + 0.5f,
203 (GLfloat)(y + transY) + 0.5f);
204 }
205
206 if (isClosed && !isEmpty &&
207 (xPoints[nPoints-1] != mx ||
208 yPoints[nPoints-1] != my))
209 {
210 // In this case, the polyline's start and end positions are
211 // different and need to be closed manually; we do this by adding
212 // one more segment back to the starting position. Note that we
213 // do not need to fill in the last pixel (as we do in the following
214 // block) because we are returning to the starting pixel, which
215 // has already been filled in.
216 j2d_glVertex2f((GLfloat)(mx + transX) + 0.5f,
217 (GLfloat)(my + transY) + 0.5f);
218 RESET_PREVIOUS_OP(); // so that we don't leave the line strip open
219 } else if (!isClosed || isEmpty) {
220 // OpenGL omits the last pixel in a polyline, so we fix this by
221 // adding a one-pixel segment at the end. Also, if the polyline
222 // never went anywhere (isEmpty is true), we need to use this
223 // workaround to ensure that a single pixel is touched.
224 CHECK_PREVIOUS_OP(GL_LINES); // this closes the line strip first
225 mx = xPoints[nPoints-1] + transX;
226 my = yPoints[nPoints-1] + transY;
227 j2d_glVertex2i(mx, my);
228 j2d_glVertex2i(mx+1, my+1);
229 // no need for RESET_PREVIOUS_OP, as the line strip is no longer open
230 } else {
231 RESET_PREVIOUS_OP(); // so that we don't leave the line strip open
232 }
233}
234
235JNIEXPORT void JNICALL
236Java_sun_java2d_opengl_OGLRenderer_drawPoly
237 (JNIEnv *env, jobject oglr,
238 jintArray xpointsArray, jintArray ypointsArray,
239 jint nPoints, jboolean isClosed,
240 jint transX, jint transY)
241{
242 jint *xPoints, *yPoints;
243
244 J2dTraceLn(J2D_TRACE_INFO, "OGLRenderer_drawPoly");
245
246 xPoints = (jint *)
247 (*env)->GetPrimitiveArrayCritical(env, xpointsArray, NULL);
248 if (xPoints != NULL) {
249 yPoints = (jint *)
250 (*env)->GetPrimitiveArrayCritical(env, ypointsArray, NULL);
251 if (yPoints != NULL) {
252 OGLContext *oglc = OGLRenderQueue_GetCurrentContext();
253
254 OGLRenderer_DrawPoly(oglc,
255 nPoints, isClosed,
256 transX, transY,
257 xPoints, yPoints);
258
259 // 6358147: reset current state, and ensure rendering is
260 // flushed to dest
261 if (oglc != NULL) {
262 RESET_PREVIOUS_OP();
263 j2d_glFlush();
264 }
265
266 (*env)->ReleasePrimitiveArrayCritical(env, ypointsArray, yPoints,
267 JNI_ABORT);
268 }
269 (*env)->ReleasePrimitiveArrayCritical(env, xpointsArray, xPoints,
270 JNI_ABORT);
271 }
272}
273
274void
275OGLRenderer_DrawScanlines(OGLContext *oglc,
276 jint scanlineCount, jint *scanlines)
277{
278 J2dTraceLn(J2D_TRACE_INFO, "OGLRenderer_DrawScanlines");
279
280 RETURN_IF_NULL(oglc);
281 RETURN_IF_NULL(scanlines);
282
283 CHECK_PREVIOUS_OP(GL_LINES);
284 while (scanlineCount > 0) {
285 // Translate each vertex by a fraction so
286 // that we hit pixel centers.
287 GLfloat x1 = ((GLfloat)*(scanlines++)) + 0.2f;
288 GLfloat x2 = ((GLfloat)*(scanlines++)) + 1.2f;
289 GLfloat y = ((GLfloat)*(scanlines++)) + 0.5f;
290 j2d_glVertex2f(x1, y);
291 j2d_glVertex2f(x2, y);
292 scanlineCount--;
293 }
294}
295
296void
297OGLRenderer_FillRect(OGLContext *oglc, jint x, jint y, jint w, jint h)
298{
299 J2dTraceLn(J2D_TRACE_INFO, "OGLRenderer_FillRect");
300
301 if (w <= 0 || h <= 0) {
302 return;
303 }
304
305 RETURN_IF_NULL(oglc);
306
307 CHECK_PREVIOUS_OP(GL_QUADS);
308 GLRECT_BODY_XYWH(x, y, w, h);
309}
310
311void
312OGLRenderer_FillSpans(OGLContext *oglc, jint spanCount, jint *spans)
313{
314 J2dTraceLn(J2D_TRACE_INFO, "OGLRenderer_FillSpans");
315
316 RETURN_IF_NULL(oglc);
317 RETURN_IF_NULL(spans);
318
319 CHECK_PREVIOUS_OP(GL_QUADS);
320 while (spanCount > 0) {
321 jint x1 = *(spans++);
322 jint y1 = *(spans++);
323 jint x2 = *(spans++);
324 jint y2 = *(spans++);
325 GLRECT_BODY_XYXY(x1, y1, x2, y2);
326 spanCount--;
327 }
328}
329
330#define FILL_PGRAM(fx11, fy11, dx21, dy21, dx12, dy12) \
331 do { \
332 j2d_glVertex2f(fx11, fy11); \
333 j2d_glVertex2f(fx11 + dx21, fy11 + dy21); \
334 j2d_glVertex2f(fx11 + dx21 + dx12, fy11 + dy21 + dy12); \
335 j2d_glVertex2f(fx11 + dx12, fy11 + dy12); \
336 } while (0)
337
338void
339OGLRenderer_FillParallelogram(OGLContext *oglc,
340 jfloat fx11, jfloat fy11,
341 jfloat dx21, jfloat dy21,
342 jfloat dx12, jfloat dy12)
343{
344 J2dTraceLn6(J2D_TRACE_INFO,
345 "OGLRenderer_FillParallelogram "
346 "(x=%6.2f y=%6.2f "
347 "dx1=%6.2f dy1=%6.2f "
348 "dx2=%6.2f dy2=%6.2f)",
349 fx11, fy11,
350 dx21, dy21,
351 dx12, dy12);
352
353 RETURN_IF_NULL(oglc);
354
355 CHECK_PREVIOUS_OP(GL_QUADS);
356
357 FILL_PGRAM(fx11, fy11, dx21, dy21, dx12, dy12);
358}
359
360void
361OGLRenderer_DrawParallelogram(OGLContext *oglc,
362 jfloat fx11, jfloat fy11,
363 jfloat dx21, jfloat dy21,
364 jfloat dx12, jfloat dy12,
365 jfloat lwr21, jfloat lwr12)
366{
367 // dx,dy for line width in the "21" and "12" directions.
368 jfloat ldx21 = dx21 * lwr21;
369 jfloat ldy21 = dy21 * lwr21;
370 jfloat ldx12 = dx12 * lwr12;
371 jfloat ldy12 = dy12 * lwr12;
372
373 // calculate origin of the outer parallelogram
374 jfloat ox11 = fx11 - (ldx21 + ldx12) / 2.0f;
375 jfloat oy11 = fy11 - (ldy21 + ldy12) / 2.0f;
376
377 J2dTraceLn8(J2D_TRACE_INFO,
378 "OGLRenderer_DrawParallelogram "
379 "(x=%6.2f y=%6.2f "
380 "dx1=%6.2f dy1=%6.2f lwr1=%6.2f "
381 "dx2=%6.2f dy2=%6.2f lwr2=%6.2f)",
382 fx11, fy11,
383 dx21, dy21, lwr21,
384 dx12, dy12, lwr12);
385
386 RETURN_IF_NULL(oglc);
387
388 CHECK_PREVIOUS_OP(GL_QUADS);
389
390 // Only need to generate 4 quads if the interior still
391 // has a hole in it (i.e. if the line width ratio was
392 // less than 1.0)
393 if (lwr21 < 1.0f && lwr12 < 1.0f) {
394 // Note: "TOP", "BOTTOM", "LEFT" and "RIGHT" here are
395 // relative to whether the dxNN variables are positive
396 // and negative. The math works fine regardless of
397 // their signs, but for conceptual simplicity the
398 // comments will refer to the sides as if the dxNN
399 // were all positive. "TOP" and "BOTTOM" segments
400 // are defined by the dxy21 deltas. "LEFT" and "RIGHT"
401 // segments are defined by the dxy12 deltas.
402
403 // Each segment includes its starting corner and comes
404 // to just short of the following corner. Thus, each
405 // corner is included just once and the only lengths
406 // needed are the original parallelogram delta lengths
407 // and the "line width deltas". The sides will cover
408 // the following relative territories:
409 //
410 // T T T T T R
411 // L R
412 // L R
413 // L R
414 // L R
415 // L B B B B B
416
417 // TOP segment, to left side of RIGHT edge
418 // "width" of original pgram, "height" of hor. line size
419 fx11 = ox11;
420 fy11 = oy11;
421 FILL_PGRAM(fx11, fy11, dx21, dy21, ldx12, ldy12);
422
423 // RIGHT segment, to top of BOTTOM edge
424 // "width" of vert. line size , "height" of original pgram
425 fx11 = ox11 + dx21;
426 fy11 = oy11 + dy21;
427 FILL_PGRAM(fx11, fy11, ldx21, ldy21, dx12, dy12);
428
429 // BOTTOM segment, from right side of LEFT edge
430 // "width" of original pgram, "height" of hor. line size
431 fx11 = ox11 + dx12 + ldx21;
432 fy11 = oy11 + dy12 + ldy21;
433 FILL_PGRAM(fx11, fy11, dx21, dy21, ldx12, ldy12);
434
435 // LEFT segment, from bottom of TOP edge
436 // "width" of vert. line size , "height" of inner pgram
437 fx11 = ox11 + ldx12;
438 fy11 = oy11 + ldy12;
439 FILL_PGRAM(fx11, fy11, ldx21, ldy21, dx12, dy12);
440 } else {
441 // The line width ratios were large enough to consume
442 // the entire hole in the middle of the parallelogram
443 // so we can just issue one large quad for the outer
444 // parallelogram.
445 dx21 += ldx21;
446 dy21 += ldy21;
447 dx12 += ldx12;
448 dy12 += ldy12;
449 FILL_PGRAM(ox11, oy11, dx21, dy21, dx12, dy12);
450 }
451}
452
453static GLhandleARB aaPgramProgram = 0;
454
455/*
456 * This shader fills the space between an outer and inner parallelogram.
457 * It can be used to draw an outline by specifying both inner and outer
458 * values. It fills pixels by estimating what portion falls inside the
459 * outer shape, and subtracting an estimate of what portion falls inside
460 * the inner shape. Specifying both inner and outer values produces a
461 * standard "wide outline". Specifying an inner shape that falls far
462 * outside the outer shape allows the same shader to fill the outer
463 * shape entirely since pixels that fall within the outer shape are never
464 * inside the inner shape and so they are filled based solely on their
465 * coverage of the outer shape.
466 *
467 * The setup code renders this shader over the bounds of the outer
468 * shape (or the only shape in the case of a fill operation) and
469 * sets the texture 0 coordinates so that 0,0=>0,1=>1,1=>1,0 in those
470 * texture coordinates map to the four corners of the parallelogram.
471 * Similarly the texture 1 coordinates map the inner shape to the
472 * unit square as well, but in a different coordinate system.
473 *
474 * When viewed in the texture coordinate systems the parallelograms
475 * we are filling are unit squares, but the pixels have then become
476 * tiny parallelograms themselves. Both of the texture coordinate
477 * systems are affine transforms so the rate of change in X and Y
478 * of the texture coordinates are essentially constants and happen
479 * to correspond to the size and direction of the slanted sides of
480 * the distorted pixels relative to the "square mapped" boundary
481 * of the parallelograms.
482 *
483 * The shader uses the dFdx() and dFdy() functions to measure the "rate
484 * of change" of these texture coordinates and thus gets an accurate
485 * measure of the size and shape of a pixel relative to the two
486 * parallelograms. It then uses the bounds of the size and shape
487 * of a pixel to intersect with the unit square to estimate the
488 * coverage of the pixel. Unfortunately, without a lot more work
489 * to calculate the exact area of intersection between a unit
490 * square (the original parallelogram) and a parallelogram (the
491 * distorted pixel), this shader only approximates the pixel
492 * coverage, but emperically the estimate is very useful and
493 * produces visually pleasing results, if not theoretically accurate.
494 */
495static const char *aaPgramShaderSource =
496 "void main() {"
497 // Calculate the vectors for the "legs" of the pixel parallelogram
498 // for the outer parallelogram.
499 " vec2 oleg1 = dFdx(gl_TexCoord[0].st);"
500 " vec2 oleg2 = dFdy(gl_TexCoord[0].st);"
501 // Calculate the bounds of the distorted pixel parallelogram.
502 " vec2 corner = gl_TexCoord[0].st - (oleg1+oleg2)/2.0;"
503 " vec2 omin = min(corner, corner+oleg1);"
504 " omin = min(omin, corner+oleg2);"
505 " omin = min(omin, corner+oleg1+oleg2);"
506 " vec2 omax = max(corner, corner+oleg1);"
507 " omax = max(omax, corner+oleg2);"
508 " omax = max(omax, corner+oleg1+oleg2);"
509 // Calculate the vectors for the "legs" of the pixel parallelogram
510 // for the inner parallelogram.
511 " vec2 ileg1 = dFdx(gl_TexCoord[1].st);"
512 " vec2 ileg2 = dFdy(gl_TexCoord[1].st);"
513 // Calculate the bounds of the distorted pixel parallelogram.
514 " corner = gl_TexCoord[1].st - (ileg1+ileg2)/2.0;"
515 " vec2 imin = min(corner, corner+ileg1);"
516 " imin = min(imin, corner+ileg2);"
517 " imin = min(imin, corner+ileg1+ileg2);"
518 " vec2 imax = max(corner, corner+ileg1);"
519 " imax = max(imax, corner+ileg2);"
520 " imax = max(imax, corner+ileg1+ileg2);"
521 // Clamp the bounds of the parallelograms to the unit square to
522 // estimate the intersection of the pixel parallelogram with
523 // the unit square. The ratio of the 2 rectangle areas is a
524 // reasonable estimate of the proportion of coverage.
525 " vec2 o1 = clamp(omin, 0.0, 1.0);"
526 " vec2 o2 = clamp(omax, 0.0, 1.0);"
527 " float oint = (o2.y-o1.y)*(o2.x-o1.x);"
528 " float oarea = (omax.y-omin.y)*(omax.x-omin.x);"
529 " vec2 i1 = clamp(imin, 0.0, 1.0);"
530 " vec2 i2 = clamp(imax, 0.0, 1.0);"
531 " float iint = (i2.y-i1.y)*(i2.x-i1.x);"
532 " float iarea = (imax.y-imin.y)*(imax.x-imin.x);"
533 // Proportion of pixel in outer shape minus the proportion
534 // of pixel in the inner shape == the coverage of the pixel
535 // in the area between the two.
536 " float coverage = oint/oarea - iint / iarea;"
537 " gl_FragColor = gl_Color * coverage;"
538 "}";
539
540#define ADJUST_PGRAM(V1, DV, V2) \
541 do { \
542 if ((DV) >= 0) { \
543 (V2) += (DV); \
544 } else { \
545 (V1) += (DV); \
546 } \
547 } while (0)
548
549// Invert the following transform:
550// DeltaT(0, 0) == (0, 0)
551// DeltaT(1, 0) == (DX1, DY1)
552// DeltaT(0, 1) == (DX2, DY2)
553// DeltaT(1, 1) == (DX1+DX2, DY1+DY2)
554// TM00 = DX1, TM01 = DX2, (TM02 = X11)
555// TM10 = DY1, TM11 = DY2, (TM12 = Y11)
556// Determinant = TM00*TM11 - TM01*TM10
557// = DX1*DY2 - DX2*DY1
558// Inverse is:
559// IM00 = TM11/det, IM01 = -TM01/det
560// IM10 = -TM10/det, IM11 = TM00/det
561// IM02 = (TM01 * TM12 - TM11 * TM02) / det,
562// IM12 = (TM10 * TM02 - TM00 * TM12) / det,
563
564#define DECLARE_MATRIX(MAT) \
565 jfloat MAT ## 00, MAT ## 01, MAT ## 02, MAT ## 10, MAT ## 11, MAT ## 12
566
567#define GET_INVERTED_MATRIX(MAT, X11, Y11, DX1, DY1, DX2, DY2, RET_CODE) \
568 do { \
569 jfloat det = DX1*DY2 - DX2*DY1; \
570 if (det == 0) { \
571 RET_CODE; \
572 } \
573 MAT ## 00 = DY2/det; \
574 MAT ## 01 = -DX2/det; \
575 MAT ## 10 = -DY1/det; \
576 MAT ## 11 = DX1/det; \
577 MAT ## 02 = (DX2 * Y11 - DY2 * X11) / det; \
578 MAT ## 12 = (DY1 * X11 - DX1 * Y11) / det; \
579 } while (0)
580
581#define TRANSFORM(MAT, TX, TY, X, Y) \
582 do { \
583 TX = (X) * MAT ## 00 + (Y) * MAT ## 01 + MAT ## 02; \
584 TY = (X) * MAT ## 10 + (Y) * MAT ## 11 + MAT ## 12; \
585 } while (0)
586
587void
588OGLRenderer_FillAAParallelogram(OGLContext *oglc, OGLSDOps *dstOps,
589 jfloat fx11, jfloat fy11,
590 jfloat dx21, jfloat dy21,
591 jfloat dx12, jfloat dy12)
592{
593 DECLARE_MATRIX(om);
594 // parameters for parallelogram bounding box
595 jfloat bx11, by11, bx22, by22;
596 // parameters for uv texture coordinates of parallelogram corners
597 jfloat u11, v11, u12, v12, u21, v21, u22, v22;
598
599 J2dTraceLn6(J2D_TRACE_INFO,
600 "OGLRenderer_FillAAParallelogram "
601 "(x=%6.2f y=%6.2f "
602 "dx1=%6.2f dy1=%6.2f "
603 "dx2=%6.2f dy2=%6.2f)",
604 fx11, fy11,
605 dx21, dy21,
606 dx12, dy12);
607
608 RETURN_IF_NULL(oglc);
609 RETURN_IF_NULL(dstOps);
610
611 GET_INVERTED_MATRIX(om, fx11, fy11, dx21, dy21, dx12, dy12,
612 return);
613
614 CHECK_PREVIOUS_OP(OGL_STATE_PGRAM_OP);
615
616 bx11 = bx22 = fx11;
617 by11 = by22 = fy11;
618 ADJUST_PGRAM(bx11, dx21, bx22);
619 ADJUST_PGRAM(by11, dy21, by22);
620 ADJUST_PGRAM(bx11, dx12, bx22);
621 ADJUST_PGRAM(by11, dy12, by22);
622 bx11 = (jfloat) floor(bx11);
623 by11 = (jfloat) floor(by11);
624 bx22 = (jfloat) ceil(bx22);
625 by22 = (jfloat) ceil(by22);
626
627 TRANSFORM(om, u11, v11, bx11, by11);
628 TRANSFORM(om, u21, v21, bx22, by11);
629 TRANSFORM(om, u12, v12, bx11, by22);
630 TRANSFORM(om, u22, v22, bx22, by22);
631
632 j2d_glBegin(GL_QUADS);
633 j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, u11, v11);
634 j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 5.f, 5.f);
635 j2d_glVertex2f(bx11, by11);
636 j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, u21, v21);
637 j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 6.f, 5.f);
638 j2d_glVertex2f(bx22, by11);
639 j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, u22, v22);
640 j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 6.f, 6.f);
641 j2d_glVertex2f(bx22, by22);
642 j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, u12, v12);
643 j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 5.f, 6.f);
644 j2d_glVertex2f(bx11, by22);
645 j2d_glEnd();
646}
647
648void
649OGLRenderer_FillAAParallelogramInnerOuter(OGLContext *oglc, OGLSDOps *dstOps,
650 jfloat ox11, jfloat oy11,
651 jfloat ox21, jfloat oy21,
652 jfloat ox12, jfloat oy12,
653 jfloat ix11, jfloat iy11,
654 jfloat ix21, jfloat iy21,
655 jfloat ix12, jfloat iy12)
656{
657 DECLARE_MATRIX(om);
658 DECLARE_MATRIX(im);
659 // parameters for parallelogram bounding box
660 jfloat bx11, by11, bx22, by22;
661 // parameters for uv texture coordinates of outer parallelogram corners
662 jfloat ou11, ov11, ou12, ov12, ou21, ov21, ou22, ov22;
663 // parameters for uv texture coordinates of inner parallelogram corners
664 jfloat iu11, iv11, iu12, iv12, iu21, iv21, iu22, iv22;
665
666 RETURN_IF_NULL(oglc);
667 RETURN_IF_NULL(dstOps);
668
669 GET_INVERTED_MATRIX(im, ix11, iy11, ix21, iy21, ix12, iy12,
670 // inner parallelogram is degenerate
671 // therefore it encloses no area
672 // fill outer
673 OGLRenderer_FillAAParallelogram(oglc, dstOps,
674 ox11, oy11,
675 ox21, oy21,
676 ox12, oy12);
677 return);
678 GET_INVERTED_MATRIX(om, ox11, oy11, ox21, oy21, ox12, oy12,
679 return);
680
681 CHECK_PREVIOUS_OP(OGL_STATE_PGRAM_OP);
682
683 bx11 = bx22 = ox11;
684 by11 = by22 = oy11;
685 ADJUST_PGRAM(bx11, ox21, bx22);
686 ADJUST_PGRAM(by11, oy21, by22);
687 ADJUST_PGRAM(bx11, ox12, bx22);
688 ADJUST_PGRAM(by11, oy12, by22);
689 bx11 = (jfloat) floor(bx11);
690 by11 = (jfloat) floor(by11);
691 bx22 = (jfloat) ceil(bx22);
692 by22 = (jfloat) ceil(by22);
693
694 TRANSFORM(om, ou11, ov11, bx11, by11);
695 TRANSFORM(om, ou21, ov21, bx22, by11);
696 TRANSFORM(om, ou12, ov12, bx11, by22);
697 TRANSFORM(om, ou22, ov22, bx22, by22);
698
699 TRANSFORM(im, iu11, iv11, bx11, by11);
700 TRANSFORM(im, iu21, iv21, bx22, by11);
701 TRANSFORM(im, iu12, iv12, bx11, by22);
702 TRANSFORM(im, iu22, iv22, bx22, by22);
703
704 j2d_glBegin(GL_QUADS);
705 j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, ou11, ov11);
706 j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, iu11, iv11);
707 j2d_glVertex2f(bx11, by11);
708 j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, ou21, ov21);
709 j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, iu21, iv21);
710 j2d_glVertex2f(bx22, by11);
711 j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, ou22, ov22);
712 j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, iu22, iv22);
713 j2d_glVertex2f(bx22, by22);
714 j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, ou12, ov12);
715 j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, iu12, iv12);
716 j2d_glVertex2f(bx11, by22);
717 j2d_glEnd();
718}
719
720void
721OGLRenderer_DrawAAParallelogram(OGLContext *oglc, OGLSDOps *dstOps,
722 jfloat fx11, jfloat fy11,
723 jfloat dx21, jfloat dy21,
724 jfloat dx12, jfloat dy12,
725 jfloat lwr21, jfloat lwr12)
726{
727 // dx,dy for line width in the "21" and "12" directions.
728 jfloat ldx21, ldy21, ldx12, ldy12;
729 // parameters for "outer" parallelogram
730 jfloat ofx11, ofy11, odx21, ody21, odx12, ody12;
731 // parameters for "inner" parallelogram
732 jfloat ifx11, ify11, idx21, idy21, idx12, idy12;
733
734 J2dTraceLn8(J2D_TRACE_INFO,
735 "OGLRenderer_DrawAAParallelogram "
736 "(x=%6.2f y=%6.2f "
737 "dx1=%6.2f dy1=%6.2f lwr1=%6.2f "
738 "dx2=%6.2f dy2=%6.2f lwr2=%6.2f)",
739 fx11, fy11,
740 dx21, dy21, lwr21,
741 dx12, dy12, lwr12);
742
743 RETURN_IF_NULL(oglc);
744 RETURN_IF_NULL(dstOps);
745
746 // calculate true dx,dy for line widths from the "line width ratios"
747 ldx21 = dx21 * lwr21;
748 ldy21 = dy21 * lwr21;
749 ldx12 = dx12 * lwr12;
750 ldy12 = dy12 * lwr12;
751
752 // calculate coordinates of the outer parallelogram
753 ofx11 = fx11 - (ldx21 + ldx12) / 2.0f;
754 ofy11 = fy11 - (ldy21 + ldy12) / 2.0f;
755 odx21 = dx21 + ldx21;
756 ody21 = dy21 + ldy21;
757 odx12 = dx12 + ldx12;
758 ody12 = dy12 + ldy12;
759
760 // Only process the inner parallelogram if the line width ratio
761 // did not consume the entire interior of the parallelogram
762 // (i.e. if the width ratio was less than 1.0)
763 if (lwr21 < 1.0f && lwr12 < 1.0f) {
764 // calculate coordinates of the inner parallelogram
765 ifx11 = fx11 + (ldx21 + ldx12) / 2.0f;
766 ify11 = fy11 + (ldy21 + ldy12) / 2.0f;
767 idx21 = dx21 - ldx21;
768 idy21 = dy21 - ldy21;
769 idx12 = dx12 - ldx12;
770 idy12 = dy12 - ldy12;
771
772 OGLRenderer_FillAAParallelogramInnerOuter(oglc, dstOps,
773 ofx11, ofy11,
774 odx21, ody21,
775 odx12, ody12,
776 ifx11, ify11,
777 idx21, idy21,
778 idx12, idy12);
779 } else {
780 OGLRenderer_FillAAParallelogram(oglc, dstOps,
781 ofx11, ofy11,
782 odx21, ody21,
783 odx12, ody12);
784 }
785}
786
787void
788OGLRenderer_EnableAAParallelogramProgram()
789{
790 J2dTraceLn(J2D_TRACE_INFO, "OGLRenderer_EnableAAParallelogramProgram");
791
792 if (aaPgramProgram == 0) {
793 aaPgramProgram = OGLContext_CreateFragmentProgram(aaPgramShaderSource);
794 if (aaPgramProgram == 0) {
795 J2dRlsTraceLn(J2D_TRACE_ERROR,
796 "OGLRenderer_EnableAAParallelogramProgram: "
797 "error creating program");
798 return;
799 }
800 }
801 j2d_glUseProgramObjectARB(aaPgramProgram);
802}
803
804void
805OGLRenderer_DisableAAParallelogramProgram()
806{
807 J2dTraceLn(J2D_TRACE_INFO, "OGLRenderer_DisableAAParallelogramProgram");
808
809 j2d_glUseProgramObjectARB(0);
810}
811
812#endif /* !HEADLESS */
813