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