/* * 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.Color; import java.awt.Graphics2D; import java.awt.GridLayout; import java.awt.image.BufferedImage; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; import java.io.IOException; import java.io.RandomAccessFile; import javax.swing.BorderFactory; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import com.kreative.paint.Canvas; import com.kreative.paint.form.BooleanOption; import com.kreative.paint.form.Form; import com.kreative.paint.form.IntegerEnumOption; import com.kreative.paint.io.Monitor; public class SICFormat implements Format { public String getName() { return "Sabine Icon"; } public String getExpandedName() { return "Sabine Icon"; } public String getExtension() { return "sic"; } public int getMacFileType() { return 0x53494320; } public int getMacResourceType() { return 0x53494320; } public long getDFFType() { return 0x49636F6E20534943L; } public MediaType getMediaType() { return MediaType.IMAGE; } public GraphicType getGraphicType() { return GraphicType.BITMAP; } public SizeType getSizeType() { return SizeType.ARBITRARY; } public ColorType getColorType() { return ColorType.INDEXED_OTHER; } public AlphaType getAlphaType() { return AlphaType.OPAQUE_AND_TRANSPARENT; } public LayerType getLayerType() { return LayerType.FLAT; } public boolean onlyUponRequest() { return false; } public int usesMagic() { return 0; } public boolean acceptsMagic(byte[] start, long length) { return false; } public boolean acceptsExtension(String ext) { return ext.equalsIgnoreCase("sic"); } public boolean acceptsMacFileType(int type) { return type == 0x53494320 || type == 0x53494366 || type == 0x53494363; } public boolean acceptsMacResourceType(int type) { return type == 0x53494320 || type == 0x53494366 || type == 0x53494363; } public boolean acceptsDFFType(long type) { return type == 0x49636F6E20534943L; } public boolean supportsRead() { return true; } public boolean usesReadOptionForm() { return false; } public Form getReadOptionForm() { return null; } public Canvas read(DataInputStream in, Monitor m) throws IOException { int w = 24; int h = 24; int[] pixels = null; int pp = 0; int repeat = 0; while (pixels == null || pp < pixels.length) try { int b = in.readByte() & 0xFF; if (b < 0x20) { if (b >= 0x10) { if (repeat < 0) repeat = 0; repeat = (repeat << 4) | (b & 0xF); } else if (b == 0x0E && pixels == null) { w = 0; w = (w << 7) | (in.readByte() & 0x7F); w = (w << 7) | (in.readByte() & 0x7F); w = (w << 7) | (in.readByte() & 0x7F); } else if (b == 0x0F && pixels == null) { h = 0; h = (h << 7) | (in.readByte() & 0x7F); h = (h << 7) | (in.readByte() & 0x7F); h = (h << 7) | (in.readByte() & 0x7F); } else if (b == 0) { if (pixels == null) pixels = new int[w*h]; if (repeat > 0) { while (repeat-->0) pixels[pp++] = 0xFF000000; } else { pixels[pp++] = 0xFF000000; } } } else { if (pixels == null) pixels = new int[w*h]; int c = getRGB(b); if (repeat > 0) { while (repeat-->0) pixels[pp++] = c; } else { pixels[pp++] = c; } } } catch (EOFException eof) { if (pixels == null) pixels = new int[w*h]; } Canvas c = new Canvas(w, h); c.get(0).setRGB(0, 0, w, h, pixels, 0, w); return c; } private static int colors = 84; private static boolean compressed = false; public boolean supportsWrite() { return true; } public boolean usesWriteOptionForm() { return true; } public Form getWriteOptionForm() { Form f = new Form(); f.add(new IntegerEnumOption() { public String getName() { return FormatUtilities.messages.getString("sic.Colors"); } public int getValue() { return colors; } public void setValue(int v) { colors = v; } public int[] values() { return new int[] { 16, 84, 125 }; } public String getLabel(int v) { return FormatUtilities.messages.getString("sic.Colors." + v); } }); f.add(new BooleanOption() { public String getName() { return ""; } public boolean getValue() { return compressed; } public void setValue(boolean v) { compressed = v; } public boolean useTrueFalseLabels() { return false; } public String getLabel(boolean v) { return FormatUtilities.messages.getString("sic.Compressed"); } }); return f; } public int approximateFileSize(Canvas c) { return c.getWidth()*c.getHeight(); } public void write(Canvas c, DataOutputStream out, Monitor m) throws IOException { int w = c.getWidth(); int h = c.getHeight(); int d = colors; boolean k = compressed; BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); Graphics2D g = bi.createGraphics(); c.paint(g); g.dispose(); int l = w*h; int[] pixels = new int[l]; bi.getRGB(0, 0, w, h, pixels, 0, w); byte[] data = new byte[l]; switch (d) { case 16: for (int i = 0; i < l; i++) data[i] = quantize16(pixels[i]); break; case 84: for (int i = 0; i < l; i++) data[i] = quantize84(pixels[i]); break; case 125: for (int i = 0; i < l; i++) data[i] = quantize125(pixels[i]); break; default: for (int i = 0; i < l; i++) data[i] = quantize84(pixels[i]); break; } if (k) { int sp = 0; int dp = 0; while (sp < l) { byte cv = data[sp++]; int cr = 1; while (sp < l && data[sp] == cv) { sp++; cr++; } if (cr <= 2) { while (cr-->0) data[dp++] = cv; } else { if (cr >= 0x10000000) data[dp++] = (byte)(0x10 | ((cr >>> 28) & 0xF)); if (cr >= 0x01000000) data[dp++] = (byte)(0x10 | ((cr >>> 24) & 0xF)); if (cr >= 0x00100000) data[dp++] = (byte)(0x10 | ((cr >>> 20) & 0xF)); if (cr >= 0x00010000) data[dp++] = (byte)(0x10 | ((cr >>> 16) & 0xF)); if (cr >= 0x00001000) data[dp++] = (byte)(0x10 | ((cr >>> 12) & 0xF)); if (cr >= 0x00000100) data[dp++] = (byte)(0x10 | ((cr >>> 8) & 0xF)); if (cr >= 0x00000010) data[dp++] = (byte)(0x10 | ((cr >>> 4) & 0xF)); if (cr >= 0x00000001) data[dp++] = (byte)(0x10 | ((cr >>> 0) & 0xF)); data[dp++] = cv; } } l = dp; } if (w == 24 && h == 24) { out.write(data, 0, l); } else { out.writeByte(0x0E); out.writeByte(((w >>> 14) & 0x7F) | 0x80); out.writeByte(((w >>> 7) & 0x7F) | 0x80); out.writeByte(((w >>> 0) & 0x7F) | 0x80); out.writeByte(0x0F); out.writeByte(((h >>> 14) & 0x7F) | 0x80); out.writeByte(((h >>> 7) & 0x7F) | 0x80); out.writeByte(((h >>> 0) & 0x7F) | 0x80); out.write(data, 0, l); } } public static int getRGB(int i) { switch (i) { case 'T': return 0x00000000; case 't': return 0x00000000; case '+': return 0x80000000; case '4': return 0x80808080; case '7': return 0x80FFFFFF; case 'W': return 0xFFFFFFFF; case ' ': return 0xFFC0C0C0; case 'w': return 0xFFAAAAAA; case '`': return 0xFF808080; case 'k': return 0xFF555555; case '@': return 0xFF404040; case 'K': return 0xFF000000; case '?': return 0xFFEEEEEE; case '>': return 0xFFDDDDDD; case '=': return 0xFFCCCCCC; case '<': return 0xFFBBBBBB; case ';': return 0xFFAAAAAA; case '~': return 0xFF999999; case '}': return 0xFF888888; case '|': return 0xFF777777; case '{': return 0xFF666666; case '_': return 0xFF555555; case '^': return 0xFF444444; case ']': return 0xFF333333; case'\\': return 0xFF222222; case '[': return 0xFF111111; case 'R': return 0xFF800000; case 'r': return 0xFFFF0000; case '2': return 0xFFFF8080; case 'O': return 0xFF804000; case 'o': return 0xFFFF8000; case '/': return 0xFFFFC080; case 'Y': return 0xFF808000; case 'y': return 0xFFFFFF00; case '9': return 0xFFFFFF80; case 'L': return 0xFF408000; case 'l': return 0xFF80FF00; case ',': return 0xFFC0FF80; case 'G': return 0xFF008000; case 'g': return 0xFF00FF00; case'\'': return 0xFF80FF80; case 'A': return 0xFF008040; case 'a': return 0xFF00FF80; case '!': return 0xFF80FFC0; case 'C': return 0xFF008080; case 'c': return 0xFF00FFFF; case '#': return 0xFF80FFFF; case 'D': return 0xFF004080; case 'd': return 0xFF0080FF; case '$': return 0xFF80C0FF; case 'B': return 0xFF000080; case 'b': return 0xFF0000FF; case'\"': return 0xFF8080FF; case 'V': return 0xFF400080; case 'v': return 0xFF8000FF; case '6': return 0xFFC080FF; case 'P': return 0xFF800080; case 'p': return 0xFFFF00FF; case '0': return 0xFFFF80FF; case 'M': return 0xFF800080; case 'm': return 0xFFFF00FF; case '-': return 0xFFFF80FF; case 'S': return 0xFF800040; case 's': return 0xFFFF0080; case '3': return 0xFFFF80C0; case 'Q': return 0xFF663300; case 'q': return 0xFF996633; case '1': return 0xFFCC9966; case 'U': return 0xFF660099; case 'u': return 0xFF9933CC; case '5': return 0xFFCC66FF; case 'E': return 0xFF806C00; case 'e': return 0xFFFFDD00; case '%': return 0xFFFFEE80; case 'F': return 0xFFCC9966; case 'f': return 0xFFFFDDAA; case '&': return 0xFFFFEEDD; case 'N': return 0xFF6666CC; case 'n': return 0xFF9999FF; case '.': return 0xFFCCCCFF; case 'H': return 0xFF990011; case 'h': return 0xFFFF0077; case '(': return 0xFFFF66DD; case 'Z': return 0xFF999966; case 'z': return 0xFFEEDD99; case ':': return 0xFFFFFFDD; case 'I': return 0xFF7799CC; case 'i': return 0xFFAAEEFF; case ')': return 0xFFDDFFFF; case 'J': return 0xFFAA88CC; case 'j': return 0xFFDDBBFF; case '*': return 0xFFFFEEFF; case 'X': return 0xFF806000; case 'x': return 0xFFFFC000; case '8': return 0xFFFFE080; } if (i >= 128 && i < 253) { int r = ((i-128) % 5) * 0x40; if (r > 0xFF) r = 0xFF; int g = (((i-128) / 5) % 5) * 0x40; if (g > 0xFF) g = 0xFF; int b = (((i-128) / 25) % 5) * 0x40; if (b > 0xFF) b = 0xFF; return 0xFF000000 | (r << 16) | (g << 8) | b; } return 0; } public static Color getColor(int i) { return new Color(getRGB(i),true); } private static final byte[] QUANTIZE_16_PALETTE = new byte[] { 'K','k','w','W','R','r','Y','y','G','g','C','c','B','b','P','p' }; public static byte quantize16(int color) { int a = (color >>> 24) & 0xFF; int r = (color >>> 16) & 0xFF; int g = (color >>> 8) & 0xFF; int b = (color >>> 0) & 0xFF; if (a < 0x80) { return 'T'; } else { byte q = 'T'; int diff = Integer.MAX_VALUE; for (byte pq : QUANTIZE_16_PALETTE) { int pc = getRGB(pq & 0xFF); int pa = (pc >>> 24) & 0xFF; if (pa < 0xAA) continue; int pr = (pc >>> 16) & 0xFF; int pg = (pc >>> 8) & 0xFF; int pb = (pc >>> 0) & 0xFF; int pd = Math.abs(pr-r) + Math.abs(pg-g) + Math.abs(pb-b); if (pd < diff) { q = pq; diff = pd; } } return q; } } public static byte quantize84(int color) { int a = (color >>> 24) & 0xFF; int r = (color >>> 16) & 0xFF; int g = (color >>> 8) & 0xFF; int b = (color >>> 0) & 0xFF; if (a < 0x55) { return 'T'; } else if (a < 0xAA) { int k = (30*r + 59*g + 11*b) / 100; if (k < 0x55) return '+'; else if (k < 0xAA) return '4'; else return '7'; } else { byte q = 'T'; int diff = Integer.MAX_VALUE; for (byte pq = 0x20; pq < 0x7F; pq++) { int pc = getRGB(pq & 0xFF); int pa = (pc >>> 24) & 0xFF; if (pa < 0xAA) continue; int pr = (pc >>> 16) & 0xFF; int pg = (pc >>> 8) & 0xFF; int pb = (pc >>> 0) & 0xFF; int pd = Math.abs(pr-r) + Math.abs(pg-g) + Math.abs(pb-b); if (pd < diff) { q = pq; diff = pd; } } return q; } } public static byte quantize125(int color) { int a = (color >>> 24) & 0xFF; int r = (color >>> 16) & 0xFF; int g = (color >>> 8) & 0xFF; int b = (color >>> 0) & 0xFF; if (a < 0x55) { return 'T'; } else if (a < 0xAA) { int k = (30*r + 59*g + 11*b) / 100; if (k < 0x55) return '+'; else if (k < 0xAA) return '4'; else return '7'; } else { int q = 128; if (r < 0x33) q += 0; else if (r < 0x66) q += 1; else if (r < 0x99) q += 2; else if (r < 0xCC) q += 3; else q += 4; if (g < 0x33) q += 0; else if (g < 0x66) q += 5; else if (g < 0x99) q += 10; else if (g < 0xCC) q += 15; else q += 20; if (b < 0x33) q += 0; else if (b < 0x66) q += 25; else if (b < 0x99) q += 50; else if (b < 0xCC) q += 75; else q += 100; return (byte)q; } } public static void main(String[] args) { if (args.length < 1) { JFrame f = new JFrame("Sabine Color Palette"); JPanel p = new JPanel(new GridLayout(0,32,2,2)); for (int b=64; b<96; b++) { JLabel l = new JLabel(new String(new char[]{(char)b})); l.setOpaque(true); l.setBackground(getColor(b)); l.setHorizontalAlignment(JLabel.CENTER); p.add(l); } for (int b=96; b<128; b++) { JLabel l = new JLabel(new String(new char[]{(char)b})); l.setOpaque(true); l.setBackground(getColor(b)); l.setHorizontalAlignment(JLabel.CENTER); p.add(l); } for (int b=32; b<64; b++) { JLabel l = new JLabel(new String(new char[]{(char)b})); l.setOpaque(true); l.setBackground(getColor(b)); l.setHorizontalAlignment(JLabel.CENTER); p.add(l); } for (int b=128; b<256; b++) { JLabel l = new JLabel(new String(new char[]{(char)b})); l.setOpaque(true); l.setBackground(getColor(b)); l.setHorizontalAlignment(JLabel.CENTER); p.add(l); } p.setBorder(BorderFactory.createEmptyBorder(2,2,2,2)); f.setContentPane(p); f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); f.pack(); f.setVisible(true); } else { for (int i=0; i<args.length; i++) { try { RandomAccessFile raf = new RandomAccessFile(args[i],"r"); byte[] stuff = new byte[(int)raf.length()]; raf.read(stuff); raf.close(); int s = (int)Math.ceil(Math.sqrt(stuff.length)); JFrame f = new JFrame(args[i]); JPanel p = new JPanel(new GridLayout(s,s,0,0)); for (int j=0; j<stuff.length; j++) { JPanel l = new JPanel(); l.setOpaque(true); l.setBackground(getColor(stuff[j])); p.add(l); } f.setContentPane(p); f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); f.pack(); f.setVisible(true); } catch (IOException ioe) { System.out.println("Could not read "+args[i]+"."); } } } } }