2
0

GR32.ImageFormats.PSD.pas 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990
  1. unit GR32.ImageFormats.PSD;
  2. (* ***** BEGIN LICENSE BLOCK *****
  3. * Version: MPL 1.1 or LGPL 2.1 with linking exception
  4. *
  5. * The contents of this file are subject to the Mozilla Public License Version
  6. * 1.1 (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. * http://www.mozilla.org/MPL/
  9. *
  10. * Software distributed under the License is distributed on an "AS IS" basis,
  11. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. * for the specific language governing rights and limitations under the
  13. * License.
  14. *
  15. * Alternatively, the contents of this file may be used under the terms of the
  16. * Free Pascal modified version of the GNU Lesser General Public License
  17. * Version 2.1 (the "FPC modified LGPL License"), in which case the provisions
  18. * of this license are applicable instead of those above.
  19. * Please see the file LICENSE.txt for additional information concerning this
  20. * license.
  21. *
  22. * The Original Code is PSD Image Format support for Graphics32
  23. *
  24. * The Initial Developer of the Original Code is
  25. * Lamdalili
  26. *
  27. * Portions created by the Initial Developer are Copyright (C) 2023
  28. * the Initial Developer. All Rights Reserved.
  29. *
  30. * Contributor(s):
  31. * Anders Melander <[email protected]>
  32. *
  33. * ***** END LICENSE BLOCK ***** *)
  34. interface
  35. {$include GR32.inc}
  36. uses
  37. Generics.Collections,
  38. Classes,
  39. SysUtils,
  40. GR32,
  41. GR32_Image;
  42. //------------------------------------------------------------------------------
  43. //
  44. // PSD simple types
  45. //
  46. //------------------------------------------------------------------------------
  47. type
  48. TPSDLayerBlendMode = (
  49. lbmPass,
  50. lbmNormal,
  51. lbmDarken,
  52. lbmLighten,
  53. lbmHue,
  54. lbmSaturation,
  55. lbmColor,
  56. lbmLuminosity,
  57. lbmMultiply,
  58. lbmScreen,
  59. lbmDissolve,
  60. lbmOverlay,
  61. lbmHardLight,
  62. lbmSoftLight,
  63. lbmDifference,
  64. lbmExclusion,
  65. lbmColorDodge,
  66. lbmColorBurn,
  67. lbmLinearLight,
  68. lbmLinearBurn,
  69. lbmDarkerColor,
  70. lbmLinearDodge,
  71. lbmPinLight,
  72. lbmVividLight,
  73. lbmHardMix,
  74. lbmLighterColor,
  75. lbmSubtract,
  76. lbmDivide
  77. );
  78. TPSDLayerCompression = (
  79. lcRAW,
  80. lcRLE,
  81. lcZIP,
  82. lcPredictedZIP
  83. );
  84. TPSDLayerOptions = set of (
  85. loTransparencyProtected,
  86. loHidden,
  87. loIrrelevantData,
  88. loFlag3,
  89. loFlag4
  90. );
  91. type
  92. EPhotoshopDocument = class(Exception);
  93. //------------------------------------------------------------------------------
  94. //
  95. // TCustomPhotoshopLayer
  96. //
  97. //------------------------------------------------------------------------------
  98. // Represents a single PSD layer
  99. //------------------------------------------------------------------------------
  100. type
  101. TPhotoshopDocument = class;
  102. TCustomPhotoshopLayer = class abstract
  103. private
  104. FDocument: TPhotoshopDocument;
  105. FTop: integer;
  106. FLeft: integer;
  107. FHeight: integer;
  108. FWidth: integer;
  109. FName: string;
  110. FBlendMode: TPSDLayerBlendMode;
  111. FOpacity: Byte;
  112. FOptions: TPSDLayerOptions;
  113. FClipping: boolean;
  114. FCompression: TPSDLayerCompression;
  115. FUseDocumentCompression: boolean;
  116. protected
  117. procedure SetDocument(const Value: TPhotoshopDocument);
  118. function GetIndex: integer;
  119. procedure SetIndex(const Value: integer);
  120. function GetBoundsRect: TRect;
  121. procedure SetBoundsRect(const Value: TRect);
  122. procedure SetCompression(const Value: TPSDLayerCompression);
  123. function GetCompression: TPSDLayerCompression;
  124. procedure SetUseDocumentCompression(const Value: boolean);
  125. procedure GetChannelScanLine(AChannel: TColor32Component; ALine: integer; var Bytes); virtual; abstract;
  126. function GetHeight: Integer; virtual;
  127. function GetWidth: Integer; virtual;
  128. procedure SetHeight(const Value: Integer); virtual;
  129. procedure SetWidth(const Value: Integer); virtual;
  130. public
  131. constructor Create(ADocument: TPhotoshopDocument = nil); virtual;
  132. destructor Destroy; override;
  133. procedure SetBounds(ALeft, ATop, AWidth, AHeight :integer);
  134. procedure BeginScan; virtual;
  135. procedure EndScan; virtual;
  136. property Document: TPhotoshopDocument read FDocument write SetDocument;
  137. property Index: integer read GetIndex write SetIndex;
  138. property BoundsRect: TRect read GetBoundsRect write SetBoundsRect;
  139. property Top: integer read FTop write FTop;
  140. property Left: integer read FLeft write FLeft;
  141. property Height: Integer read GetHeight write SetHeight;
  142. property Width: Integer read GetWidth write SetWidth;
  143. property Name: string read FName write FName;
  144. property BlendMode: TPSDLayerBlendMode read FBlendMode write FBlendMode;
  145. property Opacity: Byte read FOpacity write FOpacity;
  146. property Options: TPSDLayerOptions read FOptions write FOptions;
  147. property Clipping: boolean read FClipping write FClipping;
  148. property Compression: TPSDLayerCompression read GetCompression write SetCompression;
  149. property UseDocumentCompression: boolean read FUseDocumentCompression write SetUseDocumentCompression;
  150. end;
  151. TPhotoshopLayerClass = class of TCustomPhotoshopLayer;
  152. //------------------------------------------------------------------------------
  153. //
  154. // TPhotoshopDocument
  155. //
  156. //------------------------------------------------------------------------------
  157. // Represents a PSD document/file, (typically) containing one or more layers
  158. //------------------------------------------------------------------------------
  159. TPhotoshopDocument = class(TPersistent)
  160. private type
  161. TPhotoshopLayers = class
  162. private
  163. FDocument: TPhotoshopDocument;
  164. FLayers: TObjectList<TCustomPhotoshopLayer>;
  165. protected
  166. function GetCount: integer;
  167. function GetLayer(Index: integer): TCustomPhotoshopLayer;
  168. procedure AddLayer(ALayer: TCustomPhotoshopLayer);
  169. procedure RemoveLayer(ALayer: TCustomPhotoshopLayer);
  170. function IndexOf(ALayer: TCustomPhotoshopLayer): integer;
  171. procedure Move(OldIndex, NewIndex: integer);
  172. public
  173. constructor Create(ADocument: TPhotoshopDocument);
  174. destructor Destroy; override;
  175. function Add(ALayerClass: TPhotoshopLayerClass = nil): TCustomPhotoshopLayer;
  176. procedure Clear;
  177. property Count: integer read GetCount;
  178. property Layers[Index: integer]: TCustomPhotoshopLayer read GetLayer; default;
  179. end;
  180. private
  181. FLayers: TPhotoshopLayers;
  182. FWidth: Integer;
  183. FHeight: Integer;
  184. FBackground: TCustomPhotoshopLayer;
  185. FCompression: TPSDLayerCompression;
  186. private
  187. class var
  188. FDefaultLayerClass: TPhotoshopLayerClass;
  189. FDefaultCompression: TPSDLayerCompression;
  190. protected
  191. procedure SetBackground(const Value: TCustomPhotoshopLayer);
  192. procedure SetCompression(const Value: TPSDLayerCompression);
  193. procedure AddLayer(ALayer: TCustomPhotoshopLayer);
  194. procedure RemoveLayer(ALayer: TCustomPhotoshopLayer);
  195. public
  196. constructor Create(ABackground: TCustomPhotoshopLayer = nil);
  197. destructor Destroy; override;
  198. procedure Assign(Source: TPersistent); override;
  199. procedure Clear;
  200. procedure SetSize(AWidth, AHeight: Integer);
  201. property Width: Integer read FWidth write FWidth;
  202. property Height: Integer read FHeight write FHeight;
  203. // Layers: The individual PSD layers
  204. property Layers: TPhotoshopLayers read FLayers;
  205. // Background: A composite of the flattened image.
  206. // If the document contains no layers this is the primary image. Otherwise
  207. // it is usually just used as a preview of the image. Applications that
  208. // cannot handle layers will often just load this bitmap and ignore the
  209. // layers, while application that does handle layers will ignore the
  210. // background if the image contains layers. For this reason you should only
  211. // omit the background if you are sure that the reader will not require it.
  212. // If no background is specified then a fully transparent bitmap will be
  213. // saved in its place.
  214. property Background: TCustomPhotoshopLayer read FBackground write SetBackground;
  215. // Default background and layer compression. Initialized to the
  216. // value of DefaultCompression.
  217. property Compression: TPSDLayerCompression read FCompression write SetCompression;
  218. // DefaultLayerClass: The type of layer create when calling Layers.Add
  219. // with no layer type specified.
  220. class property DefaultLayerClass: TPhotoshopLayerClass read FDefaultLayerClass write FDefaultLayerClass;
  221. // DefaultCompression: The default background and layer compression used
  222. // if no explicit compression type is specified.
  223. class property DefaultCompression: TPSDLayerCompression read FDefaultCompression write FDefaultCompression;
  224. end;
  225. //------------------------------------------------------------------------------
  226. //
  227. // TPhotoshopLayer32
  228. //
  229. //------------------------------------------------------------------------------
  230. // Layer wrapping a TBitmap32
  231. // Note that by default the layer only references the bitmap; It doesn't own it.
  232. //------------------------------------------------------------------------------
  233. type
  234. TPhotoshopLayer32 = class(TCustomPhotoshopLayer)
  235. private
  236. FBitmap: TCustomBitmap32;
  237. FOwnsBitmap: boolean;
  238. FSourceTop: integer;
  239. FSourceLeft: integer;
  240. protected
  241. procedure GetChannelScanLine(AChannel: TColor32Component; ALine: integer; var Bytes); override;
  242. function GetHeight: Integer; override;
  243. function GetWidth: Integer; override;
  244. procedure SetBitmap(const Value: TCustomBitmap32);
  245. function GetSourceRect: TRect;
  246. procedure SetSourceRect(const Value: TRect);
  247. public
  248. destructor Destroy; override;
  249. property Bitmap: TCustomBitmap32 read FBitmap write SetBitmap;
  250. // OwnsBitmap: Specifies if the layers owns the bit referenced by
  251. // the Bitmap property. Default: False
  252. property OwnsBitmap: boolean read FOwnsBitmap write FOwnsBitmap;
  253. // SourceRect: The area of the bitmap used to produce the layer bitmap.
  254. // By default the whole bitmap is used, but SourceRect can be used to
  255. // only use a section of it.
  256. property SourceRect: TRect read GetSourceRect write SetSourceRect;
  257. end;
  258. //------------------------------------------------------------------------------
  259. //
  260. // Construct a TPhotoshopDocument from a TCustomImage32
  261. //
  262. //------------------------------------------------------------------------------
  263. // The function produces a PSD where the background is the composite of the
  264. // TCustomImage32 and its layers (i.e. a flattened view of the image) and one
  265. // PSD layer for each bitmap layer in the TCustomImage32.
  266. //
  267. // If the TCustomImage32 does not have layers then the TCustomImage32.Bitmap
  268. // will be exported as the "background" in a PSD with no layers, otherwise the
  269. // Bitmap will be exported as a PSD layer.
  270. //------------------------------------------------------------------------------
  271. procedure CreatePhotoshopDocument(AImage: TCustomImage32; ADocument: TPhotoshopDocument); overload;
  272. //------------------------------------------------------------------------------
  273. //
  274. // Construct a TPhotoshopDocument from a TBitmap32
  275. //
  276. //------------------------------------------------------------------------------
  277. // The function produces a PSD with no layers but with a background based on the
  278. // bitmap.
  279. //------------------------------------------------------------------------------
  280. procedure CreatePhotoshopDocument(ABitmap: TCustomBitmap32; ADocument: TPhotoshopDocument); overload;
  281. //------------------------------------------------------------------------------
  282. //------------------------------------------------------------------------------
  283. //------------------------------------------------------------------------------
  284. implementation
  285. uses
  286. Math,
  287. Types,
  288. GR32_Layers,
  289. GR32_Backends_Generic,
  290. GR32.ImageFormats,
  291. GR32.ImageFormats.PSD.Writer;
  292. const
  293. PsdSignature: AnsiString = '8BPS'#00#01;
  294. PsdSignatureMask: AnsiString = #$ff#$ff#$ff#$ff#$ff#$ff;
  295. //------------------------------------------------------------------------------
  296. //
  297. // TImageFormatAdapterPSD
  298. //
  299. //------------------------------------------------------------------------------
  300. // Implements IImageFormatAdapter for the PSD image format using
  301. // TPhotoshopDocument.
  302. //------------------------------------------------------------------------------
  303. type
  304. TImageFormatAdapterPSD = class(TCustomImageFormatAdapter,
  305. IImageFormatAdapter,
  306. IImageFormatFileInfo,
  307. IImageFormatWriter)
  308. strict protected
  309. // IImageFormatAdapter
  310. function CanAssignFrom(Source: TPersistent): boolean; override;
  311. function AssignFrom(Dest: TCustomBitmap32; Source: TPersistent): boolean; override;
  312. function CanAssignTo(Dest: TPersistent): boolean; override;
  313. function AssignTo(Source: TCustomBitmap32; Dest: TPersistent): boolean; override;
  314. private
  315. // IImageFormatFileInfo
  316. function ImageFormatDescription: string;
  317. function ImageFormatFileTypes: TFileTypes;
  318. private
  319. // IImageFormatWriter
  320. procedure SaveToStream(ASource: TCustomBitmap32; AStream: TStream);
  321. end;
  322. //------------------------------------------------------------------------------
  323. // IImageFormatAdapter
  324. //------------------------------------------------------------------------------
  325. function TImageFormatAdapterPSD.CanAssignFrom(Source: TPersistent): boolean;
  326. begin
  327. Result := False;
  328. end;
  329. function TImageFormatAdapterPSD.AssignFrom(Dest: TCustomBitmap32; Source: TPersistent): boolean;
  330. begin
  331. Result := inherited;
  332. end;
  333. //------------------------------------------------------------------------------
  334. function TImageFormatAdapterPSD.CanAssignTo(Dest: TPersistent): boolean;
  335. begin
  336. Result := (Dest is TPhotoshopDocument);
  337. end;
  338. function TImageFormatAdapterPSD.AssignTo(Source: TCustomBitmap32; Dest: TPersistent): boolean;
  339. begin
  340. if (Dest is TPhotoshopDocument) then
  341. begin
  342. CreatePhotoshopDocument(Source, TPhotoshopDocument(Dest));
  343. Result := True;
  344. end else
  345. Result := inherited;
  346. end;
  347. //------------------------------------------------------------------------------
  348. // IImageFormatFileInfo
  349. //------------------------------------------------------------------------------
  350. function TImageFormatAdapterPSD.ImageFormatFileTypes: TFileTypes;
  351. begin
  352. Result := ['psd'];
  353. end;
  354. resourcestring
  355. sImageFormatPSDName = 'PSD images';
  356. function TImageFormatAdapterPSD.ImageFormatDescription: string;
  357. begin
  358. Result := sImageFormatPSDName;
  359. end;
  360. //------------------------------------------------------------------------------
  361. // IImageFormatWriter
  362. //------------------------------------------------------------------------------
  363. procedure TImageFormatAdapterPSD.SaveToStream(ASource: TCustomBitmap32; AStream: TStream);
  364. var
  365. PSD: TPhotoshopDocument;
  366. begin
  367. PSD := TPhotoshopDocument.Create;
  368. try
  369. CreatePhotoshopDocument(ASource, PSD);
  370. TPhotoshopDocumentWriter.SaveToStream(PSD, AStream);
  371. finally
  372. PSD.Free;
  373. end;
  374. end;
  375. //------------------------------------------------------------------------------
  376. //
  377. // Construct a TPhotoshopDocument from a TCustomImage32
  378. //
  379. //------------------------------------------------------------------------------
  380. type
  381. TBitmapLayerCracker = class(TCustomIndirectBitmapLayer);
  382. resourcestring
  383. sPSDLayerName = 'Layer %d';
  384. procedure CreatePhotoshopDocument(AImage: TCustomImage32; ADocument: TPhotoshopDocument);
  385. var
  386. i: integer;
  387. ImageWidth, ImageHeight: integer;
  388. PSDLayer: TCustomPhotoshopLayer;
  389. SourceLayer: TCustomLayer;
  390. BackgroundBitmap: TBitmap32;
  391. Location: TFloatRect;
  392. LayerBitmap: TCustomBitmap32;
  393. begin
  394. ADocument.Clear;
  395. if (AImage.Bitmap.Empty) and (AImage.Layers.Count = 0) then
  396. Exit;
  397. // Add the main bitmap as a layer
  398. if (not AImage.Bitmap.Empty) then
  399. begin
  400. PSDLayer := ADocument.Layers.Add(TPhotoshopLayer32);
  401. PSDLayer.Opacity := AImage.Bitmap.MasterAlpha;
  402. // Layer just references the bitmap; It doesn't own it.
  403. TPhotoshopLayer32(PSDLayer).Bitmap := AImage.Bitmap;
  404. PSDLayer.Name := Format(sPSDLayerName, [ADocument.Layers.Count]);
  405. end;
  406. for i := 0 to AImage.Layers.Count - 1 do
  407. begin
  408. SourceLayer := AImage.Layers[i];
  409. if not (SourceLayer is TCustomIndirectBitmapLayer) then
  410. continue;
  411. LayerBitmap := TBitmapLayerCracker(SourceLayer).Bitmap;
  412. Location := TBitmapLayerCracker(SourceLayer).Location;
  413. PSDLayer := ADocument.Layers.Add(TPhotoshopLayer32);
  414. PSDLayer.Opacity := LayerBitmap.MasterAlpha;
  415. PSDLayer.Left := Round(Location.Left);
  416. PSDLayer.Top := Round(Location.Top);
  417. // Layer just references the bitmap; It doesn't own it.
  418. TPhotoshopLayer32(PSDLayer).Bitmap := LayerBitmap;
  419. if (not SourceLayer.Visible) then
  420. PSDLayer.Options := PSDLayer.Options + [loHidden];
  421. PSDLayer.Name := Format(sPSDLayerName, [ADocument.Layers.Count]);
  422. end;
  423. BackgroundBitmap := TBitmap32.Create(TMemoryBackend);
  424. try
  425. if (AImage.Bitmap.Empty) then
  426. begin
  427. // The image has no bitmap - Calculate size from the layers instead
  428. ImageWidth := 0;
  429. ImageHeight := 0;
  430. for i := 0 to ADocument.Layers.Count - 1 do
  431. begin
  432. PSDLayer := ADocument.Layers[i];
  433. ImageWidth := Max(ImageWidth, PSDLayer.Left + PSDLayer.Width);
  434. ImageHeight := Max(ImageHeight, PSDLayer.Top + PSDLayer.Height);
  435. end;
  436. if (ImageWidth = 0) and (ImageHeight = 0) then
  437. exit;
  438. BackgroundBitmap.SetSize(ImageWidth, ImageHeight);
  439. end else
  440. BackgroundBitmap.SetSizeFrom(AImage.Bitmap);
  441. // We clear the background with:
  442. //
  443. // $00xxxxxx to make it transparent for those that can handle transparent PSD
  444. //
  445. // $xxFFFFFF to make it white for those that can't handle transparent PSD
  446. //
  447. // If the image contains layers and the reader can handle them then the
  448. // background is ignored; The background is only used when there are no
  449. // layers or if the reader cannot handle layers.
  450. BackgroundBitmap.Clear($00FFFFFF);
  451. // Create flattened bitmap for use as background
  452. AImage.PaintTo(BackgroundBitmap, BackgroundBitmap.BoundsRect);
  453. PSDLayer := TPhotoshopLayer32.Create;
  454. try
  455. TPhotoshopLayer32(PSDLayer).Bitmap := BackgroundBitmap;
  456. // We need to keep the bitmap alive when this function
  457. // returns so transfer ownership to the layer.
  458. TPhotoshopLayer32(PSDLayer).OwnsBitmap := True;
  459. BackgroundBitmap := nil;
  460. ADocument.Background := PSDLayer; // Document now owns the layer
  461. except
  462. PSDLayer.Free;
  463. raise;
  464. end;
  465. finally
  466. BackgroundBitmap.Free;
  467. end;
  468. end;
  469. //------------------------------------------------------------------------------
  470. //
  471. // Construct a TPhotoshopDocument from a TBitmap32
  472. //
  473. //------------------------------------------------------------------------------
  474. procedure CreatePhotoshopDocument(ABitmap: TCustomBitmap32; ADocument: TPhotoshopDocument); overload;
  475. var
  476. PSDLayer: TCustomPhotoshopLayer;
  477. begin
  478. ADocument.Clear;
  479. if ABitmap.Empty then
  480. Exit;
  481. PSDLayer := TPhotoshopLayer32.Create;
  482. try
  483. TPhotoshopLayer32(PSDLayer).Bitmap := ABitmap;
  484. ADocument.Background := PSDLayer; // Document now owns the layer
  485. except
  486. PSDLayer.Free;
  487. raise;
  488. end;
  489. end;
  490. //------------------------------------------------------------------------------
  491. //
  492. // TCustomPhotoshopLayer
  493. //
  494. //------------------------------------------------------------------------------
  495. constructor TCustomPhotoshopLayer.Create(ADocument: TPhotoshopDocument);
  496. begin
  497. inherited Create;
  498. FBlendMode := lbmNormal;
  499. FOpacity := $FF;
  500. FUseDocumentCompression := True;
  501. SetDocument(ADocument);
  502. end;
  503. destructor TCustomPhotoshopLayer.Destroy;
  504. begin
  505. if (FDocument <> nil) and (FDocument.Background = Self) then
  506. FDocument.FBackground := nil; // Do not go through setter
  507. SetDocument(nil);
  508. inherited;
  509. end;
  510. procedure TCustomPhotoshopLayer.BeginScan;
  511. begin
  512. end;
  513. procedure TCustomPhotoshopLayer.EndScan;
  514. begin
  515. end;
  516. function TCustomPhotoshopLayer.GetBoundsRect: TRect;
  517. begin
  518. Result := Rect(Left, Top, Left+Width, Top+Height);
  519. end;
  520. function TCustomPhotoshopLayer.GetCompression: TPSDLayerCompression;
  521. begin
  522. if (FUseDocumentCompression) and (FDocument <> nil) then
  523. Result := FDocument.Compression
  524. else
  525. Result := FCompression;
  526. end;
  527. function TCustomPhotoshopLayer.GetIndex: integer;
  528. begin
  529. if (FDocument <> nil) and (FDocument.Background <> Self) then
  530. Result := FDocument.FLayers.IndexOf(Self)
  531. else
  532. Result := -1;
  533. end;
  534. procedure TCustomPhotoshopLayer.SetBounds(ALeft, ATop, AWidth, AHeight: integer);
  535. begin
  536. Left := ALeft;
  537. Top := ATop;
  538. Width := AWidth;
  539. Height := AHeight;
  540. end;
  541. procedure TCustomPhotoshopLayer.SetBoundsRect(const Value: TRect);
  542. begin
  543. SetBounds(Value.Left, Value.Top, Value.Width, Value.Height);
  544. end;
  545. function TCustomPhotoshopLayer.GetHeight: Integer;
  546. begin
  547. Result := FHeight;
  548. end;
  549. function TCustomPhotoshopLayer.GetWidth: Integer;
  550. begin
  551. Result := FWidth;
  552. end;
  553. procedure TCustomPhotoshopLayer.SetHeight(const Value: Integer);
  554. begin
  555. FHeight := Value;
  556. end;
  557. procedure TCustomPhotoshopLayer.SetWidth(const Value: Integer);
  558. begin
  559. FWidth := Value;
  560. end;
  561. procedure TCustomPhotoshopLayer.SetCompression(const Value: TPSDLayerCompression);
  562. begin
  563. if (Value = lcPredictedZIP) then
  564. raise EPhotoshopDocument.Create('"ZIP with prediction"-compression is not implemented');
  565. FCompression := Value;
  566. FUseDocumentCompression := False;
  567. end;
  568. procedure TCustomPhotoshopLayer.SetDocument(const Value: TPhotoshopDocument);
  569. begin
  570. if (FDocument = Value) then
  571. exit;
  572. if (FDocument <> nil) then
  573. FDocument.RemoveLayer(Self);
  574. FDocument := Value;
  575. if (FDocument <> nil) then
  576. FDocument.AddLayer(Self);
  577. end;
  578. procedure TCustomPhotoshopLayer.SetIndex(const Value: integer);
  579. begin
  580. if (Value <> Index) and (FDocument <> nil) then
  581. begin
  582. if (FDocument.Background = Self) then
  583. raise EPhotoshopDocument.Create('Cannot set the index of the background layer');
  584. FDocument.FLayers.Move(Index, Value);
  585. end;
  586. end;
  587. procedure TCustomPhotoshopLayer.SetUseDocumentCompression(const Value: boolean);
  588. begin
  589. FUseDocumentCompression := Value;
  590. end;
  591. //------------------------------------------------------------------------------
  592. //
  593. // TPhotoshopDocument.TPhotoshopLayers
  594. //
  595. //------------------------------------------------------------------------------
  596. constructor TPhotoshopDocument.TPhotoshopLayers.Create(ADocument: TPhotoshopDocument);
  597. begin
  598. inherited Create;
  599. FDocument := ADocument;
  600. FLayers := TObjectList<TCustomPhotoshopLayer>.Create;
  601. end;
  602. destructor TPhotoshopDocument.TPhotoshopLayers.Destroy;
  603. begin
  604. FLayers.Free;
  605. inherited;
  606. end;
  607. procedure TPhotoshopDocument.TPhotoshopLayers.Clear;
  608. begin
  609. FLayers.Clear;
  610. end;
  611. procedure TPhotoshopDocument.TPhotoshopLayers.AddLayer(ALayer: TCustomPhotoshopLayer);
  612. begin
  613. if (not FLayers.Contains(ALayer)) then
  614. FLayers.Add(ALayer);
  615. end;
  616. procedure TPhotoshopDocument.TPhotoshopLayers.RemoveLayer(ALayer: TCustomPhotoshopLayer);
  617. begin
  618. FLayers.Extract(ALayer);
  619. end;
  620. function TPhotoshopDocument.TPhotoshopLayers.Add(ALayerClass: TPhotoshopLayerClass): TCustomPhotoshopLayer;
  621. begin
  622. if (ALayerClass = nil) then
  623. ALayerClass := FDocument.DefaultLayerClass;
  624. // Layer.Create->Layer.SetDocument->Document.AddLayer->Document.Layers.AddLayer
  625. Result := ALayerClass.Create(FDocument);
  626. end;
  627. function TPhotoshopDocument.TPhotoshopLayers.GetCount: integer;
  628. begin
  629. Result := FLayers.Count;
  630. end;
  631. function TPhotoshopDocument.TPhotoshopLayers.GetLayer(Index: integer): TCustomPhotoshopLayer;
  632. begin
  633. Result := FLayers[Index];
  634. end;
  635. function TPhotoshopDocument.TPhotoshopLayers.IndexOf(ALayer: TCustomPhotoshopLayer): integer;
  636. begin
  637. Result := FLayers.IndexOf(ALayer);
  638. end;
  639. procedure TPhotoshopDocument.TPhotoshopLayers.Move(OldIndex, NewIndex: integer);
  640. begin
  641. FLayers.Move(OldIndex, NewIndex);
  642. end;
  643. //------------------------------------------------------------------------------
  644. //
  645. // TPhotoshopDocument
  646. //
  647. //------------------------------------------------------------------------------
  648. constructor TPhotoshopDocument.Create(ABackground: TCustomPhotoshopLayer);
  649. begin
  650. inherited Create;
  651. FLayers := TPhotoshopLayers.Create(Self);
  652. FCompression := FDefaultCompression;
  653. FBackground := ABackground;
  654. end;
  655. destructor TPhotoshopDocument.Destroy;
  656. begin
  657. FBackground.Free;
  658. FLayers.Free;
  659. inherited;
  660. end;
  661. procedure TPhotoshopDocument.Assign(Source: TPersistent);
  662. begin
  663. if (Source is TCustomImage32) then
  664. CreatePhotoshopDocument(TCustomImage32(Source), Self)
  665. else
  666. inherited;
  667. end;
  668. procedure TPhotoshopDocument.Clear;
  669. begin
  670. SetBackground(nil);
  671. FLayers.Clear;
  672. SetSize(0, 0);
  673. end;
  674. procedure TPhotoshopDocument.AddLayer(ALayer: TCustomPhotoshopLayer);
  675. begin
  676. if (ALayer.Document = Self) and (ALayer <> Background) then
  677. FLayers.AddLayer(ALayer);
  678. end;
  679. procedure TPhotoshopDocument.RemoveLayer(ALayer: TCustomPhotoshopLayer);
  680. begin
  681. if (ALayer.Document = Self) then
  682. begin
  683. if (ALayer <> Background) then
  684. FLayers.RemoveLayer(ALayer)
  685. else
  686. FBackground := nil;
  687. end;
  688. end;
  689. procedure TPhotoshopDocument.SetBackground(const Value: TCustomPhotoshopLayer);
  690. begin
  691. if (FBackground = Value) then
  692. Exit;
  693. FBackground.Free;
  694. FBackground := Value;
  695. if FBackground <> nil then
  696. begin
  697. // In case layer is already in layer list this extracts it...
  698. FBackground.Document := nil;
  699. // ...and reattaches it without adding it to the layer list
  700. FBackground.Document := Self;
  701. FWidth := FBackground.Width;
  702. FHeight := FBackground.Height;
  703. end;
  704. end;
  705. procedure TPhotoshopDocument.SetCompression(const Value: TPSDLayerCompression);
  706. begin
  707. if (Value = lcPredictedZIP) then
  708. raise EPhotoshopDocument.Create('"ZIP with prediction"-compression is not implemented');
  709. FCompression := Value;
  710. end;
  711. procedure TPhotoshopDocument.SetSize(AWidth, AHeight: Integer);
  712. begin
  713. Width := AWidth;
  714. Height := AHeight;
  715. end;
  716. //------------------------------------------------------------------------------
  717. //
  718. // TPhotoshopLayer32
  719. //
  720. //------------------------------------------------------------------------------
  721. destructor TPhotoshopLayer32.Destroy;
  722. begin
  723. if (FOwnsBitmap) then
  724. FBitmap.Free;
  725. inherited;
  726. end;
  727. procedure TPhotoshopLayer32.SetBitmap(const Value: TCustomBitmap32);
  728. begin
  729. if (FOwnsBitmap) and (FBitmap <> nil) then
  730. FBitmap.Free;
  731. FBitmap := Value;
  732. FSourceTop := 0;
  733. FSourceLeft := 0;
  734. if (FBitmap <> nil) then
  735. begin
  736. Height := FBitmap.Height;
  737. Width := FBitmap.Width;
  738. end else
  739. begin
  740. Height := 0;
  741. Width := 0;
  742. end;
  743. end;
  744. procedure TPhotoshopLayer32.GetChannelScanLine(AChannel: TColor32Component; ALine: integer; var Bytes);
  745. var
  746. Count: integer;
  747. pDest: PByte;
  748. pSource: PByte;
  749. begin
  750. if (Width = 0) or (Height = 0) then
  751. Exit;
  752. if (Bitmap = nil) then
  753. begin
  754. FillChar(Bytes, Width, $FF);
  755. Exit;
  756. end;
  757. pDest := @Bytes;
  758. pSource := @(PColor32Entry(Bitmap.ScanLine[ALine + FSourceTop]).Components[AChannel]);
  759. Inc(pSource, FSourceLeft * SizeOf(TColor32));
  760. Count := Width;
  761. while (Count > 0) do
  762. begin
  763. pDest^ := pSource^;
  764. Inc(pDest);
  765. Inc(pSource, SizeOf(TColor32));
  766. Dec(Count);
  767. end;
  768. end;
  769. function TPhotoshopLayer32.GetSourceRect: TRect;
  770. begin
  771. if (FBitmap <> nil) then
  772. begin
  773. Result.Top := Min(FSourceTop, FBitmap.Height);
  774. Result.Left := Min(FSourceLeft, FBitmap.Width);
  775. end else
  776. begin
  777. Result.Top := 0;
  778. Result.Left := 0;
  779. end;
  780. Result.Width := Width;
  781. Result.Height := Height;
  782. end;
  783. function TPhotoshopLayer32.GetHeight: Integer;
  784. begin
  785. // Size of bitmap can have changed since assignment
  786. // so we need to reevaluate the size.
  787. if (FBitmap <> nil) then
  788. Result := Min(inherited GetHeight, Max(0, FBitmap.Height - FSourceTop))
  789. else
  790. Result := 0;
  791. end;
  792. function TPhotoshopLayer32.GetWidth: Integer;
  793. begin
  794. // Size of bitmap can have changed since assignment
  795. // so we need to reevaluate the size.
  796. if (FBitmap <> nil) then
  797. Result := Min(inherited GetWidth, Max(0, FBitmap.Width - FSourceLeft))
  798. else
  799. Result := 0;
  800. end;
  801. procedure TPhotoshopLayer32.SetSourceRect(const Value: TRect);
  802. var
  803. SourceRect: TRect;
  804. begin
  805. if (FBitmap <> nil) then
  806. GR32.IntersectRect(SourceRect, Value, FBitmap.BoundsRect)
  807. else
  808. begin
  809. SourceRect.Top := Max(0, Value.Top);
  810. SourceRect.Left := Max(0, Value.Left);
  811. SourceRect.Bottom := Max(SourceRect.Top, Value.Top);
  812. SourceRect.Right := Max(SourceRect.Left, Value.Left);
  813. end;
  814. FSourceTop := SourceRect.Top;
  815. FSourceLeft := SourceRect.Left;
  816. Width := SourceRect.Width;
  817. Height := SourceRect.Height;
  818. end;
  819. //------------------------------------------------------------------------------
  820. //------------------------------------------------------------------------------
  821. //------------------------------------------------------------------------------
  822. var
  823. ImageFormatHandle: integer = 0;
  824. initialization
  825. TPhotoshopDocument.DefaultLayerClass := TPhotoshopLayer32;
  826. TPhotoshopDocument.DefaultCompression := lcRLE;
  827. ImageFormatHandle := ImageFormatManager.RegisterImageFormat(TImageFormatAdapterPSD.Create, ImageFormatPriorityNormal);
  828. finalization
  829. ImageFormatManager.UnregisterImageFormat(ImageFormatHandle);
  830. end.