1 /*
   2  * Copyright (c) 2001, 2005, 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 "GlyphImageRef.h"
  27 
  28 #ifdef HEADLESS
  29 #include "SurfaceData.h"
  30 #else
  31 #include "X11SurfaceData.h"
  32 #include "GraphicsPrimitiveMgr.h"
  33 #endif /* !HEADLESS */
  34 #include <jlong.h>
  35 
  36 #define TEXT_BM_WIDTH   1024
  37 #define TEXT_BM_HEIGHT  32
  38 
  39 #ifndef HEADLESS
  40 
  41 static jboolean checkPixmap(JNIEnv *env, AwtGraphicsConfigDataPtr cData)
  42 {
  43     XImage *img;
  44     int image_size;
  45     Window root;
  46 
  47     if (cData->monoImage == NULL) {
  48         img = XCreateImage(awt_display, NULL, 1, XYBitmap, 0, 0,
  49                                    TEXT_BM_WIDTH, TEXT_BM_HEIGHT, 32, 0);
  50         if (img != NULL) {
  51             image_size = img->bytes_per_line * TEXT_BM_HEIGHT;
  52             // assert(BM_W and BM_H are not large enough to overflow);
  53             img->data = (char *) malloc(image_size);
  54             if (img->data == NULL) {
  55                 XFree(img);
  56             } else {
  57                 // Force same bit/byte ordering
  58                 img->bitmap_bit_order = img->byte_order;
  59                 cData->monoImage = img;
  60             }
  61         }
  62         if (cData->monoImage == NULL) {
  63             JNU_ThrowOutOfMemoryError(env, "Cannot allocate bitmap for text");
  64             return JNI_FALSE;
  65         }
  66     }
  67     if (cData->monoPixmap == 0 ||
  68         cData->monoPixmapGC == NULL ||
  69         cData->monoPixmapWidth != TEXT_BM_WIDTH ||
  70         cData->monoPixmapHeight != TEXT_BM_HEIGHT)
  71     {
  72         if (cData->monoPixmap != 0) {
  73             XFreePixmap(awt_display, cData->monoPixmap);
  74             cData->monoPixmap = 0;
  75         }
  76         if (cData->monoPixmapGC != NULL) {
  77             XFreeGC(awt_display, cData->monoPixmapGC);
  78             cData->monoPixmapGC = 0;
  79         }
  80         root = RootWindow(awt_display, cData->awt_visInfo.screen);
  81         cData->monoPixmap = XCreatePixmap(awt_display, root,
  82                                           TEXT_BM_WIDTH, TEXT_BM_HEIGHT, 1);
  83         if (cData->monoPixmap == 0) {
  84             JNU_ThrowOutOfMemoryError(env, "Cannot allocate pixmap for text");
  85             return JNI_FALSE;
  86         }
  87         cData->monoPixmapGC = XCreateGC(awt_display, cData->monoPixmap,
  88                                         0, NULL);
  89         if (cData->monoPixmapGC == NULL) {
  90             XFreePixmap(awt_display, cData->monoPixmap);
  91             cData->monoPixmap = 0;
  92             JNU_ThrowOutOfMemoryError(env, "Cannot allocate pixmap for text");
  93             return JNI_FALSE;
  94         }
  95         XSetForeground(awt_display, cData->monoPixmapGC, 1);
  96         XSetBackground(awt_display, cData->monoPixmapGC, 0);
  97         cData->monoPixmapWidth = TEXT_BM_WIDTH;
  98         cData->monoPixmapHeight = TEXT_BM_HEIGHT;
  99     }
 100     return JNI_TRUE;
 101 }
 102 
 103 static void FillBitmap(XImage *theImage,
 104                        ImageRef *glyphs, jint totalGlyphs,
 105                        jint clipLeft, jint clipTop,
 106                        jint clipRight, jint clipBottom)
 107 {
 108     int glyphCounter;
 109     int scan = theImage->bytes_per_line;
 110     int y, left, top, right, bottom, width, height;
 111     jubyte *pPix;
 112     const jubyte *pixels;
 113     unsigned int rowBytes;
 114 
 115     pPix = (jubyte *) theImage->data;
 116     glyphCounter = ((clipRight - clipLeft) + 7) >> 3;
 117     for (y = clipTop; y < clipBottom; y++) {
 118         memset(pPix, 0, glyphCounter);
 119         pPix += scan;
 120     }
 121 
 122     for (glyphCounter = 0; glyphCounter < totalGlyphs; glyphCounter++) {
 123         pixels = (const jubyte *)glyphs[glyphCounter].pixels;
 124         if (!pixels) {
 125             continue;
 126         }
 127         rowBytes = glyphs[glyphCounter].width;
 128         left     = glyphs[glyphCounter].x;
 129         top      = glyphs[glyphCounter].y;
 130         width    = glyphs[glyphCounter].width;
 131         height   = glyphs[glyphCounter].height;
 132 
 133         /* if any clipping required, modify parameters now */
 134         right  = left + width;
 135         bottom = top + height;
 136         if (left < clipLeft) {
 137             pixels += clipLeft - left;
 138             left = clipLeft;
 139         }
 140         if (top < clipTop) {
 141             pixels += (clipTop - top) * rowBytes;
 142             top = clipTop;
 143         }
 144         if (right > clipRight) {
 145             right = clipRight;
 146         }
 147         if (bottom > clipBottom) {
 148             bottom = clipBottom;
 149         }
 150         if (right <= left || bottom <= top) {
 151             continue;
 152         }
 153         width = right - left;
 154         height = bottom - top;
 155         top -= clipTop;
 156         left -= clipLeft;
 157         pPix = ((jubyte *) theImage->data) + (left >> 3) + (intptr_t) top * scan;
 158         left &= 0x07;
 159         if (theImage->bitmap_bit_order == MSBFirst) {
 160             left = 0x80 >> left;
 161             do {
 162                 int x = 0, bx = 0;
 163                 int pix = pPix[0];
 164                 int bit = left;
 165                 do {
 166                     if (bit == 0) {
 167                         pPix[bx] = (jubyte) pix;
 168                         pix = pPix[++bx];
 169                         bit = 0x80;
 170                     }
 171                     if (pixels[x]) {
 172                         pix |= bit;
 173                     }
 174                     bit >>= 1;
 175                 } while (++x < width);
 176                 pPix[bx] = (jubyte) pix;
 177                 pPix += scan;
 178                 pixels += rowBytes;
 179             } while (--height > 0);
 180         } else {
 181             left = 1 << left;
 182             do {
 183                 int x = 0, bx = 0;
 184                 int pix = pPix[0];
 185                 int bit = left;
 186                 do {
 187                     if ((bit >> 8) != 0) {
 188                         pPix[bx] = (jubyte) pix;
 189                         pix = pPix[++bx];
 190                         bit = 1;
 191                     }
 192                     if (pixels[x]) {
 193                         pix |= bit;
 194                     }
 195                     bit <<= 1;
 196                 } while (++x < width);
 197                 pPix[bx] = (jubyte) pix;
 198                 pPix += scan;
 199                 pixels += rowBytes;
 200             } while (--height > 0);
 201         }
 202     }
 203 }
 204 #endif /* !HEADLESS */
 205 
 206 JNIEXPORT void JNICALL
 207 AWTDrawGlyphList(JNIEnv *env, jobject xtr,
 208                  jlong dstData, jlong gc,
 209                  SurfaceDataBounds *bounds, ImageRef *glyphs, jint totalGlyphs)
 210 {
 211 #ifndef HEADLESS
 212     GC xgc, theGC;
 213     XImage *theImage;
 214     Pixmap thePixmap;
 215     XGCValues xgcv;
 216     int scan, screen;
 217     AwtGraphicsConfigDataPtr cData;
 218     X11SDOps *xsdo = (X11SDOps *)jlong_to_ptr(dstData);
 219     jint cx1, cy1, cx2, cy2;
 220 
 221     if (xsdo == NULL) {
 222         return;
 223     }
 224 
 225     xgc = (GC)gc;
 226     if (xgc == NULL) {
 227         return;
 228     }
 229 
 230     screen = xsdo->configData->awt_visInfo.screen;
 231     cData = getDefaultConfig(screen);
 232     if (!checkPixmap(env, cData)) {
 233         return;
 234     }
 235     theImage = cData->monoImage;
 236     thePixmap = cData->monoPixmap;
 237     theGC = cData->monoPixmapGC;
 238 
 239     scan = theImage->bytes_per_line;
 240 
 241     xgcv.fill_style = FillStippled;
 242     xgcv.stipple = thePixmap;
 243     xgcv.ts_x_origin = bounds->x1;
 244     xgcv.ts_y_origin = bounds->y1;
 245     XChangeGC(awt_display, xgc,
 246               GCFillStyle | GCStipple | GCTileStipXOrigin | GCTileStipYOrigin,
 247               &xgcv);
 248 
 249     cy1 = bounds->y1;
 250     while (cy1 < bounds->y2) {
 251         cy2 = cy1 + TEXT_BM_HEIGHT;
 252         if (cy2 > bounds->y2) cy2 = bounds->y2;
 253 
 254         cx1 = bounds->x1;
 255         while (cx1 < bounds->x2) {
 256             cx2 = cx1 + TEXT_BM_WIDTH;
 257             if (cx2 > bounds->x2) cx2 = bounds->x2;
 258 
 259             FillBitmap(theImage,
 260                        glyphs,
 261                        totalGlyphs,
 262                        cx1, cy1, cx2, cy2);
 263 
 264             // NOTE: Since we are tiling around by BM_W, BM_H offsets
 265             // and thePixmap is BM_W x BM_H, we do not have to move
 266             // the TSOrigin at each step since the stipple repeats
 267             // every BM_W, BM_H units
 268             XPutImage(awt_display, thePixmap, theGC, theImage,
 269                       0, 0, 0, 0, cx2 - cx1, cy2 - cy1);
 270             /* MGA on Linux doesn't pick up the new stipple image data,
 271              * probably because it caches the image as a hardware pixmap
 272              * and doesn't update it when the pixmap image data is changed.
 273              * So if the loop is executed more than once, update the GC
 274              * which triggers the required behaviour. This extra XChangeGC
 275              * call only happens on large or rotated text so isn't a
 276              * significant new overhead..
 277              * This code needs to execute on a Solaris client too, in case
 278              * we are remote displaying to a MGA.
 279              */
 280             if (cy1 != bounds->y1 || cx1 != bounds->x1) {
 281                 XChangeGC(awt_display, xgc, GCStipple, &xgcv);
 282             }
 283 
 284             XFillRectangle(awt_display, xsdo->drawable, xgc,
 285                            cx1, cy1, cx2 - cx1, cy2 - cy1);
 286 
 287             cx1 = cx2;
 288         }
 289 
 290         cy1 = cy2;
 291     }
 292     XSetFillStyle(awt_display, xgc, FillSolid);
 293 
 294     X11SD_DirectRenderNotify(env, xsdo);
 295 #endif /* !HEADLESS */
 296 }