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