Package org.jmol.g3d

Source Code of org.jmol.g3d.Text3D

/* $RCSfile$
* $Author: hansonr $
* $Date: 2009-12-23 01:08:56 +0100 (mer., 23 déc. 2009) $
* $Revision: 11978 $
*
* Copyright (C) 2003-2005  Miguel, Jmol Development, www.jmol.org
*
* Contact: jmol-developers@lists.sf.net
*
*  This library is free software; you can redistribute it and/or
*  modify it under the terms of the GNU Lesser General Public
*  License as published by the Free Software Foundation; either
*  version 2.1 of the License, or (at your option) any later version.
*
*  This library 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
*  Lesser General Public License for more details.
*
*  You should have received a copy of the GNU Lesser General Public
*  License along with this library; if not, write to the Free Software
*  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*/

package org.jmol.g3d;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.PixelGrabber;
import java.util.Hashtable;

import org.jmol.api.JmolRendererInterface;


/**
* implementation for text rendering
*<p>
* uses java fonts by rendering into an offscreen buffer.
* strings are rasterized and stored as a bitmap in an int[].
*<p>
* needs work
*
*
* @author Miguel, miguel@jmol.org
*/
public class Text3D {
  /*
    we have a few problems here
    a message is probably going to vary in size with z depth
    a message is probably going to be repeated by more than one atom
    fonts?
      just one?
      a restricted number?
      any font?
      if we want to support more than one then a fontindex is probably
      called for in order to prevent a hashtable lookup
    color - can be applied by the painter
    rep
      array of booleans - uncompressed
      array of bits - uncompressed - i like this
      some type of run-length, using bytes
  */
  private int height; // this height is just ascent + descent ... no reason for leading
  private int ascent;
  private int width;
  private int mapWidth;
  private int size;
  private int[] bitmap;
  private boolean isInvalid;
 
  public int getWidth() {
    return width;
  }
 
  public static int plot(int x, int y, int z, int argb, String text,
                         Font3D font3d, Graphics3D g3d,
                         JmolRendererInterface jmolRenderer, boolean antialias) {
    if (text.length() == 0)
      return 0;
    //System.out.println(x + "  " + y + " " + text);
    if (text.indexOf("<su") >= 0)
      return plotByCharacter(x, y, z, argb, text, font3d, g3d, jmolRenderer,
          antialias);
    int offset = font3d.fontMetrics.getAscent();
    //if (antialias)
      //offset += offset;
    y -= offset;

    //setColix has presumably been carried out for argb, and the two
    //are assumed to be both the same -- translucent or not.
    Text3D text3d = getText3D(x, y, g3d, text, font3d, antialias);
    if (text3d.isInvalid)
      return text3d.width;
    //TODO: text width/height are calculated 4x correct size here when antialiased.
    // this is wasteful, as it requires drawing larger than necessary images
    if (antialias && (argb & 0xC0C0C0) == 0) {
      // an interesting problem with antialiasing occurs if
      // the label is black or almost black.
      argb = argb | 0x040404;
    }
    if (jmolRenderer != null
        || (x < 0 || x + text3d.width > g3d.width || y < 0 || y + text3d.height > g3d.height))
      plotClipped(x, y, z, argb, g3d, jmolRenderer, text3d.mapWidth,
          text3d.height, text3d.bitmap);
    else
      plotUnclipped(x, y, z, argb, g3d, text3d.mapWidth, text3d.height,
          text3d.bitmap);
    return text3d.width;
  }

  public static void plotImage(int x, int y, int z, Image image,
                               Graphics3D g3d,
                               JmolRendererInterface jmolRenderer,
                               boolean antialias, int argbBackground,
                               int width, int height) {
    boolean isBackground = (x == Integer.MIN_VALUE);
    int width0 = image.getWidth(null);
    int height0 = image.getHeight(null);
    int bgcolor = (isBackground ? g3d.bgcolor : argbBackground);
    /*
    boolean haveTranslucent = false;
    PixelGrabber pg1 = new PixelGrabber(image, 0, 0, width0, height0, true);
    if (pg1.getColorModel().hasAlpha())
      try {
        pg1.grabPixels();
        int[] buffer = (int[]) pg1.getPixels();
        for (int i = 0; i < buffer.length; i++)
          if ((buffer[i] & 0xFF00000) != 0xFF000000) {
            haveTranslucent = true;
            break;
          }
        System.out.println(buffer.length + " " + haveTranslucent + " "
            + pg1.getColorModel().hasAlpha());
      } catch (InterruptedException e) {
        // impossible?
        return;
      }
      */
    if (isBackground) {
      x = 0;
      z = Integer.MAX_VALUE - 1;
      width = g3d.width;
      height = g3d.height;
    }
    if (x + width <= 0 || x >= g3d.width || y + height <= 0 || y >= g3d.height)
      return;
    g3d.platform.checkOffscreenSize(width, height);
    Graphics g = g3d.platform.gOffscreen;
    if (g instanceof Graphics2D) {
      ((Graphics2D)g).setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, 1.0f));
      g.setColor(isBackground ? new Color(bgcolor) : new Color(0, 0, 0, 0));
      g.fillRect(0, 0, width, height);
      ((Graphics2D)g).setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f));
      g.drawImage(image, 0, 0, width, height, 0, 0, width0, height0, null);
    } else {
      g.clearRect(0, 0, width, height);
      g.drawImage(image, 0, 0, width, height, 0, 0, width0, height0, null);
    }
    PixelGrabber pixelGrabber = new PixelGrabber(g3d.platform.imageOffscreen,
        0, 0, width, height, true);
    try {
      pixelGrabber.grabPixels();
    } catch (InterruptedException e) {
      // impossible?
      return;
    }
    int[] buffer = (int[]) pixelGrabber.getPixels();
/*   
    int n = 0;
    for (int i = 0; i < buffer.length; i++) {
      if ((buffer[i] & 0xFF000000) != 0xFF000000) {
        // System.out.println("testing " + i + " " + buffer[i]);
        n++;
      }
    }
    System.out.println(n + " transparent argbBackground=" + argbBackground);
*/
    if (jmolRenderer != null
        || (x < 0 || x + width > g3d.width || y < 0 || y + height > g3d.height))
      plotImageClipped(x, y, z, g3d, jmolRenderer, width, height, buffer,
          bgcolor);
    else
      plotImageUnClipped(x, y, z, g3d, width, height, buffer, bgcolor);
    return;
  }

  private static void plotImageClipped(int x, int y, int z, Graphics3D g3d,
                                       JmolRendererInterface jmolRenderer,
                                       int width, int height,
                                       int[] buffer, int bgcolor) {
    if (jmolRenderer == null)
      jmolRenderer = g3d;
    for (int i = 0, offset = 0; i < height; i++) {
      for (int j = 0; j < width; j++) {
        int argb = buffer[offset++];
        if (argb != bgcolor && (argb & 0xFF000000) == 0xFF000000)
          jmolRenderer.plotPixelClippedNoSlab(argb, x + j, y + i, z);
        else if (argb == 0 && bgcolor != 0)
          jmolRenderer.plotPixelClippedNoSlab(bgcolor, x + j, y + i, z);
      }
    }
  }

  private static void plotImageUnClipped(int x, int y, int z, Graphics3D g3d,
                                         int textWidth, int textHeight,
                                         int[] buffer, int bgcolor) {
    int[] zbuf = g3d.zbuf;
    int renderWidth = g3d.width;
    int pbufOffset = y * renderWidth + x;
    int i = 0;
    int j = 0;
    int offset = 0;
    while (i < textHeight) {
      while (j < textWidth) {
        if (z < zbuf[pbufOffset]) {
          int argb = buffer[offset];
          if (argb != bgcolor && (argb & 0xFF000000) == 0xFF000000)
            g3d.addPixel(pbufOffset, z, argb);
          else if (argb == 0 && bgcolor != 0)
            g3d.addPixel(pbufOffset, z, bgcolor);
        }
        ++offset;
        ++j;
        ++pbufOffset;
      }
      ++i;
      j -= textWidth;
      pbufOffset += (renderWidth - textWidth);
    }
  }

  private static int plotByCharacter(int x, int y, int z, int argb,
                                      String text, Font3D font3d,
                                      Graphics3D g3d, JmolRendererInterface jmolRenderer,
                                      boolean antialias) {
    //int subscale = 1; //could be something less than that
    int w = 0;
    int len = text.length();
    int suboffset = (int)(font3d.fontMetrics.getHeight() * 0.25);
    int supoffset = -(int)(font3d.fontMetrics.getHeight() * 0.3);
    for (int i = 0; i < len; i++) {
      if (text.charAt(i) == '<') {
        if (i + 4 < len && text.substring(i, i + 5).equals("<sub>")) {
          i += 4;
          y += suboffset;
          continue;
        }
        if (i + 4 < len && text.substring(i, i + 5).equals("<sup>")) {
          i += 4;
          y += supoffset;
          continue;
        }
        if (i + 5 < len  && text.substring(i, i + 6).equals("</sub>")) {
          i += 5;
          y -= suboffset;
          continue;
        }
        if (i + 5 < len  && text.substring(i, i + 6).equals("</sup>")) {
          i += 5;
          y -= supoffset;
          continue;
        }
      }
      int width = plot(x + w, y, z, argb, text.substring(i, i + 1), font3d,
          g3d, jmolRenderer, antialias);
      w += width;
    }
    //System.out.println("w=" + w);
    return w;
  }
 
  private static void plotUnclipped(int x, int y, int z, int argb,
                                    Graphics3D g3d, int textWidth,
                                    int textHeight, int[] bitmap) {
    int offset = 0;
    int shiftregister = 0;
    int i = 0, j = 0;
    int[] zbuf = g3d.zbuf;
    int renderWidth = g3d.width;
    int pbufOffset = y * renderWidth + x;
    while (i < textHeight) {
      while (j < textWidth) {
        if ((offset & 31) == 0)
          shiftregister = bitmap[offset >> 5];
        if (shiftregister == 0) {
          int skip = 32 - (offset & 31);
          j += skip;
          offset += skip;
          pbufOffset += skip;
          continue;
        }
        if (shiftregister < 0 && z < zbuf[pbufOffset])
          g3d.addPixel(pbufOffset, z, argb);
        shiftregister <<= 1;
        ++offset;
        ++j;
        ++pbufOffset;
      }
      while (j >= textWidth) {
        ++i;
        j -= textWidth;
        pbufOffset += (renderWidth - textWidth);
      }
    }
  }
 
  private static void plotClipped(int x, int y, int z, int argb,
                                  Graphics3D g3d,
                                  JmolRendererInterface jmolRenderer,
                                  int textWidth, int textHeight, int[] bitmap) {
    if (jmolRenderer == null)
      jmolRenderer = g3d;
    int offset = 0;
    int shiftregister = 0;
    int i = 0, j = 0;
    while (i < textHeight) {
      while (j < textWidth) {
        if ((offset & 31) == 0)
          shiftregister = bitmap[offset >> 5];
        if (shiftregister == 0) {
          int skip = 32 - (offset & 31);
          j += skip;
          offset += skip;
          continue;
        }
        if (shiftregister < 0)
          jmolRenderer.plotPixelClippedNoSlab(argb, x + j, y + i, z);
        shiftregister <<= 1;
        ++offset;
        ++j;
      }
      while (j >= textWidth) {
        ++i;
        j -= textWidth;
      }
    }
  }

  private Text3D(String text, Font3D font3d,
                 boolean antialias) {
    FontMetrics fontMetrics = font3d.fontMetrics;
    ascent = fontMetrics.getAscent();
    height = ascent + fontMetrics.getDescent();
    width = fontMetrics.stringWidth(text);
    if (width == 0)
      return;
    //System.out.println(text + " " + antialias + " "  + ascent + " " + height + " " + width );
    mapWidth = width;
    size = mapWidth * height;
  }

  private void renderOffscreen(String text, Font3D font3d, Platform3D platform,
                               boolean antialias) {
    Graphics g = platform.gOffscreen;
    g.setColor(Color.black);
    g.fillRect(0, 0, mapWidth, height);
    g.setColor(Color.white);
    g.setFont(font3d.font);
    g.drawString(text, 0, ascent);
  }

  private void rasterize(Platform3D platform, boolean antialias) {
   
    PixelGrabber pixelGrabber = new PixelGrabber(platform.imageOffscreen, 0, 0,
                                                 mapWidth, height, true);
    try {
      pixelGrabber.grabPixels();
    } catch (InterruptedException e) {
      // impossible?
      return;
    }
    int pixels[] = (int[])pixelGrabber.getPixels();

    int bitmapSize = (size + 31) >> 5;
    bitmap = new int[bitmapSize];

    int offset, shifter;
    for (offset = shifter = 0; offset < size; ++offset, shifter <<= 1) {
      if ((pixels[offset] & 0x00FFFFFF) != 0)
        shifter |= 1;
      if ((offset & 31) == 31)
        bitmap[offset >> 5] = shifter;
    }
    if ((offset & 31) != 0) {
      shifter <<= 31 - (offset & 31);
      bitmap[offset >> 5] = shifter;
    }
    /*      // error checking
      // shifter error checking
      boolean[] bits = new boolean[size];
      for (int i = 0; i < size; ++i)
        bits[i] = (pixels[i] & 0x00FFFFFF) != 0;
      //
      for (offset = 0; offset < size; ++offset, shifter <<= 1) {
        if ((offset & 31) == 0)
          shifter = bitmap[offset >> 5];
        if (shifter < 0) {
          if (!bits[offset]) {
            Logger.debug("false positive @" + offset);
            Logger.debug("size = " + size);
          }
        } else {
          if (bits[offset]) {
            Logger.debug("false negative @" + offset);
            Logger.debug("size = " + size);
          }
        }
      }
      // error checking
    */
  }

  private final static Hashtable htFont3d = new Hashtable();
  private final static Hashtable htFont3dAntialias = new Hashtable();
  private static boolean working;
 
  public synchronized static void clearFontCache() {
    if (working)
      return;
    htFont3d.clear();
    htFont3dAntialias.clear();
  }
 
  // FIXME mth
  // we have a synchronization issue/race condition  here with multiple
  // so only one Text3D can be generated at a time

  // Jmol 11.7.8: caching and rasterization only carried out if the font is
  // valid -- that is in the rectangle to be plotted.
 
  private synchronized static Text3D getText3D(int x, int y, Graphics3D g3d,
                                               String text, Font3D font3d,
                                               boolean antialias) {
    working = true;
    Hashtable ht = (antialias ? htFont3dAntialias : htFont3d);
    Hashtable htForThisFont = (Hashtable) ht.get(font3d);
    Text3D text3d = null;
    boolean newFont = false;
    boolean newText = false;
    if (htForThisFont != null) {
      text3d = (Text3D) htForThisFont.get(text);
    } else {
      htForThisFont = new Hashtable();
      newFont = true;
    }
    if (text3d == null) {
      text3d = new Text3D(text, font3d, antialias);
      newText = true;
    }
    text3d.isInvalid = (text3d.width == 0 || x + text3d.width <= 0
        || x >= g3d.width || y + text3d.height <= 0 || y >= g3d.height);
    if (text3d.isInvalid)
      return text3d;
    if (newFont)
      ht.put(font3d, htForThisFont);
    if (newText) {
      //System.out.println(text + " " + x + " " + text3d.width + " " + g3d.width + " " + y + " " + g3d.height);
      text3d.setBitmap(text, font3d, g3d.platform, antialias);
      htForThisFont.put(text, text3d);
    }
    working = false;
    return text3d;
  }

  private void setBitmap(String text, Font3D font3d, Platform3D platform, boolean antialias) {
    //System.out.println(text + " height=" + height + " setBitmap width= " + width);
    platform.checkOffscreenSize(mapWidth, height);
    renderOffscreen(text, font3d, platform, antialias);
    rasterize(platform, antialias);
  }

}
TOP

Related Classes of org.jmol.g3d.Text3D

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.