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 */