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 "Trace.h"
30 #include "ParallelogramUtils.h"
31 
32 #include "sun_java2d_loops_DrawParallelogram.h"
33 
34 #define HANDLE_PGRAM_EDGE(X1, Y1, X2, Y2, \
35                           pRasInfo, pixel, pPrim, pFunc, pCompInfo) \
36     do { \
37          jint ix1 = (jint) floor(X1); \
38          jint ix2 = (jint) floor(X2); \
39          jint iy1 = (jint) floor(Y1); \
40          jint iy2 = (jint) floor(Y2); \
41          LineUtils_ProcessLine(pRasInfo, pixel, \
42                                pFunc, pPrim, pCompInfo, \
43                                ix1, iy1, ix2, iy2, JNI_TRUE); \
44     } while (0)
45 
46 typedef struct {
47     jdouble x0;
48     jdouble y0;
49     jdouble y1;
50     jdouble slope;
51     jlong dx;
52     jint ystart;
53     jint yend;
54 } EdgeInfo;
55 
56 #define STORE_EDGE(pEDGE, X0, Y0, Y1, SLOPE, DELTAX) \
57     do { \
58         (pEDGE)->x0 = (X0); \
59         (pEDGE)->y0 = (Y0); \
60         (pEDGE)->y1 = (Y1); \
61         (pEDGE)->slope = (SLOPE); \
62         (pEDGE)->dx = (DELTAX); \
63         (pEDGE)->ystart = (jint) floor((Y0) + 0.5); \
64         (pEDGE)->yend   = (jint) floor((Y1) + 0.5); \
65     } while (0)
66 
67 #define STORE_PGRAM(pLTEDGE, pRTEDGE, \
68                     X0, Y0, dX1, dY1, dX2, dY2, \
69                     SLOPE1, SLOPE2, DELTAX1, DELTAX2) \
70     do { \
71         STORE_EDGE((pLTEDGE)+0, \
72                    (X0), (Y0), (Y0) + (dY1), \
73                    (SLOPE1), (DELTAX1)); \
74         STORE_EDGE((pRTEDGE)+0, \
75                    (X0), (Y0), (Y0) + (dY2), \
76                    (SLOPE2), (DELTAX2)); \
77         STORE_EDGE((pLTEDGE)+1, \
78                    (X0) + (dX1), (Y0) + (dY1), (Y0) + (dY1) + (dY2), \
79                    (SLOPE2), (DELTAX2)); \
80         STORE_EDGE((pRTEDGE)+1, \
81                    (X0) + (dX2), (Y0) + (dY2), (Y0) + (dY1) + (dY2), \
82                    (SLOPE1), (DELTAX1)); \
83     } while (0)
84 
85 /*
86  * Class:     sun_java2d_loops_DrawParallelogram
87  * Method:    DrawParallelogram
88  * Signature: (Lsun/java2d/SunGraphics2D;Lsun/java2d/SurfaceData;DDDDDDDD)V
89  */
90 JNIEXPORT void JNICALL
Java_sun_java2d_loops_DrawParallelogram_DrawParallelogram(JNIEnv * env,jobject self,jobject sg2d,jobject sData,jdouble x0,jdouble y0,jdouble dx1,jdouble dy1,jdouble dx2,jdouble dy2,jdouble lw1,jdouble lw2)91 Java_sun_java2d_loops_DrawParallelogram_DrawParallelogram
92     (JNIEnv *env, jobject self,
93      jobject sg2d, jobject sData,
94      jdouble x0, jdouble y0,
95      jdouble dx1, jdouble dy1,
96      jdouble dx2, jdouble dy2,
97      jdouble lw1, jdouble lw2)
98 {
99     SurfaceDataOps *sdOps;
100     SurfaceDataRasInfo rasInfo;
101     NativePrimitive *pPrim;
102     CompositeInfo compInfo;
103     jint pixel;
104     EdgeInfo edges[8];
105     EdgeInfo *active[4];
106     jint ix1, iy1, ix2, iy2;
107     jdouble ldx1, ldy1, ldx2, ldy2;
108     jdouble ox0, oy0;
109 
110     /*
111      * Sort parallelogram by y values, ensure that each delta vector
112      * has a non-negative y delta.
113      */
114     SORT_PGRAM(x0, y0, dx1, dy1, dx2, dy2,
115                v = lw1; lw1 = lw2; lw2 = v;);
116 
117     // dx,dy for line width in the "1" and "2" directions.
118     ldx1 = dx1 * lw1;
119     ldy1 = dy1 * lw1;
120     ldx2 = dx2 * lw2;
121     ldy2 = dy2 * lw2;
122 
123     // calculate origin of the outer parallelogram
124     ox0 = x0 - (ldx1 + ldx2) / 2.0;
125     oy0 = y0 - (ldy1 + ldy2) / 2.0;
126 
127     PGRAM_MIN_MAX(ix1, ix2, ox0, dx1+ldx1, dx2+ldx2, JNI_FALSE);
128     iy1 = (jint) floor(oy0 + 0.5);
129     iy2 = (jint) floor(oy0 + dy1 + ldy1 + dy2 + ldy2 + 0.5);
130 
131     pPrim = GetNativePrim(env, self);
132     if (pPrim == NULL) {
133         return;
134     }
135     pixel = GrPrim_Sg2dGetPixel(env, sg2d);
136     if (pPrim->pCompType->getCompInfo != NULL) {
137         GrPrim_Sg2dGetCompInfo(env, sg2d, pPrim, &compInfo);
138     }
139 
140     sdOps = SurfaceData_GetOps(env, sData);
141     if (sdOps == NULL) {
142         return;
143     }
144 
145     GrPrim_Sg2dGetClip(env, sg2d, &rasInfo.bounds);
146     SurfaceData_IntersectBoundsXYXY(&rasInfo.bounds, ix1, iy1, ix2, iy2);
147     if (rasInfo.bounds.y2 <= rasInfo.bounds.y1 ||
148         rasInfo.bounds.x2 <= rasInfo.bounds.x1)
149     {
150         return;
151     }
152 
153     if (sdOps->Lock(env, sdOps, &rasInfo, pPrim->dstflags) != SD_SUCCESS) {
154         return;
155     }
156 
157     ix1 = rasInfo.bounds.x1;
158     iy1 = rasInfo.bounds.y1;
159     ix2 = rasInfo.bounds.x2;
160     iy2 = rasInfo.bounds.y2;
161     if (ix2 > ix1 && iy2 > iy1) {
162         sdOps->GetRasInfo(env, sdOps, &rasInfo);
163         if (rasInfo.rasBase) {
164             jdouble lslope, rslope;
165             jlong ldx, rdx;
166             jint loy, hiy, numedges;
167             FillParallelogramFunc *pFill =
168                 pPrim->funcs.drawparallelogram->fillpgram;
169 
170             lslope = (dy1 == 0) ? 0 : dx1 / dy1;
171             rslope = (dy2 == 0) ? 0 : dx2 / dy2;
172             ldx = DblToLong(lslope);
173             rdx = DblToLong(rslope);
174 
175             // Only need to generate 4 quads if the interior still
176             // has a hole in it (i.e. if the line width ratios were
177             // both less than 1.0)
178             if (lw1 < 1.0 && lw2 < 1.0) {
179                 // If the line widths are both less than a pixel wide
180                 // then we can use a drawline function instead for even
181                 // more performance.
182                 lw1 = sqrt(ldx1*ldx1 + ldy1*ldy1);
183                 lw2 = sqrt(ldx2*ldx2 + ldy2*ldy2);
184                 if (lw1 <= 1.0001 && lw2 <= 1.0001) {
185                     jdouble x3, y3;
186                     DrawLineFunc *pLine =
187                         pPrim->funcs.drawparallelogram->drawline;
188 
189                     x3 = (dx1 += x0);
190                     y3 = (dy1 += y0);
191                     x3 += dx2;
192                     y3 += dy2;
193                     dx2 += x0;
194                     dy2 += y0;
195 
196                     HANDLE_PGRAM_EDGE( x0,  y0, dx1, dy1,
197                                       &rasInfo, pixel, pPrim, pLine, &compInfo);
198                     HANDLE_PGRAM_EDGE(dx1, dy1,  x3,  y3,
199                                       &rasInfo, pixel, pPrim, pLine, &compInfo);
200                     HANDLE_PGRAM_EDGE( x3,  y3, dx2, dy2,
201                                       &rasInfo, pixel, pPrim, pLine, &compInfo);
202                     HANDLE_PGRAM_EDGE(dx2, dy2,  x0,  y0,
203                                       &rasInfo, pixel, pPrim, pLine, &compInfo);
204                     SurfaceData_InvokeRelease(env, sdOps, &rasInfo);
205                     SurfaceData_InvokeUnlock(env, sdOps, &rasInfo);
206                     return;
207                 }
208 
209                 // To simplify the edge management below we presort the
210                 // inner and outer edges so that they are globally sorted
211                 // from left to right.  If you scan across the array of
212                 // edges for a given Y range then the edges you encounter
213                 // will be sorted in X as well.
214                 // If AB are left top and bottom edges of outer parallelogram,
215                 // and CD are the right pair of edges, and abcd are the
216                 // corresponding inner parallelogram edges then we want them
217                 // sorted as ABabcdCD to ensure this horizontal ordering.
218                 // Conceptually it is like 2 pairs of nested parentheses.
219                 STORE_PGRAM(edges + 2, edges + 4,
220                             ox0 + ldx1 + ldx2, oy0 + ldy1 + ldy2,
221                             dx1 - ldx1, dy1 - ldy1,
222                             dx2 - ldx2, dy2 - ldy2,
223                             lslope, rslope, ldx, rdx);
224                 numedges = 8;
225             } else {
226                 // The line width ratios were large enough to consume
227                 // the entire hole in the middle of the parallelogram
228                 // so we can just issue one large quad for the outer
229                 // parallelogram.
230                 numedges = 4;
231             }
232 
233             // The outer parallelogram always goes in the first two
234             // and last two entries in the array so we either have
235             // ABabcdCD ordering for 8 edges or ABCD ordering for 4
236             // edges.  See comment above where we store the inner
237             // parallelogram for a more complete description.
238             STORE_PGRAM(edges + 0, edges + numedges-2,
239                         ox0, oy0,
240                         dx1 + ldx1, dy1 + ldy1,
241                         dx2 + ldx2, dy2 + ldy2,
242                         lslope, rslope, ldx, rdx);
243 
244             loy = edges[0].ystart;
245             if (loy < iy1) loy = iy1;
246             while (loy < iy2) {
247                 jint numactive = 0;
248                 jint cur;
249 
250                 hiy = iy2;
251                 // Maintaining a sorted edge list is probably overkill for
252                 // 4 or 8 edges.  The indices chosen above for storing the
253                 // inner and outer left and right edges already guarantee
254                 // left to right ordering so we just need to scan for edges
255                 // that overlap the current Y range (and also determine the
256                 // maximum Y value for which the range is valid).
257                 for (cur = 0; cur < numedges; cur++) {
258                     EdgeInfo *pEdge = &edges[cur];
259                     jint yend = pEdge->yend;
260                     if (loy < yend) {
261                         // This edge is still in play, have we reached it yet?
262                         jint ystart = pEdge->ystart;
263                         if (loy < ystart) {
264                             // This edge is not active (yet)
265                             // Stop before we get to the top of it
266                             if (hiy > ystart) hiy = ystart;
267                         } else {
268                             // This edge is active, store it
269                             active[numactive++] = pEdge;
270                             // And stop when we get to the bottom of it
271                             if (hiy > yend) hiy = yend;
272                         }
273                     }
274                 }
275 #ifdef DEBUG
276                 if ((numactive & 1) != 0) {
277                     J2dTraceLn1(J2D_TRACE_ERROR,
278                                 "DrawParallelogram: "
279                                 "ODD NUMBER OF PGRAM EDGES (%d)!!",
280                                 numactive);
281                 }
282 #endif
283                 for (cur = 0; cur < numactive; cur += 2) {
284                     EdgeInfo *pLeft  = active[cur+0];
285                     EdgeInfo *pRight = active[cur+1];
286                     jlong lx = PGRAM_INIT_X(loy,
287                                             pLeft->x0, pLeft->y0,
288                                             pLeft->slope);
289                     jlong rx = PGRAM_INIT_X(loy,
290                                             pRight->x0, pRight->y0,
291                                             pRight->slope);
292                     (*pFill)(&rasInfo,
293                              ix1, loy, ix2, hiy,
294                              lx, pLeft->dx,
295                              rx, pRight->dx,
296                              pixel, pPrim, &compInfo);
297                 }
298                 loy = hiy;
299             }
300         }
301         SurfaceData_InvokeRelease(env, sdOps, &rasInfo);
302     }
303     SurfaceData_InvokeUnlock(env, sdOps, &rasInfo);
304 }
305