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