/* * $Id$ * * Copyright (C) 2003-2015 JNode.org * * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.jnode.awt.font.renderer; import java.awt.Dimension; import java.awt.Rectangle; import java.awt.Shape; import java.awt.Transparency; import java.awt.color.ColorSpace; import java.awt.geom.AffineTransform; import java.awt.geom.Area; import java.awt.geom.Point2D; import java.awt.image.ComponentColorModel; import java.awt.image.DataBuffer; import java.awt.image.WritableRaster; import java.util.BitSet; /** * @author Ewout Prangsma (epr@users.sourceforge.net) */ public class GlyphRenderer { private final double MASTER_HEIGHT = 128.0; private final SummedAreaTable sumAreaTable; private final double minX; private final double minY; private static final String BITS_NAME = GlyphRenderer.class.getName() + "#BITS"; /** * Initialize this instance. * * @param shape */ public GlyphRenderer(RenderContext ctx, Shape shape, double ascent) { final Master master = createMaster(ctx, shape, ascent); this.minX = master.minX; this.minY = master.minY; this.sumAreaTable = SummedAreaTable.create(master.bits, master.width, master.height); } /** * Create a raster that can be used in {@link #createGlyphRaster(WritableRaster, double)}. * * @param width * @param height * @return the raster */ public static final WritableRaster createRaster(int width, int height) { final ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY); final int[] nBits = {8}; final ComponentColorModel cm = new ComponentColorModel(cs, nBits, false, true, Transparency.OPAQUE, DataBuffer.TYPE_BYTE); return cm.createCompatibleWritableRaster(width, height); } /** * Create a raster for the given at a given font-size. * * @param fontSize * @return The size of the created raster. */ public final Dimension createGlyphRaster(WritableRaster dst, double fontSize) { final double scale = MASTER_HEIGHT / fontSize; final int height = (int) ((((double) sumAreaTable.getHeight()) / scale) + 0.5); final int width = (int) ((((double) sumAreaTable.getWidth()) / scale) + 0.5); if (dst.getWidth() < width) { throw new IllegalArgumentException("Raster width (" + dst.getWidth() + ") is too small (" + width + ")"); } if (dst.getHeight() < height) { throw new IllegalArgumentException("Raster height (" + dst.getHeight() + ") is too small (" + height + ")"); } final int si = (int) (scale + 0.5); int ypos = (height - 1) * si; for (int y = height - 1; y >= 0; y--) { int xpos = (width - 1) * si; for (int x = width - 1; x >= 0; x--) { final int v = sumAreaTable.getIntensity8b(xpos, ypos, si, si); dst.setSample(x, y, 0, v); xpos -= si; } ypos -= si; } return new Dimension(width, height); } /** * Gets the minX, minY location that was removed when the glyph was rendered * into the raster. * * @param fontSize * @return the location as a Point2D */ public Point2D getMinLocation(double fontSize) { final double scale = MASTER_HEIGHT / fontSize; return new Point2D.Double(minX / scale, -minY / scale); } /** * Creates a master shape of the letter. * * @param shape * @return the master shape */ private final Master createMaster(RenderContext ctx, Shape shape, double ascent) { final Area area = new Area(shape); final double scale = MASTER_HEIGHT / ascent; area.transform(AffineTransform.getScaleInstance(scale, scale)); final Rectangle bounds = area.getBounds(); // System.out.println("createMaster bounds " + bounds); // area.transform(AffineTransform.getTranslateInstance(-bounds.getMinX(), // -bounds.getMinY())); // bounds = area.getBounds(); final int minX = (int) (bounds.getMinX() - 0.5); final int maxX = (int) (bounds.getMaxX() + 0.5); final int minY = (int) (bounds.getMinY() - 0.5); final int maxY = (int) (bounds.getMaxY() + 0.5); final int width = maxX - minX; final int height = maxY - minY; BitSet bits = (BitSet) ctx.getObject(BITS_NAME); if (bits == null) { bits = new BitSet(width * height); ctx.setObject(BITS_NAME, bits); } else { bits.clear(); } int ofs = 0; for (int y = maxY; y > minY; y--) { for (int x = minX; x < maxX; x++) { if (area.contains(x, y)) { bits.set(ofs); } ofs++; } } return new Master(bits, width, height, minX, minY); } public static class Master { public final BitSet bits; public final int width; public final int height; public final int minX; public final int minY; /** * @param bits * @param width * @param height */ public Master(BitSet bits, int width, int height, int minX, int minY) { super(); this.bits = bits; this.width = width; this.height = height; this.minX = minX; this.minY = minY; } } }