text: /*
   * 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);
  }
  