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 | |
65 | void |
66 | OGLRenderer_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 | |
126 | void |
127 | OGLRenderer_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 | |
168 | void |
169 | OGLRenderer_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 | |
235 | JNIEXPORT void JNICALL |
236 | Java_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 | |
274 | void |
275 | OGLRenderer_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 | |
296 | void |
297 | OGLRenderer_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 | |
311 | void |
312 | OGLRenderer_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 | |
338 | void |
339 | OGLRenderer_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 | |
360 | void |
361 | OGLRenderer_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 | |
453 | static 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 | */ |
495 | static 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 | |
587 | void |
588 | OGLRenderer_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 | |
648 | void |
649 | OGLRenderer_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 | |
720 | void |
721 | OGLRenderer_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 | |
787 | void |
788 | OGLRenderer_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 | |
804 | void |
805 | OGLRenderer_DisableAAParallelogramProgram() |
806 | { |
807 | J2dTraceLn(J2D_TRACE_INFO, "OGLRenderer_DisableAAParallelogramProgram" ); |
808 | |
809 | j2d_glUseProgramObjectARB(0); |
810 | } |
811 | |
812 | #endif /* !HEADLESS */ |
813 | |