1 /*
   2  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #include "math.h"
  27 #include "GraphicsPrimitiveMgr.h"
  28 #include "LineUtils.h"
  29 #include "LoopMacros.h"
  30 #include "Trace.h"
  31 
  32 #include "sun_java2d_loops_FillParallelogram.h"
  33 #include "sun_java2d_loops_DrawParallelogram.h"
  34 
  35 DECLARE_SOLID_DRAWLINE(AnyInt);
  36 
  37 #define HANDLE_PGRAM_EDGE(X1, Y1, X2, Y2, \
  38                           pRasInfo, pixel, pPrim, pFunc, pCompInfo) \
  39     do { \
  40          jint ix1 = (jint) floor(X1); \
  41          jint ix2 = (jint) floor(X2); \
  42          jint iy1 = (jint) floor(Y1); \
  43          jint iy2 = (jint) floor(Y2); \
  44          LineUtils_ProcessLine(pRasInfo, pixel, \
  45                                pFunc, pPrim, pCompInfo, \
  46                                ix1, iy1, ix2, iy2, JNI_TRUE); \
  47     } while (0)
  48 
  49 #define PGRAM_MIN_MAX(bmin, bmax, v0, dv1, dv2) \
  50     do { \
  51         double vmin, vmax; \
  52         if (dv1 < 0) { \
  53             vmin = v0+dv1; \
  54             vmax = v0; \
  55         } else { \
  56             vmin = v0; \
  57             vmax = v0+dv1; \
  58         } \
  59         if (dv2 < 0) { \
  60             vmin -= dv2; \
  61         } else { \
  62             vmax += dv2; \
  63         } \
  64         bmin = (jint) floor(vmin + 0.5); \
  65         bmax = (jint) floor(vmax + 0.5); \
  66     } while(0)
  67 
  68 #define PGRAM_INIT_X(starty, x, y, slope) \
  69     (DblToLong((x) + (slope) * ((starty)+0.5 - (y))) + LongOneHalf - 1)
  70 
  71 typedef struct {
  72     jdouble x0;
  73     jdouble y0;
  74     jdouble y1;
  75     jdouble slope;
  76     jlong dx;
  77     jint ystart;
  78     jint yend;
  79 } EdgeInfo;
  80 
  81 #define STORE_EDGE(pEDGE, X0, Y0, Y1, SLOPE, DELTAX) \
  82     do { \
  83         (pEDGE)->x0 = (X0); \
  84         (pEDGE)->y0 = (Y0); \
  85         (pEDGE)->y1 = (Y1); \
  86         (pEDGE)->slope = (SLOPE); \
  87         (pEDGE)->dx = (DELTAX); \
  88         (pEDGE)->ystart = (jint) floor((Y0) + 0.5); \
  89         (pEDGE)->yend   = (jint) floor((Y1) + 0.5); \
  90     } while (0)
  91 
  92 #define STORE_PGRAM(pLTEDGE, pRTEDGE, \
  93                     X0, Y0, dX1, dY1, dX2, dY2, \
  94                     SLOPE1, SLOPE2, DELTAX1, DELTAX2) \
  95     do { \
  96         STORE_EDGE((pLTEDGE)+0, \
  97                    (X0), (Y0), (Y0) + (dY1), \
  98                    (SLOPE1), (DELTAX1)); \
  99         STORE_EDGE((pRTEDGE)+0, \
 100                    (X0), (Y0), (Y0) + (dY2), \
 101                    (SLOPE2), (DELTAX2)); \
 102         STORE_EDGE((pLTEDGE)+1, \
 103                    (X0) + (dX1), (Y0) + (dY1), (Y0) + (dY1) + (dY2), \
 104                    (SLOPE2), (DELTAX2)); \
 105         STORE_EDGE((pRTEDGE)+1, \
 106                    (X0) + (dX2), (Y0) + (dY2), (Y0) + (dY1) + (dY2), \
 107                    (SLOPE1), (DELTAX1)); \
 108     } while (0)
 109 
 110 /*
 111  * Class:     sun_java2d_loops_DrawParallelogram
 112  * Method:    DrawParallelogram
 113  * Signature: (Lsun/java2d/SunGraphics2D;Lsun/java2d/SurfaceData;DDDDDDDD)V
 114  */
 115 JNIEXPORT void JNICALL
 116 Java_sun_java2d_loops_DrawParallelogram_DrawParallelogram
 117     (JNIEnv *env, jobject self,
 118      jobject sg2d, jobject sData,
 119      jdouble x0, jdouble y0,
 120      jdouble dx1, jdouble dy1,
 121      jdouble dx2, jdouble dy2,
 122      jdouble lw1, jdouble lw2)
 123 {
 124     SurfaceDataOps *sdOps;
 125     SurfaceDataRasInfo rasInfo;
 126     NativePrimitive *pPrim;
 127     CompositeInfo compInfo;
 128     jint pixel;
 129     EdgeInfo edges[8];
 130     EdgeInfo *active[4];
 131     jint ix1, iy1, ix2, iy2;
 132     jdouble ldx1, ldy1, ldx2, ldy2;
 133     jdouble ox0, oy0;
 134 
 135     /*
 136      * Sort parallelogram by y values, ensure that each delta vector
 137      * has a non-negative y delta.
 138      */
 139     if (dy1 < 0) {
 140         x0 += dx1;  y0 += dy1;
 141         dx1 = -dx1; dy1 = -dy1;
 142     }
 143     if (dy2 < 0) {
 144         x0 += dx2;  y0 += dy2;
 145         dx2 = -dx2; dy2 = -dy2;
 146     }
 147     /* Sort delta vectors so dxy1 is left of dxy2. */
 148     if (dx1 * dy2 > dx2 * dy1) {
 149         double v = dx1; dx1 = dx2; dx2 = v;
 150                v = dy1; dy1 = dy2; dy2 = v;
 151                v = lw1; lw1 = lw2; lw2 = v;
 152     }
 153 
 154     // dx,dy for line width in the "1" and "2" directions.
 155     ldx1 = dx1 * lw1;
 156     ldy1 = dy1 * lw1;
 157     ldx2 = dx2 * lw2;
 158     ldy2 = dy2 * lw2;
 159 
 160     // calculate origin of the outer parallelogram
 161     ox0 = x0 - (ldx1 + ldx2) / 2.0;
 162     oy0 = y0 - (ldy1 + ldy2) / 2.0;
 163 
 164     PGRAM_MIN_MAX(ix1, ix2, ox0, dx1+ldx1, dx2+ldx2);
 165     iy1 = (jint) floor(oy0 + 0.5);
 166     iy2 = (jint) floor(oy0 + dy1 + ldy1 + dy2 + ldy2 + 0.5);
 167 
 168     pPrim = GetNativePrim(env, self);
 169     if (pPrim == NULL) {
 170         return;
 171     }
 172     pixel = GrPrim_Sg2dGetPixel(env, sg2d);
 173     if (pPrim->pCompType->getCompInfo != NULL) {
 174         GrPrim_Sg2dGetCompInfo(env, sg2d, pPrim, &compInfo);
 175     }
 176 
 177     sdOps = SurfaceData_GetOps(env, sData);
 178     if (sdOps == NULL) {
 179         return;
 180     }
 181 
 182     GrPrim_Sg2dGetClip(env, sg2d, &rasInfo.bounds);
 183     SurfaceData_IntersectBoundsXYXY(&rasInfo.bounds, ix1, iy1, ix2, iy2);
 184     if (rasInfo.bounds.y2 <= rasInfo.bounds.y1 ||
 185         rasInfo.bounds.x2 <= rasInfo.bounds.x1)
 186     {
 187         return;
 188     }
 189 
 190     if (sdOps->Lock(env, sdOps, &rasInfo, pPrim->dstflags) != SD_SUCCESS) {
 191         return;
 192     }
 193 
 194     ix1 = rasInfo.bounds.x1;
 195     iy1 = rasInfo.bounds.y1;
 196     ix2 = rasInfo.bounds.x2;
 197     iy2 = rasInfo.bounds.y2;
 198     if (ix2 > ix1 && iy2 > iy1) {
 199         sdOps->GetRasInfo(env, sdOps, &rasInfo);
 200         if (rasInfo.rasBase) {
 201             jdouble lslope, rslope;
 202             jlong ldx, rdx;
 203             jint loy, hiy, numedges;
 204             FillParallelogramFunc *pFill =
 205                 pPrim->funcs.drawparallelogram->fillpgram;
 206 
 207             lslope = (dy1 == 0) ? 0 : dx1 / dy1;
 208             rslope = (dy2 == 0) ? 0 : dx2 / dy2;
 209             ldx = DblToLong(lslope);
 210             rdx = DblToLong(rslope);
 211 
 212             // Only need to generate 4 quads if the interior still
 213             // has a hole in it (i.e. if the line width ratios were
 214             // both less than 1.0)
 215             if (lw1 < 1.0f && lw2 < 1.0f) {
 216                 // If the line widths are both less than a pixel wide
 217                 // then we can use a drawline function instead for even
 218                 // more performance.
 219                 lw1 = sqrt(ldx1*ldx1 + ldy1*ldy1);
 220                 lw2 = sqrt(ldx2*ldx2 + ldy2*ldy2);
 221                 if (lw1 <= 1.0001 && lw2 <= 1.0001) {
 222                     jdouble x3, y3;
 223                     DrawLineFunc *pLine =
 224                         pPrim->funcs.drawparallelogram->drawline;
 225 
 226                     x3 = (dx1 += x0);
 227                     y3 = (dy1 += y0);
 228                     x3 += dx2;
 229                     y3 += dy2;
 230                     dx2 += x0;
 231                     dy2 += y0;
 232 
 233                     HANDLE_PGRAM_EDGE( x0,  y0, dx1, dy1,
 234                                       &rasInfo, pixel, pPrim, pLine, &compInfo);
 235                     HANDLE_PGRAM_EDGE(dx1, dy1,  x3,  y3,
 236                                       &rasInfo, pixel, pPrim, pLine, &compInfo);
 237                     HANDLE_PGRAM_EDGE( x3,  y3, dx2, dy2,
 238                                       &rasInfo, pixel, pPrim, pLine, &compInfo);
 239                     HANDLE_PGRAM_EDGE(dx2, dy2,  x0,  y0,
 240                                       &rasInfo, pixel, pPrim, pLine, &compInfo);
 241                     SurfaceData_InvokeRelease(env, sdOps, &rasInfo);
 242                     SurfaceData_InvokeUnlock(env, sdOps, &rasInfo);
 243                     return;
 244                 }
 245 
 246                 // To simplify the edge management below we presort the
 247                 // inner and outer edges so that they are globally sorted
 248                 // from left to right.  If you scan across the array of
 249                 // edges for a given Y range then the edges you encounter
 250                 // will be sorted in X as well.
 251                 // If AB are left top and bottom edges of outer parallelogram,
 252                 // and CD are the right pair of edges, and abcd are the
 253                 // corresponding inner parallelogram edges then we want them
 254                 // sorted as ABabcdCD to ensure this horizontal ordering.
 255                 // Conceptually it is like 2 pairs of nested parentheses.
 256                 STORE_PGRAM(edges + 2, edges + 4,
 257                             ox0 + ldx1 + ldx2, oy0 + ldy1 + ldy2,
 258                             dx1 - ldx1, dy1 - ldy1,
 259                             dx2 - ldx2, dy2 - ldy2,
 260                             lslope, rslope, ldx, rdx);
 261                 numedges = 8;
 262             } else {
 263                 // The line width ratios were large enough to consume
 264                 // the entire hole in the middle of the parallelogram
 265                 // so we can just issue one large quad for the outer
 266                 // parallelogram.
 267                 numedges = 4;
 268             }
 269 
 270             // The outer parallelogram always goes in the first two
 271             // and last two entries in the array so we either have
 272             // ABabcdCD ordering for 8 edges or ABCD ordering for 4
 273             // edges.  See comment above where we store the inner
 274             // parallelogram for a more complete description.
 275             STORE_PGRAM(edges + 0, edges + numedges-2,
 276                         ox0, oy0,
 277                         dx1 + ldx1, dy1 + ldy1,
 278                         dx2 + ldx2, dy2 + ldy2,
 279                         lslope, rslope, ldx, rdx);
 280 
 281             loy = edges[0].ystart;
 282             if (loy < iy1) loy = iy1;
 283             while (loy < iy2) {
 284                 jint numactive = 0;
 285                 jint cur;
 286 
 287                 hiy = iy2;
 288                 // Maintaining a sorted edge list is probably overkill for
 289                 // 4 or 8 edges.  The indices chosen above for storing the
 290                 // inner and outer left and right edges already guarantee
 291                 // left to right ordering so we just need to scan for edges
 292                 // that overlap the current Y range (and also determine the
 293                 // maximum Y value for which the range is valid).
 294                 for (cur = 0; cur < numedges; cur++) {
 295                     EdgeInfo *pEdge = &edges[cur];
 296                     jint yend = pEdge->yend;
 297                     if (loy < yend) {
 298                         // This edge is still in play, have we reached it yet?
 299                         jint ystart = pEdge->ystart;
 300                         if (loy < ystart) {
 301                             // This edge is not active (yet)
 302                             // Stop before we get to the top of it
 303                             if (hiy > ystart) hiy = ystart;
 304                         } else {
 305                             // This edge is active, store it
 306                             active[numactive++] = pEdge;
 307                             // And stop when we get to the bottom of it
 308                             if (hiy > yend) hiy = yend;
 309                         }
 310                     }
 311                 }
 312 #ifdef DEBUG
 313                 if ((numactive & 1) != 0) {
 314                     J2dTraceLn1(J2D_TRACE_ERROR,
 315                                 "DrawParallelogram: "
 316                                 "ODD NUMBER OF PGRAM EDGES (%d)!!",
 317                                 numactive);
 318                 }
 319 #endif
 320                 for (cur = 0; cur < numactive; cur += 2) {
 321                     EdgeInfo *pLeft  = active[cur+0];
 322                     EdgeInfo *pRight = active[cur+1];
 323                     jlong lx = PGRAM_INIT_X(loy,
 324                                             pLeft->x0, pLeft->y0,
 325                                             pLeft->slope);
 326                     jlong rx = PGRAM_INIT_X(loy,
 327                                             pRight->x0, pRight->y0,
 328                                             pRight->slope);
 329                     (*pFill)(&rasInfo,
 330                              ix1, loy, ix2, hiy,
 331                              lx, pLeft->dx,
 332                              rx, pRight->dx,
 333                              pixel, pPrim, &compInfo);
 334                 }
 335                 loy = hiy;
 336             }
 337         }
 338         SurfaceData_InvokeRelease(env, sdOps, &rasInfo);
 339     }
 340     SurfaceData_InvokeUnlock(env, sdOps, &rasInfo);
 341 }