2 {plugwashes png write code}
\r
4 { Copyright (C) 2005 Bas Steendijk and Peter Green
\r
5 For conditions of distribution and use, see copyright notice in zlib_license.txt
\r
6 which is included in the package
\r
7 ----------------------------------------------------------------------------- }
\r
13 zlib,zdeflate,zutil,classes,sysutils,pgtypes,
\r
23 tcolortype=(ctgreyscale,ctpallette,ctbgr,ctrgb,ct8bp);
\r
25 // There are two types of color types
\r
27 // native types result in direct output of the apps data to the png file
\r
29 // ctgreyscale : grey scale data all png supported depths should work
\r
30 // ctpallette : palletted data all png supported depths should work
\r
31 // ctrgb : currently 24 bit only
\r
33 // Processed types are processed before data is output to the png file
\r
34 // ctbgr : truecolor data in bgr order usefull with tbitmap.
\r
35 // ct8bp : data from app is 8 bits per sample palletted but data is saved as
\r
36 // a lower depth palletted format (the app MUST only use the first
\r
37 // 2^bitdepth pallette entries
\r
40 destination : tstream;
\r
43 deflatestream : z_stream;
\r
44 chunkstart : integer; //used to fill in chunk length later
\r
45 chunkbytes : integer;
\r
47 crc_table : array[byte] of uint32;
\r
48 colortype:tcolortype;
\r
50 bufferingchunkdata : boolean;
\r
51 bufferedchunkdata : tstringlist;
\r
54 procedure pngstart(var f : tpngwrite;destination : tstream;colordepth : integer;colortype:tcolortype;lines : integer;cols : integer);
\r
55 procedure pngwritescanline(var f : tpngwrite;scanline: pointer);
\r
58 procedure pngwritepal(var f : tpngwrite;p:pointer;entrys : integer);
\r
59 procedure pngstartdata(var f : tpngwrite);
\r
60 procedure pngfinishdata(var f : tpngwrite);
\r
61 procedure pngfinish(var f : tpngwrite);
\r
68 procedure make_crc_table(var f:tpngwrite);
\r
77 // for (n = 0; n < 256; n++)
\r
78 for n := 0 to 255 do begin
\r
79 // c = (unsigned long) n;
\r
81 // for (k = 0; k < 8; k++)
\r
82 for k := 0 to 7 do begin
\r
84 if (c and 1) <> 0 then begin
\r
85 // c = 0xedb88320L ^ (c >> 1);
\r
86 c := $edb88320 xor (c shr 1);
\r
94 // crc_table[n] = c;
\r
98 // crc_table_computed = 1;
\r
102 unsigned long update_crc(unsigned long crc, unsigned char *buf,
\r
105 unsigned long c = crc;
\r
108 if (!crc_table_computed)
\r
110 for (n = 0; n < len; n++) {
\r
111 c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8);
\r
116 procedure update_crc(var f : tpngwrite;buf : string);
\r
122 for n := 1 to length(buf) do begin
\r
123 chunkcrc := crc_table[(chunkcrc xor byte(buf[n])) and $ff] xor (chunkcrc shr 8);
\r
128 procedure writetochunk(var f : tpngwrite;buf : string);
\r
130 if length(buf)=0 then raise exception.create('attempt to write zero length block to chunk');
\r
133 if bufferingchunkdata then begin
\r
134 bufferedchunkdata.Add(buf);
\r
136 destination.Write(buf[1],length(buf));
\r
139 inc(chunkbytes,length(buf));
\r
144 procedure startchunk(var f : tpngwrite;chunktype : string);
\r
146 outputdata : string;
\r
150 chunkstart := destination.Position;
\r
151 // 4 btye dummy for length we will insert this later.
\r
153 destination.WriteBuffer(pchar(outputdata)^,4);
\r
155 bufferingchunkdata := true;
\r
156 bufferedchunkdata := tstringlist.create;
\r
159 chunkcrc := $FFFFFFFF;
\r
161 writetochunk(f,chunktype);
\r
170 procedure stopchunk(var f:tpngwrite);
\r
172 currentpos : integer;
\r
176 if not bufferingchunkdata then begin
\r
178 currentpos := destination.Position;
\r
179 destination.position := chunkstart;
\r
181 chunkbytes := htonl(chunkbytes);
\r
182 destination.WriteBuffer(chunkbytes,4);
\r
183 if bufferingchunkdata then begin
\r
184 // writeln('begin write of buffered chunk data');
\r
185 for i := 0 to bufferedchunkdata.Count-1 do begin
\r
186 destination.Write(bufferedchunkdata[i][1],length(bufferedchunkdata[i]));
\r
188 bufferedchunkdata.Free;
\r
189 // writeln('end write of buffered chunk data');
\r
191 destination.position := currentpos;
\r
193 chunkcrc := htonl(chunkcrc) ;
\r
194 chunkcrc := chunkcrc xor $FFFFFFFF;
\r
195 destination.WriteBuffer(chunkcrc,4);
\r
201 tihdr=packed record
\r
206 Compressionmethod : byte;
\r
207 Filtermethod : byte;
\r
208 Interlacemethod : byte;
\r
211 procedure pngstart(var f : tpngwrite;destination : tstream;colordepth : integer;colortype:tcolortype;lines : integer;cols : integer);
\r
213 tempstring : string;
\r
216 fillchar(f,sizeof(f),#0);
\r
217 f.destination := destination;
\r
220 f.colortype := colortype;
\r
221 f.colordepth := colordepth;
\r
225 destination.Write(pchar(#137'PNG'#13#10#26#10)^,8);
\r
227 startchunk(f,'IHDR');
\r
228 setlength(tempstring,sizeof(tihdr));
\r
229 ihdr := pihdr(tempstring);
\r
230 ihdr.width := htonl(cols);
\r
231 ihdr.height := htonl(lines);
\r
233 ctgreyscale : begin
\r
234 ihdr.Bitdepth := colordepth;
\r
235 ihdr.Colortype := 0;
\r
237 ctpallette,ct8bp : begin
\r
238 ihdr.Bitdepth := colordepth;
\r
239 ihdr.Colortype := 3;
\r
242 ctrgb,ctbgr : begin
\r
243 ihdr.Bitdepth := 8;
\r
244 ihdr.Colortype := 2;
\r
248 ihdr.Compressionmethod := 0;
\r
249 ihdr.Filtermethod := 0;
\r
250 ihdr.Interlacemethod := 0;
\r
251 writetochunk(f,tempstring);
\r
257 procedure pngwritepal(var f : tpngwrite;p:pointer;entrys : integer);
\r
259 tempstring : string;
\r
262 startchunk(f,'PLTE');
\r
263 setlength(tempstring,entrys*3);
\r
264 move(p^,tempstring[1],entrys*3);
\r
265 writetochunk(f,tempstring);
\r
270 procedure pngstartdata(var f : tpngwrite);
\r
273 startchunk(f,'IDAT');
\r
274 deflateInit(deflatestream,9);
\r
283 tlinedata = array[0..0] of byte;
\r
284 plinedata = ^tlinedata;
\r
285 function divup(a,b:integer):integer;
\r
287 result := (a div b)+ord((a mod b)<>0);
\r
291 procedure pngwritescanline(var f : tpngwrite;scanline: pointer);
\r
293 outputstr : string;
\r
295 scanlineb : plinedata absolute scanline;
\r
296 rearrangebuf : plinedata;
\r
298 pixelsperbyte : integer;
\r
299 samplevalue : byte;
\r
302 //writeln('about to write starter byte');
\r
303 if cols > 127 then begin
\r
304 setlength(outputstr,cols*6);
\r
306 setlength(outputstr,768);
\r
308 deflatestream.next_out := pbytef(outputstr);
\r
309 deflatestream.avail_out := length(outputstr);
\r
310 // writeln(deflatestream.avail_out,' ',deflatestream.avail_in);
\r
312 deflatestream.next_in := @nullchar;
\r
313 deflatestream.avail_in :=1;
\r
314 //writeln('about to start deflation');
\r
315 deflate(deflatestream,0);
\r
316 //writeln('deflation complete deflatestream.avail_out=',deflatestream.avail_out);
\r
317 // writeln(deflatestream.avail_out,' ',deflatestream.avail_in);
\r
318 while deflatestream.avail_out = 0 do begin
\r
319 deflatestream.next_out := pbytef(taddrint(deflatestream.next_out) - taddrint(outputstr));
\r
320 deflatestream.avail_out := length(outputstr);
\r
321 setlength(outputstr,length(outputstr)*2);
\r
322 deflatestream.next_out := pbytef(taddrint(deflatestream.next_out) + taddrint(outputstr));
\r
323 deflate(deflatestream,0);
\r
325 //writeln('extra deflation done');
\r
326 if deflatestream.avail_in <> 0 then raise exception.create('compression problem');
\r
327 //writeln('about to write main data');
\r
331 getmem(rearrangebuf,cols*3);
\r
333 for counter := 0 to cols-1 do begin
\r
334 rearrangebuf[(counter*3) ] := scanlineb[(counter*3)+2];
\r
335 rearrangebuf[(counter*3)+1] := scanlineb[(counter*3)+1];
\r
336 rearrangebuf[(counter*3)+2] := scanlineb[(counter*3) ];
\r
338 deflatestream.next_in := pointer(rearrangebuf);
\r
339 deflatestream.avail_in := cols*3;
\r
340 deflate(deflatestream,0);
\r
341 if deflatestream.avail_in <> 0 then raise exception.create('compression problem');
\r
342 freemem(rearrangebuf);
\r
346 rearrangebuf := allocmem(divup(cols* colordepth, 8));
\r
347 pixelsperbyte := 8 div colordepth;
\r
348 for counter := 0 to cols-1 do begin
\r
349 //shift sample value into most significant bits (which has the nice side effect of removing any garbage bits)
\r
350 samplevalue := scanlineb[counter] shl (8-colordepth);
\r
351 //shift sample value into correct place for pixel in question.
\r
352 samplevalue := samplevalue shr ((counter mod pixelsperbyte)*colordepth);
\r
353 rearrangebuf[counter div pixelsperbyte] := rearrangebuf[counter div pixelsperbyte] or samplevalue;
\r
355 deflatestream.next_in := pointer(rearrangebuf);
\r
356 deflatestream.avail_in := divup(cols* colordepth, 8);
\r
357 deflate(deflatestream,0);
\r
358 if deflatestream.avail_in <> 0 then raise exception.create('compression problem');
\r
359 freemem(rearrangebuf);
\r
363 deflatestream.next_in := scanline;
\r
365 deflatestream.avail_in := divup(cols* colordepth, 8);
\r
367 deflate(deflatestream,0);
\r
368 while deflatestream.avail_out = 0 do begin
\r
369 deflatestream.next_out := pbytef(taddrint(deflatestream.next_out) - taddrint(outputstr));
\r
370 deflatestream.avail_out := length(outputstr);
\r
371 setlength(outputstr,length(outputstr)*2);
\r
372 deflatestream.next_out := pbytef(taddrint(deflatestream.next_out) + taddrint(outputstr));
\r
373 deflate(deflatestream,0);
\r
375 if deflatestream.avail_in <> 0 then raise exception.create('compression problem');
\r
379 setlength(outputstr,length(outputstr)-deflatestream.avail_out);
\r
380 //deflate doesn't always produce output when its given input, if this is the case no point pushing it any further
\r
381 if length(outputstr) > 0 then writetochunk(f,outputstr);
\r
386 procedure pngfinishdata(var f : tpngwrite);
\r
388 outputstr : string;
\r
389 deflateresult : integer;
\r
393 setlength(outputstr,512);
\r
394 deflatestream.next_out := pbytef(outputstr);
\r
395 deflatestream.avail_out := length(outputstr);
\r
396 deflateresult := deflate(deflatestream,Z_FINISH);
\r
397 setlength(outputstr,length(outputstr)-deflatestream.avail_out);
\r
398 writetochunk(f,outputstr);
\r
400 until deflateresult=Z_STREAM_END;
\r
401 deflateEnd(deflatestream);
\r
408 procedure pngfinish(var f : tpngwrite);
\r
412 //setlength(outputstr,512);
\r
413 startchunk(f,'IEND');
\r