xref: /haiku/src/add-ons/translators/psd/PSDWriter.cpp (revision 2b01519444fb8e68325783b37e5163bbb3ba44cf)
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 
PSDWriter(BPositionIO * stream)14 PSDWriter::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 
~PSDWriter()64 PSDWriter::~PSDWriter()
65 {
66 }
67 
68 
69 bool
IsReady(void)70 PSDWriter::IsReady(void)
71 {
72 	return fReady;
73 }
74 
75 
76 void
SetCompression(int16 compression)77 PSDWriter::SetCompression(int16 compression)
78 {
79 	fCompression = compression;
80 }
81 
82 
83 void
SetVersion(int16 ver)84 PSDWriter::SetVersion(int16 ver)
85 {
86 	fVersion = ver;
87 }
88 
89 
90 status_t
Encode(BPositionIO * target)91 PSDWriter::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 
229 BDataArray*
_PackBits(uint8 * buff,int32 len)230 PSDWriter::_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 
274 status_t
_LoadChannelsFromRGBA32(void)275 PSDWriter::_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 
328 void
_WriteInt64ToStream(BPositionIO * stream,int64 val)329 PSDWriter::_WriteInt64ToStream(BPositionIO *stream, int64 val)
330 {
331 	val = B_HOST_TO_BENDIAN_INT64(val);
332 	stream->Write(&val, sizeof(int32));
333 }
334 
335 
336 void
_WriteUInt64ToStream(BPositionIO * stream,uint64 val)337 PSDWriter::_WriteUInt64ToStream(BPositionIO *stream, uint64 val)
338 {
339 	val = B_HOST_TO_BENDIAN_INT64(val);
340 	stream->Write(&val, sizeof(uint64));
341 }
342 
343 
344 void
_WriteInt32ToStream(BPositionIO * stream,int32 val)345 PSDWriter::_WriteInt32ToStream(BPositionIO *stream, int32 val)
346 {
347 	val = B_HOST_TO_BENDIAN_INT32(val);
348 	stream->Write(&val, sizeof(int32));
349 }
350 
351 
352 void
_WriteUInt32ToStream(BPositionIO * stream,uint32 val)353 PSDWriter::_WriteUInt32ToStream(BPositionIO *stream, uint32 val)
354 {
355 	val = B_HOST_TO_BENDIAN_INT32(val);
356 	stream->Write(&val, sizeof(uint32));
357 }
358 
359 
360 void
_WriteInt16ToStream(BPositionIO * stream,int16 val)361 PSDWriter::_WriteInt16ToStream(BPositionIO *stream, int16 val)
362 {
363 	val = B_HOST_TO_BENDIAN_INT16(val);
364 	stream->Write(&val, sizeof(int16));
365 }
366 
367 
368 void
_WriteUInt16ToStream(BPositionIO * stream,uint16 val)369 PSDWriter::_WriteUInt16ToStream(BPositionIO *stream, uint16 val)
370 {
371 	val = B_HOST_TO_BENDIAN_INT16(val);
372 	stream->Write(&val, sizeof(int16));
373 }
374 
375 
376 void
_WriteInt8ToStream(BPositionIO * stream,int8 val)377 PSDWriter::_WriteInt8ToStream(BPositionIO *stream, int8 val)
378 {
379 	stream->Write(&val, sizeof(int8));
380 }
381 
382 
383 void
_WriteUInt8ToStream(BPositionIO * stream,uint8 val)384 PSDWriter::_WriteUInt8ToStream(BPositionIO *stream, uint8 val)
385 {
386 	stream->Write(&val, sizeof(uint8));
387 }
388 
389 
390 void
_WriteFillBlockToStream(BPositionIO * stream,uint8 val,size_t count)391 PSDWriter::_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 
399 void
_WriteBlockToStream(BPositionIO * stream,uint8 * val,size_t count)400 PSDWriter::_WriteBlockToStream(BPositionIO *stream,
401 	uint8 *val, size_t count)
402 {
403 	stream->Write(val, count);
404 }
405