| 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 |
|
|---|