* 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;
BDataArray psdHeader(64);
psdHeader << "8BPS";
psdHeader << (uint16)fVersion;
psdHeader.Repeat(0, 6);
psdHeader << fChannels;
psdHeader << fHeight << fWidth;
psdHeader << (int16)8;
psdHeader << (int16)PSD_COLOR_MODE_RGB;
BDataArray psdColorModeSection(16);
psdColorModeSection << (uint32)0;
BDataArray psdImageResourceSection(64);
psdImageResourceSection << "8BIM";
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);
psdImageResourceSection << "8BIM";
psdImageResourceSection << (uint16)1024;
psdImageResourceSection << (uint16)0;
psdImageResourceSection << (uint32)2;
psdImageResourceSection << (uint16)0;
BDataArray psdLayersSection;
psdLayersSection << (uint16)1;
psdLayersSection << (uint32)0;
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;
else
psdLayersSection << (int16)channelIdx;
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";
psdLayersSection << (uint8)255;
psdLayersSection << (uint8)0;
psdLayersSection << (uint8)1;
psdLayersSection << (uint8)0;
psdLayersSection << (uint32)24;
psdLayersSection << (uint32)0;
psdLayersSection << (uint32)0;
psdLayersSection << (uint8)15;
uint8 layerName[16] = {"Layer #1 "};
psdLayersSection.Append(layerName, 15);
if (fCompression == PSD_COMPRESSED_RAW) {
for (int channelIdx = 0; channelIdx < fChannels; channelIdx++) {
psdLayersSection << fCompression;
psdLayersSection.Append(psdChannel[channelIdx]);
}
} else {
for (int channelIdx = 0; channelIdx < fChannels; channelIdx++) {
psdLayersSection << fCompression;
psdLayersSection.Append(psdByteCounts[channelIdx]);
psdLayersSection.Append(psdChannel[channelIdx]);
}
}
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);
_WriteUInt16ToStream(target, fCompression);
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]);
psdChannel[1].Append((uint8)rgba[1]);
psdChannel[2].Append((uint8)rgba[0]);
if (fChannels == 4)
psdChannel[3].Append((uint8)rgba[3]);
}
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]);
lineData[1].Append((uint8)rgba[1]);
lineData[2].Append((uint8)rgba[0]);
if (fChannels == 4)
lineData[3].Append((uint8)rgba[3]);
}
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);
}