/* * Copyright (C) 2007, 2008 Quadduc <quadduc@gmail.com> * Copyright (C) 2007, 2011 IsmAvatar <IsmAvatar@gmail.com> * Copyright (C) 2007 Clam <clamisgood@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.main; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Image; import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.image.BufferedImage; import java.awt.image.FilteredImageSource; import java.awt.image.ImageFilter; import java.awt.image.ImageProducer; import java.awt.image.RGBImageFilter; import java.awt.image.WritableRaster; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Iterator; import javax.imageio.ImageIO; import javax.imageio.ImageReader; import javax.imageio.spi.IIORegistry; import javax.imageio.stream.ImageInputStream; import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.ImageIcon; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.SwingUtilities; import org.lateralgm.components.CustomFileChooser; import org.lateralgm.components.impl.CustomFileFilter; import org.lateralgm.components.visual.FileChooserImagePreview; import org.lateralgm.file.iconio.BitmapDescriptor; import org.lateralgm.file.iconio.ICOFile; import org.lateralgm.file.iconio.ICOImageReaderSPI; import org.lateralgm.file.iconio.WBMPImageReaderSpiFix; import org.lateralgm.jedit.SyntaxStyle; import org.lateralgm.messages.Messages; import org.lateralgm.resources.Resource; import org.lateralgm.resources.ResourceReference; import com.sun.imageio.plugins.wbmp.WBMPImageReaderSpi; public final class Util { private static final InvokeOnceRunnable IOR = new InvokeOnceRunnable(); private Util() { } public static CustomFileChooser imageFc = null; public static void tweakIIORegistry() { IIORegistry reg = IIORegistry.getDefaultInstance(); reg.registerServiceProvider(new ICOImageReaderSPI()); reg.deregisterServiceProvider(reg.getServiceProviderByClass(WBMPImageReaderSpi.class)); reg.registerServiceProvider(new WBMPImageReaderSpiFix()); } public static String urlEncode(String s) { try { return URLEncoder.encode(s,"UTF-8"); } catch (UnsupportedEncodingException e) { throw new Error(e); } } public static String urlDecode(String s) { try { return URLDecoder.decode(s,"UTF-8"); } catch (UnsupportedEncodingException e) { throw new Error(e); } } public static ByteArrayOutputStream readFully(InputStream in) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[4096]; // Read in the bytes int numRead = 0; while ((numRead = in.read(buffer)) >= 0) baos.write(buffer,0,numRead); // Close the input stream and return bytes return baos; } public static Rectangle stringToRectangle(String s, Rectangle defaultValue) { if (s == null) return defaultValue; String[] sa = s.split(" +"); if (sa.length != 4) return defaultValue; int[] ia = new int[4]; for (int i = 0; i < 4; i++) try { ia[i] = Integer.parseInt(sa[i]); } catch (NumberFormatException e) { return defaultValue; } return new Rectangle(ia[0],ia[1],ia[2],ia[3]); } public static String rectangleToString(Rectangle r) { return String.format("%d %d %d %d",r.x,r.y,r.width,r.height); } public static SyntaxStyle stringToSyntaxStyle(String s, SyntaxStyle defaultValue) { String[] a; Color c; try { a = s.split(" ",2); c = new Color(Integer.valueOf(a[0],16)); } catch (NullPointerException npe) { return defaultValue; } catch (NumberFormatException nfe) { return defaultValue; } boolean i = false, b = false; if (a.length > 1) { i = a[1].matches("(?i).*\\bitalic\\b.*"); b = a[1].matches("(?i).*\\bbold\\b.*"); } return new SyntaxStyle(c,i,b); } public static BufferedImage toBufferedImage(Image image) { if (image instanceof BufferedImage) return (BufferedImage) image; // This code ensures that all the pixels in the image are loaded image = new ImageIcon(image).getImage(); BufferedImage bimage = new BufferedImage(image.getWidth(null),image.getHeight(null), BufferedImage.TYPE_INT_ARGB); Graphics g = bimage.createGraphics(); g.drawImage(image,0,0,null); g.dispose(); return bimage; } public static BufferedImage getTransparentIcon(BufferedImage i) { if (i == null) return null; final int t = i.getRGB(0,i.getHeight() - 1) & 0x00FFFFFF; ImageFilter filter = new RGBImageFilter() { public int filterRGB(int x, int y, int rgb) { if ((rgb & 0x00FFFFFF) == t) return t; return rgb; } }; ImageProducer ip = new FilteredImageSource(i.getSource(),filter); return toBufferedImage(Toolkit.getDefaultToolkit().createImage(ip)); } /** * Shows a JFileChooser with file filters for all currently registered instances of * ImageReaderSpi. * * @return The selected image, or null if one is not chosen */ public static BufferedImage getValidImage() { BufferedImage[] img = getValidImages(); if (img == null || img.length == 0) return null; return img[0]; } public static BufferedImage[] getValidImages() { if (imageFc == null) { imageFc = new CustomFileChooser("/org/lateralgm","LAST_IMAGE_DIR"); imageFc.setAccessory(new FileChooserImagePreview(imageFc)); String[] exts = { "jpg","bmp","tif","jpeg","wbmp","png","ico","TIF","TIFF","gif","tiff" }; if (LGM.javaVersion >= 10600) exts = ImageIO.getReaderFileSuffixes(); for (int i = 0; i < exts.length; i++) exts[i] = "." + exts[i]; //$NON-NLS-1$ String allSpiImages = Messages.getString("Util.ALL_SPI_IMAGES"); //$NON-NLS-1$ CustomFileFilter filt = new CustomFileFilter(allSpiImages,exts); imageFc.addChoosableFileFilter(filt); for (String element : exts) { imageFc.addChoosableFileFilter(new CustomFileFilter(Messages.format("Util.FILES", //$NON-NLS-1$ element),element)); } imageFc.setFileFilter(filt); } if (imageFc.showOpenDialog(LGM.frame) == JFileChooser.APPROVE_OPTION) { try { ImageInputStream in = ImageIO.createImageInputStream(imageFc.getSelectedFile()); Iterator<ImageReader> it = ImageIO.getImageReaders(in); ImageReader reader = it.next(); reader.setInput(in); int count = reader.getNumImages(true); BufferedImage[] img = new BufferedImage[count]; for (int i = 0; i < count; i++) img[i] = reader.read(i); //TODO: Gif overlay support (as GM already does) return img; } catch (Throwable t) { String msg = Messages.format("Util.ERROR_LOADING",imageFc.getSelectedFile()); //$NON-NLS-1$ String title = Messages.getString("Util.ERROR_TITLE"); //$NON-NLS-1$ JOptionPane.showMessageDialog(LGM.frame,msg,title,JOptionPane.ERROR_MESSAGE); t.printStackTrace(); } } return null; } public static BufferedImage cloneImage(BufferedImage bi) { if (bi == null) return null; //clone the raster WritableRaster or = bi.getRaster(); WritableRaster nr = or.createCompatibleWritableRaster(); nr.setRect(or); //construct with cloned raster, assume it has no special properties return new BufferedImage(bi.getColorModel(),or,bi.isAlphaPremultiplied(),null); } public static Color convertGmColor(int col) { return new Color(col & 0xFF,(col & 0xFF00) >> 8,(col & 0xFF0000) >> 16); } public static int getGmColor(Color col) { return col.getRed() | col.getGreen() << 8 | col.getBlue() << 16; } public static Component addDim(Container container, Component comp, int width, int height) { comp.setPreferredSize(new Dimension(width,height)); return container.add(comp); } public static JPanel makeRadioPanel(String paneTitle, int width, int height) { JPanel panel = makeTitledPanel(paneTitle,width,height); panel.setLayout(new BoxLayout(panel,BoxLayout.Y_AXIS)); return panel; } public static JPanel makeTitledPanel(String paneTitle, int width, int height) { JPanel panel = new JPanel(); panel.setBorder(BorderFactory.createTitledBorder(paneTitle)); Dimension newSize = new Dimension(width,height); panel.setPreferredSize(newSize); panel.setMaximumSize(newSize); panel.setMinimumSize(newSize); return panel; } public static <R extends Resource<R,?>>R deRef(ResourceReference<R> ref) { return ref == null ? null : ref.get(); } public static int gcd(int a, int b) { while (b != 0) { int c = a % b; a = b; b = c; } return a; } /** * Integer division with rounding towards negative infinity. */ public static int negDiv(int a, int b) { return a >= 0 ? a / b : ~(~a / b); } public static void invokeOnceLater(Runnable r) { IOR.add(r); } private static class InvokeOnceRunnable implements Runnable { private final ArrayList<Runnable> queue = new ArrayList<Runnable>(); private boolean inDispatcher = false; public synchronized void add(Runnable r) { if (queue.contains(r)) return; queue.add(r); if (!inDispatcher) { SwingUtilities.invokeLater(this); inDispatcher = true; } } public void run() { Runnable[] q; synchronized (this) { inDispatcher = false; q = new Runnable[queue.size()]; q = queue.toArray(q); queue.clear(); } for (Runnable r : q) r.run(); } } /** * Makes an icon suitable for embedding into a GM runner * of the given version. Before the compilation process was improved, * the icon was written by overwriting a placeholder of fixed size. * Any icon greater than this size (32x32@32bpp) will usually overflow * onto the resource table of the exe, causing a crash. If required, * this function will do its best to choose the image with the best resolution * and colour depth possible, discarding all the other images. * * @param ico the icon to (possibly) modify * @param ver the version to make the icon suitable for */ public static void fixIcon(ICOFile ico, int ver) { //Preference weighting: //32x32 = 3, 16x16 = 1, anything else = -9 //32bpp = 3, 24bpp = 2, 8bpp = 1, >0 bpp = 0, anything else = -9 if (ver < 800) { byte[] weights = new byte[ico.getImageCount()]; int i = 0; for (BitmapDescriptor bmd : ico.getDescriptors()) { int width = bmd.getWidth(); if (width == 32) weights[i] += 3; else if (width == 16) weights[i]++; else weights[i] -= 9; int bpp = bmd.getBPP(); if (bpp == 32) weights[i] += 3; if (bpp == 24) weights[i] += 2; if (bpp == 8) weights[i]++; else if (bpp <= 0) weights[i] -= 9; i++; } int maxind = 0; int maxweight = 0; for (i = 0; i < weights.length; i++) if (weights[i] > maxweight) { maxweight = weights[i]; maxind = i; } BitmapDescriptor bmd = ico.getDescriptor(maxind); ico.getDescriptors().clear(); ico.getDescriptors().add(bmd); } } }