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