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