Unit WrPPM; { wrppm.c Copyright (C) 1991-1996, Thomas G. Lane. This file is part of the Independent JPEG Group's software. For conditions of distribution and use, see the accompanying README file. This file contains routines to write output images in PPM/PGM format. The extended 2-byte-per-sample raw PPM/PGM formats are supported. The PBMPLUS library is NOT required to compile this software (but it is highly useful as a set of PPM image manipulation programs). These routines may need modification for non-Unix environments or specialized applications. As they stand, they assume output to an ordinary stdio stream. } interface {$I jconfig.inc} uses jdeferr, jmorecfg, jerror, jpeglib, jinclude, jdmaster, cdjpeg; { Common decls for cjpeg/djpeg applications } {GLOBAL} function jinit_write_ppm (cinfo : j_decompress_ptr) : djpeg_dest_ptr; implementation { For 12-bit JPEG data, we either downscale the values to 8 bits (to write standard byte-per-sample PPM/PGM files), or output nonstandard word-per-sample PPM/PGM files. Downscaling is done if PPM_NORAWWORD is defined (this can be done in the Makefile or in jconfig.h). (When the core library supports data precision reduction, a cleaner implementation will be to ask for that instead.) } type CharPtr = ^char; {$ifdef BITS_IN_JSAMPLE_IS_8} procedure PUTPPMSAMPLE(var ptr : CharPtr; v : byte); begin ptr^ := char(v); Inc(ptr); end; const BYTESPERSAMPLE = 1; PPM_MAXVAL = 255; {$else} {$ifdef PPM_NORAWWORD} procedure PUTPPMSAMPLE(var ptr : CharPtr; v : byte); begin ptr^ := char (v shr (BITS_IN_JSAMPLE-8)); Inc(ptr); end; const BYTESPERSAMPLE = 1; PPM_MAXVAL = 255; {$else} { The word-per-sample format always puts the LSB first. } procedure PUTPPMSAMPLE(var ptr : CharPtr; v : int); var {register} val_ : int; begin val_ := v; ptr^ := char (val_ and $FF); Inc(ptr); ptr^ := char ((val_ shr 8) and $FF); Inc(ptr); end; const BYTESPERSAMPLE = 2; PPM_MAXVAL = (1 shl BITS_IN_JSAMPLE)-1; {$endif} {$endif} { When JSAMPLE is the same size as char, we can just fwrite() the decompressed data to the PPM or PGM file. On PCs, in order to make this work the output buffer must be allocated in near data space, because we are assuming small-data memory model wherein fwrite() can't reach far memory. If you need to process very wide images on a PC, you might have to compile in large-memory model, or else replace fwrite() with a putc() loop --- which will be much slower. } { Private version of data destination object } type ppm_dest_ptr = ^ppm_dest_struct; ppm_dest_struct = record pub : djpeg_dest_struct; { public fields } { Usually these two pointers point to the same place: } iobuffer : CharPtr; { fwrite's I/O buffer } pixrow : JSAMPROW; { decompressor output buffer } buffer_width : size_t; { width of I/O buffer } samples_per_row : JDIMENSION; { JSAMPLEs per output row } end; { Write some pixel data. In this module rows_supplied will always be 1. put_pixel_rows handles the "normal" 8-bit case where the decompressor output buffer is physically the same as the fwrite buffer. } {METHODDEF} procedure put_pixel_rows (cinfo : j_decompress_ptr; dinfo : djpeg_dest_ptr; rows_supplied : JDIMENSION); far; var dest : ppm_dest_ptr; begin dest := ppm_dest_ptr(dinfo); {void} JFWRITE(dest^.pub.output_file, dest^.iobuffer, dest^.buffer_width); end; { This code is used when we have to copy the data and apply a pixel format translation. Typically this only happens in 12-bit mode. } {METHODDEF} procedure copy_pixel_rows (cinfo : j_decompress_ptr; dinfo : djpeg_dest_ptr; rows_supplied : JDIMENSION); far; var dest : ppm_dest_ptr; {register} bufferptr : CharPtr; {register} ptr : JSAMPLE_PTR; {register} col : JDIMENSION; begin dest := ppm_dest_ptr(dinfo); ptr := JSAMPLE_PTR(dest^.pub.buffer^[0]); bufferptr := dest^.iobuffer; for col := pred(dest^.samples_per_row) downto 0 do begin PUTPPMSAMPLE(bufferptr, GETJSAMPLE(ptr^)); Inc(ptr); end; {void} JFWRITE(dest^.pub.output_file, dest^.iobuffer, dest^.buffer_width); end; { Write some pixel data when color quantization is in effect. We have to demap the color index values to straight data. } {METHODDEF} procedure put_demapped_rgb (cinfo : j_decompress_ptr; dinfo : djpeg_dest_ptr; rows_supplied : JDIMENSION); far; var dest : ppm_dest_ptr; {register} bufferptr : CharPtr; {register} ptr : JSAMPLE_PTR; {register} col : JDIMENSION; {register} pixval : int; {register} color_map0 : JSAMPROW; {register} color_map1 : JSAMPROW; {register} color_map2 : JSAMPROW; begin dest := ppm_dest_ptr(dinfo); ptr := JSAMPLE_PTR(dest^.pub.buffer^[0]); bufferptr := dest^.iobuffer; color_map0 := cinfo^.colormap^[0]; color_map1 := cinfo^.colormap^[1]; color_map2 := cinfo^.colormap^[2]; for col := pred(cinfo^.output_width) downto 0 do begin pixval := GETJSAMPLE(ptr^); Inc(ptr); PUTPPMSAMPLE(bufferptr, GETJSAMPLE(color_map0^[pixval])); PUTPPMSAMPLE(bufferptr, GETJSAMPLE(color_map1^[pixval])); PUTPPMSAMPLE(bufferptr, GETJSAMPLE(color_map2^[pixval])); end; {void} JFWRITE(dest^.pub.output_file, dest^.iobuffer, dest^.buffer_width); end; {METHODDEF} procedure put_demapped_gray (cinfo : j_decompress_ptr; dinfo : djpeg_dest_ptr; rows_supplied : JDIMENSION); far; var dest : ppm_dest_ptr; {register} bufferptr : CharPtr; {register} ptr : JSAMPLE_PTR; {register} color_map : JSAMPROW; {register} col : JDIMENSION; begin dest := ppm_dest_ptr(dinfo); color_map := cinfo^.colormap^[0]; ptr := JSAMPLE_PTR(dest^.pub.buffer^[0]); bufferptr := dest^.iobuffer; for col := pred(cinfo^.output_width) downto 0 do begin PUTPPMSAMPLE(bufferptr, GETJSAMPLE(color_map^[GETJSAMPLE(ptr^)])); Inc(ptr); end; {void} JFWRITE(dest^.pub.output_file, dest^.iobuffer, dest^.buffer_width); end; { Startup: write the file header. } {METHODDEF} procedure start_output_ppm (cinfo : j_decompress_ptr; dinfo : djpeg_dest_ptr); far; const LF = #10; var dest : ppm_dest_ptr; var header : string[200]; function LongToStr(l : long) : string; var helpstr : string[20]; begin Str(l, helpstr); LongToStr := helpstr; end; begin dest := ppm_dest_ptr(dinfo); { Emit file header } case (cinfo^.out_color_space) of JCS_GRAYSCALE: begin { emit header for raw PGM format } header := 'P5'+LF+LongToStr(cinfo^.output_width)+' '+ LongToStr(cinfo^.output_height)+LF+ LongToStr(Long(PPM_MAXVAL)) + LF; JFWRITE(dest^.pub.output_file, @header[1], Length(header)); end; JCS_RGB: begin { emit header for raw PPM format } header := 'P6'+LF+LongToStr(cinfo^.output_width)+' '+ LongToStr(cinfo^.output_height)+LF+ LongToStr(Long(PPM_MAXVAL)) + LF; JFWRITE(dest^.pub.output_file, @header[1], Length(header)); end; else ERREXIT(j_common_ptr(cinfo), JERR_PPM_COLORSPACE); end; end; { Finish up at the end of the file. } {METHODDEF} procedure finish_output_ppm (cinfo : j_decompress_ptr; dinfo : djpeg_dest_ptr); far; begin { Make sure we wrote the output file OK } {Flush(dinfo^.output_file^);} if (IOresult <> 0) then ERREXIT(j_common_ptr(cinfo), JERR_FILE_WRITE); end; { The module selection routine for PPM format output. } {GLOBAL} function jinit_write_ppm (cinfo : j_decompress_ptr) : djpeg_dest_ptr; var dest : ppm_dest_ptr; begin { Create module interface object, fill in method pointers } dest := ppm_dest_ptr ( cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, SIZEOF(ppm_dest_struct)) ); dest^.pub.start_output := start_output_ppm; dest^.pub.finish_output := finish_output_ppm; { Calculate output image dimensions so we can allocate space } jpeg_calc_output_dimensions(cinfo); { Create physical I/O buffer. Note we make this near on a PC. } dest^.samples_per_row := cinfo^.output_width * cinfo^.out_color_components; dest^.buffer_width := dest^.samples_per_row * (BYTESPERSAMPLE * SIZEOF(char)); dest^.iobuffer := CharPtr( cinfo^.mem^.alloc_small (j_common_ptr(cinfo), JPOOL_IMAGE, dest^.buffer_width) ); if (cinfo^.quantize_colors) or (BITS_IN_JSAMPLE <> 8) or (SIZEOF(JSAMPLE) <> SIZEOF(char)) then begin { When quantizing, we need an output buffer for colormap indexes that's separate from the physical I/O buffer. We also need a separate buffer if pixel format translation must take place. } dest^.pub.buffer := cinfo^.mem^.alloc_sarray (j_common_ptr(cinfo), JPOOL_IMAGE, cinfo^.output_width * cinfo^.output_components, JDIMENSION(1)); dest^.pub.buffer_height := 1; if (not cinfo^.quantize_colors) then dest^.pub.put_pixel_rows := copy_pixel_rows else if (cinfo^.out_color_space = JCS_GRAYSCALE) then dest^.pub.put_pixel_rows := put_demapped_gray else dest^.pub.put_pixel_rows := put_demapped_rgb; end else begin { We will fwrite() directly from decompressor output buffer. } { Synthesize a JSAMPARRAY pointer structure } { Cast here implies near^.far pointer conversion on PCs } dest^.pixrow := JSAMPROW(dest^.iobuffer); dest^.pub.buffer := JSAMPARRAY (@dest^.pixrow); dest^.pub.buffer_height := 1; dest^.pub.put_pixel_rows := put_pixel_rows; end; jinit_write_ppm := djpeg_dest_ptr(dest); end; end.