| /* | |
| * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| #include <math.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include "GraphicsPrimitiveMgr.h" | |
| #include "ParallelogramUtils.h" | |
| #include "sun_java2d_loops_MaskFill.h" | |
| /* | |
| * Class: sun_java2d_loops_MaskFill | |
| * Method: MaskFill | |
| * Signature: (Lsun/java2d/SunGraphics2D;Lsun/java2d/SurfaceData;Ljava/awt/Composite;IIII[BII)V | |
| */ | |
| JNIEXPORT void JNICALL | |
| Java_sun_java2d_loops_MaskFill_MaskFill | |
| (JNIEnv *env, jobject self, | |
| jobject sg2d, jobject sData, jobject comp, | |
| jint x, jint y, jint w, jint h, | |
| jbyteArray maskArray, jint maskoff, jint maskscan) | |
| { | |
| SurfaceDataOps *sdOps; | |
| SurfaceDataRasInfo rasInfo; | |
| NativePrimitive *pPrim; | |
| CompositeInfo compInfo; | |
| pPrim = GetNativePrim(env, self); | |
| if (pPrim == NULL) { | |
| return; | |
| } | |
| if (pPrim->pCompType->getCompInfo != NULL) { | |
| (*pPrim->pCompType->getCompInfo)(env, &compInfo, comp); | |
| } | |
| sdOps = SurfaceData_GetOps(env, sData); | |
| if (sdOps == 0) { | |
| return; | |
| } | |
| rasInfo.bounds.x1 = x; | |
| rasInfo.bounds.y1 = y; | |
| rasInfo.bounds.x2 = x + w; | |
| rasInfo.bounds.y2 = y + h; | |
| if (sdOps->Lock(env, sdOps, &rasInfo, pPrim->dstflags) != SD_SUCCESS) { | |
| return; | |
| } | |
| if (rasInfo.bounds.x2 > rasInfo.bounds.x1 && | |
| rasInfo.bounds.y2 > rasInfo.bounds.y1) | |
| { | |
| jint color = GrPrim_Sg2dGetEaRGB(env, sg2d); | |
| sdOps->GetRasInfo(env, sdOps, &rasInfo); | |
| if (rasInfo.rasBase) { | |
| jint width = rasInfo.bounds.x2 - rasInfo.bounds.x1; | |
| jint height = rasInfo.bounds.y2 - rasInfo.bounds.y1; | |
| void *pDst = PtrCoord(rasInfo.rasBase, | |
| rasInfo.bounds.x1, rasInfo.pixelStride, | |
| rasInfo.bounds.y1, rasInfo.scanStride); | |
| unsigned char *pMask = | |
| (maskArray | |
| ? (*env)->GetPrimitiveArrayCritical(env, maskArray, 0) | |
| : 0); | |
| if (maskArray != NULL && pMask == NULL) { | |
| SurfaceData_InvokeRelease(env, sdOps, &rasInfo); | |
| SurfaceData_InvokeUnlock(env, sdOps, &rasInfo); | |
| return; | |
| } | |
| maskoff += ((rasInfo.bounds.y1 - y) * maskscan + | |
| (rasInfo.bounds.x1 - x)); | |
| (*pPrim->funcs.maskfill)(pDst, | |
| pMask, maskoff, maskscan, | |
| width, height, | |
| color, &rasInfo, | |
| pPrim, &compInfo); | |
| if (pMask) { | |
| (*env)->ReleasePrimitiveArrayCritical(env, maskArray, | |
| pMask, JNI_ABORT); | |
| } | |
| } | |
| SurfaceData_InvokeRelease(env, sdOps, &rasInfo); | |
| } | |
| SurfaceData_InvokeUnlock(env, sdOps, &rasInfo); | |
| } | |
| #define MASK_BUF_LEN 1024 | |
| #define DblToMask(v) ((unsigned char) ((v)*255.9999)) | |
| /* Fills an aligned rectangle with potentially translucent edges. */ | |
| static void | |
| fillAARect(NativePrimitive *pPrim, SurfaceDataRasInfo *pRasInfo, | |
| CompositeInfo *pCompInfo, jint color, unsigned char *pMask, | |
| void *pDst, | |
| jdouble x1, jdouble y1, jdouble x2, jdouble y2) | |
| { | |
| jint cx1 = pRasInfo->bounds.x1; | |
| jint cy1 = pRasInfo->bounds.y1; | |
| jint cx2 = pRasInfo->bounds.x2; | |
| jint cy2 = pRasInfo->bounds.y2; | |
| jint rx1 = (jint) ceil(x1); | |
| jint ry1 = (jint) ceil(y1); | |
| jint rx2 = (jint) floor(x2); | |
| jint ry2 = (jint) floor(y2); | |
| jint width = cx2 - cx1; | |
| jint scan = pRasInfo->scanStride; | |
| /* Convert xy12 into the edge coverage fractions for those edges. */ | |
| x1 = rx1-x1; | |
| y1 = ry1-y1; | |
| x2 = x2-rx2; | |
| y2 = y2-ry2; | |
| if (ry2 < ry1) { | |
| /* Accumulate bottom coverage into top coverage. */ | |
| y1 = y1 + y2 - 1.0; | |
| /* prevent processing of "bottom fractional row" */ | |
| ry2 = cy2; | |
| } | |
| if (rx2 < rx1) { | |
| /* Accumulate right coverage into left coverage. */ | |
| x1 = x1 + x2 - 1.0; | |
| /* prevent processing of "right fractional column" */ | |
| rx2 = cx2; | |
| } | |
| /* Check for a visible "top fractional row" and process it */ | |
| if (cy1 < ry1) { | |
| unsigned char midcov = DblToMask(y1); | |
| jint x; | |
| for (x = 0; x < width; x++) { | |
| pMask[x] = midcov; | |
| } | |
| if (cx1 < rx1) { | |
| pMask[0] = DblToMask(y1 * x1); | |
| } | |
| if (cx2 > rx2) { | |
| pMask[width-1] = DblToMask(y1 * x2); | |
| } | |
| (*pPrim->funcs.maskfill)(pDst, | |
| pMask, 0, 0, | |
| width, 1, | |
| color, pRasInfo, | |
| pPrim, pCompInfo); | |
| pDst = PtrAddBytes(pDst, scan); | |
| cy1++; | |
| } | |
| /* Check for a visible "left fract, solid middle, right fract" section. */ | |
| if (cy1 < ry2 && cy1 < cy2) { | |
| jint midh = ((ry2 < cy2) ? ry2 : cy2) - cy1; | |
| jint midx = cx1; | |
| void *pMid = pDst; | |
| /* First process the left "fractional column" if it is visible. */ | |
| if (midx < rx1) { | |
| pMask[0] = DblToMask(x1); | |
| /* Note: maskscan == 0 means we reuse this value for every row. */ | |
| (*pPrim->funcs.maskfill)(pMid, | |
| pMask, 0, 0, | |
| 1, midh, | |
| color, pRasInfo, | |
| pPrim, pCompInfo); | |
| pMid = PtrAddBytes(pMid, pRasInfo->pixelStride); | |
| midx++; | |
| } | |
| /* Process the central solid section if it is visible. */ | |
| if (midx < rx2 && midx < cx2) { | |
| jint midw = ((rx2 < cx2) ? rx2 : cx2) - midx; | |
| /* A NULL mask buffer means "all coverages are 0xff" */ | |
| (*pPrim->funcs.maskfill)(pMid, | |
| NULL, 0, 0, | |
| midw, midh, | |
| color, pRasInfo, | |
| pPrim, pCompInfo); | |
| pMid = PtrCoord(pMid, midw, pRasInfo->pixelStride, 0, 0); | |
| midx += midw; | |
| } | |
| /* Finally process the right "fractional column" if it is visible. */ | |
| if (midx < cx2) { | |
| pMask[0] = DblToMask(x2); | |
| /* Note: maskscan == 0 means we reuse this value for every row. */ | |
| (*pPrim->funcs.maskfill)(pMid, | |
| pMask, 0, 0, | |
| 1, midh, | |
| color, pRasInfo, | |
| pPrim, pCompInfo); | |
| } | |
| cy1 += midh; | |
| pDst = PtrCoord(pDst, 0, 0, midh, scan); | |
| } | |
| /* Check for a visible "bottom fractional row" and process it */ | |
| if (cy1 < cy2) { | |
| unsigned char midcov = DblToMask(y2); | |
| jint x; | |
| for (x = 0; x < width; x++) { | |
| pMask[x] = midcov; | |
| } | |
| if (cx1 < rx1) { | |
| pMask[0] = DblToMask(y2 * x1); | |
| } | |
| if (cx2 > rx2) { | |
| pMask[width-1] = DblToMask(y2 * x2); | |
| } | |
| (*pPrim->funcs.maskfill)(pDst, | |
| pMask, 0, 0, | |
| width, 1, | |
| color, pRasInfo, | |
| pPrim, pCompInfo); | |
| } | |
| } | |
| /* | |
| * Support code for arbitrary tracing and MaskFill filling of | |
| * non-rectilinear (diagonal) parallelograms. | |
| * | |
| * This code is based upon the following model of AA coverage. | |
| * | |
| * Each edge of a parallelogram (for fillPgram) or a double | |
| * parallelogram (inner and outer parallelograms for drawPgram) | |
| * can be rasterized independently because the geometry is well | |
| * defined in such a way that none of the sides will ever cross | |
| * each other and they have a fixed ordering that is fairly | |
| * well predetermined. | |
| * | |
| * So, for each edge we will look at the diagonal line that | |
| * the edge makes as it passes through a row of pixels. Some | |
| * such diagonal lines may pass entirely through the row of | |
| * pixels in a single pixel column. Some may cut across the | |
| * row and pass through several pixel columns before they pass | |
| * on to the next row. | |
| * | |
| * As the edge passes through the row of pixels it will affect | |
| * the coverage of the pixels it passes through as well as all | |
| * of the pixels to the right of the edge. The coverage will | |
| * either be increased (by a left edge of a parallelogram) or | |
| * decreased (by a right edge) for all pixels to the right, until | |
| * another edge passing the opposite direction is encountered. | |
| * | |
| * The coverage added or subtracted by an edge as it crosses a | |
| * given pixel is calculated using a trapezoid formula in the | |
| * following manner: | |
| * | |
| * / | |
| * +-----+---/-+-----+ | |
| * | | / | | | |
| * | | / | | | |
| * +-----+/----+-----+ | |
| * / | |
| * | |
| * The area to the right of that edge for the pixel where it | |
| * crosses is given as: | |
| * | |
| * trapheight * (topedge + bottomedge)/2 | |
| * | |
| * Another thing to note is that the above formula gives the | |
| * contribution of that edge to the given pixel where it crossed, | |
| * but in so crossing the pixel row, it also created 100% coverage | |
| * for all of the pixels to the right. | |
| * | |
| * This example was simplified in that the edge depicted crossed | |
| * the complete pixel row and it did so entirely within the bounds | |
| * of a single pixel column. In practice, many edges may start or | |
| * end in a given row and thus provide only partial row coverage | |
| * (i.e. the total "trapheight" in the formula never reaches 1.0). | |
| * And in other cases, edges may travel sideways through several | |
| * pixel columns on a given pixel row from where they enter it to | |
| * where the leave it (which also mans that the trapheight for a | |
| * given pixel will be less than 1.0, but by the time the edge | |
| * completes its journey through the pixel row the "coverage shadow" | |
| * that it casts on all pixels to the right eventually reaches 100%). | |
| * | |
| * In order to simplify the calculations so that we don't have to | |
| * keep propagating coverages we calculate for one edge "until we | |
| * reach another edge" we will process one edge at a time and | |
| * simply record in a buffer the amount that an edge added to | |
| * or subtracted from the coverage for a given pixel and its | |
| * following right-side neighbors. Thus, the true total coverage | |
| * of a given pixel is only determined by summing the deltas for | |
| * that pixel and all of the pixels to its left. Since we already | |
| * have to scan the buffer to change floating point coverages into | |
| * mask values for a MaskFill loop, it is simple enough to sum the | |
| * values as we perform that scan from left to right. | |
| * | |
| * In the above example, note that 2 deltas need to be recorded even | |
| * though the edge only intersected a single pixel. The delta recorded | |
| * for the pixel where the edge crossed will be approximately 55% | |
| * (guesstimating by examining the poor ascii art) which is fine for | |
| * determining how to render that pixel, but the rest of the pixels | |
| * to its right should have their coverage modified by a full 100% | |
| * and the 55% delta value we recorded for the pixel that the edge | |
| * crossed will not get them there. We adjust for this by adding | |
| * the "remainder" of the coverage implied by the shadow to the | |
| * pixel immediately to the right of where we record a trapezoidal | |
| * contribution. In this case a delta of 45% will be recorded in | |
| * the pixel immediately to the right to raise the total to 100%. | |
| * | |
| * As we sum these delta values as we process the line from left | |
| * to right, these delta values will typically drive the sum from | |
| * 0% up to 100% and back down to 0% over the course of a single | |
| * pixel row. In the case of a drawn (double) parallelogram the | |
| * sum will go to 100% and back to 0% twice on most scanlines. | |
| * | |
| * The fillAAPgram and drawAAPgram functions drive the main flow | |
| * of the algorithm with help from the following structures, | |
| * macros, and functions. It is probably best to start with | |
| * those 2 functions to gain an understanding of the algorithm. | |
| */ | |
| typedef struct { | |
| jdouble x; | |
| jdouble y; | |
| jdouble xbot; | |
| jdouble ybot; | |
| jdouble xnexty; | |
| jdouble ynextx; | |
| jdouble xnextx; | |
| jdouble linedx; | |
| jdouble celldx; | |
| jdouble celldy; | |
| jboolean isTrailing; | |
| } EdgeInfo; | |
| #define MIN_DELTA (1.0/256.0) | |
| /* | |
| * Calculates slopes and deltas for an edge and stores results in an EdgeInfo. | |
| * Returns true if the edge was valid (i.e. not ignored for some reason). | |
| */ | |
| static jboolean | |
| storeEdge(EdgeInfo *pEdge, | |
| jdouble x, jdouble y, jdouble dx, jdouble dy, | |
| jint cx1, jint cy1, jint cx2, jint cy2, | |
| jboolean isTrailing) | |
| { | |
| jdouble xbot = x + dx; | |
| jdouble ybot = y + dy; | |
| jboolean ret; | |
| pEdge->x = x; | |
| pEdge->y = y; | |
| pEdge->xbot = xbot; | |
| pEdge->ybot = ybot; | |
| /* Note that parallelograms are sorted so dy is always non-negative */ | |
| if (dy > MIN_DELTA && /* NaN and horizontal protection */ | |
| ybot > cy1 && /* NaN and "OUT_ABOVE" protection */ | |
| y < cy2 && /* NaN and "OUT_BELOW" protection */ | |
| xbot == xbot && /* NaN protection */ | |
| (x < cx2 || xbot < cx2)) /* "OUT_RIGHT" protection */ | |
| /* Note: "OUT_LEFT" segments may still contribute coverage... */ | |
| { | |
| /* no NaNs, dy is not horizontal, and segment contributes to clip */ | |
| if (dx < -MIN_DELTA || dx > MIN_DELTA) { | |
| /* dx is not vertical */ | |
| jdouble linedx; | |
| jdouble celldy; | |
| jdouble nextx; | |
| linedx = dx / dy; | |
| celldy = dy / dx; | |
| if (y < cy1) { | |
| pEdge->x = x = x + (cy1 - y) * linedx; | |
| pEdge->y = y = cy1; | |
| } | |
| pEdge->linedx = linedx; | |
| if (dx < 0) { | |
| pEdge->celldx = -1.0; | |
| pEdge->celldy = -celldy; | |
| pEdge->xnextx = nextx = ceil(x) - 1.0; | |
| } else { | |
| pEdge->celldx = +1.0; | |
| pEdge->celldy = celldy; | |
| pEdge->xnextx = nextx = floor(x) + 1.0; | |
| } | |
| pEdge->ynextx = y + (nextx - x) * celldy; | |
| pEdge->xnexty = x + ((floor(y) + 1) - y) * linedx; | |
| } else { | |
| /* dx is essentially vertical */ | |
| if (y < cy1) { | |
| pEdge->y = y = cy1; | |
| } | |
| pEdge->xbot = x; | |
| pEdge->linedx = 0.0; | |
| pEdge->celldx = 0.0; | |
| pEdge->celldy = 1.0; | |
| pEdge->xnextx = x; | |
| pEdge->xnexty = x; | |
| pEdge->ynextx = ybot; | |
| } | |
| ret = JNI_TRUE; | |
| } else { | |
| /* There is some reason to ignore this segment, "celldy=0" omits it */ | |
| pEdge->ybot = y; | |
| pEdge->linedx = dx; | |
| pEdge->celldx = dx; | |
| pEdge->celldy = 0.0; | |
| pEdge->xnextx = xbot; | |
| pEdge->xnexty = xbot; | |
| pEdge->ynextx = y; | |
| ret = JNI_FALSE; | |
| } | |
| pEdge->isTrailing = isTrailing; | |
| return ret; | |
| } | |
| /* | |
| * Calculates and stores slopes and deltas for all edges of a parallelogram. | |
| * Returns true if at least 1 edge was valid (i.e. not ignored for some reason). | |
| * | |
| * The inverted flag is true for an outer parallelogram (left and right | |
| * edges are leading and trailing) and false for an inner parallelogram | |
| * (where the left edge is trailing and the right edge is leading). | |
| */ | |
| static jboolean | |
| storePgram(EdgeInfo *pLeftEdge, EdgeInfo *pRightEdge, | |
| jdouble x, jdouble y, | |
| jdouble dx1, jdouble dy1, | |
| jdouble dx2, jdouble dy2, | |
| jint cx1, jint cy1, jint cx2, jint cy2, | |
| jboolean inverted) | |
| { | |
| jboolean ret = JNI_FALSE; | |
| ret = (storeEdge(pLeftEdge + 0, | |
| x , y , dx1, dy1, | |
| cx1, cy1, cx2, cy2, inverted) || ret); | |
| ret = (storeEdge(pLeftEdge + 1, | |
| x+dx1, y+dy1, dx2, dy2, | |
| cx1, cy1, cx2, cy2, inverted) || ret); | |
| ret = (storeEdge(pRightEdge + 0, | |
| x , y , dx2, dy2, | |
| cx1, cy1, cx2, cy2, !inverted) || ret); | |
| ret = (storeEdge(pRightEdge + 1, | |
| x+dx2, y+dy2, dx1, dy1, | |
| cx1, cy1, cx2, cy2, !inverted) || ret); | |
| return ret; | |
| } | |
| /* | |
| * The X0,Y0,X1,Y1 values represent a trapezoidal fragment whose | |
| * coverage must be accounted for in the accum buffer. | |
| * | |
| * All four values are assumed to fall within (or on the edge of) | |
| * a single pixel. | |
| * | |
| * The trapezoid area is accumulated into the proper element of | |
| * the accum buffer and the remainder of the "slice height" is | |
| * accumulated into the element to its right. | |
| */ | |
| #define INSERT_ACCUM(pACCUM, IMIN, IMAX, X0, Y0, X1, Y1, CX1, CX2, MULT) \ | |
| do { \ | |
| jdouble xmid = ((X0) + (X1)) * 0.5; \ | |
| if (xmid < (CX2)) { \ | |
| jdouble sliceh = ((Y1) - (Y0)); \ | |
| jdouble slicearea; \ | |
| jint i; \ | |
| if (xmid < (CX1)) { \ | |
| /* Accumulate the entire slice height into accum[0]. */ \ | |
| i = 0; \ | |
| slicearea = sliceh; \ | |
| } else { \ | |
| jdouble xpos = floor(xmid); \ | |
| i = ((jint) xpos) - (CX1); \ | |
| slicearea = (xpos+1-xmid) * sliceh; \ | |
| } \ | |
| if (IMIN > i) { \ | |
| IMIN = i; \ | |
| } \ | |
| (pACCUM)[i++] += (jfloat) ((MULT) * slicearea); \ | |
| (pACCUM)[i++] += (jfloat) ((MULT) * (sliceh - slicearea)); \ | |
| if (IMAX < i) { \ | |
| IMAX = i; \ | |
| } \ | |
| } \ | |
| } while (0) | |
| /* | |
| * Accumulate the contributions for a given edge crossing a given | |
| * scan line into the corresponding entries of the accum buffer. | |
| * CY1 is the Y coordinate of the top edge of the scanline and CY2 | |
| * is equal to (CY1 + 1) and is the Y coordinate of the bottom edge | |
| * of the scanline. CX1 and CX2 are the left and right edges of the | |
| * clip (or area of interest) being rendered. | |
| * | |
| * The edge is processed from the top edge to the bottom edge and | |
| * a single pixel column at a time. | |
| */ | |
| #define ACCUM_EDGE(pEDGE, pACCUM, IMIN, IMAX, CX1, CY1, CX2, CY2) \ | |
| do { \ | |
| jdouble x, y, xnext, ynext, xlast, ylast, dx, dy, mult; \ | |
| y = (pEDGE)->y; \ | |
| dy = (pEDGE)->celldy; \ | |
| ylast = (pEDGE)->ybot; \ | |
| if (ylast <= (CY1) || y >= (CY2) || dy == 0.0) { \ | |
| break; \ | |
| } \ | |
| x = (pEDGE)->x; \ | |
| dx = (pEDGE)->celldx; \ | |
| if (ylast > (CY2)) { \ | |
| ylast = (CY2); \ | |
| xlast = (pEDGE)->xnexty; \ | |
| } else { \ | |
| xlast = (pEDGE)->xbot; \ | |
| } \ | |
| xnext = (pEDGE)->xnextx; \ | |
| ynext = (pEDGE)->ynextx; \ | |
| mult = ((pEDGE)->isTrailing) ? -1.0 : 1.0; \ | |
| while (ynext <= ylast) { \ | |
| INSERT_ACCUM(pACCUM, IMIN, IMAX, \ | |
| x, y, xnext, ynext, \ | |
| CX1, CX2, mult); \ | |
| x = xnext; \ | |
| y = ynext; \ | |
| xnext += dx; \ | |
| ynext += dy; \ | |
| } \ | |
| (pEDGE)->ynextx = ynext; \ | |
| (pEDGE)->xnextx = xnext; \ | |
| INSERT_ACCUM(pACCUM, IMIN, IMAX, \ | |
| x, y, xlast, ylast, \ | |
| CX1, CX2, mult); \ | |
| (pEDGE)->x = xlast; \ | |
| (pEDGE)->y = ylast; \ | |
| (pEDGE)->xnexty = xlast + (pEDGE)->linedx; \ | |
| } while(0) | |
| /* Main function to fill a single Parallelogram */ | |
| static void | |
| fillAAPgram(NativePrimitive *pPrim, SurfaceDataRasInfo *pRasInfo, | |
| CompositeInfo *pCompInfo, jint color, unsigned char *pMask, | |
| void *pDst, | |
| jdouble x1, jdouble y1, | |
| jdouble dx1, jdouble dy1, | |
| jdouble dx2, jdouble dy2) | |
| { | |
| jint cx1 = pRasInfo->bounds.x1; | |
| jint cy1 = pRasInfo->bounds.y1; | |
| jint cx2 = pRasInfo->bounds.x2; | |
| jint cy2 = pRasInfo->bounds.y2; | |
| jint width = cx2 - cx1; | |
| EdgeInfo edges[4]; | |
| jfloat localaccum[MASK_BUF_LEN + 2]; | |
| jfloat *pAccum; | |
| if (!storePgram(edges + 0, edges + 2, | |
| x1, y1, dx1, dy1, dx2, dy2, | |
| cx1, cy1, cx2, cy2, | |
| JNI_FALSE)) | |
| { | |
| return; | |
| } | |
| pAccum = ((width > MASK_BUF_LEN) | |
| ? malloc((width + 2) * sizeof(jfloat)) | |
| : localaccum); | |
| if (pAccum == NULL) { | |
| return; | |
| } | |
| memset(pAccum, 0, (width + 2) * sizeof(jfloat)); | |
| while (cy1 < cy2) { | |
| jint lmin, lmax, rmin, rmax; | |
| jint moff, x; | |
| jdouble accum; | |
| unsigned char lastcov; | |
| lmin = rmin = width + 2; | |
| lmax = rmax = 0; | |
| ACCUM_EDGE(&edges[0], pAccum, lmin, lmax, | |
| cx1, cy1, cx2, cy1+1); | |
| ACCUM_EDGE(&edges[1], pAccum, lmin, lmax, | |
| cx1, cy1, cx2, cy1+1); | |
| ACCUM_EDGE(&edges[2], pAccum, rmin, rmax, | |
| cx1, cy1, cx2, cy1+1); | |
| ACCUM_EDGE(&edges[3], pAccum, rmin, rmax, | |
| cx1, cy1, cx2, cy1+1); | |
| if (lmax > width) { | |
| lmax = width; /* Extra col has data we do not need. */ | |
| } | |
| if (rmax > width) { | |
| rmax = width; /* Extra col has data we do not need. */ | |
| } | |
| /* If ranges overlap, handle both in the first pass. */ | |
| if (rmin <= lmax) { | |
| lmax = rmax; | |
| } | |
| x = lmin; | |
| accum = 0.0; | |
| moff = 0; | |
| lastcov = 0; | |
| while (x < lmax) { | |
| accum += pAccum[x]; | |
| pAccum[x] = 0.0f; | |
| pMask[moff++] = lastcov = DblToMask(accum); | |
| x++; | |
| } | |
| /* Check for a solid center section. */ | |
| if (lastcov == 0xFF) { | |
| jint endx; | |
| void *pRow; | |
| /* First process the existing partial coverage data. */ | |
| if (moff > 0) { | |
| pRow = PtrCoord(pDst, x-moff, pRasInfo->pixelStride, 0, 0); | |
| (*pPrim->funcs.maskfill)(pRow, | |
| pMask, 0, 0, | |
| moff, 1, | |
| color, pRasInfo, | |
| pPrim, pCompInfo); | |
| moff = 0; | |
| } | |
| /* Where does the center section end? */ | |
| /* If there is no right AA edge in the accum buffer, then */ | |
| /* the right edge was beyond the clip, so fill out to width */ | |
| endx = (rmin < rmax) ? rmin : width; | |
| if (x < endx) { | |
| pRow = PtrCoord(pDst, x, pRasInfo->pixelStride, 0, 0); | |
| (*pPrim->funcs.maskfill)(pRow, | |
| NULL, 0, 0, | |
| endx - x, 1, | |
| color, pRasInfo, | |
| pPrim, pCompInfo); | |
| x = endx; | |
| } | |
| } else if (lastcov > 0 && rmin >= rmax) { | |
| /* We are not at 0 coverage, but there is no right edge, */ | |
| /* force a right edge so we process pixels out to width. */ | |
| rmax = width; | |
| } | |
| /* The following loop will process the right AA edge and/or any */ | |
| /* partial coverage center section not processed above. */ | |
| while (x < rmax) { | |
| accum += pAccum[x]; | |
| pAccum[x] = 0.0f; | |
| pMask[moff++] = DblToMask(accum); | |
| x++; | |
| } | |
| if (moff > 0) { | |
| void *pRow = PtrCoord(pDst, x-moff, pRasInfo->pixelStride, 0, 0); | |
| (*pPrim->funcs.maskfill)(pRow, | |
| pMask, 0, 0, | |
| moff, 1, | |
| color, pRasInfo, | |
| pPrim, pCompInfo); | |
| } | |
| pDst = PtrAddBytes(pDst, pRasInfo->scanStride); | |
| cy1++; | |
| } | |
| if (pAccum != localaccum) { | |
| free(pAccum); | |
| } | |
| } | |
| /* | |
| * Class: sun_java2d_loops_MaskFill | |
| * Method: FillAAPgram | |
| * Signature: (Lsun/java2d/SunGraphics2D;Lsun/java2d/SurfaceData;Ljava/awt/Composite;DDDDDD)V | |
| */ | |
| JNIEXPORT void JNICALL | |
| Java_sun_java2d_loops_MaskFill_FillAAPgram | |
| (JNIEnv *env, jobject self, | |
| jobject sg2d, jobject sData, jobject comp, | |
| jdouble x0, jdouble y0, | |
| jdouble dx1, jdouble dy1, | |
| jdouble dx2, jdouble dy2) | |
| { | |
| SurfaceDataOps *sdOps; | |
| SurfaceDataRasInfo rasInfo; | |
| NativePrimitive *pPrim; | |
| CompositeInfo compInfo; | |
| jint ix1, iy1, ix2, iy2; | |
| if ((dy1 == 0 && dx1 == 0) || (dy2 == 0 && dx2 == 0)) { | |
| return; | |
| } | |
| /* | |
| * Sort parallelogram by y values, ensure that each delta vector | |
| * has a non-negative y delta. | |
| */ | |
| SORT_PGRAM(x0, y0, dx1, dy1, dx2, dy2, ); | |
| PGRAM_MIN_MAX(ix1, ix2, x0, dx1, dx2, JNI_TRUE); | |
| iy1 = (jint) floor(y0); | |
| iy2 = (jint) ceil(y0 + dy1 + dy2); | |
| pPrim = GetNativePrim(env, self); | |
| if (pPrim == NULL) { | |
| return; | |
| } | |
| if (pPrim->pCompType->getCompInfo != NULL) { | |
| (*pPrim->pCompType->getCompInfo)(env, &compInfo, comp); | |
| } | |
| sdOps = SurfaceData_GetOps(env, sData); | |
| if (sdOps == 0) { | |
| return; | |
| } | |
| GrPrim_Sg2dGetClip(env, sg2d, &rasInfo.bounds); | |
| SurfaceData_IntersectBoundsXYXY(&rasInfo.bounds, ix1, iy1, ix2, iy2); | |
| if (rasInfo.bounds.y2 <= rasInfo.bounds.y1 || | |
| rasInfo.bounds.x2 <= rasInfo.bounds.x1) | |
| { | |
| return; | |
| } | |
| if (sdOps->Lock(env, sdOps, &rasInfo, pPrim->dstflags) != SD_SUCCESS) { | |
| return; | |
| } | |
| ix1 = rasInfo.bounds.x1; | |
| iy1 = rasInfo.bounds.y1; | |
| ix2 = rasInfo.bounds.x2; | |
| iy2 = rasInfo.bounds.y2; | |
| if (ix2 > ix1 && iy2 > iy1) { | |
| jint width = ix2 - ix1; | |
| jint color = GrPrim_Sg2dGetEaRGB(env, sg2d); | |
| unsigned char localmask[MASK_BUF_LEN]; | |
| unsigned char *pMask = ((width > MASK_BUF_LEN) | |
| ? malloc(width) | |
| : localmask); | |
| sdOps->GetRasInfo(env, sdOps, &rasInfo); | |
| if (rasInfo.rasBase != NULL && pMask != NULL) { | |
| void *pDst = PtrCoord(rasInfo.rasBase, | |
| ix1, rasInfo.pixelStride, | |
| iy1, rasInfo.scanStride); | |
| if (dy1 == 0 && dx2 == 0) { | |
| if (dx1 < 0) { | |
| // We sorted by Y above, but not by X | |
| x0 += dx1; | |
| dx1 = -dx1; | |
| } | |
| fillAARect(pPrim, &rasInfo, &compInfo, | |
| color, pMask, pDst, | |
| x0, y0, x0+dx1, y0+dy2); | |
| } else if (dx1 == 0 && dy2 == 0) { | |
| if (dx2 < 0) { | |
| // We sorted by Y above, but not by X | |
| x0 += dx2; | |
| dx2 = -dx2; | |
| } | |
| fillAARect(pPrim, &rasInfo, &compInfo, | |
| color, pMask, pDst, | |
| x0, y0, x0+dx2, y0+dy1); | |
| } else { | |
| fillAAPgram(pPrim, &rasInfo, &compInfo, | |
| color, pMask, pDst, | |
| x0, y0, dx1, dy1, dx2, dy2); | |
| } | |
| } | |
| SurfaceData_InvokeRelease(env, sdOps, &rasInfo); | |
| if (pMask != NULL && pMask != localmask) { | |
| free(pMask); | |
| } | |
| } | |
| SurfaceData_InvokeUnlock(env, sdOps, &rasInfo); | |
| } | |
| /* Main function to fill a double pair of (inner and outer) parallelograms */ | |
| static void | |
| drawAAPgram(NativePrimitive *pPrim, SurfaceDataRasInfo *pRasInfo, | |
| CompositeInfo *pCompInfo, jint color, unsigned char *pMask, | |
| void *pDst, | |
| jdouble ox0, jdouble oy0, | |
| jdouble dx1, jdouble dy1, | |
| jdouble dx2, jdouble dy2, | |
| jdouble ldx1, jdouble ldy1, | |
| jdouble ldx2, jdouble ldy2) | |
| { | |
| jint cx1 = pRasInfo->bounds.x1; | |
| jint cy1 = pRasInfo->bounds.y1; | |
| jint cx2 = pRasInfo->bounds.x2; | |
| jint cy2 = pRasInfo->bounds.y2; | |
| jint width = cx2 - cx1; | |
| EdgeInfo edges[8]; | |
| jfloat localaccum[MASK_BUF_LEN + 2]; | |
| jfloat *pAccum; | |
| if (!storePgram(edges + 0, edges + 6, | |
| ox0, oy0, | |
| dx1 + ldx1, dy1 + ldy1, | |
| dx2 + ldx2, dy2 + ldy2, | |
| cx1, cy1, cx2, cy2, | |
| JNI_FALSE)) | |
| { | |
| /* If outer pgram does not contribute, then inner cannot either. */ | |
| return; | |
| } | |
| storePgram(edges + 2, edges + 4, | |
| ox0 + ldx1 + ldx2, oy0 + ldy1 + ldy2, | |
| dx1 - ldx1, dy1 - ldy1, | |
| dx2 - ldx2, dy2 - ldy2, | |
| cx1, cy1, cx2, cy2, | |
| JNI_TRUE); | |
| pAccum = ((width > MASK_BUF_LEN) | |
| ? malloc((width + 2) * sizeof(jfloat)) | |
| : localaccum); | |
| if (pAccum == NULL) { | |
| return; | |
| } | |
| memset(pAccum, 0, (width + 2) * sizeof(jfloat)); | |
| while (cy1 < cy2) { | |
| jint lmin, lmax, rmin, rmax; | |
| jint moff, x; | |
| jdouble accum; | |
| unsigned char lastcov; | |
| lmin = rmin = width + 2; | |
| lmax = rmax = 0; | |
| ACCUM_EDGE(&edges[0], pAccum, lmin, lmax, | |
| cx1, cy1, cx2, cy1+1); | |
| ACCUM_EDGE(&edges[1], pAccum, lmin, lmax, | |
| cx1, cy1, cx2, cy1+1); | |
| ACCUM_EDGE(&edges[2], pAccum, lmin, lmax, | |
| cx1, cy1, cx2, cy1+1); | |
| ACCUM_EDGE(&edges[3], pAccum, lmin, lmax, | |
| cx1, cy1, cx2, cy1+1); | |
| ACCUM_EDGE(&edges[4], pAccum, rmin, rmax, | |
| cx1, cy1, cx2, cy1+1); | |
| ACCUM_EDGE(&edges[5], pAccum, rmin, rmax, | |
| cx1, cy1, cx2, cy1+1); | |
| ACCUM_EDGE(&edges[6], pAccum, rmin, rmax, | |
| cx1, cy1, cx2, cy1+1); | |
| ACCUM_EDGE(&edges[7], pAccum, rmin, rmax, | |
| cx1, cy1, cx2, cy1+1); | |
| if (lmax > width) { | |
| lmax = width; /* Extra col has data we do not need. */ | |
| } | |
| if (rmax > width) { | |
| rmax = width; /* Extra col has data we do not need. */ | |
| } | |
| /* If ranges overlap, handle both in the first pass. */ | |
| if (rmin <= lmax) { | |
| lmax = rmax; | |
| } | |
| x = lmin; | |
| accum = 0.0; | |
| moff = 0; | |
| lastcov = 0; | |
| while (x < lmax) { | |
| accum += pAccum[x]; | |
| pAccum[x] = 0.0f; | |
| pMask[moff++] = lastcov = DblToMask(accum); | |
| x++; | |
| } | |
| /* Check for an empty or solidcenter section. */ | |
| if (lastcov == 0 || lastcov == 0xFF) { | |
| jint endx; | |
| void *pRow; | |
| /* First process the existing partial coverage data. */ | |
| if (moff > 0) { | |
| pRow = PtrCoord(pDst, x-moff, pRasInfo->pixelStride, 0, 0); | |
| (*pPrim->funcs.maskfill)(pRow, | |
| pMask, 0, 0, | |
| moff, 1, | |
| color, pRasInfo, | |
| pPrim, pCompInfo); | |
| moff = 0; | |
| } | |
| /* Where does the center section end? */ | |
| /* If there is no right AA edge in the accum buffer, then */ | |
| /* the right edge was beyond the clip, so fill out to width */ | |
| endx = (rmin < rmax) ? rmin : width; | |
| if (x < endx) { | |
| if (lastcov == 0xFF) { | |
| pRow = PtrCoord(pDst, x, pRasInfo->pixelStride, 0, 0); | |
| (*pPrim->funcs.maskfill)(pRow, | |
| NULL, 0, 0, | |
| endx - x, 1, | |
| color, pRasInfo, | |
| pPrim, pCompInfo); | |
| } | |
| x = endx; | |
| } | |
| } else if (rmin >= rmax) { | |
| /* We are not at 0 coverage, but there is no right edge, */ | |
| /* force a right edge so we process pixels out to width. */ | |
| rmax = width; | |
| } | |
| /* The following loop will process the right AA edge and/or any */ | |
| /* partial coverage center section not processed above. */ | |
| while (x < rmax) { | |
| accum += pAccum[x]; | |
| pAccum[x] = 0.0f; | |
| pMask[moff++] = lastcov = DblToMask(accum); | |
| x++; | |
| } | |
| if (moff > 0) { | |
| void *pRow = PtrCoord(pDst, x-moff, pRasInfo->pixelStride, 0, 0); | |
| (*pPrim->funcs.maskfill)(pRow, | |
| pMask, 0, 0, | |
| moff, 1, | |
| color, pRasInfo, | |
| pPrim, pCompInfo); | |
| } | |
| if (lastcov == 0xFF && x < width) { | |
| void *pRow = PtrCoord(pDst, x, pRasInfo->pixelStride, 0, 0); | |
| (*pPrim->funcs.maskfill)(pRow, | |
| NULL, 0, 0, | |
| width - x, 1, | |
| color, pRasInfo, | |
| pPrim, pCompInfo); | |
| } | |
| pDst = PtrAddBytes(pDst, pRasInfo->scanStride); | |
| cy1++; | |
| } | |
| if (pAccum != localaccum) { | |
| free(pAccum); | |
| } | |
| } | |
| /* | |
| * Class: sun_java2d_loops_MaskFill | |
| * Method: DrawAAPgram | |
| * Signature: (Lsun/java2d/SunGraphics2D;Lsun/java2d/SurfaceData;Ljava/awt/Composite;DDDDDDDD)V | |
| */ | |
| JNIEXPORT void JNICALL | |
| Java_sun_java2d_loops_MaskFill_DrawAAPgram | |
| (JNIEnv *env, jobject self, | |
| jobject sg2d, jobject sData, jobject comp, | |
| jdouble x0, jdouble y0, | |
| jdouble dx1, jdouble dy1, | |
| jdouble dx2, jdouble dy2, | |
| jdouble lw1, jdouble lw2) | |
| { | |
| SurfaceDataOps *sdOps; | |
| SurfaceDataRasInfo rasInfo; | |
| NativePrimitive *pPrim; | |
| CompositeInfo compInfo; | |
| jint ix1, iy1, ix2, iy2; | |
| jdouble ldx1, ldy1, ldx2, ldy2; | |
| jdouble ox0, oy0; | |
| if ((dy1 == 0 && dx1 == 0) || (dy2 == 0 && dx2 == 0)) { | |
| return; | |
| } | |
| /* | |
| * Sort parallelogram by y values, ensure that each delta vector | |
| * has a non-negative y delta. | |
| */ | |
| SORT_PGRAM(x0, y0, dx1, dy1, dx2, dy2, | |
| v = lw1; lw1 = lw2; lw2 = v;); | |
| // dx,dy for line width in the "1" and "2" directions. | |
| ldx1 = dx1 * lw1; | |
| ldy1 = dy1 * lw1; | |
| ldx2 = dx2 * lw2; | |
| ldy2 = dy2 * lw2; | |
| // calculate origin of the outer parallelogram | |
| ox0 = x0 - (ldx1 + ldx2) / 2.0; | |
| oy0 = y0 - (ldy1 + ldy2) / 2.0; | |
| if (lw1 >= 1.0 || lw2 >= 1.0) { | |
| /* Only need to fill an outer pgram if the interior no longer | |
| * has a hole in it (i.e. if either of the line width ratios | |
| * were greater than or equal to 1.0). | |
| */ | |
| Java_sun_java2d_loops_MaskFill_FillAAPgram(env, self, | |
| sg2d, sData, comp, | |
| ox0, oy0, | |
| dx1 + ldx1, dy1 + ldy1, | |
| dx2 + ldx2, dy2 + ldy2); | |
| return; | |
| } | |
| PGRAM_MIN_MAX(ix1, ix2, ox0, dx1+ldx1, dx2+ldx2, JNI_TRUE); | |
| iy1 = (jint) floor(oy0); | |
| iy2 = (jint) ceil(oy0 + dy1 + ldy1 + dy2 + ldy2); | |
| pPrim = GetNativePrim(env, self); | |
| if (pPrim == NULL) { | |
| return; | |
| } | |
| if (pPrim->pCompType->getCompInfo != NULL) { | |
| (*pPrim->pCompType->getCompInfo)(env, &compInfo, comp); | |
| } | |
| sdOps = SurfaceData_GetOps(env, sData); | |
| if (sdOps == 0) { | |
| return; | |
| } | |
| GrPrim_Sg2dGetClip(env, sg2d, &rasInfo.bounds); | |
| SurfaceData_IntersectBoundsXYXY(&rasInfo.bounds, ix1, iy1, ix2, iy2); | |
| if (rasInfo.bounds.y2 <= rasInfo.bounds.y1 || | |
| rasInfo.bounds.x2 <= rasInfo.bounds.x1) | |
| { | |
| return; | |
| } | |
| if (sdOps->Lock(env, sdOps, &rasInfo, pPrim->dstflags) != SD_SUCCESS) { | |
| return; | |
| } | |
| ix1 = rasInfo.bounds.x1; | |
| iy1 = rasInfo.bounds.y1; | |
| ix2 = rasInfo.bounds.x2; | |
| iy2 = rasInfo.bounds.y2; | |
| if (ix2 > ix1 && iy2 > iy1) { | |
| jint width = ix2 - ix1; | |
| jint color = GrPrim_Sg2dGetEaRGB(env, sg2d); | |
| unsigned char localmask[MASK_BUF_LEN]; | |
| unsigned char *pMask = ((width > MASK_BUF_LEN) | |
| ? malloc(width) | |
| : localmask); | |
| sdOps->GetRasInfo(env, sdOps, &rasInfo); | |
| if (rasInfo.rasBase != NULL && pMask != NULL) { | |
| void *pDst = PtrCoord(rasInfo.rasBase, | |
| ix1, rasInfo.pixelStride, | |
| iy1, rasInfo.scanStride); | |
| /* | |
| * NOTE: aligned rects could probably be drawn | |
| * even faster with a little work here. | |
| * if (dy1 == 0 && dx2 == 0) { | |
| * drawAARect(pPrim, &rasInfo, &compInfo, | |
| * color, pMask, pDst, | |
| * ox0, oy0, ox0+dx1+ldx1, oy0+dy2+ldy2, ldx1, ldy2); | |
| * } else if (dx1 == 0 && dy2 == 0) { | |
| * drawAARect(pPrim, &rasInfo, &compInfo, | |
| * color, pMask, pDst, | |
| * ox0, oy0, ox0+dx2+ldx2, oy0+dy1+ldy1, ldx2, ldy1); | |
| * } else { | |
| */ | |
| drawAAPgram(pPrim, &rasInfo, &compInfo, | |
| color, pMask, pDst, | |
| ox0, oy0, | |
| dx1, dy1, dx2, dy2, | |
| ldx1, ldy1, ldx2, ldy2); | |
| /* | |
| * } | |
| */ | |
| } | |
| SurfaceData_InvokeRelease(env, sdOps, &rasInfo); | |
| if (pMask != NULL && pMask != localmask) { | |
| free(pMask); | |
| } | |
| } | |
| SurfaceData_InvokeUnlock(env, sdOps, &rasInfo); | |
| } |