1 | unit BGRAStreamLayers;
|
---|
2 |
|
---|
3 | {$mode objfpc}{$H+}
|
---|
4 | {$MODESWITCH ADVANCEDRECORDS}
|
---|
5 |
|
---|
6 | interface
|
---|
7 |
|
---|
8 | uses
|
---|
9 | Classes, SysUtils, BGRALayers, BGRABitmap, BGRALzpCommon;
|
---|
10 |
|
---|
11 | function CheckStreamForLayers(AStream: TStream): boolean;
|
---|
12 | function LoadLayersFromStream(AStream: TStream; out ASelectedLayerIndex: integer; ALoadLayerUniqueIds: boolean = false;
|
---|
13 | ADestination: TBGRALayeredBitmap = nil): TBGRALayeredBitmap;
|
---|
14 | procedure SaveLayersToStream(AStream: TStream; ALayers: TBGRACustomLayeredBitmap; ASelectedLayerIndex: integer; ACompression: TLzpCompression = lzpZStream);
|
---|
15 | procedure SaveLayerBitmapToStream(AStream: TStream; ABitmap: TBGRABitmap; ACaption: string; ACompression: TLzpCompression = lzpZStream);
|
---|
16 | function LoadLayerBitmapFromStream(AStream: TStream; ACompression: TLzpCompression = lzpZStream) : TBGRABitmap;
|
---|
17 | procedure RegisterStreamLayers;
|
---|
18 |
|
---|
19 | implementation
|
---|
20 |
|
---|
21 | uses BGRABitmapTypes, BGRACompressableBitmap, zstream, BGRAReadLzp, BGRAWriteLzp,
|
---|
22 | BGRAUTF8, Math;
|
---|
23 |
|
---|
24 | type
|
---|
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 |
|
---|
41 | procedure TLayerHeader.FixEndian;
|
---|
42 | begin
|
---|
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]));
|
---|
59 | end;
|
---|
60 |
|
---|
61 | procedure SaveLayeredBitmapToStream(AStream: TStream; ALayers: TBGRACustomLayeredBitmap);
|
---|
62 | begin
|
---|
63 | SaveLayersToStream(AStream,ALayers,-1);
|
---|
64 | end;
|
---|
65 |
|
---|
66 | procedure LoadLayeredBitmapFromStream(AStream: TStream; ALayers: TBGRACustomLayeredBitmap);
|
---|
67 | var selectedIndex: integer;
|
---|
68 | begin
|
---|
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);
|
---|
75 | end;
|
---|
76 |
|
---|
77 | const
|
---|
78 | StreamHeader = 'TBGRALayeredBitmap'#26#0;
|
---|
79 | StreamMaxLayerCount = 4096;
|
---|
80 | StreamMaxHeaderSize = 256;
|
---|
81 |
|
---|
82 | function CheckStreamForLayers(AStream: TStream): boolean;
|
---|
83 | var
|
---|
84 | OldPosition: Int64;
|
---|
85 | HeaderFound: string;
|
---|
86 | begin
|
---|
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;
|
---|
101 | end;
|
---|
102 |
|
---|
103 | function LoadLayersFromStream(AStream: TStream; out ASelectedLayerIndex: integer; ALoadLayerUniqueIds: boolean = false;
|
---|
104 | ADestination: TBGRALayeredBitmap = nil): TBGRALayeredBitmap;
|
---|
105 | var
|
---|
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;
|
---|
120 | begin
|
---|
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;
|
---|
230 | end;
|
---|
231 |
|
---|
232 | procedure SaveLayersToStream(AStream: TStream; ALayers: TBGRACustomLayeredBitmap; ASelectedLayerIndex: integer; ACompression: TLzpCompression);
|
---|
233 | var
|
---|
234 | StackOption: longint;
|
---|
235 | i: integer;
|
---|
236 | DirectoryOffsetPos, EndPos: int64;
|
---|
237 | LayerHeaderPosition: int64;
|
---|
238 | LayerBitmapPosition,BitmapSize, startPos: int64;
|
---|
239 | bitmap: TBGRABitmap;
|
---|
240 | h: TLayerHeader;
|
---|
241 | begin
|
---|
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;
|
---|
310 | end;
|
---|
311 |
|
---|
312 | procedure SaveLayerBitmapToStream(AStream: TStream; ABitmap: TBGRABitmap; ACaption: string; ACompression: TLzpCompression);
|
---|
313 | var Compressed: TBGRACompressableBitmap;
|
---|
314 | begin
|
---|
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);
|
---|
324 | end;
|
---|
325 |
|
---|
326 | function LoadLayerBitmapFromStream(AStream: TStream; ACompression: TLzpCompression): TBGRABitmap;
|
---|
327 | var Compressed: TBGRACompressableBitmap;
|
---|
328 | captionFound: string;
|
---|
329 | begin
|
---|
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;
|
---|
342 | end;
|
---|
343 |
|
---|
344 | procedure RegisterStreamLayers;
|
---|
345 | begin
|
---|
346 | LayeredBitmapSaveToStreamProc := @SaveLayeredBitmapToStream;
|
---|
347 | LayeredBitmapLoadFromStreamProc := @LoadLayeredBitmapFromStream;
|
---|
348 | LayeredBitmapCheckStreamProc := @CheckStreamForLayers;
|
---|
349 | end;
|
---|
350 |
|
---|
351 | initialization
|
---|
352 |
|
---|
353 | RegisterStreamLayers;
|
---|
354 |
|
---|
355 | end.
|
---|
356 |
|
---|