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
PSDLoader(BPositionIO * src)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
~PSDLoader()70 PSDLoader::~PSDLoader()
71 {
72 }
73
74
75 bool
IsLoaded(void)76 PSDLoader::IsLoaded(void)
77 {
78 return fLoaded;
79 }
80
81
82 bool
IsSupported(void)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
ColorFormatName(void)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
_ColorFormat(void)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
Decode(BPositionIO * target)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
_GetInt64FromStream(BPositionIO * in)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
_GetInt32FromStream(BPositionIO * in)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
_GetInt16FromStream(BPositionIO * in)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
_GetInt8FromStream(BPositionIO * in)492 PSDLoader::_GetInt8FromStream(BPositionIO *in)
493 {
494 int8 ret;
495 in->Read(&ret, sizeof(int8));
496 return ret;
497 }
498
499
500 uint8
_GetUInt8FromStream(BPositionIO * in)501 PSDLoader::_GetUInt8FromStream(BPositionIO *in)
502 {
503 uint8 ret;
504 in->Read(&ret, sizeof(uint8));
505 return ret;
506 }
507
508
509 void
_SkipStreamBlock(BPositionIO * in,size_t count)510 PSDLoader::_SkipStreamBlock(BPositionIO *in, size_t count)
511 {
512 in->Seek(count, SEEK_CUR);
513 }
514
515
516 status_t
_ParseImageResources(void)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