* Copyright 2013, Gerasim Troeglazov, 3dEyes@gmail.com. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include "PSDLoader.h"
#include <Catalog.h>
#include "BaseTranslator.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "PSDLoader"
PSDLoader::PSDLoader(BPositionIO *src)
{
fLoaded = false;
fStream = src;
fStream->Seek(0, SEEK_END);
fStreamSize = fStream->Position();
fStream->Seek(0, SEEK_SET);
if (fStreamSize <= 0)
return;
fStream->Seek(0, SEEK_SET);
fSignature = _GetInt32FromStream(fStream);
if (fSignature != 0x38425053)
return;
fVersion = _GetInt16FromStream(fStream);
_SkipStreamBlock(fStream, 6);
fChannels = _GetInt16FromStream(fStream);
fHeight = _GetInt32FromStream(fStream);
fWidth = _GetInt32FromStream(fStream);
fDepth = _GetInt16FromStream(fStream);
fColorFormat = _GetInt16FromStream(fStream);
fColorModeDataSize = _GetInt32FromStream(fStream);
fColorModeDataPos = fStream->Position();
_SkipStreamBlock(fStream, fColorModeDataSize);
fImageResourceSectionSize = _GetInt32FromStream(fStream);
fImageResourceSectionPos = fStream->Position();
_SkipStreamBlock(fStream, fImageResourceSectionSize);
if (fVersion == PSD_FILE)
_SkipStreamBlock(fStream, _GetInt32FromStream(fStream));
else if (fVersion == PSB_FILE)
_SkipStreamBlock(fStream, _GetInt64FromStream(fStream));
else
return;
fCompression = _GetInt16FromStream(fStream);
fStreamPos = fStream->Position();
fLoaded = true;
}
PSDLoader::~PSDLoader()
{
}
bool
PSDLoader::IsLoaded(void)
{
return fLoaded;
}
bool
PSDLoader::IsSupported(void)
{
if (!fLoaded)
return false;
if (fVersion != PSD_FILE
&& fVersion != PSB_FILE) {
return false;
}
if (fChannels < 0 || fChannels > PSD_MAX_CHANNELS)
return false;
if (fDepth > 16)
return false;
if (_ColorFormat() == PSD_COLOR_FORMAT_UNSUPPORTED)
return false;
if (fCompression != PSD_COMPRESSED_RAW
&& fCompression != PSD_COMPRESSED_RLE) {
return false;
}
return true;
}
BString
PSDLoader::ColorFormatName(void)
{
switch (fColorFormat) {
case PSD_COLOR_MODE_BITS:
return B_TRANSLATE("Bitmap");
case PSD_COLOR_MODE_GRAYSCALE:
return B_TRANSLATE("Grayscale");
case PSD_COLOR_MODE_INDEXED:
return B_TRANSLATE("Indexed");
case PSD_COLOR_MODE_RGB:
return fChannels > 3 ? B_TRANSLATE("RGBA") : B_TRANSLATE("RGB");
case PSD_COLOR_MODE_CMYK:
return B_TRANSLATE("CMYK");
case PSD_COLOR_MODE_MULTICHANNEL:
return B_TRANSLATE("Multichannel");
case PSD_COLOR_MODE_LAB:
return B_TRANSLATE("Lab");
case PSD_COLOR_MODE_DUOTONE:
return B_TRANSLATE("Duotone");
}
return "";
}
psd_color_format
PSDLoader::_ColorFormat(void)
{
psd_color_format format = PSD_COLOR_FORMAT_UNSUPPORTED;
if (!fLoaded)
return format;
switch (fColorFormat) {
case PSD_COLOR_MODE_BITS:
format = PSD_COLOR_FORMAT_BITMAP;
break;
case PSD_COLOR_MODE_RGB:
if (fChannels == 3)
format = PSD_COLOR_FORMAT_RGB;
else if (fChannels >= 4)
format = PSD_COLOR_FORMAT_RGB_A;
break;
case PSD_COLOR_MODE_GRAYSCALE:
if (fChannels == 1)
format = PSD_COLOR_FORMAT_GRAY;
else if (fChannels == 2)
format = PSD_COLOR_FORMAT_GRAY_A;
break;
case PSD_COLOR_MODE_MULTICHANNEL:
if (fChannels == 3)
format = PSD_COLOR_FORMAT_MULTICHANNEL;
break;
case PSD_COLOR_MODE_CMYK:
if (fChannels == 3)
format = PSD_COLOR_FORMAT_MULTICHANNEL;
else if (fChannels == 4)
format = PSD_COLOR_FORMAT_CMYK;
else if (fChannels > 4)
format = PSD_COLOR_FORMAT_CMYK_A;
break;
case PSD_COLOR_MODE_LAB:
if (fChannels == 3)
format = PSD_COLOR_FORMAT_LAB;
else if (fChannels > 3)
format = PSD_COLOR_FORMAT_LAB_A;
break;
case PSD_COLOR_MODE_DUOTONE:
if (fChannels >= 1)
format = PSD_COLOR_FORMAT_DUOTONE;
break;
case PSD_COLOR_MODE_INDEXED:
if (fChannels >= 1 && fColorModeDataSize >= 3)
format = PSD_COLOR_FORMAT_INDEXED;
break;
default:
break;
};
return format;
}
status_t
PSDLoader::Decode(BPositionIO *target)
{
if (!IsSupported())
return B_NO_TRANSLATOR;
fStreamBuffer = new uint8[fStreamSize];
fStream->Seek(0, SEEK_SET);
fStream->Read(fStreamBuffer, fStreamSize);
int32 depthBytes = fDepth / 8;
int32 rowBytes = (fWidth * fDepth) / 8;
int32 channelBytes = rowBytes * fHeight;
uint8 *imageData[PSD_MAX_CHANNELS];
for (int i = 0; i < fChannels; i++)
imageData[i] = new uint8[channelBytes];
switch (fCompression) {
case PSD_COMPRESSED_RAW:
{
for (int channelIdx = 0; channelIdx < fChannels; channelIdx++) {
uint8 *ptr = imageData[channelIdx];
for (int i = 0; i < channelBytes; i++, ptr++)
*ptr = (uint8)fStreamBuffer[fStreamPos++];
}
break;
}
case PSD_COMPRESSED_RLE:
{
if (fVersion == PSD_FILE)
fStreamPos += fHeight * fChannels * 2;
else if (fVersion == PSB_FILE)
fStreamPos += fHeight * fChannels * 4;
for (int channelIdx = 0; channelIdx < fChannels; channelIdx++) {
uint8 *ptr = imageData[channelIdx];
int count = 0;
while (count < channelBytes) {
uint8 len = (uint8)fStreamBuffer[fStreamPos++];
if (len == 128) {
continue;
} else if (len < 128) {
len++;
count += len;
while (len) {
*ptr++ = (int8)fStreamBuffer[fStreamPos++];
len--;
}
} else if (len > 128) {
int8 val = (int8)fStreamBuffer[fStreamPos++];
len ^= 255;
len += 2;
count += len;
while (len) {
*ptr++ = val;
len--;
}
}
}
}
break;
}
default:
delete[] fStreamBuffer;
for (int i = 0; i < fChannels; i++)
delete[] imageData[i];
return B_NO_TRANSLATOR;
}
delete[] fStreamBuffer;
TranslatorBitmap bitsHeader;
bitsHeader.magic = B_TRANSLATOR_BITMAP;
bitsHeader.bounds.left = 0;
bitsHeader.bounds.top = 0;
bitsHeader.bounds.right = fWidth - 1;
bitsHeader.bounds.bottom = fHeight - 1;
psd_color_format colorFormat = _ColorFormat();
if (colorFormat == PSD_COLOR_FORMAT_BITMAP) {
bitsHeader.rowBytes = rowBytes;
bitsHeader.dataSize = channelBytes;
bitsHeader.colors = B_GRAY1;
} else {
bitsHeader.rowBytes = sizeof(uint32) * fWidth;
bitsHeader.colors = B_RGBA32;
bitsHeader.dataSize = bitsHeader.rowBytes * fHeight;
}
if (swap_data(B_UINT32_TYPE, &bitsHeader,
sizeof(TranslatorBitmap), B_SWAP_HOST_TO_BENDIAN) != B_OK) {
return B_NO_TRANSLATOR;
}
target->Write(&bitsHeader, sizeof(TranslatorBitmap));
uint8 *lineData = new uint8[fWidth * sizeof(uint32)];
switch (colorFormat) {
case PSD_COLOR_FORMAT_BITMAP:
{
int32 rowBytes = (fWidth / 8 ) * fHeight;
for (int32 i = 0; i < rowBytes; i++)
imageData[0][i]^=255;
target->Write(imageData[0], rowBytes);
break;
}
case PSD_COLOR_FORMAT_INDEXED:
{
int32 paletteSize = fColorModeDataSize / 3;
uint8 *colorData = new uint8[fColorModeDataSize];
fStream->Seek(fColorModeDataPos, SEEK_SET);
fStream->Read(colorData, fColorModeDataSize);
if (_ParseImageResources() != B_OK)
fTransparentIndex = 256;
uint8 *redPalette = colorData;
uint8 *greenPalette = colorData + paletteSize;
uint8 *bluePalette = colorData + paletteSize * 2;
int32 index = 0;
for (int h = 0; h < fHeight; h++) {
uint8 *ptr = lineData;
for (int w = 0; w < fWidth; w++) {
uint8 colorIndex = imageData[0][index];
ptr[0] = bluePalette[colorIndex];
ptr[1] = greenPalette[colorIndex];
ptr[2] = redPalette[colorIndex];
ptr[3] = colorIndex == fTransparentIndex ? 0 : 255;
ptr += sizeof(uint32);
index++;
}
target->Write(lineData, fWidth * sizeof(uint32));
}
delete[] colorData;
break;
}
case PSD_COLOR_FORMAT_DUOTONE:
case PSD_COLOR_FORMAT_GRAY:
case PSD_COLOR_FORMAT_GRAY_A:
{
bool isAlpha = colorFormat == PSD_COLOR_FORMAT_GRAY_A;
int32 index = 0;
for (int h = 0; h < fHeight; h++) {
uint8 *ptr = lineData;
for (int w = 0; w < fWidth; w++) {
ptr[0] = imageData[0][index];
ptr[1] = imageData[0][index];
ptr[2] = imageData[0][index];
ptr[3] = isAlpha ? imageData[1][index] : 255;
ptr += sizeof(uint32);
index += depthBytes;
}
target->Write(lineData, fWidth * sizeof(uint32));
}
break;
}
case PSD_COLOR_FORMAT_MULTICHANNEL:
case PSD_COLOR_FORMAT_RGB:
case PSD_COLOR_FORMAT_RGB_A:
{
bool isAlpha = colorFormat == PSD_COLOR_FORMAT_RGB_A;
int32 index = 0;
for (int h = 0; h < fHeight; h++) {
uint8 *ptr = lineData;
for (int w = 0; w < fWidth; w++) {
ptr[0] = imageData[2][index];
ptr[1] = imageData[1][index];
ptr[2] = imageData[0][index];
ptr[3] = isAlpha ? imageData[3][index] : 255;
ptr += sizeof(uint32);
index += depthBytes;
}
target->Write(lineData, fWidth * sizeof(uint32));
}
break;
}
case PSD_COLOR_FORMAT_CMYK:
case PSD_COLOR_FORMAT_CMYK_A:
{
bool isAlpha = colorFormat == PSD_COLOR_FORMAT_CMYK_A;
int32 index = 0;
for (int h = 0; h < fHeight; h++) {
uint8 *ptr = lineData;
for (int w = 0; w < fWidth; w++) {
double c = 1.0 - imageData[0][index] / 255.0;
double m = 1.0 - imageData[1][index] / 255.0;
double y = 1.0 - imageData[2][index] / 255.0;
double k = 1.0 - imageData[3][index] / 255.0;
ptr[0] = (uint8)((1.0 - (y * (1.0 - k) + k)) * 255.0);
ptr[1] = (uint8)((1.0 - (m * (1.0 - k) + k)) * 255.0);
ptr[2] = (uint8)((1.0 - (c * (1.0 - k) + k)) * 255.0);
ptr[3] = isAlpha ? imageData[4][index] : 255;
ptr += sizeof(uint32);
index += depthBytes;
}
target->Write(lineData, fWidth * sizeof(uint32));
}
break;
}
case PSD_COLOR_FORMAT_LAB:
case PSD_COLOR_FORMAT_LAB_A:
{
bool isAlpha = colorFormat == PSD_COLOR_FORMAT_LAB_A;
int32 index = 0;
for (int h = 0; h < fHeight; h++) {
uint8 *ptr = lineData;
for (int w = 0; w < fWidth; w++) {
double L = imageData[0][index] / 255.0 * 100.0;
double a = imageData[1][index] - 128.0;
double b = imageData[2][index] - 128.0;
double Y = L * (1.0 / 116.0) + 16.0 / 116.0;
double X = a * (1.0 / 500.0) + Y;
double Z = b * (-1.0 / 200.0) + Y;
X = X > 6.0 / 29.0 ? X * X * X : X * (108.0 / 841.0)
- (432.0 / 24389.0);
Y = L > 8.0 ? Y * Y * Y : L * (27.0 / 24389.0);
Z = Z > 6.0 / 29.0 ? Z * Z * Z : Z * (108.0 / 841.0)
- (432.0 / 24389.0);
double R = X * (1219569.0 / 395920.0)
+ Y * (-608687.0 / 395920.0)
+ Z * (-107481.0 / 197960.0);
double G = X * (-80960619.0 / 87888100.0)
+ Y * (82435961.0 / 43944050.0)
+ Z * (3976797.0 / 87888100.0);
double B = X * (93813.0 / 1774030.0)
+ Y * (-180961.0 / 887015.0)
+ Z * (107481.0 / 93370.0);
R = R > 0.0031308 ? pow(R, 1.0 / 2.4) * 1.055 - 0.055
: R * 12.92;
G = G > 0.0031308 ? pow(G, 1.0 / 2.4) * 1.055 - 0.055
: G * 12.92;
B = B > 0.0031308 ? pow(B, 1.0 / 2.4) * 1.055 - 0.055
: B * 12.92;
R = (R < 0) ? 0 : ((R > 1) ? 1 : R);
G = (G < 0) ? 0 : ((G > 1) ? 1 : G);
B = (B < 0) ? 0 : ((B > 1) ? 1 : B);
ptr[0] = (uint8)(B * 255.0);
ptr[1] = (uint8)(G * 255.0);
ptr[2] = (uint8)(R * 255.0);
ptr[3] = isAlpha ? imageData[3][index] : 255;
ptr += sizeof(uint32);
index += depthBytes;
}
target->Write(lineData, fWidth * sizeof(uint32));
}
break;
}
default:
break;
};
delete[] lineData;
for (int i = 0; i < fChannels; i++)
delete[] imageData[i];
return B_OK;
}
int64
PSDLoader::_GetInt64FromStream(BPositionIO *in)
{
int64 ret;
in->Read(&ret, sizeof(int64));
return B_BENDIAN_TO_HOST_INT64(ret);
}
int32
PSDLoader::_GetInt32FromStream(BPositionIO *in)
{
int32 ret;
in->Read(&ret, sizeof(int32));
return B_BENDIAN_TO_HOST_INT32(ret);
}
int16
PSDLoader::_GetInt16FromStream(BPositionIO *in)
{
int16 ret;
in->Read(&ret, sizeof(int16));
return B_BENDIAN_TO_HOST_INT16(ret);
}
int8
PSDLoader::_GetInt8FromStream(BPositionIO *in)
{
int8 ret;
in->Read(&ret, sizeof(int8));
return ret;
}
uint8
PSDLoader::_GetUInt8FromStream(BPositionIO *in)
{
uint8 ret;
in->Read(&ret, sizeof(uint8));
return ret;
}
void
PSDLoader::_SkipStreamBlock(BPositionIO *in, size_t count)
{
in->Seek(count, SEEK_CUR);
}
status_t
PSDLoader::_ParseImageResources(void)
{
if (!fLoaded && fImageResourceSectionSize == 0)
return B_ERROR;
size_t currentPos = fStream->Position();
fStream->Seek(fImageResourceSectionPos, SEEK_SET);
while (fStream->Position() < currentPos + fImageResourceSectionSize) {
int32 resBlockSignature = _GetInt32FromStream(fStream);
if (resBlockSignature != 0x3842494D)
return B_ERROR;
uint16 resID = _GetInt16FromStream(fStream);
BString resName, name;
int nameLength = 0;
while (true) {
int charData = _GetUInt8FromStream(fStream);
nameLength++;
if (charData == 0) {
if (nameLength % 2 == 1) {
_GetUInt8FromStream(fStream);
nameLength++;
}
break;
} else
name += charData;
resName = name;
}
uint32 resSize = _GetInt32FromStream(fStream);
if (resSize % 2 == 1)
resSize++;
switch (resID) {
case 0x0417:
fTransparentIndex = _GetInt16FromStream(fStream);
break;
default:
_SkipStreamBlock(fStream, resSize);
}
}
fStream->Seek(currentPos, SEEK_SET);
return B_OK;
}