⛏️ index : haiku.git

/*
 * Copyright 2013, Gerasim Troeglazov, 3dEyes@gmail.com. All rights reserved.
 * Distributed under the terms of the MIT License.
 */ 


#include <File.h>

#include "BaseTranslator.h"
#include "PSDWriter.h"
#include "DataArray.h"


PSDWriter::PSDWriter(BPositionIO *stream)
{	
	fAlphaChannel = -1;
	fStream = stream;
	fReady = false;

	TranslatorBitmap header;
	stream->Seek(0, SEEK_SET);
	status_t err = stream->Read(&header, sizeof(TranslatorBitmap));
	if (err < B_OK)
		return;
	else if (err < (int)sizeof(TranslatorBitmap))
		return;
		
	fBitmapDataPos = stream->Position();

	BRect bounds;
	bounds.left = B_BENDIAN_TO_HOST_FLOAT(header.bounds.left);
	bounds.top = B_BENDIAN_TO_HOST_FLOAT(header.bounds.top);
	bounds.right = B_BENDIAN_TO_HOST_FLOAT(header.bounds.right);
	bounds.bottom = B_BENDIAN_TO_HOST_FLOAT(header.bounds.bottom);
	fInRowBytes = B_BENDIAN_TO_HOST_INT32(header.rowBytes);
	fColorSpace = (color_space)B_BENDIAN_TO_HOST_INT32(header.colors);
	
	switch (fColorSpace) {
		case B_GRAY8:
		case B_CMAP8:
			fChannels = 1;
			break;
		case B_RGBA32:
			fChannels = 4;
			fAlphaChannel = 3;
			break;
		case B_RGB32:
			fChannels = 3;
			break;
		default:
			return;
	};

	fWidth = bounds.IntegerWidth() + 1;
	fHeight = bounds.IntegerHeight() + 1;
	
	fVersion = PSD_FILE;
	fCompression = PSD_COMPRESSED_RAW;

	fReady = true;
}


PSDWriter::~PSDWriter()
{
}


bool
PSDWriter::IsReady(void)
{
	return fReady;
}


void
PSDWriter::SetCompression(int16 compression)
{
	fCompression = compression;
}


void
PSDWriter::SetVersion(int16 ver)
{
	fVersion = ver;
}


status_t
PSDWriter::Encode(BPositionIO *target)
{
	if (!fReady)
		return B_NO_TRANSLATOR;

	status_t status = _LoadChannelsFromRGBA32();
	if (status != B_OK)
		return status;

	// PSD header
	BDataArray psdHeader(64);
	psdHeader << "8BPS"; // Signature
	psdHeader << (uint16)fVersion; // Version
	psdHeader.Repeat(0, 6); // Reserved
	psdHeader << fChannels; // Channels
	psdHeader << fHeight << fWidth; // Image size
	psdHeader << (int16)8; // Depth
	psdHeader << (int16)PSD_COLOR_MODE_RGB; // ColorMode

	// Color mode section
	BDataArray psdColorModeSection(16);
	psdColorModeSection << (uint32)0;

	// Image resource section
	BDataArray psdImageResourceSection(64);
	psdImageResourceSection << "8BIM"; // Block signature
	psdImageResourceSection << (uint16)1005;
	psdImageResourceSection << (uint16)0;
	psdImageResourceSection << (uint32)16;
	uint8 resBlock[16] = {0x00, 0x48, 0x00, 0x00,
		0x00, 0x01, 0x00, 0x01,
		0x00, 0x48, 0x00, 0x00,
		0x00, 0x01, 0x00, 0x01};
	psdImageResourceSection.Append(resBlock, 16);	
	// Current layer info
	psdImageResourceSection << "8BIM"; // Block signature
	psdImageResourceSection << (uint16)1024;
	psdImageResourceSection << (uint16)0;
	psdImageResourceSection << (uint32)2;
	psdImageResourceSection << (uint16)0; // Set current layer to 0

	// Layer & mask section
	BDataArray psdLayersSection;
	psdLayersSection << (uint16)1; // Layers count
	psdLayersSection << (uint32)0; // Layer rect
	psdLayersSection << (uint32)0;
	psdLayersSection << (uint32)fHeight;
	psdLayersSection << (uint32)fWidth;	
	psdLayersSection << (uint16)fChannels;
	
	for (int channelIdx = 0; channelIdx < fChannels; channelIdx++) {
		if (channelIdx == 3)
			psdLayersSection << (int16)-1; // Alpha channel id (-1)
		else
			psdLayersSection << (int16)channelIdx; // Channel num
			
		if (fCompression == PSD_COMPRESSED_RAW) {
			if (fVersion == PSD_FILE) {
					psdLayersSection << (uint32)(psdChannel[channelIdx].Length()
						+ sizeof(int16));
			} else {
					psdLayersSection << (uint64)(psdChannel[channelIdx].Length()
						+ sizeof(int16));
			}
		} else {
			if (fVersion == PSD_FILE) {
				psdLayersSection << (uint32)(psdChannel[channelIdx].Length()
					+ psdByteCounts[channelIdx].Length() + sizeof(int16));
			} else {
				psdLayersSection << (uint64)(psdChannel[channelIdx].Length()
					+ psdByteCounts[channelIdx].Length() + sizeof(int16));
			}
		}
	}

	psdLayersSection << "8BIM";
	psdLayersSection << "norm"; // Blend mode = norm
	psdLayersSection << (uint8)255; // Opacity
	psdLayersSection << (uint8)0; // Clipping
	psdLayersSection << (uint8)1; // Flags
	psdLayersSection << (uint8)0; // Flags
	psdLayersSection << (uint32)24; // Extra data length
	psdLayersSection << (uint32)0; // Mask info
	psdLayersSection << (uint32)0;

	psdLayersSection << (uint8)15; // Layer name length
	uint8 layerName[16] = {"Layer #1       "};
	psdLayersSection.Append(layerName, 15); // Layer name

	if (fCompression == PSD_COMPRESSED_RAW) {
		for (int channelIdx = 0; channelIdx < fChannels; channelIdx++) {
			psdLayersSection << fCompression; // Compression mode
			psdLayersSection.Append(psdChannel[channelIdx]); // Channel data
		}
	} else {	
		for (int channelIdx = 0; channelIdx < fChannels; channelIdx++) {
			psdLayersSection << fCompression; // Compression mode
			psdLayersSection.Append(psdByteCounts[channelIdx]); // Bytes count
			psdLayersSection.Append(psdChannel[channelIdx]); // Channel data
		}
	}

	if (fCompression == PSD_COMPRESSED_RLE
		&& psdLayersSection.Length() % 2 != 0) {
		psdLayersSection << (uint8)0;
	}

	psdHeader.WriteToStream(target);
	
	psdColorModeSection.WriteToStream(target);
	
	_WriteUInt32ToStream(target, psdImageResourceSection.Length());
	psdImageResourceSection.WriteToStream(target);

	if (fVersion == PSD_FILE) {
		_WriteUInt32ToStream(target, psdLayersSection.Length() + sizeof(int32));
		_WriteUInt32ToStream(target, psdLayersSection.Length());
	} else {
		_WriteUInt64ToStream(target, psdLayersSection.Length() + sizeof(int64));
		_WriteUInt64ToStream(target, psdLayersSection.Length());		
	}
	psdLayersSection.WriteToStream(target);

	// Merged layer
	_WriteUInt16ToStream(target, fCompression); // Compression mode
	
	if (fCompression == PSD_COMPRESSED_RLE) {
		for (int channelIdx = 0; channelIdx < fChannels; channelIdx++)
			psdByteCounts[channelIdx].WriteToStream(target);
	}

	for (int channelIdx = 0; channelIdx < fChannels; channelIdx++)
		psdChannel[channelIdx].WriteToStream(target);

	return B_OK;
}


BDataArray*
PSDWriter::_PackBits(uint8 *buff, int32  len)
{
	BDataArray *packedBits = new BDataArray();

	int32  count = len;  
	len = 0;

	while (count > 0) {
		int i;
		for (i = 0; (i < 128) && (buff[0] == buff[i]) && (count - i > 0); i++);
		if (i < 2) {
			for (i = 0; i < 128; i++) {
				bool b1 = buff[i] != buff[i + 1];
				bool b3 = buff[i] != buff[i + 2];
				bool b2 = count - (i + 2) < 1;
				if (count - (i + 1) <= 0)
					break;
				if (!(b1 || b2 || b3))
					break;
			}

			if (count == 1)
				i = 1;

			if (i > 0) {
				packedBits->Append((uint8)(i - 1));
				for (int j = 0; j < i; j++)
					packedBits->Append((uint8)buff[j]);
				buff += i;
				count -= i;
				len += (i + 1);
			}
		} else {
			packedBits->Append((uint8)(-(i - 1)));
			packedBits->Append((uint8)(*buff));
			buff += i;
			count -= i;
			len += 2;
		}
	}
	return packedBits;
}


status_t
PSDWriter::_LoadChannelsFromRGBA32(void)
{
	if (fColorSpace != B_RGB32 && fColorSpace != B_RGBA32)
		return B_NO_TRANSLATOR;

	int32 channelSize = fWidth * fHeight;

	fStream->Seek(fBitmapDataPos, SEEK_SET);

	if (fCompression == PSD_COMPRESSED_RAW) {
		for (int i = 0; i < channelSize; i++) {
			uint8 rgba[4];
			fStream->Read(rgba, sizeof(uint32));
			psdChannel[0].Append((uint8)rgba[2]); // Red channel
			psdChannel[1].Append((uint8)rgba[1]); // Green channel
			psdChannel[2].Append((uint8)rgba[0]); // Blue channel
			if (fChannels == 4)
				psdChannel[3].Append((uint8)rgba[3]); // Alpha channel
		}
		return B_OK;
	} else if (fCompression == PSD_COMPRESSED_RLE) {
		for (int32 h = 0; h < fHeight; h++) {
			BDataArray lineData[4];
			
			for (int32 w = 0; w < fWidth; w++) {
				uint8 rgba[4];
				fStream->Read(rgba, sizeof(uint32));
				lineData[0].Append((uint8)rgba[2]); // Red channel
				lineData[1].Append((uint8)rgba[1]); // Green channel
				lineData[2].Append((uint8)rgba[0]); // Blue channel
				if (fChannels == 4)
					lineData[3].Append((uint8)rgba[3]); // Alpha channel
			}
			
			for (int channelIdx = 0; channelIdx < fChannels; channelIdx++) {
				BDataArray *packedLine = _PackBits(lineData[channelIdx].Buffer(),
					lineData[channelIdx].Length());

				if (fVersion == PSD_FILE)
					psdByteCounts[channelIdx].Append((uint16)packedLine->Length());
				else
					psdByteCounts[channelIdx].Append((uint32)packedLine->Length());

				psdChannel[channelIdx].Append(*packedLine);
				delete packedLine;
			}
		}
		return B_OK;
	}
	return B_NO_TRANSLATOR;
}


void
PSDWriter::_WriteInt64ToStream(BPositionIO *stream, int64 val)
{
	val = B_HOST_TO_BENDIAN_INT64(val);
	stream->Write(&val, sizeof(int32));
}


void
PSDWriter::_WriteUInt64ToStream(BPositionIO *stream, uint64 val)
{
	val = B_HOST_TO_BENDIAN_INT64(val);
	stream->Write(&val, sizeof(uint64));
}


void
PSDWriter::_WriteInt32ToStream(BPositionIO *stream, int32 val)
{
	val = B_HOST_TO_BENDIAN_INT32(val);
	stream->Write(&val, sizeof(int32));
}


void
PSDWriter::_WriteUInt32ToStream(BPositionIO *stream, uint32 val)
{
	val = B_HOST_TO_BENDIAN_INT32(val);
	stream->Write(&val, sizeof(uint32));
}


void
PSDWriter::_WriteInt16ToStream(BPositionIO *stream, int16 val)
{
	val = B_HOST_TO_BENDIAN_INT16(val);
	stream->Write(&val, sizeof(int16));
}


void
PSDWriter::_WriteUInt16ToStream(BPositionIO *stream, uint16 val)
{
	val = B_HOST_TO_BENDIAN_INT16(val);
	stream->Write(&val, sizeof(int16));
}


void
PSDWriter::_WriteInt8ToStream(BPositionIO *stream, int8 val)
{
	stream->Write(&val, sizeof(int8));
}


void
PSDWriter::_WriteUInt8ToStream(BPositionIO *stream, uint8 val)
{
	stream->Write(&val, sizeof(uint8));
}


void
PSDWriter::_WriteFillBlockToStream(BPositionIO *stream,
	uint8 val, size_t count)
{
	for (size_t i = 0; i < count; i++)
		stream->Write(&val, sizeof(uint8));
}


void
PSDWriter::_WriteBlockToStream(BPositionIO *stream,
	uint8 *val, size_t count)
{
	stream->Write(val, count);
}