text: /*
   * Copyright (c) 2001, 2005, 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 "GlyphImageRef.h"
  
  #ifdef HEADLESS
  #include "SurfaceData.h"
  #else
  #include "X11SurfaceData.h"
  #include "GraphicsPrimitiveMgr.h"
  #endif /* !HEADLESS */
  #include <jlong.h>
  
  #define TEXT_BM_WIDTH   1024
  #define TEXT_BM_HEIGHT  32
  
  #ifndef HEADLESS
  
  static jboolean checkPixmap(JNIEnv *env, AwtGraphicsConfigDataPtr cData)
  {
      XImage *img;
      int image_size;
      Window root;
  
      if (cData->monoImage == NULL) {
          img = XCreateImage(awt_display, NULL, 1, XYBitmap, 0, 0,
                                     TEXT_BM_WIDTH, TEXT_BM_HEIGHT, 32, 0);
          if (img != NULL) {
              image_size = img->bytes_per_line * TEXT_BM_HEIGHT;
              // assert(BM_W and BM_H are not large enough to overflow);
              img->data = (char *) malloc(image_size);
              if (img->data == NULL) {
                  XFree(img);
              } else {
                  // Force same bit/byte ordering
                  img->bitmap_bit_order = img->byte_order;
                  cData->monoImage = img;
              }
          }
          if (cData->monoImage == NULL) {
              JNU_ThrowOutOfMemoryError(env, "Cannot allocate bitmap for text");
              return JNI_FALSE;
          }
      }
      if (cData->monoPixmap == 0 ||
          cData->monoPixmapGC == NULL ||
          cData->monoPixmapWidth != TEXT_BM_WIDTH ||
          cData->monoPixmapHeight != TEXT_BM_HEIGHT)
      {
          if (cData->monoPixmap != 0) {
              XFreePixmap(awt_display, cData->monoPixmap);
              cData->monoPixmap = 0;
          }
          if (cData->monoPixmapGC != NULL) {
              XFreeGC(awt_display, cData->monoPixmapGC);
              cData->monoPixmapGC = 0;
          }
          root = RootWindow(awt_display, cData->awt_visInfo.screen);
          cData->monoPixmap = XCreatePixmap(awt_display, root,
                                            TEXT_BM_WIDTH, TEXT_BM_HEIGHT, 1);
          if (cData->monoPixmap == 0) {
              JNU_ThrowOutOfMemoryError(env, "Cannot allocate pixmap for text");
              return JNI_FALSE;
          }
          cData->monoPixmapGC = XCreateGC(awt_display, cData->monoPixmap,
                                          0, NULL);
          if (cData->monoPixmapGC == NULL) {
              XFreePixmap(awt_display, cData->monoPixmap);
              cData->monoPixmap = 0;
              JNU_ThrowOutOfMemoryError(env, "Cannot allocate pixmap for text");
              return JNI_FALSE;
          }
          XSetForeground(awt_display, cData->monoPixmapGC, 1);
          XSetBackground(awt_display, cData->monoPixmapGC, 0);
          cData->monoPixmapWidth = TEXT_BM_WIDTH;
          cData->monoPixmapHeight = TEXT_BM_HEIGHT;
      }
      return JNI_TRUE;
  }
  
  static void FillBitmap(XImage *theImage,
                         ImageRef *glyphs, jint totalGlyphs,
                         jint clipLeft, jint clipTop,
                         jint clipRight, jint clipBottom)
  {
      int glyphCounter;
      int scan = theImage->bytes_per_line;
      int y, left, top, right, bottom, width, height;
      jubyte *pPix;
      const jubyte *pixels;
      unsigned int rowBytes;
  
      pPix = (jubyte *) theImage->data;
      glyphCounter = ((clipRight - clipLeft) + 7) >> 3;
      for (y = clipTop; y < clipBottom; y++) {
          memset(pPix, 0, glyphCounter);
          pPix += scan;
      }
  
      for (glyphCounter = 0; glyphCounter < totalGlyphs; glyphCounter++) {
          pixels = (const jubyte *)glyphs[glyphCounter].pixels;
          if (!pixels) {
              continue;
          }
          rowBytes = glyphs[glyphCounter].width;
          left     = glyphs[glyphCounter].x;
          top      = glyphs[glyphCounter].y;
          width    = glyphs[glyphCounter].width;
          height   = glyphs[glyphCounter].height;
  
          /* if any clipping required, modify parameters now */
          right  = left + width;
          bottom = top + height;
          if (left < clipLeft) {
              pixels += clipLeft - left;
              left = clipLeft;
          }
          if (top < clipTop) {
              pixels += (clipTop - top) * rowBytes;
              top = clipTop;
          }
          if (right > clipRight) {
              right = clipRight;
          }
          if (bottom > clipBottom) {
              bottom = clipBottom;
          }
          if (right <= left || bottom <= top) {
              continue;
          }
          width = right - left;
          height = bottom - top;
          top -= clipTop;
          left -= clipLeft;
          pPix = ((jubyte *) theImage->data) + (left >> 3) + top * scan;
          left &= 0x07;
          if (theImage->bitmap_bit_order == MSBFirst) {
              left = 0x80 >> left;
              do {
                  int x = 0, bx = 0;
                  int pix = pPix[0];
                  int bit = left;
                  do {
                      if (bit == 0) {
                          pPix[bx] = (jubyte) pix;
                          pix = pPix[++bx];
                          bit = 0x80;
                      }
                      if (pixels[x]) {
                          pix |= bit;
                      }
                      bit >>= 1;
                  } while (++x < width);
                  pPix[bx] = (jubyte) pix;
                  pPix += scan;
                  pixels += rowBytes;
              } while (--height > 0);
          } else {
              left = 1 << left;
              do {
                  int x = 0, bx = 0;
                  int pix = pPix[0];
                  int bit = left;
                  do {
                      if ((bit >> 8) != 0) {
                          pPix[bx] = (jubyte) pix;
                          pix = pPix[++bx];
                          bit = 1;
                      }
                      if (pixels[x]) {
                          pix |= bit;
                      }
                      bit <<= 1;
                  } while (++x < width);
                  pPix[bx] = (jubyte) pix;
                  pPix += scan;
                  pixels += rowBytes;
              } while (--height > 0);
          }
      }
  }
  #endif /* !HEADLESS */
  
  JNIEXPORT void JNICALL
  AWTDrawGlyphList(JNIEnv *env, jobject xtr,
                   jlong dstData, jlong gc,
                   SurfaceDataBounds *bounds, ImageRef *glyphs, jint totalGlyphs)
  {
  #ifndef HEADLESS
      GC xgc, theGC;
      XImage *theImage;
      Pixmap thePixmap;
      XGCValues xgcv;
      int scan, screen;
      AwtGraphicsConfigDataPtr cData;
      X11SDOps *xsdo = (X11SDOps *)jlong_to_ptr(dstData);
      jint cx1, cy1, cx2, cy2;
  
      if (xsdo == NULL) {
          return;
      }
  
      xgc = (GC)gc;
      if (xgc == NULL) {
          return;
      }
  
      screen = xsdo->configData->awt_visInfo.screen;
      cData = getDefaultConfig(screen);
      if (!checkPixmap(env, cData)) {
          return;
      }
      theImage = cData->monoImage;
      thePixmap = cData->monoPixmap;
      theGC = cData->monoPixmapGC;
  
      scan = theImage->bytes_per_line;
  
      xgcv.fill_style = FillStippled;
      xgcv.stipple = thePixmap;
      xgcv.ts_x_origin = bounds->x1;
      xgcv.ts_y_origin = bounds->y1;
      XChangeGC(awt_display, xgc,
                GCFillStyle | GCStipple | GCTileStipXOrigin | GCTileStipYOrigin,
                &xgcv);
  
      cy1 = bounds->y1;
      while (cy1 < bounds->y2) {
          cy2 = cy1 + TEXT_BM_HEIGHT;
          if (cy2 > bounds->y2) cy2 = bounds->y2;
  
          cx1 = bounds->x1;
          while (cx1 < bounds->x2) {
              cx2 = cx1 + TEXT_BM_WIDTH;
              if (cx2 > bounds->x2) cx2 = bounds->x2;
  
              FillBitmap(theImage,
                         glyphs,
                         totalGlyphs,
                         cx1, cy1, cx2, cy2);
  
              // NOTE: Since we are tiling around by BM_W, BM_H offsets
              // and thePixmap is BM_W x BM_H, we do not have to move
              // the TSOrigin at each step since the stipple repeats
              // every BM_W, BM_H units
              XPutImage(awt_display, thePixmap, theGC, theImage,
                        0, 0, 0, 0, cx2 - cx1, cy2 - cy1);
              /* MGA on Linux doesn't pick up the new stipple image data,
               * probably because it caches the image as a hardware pixmap
               * and doesn't update it when the pixmap image data is changed.
               * So if the loop is executed more than once, update the GC
               * which triggers the required behaviour. This extra XChangeGC
               * call only happens on large or rotated text so isn't a
               * significant new overhead..
               * This code needs to execute on a Solaris client too, in case
               * we are remote displaying to a MGA.
               */
              if (cy1 != bounds->y1 || cx1 != bounds->x1) {
                  XChangeGC(awt_display, xgc, GCStipple, &xgcv);
              }
  
              XFillRectangle(awt_display, xsdo->drawable, xgc,
                             cx1, cy1, cx2 - cx1, cy2 - cy1);
  
              cx1 = cx2;
          }
  
          cy1 = cy2;
      }
      XSetFillStyle(awt_display, xgc, FillSolid);
  
      X11SD_DirectRenderNotify(env, xsdo);
  #endif /* !HEADLESS */
  }
  