/* * 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 java.util.Arrays; import com.kreative.paint.Canvas; import com.kreative.paint.form.Form; import com.kreative.paint.io.Monitor; public class RaaBitsFormat implements Format { public String getName() { return "RaaBits"; } public String getExpandedName() { return "RaaBits"; } public String getExtension() { return "rbit"; } public int getMacFileType() { return 0x52424954; } public int getMacResourceType() { return 0x52424954; } public long getDFFType() { return 0x496D672052424954L; } 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.CHANNEL; } public LayerType getLayerType() { return LayerType.FLAT; } public boolean onlyUponRequest() { return false; } public int usesMagic() { return 8; } public boolean acceptsMagic(byte[] start, long length) { try { DataInputStream in = new DataInputStream(new ByteArrayInputStream(start)); if (in.readInt() != 0x12AAB175) return false; if (in.readInt() != 0) return false; in.close(); return true; } catch (IOException ioe) { return false; } } public boolean acceptsExtension(String ext) { return ext.equalsIgnoreCase("rbit") || ext.equalsIgnoreCase("raa"); } public boolean acceptsMacFileType(int type) { return type == 0x52424954; } public boolean acceptsMacResourceType(int type) { return type == 0x52424954; } public boolean acceptsDFFType(long type) { return type == 0x496D672052424954L || type == 0x496D672052414120L || type == 0x496D616765524141L; } public boolean supportsRead() { return true; } public boolean usesReadOptionForm() { return false; } public Form getReadOptionForm() { return null; } public Canvas read(DataInputStream in, Monitor m) throws IOException { if (in.readInt() != 0x12AAB175) throw new NotThisFormatException(); if (in.readInt() != 0) throw new NotThisFormatException(); int w = in.readInt(); int h = in.readInt(); int d = in.readInt(); int md = in.readInt(); int l = in.readInt(); int ml = in.readInt(); byte[] data = new byte[l]; in.readFully(data); byte[] mdata = new byte[ml]; in.readFully(mdata); int[] pixels = new int[w*h]; Arrays.fill(pixels, 0xFF000000); if (d > 0 && l > 0) { int ppb = 8/d; int bpr = w*d; if ((bpr & 7) != 0) bpr = (bpr | 7) + 1; bpr >>>= 3; int bm = ((1 << d)-1); int[] table = (d <= 1) ? COLORS_1BIT : (d <= 2) ? COLORS_2BIT : (d <= 4) ? COLORS_4BIT : (d <= 8) ? COLORS_8BIT : COLORS_8BIT; for (int y = 0, py = 0, dy = 0; y < h && py < w*h && dy < l; y++, py += w, dy += bpr) { for (int x = 0, px = py, dx = dy; x < w && px < w*h && dx < l; x += ppb, px += ppb, dx++) { for (int p = 0, pp = px, dp = data[dx] & 0xFF, ds = 8-d; p < ppb && pp < w*h; p++, pp++, ds -= d) { pixels[pp] = table[(dp >>> ds) & bm]; } } } } if (md > 0 && ml > 0) { int mppb = 8/md; int mbpr = w*md; if ((mbpr & 7) != 0) mbpr = (mbpr | 7) + 1; mbpr >>>= 3; int mbm = ((1 << md)-1); for (int y = 0, py = 0, dy = 0; y < h && py < w*h && dy < ml; y++, py += w, dy += mbpr) { for (int x = 0, px = py, dx = dy; x < w && px < w*h && dx < ml; x += mppb, px += mppb, dx++) { for (int p = 0, pp = px, dp = mdata[dx] & 0xFF, ds = 8-md; p < mppb && pp < w*h; p++, pp++, ds -= md) { pixels[pp] = (pixels[pp] & 0x00FFFFFF) | (((((dp >>> ds) & mbm)*0xFF)/mbm) << 24); } } } } 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()*2; } 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); int d = calculateDepth(pixels); int md = calculateMaskDepth(pixels); byte[] data = null; byte[] mdata = null; if (d == 0) { data = new byte[0]; d = 1; } else { int ppb = 8/d; int bpr = w*d; if ((bpr & 7) != 0) bpr = (bpr | 7) + 1; bpr >>>= 3; int bm = ((1 << d)-1); data = new byte[bpr*h]; for (int y = 0, py = 0, dy = 0; y < h && py < w*h && dy < bpr*h; y++, py += w, dy += bpr) { for (int x = 0, px = py, dx = dy; x < w && px < w*h && dx < bpr*h; x += ppb, px += ppb, dx++) { data[dx] = 0; for (int p = 0, pp = px; p < ppb; p++, pp++) { data[dx] <<= d; if (pp < w*h) { int i = (d <= 1) ? reverse1Bit(pixels[pp]) : (d <= 2) ? reverse2Bit(pixels[pp]) : (d <= 4) ? reverse4Bit(pixels[pp]) : (d <= 8) ? reverse8Bit(pixels[pp]) : reverse8Bit(pixels[pp]); data[dx] |= (i & bm); } } } } } if (md == 0) { mdata = new byte[0]; md = 1; } else { int mppb = 8/md; int mbpr = w*md; if ((mbpr & 7) != 0) mbpr = (mbpr | 7) + 1; mbpr >>>= 3; int mbm = ((1 << md)-1); mdata = new byte[mbpr*h]; for (int y = 0, py = 0, dy = 0; y < h && py < w*h && dy < mbpr*h; y++, py += w, dy += mbpr) { for (int x = 0, px = py, dx = dy; x < w && px < w*h && dx < mbpr*h; x += mppb, px += mppb, dx++) { mdata[dx] = 0; for (int p = 0, pp = px; p < mppb; p++, pp++) { mdata[dx] <<= md; if (pp < w*h) { mdata[dx] |= ((int)Math.round((((pixels[pp] >>> 24) & 0xFF) * mbm)/255.0) & mbm); } } } } } out.writeInt(0x12AAB175); out.writeInt(0); out.writeInt(w); out.writeInt(h); out.writeInt(d); out.writeInt(md); out.writeInt(data.length); out.writeInt(mdata.length); out.write(data); out.write(mdata); } private static int calculateDepth(int[] pixels) { int d = 1; for (int pixel : pixels) { int p = pixel & 0xFFFFFF; switch (p) { case 0x000000: case 0xFFFFFF: if (d < 1) d = 1; break; case 0x808080: if (d < 2) d = 2; break; case 0xFCF305: case 0xFF6503: case 0xDD0907: case 0xF30885: case 0x4700A5: case 0x0000D4: case 0x02ABEB: case 0x1FB814: case 0x006512: case 0x562D05: case 0x91713A: case 0xC0C0C0: case 0x404040: if (d < 4) d = 4; break; default: if (d < 8) d = 8; break; } } return d; } private static int calculateMaskDepth(int[] pixels) { int md = 0; for (int pixel : pixels) { int a = (pixel >>> 24) & 0xFF; switch (a) { case 0xFF: if (md < 0) md = 0; break; case 0x00: if (md < 1) md = 1; break; case 0x55: case 0xAA: if (md < 2) md = 2; break; case 0x11: case 0x22: case 0x33: case 0x44: case 0x66: case 0x77: case 0x88: case 0x99: case 0xBB: case 0xCC: case 0xDD: case 0xEE: if (md < 4) md = 4; break; default: if (md < 8) md = 8; break; } } return md; } private static int reverse8Bit(int pixel) { if ((pixel & 0xFFFFFF) == 0xFFFFFF) return 0; if ((pixel & 0xFFFFFF) == 0x000000) return 255; int r = (int)Math.round(((pixel >>> 16) & 0xFF) / 17.0) & 0xF; int g = (int)Math.round(((pixel >>> 8) & 0xFF) / 17.0) & 0xF; int b = (int)Math.round(((pixel >>> 0) & 0xFF) / 17.0) & 0xF; if (r == g && g == b) { switch (r) { case 0xF: return 0; case 0xC: return 43; case 0x9: return 86; case 0x6: return 129; case 0x3: return 172; case 0xE: return 245; case 0xD: return 246; case 0xB: return 247; case 0xA: return 248; case 0x8: return 249; case 0x7: return 250; case 0x5: return 251; case 0x4: return 252; case 0x2: return 253; case 0x1: return 254; case 0x0: return 255; } } if (r == 0 && g == 0) { switch (b) { case 0xF: return 210; case 0xC: return 211; case 0x9: return 212; case 0x6: return 213; case 0x3: return 214; case 0xE: return 235; case 0xD: return 236; case 0xB: return 237; case 0xA: return 238; case 0x8: return 239; case 0x7: return 240; case 0x5: return 241; case 0x4: return 242; case 0x2: return 243; case 0x1: return 244; case 0x0: return 255; } } if (r == 0 && b == 0) { switch (g) { case 0xF: return 185; case 0xC: return 191; case 0x9: return 197; case 0x6: return 203; case 0x3: return 209; case 0xE: return 225; case 0xD: return 226; case 0xB: return 227; case 0xA: return 228; case 0x8: return 229; case 0x7: return 230; case 0x5: return 231; case 0x4: return 232; case 0x2: return 233; case 0x1: return 234; case 0x0: return 255; } } if (g == 0 && b == 0) { switch (r) { case 0xF: return 35; case 0xC: return 71; case 0x9: return 107; case 0x6: return 143; case 0x3: return 179; case 0xE: return 215; case 0xD: return 216; case 0xB: return 217; case 0xA: return 218; case 0x8: return 219; case 0x7: return 220; case 0x5: return 221; case 0x4: return 222; case 0x2: return 223; case 0x1: return 224; case 0x0: return 255; } } r = 5 - ((int)Math.round(((pixel >>> 16) & 0xFF) / 51.0) & 0x7); g = 5 - ((int)Math.round(((pixel >>> 8) & 0xFF) / 51.0) & 0x7); b = 5 - ((int)Math.round(((pixel >>> 0) & 0xFF) / 51.0) & 0x7); if (r == 5 && g == 5 && b == 5) return 255; else return r*36 + g*6 + b; } private static int reverse4Bit(int pixel) { int r = ((pixel >>> 16) & 0xFF); int g = ((pixel >>> 8) & 0xFF); int b = ((pixel >>> 0) & 0xFF); int cc = 0; int cd = 1000; for (int i = 0; i < 16; i++) { int c = COLORS_4BIT[i]; int cr = ((c >>> 16) & 0xFF); int cg = ((c >>> 8) & 0xFF); int cb = ((c >>> 0) & 0xFF); int d = Math.abs(cr-r)+Math.abs(cg-g)+Math.abs(cb-b); if (d < cd) { cc = i; cd = d; } } return cc; } private static int reverse2Bit(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 >= 170) ? 0 : (k >= 85) ? 1 : 3; } private static int reverse1Bit(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 >= 0x80) ? 0 : 1; } private static final int[] COLORS_8BIT = { 0xFFFFFFFF, 0xFFFFFFCC, 0xFFFFFF99, 0xFFFFFF66, 0xFFFFFF33, 0xFFFFFF00, 0xFFFFCCFF, 0xFFFFCCCC, 0xFFFFCC99, 0xFFFFCC66, 0xFFFFCC33, 0xFFFFCC00, 0xFFFF99FF, 0xFFFF99CC, 0xFFFF9999, 0xFFFF9966, 0xFFFF9933, 0xFFFF9900, 0xFFFF66FF, 0xFFFF66CC, 0xFFFF6699, 0xFFFF6666, 0xFFFF6633, 0xFFFF6600, 0xFFFF33FF, 0xFFFF33CC, 0xFFFF3399, 0xFFFF3366, 0xFFFF3333, 0xFFFF3300, 0xFFFF00FF, 0xFFFF00CC, 0xFFFF0099, 0xFFFF0066, 0xFFFF0033, 0xFFFF0000, 0xFFCCFFFF, 0xFFCCFFCC, 0xFFCCFF99, 0xFFCCFF66, 0xFFCCFF33, 0xFFCCFF00, 0xFFCCCCFF, 0xFFCCCCCC, 0xFFCCCC99, 0xFFCCCC66, 0xFFCCCC33, 0xFFCCCC00, 0xFFCC99FF, 0xFFCC99CC, 0xFFCC9999, 0xFFCC9966, 0xFFCC9933, 0xFFCC9900, 0xFFCC66FF, 0xFFCC66CC, 0xFFCC6699, 0xFFCC6666, 0xFFCC6633, 0xFFCC6600, 0xFFCC33FF, 0xFFCC33CC, 0xFFCC3399, 0xFFCC3366, 0xFFCC3333, 0xFFCC3300, 0xFFCC00FF, 0xFFCC00CC, 0xFFCC0099, 0xFFCC0066, 0xFFCC0033, 0xFFCC0000, 0xFF99FFFF, 0xFF99FFCC, 0xFF99FF99, 0xFF99FF66, 0xFF99FF33, 0xFF99FF00, 0xFF99CCFF, 0xFF99CCCC, 0xFF99CC99, 0xFF99CC66, 0xFF99CC33, 0xFF99CC00, 0xFF9999FF, 0xFF9999CC, 0xFF999999, 0xFF999966, 0xFF999933, 0xFF999900, 0xFF9966FF, 0xFF9966CC, 0xFF996699, 0xFF996666, 0xFF996633, 0xFF996600, 0xFF9933FF, 0xFF9933CC, 0xFF993399, 0xFF993366, 0xFF993333, 0xFF993300, 0xFF9900FF, 0xFF9900CC, 0xFF990099, 0xFF990066, 0xFF990033, 0xFF990000, 0xFF66FFFF, 0xFF66FFCC, 0xFF66FF99, 0xFF66FF66, 0xFF66FF33, 0xFF66FF00, 0xFF66CCFF, 0xFF66CCCC, 0xFF66CC99, 0xFF66CC66, 0xFF66CC33, 0xFF66CC00, 0xFF6699FF, 0xFF6699CC, 0xFF669999, 0xFF669966, 0xFF669933, 0xFF669900, 0xFF6666FF, 0xFF6666CC, 0xFF666699, 0xFF666666, 0xFF666633, 0xFF666600, 0xFF6633FF, 0xFF6633CC, 0xFF663399, 0xFF663366, 0xFF663333, 0xFF663300, 0xFF6600FF, 0xFF6600CC, 0xFF660099, 0xFF660066, 0xFF660033, 0xFF660000, 0xFF33FFFF, 0xFF33FFCC, 0xFF33FF99, 0xFF33FF66, 0xFF33FF33, 0xFF33FF00, 0xFF33CCFF, 0xFF33CCCC, 0xFF33CC99, 0xFF33CC66, 0xFF33CC33, 0xFF33CC00, 0xFF3399FF, 0xFF3399CC, 0xFF339999, 0xFF339966, 0xFF339933, 0xFF339900, 0xFF3366FF, 0xFF3366CC, 0xFF336699, 0xFF336666, 0xFF336633, 0xFF336600, 0xFF3333FF, 0xFF3333CC, 0xFF333399, 0xFF333366, 0xFF333333, 0xFF333300, 0xFF3300FF, 0xFF3300CC, 0xFF330099, 0xFF330066, 0xFF330033, 0xFF330000, 0xFF00FFFF, 0xFF00FFCC, 0xFF00FF99, 0xFF00FF66, 0xFF00FF33, 0xFF00FF00, 0xFF00CCFF, 0xFF00CCCC, 0xFF00CC99, 0xFF00CC66, 0xFF00CC33, 0xFF00CC00, 0xFF0099FF, 0xFF0099CC, 0xFF009999, 0xFF009966, 0xFF009933, 0xFF009900, 0xFF0066FF, 0xFF0066CC, 0xFF006699, 0xFF006666, 0xFF006633, 0xFF006600, 0xFF0033FF, 0xFF0033CC, 0xFF003399, 0xFF003366, 0xFF003333, 0xFF003300, 0xFF0000FF, 0xFF0000CC, 0xFF000099, 0xFF000066, 0xFF000033, 0xFFEE0000, 0xFFDD0000, 0xFFBB0000, 0xFFAA0000, 0xFF880000, 0xFF770000, 0xFF550000, 0xFF440000, 0xFF220000, 0xFF110000, 0xFF00EE00, 0xFF00DD00, 0xFF00BB00, 0xFF00AA00, 0xFF008800, 0xFF007700, 0xFF005500, 0xFF004400, 0xFF002200, 0xFF001100, 0xFF0000EE, 0xFF0000DD, 0xFF0000BB, 0xFF0000AA, 0xFF000088, 0xFF000077, 0xFF000055, 0xFF000044, 0xFF000022, 0xFF000011, 0xFFEEEEEE, 0xFFDDDDDD, 0xFFBBBBBB, 0xFFAAAAAA, 0xFF888888, 0xFF777777, 0xFF555555, 0xFF444444, 0xFF222222, 0xFF111111, 0xFF000000 }; private static final int[] COLORS_4BIT = { 0xFFFFFFFF, 0xFFFCF305, 0xFFFF6503, 0xFFDD0907, 0xFFF30885, 0xFF4700A5, 0xFF0000D4, 0xFF02ABEB, 0xFF1FB814, 0xFF006512, 0xFF562D05, 0xFF91713A, 0xFFC0C0C0, 0xFF808080, 0xFF404040, 0xFF000000 }; private static final int[] COLORS_2BIT = { 0xFFFFFFFF, 0xFF808080, 0xFFCCCCFF, 0xFF000000 }; private static final int[] COLORS_1BIT = { 0xFFFFFFFF, 0xFF000000 }; }