1 /*
2  * Copyright (c) 2010, 2016, 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 "X11SurfaceData.h"
27 #include <jni.h>
28 #include <math.h>
29 #include "Region.h"
30 #include "fontscalerdefs.h"
31 
32 #include <X11/extensions/Xrender.h>
33 
34 #ifdef __linux__
35     #include <sys/utsname.h>
36 #endif
37 
38 /* On Solaris 10 updates 8, 9, the render.h file defines these
39  * protocol values but does not define the structs in Xrender.h.
40  * Thus in order to get these always defined on Solaris 10
41  * we will undefine the symbols if we have determined via the
42  * makefiles that Xrender.h is lacking the structs. This will
43  * trigger providing our own definitions as on earlier updates.
44  * We could assume that *all* Solaris 10 update versions will lack the updated
45  * Xrender.h and do this based solely on O/S being any 5.10 version, but this
46  * could still change and we'd be broken again as we'd be re-defining them.
47  */
48 #ifdef SOLARIS10_NO_XRENDER_STRUCTS
49 #undef X_RenderCreateLinearGradient
50 #undef X_RenderCreateRadialGradient
51 #endif
52 
53 #ifndef X_RenderCreateLinearGradient
54 typedef struct _XLinearGradient {
55     XPointFixed p1;
56     XPointFixed p2;
57 } XLinearGradient;
58 #endif
59 
60 #ifndef X_RenderCreateRadialGradient
61 typedef struct _XCircle {
62     XFixed x;
63     XFixed y;
64     XFixed radius;
65 } XCircle;
66 
67 typedef struct _XRadialGradient {
68     XCircle inner;
69     XCircle outer;
70 } XRadialGradient;
71 #endif
72 
73 #include <dlfcn.h>
74 
75 #if defined(__solaris__)
76 /* Solaris 10 will not have these symbols at compile time */
77 
78 typedef Picture (*XRenderCreateLinearGradientFuncType)
79                                      (Display *dpy,
80                                      const XLinearGradient *gradient,
81                                      const XFixed *stops,
82                                      const XRenderColor *colors,
83                                      int nstops);
84 
85 typedef Picture (*XRenderCreateRadialGradientFuncType)
86                                      (Display *dpy,
87                                      const XRadialGradient *gradient,
88                                      const XFixed *stops,
89                                      const XRenderColor *colors,
90                                      int nstops);
91 
92 static
93 XRenderCreateLinearGradientFuncType XRenderCreateLinearGradientFunc = NULL;
94 static
95  XRenderCreateRadialGradientFuncType XRenderCreateRadialGradientFunc = NULL;
96 #endif
97 
98 #define BUILD_TRANSFORM_MATRIX(TRANSFORM, M00, M01, M02, M10, M11, M12)                        \
99     {                                                                                          \
100       TRANSFORM.matrix[0][0] = M00;                                                            \
101       TRANSFORM.matrix[0][1] = M01;                                                            \
102       TRANSFORM.matrix[0][2] = M02;                                                            \
103       TRANSFORM.matrix[1][0] = M10;                                                            \
104       TRANSFORM.matrix[1][1] = M11;                                                            \
105       TRANSFORM.matrix[1][2] = M12;                                                            \
106       TRANSFORM.matrix[2][0] = 0;                                                              \
107       TRANSFORM.matrix[2][1] = 0;                                                              \
108       TRANSFORM.matrix[2][2] = 1<<16;                                                          \
109     }
110 
111 /* The xrender pipleine requires libXrender.so version 0.9.3 or later. */
112 #define REQUIRED_XRENDER_VER1 0
113 #define REQUIRED_XRENDER_VER2 9
114 #define REQUIRED_XRENDER_VER3 3
115 
116 #define PKGINFO_LINE_LEN_MAX 256
117 #define PKGINFO_LINE_CNT_MAX 50
118 
119 /*
120  * X protocol uses (u_int16)length to specify the length in 4 bytes quantities
121  * of the whole request.  Both XRenderFillRectangles() and XFillRectangles()
122  * have provisions to fragment into several requests if the number of rectangles
123  * plus the current x request does not fit into 65535*4 bytes.  While
124  * XRenderCreateLinearGradient() and XRenderCreateRadialGradient() have
125  * provisions to gracefully degrade if the resulting request would exceed
126  * 65535*4 bytes.
127  *
128  * Below, we define a cap of 65535*4 bytes for the maximum X request payload
129  * allowed for Non-(XRenderFillRectangles() or XFillRectangles()) API calls,
130  * just to be conservative.  This is offset by the size of our maximum x*Req
131  * type in this compilation unit, which is xRenderCreateRadiaGradientReq.
132  *
133  * Note that sizeof(xRenderCreateRadiaGradientReq) = 36
134  */
135 #define MAX_PAYLOAD (262140u - 36u)
136 #define MAXUINT (0xffffffffu)
137 
IsXRenderAvailable(jboolean verbose,jboolean ignoreLinuxVersion)138 static jboolean IsXRenderAvailable(jboolean verbose, jboolean ignoreLinuxVersion) {
139 
140     void *xrenderlib;
141 
142     int major_opcode, first_event, first_error;
143     jboolean available = JNI_TRUE;
144 
145     if (!XQueryExtension(awt_display, "RENDER",
146                          &major_opcode, &first_event, &first_error)) {
147         return JNI_FALSE;
148     }
149 
150 #if defined(_AIX)
151     // On AIX we have to use a special syntax because the shared libraries are packed in
152     // multi-architecture archives. We first try to load the system default libXrender
153     // which is contained in the 'X11.base.lib' fileset starting with AIX 6.1
154     xrenderlib = dlopen("libXrender.a(shr_64.o)", RTLD_GLOBAL | RTLD_LAZY | RTLD_MEMBER);
155     if (xrenderlib == NULL) {
156       // If the latter wasn't successful, we also try to load the version under /opt/freeware
157       // This may be downloaded from the "AIX Toolbox for Linux Applications" even for AIX 5.3
158       xrenderlib = dlopen("libXrender.a(libXrender.so.0)", RTLD_GLOBAL | RTLD_LAZY | RTLD_MEMBER);
159     }
160     if (xrenderlib != NULL) {
161       dlclose(xrenderlib);
162     } else {
163       available = JNI_FALSE;
164     }
165 #elif defined(__solaris__)
166     xrenderlib = dlopen("libXrender.so",RTLD_GLOBAL|RTLD_LAZY);
167     if (xrenderlib != NULL) {
168 
169       XRenderCreateLinearGradientFunc =
170         (XRenderCreateLinearGradientFuncType)
171         dlsym(xrenderlib, "XRenderCreateLinearGradient");
172 
173       XRenderCreateRadialGradientFunc =
174         (XRenderCreateRadialGradientFuncType)
175         dlsym(xrenderlib, "XRenderCreateRadialGradient");
176 
177       if (XRenderCreateLinearGradientFunc == NULL ||
178           XRenderCreateRadialGradientFunc == NULL)
179       {
180         available = JNI_FALSE;
181       }
182       dlclose(xrenderlib);
183     } else {
184       available = JNI_FALSE;
185     }
186 #else
187     Dl_info info;
188     jboolean versionInfoIsFound = JNI_FALSE;
189 
190     memset(&info, 0, sizeof(Dl_info));
191     if (dladdr(&XRenderChangePicture, &info) && info.dli_fname != NULL) {
192       char pkgInfoPath[FILENAME_MAX];
193       char *pkgFileName = "/pkgconfig/xrender.pc";
194       size_t pkgFileNameLen = strlen(pkgFileName);
195       size_t pos, len = strlen(info.dli_fname);
196 
197       pos = len;
198       while (pos > 0 && info.dli_fname[pos] != '/') {
199         pos -= 1;
200       }
201 
202       if (pos > 0 && pos < (FILENAME_MAX - pkgFileNameLen - 1)) {
203         struct stat stat_info;
204 
205         // compose absolute filename to package config
206         strncpy(pkgInfoPath, info.dli_fname, pos);
207 
208         strcpy(pkgInfoPath + pos, pkgFileName);
209         pkgInfoPath[pos + pkgFileNameLen] = '\0';
210 
211         // check whether the config file exist and is a regular file
212         if ((stat(pkgInfoPath, &stat_info)== 0) &&
213             S_ISREG(stat_info.st_mode))
214         {
215           FILE *fp = fopen(pkgInfoPath, "r");
216           if (fp != NULL) {
217             char line[PKGINFO_LINE_LEN_MAX];
218             int lineCount = PKGINFO_LINE_CNT_MAX;
219             char *versionPrefix = "Version: ";
220             size_t versionPrefixLen = strlen(versionPrefix);
221 
222             // look for version
223             while(fgets(line,sizeof(line),fp) != NULL && --lineCount > 0) {
224               size_t lineLen = strlen(line);
225 
226               if (lineLen > versionPrefixLen &&
227                   strncmp(versionPrefix, line, versionPrefixLen) == 0)
228               {
229                 int v1 = 0, v2 = 0, v3 = 0;
230                 int numNeeded = 3,numProcessed;
231                 char* version = line + versionPrefixLen;
232                 numProcessed = sscanf(version, "%d.%d.%d", &v1, &v2, &v3);
233 
234                 if (numProcessed == numNeeded) {
235                   // we successfuly read the library version
236                   versionInfoIsFound = JNI_TRUE;
237 
238                   if (REQUIRED_XRENDER_VER1 == v1 &&
239                       ((REQUIRED_XRENDER_VER2 > v2) ||
240                        ((REQUIRED_XRENDER_VER2 == v2) && (REQUIRED_XRENDER_VER3 > v3))))
241                   {
242                     available = JNI_FALSE;
243 
244                     if (verbose) {
245                       printf("INFO: the version %d.%d.%d of libXrender.so is "
246                              "not supported.\n\tSee release notes for more details.\n",
247                              v1, v2, v3);
248                       fflush(stdout);
249                     }
250                   } else {
251                     if (verbose) {
252                       printf("INFO: The version of libXrender.so "
253                              "is detected as %d.%d%d\n", v1, v2, v3);
254                       fflush(stdout);
255                     }
256                   }
257                 }
258                 break;
259               }
260             }
261             fclose(fp);
262           }
263         }
264       }
265     }
266     if (verbose && !versionInfoIsFound) {
267       printf("WARNING: The version of libXrender.so cannot be detected.\n,"
268              "The pipe line will be enabled, but note that versions less than 0.9.3\n"
269              "may cause hangs and crashes\n"
270              "\tSee the release notes for more details.\n");
271       fflush(stdout);
272     }
273 #endif
274 
275 #ifdef __linux__
276     /*
277      * Check for Linux >= 3.5 (Ubuntu 12.04.02 LTS) to avoid hitting
278      * https://bugs.freedesktop.org/show_bug.cgi?id=48045
279      */
280     struct utsname utsbuf;
281     if(uname(&utsbuf) >= 0) {
282         int major, minor, revision;
283         if(sscanf(utsbuf.release, "%i.%i.%i", &major, &minor, &revision) == 3) {
284             if(major < 3 || (major == 3 && minor < 5)) {
285                 if(!ignoreLinuxVersion) {
286                     available = JNI_FALSE;
287                 }
288                 else if(verbose) {
289                  printf("WARNING: Linux < 3.5 detected.\n"
290                         "The pipeline will be enabled, but graphical "
291                         "artifacts can occur with old graphic drivers.\n"
292                         "See the release notes for more details.\n");
293                         fflush(stdout);
294                 }
295             }
296         }
297     }
298 #endif // __linux__
299 
300     return available;
301 }
302 /*
303  * Class:     sun_awt_X11GraphicsEnvironment
304  * Method:    initGLX
305  * Signature: ()Z
306  */
307 JNIEXPORT jboolean JNICALL
Java_sun_awt_X11GraphicsEnvironment_initXRender(JNIEnv * env,jclass x11ge,jboolean verbose,jboolean ignoreLinuxVersion)308 Java_sun_awt_X11GraphicsEnvironment_initXRender
309 (JNIEnv *env, jclass x11ge, jboolean verbose, jboolean ignoreLinuxVersion)
310 {
311 #ifndef HEADLESS
312     static jboolean xrenderAvailable = JNI_FALSE;
313     static jboolean firstTime = JNI_TRUE;
314 
315     if (firstTime) {
316 #ifdef DISABLE_XRENDER_BY_DEFAULT
317         if (verbose == JNI_FALSE) {
318             xrenderAvailable = JNI_FALSE;
319             firstTime = JNI_FALSE;
320             return xrenderAvailable;
321         }
322 #endif
323         AWT_LOCK();
324         xrenderAvailable = IsXRenderAvailable(verbose, ignoreLinuxVersion);
325         AWT_UNLOCK();
326         firstTime = JNI_FALSE;
327     }
328     return xrenderAvailable;
329 #else
330     return JNI_FALSE;
331 #endif /* !HEADLESS */
332 }
333 
334 
335 JNIEXPORT void JNICALL
Java_sun_java2d_xr_XRBackendNative_initIDs(JNIEnv * env,jclass cls)336 Java_sun_java2d_xr_XRBackendNative_initIDs(JNIEnv *env, jclass cls) {
337     char *maskData;
338     XImage* defaultImg;
339     jfieldID maskImgID;
340     jlong fmt8;
341     jlong fmt32;
342 
343     jfieldID a8ID = (*env)->GetStaticFieldID(env, cls, "FMTPTR_A8", "J");
344     if (a8ID == NULL) {
345         return;
346     }
347     jfieldID argb32ID = (*env)->GetStaticFieldID(env, cls, "FMTPTR_ARGB32", "J");
348     if (argb32ID == NULL) {
349         return;
350     }
351 
352     if (awt_display == (Display *)NULL) {
353         return;
354     }
355 
356     fmt8 = ptr_to_jlong(XRenderFindStandardFormat(awt_display, PictStandardA8));
357     fmt32 = ptr_to_jlong(XRenderFindStandardFormat(awt_display, PictStandardARGB32));
358 
359     (*env)->SetStaticLongField(env, cls, a8ID, fmt8);
360     (*env)->SetStaticLongField(env, cls, argb32ID, fmt32);
361 
362     maskData = (char *) malloc(32*32);
363     if (maskData == NULL) {
364        return;
365     }
366 
367     defaultImg = XCreateImage(awt_display, NULL, 8, ZPixmap, 0, maskData, 32, 32, 8, 0);
368     defaultImg->data = maskData; //required?
369     maskImgID = (*env)->GetStaticFieldID(env, cls, "MASK_XIMG", "J");
370     if (maskImgID == NULL) {
371        return;
372     }
373 
374     (*env)->SetStaticLongField(env, cls, maskImgID, ptr_to_jlong(defaultImg));
375 }
376 
377 JNIEXPORT void JNICALL
Java_sun_java2d_xr_XRBackendNative_freeGC(JNIEnv * env,jobject this,jlong gc)378 Java_sun_java2d_xr_XRBackendNative_freeGC
379  (JNIEnv *env, jobject this, jlong gc) {
380     XFreeGC(awt_display, (GC) jlong_to_ptr(gc));
381 }
382 
383 JNIEXPORT jlong JNICALL
Java_sun_java2d_xr_XRBackendNative_createGC(JNIEnv * env,jobject this,jint drawable)384 Java_sun_java2d_xr_XRBackendNative_createGC
385  (JNIEnv *env, jobject this, jint drawable) {
386   GC xgc = XCreateGC(awt_display, (Drawable) drawable, 0L, NULL);
387   return ptr_to_jlong(xgc);
388 }
389 
390 JNIEXPORT jint JNICALL
Java_sun_java2d_xr_XRBackendNative_createPixmap(JNIEnv * env,jobject this,jint drawable,jint depth,jint width,jint height)391 Java_sun_java2d_xr_XRBackendNative_createPixmap(JNIEnv *env, jobject this,
392                                                 jint drawable, jint depth,
393                                                 jint width, jint height) {
394     return (jint) XCreatePixmap(awt_display, (Drawable) drawable,
395                                 width, height, depth);
396 }
397 
398 JNIEXPORT jint JNICALL
Java_sun_java2d_xr_XRBackendNative_createPictureNative(JNIEnv * env,jclass cls,jint drawable,jlong formatPtr)399 Java_sun_java2d_xr_XRBackendNative_createPictureNative
400  (JNIEnv *env, jclass cls, jint drawable, jlong formatPtr) {
401   XRenderPictureAttributes pict_attr;
402   return XRenderCreatePicture(awt_display, (Drawable) drawable,
403                               (XRenderPictFormat *) jlong_to_ptr(formatPtr),
404                                0, &pict_attr);
405 }
406 
407 JNIEXPORT void JNICALL
Java_sun_java2d_xr_XRBackendNative_freePicture(JNIEnv * env,jobject this,jint picture)408 Java_sun_java2d_xr_XRBackendNative_freePicture
409  (JNIEnv *env, jobject this, jint picture) {
410       XRenderFreePicture(awt_display, (Picture) picture);
411 }
412 
413 JNIEXPORT void JNICALL
Java_sun_java2d_xr_XRBackendNative_freePixmap(JNIEnv * env,jobject this,jint pixmap)414 Java_sun_java2d_xr_XRBackendNative_freePixmap
415  (JNIEnv *env, jobject this, jint pixmap) {
416    XFreePixmap(awt_display, (Pixmap) pixmap);
417 }
418 
419 JNIEXPORT void JNICALL
Java_sun_java2d_xr_XRBackendNative_setPictureRepeat(JNIEnv * env,jobject this,jint picture,jint repeat)420 Java_sun_java2d_xr_XRBackendNative_setPictureRepeat
421  (JNIEnv *env, jobject this, jint picture, jint repeat) {
422     XRenderPictureAttributes pict_attr;
423     pict_attr.repeat = repeat;
424     XRenderChangePicture (awt_display, (Picture) picture, CPRepeat, &pict_attr);
425 }
426 
427 
428 JNIEXPORT void JNICALL
Java_sun_java2d_xr_XRBackendNative_setGCExposures(JNIEnv * env,jobject this,jlong gc,jboolean exposure)429 Java_sun_java2d_xr_XRBackendNative_setGCExposures
430  (JNIEnv *env, jobject this, jlong gc, jboolean exposure) {
431     XSetGraphicsExposures(awt_display,
432                          (GC) jlong_to_ptr(gc), exposure ? True : False); //TODO: ????
433 }
434 
435 JNIEXPORT void JNICALL
Java_sun_java2d_xr_XRBackendNative_setGCForeground(JNIEnv * env,jobject this,jlong gc,jint pixel)436 Java_sun_java2d_xr_XRBackendNative_setGCForeground
437  (JNIEnv *env, jobject this, jlong gc, jint pixel) {
438     XSetForeground(awt_display, (GC) jlong_to_ptr(gc), (unsigned long) pixel);
439 }
440 
441 
442 JNIEXPORT void JNICALL
Java_sun_java2d_xr_XRBackendNative_copyArea(JNIEnv * env,jobject this,jint src,jint dst,jlong gc,jint srcx,jint srcy,jint width,jint height,jint dstx,jint dsty)443 Java_sun_java2d_xr_XRBackendNative_copyArea
444  (JNIEnv *env, jobject this, jint src, jint dst, jlong gc,
445   jint srcx, jint srcy, jint width, jint height, jint dstx, jint dsty) {
446     XCopyArea(awt_display, (Drawable) src, (Drawable) dst,
447              (GC) jlong_to_ptr(gc), srcx, srcy, width, height, dstx, dsty);
448 }
449 
450 JNIEXPORT void JNICALL
Java_sun_java2d_xr_XRBackendNative_renderComposite(JNIEnv * env,jobject this,jbyte op,jint src,jint mask,jint dst,jint srcX,jint srcY,jint maskX,jint maskY,jint dstX,jint dstY,jint width,jint height)451 Java_sun_java2d_xr_XRBackendNative_renderComposite
452  (JNIEnv *env, jobject this, jbyte op, jint src, jint mask, jint dst,
453   jint srcX, jint srcY, jint maskX, jint maskY,
454   jint dstX, jint dstY, jint width, jint height) {
455     XRenderComposite (awt_display, op,
456                       (Picture)src, (Picture)mask, (Picture)dst,
457                        srcX, srcY, maskX, maskY, dstX, dstY, width, height);
458 }
459 
460 JNIEXPORT void JNICALL
Java_sun_java2d_xr_XRBackendNative_renderRectangle(JNIEnv * env,jobject this,jint dst,jbyte op,jshort red,jshort green,jshort blue,jshort alpha,jint x,jint y,jint width,jint height)461 Java_sun_java2d_xr_XRBackendNative_renderRectangle
462  (JNIEnv *env, jobject this, jint dst, jbyte op,
463   jshort red, jshort green, jshort blue, jshort alpha,
464   jint x, jint y, jint width, jint height) {
465     XRenderColor color;
466     color.alpha = alpha;
467     color.red = red;
468     color.green = green;
469     color.blue = blue;
470     XRenderFillRectangle(awt_display, op, (Picture) dst, &color,
471                          x, y, width, height);
472 }
473 
474 JNIEXPORT void JNICALL
Java_sun_java2d_xr_XRBackendNative_XRenderRectanglesNative(JNIEnv * env,jclass xsd,jint dst,jbyte op,jshort red,jshort green,jshort blue,jshort alpha,jintArray rectArray,jint rectCnt)475 Java_sun_java2d_xr_XRBackendNative_XRenderRectanglesNative
476  (JNIEnv *env, jclass xsd, jint dst, jbyte op,
477   jshort red, jshort green, jshort blue, jshort alpha,
478   jintArray rectArray, jint rectCnt) {
479     int i;
480     jint* rects;
481     XRectangle *xRects;
482     XRectangle sRects[256];
483 
484     XRenderColor color;
485     color.alpha = alpha;
486     color.red = red;
487     color.green = green;
488     color.blue = blue;
489 
490     if (rectCnt <= 256) {
491         xRects = &sRects[0];
492     } else {
493         if (MAXUINT / sizeof(XRectangle) < (unsigned)rectCnt) {
494             /* rectCnt too big, integer overflow */
495             return;
496         }
497         xRects = (XRectangle *) malloc(sizeof(XRectangle) * rectCnt);
498         if (xRects == NULL) {
499             return;
500         }
501     }
502 
503     if ((rects = (jint *)
504          (*env)->GetPrimitiveArrayCritical(env, rectArray, NULL)) == NULL) {
505         if (xRects != &sRects[0]) {
506             free(xRects);
507         }
508         return;
509     }
510 
511     for (i=0; i < rectCnt; i++) {
512         xRects[i].x = rects[i*4 + 0];
513         xRects[i].y = rects[i*4 + 1];
514         xRects[i].width = rects[i*4 + 2];
515         xRects[i].height = rects[i*4 + 3];
516     }
517 
518     XRenderFillRectangles(awt_display, op,
519                           (Picture) dst, &color, xRects, rectCnt);
520 
521     (*env)->ReleasePrimitiveArrayCritical(env, rectArray, rects, JNI_ABORT);
522     if (xRects != &sRects[0]) {
523         free(xRects);
524     }
525 }
526 
527 JNIEXPORT void JNICALL
Java_sun_java2d_xr_XRBackendNative_XRSetTransformNative(JNIEnv * env,jclass xsd,jint pic,jint m00,jint m01,jint m02,jint m10,jint m11,jint m12)528 Java_sun_java2d_xr_XRBackendNative_XRSetTransformNative
529  (JNIEnv *env, jclass xsd, jint pic,
530   jint m00, jint m01, jint m02, jint m10, jint m11, jint m12) {
531 
532   XTransform tr;
533   BUILD_TRANSFORM_MATRIX(tr, m00, m01, m02, m10, m11, m12);
534   XRenderSetPictureTransform (awt_display, (Picture) pic, &tr);
535 }
536 
537 JNIEXPORT jint JNICALL
Java_sun_java2d_xr_XRBackendNative_XRCreateLinearGradientPaintNative(JNIEnv * env,jclass xsd,jfloatArray fractionsArray,jshortArray pixelsArray,jint x1,jint y1,jint x2,jint y2,jint numStops,jint repeat)538 Java_sun_java2d_xr_XRBackendNative_XRCreateLinearGradientPaintNative
539     (JNIEnv *env, jclass xsd, jfloatArray fractionsArray,
540      jshortArray pixelsArray, jint x1, jint y1, jint x2, jint y2,
541      jint numStops, jint repeat) {
542    jint i;
543    jshort* pixels;
544    jfloat* fractions;
545    XRenderPictureAttributes pict_attr;
546    Picture gradient = 0;
547    XRenderColor *colors;
548    XFixed *stops;
549    XLinearGradient grad;
550 
551    if (MAX_PAYLOAD / (sizeof(XRenderColor) + sizeof(XFixed))
552        < (unsigned)numStops) {
553        /* numStops too big, payload overflow */
554        return -1;
555    }
556 
557    if ((pixels = (jshort *)
558         (*env)->GetPrimitiveArrayCritical(env, pixelsArray, NULL)) == NULL) {
559        return -1;
560    }
561    if ((fractions = (jfloat *)
562        (*env)->GetPrimitiveArrayCritical(env, fractionsArray, NULL)) == NULL) {
563        (*env)->ReleasePrimitiveArrayCritical(env,
564                                               pixelsArray, pixels, JNI_ABORT);
565        return -1;
566    }
567 
568     grad.p1.x = x1;
569     grad.p1.y = y1;
570     grad.p2.x = x2;
571     grad.p2.y = y2;
572 
573     /*TODO optimized & malloc check*/
574     colors = (XRenderColor *) malloc(numStops * sizeof(XRenderColor));
575     stops =  (XFixed *) malloc(numStops * sizeof(XFixed));
576 
577     if (colors == NULL || stops == NULL) {
578         if (colors != NULL) {
579             free(colors);
580         }
581         if (stops != NULL) {
582             free(stops);
583         }
584         (*env)->ReleasePrimitiveArrayCritical(env, pixelsArray, pixels, JNI_ABORT);
585         (*env)->ReleasePrimitiveArrayCritical(env, fractionsArray, fractions, JNI_ABORT);
586         return -1;
587     }
588 
589     for (i=0; i < numStops; i++) {
590       stops[i] = XDoubleToFixed(fractions[i]);
591       colors[i].alpha = pixels[i*4 + 0];
592       colors[i].red = pixels[i*4 + 1];
593       colors[i].green = pixels[i*4 + 2];
594       colors[i].blue = pixels[i*4 + 3];
595     }
596 #ifdef __solaris__
597     if (XRenderCreateLinearGradientFunc!=NULL) {
598       gradient = (*XRenderCreateLinearGradientFunc)(awt_display, &grad, stops, colors, numStops);
599     }
600 #else
601     gradient = XRenderCreateLinearGradient(awt_display, &grad, stops, colors, numStops);
602 #endif
603     free(colors);
604     free(stops);
605 
606    (*env)->ReleasePrimitiveArrayCritical(env, pixelsArray, pixels, JNI_ABORT);
607    (*env)->ReleasePrimitiveArrayCritical(env, fractionsArray, fractions, JNI_ABORT);
608 
609     if (gradient != 0) {
610         pict_attr.repeat = repeat;
611         XRenderChangePicture (awt_display, gradient, CPRepeat, &pict_attr);
612     }
613 
614    return (jint) gradient;
615 }
616 
617 
618 JNIEXPORT jint JNICALL
Java_sun_java2d_xr_XRBackendNative_XRCreateRadialGradientPaintNative(JNIEnv * env,jclass xsd,jfloatArray fractionsArray,jshortArray pixelsArray,jint numStops,jint centerX,jint centerY,jint innerRadius,jint outerRadius,jint repeat)619 Java_sun_java2d_xr_XRBackendNative_XRCreateRadialGradientPaintNative
620     (JNIEnv *env, jclass xsd, jfloatArray fractionsArray,
621      jshortArray pixelsArray, jint numStops,
622      jint centerX, jint centerY,
623      jint innerRadius, jint outerRadius, jint repeat) {
624    jint i;
625    jshort* pixels;
626    jfloat* fractions;
627    XRenderPictureAttributes pict_attr;
628    Picture gradient = 0;
629    XRenderColor *colors;
630    XFixed *stops;
631    XRadialGradient grad;
632 
633    if (MAX_PAYLOAD / (sizeof(XRenderColor) + sizeof(XFixed))
634        < (unsigned)numStops) {
635        /* numStops too big, payload overflow */
636        return -1;
637    }
638 
639    if ((pixels =
640        (jshort *)(*env)->GetPrimitiveArrayCritical(env, pixelsArray, NULL)) == NULL) {
641        return -1;
642    }
643    if ((fractions = (jfloat *)
644         (*env)->GetPrimitiveArrayCritical(env, fractionsArray, NULL)) == NULL) {
645        (*env)->ReleasePrimitiveArrayCritical(env,
646                                              pixelsArray, pixels, JNI_ABORT);
647        return -1; //TODO release pixels first
648    }
649 
650     grad.inner.x = centerX;
651     grad.inner.y = centerY;
652     grad.inner.radius = innerRadius;
653     grad.outer.x = centerX;
654     grad.outer.y = centerY;
655     grad.outer.radius = outerRadius;
656 
657     /*TODO optimized & malloc check*/
658     colors = (XRenderColor *) malloc(numStops * sizeof(XRenderColor));
659     stops =  (XFixed *) malloc(numStops * sizeof(XFixed));
660 
661     if (colors == NULL || stops == NULL) {
662         if (colors != NULL) {
663             free(colors);
664         }
665         if (stops != NULL) {
666             free(stops);
667         }
668         (*env)->ReleasePrimitiveArrayCritical(env, pixelsArray, pixels, JNI_ABORT);
669         (*env)->ReleasePrimitiveArrayCritical(env, fractionsArray, fractions, JNI_ABORT);
670         return -1;
671     }
672 
673     for (i=0; i < numStops; i++) {
674       stops[i] = XDoubleToFixed(fractions[i]);
675       colors[i].alpha = pixels[i*4 + 0];
676       colors[i].red = pixels[i*4 + 1];
677       colors[i].green = pixels[i*4 + 2];
678       colors[i].blue = pixels[i*4 + 3];
679     }
680 #ifdef __solaris__
681     if (XRenderCreateRadialGradientFunc != NULL) {
682         gradient = (jint) (*XRenderCreateRadialGradientFunc)(awt_display, &grad, stops, colors, numStops);
683     }
684 #else
685     gradient = (jint) XRenderCreateRadialGradient(awt_display, &grad, stops, colors, numStops);
686 #endif
687     free(colors);
688     free(stops);
689 
690    (*env)->ReleasePrimitiveArrayCritical(env, pixelsArray, pixels, JNI_ABORT);
691    (*env)->ReleasePrimitiveArrayCritical(env, fractionsArray, fractions, JNI_ABORT);
692 
693 
694     if (gradient != 0) {
695         pict_attr.repeat = repeat;
696         XRenderChangePicture (awt_display, gradient, CPRepeat, &pict_attr);
697     }
698 
699    return (jint) gradient;
700 }
701 
702 JNIEXPORT void JNICALL
Java_sun_java2d_xr_XRBackendNative_setFilter(JNIEnv * env,jobject this,jint picture,jint filter)703 Java_sun_java2d_xr_XRBackendNative_setFilter
704  (JNIEnv *env, jobject this, jint picture, jint filter) {
705 
706   char * filterName = "fast";
707 
708   switch(filter) {
709     case 0:
710       filterName = "fast";
711       break;
712 
713     case 1:
714       filterName = "good";
715       break;
716 
717     case 2:
718       filterName = "best";
719       break;
720   }
721 
722     XRenderSetPictureFilter(awt_display, (Picture) picture, filterName, NULL, 0);
723 }
724 
725 JNIEXPORT void JNICALL
Java_sun_java2d_xr_XRBackendNative_XRSetClipNative(JNIEnv * env,jclass xsd,jlong dst,jint x1,jint y1,jint x2,jint y2,jobject complexclip,jboolean isGC)726 Java_sun_java2d_xr_XRBackendNative_XRSetClipNative
727     (JNIEnv *env, jclass xsd, jlong dst,
728      jint x1, jint y1, jint x2, jint y2,
729      jobject complexclip, jboolean isGC)
730 {
731     int numrects;
732     XRectangle rects[256];
733     XRectangle *pRect = rects;
734 
735     numrects = RegionToYXBandedRectangles(env,
736             x1, y1, x2, y2, complexclip,
737             &pRect, 256);
738 
739     if (isGC == JNI_TRUE) {
740       if (dst != (jlong) 0) {
741           XSetClipRectangles(awt_display, (GC) jlong_to_ptr(dst), 0, 0, pRect, numrects, YXBanded);
742       }
743     } else {
744        XRenderSetPictureClipRectangles (awt_display, (Picture) dst, 0, 0, pRect, numrects);
745     }
746 
747     if (pRect != rects) {
748         free(pRect);
749     }
750 }
751 
752 JNIEXPORT void JNICALL
Java_sun_java2d_xr_XRBackendNative_putMaskNative(JNIEnv * env,jclass cls,jint drawable,jlong gc,jbyteArray imageData,jint sx,jint sy,jint dx,jint dy,jint width,jint height,jint maskOff,jint maskScan,jfloat ea,jlong imgPtr)753 Java_sun_java2d_xr_XRBackendNative_putMaskNative
754  (JNIEnv *env, jclass cls, jint drawable, jlong gc, jbyteArray imageData,
755   jint sx, jint sy, jint dx, jint dy, jint width, jint height,
756   jint maskOff, jint maskScan, jfloat ea, jlong imgPtr) {
757 
758     int line, pix;
759     char *mask;
760     char *defaultData;
761     XImage *defaultImg, *img;
762     jboolean imageFits;
763 
764     if ((mask = (char *)
765          (*env)->GetPrimitiveArrayCritical(env, imageData, NULL)) == NULL) {
766         return;
767      }
768 
769     defaultImg = (XImage *) jlong_to_ptr(imgPtr);
770 
771     if (ea != 1.0f) {
772         for (line=0; line < height; line++) {
773             for (pix=0; pix < width; pix++) {
774                 int index = maskScan*line + pix + maskOff;
775                 mask[index] = (((unsigned char) mask[index])*ea);
776             }
777         }
778     }
779 
780     /*
781     * 1. If existing XImage and supplied buffer match, only adjust the data pointer
782     * 2. If existing XImage is large enough to hold the data but does not match in
783     *    scan the data is copied to fit the XImage.
784     * 3. If data is larger than the existing XImage a new temporary XImage is
785     *    allocated.
786     * The default XImage is optimized for the AA tiles, which are currently 32x32.
787     */
788     defaultData = defaultImg->data;
789     img = defaultImg;
790     imageFits = defaultImg->width >= width && defaultImg->height >= height;
791 
792     if (imageFits &&
793         maskOff == defaultImg->xoffset && maskScan == defaultImg->bytes_per_line) {
794         defaultImg->data = mask;
795     } else {
796         if (imageFits) {
797             for (line=0; line < height; line++) {
798                 for (pix=0; pix < width; pix++) {
799                     img->data[line*img->bytes_per_line + pix] =
800                         (unsigned char) (mask[maskScan*line + pix + maskOff]);
801                 }
802             }
803         } else {
804             img = XCreateImage(awt_display, NULL, 8, ZPixmap,
805                                maskOff, mask, maskScan, height, 8, 0);
806         }
807     }
808 
809     XPutImage(awt_display, (Pixmap) drawable, (GC) jlong_to_ptr(gc),
810               img, 0, 0, 0, 0, width, height);
811     (*env)->ReleasePrimitiveArrayCritical(env, imageData, mask, JNI_ABORT);
812 
813     if (img != defaultImg) {
814         img->data = NULL;
815         XDestroyImage(img);
816     }
817     defaultImg->data = defaultData;
818 }
819 
820 JNIEXPORT void JNICALL
Java_sun_java2d_xr_XRBackendNative_XRAddGlyphsNative(JNIEnv * env,jclass cls,jint glyphSet,jlongArray glyphInfoPtrsArray,jint glyphCnt,jbyteArray pixelDataArray,int pixelDataLength)821 Java_sun_java2d_xr_XRBackendNative_XRAddGlyphsNative
822  (JNIEnv *env, jclass cls, jint glyphSet,
823   jlongArray glyphInfoPtrsArray, jint glyphCnt,
824   jbyteArray pixelDataArray, int pixelDataLength) {
825     jlong *glyphInfoPtrs;
826     unsigned char *pixelData;
827     int i;
828 
829     if (MAX_PAYLOAD / (sizeof(XGlyphInfo) + sizeof(Glyph))
830         < (unsigned)glyphCnt) {
831         /* glyphCnt too big, payload overflow */
832         return;
833     }
834 
835     XGlyphInfo *xginfo = (XGlyphInfo *) malloc(sizeof(XGlyphInfo) * glyphCnt);
836     Glyph *gid = (Glyph *) malloc(sizeof(Glyph) * glyphCnt);
837 
838     if (xginfo == NULL || gid == NULL) {
839         if (xginfo != NULL) {
840             free(xginfo);
841         }
842         if (gid != NULL) {
843             free(gid);
844         }
845         return;
846     }
847 
848     if ((glyphInfoPtrs = (jlong *)(*env)->
849         GetPrimitiveArrayCritical(env, glyphInfoPtrsArray, NULL)) == NULL)
850     {
851         free(xginfo);
852         free(gid);
853         return;
854     }
855 
856     if ((pixelData = (unsigned char *)
857         (*env)->GetPrimitiveArrayCritical(env, pixelDataArray, NULL)) == NULL)
858     {
859         (*env)->ReleasePrimitiveArrayCritical(env,
860                                 glyphInfoPtrsArray, glyphInfoPtrs, JNI_ABORT);
861         free(xginfo);
862         free(gid);
863         return;
864     }
865 
866     for (i=0; i < glyphCnt; i++) {
867       GlyphInfo *jginfo = (GlyphInfo *) jlong_to_ptr(glyphInfoPtrs[i]);
868 
869       // 'jginfo->cellInfo' is of type 'void*'
870       // (see definition of 'GlyphInfo' in fontscalerdefs.h)
871       // 'Glyph' is typedefed to 'unsigned long'
872       // (see http://www.x.org/releases/X11R7.7/doc/libXrender/libXrender.txt)
873       // Maybe we should assert that (sizeof(void*) == sizeof(Glyph)) ?
874       gid[i] = (Glyph) (jginfo->cellInfo);
875       xginfo[i].x = (-jginfo->topLeftX);
876       xginfo[i].y = (-jginfo->topLeftY);
877       xginfo[i].width = jginfo->width;
878       xginfo[i].height = jginfo->height;
879       xginfo[i].xOff = round(jginfo->advanceX);
880       xginfo[i].yOff = round(jginfo->advanceY);
881     }
882 
883     XRenderAddGlyphs(awt_display, glyphSet, &gid[0], &xginfo[0], glyphCnt,
884                      (const char*)pixelData, pixelDataLength);
885 
886     (*env)->ReleasePrimitiveArrayCritical(env, glyphInfoPtrsArray, glyphInfoPtrs, JNI_ABORT);
887     (*env)->ReleasePrimitiveArrayCritical(env, pixelDataArray, pixelData, JNI_ABORT);
888 
889     free(xginfo);
890     free(gid);
891 }
892 
893 JNIEXPORT void JNICALL
Java_sun_java2d_xr_XRBackendNative_XRFreeGlyphsNative(JNIEnv * env,jclass cls,jint glyphSet,jintArray gidArray,jint glyphCnt)894 Java_sun_java2d_xr_XRBackendNative_XRFreeGlyphsNative
895  (JNIEnv *env, jclass cls, jint glyphSet, jintArray gidArray, jint glyphCnt) {
896 
897     if (MAX_PAYLOAD / sizeof(Glyph) < (unsigned)glyphCnt) {
898         /* glyphCnt too big, payload overflow */
899         return;
900     }
901 
902     /* The glyph ids are 32 bit but may be stored in a 64 bit long on
903      * a 64 bit architecture. So optimise the 32 bit case to avoid
904      * extra stack or heap allocations by directly referencing the
905      * underlying Java array and only allocate on 64 bit.
906      */
907     if (sizeof(jint) == sizeof(Glyph)) {
908         jint *gids =
909             (*env)->GetPrimitiveArrayCritical(env, gidArray, NULL);
910         if (gids == NULL) {
911             return;
912         } else {
913              XRenderFreeGlyphs(awt_display,
914                                (GlyphSet)glyphSet, (Glyph *)gids, glyphCnt);
915              (*env)->ReleasePrimitiveArrayCritical(env, gidArray,
916                                                    gids, JNI_ABORT);
917         }
918         return;
919     } else {
920         Glyph stack_ids[64];
921         Glyph *gids = NULL;
922         jint* jgids = NULL;
923         int i;
924 
925         if (glyphCnt <= 64) {
926             gids = stack_ids;
927         } else {
928             gids = (Glyph *)malloc(sizeof(Glyph) * glyphCnt);
929             if (gids == NULL) {
930                 return;
931             }
932         }
933         jgids = (*env)->GetPrimitiveArrayCritical(env, gidArray, NULL);
934         if (jgids == NULL) {
935             if (gids != stack_ids) {
936                 free(gids);
937             }
938             return;
939         }
940         for (i=0; i < glyphCnt; i++) {
941             gids[i] = jgids[i];
942         }
943         XRenderFreeGlyphs(awt_display,
944                           (GlyphSet) glyphSet, gids, glyphCnt);
945         (*env)->ReleasePrimitiveArrayCritical(env, gidArray,
946                                               jgids, JNI_ABORT);
947         if (gids != stack_ids) {
948             free(gids);
949         }
950     }
951 }
952 
953 JNIEXPORT jint JNICALL
Java_sun_java2d_xr_XRBackendNative_XRenderCreateGlyphSetNative(JNIEnv * env,jclass cls,jlong format)954 Java_sun_java2d_xr_XRBackendNative_XRenderCreateGlyphSetNative
955  (JNIEnv *env, jclass cls, jlong format) {
956   return XRenderCreateGlyphSet(awt_display, (XRenderPictFormat *) jlong_to_ptr(format));
957 }
958 
959 JNIEXPORT void JNICALL
Java_sun_java2d_xr_XRBackendNative_XRenderCompositeTextNative(JNIEnv * env,jclass cls,jint op,jint src,jint dst,jint sx,jint sy,jlong maskFmt,jintArray eltArray,jintArray glyphIDArray,jint eltCnt,jint glyphCnt)960 Java_sun_java2d_xr_XRBackendNative_XRenderCompositeTextNative
961  (JNIEnv *env, jclass cls, jint op, jint src, jint dst,
962   jint sx, jint sy, jlong maskFmt, jintArray eltArray,
963   jintArray  glyphIDArray, jint eltCnt, jint glyphCnt) {
964     jint i;
965     jint *ids;
966     jint *elts;
967     XGlyphElt32 *xelts;
968     unsigned int *xids;
969     XGlyphElt32 selts[24];
970     unsigned int sids[256];
971     int charCnt = 0;
972 
973     if ((MAX_PAYLOAD / sizeof(XGlyphElt32) < (unsigned)eltCnt)
974         || (MAX_PAYLOAD / sizeof(unsigned int) < (unsigned)glyphCnt)
975         || ((MAX_PAYLOAD - sizeof(XGlyphElt32)*(unsigned)eltCnt) /
976             sizeof(unsigned int) < (unsigned)glyphCnt))
977     {
978         /* (eltCnt, glyphCnt) too big, payload overflow */
979         return;
980     }
981 
982     if (eltCnt <= 24) {
983       xelts = &selts[0];
984     }else {
985       xelts = (XGlyphElt32 *) malloc(sizeof(XGlyphElt32) * eltCnt);
986       if (xelts == NULL) {
987           return;
988       }
989     }
990 
991     if (glyphCnt <= 256) {
992       xids = &sids[0];
993     } else {
994       xids = (unsigned int*)malloc(sizeof(unsigned int) * glyphCnt);
995       if (xids == NULL) {
996           if (xelts != &selts[0]) {
997             free(xelts);
998           }
999           return;
1000       }
1001     }
1002 
1003     if ((ids = (jint *)
1004          (*env)->GetPrimitiveArrayCritical(env, glyphIDArray, NULL)) == NULL) {
1005         if (xelts != &selts[0]) {
1006             free(xelts);
1007         }
1008         if (xids != &sids[0]) {
1009             free(xids);
1010         }
1011         return;
1012     }
1013     if ((elts = (jint *)
1014           (*env)->GetPrimitiveArrayCritical(env, eltArray, NULL)) == NULL) {
1015         (*env)->ReleasePrimitiveArrayCritical(env,
1016                                               glyphIDArray, ids, JNI_ABORT);
1017         if (xelts != &selts[0]) {
1018             free(xelts);
1019         }
1020         if (xids != &sids[0]) {
1021             free(xids);
1022         }
1023         return;
1024     }
1025 
1026     for (i=0; i < glyphCnt; i++) {
1027       xids[i] = ids[i];
1028     }
1029 
1030     for (i=0; i < eltCnt; i++) {
1031       xelts[i].nchars = elts[i*4 + 0];
1032       xelts[i].xOff = elts[i*4 + 1];
1033       xelts[i].yOff = elts[i*4 + 2];
1034       xelts[i].glyphset = (GlyphSet) elts[i*4 + 3];
1035       xelts[i].chars = &xids[charCnt];
1036 
1037       charCnt += xelts[i].nchars;
1038     }
1039 
1040     XRenderCompositeText32(awt_display, op, (Picture) src, (Picture) dst,
1041                            (XRenderPictFormat *) jlong_to_ptr(maskFmt),
1042                             sx, sy, 0, 0, xelts, eltCnt);
1043 
1044     (*env)->ReleasePrimitiveArrayCritical(env, glyphIDArray, ids, JNI_ABORT);
1045     (*env)->ReleasePrimitiveArrayCritical(env, eltArray, elts, JNI_ABORT);
1046 
1047     if (xelts != &selts[0]) {
1048         free(xelts);
1049     }
1050 
1051     if (xids != &sids[0]) {
1052         free(xids);
1053     }
1054 }
1055 
1056 JNIEXPORT void JNICALL
Java_sun_java2d_xr_XRBackendNative_setGCMode(JNIEnv * env,jobject this,jlong gc,jboolean copy)1057 Java_sun_java2d_xr_XRBackendNative_setGCMode
1058  (JNIEnv *env, jobject this, jlong gc, jboolean copy) {
1059   GC xgc = (GC) jlong_to_ptr(gc);
1060 
1061   if (copy == JNI_TRUE) {
1062     XSetFunction(awt_display, xgc, GXcopy);
1063   } else {
1064     XSetFunction(awt_display, xgc, GXxor);
1065   }
1066 }
1067 
1068 JNIEXPORT void JNICALL
Java_sun_java2d_xr_XRBackendNative_GCRectanglesNative(JNIEnv * env,jclass xsd,jint dst,jlong gc,jintArray rectArray,jint rectCnt)1069 Java_sun_java2d_xr_XRBackendNative_GCRectanglesNative
1070  (JNIEnv *env, jclass xsd, jint dst, jlong gc,
1071   jintArray rectArray, jint rectCnt) {
1072     int i;
1073     jint* rects;
1074     XRectangle *xRects;
1075     XRectangle sRects[256];
1076 
1077     if (rectCnt <= 256) {
1078       xRects = &sRects[0];
1079     } else {
1080       if (MAXUINT / sizeof(XRectangle) < (unsigned)rectCnt) {
1081         /* rectCnt too big, integer overflow */
1082         return;
1083       }
1084 
1085       xRects = (XRectangle *) malloc(sizeof(XRectangle) * rectCnt);
1086       if (xRects == NULL) {
1087         return;
1088       }
1089     }
1090 
1091     if ((rects = (jint*)
1092          (*env)->GetPrimitiveArrayCritical(env, rectArray, NULL)) == NULL) {
1093         if (xRects != &sRects[0]) {
1094             free(xRects);
1095         }
1096         return;
1097     }
1098 
1099     for (i=0; i < rectCnt; i++) {
1100       xRects[i].x = rects[i*4 + 0];
1101       xRects[i].y = rects[i*4 + 1];
1102       xRects[i].width = rects[i*4 + 2];
1103       xRects[i].height = rects[i*4 + 3];
1104     }
1105 
1106     XFillRectangles(awt_display, (Drawable) dst, (GC) jlong_to_ptr(gc), xRects, rectCnt);
1107 
1108     (*env)->ReleasePrimitiveArrayCritical(env, rectArray, rects, JNI_ABORT);
1109     if (xRects != &sRects[0]) {
1110       free(xRects);
1111     }
1112 }
1113