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