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