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