xref: /haiku/src/add-ons/translators/psd/PSDLoader.cpp (revision e1c4049fed1047bdb957b0529e1921e97ef94770)
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 && 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 
107 BString
108 PSDLoader::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 
132 psd_color_format
133 PSDLoader::_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 
189 status_t
190 PSDLoader::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 
464 int64
465 PSDLoader::_GetInt64FromStream(BPositionIO *in)
466 {
467 	int64 ret;
468 	in->Read(&ret, sizeof(int64));
469 	return B_BENDIAN_TO_HOST_INT64(ret);
470 }
471 
472 
473 int32
474 PSDLoader::_GetInt32FromStream(BPositionIO *in)
475 {
476 	int32 ret;
477 	in->Read(&ret, sizeof(int32));
478 	return B_BENDIAN_TO_HOST_INT32(ret);
479 }
480 
481 
482 int16
483 PSDLoader::_GetInt16FromStream(BPositionIO *in)
484 {
485 	int16 ret;
486 	in->Read(&ret, sizeof(int16));
487 	return B_BENDIAN_TO_HOST_INT16(ret);
488 }
489 
490 
491 int8
492 PSDLoader::_GetInt8FromStream(BPositionIO *in)
493 {
494 	int8 ret;
495 	in->Read(&ret, sizeof(int8));
496 	return ret;
497 }
498 
499 
500 uint8
501 PSDLoader::_GetUInt8FromStream(BPositionIO *in)
502 {
503 	uint8 ret;
504 	in->Read(&ret, sizeof(uint8));
505 	return ret;
506 }
507 
508 
509 void
510 PSDLoader::_SkipStreamBlock(BPositionIO *in, size_t count)
511 {
512 	in->Seek(count, SEEK_CUR);
513 }
514 
515 
516 status_t
517 PSDLoader::_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