| /* |
| * Copyright (c) 2000, 2013, 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 + 1]; |
| 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 + 1) * sizeof(jfloat)) |
| : localaccum); |
| if (pAccum == NULL) { |
| return; |
| } |
| memset(pAccum, 0, (width+1) * 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 + 1]; |
| 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 + 1) * sizeof(jfloat)) |
| : localaccum); |
| if (pAccum == NULL) { |
| return; |
| } |
| memset(pAccum, 0, (width+1) * 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); |
| } |