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 "PSDLoader.h"
8
9#include <Catalog.h>
10
11#include "BaseTranslator.h"
12
13#undef B_TRANSLATION_CONTEXT
14#define B_TRANSLATION_CONTEXT "PSDLoader"
15
16
17PSDLoader::PSDLoader(BPositionIO *src)
18{
19	fLoaded = false;
20	fStream = src;
21
22	fStream->Seek(0, SEEK_END);
23	fStreamSize = fStream->Position();
24	fStream->Seek(0, SEEK_SET);
25
26	if (fStreamSize <= 0)
27		return;
28
29	fStream->Seek(0, SEEK_SET);
30
31	fSignature = _GetInt32FromStream(fStream);
32	if (fSignature != 0x38425053) // 8BPS
33		return;
34
35	fVersion = _GetInt16FromStream(fStream);
36
37	// Skip reserved data
38	_SkipStreamBlock(fStream, 6);
39
40	fChannels = _GetInt16FromStream(fStream);
41	fHeight = _GetInt32FromStream(fStream);
42	fWidth = _GetInt32FromStream(fStream);
43	fDepth = _GetInt16FromStream(fStream);
44	fColorFormat = _GetInt16FromStream(fStream);
45
46	fColorModeDataSize = _GetInt32FromStream(fStream);
47	fColorModeDataPos = fStream->Position();
48	_SkipStreamBlock(fStream, fColorModeDataSize);
49
50	fImageResourceSectionSize = _GetInt32FromStream(fStream);
51	fImageResourceSectionPos = fStream->Position();
52	_SkipStreamBlock(fStream, fImageResourceSectionSize);
53
54	// Skip [layer and mask] block
55	if (fVersion == PSD_FILE)
56		_SkipStreamBlock(fStream, _GetInt32FromStream(fStream));
57	else if (fVersion == PSB_FILE)
58		_SkipStreamBlock(fStream, _GetInt64FromStream(fStream));
59	else
60		return;
61
62	fCompression = _GetInt16FromStream(fStream);
63
64	fStreamPos = fStream->Position();
65
66	fLoaded = true;
67}
68
69
70PSDLoader::~PSDLoader()
71{
72}
73
74
75bool
76PSDLoader::IsLoaded(void)
77{
78	return fLoaded;
79}
80
81
82bool
83PSDLoader::IsSupported(void)
84{
85	if (!fLoaded)
86		return false;
87
88	if (fVersion != PSD_FILE && fVersion != PSB_FILE)
89		return false;
90
91	if (fChannels < 0 || fChannels > PSD_MAX_CHANNELS)
92		return false;
93
94	if (fDepth > 16)
95		return false;
96
97	if (_ColorFormat() == PSD_COLOR_FORMAT_UNSUPPORTED)
98		return false;
99
100	if (fCompression != PSD_COMPRESSED_RAW && fCompression != PSD_COMPRESSED_RLE)
101		return false;
102
103	return true;
104}
105
106
107BString
108PSDLoader::ColorFormatName(void)
109{
110	switch (fColorFormat) {
111		case PSD_COLOR_MODE_BITS:
112			return B_TRANSLATE("Bitmap");
113		case PSD_COLOR_MODE_GRAYSCALE:
114			return B_TRANSLATE("Grayscale");
115		case PSD_COLOR_MODE_INDEXED:
116			return B_TRANSLATE("Indexed");
117		case PSD_COLOR_MODE_RGB:
118			return fChannels > 3 ? B_TRANSLATE("RGBA") : B_TRANSLATE("RGB");
119		case PSD_COLOR_MODE_CMYK:
120			return B_TRANSLATE("CMYK");
121		case PSD_COLOR_MODE_MULTICHANNEL:
122			return B_TRANSLATE("Multichannel");
123		case PSD_COLOR_MODE_LAB:
124			return B_TRANSLATE("Lab");
125		case PSD_COLOR_MODE_DUOTONE:
126			return B_TRANSLATE("Duotone");
127	}
128	return "";
129}
130
131
132psd_color_format
133PSDLoader::_ColorFormat(void)
134{
135	psd_color_format format = PSD_COLOR_FORMAT_UNSUPPORTED;
136	if (!fLoaded)
137		return format;
138
139	switch (fColorFormat) {
140		case PSD_COLOR_MODE_BITS:
141			format = PSD_COLOR_FORMAT_BITMAP;
142			break;
143		case PSD_COLOR_MODE_RGB:
144			if (fChannels == 3)
145				format = PSD_COLOR_FORMAT_RGB;
146			else if (fChannels >= 4)
147				format = PSD_COLOR_FORMAT_RGB_A;
148			break;
149		case PSD_COLOR_MODE_GRAYSCALE:
150			if (fChannels == 1)
151				format = PSD_COLOR_FORMAT_GRAY;
152			else if (fChannels == 2)
153				format = PSD_COLOR_FORMAT_GRAY_A;
154			break;
155		case PSD_COLOR_MODE_MULTICHANNEL:
156			if (fChannels == 3)
157				format = PSD_COLOR_FORMAT_MULTICHANNEL;
158			break;
159		case PSD_COLOR_MODE_CMYK:
160			if (fChannels == 3)
161				format = PSD_COLOR_FORMAT_MULTICHANNEL;
162			else if (fChannels == 4)
163				format = PSD_COLOR_FORMAT_CMYK;
164			else if (fChannels > 4)
165				format = PSD_COLOR_FORMAT_CMYK_A;
166			break;
167		case PSD_COLOR_MODE_LAB:
168			if (fChannels == 3)
169				format = PSD_COLOR_FORMAT_LAB;
170			else if (fChannels > 3)
171				format = PSD_COLOR_FORMAT_LAB_A;
172			break;
173		case PSD_COLOR_MODE_DUOTONE:
174			if (fChannels >= 1)
175				format = PSD_COLOR_FORMAT_DUOTONE;
176			break;
177		case PSD_COLOR_MODE_INDEXED:
178			if (fChannels >= 1 && fColorModeDataSize >= 3)
179				format = PSD_COLOR_FORMAT_INDEXED;
180			break;
181		default:
182			break;
183	};
184
185	return format;
186}
187
188
189status_t
190PSDLoader::Decode(BPositionIO *target)
191{
192	if (!IsSupported())
193		return B_NO_TRANSLATOR;
194
195	fStreamBuffer = new uint8[fStreamSize];
196	fStream->Seek(0, SEEK_SET);
197	fStream->Read(fStreamBuffer, fStreamSize);
198
199	int32 depthBytes = fDepth / 8;
200	int32 rowBytes = (fWidth * fDepth) / 8;
201	int32 channelBytes = rowBytes * fHeight;
202
203	uint8 *imageData[PSD_MAX_CHANNELS];
204	for (int i = 0; i < fChannels; i++)
205		imageData[i] = new uint8[channelBytes];
206
207	switch (fCompression) {
208		case PSD_COMPRESSED_RAW:
209		{
210			for (int channelIdx = 0; channelIdx < fChannels; channelIdx++) {
211				uint8 *ptr = imageData[channelIdx];
212				for (int i = 0; i < channelBytes; i++, ptr++)
213					*ptr = (uint8)fStreamBuffer[fStreamPos++];
214			}
215			break;
216		}
217		case PSD_COMPRESSED_RLE:
218		{
219			if (fVersion == PSD_FILE)
220				fStreamPos += fHeight * fChannels * 2;
221			else if (fVersion == PSB_FILE)
222				fStreamPos += fHeight * fChannels * 4;
223
224			for (int channelIdx = 0; channelIdx < fChannels; channelIdx++) {
225				uint8 *ptr = imageData[channelIdx];
226				// Read the RLE data.
227				int count = 0;
228				while (count < channelBytes) {
229					uint8 len = (uint8)fStreamBuffer[fStreamPos++];
230					if (len == 128) {
231						continue;
232					} else if (len < 128) {
233						len++;
234						count += len;
235						while (len) {
236							*ptr++ = (int8)fStreamBuffer[fStreamPos++];
237							len--;
238						}
239					} else if (len > 128) {
240						int8 val = (int8)fStreamBuffer[fStreamPos++];
241						len ^= 255;
242						len += 2;
243						count += len;
244						while (len) {
245							*ptr++ = val;
246							len--;
247						}
248					}
249				}
250			}
251			break;
252		}
253		default:
254			delete[] fStreamBuffer;
255			for (int i = 0; i < fChannels; i++)
256				delete[] imageData[i];
257			return 	B_NO_TRANSLATOR;
258	}
259
260	delete[] fStreamBuffer;
261
262	TranslatorBitmap bitsHeader;
263	bitsHeader.magic = B_TRANSLATOR_BITMAP;
264	bitsHeader.bounds.left = 0;
265	bitsHeader.bounds.top = 0;
266	bitsHeader.bounds.right = fWidth - 1;
267	bitsHeader.bounds.bottom = fHeight - 1;
268
269	psd_color_format colorFormat = _ColorFormat();
270
271	if (colorFormat == PSD_COLOR_FORMAT_BITMAP) {
272		bitsHeader.rowBytes = rowBytes;
273		bitsHeader.dataSize = channelBytes;
274		bitsHeader.colors = B_GRAY1;
275	} else {
276		bitsHeader.rowBytes = sizeof(uint32) * fWidth;
277		bitsHeader.colors = B_RGBA32;
278		bitsHeader.dataSize = bitsHeader.rowBytes * fHeight;
279	}
280
281	if (swap_data(B_UINT32_TYPE, &bitsHeader,
282		sizeof(TranslatorBitmap), B_SWAP_HOST_TO_BENDIAN) != B_OK) {
283		return B_NO_TRANSLATOR;
284	}
285
286	target->Write(&bitsHeader, sizeof(TranslatorBitmap));
287
288	uint8 *lineData = new uint8[fWidth * sizeof(uint32)];
289
290	switch (colorFormat) {
291		case PSD_COLOR_FORMAT_BITMAP:
292		{
293			int32 rowBytes = (fWidth / 8 ) * fHeight;
294			for (int32 i = 0; i < rowBytes; i++)
295				imageData[0][i]^=255;
296			target->Write(imageData[0], rowBytes);
297			break;
298		}
299		case PSD_COLOR_FORMAT_INDEXED:
300		{
301			int32 paletteSize = fColorModeDataSize / 3;
302
303			uint8 *colorData = new uint8[fColorModeDataSize];
304			fStream->Seek(fColorModeDataPos, SEEK_SET);
305			fStream->Read(colorData, fColorModeDataSize);
306
307			if (_ParseImageResources() != B_OK)
308				fTransparentIndex = 256;
309
310			uint8 *redPalette = colorData;
311			uint8 *greenPalette = colorData + paletteSize;
312			uint8 *bluePalette = colorData + paletteSize * 2;
313			int32 index = 0;
314			for (int h = 0; h < fHeight; h++) {
315				uint8 *ptr = lineData;
316				for (int w = 0; w < fWidth; w++) {
317					uint8 colorIndex = imageData[0][index];
318					ptr[0] = bluePalette[colorIndex];
319					ptr[1] = greenPalette[colorIndex];
320					ptr[2] = redPalette[colorIndex];
321					ptr[3] = colorIndex == fTransparentIndex ? 0 : 255;
322
323					ptr += sizeof(uint32);
324					index++;
325				}
326				target->Write(lineData, fWidth * sizeof(uint32));
327			}
328			delete[] colorData;
329			break;
330		}
331		case PSD_COLOR_FORMAT_DUOTONE:
332		case PSD_COLOR_FORMAT_GRAY:
333		case PSD_COLOR_FORMAT_GRAY_A:
334		{
335			bool isAlpha = colorFormat == PSD_COLOR_FORMAT_GRAY_A;
336			int32 index = 0;
337			for (int h = 0; h < fHeight; h++) {
338				uint8 *ptr = lineData;
339				for (int w = 0; w < fWidth; w++) {
340					ptr[0] = imageData[0][index];
341					ptr[1] = imageData[0][index];
342					ptr[2] = imageData[0][index];
343					ptr[3] = isAlpha ? imageData[1][index] : 255;
344
345					ptr += sizeof(uint32);
346					index += depthBytes;
347				}
348				target->Write(lineData, fWidth * sizeof(uint32));
349			}
350			break;
351		}
352		case PSD_COLOR_FORMAT_MULTICHANNEL:
353		case PSD_COLOR_FORMAT_RGB:
354		case PSD_COLOR_FORMAT_RGB_A:
355		{
356			bool isAlpha = colorFormat == PSD_COLOR_FORMAT_RGB_A;
357			int32 index = 0;
358			for (int h = 0; h < fHeight; h++) {
359				uint8 *ptr = lineData;
360				for (int w = 0; w < fWidth; w++) {
361					ptr[0] = imageData[2][index];
362					ptr[1] = imageData[1][index];
363					ptr[2] = imageData[0][index];
364					ptr[3] = isAlpha ? imageData[3][index] : 255;
365
366					ptr += sizeof(uint32);
367					index += depthBytes;
368				}
369				target->Write(lineData, fWidth * sizeof(uint32));
370			}
371			break;
372		}
373		case PSD_COLOR_FORMAT_CMYK:
374		case PSD_COLOR_FORMAT_CMYK_A:
375		{
376			bool isAlpha = colorFormat == PSD_COLOR_FORMAT_CMYK_A;
377			int32 index = 0;
378			for (int h = 0; h < fHeight; h++) {
379				uint8 *ptr = lineData;
380				for (int w = 0; w < fWidth; w++) {
381					double c = 1.0 - imageData[0][index] / 255.0;
382					double m = 1.0 - imageData[1][index] / 255.0;
383					double y = 1.0 - imageData[2][index] / 255.0;
384					double k = 1.0 - imageData[3][index] / 255.0;
385					ptr[0] = (uint8)((1.0 - (y * (1.0 - k) + k)) * 255.0);
386					ptr[1] = (uint8)((1.0 - (m * (1.0 - k) + k)) * 255.0);
387					ptr[2] = (uint8)((1.0 - (c * (1.0 - k) + k)) * 255.0);
388					ptr[3] = isAlpha ?  imageData[4][index] : 255;
389
390					ptr += sizeof(uint32);
391					index += depthBytes;
392				}
393				target->Write(lineData, fWidth * sizeof(uint32));
394			}
395			break;
396		}
397		case PSD_COLOR_FORMAT_LAB:
398		case PSD_COLOR_FORMAT_LAB_A:
399		{
400			bool isAlpha = colorFormat == PSD_COLOR_FORMAT_LAB_A;
401			int32 index = 0;
402			for (int h = 0; h < fHeight; h++) {
403				uint8 *ptr = lineData;
404				for (int w = 0; w < fWidth; w++) {
405					double L = imageData[0][index] / 255.0 * 100.0;
406					double a = imageData[1][index] - 128.0;
407					double b = imageData[2][index] - 128.0;
408
409					double Y = L * (1.0 / 116.0) + 16.0 / 116.0;
410					double X = a * (1.0 / 500.0) + Y;
411					double Z = b * (-1.0 / 200.0) + Y;
412
413					X = X > 6.0 / 29.0 ? X * X * X : X * (108.0 / 841.0)
414						- (432.0 / 24389.0);
415					Y = L > 8.0 ? Y * Y * Y : L * (27.0 / 24389.0);
416					Z = Z > 6.0 / 29.0 ? Z * Z * Z : Z * (108.0 / 841.0)
417						- (432.0 / 24389.0);
418
419					double R = X * (1219569.0 / 395920.0)
420						+ Y * (-608687.0 / 395920.0)
421						+ Z * (-107481.0 / 197960.0);
422					double G = X * (-80960619.0 / 87888100.0)
423						+ Y * (82435961.0 / 43944050.0)
424						+ Z * (3976797.0 / 87888100.0);
425					double B = X * (93813.0 / 1774030.0)
426						+ Y * (-180961.0 / 887015.0)
427						+ Z * (107481.0 / 93370.0);
428
429					R = R > 0.0031308 ?	pow(R, 1.0 / 2.4) * 1.055 - 0.055
430						: R * 12.92;
431					G = G > 0.0031308 ?	pow(G, 1.0 / 2.4) * 1.055 - 0.055
432						: G * 12.92;
433					B = B > 0.0031308 ?	pow(B, 1.0 / 2.4) * 1.055 - 0.055
434						: B * 12.92;
435
436					R = (R < 0) ? 0 : ((R > 1) ? 1 : R);
437					G = (G < 0) ? 0 : ((G > 1) ? 1 : G);
438					B = (B < 0) ? 0 : ((B > 1) ? 1 : B);
439
440					ptr[0] = (uint8)(B * 255.0);
441					ptr[1] = (uint8)(G * 255.0);
442					ptr[2] = (uint8)(R * 255.0);
443					ptr[3] = isAlpha ? imageData[3][index] : 255;
444
445					ptr += sizeof(uint32);
446					index += depthBytes;
447				}
448				target->Write(lineData, fWidth * sizeof(uint32));
449			}
450			break;
451		}
452		default:
453			break;
454	};
455
456	delete[] lineData;
457	for (int i = 0; i < fChannels; i++)
458		delete[] imageData[i];
459
460	return B_OK;
461}
462
463
464int64
465PSDLoader::_GetInt64FromStream(BPositionIO *in)
466{
467	int64 ret;
468	in->Read(&ret, sizeof(int64));
469	return B_BENDIAN_TO_HOST_INT64(ret);
470}
471
472
473int32
474PSDLoader::_GetInt32FromStream(BPositionIO *in)
475{
476	int32 ret;
477	in->Read(&ret, sizeof(int32));
478	return B_BENDIAN_TO_HOST_INT32(ret);
479}
480
481
482int16
483PSDLoader::_GetInt16FromStream(BPositionIO *in)
484{
485	int16 ret;
486	in->Read(&ret, sizeof(int16));
487	return B_BENDIAN_TO_HOST_INT16(ret);
488}
489
490
491int8
492PSDLoader::_GetInt8FromStream(BPositionIO *in)
493{
494	int8 ret;
495	in->Read(&ret, sizeof(int8));
496	return ret;
497}
498
499
500uint8
501PSDLoader::_GetUInt8FromStream(BPositionIO *in)
502{
503	uint8 ret;
504	in->Read(&ret, sizeof(uint8));
505	return ret;
506}
507
508
509void
510PSDLoader::_SkipStreamBlock(BPositionIO *in, size_t count)
511{
512	in->Seek(count, SEEK_CUR);
513}
514
515
516status_t
517PSDLoader::_ParseImageResources(void)
518{
519	if (!fLoaded && fImageResourceSectionSize == 0)
520		return B_ERROR;
521
522	off_t currentPos = fStream->Position();
523	fStream->Seek(fImageResourceSectionPos, SEEK_SET);
524
525	while (fStream->Position() < currentPos + fImageResourceSectionSize) {
526		int32 resBlockSignature = _GetInt32FromStream(fStream);
527		if (resBlockSignature != 0x3842494D) // 8BIM
528			return B_ERROR;
529
530		uint16 resID = _GetInt16FromStream(fStream);
531
532		BString resName, name;
533		int nameLength = 0;
534		while (true) {
535			int charData = _GetUInt8FromStream(fStream);
536			nameLength++;
537			if (charData == 0) {
538				if (nameLength % 2 == 1) {
539					_GetUInt8FromStream(fStream);
540					nameLength++;
541				}
542				break;
543			} else
544				name += charData;
545			resName = name;
546		}
547
548		uint32 resSize = _GetInt32FromStream(fStream);
549
550		if (resSize % 2 == 1)
551			resSize++;
552
553		switch (resID) {
554			case 0x0417:
555				fTransparentIndex = _GetInt16FromStream(fStream);
556				break;
557			default:
558				_SkipStreamBlock(fStream, resSize);
559		}
560	}
561
562	fStream->Seek(currentPos, SEEK_SET);
563
564	return B_OK;
565}
566