PSDTranslator: Add Photoshop Big (PSB) format support

This commit is contained in:
Gerasim Troeglazov 2013-12-20 09:30:03 +00:00
parent a47ae0d08e
commit 6d7acad27d
9 changed files with 152 additions and 51 deletions

View File

@ -22,18 +22,31 @@ ConfigView::ConfigView(TranslatorSettings *settings)
{
fSettings = settings;
BPopUpMenu* popupMenu = new BPopUpMenu("popup_compression");
BPopUpMenu* compressionPopupMenu = new BPopUpMenu("popup_compression");
uint32 currentCompression =
fSettings->SetGetInt32(PSD_SETTING_COMPRESSION);
_AddItemToMenu(popupMenu, "Uncompressed",
PSD_COMPRESSED_RAW, currentCompression);
_AddItemToMenu(popupMenu, "RLE",
PSD_COMPRESSED_RLE, currentCompression);
_AddItemToMenu(compressionPopupMenu, "Uncompressed",
MSG_COMPRESSION_CHANGED, PSD_COMPRESSED_RAW, currentCompression);
_AddItemToMenu(compressionPopupMenu, "RLE",
MSG_COMPRESSION_CHANGED, PSD_COMPRESSED_RLE, currentCompression);
fCompressionField = new BMenuField("compression",
"Compression: ", popupMenu);
fCompressionField = new BMenuField("compression",
"Compression: ", compressionPopupMenu);
BPopUpMenu* versionPopupMenu = new BPopUpMenu("popup_version");
uint32 currentVersion =
fSettings->SetGetInt32(PSD_SETTING_VERSION);
_AddItemToMenu(versionPopupMenu, "Photoshop Document (PSD File)",
MSG_VERSION_CHANGED, PSD_FILE, currentVersion);
_AddItemToMenu(versionPopupMenu, "Photoshop Big Document (PSB File)",
MSG_VERSION_CHANGED, PSB_FILE, currentVersion);
fVersionField = new BMenuField("version",
"Format: ", versionPopupMenu);
BAlignment leftAlignment(B_ALIGN_LEFT, B_ALIGN_VERTICAL_UNSET);
@ -66,6 +79,8 @@ ConfigView::ConfigView(TranslatorSettings *settings)
stringView->SetExplicitAlignment(leftAlignment);
AddChild(stringView);
AddChild(fVersionField);
AddChild(fCompressionField);
AddChild(BSpaceLayoutItem::CreateGlue());
@ -86,6 +101,7 @@ void
ConfigView::AllAttached()
{
fCompressionField->Menu()->SetTargetForItems(this);
fVersionField->Menu()->SetTargetForItems(this);
}
@ -101,6 +117,14 @@ ConfigView::MessageReceived(BMessage* message)
}
break;
}
case MSG_VERSION_CHANGED: {
int32 value;
if (message->FindInt32("value", &value) >= B_OK) {
fSettings->SetGetInt32(PSD_SETTING_VERSION, &value);
fSettings->SaveSettings();
}
break;
}
default:
BView::MessageReceived(message);
}
@ -109,9 +133,9 @@ ConfigView::MessageReceived(BMessage* message)
void
ConfigView::_AddItemToMenu(BMenu* menu, const char* label,
uint32 value, uint32 current_value)
uint32 mess, uint32 value, uint32 current_value)
{
BMessage* message = new BMessage(MSG_COMPRESSION_CHANGED);
BMessage* message = new BMessage(mess);
message->AddInt32("value", value);
BMenuItem* item = new BMenuItem(label, message);
item->SetMarked(value == current_value);

View File

@ -16,6 +16,7 @@
#include <MenuField.h>
#define MSG_COMPRESSION_CHANGED 'cchg'
#define MSG_VERSION_CHANGED 'vchg'
class ConfigView : public BGroupView {
public:
@ -27,10 +28,11 @@ class ConfigView : public BGroupView {
private:
void _AddItemToMenu(BMenu* menu, const char* label,
uint32 value, uint32 current_value);
uint32 mess, uint32 value, uint32 current_value);
BTextView* fCopyrightView;
BMenuField* fCompressionField;
BMenuField* fVersionField;
TranslatorSettings *fSettings;
};

View File

@ -46,7 +46,12 @@ PSDLoader::PSDLoader(BPositionIO *src)
_SkipStreamBlock(fStream, fImageResourceSectionSize);
// Skip [layer and mask] block
_SkipStreamBlock(fStream, _GetInt32FromStream(fStream));
if (fVersion == PSD_FILE)
_SkipStreamBlock(fStream, _GetInt32FromStream(fStream));
else if (fVersion == PSB_FILE)
_SkipStreamBlock(fStream, _GetInt64FromStream(fStream));
else
return;
fCompression = _GetInt16FromStream(fStream);
@ -74,8 +79,10 @@ PSDLoader::IsSupported(void)
if (!fLoaded)
return false;
if (fVersion != 1)
if (fVersion != PSD_FILE
&& fVersion != PSB_FILE) {
return false;
}
if (fChannels < 0 || fChannels > PSD_MAX_CHANNELS)
return false;
@ -87,8 +94,9 @@ PSDLoader::IsSupported(void)
return false;
if (fCompression != PSD_COMPRESSED_RAW
&& fCompression != PSD_COMPRESSED_RLE)
&& fCompression != PSD_COMPRESSED_RLE) {
return false;
}
return true;
}
@ -207,7 +215,11 @@ PSDLoader::Decode(BPositionIO *target)
}
case PSD_COMPRESSED_RLE:
{
fStreamPos += fHeight * fChannels * 2;
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];
// Read the RLE data.
@ -448,6 +460,15 @@ PSDLoader::Decode(BPositionIO *target)
}
int64
PSDLoader::_GetInt64FromStream(BPositionIO *in)
{
int64 ret;
in->Read(&ret, sizeof(int64));
return B_BENDIAN_TO_HOST_INT64(ret);
}
int32
PSDLoader::_GetInt32FromStream(BPositionIO *in)
{

View File

@ -25,6 +25,12 @@
#define PSD_MAX_CHANNELS 16
enum psd_version {
PSD_FILE = 1,
PSB_FILE = 2
};
enum psd_compressed_type {
PSD_COMPRESSED_RAW = 0,
PSD_COMPRESSED_RLE = 1
@ -72,6 +78,7 @@ public:
BString ColorFormatName(void);
private:
int64 _GetInt64FromStream(BPositionIO *in);
int32 _GetInt32FromStream(BPositionIO *in);
int16 _GetInt16FromStream(BPositionIO *in);
uint8 _GetUInt8FromStream(BPositionIO *in);

View File

@ -61,7 +61,8 @@ static const translation_format sOutputFormats[] = {
static const TranSetting sDefaultSettings[] = {
{B_TRANSLATOR_EXT_HEADER_ONLY, TRAN_SETTING_BOOL, false},
{B_TRANSLATOR_EXT_DATA_ONLY, TRAN_SETTING_BOOL, false},
{PSD_SETTING_COMPRESSION, TRAN_SETTING_INT32, PSD_COMPRESSED_RLE}
{PSD_SETTING_COMPRESSION, TRAN_SETTING_INT32, PSD_COMPRESSED_RLE},
{PSD_SETTING_VERSION, TRAN_SETTING_INT32, PSD_FILE}
};
const uint32 kNumInputFormats = sizeof(sInputFormats)
@ -141,11 +142,17 @@ PSDTranslator::DerivedTranslate(BPositionIO *source,
}
case 1:
{
if (outType == PSD_IMAGE_FORMAT) {
if (outType == PSD_IMAGE_FORMAT) {
PSDWriter psdFile(source);
uint32 compression =
fSettings->SetGetInt32(PSD_SETTING_COMPRESSION);
uint32 version =
fSettings->SetGetInt32(PSD_SETTING_VERSION);
psdFile.SetCompression(compression);
psdFile.SetVersion(version);
if (psdFile.IsReady())
return psdFile.Encode(target);
}

View File

@ -22,8 +22,9 @@
#define DOCUMENT_INDEX "/documentIndex"
#define PSD_SETTING_COMPRESSION "psd /compression"
#define PSD_SETTING_VERSION "psd /psdversion"
#define PSD_TRANSLATOR_VERSION B_TRANSLATION_MAKE_VERSION(1, 2, 1)
#define PSD_TRANSLATOR_VERSION B_TRANSLATION_MAKE_VERSION(1, 3, 0)
#define PSD_IMAGE_FORMAT 'PSD '
#define PSD_IN_QUALITY 0.7

View File

@ -6,10 +6,10 @@ resource app_signature "application/x-vnd.Haiku-PSDTranslator";
resource app_version {
major = 1,
middle = 2,
minor = 1,
middle = 3,
minor = 0,
variety = 0,
internal = 0,
short_info = "1.2.1",
short_info = "1.3.0",
long_info = "Haiku PSDTranslator Add-Ons."
};

View File

@ -33,11 +33,14 @@ PSDWriter::PSDWriter(BPositionIO *stream)
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);
if (fColorSpace != B_RGB32 && fColorSpace != B_RGBA32)
return;
fChannels = fColorSpace == B_RGB32 ? 3 : 4;
fWidth = bounds.IntegerWidth() + 1;
fHeight = bounds.IntegerHeight() + 1;
fVersion = PSD_FILE;
fCompression = PSD_COMPRESSED_RAW;
fReady = true;
@ -63,6 +66,13 @@ PSDWriter::SetCompression(int16 compression)
}
void
PSDWriter::SetVersion(int16 ver)
{
fVersion = ver;
}
status_t
PSDWriter::Encode(BPositionIO *target)
{
@ -98,16 +108,18 @@ PSDWriter::Encode(BPositionIO *target)
lineData[2].Append((uint8)rgba[0]); // Blue channel
if (fChannels == 4)
lineData[3].Append((uint8)rgba[3]); // Alpha channel
else
lineData[3].Append((uint8)255);
}
for (int channelIdx = 0; channelIdx < fChannels; channelIdx++) {
BDataArray *packedLine = PackBits(lineData[channelIdx].Buffer(),
lineData[channelIdx].Length());
psdByteCounts[channelIdx].Append((uint16)packedLine->Length());
psdChannel[channelIdx].Append(packedLine->Buffer(),
packedLine->Length());
if (fVersion == PSD_FILE)
psdByteCounts[channelIdx].Append((uint16)packedLine->Length());
else
psdByteCounts[channelIdx].Append((uint32)packedLine->Length());
psdChannel[channelIdx].Append(*packedLine);
delete packedLine;
}
}
@ -117,7 +129,7 @@ PSDWriter::Encode(BPositionIO *target)
// PSD header
BDataArray psdHeader(64);
psdHeader << "8BPS"; // Signature
psdHeader << (uint16)1; // Version = 1
psdHeader << (uint16)fVersion; // Version
psdHeader.Repeat(0, 6); // Reserved
psdHeader << fChannels; // Channels
psdHeader << fHeight << fWidth; // Image size
@ -155,26 +167,31 @@ PSDWriter::Encode(BPositionIO *target)
psdLayersSection << (uint32)fWidth;
psdLayersSection << (uint16)fChannels;
for (int channelIdx = 0; channelIdx < 3; channelIdx++) {
psdLayersSection << (int16)channelIdx; // Channel num
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) {
psdLayersSection << (uint32)(psdChannel[channelIdx].Length()
+ sizeof(int16));
if (fVersion == PSD_FILE) {
psdLayersSection << (uint32)(psdChannel[channelIdx].Length()
+ sizeof(int16));
} else {
psdLayersSection << (uint64)(psdChannel[channelIdx].Length()
+ sizeof(int16));
}
} else {
psdLayersSection << (uint32)(psdChannel[channelIdx].Length()
+ psdByteCounts[channelIdx].Length() + sizeof(int16));
}
}
if (fChannels == 4) {
psdLayersSection << (int16)-1; // Alpha channel id (-1)
if (fCompression == PSD_COMPRESSED_RAW) {
psdLayersSection << (uint32)(psdChannel[3].Length()
+ sizeof(int16));
} else {
psdLayersSection << (uint32)(psdChannel[3].Length()
+ psdByteCounts[3].Length() + sizeof(int16));
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
@ -192,16 +209,13 @@ PSDWriter::Encode(BPositionIO *target)
if (fCompression == PSD_COMPRESSED_RAW) {
for (int channelIdx = 0; channelIdx < fChannels; channelIdx++) {
psdLayersSection << fCompression; // Compression mode
psdLayersSection.Append(psdChannel[channelIdx].Buffer(),
psdChannel[channelIdx].Length()); // Layer image data
psdLayersSection.Append(psdChannel[channelIdx]); // Channel data
}
} else {
for (int channelIdx = 0; channelIdx < fChannels; channelIdx++) {
psdLayersSection << fCompression; // Compression mode
psdLayersSection.Append(psdByteCounts[channelIdx].Buffer(),
psdByteCounts[channelIdx].Length()); // Bytes count
psdLayersSection.Append(psdChannel[channelIdx].Buffer(),
psdChannel[channelIdx].Length()); // Layer image data
psdLayersSection.Append(psdByteCounts[channelIdx]); // Bytes count
psdLayersSection.Append(psdChannel[channelIdx]); // Channel data
}
}
@ -217,8 +231,13 @@ PSDWriter::Encode(BPositionIO *target)
_WriteUInt32ToStream(target, psdImageResourceSection.Length());
psdImageResourceSection.WriteToStream(target);
_WriteUInt32ToStream(target, psdLayersSection.Length() + sizeof(int32));
_WriteUInt32ToStream(target, psdLayersSection.Length());
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
@ -281,6 +300,22 @@ PSDWriter::PackBits(uint8 *buff, int32 len)
}
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)
{

View File

@ -30,11 +30,14 @@ public:
PSDWriter(BPositionIO *stream);
~PSDWriter();
bool IsReady(void);
bool IsReady(void);
void SetCompression(int16 compression);
void SetVersion(int16 version);
status_t Encode(BPositionIO *target);
private:
void _WriteInt64ToStream(BPositionIO *stream, int64);
void _WriteUInt64ToStream(BPositionIO *stream, uint64);
void _WriteInt32ToStream(BPositionIO *stream, int32);
void _WriteUInt32ToStream(BPositionIO *stream, uint32);
void _WriteInt16ToStream(BPositionIO *stream, int16);
@ -57,6 +60,7 @@ private:
int32 fWidth;
int32 fHeight;
int16 fCompression;
int16 fVersion;
bool fReady;
};