blob: 354934069c04f4c27075bfa7a1bf46c70ac80bed [file] [log] [blame]
/*
* 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);
}