/* * Copyright (c) 2008, 2010, 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. */ package com.sun.lwuit; import java.util.Hashtable; /** * Implements a bitmap font that uses an image and sets of offsets to draw a font * with a given character set. * * @author Shai Almog */ class CustomFont extends Font { /** * Keep two colors in cache by default to allow faster selection colors */ private static final int COLOR_CACHE_SIZE = 20; private Hashtable colorCache = new Hashtable(); private String charsets; private int color; // package protected for the resource editor Image cache; /** * The offset in which to cut the character from the bitmap */ int[] cutOffsets; /** * The width of the character when drawing... this should not be confused with * the number of cutOffset[o + 1] - cutOffset[o]. They are completely different * since a character can be "wider" and "seep" into the next region. This is * especially true with italic characters all of which "lean" outside of their * bounds. */ int[] charWidth; private int imageWidth; private int imageHeight; private Object imageArrayRef; private int[] getImageArray() { if(imageArrayRef != null) { int[] a = (int[])Display.getInstance().extractHardRef(imageArrayRef); if(a != null) { return a; } } int[] a = cache.getRGBCached(); imageArrayRef = Display.getInstance().createSoftWeakRef(a); return a; } /** * Creates a bitmap font with the given arguments * * @param bitmap a transparency map in red and black that indicates the characters * @param cutOffsets character offsets matching the bitmap pixels and characters in the font * @param charWidth The width of the character when drawing... this should not be confused with * the number of cutOffset[o + 1] - cutOffset[o]. They are completely different * since a character can be "wider" and "seep" into the next region. This is * especially true with italic characters all of which "lean" outside of their * bounds. * @param charsets the set of characters in the font * @return a font object to draw bitmap fonts */ public CustomFont(Image bitmap, int[] cutOffsets, int[] charWidth, String charsets) { this.cutOffsets = cutOffsets; this.charWidth = charWidth; this.charsets = charsets; imageWidth = bitmap.getWidth(); imageHeight = bitmap.getHeight(); int[] imageArray = new int[imageWidth * imageHeight]; // default to black colored font bitmap.getRGB(imageArray, 0, 0, 0, imageWidth, imageHeight); for(int iter = 0 ; iter < imageArray.length ; iter++) { // extract the red component from the font image // shift the alpha 8 bits to the left // apply the alpha to the image imageArray[iter] = ((imageArray[iter] & 0xff0000) << 8); } cache = Image.createImage(imageArray, imageWidth, imageHeight); imageArrayRef = Display.getInstance().createSoftWeakRef(imageArray); } /** * @inheritDoc */ public int charWidth(char ch) { int i = charsets.indexOf(ch); if(i < 0) { return 0; } return charWidth[i]; } /** * @inheritDoc */ public int getHeight() { return imageHeight; } private boolean checkCacheCurrentColor(int newColor) { Integer currentColor = new Integer(color); Integer newColorKey = new Integer(newColor); if(colorCache.get(currentColor) == null){ colorCache.put(currentColor, Display.getInstance().createSoftWeakRef(cache)); } color = newColor; Object newCache = Display.getInstance().extractHardRef(colorCache.get(newColorKey)); if(newCache != null) { Image i = (Image)newCache; if(i != null) { cache = i; if(colorCache.size() > COLOR_CACHE_SIZE) { // remove a random cache element colorCache.remove(colorCache.keys().nextElement()); } return true; }else{ colorCache.remove(newColorKey); } } if(colorCache.size() > COLOR_CACHE_SIZE) { // remove a random cache element colorCache.remove(colorCache.keys().nextElement()); } return false; } private void initColor(Graphics g) { int newColor = g.getColor(); if(newColor != color && !checkCacheCurrentColor(newColor)) { color = newColor & 0xffffff; int[] imageArray = getImageArray(); for(int iter = 0 ; iter < imageArray.length ; iter++) { // extract the red component from the font image // shift the alpha 8 bits to the left // apply the alpha to the image imageArray[iter] = color | (imageArray[iter] & 0xff000000); } cache = Image.createImage(imageArray, imageWidth, imageHeight); } } /** * @inheritDoc */ void drawChar(Graphics g, char character, int x, int y) { int clipX = g.getClipX(); int clipY = g.getClipY(); int clipWidth = g.getClipWidth(); int clipHeight = g.getClipHeight(); int i = charsets.indexOf(character); if(i > -1) { initColor(g); // draw region is flaky on some devices, use setClip instead g.clipRect(x, y, charWidth[i], imageHeight); g.drawImage(cache, x - cutOffsets[i], y); //g.drawRegion(cache, cutOffsets[i], 0, charWidth[i], imageHeight, x, y); } // restore the clip g.setClip(clipX, clipY, clipWidth, clipHeight); } /** * @inheritDoc */ public void addContrast(byte value) { int[] imageArray = getImageArray(); for(int iter = 0 ; iter < imageArray.length ; iter++) { int alpha = (imageArray[iter] >> 24) & 0xff; if(alpha != 0) { alpha = Math.min(alpha + value, 255); imageArray[iter] = ((alpha << 24) & 0xff000000) | color; } } } /** * Override this frequently used method for a slight performance boost... * * @param g the component graphics * @param data the chars to draw * @param offset the offset to draw the chars * @param length the length of chars * @param x the x coordinate to draw the chars * @param y the y coordinate to draw the chars */ void drawChars(Graphics g, char[] data, int offset, int length, int x, int y) { if(Display.getInstance().isBidiAlgorithm()) { for(int i = offset ; i < length ; i++) { if(Display.getInstance().isRTL(data[i])) { String s = Display.getInstance().convertBidiLogicalToVisual(new String(data, offset, length)); data = s.toCharArray(); offset = 0; length = s.length(); break; } } } initColor(g); int clipX = g.getClipX(); int clipY = g.getClipY(); int clipWidth = g.getClipWidth(); int clipHeight = g.getClipHeight(); if(clipY <= y + getHeight() && clipY + clipHeight >= y) { char c; for ( int i = 0; i < length; i++ ) { c = data[offset+i]; int position = charsets.indexOf(c); if(position < 0) { continue; } // draw region is flaky on some devices, use setClip instead g.clipRect(x, y, charWidth[position], imageHeight); if(g.getClipWidth() > 0 && g.getClipHeight() > 0) { g.drawImage(cache, x - cutOffsets[position], y); } x += charWidth[position]; g.setClip(clipX, clipY, clipWidth, clipHeight); } } } /** * @inheritDoc */ public String getCharset() { return charsets; } /** * @inheritDoc */ public int charsWidth(char[] ch, int offset, int length){ int retVal = 0; for(int i=0; i<length; i++){ retVal += charWidth(ch[i + offset]); } return retVal; } /** * @inheritDoc */ public int substringWidth(String str, int offset, int len){ return charsWidth(str.toCharArray(), offset, len); } /** * @inheritDoc */ public int stringWidth(String str){ if( str==null || str.length()==0) return 0; return substringWidth(str, 0, str.length()); } /** * @inheritDoc */ public int getFace(){ return 0; } /** * @inheritDoc */ public int getSize(){ return 0; } /** * @inheritDoc */ public int getStyle() { return 0; } /** * @inheritDoc */ public boolean equals(Object o) { if(o == this) { return true; } if(o != null && o.getClass() == getClass()) { CustomFont f = (CustomFont)o; if(charsets.equals(f.charsets)) { for(int iter = 0 ; iter < cutOffsets.length ; iter++) { if(cutOffsets[iter] != f.cutOffsets[iter]) { return false; } } return true; } } return false; } }