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.blendModeKey = file.readValue!uint; 587 layer.opacity = file.readValue!ubyte; 588 layer.clipping = !file.readValue!bool; 589 layer.flags = cast(LayerFlags)file.readValue!ubyte; 590 591 file.skip(1); 592 593 const uint extraDataLength = file.readValue!uint; 594 const uint layerMaskDataLength = file.readValue!uint; 595 596 // the layer mask data section is weird. it may contain extra data for masks, such as density and feather parameters. 597 // there are 3 main possibilities: 598 // *) length == zero . skip this section 599 // *) length == [20, 28] . there is one mask, and that could be either a layer or vector mask. 600 // the mask flags give rise to mask parameters. they store the mask type, and additional parameters, if any. 601 // there might be some padding at the end of this section, and its size depends on which parameters are there. 602 // *) length == [36, 56] . there are two masks. the first mask has parameters, but does NOT store flags yet. 603 // instead, there comes a second section with the same info (flags, default color, rectangle), and 604 // the parameters follow after that. there is also padding at the end of this second section. 605 if (layerMaskDataLength != 0) 606 { 607 // there can be at most two masks, one layer and one vector mask 608 MaskData[2] maskData; 609 uint maskCount = 1u; 610 611 double layerFeather = 0.0; 612 double vectorFeather = 0.0; 613 ubyte layerDensity = 0; 614 ubyte vectorDensity = 0; 615 616 long toRead = layerMaskDataLength; 617 618 // enclosing rectangle 619 toRead -= ReadMaskRectangle(file, maskData[0]); 620 621 maskData[0].defaultColor = file.readValue!ubyte; 622 toRead -= ubyte.sizeof; 623 624 const ubyte maskFlags = file.readValue!ubyte; 625 toRead -= ubyte.sizeof; 626 627 maskData[0].isVectorMask = (maskFlags & (1u << 3)) != 0; 628 bool maskHasParameters = (maskFlags & (1u << 4)) != 0; 629 if (maskHasParameters && (layerMaskDataLength <= 28)) 630 { 631 toRead -= ReadMaskParameters(file, layerDensity, layerFeather, vectorDensity, vectorFeather); 632 } 633 634 // check if there is enough data left for another section of mask data 635 if (toRead >= 18) 636 { 637 // in case there is still data left to read, the following values are for the real layer mask. 638 // the data we just read was for the vector mask. 639 maskCount = 2u; 640 641 const ubyte realFlags = file.readValue!ubyte; 642 toRead -= ubyte.sizeof; 643 644 maskData[1].defaultColor = file.readValue!ubyte; 645 toRead -= ubyte.sizeof; 646 647 toRead -= ReadMaskRectangle(file, maskData[1]); 648 649 maskData[1].isVectorMask = (realFlags & (1u << 3)) != 0; 650 651 // note the OR here. whether the following section has mask parameter data or not is influenced by 652 // the availability of parameter data of the previous mask! 653 maskHasParameters |= ((realFlags & (1u << 4)) != 0); 654 if (maskHasParameters) 655 { 656 toRead -= ReadMaskParameters(file, layerDensity, layerFeather, vectorDensity, vectorFeather); 657 } 658 } 659 660 // skip the remaining padding bytes, if any 661 enforce(toRead >= 0, format("Parsing failed, #d bytes left", toRead)); 662 file.skip(cast(ulong)toRead); 663 664 // apply mask data to our own data structures 665 for (uint mask=0; mask < maskCount; ++mask) 666 { 667 const bool isVectorMask = maskData[mask].isVectorMask; 668 if (isVectorMask) 669 { 670 enforce(layer.vectorMask == null, "A vector mask already exists."); 671 layer.vectorMask = new VectorMask[1]; 672 layer.vectorMask[0].data = null; 673 layer.vectorMask[0].fileOffset = 0; 674 ApplyMaskData!VectorMask(maskData[mask], vectorFeather, vectorDensity, &layer.vectorMask[0]); 675 } 676 else 677 { 678 enforce(layer.layerMask == null, "A layer mask already exists."); 679 layer.layerMask = new LayerMask[1]; 680 layer.layerMask[0].data = null; 681 layer.layerMask[0].fileOffset = 0; 682 ApplyMaskData!LayerMask(maskData[mask], layerFeather, layerDensity, &layer.layerMask[0]); 683 } 684 } 685 } 686 687 // skip blending ranges data, we are not interested in that for now 688 const uint layerBlendingRangesDataLength = file.readValue!uint; 689 file.skip(layerBlendingRangesDataLength); 690 691 // the layer name is stored as pascal string, padded to a multiple of 4 692 // we peek here as the actual calculation happens inside readPascalStr 693 // TODO: make this more pretty? 694 const ubyte nameLength = file.peekValue!ubyte; 695 const uint paddedNameLength = roundUpToMultiple(nameLength + 1u, 4u); 696 697 layer.name = file.readPascalStr(paddedNameLength - 1u); 698 699 // read Additional Layer Information that exists since Photoshop 4.0. 700 // getting the size of this data is a bit awkward, because it's not stored explicitly somewhere. furthermore, 701 // the PSD format sometimes includes the 4-byte length in its section size, and sometimes not. 702 const uint additionalLayerInfoSize = extraDataLength - layerMaskDataLength - layerBlendingRangesDataLength - paddedNameLength - 8u; 703 long toRead = additionalLayerInfoSize; 704 705 while (toRead > 0) 706 { 707 const string signature = file.readStr(4); 708 enforce(signature == "8BIM", "Additional Layer Information section seems to be corrupt, signature does not match \"8BIM\". (was \"%s\")".format(signature)); 709 710 const string key = file.readStr(4); 711 712 // length needs to be rounded to an even number 713 uint length = file.readValue!uint; 714 length = roundUpToMultiple(length, 2u); 715 716 // read "Section divider setting" to identify whether a layer is a group, or a section divider 717 if (key == "lsct") 718 { 719 layer.type = cast(LayerType)file.readValue!uint; 720 721 // skip the rest of the data 722 file.skip(length - 4u); 723 } 724 // read Unicode layer name 725 else if (key == "luni") 726 { 727 // PSD Unicode strings store 4 bytes for the number of characters, NOT bytes, followed by 728 // 2-byte UTF16 Unicode data without the terminating null. 729 const uint characterCountWithoutNull = file.readValue!uint; 730 wstring utf16Name; 731 for (uint c = 0u; c < characterCountWithoutNull; ++c) 732 { 733 utf16Name ~= cast(wchar)file.readValue!ushort; 734 } 735 736 // If there's a unicode name we may as well use that here. 737 import std.utf : toUTF8; 738 layer.name = utf16Name.toUTF8; 739 740 // skip possible padding bytes 741 file.skip(length - 4u - characterCountWithoutNull * ushort.sizeof); 742 } 743 else 744 { 745 file.skip(length); 746 } 747 748 toRead -= 3*uint.sizeof + length; 749 } 750 751 layerMaskSection.layers[i] = layer; 752 } 753 754 755 // walk through the layers and channels, but don't extract their data just yet. only save the file offset for extracting the 756 // data later. 757 foreach (i; 0..layerMaskSection.layers.length) 758 { 759 Layer* layer = &layerMaskSection.layers[i]; 760 foreach(j; 0..layer.channels.length) { 761 ChannelInfo* channel = &layer.channels[j]; 762 channel.fileOffset = cast(uint)file.tell(); 763 file.skip(channel.dataLength); 764 } 765 } 766 } 767 768 if (sectionLength > 0) 769 { 770 // start loading at the global layer mask info section, located after the Layer Information Section. 771 // note that the 4 bytes that stored the length of the section are not included in the length itself. 772 const ulong globalInfoSectionOffset = sectionOffset + layerLength + 4u; 773 file.seek(globalInfoSectionOffset); 774 775 // work out how many bytes are left to read at this point. we need that to figure out the size of the last 776 // optional section, the Additional Layer Information. 777 if (sectionOffset + sectionLength > globalInfoSectionOffset) 778 { 779 long toRead = cast(long)(sectionOffset + sectionLength - globalInfoSectionOffset); 780 const uint globalLayerMaskLength = file.readValue!uint; 781 toRead -= uint.sizeof; 782 783 if (globalLayerMaskLength != 0) 784 { 785 layerMaskSection.overlayColorSpace = file.readValue!ushort; 786 787 // 4*2 byte color components 788 file.skip(8); 789 790 layerMaskSection.opacity = file.readValue!ushort; 791 layerMaskSection.kind = file.readValue!ubyte; 792 793 toRead -= 2u*ushort.sizeof + ubyte.sizeof + 8u; 794 795 // filler bytes (zeroes) 796 const uint remaining = cast(uint)(globalLayerMaskLength - 2u*ushort.sizeof - ubyte.sizeof - 8u); 797 file.skip(remaining); 798 799 toRead -= remaining; 800 } 801 802 // are there still bytes left to read? then this is the Additional Layer Information that exists since Photoshop 4.0. 803 while (toRead > 0) 804 { 805 const string signature = file.readStr(4); 806 enforce(signature == "8BIM", "Additional Layer Information section seems to be corrupt, signature does not match \"8BIM\"."); 807 808 const string key = file.readStr(4); 809 810 // again, length is rounded to a multiple of 4 811 uint length = file.readValue!uint; 812 length = roundUpToMultiple(length, 4u); 813 814 if (key == "Lr16") 815 { 816 const ulong offset = file.tell(); 817 818 // NOTE: I think in D we...can just let this get copied over? 819 //DestroyLayerMaskSection(layerMaskSection, allocator); 820 layerMaskSection = parseLayer(file, psd, 0u, 0u, length); 821 file.seek(offset + length); 822 } 823 else if (key == "Lr32") 824 { 825 const ulong offset = file.tell(); 826 827 // NOTE: I think in D we...can just let this get copied over? 828 //DestroyLayerMaskSection(layerMaskSection, allocator); 829 830 layerMaskSection = parseLayer(file, psd, 0u, 0u, length); 831 file.seek(offset + length); 832 } 833 else if (key == "vmsk") 834 { 835 // TODO: could read extra vector mask data here 836 file.skip(length); 837 } 838 else if (key == "lnk2") 839 { 840 // TODO: could read individual smart object layer data here 841 file.skip(length); 842 } 843 else 844 { 845 file.skip(length); 846 } 847 848 toRead -= 3u*uint.sizeof + length; 849 } 850 } 851 } 852 853 return layerMaskSection; 854 } 855 856 857 /* 858 IMAGE DATA 859 */ 860 void parseImageDataSectionOffset(ref File file, ref PSD psd) { 861 psd.imageDataSectionOffset = file.tell(); 862 psd.imageDataSectionLength = file.size() - psd.imageDataSectionOffset; 863 864 // TODO: read 865 }