Path: blob/master/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLRenderer.m
41159 views
/*1* Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#include <jlong.h>26#include <jni_util.h>27#include <math.h>2829#include "sun_java2d_metal_MTLRenderer.h"3031#include "MTLRenderer.h"32#include "MTLRenderQueue.h"33#include "MTLSurfaceData.h"34#include "MTLUtils.h"35#import "MTLLayer.h"3637/**38* Note: Some of the methods in this file apply a "magic number"39* translation to line segments. It is same as what we have in40* OGLrenderer.41*42* The "magic numbers" you see here have been empirically derived43* after testing on a variety of graphics hardware in order to find some44* reasonable middle ground between the two specifications. The general45* approach is to apply a fractional translation to vertices so that they46* hit pixel centers and therefore touch the same pixels as in our other47* pipelines. Emphasis was placed on finding values so that MTL lines with48* a slope of +/- 1 hit all the same pixels as our other (software) loops.49* The stepping in other diagonal lines rendered with MTL may deviate50* slightly from those rendered with our software loops, but the most51* important thing is that these magic numbers ensure that all MTL lines52* hit the same endpoints as our software loops.53*54* If you find it necessary to change any of these magic numbers in the55* future, just be sure that you test the changes across a variety of56* hardware to ensure consistent rendering everywhere.57*/5859void MTLRenderer_DrawLine(MTLContext *mtlc, BMTLSDOps * dstOps, jint x1, jint y1, jint x2, jint y2) {60if (mtlc == NULL || dstOps == NULL || dstOps->pTexture == NULL) {61J2dTraceLn(J2D_TRACE_ERROR, "MTLRenderer_DrawLine: dest is null");62return;63}6465J2dTraceLn5(J2D_TRACE_INFO, "MTLRenderer_DrawLine (x1=%d y1=%d x2=%d y2=%d), dst tex=%p", x1, y1, x2, y2, dstOps->pTexture);6667id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dstOps];68if (mtlEncoder == nil)69return;7071// DrawLine implementation same as in OGLRenderer.c72struct Vertex verts[2];73if (y1 == y2) {74// horizontal75float fx1 = (float)x1;76float fx2 = (float)x2;77float fy = ((float)y1) + 0.2f;7879if (x1 > x2) {80float t = fx1; fx1 = fx2; fx2 = t;81}8283verts[0].position[0] = fx1 + 0.2f;84verts[0].position[1] = fy;85verts[1].position[0] = fx2 + 1.2f;86verts[1].position[1] = fy;87} else if (x1 == x2) {88// vertical89float fx = ((float)x1) + 0.2f;90float fy1 = (float)y1;91float fy2 = (float)y2;9293if (y1 > y2) {94float t = fy1; fy1 = fy2; fy2 = t;95}9697verts[0].position[0] = fx;98verts[0].position[1] = fy1 + 0.2f;99verts[1].position[0] = fx;100verts[1].position[1] = fy2 + 1.2f;101} else {102// diagonal103float fx1 = (float)x1;104float fy1 = (float)y1;105float fx2 = (float)x2;106float fy2 = (float)y2;107108if (x1 < x2) {109fx1 += 0.2f;110fx2 += 1.0f;111} else {112fx1 += 0.8f;113fx2 -= 0.2f;114}115116if (y1 < y2) {117fy1 += 0.2f;118fy2 += 1.0f;119} else {120fy1 += 0.8f;121fy2 -= 0.2f;122}123verts[0].position[0] = fx1;124verts[0].position[1] = fy1;125verts[1].position[0] = fx2;126verts[1].position[1] = fy2;127}128129[mtlEncoder setVertexBytes:verts length:sizeof(verts) atIndex:MeshVertexBuffer];130[mtlEncoder drawPrimitives:MTLPrimitiveTypeLine vertexStart:0 vertexCount:2];131}132133void MTLRenderer_DrawPixel(MTLContext *mtlc, BMTLSDOps * dstOps, jint x, jint y) {134if (mtlc == NULL || dstOps == NULL || dstOps->pTexture == NULL) {135J2dTraceLn(J2D_TRACE_ERROR, "MTLRenderer_DrawPixel: dest is null");136return;137}138139id<MTLTexture> dest = dstOps->pTexture;140J2dTraceLn3(J2D_TRACE_INFO, "MTLRenderer_DrawPixel (x=%d y=%d), dst tex=%p", x, y, dest);141142id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dstOps];143if (mtlEncoder == nil)144return;145146// Translate each vertex by a fraction so147// that we hit pixel centers.148float fx = (float)x + 0.2f;149float fy = (float)y + 0.5f;150struct Vertex vert = {{fx, fy}};151[mtlEncoder setVertexBytes:&vert length:sizeof(vert) atIndex:MeshVertexBuffer];152[mtlEncoder drawPrimitives:MTLPrimitiveTypePoint vertexStart:0 vertexCount:1];153}154155void MTLRenderer_DrawRect(MTLContext *mtlc, BMTLSDOps * dstOps, jint x, jint y, jint w, jint h) {156if (mtlc == NULL || dstOps == NULL || dstOps->pTexture == NULL) {157J2dTraceLn(J2D_TRACE_ERROR, "MTLRenderer_DrawRect: dest is null");158return;159}160161id<MTLTexture> dest = dstOps->pTexture;162J2dTraceLn5(J2D_TRACE_INFO, "MTLRenderer_DrawRect (x=%d y=%d w=%d h=%d), dst tex=%p", x, y, w, h, dest);163164// TODO: use DrawParallelogram(x, y, w, h, lw=1, lh=1)165id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dstOps];166if (mtlEncoder == nil)167return;168169// Translate each vertex by a fraction so170// that we hit pixel centers.171const int verticesCount = 5;172float fx = (float)x + 0.2f;173float fy = (float)y + 0.5f;174float fw = (float)w;175float fh = (float)h;176struct Vertex vertices[5] = {177{{fx, fy}},178{{fx + fw, fy}},179{{fx + fw, fy + fh}},180{{fx, fy + fh}},181{{fx, fy}},182};183[mtlEncoder setVertexBytes:vertices length:sizeof(vertices) atIndex:MeshVertexBuffer];184[mtlEncoder drawPrimitives:MTLPrimitiveTypeLineStrip vertexStart:0 vertexCount:verticesCount];185}186187const int POLYLINE_BUF_SIZE = 64;188189NS_INLINE void fillVertex(struct Vertex * vertex, int x, int y) {190vertex->position[0] = x;191vertex->position[1] = y;192}193194void MTLRenderer_DrawPoly(MTLContext *mtlc, BMTLSDOps * dstOps,195jint nPoints, jint isClosed,196jint transX, jint transY,197jint *xPoints, jint *yPoints)198{199// Note that BufferedRenderPipe.drawPoly() has already rejected polys200// with nPoints<2, so we can be certain here that we have nPoints>=2.201if (xPoints == NULL || yPoints == NULL || nPoints < 2) { // just for insurance202J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_DrawPoly: points array is empty");203return;204}205206if (mtlc == NULL || dstOps == NULL || dstOps->pTexture == NULL) {207J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_DrawPoly: dest is null");208return;209}210211J2dTraceLn4(J2D_TRACE_INFO, "MTLRenderer_DrawPoly: %d points, transX=%d, transY=%d, dst tex=%p", nPoints, transX, transY, dstOps->pTexture);212213__block struct {214struct Vertex verts[POLYLINE_BUF_SIZE];215} pointsChunk;216217// We intend to submit draw commands in batches of POLYLINE_BUF_SIZE vertices at a time218// Subsequent batches need to be connected - so end point in one batch is repeated as first point in subsequent batch219// This inflates the total number of points by a factor of number of batches of size POLYLINE_BUF_SIZE220nPoints += (nPoints/POLYLINE_BUF_SIZE);221222jint prevX = *(xPoints++);223jint prevY = *(yPoints++);224const jint firstX = prevX;225const jint firstY = prevY;226while (nPoints > 0) {227const bool isLastChunk = nPoints <= POLYLINE_BUF_SIZE;228__block int chunkSize = isLastChunk ? nPoints : POLYLINE_BUF_SIZE;229230fillVertex(pointsChunk.verts, prevX + transX + 0.5f, prevY + transY + 0.5f);231J2dTraceLn2(J2D_TRACE_INFO, "MTLRenderer_DrawPoly: Point - (%1.2f, %1.2f)", prevX + transX + 0.5f, prevY + transY + 0.5f);232233for (int i = 1; i < chunkSize; i++) {234prevX = *(xPoints++);235prevY = *(yPoints++);236fillVertex(pointsChunk.verts + i, prevX + transX + 0.5f, prevY + transY + 0.5f);237J2dTraceLn2(J2D_TRACE_INFO, "MTLRenderer_DrawPoly: Point - (%1.2f, %1.2f)", prevX + transX + 0.5f,prevY + transY + 0.5f);238}239240bool drawCloseSegment = false;241if (isClosed && isLastChunk) {242if (chunkSize + 2 <= POLYLINE_BUF_SIZE) {243fillVertex(pointsChunk.verts + chunkSize, firstX + transX + 0.5f, firstY + transY + 0.5f);244J2dTraceLn2(J2D_TRACE_INFO, "MTLRenderer_DrawPoly: Point - (%1.2f, %1.2f)",firstX + transX + 0.5f, firstY + transY + 0.5f);245246++chunkSize;247} else248drawCloseSegment = true;249}250251nPoints -= chunkSize;252id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dstOps];253if (mtlEncoder == nil)254return;255256[mtlEncoder setVertexBytes:pointsChunk.verts length:sizeof(pointsChunk.verts) atIndex:MeshVertexBuffer];257[mtlEncoder drawPrimitives:MTLPrimitiveTypeLineStrip vertexStart:0 vertexCount:chunkSize];258259if (drawCloseSegment) {260struct Vertex vertices[2] = {261{{prevX + transX + 0.5f, prevY + transY + 0.5f}},262{{firstX + transX + 0.5f, firstY + transY + 0.5f}}263};264265J2dTraceLn2(J2D_TRACE_INFO, "MTLRenderer_DrawPoly: last segment Point1 - (%1.2f, %1.2f)",prevX + transX + 0.5f, prevY + transY + 0.5f);266J2dTraceLn2(J2D_TRACE_INFO, "MTLRenderer_DrawPoly: last segment Point2 - (%1.2f, %1.2f)",firstX + transX + 0.5f, firstY + transY + 0.5f);267268[mtlEncoder setVertexBytes:vertices length:sizeof(vertices) atIndex:MeshVertexBuffer];269[mtlEncoder drawPrimitives:MTLPrimitiveTypeLine vertexStart:0 vertexCount:2];270}271}272}273274JNIEXPORT void JNICALL275Java_sun_java2d_metal_MTLRenderer_drawPoly276(JNIEnv *env, jobject mtlr,277jintArray xpointsArray, jintArray ypointsArray,278jint nPoints, jboolean isClosed,279jint transX, jint transY)280{281jint *xPoints, *yPoints;282283J2dTraceLn(J2D_TRACE_INFO, "MTLRenderer_drawPoly");284285xPoints = (jint *)286(*env)->GetPrimitiveArrayCritical(env, xpointsArray, NULL);287if (xPoints != NULL) {288yPoints = (jint *)289(*env)->GetPrimitiveArrayCritical(env, ypointsArray, NULL);290if (yPoints != NULL) {291MTLContext *mtlc = MTLRenderQueue_GetCurrentContext();292BMTLSDOps *dstOps = MTLRenderQueue_GetCurrentDestination();293294MTLRenderer_DrawPoly(mtlc, dstOps,295nPoints, isClosed,296transX, transY,297xPoints, yPoints);298if (mtlc != NULL) {299RESET_PREVIOUS_OP();300[mtlc.encoderManager endEncoder];301MTLCommandBufferWrapper * cbwrapper = [mtlc pullCommandBufferWrapper];302id<MTLCommandBuffer> commandbuf = [cbwrapper getCommandBuffer];303[commandbuf addCompletedHandler:^(id <MTLCommandBuffer> commandbuf) {304[cbwrapper release];305}];306[commandbuf commit];307}308309(*env)->ReleasePrimitiveArrayCritical(env, ypointsArray, yPoints,310JNI_ABORT);311}312(*env)->ReleasePrimitiveArrayCritical(env, xpointsArray, xPoints,313JNI_ABORT);314}315}316317const int SCANLINE_MAX_VERTEX_SIZE = 4096;318const int VERTEX_STRUCT_SIZE = 8;319const int NUM_OF_VERTICES_PER_SCANLINE = 2;320321void322MTLRenderer_DrawScanlines(MTLContext *mtlc, BMTLSDOps * dstOps,323jint scanlineCount, jint *scanlines)324{325326J2dTraceLn2(J2D_TRACE_INFO, "MTLRenderer_DrawScanlines (scanlineCount=%d), dst tex=%p", scanlineCount, dstOps->pTexture);327if (mtlc == NULL || dstOps == NULL || dstOps->pTexture == NULL) {328J2dTraceLn(J2D_TRACE_ERROR, "MTLRenderer_DrawScanlines: dest is null");329return;330}331RETURN_IF_NULL(scanlines);332int vertexSize = NUM_OF_VERTICES_PER_SCANLINE333* scanlineCount * VERTEX_STRUCT_SIZE;334J2dTraceLn1(J2D_TRACE_INFO, "MTLRenderer_DrawScanlines: Total vertex size : %d", vertexSize);335if (vertexSize == 0) return;336337id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dstOps];338339if (mtlEncoder == nil) return;340341if (vertexSize <= SCANLINE_MAX_VERTEX_SIZE) {342struct Vertex verts[NUM_OF_VERTICES_PER_SCANLINE * scanlineCount];343344for (int j = 0, i = 0; j < scanlineCount; j++) {345// Translate each vertex by a fraction so346// that we hit pixel centers.347float x1 = ((float)*(scanlines++)) + 0.2f;348float x2 = ((float)*(scanlines++)) + 1.2f;349float y = ((float)*(scanlines++)) + 0.5f;350struct Vertex v1 = {{x1, y}};351struct Vertex v2 = {{x2, y}};352verts[i++] = v1;353verts[i++] = v2;354}355356[mtlEncoder setVertexBytes:verts length:sizeof(verts) atIndex:MeshVertexBuffer];357[mtlEncoder drawPrimitives:MTLPrimitiveTypeLine vertexStart:0358vertexCount:NUM_OF_VERTICES_PER_SCANLINE * scanlineCount];359} else {360int remainingScanlineCount = vertexSize;361do {362if (remainingScanlineCount > SCANLINE_MAX_VERTEX_SIZE) {363struct Vertex verts[SCANLINE_MAX_VERTEX_SIZE/ VERTEX_STRUCT_SIZE];364365for (int j = 0, i = 0; j < (SCANLINE_MAX_VERTEX_SIZE / (VERTEX_STRUCT_SIZE * 2)); j++) {366// Translate each vertex by a fraction so367// that we hit pixel centers.368float x1 = ((float)*(scanlines++)) + 0.2f;369float x2 = ((float)*(scanlines++)) + 1.2f;370float y = ((float)*(scanlines++)) + 0.5f;371struct Vertex v1 = {{x1, y}};372struct Vertex v2 = {{x2, y}};373verts[i++] = v1;374verts[i++] = v2;375}376377[mtlEncoder setVertexBytes:verts length:sizeof(verts) atIndex:MeshVertexBuffer];378[mtlEncoder drawPrimitives:MTLPrimitiveTypeLine vertexStart:0379vertexCount:(SCANLINE_MAX_VERTEX_SIZE / VERTEX_STRUCT_SIZE)];380remainingScanlineCount -= SCANLINE_MAX_VERTEX_SIZE;381} else {382struct Vertex verts[remainingScanlineCount / VERTEX_STRUCT_SIZE];383384for (int j = 0, i = 0; j < (remainingScanlineCount / (VERTEX_STRUCT_SIZE * 2)); j++) {385// Translate each vertex by a fraction so386// that we hit pixel centers.387float x1 = ((float)*(scanlines++)) + 0.2f;388float x2 = ((float)*(scanlines++)) + 1.2f;389float y = ((float)*(scanlines++)) + 0.5f;390struct Vertex v1 = {{x1, y}};391struct Vertex v2 = {{x2, y}};392verts[i++] = v1;393verts[i++] = v2;394}395396[mtlEncoder setVertexBytes:verts length:sizeof(verts) atIndex:MeshVertexBuffer];397[mtlEncoder drawPrimitives:MTLPrimitiveTypeLine vertexStart:0398vertexCount:(remainingScanlineCount / VERTEX_STRUCT_SIZE)];399remainingScanlineCount -= remainingScanlineCount;400}401J2dTraceLn1(J2D_TRACE_INFO,402"MTLRenderer_DrawScanlines: Remaining vertex size %d", remainingScanlineCount);403} while (remainingScanlineCount != 0);404}405}406407void408MTLRenderer_FillRect(MTLContext *mtlc, BMTLSDOps * dstOps, jint x, jint y, jint w, jint h)409{410J2dTraceLn(J2D_TRACE_INFO, "MTLRenderer_FillRect");411412if (mtlc == NULL || dstOps == NULL || dstOps->pTexture == NULL) {413J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_FillRect: current dest is null");414return;415}416417struct Vertex verts[QUAD_VERTEX_COUNT] = {418{ {x, y}},419{ {x, y+h}},420{ {x+w, y}},421{ {x+w, y+h}422}};423424425id<MTLTexture> dest = dstOps->pTexture;426J2dTraceLn5(J2D_TRACE_INFO, "MTLRenderer_FillRect (x=%d y=%d w=%d h=%d), dst tex=%p", x, y, w, h, dest);427428// Encode render command.429id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dstOps];430if (mtlEncoder == nil)431return;432433[mtlEncoder setVertexBytes:verts length:sizeof(verts) atIndex:MeshVertexBuffer];434[mtlEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount: QUAD_VERTEX_COUNT];435}436437void MTLRenderer_FillSpans(MTLContext *mtlc, BMTLSDOps * dstOps, jint spanCount, jint *spans)438{439J2dTraceLn(J2D_TRACE_INFO, "MTLRenderer_FillSpans");440if (mtlc == NULL || dstOps == NULL || dstOps->pTexture == NULL) {441J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_FillSpans: dest is null");442return;443}444445// MTLRenderCommandEncoder setVertexBytes usage is recommended if the data is of 4KB.446447// We use a buffer that closely matches the 4KB limit size448// This buffer is resued multiple times to encode draw calls of a triangle list449// NOTE : Due to nature of *spans data - it is not possible to use triangle strip.450// We use triangle list to draw spans451452// Destination texture to which render commands are encoded453id<MTLTexture> dest = dstOps->pTexture;454id<MTLTexture> destAA = nil;455BOOL isDestOpaque = dstOps->isOpaque;456if (mtlc.clip.stencilMaskGenerationInProgress == JNI_TRUE) {457dest = dstOps->pStencilData;458isDestOpaque = NO;459}460id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dest isDstOpaque:isDestOpaque];461if (mtlEncoder == nil) {462J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_FillSpans: mtlEncoder is nil");463return;464}465466// This is the max no of vertices (of struct Vertex - 8 bytes) we can accomodate in 4KB467const int TOTAL_VERTICES_IN_BLOCK = 510;468struct Vertex vertexList[TOTAL_VERTICES_IN_BLOCK]; // a total of 170 triangles ==> 85 spans469470jfloat shapeX1 = mtlc.clip.shapeX;471jfloat shapeY1 = mtlc.clip.shapeY;472jfloat shapeX2 = (mtlc.clip.shapeWidth > 0)? shapeX1 + mtlc.clip.shapeWidth : 0;473jfloat shapeY2 = (mtlc.clip.shapeHeight > 0)? shapeY1 + mtlc.clip.shapeHeight : 0;474475int counter = 0;476for (int i = 0; i < spanCount; i++) {477jfloat x1 = *(spans++);478jfloat y1 = *(spans++);479jfloat x2 = *(spans++);480jfloat y2 = *(spans++);481482if (mtlc.clip.stencilMaskGenerationInProgress == JNI_TRUE) {483if (shapeX1 > x1) shapeX1 = x1;484if (shapeY1 > y1) shapeY1 = y1;485if (shapeX2 < x2) shapeX2 = x2;486if (shapeY2 < y2) shapeY2 = y2;487}488489struct Vertex verts[6] = {490{{x1, y1}},491{{x1, y2}},492{{x2, y1}},493494{{x1, y2}},495{{x2, y1}},496{{x2, y2}497}};498499memcpy(&vertexList[counter], &verts, sizeof(verts));500counter += 6;501502// If vertexList buffer full503if (counter % TOTAL_VERTICES_IN_BLOCK == 0) {504[mtlEncoder setVertexBytes:vertexList length:sizeof(vertexList) atIndex:MeshVertexBuffer];505[mtlEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:TOTAL_VERTICES_IN_BLOCK];506counter = 0;507}508}509510// Draw triangles using remaining vertices if any511if (counter != 0) {512[mtlEncoder setVertexBytes:vertexList length:sizeof(vertexList) atIndex:MeshVertexBuffer];513[mtlEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:counter];514}515516if (mtlc.clip.stencilMaskGenerationInProgress == JNI_TRUE) {517if (shapeX1 < 0) shapeX1 = 0;518if (shapeY1 < 0) shapeY1 = 0;519if (shapeX1 > dest.width) shapeX1 = dest.width;520if (shapeY1 > dest.height) shapeY1 = dest.height;521if (shapeX2 < 0) shapeX2 = 0;522if (shapeY2 < 0) shapeY2 = 0;523if (shapeX2 > dest.width) shapeX2 = dest.width;524if (shapeY2 > dest.height) shapeY2 = dest.height;525526mtlc.clip.shapeX = (NSUInteger) shapeX1;527mtlc.clip.shapeY = (NSUInteger) shapeY1;528mtlc.clip.shapeWidth = (NSUInteger) (shapeX2 - shapeX1);529mtlc.clip.shapeHeight = (NSUInteger) (shapeY2 - shapeY1);530}531}532533void534MTLRenderer_FillParallelogram(MTLContext *mtlc, BMTLSDOps * dstOps,535jfloat fx11, jfloat fy11,536jfloat dx21, jfloat dy21,537jfloat dx12, jfloat dy12)538{539540if (mtlc == NULL || dstOps == NULL || dstOps->pTexture == NULL) {541J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_FillParallelogram: current dest is null");542return;543}544545id<MTLTexture> dest = dstOps->pTexture;546J2dTraceLn7(J2D_TRACE_INFO,547"MTLRenderer_FillParallelogram"548"(x=%6.2f y=%6.2f "549"dx1=%6.2f dy1=%6.2f "550"dx2=%6.2f dy2=%6.2f dst tex=%p)",551fx11, fy11,552dx21, dy21,553dx12, dy12, dest);554555struct Vertex verts[QUAD_VERTEX_COUNT] = {556{ {fx11, fy11}},557{ {fx11+dx21, fy11+dy21}},558{ {fx11+dx12, fy11+dy12}},559{ {fx11 + dx21 + dx12, fy11+ dy21 + dy12}560}};561562// Encode render command.563id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dstOps];;564565if (mtlEncoder == nil) {566J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_FillParallelogram: error creating MTLRenderCommandEncoder.");567return;568}569570[mtlEncoder setVertexBytes:verts length:sizeof(verts) atIndex:MeshVertexBuffer];571[mtlEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount: QUAD_VERTEX_COUNT];572}573574void575MTLRenderer_DrawParallelogram(MTLContext *mtlc, BMTLSDOps * dstOps,576jfloat fx11, jfloat fy11,577jfloat dx21, jfloat dy21,578jfloat dx12, jfloat dy12,579jfloat lwr21, jfloat lwr12)580{581// dx,dy for line width in the "21" and "12" directions.582jfloat ldx21 = dx21 * lwr21;583jfloat ldy21 = dy21 * lwr21;584jfloat ldx12 = dx12 * lwr12;585jfloat ldy12 = dy12 * lwr12;586587// calculate origin of the outer parallelogram588jfloat ox11 = fx11 - (ldx21 + ldx12) / 2.0f;589jfloat oy11 = fy11 - (ldy21 + ldy12) / 2.0f;590591J2dTraceLn8(J2D_TRACE_INFO,592"MTLRenderer_DrawParallelogram"593"(x=%6.2f y=%6.2f "594"dx1=%6.2f dy1=%6.2f lwr1=%6.2f "595"dx2=%6.2f dy2=%6.2f lwr2=%6.2f)",596fx11, fy11,597dx21, dy21, lwr21,598dx12, dy12, lwr12);599600601// Only need to generate 4 quads if the interior still602// has a hole in it (i.e. if the line width ratio was603// less than 1.0)604if (lwr21 < 1.0f && lwr12 < 1.0f) {605606// Note: "TOP", "BOTTOM", "LEFT" and "RIGHT" here are607// relative to whether the dxNN variables are positive608// and negative. The math works fine regardless of609// their signs, but for conceptual simplicity the610// comments will refer to the sides as if the dxNN611// were all positive. "TOP" and "BOTTOM" segments612// are defined by the dxy21 deltas. "LEFT" and "RIGHT"613// segments are defined by the dxy12 deltas.614615// Each segment includes its starting corner and comes616// to just short of the following corner. Thus, each617// corner is included just once and the only lengths618// needed are the original parallelogram delta lengths619// and the "line width deltas". The sides will cover620// the following relative territories:621//622// T T T T T R623// L R624// L R625// L R626// L R627// L B B B B B628629// Every segment is drawn as a filled Parallelogram quad630// Each quad is encoded using two triangles631// For 4 segments - there are 8 triangles in total632// Each triangle has 3 vertices633const int TOTAL_VERTICES = 8 * 3;634struct Vertex vertexList[TOTAL_VERTICES];635int i = 0;636637// TOP segment, to left side of RIGHT edge638// "width" of original pgram, "height" of hor. line size639fx11 = ox11;640fy11 = oy11;641642fillVertex(vertexList + (i++), fx11, fy11);643fillVertex(vertexList + (i++), fx11 + dx21, fy11 + dy21);644fillVertex(vertexList + (i++), fx11 + dx21 + ldx12, fy11 + dy21 + ldy12);645646fillVertex(vertexList + (i++), fx11 + dx21 + ldx12, fy11 + dy21 + ldy12);647fillVertex(vertexList + (i++), fx11 + ldx12, fy11 + ldy12);648fillVertex(vertexList + (i++), fx11, fy11);649650// RIGHT segment, to top of BOTTOM edge651// "width" of vert. line size , "height" of original pgram652fx11 = ox11 + dx21;653fy11 = oy11 + dy21;654fillVertex(vertexList + (i++), fx11, fy11);655fillVertex(vertexList + (i++), fx11 + ldx21, fy11 + ldy21);656fillVertex(vertexList + (i++), fx11 + ldx21 + dx12, fy11 + ldy21 + dy12);657658fillVertex(vertexList + (i++), fx11 + ldx21 + dx12, fy11 + ldy21 + dy12);659fillVertex(vertexList + (i++), fx11 + dx12, fy11 + dy12);660fillVertex(vertexList + (i++), fx11, fy11);661662// BOTTOM segment, from right side of LEFT edge663// "width" of original pgram, "height" of hor. line size664fx11 = ox11 + dx12 + ldx21;665fy11 = oy11 + dy12 + ldy21;666fillVertex(vertexList + (i++), fx11, fy11);667fillVertex(vertexList + (i++), fx11 + dx21, fy11 + dy21);668fillVertex(vertexList + (i++), fx11 + dx21 + ldx12, fy11 + dy21 + ldy12);669670fillVertex(vertexList + (i++), fx11 + dx21 + ldx12, fy11 + dy21 + ldy12);671fillVertex(vertexList + (i++), fx11 + ldx12, fy11 + ldy12);672fillVertex(vertexList + (i++), fx11, fy11);673674// LEFT segment, from bottom of TOP edge675// "width" of vert. line size , "height" of inner pgram676fx11 = ox11 + ldx12;677fy11 = oy11 + ldy12;678fillVertex(vertexList + (i++), fx11, fy11);679fillVertex(vertexList + (i++), fx11 + ldx21, fy11 + ldy21);680fillVertex(vertexList + (i++), fx11 + ldx21 + dx12, fy11 + ldy21 + dy12);681682fillVertex(vertexList + (i++), fx11 + ldx21 + dx12, fy11 + ldy21 + dy12);683fillVertex(vertexList + (i++), fx11 + dx12, fy11 + dy12);684fillVertex(vertexList + (i++), fx11, fy11);685686// Encode render command.687id<MTLRenderCommandEncoder> mtlEncoder = [mtlc.encoderManager getRenderEncoder:dstOps];688689if (mtlEncoder == nil) {690J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLRenderer_DrawParallelogram: error creating MTLRenderCommandEncoder.");691return;692}693694[mtlEncoder setVertexBytes:vertexList length:sizeof(vertexList) atIndex:MeshVertexBuffer];695[mtlEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:TOTAL_VERTICES];696} else {697// The line width ratios were large enough to consume698// the entire hole in the middle of the parallelogram699// so we can just issue one large quad for the outer700// parallelogram.701dx21 += ldx21;702dy21 += ldy21;703dx12 += ldx12;704dy12 += ldy12;705MTLRenderer_FillParallelogram(mtlc, dstOps, ox11, oy11, dx21, dy21, dx12, dy12);706}707}708709static struct AAVertex aaVertices[6];710static jint vertexCacheIndex = 0;711712#define AA_ADD_VERTEX(OU, OV, IU, IV, DX, DY) \713do { \714struct AAVertex *v = &aaVertices[vertexCacheIndex++]; \715v->otxtpos[0] = OU; \716v->otxtpos[1] = OV; \717v->itxtpos[0] = IU; \718v->itxtpos[1] = IV; \719v->position[0]= DX; \720v->position[1] = DY; \721} while (0)722723#define AA_ADD_TRIANGLES(ou11, ov11, iu11, iv11, ou21, ov21, iu21, iv21, ou22, ov22, iu22, iv22, ou12, ov12, iu12, iv12, DX1, DY1, DX2, DY2) \724do { \725AA_ADD_VERTEX(ou11, ov11, iu11, iv11, DX1, DY1); \726AA_ADD_VERTEX(ou21, ov21, iu21, iv21, DX2, DY1); \727AA_ADD_VERTEX(ou22, ov22, iu22, iv22, DX2, DY2); \728AA_ADD_VERTEX(ou22, ov22, iu22, iv22, DX2, DY2); \729AA_ADD_VERTEX(ou12, ov12, iu12, iv12, DX1, DY2); \730AA_ADD_VERTEX(ou11, ov11, iu11, iv11, DX1, DY1); \731} while (0)732733#define ADJUST_PGRAM(V1, DV, V2) \734do { \735if ((DV) >= 0) { \736(V2) += (DV); \737} else { \738(V1) += (DV); \739} \740} while (0)741742// Invert the following transform:743// DeltaT(0, 0) == (0, 0)744// DeltaT(1, 0) == (DX1, DY1)745// DeltaT(0, 1) == (DX2, DY2)746// DeltaT(1, 1) == (DX1+DX2, DY1+DY2)747// TM00 = DX1, TM01 = DX2, (TM02 = X11)748// TM10 = DY1, TM11 = DY2, (TM12 = Y11)749// Determinant = TM00*TM11 - TM01*TM10750// = DX1*DY2 - DX2*DY1751// Inverse is:752// IM00 = TM11/det, IM01 = -TM01/det753// IM10 = -TM10/det, IM11 = TM00/det754// IM02 = (TM01 * TM12 - TM11 * TM02) / det,755// IM12 = (TM10 * TM02 - TM00 * TM12) / det,756757#define DECLARE_MATRIX(MAT) \758jfloat MAT ## 00, MAT ## 01, MAT ## 02, MAT ## 10, MAT ## 11, MAT ## 12759760#define GET_INVERTED_MATRIX(MAT, X11, Y11, DX1, DY1, DX2, DY2, RET_CODE) \761do { \762jfloat det = DX1*DY2 - DX2*DY1; \763if (det == 0) { \764RET_CODE; \765} \766MAT ## 00 = DY2/det; \767MAT ## 01 = -DX2/det; \768MAT ## 10 = -DY1/det; \769MAT ## 11 = DX1/det; \770MAT ## 02 = (DX2 * Y11 - DY2 * X11) / det; \771MAT ## 12 = (DY1 * X11 - DX1 * Y11) / det; \772} while (0)773774#define TRANSFORM(MAT, TX, TY, X, Y) \775do { \776TX = (X) * MAT ## 00 + (Y) * MAT ## 01 + MAT ## 02; \777TY = (X) * MAT ## 10 + (Y) * MAT ## 11 + MAT ## 12; \778} while (0)779780void781MTLRenderer_FillAAParallelogram(MTLContext *mtlc, BMTLSDOps * dstOps,782jfloat fx11, jfloat fy11,783jfloat dx21, jfloat dy21,784jfloat dx12, jfloat dy12)785{786DECLARE_MATRIX(om);787// parameters for parallelogram bounding box788jfloat bx11, by11, bx22, by22;789// parameters for uv texture coordinates of parallelogram corners790jfloat ou11, ov11, ou12, ov12, ou21, ov21, ou22, ov22;791792J2dTraceLn6(J2D_TRACE_INFO,793"MTLRenderer_FillAAParallelogram "794"(x=%6.2f y=%6.2f "795"dx1=%6.2f dy1=%6.2f "796"dx2=%6.2f dy2=%6.2f)",797fx11, fy11,798dx21, dy21,799dx12, dy12);800801RETURN_IF_NULL(mtlc);802RETURN_IF_NULL(dstOps);803804GET_INVERTED_MATRIX(om, fx11, fy11, dx21, dy21, dx12, dy12,805return);806807bx11 = bx22 = fx11;808by11 = by22 = fy11;809ADJUST_PGRAM(bx11, dx21, bx22);810ADJUST_PGRAM(by11, dy21, by22);811ADJUST_PGRAM(bx11, dx12, bx22);812ADJUST_PGRAM(by11, dy12, by22);813bx11 = (jfloat) floor(bx11);814by11 = (jfloat) floor(by11);815bx22 = (jfloat) ceil(bx22);816by22 = (jfloat) ceil(by22);817818TRANSFORM(om, ou11, ov11, bx11, by11);819TRANSFORM(om, ou21, ov21, bx22, by11);820TRANSFORM(om, ou12, ov12, bx11, by22);821TRANSFORM(om, ou22, ov22, bx22, by22);822823id<MTLRenderCommandEncoder> encoder =824[mtlc.encoderManager getAAShaderRenderEncoder:dstOps];825826AA_ADD_TRIANGLES(ou11, ov11, 5.f, 5.f, ou21, ov21, 6.f, 5.f, ou22, ov22, 6.f, 6.f, ou12, ov12, 5.f, 5.f, bx11, by11, bx22, by22);827[encoder setVertexBytes:aaVertices length:sizeof(aaVertices) atIndex:MeshVertexBuffer];828[encoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:6];829vertexCacheIndex = 0;830}831832void833MTLRenderer_FillAAParallelogramInnerOuter(MTLContext *mtlc, MTLSDOps *dstOps,834jfloat ox11, jfloat oy11,835jfloat ox21, jfloat oy21,836jfloat ox12, jfloat oy12,837jfloat ix11, jfloat iy11,838jfloat ix21, jfloat iy21,839jfloat ix12, jfloat iy12)840{841DECLARE_MATRIX(om);842DECLARE_MATRIX(im);843// parameters for parallelogram bounding box844jfloat bx11, by11, bx22, by22;845// parameters for uv texture coordinates of outer parallelogram corners846jfloat ou11, ov11, ou12, ov12, ou21, ov21, ou22, ov22;847// parameters for uv texture coordinates of inner parallelogram corners848jfloat iu11, iv11, iu12, iv12, iu21, iv21, iu22, iv22;849850RETURN_IF_NULL(mtlc);851RETURN_IF_NULL(dstOps);852853GET_INVERTED_MATRIX(im, ix11, iy11, ix21, iy21, ix12, iy12,854// inner parallelogram is degenerate855// therefore it encloses no area856// fill outer857MTLRenderer_FillAAParallelogram(mtlc, dstOps,858ox11, oy11,859ox21, oy21,860ox12, oy12);861return);862GET_INVERTED_MATRIX(om, ox11, oy11, ox21, oy21, ox12, oy12,863return);864865bx11 = bx22 = ox11;866by11 = by22 = oy11;867ADJUST_PGRAM(bx11, ox21, bx22);868ADJUST_PGRAM(by11, oy21, by22);869ADJUST_PGRAM(bx11, ox12, bx22);870ADJUST_PGRAM(by11, oy12, by22);871bx11 = (jfloat) floor(bx11);872by11 = (jfloat) floor(by11);873bx22 = (jfloat) ceil(bx22);874by22 = (jfloat) ceil(by22);875876TRANSFORM(om, ou11, ov11, bx11, by11);877TRANSFORM(om, ou21, ov21, bx22, by11);878TRANSFORM(om, ou12, ov12, bx11, by22);879TRANSFORM(om, ou22, ov22, bx22, by22);880881TRANSFORM(im, iu11, iv11, bx11, by11);882TRANSFORM(im, iu21, iv21, bx22, by11);883TRANSFORM(im, iu12, iv12, bx11, by22);884TRANSFORM(im, iu22, iv22, bx22, by22);885886id<MTLRenderCommandEncoder> encoder =887[mtlc.encoderManager getAAShaderRenderEncoder:dstOps];888889AA_ADD_TRIANGLES(ou11, ov11, iu11, iv11, ou21, ov21, iu21, iv21, ou22, ov22, iu22, iv22, ou12, ov12, iu12, iv12, bx11, by11, bx22, by22);890[encoder setVertexBytes:aaVertices length:sizeof(aaVertices) atIndex:MeshVertexBuffer];891[encoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:6];892vertexCacheIndex = 0;893}894895void896MTLRenderer_DrawAAParallelogram(MTLContext *mtlc, BMTLSDOps * dstOps,897jfloat fx11, jfloat fy11,898jfloat dx21, jfloat dy21,899jfloat dx12, jfloat dy12,900jfloat lwr21, jfloat lwr12)901{902// dx,dy for line width in the "21" and "12" directions.903jfloat ldx21, ldy21, ldx12, ldy12;904// parameters for "outer" parallelogram905jfloat ofx11, ofy11, odx21, ody21, odx12, ody12;906// parameters for "inner" parallelogram907jfloat ifx11, ify11, idx21, idy21, idx12, idy12;908909J2dTraceLn8(J2D_TRACE_INFO,910"MTLRenderer_DrawAAParallelogram "911"(x=%6.2f y=%6.2f "912"dx1=%6.2f dy1=%6.2f lwr1=%6.2f "913"dx2=%6.2f dy2=%6.2f lwr2=%6.2f)",914fx11, fy11,915dx21, dy21, lwr21,916dx12, dy12, lwr12);917918RETURN_IF_NULL(mtlc);919RETURN_IF_NULL(dstOps);920921// calculate true dx,dy for line widths from the "line width ratios"922ldx21 = dx21 * lwr21;923ldy21 = dy21 * lwr21;924ldx12 = dx12 * lwr12;925ldy12 = dy12 * lwr12;926927// calculate coordinates of the outer parallelogram928ofx11 = fx11 - (ldx21 + ldx12) / 2.0f;929ofy11 = fy11 - (ldy21 + ldy12) / 2.0f;930odx21 = dx21 + ldx21;931ody21 = dy21 + ldy21;932odx12 = dx12 + ldx12;933ody12 = dy12 + ldy12;934935// Only process the inner parallelogram if the line width ratio936// did not consume the entire interior of the parallelogram937// (i.e. if the width ratio was less than 1.0)938if (lwr21 < 1.0f && lwr12 < 1.0f) {939// calculate coordinates of the inner parallelogram940ifx11 = fx11 + (ldx21 + ldx12) / 2.0f;941ify11 = fy11 + (ldy21 + ldy12) / 2.0f;942idx21 = dx21 - ldx21;943idy21 = dy21 - ldy21;944idx12 = dx12 - ldx12;945idy12 = dy12 - ldy12;946947MTLRenderer_FillAAParallelogramInnerOuter(mtlc, dstOps,948ofx11, ofy11,949odx21, ody21,950odx12, ody12,951ifx11, ify11,952idx21, idy21,953idx12, idy12);954} else {955MTLRenderer_FillAAParallelogram(mtlc, dstOps,956ofx11, ofy11,957odx21, ody21,958odx12, ody12);959}960}961962963