1/*
2 * Copyright 2013, Gerasim Troeglazov, 3dEyes@gmail.com. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include <File.h>
8
9#include "BaseTranslator.h"
10#include "PSDWriter.h"
11#include "DataArray.h"
12
13
14PSDWriter::PSDWriter(BPositionIO *stream)
15{
16	fAlphaChannel = -1;
17	fStream = stream;
18	fReady = false;
19
20	TranslatorBitmap header;
21	stream->Seek(0, SEEK_SET);
22	status_t err = stream->Read(&header, sizeof(TranslatorBitmap));
23	if (err < B_OK)
24		return;
25	else if (err < (int)sizeof(TranslatorBitmap))
26		return;
27
28	fBitmapDataPos = stream->Position();
29
30	BRect bounds;
31	bounds.left = B_BENDIAN_TO_HOST_FLOAT(header.bounds.left);
32	bounds.top = B_BENDIAN_TO_HOST_FLOAT(header.bounds.top);
33	bounds.right = B_BENDIAN_TO_HOST_FLOAT(header.bounds.right);
34	bounds.bottom = B_BENDIAN_TO_HOST_FLOAT(header.bounds.bottom);
35	fInRowBytes = B_BENDIAN_TO_HOST_INT32(header.rowBytes);
36	fColorSpace = (color_space)B_BENDIAN_TO_HOST_INT32(header.colors);
37
38	switch (fColorSpace) {
39		case B_GRAY8:
40		case B_CMAP8:
41			fChannels = 1;
42			break;
43		case B_RGBA32:
44			fChannels = 4;
45			fAlphaChannel = 3;
46			break;
47		case B_RGB32:
48			fChannels = 3;
49			break;
50		default:
51			return;
52	};
53
54	fWidth = bounds.IntegerWidth() + 1;
55	fHeight = bounds.IntegerHeight() + 1;
56
57	fVersion = PSD_FILE;
58	fCompression = PSD_COMPRESSED_RAW;
59
60	fReady = true;
61}
62
63
64PSDWriter::~PSDWriter()
65{
66}
67
68
69bool
70PSDWriter::IsReady(void)
71{
72	return fReady;
73}
74
75
76void
77PSDWriter::SetCompression(int16 compression)
78{
79	fCompression = compression;
80}
81
82
83void
84PSDWriter::SetVersion(int16 ver)
85{
86	fVersion = ver;
87}
88
89
90status_t
91PSDWriter::Encode(BPositionIO *target)
92{
93	if (!fReady)
94		return B_NO_TRANSLATOR;
95
96	status_t status = _LoadChannelsFromRGBA32();
97	if (status != B_OK)
98		return status;
99
100	// PSD header
101	BDataArray psdHeader(64);
102	psdHeader << "8BPS"; // Signature
103	psdHeader << (uint16)fVersion; // Version
104	psdHeader.Repeat(0, 6); // Reserved
105	psdHeader << fChannels; // Channels
106	psdHeader << fHeight << fWidth; // Image size
107	psdHeader << (int16)8; // Depth
108	psdHeader << (int16)PSD_COLOR_MODE_RGB; // ColorMode
109
110	// Color mode section
111	BDataArray psdColorModeSection(16);
112	psdColorModeSection << (uint32)0;
113
114	// Image resource section
115	BDataArray psdImageResourceSection(64);
116	psdImageResourceSection << "8BIM"; // Block signature
117	psdImageResourceSection << (uint16)1005;
118	psdImageResourceSection << (uint16)0;
119	psdImageResourceSection << (uint32)16;
120	uint8 resBlock[16] = {0x00, 0x48, 0x00, 0x00,
121		0x00, 0x01, 0x00, 0x01,
122		0x00, 0x48, 0x00, 0x00,
123		0x00, 0x01, 0x00, 0x01};
124	psdImageResourceSection.Append(resBlock, 16);
125	// Current layer info
126	psdImageResourceSection << "8BIM"; // Block signature
127	psdImageResourceSection << (uint16)1024;
128	psdImageResourceSection << (uint16)0;
129	psdImageResourceSection << (uint32)2;
130	psdImageResourceSection << (uint16)0; // Set current layer to 0
131
132	// Layer & mask section
133	BDataArray psdLayersSection;
134	psdLayersSection << (uint16)1; // Layers count
135	psdLayersSection << (uint32)0; // Layer rect
136	psdLayersSection << (uint32)0;
137	psdLayersSection << (uint32)fHeight;
138	psdLayersSection << (uint32)fWidth;
139	psdLayersSection << (uint16)fChannels;
140
141	for (int channelIdx = 0; channelIdx < fChannels; channelIdx++) {
142		if (channelIdx == 3)
143			psdLayersSection << (int16)-1; // Alpha channel id (-1)
144		else
145			psdLayersSection << (int16)channelIdx; // Channel num
146
147		if (fCompression == PSD_COMPRESSED_RAW) {
148			if (fVersion == PSD_FILE) {
149					psdLayersSection << (uint32)(psdChannel[channelIdx].Length()
150						+ sizeof(int16));
151			} else {
152					psdLayersSection << (uint64)(psdChannel[channelIdx].Length()
153						+ sizeof(int16));
154			}
155		} else {
156			if (fVersion == PSD_FILE) {
157				psdLayersSection << (uint32)(psdChannel[channelIdx].Length()
158					+ psdByteCounts[channelIdx].Length() + sizeof(int16));
159			} else {
160				psdLayersSection << (uint64)(psdChannel[channelIdx].Length()
161					+ psdByteCounts[channelIdx].Length() + sizeof(int16));
162			}
163		}
164	}
165
166	psdLayersSection << "8BIM";
167	psdLayersSection << "norm"; // Blend mode = norm
168	psdLayersSection << (uint8)255; // Opacity
169	psdLayersSection << (uint8)0; // Clipping
170	psdLayersSection << (uint8)1; // Flags
171	psdLayersSection << (uint8)0; // Flags
172	psdLayersSection << (uint32)24; // Extra data length
173	psdLayersSection << (uint32)0; // Mask info
174	psdLayersSection << (uint32)0;
175
176	psdLayersSection << (uint8)15; // Layer name length
177	uint8 layerName[16] = {"Layer #1       "};
178	psdLayersSection.Append(layerName, 15); // Layer name
179
180	if (fCompression == PSD_COMPRESSED_RAW) {
181		for (int channelIdx = 0; channelIdx < fChannels; channelIdx++) {
182			psdLayersSection << fCompression; // Compression mode
183			psdLayersSection.Append(psdChannel[channelIdx]); // Channel data
184		}
185	} else {
186		for (int channelIdx = 0; channelIdx < fChannels; channelIdx++) {
187			psdLayersSection << fCompression; // Compression mode
188			psdLayersSection.Append(psdByteCounts[channelIdx]); // Bytes count
189			psdLayersSection.Append(psdChannel[channelIdx]); // Channel data
190		}
191	}
192
193	if (fCompression == PSD_COMPRESSED_RLE
194		&& psdLayersSection.Length() % 2 != 0) {
195		psdLayersSection << (uint8)0;
196	}
197
198	psdHeader.WriteToStream(target);
199
200	psdColorModeSection.WriteToStream(target);
201
202	_WriteUInt32ToStream(target, psdImageResourceSection.Length());
203	psdImageResourceSection.WriteToStream(target);
204
205	if (fVersion == PSD_FILE) {
206		_WriteUInt32ToStream(target, psdLayersSection.Length() + sizeof(int32));
207		_WriteUInt32ToStream(target, psdLayersSection.Length());
208	} else {
209		_WriteUInt64ToStream(target, psdLayersSection.Length() + sizeof(int64));
210		_WriteUInt64ToStream(target, psdLayersSection.Length());
211	}
212	psdLayersSection.WriteToStream(target);
213
214	// Merged layer
215	_WriteUInt16ToStream(target, fCompression); // Compression mode
216
217	if (fCompression == PSD_COMPRESSED_RLE) {
218		for (int channelIdx = 0; channelIdx < fChannels; channelIdx++)
219			psdByteCounts[channelIdx].WriteToStream(target);
220	}
221
222	for (int channelIdx = 0; channelIdx < fChannels; channelIdx++)
223		psdChannel[channelIdx].WriteToStream(target);
224
225	return B_OK;
226}
227
228
229BDataArray*
230PSDWriter::_PackBits(uint8 *buff, int32  len)
231{
232	BDataArray *packedBits = new BDataArray();
233
234	int32  count = len;
235	len = 0;
236
237	while (count > 0) {
238		int i;
239		for (i = 0; (i < 128) && (buff[0] == buff[i]) && (count - i > 0); i++);
240		if (i < 2) {
241			for (i = 0; i < 128; i++) {
242				bool b1 = buff[i] != buff[i + 1];
243				bool b3 = buff[i] != buff[i + 2];
244				bool b2 = count - (i + 2) < 1;
245				if (count - (i + 1) <= 0)
246					break;
247				if (!(b1 || b2 || b3))
248					break;
249			}
250
251			if (count == 1)
252				i = 1;
253
254			if (i > 0) {
255				packedBits->Append((uint8)(i - 1));
256				for (int j = 0; j < i; j++)
257					packedBits->Append((uint8)buff[j]);
258				buff += i;
259				count -= i;
260				len += (i + 1);
261			}
262		} else {
263			packedBits->Append((uint8)(-(i - 1)));
264			packedBits->Append((uint8)(*buff));
265			buff += i;
266			count -= i;
267			len += 2;
268		}
269	}
270	return packedBits;
271}
272
273
274status_t
275PSDWriter::_LoadChannelsFromRGBA32(void)
276{
277	if (fColorSpace != B_RGB32 && fColorSpace != B_RGBA32)
278		return B_NO_TRANSLATOR;
279
280	int32 channelSize = fWidth * fHeight;
281
282	fStream->Seek(fBitmapDataPos, SEEK_SET);
283
284	if (fCompression == PSD_COMPRESSED_RAW) {
285		for (int i = 0; i < channelSize; i++) {
286			uint8 rgba[4];
287			fStream->Read(rgba, sizeof(uint32));
288			psdChannel[0].Append((uint8)rgba[2]); // Red channel
289			psdChannel[1].Append((uint8)rgba[1]); // Green channel
290			psdChannel[2].Append((uint8)rgba[0]); // Blue channel
291			if (fChannels == 4)
292				psdChannel[3].Append((uint8)rgba[3]); // Alpha channel
293		}
294		return B_OK;
295	} else if (fCompression == PSD_COMPRESSED_RLE) {
296		for (int32 h = 0; h < fHeight; h++) {
297			BDataArray lineData[4];
298
299			for (int32 w = 0; w < fWidth; w++) {
300				uint8 rgba[4];
301				fStream->Read(rgba, sizeof(uint32));
302				lineData[0].Append((uint8)rgba[2]); // Red channel
303				lineData[1].Append((uint8)rgba[1]); // Green channel
304				lineData[2].Append((uint8)rgba[0]); // Blue channel
305				if (fChannels == 4)
306					lineData[3].Append((uint8)rgba[3]); // Alpha channel
307			}
308
309			for (int channelIdx = 0; channelIdx < fChannels; channelIdx++) {
310				BDataArray *packedLine = _PackBits(lineData[channelIdx].Buffer(),
311					lineData[channelIdx].Length());
312
313				if (fVersion == PSD_FILE)
314					psdByteCounts[channelIdx].Append((uint16)packedLine->Length());
315				else
316					psdByteCounts[channelIdx].Append((uint32)packedLine->Length());
317
318				psdChannel[channelIdx].Append(*packedLine);
319				delete packedLine;
320			}
321		}
322		return B_OK;
323	}
324	return B_NO_TRANSLATOR;
325}
326
327
328void
329PSDWriter::_WriteInt64ToStream(BPositionIO *stream, int64 val)
330{
331	val = B_HOST_TO_BENDIAN_INT64(val);
332	stream->Write(&val, sizeof(int32));
333}
334
335
336void
337PSDWriter::_WriteUInt64ToStream(BPositionIO *stream, uint64 val)
338{
339	val = B_HOST_TO_BENDIAN_INT64(val);
340	stream->Write(&val, sizeof(uint64));
341}
342
343
344void
345PSDWriter::_WriteInt32ToStream(BPositionIO *stream, int32 val)
346{
347	val = B_HOST_TO_BENDIAN_INT32(val);
348	stream->Write(&val, sizeof(int32));
349}
350
351
352void
353PSDWriter::_WriteUInt32ToStream(BPositionIO *stream, uint32 val)
354{
355	val = B_HOST_TO_BENDIAN_INT32(val);
356	stream->Write(&val, sizeof(uint32));
357}
358
359
360void
361PSDWriter::_WriteInt16ToStream(BPositionIO *stream, int16 val)
362{
363	val = B_HOST_TO_BENDIAN_INT16(val);
364	stream->Write(&val, sizeof(int16));
365}
366
367
368void
369PSDWriter::_WriteUInt16ToStream(BPositionIO *stream, uint16 val)
370{
371	val = B_HOST_TO_BENDIAN_INT16(val);
372	stream->Write(&val, sizeof(int16));
373}
374
375
376void
377PSDWriter::_WriteInt8ToStream(BPositionIO *stream, int8 val)
378{
379	stream->Write(&val, sizeof(int8));
380}
381
382
383void
384PSDWriter::_WriteUInt8ToStream(BPositionIO *stream, uint8 val)
385{
386	stream->Write(&val, sizeof(uint8));
387}
388
389
390void
391PSDWriter::_WriteFillBlockToStream(BPositionIO *stream,
392	uint8 val, size_t count)
393{
394	for (size_t i = 0; i < count; i++)
395		stream->Write(&val, sizeof(uint8));
396}
397
398
399void
400PSDWriter::_WriteBlockToStream(BPositionIO *stream,
401	uint8 *val, size_t count)
402{
403	stream->Write(val, count);
404}
405