/* * Copyright (C) 2009 Quadduc <quadduc@gmail.com> * * This file is part of LateralGM. * LateralGM is free software and comes with ABSOLUTELY NO WARRANTY. * See LICENSE for details. */ package org.lateralgm.ui.swing.visuals; import static org.lateralgm.main.Util.negDiv; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Transparency; import java.awt.image.BufferedImage; import java.lang.ref.SoftReference; import java.util.Arrays; import java.util.WeakHashMap; public class GridVisual implements Visual { private static final Color GRID_DARK = new Color(0,0,0,96); private static final Color GRID_BRIGHT = new Color(255,255,255,96); private static final WeakHashMap<GraphicsConfiguration,SoftReference<LineImageData>> IMAGE_DATA; static { IMAGE_DATA = new WeakHashMap<GraphicsConfiguration,SoftReference<LineImageData>>(4); } private boolean rhombic; private int width, height; /* * Drawing grid lines with alpha appears to be incredibly slow (at least for a 16x16 grid), * so for grid sizes that aren't too large, it is drawn to an image which is then repeated over * the screen. */ private BufferedImage gridImage; private LineImageData imageData; public GridVisual(boolean r, int w, int h) { rhombic = r; width = w; height = h; } public void setRhombic(boolean r) { if (r == rhombic) return; rhombic = r; flush(r); } public void setWidth(int w) { if (width == w) return; width = w; flush(false); } public void setHeight(int h) { if (height == h) return; height = h; flush(false); } public void flush(boolean full) { if (gridImage != null) gridImage.flush(); gridImage = null; if (full) imageData = null; } private void paintGrid(Graphics g, int x0, int y0, int x1, int y1) { boolean rx = width >= 2; boolean ry = height >= 2; if (rhombic) { ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); for (int y = negDiv(y0,height) * height; y <= y1; y += height) for (int x = negDiv(x0,width) * width; x <= x1; x += width) { int cx0 = x + (width >> 1); int cy0 = y + (height >> 1); int cx1 = x + (width + 1 >> 1); int cy1 = y + (height + 1 >> 1); g.setColor(GRID_DARK); g.drawLine(x + 1,cy1,cx0,y + height - 1); g.drawLine(cx1,y + height - 1,x + width - 1,cy1); g.drawLine(x + width,cy0 - 1,cx1 + 1,y); g.drawLine(cx0 - 1,y,x,cy0 - 1); g.setColor(GRID_BRIGHT); g.drawLine(x + width - 1,cy0,cx1,y + 1); g.drawLine(cx0,y + 1,x + 1,cy0); g.drawLine(x,cy1 + 1,cx0 - 1,y + height); g.drawLine(cx1 + 1,y + height,x + width,cy1 + 1); } } else { GraphicsConfiguration gc = g instanceof Graphics2D ? ((Graphics2D) g).getDeviceConfiguration() : null; if (imageData == null || !imageData.configuration.equals(gc)) { SoftReference<LineImageData> idr = IMAGE_DATA.get(gc); imageData = idr == null ? null : idr.get(); if (imageData == null) { imageData = new LineImageData(gc); IMAGE_DATA.put(gc,new SoftReference<LineImageData>(imageData)); } } if (!ry) for (int x = negDiv(x0,width) * width; x <= x1; x += width) imageData.paintVertical(g,x - 1,y0,y1 - y0); else if (!rx) for (int y = negDiv(y0,height) * height; y <= y1; y += height) imageData.paintHorizontal(g,x0,y - 1,x1 - x0,false); else for (int y = negDiv(y0,height) * height; y <= y1; y += height) for (int x = negDiv(x0,width) * width; x <= x1; x += width) { imageData.paintHorizontal(g,x - 1,y - 1,Math.min(width,x1 - x + 1),true); imageData.paintVertical(g,x - 1,y + 1,Math.min(height - 2,y1 - y - 1)); } } } public void paint(Graphics g) { boolean rx = width >= 2; boolean ry = height >= 2; if (!rx && !ry || (rhombic && (!rx || !ry))) return; int iw = rx ? width * ((48 + width - 1) / width) : 64; int ih = ry ? height * ((48 + height - 1) / height) : 64; if (gridImage == null && (rx && ry ? (width * height <= (rhombic ? 65536 : 9216)) : (rx ? width : height) < 16)) { GraphicsConfiguration gc = g instanceof Graphics2D ? ((Graphics2D) g).getDeviceConfiguration() : null; gridImage = gc == null ? new BufferedImage(iw,ih,BufferedImage.TYPE_INT_ARGB) : gc.createCompatibleImage(iw,ih,Transparency.TRANSLUCENT); Graphics2D g2 = gridImage.createGraphics(); paintGrid(g2,0,0,iw,ih); } Rectangle clip = g.getClipBounds(); int x0 = clip.x; int x1 = x0 + clip.width; int y0 = clip.y; int y1 = y0 + clip.height; if (gridImage != null) for (int y = negDiv(y0,ih) * ih; y <= y1; y += ih) for (int x = negDiv(x0,iw) * iw; x <= x1; x += iw) g.drawImage(gridImage,x,y,null); else paintGrid(g,x0,y0,x1,y1); } class LineImageData { private final BufferedImage horizSub, horizontal, vertical; public final GraphicsConfiguration configuration; public LineImageData(GraphicsConfiguration gc) { configuration = gc; if (gc != null) { horizontal = gc.createCompatibleImage(130,2,Transparency.TRANSLUCENT); vertical = gc.createCompatibleImage(2,128,Transparency.TRANSLUCENT); } else { horizontal = new BufferedImage(130,2,BufferedImage.TYPE_INT_ARGB); vertical = new BufferedImage(2,128,BufferedImage.TYPE_INT_ARGB); } int[] rgba = new int[128]; Arrays.fill(rgba,GRID_DARK.getRGB()); horizontal.setRGB(0,0,rgba[0]); horizontal.setRGB(2,0,128,1,rgba,0,128); vertical.setRGB(0,0,1,128,rgba,0,1); Arrays.fill(rgba,GRID_BRIGHT.getRGB()); horizontal.setRGB(1,1,rgba[0]); horizontal.setRGB(2,1,128,1,rgba,0,128); vertical.setRGB(1,0,1,128,rgba,0,1); horizSub = horizontal.getSubimage(2,0,128,2); } public void paintHorizontal(Graphics g, int x, int y, int l, boolean start) { if (start) { if (l >= 130) g.drawImage(horizontal,x,y,null); else g.drawImage(horizontal,x,y,x + l,y + 2,0,0,l,2,null); } int t; for (t = start ? 130 : 0; t <= l - 128; t += 128) g.drawImage(horizSub,x + t,y,null); if (t < l) g.drawImage(horizSub,x + t,y,x + l,y + 2,0,0,l - t,2,null); } public void paintVertical(Graphics g, int x, int y, int l) { int t; for (t = 0; t <= l - 128; t += 128) g.drawImage(vertical,x,y + t,null); if (t < l) g.drawImage(vertical,x,y + t,x + 2,y + l,0,0,2,l - t,null); } } }