/////////////////////////////////////////////////////////////////////////////////
//
// Photoshop PSD FileType Plugin for Paint.NET
// http://psdplugin.codeplex.com/
//
// This software is provided under the MIT License:
// Copyright (c) 2006-2007 Frank Blumenberg
// Copyright (c) 2010-2015 Tao Yue
//
// Portions of this file are provided under the BSD 3-clause License:
// Copyright (c) 2006, Jonas Beckeman
//
// See LICENSE.txt for complete licensing and attribution information.
//
/////////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
namespace PhotoshopFile
{
internal enum ResourceID
{
Undefined = 0,
MacPrintInfo = 1001,
ResolutionInfo = 1005,
AlphaChannelNames = 1006,
DisplayInfo = 1007,
Caption = 1008,
BorderInfo = 1009,
BackgroundColor = 1010,
PrintFlags = 1011,
MultichannelHalftoneInfo = 1012,
ColorHalftoneInfo = 1013,
DuotoneHalftoneInfo = 1014,
MultichannelTransferFunctions = 1015,
ColorTransferFunctions = 1016,
DuotoneTransferFunctions = 1017,
DuotoneImageInfo = 1018,
BlackWhiteRange = 1019,
EpsOptions = 1021,
QuickMaskInfo = 1022,
LayerStateInfo = 1024,
WorkingPathUnsaved = 1025,
LayersGroupInfo = 1026,
IptcNaa = 1028,
RawFormatImageMode = 1029,
JpegQuality = 1030,
GridGuidesInfo = 1032,
ThumbnailBgr = 1033,
CopyrightInfo = 1034,
Url = 1035,
ThumbnailRgb = 1036,
GlobalAngle = 1037,
ColorSamplersObsolete = 1038,
IccProfile = 1039,
Watermark = 1040,
IccUntagged = 1041,
EffectsVisible = 1042,
SpotHalftone = 1043,
DocumentSpecific = 1044,
UnicodeAlphaNames = 1045,
IndexedColorTableCount = 1046,
TransparentIndex = 1047,
GlobalAltitude = 1049,
Slices = 1050,
WorkflowUrl = 1051,
JumpToXpep = 1052,
AlphaIdentifiers = 1053,
UrlList = 1054,
VersionInfo = 1057,
ExifData1 = 1058,
ExifData3 = 1059,
XmpMetadata = 1060,
CaptionDigest = 1061,
PrintScale = 1062,
PixelAspectRatio = 1064,
LayerComps = 1065,
AlternateDuotoneColors = 1066,
AlternateSpotColors = 1067,
LayerSelectionIDs = 1069,
HdrToningInfo = 1070,
PrintInfo = 1071,
LayerGroupsEnabled = 1072,
ColorSamplers = 1073,
MeasurementScale = 1074,
TimelineInfo = 1075,
SheetDisclosure = 1076,
FloatDisplayInfo = 1077,
OnionSkins = 1078,
CountInfo = 1080,
PrintSettingsInfo = 1082,
PrintStyle = 1083,
MacNSPrintInfo = 1084,
WinDevMode = 1085,
AutoSaveFilePath = 1086,
AutoSaveFormat = 1087,
PathInfo = 2000, // 2000-2999: Path Information
ClippingPathName = 2999,
LightroomWorkflow = 8000,
PrintFlagsInfo = 10000
}
///
/// Abstract class for Image Resources
///
internal abstract class ImageResource
{
private string signature;
public string Signature
{
get { return signature; }
set
{
if (value.Length != 4)
{
throw new ArgumentException(
"Signature must be 4 characters in length.");
}
signature = value;
}
}
public string Name { get; set; }
public abstract ResourceID ID { get; }
protected ImageResource(string name)
{
Signature = "8BIM";
Name = name;
}
}
///
/// Creates the appropriate subclass of ImageResource.
///
internal static class ImageResourceFactory
{
public static ImageResource CreateImageResource(PsdBinaryReader reader)
{
Util.DebugMessage(reader.BaseStream, "Load, Begin, ImageResource");
var signature = reader.ReadAsciiChars(4);
var resourceIdInt = reader.ReadUInt16();
var name = reader.ReadPascalString(2);
var dataLength = (int)reader.ReadUInt32();
var dataPaddedLength = Util.RoundUp(dataLength, 2);
var endPosition = reader.BaseStream.Position + dataPaddedLength;
ImageResource resource = null;
var resourceId = (ResourceID)resourceIdInt;
switch (resourceId)
{
case ResourceID.ResolutionInfo:
resource = new ResolutionInfo(reader, name);
break;
case ResourceID.ThumbnailRgb:
case ResourceID.ThumbnailBgr:
resource = new Thumbnail(reader, resourceId, name, dataLength);
break;
case ResourceID.AlphaChannelNames:
resource = new AlphaChannelNames(reader, name, dataLength);
break;
case ResourceID.UnicodeAlphaNames:
resource = new UnicodeAlphaNames(reader, name, dataLength);
break;
case ResourceID.VersionInfo:
resource = new VersionInfo(reader, name);
break;
default:
resource = new RawImageResource(reader, signature, resourceId, name, dataLength);
break;
}
Util.DebugMessage(reader.BaseStream, "Load, End, ImageResource, {0}",
resourceId);
// Reposition the reader if we do not consume the full resource block.
// This takes care of the even-padding, and also preserves forward-
// compatibility in case a resource block is later extended with
// additional properties.
if (reader.BaseStream.Position < endPosition)
reader.BaseStream.Position = endPosition;
// However, overruns are definitely an error.
if (reader.BaseStream.Position > endPosition)
throw new PsdInvalidException("Corruption detected in resource.");
return resource;
}
}
internal class ImageResources : List
{
public ImageResources() : base()
{
}
public ImageResource Get(ResourceID id)
{
return Find(x => x.ID == id);
}
public void Set(ImageResource resource)
{
Predicate matchId = delegate(ImageResource res)
{
return res.ID == resource.ID;
};
var itemIdx = this.FindIndex(matchId);
var lastItemIdx = this.FindLastIndex(matchId);
if (itemIdx == -1)
{
Add(resource);
}
else if (itemIdx != lastItemIdx)
{
RemoveAll(matchId);
Insert(itemIdx, resource);
}
else
{
this[itemIdx] = resource;
}
}
}
}