/*
* 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.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import com.kreative.paint.Canvas;
import com.kreative.paint.form.Form;
import com.kreative.paint.io.Monitor;
public class PAMFormat implements Format {
public String getName() { return "PAM"; }
public String getExpandedName() { return "Portable Arbitrary Map"; }
public String getExtension() { return "pam"; }
public int getMacFileType() { return 0x50414D20; }
public int getMacResourceType() { return 0x50414D20; }
public long getDFFType() { return 0x496D61676550414DL; }
public MediaType getMediaType() { return MediaType.IMAGE; }
public GraphicType getGraphicType() { return GraphicType.BITMAP; }
public SizeType getSizeType() { return SizeType.ARBITRARY; }
public ColorType getColorType() { return ColorType.RGB_8; }
public AlphaType getAlphaType() { return AlphaType.CHANNEL; }
public LayerType getLayerType() { return LayerType.FLAT; }
public boolean onlyUponRequest() { return false; }
public int usesMagic() { return 3; }
public boolean acceptsMagic(byte[] start, long length) {
try {
DataInputStream in = new DataInputStream(new ByteArrayInputStream(start));
if (in.readShort() != (short)0x5037) return false;
if (!isLineBreak(in.readByte())) return false;
in.close();
return true;
} catch (IOException ioe) {
return false;
}
}
public boolean acceptsExtension(String ext) { return ext.equalsIgnoreCase("pam"); }
public boolean acceptsMacFileType(int type) { return type == 0x50414D20 || type == 0x50414D66 || type == 0x50414D6D; }
public boolean acceptsMacResourceType(int type) { return type == 0x50414D20 || type == 0x50414D66 || type == 0x50414D6D; }
public boolean acceptsDFFType(long type) { return type == 0x496D61676550414DL || type == 0x496D672050414D20L; }
public boolean supportsRead() { return true; }
public boolean usesReadOptionForm() { return false; }
public Form getReadOptionForm() { return null; }
public Canvas read(DataInputStream in, Monitor mon) throws IOException {
if (in.readShort() != (short)0x5037) throw new NotThisFormatException();
if (!isLineBreak(in.readByte())) throw new NotThisFormatException();
int w = 0;
int h = 0;
int d = 0;
int m = 0;
//String t = "";
while (true) {
String s = readLine(in).trim();
if (s.length() > 0 && !s.startsWith("#")) {
String[] ss = s.split("\\s+");
if (ss[0].equalsIgnoreCase("ENDHDR")) {
break;
}
else if (ss[0].equalsIgnoreCase("WIDTH") && ss.length > 1) {
w = Integer.parseInt(ss[1]);
}
else if (ss[0].equalsIgnoreCase("HEIGHT") && ss.length > 1) {
h = Integer.parseInt(ss[1]);
}
else if (ss[0].equalsIgnoreCase("DEPTH") && ss.length > 1) {
d = Integer.parseInt(ss[1]);
}
else if (ss[0].equalsIgnoreCase("MAXVAL") && ss.length > 1) {
m = Integer.parseInt(ss[1]);
}
else if (ss[0].equalsIgnoreCase("TUPLTYPE") && ss.length > 1) {
//t = ss[1].toUpperCase();
}
}
}
int[] pixels = new int[w*h];
for (int i = 0; i < pixels.length; i++) {
switch (d) {
case 1:
{
int v = ((m >= 256) ? (in.readShort() & 0xFFFF) : (in.readByte() & 0xFF))*255/m;
pixels[i] = 0xFF000000 | (v << 16) | (v << 8) | v;
}
break;
case 2:
{
int v = ((m >= 256) ? (in.readShort() & 0xFFFF) : (in.readByte() & 0xFF))*255/m;
int a = ((m >= 256) ? (in.readShort() & 0xFFFF) : (in.readByte() & 0xFF))*255/m;
pixels[i] = (a << 24) | (v << 16) | (v << 8) | v;
}
break;
case 3:
{
int r = ((m >= 256) ? (in.readShort() & 0xFFFF) : (in.readByte() & 0xFF))*255/m;
int g = ((m >= 256) ? (in.readShort() & 0xFFFF) : (in.readByte() & 0xFF))*255/m;
int b = ((m >= 256) ? (in.readShort() & 0xFFFF) : (in.readByte() & 0xFF))*255/m;
pixels[i] = 0xFF000000 | (r << 16) | (g << 8) | b;
}
break;
case 4:
{
int r = ((m >= 256) ? (in.readShort() & 0xFFFF) : (in.readByte() & 0xFF))*255/m;
int g = ((m >= 256) ? (in.readShort() & 0xFFFF) : (in.readByte() & 0xFF))*255/m;
int b = ((m >= 256) ? (in.readShort() & 0xFFFF) : (in.readByte() & 0xFF))*255/m;
int a = ((m >= 256) ? (in.readShort() & 0xFFFF) : (in.readByte() & 0xFF))*255/m;
pixels[i] = (a << 24) | (r << 16) | (g << 8) | b;
}
break;
}
}
Canvas c = new Canvas(w, h);
c.get(0).setRGB(0, 0, w, h, pixels, 0, w);
return c;
}
public boolean supportsWrite() { return true; }
public boolean usesWriteOptionForm() { return false; }
public Form getWriteOptionForm() { return null; }
public int approximateFileSize(Canvas c) {
return c.getWidth()*c.getHeight()*4;
}
public void write(Canvas c, DataOutputStream out, Monitor mon) 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);
int tt = determineTupleType(pixels);
int d = TUPLE_TYPE_DEPTH[tt];
int m = TUPLE_TYPE_MAXVAL[tt];
String t = TUPLE_TYPE_STRING[tt];
out.writeShort(0x5037);
out.writeByte(0x0A);
out.writeBytes("WIDTH "+w);
out.writeByte(0x0A);
out.writeBytes("HEIGHT "+h);
out.writeByte(0x0A);
out.writeBytes("DEPTH "+d);
out.writeByte(0x0A);
out.writeBytes("MAXVAL "+m);
out.writeByte(0x0A);
out.writeBytes("TUPLTYPE "+t);
out.writeByte(0x0A);
out.writeBytes("ENDHDR");
out.writeByte(0x0A);
for (int pixel : pixels) {
if (tt == 0 || tt == 3) {
out.writeByte((isBlack(pixel) < 0x80) ? 0 : 1);
}
if (tt == 1 || tt == 4) {
out.writeByte(isBlack(pixel));
}
if (tt == 2 || tt == 5) {
out.writeByte((pixel >>> 16) & 0xFF);
out.writeByte((pixel >>> 8) & 0xFF);
out.writeByte((pixel >>> 0) & 0xFF);
}
if (tt == 3) {
out.writeByte((((pixel >>> 24) & 0xFF) < 0x80) ? 0 : 1);
}
if (tt == 4 || tt == 5) {
out.writeByte((pixel >>> 24) & 0xFF);
}
}
}
private static boolean isLineBreak(byte b) {
return ((b == (byte)0x0A) || (b == (byte)0x0D));
}
private static int isBlack(int pixel) {
int r = ((pixel >>> 16) & 0xFF);
int g = ((pixel >>> 8) & 0xFF);
int b = ((pixel >>> 0) & 0xFF);
int k = (30*r + 59*g + 11*b) / 100;
return k;
}
private static String readLine(DataInputStream in) throws IOException {
StringBuffer sb = new StringBuffer();
while (true) {
byte b = in.readByte();
if (isLineBreak(b)) return sb.toString();
else sb.append((char)(b & 0xFF));
}
}
private static final String[] TUPLE_TYPE_STRING = new String[] {
"BLACKANDWHITE", "GRAYSCALE", "RGB",
"BLACKANDWHITE_ALPHA", "GRAYSCALE_ALPHA", "RGB_ALPHA",
};
private static final int[] TUPLE_TYPE_DEPTH = new int[] { 1, 1, 3, 2, 2, 4 };
private static final int[] TUPLE_TYPE_MAXVAL = new int[] { 1, 255, 255, 1, 255, 255 };
private static int determineTupleType(int[] pixels) {
boolean alpha = false;
int complexity = 0;
for (int pixel : pixels) {
int a = (pixel >>> 24) & 0xFF;
int rgb = pixel & 0xFFFFFF;
if (a != 0xFF) alpha = true;
if (a == 0x00 || a == 0xFF) {
if (complexity < 0) complexity = 0;
} else {
if (complexity < 1) complexity = 1;
}
if (rgb == 0x000000 || rgb == 0xFFFFFF) {
if (complexity < 0) complexity = 0;
} else {
int r = (pixel >>> 16) & 0xFF;
int g = (pixel >>> 8) & 0xFF;
int b = pixel & 0xFF;
if (r == g && g == b) {
if (complexity < 1) complexity = 1;
} else {
if (complexity < 2) complexity = 2;
}
}
}
if (alpha) complexity += 3;
return complexity;
}
}