source: trunk/Packages/bgrabitmap/bgracompressablebitmap.pas

Last change on this file was 2, checked in by chronos, 5 years ago
File size: 9.4 KB
Line 
1unit BGRACompressableBitmap;
2
3{$mode objfpc}{$H+}
4
5interface
6
7{ This unit contains the TBGRACompressableBitmap class, which
8 can be used to temporarily compress bitmaps in memory.
9 To use it, create an instance with the bitmap you want
10 to compress. You can then free the original bitmap because
11 TBGRACompressableBitmap contains all information necessary
12 to build it again. To construct again your bitmap, call
13 the GetBitmap function.
14
15 When you have your bitmap in TBGRACompressableBitmap,
16 you can call Compress function as many times as necessary
17 until all data is compressed. It does only a part of the
18 work at each call, so you can put it in a loop or in
19 a timer. When it's done, Compress returns false to
20 notify that it did nothing, which means you can
21 stop calling Compress.
22
23 In this implementation, the memory usage grows during
24 the compression process and is lower only after it is
25 finished. So it is recommended to compress one bitmap
26 at a time. }
27
28uses
29 Classes, SysUtils, BGRABitmapTypes, BGRABitmap, zstream;
30
31type
32
33 { TBGRACompressableBitmap }
34
35 TBGRACompressableBitmap = class
36 private
37 FWidth,FHeight: integer;
38 FCaption: String;
39 FBounds: TRect;
40 FCompressedDataArray: array of TMemoryStream;
41 FUncompressedData: TMemoryStream;
42 FLineOrder: TRawImageLineOrder;
43 FCompressionProgress: Int64;
44 procedure Decompress;
45 procedure FreeData;
46 procedure Init;
47 public
48 CompressionLevel: Tcompressionlevel;
49 constructor Create; overload;
50 constructor Create(Source: TBGRABitmap); overload;
51 function GetBitmap: TBGRABitmap;
52
53 //call Compress as many times as necessary
54 //when it returns false, it means that
55 //the image compression is finished
56 function Compress: boolean;
57 procedure WriteToStream(AStream: TStream);
58 procedure ReadFromStream(AStream: TStream);
59
60 function UsedMemory: Int64;
61 procedure Assign(Source: TBGRABitmap);
62 destructor Destroy; override;
63 property Width : Integer read FWidth;
64 property Height: Integer read FHeight;
65 property Caption : string read FCaption write FCaption;
66
67 end;
68
69implementation
70
71uses BGRAUTF8;
72
73// size of each chunk treated by Compress function
74const maxPartSize = 524288;
75
76{ TBGRACompressedBitmap }
77
78constructor TBGRACompressableBitmap.Create;
79begin
80 Init;
81end;
82
83constructor TBGRACompressableBitmap.Create(Source: TBGRABitmap);
84begin
85 Init;
86 Assign(Source);
87end;
88
89{ Constructs the bitmap again, decompressing if necessary.
90 After this, the image is not compressed anymore so the
91 memoy usage grows again and the access becomes fast
92 because there is no need to decompress anymore. }
93function TBGRACompressableBitmap.GetBitmap: TBGRABitmap;
94var UsedPart: TBGRABitmap;
95 UsedNbPixels: Integer;
96begin
97 Decompress;
98 if FUncompressedData = nil then
99 begin
100 result := nil;
101 exit;
102 end;
103 result := TBGRABitmap.Create(FWidth,FHeight);
104 result.Caption := FCaption;
105 FUncompressedData.Position := 0;
106 if (FBounds.Left <> 0) or (FBounds.Top <> 0)
107 or (FBounds.Right <> FWidth) or (FBounds.Bottom <> FHeight) then
108 begin
109 UsedNbPixels := (FBounds.Right-FBounds.Left)*(FBounds.Bottom-FBounds.Top);
110 if UsedNbPixels > 0 then
111 begin
112 UsedPart := TBGRABitmap.Create(FBounds.Right-FBounds.Left,FBounds.Bottom-FBounds.Top);
113 FUncompressedData.Read(UsedPart.Data^,UsedPart.NbPixels*Sizeof(TBGRAPixel));
114 if UsedPart.LineOrder <> FLineOrder then UsedPart.VerticalFlip;
115 If TBGRAPixel_RGBAOrder then UsedPart.SwapRedBlue;
116 result.PutImage(FBounds.Left,FBounds.Top,UsedPart,dmSet);
117 UsedPart.Free;
118 end;
119 end else
120 begin
121 FUncompressedData.Read(result.Data^,result.NbPixels*Sizeof(TBGRAPixel));
122 If TBGRAPixel_RGBAOrder then result.SwapRedBlue;
123 end;
124end;
125
126{ Returns the total memory used by this object for storing bitmap data }
127function TBGRACompressableBitmap.UsedMemory: Int64;
128var i: integer;
129begin
130 result := 0;
131 for i := 0 to high(FCompressedDataArray) do
132 result += FCompressedDataArray[i].Size;
133 if FUncompressedData <> nil then result += FUncompressedData.Size;
134end;
135
136{ Do one compress step or return false }
137function TBGRACompressableBitmap.Compress: boolean;
138var comp: Tcompressionstream;
139 partSize: integer;
140begin
141 if FCompressedDataArray = nil then FCompressionProgress := 0;
142 if (FUncompressedData = nil) or (FUncompressedData.Size = 0) then
143 begin
144 result := false;
145 exit;
146 end;
147 if FCompressionProgress < FUncompressedData.Size then
148 begin
149 setlength(FCompressedDataArray, length(FCompressedDataArray)+1);
150 FCompressedDataArray[high(FCompressedDataArray)] := TMemoryStream.Create;
151 FUncompressedData.Position := FCompressionProgress;
152 if FUncompressedData.Size - FCompressionProgress > maxPartSize then
153 partSize := maxPartSize else
154 partSize := integer(FUncompressedData.Size - FCompressionProgress);
155
156 comp := Tcompressionstream.Create(CompressionLevel,FCompressedDataArray[high(FCompressedDataArray)],true);
157 LEWriteLongint(comp, partSize);
158 comp.CopyFrom(FUncompressedData,partSize);
159 comp.Free;
160 inc(FCompressionProgress, partSize);
161 end;
162 if FCompressionProgress >= FUncompressedData.Size then
163 FreeAndNil(FUncompressedData);
164 result := true;
165end;
166
167procedure TBGRACompressableBitmap.WriteToStream(AStream: TStream);
168var i:integer;
169begin
170 repeat
171 until not Compress;
172 LEWriteLongint(AStream,FWidth);
173 LEWriteLongint(AStream,FHeight);
174 LEWriteLongint(AStream,length(FCaption));
175 AStream.Write(FCaption[1],length(FCaption));
176 if (FWidth=0) or (FHeight = 0) then exit;
177
178 LEWriteLongint(AStream,FBounds.Left);
179 LEWriteLongint(AStream,FBounds.Top);
180 LEWriteLongint(AStream,FBounds.Right);
181 LEWriteLongint(AStream,FBounds.Bottom);
182 LEWriteLongint(AStream,ord(FLineOrder));
183
184 LEWriteLongint(AStream,length(FCompressedDataArray));
185 for i := 0 to high(FCompressedDataArray) do
186 begin
187 LEWriteLongint(AStream,FCompressedDataArray[i].Size);
188 FCompressedDataArray[i].Position := 0;
189 AStream.CopyFrom(FCompressedDataArray[i],FCompressedDataArray[i].Size);
190 end;
191end;
192
193procedure TBGRACompressableBitmap.ReadFromStream(AStream: TStream);
194var size,i: integer;
195begin
196 FreeData;
197 FWidth := LEReadLongint(AStream);
198 FHeight := LEReadLongint(AStream);
199 setlength(FCaption,LEReadLongint(AStream));
200 AStream.Read(FCaption[1],length(FCaption));
201 if (FWidth=0) or (FHeight = 0) then
202 begin
203 FUncompressedData := TMemoryStream.Create;
204 exit;
205 end;
206
207 FBounds.Left := LEReadLongint(AStream);
208 FBounds.Top := LEReadLongint(AStream);
209 FBounds.Right := LEReadLongint(AStream);
210 FBounds.Bottom := LEReadLongint(AStream);
211 FLineOrder := TRawImageLineOrder(LEReadLongint(AStream));
212
213 setlength(FCompressedDataArray,LEReadLongint(AStream));
214 for i := 0 to high(FCompressedDataArray) do
215 begin
216 size := LEReadLongint(AStream);
217 FCompressedDataArray[i] := TMemoryStream.Create;
218 FCompressedDataArray[i].CopyFrom(AStream,size);
219 end;
220
221 if FCompressedDataArray = nil then
222 FUncompressedData := TMemoryStream.Create;
223end;
224
225procedure TBGRACompressableBitmap.Decompress;
226var decomp: Tdecompressionstream;
227 i: integer;
228 partSize: integer;
229begin
230 if (FUncompressedData <> nil) or (FCompressedDataArray = nil) then exit;
231 FUncompressedData := TMemoryStream.Create;
232 for i := 0 to high(FCompressedDataArray) do
233 begin
234 FCompressedDataArray[i].Position := 0;
235 decomp := Tdecompressionstream.Create(FCompressedDataArray[i],true);
236 partSize := LEReadLongint(decomp);
237 FUncompressedData.CopyFrom(decomp,partSize);
238 decomp.Free;
239 FreeAndNil(FCompressedDataArray[i]);
240 end;
241 FCompressedDataArray := nil;
242end;
243
244{ Free all data }
245procedure TBGRACompressableBitmap.FreeData;
246var i: integer;
247begin
248 if FCompressedDataArray <> nil then
249 begin
250 for i := 0 to high(FCompressedDataArray) do
251 FCompressedDataArray[I].Free;
252 FCompressedDataArray := nil;
253 end;
254 if FUncompressedData <> nil then FreeAndNil(FUncompressedData);
255end;
256
257procedure TBGRACompressableBitmap.Init;
258begin
259 FUncompressedData := nil;
260 FCompressedDataArray := nil;
261 FWidth := 0;
262 FHeight := 0;
263 FCaption := '';
264 FCompressionProgress := 0;
265 CompressionLevel := clfastest;
266end;
267
268{ Copy a bitmap into this object. As it is copied, you need not
269 keep a copy of the source }
270procedure TBGRACompressableBitmap.Assign(Source: TBGRABitmap);
271var
272 UsedPart: TBGRABitmap;
273 NbUsedPixels: integer;
274begin
275 FreeData;
276 if Source = nil then
277 begin
278 FWidth := 0;
279 FHeight := 0;
280 FCaption := '';
281 exit;
282 end;
283 FWidth := Source.Width;
284 FHeight := Source.Height;
285 FCaption := Source.Caption;
286 FBounds := Source.GetImageBounds([cRed,cGreen,cBlue,cAlpha]);
287 NbUsedPixels := (FBounds.Right-FBounds.Left)*(FBounds.Bottom-FBounds.Top);
288 FUncompressedData := TMemoryStream.Create;
289 if NbUsedPixels = 0 then exit;
290
291 if (FBounds.Left <> 0) or (FBounds.Top <> 0)
292 or (FBounds.Right <> Source.Width) or (FBounds.Bottom <> Source.Height) then
293 begin
294 UsedPart := Source.GetPart(FBounds) as TBGRABitmap;
295 If TBGRAPixel_RGBAOrder then UsedPart.SwapRedBlue;
296 FUncompressedData.Write(UsedPart.Data^,NbUsedPixels*Sizeof(TBGRAPixel));
297 FLineOrder := UsedPart.LineOrder;
298 UsedPart.Free;
299 end else
300 begin
301 If TBGRAPixel_RGBAOrder then Source.SwapRedBlue;
302 FUncompressedData.Write(Source.Data^,Source.NbPixels*Sizeof(TBGRAPixel));
303 If TBGRAPixel_RGBAOrder then Source.SwapRedBlue;
304 FLineOrder := Source.LineOrder;
305 end;
306end;
307
308destructor TBGRACompressableBitmap.Destroy;
309begin
310 FreeData;
311 inherited Destroy;
312end;
313
314end.
315
Note: See TracBrowser for help on using the repository browser.