source: trunk/Packages/bgrabitmap/bgrastreamlayers.pas

Last change on this file was 2, checked in by chronos, 5 years ago
File size: 11.4 KB
Line 
1unit BGRAStreamLayers;
2
3{$mode objfpc}{$H+}
4{$MODESWITCH ADVANCEDRECORDS}
5
6interface
7
8uses
9 Classes, SysUtils, BGRALayers, BGRABitmap, BGRALzpCommon;
10
11function CheckStreamForLayers(AStream: TStream): boolean;
12function LoadLayersFromStream(AStream: TStream; out ASelectedLayerIndex: integer; ALoadLayerUniqueIds: boolean = false;
13 ADestination: TBGRALayeredBitmap = nil): TBGRALayeredBitmap;
14procedure SaveLayersToStream(AStream: TStream; ALayers: TBGRACustomLayeredBitmap; ASelectedLayerIndex: integer; ACompression: TLzpCompression = lzpZStream);
15procedure SaveLayerBitmapToStream(AStream: TStream; ABitmap: TBGRABitmap; ACaption: string; ACompression: TLzpCompression = lzpZStream);
16function LoadLayerBitmapFromStream(AStream: TStream; ACompression: TLzpCompression = lzpZStream) : TBGRABitmap;
17procedure RegisterStreamLayers;
18
19implementation
20
21uses BGRABitmapTypes, BGRACompressableBitmap, zstream, BGRAReadLzp, BGRAWriteLzp,
22 BGRAUTF8, Math;
23
24type
25 PLayerHeader = ^TLayerHeader;
26
27 { TLayerHeader }
28
29 TLayerHeader = packed record
30 LayerOption, BlendOp,
31 LayerOfsX, LayerOfsY,
32 LayerUniqueId, LayerOpacity: Longint;
33 LayerBitmapSize: int64;
34 OriginalGuid: TGuid;
35 OriginalMatrix: TAffineMatrix;
36 procedure FixEndian;
37 end;
38
39{ TLayerHeader }
40
41procedure TLayerHeader.FixEndian;
42begin
43 LayerOption := NtoLE(LayerOption);
44 BlendOp := NtoLE(BlendOp);
45 LayerOfsX := NtoLE(LayerOfsX);
46 LayerOfsY := NtoLE(LayerOfsY);
47 LayerUniqueId := NtoLE(LayerUniqueId);
48 LayerOpacity := NtoLE(LayerOpacity);
49 LayerBitmapSize := NtoLE(LayerBitmapSize);
50 OriginalGuid.D1 := NtoBE(OriginalGuid.D1);
51 OriginalGuid.D2 := NtoBE(OriginalGuid.D2);
52 OriginalGuid.D3 := NtoBE(OriginalGuid.D3);
53 DWord(OriginalMatrix[1,1]) := NtoLE(DWord(OriginalMatrix[1,1]));
54 DWord(OriginalMatrix[2,1]) := NtoLE(DWord(OriginalMatrix[2,1]));
55 DWord(OriginalMatrix[1,2]) := NtoLE(DWord(OriginalMatrix[1,2]));
56 DWord(OriginalMatrix[2,2]) := NtoLE(DWord(OriginalMatrix[2,2]));
57 DWord(OriginalMatrix[1,3]) := NtoLE(DWord(OriginalMatrix[1,3]));
58 DWord(OriginalMatrix[2,3]) := NtoLE(DWord(OriginalMatrix[2,3]));
59end;
60
61procedure SaveLayeredBitmapToStream(AStream: TStream; ALayers: TBGRACustomLayeredBitmap);
62begin
63 SaveLayersToStream(AStream,ALayers,-1);
64end;
65
66procedure LoadLayeredBitmapFromStream(AStream: TStream; ALayers: TBGRACustomLayeredBitmap);
67var selectedIndex: integer;
68begin
69 if not CheckStreamForLayers(AStream) then
70 begin
71 if Assigned(ALayers) then ALayers.Clear;
72 end
73 else
74 LoadLayersFromStream(AStream,selectedIndex,false,ALayers as TBGRALayeredBitmap);
75end;
76
77const
78 StreamHeader = 'TBGRALayeredBitmap'#26#0;
79 StreamMaxLayerCount = 4096;
80 StreamMaxHeaderSize = 256;
81
82function CheckStreamForLayers(AStream: TStream): boolean;
83var
84 OldPosition: Int64;
85 HeaderFound: string;
86begin
87 result := false;
88 OldPosition:= AStream.Position;
89 try
90 SetLength(HeaderFound, length(StreamHeader));
91 SetLength(HeaderFound, AStream.Read(HeaderFound[1], length(HeaderFound)));
92 if HeaderFound = StreamHeader then
93 result := true;
94 except
95 on ex: exception do
96 begin
97 //nothing
98 end;
99 end;
100 AStream.Position:= OldPosition;
101end;
102
103function LoadLayersFromStream(AStream: TStream; out ASelectedLayerIndex: integer; ALoadLayerUniqueIds: boolean = false;
104 ADestination: TBGRALayeredBitmap = nil): TBGRALayeredBitmap;
105var
106 OldPosition: Int64;
107 HeaderFound: string;
108 NbLayers, canvasWidth, canvasHeight: LongInt;
109 HeaderSize, LayerHeaderSize: LongInt;
110 LayerStackStartPosition, LayerHeaderPosition,
111 LayerBitmapPosition, LayerEndPosition, MemDirPos: Int64;
112 StackOption: LongInt;
113 Layer: TBGRABitmap;
114 i,LayerIndex: integer;
115 LayerName: string;
116 Compression: TLzpCompression;
117 LayerBlendOp: TBlendOperation;
118 LayerIdFound: boolean;
119 h: TLayerHeader;
120begin
121 if Assigned(ADestination) then
122 begin
123 result := ADestination;
124 result.Clear;
125 end else
126 result := TBGRALayeredBitmap.Create;
127 OldPosition:= AStream.Position;
128 SetLength(HeaderFound, length(StreamHeader));
129 try
130 //format identifier
131 SetLength(HeaderFound, AStream.Read(HeaderFound[1], length(HeaderFound)));
132 if HeaderFound <> StreamHeader then
133 raise exception.Create('Invalid header');
134
135 //header size
136 HeaderSize:= LEReadLongint(AStream);
137 if (HeaderSize < 12) or (HeaderSize > StreamMaxHeaderSize) then
138 raise exception.Create('Invalid header size');
139 LayerStackStartPosition := AStream.Position + HeaderSize;
140
141 NbLayers:= LEReadLongint(AStream);
142 if (NbLayers < 0) or (NbLayers > StreamMaxLayerCount) then
143 raise exception.Create('Invalid layer count');
144
145 ASelectedLayerIndex:= LEReadLongint(AStream);
146 if (ASelectedLayerIndex < -1) or (ASelectedLayerIndex >= NbLayers) then
147 raise exception.Create('Selected layer out of bounds');
148
149 StackOption := LEReadLongint(AStream);
150 result.LinearBlend := (StackOption and 1) = 1;
151 if (StackOption and 2) = 2 then Compression := lzpRLE else Compression:= lzpZStream;
152
153 if headerSize >= 20 then
154 begin
155 canvasWidth := LEReadLongint(AStream);
156 canvasHeight := LEReadLongint(AStream);
157 result.SetSize(canvasWidth,canvasHeight);
158 end;
159
160 if headerSize >= 28 then
161 begin
162 MemDirPos := LEReadInt64(AStream);
163 end else MemDirPos := 0;
164 //end of header
165
166 if MemDirPos <> 0 then
167 begin
168 AStream.Position:= MemDirPos+OldPosition;
169 result.MemDirectory.LoadFromStream(AStream);
170 end else
171 result.MemDirectory.Clear;
172
173 AStream.Position:= LayerStackStartPosition;
174 for i := 0 to NbLayers-1 do
175 begin
176 LayerHeaderSize:= LEReadLongint(AStream);
177
178 LayerHeaderPosition := AStream.Position;
179 LayerBitmapPosition := LayerHeaderPosition + LayerHeaderSize;
180 LayerEndPosition := -1;
181
182 fillchar({%H-}h, sizeof(h), 0);
183 h.LayerOption := 1; //visible
184 h.BlendOp:= integer(result.DefaultBlendingOperation);
185 h.LayerOpacity := 65535; //opaque
186 h.LayerUniqueId:= maxLongint;
187 h.FixEndian;
188
189 AStream.ReadBuffer(h, min(LayerHeaderSize, sizeof(h)));
190 h.FixEndian;
191
192 if h.BlendOp > ord(high(TBlendOperation)) then
193 LayerBlendOp := result.DefaultBlendingOperation
194 else
195 LayerBlendOp:= TBlendOperation(h.BlendOp);
196
197 LayerIdFound := h.LayerUniqueId <> maxLongint;
198
199 if h.LayerBitmapSize > 0 then
200 LayerEndPosition:= LayerBitmapPosition+h.LayerBitmapSize;
201
202 AStream.Position:= LayerBitmapPosition;
203 Layer := LoadLayerBitmapFromStream(AStream, Compression);
204 LayerName := Layer.Caption;
205 LayerIndex := result.AddOwnedLayer(Layer);
206 Layer := nil;
207
208 result.LayerName[LayerIndex] := LayerName;
209 result.LayerVisible[LayerIndex] := (h.LayerOption and 1) = 1;
210 result.BlendOperation[LayerIndex]:= LayerBlendOp;
211 result.LayerOffset[LayerIndex] := Point(h.LayerOfsX,h.LayerOfsY);
212 if ALoadLayerUniqueIds and LayerIdFound then
213 result.LayerUniqueId[LayerIndex] := h.LayerUniqueId;
214 result.LayerOpacity[LayerIndex] := h.LayerOpacity shr 8;
215 result.LayerOriginalGuid[LayerIndex] := h.OriginalGuid;
216 result.LayerOriginalMatrix[LayerIndex] := h.OriginalMatrix;
217 result.LayerOriginalRenderStatus[layerIndex] := orsProof;
218
219 if LayerEndPosition <> -1 then AStream.Position := LayerEndPosition;
220 end;
221 result.NotifyLoaded;
222 except
223 on ex: Exception do
224 begin
225 AStream.Position := OldPosition;
226 if not Assigned(ADestination) then result.Free;
227 raise ex;
228 end;
229 end;
230end;
231
232procedure SaveLayersToStream(AStream: TStream; ALayers: TBGRACustomLayeredBitmap; ASelectedLayerIndex: integer; ACompression: TLzpCompression);
233var
234 StackOption: longint;
235 i: integer;
236 DirectoryOffsetPos, EndPos: int64;
237 LayerHeaderPosition: int64;
238 LayerBitmapPosition,BitmapSize, startPos: int64;
239 bitmap: TBGRABitmap;
240 h: TLayerHeader;
241begin
242 if (ASelectedLayerIndex < -1) or (ASelectedLayerIndex >= ALayers.NbLayers) then
243 raise exception.Create('Selected layer out of bounds');
244
245 ALayers.NotifySaving;
246
247 startPos := AStream.Position;
248 AStream.Write(StreamHeader[1], length(StreamHeader));
249 LEWriteLongint(AStream, 28); //header size
250 LEWriteLongint(AStream, ALayers.NbLayers);
251 LEWriteLongint(AStream, ASelectedLayerIndex);
252 StackOption := 0;
253 if ALayers.LinearBlend then StackOption := StackOption or 1;
254 if ACompression = lzpRLE then StackOption:= StackOption or 2;
255 LEWriteLongint(AStream, StackOption);
256 LEWriteLongint(AStream, ALayers.Width);
257 LEWriteLongint(AStream, ALayers.Height);
258 DirectoryOffsetPos := AStream.Position;
259 LEWriteInt64(AStream, 0);
260 //end of header
261
262 for i := 0 to ALayers.NbLayers-1 do
263 begin
264 LEWriteLongint(AStream, sizeof(h));
265 LayerHeaderPosition := AStream.Position;
266
267 bitmap := ALayers.GetLayerBitmapDirectly(i); //do it before to ensure update from original
268
269 h.LayerOption:= 0;
270 if ALayers.LayerVisible[i] then h.LayerOption:= h.LayerOption or 1;
271 h.BlendOp:= Longint(ALayers.BlendOperation[i]);
272 h.LayerOfsX:= ALayers.LayerOffset[i].x;
273 h.LayerOfsY:= ALayers.LayerOffset[i].y;
274 h.LayerUniqueId:= ALayers.LayerUniqueId[i];
275 h.LayerOpacity:= integer(ALayers.LayerOpacity[i])*$101;
276 h.LayerBitmapSize := 0;
277 h.OriginalGuid := ALayers.LayerOriginalGuid[i];
278 h.OriginalMatrix := ALayers.LayerOriginalMatrix[i];
279 h.FixEndian;
280 AStream.WriteBuffer(h, sizeof(h));
281 //end of layer header
282
283 LayerBitmapPosition:=AStream.Position;
284 if bitmap <> nil then
285 SaveLayerBitmapToStream(AStream, bitmap, ALayers.LayerName[i], ACompression)
286 else
287 begin
288 bitmap := ALayers.GetLayerBitmapCopy(i);
289 SaveLayerBitmapToStream(AStream, bitmap, ALayers.LayerName[i], ACompression);
290 bitmap.free;
291 end;
292
293 BitmapSize := AStream.Position - LayerBitmapPosition;
294
295 //store back the bitmap size
296 AStream.Position:= LayerHeaderPosition + (PByte(@PLayerHeader(nil)^.LayerBitmapSize)-PByte(nil));
297 LEWriteInt64(AStream, BitmapSize);
298
299 AStream.Position:= LayerBitmapPosition+BitmapSize;
300 end;
301
302 EndPos:= AStream.Position;
303 if ALayers.HasMemFiles then
304 begin
305 AStream.Position := DirectoryOffsetPos;
306 LEWriteInt64(AStream,EndPos-startPos);
307 AStream.Position:= EndPos;
308 ALayers.MemDirectory.SaveToStream(AStream);
309 end;
310end;
311
312procedure SaveLayerBitmapToStream(AStream: TStream; ABitmap: TBGRABitmap; ACaption: string; ACompression: TLzpCompression);
313var Compressed: TBGRACompressableBitmap;
314begin
315 if ACompression = lzpZStream then
316 begin
317 Compressed := TBGRACompressableBitmap.Create(ABitmap);
318 Compressed.Caption := ACaption;
319 Compressed.CompressionLevel:= cldefault;
320 Compressed.WriteToStream(AStream);
321 Compressed.Free;
322 end else
323 TBGRAWriterLazPaint.WriteRLEImage(AStream, ABitmap, ACaption);
324end;
325
326function LoadLayerBitmapFromStream(AStream: TStream; ACompression: TLzpCompression): TBGRABitmap;
327var Compressed: TBGRACompressableBitmap;
328 captionFound: string;
329begin
330 if ACompression = lzpZStream then
331 begin
332 Compressed := TBGRACompressableBitmap.Create;
333 Compressed.ReadFromStream(AStream);
334 result := Compressed.GetBitmap;
335 Compressed.Free;
336 end else
337 begin
338 result := TBGRABitmap.Create;
339 TBGRAReaderLazPaint.LoadRLEImage(AStream, result, captionFound);
340 result.Caption := captionFound;
341 end;
342end;
343
344procedure RegisterStreamLayers;
345begin
346 LayeredBitmapSaveToStreamProc := @SaveLayeredBitmapToStream;
347 LayeredBitmapLoadFromStreamProc := @LoadLayeredBitmapFromStream;
348 LayeredBitmapCheckStreamProc := @CheckStreamForLayers;
349end;
350
351initialization
352
353 RegisterStreamLayers;
354
355end.
356
Note: See TracBrowser for help on using the repository browser.