/* * Copyright © 2009-2011 Rebecca G. Bettencourt / Kreative Software * <p> * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * <a href="http://www.mozilla.org/MPL/">http://www.mozilla.org/MPL/</a> * <p> * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the * License for the specific language governing rights and limitations * under the License. * <p> * Alternatively, the contents of this file may be used under the terms * of the GNU Lesser General Public License (the "LGPL License"), in which * case the provisions of LGPL License are applicable instead of those * above. If you wish to allow use of your version of this file only * under the terms of the LGPL License and not to allow others to use * your version of this file under the MPL, indicate your decision by * deleting the provisions above and replace them with the notice and * other provisions required by the LGPL License. If you do not delete * the provisions above, a recipient may use your version of this file * under either the MPL or the LGPL License. * @since PowerPaint 1.0 * @author Rebecca G. Bettencourt, Kreative Software */ package com.kreative.paint.format; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.awt.image.IndexColorModel; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import javax.imageio.ImageIO; import com.kreative.paint.Canvas; import com.kreative.paint.form.Form; import com.kreative.paint.io.Monitor; public class GIFFormat implements Format { public String getName() { return "GIF"; } public String getExpandedName() { return "Graphics Interchange Format"; } public String getExtension() { return "gif"; } public int getMacFileType() { return 0x47494620; } public int getMacResourceType() { return 0x47494620; } public long getDFFType() { return 0x496D616765474946L; } public MediaType getMediaType() { return MediaType.IMAGE; } public GraphicType getGraphicType() { return GraphicType.BITMAP; } public SizeType getSizeType() { return SizeType.ARBITRARY; } public ColorType getColorType() { return ColorType.INDEXED_256; } public AlphaType getAlphaType() { return AlphaType.OPAQUE_AND_TRANSPARENT; } public LayerType getLayerType() { return LayerType.FLAT; } public boolean onlyUponRequest() { return false; } public int usesMagic() { return 6; } public boolean acceptsMagic(byte[] start, long length) { try { DataInputStream in = new DataInputStream(new ByteArrayInputStream(start)); long magic = 0; magic = (magic << 16) | (in.readShort() & 0xFFFF); magic = (magic << 16) | (in.readShort() & 0xFFFF); magic = (magic << 16) | (in.readShort() & 0xFFFF); if (!(magic == 0x474946383761L || magic == 0x474946383961L)) return false; in.close(); return true; } catch (IOException ioe) { return false; } } public boolean acceptsExtension(String ext) { return ext.equalsIgnoreCase("gif"); } public boolean acceptsMacFileType(int type) { return type == 0x47494620 || type == 0x47494666; } public boolean acceptsMacResourceType(int type) { return type == 0x47494620 || type == 0x47494666; } public boolean acceptsDFFType(long type) { return type == 0x496D616765474946L || type == 0x496D672047494620L; } public boolean supportsRead() { for (String s : ImageIO.getReaderFormatNames()) { if (s.equalsIgnoreCase("gif")) return true; } return false; } public boolean usesReadOptionForm() { return false; } public Form getReadOptionForm() { return null; } public Canvas read(DataInputStream in, Monitor m) throws IOException { BufferedImage bi = ImageIO.read(in); if (bi == null) { throw new IOException(); } else { Canvas c = new Canvas(bi.getWidth(), bi.getHeight()); Graphics2D g = c.get(0).createPaintGraphics(); g.drawImage(bi, null, 0, 0); g.dispose(); return c; } } public boolean supportsWrite() { for (String s : ImageIO.getWriterFormatNames()) { if (s.equalsIgnoreCase("gif")) return true; } return false; } public boolean usesWriteOptionForm() { return false; } public Form getWriteOptionForm() { return null; } public int approximateFileSize(Canvas c) { return c.getWidth()*c.getHeight()/5; } public void write(Canvas c, DataOutputStream out, Monitor m) throws IOException { int w = c.getWidth(); int h = c.getHeight(); BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); Graphics2D g = bi.createGraphics(); c.paint(g); g.dispose(); int[] pixels = new int[w*h]; bi.getRGB(0, 0, w, h, pixels, 0, w); IndexColorModel cm = makeColorModel(pixels); BufferedImage bi2 = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_INDEXED, cm); Graphics2D g2 = bi2.createGraphics(); while (!g2.drawImage(bi, 0, 0, null)); g2.dispose(); ImageIO.write(bi2, "gif", out); } private static IndexColorModel makeColorModel(int[] pixels) { Map<Integer,Long> h1 = new HashMap<Integer,Long>(); for (int pixel : pixels) { int p = (pixel < 0) ? (pixel & 0xFFFFFF) : 0x01000000; if (h1.containsKey(p)) { h1.put(p, h1.get(p)+0x100000000L); } else { h1.put(p, p+0x100000000L); } } Long[] h2 = h1.values().toArray(new Long[0]); Arrays.sort(h2); int offset = (h2.length > 256) ? (h2.length-256) : 0; int length = (h2.length > 256) ? 256 : h2.length; int[] h3 = new int[length]; for (int i = 0, j = offset; i < length && j < h2.length; i++, j++) { h3[i] = h2[j].intValue(); } Arrays.sort(h3); byte[] r = new byte[256]; byte[] g = new byte[256]; byte[] b = new byte[256]; int t = -1; for (int k = 0; k < 256; k += length) { for (int i = 0, j = length-1; i < length && k+i < 256 && j >= 0; i++, j--) { if (h3[j] == 0x01000000) { r[k+i] = -1; g[k+i] = -1; b[k+i] = -1; t = i; } else { r[k+i] = (byte)((h3[j] >>> 16) & 0xFF); g[k+i] = (byte)((h3[j] >>> 8) & 0xFF); b[k+i] = (byte)((h3[j] >>> 0) & 0xFF); } } } if (t >= 0) { int nc; do { nc = (int)(Math.random() * 0x1000000) & 0xFFFFFF; } while (h1.containsKey(nc)); r[t] = (byte)((nc >>> 16) & 0xFF); g[t] = (byte)((nc >>> 8) & 0xFF); b[t] = (byte)((nc >>> 0) & 0xFF); return new IndexColorModel(8, 256, r, g, b, t); } else { return new IndexColorModel(8, 256, r, g, b); } } }