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 
  33 #define DEBUG 0
  34 
  35 #include "sun_java2d_metal_MTLRenderer.h"
  36 
  37 #include "MTLRenderer.h"
  38 #include "MTLRenderQueue.h"
  39 #include "MTLSurfaceData.h"
  40 #import "MTLLayer.h"
  41 
  42 void MTLRenderer_BeginFrame(MTLContext* ctx, MTLLayer* layer) {
  43     if (ctx == NULL) {
  44         return;
  45     }
  46 
  47     if (ctx->mtlCommandBuffer && layer != NULL) {
  48         [layer blitTexture];
  49         return;
  50     }
  51 
  52     // Create a command buffer.
  53     ctx->mtlCommandBuffer = [[ctx->mtlCommandQueue commandBuffer] retain];
  54 }
  55 
  56 void _prepareRenderPassDescriptor(MTLContext* ctx) {
  57     if (ctx == NULL || ctx->mtlRenderPassDesc != NULL)
  58         return;
  59 
  60     ctx->mtlRenderPassDesc = [[MTLRenderPassDescriptor renderPassDescriptor] retain];
  61     if (ctx->mtlRenderPassDesc) {
  62         MTLRenderPassColorAttachmentDescriptor *colorAttachment = ctx->mtlRenderPassDesc.colorAttachments[0];
  63         colorAttachment.texture = ctx->mtlFrameBuffer;
  64 
  65         colorAttachment.loadAction = MTLLoadActionLoad;
  66         colorAttachment.clearColor = MTLClearColorMake(0.0f, 0.0f, 0.0f, 1.0f);
  67 
  68         colorAttachment.storeAction = MTLStoreActionStore;
  69     }
  70 }
  71 
  72 id<MTLRenderCommandEncoder> _prepareEncoder(MTLContext* ctx) {
  73     _prepareRenderPassDescriptor(ctx);
  74     if (ctx == NULL || ctx->mtlRenderPassDesc == NULL)
  75         return nil;
  76 
  77     id <MTLRenderCommandEncoder> mtlEncoder = [ctx->mtlCommandBuffer renderCommandEncoderWithDescriptor:ctx->mtlRenderPassDesc];
  78 
  79     // set viewport and pipeline state
  80     MTLViewport vp = {0, 0, ctx->mtlFrameBuffer.width, ctx->mtlFrameBuffer.height, 0, 1};
  81     [mtlEncoder setViewport:vp];
  82     [mtlEncoder setRenderPipelineState:ctx->mtlPipelineState];
  83 
  84     // set color from ctx
  85     int r = (ctx->mtlColor >> 16) & (0xFF);
  86     int g = (ctx->mtlColor >> 8) & 0xFF;
  87     int b = (ctx->mtlColor) & 0xFF;
  88     int a = (ctx->mtlColor >> 24) & 0xFF;
  89 
  90     vector_float4 color = {r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f};
  91     struct FrameUniforms uf = {color};
  92     [mtlEncoder setVertexBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer];
  93 
  94     return mtlEncoder;
  95 }
  96 
  97 jfloat _screen2mtlX(MTLContext* ctx, jfloat x) { return (2.0*x/ctx->mtlFrameBuffer.width) - 1.0; }
  98 
  99 jfloat _screen2mtlY(MTLContext* ctx, jfloat y) { return 2.0*(1.0 - y/ctx->mtlFrameBuffer.height) - 1.0; }
 100 
 101 void MTLRenderer_FillParallelogramMetal(
 102     MTLContext* ctx, jfloat x, jfloat y, jfloat dx1, jfloat dy1, jfloat dx2, jfloat dy2)
 103 {
 104     if (ctx == NULL) {
 105         return;
 106     }
 107 
 108     ctx->mtlEmptyCommandBuffer = NO;
 109 
 110     struct Vertex verts[PGRAM_VERTEX_COUNT] = {
 111     { {(2.0*x/ctx->mtlFrameBuffer.width) - 1.0,
 112        2.0*(1.0 - y/ctx->mtlFrameBuffer.height) - 1.0, 0.0}},
 113 
 114     { {2.0*(x+dx1)/ctx->mtlFrameBuffer.width - 1.0,
 115       2.0*(1.0 - (y+dy1)/ctx->mtlFrameBuffer.height) - 1.0, 0.0}},
 116 
 117     { {2.0*(x+dx2)/ctx->mtlFrameBuffer.width - 1.0,
 118       2.0*(1.0 - (y+dy2)/ctx->mtlFrameBuffer.height) - 1.0, 0.0}},
 119 
 120     { {2.0*(x+dx1)/ctx->mtlFrameBuffer.width - 1.0,
 121       2.0*(1.0 - (y+dy1)/ctx->mtlFrameBuffer.height) - 1.0, 0.0}},
 122 
 123     { {2.0*(x + dx1 + dx2)/ctx->mtlFrameBuffer.width - 1.0,
 124       2.0*(1.0 - (y+ dy1 + dy2)/ctx->mtlFrameBuffer.height) - 1.0, 0.0}},
 125 
 126     { {2.0*(x+dx2)/ctx->mtlFrameBuffer.width - 1.0,
 127       2.0*(1.0 - (y+dy2)/ctx->mtlFrameBuffer.height) - 1.0, 0.0},
 128     }};
 129 
 130     // Encode render command.
 131     id<MTLRenderCommandEncoder> mtlEncoder = _prepareEncoder(ctx);
 132     if (mtlEncoder == nil)
 133         return;
 134 
 135     [mtlEncoder setVertexBytes:verts length:sizeof(verts) atIndex:MeshVertexBuffer];
 136     [mtlEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount: PGRAM_VERTEX_COUNT];
 137     [mtlEncoder endEncoding];
 138 }
 139 
 140 /**
 141  * Note: Some of the methods in this file apply a "magic number"
 142  * translation to line segments.  The OpenGL specification lays out the
 143  * "diamond exit rule" for line rasterization, but it is loose enough to
 144  * allow for a wide range of line rendering hardware.  (It appears that
 145  * some hardware, such as the Nvidia GeForce2 series, does not even meet
 146  * the spec in all cases.)  As such it is difficult to find a mapping
 147  * between the Java2D and OpenGL line specs that works consistently across
 148  * all hardware combinations.
 149  *
 150  * Therefore the "magic numbers" you see here have been empirically derived
 151  * after testing on a variety of graphics hardware in order to find some
 152  * reasonable middle ground between the two specifications.  The general
 153  * approach is to apply a fractional translation to vertices so that they
 154  * hit pixel centers and therefore touch the same pixels as in our other
 155  * pipelines.  Emphasis was placed on finding values so that MTL lines with
 156  * a slope of +/- 1 hit all the same pixels as our other (software) loops.
 157  * The stepping in other diagonal lines rendered with MTL may deviate
 158  * slightly from those rendered with our software loops, but the most
 159  * important thing is that these magic numbers ensure that all MTL lines
 160  * hit the same endpoints as our software loops.
 161  *
 162  * If you find it necessary to change any of these magic numbers in the
 163  * future, just be sure that you test the changes across a variety of
 164  * hardware to ensure consistent rendering everywhere.
 165  */
 166 
 167 void MTLRenderer_DrawLineMetal(MTLContext *ctx, jfloat x1, jfloat y1, jfloat x2, jfloat y2) {
 168     id<MTLRenderCommandEncoder> mtlEncoder = _prepareEncoder(ctx);
 169     if (mtlEncoder == nil)
 170         return;
 171 
 172     ctx->mtlEmptyCommandBuffer = NO;
 173 
 174     struct Vertex verts[2] = {
 175             {{_screen2mtlX(ctx, x1), _screen2mtlY(ctx, y1), 0.0}},
 176             {{_screen2mtlX(ctx, x2), _screen2mtlY(ctx, y2), 0.0}}
 177     };
 178 
 179     [mtlEncoder setVertexBytes:verts length:sizeof(verts) atIndex:MeshVertexBuffer];
 180     [mtlEncoder drawPrimitives:MTLPrimitiveTypeLine vertexStart:0 vertexCount:2];
 181     [mtlEncoder endEncoding];
 182 }
 183 
 184 void MTLRenderer_DrawLine(MTLContext *mtlc, jint x1, jint y1, jint x2, jint y2) {
 185     J2dTraceLn4(J2D_TRACE_INFO, "MTLRenderer_DrawLine (x1=%d y1=%d x2=%d y2=%d)", x1, y1, x2, y2);
 186 
 187     BMTLSDOps *dstOps = MTLRenderQueue_GetCurrentDestination();
 188 
 189     if (dstOps != NULL) {
 190         MTLSDOps *dstCGLOps = (MTLSDOps *)dstOps->privOps;
 191         [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){
 192             MTLRenderer_DrawLineMetal(dstCGLOps->configInfo->context, x1, y1, x2, y2);
 193         }];
 194     }
 195 }
 196 
 197 void MTLRenderer_DrawRectMetal(MTLContext *ctx, jint x, jint y, jint w, jint h) {
 198     // TODO: use DrawParallelogram(x, y, w, h, lw=1, lh=1)
 199     id<MTLRenderCommandEncoder> mtlEncoder = _prepareEncoder(ctx);
 200     if (mtlEncoder == nil)
 201         return;
 202 
 203     ctx->mtlEmptyCommandBuffer = NO;
 204 
 205     const int verticesCount = 5;
 206     struct Vertex vertices[verticesCount] = {
 207             {{_screen2mtlX(ctx, x), _screen2mtlY(ctx, y), 0.0}},
 208             {{_screen2mtlX(ctx, x + w), _screen2mtlY(ctx, y), 0.0}},
 209             {{_screen2mtlX(ctx, x + w), _screen2mtlY(ctx, y + h), 0.0}},
 210             {{_screen2mtlX(ctx, x), _screen2mtlY(ctx, y + h), 0.0}},
 211             {{_screen2mtlX(ctx, x), _screen2mtlY(ctx, y), 0.0}},
 212     };
 213     [mtlEncoder setVertexBytes:vertices length:sizeof(vertices) atIndex:MeshVertexBuffer];
 214     [mtlEncoder drawPrimitives:MTLPrimitiveTypeLineStrip vertexStart:0 vertexCount:verticesCount];
 215     [mtlEncoder endEncoding];
 216 }
 217 
 218 void MTLRenderer_DrawRect(MTLContext *mtlc, jint x, jint y, jint w, jint h) {
 219     J2dTraceLn4(J2D_TRACE_INFO, "MTLRenderer_DrawRect (x=%d y=%d w=%d h=%d)", x, y, w, h);
 220 
 221     BMTLSDOps *dstOps = MTLRenderQueue_GetCurrentDestination();
 222 
 223     if (dstOps != NULL) {
 224         MTLSDOps *dstCGLOps = (MTLSDOps *)dstOps->privOps;
 225         [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){
 226             MTLRenderer_DrawRectMetal(dstCGLOps->configInfo->context, x, y, w, h);
 227         }];
 228     }
 229 }
 230 
 231 void _tracePoints(jint nPoints, jint *xPoints, jint *yPoints) {
 232     for (int i = 0; i < nPoints; i++)
 233         J2dTraceLn2(J2D_TRACE_INFO, "\t(%d, %d)", *(xPoints++), *(yPoints++));
 234 }
 235 
 236 const int POLYLINE_BUF_SIZE = 64;
 237 
 238 void _fillVertex(MTLContext* ctx, struct Vertex * vertex, int x, int y) {
 239     vertex->position[0] = _screen2mtlX(ctx, x);
 240     vertex->position[1] = _screen2mtlY(ctx, y);
 241     vertex->position[2] = 0;
 242 }
 243 
 244 void MTLRenderer_DrawPoly(MTLContext *mtlc,
 245                      jint nPoints, jint isClosed,
 246                      jint transX, jint transY,
 247                      jint *xPoints, jint *yPoints)
 248 {
 249     J2dTraceLn3(J2D_TRACE_INFO, "MTLRenderer_DrawPoly (%d points, transX=%d, transY=%d)", nPoints, transX, transY);
 250 
 251     // Note that BufferedRenderPipe.drawPoly() has already rejected polys
 252     // with nPoints<2, so we can be certain here that we have nPoints>=2.
 253     if (xPoints == NULL || yPoints == NULL || nPoints < 2) { // just for insurance
 254         J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_DrawPoly: points array is empty");
 255         return;
 256     }
 257 
 258     BMTLSDOps *dstOps = MTLRenderQueue_GetCurrentDestination();
 259     if (dstOps == NULL || dstOps->privOps == NULL)
 260         return;
 261     MTLSDOps *dstCGLOps = (MTLSDOps *) dstOps->privOps;
 262     MTLContext* ctx = dstCGLOps->configInfo;
 263     if (ctx == NULL)
 264         return;
 265 
 266     ctx->mtlEmptyCommandBuffer = NO;
 267 
 268     jint prevX = *(xPoints++);
 269     jint prevY = *(yPoints++);
 270     --nPoints;
 271     const jint firstX = prevX;
 272     const jint firstY = prevY;
 273     while (nPoints > 0) {
 274         __block struct {
 275             struct Vertex verts[POLYLINE_BUF_SIZE];
 276         } pointsChunk;
 277 
 278         _fillVertex(ctx, pointsChunk.verts, prevX + transX, prevY + transY);
 279 
 280         const bool isLastChunk = nPoints + 1 <= POLYLINE_BUF_SIZE;
 281         __block int chunkSize = isLastChunk ? nPoints : POLYLINE_BUF_SIZE - 1;
 282 
 283         for (int i = 1; i < chunkSize; i++) {
 284             prevX = *(xPoints++);
 285             prevY = *(yPoints++);
 286             _fillVertex(ctx, pointsChunk.verts + i, prevX + transX, prevY + transY);
 287         }
 288 
 289         bool drawCloseSegment = false;
 290         if (isClosed && isLastChunk) {
 291             if (chunkSize + 2 <= POLYLINE_BUF_SIZE) {
 292                 _fillVertex(ctx, pointsChunk.verts + chunkSize, firstX + transX, firstY + transY);
 293                 ++chunkSize;
 294             } else
 295                 drawCloseSegment = true;
 296         }
 297 
 298         [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^() {
 299             id<MTLRenderCommandEncoder> mtlEncoder = _prepareEncoder(ctx);
 300             if (mtlEncoder == nil)
 301                 return;
 302 
 303             [mtlEncoder setVertexBytes:pointsChunk.verts length:sizeof(pointsChunk.verts) atIndex:MeshVertexBuffer];
 304             [mtlEncoder drawPrimitives:MTLPrimitiveTypeLineStrip vertexStart:0 vertexCount:chunkSize + 1];
 305             if (drawCloseSegment) {
 306                 struct Vertex vertices[2] = {
 307                         {{_screen2mtlX(ctx, prevX + transX),     _screen2mtlY(ctx, prevY + transY), 0.0}},
 308                         {{_screen2mtlX(ctx, firstX + transX),    _screen2mtlY(ctx, firstY + transY), 0.0}},
 309                 };
 310                 [mtlEncoder setVertexBytes:vertices length:sizeof(vertices) atIndex:MeshVertexBuffer];
 311                 [mtlEncoder drawPrimitives:MTLPrimitiveTypeLine vertexStart:0 vertexCount:2];
 312             }
 313             [mtlEncoder endEncoding];
 314         }];
 315 
 316         nPoints -= chunkSize;
 317     }
 318 }
 319 
 320 JNIEXPORT void JNICALL
 321 Java_sun_java2d_metal_MTLRenderer_drawPoly
 322     (JNIEnv *env, jobject mtlr,
 323      jintArray xpointsArray, jintArray ypointsArray,
 324      jint nPoints, jboolean isClosed,
 325      jint transX, jint transY)
 326 {
 327     jint *xPoints, *yPoints;
 328     //TODO
 329     J2dTraceNotImplPrimitive("MTLRenderer_drawPoly");
 330     J2dTraceLn(J2D_TRACE_INFO, "MTLRenderer_drawPoly");
 331 }
 332 
 333 void
 334 MTLRenderer_DrawScanlines(MTLContext *mtlc,
 335                           jint scanlineCount, jint *scanlines)
 336 {
 337     //TODO
 338     J2dTraceNotImplPrimitive("MTLRenderer_DrawScanlines");
 339     J2dTraceLn(J2D_TRACE_INFO, "MTLRenderer_DrawScanlines");
 340 }
 341 
 342 void
 343 MTLRenderer_FillRect(MTLContext *mtlc, jint x, jint y, jint w, jint h)
 344 {
 345     //TODO
 346     J2dTraceNotImplPrimitive("MTLRenderer_FillRect");
 347     J2dTraceLn(J2D_TRACE_INFO, "MTLRenderer_FillRect");
 348 }
 349 
 350 const int SPAN_BUF_SIZE=64;
 351 
 352 void
 353 MTLRenderer_FillSpans(MTLContext *mtlc, jint spanCount, jint *spans)
 354 {
 355     J2dTraceLn(J2D_TRACE_INFO, "MTLRenderer_FillSpans");
 356     while (spanCount > 0) {
 357         __block struct {
 358             jfloat spns[SPAN_BUF_SIZE*4];
 359         } spanStruct;
 360 
 361         __block jfloat sc = spanCount > SPAN_BUF_SIZE ? SPAN_BUF_SIZE : spanCount;
 362 
 363         for (int i = 0; i < sc; i++) {
 364             spanStruct.spns[i * 4] = *(spans++);
 365             spanStruct.spns[i * 4 + 1] = *(spans++);
 366             spanStruct.spns[i * 4 + 2] = *(spans++);
 367             spanStruct.spns[i * 4 + 3] = *(spans++);
 368         }
 369 
 370         BMTLSDOps *dstOps = MTLRenderQueue_GetCurrentDestination();
 371 
 372         if (dstOps != NULL) {
 373             MTLSDOps *dstCGLOps = (MTLSDOps *) dstOps->privOps;
 374             [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){
 375                 MTLContext* ctx = dstCGLOps->configInfo->context;
 376                     if (ctx == NULL) {
 377                         return;
 378                     }
 379 
 380                     ctx->mtlEmptyCommandBuffer = NO;
 381 
 382                     id<MTLRenderCommandEncoder> mtlEncoder = _prepareEncoder(ctx);
 383                     if (mtlEncoder == nil)
 384                         return;
 385 
 386                     for (int i = 0; i < sc; i++) {
 387                         jfloat x1 = spanStruct.spns[i * 4];
 388                         jfloat y1 = spanStruct.spns[i * 4 + 1];
 389                         jfloat x2 = spanStruct.spns[i * 4 + 2];
 390                         jfloat y2 = spanStruct.spns[i * 4 + 3];
 391 
 392                         struct Vertex verts[PGRAM_VERTEX_COUNT] = {
 393                             {{(2.0 * x1 / ctx->mtlFrameBuffer.width) - 1.0,
 394                             2.0 * (1.0 - y1 / ctx->mtlFrameBuffer.height) - 1.0, 0.0}},
 395 
 396                             {{2.0 * (x2) / ctx->mtlFrameBuffer.width - 1.0,
 397                             2.0 * (1.0 - y1 / ctx->mtlFrameBuffer.height) - 1.0, 0.0}},
 398 
 399                             {{2.0 * x1 / ctx->mtlFrameBuffer.width - 1.0,
 400                             2.0 * (1.0 - y2 / ctx->mtlFrameBuffer.height) - 1.0, 0.0}},
 401 
 402                             {{2.0 * x2 / ctx->mtlFrameBuffer.width - 1.0,
 403                             2.0 * (1.0 - y1 / ctx->mtlFrameBuffer.height) - 1.0, 0.0}},
 404 
 405                             {{2.0 * (x2) / ctx->mtlFrameBuffer.width - 1.0,
 406                             2.0 * (1.0 - y2 / ctx->mtlFrameBuffer.height) - 1.0, 0.0}},
 407 
 408                             {{2.0 * (x1) / ctx->mtlFrameBuffer.width - 1.0,
 409                             2.0 * (1.0 - y2 / ctx->mtlFrameBuffer.height) - 1.0, 0.0},
 410                         }};
 411 
 412                         [mtlEncoder setVertexBytes:verts length:sizeof(verts) atIndex:MeshVertexBuffer];
 413                         [mtlEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:PGRAM_VERTEX_COUNT];
 414                     }
 415                     [mtlEncoder endEncoding];
 416                 }];
 417         }
 418         spanCount -= sc;
 419     }
 420 }
 421 
 422 void
 423 MTLRenderer_FillParallelogram(MTLContext *mtlc,
 424                               jfloat fx11, jfloat fy11,
 425                               jfloat dx21, jfloat dy21,
 426                               jfloat dx12, jfloat dy12)
 427 {
 428     J2dTracePrimitive("MTLRenderer_FillParallelogram");
 429     J2dTraceLn6(J2D_TRACE_INFO,
 430                 "MTLRenderer_FillParallelogram "
 431                 "(x=%6.2f y=%6.2f "
 432                 "dx1=%6.2f dy1=%6.2f "
 433                 "dx2=%6.2f dy2=%6.2f)",
 434                 fx11, fy11,
 435                 dx21, dy21,
 436                 dx12, dy12);
 437    // MTLCtxInfo *ctxinfo = (MTLCtxInfo *)mtlc->ctxInfo;
 438     BMTLSDOps *dstOps = MTLRenderQueue_GetCurrentDestination();
 439 
 440 
 441     if (dstOps != NULL) {
 442         MTLSDOps *dstCGLOps = (MTLSDOps *)dstOps->privOps;
 443         [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){
 444          MTLRenderer_FillParallelogramMetal(
 445             dstCGLOps->configInfo->context,
 446             fx11, fy11, dx21, dy21, dx12, dy12);
 447          }];
 448     }
 449 }
 450 
 451 void
 452 MTLRenderer_DrawParallelogram(MTLContext *mtlc,
 453                               jfloat fx11, jfloat fy11,
 454                               jfloat dx21, jfloat dy21,
 455                               jfloat dx12, jfloat dy12,
 456                               jfloat lwr21, jfloat lwr12)
 457 {
 458     //TODO
 459     J2dTraceNotImplPrimitive("MTLRenderer_DrawParallelogram");
 460     // dx,dy for line width in the "21" and "12" directions.
 461     jfloat ldx21 = dx21 * lwr21;
 462     jfloat ldy21 = dy21 * lwr21;
 463     jfloat ldx12 = dx12 * lwr12;
 464     jfloat ldy12 = dy12 * lwr12;
 465 
 466     // calculate origin of the outer parallelogram
 467     jfloat ox11 = fx11 - (ldx21 + ldx12) / 2.0f;
 468     jfloat oy11 = fy11 - (ldy21 + ldy12) / 2.0f;
 469 
 470     J2dTraceLn8(J2D_TRACE_INFO,
 471                 "MTLRenderer_DrawParallelogram "
 472                 "(x=%6.2f y=%6.2f "
 473                 "dx1=%6.2f dy1=%6.2f lwr1=%6.2f "
 474                 "dx2=%6.2f dy2=%6.2f lwr2=%6.2f)",
 475                 fx11, fy11,
 476                 dx21, dy21, lwr21,
 477                 dx12, dy12, lwr12);
 478 
 479 }
 480 
 481 static GLhandleARB aaPgramProgram = 0;
 482 
 483 /*
 484  * This shader fills the space between an outer and inner parallelogram.
 485  * It can be used to draw an outline by specifying both inner and outer
 486  * values.  It fills pixels by estimating what portion falls inside the
 487  * outer shape, and subtracting an estimate of what portion falls inside
 488  * the inner shape.  Specifying both inner and outer values produces a
 489  * standard "wide outline".  Specifying an inner shape that falls far
 490  * outside the outer shape allows the same shader to fill the outer
 491  * shape entirely since pixels that fall within the outer shape are never
 492  * inside the inner shape and so they are filled based solely on their
 493  * coverage of the outer shape.
 494  *
 495  * The setup code renders this shader over the bounds of the outer
 496  * shape (or the only shape in the case of a fill operation) and
 497  * sets the texture 0 coordinates so that 0,0=>0,1=>1,1=>1,0 in those
 498  * texture coordinates map to the four corners of the parallelogram.
 499  * Similarly the texture 1 coordinates map the inner shape to the
 500  * unit square as well, but in a different coordinate system.
 501  *
 502  * When viewed in the texture coordinate systems the parallelograms
 503  * we are filling are unit squares, but the pixels have then become
 504  * tiny parallelograms themselves.  Both of the texture coordinate
 505  * systems are affine transforms so the rate of change in X and Y
 506  * of the texture coordinates are essentially constants and happen
 507  * to correspond to the size and direction of the slanted sides of
 508  * the distorted pixels relative to the "square mapped" boundary
 509  * of the parallelograms.
 510  *
 511  * The shader uses the dFdx() and dFdy() functions to measure the "rate
 512  * of change" of these texture coordinates and thus gets an accurate
 513  * measure of the size and shape of a pixel relative to the two
 514  * parallelograms.  It then uses the bounds of the size and shape
 515  * of a pixel to intersect with the unit square to estimate the
 516  * coverage of the pixel.  Unfortunately, without a lot more work
 517  * to calculate the exact area of intersection between a unit
 518  * square (the original parallelogram) and a parallelogram (the
 519  * distorted pixel), this shader only approximates the pixel
 520  * coverage, but emperically the estimate is very useful and
 521  * produces visually pleasing results, if not theoretically accurate.
 522  */
 523 static const char *aaPgramShaderSource =
 524     "void main() {"
 525     // Calculate the vectors for the "legs" of the pixel parallelogram
 526     // for the outer parallelogram.
 527     "    vec2 oleg1 = dFdx(gl_TexCoord[0].st);"
 528     "    vec2 oleg2 = dFdy(gl_TexCoord[0].st);"
 529     // Calculate the bounds of the distorted pixel parallelogram.
 530     "    vec2 corner = gl_TexCoord[0].st - (oleg1+oleg2)/2.0;"
 531     "    vec2 omin = min(corner, corner+oleg1);"
 532     "    omin = min(omin, corner+oleg2);"
 533     "    omin = min(omin, corner+oleg1+oleg2);"
 534     "    vec2 omax = max(corner, corner+oleg1);"
 535     "    omax = max(omax, corner+oleg2);"
 536     "    omax = max(omax, corner+oleg1+oleg2);"
 537     // Calculate the vectors for the "legs" of the pixel parallelogram
 538     // for the inner parallelogram.
 539     "    vec2 ileg1 = dFdx(gl_TexCoord[1].st);"
 540     "    vec2 ileg2 = dFdy(gl_TexCoord[1].st);"
 541     // Calculate the bounds of the distorted pixel parallelogram.
 542     "    corner = gl_TexCoord[1].st - (ileg1+ileg2)/2.0;"
 543     "    vec2 imin = min(corner, corner+ileg1);"
 544     "    imin = min(imin, corner+ileg2);"
 545     "    imin = min(imin, corner+ileg1+ileg2);"
 546     "    vec2 imax = max(corner, corner+ileg1);"
 547     "    imax = max(imax, corner+ileg2);"
 548     "    imax = max(imax, corner+ileg1+ileg2);"
 549     // Clamp the bounds of the parallelograms to the unit square to
 550     // estimate the intersection of the pixel parallelogram with
 551     // the unit square.  The ratio of the 2 rectangle areas is a
 552     // reasonable estimate of the proportion of coverage.
 553     "    vec2 o1 = clamp(omin, 0.0, 1.0);"
 554     "    vec2 o2 = clamp(omax, 0.0, 1.0);"
 555     "    float oint = (o2.y-o1.y)*(o2.x-o1.x);"
 556     "    float oarea = (omax.y-omin.y)*(omax.x-omin.x);"
 557     "    vec2 i1 = clamp(imin, 0.0, 1.0);"
 558     "    vec2 i2 = clamp(imax, 0.0, 1.0);"
 559     "    float iint = (i2.y-i1.y)*(i2.x-i1.x);"
 560     "    float iarea = (imax.y-imin.y)*(imax.x-imin.x);"
 561     // Proportion of pixel in outer shape minus the proportion
 562     // of pixel in the inner shape == the coverage of the pixel
 563     // in the area between the two.
 564     "    float coverage = oint/oarea - iint / iarea;"
 565     "    gl_FragColor = gl_Color * coverage;"
 566     "}";
 567 
 568 #define ADJUST_PGRAM(V1, DV, V2) \
 569     do { \
 570         if ((DV) >= 0) { \
 571             (V2) += (DV); \
 572         } else { \
 573             (V1) += (DV); \
 574         } \
 575     } while (0)
 576 
 577 // Invert the following transform:
 578 // DeltaT(0, 0) == (0,       0)
 579 // DeltaT(1, 0) == (DX1,     DY1)
 580 // DeltaT(0, 1) == (DX2,     DY2)
 581 // DeltaT(1, 1) == (DX1+DX2, DY1+DY2)
 582 // TM00 = DX1,   TM01 = DX2,   (TM02 = X11)
 583 // TM10 = DY1,   TM11 = DY2,   (TM12 = Y11)
 584 // Determinant = TM00*TM11 - TM01*TM10
 585 //             =  DX1*DY2  -  DX2*DY1
 586 // Inverse is:
 587 // IM00 =  TM11/det,   IM01 = -TM01/det
 588 // IM10 = -TM10/det,   IM11 =  TM00/det
 589 // IM02 = (TM01 * TM12 - TM11 * TM02) / det,
 590 // IM12 = (TM10 * TM02 - TM00 * TM12) / det,
 591 
 592 #define DECLARE_MATRIX(MAT) \
 593     jfloat MAT ## 00, MAT ## 01, MAT ## 02, MAT ## 10, MAT ## 11, MAT ## 12
 594 
 595 #define GET_INVERTED_MATRIX(MAT, X11, Y11, DX1, DY1, DX2, DY2, RET_CODE) \
 596     do { \
 597         jfloat det = DX1*DY2 - DX2*DY1; \
 598         if (det == 0) { \
 599             RET_CODE; \
 600         } \
 601         MAT ## 00 = DY2/det; \
 602         MAT ## 01 = -DX2/det; \
 603         MAT ## 10 = -DY1/det; \
 604         MAT ## 11 = DX1/det; \
 605         MAT ## 02 = (DX2 * Y11 - DY2 * X11) / det; \
 606         MAT ## 12 = (DY1 * X11 - DX1 * Y11) / det; \
 607     } while (0)
 608 
 609 #define TRANSFORM(MAT, TX, TY, X, Y) \
 610     do { \
 611         TX = (X) * MAT ## 00 + (Y) * MAT ## 01 + MAT ## 02; \
 612         TY = (X) * MAT ## 10 + (Y) * MAT ## 11 + MAT ## 12; \
 613     } while (0)
 614 
 615 void
 616 MTLRenderer_FillAAParallelogram(MTLContext *mtlc, BMTLSDOps *dstOps,
 617                                 jfloat fx11, jfloat fy11,
 618                                 jfloat dx21, jfloat dy21,
 619                                 jfloat dx12, jfloat dy12)
 620 {
 621     //TODO
 622     J2dTraceNotImplPrimitive("MTLRenderer_FillAAParallelogram");
 623     DECLARE_MATRIX(om);
 624     // parameters for parallelogram bounding box
 625     jfloat bx11, by11, bx22, by22;
 626     // parameters for uv texture coordinates of parallelogram corners
 627     jfloat u11, v11, u12, v12, u21, v21, u22, v22;
 628 
 629     J2dTraceLn6(J2D_TRACE_INFO,
 630                 "MTLRenderer_FillAAParallelogram "
 631                 "(x=%6.2f y=%6.2f "
 632                 "dx1=%6.2f dy1=%6.2f "
 633                 "dx2=%6.2f dy2=%6.2f)",
 634                 fx11, fy11,
 635                 dx21, dy21,
 636                 dx12, dy12);
 637 
 638 }
 639 
 640 void
 641 MTLRenderer_FillAAParallelogramInnerOuter(MTLContext *mtlc, MTLSDOps *dstOps,
 642                                           jfloat ox11, jfloat oy11,
 643                                           jfloat ox21, jfloat oy21,
 644                                           jfloat ox12, jfloat oy12,
 645                                           jfloat ix11, jfloat iy11,
 646                                           jfloat ix21, jfloat iy21,
 647                                           jfloat ix12, jfloat iy12)
 648 {
 649     //TODO
 650     J2dTraceNotImplPrimitive("MTLRenderer_FillAAParallelogramInnerOuter");
 651 }
 652 
 653 void
 654 MTLRenderer_DrawAAParallelogram(MTLContext *mtlc, BMTLSDOps *dstOps,
 655                                 jfloat fx11, jfloat fy11,
 656                                 jfloat dx21, jfloat dy21,
 657                                 jfloat dx12, jfloat dy12,
 658                                 jfloat lwr21, jfloat lwr12)
 659 {
 660     //TODO
 661     J2dTraceNotImplPrimitive("MTLRenderer_DrawAAParallelogram");
 662     // dx,dy for line width in the "21" and "12" directions.
 663     jfloat ldx21, ldy21, ldx12, ldy12;
 664     // parameters for "outer" parallelogram
 665     jfloat ofx11, ofy11, odx21, ody21, odx12, ody12;
 666     // parameters for "inner" parallelogram
 667     jfloat ifx11, ify11, idx21, idy21, idx12, idy12;
 668 
 669     J2dTraceLn8(J2D_TRACE_INFO,
 670                 "MTLRenderer_DrawAAParallelogram "
 671                 "(x=%6.2f y=%6.2f "
 672                 "dx1=%6.2f dy1=%6.2f lwr1=%6.2f "
 673                 "dx2=%6.2f dy2=%6.2f lwr2=%6.2f)",
 674                 fx11, fy11,
 675                 dx21, dy21, lwr21,
 676                 dx12, dy12, lwr12);
 677 
 678 }
 679 
 680 void
 681 MTLRenderer_EnableAAParallelogramProgram()
 682 {
 683     //TODO
 684     J2dTraceNotImplPrimitive("MTLRenderer_EnableAAParallelogramProgram");
 685     J2dTraceLn(J2D_TRACE_INFO, "MTLRenderer_EnableAAParallelogramProgram");
 686 }
 687 
 688 void
 689 MTLRenderer_DisableAAParallelogramProgram()
 690 {
 691     //TODO
 692     J2dTraceNotImplPrimitive("MTLRenderer_DisableAAParallelogramProgram");
 693     J2dTraceLn(J2D_TRACE_INFO, "MTLRenderer_DisableAAParallelogramProgram");
 694 }
 695 
 696 #endif /* !HEADLESS */