Sketcher2 source code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

569 lines
16 KiB

4 years ago
package com.jotuntech.sketcher.client;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.io.DataOutput;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Arrays;
import java.util.Map;
import com.jotuntech.sketcher.common.BitmapLayer;
import com.jotuntech.sketcher.common.BitmapTile;
import com.jotuntech.sketcher.common.Layer;
import com.jotuntech.sketcher.common.TwoWayHashMap;
public class PSDEncoder {
private RandomAccessFile raf;
private BufferedImage image;
private BitmapLayer[] layers;
private long[][] channelLengthPositions;
public PSDEncoder(RandomAccessFile raf, BufferedImage image, TwoWayHashMap<Integer, Layer> layers) throws IOException {
this.raf = raf;
this.image = image;
int layersLength = 0;
for(Layer l : layers.values()) {
if(l instanceof BitmapLayer) {
++layersLength;
}
}
this.layers = new BitmapLayer[layersLength];
int layerIndex = 0;
for(Layer l : layers.values()) {
if(l instanceof BitmapLayer) {
this.layers[layerIndex++] = (BitmapLayer) l;
}
}
channelLengthPositions = new long[this.layers.length][4];
}
public void encode() throws IOException {
/* Signature */
raf.write(new byte[] {'8', 'B', 'P', 'S'});
/* Version */
raf.writeShort(1);
/* Reserved */
raf.write(new byte[] {0, 0, 0, 0, 0, 0});
/* Number of channels */
raf.writeShort(4);
/* Image height */
raf.writeInt(image.getHeight());
/* Image width */
raf.writeInt(image.getWidth());
/* Bits per channel */
raf.writeShort(8);
/* Color mode: RGB color */
raf.writeShort(3);
/* Length of color mode data section */
raf.writeInt(0);
/* Length of image resources section */
raf.writeInt(0);
/* Layer and mask information */
writeLayerAndMaskInfo(raf);
/* Image data */
writeImagePixelData(raf);
}
private void writeLayerAndMaskInfo(RandomAccessFile raf) throws IOException {
long lengthPointer = raf.getFilePointer();
raf.writeInt(0);
/* Layer info */
writeLayerInfo(raf);
/* Mask info */
writeMaskInfo(raf);
/* Go back and write length */
long endPointer = raf.getFilePointer();
raf.seek(lengthPointer);
raf.writeInt((int)(endPointer - lengthPointer - 4));
raf.seek(endPointer);
}
private void writeLayerInfo(RandomAccessFile odo) throws IOException {
System.err.println("Writing layer info.");
long lengthPointer = raf.getFilePointer();
odo.writeInt(0);
/* Layers struct */
writeLayersStruct(odo);
/* Pixel data */
writeAllLayersPixelData(odo);
/* Go back and write length */
long endPointer = raf.getFilePointer();
raf.seek(lengthPointer);
raf.writeInt((int)(endPointer - lengthPointer - 4));
raf.seek(endPointer);
}
private void writeLayersStruct(RandomAccessFile odo) throws IOException {
System.err.println("Writing layers struct.");
/* Layer count */
odo.writeShort(layers.length);
/* Layers */
for(int layerIndex = 0; layerIndex < layers.length; layerIndex++) {
writeLayer(odo, layerIndex);
}
}
private void writeLayer(RandomAccessFile odo, int layerIndex) throws IOException {
/* Get layer */
BitmapLayer l = layers[layerIndex];
System.err.println("Writing layer: " + l.getName());
/* Layer top */
odo.writeInt(0);
/* Layer left */
odo.writeInt(0);
/* Layer bottom */
odo.writeInt(image.getHeight());
/* Layer right */
odo.writeInt(image.getWidth());
/* Number of channels */
odo.writeShort(4);
/* Channel length info */
writeChannelLengthInfo(odo, layerIndex);
/* Blend mode signature */
odo.write(new byte[] {'8', 'B', 'I', 'M'});
/* Blend mode key */
odo.write(new byte[] {'n', 'o', 'r', 'm'});
/* Opacity */
odo.writeByte(Math.round(l.getOpacity() * 255f));
/* Clipping: Base */
odo.writeByte(0);
/* Flags: Transparency not protected and visible */
odo.writeByte(0x80);
/* Filler */
odo.writeByte(0);
/* Prepare layer name */
String name = l.getName();
int origLength = name.length();
int moduloLength = (origLength + 1) % 4;
int padLength = 0;
if(moduloLength > 0) {
padLength = 4 - moduloLength;
for(int i = 0; i < padLength; i++) {
name += "\000";
}
}
/* Extra data size */
odo.writeInt(4 + 44 + 1 + origLength + padLength);
/* Layer mask data size */
odo.writeInt(0);
/* Layer blending ranges */
writeLayerBlendingRanges(odo);
odo.writeByte(origLength);
odo.writeBytes(name);
}
private void writeChannelLengthInfo(RandomAccessFile raf, int layerIndex) throws IOException {
System.err.println("Writing channel length info.");
/* Channel ID: Transparency Mask */
raf.writeShort(-1);
/* Channel length */
channelLengthPositions[layerIndex][0] = raf.getFilePointer();
raf.writeInt(0);
/* Channel ID: Red */
raf.writeShort(0);
/* Channel length */
channelLengthPositions[layerIndex][1] = raf.getFilePointer();
raf.writeInt(0);
/* Channel ID: Green */
raf.writeShort(1);
/* Channel length */
channelLengthPositions[layerIndex][2] = raf.getFilePointer();
raf.writeInt(0);
/* Channel ID: Blue */
raf.writeShort(2);
/* Channel length */
channelLengthPositions[layerIndex][3] = raf.getFilePointer();
raf.writeInt(0);
}
private void writeLayerBlendingRanges(DataOutput odo) throws IOException {
System.err.println("Writing layer blending ranges.");
/* Length */
odo.writeInt(40);
/* Composite gray blend source */
odo.writeInt(0x0000FFFF);
/* Composite gray blend destination */
odo.writeInt(0x0000FFFF);
/* First channel source range */
odo.writeInt(0x0000FFFF);
/* First channel destination range */
odo.writeInt(0x0000FFFF);
/* Second channel source range */
odo.writeInt(0x0000FFFF);
/* Second channel destination range */
odo.writeInt(0x0000FFFF);
/* Third channel source range */
odo.writeInt(0x0000FFFF);
/* Third channel destination range */
odo.writeInt(0x0000FFFF);
/* Fourth channel source range */
odo.writeInt(0x0000FFFF);
/* Fourth channel destination range */
odo.writeInt(0x0000FFFF);
}
private void writeAllLayersPixelData(RandomAccessFile odo) throws IOException {
System.out.println("Writing pixel layer data.");
for(int layerIndex = 0; layerIndex < layers.length; layerIndex++) {
writeLayerPixelData(odo, layerIndex);
}
}
private void writeMaskInfo(RandomAccessFile raf) throws IOException {
System.err.println("Writing mask info.");
/* Mask info length */
long lengthPointer = raf.getFilePointer();
raf.writeInt(0);
/* Overlay color space */
raf.writeShort(0);
/* Color components */
raf.writeShort(0);
raf.writeShort(0);
raf.writeShort(0);
raf.writeShort(0);
/* Opacity */
raf.writeShort(0);
/* Kind: Use value stored per layer */
raf.writeByte(128);
/* Filler */
raf.writeByte(0);
/* Go back and write length */
long endPointer = raf.getFilePointer();
raf.seek(lengthPointer);
raf.writeInt((int)(endPointer - lengthPointer - 4));
raf.seek(endPointer);
}
public void writeImagePixelData(RandomAccessFile odo) throws IOException {
System.out.println("Writing image data.");
int[] pixels = ((DataBufferInt)image.getRaster().getDataBuffer()).getData();
byte[] scanline = new byte[image.getWidth()];
/* Compression: RLE */
odo.writeShort(1);
/* Placeholder scanline bytecounts */
long redCountsPosition = raf.getFilePointer();
for(int i = 0; i < image.getHeight(); i++) {
odo.writeShort(0);
}
long greenCountsPosition = raf.getFilePointer();
for(int i = 0; i < image.getHeight(); i++) {
odo.writeShort(0);
}
long blueCountsPosition = raf.getFilePointer();
for(int i = 0; i < image.getHeight(); i++) {
odo.writeShort(0);
}
long alphaCountsPosition = raf.getFilePointer();
for(int i = 0; i < image.getHeight(); i++) {
odo.writeShort(0);
}
PackBitsOutputStream pbos = new PackBitsOutputStream(raf);
/* Image data - Red */
for(int y = 0; y < image.getHeight(); y++) {
for(int x = 0; x < scanline.length; x++) {
scanline[x] = (byte) ((pixels[y * scanline.length + x] >> 16) & 0xFF);
}
long scanlineStart = raf.getFilePointer();
pbos.write(scanline);
pbos.flush();
long scanlineStop = raf.getFilePointer();
raf.seek(redCountsPosition + y * 2);
raf.writeShort((short)(scanlineStop - scanlineStart));
raf.seek(scanlineStop);
}
/* Image data - Green */
for(int y = 0; y < image.getHeight(); y++) {
for(int x = 0; x < scanline.length; x++) {
scanline[x] = (byte) ((pixels[y * scanline.length + x] >> 8) & 0xFF);
}
long scanlineStart = raf.getFilePointer();
pbos.write(scanline);
pbos.flush();
long scanlineStop = raf.getFilePointer();
raf.seek(greenCountsPosition + y * 2);
raf.writeShort((short)(scanlineStop - scanlineStart));
raf.seek(scanlineStop);
}
/* Image data - Blue */
for(int y = 0; y < image.getHeight(); y++) {
for(int x = 0; x < scanline.length; x++) {
scanline[x] = (byte) (pixels[y * scanline.length + x] & 0xFF);
}
long scanlineStart = raf.getFilePointer();
pbos.write(scanline);
pbos.flush();
long scanlineStop = raf.getFilePointer();
raf.seek(blueCountsPosition + y * 2);
raf.writeShort((short)(scanlineStop - scanlineStart));
raf.seek(scanlineStop);
}
/* Image data - Alpha */
for(int y = 0; y < image.getHeight(); y++) {
for(int x = 0; x < scanline.length; x++) {
scanline[x] = (byte) ((pixels[y * scanline.length + x] >> 24) & 0xFF);
}
long scanlineStart = raf.getFilePointer();
pbos.write(scanline);
pbos.flush();
long scanlineStop = raf.getFilePointer();
raf.seek(alphaCountsPosition + y * 2);
raf.writeShort((short)(scanlineStop - scanlineStart));
raf.seek(scanlineStop);
}
}
public void writeLayerPixelData(RandomAccessFile raf, int layerIndex) throws IOException {
System.out.println("Writing image data.");
byte[] scanline = new byte[image.getWidth()];
/* Get tiles */
Map<Point, BitmapTile> tiles = layers[layerIndex].getTiles();
PackBitsOutputStream pbos = new PackBitsOutputStream(raf);
long startOfAlpha = raf.getFilePointer();
int alphaLength;
/* Compression: RLE */
raf.writeShort(1);
/* Alpha scanline counts */
long alphaCountsPosition = raf.getFilePointer();
for(int y = 0; y < image.getHeight(); y++) {
raf.writeShort(0);
}
/* Alpha image data */
for(int y = 0; y < image.getHeight(); y++) {
int yModulus = y & BitmapTile.SIZE_MASK;
for(int x = 0; x < scanline.length; x += BitmapTile.SIZE) {
BitmapTile t = tiles.get(new Point(x >> BitmapTile.SIZE_2, y >> BitmapTile.SIZE_2));
if(t == null) {
Arrays.fill(scanline, x, x + BitmapTile.SIZE, (byte) 0);
} else {
int[] tilePixels = t.getPixels();
for(int offset = 0; offset < BitmapTile.SIZE; offset++) {
scanline[x + offset] = (byte) ((tilePixels[(yModulus << BitmapTile.SIZE_2) + offset] >> 24) & 0xFF);
}
}
}
long scanlineStart = raf.getFilePointer();
pbos.write(scanline);
pbos.flush();
long scanlineEnd = raf.getFilePointer();
raf.seek(alphaCountsPosition + y * 2);
raf.writeShort((short) (scanlineEnd - scanlineStart));
raf.seek(scanlineEnd);
}
long endOfAlpha = raf.getFilePointer();
alphaLength = (int) (endOfAlpha - startOfAlpha);
raf.seek(channelLengthPositions[layerIndex][0]);
raf.writeInt(alphaLength);
raf.seek(endOfAlpha);
long startOfRed = raf.getFilePointer();
int redLength;
/* Compression: RLE */
raf.writeShort(1);
/* Red scanline counts */
long redCountsPosition = raf.getFilePointer();
for(int y = 0; y < image.getHeight(); y++) {
raf.writeShort(0);
}
/* Red image data */
for(int y = 0; y < image.getHeight(); y++) {
int yModulus = y & BitmapTile.SIZE_MASK;
for(int x = 0; x < scanline.length; x += BitmapTile.SIZE) {
BitmapTile t = tiles.get(new Point(x >> BitmapTile.SIZE_2, y >> BitmapTile.SIZE_2));
if(t == null) {
Arrays.fill(scanline, x, x + BitmapTile.SIZE, (byte) 0);
} else {
for(int offset = 0; offset < BitmapTile.SIZE; offset++) {
int[] tilePixels = t.getPixels();
scanline[x + offset] = (byte) ((tilePixels[(yModulus << BitmapTile.SIZE_2) + offset] >> 16) & 0xFF);
}
}
}
long scanlineStart = raf.getFilePointer();
pbos.write(scanline);
pbos.flush();
long scanlineEnd = raf.getFilePointer();
raf.seek(redCountsPosition + y * 2);
raf.writeShort((short) (scanlineEnd - scanlineStart));
raf.seek(scanlineEnd);
}
long endOfRed = raf.getFilePointer();
redLength = (int) (endOfRed - startOfRed);
raf.seek(channelLengthPositions[layerIndex][1]);
raf.writeInt(redLength);
raf.seek(endOfRed);
long startOfGreen = raf.getFilePointer();
int greenLength;
/* Compression: RLE */
raf.writeShort(1);
/* Green scanline counts */
long greenCountsPosition = raf.getFilePointer();
for(int y = 0; y < image.getHeight(); y++) {
raf.writeShort(0);
}
/* Green image data */
for(int y = 0; y < image.getHeight(); y++) {
int yModulus = y & BitmapTile.SIZE_MASK;
for(int x = 0; x < scanline.length; x += BitmapTile.SIZE) {
BitmapTile t = tiles.get(new Point(x >> BitmapTile.SIZE_2, y >> BitmapTile.SIZE_2));
if(t == null) {
Arrays.fill(scanline, x, x + BitmapTile.SIZE, (byte) 0);
} else {
for(int offset = 0; offset < BitmapTile.SIZE; offset++) {
int[] tilePixels = t.getPixels();
scanline[x + offset] = (byte) ((tilePixels[(yModulus << BitmapTile.SIZE_2) + offset] >> 8) & 0xFF);
}
}
}
long scanlineStart = raf.getFilePointer();
pbos.write(scanline);
pbos.flush();
long scanlineEnd = raf.getFilePointer();
raf.seek(greenCountsPosition + y * 2);
raf.writeShort((short)(scanlineEnd - scanlineStart));
raf.seek(scanlineEnd);
}
long endOfGreen = raf.getFilePointer();
greenLength = (int) (endOfGreen - startOfGreen);
raf.seek(channelLengthPositions[layerIndex][2]);
raf.writeInt(greenLength);
raf.seek(endOfGreen);
long startOfBlue = raf.getFilePointer();
int blueLength;
/* Compression: RLE */
raf.writeShort(1);
/* Blue scanline counts */
long blueCountsPosition = raf.getFilePointer();
for(int y = 0; y < image.getHeight(); y++) {
raf.writeShort(0);
}
/* Blue image data */
for(int y = 0; y < image.getHeight(); y++) {
int yModulus = y & BitmapTile.SIZE_MASK;
for(int x = 0; x < scanline.length; x += BitmapTile.SIZE) {
BitmapTile t = tiles.get(new Point(x >> BitmapTile.SIZE_2, y >> BitmapTile.SIZE_2));
if(t == null) {
Arrays.fill(scanline, x, x + BitmapTile.SIZE, (byte) 0);
} else {
int[] tilePixels = t.getPixels();
for(int offset = 0; offset < BitmapTile.SIZE; offset++) {
scanline[x + offset] = (byte) (tilePixels[(yModulus << BitmapTile.SIZE_2) + offset] & 0xFF);
}
}
}
long scanlineStart = raf.getFilePointer();
pbos.write(scanline);
pbos.flush();
long scanlineEnd = raf.getFilePointer();
raf.seek(blueCountsPosition + y * 2);
raf.writeShort((short)(scanlineEnd - scanlineStart));
raf.seek(scanlineEnd);
}
long endOfBlue = raf.getFilePointer();
blueLength = (int) (endOfBlue - startOfBlue);
raf.seek(channelLengthPositions[layerIndex][3]);
raf.writeInt(blueLength);
raf.seek(endOfBlue);
}
}