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         verts[0].position[0] = fx1 + 0.2f;
 92         verts[0].position[1] = fy;
 93         verts[1].position[0] = fx2 + 1.2f;
 94         verts[1].position[1] = fy;
 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         verts[0].position[0] = fx;
106         verts[0].position[1] = fy1 + 0.2f;
107         verts[1].position[0] = fx;
108         verts[1].position[1] = fy2 + 1.2f;
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         verts[0].position[0] = fx1;
132         verts[0].position[1] = fy1;
133         verts[1].position[0] = fx2;
134         verts[1].position[1] = fy2;
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 */