source: trunk/Packages/bgrabitmap/bgrawritebmpmiomap.pas

Last change on this file was 2, checked in by chronos, 5 years ago
File size: 10.0 KB
Line 
1unit BGRAWriteBmpMioMap;
2
3{$mode objfpc}{$H+}
4
5interface
6
7uses
8 Classes, SysUtils, FPimage, BGRABitmapTypes, BGRAReadBmpMioMap;
9
10type
11
12 { TBGRAWriterBmpMioMap }
13
14 TBGRAWriterBmpMioMap = class (TFPCustomImageWriter)
15 protected
16 FHeader: TMioHeader;
17 FPalette: packed array of record
18 ColorValue: Word;
19 AlphaValue: Byte;
20 Padding: Byte;
21 end;
22 FPaletteIndexes: packed array[0..65535] of NativeInt;
23 FPaletteOffset: NativeInt;
24 FPaletteAlpha: boolean;
25 FChunks: array of TMemoryStream;
26 FCurrentChunk: TMemoryStream;
27 FMaxChunkSize: Word;
28 function IndexOfColor(const AColor: TBGRAPixel): NativeInt;
29 procedure InitHeader(Img: TFPCustomImage);
30 procedure InitPalette;
31 procedure InitChunks;
32 procedure FlushChunk;
33 procedure FreeChunks;
34 procedure NeedChunk;
35 procedure AppendToChunks(const Buffer; Count: integer);
36 procedure BuildPaletteAndChunks(Img: TFPCustomImage);
37 procedure WriteHeader(Str: TStream);
38 procedure WritePalette(Str: TStream);
39 procedure WriteChunks(Str: TStream);
40 procedure ReadScanline(Img: TFPCustomImage; Y: integer; ADest: PBGRAPixel);
41 procedure InternalWrite(Str: TStream; Img: TFPCustomImage); override;
42 public
43 constructor Create; override;
44 property MaxChunkSize: Word read FMaxChunkSize write FMaxChunkSize;
45 end;
46
47implementation
48
49{ TBGRAWriterBmpMioMap }
50
51function TBGRAWriterBmpMioMap.IndexOfColor(const AColor: TBGRAPixel): NativeInt;
52var searchedColorValue: Word;
53 searchedAlphaValue: Byte;
54 i,startSearch,endSearch: NativeInt;
55begin
56 searchedColorValue:= BGRAToMioMap(AColor);
57 searchedAlphaValue:= AlphaToMioMap(AColor.alpha);
58 if length(FPalette)>0 then
59 begin
60 with FPalette[0] do
61 begin
62 if (ColorValue = searchedColorValue) and
63 (AlphaValue = searchedAlphaValue) then
64 begin
65 result := 0;
66 exit;
67 end;
68 end;
69 end;
70
71 startSearch:= FPaletteOffset+1;
72 endSearch:= FPaletteOffset+$FC;
73 if endSearch >= FHeader.nbColors then
74 endSearch:= FHeader.nbColors-1;
75 for i := startSearch to endSearch do
76 with FPalette[i] do
77 begin
78 if (ColorValue = searchedColorValue)
79 and (AlphaValue = searchedAlphaValue) then
80 begin
81 result := i;
82 exit;
83 end;
84 end;
85
86 result := FPaletteIndexes[searchedColorValue];
87 if (result <> -1) and (FPalette[result].AlphaValue <> searchedAlphaValue) then
88 result := -1;
89
90 if result = -1 then
91 begin
92 if fheader.nbColors = 65535 then
93 raise exception.Create('Too many colors');
94 result := fheader.nbColors;
95 inc(FHeader.nbColors);
96 if length(FPalette) <= result then
97 setlength(FPalette, length(FPalette)*2 + 128);
98 with FPalette[result] do
99 begin
100 ColorValue := searchedColorValue;
101 AlphaValue := searchedAlphaValue;
102 end;
103 FPaletteIndexes[searchedColorValue] := result;
104 if (searchedAlphaValue > 0) and (searchedAlphaValue < 32) then
105 FPaletteAlpha := true;
106 end;
107end;
108
109procedure TBGRAWriterBmpMioMap.InitHeader(Img: TFPCustomImage);
110begin
111 if (Img.Width > 65535) or (Img.Height > 65535) then
112 raise exception.Create('Image too big to be saved in Bmp MioMap format');
113 FHeader.magic := MioMapMagicValue;
114 fheader.format:= 0;
115 FHeader.width := Img.Width;
116 FHeader.height := img.Height;
117 FHeader.nbColors := 0;
118 FHeader.nbChunks := 0;
119end;
120
121procedure TBGRAWriterBmpMioMap.InitPalette;
122var i: NativeInt;
123begin
124 for i := 0 to high(FPaletteIndexes) do
125 FPaletteIndexes[i] := -1;
126 FPaletteOffset := 0;
127 FPaletteAlpha := false;
128 IndexOfColor(BGRAPixelTransparent); //define transparent color as zero
129end;
130
131procedure TBGRAWriterBmpMioMap.InitChunks;
132begin
133 FCurrentChunk := nil;
134end;
135
136procedure TBGRAWriterBmpMioMap.FlushChunk;
137begin
138 if FCurrentChunk <> nil then
139 begin
140 setlength(FChunks, length(FChunks)+1);
141 FChunks[high(FChunks)] := FCurrentChunk;
142 FCurrentChunk := nil;
143 FHeader.nbChunks += 1;
144 end;
145end;
146
147procedure TBGRAWriterBmpMioMap.FreeChunks;
148var
149 i: Integer;
150begin
151 FreeAndNil(FCurrentChunk);
152 for i := 0 to high(FChunks) do
153 FChunks[i].Free;
154 FChunks := nil;
155end;
156
157procedure TBGRAWriterBmpMioMap.NeedChunk;
158begin
159 if FCurrentChunk = nil then
160 begin
161 if FHeader.nbChunks = 65535 then
162 raise exception.Create('Too many chunks');
163 FCurrentChunk := TMemoryStream.Create;
164 end;
165end;
166
167procedure TBGRAWriterBmpMioMap.AppendToChunks(const Buffer; Count: integer);
168begin
169 if Count > 65535 then
170 raise exception.Create('Buffer too big');
171 NeedChunk;
172 if FCurrentChunk.Size + Count > MaxChunkSize then
173 begin
174 FlushChunk;
175 NeedChunk;
176 end;
177 FCurrentChunk.WriteBuffer(Buffer,Count);
178end;
179
180procedure TBGRAWriterBmpMioMap.BuildPaletteAndChunks(Img: TFPCustomImage);
181var y,w: NativeInt;
182 PData,PDataEnd: PBGRAPixel;
183 p: PBGRAPixel;
184 currentColorIndex,
185 nextColorIndex,
186 repCount: NativeInt;
187 b: byte;
188 changeOfsRec: packed record
189 valFD: byte;
190 valLo: byte;
191 valHi: byte;
192 end;
193 repRec: packed record
194 valFE: byte;
195 relativeColorIndex: byte;
196 count: byte;
197 end;
198 repZeroRec: packed record
199 valFF: byte;
200 count: byte;
201 end;
202
203begin
204 w := Img.Width;
205 getmem(PData, w*sizeof(TBGRAPixel));
206 try
207 PDataEnd := PData+w;
208 for y := 0 to Img.Height-1 do
209 begin
210 ReadScanline(Img,Y,PData);
211 p := PData;
212 while p < PDataEnd do
213 begin
214 currentColorIndex:= IndexOfColor(p^);
215 nextColorIndex := currentColorIndex;
216 repCount:= 1;
217 inc(p);
218 while p < PDataEnd do
219 begin
220 nextColorIndex:= IndexOfColor(p^);
221 if nextColorIndex = currentColorIndex then
222 begin
223 inc(p);
224 inc(repCount);
225 if repCount = 255 then break;
226 end
227 else
228 break;
229 end;
230 if currentColorIndex = 0 then
231 begin
232 if repCount = 1 then
233 begin
234 b := 0;
235 AppendToChunks(b,1);
236 end else
237 begin
238 repZeroRec.valFF := $ff;
239 repZeroRec.count := repCount;
240 AppendToChunks(repZeroRec, sizeof(repZeroRec));
241 end;
242 end else
243 begin
244 if (currentColorIndex < FPaletteOffset+1)
245 or (currentColorIndex > FPaletteOffset+$FC) then
246 begin
247 if (abs(nextColorIndex-currentColorIndex) < $FC) then
248 begin
249 FPaletteOffset := (nextColorIndex+currentColorIndex) div 2 - 126;
250 end else
251 FPaletteOffset := currentColorIndex-126;
252 if FPaletteOffset < 0 then FPaletteOffset := 0;
253 changeOfsRec.valFD := $fd;
254 changeOfsRec.valLo := FPaletteOffset and 255;
255 changeOfsRec.valHi := FPaletteOffset shr 8;
256 AppendToChunks(changeOfsRec,sizeof(changeOfsRec));
257 end;
258 if (currentColorIndex < FPaletteOffset+1)
259 or (currentColorIndex > FPaletteOffset+$FC) then
260 raise exception.Create('Index out of range');
261 if repCount = 1 then
262 begin
263 b := currentColorIndex-FPaletteOffset;
264 AppendToChunks(b,1);
265 end else
266 if repCount = 2 then
267 begin
268 b := currentColorIndex-FPaletteOffset;
269 AppendToChunks(b,1);
270 AppendToChunks(b,1);
271 end else
272 begin
273 repRec.valFE:= $FE;
274 repRec.count := repCount;
275 repRec.relativeColorIndex := currentColorIndex-FPaletteOffset;
276 AppendToChunks(repRec, sizeof(repRec));
277 end;
278 end;
279 end;
280 FlushChunk;
281 end;
282 finally
283 freemem(PData);
284 end;
285end;
286
287procedure TBGRAWriterBmpMioMap.WriteChunks(Str: TStream);
288var
289 bigChunkDef: packed record
290 val255: byte;
291 valHi: byte;
292 valLo: byte;
293 end;
294 i: NativeInt;
295begin
296 for i := 0 to high(FChunks) do
297 begin
298 if FChunks[i].Size > 254 then
299 begin
300 bigChunkDef.val255 := 255;
301 bigChunkDef.valHi := FChunks[i].Size shr 8;
302 bigChunkDef.valLo := FChunks[i].Size and 255;
303 Str.WriteBuffer(bigChunkDef, sizeof(bigChunkDef));
304 end else
305 Str.WriteByte(FChunks[i].Size);
306 end;
307 for i := 0 to high(FChunks) do
308 begin
309 FChunks[i].Position := 0;
310 if Str.CopyFrom(FChunks[i],FChunks[i].Size) <> FChunks[i].Size then
311 raise exception.Create('Unable to write chunk');
312 end;
313end;
314
315procedure TBGRAWriterBmpMioMap.WriteHeader(Str: TStream);
316var header: TMioHeader;
317begin
318 if FPaletteAlpha then FHeader.format := 1;
319 FlushChunk;
320
321 header := FHeader;
322 header.format:= NtoLE(header.format);
323 header.width:= NtoLE(header.width);
324 header.height:= NtoLE(header.height);
325 header.nbColors:= NtoLE(header.nbColors);
326 header.nbChunks:= NtoLE(header.nbChunks);
327 Str.WriteBuffer(header, sizeof(header));
328end;
329
330procedure TBGRAWriterBmpMioMap.WritePalette(Str: TStream);
331var
332 colors: packed array of Word;
333 alphas: packed array of byte;
334 i: NativeInt;
335begin
336 setlength(Colors, FHeader.nbColors);
337 for i := 0 to FHeader.nbColors-1 do
338 colors[i] := NtoLE(FPalette[i].ColorValue);
339 Str.WriteBuffer(colors[0], length(Colors)*sizeof(word));
340 if FPaletteAlpha then
341 begin
342 setlength(alphas, FHeader.nbColors);
343 for i := 0 to FHeader.nbColors-1 do
344 alphas[i] := FPalette[i].AlphaValue;
345 Str.WriteBuffer(alphas[0], length(alphas)*sizeof(byte));
346 end;
347end;
348
349procedure TBGRAWriterBmpMioMap.ReadScanline(Img: TFPCustomImage; Y: integer;
350 ADest: PBGRAPixel);
351var
352 i: NativeInt;
353begin
354 if Img is TBGRACustomBitmap then
355 Move(TBGRACustomBitmap(Img).ScanLine[Y]^, ADest^, Img.Width*sizeof(TBGRAPixel))
356 else
357 begin
358 for i := 0 to Img.Width-1 do
359 (ADest+i)^ := FPColorToBGRA(Img.Colors[y,i]);
360 end;
361end;
362
363procedure TBGRAWriterBmpMioMap.InternalWrite(Str: TStream; Img: TFPCustomImage);
364begin
365 try
366 InitHeader(Img);
367 InitPalette;
368 InitChunks;
369 BuildPaletteAndChunks(Img);
370 WriteHeader(Str);
371 WritePalette(Str);
372 WriteChunks(Str);
373 finally
374 FreeChunks;
375 end;
376end;
377
378constructor TBGRAWriterBmpMioMap.Create;
379begin
380 inherited Create;
381 MaxChunkSize := 254;
382end;
383
384initialization
385
386 DefaultBGRAImageWriter[ifBmpMioMap] := TBGRAWriterBmpMioMap;
387
388end.
389
Note: See TracBrowser for help on using the repository browser.