3194 lines
53 KiB
C
3194 lines
53 KiB
C
|
/**
|
||
|
|
||
|
** $Header: /roq/libim/impcx.c 1 11/02/99 4:38p Zaphod $
|
||
|
|
||
|
** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC)
|
||
|
|
||
|
** a division of General Atomics, San Diego, California, USA
|
||
|
|
||
|
**
|
||
|
|
||
|
** Users and possessors of this source code are hereby granted a
|
||
|
|
||
|
** nonexclusive, royalty-free copyright and design patent license to
|
||
|
|
||
|
** use this code in individual software. License is not granted for
|
||
|
|
||
|
** commercial resale, in whole or in part, without prior written
|
||
|
|
||
|
** permission from SDSC. This source is provided "AS IS" without express
|
||
|
|
||
|
** or implied warranty of any kind.
|
||
|
|
||
|
**
|
||
|
|
||
|
** For further information contact:
|
||
|
|
||
|
** E-Mail: info@sds.sdsc.edu
|
||
|
|
||
|
**
|
||
|
|
||
|
** Surface Mail: Information Center
|
||
|
|
||
|
** San Diego Supercomputer Center
|
||
|
|
||
|
** P.O. Box 85608
|
||
|
|
||
|
** San Diego, CA 92138-5608
|
||
|
|
||
|
** (619) 534-5000
|
||
|
|
||
|
**/
|
||
|
|
||
|
|
||
|
|
||
|
#define HEADER " $Header: /roq/libim/impcx.c 1 11/02/99 4:38p Zaphod $"
|
||
|
|
||
|
|
||
|
|
||
|
/**
|
||
|
|
||
|
** FILE
|
||
|
|
||
|
** impcx.c - ZSoft IBM PC Paintbrush image file I/O
|
||
|
|
||
|
**
|
||
|
|
||
|
** PROJECT
|
||
|
|
||
|
** libim - SDSC image manipulation library
|
||
|
|
||
|
**
|
||
|
|
||
|
** DESCRIPTION
|
||
|
|
||
|
** impcx.c contains routines to read and write ZSoft PCX files for
|
||
|
|
||
|
** the image manipulation library. Raster data read in is stored
|
||
|
|
||
|
** in a VFB. Raster data written out is taken from a tag table.
|
||
|
|
||
|
**
|
||
|
|
||
|
** PUBLIC CONTENTS
|
||
|
|
||
|
** d =defined constant
|
||
|
|
||
|
** f =function
|
||
|
|
||
|
** m =defined macro
|
||
|
|
||
|
** t =typedef/struct/union
|
||
|
|
||
|
** v =variable
|
||
|
|
||
|
** ? =other
|
||
|
|
||
|
**
|
||
|
|
||
|
** imPcxRead f read an PCX file
|
||
|
|
||
|
** imPcxWrite1 f write a 1-bit PCX file
|
||
|
|
||
|
** imPcxWrite8 f write an 8-bit PCX file
|
||
|
|
||
|
**
|
||
|
|
||
|
** PRIVATE CONTENTS
|
||
|
|
||
|
** imPcxHeaderInfo t header for PCX files
|
||
|
|
||
|
** imPcxHeaderFields v header description for binary I/O package
|
||
|
|
||
|
**
|
||
|
|
||
|
** HISTORY
|
||
|
|
||
|
** $Log: /roq/libim/impcx.c $
|
||
|
*
|
||
|
* 1 11/02/99 4:38p Zaphod
|
||
|
|
||
|
** Revision 1.12 1995/06/29 00:28:04 bduggan
|
||
|
|
||
|
** updated copyright year
|
||
|
|
||
|
**
|
||
|
|
||
|
** Revision 1.11 1995/06/15 20:48:58 bduggan
|
||
|
|
||
|
** Removed useless ++. Changed bzero to memset.
|
||
|
|
||
|
**
|
||
|
|
||
|
** Revision 1.10 1995/04/03 21:32:34 bduggan
|
||
|
|
||
|
** took out #ifdef NEWMAGIC
|
||
|
|
||
|
**
|
||
|
|
||
|
** Revision 1.9 1995/01/10 23:36:33 bduggan
|
||
|
|
||
|
** put in IMMULTI, IMPIPE instead of TRUE/FALSE
|
||
|
|
||
|
** uncapitlized i's in local functions
|
||
|
|
||
|
** made read/write routines static
|
||
|
|
||
|
**
|
||
|
|
||
|
** Revision 1.8 94/10/03 11:30:21 nadeau
|
||
|
|
||
|
** Updated to ANSI C and C++ compatibility.
|
||
|
|
||
|
** Removed all use of register keyword.
|
||
|
|
||
|
** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char)
|
||
|
|
||
|
** Changed all float arguments to double.
|
||
|
|
||
|
** Added forward declarations.
|
||
|
|
||
|
** Added misc. casts to passify SGI and DEC compilers.
|
||
|
|
||
|
** Changed all macros and defined constants to have names
|
||
|
|
||
|
** starting with IM.
|
||
|
|
||
|
** Rearranged magic number structures for format handlers.
|
||
|
|
||
|
** Made format handler routines static (i.e., local to file).
|
||
|
|
||
|
** Updated comments, adding format descriptions and references.
|
||
|
|
||
|
** Updated indenting on some code.
|
||
|
|
||
|
** Updated copyright message.
|
||
|
|
||
|
**
|
||
|
|
||
|
** Revision 1.7 92/12/03 01:50:23 nadeau
|
||
|
|
||
|
** Corrected info messages.
|
||
|
|
||
|
**
|
||
|
|
||
|
** Revision 1.6 92/11/23 18:42:43 nadeau
|
||
|
|
||
|
** Removed use of IMINFOMSG.
|
||
|
|
||
|
**
|
||
|
|
||
|
** Revision 1.5 92/11/04 12:12:37 groening
|
||
|
|
||
|
** put ImFIleFormat info and magic number info
|
||
|
|
||
|
** from imfmt.c into this file.
|
||
|
|
||
|
**
|
||
|
|
||
|
** Revision 1.4 92/09/29 18:00:29 vle
|
||
|
|
||
|
** Added ImInfo messages.
|
||
|
|
||
|
**
|
||
|
|
||
|
** Revision 1.3 92/08/31 17:30:11 vle
|
||
|
|
||
|
** Updated copyright notice.
|
||
|
|
||
|
**
|
||
|
|
||
|
** Revision 1.2 91/10/03 09:15:18 nadeau
|
||
|
|
||
|
** Added undocumented PC PaintBrush CLT type.
|
||
|
|
||
|
**
|
||
|
|
||
|
** Revision 1.1 91/09/17 20:16:28 nadeau
|
||
|
|
||
|
** Initial revision
|
||
|
|
||
|
**
|
||
|
|
||
|
**/
|
||
|
|
||
|
|
||
|
|
||
|
#include "iminternal.h"
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/**
|
||
|
|
||
|
** $Header: /roq/libim/impcx.c 1 11/02/99 4:38p Zaphod $
|
||
|
|
||
|
** Copyright (c) 1989-1994 San Diego Supercomputer Center (SDSC)
|
||
|
|
||
|
** a division of General Atomics, San Diego, California, USA
|
||
|
|
||
|
**
|
||
|
|
||
|
** Users and possessors of this source code are hereby granted a
|
||
|
|
||
|
** nonexclusive, royalty-free copyright and design patent license to
|
||
|
|
||
|
** use this code in individual software. License is not granted for
|
||
|
|
||
|
** commercial resale, in whole or in part, without prior written
|
||
|
|
||
|
** permission from SDSC. This source is provided "AS IS" without express
|
||
|
|
||
|
** or implied warranty of any kind.
|
||
|
|
||
|
**
|
||
|
|
||
|
** For further information contact:
|
||
|
|
||
|
** E-Mail: info@sds.sdsc.edu
|
||
|
|
||
|
**
|
||
|
|
||
|
** Surface Mail: Information Center
|
||
|
|
||
|
** San Diego Supercomputer Center
|
||
|
|
||
|
** P.O. Box 85608
|
||
|
|
||
|
** San Diego, CA 92138-5608
|
||
|
|
||
|
** (619) 534-5000
|
||
|
|
||
|
**/
|
||
|
|
||
|
|
||
|
|
||
|
#define HEADER " $Header: /roq/libim/impcx.c 1 11/02/99 4:38p Zaphod $"
|
||
|
|
||
|
|
||
|
|
||
|
/**
|
||
|
|
||
|
** FILE
|
||
|
|
||
|
** impcx.c - ZSoft IBM PC Paintbrush image file I/O
|
||
|
|
||
|
**
|
||
|
|
||
|
** PROJECT
|
||
|
|
||
|
** libim - SDSC image manipulation library
|
||
|
|
||
|
**
|
||
|
|
||
|
** DESCRIPTION
|
||
|
|
||
|
** impcx.c contains routines to read and write ZSoft PCX files for
|
||
|
|
||
|
** the image manipulation library. Raster data read in is stored
|
||
|
|
||
|
** in a VFB. Raster data written out is taken from a tag table.
|
||
|
|
||
|
**
|
||
|
|
||
|
** PUBLIC CONTENTS
|
||
|
|
||
|
** d =defined constant
|
||
|
|
||
|
** f =function
|
||
|
|
||
|
** m =defined macro
|
||
|
|
||
|
** t =typedef/struct/union
|
||
|
|
||
|
** v =variable
|
||
|
|
||
|
** ? =other
|
||
|
|
||
|
**
|
||
|
|
||
|
** done
|
||
|
|
||
|
**
|
||
|
|
||
|
** PRIVATE CONTENTS
|
||
|
|
||
|
** imPcxRead f read an PCX file
|
||
|
|
||
|
** imPcxWrite1 f write a 1-bit PCX file
|
||
|
|
||
|
** imPcxWrite8 f write an 8-bit PCX file
|
||
|
|
||
|
** imPcxHeaderInfo t header for PCX files
|
||
|
|
||
|
** imPcxHeaderFields v header description for binary I/O package
|
||
|
|
||
|
**
|
||
|
|
||
|
** HISTORY
|
||
|
|
||
|
** $Log: /roq/libim/impcx.c $
|
||
|
*
|
||
|
* 1 11/02/99 4:38p Zaphod
|
||
|
|
||
|
** Revision 1.12 1995/06/29 00:28:04 bduggan
|
||
|
|
||
|
** updated copyright year
|
||
|
|
||
|
**
|
||
|
|
||
|
** Revision 1.11 1995/06/15 20:48:58 bduggan
|
||
|
|
||
|
** Removed useless ++. Changed bzero to memset.
|
||
|
|
||
|
**
|
||
|
|
||
|
** Revision 1.10 1995/04/03 21:32:34 bduggan
|
||
|
|
||
|
** took out #ifdef NEWMAGIC
|
||
|
|
||
|
**
|
||
|
|
||
|
** Revision 1.9 1995/01/10 23:36:33 bduggan
|
||
|
|
||
|
** put in IMMULTI, IMPIPE instead of TRUE/FALSE
|
||
|
|
||
|
** uncapitlized i's in local functions
|
||
|
|
||
|
** made read/write routines static
|
||
|
|
||
|
**
|
||
|
|
||
|
** Revision 1.8 94/10/03 11:30:21 nadeau
|
||
|
|
||
|
** Updated to ANSI C and C++ compatibility.
|
||
|
|
||
|
** Removed all use of register keyword.
|
||
|
|
||
|
** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char)
|
||
|
|
||
|
** Changed all float arguments to double.
|
||
|
|
||
|
** Added forward declarations.
|
||
|
|
||
|
** Added misc. casts to passify SGI and DEC compilers.
|
||
|
|
||
|
** Changed all macros and defined constants to have names
|
||
|
|
||
|
** starting with IM.
|
||
|
|
||
|
** Rearranged magic number structures for format handlers.
|
||
|
|
||
|
** Made format handler routines static (i.e., local to file).
|
||
|
|
||
|
** Updated comments, adding format descriptions and references.
|
||
|
|
||
|
** Updated indenting on some code.
|
||
|
|
||
|
** Updated copyright message.
|
||
|
|
||
|
**
|
||
|
|
||
|
** Revision 1.7 92/12/03 01:50:23 nadeau
|
||
|
|
||
|
** Corrected info messages.
|
||
|
|
||
|
**
|
||
|
|
||
|
** Revision 1.6 92/11/23 18:42:43 nadeau
|
||
|
|
||
|
** Removed use of IMINFOMSG.
|
||
|
|
||
|
**
|
||
|
|
||
|
** Revision 1.5 92/11/04 12:12:37 groening
|
||
|
|
||
|
** put ImFIleFormat info and magic number info
|
||
|
|
||
|
** from imfmt.c into this file.
|
||
|
|
||
|
**
|
||
|
|
||
|
** Revision 1.4 92/09/29 18:00:29 vle
|
||
|
|
||
|
** Added ImInfo messages.
|
||
|
|
||
|
**
|
||
|
|
||
|
** Revision 1.3 92/08/31 17:30:11 vle
|
||
|
|
||
|
** Updated copyright notice.
|
||
|
|
||
|
**
|
||
|
|
||
|
** Revision 1.2 91/10/03 09:15:18 nadeau
|
||
|
|
||
|
** Added undocumented PC PaintBrush CLT type.
|
||
|
|
||
|
**
|
||
|
|
||
|
** Revision 1.1 91/09/17 20:16:28 nadeau
|
||
|
|
||
|
** Initial revision
|
||
|
|
||
|
**
|
||
|
|
||
|
**/
|
||
|
|
||
|
|
||
|
|
||
|
#include "iminternal.h"
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/**
|
||
|
|
||
|
** FORMAT
|
||
|
|
||
|
** PCX - ZSoft IBM PC Paintbrush image
|
||
|
|
||
|
**
|
||
|
|
||
|
** AKA
|
||
|
|
||
|
** pcc
|
||
|
|
||
|
**
|
||
|
|
||
|
** FORMAT REFERENCES
|
||
|
|
||
|
** - Technical Reference Manual for PC Paintbrush et al, ZSoft Corp.
|
||
|
|
||
|
** - Bit-Mapped Graphics, Steve Rimmer
|
||
|
|
||
|
** - Supercharged Bitmapped Graphics, Steve Rimmer
|
||
|
|
||
|
** - Graphics File Formats, David C. Kay, John R. Levine
|
||
|
|
||
|
** - HP LaserJet Programming, Andrew Binstock, David Babcock, Marv Luse
|
||
|
|
||
|
**
|
||
|
|
||
|
** CODE CREDITS
|
||
|
|
||
|
** Custom development, Dave Nadeau, San Diego Supercomputer Center, 1991.
|
||
|
|
||
|
**
|
||
|
|
||
|
** DESCRIPTION
|
||
|
|
||
|
** ZSoft's PCX format is used by a variety of IBM PC software, including
|
||
|
|
||
|
** ZSoft's own Publisher's Paintbrush, PC Paintbrush Plus, and PC
|
||
|
|
||
|
** Paintbrush.
|
||
|
|
||
|
**
|
||
|
|
||
|
** This code supports the format as described in:
|
||
|
|
||
|
**
|
||
|
|
||
|
** Technical Reference Manual
|
||
|
|
||
|
** ZSoft Corporation
|
||
|
|
||
|
** 450 Franklin Rd. Suite 100
|
||
|
|
||
|
** Marietta, GA 30067
|
||
|
|
||
|
** (404) 428-0008
|
||
|
|
||
|
**
|
||
|
|
||
|
** This report is dated 1988.
|
||
|
|
||
|
**
|
||
|
|
||
|
** Additional format information was obtained from the book "Bit-Mapped
|
||
|
|
||
|
** Graphics" by Steve Rimmer and published by WINDCREST/McGraw-Hill.
|
||
|
|
||
|
**
|
||
|
|
||
|
** PCX files start with a fixed-length 128-byte header consisting of
|
||
|
|
||
|
** the following:
|
||
|
|
||
|
**
|
||
|
|
||
|
** Byte Item Size Description/Comments
|
||
|
|
||
|
**
|
||
|
|
||
|
** 0 Manufacturer 1 10 = ZSoft .PCX
|
||
|
|
||
|
**
|
||
|
|
||
|
** 1 Version 1 Version #
|
||
|
|
||
|
** 0 = Version 2.5
|
||
|
|
||
|
** 2 = Version 2.8 w/palette info
|
||
|
|
||
|
** 3 = Version 2.8 wo/palette info
|
||
|
|
||
|
** 5 = Version 3.0
|
||
|
|
||
|
**
|
||
|
|
||
|
** 2 Encoding 1 1 = .PCX run length encoding
|
||
|
|
||
|
**
|
||
|
|
||
|
** 3 Bits per pixel 1 # of bits/pixel per plane
|
||
|
|
||
|
** 1 = monochrome (CGA)
|
||
|
|
||
|
** 4 = 16-color (EGA)
|
||
|
|
||
|
** 8 = 256-color (VGA)
|
||
|
|
||
|
**
|
||
|
|
||
|
** 4 Window 4*2 Picture dimensions
|
||
|
|
||
|
** xmin
|
||
|
|
||
|
** ymin
|
||
|
|
||
|
** xmax
|
||
|
|
||
|
** ymax
|
||
|
|
||
|
**
|
||
|
|
||
|
** 12 Horizontal Res 2 Horizontal resolution of dev, in pixels
|
||
|
|
||
|
**
|
||
|
|
||
|
** 14 Vertical Res 2 Vertical resolution of dev, in pixels
|
||
|
|
||
|
**
|
||
|
|
||
|
** 16 Colormap 16*3 Basic color table
|
||
|
|
||
|
**
|
||
|
|
||
|
** 64 Reserved 1
|
||
|
|
||
|
**
|
||
|
|
||
|
** 65 No. Planes 1 # of color planes
|
||
|
|
||
|
**
|
||
|
|
||
|
** 66 Bytes per line 2 # of bytes per scanline per color
|
||
|
|
||
|
** color plane (always even)
|
||
|
|
||
|
**
|
||
|
|
||
|
** 68 Colormap type 2 Colormap type
|
||
|
|
||
|
** 1 = color/BW
|
||
|
|
||
|
** 2 = grayscale
|
||
|
|
||
|
**
|
||
|
|
||
|
** 70 Filler 58 Blanks
|
||
|
|
||
|
**
|
||
|
|
||
|
** All 2-byte integers are given in Intel byte order (LBF).
|
||
|
|
||
|
**
|
||
|
|
||
|
** Image data immediately follows the header. In version 6.0 PCX files,
|
||
|
|
||
|
** an optional 256-entry color palette may follow the image data (but
|
||
|
|
||
|
** see below).
|
||
|
|
||
|
**
|
||
|
|
||
|
** HEADER DETAILS
|
||
|
|
||
|
** The 'Manufacturer' field is always '10' for PCX files. ZSoft has never
|
||
|
|
||
|
** introduced further 'Manufacturer' codes. The SDSC Image Tools use
|
||
|
|
||
|
** the 'Manufacturer' field as the file's magic number.
|
||
|
|
||
|
**
|
||
|
|
||
|
** The 'Encoding' field is always '1'. ZSoft has never introduced other
|
||
|
|
||
|
** encoding schemes (which it should have, since this one is particularly
|
||
|
|
||
|
** stupid). ZSoft does not support unencoded images in PCX files.
|
||
|
|
||
|
**
|
||
|
|
||
|
** The 'Window' field is used to compute the image dimensions using:
|
||
|
|
||
|
**
|
||
|
|
||
|
** width = xmax - xmin + 1;
|
||
|
|
||
|
** height = ymax - ymin + 1;
|
||
|
|
||
|
**
|
||
|
|
||
|
** Note the '+1' needed on both of these. Just a quirk of PCX files.
|
||
|
|
||
|
** On write, the SDSC Image Tools set the 'xmin' and 'ymin' fields to 0,
|
||
|
|
||
|
** and the 'xmax' and 'ymax' fields to the width and height, minus 1.
|
||
|
|
||
|
**
|
||
|
|
||
|
** The 'Horizontal Res' and 'Vertical Res' fields are irrelevant.
|
||
|
|
||
|
** SDSC Image Tools code ignores these on read, and sets them to 0's
|
||
|
|
||
|
** on write.
|
||
|
|
||
|
**
|
||
|
|
||
|
** NOTE: The popular UNIX PBM+ tools set the horizontal and
|
||
|
|
||
|
** vertical device resolution to the image size on write, but
|
||
|
|
||
|
** ignore them on read. This is incorrect. The resolution fields
|
||
|
|
||
|
** are defined by the spec to be HARDWARE resolution, not image
|
||
|
|
||
|
** resolution. We feel that setting them to zeroes is less
|
||
|
|
||
|
** incorrect than setting them to some value.
|
||
|
|
||
|
**
|
||
|
|
||
|
** The 'Reserved' and 'Filler' fields are always ignored by us on
|
||
|
|
||
|
** read, and always set to 0's on write.
|
||
|
|
||
|
**
|
||
|
|
||
|
** The remaining fields have specific combinations of values depending
|
||
|
|
||
|
** upon the type of image being stored.
|
||
|
|
||
|
**
|
||
|
|
||
|
** MONOCHROME IMAGES
|
||
|
|
||
|
** Monochrome PCX files were intended for the original CGA card as well
|
||
|
|
||
|
** as single-plane use of the EGA card.
|
||
|
|
||
|
**
|
||
|
|
||
|
** Header values set to the following flag monochrome images:
|
||
|
|
||
|
**
|
||
|
|
||
|
** Version = 0, 2, or 3
|
||
|
|
||
|
** Bits per pixel = 1
|
||
|
|
||
|
** Colormap = ignored
|
||
|
|
||
|
** No. Planes = 1
|
||
|
|
||
|
** Bytes per line = (width + 7) / 8 rounded up to even byte count
|
||
|
|
||
|
** Colormap type = ignored
|
||
|
|
||
|
**
|
||
|
|
||
|
** Image data following the header is an encoded buffer with 8 pixels
|
||
|
|
||
|
** to the byte. The left-most bit in each byte corresponds to the
|
||
|
|
||
|
** left-most pixel of each group of 8. Scanline buffers are rounded up
|
||
|
|
||
|
** to an even number of bytes, then encoded (see below).
|
||
|
|
||
|
**
|
||
|
|
||
|
** "Extra" image data may follow the last scanline in order to bring the
|
||
|
|
||
|
** image data up to a multiple of 8 or 16 scanlines. On read, we ignore
|
||
|
|
||
|
** such extra data. On write we don't write extra data.
|
||
|
|
||
|
**
|
||
|
|
||
|
** 4-BIT COLOR IMAGES
|
||
|
|
||
|
** 4-bit color PCX files were intended for the four-plane mode of the
|
||
|
|
||
|
** EGA card.
|
||
|
|
||
|
**
|
||
|
|
||
|
** Header values set to the following flag 4-bit color images:
|
||
|
|
||
|
**
|
||
|
|
||
|
** Version = 2 or 3
|
||
|
|
||
|
** Bits per pixel = 1
|
||
|
|
||
|
** Colormap = used (see below)
|
||
|
|
||
|
** No. Planes = 4
|
||
|
|
||
|
** Bytes per line = (width + 7)/8 rounded up to even byte count
|
||
|
|
||
|
** Colormap type = ignored
|
||
|
|
||
|
**
|
||
|
|
||
|
** If the 'Version' field of the header is a '2', the header contains
|
||
|
|
||
|
** a color palette in the 'Colormap' portion of the header. Palette
|
||
|
|
||
|
** values are given from low to high and consist of 16 3-byte RGB triplets.
|
||
|
|
||
|
**
|
||
|
|
||
|
** If the 'Version' field of the header is a '3', the header does not
|
||
|
|
||
|
** contain a palette for the image. For our purposes, this makes the
|
||
|
|
||
|
** image grayscale.
|
||
|
|
||
|
**
|
||
|
|
||
|
** Image data following the header is grouped as four consequetive
|
||
|
|
||
|
** 1-bit scanlines of data. Each 1-bit scanline is packed into bytes
|
||
|
|
||
|
** at 8 pixels to the byte, with the left most bit in each byte
|
||
|
|
||
|
** corresponding to the left-most pixel of each group of 8. The entire
|
||
|
|
||
|
** set of 4 1-bit scanlines is considered one buffer, and as a whole
|
||
|
|
||
|
** is padded to the next higher even byte boundary, then encoded.
|
||
|
|
||
|
**
|
||
|
|
||
|
** For encoding purposes, the group of 4 1-bit scanlines is considered
|
||
|
|
||
|
** a single buffer. Encoding always breaks at the end of such a buffer,
|
||
|
|
||
|
** but might not break going from one 1-bit scanline to the next within
|
||
|
|
||
|
** such a group.
|
||
|
|
||
|
**
|
||
|
|
||
|
** "Extra" image data may follow the last scanline in order to bring the
|
||
|
|
||
|
** image data up to a multiple of 8 or 16 scanlines. We neither read it
|
||
|
|
||
|
** or write it.
|
||
|
|
||
|
**
|
||
|
|
||
|
** 8-BIT COLOR IMAGES
|
||
|
|
||
|
** 8-bit color PCX files were intended for the VGA card.
|
||
|
|
||
|
**
|
||
|
|
||
|
** Header values set to the following flag 8-bit color images:
|
||
|
|
||
|
**
|
||
|
|
||
|
** Version = 5
|
||
|
|
||
|
** Bits per plane = 8
|
||
|
|
||
|
** Colormap = unused (see below)
|
||
|
|
||
|
** No. Planes = 1
|
||
|
|
||
|
** Bytes per line = ((width + 1)/2) * 2
|
||
|
|
||
|
** Colormap type = 1 or 2
|
||
|
|
||
|
**
|
||
|
|
||
|
** The 'Version' field will always be 5 for 8-bit color images.
|
||
|
|
||
|
**
|
||
|
|
||
|
** The color palette for 8-bit color images is too big for the 'Colormap'
|
||
|
|
||
|
** field of the header. Instead, the palette, if any, is tacked onto
|
||
|
|
||
|
** the end of the image data. The "official" procedure (from the spec)
|
||
|
|
||
|
** is to seek to 769 bytes before the end of the file and read a byte.
|
||
|
|
||
|
** If it is a 0x0C (12 decimal), then the next 768 bytes are 256 3-byte
|
||
|
|
||
|
** RGB triplets.
|
||
|
|
||
|
**
|
||
|
|
||
|
** The 'Colormap type' field is meant to toggle the VGA card between
|
||
|
|
||
|
** grayscale and color. In our case, it is meaningless. The existance
|
||
|
|
||
|
** of a palette makes the image color. Nonexistance means the image is
|
||
|
|
||
|
** grayscale. On read, we ignore this field. On write, we always set
|
||
|
|
||
|
** it to 1 (color).
|
||
|
|
||
|
**
|
||
|
|
||
|
** Image data following the header is encoded from a stream of bytes,
|
||
|
|
||
|
** at one byte per pixel. Consequetive bits in each byte correspond to
|
||
|
|
||
|
** consequetive color planes (as with normal graphics hardware).
|
||
|
|
||
|
** As with monochrome and 4-bit images, each scanline buffer is rounded
|
||
|
|
||
|
** up to an even number of bytes. Encoding breaks at the end of each
|
||
|
|
||
|
** scanline.
|
||
|
|
||
|
**
|
||
|
|
||
|
** "Extra" image data may follow the last scanline in order to bring the
|
||
|
|
||
|
** image data up to a multiple of 8 or 16 scanlines. We neither read or
|
||
|
|
||
|
** write such extra scanlines.
|
||
|
|
||
|
**
|
||
|
|
||
|
** ENCODING
|
||
|
|
||
|
** Image data buffers are encoded with a type of run-length encoding.
|
||
|
|
||
|
** A data byte with its upper two bits set is a run count for the number
|
||
|
|
||
|
** of times to repeat the next byte in the file. Data bytes without
|
||
|
|
||
|
** their upper two bits set are literal values. For example:
|
||
|
|
||
|
**
|
||
|
|
||
|
** Decode:
|
||
|
|
||
|
** pEncoded = encodedBuffer;
|
||
|
|
||
|
** pDecoded = decodedBuffer;
|
||
|
|
||
|
** pEnd = decodedBuffer + BytesPerLine;
|
||
|
|
||
|
** while ( pDecoded < pEnd )
|
||
|
|
||
|
** {
|
||
|
|
||
|
** if ( (*pEncoded & 0xC0) == 0xC0 )
|
||
|
|
||
|
** {
|
||
|
|
||
|
** count = (*pEncoded++) & 0x3F;
|
||
|
|
||
|
** value = *pEncoded++;
|
||
|
|
||
|
** for ( j = 0; j < count; j++ )
|
||
|
|
||
|
** *pDecoded++ = value;
|
||
|
|
||
|
** }
|
||
|
|
||
|
** else
|
||
|
|
||
|
** *pDecoded++ = *pEncoded++;
|
||
|
|
||
|
** }
|
||
|
|
||
|
**
|
||
|
|
||
|
** When encoding one must watch for data values with their upper two
|
||
|
|
||
|
** bits set. Such bytes are encoded as a run byte of 1 followed by
|
||
|
|
||
|
** the data byte.
|
||
|
|
||
|
**
|
||
|
|
||
|
** The worst case image only contains data bytes with their upper two
|
||
|
|
||
|
** bits set. This will cause an encoded image to take DOUBLE the space
|
||
|
|
||
|
** of the unencoded image. Brain dead.
|
||
|
|
||
|
**
|
||
|
|
||
|
** COLOR TABLE CAVEATS
|
||
|
|
||
|
** #1: PBM+ Problems.
|
||
|
|
||
|
** According to the spec, the existance and style of CLT is determined
|
||
|
|
||
|
** by the header's version number:
|
||
|
|
||
|
**
|
||
|
|
||
|
** 0 2.5 no CLT
|
||
|
|
||
|
** 2 2.8W CLT in header
|
||
|
|
||
|
** 3 2.8WO no CLT
|
||
|
|
||
|
** 5 3.0 CLT at end of file (possibly)
|
||
|
|
||
|
**
|
||
|
|
||
|
** Unfortunately, the popular PBM+ package for UNIX systems always
|
||
|
|
||
|
** generates PCX files for verison 3.0, and assumes other packages
|
||
|
|
||
|
** will check the header plane and bits per pixel fields to decide
|
||
|
|
||
|
** where a CLT is.
|
||
|
|
||
|
**
|
||
|
|
||
|
** In order to be compatible with this software, we ignore the version
|
||
|
|
||
|
** number header and similarly depend upon the plane and bits per pixel
|
||
|
|
||
|
** counts to decide what type of CLT, if any, is present. The only
|
||
|
|
||
|
** version number we check is 2.8WO which explicitly says there is no
|
||
|
|
||
|
** CLT for the image and that the header CLT should be ignored.
|
||
|
|
||
|
**
|
||
|
|
||
|
** Fortunately, this change of parsing still maintains compatibility
|
||
|
|
||
|
** with PCX usage. For CLT writing, we choose the right version number
|
||
|
|
||
|
** the way we're supposed to.
|
||
|
|
||
|
**
|
||
|
|
||
|
** #2: Spec Problems.
|
||
|
|
||
|
** The PCX spec recommends finding end-of-file CLTs by the following:
|
||
|
|
||
|
**
|
||
|
|
||
|
** lseek( fd, -769, 2 ); -- seek to end of file, -769 bytes
|
||
|
|
||
|
** read( fd, buffer, 1 ); -- read 1 byte
|
||
|
|
||
|
** if ( *buffer == 0x0C )
|
||
|
|
||
|
** there is a CLT
|
||
|
|
||
|
**
|
||
|
|
||
|
** This is not a robust way of checking for an EOF CLT. Imagine the
|
||
|
|
||
|
** case where there is no CLT, and seeking back 769 bytes gets you
|
||
|
|
||
|
** into the middle of image data. 0x0C is not a byte that is impossible
|
||
|
|
||
|
** to encounter in image data. If you happened to encounter it, you
|
||
|
|
||
|
** would mistakenly conclude there was a CLT.
|
||
|
|
||
|
**
|
||
|
|
||
|
** The popular UNIX PBM+ package simply reads the image data in, one
|
||
|
|
||
|
** byte at a time. When it has finished reading in the data, it looks
|
||
|
|
||
|
** at the next byte to see if it is 0x0C. If so, it reads in a CLT.
|
||
|
|
||
|
**
|
||
|
|
||
|
** This is neither speedy (lots of single byte reads) nor robust.
|
||
|
|
||
|
** The spec says:
|
||
|
|
||
|
** "There may be extra scan lines at the bottom of the image,
|
||
|
|
||
|
** to round to 8 or 16 scan lines."
|
||
|
|
||
|
**
|
||
|
|
||
|
** If they were there, PBM+ would not find the CLT.
|
||
|
|
||
|
**
|
||
|
|
||
|
** Our approach is more robust and much faster.
|
||
|
|
||
|
**
|
||
|
|
||
|
** The CLT is read in first by the seek approach. Doing it first is
|
||
|
|
||
|
** merely convenient algorithmically. It could be done last as well.
|
||
|
|
||
|
**
|
||
|
|
||
|
** The image data is then read in by making a worst-case guess at its
|
||
|
|
||
|
** size in bytes (twice the size of a decoded image). This will
|
||
|
|
||
|
** very likely read too much data. This is OK since we watch the
|
||
|
|
||
|
** decode count while decoding so that we don't decode off the end of
|
||
|
|
||
|
** the image. The advantage is that we can read it all in with one
|
||
|
|
||
|
** read system call. Much much much faster.
|
||
|
|
||
|
**
|
||
|
|
||
|
** After decoding, a count is returned of the number of encoded bytes
|
||
|
|
||
|
** used. To determine if the CLT we read in earlier is truely a CLT,
|
||
|
|
||
|
** and not image data improperly interpreted, we compare the file position
|
||
|
|
||
|
** of the 769-bytes-from-the-end CLT with the position we'd be at if we
|
||
|
|
||
|
** had only read in the image data (header + # of encoded bytes used).
|
||
|
|
||
|
** If the CLT file position is in advance of the end-of-image data
|
||
|
|
||
|
** position, then it is considered a valid CLT.
|
||
|
|
||
|
**
|
||
|
|
||
|
** It is still possible that there are lots and lots of "extra" scanlines
|
||
|
|
||
|
** after the image data and that they contain 0x0C's. A seek into the
|
||
|
|
||
|
** midst of these would still falsly conclude there was a CLT there.
|
||
|
|
||
|
** This seems very unlikely. Software that pads with extra scanlines
|
||
|
|
||
|
** usually sets them to zeroes.
|
||
|
|
||
|
**
|
||
|
|
||
|
** IMAGE DATA CAVEATS
|
||
|
|
||
|
** #1: Even-byte-count buffers
|
||
|
|
||
|
** According to the spec, all scanlines, prior to encoding, are rounded
|
||
|
|
||
|
** up to an even number of bytes. Unfortunately, the popular PBM+
|
||
|
|
||
|
** package for UNIX systems fails to do this.
|
||
|
|
||
|
**
|
||
|
|
||
|
** In order to be compatible with this software, we check the header's
|
||
|
|
||
|
** "Bytes per line" field and skip the difference between it and the
|
||
|
|
||
|
** size of a decoded buffer. If the PCX generator made the byte count
|
||
|
|
||
|
** even, this will skip 1 byte for odd-length lines. If the PCX generator
|
||
|
|
||
|
** did not make this even, such as PBM+, then this will skip no bytes
|
||
|
|
||
|
** in such cases.
|
||
|
|
||
|
**
|
||
|
|
||
|
** Fortunately, this change of skipping still maintains compatibility
|
||
|
|
||
|
** with PCX usage. For image writing, we round up the way we're
|
||
|
|
||
|
** supposed to.
|
||
|
|
||
|
**
|
||
|
|
||
|
** #2: 2-bit and 3-bit color images
|
||
|
|
||
|
** According to the documentation, 2-bit and 3-bit images aren't really
|
||
|
|
||
|
** allowed. However, the poplular UNIX PBM+ package freely generates
|
||
|
|
||
|
** them.
|
||
|
|
||
|
**
|
||
|
|
||
|
** In order to be compatible with such code, we also accept 2-bit and
|
||
|
|
||
|
** 3-bit images as variations of the 4-bit case.
|
||
|
|
||
|
**
|
||
|
|
||
|
** Header values set to the following flag 4-bit color images:
|
||
|
|
||
|
**
|
||
|
|
||
|
** Version = 2 or 3
|
||
|
|
||
|
** Bits per pixel = 1
|
||
|
|
||
|
** Colormap = used (see below)
|
||
|
|
||
|
** No. Planes = 2 or 3
|
||
|
|
||
|
** Bytes per line = (width + 7)/8 rounded up to even byte count
|
||
|
|
||
|
** Colormap type = ignored
|
||
|
|
||
|
**
|
||
|
|
||
|
** 2-bit and 3-bit cases are stored the same as for the 4-bit case.
|
||
|
|
||
|
*/
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
|
||
|
* PCX - ZSoft IBM PC Paintbrush
|
||
|
|
||
|
* For information on these structures, how to use them, etc. please
|
||
|
|
||
|
* see imfmt.c.
|
||
|
|
||
|
*/
|
||
|
|
||
|
#ifdef __STDC__
|
||
|
|
||
|
static int imPcxRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable );
|
||
|
|
||
|
static int imPcxWrite1( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable );
|
||
|
|
||
|
static int imPcxWrite8( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable );
|
||
|
|
||
|
#else
|
||
|
|
||
|
static int imPcxRead( );
|
||
|
|
||
|
static int imPcxWrite1( ), imPcxWrite8( );
|
||
|
|
||
|
#endif
|
||
|
|
||
|
static char *imPcxNames[ ] = { "pcx", "pcc", NULL };
|
||
|
|
||
|
static unsigned char imPcxMagicNumber[ ] = { 0x0a };
|
||
|
|
||
|
static ImFileFormatReadMap imPcxReadMap[ ] =
|
||
|
|
||
|
{
|
||
|
|
||
|
/* in out */
|
||
|
|
||
|
/* type,ch,dep, attr. VFB type attr. */
|
||
|
|
||
|
{ IN,1,1, RLE, IMVFBMONO, 0 },
|
||
|
|
||
|
{ IN,1,2, RLE, IMVFBINDEX8, 0 },
|
||
|
|
||
|
{ IN,1,2, RLE|C, IMVFBINDEX8, C },
|
||
|
|
||
|
{ IN,1,3, RLE, IMVFBINDEX8, 0 },
|
||
|
|
||
|
{ IN,1,3, RLE|C, IMVFBINDEX8, C },
|
||
|
|
||
|
{ IN,1,4, RLE, IMVFBINDEX8, 0 },
|
||
|
|
||
|
{ IN,1,4, RLE|C, IMVFBINDEX8, C },
|
||
|
|
||
|
{ IN,1,8, RLE, IMVFBINDEX8, 0 },
|
||
|
|
||
|
{ IN,1,8, RLE|C, IMVFBINDEX8, C },
|
||
|
|
||
|
{ -1, 0, -1, 0 },
|
||
|
|
||
|
};
|
||
|
|
||
|
static ImFileFormatWriteMap imPcxWriteMap[ ] =
|
||
|
|
||
|
{
|
||
|
|
||
|
/* in out */
|
||
|
|
||
|
/* VFB type, attr., type,ch,dep, attr., func */
|
||
|
|
||
|
{ IMVFBMONO, 0, IN,1,1, RLE, imPcxWrite1 },
|
||
|
|
||
|
{ IMVFBINDEX8, 0, IN,1,8, RLE, imPcxWrite8 },
|
||
|
|
||
|
{ IMVFBINDEX8, C, IN,1,8, RLE| C, imPcxWrite8 },
|
||
|
|
||
|
{ -1, 0, -1, 0, NULL },
|
||
|
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
static ImFileMagic imFilePcxMagic[]=
|
||
|
|
||
|
{
|
||
|
|
||
|
{ 0, 1, imPcxMagicNumber },
|
||
|
|
||
|
{ 0, 0, NULL },
|
||
|
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
ImFileFormat ImFilePcxFormat =
|
||
|
|
||
|
{
|
||
|
|
||
|
imPcxNames, "ZSoft IBM PC Paintbrush file",
|
||
|
|
||
|
"ZSoft Corp.",
|
||
|
|
||
|
"1-bit monochrome (CGA), 2-bit, 3-bit, and 4-bit color (EGA) files\n\
|
||
|
with or without a color table, and 8-bit color (VGA) files with or\n\
|
||
|
without a color table. All are RLE-encoded.",
|
||
|
"1-bit monochrome (CGA), and 8-bit color (VGA) files with or\n\
|
||
|
without a color table. All are RLE-encoded.",
|
||
|
imFilePcxMagic,
|
||
|
|
||
|
IMNOMULTI, IMNOPIPE,
|
||
|
|
||
|
IMNOMULTI, IMPIPE,
|
||
|
|
||
|
imPcxRead, imPcxReadMap, imPcxWriteMap
|
||
|
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
|
||
|
* TYPEDEF & STRUCT
|
||
|
|
||
|
* imPcxHeaderInfo - header for PCX files
|
||
|
|
||
|
* imPcxHeaderFields - header description for binary I/O package
|
||
|
|
||
|
*
|
||
|
|
||
|
* DESCRIPTION
|
||
|
|
||
|
* PCX files start with a header that gives various details of the
|
||
|
|
||
|
* image contained in the file.
|
||
|
|
||
|
*/
|
||
|
|
||
|
|
||
|
|
||
|
typedef struct imPcxHeaderInfo /* PCX */
|
||
|
|
||
|
{
|
||
|
|
||
|
unsigned char pcx_manufacturer;
|
||
|
|
||
|
unsigned char pcx_version;
|
||
|
|
||
|
unsigned char pcx_encoding;
|
||
|
|
||
|
unsigned char pcx_bitsPerPixel;
|
||
|
|
||
|
unsigned short pcx_window[4];
|
||
|
|
||
|
unsigned short pcx_horizontalRes;
|
||
|
|
||
|
unsigned short pcx_verticalRes;
|
||
|
|
||
|
unsigned char pcx_clt[16][3];
|
||
|
|
||
|
unsigned char pcx_reserved;
|
||
|
|
||
|
unsigned char pcx_nPlanes;
|
||
|
|
||
|
unsigned short pcx_bytesPerLine;
|
||
|
|
||
|
unsigned short pcx_cltType;
|
||
|
|
||
|
unsigned char pcx_filler[58];
|
||
|
|
||
|
} imPcxHeaderInfo;
|
||
|
|
||
|
|
||
|
|
||
|
static BinField imPcxHeaderFields[ ] =
|
||
|
|
||
|
{
|
||
|
|
||
|
/* pcx_manufacturer */ UCHAR, 1, 1,
|
||
|
|
||
|
/* pcx_version */ UCHAR, 1, 1,
|
||
|
|
||
|
/* pcx_encoding */ UCHAR, 1, 1,
|
||
|
|
||
|
/* pcx_bitsPerPixel */ UCHAR, 1, 1,
|
||
|
|
||
|
/* pcx_window */ USHORT, 2, 4,
|
||
|
|
||
|
/* pcx_horizontalRes */ USHORT, 2, 1,
|
||
|
|
||
|
/* pcx_verticalRes */ USHORT, 2, 1,
|
||
|
|
||
|
/* pcx_clt */ UCHAR, 1, 48,
|
||
|
|
||
|
/* pcx_reserved */ UCHAR, 1, 1,
|
||
|
|
||
|
/* pcx_nPlanes */ UCHAR, 1, 1,
|
||
|
|
||
|
/* pcx_bytesPerLine */ USHORT, 2, 1,
|
||
|
|
||
|
/* pcx_cltType */ USHORT, 2, 1,
|
||
|
|
||
|
/* pcx_filler */ UCHAR, 1, 58,
|
||
|
|
||
|
0, 0, 0,
|
||
|
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
#define IMPCXHEADERSIZE (128)
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
|
||
|
* Value for fields:
|
||
|
|
||
|
*/
|
||
|
|
||
|
#define IMPCXMANUFACTURER 0x0A /* ZSoft .PCX */
|
||
|
|
||
|
|
||
|
|
||
|
#define IMPCXV_2_5 0 /* Version 2.5 */
|
||
|
|
||
|
#define IMPCXV_2_8W 2 /* Version 2.8 w/clt */
|
||
|
|
||
|
#define IMPCXV_2_8WO 3 /* Version 2.8 wo/clt */
|
||
|
|
||
|
#define IMPCXV_3_0 5 /* Version 3.0 */
|
||
|
|
||
|
|
||
|
|
||
|
#define IMPCXENCODING 1 /* Run-length encoded */
|
||
|
|
||
|
|
||
|
|
||
|
#define IMPCXCOLOR 1 /* Color or BW CLT */
|
||
|
|
||
|
#define IMPCXGRAY 2 /* Grayscale CLT */
|
||
|
|
||
|
#define IMPCXPAINTBRUSH 2992 /* Strange PC Paintbrush type */
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
|
||
|
* FUNCTION
|
||
|
|
||
|
* imPcxDecode - decode a run buffer
|
||
|
|
||
|
*
|
||
|
|
||
|
* DESCRIPTION
|
||
|
|
||
|
* An incomming encoded data buffer is decoded into a second buffer.
|
||
|
|
||
|
*/
|
||
|
|
||
|
|
||
|
|
||
|
static int /* Returns # of bytes decoded */
|
||
|
|
||
|
#ifdef __STDC__
|
||
|
|
||
|
imPcxDecode( unsigned char *src, int nDst, unsigned char *dst )
|
||
|
|
||
|
#else
|
||
|
|
||
|
imPcxDecode( src, nDst, dst )
|
||
|
|
||
|
unsigned char *src; /* Source buffer (encoded) */
|
||
|
|
||
|
int nDst; /* # of bytes in destination buffer*/
|
||
|
|
||
|
unsigned char *dst; /* Destination buffer (decoded) */
|
||
|
|
||
|
#endif
|
||
|
|
||
|
{
|
||
|
|
||
|
int i; /* Counter */
|
||
|
|
||
|
unsigned char value; /* Run value */
|
||
|
|
||
|
unsigned char *dstLast; /* Last byte +1 of dst buffer */
|
||
|
|
||
|
unsigned char *srcSave; /* Saved src pointer */
|
||
|
|
||
|
|
||
|
|
||
|
srcSave = src;
|
||
|
|
||
|
dstLast = dst + nDst;
|
||
|
|
||
|
while ( dst < dstLast )
|
||
|
|
||
|
{
|
||
|
|
||
|
if ( (*src & 0xC0) != 0xC0 )
|
||
|
|
||
|
*dst++ = *src++;
|
||
|
|
||
|
else
|
||
|
|
||
|
{
|
||
|
|
||
|
i = *src++ & 0x3F;
|
||
|
|
||
|
for ( value = *src++; i; i-- )
|
||
|
|
||
|
*dst++ = value;
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
return ( src - srcSave );
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
|
||
|
* FUNCTION
|
||
|
|
||
|
* imPcxEncode - encode a run buffer
|
||
|
|
||
|
*
|
||
|
|
||
|
* DESCRIPTION
|
||
|
|
||
|
* An incomming data buffer is encoded into a second buffer. Bytes
|
||
|
|
||
|
* with their upper two bits set are flagged and stored as runs of 1.
|
||
|
|
||
|
*
|
||
|
|
||
|
* Note: becuase the upper two bits of a byte flag a run, we get two
|
||
|
|
||
|
* special cases to watch for:
|
||
|
|
||
|
* 1. The maximum run length is 63 (lower 6 bits set).
|
||
|
|
||
|
* 2. Data bytes with the upper two bits set have to be
|
||
|
|
||
|
* treated as runs of 1.
|
||
|
|
||
|
*/
|
||
|
|
||
|
|
||
|
|
||
|
static int /* Returns # of bytes encoded */
|
||
|
|
||
|
#ifdef __STDC__
|
||
|
|
||
|
imPcxEncode( unsigned char *src, int nSrc, unsigned char *dst )
|
||
|
|
||
|
#else
|
||
|
|
||
|
imPcxEncode( src, nSrc, dst )
|
||
|
|
||
|
unsigned char *src; /* Source buffer (encoded) */
|
||
|
|
||
|
int nSrc; /* # of bytes in source buffer */
|
||
|
|
||
|
unsigned char *dst; /* Destination buffer (decoded) */
|
||
|
|
||
|
#endif
|
||
|
|
||
|
{
|
||
|
|
||
|
int run; /* Run count */
|
||
|
|
||
|
unsigned char *srcLast; /* Last byte +1 of source buffer*/
|
||
|
|
||
|
unsigned char *dstSave; /* Saved original dst value */
|
||
|
|
||
|
|
||
|
|
||
|
srcLast = src + nSrc - 1;
|
||
|
|
||
|
dstSave = dst;
|
||
|
|
||
|
while ( src <= srcLast )
|
||
|
|
||
|
{
|
||
|
|
||
|
run = 0;
|
||
|
|
||
|
while ( *src == *(src+1) && src < srcLast && run < 62 )
|
||
|
|
||
|
{
|
||
|
|
||
|
run++;
|
||
|
|
||
|
src++;
|
||
|
|
||
|
}
|
||
|
|
||
|
if ( run > 0 )
|
||
|
|
||
|
*dst++ = (run + 1) | 0xC0;/* Store the run count*/
|
||
|
|
||
|
else if ( (*src & 0xC0) == 0xC0 )
|
||
|
|
||
|
*dst++ = 1 | 0xC0; /* Make it a run of 1 */
|
||
|
|
||
|
*dst++ = *src++;
|
||
|
|
||
|
}
|
||
|
|
||
|
return ( dst - dstSave );
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
|
||
|
* FUNCTION
|
||
|
|
||
|
* imPcxRead - read a PCX file
|
||
|
|
||
|
*
|
||
|
|
||
|
* DESCRIPTION
|
||
|
|
||
|
* The file header is read and checked. The color table, if any, is
|
||
|
|
||
|
* read in. The image data is read in and decoded, then dispersed into
|
||
|
|
||
|
* a VFB of the appropriate depth. The VFB and CLT are added to the
|
||
|
|
||
|
* tag table.
|
||
|
|
||
|
*/
|
||
|
|
||
|
|
||
|
|
||
|
static int /* Returns # tags read in */
|
||
|
|
||
|
#ifdef __STDC__
|
||
|
|
||
|
imPcxRead( int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable )
|
||
|
|
||
|
#else
|
||
|
|
||
|
imPcxRead( ioType, fd, fp, flagsTable, tagTable )
|
||
|
|
||
|
int ioType; /* I/O flags */
|
||
|
|
||
|
int fd; /* Input file descriptor */
|
||
|
|
||
|
FILE *fp; /* Input file pointer */
|
||
|
|
||
|
TagTable *flagsTable; /* Flags */
|
||
|
|
||
|
TagTable *tagTable; /* Tag table to add to */
|
||
|
|
||
|
#endif
|
||
|
|
||
|
{
|
||
|
|
||
|
ImVfb *vfb; /* Read in image */
|
||
|
|
||
|
ImVfbPtr pPixel; /* Pcxel pointer */
|
||
|
|
||
|
|
||
|
|
||
|
ImClt *clt; /* Image CLT */
|
||
|
|
||
|
ImCltPtr pColor; /* Color pointer */
|
||
|
|
||
|
|
||
|
|
||
|
unsigned char cltBuffer[769]; /* VGA card palette */
|
||
|
|
||
|
unsigned char *pCltBuffer; /* Buffer pointer */
|
||
|
|
||
|
|
||
|
|
||
|
unsigned char *encoded; /* Encoded scanline buffer */
|
||
|
|
||
|
unsigned char *decoded; /* Decoded scanline buffer */
|
||
|
|
||
|
unsigned char *pDecoded; /* Decoded buffer pointer */
|
||
|
|
||
|
unsigned char value; /* Pixel value */
|
||
|
|
||
|
unsigned char value2; /* Old pixel value */
|
||
|
|
||
|
|
||
|
|
||
|
imPcxHeaderInfo header; /* PCX file header */
|
||
|
|
||
|
|
||
|
|
||
|
int height, width; /* Image dimensions */
|
||
|
|
||
|
int width8; /* Width / 8 */
|
||
|
|
||
|
int widthextra; /* Width % 8 */
|
||
|
|
||
|
|
||
|
|
||
|
int n; /* Byte count */
|
||
|
|
||
|
int i, j; /* Counter */
|
||
|
|
||
|
int x, y; /* Current scanline */
|
||
|
|
||
|
|
||
|
|
||
|
int cltInHeader; /* Get the CLT from the header */
|
||
|
|
||
|
int cltAtEnd; /* Get the CLT from the EOF */
|
||
|
|
||
|
|
||
|
|
||
|
long cltFilePosition; /* File position of CLT */
|
||
|
|
||
|
long imageFilePosition; /* File position of Image */
|
||
|
|
||
|
char message[100]; /* ImInfo message */
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
|
||
|
* Read in the header in LBF (Intel) byte order. Check that it is
|
||
|
|
||
|
* valid.
|
||
|
|
||
|
*/
|
||
|
|
||
|
BinByteOrder( BINLBF );
|
||
|
|
||
|
if ( ImBinReadStruct( ioType, fd, fp, &header, imPcxHeaderFields )== -1)
|
||
|
|
||
|
ImReturnBinError( );
|
||
|
|
||
|
|
||
|
|
||
|
if ( header.pcx_manufacturer != IMPCXMANUFACTURER )
|
||
|
|
||
|
ImErrorFatal( "Bad manufacturer code in PCX file header", -1, IMESYNTAX );
|
||
|
|
||
|
|
||
|
|
||
|
switch ( header.pcx_version )
|
||
|
|
||
|
{
|
||
|
|
||
|
case IMPCXV_2_5:
|
||
|
|
||
|
ImInfo( "Version", "2.5" );
|
||
|
|
||
|
break;
|
||
|
|
||
|
case IMPCXV_2_8W:
|
||
|
|
||
|
ImInfo( "Version", "2.8 with Palette" );
|
||
|
|
||
|
break;
|
||
|
|
||
|
case IMPCXV_2_8WO:
|
||
|
|
||
|
ImInfo( "Version", "2.8 without Palette" );
|
||
|
|
||
|
break;
|
||
|
|
||
|
case IMPCXV_3_0:
|
||
|
|
||
|
ImInfo( "Version", "3.0" );
|
||
|
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
|
||
|
ImErrorFatal( "Unknown file version in PCX file header", -1, IMESYNTAX );
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
if ( header.pcx_encoding != IMPCXENCODING )
|
||
|
|
||
|
ImErrorFatal( "Unknown encoding method in PCX file header", -1, IMESYNTAX );
|
||
|
|
||
|
|
||
|
|
||
|
switch ( header.pcx_bitsPerPixel )
|
||
|
|
||
|
{
|
||
|
|
||
|
case 1: /* 1-bit monochrome (CGA) */
|
||
|
|
||
|
/* 2-bit color (EGA) */
|
||
|
|
||
|
/* 3-bit color (EGA) */
|
||
|
|
||
|
/* 4-bit color (EGA) */
|
||
|
|
||
|
if ( header.pcx_nPlanes > 4 )
|
||
|
|
||
|
ImErrorFatal( "Bad color plane count in PCX file header", -1, IMESYNTAX );
|
||
|
|
||
|
if ( header.pcx_version == IMPCXV_2_8WO )
|
||
|
|
||
|
cltInHeader = FALSE;
|
||
|
|
||
|
else
|
||
|
|
||
|
cltInHeader = TRUE;
|
||
|
|
||
|
cltAtEnd = FALSE;
|
||
|
|
||
|
break;
|
||
|
|
||
|
|
||
|
|
||
|
case 8: /* 8-bit color (VGA) */
|
||
|
|
||
|
if ( header.pcx_nPlanes != 1 )
|
||
|
|
||
|
ImErrorFatal( "Bad color plane count in PCX file header", -1, IMESYNTAX );
|
||
|
|
||
|
cltInHeader = FALSE;
|
||
|
|
||
|
cltAtEnd = TRUE;
|
||
|
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
|
||
|
ImErrorFatal( "Unknown image depth. Must be 1, 4, or 8.", -1, IMESYNTAX );
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
width = header.pcx_window[2] - header.pcx_window[0] + 1;
|
||
|
|
||
|
height = header.pcx_window[3] - header.pcx_window[1] + 1;
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
switch ( header.pcx_cltType )
|
||
|
|
||
|
{
|
||
|
|
||
|
case 0: /* For backwards compatibility */
|
||
|
|
||
|
case IMPCXCOLOR:
|
||
|
|
||
|
case IMPCXGRAY:
|
||
|
|
||
|
case IMPCXPAINTBRUSH:
|
||
|
|
||
|
break;
|
||
|
|
||
|
|
||
|
|
||
|
default:
|
||
|
|
||
|
ImErrorFatal( "Unknown palette type in PCX file header", -1, IMESYNTAX );
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
ImInfo( "Byte Order", "Least Significant Byte First" );
|
||
|
|
||
|
|
||
|
|
||
|
sprintf( message, "%d x %d", width, height );
|
||
|
|
||
|
ImInfo( "Resolution", message );
|
||
|
|
||
|
|
||
|
|
||
|
sprintf( message, "%d-bit Color Indexed", header.pcx_bitsPerPixel);
|
||
|
|
||
|
ImInfo( "Type", message );
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
|
||
|
* Make a CLT if we need one.
|
||
|
|
||
|
*/
|
||
|
|
||
|
clt = IMCLTNULL;
|
||
|
|
||
|
if ( cltInHeader )
|
||
|
|
||
|
{
|
||
|
|
||
|
/* 16-color CLT included in header. */
|
||
|
|
||
|
if ( (clt = ImCltAlloc( 16 )) == IMCLTNULL )
|
||
|
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
|
||
|
pColor = ImCltQFirst( clt );
|
||
|
|
||
|
for ( i = 0; i < 16; i++ )
|
||
|
|
||
|
{
|
||
|
|
||
|
ImCltSRed( pColor, header.pcx_clt[i][0] );
|
||
|
|
||
|
ImCltSGreen( pColor, header.pcx_clt[i][1] );
|
||
|
|
||
|
ImCltSBlue( pColor, header.pcx_clt[i][2] );
|
||
|
|
||
|
ImCltSInc( clt, pColor );
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
ImInfo( "Color Table", "16 Entries" );
|
||
|
|
||
|
}
|
||
|
|
||
|
else if ( cltAtEnd )
|
||
|
|
||
|
{
|
||
|
|
||
|
/*
|
||
|
|
||
|
* Seek to 769 bytes from the bottom and see if there
|
||
|
|
||
|
* is a 0x0C there. If so, we've got a 256 entry palette.
|
||
|
|
||
|
*
|
||
|
|
||
|
* HOWEVER, there is the chance that seeking to 769 bytes
|
||
|
|
||
|
* from the end will put us within the image data if there
|
||
|
|
||
|
* really isn't a CLT. If that next image data byte just
|
||
|
|
||
|
* happened to be a 0x0C, then we'd falsely conclude there
|
||
|
|
||
|
* was a CLT. This is a problem in the PCX spec.
|
||
|
|
||
|
*
|
||
|
|
||
|
* To handle this case, we remember where things are and
|
||
|
|
||
|
* later compare where we got the alleged CLT versus the
|
||
|
|
||
|
* end of the image data. If it is too early in the file,
|
||
|
|
||
|
* we were fooled and we get rid of the bogus CLT.
|
||
|
|
||
|
*/
|
||
|
|
||
|
imageFilePosition = ImTell( ioType, fd, fp );
|
||
|
|
||
|
ImSeek( ioType, fd, fp, -769, 2 );
|
||
|
|
||
|
cltFilePosition = ImTell( ioType, fd, fp );
|
||
|
|
||
|
n = ImBinRead( ioType, fd, fp, cltBuffer, UCHAR, 1, 769 );
|
||
|
|
||
|
if ( n != -1 && *cltBuffer == 0x0C )
|
||
|
|
||
|
{
|
||
|
|
||
|
if ( (clt = ImCltAlloc( 256 )) == IMCLTNULL )
|
||
|
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
|
||
|
pColor = ImCltQFirst( clt );
|
||
|
|
||
|
pCltBuffer = cltBuffer + 1;
|
||
|
|
||
|
for ( i = 0; i < 256; i++ )
|
||
|
|
||
|
{
|
||
|
|
||
|
ImCltSRed( pColor, *pCltBuffer++ );
|
||
|
|
||
|
ImCltSGreen( pColor, *pCltBuffer++ );
|
||
|
|
||
|
ImCltSBlue( pColor, *pCltBuffer++ );
|
||
|
|
||
|
ImCltSInc( clt, pColor );
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
ImInfo( "Color Table", "256 Entries" );
|
||
|
|
||
|
}
|
||
|
|
||
|
else
|
||
|
|
||
|
{
|
||
|
|
||
|
ImInfo( "Color Table", "none" );
|
||
|
|
||
|
}
|
||
|
|
||
|
ImSeek( ioType,fd, fp, IMPCXHEADERSIZE, 0 );
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
ImInfo( "Compression Type", "Run Length Encoded (RLE)" );
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
|
||
|
* Read in the image data. Unfortunately, because it is encoded,
|
||
|
|
||
|
* we don't know how much data there is. We do know our worst
|
||
|
|
||
|
* case encoding could produce a file twice the size of the image.
|
||
|
|
||
|
* So, we allocate a buffer that big and try to read that much
|
||
|
|
||
|
* data. It is very likely that BinRead will return less than
|
||
|
|
||
|
* that. Fine. We then decode that buffer into a new buffer
|
||
|
|
||
|
* that is the size of the image (in bytes).
|
||
|
|
||
|
*/
|
||
|
|
||
|
n = header.pcx_bytesPerLine * header.pcx_nPlanes * height;
|
||
|
|
||
|
ImMalloc( encoded, unsigned char *, 2 * n );
|
||
|
|
||
|
ImMalloc( decoded, unsigned char *, n );
|
||
|
|
||
|
|
||
|
|
||
|
if ( ImBinRead( ioType, fd, fp, encoded, UCHAR, 1, 2 * n ) == -1 )
|
||
|
|
||
|
{
|
||
|
|
||
|
free( (char *)encoded );
|
||
|
|
||
|
free( (char *)decoded );
|
||
|
|
||
|
if ( clt != IMCLTNULL )
|
||
|
|
||
|
ImCltFree( clt );
|
||
|
|
||
|
ImReturnBinError( );
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
n = imPcxDecode( encoded, n, decoded );
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
|
||
|
* If the location at which we found the CLT is within the image
|
||
|
|
||
|
* data we just read in, then there wasn't really a CLT there at
|
||
|
|
||
|
* all. It just happened that seeking to 769 bytes from the end
|
||
|
|
||
|
* fell upon a 0x0C within the image data, and we falsly concluded
|
||
|
|
||
|
* there was a CLT. We fix our mistake now.
|
||
|
|
||
|
*/
|
||
|
|
||
|
if ( cltAtEnd && imageFilePosition + n > cltFilePosition )
|
||
|
|
||
|
{
|
||
|
|
||
|
ImCltFree( clt );
|
||
|
|
||
|
clt = IMCLTNULL;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
|
||
|
* Walk the decoded buffer and expand it into a new VFB.
|
||
|
|
||
|
*/
|
||
|
|
||
|
if ( header.pcx_bitsPerPixel == 1 && header.pcx_nPlanes == 1 )
|
||
|
|
||
|
{
|
||
|
|
||
|
/*
|
||
|
|
||
|
* Monochrome. The decoded buffer contains packed bytes,
|
||
|
|
||
|
* each holding 8 1-bit pixel values. Watch for widths that
|
||
|
|
||
|
* are not a multiple of 8, and remember to toss an extra
|
||
|
|
||
|
* byte at the end of each scanline if the width (in bytes)
|
||
|
|
||
|
* is not even.
|
||
|
|
||
|
*/
|
||
|
|
||
|
if ( (vfb=ImVfbAlloc( width, height, IMVFBMONO )) == IMVFBNULL )
|
||
|
|
||
|
{
|
||
|
|
||
|
free( (char *)encoded );
|
||
|
|
||
|
free( (char *)decoded );
|
||
|
|
||
|
if ( clt != IMCLTNULL )
|
||
|
|
||
|
ImCltFree( clt );
|
||
|
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
pPixel = ImVfbQFirst( vfb );
|
||
|
|
||
|
pDecoded = decoded;
|
||
|
|
||
|
width8 = width / 8; /* Width in bytes*/
|
||
|
|
||
|
widthextra = width & 0xF; /* width % 8 */
|
||
|
|
||
|
for ( y = 0; y < height; y++ )
|
||
|
|
||
|
{
|
||
|
|
||
|
for ( x = 0; x < width8; x++ )
|
||
|
|
||
|
{
|
||
|
|
||
|
value = *pDecoded++;
|
||
|
|
||
|
for ( i = 7; i >= 0; i-- )
|
||
|
|
||
|
{
|
||
|
|
||
|
ImVfbSMono( vfb, pPixel,
|
||
|
|
||
|
(value>>i) & 0x1 );
|
||
|
|
||
|
ImVfbSInc( vfb, pPixel );
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
if ( widthextra )
|
||
|
|
||
|
{
|
||
|
|
||
|
value = *pDecoded++;
|
||
|
|
||
|
for ( i = 7; i >= (8-widthextra); i-- )
|
||
|
|
||
|
{
|
||
|
|
||
|
ImVfbSMono( vfb, pPixel,
|
||
|
|
||
|
(value>>i) & 0x1 );
|
||
|
|
||
|
ImVfbSInc( vfb, pPixel );
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
|
||
|
* According to the spec, we only have to skip a
|
||
|
|
||
|
* byte if the width is odd. However, some packages
|
||
|
|
||
|
* don't adhear to the even-byte-count requirement
|
||
|
|
||
|
* (such as PBM+). So, we only skip if the header
|
||
|
|
||
|
* says we should, and then as much as it says we
|
||
|
|
||
|
* should.
|
||
|
|
||
|
*/
|
||
|
|
||
|
for ( i = width8 + (widthextra?1:0);
|
||
|
|
||
|
i < header.pcx_bytesPerLine; i++ )
|
||
|
|
||
|
pDecoded++; /* Skip */
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
else if ( header.pcx_bitsPerPixel == 1 )
|
||
|
|
||
|
{
|
||
|
|
||
|
/*
|
||
|
|
||
|
* n-bit color (usually 4-bit). The decoded buffer contains
|
||
|
|
||
|
* packed bytes, each holding 8 1-bit pixel values... yes,
|
||
|
|
||
|
* 1-bit pixel values! Each scanline is stored as nPlanes
|
||
|
|
||
|
* of 1-bit planes. We need to remember to toss an extra
|
||
|
|
||
|
* byte per set if each scanline plane set's byte count is
|
||
|
|
||
|
* not even.
|
||
|
|
||
|
*/
|
||
|
|
||
|
if ( (vfb=ImVfbAlloc( width, height, IMVFBINDEX8 ))==IMVFBNULL )
|
||
|
|
||
|
{
|
||
|
|
||
|
free( (char *)encoded );
|
||
|
|
||
|
free( (char *)decoded );
|
||
|
|
||
|
if ( clt != IMCLTNULL )
|
||
|
|
||
|
ImCltFree( clt );
|
||
|
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
pDecoded = decoded;
|
||
|
|
||
|
width8 = width / 8; /* Width in bytes*/
|
||
|
|
||
|
widthextra = width & 0xF; /* width % 8 */
|
||
|
|
||
|
for ( y = 0; y < height; y++ )
|
||
|
|
||
|
{
|
||
|
|
||
|
/* First plane. */
|
||
|
|
||
|
pPixel = ImVfbQPtr( vfb, 0, y );
|
||
|
|
||
|
for ( x = 0; x < width8; x++ )
|
||
|
|
||
|
{
|
||
|
|
||
|
value = *pDecoded++;
|
||
|
|
||
|
for ( i = 7; i >= 0; i-- )
|
||
|
|
||
|
{
|
||
|
|
||
|
ImVfbSIndex8( vfb, pPixel,
|
||
|
|
||
|
((value>>i) & 0x1) );
|
||
|
|
||
|
ImVfbSInc( vfb, pPixel );
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
if ( widthextra )
|
||
|
|
||
|
{
|
||
|
|
||
|
value = *pDecoded++;
|
||
|
|
||
|
for ( i = 7; i >= (8-widthextra); i-- )
|
||
|
|
||
|
{
|
||
|
|
||
|
ImVfbSIndex8( vfb, pPixel,
|
||
|
|
||
|
((value>>i) & 0x1) );
|
||
|
|
||
|
ImVfbSInc( vfb, pPixel );
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/* Additional planes. */
|
||
|
|
||
|
for ( j = 1; j < header.pcx_nPlanes; j++ )
|
||
|
|
||
|
{
|
||
|
|
||
|
pPixel = ImVfbQPtr( vfb, 0, y );
|
||
|
|
||
|
for ( x = 0; x < width8; x++ )
|
||
|
|
||
|
{
|
||
|
|
||
|
value = *pDecoded++;
|
||
|
|
||
|
for ( i = 7; i >= 0; i-- )
|
||
|
|
||
|
{
|
||
|
|
||
|
value2 = ImVfbQIndex8( vfb,
|
||
|
|
||
|
pPixel );
|
||
|
|
||
|
ImVfbSIndex8( vfb, pPixel,
|
||
|
|
||
|
value2 |
|
||
|
|
||
|
((value>>i) & 0x1)<<j );
|
||
|
|
||
|
ImVfbSInc( vfb, pPixel );
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
if ( widthextra )
|
||
|
|
||
|
{
|
||
|
|
||
|
value = *pDecoded++;
|
||
|
|
||
|
for ( i = 7; i >= (8-widthextra); i-- )
|
||
|
|
||
|
{
|
||
|
|
||
|
value2 = ImVfbQIndex8( vfb,
|
||
|
|
||
|
pPixel );
|
||
|
|
||
|
ImVfbSIndex8( vfb, pPixel,
|
||
|
|
||
|
value2 |
|
||
|
|
||
|
((value>>i) & 0x1)<<j );
|
||
|
|
||
|
ImVfbSInc( vfb, pPixel );
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
|
||
|
* According to the spec, we only have to skip a
|
||
|
|
||
|
* byte if the width is odd (note: width includes
|
||
|
|
||
|
* bytes for all the planes for one scanline).
|
||
|
|
||
|
* However, some packages don't adhear to the
|
||
|
|
||
|
* even-byte-count requirement (such as PBM+). So
|
||
|
|
||
|
* we only skip if the header says we should, and
|
||
|
|
||
|
* then as much as it says we should.
|
||
|
|
||
|
*/
|
||
|
|
||
|
for ( i = (width8+(widthextra?1:0))*header.pcx_nPlanes;
|
||
|
|
||
|
i < header.pcx_bytesPerLine; i++ )
|
||
|
|
||
|
pDecoded++; /* Skip */
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
else
|
||
|
|
||
|
{
|
||
|
|
||
|
/*
|
||
|
|
||
|
* 8-bit color. The decoded buffer contains one byte per
|
||
|
|
||
|
* pixel. We just copy it out and into the VFB. We need
|
||
|
|
||
|
* to toss a byte for each scanline if the width is not
|
||
|
|
||
|
* even.
|
||
|
|
||
|
*/
|
||
|
|
||
|
if ( (vfb=ImVfbAlloc( width, height, IMVFBINDEX8 ))==IMVFBNULL )
|
||
|
|
||
|
{
|
||
|
|
||
|
free( (char *)encoded );
|
||
|
|
||
|
free( (char *)decoded );
|
||
|
|
||
|
if ( clt != IMCLTNULL )
|
||
|
|
||
|
ImCltFree( clt );
|
||
|
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
pPixel = ImVfbQFirst( vfb );
|
||
|
|
||
|
pDecoded = decoded;
|
||
|
|
||
|
for ( y = 0; y < height; y++ )
|
||
|
|
||
|
{
|
||
|
|
||
|
for ( x = 0; x < width; x++ )
|
||
|
|
||
|
{
|
||
|
|
||
|
ImVfbSIndex8( vfb, pPixel, *pDecoded++ );
|
||
|
|
||
|
ImVfbSInc( vfb, pPixel );
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
|
||
|
* According to the spec, we only have to skip a
|
||
|
|
||
|
* byte if the width is odd. However, some packages
|
||
|
|
||
|
* don't adhear to the even-byte-count requirement
|
||
|
|
||
|
* (such as PBM+). So, we only skip if the header
|
||
|
|
||
|
* says we should, and then as much as it says we
|
||
|
|
||
|
* should.
|
||
|
|
||
|
*/
|
||
|
|
||
|
for ( i = width; i < header.pcx_bytesPerLine; i++ )
|
||
|
|
||
|
*pDecoded++; /* Skip */
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
|
||
|
* Clean up.
|
||
|
|
||
|
*/
|
||
|
|
||
|
free( (char *)encoded );
|
||
|
|
||
|
free( (char *)decoded );
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
|
||
|
* Add the VFB to the tagTable.
|
||
|
|
||
|
*/
|
||
|
|
||
|
if ( clt != IMCLTNULL )
|
||
|
|
||
|
{
|
||
|
|
||
|
ImVfbSClt( vfb, clt );
|
||
|
|
||
|
TagTableAppend( tagTable,
|
||
|
|
||
|
TagEntryAlloc( "image clt", POINTER, &clt ) );
|
||
|
|
||
|
TagTableAppend( tagTable,
|
||
|
|
||
|
TagEntryAlloc( "image vfb", POINTER, &vfb ) );
|
||
|
|
||
|
return ( 2 );
|
||
|
|
||
|
}
|
||
|
|
||
|
TagTableAppend( tagTable,
|
||
|
|
||
|
TagEntryAlloc( "image vfb", POINTER, &vfb ) );
|
||
|
|
||
|
return ( 1 );
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
|
||
|
* FUNCTION
|
||
|
|
||
|
* imPcxWrite1 - write a 1-bit PCX file
|
||
|
|
||
|
*
|
||
|
|
||
|
* DESCRIPTION
|
||
|
|
||
|
* The PCX file header set up and written out followed by the encoded
|
||
|
|
||
|
* image data.
|
||
|
|
||
|
*/
|
||
|
|
||
|
|
||
|
|
||
|
static int /* Returns # of tags used */
|
||
|
|
||
|
#ifdef __STDC__
|
||
|
|
||
|
imPcxWrite1( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable )
|
||
|
|
||
|
#else
|
||
|
|
||
|
imPcxWrite1( pMap, ioType, fd, fp, flagsTable, tagTable )
|
||
|
|
||
|
ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */
|
||
|
|
||
|
int ioType; /* I/O flags */
|
||
|
|
||
|
int fd; /* Input file descriptor */
|
||
|
|
||
|
FILE *fp; /* Input file pointer */
|
||
|
|
||
|
TagTable *flagsTable; /* Flags */
|
||
|
|
||
|
TagTable *tagTable; /* Tag table to read from */
|
||
|
|
||
|
#endif
|
||
|
|
||
|
{
|
||
|
|
||
|
ImVfb *vfb; /* Read in image */
|
||
|
|
||
|
ImVfbPtr pPixel; /* Pixel pointer */
|
||
|
|
||
|
|
||
|
|
||
|
imPcxHeaderInfo header; /* PCX file header */
|
||
|
|
||
|
|
||
|
|
||
|
unsigned char *encoded; /* Encoded data buffer */
|
||
|
|
||
|
unsigned char *decoded; /* Decoded data buffer */
|
||
|
|
||
|
unsigned char *pDecoded; /* Encoded buffer pointer */
|
||
|
|
||
|
|
||
|
|
||
|
int n; /* Byte count */
|
||
|
|
||
|
int x, y; /* Pixel counters */
|
||
|
|
||
|
int i; /* Counter */
|
||
|
|
||
|
int value; /* Pixel value */
|
||
|
|
||
|
int height, width; /* Image size */
|
||
|
|
||
|
int width8; /* Width in bytes */
|
||
|
|
||
|
int widthextra; /* Extra bits in last byte */
|
||
|
|
||
|
char message[100]; /* ImInfo message */
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
|
||
|
* Set up the header and write it out.
|
||
|
|
||
|
*/
|
||
|
|
||
|
TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb );
|
||
|
|
||
|
width = ImVfbQWidth( vfb );
|
||
|
|
||
|
height = ImVfbQHeight( vfb );
|
||
|
|
||
|
|
||
|
|
||
|
BinByteOrder( BINLBF );
|
||
|
|
||
|
|
||
|
|
||
|
memset( (void *)&header, 0x00, sizeof( header ) );
|
||
|
|
||
|
header.pcx_manufacturer = IMPCXMANUFACTURER;
|
||
|
|
||
|
header.pcx_version = IMPCXV_2_5; /* No color */
|
||
|
|
||
|
header.pcx_encoding = IMPCXENCODING;
|
||
|
|
||
|
header.pcx_bitsPerPixel = 1; /* Monochrome */
|
||
|
|
||
|
header.pcx_window[0] = 0; /* xmin */
|
||
|
|
||
|
header.pcx_window[1] = 0; /* ymin */
|
||
|
|
||
|
header.pcx_window[2] = width - 1; /* xmax (minus 1) */
|
||
|
|
||
|
header.pcx_window[3] = height - 1; /* ymax (minus 1) */
|
||
|
|
||
|
header.pcx_nPlanes = 1; /* Monochrome */
|
||
|
|
||
|
header.pcx_bytesPerLine = (width + 7)/8;
|
||
|
|
||
|
if ( header.pcx_bytesPerLine & 0x1 )
|
||
|
|
||
|
++header.pcx_bytesPerLine; /* Make it even */
|
||
|
|
||
|
header.pcx_cltType = 0; /* Not relevant */
|
||
|
|
||
|
|
||
|
|
||
|
header.pcx_clt[0][0] = 0; /* Black */
|
||
|
|
||
|
header.pcx_clt[0][1] = 0;
|
||
|
|
||
|
header.pcx_clt[0][2] = 0;
|
||
|
|
||
|
header.pcx_clt[1][0] = 255; /* White */
|
||
|
|
||
|
header.pcx_clt[1][1] = 255;
|
||
|
|
||
|
header.pcx_clt[1][2] = 255;
|
||
|
|
||
|
|
||
|
|
||
|
/* Remaining fields left zero. */
|
||
|
|
||
|
|
||
|
|
||
|
if ( ImBinWriteStruct( ioType, fd, fp, &header, imPcxHeaderFields )==-1)
|
||
|
|
||
|
{
|
||
|
|
||
|
ImReturnBinError( );
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
|
||
|
* Output -verbose message
|
||
|
|
||
|
*/
|
||
|
|
||
|
ImInfo( "Version", "2.5" );
|
||
|
|
||
|
ImInfo( "Byte Order", "Least Significant Byte First" );
|
||
|
|
||
|
|
||
|
|
||
|
sprintf( message, "%d x %d", width, height );
|
||
|
|
||
|
ImInfo( "Resolution", message);
|
||
|
|
||
|
|
||
|
|
||
|
ImInfo( "Type", "1-bit Color Indexed" );
|
||
|
|
||
|
ImInfo( "Color Table", "16 Entries" );
|
||
|
|
||
|
ImInfo( "Compression Type", "Run Length Encoded (RLE)" );
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
|
||
|
* Buffer up, encode, and write out the image data. Remember that
|
||
|
|
||
|
* an encoded buffer could take up twice as much space as the
|
||
|
|
||
|
* decoded one, in the pathelogical case.
|
||
|
|
||
|
*/
|
||
|
|
||
|
ImMalloc( decoded, unsigned char *, header.pcx_bytesPerLine );
|
||
|
|
||
|
ImMalloc( encoded, unsigned char *, 2 * header.pcx_bytesPerLine );
|
||
|
|
||
|
memset( (void *)decoded, 0x00, header.pcx_bytesPerLine );
|
||
|
|
||
|
|
||
|
|
||
|
width8 = width / 8; /* # of bytes */
|
||
|
|
||
|
widthextra = width & 0xF; /* width % 8 */
|
||
|
|
||
|
|
||
|
|
||
|
pPixel = ImVfbQFirst( vfb );
|
||
|
|
||
|
for ( y = 0; y < height; y++ )
|
||
|
|
||
|
{
|
||
|
|
||
|
pDecoded = decoded;
|
||
|
|
||
|
for ( x = 0; x < width8; x++ )
|
||
|
|
||
|
{
|
||
|
|
||
|
value = 0;
|
||
|
|
||
|
for ( i = 7; i >= 0; i-- )
|
||
|
|
||
|
{
|
||
|
|
||
|
value |= (ImVfbQMono( vfb, pPixel ) & 0x1) << i;
|
||
|
|
||
|
ImVfbSInc( vfb, pPixel );
|
||
|
|
||
|
}
|
||
|
|
||
|
*pDecoded++ = value;
|
||
|
|
||
|
}
|
||
|
|
||
|
if ( widthextra )
|
||
|
|
||
|
{
|
||
|
|
||
|
value = 0;
|
||
|
|
||
|
for ( i = 7; i >= (8-widthextra); i-- )
|
||
|
|
||
|
{
|
||
|
|
||
|
value |= (ImVfbQMono( vfb, pPixel ) & 0x1) << i;
|
||
|
|
||
|
ImVfbSInc( vfb, pPixel );
|
||
|
|
||
|
}
|
||
|
|
||
|
*pDecoded++ = value;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
|
||
|
* Encoded it and write it out.
|
||
|
|
||
|
*/
|
||
|
|
||
|
n = imPcxEncode( decoded, header.pcx_bytesPerLine, encoded );
|
||
|
|
||
|
if ( ImBinWrite( ioType, fd, fp, encoded, UCHAR, 1, n ) == -1 )
|
||
|
|
||
|
{
|
||
|
|
||
|
free( (char *)encoded );
|
||
|
|
||
|
free( (char *)decoded );
|
||
|
|
||
|
ImReturnBinError( );
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
free( (char *)encoded );
|
||
|
|
||
|
free( (char *)decoded );
|
||
|
|
||
|
return ( 1 );
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
|
||
|
* FUNCTION
|
||
|
|
||
|
* imPcxWrite8 - write an 8-bit PCX file
|
||
|
|
||
|
*
|
||
|
|
||
|
* DESCRIPTION
|
||
|
|
||
|
* The PCX file header set up and written out followed by the encoded
|
||
|
|
||
|
* image data.
|
||
|
|
||
|
*/
|
||
|
|
||
|
|
||
|
|
||
|
static int /* Returns # of tags used */
|
||
|
|
||
|
#ifdef __STDC__
|
||
|
|
||
|
imPcxWrite8( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp, TagTable *flagsTable, TagTable *tagTable )
|
||
|
|
||
|
#else
|
||
|
|
||
|
imPcxWrite8( pMap, ioType, fd, fp, flagsTable, tagTable )
|
||
|
|
||
|
ImFileFormatWriteMap *pMap; /* Write map entry to adhear to */
|
||
|
|
||
|
int ioType; /* I/O flags */
|
||
|
|
||
|
int fd; /* Input file descriptor */
|
||
|
|
||
|
FILE *fp; /* Input file pointer */
|
||
|
|
||
|
TagTable *flagsTable; /* Flags */
|
||
|
|
||
|
TagTable *tagTable; /* Tag table to read from */
|
||
|
|
||
|
#endif
|
||
|
|
||
|
{
|
||
|
|
||
|
ImVfb *vfb; /* Read in image */
|
||
|
|
||
|
ImVfbPtr pPixel; /* Pixel pointer */
|
||
|
|
||
|
|
||
|
|
||
|
ImClt *clt; /* Color lookup table */
|
||
|
|
||
|
ImCltPtr pColor; /* Color pointer */
|
||
|
|
||
|
|
||
|
|
||
|
imPcxHeaderInfo header; /* PCX file header */
|
||
|
|
||
|
|
||
|
|
||
|
unsigned char *encoded; /* Encoded data buffer */
|
||
|
|
||
|
unsigned char *decoded; /* Decoded data buffer */
|
||
|
|
||
|
unsigned char *pDecoded; /* Encoded buffer pointer */
|
||
|
|
||
|
|
||
|
|
||
|
unsigned char *colorBuf; /* Color table output buffer */
|
||
|
|
||
|
unsigned char *pColorBuf; /* Color table buffer pointer */
|
||
|
|
||
|
|
||
|
|
||
|
int n; /* Byte count */
|
||
|
|
||
|
int i, j; /* Color counters */
|
||
|
|
||
|
int x, y; /* Pixel counters */
|
||
|
|
||
|
int height, width; /* Image size */
|
||
|
|
||
|
char message[100]; /* ImInfo message */
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
|
||
|
* Set up the header and write it out.
|
||
|
|
||
|
*/
|
||
|
|
||
|
TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &vfb );
|
||
|
|
||
|
clt = ImVfbQClt( vfb );
|
||
|
|
||
|
width = ImVfbQWidth( vfb );
|
||
|
|
||
|
height = ImVfbQHeight( vfb );
|
||
|
|
||
|
|
||
|
|
||
|
BinByteOrder( BINLBF );
|
||
|
|
||
|
|
||
|
|
||
|
memset( (void *)&header, 0x00, sizeof( header ) );
|
||
|
|
||
|
header.pcx_manufacturer = IMPCXMANUFACTURER;
|
||
|
|
||
|
header.pcx_version = IMPCXV_3_0; /* VGA color */
|
||
|
|
||
|
header.pcx_encoding = IMPCXENCODING;
|
||
|
|
||
|
header.pcx_bitsPerPixel = 8; /* 8-bit color */
|
||
|
|
||
|
header.pcx_window[0] = 0; /* xmin */
|
||
|
|
||
|
header.pcx_window[1] = 0; /* ymin */
|
||
|
|
||
|
header.pcx_window[2] = width - 1; /* xmax (minus 1) */
|
||
|
|
||
|
header.pcx_window[3] = height - 1; /* ymax (minus 1) */
|
||
|
|
||
|
header.pcx_nPlanes = 1; /* 1 color plane */
|
||
|
|
||
|
header.pcx_bytesPerLine = width;
|
||
|
|
||
|
if ( header.pcx_bytesPerLine & 0x1 )
|
||
|
|
||
|
++header.pcx_bytesPerLine; /* Make it even */
|
||
|
|
||
|
header.pcx_cltType = IMPCXCOLOR; /* Yup, its color */
|
||
|
|
||
|
|
||
|
|
||
|
/* Remaining fields left zero. */
|
||
|
|
||
|
|
||
|
|
||
|
if ( ImBinWriteStruct( ioType, fd, fp, &header, imPcxHeaderFields )==-1)
|
||
|
|
||
|
{
|
||
|
|
||
|
ImReturnBinError( );
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
|
||
|
* Output -verbose message
|
||
|
|
||
|
*/
|
||
|
|
||
|
ImInfo( "Version", "3.0" );
|
||
|
|
||
|
ImInfo( "Byte Order", "Least Significant Byte First" );
|
||
|
|
||
|
|
||
|
|
||
|
sprintf( message, "%d x %d", width, height );
|
||
|
|
||
|
ImInfo( "Resolution", message);
|
||
|
|
||
|
|
||
|
|
||
|
ImInfo( "Type", "8-bit Color Indexed" );
|
||
|
|
||
|
ImInfo( "Color Table", "256 Entries" );
|
||
|
|
||
|
ImInfo( "Compression Type", "Run Length Encoded (RLE)" );
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
|
||
|
* Buffer up, encode, and write out the image data. Remember that
|
||
|
|
||
|
* an encoded buffer could take up twice as much space as the
|
||
|
|
||
|
* decoded one, in the pathelogical case.
|
||
|
|
||
|
*/
|
||
|
|
||
|
ImMalloc( decoded, unsigned char *, header.pcx_bytesPerLine );
|
||
|
|
||
|
ImMalloc( encoded, unsigned char *, 2 * header.pcx_bytesPerLine );
|
||
|
|
||
|
memset( (void *)decoded, 0x00, header.pcx_bytesPerLine );
|
||
|
|
||
|
|
||
|
|
||
|
pPixel = ImVfbQFirst( vfb );
|
||
|
|
||
|
for ( y = 0; y < height; y++ )
|
||
|
|
||
|
{
|
||
|
|
||
|
pDecoded = decoded;
|
||
|
|
||
|
for ( x = 0; x < width; x++ )
|
||
|
|
||
|
{
|
||
|
|
||
|
*pDecoded++ = ImVfbQIndex8( vfb, pPixel ) & 0xFF;
|
||
|
|
||
|
ImVfbSInc( vfb, pPixel );
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
|
||
|
* Encoded it and write it out.
|
||
|
|
||
|
*/
|
||
|
|
||
|
n = imPcxEncode( decoded, header.pcx_bytesPerLine, encoded );
|
||
|
|
||
|
if ( ImBinWrite( ioType, fd, fp, encoded, UCHAR, 1, n ) == -1 )
|
||
|
|
||
|
{
|
||
|
|
||
|
free( (char *)encoded );
|
||
|
|
||
|
free( (char *)decoded );
|
||
|
|
||
|
ImReturnBinError( );
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
free( (char *)encoded );
|
||
|
|
||
|
free( (char *)decoded );
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
|
||
|
* Write out the color table (if any). Remember to start off the
|
||
|
|
||
|
* color table with the special "yes it's here" byte: 0x0C.
|
||
|
|
||
|
*/
|
||
|
|
||
|
if ( clt == IMCLTNULL )
|
||
|
|
||
|
return ( 1 ); /* No color table */
|
||
|
|
||
|
|
||
|
|
||
|
n = 256 * 3 + 1; /* Always 256 entries */
|
||
|
|
||
|
ImMalloc( colorBuf, unsigned char *, n );
|
||
|
|
||
|
memset( (void *)colorBuf, 0x00, n );
|
||
|
|
||
|
|
||
|
|
||
|
pColorBuf = colorBuf;
|
||
|
|
||
|
*pColorBuf++ = 0x0C; /* Yes, it's here */
|
||
|
|
||
|
|
||
|
|
||
|
/* Fill in with CLT entries from the VFB. */
|
||
|
|
||
|
i = ImCltQNColors( clt );
|
||
|
|
||
|
pColor = ImCltQFirst( clt );
|
||
|
|
||
|
for ( j = 0; j < i; j++ )
|
||
|
|
||
|
{
|
||
|
|
||
|
*pColorBuf++ = ImCltQRed( pColor ) & 0xFF;
|
||
|
|
||
|
*pColorBuf++ = ImCltQGreen( pColor ) & 0xFF;
|
||
|
|
||
|
*pColorBuf++ = ImCltQBlue( pColor ) & 0xFF;
|
||
|
|
||
|
ImCltSInc( clt, pColor );
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/* Pad to 256 entries. */
|
||
|
|
||
|
for ( ; j < 256; j++ )
|
||
|
|
||
|
{
|
||
|
|
||
|
*pColorBuf++ = 0;
|
||
|
|
||
|
*pColorBuf++ = 0;
|
||
|
|
||
|
*pColorBuf++ = 0;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/* Write it out. */
|
||
|
|
||
|
if ( ImBinWrite( ioType, fd, fp, colorBuf, UCHAR, 1, n ) == -1 )
|
||
|
|
||
|
{
|
||
|
|
||
|
free( (char *)colorBuf );
|
||
|
|
||
|
ImReturnBinError( );
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
free( (char *)colorBuf );
|
||
|
|
||
|
return ( 2 ); /* VFB + CLT */
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|