1 /*
2 * Copyright (c) 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 <jlong.h>
29 #include <jni_util.h>
30 #include <math.h>
31
32 #include "sun_java2d_metal_MTLRenderer.h"
33
34 #include "MTLRenderer.h"
35 #include "MTLRenderQueue.h"
36 #include "MTLSurfaceData.h"
37 #include "MTLUtils.h"
38 #import "MTLLayer.h"
39
40 /**
41 * Note: Some of the methods in this file apply a "magic number"
42 * translation to line segments. The OpenGL specification lays out the
43 * "diamond exit rule" for line rasterization, but it is loose enough to
44 * allow for a wide range of line rendering hardware. (It appears that
45 * some hardware, such as the Nvidia GeForce2 series, does not even meet
46 * the spec in all cases.) As such it is difficult to find a mapping
47 * between the Java2D and OpenGL line specs that works consistently across
48 * all hardware combinations.
49 *
50 * Therefore the "magic numbers" you see here have been empirically derived
51 * after testing on a variety of graphics hardware in order to find some
52 * reasonable middle ground between the two specifications. The general
53 * approach is to apply a fractional translation to vertices so that they
54 * hit pixel centers and therefore touch the same pixels as in our other
55 * pipelines. Emphasis was placed on finding values so that MTL lines with
56 * a slope of +/- 1 hit all the same pixels as our other (software) loops.
57 * The stepping in other diagonal lines rendered with MTL may deviate
58 * slightly from those rendered with our software loops, but the most
59 * important thing is that these magic numbers ensure that all MTL lines
60 * hit the same endpoints as our software loops.
61 *
62 * If you find it necessary to change any of these magic numbers in the
63 * future, just be sure that you test the changes across a variety of
64 * hardware to ensure consistent rendering everywhere.
65 */
66
67 void MTLRenderer_DrawLine(MTLContext *mtlc, BMTLSDOps * dstOps, jint x1, jint y1, jint x2, jint y2) {
68 if (mtlc == NULL || dstOps == NULL || dstOps->pTexture == NULL) {
69 J2dTraceLn(J2D_TRACE_ERROR, "MTLRenderer_DrawLine: dest is null");
70 return;
71 }
72
73 J2dTraceLn5(J2D_TRACE_INFO, "MTLRenderer_DrawLine (x1=%1.2f y1=%1.2f x2=%1.2f y2=%1.2f), dst tex=%p", x1, y1, x2, y2, dstOps->pTexture);
74
75 id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dstOps];
76 if (mtlEncoder == nil)
77 return;
78
79 struct Vertex verts[2] = {
80 {{x1, y1}},
81 {{x2, y2}}
82 };
83
84 [mtlEncoder setVertexBytes:verts length:sizeof(verts) atIndex:MeshVertexBuffer];
85 [mtlEncoder drawPrimitives:MTLPrimitiveTypeLine vertexStart:0 vertexCount:2];
86 }
87
88 void MTLRenderer_DrawRect(MTLContext *mtlc, BMTLSDOps * dstOps, jint x, jint y, jint w, jint h) {
89 if (mtlc == NULL || dstOps == NULL || dstOps->pTexture == NULL) {
90 J2dTraceLn(J2D_TRACE_ERROR, "MTLRenderer_DrawRect: dest is null");
91 return;
92 }
93
94 id<MTLTexture> dest = dstOps->pTexture;
95 J2dTraceLn5(J2D_TRACE_INFO, "MTLRenderer_DrawRect (x=%d y=%d w=%d h=%d), dst tex=%p", x, y, w, h, dest);
96
97 // TODO: use DrawParallelogram(x, y, w, h, lw=1, lh=1)
98 id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dstOps];
99 if (mtlEncoder == nil)
100 return;
101
102 const int verticesCount = 5;
103 struct Vertex vertices[5] = {
104 {{x, y}},
105 {{x + w, y}},
106 {{x + w, y + h}},
107 {{x, y + h}},
108 {{x, y}},
109 };
110 [mtlEncoder setVertexBytes:vertices length:sizeof(vertices) atIndex:MeshVertexBuffer];
111 [mtlEncoder drawPrimitives:MTLPrimitiveTypeLineStrip vertexStart:0 vertexCount:verticesCount];
112 }
113
114 const int POLYLINE_BUF_SIZE = 64;
115
116 NS_INLINE void fillVertex(struct Vertex * vertex, int x, int y) {
117 vertex->position[0] = x;
118 vertex->position[1] = y;
119 }
120
121 void MTLRenderer_DrawPoly(MTLContext *mtlc, BMTLSDOps * dstOps,
122 jint nPoints, jint isClosed,
123 jint transX, jint transY,
124 jint *xPoints, jint *yPoints)
125 {
126 // Note that BufferedRenderPipe.drawPoly() has already rejected polys
127 // with nPoints<2, so we can be certain here that we have nPoints>=2.
128 if (xPoints == NULL || yPoints == NULL || nPoints < 2) { // just for insurance
129 J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_DrawPoly: points array is empty");
130 return;
131 }
132
133 if (mtlc == NULL || dstOps == NULL || dstOps->pTexture == NULL) {
134 J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_DrawPoly: dest is null");
135 return;
136 }
137
138 J2dTraceLn4(J2D_TRACE_INFO, "MTLRenderer_DrawPoly: %d points, transX=%d, transY=%d, dst tex=%p", nPoints, transX, transY, dstOps->pTexture);
139
140 __block struct {
141 struct Vertex verts[POLYLINE_BUF_SIZE];
142 } pointsChunk;
143
144 // We intend to submit draw commands in batches of POLYLINE_BUF_SIZE vertices at a time
145 // Subsequent batches need to be connected - so end point in one batch is repeated as first point in subsequent batch
146 // This inflates the total number of points by a factor of number of batches of size POLYLINE_BUF_SIZE
147 nPoints += (nPoints/POLYLINE_BUF_SIZE);
148
149 jint prevX = *(xPoints++);
150 jint prevY = *(yPoints++);
151 const jint firstX = prevX;
152 const jint firstY = prevY;
153 while (nPoints > 0) {
154 const bool isLastChunk = nPoints <= POLYLINE_BUF_SIZE;
155 __block int chunkSize = isLastChunk ? nPoints : POLYLINE_BUF_SIZE;
156
157 fillVertex(pointsChunk.verts, prevX + transX, prevY + transY);
158
159 for (int i = 1; i < chunkSize; i++) {
160 prevX = *(xPoints++);
161 prevY = *(yPoints++);
162 fillVertex(pointsChunk.verts + i, prevX + transX, prevY + transY);
163 }
164
165 bool drawCloseSegment = false;
166 if (isClosed && isLastChunk) {
167 if (chunkSize + 2 <= POLYLINE_BUF_SIZE) {
168 fillVertex(pointsChunk.verts + chunkSize, firstX + transX, firstY + transY);
169 ++chunkSize;
170 } else
171 drawCloseSegment = true;
172 }
173
174 nPoints -= chunkSize;
175 id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dstOps];
176 if (mtlEncoder == nil)
177 return;
178
179 [mtlEncoder setVertexBytes:pointsChunk.verts length:sizeof(pointsChunk.verts) atIndex:MeshVertexBuffer];
180 [mtlEncoder drawPrimitives:MTLPrimitiveTypeLineStrip vertexStart:0 vertexCount:chunkSize];
181
182 if (drawCloseSegment) {
183 struct Vertex vertices[2] = {
184 {{prevX + transX, prevY + transY}},
185 {{firstX + transX, firstY + transY}},
186 };
187
188 [mtlEncoder setVertexBytes:vertices length:sizeof(vertices) atIndex:MeshVertexBuffer];
189 [mtlEncoder drawPrimitives:MTLPrimitiveTypeLine vertexStart:0 vertexCount:2];
190 }
191 }
192 }
193
194 JNIEXPORT void JNICALL
195 Java_sun_java2d_metal_MTLRenderer_drawPoly
196 (JNIEnv *env, jobject mtlr,
197 jintArray xpointsArray, jintArray ypointsArray,
198 jint nPoints, jboolean isClosed,
199 jint transX, jint transY)
200 {
201 jint *xPoints, *yPoints;
202 //TODO
203 J2dTraceLn(J2D_TRACE_ERROR, "MTLRenderer_drawPoly -- :TODO");
204 }
205
206 void
207 MTLRenderer_DrawScanlines(MTLContext *mtlc, BMTLSDOps * dstOps,
208 jint scanlineCount, jint *scanlines)
209 {
210
211 J2dTraceLn2(J2D_TRACE_INFO, "MTLRenderer_DrawScanlines (scanlineCount=%d), dst tex=%p", scanlineCount, dstOps->pTexture);
212 if (mtlc == NULL || dstOps == NULL || dstOps->pTexture == NULL) {
213 J2dTraceLn(J2D_TRACE_ERROR, "MTLRenderer_DrawScanlines: dest is null");
214 return;
215 }
216
217 id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dstOps];
218
219 if (mtlEncoder == nil) return;
220
221 struct Vertex verts[2*scanlineCount];
222
223 for (int j = 0, i = 0; j < scanlineCount; j++) {
224 // Translate each vertex by a fraction so
225 // that we hit pixel centers.
226 float x1 = ((float)*(scanlines++)) + 0.2f;
227 float x2 = ((float)*(scanlines++)) + 1.2f;
228 float y = ((float)*(scanlines++)) + 0.5f;
229 struct Vertex v1 = {{x1, y}};
230 struct Vertex v2 = {{x2, y}};
231 verts[i++] = v1;
232 verts[i++] = v2;
233 }
234
235 [mtlEncoder setVertexBytes:verts length:sizeof(verts) atIndex:MeshVertexBuffer];
236 [mtlEncoder drawPrimitives:MTLPrimitiveTypeLine vertexStart:0 vertexCount:2*scanlineCount];
237 }
238
239 void
240 MTLRenderer_FillRect(MTLContext *mtlc, BMTLSDOps * dstOps, jint x, jint y, jint w, jint h)
241 {
242 J2dTraceLn(J2D_TRACE_INFO, "MTLRenderer_FillRect");
243
244 if (mtlc == NULL || dstOps == NULL || dstOps->pTexture == NULL) {
245 J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_FillRect: current dest is null");
246 return;
247 }
248
249 struct Vertex verts[QUAD_VERTEX_COUNT] = {
250 { {x, y}},
251 { {x, y+h}},
252 { {x+w, y}},
253 { {x+w, y+h}
254 }};
255
256
257 id<MTLTexture> dest = dstOps->pTexture;
258 J2dTraceLn5(J2D_TRACE_INFO, "MTLRenderer_FillRect (x=%d y=%d w=%d h=%d), dst tex=%p", x, y, w, h, dest);
259
260 // Encode render command.
261 id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dstOps];
262 if (mtlEncoder == nil)
263 return;
264
265 [mtlEncoder setVertexBytes:verts length:sizeof(verts) atIndex:MeshVertexBuffer];
266 [mtlEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount: QUAD_VERTEX_COUNT];
267 }
268
269 void MTLRenderer_FillSpans(MTLContext *mtlc, BMTLSDOps * dstOps, jint spanCount, jint *spans)
270 {
271 J2dTraceLn(J2D_TRACE_INFO, "MTLRenderer_FillSpans");
272 if (mtlc == NULL || dstOps == NULL || dstOps->pTexture == NULL) {
273 J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_FillSpans: dest is null");
274 return;
275 }
276
277 // MTLRenderCommandEncoder setVertexBytes usage is recommended if the data is of 4KB.
278
279 // We use a buffer that closely matches the 4KB limit size
280 // This buffer is resued multiple times to encode draw calls of a triangle list
281 // NOTE : Due to nature of *spans data - it is not possible to use triangle strip.
282 // We use triangle list to draw spans
283
284 // Destination texture to which render commands are encoded
285 id<MTLTexture> dest = dstOps->pTexture;
286 id<MTLTexture> destAA = nil;
287 BOOL isDestOpaque = dstOps->isOpaque;
288 if (mtlc.clip.stencilMaskGenerationInProgress == JNI_TRUE) {
289 dest = dstOps->pStencilData;
290 isDestOpaque = NO;
291 }
292 id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dest isDstOpaque:isDestOpaque];
293 if (mtlEncoder == nil) {
294 J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_FillSpans: mtlEncoder is nil");
295 return;
296 }
297
298 // This is the max no of vertices (of struct Vertex - 8 bytes) we can accomodate in 4KB
299 const int TOTAL_VERTICES_IN_BLOCK = 510;
300 struct Vertex vertexList[TOTAL_VERTICES_IN_BLOCK]; // a total of 170 triangles ==> 85 spans
301
302 int counter = 0;
303 jint *aaspans = spans;
304 for (int i = 0; i < spanCount; i++) {
305 jfloat x1 = *(spans++);
306 jfloat y1 = *(spans++);
307 jfloat x2 = *(spans++);
308 jfloat y2 = *(spans++);
309
310 struct Vertex verts[6] = {
311 {{x1, y1}},
312 {{x1, y2}},
313 {{x2, y1}},
314
315 {{x1, y2}},
316 {{x2, y1}},
317 {{x2, y2}
318 }};
319
320 memcpy(&vertexList[counter], &verts, sizeof(verts));
321 counter += 6;
322
323 // If vertexList buffer full
324 if (counter % TOTAL_VERTICES_IN_BLOCK == 0) {
325 [mtlEncoder setVertexBytes:vertexList length:sizeof(vertexList) atIndex:MeshVertexBuffer];
326 [mtlEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:TOTAL_VERTICES_IN_BLOCK];
327 counter = 0;
328 }
329 }
330
331 // Draw triangles using remaining vertices if any
332 if (counter != 0) {
333 [mtlEncoder setVertexBytes:vertexList length:sizeof(vertexList) atIndex:MeshVertexBuffer];
334 [mtlEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:counter];
335 }
336 }
337
338 void
339 MTLRenderer_FillParallelogram(MTLContext *mtlc, BMTLSDOps * dstOps,
340 jfloat fx11, jfloat fy11,
341 jfloat dx21, jfloat dy21,
342 jfloat dx12, jfloat dy12)
343 {
344
345 if (mtlc == NULL || dstOps == NULL || dstOps->pTexture == NULL) {
346 J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_FillParallelogram: current dest is null");
347 return;
348 }
349
350 id<MTLTexture> dest = dstOps->pTexture;
351 J2dTraceLn7(J2D_TRACE_INFO,
352 "MTLRenderer_FillParallelogram "
353 "(x=%6.2f y=%6.2f "
354 "dx1=%6.2f dy1=%6.2f "
355 "dx2=%6.2f dy2=%6.2f dst tex=%p)",
356 fx11, fy11,
357 dx21, dy21,
358 dx12, dy12, dest);
359
360 struct Vertex verts[QUAD_VERTEX_COUNT] = {
361 { {fx11, fy11}},
362 { {fx11+dx21, fy11+dy21}},
363 { {fx11+dx12, fy11+dy12}},
364 { {fx11 + dx21 + dx12, fy11+ dy21 + dy12}
365 }};
366
367 // Encode render command.
368 id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dstOps];
369 if (mtlEncoder == nil)
370 return;
371
372 [mtlEncoder setVertexBytes:verts length:sizeof(verts) atIndex:MeshVertexBuffer];
373 [mtlEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount: QUAD_VERTEX_COUNT];
374 }
375
376 void
377 MTLRenderer_DrawParallelogram(MTLContext *mtlc, BMTLSDOps * dstOps,
378 jfloat fx11, jfloat fy11,
379 jfloat dx21, jfloat dy21,
380 jfloat dx12, jfloat dy12,
381 jfloat lwr21, jfloat lwr12)
382 {
383 // dx,dy for line width in the "21" and "12" directions.
384 jfloat ldx21 = dx21 * lwr21;
385 jfloat ldy21 = dy21 * lwr21;
386 jfloat ldx12 = dx12 * lwr12;
387 jfloat ldy12 = dy12 * lwr12;
388
389 // calculate origin of the outer parallelogram
390 jfloat ox11 = fx11 - (ldx21 + ldx12) / 2.0f;
391 jfloat oy11 = fy11 - (ldy21 + ldy12) / 2.0f;
392
393 J2dTraceLn8(J2D_TRACE_INFO,
394 "MTLRenderer_DrawParallelogram "
395 "(x=%6.2f y=%6.2f "
396 "dx1=%6.2f dy1=%6.2f lwr1=%6.2f "
397 "dx2=%6.2f dy2=%6.2f lwr2=%6.2f)",
398 fx11, fy11,
399 dx21, dy21, lwr21,
400 dx12, dy12, lwr12);
401
402
403 // Only need to generate 4 quads if the interior still
404 // has a hole in it (i.e. if the line width ratio was
405 // less than 1.0)
406 if (lwr21 < 1.0f && lwr12 < 1.0f) {
407
408 // Note: "TOP", "BOTTOM", "LEFT" and "RIGHT" here are
409 // relative to whether the dxNN variables are positive
410 // and negative. The math works fine regardless of
411 // their signs, but for conceptual simplicity the
412 // comments will refer to the sides as if the dxNN
413 // were all positive. "TOP" and "BOTTOM" segments
414 // are defined by the dxy21 deltas. "LEFT" and "RIGHT"
415 // segments are defined by the dxy12 deltas.
416
417 // Each segment includes its starting corner and comes
418 // to just short of the following corner. Thus, each
419 // corner is included just once and the only lengths
420 // needed are the original parallelogram delta lengths
421 // and the "line width deltas". The sides will cover
422 // the following relative territories:
423 //
424 // T T T T T R
425 // L R
426 // L R
427 // L R
428 // L R
429 // L B B B B B
430
431 // Every segment is drawn as a filled Parallelogram quad
432 // Each quad is encoded using two triangles
433 // For 4 segments - there are 8 triangles in total
434 // Each triangle has 3 vertices
435 const int TOTAL_VERTICES = 8 * 3;
436 struct Vertex vertexList[TOTAL_VERTICES];
437 int i = 0;
438
439 // TOP segment, to left side of RIGHT edge
440 // "width" of original pgram, "height" of hor. line size
441 fx11 = ox11;
442 fy11 = oy11;
443
444 fillVertex(vertexList + (i++), fx11, fy11);
445 fillVertex(vertexList + (i++), fx11 + dx21, fy11 + dy21);
446 fillVertex(vertexList + (i++), fx11 + dx21 + ldx12, fy11 + dy21 + ldy12);
447
448 fillVertex(vertexList + (i++), fx11 + dx21 + ldx12, fy11 + dy21 + ldy12);
449 fillVertex(vertexList + (i++), fx11 + ldx12, fy11 + ldy12);
450 fillVertex(vertexList + (i++), fx11, fy11);
451
452 // RIGHT segment, to top of BOTTOM edge
453 // "width" of vert. line size , "height" of original pgram
454 fx11 = ox11 + dx21;
455 fy11 = oy11 + dy21;
456 fillVertex(vertexList + (i++), fx11, fy11);
457 fillVertex(vertexList + (i++), fx11 + ldx21, fy11 + ldy21);
458 fillVertex(vertexList + (i++), fx11 + ldx21 + dx12, fy11 + ldy21 + dy12);
459
460 fillVertex(vertexList + (i++), fx11 + ldx21 + dx12, fy11 + ldy21 + dy12);
461 fillVertex(vertexList + (i++), fx11 + dx12, fy11 + dy12);
462 fillVertex(vertexList + (i++), fx11, fy11);
463
464 // BOTTOM segment, from right side of LEFT edge
465 // "width" of original pgram, "height" of hor. line size
466 fx11 = ox11 + dx12 + ldx21;
467 fy11 = oy11 + dy12 + ldy21;
468 fillVertex(vertexList + (i++), fx11, fy11);
469 fillVertex(vertexList + (i++), fx11 + dx21, fy11 + dy21);
470 fillVertex(vertexList + (i++), fx11 + dx21 + ldx12, fy11 + dy21 + ldy12);
471
472 fillVertex(vertexList + (i++), fx11 + dx21 + ldx12, fy11 + dy21 + ldy12);
473 fillVertex(vertexList + (i++), fx11 + ldx12, fy11 + ldy12);
474 fillVertex(vertexList + (i++), fx11, fy11);
475
476 // LEFT segment, from bottom of TOP edge
477 // "width" of vert. line size , "height" of inner pgram
478 fx11 = ox11 + ldx12;
479 fy11 = oy11 + ldy12;
480 fillVertex(vertexList + (i++), fx11, fy11);
481 fillVertex(vertexList + (i++), fx11 + ldx21, fy11 + ldy21);
482 fillVertex(vertexList + (i++), fx11 + ldx21 + dx12, fy11 + ldy21 + dy12);
483
484 fillVertex(vertexList + (i++), fx11 + ldx21 + dx12, fy11 + ldy21 + dy12);
485 fillVertex(vertexList + (i++), fx11 + dx12, fy11 + dy12);
486 fillVertex(vertexList + (i++), fx11, fy11);
487
488 // Encode render command.
489 id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dstOps];
490 if (mtlEncoder == nil)
491 return;
492
493 [mtlEncoder setVertexBytes:vertexList length:sizeof(vertexList) atIndex:MeshVertexBuffer];
494 [mtlEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:TOTAL_VERTICES];
495 } else {
496 // The line width ratios were large enough to consume
497 // the entire hole in the middle of the parallelogram
498 // so we can just issue one large quad for the outer
499 // parallelogram.
500 dx21 += ldx21;
501 dy21 += ldy21;
502 dx12 += ldx12;
503 dy12 += ldy12;
504 MTLRenderer_FillParallelogram(mtlc, dstOps, ox11, oy11, dx21, dy21, dx12, dy12);
505 }
506 }
507
508
509 static GLhandleARB aaPgramProgram = 0;
510
511 /*
512 * This shader fills the space between an outer and inner parallelogram.
513 * It can be used to draw an outline by specifying both inner and outer
514 * values. It fills pixels by estimating what portion falls inside the
515 * outer shape, and subtracting an estimate of what portion falls inside
516 * the inner shape. Specifying both inner and outer values produces a
517 * standard "wide outline". Specifying an inner shape that falls far
518 * outside the outer shape allows the same shader to fill the outer
519 * shape entirely since pixels that fall within the outer shape are never
520 * inside the inner shape and so they are filled based solely on their
521 * coverage of the outer shape.
522 *
523 * The setup code renders this shader over the bounds of the outer
524 * shape (or the only shape in the case of a fill operation) and
525 * sets the texture 0 coordinates so that 0,0=>0,1=>1,1=>1,0 in those
526 * texture coordinates map to the four corners of the parallelogram.
527 * Similarly the texture 1 coordinates map the inner shape to the
528 * unit square as well, but in a different coordinate system.
529 *
530 * When viewed in the texture coordinate systems the parallelograms
531 * we are filling are unit squares, but the pixels have then become
532 * tiny parallelograms themselves. Both of the texture coordinate
533 * systems are affine transforms so the rate of change in X and Y
534 * of the texture coordinates are essentially constants and happen
535 * to correspond to the size and direction of the slanted sides of
536 * the distorted pixels relative to the "square mapped" boundary
537 * of the parallelograms.
538 *
539 * The shader uses the dFdx() and dFdy() functions to measure the "rate
540 * of change" of these texture coordinates and thus gets an accurate
541 * measure of the size and shape of a pixel relative to the two
542 * parallelograms. It then uses the bounds of the size and shape
543 * of a pixel to intersect with the unit square to estimate the
544 * coverage of the pixel. Unfortunately, without a lot more work
545 * to calculate the exact area of intersection between a unit
546 * square (the original parallelogram) and a parallelogram (the
547 * distorted pixel), this shader only approximates the pixel
548 * coverage, but emperically the estimate is very useful and
549 * produces visually pleasing results, if not theoretically accurate.
550 */
551 static const char *aaPgramShaderSource =
552 "void main() {"
553 // Calculate the vectors for the "legs" of the pixel parallelogram
554 // for the outer parallelogram.
555 " vec2 oleg1 = dFdx(gl_TexCoord[0].st);"
556 " vec2 oleg2 = dFdy(gl_TexCoord[0].st);"
557 // Calculate the bounds of the distorted pixel parallelogram.
558 " vec2 corner = gl_TexCoord[0].st - (oleg1+oleg2)/2.0;"
559 " vec2 omin = min(corner, corner+oleg1);"
560 " omin = min(omin, corner+oleg2);"
561 " omin = min(omin, corner+oleg1+oleg2);"
562 " vec2 omax = max(corner, corner+oleg1);"
563 " omax = max(omax, corner+oleg2);"
564 " omax = max(omax, corner+oleg1+oleg2);"
565 // Calculate the vectors for the "legs" of the pixel parallelogram
566 // for the inner parallelogram.
567 " vec2 ileg1 = dFdx(gl_TexCoord[1].st);"
568 " vec2 ileg2 = dFdy(gl_TexCoord[1].st);"
569 // Calculate the bounds of the distorted pixel parallelogram.
570 " corner = gl_TexCoord[1].st - (ileg1+ileg2)/2.0;"
571 " vec2 imin = min(corner, corner+ileg1);"
572 " imin = min(imin, corner+ileg2);"
573 " imin = min(imin, corner+ileg1+ileg2);"
574 " vec2 imax = max(corner, corner+ileg1);"
575 " imax = max(imax, corner+ileg2);"
576 " imax = max(imax, corner+ileg1+ileg2);"
577 // Clamp the bounds of the parallelograms to the unit square to
578 // estimate the intersection of the pixel parallelogram with
579 // the unit square. The ratio of the 2 rectangle areas is a
580 // reasonable estimate of the proportion of coverage.
581 " vec2 o1 = clamp(omin, 0.0, 1.0);"
582 " vec2 o2 = clamp(omax, 0.0, 1.0);"
583 " float oint = (o2.y-o1.y)*(o2.x-o1.x);"
584 " float oarea = (omax.y-omin.y)*(omax.x-omin.x);"
585 " vec2 i1 = clamp(imin, 0.0, 1.0);"
586 " vec2 i2 = clamp(imax, 0.0, 1.0);"
587 " float iint = (i2.y-i1.y)*(i2.x-i1.x);"
588 " float iarea = (imax.y-imin.y)*(imax.x-imin.x);"
589 // Proportion of pixel in outer shape minus the proportion
590 // of pixel in the inner shape == the coverage of the pixel
591 // in the area between the two.
592 " float coverage = oint/oarea - iint / iarea;"
593 " gl_FragColor = gl_Color * coverage;"
594 "}";
595
596 #define ADJUST_PGRAM(V1, DV, V2) \
597 do { \
598 if ((DV) >= 0) { \
599 (V2) += (DV); \
600 } else { \
601 (V1) += (DV); \
602 } \
603 } while (0)
604
605 // Invert the following transform:
606 // DeltaT(0, 0) == (0, 0)
607 // DeltaT(1, 0) == (DX1, DY1)
608 // DeltaT(0, 1) == (DX2, DY2)
609 // DeltaT(1, 1) == (DX1+DX2, DY1+DY2)
610 // TM00 = DX1, TM01 = DX2, (TM02 = X11)
611 // TM10 = DY1, TM11 = DY2, (TM12 = Y11)
612 // Determinant = TM00*TM11 - TM01*TM10
613 // = DX1*DY2 - DX2*DY1
614 // Inverse is:
615 // IM00 = TM11/det, IM01 = -TM01/det
616 // IM10 = -TM10/det, IM11 = TM00/det
617 // IM02 = (TM01 * TM12 - TM11 * TM02) / det,
618 // IM12 = (TM10 * TM02 - TM00 * TM12) / det,
619
620 #define DECLARE_MATRIX(MAT) \
621 jfloat MAT ## 00, MAT ## 01, MAT ## 02, MAT ## 10, MAT ## 11, MAT ## 12
622
623 #define GET_INVERTED_MATRIX(MAT, X11, Y11, DX1, DY1, DX2, DY2, RET_CODE) \
624 do { \
625 jfloat det = DX1*DY2 - DX2*DY1; \
626 if (det == 0) { \
627 RET_CODE; \
628 } \
629 MAT ## 00 = DY2/det; \
630 MAT ## 01 = -DX2/det; \
631 MAT ## 10 = -DY1/det; \
632 MAT ## 11 = DX1/det; \
633 MAT ## 02 = (DX2 * Y11 - DY2 * X11) / det; \
634 MAT ## 12 = (DY1 * X11 - DX1 * Y11) / det; \
635 } while (0)
636
637 #define TRANSFORM(MAT, TX, TY, X, Y) \
638 do { \
639 TX = (X) * MAT ## 00 + (Y) * MAT ## 01 + MAT ## 02; \
640 TY = (X) * MAT ## 10 + (Y) * MAT ## 11 + MAT ## 12; \
641 } while (0)
642
643 void
644 MTLRenderer_FillAAParallelogram(MTLContext *mtlc, BMTLSDOps *dstOps,
645 jfloat fx11, jfloat fy11,
646 jfloat dx21, jfloat dy21,
647 jfloat dx12, jfloat dy12)
648 {
649 if (mtlc == NULL || dstOps == NULL || dstOps->pTexture == NULL) {
650 J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_FillParallelogram: current dest is null");
651 return;
652 }
653
654 J2dTraceLn7(J2D_TRACE_INFO,
655 "MTLRenderer_FillAAParallelogram "
656 "(x=%6.2f y=%6.2f "
657 "dx1=%6.2f dy1=%6.2f "
658 "dx2=%6.2f dy2=%6.2f dst tex=%p)",
659 fx11, fy11,
660 dx21, dy21,
661 dx12, dy12, dstOps->pTexture);
662
663 struct Vertex verts[QUAD_VERTEX_COUNT] = {
664 { {fx11, fy11}},
665 { {fx11+dx21, fy11+dy21}},
666 { {fx11+dx12, fy11+dy12}},
667 { {fx11 + dx21 + dx12, fy11+ dy21 + dy12}
668 }};
669
670 id<MTLTexture> dstTxt = dstOps->pTexture;
671
672 // Encode render command.
673 id<MTLRenderCommandEncoder> mtlEncoder =
674 [mtlc.encoderManager getAARenderEncoder:dstOps];
675
676 if (mtlEncoder == nil) {
677 return;
678 }
679
680 [mtlEncoder setVertexBytes:verts length:sizeof(verts) atIndex:MeshVertexBuffer];
681 [mtlEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount: QUAD_VERTEX_COUNT];
682 }
683
684 void
685 MTLRenderer_FillAAParallelogramInnerOuter(MTLContext *mtlc, MTLSDOps *dstOps,
686 jfloat ox11, jfloat oy11,
687 jfloat ox21, jfloat oy21,
688 jfloat ox12, jfloat oy12,
689 jfloat ix11, jfloat iy11,
690 jfloat ix21, jfloat iy21,
691 jfloat ix12, jfloat iy12)
692 {
693 //TODO
694 J2dTraceLn(J2D_TRACE_ERROR, "MTLRenderer_FillAAParallelogramInnerOuter -- :TODO");
695 }
696
697 void
698 MTLRenderer_DrawAAParallelogram(MTLContext *mtlc, BMTLSDOps *dstOps,
699 jfloat fx11, jfloat fy11,
700 jfloat dx21, jfloat dy21,
701 jfloat dx12, jfloat dy12,
702 jfloat lwr21, jfloat lwr12)
703 {
704 //TODO
705 // dx,dy for line width in the "21" and "12" directions.
706 jfloat ldx21, ldy21, ldx12, ldy12;
707 // parameters for "outer" parallelogram
708 jfloat ofx11, ofy11, odx21, ody21, odx12, ody12;
709 // parameters for "inner" parallelogram
710 jfloat ifx11, ify11, idx21, idy21, idx12, idy12;
711
712 J2dTraceLn8(J2D_TRACE_ERROR,
713 "MTLRenderer_DrawAAParallelogram -- :TODO"
714 "(x=%6.2f y=%6.2f "
715 "dx1=%6.2f dy1=%6.2f lwr1=%6.2f "
716 "dx2=%6.2f dy2=%6.2f lwr2=%6.2f)",
717 fx11, fy11,
718 dx21, dy21, lwr21,
719 dx12, dy12, lwr12);
720
721 }
722
723 void
724 MTLRenderer_EnableAAParallelogramProgram()
725 {
726 //TODO
727 J2dTraceLn(J2D_TRACE_INFO, "MTLRenderer_EnableAAParallelogramProgram -- :TODO");
728 }
729
730 void
731 MTLRenderer_DisableAAParallelogramProgram()
732 {
733 //TODO
734 J2dTraceLn(J2D_TRACE_INFO, "MTLRenderer_DisableAAParallelogramProgram -- :TODO");
735 }
736
737 #endif /* !HEADLESS */