1 /** 2 This PSD file parser 3 4 This is more or less based on psd_sdk 5 6 Various features of PSD_SDK are left out for simplicity 7 */ 8 module psd.parser; 9 import utils.io; 10 import utils; 11 import psd; 12 import std.exception; 13 import std.format; 14 import std..string; 15 import psd.rle; 16 17 /** 18 Parses document 19 */ 20 PSD parseDocument(string fileName) { 21 auto file = File(fileName, "r"); 22 return parseDocument(file); 23 } 24 25 /** 26 Parses a Photoshop Document 27 */ 28 PSD parseDocument(ref File file) { 29 PSD psd; 30 file.seek(0); // Seek back to start of file, just in case. 31 32 // First, parse the header section. 33 parseHeader(file, psd); 34 35 // Parse the various sections 36 parseColorModeSection(file, psd); 37 //parseImageResourceSection(file, psd); 38 psd.imageResourceSectionOffset = file.tell(); 39 psd.imageResourceSectionLength = file.readValue!uint; 40 file.skip(psd.imageResourceSectionLength); 41 42 parseLayerMaskInfoSection(file, psd); 43 parseImageDataSectionOffset(file, psd); 44 45 return psd; 46 } 47 48 49 50 package(psd): 51 52 /** 53 Finds the index of the channel 54 */ 55 uint findChannel(Layer* layer, ChannelType channelType) 56 { 57 for (uint i = 0; i < layer.channels.length; ++i) 58 { 59 const ChannelInfo* channel = &layer.channels[i]; 60 if ((channel.dataLength > 0) && channel.type == channelType) 61 return i; 62 } 63 64 return ChannelType.INVALID; 65 } 66 67 68 void extractLayer(ref Layer layer) { 69 auto file = layer.filePtr; 70 71 // Skip empty layers 72 if (layer.width == 0 && layer.height == 0) return; 73 74 const size_t channelCount = layer.channels.length; 75 //layer.data = new ubyte[layer.width*layer.height*channelCount]; 76 77 foreach(i; 0..channelCount) { 78 ChannelInfo* channel = &layer.channels[i]; 79 file.seek(channel.fileOffset); 80 81 // HACK: To allow transparency to be put as RGBA 82 // an offset is applied based on its layer type. 83 size_t offset = i; 84 if (channelCount > 3) { 85 switch(channel.type) { 86 case -1: 87 offset = 3; 88 break; 89 default: 90 offset = channel.type; 91 break; 92 } 93 } 94 95 const ushort compressionType = file.readValue!ushort; 96 switch(compressionType) { 97 //RAW 98 case 0: 99 // TODO: RAW decode 100 enforce(false, "RAW encoding not implemented yet."); 101 break; 102 103 // RLE 104 case 1: 105 106 // RLE compressed data is preceded by a 2-byte data count for each scanline 107 uint rleDataSize; 108 foreach(_; 0..layer.height) { 109 const ushort dataCount = file.readValue!ushort; 110 rleDataSize += dataCount; 111 } 112 113 if (rleDataSize > 0) { 114 115 // Read planar data 116 ubyte[] rleData = new ubyte[rleDataSize]; 117 118 // We need to work around the same D bug as before. 119 size_t readBegin = file.tell(); 120 file.rawRead(rleData); 121 file.seek(readBegin+rleDataSize); 122 123 // Decompress RLE 124 // FIXME: We're assuming psd.channelsPerBit == 8 right now, and that's not 125 // always the case. 126 channel.data = new ubyte[layer.width*layer.height]; 127 decodeRLE(rleData, channel.data); 128 } 129 break; 130 default: assert(0, "Unsupported compression type."); 131 } 132 133 } 134 135 // Transcode to RGBA 136 auto rgba = new ubyte[layer.width*layer.height*channelCount]; 137 auto rgbaPtr = rgba.ptr; 138 139 140 // Channel indices. 141 auto aI = findChannel(&layer, ChannelType.TRANSPARENCY_MASK); 142 auto rI = findChannel(&layer, ChannelType.R); 143 auto gI = findChannel(&layer, ChannelType.G); 144 auto bI = findChannel(&layer, ChannelType.B); 145 146 147 auto a = layer.channels[aI].data.ptr; 148 auto r = layer.channels[rI].data.ptr; 149 auto g = layer.channels[gI].data.ptr; 150 auto b = layer.channels[bI].data.ptr; 151 152 for (size_t j = 0; j < rgba.length; j += 4) 153 { 154 rgba[j+0] = *r++; 155 rgba[j+1] = *g++; 156 rgba[j+2] = *b++; 157 rgba[j+3] = *a++; 158 } 159 160 // 161 //foreach (size_t y; 0 .. layer.height) 162 //{ 163 // ubyte* a = layer.data.ptr + (y * layer.width * channelCount) + (layer.width * aI); 164 // ubyte* r = layer.data.ptr + (y * layer.width * channelCount) + (layer.width * rI); 165 // ubyte* g = layer.data.ptr + (y * layer.width * channelCount) + (layer.width * gI); 166 // ubyte* b = layer.data.ptr + (y * layer.width * channelCount) + (layer.width * bI); 167 // foreach (size_t x; 0 .. layer.width) 168 // { 169 // *rgbaPtr++ = *r++; 170 // *rgbaPtr++ = *g++; 171 // *rgbaPtr++ = *b++; 172 // *rgbaPtr++ = *a++; 173 // } 174 //} 175 176 layer.data = rgba; 177 } 178 179 private: 180 181 /* 182 PSD HEADER 183 */ 184 void parseHeader(ref File file, ref PSD psd) { 185 186 // Check signature 187 { 188 enforce(file.readStr(4) == "8BPS", "Invalid file, must be a Photoshop PSD file. PSB's are not supported!"); 189 } 190 191 // Check version (must be 1) 192 { 193 enforce(file.readValue!ushort() == 1, "Version does not match 1."); 194 } 195 196 // Check reserve bytes 197 { 198 enforce(file.read(6) == [0, 0, 0, 0, 0, 0], "Unexpected reserve bytes, file may be corrupted."); 199 } 200 201 // Read number of channels 202 // This is the number of channels contained in the document for all layers, including alpha channels. 203 // e.g. for an RGB document with 3 alpha channels, this would be 3 (RGB) + 3 (Alpha) = 6 channels 204 // however, note that the individual layers can have extra channels for transparency masks, vector masks, and user masks. 205 // this is different from layer to layer. 206 psd.channels = file.readValue!ushort; 207 208 // Read rest of header info 209 psd.height = file.readValue!uint; 210 psd.width = file.readValue!uint; 211 psd.bitsPerChannel = file.readValue!ushort; 212 psd.colorMode = cast(ColorMode)file.readValue!ushort; 213 } 214 215 216 217 218 /* 219 COLOR MODE DATA 220 */ 221 void parseColorModeSection(ref File file, ref PSD psd) { 222 psd.colorModeDataSectionLength = file.readValue!uint; 223 psd.colorModeDataSectionOffset = file.tell(); 224 225 file.skip(psd.colorModeDataSectionLength); 226 } 227 228 229 230 231 /* 232 IMAGE RESOURCES 233 */ 234 235 /** 236 Reads a padded value. 237 */ 238 pragma(inline, true) 239 T readPaddedValue(T)(ref File file, T multipleOf = 2, T addTo = 0) { 240 T value = file.readValue!T; 241 return cast(T)roundUpToMultiple(value + addTo, multipleOf); 242 } 243 244 void parseImageResourceSection(ref File file, ref PSD psd) { 245 psd.imageResourceSectionLength = file.readValue!uint; 246 psd.imageResourceSectionOffset = file.tell(); 247 248 // TODO: read 249 ulong leftToRead = psd.imageResourceSectionLength; 250 251 while (leftToRead > 0) { 252 string signature = file.readStr(4); 253 254 enforce(signature == "8BIM" || signature == "psdM", 255 "Image resources section seems to be corrupt, signature does not match \"8BIM\" nor \"psdM\"."); 256 257 const ushort id = file.readValue!ushort; 258 259 const ubyte nameLength = readPaddedValue!ubyte(file, 2, 1); 260 const string name = file.readStr(nameLength - 1); 261 262 const uint resourceSize = readPaddedValue!uint(file); 263 264 switch (id) { 265 case ImageResourceType.IPTC_NAA: 266 case ImageResourceType.CAPTION_DIGEST: 267 case ImageResourceType.PRINT_INFORMATION: 268 case ImageResourceType.PRINT_STYLE: 269 case ImageResourceType.PRINT_SCALE: 270 case ImageResourceType.PRINT_FLAGS: 271 case ImageResourceType.PRINT_FLAGS_INFO: 272 case ImageResourceType.PRINT_INFO: 273 case ImageResourceType.RESOLUTION_INFO: 274 case ImageResourceType.GLOBAL_ANGLE: 275 case ImageResourceType.GLOBAL_ALTITUDE: 276 case ImageResourceType.COLOR_HALFTONING_INFO: 277 case ImageResourceType.COLOR_TRANSFER_FUNCTIONS: 278 case ImageResourceType.MULTICHANNEL_HALFTONING_INFO: 279 case ImageResourceType.MULTICHANNEL_TRANSFER_FUNCTIONS: 280 case ImageResourceType.LAYER_STATE_INFORMATION: 281 case ImageResourceType.LAYER_GROUP_INFORMATION: 282 case ImageResourceType.LAYER_GROUP_ENABLED_ID: 283 case ImageResourceType.LAYER_SELECTION_ID: 284 case ImageResourceType.GRID_GUIDES_INFO: 285 case ImageResourceType.URL_LIST: 286 case ImageResourceType.SLICES: 287 case ImageResourceType.PIXEL_ASPECT_RATIO: 288 case ImageResourceType.ICC_UNTAGGED_PROFILE: 289 case ImageResourceType.ID_SEED_NUMBER: 290 case ImageResourceType.BACKGROUND_COLOR: 291 case ImageResourceType.ALPHA_CHANNEL_UNICODE_NAMES: 292 case ImageResourceType.ALPHA_IDENTIFIERS: 293 case ImageResourceType.COPYRIGHT_FLAG: 294 case ImageResourceType.PATH_SELECTION_STATE: 295 case ImageResourceType.ONION_SKINS: 296 case ImageResourceType.TIMELINE_INFO: 297 case ImageResourceType.SHEET_DISCLOSURE: 298 case ImageResourceType.WORKING_PATH: 299 case ImageResourceType.MAC_PRINT_MANAGER_INFO: 300 case ImageResourceType.WINDOWS_DEVMODE: 301 // we are currently not interested in this resource type, skip it 302 default: 303 // this is a resource we know nothing about, so skip it 304 file.skip(resourceSize); 305 break; 306 307 case ImageResourceType.DISPLAY_INFO: 308 { 309 // the display info resource stores color information and opacity for extra channels contained 310 // in the document. these extra channels could be alpha/transparency, as well as spot color 311 // channels used for printing. 312 313 // check whether storage for alpha channels has been allocated yet 314 // (ImageResourceType.ALPHA_CHANNEL_ASCII_NAMES stores the channel names) 315 if (psd.imageResourcesData.alphaChannels.length == 0) 316 { 317 // note that this assumes RGB mode 318 const uint channelCount = psd.channels - 3; 319 psd.imageResourcesData.alphaChannelCount = channelCount; 320 psd.imageResourcesData.alphaChannels.length = channelCount; 321 } 322 323 const uint versionNum = file.readValue!uint; 324 325 for (uint i = 0u; i < psd.imageResourcesData.alphaChannelCount; ++i) { 326 AlphaChannel* channel = &psd.imageResourcesData.alphaChannels[i]; 327 channel.colorSpace = file.readValue!ushort; 328 channel.color[0] = file.readValue!ushort; 329 channel.color[1] = file.readValue!ushort; 330 channel.color[2] = file.readValue!ushort; 331 channel.color[3] = file.readValue!ushort; 332 channel.opacity = file.readValue!ushort; 333 channel.mode = cast(AlphaChannel.Mode)file.readValue!ubyte; 334 } 335 } 336 break; 337 338 case ImageResourceType.VERSION_INFO: 339 { 340 const uint versionNum = file.readValue!uint; 341 const ubyte hasRealMergedData = file.readValue!ubyte; 342 psd.imageResourcesData.containsRealMergedData = (hasRealMergedData != 0u); 343 file.skip(resourceSize - 5u); 344 } 345 break; 346 347 case ImageResourceType.THUMBNAIL_RESOURCE: 348 { 349 const uint format = file.readValue!uint; 350 const uint width = file.readValue!uint; 351 const uint height = file.readValue!uint; 352 const uint widthInBytes = file.readValue!uint; 353 const uint totalSize = file.readValue!uint; 354 const uint binaryJpegSize = file.readValue!uint; 355 356 const ushort bitsPerPixel = file.readValue!ushort; 357 const ushort numberOfPlanes = file.readValue!ushort; 358 359 psd.imageResourcesData.thumbnail.width = width; 360 psd.imageResourcesData.thumbnail.height = height; 361 psd.imageResourcesData.thumbnail.binaryJpegSize = binaryJpegSize; 362 psd.imageResourcesData.thumbnail.binaryJpeg = file.rawRead(new ubyte[binaryJpegSize]); 363 364 const uint bytesToSkip = resourceSize - 28u - binaryJpegSize; 365 file.skip(bytesToSkip); 366 } 367 break; 368 369 case ImageResourceType.XMP_METADATA: 370 { 371 // load the XMP metadata as raw data 372 enforce(psd.imageResourcesData.xmpMetadata.length != 0, "File contains more than one XMP metadata resource."); 373 psd.imageResourcesData.xmpMetadata = file.rawRead(new ubyte[resourceSize]); 374 } 375 break; 376 377 case ImageResourceType.ICC_PROFILE: 378 { 379 // load the ICC profile as raw data 380 enforce(psd.imageResourcesData.iccProfile.length != 0, "File contains more than one ICC profile."); 381 psd.imageResourcesData.iccProfile = file.rawRead(new ubyte[resourceSize]); 382 psd.imageResourcesData.sizeOfICCProfile = resourceSize; 383 } 384 break; 385 386 case ImageResourceType.EXIF_DATA: 387 { 388 // load the EXIF data as raw data 389 enforce(psd.imageResourcesData.exifData.length != 0, "File contains more than one EXIF data block."); 390 psd.imageResourcesData.exifData = file.rawRead(new ubyte[resourceSize]); 391 psd.imageResourcesData.sizeOfExifData = resourceSize; 392 } 393 break; 394 395 case ImageResourceType.ALPHA_CHANNEL_ASCII_NAMES: 396 { 397 // check whether storage for alpha channels has been allocated yet 398 // (ImageResourceType.DISPLAY_INFO stores the channel color data) 399 if (psd.imageResourcesData.alphaChannels.length == 0) 400 { 401 // note that this assumes RGB mode 402 const uint channelCount = psd.channels - 3; 403 psd.imageResourcesData.alphaChannelCount = channelCount; 404 psd.imageResourcesData.alphaChannels.length = channelCount; 405 } 406 407 // the names of the alpha channels are stored as a series of Pascal strings 408 uint channel = 0; 409 long remaining = resourceSize; 410 while (remaining > 0) { 411 string channelName; 412 const ubyte channelNameLength = file.readValue!ubyte; 413 if (channelNameLength > 0) { 414 channelName = file.readStr(channelNameLength); 415 } 416 417 remaining -= 1 + channelNameLength; 418 419 if (channel < psd.imageResourcesData.alphaChannelCount) { 420 psd.imageResourcesData.alphaChannels[channel].asciiName = channelName; 421 ++channel; 422 } 423 } 424 } 425 break; 426 } 427 428 leftToRead -= 10 + nameLength + resourceSize; 429 } 430 } 431 432 433 434 435 436 437 /* 438 LAYER MASK INFO 439 */ 440 441 // --------------------------------------------------------------------------------------------------------------------- 442 // --------------------------------------------------------------------------------------------------------------------- 443 long ReadMaskRectangle(ref File file, ref MaskData maskData) 444 { 445 maskData.top = file.readValue!int; 446 maskData.left = file.readValue!int; 447 maskData.bottom = file.readValue!int; 448 maskData.right = file.readValue!int; 449 450 return 4u*int.sizeof; 451 } 452 453 454 // --------------------------------------------------------------------------------------------------------------------- 455 // --------------------------------------------------------------------------------------------------------------------- 456 long ReadMaskDensity(ref File file, ref ubyte density) 457 { 458 density = file.readValue!ubyte; 459 return ubyte.sizeof; 460 } 461 462 463 // --------------------------------------------------------------------------------------------------------------------- 464 // --------------------------------------------------------------------------------------------------------------------- 465 long ReadMaskFeather(ref File file, ref double feather) 466 { 467 feather = file.readValue!double; 468 return double.sizeof; 469 } 470 471 472 // --------------------------------------------------------------------------------------------------------------------- 473 // --------------------------------------------------------------------------------------------------------------------- 474 long ReadMaskParameters(ref File file, ref ubyte layerDensity, ref double layerFeather, ref ubyte vectorDensity, ref double vectorFeather) 475 { 476 long bytesRead = 0; 477 478 const ubyte flags = file.readValue!ubyte; 479 bytesRead += ubyte.sizeof; 480 481 const bool hasUserDensity = (flags & (1u << 0)) != 0; 482 const bool hasUserFeather = (flags & (1u << 1)) != 0; 483 const bool hasVectorDensity = (flags & (1u << 2)) != 0; 484 const bool hasVectorFeather = (flags & (1u << 3)) != 0; 485 if (hasUserDensity) 486 { 487 bytesRead += ReadMaskDensity(file, layerDensity); 488 } 489 if (hasUserFeather) 490 { 491 bytesRead += ReadMaskFeather(file, layerFeather); 492 } 493 if (hasVectorDensity) 494 { 495 bytesRead += ReadMaskDensity(file, vectorDensity); 496 } 497 if (hasVectorFeather) 498 { 499 bytesRead += ReadMaskFeather(file, vectorFeather); 500 } 501 502 return bytesRead; 503 } 504 505 // --------------------------------------------------------------------------------------------------------------------- 506 // --------------------------------------------------------------------------------------------------------------------- 507 template ApplyMaskData(T) 508 { 509 void ApplyMaskData(ref const MaskData maskData, double feather, ubyte density, T* layerMask) 510 { 511 layerMask.top = maskData.top; 512 layerMask.left = maskData.left; 513 layerMask.bottom = maskData.bottom; 514 layerMask.right = maskData.right; 515 layerMask.feather = feather; 516 layerMask.density = density; 517 layerMask.defaultColor = maskData.defaultColor; 518 } 519 } 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 void parseLayerMaskInfoSection(ref File file, ref PSD psd) { 535 psd.layerMaskInfoSectionLength = file.readValue!uint; 536 psd.layerMaskInfoSectionOffset = file.tell(); 537 538 // Parse the length of the layer info section 539 uint layerInfoSectionLength = file.readValue!uint; 540 LayerMaskSection* layerMaskSection = parseLayer(file, psd, psd.layerMaskInfoSectionOffset, cast(uint)psd.layerMaskInfoSectionLength, cast(uint)layerInfoSectionLength); 541 542 // TODO: Build hirearchy 543 psd.layers = layerMaskSection.layers; 544 } 545 546 LayerMaskSection* parseLayer(ref File file, ref PSD psd, ulong sectionOffset, uint sectionLength, uint layerLength) { 547 LayerMaskSection* layerMaskSection = new LayerMaskSection; 548 549 if (layerLength != 0) { 550 551 // Read the layer count. If it is a negative number, its absolute value is the number of the layers and the 552 // first alpha channel contains the transparency data for the merged result. 553 // this will also be reflected in the channelCount of the document. 554 short layerCount = file.readValue!short; 555 layerMaskSection.hasTransparencyMask = (layerCount < 0); 556 if (layerCount < 0) layerCount *= -1; 557 558 layerMaskSection.layerCount = cast(uint)layerCount; 559 layerMaskSection.layers = new Layer[layerCount]; 560 561 foreach(i; 0..layerMaskSection.layers.length) { 562 Layer layer; 563 layer.filePtr = file; 564 layer.type = LayerType.Any; 565 566 layer.top = file.readValue!int; 567 layer.left = file.readValue!int; 568 569 // NOTE: It breaks here WTF??? 570 layer.bottom = file.readValue!int; 571 layer.right = file.readValue!int; 572 573 // Number of channels in the layer. 574 // this includes channels for transparency, layer, and vector masks, if any. 575 const ushort channelCount = file.readValue!ushort; 576 layer.channels = new ChannelInfo[channelCount]; 577 578 foreach(j; 0..layer.channels.length) { 579 ChannelInfo* channel = &layer.channels[j]; 580 channel.type = file.readValue!short; 581 channel.dataLength = file.readValue!uint; 582 } 583 584 auto blendModeSignature = file.readStr(4); 585 enforce(blendModeSignature == "8BIM", "Layer mask info section seems to be corrupt, signature does not match \"8BIM\". (was \"%s\")".format(blendModeSignature)); 586 587 //layer.blendModeKey = cast(BlendingMode)file.readStr(4); 588 layer.blendModeKey = file.readValue!uint; 589 layer.opacity = file.readValue!ubyte; 590 layer.clipping = !file.readValue!bool; 591 layer.flags = cast(LayerFlags)file.readValue!ubyte; 592 593 file.skip(1); 594 595 const uint extraDataLength = file.readValue!uint; 596 const uint layerMaskDataLength = file.readValue!uint; 597 598 // the layer mask data section is weird. it may contain extra data for masks, such as density and feather parameters. 599 // there are 3 main possibilities: 600 // *) length == zero . skip this section 601 // *) length == [20, 28] . there is one mask, and that could be either a layer or vector mask. 602 // the mask flags give rise to mask parameters. they store the mask type, and additional parameters, if any. 603 // there might be some padding at the end of this section, and its size depends on which parameters are there. 604 // *) length == [36, 56] . there are two masks. the first mask has parameters, but does NOT store flags yet. 605 // instead, there comes a second section with the same info (flags, default color, rectangle), and 606 // the parameters follow after that. there is also padding at the end of this second section. 607 if (layerMaskDataLength != 0) 608 { 609 // there can be at most two masks, one layer and one vector mask 610 MaskData[2] maskData; 611 uint maskCount = 1u; 612 613 double layerFeather = 0.0; 614 double vectorFeather = 0.0; 615 ubyte layerDensity = 0; 616 ubyte vectorDensity = 0; 617 618 long toRead = layerMaskDataLength; 619 620 // enclosing rectangle 621 toRead -= ReadMaskRectangle(file, maskData[0]); 622 623 maskData[0].defaultColor = file.readValue!ubyte; 624 toRead -= ubyte.sizeof; 625 626 const ubyte maskFlags = file.readValue!ubyte; 627 toRead -= ubyte.sizeof; 628 629 maskData[0].isVectorMask = (maskFlags & (1u << 3)) != 0; 630 bool maskHasParameters = (maskFlags & (1u << 4)) != 0; 631 if (maskHasParameters && (layerMaskDataLength <= 28)) 632 { 633 toRead -= ReadMaskParameters(file, layerDensity, layerFeather, vectorDensity, vectorFeather); 634 } 635 636 // check if there is enough data left for another section of mask data 637 if (toRead >= 18) 638 { 639 // in case there is still data left to read, the following values are for the real layer mask. 640 // the data we just read was for the vector mask. 641 maskCount = 2u; 642 643 const ubyte realFlags = file.readValue!ubyte; 644 toRead -= ubyte.sizeof; 645 646 maskData[1].defaultColor = file.readValue!ubyte; 647 toRead -= ubyte.sizeof; 648 649 toRead -= ReadMaskRectangle(file, maskData[1]); 650 651 maskData[1].isVectorMask = (realFlags & (1u << 3)) != 0; 652 653 // note the OR here. whether the following section has mask parameter data or not is influenced by 654 // the availability of parameter data of the previous mask! 655 maskHasParameters |= ((realFlags & (1u << 4)) != 0); 656 if (maskHasParameters) 657 { 658 toRead -= ReadMaskParameters(file, layerDensity, layerFeather, vectorDensity, vectorFeather); 659 } 660 } 661 662 // skip the remaining padding bytes, if any 663 enforce(toRead >= 0, format("Parsing failed, #d bytes left", toRead)); 664 file.skip(cast(ulong)toRead); 665 666 // apply mask data to our own data structures 667 for (uint mask=0; mask < maskCount; ++mask) 668 { 669 const bool isVectorMask = maskData[mask].isVectorMask; 670 if (isVectorMask) 671 { 672 enforce(layer.vectorMask == null, "A vector mask already exists."); 673 layer.vectorMask = new VectorMask[1]; 674 layer.vectorMask[0].data = null; 675 layer.vectorMask[0].fileOffset = 0; 676 ApplyMaskData!VectorMask(maskData[mask], vectorFeather, vectorDensity, &layer.vectorMask[0]); 677 } 678 else 679 { 680 enforce(layer.layerMask == null, "A layer mask already exists."); 681 layer.layerMask = new LayerMask[1]; 682 layer.layerMask[0].data = null; 683 layer.layerMask[0].fileOffset = 0; 684 ApplyMaskData!LayerMask(maskData[mask], layerFeather, layerDensity, &layer.layerMask[0]); 685 } 686 } 687 } 688 689 // skip blending ranges data, we are not interested in that for now 690 const uint layerBlendingRangesDataLength = file.readValue!uint; 691 file.skip(layerBlendingRangesDataLength); 692 693 // the layer name is stored as pascal string, padded to a multiple of 4 694 // we peek here as the actual calculation happens inside readPascalStr 695 // TODO: make this more pretty? 696 const ubyte nameLength = file.peekValue!ubyte; 697 const uint paddedNameLength = roundUpToMultiple(nameLength + 1u, 4u); 698 699 layer.name = file.readPascalStr(paddedNameLength - 1u); 700 701 // read Additional Layer Information that exists since Photoshop 4.0. 702 // getting the size of this data is a bit awkward, because it's not stored explicitly somewhere. furthermore, 703 // the PSD format sometimes includes the 4-byte length in its section size, and sometimes not. 704 const uint additionalLayerInfoSize = extraDataLength - layerMaskDataLength - layerBlendingRangesDataLength - paddedNameLength - 8u; 705 long toRead = additionalLayerInfoSize; 706 707 while (toRead > 0) 708 { 709 const string signature = file.readStr(4); 710 enforce(signature == "8BIM", "Additional Layer Information section seems to be corrupt, signature does not match \"8BIM\". (was \"%s\")".format(signature)); 711 712 const string key = file.readStr(4); 713 714 // length needs to be rounded to an even number 715 uint length = file.readValue!uint; 716 length = roundUpToMultiple(length, 2u); 717 718 // read "Section divider setting" to identify whether a layer is a group, or a section divider 719 if (key == "lsct") 720 { 721 layer.type = cast(LayerType)file.readValue!uint; 722 723 // skip the rest of the data 724 file.skip(length - 4u); 725 } 726 // read Unicode layer name 727 else if (key == "luni") 728 { 729 // PSD Unicode strings store 4 bytes for the number of characters, NOT bytes, followed by 730 // 2-byte UTF16 Unicode data without the terminating null. 731 const uint characterCountWithoutNull = file.readValue!uint; 732 wstring utf16Name; 733 for (uint c = 0u; c < characterCountWithoutNull; ++c) 734 { 735 utf16Name ~= cast(wchar)file.readValue!ushort; 736 } 737 738 // If there's a unicode name we may as well use that here. 739 import std.utf : toUTF8; 740 layer.name = utf16Name.toUTF8; 741 742 // skip possible padding bytes 743 file.skip(length - 4u - characterCountWithoutNull * ushort.sizeof); 744 } 745 else 746 { 747 file.skip(length); 748 } 749 750 toRead -= 3*uint.sizeof + length; 751 } 752 753 layerMaskSection.layers[i] = layer; 754 writeln(file.tell(), ": ", layer.name, " : ", layer.type); 755 } 756 757 758 // walk through the layers and channels, but don't extract their data just yet. only save the file offset for extracting the 759 // data later. 760 foreach (i; 0..layerMaskSection.layers.length) 761 { 762 Layer* layer = &layerMaskSection.layers[i]; 763 foreach(j; 0..layer.channels.length) { 764 ChannelInfo* channel = &layer.channels[j]; 765 channel.fileOffset = cast(uint)file.tell(); 766 file.skip(channel.dataLength); 767 } 768 } 769 } 770 771 if (sectionLength > 0) 772 { 773 // start loading at the global layer mask info section, located after the Layer Information Section. 774 // note that the 4 bytes that stored the length of the section are not included in the length itself. 775 const ulong globalInfoSectionOffset = sectionOffset + layerLength + 4u; 776 file.seek(globalInfoSectionOffset); 777 778 // work out how many bytes are left to read at this point. we need that to figure out the size of the last 779 // optional section, the Additional Layer Information. 780 if (sectionOffset + sectionLength > globalInfoSectionOffset) 781 { 782 long toRead = cast(long)(sectionOffset + sectionLength - globalInfoSectionOffset); 783 const uint globalLayerMaskLength = file.readValue!uint; 784 toRead -= uint.sizeof; 785 786 if (globalLayerMaskLength != 0) 787 { 788 layerMaskSection.overlayColorSpace = file.readValue!ushort; 789 790 // 4*2 byte color components 791 file.skip(8); 792 793 layerMaskSection.opacity = file.readValue!ushort; 794 layerMaskSection.kind = file.readValue!ubyte; 795 796 toRead -= 2u*ushort.sizeof + ubyte.sizeof + 8u; 797 798 // filler bytes (zeroes) 799 const uint remaining = cast(uint)(globalLayerMaskLength - 2u*ushort.sizeof - ubyte.sizeof - 8u); 800 file.skip(remaining); 801 802 toRead -= remaining; 803 } 804 805 // are there still bytes left to read? then this is the Additional Layer Information that exists since Photoshop 4.0. 806 while (toRead > 0) 807 { 808 const string signature = file.readStr(4); 809 enforce(signature == "8BIM", "Additional Layer Information section seems to be corrupt, signature does not match \"8BIM\"."); 810 811 const string key = file.readStr(4); 812 813 // again, length is rounded to a multiple of 4 814 uint length = file.readValue!uint; 815 length = roundUpToMultiple(length, 4u); 816 817 if (key == "Lr16") 818 { 819 const ulong offset = file.tell(); 820 821 // NOTE: I think in D we...can just let this get copied over? 822 //DestroyLayerMaskSection(layerMaskSection, allocator); 823 layerMaskSection = parseLayer(file, psd, 0u, 0u, length); 824 file.seek(offset + length); 825 } 826 else if (key == "Lr32") 827 { 828 const ulong offset = file.tell(); 829 830 // NOTE: I think in D we...can just let this get copied over? 831 //DestroyLayerMaskSection(layerMaskSection, allocator); 832 833 layerMaskSection = parseLayer(file, psd, 0u, 0u, length); 834 file.seek(offset + length); 835 } 836 else if (key == "vmsk") 837 { 838 // TODO: could read extra vector mask data here 839 file.skip(length); 840 } 841 else if (key == "lnk2") 842 { 843 // TODO: could read individual smart object layer data here 844 file.skip(length); 845 } 846 else 847 { 848 file.skip(length); 849 } 850 851 toRead -= 3u*uint.sizeof + length; 852 } 853 } 854 } 855 856 return layerMaskSection; 857 } 858 859 860 /* 861 IMAGE DATA 862 */ 863 void parseImageDataSectionOffset(ref File file, ref PSD psd) { 864 psd.imageDataSectionOffset = file.tell(); 865 psd.imageDataSectionLength = file.size() - psd.imageDataSectionOffset; 866 867 // TODO: read 868 }