diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2006-08-19 03:12:28 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2006-08-19 03:12:28 +0000 |
commit | 1fd361a1ea06e44286c213ca1f814f49306fdc43 (patch) | |
tree | 64c8c96cf54d8718847339a403e5e67b922e8c3f /converter/ppm | |
download | netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.tar.gz netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.tar.xz netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.zip |
Create Subversion repository
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@1 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'converter/ppm')
325 files changed, 67176 insertions, 0 deletions
diff --git a/converter/ppm/411toppm.c b/converter/ppm/411toppm.c new file mode 100644 index 00000000..a5b25ac1 --- /dev/null +++ b/converter/ppm/411toppm.c @@ -0,0 +1,261 @@ +/* + I recently had a visit from my mom who owns a Sony Mavica camera. + This camera produces standard MPEG and JPEG files, but it also + creates 64x48 pixel thumbnails for preview/index on its own tiny + LCD screen. These files are named with an extension that is + ".411". + + Sony appears not to want to document the ".411" file format, but it + is clear from various web pages that it is a variant of the + CCIR.601 standard YUV encoding used in MPEG. The name indicates + that the file content consists of chunks of 6 bytes: 4 bytes of + image Y values, followed by 1 bytes of U and one byte of V values + that apply to the previous 4 Y pixel values. + + There appear to be some commercial 411 file readers on the net, and + there is the Java-based Javica program, but I prefer Open Source + command-line utilities. So, I grabbed a copy of netpbm-9.11 from + SourceForge and hacked the eyuvtoppm.c file so that it looks like + this. While this may not be exactly the right thing to do, it + produces results which are close enough for me. + + There are all sorts of user-interface gotchas possible in here that + I'm not going to bother changing -- especially not without actual + documentation from Sony about what they intend to do with ".411" + files in the future. I place my modifications into the public + domain, but I ask that my name & e-mail be mentioned in the + commentary of any derived version. + + Steve Allen <sla@alumni.caltech.edu>, 2001-03-01 + + Bryan Henderson reworked the program to use the Netpbm libraries to + create the PPM output and follow some other Netpbm conventions. + 2001-03-03. Bryan's contribution is public domain. +*/ +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. */ + +/*==============* + * HEADER FILES * + *==============*/ +#include <stdio.h> +#include <stdlib.h> + +#include "ppm.h" +#include "shhopt.h" +#include "mallocvar.h" + +typedef unsigned char uint8; + +#define CHOP(x) ((x < 0) ? 0 : ((x > 255) ? 255 : x)) + +struct cmdline_info { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char *input_filespec; /* Filespec of input file */ + int width; + int height; +}; + + + +static void +parse_command_line(int argc, char ** argv, + struct cmdline_info *cmdline_p) { +/*---------------------------------------------------------------------------- + Note that the file spec array we return is stored in the storage that + was passed to us as the argv array. +-----------------------------------------------------------------------------*/ + + optStruct *option_def = malloc(100*sizeof(optStruct)); + /* Instructions to OptParseOptions2 on how to parse our options. + */ + optStruct2 opt; + + unsigned int option_def_index; + + option_def_index = 0; /* incremented by OPTENTRY */ + OPTENTRY(0, "width", OPT_INT, &cmdline_p->width, 0); + OPTENTRY(0, "height", OPT_INT, &cmdline_p->height, 0); + + /* Set the defaults */ + cmdline_p->width = 64; + cmdline_p->height = 48; + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ + + optParseOptions2(&argc, argv, opt, 0); + /* Uses and sets argc, argv, and some of *cmdline_p and others. */ + + if (cmdline_p->width <= 0) + pm_error("-width must be positive."); + if (cmdline_p->height <= 0) + pm_error("-height must be positive."); + + if (argc > 2) + pm_error("There is at most 1 argument: the input file spec. " + "You supplied %d", argc-1); + else { + if (argc > 1) + cmdline_p->input_filespec = argv[1]; + else + cmdline_p->input_filespec = "-"; + } +} + + + +static void +ReadYUV(FILE *fpointer, const int width, const int height, + uint8 ** const orig_y, uint8 ** const orig_cb, + uint8 ** const orig_cr) { + + int y, x, i; + + for (y = 0; y < height; y++) { + for (x = 0, i = 0; x < width; x+=4, i++) { + fread(orig_y[y]+x , 1, 4, fpointer); + fread(orig_cb[y]+i, 1, 1, fpointer); + fread(orig_cr[y]+i, 1, 1, fpointer); + } + } +} + + + +static void +AllocYCC(const int width, const int height, + uint8 *** const orig_yP, uint8 *** const orig_crP, + uint8 *** const orig_cbP) { + int y; + + MALLOCARRAY_NOFAIL(*orig_yP, height); + for (y = 0; y < height; y++) + MALLOCARRAY_NOFAIL((*orig_yP)[y], width); + + MALLOCARRAY_NOFAIL(*orig_crP, height); + for (y = 0; y < height; y++) + MALLOCARRAY_NOFAIL((*orig_crP)[y], width/4); + + MALLOCARRAY_NOFAIL(*orig_cbP, height); + for (y = 0; y < height; y++) + MALLOCARRAY_NOFAIL((*orig_cbP)[y], width/4); +} + + + +static void +YUVtoPPM(const int width, const int height, + uint8 ** const orig_y, + uint8 ** const orig_cb, + uint8 ** const orig_cr, + pixel ** const ppm_image + ) { + int **Y, **U, **V; + int y; + + /* first, allocate tons of memory */ + + MALLOCARRAY_NOFAIL(Y, height); + for (y = 0; y < height; y++) + MALLOCARRAY_NOFAIL(Y[y], width); + + MALLOCARRAY_NOFAIL(U, height); + for (y = 0; y < height; y++) + MALLOCARRAY_NOFAIL(U[y], width / 4); + + MALLOCARRAY_NOFAIL(V, height); + for (y = 0; y < height; y++) + MALLOCARRAY_NOFAIL(V[y], width/4); + + for ( y = 0; y < height; y ++ ) { + int x; + for ( x = 0; x < width/4; x ++ ) { + U[y][x] = orig_cb[y][x] - 128; + V[y][x] = orig_cr[y][x] - 128; + } + } + for ( y = 0; y < height; y ++ ) { + int x; + for ( x = 0; x < width; x ++ ) + { + Y[y][x] = orig_y[y][x] - 16; + } + } + for ( y = 0; y < height; y++ ) { + int x; + for ( x = 0; x < width; x++ ) { + pixval r, g, b; + long tempR, tempG, tempB; + /* look at yuvtoppm source for explanation */ + + tempR = 104635*V[y][x/4]; + tempG = -25690*U[y][x/4] + -53294 * V[y][x/4]; + tempB = 132278*U[y][x/4]; + + tempR += (Y[y][x]*76310); + tempG += (Y[y][x]*76310); + tempB += (Y[y][x]*76310); + + r = CHOP((int)(tempR >> 16)); + g = CHOP((int)(tempG >> 16)); + b = CHOP((int)(tempB >> 16)); + + PPM_ASSIGN(ppm_image[y][x], r, g, b); + } + } + /* We really should free the Y, U, and V arrays now */ +} + + + +int +main(int argc, char **argv) { + FILE *infile; + struct cmdline_info cmdline; + uint8 **orig_y, **orig_cb, **orig_cr; + pixel **ppm_image; + + ppm_init(&argc, argv); + + parse_command_line(argc, argv, &cmdline); + + AllocYCC(cmdline.width, cmdline.height, &orig_y, &orig_cr, &orig_cb); + ppm_image = ppm_allocarray(cmdline.width, cmdline.height); + + pm_message("Reading (%dx%d): %s\n", cmdline.width, cmdline.height, + cmdline.input_filespec); + infile = pm_openr(cmdline.input_filespec); + ReadYUV(infile, cmdline.width, cmdline.height, orig_y, orig_cb, orig_cr); + pm_close(infile); + + YUVtoPPM(cmdline.width, cmdline.height, orig_y, orig_cb, orig_cr, + ppm_image); + + ppm_writeppm(stdout, ppm_image, cmdline.width, cmdline.height, 255, 0); + + ppm_freearray(ppm_image, cmdline.height); + + return 0; +} + diff --git a/converter/ppm/Makefile b/converter/ppm/Makefile new file mode 100644 index 00000000..31ca826b --- /dev/null +++ b/converter/ppm/Makefile @@ -0,0 +1,49 @@ +ifeq ($(SRCDIR)x,x) + SRCDIR = $(CURDIR)/../.. + BUILDDIR = $(SRCDIR) +endif +SUBDIR = converter/ppm +VPATH=.:$(SRCDIR)/$(SUBDIR) + +include $(BUILDDIR)/Makefile.config + +SUBDIRS = hpcdtoppm ppmtompeg + +PORTBINARIES = 411toppm eyuvtoppm gouldtoppm ilbmtoppm imgtoppm \ + leaftoppm mtvtoppm neotoppm \ + pcxtoppm pc1toppm pi1toppm picttoppm pjtoppm \ + ppmtoacad ppmtoarbtxt \ + ppmtobmp ppmtoeyuv ppmtogif ppmtoicr ppmtoilbm \ + ppmtoleaf ppmtolj ppmtomitsu ppmtoneo \ + ppmtopcx ppmtopi1 ppmtopict ppmtopj \ + ppmtopjxl ppmtoppm ppmtopuzz ppmtorgb3 ppmtosixel ppmtoterm \ + ppmtowinicon ppmtoxpm ppmtoyuv ppmtoyuvsplit \ + qrttoppm rawtoppm rgb3toppm spctoppm \ + sputoppm tgatoppm winicontoppm ximtoppm xpmtoppm xvminitoppm \ + yuvtoppm yuvsplittoppm + +MATHBINARIES = sldtoppm + +# We don't build vidtoppm by default, because it requires special libraries +# and there is no known requirement for vidtoppm. + + +MERGEBINARIES = $(PORTBINARIES) $(MATHBINARIES) +NOMERGEBINARIES = + +BINARIES = $(MERGEBINARIES) $(NOMERGEBINARIES) + +DATAFILES = pcxstd.ppm + +OBJECTS = $(BINARIES:%=%.o) + +MERGE_OBJECTS = $(BINARIES:%=%.o2) + +.PHONY: all +all: $(BINARIES) $(SUBDIRS:%=%/all) + +include $(SRCDIR)/Makefile.common + +ppmtobmp.o ppmtobmp.o2: bmp.h + +tgatoppm.o tgatoppm.o2: tga.h diff --git a/converter/ppm/autocad.h b/converter/ppm/autocad.h new file mode 100644 index 00000000..3005782b --- /dev/null +++ b/converter/ppm/autocad.h @@ -0,0 +1,64 @@ +#ifndef AUTOCAD_H_INCLUDED +#define AUTOCAD_H_INCLUDED +/* The following table maps the 256 standard AutoCAD colors into RGB + values with a Maxval of 255. These colors are actually derived in + an algorithmic way from the HLS color system, but it's easier and + faster to just provide a table than to compute the RGB components + on the fly from the color index. */ + +static unsigned char acadcol[256][3] = { + {0, 0, 0}, {255, 0, 0}, {255, 255, 0}, {0, 255, 0}, {0, 255, 255}, + {0, 0, 255}, {255, 0, 255}, {255, 255, 255}, {255, 255, 255}, + {255, 255, 255}, {255, 0, 0}, {255, 127, 127}, {165, 0, 0}, {165, 82, 82}, + {127, 0, 0}, {127, 63, 63}, {76, 0, 0}, {76, 38, 38}, {38, 0, 0}, + {38, 19, 19}, {255, 63, 0}, {255, 159, 127}, {165, 41, 0}, {165, 103, 82}, + {127, 31, 0}, {127, 79, 63}, {76, 19, 0}, {76, 47, 38}, {38, 9, 0}, + {38, 23, 19}, {255, 127, 0}, {255, 191, 127}, {165, 82, 0}, {165, 124, 82}, + {127, 63, 0}, {127, 95, 63}, {76, 38, 0}, {76, 57, 38}, {38, 19, 0}, + {38, 28, 19}, {255, 191, 0}, {255, 223, 127}, {165, 124, 0}, + {165, 145, 82}, {127, 95, 0}, {127, 111, 63}, {76, 57, 0}, {76, 66, 38}, + {38, 28, 0}, {38, 33, 19}, {255, 255, 0}, {255, 255, 127}, {165, 165, 0}, + {165, 165, 82}, {127, 127, 0}, {127, 127, 63}, {76, 76, 0}, {76, 76, 38}, + {38, 38, 0}, {38, 38, 19}, {191, 255, 0}, {223, 255, 127}, + {124, 165, 0}, {145, 165, 82}, {95, 127, 0}, {111, 127, 63}, {57, 76, 0}, + {66, 76, 38}, {28, 38, 0}, {33, 38, 19}, {127, 255, 0}, {191, 255, 127}, + {82, 165, 0}, {124, 165, 82}, {63, 127, 0}, {95, 127, 63}, {38, 76, 0}, + {57, 76, 38}, {19, 38, 0}, {28, 38, 19}, {63, 255, 0}, {159, 255, 127}, + {41, 165, 0}, {103, 165, 82}, {31, 127, 0}, {79, 127, 63}, {19, 76, 0}, + {47, 76, 38}, {9, 38, 0}, {23, 38, 19}, {0, 255, 0}, {127, 255, 127}, + {0, 165, 0}, {82, 165, 82}, {0, 127, 0}, {63, 127, 63}, {0, 76, 0}, + {38, 76, 38}, {0, 38, 0}, {19, 38, 19}, {0, 255, 63}, {127, 255, 159}, + {0, 165, 41}, {82, 165, 103}, {0, 127, 31}, {63, 127, 79}, {0, 76, 19}, + {38, 76, 47}, {0, 38, 9}, {19, 38, 23}, {0, 255, 127}, {127, 255, 191}, + {0, 165, 82}, {82, 165, 124}, {0, 127, 63}, {63, 127, 95}, {0, 76, 38}, + {38, 76, 57}, {0, 38, 19}, {19, 38, 28}, {0, 255, 191}, {127, 255, 223}, + {0, 165, 124}, {82, 165, 145}, {0, 127, 95}, {63, 127, 111}, {0, 76, 57}, + {38, 76, 66}, {0, 38, 28}, {19, 38, 33}, {0, 255, 255}, {127, 255, 255}, + {0, 165, 165}, {82, 165, 165}, {0, 127, 127}, {63, 127, 127}, {0, 76, 76}, + {38, 76, 76}, {0, 38, 38}, {19, 38, 38}, {0, 191, 255}, {127, 223, 255}, + {0, 124, 165}, {82, 145, 165}, {0, 95, 127}, {63, 111, 127}, {0, 57, 76}, + {38, 66, 76}, {0, 28, 38}, {19, 33, 38}, {0, 127, 255}, {127, 191, 255}, + {0, 82, 165}, {82, 124, 165}, {0, 63, 127}, {63, 95, 127}, {0, 38, 76}, + {38, 57, 76}, {0, 19, 38}, {19, 28, 38}, {0, 63, 255}, {127, 159, 255}, + {0, 41, 165}, {82, 103, 165}, {0, 31, 127}, {63, 79, 127}, {0, 19, 76}, + {38, 47, 76}, {0, 9, 38}, {19, 23, 38}, {0, 0, 255}, {127, 127, 255}, + {0, 0, 165}, {82, 82, 165}, {0, 0, 127}, {63, 63, 127}, {0, 0, 76}, + {38, 38, 76}, {0, 0, 38}, {19, 19, 38}, {63, 0, 255}, {159, 127, 255}, + {41, 0, 165}, {103, 82, 165}, {31, 0, 127}, {79, 63, 127}, {19, 0, 76}, + {47, 38, 76}, {9, 0, 38}, {23, 19, 38}, {127, 0, 255}, {191, 127, 255}, + {82, 0, 165}, {124, 82, 165}, {63, 0, 127}, {95, 63, 127}, {38, 0, 76}, + {57, 38, 76}, {19, 0, 38}, {28, 19, 38}, {191, 0, 255}, {223, 127, 255}, + {124, 0, 165}, {145, 82, 165}, {95, 0, 127}, {111, 63, 127}, {57, 0, 76}, + {66, 38, 76}, {28, 0, 38}, {33, 19, 38}, {255, 0, 255}, {255, 127, 255}, + {165, 0, 165}, {165, 82, 165}, {127, 0, 127}, {127, 63, 127}, {76, 0, 76}, + {76, 38, 76}, {38, 0, 38}, {38, 19, 38}, {255, 0, 191}, {255, 127, 223}, + {165, 0, 124}, {165, 82, 145}, {127, 0, 95}, {127, 63, 111}, {76, 0, 57}, + {76, 38, 66}, {38, 0, 28}, {38, 19, 33}, {255, 0, 127}, {255, 127, 191}, + {165, 0, 82}, {165, 82, 124}, {127, 0, 63}, {127, 63, 95}, {76, 0, 38}, + {76, 38, 57}, {38, 0, 19}, {38, 19, 28}, {255, 0, 63}, {255, 127, 159}, + {165, 0, 41}, {165, 82, 103}, {127, 0, 31}, {127, 63, 79}, {76, 0, 19}, + {76, 38, 47}, {38, 0, 9}, {38, 19, 23}, {84, 84, 84}, {118, 118, 118}, + {152, 152, 152}, {186, 186, 186}, {220, 220, 220}, {255, 255, 255} +}; + +#endif diff --git a/converter/ppm/eyuvtoppm.c b/converter/ppm/eyuvtoppm.c new file mode 100644 index 00000000..dcbb9547 --- /dev/null +++ b/converter/ppm/eyuvtoppm.c @@ -0,0 +1,338 @@ +/* Bryan got this from mm.ftp-cs.berkeley.edu from the package + mpeg-encode-1.5b-src under the name eyuvtoppm.c on March 30, 2000. + The file was dated April 14, 1995. + + Bryan rewrote the program entirely to match Netpbm coding style, + use the Netpbm libraries and also to output to stdout and ignore + any specification of an output file on the command line and not + segfault when called with no arguments. + + There was no attached documentation except for this: Encoder/Berkeley + YUV format is merely the concatenation of Y, U, and V data in order. + Compare with Abekda YUV, which interlaces Y, U, and V data. */ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include "ppm.h" +#include "shhopt.h" +#include "mallocvar.h" + +typedef unsigned char uint8; + +#define CHOP(x) ((x < 0) ? 0 : ((x > 255) ? 255 : x)) + + + +struct cmdline_info { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char *input_filespec; /* Filespecs of input file */ + unsigned int width; + unsigned int height; +}; + + + +static void +parseCommandLine(int argc, char ** argv, + struct cmdline_info *cmdlineP) { + + optStruct3 opt; /* Set by OPTENT3 */ + unsigned int option_def_index; + optEntry *option_def = malloc(100*sizeof(optEntry)); + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3('w', "width", OPT_UINT, &cmdlineP->width, NULL, 0); + OPTENT3('h', "height", OPT_UINT, &cmdlineP->height, NULL, 0); + + /* DEFAULTS */ + cmdlineP->width = 352; + cmdlineP->height = 240; + + opt.opt_table = option_def; + opt.short_allowed = TRUE; + opt.allowNegNum = FALSE; + + optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + + if (cmdlineP->width == 0) + pm_error("The width cannot be zero."); + if (cmdlineP->width % 2 != 0) + pm_error("The width of an eyuv image must be an even number. " + "You specified %u.", cmdlineP->width); + if (cmdlineP->height == 0) + pm_error("The height cannot be zero."); + if (cmdlineP->height % 2 != 0) + pm_error("The height of an eyuv image must be an even number. " + "You specified %u.", cmdlineP->height); + + + if (argc-1 == 0) + cmdlineP->input_filespec = "-"; + else if (argc-1 != 1) + pm_error("Program takes zero or one argument (filename). You " + "specified %d", argc-1); + else + cmdlineP->input_filespec = argv[1]; + +} + + + +static uint8 ** +AllocUint8Array(unsigned int const cols, unsigned int const rows) { + + uint8 **retval; + unsigned int y; + + MALLOCARRAY(retval, rows); + if (retval == NULL) + pm_error("Unable to allocate storage for %d x %d byte array.", + cols, rows); + + for (y = 0; y < rows; y++) { + MALLOCARRAY(retval[y], cols); + if (retval[y] == NULL) + pm_error("Unable to allocate storage for %d x %d byte array.", + cols, rows); + } + return retval; +} + + + +static int ** +AllocIntArray(unsigned int const cols, unsigned int const rows) { + + int **retval; + unsigned int y; + + MALLOCARRAY(retval, rows); + if (retval == NULL) + pm_error("Unable to allocate storage for %d x %d byte array.", + cols, rows); + + for (y = 0; y < rows; y++) { + MALLOCARRAY(retval[y], cols); + if (retval[y] == NULL) + pm_error("Unable to allocate storage for %d x %d byte array.", + cols, rows); + } + return retval; +} + + + +static void +allocateStorage(int const cols, int const rows, + int *** const YP, int *** const UP, int *** const VP, + pixel *** const pixelsP, + uint8 *** const orig_yP, uint8 *** const orig_cbP, + uint8 *** const orig_crP) { + + *YP = AllocIntArray(cols, rows); + *UP = AllocIntArray(cols, rows); + *VP = AllocIntArray(cols, rows); + + *pixelsP = ppm_allocarray(cols, rows); + + *orig_yP = AllocUint8Array(cols, rows); + *orig_cbP = AllocUint8Array(cols, rows); + *orig_crP = AllocUint8Array(cols, rows); +} + + + +static void +FreeArray(void ** const array, unsigned int const rows) { + + unsigned int y; + + for (y = 0; y < rows; y++) + free(array[y]); + free(array); +} + + + +static void +freeStorage(int const rows, + int ** const Y, int ** const U, int ** const V, + pixel ** const pixels, + uint8 ** const orig_y, uint8 ** const orig_cb, + uint8 ** const orig_cr) { + + FreeArray((void**) orig_y, rows); + FreeArray((void**) orig_cb, rows); + FreeArray((void**) orig_cr, rows); + + ppm_freearray(pixels, rows); + + FreeArray((void**) Y, rows); + FreeArray((void**) U, rows); + FreeArray((void**) V, rows); +} + + + +static void +YUVtoPPM(unsigned int const cols, unsigned int const rows, + uint8 ** const orig_y, uint8 ** const orig_cb, uint8 ** const orig_cr, + pixel ** const pixels, + int ** const Y, int ** const U, int ** const V) { +/*---------------------------------------------------------------------------- + Convert the YUV image in arrays orig_y[][], orig_cb[][], and orig_cr[][] + to a PPM image in the array (already allocated) pixels[][]. + + Use the preallocated areas Y[][], U[][], and V[][] for working space. +-----------------------------------------------------------------------------*/ + + int y; + + for ( y = 0; y < rows/2; y ++ ) { + int x; + for ( x = 0; x < cols/2; x ++ ) { + U[y][x] = orig_cb[y][x] - 128; + V[y][x] = orig_cr[y][x] - 128; + } + } + + for ( y = 0; y < rows; y ++ ) { + int x; + for ( x = 0; x < cols; x ++ ) + Y[y][x] = orig_y[y][x] - 16; + } + + for ( y = 0; y < rows; y++ ) { + int x; + for ( x = 0; x < cols; x++ ) { + long tempR, tempG, tempB; + int r, g, b; + /* look at yuvtoppm source for explanation */ + + tempR = 104635*V[y/2][x/2]; + tempG = -25690*U[y/2][x/2] + -53294 * V[y/2][x/2]; + tempB = 132278*U[y/2][x/2]; + + tempR += (Y[y][x]*76310); + tempG += (Y[y][x]*76310); + tempB += (Y[y][x]*76310); + + r = CHOP((int)(tempR >> 16)); + g = CHOP((int)(tempG >> 16)); + b = CHOP((int)(tempB >> 16)); + + PPM_ASSIGN(pixels[y][x], r, g, b); + } + } +} + + + +static void +ReadYUV(FILE * const yuvfile, + unsigned int const cols, unsigned int const rows, + uint8 ** const orig_y, + uint8 ** const orig_cb, + uint8 ** const orig_cr, + bool * const eofP) { + + unsigned int y; + int c; + + c = fgetc(yuvfile); + if (c < 0) + *eofP = TRUE; + else { + *eofP = FALSE; + ungetc(c, yuvfile); + } + if (!*eofP) { + for (y = 0; y < rows; y++) /* Y */ + fread(orig_y[y], 1, cols, yuvfile); + + for (y = 0; y < rows / 2; y++) /* U */ + fread(orig_cb[y], 1, cols / 2, yuvfile); + + for (y = 0; y < rows / 2; y++) /* V */ + fread(orig_cr[y], 1, cols / 2, yuvfile); + if (feof(yuvfile)) + pm_error("Premature end of file reading EYUV input file"); + } +} + + + +int +main(int argc, char **argv) { + + FILE *ifp; + struct cmdline_info cmdline; + unsigned int frameSeq; + + /* The following are addresses of malloc'ed storage areas for use by + subroutines. + */ + int ** Y; + int ** U; + int ** V; + uint8 **orig_y, **orig_cb, **orig_cr; + pixel ** pixels; + + ppm_init(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + /* Allocate all the storage once, to save time. */ + allocateStorage(cmdline.width, cmdline.height, + &Y, &U, &V, &pixels, &orig_y, &orig_cb, &orig_cr); + + ifp = pm_openr(cmdline.input_filespec); + + for (frameSeq = 0; !feof(ifp); frameSeq++) { + bool eof; + + ReadYUV(ifp, cmdline.width, cmdline.height, + orig_y, orig_cb, orig_cr, &eof); + if (!eof) { + pm_message("Converting Frame %u", frameSeq); + + YUVtoPPM(cmdline.width, cmdline.height, orig_y, orig_cb, orig_cr, + pixels, Y, U, V); + ppm_writeppm(stdout, pixels, cmdline.width, cmdline.height, + 255, FALSE); + } + } + + freeStorage(cmdline.height, Y, U, V, pixels, orig_y, orig_cb, orig_cr); + + pm_close(ifp); + exit(0); +} + + + + + diff --git a/converter/ppm/gouldtoppm.c b/converter/ppm/gouldtoppm.c new file mode 100644 index 00000000..5db7c51a --- /dev/null +++ b/converter/ppm/gouldtoppm.c @@ -0,0 +1,122 @@ +/* gouldtoppm.c - read GOULD imaging system files and write a portable pixmap +** +** Copyright (C) 1990 Stephen P. Lesniewski +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include <stdio.h> +#include "ppm.h" + +#define MAXVAL 255 + +static void getgouldheader ARGS(( FILE *infile, unsigned long *cols, unsigned long *nlines, unsigned long *bytesperline, unsigned long *bitsperpixel, unsigned long *NB )); + +int +main( argc, argv ) +int argc; +char *argv[]; +{ + FILE *ifp; + pixel *pixrow; + + unsigned long cols, nlines, bytesperline, bitsperpixel, NB, x, y; + unsigned char pbuffer[4]; + + int color_type; + + + ppm_init( &argc, argv ); + + if ( argc > 2 ) + pm_usage( "[gouldfile]" ); + + if ( argc == 2 ) + ifp = pm_openr( argv[1] ); + else + ifp = stdin; + + getgouldheader( ifp, &cols, &nlines, &bytesperline, &bitsperpixel, &NB); + + ppm_writeppminit(stdout, cols, nlines, MAXVAL, 0); + + color_type = bitsperpixel/8; + if (color_type == 0) color_type = NB; + + pixrow = ppm_allocrow(cols); + + for (y = 0; y < nlines; ++y) + { + for (x = 0; x < cols; ++x) + { + switch (color_type) + { + case 0: + pm_error("incorrect color type" ); + + case 1: + fread(pbuffer, 1, 1, ifp); + PPM_ASSIGN( pixrow[x], pbuffer[0], pbuffer[0], pbuffer[0]); + break; + + case 2: + fread(pbuffer, 2, 1, ifp); + PPM_ASSIGN( pixrow[x], pbuffer[0], pbuffer[1], pbuffer[1]); + break; + + case 3: + fread(pbuffer, 3, 1, ifp); + PPM_ASSIGN( pixrow[x], pbuffer[0], pbuffer[1], pbuffer[2]); + break; + + default : + fread(pbuffer, 3, 1, ifp); + PPM_ASSIGN( pixrow[x], pbuffer[0], pbuffer[1], pbuffer[2]); + break; + + } /* switch */ + } + ppm_writeppmrow(stdout, pixrow, cols, MAXVAL, 0); + } + + pm_close(ifp); + pm_close(stdout); + + exit(0); +} + +static void +getgouldheader( infile, cols, nlines, bytesperline, bitsperpixel, NB) +FILE *infile; +unsigned long *cols, *nlines, *bytesperline, *bitsperpixel, *NB; +{ + unsigned long nlines_new, bytesperline_new, numheaderrec; + unsigned char headerblk[512]; + int i; + + if (fread(headerblk, 512, 1, infile) == 0) + pm_error("cannot read gould header" ); + + *nlines = (headerblk[3]<<8) | headerblk[2]; + *bytesperline = (headerblk[5]<<8) | headerblk[4]; + *bitsperpixel = (headerblk[7]<<8) | headerblk[6]; + numheaderrec = (headerblk[9]<<8) | headerblk[8]; + nlines_new = (headerblk[15]<<24)| (headerblk[14]<<16) | (headerblk[13]<<8) | (headerblk[12]); + bytesperline_new = (headerblk[19]<<24)| (headerblk[18]<<16) | (headerblk[17]<<8) | (headerblk[16]); + *NB = (headerblk[23]<<24)| (headerblk[22]<<16) | (headerblk[21]<<8) | (headerblk[20]); + + if (numheaderrec > 1) + for (i = 1 ; i <numheaderrec; ++i) + if (fread(headerblk, 512, 1, infile) == 0) + pm_error("cannot read gould header(2nd)" ); + + if (*nlines==0) *nlines=nlines_new; + if (*bytesperline==0) *bytesperline=bytesperline_new; + + *cols = (*bytesperline)*8 / (*bitsperpixel); +} diff --git a/converter/ppm/hpcdtoppm/Makefile b/converter/ppm/hpcdtoppm/Makefile new file mode 100644 index 00000000..9a7c67d0 --- /dev/null +++ b/converter/ppm/hpcdtoppm/Makefile @@ -0,0 +1,25 @@ +ifeq ($(SRCDIR)x,x) + SRCDIR = $(CURDIR)/../../.. + BUILDDIR = $(SRCDIR) +endif +SUBDIR = converter/ppm/hpcdtoppm +VPATH=.:$(SRCDIR)/$(SUBDIR) + +include $(BUILDDIR)/Makefile.config + +all: hpcdtoppm + +SCRIPTS = hpcdtoppm pcdovtoppm +MERGE_OBJECTS = + +include $(SRCDIR)/Makefile.common + +install: install.bin.local +.PHONY: install.bin.local +install.bin.local: $(PKGDIR)/bin +# In June 2002, pcdovtoppm replaced pcdindex + cd $(PKGDIR)/bin ; \ + $(SYMLINK) pcdindex$(EXE) pcdovtoppm + + +FORCE: diff --git a/converter/ppm/hpcdtoppm/README b/converter/ppm/hpcdtoppm/README new file mode 100644 index 00000000..a1c7c8c2 --- /dev/null +++ b/converter/ppm/hpcdtoppm/README @@ -0,0 +1,20 @@ +This 'hpcdtoppm' directory is really just a placeholder for the real +Hpcdtoppm source code. + +The real Hpcdtoppm source code cannot be distributed on Sourceforge +because a copyright holder does not license it in a way open enough to +meet Sourceforge's requirements. + +Therefore, the Netpbm maintainer distributes Hpcdtoppm via another channel +and distributes this dummy directory in the main Netpbm source tree on +Sourceforge. When you run the program named 'hpcdtoppm' in this directory, +it tells you how to get the real Hpcdtoppm. + +When you get the real Hpcdtoppm tarball, just unpack it and replace +this entire 'hpcdtoppm' directory with its contents. Then build +Netpbm normally. + +Bear in mind when you get the real Hpcdtoppm, that it is not as openly +licensed as what's in the rest of the Netpbm source tree. As you'll +see in the license that comes with Hpcdtoppm, you may _not_ sell it to +someone else.) diff --git a/converter/ppm/hpcdtoppm/hpcdtoppm b/converter/ppm/hpcdtoppm/hpcdtoppm new file mode 100755 index 00000000..cb6d084b --- /dev/null +++ b/converter/ppm/hpcdtoppm/hpcdtoppm @@ -0,0 +1,25 @@ +#!/bin/sh + +cat <<EOF 1>&2 +You are running a program named 'hpcdtoppm' that stands in for the +real program by that name. + +The real 'hpcdtoppm' is a program that converts an image from Photo CD +format to PPM format. The program you are running now just issues the +message you are reading. + +Please get a copy of Hpcdtoppm from + + ftp://ibiblio.org/pub/linux/graphics/convert + +and replace this stand-in program with the real one. + +The point of this is that this stand-in 'hpcdtoppm' is distributed +with Netpbm on Sourceforge. Hpcdtoppm does not meet the requirements +to be distributed on Sourceforge because a copyright holder does not +permit people to sell copies. In the past the real Hpcdtoppm was +distributed with Netpbm on Sourceforge by mistake. + +EOF + +exit 1 \ No newline at end of file diff --git a/converter/ppm/hpcdtoppm/pcdovtoppm b/converter/ppm/hpcdtoppm/pcdovtoppm new file mode 100755 index 00000000..1f8b3006 --- /dev/null +++ b/converter/ppm/hpcdtoppm/pcdovtoppm @@ -0,0 +1,214 @@ +#!/bin/sh +# +# pcdovtoppm - generate a single PPM file from a PCD overview file +# +# Based on pnmindex (PBMPLUS), which was written by Jef Poskanzer, +# this script makes also use of hpcdtoppm, written by Hadmut Danisch. +# +# Formerly called Pcdindex. +# +# A similar result can be achieved by using "hpcdtoppm -Overview" +# followed by "pnmindex -black" on the generated PPM images. +# This shell just makes it more convenient and transparent to +# convert from one PCD to one PPM overview file. +# +# Additional options (compared to pnmindex) are -maxwidth and +# -font <font>. See "man pbmtext" on how to create your own font. +# +# Pieter S. van der Meulen, 1992. +# Rewritten in sh by Steve McIntyre <93sam@debian.org>, 2001 + +# You may want to change the default values in the next 6 lines: +maxwidth=1152 # maximum width of the index image +size=192 # make the images about this big +across=6 # show this many images per row +colors="noquant" # maximum amount of colors or noquant (no quantization) +back="-black" # default background color +font=" " # default font or none (pbmtext's internal font) + +usage () +{ + echo "Usage: $0 [-m W] [-s S] [-a A] [-c N|n] [-f F] [-b|-w] <overview.pcd>" + echo " with" + echo " W = maximum width of the result image (default: $maxwidth)" + echo " S = maximum size of each of the images (default: $size)" + echo " A = maximum number of images across (default: $across)" + echo " N = maximum number of colors or noquant (default: $colors)" + echo -n " F = font to be used for annotation (default: " + if [ "$font" = " " ] ; then + echo "internal font)" + else + echo "$font)" + fi + echo " -b/-w = black/white background color (default: $back)" + echo " " + echo " e.g.: $0 -m 768 -s 96 -f smallfont.pbm overview.pcd > overview.ppm" + echo " or : $0 /cdrom/photo_cd/overview.pcd | ppmtojpeg > overview.jpg" + exit 1 +} + +# Parse the options +while :; do + case "$1" in + -m*) + if [ $# -lt 2 ] ; then usage; fi + maxwidth="$2" + shift + shift + ;; + + -s*) + if [ $# -lt 2 ] ; then usage; fi + size="$2" + shift + shift + ;; + + -a*) + if [ $# -lt 2 ] ; then usage; fi + across="$2" + shift + shift + ;; + + -c*) + if [ $# -lt 2 ] ; then usage; fi + colors="$2" + shift + shift + ;; + + -f*) + if [ $# -lt 2 ] ; then usage; fi + font="-font $2" + shift + shift + ;; + + -b*) + back="-black" + shift + ;; + + -w*) + back="-white" + shift + ;; + + -*) + echo "$0 : Unknown option $1" + echo " " + usage + ;; + + *) + break + ;; + + esac +done + +if [ $# = 0 ]; then + usage +fi + +tmpfile=`tempfile -p pi -m 600` + +rowfiles=() +imagefiles=() +row=1 +col=1 +width=$size + +# Convert the PCD overview file to many PPM images +if [ -f $1 ] ; then + hpcdtoppm -Overview $1 $tmpfile +else + echo "$0 : Could not access $1" + echo " " + usage +fi + +for i in "$tmpfile"* +do + if [ -f $i ] ; then + description=`pnmfile $i` + if [ "${description[4]}" -le $size -a \ + "${description[6]}" -le $size ] ; then + cat $i > $tmpfile + else + if [ "$colors" = "n" ] ; then + pnmscale -quiet -xysize $size $size $i > $tmpfile + else + pnmscale -quiet -xysize $size $size $i | \ + ppmquant -quiet $colors > $tmpfile + fi + fi + fi + imagefile=pi.${row}.${col}.$$ + rm -f $imagefile + ttext="$i:t" + + if [ "$back" = "-white" ] ; then + pbmtext $font "$ttext" | pnmcrop -quiet | pnmmargin -white 2| \ + pnmcat $back -tb $tmpfile - > $imagefile + else + pbmtext $font "$ttext" | pnmcrop -quiet | pnmmargin -white 2 | \ + pnminvert | pnmcat $back -tb $tmpfile - > $imagefile + fi + + rm -f $tmpfile + description=`pnmfile $imagefile` + width=$(( $width + ${description[4]} )) + imagefiles="$imagefiles $imagefile" + + if [ $col -ge $across -o $width -gt $maxwidth ] ; then + rowfile=pi.${row}.$$ + rm -f $rowfile + if [ "$colors" = "n" ] ; then + pnmcat $back -lr -jbottom $imagefiles > $rowfile + else + pnmcat $back -lr -jbottom $imagefiles | \ + ppmquant -quiet $colors > $rowfile + fi + rm -f $imagefiles + imagefiles=() + rowfiles="$rowfiles $rowfile" + col=1 + row=$(( $row + 1 )) + width=$size + else + col=$(( $col + 1 )) + fi +done + +if [ ${#imagefiles[*]} -gt 0 ] ; then + rowfile=pi.${row}.$$ + rm -f $rowfile + if [ "$colors" = "n" ] ; then + pnmcat $back -lr -jbottom $imagefiles > $rowfile + else + pnmcat $back -lr -jbottom $imagefiles | \ + ppmquant -quiet $colors > $rowfile + fi + rm -f $imagefiles + rowfiles="$rowfiles $rowfile" +fi + +if [ ${#rowfiles[*]} == 1 ]; then + cat $rowfiles +else + if [ "$colors" = "n" ] ; then + pnmcat $back -tb $rowfiles + else + pnmcat $back -tb $rowfiles | ppmquant -quiet $colors + fi +fi + +rm -f $rowfiles + +exit 0 + + + + diff --git a/converter/ppm/ilbm.h b/converter/ppm/ilbm.h new file mode 100644 index 00000000..68657956 --- /dev/null +++ b/converter/ppm/ilbm.h @@ -0,0 +1,256 @@ +#ifndef ILBM_H_INCLUDED +#define ILBM_H_INCLUDED + +/* ilbm.h - definitions for IFF ILBM files */ + +#define RowBytes(cols) ((((cols) + 15) / 16) * 2) + + +/* definitions for BMHD */ + +typedef struct { + unsigned short w, h; + short x, y; + unsigned char nPlanes, masking, compression, flags; + unsigned short transparentColor; + unsigned char xAspect, yAspect; + short pageWidth, pageHeight; +} BitMapHeader; +#define BitMapHeaderSize 20 + +#define BMHD_FLAGS_CMAPOK (1<<7) /* valid 8bit colormap */ + +#define mskNone 0 +#define mskHasMask 1 +#define mskHasTransparentColor 2 +#define mskLasso 3 /* not supported */ +#define mskMAXKNOWN mskLasso +static const char * mskNAME[] = { + "none", "mask plane", "transparent color", "lasso" +}; + +#define cmpNone 0 +#define cmpByteRun1 1 +#define cmpMAXKNOWN cmpByteRun1 +static const char * cmpNAME[] = { "none", "byterun1" }; + + +/* definitions for CMAP */ + +#if 0 /* not used */ +typedef struct { + unsigned char r, g, b; +} ColorRegister; +#endif + + +/* definitions for CAMG */ + +#define CAMGChunkSize 4 + +#define vmLACE 0x0004 +#define vmEXTRA_HALFBRITE 0x0080 +#define vmHAM 0x0800 +#define vmHIRES 0x8000 + +#define HAMCODE_CMAP 0 /* look up color in colormap */ +#define HAMCODE_BLUE 1 /* new blue component */ +#define HAMCODE_RED 2 /* new red component */ +#define HAMCODE_GREEN 3 /* new green component */ + + +/* multipalette PCHG chunk definitions */ + +/* get number of longwords in line mask from PCHG.LineCount */ +#define MaskLongWords(x) (((x) + 31) / 32) + +typedef struct { + unsigned short Compression; + unsigned short Flags; + short StartLine; /* may be negative */ + unsigned short LineCount; + unsigned short ChangedLines; + unsigned short MinReg; + unsigned short MaxReg; + unsigned short MaxChanges; + unsigned long TotalChanges; +} PCHGHeader; +#define PCHGHeaderSize 20 + +/* Compression modes */ +#define PCHG_COMP_NONE 0 +#define PCHG_COMP_HUFFMAN 1 + +/* Flags */ +#define PCHGF_12BIT (1 << 0) /* use SmallLineChanges */ +#define PCHGF_32BIT (1 << 1) /* use BigLineChanges */ +#define PCHGF_USE_ALPHA (1 << 2) /* meaningful only if PCHG_32BIT is on: + use the Alpha channel info */ +typedef struct { + unsigned long CompInfoSize; + unsigned long OriginalDataSize; +} PCHGCompHeader; +#define PCHGCompHeaderSize 8 + +#if 0 /* not used */ +typedef struct { + unsigned char ChangeCount16; + unsigned char ChangeCount32; + unsigned short *PaletteChange; +} SmallLineChanges; + +typedef struct { + unsigned short Register; + unsigned char Alpha, Red, Blue, Green; /* ARBG, not ARGB */ +} BigPaletteChange; + +typedef struct { + unsigned short ChangeCount; + BigPaletteChange *PaletteChange; +} BigLineChanges; +#endif /* 0 */ + + +/* definitions for CLUT */ + +#if 0 /* not used */ +typedef struct { + unsigned long type; + unsigned long reserved0; + unsigned char lut[256]; +} ColorLUT; +#endif /* 0 */ +#define CLUTSize (256+4+4) + +/* types */ +#define CLUT_MONO 0 +#define CLUT_RED 1 +#define CLUT_GREEN 2 +#define CLUT_BLUE 3 +#define CLUT_HUE 4 /* not supported */ +#define CLUT_SAT 5 /* not supported */ + + +/* unofficial DCOL chunk for direct-color */ + +typedef struct { + unsigned char r, g, b, pad1; +} DirectColor; +#define DirectColorSize 4 + + + +/* IFF chunk IDs */ + +typedef unsigned long IFF_ID; + +#define MAKE_ID(a, b, c, d) \ + ((IFF_ID)(a)<<24 | (IFF_ID)(b)<<16 | (IFF_ID)(c)<<8 | (IFF_ID)(d)) + +#define ID_FORM MAKE_ID('F', 'O', 'R', 'M') + /* EA IFF 85 group identifier */ +#define ID_CAT MAKE_ID('C', 'A', 'T', ' ') + /* EA IFF 85 group identifier */ +#define ID_LIST MAKE_ID('L', 'I', 'S', 'T') + /* EA IFF 85 group identifier */ +#define ID_PROP MAKE_ID('P', 'R', 'O', 'P') + /* EA IFF 85 group identifier */ +#define ID_END MAKE_ID('E', 'N', 'D', ' ') + /* unofficial END-of-FORM identifier (see Amiga RKM Devices Ed.3 + page 376) */ +#define ID_ILBM MAKE_ID('I', 'L', 'B', 'M') + /* EA IFF 85 raster bitmap form */ +#define ID_DEEP MAKE_ID('D', 'E', 'E', 'P') + /* Chunky pixel image files (Used in TV Paint) */ +#define ID_RGB8 MAKE_ID('R', 'G', 'B', '8') + /* RGB image forms, Turbo Silver (Impulse) */ +#define ID_RGBN MAKE_ID('R', 'G', 'B', 'N') + /* RGB image forms, Turbo Silver (Impulse) */ +#define ID_PBM MAKE_ID('P', 'B', 'M', ' ') + /* 256-color chunky format (DPaint 2 ?) */ +#define ID_ACBM MAKE_ID('A', 'C', 'B', 'M') + /* Amiga Contiguous Bitmap (AmigaBasic) */ + +/* generic */ + +#define ID_FVER MAKE_ID('F', 'V', 'E', 'R') + /* AmigaOS version string */ +#define ID_JUNK MAKE_ID('J', 'U', 'N', 'K') + /* always ignore this chunk */ +#define ID_ANNO MAKE_ID('A', 'N', 'N', 'O') + /* EA IFF 85 Generic Annotation chunk */ +#define ID_AUTH MAKE_ID('A', 'U', 'T', 'H') + /* EA IFF 85 Generic Author chunk */ +#define ID_CHRS MAKE_ID('C', 'H', 'R', 'S') + /* EA IFF 85 Generic character string chunk */ +#define ID_NAME MAKE_ID('N', 'A', 'M', 'E') + /* EA IFF 85 Generic Name of art, music, etc. chunk */ +#define ID_TEXT MAKE_ID('T', 'E', 'X', 'T') + /* EA IFF 85 Generic unformatted ASCII text chunk */ +#define ID_copy MAKE_ID('(', 'c', ')', ' ') +/* EA IFF 85 Generic Copyright text chunk */ + +/* ILBM chunks */ + +#define ID_BMHD MAKE_ID('B', 'M', 'H', 'D') + /* ILBM BitmapHeader */ +#define ID_CMAP MAKE_ID('C', 'M', 'A', 'P') + /* ILBM 8bit RGB colormap */ +#define ID_GRAB MAKE_ID('G', 'R', 'A', 'B') + /* ILBM "hotspot" coordiantes */ +#define ID_DEST MAKE_ID('D', 'E', 'S', 'T') + /* ILBM destination image info */ +#define ID_SPRT MAKE_ID('S', 'P', 'R', 'T') + /* ILBM sprite identifier */ +#define ID_CAMG MAKE_ID('C', 'A', 'M', 'G') + /* Amiga viewportmodes */ +#define ID_BODY MAKE_ID('B', 'O', 'D', 'Y') + /* ILBM image data */ +#define ID_CRNG MAKE_ID('C', 'R', 'N', 'G') + /* color cycling */ +#define ID_CCRT MAKE_ID('C', 'C', 'R', 'T') + /* color cycling */ +#define ID_CLUT MAKE_ID('C', 'L', 'U', 'T') + /* Color Lookup Table chunk */ +#define ID_DPI MAKE_ID('D', 'P', 'I', ' ') + /* Dots per inch chunk */ +#define ID_DPPV MAKE_ID('D', 'P', 'P', 'V') + /* DPaint perspective chunk (EA) */ +#define ID_DRNG MAKE_ID('D', 'R', 'N', 'G') + /* DPaint IV enhanced color cycle chunk (EA) */ +#define ID_EPSF MAKE_ID('E', 'P', 'S', 'F') + /* Encapsulated Postscript chunk */ +#define ID_CMYK MAKE_ID('C', 'M', 'Y', 'K') + /* Cyan, Magenta, Yellow, & Black color map (Soft-Logik) */ +#define ID_CNAM MAKE_ID('C', 'N', 'A', 'M') + /* Color naming chunk (Soft-Logik) */ +#define ID_PCHG MAKE_ID('P', 'C', 'H', 'G') + /* Line by line palette control information (Sebastiano Vigna) */ +#define ID_PRVW MAKE_ID('P', 'R', 'V', 'W') + /* A mini duplicate ILBM used for preview (Gary Bonham) */ +#define ID_XBMI MAKE_ID('X', 'B', 'M', 'I') + /* eXtended BitMap Information (Soft-Logik) */ +#define ID_CTBL MAKE_ID('C', 'T', 'B', 'L') + /* Newtek Dynamic Ham color chunk */ +#define ID_DYCP MAKE_ID('D', 'Y', 'C', 'P') + /* Newtek Dynamic Ham chunk */ +#define ID_SHAM MAKE_ID('S', 'H', 'A', 'M') + /* Sliced HAM color chunk */ +#define ID_ABIT MAKE_ID('A', 'B', 'I', 'T') + /* ACBM body chunk */ +#define ID_DCOL MAKE_ID('D', 'C', 'O', 'L') + /* unofficial direct color */ +#define ID_DPPS MAKE_ID('D', 'P', 'P', 'S') + /* ? */ +#define ID_TINY MAKE_ID('T', 'I', 'N', 'Y') + /* ? */ + +/* other stuff */ + +#define MAXPLANES 16 +typedef unsigned short rawtype; + +#define MAXCMAPCOLORS (1 << MAXPLANES) +#define MAXCOLVAL 255 /* max value of color component */ + +#endif diff --git a/converter/ppm/ilbmtoppm.c b/converter/ppm/ilbmtoppm.c new file mode 100644 index 00000000..f9f9bac3 --- /dev/null +++ b/converter/ppm/ilbmtoppm.c @@ -0,0 +1,2381 @@ +/* ilbmtoppm.c - read an IFF ILBM file and produce a portable pixmap +** +** Copyright (C) 1989 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +** +** Modified by Mark Thompson on 10/4/90 to accomodate 24-bit IFF files +** as used by ASDG, NewTek, etc. +** +** Modified by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de) +** 20/Jun/93: +** - row-by-row operation +** - better de-interleave algorithm +** - colormap files +** - direct color +** 04/Oct/93: +** - multipalette capability (PCHG chunk) +** - options -ignore, -isham, -isehb and -adjustcolors +** 22/May/94: +** - minor change: check first for 24 planes, then for HAM +** 21/Sep/94: +** - write mask plane to a file if -maskfile option used +** - write colormap file +** - added sliced HAM/dynamic HAM/dynamic Hires multipalette formats (SHAM, CTBL chunk) +** - added color lookup tables (CLUT chunk) +** - major rework of colormap/multipalette handling +** - now uses numeric IFF IDs +** 24/Oct/94: +** - transparentColor capability +** - added RGBN/RGB8 image types +** - 24-bit & direct color modified to n-bit deep ILBM +** 22/Feb/95: +** - direct color (DCOL) reimplemented +** 29/Mar/95 +** - added IFF-PBM format +*/ + +#include <string.h> + +#include "pm_c_util.h" +#include "ppm.h" +#include "ilbm.h" +#include "mallocvar.h" + +/*#define DEBUG*/ + +typedef struct { + int reg; /* color register to change */ + pixval r, g, b; /* new colors for register */ +} PaletteChange; + +typedef struct { + pixel *color; + int ncolors; + /* lookup tables */ + unsigned char *redlut; + unsigned char *greenlut; + unsigned char *bluelut; + unsigned char *monolut; + /* multipalette stuff */ + PaletteChange *mp_init; + PaletteChange **mp_change; + int mp_rows; /* # of rows in change array */ + int mp_type; /* see below, higher types preferred */ + int mp_flags; + IFF_ID mp_id; +} ColorMap; + +#define HAS_COLORMAP(cmap) ((cmap) && (cmap)->color) +#define HAS_COLORLUT(cmap) ((cmap) && ((cmap)->redlut || (cmap)->greenlut || (cmap)->bluelut)) +#define HAS_MONOLUT(cmap) ((cmap) && (cmap)->monolut) +#define HAS_MULTIPALETTE(cmap) (HAS_COLORMAP(cmap) && (cmap)->mp_type) +#define MP_TYPE_SHAM 1 +#define MP_TYPE_CTBL 2 +#define MP_TYPE_PCHG 3 +#define MP_REG_IGNORE -1 +#define MP_REG_END -2 +#define MP_FLAGS_SKIPLACED (1<<0) + +#define FACTOR_4BIT 17 /* scale factor maxval 15 -> maxval 255 */ + +static short verbose = 0; +static short adjustcolors = 0; +static unsigned char *ilbmrow; +static pixel *pixelrow; +static FILE *maskfile = NULL; +static bit *maskrow = NULL; +static short wrotemask = 0; +static IFF_ID typeid; /* ID_ILBM, ID_RGBN, ID_RGB8 */ + +static pixel *transpColor = NULL; /* transparent color */ +static short transpIndex = -1; +static char *transpName = NULL; + +static bool debug = FALSE; + + + +static char * +ID2string(id) + IFF_ID id; +{ + static char str[] = "abcd"; + + str[0] = (char)(id >> 24 & 0xff); + str[1] = (char)(id >> 16 & 0xff); + str[2] = (char)(id >> 8 & 0xff); + str[3] = (char)(id >> 0 & 0xff); + + return str; +} + + +/**************************************************************************** + Memory allocation + ****************************************************************************/ +static ColorMap * +alloc_cmap(void) { + + ColorMap * cmap; + + MALLOCVAR_NOFAIL(cmap); + + cmap->color = NULL; + cmap->ncolors = 0; + cmap->monolut = NULL; + cmap->redlut = NULL; + cmap->greenlut = NULL; + cmap->bluelut = NULL; + cmap->mp_init = NULL; + cmap->mp_change = NULL; + cmap->mp_rows = 0; + cmap->mp_type = 0; + cmap->mp_flags = 0; + + return cmap; +} + + + +static rawtype * +alloc_rawrow(cols) + int cols; +{ + rawtype *r; + int i; + + MALLOCARRAY_NOFAIL(r, cols); + + for( i = 0; i < cols; i++ ) + r[i] = 0; + + return r; +} + + +/**************************************************************************** + Basic I/O functions + ****************************************************************************/ + +static void +readerr(f, iffid) + FILE *f; + IFF_ID iffid; +{ + if( ferror(f) ) + pm_error("read error"); + else + pm_error("premature EOF in %s chunk", ID2string(iffid)); +} + + +static void +read_bytes(FILE * const ifP, + int const bytes, + unsigned char * const buffer, + IFF_ID const iffid, + unsigned long * const counterP) { + + if (counterP) { + if (*counterP < bytes) + pm_error("insufficient data in %s chunk", ID2string(iffid)); + *counterP -= bytes; + } + if (fread(buffer, 1, bytes, ifP) != bytes) + readerr(ifP, iffid); +} + + + +static unsigned char +get_byte(ifp, iffid, counter) + FILE* ifp; + IFF_ID iffid; + long *counter; +{ + int i; + + if( counter ) { + if( *counter == 0 ) + pm_error("insufficient data in %s chunk", ID2string(iffid)); + --(*counter); + } + i = getc(ifp); + if( i == EOF ) + readerr(ifp, iffid); + + return (unsigned char) i; +} + +static long +get_big_long(FILE * const ifP, + IFF_ID const iffid, + unsigned long * const counterP) { + + long l; + + if (counterP) { + if (*counterP < 4) + pm_error("insufficient data in %s chunk", ID2string(iffid)); + *counterP -= 4; + } + if (pm_readbiglong(ifP, &l) == -1) + readerr(ifP, iffid); + + return l; +} + + + +static short +get_big_short(FILE * const ifP, + IFF_ID const iffid, + unsigned long * const counterP) { + + short s; + + if (counterP) { + if (*counterP < 2) + pm_error("insufficient data in %s chunk", ID2string(iffid)); + *counterP -= 2; + } + if (pm_readbigshort(ifP, &s) == -1) + readerr(ifP, iffid); + + return s; +} + + + +/**************************************************************************** + Chunk reader + ****************************************************************************/ + +static void +chunk_end(FILE * const ifP, + IFF_ID const iffid, + unsigned long const chunksize) { + + if (chunksize > 0) { + unsigned long remainingChunksize; + pm_message("warning - %ld extraneous byte%s in %s chunk", + chunksize, (chunksize == 1 ? "" : "s"), ID2string(iffid)); + remainingChunksize = chunksize; /* initial value */ + while (remainingChunksize > 0) + get_byte(ifP, iffid, &remainingChunksize); + } +} + + + +static void +skip_chunk(FILE * const ifP, + IFF_ID const iffid, + unsigned long const chunksize) { + unsigned long remainingChunksize; + + remainingChunksize = chunksize; /* initial value */ + + while (remainingChunksize > 0) + get_byte(ifP, iffid, &remainingChunksize); +} + + + +static void +display_chunk(FILE * const ifP, + IFF_ID const iffid, + unsigned long const chunksize) { + + int byte; + unsigned long remainingChunksize; + + pm_message("contents of %s chunk:", ID2string(iffid)); + + remainingChunksize = chunksize; /* initial value */ + byte = '\0'; + + while (remainingChunksize > 0) { + byte = get_byte(ifP, iffid, &remainingChunksize); + if (fputc(byte, stderr) == EOF) + pm_error("write error"); + } + if (byte != '\n') + if (fputc('\n', stderr) == EOF) + pm_error("write error"); +} + + +static void +read_cmap(FILE * const ifp, + IFF_ID const iffid, + long const chunksize, + ColorMap * const cmap) { + + long colors; + + colors = chunksize / 3; + if( colors == 0 ) { + pm_error("warning - empty %s colormap", ID2string(iffid)); + skip_chunk(ifp, iffid, chunksize); + } else { + unsigned int i; + if( cmap->color ) /* prefer CMAP-chunk over CMYK-chunk */ + ppm_freerow(cmap->color); + cmap->color = ppm_allocrow(colors); + cmap->ncolors = colors; + + for( i = 0; i < colors; ++i ) { + int r, g, b; + r = get_byte(ifp, iffid, &chunksize); + g = get_byte(ifp, iffid, &chunksize); + b = get_byte(ifp, iffid, &chunksize); + PPM_ASSIGN(cmap->color[i], r, g, b); + } + chunk_end(ifp, iffid, chunksize); + } +} + + + +static void +read_cmyk(FILE * const ifp, + IFF_ID const iffid, + long const chunksize, + ColorMap * const cmap) { + + if( HAS_COLORMAP(cmap) ) { /* prefer RGB color map */ + skip_chunk(ifp, iffid, chunksize); + } else { + long const colors = chunksize/4; + if( colors == 0 ) { + pm_error("warning - empty %s colormap", ID2string(iffid)); + skip_chunk(ifp, iffid, chunksize); + } else { + unsigned int i; + cmap->color = ppm_allocrow(colors); + cmap->ncolors = colors; + + for( i = 0; i < colors; ++i ) { + int c, m, y, k; + c = get_byte(ifp, iffid, &chunksize); + m = get_byte(ifp, iffid, &chunksize); + y = get_byte(ifp, iffid, &chunksize); + k = get_byte(ifp, iffid, &chunksize); + + { + pixval const red = + MAXCOLVAL - MIN(MAXCOLVAL, + c*(MAXCOLVAL-k)/MAXCOLVAL+k); + pixval const green = + MAXCOLVAL - MIN(MAXCOLVAL, + m*(MAXCOLVAL-k)/MAXCOLVAL+k); + pixval const blue = + MAXCOLVAL - MIN(MAXCOLVAL, + y*(MAXCOLVAL-k)/MAXCOLVAL+k); + + PPM_ASSIGN(cmap->color[i], red, green, blue); + } + } + chunk_end(ifp, iffid, chunksize); + } + } +} + + + +static void +read_clut(FILE * const ifp, + IFF_ID const iffid, + unsigned long const chunksize, + ColorMap * const cmap) { + + if (chunksize != CLUTSize) { + pm_message("invalid size for %s chunk - skipping it", + ID2string(iffid)); + skip_chunk(ifp, iffid, chunksize); + } else { + long type; + unsigned char * lut; + unsigned long remainingChunksize; + unsigned int i; + + type = get_big_long(ifp, iffid, &remainingChunksize); + get_big_long(ifp, iffid, &remainingChunksize); /* skip reserved fld */ + + MALLOCARRAY_NOFAIL(lut, 256); + for( i = 0; i < 256; ++i ) + lut[i] = get_byte(ifp, iffid, &remainingChunksize); + + switch( type ) { + case CLUT_MONO: + cmap->monolut = lut; + break; + case CLUT_RED: + cmap->redlut = lut; + break; + case CLUT_GREEN: + cmap->greenlut = lut; + break; + case CLUT_BLUE: + cmap->bluelut = lut; + break; + default: + pm_message("warning - %s type %ld not recognized", + ID2string(iffid), type); + free(lut); + } + } +} + + + +static BitMapHeader * +read_bmhd(FILE * const ifP, + IFF_ID const iffid, + unsigned long const chunksize) { + + BitMapHeader * bmhdP; + + if( chunksize != BitMapHeaderSize ) { + pm_message("invalid size for %s chunk - skipping it", + ID2string(iffid)); + skip_chunk(ifP, iffid, chunksize); + bmhdP = NULL; + } else { + unsigned long remainingChunksize; + + MALLOCVAR_NOFAIL(bmhdP); + + remainingChunksize = chunksize; /* initial value */ + + bmhdP->w = get_big_short(ifP, iffid, &remainingChunksize); + bmhdP->h = get_big_short(ifP, iffid, &remainingChunksize); + bmhdP->x = get_big_short(ifP, iffid, &remainingChunksize); + bmhdP->y = get_big_short(ifP, iffid, &remainingChunksize); + bmhdP->nPlanes = get_byte(ifP, iffid, &remainingChunksize); + bmhdP->masking = get_byte(ifP, iffid, &remainingChunksize); + bmhdP->compression = get_byte(ifP, iffid, &remainingChunksize); + bmhdP->flags = get_byte(ifP, iffid, &remainingChunksize); + bmhdP->transparentColor = + get_big_short(ifP, iffid, &remainingChunksize); + bmhdP->xAspect = get_byte(ifP, iffid, &remainingChunksize); + bmhdP->yAspect = get_byte(ifP, iffid, &remainingChunksize); + bmhdP->pageWidth = get_big_short(ifP, iffid, &remainingChunksize); + bmhdP->pageHeight = get_big_short(ifP, iffid, &remainingChunksize); + + if( verbose ) { + if( typeid == ID_ILBM ) + pm_message("dimensions: %dx%d, %d planes", + bmhdP->w, bmhdP->h, bmhdP->nPlanes); + else + pm_message("dimensions: %dx%d", bmhdP->w, bmhdP->h); + + if( typeid == ID_ILBM || typeid == ID_PBM ) { + pm_message("compression: %s", + bmhdP->compression <= cmpMAXKNOWN ? + cmpNAME[bmhdP->compression] : "unknown"); + + switch( bmhdP->masking ) { + case mskNone: + break; + case mskHasMask: + case mskHasTransparentColor: + if( !maskfile ) + pm_message("use '-maskfile <filename>' " + "to generate a PBM mask file from %s", + mskNAME[bmhdP->masking]); + break; + case mskLasso: + pm_message("warning - masking type '%s' not recognized", + mskNAME[bmhdP->masking]); + break; + default: + pm_error("unknown masking type %d", bmhdP->masking); + } + } + else /* RGBN/RGB8 */ + if( !maskfile ) + pm_message("use '-maskfile <filename>' " + "to generate a PBM mask file " + "from genlock bits"); + } + + /* fix aspect ratio */ + if( bmhdP->xAspect == 0 || bmhdP->yAspect == 0 ) { + pm_message("warning - illegal aspect ratio %d:%d, using 1:1", + bmhdP->xAspect, bmhdP->yAspect); + bmhdP->xAspect = bmhdP->yAspect = 1; + } + + if( bmhdP->xAspect != bmhdP->yAspect ) { + pm_message("warning - non-square pixels; " + "to fix do a 'pnmscale -%cscale %g'", + bmhdP->xAspect > bmhdP->yAspect ? 'x' : 'y', + bmhdP->xAspect > bmhdP->yAspect ? + (float)(bmhdP->xAspect)/bmhdP->yAspect : + (float)(bmhdP->yAspect)/bmhdP->xAspect); + } + } + return bmhdP; +} + + +/**************************************************************************** + ILBM functions + ****************************************************************************/ + + +static void +read_ilbm_plane(FILE * const ifP, + unsigned long * const remainingChunksizeP, + int const bytes, + int const compression) { + + unsigned char *ubp; + int j, byte; + int bytesRemaining; + + bytesRemaining = bytes; /* initial value */ + + switch(compression) { + case cmpNone: + read_bytes(ifP, bytesRemaining, ilbmrow, + ID_BODY, remainingChunksizeP); + break; + case cmpByteRun1: + ubp = ilbmrow; + do { + byte = (int)get_byte(ifP, ID_BODY, remainingChunksizeP); + if( byte <= 127 ) { + j = byte; + bytesRemaining -= (j+1); + if( bytesRemaining < 0 ) + pm_error("error doing ByteRun1 decompression"); + for( ; j >= 0; j-- ) + *ubp++ = get_byte(ifP, ID_BODY, remainingChunksizeP); + } + else + if ( byte != 128 ) { + j = 256 - byte; + bytesRemaining -= (j+1); + if( bytesRemaining < 0 ) + pm_error("error doing ByteRun1 decompression"); + byte = (int)get_byte(ifP, ID_BODY, remainingChunksizeP); + for( ; j >= 0; j-- ) + *ubp++ = (unsigned char)byte; + } + /* 128 is a NOP */ + } + while( bytesRemaining > 0 ); + break; + default: + pm_error("unknown compression type %d", compression); + } +} + + +static const unsigned char bit_mask[] = {1, 2, 4, 8, 16, 32, 64, 128}; + +static void +decode_row(FILE * const ifP, + unsigned long * const remainingChunksizeP, + rawtype * const chunkyrow, + int const nPlanes, + BitMapHeader * const bmhdP) { + + int plane, col, cols, cbit, bytes; + unsigned char *ilp; + rawtype *chp; + + cols = bmhdP->w; + bytes = RowBytes(cols); + for( plane = 0; plane < nPlanes; plane++ ) { + int mask; + + mask = 1 << plane; + read_ilbm_plane(ifP, remainingChunksizeP, bytes, bmhdP->compression); + + ilp = ilbmrow; + chp = chunkyrow; + + cbit = 7; + for( col = 0; col < cols; col++, cbit--, chp++ ) { + if( cbit < 0 ) { + cbit = 7; + ilp++; + } + if( *ilp & bit_mask[cbit] ) + *chp |= mask; + else + *chp &= ~mask; + } + } +} + + +static void +decode_mask(FILE * const ifP, + unsigned long * const remainingChunksizeP, + rawtype * const chunkyrow, + BitMapHeader * const bmhdP) { + + int col, cols, cbit; + unsigned char *ilp; + + cols = bmhdP->w; + switch( bmhdP->masking ) { + case mskNone: + break; + case mskHasMask: /* mask plane */ + read_ilbm_plane(ifP, remainingChunksizeP, RowBytes(cols), + bmhdP->compression); + if( maskfile ) { + ilp = ilbmrow; + cbit = 7; + for( col = 0; col < cols; col++, cbit-- ) { + if( cbit < 0 ) { + cbit = 7; + ilp++; + } + if( *ilp & bit_mask[cbit] ) + maskrow[col] = PBM_BLACK; + else + maskrow[col] = PBM_WHITE; + } + pbm_writepbmrow(maskfile, maskrow, cols, 0); + wrotemask = 1; + } + break; + case mskHasTransparentColor: + if( !chunkyrow ) + pm_error("decode_mask(): chunkyrow == NULL - can't happen"); + + if( maskfile ) { + for( col = 0; col < cols; col++ ) { + if( chunkyrow[col] == bmhdP->transparentColor ) + maskrow[col] = PBM_WHITE; + else + maskrow[col] = PBM_BLACK; + } + pbm_writepbmrow(maskfile, maskrow, cols, 0); + wrotemask = 1; + } + break; + case mskLasso: + pm_error("This program does not know how to process Lasso masking"); + break; + default: + pm_error("decode_mask(): unknown masking type %d - can't happen", + bmhdP->masking); + } +} + + +/**************************************************************************** + Multipalette handling + ****************************************************************************/ + + +static void +multi_adjust(cmap, row, palchange) + ColorMap *cmap; + int row; + PaletteChange *palchange; +{ + int i, reg; + + for( i = 0; palchange[i].reg != MP_REG_END; i++ ) { + reg = palchange[i].reg; + if( reg >= cmap->ncolors ) { + pm_message("warning - palette change register out of range"); + pm_message(" row %d change structure %d reg=%d (max %d)", + row, i, reg, cmap->ncolors-1); + pm_message(" ignoring it... " + "colors might get messed up from here"); + } + else + if( reg != MP_REG_IGNORE ) { + PPM_ASSIGN(cmap->color[reg], + palchange[i].r, palchange[i].g, palchange[i].b); + } + } +} + +static void +multi_init(cmap, viewportmodes) + ColorMap *cmap; + long viewportmodes; +{ + if( cmap->mp_init ) + multi_adjust(cmap, -1, cmap->mp_init); + if( !(viewportmodes & vmLACE) ) + cmap->mp_flags &= ~(MP_FLAGS_SKIPLACED); +} + +static void +multi_update(cmap, row) + ColorMap *cmap; + int row; +{ + if( cmap->mp_flags & MP_FLAGS_SKIPLACED ) { + if( ODD(row) ) + return; + if( row/2 < cmap->mp_rows && cmap->mp_change[row/2] ) + multi_adjust(cmap, row, cmap->mp_change[row/2]); + } + else { + if( row < cmap->mp_rows && cmap->mp_change[row] ) + multi_adjust(cmap, row, cmap->mp_change[row]); + } +} + +static void +multi_free(cmap) + ColorMap *cmap; +{ + int i; + + if( cmap->mp_init ) { + free(cmap->mp_init); + cmap->mp_init = NULL; + } + if( cmap->mp_change ) { + for( i = 0; i < cmap->mp_rows; i++ ) { + if( cmap->mp_change[i] ) + free(cmap->mp_change[i]); + } + free(cmap->mp_change); + cmap->mp_change = NULL; + } + cmap->mp_rows = 0; + cmap->mp_type = 0; + cmap->mp_flags = 0; +} + + +/**************************************************************************** + Colormap handling + ****************************************************************************/ + +static void +check_cmap(bmhd, cmap) + BitMapHeader *bmhd; + ColorMap *cmap; +{ + pixval colmaxval = 0; + int shifted = 1; + int i, r, g, b; + + if( bmhd ) { + if( bmhd->masking == mskHasTransparentColor || + bmhd->masking == mskLasso ) { + transpIndex = bmhd->transparentColor; + if( !transpName ) { + MALLOCVAR_NOFAIL(transpColor); + if( transpIndex >= cmap->ncolors ) { + pm_message("using default transparent color (black)"); + PPM_ASSIGN(*transpColor, 0, 0, 0); + } + else + *transpColor = cmap->color[transpIndex]; + } + } + + if( bmhd->flags & BMHD_FLAGS_CMAPOK ) + return; + } + + if( !HAS_COLORMAP(cmap) ) + return; + + for( i = 0; i < cmap->ncolors; i++ ) { + r = PPM_GETR(cmap->color[i]); + if( r > colmaxval ) colmaxval = r; + if( r & 0x0f ) shifted = 0; + + g = PPM_GETG(cmap->color[i]); + if( g > colmaxval ) colmaxval = g; + if( g & 0x0f ) shifted = 0; + + b = PPM_GETB(cmap->color[i]); + if( b > colmaxval ) colmaxval = b; + if( b & 0x0f ) shifted = 0; + } + +#ifdef DEBUG + pm_message("colormap maxval is %d", colmaxval); +#endif + if( colmaxval == 0 ) + pm_message("warning - black colormap"); + else + if( shifted || colmaxval <= 15 ) { + if( !adjustcolors ) { + pm_message("warning - probably %s4-bit colormap", + shifted ? "shifted " : ""); + pm_message(" use '-adjustcolors' to scale colormap to 8 bits"); + } + else { + pm_message("scaling colormap to 8 bits"); + for( i = 0; i < cmap->ncolors; i++ ) { + r = PPM_GETR(cmap->color[i]); + g = PPM_GETG(cmap->color[i]); + b = PPM_GETB(cmap->color[i]); + if( shifted ) { + r >>= 4; + g >>= 4; + b >>= 4; + } + r *= FACTOR_4BIT; + g *= FACTOR_4BIT; + b *= FACTOR_4BIT; + PPM_ASSIGN(cmap->color[i], r, g, b); + } + } + } +} + + +static pixval +lookup_red(cmap, oldval) + ColorMap *cmap; + int oldval; +{ + if( cmap && cmap->redlut && oldval < 256 ) + return cmap->redlut[oldval]; + else + return oldval; +} + +static pixval +lookup_green(cmap, oldval) + ColorMap *cmap; + int oldval; +{ + if( cmap && cmap->greenlut && oldval < 256 ) + return cmap->greenlut[oldval]; + else + return oldval; +} + +static pixval +lookup_blue(cmap, oldval) + ColorMap *cmap; + int oldval; +{ + if( cmap && cmap->bluelut && oldval < 256 ) + return cmap->bluelut[oldval]; + else + return oldval; +} + +static pixval +lookup_mono(cmap, oldval) + ColorMap *cmap; + int oldval; +{ + if( cmap && cmap->monolut && oldval < 256 ) + return cmap->monolut[oldval]; + else + return oldval; +} + +static ColorMap * +ehbcmap(cmap) + ColorMap *cmap; +{ + pixel *tempcolor = NULL; + int i, col; + + col = cmap->ncolors; + + tempcolor = ppm_allocrow(col * 2); + for( i = 0; i < col; i++ ) { + tempcolor[i] = cmap->color[i]; + PPM_ASSIGN(tempcolor[col + i], PPM_GETR(cmap->color[i]) / 2, + PPM_GETG(cmap->color[i]) / 2, + PPM_GETB(cmap->color[i]) / 2 ); + } + ppm_freerow(cmap->color); + cmap->color = tempcolor; + cmap->ncolors *= 2; + + return cmap; +} + + + +static pixval +lut_maxval(ColorMap * const cmap, + pixval const maxval) { + + pixval retval; + + if (maxval >= 255) + retval = maxval; + else { + if (!HAS_COLORLUT(cmap)) + retval = maxval; + else { + unsigned int i; + unsigned char maxlut; + maxlut = maxval; + for( i = 0; i < maxval; i++ ) { + if( cmap->redlut && cmap->redlut[i] > maxlut ) + maxlut = cmap->redlut[i]; + if( cmap->greenlut && cmap->greenlut[i] > maxlut ) + maxlut = cmap->greenlut[i]; + if( cmap->bluelut && cmap->bluelut[i] > maxlut ) + maxlut = cmap->bluelut[i]; + } + pm_message("warning - " + "%d-bit index into 8-bit color lookup table, " + "table maxval=%d", + pm_maxvaltobits(maxval), maxlut); + if( maxlut != maxval ) + retval = 255; + else + retval = maxval; + pm_message(" assuming image maxval=%d", retval); + } + } + return retval; +} + + + +static void +get_color(cmap, idx, red, green, blue) + ColorMap *cmap; + int idx; + pixval *red, *green, *blue; +{ + if( HAS_COLORMAP(cmap) ) { + pixval r, g, b; + + if( idx >= cmap->ncolors ) + pm_error("color index out of range: %d (max %d)", + idx, cmap->ncolors); + r = PPM_GETR(cmap->color[idx]); + g = PPM_GETG(cmap->color[idx]); + b = PPM_GETB(cmap->color[idx]); + + *red = lookup_red(cmap, r); + *green = lookup_green(cmap, g); + *blue = lookup_blue(cmap, b); + } + else { + *red = *green = *blue = lookup_mono(cmap, idx); + } +} + + +/**************************************************************************** + Conversion functions + ****************************************************************************/ + +static void +std_to_ppm(FILE * const ifp, + long const chunksize, + BitMapHeader * const bmhd, + ColorMap * const cmap, + long const viewportmodes); + +static void +ham_to_ppm(FILE * const ifp, + long const chunksize, + BitMapHeader * const bmhd, + ColorMap * const cmap, + long const viewportmodes) { + + int cols, rows, hambits, hammask, hamshift, hammask2, col, row; + pixval maxval; + rawtype *rawrow; + unsigned char hamlut[256]; + + cols = bmhd->w; + rows = bmhd->h; + hambits = bmhd->nPlanes - 2; + hammask = (1 << hambits) - 1; + hamshift = 8 - hambits; + hammask2 = (1 << hamshift) - 1; + + if( hambits > 8 || hambits < 1 ) { + int const assumed_viewportmodes = viewportmodes & ~(vmHAM); + + pm_message("%d-plane HAM?? - interpreting image as a normal ILBM", + bmhd->nPlanes); + std_to_ppm(ifp, chunksize, bmhd, cmap, assumed_viewportmodes); + return; + } else { + unsigned long remainingChunksize; + + pm_message("input is a %sHAM%d file", + HAS_MULTIPALETTE(cmap) ? "multipalette " : "", + bmhd->nPlanes); + + if( HAS_COLORLUT(cmap) || HAS_MONOLUT(cmap) ) { + pm_message("warning - color lookup tables ignored in HAM"); + if( cmap->redlut ) free(cmap->redlut); + if( cmap->greenlut ) free(cmap->greenlut); + if( cmap->bluelut ) free(cmap->bluelut); + if( cmap->monolut ) free(cmap->monolut); + cmap->redlut = cmap->greenlut = cmap->bluelut = + cmap->monolut = NULL; + } + if( !HAS_COLORMAP(cmap) ) { + pm_message("no colormap - interpreting values as grayscale"); + maxval = pm_bitstomaxval(hambits); + for( col = 0; col <= maxval; col++ ) + hamlut[col] = col * MAXCOLVAL / maxval; + cmap->monolut = hamlut; + } + + if( transpName ) { + MALLOCVAR_NOFAIL(transpColor); + *transpColor = ppm_parsecolor(transpName, MAXCOLVAL); + } + + if( HAS_MULTIPALETTE(cmap) ) + multi_init(cmap, viewportmodes); + + rawrow = alloc_rawrow(cols); + + ppm_writeppminit(stdout, cols, rows, MAXCOLVAL, 0); + + remainingChunksize = chunksize; /* initial value */ + + for (row = 0; row < rows; ++row) { + pixval r, g, b; + + if( HAS_MULTIPALETTE(cmap) ) + multi_update(cmap, row); + + decode_row(ifp, &remainingChunksize, rawrow, bmhd->nPlanes, bmhd); + decode_mask(ifp, &remainingChunksize, rawrow, bmhd); + + r = g = b = 0; + for( col = 0; col < cols; col++ ) { + int idx = rawrow[col] & hammask; + + if( transpColor && maskrow && maskrow[col] == PBM_WHITE ) + pixelrow[col] = *transpColor; + else { + switch((rawrow[col] >> hambits) & 0x03) { + case HAMCODE_CMAP: + get_color(cmap, idx, &r, &g, &b); + break; + case HAMCODE_BLUE: + b = ((b & hammask2) | (idx << hamshift)); + /*b = hamlut[idx];*/ + break; + case HAMCODE_RED: + r = ((r & hammask2) | (idx << hamshift)); + /*r = hamlut[idx];*/ + break; + case HAMCODE_GREEN: + g = ((g & hammask2) | (idx << hamshift)); + /*g = hamlut[idx];*/ + break; + default: + pm_error("ham_to_ppm(): " + "impossible HAM code - can't happen"); + } + PPM_ASSIGN(pixelrow[col], r, g, b); + } + } + ppm_writeppmrow(stdout, pixelrow, cols, MAXCOLVAL, 0); + } + chunk_end(ifp, ID_BODY, remainingChunksize); + } +} + + + +static void +std_to_ppm(FILE * const ifP, + long const chunksize, + BitMapHeader * const bmhd, + ColorMap * const cmap, + long const viewportmodes) { + + if (viewportmodes & vmHAM) { + ham_to_ppm(ifP, chunksize, bmhd, cmap, viewportmodes); + } else { + unsigned int const cols = bmhd->w; + unsigned int const rows = bmhd->h; + + rawtype *rawrow; + unsigned int row, col; + pixval maxval; + unsigned long remainingChunksize; + + pm_message("input is a %d-plane %s%sILBM", bmhd->nPlanes, + HAS_MULTIPALETTE(cmap) ? "multipalette " : "", + viewportmodes & vmEXTRA_HALFBRITE ? "EHB " : "" + ); + + if( bmhd->nPlanes > MAXPLANES ) + pm_error("too many planes (max %d)", MAXPLANES); + + if( HAS_COLORMAP(cmap) ) { + if( viewportmodes & vmEXTRA_HALFBRITE ) + ehbcmap(cmap); /* Modifies *cmap */ + maxval = MAXCOLVAL; + } + else { + pm_message("no colormap - interpreting values as grayscale"); + maxval = lut_maxval(cmap, pm_bitstomaxval(bmhd->nPlanes)); + if( maxval > PPM_OVERALLMAXVAL ) + pm_error("nPlanes is too large"); + } + + if( transpName ) { + MALLOCVAR_NOFAIL(transpColor); + *transpColor = ppm_parsecolor(transpName, maxval); + } + + rawrow = alloc_rawrow(cols); + + if( HAS_MULTIPALETTE(cmap) ) + multi_init(cmap, viewportmodes); + + ppm_writeppminit( stdout, cols, rows, maxval, 0 ); + + remainingChunksize = chunksize; /* initial value */ + + for (row = 0; row < rows; ++row) { + + if( HAS_MULTIPALETTE(cmap) ) + multi_update(cmap, row); + + decode_row(ifP, &remainingChunksize, rawrow, bmhd->nPlanes, bmhd); + decode_mask(ifP, &remainingChunksize, rawrow, bmhd); + + for( col = 0; col < cols; col++ ) { + pixval r, g, b; + if( transpColor && maskrow && maskrow[col] == PBM_WHITE ) + pixelrow[col] = *transpColor; + else { + get_color(cmap, rawrow[col], &r, &g, &b); + PPM_ASSIGN(pixelrow[col], r, g, b); + } + } + ppm_writeppmrow(stdout, pixelrow, cols, maxval, 0); + } + chunk_end(ifP, ID_BODY, remainingChunksize); + } +} + + + +static void +deep_to_ppm(FILE * const ifP, + long const chunksize, + BitMapHeader * const bmhdP, + ColorMap * const cmap) { + + unsigned int const cols = bmhdP->w; + unsigned int const rows = bmhdP->h; + unsigned int const planespercolor = bmhdP->nPlanes / 3; + + unsigned int col, row; + rawtype *Rrow, *Grow, *Brow; + pixval maxval; + unsigned long remainingChunksize; + + pm_message("input is a deep (%d-bit) ILBM", bmhdP->nPlanes); + if( planespercolor > MAXPLANES ) + pm_error("too many planes (max %d)", MAXPLANES * 3); + + if( bmhdP->masking == mskHasTransparentColor || + bmhdP->masking == mskLasso ) { + pm_message("masking type '%s' in a deep ILBM?? - ignoring it", + mskNAME[bmhdP->masking]); + bmhdP->masking = mskNone; + } + + maxval = lut_maxval(cmap, pm_bitstomaxval(planespercolor)); + if( maxval > PPM_OVERALLMAXVAL ) + pm_error("nPlanes is too large"); + + if( transpName ) { + MALLOCVAR_NOFAIL(transpColor); + *transpColor = ppm_parsecolor(transpName, maxval); + } + + Rrow = alloc_rawrow(cols); + Grow = alloc_rawrow(cols); + Brow = alloc_rawrow(cols); + + ppm_writeppminit(stdout, cols, rows, maxval, 0); + + remainingChunksize = chunksize; /* initial value */ + + for( row = 0; row < rows; row++ ) { + decode_row(ifP, &remainingChunksize, Rrow, planespercolor, bmhdP); + decode_row(ifP, &remainingChunksize, Grow, planespercolor, bmhdP); + decode_row(ifP, &remainingChunksize, Brow, planespercolor, bmhdP); + decode_mask(ifP, &remainingChunksize, NULL, bmhdP); + + for( col = 0; col < cols; col++ ) { + if( transpColor && maskrow && maskrow[col] == PBM_WHITE ) + pixelrow[col] = *transpColor; + else + PPM_ASSIGN(pixelrow[col], lookup_red(cmap, Rrow[col]), + lookup_green(cmap, Grow[col]), + lookup_blue(cmap, Brow[col]) ); + } + ppm_writeppmrow(stdout, pixelrow, cols, maxval, 0); + } + chunk_end(ifP, ID_BODY, remainingChunksize); +} + + + +static void +dcol_to_ppm(FILE * const ifP, + long const chunksize, + BitMapHeader * const bmhdP, + ColorMap * const cmap, + DirectColor * const dcol) { + + unsigned int const cols = bmhdP->w; + unsigned int const rows = bmhdP->h; + unsigned int const redplanes = dcol->r; + unsigned int const greenplanes = dcol->g; + unsigned int const blueplanes = dcol->b; + + int col, row, i; + rawtype *Rrow, *Grow, *Brow; + pixval maxval, redmaxval, greenmaxval, bluemaxval; + pixval *redtable, *greentable, *bluetable; + unsigned long remainingChunksize; + + pm_message("input is a %d:%d:%d direct color ILBM", + redplanes, greenplanes, blueplanes); + + if( redplanes > MAXPLANES || + blueplanes > MAXPLANES || + greenplanes > MAXPLANES ) + pm_error("too many planes (max %d per color component)", MAXPLANES); + + if( bmhdP->nPlanes != (redplanes + greenplanes + blueplanes) ) + pm_error("%s/%s plane number mismatch", + ID2string(ID_BMHD), ID2string(ID_DCOL)); + + if( bmhdP->masking == mskHasTransparentColor || + bmhdP->masking == mskLasso ) { + pm_message("masking type '%s' in a direct color ILBM?? - ignoring it", + mskNAME[bmhdP->masking]); + bmhdP->masking = mskNone; + } + + if( HAS_COLORLUT(cmap) ) { + pm_error("This program does not know how to process a %s chunk " + "in direct color", ID2string(ID_CLUT)); + cmap->redlut = cmap->greenlut = cmap->bluelut = NULL; + } + + redmaxval = pm_bitstomaxval(redplanes); + greenmaxval = pm_bitstomaxval(greenplanes); + bluemaxval = pm_bitstomaxval(blueplanes); + maxval = MAX(redmaxval, MAX(greenmaxval, bluemaxval)); + + if( maxval > PPM_OVERALLMAXVAL ) + pm_error("too many planes"); + + if( redmaxval != maxval || greenmaxval != maxval || bluemaxval != maxval ) + pm_message("scaling colors to %d bits", pm_maxvaltobits(maxval)); + + MALLOCARRAY_NOFAIL(redtable, redmaxval +1); + MALLOCARRAY_NOFAIL(greentable, greenmaxval +1); + MALLOCARRAY_NOFAIL(bluetable, bluemaxval +1); + + for( i = 0; i <= redmaxval; i++ ) + redtable[i] = (i * maxval + redmaxval/2)/redmaxval; + for( i = 0; i <= greenmaxval; i++ ) + greentable[i] = (i * maxval + greenmaxval/2)/greenmaxval; + for( i = 0; i <= bluemaxval; i++ ) + bluetable[i] = (i * maxval + bluemaxval/2)/bluemaxval; + + if( transpName ) { + MALLOCVAR_NOFAIL(transpColor); + *transpColor = ppm_parsecolor(transpName, maxval); + } + + Rrow = alloc_rawrow(cols); + Grow = alloc_rawrow(cols); + Brow = alloc_rawrow(cols); + + ppm_writeppminit(stdout, cols, rows, maxval, 0); + + remainingChunksize = chunksize; /* initial value */ + + for( row = 0; row < rows; row++ ) { + decode_row(ifP, &remainingChunksize, Rrow, redplanes, bmhdP); + decode_row(ifP, &remainingChunksize, Grow, greenplanes, bmhdP); + decode_row(ifP, &remainingChunksize, Brow, blueplanes, bmhdP); + decode_mask(ifP, &remainingChunksize, NULL, bmhdP); + + for( col = 0; col < cols; col++ ) { + if( transpColor && maskrow && maskrow[col] == PBM_WHITE ) + pixelrow[col] = *transpColor; + else + PPM_ASSIGN( pixelrow[col], redtable[Rrow[col]], + greentable[Grow[col]], + bluetable[Brow[col]] ); + } + ppm_writeppmrow(stdout, pixelrow, cols, maxval, 0); + } + chunk_end(ifP, ID_BODY, remainingChunksize); +} + + + +static void +cmap_to_ppm(cmap) + ColorMap *cmap; +{ + ppm_colorrowtomapfile(stdout, cmap->color, cmap->ncolors, MAXCOLVAL); +#if 0 + int i; + ppm_writeppminit(stdout, cmap->ncolors, 1, MAXCOLVAL, 1); + for( i = 0; i < cmap->ncolors; i++ ) + ppm_writeppmrow(stdout, &(cmap->color[i]), 1, MAXCOLVAL, 1); +#endif +} + + + +static void +ipbm_to_ppm(FILE * const ifP, + long const chunksize, + BitMapHeader * const bmhdP, + ColorMap * const cmap, + long const viewportmodes) { + + unsigned int const cols = bmhdP->w; + unsigned int const rows = bmhdP->h; + + int col, row; + pixval maxval; + unsigned long remainingChunksize; + + pm_message("input is a %sPBM ", + HAS_MULTIPALETTE(cmap) ? "multipalette " : ""); + + if( bmhdP->nPlanes != 8 ) + pm_error("invalid number of planes for IFF-PBM: %d (must be 8)", + bmhdP->nPlanes); + + if( bmhdP->masking == mskHasMask ) + pm_error("Image has a maskplane, which is invalid in IFF-PBM"); + + if( HAS_COLORMAP(cmap) ) + maxval = MAXCOLVAL; + else { + pm_message("no colormap - interpreting values as grayscale"); + maxval = lut_maxval(cmap, pm_bitstomaxval(bmhdP->nPlanes)); + } + + if( transpName ) { + MALLOCVAR_NOFAIL(transpColor); + *transpColor = ppm_parsecolor(transpName, maxval); + } + + if( HAS_MULTIPALETTE(cmap) ) + multi_init(cmap, viewportmodes); + + MALLOCARRAY_NOFAIL(ilbmrow, cols); + + ppm_writeppminit(stdout, cols, rows, maxval, 0); + + remainingChunksize = chunksize; /* initial value */ + + for( row = 0; row < rows; row++ ) { + if( HAS_MULTIPALETTE(cmap) ) + multi_update(cmap, row); + + read_ilbm_plane(ifP, &remainingChunksize, cols, bmhdP->compression); + + for( col = 0; col < cols; col++ ) { + pixval r, g, b; + if( transpColor && maskrow && maskrow[col] == PBM_WHITE ) + pixelrow[col] = *transpColor; + else { + get_color(cmap, ilbmrow[col], &r, &g, &b); + PPM_ASSIGN(pixelrow[col], r, g, b); + } + } + ppm_writeppmrow(stdout, pixelrow, cols, maxval, 0); + } + chunk_end(ifP, ID_BODY, remainingChunksize); +} + + + +static void +rgbn_to_ppm(FILE * const ifP, + long const chunksize, + BitMapHeader * const bmhdP, + ColorMap * const cmap /* for CLUTs */ + ) { + + unsigned int const rows = bmhdP->h; + unsigned int const cols = bmhdP->w; + + int row, col, count, genlock, tries; + pixval r, g, b, maxval; + unsigned long remainingChunksize; + + pm_message("input is a %d-bit RGB image", (typeid == ID_RGB8 ? 8 : 4)); + + if( bmhdP->compression != 4 ) + pm_error("invalid compression mode for %s: %d (must be 4)", + ID2string(typeid), bmhdP->compression); + + switch( typeid ) { + case ID_RGBN: + if( bmhdP->nPlanes != 13 ) + pm_error("invalid number of planes for %s: %d (must be 13)", + ID2string(typeid), bmhdP->nPlanes); + maxval = lut_maxval(cmap, 15); + break; + case ID_RGB8: + if( bmhdP->nPlanes != 25 ) + pm_error("invalid number of planes for %s: %d (must be 25)", + ID2string(typeid), bmhdP->nPlanes); + maxval = 255; + break; + default: + pm_error("rgbn_to_ppm(): invalid IFF ID %s - can't happen", + ID2string(typeid)); + } + + if( transpName ) { + MALLOCVAR_NOFAIL(transpColor); + *transpColor = ppm_parsecolor(transpName, maxval); + } + + ppm_writeppminit(stdout, cols, rows, maxval, 0); + + remainingChunksize = chunksize; /* initial value */ + count = 0; + for( row = 0; row < rows; row++ ) { + for( col = 0; col < cols; col++ ) { + tries = 0; + while( !count ) { + if( typeid == ID_RGB8 ) { + r = lookup_red(cmap, get_byte(ifP, ID_BODY, + &remainingChunksize)); + g = lookup_green(cmap, get_byte(ifP, ID_BODY, + &remainingChunksize)); + b = lookup_blue(cmap, get_byte(ifP, ID_BODY, + &remainingChunksize)); + count = get_byte(ifP, ID_BODY, &remainingChunksize); + genlock = count & 0x80; + count &= 0x7f; + } + else { + int word; + word = get_big_short(ifP, ID_BODY, &remainingChunksize); + r = lookup_red(cmap, (word & 0xf000) >> 12); + g = lookup_green(cmap, (word & 0x0f00) >> 8); + b = lookup_blue(cmap, (word & 0x00f0) >> 4); + genlock = word & 0x0008; + count = word & 0x0007; + } + if( !count ) { + count = get_byte(ifP, ID_BODY, &remainingChunksize); + if( !count ) + count = + get_big_short(ifP, ID_BODY, &remainingChunksize); + if( !count ) + ++tries; + } + } + if( tries ) { + pm_message("warning - repeat count 0 at col %d row %d: " + "skipped %d RGB entr%s", + col, row, tries, (tries == 1 ? "y" : "ies")); + } + if( maskfile ) { + /* genlock bit set -> transparent */ + if( genlock ) + maskrow[col] = PBM_WHITE; + else + maskrow[col] = PBM_BLACK; + } + if( transpColor && maskrow && maskrow[col] == PBM_WHITE ) + pixelrow[col] = *transpColor; + else + PPM_ASSIGN(pixelrow[col], r, g, b); + --count; + } + ppm_writeppmrow(stdout, pixelrow, cols, maxval, 0); + if( maskfile ) { + pbm_writepbmrow(maskfile, maskrow, cols, 0); + wrotemask = 1; + } + } + chunk_end(ifP, ID_BODY, remainingChunksize); +} + +/**************************************************************************** + Multipalette chunk reader + + Currently there are three multipalette formats: + SHAM - sliced HAM (obselete) + CTBL - dynamic HAM/Hires (obselete) + PCHG - palette change + There is no official documentation available for SHAM and CTBL, so + this is mostly guesswork from other sources and hexdumps of pictures... + + CTBL format (dynamic HAM/Hires): + 16 bigendian words per row (16 bit: 0000rrrrggggbbbb) + This is simply an entire 4-bit colormap per row. + I have no idea what the DYCP chunk is for. + + SHAM format (sliced HAM): + 1 word - version info (?) - always 0 + 16 bigendian words per row (16 bit: 0000rrrrggggbbbb) + If the picture is laced, then there are only rows/2 changes, don't change + palette on odd lines. + + PCHG format: A detailed description of this format is available + from AmiNet as PCHGLib12.lha. + + ****************************************************************************/ + +/* Turn big-endian 4-byte long and 2-byte short stored at x (unsigned char *) + * into the native format of the CPU + */ +#define BIG_LONG(x) ( ((unsigned long)((x)[0]) << 24) + \ + ((unsigned long)((x)[1]) << 16) + \ + ((unsigned long)((x)[2]) << 8) + \ + ((unsigned long)((x)[3]) << 0) ) +#define BIG_WORD(x) ( ((unsigned short)((x)[0]) << 8) + \ + ((unsigned short)((x)[1]) << 0) ) + + + +static void +read_4bit_mp(FILE * const ifP, + IFF_ID const iffid, + long const chunksize, + ColorMap * const cmap) { + + int row, rows, i, type; + short data; + unsigned long remainingChunksize; + + type = (iffid == ID_SHAM ? MP_TYPE_SHAM : MP_TYPE_CTBL); + + if( cmap->mp_type >= type ) { + skip_chunk(ifP, iffid, chunksize); + } else { + if( cmap->mp_type ) + multi_free(cmap); + cmap->mp_type = type; + + remainingChunksize = chunksize; /* initial value */ + + if( type == MP_TYPE_SHAM ) { + cmap->mp_flags = MP_FLAGS_SKIPLACED; + get_big_short(ifP, iffid, &remainingChunksize); /* skip first wd */ + } + + cmap->mp_rows = rows = remainingChunksize/32; /* sizeof(word) * 16 */ + MALLOCARRAY_NOFAIL(cmap->mp_change, rows); + + for( row = 0; row < rows; row++ ) { + MALLOCARRAY_NOFAIL(cmap->mp_change[row], 17); /* 16 + sentinel */ + for( i = 0; i < 16; i++ ) { + data = get_big_short(ifP, iffid, &remainingChunksize); + cmap->mp_change[row][i].reg = i; + cmap->mp_change[row][i].r = + ((data & 0x0f00) >> 8) * FACTOR_4BIT; + cmap->mp_change[row][i].g = + ((data & 0x00f0) >> 4) * FACTOR_4BIT; + cmap->mp_change[row][i].b = + ((data & 0x000f) >> 0) * FACTOR_4BIT; + } + cmap->mp_change[row][16].reg = MP_REG_END; /* sentinel */ + } + chunk_end(ifP, iffid, remainingChunksize); + } +} + + + +static void +PCHG_DecompHuff(src, dest, tree, origsize) + unsigned char *src, *dest; + short *tree; + unsigned long origsize; +{ + unsigned long i = 0, bits = 0; + unsigned char thisbyte; + short *p; + + p = tree; + while( i < origsize ) { + if( bits == 0 ) { + thisbyte = *src++; + bits = 8; + } + if( thisbyte & (1 << 7) ) { + if( *p >= 0 ) { + *dest++ = (unsigned char)*p; + i++; + p = tree; + } + else + p += (*p / 2); + } + else { + p--; + if( *p > 0 && (*p & 0x100) ) { + *dest++ = (unsigned char )*p; + i++; + p = tree; + } + } + thisbyte <<= 1; + bits--; + } +} + + +static void +PCHG_Decompress(PCHG, CompHdr, compdata, compsize, comptree, data) + PCHGHeader *PCHG; + PCHGCompHeader *CompHdr; + unsigned char *compdata; + unsigned long compsize; + unsigned char *comptree; + unsigned char *data; +{ + short *hufftree; + unsigned long huffsize, i; + unsigned long treesize = CompHdr->CompInfoSize; + + switch( PCHG->Compression ) { + case PCHG_COMP_HUFFMAN: + +#ifdef DEBUG + pm_message("PCHG Huffman compression"); +#endif + /* turn big-endian 2-byte shorts into native format */ + huffsize = treesize/2; + MALLOCVAR_NOFAIL(hufftree); + for( i = 0; i < huffsize; i++ ) { + hufftree[i] = (short)BIG_WORD(comptree); + comptree += 2; + } + + /* decompress the change structure data */ + PCHG_DecompHuff(compdata, data, &hufftree[huffsize-1], + CompHdr->OriginalDataSize); + + free(hufftree); + break; + default: + pm_error("unknown PCHG compression type %d", PCHG->Compression); + } +} + + +static void +PCHG_ConvertSmall(PCHG, cmap, mask, datasize) + PCHGHeader *PCHG; + ColorMap *cmap; + unsigned char *mask; + unsigned long datasize; +{ + unsigned char *data; + unsigned char thismask; + int bits, row, i, changes, masklen, reg; + unsigned char ChangeCount16, ChangeCount32; + unsigned short SmallChange; + unsigned long totalchanges = 0; + int changedlines = PCHG->ChangedLines; + + masklen = 4 * MaskLongWords(PCHG->LineCount); + data = mask + masklen; datasize -= masklen; + + bits = 0; + for( row = PCHG->StartLine; changedlines && row < 0; row++ ) { + if( bits == 0 ) { + if( masklen == 0 ) goto fail2; + thismask = *mask++; + --masklen; + bits = 8; + } + if( thismask & (1<<7) ) { + if( datasize < 2 ) goto fail; + ChangeCount16 = *data++; + ChangeCount32 = *data++; + datasize -= 2; + + changes = ChangeCount16 + ChangeCount32; + for( i = 0; i < changes; i++ ) { + if( totalchanges >= PCHG->TotalChanges ) goto fail; + if( datasize < 2 ) goto fail; + SmallChange = BIG_WORD(data); data += 2; datasize -= 2; + reg = ((SmallChange & 0xf000) >> 12) + + (i >= ChangeCount16 ? 16 : 0); + cmap->mp_init[reg - PCHG->MinReg].reg = reg; + cmap->mp_init[reg - PCHG->MinReg].r = + ((SmallChange & 0x0f00) >> 8) * FACTOR_4BIT; + cmap->mp_init[reg - PCHG->MinReg].g = + ((SmallChange & 0x00f0) >> 4) * FACTOR_4BIT; + cmap->mp_init[reg - PCHG->MinReg].b = + ((SmallChange & 0x000f) >> 0) * FACTOR_4BIT; + ++totalchanges; + } + --changedlines; + } + thismask <<= 1; + bits--; + } + + for( row = PCHG->StartLine; changedlines && row < cmap->mp_rows; row++ ) { + if( bits == 0 ) { + if( masklen == 0 ) goto fail2; + thismask = *mask++; + --masklen; + bits = 8; + } + if( thismask & (1<<7) ) { + if( datasize < 2 ) goto fail; + ChangeCount16 = *data++; + ChangeCount32 = *data++; + datasize -= 2; + + changes = ChangeCount16 + ChangeCount32; + MALLOCARRAY_NOFAIL(cmap->mp_change[row], changes + 1); + for( i = 0; i < changes; i++ ) { + if( totalchanges >= PCHG->TotalChanges ) goto fail; + if( datasize < 2 ) goto fail; + SmallChange = BIG_WORD(data); data += 2; datasize -= 2; + reg = ((SmallChange & 0xf000) >> 12) + + (i >= ChangeCount16 ? 16 : 0); + cmap->mp_change[row][i].reg = reg; + cmap->mp_change[row][i].r = + ((SmallChange & 0x0f00) >> 8) * FACTOR_4BIT; + cmap->mp_change[row][i].g = + ((SmallChange & 0x00f0) >> 4) * FACTOR_4BIT; + cmap->mp_change[row][i].b = + ((SmallChange & 0x000f) >> 0) * FACTOR_4BIT; + ++totalchanges; + } + cmap->mp_change[row][changes].reg = MP_REG_END; + --changedlines; + } + thismask <<= 1; + bits--; + } + if( totalchanges != PCHG->TotalChanges ) + pm_message("warning - got %ld change structures, " + "chunk header reports %ld", + totalchanges, PCHG->TotalChanges); + return; +fail: + pm_error("insufficient data in SmallLineChanges structures"); +fail2: + pm_error("insufficient data in line mask"); +} + + +static void +PCHG_ConvertBig(PCHG, cmap, mask, datasize) + PCHGHeader *PCHG; + ColorMap *cmap; + unsigned char *mask; + unsigned long datasize; +{ + unsigned char *data; + unsigned char thismask; + int bits, row, i, changes, masklen, reg; + unsigned long totalchanges = 0; + int changedlines = PCHG->ChangedLines; + + masklen = 4 * MaskLongWords(PCHG->LineCount); + data = mask + masklen; datasize -= masklen; + + bits = 0; + for( row = PCHG->StartLine; changedlines && row < 0; row++ ) { + if( bits == 0 ) { + if( masklen == 0 ) goto fail2; + thismask = *mask++; + --masklen; + bits = 8; + } + if( thismask & (1<<7) ) { + if( datasize < 2 ) goto fail; + changes = BIG_WORD(data); data += 2; datasize -= 2; + + for( i = 0; i < changes; i++ ) { + if( totalchanges >= PCHG->TotalChanges ) goto fail; + if( datasize < 6 ) goto fail; + reg = BIG_WORD(data); data += 2; + cmap->mp_init[reg - PCHG->MinReg].reg = reg; + ++data; /* skip alpha */ + cmap->mp_init[reg - PCHG->MinReg].r = *data++; + cmap->mp_init[reg - PCHG->MinReg].b = *data++; /* yes, RBG */ + cmap->mp_init[reg - PCHG->MinReg].g = *data++; + datasize -= 6; + ++totalchanges; + } + --changedlines; + } + thismask <<= 1; + bits--; + } + + for( row = PCHG->StartLine; changedlines && row < cmap->mp_rows; row++ ) { + if( bits == 0 ) { + if( masklen == 0 ) goto fail2; + thismask = *mask++; + --masklen; + bits = 8; + } + if( thismask & (1<<7) ) { + if( datasize < 2 ) goto fail; + changes = BIG_WORD(data); data += 2; datasize -= 2; + + MALLOCARRAY_NOFAIL(cmap->mp_change[row], changes + 1); + for( i = 0; i < changes; i++ ) { + if( totalchanges >= PCHG->TotalChanges ) goto fail; + if( datasize < 6 ) goto fail; + reg = BIG_WORD(data); data += 2; + cmap->mp_change[row][i].reg = reg; + ++data; /* skip alpha */ + cmap->mp_change[row][i].r = *data++; + cmap->mp_change[row][i].b = *data++; /* yes, RBG */ + cmap->mp_change[row][i].g = *data++; + datasize -= 6; + ++totalchanges; + } + cmap->mp_change[row][changes].reg = MP_REG_END; + --changedlines; + } + thismask <<= 1; + bits--; + } + if( totalchanges != PCHG->TotalChanges ) + pm_message("warning - got %ld change structures, " + "chunk header reports %ld", + totalchanges, PCHG->TotalChanges); + return; +fail: + pm_error("insufficient data in BigLineChanges structures"); +fail2: + pm_error("insufficient data in line mask"); +} + + +static void +read_pchg(FILE * const ifp, + IFF_ID const iffid, + long const chunksize, + ColorMap * const cmap) { + + if( cmap->mp_type >= MP_TYPE_PCHG ) { + skip_chunk(ifp, iffid, chunksize); + } else { + PCHGHeader PCHG; + unsigned char *data; + unsigned long datasize; + unsigned long remainingChunksize; + int i; + + if( cmap->mp_type ) + multi_free(cmap); + cmap->mp_type = MP_TYPE_PCHG; + + remainingChunksize = chunksize; /* initial value */ + + PCHG.Compression = get_big_short(ifp, iffid, &remainingChunksize); + PCHG.Flags = get_big_short(ifp, iffid, &remainingChunksize); + PCHG.StartLine = get_big_short(ifp, iffid, &remainingChunksize); + PCHG.LineCount = get_big_short(ifp, iffid, &remainingChunksize); + PCHG.ChangedLines= get_big_short(ifp, iffid, &remainingChunksize); + PCHG.MinReg = get_big_short(ifp, iffid, &remainingChunksize); + PCHG.MaxReg = get_big_short(ifp, iffid, &remainingChunksize); + PCHG.MaxChanges = get_big_short(ifp, iffid, &remainingChunksize); + PCHG.TotalChanges= get_big_long(ifp, iffid, &remainingChunksize); + +#ifdef DEBUG + pm_message("PCHG StartLine : %d", PCHG.StartLine); + pm_message("PCHG LineCount : %d", PCHG.LineCount); + pm_message("PCHG ChangedLines: %d", PCHG.ChangedLines); + pm_message("PCHG TotalChanges: %d", PCHG.TotalChanges); +#endif + + if( PCHG.Compression != PCHG_COMP_NONE ) { + PCHGCompHeader CompHdr; + unsigned char *compdata, *comptree; + long treesize, compsize; + + CompHdr.CompInfoSize = + get_big_long(ifp, iffid, &remainingChunksize); + CompHdr.OriginalDataSize = + get_big_long(ifp, iffid, &remainingChunksize); + + treesize = CompHdr.CompInfoSize; + MALLOCVAR_NOFAIL(comptree); + read_bytes(ifp, treesize, comptree, iffid, &remainingChunksize); + + compsize = remainingChunksize; + MALLOCARRAY_NOFAIL(compdata, remainingChunksize); + read_bytes(ifp, compsize, compdata, iffid, &remainingChunksize); + + datasize = CompHdr.OriginalDataSize; + MALLOCARRAY_NOFAIL(data, datasize); + PCHG_Decompress(&PCHG, &CompHdr, compdata, + compsize, comptree, data); + + free(comptree); + free(compdata); + } else { +#ifdef DEBUG + pm_message("uncompressed PCHG"); +#endif + datasize = remainingChunksize; + MALLOCARRAY_NOFAIL(data, datasize); + read_bytes(ifp, datasize, data, iffid, &remainingChunksize); + } + + if( PCHG.Flags & PCHGF_USE_ALPHA ) + pm_message("warning - ignoring PCHG alpha channel because " + "this program doesn't know what to do with it"); + + cmap->mp_rows = PCHG.StartLine + PCHG.LineCount; + MALLOCARRAY_NOFAIL(cmap->mp_change, cmap->mp_rows); + for( i = 0; i < cmap->mp_rows; i++ ) + cmap->mp_change[i] = NULL; + if( PCHG.StartLine < 0 ) { + int nch; + nch = PCHG.MaxReg - PCHG.MinReg +1; + MALLOCARRAY_NOFAIL(cmap->mp_init, nch + 1); + for( i = 0; i < nch; i++ ) + cmap->mp_init[i].reg = MP_REG_IGNORE; + cmap->mp_init[nch].reg = MP_REG_END; + } + + if( PCHG.Flags & PCHGF_12BIT ) { +#ifdef DEBUG + pm_message("SmallLineChanges"); +#endif + PCHG_ConvertSmall(&PCHG, cmap, data, datasize); + } + else { + if( PCHG.Flags & PCHGF_32BIT ) { +#ifdef DEBUG + pm_message("BigLineChanges"); +#endif + PCHG_ConvertBig(&PCHG, cmap, data, datasize); + } + else + pm_error("unknown palette changes structure " + "format in %s chunk", + ID2string(iffid)); + } + free(data); + chunk_end(ifp, iffid, remainingChunksize); + } +} + + + +static bool +ignored_iffid(IFF_ID const iffid, + IFF_ID const ignorelist[], + unsigned int const ignorecount) { + + bool ignore; + + unsigned int i; + ignore = FALSE; /* initial assumption */ + for( i = 0; i < ignorecount && !ignore; i++ ) { + if( iffid == ignorelist[i] ) + ignore = TRUE; + } + return ignore; +} + + + +static void +process_body( FILE * const ifp, + long const chunksize, + BitMapHeader * const bmhdP, + ColorMap * const cmap, + FILE * const maskfile, + int const fakeviewport, + int const isdeepopt, + DirectColor * const dcol, + int * const viewportmodesP) { + + if( bmhdP == NULL ) + pm_error("%s chunk without %s chunk", + ID2string(ID_BODY), ID2string(ID_BMHD)); + + check_cmap(bmhdP, cmap); + + pixelrow = ppm_allocrow(bmhdP->w); + if( maskfile ) { + maskrow = pbm_allocrow(bmhdP->w); + pbm_writepbminit(maskfile, bmhdP->w, bmhdP->h, 0); + } + + if( typeid == ID_ILBM ) { + int isdeep; + + MALLOCARRAY_NOFAIL(ilbmrow, RowBytes(bmhdP->w)); + *viewportmodesP |= fakeviewport; /* -isham/-isehb */ + + if( isdeepopt > 0 && (bmhdP->nPlanes % 3 != 0) ) { + pm_message("cannot interpret %d-plane image as 'deep' " + "(# of planes must be divisible by 3)", + bmhdP->nPlanes); + isdeep = 0; + } else + isdeep = isdeepopt; + + if( isdeep > 0 ) + deep_to_ppm(ifp, chunksize, bmhdP, cmap); + else if( dcol ) + dcol_to_ppm(ifp, chunksize, bmhdP, cmap, dcol); + else if( bmhdP->nPlanes > 8 ) { + if( bmhdP->nPlanes <= 16 && HAS_COLORMAP(cmap) ) + std_to_ppm(ifp, chunksize, bmhdP, cmap, *viewportmodesP); + else if( isdeep >= 0 && (bmhdP->nPlanes % 3 == 0) ) + deep_to_ppm(ifp, chunksize, bmhdP, cmap); + else if( bmhdP->nPlanes <= 16 ) + /* will be interpreted as grayscale */ + std_to_ppm(ifp, chunksize, bmhdP, cmap, *viewportmodesP); + else + pm_error("don't know how to interpret %d-plane image", + bmhdP->nPlanes); + } else + std_to_ppm(ifp, chunksize, bmhdP, cmap, *viewportmodesP); + } else if( typeid == ID_PBM ) + ipbm_to_ppm(ifp, chunksize, bmhdP, cmap, *viewportmodesP); + else /* RGBN or RGB8 */ + rgbn_to_ppm(ifp, chunksize, bmhdP, cmap); +} + + + +static void +process_chunk(FILE * const ifp, + long const formsize, + IFF_ID const ignorelist[], + unsigned int const ignorecount, + int const fakeviewport, + int const viewportmask, + int const isdeepopt, + bool const cmaponly, + bool * const bodyChunkProcessedP, + bool * const endchunkP, + BitMapHeader ** const bmhdP, + ColorMap * const cmap, + DirectColor ** const dcolP, + int * const viewportmodesP, + long * const bytesReadP + ) { + + IFF_ID iffid; + long chunksize; + long bytesread; + + bytesread = 0; + + iffid = get_big_long(ifp, ID_FORM, NULL); + chunksize = get_big_long(ifp, iffid, NULL); + bytesread += 8; + + if (debug) + pm_message("reading %s chunk: %ld bytes", + ID2string(iffid), chunksize); + + if( ignored_iffid(iffid, ignorelist, ignorecount) ) { + pm_message("ignoring %s chunk", ID2string(iffid)); + skip_chunk(ifp, iffid, chunksize); + } else if( iffid == ID_END ) { + /* END chunks are not officially valid in IFF, but + suggested as a future expansion for stream-writing, + see Amiga RKM Devices, 3rd Ed, page 376 + */ + if( chunksize != 0 ) { + pm_message("warning - non-0 %s chunk", ID2string(iffid)); + skip_chunk(ifp, iffid, chunksize); + } + if( formsize != 0xffffffff ) + pm_message("warning - %s chunk with FORM size 0x%08lx " + "(should be 0x%08x)", + ID2string(iffid), formsize, 0xffffffff); + *endchunkP = 1; + } else if( *bodyChunkProcessedP ) { + pm_message("%s chunk found after %s chunk - skipping", + ID2string(iffid), ID2string(ID_BODY)); + skip_chunk(ifp, iffid, chunksize); + } else + switch( iffid ) { + case ID_BMHD: + *bmhdP = read_bmhd(ifp, iffid, chunksize); + break; + case ID_CMAP: + read_cmap(ifp, iffid, chunksize, cmap); + break; + case ID_CMYK: + read_cmyk(ifp, iffid, chunksize, cmap); + break; + case ID_CLUT: + read_clut(ifp, iffid, chunksize, cmap); + break; + case ID_CAMG: + if( chunksize != CAMGChunkSize ) + pm_error("%s chunk size mismatch", ID2string(iffid)); + *viewportmodesP = get_big_long(ifp, ID_CAMG, NULL); + *viewportmodesP &= viewportmask; /* -isnotham/-isnotehb */ + break; + case ID_PCHG: + read_pchg(ifp, iffid, chunksize, cmap); + break; + case ID_CTBL: + case ID_SHAM: + read_4bit_mp(ifp, iffid, chunksize, cmap); + break; + case ID_DCOL: + if( chunksize != DirectColorSize ) + pm_error("%s chunk size mismatch", ID2string(iffid)); + MALLOCVAR_NOFAIL(*dcolP); + (*dcolP)->r = get_byte(ifp, iffid, NULL); + (*dcolP)->g = get_byte(ifp, iffid, NULL); + (*dcolP)->b = get_byte(ifp, iffid, NULL); + (void)get_byte(ifp, iffid, NULL); /* skip pad byte */ + break; + case ID_BODY: + if( cmaponly || (*bmhdP && (*bmhdP)->nPlanes == 0 )) { + skip_chunk(ifp, ID_BODY, chunksize); + return; + } + + process_body(ifp, chunksize, *bmhdP, cmap, + maskfile, fakeviewport, isdeepopt, *dcolP, + viewportmodesP); + + *bodyChunkProcessedP = TRUE; + + break; + case ID_GRAB: case ID_DEST: case ID_SPRT: case ID_CRNG: + case ID_CCRT: case ID_DYCP: case ID_DPPV: case ID_DRNG: + case ID_EPSF: case ID_JUNK: case ID_CNAM: case ID_PRVW: + case ID_TINY: case ID_DPPS: + skip_chunk(ifp, iffid, chunksize); + break; + case ID_copy: case ID_AUTH: case ID_NAME: case ID_ANNO: + case ID_TEXT: case ID_FVER: + if( verbose ) + display_chunk(ifp, iffid, chunksize); + else + skip_chunk(ifp, iffid, chunksize); + break; + case ID_DPI: + { + int x, y; + + x = get_big_short(ifp, ID_DPI, NULL); + y = get_big_short(ifp, ID_DPI, NULL); + if( verbose ) + pm_message("%s chunk: dpi_x = %d dpi_y = %d", + ID2string(ID_DPI), x, y); + } + break; + default: + pm_message("unknown chunk type %s - skipping", + ID2string(iffid)); + skip_chunk(ifp, iffid, chunksize); + break; + } + + bytesread += chunksize; + + if( ODD(chunksize) ) { + (void) get_byte(ifp, iffid, NULL); + bytesread += 1; + } + *bytesReadP = bytesread; +} + + +int +main(int argc, char *argv[]) { + + FILE *ifp; + int argn; + short cmaponly = 0, isdeepopt = 0; + bool endchunk; + bool bodyChunkProcessed; + long formsize, bytesread; + int viewportmodes = 0, fakeviewport = 0, viewportmask; + IFF_ID firstIffid; + BitMapHeader *bmhdP = NULL; + ColorMap * cmap; + DirectColor *dcol = NULL; +#define MAX_IGNORE 16 + IFF_ID ignorelist[MAX_IGNORE]; + int ignorecount = 0; + char *maskname; + const char * const usage = + "[-verbose] [-ignore <chunkID> [-ignore <chunkID>] ...] " + "[-isham|-isehb|-isdeep|-isnotham|-isnotehb|-isnotdeep] " + "[-cmaponly] [-adjustcolors] " + "[-transparent <color>] [-maskfile <filename>] [ilbmfile]"; + ppm_init(&argc, argv); + + viewportmask = 0xffffffff; + + argn = 1; + while( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' ) { + if( pm_keymatch(argv[argn], "-verbose", 2) ) + verbose = 1; + else if( pm_keymatch(argv[argn], "-noverbose", 4) ) + verbose = 0; + else if( pm_keymatch(argv[argn], "-isham", 4) ) + fakeviewport |= vmHAM; + else if( pm_keymatch(argv[argn], "-isehb", 4) ) + fakeviewport |= vmEXTRA_HALFBRITE; + else if( pm_keymatch(argv[argn], "-isdeep", 4) ) + isdeepopt = 1; + else if( pm_keymatch(argv[argn], "-isnotham", 7) ) + viewportmask &= ~(vmHAM); + else if( pm_keymatch(argv[argn], "-isnotehb", 7) ) + viewportmask &= ~(vmEXTRA_HALFBRITE); + else if( pm_keymatch(argv[argn], "-isnotdeep", 7) ) + isdeepopt = -1; + else if( pm_keymatch(argv[argn], "-cmaponly", 2) ) + cmaponly = 1; + else if( pm_keymatch(argv[argn], "-adjustcolors", 2) ) + adjustcolors = 1; + else if( pm_keymatch(argv[argn], "-noadjustcolors", 4) ) + adjustcolors = 0; + else if( pm_keymatch(argv[argn], "-transparent", 2) ) { + if( ++argn >= argc ) + pm_usage(usage); + transpName = argv[argn]; + } else if( pm_keymatch(argv[argn], "-maskfile", 2) ) { + if( ++argn >= argc ) + pm_usage(usage); + maskname = argv[argn]; + maskfile = pm_openw(maskname); + } else if( pm_keymatch(argv[argn], "-ignore", 2) ) { + if( ++argn >= argc ) + pm_usage(usage); + if( strlen(argv[argn]) != 4 ) + pm_error("'-ignore' option needs a 4 byte chunk ID string " + "as argument"); + if( ignorecount >= MAX_IGNORE ) + pm_error("max %d chunk IDs to ignore", MAX_IGNORE); + ignorelist[ignorecount++] = + MAKE_ID(argv[argn][0], argv[argn][1], argv[argn][2], + argv[argn][3]); + } else + pm_usage(usage); + ++argn; + } + + if( argn < argc ) { + ifp = pm_openr( argv[argn] ); + argn++; + } else + ifp = stdin; + + if( argn != argc ) + pm_usage(usage); + + /* Read in the ILBM file. */ + + firstIffid = get_big_long(ifp, ID_FORM, NULL); + if( firstIffid != ID_FORM ) + pm_error("input is not a FORM type IFF file"); + formsize = get_big_long(ifp, ID_FORM, NULL); + typeid = get_big_long(ifp, ID_FORM, NULL); + if( typeid != ID_ILBM && typeid != ID_RGBN && typeid != ID_RGB8 && + typeid != ID_PBM ) + pm_error( "input is not an ILBM, RGBN, RGB8 or PBM " + "type FORM IFF file" ); + bytesread = 4; /* FORM and formsize do not count */ + + cmap = alloc_cmap(); + + /* Main loop, parsing the IFF FORM. */ + bodyChunkProcessed = FALSE; + endchunk = FALSE; + while( !endchunk && formsize-bytesread >= 8 ) { + long bytes_read_for_chunk; + + process_chunk(ifp, formsize, ignorelist, ignorecount, + fakeviewport, viewportmask, + isdeepopt, cmaponly, + &bodyChunkProcessed, + &endchunk, &bmhdP, cmap, &dcol, + &viewportmodes, &bytes_read_for_chunk); + + bytesread += bytes_read_for_chunk; + } + + if( maskfile ) { + pm_close(maskfile); + if( !wrotemask ) + remove(maskname); + pbm_freerow(maskrow); + } + + if( cmaponly || (bmhdP && bmhdP->nPlanes == 0 )) { + if( HAS_COLORMAP(cmap) ) { + check_cmap(bmhdP, cmap); + cmap_to_ppm(cmap); + } else + pm_error("no colormap"); + } else if( bodyChunkProcessed ) { + if( HAS_COLORMAP(cmap) ) { + pm_message("input is a colormap file"); + check_cmap(bmhdP, cmap); + cmap_to_ppm(cmap); + } else + pm_error("no %s or %s chunk found", + ID2string(ID_BODY), ID2string(ID_CMAP)); + } + + { + unsigned int skipped; + + for( skipped = 0; fgetc(ifp) != EOF; ++skipped ) + bytesread++; + + if( skipped > 0 ) + pm_message("skipped %u extraneous byte%s after last chunk", + skipped, (skipped == 1 ? "" : "s")); + } + pm_close(ifp); + + if( !endchunk && bytesread != formsize ) { + pm_message("warning - file length/FORM size field mismatch " + "(%ld != %ld+8)", + bytesread+8 /* FORM+size */, formsize); + } + + return 0; +} diff --git a/converter/ppm/imgtoppm.c b/converter/ppm/imgtoppm.c new file mode 100644 index 00000000..7078b882 --- /dev/null +++ b/converter/ppm/imgtoppm.c @@ -0,0 +1,142 @@ +/* imgtoppm.c - read an Img-whatnot file and produce a portable pixmap +** +** Based on a simple conversion program posted to comp.graphics by Ed Falk. +** +** Copyright (C) 1989 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include <string.h> +#include "ppm.h" + + + +int +main(int argc, char ** argv) { + + FILE* ifp; + pixel* pixelrow; + pixel colormap[256]; + register pixel* pP; + unsigned int cols; + unsigned int rows; + int argn, row, i; + int col; + pixval maxval; + unsigned int cmaplen; + int len, gotAT, gotCM, gotPD; + unsigned char buf[4096]; + unsigned char* bP; + + + ppm_init( &argc, argv ); + + argn = 1; + + if ( argn < argc ) + { + ifp = pm_openr( argv[argn] ); + argn++; + } + else + ifp = stdin; + + if ( argn != argc ) + pm_usage( "[imgfile]" ); + + /* Get signature. */ + fread( buf, 8, 1, ifp ); + buf[8] = '\0'; + + /* Get entries. */ + gotAT = 0; + gotCM = 0; + gotPD = 0; + while ( fread( buf, 2, 1, ifp ) == 1 ) + { + if ( strncmp( (char*) buf, "AT", 2 ) == 0 ) + { + if ( fread( buf, 8, 1, ifp ) != 1 ) + pm_error( "bad attributes header" ); + buf[8] = '\0'; + len = atoi( (char*) buf ); + if ( fread( buf, len, 1, ifp ) != 1 ) + pm_error( "bad attributes buf" ); + buf[len] = '\0'; + sscanf( (char*) buf, "%4u%4u%4u", &cols, &rows, &cmaplen ); + maxval = 255; + gotAT = 1; + } + + else if ( strncmp( (char*) buf, "CM", 2 ) == 0 ) + { + if ( ! gotAT ) + pm_error( "missing attributes header" ); + if ( fread( buf, 8, 1, ifp ) != 1 ) + pm_error( "bad colormap header" ); + buf[8] = '\0'; + len = atoi((char*) buf ); + if ( fread( buf, len, 1, ifp ) != 1 ) + pm_error( "bad colormap buf" ); + if ( cmaplen * 3 != len ) + { + pm_message( + "cmaplen (%d) and colormap buf length (%d) do not match", + cmaplen, len ); + if ( cmaplen * 3 < len ) + len = cmaplen * 3; + else if ( cmaplen * 3 > len ) + cmaplen = len / 3; + } + for ( i = 0; i < len; i += 3 ) + PPM_ASSIGN( colormap[i / 3], buf[i], buf[i + 1], buf[i + 2] ); + gotCM = 1; + } + + else if ( strncmp( (char*) buf, "PD", 2 ) == 0 ) + { + if ( fread( buf, 8, 1, ifp ) != 1 ) + pm_error( "bad pixel data header" ); + buf[8] = '\0'; + len = atoi((char*) buf ); + if ( len != cols * rows ) + pm_message( + "pixel data length (%d) does not match image size (%d)", + len, cols * rows ); + + ppm_writeppminit( stdout, cols, rows, maxval, 0 ); + pixelrow = ppm_allocrow( cols ); + + for ( row = 0; row < rows; row++ ) + { + if ( fread( buf, 1, cols, ifp ) != cols ) + pm_error( "EOF / read error" ); + for ( col = 0, pP = pixelrow, bP = buf; + col < cols; col++, pP++, bP++ ) + { + if ( gotCM ) + *pP = colormap[*bP]; + else + PPM_ASSIGN( *pP, *bP, *bP, *bP ); + } + ppm_writeppmrow( stdout, pixelrow, cols, maxval, 0 ); + } + gotPD = 1; + + } + } + if ( ! gotPD ) + pm_error( "missing pixel data header" ); + + pm_close( ifp ); + /* If the program failed, it previously aborted with nonzero completion + code, via various function calls. + */ + return 0; +} diff --git a/converter/ppm/leaftoppm.c b/converter/ppm/leaftoppm.c new file mode 100644 index 00000000..889400f6 --- /dev/null +++ b/converter/ppm/leaftoppm.c @@ -0,0 +1,216 @@ +/* leaftoppm.c - read an ileaf img file and write a PPM + * + * Copyright (C) 1994 by Bill O'Donnell. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. This software is provided "as is" without express or + * implied warranty. + * + * known problems: doesn't do compressed ileaf images. + * + */ + +#include <stdio.h> +#include "ppm.h" + +#define MAXCOLORS 256 +#define LEAF_MAXVAL 255 + +static void +leaf_init(FILE * const fp, + int * const colsP, + int * const rowsP, + int * const depthP, + int * const ncolorsP, + pixel * const colors) { + + unsigned char buf[256]; + unsigned char compressed; + short version; + short cols; + short rows; + short depth; + long magic; + + pm_readbiglong(fp, &magic); + if (magic != 0x894f5053) + pm_error("Bad magic number. First 4 bytes should be " + "0x894f5053 but are instead 0x%08x", (unsigned)magic); + + /* version = 2 bytes + hres = 2 + vres = 2 + unique id = 4 + offset x = 2 + offset y = 2 + TOTAL = 14 bytes + */ + + pm_readbigshort(fp, &version); + + if (fread(buf, 1, 12, fp) != 12) + pm_error("bad header, short file?"); + + pm_readbigshort(fp, &cols); + *colsP = cols; + pm_readbigshort(fp, &rows); + *rowsP = rows; + pm_readbigshort(fp, &depth); + *depthP = depth; + + if ((compressed = fgetc(fp)) != 0) + pm_error("Can't do compressed images."); + + if ((*depthP == 1) && (version < 4)) { + fgetc(fp); + *ncolorsP = 0; + } else if ((*depthP == 8) && (version < 4)) { + fgetc(fp); + *ncolorsP = 0; + } else { + long format; + pm_readbiglong(fp, &format); + if (format == 0x29000000) { + /* color image */ + short ncolors; + pm_readbigshort(fp, &ncolors); + + if (ncolors > 0) { + /* 8-bit image */ + unsigned int i; + + if (ncolors > 256) + pm_error("Can't have > 256 colors in colormap."); + /* read colormap */ + for (i=0; i < 256; ++i) + PPM_PUTR(colors[i], fgetc(fp)); + for (i=0; i < 256; ++i) + PPM_PUTR(colors[i], fgetc(fp)); + for (i=0; i < 256; ++i) + PPM_PUTR(colors[i], fgetc(fp)); + *ncolorsP = ncolors; + } else { + /* 24-bit image */ + *ncolorsP = 0; + } + } else if (*depthP ==1) { + /* mono image */ + short dummy; + pm_readbigshort(fp, &dummy); /* must toss cmap size */ + *ncolorsP = 0; + } else { + /* gray image */ + short dummy; + pm_readbigshort(fp, &dummy); /* must toss cmap size */ + *ncolorsP = 0; + } + } +} + + + +int +main(int argc, char * argv[]) { + + pixval const maxval = LEAF_MAXVAL; + + FILE *ifd; + pixel colormap[MAXCOLORS]; + int rows, cols, row, col, depth, ncolors; + + + ppm_init(&argc, argv); + + if (argc-1 > 1) + pm_error("Too many arguments. Only argument is ileaf file name"); + + if (argc-1 == 1) + ifd = pm_openr(argv[1]); + else + ifd = stdin; + + leaf_init(ifd, &cols, &rows, &depth, &ncolors, colormap); + + if ((depth == 8) && (ncolors == 0)) { + /* gray image */ + gray * grayrow; + unsigned int row; + pgm_writepgminit( stdout, cols, rows, maxval, 0 ); + grayrow = pgm_allocrow(cols); + for (row = 0; row < rows; ++row) { + unsigned int col; + for ( col = 0; col < cols; ++col) + grayrow[col] = (gray)fgetc(ifd); + if (cols % 2) + fgetc (ifd); /* padding */ + pgm_writepgmrow(stdout, grayrow, cols, maxval, 0); + } + pgm_freerow(grayrow); + } else if (depth == 24) { + pixel * pixrow; + unsigned int row; + ppm_writeppminit(stdout, cols, rows, maxval, 0); + pixrow = ppm_allocrow(cols); + /* true color */ + for (row = 0; row < rows; ++row) { + for (col = 0; col < cols; ++col) + PPM_PUTR(pixrow[col], fgetc(ifd)); + if (cols % 2) + fgetc (ifd); /* padding */ + for (col = 0; col < cols; ++col) + PPM_PUTG(pixrow[col], fgetc(ifd)); + if (cols % 2) + fgetc (ifd); /* padding */ + for (col = 0; col < cols; ++col) + PPM_PUTB(pixrow[col], fgetc(ifd)); + if (cols % 2) + fgetc (ifd); /* padding */ + ppm_writeppmrow(stdout, pixrow, cols, maxval, 0); + } + ppm_freerow(pixrow); + } else if (depth == 8) { + /* 8-bit (color mapped) image */ + pixel * pixrow; + + ppm_writeppminit(stdout, cols, rows, (pixval) maxval, 0); + pixrow = ppm_allocrow( cols ); + + for (row = 0; row < rows; ++row) { + for (col = 0; col < cols; ++col) + pixrow[col] = colormap[fgetc(ifd)]; + if (cols %2) + fgetc(ifd); /* padding */ + ppm_writeppmrow(stdout, pixrow, cols, maxval, 0); + } + ppm_freerow(pixrow); + } else if (depth == 1) { + /* mono image */ + bit *bitrow; + unsigned int row; + + pbm_writepbminit(stdout, cols, rows, 0); + bitrow = pbm_allocrow(cols); + + for (row = 0; row < rows; ++row) { + unsigned char bits; + bits = 0x00; /* initial value */ + for (col = 0; col < cols; ++col) { + int const shift = col % 8; + if (shift == 0) + bits = (unsigned char) fgetc(ifd); + bitrow[col] = (bits & (unsigned char)(0x01 << (7 - shift))) ? + PBM_WHITE : PBM_BLACK; + } + if ((cols % 16) && (cols % 16) <= 8) + fgetc(ifd); /* 16 bit pad */ + pbm_writepbmrow(stdout, bitrow, cols, 0); + } + pbm_freerow(bitrow); + } + pm_close(ifd); + + return 0; +} diff --git a/converter/ppm/mitsu.h b/converter/ppm/mitsu.h new file mode 100644 index 00000000..8676a39d --- /dev/null +++ b/converter/ppm/mitsu.h @@ -0,0 +1,97 @@ +#ifndef MITSU_H_INCLUDED +#define MITSU_H_INCLUDED + +/* static char SCCSid[] = "@(#)mitsu.h\t\t1.3\t(SPZ)\t3/11/92\n"; */ +#define MAXLUTCOL 255 + +#define A4_MAXCOLS 1184 +#define A4_MAXROWS 1452 +#define A4S_MAXROWS 1754 +#define A_MAXCOLS 1216 +#define A_MAXROWS 1350 +#define AS_MAXROWS 1650 + +#define ONLINE cmd('\021') +#define CLRMEM cmd('\033'), cmd('Z') + +struct mediasize { + char size; + int maxcols, maxrows; +}; + +const struct mediasize MSize_User={' ',1184,1350}; +const struct mediasize MSize_A4 ={'0',1184,1452}; +const struct mediasize MSize_A ={'1',1216,1350}; +const struct mediasize MSize_A4S ={'2',1184,1754}; +const struct mediasize MSize_AS ={'3',1216,1650}; +#define MEDIASIZE(chr) cmd('\033'), cmd('#'), cmd('P'), cmd((chr).size) + +#define HENLARGE(enl) cmd('\033'), cmd('&'), cmd('P'), cmd(enl), cmd('\001') +#define VENLARGE(enl) cmd('\033'), cmd('&'), cmd('Q'), cmd(enl), cmd('\001') +#define NOENLARGE '\001' +#define ENLARGEx2 '\002' +#define ENLARGEx3 '\003' + +#define COLREVERSION(arg) cmd('\033'), cmd('&'), cmd('W'), cmd(arg) +#define DONTREVERTCOLOR '0' +#define REVERTCOLOR '2' + +#define NUMCOPY(num) cmd('\033'), cmd('#'), cmd('C'), cmd((num) & 0xff) + +#define HOFFINCH(off) cmd('\033'), cmd('&'), cmd('S'), cmd((off) & 0xff) +#define VOFFINCH(off) cmd('\033'), cmd('&'), cmd('T'), cmd((off) & 0xff) + +#define CENTERING(cen) cmd('\033'), cmd('&'), cmd('C'), cmd(cen) +#define DONTCENTER '0' +#define DOCENTER '1' + +#define TRANSFERFORMAT(fmt) cmd('\033'), cmd('&'), cmd('A'), cmd(fmt) +#define FRAMEORDER '0' +#define LINEORDER '1' +#define LOOKUPTABLE '3' + +#define COLORSYSTEM(cs) cmd('\033'), cmd('&'), cmd('I'), cmd(cs) +#define RGB '0' +#define YMC '1' + +#define SHARPNESS(spn) cmd('\033'), cmd('#'), cmd('E'), cmd(spn) +#define SP_USER ' ' +#define SP_NONE '0' +#define SP_LOW '1' +#define SP_MIDLOW '2' +#define SP_MIDHIGH '3' +#define SP_HIGH '4' + +#define COLORDES(col) cmd('\033'), cmd('C'), cmd(col) +#define RED '1' +#define GREEN '2' +#define BLUE '3' +#define YELLOW '1' +#define MAGENTA '2' +#define CYAN '3' + +#define HPIXELS(hpix) cmd('\033'), cmd('&'), cmd('H'),\ + cmd(((hpix) >> 8) & 0xff), cmd((hpix) & 0xff) +#define VPIXELS(vpix) cmd('\033'), cmd('&'), cmd('V'),\ + cmd(((vpix) >> 8) & 0xff), cmd((vpix) & 0xff) +#define HPIXELSOFF(hoff) cmd('\033'), cmd('&'), cmd('J'),\ + cmd(((hoff) >> 8) & 0xff), cmd((hoff) & 0xff) +#define VPIXELSOFF(voff) cmd('\033'), cmd('&'), cmd('K'),\ + cmd(((voff) >> 8) & 0xff), cmd((voff) & 0xff) + +#define GRAYSCALELVL(lvl) cmd('\033'), cmd('#'), cmd('L'), cmd(lvl) +#define BIT_6 '\006' +#define BIT_8 '\010' + +#define LOADLOOKUPTABLE cmd('\033'), cmd('&'), cmd('L') +#define DONELOOKUPTABLE cmd('\004') + +#define ROTATEIMG(rot) cmd('\033'), cmd('#'), cmd('R'), cmd(rot) +#define DONTROTATE '0' +#define DOROTATE '1' + +#define DATASTART cmd('\033'), cmd('O') +#define PRINTIT cmd('\014') +#define OFFLINE cmd('\023') + +#endif diff --git a/converter/ppm/mtvtoppm.c b/converter/ppm/mtvtoppm.c new file mode 100644 index 00000000..e8558632 --- /dev/null +++ b/converter/ppm/mtvtoppm.c @@ -0,0 +1,68 @@ +/* mtvtoppm.c - read an MTV ray-tracer output file and produce a portable pixmap +** +** Copyright (C) 1989 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "ppm.h" + +int +main( argc, argv ) + int argc; + char* argv[]; + { + FILE* ifp; + pixel* pixelrow; + register pixel* pP; + int rows, cols, row, col; + pixval maxval; +#define MAXLINE 500 + char line[MAXLINE]; + unsigned char buf[3]; + + + ppm_init( &argc, argv ); + + if ( argc > 2 ) + pm_usage( "[mtvfile]" ); + + if ( argc == 2 ) + ifp = pm_openr( argv[1] ); + else + ifp = stdin; + + /* Read in the MTV file. First the header. */ + if ( fgets( line, MAXLINE, ifp ) == NULL ) + pm_error( "unable to read MTV file header" ); + if ( sscanf( line, "%d%d", &cols, &rows ) != 2 ) + pm_error( "unable to parse MTV file header" ); + + if ( cols <= 0 || rows <= 0 ) + pm_error( "invalid size: %d %d", cols, rows ); + maxval = 255; + + ppm_writeppminit( stdout, cols, rows, maxval, 0 ); + pixelrow = ppm_allocrow( cols ); + + for ( row = 0; row < rows; row++ ) + { + for ( col = 0, pP = pixelrow; col < cols; col++, pP++ ) + { + if ( fread( buf, sizeof(buf), 1, ifp ) != 1 ) + pm_error( "EOF / read error" ); + PPM_ASSIGN( *pP, buf[0], buf[1], buf[2] ); + } + ppm_writeppmrow( stdout, pixelrow, cols, maxval, 0 ); + } + + pm_close( ifp ); + pm_close( stdout ); + + exit( 0 ); + } diff --git a/converter/ppm/neotoppm.c b/converter/ppm/neotoppm.c new file mode 100644 index 00000000..d8cf2918 --- /dev/null +++ b/converter/ppm/neotoppm.c @@ -0,0 +1,104 @@ +/* neotoppm.c - read a Neochrome NEO file and produce a portable pixmap +** +** Copyright (C) 2001 by Teemu Hukkanen <tjhukkan@iki.fi> +** Based on pi1toppm by Steve Belczyk (seb3@gte.com) and Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "ppm.h" + +#define ROWS 200 +#define COLS 320 +#define MAXVAL 7 + +static short screen[ROWS*COLS/4]; /* simulates the Atari's video RAM */ + +int +main( int argc, char * argv[] ) { + FILE* ifp; + pixel pal[16]; /* Degas palette */ + pixel* pixelrow; + int row; + + ppm_init( &argc, argv ); + + /* Check args. */ + if ( argc > 2 ) + pm_usage( "[neofile]" ); + + if ( argc == 2 ) + ifp = pm_openr( argv[1] ); + else + ifp = stdin; + + /* Check header */ + { + /* Flag */ + short j; + pm_readbigshort (ifp, &j); + if ( j != 0 ) + pm_error( "not a NEO file" ); + } + { + /* Check resolution word */ + short j; + pm_readbigshort (ifp, &j); + if ( j != 0 ) + pm_error( "not a NEO file" ); + } + { + int i; + /* Read the palette. */ + for ( i = 0; i < 16; ++i ) { + short j; + + pm_readbigshort (ifp, &j); + PPM_ASSIGN( pal[i], + ( j & 0x700 ) >> 8, + ( j & 0x070 ) >> 4, + ( j & 0x007 ) ); + } + } + + /* Skip rest of header */ + fseek(ifp, 128, SEEK_SET); + + { + /* Read the screen data */ + int i; + for ( i = 0; i < ROWS*COLS/4; ++i ) + pm_readbigshort( ifp, &screen[i] ); + } + pm_close( ifp ); + + /* Ok, get set for writing PPM. */ + ppm_writeppminit( stdout, COLS, ROWS, MAXVAL, 0 ); + pixelrow = ppm_allocrow( COLS ); + + /* Now do the conversion. */ + for ( row = 0; row < ROWS; ++row ) { + int col; + for ( col = 0; col < COLS; ++col ) { + int c, ind, b, plane; + + ind = 80 * row + ( ( col >> 4 ) << 2 ); + b = 0x8000 >> ( col & 0xf ); + c = 0; + for ( plane = 0; plane < 4; ++plane ) + if ( b & screen[ind+plane] ) + c |= (1 << plane); + pixelrow[col] = pal[c]; + } + ppm_writeppmrow( stdout, pixelrow, COLS, MAXVAL, 0 ); + } + + pm_close( stdout ); + + exit( 0 ); +} diff --git a/converter/ppm/pc1toppm.c b/converter/ppm/pc1toppm.c new file mode 100644 index 00000000..5ba247e9 --- /dev/null +++ b/converter/ppm/pc1toppm.c @@ -0,0 +1,233 @@ +/* pc1toppm.c - read a Degas Elite PC1 file and produce a PPM + + Copyright (C) 1991 by Steve Belczyk (seb3@gte.com) and Jef Poskanzer. + Copyright (C) 2004 by Roine Gustafsson (roine@users.sourceforge.net) + + Converted to modern Netpbm code style by Bryan Henderson April 2004. + That work is contributed to the public domain by Bryan. + + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose and without fee is hereby granted, provided + that the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. This software is provided "as is" without express or + implied warranty. + + Algorithm for PC1 compression from "gimp-degas" GIMP plugin + by Markus F.X.J. Oberhumer + +*/ + +#include "ppm.h" + +/* The code has not been tested for other resolutions than 320x200 */ +static unsigned int const rows = 200; +static unsigned int const cols = 320; +static unsigned int const planes = 4; /* 4 bits for 16 colors */ +static pixval const maxval = 7; + +unsigned int const colsPerBlock = 16; + +#define BLOCKS (cols/colsPerBlock) +#define ROWBYTES (cols * planes / 8) +#define BLOCKBYTES (colsPerBlock / 8) +#define PLANEBYTES (ROWBYTES / planes) +#define ROWSHORTS (cols * planes / 16) +#define BLOCKSHORTS (colsPerBlock * planes / 16) +#define PLANESHORTS (ROWSHORTS / planes) + + + +static void +readPalette(FILE * const ifP, + pixel (* const palP)[]) { + + /* Read the palette. */ + unsigned int i; + for (i = 0; i < 16; ++i) { + unsigned short j; + pm_readbigshortu(ifP, &j); + PPM_ASSIGN((*palP)[i], + (j & 0x700) >> 8, + (j & 0x070) >> 4, + (j & 0x007) >> 0); + } +} + + + +static void +processStretch(unsigned int const countbyte, + FILE * const ifP, + unsigned int * const colP, + unsigned char ** const lineposP) { + + if (countbyte <= 127) { + /* countbyte+1 literal bytes follows */ + + unsigned int const count = countbyte + 1; + unsigned int i; + + if (*colP + count > ROWBYTES) + pm_error("Error in PC1 file. " + "A literal stretch extends beyond the end of a row"); + + for (i = 0; i < count; ++i) { + *(*lineposP)++ = getc(ifP); + ++(*colP); + } + } else { + /* next byte repeated 257-countbyte times */ + + unsigned char const duplicated_color = getc(ifP); + unsigned int const count = 257 - countbyte; + unsigned int i; + + if (*colP + count > ROWBYTES) + pm_error("Error in PC1 file. " + "A run extends beyond the end of a row."); + for (i = 0; i < count; ++i) { + *(*lineposP)++ = duplicated_color; + ++(*colP); + } + } +} + + + +static void +readPc1(FILE * const ifP, + pixel (*palP)[], + unsigned char (*bufferP)[]) { + + unsigned short j; + unsigned int row; + unsigned char* bufferCursor; + + /* Check resolution word */ + pm_readbigshortu(ifP, &j); + if (j != 0x8000) + pm_error("This is not a PC1 file. " + "The first two bytes are 0x%04X instead of 0x8000", j); + + readPalette(ifP, palP); + + /* Read the screen data */ + bufferCursor = &(*bufferP)[0]; + for (row = 0; row < rows; ++row) { + unsigned int col; + + for (col = 0; col < ROWBYTES;) { + unsigned int const countbyte = getc(ifP); + + processStretch(countbyte, ifP, &col, &bufferCursor); + } + } +} + + + +static void +reInterleave(unsigned char const buffer[], + unsigned short (* const screenP)[]) { + + /* The buffer is in one plane for each line, to optimize packing */ + /* Re-interleave to match the Atari screen layout */ + + unsigned short * const screen = *screenP; + unsigned int row; + + for (row = 0; row < rows; ++row) { + unsigned int block; + for (block = 0; block < BLOCKS; ++block) { + unsigned int plane; + for (plane = 0; plane < planes; ++plane) { + unsigned int const blockIndex = + row*ROWBYTES + plane*PLANEBYTES + block*BLOCKBYTES; + + screen[row*ROWSHORTS + block*BLOCKSHORTS + plane] = + (buffer[blockIndex+0] << 8) + (buffer[blockIndex+1]); + } + } + } +} + + + +static void +writePpm(FILE * const ofP, + unsigned short const screen[], + pixel const palette[]) { + + pixel* pixelrow; + unsigned int row; + + ppm_writeppminit(ofP, cols, rows, maxval, 0); + pixelrow = ppm_allocrow(cols); + + for (row = 0; row < rows; ++row) { + /* Each row is arranged into blocks of 16 columns. Each block + is represented by 4 shorts (16 bit integers). The + first is for Plane 0, the second for Plane 1, etc. Each short + contains the bits for that plane for each of the 16 columns, + arranged from most signficant bit to least in increasing column + number. + */ + unsigned int col0ScreenIndex = cols/16*planes * row; + + unsigned int col; + for (col = 0; col < cols; ++col) { + unsigned int const ind = col0ScreenIndex + col/16 * planes; + unsigned int const b = 0x8000 >> (col % 16); + + unsigned int plane; + unsigned int colorIndex; + + colorIndex = 0; + for (plane = 0; plane < planes; ++plane) + if (b & screen[ind+plane]) + colorIndex |= (1 << plane); + pixelrow[col] = palette[colorIndex]; + } + ppm_writeppmrow(ofP, pixelrow, cols, maxval, 0); + } + ppm_freerow(pixelrow); +} + + + +int +main(int argc, char ** argv) { + + const char * inputFilename; + FILE* ifP; + pixel palette[16]; /* Degas palette */ + static unsigned short screen[32000/2]; + /* simulates the Atari's video RAM */ + static unsigned char buffer[32000]; + /* simulates the Atari's video RAM */ + + ppm_init(&argc, argv); + + if (argc-1 == 0) + inputFilename = "-"; + else if (argc-1 == 1) + inputFilename = argv[1]; + else + pm_error("There is at most one argument to this program: " + "the input file name. You specified %d", argc-1); + + ifP = pm_openr(inputFilename); + + readPc1(ifP, &palette, &buffer); + + pm_close(ifP); + + reInterleave(buffer, &screen); + + writePpm(stdout, screen, palette); + + pm_close(stdout); + + return 0; +} diff --git a/converter/ppm/pcxstd.ppm b/converter/ppm/pcxstd.ppm new file mode 100644 index 00000000..513602e7 --- /dev/null +++ b/converter/ppm/pcxstd.ppm @@ -0,0 +1,19 @@ +P3 +16 1 +255 + 0 0 0 + 0 0 170 + 0 170 0 + 0 170 170 +170 0 0 +170 0 170 +170 170 0 +170 170 170 + 85 85 85 + 85 85 255 + 85 255 85 + 85 255 255 +255 85 85 +255 85 255 +255 255 85 +255 255 255 diff --git a/converter/ppm/pcxtoppm.c b/converter/ppm/pcxtoppm.c new file mode 100644 index 00000000..9f403538 --- /dev/null +++ b/converter/ppm/pcxtoppm.c @@ -0,0 +1,710 @@ +/* + * pcxtoppm.c - Converts from a PC Paintbrush PCX file to a PPM file. + * + * Copyright (c) 1990 by Michael Davidson + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + * + * Modifications by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de) + * 20/Apr/94: + * - checks if 16-color-palette is completely black -> use standard palette + * - "-stdpalette" option to enforce this + * - row-by-row operation (PPM output) + * 11/Dec/94: + * - capability for 24bit and 32bit (24bit + 8bit intensity) images + * - row-by-row operation (PCX input, for 16-color and truecolor images) + * - some code restructuring + * 15/Feb/95: + * - bugfix for 16 color-images: few bytes allocated for rawrow in some cases + * - added sanity checks for cols<->BytesPerLine + * 17/Jul/95: + * - moved check of 16-color-palette into pcx_16col_to_ppm(), + * now checks if it contains only a single color + */ +#include "mallocvar.h" +#include "shhopt.h" +#include "ppm.h" + +#define PCX_MAGIC 0x0a /* PCX magic number */ +#define PCX_HDR_SIZE 128 /* size of PCX header */ +#define PCX_256_COLORS 0x0c /* magic number for 256 colors */ + +#define PCX_MAXVAL (pixval)255 + +/* standard palette */ +static unsigned char const StdRed[] = { 0, 255, 0, 0, 170, 170, 170, 170, 85, 85, 85, 85, 255, 255, 255, 255 }; +static unsigned char const StdGreen[] = { 0, 255, 170, 170, 0, 0, 170, 170, 85, 85, 255, 255, 85, 85, 255, 255 }; +static unsigned char const StdBlue[] = { 0, 255, 0, 170, 0, 170, 0, 170, 85, 255, 85, 255, 85, 255, 85, 255 }; + +static pixel stdPalette[16]; + +static void +generateStdPalette(void) { + + int i; + for (i = 0; i < 16; i++) + PPM_ASSIGN(stdPalette[i], StdRed[i], StdGreen[i], StdBlue[i]); +} + + + +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char *inputFilespec; /* '-' if stdin */ + unsigned int verbose; + unsigned int stdpalette; +}; + + + +static void +parseCommandLine ( int argc, char ** argv, + struct cmdlineInfo *cmdlineP ) { +/*---------------------------------------------------------------------------- + parse program command line described in Unix standard form by argc + and argv. Return the information in the options as *cmdlineP. + + If command line is internally inconsistent (invalid options, etc.), + issue error message to stderr and abort program. + + Note that the strings we return are stored in the storage that + was passed to us as the argv array. We also trash *argv. +-----------------------------------------------------------------------------*/ + optEntry *option_def = malloc( 100*sizeof( optEntry ) ); + /* Instructions to optParseOptions3 on how to parse our options. + */ + optStruct3 opt; + + unsigned int option_def_index; + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "stdpalette", OPT_FLAG, NULL, + &cmdlineP->stdpalette, 0 ); + OPTENT3(0, "verbose", OPT_FLAG, NULL, + &cmdlineP->verbose, 0 ); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ + + optParseOptions3( &argc, argv, opt, sizeof(opt), 0 ); + /* Uses and sets argc, argv, and some of *cmdline_p and others. */ + + if (argc-1 < 1) + cmdlineP->inputFilespec = "-"; + else if (argc-1 == 1) + cmdlineP->inputFilespec = argv[1]; + else + pm_error("Program takes at most one argument " + "(input file specification). You specified %d", + argc-1); +} + + + +struct pcxHeader { + int Version; + /* Xmin, Ymin, Xmax, and Ymax are positions in some field (in units of + pixels) of the edges of the image. They may be negative. You can + derive the image width and height from these. + */ + short Xmin; + short Ymin; + short Xmax; + short Ymax; + short Encoding; + short Planes; + short BitsPerPixel; + short BytesPerLine; + /* Number of decompressed bytes each plane of each row of the image + takes. Due to padding (this is always an even number), there may + be garbage on the right end that isn't part of the image. + */ + short PaletteInfo; + short HorizontalResolution; + short VerticalResolution; + pixel cmap16[16]; +}; + + + +static void +readInt(FILE * const ifP, short * const retvalP) { +/*---------------------------------------------------------------------------- + Read a 2-byte little-endian word from the file. +-----------------------------------------------------------------------------*/ + int rc; + + rc = pm_readlittleshort(ifP, retvalP); + + if (rc != 0) + pm_error("EOF/error reading integer from input file."); +} + + + +static int +GetByte(FILE * const fp) { + + int c; + + if ((c = fgetc(fp)) == EOF) + pm_error("unexpected end of file" ); + + return c; +} + + + +static void +readPcxHeader(FILE * const ifP, + struct pcxHeader * const pcxHeaderP) { +/*---------------------------------------------------------------------------- + Read the PCX header +-----------------------------------------------------------------------------*/ + if (GetByte(ifP) != PCX_MAGIC) + pm_error("bad magic number - not a PCX file"); + + pcxHeaderP->Version = GetByte(ifP); /* get version # */ + + pcxHeaderP->Encoding = GetByte(ifP); + if (pcxHeaderP->Encoding != 1) /* check for PCX run length encoding */ + pm_error("unknown encoding scheme: %d", pcxHeaderP->Encoding); + + pcxHeaderP->BitsPerPixel= GetByte(ifP); + readInt(ifP, &pcxHeaderP->Xmin); + readInt(ifP, &pcxHeaderP->Ymin); + readInt(ifP, &pcxHeaderP->Xmax); + readInt(ifP, &pcxHeaderP->Ymax); + + if (pcxHeaderP->Xmax < pcxHeaderP->Xmin) + pm_error("Invalid PCX input: minimum X value (%d) is greater than " + "maximum X value (%d).", + pcxHeaderP->Xmin, pcxHeaderP->Xmax); + if (pcxHeaderP->Ymax < pcxHeaderP->Ymin) + pm_error("Invalid PCX input: minimum Y value (%d) is greater than " + "maximum Y value (%d).", + pcxHeaderP->Ymin, pcxHeaderP->Ymax); + + readInt(ifP, &pcxHeaderP->HorizontalResolution); + readInt(ifP, &pcxHeaderP->VerticalResolution); + + { + int i; + /* + * get the 16-color color map + */ + for (i = 0; i < 16; i++) { + unsigned int const r = GetByte(ifP); + unsigned int const g = GetByte(ifP); + unsigned int const b = GetByte(ifP); + PPM_ASSIGN(pcxHeaderP->cmap16[i], r, g, b); + } + } + + GetByte(ifP); /* skip reserved byte */ + pcxHeaderP->Planes = GetByte(ifP); /* # of color planes */ + readInt(ifP, &pcxHeaderP->BytesPerLine); + readInt(ifP, &pcxHeaderP->PaletteInfo); + + /* Read past a bunch of reserved space in the header. We have read + 70 bytes of the header so far. We would just seek here, except that + we want to work with unseekable (e.g. pipe input). + */ + { + unsigned int pos; + + for (pos = 70; pos < PCX_HDR_SIZE; ++pos) + GetByte(ifP); + } +} + + + +static void +reportPcxHeader(struct pcxHeader const pcxHeader) { + + pm_message("Version: %d", pcxHeader.Version); + pm_message("BitsPerPixel: %d", pcxHeader.BitsPerPixel); + pm_message("Xmin: %d Ymin: %d Xmax: %d Ymax: %d", + pcxHeader.Xmin, pcxHeader.Ymin, pcxHeader.Xmax, pcxHeader.Ymax); + pm_message("Planes: %d BytesPerLine: %d PaletteInfo: %d", + pcxHeader.Planes, pcxHeader.BytesPerLine, + pcxHeader.PaletteInfo); + pm_message("Color map in image: (index: r/g/b)"); + if (pcxHeader.BitsPerPixel < 8) { + unsigned int colorIndex; + for (colorIndex = 0; colorIndex < 16; ++colorIndex) { + pixel const p = pcxHeader.cmap16[colorIndex]; + pm_message(" %u: %u/%u/%u", colorIndex, + PPM_GETR(p), PPM_GETG(p), PPM_GETB(p)); + } + } +} + + + +static bool +allBlackPalette(pixel cmap16[]) { + + unsigned int colorIndex; + bool allBlack; + + allBlack = TRUE; /* initial assumption */ + for (colorIndex = 0; colorIndex < 16; ++colorIndex) { + pixel const p = cmap16[colorIndex]; + + if (PPM_GETR(p) != 0 || + PPM_GETG(p) != 0 || + PPM_GETB(p) != 0) + + allBlack = FALSE; + } + return allBlack; +} + + + +/* + * read a single row encoded row, handles encoding across rows + */ +static void +GetPCXRow(FILE * const ifP, unsigned char * const pcxrow, + int const bytesperline) { +/*---------------------------------------------------------------------------- + Read one row from the PCX raster. + + The PCX raster is run length encoded as follows: If the upper two + bits of a byte are 11, the lower 6 bits are a repetition count for the + raster byte that follows. If the upper two bits are not 11, the byte + _is_ a raster byte, with repetition count 1. + + A run can't span rows, but it can span planes within a row. That's + why 'repetitionsLeft' and 'c' are static variables in this + subroutine. +-----------------------------------------------------------------------------*/ + static int repetitionsLeft = 0; + static int c; + int bytesGenerated; + + bytesGenerated = 0; + while( bytesGenerated < bytesperline ) { + if(repetitionsLeft > 0) { + pcxrow[bytesGenerated++] = c; + --repetitionsLeft; + } else { + c = GetByte(ifP); + if ((c & 0xc0) != 0xc0) + /* This is a 1-shot byte, not a repetition count */ + pcxrow[bytesGenerated++] = c; + else { + /* This is a repetition count for the following byte */ + repetitionsLeft = c & 0x3f; + c = GetByte(ifP); + } + } + } +} + + + +/* + * convert packed pixel format into 1 pixel per byte + */ +static void +pcx_unpack_pixels(pixels, bitplanes, bytesperline, planes, bitsperpixel) + unsigned char *pixels; + unsigned char *bitplanes; + int bytesperline; + int planes; + int bitsperpixel; +{ + register int bits; + + if (planes != 1) + pm_error("can't handle packed pixels with more than 1 plane" ); +#if 0 + if (bitsperpixel == 8) + { + while (--bytesperline >= 0) + *pixels++ = *bitplanes++; + } + else +#endif + if (bitsperpixel == 4) + { + while (--bytesperline >= 0) + { + bits = *bitplanes++; + *pixels++ = (bits >> 4) & 0x0f; + *pixels++ = (bits ) & 0x0f; + } + } + else if (bitsperpixel == 2) + { + while (--bytesperline >= 0) + { + bits = *bitplanes++; + *pixels++ = (bits >> 6) & 0x03; + *pixels++ = (bits >> 4) & 0x03; + *pixels++ = (bits >> 2) & 0x03; + *pixels++ = (bits ) & 0x03; + } + } + else if (bitsperpixel == 1) + { + while (--bytesperline >= 0) + { + bits = *bitplanes++; + *pixels++ = ((bits & 0x80) != 0); + *pixels++ = ((bits & 0x40) != 0); + *pixels++ = ((bits & 0x20) != 0); + *pixels++ = ((bits & 0x10) != 0); + *pixels++ = ((bits & 0x08) != 0); + *pixels++ = ((bits & 0x04) != 0); + *pixels++ = ((bits & 0x02) != 0); + *pixels++ = ((bits & 0x01) != 0); + } + } + else + pm_error("pcx_unpack_pixels - can't handle %d bits per pixel", + bitsperpixel); +} + + + +/* + * convert multi-plane format into 1 pixel per byte + */ +static void +pcx_planes_to_pixels(pixels, bitplanes, bytesperline, planes, bitsperpixel) + unsigned char *pixels; + unsigned char *bitplanes; + int bytesperline; + int planes; + int bitsperpixel; +{ + int i, j; + int npixels; + unsigned char *p; + + if (planes > 4) + pm_error("can't handle more than 4 planes" ); + if (bitsperpixel != 1) + pm_error("can't handle more than 1 bit per pixel" ); + + /* + * clear the pixel buffer + */ + npixels = (bytesperline * 8) / bitsperpixel; + p = pixels; + while (--npixels >= 0) + *p++ = 0; + + /* + * do the format conversion + */ + for (i = 0; i < planes; i++) + { + int pixbit, bits, mask; + + p = pixels; + pixbit = (1 << i); + for (j = 0; j < bytesperline; j++) + { + bits = *bitplanes++; + for (mask = 0x80; mask != 0; mask >>= 1, p++) + if (bits & mask) + *p |= pixbit; + } + } +} + + + +static void +pcx_16col_to_ppm(FILE * const ifP, + unsigned int const headerCols, + unsigned int const rows, + unsigned int const BytesPerLine, + unsigned int const BitsPerPixel, + unsigned int const Planes, + pixel * const cmap) { + + unsigned int cols; + int row, col, rawcols, colors; + unsigned char * pcxrow; + unsigned char * rawrow; + pixel * ppmrow; + bool paletteOk; + + paletteOk = FALSE; + + /* check if palette is ok */ + colors = (1 << BitsPerPixel) * (1 << Planes); + for (col = 0; col < colors - 1; ++col) { + if (!PPM_EQUAL(cmap[col], cmap[col+1])) { + paletteOk = TRUE; + break; + } + } + if (!paletteOk) { + unsigned int col; + pm_message("warning - useless header palette, " + "using builtin standard palette"); + for (col = 0; col < colors; ++col) + PPM_ASSIGN(cmap[col], StdRed[col], StdGreen[col], StdBlue[col]); + } + + /* BytesPerLine should be >= BitsPerPixel * cols / 8 */ + rawcols = BytesPerLine * 8 / BitsPerPixel; + if (headerCols > rawcols) { + pm_message("warning - BytesPerLine = %d, " + "truncating image to %d pixels", + BytesPerLine, rawcols); + cols = rawcols; + } else + cols = headerCols; + + MALLOCARRAY(pcxrow, Planes * BytesPerLine); + if (pcxrow == NULL) + pm_error("Out of memory"); + MALLOCARRAY(rawrow, rawcols); + if (rawrow == NULL) + pm_error("Out of memory"); + + ppmrow = ppm_allocrow(cols); + + for (row = 0; row < rows; ++row) { + unsigned int col; + + GetPCXRow(ifP, pcxrow, Planes * BytesPerLine); + + if (Planes == 1) + pcx_unpack_pixels(rawrow, pcxrow, BytesPerLine, + Planes, BitsPerPixel); + else + pcx_planes_to_pixels(rawrow, pcxrow, BytesPerLine, + Planes, BitsPerPixel); + + for (col = 0; col < cols; ++col) + ppmrow[col] = cmap[rawrow[col]]; + ppm_writeppmrow(stdout, ppmrow, cols, PCX_MAXVAL, 0); + } + ppm_freerow(ppmrow); + free(rawrow); + free(pcxrow); +} + + + +static void +pcx_256col_to_ppm(FILE * const ifP, + unsigned int const headerCols, + unsigned int const rows, + unsigned int const BytesPerLine) { + + unsigned int cols; + pixel colormap[256]; + pixel * ppmrow; + unsigned char ** image; + unsigned char colormapSignature; + unsigned int row; + + if (headerCols > BytesPerLine) { + pm_message("warning - BytesPerLine = %u, " + "truncating image to %u pixels", + BytesPerLine, BytesPerLine); + cols = BytesPerLine; + } else + cols = headerCols; + + image = (unsigned char **)pm_allocarray(BytesPerLine, rows, + sizeof(unsigned char)); + for (row = 0; row < rows; ++row) + GetPCXRow(ifP, image[row], BytesPerLine); + + /* + * 256 color images have their color map at the end of the file + * preceeded by a magic byte + */ + colormapSignature = GetByte(ifP); + if (colormapSignature != PCX_256_COLORS) + pm_error("bad color map signature. In a 1-plane PCX image " + "such as this, we expect a magic number of %u in the byte " + "following the raster, to introduce the color map. " + "Instead, this image has %u.", + PCX_256_COLORS, colormapSignature); + else { + unsigned int colorIndex; + + for (colorIndex = 0; colorIndex < 256; ++colorIndex) { + pixval const r = GetByte(ifP); + pixval const g = GetByte(ifP); + pixval const b = GetByte(ifP); + PPM_ASSIGN(colormap[colorIndex], r, g, b); + } + } + + ppmrow = ppm_allocrow(cols); + for (row = 0; row < rows; ++row) { + unsigned int col; + for (col = 0; col < cols; ++col) + ppmrow[col] = colormap[image[row][col]]; + ppm_writeppmrow(stdout, ppmrow, cols, PCX_MAXVAL, 0); + } + + ppm_freerow(ppmrow); + pm_freearray((void *)image, rows); +} + + + +static void +pcx_truecol_to_ppm(FILE * const ifP, + unsigned int const headerCols, + unsigned int const rows, + unsigned int const BytesPerLine, + unsigned int const Planes) { + + unsigned int cols; + unsigned char *redrow, *greenrow, *bluerow, *intensityrow; + pixel * ppmrow; + unsigned int row; + + if (headerCols > BytesPerLine) { + pm_message("warning - BytesPerLine = %u, " + "truncating image to %u pixels", + BytesPerLine, BytesPerLine); + cols = BytesPerLine; + } else + cols = headerCols; + + MALLOCARRAY(redrow, BytesPerLine); + MALLOCARRAY(greenrow, BytesPerLine); + MALLOCARRAY(bluerow, BytesPerLine); + + if (redrow == NULL || greenrow == NULL || bluerow == NULL) + pm_error("out of memory"); + + if (Planes == 4) { + MALLOCARRAY(intensityrow, BytesPerLine); + if (intensityrow == NULL) + pm_error("out of memory"); + } else + intensityrow = NULL; + + ppmrow = ppm_allocrow(cols); + for (row = 0; row < rows; ++row) { + unsigned int col; + GetPCXRow(ifP, redrow, BytesPerLine); + GetPCXRow(ifP, greenrow, BytesPerLine); + GetPCXRow(ifP, bluerow, BytesPerLine); + if( intensityrow ) + GetPCXRow(ifP, intensityrow, BytesPerLine); + for (col = 0; col < cols; ++col) { + unsigned int const r = redrow[col]; + unsigned int const g = greenrow[col]; + unsigned int const b = bluerow[col]; + unsigned int const i = intensityrow ? intensityrow[col] : 256; + + PPM_ASSIGN(ppmrow[col], + r * i / 256, g * i / 256, b * i / 256); + } + ppm_writeppmrow(stdout, ppmrow, cols, PCX_MAXVAL, 0); + } + + ppm_freerow(ppmrow); + if (intensityrow) + free(intensityrow); + free(bluerow); + free(greenrow); + free(redrow); +} + + + +int +main(int argc, char *argv[]) { + + FILE * ifP; + struct cmdlineInfo cmdline; + struct pcxHeader pcxHeader; + unsigned int Width, Height; + pixel * cmap16; + + ppm_init(&argc, argv); + + generateStdPalette(); + + parseCommandLine(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.inputFilespec); + + readPcxHeader(ifP, &pcxHeader); + + if (cmdline.verbose) + reportPcxHeader(pcxHeader); + + Width = (pcxHeader.Xmax - pcxHeader.Xmin) + 1; + Height = (pcxHeader.Ymax - pcxHeader.Ymin) + 1; + + if (cmdline.stdpalette || allBlackPalette(pcxHeader.cmap16)) + cmap16 = stdPalette; + else + cmap16 = pcxHeader.cmap16; + + ppm_writeppminit(stdout, Width, Height, PCX_MAXVAL, 0); + switch (pcxHeader.BitsPerPixel) { + case 1: + if(pcxHeader.Planes >= 1 && pcxHeader.Planes <= 4) + pcx_16col_to_ppm(ifP, Width, Height, pcxHeader.BytesPerLine, + pcxHeader.BitsPerPixel, pcxHeader.Planes, cmap16); + else + goto fail; + break; + case 2: + case 4: + if (pcxHeader.Planes == 1) + pcx_16col_to_ppm(ifP, Width, Height, pcxHeader.BytesPerLine, + pcxHeader.BitsPerPixel, pcxHeader.Planes, cmap16); + else + goto fail; + break; + case 8: + switch(pcxHeader.Planes) { + case 1: + pcx_256col_to_ppm(ifP, Width, Height, pcxHeader.BytesPerLine); + break; + case 3: + case 4: + pcx_truecol_to_ppm(ifP, Width, Height, + pcxHeader.BytesPerLine, pcxHeader.Planes); + break; + default: + goto fail; + } + break; + default: + fail: + pm_error("can't handle %d bits per pixel image with %d planes", + pcxHeader.BitsPerPixel, pcxHeader.Planes); + } + pm_close(ifP); + + return 0; +} + + diff --git a/converter/ppm/pi1toppm.c b/converter/ppm/pi1toppm.c new file mode 100644 index 00000000..69b99863 --- /dev/null +++ b/converter/ppm/pi1toppm.c @@ -0,0 +1,92 @@ +/* pi1toppm.c - read a Degas PI1 file and produce a portable pixmap +** +** Copyright (C) 1991 by Steve Belczyk (seb3@gte.com) and Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "ppm.h" + +#define ROWS 200 +#define COLS 320 +#define MAXVAL 7 + +static short screen[ROWS*COLS/4]; /* simulates the Atari's video RAM */ + +int +main( argc, argv ) + int argc; + char* argv[]; + { + FILE* ifp; + pixel pal[16]; /* Degas palette */ + short i; + short j; + pixel* pixelrow; + register pixel* pP; + int row, col; + + + ppm_init( &argc, argv ); + + /* Check args. */ + if ( argc > 2 ) + pm_usage( "[pi1file]" ); + + if ( argc == 2 ) + ifp = pm_openr( argv[1] ); + else + ifp = stdin; + + /* Check resolution word */ + (void) pm_readbigshort (ifp, &j); + if ( j != 0 ) + pm_error( "not a PI1 file" ); + + /* Read the palette. */ + for ( i = 0; i < 16; ++i ) + { + (void) pm_readbigshort (ifp, &j); + PPM_ASSIGN( pal[i], + ( j & 0x700 ) >> 8, + ( j & 0x070 ) >> 4, + ( j & 0x007 ) ); + } + + /* Read the screen data */ + for ( i = 0; i < ROWS*COLS/4; ++i ) + (void) pm_readbigshort( ifp, &screen[i] ); + + pm_close( ifp ); + + /* Ok, get set for writing PPM. */ + ppm_writeppminit( stdout, COLS, ROWS, (pixval) MAXVAL, 0 ); + pixelrow = ppm_allocrow( COLS ); + + /* Now do the conversion. */ + for ( row = 0; row < ROWS; ++row ) + { + for ( col = 0, pP = pixelrow; col < COLS; ++col, ++pP ) + { + register int c, ind, b, plane; + + ind = 80 * row + ( ( col >> 4 ) << 2 ); + b = 0x8000 >> ( col & 0xf ); + c = 0; + for ( plane = 0; plane < 4; ++plane ) + if ( b & screen[ind+plane] ) + c |= (1 << plane); + *pP = pal[c]; + } + ppm_writeppmrow( stdout, pixelrow, COLS, (pixval) MAXVAL, 0 ); + } + + pm_close( stdout ); + + exit( 0 ); + } diff --git a/converter/ppm/picttoppm.c b/converter/ppm/picttoppm.c new file mode 100644 index 00000000..cfc5760e --- /dev/null +++ b/converter/ppm/picttoppm.c @@ -0,0 +1,3788 @@ +/* + * picttoppm.c -- convert a MacIntosh PICT file to PPM format. + * + * Copyright 1989,1992,1993 George Phillips + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. This software is provided "as is" without express or + * implied warranty. + * + * George Phillips <phillips@cs.ubc.ca> + * Department of Computer Science + * University of British Columbia + * + * + * 2003-02: Handling for DirectBitsRgn opcode (0x9b) added by + * kabe@sra-tohoku.co.jp. + * + * 2004-03-27: Several bugs fixed by Steve Summit, scs@eskimo.com. + * + */ + +#define _XOPEN_SOURCE + +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <assert.h> + +#include "pm_c_util.h" +#include "ppm.h" +#include "pbmfont.h" +#include "mallocvar.h" +#include "nstring.h" + + +/* + * Typical byte, 2 byte and 4 byte integers. + */ +typedef unsigned char byte; +typedef char signed_byte; +typedef unsigned short word; +typedef unsigned long longword; + + +/* + * Data structures for QuickDraw (and hence PICT) stuff. + */ + +struct Rect { + word top; + word left; + word bottom; + word right; +}; + +struct pixMap { + struct Rect Bounds; + word version; + word packType; + longword packSize; + longword hRes; + longword vRes; + word pixelType; + word pixelSize; + word cmpCount; + word cmpSize; + longword planeBytes; + longword pmTable; + longword pmReserved; +}; + +struct RGBColor { + word red; + word grn; + word blu; +}; + +struct Point { + word x; + word y; +}; + +struct Pattern { + byte pix[64]; +}; + +struct rgbPlanes { + word * red; + word * grn; + word * blu; +}; + +typedef void (*transfer_func) (struct RGBColor* src, struct RGBColor* dst); + +static const char* stage; +static struct Rect picFrame; +static word* red; +static word* green; +static word* blue; +static word rowlen; +static word collen; +static longword planelen; +static int verbose; +static int fullres; +static int recognize_comment; + +static struct RGBColor black = { 0, 0, 0 }; +static struct RGBColor white = { 0xffff, 0xffff, 0xffff }; + +/* various bits of drawing state */ +static struct RGBColor foreground = { 0, 0, 0 }; +static struct RGBColor background = { 0xffff, 0xffff, 0xffff }; +static struct RGBColor op_color; +static struct Pattern bkpat; +static struct Pattern fillpat; +static struct Rect clip_rect; +static struct Rect cur_rect; +static struct Point current; +static struct Pattern pen_pat; +static word pen_width; +static word pen_height; +static word pen_mode; +static transfer_func pen_trf; +static word text_font; +static byte text_face; +static word text_mode; +static transfer_func text_trf; +static word text_size; +static struct font* tfont; + +/* state for magic printer comments */ +static int ps_text; +static byte ps_just; +static byte ps_flip; +static word ps_rotation; +static byte ps_linespace; +static int ps_cent_x; +static int ps_cent_y; +static int ps_cent_set; + +struct opdef { + const char* name; + int len; + void (*impl) (int); + const char* description; +}; + +struct raster { +/*---------------------------------------------------------------------------- + An image raster. May be either truecolor or paletted. + + This is an array of pixels in row-major order, with 'rowSize' + bytes per row, 'rowCount' high. + + Within a row, pixels go left to right. The rows go top to bottom. + + Each pixel is either a palette index or an RGB triple, depending on + the format of the associated PICT. + + Each pixel is one byte if the associated PICT has 8 or fewer bits + per pixel. If the associated PICT has 16 or 32 bits per pixel, an + element herein is 2 or 4 bytes, respectively. + + For 16 bits per pixel, the two bytes for each pixel encode RGB values + as described in decode16(). + + For 32 bits per pixel, each row is divided into 4 planes. Red, + green, blue, and something else, in that order. The format of a + plane is one byte per pixel, left to right. +-----------------------------------------------------------------------------*/ + unsigned char * bytes; /* malloc'ed */ + unsigned int rowSize; + unsigned int rowCount; +}; + + +static void +allocateRaster(struct raster * const rasterP, + unsigned int const width, + unsigned int const height, + unsigned int const bitsPerPixel) { + + if (width > UINT_MAX/4) + pm_error("Width %u pixels too large for arithmetic", width); + + rasterP->rowCount = height; + + switch (bitsPerPixel) { + case 32: + /* TODO: I'm still trying to figure out this format. + + My theory today: + The row data is in plane order (a row consists of red + plane, then, green, then blue, then some 4th plane). + + The old hack code said 3 bytes per pixel here, and could get + away with it because it never got to decoding the 4th plane. + + But the new clean code needs to tell it like it is and allocate + 4 bytes per pixel. If we say 3 bytes per pixel here, we get an + "invalid PICT" error because the image actually contains 4 + bytes per pixel and as we decompress it, we run out of place + to put the data. + + We have yet to see if we can properly interpret the data. + */ + + rasterP->rowSize = width * 4; + break; + case 16: + rasterP->rowSize = width * 2; + break; + case 8: + case 4: + case 2: + case 1: + rasterP->rowSize = width * 1; + break; + default: + pm_error("INTERNAL ERROR: impossible bitsPerPixel value in " + "unpackbits(): %u", bitsPerPixel); + } + if (UINT_MAX / rasterP->rowSize < rasterP->rowCount) + pm_error("Arithmetic overflow computing size of %u x %u pixel " + "array.", rasterP->rowSize, rasterP->rowCount); + + MALLOCARRAY(rasterP->bytes, rasterP->rowSize * rasterP->rowCount); + if (rasterP->bytes == NULL) + pm_error("unable to get memory for %u x %u pixel packbits rectangle", + width, height); +} + + +static void +freeRaster(struct raster const raster) { + + free(raster.bytes); +} + + +struct blit_info { + struct Rect srcRect; + struct Rect srcBounds; + struct raster srcplane; + int pixSize; + struct Rect dstRect; + struct RGBColor * color_map; + int mode; + struct blit_info * next; +}; + +static struct blit_info* blit_list = 0; +static struct blit_info** last_bl = &blit_list; + +#define WORD_LEN (-1) + +/* + * a table of the first 194(?) opcodes. The table is too empty. + * + * Probably could use an entry specifying if the opcode is valid in version + * 1, etc. + */ + +/* for reserved opcodes of known length */ +#define res(length) \ +{ "reserved", (length), NULL, "reserved for Apple use" } + +/* for reserved opcodes of length determined by a function */ +#define resf(skipfunction) \ +{ "reserved", NA, (skipfunction), "reserved for Apple use" } + +/* seems like RGB colors are 6 bytes, but Apple says they're variable */ +/* I'll use 6 for now as I don't care that much. */ +#define RGB_LEN (6) + + +static FILE* ifp; +static int align = 0; + + + +static byte +read_byte(void) { + int c; + + if ((c = fgetc(ifp)) == EOF) + pm_error("EOF / read error while %s", stage); + + ++align; + return c & 255; +} + + + +static word +read_word(void) { + byte b; + + b = read_byte(); + + return (b << 8) | read_byte(); +} + + + +static void read_point(struct Point * const p) { + p->y = read_word(); + p->x = read_word(); +} + + + +static longword +read_long(void) { + word i; + + i = read_word(); + return (i << 16) | read_word(); +} + + + +static signed_byte +read_signed_byte(void) { + return (signed_byte)read_byte(); +} + + + +static void +read_short_point(struct Point * const p) { + p->x = read_signed_byte(); + p->y = read_signed_byte(); +} + + + +static void +skip(int const byteCount) { + static byte buf[1024]; + int n; + + align += byteCount; + + for (n = byteCount; n > 0; n -= 1024) + if (fread(buf, n > 1024 ? 1024 : n, 1, ifp) != 1) + pm_error("EOF / read error while %s", stage); +} + + + +struct const_name { + int value; + const char * name; +}; + +struct const_name const transfer_name[] = { + { 0, "srcCopy" }, + { 1, "srcOr" }, + { 2, "srcXor" }, + { 3, "srcBic" }, + { 4, "notSrcCopy" }, + { 5, "notSrcOr" }, + { 6, "notSrcXor" }, + { 7, "notSrcBic" }, + { 32, "blend" }, + { 33, "addPin" }, + { 34, "addOver" }, + { 35, "subPin" }, + { 36, "transparent" }, + { 37, "adMax" }, + { 38, "subOver" }, + { 39, "adMin" }, + { -1, 0 } +}; + +struct const_name font_name[] = { + { 0, "systemFont" }, + { 1, "applFont" }, + { 2, "newYork" }, + { 3, "geneva" }, + { 4, "monaco" }, + { 5, "venice" }, + { 6, "london" }, + { 7, "athens" }, + { 8, "sanFran" }, + { 9, "toronto" }, + { 11, "cairo" }, + { 12, "losAngeles" }, + { 20, "times" }, + { 21, "helvetica" }, + { 22, "courier" }, + { 23, "symbol" }, + { 24, "taliesin" }, + { -1, 0 } +}; + +struct const_name ps_just_name[] = { + { 0, "no" }, + { 1, "left" }, + { 2, "center" }, + { 3, "right" }, + { 4, "full" }, + { -1, 0 } +}; + +struct const_name ps_flip_name[] = { + { 0, "no" }, + { 1, "horizontal" }, + { 2, "vertical" }, + { -1, 0 } +}; + + + +static const char* +const_name(const struct const_name * const table, + unsigned int const ct) { + + static char numbuf[32]; + + unsigned int i; + + for (i = 0; table[i].name; ++i) + if (table[i].value == ct) + return table[i].name; + + sprintf(numbuf, "? (%u)", ct); + return numbuf; +} + + + +static void +picComment(word const type, + int const length) { + + unsigned int remainingLength; + + switch (type) { + case 150: + if (verbose) pm_message("TextBegin"); + if (length >= 6) { + ps_just = read_byte(); + ps_flip = read_byte(); + ps_rotation = read_word(); + ps_linespace = read_byte(); + remainingLength = length - 5; + if (recognize_comment) + ps_text = 1; + ps_cent_set = 0; + if (verbose) { + pm_message("%s justification, %s flip, %d degree rotation, " + "%d/2 linespacing", + const_name(ps_just_name, ps_just), + const_name(ps_flip_name, ps_flip), + ps_rotation, ps_linespace); + } + } else + remainingLength = length; + break; + case 151: + if (verbose) pm_message("TextEnd"); + ps_text = 0; + remainingLength = length; + break; + case 152: + if (verbose) pm_message("StringBegin"); + remainingLength = length; + break; + case 153: + if (verbose) pm_message("StringEnd"); + remainingLength = length; + break; + case 154: + if (verbose) pm_message("TextCenter"); + if (length < 8) + remainingLength = length; + else { + ps_cent_y = read_word(); + if (ps_cent_y > 32767) + ps_cent_y -= 65536; + skip(2); /* ignore fractional part */ + ps_cent_x = read_word(); + if (ps_cent_x > 32767) + ps_cent_x -= 65536; + skip(2); /* ignore fractional part */ + remainingLength = length - 8; + if (verbose) + pm_message("offset %d %d", ps_cent_x, ps_cent_y); + } + break; + case 155: + if (verbose) pm_message("LineLayoutOff"); + remainingLength = length; + break; + case 156: + if (verbose) pm_message("LineLayoutOn"); + remainingLength = length; + break; + case 160: + if (verbose) pm_message("PolyBegin"); + remainingLength = length; + break; + case 161: + if (verbose) pm_message("PolyEnd"); + remainingLength = length; + break; + case 163: + if (verbose) pm_message("PolyIgnore"); + remainingLength = length; + break; + case 164: + if (verbose) pm_message("PolySmooth"); + remainingLength = length; + break; + case 165: + if (verbose) pm_message("picPlyClo"); + remainingLength = length; + break; + case 180: + if (verbose) pm_message("DashedLine"); + remainingLength = length; + break; + case 181: + if (verbose) pm_message("DashedStop"); + remainingLength = length; + break; + case 182: + if (verbose) pm_message("SetLineWidth"); + remainingLength = length; + break; + case 190: + if (verbose) pm_message("PostScriptBegin"); + remainingLength = length; + break; + case 191: + if (verbose) pm_message("PostScriptEnd"); + remainingLength = length; + break; + case 192: + if (verbose) pm_message("PostScriptHandle"); + remainingLength = length; + break; + case 193: + if (verbose) pm_message("PostScriptFile"); + remainingLength = length; + break; + case 194: + if (verbose) pm_message("TextIsPostScript"); + remainingLength = length; + break; + case 195: + if (verbose) pm_message("ResourcePS"); + remainingLength = length; + break; + case 200: + if (verbose) pm_message("RotateBegin"); + remainingLength = length; + break; + case 201: + if (verbose) pm_message("RotateEnd"); + remainingLength = length; + break; + case 202: + if (verbose) pm_message("RotateCenter"); + remainingLength = length; + break; + case 210: + if (verbose) pm_message("FormsPrinting"); + remainingLength = length; + break; + case 211: + if (verbose) pm_message("EndFormsPrinting"); + remainingLength = length; + break; + default: + if (verbose) pm_message("%d", type); + remainingLength = length; + break; + } + if (remainingLength > 0) + skip(remainingLength); +} + + + +static void +ShortComment(int const version) { + picComment(read_word(), 0); +} + + + +static void +LongComment(int const version) { + word type; + + type = read_word(); + picComment(type, read_word()); +} + + + +static void +skip_poly_or_region(int const version) { + stage = "skipping polygon or region"; + skip(read_word() - 2); +} + + +#define NA (0) + +#define FNT_BOLD (1) +#define FNT_ITALIC (2) +#define FNT_ULINE (4) +#define FNT_OUTLINE (8) +#define FNT_SHADOW (16) +#define FNT_CONDENSE (32) +#define FNT_EXTEND (64) + +/* Some font searching routines */ + +struct fontinfo { + int font; + int size; + int style; + char* filename; + struct font* loaded; + struct fontinfo* next; +}; + +static struct fontinfo* fontlist = 0; +static struct fontinfo** fontlist_ins = &fontlist; + + + +static int +load_fontdir(const char * const dirfile) { +/*---------------------------------------------------------------------------- + Load the font directory from file named 'dirfile'. Add its contents + to the global list of fonts 'fontlist'. +-----------------------------------------------------------------------------*/ + FILE* fp; + int n, nfont; + char* arg[5], line[1024]; + struct fontinfo* fontinfo; + + if (!(fp = fopen(dirfile, "rb"))) + return -1; + + nfont = 0; + while (fgets(line, 1024, fp)) { + if ((n = mk_argvn(line, arg, 5)) == 0 || arg[0][0] == '#') + continue; + if (n != 4) + continue; + MALLOCVAR(fontinfo); + if (fontinfo == NULL) + pm_error("out of memory for font information"); + MALLOCARRAY(fontinfo->filename, strlen(arg[3] + 1)); + if (fontinfo->filename == NULL) + pm_error("out of memory for font information file name"); + + fontinfo->font = atoi(arg[0]); + fontinfo->size = atoi(arg[1]); + fontinfo->style = atoi(arg[2]); + strcpy(fontinfo->filename, arg[3]); + fontinfo->loaded = 0; + + fontinfo->next = 0; + *fontlist_ins = fontinfo; + fontlist_ins = &fontinfo->next; + nfont++; + } + + return nfont; +} + + + +static void +read_rect(struct Rect * const r) { + r->top = read_word(); + r->left = read_word(); + r->bottom = read_word(); + r->right = read_word(); +} + + + +static void +dumpRect(const char * const label, + struct Rect const rectangle) { + + pm_message("%s (%u,%u) (%u,%u)", + label, + rectangle.left, rectangle.top, + rectangle.right, rectangle.bottom); +} + + + +static int +rectwidth(const struct Rect * const r) { + return r->right - r->left; +} + + + +static int +rectheight(const struct Rect * const r) { + return r->bottom - r->top; +} + + + +static bool +rectsamesize(const struct Rect * const r1, + const struct Rect * const r2) { + return r1->right - r1->left == r2->right - r2->left && + r1->bottom - r1->top == r2->bottom - r2->top ; +} + + + +static void +rectinter(struct Rect const r1, + struct Rect const r2, + struct Rect * const intersectionP) { + + intersectionP->left = MAX(r1.left, r2.left); + intersectionP->top = MAX(r1.top, r2.top); + intersectionP->right = MIN(r1.right, r2.right); + intersectionP->bottom = MIN(r1.bottom, r2.bottom); +} + + + +static void +rectscale(struct Rect * const r, + double const xscale, + double const yscale) { + r->left *= xscale; + r->right *= xscale; + r->top *= yscale; + r->bottom *= yscale; +} + + + +static struct blit_info* +add_blit_list(void) { + + struct blit_info * bi; + + MALLOCVAR(bi); + if (bi == NULL) + pm_error("out of memory for blit list"); + + bi->next = 0; + *last_bl = bi; + last_bl = &bi->next; + + return bi; +} + + + +/* Various transfer functions for blits. + * + * Note src[Not]{Or,Xor,Copy} only work if the source pixmap was originally + * a bitmap. + * There's also a small bug that the foreground and background colors + * are not used in a srcCopy; this wouldn't be hard to fix. + * It IS a problem since the foreground and background colors CAN be changed. + */ + +static bool +rgbAllSame(const struct RGBColor * const colorP, + unsigned int const value) { + + return (colorP->red == value && + colorP->grn == value && + colorP->blu == value); +} + + +static bool +rgbIsWhite(const struct RGBColor * const colorP) { + + return rgbAllSame(colorP, 0xffff); +} + +static bool +rgbIsBlack(const struct RGBColor * const colorP) { + + return rgbAllSame(colorP, 0); +} + + +static void +srcCopy(struct RGBColor * const src, + struct RGBColor * const dst) { + + if (rgbIsBlack(src)) + *dst = foreground; + else + *dst = background; +} + + + +static void +srcOr(struct RGBColor * const src, + struct RGBColor * const dst) { + if (rgbIsBlack(src)) + *dst = foreground; +} + + + +static void +srcXor(struct RGBColor * const src, + struct RGBColor * const dst) { + dst->red ^= ~src->red; + dst->grn ^= ~src->grn; + dst->blu ^= ~src->blu; +} + + + +static void +srcBic(struct RGBColor * const src, + struct RGBColor * const dst) { + if (rgbIsBlack(src)) + *dst = background; +} + + + +static void +notSrcCopy(struct RGBColor * const src, + struct RGBColor * const dst) { + if (rgbIsWhite(src)) + *dst = foreground; + else if (rgbIsBlack(src)) + *dst = background; +} + + + +static void +notSrcOr(struct RGBColor * const src, + struct RGBColor * const dst) { + if (rgbIsWhite(src)) + *dst = foreground; +} + + + +static void +notSrcBic(struct RGBColor * const src, + struct RGBColor * const dst) { + if (rgbIsWhite(src)) + *dst = background; +} + + + +static void +notSrcXor(struct RGBColor * const src, + struct RGBColor * const dst) { + dst->red ^= src->red; + dst->grn ^= src->grn; + dst->blu ^= src->blu; +} + + + +static void +addOver(struct RGBColor * const src, + struct RGBColor * const dst) { + dst->red += src->red; + dst->grn += src->grn; + dst->blu += src->blu; +} + + + +static void +addPin(struct RGBColor * const src, + struct RGBColor * const dst) { + if ((long)dst->red + (long)src->red > (long)op_color.red) + dst->red = op_color.red; + else + dst->red = dst->red + src->red; + + if ((long)dst->grn + (long)src->grn > (long)op_color.grn) + dst->grn = op_color.grn; + else + dst->grn = dst->grn + src->grn; + + if ((long)dst->blu + (long)src->blu > (long)op_color.blu) + dst->blu = op_color.blu; + else + dst->blu = dst->blu + src->blu; +} + + + +static void +subOver(struct RGBColor * const src, + struct RGBColor * const dst) { + dst->red -= src->red; + dst->grn -= src->grn; + dst->blu -= src->blu; +} + + + +/* or maybe its src - dst; my copy of Inside Mac is unclear */ + + +static void +subPin(struct RGBColor * const src, + struct RGBColor * const dst) { + if ((long)dst->red - (long)src->red < (long)op_color.red) + dst->red = op_color.red; + else + dst->red = dst->red - src->red; + + if ((long)dst->grn - (long)src->grn < (long)op_color.grn) + dst->grn = op_color.grn; + else + dst->grn = dst->grn - src->grn; + + if ((long)dst->blu - (long)src->blu < (long)op_color.blu) + dst->blu = op_color.blu; + else + dst->blu = dst->blu - src->blu; +} + + + +static void +adMax(struct RGBColor * const src, + struct RGBColor * const dst) { + if (src->red > dst->red) dst->red = src->red; + if (src->grn > dst->grn) dst->grn = src->grn; + if (src->blu > dst->blu) dst->blu = src->blu; +} + + + +static void +adMin(struct RGBColor * const src, + struct RGBColor * const dst) { + if (src->red < dst->red) dst->red = src->red; + if (src->grn < dst->grn) dst->grn = src->grn; + if (src->blu < dst->blu) dst->blu = src->blu; +} + + + +static void +blend(struct RGBColor * const src, + struct RGBColor * const dst) { +#define blend_component(cmp) \ + ((long)src->cmp * (long)op_color.cmp) / 65536 + \ + ((long)dst->cmp * (long)(65536 - op_color.cmp) / 65536) + + dst->red = blend_component(red); + dst->grn = blend_component(grn); + dst->blu = blend_component(blu); +} + + + +static void +transparent(struct RGBColor * const src, + struct RGBColor * const dst) { + if (src->red != background.red || + src->grn != background.grn || + src->blu != background.blu) { + *dst = *src; + } +} + + + +static transfer_func +transfer(int const mode) { + switch (mode) { + case 0: return srcCopy; + case 1: return srcOr; + case 2: return srcXor; + case 3: return srcBic; + case 4: return notSrcCopy; + case 5: return notSrcOr; + case 6: return notSrcXor; + case 7: return notSrcBic; + case 32: return blend; + case 33: return addPin; + case 34: return addOver; + case 35: return subPin; + case 36: return transparent; + case 37: return adMax; + case 38: return subOver; + case 39: return adMin; + default: + if (mode != 0) + pm_message("no transfer function for code %s, using srcCopy", + const_name(transfer_name, mode)); + return srcCopy; + } +} + + + +static pixval +redepth(pixval const c, + pixval const oldMaxval) { + + return (c * PPM_MAXMAXVAL + oldMaxval / 2) / oldMaxval; +} + + + +static struct RGBColor +decode16(unsigned char * const sixteen) { +/*---------------------------------------------------------------------------- + Decode a 16 bit PICT encoding of RGB: + + Bit 0: nothing + Bits 1- 5: red + Bits 6-10: green + Bits 11-15: blue + + 'sixteen' is a two byte array. +-----------------------------------------------------------------------------*/ + struct RGBColor retval; + + retval.red = (sixteen[0] & 0x7c) >> 2; + retval.grn = (sixteen[0] & 0x03) << 3 | (sixteen[1] & 0xe0) >> 5; + retval.blu = (sixteen[1] & 0x1f) >> 0; + + return retval; +} + + + +static void +doDiffSize(struct Rect const clipsrc, + struct Rect const clipdst, + int const pixSize, + int const xsize, + int const ysize, + transfer_func const trf, + struct RGBColor * const color_map, + unsigned char * const src, + int const srcwid, + struct rgbPlanes const dst, + unsigned int const dstwid) { + + unsigned int const dstadd = dstwid - xsize; + + FILE * pnmscalePipeP; + const char * command; + FILE * scaled; + int cols, rows, format; + pixval maxval; + pixel * row; + pixel * rowp; + FILE * tempFileP; + const char * tempFilename; + word * reddst; + word * grndst; + word * bludst; + + reddst = dst.red; /* initial value */ + grndst = dst.grn; /* initial value */ + bludst = dst.blu; /* initial value */ + + pm_make_tmpfile(&tempFileP, &tempFilename); + + pm_close(tempFileP); + + asprintfN(&command, "pnmscale -xsize %d -ysize %d > %s", + rectwidth(&clipdst), rectheight(&clipdst), tempFilename); + + pm_message("running command '%s'", command); + + pnmscalePipeP = popen(command, "w"); + if (pnmscalePipeP == NULL) + pm_error("cannot execute command '%s' popen() errno = %s (%d)", + command, strerror(errno), errno); + + strfree(command); + + fprintf(pnmscalePipeP, "P6\n%d %d\n%d\n", + rectwidth(&clipsrc), rectheight(&clipsrc), PPM_MAXMAXVAL); + + switch (pixSize) { + case 8: { + unsigned int rowNumber; + for (rowNumber = 0; rowNumber < ysize; ++rowNumber) { + unsigned char * const row = &src[rowNumber * srcwid]; + unsigned int colNumber; + for (colNumber = 0; colNumber < xsize; ++colNumber) { + unsigned int const colorIndex = row[colNumber]; + struct RGBColor * const ct = &color_map[colorIndex]; + fputc(redepth(ct->red, 65535L), pnmscalePipeP); + fputc(redepth(ct->grn, 65535L), pnmscalePipeP); + fputc(redepth(ct->blu, 65535L), pnmscalePipeP); + } + } + } + break; + case 16: { + unsigned int rowNumber; + for (rowNumber = 0; rowNumber < ysize; ++rowNumber) { + unsigned char * const row = &src[rowNumber * srcwid]; + unsigned int colNumber; + for (colNumber = 0; colNumber < xsize; ++colNumber) { + struct RGBColor const color = decode16(&row[colNumber * 2]); + fputc(redepth(color.red, 32), pnmscalePipeP); + fputc(redepth(color.grn, 32), pnmscalePipeP); + fputc(redepth(color.blu, 32), pnmscalePipeP); + } + } + } + break; + case 32: { + unsigned int const planeSize = srcwid / 4; + unsigned int rowNumber; + + for (rowNumber = 0; rowNumber < ysize; ++rowNumber) { + unsigned char * const row = &src[rowNumber * srcwid]; + unsigned char * const redPlane = &row[planeSize * 0]; + unsigned char * const grnPlane = &row[planeSize * 1]; + unsigned char * const bluPlane = &row[planeSize * 2]; + + unsigned int colNumber; + for (colNumber = 0; colNumber < xsize; ++colNumber) { + fputc(redepth(redPlane[colNumber], 256), pnmscalePipeP); + fputc(redepth(grnPlane[colNumber], 256), pnmscalePipeP); + fputc(redepth(bluPlane[colNumber], 256), pnmscalePipeP); + } + } + } + break; + } + + if (pclose(pnmscalePipeP)) + pm_error("pnmscale failed. pclose() returned Errno %s (%d)", + strerror(errno), errno); + + ppm_readppminit(scaled = pm_openr(tempFilename), &cols, &rows, + &maxval, &format); + row = ppm_allocrow(cols); + /* couldn't hurt to assert cols, rows and maxval... */ + + if (trf == NULL) { + while (rows-- > 0) { + unsigned int i; + ppm_readppmrow(scaled, row, cols, maxval, format); + for (i = 0, rowp = row; i < cols; ++i, ++rowp) { + *reddst++ = PPM_GETR(*rowp) * 65536L / (maxval + 1); + *grndst++ = PPM_GETG(*rowp) * 65536L / (maxval + 1); + *bludst++ = PPM_GETB(*rowp) * 65536L / (maxval + 1); + } + reddst += dstadd; + grndst += dstadd; + bludst += dstadd; + } + } + else { + while (rows-- > 0) { + unsigned int i; + ppm_readppmrow(scaled, row, cols, maxval, format); + for (i = 0, rowp = row; i < cols; i++, rowp++) { + struct RGBColor dst_c, src_c; + dst_c.red = *reddst; + dst_c.grn = *grndst; + dst_c.blu = *bludst; + src_c.red = PPM_GETR(*rowp) * 65536L / (maxval + 1); + src_c.grn = PPM_GETG(*rowp) * 65536L / (maxval + 1); + src_c.blu = PPM_GETB(*rowp) * 65536L / (maxval + 1); + (*trf)(&src_c, &dst_c); + *reddst++ = dst_c.red; + *grndst++ = dst_c.grn; + *bludst++ = dst_c.blu; + } + reddst += dstadd; + grndst += dstadd; + bludst += dstadd; + } + } + + pm_close(scaled); + ppm_freerow(row); + strfree(tempFilename); + unlink(tempFilename); +} + + + +static void +getRgb(struct rgbPlanes const planes, + unsigned int const index, + struct RGBColor * const rgbP) { + + rgbP->red = planes.red[index]; + rgbP->grn = planes.grn[index]; + rgbP->blu = planes.blu[index]; +} + + + +static void +putRgb(struct RGBColor const rgb, + unsigned int const index, + struct rgbPlanes const planes) { + + planes.red[index] = rgb.red; + planes.grn[index] = rgb.grn; + planes.blu[index] = rgb.blu; +} + + + +static void +doSameSize(transfer_func trf, + int const pixSize, + int const xsize, + int const ysize, + unsigned char * const src, + unsigned int const srcwid, + struct RGBColor * const color_map, + struct rgbPlanes const dst, + unsigned int const dstwid) { +/*---------------------------------------------------------------------------- + Generalized (but slow) blit. + + Transfer pixels from 'src' to 'dst', applying the transfer function + 'trf'. + + 'src' has the same format as the 'bytes' member of struct raster. + 'srcwid' is the size in bytes of each row, like raster.rowSize. + + We use only the first 'ysize' rows and only the first 'xsize' + pixels of each row. + + We really should clean this up so that we can take pixels out of + the middle of a row and rows out of the middle of the raster. As + it stands, Caller achieves the same result by passing as 'src' + a pointer into the middle of a raster -- the upper left corner of + the rectangle he wants. But that is messy and nonobvious. + + Each plane of 'dst' is one word per pixel and contains actual + colors, never a palette index. It is an array in row-major order + with 'dstwid' words per row. +-----------------------------------------------------------------------------*/ + switch (pixSize) { + case 8: { + unsigned int rowNumber; + + for (rowNumber = 0; rowNumber < ysize; ++rowNumber) { + unsigned char * const srcrow = &src[rowNumber * srcwid]; + unsigned int const dstRowCurs = rowNumber * dstwid; + + unsigned int colNumber; + for (colNumber = 0; colNumber < xsize; ++colNumber) { + unsigned int const dstCursor = dstRowCurs + colNumber; + unsigned int const colorIndex = srcrow[colNumber]; + struct RGBColor dstColor; + getRgb(dst, dstCursor, &dstColor); + (*trf)(&color_map[colorIndex], &dstColor); + putRgb(dstColor, dstCursor, dst); + } + } + } break; + case 16: { + unsigned int rowNumber; + + for (rowNumber = 0; rowNumber < ysize; ++rowNumber) { + unsigned char * const row = &src[rowNumber * srcwid]; + unsigned int const dstRowCurs = rowNumber * dstwid; + + unsigned int colNumber; + for (colNumber = 0; colNumber < xsize; ++colNumber) { + unsigned int const dstCursor = dstRowCurs + colNumber; + struct RGBColor const srcColor = decode16(&row[colNumber*2]); + struct RGBColor dstColor; + struct RGBColor scaledSrcColor; + scaledSrcColor.red = srcColor.red << 11; + scaledSrcColor.grn = srcColor.grn << 11; + scaledSrcColor.blu = srcColor.blu << 11; + getRgb(dst, dstCursor, &dstColor); + (*trf)(&scaledSrcColor, &dstColor); + putRgb(dstColor, dstCursor, dst); + } + } + } break; + case 32: { + unsigned int const planeSize = srcwid / 4; + unsigned int rowNumber; + + for (rowNumber = 0; rowNumber < ysize; ++rowNumber) { + unsigned char * const row = &src[rowNumber * srcwid]; + unsigned char * const redPlane = &row[planeSize * 0]; + unsigned char * const grnPlane = &row[planeSize * 1]; + unsigned char * const bluPlane = &row[planeSize * 2]; + unsigned int const dstRowCurs = rowNumber * dstwid; + + unsigned int colNumber; + + for (colNumber = 0; colNumber < xsize; ++colNumber) { + unsigned int const dstCursor = dstRowCurs + colNumber; + struct RGBColor srcColor, dstColor; + getRgb(dst, dstCursor, &dstColor); + srcColor.red = redPlane[colNumber] << 8; + srcColor.grn = grnPlane[colNumber] << 8; + srcColor.blu = bluPlane[colNumber] << 8; + (*trf)(&srcColor, &dstColor); + putRgb(dstColor, dstCursor, dst); + } + } + } break; + default: + pm_error("Impossible value of pixSize: %u", pixSize); + } +} + + + +static void +blitIdempotent(unsigned int const pixSize, + unsigned int const xsize, + unsigned int const ysize, + unsigned char * const src, + unsigned int const srcwid, + struct RGBColor * const colorMap, + struct rgbPlanes const dst, + unsigned int const dstwid) { +/*---------------------------------------------------------------------------- + This is the same as doSameSize(), except optimized for the case that + the transfer function is idempotent (i.e. it's just a straight copy). + The original author's comments suggest that this optimization isn't + all that important -- that he just wrote this first and instead of + expanding it to handle arbitrary transfer functions, added functions + for that. +-----------------------------------------------------------------------------*/ + switch (pixSize) { + case 8: { + unsigned int rowNumber; + + for (rowNumber = 0; rowNumber < ysize; ++rowNumber) { + unsigned char * const srcrow = &src[rowNumber * srcwid]; + unsigned int const dstRowCurs = rowNumber * dstwid; + unsigned int colNumber; + for (colNumber = 0; colNumber < xsize; ++colNumber) { + unsigned int const dstCursor = dstRowCurs + colNumber; + struct RGBColor * const ct = colorMap + srcrow[colNumber]; + dst.red[dstCursor] = ct->red; + dst.grn[dstCursor] = ct->grn; + dst.blu[dstCursor] = ct->blu; + } + } + } break; + case 16: { + unsigned int rowNumber; + + for (rowNumber = 0; rowNumber < ysize; ++rowNumber) { + unsigned char * const srcrow = &src[rowNumber * srcwid]; + unsigned int const dstRowCurs = rowNumber * dstwid; + unsigned int colNumber; + for (colNumber = 0; colNumber < xsize; ++colNumber) { + unsigned int const dstCursor = dstRowCurs + colNumber; + struct RGBColor const srcColor = + decode16(&srcrow[colNumber * 2]); + dst.red[dstCursor] = srcColor.red << 11; + dst.grn[dstCursor] = srcColor.grn << 11; + dst.blu[dstCursor] = srcColor.blu << 11; + } + } + } break; + case 32: { + unsigned int const planeSize = srcwid / 4; + unsigned int rowNumber; + + for (rowNumber = 0; rowNumber < ysize; ++rowNumber) { + unsigned char * const srcrow = &src[rowNumber * srcwid]; + unsigned char * const redPlane = &srcrow[planeSize * 0]; + unsigned char * const grnPlane = &srcrow[planeSize * 1]; + unsigned char * const bluPlane = &srcrow[planeSize * 2]; + unsigned int const dstRowCurs = rowNumber * dstwid; + + unsigned int colNumber; + for (colNumber = 0; colNumber < xsize; ++colNumber) { + unsigned int const dstCursor = dstRowCurs + colNumber; + dst.red[dstCursor] = redPlane[colNumber] << 8; + dst.grn[dstCursor] = grnPlane[colNumber] << 8; + dst.blu[dstCursor] = bluPlane[colNumber] << 8; + } + } + } break; + default: + pm_error("INTERNAL ERROR: invalid bits per pixel (%u) in " + "blitIdempotent()", pixSize); + } +} + + + +static void +generalBlit(struct Rect const srcRect, + struct Rect const srcBounds, + struct raster const srcplane, + int const pixSize, + struct Rect const dstRect, + struct Rect const dstBounds, + int const dstwid, + struct RGBColor * const color_map, + int const mode, + struct Rect const clipsrc, + struct Rect const clipdst) { + + unsigned char * src; + struct rgbPlanes dst; + int dstoff; + int xsize; + int ysize; + int srcadd; + transfer_func trf; + + if (verbose) { + dumpRect("copying from:", clipsrc); + dumpRect("to: ", clipdst); + pm_message("a %u x %u area to a %u x %u area", + rectwidth(&clipsrc), rectheight(&clipsrc), + rectwidth(&clipdst), rectheight(&clipdst)); + } + + { + unsigned int const pkpixsize = pixSize == 16 ? 2 : 1; + unsigned int const srcRowNumber = clipsrc.top - srcBounds.top; + unsigned int const srcRowOffset = + (clipsrc.left - srcBounds.left) * pkpixsize; + assert(srcRowNumber < srcplane.rowCount); + assert(srcRowOffset < srcplane.rowSize); + src = srcplane.bytes + srcRowNumber * srcplane.rowSize + srcRowOffset; + xsize = clipsrc.right - clipsrc.left; + ysize = clipsrc.bottom - clipsrc.top; + srcadd = srcplane.rowSize - xsize * pkpixsize; + } + + dstoff = (clipdst.top - dstBounds.top) * dstwid + + (clipdst.left - dstBounds.left); + dst.red = red + dstoff; + dst.grn = green + dstoff; + dst.blu = blue + dstoff; + + /* get rid of Text mask mode bit, if (erroneously) set */ + if ((mode & ~64) == 0) + trf = NULL; /* optimized srcCopy */ + else + trf = transfer(mode & ~64); + + if (!rectsamesize(&clipsrc, &clipdst)) + doDiffSize(clipsrc, clipdst, pixSize, xsize, ysize, + trf, color_map, src, srcplane.rowSize, dst, dstwid); + else { + if (trf == NULL) + blitIdempotent(pixSize, xsize, ysize, src, srcplane.rowSize, + color_map, dst, dstwid); + else + doSameSize(trf, pixSize, xsize, ysize, src, srcplane.rowSize, + color_map, dst, dstwid); + } +} + + + +static int +blit(struct Rect const srcRect, + struct Rect const srcBounds, + struct raster const srcplane, + int const pixSize, + struct Rect const dstRect, + struct Rect const dstBounds, + int const dstwid, + struct RGBColor * const color_map, + int const mode) { + + /* I can't tell what the result value of this function is supposed to mean, + but I found several return statements that did not set it to anything, + and several calls that examine it. I'm guessing that "1" is the + appropriate thing to return in those cases, so I made it so. + -Bryan 00.03.02 + */ + + int retval; + + if (ps_text) + retval = 1; + else { + /* Almost got it. Clip source rect with source bounds. + clip dest rect with dest bounds. If source and + destination are not the same size, use Pnmscale + to get a nicely sized rectangle. + */ + struct Rect clipsrc; + struct Rect clipdst; + + rectinter(srcBounds, srcRect, &clipsrc); + rectinter(dstBounds, dstRect, &clipdst); + + if (fullres) { + struct blit_info * bi; + bi = add_blit_list(); + bi->srcRect = clipsrc; + bi->srcBounds = srcBounds; + bi->srcplane = srcplane; + bi->pixSize = pixSize; + bi->dstRect = clipdst; + bi->color_map = color_map; + bi->mode = mode; + + retval = 0; + } else { + generalBlit(srcRect, srcBounds, srcplane, pixSize, + dstRect, dstBounds, dstwid, color_map, mode, + clipsrc, clipdst); + + retval = 1; + } + } + return retval; +} + + + +/* allocation is same for version 1 or version 2. We are super-duper + * wasteful of memory for version 1 picts. Someday, we'll separate + * things and only allocate a byte per pixel for version 1 (or heck, + * even only a bit, but that would require even more extra work). + */ + +static void +allocPlanes(struct rgbPlanes * const planesP) { + + struct rgbPlanes planes; + + rowlen = picFrame.right - picFrame.left; + collen = picFrame.bottom - picFrame.top; + + clip_rect = picFrame; + + planelen = rowlen * collen; + MALLOCARRAY(planes.red, planelen); + MALLOCARRAY(planes.grn, planelen); + MALLOCARRAY(planes.blu, planelen); + if (planes.red == NULL || planes.grn == NULL || planes.blu == NULL) + pm_error("not enough memory to hold picture"); + + /* initialize background to white */ + memset(planes.red, 255, planelen * sizeof(word)); + memset(planes.grn, 255, planelen * sizeof(word)); + memset(planes.blu, 255, planelen * sizeof(word)); + + /* Until we wean this program off of global variables, we have to + set these: + */ + + red = planes.red; + green = planes.grn; + blue = planes.blu; +} + + + +static void +freePlanes(struct rgbPlanes const planes) { + + free(planes.red); + free(planes.grn); + free(planes.blu); +} + + + +static unsigned char +compact(word const input) { + + return (input >> 8) & 0xff; +} + + + +static void +do_blits(struct rgbPlanes * const planesP) { + + struct blit_info* bi; + int srcwidth, dstwidth, srcheight, dstheight; + double scale, scalelow, scalehigh; + double xscale = 1.0; + double yscale = 1.0; + double lowxscale, highxscale, lowyscale, highyscale; + int xscalecalc = 0, yscalecalc = 0; + + if (!blit_list) return; + + fullres = 0; + + for (bi = blit_list; bi; bi = bi->next) { + srcwidth = rectwidth(&bi->srcRect); + dstwidth = rectwidth(&bi->dstRect); + srcheight = rectheight(&bi->srcRect); + dstheight = rectheight(&bi->dstRect); + if (srcwidth > dstwidth) { + scalelow = (double)(srcwidth ) / (double)dstwidth; + scalehigh = (double)(srcwidth + 1.0) / (double)dstwidth; + switch (xscalecalc) { + case 0: + lowxscale = scalelow; + highxscale = scalehigh; + xscalecalc = 1; + break; + case 1: + if (scalelow < highxscale && scalehigh > lowxscale) { + if (scalelow > lowxscale) lowxscale = scalelow; + if (scalehigh < highxscale) highxscale = scalehigh; + } + else { + scale = (lowxscale + highxscale) / 2.0; + xscale = (double)srcwidth / (double)dstwidth; + if (scale > xscale) xscale = scale; + xscalecalc = 2; + } + break; + case 2: + scale = (double)srcwidth / (double)dstwidth; + if (scale > xscale) xscale = scale; + break; + } + } + + if (srcheight > dstheight) { + scalelow = (double)(srcheight ) / (double)dstheight; + scalehigh = (double)(srcheight + 1.0) / (double)dstheight; + switch (yscalecalc) { + case 0: + lowyscale = scalelow; + highyscale = scalehigh; + yscalecalc = 1; + break; + case 1: + if (scalelow < highyscale && scalehigh > lowyscale) { + if (scalelow > lowyscale) lowyscale = scalelow; + if (scalehigh < highyscale) highyscale = scalehigh; + } + else { + scale = (lowyscale + highyscale) / 2.0; + yscale = (double)srcheight / (double)dstheight; + if (scale > yscale) yscale = scale; + yscalecalc = 2; + } + break; + case 2: + scale = (double)srcheight / (double)dstheight; + if (scale > yscale) yscale = scale; + break; + } + } + } + + if (xscalecalc == 1) { + if (1.0 >= lowxscale && 1.0 <= highxscale) + xscale = 1.0; + else + xscale = lowxscale; + } + if (yscalecalc == 1) { + if (1.0 >= lowyscale && 1.0 <= lowyscale) + yscale = 1.0; + else + yscale = lowyscale; + } + + if (xscale != 1.0 || yscale != 1.0) { + for (bi = blit_list; bi; bi = bi->next) + rectscale(&bi->dstRect, xscale, yscale); + + pm_message("Scaling output by %f in X and %f in Y", + xscale, yscale); + rectscale(&picFrame, xscale, yscale); + } + + allocPlanes(planesP); + + for (bi = blit_list; bi; bi = bi->next) { + blit(bi->srcRect, bi->srcBounds, bi->srcplane, + bi->pixSize, + bi->dstRect, picFrame, rowlen, + bi->color_map, + bi->mode); + } +} + + + +static void +outputPpm(struct rgbPlanes const planes) { + + unsigned int width; + unsigned int height; + pixel * pixelrow; + unsigned int row; + unsigned int srcCursor; + + stage = "writing PPM"; + + assert(picFrame.right > picFrame.left); + assert(picFrame.bottom > picFrame.top); + + width = picFrame.right - picFrame.left; + height = picFrame.bottom - picFrame.top; + + ppm_writeppminit(stdout, width, height, PPM_MAXMAXVAL, 0); + pixelrow = ppm_allocrow(width); + srcCursor = 0; + for (row = 0; row < height; ++row) { + unsigned int col; + for (col = 0; col < width; ++col) { + PPM_ASSIGN(pixelrow[col], + compact(planes.red[srcCursor]), + compact(planes.grn[srcCursor]), + compact(planes.blu[srcCursor]) + ); + ++srcCursor; + } + ppm_writeppmrow(stdout, pixelrow, width, PPM_MAXMAXVAL, 0); + } + pm_close(stdout); +} + + + +/* + * All data in version 2 is 2-byte word aligned. Odd size data + * is padded with a null. + */ +static word +get_op(int const version) { + if ((align & 1) && version == 2) { + stage = "aligning for opcode"; + read_byte(); + } + + stage = "reading opcode"; + + if (version == 1) + return read_byte(); + else + return read_word(); +} + + + +static void +Clip(int const version) { + word len; + + len = read_word(); + + if (len == 0x000a) { /* null rgn */ + read_rect(&clip_rect); + /* XXX should clip this by picFrame */ + if (verbose) + dumpRect("clipping to", clip_rect); + } + else + skip(len - 2); +} + + + +static void +OpColor(int const version) { + op_color.red = read_word(); + op_color.grn = read_word(); + op_color.blu = read_word(); +} + + + +static void +read_pixmap(struct pixMap * const p) { + + stage = "getting pixMap header"; + + read_rect(&p->Bounds); + p->version = read_word(); + p->packType = read_word(); + p->packSize = read_long(); + p->hRes = read_long(); + p->vRes = read_long(); + p->pixelType = read_word(); + p->pixelSize = read_word(); + p->cmpCount = read_word(); + p->cmpSize = read_word(); + p->planeBytes = read_long(); + p->pmTable = read_long(); + p->pmReserved = read_long(); + + if (verbose) { + pm_message("pixelType: %d", p->pixelType); + pm_message("pixelSize: %d", p->pixelSize); + pm_message("cmpCount: %d", p->cmpCount); + pm_message("cmpSize: %d", p->cmpSize); + } + + if (p->pixelType != 0) + pm_error("sorry, I do only chunky format. " + "This image has pixel type %hu", p->pixelType); + if (p->cmpCount != 1) + pm_error("sorry, cmpCount != 1"); + if (p->pixelSize != p->cmpSize) + pm_error("oops, pixelSize != cmpSize"); +} + + + +static struct RGBColor* +read_color_table(void) { + longword ctSeed; + word ctFlags; + word ctSize; + word val; + int i; + struct RGBColor* color_table; + + stage = "getting color table info"; + + ctSeed = read_long(); + ctFlags = read_word(); + ctSize = read_word(); + + if (verbose) { + pm_message("ctSeed: %ld", ctSeed); + pm_message("ctFlags: %d", ctFlags); + pm_message("ctSize: %d", ctSize); + } + + stage = "reading color table"; + + MALLOCARRAY(color_table, ctSize + 1); + if (color_table == NULL) + pm_error("no memory for color table"); + + for (i = 0; i <= ctSize; i++) { + val = read_word(); + /* The indices in a device color table are bogus and usually == 0. + * so I assume we allocate up the list of colors in order. + */ + if (ctFlags & 0x8000) + val = i; + if (val > ctSize) + pm_error("pixel value greater than color table size"); + color_table[val].red = read_word(); + color_table[val].grn = read_word(); + color_table[val].blu = read_word(); + + if (verbose > 1) + pm_message("%d: [%d,%d,%d]", val, + color_table[val].red, + color_table[val].grn, + color_table[val].blu); + } + + return color_table; +} + + + +static void +readBytes(FILE * const ifP, + unsigned int const n, + unsigned char * const buf) { + + align += n; + + if (fread(buf, n, 1, ifP) != 1) + pm_error("EOF / read error while %s", stage); +} + + + +static void +copyFullBytes(unsigned char * const packed, + unsigned char * const expanded, + unsigned int const packedLen) { + + unsigned int i; + + for (i = 0; i < packedLen; ++i) + expanded[i] = packed[i]; +} + + + +static void +expand4Bits(unsigned char * const packed, + unsigned char * const expanded, + unsigned int const packedLen) { + + unsigned int i; + unsigned char * dst; + + dst = &expanded[0]; + + for (i = 0; i < packedLen; ++i) { + *dst++ = (packed[i] >> 4) & 0x0f; + *dst++ = (packed[i] >> 0) & 0x0f; + } +} + + + +static void +expand2Bits(unsigned char * const packed, + unsigned char * const expanded, + unsigned int const packedLen) { + + unsigned int i; + unsigned char * dst; + + dst = &expanded[0]; + + for (i = 0; i < packedLen; ++i) { + *dst++ = (packed[i] >> 6) & 0x03; + *dst++ = (packed[i] >> 4) & 0x03; + *dst++ = (packed[i] >> 2) & 0x03; + *dst++ = (packed[i] >> 0) & 0x03; + } +} + + + +static void +expand1Bit(unsigned char * const packed, + unsigned char * const expanded, + unsigned int const packedLen) { + + unsigned int i; + unsigned char * dst; + + dst = &expanded[0]; + + for (i = 0; i < packedLen; ++i) { + *dst++ = (packed[i] >> 7) & 0x01; + *dst++ = (packed[i] >> 6) & 0x01; + *dst++ = (packed[i] >> 5) & 0x01; + *dst++ = (packed[i] >> 4) & 0x01; + *dst++ = (packed[i] >> 3) & 0x01; + *dst++ = (packed[i] >> 2) & 0x01; + *dst++ = (packed[i] >> 1) & 0x01; + *dst++ = (packed[i] >> 0) & 0x01; + } +} + + + +static void +unpackBuf(unsigned char * const packed, + unsigned int const packedLen, + int const bitsPerPixel, + unsigned char ** const expandedP, + unsigned int * const expandedLenP) { +/*---------------------------------------------------------------------------- + Expand the bit string 'packed', which is 'packedLen' bytes long + into an array of bytes, with one byte per pixel. Each 'bitsPerPixel' + of 'packed' is a pixel. + + So e.g. if it's 4 bits per pixel and 'packed' is 0xabcdef01, we + return 0x0a0b0c0d0e0f0001 as *expandedP. + + As a special case, if there are multiple bytes per pixel, we just + return the exact same bit string. + + *expandedP is static storage. + + 'packedLen' must not be greater than 256. +-----------------------------------------------------------------------------*/ + static unsigned char expanded[256 * 8]; + unsigned char * src; + unsigned char * dst; + + assert(packedLen <= 256); + + src = &packed[0]; + dst = &expanded[0]; + + switch (bitsPerPixel) { + case 8: + case 16: + case 32: + assert(packedLen <= sizeof(expanded)); + copyFullBytes(packed, expanded, packedLen); + *expandedLenP = packedLen; + break; + case 4: + assert(packedLen * 2 <= sizeof(expanded)); + expand4Bits(packed, expanded, packedLen); + *expandedLenP = packedLen * 2; + break; + case 2: + assert(packedLen * 4 <= sizeof(expanded)); + expand2Bits(packed, expanded, packedLen); + *expandedLenP = packedLen * 4; + break; + case 1: + assert(packedLen * 8 <= sizeof(expanded)); + expand1Bit(packed, expanded, packedLen); + *expandedLenP = packedLen * 8; + break; + default: + pm_error("INTERNAL ERROR: bitsPerPixel = %u in unpackBuf", + bitsPerPixel); + } + *expandedP = expanded; +} + + + +static void +unpackUncompressedBits(FILE * const ifP, + struct raster const raster, + unsigned int const rowBytes, + unsigned int const bitsPerPixel) { +/*---------------------------------------------------------------------------- + Read the raster from the file into 'raster'. The data in the file is not + compressed (but may still be packed multiple pixels per byte). + + In PICT terminology, it appears that compression is called + "packing" and I don't know what packing is called. But we don't + use that confusing terminology in this program, except when talking + to the user. +-----------------------------------------------------------------------------*/ + unsigned int rowOfRect; + unsigned char * linebuf; + + MALLOCARRAY(linebuf, rowBytes + 100); + if (linebuf == NULL) + pm_error("can't allocate memory for line buffer"); + + for (rowOfRect = 0; rowOfRect < raster.rowCount; ++rowOfRect) { + unsigned char * bytePixels; + unsigned int expandedByteCount; + unsigned char * rasterRow; + unsigned int i; + + rasterRow = raster.bytes + rowOfRect * raster.rowSize; + + readBytes(ifP, rowBytes, linebuf); + + unpackBuf(linebuf, rowBytes, bitsPerPixel, + &bytePixels, &expandedByteCount); + + assert(expandedByteCount <= raster.rowSize); + + for (i = 0; i < expandedByteCount; ++i) + rasterRow[i] = bytePixels[i]; + } + free(linebuf); +} + + + +static void +expandRun(unsigned char * const block, + unsigned int const blockLimit, + unsigned int const bitsPerPixel, + unsigned char * const expanded, + unsigned int const expandedSize, + unsigned int * const blockLengthP, + unsigned int * const expandedByteCountP) { +/*---------------------------------------------------------------------------- + Expand a run (the data says, "repeat the next pixel N times"). + + Return the expanded run as expanded[], which has room for 'expandedSize' + elements. Return as *expandedByteCountP the number of elements actually + returned. +-----------------------------------------------------------------------------*/ + unsigned int const pkpixsize = bitsPerPixel == 16 ? 2 : 1; + + if (1 + pkpixsize > blockLimit) + pm_error("PICT run block runs off the end of its line. " + "Invalid PICT file."); + else { + unsigned int const runLength = (block[0] ^ 0xff) + 2; + + unsigned int i; + unsigned char * bytePixels; /* Points to static storage */ + unsigned int expandedByteCount; + unsigned int outputCursor; + + assert(block[0] & 0x80); /* It's a run */ + + if (verbose > 1) + pm_message("Block: run of %u pixels or plane samples", runLength); + + unpackBuf(&block[1], pkpixsize, bitsPerPixel, + &bytePixels, &expandedByteCount); + + if (expandedByteCount > expandedSize) + pm_error("Invalid PICT image. It contains a row with more pixels " + "than the width of the image"); + + outputCursor = 0; + for (i = 0; i < runLength; ++i) { + unsigned int j; + for (j = 0; j < expandedByteCount; ++j) + expanded[outputCursor++] = bytePixels[j]; + } + *blockLengthP = 1 + pkpixsize; + *expandedByteCountP = expandedByteCount * runLength; + } +} + + + +static void +copyPixelGroup(unsigned char * const block, + unsigned int const blockLimit, + unsigned int const bitsPerPixel, + unsigned char * const dest, + unsigned int const destSize, + unsigned int * const blockLengthP, + unsigned int * const rasterBytesGeneratedP) { +/*---------------------------------------------------------------------------- + Copy a group of pixels (the data says, "take the following N pixels"). +-----------------------------------------------------------------------------*/ + unsigned int const pkpixsize = bitsPerPixel == 16 ? 2 : 1; + unsigned int const groupLen = block[0] + 1; + unsigned int const blockLength = 1 + groupLen * pkpixsize; + + if (blockLength > blockLimit) + pm_error("PICT non-run block (length %u) " + "runs off the end of its line. " + "Invalid PICT file.", blockLength); + else { + unsigned int i; + unsigned char * bytePixels; /* Points to static storage */ + unsigned int bytePixelLen; + + assert(blockLimit >= 1); /* block[0] exists */ + assert((block[0] & 0x80) == 0); /* It's not a run */ + + if (verbose > 1) + pm_message("Block: %u individual pixels or plane samples", + groupLen); + + unpackBuf(&block[1], groupLen * pkpixsize, bitsPerPixel, + &bytePixels, &bytePixelLen); + + if (bytePixelLen > destSize) + pm_error("Invalid PICT image. It contains a row with more pixels " + "than the width of the image"); + + for (i = 0; i < bytePixelLen; ++i) + dest[i] = bytePixels[i]; + + *blockLengthP = blockLength; + *rasterBytesGeneratedP = bytePixelLen; + } +} + + + +static void +interpretOneRasterBlock(unsigned char * const block, + unsigned int const blockLimit, + unsigned int const bitsPerPixel, + unsigned char * const raster, + unsigned int const rasterSize, + unsigned int * const blockLengthP, + unsigned int * const rasterBytesGeneratedP) { +/*---------------------------------------------------------------------------- + Return the pixels described by the PICT block block[], assuming + the PICT format is 'bitsPerPixel' bits per pixel. + + Return them as raster[], which has 'rasterSize' elements of space. + Return as *rasterBytesGeneratedP the number of elements actually + returned. + + block[] self-describes its length, and we return that as + *blockLengthP. But there are at most 'blockLimit' bytes there, so + if block[] claims to be longer than that, some of the block is + missing (i.e. corrupt PICT). +-----------------------------------------------------------------------------*/ + if (block[0] & 0x80) + expandRun(block, blockLimit, bitsPerPixel, raster, rasterSize, + blockLengthP, rasterBytesGeneratedP); + else + copyPixelGroup(block, blockLimit, bitsPerPixel, raster, rasterSize, + blockLengthP, rasterBytesGeneratedP); + + assert(*rasterBytesGeneratedP <= rasterSize); +} + + + +static unsigned int const maxPixelBytesPerBlock = 1024; + + + +static void +unpackCompressedBits(FILE * const ifP, + struct raster const raster, + unsigned int const rowBytes, + unsigned int const bitsPerPixel) { +/*---------------------------------------------------------------------------- + Read the raster of on file *ifP and place it in 'raster'. + + The data in the file is compressed with run length encoding and + possibly packed multiple pixels per byte as well. + + In PICT terminology, it appears that compression is called + "packing" and I don't know what packing is called. But we don't + use that confusing terminology in this program, except when talking + to the user. + + *boundsP describes the rectangle. +-----------------------------------------------------------------------------*/ + unsigned int const llsize = rowBytes > 200 ? 2 : 1; + unsigned int rowOfRect; + unsigned char * linebuf; + unsigned int linebufSize; + + linebufSize = rowBytes; + MALLOCARRAY(linebuf, linebufSize); + if (linebuf == NULL) + pm_error("can't allocate memory for line buffer"); + + for (rowOfRect = 0; rowOfRect < raster.rowCount; ++rowOfRect) { + unsigned char * const rowRaster = + &raster.bytes[rowOfRect * raster.rowSize]; + unsigned int linelen; + unsigned int lineCursor; + unsigned int rasterCursor; + + if (llsize == 2) + linelen = read_word(); + else + linelen = read_byte(); + + if (verbose > 1) + pm_message("linelen: %u", linelen); + + if (linelen > linebufSize) { + linebufSize = linelen; + REALLOCARRAY(linebuf, linebufSize); + if (linebuf == NULL) + pm_error("can't allocate memory for line buffer"); + } + readBytes(ifP, linelen, linebuf); + + for (lineCursor = 0, rasterCursor = 0; lineCursor < linelen; ) { + unsigned int blockLength, rasterBytesGenerated; + + assert(lineCursor <= linelen); + + interpretOneRasterBlock( + &linebuf[lineCursor], linelen - lineCursor, + bitsPerPixel, + &rowRaster[rasterCursor], raster.rowSize - rasterCursor, + &blockLength, &rasterBytesGenerated); + + lineCursor += blockLength; + rasterCursor += rasterBytesGenerated; + assert(rasterCursor <= raster.rowSize); + } + if (verbose > 1) + pm_message("row %u: got %u", rowOfRect, rasterCursor); + } + free(linebuf); +} + + + +static void +unpackbits(FILE * const ifP, + struct Rect * const boundsP, + word const rowBytesArg, + int const bitsPerPixel, + struct raster * const rasterP) { + + unsigned int const rectHeight = boundsP->bottom - boundsP->top; + unsigned int const rectWidth = boundsP->right - boundsP->left; + + struct raster raster; + unsigned int rowBytes; + + stage = "unpacking packbits"; + + if (verbose) + pm_message("rowBytes = %u, bitsPerPixel = %d", + rowBytesArg, bitsPerPixel); + + allocateRaster(&raster, rectWidth, rectHeight, bitsPerPixel); + + rowBytes = rowBytesArg ? rowBytesArg : raster.rowSize; + + if (verbose) + pm_message("raster.rowSize: %u bytes; file row = %u bytes", + raster.rowSize, rowBytes); + + if (rowBytes < 8) + unpackUncompressedBits(ifP, raster, rowBytes, bitsPerPixel); + else + unpackCompressedBits(ifP, raster, rowBytes, bitsPerPixel); + + *rasterP = raster; +} + + + +static void +interpretRowBytesWord(word const rowBytesWord, + bool * const pixMapP, + unsigned int * const rowBytesP) { + + *pixMapP = !!(rowBytesWord & 0x8000); + *rowBytesP = rowBytesWord & 0x7fff; + + if (verbose) + pm_message("PCT says %s, %u bytes per row", + *pixMapP ? "pixmap" : "bitmap", *rowBytesP); +} + + + +/* this just skips over a version 2 pattern. Probably will return + * a pattern in the fabled complete version. + */ +static void +read_pattern(void) { + + word PatType; + + stage = "Reading a pattern"; + + PatType = read_word(); + + switch (PatType) { + case 2: + skip(8); /* old pattern data */ + skip(5); /* RGB for pattern */ + break; + case 1: { + word rowBytesWord; + bool pixMap; + unsigned int rowBytes; + struct pixMap p; + struct raster raster; + struct RGBColor * ct; + + skip(8); /* old pattern data */ + rowBytesWord = read_word(); + interpretRowBytesWord(rowBytesWord, &pixMap, &rowBytes); + read_pixmap(&p); + ct = read_color_table(); + unpackbits(ifp, &p.Bounds, rowBytes, p.pixelSize, &raster); + freeRaster(raster); + free(ct); + } break; + default: + pm_error("unknown pattern type in read_pattern"); + } +} + + + +/* these 3 do nothing but skip over their data! */ + +static void +BkPixPat(int const version) { + read_pattern(); +} + +static void +PnPixPat(int const version) { + read_pattern(); +} + +static void +FillPixPat(int const version) { + read_pattern(); +} + +static void +read_8x8_pattern(struct Pattern * const pat) { + unsigned char buf[8]; + unsigned char * exp; + unsigned int len; + unsigned int expandedLen; + unsigned int i; + + len = 8; /* initial value */ + readBytes(ifp, len, buf); + if (verbose) { + pm_message("pattern: %02x%02x%02x%02x", + buf[0], buf[1], buf[2], buf[3]); + pm_message("pattern: %02x%02x%02x%02x", + buf[4], buf[5], buf[6], buf[7]); + } + unpackBuf(buf, len, 1, &exp, &expandedLen); + for (i = 0; i < 64; ++i) + pat->pix[i] = exp[i]; +} + + + +static void +BkPat(int const version) { + read_8x8_pattern(&bkpat); +} + + + +static void +PnPat(int const version) { + read_8x8_pattern(&pen_pat); +} + + + +static void +FillPat(int const version) { + read_8x8_pattern(&fillpat); +} + + + +static void +PnSize(int const version) { + pen_height = read_word(); + pen_width = read_word(); + if (verbose) + pm_message("pen size %d x %d", pen_width, pen_height); +} + + + +static void +PnMode(int const version) { + + pen_mode = read_word(); + + if (pen_mode >= 8 && pen_mode < 15) + pen_mode -= 8; + if (verbose) + pm_message("pen transfer mode = %s", + const_name(transfer_name, pen_mode)); + + pen_trf = transfer(pen_mode); +} + + + +static void +read_rgb(struct RGBColor * const rgb) { + rgb->red = read_word(); + rgb->grn = read_word(); + rgb->blu = read_word(); +} + + + +static void +RGBFgCol(int const v) { + read_rgb(&foreground); + if (verbose) + pm_message("foreground now [%d,%d,%d]", + foreground.red, foreground.grn, foreground.blu); +} + + + +static void +RGBBkCol(int const v) { + read_rgb(&background); + if (verbose) + pm_message("background now [%d,%d,%d]", + background.red, background.grn, background.blu); +} + + + +#define PIXEL_INDEX(x,y) ((y) - picFrame.top) * rowlen + (x) - picFrame.left + +static void +draw_pixel(int const x, + int const y, + struct RGBColor * const clr, + transfer_func trf) { + + int i; + struct RGBColor dst; + + if (x < clip_rect.left || x >= clip_rect.right || + y < clip_rect.top || y >= clip_rect.bottom) + { + return; + } + + i = PIXEL_INDEX(x, y); + dst.red = red[i]; + dst.grn = green[i]; + dst.blu = blue[i]; + (*trf)(clr, &dst); + red[i] = dst.red; + green[i] = dst.grn; + blue[i] = dst.blu; +} + + + +static void +draw_pen_rect(struct Rect * const r) { + int const rowadd = rowlen - (r->right - r->left); + + int i; + int x, y; + struct RGBColor dst; + + i = PIXEL_INDEX(r->left, r->top); /* initial value */ + + for (y = r->top; y < r->bottom; y++) { + for (x = r->left; x < r->right; x++) { + dst.red = red[i]; + dst.grn = green[i]; + dst.blu = blue[i]; + if (pen_pat.pix[(x & 7) + (y & 7) * 8]) + (*pen_trf)(&black, &dst); + else + (*pen_trf)(&white, &dst); + red[i] = dst.red; + green[i] = dst.grn; + blue[i] = dst.blu; + + i++; + } + i += rowadd; + } +} + + + +static void +draw_pen(int const x, + int const y) { + struct Rect penrect; + + penrect.left = x; + penrect.right = x + pen_width; + penrect.top = y; + penrect.bottom = y + pen_height; + + rectinter(penrect, clip_rect, &penrect); + + draw_pen_rect(&penrect); +} + +/* + * Digital Line Drawing + * by Paul Heckbert + * from "Graphics Gems", Academic Press, 1990 + */ + +/* + * digline: draw digital line from (x1,y1) to (x2,y2), + * calling a user-supplied procedure at each pixel. + * Does no clipping. Uses Bresenham's algorithm. + * + * Paul Heckbert 3 Sep 85 + */ +static void +scan_line(short const x1, + short const y1, + short const x2, + short const y2) { + int d, x, y, ax, ay, sx, sy, dx, dy; + + if (!(pen_width == 0 && pen_height == 0)) { + + dx = x2-x1; ax = ABS(dx)<<1; sx = SGN(dx); + dy = y2-y1; ay = ABS(dy)<<1; sy = SGN(dy); + + x = x1; + y = y1; + if (ax>ay) { /* x dominant */ + d = ay-(ax>>1); + for (;;) { + draw_pen(x, y); + if (x==x2) return; + if ((x > rowlen) && (sx > 0)) return; + if (d>=0) { + y += sy; + d -= ax; + } + x += sx; + d += ay; + } + } + else { /* y dominant */ + d = ax-(ay>>1); + for (;;) { + draw_pen(x, y); + if (y==y2) return; + if ((y > collen) && (sy > 0)) return; + if (d>=0) { + x += sx; + d -= ay; + } + y += sy; + d += ax; + } + } + } +} + + + +static void +Line(int const v) { + struct Point p1; + read_point(&p1); + read_point(¤t); + if (verbose) + pm_message("(%d,%d) to (%d, %d)", + p1.x,p1.y,current.x,current.y); + scan_line(p1.x,p1.y,current.x,current.y); +} + + + +static void +LineFrom(int const v) { + struct Point p1; + read_point(&p1); + if (verbose) + pm_message("(%d,%d) to (%d, %d)", + current.x,current.y,p1.x,p1.y); + + if (!fullres) + scan_line(current.x,current.y,p1.x,p1.y); + + current.x = p1.x; + current.y = p1.y; +} + + + +static void +ShortLine(int const v) { + struct Point p1; + read_point(&p1); + read_short_point(¤t); + if (verbose) + pm_message("(%d,%d) delta (%d, %d)", + p1.x,p1.y,current.x,current.y); + current.x += p1.x; + current.y += p1.y; + + if (!fullres) + scan_line(p1.x,p1.y,current.x,current.y); +} + + + +static void +ShortLineFrom(int const v) { + struct Point p1; + read_short_point(&p1); + if (verbose) + pm_message("(%d,%d) delta (%d, %d)", + current.x,current.y,p1.x,p1.y); + p1.x += current.x; + p1.y += current.y; + if (!fullres) + scan_line(current.x,current.y,p1.x,p1.y); + current.x = p1.x; + current.y = p1.y; +} + +static void +do_paintRect(struct Rect const prect) { + struct Rect rect; + + if (fullres) + return; + + if (verbose) + dumpRect("painting", prect); + + rectinter(clip_rect, prect, &rect); + + draw_pen_rect(&rect); +} + + + +static void +paintRect(int const v) { + read_rect(&cur_rect); + do_paintRect(cur_rect); +} + + + +static void +paintSameRect(int const v) { + do_paintRect(cur_rect); +} + + + +static void +do_frameRect(struct Rect const rect) { + int x, y; + + if (fullres) + return; + + if (verbose) + dumpRect("framing", rect); + + if (pen_width == 0 || pen_height == 0) + return; + + for (x = rect.left; x <= rect.right - pen_width; x += pen_width) { + draw_pen(x, rect.top); + draw_pen(x, rect.bottom - pen_height); + } + + for (y = rect.top; y <= rect.bottom - pen_height ; y += pen_height) { + draw_pen(rect.left, y); + draw_pen(rect.right - pen_width, y); + } +} + + + +static void +frameRect(int const v) { + read_rect(&cur_rect); + do_frameRect(cur_rect); +} + + + +static void +frameSameRect(int const v) { + do_frameRect(cur_rect); +} + + + +/* a stupid shell sort - I'm so embarassed */ + +static void +poly_sort(int const sort_index, struct Point points[]) { + int d, i, j, temp; + + /* initialize and set up sort interval */ + d = 4; + while (d<=sort_index) d <<= 1; + d -= 1; + + while (d > 1) { + d >>= 1; + for (j = 0; j <= (sort_index-d); j++) { + for(i = j; i >= 0; i -= d) { + if ((points[i+d].y < points[i].y) || + ((points[i+d].y == points[i].y) && + (points[i+d].x <= points[i].x))) { + /* swap x1,y1 with x2,y2 */ + temp = points[i].y; + points[i].y = points[i+d].y; + points[i+d].y = temp; + temp = points[i].x; + points[i].x = points[i+d].x; + points[i+d].x = temp; + } + } + } + } +} + + + +/* Watch out for the lack of error checking in the next two functions ... */ + +static void +scan_poly(int const np, + struct Point pts[]) { + int dx,dy,dxabs,dyabs,i,scan_index,j,k,px,py; + int sdx,sdy,x,y,toggle,old_sdy,sy0; + + /* This array needs to be at least as large as the largest dimension of + the bounding box of the poly (but I don't check for overflows ...) */ + struct Point coord[5000]; + + scan_index = 0; + + /* close polygon */ + px = pts[np].x = pts[0].x; + py = pts[np].y = pts[0].y; + + /* This section draws the polygon and stores all the line points + * in an array. This doesn't work for concave or non-simple polys. + */ + /* are y levels same for first and second points? */ + if (pts[1].y == pts[0].y) { + coord[scan_index].x = px; + coord[scan_index].y = py; + scan_index++; + } + +#define sign(x) ((x) > 0 ? 1 : ((x)==0 ? 0:(-1)) ) + + old_sdy = sy0 = sign(pts[1].y - pts[0].y); + for (j=0; j<np; j++) { + /* x,y difference between consecutive points and their signs */ + dx = pts[j+1].x - pts[j].x; + dy = pts[j+1].y - pts[j].y; + sdx = SGN(dx); + sdy = SGN(dy); + dxabs = abs(dx); + dyabs = abs(dy); + x = y = 0; + + if (dxabs >= dyabs) + { + for (k=0; k < dxabs; k++) { + y += dyabs; + if (y >= dxabs) { + y -= dxabs; + py += sdy; + if (old_sdy != sdy) { + old_sdy = sdy; + scan_index--; + } + coord[scan_index].x = px+sdx; + coord[scan_index].y = py; + scan_index++; + } + px += sdx; + draw_pen(px, py); + } + } + else + { + for (k=0; k < dyabs; k++) { + x += dxabs; + if (x >= dyabs) { + x -= dyabs; + px += sdx; + } + py += sdy; + if (old_sdy != sdy) { + old_sdy = sdy; + if (sdy != 0) scan_index--; + } + draw_pen(px,py); + coord[scan_index].x = px; + coord[scan_index].y = py; + scan_index++; + } + } + } + + /* after polygon has been drawn now fill it */ + + scan_index--; + if (sy0 + sdy == 0) scan_index--; + + poly_sort(scan_index, coord); + + toggle = 0; + for (i = 0; i < scan_index; i++) { + if ((coord[i].y == coord[i+1].y) && (toggle == 0)) + { + for (j = coord[i].x; j <= coord[i+1].x; j++) + draw_pen(j, coord[i].y); + toggle = 1; + } + else + toggle = 0; + } +} + + + +static void +paintPoly(int const v) { + struct Rect bb; + struct Point pts[100]; + int i, np = (read_word() - 10) >> 2; + + read_rect(&bb); + for (i=0; i<np; ++i) + read_point(&pts[i]); + + /* scan convert poly ... */ + if (!fullres) + scan_poly(np, pts); +} + + + +static void +PnLocHFrac(int const version) { + word frac = read_word(); + + if (verbose) + pm_message("PnLocHFrac = %d", frac); +} + + + +static void +TxMode(int const version) { + text_mode = read_word(); + + if (text_mode >= 8 && text_mode < 15) + text_mode -= 8; + if (verbose) + pm_message("text transfer mode = %s", + const_name(transfer_name, text_mode)); + + /* ignore the text mask bit 'cause we don't handle it yet */ + text_trf = transfer(text_mode & ~64); +} + + + +static void +TxFont(int const version) { + text_font = read_word(); + if (verbose) + pm_message("text font %s", const_name(font_name, text_font)); +} + + + +static void +TxFace(int const version) { + text_face = read_byte(); + if (verbose) + pm_message("text face %d", text_face); +} + + + +static void +TxSize(int const version) { + text_size = read_word(); + if (verbose) + pm_message("text size %d", text_size); +} + + + +static void +skip_text(void) { + skip(read_byte()); +} + + + +static int +abs_value(int const x) { + if (x < 0) + return -x; + else + return x; +} + + + +static struct font* +get_font(int const font, + int const size, + int const style) { + int closeness, bestcloseness; + struct fontinfo* fi, *best; + + best = 0; + for (fi = fontlist; fi; fi = fi->next) { + closeness = abs_value(fi->font - font) * 10000 + + abs_value(fi->size - size) * 100 + + abs_value(fi->style - style); + if (!best || closeness < bestcloseness) { + best = fi; + bestcloseness = closeness; + } + } + + if (best) { + if (best->loaded) + return best->loaded; + + if ((best->loaded = pbm_loadbdffont(best->filename))) + return best->loaded; + } + + /* It would be better to go looking for the nth best font, really */ + return 0; +} + + + +/* This only does 0, 90, 180 and 270 degree rotations */ + +static void +rotate(int * const x, + int * const y) { + int tmp; + + if (ps_rotation >= 315 || ps_rotation <= 45) + return; + + *x -= ps_cent_x; + *y -= ps_cent_y; + + if (ps_rotation > 45 && ps_rotation < 135) { + tmp = *x; + *x = *y; + *y = tmp; + } + else if (ps_rotation >= 135 && ps_rotation < 225) { + *x = -*x; + } + else if (ps_rotation >= 225 && ps_rotation < 315) { + tmp = *x; + *x = *y; + *y = -tmp; + } + *x += ps_cent_x; + *y += ps_cent_y; +} + + + +static void +do_ps_text(word const tx, + word const ty) { + int len, width, i, w, h, x, y, rx, ry, o; + byte str[256], ch; + struct glyph* glyph; + + current.x = tx; + current.y = ty; + + if (!ps_cent_set) { + ps_cent_x += tx; + ps_cent_y += ty; + ps_cent_set = 1; + } + + len = read_byte(); + + /* XXX this width calculation is not completely correct */ + width = 0; + for (i = 0; i < len; i++) { + ch = str[i] = read_byte(); + if (tfont->glyph[ch]) + width += tfont->glyph[ch]->xadd; + } + + if (verbose) { + str[len] = '\0'; + pm_message("ps text: %s", str); + } + + /* XXX The width is calculated in order to do different justifications. + * However, I need the width of original text to finish the job. + * In other words, font metrics for Quickdraw fonts + */ + + x = tx; + + for (i = 0; i < len; i++) { + if (!(glyph = tfont->glyph[str[i]])) + continue; + + y = ty - glyph->height - glyph->y; + for (h = 0; h < glyph->height; h++) { + for (w = 0; w < glyph->width; w++) { + rx = x + glyph->x + w; + ry = y; + rotate(&rx, &ry); + if ((rx >= picFrame.left) && (rx < picFrame.right) && + (ry >= picFrame.top) && (ry < picFrame.bottom)) + { + o = PIXEL_INDEX(rx, ry); + if (glyph->bmap[h * glyph->width + w]) { + red[o] = foreground.red; + green[o] = foreground.grn; + blue[o] = foreground.blu; + } + } + } + y++; + } + x += glyph->xadd; + } +} + + + +static void +do_text(word const startx, + word const starty) { + if (fullres) + skip_text(); + else { + if (!(tfont = get_font(text_font, text_size, text_face))) + tfont = pbm_defaultfont("bdf"); + + if (ps_text) + do_ps_text(startx, starty); + else { + int len; + word x, y; + + x = startx; + y = starty; + for (len = read_byte(); len > 0; --len) { + struct glyph* const glyph = tfont->glyph[read_byte()]; + if (glyph) { + int dy; + int h; + for (h = 0, dy = y - glyph->height - glyph->y; + h < glyph->height; + ++h, ++dy) { + int w; + for (w = 0; w < glyph->width; ++w) { + struct RGBColor * const colorP = + glyph->bmap[h * glyph->width + w] ? + &black : &white; + draw_pixel(x + w + glyph->x, dy, colorP, text_trf); + } + } + x += glyph->xadd; + } + } + current.x = x; + current.y = y; + } + } +} + + + +static void +LongText(int const version) { + struct Point p; + + read_point(&p); + do_text(p.x, p.y); +} + + + +static void +DHText(int const version) { + current.x += read_byte(); + do_text(current.x, current.y); +} + + + +static void +DVText(int const version) { + current.y += read_byte(); + do_text(current.x, current.y); +} + + + +static void +DHDVText(int const version) { + byte dh, dv; + + dh = read_byte(); + dv = read_byte(); + + if (verbose) + pm_message("dh, dv = %d, %d", dh, dv); + + current.x += dh; + current.y += dv; + do_text(current.x, current.y); +} + + + +/* + * This could use read_pixmap, but I'm too lazy to hack read_pixmap. + */ + +static void +directBits(unsigned int const pictVersion, + bool const skipRegion) { + + struct pixMap p; + struct Rect srcRect; + struct Rect dstRect; + struct raster raster; + word mode; + unsigned int rectWidth; + + /* skip fake len, and fake EOF */ + skip(4); /* Ptr baseAddr == 0x000000ff */ + read_word(); /* version */ + read_rect(&p.Bounds); + rectWidth = p.Bounds.right - p.Bounds.left; + p.packType = read_word(); + p.packSize = read_long(); + p.hRes = read_long(); + p.vRes = read_long(); + p.pixelType = read_word(); + p.pixelSize = read_word(); + p.pixelSize = read_word(); /* XXX twice??? */ + p.cmpCount = read_word(); + p.cmpSize = read_word(); + p.planeBytes = read_long(); + p.pmTable = read_long(); + p.pmReserved = read_long(); + + read_rect(&srcRect); + if (verbose) + dumpRect("source rectangle:", srcRect); + + read_rect(&dstRect); + if (verbose) + dumpRect("destination rectangle:", dstRect); + + mode = read_word(); + if (verbose) + pm_message("transfer mode = %s", const_name(transfer_name, mode)); + + if (skipRegion) + skip_poly_or_region(pictVersion); + + unpackbits(ifp, &p.Bounds, 0, p.pixelSize, &raster); + + blit(srcRect, p.Bounds, raster, p.pixelSize, + dstRect, picFrame, rowlen, NULL, mode); + + freeRaster(raster); +} + + + +#define SKIP_REGION_TRUE TRUE +#define SKIP_REGION_FALSE FALSE + +static void +DirectBitsRect(int const version) { + + directBits(version, SKIP_REGION_FALSE); +} + + + +static void +DirectBitsRgn(int const version) { + + directBits(version, SKIP_REGION_TRUE); +} + + + +static void +do_pixmap(int const version, + word const rowBytes, + int const is_region) { +/*---------------------------------------------------------------------------- + Do a paletted image. +-----------------------------------------------------------------------------*/ + word mode; + struct pixMap p; + struct raster raster; + struct RGBColor * color_table; + struct Rect srcRect; + struct Rect dstRect; + + read_pixmap(&p); + + if (verbose) + pm_message("%u x %u paletted image", + p.Bounds.right - p.Bounds.left, + p.Bounds.bottom - p.Bounds.top); + + color_table = read_color_table(); + + read_rect(&srcRect); + + if (verbose) + dumpRect("source rectangle:", srcRect); + + read_rect(&dstRect); + + if (verbose) + dumpRect("destination rectangle:", dstRect); + + mode = read_word(); + + if (verbose) + pm_message("transfer mode = %s", const_name(transfer_name, mode)); + + if (is_region) + skip_poly_or_region(version); + + stage = "unpacking rectangle"; + + unpackbits(ifp, &p.Bounds, rowBytes, p.pixelSize, &raster); + + blit(srcRect, p.Bounds, raster, 8, + dstRect, picFrame, rowlen, color_table, mode); + + free(color_table); + freeRaster(raster); +} + + + +static void +do_bitmap(int const version, + int const rowBytes, + int const is_region) { +/*---------------------------------------------------------------------------- + Do a bitmap. That's one bit per pixel, 0 is white, 1 is black. +-----------------------------------------------------------------------------*/ + struct Rect Bounds; + struct Rect srcRect; + struct Rect dstRect; + word mode; + struct raster raster; + static struct RGBColor color_table[] = { + {65535L, 65535L, 65535L}, {0, 0, 0} }; + + read_rect(&Bounds); + read_rect(&srcRect); + read_rect(&dstRect); + mode = read_word(); + if (verbose) + pm_message("transfer mode = %s", const_name(transfer_name, mode)); + + if (is_region) + skip_poly_or_region(version); + + stage = "unpacking rectangle"; + + unpackbits(ifp, &Bounds, rowBytes, 1, &raster); + + blit(srcRect, Bounds, raster, 8, + dstRect, picFrame, rowlen, color_table, mode); + + freeRaster(raster); +} + + + +static void +BitsRect(int const version) { + + word rowBytesWord; + bool pixMap; + unsigned int rowBytes; + + stage = "Reading rowBytes word for bitsrect"; + rowBytesWord = read_word(); + + interpretRowBytesWord(rowBytesWord, &pixMap, &rowBytes); + + if (pixMap) + do_pixmap(version, rowBytes, 0); + else + do_bitmap(version, rowBytes, 0); +} + + + +static void +BitsRegion(int const version) { + + word rowBytesWord; + bool pixMap; + unsigned int rowBytes; + + stage = "Reading rowBytes for bitsregion"; + rowBytesWord = read_word(); + + interpretRowBytesWord(rowBytesWord, &pixMap, &rowBytes); + + if (pixMap) + do_pixmap(version, rowBytes, 1); + else + do_bitmap(version, rowBytes, 1); +} + + + + /* + * See http://developer.apple.com/techpubs/mac/QuickDraw/QuickDraw-461.html + * for opcode description + */ +static struct opdef const optable[] = { +/* 0x00 */ { "NOP", 0, NULL, "nop" }, +/* 0x01 */ { "Clip", NA, Clip, "clip" }, +/* 0x02 */ { "BkPat", 8, BkPat, "background pattern" }, +/* 0x03 */ { "TxFont", 2, TxFont, "text font (word)" }, +/* 0x04 */ { "TxFace", 1, TxFace, "text face (byte)" }, +/* 0x05 */ { "TxMode", 2, TxMode, "text mode (word)" }, +/* 0x06 */ { "SpExtra", 4, NULL, "space extra (fixed point)" }, +/* 0x07 */ { "PnSize", 4, PnSize, "pen size (point)" }, +/* 0x08 */ { "PnMode", 2, PnMode, "pen mode (word)" }, +/* 0x09 */ { "PnPat", 8, PnPat, "pen pattern" }, +/* 0x0a */ { "FillPat", 8, FillPat, "fill pattern" }, +/* 0x0b */ { "OvSize", 4, NULL, "oval size (point)" }, +/* 0x0c */ { "Origin", 4, NULL, "dh, dv (word)" }, +/* 0x0d */ { "TxSize", 2, TxSize, "text size (word)" }, +/* 0x0e */ { "FgColor", 4, NULL, "foreground color (longword)" }, +/* 0x0f */ { "BkColor", 4, NULL, "background color (longword)" }, +/* 0x10 */ { "TxRatio", 8, NULL, "numerator (point), denominator (point)" }, +/* 0x11 */ { "Version", 1, NULL, "version (byte)" }, +/* 0x12 */ { "BkPixPat", NA, BkPixPat, "color background pattern" }, +/* 0x13 */ { "PnPixPat", NA, PnPixPat, "color pen pattern" }, +/* 0x14 */ { "FillPixPat", NA, FillPixPat, "color fill pattern" }, +/* 0x15 */ { "PnLocHFrac", 2, PnLocHFrac, "fractional pen position" }, +/* 0x16 */ { "ChExtra", 2, NULL, "extra for each character" }, +/* 0x17 */ res(0), +/* 0x18 */ res(0), +/* 0x19 */ res(0), +/* 0x1a */ { "RGBFgCol", RGB_LEN, RGBFgCol, "RGB foreColor" }, +/* 0x1b */ { "RGBBkCol", RGB_LEN, RGBBkCol, "RGB backColor" }, +/* 0x1c */ { "HiliteMode", 0, NULL, "hilite mode flag" }, +/* 0x1d */ { "HiliteColor", RGB_LEN, NULL, "RGB hilite color" }, +/* 0x1e */ { "DefHilite", 0, NULL, "Use default hilite color" }, +/* 0x1f */ { "OpColor", NA, OpColor, "RGB OpColor for arithmetic modes" }, +/* 0x20 */ { "Line", 8, Line, "pnLoc (point), newPt (point)" }, +/* 0x21 */ { "LineFrom", 4, LineFrom, "newPt (point)" }, +/* 0x22 */ { "ShortLine", 6, ShortLine, + "pnLoc (point, dh, dv (-128 .. 127))" }, +/* 0x23 */ { "ShortLineFrom", 2, ShortLineFrom, "dh, dv (-128 .. 127)" }, +/* 0x24 */ res(WORD_LEN), +/* 0x25 */ res(WORD_LEN), +/* 0x26 */ res(WORD_LEN), +/* 0x27 */ res(WORD_LEN), +/* 0x28 */ { "LongText", NA, LongText, + "txLoc (point), count (0..255), text" }, +/* 0x29 */ { "DHText", NA, DHText, "dh (0..255), count (0..255), text" }, +/* 0x2a */ { "DVText", NA, DVText, "dv (0..255), count (0..255), text" }, +/* 0x2b */ { "DHDVText", NA, DHDVText, + "dh, dv (0..255), count (0..255), text" }, +/* 0x2c */ res(WORD_LEN), +/* 0x2d */ res(WORD_LEN), +/* 0x2e */ res(WORD_LEN), +/* 0x2f */ res(WORD_LEN), +/* 0x30 */ { "frameRect", 8, frameRect, "rect" }, +/* 0x31 */ { "paintRect", 8, paintRect, "rect" }, +/* 0x32 */ { "eraseRect", 8, NULL, "rect" }, +/* 0x33 */ { "invertRect", 8, NULL, "rect" }, +/* 0x34 */ { "fillRect", 8, NULL, "rect" }, +/* 0x35 */ res(8), +/* 0x36 */ res(8), +/* 0x37 */ res(8), +/* 0x38 */ { "frameSameRect", 0, frameSameRect, "rect" }, +/* 0x39 */ { "paintSameRect", 0, paintSameRect, "rect" }, +/* 0x3a */ { "eraseSameRect", 0, NULL, "rect" }, +/* 0x3b */ { "invertSameRect", 0, NULL, "rect" }, +/* 0x3c */ { "fillSameRect", 0, NULL, "rect" }, +/* 0x3d */ res(0), +/* 0x3e */ res(0), +/* 0x3f */ res(0), +/* 0x40 */ { "frameRRect", 8, NULL, "rect" }, +/* 0x41 */ { "paintRRect", 8, NULL, "rect" }, +/* 0x42 */ { "eraseRRect", 8, NULL, "rect" }, +/* 0x43 */ { "invertRRect", 8, NULL, "rect" }, +/* 0x44 */ { "fillRRrect", 8, NULL, "rect" }, +/* 0x45 */ res(8), +/* 0x46 */ res(8), +/* 0x47 */ res(8), +/* 0x48 */ { "frameSameRRect", 0, NULL, "rect" }, +/* 0x49 */ { "paintSameRRect", 0, NULL, "rect" }, +/* 0x4a */ { "eraseSameRRect", 0, NULL, "rect" }, +/* 0x4b */ { "invertSameRRect", 0, NULL, "rect" }, +/* 0x4c */ { "fillSameRRect", 0, NULL, "rect" }, +/* 0x4d */ res(0), +/* 0x4e */ res(0), +/* 0x4f */ res(0), +/* 0x50 */ { "frameOval", 8, NULL, "rect" }, +/* 0x51 */ { "paintOval", 8, NULL, "rect" }, +/* 0x52 */ { "eraseOval", 8, NULL, "rect" }, +/* 0x53 */ { "invertOval", 8, NULL, "rect" }, +/* 0x54 */ { "fillOval", 8, NULL, "rect" }, +/* 0x55 */ res(8), +/* 0x56 */ res(8), +/* 0x57 */ res(8), +/* 0x58 */ { "frameSameOval", 0, NULL, "rect" }, +/* 0x59 */ { "paintSameOval", 0, NULL, "rect" }, +/* 0x5a */ { "eraseSameOval", 0, NULL, "rect" }, +/* 0x5b */ { "invertSameOval", 0, NULL, "rect" }, +/* 0x5c */ { "fillSameOval", 0, NULL, "rect" }, +/* 0x5d */ res(0), +/* 0x5e */ res(0), +/* 0x5f */ res(0), +/* 0x60 */ { "frameArc", 12, NULL, "rect, startAngle, arcAngle" }, +/* 0x61 */ { "paintArc", 12, NULL, "rect, startAngle, arcAngle" }, +/* 0x62 */ { "eraseArc", 12, NULL, "rect, startAngle, arcAngle" }, +/* 0x63 */ { "invertArc", 12, NULL, "rect, startAngle, arcAngle" }, +/* 0x64 */ { "fillArc", 12, NULL, "rect, startAngle, arcAngle" }, +/* 0x65 */ res(12), +/* 0x66 */ res(12), +/* 0x67 */ res(12), +/* 0x68 */ { "frameSameArc", 4, NULL, "rect, startAngle, arcAngle" }, +/* 0x69 */ { "paintSameArc", 4, NULL, "rect, startAngle, arcAngle" }, +/* 0x6a */ { "eraseSameArc", 4, NULL, "rect, startAngle, arcAngle" }, +/* 0x6b */ { "invertSameArc", 4, NULL, "rect, startAngle, arcAngle" }, +/* 0x6c */ { "fillSameArc", 4, NULL, "rect, startAngle, arcAngle" }, +/* 0x6d */ res(4), +/* 0x6e */ res(4), +/* 0x6f */ res(4), +/* 0x70 */ { "framePoly", NA, skip_poly_or_region, "poly" }, +/* 0x71 */ { "paintPoly", NA, paintPoly, "poly" }, +/* 0x72 */ { "erasePoly", NA, skip_poly_or_region, "poly" }, +/* 0x73 */ { "invertPoly", NA, skip_poly_or_region, "poly" }, +/* 0x74 */ { "fillPoly", NA, skip_poly_or_region, "poly" }, +/* 0x75 */ resf(skip_poly_or_region), +/* 0x76 */ resf(skip_poly_or_region), +/* 0x77 */ resf(skip_poly_or_region), +/* 0x78 */ { "frameSamePoly", 0, NULL, "poly (NYI)" }, +/* 0x79 */ { "paintSamePoly", 0, NULL, "poly (NYI)" }, +/* 0x7a */ { "eraseSamePoly", 0, NULL, "poly (NYI)" }, +/* 0x7b */ { "invertSamePoly", 0, NULL, "poly (NYI)" }, +/* 0x7c */ { "fillSamePoly", 0, NULL, "poly (NYI)" }, +/* 0x7d */ res(0), +/* 0x7e */ res(0), +/* 0x7f */ res(0), +/* 0x80 */ { "frameRgn", NA, skip_poly_or_region, "region" }, +/* 0x81 */ { "paintRgn", NA, skip_poly_or_region, "region" }, +/* 0x82 */ { "eraseRgn", NA, skip_poly_or_region, "region" }, +/* 0x83 */ { "invertRgn", NA, skip_poly_or_region, "region" }, +/* 0x84 */ { "fillRgn", NA, skip_poly_or_region, "region" }, +/* 0x85 */ resf(skip_poly_or_region), +/* 0x86 */ resf(skip_poly_or_region), +/* 0x87 */ resf(skip_poly_or_region), +/* 0x88 */ { "frameSameRgn", 0, NULL, "region (NYI)" }, +/* 0x89 */ { "paintSameRgn", 0, NULL, "region (NYI)" }, +/* 0x8a */ { "eraseSameRgn", 0, NULL, "region (NYI)" }, +/* 0x8b */ { "invertSameRgn", 0, NULL, "region (NYI)" }, +/* 0x8c */ { "fillSameRgn", 0, NULL, "region (NYI)" }, +/* 0x8d */ res(0), +/* 0x8e */ res(0), +/* 0x8f */ res(0), +/* 0x90 */ { "BitsRect", NA, BitsRect, "copybits, rect clipped" }, +/* 0x91 */ { "BitsRgn", NA, BitsRegion, "copybits, rgn clipped" }, +/* 0x92 */ res(WORD_LEN), +/* 0x93 */ res(WORD_LEN), +/* 0x94 */ res(WORD_LEN), +/* 0x95 */ res(WORD_LEN), +/* 0x96 */ res(WORD_LEN), +/* 0x97 */ res(WORD_LEN), +/* 0x98 */ { "PackBitsRect", NA, BitsRect, "packed copybits, rect clipped" }, +/* 0x99 */ { "PackBitsRgn", NA, BitsRegion, "packed copybits, rgn clipped" }, +/* 0x9a */ { "DirectBitsRect", NA, DirectBitsRect, + "PixMap, srcRect, dstRect, int copymode, PixData" }, +/* 0x9b */ { "DirectBitsRgn", NA, DirectBitsRgn, + "PixMap, srcRect, dstRect, int copymode, maskRgn, PixData" }, +/* 0x9c */ res(WORD_LEN), +/* 0x9d */ res(WORD_LEN), +/* 0x9e */ res(WORD_LEN), +/* 0x9f */ res(WORD_LEN), +/* 0xa0 */ { "ShortComment", 2, ShortComment, "kind (word)" }, +/* 0xa1 */ { "LongComment", NA, LongComment, + "kind (word), size (word), data" } +}; + + + +static void +interpret_pict(void) { + byte ch; + word picSize; + word opcode; + word len; + unsigned int version; + int i; + struct rgbPlanes planes; + + for (i = 0; i < 64; i++) + pen_pat.pix[i] = bkpat.pix[i] = fillpat.pix[i] = 1; + pen_width = pen_height = 1; + pen_mode = 0; /* srcCopy */ + pen_trf = transfer(pen_mode); + text_mode = 0; /* srcCopy */ + text_trf = transfer(text_mode); + + stage = "Reading picture size"; + picSize = read_word(); + + if (verbose) + pm_message("picture size = %d (0x%x)", picSize, picSize); + + stage = "reading picture frame"; + read_rect(&picFrame); + + if (verbose) { + dumpRect("Picture frame:", picFrame); + pm_message("Picture size is %d x %d", + picFrame.right - picFrame.left, + picFrame.bottom - picFrame.top); + } + + if (!fullres) + allocPlanes(&planes); + + while ((ch = read_byte()) == 0) + ; + if (ch != 0x11) + pm_error("No version number"); + + version = read_byte(); + + switch (version) { + case 1: + break; + case 2: { + unsigned char const subcode = read_byte(); + if (subcode != 0xff) + pm_error("The only Version 2 PICT images this program " + "undertands are subcode 0xff. This image has " + "subcode 0x%02x", subcode); + } break; + default: + pm_error("Unrecognized PICT version %u", version); + } + + if (verbose) + pm_message("PICT version %u", version); + + while((opcode = get_op(version)) != 0xff) { + if (opcode < 0xa2) { + stage = optable[opcode].name; + if (verbose) { + if (STREQ(stage, "reserved")) + pm_message("reserved opcode=0x%x", opcode); + else + pm_message("Opcode: %s", optable[opcode].name); + } + + if (optable[opcode].impl != NULL) + (*optable[opcode].impl)(version); + else if (optable[opcode].len >= 0) + skip(optable[opcode].len); + else switch (optable[opcode].len) { + case WORD_LEN: + len = read_word(); + skip(len); + break; + default: + pm_error("can't do length %u", optable[opcode].len); + } + } else if (opcode == 0xc00) { + if (verbose) + pm_message("HeaderOp"); + stage = "HeaderOp"; + skip(24); + } else if (opcode >= 0xa2 && opcode <= 0xaf) { + stage = "skipping reserved"; + if (verbose) + pm_message("%s 0x%x", stage, opcode); + skip(read_word()); + } else if (opcode >= 0xb0 && opcode <= 0xcf) { + /* just a reserved opcode, no data */ + if (verbose) + pm_message("reserved 0x%x", opcode); + } else if (opcode >= 0xd0 && opcode <= 0xfe) { + stage = "skipping reserved"; + if (verbose) + pm_message("%s 0x%x", stage, opcode); + skip(read_long()); + } else if (opcode >= 0x100 && opcode <= 0x7fff) { + stage = "skipping reserved"; + if (verbose) + pm_message("%s 0x%x", stage, opcode); + skip((opcode >> 7) & 255); + } else if (opcode >= 0x8000 && opcode <= 0x80ff) { + /* just a reserved opcode */ + if (verbose) + pm_message("reserved 0x%x", opcode); + } else if (opcode >= 0x8100) { + stage = "skipping reserved"; + if (verbose) + pm_message("%s 0x%x", stage, opcode); + skip(read_long()); + } else + pm_error("This program does not understand opcode 0x%04x", opcode); + } + + if (fullres) + do_blits(&planes); + + outputPpm(planes); + + freePlanes(planes); +} + + + +int +main(int argc, char * argv[]) { + int argn; + int header; + const char* const usage = +"[-verbose] [-fullres] [-noheader] [-quickdraw] [-fontdir file] [pictfile]"; + + ppm_init( &argc, argv ); + + argn = 1; + verbose = 0; + fullres = 0; + header = 1; + recognize_comment = 1; + + while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0') { + if (pm_keymatch(argv[argn], "-verbose", 2)) + verbose++; + else if (pm_keymatch(argv[argn], "-fullres", 3)) + fullres = 1; + else if (pm_keymatch(argv[argn], "-noheader", 2)) + header = 0; + else if (pm_keymatch(argv[argn], "-quickdraw", 2)) + recognize_comment = 0; + else if (pm_keymatch(argv[argn], "-fontdir", 3)) { + argn++; + if (!argv[argn]) + pm_usage(usage); + else + load_fontdir(argv[argn]); + } + else + pm_usage(usage); + ++argn; + } + + load_fontdir("fontdir"); + + if (argn < argc) { + ifp = pm_openr(argv[argn]); + ++argn; + } else + ifp = stdin; + + if (argn != argc) + pm_usage(usage); + + if (header) { + stage = "Reading 512 byte header"; + skip(512); + } + + interpret_pict(); + + return 0; +} diff --git a/converter/ppm/pjtoppm.c b/converter/ppm/pjtoppm.c new file mode 100644 index 00000000..7b694fb3 --- /dev/null +++ b/converter/ppm/pjtoppm.c @@ -0,0 +1,253 @@ +/* pjtoppm.c - convert an HP PainJetXL image to a portable pixmap file +** +** Copyright (C) 1990 by Christos Zoulas (christos@ee.cornell.edu) +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "ppm.h" +#include "mallocvar.h" + +static char usage[] = "[paintjetfile]"; + +static int egetc ARGS((FILE *fp)); +static int +egetc(fp) + FILE *fp; +{ + int c; + if ((c = fgetc(fp)) == -1) + pm_error("unexpected end of file"); + return(c); +} + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int cmd, val; + char buffer[BUFSIZ]; + int planes = 3, rows = -1, cols = -1; + int r = 0, c = 0, p = 0, i; + unsigned char **image = NULL; + int *imlen; + FILE *fp = stdin; + int mode; + int argn; + unsigned char bf[3]; + pixel *pixrow; + + + ppm_init(&argc, argv); + argn = 1; + if (argn != argc) + fp = pm_openr(argv[argn++]); + else + fp = stdin; + + if (argn != argc) + pm_usage(usage); + + while ((c = fgetc(fp)) != -1) { + if (c != '\033') + continue; + switch (c = egetc(fp)) { + case 'E': /* reset */ + break; + case '*': + cmd = egetc(fp); + for (i = 0; i < BUFSIZ; i++) { + if (!isdigit(c = egetc(fp)) && c != '+' && c != '-') + break; + buffer[i] = c; + } + if (i != 0) { + buffer[i] = '\0'; + if (sscanf(buffer, "%d", &val) != 1) + pm_error("bad value `%s' at <ESC>*%c%c", buffer, cmd, c); + } + else + val = -1; + switch (cmd) { + case 't': + switch (c) { + case 'J': /* render */ + break; + case 'K': /* back scale */ + break; + case 'I': /* gamma */ + break; + case 'R': + break; /* set resolution */ + default: + pm_message("uninmplemented <ESC>*%c%d%c", cmd, val, c); + break; + } + break; + case 'r': + switch (c) { + case 'S': /* width */ + cols = val; + break; + case 'T': /* height */ + rows = val; + break; + case 'U': /* planes */ + planes = val; + if (planes != 3) + pm_error("can handle only 3 plane files"); + break; + case 'A': /* begin raster */ + break; + case 'B': + case 'C': /* end raster */ + break; + case 'V': + break; /* set deci height */ + case 'H': + break; /* set deci width */ + default: + pm_message("uninmplemented <ESC>*%c%d%c", cmd, val, c); + break; + } + break; + case 'b': + switch (c) { + case 'M': /* transmission mode */ + if (val != 0 && val != 1) + pm_error("unimplemented trasmission mode %d", val); + mode = val; + break; + case 'V': /* send plane */ + case 'W': /* send last plane */ + if (rows == -1 || r >= rows || image == NULL) { + if (rows == -1 || r >= rows) + rows += 100; + if (image == NULL) { + MALLOCARRAY(image, rows * planes); + MALLOCARRAY(imlen, rows * planes); + } + else { + image = (unsigned char **) + realloc(image, + rows * planes * + sizeof(unsigned char *)); + imlen = (int *) + realloc(imlen, rows * planes * sizeof(int)); + } + } + if (image == NULL || imlen == NULL) + pm_error("out of memory"); + if (p == planes) + pm_error("too many planes"); + cols = cols > val ? cols : val; + imlen[r * planes + p] = val; + MALLOCARRAY(image[r * planes + p], val); + if (image[r * planes + p] == NULL) + pm_error("out of memory"); + if (fread(image[r * planes + p], 1, val, fp) != val) + pm_error("short data"); + if (c == 'V') + p++; + else { + p = 0; + r++; + } + break; + default: + pm_message("uninmplemented <ESC>*%c%d%c", cmd, val, c); + break; + } + break; + case 'p': /* Position */ + if (p != 0) + pm_error("changed position in the middle of " + "transferring planes"); + switch (c) { + case 'X': + pm_message("can only position in y"); + break; + case 'Y': + if (buffer[0] == '+') + val = r + val; + if (buffer[0] == '-') + val = r - val; + for (; val > r; r++) + for (p = 0; p < 3; p++) { + imlen[r * planes + p] = 0; + image[r * planes + p] = NULL; + } + r = val; + break; + default: + pm_message("uninmplemented <ESC>*%c%d%c", cmd, val, c); + break; + } + default: + pm_message("uninmplemented <ESC>*%c%d%c", cmd, val, c); + break; + } + } + } + pm_close(fp); + rows = r; + if (mode == 1) { + unsigned char *buf; + int newcols = 0; + newcols = 10240; /* It could not be larger that that! */ + cols = 0; + for (r = 0; r < rows; r++) { + if (image[r * planes] == NULL) + continue; + for (p = 0; p < planes; p++) { + MALLOCARRAY(buf, newcols); + if (buf == NULL) + pm_error("out of memory"); + for (i = 0, c = 0; c < imlen[p + r * planes]; c += 2) + for (cmd = image[p + r * planes][c], + val = image[p + r * planes][c+1]; + cmd >= 0 && i < newcols; cmd--, i++) + buf[i] = val; + cols = cols > i ? cols : i; + free(image[p + r * planes]); + /* + * This is less than what we have so it realloc should + * not return null. Even if it does, tough! We will + * lose a line, and probably die on the next line anyway + */ + image[p + r * planes] = (unsigned char *) realloc(buf, i); + } + } + cols *= 8; + } + + + ppm_writeppminit(stdout, cols, rows, (pixval) 255, 0); + pixrow = ppm_allocrow(cols); + for (r = 0; r < rows; r++) { + if (image[r * planes] == NULL) { + for (c = 0; c < cols; c++) + PPM_ASSIGN(pixrow[c], 0, 0, 0); + continue; + } + for (cmd = 0, c = 0; c < cols; c += 8, cmd++) + for (i = 0; i < 8 && c + i < cols; i++) { + for (p = 0; p < planes; p++) + if (mode == 0 && cmd >= imlen[r * planes + p]) + bf[p] = 0; + else + bf[p] = (image[r * planes + p][cmd] & + (1 << (7 - i))) ? 255 : 0; + PPM_ASSIGN(pixrow[c + i], bf[0], bf[1], bf[2]); + } + ppm_writeppmrow(stdout, pixrow, cols, (pixval) 255, 0); + } + pm_close(stdout); + exit(0); +} diff --git a/converter/ppm/ppmtoacad.c b/converter/ppm/ppmtoacad.c new file mode 100644 index 00000000..4f927f02 --- /dev/null +++ b/converter/ppm/ppmtoacad.c @@ -0,0 +1,394 @@ +/* + + Convert a portable pixmap to an AutoCAD slide or DXB file + + Author: + John Walker + Autodesk SA + Avenue des Champs-Montants 14b + CH-2074 MARIN + Switzerland + Usenet: kelvin@Autodesk.com + Fax: 038/33 88 15 + Voice: 038/33 76 33 + + Permission to use, copy, modify, and distribute this software and + its documentation for any purpose and without fee is hereby + granted, without any conditions or restrictions. This software is + provided "as is" without express or implied warranty. + +*/ + +#include <stdio.h> +#include "ppm.h" + +#define TRUE 1 +#define FALSE 0 + +#define EOS '\0' + +#define MAXHIST 32767 /* Color histogram maximum size */ + +static pixel **pixels; /* Input pixel map */ +static colorhash_table cht; /* Color hash table */ +static int curcol = -1; /* Current slide output color */ +static int polymode = FALSE; /* Output filled polygons ? */ +static int dxbmode = FALSE; /* Output .dxb format ? */ +static int bgcol = -1; /* Screen background color */ +static double aspect = 1.0; /* Pixel aspect ratio correction */ +static int gamut = 256; /* Output color gamut */ + +#include "autocad.h" /* AutoCAD standard color assignments */ + +/* prototypes */ +static void outrun ARGS((int color, int ysize, int y, int xstart, int xend)); +static void slideout ARGS((int xdots, int ydots, int ncolors, + unsigned char *red, unsigned char *green, unsigned char *blue)); + + +/* OUTRUN -- Output a run of pixels. */ + +static void outrun(color, ysize, y, xstart, xend) + int color, ysize, y, xstart, xend; +{ + if (color == 0) { + return; /* Let screen background handle this */ + } + + if (curcol != color) { + if (dxbmode) { + putchar((char)136); + (void) pm_writelittleshort(stdout, color); + } else { + (void) pm_writelittleshort(stdout, (short) 0xFF00 | color); + } + curcol = color; + } + if (polymode) { + int v, yb = (ysize - y) + 1, yspan = 1; + + /* Since we're emitting filled polygons, let's scan downward + in the pixmap and see if we can extend the run on this line + vertically as well. If so, emit a polygon that handles + both the horizontal and vertical run and clear the pixels + in the subsequent lines to the background color. */ + + for (v = y + 1; v <= ysize; v++) { + int j, mismatch = FALSE; + + for (j = xstart; j <= xend; j++) { + if (PPM_GETR(pixels[y][j]) != PPM_GETR(pixels[v][j])) { + mismatch = TRUE; + break; + } + } + if (mismatch) { + break; + } + for (j = xstart; j <= xend; j++) { + PPM_ASSIGN(pixels[v][j], 0, 0, 0); + } + } + yspan = v - y; + + if (dxbmode) { + putchar(11); /* Solid */ + (void) pm_writelittleshort( + stdout, (int) (xstart * aspect + 0.4999)); + (void) pm_writelittleshort(stdout, yb); + + (void) pm_writelittleshort( + stdout, (int) ((xend + 1) * aspect + 0.4999)); + (void) pm_writelittleshort(stdout, yb); + + (void) pm_writelittleshort( + stdout, (int) (xstart * aspect + 0.4999)); + (void) pm_writelittleshort(stdout, yb - yspan); + + (void) pm_writelittleshort( + stdout, (int) ((xend + 1) * aspect + 0.4999)); + (void) pm_writelittleshort(stdout, yb - yspan); + } else { + (void) pm_writelittleshort(stdout, (short) 0xFD00); + /* Solid fill header */ + (void) pm_writelittleshort(stdout, 4); + /* Vertices to follow */ + (void) pm_writelittleshort(stdout, -2); /* Fill type */ + + (void) pm_writelittleshort(stdout, (short)0xFD00); + /* Solid fill vertex */ + (void) pm_writelittleshort(stdout, xstart); + (void) pm_writelittleshort(stdout, yb); + + (void) pm_writelittleshort(stdout, (short) 0xFD00); + /* Solid fill vertex */ + (void) pm_writelittleshort(stdout, xend + 1); + (void) pm_writelittleshort(stdout, yb); + + (void) pm_writelittleshort(stdout, (short) 0xFD00); + /* Solid fill vertex */ + (void) pm_writelittleshort(stdout, xend + 1); + (void) pm_writelittleshort(stdout, yb - yspan); + + (void) pm_writelittleshort(stdout, (short) 0xFD00); + /* Solid fill vertex */ + (void) pm_writelittleshort(stdout, xstart); + (void) pm_writelittleshort(stdout, yb - yspan); + + (void) pm_writelittleshort(stdout, (short) 0xFD00); + /* Solid fill trailer */ + (void) pm_writelittleshort(stdout, 4); /* Vertices that precede */ + (void) pm_writelittleshort(stdout, -2); /* Fill type */ + } + } else { + (void) pm_writelittleshort(stdout, xstart); /* Vector: From X */ + (void) pm_writelittleshort(stdout, ysize - y); /* From Y */ + (void) pm_writelittleshort(stdout, xend); /* To X */ + (void) pm_writelittleshort(stdout, ysize - y); /* To Y */ + } +} + +/* SLIDEOUT -- Write an AutoCAD slide. */ + +static void slideout(xdots, ydots, ncolors, red, green, blue) + int xdots, ydots, ncolors; + unsigned char *red, *green, *blue; +{ + static char sldhead[18] = "AutoCAD Slide\r\n\32"; + static char dxbhead[20] = "AutoCAD DXB 1.0\r\n\32"; + unsigned char *acadmap; + int i, xsize, ysize; + + /* If the user has specified a non-black screen background color, + swap the screen background color into color index zero and + move black into the slot previously occupied by the background + color. */ + + if (bgcol > 0) { + acadcol[0][0] = acadcol[bgcol][0]; + acadcol[0][1] = acadcol[bgcol][1]; + acadcol[0][2] = acadcol[bgcol][2]; + acadcol[bgcol][0] = acadcol[bgcol][1] = acadcol[bgcol][2] = 0; + } + + acadmap = (unsigned char *) pm_allocrow(ncolors, sizeof(unsigned char)); + xsize = polymode ? xdots : (xdots - 1); + ysize = polymode ? ydots : (ydots - 1); + if (dxbmode) { + fwrite(dxbhead, 19, 1, stdout); /* DXB file header */ + putchar((char)135); /* Number mode */ + (void) pm_writelittleshort(stdout, 0); /* ...short integers */ + } else { + fwrite(sldhead, 17, 1, stdout); /* Slide file header */ + putchar(86); /* Number format indicator */ + putchar(2); /* File level number */ + (void) pm_writelittleshort(stdout, xsize); /* Max X co-ordinate value */ + (void) pm_writelittleshort(stdout, ysize); /* Max Y co-ordinate value */ + /* Aspect ratio indicator */ + (void) pm_writelittlelong( + stdout, (long) ((((double) xsize) / ysize) * aspect * 1E7)); + (void) pm_writelittleshort(stdout, 2); /* Polygon fill type */ + (void) pm_writelittleshort(stdout, 0x1234); /* Byte order indicator */ + } + + /* Now build a mapping from the image's computed color map + indices to the closest representation of each color within + AutoCAD's color repertoire. */ + + for (i = 0; i < ncolors; i++) { + int best, j; + long dist = 3 * 256 * 256; + + for (j = 0; j < gamut; j++) { + long dr = red[i] - acadcol[j][0], + dg = green[i] - acadcol[j][1], + db = blue[i] - acadcol[j][2]; + long tdist = dr * dr + dg * dg + db * db; + + if (tdist < dist) { + dist = tdist; + best = j; + } + } + acadmap[i] = best; + } + + /* Swoop over the entire map and replace each RGB pixel with its + closest AutoCAD color representation. We do this in a + separate pass since it avoids repetitive mapping of pixels as + we examine them for compression. */ + + for (i = 0; i < ydots; i++) { + int x; + + for (x = 0; x < xdots; x++) { + PPM_ASSIGN(pixels[i][x], + acadmap[ppm_lookupcolor(cht, &pixels[i][x])], 0 ,0); + } + } + + /* Output a run-length encoded expression of the pixmap as AutoCAD + vectors. */ + + for (i = 0; i < ydots; i++) { + int x, rx, rpix = -1, nrun = 0; + + for (x = 0; x < xdots; x++) { + int pix = PPM_GETR(pixels[i][x]); + + if (pix != rpix) { + if (nrun > 0) { + if (rpix > 0) { + outrun(rpix, ydots - 1, i, rx, x - 1); + } + } + rpix = pix; + rx = x; + nrun = 1; + } + } + if ((nrun > 0) && (rpix > 0)) { + outrun(rpix, ydots - 1, i, rx, xdots - 1); + } + } + if (dxbmode) { + putchar(0); /* DXB end sentinel */ + } else { + (void) pm_writelittleshort(stdout, (short) 0xFC00); + /* End of file marker */ + } + pm_freerow((char *) acadmap); +} + +/* Main program. */ + +int main(argc, argv) + int argc; + char* argv[]; +{ + FILE *ifp; + int argn, rows, cols, ncolors, i; + int aspectspec = FALSE; + pixval maxval; + colorhist_vector chv; + unsigned char *Red, *Green, *Blue; + const char * const usage = + "[-poly] [-dxb] [-white] [-background <col>]\n\ + [-aspect <f>] [-8] [ppmfile]"; + + + ppm_init(&argc, argv); + + argn = 1; + while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != EOS) { + if (pm_keymatch(argv[argn], "-dxb", 2)) { + dxbmode = polymode = TRUE; + } else if (pm_keymatch(argv[argn], "-poly", 2)) { + polymode = TRUE; + } else if (pm_keymatch(argv[argn], "-white", 2)) { + if (bgcol >= 0) { + pm_error("already specified a background color"); + } + bgcol = 7; + } else if (pm_keymatch(argv[argn], "-background", 2)) { + if (bgcol >= 0) { + pm_error("already specified a background color"); + } + argn++; + if ((argn == argc) || (sscanf(argv[argn], "%d", &bgcol) != 1)) + pm_usage(usage); + if (bgcol < 0) { + pm_error("background color must be >= 0 and <= 255"); + } + } else if (pm_keymatch(argv[argn], "-aspect", 2)) { + if (aspectspec) { + pm_error("already specified an aspect ratio"); + } + argn++; + if ((argn == argc) || (sscanf(argv[argn], "%lf", &aspect) != 1)) + pm_usage(usage); + if (aspect <= 0.0) { + pm_error("aspect ratio must be greater than 0"); + } + aspectspec = TRUE; + } else if (pm_keymatch(argv[argn], "-8", 2)) { + gamut = 8; + } else { + pm_usage(usage); + } + argn++; + } + + if (argn < argc) { + ifp = pm_openr(argv[argn]); + argn++; + } else { + ifp = stdin; + } + + if (argn != argc) { + pm_usage(usage); + } + + pixels = ppm_readppm(ifp, &cols, &rows, &maxval); + + pm_close(ifp); + + /* Figure out the colormap. Logic for squeezing depth to limit the + number of colors in the image was swiped from ppmquant.c. */ + + while (TRUE) { + int row, col; + pixval newmaxval; + pixel *pP; + + pm_message("computing colormap..."); + chv = ppm_computecolorhist(pixels, cols, rows, MAXHIST, &ncolors); + if (chv != (colorhist_vector) 0) + break; + newmaxval = maxval / 2; + pm_message( + "scaling colors from maxval=%d to maxval=%d to improve clustering...", + maxval, newmaxval ); + for (row = 0; row < rows; ++row) { + for (col = 0, pP = pixels[row]; col < cols; ++col, ++pP) { + PPM_DEPTH(*pP, *pP, maxval, newmaxval); + } + } + maxval = newmaxval; + } + pm_message("%d colors found", ncolors); + + /* Scale the color map derived for the PPM file into one compatible + with AutoCAD's convention of 8 bit intensities. */ + + if (maxval != 255) { + pm_message("maxval is not 255 - automatically rescaling colors"); + } + Red = (unsigned char *) pm_allocrow(ncolors, sizeof(unsigned char)); + Green = (unsigned char *) pm_allocrow(ncolors, sizeof(unsigned char)); + Blue = (unsigned char *) pm_allocrow(ncolors, sizeof(unsigned char)); + + for (i = 0; i < ncolors; ++i) { + if ( maxval == 255 ) { + Red[i] = PPM_GETR(chv[i].color); + Green[i] = PPM_GETG(chv[i].color); + Blue[i] = PPM_GETB( chv[i].color ); + } else { + Red[i] = ((int) PPM_GETR(chv[i].color) * 255) / maxval; + Green[i] = ((int) PPM_GETG(chv[i].color) * 255) / maxval; + Blue[i] = ((int) PPM_GETB(chv[i].color) * 255) / maxval; + } + } + + /* And make a hash table for fast lookup. */ + + cht = ppm_colorhisttocolorhash(chv, ncolors); + ppm_freecolorhist(chv); + + slideout(cols, rows, ncolors, Red, Green, Blue); + pm_freerow((char *) Red); + pm_freerow((char *) Green); + pm_freerow((char *) Blue); + exit(0); +} diff --git a/converter/ppm/ppmtoarbtxt.c b/converter/ppm/ppmtoarbtxt.c new file mode 100644 index 00000000..774a47c4 --- /dev/null +++ b/converter/ppm/ppmtoarbtxt.c @@ -0,0 +1,505 @@ +/* ppmtoarbtxt.c - convert portable pixmap to cleartext +** +** Renamed from ppmtotxt.c by Bryan Henderson in January 2003. +** +** Copyright (C) 1995 by Peter Kirchgessner +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include <string.h> + +#include "ppm.h" +#include "mallocvar.h" +#include "nstring.h" + +typedef enum { +/* The types of object we handle */ + BDATA, IRED, IGREEN, IBLUE, ILUM, FRED, FGREEN, FBLUE, FLUM, + WIDTH, HEIGHT, POSX, POSY +} SKL_OBJ_TYP; + +/* Maximum size for a format string ("%d" etc.) */ +#define MAXFORMAT 16 + +/* The data we keep for each object */ +typedef union + { + struct BNDAT { char *bdat; /* Binary data (text with newlines etc.) */ + int ndat; + } bin_data; + + struct ICDAT { char icformat[MAXFORMAT]; /* Integer colors */ + int icolmin, icolmax; + } icol_data; + + struct FCDAT { char fcformat[MAXFORMAT]; /* Float colors */ + double fcolmin, fcolmax; + } fcol_data; + + struct IDAT { char iformat[MAXFORMAT]; /* Integer data */ + } i_data; + } SKL_OBJ_DATA; + + +/* Each object has a type and some data */ +typedef struct + { + SKL_OBJ_TYP otyp; + SKL_OBJ_DATA odata; + } SKL_OBJ; + + +#define MAX_SKL_HEAD_OBJ 64 +#define MAX_SKL_BODY_OBJ 256 +#define MAX_SKL_TAIL_OBJ 64 +#define MAX_LINE_BUF 1024 +#define MAX_OBJ_BUF 80 + + +static void write_txt (fout, nobj, obj, width, height, x, y, red, green, blue) +FILE *fout; +int nobj; +SKL_OBJ *obj[]; +int width, height, x, y; +double red, green, blue; + +{register int count; + +#define WRITE_BNDAT(fd,theobj) \ + {struct BNDAT *bdata = &((theobj)->odata.bin_data); \ + fwrite (bdata->bdat,bdata->ndat,1,fd); } + +#define WRITE_ICOL(fd,theobj,thecol) \ + {struct ICDAT *icdata = &((theobj)->odata.icol_data); \ + fprintf (fd,icdata->icformat,(int)(icdata->icolmin \ + + (icdata->icolmax - icdata->icolmin)*(thecol))); } + +#define WRITE_FCOL(fd,theobj,thecol) \ + {struct FCDAT *fcdata = &((theobj)->odata.fcol_data); \ + fprintf (fd,fcdata->fcformat,(double)(fcdata->fcolmin \ + + (fcdata->fcolmax - fcdata->fcolmin)*(thecol))); } + +#define WRITE_IDAT(fd,theobj,thedat) \ + {struct IDAT *idata = &((theobj)->odata.i_data); \ + fprintf (fd,idata->iformat,thedat); } + + for (count = 0; count < nobj; count++) + { + switch (obj[count]->otyp) + { + case BDATA: + WRITE_BNDAT (fout,obj[count]); + break; + case IRED: + WRITE_ICOL (fout,obj[count],red); + break; + case IGREEN: + WRITE_ICOL (fout,obj[count],green); + break; + case IBLUE: + WRITE_ICOL (fout,obj[count],blue); + break; + case ILUM: + WRITE_ICOL (fout,obj[count],0.299*red+0.587*green+0.114*blue); + break; + case FRED: + WRITE_FCOL (fout,obj[count],red); + break; + case FGREEN: + WRITE_FCOL (fout,obj[count],green); + break; + case FBLUE: + WRITE_FCOL (fout,obj[count],blue); + break; + case FLUM: + WRITE_FCOL (fout,obj[count],0.299*red+0.587*green+0.114*blue); + break; + case WIDTH: + WRITE_IDAT (fout,obj[count],width); + break; + case HEIGHT: + WRITE_IDAT (fout,obj[count],height); + break; + case POSX: + WRITE_IDAT (fout,obj[count],x); + break; + case POSY: + WRITE_IDAT (fout,obj[count],y); + break; + } + } +} + + +static SKL_OBJ * +save_bin_data(int const ndat, + char * const bdat) { + + /* Save binary data in Object */ + + SKL_OBJ *obj; + + obj = (SKL_OBJ *)malloc (sizeof (SKL_OBJ) + ndat); + if (obj != NULL) + { + obj->otyp = BDATA; + obj->odata.bin_data.ndat = ndat; + obj->odata.bin_data.bdat = ((char *)(obj))+sizeof (SKL_OBJ); + memcpy (obj->odata.bin_data.bdat,bdat,ndat); + } + return (obj); +} + + + +/* Save integer color data in Object */ +static SKL_OBJ *save_icol_data (ctyp,format,icolmin,icolmax) +SKL_OBJ_TYP ctyp; +char *format; +int icolmin, icolmax; + +{SKL_OBJ *obj; + + obj = (SKL_OBJ *)malloc (sizeof (SKL_OBJ)); + if (obj != NULL) + { + obj->otyp = ctyp; + strcpy (obj->odata.icol_data.icformat,format); + obj->odata.icol_data.icolmin = icolmin; + obj->odata.icol_data.icolmax = icolmax; + } + return (obj); +} + + +/* Save float color data in Object */ +static SKL_OBJ *save_fcol_data (ctyp,format,fcolmin,fcolmax) +SKL_OBJ_TYP ctyp; +char *format; +double fcolmin, fcolmax; + +{SKL_OBJ *obj; + + obj = (SKL_OBJ *)malloc (sizeof (SKL_OBJ)); + if (obj != NULL) + { + obj->otyp = ctyp; + strcpy (obj->odata.fcol_data.fcformat,format); + obj->odata.fcol_data.fcolmin = fcolmin; + obj->odata.fcol_data.fcolmax = fcolmax; + } + return (obj); +} + + +/* Save universal data in Object */ +static SKL_OBJ *save_i_data (ctyp,format) +SKL_OBJ_TYP ctyp; +char *format; + +{SKL_OBJ *obj; + + obj = (SKL_OBJ *)malloc (sizeof (SKL_OBJ)); + if (obj != NULL) + { + obj->otyp = ctyp; + strcpy (obj->odata.i_data.iformat,format); + } + return (obj); +} + + +/* Read skeleton file */ +static int read_skeleton (filename,maxskl,nskl,skl) +char *filename; +int maxskl,*nskl; +SKL_OBJ **skl; + +{FILE *sklfile; + int slen, objlen, chr, n_odata; + int icolmin,icolmax; + double fcolmin,fcolmax; + SKL_OBJ_TYP otyp; + char line[MAX_LINE_BUF+MAX_OBJ_BUF+16]; + char objstr[MAX_OBJ_BUF],typstr[MAX_OBJ_BUF]; + char formstr[MAX_OBJ_BUF]; + char meta1 = '#', meta2 = '(', meta3 = ')'; + +#define SAVE_BIN(slen,line) \ + { if ((skl[*nskl] = save_bin_data (slen,line)) != NULL) (*nskl)++; \ + slen = 0; } + +#define ADD_STR(slen,line,addlen,addstr) \ + {int count=0; line[slen++] = meta1; line[slen++] = meta2; \ + while (count++ < addlen) line[slen++] = addstr[count]; } + + if ((sklfile = fopen (filename,"r")) == NULL) + return (-1); + + /* Parse skeleton file */ + *nskl = 0; + + slen = 0; + while ((chr = getc (sklfile)) != EOF) /* Up to end of skeleton file */ + { + if (*nskl >= maxskl) return (-1); + + if (slen+1 >= MAX_LINE_BUF) /* Buffer finished ? Save as binary object */ + { + SAVE_BIN (slen,line); + } + + if (chr != meta1) /* Look for start of replacement string */ + { + line[slen++] = chr; + continue; + } + + if ((chr = getc (sklfile)) == EOF) + { + line[slen++] = meta1; + break; + } + if (chr != meta2) /* '(' ? Otherwise no replacement */ + { + line[slen++] = meta1; + line[slen++] = chr; + continue; + } + + objlen = 0; + for (;;) /* Read replacement string up to ')' */ + { + if (objlen == sizeof (objstr)) break; /* ')' not found */ + if ((chr = getc (sklfile)) == EOF) break; + if (chr == meta3) break; + objstr[objlen++] = chr; + } + objstr[objlen] = '\0'; /* Now objstr keeps data without metas */ + + if (chr != meta3) /* Object not found ? */ + { + ADD_STR (slen,line,objlen,objstr); /* Save what we already read */ + if (chr == EOF) break; + continue; + } + + typstr[0] = '\0'; /* Get typ of data */ + sscanf (objstr,"%s",typstr); + + /* Check for integer colors */ + if (STREQ(typstr,"ired") ) otyp = IRED; + else if (STREQ(typstr,"igreen")) otyp = IGREEN; + else if (STREQ(typstr,"iblue") ) otyp = IBLUE; + else if (STREQ(typstr,"ilum") ) otyp = ILUM; + /* Check for real colors */ + else if (STREQ(typstr,"fred") ) otyp = FRED; + else if (STREQ(typstr,"fgreen")) otyp = FGREEN; + else if (STREQ(typstr,"fblue") ) otyp = FBLUE; + else if (STREQ(typstr,"flum") ) otyp = FLUM; + /* Check for integer data */ + else if (STREQ(typstr,"width") ) otyp = WIDTH; + else if (STREQ(typstr,"height")) otyp = HEIGHT; + else if (STREQ(typstr,"posx") ) otyp = POSX; + else if (STREQ(typstr,"posy") ) otyp = POSY; + else otyp = BDATA; + + if ((otyp == IRED) || (otyp == IGREEN) || (otyp == IBLUE) || (otyp == ILUM)) + { + n_odata = sscanf (objstr,"%*s%s%d%d",formstr,&icolmin,&icolmax); + + if (n_odata == EOF) /* No arguments specified ? Use defaults */ + { + strcpy (formstr,"%d"); icolmin = 0; icolmax = 255; + } + else if (n_odata != 3) /* Wrong specification */ + { + otyp = BDATA; + } + } + + if ((otyp == FRED) || (otyp == FGREEN) || (otyp == FBLUE) || (otyp == FLUM)) + { + n_odata = sscanf (objstr,"%*s%s%lf%lf",formstr,&fcolmin,&fcolmax); + + if (n_odata == EOF) /* No arguments specified ? Use defaults */ + { + strcpy (formstr,"%f"); fcolmin = 0.0; fcolmax = 1.0; + } + else if (n_odata != 3) /* Wrong specification */ + { + otyp = BDATA; + } + } + + if ( (otyp == WIDTH) || (otyp == HEIGHT) + || (otyp == POSX) || (otyp == POSY)) + { + n_odata = sscanf (objstr,"%*s%s",formstr); + + if (n_odata == EOF) /* No arguments specified ? Use defaults */ + { + strcpy (formstr,"%d"); + } + else if (n_odata != 1) /* Wrong specification */ + { + otyp = BDATA; + } + } + + if (otyp != BDATA) /* Got an object definition ? */ + { + if (slen > 0) /* Save what we already found */ + { + SAVE_BIN (slen,line); + } + } + + /* Now process the object in objstr */ + switch (otyp) + { + case BDATA: /* Bad object definition ? Save as text */ + ADD_STR (slen,line,objlen,objstr); + break; + + case IRED: + case IGREEN: + case IBLUE: + case ILUM: + skl[*nskl] = save_icol_data (otyp,formstr,icolmin,icolmax); + if (skl[*nskl] != NULL) (*nskl)++; + break; + + case FRED: + case FGREEN: + case FBLUE: + case FLUM: + skl[*nskl] = save_fcol_data (otyp,formstr,fcolmin,fcolmax); + if (skl[*nskl] != NULL) (*nskl)++; + break; + + case WIDTH: + case HEIGHT: + case POSX: + case POSY: + skl[*nskl] = save_i_data (otyp,formstr); + if (skl[*nskl] != NULL) (*nskl)++; + break; + } + } /* EOF of skeleton file */ + + if (slen > 0) /* Drop finishing newline character */ + { + if (line[slen-1] == '\n') slen--; + } + + if (slen > 0) /* Something left ? */ + { + SAVE_BIN (slen,line); /* Save it */ + } + + fclose (sklfile); + return (0); +} + + +int main( argc, argv ) +int argc; +char* argv[]; + +{register int col; + register pixel* xP; + pixel* pixelrow; + pixval maxval,red,green,blue; + double dmaxval; + int argn, rows, cols, format, row; + int head_nskl,body_nskl,tail_nskl; + SKL_OBJ *head_skl[MAX_SKL_HEAD_OBJ]; + SKL_OBJ *body_skl[MAX_SKL_BODY_OBJ]; + SKL_OBJ *tail_skl[MAX_SKL_TAIL_OBJ]; + FILE *ifp; + const char *usage = "bodyskl [ -hd headskl ] [ -tl tailskl ] [pnmfile]"; + + ppm_init( &argc, argv ); + + argn = 1; + if (argn == argc) + pm_usage( usage ); + /* Read body skeleton file */ + if (read_skeleton (argv[argn],sizeof (body_skl)/sizeof (SKL_OBJ *), + &body_nskl,body_skl) < 0) + pm_usage ( usage ); + ++argn; + + head_nskl = tail_nskl = 0; + + while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0') + { + if ( pm_keymatch ( argv[argn], "-hd", 1) && argn+1 < argc ) + { + argn++; /* Read header skeleton */ + if (read_skeleton (argv[argn],sizeof (head_skl)/sizeof (SKL_OBJ *), + &head_nskl,head_skl) < 0) + pm_usage ( usage ); + } + else if ( pm_keymatch ( argv[argn], "-tl", 1) && argn+1 < argc ) + { + argn++; /* Read tail skeleton */ + if (read_skeleton (argv[argn],sizeof (tail_skl)/sizeof (SKL_OBJ *), + &tail_nskl,tail_skl) < 0) + pm_usage ( usage ); + } + else + { + pm_usage ( usage ); + } + argn++; + } + + if ( argn != argc ) + { + ifp = pm_openr( argv[argn] ); + ++argn; + } + else + { + ifp = stdin; + } + + if ( argn != argc ) + pm_usage( usage ); + + ppm_readppminit( ifp, &cols, &rows, &maxval, &format ); + pixelrow = ppm_allocrow( cols ); + dmaxval = (double)maxval; + + if (head_nskl > 0) /* Write header */ + write_txt (stdout,head_nskl,head_skl,cols,rows,0,0,0.0,0.0,0.0); + + for ( row = 0; row < rows; ++row ) + { + ppm_readppmrow( ifp, pixelrow, cols, maxval, format ); + + for ( col = 0, xP = pixelrow; col < cols; ++col, ++xP ) + { + red = PPM_GETR( *xP ); + green = PPM_GETG( *xP ); + blue = PPM_GETB( *xP ); + write_txt (stdout,body_nskl,body_skl,cols,rows,col,row, + red/dmaxval,green/dmaxval,blue/dmaxval); + } + } + + if (tail_nskl > 0) /* Write trailer */ + write_txt (stdout,tail_nskl,tail_skl,cols,rows,0,0,0.0,0.0,0.0); + + pm_close( ifp ); + + exit( 0 ); +} diff --git a/converter/ppm/ppmtobmp.c b/converter/ppm/ppmtobmp.c new file mode 100644 index 00000000..071f3b12 --- /dev/null +++ b/converter/ppm/ppmtobmp.c @@ -0,0 +1,795 @@ +/* + * ppmtobmp.c - Converts from a PPM file to a Microsoft Windows or OS/2 + * .BMP file. + * + * Copyright (C) 1992 by David W. Sanderson. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. This software is provided "as is" + * without express or implied warranty. + * + */ + +#define _BSD_SOURCE 1 /* Make sure strdup() is in string.h */ +#define _XOPEN_SOURCE 500 /* Make sure strdup() is in string.h */ + +#include <assert.h> +#include <string.h> +#include "bmp.h" +#include "ppm.h" +#include "shhopt.h" +#include "bitio.h" + +#define MAXCOLORS 256 + +enum colortype {TRUECOLOR, PALETTE}; + +typedef struct { +/*---------------------------------------------------------------------------- + A color map for a BMP file. +-----------------------------------------------------------------------------*/ + unsigned int count; + /* Number of colors in the map. The first 'count' elements of these + arrays are defined; all others are not. + */ + colorhash_table cht; + + /* Indices in the following arrays are the same as in 'cht', above. */ + unsigned char red[MAXCOLORS]; + unsigned char grn[MAXCOLORS]; + unsigned char blu[MAXCOLORS]; +} colorMap; + + + +static void +freeColorMap(const colorMap * const colorMapP) { + + if (colorMapP->cht) + ppm_freecolorhash(colorMapP->cht); +} + + + +static struct cmdline_info { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + char *input_filename; + int class; /* C_WIN or C_OS2 */ + unsigned int bppSpec; + unsigned int bpp; +} cmdline; + + +static void +parse_command_line(int argc, char ** argv, + struct cmdline_info *cmdline_p) { +/*---------------------------------------------------------------------------- + Note that many of the strings that this function returns in the + *cmdline_p structure are actually in the supplied argv array. And + sometimes, one of these strings is actually just a suffix of an entry + in argv! +-----------------------------------------------------------------------------*/ + optEntry *option_def = malloc(100*sizeof(optEntry)); + /* Instructions to OptParseOptions3 on how to parse our options. + */ + optStruct3 opt; + + unsigned int windowsSpec, os2Spec; + + unsigned int option_def_index; + + option_def_index = 0; /* incremented by OPTENTRY */ + OPTENT3('w', "windows", OPT_FLAG, NULL, &windowsSpec, 0); + OPTENT3('o', "os2", OPT_FLAG, NULL, &os2Spec, 0); + OPTENT3(0, "bpp", OPT_UINT, &cmdline_p->bpp, + &cmdline_p->bppSpec, 0); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ + + optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + + if (windowsSpec && os2Spec) + pm_error("Can't specify both -windows and -os2 options."); + else if (windowsSpec) + cmdline_p->class = C_WIN; + else if (os2Spec) + cmdline_p->class = C_OS2; + else + cmdline_p->class = C_WIN; + + + if (cmdline_p->bppSpec) { + if (cmdline_p->bpp != 1 && cmdline_p->bpp != 4 && + cmdline_p->bpp != 8 && cmdline_p->bpp != 24) + pm_error("Invalid -bpp value specified: %u. The only values valid\n" + "in the BMP format are 1, 4, 8, and 24 bits per pixel", + cmdline_p->bpp); + } + + if (argc - 1 == 0) + cmdline_p->input_filename = strdup("-"); /* he wants stdin */ + else if (argc - 1 == 1) + cmdline_p->input_filename = strdup(argv[1]); + else + pm_error("Too many arguments. The only argument accepted\n" + "is the input file specificaton"); + +} + + + +static void +PutByte(FILE * const fp, unsigned char const v) { + if (putc(v, fp) == EOF) + pm_error("Write of a byte to a file failed."); + + /* Note: a Solaris/SPARC user reported on 2003.09.29 that the above + putc() returned EOF when a former version of this code declared + v as "char" instead of "unsigned char". This was apparently due + to a bug in his C library that caused 255 to look like -1 at some + critical internal point. + */ +} + + + +static void +PutShort(FILE * const fp, short const v) { + if (pm_writelittleshort(fp, v) == -1) + pm_error("Write of a halfword to a file failed."); +} + + + +static void +PutLong(FILE * const fp, long const v) { + if (pm_writelittlelong(fp, v) == -1) + pm_error("Write of a word to a file failed."); +} + + + +/* + * BMP writing + */ + +static int +BMPwritefileheader(FILE * const fp, + int const class, + unsigned long const bitcount, + unsigned long const x, + unsigned long const y) { +/*---------------------------------------------------------------------------- + Return the number of bytes written, or -1 on error. +-----------------------------------------------------------------------------*/ + PutByte(fp, 'B'); + PutByte(fp, 'M'); + + /* cbSize */ + PutLong(fp, BMPlenfile(class, bitcount, -1, x, y)); + + /* xHotSpot */ + PutShort(fp, 0); + + /* yHotSpot */ + PutShort(fp, 0); + + /* offBits */ + PutLong(fp, BMPoffbits(class, bitcount, -1)); + + return 14; +} + + + +static int +BMPwriteinfoheader(FILE * const fp, + int const class, + unsigned long const bitcount, + unsigned long const x, + unsigned long const y) { +/*---------------------------------------------------------------------------- + Return the number of bytes written, or -1 on error. +----------------------------------------------------------------------------*/ + long cbFix; + + switch (class) { + case C_WIN: { + cbFix = 40; + PutLong(fp, cbFix); + + PutLong(fp, x); /* cx */ + PutLong(fp, y); /* cy */ + PutShort(fp, 1); /* cPlanes */ + PutShort(fp, bitcount); /* cBitCount */ + + /* + * We've written 16 bytes so far, need to write 24 more + * for the required total of 40. + */ + + PutLong(fp, 0); /* Compression */ + PutLong(fp, 0); /* ImageSize */ + PutLong(fp, 0); /* XpixelsPerMeter */ + PutLong(fp, 0); /* YpixelsPerMeter */ + PutLong(fp, 0); /* ColorsUsed */ + PutLong(fp, 0); /* ColorsImportant */ + } + break; + case C_OS2: { + cbFix = 12; + PutLong(fp, cbFix); + + PutShort(fp, x); /* cx */ + PutShort(fp, y); /* cy */ + PutShort(fp, 1); /* cPlanes */ + PutShort(fp, bitcount); /* cBitCount */ + } + break; + default: + pm_error(er_internal, "BMPwriteinfoheader"); + } + + return cbFix; +} + + + +static int +BMPwritergb(FILE * const fp, + int const class, + pixval const R, + pixval const G, + pixval const B) { +/*---------------------------------------------------------------------------- + Return the number of bytes written, or -1 on error. +-----------------------------------------------------------------------------*/ + switch (class) { + case C_WIN: + PutByte(fp, B); + PutByte(fp, G); + PutByte(fp, R); + PutByte(fp, 0); + return 4; + case C_OS2: + PutByte(fp, B); + PutByte(fp, G); + PutByte(fp, R); + return 3; + default: + pm_error(er_internal, "BMPwritergb"); + } + return -1; +} + + + +static int +BMPwritecolormap(FILE * const ifP, + int const class, + int const bpp, + const colorMap * const colorMapP) { +/*---------------------------------------------------------------------------- + Return the number of bytes written, or -1 on error. +-----------------------------------------------------------------------------*/ + long const ncolors = (1 << bpp); + + unsigned int nbyte; + unsigned int i; + + nbyte = 0; + for (i = 0; i < colorMapP->count; ++i) + nbyte += BMPwritergb(ifP, class, + colorMapP->red[i], + colorMapP->grn[i], + colorMapP->blu[i]); + + for (; i < ncolors; ++i) + nbyte += BMPwritergb(ifP, class, 0, 0, 0); + + return nbyte; +} + + + +static int +BMPwriterow_palette(FILE * const fp, + const pixel * const row, + unsigned long const cx, + unsigned short const bpp, + colorhash_table const cht) { +/*---------------------------------------------------------------------------- + Return the number of bytes written, or -1 on error. +-----------------------------------------------------------------------------*/ + BITSTREAM b; + int retval; + + b = pm_bitinit(fp, "w"); + if (b == NULL) + retval = -1; + else { + unsigned int nbyte; + unsigned int x; + bool error; + + nbyte = 0; /* initial value */ + error = FALSE; /* initial value */ + + for (x = 0; x < cx && !error; ++x) { + int rc; + rc = pm_bitwrite(b, bpp, ppm_lookupcolor(cht, &row[x])); + if (rc == -1) + error = TRUE; + else + nbyte += rc; + } + if (error) + retval = -1; + else { + int rc; + + rc = pm_bitfini(b); + if (rc == -1) + retval = -1; + else { + nbyte += rc; + + /* Make sure we write a multiple of 4 bytes. */ + while (nbyte % 4 != 0) { + PutByte(fp, 0); + ++nbyte; + } + retval = nbyte; + } + } + } + return retval; +} + + + +static int +BMPwriterow_truecolor(FILE * const fp, + const pixel * const row, + unsigned long const cols, + pixval const maxval) { +/*---------------------------------------------------------------------------- + Write a row of a truecolor BMP image to the file 'fp'. The row is + 'row', which is 'cols' columns long. + + Return the number of bytes written. + + On error, issue error message and exit program. +-----------------------------------------------------------------------------*/ + /* This works only for 24 bits per pixel. To implement this for the + general case (which is only hypothetical -- this program doesn't + write any truecolor images except 24 bit and apparently no one + else does either), you would move this function into + BMPwriterow_palette, which writes arbitrary bit strings. But + that would be a lot slower and less robust. + */ + + int nbyte; /* Number of bytes we have written to file so far */ + int col; + + nbyte = 0; /* initial value */ + for (col = 0; col < cols; col++) { + /* We scale to the BMP maxval, which is always 255. */ + PutByte(fp, PPM_GETB(row[col]) * 255 / maxval); + PutByte(fp, PPM_GETG(row[col]) * 255 / maxval); + PutByte(fp, PPM_GETR(row[col]) * 255 / maxval); + nbyte += 3; + } + + /* + * Make sure we write a multiple of 4 bytes. + */ + while (nbyte % 4) { + PutByte(fp, 0); + nbyte++; + } + + return nbyte; +} + + + +static int +BMPwritebits(FILE * const fp, + unsigned long const cx, + unsigned long const cy, + enum colortype const colortype, + unsigned short const cBitCount, + const pixel ** const pixels, + pixval const maxval, + colorhash_table const cht) { +/*---------------------------------------------------------------------------- + Return the number of bytes written, or -1 on error. +-----------------------------------------------------------------------------*/ + int nbyte; + long y; + + if (cBitCount > 24) + pm_error("cannot handle cBitCount: %d", cBitCount); + + nbyte = 0; /* initial value */ + + /* The picture is stored bottom line first, top line last */ + + for (y = cy - 1; y >= 0; --y) { + int rc; + if (colortype == PALETTE) + rc = BMPwriterow_palette(fp, pixels[y], cx, + cBitCount, cht); + else + rc = BMPwriterow_truecolor(fp, pixels[y], cx, maxval); + + if (rc == -1) + pm_error("couldn't write row %ld", y); + if (rc % 4 != 0) + pm_error("row had bad number of bytes: %d", rc); + nbyte += rc; + } + + return nbyte; +} + + + +static void +BMPEncode(FILE * const ifP, + int const class, + enum colortype const colortype, + int const bpp, + int const x, + int const y, + const pixel ** const pixels, + pixval const maxval, + const colorMap * const colorMapP) { +/*---------------------------------------------------------------------------- + Write a BMP file of the given class. +-----------------------------------------------------------------------------*/ + unsigned long nbyte; + + if (colortype == PALETTE) + pm_message("Writing %d bits per pixel with a color palette", bpp); + else + pm_message("Writing %d bits per pixel truecolor (no palette)", bpp); + + nbyte = 0; /* initial value */ + nbyte += BMPwritefileheader(ifP, class, bpp, x, y); + nbyte += BMPwriteinfoheader(ifP, class, bpp, x, y); + if (colortype == PALETTE) + nbyte += BMPwritecolormap(ifP, class, bpp, colorMapP); + + if (nbyte != (BMPlenfileheader(class) + + BMPleninfoheader(class) + + BMPlencolormap(class, bpp, -1))) + pm_error(er_internal, "BMPEncode 1"); + + nbyte += BMPwritebits(ifP, x, y, colortype, bpp, pixels, maxval, + colorMapP->cht); + if (nbyte != BMPlenfile(class, bpp, -1, x, y)) + pm_error(er_internal, "BMPEncode 2"); +} + + + +static void +makeBilevelColorMap(colorMap * const colorMapP) { + + colorMapP->count = 2; + colorMapP->cht = NULL; + colorMapP->red[0] = 0; + colorMapP->grn[0] = 0; + colorMapP->blu[0] = 0; + colorMapP->red[1] = 255; + colorMapP->grn[1] = 255; + colorMapP->blu[1] = 255; +} + + + +static void +BMPEncodePBM(FILE * const ifP, + int const class, + int const cols, + int const rows, + unsigned char ** const bitrow) { +/*---------------------------------------------------------------------------- + Write a bi-level BMP file of the given class. +-----------------------------------------------------------------------------*/ + /* Note: + Only PBM input uses this routine. Color images represented by 1 bpp via + color palette use the general BMPEncode(). + */ + unsigned int const adjustedCols = (cols + 31) / 32 * 32; + unsigned int const packedBytes = adjustedCols / 8; + + unsigned long nbyte; + colorMap bilevelColorMap; + unsigned int row; + + /* colortype == PALETTE */ + pm_message("Writing 1 bit per pixel with a black-white palette"); + + nbyte = 0; /* initial value */ + nbyte += BMPwritefileheader(ifP, class, 1, cols, rows); + nbyte += BMPwriteinfoheader(ifP, class, 1, cols, rows); + + makeBilevelColorMap(&bilevelColorMap); + + nbyte += BMPwritecolormap(ifP, class, 1, &bilevelColorMap); + + if (nbyte != (BMPlenfileheader(class) + + BMPleninfoheader(class) + + BMPlencolormap(class, 1, -1))) + pm_error(er_internal, "BMPEncodePBM 1"); + + for (row = 0; row < rows; ++row){ + size_t bytesWritten; + + bytesWritten = fwrite(bitrow[row], 1, packedBytes, ifP); + if (bytesWritten != packedBytes){ + if (feof(ifP)) + pm_error("End of file writing row %u of BMP raster.", row); + else + pm_error("Error writing BMP raster. Errno=%d (%s)", + errno, strerror(errno)); + } else + nbyte += bytesWritten; + } + + if (nbyte != BMPlenfile(class, 1, -1, cols, rows)) + pm_error(er_internal, "BMPEncodePBM 2"); +} + + + +static void +analyze_colors(const pixel ** const pixels, + int const cols, + int const rows, + pixval const maxval, + int * const minimum_bpp_p, + colorMap * const colorMapP) { +/*---------------------------------------------------------------------------- + Look at the colors in the image 'pixels' and compute values to use in + representing those colors in a BMP image. + + First of all, count the distinct colors. Return as *minimum_bpp_p + the minimum number of bits per pixel it will take to represent all + the colors in BMP format. + + If there are few enough colors to represent with a palette, also + return the number of colors as *colors_p and a suitable palette + (colormap) and a hash table in which to look up indexes into that + palette as *colorMapP. Use only the first *colors_p entries of the + map. + + If there are too many colors for a palette, return colorMapP->cht + == NULL. + + Issue informational messages. +-----------------------------------------------------------------------------*/ + /* Figure out the colormap. */ + colorhist_vector chv; + int colorCount; + + pm_message("analyzing colors..."); + chv = ppm_computecolorhist((pixel**)pixels, cols, rows, MAXCOLORS, + &colorCount); + colorMapP->count = colorCount; + if (chv == NULL) { + pm_message("More than %u colors found", MAXCOLORS); + *minimum_bpp_p = 24; + colorMapP->cht = NULL; + } else { + unsigned int const minbits = pm_maxvaltobits(colorMapP->count - 1); + + unsigned int i; + + pm_message("%u colors found", colorMapP->count); + + /* Only 1, 4, 8, and 24 are defined in the BMP spec we + implement and other bpp's have in fact been seen to confuse + viewers. There is an extended BMP format that has 16 bpp + too, but this program doesn't know how to generate that + (see Bmptopnm.c, though). + */ + if (minbits == 1) + *minimum_bpp_p = 1; + else if (minbits <= 4) + *minimum_bpp_p = 4; + else if (minbits <= 8) + *minimum_bpp_p = 8; + else + *minimum_bpp_p = 24; + + /* + * Now scale the maxval to 255 as required by BMP format. + */ + for (i = 0; i < colorMapP->count; ++i) { + colorMapP->red[i] = (pixval) PPM_GETR(chv[i].color) * 255 / maxval; + colorMapP->grn[i] = (pixval) PPM_GETG(chv[i].color) * 255 / maxval; + colorMapP->blu[i] = (pixval) PPM_GETB(chv[i].color) * 255 / maxval; + } + + /* And make a hash table for fast lookup. */ + colorMapP->cht = ppm_colorhisttocolorhash(chv, colorMapP->count); + ppm_freecolorhist(chv); + } +} + + + +static void +choose_colortype_bpp(struct cmdline_info const cmdline, + unsigned int const colors, + unsigned int const minimum_bpp, + enum colortype * const colortype_p, + unsigned int * const bits_per_pixel_p) { + + if (!cmdline.bppSpec) { + /* User has no preference as to bits per pixel. Choose the + smallest number possible for this image. + */ + *bits_per_pixel_p = minimum_bpp; + } else { + if (cmdline.bpp < minimum_bpp) + pm_error("There are too many colors in the image to " + "represent in the\n" + "number of bits per pixel you requested: %d.\n" + "You may use Ppmquant to reduce the number of " + "colors in the image.", + cmdline.bpp); + else + *bits_per_pixel_p = cmdline.bpp; + } + + assert(*bits_per_pixel_p == 1 || + *bits_per_pixel_p == 4 || + *bits_per_pixel_p == 8 || + *bits_per_pixel_p == 24); + + if (*bits_per_pixel_p > 8) + *colortype_p = TRUECOLOR; + else { + *colortype_p = PALETTE; + } +} + + + +static void +doPbm(FILE * const ifP, + unsigned int const cols, + unsigned int const rows, + int const format, + int const class, + FILE * const ofP) { + + /* In the PBM case the raster is read directly from the input by + pbm_readpbmrow_packed. The raster format is almost identical, + except that BMP specifies rows to be zero-filled to 32 bit borders + and that in BMP the bottom row comes first in order. + */ + + int const CHARBITS = (sizeof(unsigned char)*8); + int const colChars = pbm_packed_bytes(cols); + int const adjustedCols = (cols+31) /32 * 32; + int const packedBytes = adjustedCols /8; + + unsigned char ** bitrow; + unsigned int row; + + bitrow = pbm_allocarray_packed(adjustedCols, rows); + + for (row = 0; row < rows; ++row) { + unsigned char * const thisRow = bitrow[rows - row - 1]; + + /* Clear end of each row */ + thisRow[packedBytes-1] = 0x00; + thisRow[packedBytes-2] = 0x00; + thisRow[packedBytes-3] = 0x00; + thisRow[packedBytes-4] = 0x00; + + pbm_readpbmrow_packed(ifP, thisRow, cols, format); + + { + unsigned int i; + for (i = 0; i < colChars; ++i) + thisRow[i] = ~thisRow[i]; /* flip all pixels */ + } + /* This may seem unnecessary, because the color palette + (RGB[] in BMPEncodePBM) can be inverted for the same effect. + However we take this precaution, for there is indication that + some BMP viewers may get confused with that. + */ + + if (cols % 8 >0) { + /* adjust final partial byte */ + thisRow[colChars-1] >>= CHARBITS - cols % CHARBITS; + thisRow[colChars-1] <<= CHARBITS - cols % CHARBITS; + } + } + + BMPEncodePBM(ofP, class, cols, rows, bitrow); +} + + + +static void +doPgmPpm(FILE * const ifP, + unsigned int const cols, + unsigned int const rows, + pixval const maxval, + int const ppmFormat, + int const class, + FILE * const ofP) { + + /* PGM and PPM. The input image is read into a PPM array, scanned + for color analysis and converted to a BMP raster. + Logic works for PBM. + */ + int minimumBpp; + unsigned int bitsPerPixel; + enum colortype colortype; + unsigned int row; + + pixel ** pixels; + colorMap colorMap; + + pixels = ppm_allocarray(cols, rows); + + for (row = 0; row < rows; ++row) + ppm_readppmrow(ifP, pixels[row], cols, maxval, ppmFormat); + + analyze_colors((const pixel**)pixels, cols, rows, maxval, + &minimumBpp, &colorMap); + + choose_colortype_bpp(cmdline, colorMap.count, minimumBpp, &colortype, + &bitsPerPixel); + + BMPEncode(stdout, class, colortype, bitsPerPixel, + cols, rows, (const pixel**)pixels, maxval, &colorMap); + + freeColorMap(&colorMap); +} + + + +int +main(int argc, char **argv) { + + FILE * ifP; + int rows; + int cols; + pixval maxval; + int ppmFormat; + + ppm_init(&argc, argv); + + parse_command_line(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.input_filename); + + ppm_readppminit(ifP, &cols, &rows, &maxval, &ppmFormat); + + if (PPM_FORMAT_TYPE(ppmFormat) == PBM_TYPE) + doPbm(ifP, cols, rows, ppmFormat, cmdline.class, stdout); + else + doPgmPpm(ifP, cols, rows, maxval, ppmFormat, cmdline.class, stdout); + + pm_close(ifP); + pm_close(stdout); + + return 0; +} diff --git a/converter/ppm/ppmtoeyuv.c b/converter/ppm/ppmtoeyuv.c new file mode 100644 index 00000000..f5ce1156 --- /dev/null +++ b/converter/ppm/ppmtoeyuv.c @@ -0,0 +1,396 @@ +/* Bryan got this from mm.ftp-cs.berkeley.edu from the package + mpeg-encode-1.5b-src under the name ppmtoeyuv.c on March 30, 2000. + The file was dated January 19, 1995. + + Bryan changed the program to take an argument as the input filename + and fixed a crash when the input image has an odd number of rows or + columns. + + Then Bryan updated the program on March 15, 2001 to use the Netpbm + libraries to read the PPM input and handle multi-image PPM files + and arbitrary maxvals. + + There was no attached documentation except for this: Encoder/Berkeley + YUV format is merely the concatenation of Y, U, and V data in order. + Compare with Abekas YUV, which interlaces Y, U, and V data. + + Future enhancement: It may be useful to have an option to do the + calculations without multiplication tables to save memory at the + expense of execution speed for large maxvals. Actually, a large + maxval without a lot of colors might actually make the tables + slower. + +*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/* + * $Header: /n/picasso/users/keving/encode/src/RCS/readframe.c,v 1.1 1993/07/22 22:23:43 keving Exp keving $ + * $Log: readframe.c,v $ + * Revision 1.1 1993/07/22 22:23:43 keving + * nothing + * + */ + + +/*==============* + * HEADER FILES * + *==============*/ + +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <stdlib.h> + +#include "pm_c_util.h" +#include "ppm.h" +#include "mallocvar.h" + +typedef unsigned char uint8; + +/* Multiplication tables */ + +#define YUVMAXVAL 255 +#define HALFYUVMAXVAL 128 +/* multXXX are multiplication tables used in RGB-YCC calculations for + speed. mult299[x] is x * .299, scaled to a maxval of 255. These + are malloc'ed and essentially constant. + + We use these tables because it is much faster to do a + multiplication once for each possible sample value than once for + each pixel in the image. +*/ +static float *mult299, *mult587, *mult114; +static float *mult16874, *mult33126, *mult5; +static float *mult41869, *mult08131; + +static __inline__ float +luminance(const pixel p) { + return mult299[PPM_GETR(p)] + + mult587[PPM_GETG(p)] + + mult114[PPM_GETB(p)] + ; +} + +static __inline__ float +chrominance_red(const pixel p) { + return mult5[PPM_GETR(p)] + + mult41869[PPM_GETG(p)] + + mult08131[PPM_GETB(p)] + ; +} + +static __inline__ float +chrominance_blue(const pixel p) { + return mult16874[PPM_GETR(p)] + + mult33126[PPM_GETG(p)] + + mult5[PPM_GETB(p)] + ; +} + + + +static void +create_multiplication_tables(const pixval maxval) { + + int index; + + MALLOCARRAY_NOFAIL(mult299 , maxval+1); + MALLOCARRAY_NOFAIL(mult587 , maxval+1); + MALLOCARRAY_NOFAIL(mult114 , maxval+1); + MALLOCARRAY_NOFAIL(mult16874 , maxval+1); + MALLOCARRAY_NOFAIL(mult33126 , maxval+1); + MALLOCARRAY_NOFAIL(mult5 , maxval+1); + MALLOCARRAY_NOFAIL(mult41869 , maxval+1); + MALLOCARRAY_NOFAIL(mult08131 , maxval+1); + + if (maxval == YUVMAXVAL) { + /* fast path */ + for ( index = 0; index <= maxval; index++ ) { + mult299[index] = 0.29900*index; + mult587[index] = 0.58700*index; + mult114[index] = 0.11400*index; + mult5[index] = 0.50000*index; + mult41869[index] = -0.41869*index; + mult08131[index] = -0.08131*index; + mult16874[index] = -0.16874*index; + mult33126[index] = -0.33126*index; + } + } else { + for ( index = 0; index <= maxval; index++ ) { + mult299[index] = 0.29900*index*(maxval/YUVMAXVAL); + mult587[index] = 0.58700*index*(maxval/YUVMAXVAL); + mult114[index] = 0.11400*index*(maxval/YUVMAXVAL); + mult5[index] = 0.50000*index*(maxval/YUVMAXVAL); + mult41869[index] = -0.41869*index*(maxval/YUVMAXVAL); + mult08131[index] = -0.08131*index*(maxval/YUVMAXVAL); + mult16874[index] = -0.16874*index*(maxval/YUVMAXVAL); + mult33126[index] = -0.33126*index*(maxval/YUVMAXVAL); + } + + } +} + + + +static void +free_multiplication_tables(void) { + free(mult299 ); + free(mult587 ); + free(mult114 ); + free(mult16874 ); + free(mult33126 ); + free(mult5 ); + free(mult41869 ); + free(mult08131 ); +} + + + +/*===========================================================================* + * + * PPMtoYUV + * + * convert PPM data into YUV data + * assumes that ydivisor = 1 + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + * This function processes the input file in 4 pixel squares. If the + * Image does not have an even number of rows and columns, the rightmost + * column or the bottom row gets ignored and output has one fewer row + * or column than the input. + * + *===========================================================================*/ +static void +PPMtoYUV(pixel ** const ppm_image, const int width, const int height, + uint8 *** const orig_yP, + uint8 *** const orig_crP, + uint8 *** const orig_cbP) { + + int y; + uint8 ** orig_y; + uint8 ** orig_cr; + uint8 ** orig_cb; + + orig_y = *orig_yP; + orig_cr = *orig_crP; + orig_cb = *orig_cbP; + + for (y = 0; y + 1 < height; y += 2) { + uint8 *dy0, *dy1; + uint8 *dcr, *dcb; + const pixel *src0, *src1; + /* Pair of contiguous rows of the ppm input image we are + converting */ + int x; + + src0 = ppm_image[y]; + src1 = ppm_image[y + 1]; + + dy0 = orig_y[y]; + dy1 = orig_y[y + 1]; + dcr = orig_cr[y / 2]; + dcb = orig_cb[y / 2]; + + for ( x = 0; x + 1 < width; x += 2) { + dy0[x] = luminance(src0[x]); + dy1[x] = luminance(src1[x]); + + dy0[x+1] = luminance(src0[x+1]); + dy1[x+1] = luminance(src1[x+1]); + + dcr[x/2] = (( + chrominance_red(src0[x]) + + chrominance_red(src1[x]) + + chrominance_red(src0[x+1]) + + chrominance_red(src1[x+1]) + ) / 4) + HALFYUVMAXVAL; + + dcb[x/2] = (( + chrominance_blue(src0[x]) + + chrominance_blue(src1[x]) + + chrominance_blue(src0[x+1]) + + chrominance_blue(src1[x+1]) + ) / 4) + HALFYUVMAXVAL; + } + } +} + + + +static void +WriteYUV(FILE *fpointer, const int width, const int height, + uint8 ** const orig_y, uint8 ** const orig_cr, uint8 ** const orig_cb) +{ + register int y; + + for (y = 0; y < height; y++) /* Y */ + fwrite(orig_y[y], 1, width, fpointer); + + for (y = 0; y < height / 2; y++) /* U */ + fwrite(orig_cb[y], 1, width / 2, fpointer); + + for (y = 0; y < height / 2; y++) /* V */ + fwrite(orig_cr[y], 1, width / 2, fpointer); +} + + + +static void +AllocYUV(int const width, + int const height, + uint8 *** const orig_yP, + uint8 *** const orig_crP, + uint8 *** const orig_cbP) { + + int y; + uint8 ** orig_y; + uint8 ** orig_cr; + uint8 ** orig_cb; + + MALLOCARRAY_NOFAIL(*orig_yP, height); + orig_y = *orig_yP; + for (y = 0; y < height; y++) + MALLOCARRAY_NOFAIL(orig_y[y], width); + + MALLOCARRAY_NOFAIL(*orig_crP, height / 2); + orig_cr = *orig_crP; + for (y = 0; y < height / 2; y++) + MALLOCARRAY_NOFAIL(orig_cr[y], width / 2); + + MALLOCARRAY_NOFAIL(*orig_cbP, height / 2); + orig_cb = *orig_cbP; + for (y = 0; y < height / 2; y++) + MALLOCARRAY_NOFAIL(orig_cb[y], width / 2); +} + + + +static void +FreeYUV(const int width, const int height, + uint8 ** const orig_y, uint8 ** const orig_cr, uint8 ** const orig_cb){ + + int y; + + if (orig_y) { + for (y = 0; y < height; y++) + free(orig_y[y]); + free(orig_y); + } + + if (orig_cr) { + for (y = 0; y < height / 2; y++) + free(orig_cr[y]); + free(orig_cr); + } + + if (orig_cb) { + for (y = 0; y < height / 2; y++) + free(orig_cb[y]); + free(orig_cb); + } +} + + + +int +main(int argc, char **argv) { + const char *input_filename; /* NULL for stdin */ + FILE * ifp; + int width, height; + pixval maxval; + pixel **ppm_image; /* malloc'ed */ + uint8 **orig_y, **orig_cr, **orig_cb; + /* orig_y is the height x width array of individual pixel luminances + orig_cr and orig_cb are the height/2 x width/2 arrays of average + red and blue chrominance values over each 4 pixel square. + */ + int eof; + + /* The following are width, height, and maxval of the image we + processed before this one. Zero if there was no image before + this one. + */ + int last_width, last_height; + pixval last_maxval; + + ppm_init(&argc, argv); + + if (argc > 2) { + pm_error("Program takes either one argument -- " + "the input filename -- or no arguments (input is stdin)"); + exit(1); + } else if (argc == 2) + input_filename = argv[1]; + else input_filename = NULL; + + if (input_filename == NULL) ifp = stdin; + else ifp = pm_openr(input_filename); + + eof = FALSE; + last_maxval = 0; /* No previous maxval */ + last_width = 0; /* No previous width */ + last_height = 0; /* No previous height */ + orig_y = orig_cr = orig_cb = 0; + + while (!eof) { + ppm_image = ppm_readppm(ifp, &width, &height, &maxval); + + if (width % 2 != 0) + pm_message("Input image has odd number of columns. The rightmost " + "column will be omitted from the output."); + if (height % 2 != 0) + pm_message("Input image has odd number of rows. The bottom " + "row will be omitted from the output."); + + if (maxval != last_maxval) { + /* We're going to need all new multiplication tables. */ + free_multiplication_tables(); + create_multiplication_tables(maxval); + } + last_maxval = maxval; + + if (height != last_height || width != last_width) { + FreeYUV(width, height, orig_y, orig_cr, orig_cb); + /* Need new YUV buffers for different size */ + AllocYUV(width, height, &orig_y, &orig_cr, &orig_cb); + } + last_height = height; + last_width = width; + + PPMtoYUV(ppm_image, width, height, &orig_y, &orig_cr, &orig_cb); + + WriteYUV(stdout, (width/2)*2, (height/2)*2, orig_y, orig_cr, orig_cb); + + ppm_freearray(ppm_image, height); + ppm_nextimage(ifp, &eof); + } + FreeYUV(width, height, orig_y, orig_cr, orig_cb); + free_multiplication_tables(); + pm_close(ifp); + + return 0; +} + diff --git a/converter/ppm/ppmtogif.c b/converter/ppm/ppmtogif.c new file mode 100644 index 00000000..9521237b --- /dev/null +++ b/converter/ppm/ppmtogif.c @@ -0,0 +1,1681 @@ +/* ppmtogif.c - read a portable pixmap and produce a GIF file +** +** Based on GIFENCOD by David Rowley <mgardi@watdscu.waterloo.edu>.A +** Lempel-Zim compression based on "compress". +** +** Modified by Marcel Wijkstra <wijkstra@fwi.uva.nl> +** +** The non-LZW GIF generation stuff was adapted from the Independent +** JPEG Group's djpeg on 2001.09.29. The uncompressed output subroutines +** are derived directly from the corresponding subroutines in djpeg's +** wrgif.c source file. Its copyright notice say: + + * Copyright (C) 1991-1997, 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. + * + The reference README file is README.JPEG in the Netpbm package. +** +** Copyright (C) 1989 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +** +** The Graphics Interchange Format(c) is the Copyright property of +** CompuServe Incorporated. GIF(sm) is a Service Mark property of +** CompuServe Incorporated. +*/ + +/* TODO: merge the LZW and uncompressed subroutines. They are separate + only because they had two different lineages and the code is too + complicated for me quickly to rewrite it. +*/ +#include <assert.h> +#include <string.h> + +#include "mallocvar.h" +#include "shhopt.h" +#include "ppm.h" + +#define MAXCMAPSIZE 256 + +static unsigned int const gifMaxval = 255; + +static bool verbose; +/* + * a code_int must be able to hold 2**BITS values of type int, and also -1 + */ +typedef int code_int; + +typedef long int count_int; + + +struct cmap { + /* This is the information for the GIF colormap (aka palette). */ + + int red[MAXCMAPSIZE], green[MAXCMAPSIZE], blue[MAXCMAPSIZE]; + /* These arrays arrays map a color index, as is found in + the raster part of the GIF, to an intensity value for the indicated + RGB component. + */ + int perm[MAXCMAPSIZE], permi[MAXCMAPSIZE]; + /* perm[i] is the position in the sorted colormap of the color which + is at position i in the unsorted colormap. permi[] is the inverse + function of perm[]. + */ + unsigned int cmapsize; + /* Number of entries in the GIF colormap. I.e. number of colors + in the image, plus possibly one fake transparency color. + */ + int transparent; + /* color index number in GIF palette of the color that is to be + transparent. -1 if no color is transparent. + */ + colorhash_table cht; + /* A hash table that relates a PPM pixel value to to a pre-sort + GIF colormap index. + */ + pixval maxval; + /* The maxval for the colors in 'cht'. */ +}; + +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char *input_filespec; /* Filespec of input file */ + const char *alpha_filespec; /* Filespec of alpha file; NULL if none */ + const char *alphacolor; /* -alphacolor option value or default */ + unsigned int interlace; /* -interlace option value */ + unsigned int sort; /* -sort option value */ + const char *mapfile; /* -mapfile option value. NULL if none. */ + const char *transparent; /* -transparent option value. NULL if none. */ + const char *comment; /* -comment option value; NULL if none */ + unsigned int nolzw; /* -nolzw option */ + unsigned int verbose; +}; + + +static void +handleLatex2htmlHack(void) { +/*---------------------------------------------------------------------------- + This program used to put out a "usage" message when it saw an option + it didn't understand. Latex2html's configure program does a + ppmtogif -h (-h was never a valid option) to elicit that message and + then parses the message to see if it included the strings + "-interlace" and "-transparent". That way it knows if the + 'ppmtogif' program it found has those options or not. I don't think + any 'ppmtogif' you're likely to find today lacks those options, but + latex2html checks anyway, and we don't want it to conclude that we + don't have them. + + So we issue a special error message just to trick latex2html into + deciding that we have -interlace and -transparent options. The function + is not documented in the man page. We would like to see Latex2html + either stop checking or check like configure programs usually do -- + try the option and see if you get success or failure. + + -Bryan 2001.11.14 +-----------------------------------------------------------------------------*/ + pm_error("latex2html, you should just try the -interlace and " + "-transparent options to see if they work instead of " + "expecting a 'usage' message from -h"); +} + + + +static void +parseCommandLine(int argc, char ** argv, + struct cmdlineInfo * const cmdlineP) { +/*---------------------------------------------------------------------------- + Parse the program arguments (given by argc and argv) into a form + the program can deal with more easily -- a cmdline_info structure. + If the syntax is invalid, issue a message and exit the program via + pm_error(). + + Note that the file spec array we return is stored in the storage that + was passed to us as the argv array. +-----------------------------------------------------------------------------*/ + optEntry * option_def; /* malloc'ed */ + optStruct3 opt; /* set by OPTENT3 */ + unsigned int option_def_index; + + unsigned int latex2htmlhack; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "interlace", OPT_FLAG, + NULL, &cmdlineP->interlace, 0); + OPTENT3(0, "sort", OPT_FLAG, + NULL, &cmdlineP->sort, 0); + OPTENT3(0, "nolzw", OPT_FLAG, + NULL, &cmdlineP->nolzw, 0); + OPTENT3(0, "mapfile", OPT_STRING, + &cmdlineP->mapfile, NULL, 0); + OPTENT3(0, "transparent", OPT_STRING, + &cmdlineP->transparent, NULL, 0); + OPTENT3(0, "comment", OPT_STRING, + &cmdlineP->comment, NULL, 0); + OPTENT3(0, "alpha", OPT_STRING, + &cmdlineP->alpha_filespec, NULL, 0); + OPTENT3(0, "alphacolor", OPT_STRING, + &cmdlineP->alphacolor, NULL, 0); + OPTENT3(0, "h", OPT_FLAG, + NULL, &latex2htmlhack, 0); + OPTENT3(0, "verbose", OPT_FLAG, + NULL, &cmdlineP->verbose, 0); + + /* Set the defaults */ + cmdlineP->mapfile = NULL; + cmdlineP->transparent = NULL; /* no transparency */ + cmdlineP->comment = NULL; /* no comment */ + cmdlineP->alpha_filespec = NULL; /* no alpha file */ + cmdlineP->alphacolor = "rgb:0/0/0"; + /* We could say "black" here, but then we depend on the color names + database existing. + */ + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ + + optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (latex2htmlhack) + handleLatex2htmlHack(); + + if (argc-1 == 0) + cmdlineP->input_filespec = "-"; + else if (argc-1 != 1) + pm_error("Program takes zero or one argument (filename). You " + "specified %d", argc-1); + else + cmdlineP->input_filespec = argv[1]; + + if (cmdlineP->alpha_filespec && cmdlineP->transparent) + pm_error("You cannot specify both -alpha and -transparent."); +} + + + +/* + * Write out a word to the GIF file + */ +static void +Putword(int const w, FILE * const fp) { + + fputc( w & 0xff, fp ); + fputc( (w / 256) & 0xff, fp ); +} + + +static int +closestcolor(pixel const color, + pixval const maxval, + struct cmap * const cmapP) { +/*---------------------------------------------------------------------------- + Return the pre-sort colormap index of the color in the colormap *cmapP + that is closest to the color 'color', whose maxval is 'maxval'. + + Also add 'color' to the colormap hash, with the colormap index we + are returning. Caller must ensure that the color is not already in + there. +-----------------------------------------------------------------------------*/ + unsigned int i; + unsigned int imin, dmin; + + pixval const r = PPM_GETR(color) * gifMaxval / maxval; + pixval const g = PPM_GETG(color) * gifMaxval / maxval; + pixval const b = PPM_GETB(color) * gifMaxval / maxval; + + dmin = SQR(255) * 3; + imin = 0; + for (i=0;i < cmapP->cmapsize; i++) { + int const d = SQR(r-cmapP->red[i]) + + SQR(g-cmapP->green[i]) + + SQR(b-cmapP->blue[i]); + if (d < dmin) { + dmin = d; + imin = i; + } + } + ppm_addtocolorhash(cmapP->cht, &color, cmapP->permi[imin]); + + return cmapP->permi[imin]; +} + + + +enum pass {MULT8PLUS0, MULT8PLUS4, MULT4PLUS2, MULT2PLUS1}; + + +typedef struct { + FILE * fileP; + /* The PPM file stream from which pixels come. The position + of this file is also part of the state of this pixelReader. + */ + unsigned int width; + /* Width of the image, in columns */ + unsigned int height; + /* Height of the image, in rows */ + pixval maxval; + int format; + pm_filepos rasterPos; + /* Position in file fileP of the start of the raster */ + bool interlace; + /* We're accessing the image in interlace fashion */ + unsigned int nPixelsLeft; + /* Number of pixels we have left to read in the image */ + pm_pixelcoord next; + /* Location of next pixel to read */ + enum pass pass; + /* The interlace pass. Undefined if !interlace */ + pixel * curPixelRow; + /* The pixels of the current row (the one numbered in with pixel + 'next' resides). + Dynamically allocated. + */ +} pixelReader; + + + +static void +pixelReaderReadCurrentRow(pixelReader * const rdrP) { + + ppm_readppmrow(rdrP->fileP, rdrP->curPixelRow, + rdrP->width, rdrP->maxval, rdrP->format); +} + + + +static void +pixelReaderCreate(FILE * const ifP, + unsigned int const width, + unsigned int const height, + pixval const maxval, + int const format, + pm_filepos const rasterPos, + bool const interlace, + pixelReader ** const pixelReaderPP) { + + pixelReader * rdrP; + + MALLOCVAR_NOFAIL(rdrP); + + rdrP->fileP = ifP; + rdrP->width = width; + rdrP->height = height; + rdrP->maxval = maxval; + rdrP->format = format; + rdrP->rasterPos = rasterPos; + rdrP->interlace = interlace; + rdrP->pass = MULT8PLUS0; + rdrP->next.col = 0; + rdrP->next.row = 0; + rdrP->nPixelsLeft = width * height; + + rdrP->curPixelRow = ppm_allocrow(width); + + pm_seek2(rdrP->fileP, &rasterPos, sizeof(rasterPos)); + + pixelReaderReadCurrentRow(rdrP); + + *pixelReaderPP = rdrP; +} + + + +static void +pixelReaderDestroy(pixelReader * const pixelReaderP) { + + ppm_freerow(pixelReaderP->curPixelRow); + + free(pixelReaderP); +} + + + +static size_t +bytesPerSample(pixval const maxval) { + + return maxval < (1 << 16) ? 1 : 2; +} + + + +/* TODO - move this to libnetpbm */ + + + +void +ppm_seek(FILE * const fileP, + const pm_filepos * const rasterPosP, + size_t const rasterPosSize, + unsigned int const cols, + pixval const maxval, + unsigned int const col, + unsigned int const row); + +void +ppm_seek(FILE * const fileP, + const pm_filepos * const rasterPosP, + size_t const rasterPosSize, + unsigned int const cols, + pixval const maxval, + unsigned int const col, + unsigned int const row) { + + pm_filepos rasterPos; + pm_filepos pixelPos; + + if (rasterPosSize == sizeof(pm_filepos)) + rasterPos = *rasterPosP; + else if (rasterPosSize == sizeof(long)) + rasterPos = *(long*)rasterPosP; + else + pm_error("File position size passed to ppm_seek() is invalid: %u. " + "Valid sizes are %u and %u", + rasterPosSize, sizeof(pm_filepos), sizeof(long)); + + pixelPos = rasterPos + row * cols * bytesPerSample(maxval) + col; + + pm_seek2(fileP, &pixelPos, sizeof(pixelPos)); +} + + + + +static void +pixelReaderGotoNextInterlaceRow(pixelReader * const rdrP) { +/*---------------------------------------------------------------------------- + Position reader to the next row in the interlace pattern. + + Assume there is at least one more row to read. +-----------------------------------------------------------------------------*/ + assert(rdrP->nPixelsLeft >= rdrP->width); + + /* There are 4 passes: + MULT8PLUS0: Rows 0, 8, 16, 24, 32, etc. + MULT8PLUS4: Rows 4, 12, 20, 28, etc. + MULT4PLUS2: Rows 2, 6, 10, 14, etc. + MULT2PLUS1: Rows 1, 3, 5, 7, 9, etc. + */ + + switch (rdrP->pass) { + case MULT8PLUS0: + rdrP->next.row += 8; + break; + case MULT8PLUS4: + rdrP->next.row += 8; + break; + case MULT4PLUS2: + rdrP->next.row += 4; + break; + case MULT2PLUS1: + rdrP->next.row += 2; + break; + } + + /* If we've finished a pass, next.row is now beyond the end of + the image. In that case, we switch to the next pass now. + + Note that if there are more than 4 rows, the sequence of passes + is sequential, but when there are fewer than 4, we may skip + e.g. from MULT8PLUS0 to MULT4PLUS2. + */ + while (rdrP->next.row >= rdrP->height) { + switch (rdrP->pass) { + case MULT8PLUS0: + rdrP->pass = MULT8PLUS4; + rdrP->next.row = 4; + break; + case MULT8PLUS4: + rdrP->pass = MULT4PLUS2; + rdrP->next.row = 2; + break; + case MULT4PLUS2: + rdrP->pass = MULT2PLUS1; + rdrP->next.row = 1; + break; + case MULT2PLUS1: + /* An entry condition is that there be a row left to read, + but we have finished the last pass. That can't be: + */ + assert(false); + break; + } + } + /* Now that we know which row should be current, get its + pixels into the buffer. + */ + ppm_seek(rdrP->fileP, &rdrP->rasterPos, sizeof(rdrP->rasterPos), + rdrP->width, rdrP->maxval, 0, rdrP->next.row); +} + + + +static void +pixelReaderRead(pixelReader * const rdrP, + pixel * const pixelP, + bool * const eofP) { + + if (rdrP->nPixelsLeft == 0) + *eofP = TRUE; + else { + *eofP = FALSE; + + *pixelP = rdrP->curPixelRow[rdrP->next.col]; + + --rdrP->nPixelsLeft; + + /* Move one column to the right */ + ++rdrP->next.col; + + if (rdrP->next.col >= rdrP->width) { + /* That pushed us past the end of a row. */ + if (rdrP->nPixelsLeft > 0) { + /* Reset to the left edge ... */ + rdrP->next.col = 0; + + /* ... of the next row */ + if (!rdrP->interlace) + ++rdrP->next.row; + else + pixelReaderGotoNextInterlaceRow(rdrP); + + pixelReaderReadCurrentRow(rdrP); + } + } + } +} + + + +static pm_pixelcoord +pixelReaderNextCoord(pixelReader * const pixelReaderP) { + + return pixelReaderP->next; +} + + + +static void +gifNextPixel(pixelReader * const pixelReaderP, + pixval const inputMaxval, + gray ** const alpha, + gray const alphaThreshold, + struct cmap * const cmapP, + unsigned int * const colorIndexP, + bool * const eofP) { +/*---------------------------------------------------------------------------- + Return as *colorIndexP the colormap index of the next pixel supplied by + pixel reader 'pixelReaderP', using colormap *cmapP. + + Iff the reader is at the end of the image, return *eofP == TRUE + and nothing as *colorIndexP. + + 'alphaThreshold' is the gray level such that a pixel in the alpha + map whose value is less that that represents a transparent pixel + in the output. +-----------------------------------------------------------------------------*/ + pm_pixelcoord const coord = pixelReaderNextCoord(pixelReaderP); + + pixel pixel; + + pixelReaderRead(pixelReaderP, &pixel, eofP); + if (!*eofP) { + int colorindex; + + if (alpha && alpha[coord.row][coord.col] < alphaThreshold) + colorindex = cmapP->transparent; + else { + int presortColorindex; + + presortColorindex = ppm_lookupcolor(cmapP->cht, &pixel); + if (presortColorindex == -1) + presortColorindex = closestcolor(pixel, inputMaxval, cmapP); + colorindex = cmapP->perm[presortColorindex]; + } + *colorIndexP = colorindex; + } +} + + + +static void +write_transparent_color_index_extension(FILE *fp, const int Transparent) { +/*---------------------------------------------------------------------------- + Write out extension for transparent color index. +-----------------------------------------------------------------------------*/ + + fputc( '!', fp ); + fputc( 0xf9, fp ); + fputc( 4, fp ); + fputc( 1, fp ); + fputc( 0, fp ); + fputc( 0, fp ); + fputc( Transparent, fp ); + fputc( 0, fp ); +} + + + +static void +write_comment_extension(FILE *fp, const char comment[]) { +/*---------------------------------------------------------------------------- + Write out extension for a comment +-----------------------------------------------------------------------------*/ + char *segment; + + fputc('!', fp); /* Identifies an extension */ + fputc(0xfe, fp); /* Identifies a comment */ + + /* Write it out in segments no longer than 255 characters */ + for (segment = (char *) comment; + segment < comment+strlen(comment); + segment += 255) { + + const int length_this_segment = MIN(255, strlen(segment)); + + fputc(length_this_segment, fp); + + fwrite(segment, 1, length_this_segment, fp); + } + + fputc(0, fp); /* No more comment blocks in this extension */ +} + + + +/*************************************************************************** + * + * GIFCOMPR.C - GIF Image compression routines + * + * Lempel-Ziv compression based on 'compress'. GIF modifications by + * David Rowley (mgardi@watdcsu.waterloo.edu) + * + ***************************************************************************/ + +/* + * General DEFINEs + */ + +#define BITS 12 + +#define HSIZE 5003 /* 80% occupancy */ + +#ifdef NO_UCHAR + typedef char char_type; +#else /*NO_UCHAR*/ + typedef unsigned char char_type; +#endif /*NO_UCHAR*/ + +/* + * + * GIF Image compression - modified 'compress' + * + * Based on: compress.c - File compression ala IEEE Computer, June 1984. + * + * By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas) + * Jim McKie (decvax!mcvax!jim) + * Steve Davies (decvax!vax135!petsd!peora!srd) + * Ken Turkowski (decvax!decwrl!turtlevax!ken) + * James A. Woods (decvax!ihnp4!ames!jaw) + * Joe Orost (decvax!vax135!petsd!joe) + * + */ +#include <ctype.h> + +#define ARGVAL() (*++(*argv) || (--argc && *++argv)) + +static code_int const maxmaxcode = (code_int)1 << BITS; + /* should NEVER generate this code */ +#define MAXCODE(n_bits) (((code_int) 1 << (n_bits)) - 1) + +static long htab [HSIZE]; +static unsigned short codetab [HSIZE]; +#define HashTabOf(i) htab[i] +#define CodeTabOf(i) codetab[i] + +/* + * To save much memory, we overlay the table used by compress() with those + * used by decompress(). The tab_prefix table is the same size and type + * as the codetab. The tab_suffix table needs 2**BITS characters. We + * get this from the beginning of htab. The output stack uses the rest + * of htab, and contains characters. There is plenty of room for any + * possible stack (stack used to be 8000 characters). + */ + +#define tab_prefixof(i) CodeTabOf(i) +#define tab_suffixof(i) ((char_type*)(htab))[i] +#define de_stack ((char_type*)&tab_suffixof((code_int)1<<BITS)) + +static code_int free_ent = 0; /* first unused entry */ + +/* + * block compression parameters -- after all codes are used up, + * and compression rate changes, start over. + */ +static int clear_flg = 0; + +static int offset; +static long int in_count = 1; /* length of input */ +static long int out_count = 0; /* # of codes output (for debugging) */ + +/* + * compress stdin to stdout + * + * Algorithm: use open addressing double hashing (no chaining) on the + * prefix code / next character combination. We do a variant of Knuth's + * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime + * secondary probe. Here, the modular division first probe is gives way + * to a faster exclusive-or manipulation. Also do block compression with + * an adaptive reset, whereby the code table is cleared when the compression + * ratio decreases, but after the table fills. The variable-length output + * codes are re-sized at this point, and a special CLEAR code is generated + * for the decompressor. Late addition: construct the table according to + * file size for noticeable speed improvement on small files. Please direct + * questions about this implementation to ames!jaw. + */ + +static int ClearCode; +static int EOFCode; + +/*************************************************************************** +* BYTE OUTPUTTER +***************************************************************************/ + +typedef struct { + FILE * fileP; /* The file to which to output */ + unsigned int count; + /* Number of bytes so far in the current data block */ + unsigned char buffer[256]; + /* The current data block, under construction */ +} byteBuffer; + + + +static byteBuffer * +byteBuffer_create(FILE * const fileP) { + + byteBuffer * byteBufferP; + + MALLOCVAR_NOFAIL(byteBufferP); + + byteBufferP->fileP = fileP; + byteBufferP->count = 0; + + return byteBufferP; +} + + + +static void +byteBuffer_destroy(byteBuffer * const byteBufferP) { + + free(byteBufferP); +} + + + +static void +byteBuffer_flush(byteBuffer * const byteBufferP) { +/*---------------------------------------------------------------------------- + Write the current data block to the output file, then reset the current + data block to empty. +-----------------------------------------------------------------------------*/ + if (byteBufferP->count > 0 ) { + if (verbose) + pm_message("Writing %u byte block", byteBufferP->count); + fputc(byteBufferP->count, byteBufferP->fileP); + fwrite(byteBufferP->buffer, 1, byteBufferP->count, byteBufferP->fileP); + byteBufferP->count = 0; + } +} + + + +static void +byteBuffer_flushFile(byteBuffer * const byteBufferP) { + + fflush(byteBufferP->fileP); + + if (ferror(byteBufferP->fileP)) + pm_error("error writing output file"); +} + + + +static void +byteBuffer_out(byteBuffer * const byteBufferP, + unsigned char const c) { +/*---------------------------------------------------------------------------- + Add a byte to the end of the current data block, and if it is now 254 + characters, flush the data block to the output file. +-----------------------------------------------------------------------------*/ + byteBufferP->buffer[byteBufferP->count++] = c; + if (byteBufferP->count >= 254) + byteBuffer_flush(byteBufferP); +} + + + +struct gif_dest { + /* This structure controls output of uncompressed GIF raster */ + + byteBuffer * byteBufferP; /* Where the full bytes go */ + + /* State for packing variable-width codes into a bitstream */ + int n_bits; /* current number of bits/code */ + int maxcode; /* maximum code, given n_bits */ + int cur_accum; /* holds bits not yet output */ + int cur_bits; /* # of bits in cur_accum */ + + /* State for GIF code assignment */ + int ClearCode; /* clear code (doesn't change) */ + int EOFCode; /* EOF code (ditto) */ + int code_counter; /* counts output symbols */ +}; + + + +static unsigned long const masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, + 0x001F, 0x003F, 0x007F, 0x00FF, + 0x01FF, 0x03FF, 0x07FF, 0x0FFF, + 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; + +typedef struct { + byteBuffer * byteBufferP; + unsigned int initBits; + unsigned int n_bits; /* number of bits/code */ + code_int maxcode; /* maximum code, given n_bits */ + unsigned long curAccum; + int curBits; +} codeBuffer; + + + +static codeBuffer * +codeBuffer_create(FILE * const ofP, + unsigned int const initBits) { + + codeBuffer * codeBufferP; + + MALLOCVAR_NOFAIL(codeBufferP); + + codeBufferP->initBits = initBits; + codeBufferP->n_bits = codeBufferP->initBits; + codeBufferP->maxcode = MAXCODE(codeBufferP->n_bits); + codeBufferP->byteBufferP = byteBuffer_create(ofP); + codeBufferP->curAccum = 0; + codeBufferP->curBits = 0; + + return codeBufferP; +} + + + +static void +codeBuffer_destroy(codeBuffer * const codeBufferP) { + + byteBuffer_destroy(codeBufferP->byteBufferP); + + free(codeBufferP); +} + + + +static void +codeBuffer_output(codeBuffer * const codeBufferP, + code_int const code) { +/*---------------------------------------------------------------------------- + Output one GIF code to the file, through the code buffer. + + The code is represented as n_bits bits in the file -- the lower + n_bits bits of 'code'. + + If the code is EOF, flush the code buffer to the file. + + In some cases, change n_bits and recalculate maxcode to go with it. +-----------------------------------------------------------------------------*/ + /* + Algorithm: + Maintain a BITS character long buffer (so that 8 codes will + fit in it exactly). Use the VAX insv instruction to insert each + code in turn. When the buffer fills up empty it and start over. + */ + + codeBufferP->curAccum &= masks[codeBufferP->curBits]; + + if (codeBufferP->curBits > 0) + codeBufferP->curAccum |= ((long)code << codeBufferP->curBits); + else + codeBufferP->curAccum = code; + + codeBufferP->curBits += codeBufferP->n_bits; + + while (codeBufferP->curBits >= 8) { + byteBuffer_out(codeBufferP->byteBufferP, + codeBufferP->curAccum & 0xff); + codeBufferP->curAccum >>= 8; + codeBufferP->curBits -= 8; + } + + if (clear_flg) { + codeBufferP->n_bits = codeBufferP->initBits; + codeBufferP->maxcode = MAXCODE(codeBufferP->n_bits); + clear_flg = 0; + } else if (free_ent > codeBufferP->maxcode) { + /* The next entry is going to be too big for the code size, so + increase it, if possible. + */ + ++codeBufferP->n_bits; + if (codeBufferP->n_bits == BITS) + codeBufferP->maxcode = maxmaxcode; + else + codeBufferP->maxcode = MAXCODE(codeBufferP->n_bits); + } + + if (code == EOFCode) { + /* We're at EOF. Output the possible partial byte in the buffer */ + if (codeBufferP->curBits > 0) { + byteBuffer_out(codeBufferP->byteBufferP, + codeBufferP->curAccum & 0xff); + codeBufferP->curBits = 0; + } + byteBuffer_flush(codeBufferP->byteBufferP); + + byteBuffer_flushFile(codeBufferP->byteBufferP); + } +} + + + +static void +cl_hash(long const hsize) { + /* reset code table */ + + long const m1 = -1; + + long * htab_p; + long i; + + htab_p = htab + hsize; /* initial value */ + + i = hsize - 16; + do { /* might use Sys V memset(3) here */ + *(htab_p-16) = m1; + *(htab_p-15) = m1; + *(htab_p-14) = m1; + *(htab_p-13) = m1; + *(htab_p-12) = m1; + *(htab_p-11) = m1; + *(htab_p-10) = m1; + *(htab_p-9) = m1; + *(htab_p-8) = m1; + *(htab_p-7) = m1; + *(htab_p-6) = m1; + *(htab_p-5) = m1; + *(htab_p-4) = m1; + *(htab_p-3) = m1; + *(htab_p-2) = m1; + *(htab_p-1) = m1; + htab_p -= 16; + } while ((i -= 16) >= 0); + + for (i += 16; i > 0; --i) + *--htab_p = m1; +} + + + +static void +cl_block(codeBuffer * const codeBufferP) { +/*---------------------------------------------------------------------------- + Clear out the hash table +-----------------------------------------------------------------------------*/ + cl_hash(HSIZE); + free_ent = ClearCode + 2; + clear_flg = 1; + + codeBuffer_output(codeBufferP, (code_int)ClearCode); +} + + + +static void +writeRasterLzw(pixelReader * const pixelReaderP, + pixval const inputMaxval, + gray ** const alpha, + gray const alphaMaxval, + struct cmap * const cmapP, + int const initBits, + FILE * const ofP) { +/*---------------------------------------------------------------------------- + Write the raster to file 'ofP'. + + The raster to write is 'pixels', which has maxval 'inputMaxval', + modified by alpha mask 'alpha', which has maxval 'alphaMaxval'. + + Use the colormap 'cmapP' to generate the raster ('pixels' is + composed of RGB samples; the GIF raster is colormap indices). + + Write the raster using LZW compression. +-----------------------------------------------------------------------------*/ + gray const alpha_threshold = (alphaMaxval + 1) / 2; + /* gray levels below this in the alpha mask indicate transparent + pixels in the output image. + */ + code_int ent; + code_int disp; + int hshift; + bool eof; + codeBuffer * codeBufferP; + unsigned int colorIndex; + + codeBufferP = codeBuffer_create(ofP, initBits); + + /* + * Set up the necessary values + */ + offset = 0; + out_count = 0; + clear_flg = 0; + in_count = 1; + + ClearCode = (1 << (initBits - 1)); + EOFCode = ClearCode + 1; + free_ent = ClearCode + 2; + + gifNextPixel(pixelReaderP, inputMaxval, alpha, alpha_threshold, cmapP, + &colorIndex, &eof); + ent = colorIndex; + + { + long fcode; + hshift = 0; + for (fcode = HSIZE; fcode < 65536L; fcode *= 2L) + ++hshift; + hshift = 8 - hshift; /* set hash code range bound */ + } + cl_hash(HSIZE); /* clear hash table */ + + codeBuffer_output(codeBufferP, (code_int)ClearCode); + + while (!eof) { + unsigned int gifpixel; + /* The value for the pixel in the GIF image. I.e. the colormap + index. + */ + gifNextPixel(pixelReaderP, inputMaxval, alpha, alpha_threshold, cmapP, + &gifpixel, &eof); + if (!eof) { + long const fcode = (long) (((long) gifpixel << BITS) + ent); + code_int i; + /* xor hashing */ + + ++in_count; + + i = (((code_int)gifpixel << hshift) ^ ent); + + if (HashTabOf (i) == fcode) { + ent = CodeTabOf (i); + continue; + } else if ((long)HashTabOf(i) < 0) /* empty slot */ + goto nomatch; + disp = HSIZE - i; /* secondary hash (after G. Knott) */ + if (i == 0) + disp = 1; + probe: + if ((i -= disp) < 0) + i += HSIZE; + + if (HashTabOf(i) == fcode) { + ent = CodeTabOf(i); + continue; + } + if ((long)HashTabOf(i) > 0) + goto probe; + nomatch: + codeBuffer_output(codeBufferP, (code_int)ent); + ++out_count; + ent = gifpixel; + if (free_ent < maxmaxcode) { + CodeTabOf(i) = free_ent++; /* code -> hashtable */ + HashTabOf(i) = fcode; + } else + cl_block(codeBufferP); + } + } + /* Put out the final code. */ + codeBuffer_output(codeBufferP, (code_int)ent); + ++out_count; + codeBuffer_output(codeBufferP, (code_int) EOFCode); + + codeBuffer_destroy(codeBufferP); +} + + + +/* Routine to convert variable-width codes into a byte stream */ + +static void +outputUncompressed(struct gif_dest * const dinfoP, + int const code) { + + /* Emit a code of n_bits bits */ + /* Uses cur_accum and cur_bits to reblock into 8-bit bytes */ + dinfoP->cur_accum |= ((int) code) << dinfoP->cur_bits; + dinfoP->cur_bits += dinfoP->n_bits; + + while (dinfoP->cur_bits >= 8) { + byteBuffer_out(dinfoP->byteBufferP, dinfoP->cur_accum & 0xFF); + dinfoP->cur_accum >>= 8; + dinfoP->cur_bits -= 8; + } +} + + +static void +writeRasterUncompressedInit(FILE * const ofP, + struct gif_dest * const dinfoP, + int const i_bits) { +/*---------------------------------------------------------------------------- + Initialize pseudo-compressor +-----------------------------------------------------------------------------*/ + + /* init all the state variables */ + dinfoP->n_bits = i_bits; + dinfoP->maxcode = MAXCODE(dinfoP->n_bits); + dinfoP->ClearCode = (1 << (i_bits - 1)); + dinfoP->EOFCode = dinfoP->ClearCode + 1; + dinfoP->code_counter = dinfoP->ClearCode + 2; + /* init output buffering vars */ + dinfoP->byteBufferP = byteBuffer_create(ofP); + dinfoP->cur_accum = 0; + dinfoP->cur_bits = 0; + /* GIF specifies an initial Clear code */ + outputUncompressed(dinfoP, dinfoP->ClearCode); +} + + + +static void +writeRasterUncompressedPixel(struct gif_dest * const dinfoP, + unsigned int const colormapIndex) { +/*---------------------------------------------------------------------------- + "Compress" one pixel value and output it as a symbol. + + 'colormapIndex' must be less than dinfoP->n_bits wide. +-----------------------------------------------------------------------------*/ + assert(colormapIndex >> dinfoP->n_bits == 0); + + outputUncompressed(dinfoP, colormapIndex); + /* Issue Clear codes often enough to keep the reader from ratcheting up + * its symbol size. + */ + if (dinfoP->code_counter < dinfoP->maxcode) { + ++dinfoP->code_counter; + } else { + outputUncompressed(dinfoP, dinfoP->ClearCode); + dinfoP->code_counter = dinfoP->ClearCode + 2; /* reset the counter */ + } +} + + + +static void +writeRasterUncompressedTerm(struct gif_dest * const dinfoP) { + + outputUncompressed(dinfoP, dinfoP->EOFCode); + + if (dinfoP->cur_bits > 0) + byteBuffer_out(dinfoP->byteBufferP, dinfoP->cur_accum & 0xFF); + + byteBuffer_flush(dinfoP->byteBufferP); + + byteBuffer_destroy(dinfoP->byteBufferP); +} + + + +static void +writeRasterUncompressed(pixelReader * const pixelReaderP, + pixval const inputMaxval, + gray ** const alpha, + gray const alphaMaxval, + struct cmap * const cmapP, + int const initBits, + FILE * const ofP) { +/*---------------------------------------------------------------------------- + Write the raster to file 'ofP'. + + Same as writeRasterLzw(), except written out one code per + pixel (plus some clear codes), so no compression. And no use + of the LZW patent. +-----------------------------------------------------------------------------*/ + gray const alphaThreshold = (alphaMaxval + 1) / 2; + /* gray levels below this in the alpha mask indicate transparent + pixels in the output image. + */ + bool eof; + struct gif_dest gifDest; + + writeRasterUncompressedInit(ofP, &gifDest, initBits); + + eof = FALSE; + while (!eof) { + unsigned int gifpixel; + /* The value for the pixel in the GIF image. I.e. the colormap + index. + */ + gifNextPixel(pixelReaderP, inputMaxval, alpha, alphaThreshold, cmapP, + &gifpixel, &eof); + if (!eof) + writeRasterUncompressedPixel(&gifDest, gifpixel); + } + writeRasterUncompressedTerm(&gifDest); +} + + + +/****************************************************************************** + * + * GIF Specific routines + * + *****************************************************************************/ + +static void +writeGifHeader(FILE * const fp, + int const Width, int const Height, + int const GInterlace, int const Background, + int const BitsPerPixel, struct cmap * const cmapP, + const char comment[]) { + + int B; + int const Resolution = BitsPerPixel; + int const ColorMapSize = 1 << BitsPerPixel; + + /* Write the Magic header */ + if (cmapP->transparent != -1 || comment) + fwrite("GIF89a", 1, 6, fp); + else + fwrite("GIF87a", 1, 6, fp); + + /* Write out the screen width and height */ + Putword( Width, fp ); + Putword( Height, fp ); + + /* Indicate that there is a global color map */ + B = 0x80; /* Yes, there is a color map */ + + /* OR in the resolution */ + B |= (Resolution - 1) << 4; + + /* OR in the Bits per Pixel */ + B |= (BitsPerPixel - 1); + + /* Write it out */ + fputc( B, fp ); + + /* Write out the Background color */ + fputc( Background, fp ); + + /* Byte of 0's (future expansion) */ + fputc( 0, fp ); + + { + /* Write out the Global Color Map */ + /* Note that the Global Color Map is always a power of two colors + in size, but *cmapP could be smaller than that. So we pad with + black. + */ + int i; + for ( i=0; i < ColorMapSize; ++i ) { + if ( i < cmapP->cmapsize ) { + fputc( cmapP->red[i], fp ); + fputc( cmapP->green[i], fp ); + fputc( cmapP->blue[i], fp ); + } else { + fputc( 0, fp ); + fputc( 0, fp ); + fputc( 0, fp ); + } + } + } + + if ( cmapP->transparent >= 0 ) + write_transparent_color_index_extension(fp, cmapP->transparent); + + if ( comment ) + write_comment_extension(fp, comment); +} + + + +static void +writeImageHeader(FILE * const ofP, + unsigned int const leftOffset, + unsigned int const topOffset, + unsigned int const gWidth, + unsigned int const gHeight, + unsigned int const gInterlace, + unsigned int const initCodeSize) { + + Putword(leftOffset, ofP); + Putword(topOffset, ofP); + Putword(gWidth, ofP); + Putword(gHeight, ofP); + + /* Write out whether or not the image is interlaced */ + if (gInterlace) + fputc(0x40, ofP); + else + fputc(0x00, ofP); + + /* Write out the initial code size */ + fputc(initCodeSize, ofP); +} + + + +static void +gifEncode(FILE * const ofP, + FILE * const ifP, + int const gWidth, + int const gHeight, + pixval const inputMaxval, + int const inputFormat, + pm_filepos const rasterPos, + gray ** const alpha, + gray const alphaMaxval, + int const gInterlace, + int const background, + int const bitsPerPixel, + struct cmap * const cmapP, + char const comment[], + bool const nolzw) { + + unsigned int const leftOffset = 0; + unsigned int const topOffset = 0; + + unsigned int const initCodeSize = bitsPerPixel <= 1 ? 2 : bitsPerPixel; + /* The initial code size */ + + pixelReader * pixelReaderP; + + writeGifHeader(ofP, gWidth, gHeight, gInterlace, background, + bitsPerPixel, cmapP, comment); + + /* Write an Image separator */ + fputc(',', ofP); + + writeImageHeader(ofP, leftOffset, topOffset, gWidth, gHeight, gInterlace, + initCodeSize); + + pixelReaderCreate(ifP, gWidth, gHeight, inputMaxval, inputFormat, + rasterPos, gInterlace, &pixelReaderP); + + /* Write the actual raster */ + if (nolzw) + writeRasterUncompressed(pixelReaderP, + inputMaxval, alpha, alphaMaxval, cmapP, + initCodeSize + 1, ofP); + else + writeRasterLzw(pixelReaderP, + inputMaxval, alpha, alphaMaxval, cmapP, + initCodeSize + 1, ofP); + + pixelReaderDestroy(pixelReaderP); + + /* Write out a zero length data block (to end the series) */ + fputc(0, ofP); + + /* Write the GIF file terminator */ + fputc(';', ofP); +} + + + +static int +compute_transparent(const char colorarg[], + struct cmap * const cmapP) { +/*---------------------------------------------------------------------------- + Figure out the color index (index into the colormap) of the color + that is to be transparent in the GIF. + + colorarg[] is the string that specifies the color the user wants to + be transparent (e.g. "red", "#fefefe"). Its maxval is the maxval + of the colormap. 'cmap' is the full colormap except that its + 'transparent' component isn't valid. + + colorarg[] is a standard Netpbm color specification, except that + may have a "=" prefix, which means it specifies a particular exact + color, as opposed to without the "=", which means "the color that + is closest to this and actually in the image." + + Return -1 if colorarg[] specifies an exact color and that color is not + in the image. Also issue an informational message. +-----------------------------------------------------------------------------*/ + int retval; + + const char *colorspec; + bool exact; + int presort_colorindex; + pixel transcolor; + + if (colorarg[0] == '=') { + colorspec = &colorarg[1]; + exact = TRUE; + } else { + colorspec = colorarg; + exact = FALSE; + } + + transcolor = ppm_parsecolor((char*)colorspec, cmapP->maxval); + presort_colorindex = ppm_lookupcolor(cmapP->cht, &transcolor); + + if (presort_colorindex != -1) + retval = cmapP->perm[presort_colorindex]; + else if (!exact) + retval = cmapP->perm[closestcolor(transcolor, cmapP->maxval, cmapP)]; + else { + retval = -1; + pm_message( + "Warning: specified transparent color does not occur in image."); + } + return retval; +} + + + +static void +sort_colormap(int const sort, struct cmap * const cmapP) { +/*---------------------------------------------------------------------------- + Sort (in place) the colormap *cmapP. + + Create the perm[] and permi[] mappings for the colormap. + + 'sort' is logical: true means to sort the colormap by red intensity, + then by green intensity, then by blue intensity. False means a null + sort -- leave it in the same order in which we found it. +-----------------------------------------------------------------------------*/ + int * const Red = cmapP->red; + int * const Blue = cmapP->blue; + int * const Green = cmapP->green; + int * const perm = cmapP->perm; + int * const permi = cmapP->permi; + unsigned int const cmapsize = cmapP->cmapsize; + + int i; + + for (i=0; i < cmapsize; i++) + permi[i] = i; + + if (sort) { + pm_message("sorting colormap"); + for (i=0; i < cmapsize; i++) { + int j; + for (j=i+1; j < cmapsize; j++) + if (((Red[i]*MAXCMAPSIZE)+Green[i])*MAXCMAPSIZE+Blue[i] > + ((Red[j]*MAXCMAPSIZE)+Green[j])*MAXCMAPSIZE+Blue[j]) { + int tmp; + + tmp=permi[i]; permi[i]=permi[j]; permi[j]=tmp; + tmp=Red[i]; Red[i]=Red[j]; Red[j]=tmp; + tmp=Green[i]; Green[i]=Green[j]; Green[j]=tmp; + tmp=Blue[i]; Blue[i]=Blue[j]; Blue[j]=tmp; } } + } + + for (i=0; i < cmapsize; i++) + perm[permi[i]] = i; +} + + + +static void +normalize_to_255(colorhist_vector const chv, struct cmap * const cmapP) { +/*---------------------------------------------------------------------------- + With a PPM color histogram vector 'chv' as input, produce a colormap + of integers 0-255 as output in *cmapP. +-----------------------------------------------------------------------------*/ + int i; + pixval const maxval = cmapP->maxval; + + if ( maxval != 255 ) + pm_message( + "maxval is not 255 - automatically rescaling colors" ); + + for ( i = 0; i < cmapP->cmapsize; ++i ) { + if ( maxval == 255 ) { + cmapP->red[i] = (int) PPM_GETR( chv[i].color ); + cmapP->green[i] = (int) PPM_GETG( chv[i].color ); + cmapP->blue[i] = (int) PPM_GETB( chv[i].color ); + } else { + cmapP->red[i] = (int) PPM_GETR( chv[i].color ) * 255 / maxval; + cmapP->green[i] = (int) PPM_GETG( chv[i].color ) * 255 / maxval; + cmapP->blue[i] = (int) PPM_GETB( chv[i].color ) * 255 / maxval; + } + } +} + + + +static void add_to_colormap(struct cmap * const cmapP, + const char * const colorspec, + int * const new_indexP) { +/*---------------------------------------------------------------------------- + Add a new entry to the colormap. Make the color that specified by + 'colorspec', and return the index of the new entry as *new_indexP. + + 'colorspec' is a color specification given by the user, e.g. + "red" or "rgb:ff/03.0d". The maxval for this color specification is + that for the colormap *cmapP. +-----------------------------------------------------------------------------*/ + pixel const transcolor = ppm_parsecolor((char*)colorspec, cmapP->maxval); + + *new_indexP = cmapP->cmapsize++; + + cmapP->red[*new_indexP] = PPM_GETR(transcolor); + cmapP->green[*new_indexP] = PPM_GETG(transcolor); + cmapP->blue[*new_indexP] = PPM_GETB(transcolor); +} + + + +static void +colormapFromFile(char const filespec[], + unsigned int const maxcolors, + colorhist_vector * const chvP, + pixval * const maxvalP, + unsigned int * const colorsP) { +/*---------------------------------------------------------------------------- + Read a colormap from the PPM file filespec[]. Return the color histogram + vector (which is practically a colormap) of the input image as *cvhP + and the maxval for that histogram as *maxvalP. +-----------------------------------------------------------------------------*/ + FILE * mapfileP; + int cols, rows; + pixel ** colormapPpm; + int colors; + + mapfileP = pm_openr(filespec); + colormapPpm = ppm_readppm(mapfileP, &cols, &rows, maxvalP); + pm_close(mapfileP); + + pm_message("computing other colormap ..."); + *chvP = ppm_computecolorhist(colormapPpm, cols, rows, maxcolors, &colors); + + *colorsP = colors; + + ppm_freearray(colormapPpm, rows); +} + + + +static void +get_alpha(const char * const alpha_filespec, int const cols, int const rows, + gray *** const alphaP, gray * const maxvalP) { + + if (alpha_filespec) { + int alpha_cols, alpha_rows; + *alphaP = pgm_readpgm(pm_openr(alpha_filespec), + &alpha_cols, &alpha_rows, maxvalP); + if (alpha_cols != cols || alpha_rows != rows) + pm_error("alpha mask is not the same dimensions as the " + "input file (alpha is %dW x %dH; image is %dW x %dH)", + alpha_cols, alpha_rows, cols, rows); + } else + *alphaP = NULL; +} + + + +static void +computePpmColormap(FILE * const ifP, + unsigned int const cols, + unsigned int const rows, + pixval const maxval, + int const format, + bool const haveAlpha, + const char * const mapfile, + colorhist_vector * const chvP, + colorhash_table * const chtP, + pixval * const colormapMaxvalP, + unsigned int * const colorsP) { +/*---------------------------------------------------------------------------- + Compute a colormap, PPM style, for the image on file 'ifP', which + is positioned to the raster and is 'cols' by 'rows' with maxval + 'maxval' and format 'format'. If 'mapfile' is non-null, Use the + colors in that (PPM) file for the color map instead of the colors + in 'ifP'. + + Return the colormap as *chvP and *chtP. Return the maxval for that + colormap as *colormapMaxvalP. + + While we're at it, count the colors and validate that there aren't + too many. Return the count as *colorsP. In determining if there are + too many, allow one slot for a fake transparency color if 'have_alpha' + is true. If there are too many, issue an error message and abort the + program. +-----------------------------------------------------------------------------*/ + unsigned int maxcolors; + /* The most colors we can tolerate in the image. If we have + our own made-up entry in the colormap for transparency, it + isn't included in this count. + */ + + if (haveAlpha) + maxcolors = MAXCMAPSIZE - 1; + else + maxcolors = MAXCMAPSIZE; + + if (mapfile) { + /* Read the colormap from a separate colormap file. */ + colormapFromFile(mapfile, maxcolors, chvP, colormapMaxvalP, + colorsP); + } else { + /* Figure out the color map from the input file */ + int colors; + pm_message("computing colormap..."); + *chvP = ppm_computecolorhist2(ifP, cols, rows, maxval, format, + maxcolors, &colors); + *colorsP = colors; + *colormapMaxvalP = maxval; + } + + if (*chvP == NULL) + pm_error("too many colors - try doing a 'pnmquant %d'", maxcolors); + pm_message("%d colors found", *colorsP); + + /* And make a hash table for fast lookup. */ + *chtP = ppm_colorhisttocolorhash(*chvP, *colorsP); +} + + + +int +main(int argc, char *argv[]) { + struct cmdlineInfo cmdline; + FILE * ifP; + int rows, cols; + pixval inputMaxval; + int inputFormat; + int BitsPerPixel; + gray ** alpha; /* The supplied alpha mask; NULL if none */ + gray alpha_maxval; /* Maxval for 'alpha' */ + pm_filepos rasterPos; + + struct cmap cmap; + /* The colormap, with all its accessories */ + colorhist_vector chv; + int fake_transparent; + /* colormap index of the fake transparency color we're using to + implement the alpha mask. Undefined if we're not doing an alpha + mask. + */ + + ppm_init( &argc, argv ); + + parseCommandLine(argc, argv, &cmdline); + + verbose = cmdline.verbose; + + ifP = pm_openr_seekable(cmdline.input_filespec); + + ppm_readppminit(ifP, &cols, &rows, &inputMaxval, &inputFormat); + + pm_tell2(ifP, &rasterPos, sizeof(rasterPos)); + + get_alpha(cmdline.alpha_filespec, cols, rows, &alpha, &alpha_maxval); + + computePpmColormap(ifP, cols, rows, inputMaxval, inputFormat, + (alpha != NULL), cmdline.mapfile, + &chv, &cmap.cht, &cmap.maxval, &cmap.cmapsize); + + /* Now turn the ppm colormap into the appropriate GIF colormap. */ + + normalize_to_255(chv, &cmap); + + ppm_freecolorhist(chv); + + if (alpha) { + /* Add a fake entry to the end of the colormap for transparency. + Make its color black. + */ + add_to_colormap(&cmap, cmdline.alphacolor, &fake_transparent); + } + sort_colormap(cmdline.sort, &cmap); + + BitsPerPixel = pm_maxvaltobits(cmap.cmapsize-1); + + if (alpha) { + cmap.transparent = cmap.perm[fake_transparent]; + } else { + if (cmdline.transparent) + cmap.transparent = + compute_transparent(cmdline.transparent, &cmap); + else + cmap.transparent = -1; + } + + /* All set, let's do it. */ + gifEncode(stdout, ifP, cols, rows, inputMaxval, inputFormat, rasterPos, + alpha, alpha_maxval, + cmdline.interlace, 0, BitsPerPixel, &cmap, cmdline.comment, + cmdline.nolzw); + + if (alpha) + pgm_freearray(alpha, rows); + + pm_close(ifP); + pm_close(stdout); + + return 0; +} diff --git a/converter/ppm/ppmtoicr.c b/converter/ppm/ppmtoicr.c new file mode 100644 index 00000000..feca0c18 --- /dev/null +++ b/converter/ppm/ppmtoicr.c @@ -0,0 +1,320 @@ +/* ppmtoicr.c - convert a portable pixmap to NCSA ICR protocol +** +** Copyright (C) 1990 by Kanthan Pillay (svpillay@Princeton.EDU) +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "ppm.h" + +#define MAXCOLORS 256 +#define CLUTCOLORS 768 + +static int colorstobpp ARGS(( int colors )); +static int GetPixel ARGS(( int x, int y )); +static int rleit ARGS(( char* buf, char* bufto, int len )); + +static pixel** pixels; +static colorhash_table cht; +static char* testimage; + +int +main(argc, argv) +int argc; +char* argv[]; +{ + FILE* ifp; + int argn, rows, cols, colors, i, j, BitsPerPixel, newxsize; + pixval maxval; + colorhist_vector chv; + char rgb[CLUTCOLORS]; + const char* windowname; + char* thischar; + char* thisline; + char* space; + register unsigned char c; + register char* p; + int display, expand; + int rleflag, winflag; + const char* const usage = "[-windowname windowname] [-expand expand] [-display display] [-rle] [ppmfile]"; + + + ppm_init( &argc, argv ); + + argn = 1; + windowname = "untitled"; + winflag = 0; + expand = 1; + display = 0; + rleflag = 0; + + while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' ) + { + if ( pm_keymatch(argv[argn],"-windowname",2) && argn + 1 < argc ) + { + ++argn; + windowname = argv[argn]; + winflag = 1; + } + else if ( pm_keymatch(argv[argn],"-expand",2) && argn + 1 < argc ) + { + ++argn; + if ( sscanf( argv[argn], "%d",&expand ) != 1 ) + pm_usage( usage ); + } + else if ( pm_keymatch(argv[argn],"-display",2) && argn + 1 < argc ) + { + ++argn; + if ( sscanf( argv[argn], "%d",&display ) != 1 ) + pm_usage( usage ); + } + else if ( pm_keymatch(argv[argn],"-rle",2) ) + rleflag = 1; + else if ( pm_keymatch(argv[argn],"-norle",2) ) + rleflag = 0; + else + pm_usage( usage ); + } + + if ( argn < argc ) + { + ifp = pm_openr( argv[argn] ); + if ( ! winflag ) + windowname = argv[argn]; + ++argn; + } + else + ifp = stdin; + + if ( argn != argc ) + pm_usage( usage ); + + pixels = ppm_readppm( ifp, &cols, &rows, &maxval ); + + pm_close( ifp ); + + for (i = 0; i < CLUTCOLORS; i++) + rgb[i] = 0; + + /* Figure out the colormap. */ + pm_message("computing colormap..." ); + chv = ppm_computecolorhist(pixels, cols, rows, MAXCOLORS, &colors); + if (chv == (colorhist_vector) 0) + pm_error( "too many colors - try doing a 'pnmquant %d'", MAXCOLORS ); + pm_message("%d colors found", colors ); + + /* Turn the ppm colormap into an ICR colormap. */ + if (maxval > 255) + pm_message( + "maxval is not 255 - automatically rescaling colors" ); + for (i = 0; i < colors; i++) + { + j = (3 * i); + if (maxval == 255) + { + rgb[j] = PPM_GETR(chv[i].color) ; + j++; + rgb[j] = PPM_GETG(chv[i].color) ; + j++; + rgb[j] = PPM_GETB(chv[i].color) ; + } + else + { + rgb[j] = (int) PPM_GETR(chv[i].color) * 255 / maxval; + j++; + rgb[j] = (int) PPM_GETG(chv[i].color) * 255 / maxval; + j++; + rgb[j] = (int) PPM_GETB(chv[i].color) * 255 / maxval; + } + } + BitsPerPixel = colorstobpp(colors); + + /* And make a hash table for fast lookup. */ + cht = ppm_colorhisttocolorhash(chv, colors); + ppm_freecolorhist(chv); + + + /************** Create a new window using ICR protocol *********/ + /* Format is "ESC^W;left;top;width;height;display;windowname" */ + + pm_message("creating window %s ...", windowname ); + (void)printf("\033^W;%d;%d;%d;%d;%d;%s^",0,0,cols*expand,rows*expand,display,windowname); + fflush(stdout); + + + /****************** Download the colormap. ********************/ + pm_message("downloading colormap for %s ...", windowname ); + + (void)printf("\033^M;%d;%d;%d;%s^",0,MAXCOLORS,CLUTCOLORS,windowname); + thischar = rgb; + for (j=0; j<CLUTCOLORS; j++) { + c = *thischar++; + if (c > 31 && c < 123 ) { /* printable ASCII */ + putchar(c); + } + else { + putchar((c>>6)+123); /* non-printable, so encode it */ + putchar((c & 0x3f) + 32); + } + } + fflush(stdout); + + /**************** send out picture *************************/ + /* Protocol's RLE scheme is quicker but buggy */ + + if (rleflag) { + pm_message("sending run-length encoded picture data ..." ); + testimage = (char*) malloc(rows*cols); + p = testimage; + for (i=0; i<rows; i++) + for (j=0; j<cols; j++) + *p++ = GetPixel(j,i); + space = (char*) malloc(rows*3); + thisline = testimage; + for (i = 0; i < rows; i++) { + newxsize = rleit(thisline,space,cols); + thisline += cols; /* increment to next line */ + (void)printf("\033^R;%d;%d;%d;%d;%s^",0,i*expand,expand,newxsize,windowname); + thischar = space; + for (j=0; j< newxsize; j++) { + c= *thischar++; /*get byte to send */ + if (c>31 && c <123) { + putchar(c); + } + else { + putchar((c>>6) + 123); + putchar((c & 0x3f) + 32); + } + } + fflush(stdout); + } + free(space); + exit(0); + } + + /* Otherwise, send out uncompressed pixel data via the slow method */ + + else { + pm_message("sending picture data ..." ); + for (i = 0; i < rows; i++) { + (void)printf("\033^P;%d;%d;%d;%d;%s^",0,i*expand,expand,cols,windowname); + for (j = 0; j < cols; j++) { + c = GetPixel(j,i); + if (c > 31 && c < 123) { + putchar(c); + } + else { + putchar((c>>6)+123); + putchar((c & 0x3f) + 32); + } + } + } + fflush(stdout); + exit(0); + } + } + +static int +colorstobpp(colors) +int colors; + { + int bpp; + + if (colors <= 2) + bpp = 1; + else if (colors <= 4) + bpp = 2; + else if (colors <= 8) + bpp = 3; + else if (colors <= 16) + bpp = 4; + else if (colors <= 32) + bpp = 5; + else if (colors <= 64) + bpp = 6; + else if (colors <= 128) + bpp = 7; + else if (colors <= 256) + bpp = 8; + else + pm_error("can't happen" ); + return bpp; + } + +static int +GetPixel(x, y) +int x, y; + { + int color; + + color = ppm_lookupcolor(cht, &pixels[y][x]); + return color; + } + + +/* rleit compress with run length encoding as per NCSA's documentation */ + +static int +rleit(buf,bufto,len) + char* buf; + char* bufto; + int len; + { + register char* p; + register char* q; + register char* cfoll; + register char* clead; + char* begp; + int i; + + p = buf; + cfoll = bufto; + clead = cfoll + 1; + + begp = p; + while (len > 0 ) { /* encode until gone */ + + q = p + 1; + i = len-1; + while (*p == *q && i+120 > len && i) { + q++; + i--; + } + + if (q > p +2) { /* three in a row */ + if (p > begp) { + *cfoll = p - begp; + cfoll = clead; + } + *cfoll++ = 128 | (q-p); /*len of seq*/ + *cfoll++ = *p; /* char of seq */ + len -= q-p; /* subtract len of seq */ + p = q; + clead = cfoll+1; + begp = p; + } + else { + *clead++ = *p++; /* copy one char */ + len--; + if (p>begp + 120) { + *cfoll = p - begp; + cfoll = clead++; + begp = p; + } + } + } + +/* fillin last bytecount */ + + if (p>begp) + *cfoll = (p - begp); + else + clead--; + + return((int) (clead-bufto)); /*how many stored as encoded */ +} diff --git a/converter/ppm/ppmtoilbm.c b/converter/ppm/ppmtoilbm.c new file mode 100644 index 00000000..6c04c9be --- /dev/null +++ b/converter/ppm/ppmtoilbm.c @@ -0,0 +1,2362 @@ +/* ppmtoilbm.c - read a portable pixmap and produce an IFF ILBM file +** +** Copyright (C) 1989 by Jef Poskanzer. +** Modified by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de) +** 20/Jun/93: +** - 24-bit capability (new options -24if, -24force) +** - HAM8 capability (well, anything from HAM3 to HAM(MAXPLANES)) +** - now writes up to 8 (16) planes (new options -maxplanes, -fixplanes) +** - colormap file (new option -map) +** - write colormap only (new option -cmaponly) +** - only writes CAMG chunk if it is a HAM-picture +** 29/Aug/93: +** - operates row-by-row whenever possible +** - faster colorscaling with lookup-table (~20% faster on HAM pictures) +** - options -ham8 and -ham6 now imply -hamforce +** 27/Nov/93: +** - byterun1 compression (this is now default) with new options: +** -compress, -nocompress, -cmethod, -savemem +** - floyd-steinberg error diffusion (for std+mapfile and HAM) +** - new options: -lace and -hires --> write CAMG chunk +** - LUT for luminance calculation (used by ppm_to_ham) +** 23/Oct/94: +** - rework of mapfile handling +** - added RGB8 & RGBN image types +** - added maskplane and transparent color capability +** - 24-bit & direct color modified to n-bit deep ILBM +** - removed "-savemem" option +** 22/Feb/95: +** - minor bugfixes +** - fixed "-camg 0" behaviour: now writes a CAMG chunk with value 0 +** - "-24if" is now default +** - "-mmethod" and "-cmethod" options accept numeric args and keywords +** - direct color (DCOL) reimplemented +** - mapfile useable for HAM +** - added HAM colormap "fixed" +** 29/Mar/95: +** - added HAM colormap "rgb4" and "rgb5" (compute with 4/5-bit table) +** - added IFF text chunks +** +** TODO: +** - multipalette capability (PCHG chunk) for std and HAM +** +** +** std HAM deep cmap RGB8 RGBN +** -------+-----+-----+-----+-----+-----+----- +** BMHD yes yes yes yes yes yes +** CMAP yes (1) no yes no no +** BODY yes yes yes no yes yes +** CAMG (2) yes (2) no yes yes +** nPlanes 1-16 3-16 3-48 0 25 13 +** +** (1): grayscale colormap +** (2): only if "-lace", "-hires" or "-camg" option used +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include <string.h> + +#include "pm_c_util.h" +#include "mallocvar.h" +#include "ppm.h" +#include "ppmfloyd.h" +#include "pbm.h" +#include "ilbm.h" +#include "lum.h" + +/*#define DEBUG*/ + +#define MODE_RGB8 6 /* RGB8: 8-bit RGB */ +#define MODE_RGBN 5 /* RGBN: 4-bit RGB */ +#define MODE_CMAP 4 /* ILBM: colormap only */ +#define MODE_DCOL 3 /* ILBM: direct color */ +#define MODE_DEEP 2 /* ILBM: deep (24-bit) */ +#define MODE_HAM 1 /* ILBM: hold-and-modify (HAM) */ +#define MODE_NONE 0 /* ILBM: colormapped */ + +#define HAMMODE_GRAY 0 /* HAM colormap: grayscale */ +#define HAMMODE_FIXED 1 /* HAM colormap: 7 "rays" in RGB cube */ +#define HAMMODE_MAPFILE 2 /* HAM colormap: loaded from mapfile */ +#define HAMMODE_RGB4 3 /* HAM colormap: compute, 4bit RGB */ +#define HAMMODE_RGB5 4 /* HAM colormap: compute, 5bit RGB */ + +#define ECS_MAXPLANES 5 +#define ECS_HAMPLANES 6 +#define AGA_MAXPLANES 8 +#define AGA_HAMPLANES 8 + +#define HAMMAXPLANES 10 /* maximum planes for HAM */ + +#define DEF_MAXPLANES ECS_MAXPLANES +#define DEF_HAMPLANES ECS_HAMPLANES +#define DEF_COMPRESSION cmpByteRun1 +#define DEF_DEEPPLANES 8 +#define DEF_DCOLPLANES 5 +#define DEF_IFMODE MODE_DEEP + +static void put_big_short ARGS((short s)); +static void put_big_long ARGS((long l)); +#define put_byte(b) (void)(putc((unsigned char)(b), stdout)) +static void write_bytes ARGS((unsigned char *buffer, int bytes)); +static void ppm_to_ham ARGS((FILE *fp, int cols, int rows, int maxval, pixel *colormap, int colors, int cmapmaxval, int hamplanes)); +static void ppm_to_deep ARGS((FILE *fp, int cols, int rows, int maxval, int bitspercolor)); +static void ppm_to_dcol ARGS((FILE *fp, int cols, int rows, int maxval, DirectColor *dcol)); +static void ppm_to_rgb8 ARGS((FILE *fp, int cols, int rows, int maxval)); +static void ppm_to_rgbn ARGS((FILE *fp, int cols, int rows, int maxval)); +static void ppm_to_std ARGS((FILE *fp, int cols, int rows, int maxval, pixel *colormap, int colors, int cmapmaxval, int maxcolors, int nPlanes)); +static void ppm_to_cmap ARGS((pixel *colormap, int colors, int maxval)); +static void write_bmhd ARGS((int cols, int rows, int nPlanes)); +static void write_cmap ARGS((pixel *colormap, int colors, int maxval)); +static long encode_row ARGS((FILE *outfile, rawtype *rawrow, int cols, int nPlanes)); +static long encode_maskrow ARGS((FILE *outfile, rawtype *rawrow, int cols)); +static int compress_row ARGS((int bytes)); +static void store_bodyrow ARGS((unsigned char *row, int len)); +static int runbyte1 ARGS((int bytes)); +static pixel * next_pixrow ARGS((FILE *fp, int row)); +static int * make_val_table ARGS((int oldmaxval, int newmaxval)); +static void init_read ARGS((FILE *fp, int *colsP, int *rowsP, pixval *maxvalP, int *formatP, int readall)); +static void write_body_rows ARGS((void)); +static void write_camg ARGS((void)); +static int length_of_text_chunks ARGS((void)); +static void write_text_chunks ARGS((void)); +#define PAD(n) (ODD(n) ? 1 : 0) /* pad to a word */ + + +/* global data */ +static unsigned char *coded_rowbuf; /* buffer for uncompressed scanline */ +static unsigned char *compr_rowbuf; /* buffer for compressed scanline */ +static pixel **pixels; /* PPM image (NULL for row-by-row operation) */ +static pixel *pixrow; + /* current row in PPM image (pointer into pixels array, or buffer + for row-by-row operation) + */ + +static long viewportmodes = 0; +static int slicesize = 1; + /* rows per slice for multipalette images - NOT USED */ + +static unsigned char compmethod = DEF_COMPRESSION; /* default compression */ +static unsigned char maskmethod = mskNone; + +static pixel *transpColor = NULL; /* transparent color */ +static short transpIndex = -1; /* index of transparent color */ + +static short hammapmode = HAMMODE_GRAY; +static short sortcmap = 0; /* sort colormap */ + +static FILE *maskfile = NULL; +static bit *maskrow = NULL; +static int maskcols, maskformat; +#define TOTALPLANES(nplanes) ((nplanes) + ((maskmethod == mskHasMask) ? 1 : 0)) + + +#define ROWS_PER_BLOCK 1024 +typedef struct bodyblock { + int used; + unsigned char *row[ROWS_PER_BLOCK]; + int len[ROWS_PER_BLOCK]; + struct bodyblock *next; +} bodyblock; +static bodyblock firstblock = { 0 }; +static bodyblock *cur_block = &firstblock; + +static char *anno_chunk, *auth_chunk, *name_chunk, *text_chunk, *copyr_chunk; + +/* flags */ +static short compr_force = 0; + /* force compressed output, even if the image got larger - NOT USED */ +static short floyd = 0; /* apply floyd-steinberg error diffusion */ +static short gen_camg = 0; /* write CAMG chunk */ + +#define WORSTCOMPR(bytes) ((bytes) + (bytes)/128 + 1) +#define DO_COMPRESS (compmethod != cmpNone) + + +/***** parse options and figure out what kind of ILBM to write *****/ + +static int get_int_val ARGS((char *string, char *option, int bot, int top)); +static int get_compr_method ARGS((char *string)); +static int get_mask_type ARGS((char *string)); +static int get_hammap_mode ARGS((char *string)); + + + +#define NEWDEPTH(pix, table) PPM_ASSIGN((pix), (table)[PPM_GETR(pix)], (table)[PPM_GETG(pix)], (table)[PPM_GETB(pix)]) + + +static void +report_too_many_colors(int const ifmode, + int const maxplanes, + int const hamplanes, + DirectColor const dcol, + int const deepbits) { + + int const maxcolors = 1 << maxplanes; + + switch( ifmode ) { + case MODE_HAM: + pm_message("too many colors for %d planes - " + "proceeding to write a HAM%d file", + maxplanes, hamplanes); + pm_message("if you want a non-HAM file, try doing a 'pnmquant %d'", + maxcolors); + break; + case MODE_DCOL: + pm_message("too many colors for %d planes - " + "proceeding to write a %d:%d:%d direct color ILBM", + maxplanes, dcol.r, dcol.g, dcol.b); + pm_message("if you want a non-direct color file, " + "try doing a 'pnmquant %d'", maxcolors); + break; + case MODE_DEEP: + pm_message("too many colors for %d planes - " + "proceeding to write a %d-bit \'deep\' ILBM", + maxplanes, deepbits*3); + pm_message("if you want a non-deep file, " + "try doing a 'pnmquant %d'", + maxcolors); + break; + default: + pm_error("too many colors for %d planes - " + "try doing a 'pnmquant %d'", + maxplanes, maxcolors); + break; + } +} + + +static int +get_int_val(string, option, bot, top) + char *string, *option; + int bot, top; +{ + int val; + + if( sscanf(string, "%d", &val) != 1 ) + pm_error("option \"%s\" needs integer argument", option); + + if( val < bot || val > top ) + pm_error("option \"%s\" argument value out of range (%d..%d)", + option, bot, top); + + return val; +} + + +static int +get_compr_method(string) + char *string; +{ + int retval; + if( pm_keymatch(string, "none", 1) || pm_keymatch(string, "0", 1) ) + retval = cmpNone; + else if( pm_keymatch(string, "byterun1", 1) || + pm_keymatch(string, "1", 1) ) + retval = cmpByteRun1; + else + pm_error("unknown compression method: %s", string); + return retval; +} + + +static int +get_mask_type(string) + char *string; +{ + int retval; + + if( pm_keymatch(string, "none", 1) || pm_keymatch(string, "0", 1) ) + retval = mskNone; + else + if( pm_keymatch(string, "plane", 1) || + pm_keymatch(string, "maskplane", 1) || + pm_keymatch(string, "1", 1) ) + retval = mskHasMask; + else + if( pm_keymatch(string, "transparentcolor", 1) || + pm_keymatch(string, "2", 1) ) + retval = mskHasTransparentColor; + else + if( pm_keymatch(string, "lasso", 1) || pm_keymatch(string, "3", 1) ) + retval = mskLasso; + else + pm_error("unknown masking method: %s", string); + return retval; +} + + +static int +get_hammap_mode(string) + char *string; +{ + int retval; + + if( pm_keymatch(string, "grey", 1) || pm_keymatch(string, "gray", 1) ) + retval = HAMMODE_GRAY; + else + if( pm_keymatch(string, "fixed", 1) ) + retval = HAMMODE_FIXED; + else + if( pm_keymatch(string, "rgb4", 4) ) + retval = HAMMODE_RGB4; + else + if( pm_keymatch(string, "rgb5", 4) ) + retval = HAMMODE_RGB5; + else + pm_error("unknown HAM colormap selection mode: %s", string); + return retval; +} + + +/************ colormap file ************/ + +static void +ppm_to_cmap(colorrow, colors, maxval) + pixel *colorrow; + int colors; + int maxval; +{ + int formsize, cmapsize; + + cmapsize = colors * 3; + + formsize = + 4 + /* ILBM */ + 4 + 4 + BitMapHeaderSize + /* BMHD size header */ + 4 + 4 + cmapsize + PAD(cmapsize) + /* CMAP size colormap */ + length_of_text_chunks(); + + put_big_long(ID_FORM); + put_big_long(formsize); + put_big_long(ID_ILBM); + + write_bmhd(0, 0, 0); + write_text_chunks(); + write_cmap(colorrow, colors, maxval); +} + +/************ HAM ************/ + +static long +do_ham_body ARGS((FILE *ifP, FILE *ofp, int cols, int rows, pixval maxval, + pixval hammaxval, int nPlanes, pixel *cmap, int colors)); + + +static int hcmp (const void *va, const void *vb); +static pixel *compute_ham_cmap ARGS((int cols, int rows, int maxval, + int maxcolors, int *colorsP, int hbits)); + + +typedef struct { + long count; + pixval r, g, b; +} hentry; + + +static int +hcmp(const void *va, const void *vb) +{ + return(((hentry *)vb)->count - ((hentry *)va)->count); + /* reverse sort, highest count first */ +} + + +static pixel * +compute_ham_cmap(cols, rows, maxval, maxcolors, colorsP, hbits) + int cols, rows, maxval, maxcolors; + int *colorsP; + int hbits; +{ + int colors; + hentry *hmap; + pixel *cmap; + pixval hmaxval; + int i, r, g, b, col, row, *htable; + unsigned long dist, maxdist; + + pm_message("initializing HAM colormap..."); + + colors = 1<<(3*hbits); + MALLOCARRAY(hmap, colors); + if (hmap == NULL) + pm_error("Unable to allocate memory for HAM colormap."); + hmaxval = pm_bitstomaxval(hbits); + + i = 0; + for( r = 0; r <= hmaxval; r++ ) { + for( g = 0; g <= hmaxval; g++ ) { + for( b = 0; b <= hmaxval; b++ ) { + hmap[i].r = r; hmap[i].g = g; hmap[i].b = b; + hmap[i].count = 0; + i++; + } + } + } + + htable = make_val_table(maxval, hmaxval); + for( row = 0; row < rows; row++ ) { + unsigned int col; + for( col = 0; col < cols; ++col) { + pixel const p = pixels[row][col]; + pixval const r = PPM_GETR(p); + pixval const g = PPM_GETG(p); + pixval const b = PPM_GETB(p); + i = (htable[r]<<(2*hbits)) + (htable[g]<<hbits) + htable[b]; + hmap[i].count++; + } + } + free(htable); + + qsort((void *)hmap, colors, sizeof(hentry), hcmp); + for( i = colors-1; i >= 0; i-- ) { + if( hmap[i].count ) + break; + } + colors = i+1; + + if( colors > maxcolors ) { + pm_message("selecting HAM colormap from %d colors...", colors); + for( maxdist = 1; ; maxdist++ ) { + for( col = colors-1; col > 0; col-- ) { + r = hmap[col].r; g = hmap[col].g; b = hmap[col].b; + for( i = 0; i < col; i++ ) { + register int tmp; + + tmp = hmap[i].r - r; dist = tmp * tmp; + tmp = hmap[i].g - g; dist += tmp * tmp; + tmp = hmap[i].b - b; dist += tmp * tmp; + + if( dist <= maxdist ) { + int sum = hmap[i].count + hmap[col].count; + + hmap[i].r = (hmap[i].r * hmap[i].count + + r * hmap[col].count + sum/2)/sum; + hmap[i].g = (hmap[i].g * hmap[i].count + + g * hmap[col].count + sum/2)/sum; + hmap[i].b = (hmap[i].b * hmap[i].count + + b * hmap[col].count + sum/2)/sum; + hmap[i].count = sum; + + hmap[col] = hmap[i]; /* temp store */ + for( tmp = i-1; + tmp >= 0 && hmap[tmp].count < hmap[col].count; + tmp-- ) + hmap[tmp+1] = hmap[tmp]; + hmap[tmp+1] = hmap[col]; + + for( tmp = col; tmp < colors-1; tmp++ ) + hmap[tmp] = hmap[tmp+1]; + if( --colors <= maxcolors ) + goto out; + break; + } + } + } +#ifdef DEBUG + pm_message("\tmaxdist=%ld: %d colors left", maxdist, colors); +#endif + } + } +out: + pm_message("%d colors in HAM colormap", colors); + + cmap = ppm_allocrow(colors); + *colorsP = colors; + + for( i = 0; i < colors; i++ ) { + r = hmap[i].r; g = hmap[i].g; b = hmap[i].b; + PPM_ASSIGN(cmap[i], r, g, b); + } + + ppm_freerow(hmap); + return cmap; +} + + +static void +ppm_to_ham(fp, cols, rows, maxval, colormap, colors, cmapmaxval, hamplanes) + FILE *fp; + int cols, rows, maxval; + pixel *colormap; + int colors, cmapmaxval, hamplanes; +{ + int hamcolors, nPlanes, i, hammaxval; + long oldsize, bodysize, formsize, cmapsize; + int *table = NULL; + + if( maskmethod == mskHasTransparentColor ) { + pm_message("masking method '%s' not usable with HAM - " + "using '%s' instead", + mskNAME[mskHasTransparentColor], mskNAME[mskHasMask]); + maskmethod = mskHasMask; + } + + hamcolors = 1 << (hamplanes-2); + hammaxval = pm_bitstomaxval(hamplanes-2); + + if( colors == 0 ) { + /* no colormap, make our own */ + switch( hammapmode ) { + case HAMMODE_GRAY: + colors = hamcolors; + MALLOCARRAY_NOFAIL(colormap, colors); +#ifdef DEBUG + pm_message("generating grayscale colormap"); +#endif + table = make_val_table(hammaxval, MAXCOLVAL); + for( i = 0; i < colors; i++ ) + PPM_ASSIGN(colormap[i], table[i], table[i], table[i]); + free(table); + cmapmaxval = MAXCOLVAL; + break; + case HAMMODE_FIXED: { + int entries, val; + double step; + +#ifdef DEBUG + pm_message("generating rgb colormap"); +#endif + /* generate a colormap of 7 "rays" in an RGB color cube: + r, g, b, r+g, r+b, g+b, r+g+b + we need one colormap entry for black, so the number of + entries per ray is (maxcolors-1)/7 */ + + entries = (hamcolors-1)/7; + colors = 7*entries+1; + MALLOCARRAY_NOFAIL(colormap, colors); + step = (double)MAXCOLVAL / (double)entries; + + PPM_ASSIGN(colormap[0], 0, 0, 0); + for( i = 1; i <= entries; i++ ) { + val = (int)((double)i * step); + PPM_ASSIGN(colormap[ i], val, 0, 0); /* r */ + PPM_ASSIGN(colormap[ entries+i], 0, val, 0); /* g */ + PPM_ASSIGN(colormap[2*entries+i], 0, 0, val); /* b */ + PPM_ASSIGN(colormap[3*entries+i], val, val, 0); /* r+g */ + PPM_ASSIGN(colormap[4*entries+i], val, 0, val); /* r+b */ + PPM_ASSIGN(colormap[5*entries+i], 0, val, val); /* g+b */ + PPM_ASSIGN(colormap[6*entries+i], val, val, val); /*r+g+b*/ + } + cmapmaxval = MAXCOLVAL; + } + break; + case HAMMODE_RGB4: + colormap = compute_ham_cmap(cols, rows, maxval, hamcolors, + &colors, 4); + cmapmaxval = 15; + break; + case HAMMODE_RGB5: + colormap = compute_ham_cmap(cols, rows, maxval, + hamcolors, &colors, 5); + cmapmaxval = 31; + break; + default: + pm_error("ppm_to_ham(): unknown hammapmode - can't happen"); + } + } + else { + hammapmode = HAMMODE_MAPFILE; + if( colors > hamcolors ) { + pm_message("colormap too large - using first %d colors", + hamcolors); + colors = hamcolors; + } + } + + if( cmapmaxval != maxval ) { + int i, *table; + pixel *newcmap; + + newcmap = ppm_allocrow(colors); + table = make_val_table(cmapmaxval, maxval); + for( i = 0; i < colors; i++ ) + PPM_ASSIGN(newcmap[i], + table[PPM_GETR(colormap[i])], + table[PPM_GETG(colormap[i])], + table[PPM_GETB(colormap[i])]); + free(table); + ppm_freerow(colormap); + colormap = newcmap; + } + if( sortcmap ) + ppm_sortcolorrow(colormap, colors, PPM_STDSORT); + + nPlanes = hamplanes; + cmapsize = colors * 3; + + bodysize = oldsize = rows * TOTALPLANES(nPlanes) * RowBytes(cols); + if( DO_COMPRESS ) { + bodysize = do_ham_body(fp, NULL, cols, rows, maxval, + hammaxval, nPlanes, colormap, colors); + /*bodysize = do_ham_body(fp, NULL, cols, + rows, maxval, hammaxval, nPlanes, colbits, nocolor);*/ + if( bodysize > oldsize ) + pm_message("warning - %s compression increases BODY size " + "by %ld%%", + cmpNAME[compmethod], 100*(bodysize-oldsize)/oldsize); + else + pm_message("BODY compression (%s): %ld%%", + cmpNAME[compmethod], 100*(oldsize-bodysize)/oldsize); + } + + + formsize = + 4 + /* ILBM */ + 4 + 4 + BitMapHeaderSize + /* BMHD size header */ + 4 + 4 + CAMGChunkSize + /* CAMG size viewportmodes */ + 4 + 4 + cmapsize + PAD(cmapsize) + /* CMAP size colormap */ + 4 + 4 + bodysize + PAD(bodysize) + /* BODY size data */ + length_of_text_chunks(); + + put_big_long(ID_FORM); + put_big_long(formsize); + put_big_long(ID_ILBM); + + write_bmhd(cols, rows, nPlanes); + write_text_chunks(); + write_camg(); /* HAM requires CAMG chunk */ + write_cmap(colormap, colors, maxval); + + /* write body */ + put_big_long(ID_BODY); + put_big_long(bodysize); + if( DO_COMPRESS ) + write_body_rows(); + else + do_ham_body(fp, NULL, cols, rows, maxval, hammaxval, + nPlanes, colormap, colors); +} + + +static long +#ifdef __STDC__ +do_ham_body(FILE *ifP, FILE *ofp, int cols, int rows, + pixval maxval, pixval hammaxval, int nPlanes, + pixel *colormap, int colors) +#else +do_ham_body(ifP, ofp, cols, rows, maxval, hammaxval, nPlanes, colormap, colors) + FILE *ifP, *ofp; + int cols, rows; + pixval maxval; /* maxval of image color components */ + pixval hammaxval; /* maxval of HAM color changes */ + int nPlanes; + pixel *colormap; + int colors; +#endif +{ + register int col, row, i; + rawtype *raw_rowbuf; + ppm_fs_info *fi = NULL; + colorhash_table cht, cht2; + long bodysize = 0; + int *itoh; /* table image -> ham */ + int usehash = 1; + int colbits; + int hamcode_red, hamcode_green, hamcode_blue; + + MALLOCARRAY_NOFAIL(raw_rowbuf, cols); + + cht = ppm_colorrowtocolorhash(colormap, colors); + cht2 = ppm_alloccolorhash(); + colbits = pm_maxvaltobits(hammaxval); + + hamcode_red = HAMCODE_RED << colbits; + hamcode_green = HAMCODE_GREEN << colbits; + hamcode_blue = HAMCODE_BLUE << colbits; + + itoh = make_val_table(maxval, hammaxval); + + if( floyd ) + fi = ppm_fs_init(cols, maxval, 0); + + for( row = 0; row < rows; row++ ) { + int noprev; + int spr, spg, spb; /* scaled values of previous pixel */ + int upr, upg, upb; /* unscaled values of previous pixel, for floyd */ + pixel *prow; + + noprev = 1; + prow = next_pixrow(ifP, row); + for( col = ppm_fs_startrow(fi, prow); + col < cols; + col = ppm_fs_next(fi, col) ) { + + pixel const p = prow[col]; + + /* unscaled values of current pixel */ + pixval const ur = PPM_GETR(p); + pixval const ug = PPM_GETG(p); + pixval const ub = PPM_GETB(p); + + /* scaled values of current pixel */ + int const sr = itoh[ur]; + int const sg = itoh[ug]; + int const sb = itoh[ub]; + + i = ppm_lookupcolor(cht, &p); + if( i == -1 ) { /* no matching color in cmap, find closest match */ + int ucr, ucg, ucb; /* unscaled values of colormap entry */ + + if( hammapmode == HAMMODE_GRAY ) { + if( maxval <= 255 ) + /* Use fast approximation to + 0.299 r + 0.587 g + 0.114 b. */ + i = (int)ppm_fastlumin(p); + else + /* Can't use fast approximation, + so fall back on floats. + */ + i = (int)(PPM_LUMIN(p) + 0.5); + /* -IUW added '+ 0.5' */ + i = itoh[i]; + } + else { + i = ppm_lookupcolor(cht2, &p); + if( i == -1 ) { + i = ppm_findclosestcolor(colormap, colors, &p); + if( usehash ) { + if( ppm_addtocolorhash(cht2, &p, i) < 0 ) { + pm_message("out of memory " + "adding to hash table, " + "proceeding without it"); + usehash = 0; + } + } + } + } + ucr = PPM_GETR(colormap[i]); + ucg = PPM_GETG(colormap[i]); + ucb = PPM_GETB(colormap[i]); + + if( noprev ) { /* no previous pixel, must use colormap */ + raw_rowbuf[col] = i; /* + (HAMCODE_CMAP << colbits) */ + upr = ucr; upg = ucg; upb = ucb; + spr = itoh[upr]; spg = itoh[upg]; spb = itoh[upb]; + noprev = 0; + } else { + register long di, dr, dg, db; + int scr, scg, scb; /* scaled values of colormap entry */ + + scr = itoh[ucr]; scg = itoh[ucg]; scb = itoh[ucb]; + + /* compute distances for the four options */ +#if 1 + dr = abs(sg - spg) + abs(sb - spb); + dg = abs(sr - spr) + abs(sb - spb); + db = abs(sr - spr) + abs(sg - spg); + di = abs(sr - scr) + abs(sg - scg) + abs(sb - scb); +#else + dr = (sg - spg)*(sg - spg) + (sb - spb)*(sb - spb); + dg = (sr - spr)*(sr - spr) + (sb - spb)*(sb - spb); + db = (sr - spr)*(sr - spr) + (sg - spg)*(sg - spg); + di = (sr - scr)*(sr - scr) + (sg - scg)*(sg - scg) + + (sb - scb)*(sb - scb); +#endif + + if( di <= dr && di <= dg && di <= db ) { + /* prefer colormap lookup */ + raw_rowbuf[col] = i; + upr = ucr; upg = ucg; upb = ucb; + spr = scr; spg = scg; spb = scb; + } + else + if( db <= dr && db <= dg ) { + raw_rowbuf[col] = sb + hamcode_blue; + spb = sb; + upb = ub; + } + else + if( dr <= dg ) { + raw_rowbuf[col] = sr + hamcode_red; + spr = sr; + upr = ur; + } + else { + raw_rowbuf[col] = sg + hamcode_green; + spg = sg; + upg = ug; + } + } + } + else { /* prefect match in cmap */ + raw_rowbuf[col] = i; /* + (HAMCODE_CMAP << colbits) */ + upr = PPM_GETR(colormap[i]); + upg = PPM_GETG(colormap[i]); + upb = PPM_GETB(colormap[i]); + spr = itoh[upr]; + spg = itoh[upg]; + spb = itoh[upb]; + } + ppm_fs_update3(fi, col, upr, upg, upb); + } + bodysize += encode_row(ofp, raw_rowbuf, cols, nPlanes); + if( maskmethod == mskHasMask ) + bodysize += encode_maskrow(ofp, raw_rowbuf, cols); + ppm_fs_endrow(fi); + } + if( ofp && ODD(bodysize) ) + put_byte(0); + + free(itoh); + + /* clean up */ + free(raw_rowbuf); + ppm_fs_free(fi); + + return bodysize; +} + + +/************ deep (24-bit) ************/ + +static long do_deep_body ARGS((FILE *ifP, FILE *ofp, + int cols, int rows, + pixval maxval, int bitspercolor)); + +static void +ppm_to_deep(fp, cols, rows, maxval, bitspercolor) + FILE *fp; + int cols, rows, maxval, bitspercolor; +{ + int nPlanes; + long bodysize, oldsize, formsize; + + if( maskmethod == mskHasTransparentColor ) { + pm_message("masking method '%s' not usable with deep ILBM - " + "using '%s' instead", + mskNAME[mskHasTransparentColor], mskNAME[mskHasMask]); + maskmethod = mskHasMask; + } + + nPlanes = 3*bitspercolor; + + bodysize = oldsize = rows * TOTALPLANES(nPlanes) * RowBytes(cols); + if( DO_COMPRESS ) { + bodysize = do_deep_body(fp, NULL, cols, rows, maxval, bitspercolor); + if( bodysize > oldsize ) + pm_message("warning - %s compression increases BODY size by %ld%%", + cmpNAME[compmethod], 100*(bodysize-oldsize)/oldsize); + else + pm_message("BODY compression (%s): %ld%%", + cmpNAME[compmethod], 100*(oldsize-bodysize)/oldsize); + } + + + formsize = + 4 + /* ILBM */ + 4 + 4 + BitMapHeaderSize + /* BMHD size header */ + 4 + 4 + bodysize + PAD(bodysize) + /* BODY size data */ + length_of_text_chunks(); + if( gen_camg ) + formsize += 4 + 4 + CAMGChunkSize; /* CAMG size viewportmodes */ + + put_big_long(ID_FORM); + put_big_long(formsize); + put_big_long(ID_ILBM); + + write_bmhd(cols, rows, nPlanes); + write_text_chunks(); + if( gen_camg ) + write_camg(); + + /* write body */ + put_big_long(ID_BODY); + put_big_long(bodysize); + if( DO_COMPRESS ) + write_body_rows(); + else + do_deep_body(fp, stdout, cols, rows, maxval, bitspercolor); +} + + +static long +#if __STDC__ +do_deep_body(FILE *ifP, FILE *ofp, int cols, int rows, pixval maxval, + int bitspercolor) +#else +do_deep_body(ifP, ofp, cols, rows, maxval, bitspercolor) + FILE *ifP, *ofp; + int cols, rows; + pixval maxval; + int bitspercolor; +#endif +{ + register int row, col; + pixel *pP; + int *table = NULL; + long bodysize = 0; + rawtype *redbuf, *greenbuf, *bluebuf; + int newmaxval; + + MALLOCARRAY_NOFAIL(redbuf, cols); + MALLOCARRAY_NOFAIL(greenbuf, cols); + MALLOCARRAY_NOFAIL(bluebuf, cols); + + newmaxval = pm_bitstomaxval(bitspercolor); + if( maxval != newmaxval ) { + pm_message("maxval is not %d - automatically rescaling colors", + newmaxval); + table = make_val_table(maxval, newmaxval); + } + + for( row = 0; row < rows; row++ ) { + pP = next_pixrow(ifP, row); + if( table ) { + for( col = 0; col < cols; col++, pP++ ) { + redbuf[col] = table[PPM_GETR(*pP)]; + greenbuf[col] = table[PPM_GETG(*pP)]; + bluebuf[col] = table[PPM_GETB(*pP)]; + } + } + else { + for( col = 0; col < cols; col++, pP++ ) { + redbuf[col] = PPM_GETR(*pP); + greenbuf[col] = PPM_GETG(*pP); + bluebuf[col] = PPM_GETB(*pP); + } + } + bodysize += encode_row(ofp, redbuf, cols, bitspercolor); + bodysize += encode_row(ofp, greenbuf, cols, bitspercolor); + bodysize += encode_row(ofp, bluebuf, cols, bitspercolor); + if( maskmethod == mskHasMask ) + bodysize += encode_maskrow(ofp, redbuf, cols); + } + if( ofp && ODD(bodysize) ) + put_byte(0); + + /* clean up */ + if( table ) + free(table); + free(redbuf); + free(greenbuf); + free(bluebuf); + + return bodysize; +} + + +/************ direct color ************/ + +static long do_dcol_body ARGS((FILE *ifP, FILE *ofp, int cols, int rows, + pixval maxval, DirectColor *dcol)); + +static void +ppm_to_dcol(fp, cols, rows, maxval, dcol) + FILE *fp; + int cols, rows, maxval; + DirectColor *dcol; +{ + int nPlanes; + long bodysize, oldsize, formsize; + + if( maskmethod == mskHasTransparentColor ) { + pm_message("masking method '%s' not usable with deep ILBM - " + "using '%s' instead", + mskNAME[mskHasTransparentColor], mskNAME[mskHasMask]); + maskmethod = mskHasMask; + } + + nPlanes = dcol->r + dcol->g + dcol->b; + + bodysize = oldsize = rows * TOTALPLANES(nPlanes) * RowBytes(cols); + if( DO_COMPRESS ) { + bodysize = do_dcol_body(fp, NULL, cols, rows, maxval, dcol); + if( bodysize > oldsize ) + pm_message("warning - %s compression increases BODY size by %ld%%", + cmpNAME[compmethod], + 100*(bodysize-oldsize)/oldsize); + else + pm_message("BODY compression (%s): %ld%%", cmpNAME[compmethod], + 100*(oldsize-bodysize)/oldsize); + } + + + formsize = + 4 + /* ILBM */ + 4 + 4 + BitMapHeaderSize + /* BMHD size header */ + 4 + 4 + DirectColorSize + /* DCOL size dcol */ + 4 + 4 + bodysize + PAD(bodysize) + /* BODY size data */ + length_of_text_chunks(); + if( gen_camg ) + formsize += 4 + 4 + CAMGChunkSize; /* CAMG size viewportmodes */ + + put_big_long(ID_FORM); + put_big_long(formsize); + put_big_long(ID_ILBM); + + write_bmhd(cols, rows, nPlanes); + write_text_chunks(); + + put_big_long(ID_DCOL); + put_big_long(DirectColorSize); + put_byte(dcol->r); + put_byte(dcol->g); + put_byte(dcol->b); + put_byte(0); /* pad */ + + if( gen_camg ) + write_camg(); + + /* write body */ + put_big_long(ID_BODY); + put_big_long(bodysize); + if( DO_COMPRESS ) + write_body_rows(); + else + do_dcol_body(fp, stdout, cols, rows, maxval, dcol); +} + + +static long +#if __STDC__ +do_dcol_body(FILE *ifP, FILE *ofp, int cols, int rows, pixval maxval, + DirectColor *dcol) +#else +do_dcol_body(ifP, ofp, cols, rows, maxval, dcol) + FILE *ifP, *ofp; + int cols, rows; + pixval maxval; + DirectColor *dcol; +#endif +{ + register int row, col; + pixel *pP; + long bodysize = 0; + rawtype *redbuf, *greenbuf, *bluebuf; + int *redtable, *greentable, *bluetable; + + MALLOCARRAY_NOFAIL(redbuf, cols); + MALLOCARRAY_NOFAIL(greenbuf, cols); + MALLOCARRAY_NOFAIL(bluebuf, cols); + + redtable = make_val_table(maxval, pm_bitstomaxval(dcol->r)); + greentable = make_val_table(maxval, pm_bitstomaxval(dcol->g)); + bluetable = make_val_table(maxval, pm_bitstomaxval(dcol->b)); + + for( row = 0; row < rows; row++ ) { + pP = next_pixrow(ifP, row); + for( col = 0; col < cols; col++, pP++ ) { + redbuf[col] = redtable[PPM_GETR(*pP)]; + greenbuf[col] = greentable[PPM_GETG(*pP)]; + bluebuf[col] = bluetable[PPM_GETB(*pP)]; + } + bodysize += encode_row(ofp, redbuf, cols, dcol->r); + bodysize += encode_row(ofp, greenbuf, cols, dcol->g); + bodysize += encode_row(ofp, bluebuf, cols, dcol->b); + if( maskmethod == mskHasMask ) + bodysize += encode_maskrow(ofp, redbuf, cols); + } + if( ofp && ODD(bodysize) ) + put_byte(0); + + /* clean up */ + free(redtable); + free(greentable); + free(bluetable); + free(redbuf); + free(greenbuf); + free(bluebuf); + + return bodysize; +} + + +/************ normal colormapped ************/ + +static long do_std_body ARGS((FILE *ifP, FILE *ofp, int cols, int rows, + pixval maxval, pixel *colormap, + int colors, int nPlanes)); + +static void +ppm_to_std(fp, cols, rows, maxval, colormap, colors, cmapmaxval, + maxcolors, nPlanes) + FILE *fp; + int cols, rows, maxval; + pixel *colormap; + int cmapmaxval, colors, maxcolors, nPlanes; +{ + long formsize, cmapsize, bodysize, oldsize; + + if( maskmethod == mskHasTransparentColor ) { + if( transpColor ) { + transpIndex = + ppm_addtocolorrow(colormap, &colors, maxcolors, transpColor); + } + else + if( colors < maxcolors ) + transpIndex = colors; + + if( transpIndex < 0 ) { + pm_message("too many colors for masking method '%s' - " + "using '%s' instead", + mskNAME[mskHasTransparentColor], mskNAME[mskHasMask]); + maskmethod = mskHasMask; + } + } + + if( cmapmaxval != maxval ) { + int i, *table; + pixel *newcmap; + + newcmap = ppm_allocrow(colors); + table = make_val_table(cmapmaxval, maxval); + for (i = 0; i < colors; ++i) + PPM_ASSIGN(newcmap[i], + table[PPM_GETR(colormap[i])], + table[PPM_GETG(colormap[i])], + table[PPM_GETB(colormap[i])]); + free(table); + colormap = newcmap; + } + if( sortcmap ) + ppm_sortcolorrow(colormap, colors, PPM_STDSORT); + + bodysize = oldsize = rows * TOTALPLANES(nPlanes) * RowBytes(cols); + if( DO_COMPRESS ) { + bodysize = do_std_body(fp, NULL, cols, rows, maxval, colormap, + colors, nPlanes); + if( bodysize > oldsize ) + pm_message("warning - %s compression increases BODY size by %ld%%", + cmpNAME[compmethod], 100*(bodysize-oldsize)/oldsize); + else + pm_message("BODY compression (%s): %ld%%", + cmpNAME[compmethod], 100*(oldsize-bodysize)/oldsize); + } + + cmapsize = colors * 3; + + formsize = + 4 + /* ILBM */ + 4 + 4 + BitMapHeaderSize + /* BMHD size header */ + 4 + 4 + cmapsize + PAD(cmapsize) + /* CMAP size colormap */ + 4 + 4 + bodysize + PAD(bodysize) + /* BODY size data */ + length_of_text_chunks(); + if( gen_camg ) + formsize += 4 + 4 + CAMGChunkSize; /* CAMG size viewportmodes */ + + put_big_long(ID_FORM); + put_big_long(formsize); + put_big_long(ID_ILBM); + + write_bmhd(cols, rows, nPlanes); + write_text_chunks(); + if( gen_camg ) + write_camg(); + write_cmap(colormap, colors, maxval); + + /* write body */ + put_big_long(ID_BODY); + put_big_long(bodysize); + if( DO_COMPRESS ) + write_body_rows(); + else + do_std_body(fp, stdout, cols, rows, maxval, colormap, colors, nPlanes); +} + + +static long +#if __STDC__ +do_std_body(FILE *ifP, FILE *ofp, int cols, int rows, pixval maxval, + pixel *colormap, int colors, int nPlanes) +#else +do_std_body(ifP, ofp, cols, rows, maxval, colormap, colors, nPlanes) + FILE *ifP, *ofp; + int cols, rows; + pixval maxval; + pixel *colormap; + int colors; + int nPlanes; +#endif +{ + register int row, col, i; + pixel *pP; + rawtype *raw_rowbuf; + ppm_fs_info *fi = NULL; + long bodysize = 0; + int usehash = 1; + colorhash_table cht; + + MALLOCARRAY_NOFAIL(raw_rowbuf, cols); + cht = ppm_colorrowtocolorhash(colormap, colors); + if( floyd ) + fi = ppm_fs_init(cols, maxval, FS_ALTERNATE); + + for( row = 0; row < rows; row++ ) { + pixel *prow; + prow = next_pixrow(ifP, row); + + for( col = ppm_fs_startrow(fi, prow); + col < cols; + col = ppm_fs_next(fi, col) ) { + pP = &prow[col]; + + if( maskmethod == mskHasTransparentColor && + maskrow[col] == PBM_WHITE ) + i = transpIndex; + else { + /* Check hash table to see if we have already matched + this color. + */ + i = ppm_lookupcolor(cht, pP); + if( i == -1 ) { + i = ppm_findclosestcolor(colormap, colors, pP); + /* No; search colormap for closest match. */ + if( usehash ) { + if( ppm_addtocolorhash(cht, pP, i) < 0 ) { + pm_message("out of memory adding to hash table, " + "proceeding without it"); + usehash = 0; + } + } + } + } + raw_rowbuf[col] = i; + ppm_fs_update(fi, col, &colormap[i]); + } + bodysize += encode_row(ofp, raw_rowbuf, cols, nPlanes); + if( maskmethod == mskHasMask ) + bodysize += encode_maskrow(ofp, raw_rowbuf, cols); + ppm_fs_endrow(fi); + } + if( ofp && ODD(bodysize) ) + put_byte(0); + + /* clean up */ + ppm_freecolorhash(cht); + free(raw_rowbuf); + ppm_fs_free(fi); + + return bodysize; +} + +/************ RGB8 ************/ + +static void +ppm_to_rgb8(ifP, cols, rows, maxval) + FILE *ifP; + int cols, rows; + int maxval; +{ + long bodysize, oldsize, formsize; + pixel *pP; + int *table = NULL; + int row, col1, col2, compr_len, len; + unsigned char *compr_row; + + maskmethod = 0; /* no masking - RGB8 uses genlock bits */ + compmethod = 4; /* RGB8 files are always compressed */ + MALLOCARRAY_NOFAIL(compr_row, cols * 4); + + if( maxval != 255 ) { + pm_message("maxval is not 255 - automatically rescaling colors"); + table = make_val_table(maxval, 255); + } + + oldsize = cols * rows * 4; + bodysize = 0; + for( row = 0; row < rows; row++ ) { + pP = next_pixrow(ifP, row); + compr_len = 0; + for( col1 = 0; col1 < cols; col1 = col2 ) { + col2 = col1 + 1; + if( maskrow ) { + while( col2 < cols && PPM_EQUAL(pP[col1], pP[col2]) && + maskrow[col1] == maskrow[col2] ) + col2++; + } + else { + while( col2 < cols && PPM_EQUAL(pP[col1], pP[col2]) ) + col2++; + } + len = col2 - col1; + while( len ) { + int count; + count = (len > 127 ? 127 : len); + len -= count; + if( table ) { + compr_row[compr_len++] = table[PPM_GETR(pP[col1])]; + compr_row[compr_len++] = table[PPM_GETG(pP[col1])]; + compr_row[compr_len++] = table[PPM_GETB(pP[col1])]; + } + else { + compr_row[compr_len++] = PPM_GETR(pP[col1]); + compr_row[compr_len++] = PPM_GETG(pP[col1]); + compr_row[compr_len++] = PPM_GETB(pP[col1]); + } + compr_row[compr_len] = count; + if( maskrow && maskrow[col1] == PBM_WHITE ) + compr_row[compr_len] |= 1<<7; /* genlock bit */ + ++compr_len; + } + } + store_bodyrow(compr_row, compr_len); + bodysize += compr_len; + } + + pm_message("BODY compression: %ld%%", 100*(oldsize-bodysize)/oldsize); + + formsize = + 4 + /* RGB8 */ + 4 + 4 + BitMapHeaderSize + /* BMHD size header */ + 4 + 4 + CAMGChunkSize + /* CAMG size viewportmode */ + 4 + 4 + bodysize + PAD(bodysize) + /* BODY size data */ + length_of_text_chunks(); + + /* write header */ + put_big_long(ID_FORM); + put_big_long(formsize); + put_big_long(ID_RGB8); + + write_bmhd(cols, rows, 25); + write_text_chunks(); + write_camg(); /* RGB8 requires CAMG chunk */ + + put_big_long(ID_BODY); + put_big_long(bodysize); + write_body_rows(); +} + + +/************ RGBN ************/ + +static void +ppm_to_rgbn(ifP, cols, rows, maxval) + FILE *ifP; + int cols, rows; + int maxval; +{ + long bodysize, oldsize, formsize; + pixel *pP; + int *table = NULL; + int row, col1, col2, compr_len, len; + unsigned char *compr_row; + + maskmethod = 0; /* no masking - RGBN uses genlock bits */ + compmethod = 4; /* RGBN files are always compressed */ + MALLOCARRAY_NOFAIL(compr_row, cols * 2); + + if( maxval != 15 ) { + pm_message("maxval is not 15 - automatically rescaling colors"); + table = make_val_table(maxval, 15); + } + + oldsize = cols * rows * 2; + bodysize = 0; + for( row = 0; row < rows; row++ ) { + pP = next_pixrow(ifP, row); + compr_len = 0; + for( col1 = 0; col1 < cols; col1 = col2 ) { + col2 = col1 + 1; + if( maskrow ) { + while( col2 < cols && PPM_EQUAL(pP[col1], pP[col2]) && + maskrow[col1] == maskrow[col2] ) + col2++; + } + else { + while( col2 < cols && PPM_EQUAL(pP[col1], pP[col2]) ) + col2++; + } + len = col2 - col1; + while( len ) { + int count; + count = (len > 65535 ? 65535 : len); + len -= count; + if( table ) { + compr_row[compr_len] = table[PPM_GETR(pP[col1])] << 4; + compr_row[compr_len] |= table[PPM_GETG(pP[col1])]; + ++compr_len; + compr_row[compr_len] = table[PPM_GETB(pP[col1])] << 4; + } + else { + compr_row[compr_len] = PPM_GETR(pP[col1]) << 4; + compr_row[compr_len] |= PPM_GETG(pP[col1]); + ++compr_len; + compr_row[compr_len] = PPM_GETB(pP[col1]) << 4; + } + if( maskrow && maskrow[col1] == PBM_WHITE ) + compr_row[compr_len] |= 1<<3; /* genlock bit */ + if( count <= 7 ) + compr_row[compr_len++] |= count; /* 3 bit repeat count */ + else { + ++compr_len; /* 3 bit repeat count = 0 */ + if( count <= 255 ) + compr_row[compr_len++] = (unsigned char)count; + /* byte repeat count */ + else { + compr_row[compr_len++] = (unsigned char)0; + /* byte repeat count = 0 */ + compr_row[compr_len++] = (count >> 8) & 0xff; + /* word repeat count MSB */ + compr_row[compr_len++] = count & 0xff; + /* word repeat count LSB */ + } + } + } + } + store_bodyrow(compr_row, compr_len); + bodysize += compr_len; + } + + pm_message("BODY compression: %ld%%", 100*(oldsize-bodysize)/oldsize); + + formsize = + 4 + /* RGBN */ + 4 + 4 + BitMapHeaderSize + /* BMHD size header */ + 4 + 4 + CAMGChunkSize + /* CAMG size viewportmode */ + 4 + 4 + bodysize + PAD(bodysize) + /* BODY size data */ + length_of_text_chunks(); + + /* write header */ + put_big_long(ID_FORM); + put_big_long(formsize); + put_big_long(ID_RGBN); + + write_bmhd(cols, rows, 13); + write_text_chunks(); + write_camg(); /* RGBN requires CAMG chunk */ + + put_big_long(ID_BODY); + put_big_long(bodysize); + write_body_rows(); +} + + +/************ multipalette ************/ + +#ifdef ILBM_PCHG +static pixel *ppmslice[2]; /* need 2 for laced ILBMs, else 1 */ + +void ppm_to_pchg() +{ +/* + read first slice + build a colormap from this slice + select upto <maxcolors> colors + build colormap from selected colors + map slice to colormap + write slice + while( !finished ) { + read next slice + compute distances for each pixel and select upto + <maxchangesperslice> unused colors in this slice + modify selected colors to the ones with maximum(?) distance + map slice to colormap + write slice + } + + + for HAM use a different mapping: + compute distance to closest color in colormap + if( there is no matching color in colormap ) { + compute distances for the three "modify" cases + use the shortest distance from the four cases + } +*/ +} +#endif + + +/************ ILBM functions ************/ + +static int +length_of_text_chunks ARGS((void)) +{ + int len, n; + + len = 0; + if( anno_chunk ) { + n = strlen(anno_chunk); + len += 4 + 4 + n + PAD(n); /* ID chunksize text */ + } + if( auth_chunk ) { + n = strlen(auth_chunk); + len += 4 + 4 + n + PAD(n); /* ID chunksize text */ + } + if( name_chunk ) { + n = strlen(name_chunk); + len += 4 + 4 + n + PAD(n); /* ID chunksize text */ + } + if( copyr_chunk ) { + n = strlen(copyr_chunk); + len += 4 + 4 + n + PAD(n); /* ID chunksize text */ + } + if( text_chunk ) { + n = strlen(text_chunk); + len += 4 + 4 + n + PAD(n); /* ID chunksize text */ + } + return len; +} + + +static void +write_text_chunks ARGS((void)) +{ + int n; + + if( anno_chunk ) { + n = strlen(anno_chunk); + put_big_long(ID_ANNO); + put_big_long(n); + write_bytes((unsigned char *)anno_chunk, n); + if( ODD(n) ) + put_byte(0); + } + if( auth_chunk ) { + n = strlen(auth_chunk); + put_big_long(ID_AUTH); + put_big_long(n); + write_bytes((unsigned char *)auth_chunk, n); + if( ODD(n) ) + put_byte(0); + } + if( copyr_chunk ) { + n = strlen(copyr_chunk); + put_big_long(ID_copy); + put_big_long(n); + write_bytes((unsigned char *)copyr_chunk, n); + if( ODD(n) ) + put_byte(0); + } + if( name_chunk ) { + n = strlen(name_chunk); + put_big_long(ID_NAME); + put_big_long(n); + write_bytes((unsigned char *)name_chunk, n); + if( ODD(n) ) + put_byte(0); + } + if( text_chunk ) { + n = strlen(text_chunk); + put_big_long(ID_TEXT); + put_big_long(n); + write_bytes((unsigned char *)text_chunk, n); + if( ODD(n) ) + put_byte(0); + } +} + + +static void +write_cmap(colormap, colors, maxval) + pixel *colormap; + int colors, maxval; +{ + int cmapsize, i; + + cmapsize = 3 * colors; + + /* write colormap */ + put_big_long(ID_CMAP); + put_big_long(cmapsize); + if( maxval != MAXCOLVAL ) { + int *table; + pm_message("maxval is not %d - automatically rescaling colors", + MAXCOLVAL); + table = make_val_table(maxval, MAXCOLVAL); + for( i = 0; i < colors; i++ ) { + put_byte(table[PPM_GETR(colormap[i])]); + put_byte(table[PPM_GETG(colormap[i])]); + put_byte(table[PPM_GETB(colormap[i])]); + } + free(table); + } + else { + for( i = 0; i < colors; i++ ) { + put_byte(PPM_GETR(colormap[i])); + put_byte(PPM_GETG(colormap[i])); + put_byte(PPM_GETB(colormap[i])); + } + } + if( ODD(cmapsize) ) + put_byte(0); +} + + +static void +write_bmhd(cols, rows, nPlanes) + int cols, rows, nPlanes; +{ + unsigned char xasp = 10, yasp = 10; + + if( viewportmodes & vmLACE ) + xasp *= 2; + if( viewportmodes & vmHIRES ) + yasp *= 2; + + put_big_long(ID_BMHD); + put_big_long(BitMapHeaderSize); + + put_big_short(cols); + put_big_short(rows); + put_big_short(0); /* x-offset */ + put_big_short(0); /* y-offset */ + put_byte(nPlanes); /* no of planes */ + put_byte(maskmethod); /* masking */ + put_byte(compmethod); /* compression */ + put_byte(BMHD_FLAGS_CMAPOK); /* flags */ + if( maskmethod == mskHasTransparentColor ) + put_big_short(transpIndex); + else + put_big_short(0); + put_byte(xasp); /* x-aspect */ + put_byte(yasp); /* y-aspect */ + put_big_short(cols); /* pageWidth */ + put_big_short(rows); /* pageHeight */ +} + + +/* encode algorithm by Johan Widen (jw@jwdata.se) */ +static const unsigned char bitmask[] = {1, 2, 4, 8, 16, 32, 64, 128}; + +static long +encode_row(outfile, rawrow, cols, nPlanes) + FILE *outfile; /* if non-NULL, write uncompressed row to this file */ + rawtype *rawrow; + int cols, nPlanes; +{ + int plane, bytes; + long retbytes = 0; + + bytes = RowBytes(cols); + + /* Encode and write raw bytes in plane-interleaved form. */ + for( plane = 0; plane < nPlanes; plane++ ) { + register int col, cbit; + register rawtype *rp; + register unsigned char *cp; + int mask; + + mask = 1 << plane; + cbit = -1; + cp = coded_rowbuf-1; + rp = rawrow; + for( col = 0; col < cols; col++, cbit--, rp++ ) { + if( cbit < 0 ) { + cbit = 7; + *++cp = 0; + } + if( *rp & mask ) + *cp |= bitmask[cbit]; + } + if( outfile ) { + write_bytes(coded_rowbuf, bytes); + retbytes += bytes; + } + else + retbytes += compress_row(bytes); + } + return retbytes; +} + + +static long +encode_maskrow(ofp, rawrow, cols) + FILE *ofp; + rawtype *rawrow; + int cols; +{ + int col; + + for( col = 0; col < cols; col++ ) { + if( maskrow[col] == PBM_BLACK ) + rawrow[col] = 1; + else + rawrow[col] = 0; + } + return encode_row(ofp, rawrow, cols, 1); +} + + +static int +compress_row(bytes) + int bytes; +{ + int newbytes; + + switch( compmethod ) { + case cmpByteRun1: + newbytes = runbyte1(bytes); + break; + default: + pm_error("compress_row(): unknown compression method %d", + compmethod); + } + store_bodyrow(compr_rowbuf, newbytes); + + return newbytes; +} + + +static void +store_bodyrow(row, len) + unsigned char *row; + int len; +{ + int idx = cur_block->used; + if( idx >= ROWS_PER_BLOCK ) { + MALLOCVAR_NOFAIL(cur_block->next); + cur_block = cur_block->next; + cur_block->used = idx = 0; + cur_block->next = NULL; + } + MALLOCARRAY_NOFAIL(cur_block->row[idx], len); + cur_block->len[idx] = len; + memcpy(cur_block->row[idx], row, len); + cur_block->used++; +} + + +static void +write_body_rows ARGS((void)) +{ + bodyblock *b; + int i; + long total = 0; + + for( b = &firstblock; b != NULL; b = b->next ) { + for( i = 0; i < b->used; i++ ) { + write_bytes(b->row[i], b->len[i]); + total += b->len[i]; + } + } + if( ODD(total) ) + put_byte(0); +} + + +static void +write_camg ARGS((void)) +{ + put_big_long(ID_CAMG); + put_big_long(CAMGChunkSize); + put_big_long(viewportmodes); +} + + +/************ compression ************/ + + +/* runbyte1 algorithm by Robert A. Knop (rknop@mop.caltech.edu) */ +static int +runbyte1(size) + int size; +{ + int in,out,count,hold; + register unsigned char *inbuf = coded_rowbuf; + register unsigned char *outbuf = compr_rowbuf; + + + in=out=0; + while( in<size ) { + if( (in<size-1) && (inbuf[in]==inbuf[in+1]) ) { + /*Begin replicate run*/ + for( count=0, hold=in; + in < size && inbuf[in] == inbuf[hold] && count < 128; + in++, count++) + ; + outbuf[out++]=(unsigned char)(char)(-count+1); + outbuf[out++]=inbuf[hold]; + } + else { /*Do a literal run*/ + hold=out; out++; count=0; + while( ((in>=size-2)&&(in<size)) || + ((in<size-2) && ((inbuf[in]!=inbuf[in+1]) + ||(inbuf[in]!=inbuf[in+2]))) ) { + outbuf[out++]=inbuf[in++]; + if( ++count>=128 ) + break; + } + outbuf[hold]=count-1; + } + } + return(out); +} + + + +/************ other utility functions ************/ + +static void +#if __STDC__ +put_big_short(short s) +#else +put_big_short(s) + short s; +#endif +{ + if ( pm_writebigshort( stdout, s ) == -1 ) + pm_error( "write error" ); +} + + +static void +put_big_long(l) + long l; +{ + if ( pm_writebiglong( stdout, l ) == -1 ) + pm_error( "write error" ); +} + + +static void +write_bytes(buffer, bytes) + unsigned char *buffer; + int bytes; +{ + if( fwrite(buffer, 1, bytes, stdout) != bytes ) + pm_error("write error"); +} + + +static int * +make_val_table(oldmaxval, newmaxval) + int oldmaxval, newmaxval; +{ + int i; + int *table; + + MALLOCARRAY_NOFAIL(table, oldmaxval + 1); + for(i = 0; i <= oldmaxval; i++ ) + table[i] = (i * newmaxval + oldmaxval/2) / oldmaxval; + + return table; +} + + + +static int gFormat; +static int gCols; +static int gMaxval; + +static void +init_read(fp, colsP, rowsP, maxvalP, formatP, readall) + FILE *fp; + int *colsP, *rowsP; + pixval *maxvalP; + int *formatP; + int readall; +{ + ppm_readppminit(fp, colsP, rowsP, maxvalP, formatP); + if( readall ) { + int row; + + pixels = ppm_allocarray(*colsP, *rowsP); + for( row = 0; row < *rowsP; row++ ) + ppm_readppmrow(fp, pixels[row], *colsP, *maxvalP, *formatP); + /* pixels = ppm_readppm(fp, colsP, rowsP, maxvalP); */ + } + else { + pixrow = ppm_allocrow(*colsP); + } + gCols = *colsP; + gMaxval = *maxvalP; + gFormat = *formatP; +} + + +static pixel * +next_pixrow(fp, row) + FILE *fp; + int row; +{ + if( pixels ) + pixrow = pixels[row]; + else { + ppm_readppmrow(fp, pixrow, gCols, gMaxval, gFormat); + } + if( maskrow ) { + int col; + + if( maskfile ) + pbm_readpbmrow(maskfile, maskrow, maskcols, maskformat); + else { + for( col = 0; col < gCols; col++ ) + maskrow[col] = PBM_BLACK; + } + if( transpColor ) { + for( col = 0; col < gCols; col++ ) + if( PPM_EQUAL(pixrow[col], *transpColor) ) + maskrow[col] = PBM_WHITE; + } + } + return pixrow; +} + + + +int +main(int argc, char ** argv) { + + FILE * ifP; + int argn, rows, cols, format, nPlanes; + int ifmode, forcemode, maxplanes, fixplanes, mode; + int hamplanes; + int deepbits; /* bits per color component in deep ILBM */ + DirectColor dcol; +#define MAXCOLORS (1<<maxplanes) + pixval maxval; + pixel * colormap; + int colors = 0; + pixval cmapmaxval; /* maxval of colors in cmap */ + const char * mapfile; + const char * transpname; + + ppm_init(&argc, argv); + + colormap = NULL; /* initial value */ + ifmode = DEF_IFMODE; forcemode = MODE_NONE; + maxplanes = DEF_MAXPLANES; fixplanes = 0; + hamplanes = DEF_HAMPLANES; + deepbits = DEF_DEEPPLANES; + dcol.r = dcol.g = dcol.b = DEF_DCOLPLANES; + mapfile = transpname = NULL; + + argn = 1; + while( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' ) { + if( pm_keymatch(argv[argn], "-ilbm", 5) ) { + if( forcemode == MODE_RGB8 || forcemode == MODE_RGBN ) + forcemode = MODE_NONE; + } + else + if( pm_keymatch(argv[argn], "-rgb8", 5) ) + forcemode = MODE_RGB8; + else + if( pm_keymatch(argv[argn], "-rgbn", 5) ) + forcemode = MODE_RGBN; + else + if( pm_keymatch(argv[argn], "-maxplanes", 4) || + pm_keymatch(argv[argn], "-mp", 3) ) { + if( ++argn >= argc ) + pm_error("-maxplanes requires a value"); + maxplanes = get_int_val(argv[argn], argv[argn-1], 1, MAXPLANES); + fixplanes = 0; + } + else + if( pm_keymatch(argv[argn], "-fixplanes", 4) || + pm_keymatch(argv[argn], "-fp", 3) ) { + if( ++argn >= argc ) + pm_error("-fixplanes requires a value"); + fixplanes = get_int_val(argv[argn], argv[argn-1], 1, MAXPLANES); + maxplanes = fixplanes; + } + else + if( pm_keymatch(argv[argn], "-mapfile", 4) ) { + if( ++argn >= argc ) + pm_error("-mapfile requires a value"); + mapfile = argv[argn]; + } + else + if( pm_keymatch(argv[argn], "-mmethod", 3) ) { + if( ++argn >= argc ) + pm_error("-mmethod requires a value"); + maskmethod = get_mask_type(argv[argn]); + switch( maskmethod ) { + case mskNone: + case mskHasMask: + case mskHasTransparentColor: + break; + default: + pm_error("This program does not know how to handle " + "masking method '%s'", + mskNAME[maskmethod]); + } + } + else + if( pm_keymatch(argv[argn], "-maskfile", 4) ) { + if( ++argn >= argc ) + pm_error("-maskfile requires a value"); + maskfile = pm_openr(argv[argn]); + if( maskmethod == mskNone ) + maskmethod = mskHasMask; + } + else + if( pm_keymatch(argv[argn], "-transparent", 3) ) { + if( ++argn >= argc ) + pm_error("-transparent requires a value"); + transpname = argv[argn]; + if( maskmethod == mskNone ) + maskmethod = mskHasTransparentColor; + } + else + if( pm_keymatch(argv[argn], "-sortcmap", 5) ) + sortcmap = 1; + else + if( pm_keymatch(argv[argn], "-cmaponly", 3) ) { + forcemode = MODE_CMAP; + } + else + if( pm_keymatch(argv[argn], "-lace", 2) ) { + slicesize = 2; + viewportmodes |= vmLACE; + gen_camg = 1; + } + else + if( pm_keymatch(argv[argn], "-nolace", 4) ) { + slicesize = 1; + viewportmodes &= ~vmLACE; + } + else + if( pm_keymatch(argv[argn], "-hires", 3) ) { + viewportmodes |= vmHIRES; + gen_camg = 1; + } + else + if( pm_keymatch(argv[argn], "-nohires", 5) ) + viewportmodes &= ~vmHIRES; + else + if( pm_keymatch(argv[argn], "-camg", 5) ) { + char *tail; + long value = 0L; + + if( ++argn >= argc ) + pm_error("-camg requires a value"); + value = strtol(argv[argn], &tail, 16); + /* TODO: should do some error checking here */ + viewportmodes |= value; + gen_camg = 1; + } + else + if( pm_keymatch(argv[argn], "-ecs", 2) ) { + maxplanes = ECS_MAXPLANES; + hamplanes = ECS_HAMPLANES; + } + else + if( pm_keymatch(argv[argn], "-aga", 3) ) { + maxplanes = AGA_MAXPLANES; + hamplanes = AGA_HAMPLANES; + } + else + if( pm_keymatch(argv[argn], "-hamplanes", 5) ) { + if( ++argn >= argc ) + pm_error("-hamplanes requires a value"); + hamplanes = get_int_val(argv[argn], argv[argn-1], 3, HAMMAXPLANES); + } + else + if( pm_keymatch(argv[argn], "-hambits", 5) ) { + if( ++argn >= argc ) + pm_usage("-hambits requires a value"); + hamplanes = + get_int_val(argv[argn], argv[argn-1], 3, HAMMAXPLANES-2) +2; + } + else + if( pm_keymatch(argv[argn], "-ham6", 5) ) { + hamplanes = ECS_HAMPLANES; + forcemode = MODE_HAM; + } + else + if( pm_keymatch(argv[argn], "-ham8", 5) ) { + hamplanes = AGA_HAMPLANES; + forcemode = MODE_HAM; + } + else + if( pm_keymatch(argv[argn], "-hammap", 5) ) { + if( ++argn >= argc ) + pm_error("-hammap requires a value"); + hammapmode = get_hammap_mode(argv[argn]); + } + else + if( pm_keymatch(argv[argn], "-hamif", 5) ) + ifmode = MODE_HAM; + else + if( pm_keymatch(argv[argn], "-nohamif", 7) ) { + if( ifmode == MODE_HAM ) + ifmode = MODE_NONE; + } + else + if( pm_keymatch(argv[argn], "-hamforce", 4) ) + forcemode = MODE_HAM; + else + if( pm_keymatch(argv[argn], "-nohamforce", 6) ) { + if( forcemode == MODE_HAM ) + forcemode = MODE_NONE; + } + else + if( pm_keymatch(argv[argn], "-24if", 4) ) { + ifmode = MODE_DEEP; + deepbits = 8; + } + else + if( pm_keymatch(argv[argn], "-no24if", 6) ) { + if( ifmode == MODE_DEEP ) + ifmode = MODE_NONE; + } + else + if( pm_keymatch(argv[argn], "-24force", 3) ) { + forcemode = MODE_DEEP; + deepbits = 8; + } + else + if( pm_keymatch(argv[argn], "-no24force", 5) ) { + if( forcemode == MODE_DEEP ) + forcemode = MODE_NONE; + } + else + if( pm_keymatch(argv[argn], "-deepplanes", 6) ) { + if( ++argn >= argc ) + pm_error("-deepplanes requires a value"); + deepbits = get_int_val(argv[argn], argv[argn-1], 3, 3*MAXPLANES); + if( deepbits % 3 != 0 ) + pm_error("option \"%s\" argument value must be divisible by 3", + argv[argn-1]); + deepbits /= 3; + } + else + if( pm_keymatch(argv[argn], "-deepbits", 6) ) { + if( ++argn >= argc ) + pm_error("-deepbits requires a value"); + deepbits = get_int_val(argv[argn], argv[argn-1], 1, MAXPLANES); + } + else + if( pm_keymatch(argv[argn], "-deepif", 6) ) + ifmode = MODE_DEEP; + else + if( pm_keymatch(argv[argn], "-nodeepif", 8) ) { + if( ifmode == MODE_DEEP ) + ifmode = MODE_NONE; + } + else + if( pm_keymatch(argv[argn], "-deepforce", 5) ) + forcemode = MODE_DEEP; + else + if( pm_keymatch(argv[argn], "-nodeepforce", 7) ) { + if( forcemode == MODE_DEEP ) + forcemode = MODE_NONE; + } + else + if( pm_keymatch(argv[argn], "-dcif", 4) ) + ifmode = MODE_DCOL; + else + if( pm_keymatch(argv[argn], "-nodcif", 6) ) { + if( ifmode == MODE_DCOL ) + ifmode = MODE_NONE; + } + else + if( pm_keymatch(argv[argn], "-dcforce", 4) ) + forcemode = MODE_DCOL; + else + if( pm_keymatch(argv[argn], "-nodcforce", 6) ) { + if( forcemode == MODE_DCOL ) + forcemode = MODE_NONE; + } + else + if( pm_keymatch(argv[argn], "-dcbits", 4) || + pm_keymatch(argv[argn], "-dcplanes", 4) ) { + if( argc - argn < 4 ) + pm_error("-dcbits requires 4 arguments"); + dcol.r = get_int_val(argv[argn+1], argv[argn], 1, MAXPLANES); + dcol.g = get_int_val(argv[argn+2], argv[argn], 1, MAXPLANES); + dcol.b = get_int_val(argv[argn+3], argv[argn], 1, MAXPLANES); + argn += 3; + } + else + if( pm_keymatch(argv[argn], "-normal", 4) ) { + ifmode = forcemode = MODE_NONE; + compmethod = DEF_COMPRESSION; + } + else + if( pm_keymatch(argv[argn], "-compress", 4) ) { + compr_force = 1; + if( compmethod == cmpNone ) +#if DEF_COMPRESSION == cmpNone + compmethod = cmpByteRun1; +#else + compmethod = DEF_COMPRESSION; +#endif + } + else + if( pm_keymatch(argv[argn], "-nocompress", 4) ) { + compr_force = 0; + compmethod = cmpNone; + } + else + if( pm_keymatch(argv[argn], "-cmethod", 4) ) { + if( ++argn >= argc ) + pm_error("-cmethod requires a value"); + compmethod = get_compr_method(argv[argn]); + } + else + if( pm_keymatch(argv[argn], "-floyd", 3) || + pm_keymatch(argv[argn], "-fs", 3) ) + floyd = 1; + else + if( pm_keymatch(argv[argn], "-nofloyd", 5) || + pm_keymatch(argv[argn], "-nofs", 5) ) + floyd = 0; + else + if( pm_keymatch(argv[argn], "-annotation", 3) ) { + if( ++argn >= argc ) + pm_error("-annotation requires a value"); + anno_chunk = argv[argn]; + } + else + if( pm_keymatch(argv[argn], "-author", 3) ) { + if( ++argn >= argc ) + pm_error("-author requires a value"); + auth_chunk = argv[argn]; + } + else + if( pm_keymatch(argv[argn], "-copyright", 4) ) { + if( ++argn >= argc ) + pm_error("-copyright requires a value"); + copyr_chunk = argv[argn]; + } + else + if( pm_keymatch(argv[argn], "-name", 3) ) { + if( ++argn >= argc ) + pm_error("-name requires a value"); + name_chunk = argv[argn]; + } + else + if( pm_keymatch(argv[argn], "-text", 3) ) { + if( ++argn >= argc ) + pm_error("-text requires a value"); + text_chunk = argv[argn]; + } + else + pm_error("invalid option: %s", argv[argn]); + ++argn; + } + + if( argn < argc ) { + ifP = pm_openr(argv[argn]); + ++argn; + } + else + ifP = stdin; + + if( argn != argc ) + pm_error("Program takes no arguments."); + + mode = forcemode; + switch(forcemode) { + case MODE_HAM: + if (hammapmode == HAMMODE_RGB4 || hammapmode == HAMMODE_RGB5) + init_read(ifP, &cols, &rows, &maxval, &format, 1); + else + init_read(ifP, &cols, &rows, &maxval, &format, 0); + break; + case MODE_DCOL: + case MODE_DEEP: + mapfile = NULL; + init_read(ifP, &cols, &rows, &maxval, &format, 0); + break; + case MODE_RGB8: + mapfile = NULL; + init_read(ifP, &cols, &rows, &maxval, &format, 0); + break; + case MODE_RGBN: + mapfile = NULL; + init_read(ifP, &cols, &rows, &maxval, &format, 0); + break; + case MODE_CMAP: + /* Figure out the colormap. */ + pm_message("computing colormap..."); + colormap = ppm_mapfiletocolorrow(ifP, MAXCOLORS, &colors, + &cmapmaxval); + if (colormap == NULL) + pm_error("too many colors - try doing a 'pnmquant %d'", + MAXCOLORS); + pm_message("%d colors found", colors); + break; + default: + if (mapfile) + init_read(ifP, &cols, &rows, &maxval, &format, 0); + else { + init_read(ifP, &cols, &rows, &maxval, &format, 1); + /* read file into memory */ + pm_message("computing colormap..."); + colormap = + ppm_computecolorrow(pixels, cols, rows, MAXCOLORS, + &colors); + if (colormap) { + cmapmaxval = maxval; + pm_message("%d colors found", colors); + nPlanes = pm_maxvaltobits(colors-1); + if (fixplanes > nPlanes) + nPlanes = fixplanes; + } else { /* too many colors */ + mode = ifmode; + report_too_many_colors(ifmode, maxplanes, hamplanes, + dcol, deepbits ); + } + } + } + + if (mapfile) { + FILE * mapfp; + + pm_message("reading colormap file..."); + mapfp = pm_openr(mapfile); + colormap = ppm_mapfiletocolorrow(mapfp, MAXCOLORS, &colors, + &cmapmaxval); + pm_close(mapfp); + if (colormap == NULL) + pm_error("too many colors in mapfile for %d planes", maxplanes); + if (colors == 0) + pm_error("empty colormap??"); + pm_message("%d colors found in colormap", colors); + } + + if (maskmethod != mskNone) { + if (transpname) { + MALLOCVAR_NOFAIL(transpColor); + *transpColor = ppm_parsecolor(transpname, maxval); + } + if (maskfile) { + int maskrows; + pbm_readpbminit(maskfile, &maskcols, &maskrows, &maskformat); + if (maskcols < cols || maskrows < rows) + pm_error("maskfile too small - try scaling it"); + if (maskcols > cols || maskrows > rows) + pm_message("warning - maskfile larger than image"); + } else + maskcols = rows; + maskrow = pbm_allocrow(maskcols); + } + + if (mode != MODE_CMAP) { + unsigned int i; + MALLOCARRAY_NOFAIL(coded_rowbuf, RowBytes(cols)); + for (i = 0; i < RowBytes(cols); ++i) + coded_rowbuf[i] = 0; + if (DO_COMPRESS) + MALLOCARRAY_NOFAIL(compr_rowbuf, WORSTCOMPR(RowBytes(cols))); + } + + switch (mode) { + case MODE_HAM: + viewportmodes |= vmHAM; + ppm_to_ham(ifP, cols, rows, maxval, + colormap, colors, cmapmaxval, hamplanes); + break; + case MODE_DEEP: + ppm_to_deep(ifP, cols, rows, maxval, deepbits); + break; + case MODE_DCOL: + ppm_to_dcol(ifP, cols, rows, maxval, &dcol); + break; + case MODE_RGB8: + ppm_to_rgb8(ifP, cols, rows, maxval); + break; + case MODE_RGBN: + ppm_to_rgbn(ifP, cols, rows, maxval); + break; + case MODE_CMAP: + ppm_to_cmap(colormap, colors, cmapmaxval); + break; + default: + if (mapfile == NULL) + floyd = 0; /* would only slow down conversion */ + ppm_to_std(ifP, cols, rows, maxval, colormap, colors, + cmapmaxval, MAXCOLORS, nPlanes); + break; + } + pm_close(ifP); + return 0; +} diff --git a/converter/ppm/ppmtoleaf.c b/converter/ppm/ppmtoleaf.c new file mode 100644 index 00000000..fa5fdaf5 --- /dev/null +++ b/converter/ppm/ppmtoleaf.c @@ -0,0 +1,250 @@ +/* ppmtoleaf.c - read a portable pixmap and produce a ileaf img file + * + * Copyright (C) 1994 by Bill O'Donnell. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. This software is provided "as is" without express or + * implied warranty. + * + * Known problems: pgms are not converted to leaf grayscales; they are + * converted to 8-bit color images with all gray for colors. + * + */ + +#include <stdio.h> +#include "ppm.h" + +#define MAXCOLORS 256 + +pixel **pixels; +colorhash_table cht; + +int Red[MAXCOLORS], Green[MAXCOLORS], Blue[MAXCOLORS]; + + + +static int +colorstobpp( colors ) +int colors; +{ + int bpp; + + if ( colors <= 2 ) + bpp = 1; + else if ( colors <= 256 ) + bpp = 8; + else + bpp = 24; + return bpp; +} + + + +static int +GetPixel( x, y ) +int x, y; +{ + int color; + + color = ppm_lookupcolor( cht, &pixels[y][x] ); + return color; +} + + + +/* OK, this routine is not wicked efficient, but it is simple to follow + and it works. */ +static void +leaf_writeimg(width, height, depth, ncolors, maxval) +int width; +int height; +int depth; +int ncolors; +{ + int i,row,col; + + /* NOTE: byte order in ileaf img file fmt is big-endian, always! */ + + /* magic */ + fputc(0x89, stdout); + fputc(0x4f, stdout); + fputc(0x50, stdout); + fputc(0x53, stdout); + + /* version 4 */ + fputc(0x00, stdout); + fputc(0x04, stdout); + + /* h resolution: pixels/inch: say 75=screen resolution */ + fputc(0x00, stdout); + fputc(75, stdout); + + /* v resolution: pixels/inch: say 75=screen resolution */ + fputc(0x00, stdout); + fputc(75, stdout); + + /* unique id, could be anything */ + fputc(0x01, stdout); + fputc(0x02, stdout); + fputc(0x03, stdout); + fputc(0x04, stdout); + + /* x offset, always zero */ + fputc(0x00, stdout); + fputc(0x00, stdout); + + /* y offset, always zero */ + fputc(0x00, stdout); + fputc(0x00, stdout); + + /* dimensions 64k x 64k max */ + fputc((unsigned char)((width >> 8) & 0x00ff), stdout); + fputc((unsigned char)(width & 0x00ff), stdout); + fputc((unsigned char)((height >> 8) & 0x00ff), stdout); + fputc((unsigned char)(height & 0x00ff), stdout); + + /* depth */ + fputc(0x00, stdout); + fputc((unsigned char)depth, stdout); + + /* compressed, 0=uncompressed, 1=compressed */ + fputc(0x00, stdout); + + /* format, mono/gray = 0x20000000, RGB=0x29000000 */ + if (depth == 1) + fputc(0x20, stdout); + else + fputc(0x29, stdout); + fputc(0x00, stdout); + fputc(0x00, stdout); + fputc(0x00, stdout); + + /* colormap size */ + if (depth == 8) + { + fputc((unsigned char)((ncolors >> 8) & 0x00ff), stdout); + fputc((unsigned char)(ncolors & 0x00ff), stdout); + for (i=0; i<256; i++) + fputc((unsigned char) Red[i]*255/maxval, stdout); + for (i=0; i<256; i++) + fputc((unsigned char) Green[i]*255/maxval, stdout); + for (i=0; i<256; i++) + fputc((unsigned char) Blue[i]*255/maxval, stdout); + + for (row=0; row<height; row++) + { + for (col=0; col<width; col++) + fputc(GetPixel(col, row), stdout); + if (width % 2) + fputc(0x00, stdout); /* pad to 2-bytes */ + } + } else if (depth == 1) { + /* mono image */ + /* no colormap */ + fputc(0x00, stdout); + fputc(0x00, stdout); + + for (row=0; row<height; row++) + { + unsigned char bits = 0; + for (col=0; col<width; col++) { + if (GetPixel(col,row)) + bits |= (unsigned char) (0x0080 >> (col % 8)); + if (((col + 1) % 8) == 0) { + fputc(bits, stdout); + bits = 0; + } + } + if ((width % 8) != 0) + fputc(bits, stdout); + if ((width % 16) && (width % 16) <= 8) + fputc(0x00, stdout); /* 16 bit pad */ + } + } else { + /* no colormap, direct or true color (24 bit) image */ + fputc(0x00, stdout); + fputc(0x00, stdout); + + for (row=0; row<height; row++) + { + for (col=0; col<width; col++) + fputc(pixels[row][col].r * 255 / maxval, stdout); + if (width % 2) + fputc(0x00, stdout); /* pad to 2-bytes */ + for (col=0; col<width; col++) + fputc(pixels[row][col].g * 255 / maxval, stdout); + if (width % 2) + fputc(0x00, stdout); /* pad to 2-bytes */ + for (col=0; col<width; col++) + fputc(pixels[row][col].b * 255 / maxval, stdout); + if (width % 2) + fputc(0x00, stdout); /* pad to 2-bytes */ + } + } +} + + + +int +main( argc, argv ) +int argc; +char *argv[]; +{ + FILE *ifd; + int argn, rows, cols, colors, i, BitsPerPixel; + pixval maxval; + colorhist_vector chv; + const char * const usage = "[ppmfile]"; + + ppm_init(&argc, argv); + + argn = 1; + + if ( argn < argc ) + { + ifd = pm_openr( argv[argn] ); + argn++; + } else + ifd = stdin; + + if ( argn != argc ) + pm_usage( usage ); + + pixels = ppm_readppm( ifd, &cols, &rows, &maxval ); + + pm_close( ifd ); + + /* Figure out the colormap. */ + fprintf( stderr, "(Computing colormap..." ); + fflush( stderr ); + chv = ppm_computecolorhist( pixels, cols, rows, MAXCOLORS, &colors ); + if ( chv != (colorhist_vector) 0 ) + { + fprintf( stderr, " Done. %d colors found.)\n", colors ); + + for ( i = 0; i < colors; i++ ) + { + Red[i] = (int) PPM_GETR( chv[i].color ); + Green[i] = (int) PPM_GETG( chv[i].color ); + Blue[i] = (int) PPM_GETB( chv[i].color ); + } + BitsPerPixel = colorstobpp( colors ); + + /* And make a hash table for fast lookup. */ + cht = ppm_colorhisttocolorhash( chv, colors ); + ppm_freecolorhist( chv ); + } else { + BitsPerPixel = 24; + fprintf( stderr, " Done. 24-bit true color %d color image.)\n", colors ); + } + + leaf_writeimg(cols, rows, BitsPerPixel, colors, maxval); + + return( 0 ); +} + + + diff --git a/converter/ppm/ppmtolj.c b/converter/ppm/ppmtolj.c new file mode 100644 index 00000000..7ed814ed --- /dev/null +++ b/converter/ppm/ppmtolj.c @@ -0,0 +1,297 @@ +/* ppmtolj.c - convert a portable pixmap to an HP PCL 5 color image +** +** Copyright (C) 2000 by Jonathan Melvin (jonathan.melvin@heywood.co.uk) +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include <string.h> + +#include "ppm.h" + +static int compress_row_delta (unsigned char *op, unsigned char *prev_op, + unsigned char *cp, int bufsize); + +#define C_RESET "\033E" +#define C_PRESENTATION "\033*r%dF" +#define C_PRESENTATION_LOGICAL 0 +#define C_PRESENTATION_PHYSICAL 3 +#define C_GAMMA "\033*t%dI" +#define C_IMAGE_WIDTH "\033*r%dS" +#define C_IMAGE_HEIGHT "\033*r%dT" +#define C_DATA_PLANES "\033*r%dU" +#define C_TRANS_MODE "\033*b%dM" +#define C_TRANS_MODE_STD 0 /*compression modes*/ +#define C_TRANS_MODE_RLE 1 /*no good for rgb*/ +#define C_TRANS_MODE_TIFF 2 /*no good for rgb*/ +#define C_TRANS_MODE_DELTA 3 /*only on to use for rgb values*/ +#define C_CONFIG_IMAGE_DATA "\033*v6W" +#define C_SEND_ROW "\033*b%dW" +#define C_BEGIN_RASTER "\033*r%dA" +#define C_BEGIN_RASTER_CUR 1 +#define C_END_RASTER "\033*r%dC" +#define C_END_RASTER_UNUSED 0 +#define C_RESOLUTION "\033*t%dR" +#define C_RESOLUTION_300DPI 300 +#define C_MOVE_X "\033*p+%dX" +#define C_MOVE_Y "\033*p+%dY" +#define C_LEFT_MARGIN "\033*r%dA" +#define C_Y_OFFSET "\033*b%dY" + + +/* + * delta encoding. + */ +/* +op row buffer +prev_op previous row buffer +bufsize length of row +cp buffer for compressed data +*/ +static int +compress_row_delta(op, prev_op, cp, bufsize) +unsigned char *op, *prev_op, *cp; +int bufsize; +{ + int burstStart, burstEnd, burstCode, mustBurst, ptr, skip, skipped, code; + int deltaBufferIndex = 0; + if (memcmp(op, prev_op , bufsize/*rowBufferIndex*/) == 0) + return 0; /* exact match, no deltas required */ + + ptr = 0; + skipped = 0; + burstStart = -1; + burstEnd = -1; + mustBurst = 0; + while (ptr < bufsize/*rowBufferIndex*/) + { + skip = 0; + if (ptr == 0 || skipped == 30 || op[ptr] != prev_op[ptr] || + (burstStart != -1 && ptr == bufsize - 1)) + { + /* we want to output this byte... */ + if (burstStart == -1) + { + burstStart = ptr; + } + if (ptr - burstStart == 7 || ptr == bufsize - 1) + { + /* we have to output it now... */ + burstEnd = ptr; + mustBurst = 1; + } + } + else + { + /* duplicate byte, we can skip it */ + if (burstStart != -1) + { + burstEnd = ptr - 1; + mustBurst = 1; + } + skip = 1; + } + if (mustBurst) + { + burstCode = burstEnd - burstStart; /* 0-7 means 1-8 bytes follow */ + code = (burstCode << 5) | skipped; + cp[deltaBufferIndex++] = (char) code; + memcpy(cp+deltaBufferIndex, op+burstStart, burstCode + 1); + deltaBufferIndex += burstCode + 1; + burstStart = -1; + burstEnd = -1; + mustBurst = 0; + skipped = 0; + } + if (skip) + { + skipped ++; + } + ptr ++; + } + return deltaBufferIndex; +} + + +int main(int argc, char *argv[]) { + pixel * pixelrow; + FILE *ifp; + int argn, rows, cols, r, c, k; + pixval maxval; + unsigned char *obuf, *op, *cbuf, *previous_obuf; + int format; + int gamma = 0; + int mode = C_TRANS_MODE_STD; + int currentmode = 0; + int floating = 0; /* suppress the ``ESC & l 0 E'' ? */ + int resets = 3; /* bit mask for when to emit printer reset seq */ + + int resolution = C_RESOLUTION_300DPI; + + char CID[6] = { 0, 3, 0, 8, 8, 8 }; + /*data for the configure image data command*/ + + const char * const usage = "[-noreset][-float][-delta][-gamma <val>] [-resolution N] " + "[ppmfile]\n\tresolution = [75|100|150|300|600] (dpi)"; + + ppm_init( &argc, argv ); + + argn = 1; + while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' ) + { + if( pm_keymatch( argv[argn], "-resolution", 2 ) && argn + 1 < argc ) + { + ++argn; + if ( argn == argc || sscanf( argv[argn], "%d", &resolution ) != 1 ) + pm_usage( usage ); + } + else if ( pm_keymatch(argv[argn],"-gamma",2) && argn + 1 < argc ) + { + ++argn; + if ( sscanf( argv[argn], "%d",&gamma ) != 1 ) + pm_usage( usage ); + } + else if (pm_keymatch(argv[argn],"-delta",2)) + mode = C_TRANS_MODE_DELTA; + else if (pm_keymatch(argv[argn],"-float",2)) + floating = 1; + else if (pm_keymatch(argv[argn],"-noreset",2)) + resets = 0; + + else + pm_usage( usage ); + ++argn; + } + + if ( argn < argc ) + { + ifp = pm_openr( argv[argn] ); + ++argn; + } + else + ifp = stdin; + + if ( argn != argc ) + pm_usage( usage ); + + ppm_readppminit( ifp, &cols, &rows, &maxval, &format ); + pixelrow = ppm_allocrow( cols ); + + obuf = (unsigned char *) pm_allocrow(cols * 3, sizeof(unsigned char)); + cbuf = (unsigned char *) pm_allocrow(cols * 6, sizeof(unsigned char)); + if (mode == C_TRANS_MODE_DELTA) + { + previous_obuf = + (unsigned char *) pm_allocrow(cols * 3, sizeof(unsigned char)); + memset(previous_obuf, 0, cols * 3); + } + + if(resets & 1) + { + /* Printer reset. */ + printf(C_RESET); + } + + if(!floating) + { + /* Ensure top margin is zero */ + printf("\033&l0E"); + } + + /*Set Presentation mode*/ + (void) printf(C_PRESENTATION, C_PRESENTATION_PHYSICAL); + /* Set the resolution */ + (void) printf(C_RESOLUTION, resolution); + /* Set raster height*/ + (void) printf(C_IMAGE_HEIGHT, rows); + /* Set raster width*/ + (void) printf(C_IMAGE_WIDTH, cols); + /* set left margin to current x pos*/ + /*(void) printf(C_LEFT_MARGIN, 1);*/ + /* set the correct color mode */ + (void) printf(C_CONFIG_IMAGE_DATA); + (void) fwrite(CID, 1, 6, stdout); + /* Start raster graphics */ + (void) printf(C_BEGIN_RASTER, C_BEGIN_RASTER_CUR); /*posscale);*/ + /* set Y offset to 0 */ + (void) printf(C_Y_OFFSET, 0); +/* + if (xoff) + (void) printf(C_MOVE_X, xoff); + if (yoff) + (void) printf(C_MOVE_Y, yoff); +*/ + /* Set raster compression */ + (void) printf(C_TRANS_MODE, mode); + currentmode = mode; + + if(gamma) + (void) printf(C_GAMMA, gamma); + + for (r = 0; r < rows; r++) + { + ppm_readppmrow(ifp, pixelrow, cols, maxval, format); + + /* get a row of data with 3 bytes per pixel */ + for (c = 0, op = &obuf[-1]; c < cols; c++) + { + ++op; + *op = (PPM_GETR(pixelrow[c])*255)/maxval; + ++op; + *op = (PPM_GETG(pixelrow[c])*255)/maxval; + ++op; + *op = (PPM_GETB(pixelrow[c])*255)/maxval; + } + ++op; + k = op - obuf; /*size of row*/ + /*compress the row if required*/ + if(mode == C_TRANS_MODE_STD) + {/*no compression*/ + op = obuf; + } + + if(mode == C_TRANS_MODE_DELTA) + {/*delta compression*/ + int newmode = 0; + int deltasize = + compress_row_delta(obuf, previous_obuf, cbuf, cols*3); + if(deltasize >= k)/*normal is best?*/ + { + op = obuf; + } + else /*delta is best*/ + { + k = deltasize; + op = cbuf; + newmode = C_TRANS_MODE_DELTA; + } + memcpy(previous_obuf, obuf, cols*3); + + if(currentmode != newmode) + { + (void) printf(C_TRANS_MODE, newmode); + currentmode = newmode; + } + } + + (void) printf(C_SEND_ROW, k); + (void) fwrite(op, 1, k, stdout); + + } + + (void) printf(C_END_RASTER, C_END_RASTER_UNUSED); + if(resets & 2) + { + /* Printer reset. */ + printf(C_RESET); + } + pm_close( ifp ); + ppm_freerow(pixelrow); + + return 0; +} diff --git a/converter/ppm/ppmtomitsu.c b/converter/ppm/ppmtomitsu.c new file mode 100644 index 00000000..3934ae45 --- /dev/null +++ b/converter/ppm/ppmtomitsu.c @@ -0,0 +1,604 @@ +/* ppmtomitsu.c - read a portable pixmap and produce output for the +** Mitsubishi S340-10 Thermo-Sublimation Printer +** (or the S3410-30 parallel interface) +** +** Copyright (C) 1992,93 by S.Petra Zeidler +** Minor modifications by Ingo Wilken: +x** - mymalloc() and check_and_rotate() functions for often used +** code fragments. Reduces code size by a few KB. +** - use pm_error() instead of fprintf(stderr) +** - localized allocation of colorhastable +** +** This software was written for the Max Planck Institut fuer Radioastronomie, +** Bonn, Germany, Optical Interferometry group +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include <string.h> + +#include "pm_c_util.h" +#include "ppm.h" +#include "nstring.h" +#include "mallocvar.h" + +#include "mitsu.h" + + +#include <stdio.h> + +#define HASHSIZE 2048 +#define myhash(x) ((PPM_GETR(x)*3 + PPM_GETG(x)*5 + PPM_GETB(x)*7) % HASHSIZE) + +typedef struct hashinfo { + pixel color; + long flag; + struct hashinfo *next; +} hashinfo; + +#ifdef __STDC__ +static void lineputinit(int cols, int rows, int sharpness, int enlarge, int + copy, struct mediasize medias); +static void frametransferinit(int cols, int rows, int sharpness, int enlarge, + int copy, struct mediasize medias); +static void lookuptableinit(int sharpness, int enlarge, int copy, + struct mediasize medias); +static void lookuptabledata(int cols, int rows, int enlarge, + struct mediasize medias); +static void check_and_rotate(int cols, int rows, int enlarge, + struct mediasize medias); +#define CONST const +#else /*__STDC__*/ +static int lineputinit(); +static int lookuptableinit(); +static int lookuptabledata(); +static int frametransferinit(); +static int check_and_rotate(); +#define CONST +#endif + +#define cmd(arg) fputc((arg), stdout) +#define datum(arg) fputc((char)(arg), stdout) +#define data(arg,num) fwrite((arg), sizeof(char), (num), stdout) + + +#ifdef __STDC__ +int main(int argc, char *argv[] ) +#else +int main( argc, argv ) + int argc; + char* argv[]; +#endif + { + FILE *ifp; + /*hashinfo colorhashtable[HASHSIZE];*/ + struct hashinfo *hashrun; + pixel *xP; + int argn; + bool dpi300; + int cols, rows, format, col, row; + int sharpness, enlarge, copy, tiny; + pixval maxval; + struct mediasize medias; + char media[16]; + const char * const usage = "[-sharpness <1-4>] [-enlarge <1-3>] [-media <a,a4,as,a4s>] [-copy <1-9>] [-tiny] [-dpi300] [ppmfile]"; + + ppm_init(&argc, argv); + + dpi300 = FALSE; + argn = 1; + sharpness = 32; + enlarge = 1; + copy = 1; + memset(media, '\0', 16); + tiny = FALSE; + + /* check for flags */ + while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0') { + if (pm_keymatch(argv[argn], "-sharpness", 2)) { + ++argn; + if (argn == argc || sscanf(argv[argn], "%d", &sharpness) != 1) + pm_usage(usage); + else if (sharpness < 1 || sharpness > 4) + pm_usage(usage); + } + else if (pm_keymatch(argv[argn], "-enlarge", 2)) { + ++argn; + if (argn == argc || sscanf(argv[argn], "%d", &enlarge) != 1) + pm_usage(usage); + else if (enlarge < 1 || enlarge > 3) + pm_usage(usage); + } + else if (pm_keymatch(argv[argn], "-media", 2)) { + ++argn; + if (argn == argc || sscanf(argv[argn], "%15s", media) < 1) + pm_usage(usage); + else if (TOUPPER(media[0]) != 'A') + pm_usage(usage); + } + else if (pm_keymatch(argv[argn], "-copy", 2)) { + ++argn; + if (argn == argc || sscanf(argv[argn], "%d", ©) != 1) + pm_usage(usage); + else if (copy < 1 || copy > 9) + pm_usage(usage); + } + else if (pm_keymatch(argv[argn], "-dpi300", 2)) + dpi300 = TRUE; + else if (pm_keymatch(argv[argn], "-tiny", 2)) + tiny = TRUE; + else + pm_usage(usage); + ++argn; + } + + if (argn < argc) { + ifp = pm_openr(argv[argn]); + ++argn; + } + else + ifp = stdin; + + if (argn != argc) + pm_usage(usage); + + if (TOUPPER(media[0]) == 'A') + switch (TOUPPER(media[1])) { + case 'S': + medias = MSize_AS; + break; + case '4': + if(TOUPPER(media[2]) == 'S') + medias = MSize_A4S; + else { + medias = MSize_A4; + } + break; + default: + medias = MSize_A; + } + else + medias = MSize_User; + + if (dpi300) { + medias.maxcols *= 2; + medias.maxrows *= 2; + } + + if (tiny) { + pixel *pixelrow; + char *redrow, *greenrow, *bluerow; + + ppm_readppminit(ifp, &cols, &rows, &maxval, &format); + pixelrow = (pixel *) ppm_allocrow(cols); + MALLOCARRAY_NOFAIL(redrow, cols); + MALLOCARRAY_NOFAIL(greenrow, cols); + MALLOCARRAY_NOFAIL(bluerow, cols); + lineputinit(cols, rows, sharpness, enlarge, copy, medias); + + for ( row = 0; row < rows; ++row ) { + ppm_readppmrow(ifp, pixelrow, cols, maxval, format); + switch(PPM_FORMAT_TYPE(format)) { + /* color */ + case PPM_TYPE: + for (col = 0, xP = pixelrow; col < cols; col++, xP++) { + /* First red. */ + redrow[col] = PPM_GETR(*xP); + /* Then green. */ + greenrow[col] = PPM_GETG(*xP); + /* And blue. */ + bluerow[col] = PPM_GETB(*xP); + } + data(redrow, cols); + data(greenrow, cols); + data(bluerow, cols); + break; + /* grayscale */ + default: + for (col = 0, xP = pixelrow; col < cols; col++, xP++) + bluerow[col] = PPM_GETB(*xP); + data(bluerow, cols); + data(bluerow, cols); + data(bluerow, cols); + break; + } + } + pm_close(ifp); + } + else { + pixel **pixelpic; + int colanz, colval; + int i; + colorhist_vector table; + + ppm_readppminit( ifp, &cols, &rows, &maxval, &format ); + pixelpic = ppm_allocarray( cols, rows ); + for (row = 0; row < rows; row++) + ppm_readppmrow( ifp, pixelpic[row], cols, maxval, format ); + pm_close(ifp); + + /* first check wether we can use the lut transfer */ + + table = ppm_computecolorhist(pixelpic, cols, rows, MAXLUTCOL+1, + &colanz); + if (table != NULL) { + hashinfo *colorhashtable; + + MALLOCARRAY_NOFAIL(colorhashtable, HASHSIZE); + for (i=0; i<HASHSIZE; i++) { + colorhashtable[i].flag = -1; + colorhashtable[i].next = NULL; + } + + /* we can use the lookuptable */ + pm_message("found %d colors - using the lookuptable-method", + colanz); + lookuptableinit(sharpness, enlarge, copy, medias); + switch(PPM_FORMAT_TYPE(format)) { + /* color */ + case PPM_TYPE: + for (colval=0; colval<colanz; colval++) { + cmd('$'); + datum(colval); + datum(PPM_GETR((table[colval]).color)); + datum(PPM_GETG((table[colval]).color)); + datum(PPM_GETB((table[colval]).color)); + + hashrun = &colorhashtable[myhash((table[colval]).color)]; + if (hashrun->flag == -1) { + hashrun->color = (table[colval]).color; + hashrun->flag = colval; + } + else { + while (hashrun->next != NULL) + hashrun = hashrun->next; + MALLOCVAR_NOFAIL(hashrun->next); + hashrun = hashrun->next; + hashrun->color = (table[colval]).color; + hashrun->flag = colval; + hashrun->next = NULL; + } + } + break; + /* other */ + default: + for (colval=0; colval<colanz; colval++) { + cmd('$'); + datum(colval); + datum(PPM_GETB((table[colval]).color)); + datum(PPM_GETB((table[colval]).color)); + datum(PPM_GETB((table[colval]).color)); + + hashrun = &colorhashtable[myhash((table[colval]).color)]; + if (hashrun->flag == -1) { + hashrun->color = (table[colval]).color; + hashrun->flag = colval; + } + else { + while (hashrun->next != NULL) + hashrun = hashrun->next; + MALLOCVAR_NOFAIL(hashrun->next); + hashrun = hashrun->next; + hashrun->color = (table[colval]).color; + hashrun->flag = colval; + hashrun->next = NULL; + } + } + } + lookuptabledata(cols, rows, enlarge, medias); + for (row=0; row<rows; row++) { + xP = pixelpic[row]; + for (col=0; col<cols; col++, xP++) { + hashrun = &colorhashtable[myhash(*xP)]; + while (!PPM_EQUAL((hashrun->color), *xP)) + if (hashrun->next != NULL) + hashrun = hashrun->next; + else { + pm_error("you just found a lethal bug."); + } + datum(hashrun->flag); + } + } + free(colorhashtable); + } + else { + /* $#%@^!& no lut possible, so send the pic as 24bit */ + pm_message("found too many colors for fast lookuptable mode"); + frametransferinit(cols, rows, sharpness, enlarge, copy, medias); + switch(PPM_FORMAT_TYPE(format)) { + /* color */ + case PPM_TYPE: + COLORDES(RED); + DATASTART; /* red coming */ + for (row=0; row<rows; row++) { + xP = pixelpic[row]; + for (col=0; col<cols; col++, xP++) + datum(PPM_GETR(*xP)); + } + COLORDES(GREEN); + DATASTART; /* green coming */ + for (row=0; row<rows; row++) { + xP = pixelpic[row]; + for (col=0; col<cols; col++, xP++) + datum(PPM_GETG(*xP)); + } + COLORDES(BLUE); + DATASTART; /* blue coming */ + for (row=0; row<rows; row++) { + xP = pixelpic[row]; + for (col=0; col<cols; col++, xP++) + datum(PPM_GETB(*xP)); + } + break; + /* grayscale */ + default: + COLORDES(RED); + DATASTART; /* red coming */ + for (row=0; row<rows; row++) { + xP = pixelpic[row]; + for (col=0; col<cols; col++, xP++) + datum(PPM_GETB(*xP)); + } + COLORDES(GREEN); + DATASTART; /* green coming */ + for (row=0; row<rows; row++) { + xP = pixelpic[row]; + for (col=0; col<cols; col++, xP++) + datum(PPM_GETB(*xP)); + } + COLORDES(BLUE); + DATASTART; /* blue coming */ + for (row=0; row<rows; row++) { + xP = pixelpic[row]; + for (col=0; col<cols; col++, xP++) + datum(PPM_GETB(*xP)); + } + } + } + } + PRINTIT; + exit(0); +} + +#ifdef __STDC__ +static void lineputinit(int cols, int rows, + int sharpness, int enlarge, int copy, + struct mediasize medias) +#else /*__STDC__*/ +static int lineputinit(cols, rows, sharpness, enlarge, copy, medias) + int cols, rows; + int sharpness, enlarge, copy; + struct mediasize medias; +#endif /*__STDC__*/ +{ + ONLINE; + CLRMEM; + MEDIASIZE(medias); + + switch (enlarge) { + case 2: + HENLARGE(ENLARGEx2); /* enlarge horizontal */ + VENLARGE(ENLARGEx2); /* enlarge vertical */ + break; + case 3: + HENLARGE(ENLARGEx3); /* enlarge horizontal */ + VENLARGE(ENLARGEx3); /* enlarge vertical */ + break; + default: + HENLARGE(NOENLARGE); /* enlarge horizontal */ + VENLARGE(NOENLARGE); /* enlarge vertical */ + } + + COLREVERSION(DONTREVERTCOLOR); + NUMCOPY(copy); + + HOFFINCH('\000'); + VOFFINCH('\000'); + CENTERING(DONTCENTER); + + TRANSFERFORMAT(LINEORDER); + COLORSYSTEM(RGB); + GRAYSCALELVL(BIT_8); + + switch (sharpness) { /* sharpness :-) */ + case 0: + SHARPNESS(SP_NONE); + break; + case 1: + SHARPNESS(SP_LOW); + break; + case 2: + SHARPNESS(SP_MIDLOW); + break; + case 3: + SHARPNESS(SP_MIDHIGH); + break; + case 4: + SHARPNESS(SP_HIGH); + break; + default: + SHARPNESS(SP_USER); + } + check_and_rotate(cols, rows, enlarge, medias); + DATASTART; + return; +} + +#ifdef __STDC__ +static void lookuptableinit(int sharpness, int enlarge, int copy, + struct mediasize medias) +#else /*__STDC__*/ +static int lookuptableinit(sharpness, enlarge, copy, medias) + int sharpness, enlarge, copy; + struct mediasize medias; +#endif /*__STDC__*/ +{ + ONLINE; + CLRMEM; + MEDIASIZE(medias); + + switch (enlarge) { + case 2: + HENLARGE(ENLARGEx2); /* enlarge horizontal */ + VENLARGE(ENLARGEx2); /* enlarge vertical */ + break; + case 3: + HENLARGE(ENLARGEx3); /* enlarge horizontal */ + VENLARGE(ENLARGEx3); /* enlarge vertical */ + break; + default: + HENLARGE(NOENLARGE); /* enlarge horizontal */ + VENLARGE(NOENLARGE); /* enlarge vertical */ + } + + COLREVERSION(DONTREVERTCOLOR); + NUMCOPY(copy); + + HOFFINCH('\000'); + VOFFINCH('\000'); + CENTERING(DONTCENTER); + + TRANSFERFORMAT(LOOKUPTABLE); + + switch (sharpness) { /* sharpness :-) */ + case 0: + SHARPNESS(SP_NONE); + break; + case 1: + SHARPNESS(SP_LOW); + break; + case 2: + SHARPNESS(SP_MIDLOW); + break; + case 3: + SHARPNESS(SP_MIDHIGH); + break; + case 4: + SHARPNESS(SP_HIGH); + break; + default: + SHARPNESS(SP_USER); + } + + LOADLOOKUPTABLE; + return; +} + +#ifdef __STDC__ +static void lookuptabledata(int cols, int rows, int enlarge, + struct mediasize medias) +#else /*__STDC__*/ +static int lookuptabledata(cols, rows, enlarge, medias) + int rows, cols; + int enlarge; + struct mediasize medias; +#endif /*__STDC__*/ +{ + DONELOOKUPTABLE; + check_and_rotate(cols, rows, enlarge, medias); + DATASTART; + return; +} + +#ifdef __STDC__ +static void frametransferinit(int cols, int rows, int sharpness, + int enlarge, int copy, struct mediasize medias) +#else +static int frametransferinit(cols, rows, sharpness, enlarge, copy, medias) + + int rows, cols; + int sharpness, enlarge, copy; + struct mediasize medias; +#endif +{ + ONLINE; + CLRMEM; + MEDIASIZE(medias); + + switch (enlarge) { + case 2: + HENLARGE(ENLARGEx2); /* enlarge horizontal */ + VENLARGE(ENLARGEx2); /* enlarge vertical */ + break; + case 3: + HENLARGE(ENLARGEx3); /* enlarge horizontal */ + VENLARGE(ENLARGEx3); /* enlarge vertical */ + break; + default: + HENLARGE(NOENLARGE); /* enlarge horizontal */ + VENLARGE(NOENLARGE); /* enlarge vertical */ + } + + COLREVERSION(DONTREVERTCOLOR); + NUMCOPY(copy); + + HOFFINCH('\000'); + VOFFINCH('\000'); + CENTERING(DONTCENTER); + + TRANSFERFORMAT(FRAMEORDER); + COLORSYSTEM(RGB); + GRAYSCALELVL(BIT_8); + + switch (sharpness) { /* sharpness :-) */ + case 0: + SHARPNESS(SP_NONE); + break; + case 1: + SHARPNESS(SP_LOW); + break; + case 2: + SHARPNESS(SP_MIDLOW); + break; + case 3: + SHARPNESS(SP_MIDHIGH); + break; + case 4: + SHARPNESS(SP_HIGH); + break; + default: + SHARPNESS(SP_USER); + } + check_and_rotate(cols, rows, enlarge, medias); + return; +} + + +#ifdef __STDC__ +static void +check_and_rotate(int cols, int rows, int enlarge, struct mediasize medias) +#else +static int +check_and_rotate(cols, rows, enlarge, medias) + int cols, rows, enlarge; + struct mediasize medias; +#endif +{ + if (cols > rows) { + ROTATEIMG(DOROTATE); /* rotate image */ + if (enlarge*rows > medias.maxcols || enlarge*cols > medias.maxrows) { + pm_error("Image too large, MaxPixels = %d x %d", medias.maxrows, medias.maxcols); + } + HPIXELS(cols); + VPIXELS(rows); + HPIXELSOFF((medias.maxcols/enlarge - rows)/2); + VPIXELSOFF((medias.maxrows/enlarge - cols)/2); + pm_message("rotating image for output"); + } + else { + ROTATEIMG(DONTROTATE); + if (enlarge*rows > medias.maxrows || enlarge*cols > medias.maxcols) { + pm_error("Image too large, MaxPixels = %d x %d", medias.maxrows, medias.maxcols); + } + HPIXELS(cols); + VPIXELS(rows); + HPIXELSOFF((medias.maxcols/enlarge - cols)/2); + VPIXELSOFF((medias.maxrows/enlarge - rows)/2); + } +} + diff --git a/converter/ppm/ppmtompeg/BUGS b/converter/ppm/ppmtompeg/BUGS new file mode 100644 index 00000000..64269dfb --- /dev/null +++ b/converter/ppm/ppmtompeg/BUGS @@ -0,0 +1,43 @@ +Known BUGS: + +1. <fixed> + +2. rate control and specifics files interact badly + +3. using REMOTE parallel execution: + `command` to find INPUT files does not work + SPECIFICS_FILE (CDL_FILE) + USER_DATA + dont work (should send the files over the socket, but do not + +4. using any sort of parallel code: + REFERENCE_FRAME DECODED + does not work + +5. Cannot use both STDIN and CDL_FILEs (ok, since it doesnt make sense...) + +6. <fixed> + +7. Patterns of BBI... seem to cause problems for parallel encoding. + I believe the bugs are fixed for sequential, but if you hit one, let us + know! + +8. Sometimes parallel encoding results in a zero size being written into + the stream. To fix, specify SIZE of the actual size. + +8.1 (related? minor, anyway) in parallel is also writes the Quant matrix + even when it is the default one + +9. It is unclear (to us anyway) if we should generate Y's from 0..255 or 16..224 + +10. <fixed> + +11. <fixed> + +12. reading JPEG from stdin, the encoder will sometimes stop before the end +of the stream (if the images are smaller than half the buffer size). + +13. Sometimes INPUT_CONVERT * is not good (requires "cat *"), in a parallel +case reported by a user it was needed. + +14. Parallel encoding often generates improper temporal_ref's... diff --git a/converter/ppm/ppmtompeg/CHANGES b/converter/ppm/ppmtompeg/CHANGES new file mode 100644 index 00000000..fffa7a65 --- /dev/null +++ b/converter/ppm/ppmtompeg/CHANGES @@ -0,0 +1,180 @@ +Changes chronology +------------------ +1.5b Release August 1995 + - added date to default USER_DATA string + - made prototypes happier + - renamed SPECIFICS_FILE to CDL_FILE + - fixed bit_rate param in constrained param setting (was too restrictive) + - fixed obscure P-block motion vector bug + - added a file name replication: + foo.ppm [1-10] will make 10 copies of foo.ppm + - generalized fopen calls to specify binary mode (MSDOG) + - generalized times calls into elapsed_time to handle different library routines + - fixed motion vector bug in bframes; skip block bug + - fixed minor bugs in parallel, mpeg, main, iframe, param, .... + - accepts extra whitespace at the end of lines in parameter files + - "PIXEL FULL" bug in B-frames fixed (caused ghosting) + - B-blocks "can I skip" rutine now looks at color too (more ghosting problems) + - switched from JPEG4 to JPEG5 (if they crash for you, + do a -DJPEG4 for the encoder, and get jpeg4.tar.gz off our site) + - combine server (parallel) waits longer for files to exist ( + thanks to apian@ise.fhg.de) + - killing the parallel server kills the clients!!!! (apian@ise.fhg.de) + +1.5 Release 8 May 1995 Changes since version 1.3 release (to 1.5) += BUG FIXES + - removed SetBlocksPerSlice() from GOPStoMPEG + - don't access bb after freeing + - non-integer frame rates now work for all machines + - fixed parsing of -mv_histogram + - fixed numPadding bug (file name problem) + - fixed full pixel assertation bug + - corrected ASPECT_RATIO bug (was forced to 1) + - buffer size is now set correctly + - complains when file is too small + - fixed bug with non-ranged INPUT_FILE names + - forced MB at start/end of slice in P-frame bug fixed + - Actually evaluates the constrained parameter settings + - fixed a Cr/Cb mixup in bframe.c + += NEW FEATURES + - rate control + - optional B search range different from P range + - can read images from stdin, allowing encoding on-the-fly + - Does Y files (no U or V) + - New way of specifying files `cmd`, like `ls jan-*-94` + - Can encode Abekas and Phillips style YUV formats + - gamma correction + - added -realquiet mode + - JMOVIE and JPEGv4 input format fully supported + - -float_dct uses the (slower) double precision floating point DCT + - automatically identifies itself in (sequence header) userdata + - enforced [0][0] entry of Intra Q-table to be 8 + - added Jim Boucher's resize code + - added -mse flag to print mean squared error on a per-block basis + - can specify Sequence header user data + - prints version number + - finds more skip blocks in GenBFrame + - can generate multiple sequence headers (every N GOPs) + += SPEED IMPROVEMENTS + + += MAINTENANCE + - removed mpeg_jrevdct_sparse(), since it wasn't used + - added CompileTests(); + - uses libpnmrw rather than entire pbmplus directory + - redid RCS + - reorganized [ipb]frame.c to make playing with rate controls easier + += PORTABILITY + - added #ifdef's, #ifndef's LINUX'es to ease porting + + +Changes from 1.2 to 1.3 +----------------------- += BUG FIXES + + deletes frame files when done with them in parallel version + + fixed bug in ComputeFrameTable + + completion time estimation now is closer to actual time + + modified ComputeSNR to avoid overflow of varOrig + + fixed bug that caused I/P/B times on HP's to be wrong + + fixed bug that made TwoLevel search out of the search window + + fixed bug in -quiet 0 + + fixed memory leak when using PNM files + + fixed bug: crashed when CPU time was 0 (very rare) + + fixed bug in -gop option + + fixed bug in AppendFiles() + += NEW FEATURES + + added FORCE_ENCODE_LAST_FRAME option (allows encoding of all frames) + + added PARALLEL_CHUNK_TAPER option + + added -bit_rate_info option to show bit rate per frame + + added -mv_histogram option to show motion vector histogram + + custom quantization tables allowed + + can specify frame rate, aspect ratio + += SPEED IMPROVEMENTS + + replaced most remaining binary multiplies/divides with shifts + (except those concerning negative numbers) + += MAINTENANCE + + got rid of & before array warning in block.c + + got rid of references to srandom, random in pbmplus.h + + undefine 'index' if defined as 'strchr' (since we use index as + variable name) + + modified frame type code to be more flexible + += PORTABILITY + + replaced all bzero() calls with memset() calls + + +1.2 Release 20 October 1993 + +Changes from 1.1 to 1.2 +----------------------- += BUG FIXES + + allow comments in PPM files + + first and last macroblocks in a slice may not be skipped macroblocks + + fixed YUV bug: should be able to handle dimensions which + aren't multiples of 16 + + fixed the separate I/O conversion for parallel NFS version + + no_frame_summary can now be last option + += NEW FEATURES + + using DECODED frames as reference frames in parallel + version now works + + implemented multiple I/O Servers + + FORCE_I_ALIGN added + + reorganized Server structure slightly (only I/O Servers + handle individual frame files on disk) + + add option to allow exact division of frames per processor + (PARALLEL_PERFECT) + += SPEED IMPROVEMENTS + + don't start Input server if not needed + += MAINTENANCE + + got rid of niceProcesses line in main.c + + changed write() to SafeWrite() in parallel.c + + commented stuff out after #endif's + + fixed prototype warnings (eliminated non-4-byte args) + + +1.1 Release August 1993 + +Changes from 1.0 to 1.1 +----------------------- += BUG FIXES + + fixed bug in parallel Server code which didn't htonl() frame + numbers correctly (so it didn't work on little-endian machines) + + fixed bug -- B-frames were always getting just 1 slice + + fixed listen() to use max connections allowed + + fixed noparallel.c so it doesn't complain during non-parallel + execution + += NEW FEATURES + + added level-2 p-search (exhaustive full, then half) + + now prints signal-to-noise ratio + + parallel code complains if absolute path not used for parameter + file + + changed single I/O server into separate Input and Output Servers + (and have Output Server combine frames in parallel) + += SPEED IMPROVEMENTS + + slight improvement in Mpost_QuantZigBlock (.10 ms/call down to .08 ms + on Sparc-10) + + improvement in speed of BlockifyFrame (45.8 ms/call down to 21.2 ms) + + improvement in ComputeMotionBlock (0.02 ms/call down to 0.01 ms) + + improvement in ComputeMotionLumBlock (0.04 ms/call down to 0.02 ms) + + improvement in LumAddMotionError (0.06 ms/call down to 0.05 ms) + (changed /2 to >>1) + += MAINTENANCE + + removed most memory.h references (except jrevdct) + + added CFLAGS lines in Makefile for SVR4.0 and SGI machines + + removed mproto.h + + got rid of printing connection times + + +1.0 Release July 1993 diff --git a/converter/ppm/ppmtompeg/COPYRIGHT b/converter/ppm/ppmtompeg/COPYRIGHT new file mode 100644 index 00000000..08216f34 --- /dev/null +++ b/converter/ppm/ppmtompeg/COPYRIGHT @@ -0,0 +1,20 @@ +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ diff --git a/converter/ppm/ppmtompeg/HISTORY b/converter/ppm/ppmtompeg/HISTORY new file mode 100644 index 00000000..c9f4932a --- /dev/null +++ b/converter/ppm/ppmtompeg/HISTORY @@ -0,0 +1,313 @@ +The entire ppmtojpeg directory was adapted by Bryan from the package +mpeg_encode-1.5b-src (subdirectory mpeg_encode) on March 30, 1999. The +program was called mpeg_encode in that package. It was dated August 16, +1995 and came from ftp://mm-ftp.cs.berkeley.edu/pub/multimedia/mpeg/ +encode/mpeg_encode-1.5b-src.tar.gz + +Changes for Netpbm include: + + - mpeg_encode recognizes two input formats: "PPM" and "PNM". For + PPM, mpeg_encode parses it itself. For PNM, it uses the Netpbm + PNM library routines. + + In the Netpbm version, PPM is gone and "PPM" is accepted as a + synonym for "PNM". For PNM, it uses the PPM (not PNM) Netpbm + library routines. + + - mpeg_encode PNM code is broken for maxval != 255 (divides by zero + if maxval < 255 and overly quantize if > 255 because PNMtoYUV() + uses an integer divisor = maxval/256). + +In November 2004, Bryan rewrote large portions of the code so that he +could read it easily enough to debug some problems. He eliminated +long subroutines, global variables, gotos, and the like. + +See the file LOGIC for some documentation on how the code works. + + +The following is the README from the aforementioned mpeg_encode subdirectory. + + + MPEG-1 Video Software Encoder + (Version 1.5; February 1, 1995) + + Lawrence A. Rowe, Kevin Gong, Eugene Hung, Ketan Patel, Steve Smoot + and Dan Wallach + Computer Science Division-EECS, Univ. of Calif. at Berkeley + +This directory contains the freely distributed Berkeley MPEG-1 Video +Encoder. The encoder implements the standard described in the ISO/IEC +International Standard 11172-2. The code has been compiled and tested +on the following platforms: + + DECstation 5000 and Alpha + HP PA-RISC (HP/UX 9.X) (i.e., HP 9000/7XX and 9000/3XX) + SGI Indigo running IRIX 5.0.1 + Sun Sparc (SunOS 4.X) + +In addition, Rainer Menes from the Technical University of Munich has +ported the encoder and decoder to the Macintosh. You can get that code +directly from him (menes@statistik.tu-muenchen.de), or from the +Berkeley FTP archive (mm-ftp.CS.Berkeley.EDU). If you decide to port +the code to a new architecture, please let us know so that we can +incorporate the changes into our sources. + +This directory contains everything required to build the encoder +and run it. We have included source code, makefiles, binaries +for selected platforms, documentation, and test data. Installation +instructions are given in the file named src/mpeg_encode/INSTALL. A man +page is given in the file doc/mpeg_encode.1. A detailed user +manual is provided in postscript format in the file doc/user-manual.ps. + +The encoder will accept any input file format as long as you provide +a script to convert the images to PPM, YUV, JPEG, or JMOVIE format. Input +file processing is described in the file doc/INPUT.FORMAT. Options to +control input file processing and compression parameters are specified in +a parameter file. Very little error processing is done when reading +this file. We suggest you start with the sample parameter file +examples/template.param and modify it. See also examples/default.param. + +The convert directory of Mpeg-Tools contains utilities you might find +useful including: + +programs to do PPM/YUV conversion and programs to convert Parallax +XVideo JPEG files into PPM, YUV, or JPEG frames. + +The motion vector search window can be specified, including half-pixel +block matching, in the parameter file. We have implemented several +search algorithms for P-frames including: 1) exhaustive search, +2) subsampled search, and 3) logarithmic search. We have also implemented +several alternatives for B-frame block matching including: 1) interpolate +best forward and best backward block, 2) find backward block for best +forward or vice-versa (called CROSS2), and 3) exhaustive cross product +(i.e., go out for coffee and a donut!). The search algorithms are controlled +by options in the parameters file. For tips on choosing the right search +technique, see the user manual. + +The encoder can be run on one computer (i.e., sequential) or on several +computers (i.e., parallel). Our goal is to produce a portable, easy-to-use +encoder that we can use to encode large volumes of video material for +the Berkeley VOD system (see paper VodsProp93.ps.Z on the FTP archive). +The parallelism is done on a sequence of pictures. In other words, you +can spawn one or more children to encode continuous runs pictures. The +uncompressed data can be accessed either through NFS or TCP sockets. +The goal is to allow you to encode using multiple processors, think +spare cycles on workstations, to speed up the encoding time. Although +performance depends on the speed of individual processors, the file system +and network, and the P/B frame search methods, we have encoded 3.75 +frames/second on 8 HP Snakes running in parallel as compared with 0.6 +frames/second on 1 Snake. These are preliminary results. We are continuing +to experiment with and tune the code. Instructions to run the parallel system +are given in the man page and the parallel.param example parameter file. + +We have done some tuning to produce a reasonable encoder, but there are +many more optimizations that we would like to incorporate. These +extensions are listed in the file doc/EXTENSIONS. If you succeed in +implementing any of them, please let us know! + +Send bug reports to: + +mpeg-bugs@CS.Berkeley.EDU + Problems, questions, or patches should be sent to this address. + +Anyone interested in providing financial support for this research or +discussing other aspects of this project should contact Larry Rowe at +Rowe@CS.Berkeley.EDU (+1 510-642-5117). + +This software is freely distributed. That means, you may use it for +any non-commercial purpose. However, patents are held by several companies +on various aspects of the MPEG video standard. Companies or individuals +who want to develop commercial products that include this code must +acquire licenses from these companies. For information on licensing, see +Appendix F in the standard. + +ACKNOWLEDGEMENTS: + +We gratefully thank Hewlett-Packard and Fujitsu who provided financial +support for this work. We also want to thank the following people and +organizations for their help: + + Jef Poskanzer who developed the pbmplus package. + --------- + Copyright (C) 1989, 1991 by Jef Poskanzer. + + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose and without fee is hereby granted, provided + that the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. This software is provided "as is" without express or + implied warranty. + --------- + + Eiichi Kowashi of Intel and Avideh Zakhor of U.C. Berkeley who + provided valuable suggestions on motion vector searching. + + Chad Fogg of the University of Washington who has helped us + understand many issues in MPEG coding and decoding. + + Rainer Menes of the Technical University of Munich who has ported the + the Berkeley MPEG encoder and decoder to the Macintosh, and he has + provided us with many suggestions to improve the code. + + Robert Safranek of ATT for comments, suggestions, and most of the + code for custom quantization tables. + + Jim Boucher of Boston University for jmovie2jpeg. + + The San Diego SuperComputing Center for providing facilities to + develop some of the code contained within. + + +This is the TODO file from the original Berkeley package: + + +TODO list for next release +-------------------------- + +Add option to do searches in Cr/Cb as well as Lum blocks +jpeg5! (below) +last-frame must be P/I error, and pattern interact badly +add gnuconfigure so the Makefile is cake +YUV correct (cf mail below) +fix the "ifdef BUGGY_CODE" code in bframe.c +Change sizing stuff so it works with non multiples of 16 +Does "RESIZE WxH" work? Seems to for PPm but not JPG. Fix it. Document it +mpeg_encode in parallel generates a "zero size warning" It should + exit(1) here, when not in parallel, and not warn when in parallel. + sometimes it generates a 0x0 MPEG! +Give time estimates for parallel encoding +Verify YUV file sizes with stat() call (when not STDIN) + +-------------------- + +YUV mail: +Please have a look on these few lines extracted from the ISO +mpeg2encode/readpic.c/read_ppm() available on +ftp.netcom.com:/pub/cf/cfogg/mpeg2 : + + ......... + + /* convert to YUV */ + + y = cr*r +cg*g +cb*b; + u = cu*(b-y); + v = cv*(r-y); + yp[j] = (219.0/256.0)*y + 16.5; /* nominal range : 16..235 */ + up[j] = (224.0/256.0)*u + 128.5; /* nominal range : 16..240 */ + vp[j] = (224.0/256.0)*v + 128.5; /* nominal range : 16..240 */ + ........... + +I think there is a slight misunderstanding in the Berkeley's mpeg1 codec about +what the YUV format looks like, exactly about how to translate from PPM to YUV +and vice versa : the dynamic of YUV format has to be reduced as described +above. Otherwise, on a full color display a Berkeley's MPEG bitstream has not +exactly the right colors if played by an ISO compliant player. + +Best regards + + + Thierry GODIN + +-------------------- +And just for fun, kevin's list: +To-do list (in no particular order) +---------- + - should delete decoded frame files when done with them + (need to make sure no one else needs it) + - port to CM5 + - try on Snake cluster, and other clusters (FDDI -- 100Mb/s) + - fix bug on snakes (look at header file for queue length) + - look at 24-bit display + - try having I/O server getting things in order, and asking Master + where to send them + - bug: closing connections at end (on DEC 5000) + - GOP on space in input list + - pnm convolve + - telescopic search + - include system layer + - update documentation + - show error images + - graphical interface (showing motion vectors, etc.) + - use DCT-space when computing error terms + - vary the q-scale according to the error term + - modify the program to have a finer-grained parallelism option -- we + can probably encode slices in parallel (this will only be useful if + we want to do a few B-frames using exhaustive search) + - make search technique stop at x.5, not x.0 + - pel aspect ratio in parameter file (2.4.3.2) + - skipped blocks options? + - recover from parallel machine errors? + - subsample B-search + - bug: decoded with 1 machine can freeze + - malloc bug: hello.param, with DECODED frames only + - portability: + times() function + popen/pclose + +Oh yes, I liked the concept of a spiral for your full search algorithm, +however I thought this code a little difficult to read. What about +using a look up table (pre generated at compile time) to generate +the coord offsets that would then spiral around the location in question? + + + change MAXPATHLEN to something else + + put ./ in test in Makefile + +Currently, the IPPBPBB sequence is fixed for the entire sequence. A later +version should probably either test for scene changes or allow the user to +specify them. + + + check all << and >> to make sure they are used properly + (note truncation of >>) + + allow variable bit rate + + allow size of video sequences to be set + + make REMOTE usage more clear + + fix bug: when I-frame only, and decoded, does a lot of extra work + + replace ZAG[i] with a pointer? (in quantization) + (and speed up by using 31 times the space (one for each + q-value) + + add interrupt handler to parallel encoder + + should pad black instead of truncating + - graph histogram of motion vectors + - allow new PATTERN statements inside of file listing + - put in run-time checks for size of int32, int16, etc. + - replace pnm crap with ppm.dwallach + - add option to allow different p-search in b-search + - allow -frames option along with parallel option (shouldn't be + too difficult) + - incorrect compression rates given if do -frames option + + - enforce: >Hmmm...I will have to look at the standard. I did find something earlier -- +>"forced updating" makes it illegal to have more than 132 P-frames without an +>I-frame. But "forced updating" does not disallow any number of consecutive +>B-frames. I'll have to check on limits for GOP lengths... + + - rectangular search windows + + - make parallel stats to 4 digits + +One of my friend just fixed the problem.....she +retype the whole parameter file and it is working +now. I think the problem was there were some +spacing problem......for example, if there +are some space is the line, when the +program read in....it just mess everything up. + +It really become a problem if there is space after the image name... +or even after the path name. + + Subject: may not want to >> 4 in postdct.c + +------------------------------------------------------------------------ +P.S. In the future versions (is one already been released), you could add +option for encoder to remove picture after encoding it & therefore saving +space on disk + option to sleep while picture it's waiting for is not done. +I've hacked this: (in readframe.c) + + while ((tempfile = fopen(fullFileName, "r")) == NULL) { + fprintf(stderr, "Cannot open '%s', retrying...\n", fullFileName); + sleep(120); + } + + fclose(tempfile); + +arijan@kette.fer.uni-lj.si diff --git a/converter/ppm/ppmtompeg/LOGIC b/converter/ppm/ppmtompeg/LOGIC new file mode 100644 index 00000000..8c19dc8d --- /dev/null +++ b/converter/ppm/ppmtompeg/LOGIC @@ -0,0 +1,126 @@ +This file describes (a little) how the program works. + +PARALLEL MODE +------------- + +In parallel mode, two processes run on the machine where you invoke +Ppmtompeg: the master server and the combine server. Sometimes, there's +a third process called a decode server, but I don't know what that's for +yet. + +Other processes, which are typically on separate machines, one per +machine, are called encoder processes. The code normally calls these +"child" processes and the documentation normally calls them "slave" +processes. We really should fix that. + +The master server's job is to feed work assignments to the encoder +processes. A work assignment is a set of frames to encode. + +The combine server's job is to take the encoded output from the encoder +processes and combine them into a single MPEG movie. + +The master process is the first process that exists. The first thing +it does is create the combine server. Then it creates the encoder +processes. It creates each encoder process with an initial +assignment. The master process then waits in a loop for an encoder +process to report that it has finished its assignment and gives a new +assignment to it. + +The master process and the combine process both have a listening TCP +port. They choose an available port number. When the master server +creates the combine server, it passes the master server's TCP listen +port number to the combine server. It then waits to hear from the +combine process what TCP listen port it has chosen. The combine +server connects to the master server's listen port and tells it. The +master server then passes to each encode server, as it creates it, +the TCP listen ports of both the master server and the combine server. + +When the combine server has processed all the frames, it shuts down. +It connects to the master server TCP listen port and tells the master +server it is shutting down. + +When an encoder server finishes a frame, it connects to the combine +server's TCP listen port and tells the combine server that the frame +is ready. + +When an encoder server finishes an assignment, it connects to the master +server TCP listen port and tells the master it is done, and receives over +the same connetion its next assignment. If there is no more work to do, +the master server instead tells the encoder server just to terminate. + +When the master server has told every encoder server to terminate, it +waits for the combine server to say that it has terminated. The master +server then terminates. + +To create the combine server, the master server forks and execs +'ppmtompeg' with the -output_server option. + +To create an encoder server, the master server invokes Ssh (or something +like it) and tells the remote shell to execute 'ppmtompeg' with the +-child option. + +The various processes communicate frame data via a shared filesystem +(e.g. NFS). An encoder server grabs the input frames from the shared +filesystem and writes the encoded frames to it. The combine server +grabs the encoded frames from it and writes the MPEG movie file to it. +The encoded frames go in individual files. The combine server deletes +each file after it has copied it to the MPEG movie file. Use the +KEEP_TEMP_FILES parameter file statement to keep them around for +debugging. + +There's also an alternative "remote I/O" method of communication that +doesn't require a shared filesystem. I haven't studied that. + +The master server and combine server code are in parallel.c. The +encoder server code is roughly the same as what the single encoder +process executes when you aren't running in parallel mode. + + +THE ENCODING PROCEDURE +---------------------- + +Encoding is done by ppmtompeg.c, which calls GenMPEGStream in mpeg.c to +do most of the work. + +The encoder goes through the input frames in movie sequence. But the +frames don't go into an MPEG stream in movie sequence. A B frame not +only can't be encoded without the subsequent I or P frame (reference +frame); it can't be decoded without it either. So in the MPEG stream, +B frames come immediately after the reference frame which comes +immediately after the B frames in the actual movie. + +When the input is from a sequential stream (the only way that's possible +today is with Standard Input), the encoder saves up B frame input until +after it has encoded and output the post reference frame. It does +this by chaining the input frames to the pre reference frame. We really +should clean this up so that a single module handles input of all kinds +and presents only a sequential stream to the encoder. The encoder should +not have special cases for the different types of input. That input +module would let the encoder ask from Frame N even if the input is +sequential. The input module would buffer frames from the sequential +input as necessary. + +In parallel mode, the combine server takes encoded frames from the +encoder servers as it comes. It can be in any order. The combine +server knows which frames go where in the output stream and outputs them +in the proper order. This is important for two reasons. First, the +encoder servers don't finish assignments in the same order in which they +get them. Second, when an encoder server gets an assignment that ends +with a B frame, the combine server has to wait for the encoder server +that has the next assignment to produce the post reference frame before +the combine server can do anything with that trailing B frame. + +genMPEGStream() encodes a frame at a time, but it inserts a stream +header and GOP headers where they belong. genMPEGStream() operates in +three modes: Whole Stream, GOP, and Just Frames. In Whole Stream +mode, it inserts all the required headers. In GOP mode, it inserts +GOP headers but not the stream header. In Just Frames mode, it +inserts no headers at all. In parallel mode, genMPEGStream generates +one frame per file, and only Just Frames mode makes any sense. We +really should fix this up so that the encoder always works in Just +Frames mode and a GOP builder module takes the encoder's output and +splices it with GOP headers and a stream builder module takes the GOP +builder's output and adds it to a stream header. In parallel mode, +the combine server would run the GOP builder and stream builder +modules. + diff --git a/converter/ppm/ppmtompeg/Makefile b/converter/ppm/ppmtompeg/Makefile new file mode 100644 index 00000000..5e923fee --- /dev/null +++ b/converter/ppm/ppmtompeg/Makefile @@ -0,0 +1,140 @@ +ifeq ($(SRCDIR)x,x) + SRCDIR = $(CURDIR)/../../.. + BUILDDIR = $(SRCDIR) +endif +SUBDIR = converter/ppm/ppmtompeg +VPATH=.:$(SRCDIR)/$(SUBDIR) + +include $(BUILDDIR)/Makefile.config + +ifeq ($(JPEGLIB),NONE) + # 'nojpeg' is a module that implements all the jpeg access routines as + # error messages that tell you we don't have jpeg capability + JPEG_MODULE = nojpeg + JPEGLIBX = +else + # 'jpeg' is a module that accesses J-movies via the JPEG library. + JPEG_MODULE = jpeg + JPEGLIBX = $(JPEGLIB) +endif + +INCLUDES = -I$(SRCDIR)/$(SUBDIR)/headers + +ifneq ($(JPEGHDR_DIR),NONE) + ifneq ($(JPEGHDR_DIR)x,x) + INCLUDES += -I$(JPEGHDR_DIR) + endif +endif + +# use -DLONG_32 iff +# 1) long's are 32 bits and +# 2) int's are not +# +# if you are using a non-ANSI compiler, then use: +# -DNON_ANSI_COMPILER +# +# one other option: +# -DHEINOUS_DEBUG_MODE +# + +MP_BASE_OBJS = mfwddct.o postdct.o huff.o bitio.o mheaders.o +MP_ENCODE_OBJS = iframe.o pframe.o bframe.o psearch.o bsearch.o block.o +MP_OTHER_OBJS = mpeg.o subsample.o param.o rgbtoycc.o \ + readframe.o combine.o jrevdct.o frame.o fsize.o frametype.o \ + specifics.o rate.o opts.o input.o gethostname.o +ifeq ($(OMIT_NETWORK),y) + MP_PARALLEL_OBJS = noparallel.o +else + MP_PARALLEL_OBJS = parallel.o psocket.o +endif +NONMAIN_OBJS = $(MP_BASE_OBJS) $(MP_OTHER_OBJS) $(MP_ENCODE_OBJS) \ + $(MP_PARALLEL_OBJS) $(JPEG_MODULE).o +OBJECTS = ppmtompeg.o $(NONMAIN_OBJS) +MERGE_OBJECTS = ppmtompeg.o2 $(NONMAIN_OBJS) +MP_INCLUDE = mproto.h mtypes.h huff.h bitio.h +MP_MISC = Makefile huff.table parse_huff.pl + +BINARIES = ppmtompeg +MERGEBINARIES = $(BINARIES) +SCRIPTS = + +.PHONY: all +all: ppmtompeg + +include $(SRCDIR)/Makefile.common + +ifeq ($(NEED_RUNTIME_PATH),Y) + LIBOPTR = -runtime +else + LIBOPTR = +endif + +ppmtompeg: $(OBJECTS) $(NETPBMLIB) $(LIBOPT) + $(LD) -o $@ $(LDFLAGS) \ + $(OBJECTS) $(shell $(LIBOPT) $(NETPBMLIB) $(LIBOPTR) $(JPEGLIBX)) \ + $(NETWORKLD) $(MATHLIB) $(LDLIBS) \ + $(RPATH) $(LADD) + +profile: $(OBJECTS) $(NETPBMLIB) $(LIBOPT) + $(LD) -o $@ -Bstatic -pg $(LDFLAGS) \ + $(OBJECTS) $(shell $(LIBOPT) $(NETPBMLIB) $(LIBOPTR) $(JPEGLIBX)) \ + $(NETWORKLD) $(MATHLIB) $(LDLIBS) \ + $(RPATH) $(LADD) + + +######### +# OTHER # +######### + +# +# Perl is necessary if you want to modify the Huffman RLE encoding table. +# +PERL = perl + +# The following stuff is for the Huffman encoding tables. It's commented-out +# because you probably don't want to change this. If you do, then uncommment +# it. +# +# huff.h: huff.c +# +# huff.c: parse_huff.pl huff.table +# $(PERL) parse_huff.pl huff.table + +MP_ALL_SRCS = $(patsubst %.o, %.c, $(MP_ALL_OBJS)) + +wc:; wc -l *.[ch] headers/*.h *.pl *.table +ci:; ci -l $(MP_ALL_SRCS) $(MP_INCLUDE) $(MP_MISC) +tags: $(MP_ALL_SRCS) + ctags -t $(MP_ALL_SRCS) + etags -f TAGS -t $(MP_ALL_SRCS) headers/*.h + +# +# WARNING: this assumes you're using GNU indent... +# +indent:; indent -T FILE -T int8 -T int16 -T int32 -T uint8 -T uint16 -T uint32 -T BitBucket -T MpegFrame -T Block -T FlatBlock $(MP_ALL_SRCS) + +spotless: clean + rm -f huff.c huff.h *.pure.a + +FORCE: + +# +# Copyright (c) 1995 The Regents of the University of California. +# All rights reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose, without fee, and without written agreement is +# hereby granted, provided that the above copyright notice and the following +# two paragraphs appear in all copies of this software. +# +# IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR +# DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT +# OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF +# CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +# ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO +# PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +# diff --git a/converter/ppm/ppmtompeg/bframe.c b/converter/ppm/ppmtompeg/bframe.c new file mode 100644 index 00000000..5dfb76d3 --- /dev/null +++ b/converter/ppm/ppmtompeg/bframe.c @@ -0,0 +1,1347 @@ +/*===========================================================================* + * bframe.c + * + * Procedures concerned with the B-frame encoding + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + + + +/*==============* + * HEADER FILES * + *==============*/ + +#include "all.h" +#include <sys/param.h> +#include <assert.h> + +#include "ppm.h" + +#include "mtypes.h" +#include "bitio.h" +#include "frames.h" +#include "prototypes.h" +#include "block.h" +#include "fsize.h" +#include "param.h" +#include "mheaders.h" +#include "postdct.h" +#include "rate.h" +#include "opts.h" +#include "specifics.h" + +extern int **bfmvHistogram; +extern int **bbmvHistogram; + +/*==================* + * STATIC VARIABLES * + *==================*/ + +static int32 totalTime = 0; +static int qscaleB; +static float totalSNR = 0.0; +static float totalPSNR = 0.0; + +static struct bframeStats { + unsigned int BSkipped; + unsigned int BIBits; + unsigned int BBBits; + unsigned int Frames; + unsigned int FrameBits; + unsigned int BIBlocks; + unsigned int BBBlocks; + unsigned int BFOBlocks; /* forward only */ + unsigned int BBABlocks; /* backward only */ + unsigned int BINBlocks; /* interpolate */ + unsigned int BFOBits; + unsigned int BBABits; + unsigned int BINBits; +} bframeStats = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + +/*====================* + * EXTERNAL VARIABLES * + *====================*/ + +extern Block **dct, **dctr, **dctb; +extern dct_data_type **dct_data; +#define NO_MOTION 0 +#define MOTION 1 +#define SKIP 2 /* used in useMotion in dct_data */ + +/*=====================* + * INTERNAL PROCEDURES * + *=====================*/ + +static void +zeroMotion(motion * const motionP) { + + motionP->fwd.y = motionP->fwd.x = motionP->bwd.y = motionP->bwd.x = 0; +} + + + +static motion +halfMotion(motion const motion) { + struct motion half; + + half.fwd.y = motion.fwd.y / 2; + half.fwd.x = motion.fwd.x / 2; + half.bwd.y = motion.bwd.y / 2; + half.bwd.x = motion.bwd.x / 2; + + return half; +} + + + + +/*===========================================================================* + * + * compute the block resulting from motion compensation + * + * RETURNS: motionBlock is modified + * + * SIDE EFFECTS: none + * + * PRECONDITION: the motion vectors must be valid! + * + *===========================================================================*/ +static void +ComputeBMotionBlock(MpegFrame * const prev, + MpegFrame * const next, + int const by, + int const bx, + int const mode, + motion const motion, + Block * const motionBlockP, + int const type) { + + Block prevBlock, nextBlock; + + switch(mode) { + case MOTION_FORWARD: + switch (type) { + case LUM_BLOCK: + ComputeMotionBlock(prev->ref_y, by, bx, motion.fwd, motionBlockP); + break; + case CB_BLOCK: + ComputeMotionBlock(prev->ref_cb, by, bx, motion.fwd, motionBlockP); + break; + case CR_BLOCK: + ComputeMotionBlock(prev->ref_cr, by, bx, motion.fwd, motionBlockP); + } + break; + case MOTION_BACKWARD: + switch (type) { + case LUM_BLOCK: + ComputeMotionBlock(next->ref_y, by, bx, motion.bwd, motionBlockP); + break; + case CB_BLOCK: + ComputeMotionBlock(next->ref_cb, by, bx, motion.bwd, motionBlockP); + break; + case CR_BLOCK: + ComputeMotionBlock(next->ref_cr, by, bx, motion.bwd, motionBlockP); + break; + } + break; + case MOTION_INTERPOLATE: + switch (type) { + case LUM_BLOCK: + ComputeMotionBlock(prev->ref_y, by, bx, motion.fwd, &prevBlock); + ComputeMotionBlock(next->ref_y, by, bx, motion.bwd, &nextBlock); + break; + case CB_BLOCK: + ComputeMotionBlock(prev->ref_cb, by, bx, motion.fwd, &prevBlock); + ComputeMotionBlock(next->ref_cb, by, bx, motion.bwd, &nextBlock); + break; + case CR_BLOCK: + ComputeMotionBlock(prev->ref_cr, by, bx, motion.fwd, &prevBlock); + ComputeMotionBlock(next->ref_cr, by, bx, motion.bwd, &nextBlock); + break; + } + { + unsigned int y; + for (y = 0; y < 8; ++y) { + int16 * const blockRow = (*motionBlockP)[y]; + int16 * const prevRow = prevBlock[y]; + int16 * const nextRow = nextBlock[y]; + unsigned int x; + for (x = 0; x < 8; ++x) + blockRow[x] = (prevRow[x] + nextRow[x] + 1) / 2; + } + } + break; + } +} + + + +/*===========================================================================* + * + * compute the DCT of the error term + * + * RETURNS: appropriate blocks of current will contain the DCTs + * + * SIDE EFFECTS: none + * + * PRECONDITION: the motion vectors must be valid! + * + *===========================================================================*/ +static void +ComputeBDiffDCTs(MpegFrame * const current, + MpegFrame * const prev, + MpegFrame * const next, + int const by, + int const bx, + int const mode, + motion const motion, + int * const patternP) { + + Block motionBlock; + + if (*patternP & 0x20) { + boolean significantDiff; + ComputeBMotionBlock(prev, next, by, bx, mode, motion, + &motionBlock, LUM_BLOCK); + ComputeDiffDCTBlock(current->y_blocks[by][bx], dct[by][bx], + motionBlock, &significantDiff); + if (!significantDiff) + *patternP ^= 0x20; + } + + if (*patternP & 0x10) { + boolean significantDiff; + ComputeBMotionBlock(prev, next, by, bx+1, mode, motion, + &motionBlock, LUM_BLOCK); + ComputeDiffDCTBlock(current->y_blocks[by][bx+1], dct[by][bx+1], + motionBlock, &significantDiff); + if (!significantDiff) + *patternP ^= 0x10; + } + + if (*patternP & 0x8) { + boolean significantDiff; + ComputeBMotionBlock(prev, next, by+1, bx, mode, motion, + &motionBlock, LUM_BLOCK); + ComputeDiffDCTBlock(current->y_blocks[by+1][bx], dct[by+1][bx], + motionBlock, &significantDiff); + if (!significantDiff) + *patternP ^= 0x8; + } + + if (*patternP & 0x4) { + boolean significantDiff; + ComputeBMotionBlock(prev, next, by+1, bx+1, mode, motion, + &motionBlock, LUM_BLOCK); + ComputeDiffDCTBlock(current->y_blocks[by+1][bx+1], dct[by+1][bx+1], + motionBlock, &significantDiff); + if (!significantDiff) + *patternP ^= 0x4; + } + + if (*patternP & 0x2) { + boolean significantDiff; + ComputeBMotionBlock(prev, next, by/2, bx/2, mode, + halfMotion(motion), &motionBlock, CB_BLOCK); + ComputeDiffDCTBlock(current->cb_blocks[by/2][bx/2], + dctb[by/2][bx/2], motionBlock, + &significantDiff); + if (!significantDiff) + *patternP ^= 0x2; + } + + if (*patternP & 0x1) { + boolean significantDiff; + ComputeBMotionBlock(prev, next, by/2, bx/2, mode, + halfMotion(motion), + &motionBlock, CR_BLOCK); + ComputeDiffDCTBlock(current->cr_blocks[by/2][bx/2], + dctr[by/2][bx/2], motionBlock, + &significantDiff); + if (!significantDiff) + *patternP ^= 0x1; + } +} + + + +/*===========================================================================* + * + * decides if this block should be coded as intra-block + * + * RETURNS: TRUE if intra-coding should be used; FALSE otherwise + * + * SIDE EFFECTS: none + * + * PRECONDITION: the motion vectors must be valid! + * + *===========================================================================*/ +static boolean +DoBIntraCode(MpegFrame * const current, + MpegFrame * const prev, + MpegFrame * const next, + int const by, + int const bx, + int const mode, + motion const motion) { + + boolean retval; + unsigned int y; + int32 sum = 0, vard = 0, varc = 0; + LumBlock motionBlock; + int fy, fx; + + ComputeBMotionLumBlock(prev, next, by, bx, mode, motion, &motionBlock); + + MotionToFrameCoord(by, bx, 0, 0, &fy, &fx); + + for (y = 0; y < 16; ++y) { + unsigned int x; + for (x = 0; x < 16; ++x) { + int32 const currPixel = current->orig_y[fy+y][fx+x]; + int32 const prevPixel = motionBlock.l[y][x]; + int32 const dif = currPixel - prevPixel; + + sum += currPixel; + varc += SQR(currPixel); + vard += SQR(dif); + } + } + + vard >>= 8; /* divide by 256; assumes mean is close to zero */ + varc = (varc>>8) - (sum>>8)*(sum>>8); + + if (vard <= 64) + retval = FALSE; + else if (vard < varc) + retval = FALSE; + else + retval = TRUE; + return retval; +} + + + +static int +ComputeBlockColorDiff(Block current, + Block motionBlock) { + + unsigned int y; + int diffTotal; + + diffTotal = 0; + + for (y = 0; y < 8; ++y) { + unsigned int x; + for (x = 0; x < 8; ++x) { + int const diffTmp = current[y][x] - motionBlock[y][x]; + diffTotal += ABS(diffTmp); + } + } + return diffTotal; +} + + + +/*=====================* + * EXPORTED PROCEDURES * + *=====================*/ + + +/*===========================================================================* + * MotionSufficient + * + * decides if this motion vector is sufficient without DCT coding + * + * RETURNS: TRUE if no DCT is needed; FALSE otherwise + * + * SIDE EFFECTS: none + * + * PRECONDITION: the motion vectors must be valid! + * + *===========================================================================*/ +static boolean +MotionSufficient(MpegFrame * const curr, + const LumBlock * const currBlockP, + MpegFrame * const prev, + MpegFrame * const next, + int const by, + int const bx, + int const mode, + motion const motion) { + + LumBlock mLumBlock; + Block mColorBlock; + int lumErr, colorErr; + + /* check bounds */ + if ( mode != MOTION_BACKWARD ) { + if ( (by*DCTSIZE+(motion.fwd.y-1)/2 < 0) || + ((by+2)*DCTSIZE+(motion.fwd.y+1)/2-1 >= Fsize_y) ) { + return FALSE; + } + if ( (bx*DCTSIZE+(motion.fwd.x-1)/2 < 0) || + ((bx+2)*DCTSIZE+(motion.fwd.x+1)/2-1 >= Fsize_x) ) { + return FALSE; + } + } + + if ( mode != MOTION_FORWARD ) { + if ( (by*DCTSIZE+(motion.bwd.y-1)/2 < 0) || + ((by+2)*DCTSIZE+(motion.bwd.y+1)/2-1 >= Fsize_y) ) { + return FALSE; + } + if ( (bx*DCTSIZE+(motion.bwd.x-1)/2 < 0) || + ((bx+2)*DCTSIZE+(motion.bwd.x+1)/2-1 >= Fsize_x) ) { + return FALSE; + } + } + + /* check Lum */ + ComputeBMotionLumBlock(prev, next, by, bx, mode, motion, &mLumBlock); + lumErr = LumBlockMAD(currBlockP, &mLumBlock, 0x7fffffff); + if (lumErr > 512) { + return FALSE; + } + + /* check color */ + ComputeBMotionBlock(prev, next, by/2, bx/2, mode, + halfMotion(motion), &mColorBlock, CR_BLOCK); + colorErr = ComputeBlockColorDiff(curr->cr_blocks[by/2][bx/2], + mColorBlock); + ComputeBMotionBlock(prev, next, by/2, bx/2, mode, halfMotion(motion), + &mColorBlock, CB_BLOCK); + colorErr += ComputeBlockColorDiff(curr->cr_blocks[by/2][bx/2], + mColorBlock); + + return (colorErr < 256); /* lumErr checked above */ +} + + + + +struct stats { + int IBlocks; + int BBlocks; + int Skipped; + int totalBits; + int IBits; + int BBits; +}; + + + +static void +initializeStats(struct stats * const statsP) { + statsP->IBlocks = 0; + statsP->BBlocks = 0; + statsP->Skipped = 0; + statsP->totalBits = 0; + statsP->IBits = 0; + statsP->BBits = 0; +} + + + +static void +checkSpecifics(MpegFrame * const curr, + int const mbAddress, + int const QScale, + boolean * const skipItP, + boolean * const doBsearchP, + int * const modeP, + motion * const motionP) { +/*---------------------------------------------------------------------------- + We return *modeP iff we return *doBsearchP == FALSE. + + We return *motionP iff we return *skipItP == FALSE. +-----------------------------------------------------------------------------*/ + BlockMV * info; + + SpecLookup(curr->id, 2, mbAddress, &info, QScale); + if (info == NULL) { + *doBsearchP = TRUE; + *skipItP = FALSE; + } else { + *doBsearchP = FALSE; + + switch (info->typ) { + case TYP_SKIP: + *skipItP = TRUE; + break; + case TYP_FORW: + *skipItP = FALSE; + motionP->fwd.y = info->fy; + motionP->fwd.x = info->fx; + *modeP = MOTION_FORWARD; + break; + case TYP_BACK: + *skipItP = FALSE; + motionP->bwd.y = info->by; + motionP->bwd.x = info->bx; + *modeP = MOTION_BACKWARD; + break; + case TYP_BOTH: + *skipItP = FALSE; + motionP->fwd.y = info->fy; + motionP->fwd.x = info->fx; + motionP->bwd.y = info->by; + motionP->bwd.x = info->bx; + *modeP = MOTION_INTERPOLATE; + break; + default: + pm_error("Unreachable code in GenBFrame!"); + } + } +} + + + +static void +makeNonSkipBlock(int const y, + int const x, + MpegFrame * const curr, + MpegFrame * const prev, + MpegFrame * const next, + boolean const specificsOn, + int const mbAddress, + int const QScale, + const LumBlock * const currentBlockP, + int * const modeP, + int * const oldModeP, + boolean const IntrPBAllowed, + boolean * const lastIntraP, + motion * const motionP, + motion * const oldMotionP, + struct stats * const statsP) { + + motion motion; + int mode; + boolean skipIt; + boolean doBsearch; + + if (specificsOn) + checkSpecifics(curr, mbAddress, QScale, &skipIt, &doBsearch, + &mode, &motion); + else { + skipIt = FALSE; + doBsearch = TRUE; + } + if (skipIt) + dct_data[y][x].useMotion = SKIP; + else { + if (doBsearch) { + motion = *motionP; /* start with old motion */ + mode = BMotionSearch(currentBlockP, prev, next, y, x, + &motion, mode); + } + /* STEP 2: INTRA OR NON-INTRA CODING */ + if (IntraPBAllowed && + DoBIntraCode(curr, prev, next, y, x, mode, motion)) { + /* output I-block inside a B-frame */ + ++statsP->IBlocks; + zeroMotion(oldMotionP); + *lastIntraP = TRUE; + dct_data[y][x].useMotion = NO_MOTION; + *oldModeP = MOTION_FORWARD; + /* calculate forward dct's */ + if (collect_quant && (collect_quant_detailed & 1)) + fprintf(collect_quant_fp, "l\n"); + mp_fwd_dct_block2(curr->y_blocks[y][x], dct[y][x]); + mp_fwd_dct_block2(curr->y_blocks[y][x+1], dct[y][x+1]); + mp_fwd_dct_block2(curr->y_blocks[y+1][x], dct[y+1][x]); + mp_fwd_dct_block2(curr->y_blocks[y+1][x+1], dct[y+1][x+1]); + if (collect_quant && (collect_quant_detailed & 1)) { + fprintf(collect_quant_fp, "c\n"); + } + mp_fwd_dct_block2(curr->cb_blocks[y>>1][x>>1], + dctb[y>>1][x>>1]); + mp_fwd_dct_block2(curr->cr_blocks[y>>1][x>>1], + dctr[y>>1][x>>1]); + + } else { /* dct P/Bi/B block */ + int pattern; + + pattern = 63; + *lastIntraP = FALSE; + ++statsP->BBlocks; + dct_data[y][x].mode = mode; + *oldModeP = mode; + dct_data[y][x].fmotionY = motion.fwd.y; + dct_data[y][x].fmotionX = motion.fwd.x; + dct_data[y][x].bmotionY = motion.bwd.y; + dct_data[y][x].bmotionX = motion.bwd.x; + + switch (mode) { + case MOTION_FORWARD: + ++bframeStats.BFOBlocks; + oldMotionP->fwd = motion.fwd; + break; + case MOTION_BACKWARD: + ++bframeStats.BBABlocks; + oldMotionP->bwd = motion.bwd; + break; + case MOTION_INTERPOLATE: + ++bframeStats.BINBlocks; + *oldMotionP = motion; + break; + default: + pm_error("INTERNAL ERROR: Illegal mode: %d", mode); + } + + ComputeBDiffDCTs(curr, prev, next, y, x, mode, motion, &pattern); + + dct_data[y][x].pattern = pattern; + dct_data[y][x].useMotion = MOTION; + if ( computeMVHist ) { + assert(motion.fwd.x + searchRangeB + 1 >= 0); + assert(motion.fwd.y + searchRangeB + 1 >= 0); + assert(motion.fwd.x + searchRangeB + 1 <= 2*searchRangeB + 2); + assert(motion.fwd.y + searchRangeB + 1 <= 2*searchRangeB + 2); + assert(motion.bwd.x + searchRangeB + 1 >= 0); + assert(motion.bwd.y + searchRangeB + 1 >= 0); + assert(motion.bwd.x + searchRangeB + 1 <= 2*searchRangeB + 2); + assert(motion.bwd.y + searchRangeB + 1 <= 2*searchRangeB + 2); + + ++bfmvHistogram[motion.fwd.x + searchRangeB + 1] + [motion.fwd.y + searchRangeB + 1]; + ++bbmvHistogram[motion.bwd.x + searchRangeB + 1] + [motion.bwd.y + searchRangeB + 1]; + } + } /* motion-block */ + } + *motionP = motion; + *modeP = mode; +} + + + +/*===========================================================================* + * + * GenBFrame + * + * generate a B-frame from previous and next frames, adding the result + * to the given bit bucket + * + * RETURNS: frame appended to bb + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void +GenBFrame(BitBucket * const bb, + MpegFrame * const curr, + MpegFrame * const prev, + MpegFrame * const next) { + + FlatBlock fba[6], fb[6]; + Block dec[6]; + int32 y_dc_pred, cr_dc_pred, cb_dc_pred; + int x, y; + struct motion motion; + struct motion oldMotion; + int oldMode = MOTION_FORWARD; + int mode = MOTION_FORWARD; + int offsetX, offsetY; + struct motion motionRem; + struct motion motionQuot; + struct stats stats; + boolean lastIntra = TRUE; + boolean motionForward, motionBackward; + int totalFrameBits; + int32 startTime, endTime; + int lastX, lastY; + int lastBlockX, lastBlockY; + int ix, iy; + LumBlock currentBlock; + int fy, fx; + boolean make_skip_block; + int mbAddrInc = 1; + int mbAddress; + int slicePos; + float snr[3], psnr[3]; + int idx; + int QScale; + BlockMV *info; + int bitstreamMode, newQScale; + int rc_blockStart=0; + boolean overflowChange=FALSE; + int overflowValue = 0; + + assert(prev != NULL); + assert(next != NULL); + + initializeStats(&stats); + + if (collect_quant) {fprintf(collect_quant_fp, "# B\n");} + if (dct == NULL) AllocDctBlocks(); + ++bframeStats.Frames; + totalFrameBits = bb->cumulativeBits; + startTime = time_elapsed(); + + /* Rate Control */ + bitstreamMode = getRateMode(); + if (bitstreamMode == FIXED_RATE) { + targetRateControl(curr); + } + + QScale = GetBQScale(); + Mhead_GenPictureHeader(bb, B_FRAME, curr->id, fCodeB); + /* Check for Qscale change */ + if (specificsOn) { + newQScale = SpecLookup(curr->id, 0, 0 /* junk */, &info, QScale); + if (newQScale != -1) { + QScale = newQScale; + } + /* check for slice */ + newQScale = SpecLookup(curr->id, 1, 1, &info, QScale); + if (newQScale != -1) { + QScale = newQScale; + } + } + + Mhead_GenSliceHeader(bb, 1, QScale, NULL, 0); + + Frame_AllocBlocks(curr); + BlockifyFrame(curr); + + if ( printSNR ) { + Frame_AllocDecoded(curr, FALSE); + } + + /* for I-blocks */ + y_dc_pred = cr_dc_pred = cb_dc_pred = 128; + + stats.totalBits = bb->cumulativeBits; + + if ( ! pixelFullSearch ) { + if ( ! prev->halfComputed ) { + ComputeHalfPixelData(prev); + } + + if ( ! next->halfComputed ) { + ComputeHalfPixelData(next); + } + } + + lastBlockX = Fsize_x / 8; + lastBlockY = Fsize_y / 8; + lastX = lastBlockX - 2; + lastY = lastBlockY - 2; + mbAddress = 0; + + /* Start with zero motion assumption */ + zeroMotion(&motion); + zeroMotion(&oldMotion); + zeroMotion(&motionRem); + zeroMotion(&motionQuot); + + /* find motion vectors and do dcts */ + /* In this first loop, all MVs are in half-pixel scope, (if FULL + is set then they will be multiples of 2). This is not true in + the second loop. + */ + for (y = 0; y < lastBlockY; y += 2) { + for (x = 0; x < lastBlockX; x += 2) { + slicePos = (mbAddress % blocksPerSlice); + + /* compute currentBlock */ + BLOCK_TO_FRAME_COORD(y, x, fy, fx); + for ( iy = 0; iy < 16; iy++ ) { + for ( ix = 0; ix < 16; ix++ ) { + currentBlock.l[iy][ix] = (int16)curr->orig_y[fy+iy][fx+ix]; + } + } + + if (slicePos == 0) { + zeroMotion(&oldMotion); + oldMode = MOTION_FORWARD; + lastIntra = TRUE; + } + + /* STEP 1: Select Forward, Backward, or Interpolated motion + vectors */ + /* see if old motion is good enough */ + /* but force last block to be non-skipped */ + /* can only skip if: + * 1) not the last block in frame + * 2) not the last block in slice + * 3) not the first block in slice + * 4) previous block was not intra-coded + */ + if ( ((y < lastY) || (x < lastX)) && + (slicePos+1 != blocksPerSlice) && + (slicePos != 0) && + (! lastIntra) && + (BSkipBlocks) ) { + make_skip_block = + MotionSufficient(curr, ¤tBlock, + prev, next, y, x, oldMode, oldMotion); + } else + make_skip_block = FALSE; + + if (make_skip_block) { + /* skipped macro block */ + dct_data[y][x].useMotion = SKIP; + } else + makeNonSkipBlock(y, x, curr, prev, next, specificsOn, + mbAddress, + QScale, ¤tBlock, + &mode, &oldMode, + IntraPBAllowed, + &lastIntra, &motion, &oldMotion, &stats); + + ++mbAddress; + } + } + + /* reset everything */ + zeroMotion(&oldMotion); + oldMode = MOTION_FORWARD; + lastIntra = TRUE; + y_dc_pred = cr_dc_pred = cb_dc_pred = 128; + mbAddress = 0; + + /* Now generate the frame */ + for (y = 0; y < lastBlockY; y += 2) { + for (x = 0; x < lastBlockX; x += 2) { + slicePos = (mbAddress % blocksPerSlice); + + if ( (slicePos == 0) && (mbAddress != 0) ) { + if (specificsOn) { + /* Make sure no slice Qscale change */ + newQScale = + SpecLookup(curr->id,1,mbAddress/blocksPerSlice, &info, QScale); + if (newQScale != -1) QScale = newQScale; + } + Mhead_GenSliceEnder(bb); + Mhead_GenSliceHeader(bb, 1+(y>>1), QScale, NULL, 0); + + /* reset everything */ + zeroMotion(&oldMotion); + oldMode = MOTION_FORWARD; + lastIntra = TRUE; + y_dc_pred = cr_dc_pred = cb_dc_pred = 128; + + mbAddrInc = 1+(x>>1); + } + + /* Determine if new Qscale needed for Rate Control purposes */ + if (bitstreamMode == FIXED_RATE) { + rc_blockStart = bb->cumulativeBits; + newQScale = needQScaleChange(QScale, + curr->y_blocks[y][x], + curr->y_blocks[y][x+1], + curr->y_blocks[y+1][x], + curr->y_blocks[y+1][x+1]); + if (newQScale > 0) { + QScale = newQScale; + } + } + + if (specificsOn) { + newQScale = SpecLookup(curr->id, 2, mbAddress, &info, QScale); + if (newQScale != -1) { + QScale = newQScale; + }} + + if (dct_data[y][x].useMotion == NO_MOTION) { + + GEN_I_BLOCK(B_FRAME, curr, bb, mbAddrInc, QScale); + mbAddrInc = 1; + stats.IBits += (bb->cumulativeBits - stats.totalBits); + stats.totalBits = bb->cumulativeBits; + + /* reset because intra-coded */ + zeroMotion(&oldMotion); + oldMode = MOTION_FORWARD; + lastIntra = TRUE; + + if ( printSNR ) { + /* need to decode block we just encoded */ + /* and reverse the DCT transform */ + for ( idx = 0; idx < 6; idx++ ) { + Mpost_UnQuantZigBlock(fb[idx], dec[idx], QScale, TRUE); + mpeg_jrevdct((int16 *)dec[idx]); + } + + /* now, unblockify */ + BlockToData(curr->decoded_y, dec[0], y, x); + BlockToData(curr->decoded_y, dec[1], y, x+1); + BlockToData(curr->decoded_y, dec[2], y+1, x); + BlockToData(curr->decoded_y, dec[3], y+1, x+1); + BlockToData(curr->decoded_cb, dec[4], y>>1, x>>1); + BlockToData(curr->decoded_cr, dec[5], y>>1, x>>1); + } + } else if (dct_data[y][x].useMotion == SKIP) { + ++stats.Skipped; + mbAddrInc++; + + /* decode skipped block */ + if (printSNR) { + struct motion motion; + + for (idx = 0; idx < 6; ++idx) + memset((char *)dec[idx], 0, sizeof(Block)); + + if (pixelFullSearch) { + motion.fwd.y = 2 * oldMotion.fwd.y; + motion.fwd.x = 2 * oldMotion.fwd.x; + motion.bwd.y = 2 * oldMotion.bwd.y; + motion.bwd.x = 2 * oldMotion.bwd.x; + } else + motion = oldMotion; + + /* now add the motion block */ + AddBMotionBlock(dec[0], prev->decoded_y, + next->decoded_y, y, x, mode, motion); + AddBMotionBlock(dec[1], prev->decoded_y, + next->decoded_y, y, x+1, mode, motion); + AddBMotionBlock(dec[2], prev->decoded_y, + next->decoded_y, y+1, x, mode, motion); + AddBMotionBlock(dec[3], prev->decoded_y, + next->decoded_y, y+1, x+1, mode, motion); + AddBMotionBlock(dec[4], prev->decoded_cb, + next->decoded_cb, y/2, x/2, mode, + halfMotion(motion)); + AddBMotionBlock(dec[5], prev->decoded_cr, + next->decoded_cr, y/2, x/2, mode, + halfMotion(motion)); + + /* now, unblockify */ + BlockToData(curr->decoded_y, dec[0], y, x); + BlockToData(curr->decoded_y, dec[1], y, x+1); + BlockToData(curr->decoded_y, dec[2], y+1, x); + BlockToData(curr->decoded_y, dec[3], y+1, x+1); + BlockToData(curr->decoded_cb, dec[4], y/2, x/2); + BlockToData(curr->decoded_cr, dec[5], y/2, x/2); + } + } else /* B block */ { + int const fCode = fCodeB; + int pattern; + + pattern = dct_data[y][x].pattern; + motion.fwd.y = dct_data[y][x].fmotionY; + motion.fwd.x = dct_data[y][x].fmotionX; + motion.bwd.y = dct_data[y][x].bmotionY; + motion.bwd.x = dct_data[y][x].bmotionX; + + if (pixelFullSearch) + motion = halfMotion(motion); + + /* create flat blocks and update pattern if necessary */ + calc_blocks: + /* Note DoQuant references QScale, overflowChange, overflowValue, + pattern, and the calc_blocks label */ + DoQuant(0x20, dct[y][x], fba[0]); + DoQuant(0x10, dct[y][x+1], fba[1]); + DoQuant(0x08, dct[y+1][x], fba[2]); + DoQuant(0x04, dct[y+1][x+1], fba[3]); + DoQuant(0x02, dctb[y/2][x/2], fba[4]); + DoQuant(0x01, dctr[y/2][x/2], fba[5]); + + motionForward = (dct_data[y][x].mode != MOTION_BACKWARD); + motionBackward = (dct_data[y][x].mode != MOTION_FORWARD); + + /* Encode Vectors */ + if (motionForward) { + /* transform the fMotion vector into the appropriate values */ + offsetY = motion.fwd.y - oldMotion.fwd.y; + offsetX = motion.fwd.x - oldMotion.fwd.x; + + encodeMotionVector(offsetX, offsetY, + &motionQuot.fwd, &motionRem.fwd, + FORW_F, fCode); + oldMotion.fwd = motion.fwd; + } + + if (motionBackward) { + /* transform the bMotion vector into the appropriate values */ + offsetY = motion.bwd.y - oldMotion.bwd.y; + offsetX = motion.bwd.x - oldMotion.bwd.x; + encodeMotionVector(offsetX, offsetY, + &motionQuot.bwd, &motionRem.bwd, + BACK_F, fCode); + oldMotion.bwd = motion.bwd; + } + + oldMode = dct_data[y][x].mode; + + if (printSNR) { /* Need to decode */ + if (pixelFullSearch) { + motion.fwd.x *= 2; motion.fwd.y *= 2; + motion.bwd.x *= 2; motion.bwd.y *= 2; + } + for ( idx = 0; idx < 6; idx++ ) { + if ( pattern & (1 << (5-idx)) ) { + Mpost_UnQuantZigBlock(fba[idx], dec[idx], QScale, FALSE); + mpeg_jrevdct((int16 *)dec[idx]); + } else { + memset((char *)dec[idx], 0, sizeof(Block)); + } + } + + /* now add the motion block */ + AddBMotionBlock(dec[0], prev->decoded_y, + next->decoded_y, y, x, mode, motion); + AddBMotionBlock(dec[1], prev->decoded_y, + next->decoded_y, y, x+1, mode, motion); + AddBMotionBlock(dec[2], prev->decoded_y, + next->decoded_y, y+1, x, mode, motion); + AddBMotionBlock(dec[3], prev->decoded_y, + next->decoded_y, y+1, x+1, mode, motion); + AddBMotionBlock(dec[4], prev->decoded_cb, + next->decoded_cb, y/2, x/2, mode, + halfMotion(motion)); + AddBMotionBlock(dec[5], prev->decoded_cr, + next->decoded_cr, y/2, x/2, mode, + halfMotion(motion)); + + /* now, unblockify */ + BlockToData(curr->decoded_y, dec[0], y, x); + BlockToData(curr->decoded_y, dec[1], y, x+1); + BlockToData(curr->decoded_y, dec[2], y+1, x); + BlockToData(curr->decoded_y, dec[3], y+1, x+1); + BlockToData(curr->decoded_cb, dec[4], y/2, x/2); + BlockToData(curr->decoded_cr, dec[5], y/2, x/2); + } + + /* reset because non-intra-coded */ + y_dc_pred = cr_dc_pred = cb_dc_pred = 128; + lastIntra = FALSE; + mode = dct_data[y][x].mode; + + /* DBG_PRINT(("MB Header(%d,%d)\n", x, y)); */ + Mhead_GenMBHeader( + bb, 3 /* pict_code_type */, mbAddrInc /* addr_incr */, + QScale /* q_scale */, + fCodeB /* forw_f_code */, fCodeB /* back_f_code */, + motionRem.fwd.x /* horiz_forw_r */, + motionRem.fwd.y /* vert_forw_r */, + motionRem.bwd.x /* horiz_back_r */, + motionRem.bwd.y /* vert_back_r */, + motionForward /* motion_forw */, + motionQuot.fwd.x /* m_horiz_forw */, + motionQuot.fwd.y /* m_vert_forw */, + motionBackward /* motion_back */, + motionQuot.bwd.x /* m_horiz_back */, + motionQuot.bwd.y /* m_vert_back */, + pattern /* mb_pattern */, FALSE /* mb_intra */); + + mbAddrInc = 1; + + /* now output the difference */ + { + unsigned int x; + for (x = 0; x < 6; ++x) { + if (GET_ITH_BIT(pattern, 5-x)) + Mpost_RLEHuffPBlock(fba[x], bb); + } + } + + switch (mode) { + case MOTION_FORWARD: + bframeStats.BFOBits += (bb->cumulativeBits - stats.totalBits); + break; + case MOTION_BACKWARD: + bframeStats.BBABits += (bb->cumulativeBits - stats.totalBits); + break; + case MOTION_INTERPOLATE: + bframeStats.BINBits += (bb->cumulativeBits - stats.totalBits); + break; + default: + pm_error("PROGRAMMER ERROR: Illegal mode: %d", mode); + } + + stats.BBits += (bb->cumulativeBits - stats.totalBits); + stats.totalBits = bb->cumulativeBits; + + if (overflowChange) { + /* undo an overflow-caused Qscale change */ + overflowChange = FALSE; + QScale -= overflowValue; + overflowValue = 0; + } + } /* if I-block, skip, or B */ + + mbAddress++; + /* Rate Control */ + if (bitstreamMode == FIXED_RATE) { + incMacroBlockBits( bb->cumulativeBits - rc_blockStart); + rc_blockStart = bb->cumulativeBits; + MB_RateOut(TYPE_BFRAME); + } + + } + } + + if ( printSNR ) { + BlockComputeSNR(curr,snr,psnr); + totalSNR += snr[0]; + totalPSNR += psnr[0]; + } + + Mhead_GenSliceEnder(bb); + /* Rate Control */ + if (bitstreamMode == FIXED_RATE) { + updateRateControl(TYPE_BFRAME); + } + + endTime = time_elapsed(); + totalTime += (endTime-startTime); + + if ( showBitRatePerFrame ) { + /* ASSUMES 30 FRAMES PER SECOND */ + fprintf(bitRateFile, "%5d\t%8d\n", curr->id, + 30*(bb->cumulativeBits-totalFrameBits)); + } + + if ( frameSummary && !realQuiet) { + fprintf(stdout, "FRAME %d (B): " + "I BLOCKS: %5d; B BLOCKS: %5d SKIPPED: %5d (%ld seconds)\n", + curr->id, stats.IBlocks, stats.BBlocks, stats.Skipped, + (long)((endTime-startTime)/TIME_RATE)); + if ( printSNR ) + fprintf(stdout, "FRAME %d: " + "SNR: %.1f\t%.1f\t%.1f\tPSNR: %.1f\t%.1f\t%.1f\n", + curr->id, snr[0], snr[1], snr[2], + psnr[0], psnr[1], psnr[2]); + } + + bframeStats.FrameBits += (bb->cumulativeBits-totalFrameBits); + bframeStats.BIBlocks += stats.IBlocks; + bframeStats.BBBlocks += stats.BBlocks; + bframeStats.BSkipped += stats.Skipped; + bframeStats.BIBits += stats.IBits; + bframeStats.BBBits += stats.BBits; + } + + +/*===========================================================================* + * + * SetBQScale + * + * set the B-frame Q-scale + * + * RETURNS: nothing + * + * SIDE EFFECTS: qscaleB + * + *===========================================================================*/ +void +SetBQScale(qB) + int qB; +{ + qscaleB = qB; +} + + +/*===========================================================================* + * + * GetBQScale + * + * get the B-frame Q-scale + * + * RETURNS: the Q-scale + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +int +GetBQScale() +{ + return qscaleB; +} + + +/*===========================================================================* + * + * ResetBFrameStats + * + * reset the B-frame stats + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void +ResetBFrameStats() { + + bframeStats.BIBlocks = 0; + bframeStats.BBBlocks = 0; + bframeStats.BSkipped = 0; + bframeStats.BIBits = 0; + bframeStats.BBBits = 0; + bframeStats.Frames = 0; + bframeStats.FrameBits = 0; + totalTime = 0; +} + + + +float +BFrameTotalTime(void) { + return (float)totalTime/(float)TIME_RATE; +} + + + +void +ShowBFrameSummary(unsigned int const inputFrameBits, + unsigned int const totalBits, + FILE * const fpointer) { + + if (bframeStats.Frames > 0) { + fprintf(fpointer, "-------------------------\n"); + fprintf(fpointer, "*****B FRAME SUMMARY*****\n"); + fprintf(fpointer, "-------------------------\n"); + + if (bframeStats.BIBlocks > 0) { + fprintf(fpointer, + " I Blocks: %5d (%6d bits) (%5d bpb)\n", + bframeStats.BIBlocks, bframeStats.BIBits, + bframeStats.BIBits/bframeStats.BIBlocks); + } else + fprintf(fpointer, " I Blocks: %5d\n", 0); + + if (bframeStats.BBBlocks > 0) { + fprintf(fpointer, + " B Blocks: %5d (%6d bits) (%5d bpb)\n", + bframeStats.BBBlocks, bframeStats.BBBits, + bframeStats.BBBits/bframeStats.BBBlocks); + fprintf(fpointer, + " B types: %5d (%4d bpb) " + "forw %5d (%4d bpb) back %5d (%4d bpb) bi\n", + bframeStats.BFOBlocks, + (bframeStats.BFOBlocks==0) ? + 0 : bframeStats.BFOBits/bframeStats.BFOBlocks, + bframeStats.BBABlocks, + (bframeStats.BBABlocks==0) ? + 0 : bframeStats.BBABits/bframeStats.BBABlocks, + bframeStats.BINBlocks, + (bframeStats.BINBlocks==0) ? + 0 : bframeStats.BINBits/bframeStats.BINBlocks); + } else + fprintf(fpointer, " B Blocks: %5d\n", 0); + + fprintf(fpointer, " Skipped: %5d\n", bframeStats.BSkipped); + + fprintf(fpointer, " Frames: %5d (%6d bits) " + "(%5d bpf) (%2.1f%% of total)\n", + bframeStats.Frames, bframeStats.FrameBits, + bframeStats.FrameBits/bframeStats.Frames, + 100.0*(float)bframeStats.FrameBits/(float)totalBits); + fprintf(fpointer, " Compression: %3d:1 (%9.4f bpp)\n", + bframeStats.Frames*inputFrameBits/bframeStats.FrameBits, + 24.0*(float)bframeStats.FrameBits/ + (float)(bframeStats.Frames*inputFrameBits)); + if (printSNR) + fprintf(fpointer, " Avg Y SNR/PSNR: %.1f %.1f\n", + totalSNR/(float)bframeStats.Frames, + totalPSNR/(float)bframeStats.Frames); + if (totalTime == 0) { + fprintf(fpointer, " Seconds: NONE\n"); + } else { + fprintf(fpointer, " Seconds: %9ld (%9.4f fps) " + "(%9ld pps) (%9ld mps)\n", + (long)(totalTime/TIME_RATE), + (float)((float)(TIME_RATE*bframeStats.Frames)/ + (float)totalTime), + (long)((float)TIME_RATE*(float)bframeStats.Frames * + (float)inputFrameBits/(24.0*(float)totalTime)), + (long)((float)TIME_RATE*(float)bframeStats.Frames * + (float)inputFrameBits/(256.0*24.0 * + (float)totalTime))); + } + } +} + + + +/*===========================================================================* + * + * compute the luminance block resulting from motion compensation + * + * RETURNS: motionBlock modified + * + * SIDE EFFECTS: none + * + * PRECONDITION: the motion vectors must be valid! + * + *===========================================================================*/ +void +ComputeBMotionLumBlock(MpegFrame * const prev, + MpegFrame * const next, + int const by, + int const bx, + int const mode, + motion const motion, + LumBlock * const motionBlockP) { + + switch(mode) { + case MOTION_FORWARD: + ComputeMotionLumBlock(prev, by, bx, motion.fwd, motionBlockP); + break; + case MOTION_BACKWARD: + ComputeMotionLumBlock(next, by, bx, motion.bwd, motionBlockP); + break; + case MOTION_INTERPOLATE: { + LumBlock prevBlock, nextBlock; + unsigned int y; + + ComputeMotionLumBlock(prev, by, bx, motion.fwd, &prevBlock); + ComputeMotionLumBlock(next, by, bx, motion.bwd, &nextBlock); + + for (y = 0; y < 16; ++y) { + unsigned int x; + for (x = 0; x < 16; ++x) + motionBlockP->l[y][x] = + (prevBlock.l[y][x] + nextBlock.l[y][x] + 1) / 2; + } + } break; + default: + pm_error("Bad mode! Programmer error!"); + } /* switch */ +} + + +/*===========================================================================* + * + * estimate the seconds to compute a B-frame + * + * RETURNS: the time, in seconds + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +float +EstimateSecondsPerBFrame() { + if (bframeStats.Frames == 0) + return 20.0; + else + return (float)totalTime/((float)TIME_RATE*(float)bframeStats.Frames); +} + + diff --git a/converter/ppm/ppmtompeg/bitio.c b/converter/ppm/ppmtompeg/bitio.c new file mode 100644 index 00000000..e0dc4d4e --- /dev/null +++ b/converter/ppm/ppmtompeg/bitio.c @@ -0,0 +1,536 @@ +/*===========================================================================* + * bitio.c + * + * Procedures concerned with the bit-wise I/O + * + * EXPORTED PROCEDURES: + * Bitio_New + * Bitio_Free + * Bitio_Write + * Bitio_Flush + * Bitio_WriteToSocket + * Bitio_BytePad + * + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/*==============* + * HEADER FILES * + *==============*/ + +#include <assert.h> +#include <time.h> +#include <unistd.h> +#include <errno.h> + +#include "pm.h" +#include "intcode.h" + +#include "all.h" +#include "byteorder.h" +#include "bitio.h" +#include "mtypes.h" + + + +/*==================* + * STATIC VARIABLES * + *==================*/ + +static uint32 lower_mask[33] = { + 0, + 0x1, 0x3, 0x7, 0xf, + 0x1f, 0x3f, 0x7f, 0xff, + 0x1ff, 0x3ff, 0x7ff, 0xfff, + 0x1fff, 0x3fff, 0x7fff, 0xffff, + 0x1ffff, 0x3ffff, 0x7ffff, 0xfffff, + 0x1fffff, 0x3fffff, 0x7fffff, 0xffffff, + 0x1ffffff, 0x3ffffff, 0x7ffffff, 0xfffffff, + 0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff +}; + + +extern time_t IOtime; + + +/*===========================================================================* + * + * Dump + * + * Writes out the first MAX_BITS bits of the bit bucket to the + * appropriate output file + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +static void +Dump(BitBucket * const bbPtr) { + struct bitBucket *ptr, *tempPtr; + int i, nitems; + int bitsWritten = 0; + time_t tempTimeStart, tempTimeEnd; + + time(&tempTimeStart); + + for (ptr = bbPtr->firstPtr; ptr && (bitsWritten < MAX_BITS); + ptr = ptr->nextPtr) { + + bigend32 buffer[WORDS_PER_BUCKET]; + + if (ptr->bitsleftcur == 32 && ptr->currword == 0) { + continue; /* empty */ + } + + for (i = 0; i <= ptr->currword; ++i) + buffer[i] = pm_bigendFromUint32(ptr->bits[i]); + + nitems = fwrite(buffer, sizeof(buffer[0]), ptr->currword + 1, + bbPtr->filePtr); + if (nitems != ptr->currword+1) { + fprintf(stderr, + "Whoa! Trouble writing %u words (wrote %u words)! " + "Game over, dude!\n", + ptr->currword+1, nitems); + exit(1); + } + + bitsWritten += ((ptr->currword + 1) * 32); + } + + while ( bbPtr->firstPtr != ptr ) { + tempPtr = bbPtr->firstPtr; + bbPtr->firstPtr = tempPtr->nextPtr; + free(tempPtr); + } + + bbPtr->totalbits -= bitsWritten; + bbPtr->bitsWritten += bitsWritten; + + time(&tempTimeEnd); + IOtime += (tempTimeEnd-tempTimeStart); +} + + +/*=====================* + * EXPORTED PROCEDURES * + *=====================*/ + + +/*===========================================================================* + * + * Bitio_New + * + * Create a new bit bucket; filePtr is a pointer to the open file the + * bits should ultimately be written to. + * + * RETURNS: pointer to the resulting bit bucket + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +BitBucket * +Bitio_New(FILE * const filePtr) { + + BitBucket *bbPtr; + + bbPtr = (BitBucket *) malloc(sizeof(BitBucket)); + ERRCHK(bbPtr, "malloc"); + + bbPtr->firstPtr = bbPtr->lastPtr = malloc(sizeof(struct bitBucket)); + ERRCHK(bbPtr->firstPtr, "malloc"); + + bbPtr->totalbits = 0; + bbPtr->cumulativeBits = 0; + bbPtr->bitsWritten = 0; + bbPtr->filePtr = filePtr; + + bbPtr->firstPtr->nextPtr = NULL; + bbPtr->firstPtr->bitsleft = MAXBITS_PER_BUCKET; + bbPtr->firstPtr->bitsleftcur = 32; + bbPtr->firstPtr->currword = 0; + memset((char *)bbPtr->firstPtr->bits, 0, + sizeof(uint32) * WORDS_PER_BUCKET); + + return bbPtr; +} + + + +BitBucket * +Bitio_New_Filename(const char * const fileName) { + + FILE * outputFile; + + outputFile = fopen(fileName, "wb"); + if (outputFile == NULL) + pm_error("Could not open output file '%s'. " + "Errno=%d (%s)", fileName, errno, strerror(errno)); + + return Bitio_New(outputFile); +} + + + +/*===========================================================================* + * + * Bitio_Free + * + * Frees the memory associated with the given bit bucket + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void +Bitio_Free(BitBucket * const bbPtr) { + + struct bitBucket *tmpPtr, *nextPtr; + + for (tmpPtr = bbPtr->firstPtr; tmpPtr != NULL; tmpPtr = nextPtr) { + nextPtr = tmpPtr->nextPtr; + free(tmpPtr); + } + free(bbPtr); +} + + +/*===========================================================================* + * + * Bitio_Write + * + * Writes 'nbits' bits from 'bits' into the given bit bucket + * 'nbits' must be between 0 and 32 + * + * RETURNS: nothing + * + * SIDE EFFECTS: if the number of bits in the bit bucket surpasses + * MAX_BITS, then that many bits are flushed to the + * appropriate output file + * + *===========================================================================*/ +void +Bitio_Write(BitBucket * const bbPtr, + uint32 const bits_arg, + int const nbits) { + + register struct bitBucket *lastPtr, *newPtr; + register int delta; + uint32 bits; + + bits=bits_arg; + assert(nbits <= 32 && nbits >= 0); + + /* + * Clear top bits if not part of data, necessary due to down and + * dirty calls of Bitio_Write with unecessary top bits set. + */ + + bits &= lower_mask[nbits]; + bits = bits & lower_mask[nbits]; + + bbPtr->totalbits += nbits; + bbPtr->cumulativeBits += nbits; + lastPtr = bbPtr->lastPtr; + + delta = nbits - lastPtr->bitsleft; + if (delta >= 0) { + /* + * there's not enough room in the current bucket, so we're + * going to have to allocate another bucket + */ + newPtr = lastPtr->nextPtr = (struct bitBucket *) + malloc(sizeof(struct bitBucket)); + ERRCHK(newPtr, "malloc"); + newPtr->nextPtr = NULL; + newPtr->bitsleft = MAXBITS_PER_BUCKET; + newPtr->bitsleftcur = 32; + newPtr->currword = 0; + memset((char *)newPtr->bits, 0, sizeof(uint32) * WORDS_PER_BUCKET); + bbPtr->lastPtr = newPtr; + + assert(lastPtr->currword == WORDS_PER_BUCKET - 1); + lastPtr->bits[WORDS_PER_BUCKET - 1] |= (bits >> delta); + lastPtr->bitsleft = 0; + lastPtr->bitsleftcur = 0; + /* lastPtr->currword++; */ + + if (!delta) { + if ( bbPtr->totalbits > MAX_BITS ) { + Dump(bbPtr); + } + } + + assert(delta <= 32); + newPtr->bits[0] = (bits & lower_mask[delta]) << (32 - delta); + newPtr->bitsleft -= delta; + newPtr->bitsleftcur -= delta; + } else { + /* + * the current bucket will be sufficient + */ + delta = nbits - lastPtr->bitsleftcur; + lastPtr->bitsleftcur -= nbits; + lastPtr->bitsleft -= nbits; + + if (delta >= 0) + { + /* + * these bits will span more than one word + */ + lastPtr->bits[lastPtr->currword] |= (bits >> delta); + lastPtr->currword++; + lastPtr->bits[lastPtr->currword] = + (bits & lower_mask[delta]) << (32 - delta); + lastPtr->bitsleftcur = 32 - delta; + } else { + /* + * these bits will fit, whole + */ + lastPtr->bits[lastPtr->currword] |= (bits << (-delta)); + } + } + + if ( bbPtr->totalbits > MAX_BITS ) /* flush bits */ + Dump(bbPtr); +} + + +/*===========================================================================* + * + * Bitio_Flush + * + * Flushes all of the remaining bits in the given bit bucket to the + * appropriate output file. It will generate up to the nearest 8-bit + * unit of bits, which means that up to 7 extra 0 bits will be appended + * to the end of the file. + * + * RETURNS: nothing + * + * SIDE EFFECTS: frees the bit bucket + * + *===========================================================================*/ +void +Bitio_Flush(BitBucket * const bbPtr) { + + struct bitBucket *ptr, *tempPtr; + uint32 lastWord; + int i, nitems; + int bitsWritten = 0; + int bitsLeft; + unsigned int numWords; + uint8 charBuf[4]; + boolean flushHere = FALSE; + time_t tempTimeStart, tempTimeEnd; + + time(&tempTimeStart); + + bitsLeft = bbPtr->totalbits; + + for (ptr = bbPtr->firstPtr; ptr; ptr = ptr->nextPtr) { + if (ptr->bitsleftcur == 32 && ptr->currword == 0) { + continue; /* empty */ + } + + if ( bitsLeft >= 32 ) { + bigend32 buffer[WORDS_PER_BUCKET]; + + if ( ((ptr->currword + 1) * 32) > bitsLeft ) { + numWords = ptr->currword; + flushHere = TRUE; + } else + numWords = ptr->currword+1; + + for (i = 0; i < numWords; ++i) + buffer[i] = pm_bigendFromUint32(ptr->bits[i]); + + nitems = fwrite(buffer, sizeof(buffer[0]), numWords, + bbPtr->filePtr); + if (nitems != numWords) { + if (ferror(bbPtr->filePtr)) + pm_error("Error writing %u words to flush a bit bucket. " + "fwrite() gives errno %d (%s)", + numWords, errno, strerror(errno)); + else + pm_error("Problem writing %u words " + "to flush a bit bucket. " + "Only %d words transferred.", + numWords, nitems); + } + + bitsWritten += (numWords * 32); + bitsLeft -= (numWords * 32); + } else { + flushHere = TRUE; + } + + if ( (bitsLeft < 32) && flushHere ) { + lastWord = ptr->bits[ptr->currword]; + + /* output the lastPtr word in big-endian order (network) */ + + /* now write out lastPtr bits */ + while ( bitsLeft > 0 ) { + charBuf[0] = (lastWord >> 24); + charBuf[0] &= lower_mask[8]; + fwrite(charBuf, 1, sizeof(uint8), bbPtr->filePtr); + lastWord = (lastWord << 8); + bitsLeft -= 8; + bitsWritten += 8; + } + } + } + fflush(bbPtr->filePtr); + while ( bbPtr->firstPtr != ptr ) { + tempPtr = bbPtr->firstPtr; + bbPtr->firstPtr = tempPtr->nextPtr; + free(tempPtr); + } + + free(bbPtr); + + time(&tempTimeEnd); + IOtime += (tempTimeEnd-tempTimeStart); +} + + + +void +Bitio_Close(BitBucket * const bbPtr) { + + fclose(bbPtr->filePtr); +} + + + +/*===========================================================================* + * + * Bitio_WriteToSocket + * + * Writes all of the remaining bits in the given bit bucket to the + * given socket. May pad the end of the socket stream with extra 0 + * bits as does Bitio_Flush. + * + * RETURNS: nothing + * + * SIDE EFFECTS: frees the bit bucket + * + *===========================================================================*/ +void +Bitio_WriteToSocket(BitBucket * const bbPtr, + int const socket) { + + struct bitBucket *ptr, *tempPtr; + uint32 lastWord; + int i, nitems; + int bitsWritten = 0; + int bitsLeft; + int numWords; + uint8 charBuf[4]; + boolean flushHere = FALSE; + + bitsLeft = bbPtr->totalbits; + + for (ptr = bbPtr->firstPtr; ptr; ptr = ptr->nextPtr) { + if (ptr->bitsleftcur == 32 && ptr->currword == 0) { + continue; /* empty */ + } + + if ( bitsLeft >= 32 ) { + bigend32 buffer[WORDS_PER_BUCKET]; + + if ( ((ptr->currword + 1) * 32) > bitsLeft ) { + numWords = ptr->currword; + flushHere = TRUE; + } else { + numWords = ptr->currword+1; + } + + for (i = 0; i < numWords; ++i) + buffer[i] = pm_bigendFromUint32(ptr->bits[i]); + + nitems = write(socket, buffer, numWords * sizeof(buffer[0])); + if (nitems != numWords*sizeof(uint32)) { + fprintf(stderr, "Whoa! Trouble writing %u bytes " + "(wrote %u bytes)! " + "Game over, dude!\n", + (unsigned)(numWords*sizeof(buffer[0])), nitems); + exit(1); + } + + bitsWritten += (numWords * 32); + bitsLeft -= (numWords * 32); + } else { + flushHere = TRUE; + } + + if ( (bitsLeft < 32) && flushHere ) { + lastWord = ptr->bits[ptr->currword]; + + /* output the lastPtr word in big-endian order (network) */ + + /* now write out lastPtr bits */ + while ( bitsLeft > 0 ) { + charBuf[0] = (lastWord >> 24); + charBuf[0] &= lower_mask[8]; + if ( write(socket, charBuf, 1) != 1 ) { + fprintf(stderr, "ERROR: write of lastPtr bits\n"); + exit(1); + } + lastWord = (lastWord << 8); + bitsLeft -= 8; + bitsWritten += 8; + } + } + } + + while ( bbPtr->firstPtr != ptr ) { + tempPtr = bbPtr->firstPtr; + bbPtr->firstPtr = tempPtr->nextPtr; + free(tempPtr); + } + + free(bbPtr); +} + + +/*===========================================================================* + * + * Bitio_BytePad + * + * Pads the end of the bit bucket to the nearest byte with 0 bits + * + * RETURNS: nothing + * + *===========================================================================*/ +void +Bitio_BytePad(BitBucket * const bbPtr) { + + struct bitBucket *lastPtrPtr = bbPtr->lastPtr; + + if (lastPtrPtr->bitsleftcur % 8) { + Bitio_Write(bbPtr, 0, lastPtrPtr->bitsleftcur % 8); + } +} diff --git a/converter/ppm/ppmtompeg/block.c b/converter/ppm/ppmtompeg/block.c new file mode 100644 index 00000000..913518c4 --- /dev/null +++ b/converter/ppm/ppmtompeg/block.c @@ -0,0 +1,1045 @@ +/*===========================================================================* + * block.c + * + * Block routines + * + * NOTES: MAD = Mean Absolute Difference + *===========================================================================*/ + +/* Copyright information is at end of file */ + +#include <assert.h> + +#include "pm_c_util.h" +#include "all.h" +#include "mtypes.h" +#include "frames.h" +#include "bitio.h" +#include "prototypes.h" +#include "fsize.h" +#include "opts.h" +#include "postdct.h" + +#include "block.h" + +#define TRUNCATE_UINT8(x) ((x < 0) ? 0 : ((x > 255) ? 255 : x)) + +/*==================* + * GLOBAL VARIABLES * + *==================*/ + + +extern Block **dct, **dctb, **dctr; + + +static vector +halfVector(vector const v) { + vector half; + + half.y = v.y/2; + half.x = v.x/2; + + return half; +} + +/*===========================* + * COMPUTE DCT OF DIFFERENCE * + *===========================*/ + +/*===========================================================================* + * + * compute current-motionBlock, take the DCT, and put the difference + * back into current + * + * RETURNS: current block modified + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void +ComputeDiffDCTBlock(Block current, + Block dest, + Block motionBlock, + boolean * const significantDifferenceP) { + + unsigned int y; + int diff; + + diff = 0; /* initial value */ + + for (y = 0; y < 8; ++y) { + unsigned int x; + for (x = 0; x < 8; ++x) { + current[y][x] -= motionBlock[y][x]; + diff += ABS(current[y][x]); + } + } + /* Kill the block if change is too small */ + /* (block_bound defaults to 128, see opts.c) */ + if (diff < block_bound) + *significantDifferenceP = FALSE; + else { + mp_fwd_dct_block2(current, dest); + *significantDifferenceP = TRUE; + } +} + + + +/*===========================================================================* + * + * appropriate (according to pattern, the coded block pattern) blocks + * of 'current' are diff'ed and DCT'd. + * + * RETURNS: current blocks modified + * + * SIDE EFFECTS: Can remove too-small difference blocks from pattern + * + * PRECONDITIONS: appropriate blocks of 'current' have not yet been + * modified + * + *===========================================================================*/ +void +ComputeDiffDCTs(MpegFrame * const current, + MpegFrame * const prev, + int const by, + int const bx, + vector const m, + int * const patternP) { + + Block motionBlock; + + if (collect_quant && (collect_quant_detailed & 1)) + fprintf(collect_quant_fp, "l\n"); + if (*patternP & 0x20) { + boolean significantDiff; + ComputeMotionBlock(prev->ref_y, by, bx, m, &motionBlock); + ComputeDiffDCTBlock(current->y_blocks[by][bx], dct[by][bx], + motionBlock, &significantDiff); + if (!significantDiff) + *patternP ^= 0x20; + } + + if (*patternP & 0x10) { + boolean significantDiff; + ComputeMotionBlock(prev->ref_y, by, bx+1, m, &motionBlock); + ComputeDiffDCTBlock(current->y_blocks[by][bx+1], dct[by][bx+1], + motionBlock, &significantDiff); + if (!significantDiff) + *patternP ^= 0x10; + } + + if (*patternP & 0x8) { + boolean significantDiff; + ComputeMotionBlock(prev->ref_y, by+1, bx, m, &motionBlock); + ComputeDiffDCTBlock(current->y_blocks[by+1][bx], dct[by+1][bx], + motionBlock, &significantDiff); + if (!significantDiff) + *patternP ^= 0x8; + } + + if (*patternP & 0x4) { + boolean significantDiff; + ComputeMotionBlock(prev->ref_y, by+1, bx+1, m, &motionBlock); + ComputeDiffDCTBlock(current->y_blocks[by+1][bx+1], dct[by+1][bx+1], + motionBlock, &significantDiff); + if (!significantDiff) + *patternP ^= 0x4; + } + + if (collect_quant && (collect_quant_detailed & 1)) + fprintf(collect_quant_fp, "c\n"); + + if (*patternP & 0x2) { + boolean significantDiff; + ComputeMotionBlock(prev->ref_cb, by/2, bx/2, halfVector(m), + &motionBlock); + ComputeDiffDCTBlock(current->cb_blocks[by/2][bx/2], + dctb[by/2][bx/2], motionBlock, + &significantDiff); + if (!significantDiff) + *patternP ^= 0x2; + } + + if (*patternP & 0x1) { + boolean significantDiff; + ComputeMotionBlock(prev->ref_cr, by/2, bx/2, halfVector(m), + &motionBlock); + ComputeDiffDCTBlock(current->cr_blocks[by/2][bx/2], + dctr[by/2][bx/2], motionBlock, + &significantDiff); + if (!significantDiff) + *patternP ^= 0x1; + } +} + + + +static void +computePrevFyFx(MpegFrame * const prevFrame, + int const by, + int const bx, + vector const m, + uint8 *** const prevP, + int * const fyP, + int * const fxP) { + + boolean const xHalf = (ABS(m.x) % 2 == 1); + boolean const yHalf = (ABS(m.y) % 2 == 1); + + MotionToFrameCoord(by, bx, m.y/2, m.x/2, fyP, fxP); + + assert(*fyP >= 0); assert(*fxP >= 0); + + /* C integer arithmetic rounds toward zero. But what we need is a + "floor" -- i.e. round down. So we adjust now for where the dividend + in the above divide by two was negative. + */ + + if (xHalf) { + if (m.x < 0) + --*fxP; + + if (yHalf) { + if (m.y < 0) + --*fyP; + + *prevP = prevFrame->halfBoth; + } else + *prevP = prevFrame->halfX; + } else if (yHalf) { + if (m.y < 0) + --*fyP; + + *prevP = prevFrame->halfY; + } else + *prevP = prevFrame->ref_y; +} + + + +/*======================* + * COMPUTE MOTION BLOCK * + *======================*/ + +/*===========================================================================* + * + * compute the motion-compensated block + * + * RETURNS: motionBlock + * + * SIDE EFFECTS: none + * + * PRECONDITIONS: motion vector MUST be valid + * + * NOTE: could try to speed this up using halfX, halfY, halfBoth, + * but then would have to compute for chrominance, and it's just + * not worth the trouble (this procedure is not called relatively + * often -- a constant number of times per macroblock) + * + *===========================================================================*/ +void +ComputeMotionBlock(uint8 ** const prev, + int const by, + int const bx, + vector const m, + Block * const motionBlockP) { + + int fy, fx; + boolean xHalf, yHalf; + + xHalf = (ABS(m.x) % 2 == 1); + yHalf = (ABS(m.y) % 2 == 1); + + MotionToFrameCoord(by, bx, m.y/2, m.x/2, &fy, &fx); + + if (xHalf && yHalf) { + unsigned int y; + /* really should be fy+y-1 and fy+y so do (fy-1)+y = fy+y-1 and + (fy-1)+y+1 = fy+y + */ + if (m.y < 0) + --fy; + + if (m.x < 0) + --fx; + + for (y = 0; y < 8; ++y) { + int16 * const destPtr = (*motionBlockP)[y]; + uint8 * const srcPtr = &(prev[fy+y][fx]); + uint8 * const srcPtr2 = &(prev[fy+y+1][fx]); + unsigned int x; + + for (x = 0; x < 8; ++x) + destPtr[x] = + (srcPtr[x]+srcPtr[x+1]+srcPtr2[x]+srcPtr2[x+1]+2) >> 2; + } + } else if (xHalf) { + unsigned int y; + if (m.x < 0) + --fx; + + for (y = 0; y < 8; ++y) { + int16 * const destPtr = (*motionBlockP)[y]; + uint8 * const srcPtr = &(prev[fy+y][fx]); + unsigned int x; + + for (x = 0; x < 8; ++x) + destPtr[x] = (srcPtr[x]+srcPtr[x+1]+1) >> 1; + } + } else if (yHalf) { + unsigned int y; + if ( m.y < 0 ) + fy--; + + for (y = 0; y < 8; ++y) { + int16 * const destPtr = (*motionBlockP)[y]; + uint8 * const srcPtr = &(prev[fy+y][fx]); + uint8 * const srcPtr2 = &(prev[fy+y+1][fx]); + unsigned int x; + + for (x = 0; x < 8; ++x) + destPtr[x] = (srcPtr[x]+srcPtr2[x]+1) >> 1; + } + } else { + unsigned int y; + for (y = 0; y < 8; ++y) { + int16 * const destPtr = (*motionBlockP)[y]; + uint8 * const srcPtr = &(prev[fy+y][fx]); + unsigned int x; + + for (x = 0; x < 8; ++x) + destPtr[x] = srcPtr[x]; + } + } +} + + + +/*===========================================================================* + * + * compute the motion-compensated luminance block + * + * RETURNS: motionBlock + * + * SIDE EFFECTS: none + * + * PRECONDITIONS: motion vector MUST be valid + * + * NOTE: see ComputeMotionBlock + * + *===========================================================================*/ +void +ComputeMotionLumBlock(MpegFrame * const prevFrame, + int const by, + int const bx, + vector const m, + LumBlock * const motionBlockP) { + + unsigned int y; + uint8 ** prev; + int fy, fx; + + computePrevFyFx(prevFrame, by, bx, m, &prev, &fy, &fx); + + for (y = 0; y < 16; ++y) { + uint8 * const across = &(prev[fy+y][fx]); + int32 * const macross = motionBlockP->l[y]; + unsigned int x; + + for (x = 0; x < 16; ++x) + macross[x] = across[x]; + } + + /* this is what's really happening, in slow motion: + * + * for (y = 0; y < 16; ++y, ++py) + * for (x = 0; x < 16; ++x, ++px) + * motionBlock[y][x] = prev[fy+y][fx+x]; + * + */ +} + + +/*=======================* + * BASIC ERROR FUNCTIONS * + *=======================*/ + + +/*===========================================================================* + * + * return the MAD of two luminance blocks + * + * RETURNS: the MAD, if less than bestSoFar, or some number bigger if not + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +int32 +LumBlockMAD(const LumBlock * const currentBlockP, + const LumBlock * const motionBlockP, + int32 const bestSoFar) { + + int32 diff; /* max value of diff is 255*256 = 65280 */ + unsigned int y; + + diff = 0; /* initial value */ + + for (y = 0; y < 16; ++y) { + const int32 * const currentRow = currentBlockP->l[y]; + const int32 * const motionRow = motionBlockP->l[y]; + unsigned int x; + for (x = 0; x < 16; ++x) + diff += ABS(currentRow[x] - motionRow[x]); + + if (diff > bestSoFar) + /* We already know the MAD won't be less than bestSoFar; + Caller doesn't care by how much we missed, so just return + this. + */ + return diff; + } + /* Return the actual MAD */ + return diff; +} + + + +/*===========================================================================* + * + * return the MAD of the currentBlock and the motion-compensated block + * (without TUNEing) + * + * (by, bx) is the location of the block in the frame + * (block number coordinates). 'm' is the motion of the block in pixels. + * The moved block must be wholly within the frame. + * + * RETURNS: the MAD, if less than bestSoFar, or + * some number bigger if not + * + * SIDE EFFECTS: none + * + * PRECONDITIONS: motion vector MUST be valid + * + * NOTES: this is the procedure that is called the most, and should therefore + * be the most optimized!!! + * + *===========================================================================*/ +int32 +LumMotionError(const LumBlock * const currentBlockP, + MpegFrame * const prevFrame, + int const by, + int const bx, + vector const m, + int32 const bestSoFar) { + + int32 adiff; + int32 diff; /* max value of diff is 255*256 = 65280 */ + int y; + uint8 **prev; + int fy, fx; + + computePrevFyFx(prevFrame, by, bx, m, &prev, &fy, &fx); + + assert(fy >= 0); assert(fx >= 0); + + adiff = 0; /* initial value */ + diff = 0; /* initial value */ + + switch (SearchCompareMode) { + case DEFAULT_SEARCH: /* Default. */ + for (y = 0; y < 16; ++y) { + const int32 * const cacross = currentBlockP->l[y]; + uint8 * const across = &prev[fy+y][fx]; + unsigned int x; + + for (x = 0; x < 16; ++x) { + int32 const localDiff = across[x]-cacross[x]; + diff += ABS(localDiff); + } + if (diff > bestSoFar) + return diff; + } + break; + + case LOCAL_DCT: { + Block dctdiff[4], dctquant[4]; + FlatBlock quant; + int x, i, tmp; + int distortion=0, datarate=0; + int pq = GetPQScale(); + + for (y = 0; y < 16; ++y) { + const int32 * const cacross = currentBlockP->l[y]; + uint8 * const across = &(prev[fy+y][fx]); + for (x = 0; x < 16; ++x) { + dctdiff[(x>7)+2*(y>7)][y%8][x%8] = cacross[x]-across[x]; + }} + + /* Calculate rate */ + for (i = 0; i < 4; ++i) { + mp_fwd_dct_block2(dctdiff[i], dctdiff[i]); + if (Mpost_QuantZigBlock(dctdiff[i], quant, pq, FALSE) == + MPOST_ZERO) { + /* no sense in continuing */ + memset((char *)dctquant[i], 0, sizeof(Block)); + } else { + Mpost_UnQuantZigBlock(quant, dctquant[i], pq, FALSE); + mpeg_jrevdct((int16 *)dctquant[i]); + datarate += CalcRLEHuffLength(quant); + } + } + + /* Calculate distortion */ + for (y = 0; y < 16; ++y) { + const int32 * const cacross = currentBlockP->l[y]; + uint8 * const across = &(prev[fy+y][fx]); + for (x = 0; x < 16; ++x) { + tmp = across[x] - cacross[x] + + dctquant[(x>7)+2*(y>7)][y%8][x%8]; + distortion += tmp*tmp; + }} + distortion /= 256; + distortion *= LocalDCTDistortScale; + datarate *= LocalDCTRateScale; + diff = (int) sqrt(distortion*distortion + datarate*datarate); + break; + } + + case NO_DC_SEARCH: { + extern int32 niqtable[]; + int pq = niqtable[0]*GetPQScale(); + unsigned int y; + + for (y = 0; y < 16; ++y) { + const int32 * const cacross = currentBlockP->l[y]; + uint8 * const across = &(prev[fy+y][fx]); + unsigned int x; + + for (x = 0; x < 16; ++x) { + int32 const localDiff = across[x]-cacross[x]; + diff += localDiff; + adiff += ABS(localDiff); + } + } + + diff /= 64*pq; /* diff is now the DC difference (with QSCALE 1) */ + adiff -= 64*pq*ABS(diff); + diff = adiff; + } + break; + + case DO_Mean_Squared_Distortion: + for (y = 0; y < 16; ++y) { + const int32 * const cacross = currentBlockP->l[y]; + uint8 * const across = &(prev[fy+y][fx]); + unsigned int x; + + for (x = 0; x < 16; ++x) { + int32 const localDiff = across[x] - cacross[x]; + diff += localDiff * localDiff; + } + if (diff > bestSoFar) + return diff; + } + break; + } /* End of Switch */ + + return diff; +} + + + +/*===========================================================================* + * + * return the MAD of the currentBlock and the average of the blockSoFar + * and the motion-compensated block (this is used for B-frame searches) + * + * RETURNS: the MAD, if less than bestSoFar, or + * some number bigger if not + * + * SIDE EFFECTS: none + * + * PRECONDITIONS: motion vector MUST be valid + * + *===========================================================================*/ +int32 +LumAddMotionError(const LumBlock * const currentBlockP, + const LumBlock * const blockSoFarP, + MpegFrame * const prevFrame, + int const by, + int const bx, + vector const m, + int32 const bestSoFar) { + + int32 diff; /* max value of diff is 255*256 = 65280 */ + int y; + uint8 **prev; + int fy, fx; + + computePrevFyFx(prevFrame, by, bx, m, &prev, &fy, &fx); + + diff = 0; /* initial value */ + + /* do we add 1 before dividing by two? Yes -- see MPEG-1 doc page 46 */ + + for (y = 0; y < 16; ++y) { + unsigned int x; + const uint8 * const across = &prev[fy+y][fx]; + const int32 * const bacross = blockSoFarP->l[y]; + const int32 * const cacross = currentBlockP->l[y]; + + for (x = 0; x < 16; ++x) { + int32 const localDiff = + ((across[x] + bacross[x] + 1) / 2) - cacross[x]; + diff += ABS(localDiff); + } + + if (diff > bestSoFar) + return diff; + } + + /* This is what's happening: + * + * ComputeMotionLumBlock(prevFrame, by, bx, my, mx, lumMotionBlock); + * + * for (y = 0; y < 16; ++y) + * for (x = 0; x < 16; ++x) { + * localDiff = currentBlock[y][x] - lumMotionBlock[y][x]; + * diff += ABS(localDiff); + * } + */ + + return diff; +} + + + +/*===========================================================================* + * + * adds the motion-compensated block to the given block + * + * RETURNS: block modified + * + * SIDE EFFECTS: none + * + * PRECONDITIONS: motion vector MUST be valid + * + *===========================================================================*/ +void +AddMotionBlock(Block block, + uint8 ** const prev, + int const by, + int const bx, + vector const m) { + + int fy, fx; + boolean xHalf, yHalf; + + xHalf = (ABS(m.x) % 2 == 1); + yHalf = (ABS(m.y) % 2 == 1); + + MotionToFrameCoord(by, bx, (m.y/2), (m.x/2), &fy, &fx); + + if (xHalf && yHalf) { + unsigned int y; + /* really should be fy+y-1 and fy+y so do (fy-1)+y = fy+y-1 and + (fy-1)+y+1 = fy+y + */ + if (m.y < 0) + --fy; + if (m.x < 0) + --fx; + + for (y = 0; y < 8; ++y) { + unsigned int x; + for (x = 0; x < 8; ++x) + block[y][x] += (prev[fy+y][fx+x]+prev[fy+y][fx+x+1]+ + prev[fy+y+1][fx+x]+prev[fy+y+1][fx+x+1]+2)>>2; + } + } else if (xHalf) { + unsigned int y; + if (m.x < 0) + --fx; + + for (y = 0; y < 8; ++y) { + unsigned int x; + for (x = 0; x < 8; ++x) { + block[y][x] += (prev[fy+y][fx+x]+prev[fy+y][fx+x+1]+1)>>1; + } + } + } else if ( yHalf ) { + unsigned int y; + if (m.y < 0) + --fy; + + for (y = 0; y < 8; ++y) { + unsigned int x; + for (x = 0; x < 8; ++x) { + block[y][x] += (prev[fy+y][fx+x]+prev[fy+y+1][fx+x]+1)>>1; + } + } + } else { + unsigned int y; + for (y = 0; y < 8; ++y) { + unsigned int x; + for (x = 0; x < 8; ++x) { + block[y][x] += (int16)prev[fy+y][fx+x]; + } + } + } +} + + +/*===========================================================================* + * + * adds the motion-compensated B-frame block to the given block + * + * RETURNS: block modified + * + * SIDE EFFECTS: none + * + * PRECONDITIONS: motion vectors MUST be valid + * + *===========================================================================*/ +void +AddBMotionBlock(Block block, + uint8 ** const prev, + uint8 ** const next, + int const by, + int const bx, + int const mode, + motion const motion) { + + unsigned int y; + Block prevBlock, nextBlock; + + switch (mode) { + case MOTION_FORWARD: + AddMotionBlock(block, prev, by, bx, motion.fwd); + break; + case MOTION_BACKWARD: + AddMotionBlock(block, next, by, bx, motion.bwd); + break; + default: + ComputeMotionBlock(prev, by, bx, motion.fwd, &prevBlock); + ComputeMotionBlock(next, by, bx, motion.bwd, &nextBlock); + } + for (y = 0; y < 8; ++y) { + unsigned int x; + for (x = 0; x < 8; ++x) { + block[y][x] += (prevBlock[y][x] + nextBlock[y][x] + 1) / 2; + } + } +} + + +/*===========================================================================* + * + * copies the given block into the appropriate data area + * + * RETURNS: data modified + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void +BlockToData(uint8 ** const data, + Block block, + int const by, + int const bx) { + + int x, y; + int fy, fx; + int16 blockItem; + + BLOCK_TO_FRAME_COORD(by, bx, fy, fx); + + for ( y = 0; y < 8; y++ ) { + for ( x = 0; x < 8; x++ ) { + blockItem = block[y][x]; + data[fy+y][fx+x] = TRUNCATE_UINT8(blockItem); + } + } +} + + +/*===========================================================================* + * + * copies data into appropriate blocks + * + * RETURNS: mf modified + * + * SIDE EFFECTS: none + * + * NOTES: probably shouldn't be in this file + * + *===========================================================================*/ +void +BlockifyFrame(MpegFrame * const frameP) { + + int dctx, dcty; + int x, y; + int bx, by; + int fy, fx; + int16 *destPtr; + uint8 *srcPtr; + int16 *destPtr2; + uint8 *srcPtr2; + Block *blockPtr; + Block *blockPtr2; + + dctx = Fsize_x / DCTSIZE; + dcty = Fsize_y / DCTSIZE; + + /* + * copy y data into y_blocks + */ + for (by = 0; by < dcty; by++) { + fy = by*DCTSIZE; + for (bx = 0; bx < dctx; bx++) { + fx = bx*DCTSIZE; + blockPtr = (Block *) &(frameP->y_blocks[by][bx][0][0]); + for (y = 0; y < DCTSIZE; y++) { + destPtr = &((*blockPtr)[y][0]); + srcPtr = &(frameP->orig_y[fy+y][fx]); + for (x = 0; x < DCTSIZE; x++) { + destPtr[x] = srcPtr[x]; + } + } + } + } + + /* + * copy cr/cb data into cr/cb_blocks + */ + for (by = 0; by < (dcty >> 1); by++) { + fy = by*DCTSIZE; + for (bx = 0; bx < (dctx >> 1); bx++) { + fx = bx*DCTSIZE; + blockPtr = (Block *) &(frameP->cr_blocks[by][bx][0][0]); + blockPtr2 = (Block *) &(frameP->cb_blocks[by][bx][0][0]); + for (y = 0; y < DCTSIZE; y++) { + destPtr = &((*blockPtr)[y][0]); + srcPtr = &(frameP->orig_cr[fy+y][fx]); + destPtr2 = &((*blockPtr2)[y][0]); + srcPtr2 = &(frameP->orig_cb[fy+y][fx]); + for (x = 0; x < DCTSIZE; x++) { + destPtr[x] = srcPtr[x]; + destPtr2[x] = srcPtr2[x]; + } + } + } + } +} + + +/*===========================================================================* + * * + * UNUSED PROCEDURES * + * * + * The following procedures are all unused by the encoder * + * * + * They are listed here for your convenience. You might want to use * + * them if you experiment with different search techniques * + * * + *===========================================================================*/ + +#ifdef UNUSED_PROCEDURES + +/* this procedure calculates the subsampled motion block (obviously) + * + * for speed, this procedure is probably not called anywhere (it is + * incorporated directly into LumDiffA, LumDiffB, etc. + * + * but leave it here anyway for clarity + * + * (startY, startX) = (0,0) for A....(0,1) for B...(1,0) for C...(1,1) for D + * + */ +void +ComputeSubSampledMotionLumBlock(MpegFrame * const prevFrame, + int const by, + int const bx, + int const my, + int const mx, + LumBlock const motionBlock, + int const startY, + int const startX) { + + uint8 *across; + int32 *macross; + int32 *lastx; + int y; + uint8 **prev; + int fy, fx; + boolean xHalf, yHalf; + + xHalf = (ABS(mx) % 2 == 1); + yHalf = (ABS(my) % 2 == 1); + + MotionToFrameCoord(by, bx, my/2, mx/2, &fy, &fx); + + if ( xHalf ) { + if ( mx < 0 ) { + fx--; + } + + if ( yHalf ) { + if ( my < 0 ) { + fy--; + } + + prev = prevFrame->halfBoth; + } else { + prev = prevFrame->halfX; + } + } else if ( yHalf ) { + if ( my < 0 ) { + fy--; + } + + prev = prevFrame->halfY; + } else { + prev = prevFrame->ref_y; + } + + for ( y = startY; y < 16; y += 2 ) { + across = &(prev[fy+y][fx+startX]); + macross = &(motionBlock[y][startX]); + lastx = &(motionBlock[y][16]); + while ( macross < lastx ) { + (*macross) = (*across); + across += 2; + macross += 2; + } + } + + /* this is what's really going on in slow motion: + * + * for ( y = startY; y < 16; y += 2 ) + * for ( x = startX; x < 16; x += 2 ) + * motionBlock[y][x] = prev[fy+y][fx+x]; + * + */ +} + + +/*===========================================================================* + * + * return the MAD of the currentBlock and the motion-compensated block, + * subsampled 4:1 with given starting coordinates (startY, startX) + * + * RETURNS: the MAD + * + * SIDE EFFECTS: none + * + * PRECONDITIONS: motion vector MUST be valid + * + * NOTES: this procedure is never called. Instead, see subsample.c. This + * procedure is provided only for possible use in extensions + * + *===========================================================================*/ +int32 +LumMotionErrorSubSampled(LumBlock const currentBlock, + MpegFrame * const prevFrame, + int const by, + int const bx, + int const my, + int const mx, + int const startY, + int const startX) { + + int32 diff; /* max value of diff is 255*256 = 65280 */ + int32 localDiff; + int32 *cacross; + uint8 *macross; + int32 *lastx; + int y; + uint8 **prev; + int fy, fx; + boolean xHalf, yHalf; + + xHalf = (ABS(mx) % 2 == 1); + yHalf = (ABS(my) % 2 == 1); + + motionToFrameCoord(by, bx, my/2, mx/2, &fy, &fx); + + if ( xHalf ) { + if ( mx < 0 ) { + fx--; + } + + if ( yHalf ) { + if ( my < 0 ) { + fy--; + } + + prev = prevFrame->halfBoth; + } else { + prev = prevFrame->halfX; + } + } else if ( yHalf ) { + if ( my < 0 ) { + fy--; + } + + prev = prevFrame->halfY; + } else { + prev = prevFrame->ref_y; + } + + diff = 0; /* initial value */ + + for ( y = startY; y < 16; y += 2 ) { + macross = &(prev[fy+y][fx+startX]); + cacross = &(currentBlock[y][startX]); + lastx = &(currentBlock[y][16]); + while ( cacross < lastx ) { + localDiff = (*cacross)-(*macross); + diff += ABS(localDiff); + macross += 2; + cacross += 2; + } + } + + /* this is what's really happening: + * + * ComputeSubSampledMotionLumBlock(prevFrame, by, bx, my, mx, + * lumMotionBlock, startY, startX); + * + * for ( y = startY; y < 16; y += 2 ) + * for ( x = startX; x < 16; x += 2 ) + * { + * localDiff = currentBlock[y][x] - lumMotionBlock[y][x]; + * diff += ABS(localDiff); + * } + * + */ + + return (int32)diff; +} + + +#endif /* UNUSED_PROCEDURES */ +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + diff --git a/converter/ppm/ppmtompeg/bsearch.c b/converter/ppm/ppmtompeg/bsearch.c new file mode 100644 index 00000000..142987f5 --- /dev/null +++ b/converter/ppm/ppmtompeg/bsearch.c @@ -0,0 +1,1088 @@ +/*===========================================================================* + * bsearch.c + * + * Procedures concerned with the B-frame motion search + * + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/* + * $Header: /n/picasso/project/mpeg/mpeg_dist/mpeg_encode/RCS/bsearch.c,v 1.10 1995/08/07 21:49:01 smoot Exp $ + * $Log: bsearch.c,v $ + * Revision 1.10 1995/08/07 21:49:01 smoot + * fixed bug in initial-B-frame searches + * + * Revision 1.9 1995/06/26 21:36:07 smoot + * added new ordering constraints + * (B frames which are backward P's at the start of a sequence) + * + * Revision 1.8 1995/03/27 19:17:43 smoot + * killed useless type error messge (int32 defiend as int) + * + * Revision 1.7 1995/01/19 23:07:20 eyhung + * Changed copyrights + * + * Revision 1.6 1994/12/07 00:40:36 smoot + * Added seperate P and B search ranges + * + * Revision 1.5 1994/03/15 00:27:11 keving + * nothing + * + * Revision 1.4 1993/12/22 19:19:01 keving + * nothing + * + * Revision 1.3 1993/07/22 22:23:43 keving + * nothing + * + * Revision 1.2 1993/06/30 20:06:09 keving + * nothing + * + * Revision 1.1 1993/06/03 21:08:08 keving + * nothing + * + * Revision 1.1 1993/03/02 18:27:05 keving + * nothing + * + */ + + +/*==============* + * HEADER FILES * + *==============*/ + +#include "pm_c_util.h" +#include "pm.h" +#include "all.h" +#include "mtypes.h" +#include "frames.h" +#include "motion_search.h" +#include "fsize.h" + + +/*==================* + * STATIC VARIABLES * + *==================*/ + +static int bsearchAlg; + + +/*===========================* + * INITIALIZATION PROCEDURES * + *===========================*/ + + +/*===========================================================================* + * + * SetBSearchAlg + * + * set the B-search algorithm + * + * RETURNS: nothing + * + * SIDE EFFECTS: bsearchAlg modified + * + *===========================================================================*/ +void +SetBSearchAlg(const char * const alg) { + if (strcmp(alg, "SIMPLE") == 0) + bsearchAlg = BSEARCH_SIMPLE; + else if (strcmp(alg, "CROSS2") == 0) + bsearchAlg = BSEARCH_CROSS2; + else if (strcmp(alg, "EXHAUSTIVE") == 0) + bsearchAlg = BSEARCH_EXHAUSTIVE; + else + pm_error("ERROR: Impossible bsearch alg: %s", alg); +} + + +/*===========================================================================* + * + * BSearchName + * + * return the text of the B-search algorithm + * + * RETURNS: a pointer to the string + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +const char * +BSearchName(void) +{ + const char *retval; + + switch(bsearchAlg) { + case BSEARCH_SIMPLE: + retval = "SIMPLE";break; + case BSEARCH_CROSS2: + retval = "CROSS2";break; + case BSEARCH_EXHAUSTIVE: + retval = "EXHAUSTIVE";break; + default: + pm_error("ERROR: Impossible BSEARCH ALG: %d", psearchAlg); + } + return retval; +} + + + +/*===========================================================================* + * + * FindBestMatchExhaust + * + * tries to find matching motion vector + * see FindBestMatch for generic description + * + * DESCRIPTION: uses an exhaustive search + * + *===========================================================================*/ +static int32 +FindBestMatchExhaust(const LumBlock * const blockP, + const LumBlock * const currentBlockP, + MpegFrame * const prev, + int const by, + int const bx, + vector * const motionP, + int32 const bestSoFar, + int const searchRange) { + + register int mx, my; + int32 bestDiff; + int stepSize; + int leftMY, leftMX; + int rightMY, rightMX; + int distance; + int tempRightMY, tempRightMX; + boolean changed = FALSE; + + stepSize = (pixelFullSearch ? 2 : 1); + + COMPUTE_MOTION_BOUNDARY(by,bx,stepSize,leftMY,leftMX,rightMY,rightMX); + + /* try old motion vector first */ + if (VALID_MOTION(*motionP)) { + bestDiff = LumAddMotionError(currentBlockP, blockP, prev, by, bx, + *motionP, bestSoFar); + + if (bestSoFar < bestDiff) + bestDiff = bestSoFar; + } else { + motionP->y = motionP->x = 0; + bestDiff = bestSoFar; + } + + /* maybe should try spiral pattern centered around prev motion vector? */ + + /* try a spiral pattern */ + for (distance = stepSize; + distance <= searchRange; + distance += stepSize) { + tempRightMY = MIN(distance, rightMY); + tempRightMX = MIN(distance, rightMX); + + /* do top, bottom */ + for (my = -distance; my < tempRightMY; + my += max(tempRightMY+distance-stepSize, stepSize)) { + if (my >= leftMY) { + for (mx = -distance; mx < tempRightMX; mx += stepSize) { + if (mx >= leftMX) { + int diff; + vector m; + m.y = my; m.x = mx; + diff = LumAddMotionError(currentBlockP, blockP, prev, + by, bx, m, bestDiff); + + if (diff < bestDiff) { + *motionP = m; + bestDiff = diff; + } + } + } + } + } + + /* do left, right */ + for (mx = -distance; + mx < tempRightMX; + mx += max(tempRightMX+distance-stepSize, stepSize)) { + if (mx >= leftMX) { + for (my = -distance+stepSize; + my < tempRightMY-stepSize; + my += stepSize) { + if (my >= leftMY) { + int diff; + vector m; + m.y = my; m.x = mx; + diff = LumAddMotionError(currentBlockP, blockP, prev, + by, bx, + m, bestDiff); + + if (diff < bestDiff) { + *motionP = m; + bestDiff = diff; + changed = TRUE; + } + } + } + } + } + } + + if (!changed) + ++bestDiff; + + return bestDiff; +} + + +/*===========================================================================* + * + * FindBestMatchTwoLevel + * + * tries to find matching motion vector + * see FindBestMatch for generic description + * + * DESCRIPTION: uses an exhaustive full-pixel search, then looks at + * neighboring half-pixels + * + *===========================================================================*/ +static int32 +FindBestMatchTwoLevel(const LumBlock * const blockP, + const LumBlock * const currentBlockP, + MpegFrame * const prev, + int const by, + int const bx, + vector * const motionP, + int32 const bestSoFar, + int const searchRange) { + + int mx, my; + int32 bestDiff; + int leftMY, leftMX; + int rightMY, rightMX; + int distance; + int tempRightMY, tempRightMX; + boolean changed = FALSE; + int yOffset, xOffset; + + /* exhaustive full-pixel search first */ + + COMPUTE_MOTION_BOUNDARY(by,bx,2,leftMY,leftMX,rightMY,rightMX); + + --rightMY; + --rightMX; + + /* convert vector into full-pixel vector */ + if (motionP->y > 0 ) { + if ((motionP->y % 2) == 1) + --motionP->y; + } else if ((-motionP->y % 2) == 1) + ++motionP->y; + + if (motionP->x > 0 ) { + if ((motionP->x % 2) == 1 ) + --motionP->x; + } else if ((-motionP->x % 2) == 1) + ++motionP->x; + + /* try old motion vector first */ + if (VALID_MOTION(*motionP)) { + bestDiff = LumAddMotionError(currentBlockP, blockP, prev, by, bx, + *motionP, bestSoFar); + + if (bestSoFar < bestDiff) + bestDiff = bestSoFar; + } else { + motionP->y = motionP->x = 0; + bestDiff = bestSoFar; + } + + ++rightMY; + ++rightMX; + + /* maybe should try spiral pattern centered around prev motion vector? */ + + /* try a spiral pattern */ + for ( distance = 2; distance <= searchRange; distance += 2 ) { + tempRightMY = MIN(distance, rightMY); + tempRightMX = MIN(distance, rightMX); + + /* do top, bottom */ + for (my = -distance; my < tempRightMY; + my += max(tempRightMY+distance-2, 2)) { + if (my >= leftMY) { + for ( mx = -distance; mx < tempRightMX; mx += 2 ) { + if (mx >= leftMX) { + int diff; + vector m; + m.y = my; m.x = mx; + diff = LumAddMotionError(currentBlockP, blockP, prev, + by, bx, m, bestDiff); + + if (diff < bestDiff) { + *motionP = m; + bestDiff = diff; + } + } + } + } + } + + /* do left, right */ + for (mx = -distance; + mx < tempRightMX; + mx += max(tempRightMX+distance-2, 2)) { + if (mx >= leftMX) { + for (my = -distance+2; my < tempRightMY-2; my += 2) { + if (my >= leftMY) { + int diff; + vector m; + m.y = my; m.x = mx; + diff = LumAddMotionError(currentBlockP, blockP, prev, + by, bx, m, bestDiff); + + if (diff < bestDiff) { + *motionP = m; + bestDiff = diff; + changed = TRUE; + } + } + } + } + } + } + + /* now look at neighboring half-pixels */ + my = motionP->y; + mx = motionP->x; + + --rightMY; + --rightMX; + + for (yOffset = -1; yOffset <= 1; ++yOffset) { + for (xOffset = -1; xOffset <= 1; ++xOffset) { + if ((yOffset != 0) || (xOffset != 0)) { + vector m; + m.y = my + yOffset; + m.x = mx + xOffset; + if (VALID_MOTION(m)) { + int diff; + diff = LumAddMotionError(currentBlockP, blockP, + prev, by, bx, m, bestDiff); + if (diff < bestDiff) { + *motionP = m; + bestDiff = diff; + changed = TRUE; + } + } + } + } + } + + if (!changed) + ++bestDiff; + + return bestDiff; +} + + + +static void +trySpacing(int const spacing, + vector const center, + int const bestDiffSoFar, + vector * const newCenterP, + int32 * const newBestDiffP, + int const leftMY, + int const leftMX, + int const rightMY, + int const rightMX, + const LumBlock * const currentBlockP, + const LumBlock * const blockP, + MpegFrame * const prev, + int const by, + int const bx) { + + int tempRightMY, tempRightMX; + int my; + int bestDiff; + vector newCenter; + + /* Initial values */ + newCenter = center; + bestDiff = bestDiffSoFar; + + tempRightMY = MIN(rightMY, center.y + spacing + 1); + tempRightMX = MIN(rightMX, center.x + spacing + 1); + + for (my = center.y - spacing; my < tempRightMY; my += spacing) { + if (my >= leftMY) { + int mx; + for (mx = center.x - spacing; mx < tempRightMX; mx += spacing) { + if (mx >= leftMX) { + int32 diff; + vector m; + m.y = my; m.x = mx; + diff = LumAddMotionError(currentBlockP, blockP, prev, + by, bx, m, bestDiff); + + if (diff < bestDiff) { + /* We have a new best */ + newCenter = m; + bestDiff = diff; + } + } + } + } + } + *newCenterP = newCenter; + *newBestDiffP = bestDiff; +} + + + +static void +chooseNewSpacing(int const oldSpacing, + int const stepSize, + int * const newSpacingP) { + + if (stepSize == 2) { /* make sure spacing is even */ + if (oldSpacing == 2) + *newSpacingP = 0; + else { + int const trialSpacing = (oldSpacing + 1) / 2; + if ((trialSpacing % 2) != 0) + *newSpacingP = trialSpacing + 1; + else + *newSpacingP = trialSpacing; + } + } else { + if (oldSpacing == 1) + *newSpacingP = 0; + else + *newSpacingP = (oldSpacing + 1) / 2; + } +} + + + +/*===========================================================================* + * + * FindBestMatchLogarithmic + * + * tries to find matching motion vector + * see FindBestMatch for generic description + * + * DESCRIPTION: uses a logarithmic search + * + *===========================================================================*/ +static int32 +FindBestMatchLogarithmic(const LumBlock * const blockP, + const LumBlock * const currentBlockP, + MpegFrame * const prev, + int const by, + int const bx, + vector * const motionP, + int32 const bestSoFar, + int const searchRange) { + + int const stepSize = (pixelFullSearch ? 2 : 1); + + int32 diff, bestDiff; + int leftMY, leftMX; + int rightMY, rightMX; + int spacing; + vector center; + + COMPUTE_MOTION_BOUNDARY(by, bx, stepSize, + leftMY, leftMX, rightMY, rightMX); + + bestDiff = 0x7fffffff; + + /* grid spacing */ + if (stepSize == 2) { /* make sure spacing is even */ + spacing = (searchRange + 1) / 2; + if ((spacing % 2) != 0) + ++spacing; + } else + spacing = (searchRange + 1) / 2; + + /* Start at (0,0) */ + center.y = center.x = 0; + + while (spacing >= stepSize) { + trySpacing(spacing, center, bestDiff, + ¢er, &bestDiff, + leftMY, leftMX, rightMY, rightMX, + currentBlockP, blockP, prev, by, bx); + + chooseNewSpacing(spacing, stepSize, &spacing); + } + + /* check old motion -- see if it's better */ + if ((motionP->y >= leftMY) && (motionP->y < rightMY) && + (motionP->x >= leftMX) && (motionP->x < rightMX)) { + diff = LumAddMotionError(currentBlockP, blockP, prev, by, bx, + *motionP, bestDiff); + } else + diff = 0x7fffffff; + + if (bestDiff < diff) + *motionP = center; + else + bestDiff = diff; + + return bestDiff; +} + + + +/*===========================================================================* + * + * FindBestMatchSubSample + * + * tries to find matching motion vector + * see FindBestMatch for generic description + * + * DESCRIPTION: should use subsampling method, but too lazy to write all + * the code for it (so instead just calls FindBestMatchExhaust) + * + *===========================================================================*/ +static int32 +FindBestMatchSubSample(const LumBlock * const blockP, + const LumBlock * const currentBlockP, + MpegFrame * const prev, + int const by, + int const bx, + vector * const motionP, + int32 const bestSoFar, + int const searchRange) { + + /* too lazy to write the code for this... */ + + return FindBestMatchExhaust(blockP, currentBlockP, prev, + by, bx, motionP, bestSoFar, + searchRange); +} + + +/*===========================================================================* + * + * FindBestMatch + * + * given a motion-compensated block in one direction, tries to find + * the best motion vector in the opposite direction to match it + * + * RETURNS: the best vector (*motionY, *motionX), and the corresponding + * error is returned if it is better than bestSoFar. If not, + * then a number greater than bestSoFar is returned and + * (*motionY, *motionX) has no meaning. + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +static int32 +FindBestMatch(const LumBlock * const blockP, + const LumBlock * const currentBlockP, + MpegFrame * const prev, + int const by, + int const bx, + vector * const motionP, + int32 const bestSoFar, + int const searchRange) { + + int32 result; + + switch(psearchAlg) { + case PSEARCH_SUBSAMPLE: + result = FindBestMatchSubSample( + blockP, currentBlockP, prev, by, bx, + motionP, bestSoFar, searchRange); + break; + case PSEARCH_EXHAUSTIVE: + result = FindBestMatchExhaust( + blockP, currentBlockP, prev, by, bx, + motionP, bestSoFar, searchRange); + break; + case PSEARCH_LOGARITHMIC: + result = FindBestMatchLogarithmic( + blockP, currentBlockP, prev, by, bx, + motionP, bestSoFar, searchRange); + break; + case PSEARCH_TWOLEVEL: + result = FindBestMatchTwoLevel( + blockP, currentBlockP, prev, by, bx, + motionP, bestSoFar, searchRange); + break; + default: + pm_error("ERROR: Impossible P-search alg %d", psearchAlg); + } + return result; +} + + + +/*===========================================================================* + * + * finds the best backward and forward motion vectors + * if backNeeded == FALSE, then won't find best backward vector if it + * is worse than the best forward vector + * + * *motionP is input as well as output. We do not update it + * if it would make the error worse than the existing value. + * + * RETURNS: *motionP and associated errors *forwardErrP and *backErrP. + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +static void +BMotionSearchNoInterp(const LumBlock * const currentBlockP, + MpegFrame * const prev, + MpegFrame * const next, + int const by, + int const bx, + motion * const motionP, + int32 * const forwardErrP, + int32 * const backErrP, + boolean const backNeeded) { + + /* CALL SEARCH PROCEDURE */ + switch(psearchAlg) { + case PSEARCH_SUBSAMPLE: + *forwardErrP = PSubSampleSearch(currentBlockP, prev, by, bx, + &motionP->fwd,searchRangeB); + *backErrP = PSubSampleSearch(currentBlockP, next, by, bx, + &motionP->bwd, searchRangeB); + break; + case PSEARCH_EXHAUSTIVE: + *forwardErrP = PLocalSearch(currentBlockP, prev, by, bx, + &motionP->fwd, + 0x7fffffff, searchRangeB); + if (backNeeded) + *backErrP = PLocalSearch(currentBlockP, next, by, bx, + &motionP->bwd, + 0x7fffffff, searchRangeB); + else + *backErrP = PLocalSearch(currentBlockP, next, by, bx, + &motionP->bwd, + *forwardErrP, searchRangeB); + break; + case PSEARCH_LOGARITHMIC: + *forwardErrP = PLogarithmicSearch(currentBlockP, prev, by, bx, + &motionP->fwd, searchRangeB); + *backErrP = PLogarithmicSearch(currentBlockP, next, by, bx, + &motionP->bwd, searchRangeB); + break; + case PSEARCH_TWOLEVEL: + *forwardErrP = + PTwoLevelSearch(currentBlockP, prev, by, bx, + &motionP->fwd, 0x7fffffff, searchRangeB); + if ( backNeeded ) { + *backErrP = + PTwoLevelSearch(currentBlockP, next, by, bx, + &motionP->bwd, 0x7fffffff, searchRangeB); + } else { + *backErrP = + PTwoLevelSearch(currentBlockP, next, by, bx, + &motionP->bwd, *forwardErrP, searchRangeB); + } + break; + default: + pm_error("ERROR: Impossible PSEARCH ALG: %d", psearchAlg); + } +} + + + +/*===========================================================================* + * + * BMotionSearchSimple + * + * does a simple search for B-frame motion vectors + * see BMotionSearch for generic description + * + * DESCRIPTION: + * 1) find best backward and forward vectors + * 2) compute interpolated error using those two vectors + * 3) return the best of the three choices + * + * *fmyP,fmxP,bmyP,bmxP are inputs as well as outputs. We do not update + * them if it would make the error worse than the existing values. Otherwise, + * we update them to the vectors we find to be best. + * + *===========================================================================*/ +static int +BMotionSearchSimple(const LumBlock * const currentBlockP, + MpegFrame * const prev, + MpegFrame * const next, + int const by, + int const bx, + motion * const motionP, + int const oldMode) { + + int retval; + int32 forwardErr, backErr, interpErr; + LumBlock interpBlock; + int32 bestSoFar; + + /* STEP 1 */ + BMotionSearchNoInterp(currentBlockP, prev, next, by, bx, motionP, + &forwardErr, &backErr, TRUE); + + /* STEP 2 */ + + ComputeBMotionLumBlock(prev, next, by, bx, MOTION_INTERPOLATE, + *motionP, &interpBlock); + bestSoFar = min(backErr, forwardErr); + interpErr = + LumBlockMAD(currentBlockP, &interpBlock, bestSoFar); + + /* STEP 3 */ + + if (interpErr <= forwardErr) { + if (interpErr <= backErr) + retval = MOTION_INTERPOLATE; + else + retval = MOTION_BACKWARD; + } else if (forwardErr <= backErr) + retval = MOTION_FORWARD; + else + retval = MOTION_BACKWARD; + + return retval; +} + + +/*===========================================================================* + * + * BMotionSearchCross2 + * + * does a cross-2 search for B-frame motion vectors + * see BMotionSearch for generic description + * + * DESCRIPTION: + * 1) find best backward and forward vectors + * 2) find best matching interpolating vectors + * 3) return the best of the 4 choices + * + * *fmyP,fmxP,bmyP,bmxP are inputs as well as outputs. We do not update + * them if it would make the error worse than the existing values. Otherwise, + * we update them to the vectors we find to be best. + *===========================================================================*/ +static int +BMotionSearchCross2(const LumBlock * const currentBlockP, + MpegFrame * const prev, + MpegFrame * const next, + int const by, + int const bx, + motion * const motionP, + int const oldMode) { + + int retval; + LumBlock forwardBlock, backBlock; + int32 forwardErr, backErr; + motion newMotion; + int32 interpErrF, interpErrB, interpErr; + int32 bestErr; + + /* STEP 1 */ + + BMotionSearchNoInterp(currentBlockP, prev, next, by, bx, motionP, + &forwardErr, &backErr, TRUE); + + bestErr = min(forwardErr, backErr); + + { + /* STEP 2 */ + + struct motion motion; + motion.fwd = motionP->fwd; + motion.bwd.y = motion.bwd.x = 0; + ComputeBMotionLumBlock(prev, next, by, bx, MOTION_FORWARD, motion, + &forwardBlock); + + motion.fwd.y = motion.fwd.x = 0; + motion.bwd = motionP->bwd; + ComputeBMotionLumBlock(prev, next, by, bx, MOTION_BACKWARD, motion, + &backBlock); + } + /* try a cross-search; total of 4 local searches */ + newMotion = *motionP; + + interpErrF = FindBestMatch(&forwardBlock, currentBlockP, + next, by, bx, &newMotion.bwd, + bestErr, searchRangeB); + bestErr = min(bestErr, interpErr); + interpErrB = FindBestMatch(&backBlock, currentBlockP, + prev, by, bx, &newMotion.fwd, + bestErr, searchRangeB); + + /* STEP 3 */ + + if (interpErrF <= interpErrB) { + newMotion.fwd = motionP->fwd; + interpErr = interpErrF; + } else { + newMotion.bwd = motionP->bwd; + interpErr = interpErrB; + } + + if (interpErr <= forwardErr) { + if (interpErr <= backErr) { + *motionP = newMotion; + retval = MOTION_INTERPOLATE; + } else + retval = MOTION_BACKWARD; + } else if (forwardErr <= backErr) + retval = MOTION_FORWARD; + else + retval = MOTION_BACKWARD; + + return retval; +} + + +/*===========================================================================* + * + * BMotionSearchExhaust + * + * does an exhaustive search for B-frame motion vectors + * see BMotionSearch for generic description + * + * DESCRIPTION: + * 1) find best backward and forward vectors + * 2) use exhaustive search to find best interpolating vectors + * 3) return the best of the 3 choices + * + * *fmyP,fmxP,bmyP,bmxP are inputs as well as outputs. We do not update + * them if it would make the error worse than the existing values. Otherwise, + * we update them to the vectors we find to be best. + *===========================================================================*/ +static int +BMotionSearchExhaust(const LumBlock * const currentBlockP, + MpegFrame * const prev, + MpegFrame * const next, + int const by, + int const bx, + motion * const motionP, + int const oldMode) { + + int mx, my; + int32 bestDiff; + int stepSize; + LumBlock forwardBlock; + int32 forwardErr, backErr; + int leftMY, leftMX; + int rightMY, rightMX; + boolean result; + + /* STEP 1 */ + + BMotionSearchNoInterp(currentBlockP, prev, next, by, bx, motionP, + &forwardErr, &backErr, FALSE); + + if (forwardErr <= backErr) { + bestDiff = forwardErr; + result = MOTION_FORWARD; + } else { + bestDiff = backErr; + result = MOTION_BACKWARD; + } + + /* STEP 2 */ + + stepSize = (pixelFullSearch ? 2 : 1); + + COMPUTE_MOTION_BOUNDARY(by,bx,stepSize,leftMY,leftMX,rightMY,rightMX); + + if (searchRangeB < rightMY) + rightMY = searchRangeB; + if (searchRangeB < rightMX) + rightMX = searchRangeB; + + for (my = -searchRangeB; my < rightMY; my += stepSize) { + if (my >= leftMY) { + for (mx = -searchRangeB; mx < rightMX; mx += stepSize) { + if (mx >= leftMX) { + struct motion motion; + vector newMotion; + int32 diff; + motion.fwd.y = my; motion.fwd.x = mx; + motion.bwd.y = 0; motion.bwd.x = 0; + ComputeBMotionLumBlock(prev, next, by, bx, MOTION_FORWARD, + motion, &forwardBlock); + + newMotion = motion.fwd; + + diff = FindBestMatch(&forwardBlock, + currentBlockP, next, by, bx, + &newMotion, bestDiff, searchRangeB); + + if (diff < bestDiff) { + motionP->fwd = motion.fwd; + motionP->bwd = newMotion; + bestDiff = diff; + result = MOTION_INTERPOLATE; + } + } + } + } + } + return result; +} + + + +/*===========================================================================* + * + * search for the best B-frame motion vectors + * + * RETURNS: MOTION_FORWARD forward motion should be used + * MOTION_BACKWARD backward motion should be used + * MOTION_INTERPOLATE both should be used and interpolated + * + * OUTPUTS: *motionP = TWICE the forward motion vectors + * + * SIDE EFFECTS: none + * + * PRECONDITIONS: The relevant block in 'current' is valid (it has not + * been dct'd). Thus, the data in 'current' can be + * accesed through y_blocks, cr_blocks, and cb_blocks. + * This is not the case for the blocks in 'prev' and + * 'next.' Therefore, references into 'prev' and 'next' + * should be done + * through the struct items ref_y, ref_cr, ref_cb + * + * POSTCONDITIONS: current, prev, next should be unchanged. + * Some computation could be saved by requiring + * the dct'd difference to be put into current's block + * elements here, depending on the search technique. + * However, it was decided that it mucks up the code + * organization a little, and the saving in computation + * would be relatively little (if any). + * + * NOTES: the search procedure MAY return (0,0) motion vectors + * + *===========================================================================*/ +int +BMotionSearch(const LumBlock * const currentBlockP, + MpegFrame * const prev, + MpegFrame * const next, + int const by, + int const bx, + motion * const motionP, + int const oldMode) { + + int retval; + + /* If we are an initial B frame, no possibility of forward motion */ + if (prev == (MpegFrame *) NULL) { + PMotionSearch(currentBlockP, next, by, bx, &motionP->bwd); + return MOTION_BACKWARD; + } + + /* otherwise simply call the appropriate algorithm, based on user + preference + */ + + switch(bsearchAlg) { + case BSEARCH_SIMPLE: + retval = BMotionSearchSimple(currentBlockP, prev, next, by, bx, + motionP, oldMode); + break; + case BSEARCH_CROSS2: + retval = BMotionSearchCross2(currentBlockP, prev, next, by, bx, + motionP, oldMode); + break; + case BSEARCH_EXHAUSTIVE: + retval = BMotionSearchExhaust(currentBlockP, prev, next, by, bx, + motionP, oldMode); + break; + default: + pm_error("Impossible B-frame motion search algorithm: %d", + bsearchAlg); + } + return retval; +} + + +/*===========================================================================* + * * + * UNUSED PROCEDURES * + * * + * The following procedures are all unused by the encoder * + * * + * They are listed here for your convenience. You might want to use * + * them if you experiment with different search techniques * + * * + *===========================================================================*/ + +#ifdef UNUSED_PROCEDURES + +/*===========================================================================* + * + * ValidBMotion + * + * decides if the given B-frame motion is valid + * + * RETURNS: TRUE if the motion is valid, FALSE otherwise + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +boolean +ValidBMotion(by, bx, mode, fmy, fmx, bmy, bmx) + int by; + int bx; + int mode; + int fmy; + int fmx; + int bmy; + int bmx; +{ + if ( mode != MOTION_BACKWARD ) { + /* check forward motion for bounds */ + if ( (by*DCTSIZE+(fmy-1)/2 < 0) || ((by+2)*DCTSIZE+(fmy+1)/2-1 >= Fsize_y) ) { + return FALSE; + } + if ( (bx*DCTSIZE+(fmx-1)/2 < 0) || ((bx+2)*DCTSIZE+(fmx+1)/2-1 >= Fsize_x) ) { + return FALSE; + } + } + + if ( mode != MOTION_FORWARD ) { + /* check backward motion for bounds */ + if ( (by*DCTSIZE+(bmy-1)/2 < 0) || ((by+2)*DCTSIZE+(bmy+1)/2-1 >= Fsize_y) ) { + return FALSE; + } + if ( (bx*DCTSIZE+(bmx-1)/2 < 0) || ((bx+2)*DCTSIZE+(bmx+1)/2-1 >= Fsize_x) ) { + return FALSE; + } + } + + return TRUE; +} + + +#endif /* UNUSED_PROCEDURES */ diff --git a/converter/ppm/ppmtompeg/combine.c b/converter/ppm/ppmtompeg/combine.c new file mode 100644 index 00000000..341bb5dc --- /dev/null +++ b/converter/ppm/ppmtompeg/combine.c @@ -0,0 +1,357 @@ +/*===========================================================================* + * combine.c + * + * Procedures to combine frames or GOPS into an MPEG sequence + * + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/*==============* + * HEADER FILES * + *==============*/ + +#include "all.h" +#include <time.h> +#include <errno.h> +#include <unistd.h> + +#include "ppm.h" +#include "nstring.h" + +#include "mtypes.h" +#include "frames.h" +#include "frametype.h" +#include "motion_search.h" +#include "mpeg.h" +#include "prototypes.h" +#include "parallel.h" +#include "param.h" +#include "readframe.h" +#include "mheaders.h" +#include "fsize.h" +#include "input.h" +#include "combine.h" + + +#define READ_ATTEMPTS 5 /* number of times (seconds) to retry an input file */ + +/*==================* + * GLOBAL VARIABLES * + *==================*/ +extern int yuvWidth, yuvHeight; + + +/*===========================================================================* + * + * AppendFile + * + * appends the output file with the contents of the given input file + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +static void +AppendFile(FILE * const ofP, + FILE * const ifP) { + + uint8 data[9999]; + unsigned int readItems; + + readItems = 9999; + while (readItems == 9999) { + readItems = fread(data, sizeof(uint8), 9999, ifP); + if (readItems > 0) + fwrite(data, sizeof(uint8), readItems, ofP); + } + fclose(ifP); +} + + + +static void +appendSpecifiedGopFiles(struct inputSource * const inputSourceP, + const char * const currentGopPath, + FILE * const ofP) { + + unsigned int fileSeq; + + for (fileSeq = 0; fileSeq < inputSourceP->numInputFiles; ++fileSeq) { + const char * inputFileName; + const char * fileName; + unsigned int nAttempts; + FILE * ifP; + + GetNthInputFileName(inputSourceP, fileSeq, &inputFileName); + asprintfN(&fileName, "%s/%s", currentGOPPath, inputFileName); + + for (nAttempts = 0, ifP = NULL; + nAttempts < READ_ATTEMPTS && !ifP; + ++nAttempts) { + + FILE * ifP; + ifP = fopen(fileName, "rb"); + if (ifP == NULL) { + pm_message("ERROR: Couldn't read file '%s'. retry %u", + fileName, nAttempts); + sleep(1); + } + } + if (ifP) { + if (!realQuiet) + pm_message("Appending file %u '%s'", fileSeq, fileName); + AppendFile(ofP, ifP); + } else + pm_error("Unable to read file '%s' after %u attempts.", + fileName, READ_ATTEMPTS); + strfree(fileName); + strfree(inputFileName); + } +} + + + +static void +appendDefaultGopFiles(const char * const outputFileName, + FILE * const ofP) { + + unsigned int fileSeq; + bool endOfFiles; + + for (fileSeq = 0, endOfFiles = FALSE; !endOfFiles; ++fileSeq) { + const char * fileName; + FILE * ifP; + + asprintfN(&fileName, "%s.gop.%u", outputFileName, fileSeq); + + ifP = fopen(fileName, "rb"); + if (ifP == NULL) + endOfFiles = TRUE; + else { + if (!realQuiet) + pm_message("appending file: %s", fileName); + + AppendFile(ofP, ifP); + } + strfree(fileName); + } +} + + + +void +GOPsToMPEG(struct inputSource * const inputSourceP, + const char * const outputFileName, + FILE * const ofP) { +/*---------------------------------------------------------------------------- + Combine individual GOPs (one per file) into a single MPEG sequence file. +-----------------------------------------------------------------------------*/ + BitBucket * bb; + + { + /* Why is this reset called? */ + int x=Fsize_x, y=Fsize_y; + Fsize_Reset(); + Fsize_Note(0, yuvWidth, yuvHeight); + if (Fsize_x == 0 || Fsize_y == 0) + Fsize_Note(0, x, y); + } + + bb = Bitio_New(ofP); + + Mhead_GenSequenceHeader(bb, Fsize_x, Fsize_y, /* pratio */ aspectRatio, + /* pict_rate */ frameRate, /* bit_rate */ -1, + /* buf_size */ -1, /*c_param_flag */ 1, + /* iq_matrix */ customQtable, + /* niq_matrix */ customNIQtable, + /* ext_data */ NULL, /* ext_data_size */ 0, + /* user_data */ NULL, /* user_data_size */ 0); + + /* it's byte-padded, so we can dump it now */ + Bitio_Flush(bb); + + if (inputSourceP->stdinUsed) + AppendFile(ofP, stdin); + else { + if (inputSourceP->numInputFiles > 0) + appendSpecifiedGopFiles(inputSourceP, currentGOPPath, ofP); + else + appendDefaultGopFiles(outputFileName, ofP); + } + bb = Bitio_New(ofP); + + /* SEQUENCE END CODE */ + Mhead_GenSequenceEnder(bb); + + Bitio_Flush(bb); + + fclose(ofP); +} + + + +static void +makeGOPHeader(FILE * const outputFileP, + unsigned int const totalFramesSent, + unsigned int const frameNumber, + unsigned int const seqWithinGop) { + + boolean closed = (totalFramesSent == frameNumber); + + BitBucket * bbP; + + if (!realQuiet) + fprintf(stdout, "Creating new GOP (closed = %d) after %d frames\n", + closed, seqWithinGop); + + /* new GOP */ + bbP = Bitio_New(outputFileP); + Mhead_GenGOPHeader(bbP, /* drop_frame_flag */ 0, + tc_hrs, tc_min, tc_sec, tc_pict, + closed, /* broken_link */ 0, + /* ext_data */ NULL, /* ext_data_size */ 0, + /* user_data */ NULL, + /* user_data_size */ 0); + Bitio_Flush(bbP); + SetGOPStartTime(frameNumber); +} + + + +void +FramesToMPEG(FILE * const outputFile, + void * const inputHandle, + fileAcquisitionFn acquireInputFile, + fileDispositionFn disposeInputFile) { +/*---------------------------------------------------------------------------- + Combine a bunch of frames, one per file, into a single MPEG + sequence file. + + acquireInputFile() opens a file that contains an encoded frame, + identified by frame number. It returns NULL when the frame number + is beyond the frames available. +-----------------------------------------------------------------------------*/ + unsigned int frameNumber; + BitBucket *bb; + FILE *inputFile; + int pastRefNum = -1; + int futureRefNum = -1; + boolean inputLeft; + unsigned int seqWithinGop; + /* The sequence of the current frame within its GOP. 0 is the + first frame of a GOP, etc. + */ + + tc_hrs = 0; tc_min = 0; tc_sec = 0; tc_pict = 0; tc_extra = 0; + + { + /* Why is this reset called? */ + int x=Fsize_x, y=Fsize_y; + Fsize_Reset(); + Fsize_Note(0, yuvWidth, yuvHeight); + if (Fsize_x == 0 || Fsize_y == 0) + Fsize_Note(0, x, y); + } + SetBlocksPerSlice(); + + bb = Bitio_New(outputFile); + Mhead_GenSequenceHeader(bb, Fsize_x, Fsize_y, /* pratio */ aspectRatio, + /* pict_rate */ frameRate, /* bit_rate */ -1, + /* buf_size */ -1, /*c_param_flag */ 1, + /* iq_matrix */ qtable, /* niq_matrix */ niqtable, + /* ext_data */ NULL, /* ext_data_size */ 0, + /* user_data */ NULL, /* user_data_size */ 0); + /* it's byte-padded, so we can dump it now */ + Bitio_Flush(bb); + + /* need to do these in the right order!!! */ + /* also need to add GOP headers */ + + seqWithinGop = 0; + totalFramesSent = 0; + + inputLeft = TRUE; + frameNumber = 0; + + makeGOPHeader(outputFile, totalFramesSent, frameNumber, seqWithinGop); + + while (inputLeft) { + if (FType_Type(frameNumber) != 'b') { + pastRefNum = futureRefNum; + futureRefNum = frameNumber; + + if ((FType_Type(frameNumber) == 'i') && seqWithinGop >= gopSize) { + makeGOPHeader(outputFile, + totalFramesSent, frameNumber, seqWithinGop); + seqWithinGop -= gopSize; + } + + acquireInputFile(inputHandle, frameNumber, &inputFile); + if (inputFile == NULL) + inputLeft = FALSE; + else { + AppendFile(outputFile, inputFile); + + disposeInputFile(inputHandle, frameNumber); + + ++seqWithinGop; + IncrementTCTime(); + + /* now, output the B-frames */ + if (pastRefNum != -1) { + unsigned int bNum; + + for (bNum = pastRefNum+1; bNum < futureRefNum; ++bNum) { + acquireInputFile(inputHandle, bNum, &inputFile); + + if (inputFile) { + AppendFile(outputFile, inputFile); + + disposeInputFile(inputHandle, bNum); + + ++seqWithinGop; + IncrementTCTime(); + } + } + } + } + } + ++frameNumber; + } + + if (!realQuiet) + fprintf(stdout, "Wrote %d frames\n", totalFramesSent); + + bb = Bitio_New(outputFile); + + /* SEQUENCE END CODE */ + Mhead_GenSequenceEnder(bb); + + Bitio_Flush(bb); + + fclose(outputFile); +} + + + diff --git a/converter/ppm/ppmtompeg/docs/EXTENSIONS b/converter/ppm/ppmtompeg/docs/EXTENSIONS new file mode 100644 index 00000000..c683fbf8 --- /dev/null +++ b/converter/ppm/ppmtompeg/docs/EXTENSIONS @@ -0,0 +1,41 @@ +EXTENSIONS +---------- + +This is a list of things that we'd like to incorporate into the encoder. +If you succeed in implementing any of them, please let us know! + +* better B-frame search technique +* use DCT-space when computing error terms +* vary the q-scale according to the error term +* other motion vector search techniques +* modify the program to have a finer-grained parallelism option -- we + can probably encode slices in parallel (this will only be useful if we + want to do a few B-frames using exhaustive search) +* include system layer +* VBV delay with rate control + + +CREATING YOUR OWN MOTION SEARCH ROUTINES +---------------------------------------- + +Adding your own special motion search routine is very easy. We'll explain +adding a P-frame search routine; adding a B-frame routine is similar. + +First, edit the procedures PMotionSearch and SetPSearchAlg (both in the +file psearch.c) to recognize your new search routine. You probably want +to define a constant + PSEARCH_<your search name> in headers/search.h + +Have PMotionSearch call your search procedure just as it calls the other +standard search procedures. Make sure your procedure follows the guidelines +in the comments for PMotionSearch. + +Note: The encoder uses MAD as its search criterion. The reason for this: + "Among the various criteria that can be used as a measure of the + match between the two blocks, the mean absolute difference (MAD) + is favored because it requires no multiplication and gives + similar performance as the mean squared error (MSE)." + - Liu and Zaccarin, + "New Fast Algorithms for the Estimation of Block Motion Vectors," + IEEE Transactions on Circuits and Systems for Video Technology + Volume 3 No. 2 (April 1993) diff --git a/converter/ppm/ppmtompeg/docs/INPUT.FORMAT b/converter/ppm/ppmtompeg/docs/INPUT.FORMAT new file mode 100644 index 00000000..8a663d3a --- /dev/null +++ b/converter/ppm/ppmtompeg/docs/INPUT.FORMAT @@ -0,0 +1,79 @@ + ---------------- + | FILE FORMATS | + ---------------- + +PPM FORMAT +---------- + +See http://netpbm.sourceforge.net/doc/ppm.html . + + +UCB YUV FORMAT +-------------- + +You should be aware that the YUV format used in the MPEG encoder is DIFFERENT +than the Abekas YUV format. The reason for this is that in MPEG, the U and +V components are subsampled 4:1. + +To give you an idea of what format the YUV file must be in, the following +code will read in a YUV file: + + unsigned char **y_data, **cr_data, **cb_data; + + void ReadYUV(char *fileName, int width, int height) + { + FILE *fpointer; + register int y; + + /* should allocate memory for y_data, cr_data, cb_data here */ + + fpointer = fopen(fileName, "r"); + + for (y = 0; y < height; y++) /* Y */ + fread(y_data[y], 1, width, fpointer); + + for (y = 0; y < height / 2; y++) /* U */ + fread(cb_data[y], 1, width / 2, fpointer); + + for (y = 0; y < height / 2; y++) /* V */ + fread(cr_data[y], 1, width / 2, fpointer); + + fclose(fpointer); + } + +There are two reasons why you'd want to use YUV files rather than PPM files: + 1) The YUV files are 50% the size of the corresponding PPM files + 2) The ENCODER will run slightly faster, since it doesn't have to + do the RGB to YUV conversion itself. + + +ABEKAS YUV FORMAT +----------------- + +The Abekas YUV Format interlaces the Y, U, and V values in a 4:2:2 format. +The interlacing pattern is + +UYVY + +for each group of 4 bytes in the file. + + +PHILLIPS YUV FORMAT +------------------- + +The Phillips YUV Format interlaces the Y, U, and V values in a 4:2:2 format. +The interlacing pattern is + +YVYU + +for each group of 4 bytes in the file. + + +You may specify either ABEKAS, PHILLIPS, or UCB as the YUV_FORMAT when +encoding ; the encoder defaults to UCB YUV_FORMAT if not specified. +In addition, if you've got a weird interlacing format, you can also +try and de-interlace it by giving the YUV pattern in the YUV_FORMAT. +So a YUV 4:4:4 format would be + +YUVYUV + diff --git a/converter/ppm/ppmtompeg/docs/parallel.param b/converter/ppm/ppmtompeg/docs/parallel.param new file mode 100644 index 00000000..0702d32c --- /dev/null +++ b/converter/ppm/ppmtompeg/docs/parallel.param @@ -0,0 +1,142 @@ +# parameter file template with parallel execution +# +# you can use this as a template, copying it to a separate file then modifying +# the copy +# +# +# any line beginning with '#' is a comment +# +# no line should be longer than 255 characters +# +# +# general format of each line is: +# <option> <spaces and/or tabs> <value> +# +# lines can generally be in any order +# +# only exception is the option 'INPUT' which must be followed by input +# files in the order in which they must appear, followed by 'END_INPUT' +# +# <option> MUST be in UPPER CASE +# + +PATTERN IBBPBBI +OUTPUT /n/picasso/users/keving/encode/output.mpg + +# mpeg_encode really only accepts 3 different file formats, but using a +# conversion statement it can effectively handle ANY file format +# +# you must specify whether you will convert to PNM or PPM or YUV format +# (must be upper case) +# +BASE_FILE_FORMAT YUV + +# +# if YUV format (or using parallel version), must provide width and height +# YUV_SIZE widthxheight +# this option is ignored if BASE_FILE_FORMAT is PPM or PNM and you're running +# on just one machine +# +YUV_SIZE 352x240 + +# the conversion statement +# +# Each occurrence of '*' will be replaced by the input file +# +# e.g., if you have a bunch of GIF files, then this might be: +# INPUT_CONVERT giftoppm * +# +# e.g., if you have a bunch of files like a.Y a.U a.V, etc., then: +# INPUT_CONVERT cat *.Y *.U *.V +# +# e.g., if you are grabbing from laser disc you might have something like +# INPUT_CONVERT goto frame *; grabppm +# 'INPUT_CONVERT *' means the files are already in the base file format +# +INPUT_CONVERT * + +# number of frames in a GOP. +# +# since each GOP must have at least one I-frame, the encoder will find the +# the first I-frame after GOP_SIZE frames to start the next GOP +# +# later, will add more flexible GOP signalling +# +GOP_SIZE 6 + +# number of slices in a frame +# +# 1 is a good number. another possibility is the number of macroblock rows +# (which is the height divided by 16) +# +SLICES_PER_FRAME 1 + +# directory to get all input files from (makes this file easier to read) +# can't use stdin with parallel encoding +INPUT_DIR /n/picasso/users/keving/encode/input/tennis + +INPUT +# '*' is replaced by the numbers 01, 02, 03, 04 +# if I instead do [01-11], it would be 01, 02, ..., 09, 10, 11 +# if I instead do [1-11], it would be 1, 2, 3, ..., 9, 10, 11 +# if I instead do [1-11+3], it would be 1, 4, 7, 10 +# the program assumes none of your input files has a name ending in ']' +# if you do, too bad!!! +# +# +stennis.*.yuv [0-7] +# can have more files here if you want...there is no limit on the number +# of files +END_INPUT + + + +# all of the remaining options have to do with the motion search and qscale + +# FULL or HALF -- must be upper case +PIXEL HALF + +# means +/- this many pixels +RANGE 10 + +# this must be one of {EXHAUSTIVE, SUBSAMPLE, LOGARITHMIC} +PSEARCH_ALG LOGARITHMIC + +# this must be one of {SIMPLE, CROSS2, EXHAUSTIVE} +# +# note that EXHAUSTIVE is really, really, really slow +# +BSEARCH_ALG CROSS2 + +# +# these specify the q-scale for I, P, and B frames +# (values must be between 1 and 31) +# +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# this must be ORIGINAL or DECODED +REFERENCE_FRAME ORIGINAL + + +# the following two are optional (default = 10, 60) + +# number of frames to do initially to gauge speed of machine +PARALLEL_TEST_FRAMES 3 + +# number of seconds per chunk thereafter +PARALLEL_TIME_CHUNKS 30 + + +PARALLEL +# lines must be of form "machine <whitespace> username <whitespace> executable" +charlie-brown keving ~keving/encode/bin/dec-alpha/mpeg_encode +#woodstock keving ~keving/encode/bin/dec-alpha/mpeg_encode +#REMOTE charlie-brown keving ~keving/encode/bin/dec-alpha/mpeg_encode ~keving/encode/examples/parallel.param +# remote machine: "REMOTE machine username executable param_file" +# mickey keving ~keving/encode/bin/dec-5000/mpeg_encode +#REMOTE mickey keving ~keving/encode/bin/dec-5000/mpeg_encode ~keving/encode/examples/parallel.param +#REMOTE mickey keving ~keving/encode/bin/dec-5000/mpeg_encode ~keving/encode/examples/parallel.param +REMOTE woodstock keving ~keving/encode/bin/dec-alpha/mpeg_encode ~keving/encode/examples/parallel.param +END_PARALLEL diff --git a/converter/ppm/ppmtompeg/docs/param-summary b/converter/ppm/ppmtompeg/docs/param-summary new file mode 100644 index 00000000..d8322bfb --- /dev/null +++ b/converter/ppm/ppmtompeg/docs/param-summary @@ -0,0 +1,14 @@ +Different MPEG coding strategies +-------------------------------- + +Strategy Speed Compression Quality +-------- ----- ----------- ------- +Increase MV Search Range Down Up Up? +Increasing Q-Scale Same Up Down +Decreasing Q-Scale Same Down Up +Use half-pixel search Down Up Up? +Use pixel subsampling Up Down Down? +Use motion vector subsampling Up Down Down? +Use decoded frame as reference Same Unknown Up? +Varying q-scale Down Up Down + diff --git a/converter/ppm/ppmtompeg/docs/template.param b/converter/ppm/ppmtompeg/docs/template.param new file mode 100644 index 00000000..66b6dd98 --- /dev/null +++ b/converter/ppm/ppmtompeg/docs/template.param @@ -0,0 +1,154 @@ +# parameter file template with lots of comments to assist you +# +# you can use this as a template, copying it to a separate file then modifying +# the copy +# +# +# any line beginning with '#' is a comment +# +# no line should be longer than 255 characters +# +# +# general format of each line is: +# <option> <spaces and/or tabs> <value> +# +# lines can generally be in any order +# +# an exception is the option 'INPUT' which must be followed by input +# files in the order in which they must appear, followed by 'END_INPUT' +# +# Also, if you use the `command` method of generating input file names, +# the command will only be executed in the INPUT_DIR if INPUT_DIR preceeds +# the INPUT parameter. +# +# <option> MUST be in UPPER CASE +# + +PATTERN IBBPBBPBBPBBPBBP +OUTPUT output.mpg + +# mpeg_encode really only accepts 3 different file formats, but using a +# conversion statement it can effectively handle ANY file format +# +# You must specify the type of the input files. The choices are: +# YUV, PPM, JMOVIE, Y, JPEG, PNM +# (must be upper case) +# +BASE_FILE_FORMAT YUV + +# +# if YUV format (or using parallel version), must provide width and height +# YUV_SIZE widthxheight +# this option is ignored if BASE_FILE_FORMAT is not YUV and you're running +# on just one machine +# +YUV_SIZE 352x240 + +# If you are using YUV, there are different supported file formats. +# EYUV or UCB are the same as previous versions of this encoder. +# (All the Y's, then U's then V's, in 4:2:0 subsampling.) +# Other formats, such as Abekas, Phillips, or a general format are +# permissible, the general format is a string of Y's, U's, and V's +# to specify the file order. + +INPUT_FORMAT UCB + +# the conversion statement +# +# Each occurrence of '*' will be replaced by the input file +# +# e.g., if you have a bunch of GIF files, then this might be: +# INPUT_CONVERT giftoppm * +# +# e.g., if you have a bunch of files like a.Y a.U a.V, etc., then: +# INPUT_CONVERT cat *.Y *.U *.V +# +# e.g., if you are grabbing from laser disc you might have something like +# INPUT_CONVERT goto frame *; grabppm +# 'INPUT_CONVERT *' means the files are already in the base file format +# +INPUT_CONVERT * + +# number of frames in a GOP. +# +# since each GOP must have at least one I-frame, the encoder will find the +# the first I-frame after GOP_SIZE frames to start the next GOP +# +# later, will add more flexible GOP signalling +# +GOP_SIZE 16 + +# number of slices in a frame +# +# 1 is a good number. another possibility is the number of macroblock rows +# (which is the height divided by 16) +# +SLICES_PER_FRAME 1 + +# directory to get all input files from (makes this file easier to read) +INPUT_DIR ../input/tennis + +# There are a bunch of ways to specify the input files. +# from a simple one-per-line listing, to the following +# way of numbering them. See the manual for more information. +INPUT +# '*' is replaced by the numbers 01, 02, 03, 04 +# if I instead do [01-11], it would be 01, 02, ..., 09, 10, 11 +# if I instead do [1-11], it would be 1, 2, 3, ..., 9, 10, 11 +# if I instead do [1-11+3], it would be 1, 4, 7, 10 +# the program assumes none of your input files has a name ending in ']' +# if you do, too bad!!! +# +# +stennis.*.yuv [0-23] +# can have more files here if you want...there is no limit on the number +# of files +END_INPUT + + + +# Many of the remaining options have to do with the motion search and qscale + +# FULL or HALF -- must be upper case +PIXEL HALF + +# means +/- this many pixels for both P and B frame searches +# specify two numbers if you wish to serc different ranges in the two. +RANGE 10 + +# this must be one of {EXHAUSTIVE, SUBSAMPLE, LOGARITHMIC} +PSEARCH_ALG LOGARITHMIC + +# this must be one of {SIMPLE, CROSS2, EXHAUSTIVE} +# +# note that EXHAUSTIVE is really, really, really slow +# +BSEARCH_ALG CROSS2 + +# +# these specify the q-scale for I, P, and B frames +# (values must be between 1 and 31) +# These are the Qscale values for the entire frame in variable bit-rate +# mode, and starting points (but not important) for constant bit rate +# +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# this must be ORIGINAL or DECODED +REFERENCE_FRAME ORIGINAL + +# for parallel parameters see parallel.param in the exmaples subdirectory + +# if you want constant bit-rate mode, specify it as follows (number is bits/sec): +BIT_RATE 1000000 + +# To specify the buffer size (327680 is default, measused in bits, for 16bit words) +BUFFER_SIZE 327680 + +# The frame rate is the number of frames/second (legal values: +# 23.976, 24, 25, 29.97, 30, 50 ,59.94, 60 +FRAME_RATE 30 + +# There are many more options, see the users manual for examples.... +# ASPECT_RATIO, USER_DATA, GAMMA, IQTABLE, etc. diff --git a/converter/ppm/ppmtompeg/docs/users-guide.ps b/converter/ppm/ppmtompeg/docs/users-guide.ps new file mode 100644 index 00000000..2e39be0a --- /dev/null +++ b/converter/ppm/ppmtompeg/docs/users-guide.ps @@ -0,0 +1,3233 @@ +%! +%%BoundingBox: (atend) +%%Pages: (atend) +%%DocumentFonts: (atend) +%%EndComments +% +% FrameMaker PostScript Prolog 3.0, for use with FrameMaker 3.0 +% Copyright (c) 1986,87,89,90,91 by Frame Technology Corporation. +% All rights reserved. +% +% Known Problems: +% Due to bugs in Transcript, the 'PS-Adobe-' is omitted from line 1 +/FMversion (3.0) def +% Set up Color vs. Black-and-White + /FMPrintInColor systemdict /colorimage known + systemdict /currentcolortransfer known or def +% Uncomment this line to force b&w on color printer +% /FMPrintInColor false def +/FrameDict 195 dict def +systemdict /errordict known not {/errordict 10 dict def + errordict /rangecheck {stop} put} if +% The readline in 23.0 doesn't recognize cr's as nl's on AppleTalk +FrameDict /tmprangecheck errordict /rangecheck get put +errordict /rangecheck {FrameDict /bug true put} put +FrameDict /bug false put +mark +% Some PS machines read past the CR, so keep the following 3 lines together! +currentfile 5 string readline +00 +0000000000 +cleartomark +errordict /rangecheck FrameDict /tmprangecheck get put +FrameDict /bug get { + /readline { + /gstring exch def + /gfile exch def + /gindex 0 def + { + gfile read pop + dup 10 eq {exit} if + dup 13 eq {exit} if + gstring exch gindex exch put + /gindex gindex 1 add def + } loop + pop + gstring 0 gindex getinterval true + } def + } if +/FMVERSION { + FMversion ne { + /Times-Roman findfont 18 scalefont setfont + 100 100 moveto + (FrameMaker version does not match postscript_prolog!) + dup = + show showpage + } if + } def +/FMLOCAL { + FrameDict begin + 0 def + end + } def + /gstring FMLOCAL + /gfile FMLOCAL + /gindex FMLOCAL + /orgxfer FMLOCAL + /orgproc FMLOCAL + /organgle FMLOCAL + /orgfreq FMLOCAL + /yscale FMLOCAL + /xscale FMLOCAL + /manualfeed FMLOCAL + /paperheight FMLOCAL + /paperwidth FMLOCAL +/FMDOCUMENT { + array /FMfonts exch def + /#copies exch def + FrameDict begin + 0 ne dup {setmanualfeed} if + /manualfeed exch def + /paperheight exch def + /paperwidth exch def + /yscale exch def + /xscale exch def + currenttransfer cvlit /orgxfer exch def + currentscreen cvlit /orgproc exch def + /organgle exch def /orgfreq exch def + setpapername + manualfeed {true} {papersize} ifelse + {manualpapersize} {false} ifelse + {desperatepapersize} if + end + } def + /pagesave FMLOCAL + /orgmatrix FMLOCAL + /landscape FMLOCAL +/FMBEGINPAGE { + FrameDict begin + /pagesave save def + 3.86 setmiterlimit + /landscape exch 0 ne def + landscape { + 90 rotate 0 exch neg translate pop + } + {pop pop} + ifelse + xscale yscale scale + /orgmatrix matrix def + gsave + } def +/FMENDPAGE { + grestore + pagesave restore + end + showpage + } def +/FMFONTDEFINE { + FrameDict begin + findfont + ReEncode + 1 index exch + definefont + FMfonts 3 1 roll + put + end + } def +/FMFILLS { + FrameDict begin + array /fillvals exch def + end + } def +/FMFILL { + FrameDict begin + fillvals 3 1 roll put + end + } def +/FMNORMALIZEGRAPHICS { + newpath + 0.0 0.0 moveto + 1 setlinewidth + 0 setlinecap + 0 0 0 sethsbcolor + 0 setgray + } bind def + /fx FMLOCAL + /fy FMLOCAL + /fh FMLOCAL + /fw FMLOCAL + /llx FMLOCAL + /lly FMLOCAL + /urx FMLOCAL + /ury FMLOCAL +/FMBEGINEPSF { + end + /FMEPSF save def + /showpage {} def + FMNORMALIZEGRAPHICS + [/fy /fx /fh /fw /ury /urx /lly /llx] {exch def} forall + fx fy translate + rotate + fw urx llx sub div fh ury lly sub div scale + llx neg lly neg translate + } bind def +/FMENDEPSF { + FMEPSF restore + FrameDict begin + } bind def +FrameDict begin +/setmanualfeed { +%%BeginFeature *ManualFeed True + statusdict /manualfeed true put +%%EndFeature + } def +/max {2 copy lt {exch} if pop} bind def +/min {2 copy gt {exch} if pop} bind def +/inch {72 mul} def +/pagedimen { + paperheight sub abs 16 lt exch + paperwidth sub abs 16 lt and + {/papername exch def} {pop} ifelse + } def + /papersizedict FMLOCAL +/setpapername { + /papersizedict 14 dict def + papersizedict begin + /papername /unknown def + /Letter 8.5 inch 11.0 inch pagedimen + /LetterSmall 7.68 inch 10.16 inch pagedimen + /Tabloid 11.0 inch 17.0 inch pagedimen + /Ledger 17.0 inch 11.0 inch pagedimen + /Legal 8.5 inch 14.0 inch pagedimen + /Statement 5.5 inch 8.5 inch pagedimen + /Executive 7.5 inch 10.0 inch pagedimen + /A3 11.69 inch 16.5 inch pagedimen + /A4 8.26 inch 11.69 inch pagedimen + /A4Small 7.47 inch 10.85 inch pagedimen + /B4 10.125 inch 14.33 inch pagedimen + /B5 7.16 inch 10.125 inch pagedimen + end + } def +/papersize { + papersizedict begin + /Letter {lettertray letter} def + /LetterSmall {lettertray lettersmall} def + /Tabloid {11x17tray 11x17} def + /Ledger {ledgertray ledger} def + /Legal {legaltray legal} def + /Statement {statementtray statement} def + /Executive {executivetray executive} def + /A3 {a3tray a3} def + /A4 {a4tray a4} def + /A4Small {a4tray a4small} def + /B4 {b4tray b4} def + /B5 {b5tray b5} def + /unknown {unknown} def + papersizedict dup papername known {papername} {/unknown} ifelse get + end + /FMdicttop countdictstack 1 add def + statusdict begin stopped end + countdictstack -1 FMdicttop {pop end} for + } def +/manualpapersize { + papersizedict begin + /Letter {letter} def + /LetterSmall {lettersmall} def + /Tabloid {11x17} def + /Ledger {ledger} def + /Legal {legal} def + /Statement {statement} def + /Executive {executive} def + /A3 {a3} def + /A4 {a4} def + /A4Small {a4small} def + /B4 {b4} def + /B5 {b5} def + /unknown {unknown} def + papersizedict dup papername known {papername} {/unknown} ifelse get + end + stopped + } def +/desperatepapersize { + statusdict /setpageparams known + { + paperwidth paperheight 0 1 + statusdict begin + {setpageparams} stopped pop + end + } if + } def +/savematrix { + orgmatrix currentmatrix pop + } bind def +/restorematrix { + orgmatrix setmatrix + } bind def +/dmatrix matrix def +/dpi 72 0 dmatrix defaultmatrix dtransform + dup mul exch dup mul add sqrt def +/freq dpi 18.75 div 8 div round dup 0 eq {pop 1} if 8 mul dpi exch div def +/sangle 1 0 dmatrix defaultmatrix dtransform exch atan def +/DiacriticEncoding [ +/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef +/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef +/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef +/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef +/.notdef /.notdef /.notdef /.notdef /space /exclam /quotedbl +/numbersign /dollar /percent /ampersand /quotesingle /parenleft +/parenright /asterisk /plus /comma /hyphen /period /slash /zero /one +/two /three /four /five /six /seven /eight /nine /colon /semicolon +/less /equal /greater /question /at /A /B /C /D /E /F /G /H /I /J /K +/L /M /N /O /P /Q /R /S /T /U /V /W /X /Y /Z /bracketleft /backslash +/bracketright /asciicircum /underscore /grave /a /b /c /d /e /f /g /h +/i /j /k /l /m /n /o /p /q /r /s /t /u /v /w /x /y /z /braceleft /bar +/braceright /asciitilde /.notdef /Adieresis /Aring /Ccedilla /Eacute +/Ntilde /Odieresis /Udieresis /aacute /agrave /acircumflex /adieresis +/atilde /aring /ccedilla /eacute /egrave /ecircumflex /edieresis +/iacute /igrave /icircumflex /idieresis /ntilde /oacute /ograve +/ocircumflex /odieresis /otilde /uacute /ugrave /ucircumflex +/udieresis /dagger /.notdef /cent /sterling /section /bullet +/paragraph /germandbls /registered /copyright /trademark /acute +/dieresis /.notdef /AE /Oslash /.notdef /.notdef /.notdef /.notdef +/yen /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef +/ordfeminine /ordmasculine /.notdef /ae /oslash /questiondown +/exclamdown /logicalnot /.notdef /florin /.notdef /.notdef +/guillemotleft /guillemotright /ellipsis /.notdef /Agrave /Atilde +/Otilde /OE /oe /endash /emdash /quotedblleft /quotedblright +/quoteleft /quoteright /.notdef /.notdef /ydieresis /Ydieresis +/fraction /currency /guilsinglleft /guilsinglright /fi /fl /daggerdbl +/periodcentered /quotesinglbase /quotedblbase /perthousand +/Acircumflex /Ecircumflex /Aacute /Edieresis /Egrave /Iacute +/Icircumflex /Idieresis /Igrave /Oacute /Ocircumflex /.notdef /Ograve +/Uacute /Ucircumflex /Ugrave /dotlessi /circumflex /tilde /macron +/breve /dotaccent /ring /cedilla /hungarumlaut /ogonek /caron +] def +/ReEncode { + dup + length + dict begin + { + 1 index /FID ne + {def} + {pop pop} ifelse + } forall + 0 eq {/Encoding DiacriticEncoding def} if + currentdict + end + } bind def +/graymode true def + /bwidth FMLOCAL + /bpside FMLOCAL + /bstring FMLOCAL + /onbits FMLOCAL + /offbits FMLOCAL + /xindex FMLOCAL + /yindex FMLOCAL + /x FMLOCAL + /y FMLOCAL +/setpattern { + /bwidth exch def + /bpside exch def + /bstring exch def + /onbits 0 def /offbits 0 def + freq sangle landscape {90 add} if + {/y exch def + /x exch def + /xindex x 1 add 2 div bpside mul cvi def + /yindex y 1 add 2 div bpside mul cvi def + bstring yindex bwidth mul xindex 8 idiv add get + 1 7 xindex 8 mod sub bitshift and 0 ne + {/onbits onbits 1 add def 1} + {/offbits offbits 1 add def 0} + ifelse + } + setscreen + {} settransfer + offbits offbits onbits add div FMsetgray + /graymode false def + } bind def +/grayness { + FMsetgray + graymode not { + /graymode true def + orgxfer cvx settransfer + orgfreq organgle orgproc cvx setscreen + } if + } bind def + /HUE FMLOCAL + /SAT FMLOCAL + /BRIGHT FMLOCAL + /Colors FMLOCAL +FMPrintInColor + + { + /HUE 0 def + /SAT 0 def + /BRIGHT 0 def + % array of arrays Hue and Sat values for the separations [HUE BRIGHT] + /Colors + [[0 0 ] % black + [0 0 ] % white + [0.00 1.0] % red + [0.37 1.0] % green + [0.60 1.0] % blue + [0.50 1.0] % cyan + [0.83 1.0] % magenta + [0.16 1.0] % comment / yellow + ] def + + /BEGINBITMAPCOLOR { + BITMAPCOLOR} def + /BEGINBITMAPCOLORc { + BITMAPCOLORc} def + /BEGINBITMAPTRUECOLOR { + BITMAPTRUECOLOR } def + /BEGINBITMAPTRUECOLORc { + BITMAPTRUECOLORc } def + /K { + Colors exch get dup + 0 get /HUE exch store + 1 get /BRIGHT exch store + HUE 0 eq BRIGHT 0 eq and + {1.0 SAT sub setgray} + {HUE SAT BRIGHT sethsbcolor} + ifelse + } def + /FMsetgray { + /SAT exch 1.0 exch sub store + HUE 0 eq BRIGHT 0 eq and + {1.0 SAT sub setgray} + {HUE SAT BRIGHT sethsbcolor} + ifelse + } bind def + } + + { + /BEGINBITMAPCOLOR { + BITMAPGRAY} def + /BEGINBITMAPCOLORc { + BITMAPGRAYc} def + /BEGINBITMAPTRUECOLOR { + BITMAPTRUEGRAY } def + /BEGINBITMAPTRUECOLORc { + BITMAPTRUEGRAYc } def + /FMsetgray {setgray} bind def + /K { + pop + } def + } +ifelse +/normalize { + transform round exch round exch itransform + } bind def +/dnormalize { + dtransform round exch round exch idtransform + } bind def +/lnormalize { + 0 dtransform exch cvi 2 idiv 2 mul 1 add exch idtransform pop + } bind def +/H { + lnormalize setlinewidth + } bind def +/Z { + setlinecap + } bind def + /fillvals FMLOCAL +/X { + fillvals exch get + dup type /stringtype eq + {8 1 setpattern} + {grayness} + ifelse + } bind def +/V { + gsave eofill grestore + } bind def +/N { + stroke + } bind def +/M {newpath moveto} bind def +/E {lineto} bind def +/D {curveto} bind def +/O {closepath} bind def + /n FMLOCAL +/L { + /n exch def + newpath + normalize + moveto + 2 1 n {pop normalize lineto} for + } bind def +/Y { + L + closepath + } bind def + /x1 FMLOCAL + /x2 FMLOCAL + /y1 FMLOCAL + /y2 FMLOCAL + /rad FMLOCAL +/R { + /y2 exch def + /x2 exch def + /y1 exch def + /x1 exch def + x1 y1 + x2 y1 + x2 y2 + x1 y2 + 4 Y + } bind def +/RR { + /rad exch def + normalize + /y2 exch def + /x2 exch def + normalize + /y1 exch def + /x1 exch def + newpath + x1 y1 rad add moveto + x1 y2 x2 y2 rad arcto + x2 y2 x2 y1 rad arcto + x2 y1 x1 y1 rad arcto + x1 y1 x1 y2 rad arcto + closepath + 16 {pop} repeat + } bind def +/C { + grestore + gsave + R + clip + } bind def + /FMpointsize FMLOCAL +/F { + FMfonts exch get + FMpointsize scalefont + setfont + } bind def +/Q { + /FMpointsize exch def + F + } bind def +/T { + moveto show + } bind def +/RF { + rotate + 0 ne {-1 1 scale} if + } bind def +/TF { + gsave + moveto + RF + show + grestore + } bind def +/P { + moveto + 0 32 3 2 roll widthshow + } bind def +/PF { + gsave + moveto + RF + 0 32 3 2 roll widthshow + grestore + } bind def +/S { + moveto + 0 exch ashow + } bind def +/SF { + gsave + moveto + RF + 0 exch ashow + grestore + } bind def +/B { + moveto + 0 32 4 2 roll 0 exch awidthshow + } bind def +/BF { + gsave + moveto + RF + 0 32 4 2 roll 0 exch awidthshow + grestore + } bind def +/G { + gsave + newpath + normalize translate 0.0 0.0 moveto + dnormalize scale + 0.0 0.0 1.0 5 3 roll arc + closepath fill + grestore + } bind def +/A { + gsave + savematrix + newpath + 2 index 2 div add exch 3 index 2 div sub exch + normalize 2 index 2 div sub exch 3 index 2 div add exch + translate + scale + 0.0 0.0 1.0 5 3 roll arc + restorematrix + stroke + grestore + } bind def + /x FMLOCAL + /y FMLOCAL + /w FMLOCAL + /h FMLOCAL + /xx FMLOCAL + /yy FMLOCAL + /ww FMLOCAL + /hh FMLOCAL + /FMsaveobject FMLOCAL + /FMoptop FMLOCAL + /FMdicttop FMLOCAL +/BEGINPRINTCODE { + /FMdicttop countdictstack 1 add def + /FMoptop count 4 sub def + /FMsaveobject save def + userdict begin + /showpage {} def + FMNORMALIZEGRAPHICS + 3 index neg 3 index neg translate + } bind def +/ENDPRINTCODE { + count -1 FMoptop {pop pop} for + countdictstack -1 FMdicttop {pop end} for + FMsaveobject restore + } bind def +/gn { + 0 + { 46 mul + cf read pop + 32 sub + dup 46 lt {exit} if + 46 sub add + } loop + add + } bind def + /str FMLOCAL +/cfs { + /str sl string def + 0 1 sl 1 sub {str exch val put} for + str def + } bind def +/ic [ + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0223 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0223 + 0 + {0 hx} {1 hx} {2 hx} {3 hx} {4 hx} {5 hx} {6 hx} {7 hx} {8 hx} {9 hx} + {10 hx} {11 hx} {12 hx} {13 hx} {14 hx} {15 hx} {16 hx} {17 hx} {18 hx} + {19 hx} {gn hx} {0} {1} {2} {3} {4} {5} {6} {7} {8} {9} {10} {11} {12} + {13} {14} {15} {16} {17} {18} {19} {gn} {0 wh} {1 wh} {2 wh} {3 wh} + {4 wh} {5 wh} {6 wh} {7 wh} {8 wh} {9 wh} {10 wh} {11 wh} {12 wh} + {13 wh} {14 wh} {gn wh} {0 bl} {1 bl} {2 bl} {3 bl} {4 bl} {5 bl} {6 bl} + {7 bl} {8 bl} {9 bl} {10 bl} {11 bl} {12 bl} {13 bl} {14 bl} {gn bl} + {0 fl} {1 fl} {2 fl} {3 fl} {4 fl} {5 fl} {6 fl} {7 fl} {8 fl} {9 fl} + {10 fl} {11 fl} {12 fl} {13 fl} {14 fl} {gn fl} + ] def + /sl FMLOCAL + /val FMLOCAL + /ws FMLOCAL + /im FMLOCAL + /bs FMLOCAL + /cs FMLOCAL + /len FMLOCAL + /pos FMLOCAL +/ms { + /sl exch def + /val 255 def + /ws cfs + /im cfs + /val 0 def + /bs cfs + /cs cfs + } bind def +400 ms +/ip { + is + 0 + cf cs readline pop + { ic exch get exec + add + } forall + pop + + } bind def +/wh { + /len exch def + /pos exch def + ws 0 len getinterval im pos len getinterval copy pop + pos len + } bind def +/bl { + /len exch def + /pos exch def + bs 0 len getinterval im pos len getinterval copy pop + pos len + } bind def +/s1 1 string def +/fl { + /len exch def + /pos exch def + /val cf s1 readhexstring pop 0 get def + pos 1 pos len add 1 sub {im exch val put} for + pos len + } bind def +/hx { + 3 copy getinterval + cf exch readhexstring pop pop + } bind def + /h FMLOCAL + /w FMLOCAL + /d FMLOCAL + /lb FMLOCAL + /bitmapsave FMLOCAL + /is FMLOCAL + /cf FMLOCAL +/wbytes { + dup + 8 eq {pop} {1 eq {7 add 8 idiv} {3 add 4 idiv} ifelse} ifelse + } bind def +/BEGINBITMAPBWc { + 1 {} COMMONBITMAPc + } bind def +/BEGINBITMAPGRAYc { + 8 {} COMMONBITMAPc + } bind def +/BEGINBITMAP2BITc { + 2 {} COMMONBITMAPc + } bind def +/COMMONBITMAPc { + /r exch def + /d exch def + gsave + translate rotate scale /h exch def /w exch def + /lb w d wbytes def + sl lb lt {lb ms} if + /bitmapsave save def + r + /is im 0 lb getinterval def + ws 0 lb getinterval is copy pop + /cf currentfile def + w h d [w 0 0 h neg 0 h] + {ip} image + bitmapsave restore + grestore + } bind def +/BEGINBITMAPBW { + 1 {} COMMONBITMAP + } bind def +/BEGINBITMAPGRAY { + 8 {} COMMONBITMAP + } bind def +/BEGINBITMAP2BIT { + 2 {} COMMONBITMAP + } bind def +/COMMONBITMAP { + /r exch def + /d exch def + gsave + translate rotate scale /h exch def /w exch def + /bitmapsave save def + r + /is w d wbytes string def + /cf currentfile def + w h d [w 0 0 h neg 0 h] + {cf is readhexstring pop} image + bitmapsave restore + grestore + } bind def + /proc1 FMLOCAL + /proc2 FMLOCAL + /newproc FMLOCAL +/Fmcc { + /proc2 exch cvlit def + /proc1 exch cvlit def + /newproc proc1 length proc2 length add array def + newproc 0 proc1 putinterval + newproc proc1 length proc2 putinterval + newproc cvx +} bind def +/ngrayt 256 array def +/nredt 256 array def +/nbluet 256 array def +/ngreent 256 array def + /gryt FMLOCAL + /blut FMLOCAL + /grnt FMLOCAL + /redt FMLOCAL + /indx FMLOCAL + /cynu FMLOCAL + /magu FMLOCAL + /yelu FMLOCAL + /k FMLOCAL + /u FMLOCAL +/colorsetup { + currentcolortransfer + /gryt exch def + /blut exch def + /grnt exch def + /redt exch def + 0 1 255 { + /indx exch def + /cynu 1 red indx get 255 div sub def + /magu 1 green indx get 255 div sub def + /yelu 1 blue indx get 255 div sub def + /k cynu magu min yelu min def + /u k currentundercolorremoval exec def + nredt indx 1 0 cynu u sub max sub redt exec put + ngreent indx 1 0 magu u sub max sub grnt exec put + nbluet indx 1 0 yelu u sub max sub blut exec put + ngrayt indx 1 k currentblackgeneration exec sub gryt exec put + } for + {255 mul cvi nredt exch get} + {255 mul cvi ngreent exch get} + {255 mul cvi nbluet exch get} + {255 mul cvi ngrayt exch get} + setcolortransfer + {pop 0} setundercolorremoval + {} setblackgeneration + } bind def + /tran FMLOCAL +/fakecolorsetup { + /tran 256 string def + 0 1 255 {/indx exch def + tran indx + red indx get 77 mul + green indx get 151 mul + blue indx get 28 mul + add add 256 idiv put} for + currenttransfer + {255 mul cvi tran exch get 255.0 div} + exch Fmcc settransfer +} bind def +/BITMAPCOLOR { + /d 8 def + gsave + translate rotate scale /h exch def /w exch def + /bitmapsave save def + colorsetup + /is w d wbytes string def + /cf currentfile def + w h d [w 0 0 h neg 0 h] + {cf is readhexstring pop} {is} {is} true 3 colorimage + bitmapsave restore + grestore + } bind def +/BITMAPCOLORc { + /d 8 def + gsave + translate rotate scale /h exch def /w exch def + /lb w d wbytes def + sl lb lt {lb ms} if + /bitmapsave save def + colorsetup + /is im 0 lb getinterval def + ws 0 lb getinterval is copy pop + /cf currentfile def + w h d [w 0 0 h neg 0 h] + {ip} {is} {is} true 3 colorimage + bitmapsave restore + grestore + } bind def +/BITMAPTRUECOLORc { + gsave + translate rotate scale /h exch def /w exch def + /bitmapsave save def + + /is w string def + + ws 0 w getinterval is copy pop + /cf currentfile def + w h 8 [w 0 0 h neg 0 h] + {ip} {gip} {bip} true 3 colorimage + bitmapsave restore + grestore + } bind def +/BITMAPTRUECOLOR { + gsave + translate rotate scale /h exch def /w exch def + /bitmapsave save def + /is w string def + /gis w string def + /bis w string def + /cf currentfile def + w h 8 [w 0 0 h neg 0 h] + { cf is readhexstring pop } + { cf gis readhexstring pop } + { cf bis readhexstring pop } + true 3 colorimage + bitmapsave restore + grestore + } bind def +/BITMAPTRUEGRAYc { + gsave + translate rotate scale /h exch def /w exch def + /bitmapsave save def + + /is w string def + + ws 0 w getinterval is copy pop + /cf currentfile def + w h 8 [w 0 0 h neg 0 h] + {ip gip bip w gray} image + bitmapsave restore + grestore + } bind def +/ww FMLOCAL +/r FMLOCAL +/g FMLOCAL +/b FMLOCAL +/i FMLOCAL +/gray { + /ww exch def + /b exch def + /g exch def + /r exch def + 0 1 ww 1 sub { /i exch def r i get .299 mul g i get .587 mul + b i get .114 mul add add r i 3 -1 roll floor cvi put } for + r + } bind def +/BITMAPTRUEGRAY { + gsave + translate rotate scale /h exch def /w exch def + /bitmapsave save def + /is w string def + /gis w string def + /bis w string def + /cf currentfile def + w h 8 [w 0 0 h neg 0 h] + { cf is readhexstring pop + cf gis readhexstring pop + cf bis readhexstring pop w gray} image + bitmapsave restore + grestore + } bind def +/BITMAPGRAY { + 8 {fakecolorsetup} COMMONBITMAP + } bind def +/BITMAPGRAYc { + 8 {fakecolorsetup} COMMONBITMAPc + } bind def +/ENDBITMAP { + } bind def +end + /ALDsave FMLOCAL + /ALDmatrix matrix def ALDmatrix currentmatrix pop +/StartALD { + /ALDsave save def + savematrix + ALDmatrix setmatrix + } bind def +/InALD { + restorematrix + } bind def +/DoneALD { + ALDsave restore + } bind def +%%EndProlog +%%BeginSetup +(3.0) FMVERSION +1 1 612 792 0 1 12 FMDOCUMENT +0 0 /Times-Roman FMFONTDEFINE +1 0 /Times-Bold FMFONTDEFINE +2 0 /Courier FMFONTDEFINE +3 0 /Times-Italic FMFONTDEFINE +32 FMFILLS +0 0 FMFILL +1 .1 FMFILL +2 .3 FMFILL +3 .5 FMFILL +4 .7 FMFILL +5 .9 FMFILL +6 .97 FMFILL +7 1 FMFILL +8 <0f1e3c78f0e1c387> FMFILL +9 <0f87c3e1f0783c1e> FMFILL +10 <cccccccccccccccc> FMFILL +11 <ffff0000ffff0000> FMFILL +12 <8142241818244281> FMFILL +13 <03060c183060c081> FMFILL +14 <8040201008040201> FMFILL +16 1 FMFILL +17 .9 FMFILL +18 .7 FMFILL +19 .5 FMFILL +20 .3 FMFILL +21 .1 FMFILL +22 0.03 FMFILL +23 0 FMFILL +24 <f0e1c3870f1e3c78> FMFILL +25 <f0783c1e0f87c3e1> FMFILL +26 <3333333333333333> FMFILL +27 <0000ffff0000ffff> FMFILL +28 <7ebddbe7e7dbbd7e> FMFILL +29 <fcf9f3e7cf9f3f7e> FMFILL +30 <7fbfdfeff7fbfdfe> FMFILL +%%EndSetup +%%Page: "11" 11 +%%BeginPaperSize: Letter +%%EndPaperSize +612 792 0 FMBEGINPAGE +57.97 26.65 554.03 40.82 R +7 X +0 K +V +0 10 Q +0 X +(11) 301 34.15 T +56.69 54.99 297.64 735.31 R +7 X +V +1 12 Q +0 X +(7. Fr) 56.69 727.31 T +(equently Asked Questions) 81.12 727.31 T +0 F +(7.1 Questions) 56.69 699.31 T +(1. How do I encode a sequence that can be) 82.49 672.31 T +(played by the Xing player?) 56.69 658.31 T +(2. I\325m using the Parallax XV) 82.49 644.31 T +(ideo card to) 220.34 644.31 T +(digitize; how do I MPEG-encode the resulting) 56.69 630.31 T +(data?) 56.69 616.31 T +(3. How do I convert the MPEG-encoder) 82.49 602.31 T +(YUV \336les into PPM \336les?) 56.69 588.31 T +(7.2 Answers) 56.69 546.31 T +-0.76 (1. The XING player samples video at 160x120 and) 56.69 518.31 P +(expands to output 320x240. This is where their) 56.69 504.31 T +(speed comes from. The player cannot buf) 56.69 490.31 T +(fer a) 255.32 490.31 T +(320x240 and thus had data overruns. The xing) 56.69 476.31 T +(player would \325) 56.69 462.31 T +(theoretically\325 handle 160x120 I) 126.42 462.31 T +(frames.) 56.69 448.31 T +(Thus, to encode, use P) 56.69 420.31 T +(A) 163.52 420.31 T +(TTERN I and 160x120) 170.85 420.31 T +(frames.) 56.69 406.31 T +(\050jboucher@\337ash.bu.edu\051) 56.69 392.31 T +(2. Use the type JMOVIE, or use the jmovie2jpeg) 56.69 364.31 T +(utility in the misc/ directory) 56.69 350.31 T +(.) 189.84 350.31 T +(3. Stanford\325) 56.69 322.31 T +(s CVv1.2.2.tar) 113.33 322.31 T +(.Z includes) 182.62 322.31 T +(cyuv2ppm.c.) 56.69 308.31 T +-0.2 (Which after you split the Y) 56.69 294.31 P +-0.2 (, U, and V components) 185.05 294.31 P +(out, works \336ne. \050curly@hsn.cftnet.com\051) 56.69 280.31 T +(This can be ftp\325d from) 56.69 252.31 T +2 F +(havefun.stanford.edu) 56.69 238.31 T +0 F +(, in the directory) 200.61 238.31 T +2 F +(/) 282.89 238.31 T +(pub/cv/) 56.69 224.31 T +0 F +(.) 107.07 224.31 T +314.36 54.99 552.76 735.31 R +7 X +V +FMENDPAGE +%%EndPage: "11" 10 +%%Page: "10" 10 +612 792 0 FMBEGINPAGE +57.97 26.65 554.03 40.82 R +7 X +0 K +V +0 10 Q +0 X +(10) 301 34.15 T +56.69 54.99 297.64 735.31 R +7 X +V +0 X +(Usage:) 85.04 728.64 T +2 F +(ppmtoeyuv < input.ppm > output.yuv) 85.04 715.64 T +0 F +0.33 (This takes as input a ppm file and outputs a subsam-) 85.04 689.64 P +(pled yuv file suitable for the encoder.) 56.69 678.64 T +0 12 Q +(6.2 jmovie2jpeg) 56.69 636.31 T +0 10 Q +(Usage:) 85.04 610.64 T +2 F +4.9 (jmovie2jpeg infile outfile start-) 85.04 597.64 P +(frame end-frame) 56.69 586.64 T +(infile) 85.04 560.64 T +0 F +( is a version 2 Parallax J_Movie) 121.02 560.64 T +2 F +(outfile) 85.04 547.64 T +0 F +( is a base file name for the output files) 127.02 547.64 T +2 F +5.44 (start-frame) 85.04 534.64 P +0 F +2.27 ( and) 151 534.64 P +2 F +5.44 (end-frame) 174.97 534.64 P +0 F +2.27 ( are the starting) 228.94 534.64 P +(and ending frame numbers) 56.69 523.64 T +1.39 (This takes as input a J_Movie and creates separate) 85.04 497.64 P +0.09 (JFIF compatible JPEG files with the names base<num>.jpg,) 56.69 486.64 P +(where base is outfile, and <num> are the frame numbers.) 56.69 475.64 T +9.6 (jmovie2jpeg was written by Jim Boucher) 85.04 462.64 P +(\050jboucher@\337ash.bu.edu\051.) 56.69 451.64 T +0 12 Q +(6.3 movieT) 56.69 424.31 T +(oV) 111.15 424.31 T +(id) 125.09 424.31 T +0 10 Q +(Usage:) 85.04 398.64 T +2 F +22.34 (movieToVid movieFile dataDir) 85.04 385.64 P +(indexDir srcHostName) 56.69 374.64 T +0 F +0.95 (This program is used to convert a Parallax J Movie) 85.04 348.64 P +0.58 (into a \322.vid\323 file, which is video only. vid files are used by) 56.69 337.64 P +(some of the programs described later.) 56.69 326.64 T +0.55 (See the README file in misc/mtv/ for more details) 85.04 313.64 P +(on usage.) 56.69 302.64 T +0.58 (movieToVid was written by Brian Smith \050bsmith@-) 85.04 289.64 P +(cs.berkeley.edu\051) 56.69 278.64 T +0 12 Q +(6.4 eyuvtojpeg) 56.69 236.31 T +0 10 Q +(Usage:) 85.04 210.64 T +2 F +(eyuvtojpeg infile outfile) 85.04 197.64 T +0 F +0.27 (This takes as input an encoder yuv file and outputs a) 85.04 171.64 P +(jpeg file. It uses cjpeg to do the compression.) 56.69 160.64 T +0 12 Q +(6.5 blockrun) 56.69 118.31 T +0 10 Q +(Usage:) 85.04 92.64 T +2 F +2.9 (blockrun command num_args firstnum) 85.04 79.64 P +(lastnum skip arg1 ... argn) 56.69 68.64 T +314.36 54.99 552.76 735.31 R +7 X +V +0 F +0 X +7.53 (This runs the given command \050which has) 342.71 715.64 P +2 F +0.32 (num_args) 314.36 704.64 P +0 F +0.13 ( args\051, with the args) 362.34 704.64 P +2 F +0.32 (arg1 ... argn) 444.61 704.64 P +0 F +0.13 (, where) 523.21 704.64 P +-0.04 (any \325=\325 character is replaced by a number from) 314.36 693.64 P +2 F +-0.09 (firstnum) 504.78 693.64 P +0 F +(to) 314.36 682.64 T +2 F +(lastnum) 324.64 682.64 T +0 F +( skipping by) 366.61 682.64 T +2 F +(skip) 418.54 682.64 T +0 F +(. For example:) 442.52 682.64 T +2 F +7.23 (blockrun eyuvtojpeg 2 13 19 3) 342.71 669.64 P +(flow=.yuv flow=.jpg) 314.36 658.64 T +0 F +(will run:) 342.71 632.64 T +2 F +(eyuvtojpeg flow13.yuv flow13.jpg) 342.71 606.64 T +(eyuvtojpeg flow16.yuv flow16.jpg) 342.71 593.64 T +(eyuvtojpeg flow19.yuv flow19.jpg) 342.71 580.64 T +0 12 Q +(6.6 vidtoppm) 314.36 553.31 T +0 10 Q +(Usage:) 342.71 527.64 T +2 F +10.05 (vidtoppm filename width height) 342.71 514.64 P +(start end outbase [quality]) 314.36 503.64 T +0 F +0.39 (This takes as input a .vid file of given) 342.71 477.64 P +2 F +0.94 (height) 499.45 477.64 P +0 F +0.39 ( and) 535.43 477.64 P +2 F +2.91 (width) 314.36 466.64 P +0 F +1.22 (, and turns them into a bunch of ppm files named) 344.35 466.64 P +2 F +(outbase) 314.36 455.64 T +0 F +(.N, where N is a number from) 356.34 455.64 T +2 F +(start) 478.74 455.64 T +0 F +( to) 508.73 455.64 T +2 F +(end) 521.5 455.64 T +0 F +(.) 539.49 455.64 T +0 12 Q +(6.7 vidtojpeg) 314.36 428.31 T +0 10 Q +(Usage:) 342.71 402.64 T +2 F +8.05 (vidtojpeg filename width height) 342.71 389.64 P +(start end outbase [quality]) 314.36 378.64 T +0 F +2.83 (This is the same as vidtoppm, except it outputs) 342.71 352.64 P +(JPEG files instead of PPM files.) 314.36 341.64 T +0 12 Q +(6.8 vidtoeyuv) 314.36 314.31 T +0 10 Q +(Usage:) 342.71 288.64 T +2 F +8.05 (vidtoeyuv filename width height) 342.71 275.64 P +(start nth outbase [quality]) 314.36 264.64 T +0 F +0.39 (This takes as input a .vid file of given) 342.71 238.64 P +2 F +0.94 (height) 499.45 238.64 P +0 F +0.39 ( and) 535.43 238.64 P +2 F +3.58 (width) 314.36 227.64 P +0 F +1.49 (, and turns them into a bunch of yuv files named) 344.35 227.64 P +2 F +2.87 (outbase) 314.36 216.64 P +0 F +1.2 (.N, where N is a number from) 356.34 216.64 P +2 F +2.87 (start) 487.12 216.64 P +0 F +1.2 ( to) 517.1 216.64 P +2 F +2.87 (end) 532.27 216.64 P +0 F +1.2 (,) 550.26 216.64 P +(skipping by) 314.36 205.64 T +2 F +(nth) 363.79 205.64 T +0 F +(.) 381.77 205.64 T +0 12 Q +(6.8 PBMPLUS) 314.36 176.31 T +0 10 Q +-0.04 (There is a very useful package called pbmplus avail-) 342.71 150.64 P +1.46 (able for ftp \050ee.utah.edu:/pbmplus for example\051. This has) 314.36 139.64 P +0.31 (conversions from TIFF, GIF, and many other common for-) 314.36 128.64 P +0.86 (mats to PPMs, which the encoder can read. You can even) 314.36 117.64 P +0.58 (keep the originals in their own format, and do conversions) 314.36 106.64 P +(via) 314.36 95.64 T +2 F +(INPUT_CONVERT.) 329.07 95.64 T +FMENDPAGE +%%EndPage: "10" 9 +%%Page: "9" 9 +612 792 0 FMBEGINPAGE +57.97 26.65 554.03 40.82 R +7 X +0 K +V +0 10 Q +0 X +(9) 303.5 34.15 T +56.69 54.99 297.64 735.31 R +7 X +V +0 X +0.08 (ating input files must work on the remote machine. Also the) 56.69 728.64 P +1.78 (USER_DATA and CDL_FILE files must be available on) 56.69 717.64 P +2.53 (the remote sites as specified in the parameter file. This) 56.69 706.64 P +(should be fixed in future versions.) 56.69 695.64 T +1 12 Q +(4. Performance) 56.69 668.31 T +0 10 Q +1.23 (Table seven shows a comparison of sequential per-) 85.04 641.64 P +(formance on different machine types.) 56.69 630.64 T +1.31 (Parallel performance is dependent not only on pro-) 85.04 422.64 P +1.87 (cessor performance, but network performance. If you are) 56.69 411.64 P +-0.21 (using a 10 Mb/s Ethernet, don\325t expect to get better than 4 or) 56.69 400.64 P +0.89 (5 frames per second -- no matter how fast your processors) 56.69 389.64 P +(are.) 56.69 378.64 T +2.5 (Parallel performance is also greatly dependent on) 85.04 365.64 P +1.16 (how big the input files are \050YUV is better than PPM, and) 56.69 354.64 P +0.81 (JPEG is better than both\051, and how big the output files are) 56.69 343.64 P +(\050better compression will lead to less I/O\051.) 56.69 332.64 T +1 12 Q +(5. Other Options) 56.69 305.31 T +0 10 Q +-0 (This section gives example of some more rarely used) 85.04 278.64 P +0.12 (options in the parameter \336le, such as customizing the Quan-) 56.69 267.64 P +(tization tables, or setting the aspect ratio.) 56.69 256.64 T +0 12 Q +(5.1 Custom Quantization T) 56.69 229.31 T +(ables \050parameter \336le\051) 186.45 229.31 T +0 10 Q +(You can specify your own custom quantization tables.) 56.69 206.64 T +-0.31 (Currently you can only do this once per MPEG file.) 56.69 194.64 P +-0.31 ( Y) 260.99 194.64 P +-0.31 (ou can) 269.39 194.64 P +-0.06 (specify both Intra- and Non-intra quantization tables. If you) 56.69 182.64 P +-0.44 (don\325) 56.69 170.64 P +-0.44 (t specify them, then the default tables are used \050c.f. page) 74.83 170.64 P +(D-16, D-17 of the standard\051.) 56.69 158.64 T +(Usage:) 85.04 141.64 T +2 F +(IQTABLE) 85.04 115.64 T +(table row 1) 85.04 102.64 T +(table row 2) 85.04 89.64 T +(...) 85.04 76.64 T +(table row 8) 85.04 63.64 T +100.63 478.31 253.7 484.31 C +0 0 612 792 C +0 10 Q +0 X +0 K +0.06 (a. Macroblocks per second; a) 118.63 471.64 P +1.5 (320x240 pixel image is 300) 118.63 459.64 P +(macroblocks per frame.) 118.63 447.64 T +1 12 Q +(T) 99.59 594.31 T +(able 7: Machine Comparison) 106.49 594.31 T +0 10 Q +(Machine) 121.41 572.64 T +(MPS) 203.66 572.64 T +0 8 Q +(a) 223.66 576.64 T +0 10 Q +(HP 9000/755) 120.13 554.64 T +(280) 207.94 554.64 T +(DEC 3000/400) 112.92 538.64 T +(247) 207.94 538.64 T +(HP 9000/750) 120.13 522.64 T +(191) 207.94 522.64 T +(Sparc 10) 137.91 506.64 T +(104) 207.94 506.64 T +(DEC 5000) 130.69 490.64 T +(68) 210.44 490.64 T +100.63 584.06 100.63 484.56 2 L +V +0.5 H +0 Z +N +177.16 584.56 177.16 484.06 2 L +V +N +253.7 584.06 253.7 484.56 2 L +V +N +100.38 584.31 253.95 584.31 2 L +V +N +100.88 565.56 253.45 565.56 2 L +V +N +100.88 563.06 253.45 563.06 2 L +V +N +100.38 548.31 253.95 548.31 2 L +V +N +100.38 532.31 253.95 532.31 2 L +V +N +100.38 516.31 253.95 516.31 2 L +V +N +100.38 500.31 253.95 500.31 2 L +V +N +100.38 484.31 253.95 484.31 2 L +V +N +314.36 54.99 552.76 735.31 R +7 X +V +0 X +0.96 (This specifies the intra-coding quantization table \050I) 342.71 715.64 P +0.01 (frames and I-blocks in P and B frames\051. Each) 314.36 704.64 P +2 F +0.03 (table row) 498.76 704.64 P +0 F +(is simply 8 integers, separated by tabs and/or spaces.) 314.36 693.64 T +(Usage:) 342.71 680.64 T +2 F +(NIQTABLE) 342.71 654.64 T +(table row 1) 342.71 641.64 T +(table row 2) 342.71 628.64 T +(...) 342.71 615.64 T +(table row 8) 342.71 602.64 T +0 F +4.63 (This specifies the non-intra-coding quantization) 342.71 576.64 P +(table \050difference vectors in P and B frames\051.) 314.36 565.64 T +0 12 Q +(5.2 Aspect Ratio \050parameter \336le\051) 314.36 538.31 T +0 10 Q +(Y) 340.16 512.64 T +(ou can specify the aspect ratio to be one of the) 346.37 512.64 T +(fourteen legal values as speci\336ed in the standard \050c.f.) 314.36 500.64 T +(Section 2.4.3.2\051. This sets the requested aspect ration for) 314.36 488.64 T +(playback.) 314.36 476.64 T +0 12 Q +(Usage:) 340.16 463.31 T +(ASPECT_RA) 340.16 449.31 T +(TIO \337oat) 406.13 449.31 T +2 10 Q +8.03 (float) 342.71 436.64 P +0 F +3.34 ( is one of {1.0, 0.6735, 0.7031, 0.7615,) 372.69 436.64 P +2.25 (0.8055, 0.8437, 0.8935, 0.9157, 0.9815, 1.0255, 1.0695,) 314.36 425.64 P +(1.0950, 1.1575, 1.2015}.) 314.36 414.64 T +0 12 Q +(5.3 Frame Rate \050parameter \336le\051) 314.36 374.31 T +0 10 Q +-0 (You can specify the frame rate to be one of the eight) 342.71 348.64 P +1.97 (legal values \050c.f. Section 2.4.3.2\051. This is used by some) 314.36 337.64 P +(playback systems to gauge the playback rate.) 314.36 326.64 T +(Usage:) 342.71 313.64 T +2 F +(FRAME_RATE float) 342.71 300.64 T +4.91 (float) 342.71 287.64 P +0 F +2.05 ( is one of {23.976, 24, 25, 29.97, 30, 50,) 372.69 287.64 P +(59.94, 60}.) 314.36 276.64 T +0 12 Q +(5.4 Floating Point DCT \050command line\051) 314.36 249.31 T +0 10 Q +2.13 (The encoder normally uses a quick algorithm for) 342.71 236.64 P +2.06 (forward and reverse DCTs. However, in sequences with) 314.36 225.64 P +0.5 (many P frames, this can result in errors when decoded ref-) 314.36 214.64 P +0.4 (erence frames are used. To use the \050slow\051 double precision) 314.36 203.64 P +(accurate dcts, use the following flag:) 314.36 192.64 T +(Usage:) 342.71 179.64 T +(mpeg_encode -float_dct) 342.71 166.64 T +1 12 Q +(6. Other T) 314.36 139.31 T +(ools) 366.89 139.31 T +0 10 Q +(The misc/ directory contains several useful tools.) 314.36 112.64 T +0 12 Q +(6.1 ppmtoeyuv) 314.36 83.31 T +FMENDPAGE +%%EndPage: "9" 8 +%%Page: "8" 8 +612 792 0 FMBEGINPAGE +57.97 26.65 554.03 40.82 R +7 X +0 K +V +0 10 Q +0 X +(8) 303.5 34.15 T +56.69 54.99 297.64 735.31 R +7 X +V +0 X +(frame-to-I-frame scale.) 56.69 728.64 T +(Usage:) 85.04 715.64 T +2 F +(-bit_rate_info rate_file) 85.04 702.64 T +0 F +(This puts the bit rate info into the specified file) 85.04 689.64 T +(\050order of info, etc.\051) 85.04 676.64 T +1 12 Q +(3. Parallel Usage) 56.69 641.31 T +0 10 Q +1.03 (In parallel execution there are slave processes. You) 85.04 610.64 P +(can have those processes run nicely if you want.) 56.69 599.64 T +(Usage:) 85.04 586.64 T +2 F +(-nice) 85.04 573.64 T +0 F +3.56 (This makes all slave processes run nicely. This) 85.04 560.64 P +0.8 (means that interactive users take precedence, so they don\325t) 56.69 549.64 P +-0.2 (feel like they\325re running in molasses. If you want to be mean) 56.69 538.64 P +(to them, don\325t use this option. :-\051) 56.69 527.64 T +0 12 Q +(3.1 Architecture Overview) 56.69 500.31 T +0 10 Q +1.3 (Figure 1 shows a diagram of the system architecture. The) 56.69 474.64 P +-0.03 (slaves exist on the different slave machines which you spec-) 56.69 463.64 P +1.5 (ify \050see Section 3.2\051. The server processes all live on the) 56.69 452.64 P +(machine you run the encoder on.) 56.69 441.64 T +0 12 Q +(3.2 Specifying Slave Machines \050both\051) 56.69 412.31 T +0 10 Q +0.03 (You specify the slave machines in the parameter file.) 85.04 386.64 P +-0.2 (For each slave you must specify the username to use, as well) 56.69 375.64 P +0.06 (as the executable mpeg_encode program. If a slave does not) 56.69 364.64 P +1.52 (have NFS access, then it is REMOTE and you must also) 56.69 353.64 P +(specify where the parameter file is.) 56.69 342.64 T +(Usage:) 85.04 316.64 T +2 F +(PARALLEL) 85.04 303.64 T +(slave_specification) 85.04 290.64 T +(END_PARALLEL) 85.04 277.64 T +(slave_specification) 85.04 251.64 T +0 F +( can be either:) 198.98 251.64 T +2 F +(machine username executable) 85.04 238.64 T +0 F +(or) 85.04 225.64 T +2 F +2.9 (REMOTE machine username executable) 85.04 212.64 P +(param_file) 56.69 201.64 T +0 F +0.76 (You must have an account with the given username) 85.04 175.64 P +0.06 (on each machine, and you must place your machine/login in) 56.69 164.64 P +(the appropriate .rhosts files.) 56.69 153.64 T +1.59 (To make it easier to run experiments with varying) 85.04 127.64 P +1.35 (numbers of processors, there is a command-line argument) 56.69 116.64 P +(which limits the number of slave machines.) 56.69 105.64 T +(Usage:) 85.04 79.64 T +2 F +(-max_machines num_machines) 85.04 66.64 T +314.36 54.99 552.76 735.31 R +7 X +V +0 F +0 X +0.98 (This means that the encoder will use no more than) 342.71 728.64 P +2 F +(num_machines) 314.36 717.64 T +0 F +( machines as slaves.) 386.32 717.64 T +0 12 Q +(3.3 Remote Shell \050parameter \336le\051) 314.36 677.31 T +0 10 Q +0.98 (To run processes on the slave machines, mpeg_en-) 342.71 651.64 P +1.52 (code uses the remote shell command. On most machines) 314.36 640.64 P +-0.09 (this is the command) 314.36 629.64 P +2 F +-0.2 (rsh) 396.76 629.64 P +0 F +-0.09 (. On HP machines, however, rsh is) 414.75 629.64 P +0.39 (the restricted shell; on HP machines, the right command to) 314.36 618.64 P +(use is) 314.36 607.64 T +2 F +(remsh) 339.35 607.64 T +0 F +(, rather than) 369.33 607.64 T +2 F +(rsh) 419.84 607.64 T +0 F +(.) 437.83 607.64 T +(Usage:) 342.71 594.64 T +2 F +(RSH <rsh command>) 342.71 568.64 T +0 12 Q +(3.4 Scheduling Algorithms \050parameter \336le\051) 314.36 528.31 T +0 10 Q +0.87 (The encoder provides 3 different scheduling algorithms to) 314.36 502.64 P +(schedule which processors get which frames.) 314.36 491.64 T +2.03 (The first scheduling algorithm simply assigns N/P) 340.16 478.64 P +0.28 (frames to each processor, where N is the number of frames) 314.36 467.64 P +0.72 (and P is the number of processors. This has the advantage) 314.36 456.64 P +-0.01 (of minimal overhead, but only works well when all the pro-) 314.36 445.64 P +0.3 (cessors run at nearly the same speed. Also, since most pro-) 314.36 434.64 P +0.37 (cessors will finish at about the same time, you will have to) 314.36 423.64 P +1.41 (wait at the end while the Combine Server gathers all the) 314.36 412.64 P +(frame files together.) 314.36 401.64 T +(Usage:) 340.16 388.64 T +(PARALLEL_PERFECT) 340.16 375.64 T +3.6 (The second scheduling algorithm first assigns S) 340.16 349.64 P +-0.09 (frames to each processor. When a processor is finished, it is) 314.36 338.64 P +1.49 (assigned T seconds of work \050the scheduler estimates this) 314.36 327.64 P +1.97 (based on previous performance\051. S should be at least 3,) 314.36 316.64 P +0.37 (preferably at least 5 or 6, to insure a good estimate of each) 314.36 305.64 P +(processor\325s speed.) 314.36 294.64 T +(Usage:) 340.16 281.64 T +(PARALLEL_TEST_FRAMES S) 340.16 268.64 T +(PARALLEL_TIME_CHUNKS T) 340.16 255.64 T +0.79 (The third scheduling algorithm is probably the best.) 340.16 229.64 P +2.43 (It also first assigns S frames to each processor. Subse-) 314.36 218.64 P +3.44 (quently, however, whenever a processor finishes, it is) 314.36 207.64 P +1.1 (assigned enough work to keep it busy until almost every-) 314.36 196.64 P +2.04 (thing is done. Effectively, a processor is assigned many) 314.36 185.64 P +-0.06 (frames, and then fewer and fewer frames as more work gets) 314.36 174.64 P +2.92 (done. This insures good load balancing, while limiting) 314.36 163.64 P +(scheduling overhead.) 314.36 152.64 T +(Usage:) 340.16 139.64 T +(PARALLEL_TEST_FRAMES S) 340.16 126.64 T +(PARALLEL_CHUNK_TAPER) 340.16 113.64 T +0 12 Q +(3.5 Parallel problems \050parameter \336le\051) 314.36 86.31 T +0 10 Q +8.16 (There are some unsupported features using) 340.16 73.64 P +-0.18 (REMOTE to specify slaves: The \324command\324 form of gener-) 314.36 62.64 P +FMENDPAGE +%%EndPage: "8" 7 +%%Page: "7" 7 +612 792 0 FMBEGINPAGE +57.97 26.65 554.03 40.82 R +7 X +0 K +V +0 10 Q +0 X +(7) 303.5 34.15 T +56.69 54.99 297.64 477.36 R +7 X +V +0 X +(you can specify frame files to combine.) 56.69 470.69 T +1.11 (The parameter file may specify input frame files in) 85.04 457.69 P +0.94 (the same manner as normal input files -- except instead of) 56.69 446.69 P +7.11 (using INPUT_DIR, INPUT, and END_INPUT, use) 56.69 435.69 P +28.3 (FRAME_INPUT_DIR, FRAME_INPUT, and) 56.69 424.69 P +1.83 (FRAME_END_INPUT. If no input frame files are speci-) 56.69 413.69 P +0.16 (fied, then the default is to use the output file name with suf-) 56.69 402.69 P +(fix \322.frame.<frame_num>\323 starting from 0 as the input files.) 56.69 391.69 T +0 12 Q +(2.15 Stats and Other Options \050command line\051) 56.69 364.36 T +0 10 Q +0.43 (There are several options for printing or suppressing) 85.04 338.69 P +(useful information.) 56.69 327.69 T +0.75 (The encoder always prints \050to stdout\051 parameter file) 85.04 314.69 P +2.87 (information and statistics about how many I, P, and B) 56.69 303.69 P +0.8 (frames there were, and information about compression and) 56.69 292.69 P +2.31 (quality. You can send these statistics, in addition to the) 56.69 281.69 P +(screen, to a file.) 56.69 270.69 T +(Usage:) 85.04 257.69 T +2 F +( -stat stat_file) 110.83 244.69 T +0 F +2.56 (This appends the parameter file info and stats to) 85.04 231.69 P +2 F +(stat_file) 56.69 220.69 T +0 F +0.87 (Normally, the statistics do not include any informa-) 85.04 194.69 P +1.77 (tion about quality. This is because computing the quality) 56.69 183.69 P +2.02 (takes a little more time. If you wish to have the quality) 56.69 172.69 P +1.17 (included in the statistics, use the -snr command line argu-) 56.69 161.69 P +(ment.) 56.69 150.69 T +(Usage:) 85.04 137.69 T +2 F +(-snr) 85.04 124.69 T +0 F +1.78 (This prints the signal-to-noise ratio \050snr\051 and peak) 85.04 111.69 P +(snr.) 56.69 100.69 T +3.69 (An additional statistic measure is mean squared) 85.04 74.69 P +0.78 (error. If you wish to see the per-block mean squared error,) 56.69 63.69 P +314.36 54.99 552.76 477.36 R +7 X +V +0 X +(use the -mse command line flag \050sets -snr as a side effect\051.) 314.36 470.69 T +(Usage:) 342.71 457.69 T +2 F +(-mse) 342.71 444.69 T +0 F +(This prints the MSE for each block encoded) 342.71 431.69 T +1.02 (Another set of data which can be useful is a histo-) 342.71 418.69 P +0.44 (gram of the motion vectors. The encoder can keep track of) 314.36 407.69 P +-0.16 (P-frame motion vectors and forward and backward B-frame) 314.36 396.69 P +0.4 (motion vectors. The output is in the form of a matrix, each) 314.36 385.69 P +0.98 (entry corresponding to a motion vector in the search win-) 314.36 374.69 P +0.49 (dow. The center of the matrix represents \0500,0\051 motion vec-) 314.36 363.69 P +(tors.) 314.36 352.69 T +(Usage:) 342.71 339.69 T +2 F +(-mv_histogram) 342.71 326.69 T +0 F +1.47 (During normal execution, the encoder outputs two) 342.71 300.69 P +0.58 (kinds of information. It prints a single line for each frame,) 314.36 289.69 P +0.86 (summarizing block type and time info. It also prints, after) 314.36 278.69 P +-0.07 (each frame, an estimate of the remaining running time. You) 314.36 267.69 P +(can modify how often or if this information is to be shown.) 314.36 256.69 T +(Usage:) 342.71 243.69 T +2 F +( -quiet num) 342.71 230.69 T +( -no_frame_summary) 342.71 217.69 T +(-realquiet) 349.8 204.69 T +0 F +-0.13 (If) 342.71 191.69 P +2 F +-0.31 (num) 351.74 191.69 P +0 F +-0.13 ( is negative, the time estimate is never shown;) 369.73 191.69 P +1.94 (otherwise, it reports a time estimate no more often than) 314.36 180.69 P +1.22 (every) 314.36 169.69 P +2 F +2.92 (num) 340.27 169.69 P +0 F +1.22 ( seconds \050unless the time estimate rises, which) 358.26 169.69 P +1 (will happen near the beginning of the run\051. The default is) 314.36 158.69 P +2 F +(num) 314.36 147.69 T +0 F +( = 0, which means report after every frame.) 332.35 147.69 T +1.08 (If) 342.71 134.69 P +2 F +2.59 (-no_frame_summary) 352.94 134.69 P +0 F +1.08 ( is given, then informa-) 454.89 134.69 P +(tion about each frame is not printed.) 314.36 123.69 T +2 F +10.05 (-realquiet stops all printing,) 342.71 110.69 P +(other than error messages.) 314.36 99.69 T +0 F +1 (Another nice feature is that the encoder can output) 342.71 73.69 P +0.75 (the bit rate, on both a frame-to-frame scale, and also an I-) 314.36 62.69 P +385.51 518.46 470.55 575.15 R +7 X +V +0.5 H +0 Z +0 X +N +141.05 721.49 294.05 766.49 18 RR +7 X +V +0 X +N +1 18 Q +(Master Server) 165.05 738.43 T +102.05 623.49 181.05 660.49 R +7 X +V +0 X +N +(Slave) 121.05 637.49 T +452.05 625.49 531.05 662.49 R +7 X +V +0 X +N +(Slave) 471.05 639.49 T +333.05 624.49 412.05 661.49 R +7 X +V +0 X +N +(Slave) 352.05 638.49 T +218.05 623.49 297.05 660.49 R +7 X +V +0 X +N +(Slave) 237.05 637.49 T +147.47 523.06 300.47 568.06 18 RR +7 X +V +0 X +N +(Combine Server) 165.31 539.33 T +498.64 673.25 501.04 661.49 492.77 670.19 495.71 671.72 4 Y +V +472.44 709.73 470.04 721.49 478.31 712.79 475.37 711.26 4 Y +V +495.71 671.72 475.38 711.26 2 L +7 X +V +0 X +N +137.07 670.41 129.04 661.49 131.12 673.31 134.09 671.86 4 Y +V +149.02 710.57 157.04 719.49 154.97 707.67 151.99 709.12 4 Y +V +134.1 671.86 152 709.12 2 L +7 X +V +0 X +N +164.04 661.55 152.04 661.49 162.18 667.9 163.11 664.73 4 Y +V +344.03 719.43 356.03 719.49 345.89 713.08 344.96 716.26 4 Y +V +163.12 664.72 344.98 716.25 2 L +7 X +V +0 X +N +392.55 667.26 382.02 661.5 387.89 671.96 390.22 669.61 4 Y +V +430.5 714.73 441.02 720.49 435.15 710.03 432.82 712.38 4 Y +V +390.24 669.6 432.85 712.37 2 L +7 X +V +0 X +N +277.89 664.38 266.04 662.49 275.09 670.37 276.49 667.37 4 Y +V +377.19 717.6 389.04 719.49 379.98 711.61 378.59 714.61 4 Y +V +276.5 667.37 378.6 714.61 2 L +7 X +V +0 X +N +241.78 672.73 248.04 662.49 237.31 667.86 239.55 670.29 4 Y +V +192.3 709.25 186.04 719.49 196.78 714.12 194.54 711.69 4 Y +V +239.55 670.29 194.54 711.69 2 L +7 X +V +0 X +N +353.94 670.32 363.03 662.49 351.17 664.31 352.56 667.32 4 Y +V +245.13 712.66 236.04 720.49 247.9 718.67 246.52 715.67 4 Y +V +352.57 667.31 246.52 715.66 2 L +7 X +V +0 X +N +470.93 667.95 481.05 661.49 469.05 661.61 469.99 664.78 4 Y +V +287.16 714.03 277.05 720.49 289.05 720.37 288.1 717.2 4 Y +V +469.99 664.78 288.1 717.2 2 L +7 X +V +0 X +N +336.05 720.49 489.05 765.49 18 RR +7 X +V +0 X +N +(Decode Server) 360.05 737.43 T +134.71 613.56 130.39 624.76 139.99 617.55 137.35 615.56 4 Y +V +168.59 579.26 172.91 568.07 163.31 575.27 165.95 577.27 4 Y +V +137.35 615.56 165.96 577.26 2 L +7 X +V +0 X +N +234.18 617.55 243.77 624.76 239.45 613.56 236.82 615.56 4 Y +V +210.85 575.27 201.26 568.07 205.58 579.26 208.21 577.27 4 Y +V +236.82 615.56 208.22 577.26 2 L +7 X +V +0 X +N +352.39 622.81 364.23 624.77 355.23 616.83 353.81 619.82 4 Y +V +255.6 570.03 243.76 568.08 252.77 576.01 254.18 573.02 4 Y +V +353.83 619.81 254.2 573.01 2 L +7 X +V +0 X +N +465.63 624.68 477.63 624.76 467.5 618.33 466.56 621.51 4 Y +V +291.21 568.14 279.21 568.07 289.34 574.49 290.28 571.32 4 Y +V +466.57 621.51 290.28 571.31 2 L +7 X +V +0 X +N +393.94 616.24 385.49 624.76 397.17 622.01 395.55 619.12 4 Y +V +440.83 597.86 449.27 589.33 437.59 592.09 439.21 594.98 4 Y +V +395.57 619.11 439.23 594.97 2 L +7 X +V +0 X +N +281.34 617.08 272.12 624.76 284.01 623.14 282.67 620.11 4 Y +V +376.28 582.84 385.5 575.16 373.61 576.78 374.94 579.81 4 Y +V +282.68 620.1 374.96 579.8 2 L +7 X +V +0 X +N +168.9 618.38 158.74 624.76 170.74 624.73 169.82 621.56 4 Y +V +375.34 567.36 385.51 560.98 373.51 561 374.43 564.18 4 Y +V +169.82 621.55 374.43 564.18 2 L +7 X +V +0 X +N +312.01 543.5 300.47 546.8 312.01 550.11 312.01 546.8 4 Y +V +373.98 550.11 385.51 546.8 373.98 543.5 373.98 546.8 4 Y +V +312.01 546.8 373.98 546.8 2 L +7 X +V +0 X +N +302.9 498.61 259.93 498.61 2 L +V +1.14 H +N +1 12 Q +(Figure 1) 259.93 499.8 T +385.51 498.49 302.9 498.49 2 L +V +0.59 H +N +0 F +(: Network Model) 302.9 499.8 T +495.43 619 505.95 624.77 500.08 614.3 497.76 616.65 4 Y +V +473.96 588.02 463.44 582.25 469.3 592.72 471.63 590.37 4 Y +V +497.79 616.64 471.66 590.35 2 L +7 X +V +0.5 H +0 X +N +1 18 Q +(Disk) 410.52 530.49 T +7 X +90 450 42.52 14.17 428.03 575.15 G +0 X +90 450 42.52 14.17 428.03 575.15 A +7 X +180 270 42.94 21.26 428.45 521.28 G +2 Z +0 X +180 270 42.94 21.26 428.45 521.28 A +7 X +270 360 42.94 21.26 427.68 521.61 G +0 X +270 360 42.94 21.26 427.68 521.61 A +FMENDPAGE +%%EndPage: "7" 6 +%%Page: "6" 6 +612 792 0 FMBEGINPAGE +57.97 26.65 554.03 40.82 R +7 X +0 K +V +0 10 Q +0 X +(6) 303.5 34.15 T +56.69 54.99 297.64 735.31 R +7 X +V +0 X +3.03 (number\051. This field entry can be changed by adding a) 56.69 728.64 P +1.27 (USER_DATA parameter whose vale is the name of a file) 56.69 717.64 P +(continuing the data to add to the header.) 56.69 706.64 T +(Usage:) 85.04 693.64 T +(USER_DATA ./user_data.txt) 85.04 667.64 T +0 12 Q +(2.1) 56.69 640.31 T +(1 Compression Decision List \050CDL, also) 71.24 640.31 T +(Speci\336cs File \050parameter \336le\051\051) 56.69 626.31 T +0 10 Q +0.18 (If you want to be very exact in what is set during the) 85.04 599.64 P +1.71 (encoding, use CDL_FILE \050the older SPECIFICS_FILE is) 56.69 588.64 P +0.18 (supported as well\051 to point to a file describing the exact set-) 56.69 577.64 P +0.22 (tings wished for the encoding. The version 1.0 of CDL sup-) 56.69 566.64 P +(port has the following format:) 56.69 555.64 T +(version 1) 85.04 542.64 T +(frame FN T Q) 85.04 529.64 T +(slice SN Q) 85.04 516.64 T +(block BN Q | BN Q skip | BN Q bi fx fy bx by |) 85.04 503.64 T +(BN Q forw fx fy | BN Q back bx by) 109.7 490.64 T +3.51 (FN, SN, and BN signal which frame/slice/block) 85.04 477.64 P +1.64 (number the command applies to. Note that if you have a) 56.69 466.64 P +2.22 (block or slice command, must be proceeded by a frame) 56.69 455.64 P +0.63 (command for that frame. T sets the type of the frame \050I, P,) 56.69 444.64 P +-0.06 (B, or - to not set\051. Q sets the q-scale \0501-31 or +N -N for rela-) 56.69 433.64 P +0.17 (tive scaling, or 0 for no change\051. The detailed block specifi-) 56.69 422.64 P +2.81 (cations set the motion vectors \050in half-pixel units\051. See) 56.69 411.64 P +(specifications.c for more information.) 56.69 400.64 T +2.25 (Version 2 CDL files have relative Qscales, so \3222) 85.04 387.64 P +1.89 (\322means decrease the Qscale by 2, \3222\323 means increase it.) 56.69 376.64 P +(Unsigned numbers like \3224\323 set the Qscale \050to 4\051.) 56.69 365.64 T +(Usage:) 85.04 352.64 T +(CDL_FILE filename) 85.04 326.64 T +(CDL_DEFINES string) 85.04 313.64 T +3 (where filename contains the specifics, and string) 85.04 300.64 P +0.61 (\050optional\051 are defines to be passed to the C preprocessor to) 56.69 289.64 P +(use on the file \050-Db=block for example\051.) 56.69 278.64 T +0 12 Q +(2.12 Gamma Correction \050parameter \336le\051) 56.69 251.31 T +0 10 Q +-0.13 (If your movies are too light or too dark for your play-) 85.04 224.64 P +(back system, you can pre-gamma correct them.) 56.69 213.64 T +(Usage:) 85.04 200.64 T +2 F +(GAMMA gamma-val) 85.04 174.64 T +0 F +1.5 (gamma-corrects by raising each luminance fraction) 85.04 161.64 P +(to the power) 56.69 150.64 T +2 F +(gamma-val) 109.15 150.64 T +0 F +(\050a float\051) 169.12 150.64 T +0.18 (This works by converting the luminance \050brightness\051) 85.04 137.64 P +-0.17 (of the input image to a fraction zero to one, and then raises it) 56.69 126.64 P +-0.17 (to the power) 56.69 115.64 P +2 F +-0.4 (gamma-val) 108.65 115.64 P +0 F +-0.17 (. Thus values less than 1 brighten,) 162.62 115.64 P +2.36 (and greater than 1 dim. If your output device has good) 56.69 104.64 P +0.92 (brightness controls, it is better to control brightness at that) 56.69 93.64 P +(end.) 56.69 82.64 T +314.36 54.99 552.76 735.31 R +7 X +V +0 12 Q +0 X +(2.13 Encoding GOPs at a T) 314.36 727.31 T +(ime \050command line\051) 445.86 727.31 T +0 10 Q +2.65 (Instead of encoding an entire sequence, you can) 342.71 700.64 P +1.57 (encode a single GOP. GOPs can later be joined together) 314.36 689.64 P +(with the encoder to form an MPEG file.) 314.36 678.64 T +(Usage:) 342.71 665.64 T +2 F +(-gop num) 342.71 652.64 T +0 F +1.81 (This only encodes the numbered GOP \050which are) 342.71 639.64 P +(numbered beginning at 0.) 314.36 628.64 T +1.2 (The output file will be the normal output filename) 342.71 615.64 P +(with the suffix \322.gop.<gop_num>\323) 314.36 604.64 T +1.18 (GOP files can be joined at any time using the fol-) 342.71 578.64 P +(lowing command-line argument.) 314.36 567.64 T +(Usage:) 342.71 554.64 T +2 F +(-combine_gops) 342.71 541.64 T +0 F +2.05 (This causes the encoder to simply combine some) 342.71 528.64 P +0.52 (GOP files into a single MPEG stream. A sequence header/) 314.36 517.64 P +0.03 (ender are inserted. In this case, the parameter file need only) 314.36 506.64 P +0.18 (contain the YUV_SIZE value, an output file, and perhaps a) 314.36 495.64 P +0.21 (list of input GOP files. If no list of input GOP files is used,) 314.36 484.64 P +0.45 (then the encoder assumes you\325re using the same parameter) 314.36 473.64 P +-0.08 (file you used with the) 314.36 462.64 P +2 F +-0.19 (-gop) 403.08 462.64 P +0 F +-0.08 (option, and calculates the cor-) 432.88 462.64 P +0.52 (responding gop filenames itself. If this is not the case, you) 314.36 451.64 P +0.58 (can specify input GOP files in the same manner as normal) 314.36 440.64 P +0.44 (input files -- except instead of using INPUT_DIR, INPUT,) 314.36 429.64 P +1.79 (and END_INPUT, use GOP_INPUT_DIR, GOP_INPUT,) 314.36 418.64 P +1.38 (and GOP_END_INPUT. If no input GOP files are speci-) 314.36 407.64 P +-0.05 (fied, then the default is to use the output file name with suf-) 314.36 396.64 P +(fix \322.gop.<gop_num>\323 starting from 0 as the input files.) 314.36 385.64 T +4.1 (Thus, to summarize, unless you\325re mixing and) 342.71 372.64 P +0.2 (matching GOP files from different sources, you can simply) 314.36 361.64 P +1.37 (use the same parameter file for the) 314.36 350.64 P +2 F +3.28 (-gop) 464.63 350.64 P +0 F +1.37 ( and) 488.62 350.64 P +2 F +3.28 (-combi-) 510.78 350.64 P +(ne_gops) 314.36 339.64 T +0 F +( options.) 356.34 339.64 T +0 12 Q +(2.14 Encoding Frames at a T) 314.36 299.31 T +(ime \050command line\051) 452.51 299.31 T +0 10 Q +2.65 (Instead of encoding an entire sequence, you can) 342.71 273.64 P +0.34 (encode individual frames. These frames can later be joined) 314.36 262.64 P +(together to form an MPEG file.) 314.36 251.64 T +(Usage:) 342.71 238.64 T +(-frames first_frame last_frame) 342.71 225.64 T +2.37 (This causes the encoder to encode the numbered) 342.71 212.64 P +(frames in the given range, inclusive.) 314.36 201.64 T +0.89 (The output will be placed in separate files, one per) 342.71 188.64 P +0.21 (frame, with the filenames being the normal output file with) 314.36 177.64 P +(the suffix \322.frame.<frame num>\323) 314.36 166.64 T +(The frame files can later be combined as follows:) 342.71 140.64 T +(Usage:) 342.71 127.64 T +(-combine_frames) 342.71 114.64 T +2.05 (This causes the encoder to simply combine some) 342.71 101.64 P +2.21 (frames into a single MPEG stream. Sequence and GOP) 314.36 90.64 P +1.11 (headers are inserted appropriately. You can either use the) 314.36 79.64 P +1.39 (same parameter file for -frames and -combine_frames, or) 314.36 68.64 P +FMENDPAGE +%%EndPage: "6" 5 +%%Page: "5" 5 +612 792 0 FMBEGINPAGE +57.97 26.65 554.03 40.82 R +7 X +0 K +V +0 10 Q +0 X +(5) 303.5 34.15 T +72 72 297 732.47 R +7 X +V +2 F +0 X +(sflowg.07.yuv) 100.35 725.81 T +(sflowg.08.yuv) 100.35 712.81 T +(sflowg.09.yuv) 100.35 699.81 T +(sflowg.10.yuv) 100.35 686.81 T +0 F +1.37 (If there is no star, then the file name is simple) 100.35 660.81 P +0.99 (repeated the appropriate number of times \050[1-10] is 10) 72 649.81 P +(times\051.) 72 638.81 T +-0.18 (Commands can be used to dynamically create the) 100.35 625.81 P +(list of files, for example:) 72 614.81 T +2 F +(INPUT) 100.35 588.81 T +(\324ls July-*.ppm\324) 100.35 575.81 T +(\324cat file-list\324) 100.35 562.81 T +(END_INPUT) 100.35 549.81 T +0 F +1.41 (The command\050s\051 will be executed in the direc-) 100.35 523.81 P +0.44 (tory named by INPUT_DIR if it appears before INPUT) 72 512.81 P +2.09 (in the parameter file. Note that the encoder-provided) 72 501.81 P +(filling in of *\325s is not supported in this mode.) 72 490.81 T +0.36 (The encoder allows you to use other file formats) 100.35 477.81 P +1.61 (by providing an input conversion specifier. You must) 72 466.81 P +0.18 (describe how to convert the input format into one of the) 72 455.81 P +(base file types.) 72 444.81 T +2 F +(Usage:) 100.35 431.81 T +(INPUT_CONVERT conversion) 100.35 405.81 T +3.25 (conversion) 100.35 379.81 P +0 F +1.35 ( must be a multi-star expression.) 160.31 379.81 P +-0.16 (If) 72 368.81 P +2 F +-0.37 (conversion) 81 368.81 P +0 F +-0.16 ( is simply \324*\325, then no conversion takes) 140.97 368.81 P +0.92 (place. Otherwise, each of the file lines are replaced by) 72 357.81 P +0.3 (the conversion line with the file name wherever there is) 72 346.81 P +0.88 (a \324*\325. The conversion line must send the output to std-) 72 335.81 P +-0.12 (out. For example, suppose we have a bunch of GIF files.) 72 324.81 P +(Then we would do:) 72 313.81 T +2 F +(BASE_FILE_FORMAT) 100.35 300.81 T +( PPM) 196.29 300.81 T +(INPUT) 100.35 287.81 T +(pictures.*.gif [0-10]) 100.35 274.81 T +(END_INPUT) 100.35 261.81 T +(INPUT_CONVERT giftoppm *) 100.35 248.81 T +0 F +0.9 (Another example: Suppose we have separate Y,) 100.35 222.81 P +1.18 (U, and V files \050where the U and V have already been) 72 211.81 P +(subsampled\051. Then we might have:) 72 200.81 T +2 F +(BASE_FILE_FORMAT) 100.35 187.81 T +( YUV) 196.29 187.81 T +(INPUT) 100.35 174.81 T +(pictures.* [0-10]) 100.35 161.81 T +(END_INPUT) 100.35 148.81 T +(INPUT_CONVERT cat *.Y *.U *.V) 100.35 135.81 T +(YUV_FORMAT UCB) 100.35 122.81 T +0 F +0.68 (As you can see, the \322files\323 between) 100.35 109.81 P +2 F +1.63 (INPUT) 249.41 109.81 P +0 F +0.68 ( and) 279.39 109.81 P +2 F +2.41 (END_INPUT) 72 98.81 P +0 F +1 ( don\325t have to be files at all! This can be) 125.97 98.81 P +(very useful.) 72 87.81 T +315 72 540 732.47 R +7 X +V +0 X +(To read data from standard input, set:) 343.35 712.81 T +2 F +(INPUT_DIR stdin) 343.35 699.81 T +0 F +1.05 (Note that you cannot use the stdin option when) 343.35 686.81 P +0.07 (coding in parallel. \050Use GOPINPUTDIR or FRAMEIN-) 315 675.81 P +(PUTDIR if combining frames/GOPs.\051) 315 664.81 T +(The output file is specified by:) 343.35 638.81 T +2 F +(OUTPUT filename) 343.35 625.81 T +0 F +(for example:) 343.35 612.81 T +2 F +(OUTPUT /u/keving/mpg/flowers.mpg) 343.35 599.81 T +0 12 Q +(2.8 Original or Decoded \050parameter \336le\051) 315 559.47 T +0 10 Q +0.09 (The encoder can use either the original frames as) 343.35 532.81 P +3 (reference frames, or the decoded frames. Using the) 315 521.81 P +2.48 (decoded frames gives better playback quality, but is) 315 510.81 P +1.93 (slower and seems to give worse compression. It also) 315 499.81 P +0.63 (causes some complications with parallel encoding. \050see) 315 488.81 P +0.95 (the section on parallel encoding\051 One recommendation) 315 477.81 P +0.52 (is to use original, and lower the q-scale if the quality is) 315 466.81 P +(not good enough. Table six shows the trade-offs.) 315 455.81 T +(Usage:) 343.35 429.81 T +2 F +(REFERENCE_FRAME ORIGINAL) 343.35 403.81 T +0 12 Q +(2.9 Bit-rate Control \050parameter \336le\051) 315 272.47 T +0 10 Q +1.53 (The default encoding uses variable bit rate. To) 343.35 245.81 P +0.33 (limit the bit rate, the MPEG-2 Standard\325s algorithm has) 315 234.81 P +2.39 (been implemented \050suitably adjusted\051. There are two) 315 223.81 P +(parameters which must be set to use bit-rate control:) 315 212.81 T +2 F +(BUFFER_SIZE N \050in bits\051) 343.35 199.81 T +(BIT_RATE M \050in bytes/sec\051) 343.35 186.81 T +0 F +-0.09 (N sets the largest required buffer, M specifies the) 343.35 173.81 P +1 (continual rate. N is set in number of bits, the buffer is) 315 162.81 P +(actually in 16bit ints.) 315 151.81 T +0 12 Q +(2.10 Userdata \050parameter \336le\051) 315 124.47 T +0 10 Q +0.01 (An identification string is added by default to the) 343.35 97.81 P +2.96 (Sequence layer user-data field. It is \322UCB Encoder) 315 86.81 P +1.41 (Vers\323 \050where Vers is replaced by the encoder version) 315 75.81 P +1 12 Q +-0.15 (T) 315 380.47 P +-0.15 (able 6: Original or Decoded? \050Normalized\051) 321.9 380.47 P +0 10 Q +(Reference) 323.62 352.81 T +(Compress) 373.49 358.81 T +(ion) 387.1 346.81 T +(Speed) 429.46 352.81 T +(Quality) 486.21 358.81 T +(I/P/B) 490.65 346.81 T +(Decoded) 329.87 328.81 T +(1000) 383.49 328.81 T +(1000) 431.68 328.81 T +(1000/969/919) 473.44 328.81 T +(Original) 332.08 312.81 T +(885) 385.99 312.81 T +(1373) 431.68 312.81 T +(1000/912/884) 473.44 312.81 T +318.37 370.22 318.37 306.72 2 L +V +0.5 H +0 Z +N +369.39 370.72 369.39 306.22 2 L +V +N +417.58 370.72 417.58 306.22 2 L +V +N +465.77 370.72 465.77 306.22 2 L +V +N +536.63 370.22 536.63 306.72 2 L +V +N +318.12 370.47 536.88 370.47 2 L +V +N +318.62 339.72 536.38 339.72 2 L +V +N +318.62 337.22 536.38 337.22 2 L +V +N +318.12 322.47 536.88 322.47 2 L +V +N +318.12 306.47 536.88 306.47 2 L +V +N +FMENDPAGE +%%EndPage: "5" 4 +%%Page: "4" 4 +612 792 0 FMBEGINPAGE +57.97 26.65 554.03 40.82 R +7 X +0 K +V +0 10 Q +0 X +(4) 303.5 34.15 T +56.69 54.99 297.64 735.31 R +7 X +V +0 X +(A standard sequence is IBBPBBPBBPBBPB) 85.04 715.64 T +(Usage:) 85.04 702.64 T +2 F +(PATTERN) 85.04 676.64 T +( <IPB pattern>) 127.02 676.64 T +0 F +1.84 (Note that if the last frame in an encoding is a B-) 85.04 650.64 P +0.07 (frame, it will not be encoded \050since it has no future frame to) 56.69 639.64 P +1.81 (reference from\051. Pre-I patters like BBIBBP are legal, but) 56.69 628.64 P +0 (seem to have bugs, so watch out! To insure that every frame) 56.69 617.64 P +0.63 (is encoded, the encoder can force the last frame to be an I-) 56.69 606.64 P +(frame.) 56.69 595.64 T +(Usage:) 85.04 582.64 T +2 F +(FORCE_ENCODE_LAST_FRAME) 85.04 556.64 T +0 12 Q +(2.7 Specifying Input Files \050parameter \336le\051) 56.69 516.31 T +0 10 Q +0.03 (The encoder can accept five base types of input files:) 85.04 489.64 P +0.08 (PPM, PNM, JMOVIE, JPEG, and YUV. Note that PPM is a) 56.69 478.64 P +1.38 (subset of PNM; the PPM option is available because it is) 56.69 467.64 P +0.49 (faster to read if the files are known to be PPM. JMOVIE is) 56.69 456.64 P +0.19 (the format created by the Parallax video grabber. JPEGs are) 56.69 445.64 P +-0.06 (a standard image format. YUV formats are described below.) 56.69 434.64 P +0.98 (If you use YUV format, you must specify the pixel) 85.04 421.64 P +0.61 (size of the image in the parameter file and the YUV_FOR-) 56.69 410.64 P +(MAT.) 56.69 399.64 T +(Usage:) 85.04 386.64 T +2 F +(BASE_FILE_FORMAT format) 85.04 360.64 T +(YUV_SIZE widthxheight) 85.04 347.64 T +(YUV_FORMAT yuv_format) 85.04 334.64 T +0.1 (format) 85.04 308.64 P +0 F +0.04 ( is one of {) 121.02 308.64 P +2 F +0.1 (YUV, PPM, PNM, JMOVIE,) 165.4 308.64 P +(JPEG) 56.69 297.64 T +0 F +(}) 80.68 297.64 T +2 F +(width) 85.04 284.64 T +0 F +( and) 115.02 284.64 T +2 F +(height) 134.45 284.64 T +0 F +( are integers \050like 320x240\051) 170.43 284.64 T +2 F +5.81 (yuv_format) 85.04 271.64 P +0 F +2.42 (is one of) 156.81 271.64 P +2 F +5.81 ( {ABEKAS, EYUV,) 196.07 271.64 P +1.66 (PHILLIPS, UCB, {SPECIAL}},) 56.69 260.64 P +0 F +0.69 (where) 223.59 260.64 P +2 F +1.66 ( SPECIAL) 248 260.64 P +0 F +1.83 (is a specification of the pattern of Y, U, and V, such as) 56.69 249.64 P +2 F +-0.57 (UYVY) 56.69 238.64 P +0 F +-0.24 (for) 86.1 238.64 P +2 F +-0.57 ( ABEKAS. The pattern can be of any) 97.76 238.64 P +1.18 (length, or order, but must consist only) 56.69 227.64 P +0.15 (of Ys, Us, andVs, and must represent two) 56.69 216.64 P +3.18 (pixels of data \050thus YUVYUV for 4:4:4) 56.69 205.64 P +(source\051.) 56.69 194.64 T +0 F +1.73 (You must specify the directory in which the input) 85.04 168.64 P +-0.12 (files are located. You can use \324.\325 to specify the current direc-) 56.69 157.64 P +(tory.) 56.69 146.64 T +(Usage:) 85.04 133.64 T +(INPUT_DIR directory) 85.04 107.64 T +1.08 (You must also specify the names of the files them-) 85.04 81.64 P +1.11 (selves. You list them sequentially, one per line, in display) 56.69 70.64 P +2.12 (order. There are shortcuts, however, which allow you to) 56.69 59.64 P +314.36 54.99 552.76 735.31 R +7 X +V +0 X +(condense many files into one line.) 314.36 728.64 T +(Usage:) 342.71 715.64 T +2 F +(INPUT) 342.71 689.64 T +(file) 342.71 676.64 T +2 8 Q +(1) 366.7 674.14 T +2 10 Q +(file) 342.71 663.64 T +2 8 Q +(2) 366.7 661.14 T +2 10 Q +(...) 342.71 650.64 T +(file) 342.71 637.64 T +2 8 Q +(n) 366.7 635.14 T +2 10 Q +(END_INPUT) 342.71 624.64 T +8.17 (file) 342.71 598.64 P +2 8 Q +6.53 (i) 366.7 596.14 P +0 10 Q +3.4 ( can be either a file name, a single-star) 371.49 598.64 P +0.28 (expression followed by a bracketed expansion for star, or a) 314.36 587.64 P +0.28 (command to be executed. There are two types of bracketed) 314.36 576.64 P +(expansions. For example:) 314.36 565.64 T +2 F +(sflowg.*.yuv [0-10]) 342.71 539.64 T +0 F +(is expanded to:) 342.71 513.64 T +2 F +(sflowg.0.yuv) 342.71 487.64 T +(sflowg.1.yuv) 342.71 474.64 T +(sflowg.2.yuv) 342.71 461.64 T +(sflowg.3.yuv) 342.71 448.64 T +(sflowg.4.yuv) 342.71 435.64 T +(sflowg.5.yuv) 342.71 422.64 T +(sflowg.6.yuv) 342.71 409.64 T +(sflowg.7.yuv) 342.71 396.64 T +(sflowg.8.yuv) 342.71 383.64 T +(sflowg.9.yuv) 342.71 370.64 T +(sflowg.10.yuv) 342.71 357.64 T +(sflowg.*.yuv [0-10+3]) 342.71 331.64 T +0 F +(is expanded to:) 342.71 305.64 T +2 F +(sflowg.0.yuv) 342.71 279.64 T +(sflowg.3.yuv) 342.71 266.64 T +(sflowg.6.yuv) 342.71 253.64 T +(sflowg.9.yuv) 342.71 240.64 T +0 F +(Also, the encoder will pad with 0\325s if necessary:) 342.71 214.64 T +2 F +(sflowg.*.yuv [00-10]) 342.71 188.64 T +0 F +(is expanded to:) 342.71 162.64 T +2 F +(sflowg.00.yuv) 342.71 136.64 T +(sflowg.01.yuv) 342.71 123.64 T +(sflowg.02.yuv) 342.71 110.64 T +(sflowg.03.yuv) 342.71 97.64 T +(sflowg.04.yuv) 342.71 84.64 T +(sflowg.05.yuv) 342.71 71.64 T +(sflowg.06.yuv) 342.71 58.64 T +FMENDPAGE +%%EndPage: "4" 3 +%%Page: "3" 3 +612 792 0 FMBEGINPAGE +57.97 26.65 554.03 40.82 R +7 X +0 K +V +0 10 Q +0 X +(3) 303.5 34.15 T +56.69 54.99 297.64 735.31 R +7 X +V +0 X +1.31 (For some reason Simple seems to give better com-) 85.04 616.64 P +(pression, but it depends on the image sequence.) 56.69 605.64 T +(Usage:) 85.04 579.64 T +2 F +(PSEARCH_ALG ptechnique) 85.04 553.64 T +(BSEARCH_ALG btechnique) 85.04 540.64 T +0 F +0.87 (where) 85.04 514.64 P +2 F +2.08 (ptechnique) 112.82 514.64 P +0 F +0.87 (is one of {LOGARITHMIC,) 180.87 514.64 P +(SUBSAMPLE, TWOLEVEL, EXHAUSTIVE}) 56.69 503.64 T +3.14 (where) 85.04 490.64 P +2 F +7.54 (btechnique) 115.1 490.64 P +0 F +3.14 ( is one of {EXHAUSTIVE,) 175.07 490.64 P +(CROSS2, SIMPLE}) 56.69 479.64 T +0 12 Q +(2.3 GOP \050parameter \336le\051) 56.69 439.31 T +0 10 Q +1.94 (A Group of Pictures \050GOP\051 is a roughly indepen-) 85.04 412.64 P +3.02 (dently decodable sequence of frames. An MPEG video) 56.69 401.64 P +0 (stream is made of one or more GOPs. You may specify how) 56.69 390.64 P +0.8 (many frames each GOP should be. A GOP must start with) 56.69 379.64 P +0.2 (an I-frame, and the encoder will enforce that by taking your) 56.69 368.64 P +(number as the) 56.69 357.64 T +3 F +(minimum) 115.26 357.64 T +0 F +( number of frames in a GOP.) 152.46 357.64 T +(Usage:) 85.04 331.64 T +2 F +(GOP_SIZE) 85.04 305.64 T +( num) 133.01 305.64 T +0 F +(where) 85.04 292.64 T +2 F +(num) 111.96 292.64 T +0 F +( = the number of frames in a GOP) 129.95 292.64 T +0 12 Q +(2.4 Slice \050parameter \336le\051) 56.69 252.31 T +0 10 Q +-0.25 (A slice is an independently decodable unit in a frame.) 85.04 226.64 P +0.54 (It can be as small as one macroblock, or it can be as big as) 56.69 215.64 P +1.47 (the entire frame. Barring transmission error, adding slices) 56.69 204.64 P +0.55 (does not change quality or speed; the only effect is slightly) 56.69 193.64 P +-0.1 (worse compression. More slices are used for noisy transmis-) 56.69 182.64 P +2.81 (sion so that errors are more recoverable. Because most) 56.69 171.64 P +0.28 (transmission systems have more sophisticated error correct-) 56.69 160.64 P +(ing routines, we usually just use one slice per frame.) 56.69 149.64 T +(Usage:) 85.04 123.64 T +2 F +(SLICES_PER_FRAME) 85.04 97.64 T +( num) 180.99 97.64 T +0 F +(where) 85.04 84.64 T +2 F +(num) 111.96 84.64 T +0 F +( is the number of slices in a frame) 129.95 84.64 T +2.31 (Note: Some MPEG playback systems require that) 85.04 58.64 P +1 12 Q +(T) 76.29 727.31 T +(able 4: B-frame Motion V) 83.18 727.31 T +(ector Sear) 213.99 727.31 T +(ch) 266.05 727.31 T +(\050Normalized\051) 143.19 713.31 T +0 10 Q +(T) 84.14 691.64 T +(echnique) 89.54 691.64 T +(Compression) 139.45 691.64 T +(Speed) 204.64 691.64 T +(Quality) 242.96 691.64 T +(Exhaustive) 86.23 673.64 T +(1000) 155.83 673.64 T +(?) 214.63 673.64 T +(1000) 247.96 673.64 T +(Cross2) 102.88 657.64 T +(975) 158.33 657.64 T +(1000) 206.86 657.64 T +(996) 250.46 657.64 T +(Simple) 102.32 641.64 T +(938) 158.33 641.64 T +(1765) 206.86 641.64 T +(991) 250.46 641.64 T +75.12 703.06 75.12 635.56 2 L +V +0.5 H +0 Z +N +134.65 703.56 134.65 635.06 2 L +V +N +197.01 703.56 197.01 635.06 2 L +V +N +236.69 703.56 236.69 635.06 2 L +V +N +279.21 703.06 279.21 635.56 2 L +V +N +74.87 703.31 279.46 703.31 2 L +V +N +75.37 684.56 278.96 684.56 2 L +V +N +75.37 682.06 278.96 682.06 2 L +V +N +74.87 667.31 279.46 667.31 2 L +V +N +74.87 651.31 279.46 651.31 2 L +V +N +74.87 635.31 279.46 635.31 2 L +V +N +314.36 54.99 552.76 735.31 R +7 X +V +0 X +1.26 (each slice must consist of whole rows of macroblocks. If) 314.36 728.64 P +0.55 (this is the case, then if the height of the image is H pixels,) 314.36 717.64 P +1.97 (then you should set the SLICES_PER_FRAME to some) 314.36 706.64 P +0.32 (number which divides H/16. For example, if H = 240, then) 314.36 695.64 P +0.82 (you should only use SLICES_PER_FRAME values of 15,) 314.36 684.64 P +(5, 3, or 1.) 314.36 673.64 T +0.3 (Note to the note: these MPEG playback systems are) 342.71 660.64 P +1.07 (really at fault, since the MPEG standard says this doesn\325t) 314.36 649.64 P +(have to be so.) 314.36 638.64 T +0 12 Q +(2.5 Search W) 314.36 611.31 T +(indow \050parameter \336le\051) 378.83 611.31 T +0 10 Q +0.65 (The search window is the window in which motion) 342.71 585.64 P +0.47 (vectors are searched for. The window is a square. You can) 314.36 574.64 P +1.22 (specify the size of the square, and whether to allow half-) 314.36 563.64 P +(pixel motion vectors or not.) 314.36 552.64 T +(Usage:) 342.71 526.64 T +2 F +(PIXEL) 342.71 500.64 T +( <FULL or HALF>) 372.69 500.64 T +(RANGE num [numB]) 342.71 487.64 T +-0.54 (HALF) 342.71 461.64 P +0 F +-0.22 ( means that half-pixel vectors are allowed. The) 366.7 461.64 P +0.19 (search window is +/-) 314.36 450.64 P +2 F +0.47 (num) 401.27 450.64 P +0 F +0.19 ( pixels in the X and Y directions.) 419.26 450.64 P +-0.02 (It is usually important that you use) 314.36 439.64 P +2 F +-0.05 (HALF) 455.54 439.64 P +0 F +-0.02 (, because it results) 479.53 439.64 P +2.12 (in both better quality and better compression. It is only) 314.36 428.64 P +(undesirable for computer-generated images.) 314.36 417.64 T +2 F +-0.25 (num) 342.71 404.64 P +0 F +-0.1 ( should probably be set to at least 8 or 10 pixels.) 360.7 404.64 P +2.35 (This number depends on the image. Using much larger) 314.36 393.64 P +0.68 (numbers such as 20 or 30 doesn\325t seem to help much, and) 314.36 382.64 P +0.02 (increases the CPU cost drastically. The optional numB is in) 314.36 371.64 P +2.67 (case you wish to specify different ranges for predicted) 314.36 360.64 P +3.37 (frames \050P-frames, num\051, and Bi-directional frames \050B-) 314.36 349.64 P +0.31 (frames, numB\051. B-frame limits are optional as indicated by) 314.36 338.64 P +-0 (the braces above \050so \322RANGE 10 6\323 is a valid command as) 314.36 327.64 P +(is \322RANGE 9\323\051.) 314.36 316.64 T +0 12 Q +(2.6 IPB Pattern \050parameter \336le\051) 314.36 276.31 T +0 10 Q +2.24 (You can set the sequence of I, P, and B-frames.) 342.71 249.64 P +0.45 (Later versions will allow you to do more than set a repeat-) 314.36 238.64 P +2.49 (ing IPB pattern. The pattern affects speed, quality, and) 314.36 227.64 P +(compression. Table five shows some of the trade-offs.) 314.36 216.64 T +(\050this is given a certain Q-scale\051) 342.71 59.64 T +1 12 Q +(T) 337.17 193.31 T +(able 5: Comparison of I/P/B-Frames) 344.06 193.31 T +(\050Normalized\051) 399.59 179.31 T +0 F +(Frame) 331.84 156.31 T +(T) 335.26 142.31 T +(ype) 341.74 142.31 T +(Compress) 384.37 156.31 T +(ion) 400.7 142.31 T +(Speed) 451.3 149.31 T +(Quality) 505.57 149.31 T +(I-frames) 334.94 122.31 T +(1000) 396.36 122.31 T +(1000) 453.96 122.31 T +(1000) 511.57 122.31 T +(P-frames) 332.26 104.31 T +(409) 399.36 104.31 T +(601) 456.96 104.31 T +(969) 514.56 104.31 T +(B-frames) 330.93 86.31 T +(72) 402.36 86.31 T +(260) 456.96 86.31 T +(919) 514.56 86.31 T +314.76 169.06 314.76 79.56 2 L +V +N +379.56 169.56 379.56 79.06 2 L +V +N +437.16 169.56 437.16 79.06 2 L +V +N +494.76 169.56 494.76 79.06 2 L +V +N +552.36 169.06 552.36 79.56 2 L +V +N +314.51 169.31 552.61 169.31 2 L +V +N +315.01 134.56 552.11 134.56 2 L +V +N +315.01 132.06 552.11 132.06 2 L +V +N +314.51 115.31 552.61 115.31 2 L +V +N +314.51 97.31 552.61 97.31 2 L +V +N +314.51 79.31 552.61 79.31 2 L +V +N +FMENDPAGE +%%EndPage: "3" 2 +%%Page: "2" 2 +612 792 0 FMBEGINPAGE +57.97 26.65 554.03 40.82 R +7 X +0 K +V +0 10 Q +0 X +(2) 303.5 34.15 T +72 72 297 720 R +7 X +V +0 X +2.84 (Here is a description of the different command-line) 72 700.33 P +0.03 (options available and parameter-file options available in) 72 689.33 P +2.37 (sequential \050one-machine\051 encoding. You should defi-) 72 678.33 P +3.43 (nitely read sections 2.1-2.9. The other sections are) 72 667.33 P +(optional.) 72 656.33 T +0.21 (In the following, whenever a space in the parameter file) 72 630.33 P +4.29 (appears, it can be represented by any amount of) 72 619.33 P +(whitespace \050tabs or spaces\051.) 72 608.33 T +0 12 Q +(2.1 Q-Scale \050parameter \336le\051) 72 581 T +0 10 Q +1.78 (The quantization scale values \050Q-Scale\051 give a) 100.35 554.33 P +1.37 (trade-off between quality and compression. Using dif-) 72 543.33 P +-0.16 (ferent Q-Scale values has very little effect on speed. The) 72 532.33 P +1.74 (Q-Scale values can be set separately for I, P, and B-) 72 521.33 P +(frames.) 72 510.33 T +(Usage:) 100.35 484.33 T +2 F +(IQ-Scale) 126.21 471.33 T +( num) 174.18 471.33 T +(PQ-Scale num) 125.5 458.33 T +(BQ-Scale num) 126.21 445.33 T +0 F +(num in all three cases is a number from 1 to 31.) 100.35 432.33 T +3.47 (Larger numbers give better compression, but) 100.35 406.33 P +0.3 (worse quality. In the following, the quality numbers are) 72 395.33 P +(peak signal-to-noise ratio, defined as:) 72 384.33 T +(where MSE is the mean squared error.) 100.35 332.18 T +0.12 (Tables one and two show the Q-scale vs. Quality) 100.35 306.18 P +(relationship for the flower-garden sequence.) 72 295.18 T +1.84 (Note that when rate-control \050Section 2.9\051 is in) 100.35 282.18 P +0.19 (use, the rate control mechanism will change the Q-scale) 72 271.18 P +1.37 (throughout the blocks of the frame, so these specified) 72 260.18 P +(values are merely starting points.) 72 249.18 T +1 12 Q +(T) 95.77 212.85 T +(able 1: Q-Scale vs. Quality \050SNR\051) 102.66 212.85 T +0 10 Q +(Q-Scale) 91.87 191.18 T +(I-Frames) 140.95 191.18 T +(P-Frames) 190.86 191.18 T +(B-Frames) 241.33 191.18 T +(1) 124.48 173.18 T +(43.2) 150.24 173.18 T +(46.3) 201.27 173.18 T +(46.5) 252.29 173.18 T +(6) 124.48 157.18 T +(32.6) 150.24 157.18 T +(34.6) 201.27 157.18 T +(34.3) 252.29 157.18 T +(1) 119.85 141.18 T +(1) 124.48 141.18 T +(28.6) 150.24 141.18 T +(29.5) 201.27 141.18 T +(30.0) 252.29 141.18 T +(16) 119.48 125.18 T +(26.3) 150.24 125.18 T +(26.8) 201.27 125.18 T +(28.6) 252.29 125.18 T +(21) 119.48 109.18 T +(24.7) 150.24 109.18 T +(25.0) 201.27 109.18 T +(27.9) 252.29 109.18 T +(26) 119.48 93.18 T +(23.5) 150.24 93.18 T +(23.9) 201.27 93.18 T +(27.5) 252.29 93.18 T +82.45 202.6 82.45 87.1 2 L +V +0.5 H +0 Z +N +133.48 203.1 133.48 86.6 2 L +V +N +184.5 203.1 184.5 86.6 2 L +V +N +235.52 203.1 235.52 86.6 2 L +V +N +286.55 202.6 286.55 87.1 2 L +V +N +82.2 202.85 286.8 202.85 2 L +V +N +82.7 184.1 286.3 184.1 2 L +V +N +82.7 181.6 286.3 181.6 2 L +V +N +82.2 166.85 286.8 166.85 2 L +V +N +82.2 150.85 286.8 150.85 2 L +V +N +82.2 134.85 286.8 134.85 2 L +V +N +82.2 118.85 286.8 118.85 2 L +V +N +82.2 102.85 286.8 102.85 2 L +V +N +82.2 86.85 286.8 86.85 2 L +V +N +72 72 297 720 C +72 341.85 297 381 C +0 12 Q +0 X +0 K +(2) 148.86 360.39 T +(0) 154.85 360.39 T +3 F +(l) 161.56 360.39 T +(o) 165.6 360.39 T +(g) 172.3 360.39 T +0 9 Q +(1) 178.76 356.63 T +(0) 183.25 356.63 T +0 12 Q +(2) 196.32 367.58 T +(5) 202.32 367.58 T +(5) 208.31 367.58 T +3 F +(M) 196.45 349.78 T +(S) 207.15 349.78 T +(E) 213.85 349.78 T +221.18 360.98 196.45 360.98 2 L +0.33 H +0 Z +N +196.45 360.98 193.45 347.78 2 L +N +193.45 347.78 191.45 351.58 2 L +N +191.45 351.58 190.45 349.68 2 L +N +189.45 362.98 220.93 362.98 2 L +N +72 72 297 720 C +0 0 612 792 C +315 72 540 720 R +7 X +0 K +V +0 12 Q +0 X +(2.2 Search T) 315 471 T +(echniques \050parameter \336le\051) 375.11 471 T +0 10 Q +0.77 (There are several different motion vector search) 343.35 445.33 P +2.44 (techniques available for both P-frame search and B-) 315 434.33 P +0.54 (frame search. Using different search techniques present) 315 423.33 P +-0.04 (little difference in quality, but a large difference in com-) 315 412.33 P +(pression and speed.) 315 401.33 T +0.26 (There are 4 types of P-frame search: Exhaustive,) 343.35 388.33 P +(TwoLevel, SubSample, and Logarithmic.) 315 377.33 T +0.1 (There are 3 types of B-frame search: Exhaustive,) 343.35 364.33 P +(Cross2, and Simple.) 315 353.33 T +1.53 (The suggested search techniques are TwoLevel) 343.35 340.33 P +2.17 (and Logarithmic for P-frame search, and Cross2 and) 315 329.33 P +0.68 (Simple for B-frame search. Tables three and four com-) 315 318.33 P +(pare the different search methods:) 315 307.33 T +318.37 170 536.63 176 C +0 0 612 792 C +0 10 Q +0 X +0 K +(a. Smaller numbers mean better compression) 336.37 163.33 T +(b. Larger numbers mean faster execution) 336.37 151.33 T +(c. Larger numbers mean better quality) 336.37 139.33 T +(31) 362.48 672.33 T +(22.6) 393.24 672.33 T +(23.0) 444.27 672.33 T +(27.3) 495.29 672.33 T +1 12 Q +(T) 342.04 646 T +(able 2: Q-Scale vs. Compr) 348.94 646 T +(ession) 482.3 646 T +0 10 Q +(Q-Scale) 334.87 624.33 T +(I-Frames) 383.95 624.33 T +(P-Frames) 433.86 624.33 T +(B-Frames) 484.33 624.33 T +(1) 367.48 606.33 T +(2:1) 395.6 606.33 T +(2:1) 446.62 606.33 T +(2:1) 497.65 606.33 T +(6) 367.48 590.33 T +(7) 399.49 590.33 T +(10) 448.01 590.33 T +(15) 499.04 590.33 T +(1) 362.85 574.33 T +(1) 367.48 574.33 T +(1) 397.18 574.33 T +(1) 401.8 574.33 T +(18) 448.01 574.33 T +(43) 499.04 574.33 T +(16) 362.48 558.33 T +(15) 396.99 558.33 T +(29) 448.01 558.33 T +(97) 499.04 558.33 T +(21) 362.48 542.33 T +(19) 396.99 542.33 T +(41) 448.01 542.33 T +(173) 496.54 542.33 T +(26) 362.48 526.33 T +(24) 396.99 526.33 T +(56) 448.01 526.33 T +(256) 496.54 526.33 T +(31) 362.48 510.33 T +(28) 396.99 510.33 T +(73) 448.01 510.33 T +(330) 496.54 510.33 T +1 12 Q +(T) 326.96 284 T +(able 3: P-frame Motion V) 333.86 284 T +(ector Sear) 463.98 284 T +(ch) 516.05 284 T +(\050Normalized\051) 393.53 270 T +0 10 Q +(T) 327.39 248.33 T +(echnique) 332.79 248.33 T +(Compression) 382.34 248.33 T +0 8 Q +(a) 435.09 252.33 T +0 10 Q +(Speed) 454.39 248.33 T +0 8 Q +(b) 478.82 252.33 T +0 10 Q +(Quality) 498.61 248.33 T +0 8 Q +(c) 528.59 252.33 T +0 10 Q +(Exhaustive) 329.48 230.33 T +(1000) 400.5 230.33 T +(1000) 458.61 230.33 T +(1000) 505.38 230.33 T +(SubSample) 328.36 214.33 T +(1008) 400.5 214.33 T +(2456) 458.61 214.33 T +(1000) 505.38 214.33 T +(T) 333.52 198.33 T +(woLevel) 338.92 198.33 T +(1009) 400.5 198.33 T +(3237) 458.61 198.33 T +(1000) 505.38 198.33 T +(Logarithmic) 324.48 182.33 T +(1085) 400.5 182.33 T +(8229) 458.61 182.33 T +(998) 507.88 182.33 T +1 12 Q +(T) 338.77 712 T +(able 1: Q-Scale vs. Quality \050SNR\051) 345.66 712 T +0 10 Q +(Q-Scale) 334.87 690.33 T +(I-Frames) 383.95 690.33 T +(P-Frames) 433.86 690.33 T +(B-Frames) 484.33 690.33 T +325.45 701.75 325.45 666.25 2 L +V +0.5 H +0 Z +N +376.48 702.25 376.48 665.75 2 L +V +N +427.5 702.25 427.5 665.75 2 L +V +N +478.52 702.25 478.52 665.75 2 L +V +N +529.55 701.75 529.55 666.25 2 L +V +N +325.2 702 529.8 702 2 L +V +N +325.7 683.25 529.3 683.25 2 L +V +N +325.7 680.75 529.3 680.75 2 L +V +N +325.2 666 529.8 666 2 L +V +N +325.45 635.75 325.45 504.25 2 L +V +N +376.48 636.25 376.48 503.75 2 L +V +N +427.5 636.25 427.5 503.75 2 L +V +N +478.52 636.25 478.52 503.75 2 L +V +N +529.55 635.75 529.55 504.25 2 L +V +N +325.2 636 529.8 636 2 L +V +N +325.7 617.25 529.3 617.25 2 L +V +N +325.7 614.75 529.3 614.75 2 L +V +N +325.2 600 529.8 600 2 L +V +N +325.2 584 529.8 584 2 L +V +N +325.2 568 529.8 568 2 L +V +N +325.2 552 529.8 552 2 L +V +N +325.2 536 529.8 536 2 L +V +N +325.2 520 529.8 520 2 L +V +N +325.2 504 529.8 504 2 L +V +N +318.37 259.75 318.37 176.25 2 L +V +N +377.89 260.25 377.89 175.75 2 L +V +N +443.09 260.25 443.09 175.75 2 L +V +N +494.11 260.25 494.11 175.75 2 L +V +N +536.63 259.75 536.63 176.25 2 L +V +N +318.12 260 536.88 260 2 L +V +N +318.62 241.25 536.38 241.25 2 L +V +N +318.62 238.75 536.38 238.75 2 L +V +N +318.12 224 536.88 224 2 L +V +N +318.12 208 536.88 208 2 L +V +N +318.12 192 536.88 192 2 L +V +N +318.12 176 536.88 176 2 L +V +N +FMENDPAGE +%%EndPage: "2" 1 +%%Page: "1" 1 +612 792 0 FMBEGINPAGE +56.69 111.69 297.64 565.23 R +7 X +0 K +V +1 12 Q +0 X +(Contents) 56.69 557.23 T +0 10 Q +(0. Contacts/History) 56.69 539.56 T +(1. Installation) 56.69 526.56 T +(2. Sequential Usage) 56.69 513.56 T +(2.1 Q-Scale) 85.04 500.56 T +(2.2 Search Techniques) 85.04 487.56 T +(2.3 GOP) 85.04 474.56 T +(2.4 Slices) 85.04 461.56 T +(2.5) 85.04 448.56 T +( Search window) 97.53 448.56 T +(2.6 IPB pattern) 85.04 435.56 T +(2.7 Specifying Input Files) 85.04 422.56 T +(2.8) 85.04 409.56 T +( Original or Decoded) 97.53 409.56 T +(2.9 Bit-rate Control) 85.04 396.56 T +(2.10 User-data) 85.04 383.56 T +(2.11 Compression Decision List \050CDL\051) 85.04 370.56 T +(2.12 Gamma Correction) 85.04 357.56 T +(2.13 Encoding GOPs at a time) 85.04 344.56 T +(2.14) 85.04 331.56 T +( Encoding Frames at a time) 102.53 331.56 T +(2.15) 85.04 318.56 T +( Stats and other info) 102.53 318.56 T +(3. Parallel Usage) 56.69 305.56 T +(3.1) 85.04 292.56 T +( Architecture) 97.53 292.56 T +(3.2 Specifying slave machines) 85.04 279.56 T +(3.3) 85.04 266.56 T +( Remote Shell) 97.53 266.56 T +(3.4 Scheduling Algorithms) 85.04 253.56 T +(3.5 Parallel Problems) 85.04 240.56 T +0 12 Q +(4. Performance) 56.69 226.23 T +0 10 Q +(5. Other Options) 56.69 213.56 T +(5.1) 85.04 200.56 T +( Custom Quantization Tables) 97.53 200.56 T +(5.2 Aspect Ratio) 85.04 187.56 T +(5.3) 85.04 174.56 T +( Frame Rate) 97.53 174.56 T +(6. Other Tools) 56.69 161.56 T +(6.1) 85.04 148.56 T +(ppmtoeyuv) 100.03 148.56 T +(6.2 jmovie2jpeg) 85.04 135.56 T +(6.3 movieToVid) 85.04 122.56 T +314.36 54.99 552.76 565.23 R +7 X +V +0 X +(6.4) 342.71 558.56 T +(yuvtojpeg) 357.7 558.56 T +(6.5) 342.71 545.56 T +( blockrun) 355.2 545.56 T +(6.6) 342.71 532.56 T +( vidtoppm) 355.2 532.56 T +(6.7 vidtojpeg) 342.71 519.56 T +(6.8 vidtoeyuv) 342.71 506.56 T +(7. Frequently Asked Questions) 314.36 493.56 T +(7.1 Questions) 342.71 480.56 T +(7.2 Answers) 342.71 467.56 T +1 12 Q +(0. Contacts/History) 314.36 432.23 T +0 10 Q +2.56 (The Berkeley MPEG encoder was written by Kevin L.) 314.36 414.56 P +0.59 (Gong in the Berkeley Plateau Multimedia Research group,) 314.36 403.56 P +2.29 (headed by Professor Lawrence Rowe. It has since been) 314.36 392.56 P +2.69 (modified by Stephen Smoot, Eugene Hung, and Darryl) 314.36 381.56 P +0.69 (Brown. Please use the following e-mail addresses to reach) 314.36 370.56 P +(us:) 314.36 359.56 T +-0.23 (mpeg-bugs@plateau.cs.berkeley.edu \050bug reports and ques-) 314.36 346.56 P +(tions\051 larry@cs.berkeley.edu \050research funding!\051) 314.36 335.56 T +1 12 Q +(1. Installation) 314.36 300.23 T +0 10 Q +(To install, read the directions in doc/INSTALL.) 342.71 282.56 T +1.32 (Note that the) 342.71 269.56 P +2 F +3.17 (bin/) 400.8 269.56 P +0 F +1.32 ( directory contains binaries for) 424.79 269.56 P +0.35 (several different platforms. The program has been success-) 314.36 258.56 P +(fully ported to the following platforms:) 314.36 247.56 T +(SunOS 4.x) 342.71 234.56 T +(DEC Alpha running OSF1) 342.71 221.56 T +(DECstation 5000 running Ultrix) 342.71 208.56 T +(HP 9000 series) 342.71 195.56 T +-0.06 (If you are successful in porting to a new platform, or) 342.71 169.56 P +0.15 (have problems installing, please let us know \050at the address) 314.36 158.56 P +(above\051.) 314.36 147.56 T +1 12 Q +(2. Sequential Usage) 314.36 112.23 T +0 10 Q +(The encoder is invoked in the following manner:) 314.36 81.56 T +1 F +(mpeg_encode) 352.7 68.56 T +0 F +( <options> parameter_file) 410.44 68.56 T +56.69 570.9 552.76 735.31 R +7 X +V +1 18 Q +0 X +(Berkeley MPEG-1 V) 174.89 723.31 T +(ideo Encoder) 333.11 723.31 T +290.64 685.53 254.68 685.53 2 L +V +1.71 H +0 Z +N +(User) 254.68 687.31 T +296.95 685.53 290.96 685.53 2 L +V +N +(\325) 290.96 687.31 T +354.77 685.53 296.29 685.53 2 L +V +N +(s Guide) 296.29 687.31 T +3 12 Q +(Plateau Resear) 245.54 657.31 T +(ch Gr) 318.71 657.31 T +(oup) 345.92 657.31 T +(Computer Science Division) 239.1 643.31 T +(University of California) 247.08 629.31 T +(Berkeley) 239.59 615.31 T +(, California 94720) 280.89 615.31 T +(mpeg-bugs@cs.berkeley) 236.26 596.31 T +(.edu) 352.87 596.31 T +56.69 54.99 297.64 104.6 R +7 X +V +56.69 94.59 297.64 104.6 C +56.69 94.59 297.64 104.6 R +7 X +0 K +V +56.69 103.59 525.7 103.59 2 L +V +1 H +2 Z +0 X +N +0 0 612 792 C +0 9 Q +0 X +0 K +(Last Updated: 30 January 1995) 56.69 88.59 T +FMENDPAGE +%%EndPage: "1" 0 +%%Trailer +%%BoundingBox: 0 0 612 792 +%%Pages: 11 -1 +%%DocumentFonts: Times-Roman +%%+ Times-Bold +%%+ Courier +%%+ Times-Italic diff --git a/converter/ppm/ppmtompeg/examples/basicspeed.param b/converter/ppm/ppmtompeg/examples/basicspeed.param new file mode 100644 index 00000000..94b03de7 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/basicspeed.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/speed.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR /n/picasso/users/keving/encode/input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/decoded.test b/converter/ppm/ppmtompeg/examples/decoded.test new file mode 100644 index 00000000..7496524c --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/decoded.test @@ -0,0 +1,81 @@ +# speed test parameter file + +PATTERN ibpbpb +OUTPUT /n/picasso/users/keving/encode/output/flowgard.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT cat *.Y *.U *.V + +INPUT_DIR /n/picasso/users/keving/links/flowg + +INPUT +sflowg.* [0-6] +END_INPUT + +# the following two are optional (default = 10, 60) + +# number of frames to do initially to gauge speed of machine +PARALLEL_TEST_FRAMES 3 + +# number of seconds per chunk thereafter +PARALLEL_TIME_CHUNKS 30 + + +PARALLEL +# lines must be of form "machine <whitespace> username <whitespace> executable" +# these guys are sorta slow: +#elmer-fudd keving ~keving/encode/bin/sun/mpeg_encode +#zonker keving ~keving/encode/bin/sun/mpeg_encode +#roger-rabbit keving ~keving/encode/bin/sun/mpeg_encode +#tweety keving ~keving/encode/bin/sun/mpeg_encode +#mickey keving ~keving/encode/bin/mickey/mpeg_encode +# +# these guys are pretty fast: +# +#big-bird keving ~keving/encode/bin/hp/mpeg_encode +#gumby keving ~keving/encode/bin/hp/mpeg_encode +#charlie-brown keving ~keving/encode/bin/dec-alpha/mpeg_encode +#woodstock keving ~keving/encode/bin/dec-alpha/mpeg_encode +#bugs-bunny keving ~keving/encode/bin/sun/mpeg_encode +# +# remotes +# +#REMOTE anaconda keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE adder keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE moccasin keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE cobra keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE boa keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE asp keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE rattler keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE viper keving ~keving/mpeg_encode ~keving/parallel.test +# mamba doesn't seem to work for whatever reason...don't know why +#REMOTE mamba keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE cse.lbl.gov kevin ~kevin/mpeg_encode ~kevin/parallel.test +#REMOTE roger-rabbit keving ~keving/src/encode/bin/sun/mpeg_encode ~keving/src/encode/ParamExamples/parallel.test +END_PARALLEL + + +# motion vector search parameters + +# MAD or MSE -- must be upper case +ERROR MAD + +# FULL or HALF -- must be upper case +PIXEL FULL + +# means +/- this many pixels +RANGE 8 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG SIMPLE + +IQSCALE 1 +PQSCALE 1 +BQSCALE 25 + +REFERENCE_FRAME DECODED +#REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/decoded2.test b/converter/ppm/ppmtompeg/examples/decoded2.test new file mode 100644 index 00000000..b5e9a865 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/decoded2.test @@ -0,0 +1,81 @@ +# speed test parameter file + +PATTERN ibbbpbbbbbp +OUTPUT /tmp/ts.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PPM +YUV_SIZE 512x512 + +INPUT_CONVERT giftoppm * + +INPUT_DIR ../test/ts + +INPUT +med*.gif [030-039] +END_INPUT + +# the following two are optional (default = 10, 60) + +# number of frames to do initially to gauge speed of machine +PARALLEL_TEST_FRAMES 3 + +# number of seconds per chunk thereafter +PARALLEL_TIME_CHUNKS 30 + + +PARALLEL +# lines must be of form "machine <whitespace> username <whitespace> executable" +# these guys are sorta slow: +#elmer-fudd keving ~keving/encode/bin/sun/mpeg_encode +#zonker keving ~keving/encode/bin/sun/mpeg_encode +#roger-rabbit keving ~keving/encode/bin/sun/mpeg_encode +#tweety keving ~keving/encode/bin/sun/mpeg_encode +#mickey keving ~keving/encode/bin/mickey/mpeg_encode +# +# these guys are pretty fast: +# +#big-bird keving ~keving/encode/bin/hp/mpeg_encode +#gumby keving ~keving/encode/bin/hp/mpeg_encode +#charlie-brown keving ~keving/encode/bin/dec-alpha/mpeg_encode +#woodstock keving ~keving/encode/bin/dec-alpha/mpeg_encode +#bugs-bunny keving ~keving/encode/bin/sun/mpeg_encode +# +# remotes +# +#REMOTE anaconda keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE adder keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE moccasin keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE cobra keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE boa keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE asp keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE rattler keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE viper keving ~keving/mpeg_encode ~keving/parallel.test +# mamba doesn't seem to work for whatever reason...don't know why +#REMOTE mamba keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE cse.lbl.gov kevin ~kevin/mpeg_encode ~kevin/parallel.test +#REMOTE roger-rabbit keving ~keving/src/encode/bin/sun/mpeg_encode ~keving/src/encode/ParamExamples/parallel.test +END_PARALLEL + + +# motion vector search parameters + +# MAD or MSE -- must be upper case +ERROR MAD + +# FULL or HALF -- must be upper case +PIXEL FULL + +# means +/- this many pixels +RANGE 8 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG SIMPLE + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +REFERENCE_FRAME DECODED +#REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/decorig.param b/converter/ppm/ppmtompeg/examples/decorig.param new file mode 100644 index 00000000..3c570bcd --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/decorig.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBP + +OUTPUT /n/picasso/users/keving/encode/output/flowgard.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-3] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/default.param b/converter/ppm/ppmtompeg/examples/default.param new file mode 100644 index 00000000..17d8fe5d --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/default.param @@ -0,0 +1,34 @@ +# parameter file with good default values +# +# use this as a guideline for any parameters you don't really understand +# or don't care about +# + +PATTERN IBBPBBPBBPBBPBB +OUTPUT output.mpg + +BASE_FILE_FORMAT YUV +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +PIXEL HALF +RANGE 10 +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +REFERENCE_FRAME ORIGINAL + +# +# you really need to understand the following +# +YUV_SIZE 352x240 +INPUT_CONVERT * + +INPUT_DIR input/flowg + +INPUT +sflowg.*.yuv [0-23] +END_INPUT diff --git a/converter/ppm/ppmtompeg/examples/fastspeed.param b/converter/ppm/ppmtompeg/examples/fastspeed.param new file mode 100644 index 00000000..c45e0341 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/fastspeed.param @@ -0,0 +1,46 @@ +# speed test parameter file + +PATTERN ibbpbbpbbpbbpbb +OUTPUT /n/picasso/users/keving/encode/output/flowgard.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT cat *.Y *.U *.V + +INPUT_DIR /n/picasso/project/mm/mpeg/mpeg_encode/input/flowg + +INPUT +sflowg.* [0-149] +END_INPUT + +PARALLEL_TEST_FRAMES 3 +PARALLEL_TIME_CHUNKS 15 + +PARALLEL +charlie-brown keving ~keving/encode/bin/dec-alpha/mpeg_encode +woodstock keving ~keving/encode/bin/dec-alpha/mpeg_encode +END_PARALLEL + +# motion vector search parameters + +# MAD or MSE -- must be upper case +ERROR MAD + +# FULL or HALF -- must be upper case +PIXEL FULL + +# means +/- this many pixels +RANGE 8 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG SIMPLE + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +REFERENCE_FRAME ORIGINAL + diff --git a/converter/ppm/ppmtompeg/examples/foobar.param b/converter/ppm/ppmtompeg/examples/foobar.param new file mode 100644 index 00000000..1ba6f029 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/foobar.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard20.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [000-027] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL FULL + +RANGE 10 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/frame.test b/converter/ppm/ppmtompeg/examples/frame.test new file mode 100644 index 00000000..fbefb8c4 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/frame.test @@ -0,0 +1,37 @@ +# speed test parameter file + +PATTERN IBPB +OUTPUT output/food +GOP_SIZE 20 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT cat *.Y *.U *.V + +INPUT_DIR links/flowg + +INPUT +sflowg.* [0-4] +END_INPUT + + +# motion vector search parameters + +# MAD or MSE -- must be upper case +ERROR MAD + +# FULL or HALF -- must be upper case +PIXEL FULL + +# means +/- this many pixels +RANGE 8 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG SIMPLE + +IQSCALE 10 +PQSCALE 31 +BQSCALE 31 + diff --git a/converter/ppm/ppmtompeg/examples/gop.combine b/converter/ppm/ppmtompeg/examples/gop.combine new file mode 100644 index 00000000..6b5c3e89 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/gop.combine @@ -0,0 +1,11 @@ +# speed test parameter file + +OUTPUT output/gop.mpg + +YUV_SIZE 352x240 + +INPUT_DIR . + +INPUT +output/food.gop.* [0-2] +END_INPUT diff --git a/converter/ppm/ppmtompeg/examples/gop.test b/converter/ppm/ppmtompeg/examples/gop.test new file mode 100644 index 00000000..c2ae346b --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/gop.test @@ -0,0 +1,43 @@ +# speed test parameter file + +PATTERN IBPBP + +OUTPUT output/food +GOP_SIZE 5 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT cat *.Y *.U *.V + +INPUT_DIR links/flowg + +INPUT +sflowg.* [0-14] +END_INPUT + + +GOP_INPUT_DIR output +GOP_INPUT +food.gop.* [1-2] +GOP_END_INPUT + +# motion vector search parameters + +# MAD or MSE -- must be upper case +ERROR MAD + +# FULL or HALF -- must be upper case +PIXEL FULL + +# means +/- this many pixels +RANGE 8 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG SIMPLE + +IQSCALE 10 +PQSCALE 31 +BQSCALE 31 + diff --git a/converter/ppm/ppmtompeg/examples/gop1.param b/converter/ppm/ppmtompeg/examples/gop1.param new file mode 100644 index 00000000..c89a5b9a --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/gop1.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/flowgard.mpg +GOP_SIZE 6 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/hello.param b/converter/ppm/ppmtompeg/examples/hello.param new file mode 100644 index 00000000..579b01ed --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/hello.param @@ -0,0 +1,60 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/hello.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR /n/picasso/users/keving/encode/input/flowg + +INPUT +sflowg.*.yuv [0-49] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL FULL + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG SIMPLE + +#REFERENCE_FRAME DECODED +REFERENCE_FRAME ORIGINAL + +PARALLEL_TEST_FRAMES 3 + +# number of seconds per chunk thereafter +PARALLEL_TIME_CHUNKS 30 + +PARALLEL +#big-bird keving ~keving/encode/bin/hp/mpeg_encode +#gumby keving ~keving/encode/bin/hp/mpeg_encode +charlie-brown keving ~keving/encode/bin/dec-alpha/mpeg_encode +#woodstock keving ~keving/encode/bin/dec-alpha/mpeg_encode +#REMOTE cory keving ~keving/bin/mpeg_encode ~keving/hello.param +REMOTE scorpius.EECS.Berkeley.EDU keving ~keving/bin/mpeg_encode ~keving/hello.param +REMOTE monoceros.EECS.Berkeley.EDU keving ~keving/bin/mpeg_encode ~keving/hello.param +REMOTE lepus.EECS.Berkeley.EDU keving ~keving/bin/mpeg_encode ~keving/hello.param +REMOTE fornax.EECS.Berkeley.EDU keving ~keving/bin/mpeg_encode ~keving/hello.param +REMOTE delphinus.EECS.Berkeley.EDU keving ~keving/bin/mpeg_encode ~keving/hello.param +REMOTE cephus.EECS.Berkeley.EDU keving ~keving/bin/mpeg_encode ~keving/hello.param +REMOTE carina.EECS.Berkeley.EDU keving ~keving/bin/mpeg_encode ~keving/hello.param +REMOTE bootes.EECS.Berkeley.EDU keving ~keving/bin/mpeg_encode ~keving/hello.param +REMOTE aquila.EECS.Berkeley.EDU keving ~keving/bin/mpeg_encode ~keving/hello.param +# +END_PARALLEL + +# FORCE_I_ALIGN diff --git a/converter/ppm/ppmtompeg/examples/i.test b/converter/ppm/ppmtompeg/examples/i.test new file mode 100644 index 00000000..af62afae --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/i.test @@ -0,0 +1,37 @@ +# speed test parameter file + +PATTERN I +OUTPUT output/food +GOP_SIZE 20 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT cat *.Y *.U *.V + +INPUT_DIR links/flowg + +INPUT +sflowg.* [0-5] +END_INPUT + + +# motion vector search parameters + +# MAD or MSE -- must be upper case +ERROR MAD + +# FULL or HALF -- must be upper case +PIXEL FULL + +# means +/- this many pixels +RANGE 8 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG SIMPLE + +IQSCALE 10 +PQSCALE 31 +BQSCALE 31 + diff --git a/converter/ppm/ppmtompeg/examples/ispeed.jpeg.param b/converter/ppm/ppmtompeg/examples/ispeed.jpeg.param new file mode 100644 index 00000000..ce3d4ed3 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/ispeed.jpeg.param @@ -0,0 +1,48 @@ +# speed test parameter file + +PATTERN I +OUTPUT /n/picasso/users/keving/encode/output/flowgard1.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT djpeg * + +INPUT_DIR /n/picasso/users/keving/encode/input/flowg/jpeg + +INPUT +sflowg.*.jpeg [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL FULL + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME ORIGINAL + + +PARALLEL_TEST_FRAMES 3 +PARALLEL_TIME_CHUNKS 30 + +PARALLEL +big-bird keving ~keving/encode/bin/hp/mpeg_encode +gumby keving ~keving/encode/bin/hp/mpeg_encode +charlie-brown keving ~keving/encode/bin/dec-alpha/mpeg_encode +woodstock keving ~keving/encode/bin/dec-alpha/mpeg_encode +END_PARALLEL + +IO_SERVER_CONVERT * +SLAVE_CONVERT djpeg * diff --git a/converter/ppm/ppmtompeg/examples/ispeed.param b/converter/ppm/ppmtompeg/examples/ispeed.param new file mode 100644 index 00000000..01381e85 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/ispeed.param @@ -0,0 +1,45 @@ +# speed test parameter file + +PATTERN I +OUTPUT /n/picasso/users/keving/encode/output/flowgard1.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR /n/picasso/users/keving/encode/input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL FULL + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME ORIGINAL + + +PARALLEL_TEST_FRAMES 3 +PARALLEL_TIME_CHUNKS 30 + +PARALLEL +big-bird keving ~keving/encode/bin/hp/mpeg_encode +gumby keving ~keving/encode/bin/hp/mpeg_encode +charlie-brown keving ~keving/encode/bin/dec-alpha/mpeg_encode +woodstock keving ~keving/encode/bin/dec-alpha/mpeg_encode +END_PARALLEL diff --git a/converter/ppm/ppmtompeg/examples/jpeg.test b/converter/ppm/ppmtompeg/examples/jpeg.test new file mode 100644 index 00000000..75e3eb8f --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/jpeg.test @@ -0,0 +1,66 @@ +# speed test parameter file + +PATTERN ibbpbbpbbpbbpbb +OUTPUT /n/picasso/users/keving/encode/output/flowgard.jpeg.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PPM +YUV_SIZE 352x240 + +INPUT_CONVERT djpeg * + +INPUT_DIR /n/picasso/project/mm/mpeg/mpeg_encode/input/flowg/jpeg + +INPUT +sflowg.*.jpeg [1-20] +END_INPUT + +# the following two are optional (default = 10, 60) + +# number of frames to do initially to gauge speed of machine +PARALLEL_TEST_FRAMES 3 + +# number of seconds per chunk thereafter +PARALLEL_TIME_CHUNKS 30 + +# specifies what the IO server must do before transmitting to remote sites +IO_SERVER_CONVERT * + +# specifies what the remote slave must do after receiving from IO server +SLAVE_CONVERT djpeg * + +PARALLEL +# lines must be of form "machine <whitespace> username <whitespace> executable" +# these guys are sorta slow: +#elmer-fudd keving ~keving/encode/bin/sun/mpeg_encode +#roger-rabbit keving ~keving/encode/bin/sun/mpeg_encode +#tweety keving ~keving/encode/bin/sun/mpeg_encode +#mickey keving ~keving/encode/bin/mickey/mpeg_encode +# +# these guys are pretty fast: +# +#big-bird keving ~keving/encode/bin/hp/mpeg_encode +#gumby keving ~keving/encode/bin/hp/mpeg_encode +charlie-brown keving ~keving/encode/bin/dec-alpha/mpeg_encode +woodstock keving ~keving/encode/bin/dec-alpha/mpeg_encode +#bugs-bunny keving ~keving/encode/bin/sun/mpeg_encode +# +# remotes +# +END_PARALLEL + + +# motion vector search parameters + +PIXEL HALF +RANGE 8 +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG SIMPLE + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +REFERENCE_FRAME ORIGINAL + diff --git a/converter/ppm/ppmtompeg/examples/jpeg19.param b/converter/ppm/ppmtompeg/examples/jpeg19.param new file mode 100644 index 00000000..15013895 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/jpeg19.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/flowgard.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT JPEG +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg/jpeg + +INPUT +sflowg.*.jpeg [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/jpegout19.param b/converter/ppm/ppmtompeg/examples/jpegout19.param new file mode 100644 index 00000000..d31383d3 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/jpegout19.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/flowgard.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PPM +YUV_SIZE 352x240 + +INPUT_CONVERT djpeg * + +INPUT_DIR ../input/flowg/jpeg + +INPUT +sflowg.*.jpeg [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/localdisk.param b/converter/ppm/ppmtompeg/examples/localdisk.param new file mode 100644 index 00000000..e7f49f33 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/localdisk.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN I +OUTPUT /usr/tmp/flowgard.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR /usr/tmp + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL FULL + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/localremote.test b/converter/ppm/ppmtompeg/examples/localremote.test new file mode 100644 index 00000000..6c91721f --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/localremote.test @@ -0,0 +1,80 @@ +# speed test parameter file + +#PATTERN ibbpbbpbbpbbpbb +PATTERN iiiiiiii +OUTPUT /n/picasso/users/keving/src/encode/output/good +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT cat *.Y *.U *.V + +INPUT_DIR /n/picasso/users/keving/links/flowg + +INPUT +sflowg.* [0-149] +END_INPUT + +# the following two are optional (default = 10, 60) + +# number of frames to do initially to gauge speed of machine +PARALLEL_TEST_FRAMES 3 + +# number of seconds per chunk thereafter +PARALLEL_TIME_CHUNKS 30 + + +PARALLEL +# lines must be of form "machine <whitespace> username <whitespace> executable" +# these guys are sorta slow: +#REMOTE charlie-brown keving ~keving/src/encode/bin/dec/mpeg_encode ~keving/src/encode/ParamExamples/localremote.test +#REMOTE woodstock keving ~keving/src/encode/bin/dec/mpeg_encode ~keving/src/encode/ParamExamples/localremote.test +#REMOTE gumby keving ~keving/src/encode/bin/hp/mpeg_encode ~keving/src/encode/ParamExamples/localremote.test +#REMOTE big-bird keving ~keving/src/encode/bin/hp/mpeg_encode ~keving/src/encode/ParamExamples/localremote.test +#REMOTE elmer-fudd keving ~keving/src/encode/bin/sun/mpeg_encode ~keving/src/encode/ParamExamples/localremote.test +#REMOTE mickey keving ~keving/src/encode/bin/mickey/mpeg_encode ~keving/src/encode/ParamExamples/localremote.test +#zonker keving ~keving/src/encode/bin/sun/mpeg_encode +#roger-rabbit keving ~keving/src/encode/bin/sun/mpeg_encode +#tweety keving ~keving/src/encode/bin/sun/mpeg_encode +# +# these guys are pretty fast: +# +#bugs-bunny keving ~keving/src/encode/bin/sun/mpeg_encode +# +# remotes +# +#REMOTE anaconda keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE adder keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE moccasin keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE cobra keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE boa keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE asp keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE rattler keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE viper keving ~keving/mpeg_encode ~keving/parallel.test +# mamba doesn't seem to work for whatever reason...don't know why +#REMOTE mamba keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE cse.lbl.gov kevin ~kevin/mpeg_encode ~kevin/parallel.test +#REMOTE roger-rabbit keving ~keving/src/encode/bin/sun/mpeg_encode ~keving/src/encode/ParamExamples/parallel.test +END_PARALLEL + + +# motion vector search parameters + +# MAD or MSE -- must be upper case +ERROR MAD + +# FULL or HALF -- must be upper case +PIXEL FULL + +# means +/- this many pixels +RANGE 8 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG SIMPLE + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + diff --git a/converter/ppm/ppmtompeg/examples/luxo.param b/converter/ppm/ppmtompeg/examples/luxo.param new file mode 100644 index 00000000..6c92dca1 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/luxo.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/luxo.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 320x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/luxo + +INPUT +luxo*.yuv [1201-1400] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 20 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/luxo18.param b/converter/ppm/ppmtompeg/examples/luxo18.param new file mode 100644 index 00000000..4a020325 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/luxo18.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/luxo19.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 320x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/luxo + +INPUT +luxo*.yuv [1201-1400] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/luxo19.5.param b/converter/ppm/ppmtompeg/examples/luxo19.5.param new file mode 100644 index 00000000..edacc7c7 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/luxo19.5.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/luxo19.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 320x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/luxo + +INPUT +luxo*.yuv [1201-1216] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/luxo19.param b/converter/ppm/ppmtompeg/examples/luxo19.param new file mode 100644 index 00000000..7accb23a --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/luxo19.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/luxo19.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 320x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/luxo + +INPUT +luxo*.yuv [1201-1400] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/luxo2.param b/converter/ppm/ppmtompeg/examples/luxo2.param new file mode 100644 index 00000000..f75b7e1c --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/luxo2.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN I +OUTPUT /n/picasso/users/keving/encode/output/luxo2.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 320x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/luxo + +INPUT +luxo1399.yuv +luxo1400.yuv +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 20 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/luxo48.param b/converter/ppm/ppmtompeg/examples/luxo48.param new file mode 100644 index 00000000..599f6d8c --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/luxo48.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/luxo19.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 320x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/luxo + +INPUT +luxo*.yuv [1201-1400] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG SUBSAMPLE +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/luxo49.param b/converter/ppm/ppmtompeg/examples/luxo49.param new file mode 100644 index 00000000..5a493e40 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/luxo49.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/luxo19.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 320x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/luxo + +INPUT +luxo*.yuv [1201-1400] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG TWOLEVEL +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/luxo50.param b/converter/ppm/ppmtompeg/examples/luxo50.param new file mode 100644 index 00000000..f3afe8e8 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/luxo50.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/luxo19.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 320x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/luxo + +INPUT +luxo*.yuv [1201-1400] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/luxo52.5.param b/converter/ppm/ppmtompeg/examples/luxo52.5.param new file mode 100644 index 00000000..f3f16854 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/luxo52.5.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/luxo19.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 320x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/luxo + +INPUT +luxo*.yuv [1201-1216] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/luxo52.param b/converter/ppm/ppmtompeg/examples/luxo52.param new file mode 100644 index 00000000..8874ad97 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/luxo52.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/luxo19.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 320x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/luxo + +INPUT +luxo*.yuv [1201-1400] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/luxo53.param b/converter/ppm/ppmtompeg/examples/luxo53.param new file mode 100644 index 00000000..d084b18b --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/luxo53.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/luxo19.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 320x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/luxo + +INPUT +luxo*.yuv [1201-1216] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG EXHAUSTIVE + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/luxobug.param b/converter/ppm/ppmtompeg/examples/luxobug.param new file mode 100644 index 00000000..73e9de9f --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/luxobug.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/luxobug.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 320x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/luxo-skip10 + +INPUT +luxo*.yuv [1351-1701+5] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/luxoskip.param b/converter/ppm/ppmtompeg/examples/luxoskip.param new file mode 100644 index 00000000..12768ed4 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/luxoskip.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/luxoskip.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PPM +YUV_SIZE 320x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/luxo-skip10 + +INPUT +luxo*1 [1-307] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 20 + +# motion vector search parameters + +PIXEL HALF + +RANGE 20 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/med.param b/converter/ppm/ppmtompeg/examples/med.param new file mode 100644 index 00000000..0e3bbd8d --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/med.param @@ -0,0 +1,36 @@ +# 'med' parameter file + +PATTERN IBBPBBPBBPBBPBBI +OUTPUT output/med.mpg +GOP_SIZE 20 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PPM +INPUT_CONVERT giftoppm * + +INPUT_DIR links/gcmovie + +INPUT +med*.gif [001-073] +END_INPUT + + +# motion vector search parameters + +# MAD or MSE -- must be upper case +ERROR MAD + +# FULL or HALF -- must be upper case +PIXEL HALF + +# means +/- this many pixels +RANGE 8 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG CROSS2 + + +IQSCALE 13 +PQSCALE 16 +BQSCALE 26 + diff --git a/converter/ppm/ppmtompeg/examples/med18.param b/converter/ppm/ppmtompeg/examples/med18.param new file mode 100644 index 00000000..b8ad664c --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/med18.param @@ -0,0 +1,34 @@ +# 'med' parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/med18.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PPM +INPUT_CONVERT giftoppm * + +INPUT_DIR /n/picasso/users/keving/encode/input/gcmovie + +INPUT +med*.gif [001-073] +END_INPUT + + +# motion vector search parameters + +# FULL or HALF -- must be upper case +PIXEL HALF + +# means +/- this many pixels +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/medspeed.param b/converter/ppm/ppmtompeg/examples/medspeed.param new file mode 100644 index 00000000..d91f4dda --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/medspeed.param @@ -0,0 +1,38 @@ +# speed test parameter file + +PATTERN ibbpbbpbbpbbpbb +OUTPUT /n/picasso/users/keving/encode/output/flowgard.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT cat *.Y *.U *.V + +INPUT_DIR /n/picasso/project/mm/mpeg/mpeg_encode/input/flowg + +INPUT +sflowg.* [0-149] +END_INPUT + +# motion vector search parameters + +# MAD or MSE -- must be upper case +ERROR MAD + +# FULL or HALF -- must be upper case +PIXEL FULL + +# means +/- this many pixels +RANGE 10 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG CROSS2 + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +REFERENCE_FRAME ORIGINAL + diff --git a/converter/ppm/ppmtompeg/examples/nametest.param b/converter/ppm/ppmtompeg/examples/nametest.param new file mode 100644 index 00000000..445622fb --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/nametest.param @@ -0,0 +1,60 @@ +# test suite parameter file + +PATTERN IBBBPBBBBP +OUTPUT /tmp/foobar.mpg + +YUV_SIZE 352x240 + +BASE_FILE_FORMAT YUV +INPUT_CONVERT cat *.Y *.U *.V +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +INPUT_DIR /n/picasso/users/keving/encode/input/tennis + +INPUT +stennis.0 +stennis.* [1-3] +stennis.* [4-5] +stennis.* [6-6] +stennis.7 +stennis.* [8-15] +stennis.16 +stennis.17 +stennis.* [18-34] +END_INPUT + + + +# all of the remaining options have to do with the motion search and qscale +# +# change this only if you're unsatisfied with the CPU time or quality, or +# are experimenting +# + +# if this appears in the file, then in addition to testing luminance when +# computing motion vectors, program will also take into account chrominance +# +# this option MUST appear before ERROR option, or it will be ignored +# +# CHROMINANCE + +# MAD or MSE -- must be upper case +ERROR MAD + +# FULL or HALF -- must be upper case +PIXEL FULL + +# means +/- this many pixels +RANGE 8 + +PSEARCH_ALG LOGARITHMIC + +BSEARCH_ALG SIMPLE + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/nosearch.param b/converter/ppm/ppmtompeg/examples/nosearch.param new file mode 100644 index 00000000..453356c0 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/nosearch.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN I +OUTPUT /n/picasso/users/keving/encode/output/flowgard1.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL FULL + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/par3.param b/converter/ppm/ppmtompeg/examples/par3.param new file mode 100644 index 00000000..ca664334 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/par3.param @@ -0,0 +1,43 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/flowgard.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR /n/picasso/users/keving/encode/input/flowg + +INPUT +sflowg.*.yuv [0-39] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME ORIGINAL + +PARALLEL_TEST_FRAMES 3 +PARALLEL_TIME_CHUNKS 30 + +PARALLEL +bugs-bunny keving ~keving/encode/bin/sun/mpeg_encode +linus keving ~keving/encode/bin/sun/mpeg_encode +END_PARALLEL diff --git a/converter/ppm/ppmtompeg/examples/par4.param b/converter/ppm/ppmtompeg/examples/par4.param new file mode 100644 index 00000000..95a72c34 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/par4.param @@ -0,0 +1,45 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/flowgard.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR /n/picasso/users/keving/encode/input/flowg + +INPUT +sflowg.*.yuv [0-49] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME ORIGINAL + +PARALLEL_TEST_FRAMES 3 +# PARALLEL_TIME_CHUNKS 20 +PARALLEL_CHUNK_TAPER + +PARALLEL +bugs-bunny keving ~keving/encode/bin/sun/mpeg_encode +linus keving ~keving/encode/bin/sun/mpeg_encode +zonker keving ~keving/encode/bin/sun/mpeg_encode +END_PARALLEL diff --git a/converter/ppm/ppmtompeg/examples/par5.param b/converter/ppm/ppmtompeg/examples/par5.param new file mode 100644 index 00000000..ad51a519 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/par5.param @@ -0,0 +1,47 @@ +# speed test parameter file + +#PATTERN IBBPBBPBBPBBPBB +PATTERN I + +OUTPUT /n/picasso/users/keving/encode/output/flowgard.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR /n/picasso/users/keving/encode/input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME ORIGINAL + +PARALLEL_TEST_FRAMES 3 +PARALLEL_CHUNK_TAPER + +PARALLEL +woodstock keving ~keving/encode/bin/dec-alpha/mpeg_encode +charlie-brown keving ~keving/encode/bin/dec-alpha/mpeg_encode +gumby keving ~keving/encode/bin/hp/mpeg_encode +big-bird keving ~keving/encode/bin/hp/mpeg_encode +roger-rabbit keving ~keving/encode/bin/sun/mpeg_encode +END_PARALLEL diff --git a/converter/ppm/ppmtompeg/examples/parallel.2 b/converter/ppm/ppmtompeg/examples/parallel.2 new file mode 100644 index 00000000..0ab153fb --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/parallel.2 @@ -0,0 +1,87 @@ +# speed test parameter file + +PATTERN ibbpbbpbbpbbpbb +OUTPUT /n/picasso/users/keving/encode/output/flowgard.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PPM +YUV_SIZE 352x240 + +INPUT_CONVERT djpeg * + +INPUT_DIR /n/picasso/project/mm/mpeg/mpeg_encode/input/flowg/jpeg + +INPUT +sflowg.*.jpeg [1-30] +END_INPUT + +# the following are optional + +PARALLEL_PERFECT + + + +# specifies what the IO server must do before transmitting to remote sites +IO_SERVER_CONVERT * + +# specifies what the remote slave must do after receiving from IO server +SLAVE_CONVERT djpeg * + +PARALLEL +# lines must be of form "machine <whitespace> username <whitespace> executable" +# these guys are sorta slow: +#elmer-fudd keving ~keving/encode/bin/sun/mpeg_encode +#elmer-fudd keving ~keving/encode/bin/sun/mpeg_encode +#bugs-bunny keving ~keving/encode/bin/sun/mpeg_encode +#REMOTE bugs-bunny keving ~keving/encode/src/mpeg_encode ~keving/encode/examples/parallel.test +#roger-rabbit keving ~keving/encode/bin/sun/mpeg_encode +#tweety keving ~keving/encode/bin/sun/mpeg_encode +#mickey keving ~keving/encode/bin/mickey/mpeg_encode +# +# these guys are pretty fast: +# +#big-bird keving ~keving/encode/bin/hp/mpeg_encode +#gumby keving ~keving/encode/bin/hp/mpeg_encode +charlie-brown keving ~keving/encode/bin/dec-alpha/mpeg_encode +woodstock keving ~keving/encode/bin/dec-alpha/mpeg_encode +#bugs-bunny keving ~keving/encode/bin/sun/mpeg_encode +# +# remotes +# +#REMOTE anaconda keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE adder keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE moccasin keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE cobra keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE boa keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE asp keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE rattler keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE viper keving ~keving/mpeg_encode ~keving/parallel.test +# mamba doesn't seem to work for whatever reason...don't know why +#REMOTE mamba keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE cse.lbl.gov kevin ~kevin/mpeg_encode ~kevin/parallel.test +#REMOTE roger-rabbit keving ~keving/src/encode/bin/sun/mpeg_encode ~keving/src/encode/ParamExamples/parallel.test +END_PARALLEL + + +# motion vector search parameters + +# MAD or MSE -- must be upper case +ERROR MAD + +# FULL or HALF -- must be upper case +PIXEL FULL + +# means +/- this many pixels +RANGE 8 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG SIMPLE + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + + +REFERENCE_FRAME ORIGINAL + diff --git a/converter/ppm/ppmtompeg/examples/parallel.param b/converter/ppm/ppmtompeg/examples/parallel.param new file mode 100644 index 00000000..a158eae5 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/parallel.param @@ -0,0 +1,141 @@ +# parameter file template with parallel execution +# +# you can use this as a template, copying it to a separate file then modifying +# the copy +# +# +# any line beginning with '#' is a comment +# +# no line should be longer than 255 characters +# +# +# general format of each line is: +# <option> <spaces and/or tabs> <value> +# +# lines can generally be in any order +# +# only exception is the option 'INPUT' which must be followed by input +# files in the order in which they must appear, followed by 'END_INPUT' +# +# <option> MUST be in UPPER CASE +# + +PATTERN IBBPBBPBBPBBP +OUTPUT /n/picasso/users/keving/encode/output.mpg + +# mpeg_encode really only accepts 3 different file formats, but using a +# conversion statement it can effectively handle ANY file format +# +# you must specify whether you will convert to PNM or PPM or YUV format +# (must be upper case) +# +BASE_FILE_FORMAT YUV + +# +# if YUV format (or using parallel version), must provide width and height +# YUV_SIZE widthxheight +# this option is ignored if BASE_FILE_FORMAT is PPM or PNM and you're running +# on just one machine +# +YUV_SIZE 352x240 + +# the conversion statement +# +# Each occurrence of '*' will be replaced by the input file +# +# e.g., if you have a bunch of GIF files, then this might be: +# INPUT_CONVERT giftoppm * +# +# e.g., if you have a bunch of files like a.Y a.U a.V, etc., then: +# INPUT_CONVERT cat *.Y *.U *.V +# +# e.g., if you are grabbing from laser disc you might have something like +# INPUT_CONVERT goto frame *; grabppm +# 'INPUT_CONVERT *' means the files are already in the base file format +# +INPUT_CONVERT * + +# number of frames in a GOP. +# +# since each GOP must have at least one I-frame, the encoder will find the +# the first I-frame after GOP_SIZE frames to start the next GOP +# +# later, will add more flexible GOP signalling +# +GOP_SIZE 6 + +# number of slices in a frame +# +# 1 is a good number. another possibility is the number of macroblock rows +# (which is the height divided by 16) +# +SLICES_PER_FRAME 1 + +# directory to get all input files from (makes this file easier to read) +INPUT_DIR /n/picasso/users/keving/encode/input/tennis + +INPUT +# '*' is replaced by the numbers 01, 02, 03, 04 +# if I instead do [01-11], it would be 01, 02, ..., 09, 10, 11 +# if I instead do [1-11], it would be 1, 2, 3, ..., 9, 10, 11 +# if I instead do [1-11+3], it would be 1, 4, 7, 10 +# the program assumes none of your input files has a name ending in ']' +# if you do, too bad!!! +# +# +stennis.*.yuv [0-7] +# can have more files here if you want...there is no limit on the number +# of files +END_INPUT + + + +# all of the remaining options have to do with the motion search and qscale + +# FULL or HALF -- must be upper case +PIXEL HALF + +# means +/- this many pixels +RANGE 10 + +# this must be one of {EXHAUSTIVE, SUBSAMPLE, LOGARITHMIC} +PSEARCH_ALG LOGARITHMIC + +# this must be one of {SIMPLE, CROSS2, EXHAUSTIVE} +# +# note that EXHAUSTIVE is really, really, really slow +# +BSEARCH_ALG CROSS2 + +# +# these specify the q-scale for I, P, and B frames +# (values must be between 1 and 31) +# +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# this must be ORIGINAL or DECODED +REFERENCE_FRAME ORIGINAL + + +# the following two are optional (default = 10, 60) + +# number of frames to do initially to gauge speed of machine +PARALLEL_TEST_FRAMES 3 + +# number of seconds per chunk thereafter +PARALLEL_TIME_CHUNKS 30 + + +PARALLEL +# lines must be of form "machine <whitespace> username <whitespace> executable" +charlie-brown keving ~keving/encode/bin/dec-alpha/mpeg_encode +#woodstock keving ~keving/encode/bin/dec-alpha/mpeg_encode +#REMOTE charlie-brown keving ~keving/encode/bin/dec-alpha/mpeg_encode ~keving/encode/examples/parallel.param +# remote machine: "REMOTE machine username executable param_file" +# mickey keving ~keving/encode/bin/dec-5000/mpeg_encode +#REMOTE mickey keving ~keving/encode/bin/dec-5000/mpeg_encode ~keving/encode/examples/parallel.param +#REMOTE mickey keving ~keving/encode/bin/dec-5000/mpeg_encode ~keving/encode/examples/parallel.param +REMOTE woodstock keving ~keving/encode/bin/dec-alpha/mpeg_encode ~keving/encode/examples/parallel.param +END_PARALLEL diff --git a/converter/ppm/ppmtompeg/examples/parallel.test b/converter/ppm/ppmtompeg/examples/parallel.test new file mode 100644 index 00000000..2489d847 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/parallel.test @@ -0,0 +1,85 @@ +# speed test parameter file + +PATTERN ibbpbbpbbpbbpbb +OUTPUT /n/picasso/users/keving/encode/output/flowgard.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PPM +YUV_SIZE 352x240 + +INPUT_CONVERT djpeg * + +INPUT_DIR /n/picasso/project/mm/mpeg/mpeg_encode/input/flowg/jpeg + +INPUT +sflowg.*.jpeg [1-30] +END_INPUT + +# the following two are optional (default = 10, 60) + +# number of frames to do initially to gauge speed of machine +PARALLEL_TEST_FRAMES 3 + +# number of seconds per chunk thereafter +PARALLEL_TIME_CHUNKS 30 + +# specifies what the IO server must do before transmitting to remote sites +IO_SERVER_CONVERT * + +# specifies what the remote slave must do after receiving from IO server +SLAVE_CONVERT djpeg * + +PARALLEL +# lines must be of form "machine <whitespace> username <whitespace> executable" +# these guys are sorta slow: +#REMOTE bugs-bunny keving ~keving/encode/src/mpeg_encode ~keving/encode/examples/parallel.test +#mickey keving ~keving/encode/bin/mickey/mpeg_encode +# +# these guys are pretty fast: +# +#big-bird keving ~keving/encode/bin/hp/mpeg_encode +#gumby keving ~keving/encode/bin/hp/mpeg_encode +#charlie-brown keving ~keving/encode/bin/dec-alpha/mpeg_encode +woodstock keving ~keving/encode/bin/dec-alpha/mpeg_encode +bugs-bunny keving ~keving/encode/bin/sun/mpeg_encode +#roger-rabbit keving ~keving/encode/bin/sun/mpeg_encode +# +# remotes +# +#REMOTE anaconda keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE adder keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE moccasin keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE cobra keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE boa keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE asp keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE rattler keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE viper keving ~keving/mpeg_encode ~keving/parallel.test +# mamba doesn't seem to work for whatever reason...don't know why +#REMOTE mamba keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE cse.lbl.gov kevin ~kevin/mpeg_encode ~kevin/parallel.test +#REMOTE roger-rabbit keving ~keving/src/encode/bin/sun/mpeg_encode ~keving/src/encode/ParamExamples/parallel.test +END_PARALLEL + + +# motion vector search parameters + +# MAD or MSE -- must be upper case +ERROR MAD + +# FULL or HALF -- must be upper case +PIXEL FULL + +# means +/- this many pixels +RANGE 8 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG SIMPLE + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + + +REFERENCE_FRAME ORIGINAL + diff --git a/converter/ppm/ppmtompeg/examples/param.template b/converter/ppm/ppmtompeg/examples/param.template new file mode 100644 index 00000000..ce81e474 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/param.template @@ -0,0 +1,107 @@ +# parameter file template +# +# any line beginning with '#' is a comment +# +# no line should be longer than 255 characters +# +# +# general format of each line is: +# <option> <spaces and/or tabs> <value> +# +# lines can generally be in any order +# +# only exception is the option 'INPUT' which must be followed by input +# files in the order in which they must appear, followed by 'END_INPUT' +# +# <option> MUST be in UPPER CASE +# + +PATTERN IBBPBBPBBPBBP +OUTPUT output/food + +# mpeg_encode really only accepts 2 different file formats, but using a +# conversion statement it can effectively handle ANY file format +# +# you must specify whether you will convert to PPM or YUV format +# (must be upper case) +# +BASE_FILE_FORMAT YUV + +# +# if YUV format, must provide width and height +# YUV_SIZE width height +# this option is ignored if BASE_FILE_FORMAT is PPM +# +YUV_SIZE 352x240 + +# the conversion statement +# +# Each occurrence of '*' will be replaced by the input file +# +# e.g., if you have a bunch of GIF files, then this might be: +# INPUT_CONVERT giftoppm * +# +# e.g., if you have a bunch of files like a.Y a.U a.V, etc., then: +# INPUT_CONVERT cat *.Y *.U *.V +# +# e.g., if you are grabbing from laser disc you might have something like +# INPUT_CONVERT goto frame *; grabppm +# 'INPUT_CONVERT *' means the files are already in the base file format +# +INPUT_CONVERT cat *.Y *.U *.V + +# number of frames in a GOP. +# +# since each GOP must have at least one I-frame, the encoder will find the +# the first I-frame after GOP_SIZE frames to start the next GOP +# +# later, will add more flexible GOP signalling +# +GOP_SIZE 30 + +# directory to get all input files from (makes this file easier to read) +INPUT_DIR links/payam + +INPUT +# '*' is replaced by the numbers 01, 02, 03, 04 +# if I instead do [01-11], it would be 01, 02, ..., 09, 10, 11 +# if I instead do [1-11], it would be 1, 2, 3, ..., 9, 10, 11 +# the program assumes none of your input files has a name ending in ']' +# if you do, too bad!!! +# +# +stennis.* [0-23] +# can have more files here if you want...as many as you want +END_INPUT + + + +# all of the remaining options have to do with the motion search and qscale +# +# change this only if you're unsatisfied with the CPU time or quality, or +# are experimenting +# + +# if this appears in the file, then in addition to testing luminance when +# computing motion vectors, program will also take into account chrominance +# +# this option MUST appear before ERROR option, or it will be ignored +# +# CHROMINANCE + +# MAD or MSE -- must be upper case +ERROR MAD + +# FULL or HALF -- must be upper case +PIXEL FULL + +# means +/- this many pixels +RANGE 8 + +# YES or NO -- must be upper case +SUBSAMPLE NO + +IQSCALE 10 +PQSCALE 15 +BQSCALE 25 + diff --git a/converter/ppm/ppmtompeg/examples/payam.param b/converter/ppm/ppmtompeg/examples/payam.param new file mode 100644 index 00000000..ce886914 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/payam.param @@ -0,0 +1,37 @@ +# parameter file for payam's images + +#PATTERN IBBPBBPBBPBBPBBI +PATTERN II +OUTPUT output/payam.mpg +GOP_SIZE 20 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PNM + +INPUT_CONVERT * +INPUT_DIR links/payam + +INPUT +kh*.pnm [1-3] +END_INPUT + + +# motion vector search paramters + +# MAD or MSE -- must be upper case +ERROR MAD + +# FULL or HALF -- must be upper case +PIXEL FULL + +# means +/- this many pixels +RANGE 8 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG SIMPLE + + +IQSCALE 10 +PQSCALE 15 +BQSCALE 25 + diff --git a/converter/ppm/ppmtompeg/examples/payam18.param b/converter/ppm/ppmtompeg/examples/payam18.param new file mode 100644 index 00000000..7f7b767e --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/payam18.param @@ -0,0 +1,34 @@ +# parameter file for payam's images + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/payam18.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PNM + +INPUT_CONVERT * +INPUT_DIR /n/picasso/users/keving/encode/input/payam + +INPUT +kh*.pnm [1-39] +END_INPUT + + +# motion vector search paramters + +# FULL or HALF -- must be upper case +PIXEL HALF + +# means +/- this many pixels +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG SIMPLE + + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/pnm.param b/converter/ppm/ppmtompeg/examples/pnm.param new file mode 100644 index 00000000..6fce2be4 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/pnm.param @@ -0,0 +1,37 @@ +# speed test parameter file + +PATTERN I +OUTPUT output/food +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PPM +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR links/flowg + +INPUT +sflowg.*.ppm [01-10] +END_INPUT + + +# motion vector search parameters + +# MAD or MSE -- must be upper case +ERROR MAD + +# FULL or HALF -- must be upper case +PIXEL FULL + +# means +/- this many pixels +RANGE 8 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG SIMPLE + +IQSCALE 31 +PQSCALE 10 +BQSCALE 25 + diff --git a/converter/ppm/ppmtompeg/examples/ppm.param b/converter/ppm/ppmtompeg/examples/ppm.param new file mode 100644 index 00000000..6fce2be4 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/ppm.param @@ -0,0 +1,37 @@ +# speed test parameter file + +PATTERN I +OUTPUT output/food +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PPM +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR links/flowg + +INPUT +sflowg.*.ppm [01-10] +END_INPUT + + +# motion vector search parameters + +# MAD or MSE -- must be upper case +ERROR MAD + +# FULL or HALF -- must be upper case +PIXEL FULL + +# means +/- this many pixels +RANGE 8 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG SIMPLE + +IQSCALE 31 +PQSCALE 10 +BQSCALE 25 + diff --git a/converter/ppm/ppmtompeg/examples/prof.b.param b/converter/ppm/ppmtompeg/examples/prof.b.param new file mode 100644 index 00000000..2b8253fd --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/prof.b.param @@ -0,0 +1,48 @@ +# test suite parameter file + +PATTERN IBBBBBBBBI +OUTPUT output/food + +BASE_FILE_FORMAT PPM +INPUT_CONVERT giftoppm * +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +INPUT_DIR Test/ts + +INPUT +med*.gif [030-039] +END_INPUT + + + +# all of the remaining options have to do with the motion search and qscale +# +# change this only if you're unsatisfied with the CPU time or quality, or +# are experimenting +# + +# if this appears in the file, then in addition to testing luminance when +# computing motion vectors, program will also take into account chrominance +# +# this option MUST appear before ERROR option, or it will be ignored +# +# CHROMINANCE + +# MAD or MSE -- must be upper case +ERROR MAD + +# FULL or HALF -- must be upper case +PIXEL FULL + +# means +/- this many pixels +RANGE 8 + +# YES or NO -- must be upper case +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG SIMPLE + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + diff --git a/converter/ppm/ppmtompeg/examples/prof.bhalf.param b/converter/ppm/ppmtompeg/examples/prof.bhalf.param new file mode 100644 index 00000000..8d6de83a --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/prof.bhalf.param @@ -0,0 +1,48 @@ +# test suite parameter file + +PATTERN IBBBBBBBBI +OUTPUT output/food + +BASE_FILE_FORMAT PPM +INPUT_CONVERT giftoppm * +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +INPUT_DIR Test/ts + +INPUT +med*.gif [030-039] +END_INPUT + + + +# all of the remaining options have to do with the motion search and qscale +# +# change this only if you're unsatisfied with the CPU time or quality, or +# are experimenting +# + +# if this appears in the file, then in addition to testing luminance when +# computing motion vectors, program will also take into account chrominance +# +# this option MUST appear before ERROR option, or it will be ignored +# +# CHROMINANCE + +# MAD or MSE -- must be upper case +ERROR MAD + +# FULL or HALF -- must be upper case +PIXEL HALF + +# means +/- this many pixels +RANGE 8 + +# YES or NO -- must be upper case +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG CROSS2 + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + diff --git a/converter/ppm/ppmtompeg/examples/prof.param b/converter/ppm/ppmtompeg/examples/prof.param new file mode 100644 index 00000000..591eaa67 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/prof.param @@ -0,0 +1,36 @@ +# speed test parameter file + +PATTERN I +#PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG TWOLEVEL +BSEARCH_ALG CROSS2 + +#REFERENCE_FRAME DECODED +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/prof.ss.param b/converter/ppm/ppmtompeg/examples/prof.ss.param new file mode 100644 index 00000000..ef339e7a --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/prof.ss.param @@ -0,0 +1,48 @@ +# test suite parameter file + +PATTERN IPPPPPPPPP +OUTPUT output/food + +BASE_FILE_FORMAT PPM +INPUT_CONVERT giftoppm * +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +INPUT_DIR Test/ts + +INPUT +med*.gif [030-039] +END_INPUT + + + +# all of the remaining options have to do with the motion search and qscale +# +# change this only if you're unsatisfied with the CPU time or quality, or +# are experimenting +# + +# if this appears in the file, then in addition to testing luminance when +# computing motion vectors, program will also take into account chrominance +# +# this option MUST appear before ERROR option, or it will be ignored +# +# CHROMINANCE + +# MAD or MSE -- must be upper case +ERROR MAD + +# FULL or HALF -- must be upper case +PIXEL FULL + +# means +/- this many pixels +RANGE 16 + +# YES or NO -- must be upper case +PSEARCH_ALG SUBSAMPLE +BSEARCH_ALG SIMPLE + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + diff --git a/converter/ppm/ppmtompeg/examples/prof2.param b/converter/ppm/ppmtompeg/examples/prof2.param new file mode 100644 index 00000000..4073606a --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/prof2.param @@ -0,0 +1,36 @@ +# speed test parameter file + +PATTERN IPPP +#PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG TWOLEVEL +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED + diff --git a/converter/ppm/ppmtompeg/examples/prof3.param b/converter/ppm/ppmtompeg/examples/prof3.param new file mode 100644 index 00000000..4581eade --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/prof3.param @@ -0,0 +1,36 @@ +# speed test parameter file + +PATTERN IBB +#PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG TWOLEVEL +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED + diff --git a/converter/ppm/ppmtompeg/examples/prof4.param b/converter/ppm/ppmtompeg/examples/prof4.param new file mode 100644 index 00000000..583fb43c --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/prof4.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG TWOLEVEL +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED + diff --git a/converter/ppm/ppmtompeg/examples/remote.test b/converter/ppm/ppmtompeg/examples/remote.test new file mode 100644 index 00000000..d548510f --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/remote.test @@ -0,0 +1,85 @@ +# speed test parameter file + +PATTERN ibbpbbpbbpbbpbb +OUTPUT /n/picasso/users/keving/encode/output/good.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT cat *.Y *.U *.V + +INPUT_DIR /n/picasso/users/keving/links/flowg + +INPUT +sflowg.* [0-40] +END_INPUT + +# the following two are optional (default = 10, 60) + +# number of frames to do initially to gauge speed of machine +PARALLEL_TEST_FRAMES 3 + +# number of seconds per chunk thereafter +PARALLEL_TIME_CHUNKS 30 + + +PARALLEL +# lines must be of form "machine <whitespace> username <whitespace> executable" +# these guys are sorta slow: +#elmer-fudd keving ~keving/src/encode/bin/sun/mpeg_encode +#zonker keving ~keving/src/encode/bin/sun/mpeg_encode +#roger-rabbit keving ~keving/src/encode/bin/sun/mpeg_encode +#tweety keving ~keving/src/encode/bin/sun/mpeg_encode +#mickey keving ~keving/src/encode/bin/mickey/mpeg_encode +# +REMOTE zonker keving ~keving/encode/src/mpeg_encode ~keving/encode/examples/remote.test +REMOTE linus keving ~keving/encode/src/mpeg_encode ~keving/encode/examples/remote.test +REMOTE roger-rabbit keving ~keving/encode/src/mpeg_encode ~keving/encode/examples/remote.test +REMOTE bugs-bunny keving ~keving/encode/src/mpeg_encode ~keving/encode/examples/remote.test +REMOTE elmer-fudd keving ~keving/encode/src/mpeg_encode ~keving/encode/examples/remote.test +# these guys are pretty fast: +# +#big-bird keving ~keving/src/encode/bin/hp/mpeg_encode +#gumby keving ~keving/src/encode/bin/hp/mpeg_encode +#charlie-brown keving ~keving/src/encode/bin/dec/mpeg_encode +#woodstock keving ~keving/src/encode/bin/dec/mpeg_encode +#bugs-bunny keving ~keving/src/encode/bin/sun/mpeg_encode +# +# remotes +# +#REMOTE anaconda keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE adder keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE moccasin keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE cobra keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE boa keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE asp keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE rattler keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE viper keving ~keving/mpeg_encode ~keving/parallel.test +# mamba doesn't seem to work for whatever reason...don't know why +#REMOTE mamba keving ~keving/mpeg_encode ~keving/parallel.test +#REMOTE cse.lbl.gov kevin ~kevin/mpeg_encode ~kevin/parallel.test +#REMOTE roger-rabbit keving ~keving/src/encode/bin/sun/mpeg_encode ~keving/src/encode/ParamExamples/parallel.test +END_PARALLEL + + +# motion vector search parameters + +# MAD or MSE -- must be upper case +ERROR MAD + +# FULL or HALF -- must be upper case +PIXEL FULL + +# means +/- this many pixels +RANGE 4 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG SIMPLE + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search1.param b/converter/ppm/ppmtompeg/examples/search1.param new file mode 100644 index 00000000..779ffa0f --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search1.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard1.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL FULL + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search10.param b/converter/ppm/ppmtompeg/examples/search10.param new file mode 100644 index 00000000..c44c7c72 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search10.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard10.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search11.param b/converter/ppm/ppmtompeg/examples/search11.param new file mode 100644 index 00000000..78935268 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search11.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard11.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL FULL + +RANGE 20 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/search12.param b/converter/ppm/ppmtompeg/examples/search12.param new file mode 100644 index 00000000..d7a115c3 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search12.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard12.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search13.param b/converter/ppm/ppmtompeg/examples/search13.param new file mode 100644 index 00000000..80bb57f5 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search13.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard13.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 20 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search14.param b/converter/ppm/ppmtompeg/examples/search14.param new file mode 100644 index 00000000..af0ebb9c --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search14.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/search15.param b/converter/ppm/ppmtompeg/examples/search15.param new file mode 100644 index 00000000..1df18350 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search15.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard15.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL FULL + +RANGE 20 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/search16.param b/converter/ppm/ppmtompeg/examples/search16.param new file mode 100644 index 00000000..3523dce3 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search16.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard16.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 20 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search17.param b/converter/ppm/ppmtompeg/examples/search17.param new file mode 100644 index 00000000..89c3cb91 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search17.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard17.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 20 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/search18.param b/converter/ppm/ppmtompeg/examples/search18.param new file mode 100644 index 00000000..d659d914 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search18.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard18.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search19.5.param b/converter/ppm/ppmtompeg/examples/search19.5.param new file mode 100644 index 00000000..22827fce --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search19.5.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/flowgard.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-15] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/search19.param b/converter/ppm/ppmtompeg/examples/search19.param new file mode 100644 index 00000000..88f9d613 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search19.param @@ -0,0 +1,42 @@ +# speed test parameter file + +OUTPUT /n/picasso/users/keving/encode/output/flowgard.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR /n/charlie-brown/project/mm/mpeg/mpeg_encode/input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + + + + +PATTERN IBBPBBPBBPBBPBB + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +# LOGARITHMIC, TWOLEVEL, SUBSAMPLE, EXHAUSTIVE +PSEARCH_ALG LOGARITHMIC + +# CROSS2, SIMPLE +BSEARCH_ALG CROSS2 + +# DECODED or ORIGINAL +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/search2.param b/converter/ppm/ppmtompeg/examples/search2.param new file mode 100644 index 00000000..c4f6300c --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search2.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard2.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL FULL + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/search20.param b/converter/ppm/ppmtompeg/examples/search20.param new file mode 100644 index 00000000..efd05f6d --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search20.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard20.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL FULL + +RANGE 10 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search21.param b/converter/ppm/ppmtompeg/examples/search21.param new file mode 100644 index 00000000..6acb79ac --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search21.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard21.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search22.param b/converter/ppm/ppmtompeg/examples/search22.param new file mode 100644 index 00000000..252cb25b --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search22.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard22.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL FULL + +RANGE 20 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search23.param b/converter/ppm/ppmtompeg/examples/search23.param new file mode 100644 index 00000000..5e7a7620 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search23.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard23.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 20 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search24.param b/converter/ppm/ppmtompeg/examples/search24.param new file mode 100644 index 00000000..0ca71e6d --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search24.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard24.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG SUBSAMPLE +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search25.param b/converter/ppm/ppmtompeg/examples/search25.param new file mode 100644 index 00000000..724ce5a4 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search25.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard25.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG SUBSAMPLE +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search26.param b/converter/ppm/ppmtompeg/examples/search26.param new file mode 100644 index 00000000..1a7081cd --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search26.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard26.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL FULL + +RANGE 10 + +PSEARCH_ALG SUBSAMPLE +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search27.param b/converter/ppm/ppmtompeg/examples/search27.param new file mode 100644 index 00000000..49da7e7a --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search27.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard27.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL FULL + +RANGE 20 + +PSEARCH_ALG SUBSAMPLE +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search28.param b/converter/ppm/ppmtompeg/examples/search28.param new file mode 100644 index 00000000..f78331aa --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search28.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard28.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 20 + +PSEARCH_ALG SUBSAMPLE +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search29.param b/converter/ppm/ppmtompeg/examples/search29.param new file mode 100644 index 00000000..52f89a90 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search29.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard29.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR /n/picasso/users/keving/encode/input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG TWOLEVEL +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search3.param b/converter/ppm/ppmtompeg/examples/search3.param new file mode 100644 index 00000000..4c00af6c --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search3.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard3.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL FULL + +RANGE 20 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search30.param b/converter/ppm/ppmtompeg/examples/search30.param new file mode 100644 index 00000000..5664dbb8 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search30.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard30.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR /n/picasso/users/keving/encode/input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 20 + +PSEARCH_ALG TWOLEVEL +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search31.param b/converter/ppm/ppmtompeg/examples/search31.param new file mode 100644 index 00000000..3d8bc0cd --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search31.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR /n/picasso/users/keving/encode/input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG TWOLEVEL +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search32.param b/converter/ppm/ppmtompeg/examples/search32.param new file mode 100644 index 00000000..a4675cee --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search32.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard32.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR /n/picasso/users/keving/encode/input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 4 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search33.param b/converter/ppm/ppmtompeg/examples/search33.param new file mode 100644 index 00000000..465132c6 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search33.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard33.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR /n/picasso/users/keving/encode/input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 4 + +PSEARCH_ALG TWOLEVEL +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search34.param b/converter/ppm/ppmtompeg/examples/search34.param new file mode 100644 index 00000000..f53a91f3 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search34.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard34.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR /n/picasso/users/keving/encode/input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 4 + +PSEARCH_ALG SUBSAMPLE +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search35.param b/converter/ppm/ppmtompeg/examples/search35.param new file mode 100644 index 00000000..5a7fa27f --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search35.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard35.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR /n/picasso/users/keving/encode/input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 4 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search36.param b/converter/ppm/ppmtompeg/examples/search36.param new file mode 100644 index 00000000..66fb8e7f --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search36.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard36.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR /n/picasso/users/keving/encode/input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 4 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search37.param b/converter/ppm/ppmtompeg/examples/search37.param new file mode 100644 index 00000000..f5f5d4d3 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search37.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard37.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR /n/picasso/users/keving/encode/input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 4 + +PSEARCH_ALG SUBSAMPLE +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search38.param b/converter/ppm/ppmtompeg/examples/search38.param new file mode 100644 index 00000000..b8186ce2 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search38.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard38.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR /n/picasso/users/keving/encode/input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 4 + +PSEARCH_ALG TWOLEVEL +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search39.param b/converter/ppm/ppmtompeg/examples/search39.param new file mode 100644 index 00000000..ab18d28b --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search39.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard39.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR /n/picasso/users/keving/encode/input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 4 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search4.param b/converter/ppm/ppmtompeg/examples/search4.param new file mode 100644 index 00000000..68285e6d --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search4.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard4.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 20 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/search40.param b/converter/ppm/ppmtompeg/examples/search40.param new file mode 100644 index 00000000..3fd65b6e --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search40.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard40.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR /n/picasso/users/keving/encode/input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 8 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search41.param b/converter/ppm/ppmtompeg/examples/search41.param new file mode 100644 index 00000000..3a47baf7 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search41.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard41.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR /n/picasso/users/keving/encode/input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 8 + +PSEARCH_ALG TWOLEVEL +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search42.param b/converter/ppm/ppmtompeg/examples/search42.param new file mode 100644 index 00000000..3ade2c85 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search42.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard42.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR /n/picasso/users/keving/encode/input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 8 + +PSEARCH_ALG SUBSAMPLE +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search43.param b/converter/ppm/ppmtompeg/examples/search43.param new file mode 100644 index 00000000..e2c044a4 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search43.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard43.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR /n/picasso/users/keving/encode/input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 8 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search44.param b/converter/ppm/ppmtompeg/examples/search44.param new file mode 100644 index 00000000..47c97e89 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search44.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard44.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR /n/picasso/users/keving/encode/input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 8 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search45.param b/converter/ppm/ppmtompeg/examples/search45.param new file mode 100644 index 00000000..c4131d33 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search45.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard45.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR /n/picasso/users/keving/encode/input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 8 + +PSEARCH_ALG SUBSAMPLE +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search46.param b/converter/ppm/ppmtompeg/examples/search46.param new file mode 100644 index 00000000..cc9c67bf --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search46.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard46.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR /n/picasso/users/keving/encode/input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 8 + +PSEARCH_ALG TWOLEVEL +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search47.param b/converter/ppm/ppmtompeg/examples/search47.param new file mode 100644 index 00000000..7527228e --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search47.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard47.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR /n/picasso/users/keving/encode/input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 8 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search48.param b/converter/ppm/ppmtompeg/examples/search48.param new file mode 100644 index 00000000..3d75a6a2 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search48.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/flowgard.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG SUBSAMPLE +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/search49.param b/converter/ppm/ppmtompeg/examples/search49.param new file mode 100644 index 00000000..c25ab724 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search49.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/flowgard.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG TWOLEVEL +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/search5.param b/converter/ppm/ppmtompeg/examples/search5.param new file mode 100644 index 00000000..5a1ad629 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search5.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard5.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 20 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/search50.param b/converter/ppm/ppmtompeg/examples/search50.param new file mode 100644 index 00000000..6711ebb7 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search50.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/flowgard.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/search51.param b/converter/ppm/ppmtompeg/examples/search51.param new file mode 100644 index 00000000..6609e381 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search51.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-19] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG EXHAUSTIVE + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/search52.5.param b/converter/ppm/ppmtompeg/examples/search52.5.param new file mode 100644 index 00000000..be34be4b --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search52.5.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/flowgard.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-15] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/search52.param b/converter/ppm/ppmtompeg/examples/search52.param new file mode 100644 index 00000000..246aa978 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search52.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/flowgard.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/search53.param b/converter/ppm/ppmtompeg/examples/search53.param new file mode 100644 index 00000000..81afb3bc --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search53.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/flowgard.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-15] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG EXHAUSTIVE + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/search6.param b/converter/ppm/ppmtompeg/examples/search6.param new file mode 100644 index 00000000..47c36a0c --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search6.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard6.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 20 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/search7.param b/converter/ppm/ppmtompeg/examples/search7.param new file mode 100644 index 00000000..f5d1224d --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search7.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard7.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL FULL + +RANGE 10 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search8.param b/converter/ppm/ppmtompeg/examples/search8.param new file mode 100644 index 00000000..f3b17e96 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search8.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard8.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 20 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/search9.param b/converter/ppm/ppmtompeg/examples/search9.param new file mode 100644 index 00000000..80051112 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/search9.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard9.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL FULL + +RANGE 20 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/seq.param b/converter/ppm/ppmtompeg/examples/seq.param new file mode 100644 index 00000000..665c22a7 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/seq.param @@ -0,0 +1,36 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/zonker/video/sequoia.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PPM +YUV_SIZE 320x240 + +INPUT_CONVERT /n/zonker/video/getframe * + +INPUT_DIR /n/zonker/video/seqframes + +INPUT +# really goes up to 200 +* [0-1000] +END_INPUT + +# quality parameters + +IQSCALE 15 +PQSCALE 18 +BQSCALE 30 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/sequoia.param b/converter/ppm/ppmtompeg/examples/sequoia.param new file mode 100644 index 00000000..1b93d880 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/sequoia.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/sequoia.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PPM +YUV_SIZE 352x240 + +INPUT_CONVERT djpeg * + +INPUT_DIR /n/zonker/video/seqframes + +INPUT +sequoia*.jpeg [378-600] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 22 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/sequoia19.param b/converter/ppm/ppmtompeg/examples/sequoia19.param new file mode 100644 index 00000000..52e5a72b --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/sequoia19.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/charlie-brown/project/mm/mpeg/mpeg_encode/output/seq.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PPM +YUV_SIZE 352x240 + +INPUT_CONVERT /n/zonker/video/seqframes/gframe /n/video/video/sequoia * 640x480.100 352 240 + +INPUT_DIR /n/zonker/video + +INPUT +* [0-199] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/sequoia2.param b/converter/ppm/ppmtompeg/examples/sequoia2.param new file mode 100644 index 00000000..676cc2e4 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/sequoia2.param @@ -0,0 +1,36 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/sequoia.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PPM +YUV_SIZE 352x240 + +INPUT_CONVERT djpeg * + +INPUT_DIR /n/zonker/video/seqframes + +INPUT +# really goes up to 200 +sequoia*.jpeg [001-200] +END_INPUT + +# quality parameters + +IQSCALE 15 +PQSCALE 18 +BQSCALE 30 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/simple.param b/converter/ppm/ppmtompeg/examples/simple.param new file mode 100644 index 00000000..14964829 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/simple.param @@ -0,0 +1,40 @@ +# Simple parameter file + +OUTPUT ./test.mpg +GOP_SIZE 15 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR /n/charlie-brown/project/mm/mpeg/mpeg_encode/input/flowg + +INPUT +sflowg.*.yuv [0-14] +END_INPUT + +PATTERN IBBPBBPBBPBBPBBPBBPBBP + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 4 + +# LOGARITHMIC, TWOLEVEL, SUBSAMPLE, EXHAUSTIVE +PSEARCH_ALG LOGARITHMIC + +# CROSS2, SIMPLE +BSEARCH_ALG CROSS2 + +# DECODED or ORIGINAL +REFERENCE_FRAME ORIGINAL + diff --git a/converter/ppm/ppmtompeg/examples/slice.param b/converter/ppm/ppmtompeg/examples/slice.param new file mode 100644 index 00000000..308d2053 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/slice.param @@ -0,0 +1,47 @@ +# test suite parameter file + +PATTERN IIIIIIIII +OUTPUT ts.mpg + +BASE_FILE_FORMAT PPM +INPUT_CONVERT giftoppm * +GOP_SIZE 1 +SLICES_PER_FRAME 170000 + +INPUT_DIR ts + +INPUT +med*.gif [030-034] +END_INPUT + + + +# all of the remaining options have to do with the motion search and qscale +# +# change this only if you're unsatisfied with the CPU time or quality, or +# are experimenting +# + +# if this appears in the file, then in addition to testing luminance when +# computing motion vectors, program will also take into account chrominance +# +# this option MUST appear before ERROR option, or it will be ignored +# +# CHROMINANCE + +# MAD or MSE -- must be upper case +ERROR MAD + +# FULL or HALF -- must be upper case +PIXEL HALF + +# means +/- this many pixels +RANGE 4 + +# YES or NO -- must be upper case +SUBSAMPLE NO + +IQSCALE 31 +PQSCALE 10 +BQSCALE 25 + diff --git a/converter/ppm/ppmtompeg/examples/slimed.param b/converter/ppm/ppmtompeg/examples/slimed.param new file mode 100644 index 00000000..b5938b94 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/slimed.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN I +OUTPUT /n/picasso/users/keving/encode/output/slimed.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PPM +YUV_SIZE 480x360 + +INPUT_CONVERT djpeg * + +INPUT_DIR ../input/slimed + +INPUT +slimed.*.jpeg [1-100] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 20 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/slowspeed.param b/converter/ppm/ppmtompeg/examples/slowspeed.param new file mode 100644 index 00000000..321313be --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/slowspeed.param @@ -0,0 +1,38 @@ +# speed test parameter file + +PATTERN ibbpbbpbbpbbpbb +OUTPUT /n/picasso/users/keving/encode/output/flowgard.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT cat *.Y *.U *.V + +INPUT_DIR /n/picasso/project/mm/mpeg/mpeg_encode/input/flowg + +INPUT +sflowg.* [0-149] +END_INPUT + +# motion vector search parameters + +# MAD or MSE -- must be upper case +ERROR MAD + +# FULL or HALF -- must be upper case +PIXEL HALF + +# means +/- this many pixels +RANGE 12 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG CROSS2 + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +REFERENCE_FRAME ORIGINAL + diff --git a/converter/ppm/ppmtompeg/examples/stanford.param b/converter/ppm/ppmtompeg/examples/stanford.param new file mode 100644 index 00000000..4c87334c --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/stanford.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/stanford.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 4 +PQSCALE 4 +BQSCALE 8 + +# motion vector search parameters + +PIXEL HALF + +RANGE 8 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/subtest.param b/converter/ppm/ppmtompeg/examples/subtest.param new file mode 100644 index 00000000..7fb4f585 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/subtest.param @@ -0,0 +1,34 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/flowgard18.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT SUB4 +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR /n/picasso/users/keving/encode/input/flowg/yuvsub + +INPUT +* [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/temp.param b/converter/ppm/ppmtompeg/examples/temp.param new file mode 100644 index 00000000..209fdfb4 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/temp.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/flow.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/flowg + +INPUT +sflowg.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/template.param b/converter/ppm/ppmtompeg/examples/template.param new file mode 100644 index 00000000..66b6dd98 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/template.param @@ -0,0 +1,154 @@ +# parameter file template with lots of comments to assist you +# +# you can use this as a template, copying it to a separate file then modifying +# the copy +# +# +# any line beginning with '#' is a comment +# +# no line should be longer than 255 characters +# +# +# general format of each line is: +# <option> <spaces and/or tabs> <value> +# +# lines can generally be in any order +# +# an exception is the option 'INPUT' which must be followed by input +# files in the order in which they must appear, followed by 'END_INPUT' +# +# Also, if you use the `command` method of generating input file names, +# the command will only be executed in the INPUT_DIR if INPUT_DIR preceeds +# the INPUT parameter. +# +# <option> MUST be in UPPER CASE +# + +PATTERN IBBPBBPBBPBBPBBP +OUTPUT output.mpg + +# mpeg_encode really only accepts 3 different file formats, but using a +# conversion statement it can effectively handle ANY file format +# +# You must specify the type of the input files. The choices are: +# YUV, PPM, JMOVIE, Y, JPEG, PNM +# (must be upper case) +# +BASE_FILE_FORMAT YUV + +# +# if YUV format (or using parallel version), must provide width and height +# YUV_SIZE widthxheight +# this option is ignored if BASE_FILE_FORMAT is not YUV and you're running +# on just one machine +# +YUV_SIZE 352x240 + +# If you are using YUV, there are different supported file formats. +# EYUV or UCB are the same as previous versions of this encoder. +# (All the Y's, then U's then V's, in 4:2:0 subsampling.) +# Other formats, such as Abekas, Phillips, or a general format are +# permissible, the general format is a string of Y's, U's, and V's +# to specify the file order. + +INPUT_FORMAT UCB + +# the conversion statement +# +# Each occurrence of '*' will be replaced by the input file +# +# e.g., if you have a bunch of GIF files, then this might be: +# INPUT_CONVERT giftoppm * +# +# e.g., if you have a bunch of files like a.Y a.U a.V, etc., then: +# INPUT_CONVERT cat *.Y *.U *.V +# +# e.g., if you are grabbing from laser disc you might have something like +# INPUT_CONVERT goto frame *; grabppm +# 'INPUT_CONVERT *' means the files are already in the base file format +# +INPUT_CONVERT * + +# number of frames in a GOP. +# +# since each GOP must have at least one I-frame, the encoder will find the +# the first I-frame after GOP_SIZE frames to start the next GOP +# +# later, will add more flexible GOP signalling +# +GOP_SIZE 16 + +# number of slices in a frame +# +# 1 is a good number. another possibility is the number of macroblock rows +# (which is the height divided by 16) +# +SLICES_PER_FRAME 1 + +# directory to get all input files from (makes this file easier to read) +INPUT_DIR ../input/tennis + +# There are a bunch of ways to specify the input files. +# from a simple one-per-line listing, to the following +# way of numbering them. See the manual for more information. +INPUT +# '*' is replaced by the numbers 01, 02, 03, 04 +# if I instead do [01-11], it would be 01, 02, ..., 09, 10, 11 +# if I instead do [1-11], it would be 1, 2, 3, ..., 9, 10, 11 +# if I instead do [1-11+3], it would be 1, 4, 7, 10 +# the program assumes none of your input files has a name ending in ']' +# if you do, too bad!!! +# +# +stennis.*.yuv [0-23] +# can have more files here if you want...there is no limit on the number +# of files +END_INPUT + + + +# Many of the remaining options have to do with the motion search and qscale + +# FULL or HALF -- must be upper case +PIXEL HALF + +# means +/- this many pixels for both P and B frame searches +# specify two numbers if you wish to serc different ranges in the two. +RANGE 10 + +# this must be one of {EXHAUSTIVE, SUBSAMPLE, LOGARITHMIC} +PSEARCH_ALG LOGARITHMIC + +# this must be one of {SIMPLE, CROSS2, EXHAUSTIVE} +# +# note that EXHAUSTIVE is really, really, really slow +# +BSEARCH_ALG CROSS2 + +# +# these specify the q-scale for I, P, and B frames +# (values must be between 1 and 31) +# These are the Qscale values for the entire frame in variable bit-rate +# mode, and starting points (but not important) for constant bit rate +# +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# this must be ORIGINAL or DECODED +REFERENCE_FRAME ORIGINAL + +# for parallel parameters see parallel.param in the exmaples subdirectory + +# if you want constant bit-rate mode, specify it as follows (number is bits/sec): +BIT_RATE 1000000 + +# To specify the buffer size (327680 is default, measused in bits, for 16bit words) +BUFFER_SIZE 327680 + +# The frame rate is the number of frames/second (legal values: +# 23.976, 24, 25, 29.97, 30, 50 ,59.94, 60 +FRAME_RATE 30 + +# There are many more options, see the users manual for examples.... +# ASPECT_RATIO, USER_DATA, GAMMA, IQTABLE, etc. diff --git a/converter/ppm/ppmtompeg/examples/tennis.param b/converter/ppm/ppmtompeg/examples/tennis.param new file mode 100644 index 00000000..978e1904 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/tennis.param @@ -0,0 +1,37 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT output/tennis.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT cat *.Y *.U *.V + +INPUT_DIR links/tennis + +INPUT +stennis.* [0-149] +END_INPUT + + +# motion vector search parameters + +# MAD or MSE -- must be upper case +ERROR MAD + +# FULL or HALF -- must be upper case +PIXEL HALF + +# means +/- this many pixels +RANGE 8 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG CROSS2 + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + diff --git a/converter/ppm/ppmtompeg/examples/tennis18.param b/converter/ppm/ppmtompeg/examples/tennis18.param new file mode 100644 index 00000000..48979f3e --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/tennis18.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/tennis.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/tennis + +INPUT +stennis.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/tennis19.5.param b/converter/ppm/ppmtompeg/examples/tennis19.5.param new file mode 100644 index 00000000..196b7a28 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/tennis19.5.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/tennis.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/tennis + +INPUT +stennis.*.yuv [0-15] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/tennis19.param b/converter/ppm/ppmtompeg/examples/tennis19.param new file mode 100644 index 00000000..542c237c --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/tennis19.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/tennis.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/tennis + +INPUT +stennis.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/tennis48.param b/converter/ppm/ppmtompeg/examples/tennis48.param new file mode 100644 index 00000000..9db63638 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/tennis48.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/tennis.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/tennis + +INPUT +stennis.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG SUBSAMPLE +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/tennis49.param b/converter/ppm/ppmtompeg/examples/tennis49.param new file mode 100644 index 00000000..cbecb035 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/tennis49.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/tennis.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/tennis + +INPUT +stennis.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG TWOLEVEL +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/tennis50.param b/converter/ppm/ppmtompeg/examples/tennis50.param new file mode 100644 index 00000000..bbfe3cb0 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/tennis50.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/tennis.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/tennis + +INPUT +stennis.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/tennis52.5.param b/converter/ppm/ppmtompeg/examples/tennis52.5.param new file mode 100644 index 00000000..a00e4322 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/tennis52.5.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/tennis.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/tennis + +INPUT +stennis.*.yuv [0-15] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/tennis52.param b/converter/ppm/ppmtompeg/examples/tennis52.param new file mode 100644 index 00000000..0ae84f00 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/tennis52.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/tennis.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/tennis + +INPUT +stennis.*.yuv [0-149] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/tennis53.param b/converter/ppm/ppmtompeg/examples/tennis53.param new file mode 100644 index 00000000..bc311376 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/tennis53.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/tennis.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/tennis + +INPUT +stennis.*.yuv [0-15] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG EXHAUSTIVE + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/tennisbug.param b/converter/ppm/ppmtompeg/examples/tennisbug.param new file mode 100644 index 00000000..4a655b4a --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/tennisbug.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IPPPPPPPPPPP + +OUTPUT /n/picasso/users/keving/encode/output/tennis.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR ../input/tennis + +INPUT +stennis.*.yuv [0-49] +END_INPUT + +# quality parameters + +IQSCALE 1 +PQSCALE 1 +BQSCALE 1 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/testp.param b/converter/ppm/ppmtompeg/examples/testp.param new file mode 100644 index 00000000..ea8dee18 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/testp.param @@ -0,0 +1,45 @@ +# speed test parameter file + +PATTERN IPPPPPPPPP + +OUTPUT /n/picasso/users/keving/encode/output/testp.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PPM +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR /n/picasso/users/keving/encode/input + +INPUT +me.0.ppm +me.0.ppm +me.0.ppm +me.0.ppm +me.0.ppm +me.0.ppm +me.0.ppm +me.0.ppm +me.0.ppm +me.0.ppm +me.0.ppm +END_INPUT + +# quality parameters + +IQSCALE 4 +PQSCALE 4 +BQSCALE 4 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/walk.param b/converter/ppm/ppmtompeg/examples/walk.param new file mode 100644 index 00000000..52be9d2e --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/walk.param @@ -0,0 +1,61 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/walk1.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PPM +YUV_SIZE 320x240 + +INPUT_CONVERT zcat * + +INPUT_DIR /n/zonker/cluster/keving/walk + +INPUT +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +f.*.Z [1-1800] +END_INPUT + +PARALLEL_TEST_FRAMES 10 +PARALLEL_TIME_CHUNKS 30 + + +IO_SERVER_CONVERT * +SLAVE_CONVERT zcat * + +PARALLEL +#charlie-brown keving ~keving/encode/bin/dec-alpha/mpeg_encode +#woodstock keving ~keving/encode/bin/dec-alpha/mpeg_encode +END_PARALLEL + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/walk1.param b/converter/ppm/ppmtompeg/examples/walk1.param new file mode 100644 index 00000000..52be9d2e --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/walk1.param @@ -0,0 +1,61 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/walk1.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PPM +YUV_SIZE 320x240 + +INPUT_CONVERT zcat * + +INPUT_DIR /n/zonker/cluster/keving/walk + +INPUT +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +f.*.Z [1-1800] +END_INPUT + +PARALLEL_TEST_FRAMES 10 +PARALLEL_TIME_CHUNKS 30 + + +IO_SERVER_CONVERT * +SLAVE_CONVERT zcat * + +PARALLEL +#charlie-brown keving ~keving/encode/bin/dec-alpha/mpeg_encode +#woodstock keving ~keving/encode/bin/dec-alpha/mpeg_encode +END_PARALLEL + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/walk18.param b/converter/ppm/ppmtompeg/examples/walk18.param new file mode 100644 index 00000000..80ed48f3 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/walk18.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/walk19.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PPM +YUV_SIZE 320x240 + +INPUT_CONVERT zcat * + +INPUT_DIR /n/zonker/cluster/keving/walk + +INPUT +f.*.Z [1-200] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/walk19.5.param b/converter/ppm/ppmtompeg/examples/walk19.5.param new file mode 100644 index 00000000..7fb724d1 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/walk19.5.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/walk19.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PPM +YUV_SIZE 320x240 + +INPUT_CONVERT zcat * + +INPUT_DIR /n/zonker/cluster/keving/walk + +INPUT +f.*.Z [1-16] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/walk19.param b/converter/ppm/ppmtompeg/examples/walk19.param new file mode 100644 index 00000000..acc8f627 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/walk19.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/walk19.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PPM +YUV_SIZE 320x240 + +INPUT_CONVERT zcat * + +INPUT_DIR /n/zonker/cluster/keving/walk + +INPUT +f.*.Z [1-200] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/walk2.param b/converter/ppm/ppmtompeg/examples/walk2.param new file mode 100644 index 00000000..cbb28705 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/walk2.param @@ -0,0 +1,61 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/walk2.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PPM +YUV_SIZE 320x240 + +INPUT_CONVERT zcat * + +INPUT_DIR /n/zonker/cluster/keving/walk + +INPUT +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +f.*.Z [1-1800] +END_INPUT + +PARALLEL_TEST_FRAMES 10 +PARALLEL_TIME_CHUNKS 30 + + +IO_SERVER_CONVERT * +SLAVE_CONVERT zcat * + +PARALLEL +#charlie-brown keving ~keving/encode/bin/dec-alpha/mpeg_encode +#woodstock keving ~keving/encode/bin/dec-alpha/mpeg_encode +END_PARALLEL + +# quality parameters + +IQSCALE 6 +PQSCALE 8 +BQSCALE 15 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/examples/walk3.param b/converter/ppm/ppmtompeg/examples/walk3.param new file mode 100644 index 00000000..86a30812 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/walk3.param @@ -0,0 +1,61 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/walk3.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PPM +YUV_SIZE 320x240 + +INPUT_CONVERT zcat * + +INPUT_DIR /n/zonker/cluster/keving/walk + +INPUT +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +f.*.Z [1-1800] +END_INPUT + +PARALLEL_TEST_FRAMES 10 +PARALLEL_TIME_CHUNKS 30 + + +IO_SERVER_CONVERT * +SLAVE_CONVERT zcat * + +PARALLEL +#charlie-brown keving ~keving/encode/bin/dec-alpha/mpeg_encode +#woodstock keving ~keving/encode/bin/dec-alpha/mpeg_encode +END_PARALLEL + +# quality parameters + +IQSCALE 6 +PQSCALE 8 +BQSCALE 15 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG TWOLEVEL +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/walk4.param b/converter/ppm/ppmtompeg/examples/walk4.param new file mode 100644 index 00000000..12d1f505 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/walk4.param @@ -0,0 +1,61 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +OUTPUT /n/picasso/users/keving/encode/output/walk4.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PPM +YUV_SIZE 320x240 + +INPUT_CONVERT zcat * + +INPUT_DIR /n/zonker/cluster/keving/walk + +INPUT +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +f.*.Z [1-1800] +END_INPUT + +PARALLEL_TEST_FRAMES 10 +PARALLEL_TIME_CHUNKS 30 + + +IO_SERVER_CONVERT * +SLAVE_CONVERT zcat * + +PARALLEL +#charlie-brown keving ~keving/encode/bin/dec-alpha/mpeg_encode +#woodstock keving ~keving/encode/bin/dec-alpha/mpeg_encode +END_PARALLEL + +# quality parameters + +IQSCALE 4 +PQSCALE 6 +BQSCALE 10 + +# motion vector search parameters + +PIXEL HALF + +RANGE 7 + +PSEARCH_ALG TWOLEVEL +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/walk48.param b/converter/ppm/ppmtompeg/examples/walk48.param new file mode 100644 index 00000000..2c6df697 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/walk48.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/walk19.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PPM +YUV_SIZE 320x240 + +INPUT_CONVERT zcat * + +INPUT_DIR /n/zonker/cluster/keving/walk + +INPUT +f.*.Z [1-200] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG SUBSAMPLE +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/walk49.param b/converter/ppm/ppmtompeg/examples/walk49.param new file mode 100644 index 00000000..72c17a94 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/walk49.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/walk19.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PPM +YUV_SIZE 320x240 + +INPUT_CONVERT zcat * + +INPUT_DIR /n/zonker/cluster/keving/walk + +INPUT +f.*.Z [1-200] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG TWOLEVEL +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/walk5.param b/converter/ppm/ppmtompeg/examples/walk5.param new file mode 100644 index 00000000..e792064f --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/walk5.param @@ -0,0 +1,62 @@ +# speed test parameter file + +#PATTERN IBBPBBPBBPBBPBB +PATTERN I +OUTPUT /n/picasso/users/keving/encode/output/walk5.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PNM +YUV_SIZE 320x240 + +INPUT_CONVERT zcat * + +INPUT_DIR /n/zonker/cluster/keving/walk + +INPUT +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +f.*.Z [1-80] +END_INPUT + +PARALLEL_TEST_FRAMES 10 +PARALLEL_TIME_CHUNKS 30 + + +IO_SERVER_CONVERT * +SLAVE_CONVERT zcat * + +PARALLEL +#charlie-brown keving ~keving/encode/bin/dec-alpha/mpeg_encode +#woodstock keving ~keving/encode/bin/dec-alpha/mpeg_encode +END_PARALLEL + +# quality parameters + +IQSCALE 1 +PQSCALE 6 +BQSCALE 10 + +# motion vector search parameters + +PIXEL HALF + +RANGE 7 + +PSEARCH_ALG TWOLEVEL +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/walk50.param b/converter/ppm/ppmtompeg/examples/walk50.param new file mode 100644 index 00000000..8f4d27e6 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/walk50.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/walk19.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PPM +YUV_SIZE 320x240 + +INPUT_CONVERT zcat * + +INPUT_DIR /n/zonker/cluster/keving/walk + +INPUT +f.*.Z [1-200] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG EXHAUSTIVE +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/walk52.5.param b/converter/ppm/ppmtompeg/examples/walk52.5.param new file mode 100644 index 00000000..f9d038f6 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/walk52.5.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/walk19.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PPM +YUV_SIZE 320x240 + +INPUT_CONVERT zcat * + +INPUT_DIR /n/zonker/cluster/keving/walk + +INPUT +f.*.Z [1-16] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/walk52.param b/converter/ppm/ppmtompeg/examples/walk52.param new file mode 100644 index 00000000..69626d2c --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/walk52.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/walk19.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PPM +YUV_SIZE 320x240 + +INPUT_CONVERT zcat * + +INPUT_DIR /n/zonker/cluster/keving/walk + +INPUT +f.*.Z [1-200] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG SIMPLE + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/walk53.param b/converter/ppm/ppmtompeg/examples/walk53.param new file mode 100644 index 00000000..72f57c7a --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/walk53.param @@ -0,0 +1,35 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/walk19.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PPM +YUV_SIZE 320x240 + +INPUT_CONVERT zcat * + +INPUT_DIR /n/zonker/cluster/keving/walk + +INPUT +f.*.Z [1-16] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG EXHAUSTIVE + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/examples/walk93.param b/converter/ppm/ppmtompeg/examples/walk93.param new file mode 100644 index 00000000..243d6032 --- /dev/null +++ b/converter/ppm/ppmtompeg/examples/walk93.param @@ -0,0 +1,62 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB +#PATTERN I +OUTPUT /n/picasso/users/keving/encode/output/walk93.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +BASE_FILE_FORMAT PPM +YUV_SIZE 320x240 + +INPUT_CONVERT zcat * + +INPUT_DIR /n/zonker/cluster/keving/walk + +INPUT +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +title.ppm.Z +f.*.Z [1-2] +END_INPUT + +PARALLEL_TEST_FRAMES 10 +PARALLEL_TIME_CHUNKS 30 + + +IO_SERVER_CONVERT * +SLAVE_CONVERT zcat * + +PARALLEL +#charlie-brown keving ~keving/encode/bin/dec-alpha/mpeg_encode +#woodstock keving ~keving/encode/bin/dec-alpha/mpeg_encode +END_PARALLEL + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 7 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME DECODED diff --git a/converter/ppm/ppmtompeg/file.c b/converter/ppm/ppmtompeg/file.c new file mode 100644 index 00000000..ae741962 --- /dev/null +++ b/converter/ppm/ppmtompeg/file.c @@ -0,0 +1,322 @@ +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/* + * $Header: /n/picasso/project/mm/mpeg/mpeg_dist/mpeg_encode/RCS/file.c,v 1.2 1993/06/30 20:06:09 keving Exp $ + * $Log: file.c,v $ + * Revision 1.2 1993/06/30 20:06:09 keving + * nothing + * + * Revision 1.1 1993/06/03 21:08:08 keving + * nothing + * + */ + +#include "tk.h" + +#include "all.h" + +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <time.h> +#include <string.h> +#include <dirent.h> +#include <strings.h> + +#define MAX_FILES 1000 +#define MAX_NAME_LEN 256 +#define MAX_STRING_LEN MAX_NAME_LEN + +typedef int boolean; +#define TRUE 1 +#define FALSE 0 + +extern char currentPath[MAXPATHLEN]; + +char globString[1024]; + +static DIR *dfd; + +void ResetPath(void); +int ListDirectory(ClientData nulldata, Tcl_Interp *interp, int argc, + char **argv); +int ChangeDirectory(ClientData nulldata, Tcl_Interp *interp, int argc, + char **argv); +void SortFiles(int numStrings, char strings[MAX_FILES][MAX_NAME_LEN], + boolean *dirList, int permute[]); + +static void UpdatePath(Tcl_Interp *interp, char *directory); +static boolean MatchesGlob(char *string, char *glob); + + + +void ResetPath() +{ + if ( getwd(currentPath) == 0 ) + { + fprintf(stderr, "Error getting pathname!!!\n"); + exit(1); + } + + strcpy(¤tPath[strlen(currentPath)], "/"); + + dfd = opendir(currentPath); + if ( dfd == NULL ) + { + fprintf(stderr, "can't open '%s'\n", currentPath); + exit(1); + } +} + + +static void UpdatePath(Tcl_Interp *interp, char *directory) +{ + int length; + char *charPtr; + + length = strlen(currentPath); + + if ( strcmp(directory, "./") == 0 ) + return /* nothing */ ; + else if ( strcmp(directory, "../") == 0 ) + { + /* delete backwards up to '/' */ + + if ( length < 2 ) + { + fprintf(stderr, "Error: backing up from root directory!!!\n"); + exit(1); + } + + charPtr = ¤tPath[length-2]; + while ( (charPtr != currentPath) && (*charPtr != '/') ) + charPtr--; + charPtr++; /* leave the '/' */ + *charPtr = '\0'; + } + else + { + strcpy(¤tPath[length], directory); + } +} + + +int ChangeDirectory(ClientData nulldata, Tcl_Interp *interp, int argc, + char **argv) +{ + char *directory = argv[1]; + + UpdatePath(interp, directory); + + fprintf(stdout, "Opening directory: '%s'\n", currentPath); + + dfd = opendir(currentPath); + if ( dfd == NULL ) + { + fprintf(stderr, "can't open '%s'\n", currentPath); + return TCL_OK; /* shouldn't, really */ + } + + return TCL_OK; +} + + +int ListDirectory(ClientData nulldata, Tcl_Interp *interp, int argc, + char **argv) +{ + struct dirent *dp; + struct stat stbuf; + char command[256]; + char fileName[MAX_FILES][MAX_NAME_LEN]; + boolean dirList[MAX_FILES]; + int permute[MAX_FILES]; + int fileCount = 0; + register int index; + char fullName[MAXPATHLEN]; + char *restPtr; + + sprintf(command, "ShowCurrentDirectory %s", currentPath); + Tcl_Eval(interp, command, 0, (char **) NULL); + + if ( dfd == NULL ) + { + fprintf(stderr, "TRIED TO LIST NULL DIRECTORY\n"); + + return TCL_OK; + } + +/* check if root directory */ + if ( strlen(currentPath) != 1 ) + { + sprintf(fileName[fileCount], "../"); + dirList[fileCount] = TRUE; + fileCount++; + } + + strcpy(fullName, currentPath); + restPtr = &fullName[strlen(fullName)]; + + while ( (dp = readdir(dfd)) != NULL ) + { + strcpy(restPtr, dp->d_name); + stat(fullName, &stbuf); + + if ( dp->d_name[0] != '.' ) + { + if ( S_ISDIR(stbuf.st_mode) ) + { + sprintf(fileName[fileCount], "%s/", dp->d_name); + dirList[fileCount] = TRUE; + fileCount++; + } + else + { + if ( MatchesGlob(dp->d_name, globString) ) + { + strcpy(fileName[fileCount], dp->d_name); + dirList[fileCount] = FALSE; + fileCount++; + } + } + } + } + + SortFiles(fileCount, fileName, dirList, permute); + + for ( index = 0; index < fileCount; index++ ) + { + sprintf(command, "AddBrowseFile %s", fileName[permute[index]]); + Tcl_Eval(interp, command, 0, (char **) NULL); + } + + closedir(dfd); + + return TCL_OK; +} + + +void SortFiles(int numStrings, char strings[MAX_FILES][MAX_NAME_LEN], + boolean *dirList, int permute[]) +{ + register int i, j; + int temp; + int numDirs; + int ptr; + + for ( i = 0; i < numStrings; i++ ) + permute[i] = i; + + /* put all directories at front */ + numDirs = 0; + ptr = numStrings-1; + while ( numDirs != ptr ) + { + /* go past dirs */ + while ( (numDirs < ptr) && (dirList[permute[numDirs]]) ) + numDirs++; + + /* go past non-dirs */ + while ( (numDirs < ptr) && (! dirList[permute[ptr]]) ) + ptr--; + + if ( numDirs != ptr ) + { + temp = permute[numDirs]; + permute[numDirs] = ptr; + permute[ptr] = temp; + } + } + + if ( dirList[permute[numDirs]] ) + numDirs++; + + for ( i = 0; i < numDirs; i++ ) + for ( j = i+1; j < numDirs; j++ ) + { + if ( strcmp(&strings[permute[j]][0], &strings[permute[i]][0]) < 0 ) + { + temp = permute[j]; + permute[j] = permute[i]; + permute[i] = temp; + } + } + + for ( i = numDirs; i < numStrings; i++ ) + for ( j = i+1; j < numStrings; j++ ) + { + if ( strcmp(&strings[permute[j]][0], &strings[permute[i]][0]) < 0 ) + { + temp = permute[j]; + permute[j] = permute[i]; + permute[i] = temp; + } + } +} + + +int SetBrowseGlob (ClientData nulldata, Tcl_Interp *interp, + int argc, char **argv) +{ + if (argc == 2 ) + { + strcpy(globString, argv[1]); + + fprintf(stdout, "GLOB: %s\n", globString); + + return TCL_OK; + } + + Tcl_AppendResult (interp, + "wrong args: should be \"", argv[0]," string\"", (char *) NULL); + return TCL_ERROR; +} + + +static boolean MatchesGlob(char *string, char *glob) +{ + char *stringRight, *globRight; + + while ( (*glob != '\0') && (*glob != '*') ) /* match left side */ + { + if ( (*string == '\0') || (*string != *glob) ) + return FALSE; + string++; + glob++; + } + + if ( *glob == '\0' ) /* no star */ + return TRUE; + + /* now match right side */ + stringRight = &string[strlen(string)-1]; + globRight = &glob[strlen(glob)-1]; + + while ( *globRight != '*' ) + { + if ( (stringRight < string) || (*stringRight != *globRight) ) + return FALSE; + globRight--; + stringRight--; + } + + return TRUE; +} diff --git a/converter/ppm/ppmtompeg/frame.c b/converter/ppm/ppmtompeg/frame.c new file mode 100644 index 00000000..09f46f66 --- /dev/null +++ b/converter/ppm/ppmtompeg/frame.c @@ -0,0 +1,836 @@ +/*===========================================================================* + * frame.c * + * * + * basic frame procedures * + * * + * EXPORTED PROCEDURES: * + * Frame_Init * + * Frame_Exit * + * Frame_New * + * Frame_Free * + * Frame_AllocBlocks * + * Frame_AllocYCC * + * Frame_AllocDecoded * + * Frame_AllocHalf * + * Frame_Resize * + * * + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + + +#include "mallocvar.h" + +#include "all.h" +#include "mtypes.h" +#include "frames.h" +#include "frame.h" +#include "fsize.h" +#include "dct.h" + +/*===========* + * CONSTANTS * + *===========*/ + +/* The maximum number of B-Frames allowed between reference frames. */ +#define B_FRAME_RUN 16 + +/*==================* + * GLOBAL VARIABLES * + *==================*/ + +static MpegFrame * frameMemory[B_FRAME_RUN+2]; +static unsigned int numOfFrames; + + +/*==================================================== +* Resize_Array_Width +* +* This function will resize any array width up +* or down in size. The algorithm is based on the +* least common multiple approach more commonly +* used in audio frequency adjustments. +*=====================================================*/ +static void +Resize_Array_Width(uint8 ** const inarray, + int const in_x, + int const in_y, + uint8 ** const outarray, + int const out_x) { + + unsigned int i; + int in_total; + int out_total; + uint8 *inptr; + uint8 *outptr; + uint8 pointA,pointB; + /* double slope,diff; */ + + for (i = 0; i < in_y; ++i) { /* For each row */ + unsigned int j; + inptr = &inarray[i][0]; + outptr = &outarray[i][0]; + in_total = 0; + out_total = 0; + for (j=0; j < out_x; ++j) { /* For each output value */ + if (in_total == out_total) { + *outptr = *inptr; + outptr++; + out_total=out_total+in_x; + while(in_total < out_total){ + in_total = in_total + out_x; + ++inptr; + } + if (in_total > out_total) { + in_total = in_total - out_x; + --inptr; + } + } else { + pointA = *inptr; + ++inptr; + pointB = *inptr; + --inptr; +#if 0 + /*Interpolative solution */ + slope = ((double)(pointB -pointA))/((double)(out_x)); + diff = (((double)(out_total - in_total))); + if (diff < (out_x/2)){ + *outptr = (pointA + (uint8)(slope*diff)); + } else { + *outptr = (pointB - + (uint8)(slope*(((float)(out_x)) - diff))); + } +#endif + /* Non-Interpolative solution */ + *outptr = *inptr; + + ++outptr; + out_total = out_total + in_x; + while(in_total < out_total) { + in_total = in_total + out_x; + ++inptr; + } + if (in_total > out_total) { + in_total = in_total - out_x; + --inptr; + } + } /* end if */ + } /* end for each output value */ + } /* end for each row */ +} /* end main */ + + + +/*============================== +* Resize_Array_Height +* +* Resize any array height larger or smaller. +* Same as Resize_array_Width except pointer +* manipulation must change. +*===============================*/ +static void +Resize_Array_Height(uint8 ** const inarray, + int const in_x, + int const in_y, + uint8 ** const outarray, + int const out_y) { + + unsigned int i; + + for(i=0; i < in_x; ++i){ /* for each column */ + int in_total; + int out_total; + uint8 pointA, pointB; + double slope, diff; + unsigned int j; + int k; + + in_total = 0; + out_total = 0; + k = 0; + for(j=0; j < out_y; ++j){ /* for each output value */ + if (in_total == out_total) { + outarray[j][i] = inarray[k][i]; + out_total=out_total+in_y; + while(in_total < out_total){ + in_total = in_total + out_y; + ++k; + } + if (in_total > out_total) { + in_total = in_total - out_y; + --k; + } + } else { + pointA = inarray[k][i]; + if (k != (in_y -1)) { + pointB = inarray[k+1][i]; + } else + pointB = pointA; + /* Interpolative case */ + slope = ((double)(pointB -pointA))/(double)(out_y); + diff = (double)(out_total - in_total); + /* outarray[j][i] = (inarray[k][i] + (uint8)(slope*diff)); */ + /* Non-Interpolative case */ + outarray[j][i] = inarray[k][i]; + out_total = out_total + in_y; + while (in_total < out_total) { + in_total = in_total + out_y; + ++k; + } + if (in_total > out_total){ + in_total = in_total - out_y; + --k; + } + } + } + } +} + + + +/*======================================================== +* Resize_Width +*======================================================*/ +static void +Resize_Width(MpegFrame * const omfrw, + MpegFrame * const mfrw, + int const in_x, + int const in_y, + int const out_x) { + + int y; + + omfrw->orig_y = NULL; + Fsize_x = out_x; + + /* Allocate new frame memory */ + MALLOCARRAY(omfrw->orig_y, Fsize_y); + ERRCHK(omfrw->orig_y, "malloc"); + for (y = 0; y < Fsize_y; ++y) { + MALLOCARRAY(omfrw->orig_y[y], out_x); + ERRCHK(omfrw->orig_y[y], "malloc"); + } + + MALLOCARRAY(omfrw->orig_cr, Fsize_y / 2); + ERRCHK(omfrw->orig_cr, "malloc"); + for (y = 0; y < Fsize_y / 2; ++y) { + MALLOCARRAY(omfrw->orig_cr[y], out_x / 2); + ERRCHK(omfrw->orig_cr[y], "malloc"); + } + + MALLOCARRAY(omfrw->orig_cb, Fsize_y / 2); + ERRCHK(omfrw->orig_cb, "malloc"); + for (y = 0; y < Fsize_y / 2; ++y) { + MALLOCARRAY(omfrw->orig_cb[y], out_x / 2); + ERRCHK(omfrw->orig_cb[y], "malloc"); + } + + if (referenceFrame == ORIGINAL_FRAME) { + omfrw->ref_y = omfrw->orig_y; + omfrw->ref_cr = omfrw->orig_cr; + omfrw->ref_cb = omfrw->orig_cb; + } + + /* resize each component array separately */ + Resize_Array_Width(mfrw->orig_y, in_x, in_y, omfrw->orig_y, out_x); + Resize_Array_Width(mfrw->orig_cr, (in_x/2), (in_y/2), omfrw->orig_cr, + (out_x/2)); + Resize_Array_Width(mfrw->orig_cb, (in_x/2), (in_y/2), omfrw->orig_cb, + (out_x/2)); + + /* Free old frame memory */ + if (mfrw->orig_y) { + unsigned int i; + for (i = 0; i < in_y; ++i) { + free(mfrw->orig_y[i]); + } + free(mfrw->orig_y); + + for (i = 0; i < in_y / 2; ++i) { + free(mfrw->orig_cr[i]); + } + free(mfrw->orig_cr); + + for (i = 0; i < in_y / 2; ++i) { + free(mfrw->orig_cb[i]); + } + free(mfrw->orig_cb); + } +} + + + +/*======================================================= +* Resize_Height +* +* Resize Frame height up or down +*=======================================================*/ +static void +Resize_Height(MpegFrame * const omfrh, + MpegFrame * const mfrh, + int const in_x, + int const in_y, + int const out_y) { + + unsigned int y; + + Fsize_y = out_y; + + /* Allocate new frame memory */ + MALLOCARRAY(omfrh->orig_y, out_y); + ERRCHK(omfrh->orig_y, "malloc"); + for (y = 0; y < out_y; ++y) { + MALLOCARRAY(omfrh->orig_y[y], Fsize_x); + ERRCHK(omfrh->orig_y[y], "malloc"); + } + + MALLOCARRAY(omfrh->orig_cr, out_y / 2); + ERRCHK(omfrh->orig_cr, "malloc"); + for (y = 0; y < out_y / 2; ++y) { + MALLOCARRAY(omfrh->orig_cr[y], Fsize_x / 2); + ERRCHK(omfrh->orig_cr[y], "malloc"); + } + + MALLOCARRAY(omfrh->orig_cb, out_y / 2); + ERRCHK(omfrh->orig_cb, "malloc"); + for (y = 0; y < out_y / 2; ++y) { + MALLOCARRAY(omfrh->orig_cb[y], Fsize_x / 2); + ERRCHK(omfrh->orig_cb[y], "malloc"); + } + + if (referenceFrame == ORIGINAL_FRAME) { + omfrh->ref_y = omfrh->orig_y; + omfrh->ref_cr = omfrh->orig_cr; + omfrh->ref_cb = omfrh->orig_cb; + } + + /* resize component arrays separately */ + Resize_Array_Height(mfrh->orig_y, in_x, in_y, omfrh->orig_y, out_y); + Resize_Array_Height(mfrh->orig_cr, (in_x/2), (in_y/2), omfrh->orig_cr, + (out_y/2)); + Resize_Array_Height(mfrh->orig_cb, (in_x/2), (in_y/2), omfrh->orig_cb, + (out_y/2)); + + /* Free old frame memory */ + if (mfrh->orig_y) { + unsigned int i; + for (i = 0; i < in_y; ++i) { + free(mfrh->orig_y[i]); + } + free(mfrh->orig_y); + + for (i = 0; i < in_y / 2; ++i) { + free(mfrh->orig_cr[i]); + } + free(mfrh->orig_cr); + + for (i = 0; i < in_y / 2; ++i) { + free(mfrh->orig_cb[i]); + } + free(mfrh->orig_cb); + } +} + + + +/*===========================================================================* + * + * Frame_Init + * + * initializes the memory associated with all frames ever + * If the input is not coming in from stdin, only 3 frames are needed ; + * else, the program must create frames equal to the greatest distance + * between two reference frames to hold the B frames while it is parsing + * the input from stdin. + * + * RETURNS: nothing + * + * SIDE EFFECTS: frameMemory, numOfFrames + * + *===========================================================================*/ +void +Frame_Init(unsigned int const numOfFramesRequested) { + int idx; + + numOfFrames = numOfFramesRequested; + + for (idx = 0; idx < numOfFrames; ++idx) { + MALLOCVAR(frameMemory[idx]); + frameMemory[idx]->inUse = FALSE; + frameMemory[idx]->orig_y = NULL; + frameMemory[idx]->y_blocks = NULL; + frameMemory[idx]->decoded_y = NULL; + frameMemory[idx]->halfX = NULL; + frameMemory[idx]->next = NULL; + } +} + + +/*===========================================================================* + * + * FreeFrame + * + * frees the memory associated with the given frame + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +static void +FreeFrame(MpegFrame * const frameP) { + + if (frameP) { + if (frameP->orig_y) { + unsigned int i; + for (i = 0; i < Fsize_y; ++i) + free(frameP->orig_y[i]); + free(frameP->orig_y); + + for (i = 0; i < (Fsize_y / 2); ++i) + free(frameP->orig_cr[i]); + free(frameP->orig_cr); + + for (i = 0; i < (Fsize_y / 2); ++i) + free(frameP->orig_cb[i]); + free(frameP->orig_cb); + } + if (frameP->decoded_y) { + unsigned int i; + for (i = 0; i < Fsize_y; ++i) + free(frameP->decoded_y[i]); + free(frameP->decoded_y); + + for (i = 0; i < (Fsize_y / 2); ++i) + free(frameP->decoded_cr[i]); + free(frameP->decoded_cr); + + for (i = 0; i < (Fsize_y / 2); ++i) + free(frameP->decoded_cb[i]); + free(frameP->decoded_cb); + } + + if (frameP->y_blocks) { + unsigned int i; + for (i = 0; i < Fsize_y / DCTSIZE; ++i) + free(frameP->y_blocks[i]); + free(frameP->y_blocks); + + for (i = 0; i < Fsize_y / (2 * DCTSIZE); ++i) + free(frameP->cr_blocks[i]); + free(frameP->cr_blocks); + + for (i = 0; i < Fsize_y / (2 * DCTSIZE); ++i) + free(frameP->cb_blocks[i]); + free(frameP->cb_blocks); + } + if (frameP->halfX) { + unsigned int i; + for ( i = 0; i < Fsize_y; ++i ) + free(frameP->halfX[i]); + free(frameP->halfX); + + for (i = 0; i < Fsize_y-1; ++i) + free(frameP->halfY[i]); + free(frameP->halfY); + + for (i = 0; i < Fsize_y-1; ++i) + free(frameP->halfBoth[i]); + free(frameP->halfBoth); + } + free(frameP); + } +} + + + +/*===========================================================================* + * + * Frame_Exit + * + * frees the memory associated with frames + * + * RETURNS: nothing + * + * SIDE EFFECTS: frameMemory + * + *===========================================================================*/ +void +Frame_Exit(void) { + + int idx; + + for (idx = 0; idx < numOfFrames; ++idx) { + FreeFrame(frameMemory[idx]); + } +} + + +/*===========================================================================* + * + * Frame_Free + * + * frees the given frame -- allows it to be re-used + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void +Frame_Free(MpegFrame * const frameP) { + frameP->inUse = FALSE; +} + + + +/*===========================================================================* + * + * GetUnusedFrame + * + * return an unused frame + * + * RETURNS: the frame + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +static MpegFrame * +GetUnusedFrame() { + unsigned int idx; + + for (idx = 0; idx < numOfFrames; ++idx) { + if (!frameMemory[idx]->inUse) { + frameMemory[idx]->inUse = TRUE; + break; + } + } + if (idx >= numOfFrames) { + fprintf(stderr, "ERROR: No unused frames!!!\n"); + fprintf(stderr, " If you are using stdin for input, " + "it is likely that you have too many\n"); + fprintf(stderr, " B-frames between two reference frames. " + "See the man page for help.\n"); + exit(1); + } + return frameMemory[idx]; +} + + + +/*===========================================================================* + * + * ResetFrame + * + * reset a frame to the given id and type + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +static void +ResetFrame(int const id, + int const type, + MpegFrame * const frame) { + + switch (type) { + case 'i': + frame->type = TYPE_IFRAME; + break; + case 'p': + frame->type = TYPE_PFRAME; + break; + case 'b': + frame->type = TYPE_BFRAME; + break; + default: + fprintf(stderr, "Invalid MPEG frame type %c\n", type); + exit(1); + } + + frame->id = id; + frame->halfComputed = FALSE; + frame->next = NULL; +} + + + +/*===========================================================================* + * + * Frame_New + * + * finds a frame that isn't currently being used and resets it + * + * RETURNS: the frame + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +MpegFrame * +Frame_New(int const id, + int const type) { + + MpegFrame *frame; + + frame = GetUnusedFrame(); + ResetFrame(id, type, frame); + + return frame; +} + + + +/*===========================================================================* + * + * Frame_AllocBlocks + * + * allocate memory for blocks for the given frame, if required + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void +Frame_AllocBlocks(MpegFrame * const frameP) { + + if (frameP->y_blocks != NULL) { + /* already allocated */ + } else { + int const dctx = Fsize_x / DCTSIZE; + int const dcty = Fsize_y / DCTSIZE; + + unsigned int i; + + MALLOCARRAY(frameP->y_blocks, dcty); + ERRCHK(frameP->y_blocks, "malloc"); + for (i = 0; i < dcty; ++i) { + MALLOCARRAY(frameP->y_blocks[i], dctx); + ERRCHK(frameP->y_blocks[i], "malloc"); + } + + MALLOCARRAY(frameP->cr_blocks, dcty / 2); + ERRCHK(frameP->cr_blocks, "malloc"); + MALLOCARRAY(frameP->cb_blocks, dcty / 2); + ERRCHK(frameP->cb_blocks, "malloc"); + for (i = 0; i < (dcty / 2); ++i) { + MALLOCARRAY(frameP->cr_blocks[i], dctx / 2); + ERRCHK(frameP->cr_blocks[i], "malloc"); + MALLOCARRAY(frameP->cb_blocks[i], dctx / 2); + ERRCHK(frameP->cb_blocks[i], "malloc"); + } + } +} + + + +/*===========================================================================* + * + * Frame_AllocYCC + * + * allocate memory for YCC info for the given frame, if required + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void +Frame_AllocYCC(MpegFrame * const frameP) { + + if (frameP->orig_y != NULL) { + /* already allocated */ + } else { + unsigned int y; + + DBG_PRINT(("ycc_calc:\n")); + /* + * first, allocate tons of memory + */ + MALLOCARRAY(frameP->orig_y, Fsize_y); + ERRCHK(frameP->orig_y, "malloc"); + for (y = 0; y < Fsize_y; ++y) { + MALLOCARRAY(frameP->orig_y[y], Fsize_x); + ERRCHK(frameP->orig_y[y], "malloc"); + } + + MALLOCARRAY(frameP->orig_cr, Fsize_y / 2); + ERRCHK(frameP->orig_cr, "malloc"); + for (y = 0; y < (Fsize_y / 2); ++y) { + MALLOCARRAY(frameP->orig_cr[y], Fsize_x / 2); + ERRCHK(frameP->orig_cr[y], "malloc"); + } + + MALLOCARRAY(frameP->orig_cb, Fsize_y / 2); + ERRCHK(frameP->orig_cb, "malloc"); + for (y = 0; y < (Fsize_y / 2); ++y) { + MALLOCARRAY(frameP->orig_cb[y], Fsize_x / 2); + ERRCHK(frameP->orig_cb[y], "malloc"); + } + + if (referenceFrame == ORIGINAL_FRAME) { + frameP->ref_y = frameP->orig_y; + frameP->ref_cr = frameP->orig_cr; + frameP->ref_cb = frameP->orig_cb; + } + } +} + + + +/*===========================================================================* + * + * Frame_AllocHalf + * + * allocate memory for half-pixel values for the given frame, if required + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void +Frame_AllocHalf(MpegFrame * const frameP) { + + if (frameP->halfX != NULL) { + } else { + unsigned int y; + + MALLOCARRAY(frameP->halfX, Fsize_y); + ERRCHK(frameP->halfX, "malloc"); + for (y = 0; y < Fsize_y; ++y) { + MALLOCARRAY(frameP->halfX[y], Fsize_x - 1); + ERRCHK(frameP->halfX[y], "malloc"); + } + MALLOCARRAY(frameP->halfY, Fsize_y - 1); + ERRCHK(frameP->halfY, "malloc"); + for (y = 0; y < Fsize_y - 1; ++y) { + MALLOCARRAY(frameP->halfY[y], Fsize_x); + ERRCHK(frameP->halfY[y], "malloc"); + } + MALLOCARRAY(frameP->halfBoth, Fsize_y - 1); + ERRCHK(frameP->halfBoth, "malloc"); + for (y = 0; y < Fsize_y - 1; ++y) { + MALLOCARRAY(frameP->halfBoth[y], Fsize_x - 1); + ERRCHK(frameP->halfBoth[y], "malloc"); + } + } +} + + + +/*===========================================================================* + * + * Frame_AllocDecoded + * + * allocate memory for decoded frame for the given frame, if required + * if makeReference == TRUE, then makes it reference frame + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void +Frame_AllocDecoded(MpegFrame * const frameP, + boolean const makeReference) { + + if (frameP->decoded_y != NULL) { + /* already allocated */ + } else { + unsigned int y; + + /* allocate memory for decoded image */ + /* can probably reuse original image memory, but may decide to use + it for some reason, so do it this way at least for now -- more + flexible + */ + MALLOCARRAY(frameP->decoded_y, Fsize_y); + ERRCHK(frameP->decoded_y, "malloc"); + for (y = 0; y < Fsize_y; ++y) { + MALLOCARRAY(frameP->decoded_y[y], Fsize_x); + ERRCHK(frameP->decoded_y[y], "malloc"); + } + + MALLOCARRAY(frameP->decoded_cr, Fsize_y / 2); + ERRCHK(frameP->decoded_cr, "malloc"); + for (y = 0; y < (Fsize_y / 2); ++y) { + MALLOCARRAY(frameP->decoded_cr[y], Fsize_x / 2); + ERRCHK(frameP->decoded_cr[y], "malloc"); + } + + MALLOCARRAY(frameP->decoded_cb, Fsize_y / 2); + ERRCHK(frameP->decoded_cb, "malloc"); + for (y = 0; y < (Fsize_y / 2); ++y) { + MALLOCARRAY(frameP->decoded_cb[y], Fsize_x / 2); + ERRCHK(frameP->decoded_cb[y], "malloc"); + } + + if (makeReference) { + frameP->ref_y = frameP->decoded_y; + frameP->ref_cr = frameP->decoded_cr; + frameP->ref_cb = frameP->decoded_cb; + } + } +} + + + +/*=============================================================== + * + * Frame_Resize by James Boucher + * Boston University Multimedia Communications Lab + * + * This function takes the mf input frame, read in READFrame(), + * and resizes all the input component arrays to the output + * dimensions specified in the parameter file as OUT_SIZE. + * The new frame is returned with the omf pointer. As well, + * the values of Fsize_x and Fsize_y are adjusted. + ***************************************************************/ +void +Frame_Resize(MpegFrame * const omf, + MpegFrame * const mf, + int const insize_x, + int const insize_y, + int const outsize_x, + int const outsize_y) { + + MpegFrame * frameAP; /* intermediate frame */ + + MALLOCVAR_NOFAIL(frameAP); + + if (insize_x != outsize_x && insize_y != outsize_y) { + Resize_Width(frameAP, mf, insize_x, insize_y, outsize_x); + Resize_Height(omf, frameAP, outsize_x, insize_y, outsize_y); + } else + if (insize_x ==outsize_x && insize_y != outsize_y) { + Resize_Height(omf, mf, insize_x, insize_y, outsize_y); + } else + if (insize_x !=outsize_x && insize_y == outsize_y) { + Resize_Width(omf, mf, insize_x, insize_y, outsize_x); + } else + exit(1); + + free(frameAP); + free(mf); +} diff --git a/converter/ppm/ppmtompeg/frametype.c b/converter/ppm/ppmtompeg/frametype.c new file mode 100644 index 00000000..b7daacc9 --- /dev/null +++ b/converter/ppm/ppmtompeg/frametype.c @@ -0,0 +1,365 @@ +/*===========================================================================* + * frametype.c * + * * + * procedures to keep track of frame types (I, P, B) * + * * + * EXPORTED PROCEDURES: * + * FType_Type * + * FType_FutureRef * + * FType_PastRef * + * * + * SYNOPSIS * + * FType_Type returns the type of the given numbered frame * + * FType_FutureRef returns the number of the future reference frame * + * FType_PastRef returns the number of the past reference frame * + * * + * 00.12.07 change malloc from frameTable to calloc to fix bug + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + + +/*==============* + * HEADER FILES * + *==============*/ + +#include "mallocvar.h" +#include "all.h" +#include "frames.h" +#include "frame.h" +#include "param.h" +#include "specifics.h" +#include "frametype.h" + + +static FrameTable *frameTable=NULL; +static boolean use_cache = FALSE; +static int firstI = 0; +static int numFrames; + +/*==================* + * GLOBAL VARIABLES * + *==================*/ + +extern int framePatternLen; +extern char * framePattern; + + +/*=====================* + * EXPORTED PROCEDURES * + *=====================*/ + +/*===========================================================================* + * + * FType_Type + * + * returns the type of the given numbered frame + * + * RETURNS: the type + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +char +FType_Type(unsigned int const frameNum) { + + char const patternedType = framePattern[frameNum % framePatternLen]; + + char retval; + + if (use_cache) + return frameTable[frameNum].typ; + + if (frameNum+1 == numFrames) { + /* It's the last frame in the sequence. If the pattern says it's + a B, we convert it to I because a B frame makes no sense as the + last frame of a sequence. + */ + if (patternedType == 'b') + retval = 'i'; + else + retval = patternedType; + } else { + if (specificsOn) { + static int lastI = -1; + int newtype; + + if (lastI > frameNum) + lastI = -1; + newtype = SpecTypeLookup(frameNum); + switch (newtype) { + case 1: + lastI = frameNum; + retval = 'i'; + break; + case 2: + retval = 'p'; + break; + case 3: + retval = 'b'; + break; + default: + if (lastI != -1) { + unsigned int const pretendFrameNumber = + (frameNum - lastI + firstI) % framePatternLen; + retval = framePattern[pretendFrameNumber]; + } else + retval = patternedType; + } + } else + retval = patternedType; + } + return retval; +} + + + +unsigned int +FType_FutureRef(unsigned int const currFrameNum) { +/*---------------------------------------------------------------------------- + Return the number of the future reference frame for the B frame + 'currentFrameNum'. +-----------------------------------------------------------------------------*/ + unsigned int retval; + + if (use_cache) { + retval = frameTable[currFrameNum].next->number; + } else { + int const index = currFrameNum % framePatternLen; + int const futureIndex = frameTable[index].next->number; + unsigned int const patternedFutureRef = + currFrameNum + + (((futureIndex-index)+framePatternLen) % framePatternLen); + + retval = MIN(patternedFutureRef, numFrames-1); + } + return retval; +} + + + +/*===========================================================================* + * + * FType_PastRef + * + * returns the number of the past reference frame + * + * RETURNS: the number + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +int +FType_PastRef(currFrameNum) + int currFrameNum; +{ + int index; + int pastIndex; + + if (use_cache) { + return frameTable[currFrameNum].prev->number; + } else { + index = currFrameNum % framePatternLen; + pastIndex = frameTable[index].prev->number; + + return currFrameNum - + (((index-pastIndex)+framePatternLen) % framePatternLen); + } +} + + +/*===========================================================================* + * + * SetFramePattern + * + * set the IPB pattern; calls ComputeFrameTable to set up table + * + * RETURNS: nothing + * + * SIDE EFFECTS: framePattern, framePatternLen, frameTable + * + *===========================================================================*/ +#define SIMPLE_ASCII_UPPER(x) (((x)>='a') ? ((x)-'a'+'A') : (x)) +void +SetFramePattern(const char * const pattern) { + unsigned int const len = strlen(pattern); + + char *buf; + unsigned int index; + + if (!pattern) + pm_error("INTERNAL ERROR: pattern cannot be NULL " + "in SetFramePattern"); + + if (SIMPLE_ASCII_UPPER(pattern[0]) != 'I') { + unsigned int index; + for (index = 0; index < len; ++index) { + if (SIMPLE_ASCII_UPPER(pattern[index]) == 'I') { + break; + } else if (SIMPLE_ASCII_UPPER(pattern[index]) == 'P') + pm_error("first reference frame must be 'i', not '%c'", + pattern[index]); + } + } + + buf = (char *)malloc(sizeof(char)*(len+1)); + ERRCHK(buf, "malloc"); + + firstI = -1; + for (index = 0; index < len; index++) { + switch( SIMPLE_ASCII_UPPER(pattern[index]) ) { + case 'I': + buf[index] = 'i'; + if (firstI == -1) firstI = index; + break; + case 'P': + buf[index] = 'p'; + break; + case 'B': + buf[index] = 'b'; + break; + default: + pm_error("Invalid MPEG Frame type '%c'.", pattern[index]); + } + } + buf[len] = 0; + + if (firstI == -1) + pm_error("Must have an I-frame in PATTERN"); + + framePattern = buf; + framePatternLen = len; + + /* Used to ComputeFrameTable(), but now must wait until param + parsed. (STDIN or not) + */ +} + + + +void +ComputeFrameTable(unsigned int const numFramesArg) { +/*---------------------------------------------------------------------------- + Compute a table of I, P, B frames to help in determining dependencie + + 'numFrames' == 0 means number of frames is not known at this time. +-----------------------------------------------------------------------------*/ + int index; + FrameTable *lastI, *lastIP, *firstB, *secondIP; + FrameTable *ptr; + char typ; + int table_size; + + numFrames = numFramesArg; + + if (numFrames) + table_size = numFrames; + else + table_size = framePatternLen; + + MALLOCARRAY_NOFAIL(frameTable, 1 + table_size); + + lastI = NULL; + lastIP = NULL; + firstB = NULL; + secondIP = NULL; + for ( index = 0; index < table_size; index++ ) { + frameTable[index].number = index; + typ = FType_Type(index); + frameTable[index].typ = typ; + switch( typ ) { + case 'i': + ptr = firstB; + while ( ptr != NULL ) { + ptr->next = &(frameTable[index]); + ptr = ptr->nextOutput; + } + frameTable[index].nextOutput = firstB; + frameTable[index].prev = lastIP; /* for freeing */ + if ( lastIP != NULL ) { + lastIP->next = &(frameTable[index]); + if ( secondIP == NULL ) { + secondIP = &(frameTable[index]); + } + } + lastIP = &(frameTable[index]); + firstB = NULL; + break; + case 'p': + ptr = firstB; + while ( ptr != NULL ) { + ptr->next = &(frameTable[index]); + ptr = ptr->nextOutput; + } + frameTable[index].nextOutput = firstB; + frameTable[index].prev = lastIP; + if ( lastIP != NULL ) { + lastIP->next = &(frameTable[index]); + if ( secondIP == NULL ) { + secondIP = &(frameTable[index]); + } + } + lastIP = &(frameTable[index]); + firstB = NULL; + break; + case 'b': + if ( (index+1 == framePatternLen) || + (FType_Type(index+1) != 'b') ) { + frameTable[index].nextOutput = NULL; + } else { + frameTable[index].nextOutput = &(frameTable[index+1]); + } + frameTable[index].prev = lastIP; + if ( firstB == NULL ) { + firstB = &(frameTable[index]); + } + break; + default: + fprintf(stderr, "Programmer Error in ComputeFrameTable (%d)\n", + framePattern[index]); + exit(1); + break; + } + } + + /* why? SRS */ + frameTable[table_size].number = framePatternLen; + ptr = firstB; + while ( ptr != NULL ) { + ptr->next = &(frameTable[table_size]); + ptr = ptr->nextOutput; + } + frameTable[table_size].nextOutput = firstB; + frameTable[table_size].prev = lastIP; + if ( secondIP == NULL ) + frameTable[table_size].next = &(frameTable[0]); + else + frameTable[table_size].next = secondIP; + + frameTable[0].prev = lastIP; + if ( lastIP != NULL ) { + lastIP->next = &(frameTable[table_size]); + } + + if (numFrames) + use_cache = TRUE; +} diff --git a/converter/ppm/ppmtompeg/fsize.c b/converter/ppm/ppmtompeg/fsize.c new file mode 100644 index 00000000..3808405c --- /dev/null +++ b/converter/ppm/ppmtompeg/fsize.c @@ -0,0 +1,134 @@ +/*===========================================================================* + * fsize.c * + * * + * procedures to keep track of frame size * + * * + * EXPORTED PROCEDURES: * + * Fsize_Reset * + * Fsize_Note * + * Fsize_Validate * + * * + * EXPORTED VARIABLES: * + * Fsize_x * + * Fsize_y * + * * + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + + +/*==============* + * HEADER FILES * + *==============*/ + +#include "all.h" +#include "fsize.h" +#include "dct.h" + + +/*==================* + * GLOBAL VARIABLES * + *==================*/ +int Fsize_x = 0; +int Fsize_y = 0; + + +/*=====================* + * EXPORTED PROCEDURES * + *=====================*/ + +/*===========================================================================* + * + * Fsize_Reset + * + * reset the frame size to 0 + * + * RETURNS: nothing + * + * SIDE EFFECTS: Fsize_x, Fsize_y + * + *===========================================================================*/ +void +Fsize_Reset(void) { + Fsize_x = Fsize_y = 0; +} + + + +/*===========================================================================* + * + * Fsize_Validate + * + * make sure that the x, y values are 16-pixel aligned + * + * RETURNS: modifies the x, y values to 16-pixel alignment + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void +Fsize_Validate(int * const xP, + int * const yP) { + + *xP &= ~(DCTSIZE * 2 - 1); /* Zero the low-order bits */ + *yP &= ~(DCTSIZE * 2 - 1); /* Zero the low-order bits */ +} + + +/*===========================================================================* + * + * Fsize_Note + * + * note the given frame size and modify the global values as appropriate + * + * RETURNS: nothing + * + * SIDE EFFECTS: Fsize_x, Fsize_y + * + *===========================================================================*/ +void +Fsize_Note(int const id, + unsigned int const width, + unsigned int const height) { + + Fsize_x = width; + Fsize_y = height; + Fsize_Validate(&Fsize_x, &Fsize_y); + + if ((Fsize_x == 0) || (Fsize_y == 0)) { + fprintf(stderr,"Frame %d: size is less than the minimum: %d x %d!\n", + id, DCTSIZE*2, DCTSIZE*2); + exit(1); + } + +#ifdef BLEAH + if (Fsize_x == 0) { + Fsize_x = width; + Fsize_y = height; + Fsize_Validate(&Fsize_x, &Fsize_y); + } else if (width < Fsize_x || height < Fsize_y) { + fprintf(stderr, "Frame %d: wrong size: (%d,%d). Should be greater or equal to: (%d,%d)\n", + id, width, height, Fsize_x, Fsize_y); + exit(1); + } +#endif +} diff --git a/converter/ppm/ppmtompeg/gethostname.c b/converter/ppm/ppmtompeg/gethostname.c new file mode 100644 index 00000000..014b42e8 --- /dev/null +++ b/converter/ppm/ppmtompeg/gethostname.c @@ -0,0 +1,26 @@ +#define _BSD_SOURCE /* Make sure strdup() is in string.h */ + +#include <string.h> +#include <errno.h> +#include <sys/utsname.h> + +#include "pm.h" + +#include "gethostname.h" + +const char * +GetHostName(void) { +/*---------------------------------------------------------------------------- + Return the host name of this system. +-----------------------------------------------------------------------------*/ + struct utsname utsname; + int rc; + + rc = uname(&utsname); + + if (rc < 0) + pm_error("Unable to find out host name. " + "uname() failed with errno %d (%s)", errno, strerror(errno)); + + return strdup(utsname.nodename); +} diff --git a/converter/ppm/ppmtompeg/gethostname.h b/converter/ppm/ppmtompeg/gethostname.h new file mode 100644 index 00000000..e5450175 --- /dev/null +++ b/converter/ppm/ppmtompeg/gethostname.h @@ -0,0 +1,7 @@ +#ifndef GETHOSTNAME_H_INCLUDED +#define GETHOSTNAME_H_INCLUDED + +const char * +GetHostName(void); + +#endif diff --git a/converter/ppm/ppmtompeg/headers/all.h b/converter/ppm/ppmtompeg/headers/all.h new file mode 100644 index 00000000..e350aab8 --- /dev/null +++ b/converter/ppm/ppmtompeg/headers/all.h @@ -0,0 +1,95 @@ +/*===========================================================================* + * all.h * + * * + * stuff included from ALL source files * + * * + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/* + * $Header: /u/smoot/md/mpeg_encode/headers/RCS/all.h,v 1.9 1995/06/05 21:11:06 smoot Exp $ + * $Log: all.h,v $ + * Revision 1.9 1995/06/05 21:11:06 smoot + * added little_endian force for irizx + * + * Revision 1.8 1995/02/02 22:02:18 smoot + * added ifdefs for compatability on stranger and stranger architectures... + * + * Revision 1.7 1995/02/02 07:26:45 eyhung + * added parens to all.h to remove compiler warning + * + * Revision 1.6 1995/02/02 01:47:11 eyhung + * added MAXINT + * + * Revision 1.5 1995/01/19 23:54:33 eyhung + * Changed copyrights + * + * Revision 1.4 1994/11/14 22:52:04 smoot + * Added linux #include for time.h + * + * Revision 1.3 1994/11/12 02:12:13 keving + * nothing + * + * Revision 1.2 1993/07/22 22:24:23 keving + * nothing + * + * Revision 1.1 1993/07/09 00:17:23 keving + * nothing + * + */ + + +#ifndef ENCODE_ALL_INCLUDED +#define ENCODE_ALL_INCLUDED + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <memory.h> +#include <limits.h> + +/* There's got to be a better way.... */ +#ifdef LINUX +#include <time.h> +#endif +#ifdef MIPS +#include <time.h> +#endif +#ifdef IRIX +#define FORCE_LITTLE_ENDIAN +#include <time.h> +#endif + +#include "ansi.h" +#include "general.h" + +/* some machines have #define index strchr; get rid of this nonsense */ +#ifdef index +#undef index +#endif /* index */ + +#ifndef MAXINT +#define MAXINT 0x7fffffff +#endif + +#endif /* ENCODE_ALL_INCLUDED */ diff --git a/converter/ppm/ppmtompeg/headers/ansi.h b/converter/ppm/ppmtompeg/headers/ansi.h new file mode 100644 index 00000000..b3c3ab17 --- /dev/null +++ b/converter/ppm/ppmtompeg/headers/ansi.h @@ -0,0 +1,76 @@ +/*===========================================================================* + * ansi.h * + * * + * macro for non-ansi compilers * + * * + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/* + * $Header: /n/picasso/project/mpeg/mpeg_dist/mpeg_encode/headers/RCS/ansi.h,v 1.6 1995/08/15 23:43:13 smoot Exp $ + * $Log: ansi.h,v $ + * Revision 1.6 1995/08/15 23:43:13 smoot + * *** empty log message *** + * + * Revision 1.5 1995/01/19 23:54:35 eyhung + * Changed copyrights + * + * Revision 1.4 1994/11/12 02:12:13 keving + * nothing + * + * Revision 1.3 1993/07/22 22:24:23 keving + * nothing + * + * Revision 1.2 1993/07/09 00:17:23 keving + * nothing + * + * Revision 1.1 1993/06/14 22:50:22 keving + * nothing + * + */ + + +#ifndef ANSI_INCLUDED +#define ANSI_INCLUDED + + +/* + * _ANSI_ARGS_ macro stolen from Tcl6.5 by John Ousterhout + */ +#undef _ANSI_ARGS_ +#undef const +#ifdef NON_ANSI_COMPILER +#define _ANSI_ARGS_(x) () +#define CONST +#else +#define _ANSI_ARGS_(x) x +#define CONST const +#ifdef __cplusplus +#define VARARGS (...) +#else +#define VARARGS () +#endif +#endif + + +#endif /* ANSI_INCLUDED */ diff --git a/converter/ppm/ppmtompeg/headers/bitio.h b/converter/ppm/ppmtompeg/headers/bitio.h new file mode 100644 index 00000000..89e61fbb --- /dev/null +++ b/converter/ppm/ppmtompeg/headers/bitio.h @@ -0,0 +1,140 @@ +/*===========================================================================* + * bitio.h * + * * + * bitwise input/output * + * * + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/* + * $Header: /n/charlie-brown/project/mm/mpeg/mpeg_dist/mpeg_encode/headers/RCS/bitio.h,v 1.8 1995/01/19 23:54:37 eyhung Exp $ + * $Log: bitio.h,v $ + * Revision 1.8 1995/01/19 23:54:37 eyhung + * Changed copyrights + * + * Revision 1.7 1994/11/12 02:12:14 keving + * nothing + * + * Revision 1.6 1993/07/22 22:24:23 keving + * nothing + * + * Revision 1.5 1993/07/09 00:17:23 keving + * nothing + * + * Revision 1.4 1993/06/03 21:08:53 keving + * nothing + * + * Revision 1.3 1993/01/18 10:20:02 dwallach + * *** empty log message *** + * + * Revision 1.2 1993/01/18 10:17:29 dwallach + * RCS headers installed, code indented uniformly + * + * Revision 1.2 1993/01/18 10:17:29 dwallach + * RCS headers installed, code indented uniformly + * + */ + + +#ifndef BIT_IO_INCLUDED +#define BIT_IO_INCLUDED + + +/*==============* + * HEADER FILES * + *==============*/ + +#include "general.h" +#include "ansi.h" + + +/*===========* + * CONSTANTS * + *===========*/ + +#define WORDS_PER_BUCKET 128 +#define MAXBITS_PER_BUCKET (WORDS_PER_BUCKET * 32) +#define MAX_BUCKETS 128 +#define MAX_BITS MAX_BUCKETS*MAXBITS_PER_BUCKET + + +/*=======================* + * STRUCTURE DEFINITIONS * + *=======================*/ + +typedef struct bitBucket { + struct bitBucket *nextPtr; + uint32 bits[WORDS_PER_BUCKET]; + int bitsleft, bitsleftcur, currword; +} ActualBucket; + +typedef struct _BitBucket { + int totalbits; + int cumulativeBits; + int bitsWritten; + FILE *filePtr; + ActualBucket *firstPtr; + ActualBucket *lastPtr; +} BitBucket; + + +/*========* + * MACROS * + *========*/ + +#define SET_ITH_BIT(bits, i) (bits |= (1 << (i))) +#define GET_ITH_BIT(bits, i) (bits & (1 << (i))) + + +/*===============================* + * EXTERNAL PROCEDURE prototypes * + *===============================*/ + +void +Bitio_Free(BitBucket * const bbPtr); + +void +Bitio_Write(BitBucket * const bbPtr, + uint32 const bits, + int const nbits); + +void +Bitio_BytePad(BitBucket * const bbPtr); + +BitBucket * +Bitio_New(FILE * const filePtr); + +BitBucket * +Bitio_New_Filename(const char * const fileName); + +void +Bitio_Flush(BitBucket * const bbPtr); + +void +Bitio_Close(BitBucket * const bbPtr); + +void +Bitio_WriteToSocket(BitBucket * const bbPtr, + int const socket); + +#endif /* BIT_IO_INCLUDED */ diff --git a/converter/ppm/ppmtompeg/headers/block.h b/converter/ppm/ppmtompeg/headers/block.h new file mode 100644 index 00000000..46050492 --- /dev/null +++ b/converter/ppm/ppmtompeg/headers/block.h @@ -0,0 +1,53 @@ +void +ComputeDiffDCTs(MpegFrame * const current, + MpegFrame * const prev, + int const by, + int const bx, + vector const m, + int * const pattern); + +void +ComputeDiffDCTBlock(Block current, + Block dest, + Block motionBlock, + boolean * const significantDifferenceP); + +void +ComputeMotionBlock(uint8 ** const prev, + int const by, + int const bx, + vector const m, + Block * const motionBlockP); + +void +ComputeMotionLumBlock(MpegFrame * const prevFrame, + int const by, + int const bx, + vector const m, + LumBlock * const motionBlockP); + +void +BlockToData(uint8 ** const data, + Block block, + int const by, + int const bx); + +void +AddMotionBlock(Block block, + uint8 ** const prev, + int const by, + int const bx, + vector const m); + +void +AddBMotionBlock(Block block, + uint8 ** const prev, + uint8 ** const next, + int const by, + int const bx, + int const mode, + motion const motion); + +void +BlockifyFrame(MpegFrame * const frameP); + diff --git a/converter/ppm/ppmtompeg/headers/byteorder.h b/converter/ppm/ppmtompeg/headers/byteorder.h new file mode 100644 index 00000000..0070252a --- /dev/null +++ b/converter/ppm/ppmtompeg/headers/byteorder.h @@ -0,0 +1,77 @@ +/*===========================================================================* + * byteorder.h * + * * + * stuff to handle different byte order * + * * + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/* + * $Header: /u/smoot/md/mpeg_encode/headers/RCS/byteorder.h,v 1.3 1995/01/19 23:54:39 eyhung Exp $ + * $Log: byteorder.h,v $ + * Revision 1.3 1995/01/19 23:54:39 eyhung + * Changed copyrights + * + * Revision 1.3 1995/01/19 23:54:39 eyhung + * Changed copyrights + * + * Revision 1.2 1994/11/12 02:12:15 keving + * nothing + * + * Revision 1.1 1993/07/22 22:24:23 keving + * nothing + * + */ + + +#include "general.h" + + +#ifdef FORCE_BIG_ENDIAN + /* leave byte order as it is */ +#define htonl(x) (x) +#define ntohl(x) (x) +#define htons(x) (x) +#define ntohs(x) (x) +#else +#ifdef FORCE_LITTLE_ENDIAN + /* need to reverse byte order */ + /* note -- we assume here that htonl is called on a variable, not a + * constant; thus, this is not for general use, but works with bitio.c + */ +#define htonl(x) \ + ((((unsigned char *)(&x))[0] << 24) | \ + (((unsigned char *)(&x))[1] << 16) | \ + (((unsigned char *)(&x))[2] << 8) | \ + (((unsigned char *)(&x))[3])) +#define ntohl(x) htonl(x) +#define htons(x) \ + ((((unsigned char *)(&x))[0] << 8) | \ + ((unsigned char *)(&x))[1]) +#define ntohs(x) htons(x) +#else + /* let in.h handle it, if possible */ +#include <sys/types.h> +#include <netinet/in.h> +#endif /* FORCE_LITTLE_ENDIAN */ +#endif /* FORCE_BIG_ENDIAN */ diff --git a/converter/ppm/ppmtompeg/headers/combine.h b/converter/ppm/ppmtompeg/headers/combine.h new file mode 100644 index 00000000..e28c6dee --- /dev/null +++ b/converter/ppm/ppmtompeg/headers/combine.h @@ -0,0 +1,20 @@ +struct inputSource; + +void +GOPsToMPEG(struct inputSource * const inputSourceP, + const char * const outputFileName, + FILE * const outputFilePtr); + +typedef void (*fileAcquisitionFn)(void * const handle, + unsigned int const frameNumber, + FILE ** const ifPP); + + +typedef void (*fileDispositionFn)(void * const handle, + unsigned int const frameNumber); + +void +FramesToMPEG(FILE * const outputFile, + void * const inputHandle, + fileAcquisitionFn acquireInputFile, + fileDispositionFn disposeInputFile); diff --git a/converter/ppm/ppmtompeg/headers/dct.h b/converter/ppm/ppmtompeg/headers/dct.h new file mode 100644 index 00000000..e024b6c1 --- /dev/null +++ b/converter/ppm/ppmtompeg/headers/dct.h @@ -0,0 +1,79 @@ +/*===========================================================================* + * dct.h * + * * + * DCT procedures * + * * + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + + +#ifndef DCT_INCLUDED +#define DCT_INCLUDED + + +#include "ansi.h" + + + +#define DCTSIZE 8 /* you really don't want to change this */ +#define DCTSIZE_SQ 64 /* you really don't want to change this */ + +#define DCTSIZE2 64 +typedef short DCTELEM; +typedef DCTELEM DCTBLOCK[DCTSIZE2]; +typedef DCTELEM DCTBLOCK_2D[DCTSIZE][DCTSIZE]; + + +/* + * from mfwddct.c: + */ +extern void mp_fwd_dct_block2 _ANSI_ARGS_((DCTBLOCK_2D src, DCTBLOCK_2D dest)); + +/* jrevdct.c */ +extern void init_pre_idct _ANSI_ARGS_((void )); +extern void mpeg_jrevdct _ANSI_ARGS_((DCTBLOCK data )); + + +/* We assume that right shift corresponds to signed division by 2 with + * rounding towards minus infinity. This is correct for typical "arithmetic + * shift" instructions that shift in copies of the sign bit. But some + * C compilers implement >> with an unsigned shift. For these machines you + * must define RIGHT_SHIFT_IS_UNSIGNED. + * RIGHT_SHIFT provides a proper signed right shift of an int32 quantity. + * It is only applied with constant shift counts. SHIFT_TEMPS must be + * included in the variables of any routine using RIGHT_SHIFT. + */ + +#ifdef RIGHT_SHIFT_IS_UNSIGNED +#define SHIFT_TEMPS int32 shift_temp; +#define RIGHT_SHIFT(x,shft) \ + ((shift_temp = (x)) < 0 ? \ + (shift_temp >> (shft)) | ((~((int32) 0)) << (32-(shft))) : \ + (shift_temp >> (shft))) +#else +#define SHIFT_TEMPS +#define RIGHT_SHIFT(x,shft) ((x) >> (shft)) +#endif + + +#endif /* DCT_INCLUDED */ diff --git a/converter/ppm/ppmtompeg/headers/frame.h b/converter/ppm/ppmtompeg/headers/frame.h new file mode 100644 index 00000000..1003ee15 --- /dev/null +++ b/converter/ppm/ppmtompeg/headers/frame.h @@ -0,0 +1,147 @@ +/*===========================================================================* + * frame.h * + * * + * basic frames procedures * + * * + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + + +#ifndef FRAME_INCLUDED +#define FRAME_INCLUDED + +/*==============* + * HEADER FILES * + *==============*/ + +#include "general.h" +#include "ansi.h" +#include "mtypes.h" + +/*===========* + * CONSTANTS * + *===========*/ +#define TYPE_IFRAME 2 +#define TYPE_PFRAME 3 +#define TYPE_BFRAME 4 + + +/*=======================* + * STRUCTURE DEFINITIONS * + *=======================*/ + +typedef struct mpegFrame { + int type; + char inputFileName[256]; + int id; /* the frame number -- starts at 0 */ + boolean inUse; /* TRUE iff this frame is currently being used */ + /* FALSE means any data here can be thrashed */ + + /* + * now, the YCrCb data. All pixel information is stored in unsigned + * 8-bit pieces. We separate y, cr, and cb because cr and cb are + * subsampled by a factor of 2. + * + * if orig_y is NULL, then orig_cr, orig_cb are undefined + */ + uint8 **orig_y, **orig_cr, **orig_cb; + + /* now, the decoded data -- relevant only if + * referenceFrame == DECODED_FRAME + * + * if decoded_y is NULL, then decoded_cr, decoded_cb are undefined + */ + uint8 **decoded_y, **decoded_cr, **decoded_cb; + + /* reference data */ + uint8 **ref_y, **ref_cr, **ref_cb; + + /* + * these are the Blocks which will ultimately compose MacroBlocks. + * A Block is in a format that mp_fwddct() can crunch. + * if y_blocks is NULL, then cr_blocks, cb_blocks are undefined + */ + Block **y_blocks, **cr_blocks, **cb_blocks; + + /* + * this is the half-pixel luminance data (for reference frames) + */ + uint8 **halfX, **halfY, **halfBoth; + + boolean halfComputed; /* TRUE iff half-pixels already computed */ + + struct mpegFrame *next; /* points to the next B-frame to be encoded, if + * stdin is used as the input. + */ +} MpegFrame; + + +void +Frame_Init(unsigned int const numOfFramesRequested); + +void +Frame_Exit(void); + +void +Frame_Free(MpegFrame * const frameP); + +MpegFrame * +Frame_New(int const id, + int const type); + +void +Frame_AllocBlocks(MpegFrame * const frameP); + +void +Frame_AllocYCC(MpegFrame * const frameP); + +void +Frame_AllocHalf(MpegFrame * const frameP); + +void +Frame_AllocDecoded(MpegFrame * const frameP, + boolean const makeReference); + +void +Frame_Resize(MpegFrame * const omf, + MpegFrame * const mf, + int const insize_x, + int const insize_y, + int const outsize_x, + int const outsize_y); + + +extern void Frame_Free _ANSI_ARGS_((MpegFrame *frame)); +extern void Frame_Exit _ANSI_ARGS_((void)); +extern void Frame_AllocPPM _ANSI_ARGS_((MpegFrame * frame)); +extern void Frame_AllocYCC _ANSI_ARGS_((MpegFrame * mf)); +extern void Frame_AllocDecoded _ANSI_ARGS_((MpegFrame *frame, + boolean makeReference)); +extern void Frame_AllocHalf _ANSI_ARGS_((MpegFrame *frame)); +extern void Frame_AllocBlocks _ANSI_ARGS_((MpegFrame *mf)); +extern void Frame_Resize _ANSI_ARGS_((MpegFrame *omf, MpegFrame *mf, + int insize_x, int insize_y, + int outsize_x, int outsize_y)); + + +#endif /* FRAME_INCLUDED */ diff --git a/converter/ppm/ppmtompeg/headers/frames.h b/converter/ppm/ppmtompeg/headers/frames.h new file mode 100644 index 00000000..3fefaea7 --- /dev/null +++ b/converter/ppm/ppmtompeg/headers/frames.h @@ -0,0 +1,487 @@ +/*===========================================================================* + * frames.h + * + * stuff dealing with frames + * + *===========================================================================*/ + +#ifndef FRAMES_INCLUDED +#define FRAMES_INCLUDED + +/*==============* + * HEADER FILES * + *==============*/ + +#include "ansi.h" +#include "mtypes.h" +#include "mheaders.h" +#include "frame.h" + + +/*===========* + * CONSTANTS * + *===========*/ + +#define I_FRAME 1 +#define P_FRAME 2 +#define B_FRAME 3 + +#define LUM_BLOCK 0 +#define CHROM_BLOCK 1 +#define CR_BLOCK 2 +#define CB_BLOCK 3 + +#define MOTION_FORWARD 0 +#define MOTION_BACKWARD 1 +#define MOTION_INTERPOLATE 2 + + +#define USE_HALF 0 +#define USE_FULL 1 + + /* motion vector stuff */ +#define FORW_F_CODE fCode /* from picture header */ +#define BACK_F_CODE fCode +#define FORW_F (1 << (FORW_F_CODE - 1)) +#define BACK_F (1 << (BACK_F_CODE - 1)) +#define RANGE_NEG (-(1 << (3 + FORW_F_CODE))) +#define RANGE_POS ((1 << (3 + FORW_F_CODE))-1) +#define MODULUS (1 << (4 + FORW_F_CODE)) + +#define ORIGINAL_FRAME 0 +#define DECODED_FRAME 1 + + +/*=======================* + * STRUCTURE DEFINITIONS * + *=======================*/ + +typedef struct FrameTableStruct { + /* the following are all initted once and never changed */ + /* (they depend only on the pattern */ + char typ; + struct FrameTableStruct *next; + struct FrameTableStruct *prev; + + /* nextOutput is a pointer to next frame table entry to output */ + struct FrameTableStruct *nextOutput; + + boolean freeNow; /* TRUE iff no frames point back to this */ + + int number; + + int bFrameNumber; /* actual frame number, if a b-frame */ + +} FrameTable; + + +/*==================* + * TYPE DEFINITIONS * + *==================*/ + +typedef struct dct_data_tye_struct { + char useMotion; + char pattern, mode; + int fmotionX, fmotionY, bmotionX, bmotionY; +} dct_data_type; + +void EncodeYDC _ANSI_ARGS_((int32 dc_term, int32 *pred_term, BitBucket *bb)); +void +EncodeCDC(int32 const dc_term, + int32 * const pred_term, + BitBucket * const bb); + + +/*========* + * MACROS * + *========*/ + +/* return ceiling(a/b) where a, b are ints, using temp value c */ +#define int_ceil_div(a,b,c) ((b*(c = a/b) < a) ? (c+1) : c) +#define int_floor_div(a,b,c) ((b*(c = a/b) > a) ? (c-1) : c) + +/* assumes many things: + * block indices are (y,x) + * variables y_dc_pred, cr_dc_pred, and cb_dc_pred + * flat block fb exists + */ +#define GEN_I_BLOCK(frameType, frame, bb, mbAI, qscale) { \ + boolean overflow, overflowChange=FALSE; \ + int overflowValue = 0; \ + do { \ + overflow = Mpost_QuantZigBlock(dct[y][x], fb[0], \ + qscale, TRUE)==MPOST_OVERFLOW; \ + overflow |= Mpost_QuantZigBlock(dct[y][x+1], fb[1], \ + qscale, TRUE)==MPOST_OVERFLOW; \ + overflow |= Mpost_QuantZigBlock(dct[y+1][x], fb[2], \ + qscale, TRUE)==MPOST_OVERFLOW; \ + overflow |= Mpost_QuantZigBlock(dct[y+1][x+1], fb[3], \ + qscale, TRUE)==MPOST_OVERFLOW; \ + overflow |= Mpost_QuantZigBlock(dctb[y >> 1][x >> 1], \ + fb[4], qscale, TRUE)==MPOST_OVERFLOW; \ + overflow |= Mpost_QuantZigBlock(dctr[y >> 1][x >> 1], \ + fb[5], qscale, TRUE)==MPOST_OVERFLOW; \ + if ((overflow) && (qscale!=31)) { \ + overflowChange = TRUE; overflowValue++; \ + qscale++; \ + } else overflow = FALSE; \ + } while (overflow); \ + Mhead_GenMBHeader(bb, \ + frameType /* pict_code_type */, mbAI /* addr_incr */, \ + qscale /* q_scale */, \ + 0 /* forw_f_code */, 0 /* back_f_code */, \ + 0 /* horiz_forw_r */, 0 /* vert_forw_r */, \ + 0 /* horiz_back_r */, 0 /* vert_back_r */, \ + 0 /* motion_forw */, 0 /* m_horiz_forw */, \ + 0 /* m_vert_forw */, 0 /* motion_back */, \ + 0 /* m_horiz_back */, 0 /* m_vert_back */, \ + 0 /* mb_pattern */, TRUE /* mb_intra */); \ + \ + /* Y blocks */ \ + EncodeYDC(fb[0][0], &y_dc_pred, bb); \ + Mpost_RLEHuffIBlock(fb[0], bb); \ + EncodeYDC(fb[1][0], &y_dc_pred, bb); \ + Mpost_RLEHuffIBlock(fb[1], bb); \ + EncodeYDC(fb[2][0], &y_dc_pred, bb); \ + Mpost_RLEHuffIBlock(fb[2], bb); \ + EncodeYDC(fb[3][0], &y_dc_pred, bb); \ + Mpost_RLEHuffIBlock(fb[3], bb); \ + \ + /* CB block */ \ + EncodeCDC(fb[4][0], &cb_dc_pred, bb); \ + Mpost_RLEHuffIBlock(fb[4], bb); \ + \ + /* CR block */ \ + EncodeCDC(fb[5][0], &cr_dc_pred, bb); \ + Mpost_RLEHuffIBlock(fb[5], bb); \ + if (overflowChange) qscale -= overflowValue; \ + } + +#define BLOCK_TO_FRAME_COORD(bx1, bx2, x1, x2) { \ + x1 = (bx1)*DCTSIZE; \ + x2 = (bx2)*DCTSIZE; \ + } + +static __inline__ void +MotionToFrameCoord(int const by, + int const bx, + int const my, + int const mx, + int * const yP, + int * const xP) { +/*---------------------------------------------------------------------------- + Given a DCT block location and a motion vector, return the pixel + coordinates to which the upper left corner of the block moves. + + Return negative coordinates if it moves out of frame. +-----------------------------------------------------------------------------*/ + *yP = by * DCTSIZE + my; + *xP = bx * DCTSIZE + mx; +} + +#define COORD_IN_FRAME(fy,fx, type) \ + ((type == LUM_BLOCK) ? \ + ((fy >= 0) && (fx >= 0) && (fy < Fsize_y) && (fx < Fsize_x)) : \ + ((fy >= 0) && (fx >= 0) && (fy < (Fsize_y>>1)) && (fx < (Fsize_x>>1)))) + + +static __inline__ void +encodeMotionVector(int const x, + int const y, + vector * const qP, + vector * const rP, + int const f, + int const fCode) { + + int tempX; + int tempY; + int tempC; + + if (x < RANGE_NEG) + tempX = x + MODULUS; + else if (x > RANGE_POS) + tempX = x - MODULUS; + else + tempX = x; + + if (y < RANGE_NEG) + tempY = y + MODULUS; + else if (y > RANGE_POS) + tempY = y - MODULUS; + else + tempY = y; + + if (tempX >= 0) { + qP->x = int_ceil_div(tempX, f, tempC); + rP->x = f - 1 + tempX - qP->x*f; + } else { + qP->x = int_floor_div(tempX, f, tempC); + rP->x = f - 1 - tempX + qP->x*f; + } + + if (tempY >= 0) { + qP->y = int_ceil_div(tempY, f, tempC); + rP->y = f - 1 + tempY - qP->y*f; + } else { + qP->y = int_floor_div(tempY, f, tempC); + rP->y = f - 1 - tempY + qP->y*f; + } +} + + +#define DoQuant(bit, src, dest) \ + if (pattern & bit) { \ + switch (Mpost_QuantZigBlock(src, dest, QScale, FALSE)) { \ + case MPOST_NON_ZERO: \ + break; \ + case MPOST_ZERO: \ + pattern ^= bit; \ + break; \ + case MPOST_OVERFLOW: \ + if (QScale != 31) { \ + QScale++; \ + overflowChange = TRUE; \ + overflowValue++; \ + goto calc_blocks; \ + } \ + break; \ + } \ + } + +/*===============================* + * EXTERNAL PROCEDURE prototypes * + *===============================*/ + +void +ComputeBMotionLumBlock(MpegFrame * const prev, + MpegFrame * const next, + int const by, + int const bx, + int const mode, + motion const motion, + LumBlock * const motionBlockP); + +int +BMotionSearch(const LumBlock * const currentBlockP, + MpegFrame * const prev, + MpegFrame * const next, + int const by, + int const bx, + motion * const motionP, + int const oldMode); + +void GenIFrame (BitBucket * const bb, + MpegFrame * const mf); +void GenPFrame (BitBucket * const bb, + MpegFrame * const current, + MpegFrame * const prev); +void GenBFrame (BitBucket * const bb, + MpegFrame * const curr, + MpegFrame * const prev, + MpegFrame * const next); +void AllocDctBlocks _ANSI_ARGS_((void )); + + +float +IFrameTotalTime(void); + +float +PFrameTotalTime(void); + +float +BFrameTotalTime(void); + +void +ShowIFrameSummary(unsigned int const inputFrameBits, + unsigned int const totalBits, + FILE * const fpointer); + +void +ShowPFrameSummary(unsigned int const inputFrameBits, + unsigned int const totalBits, + FILE * const fpointer); + +void +ShowBFrameSummary(unsigned int const inputFrameBits, + unsigned int const totalBits, + FILE * const fpointer); + +/* DIFFERENCE FUNCTIONS */ + +int32 +LumBlockMAD(const LumBlock * const currentBlockP, + const LumBlock * const motionBlockP, + int32 const bestSoFar); + +int32 +LumBlockMSE(const LumBlock * const currentBlockP, + const LumBlock * const motionBlockP, + int32 const bestSoFar); + +int32 +LumMotionError(const LumBlock * const currentBlockP, + MpegFrame * const prev, + int const by, + int const bx, + vector const m, + int32 const bestSoFar); + +int32 +LumAddMotionError(const LumBlock * const currentBlockP, + const LumBlock * const blockSoFarP, + MpegFrame * const prev, + int const by, + int const bx, + vector const m, + int32 const bestSoFar); + +int32 +LumMotionErrorA(const LumBlock * const currentBlockP, + MpegFrame * const prevFrame, + int const by, + int const bx, + vector const m, + int32 const bestSoFar); + +int32 +LumMotionErrorB(const LumBlock * const currentP, + MpegFrame * const prevFrame, + int const by, + int const bx, + vector const m, + int32 const bestSoFar); + +int32 +LumMotionErrorC(const LumBlock * const currentP, + MpegFrame * const prevFrame, + int const by, + int const bx, + vector const m, + int32 const bestSoFar); + +int32 +LumMotionErrorD(const LumBlock * const currentP, + MpegFrame * const prevFrame, + int const by, + int const bx, + vector const m, + int32 const bestSoFar); + +int32 +LumMotionErrorSubSampled(const LumBlock * const currentBlockP, + MpegFrame * const prevFrame, + int const by, + int const bx, + vector const m, + int const startY, + int const startX); + +void +BlockComputeSNR(MpegFrame * const current, + float * const snr, + float * const psnr); + +int32 +time_elapsed(void); + +void +AllocDctBlocks(void); + +/*==================* + * GLOBAL VARIABLES * + *==================*/ + +extern int pixelFullSearch; +extern int searchRangeP,searchRangeB; /* defined in psearch.c */ +extern int qscaleI; +extern int gopSize; +extern int slicesPerFrame; +extern int blocksPerSlice; +extern int referenceFrame; +extern int specificsOn; +extern int quietTime; /* shut up for at least quietTime seconds; + * negative means shut up forever + */ +extern boolean realQuiet; /* TRUE = no messages to stdout */ + +extern boolean frameSummary; /* TRUE = frame summaries should be printed */ +extern boolean printSNR; +extern boolean printMSE; +extern boolean decodeRefFrames; /* TRUE = should decode I and P frames */ +extern int fCodeI,fCodeP,fCodeB; +extern boolean forceEncodeLast; +extern int TIME_RATE; + +#endif /* FRAMES_INCLUDED */ + + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/* + * $Header: /n/picasso/project/mpeg/mpeg_dist/mpeg_encode/headers/RCS/frames.h,v 1.13 1995/08/15 23:43:04 smoot Exp $ + * $Log: frames.h,v $ + * Revision 1.13 1995/08/15 23:43:04 smoot + * *** empty log message *** + * + * Revision 1.12 1995/04/14 23:13:18 smoot + * Reorganized for better rate control. Added overflow in DCT values + * handling. + * + * Revision 1.11 1995/01/19 23:54:46 smoot + * allow computediffdcts to un-assert parts of the pattern + * + * Revision 1.10 1995/01/16 07:43:10 eyhung + * Added realQuiet + * + * Revision 1.9 1995/01/10 23:15:28 smoot + * Fixed searchRange lack of def + * + * Revision 1.8 1994/11/15 00:55:36 smoot + * added printMSE + * + * Revision 1.7 1994/11/14 22:51:02 smoot + * added specifics flag. Added BlockComputeSNR parameters + * + * Revision 1.6 1994/11/01 05:07:23 darryl + * with rate control changes added + * + * Revision 1.1 1994/09/27 01:02:55 darryl + * Initial revision + * + * Revision 1.5 1993/07/22 22:24:23 keving + * nothing + * + * Revision 1.4 1993/07/09 00:17:23 keving + * nothing + * + * Revision 1.3 1993/06/03 21:08:53 keving + * nothing + * + * Revision 1.2 1993/03/02 19:00:27 keving + * nothing + * + * Revision 1.1 1993/02/19 20:15:51 keving + * nothing + * + */ + + diff --git a/converter/ppm/ppmtompeg/headers/frametype.h b/converter/ppm/ppmtompeg/headers/frametype.h new file mode 100644 index 00000000..63bee964 --- /dev/null +++ b/converter/ppm/ppmtompeg/headers/frametype.h @@ -0,0 +1,17 @@ +#ifndef FRAMETYPE_H_INCLUDED +#define FRAMETYPE_H_INCLUDED + +char +FType_Type(unsigned int const frameNum); + +unsigned int +FType_FutureRef(unsigned int const currFrameNum); + +int FType_PastRef _ANSI_ARGS_((int currFrameNum)); + +void SetFramePattern(const char * const pattern); + +void +ComputeFrameTable(unsigned int const numFrames); + +#endif diff --git a/converter/ppm/ppmtompeg/headers/fsize.h b/converter/ppm/ppmtompeg/headers/fsize.h new file mode 100644 index 00000000..6a1c910e --- /dev/null +++ b/converter/ppm/ppmtompeg/headers/fsize.h @@ -0,0 +1,49 @@ +/*===========================================================================* + * fsize.h * + * * + * procedures to deal with frame size * + * * + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + + +#ifndef FSIZE_H_INCLUDED +#define FSIZE_H_INCLUDED + +extern int Fsize_x; +extern int Fsize_y; + + +void +Fsize_Reset(void); + +void +Fsize_Validate(int * const xP, + int * const yP); + +void +Fsize_Note(int const id, + unsigned int const width, + unsigned int const height); + +#endif diff --git a/converter/ppm/ppmtompeg/headers/general.h b/converter/ppm/ppmtompeg/headers/general.h new file mode 100644 index 00000000..f29c9445 --- /dev/null +++ b/converter/ppm/ppmtompeg/headers/general.h @@ -0,0 +1,174 @@ +/*===========================================================================* + * general.h * + * * + * very general stuff * + * * + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/* + * $Header: /n/picasso/project/mpeg/mpeg_dist/mpeg_encode/headers/RCS/general.h,v 1.7 1995/08/04 23:34:13 smoot Exp $ + * $Log: general.h,v $ + * Revision 1.7 1995/08/04 23:34:13 smoot + * jpeg5 changed the silly HAVE_BOOLEAN define.... + * + * Revision 1.6 1995/01/19 23:54:49 eyhung + * Changed copyrights + * + * Revision 1.5 1994/11/12 02:12:48 keving + * nothing + * + * Revision 1.4 1993/07/22 22:24:23 keving + * nothing + * + * Revision 1.3 1993/07/09 00:17:23 keving + * nothing + * + * Revision 1.2 1993/06/03 21:08:53 keving + * nothing + * + * Revision 1.1 1993/02/22 22:39:19 keving + * nothing + * + */ + + +#ifndef GENERAL_INCLUDED +#define GENERAL_INCLUDED + + +/* prototypes for library procedures + * + * if your /usr/include headers do not have these, then pass -DMISSING_PROTOS + * to your compiler + * + */ +#ifdef MISSING_PROTOS +int fprintf(); +int fwrite(); +int fread(); +int fflush(); +int fclose(); + +int sscanf(); +int bzero(); +int bcopy(); +int system(); +int time(); +int perror(); + +int socket(); +int bind(); +int listen(); +int accept(); +int connect(); +int close(); +int read(); +int write(); + +int pclose(); + +#endif + + +/*===========* + * CONSTANTS * + *===========*/ + +#ifndef NULL +#define NULL 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +#define SPACE ' ' +#define TAB '\t' +#define SEMICOLON ';' +#define NULL_CHAR '\0' +#define NEWLINE '\n' + + +/*==================* + * TYPE DEFINITIONS * + *==================*/ + +#ifndef HAVE_BOOLEAN +typedef int boolean; +#define HAVE_BOOLEAN +/* JPEG library also defines boolean */ +#endif + +/* In the following, we need the "signed" in order to make these typedefs + match those in AIX system header files. Otherwise, compile fails on + AIX. 2000.09.11. +*/ +typedef unsigned char uint8; +typedef signed char int8; +typedef unsigned short uint16; +typedef signed short int16; + +/* The 32 bit integer types are probably obsolete. + + parallel.c used to use them as buffers for socket I/O, because in the + protocol on the socket, an integer is represented by 4 octets. But + the proper type for that is unsigned char[4], so we changed parallel.c + to that in September 2004. + + A user of TRU64 4.0f in May 2000 used a -DLONG_32 option, but did + not indicate that it was really necessary. After that, Configure + added -DLONG_32 for all TRU64 builds. A user of TRU64 5.1A in July + 2003 demonstrated that int is in fact 4 bytes on his machine (and + long is 8 bytes). So we removed the -DLONG_32 from Configure. This + whole issue may too be obsolete because of the fixing of parallel.c + mentioned above. +*/ + + /* LONG_32 should only be defined iff + * 1) long's are 32 bits and + * 2) int's are not + */ +#ifdef LONG_32 +typedef unsigned long uint32; +typedef long int32; +#else +typedef unsigned int uint32; +typedef signed int int32; +#endif + + +/*========* + * MACROS * + *========*/ + +#undef max +#define max(a,b) ((a) > (b) ? (a) : (b)) +#undef min +#define min(a,b) ((a) < (b) ? (a) : (b)) +#undef abs +#define abs(a) ((a) >= 0 ? (a) : -(a)) + + +#endif diff --git a/converter/ppm/ppmtompeg/headers/huff.h b/converter/ppm/ppmtompeg/headers/huff.h new file mode 100644 index 00000000..47ffb843 --- /dev/null +++ b/converter/ppm/ppmtompeg/headers/huff.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/* + * $Header: /n/charlie-brown/project/mm/mpeg/mpeg_dist/mpeg_encode/headers/RCS/huff.h,v 1.3 1995/01/19 23:54:51 eyhung Exp $ + */ + +/* + * THIS FILE IS MACHINE GENERATED! DO NOT EDIT! + */ +#define HUFF_MAXRUN 32 +#define HUFF_MAXLEVEL 41 + +extern int huff_maxlevel[]; +extern uint32 *huff_table[]; +extern int *huff_bits[]; diff --git a/converter/ppm/ppmtompeg/headers/input.h b/converter/ppm/ppmtompeg/headers/input.h new file mode 100644 index 00000000..75734237 --- /dev/null +++ b/converter/ppm/ppmtompeg/headers/input.h @@ -0,0 +1,70 @@ +#ifndef INPUT_H_INCLUDED +#define INPUT_H_INCLUDED + +#include "pm_c_util.h" +#include "ppm.h" +#include "frame.h" + +struct InputFileEntry; + +struct inputSource { +/*---------------------------------------------------------------------------- + This describes the source of data for the program. + Typically, the source data is a bunch of raw frames in PPM format. + But sometimes, it is a bunch of already encoded frames or GOPs. +-----------------------------------------------------------------------------*/ + bool stdinUsed; + int numInputFiles; + /* This is the maximum number of input files available. If + we're reading from explicitly named files, it is exactly + the number available. If we're reading from a stream, it's + infinity. (At the moment, "reading from a stream" is + equivalent to "reading from Standard Input"). + */ + + /* Members below here defined only if 'stdinUsed' is false */ + + struct InputFileEntry ** inputFileEntries; + /* Each element of this array describes a set of input files. + Valid elements are consecutive starting at index 0. + */ + unsigned int numInputFileEntries; + /* Number of valid entries in array inputFileEntries[] */ + unsigned int ifArraySize; + /* Number of allocated entries in the array inputFileEntries[] */ +}; + + +void +GetNthInputFileName(struct inputSource * const inputSourceP, + unsigned int const n, + const char ** const fileName); + +void +ReadNthFrame(struct inputSource * const inputSourceP, + unsigned int const frameNumber, + boolean const remoteIO, + boolean const childProcess, + boolean const separateConversion, + const char * const slaveConversion, + const char * const inputConversion, + MpegFrame * const frameP, + bool * const endOfStreamP); + +void +JM2JPEG(struct inputSource * const inputSourceP); + +void +AddInputFiles(struct inputSource * const inputSourceP, + const char * const input); + +void +SetStdinInput(struct inputSource * const inputSourceP); + +void +CreateInputSource(struct inputSource ** const inputSourcePP); + +void +DestroyInputSource(struct inputSource * const inputSourceP); + +#endif diff --git a/converter/ppm/ppmtompeg/headers/jpeg.h b/converter/ppm/ppmtompeg/headers/jpeg.h new file mode 100644 index 00000000..62c6f930 --- /dev/null +++ b/converter/ppm/ppmtompeg/headers/jpeg.h @@ -0,0 +1,12 @@ +#include "ansi.h" + + +void +JMovie2JPEG(const char * const infilename, + const char * const obase, + int const start, + int const end); + +void +ReadJPEG(MpegFrame * const mf, + FILE * const fp); diff --git a/converter/ppm/ppmtompeg/headers/mheaders.h b/converter/ppm/ppmtompeg/headers/mheaders.h new file mode 100644 index 00000000..21d43e3d --- /dev/null +++ b/converter/ppm/ppmtompeg/headers/mheaders.h @@ -0,0 +1,114 @@ +/*===========================================================================* + * mheaders.h * + * * + * MPEG headers * + * * + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/* + * $Header: /n/picasso/project/mm/mpeg/mpeg_dist/mpeg_encode/headers/RCS/mheaders.h,v 1.4 1995/03/27 19:29:24 smoot Exp $ + * $Log: mheaders.h,v $ + * Revision 1.4 1995/03/27 19:29:24 smoot + * changed to remove mb_quant + * + * Revision 1.3 1995/01/19 23:54:56 eyhung + * Changed copyrights + * + * Revision 1.2 1994/11/12 02:12:51 keving + * nothing + * + * Revision 1.1 1993/07/22 22:24:23 keving + * nothing + * + * + */ + + +#ifndef MHEADERS_INCLUDED +#define MHEADERS_INCLUDED + + +/*==============* + * HEADER FILES * + *==============*/ + +#include "general.h" +#include "ansi.h" +#include "bitio.h" + + +/*===============================* + * EXTERNAL PROCEDURE prototypes * + *===============================*/ + +void SetGOPStartTime _ANSI_ARGS_((int index)); + +void +Mhead_GenSequenceHeader(BitBucket * const bbPtr, + uint32 const hsize, + uint32 const vsize, + int32 const pratio, + int32 const pict_rate, + int32 const bit_rate_arg, + int32 const buf_size_arg, + int32 const c_param_flag_arg, + const int32 * const iq_matrix, + const int32 * const niq_matrix, + uint8 * const ext_data, + int32 const ext_data_size, + uint8 * const user_data, + int32 const user_data_size); + +void Mhead_GenSequenceEnder _ANSI_ARGS_((BitBucket *bbPtr)); +void Mhead_GenGOPHeader _ANSI_ARGS_((BitBucket *bbPtr, + int32 drop_frame_flag, + int32 tc_hrs, int32 tc_min, + int32 tc_sec, int32 tc_pict, + int32 closed_gop, int32 broken_link, + uint8 *ext_data, int32 ext_data_size, + uint8 *user_data, int32 user_data_size)); +void Mhead_GenPictureHeader _ANSI_ARGS_((BitBucket *bbPtr, int frameType, + int pictCount, int f_code)); +void Mhead_GenSliceHeader _ANSI_ARGS_((BitBucket *bbPtr, uint32 slicenum, + uint32 qscale, uint8 *extra_info, + uint32 extra_info_size)); +void Mhead_GenSliceEnder _ANSI_ARGS_((BitBucket *bbPtr)); +void Mhead_GenMBHeader _ANSI_ARGS_((BitBucket *bbPtr, + uint32 pict_code_type, uint32 addr_incr, + uint32 q_scale, + uint32 forw_f_code, uint32 back_f_code, + uint32 horiz_forw_r, uint32 vert_forw_r, + uint32 horiz_back_r, uint32 vert_back_r, + int32 motion_forw, int32 m_horiz_forw, + int32 m_vert_forw, int32 motion_back, + int32 m_horiz_back, int32 m_vert_back, + uint32 mb_pattern, uint32 mb_intra)); + + +#endif /* MHEADERS_INCLUDED */ + + + + + diff --git a/converter/ppm/ppmtompeg/headers/motion_search.h b/converter/ppm/ppmtompeg/headers/motion_search.h new file mode 100644 index 00000000..ab83cbca --- /dev/null +++ b/converter/ppm/ppmtompeg/headers/motion_search.h @@ -0,0 +1,154 @@ +/*===========================================================================* + * search.h * + * * + * stuff dealing with the motion search * + * * + *===========================================================================*/ + +/*==============* + * HEADER FILES * + *==============*/ + +#include "ansi.h" + + +/*===========* + * CONSTANTS * + *===========*/ + +#define PSEARCH_SUBSAMPLE 0 +#define PSEARCH_EXHAUSTIVE 1 +#define PSEARCH_LOGARITHMIC 2 +#define PSEARCH_TWOLEVEL 3 + +#define BSEARCH_EXHAUSTIVE 0 +#define BSEARCH_CROSS2 1 +#define BSEARCH_SIMPLE 2 + + +/*========* + * MACROS * + *========*/ + +#define COMPUTE_MOTION_BOUNDARY(by,bx,stepSize,leftMY,leftMX,rightMY,rightMX)\ +leftMY = -2*DCTSIZE*by; /* these are valid motion vectors */ \ +leftMX = -2*DCTSIZE*bx; \ +/* these are invalid motion vectors */ \ +rightMY = 2*(Fsize_y - (by+2)*DCTSIZE + 1) - 1; \ +rightMX = 2*(Fsize_x - (bx+2)*DCTSIZE + 1) - 1; \ +\ +if ( stepSize == 2 ) { \ + rightMY++; \ + rightMX++; \ + } + +#define VALID_MOTION(m) \ +(((m).y >= leftMY) && ((m).y < rightMY) && \ + ((m).x >= leftMX) && ((m).x < rightMX) ) + + +/*===============================* + * EXTERNAL PROCEDURE prototypes * + *===============================*/ + +void +SetPSearchAlg(const char * const alg); +void +SetBSearchAlg(const char * const alg); +const char * +BSearchName(void); +const char * +PSearchName(void); + +int +PLogarithmicSearch(const LumBlock * const currentBlockP, + MpegFrame * const prev, + int const by, + int const bx, + vector * const motionP, + int const searchRange); + +int +PSubSampleSearch(const LumBlock * const currentBlockP, + MpegFrame * const prev, + int const by, + int const bx, + vector * const motionP, + int const searchRange); + +int +PLocalSearch(const LumBlock * const currentBlockP, + MpegFrame * const prev, + int const by, + int const bx, + vector * const motionP, + int const bestSoFar, + int const searchRange); + +int +PTwoLevelSearch(const LumBlock * const currentBlockP, + MpegFrame * const prev, + int const by, + int const bx, + vector * const motionP, + int const bestSoFar, + int const searchRange); +void +PMotionSearch(const LumBlock * const currentBlockP, + MpegFrame * const prev, + int const by, + int const bx, + vector * const motionP); + +/*==================* + * GLOBAL VARIABLES * + *==================*/ + +extern int psearchAlg; + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/* + * $Header: /n/picasso/project/mpeg/mpeg_dist/mpeg_encode/headers/RCS/search.h,v 1.6 1995/08/15 23:43:36 smoot Exp $ + * $Log: search.h,v $ + * Revision 1.6 1995/08/15 23:43:36 smoot + * *** empty log message *** + * + * Revision 1.5 1995/01/19 23:55:20 eyhung + * Changed copyrights + * + * Revision 1.4 1994/12/07 00:42:01 smoot + * Added seperate P and B search ranges + * + * Revision 1.3 1994/11/12 02:12:58 keving + * nothing + * + * Revision 1.2 1993/07/22 22:24:23 keving + * nothing + * + * Revision 1.1 1993/07/09 00:17:23 keving + * nothing + * + */ + + + diff --git a/converter/ppm/ppmtompeg/headers/mpeg.h b/converter/ppm/ppmtompeg/headers/mpeg.h new file mode 100644 index 00000000..d739dede --- /dev/null +++ b/converter/ppm/ppmtompeg/headers/mpeg.h @@ -0,0 +1,116 @@ +/*===========================================================================* + * mpeg.h * + * * + * no comment * + * * + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/*==============* + * HEADER FILES * + *==============*/ + +#include <time.h> + +#include "pm_c_util.h" +#include "ppm.h" +#include "ansi.h" +#include "mtypes.h" +#include "frame.h" + +struct inputSource; + +/*===============================* + * EXTERNAL PROCEDURE prototypes * + *===============================*/ + +enum frameContext {CONTEXT_WHOLESTREAM, CONTEXT_GOP, CONTEXT_JUSTFRAMES}; + +void +GenMPEGStream(struct inputSource * const inputSourceP, + enum frameContext const context, + unsigned int const frameStart, + unsigned int const frameEnd, + int32 const qtable[], + int32 const niqtable[], + bool const childProcess, + FILE * const ofp, + const char * const outputFileName, + bool const wantVbvUnderflowWarning, + bool const wantVbvOverflowWarning, + unsigned int * const inputFrameBitsP, + unsigned int * const totalBitsP); + +void +PrintStartStats(time_t const startTime, + bool const specificFrames, + unsigned int const firstFrame, + unsigned int const lastFrame, + struct inputSource * const inputSourceP); + +void +PrintEndStats(time_t const startTime, + time_t const endTime, + unsigned int const inputFrameBits, + unsigned int const totalBits); + +void +ComputeGOPFrames(int const whichGOP, + unsigned int * const firstFrameP, + unsigned int * const lastFrameP, + unsigned int const numFrames); + +extern void IncrementTCTime _ANSI_ARGS_((void)); +void SetReferenceFrameType(const char * const type); + +boolean +NonLocalRefFrame(int const id); + +void +ReadDecodedRefFrame(MpegFrame * const frameP, + unsigned int const frameNumber); + +extern void WriteDecodedFrame _ANSI_ARGS_((MpegFrame *frame)); +extern void SetBitRateFileName _ANSI_ARGS_((char *fileName)); +extern void SetFrameRate _ANSI_ARGS_((void)); + + +/*==================* + * GLOBAL VARIABLES * + *==================*/ + +extern MpegFrame *frameMemory[3]; +extern int32 tc_hrs, tc_min, tc_sec, tc_pict, tc_extra; +extern int totalFramesSent; +extern int gopSize; +extern char *framePattern; +extern int framePatternLen; +extern int32 qtable[]; +extern int32 niqtable[]; +extern int32 *customQtable; +extern int32 *customNIQtable; +extern int aspectRatio; +extern int frameRate; +extern int frameRateRounded; +extern boolean frameRateInteger; + diff --git a/converter/ppm/ppmtompeg/headers/mproto.h b/converter/ppm/ppmtompeg/headers/mproto.h new file mode 100644 index 00000000..c3b0f4b3 --- /dev/null +++ b/converter/ppm/ppmtompeg/headers/mproto.h @@ -0,0 +1,132 @@ +/*===========================================================================* + * mproto.h * + * * + * basically a lot of miscellaneous prototypes * + * * + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/* + * $Header: /n/picasso/project/mm/mpeg/mpeg_dist/mpeg_encode/headers/RCS/mproto.h,v 1.12 1995/03/29 20:14:29 smoot Exp $ + * $Log: mproto.h,v $ + * Revision 1.12 1995/03/29 20:14:29 smoot + * deleted unneeded dct prototype + * + * Revision 1.11 1995/01/19 23:55:02 eyhung + * Changed copyrights + * + * Revision 1.10 1995/01/16 06:20:10 eyhung + * Changed ReadYUV to ReadEYUV + * + * Revision 1.9 1993/07/22 22:24:23 keving + * nothing + * + * Revision 1.8 1993/07/09 00:17:23 keving + * nothing + * + * Revision 1.7 1993/06/03 21:08:53 keving + * nothing + * + * Revision 1.6 1993/02/24 19:13:33 keving + * nothing + * + * Revision 1.5 1993/02/17 23:18:20 dwallach + * checkin prior to keving's joining the project + * + * Revision 1.4 1993/01/18 10:20:02 dwallach + * *** empty log message *** + * + * Revision 1.3 1993/01/18 10:17:29 dwallach + * RCS headers installed, code indented uniformly + * + * Revision 1.3 1993/01/18 10:17:29 dwallach + * RCS headers installed, code indented uniformly + * + */ + + +/*==============* + * HEADER FILES * + *==============*/ + +#include "general.h" +#include "ansi.h" +#include "bitio.h" + + +#define DCTSIZE2 DCTSIZE*DCTSIZE +typedef short DCTELEM; +typedef DCTELEM DCTBLOCK[DCTSIZE2]; + + + +/*===============================* + * EXTERNAL PROCEDURE prototypes * + *===============================*/ + +/* + * from mbasic.c: + */ +void mp_reset _ANSI_ARGS_((void)); +void mp_free _ANSI_ARGS_((MpegFrame *mf)); +MpegFrame *mp_new _ANSI_ARGS_((int fnumber, char type, MpegFrame *oldFrame)); +void mp_ycc_calc _ANSI_ARGS_((MpegFrame *mf)); +void mp_dct_blocks _ANSI_ARGS_((MpegFrame *mf)); +void AllocDecoded _ANSI_ARGS_((MpegFrame *frame)); + +/* + * from moutput.c: + */ +boolean mp_quant_zig_block _ANSI_ARGS_((Block in, FlatBlock out, int qscale, int iblock)); +void UnQuantZig _ANSI_ARGS_((FlatBlock in, Block out, int qscale, boolean iblock)); +void mp_rle_huff_block _ANSI_ARGS_((FlatBlock in, BitBucket *out)); +void mp_rle_huff_pblock _ANSI_ARGS_((FlatBlock in, BitBucket *out)); +void mp_create_blocks _ANSI_ARGS_((MpegFrame *mf)); + + + + +void ReadEYUV _ANSI_ARGS_((MpegFrame * mf, FILE *fpointer, int width, + int height)); +boolean ReadPPM _ANSI_ARGS_((MpegFrame *mf, FILE *fpointer)); +void PPMtoYCC _ANSI_ARGS_((MpegFrame * mf)); + +void MotionSearchPreComputation _ANSI_ARGS_((MpegFrame *frame)); +boolean PMotionSearch _ANSI_ARGS_((LumBlock currentBlock, MpegFrame *prev, + int by, int bx, int *motionY, int *motionX)); +void ComputeHalfPixelData _ANSI_ARGS_((MpegFrame *frame)); +void mp_validate_size _ANSI_ARGS_((int *x, int *y)); +void AllocYCC _ANSI_ARGS_((MpegFrame * mf)); + + +/* jrevdct.c */ +void init_pre_idct _ANSI_ARGS_((void )); +void j_rev_dct_sparse _ANSI_ARGS_((DCTBLOCK data , int pos )); +void j_rev_dct _ANSI_ARGS_((DCTBLOCK data )); +void j_rev_dct_sparse _ANSI_ARGS_((DCTBLOCK data , int pos )); +void j_rev_dct _ANSI_ARGS_((DCTBLOCK data )); + +/* block.c */ +void BlockToData _ANSI_ARGS_((uint8 **data, Block block, int by, int bx)); +void AddMotionBlock _ANSI_ARGS_((Block block, uint8 **prev, int by, int bx, + int my, int mx)); diff --git a/converter/ppm/ppmtompeg/headers/mtypes.h b/converter/ppm/ppmtompeg/headers/mtypes.h new file mode 100644 index 00000000..0db5a330 --- /dev/null +++ b/converter/ppm/ppmtompeg/headers/mtypes.h @@ -0,0 +1,109 @@ +/*===========================================================================* + * mtypes.h + * + * MPEG data types + * + *===========================================================================*/ + +/* Copyright information is at end of file */ + +#ifndef MTYPES_INCLUDED +#define MTYPES_INCLUDED + + +/*==============* + * HEADER FILES * + *==============*/ + +#include "general.h" +#include "dct.h" + + +/*===========* + * CONSTANTS * + *===========*/ + +#define TYPE_BOGUS 0 /* for the header of the circular list */ +#define TYPE_VIRGIN 1 + +#define STATUS_EMPTY 0 +#define STATUS_LOADED 1 +#define STATUS_WRITTEN 2 + + +typedef struct vector { + int y; + int x; +} vector; + +typedef struct motion { + vector fwd; + vector bwd; +} motion; + +/*==================* + * TYPE DEFINITIONS * + *==================*/ + +/* + * your basic Block type + */ +typedef int16 Block[DCTSIZE][DCTSIZE]; +typedef int16 FlatBlock[DCTSIZE_SQ]; +typedef struct { + int32 l[2*DCTSIZE][2*DCTSIZE]; +} LumBlock; +typedef int32 ChromBlock[DCTSIZE][DCTSIZE]; + +/*========* + * MACROS * + *========*/ + +#ifdef ABS +#undef ABS +#endif + +#define ABS(x) (((x)<0)?-(x):(x)) + +#ifdef HEINOUS_DEBUG_MODE +#define DBG_PRINT(x) {printf x; fflush(stdout);} +#else +#define DBG_PRINT(x) +#endif + +#define ERRCHK(bool, str) {if(!(bool)) {perror(str); exit(1);}} + +/* For Specifics */ +typedef struct detalmv_def { + int typ,fx,fy,bx,by; +} BlockMV; +#define TYP_SKIP 0 +#define TYP_FORW 1 +#define TYP_BACK 2 +#define TYP_BOTH 3 + + +#endif /* MTYPES_INCLUDED */ + + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + diff --git a/converter/ppm/ppmtompeg/headers/opts.h b/converter/ppm/ppmtompeg/headers/opts.h new file mode 100644 index 00000000..5901a677 --- /dev/null +++ b/converter/ppm/ppmtompeg/headers/opts.h @@ -0,0 +1,125 @@ +/* + * opts.h - set optional parameters + */ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/* + * $Header: /n/picasso/project/mpeg/mpeg_dist/mpeg_encode/headers/RCS/opts.h,v 1.3 1995/08/15 23:43:43 smoot Exp $ + * $Log: opts.h,v $ + * Revision 1.3 1995/08/15 23:43:43 smoot + * *** empty log message *** + * + * Revision 1.2 1995/05/02 22:00:51 smoot + * added TUNEing stuff + * + * Revision 1.1 1995/04/14 23:12:53 smoot + * Initial revision + * + */ + +#include "general.h" +#include "ansi.h" +#include "mtypes.h" + +/* + TUNE b [limit] lower limit on how different a block must be to be DCT coded + TUNE c [file [color-diff]] Collect statistics on Quantization + TUNE d [RateScale DistortionScale] Do a DCT in the P search, not just DIFF + TUNE k [breakpt end [slope]] Squash small lum values + TUNE l Figure out Laplacian distrib and use them to dequantize and do snr calc + TUNE n Dont consider DC differenece in DCT searches + TUNE q Do MSE for distortion measure, not MAD + TUNE s [Max] | [LumMax ChromMax] Squash small differences in successive frames + TUNE u disallow skip blocks in B frames + TUNE w filename [c] Write I block distortion numbers to file [with bit-rates] + TUNE z Zaps Intra blocks in P/B frames. + + [ Note k and s make -snr numbers a lie, by playing with input ] + [ Note d n and q are contradictory (can only use one) ] + [ Note c will not work on parallel encodings ] +*/ + +extern boolean tuneingOn; + +/* Smash to no-change a motion block DCT with MAD less than: */ +/* DETAL b value */ +extern int block_bound; + +/* Collect info on quantization */ +extern boolean collect_quant; +extern int collect_quant_detailed; +extern FILE *collect_quant_fp; + +/* Nuke dim areas */ +extern int kill_dim, kill_dim_break, kill_dim_end; +extern float kill_dim_slope; + + +/* Stuff to control MV search comparisons */ +#define DEFAULT_SEARCH 0 +#define LOCAL_DCT 1 /* Do DCT in search (SLOW!!!!) */ +#define NO_DC_SEARCH 2 /* Dont consider DC component in motion searches */ +#define DO_Mean_Squared_Distortion 3 /* Do Squared distortion, not ABS */ + +/* Parameters for special searches */ +/* LOCAL_DCT */ +extern float LocalDCTRateScale, LocalDCTDistortScale; + +/* Search Type Variable */ +extern int SearchCompareMode; + +/* squash small differences */ +extern boolean squash_small_differences; +extern int SquashMaxLum, SquashMaxChr; + +/* Disallows Intra blocks in P/B code */ +extern boolean IntraPBAllowed; + +/* Write out distortion numbers */ +extern boolean WriteDistortionNumbers; +extern int collect_distortion_detailed; +extern FILE *distortion_fp; +extern FILE *fp_table_rate[31], *fp_table_dist[31]; + +/* Laplacian Distrib */ +extern boolean DoLaplace; +extern double **L1, **L2, **Lambdas; +extern int LaplaceNum, LaplaceCnum; + +/* Turn on/off skipping in B frames */ +extern boolean BSkipBlocks; + +/* Procedures Prototypes */ +int GetIQScale _ANSI_ARGS_((void)); +int GetPQScale _ANSI_ARGS_((void)); +int GetBQScale _ANSI_ARGS_((void)); +void Tune_Init _ANSI_ARGS_((void)); +int CalcRLEHuffLength _ANSI_ARGS_((FlatBlock in)); +void ParseTuneParam(const char * const charPtr); +int mse _ANSI_ARGS_((Block blk1, Block blk2)); +void Mpost_UnQuantZigBlockLaplace _ANSI_ARGS_((FlatBlock in, Block out, int qscale, boolean iblock)); +extern void CalcLambdas(void); + + + + diff --git a/converter/ppm/ppmtompeg/headers/parallel.h b/converter/ppm/ppmtompeg/headers/parallel.h new file mode 100644 index 00000000..e18d3f46 --- /dev/null +++ b/converter/ppm/ppmtompeg/headers/parallel.h @@ -0,0 +1,135 @@ +/*===========================================================================* + * parallel.h + * + * parallel encoding + * + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/*==============* + * HEADER FILES * + *==============*/ + +#include "ansi.h" +#include "bitio.h" +#include "frame.h" + + +struct inputSource; + +/*===============================* + * EXTERNAL PROCEDURE prototypes * + *===============================*/ + +void +MasterServer(struct inputSource * const inputSourceP, + const char * const paramFileName, + const char * const outputFileName); + +void +NotifyMasterDone(const char * const hostName, + int const portNum, + int const machineNumber, + unsigned int const seconds, + boolean * const moreWorkToDoP, + int * const frameStartP, + int * const frameEndP); + +void +IoServer(struct inputSource * const inputSourceP, + const char * const parallelHostName, + int const portNum); + +void +CombineServer(int const numInputFiles, + const char * const masterHostName, + int const masterPortNum, + const char* const outputFileName); + +void +DecodeServer(int const numInputFiles, + const char * const decodeFileName, + const char * const parallelHostName, + int const portNum); + +void +WaitForOutputFile(int number); + +void +GetRemoteFrame(MpegFrame * const frameP, + int const frameNumber); + +void +SendRemoteFrame(int const frameNumber, + BitBucket * const bbP); + +void +NoteFrameDone(int frameStart, int frameEnd); + +void +SetIOConvert(boolean separate); + +void +SetRemoteShell(const char * const shell); + +void +NotifyDecodeServerReady(int const id); + +void +WaitForDecodedFrame(int id); + +void +SendDecodedFrame(MpegFrame * const frameP); + +void +GetRemoteDecodedRefFrame(MpegFrame * const frameP, + int const frameNumber); + +void +SetParallelPerfect(boolean val); + + +/*==================* + * GLOBAL VARIABLES * + *==================*/ + +extern int parallelTestFrames; +extern int parallelTimeChunks; + +extern const char *IOhostName; +extern int ioPortNumber; +extern int decodePortNumber; + +extern boolean ioServer; +extern boolean niceProcesses; +extern boolean forceIalign; +extern int machineNumber; +extern boolean remoteIO; +extern boolean separateConversion; + + + + + + + diff --git a/converter/ppm/ppmtompeg/headers/param.h b/converter/ppm/ppmtompeg/headers/param.h new file mode 100644 index 00000000..31be61ee --- /dev/null +++ b/converter/ppm/ppmtompeg/headers/param.h @@ -0,0 +1,87 @@ +/*===========================================================================* + * param.h * + * * + * reading the parameter file * + * * + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#include "pm_c_util.h" +#include "ansi.h" +#include "input.h" + + +/*===========* + * CONSTANTS * + *===========*/ + +#define MAX_MACHINES 256 +#ifndef MAXPATHLEN +#define MAXPATHLEN 1024 +#endif + +#define ENCODE_FRAMES 0 +#define COMBINE_GOPS 1 +#define COMBINE_FRAMES 2 + + +struct params { + struct inputSource * inputSourceP; + bool warnUnderflow; + bool warnOverflow; +}; + + +void +ReadParamFile(const char * const fileName, + int const function, + struct params * const paramP); + +/*==================* + * GLOBAL VARIABLES * + *==================*/ + +/* All this stuff ought to be in a struct param instead */ + +extern char outputFileName[256]; +extern int whichGOP; +extern int numMachines; +extern char machineName[MAX_MACHINES][256]; +extern char userName[MAX_MACHINES][256]; +extern char executable[MAX_MACHINES][1024]; +extern char remoteParamFile[MAX_MACHINES][1024]; +extern boolean remote[MAX_MACHINES]; +extern char currentPath[MAXPATHLEN]; +extern char currentFramePath[MAXPATHLEN]; +extern char currentGOPPath[MAXPATHLEN]; +extern char inputConversion[1024]; +extern char yuvConversion[256]; +extern int yuvWidth, yuvHeight; +extern int realWidth, realHeight; +extern char ioConversion[1024]; +extern char slaveConversion[1024]; +extern FILE *bitRateFile; +extern boolean showBitRatePerFrame; +extern boolean computeMVHist; +extern const double VidRateNum[9]; +extern boolean keepTempFiles; diff --git a/converter/ppm/ppmtompeg/headers/postdct.h b/converter/ppm/ppmtompeg/headers/postdct.h new file mode 100644 index 00000000..3f3b51fe --- /dev/null +++ b/converter/ppm/ppmtompeg/headers/postdct.h @@ -0,0 +1,40 @@ +/*===========================================================================* + * postdct.h * + * * + * MPEG post-DCT processing * + * * + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#include "bitio.h" + + +int Mpost_QuantZigBlock (Block in, FlatBlock out, int qscale, int iblock); +void Mpost_UnQuantZigBlock (FlatBlock in, Block out, + int qscale, boolean iblock); +void Mpost_RLEHuffIBlock (FlatBlock in, BitBucket *out); +void Mpost_RLEHuffPBlock (FlatBlock in, BitBucket *out); + +#define MPOST_ZERO 0 +#define MPOST_NON_ZERO 1 +#define MPOST_OVERFLOW (-1) diff --git a/converter/ppm/ppmtompeg/headers/prototypes.h b/converter/ppm/ppmtompeg/headers/prototypes.h new file mode 100644 index 00000000..d729aafa --- /dev/null +++ b/converter/ppm/ppmtompeg/headers/prototypes.h @@ -0,0 +1,78 @@ +/*===========================================================================* + * prototypes.h * + * * + * miscellaneous prototypes * + * * + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ +/*==============* + * HEADER FILES * + *==============*/ + +#include "general.h" +#include "ansi.h" +#include "frame.h" + + +/*===============================* + * EXTERNAL PROCEDURE prototypes * + *===============================*/ + +int GetBQScale _ANSI_ARGS_((void)); +int GetPQScale _ANSI_ARGS_((void)); +void ResetBFrameStats _ANSI_ARGS_((void)); +void ResetPFrameStats _ANSI_ARGS_((void)); +void SetSearchRange (int const pixelsP, + int const pixelsB); +void ResetIFrameStats _ANSI_ARGS_((void)); +void +SetPixelSearch(const char * const searchType); +void SetIQScale _ANSI_ARGS_((int qI)); +void SetPQScale _ANSI_ARGS_((int qP)); +void SetBQScale _ANSI_ARGS_((int qB)); +float EstimateSecondsPerIFrame _ANSI_ARGS_((void)); +float EstimateSecondsPerPFrame _ANSI_ARGS_((void)); +float EstimateSecondsPerBFrame _ANSI_ARGS_((void)); +void SetGOPSize _ANSI_ARGS_((int size)); +void +SetStatFileName(const char * const fileName); +void SetSlicesPerFrame _ANSI_ARGS_((int number)); +void SetBlocksPerSlice _ANSI_ARGS_((void)); + + +void DCTFrame _ANSI_ARGS_((MpegFrame * mf)); + +void PPMtoYCC _ANSI_ARGS_((MpegFrame * mf)); + +void MotionSearchPreComputation _ANSI_ARGS_((MpegFrame *frame)); + +void ComputeHalfPixelData _ANSI_ARGS_((MpegFrame *frame)); +void mp_validate_size _ANSI_ARGS_((int *x, int *y)); + +extern void SetFCode _ANSI_ARGS_((void)); + + +/* psearch.c */ +void ShowPMVHistogram _ANSI_ARGS_((FILE *fpointer)); +void ShowBBMVHistogram _ANSI_ARGS_((FILE *fpointer)); +void ShowBFMVHistogram _ANSI_ARGS_((FILE *fpointer)); diff --git a/converter/ppm/ppmtompeg/headers/psocket.h b/converter/ppm/ppmtompeg/headers/psocket.h new file mode 100644 index 00000000..214f5dce --- /dev/null +++ b/converter/ppm/ppmtompeg/headers/psocket.h @@ -0,0 +1,41 @@ +#ifndef PSOCKET_H_INCLUDED +#define PSOCKET_H_INCLUDED + +#include <netdb.h> + +void +ReadInt(int const socketFd, + int * const valueP); + +void +ReadBytes(int const fd, + unsigned char * const buf, + unsigned int const nbyte); + +void +WriteInt(int const socketFd, + int const value); + +void +WriteBytes(int const fd, + unsigned char * const buf, + unsigned int const nbyte); + +void +ConnectToSocket(const char * const machineName, + int const portNum, + struct hostent ** const hostEnt, + int * const socketFdP, + const char ** const errorP); + +void +CreateListeningSocket(int * const socketP, + int * const portNumP, + const char ** const errorP); + +void +AcceptConnection(int const listenSocketFd, + int * const connectSocketFdP, + const char ** const errorP); + +#endif diff --git a/converter/ppm/ppmtompeg/headers/rate.h b/converter/ppm/ppmtompeg/headers/rate.h new file mode 100644 index 00000000..df5ca1cc --- /dev/null +++ b/converter/ppm/ppmtompeg/headers/rate.h @@ -0,0 +1,204 @@ +/*===========================================================================* + * rate.h * + * * + * Procedures concerned with rate control + * * + * EXPORTED PROCEDURES: * + * getRateMode() + * setBitRate() + * getBitRate() + * setBufferSize() + * getBufferSize() + * initRateControl() + * targetRateControl() + * updateRateControl() + * MB_RateOut() + * * + *===========================================================================*/ + +/* COPYRIGHT INFO HERE */ + +#define VARIABLE_RATE 0 +#define FIXED_RATE 1 + + +/*==================* + * Exported VARIABLES * + *==================*/ + + +extern int rc_bitsThisMB; +extern int rc_numBlocks; +extern int rc_totalQuant; +extern int rc_quantOverride; + + +/*=====================* + * EXPORTED PROCEDURES * + *=====================*/ + +/*===========================================================================* + * + * initRateControl + * + * initialize the allocation parameters. + *===========================================================================*/ +int +initRateControl(bool const wantUnderflowWarning, + bool const wantOverflowWarning); + +/*===========================================================================* + * + * targetRateControl + * + * Determine the target allocation for given picture type. + * + * RETURNS: target size in bits + *===========================================================================*/ +void +targetRateControl(MpegFrame * const frameP); + + +/*===========================================================================* + * + * MB_RateOut + * + * Prints out sampling of MB rate control data. Every "nth" block + * stats are printed, with "n" controled by global RC_MB_SAMPLE_RATE + * + * RETURNS: nothing + *===========================================================================*/ +extern void MB_RateOut _ANSI_ARGS_((int type)); + + +/*===========================================================================* + * + * updateRateControl + * + * Update the statistics kept, after end of frame + * + * RETURNS: nothing + * + * SIDE EFFECTS: many global variables + *===========================================================================*/ +void +updateRateControl(int const type); + + +/*===========================================================================* + * + * needQScaleChange(current Q scale, 4 luminance blocks) + * + * + * RETURNS: new Qscale + *===========================================================================*/ +extern int needQScaleChange _ANSI_ARGS_((int oldQScale, Block blk0, Block blk1, Block blk2, Block blk3)); + +/*===========================================================================* + * + * incNumBlocks() + * + * + * RETURNS: nothing + *===========================================================================*/ +extern void incNumBlocks _ANSI_ARGS_((int num)); + + +/*===========================================================================* + * + * incMacroBlockBits() + * + * Increments the number of Macro Block bits and the total of Frame + * bits by the number passed. + * + * RETURNS: nothing + *===========================================================================*/ +extern void incMacroBlockBits _ANSI_ARGS_((int num)); + + +/*===========================================================================* + * + * SetRateControl () + * + * Checks the string parsed from the parameter file. Verifies + * number and sets global values. + * + * RETURNS: nothing + *===========================================================================*/ +extern void SetRateControl _ANSI_ARGS_((char *charPtr)); + + +/*===========================================================================* + * + * setBufferSize () + * + * Checks the string parsed from the parameter file. Verifies + * number and sets global values. + * + * RETURNS: nothing + *===========================================================================*/ +extern void +setBufferSize(const char * const charPtr); + + +/*===========================================================================* + * + * getBufferSize () + * + * returns the buffer size read from the parameter file. Size is + * in bits- not in units of 16k as written to the sequence header. + * + * RETURNS: int (or -1 if invalid) + *===========================================================================*/ +extern int getBufferSize _ANSI_ARGS_((void)); + + +/*===========================================================================* + * + * setBitRate () + * + * Checks the string parsed from the parameter file. Verifies + * number and sets global values. + * + * RETURNS: nothing + * + * SIDE EFFECTS: global variables + *===========================================================================*/ +extern void +setBitRate(const char * const charPtr); + + +/*===========================================================================* + * + * getBitRate () + * + * Returns the bit rate read from the parameter file. This is the + * real rate in bits per second, not in 400 bit units as is written to + * the sequence header. + * + * RETURNS: int (-1 if Variable mode operation) + *===========================================================================*/ +extern int getBitRate _ANSI_ARGS_((void)); + + +/*===========================================================================* + * + * getRateMode () + * + * Returns the rate mode- interpreted waa either Fixed or Variable + * + * RETURNS: integer + *===========================================================================*/ +extern int getRateMode _ANSI_ARGS_((void)); + + +/*===========================================================================* + * + * incQuantOverride() + * + * counter of override of quantization + * + * RETURNS: nothing + *===========================================================================*/ +extern void incQuantOverride _ANSI_ARGS_((int num)); + diff --git a/converter/ppm/ppmtompeg/headers/readframe.h b/converter/ppm/ppmtompeg/headers/readframe.h new file mode 100644 index 00000000..3a6876b1 --- /dev/null +++ b/converter/ppm/ppmtompeg/headers/readframe.h @@ -0,0 +1,69 @@ +/*===========================================================================* + * readframe.h * + * * + * stuff dealing with reading frames * + * * + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#include "pm_c_util.h" + +/*===========* + * CONSTANTS * + *===========*/ + +#define PPM_FILE_TYPE 0 +#define YUV_FILE_TYPE 2 +#define ANY_FILE_TYPE 3 +#define BASE_FILE_TYPE 4 +#define PNM_FILE_TYPE 5 +#define SUB4_FILE_TYPE 6 +#define JPEG_FILE_TYPE 7 +#define JMOVIE_FILE_TYPE 8 +#define Y_FILE_TYPE 9 + + +struct inputSource; + +void +ReadFrameFile(MpegFrame * const frameP, + FILE * const ifP, + const char * const conversion, + bool * const eofP); + +void +ReadFrame(MpegFrame * const frameP, + struct inputSource * const inputSourceP, + unsigned int const frameNumber, + const char * const conversion, + bool * const endOfStreamP); + +FILE * +ReadIOConvert(struct inputSource * const inputSourceP, + unsigned int const frameNumber); + +extern void SetFileType(const char * const conversion); +extern void SetFileFormat(const char * const format); +extern void SetResize(bool const set); + +extern int baseFormat; diff --git a/converter/ppm/ppmtompeg/headers/rgbtoycc.h b/converter/ppm/ppmtompeg/headers/rgbtoycc.h new file mode 100644 index 00000000..52159963 --- /dev/null +++ b/converter/ppm/ppmtompeg/headers/rgbtoycc.h @@ -0,0 +1,39 @@ +/*===========================================================================* + * rgbtoycc.h * + * * + * Procedures to convert from RGB space to YUV space * + * * + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + + + +#include "frame.h" +#include "pnm.h" + +void +PNMtoYUV(MpegFrame * const frame, + xel ** const xels, + unsigned int const cols, + unsigned int const rows, + xelval const maxval); diff --git a/converter/ppm/ppmtompeg/headers/specifics.h b/converter/ppm/ppmtompeg/headers/specifics.h new file mode 100644 index 00000000..7bcf4ace --- /dev/null +++ b/converter/ppm/ppmtompeg/headers/specifics.h @@ -0,0 +1,36 @@ +#include "ansi.h" + + +/*===========* + * TYPES * + *===========*/ + +typedef struct bs_def { + int num; + boolean relative; + char qscale; + BlockMV *mv; /* defined in mtypes.h */ + struct bs_def *next; +} Block_Specifics; + +typedef struct detail_def { + int num; + char qscale; + struct detail_def *next; +} Slice_Specifics; + +typedef struct fsl_def { + int framenum; + int frametype; + char qscale; + Slice_Specifics *slc; + Block_Specifics *bs; + struct fsl_def *next; +} FrameSpecList; + + +void Specifics_Init _ANSI_ARGS_((void)); +int SpecLookup _ANSI_ARGS_((int fn, int typ, int num, + BlockMV **info, int start_qs)); +int SpecTypeLookup _ANSI_ARGS_((int fn)); + diff --git a/converter/ppm/ppmtompeg/huff.c b/converter/ppm/ppmtompeg/huff.c new file mode 100644 index 00000000..821daca1 --- /dev/null +++ b/converter/ppm/ppmtompeg/huff.c @@ -0,0 +1,131 @@ +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/* + * $Header: /n/charlie-brown/project/mm/mpeg/mpeg_dist/mpeg_encode/RCS/huff.c,v 1.6 1995/01/19 23:07:39 eyhung Exp $ + */ + +/* + * THIS FILE IS MACHINE GENERATED! DO NOT EDIT! + */ +#include "mtypes.h" +#include "huff.h" + +int huff_maxlevel[32] = { 41, 19, 6, 5, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; + +uint32 huff_table0[41] = { 0x0, 0x6, 0x8, 0xa, 0xc, 0x4c, 0x42, 0x14, 0x3a, 0x30, 0x26, 0x20, 0x34, 0x32, 0x30, 0x2e, 0x3e, 0x3c, 0x3a, 0x38, 0x36, 0x34, 0x32, 0x30, 0x2e, 0x2c, 0x2a, 0x28, 0x26, 0x24, 0x22, 0x20, 0x30, 0x2e, 0x2c, 0x2a, 0x28, 0x26, 0x24, 0x22, 0x20 }; +int huff_bits0[41] = { 0, 3, 5, 6, 8, 9, 9, 11, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16 }; + +uint32 huff_table1[19] = { 0x0, 0x6, 0xc, 0x4a, 0x18, 0x36, 0x2c, 0x2a, 0x3e, 0x3c, 0x3a, 0x38, 0x36, 0x34, 0x32, 0x26, 0x24, 0x22, 0x20 }; +int huff_bits1[19] = { 0, 4, 7, 9, 11, 13, 14, 14, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17 }; + +uint32 huff_table2[6] = { 0x0, 0xa, 0x8, 0x16, 0x28, 0x28 }; +int huff_bits2[6] = { 0, 5, 8, 11, 13, 14 }; + +uint32 huff_table3[5] = { 0x0, 0xe, 0x48, 0x38, 0x26 }; +int huff_bits3[5] = { 0, 6, 9, 13, 14 }; + +uint32 huff_table4[4] = { 0x0, 0xc, 0x1e, 0x24 }; +int huff_bits4[4] = { 0, 6, 11, 13 }; + +uint32 huff_table5[4] = { 0x0, 0xe, 0x12, 0x24 }; +int huff_bits5[4] = { 0, 7, 11, 14 }; + +uint32 huff_table6[4] = { 0x0, 0xa, 0x3c, 0x28 }; +int huff_bits6[4] = { 0, 7, 13, 17 }; + +uint32 huff_table7[3] = { 0x0, 0x8, 0x2a }; +int huff_bits7[3] = { 0, 7, 13 }; + +uint32 huff_table8[3] = { 0x0, 0xe, 0x22 }; +int huff_bits8[3] = { 0, 8, 13 }; + +uint32 huff_table9[3] = { 0x0, 0xa, 0x22 }; +int huff_bits9[3] = { 0, 8, 14 }; + +uint32 huff_table10[3] = { 0x0, 0x4e, 0x20 }; +int huff_bits10[3] = { 0, 9, 14 }; + +uint32 huff_table11[3] = { 0x0, 0x46, 0x34 }; +int huff_bits11[3] = { 0, 9, 17 }; + +uint32 huff_table12[3] = { 0x0, 0x44, 0x32 }; +int huff_bits12[3] = { 0, 9, 17 }; + +uint32 huff_table13[3] = { 0x0, 0x40, 0x30 }; +int huff_bits13[3] = { 0, 9, 17 }; + +uint32 huff_table14[3] = { 0x0, 0x1c, 0x2e }; +int huff_bits14[3] = { 0, 11, 17 }; + +uint32 huff_table15[3] = { 0x0, 0x1a, 0x2c }; +int huff_bits15[3] = { 0, 11, 17 }; + +uint32 huff_table16[3] = { 0x0, 0x10, 0x2a }; +int huff_bits16[3] = { 0, 11, 17 }; + +uint32 huff_table17[2] = { 0x0, 0x3e }; +int huff_bits17[2] = { 0, 13 }; + +uint32 huff_table18[2] = { 0x0, 0x34 }; +int huff_bits18[2] = { 0, 13 }; + +uint32 huff_table19[2] = { 0x0, 0x32 }; +int huff_bits19[2] = { 0, 13 }; + +uint32 huff_table20[2] = { 0x0, 0x2e }; +int huff_bits20[2] = { 0, 13 }; + +uint32 huff_table21[2] = { 0x0, 0x2c }; +int huff_bits21[2] = { 0, 13 }; + +uint32 huff_table22[2] = { 0x0, 0x3e }; +int huff_bits22[2] = { 0, 14 }; + +uint32 huff_table23[2] = { 0x0, 0x3c }; +int huff_bits23[2] = { 0, 14 }; + +uint32 huff_table24[2] = { 0x0, 0x3a }; +int huff_bits24[2] = { 0, 14 }; + +uint32 huff_table25[2] = { 0x0, 0x38 }; +int huff_bits25[2] = { 0, 14 }; + +uint32 huff_table26[2] = { 0x0, 0x36 }; +int huff_bits26[2] = { 0, 14 }; + +uint32 huff_table27[2] = { 0x0, 0x3e }; +int huff_bits27[2] = { 0, 17 }; + +uint32 huff_table28[2] = { 0x0, 0x3c }; +int huff_bits28[2] = { 0, 17 }; + +uint32 huff_table29[2] = { 0x0, 0x3a }; +int huff_bits29[2] = { 0, 17 }; + +uint32 huff_table30[2] = { 0x0, 0x38 }; +int huff_bits30[2] = { 0, 17 }; + +uint32 huff_table31[2] = { 0x0, 0x36 }; +int huff_bits31[2] = { 0, 17 }; + +uint32 *huff_table[32] = { huff_table0, huff_table1, huff_table2, huff_table3, huff_table4, huff_table5, huff_table6, huff_table7, huff_table8, huff_table9, huff_table10, huff_table11, huff_table12, huff_table13, huff_table14, huff_table15, huff_table16, huff_table17, huff_table18, huff_table19, huff_table20, huff_table21, huff_table22, huff_table23, huff_table24, huff_table25, huff_table26, huff_table27, huff_table28, huff_table29, huff_table30, huff_table31 }; +int *huff_bits[32] = { huff_bits0, huff_bits1, huff_bits2, huff_bits3, huff_bits4, huff_bits5, huff_bits6, huff_bits7, huff_bits8, huff_bits9, huff_bits10, huff_bits11, huff_bits12, huff_bits13, huff_bits14, huff_bits15, huff_bits16, huff_bits17, huff_bits18, huff_bits19, huff_bits20, huff_bits21, huff_bits22, huff_bits23, huff_bits24, huff_bits25, huff_bits26, huff_bits27, huff_bits28, huff_bits29, huff_bits30, huff_bits31 }; diff --git a/converter/ppm/ppmtompeg/huff.h b/converter/ppm/ppmtompeg/huff.h new file mode 100644 index 00000000..4d0b8840 --- /dev/null +++ b/converter/ppm/ppmtompeg/huff.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/* + * $Header: /n/charlie-brown/project/mm/mpeg/mpeg_dist/mpeg_encode/RCS/huff.h,v 1.2 1995/01/19 23:08:28 eyhung Exp $ + */ + +/* + * THIS FILE IS MACHINE GENERATED! DO NOT EDIT! + */ +#define HUFF_MAXRUN 32 +#define HUFF_MAXLEVEL 41 + +extern int huff_maxlevel[]; +extern uint32 *huff_table[]; +extern int *huff_bits[]; diff --git a/converter/ppm/ppmtompeg/huff.table b/converter/ppm/ppmtompeg/huff.table new file mode 100644 index 00000000..4f01d325 --- /dev/null +++ b/converter/ppm/ppmtompeg/huff.table @@ -0,0 +1,172 @@ +# +# Copyright (c) 1993 The Regents of the University of California. +# All rights reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose, without fee, and without written agreement is +# hereby granted, provided that the above copyright notice and the following +# two paragraphs appear in all copies of this software. +# +# IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR +# DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT +# OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF +# CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +# ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO +# PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +# + +# +# $Header: /n/picasso/users/dwallach/vid2/mpeg_encode/RCS/huff.table,v 1.3 1993/02/17 23:21:58 dwallach Exp $ +# $Log: huff.table,v $ +# Revision 1.3 1993/02/17 23:21:58 dwallach +# checkin prior to keving's joining the project +# +# Revision 1.2 1993/01/18 10:20:02 dwallach +# *** empty log message *** +# +# + +# this files is the raw Huffman encoding tables, from +# the MPEG draft standard, P. D-41 - D-43 (Table D-2.15) + +# Format: +# Run Level VLC Code + +# Run 0, Level 1 is special -- this table has the "NOT FIRST COEFF" entry +# The "IF FIRST COEFF" would be: 0 1 1s + +0 1 11s + +0 2 0100 s +0 3 0010 1s +0 4 0000 110s +0 5 0010 0110 s +0 6 0010 0001 s +0 7 0000 0010 10s +0 8 0000 0001 1101 s +0 9 0000 0001 1000 s +0 10 0000 0001 0011 s +0 11 0000 0001 0000 s +0 12 0000 0000 1101 0s +0 13 0000 0000 1100 1s +0 14 0000 0000 1100 0s +0 15 0000 0000 1011 1s +0 16 0000 0000 0111 11s +0 17 0000 0000 0111 10s +0 18 0000 0000 0111 01s +0 19 0000 0000 0111 00s +0 20 0000 0000 0110 11s +0 21 0000 0000 0110 10s +0 22 0000 0000 0110 01s +0 23 0000 0000 0110 00s +0 24 0000 0000 0101 11s +0 25 0000 0000 0101 10s +0 26 0000 0000 0101 01s +0 27 0000 0000 0101 00s +0 28 0000 0000 0100 11s +0 29 0000 0000 0100 10s +0 30 0000 0000 0100 01s +0 31 0000 0000 0100 00s +0 32 0000 0000 0011 000s +0 33 0000 0000 0010 111s +0 34 0000 0000 0010 110s +0 35 0000 0000 0010 101s +0 36 0000 0000 0010 100s +0 37 0000 0000 0010 011s +0 38 0000 0000 0010 010s +0 39 0000 0000 0010 001s +0 40 0000 0000 0010 000s + +1 1 011s +1 2 0001 10s +1 3 0010 0101 s +1 4 0000 0011 00s +1 5 0000 0001 1011 s +1 6 0000 0000 1011 0s +1 7 0000 0000 1010 1s +1 8 0000 0000 0011 111s +1 9 0000 0000 0011 110s +1 10 0000 0000 0011 101s +1 11 0000 0000 0011 100s +1 12 0000 0000 0011 011s +1 13 0000 0000 0011 010s +1 14 0000 0000 0011 001s +1 15 0000 0000 0001 0011 s +1 16 0000 0000 0001 0010 s +1 17 0000 0000 0001 0001 s +1 18 0000 0000 0001 0000 s + +2 1 0101 s +2 2 0000 100s +2 3 0000 0010 11s +2 4 0000 0001 0100 s +2 5 0000 0000 1010 0s + +3 1 0011 1s +3 2 0010 0100 s +3 3 0000 0001 1100 s +3 4 0000 0000 1001 1s + +4 1 0011 0s +4 2 0000 0011 11s +4 3 0000 0001 0010 s + +5 1 0001 11s +5 2 0000 0010 01s +5 3 0000 0000 1001 0s + +6 1 0001 01s +6 2 0000 0001 1110 s +6 3 0000 0000 0001 0100 s + +7 1 0001 00s +7 2 0000 0001 0101 s + +8 1 0000 111s +8 2 0000 0001 0001 s + +9 1 0000 101s +9 2 0000 0000 1000 1s + +10 1 0010 0111 s +10 2 0000 0000 1000 0s + +11 1 0010 0011 s +11 2 0000 0000 0001 1010 s + +12 1 0010 0010 s +12 2 0000 0000 0001 1001 s + +13 1 0010 0000 s +13 2 0000 0000 0001 1000 s + +14 1 0000 0011 10s +14 2 0000 0000 0001 0111 s + +15 1 0000 0011 01s +15 2 0000 0000 0001 0110 s + +16 1 0000 0010 00s +16 2 0000 0000 0001 0101 s + +17 1 0000 0001 1111 s +18 1 0000 0001 1010 s +19 1 0000 0001 1001 s +20 1 0000 0001 0111 s +21 1 0000 0001 0110 s + +22 1 0000 0000 1111 1s +23 1 0000 0000 1111 0s +24 1 0000 0000 1110 1s +25 1 0000 0000 1110 0s +26 1 0000 0000 1101 1s + +27 1 0000 0000 0001 1111 s +28 1 0000 0000 0001 1110 s +29 1 0000 0000 0001 1101 s +30 1 0000 0000 0001 1100 s +31 1 0000 0000 0001 1011 s diff --git a/converter/ppm/ppmtompeg/iframe.c b/converter/ppm/ppmtompeg/iframe.c new file mode 100644 index 00000000..cb6d4683 --- /dev/null +++ b/converter/ppm/ppmtompeg/iframe.c @@ -0,0 +1,1062 @@ +/*===========================================================================* + * iframe.c * + * * + * Procedures concerned with the I-frame encoding * + * * + * EXPORTED PROCEDURES: * + * GenIFrame * + * SetSlicesPerFrame * + * SetBlocksPerSlice * + * SetIQScale * + * GetIQScale * + * ResetIFrameStats * + * ShowIFrameSummary * + * EstimateSecondsPerIFrame * + * EncodeYDC * + * EncodeCDC * + * time_elapsed * + * * + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/*==============* + * HEADER FILES * + *==============*/ + + +#ifdef CLOCKS_PER_SEC +#include <times.h> +#else +#include <sys/times.h> +#endif + +#include <sys/types.h> +#include <sys/param.h> +#include "all.h" +#include "mtypes.h" +#include "frames.h" +#include "prototypes.h" +#include "block.h" +#include "mpeg.h" +#include "param.h" +#include "mheaders.h" +#include "fsize.h" +#include "parallel.h" +#include "postdct.h" +#include "rate.h" +#include "specifics.h" +#include "opts.h" + +/*==================* + * STATIC VARIABLES * + *==================*/ + +static int lastNumBits = 0; +static int lastIFrame = 0; +static int numBlocks = 0; +static int numBits; +static int numFrames = 0; +static int numFrameBits = 0; +static int32 totalTime = 0; +static float totalSNR = 0.0; +static float totalPSNR = 0.0; + +static int lengths[256] = { + 0, 1, 2, 2, 3, 3, 3, 3, /* 0 - 7 */ + 4, 4, 4, 4, 4, 4, 4, 4, /* 8 - 15 */ + 5, 5, 5, 5, 5, 5, 5, 5, /* 16 - 31 */ + 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, /* 32 - 63 */ + 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, /* 64 - 127 */ + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8 +}; + + +/*==================* + * GLOBAL VARIABLES * + *==================*/ + +int qscaleI; +int slicesPerFrame; +int blocksPerSlice; +int fCodeI, fCodeP, fCodeB; +boolean printSNR = FALSE; +boolean printMSE = FALSE; +boolean decodeRefFrames = FALSE; +Block **dct=NULL, **dctr=NULL, **dctb=NULL; +dct_data_type **dct_data; /* used in p/bframe.c */ +int TIME_RATE; + + +/*=====================* + * EXPORTED PROCEDURES * + *=====================*/ +extern void PrintItoIBitRate _ANSI_ARGS_((int numBits, int frameNum)); + +/*===============================* + * INTERNAL PROCEDURE prototypes * + *===============================*/ +void AllocDctBlocks(void ); +int SetFCodeHelper (int sr); +void CalcDistortion (MpegFrame *current, int y, int x); + +int +SetFCodeHelper(int const SR) { + + int range,fCode; + + if ( pixelFullSearch ) { + range = SR; + } else { + range = SR*2; + } + + if ( range < 256 ) { + if ( range < 64 ) { + if ( range < 32 ) { + fCode = 1; + } else { + fCode = 2; + } + } else { + if ( range < 128 ) { + fCode = 3; + } else { + fCode = 4; + } + } + } else { + if ( range < 1024 ) { + if ( range < 512 ) { + fCode = 5; + } else { + fCode = 6; + } + } else { + if ( range < 2048 ) { + fCode = 7; + } else { + fprintf(stderr, "ERROR: INVALID SEARCH RANGE!!!\n"); + exit(1); + } + } + } + return fCode; +} + + + +/*===========================================================================* + * + * SetFCode + * + * set the forward_f_code and backward_f_code according to the search + * range. Must be called AFTER pixelFullSearch and searchRange have + * been initialized. Irrelevant for I-frames, but computation is + * negligible (done only once, as well) + * + * RETURNS: nothing + * + * SIDE EFFECTS: fCodeI,fCodeP,fCodeB + * + *===========================================================================*/ +void +SetFCode(void) { + fCodeI = SetFCodeHelper(1); /* GenIFrame ignores value */ + fCodeP = SetFCodeHelper(searchRangeP); + fCodeB = SetFCodeHelper(searchRangeB); +} + + + +/*===========================================================================* + * + * SetSlicesPerFrame + * + * set the number of slices per frame + * + * RETURNS: nothing + * + * SIDE EFFECTS: slicesPerFrame + * + *===========================================================================*/ +void +SetSlicesPerFrame(int const number) { + + slicesPerFrame = number; +} + + + +/*===========================================================================* + * + * SetBlocksPerSlice + * + * set the number of blocks per slice, based on slicesPerFrame + * + * RETURNS: nothing + * + * SIDE EFFECTS: blocksPerSlice + * + *===========================================================================*/ +void +SetBlocksPerSlice(void) { + + int totalBlocks; + + totalBlocks = (Fsize_y>>4)*(Fsize_x>>4); + + if ( slicesPerFrame > totalBlocks ) { + blocksPerSlice = 1; + } else { + blocksPerSlice = totalBlocks/slicesPerFrame; + } +} + + + +/*===========================================================================* + * + * SetIQScale + * + * set the I-frame Q-scale + * + * RETURNS: nothing + * + * SIDE EFFECTS: qscaleI + * + *===========================================================================*/ +void +SetIQScale(int const qI) { + qscaleI = qI; +} + + + +/*===========================================================================* + * + * GetIQScale + * + * Get the I-frame Q-scale + * + * RETURNS: the Iframe Qscale + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +int +GetIQScale(void) { + return qscaleI; +} + + + +/*===========================================================================* + * + * GenIFrame + * + * generate an I-frame; appends result to bb + * + * RETURNS: I-frame appended to bb + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void +GenIFrame(BitBucket * const bb, + MpegFrame * const current) { + + int x, y; + int index; + FlatBlock fb[6]; + Block dec[6]; + int32 y_dc_pred, cr_dc_pred, cb_dc_pred; + int totalBits; + int totalFrameBits; + int32 startTime, endTime; + float snr[3], psnr[3]; + int mbAddress; + int QScale; + BlockMV *info; /* Not used in Iframes, but nice to pass in anyway */ + int bitstreamMode, newQScale; + int rc_blockStart=0; + + if (dct==NULL) AllocDctBlocks(); + if (collect_quant) {fprintf(collect_quant_fp, "# I\n");} + + /* set-up for statistics */ + numFrames++; + totalFrameBits = bb->cumulativeBits; + if ( showBitRatePerFrame ) { + if ( lastNumBits == 0 ) { + lastNumBits = bb->cumulativeBits; + lastIFrame = current->id; + } else { + /* ASSUMES 30 FRAMES PER SECOND */ + + if (! realQuiet) { + fprintf(stdout, "I-to-I (frames %5d to %5d) bitrate: %8d\n", + lastIFrame, current->id-1, + ((bb->cumulativeBits-lastNumBits)*30)/ + (current->id-lastIFrame)); + } + + fprintf(bitRateFile, "I-to-I (frames %5d to %5d) bitrate: %8d\n", + lastIFrame, current->id-1, + ((bb->cumulativeBits-lastNumBits)*30)/ + (current->id-lastIFrame)); + lastNumBits = bb->cumulativeBits; + lastIFrame = current->id; + } + } + + startTime = time_elapsed(); + + Frame_AllocBlocks(current); + BlockifyFrame(current); + + DBG_PRINT(("Generating iframe\n")); + QScale = GetIQScale(); + /* Allocate bits for this frame for rate control purposes */ + bitstreamMode = getRateMode(); + if (bitstreamMode == FIXED_RATE) { + targetRateControl(current); + } + + Mhead_GenPictureHeader(bb, I_FRAME, current->id, fCodeI); + /* Check for Qscale change */ + if (specificsOn) { + newQScale = SpecLookup(current->id, 0, 0 /* junk */, &info, QScale); + if (newQScale != -1) { + QScale = newQScale; + } + /* check for slice */ + newQScale = SpecLookup(current->id, 1, 1, &info, QScale); + if (newQScale != -1) { + QScale = newQScale; + } + } + Mhead_GenSliceHeader(bb, 1, QScale, NULL, 0); + + if ( referenceFrame == DECODED_FRAME ) { + Frame_AllocDecoded(current, TRUE); + } else if ( printSNR ) { + Frame_AllocDecoded(current, FALSE); + } + + y_dc_pred = cr_dc_pred = cb_dc_pred = 128; + totalBits = bb->cumulativeBits; + mbAddress = 0; + + /* DCT the macroblocks */ + for (y = 0; y < (Fsize_y >> 3); y += 2) { + for (x = 0; x < (Fsize_x >> 3); x += 2) { + if (collect_quant && (collect_quant_detailed & 1)) + fprintf(collect_quant_fp, "l\n"); + if (DoLaplace) {LaplaceCnum = 0;} + mp_fwd_dct_block2(current->y_blocks[y][x], dct[y][x]); + mp_fwd_dct_block2(current->y_blocks[y][x+1], dct[y][x+1]); + mp_fwd_dct_block2(current->y_blocks[y+1][x], dct[y+1][x]); + mp_fwd_dct_block2(current->y_blocks[y+1][x+1], dct[y+1][x+1]); + if (collect_quant && (collect_quant_detailed & 1)) + fprintf(collect_quant_fp, "c\n"); + if (DoLaplace) {LaplaceCnum = 1;} + mp_fwd_dct_block2(current->cb_blocks[y>>1][x>>1], + dctb[y>>1][x>>1]); + if (DoLaplace) {LaplaceCnum = 2;} + mp_fwd_dct_block2(current->cr_blocks[y>>1][x>>1], + dctr[y>>1][x>>1]); + } + } + + if (DoLaplace) + CalcLambdas(); + + for (y = 0; y < (Fsize_y >> 3); y += 2) { + for (x = 0; x < (Fsize_x >> 3); x += 2) { + /* Check for Qscale change */ + if (specificsOn) { + newQScale = + SpecLookup(current->id, 2, mbAddress, &info, QScale); + if (newQScale != -1) { + QScale = newQScale; + } + } + + /* Determine if new Qscale needed for Rate Control purposes */ + if (bitstreamMode == FIXED_RATE) { + rc_blockStart = bb->cumulativeBits; + newQScale = needQScaleChange(qscaleI, + current->y_blocks[y][x], + current->y_blocks[y][x+1], + current->y_blocks[y+1][x], + current->y_blocks[y+1][x+1]); + if (newQScale > 0) { + QScale = newQScale; + } + } + + if ( (mbAddress % blocksPerSlice == 0) && (mbAddress != 0) ) { + /* create a new slice */ + if (specificsOn) { + /* Make sure no slice Qscale change */ + newQScale = SpecLookup(current->id, 1, + mbAddress/blocksPerSlice, &info, + QScale); + if (newQScale != -1) QScale = newQScale; + } + Mhead_GenSliceEnder(bb); + Mhead_GenSliceHeader(bb, 1+(y>>1), QScale, NULL, 0); + y_dc_pred = cr_dc_pred = cb_dc_pred = 128; + + GEN_I_BLOCK(I_FRAME, current, bb, 1+(x>>1), QScale); + } else { + GEN_I_BLOCK(I_FRAME, current, bb, 1, QScale); + } + + if (WriteDistortionNumbers) { + CalcDistortion(current, y, x); + } + + if ( decodeRefFrames ) { + /* now, reverse the DCT transform */ + LaplaceCnum = 0; + for ( index = 0; index < 6; index++ ) { + if (!DoLaplace) { + Mpost_UnQuantZigBlock(fb[index], dec[index], QScale, + TRUE); + } else { + if (index == 4) {LaplaceCnum = 1;} + if (index == 5) {LaplaceCnum = 2;} + Mpost_UnQuantZigBlockLaplace(fb[index], dec[index], + QScale, TRUE); + } + mpeg_jrevdct((int16 *)dec[index]); + } + + /* now, unblockify */ + BlockToData(current->decoded_y, dec[0], y, x); + BlockToData(current->decoded_y, dec[1], y, x+1); + BlockToData(current->decoded_y, dec[2], y+1, x); + BlockToData(current->decoded_y, dec[3], y+1, x+1); + BlockToData(current->decoded_cb, dec[4], y>>1, x>>1); + BlockToData(current->decoded_cr, dec[5], y>>1, x>>1); + } + + numBlocks++; + mbAddress++; + /* Rate Control */ + if (bitstreamMode == FIXED_RATE) { + incMacroBlockBits(bb->cumulativeBits - rc_blockStart); + rc_blockStart = bb->cumulativeBits; + MB_RateOut(TYPE_IFRAME); + } + } + } + + if ( printSNR ) { + BlockComputeSNR(current,snr,psnr); + totalSNR += snr[0]; + totalPSNR += psnr[0]; + } + + numBits += (bb->cumulativeBits-totalBits); + + DBG_PRINT(("End of frame\n")); + + Mhead_GenSliceEnder(bb); + /* Rate Control */ + if (bitstreamMode == FIXED_RATE) { + updateRateControl(TYPE_IFRAME); + } + + endTime = time_elapsed(); + totalTime += (endTime-startTime); + + numFrameBits += (bb->cumulativeBits-totalFrameBits); + + if ( showBitRatePerFrame ) { + /* ASSUMES 30 FRAMES PER SECOND */ + fprintf(bitRateFile, "%5d\t%8d\n", current->id, + 30*(bb->cumulativeBits-totalFrameBits)); + } + + if ( frameSummary && !realQuiet ) { + + /* ASSUMES 30 FRAMES PER SECOND */ + fprintf(stdout, + "FRAME %d (I): %ld seconds (%d bits/s output)\n", + current->id, (long)((endTime-startTime)/TIME_RATE), + 30*(bb->cumulativeBits-totalFrameBits)); + if ( printSNR ) { + fprintf(stdout, + "FRAME %d: SNR: %.1f\t%.1f\t%.1f\t" + "PSNR: %.1f\t%.1f\t%.1f\n", + current->id, snr[0], snr[1], snr[2], + psnr[0], psnr[1], psnr[2]); + } + } +} + + + +/*===========================================================================* + * + * ResetIFrameStats + * + * reset the I-frame statistics + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void +ResetIFrameStats(void) { + numBlocks = 0; + numBits = 0; + numFrames = 0; + numFrameBits = 0; + totalTime = 0; +} + + + +float +IFrameTotalTime(void) { + return (float)totalTime/(float)TIME_RATE; +} + + + +void +ShowIFrameSummary(unsigned int const inputFrameBits, + unsigned int const totalBits, + FILE * const fpointer) { +/*---------------------------------------------------------------------------- + Print out statistics on all I frames. +-----------------------------------------------------------------------------*/ + if (numFrames > 0) { + fprintf(fpointer, "-------------------------\n"); + fprintf(fpointer, "*****I FRAME SUMMARY*****\n"); + fprintf(fpointer, "-------------------------\n"); + + fprintf(fpointer, " Blocks: %5d (%6d bits) (%5d bpb)\n", + numBlocks, numBits, numBits/numBlocks); + fprintf(fpointer, " Frames: %5d (%6d bits) (%5d bpf)" + "(%2.1f%% of total)\n", + numFrames, numFrameBits, numFrameBits/numFrames, + 100.0*(float)numFrameBits/(float)totalBits); + fprintf(fpointer, " Compression: %3d:1 (%9.4f bpp)\n", + numFrames*inputFrameBits/numFrameBits, + 24.0*(float)numFrameBits/(float)(numFrames*inputFrameBits)); + if ( printSNR ) + fprintf(fpointer, " Avg Y SNR/PSNR: %.1f %.1f\n", + totalSNR/(float)numFrames, totalPSNR/(float)numFrames); + if ( totalTime == 0 ) { + fprintf(fpointer, " Seconds: NONE\n"); + } else { + fprintf(fpointer, " Seconds: %9ld (%9.4f fps) (%9ld pps) " + "(%9ld mps)\n", + (long)(totalTime/TIME_RATE), + (float)((float)(TIME_RATE*numFrames)/(float)totalTime), + (long)((float)TIME_RATE * (float)numFrames * + (float)inputFrameBits/(24.0*(float)totalTime)), + (long)((float)TIME_RATE*(float)numFrames * + (float)inputFrameBits/(256.0*24.0 * + (float)totalTime))); + } + } +} + + + +/*===========================================================================* + * + * EstimateSecondsPerIFrame + * + * estimates the number of seconds required per I-frame + * + * RETURNS: seconds (floating point value) + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +float +EstimateSecondsPerIFrame() +{ + return (float)totalTime/((float)TIME_RATE*(float)numFrames); +} + + +/*===========================================================================* + * + * EncodeYDC + * + * Encode the DC portion of a DCT of a luminance block + * + * RETURNS: result appended to bb + * + * SIDE EFFECTS: updates pred_term + * + *===========================================================================*/ +void +EncodeYDC(int32 const dc_term, + int32 * const pred_term, + BitBucket * const bb) { + + /* see Table B.5a -- MPEG-I doc */ + static int codes[9] = { + 0x4, 0x0, 0x1, 0x5, 0x6, 0xe, 0x1e, 0x3e, 0x7e + }; + static int codeLengths[9] = { + 3, 2, 2, 3, 3, 4, 5, 6, 7 + }; + int ydiff, ydiff_abs; + int length; + + ydiff = (dc_term - (*pred_term)); + if (ydiff > 255) { +#ifdef BLEAH + fprintf(stdout, "TRUNCATED\n"); +#endif + ydiff = 255; + } else if (ydiff < -255) { +#ifdef BLEAH + fprintf(stdout, "TRUNCATED\n"); +#endif + ydiff = -255; + } + + ydiff_abs = ABS(ydiff); + length = lengths[ydiff_abs]; + Bitio_Write(bb, codes[length], codeLengths[length]); + if ( length != 0 ) { + if ( ydiff > 0 ) { + Bitio_Write(bb, ydiff_abs, length); + } else { + Bitio_Write(bb, ~ydiff_abs, length); + } + } + + (*pred_term) += ydiff; +} + + + +/*===========================================================================* + * + * EncodeCDC + * + * Encode the DC portion of a DCT of a chrominance block + * + * RETURNS: result appended to bb + * + * SIDE EFFECTS: updates pred_term + * + *===========================================================================*/ +void +EncodeCDC(int32 const dc_term, + int32 * const pred_term, + BitBucket * const bb) { + + /* see Table B.5b -- MPEG-I doc */ + static int codes[9] = { + 0x0, 0x1, 0x2, 0x6, 0xe, 0x1e, 0x3e, 0x7e, 0xfe + }; + static int codeLengths[9] = { + 2, 2, 2, 3, 4, 5, 6, 7, 8 + }; + int cdiff, cdiff_abs; + int length; + + cdiff = (dc_term - (*pred_term)); + if (cdiff > 255) { +#ifdef BLEAH + fprintf(stdout, "TRUNCATED\n"); +#endif + cdiff = 255; + } else if (cdiff < -255) { +#ifdef BLEAH + fprintf(stdout, "TRUNCATED\n"); +#endif + cdiff = -255; + } + + cdiff_abs = ABS(cdiff); + length = lengths[cdiff_abs]; + Bitio_Write(bb, codes[length], codeLengths[length]); + if ( length != 0 ) { + if ( cdiff > 0 ) { + Bitio_Write(bb, cdiff_abs, length); + } else { + Bitio_Write(bb, ~cdiff_abs, length); + } + } + + (*pred_term) += cdiff; +} + + + +void +BlockComputeSNR(MpegFrame * const current, + float * const snr, + float * const psnr) { + + int32 tempInt; + int y, x; + int32 varDiff[3]; + double ratio[3]; + double total[3]; + uint8 **origY=current->orig_y, **origCr=current->orig_cr, + **origCb=current->orig_cb; + uint8 **newY=current->decoded_y, **newCr=current->decoded_cr, + **newCb=current->decoded_cb; + static int32 **SignalY, **NoiseY; + static int32 **SignalCb, **NoiseCb; + static int32 **SignalCr, **NoiseCr; + static short ySize[3], xSize[3]; + static boolean needs_init=TRUE; + + /* Init */ + if (needs_init) { + int ysz = (Fsize_y>>3) * sizeof(int32 *); + int xsz = (Fsize_x>>3); + + needs_init = FALSE; + for (y=0; y<3; y++) { + varDiff[y] = ratio[y] = total[y] = 0.0; + } + ySize[0]=Fsize_y; xSize[0]=Fsize_x; + ySize[1]=Fsize_y>>1; xSize[1]=Fsize_x>>1; + ySize[2]=Fsize_y>>1; xSize[2]=Fsize_x>>1; + SignalY = (int32 **) malloc(ysz); + NoiseY = (int32 **) malloc(ysz); + SignalCb = (int32 **) malloc(ysz); + NoiseCb = (int32 **) malloc(ysz); + SignalCr = (int32 **) malloc(ysz); + NoiseCr = (int32 **) malloc(ysz); + if (SignalY == NULL || NoiseY == NULL || SignalCr == NULL || + NoiseCb == NULL || SignalCb == NULL || NoiseCr == NULL) { + fprintf(stderr, "Out of memory in BlockComputeSNR\n"); + exit(-1); + } + for (y = 0; y < ySize[0]>>3; y++) { + SignalY[y] = (int32 *) calloc(xsz,4); + SignalCr[y] = (int32 *) calloc(xsz,4); + SignalCb[y] = (int32 *) calloc(xsz,4); + NoiseY[y] = (int32 *) calloc(xsz,4); + NoiseCr[y] = (int32 *) calloc(xsz,4); + NoiseCb[y] = (int32 *) calloc(xsz,4); + } + } else { + for (y = 0; y < ySize[0]>>3; y++) { + memset((char *) &NoiseY[y][0], 0, (xSize[0]>>3) * 4); + memset((char *) &SignalY[y][0], 0, (xSize[0]>>3) * 4); + memset((char *) &NoiseCb[y][0], 0, (xSize[0]>>3) * 4); + memset((char *) &NoiseCr[y][0], 0, (xSize[0]>>3) * 4); + memset((char *) &SignalCb[y][0], 0, (xSize[0]>>3) * 4); + memset((char *) &SignalCr[y][0], 0, (xSize[0]>>3) * 4); + } + } + + /* find all the signal and noise */ + for (y = 0; y < ySize[0]; y++) { + for (x = 0; x < xSize[0]; x++) { + tempInt = (origY[y][x] - newY[y][x]); + NoiseY[y>>4][x>>4] += tempInt*tempInt; + total[0] += (double)abs(tempInt); + tempInt = origY[y][x]; + SignalY[y>>4][x>>4] += tempInt*tempInt; + }} + for (y = 0; y < ySize[1]; y++) { + for (x = 0; x < xSize[1]; x ++) { + tempInt = (origCb[y][x] - newCb[y][x]); + NoiseCb[y>>3][x>>3] += tempInt*tempInt; + total[1] += (double)abs(tempInt); + tempInt = origCb[y][x]; + SignalCb[y>>3][x>>3] += tempInt*tempInt; + tempInt = (origCr[y][x]-newCr[y][x]); + NoiseCr[y>>3][x>>3] += tempInt*tempInt; + total[2] += (double)abs(tempInt); + tempInt = origCr[y][x]; + SignalCr[y>>3][x>>3] += tempInt*tempInt; + }} + + /* Now sum up that noise */ + for(y=0; y<Fsize_y>>4; y++){ + for(x=0; x<Fsize_x>>4; x++){ + varDiff[0] += NoiseY[y][x]; + varDiff[1] += NoiseCb[y][x]; + varDiff[2] += NoiseCr[y][x]; + if (printMSE) printf("%4d ",(int)(NoiseY[y][x]/256.0)); + } + if (printMSE) puts(""); + } + + /* Now look at those ratios! */ + for(y=0; y<Fsize_y>>4; y++){ + for(x=0; x<Fsize_x>>4; x++){ + ratio[0] += (double)SignalY[y][x]/(double)varDiff[0]; + ratio[1] += (double)SignalCb[y][x]/(double)varDiff[1]; + ratio[2] += (double)SignalCr[y][x]/(double)varDiff[2]; + }} + + for (x=0; x<3; x++) { + snr[x] = 10.0 * log10(ratio[x]); + psnr[x] = 20.0 * log10(255.0/sqrt((double)varDiff[x]/ + (double)(ySize[x]*xSize[x]))); + + if (! realQuiet) { + fprintf(stdout, "Mean error[%1d]: %f\n", + x, total[x] / (double)(xSize[x] * ySize[x])); + } + + } +} + + + +void +WriteDecodedFrame(MpegFrame * const frame) { + + FILE * fpointer; + char fileName[256]; + int width, height; + int y; + + /* need to save decoded frame to disk because it might be accessed + by another process */ + + width = Fsize_x; + height = Fsize_y; + + sprintf(fileName, "%s.decoded.%d", outputFileName, frame->id); + + if (!realQuiet) { + fprintf(stdout, "Outputting to %s\n", fileName); + fflush(stdout); + } + + fpointer = fopen(fileName, "wb"); + + for ( y = 0; y < height; y++ ) { + fwrite(frame->decoded_y[y], 1, width, fpointer); + } + + for (y = 0; y < (height >> 1); y++) { /* U */ + fwrite(frame->decoded_cb[y], 1, width >> 1, fpointer); + } + + for (y = 0; y < (height >> 1); y++) { /* V */ + fwrite(frame->decoded_cr[y], 1, width >> 1, fpointer); + } + fflush(fpointer); + fclose(fpointer); +} + + + +void +PrintItoIBitRate(int const numBits, + int const frameNum) { + + if ( showBitRatePerFrame ) { + /* ASSUMES 30 FRAMES PER SECOND */ + + if (! realQuiet) { + fprintf(stdout, "I-to-I (frames %5d to %5d) bitrate: %8d\n", + lastIFrame, frameNum-1, + ((numBits-lastNumBits)*30)/ + (frameNum-lastIFrame)); + } + + fprintf(bitRateFile, "I-to-I (frames %5d to %5d) bitrate: %8d\n", + lastIFrame, frameNum-1, + ((numBits-lastNumBits)*30)/ + (frameNum-lastIFrame)); + } +} + + + +/*===========================================================================* + * + * AllocDctBlocks + * + * allocate memory for dct blocks + * + * RETURNS: nothing + * + * SIDE EFFECTS: creates dct, dctr, dctb + * + *===========================================================================*/ +void +AllocDctBlocks(void) { + int dctx, dcty; + int i; + + dctx = Fsize_x / DCTSIZE; + dcty = Fsize_y / DCTSIZE; + + dct = (Block **) malloc(sizeof(Block *) * dcty); + ERRCHK(dct, "malloc"); + for (i = 0; i < dcty; i++) { + dct[i] = (Block *) malloc(sizeof(Block) * dctx); + ERRCHK(dct[i], "malloc"); + } + + dct_data = (dct_data_type **) malloc(sizeof(dct_data_type *) * dcty); + ERRCHK(dct_data, "malloc"); + for (i = 0; i < dcty; i++) { + dct_data[i] = (dct_data_type *) malloc(sizeof(dct_data_type) * dctx); + ERRCHK(dct[i], "malloc"); + } + + dctr = (Block **) malloc(sizeof(Block *) * (dcty >> 1)); + dctb = (Block **) malloc(sizeof(Block *) * (dcty >> 1)); + ERRCHK(dctr, "malloc"); + ERRCHK(dctb, "malloc"); + for (i = 0; i < (dcty >> 1); i++) { + dctr[i] = (Block *) malloc(sizeof(Block) * (dctx >> 1)); + dctb[i] = (Block *) malloc(sizeof(Block) * (dctx >> 1)); + ERRCHK(dctr[i], "malloc"); + ERRCHK(dctb[i], "malloc"); + } +} + + + +/*======================================================================* + * + * time_elapsed + * + * Handle different time systems on different machines + * + * RETURNS number of seconds process time used + * + *======================================================================*/ +int32 time_elapsed(void) { +#ifdef CLOCKS_PER_SEC + /* ANSI C */ + TIME_RATE = CLOCKS_PER_SEC; + return (int32) clock(); +#else + struct tms timeBuffer; + TIME_RATE = 60; + times(&timeBuffer); + return timeBuffer.tms_utime + timeBuffer.tms_stime; +#endif +} + + + +void +CalcDistortion(MpegFrame * const current, + int const y, + int const x) { + + int qscale, distort=0; + Block decblk; + FlatBlock fblk; + int datarate = 0; + + for (qscale = 1; qscale < 32; qscale ++) { + distort = 0; + datarate = 0; + Mpost_QuantZigBlock(dct[y][x], fblk, qscale, TRUE); + Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE); + if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk); + mpeg_jrevdct((int16 *)decblk); + distort += mse(current->y_blocks[y][x], decblk); + + Mpost_QuantZigBlock(dct[y][x+1], fblk, qscale, TRUE); + Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE); + if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk); + mpeg_jrevdct((int16 *)decblk); + distort += mse(current->y_blocks[y][x+1], decblk); + + Mpost_QuantZigBlock(dct[y+1][x], fblk, qscale, TRUE); + Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE); + if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk); + mpeg_jrevdct((int16 *)decblk); + distort += mse(current->y_blocks[y+1][x], decblk); + + Mpost_QuantZigBlock(dct[y+1][x+1], fblk, qscale, TRUE); + Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE); + if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk); + mpeg_jrevdct((int16 *)decblk); + distort += mse(current->y_blocks[y+1][x+1], decblk); + + Mpost_QuantZigBlock(dctb[y >> 1][x >> 1], fblk, qscale, TRUE); + Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE); + if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk); + mpeg_jrevdct((int16 *)decblk); + distort += mse(current->cb_blocks[y>>1][x>>1], decblk); + + Mpost_QuantZigBlock(dctr[y >> 1][x >> 1], fblk, qscale, TRUE); + Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE); + if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk); + mpeg_jrevdct((int16 *)decblk); + distort += mse(current->cr_blocks[y >> 1][x >> 1], decblk); + + if (!collect_distortion_detailed) { + fprintf(distortion_fp, "\t%d\n", distort); + } else if (collect_distortion_detailed == 1) { + fprintf(distortion_fp, "\t%d\t%d\n", distort, datarate); + } else { + fprintf(fp_table_rate[qscale-1], "%d\n", datarate); + fprintf(fp_table_dist[qscale-1], "%d\n", distort); + } + } +} + + + + diff --git a/converter/ppm/ppmtompeg/input.c b/converter/ppm/ppmtompeg/input.c new file mode 100644 index 00000000..2b87afa7 --- /dev/null +++ b/converter/ppm/ppmtompeg/input.c @@ -0,0 +1,515 @@ +/*===========================================================================* + * input.c + * + * Stuff for getting the raw frame input for the program + * + *===========================================================================*/ + +/* COPYRIGHT INFORMATION IS AT THE END OF THIS FILE */ + +#define _XOPEN_SOURCE 1 + /* This makes sure popen() is in stdio.h. In GNU libc 2.1.3, + _POSIX_C_SOURCE = 2 is sufficient, but on AIX 4.3, the higher level + _XOPEN_SOURCE is required. 2000.09.09 + */ + + +/*==============* + * HEADER FILES * + *==============*/ + +#include <assert.h> +#include <string.h> +#include <stdio.h> +#include <limits.h> + +#include "nstring.h" +#include "mallocvar.h" +#include "parallel.h" +#include "readframe.h" +#include "param.h" +#include "jpeg.h" +#include "input.h" + +extern boolean realQuiet; /* TRUE = no messages to stdout */ + +struct InputFileEntry { + char left[256]; + char right[256]; + bool glob; /* if FALSE, left is complete name */ + int startID; + int endID; + int skip; + int numPadding; /* -1 if there is none */ + int numFiles; + bool repeat; +}; + + +struct inputSource inputSource; + + + +void +GetNthInputFileName(struct inputSource * const inputSourceP, + unsigned int const n, + const char ** const fileNameP) { +/*---------------------------------------------------------------------------- + Return the file name of the Nth input file. +-----------------------------------------------------------------------------*/ + static int lastN = 0, lastMapN = 0, lastSoFar = 0; + int mapN; + int index; + int soFar; + int numPadding; + struct InputFileEntry * mapNEntryP; + + assert(!inputSourceP->stdinUsed); + assert(n < inputSourceP->numInputFiles); + + if (n >= lastN) { + soFar = lastSoFar; + index = lastMapN; + } else { + soFar = 0; + index = 0; + } + + assert(index < inputSourceP->numInputFileEntries); + + while (soFar + inputSourceP->inputFileEntries[index]->numFiles <= n) { + soFar += inputSourceP->inputFileEntries[index]->numFiles; + ++index; + assert(index < inputSourceP->numInputFileEntries); + } + + mapN = index; + + mapNEntryP = inputSourceP->inputFileEntries[mapN]; + + index = mapNEntryP->startID + mapNEntryP->skip*(n - soFar); + + numPadding = mapNEntryP->numPadding; + + if (numPadding != -1) { + char numBuffer[33]; + int loop; + + sprintf(numBuffer, "%32d", index); + for (loop = 32-numPadding; loop < 32; ++loop) { + if (numBuffer[loop] != ' ') + break; + else + numBuffer[loop] = '0'; + } + + if (mapNEntryP->repeat != TRUE) + asprintfN(fileNameP, "%s%s%s", + mapNEntryP->left, &numBuffer[32-numPadding], + mapNEntryP->right); + else + asprintfN(fileNameP, "%s", mapNEntryP->left); + } else { + if (mapNEntryP->repeat != TRUE) + asprintfN(fileNameP, "%s%d%s", + mapNEntryP->left, index, mapNEntryP->right); + else + asprintfN(fileNameP, "%s", mapNEntryP->left); + } + + lastN = n; + lastMapN = mapN; + lastSoFar = soFar; +} + + + +void +ReadNthFrame(struct inputSource * const inputSourceP, + unsigned int const frameNumber, + boolean const remoteIO, + boolean const childProcess, + boolean const separateConversion, + const char * const slaveConversion, + const char * const inputConversion, + MpegFrame * const frameP, + bool * const endOfStreamP) { + + if (remoteIO) + /* Get the frame from the remote I/O server */ + GetRemoteFrame(frameP, frameNumber); + else { + /* Get the frame out of the file in which it lives */ + + const char * conversion; + + if (childProcess && separateConversion) + conversion = slaveConversion; + else + conversion = inputConversion; + + ReadFrame(frameP, inputSourceP, frameNumber, conversion, endOfStreamP); + } +} + + + +/* Jim Boucher's code */ + +void +JM2JPEG(struct inputSource * const inputSourceP) { + char full_path[1024]; + char inter_file[1024]; + int ci; + + for(ci = 0; ci < inputSourceP->numInputFileEntries; ci++) { + inter_file[0] = '\0'; + full_path[0] = '\0'; + strcpy(full_path, currentPath); + + if (!inputSource.stdinUsed) { + strcat(full_path, "/"); + strcat(full_path, inputSourceP->inputFileEntries[ci]->left); + strcpy(inter_file,full_path); + + if (!realQuiet) + fprintf(stdout, "Extracting JPEG's in the JMOVIE from %s\n", + full_path); + + JMovie2JPEG(full_path, + inter_file, + inputSourceP->inputFileEntries[ci]->startID, + inputSourceP->inputFileEntries[ci]->endID); + } else + pm_error("ERROR: Cannot use JMovie format on Standard Input"); + } +} + + + +static const char * +SkipSpacesTabs(const char * const start) { + + const char * p; + + for (p = start; *p == ' ' || *p == '\t'; ++p); + + return p; +} + + + +static void +processGlob(const char * const input, + struct InputFileEntry * const inputFileEntryP) { + + const char *globPtr; + char * charPtr; + char left[256], right[256]; + char leftNumText[256], rightNumText[256]; + char skipNumText[256]; + int leftNum, rightNum; + int skipNum; + boolean padding; + int numPadding = 0; + + inputFileEntryP->glob = TRUE; + inputFileEntryP->repeat = FALSE; + + /* star expand */ + + globPtr = input; + charPtr = left; + /* copy left of '*' */ + while ( (*globPtr != '\0') && (*globPtr != '*') ) { + *charPtr = *globPtr; + charPtr++; + globPtr++; + } + *charPtr = '\0'; + + if (*globPtr == '\0') { + fprintf(stderr, + "WARNING: expanding non-star regular expression\n"); + inputFileEntryP->repeat = TRUE; + globPtr = input; + charPtr = left; + /* recopy left of whitespace */ + while ( (*globPtr != '\0') && (*globPtr != '*') && + (*globPtr != ' ') && (*globPtr != '\t')) { + *charPtr = *globPtr; + charPtr++; + globPtr++; + } + *charPtr = '\0'; + *right = '\0'; + } else { + + globPtr++; + charPtr = right; + /* copy right of '*' */ + while ( (*globPtr != '\0') && (*globPtr != ' ') && + (*globPtr != '\t') ) { + *charPtr = *globPtr; + charPtr++; + globPtr++; + } + *charPtr = '\0'; + } + + globPtr = SkipSpacesTabs(globPtr); + + if ( *globPtr != '[' ) { + fprintf(stderr, + "ERROR: " + "Invalid input file expansion expression (no '[')\n"); + exit(1); + } + + globPtr++; + charPtr = leftNumText; + /* copy left number */ + while ( ISDIGIT(*globPtr) ) { + *charPtr = *globPtr; + charPtr++; + globPtr++; + } + *charPtr = '\0'; + + if ( *globPtr != '-' ) { + fprintf(stderr, + "ERROR: " + "Invalid input file expansion expression (no '-')\n"); + exit(1); + } + + globPtr++; + charPtr = rightNumText; + /* copy right number */ + while ( ISDIGIT(*globPtr) ) { + *charPtr = *globPtr; + charPtr++; + globPtr++; + } + *charPtr = '\0'; + if ( atoi(rightNumText) < atoi(leftNumText) ) { + fprintf(stderr, + "ERROR: " + "Beginning of input range is higher than end.\n"); + exit(1); + } + + + if ( *globPtr != ']' ) { + if ( *globPtr != '+' ) { + fprintf(stderr, + "ERROR: " + "Invalid input file expansion expression " + "(no ']')\n"); + exit(1); + } + + globPtr++; + charPtr = skipNumText; + /* copy skip number */ + while ( ISDIGIT(*globPtr) ) { + *charPtr = *globPtr; + charPtr++; + globPtr++; + } + *charPtr = '\0'; + + if ( *globPtr != ']' ) { + fprintf(stderr, + "ERROR: Invalid input file expansion expression " + "(no ']')\n"); + exit(1); + } + + skipNum = atoi(skipNumText); + } else { + skipNum = 1; + } + + leftNum = atoi(leftNumText); + rightNum = atoi(rightNumText); + + if ( (leftNumText[0] == '0') && (leftNumText[1] != '\0') ) { + padding = TRUE; + numPadding = strlen(leftNumText); + } else { + padding = FALSE; + } + + inputFileEntryP->startID = leftNum; + inputFileEntryP->endID = rightNum; + inputFileEntryP->skip = skipNum; + inputFileEntryP->numFiles = + (rightNum-leftNum+1)/skipNum; + strcpy(inputFileEntryP->left, left); + strcpy(inputFileEntryP->right, right); + if ( padding ) { + inputFileEntryP->numPadding = numPadding; + } else { + inputFileEntryP->numPadding = -1; + } +} + + + +static void processJmovie(const char * const input, + struct InputFileEntry * const inputFileEntryP) { + FILE *jmovie; + char full_path[1024]; + + inputFileEntryP->glob = TRUE; + full_path[0] = '\0'; + strcpy(full_path, currentPath); + + strcat(full_path, "/"); + strcat(full_path, input); + jmovie = fopen(input, "rb"); + + if (jmovie == NULL) { + perror (input); + exit (1); + } + + fseek (jmovie, (8*sizeof(char)), 0); + fseek (jmovie, (2*sizeof(int)), 1); + + if (fread(&(inputFileEntryP->numFiles), + sizeof(int), 1, jmovie) != 1) { + perror ("Error in reading number of frames in JMOVIE"); + exit(1); + } + fclose (jmovie); + + strcpy(inputFileEntryP->right,".jpg"); + inputFileEntryP->numPadding = -1; + inputFileEntryP->startID = 1; + inputFileEntryP->endID = (inputFileEntryP->numFiles-1); + inputFileEntryP->skip = 1; + if (! realQuiet) { + fprintf (stdout, + "Encoding all %d frames from JMOVIE.\n", + inputFileEntryP->endID); + } +} + + + +static void +processSimpleFileName(const char * const input, + struct InputFileEntry * const inputFileEntryP) { + + inputFileEntryP->glob = FALSE; + inputFileEntryP->numFiles = 1; + /* fixes a bug from version 1.3: */ + inputFileEntryP->numPadding = 0; + /* fixes a bug from version 1.4 */ + strcpy(inputFileEntryP->right,"\0"); + inputFileEntryP->startID = 0; + inputFileEntryP->endID = 0; + inputFileEntryP->skip = 0; +} + + +#define INPUT_ENTRY_BLOCK_SIZE 128 + +void +AddInputFiles(struct inputSource * const inputSourceP, + const char * const input) { + + unsigned int const currentIndex = inputSourceP->numInputFileEntries; + + struct InputFileEntry * currentEntryP; + + if (currentIndex >= inputSourceP->ifArraySize) { + /* Get more space */ + inputSourceP->ifArraySize += INPUT_ENTRY_BLOCK_SIZE; + REALLOCARRAY_NOFAIL(inputSourceP->inputFileEntries, + inputSourceP->ifArraySize); + } + MALLOCVAR_NOFAIL(inputSourceP->inputFileEntries[currentIndex]); + currentEntryP = inputSourceP->inputFileEntries[currentIndex]; + + if (input[strlen(input)-1] == ']') + processGlob(input, currentEntryP); + else { + strcpy(currentEntryP->left, input); + if (baseFormat == JMOVIE_FILE_TYPE) + processJmovie(input, currentEntryP); + else + processSimpleFileName(input, currentEntryP); + } + inputSourceP->numInputFiles += currentEntryP->numFiles; + ++inputSourceP->numInputFileEntries; +} + + + +void +SetStdinInput(struct inputSource * const inputSourceP) { + + assert(inputSourceP->numInputFileEntries == 0); + + inputSourceP->stdinUsed = TRUE; + inputSourceP->numInputFiles = INT_MAX; +} + + + +void +CreateInputSource(struct inputSource ** const inputSourcePP) { + + struct inputSource * inputSourceP; + + MALLOCVAR_NOFAIL(inputSourceP); + + inputSourceP->stdinUsed = FALSE; + inputSourceP->numInputFileEntries = 0; + inputSourceP->ifArraySize = 1; + inputSourceP->numInputFiles = 0; + MALLOCARRAY_NOFAIL(inputSourceP->inputFileEntries, 1); + + *inputSourcePP = inputSourceP; +} + + + +void +DestroyInputSource(struct inputSource * const inputSourceP) { + + unsigned int i; + + for (i = 0; i < inputSourceP->numInputFileEntries; ++i) + free(inputSourceP->inputFileEntries[i]); + + free(inputSourceP->inputFileEntries); + + free(inputSourceP); +} + + + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ diff --git a/converter/ppm/ppmtompeg/jpeg.c b/converter/ppm/ppmtompeg/jpeg.c new file mode 100644 index 00000000..3aad6364 --- /dev/null +++ b/converter/ppm/ppmtompeg/jpeg.c @@ -0,0 +1,634 @@ +/*===========================================================================* + * jpeg.c + * + * procedures to deal with JPEG files + * + * EXPORTED PROCEDURES: + * JMovie2JPEG + * ReadJPEG + * + *===========================================================================*/ + +/* COPYRIGHT INFORMATION IS AT THE END OF THIS FILE */ + +/*==============* + * HEADER FILES * + *==============*/ +#define _XOPEN_SOURCE /* Make sure stdio.h contains fileno() */ +#include <stdio.h> +#include "all.h" +/* With the lossless jpeg patch applied to the Jpeg library + (ftp://ftp.wizards.dupont.com/pub/ImageMagick/delegates/ljpeg-6b.tar.gz), + the name of min_DCT_scaled_size changes to min_codec_data_unit, + for some reason. With this macro, we change it back. +*/ +#define min_codec_data_unit min_DCT_scaled_size +#include <jpeglib.h> +#undef min_codec_data_unit +#include "mtypes.h" +#include "frames.h" +#include "prototypes.h" +#include "param.h" +#include "readframe.h" +#include "fsize.h" +#include "rgbtoycc.h" +#include "jpeg.h" + +#include "mallocvar.h" + +/* make it happier.... */ +#undef DCTSIZE2 + +/* jcopy_sample_rows() is an internal routine in the JPEG library, not + meant for use by us. We should figure out what the official interface + for this is and use it. The following is copied out of jpegint.h, which + is part of the JPEG library source code. + */ +extern void jcopy_sample_rows JPP((JSAMPARRAY input_array, int source_row, + JSAMPARRAY output_array, int dest_row, + int num_rows, JDIMENSION num_cols)); + + +#define HEADER_SIZE 607 /*JFIF header size used on output images*/ + + + +/*=======================================================================* + * * + * JMovie2JPEG * + * * + * Splits up a Parallax J_Movie into a set of JFIF image files * + * * + * RETURNS: nothing * + * * + * SIDE EFFECTS: none * + * * + * Contributed By James Boucher(jboucher@flash.bu.edu) * + * Boston University Multimedia Communications Lab * + * This code was adapted from the Berkeley Playone.c and Brian Smith's * + * JGetFrame code after being modified on 10-7-93 by Dinesh Venkatesh * + * of BU. * + * This code converts a version 2 Parallax J_Movie into a * + * set of JFIF compatible JPEG images. It works for all image * + * sizes and qualities. * + ************************************************************************/ +void +JMovie2JPEG(const char * const infilename, + /* input filename string */ + const char * const obase, + /* output filename base string=>obase##.jpg */ + int const start, + /* first frame to be extracted */ + int const end + /* last frame to be extracted */ + ) { + + FILE *inFile; /* Jmovie file pointer */ + FILE *outFile; /* JPEG file pointer for output file */ + int fd, i; /* input file descriptor and a counting variable*/ + char ofname[256]; /* output filename string */ + int Temp = 0, temp = 0; /* dummy variables */ + int image_offset = 0; /* counting variable */ + /* J_Movie header infomation */ + int ver_no; /* version number - expected to be 2 */ + int fps; /* frame rate - frames per second */ + int no_frames; /* total number of frames in jmovie */ + int bandwidth; /* bandwidth required for normal playback*/ + int qfactor; /* quality factor used to scale Q matrix */ + int mapsize; /* number of color map entries - 2^24 */ + int audio_tracks; /* number of audio tracks ==1 */ + int audiosize; /*number of bytes in audio tracks */ + int *inoffsets; /* input frame offsets from start of jmovie*/ + int width; /* image width */ + int height; /* image height */ + int size; /* total image size in bytes */ + char op_code; /* jmovie op_code */ + char jpeg_size[4]; /* jpeg data size */ + static char junk[1000]; /* data sink for audio data */ + int last; /* Number of last frame we will process */ + + /* The next array represents the default JFIF header for + quality = 100 and size = 320x240. The values are + adjusted as the J_Movie header is read. The default + size of this array is set large so as to make room + for the appending of the jpeg bitstream. It can be + made smaller if you have a better idea of its expected size + */ + static unsigned char inbuffer[300000] = { + 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, + 0x49, 0x46, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x11, + 0x08, 0x00, 0xF0, 0x01, 0x40, 0x03, 0x01, 0x21, + 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xFF, + 0xDB, 0x00, 0x84, 0x00, 0x10, 0x0B, 0x0C, 0x0E, + 0x0C, 0x0A, 0x10, 0x0E, 0x0D, 0x0E, 0x12, + 0x11, 0x10, 0x13, 0x18, 0x28, 0x1A, 0x18, 0x16, + 0x16, 0x18, 0x31, 0x23, 0x25, 0x1D, 0x28, 0x3A, + 0x33, 0x3D, 0x3C, 0x39, 0x33, 0x38, 0x37, 0x40, + 0x48, 0x5C, 0x4E, 0x40, 0x44, 0x57, 0x45, 0x37, + 0x38, 0x50, 0x6D, 0x51, 0x57, 0x5F, 0x62, 0x67, + 0x68, 0x67, 0x3E, 0x4D, 0x71, 0x79, 0x70, 0x64, + 0x78, 0x5C, 0x65, 0x67, 0x63, 0x01, 0x11, 0x12, + 0x12, 0x18, 0x15, 0x18, 0x2F, 0x1A, 0x1A, 0x2F, + 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0xFF, 0xC4, + 0x01, 0xA2, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, + 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, + 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, + 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, + 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, + 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, + 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, + 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, + 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, + 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, + 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, + 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, + 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, + 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, + 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, + 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, + 0xF7, 0xF8, 0xF9, 0xFA, 0x01, 0x00, 0x03, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0A, 0x0B, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, + 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, + 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, + 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, + 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, + 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, + 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, + 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, + 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, + 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, + 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, + 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, + 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, + 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, + 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, + 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, + 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, + 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, + 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, + 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, + 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, + 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xDA, + 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, + 0x11, 0x00, 0x3F, 0x00 + + }; + + if (start > end) { + fprintf(stderr,"bad frame numbers\n"); + exit(1); + } + + /* open J_Movie */ + inFile = fopen(infilename, "rb"); + if (inFile == NULL) { + perror (infilename); + exit(1); + } + + /* get file descriptor */ + fd = fileno(inFile); + + /* The following lines parse the jpeg_movie header and recover the */ + /* relavant information */ + + fseek(inFile, 8 * sizeof(char), 0); + + if (fread(&ver_no, sizeof(int), 1, inFile) != 1) { + perror("Error in reading version"); + exit(1); + } + if (ver_no != 2) { + perror("Unrecognized version - Quantization tables may be wrong\n"); + } + if (fread(&fps, sizeof(int), 1, inFile) != 1) { + perror("Error in reading fps"); + exit(1); + } + if (fread (&no_frames, sizeof(int), 1, inFile) != 1) { + perror("Error in reading no_frames"); + exit(1); + } + + MALLOCARRAY(inoffsets, no_frames); + + if (fread(&width, sizeof(int), 1, inFile) != 1) { + perror("Error in reading width"); + exit(1); + } + /* set image width in JFIF header */ + inbuffer[27] = (char)(0xFF & (width >> 8)); + inbuffer[28] = (char)(0xFF & width); + + if (fread(&height, sizeof(int), 1, inFile) != 1) { + perror("Error in reading height"); + exit(1); + } + /* set image height in JFIF header */ + inbuffer[25] = (char)(0xFF & (height >> 8)); + inbuffer[26] = (char)(0xFF & height); + + if (fread(&bandwidth, sizeof(int), 1, inFile) != 1) { + perror("Error in reading bandwidth"); + exit(1); + } + + if (fread(&qfactor, sizeof(int), 1, inFile) != 1) { + perror("Error in reading qfactor"); + exit(1); + } + /* The default quality factor = 100, therefore, if + our quality factor does not equal 100 we must + scale the quantization matrices in the JFIF header + */ + /* Note values are clipped to a max of 255 */ + if (qfactor != 100) { + for (Temp = 44; Temp < 108; ++Temp) { + temp= (inbuffer[Temp]*qfactor)/100; + inbuffer[Temp] = (char)((temp<255) ? temp : 255); + } + for (Temp = 109; Temp < 173; ++Temp) { + temp = (inbuffer[Temp]*qfactor)/100; + inbuffer[Temp] = (char)((temp<255) ? temp : 255); + } + } + + if (fread(&mapsize, sizeof(int), 1, inFile) != 1) { + perror("Error in reading mapsize"); + exit(1); + } + if (fread (&image_offset, sizeof(int), 1, inFile) != 1) { + perror("Error in reading image offset"); + exit(1); + } + if (fread (&audio_tracks, sizeof(int), 1, inFile) != 1) { + perror("Error in reading audio tracks"); + exit(1); + } + + fread(junk,sizeof(int),1,inFile); + + if (fread (&audiosize, sizeof(int), 1, inFile) != 1) { + perror("Error in reading audiosize"); + exit(1); + } + + fseek (inFile, image_offset, 0); + + last = MIN(end, no_frames-1); + + for (i = 0; i < no_frames; ++i) { + fread(&(inoffsets[i]), sizeof(int), 1, inFile); + } /* Reads in the frame sizes into the array */ + + rewind(inFile); + + /* Extract JFIF files from J_Movie */ + for (i = start; i <= last; ++i) { + size = inoffsets[i] - inoffsets[i-1]- 5; + lseek(fd, inoffsets[i-1], 0); + read(fd, &(op_code), 1); + while (op_code != 0xffffffec) { + read(fd,junk,audiosize); + read(fd, &(op_code), 1); + size = size - audiosize ; + } /* To skip the audio bytes in each frame */ + read(fd, jpeg_size, 4); + read(fd, &(inbuffer[607]), size); + sprintf(ofname, "%s%d.jpg", obase, i); + outFile = fopen(ofname, "wb"); + fwrite(inbuffer, (size+607), sizeof(char), outFile); + fclose(outFile); + } + free(inoffsets); + fclose(inFile); +} + + + + +/*===========================================================================* + * + * ReadJPEG contributed by James Arthur Boucher of Boston University's + * Multimedia Communications Lab + * + * read a JPEG file and copy data into frame original data arrays + * + * RETURNS: mf modified + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +/*************************JPEG LIBRARY INTERFACE*********************/ +/* + * THE BIG PICTURE: + * + * The rough outline this JPEG decompression operation is: + * + * allocate and initialize JPEG decompression object + * specify data source (eg, a file) + * jpeg_read_header(); // obtain image dimensions and other parameters + * set parameters for decompression + * jpeg_start_decompress(); + * while (scan lines remain to be read) + * jpeg_read_scanlines(...); + * jpeg_finish_decompress(); + * release JPEG decompression object + * + */ +void +ReadJPEG(MpegFrame * const mf, + FILE * const fp) { + + static struct jpeg_decompress_struct cinfo; + /* The JPEG decompression parameters and pointers to + working data (which is allocated as needed by the JPEG library). + */ + struct jpeg_error_mgr jerr; + JSAMPARRAY scanarray[3]; + int ci,cd,cp; + JDIMENSION ncols[3]; + JDIMENSION nrows[3]; + jpeg_component_info *compptr; + int buffer_height; + int current_row[3]; + uint8 **orig[3]; + int h_samp[3],v_samp[3]; + int max_h_samp,max_v_samp; + int temp_h, temp_v; + int temp; + + /* Allocate and initialize JPEG decompression object */ + cinfo.err = jpeg_std_error(&jerr); + + /* + ** If we're reading from stdin we want to create the cinfo struct + ** ONCE (during the first read). This is because when reading jpeg + ** from stdin we will not release the cinfo struct, because doing + ** so would totally screw up the read buffer and make it impossible + ** to read jpegs from stdin. + ** Dave Scott (dhs), UofO, 7/19/95 + */ + { + static int first_stdin; + first_stdin = TRUE; /* initial value */ + if ((fp != stdin) || first_stdin) { + first_stdin = FALSE; + /* Now we can initialize the JPEG decompression object. */ + jpeg_create_decompress(&cinfo); + /* specify data source (eg, a file) */ + jpeg_stdio_src(&cinfo, fp); + } + } + + /* specify data source (eg, a file) */ + + jpeg_stdio_src(&cinfo, fp); + + /* read file parameters with jpeg_read_header() */ + + + (void) jpeg_read_header(&cinfo, TRUE); + /* We can ignore the return value from jpeg_read_header since + * (a) suspension is not possible with the stdio data source, and + * (b) we passed TRUE to reject a tables-only JPEG file as an error. + */ + + /* set parameters for decompression */ +#ifdef JPEG4 + cinfo.want_raw_output = TRUE; +#else + cinfo.raw_data_out = TRUE; +#endif + cinfo.out_color_space = JCS_YCbCr; + + /* calculate image output dimensions */ + jpeg_calc_output_dimensions(&cinfo); + /* the above calculation will set these soon */ + /* for now we'll set them ourselves */ + + /* tell mpeg_encode the size of the JPEG Image*/ + Fsize_Note(mf->id,(int)(cinfo.image_width),(int)(cinfo.image_height)); + + /* Allocate memory for the raw YCbCr data to occupy*/ + Frame_AllocYCC(mf); /*allocate space for mpeg frame*/ + + /* copy pointers to array structure- this make the following + code more compact + */ + orig[0] = mf->orig_y; + orig[1] = mf->orig_cb; + orig[2] = mf->orig_cr; + + /* Note that we can use the info obtained from jpeg_read_header. + */ + + /* Start decompressor */ + + jpeg_start_decompress(&cinfo); + + + /* JSAMPLEs per row in output buffer */ + /* collect component subsample values*/ + for (cp=0, compptr = cinfo.comp_info; + cp < cinfo.num_components; + cp++, compptr++) { + h_samp[cp] = compptr->h_samp_factor; + v_samp[cp] = compptr->v_samp_factor; + } + /* calculate max subsample values*/ + temp_h = (h_samp[0]<h_samp[1]) ? h_samp[1] : h_samp[0]; + max_h_samp = (temp_h<h_samp[2]) ? h_samp[2]:temp_h; + temp_v = (v_samp[0]<v_samp[1]) ? v_samp[1] : v_samp[0]; + max_v_samp = (temp_v<v_samp[2]) ? v_samp[2]:temp_v; + + /* Make an 8-row-high sample array that will go away when done + with image + */ +#ifdef JPEG4 + buffer_height = 8; /* could be 2, 4,8 rows high */ +#else + buffer_height = cinfo.max_v_samp_factor * cinfo.min_DCT_scaled_size; +#endif + + for(cp=0,compptr = cinfo.comp_info;cp<cinfo.num_components; + cp++,compptr++) { + ncols[cp] = (JDIMENSION)((cinfo.image_width*compptr->h_samp_factor)/ + max_h_samp); + + nrows[cp] = (JDIMENSION)((buffer_height*compptr->v_samp_factor)/ + max_v_samp); + + scanarray[cp] = (*cinfo.mem->alloc_sarray) + ((j_common_ptr) &cinfo, JPOOL_IMAGE, ncols[cp], nrows[cp]); + } + + /* while (scan lines remain to be read) + jpeg_read_scanlines(...); + */ + + /* Here we use the library's state variable cinfo.output_scanline as the + * loop counter, so that we don't have to keep track ourselves. + */ + + while (cinfo.output_scanline < cinfo.output_height) { + +#ifdef JPEG4 + (void) jpeg_read_raw_scanlines(&cinfo, scanarray, buffer_height); +#else + (void) jpeg_read_raw_data(&cinfo, scanarray, buffer_height); +#endif + + /* alter subsample ratio's if neccessary */ + if ((h_samp[0]==2) && (h_samp[1]==1) && (h_samp[2]==1) && + (v_samp[0]==2) && (v_samp[1]==1) && (v_samp[2]==1)) { + /* we are 4:1:1 as expected by the encoder*/ + } else if((h_samp[0] == 2) && (h_samp[1] == 1) && (h_samp[2]==1) && + (v_samp[0] == 1) && (v_samp[1] == 1) && (v_samp[2]==1)) { + /* must subsample 2:1 vertically and adjust params*/ + for (ci = 1; ci < 3; ++ci) { + for (cp = 0; cp < (buffer_height/2); cp = cp+1) { + for (cd = 0; cd < ncols[ci]; ++cd) { + temp = ((scanarray[ci][cp*2][cd] + + scanarray[ci][(cp*2)+1][cd]) / 2); + scanarray[ci][cp][cd] = (JSAMPLE)(temp); + } + } + } + /* only reset values the first time through*/ + if (cinfo.output_scanline == buffer_height) { + nrows[1] = nrows[1]/2; + nrows[2] = nrows[2]/2; + max_v_samp = 2; + v_samp[0] = 2; + } + } else + pm_error("Invalid subsampling ratio"); + + /* transfer data from jpeg buffer to MPEG frame */ + /* calculate the row we wish to output into */ + for (ci = 0, compptr = cinfo.comp_info; + ci < cinfo.num_components; + ++ci, ++compptr) { + current_row[ci] =((cinfo.output_scanline - buffer_height)* + (v_samp[ci])/max_v_samp); + + jcopy_sample_rows(scanarray[ci],0,(JSAMPARRAY)(orig[ci]), + current_row[ci],nrows[ci],ncols[ci]); + } + } + + /* Step 7: Finish decompression */ + + (void) jpeg_finish_decompress(&cinfo); + /* We can ignore the return value since suspension is not possible + * with the stdio data source. + */ + + /* Step 8: Release JPEG decompression object */ + + /* + ** DO NOT release the cinfo struct if we are reading from stdin, this + ** is because the cinfo struct contains the read buffer, and the read + ** buffer may (and almost always does) contain the end of one image and + ** the beginning of another. If we throw away the read buffer then + ** we loose the beginning of the next image, and we're screwed. + ** Dave Scott (dhs), UofO, 7/19/95 + */ + if (fp == stdin) { + static int no_from_stdin = 0; + ++no_from_stdin; + } else { + /* This is an important step since it will release a good deal + of memory. + */ + jpeg_destroy_decompress(&cinfo); + } + + /* After finish_decompress, we can close the input file. Here we + postpone it until after no more JPEG errors are possible, so as + to simplify the setjmp error logic above. (Actually, I don't + think that jpeg_destroy can do an error exit, but why assume + anything...) + */ + + /* At this point you may want to check to see whether any corrupt-data + * warnings occurred (test whether jerr.pub.num_warnings is nonzero). + * If you prefer to treat corrupt data as a fatal error, override the + * error handler's emit_message method to call error_exit on a warning. + */ +} + + + +/* + * SOME FINE POINTS: + * + * In the above loop, we ignored the return value of jpeg_read_scanlines, + * which is the number of scanlines actually read. We could get away with + * this for the same reasons discussed in the compression example. Actually + * there is one perfectly normal situation in which jpeg_read_scanlines may + * return fewer lines than you asked for: at the bottom of the image. But the + * loop above can't ask for more lines than there are in the image since it + * reads only one line at a time. + * + * In some high-speed operating modes, some data copying can be saved by + * making the buffer passed to jpeg_read_scanlines be cinfo.rec_outbuf_height + * lines high (or a multiple thereof). This will usually be 1, 2, or 4 lines. + * + * To decompress multiple images, you can repeat the whole sequence, or you + * can keep the JPEG object around and just repeat steps 2-7. This will + * save a little bit of startup/shutdown time. + * + * As with compression, some operating modes may require temporary files. + * On some systems you may need to set up a signal handler to ensure that + * temporary files are deleted if the program is interrupted. + * + * Scanlines are returned in the same order as they appear in the JPEG file, + * which is standardly top-to-bottom. If you must have data supplied + * bottom-to-top, you can use one of the virtual arrays provided by the + * JPEG memory manager to invert the data. See wrrle.c for an example. + */ + + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ diff --git a/converter/ppm/ppmtompeg/jrevdct.c b/converter/ppm/ppmtompeg/jrevdct.c new file mode 100644 index 00000000..2e99a67a --- /dev/null +++ b/converter/ppm/ppmtompeg/jrevdct.c @@ -0,0 +1,1278 @@ +/* + * jrevdct.c + * + * Copyright (C) 1991, 1992, 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 the basic inverse-DCT transformation subroutine. + * + * This implementation is based on an algorithm described in + * C. Loeffler, A. Ligtenberg and G. Moschytz, "Practical Fast 1-D DCT + * Algorithms with 11 Multiplications", Proc. Int'l. Conf. on Acoustics, + * Speech, and Signal Processing 1989 (ICASSP '89), pp. 988-991. + * The primary algorithm described there uses 11 multiplies and 29 adds. + * We use their alternate method with 12 multiplies and 32 adds. + * The advantage of this method is that no data path contains more than one + * multiplication; this allows a very simple and accurate implementation in + * scaled fixed-point arithmetic, with a minimal number of shifts. + * + * I've made lots of modifications to attempt to take advantage of the + * sparse nature of the DCT matrices we're getting. Although the logic + * is cumbersome, it's straightforward and the resulting code is much + * faster. + * + * A better way to do this would be to pass in the DCT block as a sparse + * matrix, perhaps with the difference cases encoded. + */ + +#include <memory.h> +#include "all.h" +#include "ansi.h" +#include "dct.h" + + +#define CONST_BITS 13 + +/* + * This routine is specialized to the case DCTSIZE = 8. + */ + +#if DCTSIZE != 8 + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ +#endif + + +/* + * A 2-D IDCT can be done by 1-D IDCT on each row followed by 1-D IDCT + * on each column. Direct algorithms are also available, but they are + * much more complex and seem not to be any faster when reduced to code. + * + * The poop on this scaling stuff is as follows: + * + * Each 1-D IDCT step produces outputs which are a factor of sqrt(N) + * larger than the true IDCT outputs. The final outputs are therefore + * a factor of N larger than desired; since N=8 this can be cured by + * a simple right shift at the end of the algorithm. The advantage of + * this arrangement is that we save two multiplications per 1-D IDCT, + * because the y0 and y4 inputs need not be divided by sqrt(N). + * + * We have to do addition and subtraction of the integer inputs, which + * is no problem, and multiplication by fractional constants, which is + * a problem to do in integer arithmetic. We multiply all the constants + * by CONST_SCALE and convert them to integer constants (thus retaining + * CONST_BITS bits of precision in the constants). After doing a + * multiplication we have to divide the product by CONST_SCALE, with proper + * rounding, to produce the correct output. This division can be done + * cheaply as a right shift of CONST_BITS bits. We postpone shifting + * as long as possible so that partial sums can be added together with + * full fractional precision. + * + * The outputs of the first pass are scaled up by PASS1_BITS bits so that + * they are represented to better-than-integral precision. These outputs + * require BITS_IN_JSAMPLE + PASS1_BITS + 3 bits; this fits in a 16-bit word + * with the recommended scaling. (To scale up 12-bit sample data further, an + * intermediate int32 array would be needed.) + * + * To avoid overflow of the 32-bit intermediate results in pass 2, we must + * have BITS_IN_JSAMPLE + CONST_BITS + PASS1_BITS <= 26. Error analysis + * shows that the values given below are the most effective. + */ + +#ifdef EIGHT_BIT_SAMPLES +#define PASS1_BITS 2 +#else +#define PASS1_BITS 1 /* lose a little precision to avoid overflow */ +#endif + +#define ONE ((int32) 1) + +#define CONST_SCALE (ONE << CONST_BITS) + +/* Convert a positive real constant to an integer scaled by CONST_SCALE. + * IMPORTANT: if your compiler doesn't do this arithmetic at compile time, + * you will pay a significant penalty in run time. In that case, figure + * the correct integer constant values and insert them by hand. + */ + +/* Actually FIX is no longer used, we precomputed them all */ +#define FIX(x) ((int32) ((x) * CONST_SCALE + 0.5)) + +/* Descale and correctly round an int32 value that's scaled by N bits. + * We assume RIGHT_SHIFT rounds towards minus infinity, so adding + * the fudge factor is correct for either sign of X. + */ + +#define DESCALE(x,n) RIGHT_SHIFT((x) + (ONE << ((n)-1)), n) + +/* Multiply an int32 variable by an int32 constant to yield an int32 result. + * For 8-bit samples with the recommended scaling, all the variable + * and constant values involved are no more than 16 bits wide, so a + * 16x16->32 bit multiply can be used instead of a full 32x32 multiply; + * this provides a useful speedup on many machines. + * There is no way to specify a 16x16->32 multiply in portable C, but + * some C compilers will do the right thing if you provide the correct + * combination of casts. + * NB: for 12-bit samples, a full 32-bit multiplication will be needed. + */ + +#ifdef EIGHT_BIT_SAMPLES +#ifdef SHORTxSHORT_32 /* may work if 'int' is 32 bits */ +#define MULTIPLY(var,const) (((INT16) (var)) * ((INT16) (const))) +#endif +#ifdef SHORTxLCONST_32 /* known to work with Microsoft C 6.0 */ +#define MULTIPLY(var,const) (((INT16) (var)) * ((int32) (const))) +#endif +#endif + +#ifndef MULTIPLY /* default definition */ +#define MULTIPLY(var,const) ((var) * (const)) +#endif + + +/* + Unlike our decoder where we approximate the FIXes, we need to use exact +ones here or successive P-frames will drift too much with Reference frame coding +*/ +#define FIX_0_211164243 1730 +#define FIX_0_275899380 2260 +#define FIX_0_298631336 2446 +#define FIX_0_390180644 3196 +#define FIX_0_509795579 4176 +#define FIX_0_541196100 4433 +#define FIX_0_601344887 4926 +#define FIX_0_765366865 6270 +#define FIX_0_785694958 6436 +#define FIX_0_899976223 7373 +#define FIX_1_061594337 8697 +#define FIX_1_111140466 9102 +#define FIX_1_175875602 9633 +#define FIX_1_306562965 10703 +#define FIX_1_387039845 11363 +#define FIX_1_451774981 11893 +#define FIX_1_501321110 12299 +#define FIX_1_662939225 13623 +#define FIX_1_847759065 15137 +#define FIX_1_961570560 16069 +#define FIX_2_053119869 16819 +#define FIX_2_172734803 17799 +#define FIX_2_562915447 20995 +#define FIX_3_072711026 25172 + +/* + Switch on reverse_dct choices +*/ +void reference_rev_dct _ANSI_ARGS_((int16 *block)); +void mpeg_jrevdct_quick _ANSI_ARGS_((int16 *block)); +void init_idctref _ANSI_ARGS_((void)); + +extern boolean pureDCT; + +void +mpeg_jrevdct(data) + DCTBLOCK data; +{ + if (pureDCT) reference_rev_dct(data); + else mpeg_jrevdct_quick(data); +} + +/* + * Perform the inverse DCT on one block of coefficients. + */ + +void +mpeg_jrevdct_quick(data) + DCTBLOCK data; +{ + int32 tmp0, tmp1, tmp2, tmp3; + int32 tmp10, tmp11, tmp12, tmp13; + int32 z1, z2, z3, z4, z5; + int32 d0, d1, d2, d3, d4, d5, d6, d7; + register DCTELEM *dataptr; + int rowctr; + SHIFT_TEMPS + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true IDCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + + dataptr = data; + + for (rowctr = DCTSIZE-1; rowctr >= 0; rowctr--) { + /* Due to quantization, we will usually find that many of the input + * coefficients are zero, especially the AC terms. We can exploit this + * by short-circuiting the IDCT calculation for any row in which all + * the AC terms are zero. In that case each output is equal to the + * DC coefficient (with scale factor as needed). + * With typical images and quantization tables, half or more of the + * row DCT calculations can be simplified this way. + */ + + register int *idataptr = (int*)dataptr; + d0 = dataptr[0]; + d1 = dataptr[1]; + if ((d1 == 0) && (idataptr[1] | idataptr[2] | idataptr[3]) == 0) { + /* AC terms all zero */ + if (d0) { + /* Compute a 32 bit value to assign. */ + DCTELEM dcval = (DCTELEM) (d0 << PASS1_BITS); + register int v = (dcval & 0xffff) | ((dcval << 16) & 0xffff0000); + + idataptr[0] = v; + idataptr[1] = v; + idataptr[2] = v; + idataptr[3] = v; + } + + dataptr += DCTSIZE; /* advance pointer to next row */ + continue; + } + d2 = dataptr[2]; + d3 = dataptr[3]; + d4 = dataptr[4]; + d5 = dataptr[5]; + d6 = dataptr[6]; + d7 = dataptr[7]; + + /* Even part: reverse the even part of the forward DCT. */ + /* The rotator is sqrt(2)*c(-6). */ +{ + if (d6) { + if (d4) { + if (d2) { + if (d0) { + /* d0 != 0, d2 != 0, d4 != 0, d6 != 0 */ + z1 = MULTIPLY(d2 + d6, FIX_0_541196100); + tmp2 = z1 + MULTIPLY(-d6, FIX_1_847759065); + tmp3 = z1 + MULTIPLY(d2, FIX_0_765366865); + + tmp0 = (d0 + d4) << CONST_BITS; + tmp1 = (d0 - d4) << CONST_BITS; + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + } else { + /* d0 == 0, d2 != 0, d4 != 0, d6 != 0 */ + z1 = MULTIPLY(d2 + d6, FIX_0_541196100); + tmp2 = z1 + MULTIPLY(-d6, FIX_1_847759065); + tmp3 = z1 + MULTIPLY(d2, FIX_0_765366865); + + tmp0 = d4 << CONST_BITS; + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp2 - tmp0; + tmp12 = -(tmp0 + tmp2); + } + } else { + if (d0) { + /* d0 != 0, d2 == 0, d4 != 0, d6 != 0 */ + tmp2 = MULTIPLY(-d6, FIX_1_306562965); + tmp3 = MULTIPLY(d6, FIX_0_541196100); + + tmp0 = (d0 + d4) << CONST_BITS; + tmp1 = (d0 - d4) << CONST_BITS; + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + } else { + /* d0 == 0, d2 == 0, d4 != 0, d6 != 0 */ + tmp2 = MULTIPLY(-d6, FIX_1_306562965); + tmp3 = MULTIPLY(d6, FIX_0_541196100); + + tmp0 = d4 << CONST_BITS; + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp2 - tmp0; + tmp12 = -(tmp0 + tmp2); + } + } + } else { + if (d2) { + if (d0) { + /* d0 != 0, d2 != 0, d4 == 0, d6 != 0 */ + z1 = MULTIPLY(d2 + d6, FIX_0_541196100); + tmp2 = z1 + MULTIPLY(-d6, FIX_1_847759065); + tmp3 = z1 + MULTIPLY(d2, FIX_0_765366865); + + tmp0 = d0 << CONST_BITS; + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp0 + tmp2; + tmp12 = tmp0 - tmp2; + } else { + /* d0 == 0, d2 != 0, d4 == 0, d6 != 0 */ + z1 = MULTIPLY(d2 + d6, FIX_0_541196100); + tmp2 = z1 + MULTIPLY(-d6, FIX_1_847759065); + tmp3 = z1 + MULTIPLY(d2, FIX_0_765366865); + + tmp10 = tmp3; + tmp13 = -tmp3; + tmp11 = tmp2; + tmp12 = -tmp2; + } + } else { + if (d0) { + /* d0 != 0, d2 == 0, d4 == 0, d6 != 0 */ + tmp2 = MULTIPLY(-d6, FIX_1_306562965); + tmp3 = MULTIPLY(d6, FIX_0_541196100); + + tmp0 = d0 << CONST_BITS; + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp0 + tmp2; + tmp12 = tmp0 - tmp2; + } else { + /* d0 == 0, d2 == 0, d4 == 0, d6 != 0 */ + tmp2 = MULTIPLY(-d6, FIX_1_306562965); + tmp3 = MULTIPLY(d6, FIX_0_541196100); + + tmp10 = tmp3; + tmp13 = -tmp3; + tmp11 = tmp2; + tmp12 = -tmp2; + } + } + } + } else { + if (d4) { + if (d2) { + if (d0) { + /* d0 != 0, d2 != 0, d4 != 0, d6 == 0 */ + tmp2 = MULTIPLY(d2, FIX_0_541196100); + tmp3 = MULTIPLY(d2, FIX_1_306562965); + + tmp0 = (d0 + d4) << CONST_BITS; + tmp1 = (d0 - d4) << CONST_BITS; + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + } else { + /* d0 == 0, d2 != 0, d4 != 0, d6 == 0 */ + tmp2 = MULTIPLY(d2, FIX_0_541196100); + tmp3 = MULTIPLY(d2, FIX_1_306562965); + + tmp0 = d4 << CONST_BITS; + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp2 - tmp0; + tmp12 = -(tmp0 + tmp2); + } + } else { + if (d0) { + /* d0 != 0, d2 == 0, d4 != 0, d6 == 0 */ + tmp10 = tmp13 = (d0 + d4) << CONST_BITS; + tmp11 = tmp12 = (d0 - d4) << CONST_BITS; + } else { + /* d0 == 0, d2 == 0, d4 != 0, d6 == 0 */ + tmp10 = tmp13 = d4 << CONST_BITS; + tmp11 = tmp12 = -tmp10; + } + } + } else { + if (d2) { + if (d0) { + /* d0 != 0, d2 != 0, d4 == 0, d6 == 0 */ + tmp2 = MULTIPLY(d2, FIX_0_541196100); + tmp3 = MULTIPLY(d2, FIX_1_306562965); + + tmp0 = d0 << CONST_BITS; + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp0 + tmp2; + tmp12 = tmp0 - tmp2; + } else { + /* d0 == 0, d2 != 0, d4 == 0, d6 == 0 */ + tmp2 = MULTIPLY(d2, FIX_0_541196100); + tmp3 = MULTIPLY(d2, FIX_1_306562965); + + tmp10 = tmp3; + tmp13 = -tmp3; + tmp11 = tmp2; + tmp12 = -tmp2; + } + } else { + if (d0) { + /* d0 != 0, d2 == 0, d4 == 0, d6 == 0 */ + tmp10 = tmp13 = tmp11 = tmp12 = d0 << CONST_BITS; + } else { + /* d0 == 0, d2 == 0, d4 == 0, d6 == 0 */ + tmp10 = tmp13 = tmp11 = tmp12 = 0; + } + } + } + } + + /* Odd part per figure 8; the matrix is unitary and hence its + * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. + */ + + if (d7) { + if (d5) { + if (d3) { + if (d1) { + /* d1 != 0, d3 != 0, d5 != 0, d7 != 0 */ + z1 = d7 + d1; + z2 = d5 + d3; + z3 = d7 + d3; + z4 = d5 + d1; + z5 = MULTIPLY(z3 + z4, FIX_1_175875602); + + tmp0 = MULTIPLY(d7, FIX_0_298631336); + tmp1 = MULTIPLY(d5, FIX_2_053119869); + tmp2 = MULTIPLY(d3, FIX_3_072711026); + tmp3 = MULTIPLY(d1, FIX_1_501321110); + z1 = MULTIPLY(-z1, FIX_0_899976223); + z2 = MULTIPLY(-z2, FIX_2_562915447); + z3 = MULTIPLY(-z3, FIX_1_961570560); + z4 = MULTIPLY(-z4, FIX_0_390180644); + + z3 += z5; + z4 += z5; + + tmp0 += z1 + z3; + tmp1 += z2 + z4; + tmp2 += z2 + z3; + tmp3 += z1 + z4; + } else { + /* d1 == 0, d3 != 0, d5 != 0, d7 != 0 */ + z2 = d5 + d3; + z3 = d7 + d3; + z5 = MULTIPLY(z3 + d5, FIX_1_175875602); + + tmp0 = MULTIPLY(d7, FIX_0_298631336); + tmp1 = MULTIPLY(d5, FIX_2_053119869); + tmp2 = MULTIPLY(d3, FIX_3_072711026); + z1 = MULTIPLY(-d7, FIX_0_899976223); + z2 = MULTIPLY(-z2, FIX_2_562915447); + z3 = MULTIPLY(-z3, FIX_1_961570560); + z4 = MULTIPLY(-d5, FIX_0_390180644); + + z3 += z5; + z4 += z5; + + tmp0 += z1 + z3; + tmp1 += z2 + z4; + tmp2 += z2 + z3; + tmp3 = z1 + z4; + } + } else { + if (d1) { + /* d1 != 0, d3 == 0, d5 != 0, d7 != 0 */ + z1 = d7 + d1; + z4 = d5 + d1; + z5 = MULTIPLY(d7 + z4, FIX_1_175875602); + + tmp0 = MULTIPLY(d7, FIX_0_298631336); + tmp1 = MULTIPLY(d5, FIX_2_053119869); + tmp3 = MULTIPLY(d1, FIX_1_501321110); + z1 = MULTIPLY(-z1, FIX_0_899976223); + z2 = MULTIPLY(-d5, FIX_2_562915447); + z3 = MULTIPLY(-d7, FIX_1_961570560); + z4 = MULTIPLY(-z4, FIX_0_390180644); + + z3 += z5; + z4 += z5; + + tmp0 += z1 + z3; + tmp1 += z2 + z4; + tmp2 = z2 + z3; + tmp3 += z1 + z4; + } else { + /* d1 == 0, d3 == 0, d5 != 0, d7 != 0 */ + tmp0 = MULTIPLY(-d7, FIX_0_601344887); + z1 = MULTIPLY(-d7, FIX_0_899976223); + z3 = MULTIPLY(-d7, FIX_1_961570560); + tmp1 = MULTIPLY(-d5, FIX_0_509795579); + z2 = MULTIPLY(-d5, FIX_2_562915447); + z4 = MULTIPLY(-d5, FIX_0_390180644); + z5 = MULTIPLY(d5 + d7, FIX_1_175875602); + + z3 += z5; + z4 += z5; + + tmp0 += z3; + tmp1 += z4; + tmp2 = z2 + z3; + tmp3 = z1 + z4; + } + } + } else { + if (d3) { + if (d1) { + /* d1 != 0, d3 != 0, d5 == 0, d7 != 0 */ + z1 = d7 + d1; + z3 = d7 + d3; + z5 = MULTIPLY(z3 + d1, FIX_1_175875602); + + tmp0 = MULTIPLY(d7, FIX_0_298631336); + tmp2 = MULTIPLY(d3, FIX_3_072711026); + tmp3 = MULTIPLY(d1, FIX_1_501321110); + z1 = MULTIPLY(-z1, FIX_0_899976223); + z2 = MULTIPLY(-d3, FIX_2_562915447); + z3 = MULTIPLY(-z3, FIX_1_961570560); + z4 = MULTIPLY(-d1, FIX_0_390180644); + + z3 += z5; + z4 += z5; + + tmp0 += z1 + z3; + tmp1 = z2 + z4; + tmp2 += z2 + z3; + tmp3 += z1 + z4; + } else { + /* d1 == 0, d3 != 0, d5 == 0, d7 != 0 */ + z3 = d7 + d3; + + tmp0 = MULTIPLY(-d7, FIX_0_601344887); + z1 = MULTIPLY(-d7, FIX_0_899976223); + tmp2 = MULTIPLY(d3, FIX_0_509795579); + z2 = MULTIPLY(-d3, FIX_2_562915447); + z5 = MULTIPLY(z3, FIX_1_175875602); + z3 = MULTIPLY(-z3, FIX_0_785694958); + + tmp0 += z3; + tmp1 = z2 + z5; + tmp2 += z3; + tmp3 = z1 + z5; + } + } else { + if (d1) { + /* d1 != 0, d3 == 0, d5 == 0, d7 != 0 */ + z1 = d7 + d1; + z5 = MULTIPLY(z1, FIX_1_175875602); + + z1 = MULTIPLY(z1, FIX_0_275899380); + z3 = MULTIPLY(-d7, FIX_1_961570560); + tmp0 = MULTIPLY(-d7, FIX_1_662939225); + z4 = MULTIPLY(-d1, FIX_0_390180644); + tmp3 = MULTIPLY(d1, FIX_1_111140466); + + tmp0 += z1; + tmp1 = z4 + z5; + tmp2 = z3 + z5; + tmp3 += z1; + } else { + /* d1 == 0, d3 == 0, d5 == 0, d7 != 0 */ + tmp0 = MULTIPLY(-d7, FIX_1_387039845); + tmp1 = MULTIPLY(d7, FIX_1_175875602); + tmp2 = MULTIPLY(-d7, FIX_0_785694958); + tmp3 = MULTIPLY(d7, FIX_0_275899380); + } + } + } + } else { + if (d5) { + if (d3) { + if (d1) { + /* d1 != 0, d3 != 0, d5 != 0, d7 == 0 */ + z2 = d5 + d3; + z4 = d5 + d1; + z5 = MULTIPLY(d3 + z4, FIX_1_175875602); + + tmp1 = MULTIPLY(d5, FIX_2_053119869); + tmp2 = MULTIPLY(d3, FIX_3_072711026); + tmp3 = MULTIPLY(d1, FIX_1_501321110); + z1 = MULTIPLY(-d1, FIX_0_899976223); + z2 = MULTIPLY(-z2, FIX_2_562915447); + z3 = MULTIPLY(-d3, FIX_1_961570560); + z4 = MULTIPLY(-z4, FIX_0_390180644); + + z3 += z5; + z4 += z5; + + tmp0 = z1 + z3; + tmp1 += z2 + z4; + tmp2 += z2 + z3; + tmp3 += z1 + z4; + } else { + /* d1 == 0, d3 != 0, d5 != 0, d7 == 0 */ + z2 = d5 + d3; + + z5 = MULTIPLY(z2, FIX_1_175875602); + tmp1 = MULTIPLY(d5, FIX_1_662939225); + z4 = MULTIPLY(-d5, FIX_0_390180644); + z2 = MULTIPLY(-z2, FIX_1_387039845); + tmp2 = MULTIPLY(d3, FIX_1_111140466); + z3 = MULTIPLY(-d3, FIX_1_961570560); + + tmp0 = z3 + z5; + tmp1 += z2; + tmp2 += z2; + tmp3 = z4 + z5; + } + } else { + if (d1) { + /* d1 != 0, d3 == 0, d5 != 0, d7 == 0 */ + z4 = d5 + d1; + + z5 = MULTIPLY(z4, FIX_1_175875602); + z1 = MULTIPLY(-d1, FIX_0_899976223); + tmp3 = MULTIPLY(d1, FIX_0_601344887); + tmp1 = MULTIPLY(-d5, FIX_0_509795579); + z2 = MULTIPLY(-d5, FIX_2_562915447); + z4 = MULTIPLY(z4, FIX_0_785694958); + + tmp0 = z1 + z5; + tmp1 += z4; + tmp2 = z2 + z5; + tmp3 += z4; + } else { + /* d1 == 0, d3 == 0, d5 != 0, d7 == 0 */ + tmp0 = MULTIPLY(d5, FIX_1_175875602); + tmp1 = MULTIPLY(d5, FIX_0_275899380); + tmp2 = MULTIPLY(-d5, FIX_1_387039845); + tmp3 = MULTIPLY(d5, FIX_0_785694958); + } + } + } else { + if (d3) { + if (d1) { + /* d1 != 0, d3 != 0, d5 == 0, d7 == 0 */ + z5 = d1 + d3; + tmp3 = MULTIPLY(d1, FIX_0_211164243); + tmp2 = MULTIPLY(-d3, FIX_1_451774981); + z1 = MULTIPLY(d1, FIX_1_061594337); + z2 = MULTIPLY(-d3, FIX_2_172734803); + z4 = MULTIPLY(z5, FIX_0_785694958); + z5 = MULTIPLY(z5, FIX_1_175875602); + + tmp0 = z1 - z4; + tmp1 = z2 + z4; + tmp2 += z5; + tmp3 += z5; + } else { + /* d1 == 0, d3 != 0, d5 == 0, d7 == 0 */ + tmp0 = MULTIPLY(-d3, FIX_0_785694958); + tmp1 = MULTIPLY(-d3, FIX_1_387039845); + tmp2 = MULTIPLY(-d3, FIX_0_275899380); + tmp3 = MULTIPLY(d3, FIX_1_175875602); + } + } else { + if (d1) { + /* d1 != 0, d3 == 0, d5 == 0, d7 == 0 */ + tmp0 = MULTIPLY(d1, FIX_0_275899380); + tmp1 = MULTIPLY(d1, FIX_0_785694958); + tmp2 = MULTIPLY(d1, FIX_1_175875602); + tmp3 = MULTIPLY(d1, FIX_1_387039845); + } else { + /* d1 == 0, d3 == 0, d5 == 0, d7 == 0 */ + tmp0 = tmp1 = tmp2 = tmp3 = 0; + } + } + } + } +} + /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */ + + dataptr[0] = (DCTELEM) DESCALE(tmp10 + tmp3, CONST_BITS-PASS1_BITS); + dataptr[7] = (DCTELEM) DESCALE(tmp10 - tmp3, CONST_BITS-PASS1_BITS); + dataptr[1] = (DCTELEM) DESCALE(tmp11 + tmp2, CONST_BITS-PASS1_BITS); + dataptr[6] = (DCTELEM) DESCALE(tmp11 - tmp2, CONST_BITS-PASS1_BITS); + dataptr[2] = (DCTELEM) DESCALE(tmp12 + tmp1, CONST_BITS-PASS1_BITS); + dataptr[5] = (DCTELEM) DESCALE(tmp12 - tmp1, CONST_BITS-PASS1_BITS); + dataptr[3] = (DCTELEM) DESCALE(tmp13 + tmp0, CONST_BITS-PASS1_BITS); + dataptr[4] = (DCTELEM) DESCALE(tmp13 - tmp0, CONST_BITS-PASS1_BITS); + + dataptr += DCTSIZE; /* advance pointer to next row */ + } + + /* Pass 2: process columns. */ + /* Note that we must descale the results by a factor of 8 == 2**3, */ + /* and also undo the PASS1_BITS scaling. */ + + dataptr = data; + for (rowctr = DCTSIZE-1; rowctr >= 0; rowctr--) { + /* Columns of zeroes can be exploited in the same way as we did with rows. + * However, the row calculation has created many nonzero AC terms, so the + * simplification applies less often (typically 5% to 10% of the time). + * On machines with very fast multiplication, it's possible that the + * test takes more time than it's worth. In that case this section + * may be commented out. + */ + + d0 = dataptr[DCTSIZE*0]; + d1 = dataptr[DCTSIZE*1]; + d2 = dataptr[DCTSIZE*2]; + d3 = dataptr[DCTSIZE*3]; + d4 = dataptr[DCTSIZE*4]; + d5 = dataptr[DCTSIZE*5]; + d6 = dataptr[DCTSIZE*6]; + d7 = dataptr[DCTSIZE*7]; + + /* Even part: reverse the even part of the forward DCT. */ + /* The rotator is sqrt(2)*c(-6). */ + if (d6) { + if (d4) { + if (d2) { + if (d0) { + /* d0 != 0, d2 != 0, d4 != 0, d6 != 0 */ + z1 = MULTIPLY(d2 + d6, FIX_0_541196100); + tmp2 = z1 + MULTIPLY(-d6, FIX_1_847759065); + tmp3 = z1 + MULTIPLY(d2, FIX_0_765366865); + + tmp0 = (d0 + d4) << CONST_BITS; + tmp1 = (d0 - d4) << CONST_BITS; + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + } else { + /* d0 == 0, d2 != 0, d4 != 0, d6 != 0 */ + z1 = MULTIPLY(d2 + d6, FIX_0_541196100); + tmp2 = z1 + MULTIPLY(-d6, FIX_1_847759065); + tmp3 = z1 + MULTIPLY(d2, FIX_0_765366865); + + tmp0 = d4 << CONST_BITS; + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp2 - tmp0; + tmp12 = -(tmp0 + tmp2); + } + } else { + if (d0) { + /* d0 != 0, d2 == 0, d4 != 0, d6 != 0 */ + tmp2 = MULTIPLY(-d6, FIX_1_306562965); + tmp3 = MULTIPLY(d6, FIX_0_541196100); + + tmp0 = (d0 + d4) << CONST_BITS; + tmp1 = (d0 - d4) << CONST_BITS; + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + } else { + /* d0 == 0, d2 == 0, d4 != 0, d6 != 0 */ + tmp2 = MULTIPLY(-d6, FIX_1_306562965); + tmp3 = MULTIPLY(d6, FIX_0_541196100); + + tmp0 = d4 << CONST_BITS; + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp2 - tmp0; + tmp12 = -(tmp0 + tmp2); + } + } + } else { + if (d2) { + if (d0) { + /* d0 != 0, d2 != 0, d4 == 0, d6 != 0 */ + z1 = MULTIPLY(d2 + d6, FIX_0_541196100); + tmp2 = z1 + MULTIPLY(-d6, FIX_1_847759065); + tmp3 = z1 + MULTIPLY(d2, FIX_0_765366865); + + tmp0 = d0 << CONST_BITS; + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp0 + tmp2; + tmp12 = tmp0 - tmp2; + } else { + /* d0 == 0, d2 != 0, d4 == 0, d6 != 0 */ + z1 = MULTIPLY(d2 + d6, FIX_0_541196100); + tmp2 = z1 + MULTIPLY(-d6, FIX_1_847759065); + tmp3 = z1 + MULTIPLY(d2, FIX_0_765366865); + + tmp10 = tmp3; + tmp13 = -tmp3; + tmp11 = tmp2; + tmp12 = -tmp2; + } + } else { + if (d0) { + /* d0 != 0, d2 == 0, d4 == 0, d6 != 0 */ + tmp2 = MULTIPLY(-d6, FIX_1_306562965); + tmp3 = MULTIPLY(d6, FIX_0_541196100); + + tmp0 = d0 << CONST_BITS; + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp0 + tmp2; + tmp12 = tmp0 - tmp2; + } else { + /* d0 == 0, d2 == 0, d4 == 0, d6 != 0 */ + tmp2 = MULTIPLY(-d6, FIX_1_306562965); + tmp3 = MULTIPLY(d6, FIX_0_541196100); + + tmp10 = tmp3; + tmp13 = -tmp3; + tmp11 = tmp2; + tmp12 = -tmp2; + } + } + } + } else { + if (d4) { + if (d2) { + if (d0) { + /* d0 != 0, d2 != 0, d4 != 0, d6 == 0 */ + tmp2 = MULTIPLY(d2, FIX_0_541196100); + tmp3 = MULTIPLY(d2, FIX_1_306562965); + + tmp0 = (d0 + d4) << CONST_BITS; + tmp1 = (d0 - d4) << CONST_BITS; + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + } else { + /* d0 == 0, d2 != 0, d4 != 0, d6 == 0 */ + tmp2 = MULTIPLY(d2, FIX_0_541196100); + tmp3 = MULTIPLY(d2, FIX_1_306562965); + + tmp0 = d4 << CONST_BITS; + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp2 - tmp0; + tmp12 = -(tmp0 + tmp2); + } + } else { + if (d0) { + /* d0 != 0, d2 == 0, d4 != 0, d6 == 0 */ + tmp10 = tmp13 = (d0 + d4) << CONST_BITS; + tmp11 = tmp12 = (d0 - d4) << CONST_BITS; + } else { + /* d0 == 0, d2 == 0, d4 != 0, d6 == 0 */ + tmp10 = tmp13 = d4 << CONST_BITS; + tmp11 = tmp12 = -tmp10; + } + } + } else { + if (d2) { + if (d0) { + /* d0 != 0, d2 != 0, d4 == 0, d6 == 0 */ + tmp2 = MULTIPLY(d2, FIX_0_541196100); + tmp3 = MULTIPLY(d2, FIX_1_306562965); + + tmp0 = d0 << CONST_BITS; + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp0 + tmp2; + tmp12 = tmp0 - tmp2; + } else { + /* d0 == 0, d2 != 0, d4 == 0, d6 == 0 */ + tmp2 = MULTIPLY(d2, FIX_0_541196100); + tmp3 = MULTIPLY(d2, FIX_1_306562965); + + tmp10 = tmp3; + tmp13 = -tmp3; + tmp11 = tmp2; + tmp12 = -tmp2; + } + } else { + if (d0) { + /* d0 != 0, d2 == 0, d4 == 0, d6 == 0 */ + tmp10 = tmp13 = tmp11 = tmp12 = d0 << CONST_BITS; + } else { + /* d0 == 0, d2 == 0, d4 == 0, d6 == 0 */ + tmp10 = tmp13 = tmp11 = tmp12 = 0; + } + } + } + } + + /* Odd part per figure 8; the matrix is unitary and hence its + * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. + */ + if (d7) { + if (d5) { + if (d3) { + if (d1) { + /* d1 != 0, d3 != 0, d5 != 0, d7 != 0 */ + z1 = d7 + d1; + z2 = d5 + d3; + z3 = d7 + d3; + z4 = d5 + d1; + z5 = MULTIPLY(z3 + z4, FIX_1_175875602); + + tmp0 = MULTIPLY(d7, FIX_0_298631336); + tmp1 = MULTIPLY(d5, FIX_2_053119869); + tmp2 = MULTIPLY(d3, FIX_3_072711026); + tmp3 = MULTIPLY(d1, FIX_1_501321110); + z1 = MULTIPLY(-z1, FIX_0_899976223); + z2 = MULTIPLY(-z2, FIX_2_562915447); + z3 = MULTIPLY(-z3, FIX_1_961570560); + z4 = MULTIPLY(-z4, FIX_0_390180644); + + z3 += z5; + z4 += z5; + + tmp0 += z1 + z3; + tmp1 += z2 + z4; + tmp2 += z2 + z3; + tmp3 += z1 + z4; + } else { + /* d1 == 0, d3 != 0, d5 != 0, d7 != 0 */ + z1 = d7; + z2 = d5 + d3; + z3 = d7 + d3; + z5 = MULTIPLY(z3 + d5, FIX_1_175875602); + + tmp0 = MULTIPLY(d7, FIX_0_298631336); + tmp1 = MULTIPLY(d5, FIX_2_053119869); + tmp2 = MULTIPLY(d3, FIX_3_072711026); + z1 = MULTIPLY(-d7, FIX_0_899976223); + z2 = MULTIPLY(-z2, FIX_2_562915447); + z3 = MULTIPLY(-z3, FIX_1_961570560); + z4 = MULTIPLY(-d5, FIX_0_390180644); + + z3 += z5; + z4 += z5; + + tmp0 += z1 + z3; + tmp1 += z2 + z4; + tmp2 += z2 + z3; + tmp3 = z1 + z4; + } + } else { + if (d1) { + /* d1 != 0, d3 == 0, d5 != 0, d7 != 0 */ + z1 = d7 + d1; + z2 = d5; + z3 = d7; + z4 = d5 + d1; + z5 = MULTIPLY(z3 + z4, FIX_1_175875602); + + tmp0 = MULTIPLY(d7, FIX_0_298631336); + tmp1 = MULTIPLY(d5, FIX_2_053119869); + tmp3 = MULTIPLY(d1, FIX_1_501321110); + z1 = MULTIPLY(-z1, FIX_0_899976223); + z2 = MULTIPLY(-d5, FIX_2_562915447); + z3 = MULTIPLY(-d7, FIX_1_961570560); + z4 = MULTIPLY(-z4, FIX_0_390180644); + + z3 += z5; + z4 += z5; + + tmp0 += z1 + z3; + tmp1 += z2 + z4; + tmp2 = z2 + z3; + tmp3 += z1 + z4; + } else { + /* d1 == 0, d3 == 0, d5 != 0, d7 != 0 */ + tmp0 = MULTIPLY(-d7, FIX_0_601344887); + z1 = MULTIPLY(-d7, FIX_0_899976223); + z3 = MULTIPLY(-d7, FIX_1_961570560); + tmp1 = MULTIPLY(-d5, FIX_0_509795579); + z2 = MULTIPLY(-d5, FIX_2_562915447); + z4 = MULTIPLY(-d5, FIX_0_390180644); + z5 = MULTIPLY(d5 + d7, FIX_1_175875602); + + z3 += z5; + z4 += z5; + + tmp0 += z3; + tmp1 += z4; + tmp2 = z2 + z3; + tmp3 = z1 + z4; + } + } + } else { + if (d3) { + if (d1) { + /* d1 != 0, d3 != 0, d5 == 0, d7 != 0 */ + z1 = d7 + d1; + z3 = d7 + d3; + z5 = MULTIPLY(z3 + d1, FIX_1_175875602); + + tmp0 = MULTIPLY(d7, FIX_0_298631336); + tmp2 = MULTIPLY(d3, FIX_3_072711026); + tmp3 = MULTIPLY(d1, FIX_1_501321110); + z1 = MULTIPLY(-z1, FIX_0_899976223); + z2 = MULTIPLY(-d3, FIX_2_562915447); + z3 = MULTIPLY(-z3, FIX_1_961570560); + z4 = MULTIPLY(-d1, FIX_0_390180644); + + z3 += z5; + z4 += z5; + + tmp0 += z1 + z3; + tmp1 = z2 + z4; + tmp2 += z2 + z3; + tmp3 += z1 + z4; + } else { + /* d1 == 0, d3 != 0, d5 == 0, d7 != 0 */ + z3 = d7 + d3; + + tmp0 = MULTIPLY(-d7, FIX_0_601344887); + z1 = MULTIPLY(-d7, FIX_0_899976223); + tmp2 = MULTIPLY(d3, FIX_0_509795579); + z2 = MULTIPLY(-d3, FIX_2_562915447); + z5 = MULTIPLY(z3, FIX_1_175875602); + z3 = MULTIPLY(-z3, FIX_0_785694958); + + tmp0 += z3; + tmp1 = z2 + z5; + tmp2 += z3; + tmp3 = z1 + z5; + } + } else { + if (d1) { + /* d1 != 0, d3 == 0, d5 == 0, d7 != 0 */ + z1 = d7 + d1; + z5 = MULTIPLY(z1, FIX_1_175875602); + + z1 = MULTIPLY(z1, FIX_0_275899380); + z3 = MULTIPLY(-d7, FIX_1_961570560); + tmp0 = MULTIPLY(-d7, FIX_1_662939225); + z4 = MULTIPLY(-d1, FIX_0_390180644); + tmp3 = MULTIPLY(d1, FIX_1_111140466); + + tmp0 += z1; + tmp1 = z4 + z5; + tmp2 = z3 + z5; + tmp3 += z1; + } else { + /* d1 == 0, d3 == 0, d5 == 0, d7 != 0 */ + tmp0 = MULTIPLY(-d7, FIX_1_387039845); + tmp1 = MULTIPLY(d7, FIX_1_175875602); + tmp2 = MULTIPLY(-d7, FIX_0_785694958); + tmp3 = MULTIPLY(d7, FIX_0_275899380); + } + } + } + } else { + if (d5) { + if (d3) { + if (d1) { + /* d1 != 0, d3 != 0, d5 != 0, d7 == 0 */ + z2 = d5 + d3; + z4 = d5 + d1; + z5 = MULTIPLY(d3 + z4, FIX_1_175875602); + + tmp1 = MULTIPLY(d5, FIX_2_053119869); + tmp2 = MULTIPLY(d3, FIX_3_072711026); + tmp3 = MULTIPLY(d1, FIX_1_501321110); + z1 = MULTIPLY(-d1, FIX_0_899976223); + z2 = MULTIPLY(-z2, FIX_2_562915447); + z3 = MULTIPLY(-d3, FIX_1_961570560); + z4 = MULTIPLY(-z4, FIX_0_390180644); + + z3 += z5; + z4 += z5; + + tmp0 = z1 + z3; + tmp1 += z2 + z4; + tmp2 += z2 + z3; + tmp3 += z1 + z4; + } else { + /* d1 == 0, d3 != 0, d5 != 0, d7 == 0 */ + z2 = d5 + d3; + + z5 = MULTIPLY(z2, FIX_1_175875602); + tmp1 = MULTIPLY(d5, FIX_1_662939225); + z4 = MULTIPLY(-d5, FIX_0_390180644); + z2 = MULTIPLY(-z2, FIX_1_387039845); + tmp2 = MULTIPLY(d3, FIX_1_111140466); + z3 = MULTIPLY(-d3, FIX_1_961570560); + + tmp0 = z3 + z5; + tmp1 += z2; + tmp2 += z2; + tmp3 = z4 + z5; + } + } else { + if (d1) { + /* d1 != 0, d3 == 0, d5 != 0, d7 == 0 */ + z4 = d5 + d1; + + z5 = MULTIPLY(z4, FIX_1_175875602); + z1 = MULTIPLY(-d1, FIX_0_899976223); + tmp3 = MULTIPLY(d1, FIX_0_601344887); + tmp1 = MULTIPLY(-d5, FIX_0_509795579); + z2 = MULTIPLY(-d5, FIX_2_562915447); + z4 = MULTIPLY(z4, FIX_0_785694958); + + tmp0 = z1 + z5; + tmp1 += z4; + tmp2 = z2 + z5; + tmp3 += z4; + } else { + /* d1 == 0, d3 == 0, d5 != 0, d7 == 0 */ + tmp0 = MULTIPLY(d5, FIX_1_175875602); + tmp1 = MULTIPLY(d5, FIX_0_275899380); + tmp2 = MULTIPLY(-d5, FIX_1_387039845); + tmp3 = MULTIPLY(d5, FIX_0_785694958); + } + } + } else { + if (d3) { + if (d1) { + /* d1 != 0, d3 != 0, d5 == 0, d7 == 0 */ + z5 = d1 + d3; + tmp3 = MULTIPLY(d1, FIX_0_211164243); + tmp2 = MULTIPLY(-d3, FIX_1_451774981); + z1 = MULTIPLY(d1, FIX_1_061594337); + z2 = MULTIPLY(-d3, FIX_2_172734803); + z4 = MULTIPLY(z5, FIX_0_785694958); + z5 = MULTIPLY(z5, FIX_1_175875602); + + tmp0 = z1 - z4; + tmp1 = z2 + z4; + tmp2 += z5; + tmp3 += z5; + } else { + /* d1 == 0, d3 != 0, d5 == 0, d7 == 0 */ + tmp0 = MULTIPLY(-d3, FIX_0_785694958); + tmp1 = MULTIPLY(-d3, FIX_1_387039845); + tmp2 = MULTIPLY(-d3, FIX_0_275899380); + tmp3 = MULTIPLY(d3, FIX_1_175875602); + } + } else { + if (d1) { + /* d1 != 0, d3 == 0, d5 == 0, d7 == 0 */ + tmp0 = MULTIPLY(d1, FIX_0_275899380); + tmp1 = MULTIPLY(d1, FIX_0_785694958); + tmp2 = MULTIPLY(d1, FIX_1_175875602); + tmp3 = MULTIPLY(d1, FIX_1_387039845); + } else { + /* d1 == 0, d3 == 0, d5 == 0, d7 == 0 */ + tmp0 = tmp1 = tmp2 = tmp3 = 0; + } + } + } + } + + /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */ + + dataptr[DCTSIZE*0] = (DCTELEM) DESCALE(tmp10 + tmp3, + CONST_BITS+PASS1_BITS+3); + dataptr[DCTSIZE*7] = (DCTELEM) DESCALE(tmp10 - tmp3, + CONST_BITS+PASS1_BITS+3); + dataptr[DCTSIZE*1] = (DCTELEM) DESCALE(tmp11 + tmp2, + CONST_BITS+PASS1_BITS+3); + dataptr[DCTSIZE*6] = (DCTELEM) DESCALE(tmp11 - tmp2, + CONST_BITS+PASS1_BITS+3); + dataptr[DCTSIZE*2] = (DCTELEM) DESCALE(tmp12 + tmp1, + CONST_BITS+PASS1_BITS+3); + dataptr[DCTSIZE*5] = (DCTELEM) DESCALE(tmp12 - tmp1, + CONST_BITS+PASS1_BITS+3); + dataptr[DCTSIZE*3] = (DCTELEM) DESCALE(tmp13 + tmp0, + CONST_BITS+PASS1_BITS+3); + dataptr[DCTSIZE*4] = (DCTELEM) DESCALE(tmp13 - tmp0, + CONST_BITS+PASS1_BITS+3); + + dataptr++; /* advance pointer to next column */ + } +} + + +/* here is the reference one, in case of problems with the normal one */ + +/* idctref.c, Inverse Discrete Fourier Transform, double precision */ + +/* Copyright (C) 1994, MPEG Software Simulation Group. All Rights Reserved. */ + +/* + * Disclaimer of Warranty + * + * These software programs are available to the user without any license fee or + * royalty on an "as is" basis. The MPEG Software Simulation Group disclaims + * any and all warranties, whether express, implied, or statuary, including any + * implied warranties or merchantability or of fitness for a particular + * purpose. In no event shall the copyright-holder be liable for any + * incidental, punitive, or consequential damages of any kind whatsoever + * arising from the use of these programs. + * + * This disclaimer of warranty extends to the user of these programs and user's + * customers, employees, agents, transferees, successors, and assigns. + * + * The MPEG Software Simulation Group does not represent or warrant that the + * programs furnished hereunder are free of infringement of any third-party + * patents. + * + * Commercial implementations of MPEG-1 and MPEG-2 video, including shareware, + * are subject to royalty fees to patent holders. Many of these patents are + * general enough such that they are unavoidable regardless of implementation + * design. + * + */ + +/* Perform IEEE 1180 reference (64-bit floating point, separable 8x1 + * direct matrix multiply) Inverse Discrete Cosine Transform +*/ + + +/* Here we use math.h to generate constants. Compiler results may + vary a little */ + +#ifndef PI +#ifdef M_PI +#define PI M_PI +#else +#define PI 3.14159265358979323846 +#endif +#endif + +/* cosine transform matrix for 8x1 IDCT */ +static double itrans_coef[8][8]; + +/* initialize DCT coefficient matrix */ + +void init_idctref() +{ + int freq, time; + double scale; + + for (freq=0; freq < 8; freq++) + { + scale = (freq == 0) ? sqrt(0.125) : 0.5; + for (time=0; time<8; time++) + itrans_coef[freq][time] = scale*cos((PI/8.0)*freq*(time + 0.5)); + } +} + +/* perform IDCT matrix multiply for 8x8 coefficient block */ + +void reference_rev_dct(block) +int16 *block; +{ + int i, j, k, v; + double partial_product; + double tmp[64]; + + for (i=0; i<8; i++) + for (j=0; j<8; j++) + { + partial_product = 0.0; + + for (k=0; k<8; k++) + partial_product+= itrans_coef[k][j]*block[8*i+k]; + + tmp[8*i+j] = partial_product; + } + + /* Transpose operation is integrated into address mapping by switching + loop order of i and j */ + + for (j=0; j<8; j++) + for (i=0; i<8; i++) + { + partial_product = 0.0; + + for (k=0; k<8; k++) + partial_product+= itrans_coef[k][i]*tmp[8*k+j]; + + v = floor(partial_product+0.5); + block[8*i+j] = (v<-256) ? -256 : ((v>255) ? 255 : v); + } +} diff --git a/converter/ppm/ppmtompeg/memory.c b/converter/ppm/ppmtompeg/memory.c new file mode 100644 index 00000000..f117994a --- /dev/null +++ b/converter/ppm/ppmtompeg/memory.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/* + * $Header: /n/charlie-brown/project/mm/mpeg/mpeg_dist/mpeg_encode/RCS/memory.c,v 1.3 1995/01/19 23:08:43 eyhung Exp $ + * $Log: memory.c,v $ + * Revision 1.3 1995/01/19 23:08:43 eyhung + * Changed copyrights + * + * Revision 1.2 1993/06/03 21:08:08 keving + * nothing + * + * Revision 1.1 1993/04/27 21:32:26 keving + * nothing + * + * + */ + + +#include <stdlib.h> +#include <stdio.h> +#include "memory.h" + + +/* memory handling routines + * + */ + +long totalMemory = 0; +long maxMemory = 0; + + +char *MemAlloc(size_t size) +{ + totalMemory += (long)size; + if ( totalMemory > maxMemory ) + { + maxMemory = totalMemory; + } + + return malloc(size); +} + +void MemFree(char *ptr, long bytes) +{ + totalMemory -= bytes; + free(ptr); +} + +void PrintMaxMemory(void) +{ + fprintf(stdout, "MMMMM-----MAX MEMORY-----MMMMM = %ld\n", maxMemory); +} diff --git a/converter/ppm/ppmtompeg/mfwddct.c b/converter/ppm/ppmtompeg/mfwddct.c new file mode 100644 index 00000000..4643bf25 --- /dev/null +++ b/converter/ppm/ppmtompeg/mfwddct.c @@ -0,0 +1,393 @@ + +/* + * mfwddct.c (derived from jfwddct.c, which carries the following info) + * + * Copyright (C) 1991, 1992, 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 the basic DCT (Discrete Cosine Transform) transformation + * subroutine. + * + * This implementation is based on Appendix A.2 of the book "Discrete Cosine + * Transform---Algorithms, Advantages, Applications" by K.R. Rao and P. Yip + * (Academic Press, Inc, London, 1990). It uses scaled fixed-point arithmetic + * instead of floating point. + */ + +#include "all.h" + +#include "dct.h" +#include "mtypes.h" +#include "opts.h" + +/* + * The poop on this scaling stuff is as follows: + * + * We have to do addition and subtraction of the integer inputs, which is no + * problem, and multiplication by fractional constants, which is a problem to + * do in integer arithmetic. We multiply all the constants by DCT_SCALE and + * convert them to integer constants (thus retaining LG2_DCT_SCALE bits of + * precision in the constants). After doing a multiplication we have to + * divide the product by DCT_SCALE, with proper rounding, to produce the + * correct output. The division can be implemented cheaply as a right shift + * of LG2_DCT_SCALE bits. The DCT equations also specify an additional + * division by 2 on the final outputs; this can be folded into the + * right-shift by shifting one more bit (see UNFIXH). + * + * If you are planning to recode this in assembler, you might want to set + * LG2_DCT_SCALE to 15. This loses a bit of precision, but then all the + * multiplications are between 16-bit quantities (given 8-bit JSAMPLEs!) so + * you could use a signed 16x16=>32 bit multiply instruction instead of full + * 32x32 multiply. Unfortunately there's no way to describe such a multiply + * portably in C, so we've gone for the extra bit of accuracy here. + */ + +#define EIGHT_BIT_SAMPLES +#ifdef EIGHT_BIT_SAMPLES +#define LG2_DCT_SCALE 16 +#else +#define LG2_DCT_SCALE 15 /* lose a little precision to avoid overflow */ +#endif + +#define ONE ((int32) 1) + +#define DCT_SCALE (ONE << LG2_DCT_SCALE) + +/* In some places we shift the inputs left by a couple more bits, */ +/* so that they can be added to fractional results without too much */ +/* loss of precision. */ +#define LG2_OVERSCALE 2 +#define OVERSCALE (ONE << LG2_OVERSCALE) +#define OVERSHIFT(x) ((x) <<= LG2_OVERSCALE) + +/* Scale a fractional constant by DCT_SCALE */ +#define FIX(x) ((int32) ((x) * DCT_SCALE + 0.5)) + +/* Scale a fractional constant by DCT_SCALE/OVERSCALE */ +/* Such a constant can be multiplied with an overscaled input */ +/* to produce something that's scaled by DCT_SCALE */ +#define FIXO(x) ((int32) ((x) * DCT_SCALE / OVERSCALE + 0.5)) + +/* Descale and correctly round a value that's scaled by DCT_SCALE */ +#define UNFIX(x) RIGHT_SHIFT((x) + (ONE << (LG2_DCT_SCALE-1)), LG2_DCT_SCALE) + +/* Same with an additional division by 2, ie, correctly rounded UNFIX(x/2) */ +#define UNFIXH(x) RIGHT_SHIFT((x) + (ONE << LG2_DCT_SCALE), LG2_DCT_SCALE+1) + +/* Take a value scaled by DCT_SCALE and round to integer scaled by OVERSCALE */ +#define UNFIXO(x) RIGHT_SHIFT((x) + (ONE << (LG2_DCT_SCALE-1-LG2_OVERSCALE)),\ + LG2_DCT_SCALE-LG2_OVERSCALE) + +/* Here are the constants we need */ +/* SIN_i_j is sine of i*pi/j, scaled by DCT_SCALE */ +/* COS_i_j is cosine of i*pi/j, scaled by DCT_SCALE */ + +#define SIN_1_4 FIX(0.707106781) +#define COS_1_4 SIN_1_4 + +#define SIN_1_8 FIX(0.382683432) +#define COS_1_8 FIX(0.923879533) +#define SIN_3_8 COS_1_8 +#define COS_3_8 SIN_1_8 + +#define SIN_1_16 FIX(0.195090322) +#define COS_1_16 FIX(0.980785280) +#define SIN_7_16 COS_1_16 +#define COS_7_16 SIN_1_16 + +#define SIN_3_16 FIX(0.555570233) +#define COS_3_16 FIX(0.831469612) +#define SIN_5_16 COS_3_16 +#define COS_5_16 SIN_3_16 + +/* OSIN_i_j is sine of i*pi/j, scaled by DCT_SCALE/OVERSCALE */ +/* OCOS_i_j is cosine of i*pi/j, scaled by DCT_SCALE/OVERSCALE */ + +#define OSIN_1_4 FIXO(0.707106781) +#define OCOS_1_4 OSIN_1_4 + +#define OSIN_1_8 FIXO(0.382683432) +#define OCOS_1_8 FIXO(0.923879533) +#define OSIN_3_8 OCOS_1_8 +#define OCOS_3_8 OSIN_1_8 + +#define OSIN_1_16 FIXO(0.195090322) +#define OCOS_1_16 FIXO(0.980785280) +#define OSIN_7_16 OCOS_1_16 +#define OCOS_7_16 OSIN_1_16 + +#define OSIN_3_16 FIXO(0.555570233) +#define OCOS_3_16 FIXO(0.831469612) +#define OSIN_5_16 OCOS_3_16 +#define OCOS_5_16 OSIN_3_16 + +/* Prototypes */ +void reference_fwd_dct _ANSI_ARGS_((Block block, Block dest)); +void mp_fwd_dct_fast _ANSI_ARGS_((Block data2d, Block dest2d)); +void init_fdct _ANSI_ARGS_((void)); + +/* + * -------------------------------------------------------------- + * + * mp_fwd_dct_block2 -- + * + * Select the appropriate mp_fwd_dct routine + * + * Results: None + * + * Side effects: None + * + * -------------------------------------------------------------- + */ +extern boolean pureDCT; +void +mp_fwd_dct_block2(data, dest) + Block data, dest; +{ + if (pureDCT) reference_fwd_dct(data, dest); + else mp_fwd_dct_fast(data, dest); +} + +/* + * -------------------------------------------------------------- + * + * mp_fwd_dct_fast -- + * + * Perform the forward DCT on one block of samples. + * + * A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT on each + * column. + * + * Results: None + * + * Side effects: Overwrites the input data + * + * -------------------------------------------------------------- + */ + +void +mp_fwd_dct_fast(data2d, dest2d) + Block data2d, dest2d; +{ + int16 *data = (int16 *) data2d; /* this algorithm wants + * a 1-d array */ + int16 *dest = (int16 *) dest2d; + int pass, rowctr; + register int16 *inptr, *outptr; + int16 workspace[DCTSIZE_SQ]; + SHIFT_TEMPS + +#ifdef ndef + { + int y; + + printf("fwd_dct (beforehand):\n"); + for (y = 0; y < 8; y++) + printf("%4d %4d %4d %4d %4d %4d %4d %4d\n", + data2d[y][0], data2d[y][1], + data2d[y][2], data2d[y][3], + data2d[y][4], data2d[y][5], + data2d[y][6], data2d[y][7]); + } +#endif + + /* + * Each iteration of the inner loop performs one 8-point 1-D DCT. It + * reads from a *row* of the input matrix and stores into a *column* + * of the output matrix. In the first pass, we read from the data[] + * array and store into the local workspace[]. In the second pass, + * we read from the workspace[] array and store into data[], thus + * performing the equivalent of a columnar DCT pass with no variable + * array indexing. + */ + + inptr = data; /* initialize pointers for first pass */ + outptr = workspace; + for (pass = 1; pass >= 0; pass--) { + for (rowctr = DCTSIZE - 1; rowctr >= 0; rowctr--) { + /* + * many tmps have nonoverlapping lifetime -- flashy + * register colorers should be able to do this lot + * very well + */ + int32 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + int32 tmp10, tmp11, tmp12, tmp13; + int32 tmp14, tmp15, tmp16, tmp17; + int32 tmp25, tmp26; + /* SHIFT_TEMPS */ + + /* temp0 through tmp7: -512 to +512 */ + /* if I-block, then -256 to +256 */ + tmp0 = inptr[7] + inptr[0]; + tmp1 = inptr[6] + inptr[1]; + tmp2 = inptr[5] + inptr[2]; + tmp3 = inptr[4] + inptr[3]; + tmp4 = inptr[3] - inptr[4]; + tmp5 = inptr[2] - inptr[5]; + tmp6 = inptr[1] - inptr[6]; + tmp7 = inptr[0] - inptr[7]; + + /* tmp10 through tmp13: -1024 to +1024 */ + /* if I-block, then -512 to +512 */ + tmp10 = tmp3 + tmp0; + tmp11 = tmp2 + tmp1; + tmp12 = tmp1 - tmp2; + tmp13 = tmp0 - tmp3; + + outptr[0] = (int16) UNFIXH((tmp10 + tmp11) * SIN_1_4); + outptr[DCTSIZE * 4] = (int16) UNFIXH((tmp10 - tmp11) * COS_1_4); + + outptr[DCTSIZE * 2] = (int16) UNFIXH(tmp13 * COS_1_8 + tmp12 * SIN_1_8); + outptr[DCTSIZE * 6] = (int16) UNFIXH(tmp13 * SIN_1_8 - tmp12 * COS_1_8); + + tmp16 = UNFIXO((tmp6 + tmp5) * SIN_1_4); + tmp15 = UNFIXO((tmp6 - tmp5) * COS_1_4); + + OVERSHIFT(tmp4); + OVERSHIFT(tmp7); + + /* + * tmp4, tmp7, tmp15, tmp16 are overscaled by + * OVERSCALE + */ + + tmp14 = tmp4 + tmp15; + tmp25 = tmp4 - tmp15; + tmp26 = tmp7 - tmp16; + tmp17 = tmp7 + tmp16; + + outptr[DCTSIZE] = (int16) UNFIXH(tmp17 * OCOS_1_16 + tmp14 * OSIN_1_16); + outptr[DCTSIZE * 7] = (int16) UNFIXH(tmp17 * OCOS_7_16 - tmp14 * OSIN_7_16); + outptr[DCTSIZE * 5] = (int16) UNFIXH(tmp26 * OCOS_5_16 + tmp25 * OSIN_5_16); + outptr[DCTSIZE * 3] = (int16) UNFIXH(tmp26 * OCOS_3_16 - tmp25 * OSIN_3_16); + + inptr += DCTSIZE; /* advance inptr to next row */ + outptr++; /* advance outptr to next column */ + } + /* end of pass; in case it was pass 1, set up for pass 2 */ + inptr = workspace; + outptr = dest; + } +#ifdef ndef + { + int y; + + printf("fwd_dct (afterward):\n"); + for (y = 0; y < 8; y++) + printf("%4d %4d %4d %4d %4d %4d %4d %4d\n", + dest2d[y][0], dest2d[y][1], + dest2d[y][2], dest2d[y][3], + dest2d[y][4], dest2d[y][5], + dest2d[y][6], dest2d[y][7]); + } +#endif +} + + +/* Modifies from the MPEG2 verification coder */ +/* fdctref.c, forward discrete cosine transform, double precision */ + +/* Copyright (C) 1994, MPEG Software Simulation Group. All Rights Reserved. */ + +/* + * Disclaimer of Warranty + * + * These software programs are available to the user without any license fee or + * royalty on an "as is" basis. The MPEG Software Simulation Group disclaims + * any and all warranties, whether express, implied, or statuary, including any + * implied warranties or merchantability or of fitness for a particular + * purpose. In no event shall the copyright-holder be liable for any + * incidental, punitive, or consequential damages of any kind whatsoever + * arising from the use of these programs. + * + * This disclaimer of warranty extends to the user of these programs and user's + * customers, employees, agents, transferees, successors, and assigns. + * + * The MPEG Software Simulation Group does not represent or warrant that the + * programs furnished hereunder are free of infringement of any third-party + * patents. + * + * Commercial implementations of MPEG-1 and MPEG-2 video, including shareware, + * are subject to royalty fees to patent holders. Many of these patents are + * general enough such that they are unavoidable regardless of implementation + * design. + * + */ + +#ifndef PI +#ifdef M_PI +#define PI M_PI +#else +#define PI 3.14159265358979323846 +#endif +#endif + +/* private data */ +static double trans_coef[8][8]; /* transform coefficients */ + +void init_fdct() +{ + int i, j; + double s; + + for (i=0; i<8; i++) + { + s = (i==0) ? sqrt(0.125) : 0.5; + + for (j=0; j<8; j++) + trans_coef[i][j] = s * cos((PI/8.0)*i*(j+0.5)); + } +} + +void reference_fwd_dct(block, dest) +Block block, dest; +{ + int i, j, k; + double s; + double tmp[64]; + + if (DoLaplace) { + LaplaceNum++; + } + + for (i=0; i<8; i++) + for (j=0; j<8; j++) + { + s = 0.0; + + for (k=0; k<8; k++) + s += trans_coef[j][k] * block[i][k]; + + tmp[8*i+j] = s; + } + + for (i=0; i<8; i++) + for (j=0; j<8; j++) + { + s = 0.0; + + for (k=0; k<8; k++) + s += trans_coef[i][k] * tmp[8*k+j]; + + if (collect_quant) { + fprintf(collect_quant_fp, "%d %f\n", 8*i+j, s); + } + if (DoLaplace) { + L1[LaplaceCnum][i*8+j] += s*s; + L2[LaplaceCnum][i*8+j] += s; + } + + + dest[i][j] = (int)floor(s+0.499999); + /* + * reason for adding 0.499999 instead of 0.5: + * s is quite often x.5 (at least for i and/or j = 0 or 4) + * and setting the rounding threshold exactly to 0.5 leads to an + * extremely high arithmetic implementation dependency of the result; + * s being between x.5 and x.500001 (which is now incorrectly rounded + * downwards instead of upwards) is assumed to occur less often + * (if at all) + */ + } +} diff --git a/converter/ppm/ppmtompeg/mheaders.c b/converter/ppm/ppmtompeg/mheaders.c new file mode 100644 index 00000000..2a5f2c63 --- /dev/null +++ b/converter/ppm/ppmtompeg/mheaders.c @@ -0,0 +1,1192 @@ +/*===========================================================================* + * mheaders.c * + * * + * Procedures to generate MPEG headers * + * * + * EXPORTED PROCEDURES: * + * Mhead_GenPictureHeader * + * Mhead_GenSequenceHeader * + * Mhead_GenSequenceEnder * + * Mhead_GenGOPHeader * + * Mhead_GenSliceHeader * + * Mhead_GenSliceEnder * + * Mhead_GenMBHeader * + * * + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/* + * $Header: /n/picasso/project/mpeg/mpeg_dist/mpeg_encode/RCS/mheaders.c,v 1.15 1995/08/07 21:45:19 smoot Exp $ + * $Log: mheaders.c,v $ + * Revision 1.15 1995/08/07 21:45:19 smoot + * check for illegal MVs (shouldnt ever be called, but....) + * fix bug which made us not weite Iframe Qscale changes + * warns if writing a size=0 mpeg + * + * Revision 1.14 1995/05/22 20:53:35 smoot + * corrected bit_rate value in constrained params flag + * + * Revision 1.13 1995/05/02 01:50:38 eyhung + * made VidRateNum un-static + * + * Revision 1.12 1995/03/27 19:28:23 smoot + * auto-determines Qscale changes (was mb_quant) + * + * Revision 1.11 1995/02/16 09:12:39 eyhung + * fixed compile bug with HP7xx + * + * Revision 1.10 1995/01/25 22:53:50 smoot + * Better buf_size checking, and actually check constrained params + * + * Revision 1.9 1995/01/19 23:08:47 eyhung + * Changed copyrights + * + * Revision 1.8 1995/01/16 08:45:10 eyhung + * BLEAH'ed hsize and vsize + * + * Revision 1.7 1994/12/09 22:27:17 smoot + * Fixed buffer size in stream + * + * Revision 1.6 1994/11/12 02:11:54 keving + * nothing + * + * Revision 1.5 1994/03/15 00:27:11 keving + * nothing + * + * Revision 1.4 1993/12/22 19:19:01 keving + * nothing + * + * Revision 1.3 1993/07/22 22:23:43 keving + * nothing + * + * Revision 1.2 1993/06/30 20:06:09 keving + * nothing + * + * Revision 1.1 1993/06/03 21:08:08 keving + * nothing + * + * Revision 1.6 1993/03/01 23:03:40 keving + * nothing + * + * Revision 1.5 1993/02/17 23:18:20 dwallach + * checkin prior to keving's joining the project + * + * Revision 1.4 1993/01/18 10:20:02 dwallach + * *** empty log message *** + * + * Revision 1.3 1993/01/18 10:17:29 dwallach + * RCS headers installed, code indented uniformly + * + * Revision 1.3 1993/01/18 10:17:29 dwallach + * RCS headers installed, code indented uniformly + * + */ + + +/*==============* + * HEADER FILES * + *==============*/ + +#include "all.h" +#include "bitio.h" +#include "frames.h" +#include "mheaders.h" + + +/*==================* + * STATIC VARIABLES * + *==================*/ + +static int gopStartFrame = 0; +static int lastGOPStart = 0; +static int lastQSSet; + +static uint32 mbAddrIncrTable[][2] = { + {0x0, 0}, + {0x1, 1}, + {0x3, 3}, + {0x2, 3}, + {0x3, 4}, + {0x2, 4}, + {0x3, 5}, + {0x2, 5}, + {0x7, 7}, + {0x6, 7}, + {0xb, 8}, + {0xa, 8}, + {0x9, 8}, + {0x8, 8}, + {0x7, 8}, + {0x6, 8}, + {0x17, 10}, + {0x16, 10}, + {0x15, 10}, + {0x14, 10}, + {0x13, 10}, + {0x12, 10}, + {0x23, 11}, + {0x22, 11}, + {0x21, 11}, + {0x20, 11}, + {0x1f, 11}, + {0x1e, 11}, + {0x1d, 11}, + {0x1c, 11}, + {0x1b, 11}, + {0x1a, 11}, + {0x19, 11}, + {0x18, 11}}; + +static uint32 mbMotionVectorTable[][2] = { + {0x19, 11}, + {0x1b, 11}, + {0x1d, 11}, + {0x1f, 11}, + {0x21, 11}, + {0x23, 11}, + {0x13, 10}, + {0x15, 10}, + {0x17, 10}, + {0x7, 8}, + {0x9, 8}, + {0xb, 8}, + {0x7, 7}, + {0x3, 5}, + {0x3, 4}, + {0x3, 3}, + {0x1, 1}, + {0x2, 3}, + {0x2, 4}, + {0x2, 5}, + {0x6, 7}, + {0xa, 8}, + {0x8, 8}, + {0x6, 8}, + {0x16, 10}, + {0x14, 10}, + {0x12, 10}, + {0x22, 11}, + {0x20, 11}, + {0x1e, 11}, + {0x1c, 11}, + {0x1a, 11}, + {0x18, 11}}; + +static uint32 mbPatTable[][2] = { + {0x0, 0}, + {0xb, 5}, + {0x9, 5}, + {0xd, 6}, + {0xd, 4}, + {0x17, 7}, + {0x13, 7}, + {0x1f, 8}, + {0xc, 4}, + {0x16, 7}, + {0x12, 7}, + {0x1e, 8}, + {0x13, 5}, + {0x1b, 8}, + {0x17, 8}, + {0x13, 8}, + {0xb, 4}, + {0x15, 7}, + {0x11, 7}, + {0x1d, 8}, + {0x11, 5}, + {0x19, 8}, + {0x15, 8}, + {0x11, 8}, + {0xf, 6}, + {0xf, 8}, + {0xd, 8}, + {0x3, 9}, + {0xf, 5}, + {0xb, 8}, + {0x7, 8}, + {0x7, 9}, + {0xa, 4}, + {0x14, 7}, + {0x10, 7}, + {0x1c, 8}, + {0xe, 6}, + {0xe, 8}, + {0xc, 8}, + {0x2, 9}, + {0x10, 5}, + {0x18, 8}, + {0x14, 8}, + {0x10, 8}, + {0xe, 5}, + {0xa, 8}, + {0x6, 8}, + {0x6, 9}, + {0x12, 5}, + {0x1a, 8}, + {0x16, 8}, + {0x12, 8}, + {0xd, 5}, + {0x9, 8}, + {0x5, 8}, + {0x5, 9}, + {0xc, 5}, + {0x8, 8}, + {0x4, 8}, + {0x4, 9}, + {0x7, 3}, + {0xa, 5}, /* grrr... 61, 62, 63 added - Kevin */ + {0x8, 5}, + {0xc, 6} +}; + +/*===========* + * CONSTANTS * + *===========*/ + +#define SEQ_HEAD_CODE 0x000001b3 +#define EXT_START_CODE 0x000001b5 +#define USER_START_CODE 0x000001b2 +#define GOP_START_CODE 0x000001b8 +#define PICT_START_CODE 0x00000100 +#define SLICE_BASE_CODE 0x00000100 + +#define SEQ_END_CODE 0x000001b7 + +/* not static anymore because information is used for computing frame rate + * and for statistics */ +const double VidRateNum[9]={1.0, 23.976, 24.0, 25.0, 29.97, 30.0, + 50.0 ,59.94, 60.0}; + + +/*===============================* + * INTERNAL PROCEDURE prototypes * + *===============================*/ + +static void GenMBAddrIncr _ANSI_ARGS_((BitBucket *bb, uint32 addr_incr)); +static void GenPictHead _ANSI_ARGS_((BitBucket *bb, uint32 temp_ref, + uint32 code_type, uint32 vbv_delay, + int32 full_pel_forw_flag, uint32 forw_f_code, + int32 full_pel_back_flag, uint32 back_f_code, + uint8 *extra_info, uint32 extra_info_size, + uint8 *ext_data, uint32 ext_data_size, + uint8 *user_data, uint32 user_data_size)); +static void GenMBType _ANSI_ARGS_((BitBucket *bb, uint32 pict_code_type, + uint32 mb_quant, uint32 motion_forw, uint32 motion_back, + uint32 mb_pattern, uint32 mb_intra)); +static void GenMotionCode _ANSI_ARGS_((BitBucket *bb, int32 vector)); +static void GenBlockPattern _ANSI_ARGS_((BitBucket *bb, + uint32 mb_pattern)); + + +/*=====================* + * EXPORTED PROCEDURES * + *=====================*/ + + +/*===========================================================================* + * + * SetGOPStartTime + * + * sets the start frame of the GOP; to be used with GenPictureHeader + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void +SetGOPStartTime(index) + int index; +{ + lastGOPStart = gopStartFrame; + gopStartFrame = index; +} + + +/*===========================================================================* + * + * Mhead_GenPictureHeader + * + * generate picture header with given frame type and picture count + * append result to the specified bitstream + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void +Mhead_GenPictureHeader(bbPtr, frameType, pictCount, f_code) + BitBucket *bbPtr; + int frameType; + int pictCount; + int f_code; +{ + int temporalRef; + + if ( pictCount >= gopStartFrame ) { + temporalRef = (pictCount-gopStartFrame); + } else { + temporalRef = (pictCount-lastGOPStart); + } + temporalRef = (temporalRef % 1024); + + DBG_PRINT(("Picture Header\n")); + GenPictHead(bbPtr, temporalRef, frameType, + 0 /* vbv_delay */, + pixelFullSearch /* full_pel_forw_flag */, + f_code /* forw_f_code */, + pixelFullSearch /* full_pel_back_flag */, + f_code /* back_f_code */, + NULL, 0, NULL, 0, NULL, 0); +} + + +/*===========================================================================* + * + * Mhead_GenSequenceHeader + * + * generate sequence header with given attributes + * append result to the specified bitstream + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void +Mhead_GenSequenceHeader(BitBucket * const bbPtr, + uint32 const hsize, + uint32 const vsize, + int32 const pratio, + int32 const pict_rate, + int32 const bit_rate_arg, + int32 const buf_size_arg, + int32 const c_param_flag_arg, + const int32 * const iq_matrix, + const int32 * const niq_matrix, + uint8 * const ext_data, + int32 const ext_data_size, + uint8 * const user_data, + int32 const user_data_size) { + + extern int ZAG[]; + int i; + int32 bit_rate; + int32 buf_size; + int32 c_param_flag; + + /* Write seq start code. */ + + Bitio_Write(bbPtr, SEQ_HEAD_CODE, 32); + + /* Write horiz. and vert. sizes. */ + +#ifdef BLEAH + fprintf(stdout, "hsize, vsize = %d, %d\n", hsize, vsize); +#endif + + if (hsize==0 || vsize==0) { + fprintf(stderr, "Writing zero size to stream!\n"); + } + Bitio_Write(bbPtr, hsize, 12); + Bitio_Write(bbPtr, vsize, 12); + + /* Write pixel aspect ratio, negative values default to 1. */ + + if (pratio < 0) { + fprintf(stderr, "PROGRAMMER ERROR: pratio = %d\n", pratio); + exit(1); + } + Bitio_Write(bbPtr, pratio, 4); + + /* Wrtie picture rate, negative values default to 30 fps. */ + + if (pict_rate < 0) { + fprintf(stderr, "PROGRAMMER ERROR: pict_rate = %d\n", pict_rate); + exit(1); + } + Bitio_Write(bbPtr, pict_rate, 4); + + /* Write bit rate, negative values default to variable. */ + + if (bit_rate_arg < 0) { + bit_rate = -1; + } else { + bit_rate = bit_rate_arg / 400; + } + + Bitio_Write(bbPtr, bit_rate, 18); + + /* Marker bit. */ + Bitio_Write(bbPtr, 0x1, 1); + + /* Write VBV buffer size. Negative values default to zero. */ + if (buf_size_arg < 0) + buf_size = 0; + else + buf_size = buf_size_arg; + + buf_size = (buf_size + (16*1024 - 1)) / (16*1024); + if (buf_size>=0x400) + buf_size=0x3ff; + Bitio_Write(bbPtr, buf_size, 10); + + /* Write constrained parameter flag. */ + { + int num_mb = ((hsize+15)/16) * ((vsize+15)/16); + /* At present we cheat on buffer size */ + c_param_flag = ((bit_rate <= 4640) && + (bit_rate >0) && + (buf_size <= 20) && + (pict_rate >= 1) && + (pict_rate <= 5) && + (hsize <= 768) && + (vsize <= 576) && + (num_mb <= 396) && + (num_mb*VidRateNum[pict_rate] <= 9900) && + (fCodeP<=4) && + (fCodeB<=4)); + } + + if (c_param_flag) { + Bitio_Write(bbPtr, 0x01, 1); + } else { + Bitio_Write(bbPtr, 0x00, 1); + } + + /* Write intra quant matrix if present. */ + + if (iq_matrix != NULL) { + Bitio_Write(bbPtr, 0x01, 1); + for (i = 0; i < 64; i++) { + Bitio_Write(bbPtr, iq_matrix[ZAG[i]], 8); + } + } else { + Bitio_Write(bbPtr, 0x00, 1); + } + + /* Write non intra quant matrix if present. */ + + if (niq_matrix != NULL) { + Bitio_Write(bbPtr, 0x01, 1); + for (i = 0; i < 64; i++) { + Bitio_Write(bbPtr, niq_matrix[ZAG[i]], 8); + } + } else { + Bitio_Write(bbPtr, 0x00, 1); + } + + /* next start code */ + Bitio_BytePad(bbPtr); + + + /* Write ext data if present. */ + + if (ext_data != NULL) { + Bitio_Write(bbPtr, EXT_START_CODE, 32); + + for (i = 0; i < ext_data_size; i++) { + Bitio_Write(bbPtr, ext_data[i], 8); + } + Bitio_BytePad(bbPtr); + } + /* Write user data if present. */ + if ((user_data != NULL) && (user_data_size != 0)) { + Bitio_Write(bbPtr, USER_START_CODE, 32); + + for (i = 0; i < user_data_size; i++) { + Bitio_Write(bbPtr, user_data[i], 8); + } + Bitio_BytePad(bbPtr); + } +} + + +/*===========================================================================* + * + * Mhead_GenSequenceEnder + * + * generate sequence ender + * append result to the specified bitstream + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void +Mhead_GenSequenceEnder(bbPtr) + BitBucket *bbPtr; +{ + Bitio_Write(bbPtr, SEQ_END_CODE, 32); +} + + +/*===========================================================================* + * + * Mhead_GenGOPHeader + * + * generate GOP header with specified attributes + * append result to the specified bitstream + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void +Mhead_GenGOPHeader(bbPtr, drop_frame_flag, tc_hrs, tc_min, tc_sec, tc_pict, + closed_gop, broken_link, ext_data, ext_data_size, + user_data, user_data_size) + BitBucket *bbPtr; + int32 drop_frame_flag; + int32 tc_hrs; + int32 tc_min; + int32 tc_sec; + int32 tc_pict; + int32 closed_gop; + int32 broken_link; + uint8 *ext_data; + int32 ext_data_size; + uint8 *user_data; + int32 user_data_size; +{ + int i; + + /* Write gop start code. */ + Bitio_Write(bbPtr, GOP_START_CODE, 32); + + /* Construct and write timecode. */ + + /* Drop frame flag. */ + if (drop_frame_flag) { + Bitio_Write(bbPtr, 0x01, 1); + } else { + Bitio_Write(bbPtr, 0x00, 1); + } + + /* Time code hours. */ + Bitio_Write(bbPtr, tc_hrs, 5); + + /* Time code minutes. */ + Bitio_Write(bbPtr, tc_min, 6); + + /* Marker bit. */ + Bitio_Write(bbPtr, 0x01, 1); + + /* Time code seconds. */ + Bitio_Write(bbPtr, tc_sec, 6); + + /* Time code pictures. */ + Bitio_Write(bbPtr, tc_pict, 6); + + + /* Closed gop flag. */ + if (closed_gop) { + Bitio_Write(bbPtr, 0x01, 1); + } else { + Bitio_Write(bbPtr, 0x00, 1); + } + + /* Broken link flag. */ + if (broken_link) { + Bitio_Write(bbPtr, 0x01, 1); + } else { + Bitio_Write(bbPtr, 0x00, 1); + } + + /* next start code */ + Bitio_BytePad(bbPtr); + + /* Write ext data if present. */ + + if (ext_data != NULL) { + Bitio_Write(bbPtr, EXT_START_CODE, 32); + + for (i = 0; i < ext_data_size; i++) { + Bitio_Write(bbPtr, ext_data[i], 8); + } + Bitio_BytePad(bbPtr); + } + /* Write user data if present. */ + if (user_data != NULL) { + Bitio_Write(bbPtr, USER_START_CODE, 32); + + for (i = 0; i < user_data_size; i++) { + Bitio_Write(bbPtr, user_data[i], 8); + } + Bitio_BytePad(bbPtr); + } +} + + +/*===========================================================================* + * + * Mhead_GenSliceHeader + * + * generate slice header with specified attributes + * append result to the specified bitstream + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void +Mhead_GenSliceHeader(bbPtr, verticalPos, qscale, extra_info, extra_info_size) + BitBucket *bbPtr; + uint32 verticalPos; + uint32 qscale; + uint8 *extra_info; + uint32 extra_info_size; +{ + int i; + + /* Write slice start code. */ + Bitio_Write(bbPtr, (SLICE_BASE_CODE + verticalPos), 32); + + /* Quant. scale. */ + Bitio_Write(bbPtr, qscale, 5); + lastQSSet = qscale; + + /* Extra bit slice info. */ + + if (extra_info != NULL) { + for (i = 0; i < extra_info_size; i++) { + Bitio_Write(bbPtr, 0x01, 1); + Bitio_Write(bbPtr, extra_info[i], 8); + } + } + + /* extra_bit_slice */ + Bitio_Write(bbPtr, 0x00, 1); +} + + +/*===========================================================================* + * + * Mhead_GenSliceEnder + * + * generate slice ender + * append result to the specified bitstream + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void +Mhead_GenSliceEnder(bbPtr) + BitBucket *bbPtr; +{ + Bitio_BytePad(bbPtr); +} + + +/*===========================================================================* + * + * Mhead_GenMBHeader + * + * generate macroblock header with given attributes + * append result to the specified bitstream + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void +Mhead_GenMBHeader(bbPtr, pict_code_type, addr_incr, q_scale, + forw_f_code, back_f_code, horiz_forw_r, vert_forw_r, + horiz_back_r, vert_back_r, motion_forw, m_horiz_forw, + m_vert_forw, motion_back, m_horiz_back, m_vert_back, + mb_pattern, mb_intra) + BitBucket *bbPtr; + uint32 pict_code_type; + uint32 addr_incr; + uint32 q_scale; + uint32 forw_f_code; + uint32 back_f_code; + uint32 horiz_forw_r; + uint32 vert_forw_r; + uint32 horiz_back_r; + uint32 vert_back_r; + int32 motion_forw; + int32 m_horiz_forw; + int32 m_vert_forw; + int32 motion_back; + int32 m_horiz_back; + int32 m_vert_back; + uint32 mb_pattern; + uint32 mb_intra; +{ + uint32 mb_quant; + + /* MB escape sequences if necessary. */ + +#ifdef BLEAH +if ( addr_incr != 1 ) + fprintf(stdout, "Creating MB_INCR: %d\n", addr_incr); +#endif + + while (addr_incr > 33) { + Bitio_Write(bbPtr, 0x008, 11); + addr_incr -= 33; + } + + /* Generate addr incr code. */ + GenMBAddrIncr(bbPtr, addr_incr); + + /* Determine mb_quant (true if change in q scale) */ + if ((q_scale != lastQSSet) && ((mb_pattern != 0) || (mb_intra == TRUE))) { + mb_quant = TRUE; + lastQSSet = q_scale; + } else { + mb_quant = FALSE; + } + + /* Generate mb type code. */ + GenMBType(bbPtr, pict_code_type, mb_quant, motion_forw, motion_back, mb_pattern, mb_intra); + + /* MB quant. */ + if (mb_quant) { + Bitio_Write(bbPtr, q_scale, 5); + } + /* Forward predictive vector stuff. */ + + if (motion_forw) { + int forw_f, forw_r_size; + + forw_r_size = forw_f_code - 1; + forw_f = 1 << forw_r_size; /* 1 > 0 */ + if ((m_horiz_forw > 16*forw_f-1) || (m_horiz_forw < -16*forw_f)) { + fprintf(stderr, "Illegal motion? %d %d\n", m_horiz_forw, 16*forw_f); + } + if ((m_vert_forw > 16*forw_f-1) || (m_vert_forw < -16*forw_f)) { + fprintf(stderr, "Illegal motion? %d %d\n", m_vert_forw, 16*forw_f); + } + GenMotionCode(bbPtr, m_horiz_forw); + + if ((forw_f != 1) && (m_horiz_forw != 0)) { + Bitio_Write(bbPtr, horiz_forw_r, forw_r_size); + } + GenMotionCode(bbPtr, m_vert_forw); + + if ((forw_f != 1) && (m_vert_forw != 0)) { + Bitio_Write(bbPtr, vert_forw_r, forw_r_size); + } + } + /* Back predicted vector stuff. */ + + if (motion_back) { + int back_f, back_r_size; + + back_r_size = back_f_code - 1; + back_f = 1 << back_r_size; /* 1 > 0 */ + + if ((m_horiz_back > 16*back_f-1) || (m_horiz_back < -16*back_f)) { + fprintf(stderr, "Illegal motion? %d %d\n", m_horiz_back, 16*back_f); + } + if ((m_vert_back > 16*back_f-1) || (m_vert_back < -16*back_f)) { + fprintf(stderr, "Illegal motion? %d %d\n", m_vert_back, 16*back_f); + } + + GenMotionCode(bbPtr, m_horiz_back); + + if ((back_f != 1) && (m_horiz_back != 0)) { + Bitio_Write(bbPtr, horiz_back_r, back_r_size); + } + GenMotionCode(bbPtr, m_vert_back); + + if ((back_f != 1) && (m_vert_back != 0)) { + Bitio_Write(bbPtr, vert_back_r, back_r_size); + } + } + /* MB pattern. */ + + if (mb_pattern) { + GenBlockPattern(bbPtr, mb_pattern); + } +} + + +/*=====================* + * INTERNAL PROCEDURES * + *=====================*/ + +/*===========================================================================* + * + * GenMBType + * + * generate macroblock type with given attributes + * append result to the specified bitstream + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +static void +GenMBType(bbPtr, pict_code_type, mb_quant, motion_forw, motion_back, + mb_pattern, mb_intra) + BitBucket *bbPtr; + uint32 pict_code_type; + uint32 mb_quant; + uint32 motion_forw; + uint32 motion_back; + uint32 mb_pattern; + uint32 mb_intra; +{ + int code; + + switch (pict_code_type) { + case 1: + if ((motion_forw != 0) || (motion_back != 0) || (mb_pattern != 0) || (mb_intra != 1)) { + perror("Illegal parameters for macroblock type."); + exit(-1); + } + if (mb_quant) { + Bitio_Write(bbPtr, 0x1, 2); + } else { + Bitio_Write(bbPtr, 0x1, 1); + } + break; + + case 2: + code = 0; + if (mb_quant) { + code += 16; + } + if (motion_forw) { + code += 8; + } + if (motion_back) { + code += 4; + } + if (mb_pattern) { + code += 2; + } + if (mb_intra) { + code += 1; + } + + switch (code) { + case 1: + Bitio_Write(bbPtr, 0x3, 5); + break; + case 2: + Bitio_Write(bbPtr, 0x1, 2); + break; + case 8: + Bitio_Write(bbPtr, 0x1, 3); + break; + case 10: + Bitio_Write(bbPtr, 0x1, 1); + break; + case 17: + Bitio_Write(bbPtr, 0x1, 6); + break; + case 18: + Bitio_Write(bbPtr, 0x1, 5); + break; + case 26: + Bitio_Write(bbPtr, 0x2, 5); + break; + default: + perror("Illegal parameters for macroblock type."); + exit(-1); + break; + } + break; + + case 3: + code = 0; + if (mb_quant) { + code += 16; + } + if (motion_forw) { + code += 8; + } + if (motion_back) { + code += 4; + } + if (mb_pattern) { + code += 2; + } + if (mb_intra) { + code += 1; + } + + switch (code) { + case 12: + Bitio_Write(bbPtr, 0x2, 2); + break; + case 14: + Bitio_Write(bbPtr, 0x3, 2); + break; + case 4: + Bitio_Write(bbPtr, 0x2, 3); + break; + case 6: + Bitio_Write(bbPtr, 0x3, 3); + break; + case 8: + Bitio_Write(bbPtr, 0x2, 4); + break; + case 10: + Bitio_Write(bbPtr, 0x3, 4); + break; + case 1: + Bitio_Write(bbPtr, 0x3, 5); + break; + case 30: + Bitio_Write(bbPtr, 0x2, 5); + break; + case 26: + Bitio_Write(bbPtr, 0x3, 6); + break; + case 22: + Bitio_Write(bbPtr, 0x2, 6); + break; + case 17: + Bitio_Write(bbPtr, 0x1, 6); + break; + default: + perror("Illegal parameters for macroblock type."); + exit(-1); + break; + } + break; + } +} + + +/*===========================================================================* + * + * GenMotionCode + * + * generate motion vector output with given value + * append result to the specified bitstream + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +static void +GenMotionCode(BitBucket * const bbPtr, + int32 const vector) { + + uint32 code, num; + + if ((vector < -16) || (vector > 16)) { + perror("Motion vector out of range."); + fprintf(stderr, "Motion vector out of range: vector = %d\n", vector); + exit(-1); + } + code = mbMotionVectorTable[vector + 16][0]; + num = mbMotionVectorTable[vector + 16][1]; + + Bitio_Write(bbPtr, code, num); +} + + +/*===========================================================================* + * + * GenBlockPattern + * + * generate macroblock pattern output + * append result to the specified bitstream + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +static void +GenBlockPattern(bbPtr, mb_pattern) + BitBucket *bbPtr; + uint32 mb_pattern; +{ + uint32 code, num; + + code = mbPatTable[mb_pattern][0]; + num = mbPatTable[mb_pattern][1]; + + Bitio_Write(bbPtr, code, num); +} + + +/*===========================================================================* + * + * GenMBAddrIncr + * + * generate macroblock address increment output + * append result to the specified bitstream + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +static void +GenMBAddrIncr(bbPtr, addr_incr) + BitBucket *bbPtr; + uint32 addr_incr; +{ + uint32 code; + uint32 num; + + code = mbAddrIncrTable[addr_incr][0]; + num = mbAddrIncrTable[addr_incr][1]; + + Bitio_Write(bbPtr, code, num); +} + + +/*===========================================================================* + * + * GenPictHead + * + * generate picture header with given attributes + * append result to the specified bitstream + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +static void +GenPictHead(bbPtr, temp_ref, code_type, vbv_delay, full_pel_forw_flag, + forw_f_code, full_pel_back_flag, back_f_code, extra_info, + extra_info_size, ext_data, ext_data_size, user_data, + user_data_size) + BitBucket *bbPtr; + uint32 temp_ref; + uint32 code_type; + uint32 vbv_delay; + int32 full_pel_forw_flag; + uint32 forw_f_code; + int32 full_pel_back_flag; + uint32 back_f_code; + uint8 *extra_info; + uint32 extra_info_size; + uint8 *ext_data; + uint32 ext_data_size; + uint8 *user_data; + uint32 user_data_size; +{ + int i; + + /* Write picture start code. */ + Bitio_Write(bbPtr, PICT_START_CODE, 32); + + /* Temp reference. */ + Bitio_Write(bbPtr, temp_ref, 10); + + /* Code_type. */ + if (code_type == 0) { + code_type = 1; + } + Bitio_Write(bbPtr, code_type, 3); + + /* vbv_delay. */ + vbv_delay = 0xffff; /* see page 36 (section 2.4.3.4) */ + Bitio_Write(bbPtr, vbv_delay, 16); + + if ((code_type == 2) || (code_type == 3)) { + + /* Full pel forw flag. */ + + if (full_pel_forw_flag) { + Bitio_Write(bbPtr, 0x01, 1); + } else { + Bitio_Write(bbPtr, 0x00, 1); + } + + /* Forw f code. */ + + Bitio_Write(bbPtr, forw_f_code, 3); + } + if (code_type == 3) { + + /* Full pel back flag. */ + + if (full_pel_back_flag) { + Bitio_Write(bbPtr, 0x01, 1); + } else { + Bitio_Write(bbPtr, 0x00, 1); + } + + /* Back f code. */ + + Bitio_Write(bbPtr, back_f_code, 3); + } + /* Extra bit picture info. */ + + if (extra_info != NULL) { + for (i = 0; i < extra_info_size; i++) { + Bitio_Write(bbPtr, 0x01, 1); + Bitio_Write(bbPtr, extra_info[i], 8); + } + } + Bitio_Write(bbPtr, 0x00, 1); + + /* next start code */ + Bitio_BytePad(bbPtr); + + /* Write ext data if present. */ + + if (ext_data != NULL) { + Bitio_Write(bbPtr, EXT_START_CODE, 32); + + for (i = 0; i < ext_data_size; i++) { + Bitio_Write(bbPtr, ext_data[i], 8); + } + Bitio_BytePad(bbPtr); + } + /* Write user data if present. */ + if (user_data != NULL) { + Bitio_Write(bbPtr, USER_START_CODE, 32); + + for (i = 0; i < user_data_size; i++) { + Bitio_Write(bbPtr, user_data[i], 8); + } + Bitio_BytePad(bbPtr); + } +} + + +#ifdef UNUSED_PROCEDURES + +/* GenMBEnd only used for `D` pictures. Shouldn't really ever be called. */ +/* - dwallach */ +void +GenMBEnd(bbPtr) + BitBucket *bbPtr; +{ + Bitio_Write(bbPtr, 0x01, 1); +} + +#endif /* UNUSED_PROCEDURES */ diff --git a/converter/ppm/ppmtompeg/moutput.c b/converter/ppm/ppmtompeg/moutput.c new file mode 100644 index 00000000..b682efab --- /dev/null +++ b/converter/ppm/ppmtompeg/moutput.c @@ -0,0 +1,442 @@ +/*===========================================================================* + * moutput.c * + * * + * Procedures concerned with quantization and RLE * + * * + * EXPORTED PROCEDURES: * + * mp_quant_zig_block * + * mp_rle_huff_block * + * mp_rle_huff_pblock * + * * + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/* + * $Header: /n/charlie-brown/project/mm/mpeg/mpeg_dist/mpeg_encode/RCS/moutput.c,v 1.12 1995/01/19 23:08:49 eyhung Exp $ + * $Log: moutput.c,v $ + * Revision 1.12 1995/01/19 23:08:49 eyhung + * Changed copyrights + * + * Revision 1.11 1993/07/22 22:23:43 keving + * nothing + * + * Revision 1.10 1993/06/30 20:06:09 keving + * nothing + * + * Revision 1.9 1993/06/03 21:08:08 keving + * nothing + * + * Revision 1.8 1993/02/24 18:57:19 keving + * nothing + * + * Revision 1.7 1993/02/23 22:58:36 keving + * nothing + * + * Revision 1.6 1993/02/23 22:54:56 keving + * nothing + * + * Revision 1.5 1993/02/17 23:18:20 dwallach + * checkin prior to keving's joining the project + * + * Revision 1.4 1993/01/18 10:20:02 dwallach + * *** empty log message *** + * + * Revision 1.3 1993/01/18 10:17:29 dwallach + * RCS headers installed, code indented uniformly + * + * Revision 1.3 1993/01/18 10:17:29 dwallach + * RCS headers installed, code indented uniformly + * + */ + + +/*==============* + * HEADER FILES * + *==============*/ + +#include "all.h" +#include "mtypes.h" +#include "mproto.h" +#include "huff.h" + + +/*==================* + * STATIC VARIABLES * + *==================*/ + +/* ZAG[i] is the natural-order position of the i'th element of zigzag order. */ +static int ZAG[] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63 +}; + + +/* + * possible optimization: reorder the qtable in the correct zigzag order, to + * reduce the number of necessary lookups + * + * this table comes from the MPEG draft, p. D-16, Fig. 2-D.15. + */ +static int qtable[] = +{ + 8, 16, 19, 22, 26, 27, 29, 34, + 16, 16, 22, 24, 27, 29, 34, 37, + 19, 22, 26, 27, 29, 34, 34, 38, + 22, 22, 26, 27, 29, 34, 37, 40, + 22, 26, 27, 29, 32, 35, 40, 48, + 26, 27, 29, 32, 35, 40, 48, 58, + 26, 27, 29, 34, 38, 46, 56, 69, + 27, 29, 35, 38, 46, 56, 69, 83}; + + +/*=====================* + * EXPORTED PROCEDURES * + *=====================*/ + + +void UnQuantZig(FlatBlock in, Block out, int qscale, boolean iblock) +{ + register int index; + int start; + int position; + register int qentry; + int level, coeff; + register int16 temp; + + if ( iblock ) + { + ((int16 *)out)[0] = (int16)(in[0]*qtable[0]); + + start = 1; + } + else + start = 0; + + for ( index = start; index < DCTSIZE_SQ; index++ ) + { + position = ZAG[index]; + + if (iblock) + qentry = qtable[position] * qscale; + else + qentry = 16 * qscale; + + level = in[index]; + coeff = (level * qentry) >> 3; + if (level < 0) { + coeff += (coeff & 1); + } else { + coeff -= (coeff & 1); + } + + ((int16 *)out)[position] = coeff; + } + +#ifdef BLEAH + for ( index = 0; index < 64; index++ ) + fprintf(stdout, "DCT[%d] = %d\n", index, + ((int16 *)out)[index]); +#endif +} + + +/* + * -------------------------------------------------------------- + * + * mp_quant_zig_block -- + * + * Quantizes and zigzags a block -- removing information + * + * Results: TRUE iff resulting 'out' is non-zero, FALSE if all + * zero + * + * Side effects: Modifies the out block. + * + * -------------------------------------------------------------- + */ +boolean mp_quant_zig_block(Block in, FlatBlock out, int qscale, int iblock) +{ + register int i; + register int y, x; + register int16 temp; + register int qentry; + int start; + boolean nonZero = FALSE; + + DBG_PRINT(("mp_quant_zig_block...\n")); + if (iblock) { + /* + * the DC coefficient is handled specially -- it's not + * sensitive to qscale, but everything else is + */ + temp = ((int16 *) in)[ZAG[0]]; + qentry = qtable[ZAG[0]]; + if (temp < 0) { + temp = -temp; + temp += qentry >> 1; + temp /= qentry; + temp = -temp; + } else { + temp += qentry >> 1; + temp /= qentry; + } + if ( temp != 0 ) + nonZero = TRUE; + out[0] = temp; + start = 1; + } else + start = 0; + + for (i = start; i < DCTSIZE_SQ; i++) { + x = ZAG[i] % 8; + y = ZAG[i] / 8; + temp = in[y][x]; + DBG_PRINT((" in[%d][%d] = %d; ", y, x, temp)); + + if (iblock) + qentry = qtable[ZAG[i]] * qscale; + else + qentry = 16 * qscale; + + DBG_PRINT(("quantized with %d = ", qentry)); + + if (temp < 0) { + temp = -temp; + temp *= 8; + temp += qentry >> 1; + temp /= qentry; + temp = -temp; + } else { + temp *= 8; + temp += qentry >> 1; + temp /= qentry; + } + if ( temp != 0 ) + nonZero = TRUE; + out[i] = temp; + DBG_PRINT(("%d\n", temp)); + } + + return nonZero; +} + + + +/* + * -------------------------------------------------------------- + * + * mp_rle_huff_block -- + * + * Given a FlatBlock, generates the Huffman bits + * + * Results: None. + * + * Side effects: Output bits changed + * + * -------------------------------------------------------------- + */ + +void mp_rle_huff_block(FlatBlock in, BitBucket *out) +{ + register int i; + register int nzeros = 0; + register int16 cur; + register int16 acur; + register uint32 code; + register int nbits; + + /* + * yes, Virginia, we start at 1. The DC coefficient is handled + * specially, elsewhere. Not here. + */ + for (i = 1; i < DCTSIZE_SQ; i++) { + cur = in[i]; + acur = ABS(cur); + if (cur) { + if (nzeros < HUFF_MAXRUN && acur < huff_maxlevel[nzeros]) { + /* + * encode using the Huffman tables + */ + + DBG_PRINT(("rle_huff %02d: Run %02d, Level %02d\n", i, nzeros, cur)); + assert(cur); + + code = (huff_table[nzeros])[acur]; + nbits = (huff_bits[nzeros])[acur]; + + assert(nbits); + + if (cur < 0) + code |= 1; /* the sign bit */ + Bitio_Write(out, code, nbits); + } else { + /* + * encode using the escape code + */ + DBG_PRINT(("Escape\n")); + Bitio_Write(out, 0x1, 6); /* ESCAPE */ + DBG_PRINT(("Run Length\n")); + Bitio_Write(out, nzeros, 6); /* Run-Length */ + + assert(cur != 0); + + /* + * this shouldn't happen, but the other + * choice is to bomb out and dump core... + */ + if (cur < -255) + cur = -255; + else if (cur > 255) + cur = 255; + + DBG_PRINT(("Level\n")); + if (acur < 128) { + Bitio_Write(out, cur, 8); + } else { + if (cur < 0) { + Bitio_Write(out, 0x8001 + cur + 255, 16); + } else + Bitio_Write(out, cur, 16); + } + } + nzeros = 0; + } else + nzeros++; + } + DBG_PRINT(("End of block\n")); + Bitio_Write(out, 0x2, 2); /* end of block marker */ +} + + +/* + * -------------------------------------------------------------- + * + * mp_rle_huff_pblock -- + * + * Given a FlatBlock, generates the Huffman bits for P DCT + * + * Results: None. + * + * Side effects: Output bits changed + * + * -------------------------------------------------------------- + */ + +void mp_rle_huff_pblock(FlatBlock in, BitBucket *out) +{ + register int i; + register int nzeros = 0; + register int16 cur; + register int16 acur; + register uint32 code; + register int nbits; + boolean first_dct = TRUE; + + /* + * yes, Virginia, we start at 0. + */ + for (i = 0; i < DCTSIZE_SQ; i++) { + cur = in[i]; + acur = ABS(cur); + if (cur) { + if (nzeros < HUFF_MAXRUN && acur < huff_maxlevel[nzeros]) { + /* + * encode using the Huffman tables + */ + + DBG_PRINT(("rle_huff %02d: Run %02d, Level %02d\n", i, nzeros, cur)); + assert(cur); + + if ( first_dct && (nzeros == 0) && (acur == 1) ) + { + /* actually, only needs = 0x2 */ + code = (cur == 1) ? 0x2 : 0x3; + nbits = 2; + } + else + { + code = (huff_table[nzeros])[acur]; + nbits = (huff_bits[nzeros])[acur]; + } + + assert(nbits); + + if (cur < 0) + code |= 1; /* the sign bit */ + Bitio_Write(out, code, nbits); + first_dct = FALSE; + } else { + /* + * encode using the escape code + */ + DBG_PRINT(("Escape\n")); + Bitio_Write(out, 0x1, 6); /* ESCAPE */ + DBG_PRINT(("Run Length\n")); + Bitio_Write(out, nzeros, 6); /* Run-Length */ + + assert(cur != 0); + + /* + * this shouldn't happen, but the other + * choice is to bomb out and dump core... + */ + if (cur < -255) + cur = -255; + else if (cur > 255) + cur = 255; + + DBG_PRINT(("Level\n")); + if (acur < 128) { + Bitio_Write(out, cur, 8); + } else { + if (cur < 0) { + Bitio_Write(out, 0x8001 + cur + 255, 16); + } else + Bitio_Write(out, cur, 16); + } + + first_dct = FALSE; + } + nzeros = 0; + } else + nzeros++; + } + + /* actually, should REALLY return FALSE and not use this! */ + if ( first_dct ) /* have to give a first_dct even if all 0's */ + { + fprintf(stdout, "HUFF called with all-zero coefficients\n"); + fprintf(stdout, "exiting...\n"); + exit(1); + } + + DBG_PRINT(("End of block\n")); + Bitio_Write(out, 0x2, 2); /* end of block marker */ +} diff --git a/converter/ppm/ppmtompeg/mpeg.c b/converter/ppm/ppmtompeg/mpeg.c new file mode 100644 index 00000000..a934ed09 --- /dev/null +++ b/converter/ppm/ppmtompeg/mpeg.c @@ -0,0 +1,1717 @@ +/*===========================================================================* + * mpeg.c + * + * Procedures to generate the MPEG sequence + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + + +/*==============* + * HEADER FILES * + *==============*/ + +#define _BSD_SOURCE /* Make sure strdup() is in string.h */ + +#include "all.h" +#include <time.h> +#include <errno.h> +#include <unistd.h> +#include <assert.h> +#include <string.h> +#ifdef MIPS +#include <sys/types.h> +#endif +#include <sys/stat.h> + +#include "ppm.h" +#include "nstring.h" + +#include "mtypes.h" +#include "frames.h" +#include "motion_search.h" +#include "prototypes.h" +#include "parallel.h" +#include "param.h" +#include "readframe.h" +#include "fsize.h" +#include "mheaders.h" +#include "rate.h" +#include "input.h" +#include "frametype.h" +#include "mpeg.h" + + +/*===========* + * VERSION * + *===========*/ + +#define VERSION "1.5b" + + +/*===========* + * CONSTANTS * + *===========*/ + +#define FPS_30 0x5 /* from MPEG standard sect. 2.4.3.2 */ +#define ASPECT_1 0x1 /* aspect ratio, from MPEG standard sect. 2.4.3.2 */ + + +/*==================* + * STATIC VARIABLES * + *==================*/ + +static unsigned int framesOutput; +static int realStart, realEnd; +static int currentGOP; +static int timeMask; +static int numI, numP, numB; +static boolean frameCountsUnknown; + + +/*==================* + * GLOBAL VARIABLES * + *==================*/ + +/* important -- don't initialize anything here */ +/* must be re-initted anyway in GenMPEGStream */ + +extern int IOtime; +extern boolean resizeFrame; +extern int outputWidth, outputHeight; +int gopSize = 100; /* default */ +int32 tc_hrs, tc_min, tc_sec, tc_pict, tc_extra; +int totalFramesSent; +int yuvWidth, yuvHeight; +int realWidth, realHeight; +char currentPath[MAXPATHLEN]; +char statFileName[256]; +char bitRateFileName[256]; +time_t timeStart, timeEnd; +FILE *statFile; +FILE *bitRateFile = NULL; +char *framePattern; +int framePatternLen; +int referenceFrame; +int frameRate = FPS_30; +int frameRateRounded = 30; +boolean frameRateInteger = TRUE; +int aspectRatio = ASPECT_1; +extern char userDataFileName[]; +extern int mult_seq_headers; + +int32 bit_rate, buf_size; + +/*===============================* + * INTERNAL PROCEDURE prototypes * + *===============================*/ + +static void ComputeDHMSTime _ANSI_ARGS_((int32 someTime, char *timeText)); +static void OpenBitRateFile _ANSI_ARGS_((void)); +static void CloseBitRateFile _ANSI_ARGS_((void)); + + +static void +ShowRemainingTime(boolean const childProcess) { +/*---------------------------------------------------------------------------- + Print out an estimate of the time left to encode +-----------------------------------------------------------------------------*/ + + if (childProcess) { + /* nothing */; + } else if ( numI + numP + numB == 0 ) { + /* no time left */ + } else if ( timeMask != 0 ) { + /* haven't encoded all types yet */ + } else { + static int lastTime = 0; + float total; + time_t nowTime; + float secondsPerFrame; + + time(&nowTime); + secondsPerFrame = (nowTime-timeStart)/(float)framesOutput; + total = secondsPerFrame*(float)(numI+numP+numB); + + if ((quietTime >= 0) && (!realQuiet) && (!frameCountsUnknown) && + ((lastTime < (int)total) || ((lastTime-(int)total) >= quietTime) || + (lastTime == 0) || (quietTime == 0))) { + if (total > 270.0) + pm_message("ESTIMATED TIME OF COMPLETION: %d minutes", + ((int)total+30)/60); + else + pm_message("ESTIMATED TIME OF COMPLETION: %d seconds", + (int)total); + } + lastTime = (int)total; + } +} + + + +static void +initTCTime(unsigned int const firstFrameNumber) { + + unsigned int frameNumber; + + tc_hrs = 0; tc_min = 0; tc_sec = 0; tc_pict = 0; tc_extra = 0; + for (frameNumber = 0; frameNumber < firstFrameNumber; ++frameNumber) + IncrementTCTime(); +} + + + +/*===========================================================================* + * + * IncrementTCTime + * + * increment the tc time by one second (and update min, hrs if necessary) + * also increments totalFramesSent + * + * RETURNS: nothing + * + * SIDE EFFECTS: totalFramesSent, tc_pict, tc_sec, tc_min, tc_hrs, tc_extra + * + *===========================================================================*/ +void +IncrementTCTime() { + /* if fps = an integer, then tc_extra = 0 and is ignored + + otherwise, it is the number of extra 1/1001 frames we've passed by + + so far; for example, if fps = 24000/1001, then 24 frames = 24024/24000 + seconds = 1 second + 24/24000 seconds = 1 + 1/1000 seconds; similary, + if fps = 30000/1001, then 30 frames = 30030/30000 = 1 + 1/1000 seconds + and if fps = 60000/1001, then 60 frames = 1 + 1/1000 seconds + + if fps = 24000/1001, then 1/1000 seconds = 24/1001 frames + if fps = 30000/1001, then 1/1000 seconds = 30/1001 frames + if fps = 60000/1001, then 1/1000 seconds = 60/1001 frames + */ + + totalFramesSent++; + tc_pict++; + if ( tc_pict >= frameRateRounded ) { + tc_pict = 0; + tc_sec++; + if ( tc_sec == 60 ) { + tc_sec = 0; + tc_min++; + if ( tc_min == 60 ) { + tc_min = 0; + tc_hrs++; + } + } + if ( ! frameRateInteger ) { + tc_extra += frameRateRounded; + if ( tc_extra >= 1001 ) { /* a frame's worth */ + tc_pict++; + tc_extra -= 1001; + } + } + } +} + + + +static void +initializeRateControl(bool const wantUnderflowWarning, + bool const wantOverflowWarning) { +/*---------------------------------------------------------------------------- + Initialize rate control +-----------------------------------------------------------------------------*/ + int32 const bitstreamMode = getRateMode(); + + if (bitstreamMode == FIXED_RATE) { + initRateControl(wantUnderflowWarning, wantOverflowWarning); + /* + SetFrameRate(); + */ + } +} + + + +/*===========================================================================* + * + * SetReferenceFrameType + * + * set the reference frame type to be original or decoded + * + * RETURNS: nothing + * + * SIDE EFFECTS: referenceFrame + * + *===========================================================================*/ +void +SetReferenceFrameType(const char * const type) { + + if (strcmp(type, "ORIGINAL") == 0) + referenceFrame = ORIGINAL_FRAME; + else if ( strcmp(type, "DECODED") == 0 ) + referenceFrame = DECODED_FRAME; + else + pm_error("INTERNAL ERROR: Illegal reference frame type: '%s'", type); +} + + + +void +SetBitRateFileName(fileName) + char *fileName; +{ + strcpy(bitRateFileName, fileName); +} + + + + +static void +finishFrameOutput(MpegFrame * const frameP, + BitBucket * const bbP, + boolean const separateFiles, + int const referenceFrame, + boolean const childProcess, + boolean const remoteIO) { + + if ((referenceFrame == DECODED_FRAME) && + childProcess && NonLocalRefFrame(frameP->id)) { + if (remoteIO) + SendDecodedFrame(frameP); + else + WriteDecodedFrame(frameP); + + NotifyDecodeServerReady(frameP->id); + } + + if (separateFiles) { + if (remoteIO) + SendRemoteFrame(frameP->id, bbP); + else { + Bitio_Flush(bbP); + Bitio_Close(bbP); + } + } +} + + + + +static void +outputIFrame(MpegFrame * const frameP, + BitBucket * const bb, + int const realStart, + int const realEnd, + MpegFrame * const pastRefFrameP, + boolean const separateFiles) { + + /* only start a new GOP with I */ + /* don't start GOP if only doing frames */ + if ((!separateFiles) && (currentGOP >= gopSize)) { + boolean const closed = + (totalFramesSent == frameP->id || pastRefFrameP == NULL); + + static int num_gop = 0; + + /* first, check to see if closed GOP */ + + /* new GOP */ + if (num_gop != 0 && mult_seq_headers && + num_gop % mult_seq_headers == 0) { + if (!realQuiet) { + fprintf(stdout, + "Creating new Sequence before GOP %d\n", num_gop); + fflush(stdout); + } + + Mhead_GenSequenceHeader( + bb, Fsize_x, Fsize_y, + /* pratio */ aspectRatio, + /* pict_rate */ frameRate, /* bit_rate */ bit_rate, + /* buf_size */ buf_size, /* c_param_flag */ 1, + /* iq_matrix */ customQtable, + /* niq_matrix */ customNIQtable, + /* ext_data */ NULL, /* ext_data_size */ 0, + /* user_data */ NULL, /* user_data_size */ 0); + } + + if (!realQuiet) + pm_message("Creating new GOP (closed = %s) before frame %d\n", + closed ? "YES" : "NO", frameP->id); + + ++num_gop; + Mhead_GenGOPHeader(bb, /* drop_frame_flag */ 0, + tc_hrs, tc_min, tc_sec, tc_pict, + closed, /* broken_link */ 0, + /* ext_data */ NULL, /* ext_data_size */ 0, + /* user_data */ NULL, /* user_data_size */ 0); + currentGOP -= gopSize; + if (pastRefFrameP == NULL) + SetGOPStartTime(0); + else + SetGOPStartTime(pastRefFrameP->id+1); + } + + if ((frameP->id >= realStart) && (frameP->id <= realEnd)) + GenIFrame(bb, frameP); + + numI--; + timeMask &= 0x6; + + currentGOP++; + IncrementTCTime(); +} + + + +static void +outputPFrame(MpegFrame * const frameP, + BitBucket * const bbP, + int const realStart, + int const realEnd, + MpegFrame * const pastRefFrameP) { + + if ((frameP->id >= realStart) && (frameP->id <= realEnd)) + GenPFrame(bbP, frameP, pastRefFrameP); + + numP--; + timeMask &= 0x5; + + currentGOP++; + IncrementTCTime(); +} + + + +static BitBucket * +bitioNew(const char * const outputFileName, + unsigned int const frameNumber, + boolean const remoteIO) { + + BitBucket * bbP; + + if (remoteIO) + bbP = Bitio_New(NULL); + else { + const char * fileName; + + asprintfN(&fileName, "%s.frame.%d", outputFileName, frameNumber); + + bbP = Bitio_New_Filename(fileName); + + strfree(fileName); + } + return bbP; +} + + + +static void +getBFrame(int const frameNum, + struct inputSource * const inputSourceP, + MpegFrame * const pastRefFrameP, + boolean const childProcess, + boolean const remoteIO, + MpegFrame ** const bFramePP, + int * const IOtimeP, + unsigned int * const framesReadP) { +/*---------------------------------------------------------------------------- + Get Frame 'frameNum', which is a B frame related to previous reference + frame 'pastRefFrameP'. Return it as *bFramePP. + + We have various ways of getting the frame, corresponding to the + multitude of modes in which Ppmtompeg works. +-----------------------------------------------------------------------------*/ + if (!inputSourceP->stdinUsed) { + time_t tempTimeStart, tempTimeEnd; + MpegFrame * bFrameP; + bool endOfStream; + + bFrameP = Frame_New(frameNum, 'b'); + + time(&tempTimeStart); + + ReadNthFrame(inputSourceP, frameNum, remoteIO, childProcess, + separateConversion, slaveConversion, inputConversion, + bFrameP, &endOfStream); + + assert(!endOfStream); /* Because it's not a stream */ + + time(&tempTimeEnd); + *IOtimeP += (tempTimeEnd-tempTimeStart); + + ++(*framesReadP); + *bFramePP = bFrameP; + } else { + /* As the frame input is serial, we can't read the B frame now. + Rather, Caller has already read it and chained it to + the previous reference frame. So we get that copy now. + */ + *bFramePP = pastRefFrameP->next; + pastRefFrameP->next = (*bFramePP)->next; /* unlink from list */ + } +} + + + +static void +processBFrames(MpegFrame * const pastRefFrameP, + MpegFrame * const futureRefFrameP, + int const realStart, + int const realEnd, + struct inputSource * const inputSourceP, + boolean const remoteIo, + boolean const childProcess, + int * const IOtimeP, + BitBucket * const wholeStreamBbP, + const char * const outputFileName, + unsigned int * const framesReadP, + unsigned int * const framesOutputP, + int * const currentGopP) { +/*---------------------------------------------------------------------------- + Process the B frames that go between 'pastRefFrameP' and + 'futureRefFrame' in the movie (but go after 'futureRefFrameP' in the + MPEG stream, so reader doesn't have to read ahead). + + Remember that a B frame is one which is described by data in the + MPEG stream that describes the frame with respect to a frame somewhere + before it, and a frame somewhere after it (i.e. reference frames). + + But do only those B frames whose frame numbers are within the range + 'realStart' through 'realEnd'. +-----------------------------------------------------------------------------*/ + boolean const separateFiles = (wholeStreamBbP == NULL); + unsigned int const firstBFrameNum = pastRefFrameP->id + 1; + + int frameNum; + + assert(pastRefFrameP != NULL); + assert(futureRefFrameP != NULL); + + for (frameNum = MAX(realStart, firstBFrameNum); + frameNum < MIN(realEnd, futureRefFrameP->id); + ++frameNum) { + + MpegFrame * bFrame; + BitBucket * bbP; + + getBFrame(frameNum, inputSourceP, pastRefFrameP, childProcess, + remoteIO, + &bFrame, IOtimeP, framesReadP); + + if (separateFiles) + bbP = bitioNew(outputFileName, bFrame->id, remoteIO); + else + bbP = wholeStreamBbP; + + GenBFrame(bbP, bFrame, pastRefFrameP, futureRefFrameP); + ++(*framesOutputP); + + if (separateFiles) { + if (remoteIO) + SendRemoteFrame(bFrame->id, bbP); + else { + Bitio_Flush(bbP); + Bitio_Close(bbP); + } + } + + /* free this B frame right away */ + Frame_Free(bFrame); + + numB--; + timeMask &= 0x3; + ShowRemainingTime(childProcess); + + ++(*currentGopP); + IncrementTCTime(); + } +} + + + +static void +processRefFrame(MpegFrame * const frameP, + BitBucket * const bb_arg, + int const realStart, + int const realEnd, + MpegFrame * const pastRefFrameP, + boolean const childProcess, + const char * const outputFileName, + unsigned int * const framesReadP, + unsigned int * const framesOutputP) { +/*---------------------------------------------------------------------------- + Process an I or P frame. Encode and output it. + + But only if its frame number is within the range 'realStart' + through 'realEnd'. +-----------------------------------------------------------------------------*/ + if (frameP->id >= realStart && frameP->id <= realEnd) { + boolean separateFiles; + BitBucket * bb; + + separateFiles = (bb_arg == NULL); + + if ( separateFiles ) + bb = bitioNew(outputFileName, frameP->id, remoteIO); + else + bb = bb_arg; + + /* first, output this reference frame */ + switch (frameP->type) { + case TYPE_IFRAME: + outputIFrame(frameP, bb, realStart, realEnd, pastRefFrameP, + separateFiles); + break; + case TYPE_PFRAME: + outputPFrame(frameP, bb, realStart, realEnd, pastRefFrameP); + ShowRemainingTime(childProcess); + break; + default: + pm_error("INTERNAL ERROR: non-reference frame passed to " + "ProcessRefFrame()"); + } + + ++(*framesOutputP); + + finishFrameOutput(frameP, bb, separateFiles, referenceFrame, + childProcess, remoteIO); + } +} + + + +static void +countFrames(unsigned int const firstFrame, + unsigned int const lastFrame, + boolean const stdinUsed, + int * const numIP, + int * const numPP, + int * const numBP, + int * const timeMaskP, + boolean * const frameCountsUnknownP) { +/*---------------------------------------------------------------------------- + Count number of I, P, and B frames +-----------------------------------------------------------------------------*/ + unsigned int numI, numP, numB; + unsigned int timeMask; + + numI = 0; numP = 0; numB = 0; + timeMask = 0; + if (stdinUsed) { + numI = numP = numB = MAXINT/4; + *frameCountsUnknownP = TRUE; + } else { + unsigned int i; + for (i = firstFrame; i <= lastFrame; ++i) { + char const frameType = FType_Type(i); + switch(frameType) { + case 'i': numI++; timeMask |= 0x1; break; + case 'p': numP++; timeMask |= 0x2; break; + case 'b': numB++; timeMask |= 0x4; break; + } + } + *frameCountsUnknownP = FALSE; + } + + *numIP = numI; + *numPP = numP; + *numBP = numB; + *timeMaskP = timeMask; + *frameCountsUnknownP = frameCountsUnknown; +} + + + +static void +readAndSaveFrame(struct inputSource * const inputSourceP, + unsigned int const frameNumber, + char const frameType, + const char * const inputConversion, + MpegFrame * const pastRefFrameP, + unsigned int * const framesReadP, + int * const ioTimeP, + bool * const endOfStreamP) { +/*---------------------------------------------------------------------------- + Read the next frame from Standard Input and add it to the linked list + at *pastRefFrameP. Assume it is Frame Number 'frameNumber' and is of + type 'frameType'. + + Increment *framesReadP. + + Add the time it took to read it, in seconds, to *iotimeP. + + Iff we can't read because we hit end of file, return + *endOfStreamP == TRUE and *framesReadP and *iotimeP untouched. +-----------------------------------------------------------------------------*/ + /* This really should be part of ReadNthFrame. The frame should be chained + to the input object, not the past reference frame. + */ + + MpegFrame * p; + MpegFrame * frameP; + time_t ioTimeStart, ioTimeEnd; + + time(&ioTimeStart); + + frameP = Frame_New(frameNumber, frameType); + ReadFrame(frameP, inputSourceP, frameNumber, inputConversion, + endOfStreamP); + + if (*endOfStreamP) + Frame_Free(frameP); + else { + ++(*framesReadP); + + time(&ioTimeEnd); + *ioTimeP += (ioTimeEnd - ioTimeStart); + + /* Add the B frame to the end of the queue of B-frames + for later encoding. + */ + assert(pastRefFrameP != NULL); + + p = pastRefFrameP; + while (p->next != NULL) + p = p->next; + p->next = frameP; + } +} + + + +static void +doFirstFrameStuff(enum frameContext const context, + const char * const userDataFileName, + BitBucket * const bb, + int const fsize_x, + int const fsize_y, + int const aspectRatio, + int const frameRate, + int32 const qtable[], + int32 const niqtable[], + unsigned int * const inputFrameBitsP) { +/*---------------------------------------------------------------------------- + Do stuff we have to do after reading the first frame in a sequence + of frames requested of GenMPEGStream(). +-----------------------------------------------------------------------------*/ + *inputFrameBitsP = 24*Fsize_x*Fsize_y; + SetBlocksPerSlice(); + + if (context == CONTEXT_WHOLESTREAM) { + int32 const bitstreamMode = getRateMode(); + char * userData; + unsigned int userDataSize; + + assert(bb != NULL); + + DBG_PRINT(("Generating sequence header\n")); + if (bitstreamMode == FIXED_RATE) { + bit_rate = getBitRate(); + buf_size = getBufferSize(); + } else { + bit_rate = -1; + buf_size = -1; + } + + if (strlen(userDataFileName) != 0) { + struct stat statbuf; + FILE *fp; + + stat(userDataFileName,&statbuf); + userDataSize = statbuf.st_size; + userData = malloc(userDataSize); + fp = fopen(userDataFileName,"rb"); + if (fp == NULL) { + pm_message("Could not open userdata file '%s'.", + userDataFileName); + userData = NULL; + userDataSize = 0; + } else { + size_t bytesRead; + + bytesRead = fread(userData,1,userDataSize,fp); + if (bytesRead != userDataSize) { + pm_message("Could not read %d bytes from " + "userdata file '%s'.", + userDataSize,userDataFileName); + userData = NULL; + userDataSize = 0; + } + } + } else { /* Put in our UserData Header */ + const char * userDataString; + time_t now; + + time(&now); + asprintfN(&userDataString,"MPEG stream encoded by UCB Encoder " + "(mpeg_encode) v%s on %s.", + VERSION, ctime(&now)); + userData = strdup(userDataString); + userDataSize = strlen(userData); + strfree(userDataString); + } + Mhead_GenSequenceHeader(bb, Fsize_x, Fsize_y, + /* pratio */ aspectRatio, + /* pict_rate */ frameRate, + /* bit_rate */ bit_rate, + /* buf_size */ buf_size, + /*c_param_flag */ 1, + /* iq_matrix */ qtable, + /* niq_matrix */ niqtable, + /* ext_data */ NULL, + /* ext_data_size */ 0, + /* user_data */ (uint8*) userData, + /* user_data_size */ userDataSize); + } +} + + + +static void +getPreviousFrame(unsigned int const frameStart, + int const referenceFrame, + struct inputSource * const inputSourceP, + boolean const childProcess, + const char * const slaveConversion, + const char * const inputConversion, + MpegFrame ** const framePP, + unsigned int * const framesReadP, + int * const ioTimeP) { + + /* This needs to be modularized. It shouldn't issue messages about + encoding GOPs and B frames, since it knows nothing about those. + It should work for Standard Input too, through a generic Standard + Input reader that buffers stuff for backward reading. + */ + + MpegFrame * frameP; + time_t ioTimeStart, ioTimeEnd; + + /* can't find the previous frame interactively */ + if (inputSourceP->stdinUsed) + pm_error("Cannot encode GOP from stdin when " + "first frame is a B-frame."); + + if (frameStart < 1) + pm_error("Cannot encode GOP when first frame is a B-frame " + "and is not preceded by anything."); + + /* need to load in previous frame; call it an I frame */ + frameP = Frame_New(frameStart-1, 'i'); + + time(&ioTimeStart); + + if ((referenceFrame == DECODED_FRAME) && childProcess) { + WaitForDecodedFrame(frameStart); + + if (remoteIO) + GetRemoteDecodedRefFrame(frameP, frameStart - 1); + else + ReadDecodedRefFrame(frameP, frameStart - 1); + } else { + bool endOfStream; + ReadNthFrame(inputSourceP, frameStart - 1, remoteIO, childProcess, + separateConversion, slaveConversion, inputConversion, + frameP, &endOfStream); + assert(!endOfStream); /* Because Stdin causes failure above */ + } + ++(*framesReadP); + + time(&ioTimeEnd); + *ioTimeP += (ioTimeEnd-ioTimeStart); + + *framePP = frameP; +} + + + +static void +computeFrameRange(unsigned int const frameStart, + unsigned int const frameEnd, + enum frameContext const context, + struct inputSource * const inputSourceP, + unsigned int * const firstFrameP, + unsigned int * const lastFrameP) { + + switch (context) { + case CONTEXT_GOP: + *firstFrameP = frameStart; + *lastFrameP = frameEnd; + break; + case CONTEXT_JUSTFRAMES: { + /* if first frame is P or B, need to read in P or I frame before it */ + if (FType_Type(frameStart) != 'i') { + /* can't find the previous frame interactively */ + if (inputSourceP->stdinUsed) + pm_error("Cannot encode frames from Standard Input " + "when first frame is not an I-frame."); + + *firstFrameP = FType_PastRef(frameStart); + } else + *firstFrameP = frameStart; + + /* if last frame is B, need to read in P or I frame after it */ + if ((FType_Type(frameEnd) == 'b') && + (frameEnd != inputSourceP->numInputFiles-1)) { + /* can't find the next reference frame interactively */ + if (inputSourceP->stdinUsed) + pm_error("Cannot encode frames from Standard Input " + "when last frame is a B-frame."); + + *lastFrameP = FType_FutureRef(frameEnd); + } else + *lastFrameP = frameEnd; + } + break; + case CONTEXT_WHOLESTREAM: + *firstFrameP = frameStart; + *lastFrameP = frameEnd; + } +} + + + +static void +getFrame(MpegFrame ** const framePP, + struct inputSource * const inputSourceP, + unsigned int const frameNumber, + char const frameType, + unsigned int const realStart, + unsigned int const realEnd, + int const referenceFrame, + boolean const childProcess, + boolean const remoteIo, + boolean const separateConversion, + const char * const slaveConversion, + const char * const inputConversion, + unsigned int * const framesReadP, + int * const ioTimeP) { +/*---------------------------------------------------------------------------- + Get frame with number 'frameNumber' as *frameP. + + Increment *framesReadP. + + Add to *ioTimeP the time in seconds we spent reading it. + + Iff we fail to get the frame because the stream ends, return + *frameP == NULL, don't increment *framesReadP, and leave + *ioTimeP unchanged. +-----------------------------------------------------------------------------*/ + time_t ioTimeStart, ioTimeEnd; + MpegFrame * frameP; + bool endOfStream; + + time(&ioTimeStart); + + frameP = Frame_New(frameNumber, frameType); + + if ((referenceFrame == DECODED_FRAME) && + ((frameNumber < realStart) || (frameNumber > realEnd)) ) { + WaitForDecodedFrame(frameNumber); + + if (remoteIo) + GetRemoteDecodedRefFrame(frameP, frameNumber); + else + ReadDecodedRefFrame(frameP, frameNumber); + + /* I don't know what this block of code does, so I don't know + what endOfStream should be. Here's a guess: + */ + endOfStream = FALSE; + } else + ReadNthFrame(inputSourceP, frameNumber, remoteIO, childProcess, + separateConversion, slaveConversion, inputConversion, + frameP, &endOfStream); + + if (endOfStream) { + Frame_Free(frameP); + *framePP = NULL; + } else { + ++(*framesReadP); + + time(&ioTimeEnd); + *ioTimeP += (ioTimeEnd - ioTimeStart); + + *framePP = frameP; + } +} + + + +static void +handleBitRate(unsigned int const realEnd, + unsigned int const numBits, + boolean const childProcess, + boolean const showBitRatePerFrame) { + + extern void PrintItoIBitRate (int numBits, int frameNum); + + if (FType_Type(realEnd) != 'i') + PrintItoIBitRate(numBits, realEnd+1); + + if ((!childProcess) && showBitRatePerFrame) + CloseBitRateFile(); +} + + + +static void +doAFrame(unsigned int const frameNumber, + struct inputSource * const inputSourceP, + enum frameContext const context, + unsigned int const frameStart, + unsigned int const frameEnd, + unsigned int const realStart, + unsigned int const realEnd, + bool const childProcess, + const char * const outputFileName, + MpegFrame * const pastRefFrameP, + MpegFrame ** const newPastRefFramePP, + unsigned int * const framesReadP, + unsigned int * const framesOutputP, + bool * const firstFrameDoneP, + BitBucket * const bbP, + unsigned int * const inputFrameBitsP, + bool * const endOfStreamP) { +/*---------------------------------------------------------------------------- + *endOfStreamP returned means we were unable to do a frame because + the input stream has ended. In that case, none of the other outputs + are valid. +-----------------------------------------------------------------------------*/ + char const frameType = FType_Type(frameNumber); + + *endOfStreamP = FALSE; /* initial assumption */ + + if (frameType == 'b') { + /* We'll process this non-reference frame later. If reading + from stdin, we read it now and save it. Otherwise, we can + just read it later. + */ + *newPastRefFramePP = pastRefFrameP; + if (inputSourceP->stdinUsed) + readAndSaveFrame(inputSourceP, + frameNumber, frameType, inputConversion, + pastRefFrameP, framesReadP, &IOtime, + endOfStreamP); + } else { + MpegFrame * frameP; + + getFrame(&frameP, inputSourceP, frameNumber, frameType, + realStart, realEnd, referenceFrame, childProcess, + remoteIO, + separateConversion, slaveConversion, inputConversion, + framesReadP, &IOtime); + + if (frameP) { + *endOfStreamP = FALSE; + + if (!*firstFrameDoneP) { + doFirstFrameStuff(context, userDataFileName, + bbP, Fsize_x, Fsize_y, aspectRatio, + frameRate, qtable, niqtable, + inputFrameBitsP); + + *firstFrameDoneP = TRUE; + } + processRefFrame(frameP, bbP, frameStart, frameEnd, + pastRefFrameP, childProcess, outputFileName, + framesReadP, framesOutputP); + + if (pastRefFrameP) { + processBFrames(pastRefFrameP, frameP, realStart, realEnd, + inputSourceP, remoteIO, childProcess, + &IOtime, bbP, outputFileName, + framesReadP, framesOutputP, ¤tGOP); + } + if (pastRefFrameP != NULL) + Frame_Free(pastRefFrameP); + + *newPastRefFramePP = frameP; + } else + *endOfStreamP = TRUE; + } +} + + + +void +GenMPEGStream(struct inputSource * const inputSourceP, + enum frameContext const context, + unsigned int const frameStart, + unsigned int const frameEnd, + int32 const qtable[], + int32 const niqtable[], + bool const childProcess, + FILE * const ofP, + const char * const outputFileName, + bool const wantVbvUnderflowWarning, + bool const wantVbvOverflowWarning, + unsigned int * const inputFrameBitsP, + unsigned int * const totalBitsP) { +/*---------------------------------------------------------------------------- + Encode a bunch of frames into an MPEG sequence stream or a part thereof. + + 'context' tells what in addition to the frames themselves must go into + the stream: + + CONTEXT_JUSTFRAMES: Nothing but the indicated frames + CONTEXT_GOP: GOP header/trailer stuff to make a single GOP + that contains the indicated frames + CONTEXT_WHOLESTREAM: A whole stream consisting of the indicated + frames -- a sequence of whole GOPS, with stream + header/trailer stuff as well. + + 'frameStart' and 'frameEnd' are the numbers of the first and last + frames we are to encode, except that if the input source is a stream, + we stop where the stream ends if that is before 'frameEnd'. + +-----------------------------------------------------------------------------*/ + BitBucket * bbP; + unsigned int frameNumber; + bool endOfStream; + bool firstFrameDone; + int numBits; + unsigned int firstFrame, lastFrame; + /* Frame numbers of the first and last frames we look at. This + could be more than the the frames we actually encode because + we may need context (i.e. to encode a B frame, we need the subsequent + I or P frame). + */ + unsigned int framesRead; + /* Number of frames we have read; for statistical purposes */ + MpegFrame * pastRefFrameP; + /* The frame that will be the past reference frame for the next + B or P frame that we put into the stream + */ + if (frameEnd + 1 > inputSourceP->numInputFiles) + pm_error("Last frame (number %u) is beyond the end of the stream " + "(%u frames)", frameEnd, inputSourceP->numInputFiles); + + if (context == CONTEXT_WHOLESTREAM && + !inputSourceP->stdinUsed && + FType_Type(inputSourceP->numInputFiles-1) == 'b') + pm_message("WARNING: " + "One or more B-frames at end will not be encoded. " + "See FORCE_ENCODE_LAST_FRAME parameter file statement."); + + time(&timeStart); + + framesRead = 0; + + ResetIFrameStats(); + ResetPFrameStats(); + ResetBFrameStats(); + + Fsize_Reset(); + + framesOutput = 0; + + if (childProcess && separateConversion) + SetFileType(slaveConversion); + else + SetFileType(inputConversion); + + realStart = frameStart; + realEnd = frameEnd; + + computeFrameRange(frameStart, frameEnd, context, inputSourceP, + &firstFrame, &lastFrame); + + if (context == CONTEXT_GOP && FType_Type(frameStart) == 'b') + getPreviousFrame(frameStart, referenceFrame, inputSourceP, + childProcess, slaveConversion, inputConversion, + &pastRefFrameP, &framesRead, &IOtime); + else + pastRefFrameP = NULL; + + countFrames(firstFrame, lastFrame, inputSourceP->stdinUsed, + &numI, &numP, &numB, &timeMask, &frameCountsUnknown); + + if (showBitRatePerFrame) + OpenBitRateFile(); /* May modify showBitRatePerFrame */ + + if (context == CONTEXT_WHOLESTREAM) + bbP = Bitio_New(ofP); + else + bbP = NULL; + + initTCTime(firstFrame); + + totalFramesSent = firstFrame; + currentGOP = gopSize; /* so first I-frame generates GOP Header */ + + initializeRateControl(wantVbvUnderflowWarning, wantVbvOverflowWarning); + + firstFrameDone = FALSE; + for (frameNumber = firstFrame, endOfStream = FALSE; + frameNumber <= lastFrame && !endOfStream; + ++frameNumber) { + + doAFrame(frameNumber, inputSourceP, context, + frameStart, frameEnd, realStart, realEnd, + childProcess, outputFileName, + pastRefFrameP, &pastRefFrameP, + &framesRead, &framesOutput, &firstFrameDone, bbP, + inputFrameBitsP, &endOfStream); + } + + if (pastRefFrameP != NULL) + Frame_Free(pastRefFrameP); + + /* SEQUENCE END CODE */ + if (context == CONTEXT_WHOLESTREAM) + Mhead_GenSequenceEnder(bbP); + + if (context == CONTEXT_WHOLESTREAM) + numBits = bbP->cumulativeBits; + else { + /* What should the correct value be? Most likely 1. "numBits" is + used below, so we need to make sure it's properly initialized + to somthing (anything). + */ + numBits = 1; + } + + if (context != CONTEXT_JUSTFRAMES) { + Bitio_Flush(bbP); + bbP = NULL; + fclose(ofP); + } + handleBitRate(realEnd, numBits, childProcess, showBitRatePerFrame); + + *totalBitsP = numBits; +} + + + +/*===========================================================================* + * + * SetStatFileName + * + * set the statistics file name + * + * RETURNS: nothing + * + * SIDE EFFECTS: statFileName + * + *===========================================================================*/ +void +SetStatFileName(const char * const fileName) { + strcpy(statFileName, fileName); +} + + +/*===========================================================================* + * + * SetGOPSize + * + * set the GOP size (frames per GOP) + * + * RETURNS: nothing + * + * SIDE EFFECTS: gopSize + * + *===========================================================================*/ +void +SetGOPSize(size) + int size; +{ + gopSize = size; +} + + +/*===========================================================================* + * + * PrintStartStats + * + * print out the starting statistics (stuff from the param file) + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void +PrintStartStats(time_t const startTime, + bool const specificFrames, + unsigned int const firstFrame, + unsigned int const lastFrame, + struct inputSource * const inputSourceP) { + + FILE *fpointer; + int i; + + if (statFileName[0] == '\0') { + statFile = NULL; + } else { + statFile = fopen(statFileName, "a"); /* open for appending */ + if (statFile == NULL) { + fprintf(stderr, "ERROR: Could not open stat file: %s\n", + statFileName); + fprintf(stderr, " Sending statistics to stdout only.\n"); + fprintf(stderr, "\n\n"); + } else if (! realQuiet) { + fprintf(stdout, "Appending statistics to file: %s\n", + statFileName); + fprintf(stdout, "\n\n"); + } + } + + for (i = 0; i < 2; ++i) { + if ( ( i == 0 ) && (! realQuiet) ) { + fpointer = stdout; + } else if ( statFile != NULL ) { + fpointer = statFile; + } else { + continue; + } + + fprintf(fpointer, "MPEG ENCODER STATS (%s)\n",VERSION); + fprintf(fpointer, "------------------------\n"); + fprintf(fpointer, "TIME STARTED: %s", ctime(&startTime)); + if (getenv("HOST") != NULL) + fprintf(fpointer, "MACHINE: %s\n", getenv("HOST")); + else + fprintf(fpointer, "MACHINE: unknown\n"); + + if (inputSourceP->stdinUsed) + fprintf(fpointer, "INPUT: stdin\n"); + else { + const char * inputFileName; + + fprintf(fpointer, "INPUT FROM FILES:\n"); + + GetNthInputFileName(inputSourceP, 0, &inputFileName); + fprintf(fpointer, "FIRST FILE: %s/%s\n", + currentPath, inputFileName); + strfree(inputFileName); + GetNthInputFileName(inputSourceP, inputSourceP->numInputFiles-1, + &inputFileName); + fprintf(fpointer, "LAST FILE: %s/%s\n", + currentPath, inputFileName); + strfree(inputFileName); + } + fprintf(fpointer, "OUTPUT: %s\n", outputFileName); + + if (resizeFrame) + fprintf(fpointer, "RESIZED TO: %dx%d\n", + outputWidth, outputHeight); + fprintf(fpointer, "PATTERN: %s\n", framePattern); + fprintf(fpointer, "GOP_SIZE: %d\n", gopSize); + fprintf(fpointer, "SLICES PER FRAME: %d\n", slicesPerFrame); + if (searchRangeP==searchRangeB) + fprintf(fpointer, "RANGE: +/-%d\n", searchRangeP/2); + else fprintf(fpointer, "RANGES: +/-%d %d\n", + searchRangeP/2,searchRangeB/2); + fprintf(fpointer, "PIXEL SEARCH: %s\n", + pixelFullSearch ? "FULL" : "HALF"); + fprintf(fpointer, "PSEARCH: %s\n", PSearchName()); + fprintf(fpointer, "BSEARCH: %s\n", BSearchName()); + fprintf(fpointer, "QSCALE: %d %d %d\n", qscaleI, + GetPQScale(), GetBQScale()); + if (specificsOn) + fprintf(fpointer, "(Except as modified by Specifics file)\n"); + if ( referenceFrame == DECODED_FRAME ) { + fprintf(fpointer, "REFERENCE FRAME: DECODED\n"); + } else if ( referenceFrame == ORIGINAL_FRAME ) { + fprintf(fpointer, "REFERENCE FRAME: ORIGINAL\n"); + } else + pm_error("Illegal referenceFrame!!!"); + + /* For new Rate control parameters */ + if (getRateMode() == FIXED_RATE) { + fprintf(fpointer, "PICTURE RATE: %d\n", frameRateRounded); + if (getBitRate() != -1) { + fprintf(fpointer, "\nBIT RATE: %d\n", getBitRate()); + } + if (getBufferSize() != -1) { + fprintf(fpointer, "BUFFER SIZE: %d\n", getBufferSize()); + } + } + } + if (!realQuiet) + fprintf(stdout, "\n\n"); +} + + + +boolean +NonLocalRefFrame(int const id) { +/*---------------------------------------------------------------------------- + Return TRUE if frame number 'id' might be referenced from a non-local + process. This is a conservative estimate. We return FALSE iff there + is no way based on the information we have that the frame could be + referenced by a non-local process. +-----------------------------------------------------------------------------*/ + boolean retval; + + int const lastIPid = FType_PastRef(id); + + /* might be accessed by B-frame */ + + if (lastIPid+1 < realStart) + retval = TRUE; + else { + unsigned int const nextIPid = FType_FutureRef(id); + + /* if B-frame is out of range, then current frame can be + ref'd by it + */ + + /* might be accessed by B-frame */ + if (nextIPid > realEnd+1) + retval = TRUE; + + /* might be accessed by P-frame */ + if ((nextIPid > realEnd) && (FType_Type(nextIPid) == 'p')) + retval = TRUE; + } + return retval; +} + + + +/*===========================================================================* + * + * SetFrameRate + * + * sets global frame rate variables. value passed is MPEG frame rate code. + * + * RETURNS: TRUE or FALSE + * + * SIDE EFFECTS: frameRateRounded, frameRateInteger + * + *===========================================================================*/ +void +SetFrameRate() +{ + switch(frameRate) { + case 1: + frameRateRounded = 24; + frameRateInteger = FALSE; + break; + case 2: + frameRateRounded = 24; + frameRateInteger = TRUE; + break; + case 3: + frameRateRounded = 25; + frameRateInteger = TRUE; + break; + case 4: + frameRateRounded = 30; + frameRateInteger = FALSE; + break; + case 5: + frameRateRounded = 30; + frameRateInteger = TRUE; + break; + case 6: + frameRateRounded = 50; + frameRateInteger = TRUE; + break; + case 7: + frameRateRounded = 60; + frameRateInteger = FALSE; + break; + case 8: + frameRateRounded = 60; + frameRateInteger = TRUE; + break; + } + printf("frame rate(%d) set to %d\n", frameRate, frameRateRounded); +} + + +/*=====================* + * INTERNAL PROCEDURES * + *=====================*/ + +/*===========================================================================* + * + * ComputeDHMSTime + * + * turn some number of seconds (someTime) into a string which + * summarizes that time according to scale (days, hours, minutes, or + * seconds) + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +static void +ComputeDHMSTime(someTime, timeText) + int32 someTime; + char *timeText; +{ + int days, hours, mins, secs; + + days = someTime / (24*60*60); + someTime -= days*24*60*60; + hours = someTime / (60*60); + someTime -= hours*60*60; + mins = someTime / 60; + secs = someTime - mins*60; + + if ( days > 0 ) { + sprintf(timeText, "Total time: %d days and %d hours", days, hours); + } else if ( hours > 0 ) { + sprintf(timeText, "Total time: %d hours and %d minutes", hours, mins); + } else if ( mins > 0 ) { + sprintf(timeText, "Total time: %d minutes and %d seconds", mins, secs); + } else { + sprintf(timeText, "Total time: %d seconds", secs); + } +} + + + +void +ComputeGOPFrames(int const whichGOP, + unsigned int * const firstFrameP, + unsigned int * const lastFrameP, + unsigned int const numFrames) { +/*---------------------------------------------------------------------------- + Figure out which frames are in GOP number 'whichGOP'. +-----------------------------------------------------------------------------*/ + unsigned int passedB; + unsigned int currGOP; + unsigned int gopNum; + unsigned int frameNum; + unsigned int firstFrame, lastFrame; + bool foundGop; + + /* calculate first, last frames of whichGOP GOP */ + + gopNum = 0; + frameNum = 0; + passedB = 0; + currGOP = 0; + foundGop = FALSE; + + while (!foundGop) { + if (frameNum >= numFrames) + pm_error("There aren't that many GOPs!"); + + if (gopNum == whichGOP) { + foundGop = TRUE; + firstFrame = frameNum; + } + + /* go past one gop */ + /* must go past at least one frame */ + do { + currGOP += (1 + passedB); + + ++frameNum; + + passedB = 0; + while ((frameNum < numFrames) && (FType_Type(frameNum) == 'b')) { + ++frameNum; + ++passedB; + } + } while ((frameNum < numFrames) && + ((FType_Type(frameNum) != 'i') || (currGOP < gopSize))); + + currGOP -= gopSize; + + if (gopNum == whichGOP) + lastFrame = (frameNum - passedB - 1); + + ++gopNum; + } + *firstFrameP = firstFrame; + *lastFrameP = lastFrame; +} + + + +static void +doEndStats(FILE * const fpointer, + time_t const startTime, + time_t const endTime, + unsigned int const inputFrameBits, + unsigned int const totalBits, + float const totalCPU) { + + int32 const diffTime = endTime - startTime; + + char timeText[256]; + + ComputeDHMSTime(diffTime, timeText); + + fprintf(fpointer, "TIME COMPLETED: %s", ctime(&endTime)); + fprintf(fpointer, "%s\n\n", timeText); + + ShowIFrameSummary(inputFrameBits, totalBits, fpointer); + ShowPFrameSummary(inputFrameBits, totalBits, fpointer); + ShowBFrameSummary(inputFrameBits, totalBits, fpointer); + + fprintf(fpointer, "---------------------------------------------\n"); + fprintf(fpointer, "Total Compression: %3d:1 (%9.4f bpp)\n", + framesOutput*inputFrameBits/totalBits, + 24.0*(float)(totalBits)/(float)(framesOutput*inputFrameBits)); + if (diffTime > 0) { + fprintf(fpointer, "Total Frames Per Sec Elapsed: %f (%ld mps)\n", + (float)framesOutput/(float)diffTime, + (long)((float)framesOutput * + (float)inputFrameBits / + (256.0*24.0*(float)diffTime))); + } else { + fprintf(fpointer, "Total Frames Per Sec Elapsed: Infinite!\n"); + } + if ( totalCPU == 0.0 ) { + fprintf(fpointer, "CPU Time: NONE!\n"); + } else { + fprintf(fpointer, "Total Frames Per Sec CPU : %f (%ld mps)\n", + (float)framesOutput/totalCPU, + (long)((float)framesOutput * + (float)inputFrameBits/(256.0*24.0*totalCPU))); + } + fprintf(fpointer, "Total Output Bit Rate (%d fps): %d bits/sec\n", + frameRateRounded, frameRateRounded*totalBits/framesOutput); + fprintf(fpointer, "MPEG file created in : %s\n", outputFileName); + fprintf(fpointer, "\n\n"); + + if ( computeMVHist ) { + ShowPMVHistogram(fpointer); + ShowBBMVHistogram(fpointer); + ShowBFMVHistogram(fpointer); + } +} + + + +/*===========================================================================* + * + * PrintEndStats + * + * print end statistics (summary, time information) + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void +PrintEndStats(time_t const startTime, + time_t const endTime, + unsigned int const inputFrameBits, + unsigned int const totalBits) { + + float totalCPU; + + totalCPU = 0.0; + totalCPU += IFrameTotalTime(); + totalCPU += PFrameTotalTime(); + totalCPU += BFrameTotalTime(); + + if (!realQuiet) { + fprintf(stdout, "\n\n"); + doEndStats(stdout, startTime, endTime, inputFrameBits, + totalBits, totalCPU); + } + + if (statFile) { + doEndStats(statFile, startTime, endTime, inputFrameBits, + totalBits, totalCPU); + + fclose(statFile); + } +} + + + +void +ReadDecodedRefFrame(MpegFrame * const frameP, + unsigned int const frameNumber) { + + FILE *fpointer; + char fileName[256]; + int width, height; + register int y; + + width = Fsize_x; + height = Fsize_y; + + sprintf(fileName, "%s.decoded.%u", outputFileName, frameNumber); + if (! realQuiet) { + fprintf(stdout, "reading %s\n", fileName); + fflush(stdout); + } + + if ((fpointer = fopen(fileName, "rb")) == NULL) { + sleep(1); + if ((fpointer = fopen(fileName, "rb")) == NULL) { + fprintf(stderr, "Cannot open %s\n", fileName); + exit(1); + }} + + Frame_AllocDecoded(frameP, TRUE); + + for ( y = 0; y < height; y++ ) { + size_t bytesRead; + + bytesRead = fread(frameP->decoded_y[y], 1, width, fpointer); + if (bytesRead != width) + pm_error("Could not read enough bytes from '%s;", fileName); + } + + for (y = 0; y < (height >> 1); y++) { /* U */ + size_t const bytesToRead = width/2; + size_t bytesRead; + + bytesRead = fread(frameP->decoded_cb[y], 1, bytesToRead, fpointer); + if (bytesRead != bytesToRead) + pm_message("Could not read enough bytes from '%s'", fileName); + } + + for (y = 0; y < (height >> 1); y++) { /* V */ + size_t const bytesToRead = width/2; + size_t bytesRead; + bytesRead = fread(frameP->decoded_cr[y], 1, bytesToRead, fpointer); + if (bytesRead != bytesToRead) + pm_message("Could not read enough bytes from '%s'", fileName); + } + fclose(fpointer); +} + + + +static void +OpenBitRateFile() { + bitRateFile = fopen(bitRateFileName, "w"); + if ( bitRateFile == NULL ) { + pm_message("ERROR: Could not open bit rate file: '%s'", + bitRateFileName); + showBitRatePerFrame = FALSE; + } +} + + + +static void +CloseBitRateFile() { + fclose(bitRateFile); +} diff --git a/converter/ppm/ppmtompeg/mquant.c b/converter/ppm/ppmtompeg/mquant.c new file mode 100644 index 00000000..1f8ca63a --- /dev/null +++ b/converter/ppm/ppmtompeg/mquant.c @@ -0,0 +1,44 @@ +#include "mtypes.h" +#include "mproto.h" + +static int qtable[][8] = { + { 8,16,19,22,26,27,29,34}, + {16,16,22,24,27,29,34,37}, + {19,22,26,27,29,34,34,38}, + {22,22,26,27,29,34,37,40}, + {22,26,27,29,32,35,40,48}, + {26,27,29,32,35,40,48,58}, + {26,27,29,34,38,46,56,69}, + {27,29,35,38,46,56,69,83} }; + + +/* + *-------------------------------------------------------------- + * + * mp_quant_block -- + * + * Quantizes a block -- removing information + * It's safe for out == in. + * + * Results: + * None. + * + * Side effects: + * Modifies the out block. + * + *-------------------------------------------------------------- + */ +void mp_quant_block(Block in, Block out) { + int i; + + for(i=0;i<8;i++) { + out[i][0] = in[i][0] / qtable[i][0]; + out[i][1] = in[i][1] / qtable[i][1]; + out[i][2] = in[i][2] / qtable[i][2]; + out[i][3] = in[i][3] / qtable[i][3]; + out[i][4] = in[i][4] / qtable[i][4]; + out[i][5] = in[i][5] / qtable[i][5]; + out[i][6] = in[i][6] / qtable[i][6]; + out[i][7] = in[i][7] / qtable[i][7]; + } +} diff --git a/converter/ppm/ppmtompeg/nojpeg.c b/converter/ppm/ppmtompeg/nojpeg.c new file mode 100644 index 00000000..38c05a9e --- /dev/null +++ b/converter/ppm/ppmtompeg/nojpeg.c @@ -0,0 +1,61 @@ +/*===========================================================================* + * nojpeg.c * + * * + * procedures to deal with JPEG files * + * * + * EXPORTED PROCEDURES: * + * JMovie2JPEG * + * ReadJPEG * + * * + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#include "all.h" +#include "mtypes.h" +#include "frames.h" +#include "prototypes.h" +#include "param.h" +#include "readframe.h" +#include "fsize.h" +#include "rgbtoycc.h" +#include "jpeg.h" + + +void +JMovie2JPEG(const char * const infilename, + const char * const obase, + int const start, + int const end) { + + pm_error("This program has not been built with the " + "ability to handle JPEG input files"); +} + + +void +ReadJPEG(MpegFrame * const mf, + FILE * const fp) { + + pm_error("This program has not been built with the " + "ability to handle JPEG input files"); +} diff --git a/converter/ppm/ppmtompeg/noparallel.c b/converter/ppm/ppmtompeg/noparallel.c new file mode 100644 index 00000000..016e3c44 --- /dev/null +++ b/converter/ppm/ppmtompeg/noparallel.c @@ -0,0 +1,229 @@ +/*===========================================================================* + * noparallel.c + * + * Would be procedures to make encoder to run in parallel -- except + * this machine doesn't have sockets, so we can only run sequentially + * so this file has dummy procedures which lets it compile + * + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/*==============* + * HEADER FILES * + *==============*/ + +#include <time.h> + +#include <pm.h> + +#include "all.h" +#include "mtypes.h" +#include "parallel.h" +#include "frame.h" + +/*==================* + * GLOBAL VARIABLES * + *==================*/ + +int parallelTestFrames = 10; +int parallelTimeChunks = 60; +const char *IOhostName; +int ioPortNumber; +int combinePortNumber; +int decodePortNumber; +boolean niceProcesses = FALSE; +boolean forceIalign = FALSE; +int machineNumber = -1; +boolean remoteIO = FALSE; +boolean separateConversion; +time_t IOtime = 0; + + +/*=====================* + * EXPORTED PROCEDURES * + *=====================*/ + +/*=================* + * IO SERVER STUFF * + *=================*/ + + +void +IoServer(struct inputSource * const inputSourceP, + const char * const parallelHostName, + int const portNum) { + + pm_error("This version of Ppmtompeg cannot run an I/O server because " + "it does not have socket capability."); +} + + + +void +SetIOConvert(boolean const separate) { + /* do nothing -- this may be called during non-parallel execution */ +} + + + +void +SetParallelPerfect(boolean const val) { + /* do nothing -- this may be called during non-parallel execution */ +} + + +void +SetRemoteShell(const char * const shell) { + /* do nothing -- this may be called during non-parallel execution */ +} + + + +void +NoteFrameDone(int const frameStart, + int const frameEnd) { + fprintf(stdout, + "ERROR: (NoteFrameDone) " + "This machine can NOT run parallel version\n"); + exit(1); +} + + + +/* SendRemoteFrame + */ +void +SendRemoteFrame(int const frameNumber, + BitBucket * const bb) { + fprintf(stdout, "ERROR: (SendRemoteFrame) " + "This machine can NOT run parallel version\n"); + exit(1); +} + + + +/* GetRemoteFrame + */ +void +GetRemoteFrame(MpegFrame * const frame, + int const frameNumber) { + + fprintf(stdout, "ERROR: (GetRemoteFrame) " + "This machine can NOT run parallel version\n"); + exit(1); +} + + + +void +WaitForOutputFile(int const number) { + fprintf(stdout, "ERROR: (WaitForOutputFile) " + "This machine can NOT run parallel version\n"); + exit(1); +} + + + +/*=======================* + * PARALLEL SERVER STUFF * + *=======================*/ + + +void +MasterServer(struct inputSource * const inputSourceP, + const char * const paramFileName, + const char * const outputFileName) { + + pm_error("This version of Ppmtompeg cannot run a master server because " + "it does not have socket capability."); +} + + + +void +CombineServer(int const numFrames, + const char * const masterHostName, + int const masterPortNum) { + + pm_error("This version of Ppmtompeg cannot run combine server because " + "it does not have socket capability."); +} + + + +void +DecodeServer(int const numInputFiles, + const char * const decodeFileName, + const char * const masterHostName, + int const masterPortNum) { + + pm_error("This version of Ppmtompeg cannot run a decode server because " + "it does not have socket capability."); +} + + + +void +NotifyMasterDone(const char * const hostName, + int const portNum, + int const machineNumber, + unsigned int const seconds, + boolean * const moreWorkToDoP, + int * const frameStartP, + int * const frameEndP) { + pm_error("This version of Ppmtompeg cannot run parallel mode because " + "it does not have socket capability."); +} + + + +void +NotifyDecodeServerReady(int const id) { + pm_error("This version of Ppmtompeg cannot run parallel mode because " + "it does not have socket capability."); +} + + + +void +WaitForDecodedFrame(int const id) { + pm_error("This version of Ppmtompeg cannot run parallel mode because " + "it does not have socket capability."); +} + + + +void +SendDecodedFrame(MpegFrame * const frame) { + pm_error("This version of Ppmtompeg cannot run parallel mode because " + "it does not have socket capability."); +} + + + +void +GetRemoteDecodedRefFrame(MpegFrame * const frame, + int const frameNumber) { + pm_error("This version of Ppmtompeg cannot run parallel mode because " + "it does not have socket capability."); +} diff --git a/converter/ppm/ppmtompeg/opts.c b/converter/ppm/ppmtompeg/opts.c new file mode 100644 index 00000000..c4d0c0b3 --- /dev/null +++ b/converter/ppm/ppmtompeg/opts.c @@ -0,0 +1,536 @@ +/*===========================================================================* + * opts.c * + * * + * Special C code to handle TUNEing options * + * * + * EXPORTED PROCEDURES: * + * Tune_Init * + * CollectQuantStats * + * * + *===========================================================================*/ + + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/*==============* + * HEADER FILES * + *==============*/ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <math.h> +#include "opts.h" + +/*==============* + * EXTERNALS * + *==============*/ + +extern char outputFileName[]; +extern boolean pureDCT; +extern int32 qtable[], niqtable[]; +extern int ZAG[]; +extern boolean printSNR, decodeRefFrames; + +void init_idctref _ANSI_ARGS_((void)); +void init_fdct _ANSI_ARGS_((void)); + + +/*===================* + * GLOBALS MADE HERE * + *===================*/ + +boolean tuneingOn = FALSE; +int block_bound = 128; +boolean collect_quant = FALSE; +int collect_quant_detailed = 0; +FILE *collect_quant_fp; +int kill_dim = FALSE; +int kill_dim_break, kill_dim_end; +float kill_dim_slope; +int SearchCompareMode = DEFAULT_SEARCH; +boolean squash_small_differences = FALSE; +int SquashMaxLum, SquashMaxChr; +float LocalDCTRateScale = 1.0, LocalDCTDistortScale = 1.0; +boolean IntraPBAllowed = TRUE; +boolean WriteDistortionNumbers = FALSE; +int collect_distortion_detailed = 0; +FILE *distortion_fp; +FILE *fp_table_rate[31], *fp_table_dist[31]; +boolean DoLaplace = FALSE; +double **L1, **L2, **Lambdas; +int LaplaceNum, LaplaceCnum; +boolean BSkipBlocks = TRUE; + +/* define this as it too much of a pain to find toupper on different arch'es */ +#define ASCII_TOUPPER(c) ((c>='a') && (c<='z')) ? c-'a'+'A' : c + +/*===============* + * Internals * + *===============*/ + +/*===========================================================================* + * + * SkipSpacesTabs + * + * skip all spaces and tabs + * + * RETURNS: point to next character not a space or tab + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +static const char * +SkipSpacesTabs(const char * const start) { + + const char * p; + + for (p = start; *p == ' ' || *p == '\t'; ++p); + + return p; +} + + + +/*===========================================================================* + * + * SetupCollectQuantStats + * + * Setup variables to collect statistics on quantization values + * + * RETURNS: nothing + * + * SIDE EFFECTS: sets collect_quant and collect_quant_fp + * + *===========================================================================*/ +static void +SetupCollectQuantStats(const char * const charPtr) +{ + char fname[256]; + const char * cp; + cp = charPtr; + while ( (*cp != ' ') && (*cp != '\t') && (*cp != '\n')) { + cp++; + } + + strncpy(fname, charPtr, cp-charPtr); + fname[cp-charPtr] = '\0'; + collect_quant = TRUE; + if ((collect_quant_fp = fopen(fname,"w")) == NULL) { + fprintf(stderr, "Error opening %s for quant statistics\n", fname); + fprintf(stderr, "Using stdout (ick!)\n"); + collect_quant_fp = stdout; + } + + cp = SkipSpacesTabs(cp); + if (*cp != '\n') { + switch (*cp) { + case 'c': + collect_quant_detailed = 1; + break; + default: + fprintf(stderr, "Unknown TUNE parameter setting format %s\n", cp); + }} +} + + + + +/*===========================================================================* + * + * SetupKillDimAreas + * + * Do a transform on small lum values + * + * RETURNS: nothing + * + * SIDE EFFECTS: sets kill_dim, kill_dim_break, kill_dim_end + * + *===========================================================================*/ +static void +SetupKillDimAreas(const char * const charPtr) +{ + int items_scanned; + + kill_dim = TRUE; + items_scanned = sscanf(charPtr, "%d %d %f", + &kill_dim_break, &kill_dim_end, &kill_dim_slope); + if (items_scanned != 3) { + kill_dim_slope = 0.25; + items_scanned = sscanf(charPtr, "%d %d", + &kill_dim_break, &kill_dim_end); + if (items_scanned != 2) { + /* Use defaults */ + kill_dim_break = 20; + kill_dim_end = 25; + } + } + /* check values */ + if (kill_dim_break > kill_dim_end) { + fprintf(stderr, "TUNE parameter k: break > end is illegal.\n"); + exit(-1); + } + if (kill_dim_slope < 0) { + fprintf(stderr, "TUNE parameter k: slope < 0 is illegal.\n"); + exit(-1); + } +} + + + +/*===========================================================================* + * + * SetupSquashSmall + * + * Setup encoder to squash small changes in Y or Cr/Cb values + * + * RETURNS: nothing + * + * SIDE EFFECTS: sets squash_max_differences SquashMaxLum SquashMaxChr + * + *===========================================================================*/ +static void +SetupSquashSmall(const char * const charPtr) +{ + squash_small_differences = TRUE; + + if (sscanf(charPtr, "%d %d", &SquashMaxLum, &SquashMaxChr) == 1) { + /* Only set one, do both */ + SquashMaxChr = SquashMaxLum; + } +} + + +/*===========================================================================* + * + * SetupLocalDCT + * + * Setup encoder to use DCT for rate-distortion estimat ein Psearches + * + * RETURNS: nothing + * + * SIDE EFFECTS: sets SearchCompareMode and + * can change LocalDCTRateScale, LocalDCTDistortScale + * + *===========================================================================*/ +static void +SetupLocalDCT(const char * const charPtr) +{ + int num_scales=0; + + SearchCompareMode = LOCAL_DCT; + + /* Set scaling factors if present */ + num_scales = sscanf(charPtr, "%f %f", &LocalDCTRateScale, &LocalDCTDistortScale); + if (num_scales == 1) { + fprintf(stderr, "Invalid number of scaling factors for local DCT\n"); + fprintf(stderr, "Must specify Rate Scale and Distorion scale (both floats)\n"); + fprintf(stderr, "Continuing with 1.0 1.0\n"); + LocalDCTRateScale = 1.0; + LocalDCTDistortScale = 1.0; + } +} + + +/*===========================================================================* + * + * SetupLaplace + * + * Setup encoder to find distrubution for I-frames, and use for -snr + * + * RETURNS: nothing + * + * SIDE EFFECTS: sets DoLaplace, L1, L2, and Lambdas + * + *===========================================================================*/ +static void +SetupLaplace() +{ + int i; + + DoLaplace = TRUE; + LaplaceNum = 0; + L1 = (double **)malloc(sizeof(double *)*3); + L2 = (double **)malloc(sizeof(double *)*3); + Lambdas = (double **)malloc(sizeof(double *)*3); + if (L1 == NULL || L2 == NULL || Lambdas == NULL) { + fprintf(stderr,"Out of memory!!!\n"); + exit(1); + } + for (i = 0; i < 3; i++) { + L1[i] = (double *)calloc(64, sizeof(double)); + L2[i] = (double *)calloc(64, sizeof(double)); + Lambdas[i] = (double *)malloc(sizeof(double) * 64); + if (L1[i] == NULL || L2[i] == NULL || Lambdas[i] == NULL) { + fprintf(stderr,"Out of memory!!!\n"); + exit(1); + } + } +} + +static void +SetupWriteDistortions(const char * const charPtr) +{ + char fname[256]; + const char * cp; + int i; + + WriteDistortionNumbers = TRUE; + cp = charPtr; + while ( (*cp != ' ') && (*cp != '\t') && (*cp != '\n')) { + cp++; + } + + strncpy(fname, charPtr, cp-charPtr); + fname[cp-charPtr] = '\0'; + collect_quant = TRUE; + if ((distortion_fp = fopen(fname,"w")) == NULL) { + fprintf(stderr, "Error opening %s for quant statistics\n", fname); + fprintf(stderr, "Using stdout (ick!)\n"); + distortion_fp = stdout; + } + + cp = SkipSpacesTabs(cp); + if (*cp != '\n') { + switch (*cp) { + case 'c': + collect_distortion_detailed = TRUE; + break; + case 't': { + char scratch[256]; + collect_distortion_detailed = 2; + for (i = 1; i < 32; i++) { + sprintf(scratch, "%srate%d", fname, i); + fp_table_rate[i-1] = fopen(scratch, "w"); + sprintf(scratch, "%sdist%d", fname, i); + fp_table_dist[i-1] = fopen(scratch, "w"); + }} + break; + default: + fprintf(stderr, "Unknown TUNE parameter setting format %s\n", cp); + }} +} + +/*=====================* + * EXPORTED PROCEDURES * + *=====================*/ + +void +CalcLambdas(void) { + + int i,j,n; + double var; + + n = LaplaceNum; + for (i = 0; i < 3; i++) { + for (j = 0; j < 64; j++) { + var = (n*L1[i][j] + L2[i][j]*L2[i][j]) / (n*(n-1)); + Lambdas[i][j] = sqrt(2.0) / sqrt(var); + } + } +} + + +/*===========================================================================* + * + * Mpost_UnQuantZigBlockLaplace + * + * unquantize and zig-zag (decode) a single block, using the distrib to get vals + * Iblocks only now + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void +Mpost_UnQuantZigBlockLaplace(in, out, qscale, iblock) + FlatBlock in; + Block out; + int qscale; + boolean iblock; +{ + register int index; + int position; + register int qentry; + int level, coeff; + double low, high; + double mid,lam; + + /* qtable[0] must be 8 */ + out[0][0] = (int16)(in[0] * 8); + + for ( index = 1; index < DCTSIZE_SQ; index++ ) { + position = ZAG[index]; + level = in[index]; + + if (level == 0) { + ((int16 *)out)[position] = 0; + continue; + } + qentry = qtable[position] * qscale; + coeff = (level*qentry)/8; + low = ((ABS(level)-.5)*qentry)/8; + high = ((ABS(level)+.5)*qentry)/8; + lam = Lambdas[LaplaceCnum][position]; + mid = (1.0/lam) * log(0.5*(exp(-lam*low)+exp(-lam*high))); + mid = ABS(mid); + if (mid - floor(mid) > .4999) { + mid = ceil(mid); + } else { + mid = floor(mid); + } + if (level<0) {mid = -mid;} +/*printf("(%2.1lf-%2.1lf): old: %d vs %d\n",low,high,coeff,(int) mid);*/ + coeff = mid; + if ( (coeff & 1) == 0 ) { + if ( coeff < 0 ) { + coeff++; + } else if ( coeff > 0 ) { + coeff--; + } + } + ((int16 *)out)[position] = coeff; + } +} + +int +mse(Block blk1, Block blk2) +{ + register int index, error, tmp; + int16 *bp1, *bp2; + + bp1 = (int16 *)blk1; + bp2 = (int16 *)blk2; + error = 0; + for ( index = 0; index < DCTSIZE_SQ; index++ ) { + tmp = *bp1++ - *bp2++; + error += tmp*tmp; + } + return error; +} + + + + +/*===========================================================================* + * + * Tune_Init + * + * Do any setup needed before coding stream + * + * RETURNS: nothing + * + * SIDE EFFECTS: varies + * + *===========================================================================*/ +void Tune_Init() +{ + int i; + + /* Just check for each, and do whats needed */ + if (collect_quant) { + if (!pureDCT) { + pureDCT = TRUE; + init_idctref(); + init_fdct(); + } + fprintf(collect_quant_fp, "# %s\n", outputFileName); + fprintf(collect_quant_fp, "#"); + for (i=0; i<64; i++) + fprintf(collect_quant_fp, " %d", qtable[i]); + fprintf(collect_quant_fp, "\n#"); + for (i=0; i<64; i++) + fprintf(collect_quant_fp, " %d", niqtable[i]); + fprintf(collect_quant_fp, "\n# %d %d %d\n\n", + GetIQScale(), GetPQScale(), GetBQScale()); + + } + + if (DoLaplace) { + if (!pureDCT) { + pureDCT = TRUE; + init_idctref(); + init_fdct(); + } + decodeRefFrames = TRUE; + printSNR = TRUE; + } + +} + +/*===========================================================================* + * + * ParseTuneParam + * + * Handle the strings following TUNE + * + * RETURNS: nothing + * + * SIDE EFFECTS: varies + * + *===========================================================================*/ +void ParseTuneParam(const char * const charPtr) +{ + switch (ASCII_TOUPPER(*charPtr)) { + case 'B': + if (1 != sscanf(charPtr+2, "%d", &block_bound)) { + fprintf(stderr, "Invalid tuning parameter (b) in parameter file.\n"); + } + break; + case 'C': + SetupCollectQuantStats(charPtr+2); + break; + case 'D': + SetupLocalDCT(SkipSpacesTabs(charPtr+1)); + break; + case 'K': + SetupKillDimAreas(SkipSpacesTabs(charPtr+1)); + break; + case 'L': + SetupLaplace(); + break; + case 'N': + SearchCompareMode = NO_DC_SEARCH; + break; + case 'Q': + SearchCompareMode = DO_Mean_Squared_Distortion; + break; + case 'S': + SetupSquashSmall(SkipSpacesTabs(charPtr+1)); + break; + case 'W': + SetupWriteDistortions(SkipSpacesTabs(charPtr+1)); + break; + case 'U': + BSkipBlocks = FALSE; + break; + case 'Z': + IntraPBAllowed = FALSE; + break; + default: + fprintf(stderr, "Unknown tuning (%s) in parameter file.\n",charPtr); + break; + } +} + + diff --git a/converter/ppm/ppmtompeg/parallel.c b/converter/ppm/ppmtompeg/parallel.c new file mode 100644 index 00000000..021e6d2b --- /dev/null +++ b/converter/ppm/ppmtompeg/parallel.c @@ -0,0 +1,2278 @@ +/*===========================================================================* + * parallel.c + * + * Procedures to make encoder run in parallel + * + *===========================================================================*/ + +/* COPYRIGHT INFORMATION IS AT THE END OF THIS FILE */ + + +/*==============* + * HEADER FILES * + *==============*/ + +#define _XOPEN_SOURCE 500 /* Make sure stdio.h contains pclose() */ +/* _ALL_SOURCE is needed on AIX to make the C library include the + socket services (e.g. define struct sockaddr) + + Note that AIX standards.h actually sets feature declaration macros such + as _XOPEN_SOURCE, unless they are already set. +*/ +#define _ALL_SOURCE + +/* On AIX, pm_config.h includes standards.h, which expects to be included + after feature declaration macros such as _XOPEN_SOURCE. So we include + pm_config.h as late as possible. +*/ + + +#include <stdarg.h> +#include <time.h> +#include <unistd.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <signal.h> +#include <netdb.h> +#include <assert.h> +#include <sys/types.h> +#include <sys/times.h> + +#include "mallocvar.h" +#include "nstring.h" + +#include "pm.h" + +#include "all.h" +#include "param.h" +#include "mpeg.h" +#include "prototypes.h" +#include "readframe.h" +#include "fsize.h" +#include "combine.h" +#include "frames.h" +#include "input.h" +#include "psocket.h" +#include "frametype.h" +#include "gethostname.h" + +#include "parallel.h" + + +struct childState { + boolean finished; + unsigned int startFrame; + unsigned int numFrames; + unsigned int lastNumFrames; + unsigned int numSeconds; + float fps; +}; + + +struct scheduler { + /* This tracks the state of the subsystem that determines the assignments + for the children + */ + unsigned int nextFrame; + /* The next frame that needs to be assigned to a child */ + unsigned int numFramesInJob; + /* Total number of frames in the whole run of Ppmtompeg */ + unsigned int numMachines; +}; + + + +#define MAX_IO_SERVERS 10 +#ifndef SOMAXCONN +#define SOMAXCONN 5 +#endif + +/*==================* + * CONSTANTS * + *==================*/ + +#define TERMINATE_PID_SIGNAL SIGTERM /* signal used to terminate forked childs */ +#ifndef MAXARGS +#define MAXARGS 1024 /* Max Number of arguments in safe_fork command */ +#endif + +/*==================* + * STATIC VARIABLES * + *==================*/ + +static char rsh[256]; +static struct hostent *hostEntry = NULL; +static boolean *frameDone; +static int outputServerSocket; +static int decodeServerSocket; +static boolean parallelPerfect = FALSE; +static int current_max_forked_pid=0; + + +/*==================* + * GLOBAL VARIABLES * + *==================*/ + +extern int yuvHeight, yuvWidth; +extern char statFileName[256]; +extern FILE *statFile; +extern boolean debugMachines; +extern boolean debugSockets; +int parallelTestFrames = 10; +int parallelTimeChunks = 60; +const char *IOhostName; +int ioPortNumber; +int decodePortNumber; +boolean niceProcesses = FALSE; +boolean forceIalign = FALSE; +int machineNumber = -1; +boolean remoteIO = FALSE; +bool separateConversion; + /* The I/O server will convert from the input format to the base format, + and the slave will convert from the base format to the YUV internal + format. If false, the I/O server assumes the input format is the + base format and converts from the base format to the YUV internal + format; the slave does no conversion. + */ +time_t IOtime = 0; +extern char encoder_name[]; +int ClientPid[MAX_MACHINES+4]; + + +/*=====================* + * INTERNAL PROCEDURES * + *=====================*/ + + +static void PM_GNU_PRINTF_ATTR(1,2) +machineDebug(const char format[], ...) { + + va_list args; + + va_start(args, format); + + if (debugMachines) { + const char * const hostname = GetHostName(); + fprintf(stderr, "%s: ---", hostname); + strfree(hostname); + vfprintf(stderr, format, args); + fputc('\n', stderr); + } + va_end(args); +} + + + +static void PM_GNU_PRINTF_ATTR(1,2) +errorExit(const char format[], ...) { + + const char * const hostname = GetHostName(); + + va_list args; + + va_start(args, format); + + fprintf(stderr, "%s: FATAL ERROR. ", hostname); + strfree(hostname); + vfprintf(stderr, format, args); + fputc('\n', stderr); + + exit(1); + + va_end(args); +} + + + +static void +TransmitPortNum(const char * const hostName, + int const portNum, + int const newPortNum) { +/*---------------------------------------------------------------------------- + Transmit the port number 'newPortNum' to the master on port 'portNum' + of host 'hostName'. +-----------------------------------------------------------------------------*/ + int clientSocket; + const char * error; + + ConnectToSocket(hostName, portNum, &hostEntry, &clientSocket, &error); + + if (error) + errorExit("Can't connect in order to transmit port number. %s", + error); + + WriteInt(clientSocket, newPortNum); + + close(clientSocket); +} + + + +static void +readYUVDecoded(int const socketFd, + unsigned int const Fsize_x, + unsigned int const Fsize_y, + MpegFrame * const frameP) { + + unsigned int y; + + for (y = 0; y < Fsize_y; ++y) /* Y */ + ReadBytes(socketFd, + (unsigned char *)frameP->decoded_y[y], Fsize_x); + + for (y = 0; y < (Fsize_y >> 1); ++y) /* U */ + ReadBytes(socketFd, + (unsigned char *)frameP->decoded_cb[y], (Fsize_x >> 1)); + + for (y = 0; y < (Fsize_y >> 1); ++y) /* V */ + ReadBytes(socketFd, + (unsigned char *)frameP->decoded_cr[y], (Fsize_x >> 1)); +} + + + +static void +writeYUVDecoded(int const socketFd, + unsigned int const Fsize_x, + unsigned int const Fsize_y, + MpegFrame * const frameP) { + + unsigned int y; + + for (y = 0; y < Fsize_y; ++y) /* Y */ + WriteBytes(socketFd, + (unsigned char *)frameP->decoded_y[y], Fsize_x); + + for (y = 0; y < (Fsize_y >> 1); ++y) /* U */ + WriteBytes(socketFd, + (unsigned char *)frameP->decoded_cb[y], (Fsize_x >> 1)); + + for (y = 0; y < (Fsize_y >> 1); ++y) /* V */ + WriteBytes(socketFd, + (unsigned char *)frameP->decoded_cr[y], (Fsize_x >> 1)); +} + + + +static void +writeYUVOrig(int const socketFd, + unsigned int const Fsize_x, + unsigned int const Fsize_y, + MpegFrame * const frameP) { + + unsigned int y; + + for (y = 0; y < Fsize_y; ++y) /* Y */ + WriteBytes(socketFd, + (unsigned char *)frameP->orig_y[y], Fsize_x); + + for (y = 0; y < (Fsize_y >> 1); ++y) /* U */ + WriteBytes(socketFd, + (unsigned char *)frameP->orig_cb[y], (Fsize_x >> 1)); + + for (y = 0; y < (Fsize_y >> 1); ++y) /* V */ + WriteBytes(socketFd, + (unsigned char *)frameP->orig_cr[y], (Fsize_x >> 1)); +} + + + +static void +readYUVOrig(int const socketFd, + unsigned int const Fsize_x, + unsigned int const Fsize_y, + MpegFrame * const frameP) { + + unsigned int y; + + for (y = 0; y < Fsize_y; ++y) /* Y */ + ReadBytes(socketFd, + (unsigned char *)frameP->orig_y[y], Fsize_x); + + for (y = 0; y < (Fsize_y >> 1); ++y) /* U */ + ReadBytes(socketFd, + (unsigned char *)frameP->orig_cb[y], (Fsize_x >> 1)); + + for (y = 0; y < (Fsize_y >> 1); ++y) /* V */ + ReadBytes(socketFd, + (unsigned char *)frameP->orig_cr[y], (Fsize_x >> 1)); +} + + + +/*===========================================================================* + * + * EndIOServer + * + * called by the master process -- tells the I/O server to commit + * suicide + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +static void + EndIOServer() +{ + /* send signal to IO server: -1 as frame number */ + GetRemoteFrame(NULL, -1); +} + + +/*===========================================================================* + * + * NotifyDecodeServerReady + * + * called by a slave to the Decode Server to tell it a decoded frame + * is ready and waiting + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void +NotifyDecodeServerReady(int const id) { + + int clientSocket; + time_t tempTimeStart, tempTimeEnd; + const char * error; + + time(&tempTimeStart); + + ConnectToSocket(IOhostName, decodePortNumber, &hostEntry, &clientSocket, + &error); + + if (error) + errorExit("CHILD: Can't connect to decode server to tell it a frame " + "is ready. %s", error); + + WriteInt(clientSocket, id); + + close(clientSocket); + + time(&tempTimeEnd); + IOtime += (tempTimeEnd-tempTimeStart); +} + + + +/*===========================================================================* + * + * WaitForDecodedFrame + * + * blah blah blah + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void + WaitForDecodedFrame(id) +int id; +{ + int const negativeTwo = -2; + int clientSocket; + int ready; + const char * error; + + /* wait for a decoded frame */ + if ( debugSockets ) { + fprintf(stdout, "WAITING FOR DECODED FRAME %d\n", id); + } + + ConnectToSocket(IOhostName, decodePortNumber, &hostEntry, &clientSocket, + &error); + + if (error) + errorExit("CHILD: Can't connect to decode server " + "to get decoded frame. %s", + error); + + /* first, tell DecodeServer we're waiting for this frame */ + WriteInt(clientSocket, negativeTwo); + + WriteInt(clientSocket, id); + + ReadInt(clientSocket, &ready); + + if ( ! ready ) { + int waitSocket; + int waitPort; + int otherSock; + const char * error; + + /* it's not ready; set up a connection and wait for decode server */ + CreateListeningSocket(&waitSocket, &waitPort, &error); + if (error) + errorExit("Unable to create socket on which to listen for " + "decoded frame. %s", error); + + /* tell decode server where we are */ + WriteInt(clientSocket, machineNumber); + + WriteInt(clientSocket, waitPort); + + close(clientSocket); + + if ( debugSockets ) { + fprintf(stdout, "SLAVE: WAITING ON SOCKET %d\n", waitPort); + fflush(stdout); + } + + AcceptConnection(waitSocket, &otherSock, &error); + if (error) + errorExit("I/O SERVER: Failed to accept next connection. %s", error); + + /* should we verify this is decode server? */ + /* for now, we won't */ + + close(otherSock); + + close(waitSocket); + } else { + close(clientSocket); + } + + if ( debugSockets ) { + fprintf(stdout, "YE-HA FRAME %d IS NOW READY\n", id); + } +} + + +/*===========================================================================* + * + * SendDecodedFrame + * + * Send the frame to the decode server. + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void +SendDecodedFrame(MpegFrame * const frameP) { +/*---------------------------------------------------------------------------- + Send frame *frameP to the decode server. +-----------------------------------------------------------------------------*/ + int const negativeTwo = -2; + + int clientSocket; + const char * error; + + /* send to IOServer */ + ConnectToSocket(IOhostName, ioPortNumber, &hostEntry, + &clientSocket, &error); + if (error) + errorExit("CHILD: Can't connect to decode server to " + "give it a decoded frame. %s", error); + + WriteInt(clientSocket, negativeTwo); + + WriteInt(clientSocket, frameP->id); + + writeYUVDecoded(clientSocket, Fsize_x, Fsize_y, frameP); + + close(clientSocket); +} + + +/*===========================================================================* + * + * GetRemoteDecodedFrame + * + * get the decoded frame from the decode server. + * + * RETURNS: nothing + * + * SIDE EFFECTS: + * + *===========================================================================*/ +void +GetRemoteDecodedRefFrame(MpegFrame * const frameP, + int const frameNumber) { +/*---------------------------------------------------------------------------- + Get decoded frame number 'frameNumber' *frameP from the decode server. +-----------------------------------------------------------------------------*/ + int const negativeThree = -3; + int clientSocket; + const char * error; + + /* send to IOServer */ + ConnectToSocket(IOhostName, ioPortNumber, &hostEntry, + &clientSocket, &error); + if (error) + errorExit("CHILD: Can't connect to decode server " + "to get a decoded frame. %s", + error); + + /* ask IOServer for decoded frame */ + WriteInt(clientSocket, negativeThree); + + WriteInt(clientSocket, frameP->id); + + readYUVDecoded(clientSocket, Fsize_x, Fsize_y, frameP); + + close(clientSocket); +} + + +/********* + routines handling forks, execs, PIDs and signals + save, system-style forks + apian@ise.fhg.de + *******/ + + +/*===========================================================================* + * + * cleanup_fork + * + * Kill all the children, to be used when we get killed + * + * RETURNS: nothing + * + * SIDE EFFECTS: kills other processes + * + *===========================================================================*/ +static void cleanup_fork( dummy ) /* try to kill all child processes */ + int dummy; +{ + register int i; + for (i = 0; i < current_max_forked_pid; ++i ) { + +#ifdef DEBUG_FORK + fprintf(stderr, "cleanup_fork: killing PID %d\n", ClientPid[i]); +#endif + + if (kill(ClientPid[i], TERMINATE_PID_SIGNAL)) { + fprintf(stderr, "cleanup_fork: killed PID=%d failed (errno %d)\n", + ClientPid[i], errno); + } + } +} + +/*===========================================================================* + * + * safe_fork + * + * fork a command + * + * RETURNS: success/failure + * + * SIDE EFFECTS: Fork the command, and save to PID so you can kil it later! + * + *===========================================================================*/ +static int safe_fork(command) /* fork child process and remember its PID */ + char *command; +{ + static int init=0; + char *argis[MAXARGS]; + register int i=1; + + if (!(argis[0] = strtok(command, " \t"))) return(0); /* tokenize */ + while ((argis[i] = strtok(NULL, " \t")) && i < MAXARGS) ++i; + argis[i] = NULL; + +#ifdef DEBUG_FORK + {register int i=0; + fprintf(stderr, "Command %s becomes:\n", command); + while(argis[i]) {fprintf(stderr, "--%s--\n", argis[i]); ++i;} } +#endif + + if (!init) { /* register clean-up routine */ + signal (SIGQUIT, cleanup_fork); + signal (SIGTERM, cleanup_fork); + signal (SIGINT , cleanup_fork); + init=1; + } + + if (-1 == (ClientPid[current_max_forked_pid] = fork()) ) { + perror("safe_fork: fork failed "); + return(-1); + } + if( !ClientPid[current_max_forked_pid]) { /* we are in child process */ + execvp(argis[0], argis ); + perror("safe_fork child: exec failed "); + exit(1); + } +#ifdef DEBUG_FORK + fprintf(stderr, "parallel: forked PID=%d\n", ClientPid[current_max_forked_pid]); +#endif + current_max_forked_pid++; + return(0); +} + + +/*=====================* + * EXPORTED PROCEDURES * + *=====================*/ + + /*=================* + * IO SERVER STUFF * + *=================*/ + + +/*===========================================================================* + * + * SetIOConvert + * + * sets the IO conversion to be separate or not. If separate, then + * some post-processing is done at slave end + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void +SetIOConvert(bool const separate) { + separateConversion = separate; +} + + +/*===========================================================================* + * + * SetParallelPerfect + * + * If this is called, then frames will be divided up completely, and + * evenly (modulo rounding) between all the processors + * + * RETURNS: nothing + * + * SIDE EFFECTS: Sets parallelPerfect .... + * + *===========================================================================*/ +void +SetParallelPerfect(val) +boolean val; +{ + parallelPerfect = val; +} + + +/*===========================================================================* + * + * SetRemoteShell + * + * sets the remote shell program (usually rsh, but different on some + * machines) + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void +SetRemoteShell(const char * const shell) { + strcpy(rsh, shell); +} + + +static void +decodedFrameToDisk(int const otherSock) { +/*---------------------------------------------------------------------------- + Get a decoded from from socket 'otherSock' and write it to disk. +-----------------------------------------------------------------------------*/ + int frameNumber; + MpegFrame * frameP; + + ReadInt(otherSock, &frameNumber); + + if (debugSockets) { + fprintf(stdout, "INPUT SERVER: GETTING DECODED FRAME %d\n", + frameNumber); + fflush(stdout); + } + + /* should read frame from socket, then write to disk */ + frameP = Frame_New(frameNumber, 'i'); + + Frame_AllocDecoded(frameP, TRUE); + + readYUVDecoded(otherSock, Fsize_x, Fsize_y, frameP); + + /* now output to disk */ + WriteDecodedFrame(frameP); + + Frame_Free(frameP); +} + + + +static void +decodedFrameFromDisk(int const otherSock) { + + /* request for decoded frame from disk */ + + int frameNumber; + MpegFrame * frameP; + + ReadInt(otherSock, &frameNumber); + + if (debugSockets) { + fprintf(stdout, "INPUT SERVER: READING DECODED FRAME %d " + "from DISK\n", frameNumber); + fflush(stdout); + } + + /* should read frame from disk, then write to socket */ + frameP = Frame_New(frameNumber, 'i'); + + Frame_AllocDecoded(frameP, TRUE); + + ReadDecodedRefFrame(frameP, frameNumber); + + writeYUVDecoded(otherSock, Fsize_x, Fsize_y, frameP); + + Frame_Free(frameP); +} + + + +static void +routeFromSocketToDisk(int const otherSock, + unsigned char ** const bigBufferP, + unsigned int * const bigBufferSizeP) { + + /* routing output frame from socket to disk */ + + int frameNumber; + int numBytes; + unsigned char * bigBuffer; + unsigned int bigBufferSize; + const char * fileName; + FILE * filePtr; + + bigBuffer = *bigBufferP; + bigBufferSize = *bigBufferSizeP; + + ReadInt(otherSock, &frameNumber); + ReadInt(otherSock, &numBytes); + + /* Expand bigBuffer if necessary to fit this frame */ + if (numBytes > bigBufferSize) { + bigBufferSize = numBytes; + if (bigBuffer != NULL) + free(bigBuffer); + + MALLOCARRAY_NOFAIL(bigBuffer, bigBufferSize); + } + + /* now read in the bytes */ + ReadBytes(otherSock, bigBuffer, numBytes); + + /* open file to output this stuff to */ + asprintfN(&fileName, "%s.frame.%d", outputFileName, frameNumber); + filePtr = fopen(fileName, "wb"); + + if (filePtr == NULL) + errorExit("I/O SERVER: Could not open output file(3): %s", fileName); + + strfree(fileName); + + /* now write the bytes here */ + fwrite(bigBuffer, sizeof(char), numBytes, filePtr); + + fclose(filePtr); + + if (debugSockets) { + fprintf(stdout, "====I/O SERVER: WROTE FRAME %d to disk\n", + frameNumber); + fflush(stdout); + } + + *bigBufferP = bigBuffer; + *bigBufferSizeP = bigBufferSize; +} + + + +static void +readConvertWriteToSocket(struct inputSource * const inputSourceP, + int const otherSock, + int const frameNumber, + bool * const endOfStreamP) { +/*---------------------------------------------------------------------------- + Get the frame numbered 'frameNumber' from input source + *inputSourceP, apply format conversion User requested, and write + the "base format" result to socket 'otherSock'. +-----------------------------------------------------------------------------*/ + FILE * convertedFileP; + + convertedFileP = ReadIOConvert(inputSourceP, frameNumber); + if (convertedFileP) { + bool eof; + eof = FALSE; /* initial value */ + while (!eof) { + unsigned char buffer[1024]; + unsigned int numBytes; + + numBytes = fread(buffer, 1, sizeof(buffer), convertedFileP); + + if (numBytes > 0) { + WriteInt(otherSock, numBytes); + WriteBytes(otherSock, buffer, numBytes); + } else + eof = TRUE; + } + + if (strcmp(ioConversion, "*") == 0 ) + fclose(convertedFileP); + else + pclose(convertedFileP); + + *endOfStreamP = FALSE; + } else + *endOfStreamP = TRUE; +} + + + +static void +readWriteYuvToSocket(struct inputSource * const inputSourceP, + int const otherSock, + int const frameNumber, + bool * const endOfStreamP) { +/*---------------------------------------------------------------------------- + Read Frame number 'frameNumber' from the input source *inputSourceP, + assuming it is in base format, and write its contents in YUV format + to socket 'otherSock'. + + Wait for acknowledgement that consumer has received it. +-----------------------------------------------------------------------------*/ + MpegFrame * frameP; + + frameP = Frame_New(frameNumber, 'i'); + + ReadFrame(frameP, inputSourceP, frameNumber, inputConversion, + endOfStreamP); + + if (!*endOfStreamP) { + writeYUVOrig(otherSock, Fsize_x, Fsize_y, frameP); + + { + /* Make sure we don't leave until other processor read + everything + */ + int dummy; + ReadInt(otherSock, &dummy); + assert(dummy == 0); + } + } + Frame_Free(frameP); +} + + + +static void +readFrameWriteToSocket(struct inputSource * const inputSourceP, + int const otherSock, + int const frameNumber, + bool * const endOfStreamP) { +/*---------------------------------------------------------------------------- + Read Frame number 'frameNumber' from the input source *inputSourceP + and write it to socket 'otherSock'. +-----------------------------------------------------------------------------*/ + if (debugSockets) { + fprintf(stdout, "I/O SERVER GETTING FRAME %d\n", frameNumber); + fflush(stdout); + } + + if (separateConversion) + readConvertWriteToSocket(inputSourceP, otherSock, frameNumber, + endOfStreamP); + else + readWriteYuvToSocket(inputSourceP, otherSock, frameNumber, + endOfStreamP); + + if (debugSockets) { + fprintf(stdout, "====I/O SERVER: READ FRAME %d\n", frameNumber); + } +} + + + +static void +processNextConnection(int const serverSocket, + struct inputSource * const inputSourceP, + bool * const doneP, + unsigned char ** const bigBufferP, + unsigned int * const bigBufferSizeP) { + + int otherSock; + int command; + const char * error; + + AcceptConnection(serverSocket, &otherSock, &error); + if (error) + errorExit("I/O SERVER: Failed to accept next connection. %s", error); + + ReadInt(otherSock, &command); + + switch (command) { + case -1: + *doneP = TRUE; + break; + case -2: + decodedFrameToDisk(otherSock); + break; + case -3: + decodedFrameFromDisk(otherSock); + break; + case -4: + routeFromSocketToDisk(otherSock, bigBufferP, bigBufferSizeP); + break; + default: { + unsigned int const frameNumber = command; + + bool endOfStream; + + readFrameWriteToSocket(inputSourceP, otherSock, frameNumber, + &endOfStream); + + if (endOfStream) { + /* We don't do anything. Closing the socket with having written + anything is our signal that there is no more input. + + (Actually, Ppmtompeg cannot handle stream input in parallel + mode -- this code is just infrastructure so maybe it can some + day). + */ + } + } + } + close(otherSock); +} + + + +void +IoServer(struct inputSource * const inputSourceP, + const char * const parallelHostName, + int const portNum) { +/*---------------------------------------------------------------------------- + Execute an I/O server. + + An I/O server is the partner on the master machine of a child process + on a "remote" system. "Remote" here doesn't just mean on another system. + It means on a system that isn't even in the same cluster -- specifically, + a system that doesn't have access to the same filesystem as the master. + + The child process passes frame contents between it and the master via + the I/O server. +-----------------------------------------------------------------------------*/ + int ioPortNum; + int serverSocket; + boolean done; + unsigned char *bigBuffer; + /* A work buffer that we keep around permanently. We increase + its size as needed, but never shrink it. + */ + unsigned int bigBufferSize; + /* The current allocated size of bigBuffer[] */ + const char * error; + + bigBufferSize = 0; /* Start with no buffer */ + bigBuffer = NULL; + + /* once we get IO port num, should transmit it to parallel server */ + + CreateListeningSocket(&serverSocket, &ioPortNum, &error); + if (error) + errorExit("Unable to create socket on which to listen for " + "reports from children. %s", error); + + if (debugSockets) + fprintf(stdout, "====I/O USING PORT %d\n", ioPortNum); + + TransmitPortNum(parallelHostName, portNum, ioPortNum); + + if (separateConversion) + SetFileType(ioConversion); /* for reading */ + else + SetFileType(inputConversion); + + done = FALSE; /* initial value */ + + while (!done) + processNextConnection(serverSocket, inputSourceP, + &done, &bigBuffer, &bigBufferSize); + + close(serverSocket); + + if ( debugSockets ) { + fprintf(stdout, "====I/O SERVER: Shutting Down\n"); + } +} + + + +/*===========================================================================* + * + * SendRemoteFrame + * + * called by a slave to the I/O server; sends an encoded frame + * to the server to be sent to disk + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void +SendRemoteFrame(int const frameNumber, BitBucket * const bb) { + + int const negativeFour = -4; + int clientSocket; + time_t tempTimeStart, tempTimeEnd; + const char * error; + + time(&tempTimeStart); + + ConnectToSocket(IOhostName, ioPortNumber, &hostEntry, + &clientSocket, &error); + if (error) + errorExit("CHILD: Can't connect to I/O server to deliver results. %s", + error); + + WriteInt(clientSocket, negativeFour); + + WriteInt(clientSocket, frameNumber); + + if (frameNumber != -1) { + /* send number of bytes */ + + WriteInt(clientSocket, (bb->totalbits+7)>>3); + + /* now send the bytes themselves */ + Bitio_WriteToSocket(bb, clientSocket); + } + + close(clientSocket); + + time(&tempTimeEnd); + IOtime += (tempTimeEnd-tempTimeStart); +} + + + +void +GetRemoteFrame(MpegFrame * const frameP, + int const frameNumber) { +/*---------------------------------------------------------------------------- + Get a frame from the I/O server. + + This is intended for use by a child. +-----------------------------------------------------------------------------*/ + int clientSocket; + const char * error; + + Fsize_Note(frameNumber, yuvWidth, yuvHeight); + + if (debugSockets) { + fprintf(stdout, "MACHINE %s REQUESTING connection for FRAME %d\n", + getenv("HOST"), frameNumber); + fflush(stdout); + } + + ConnectToSocket(IOhostName, ioPortNumber, &hostEntry, + &clientSocket, &error); + + if (error) + errorExit("CHILD: Can't connect to I/O server to get a frame. %s", + error); + + WriteInt(clientSocket, frameNumber); + + if (frameNumber != -1) { + if (separateConversion) { + unsigned char buffer[1024]; + /* This is by design the exact size of the data per message (except + the last message for a frame) the I/O server sends. + */ + int numBytes; /* Number of data bytes in message */ + FILE * filePtr = pm_tmpfile(); + + /* read in stuff, write to file, perform local conversion */ + do { + ReadInt(clientSocket, &numBytes); + + if (numBytes > sizeof(buffer)) + errorExit("Invalid message received: numBytes = %d, " + "which is greater than %d\n", + numBytes, sizeof(numBytes)); + ReadBytes(clientSocket, buffer, numBytes); + + fwrite(buffer, 1, numBytes, filePtr); + } while ( numBytes == sizeof(buffer) ); + fflush(filePtr); + { + bool endOfStream; + rewind(filePtr); + /* I/O Server gave us base format. Read it as an MpegFrame */ + ReadFrameFile(frameP, filePtr, slaveConversion, &endOfStream); + assert(!endOfStream); + } + fclose(filePtr); + } else { + Frame_AllocYCC(frameP); + + if (debugSockets) { + fprintf(stdout, "MACHINE %s allocated YCC FRAME %d\n", + getenv("HOST"), frameNumber); + fflush(stdout); + } + /* I/O Server gave us internal YUV format. Read it as MpegFrame */ + readYUVOrig(clientSocket, yuvWidth, yuvHeight, frameP); + } + } + + WriteInt(clientSocket, 0); + + close(clientSocket); + + if (debugSockets) { + fprintf(stdout, "MACHINE %s READ COMPLETELY FRAME %d\n", + getenv("HOST"), frameNumber); + fflush(stdout); + } +} + + +struct combineControl { + unsigned int numFrames; +}; + + + + +static void +getAndProcessACombineConnection(int const outputServerSocket) { + int otherSock; + int command; + const char * error; + + AcceptConnection(outputServerSocket, &otherSock, &error); + + if (error) + errorExit("COMBINE SERVER: " + "Failed to accept next connection. %s", error); + + ReadInt(otherSock, &command); + + if (command == -2) { + /* this is notification from non-remote process that a + frame is done. + */ + int frameStart, frameEnd; + + ReadInt(otherSock, &frameStart); + ReadInt(otherSock, &frameEnd); + + machineDebug("COMBINE_SERVER: Frames %d - %d done", + frameStart, frameEnd); + { + unsigned int i; + for (i = frameStart; i <= frameEnd; ++i) + frameDone[i] = TRUE; + } + } else + errorExit("COMBINE SERVER: Unrecognized command %d received.", + command); + + close(otherSock); +} + + + +#define READ_ATTEMPTS 5 /* number of times (seconds) to retry an input file */ + + +static void +openInputFile(const char * const fileName, + FILE ** const inputFilePP) { + + FILE * inputFileP; + unsigned int attempts; + + inputFileP = NULL; + attempts = 0; + + while (!inputFileP && attempts < READ_ATTEMPTS) { + inputFileP = fopen(fileName, "rb"); + if (inputFileP == NULL) { + pm_message("ERROR Couldn't read frame file '%s' errno = %d (%s)" + "attempt %d", + fileName, errno, strerror(errno), attempts); + sleep(1); + } + ++attempts; + } + if (inputFileP == NULL) + pm_error("Unable to open file '%s' after %d attempts.", + fileName, attempts); + + *inputFilePP = inputFileP; +} + + + +static void +waitForOutputFile(void * const inputHandle, + unsigned int const frameNumber, + FILE ** const ifPP) { +/*---------------------------------------------------------------------------- + Keep handling output events until we get the specified frame number. + Open the file it's in and return the stream handle. +-----------------------------------------------------------------------------*/ + struct combineControl * const combineControlP = (struct combineControl *) + inputHandle; + + if (frameNumber >= combineControlP->numFrames) + *ifPP = NULL; + else { + const char * fileName; + + while (!frameDone[frameNumber]) { + machineDebug("COMBINE_SERVER: Waiting for frame %u done", + frameNumber); + + getAndProcessACombineConnection(outputServerSocket); + } + machineDebug("COMBINE SERVER: Wait for frame %u over", frameNumber); + + asprintfN(&fileName, "%s.frame.%u", outputFileName, frameNumber); + + openInputFile(fileName, ifPP); + + strfree(fileName); + } +} + + + +static void +unlinkFile(void * const inputHandle, + unsigned int const frameNumber) { + + if (!keepTempFiles) { + const char * fileName; + + asprintfN(&fileName, "%s.frame.%u", outputFileName, frameNumber); + + unlink(fileName); + + strfree(fileName); + } +} + + + +void +CombineServer(int const numFrames, + const char * const masterHostName, + int const masterPortNum, + const char * const outputFileName) { +/*---------------------------------------------------------------------------- + Execute a combine server. + + This handles combination of frames. +-----------------------------------------------------------------------------*/ + int combinePortNum; + FILE * ofP; + const char * error; + struct combineControl combineControl; + + /* once we get Combine port num, should transmit it to parallel server */ + + CreateListeningSocket(&outputServerSocket, &combinePortNum, &error); + if (error) + errorExit("Unable to create socket on which to listen. %s", error); + + machineDebug("COMBINE SERVER: LISTENING ON PORT %d", combinePortNum); + + TransmitPortNum(masterHostName, masterPortNum, combinePortNum); + + MALLOCARRAY_NOFAIL(frameDone, numFrames); + { + unsigned int i; + for (i = 0; i < numFrames; ++i) + frameDone[i] = FALSE; + } + ofP = pm_openw(outputFileName); + + combineControl.numFrames = numFrames; + + FramesToMPEG(ofP, &combineControl, &waitForOutputFile, &unlinkFile); + + machineDebug("COMBINE SERVER: Shutting down"); + + /* tell Master server we are done */ + TransmitPortNum(masterHostName, masterPortNum, combinePortNum); + + close(outputServerSocket); + + fclose(ofP); +} + + +/*=====================* + * MASTER SERVER STUFF * + *=====================*/ + + +static void +startCombineServer(const char * const encoderName, + unsigned int const numMachines, + const char * const masterHostName, + int const masterPortNum, + unsigned int const numInputFiles, + const char * const paramFileName, + int const masterSocket, + int * const combinePortNumP) { + + char command[1024]; + int otherSock; + const char * error; + + snprintf(command, sizeof(command), + "%s %s -max_machines %d -output_server %s %d %d %s", + encoderName, + debugMachines ? "-debug_machines" : "", + numMachines, masterHostName, masterPortNum, + numInputFiles, paramFileName); + + machineDebug("MASTER: Starting combine server with shell command '%s'", + command); + + safe_fork(command); + + machineDebug("MASTER: Listening for connection back from " + "new Combine server"); + + AcceptConnection(masterSocket, &otherSock, &error); + if (error) + errorExit("MASTER SERVER: " + "Failed to accept next connection. %s", error); + + ReadInt(otherSock, combinePortNumP); + close(otherSock); + + machineDebug("MASTER: Combine port number = %d", *combinePortNumP); +} + + + +static void +startDecodeServer(const char * const encoderName, + unsigned int const numMachines, + const char * const masterHostName, + int const masterPortNum, + unsigned int const numInputFiles, + const char * const paramFileName, + int const masterSocket, + int * const decodePortNumP) { + + char command[1024]; + int otherSock; + const char * error; + + snprintf(command, sizeof(command), + "%s %s -max_machines %d -decode_server %s %d %d %s", + encoder_name, + debugMachines ? "-debug_machines" : "", + numMachines, masterHostName, masterPortNum, + numInputFiles, paramFileName); + + machineDebug("MASTER: Starting decode server with shell command '%s'", + command); + + safe_fork(command); + + machineDebug("MASTER: Listening for connection back from " + "new Decode server"); + + AcceptConnection(masterSocket, &otherSock, &error); + if (error) + errorExit("MASTER SERVER: " + "Failed to accept connection back from the new " + "decode server. %s", error); + + ReadInt(otherSock, decodePortNumP); + + close(otherSock); + + machineDebug("MASTER: Decode port number = %d", *decodePortNumP); +} + + + +static void +startIoServer(const char * const encoderName, + unsigned int const numChildren, + const char * const masterHostName, + int const masterPortNum, + int const masterSocket, + const char * const paramFileName, + int * const ioPortNumP) { + + char command[1024]; + int otherSock; + const char * error; + + sprintf(command, "%s -max_machines %d -io_server %s %d %s", + encoderName, numChildren, masterHostName, masterPortNum, + paramFileName); + + machineDebug("MASTER: Starting I/O server with remote shell command '%s'", + command); + + safe_fork(command); + + machineDebug("MASTER: Listening for connection back from " + "new I/O server"); + + AcceptConnection(masterSocket, &otherSock, &error); + if (error) + errorExit("MASTER SERVER: " + "Failed to accept connection back from the new " + "I/O server. %s", error); + + ReadInt(otherSock, ioPortNumP); + close(otherSock); + + machineDebug("MASTER: I/O port number = %d", *ioPortNumP); +} + + + +static void +extendToEndOfPattern(unsigned int * const nFramesP, + unsigned int const startFrame, + unsigned int const framePatternLen, + unsigned int const numFramesInStream) { + + assert(framePatternLen >= 1); + + while (startFrame + *nFramesP < numFramesInStream && + (startFrame + *nFramesP) % framePatternLen != 0) + ++(*nFramesP); +} + + + +static void +allocateInitialFrames(struct scheduler * const schedulerP, + boolean const parallelPerfect, + boolean const forceIalign, + unsigned int const framePatternLen, + unsigned int const parallelTestFrames, + unsigned int const childNum, + unsigned int * const startFrameP, + unsigned int * const nFramesP) { +/*---------------------------------------------------------------------------- + Choose which frames, to hand out to the new child numbered 'childNum'. +-----------------------------------------------------------------------------*/ + unsigned int const framesPerChild = + MAX(1, ((schedulerP->numFramesInJob - schedulerP->nextFrame) / + (schedulerP->numMachines - childNum))); + + unsigned int nFrames; + + if (parallelPerfect) + nFrames = framesPerChild; + else { + assert(parallelTestFrames >= 1); + + nFrames = MIN(parallelTestFrames, framesPerChild); + } + if (forceIalign) + extendToEndOfPattern(&nFrames, schedulerP->nextFrame, + framePatternLen, schedulerP->numFramesInJob); + + nFrames = MIN(nFrames, schedulerP->numFramesInJob - schedulerP->nextFrame); + + *startFrameP = schedulerP->nextFrame; + *nFramesP = nFrames; + schedulerP->nextFrame += nFrames; +} + + + +static float +taperedGoalTime(struct childState const childState[], + unsigned int const remainingFrameCount) { + + float goalTime; + float allChildrenFPS; + float remainingJobTime; + /* How long we expect it to be before the whole movie is encoded*/ + float sum; + int numMachinesToEstimate; + unsigned int childNum; + + /* frames left = lastFrameInStream - startFrame + 1 */ + for (childNum = 0, sum = 0.0, numMachinesToEstimate = 0; + childNum < numMachines; ++childNum) { + if (!childState[childNum].finished) { + if (childState[childNum].fps < 0.0 ) + ++numMachinesToEstimate; + else + sum += childState[childNum].fps; + } + } + + allChildrenFPS = (float)numMachines * + (sum/(float)(numMachines-numMachinesToEstimate)); + + remainingJobTime = (float)remainingFrameCount/allChildrenFPS; + + goalTime = MAX(5.0, remainingJobTime/2); + + return goalTime; +} + + + +static void +allocateMoreFrames(struct scheduler * const schedulerP, + unsigned int const childNum, + struct childState const childState[], + bool const forceIalign, + unsigned int const framePatternLen, + bool const goalTimeSpecified, + unsigned int const goalTimeArg, + unsigned int * const startFrameP, + unsigned int * const nFramesP) { +/*---------------------------------------------------------------------------- + Decide which frames should be child 'childNum''s next assignment, + given the state/history of all children is childState[]. + + The lowest numbered frame which needs yet to be encoded is frame + number 'startFrame' and 'lastFrameInStream' is the highest. + + The allocation always starts at the lowest numbered frame that + hasn't yet been allocated and is sequential. We return as + *startFrameP the frame number of the first frame in the allocation + and as *nFramesP the number of frames. + + If 'goalTimeSpecified' is true, we try to make the assignment take + 'goalTimeArg' seconds. If 'goalTimeSpecified' is not true, we choose + a goal time ourselves, which is based on how long we think it will + take for all the children to finish all the remaining frames. +-----------------------------------------------------------------------------*/ + float goalTime; + /* Number of seconds we want the assignment to take. We size the + assignment to try to meet this goal. + */ + unsigned int nFrames; + float avgFps; + + if (!goalTimeSpecified) { + goalTime = taperedGoalTime(childState, + schedulerP->numFramesInJob - + schedulerP->nextFrame); + + pm_message("MASTER: ASSIGNING %s %.2f seconds of work", + machineName[childNum], goalTime); + } else + goalTime = goalTimeArg; + + if (childState[childNum].numSeconds != 0) + avgFps = (float)childState[childNum].numFrames / + childState[childNum].numSeconds; + else + avgFps = 0.1; /* arbitrary small value */ + + nFrames = MAX(1u, (unsigned int)(goalTime * avgFps + 0.5)); + + nFrames = MIN(nFrames, + schedulerP->numFramesInJob - schedulerP->nextFrame); + + if (forceIalign) + extendToEndOfPattern(&nFrames, schedulerP->nextFrame, + framePatternLen, schedulerP->numFramesInJob); + + *startFrameP = schedulerP->nextFrame; + *nFramesP = nFrames; + schedulerP->nextFrame += nFrames; +} + + + +static void +startChildren(struct scheduler * const schedulerP, + const char * const encoderName, + const char * const masterHostName, + int const masterPortNum, + const char * const paramFileName, + boolean const parallelPerfect, + boolean const forceIalign, + unsigned int const framePatternLen, + unsigned int const parallelTestFrames, + boolean const beNice, + int const masterSocket, + int const combinePortNum, + int const decodePortNum, + int * const ioPortNum, + unsigned int * const numIoServersP, + struct childState ** const childStateP) { +/*---------------------------------------------------------------------------- + Start up the children. Tell them to work for the master at + 'masterHostName':'masterPortNum'. + + Start I/O servers (as processes on this system) as required and return + the port numbers of the TCP ports on which they listen as + ioPortNum[] and the number of them as *numIoServersP. + + Give each of the children some initial work to do. This may be just + a small amount for timing purposes. + + We access and manipulate the various global variables that represent + the state of the children, and the scheduler structure. +-----------------------------------------------------------------------------*/ + struct childState * childState; /* malloc'ed */ + unsigned int childNum; + unsigned int numIoServers; + unsigned int childrenLeftCurrentIoServer; + /* The number of additional children we can hook up to the + current I/O server before reaching our maximum children per + I/O server. 0 if there is no current I/O server. + */ + + MALLOCARRAY_NOFAIL(childState, schedulerP->numMachines); + + childrenLeftCurrentIoServer = 0; /* No current I/O server yet */ + + numIoServers = 0; /* None created yet */ + + for (childNum = 0; childNum < schedulerP->numMachines; ++childNum) { + char command[1024]; + unsigned int startFrame; + unsigned int nFrames; + + childState[childNum].fps = -1.0; /* illegal value as flag */ + childState[childNum].numSeconds = 0; + + allocateInitialFrames(schedulerP, parallelPerfect, forceIalign, + framePatternLen, parallelTestFrames, + childNum, &startFrame, &nFrames); + + if (nFrames == 0) { + childState[childNum].finished = TRUE; + machineDebug("MASTER: No more frames; not starting child '%s'", + machineName[childNum]); + } else { + childState[childNum].finished = FALSE; + + if (remote[childNum]) { + if (childrenLeftCurrentIoServer == 0) { + startIoServer(encoderName, schedulerP->numMachines, + masterHostName, masterPortNum, masterSocket, + paramFileName, &ioPortNum[numIoServers++]); + + childrenLeftCurrentIoServer = SOMAXCONN; + } + --childrenLeftCurrentIoServer; + } + snprintf(command, sizeof(command), + "%s %s -l %s %s " + "%s %s -child %s %d %d %d %d %d %d " + "-frames %d %d %s", + rsh, + machineName[childNum], userName[childNum], + beNice ? "nice" : "", + executable[childNum], + debugMachines ? "-debug_machines" : "", + masterHostName, masterPortNum, + remote[childNum] ? ioPortNum[numIoServers-1] : 0, + combinePortNum, decodePortNum, childNum, + remote[childNum] ? 1 : 0, + startFrame, startFrame + nFrames - 1, + remote[childNum] ? + remoteParamFile[childNum] : paramFileName + ); + + machineDebug("MASTER: Starting child server " + "with shell command '%s'", command); + + safe_fork(command); + + machineDebug("MASTER: Frames %d-%d assigned to new child %s", + startFrame, startFrame + nFrames - 1, + machineName[childNum]); + } + childState[childNum].startFrame = startFrame; + childState[childNum].lastNumFrames = nFrames; + childState[childNum].numFrames = childState[childNum].lastNumFrames; + } + *childStateP = childState; + *numIoServersP = numIoServers; +} + + + +static void +noteFrameDone(const char * const combineHostName, + int const combinePortNum, + unsigned int const frameStart, + unsigned int const frameEnd) { +/*---------------------------------------------------------------------------- + Tell the Combine server that frames 'frameStart' through 'frameEnd' + are done. +-----------------------------------------------------------------------------*/ + int const negativeTwo = -2; + int clientSocket; + time_t tempTimeStart, tempTimeEnd; + const char * error; + struct hostent * hostEntP; + + time(&tempTimeStart); + + hostEntP = NULL; + + ConnectToSocket(combineHostName, combinePortNum, &hostEntP, + &clientSocket, &error); + + if (error) + errorExit("MASTER: Can't connect to Combine server to tell it frames " + "are done. %s", error); + + WriteInt(clientSocket, negativeTwo); + + WriteInt(clientSocket, frameStart); + + WriteInt(clientSocket, frameEnd); + + close(clientSocket); + + time(&tempTimeEnd); + IOtime += (tempTimeEnd-tempTimeStart); +} + + + +static void +feedTheChildren(struct scheduler * const schedulerP, + struct childState childState[], + int const masterSocket, + const char * const combineHostName, + int const combinePortNum, + bool const forceIalign, + unsigned int const framePatternLen, + bool const goalTimeSpecified, + unsigned int const goalTime) { +/*---------------------------------------------------------------------------- + Listen for children to tell us they have finished their assignments + and give them new assignments, until all the frames have been assigned + and all the children have finished. + + As children finish assignments, inform the combine server at + 'combineHostName':'combinePortNum' of such. + + Note that the children got initial assigments when they were created. + So the first thing we do is wait for them to finish those. +-----------------------------------------------------------------------------*/ + unsigned int numFinished; + /* Number of child machines that have been excused because there + is no more work for them. + */ + unsigned int framesDone; + + numFinished = 0; + framesDone = 0; + + while (numFinished != schedulerP->numMachines) { + int otherSock; + int childNum; + int seconds; + float framesPerSecond; + struct childState * csP; + const char * error; + unsigned int nextFrame; + unsigned int nFrames; + + machineDebug("MASTER: Listening for a connection..."); + + AcceptConnection(masterSocket, &otherSock, &error); + if (error) + errorExit("MASTER SERVER: " + "Failed to accept next connection. %s", error); + + ReadInt(otherSock, &childNum); + ReadInt(otherSock, &seconds); + + csP = &childState[childNum]; + + csP->numSeconds += seconds; + csP->fps = (float)csP->numFrames / (float)csP->numSeconds; + + if (seconds != 0) + framesPerSecond = (float)csP->lastNumFrames / (float)seconds; + else + framesPerSecond = (float)csP->lastNumFrames * 2.0; + + machineDebug("MASTER: Child %s FINISHED ASSIGNMENT. " + "%f frames per second", + machineName[childNum], framesPerSecond); + + noteFrameDone(combineHostName, combinePortNum, csP->startFrame, + csP->startFrame + csP->lastNumFrames - 1); + + framesDone += csP->lastNumFrames; + + allocateMoreFrames(schedulerP, childNum, childState, + forceIalign, framePatternLen, + goalTimeSpecified, goalTime, + &nextFrame, &nFrames); + + if (nFrames == 0) { + WriteInt(otherSock, -1); + WriteInt(otherSock, 0); + + ++numFinished; + + machineDebug("MASTER: NO MORE WORK FOR CHILD %s. " + "(%d of %d children now done)", + machineName[childNum], numFinished, numMachines); + } else { + WriteInt(otherSock, nextFrame); + WriteInt(otherSock, nextFrame + nFrames - 1); + + machineDebug("MASTER: Frames %d-%d assigned to child %s", + nextFrame, nextFrame + nFrames - 1, + machineName[childNum]); + + csP->startFrame = nextFrame; + csP->lastNumFrames = nFrames; + csP->numFrames += csP->lastNumFrames; + } + close(otherSock); + + machineDebug("MASTER: %d/%d DONE; %d ARE ASSIGNED", + framesDone, schedulerP->numFramesInJob, + schedulerP->nextFrame - framesDone); + } +} + + + +static void +stopIoServers(const char * const hostName, + int const ioPortNum[], + unsigned int const numIoServers) { + + unsigned int childNum; + + IOhostName = hostName; + for (childNum = 0; childNum < numIoServers; ++childNum) { + ioPortNumber = ioPortNum[childNum]; + EndIOServer(); + } +} + + + +static void +waitForCombineServerToTerminate(int const masterSocket) { + + int otherSock; + const char * error; + + machineDebug("MASTER SERVER: Waiting for combine server to terminate"); + + AcceptConnection(masterSocket, &otherSock, &error); + if (error) + errorExit("MASTER SERVER: " + "Failed to accept connection expected from a " + "terminating combine server. %s", error); + + { + int dummy; + ReadInt(otherSock, &dummy); + } + close(otherSock); +} + + + +static void +printFinalStats(FILE * const statfileP, + time_t const startUpBegin, + time_t const startUpEnd, + time_t const shutDownBegin, + time_t const shutDownEnd, + unsigned int const numChildren, + struct childState const childState[], + unsigned int const numFrames) { + + unsigned int pass; + FILE * fileP; + + for (pass = 0; pass < 2; ++pass) { + if (pass == 0) + fileP = stdout; + else + fileP = statfileP; + + if (fileP) { + unsigned int childNum; + float totalFPS; + + fprintf(fileP, "\n\n"); + fprintf(fileP, "PARALLEL SUMMARY\n"); + fprintf(fileP, "----------------\n"); + fprintf(fileP, "\n"); + fprintf(fileP, "START UP TIME: %u seconds\n", + (unsigned int)(startUpEnd - startUpBegin)); + fprintf(fileP, "SHUT DOWN TIME: %u seconds\n", + (unsigned int)(shutDownEnd - shutDownBegin)); + + fprintf(fileP, + "%14.14s %8.8s %8.8s %12.12s %9.9s\n", + "MACHINE", "Frames", "Seconds", "Frames/Sec", + "Self Time"); + + fprintf(fileP, + "%14.14s %8.8s %8.8s %12.12s %9.9s\n", + "--------------", "--------", "--------", "------------", + "---------"); + + totalFPS = 0.0; + for (childNum = 0; childNum < numChildren; ++childNum) { + float const localFPS = + (float)childState[childNum].numFrames / + childState[childNum].numSeconds; + fprintf(fileP, "%14.14s %8u %8u %12.4f %8u\n", + machineName[childNum], + childState[childNum].numFrames, + childState[childNum].numSeconds, + localFPS, + (unsigned int)((float)numFrames/localFPS)); + totalFPS += localFPS; + } + + fprintf(fileP, + "%14.14s %8.8s %8.8s %12.12s %9.9s\n", + "--------------", "--------", "--------", "------------", + "---------"); + + fprintf(fileP, "%14s %8.8s %8u %12.4f\n", + "OPTIMAL", "", + (unsigned int)((float)numFrames/totalFPS), + totalFPS); + + { + unsigned int const diffTime = shutDownEnd - startUpBegin; + + fprintf(fileP, "%14s %8.8s %8u %12.4f\n", + "ACTUAL", "", diffTime, + (float)numFrames / diffTime); + } + fprintf(fileP, "\n\n"); + } + } +} + + + +void +MasterServer(struct inputSource * const inputSourceP, + const char * const paramFileName, + const char * const outputFileName) { +/*---------------------------------------------------------------------------- + Execute the master server function. + + Start all the other servers. +-----------------------------------------------------------------------------*/ + const char *hostName; + int portNum; + int masterSocket; + /* The file descriptor for the socket on which the master listens */ + int ioPortNum[MAX_IO_SERVERS]; + int combinePortNum, decodePortNum; + struct childState * childState; /* malloc'ed */ + unsigned int numIoServers; + time_t startUpBegin, startUpEnd; + time_t shutDownBegin, shutDownEnd; + const char * error; + struct scheduler scheduler; + + time(&startUpBegin); + + scheduler.nextFrame = 0; + scheduler.numFramesInJob = inputSourceP->numInputFiles; + scheduler.numMachines = numMachines; + + PrintStartStats(startUpBegin, FALSE, 0, 0, inputSourceP); + + hostName = GetHostName(); + + hostEntry = gethostbyname(hostName); + if (hostEntry == NULL) + errorExit("Could not find host name '%s' in database", hostName); + + CreateListeningSocket(&masterSocket, &portNum, &error); + if (error) + errorExit("Unable to create socket on which to listen. %s", error); + + if (debugSockets) + fprintf(stdout, "---MASTER USING PORT %d\n", portNum); + + startCombineServer(encoder_name, numMachines, hostName, portNum, + inputSourceP->numInputFiles, + paramFileName, masterSocket, + &combinePortNum); + + if (referenceFrame == DECODED_FRAME) + startDecodeServer(encoder_name, numMachines, hostName, portNum, + inputSourceP->numInputFiles, + paramFileName, masterSocket, + &decodePortNum); + + startChildren(&scheduler, encoder_name, hostName, portNum, + paramFileName, parallelPerfect, forceIalign, + framePatternLen, parallelTestFrames, + niceProcesses, + masterSocket, combinePortNum, decodePortNum, + ioPortNum, &numIoServers, + &childState); + + time(&startUpEnd); + + feedTheChildren(&scheduler, childState, + masterSocket, hostName, combinePortNum, + forceIalign, framePatternLen, + parallelTimeChunks != -1, parallelTimeChunks); + + assert(scheduler.nextFrame == scheduler.numFramesInJob); + + time(&shutDownBegin); + + stopIoServers(hostName, ioPortNum, numIoServers); + + waitForCombineServerToTerminate(masterSocket); + + close(masterSocket); + + time(&shutDownEnd); + + printFinalStats(statFile, startUpBegin, startUpEnd, + shutDownBegin, shutDownEnd, numMachines, + childState, inputSourceP->numInputFiles); + + if (statFile) + fclose(statFile); + + free(childState); + strfree(hostName); +} + + + +void +NotifyMasterDone(const char * const masterHostName, + int const masterPortNum, + int const childNum, + unsigned int const seconds, + boolean * const moreWorkToDoP, + int * const nextFrameStartP, + int * const nextFrameEndP) { +/*---------------------------------------------------------------------------- + Tell the master, at 'masterHostName':'masterPortNum' that child + number 'childNum' has finished its assignment, and the decoding + took 'seconds' wall clock seconds. Get the next assignment, if + any, from the master. + + If the master gives us a new assignment, return *moreWorkToDoP == + TRUE and the frames the master wants us to do as *nextFrameStartP + and nextFrameEndP. Otherwise (there is no more work for machine + 'childNum' to do), return *moreWorkToDoP == FALSE. +-----------------------------------------------------------------------------*/ + int clientSocket; + time_t tempTimeStart, tempTimeEnd; + const char * error; + + machineDebug("CHILD: NOTIFYING MASTER Machine %d assignment complete", + childNum); + + time(&tempTimeStart); + + ConnectToSocket(masterHostName, masterPortNum, &hostEntry, + &clientSocket, &error); + if (error) + errorExit("CHILD: Can't connect to master to tell him we've finished " + "our assignment. %s", error); + + WriteInt(clientSocket, childNum); + WriteInt(clientSocket, seconds); + + ReadInt(clientSocket, nextFrameStartP); + ReadInt(clientSocket, nextFrameEndP); + + *moreWorkToDoP = (*nextFrameStartP >= 0); + + if (*moreWorkToDoP) + machineDebug("CHILD: Master says next assignment: start %d end %d", + *nextFrameStartP, *nextFrameEndP); + else + machineDebug("CHILD: Master says no more work for us."); + + close(clientSocket); + + time(&tempTimeEnd); + IOtime += (tempTimeEnd-tempTimeStart); +} + + + +void +DecodeServer(int const numInputFiles, + const char * const decodeFileName, + const char * const masterHostName, + int const masterPortNum) { +/*---------------------------------------------------------------------------- + Execute the decode server. + + The decode server handles transfer of decoded frames to/from processes. + + It is necessary only if referenceFrame == DECODED_FRAME. + + Communicate to the master at hostname 'masterHostName':'masterPortNum'. + +-----------------------------------------------------------------------------*/ + int otherSock; + int decodePortNum; + int frameReady; + boolean *ready; + int *waitMachine; + int *waitPort; + int *waitList; + int slaveNumber; + int slavePort; + int waitPtr; + struct hostent *nullHost = NULL; + int clientSocket; + const char * error; + + /* should keep list of port numbers to notify when frames become ready */ + + ready = (boolean *) calloc(numInputFiles, sizeof(boolean)); + waitMachine = (int *) calloc(numInputFiles, sizeof(int)); + waitPort = (int *) malloc(numMachines*sizeof(int)); + waitList = (int *) calloc(numMachines, sizeof(int)); + + CreateListeningSocket(&decodeServerSocket, &decodePortNum, &error); + if (error) + errorExit("Unable to create socket on which to listen. %s", error); + + machineDebug("DECODE SERVER LISTENING ON PORT %d", decodePortNum); + + TransmitPortNum(masterHostName, masterPortNum, decodePortNum); + + frameDone = (boolean *) malloc(numInputFiles*sizeof(boolean)); + memset((char *)frameDone, 0, numInputFiles*sizeof(boolean)); + + /* wait for ready signals and requests */ + while ( TRUE ) { + const char * error; + + AcceptConnection(decodeServerSocket, &otherSock, &error); + if (error) + errorExit("DECODE SERVER: " + "Failed to accept next connection. %s", error); + + ReadInt(otherSock, &frameReady); + + if ( frameReady == -2 ) { + ReadInt(otherSock, &frameReady); + + machineDebug("DECODE SERVER: REQUEST FOR FRAME %d", frameReady); + + /* now respond if it's ready yet */ + WriteInt(otherSock, frameDone[frameReady]); + + if ( ! frameDone[frameReady] ) { + /* read machine number, port number */ + ReadInt(otherSock, &slaveNumber); + ReadInt(otherSock, &slavePort); + + machineDebug("DECODE SERVER: WAITING: SLAVE %d, PORT %d", + slaveNumber, slavePort); + + waitPort[slaveNumber] = slavePort; + if ( waitMachine[frameReady] == 0 ) { + waitMachine[frameReady] = slaveNumber+1; + } else { + /* someone already waiting for this frame */ + /* follow list of waiters to the end */ + waitPtr = waitMachine[frameReady]-1; + while ( waitList[waitPtr] != 0 ) { + waitPtr = waitList[waitPtr]-1; + } + + waitList[waitPtr] = slaveNumber+1; + waitList[slaveNumber] = 0; + } + } + } else { + frameDone[frameReady] = TRUE; + + machineDebug("DECODE SERVER: FRAME %d READY", frameReady); + + if ( waitMachine[frameReady] ) { + /* need to notify one or more machines it's ready */ + waitPtr = waitMachine[frameReady]-1; + while ( waitPtr >= 0 ) { + const char * error; + ConnectToSocket(machineName[waitPtr], waitPort[waitPtr], + &nullHost, + &clientSocket, &error); + if (error) + errorExit("DECODE SERVER: " + "Can't connect to child machine. %s", + error); + close(clientSocket); + waitPtr = waitList[waitPtr]-1; + } + } + } + + close(otherSock); + } + + machineDebug("DECODE SERVER: Shutting down"); + + /* tell Master server we are done */ + TransmitPortNum(masterHostName, masterPortNum, decodePortNum); + + close(decodeServerSocket); +} + + + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ diff --git a/converter/ppm/ppmtompeg/param.c b/converter/ppm/ppmtompeg/param.c new file mode 100644 index 00000000..5ea69ab6 --- /dev/null +++ b/converter/ppm/ppmtompeg/param.c @@ -0,0 +1,1077 @@ +/*===========================================================================* + * param.c + * + * Procedures to read in parameter file + * + *===========================================================================*/ + +/* COPYRIGHT INFORMATION IS AT THE END OF THIS FILE */ + +#define _XOPEN_SOURCE 500 + /* This makes sure popen() is in stdio.h. In GNU libc 2.1.3, + _POSIX_C_SOURCE = 2 is sufficient, but on AIX 4.3, the higher level + _XOPEN_SOURCE is required. 2000.09.09 + + This also makes sure strdup() is in string.h. + */ +#define _BSD_SOURCE 1 + /* Make sure string.h defines strncasecmp */ + + +/*==============* + * HEADER FILES * + *==============*/ + +#include <assert.h> +#include <string.h> +#include <ctype.h> +#include <stdio.h> +#include <string.h> + +#include "nstring.h" +#include "mallocvar.h" +#include "all.h" +#include "mtypes.h" +#include "mpeg.h" +#include "motion_search.h" +#include "prototypes.h" +#include "parallel.h" +#include "readframe.h" +#include "fsize.h" +#include "frames.h" +#include "jpeg.h" +#include "input.h" +#include "frametype.h" +#include "param.h" + +#include "rate.h" +#include "opts.h" + +/*===========* + * CONSTANTS * + *===========*/ + +#define INPUT_ENTRY_BLOCK_SIZE 128 + +#define FIRST_OPTION 0 +#define OPTION_GOP 0 +#define OPTION_PATTERN 1 +#define OPTION_PIXEL 2 +#define OPTION_PQSCALE 3 +#define OPTION_OUTPUT 4 +#define OPTION_RANGE 5 +#define OPTION_PSEARCH_ALG 6 +#define OPTION_IQSCALE 7 +#define OPTION_INPUT_DIR 8 +#define OPTION_INPUT_CONVERT 9 +#define OPTION_INPUT 10 +#define OPTION_BQSCALE 11 +#define OPTION_BASE_FORMAT 12 +#define OPTION_SPF 13 +#define OPTION_BSEARCH_ALG 14 +#define OPTION_REF_FRAME 15 +#define LAST_OPTION 15 + +/* put any non-required options after LAST_OPTION */ +#define OPTION_RESIZE 16 +#define OPTION_IO_CONVERT 17 +#define OPTION_SLAVE_CONVERT 18 +#define OPTION_IQTABLE 19 +#define OPTION_NIQTABLE 20 +#define OPTION_FRAME_RATE 21 +#define OPTION_ASPECT_RATIO 22 +#define OPTION_YUV_SIZE 23 +#define OPTION_SPECIFICS 24 +#define OPTION_DEFS_SPECIFICS 25 +#define OPTION_BUFFER_SIZE 26 +#define OPTION_BIT_RATE 27 +#define OPTION_USER_DATA 28 +#define OPTION_YUV_FORMAT 29 +#define OPTION_GAMMA 30 +#define OPTION_PARALLEL 31 +#define OPTION_FRAME_INPUT 32 +#define OPTION_GOP_INPUT 33 + +#define NUM_OPTIONS 33 + +/*==================* + * GLOBAL VARIABLES * + *==================*/ + +extern char currentPath[MAXPATHLEN]; +char outputFileName[256]; +int outputWidth, outputHeight; +char inputConversion[1024]; +char ioConversion[1024]; +char slaveConversion[1024]; +char yuvConversion[256]; +char specificsFile[256],specificsDefines[1024]=""; +boolean GammaCorrection=FALSE; +float GammaValue; +char userDataFileName[256]={0}; +boolean specificsOn = FALSE; +char currentGOPPath[MAXPATHLEN]; +char currentFramePath[MAXPATHLEN]; +boolean keepTempFiles; + +static const char * const optionText[LAST_OPTION+1] = { + "GOP_SIZE", "PATTERN", "PIXEL", "PQSCALE", + "OUTPUT", "RANGE", "PSEARCH_ALG", "IQSCALE", "INPUT_DIR", + "INPUT_CONVERT", "INPUT", "BQSCALE", "BASE_FILE_FORMAT", + "SLICES_PER_FRAME", "BSEARCH_ALG", "REFERENCE_FRAME"}; +static boolean optionSeen[NUM_OPTIONS+1]; + /* optionSeen[x] means we have seen option x in the parameter file we've + been reading. + */ + +int numMachines; +char machineName[MAX_MACHINES][256]; +char userName[MAX_MACHINES][256]; +char executable[MAX_MACHINES][1024]; +char remoteParamFile[MAX_MACHINES][1024]; +boolean remote[MAX_MACHINES]; +int mult_seq_headers = 0; /* 0 for none, N for header/N GOPs */ + + +/*===========================================================================* + * + * SkipSpacesTabs + * + * skip all spaces and tabs + * + * RETURNS: point to next character not a space or tab + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +static const char * +SkipSpacesTabs(const char * const start) { + + const char * p; + + for (p = start; *p == ' ' || *p == '\t'; ++p); + + return p; +} + + + +/*===========================================================================* + * + * GetAspectRatio + * + * take a character string with the pixel aspect ratio + * and returns the correct aspect ratio code for use in the Sequence header + * + * RETURNS: aspect ratio code as per MPEG-I spec + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +static int +GetAspectRatio(const char * const p) +{ + float ratio; + int ttRatio; + int retval; + + sscanf(p, "%f", &ratio); + ttRatio = (int)(0.5+ratio*10000.0); + + if ( ttRatio == 10000 ) retval = 1; + else if ( ttRatio == 6735 ) retval = 2; + else if ( ttRatio == 7031 ) retval = 3; + else if ( ttRatio == 7615 ) retval = 4; + else if ( ttRatio == 8055 ) retval = 5; + else if ( ttRatio == 8437 ) retval = 6; + else if ( ttRatio == 8935 ) retval = 7; + else if ( ttRatio == 9157 ) retval = 8; + else if ( ttRatio == 9815 ) retval = 9; + else if ( ttRatio == 10255 ) retval = 10; + else if ( ttRatio == 10695 ) retval = 11; + else if ( ttRatio == 10950 ) retval = 12; + else if ( ttRatio == 11575 ) retval = 13; + else if ( ttRatio == 12015 ) retval = 14; + else { + fprintf(stderr,"INVALID ASPECT RATIO: %s frames/sec\n", p); + exit(1); + } + return retval; +} + + + +/*===========================================================================* + * + * ReadMachineNames + * + * read a list of machine names for parallel execution + * + * RETURNS: nothing + * + * SIDE EFFECTS: machine info updated + * + *===========================================================================*/ +static void +ReadMachineNames(FILE * const fpointer) +{ + char input[256]; + const char *charPtr; + + while ( (fgets(input, 256, fpointer) != NULL) && + (strncmp(input, "END_PARALLEL", 12) != 0) ) { + if ( input[0] == '#' || input[0] == '\n') { + continue; + } + + if ( strncmp(input, "REMOTE", 6) == 0 ) { + charPtr = SkipSpacesTabs(&input[6]); + remote[numMachines] = TRUE; + + sscanf(charPtr, "%s %s %s %s", machineName[numMachines], + userName[numMachines], executable[numMachines], + remoteParamFile[numMachines]); + } else { + remote[numMachines] = FALSE; + + sscanf(input, "%s %s %s", machineName[numMachines], + userName[numMachines], executable[numMachines]); + } + + numMachines++; + } +} + + +/*===========================================================================* + * + * GetFrameRate + * + * take a character string with the input frame rate + * and return the correct frame rate code for use in the Sequence header + * + * RETURNS: frame rate code as per MPEG-I spec + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +static int +GetFrameRate(const char * const p) +{ + float rate; + int thouRate; + int retval; + + sscanf(p, "%f", &rate); + thouRate = (int)(0.5+1000.0*rate); + + if ( thouRate == 23976 ) retval = 1; + else if ( thouRate == 24000 ) retval = 2; + else if ( thouRate == 25000 ) retval = 3; + else if ( thouRate == 29970 ) retval = 4; + else if ( thouRate == 30000 ) retval = 5; + else if ( thouRate == 50000 ) retval = 6; + else if ( thouRate == 59940 ) retval = 7; + else if ( thouRate == 60000 ) retval = 8; + else { + fprintf(stderr,"INVALID FRAME RATE: %s frames/sec\n", p); + exit(1); + } + return retval; +} + + + +static void +mergeInputSource(struct inputSource * const baseSourceP, + struct inputSource * const addedSourceP) { + + unsigned int i; + + baseSourceP->ifArraySize += addedSourceP->numInputFileEntries; + REALLOCARRAY_NOFAIL(baseSourceP->inputFileEntries, + baseSourceP->ifArraySize); + for (i = 0; i < addedSourceP->numInputFileEntries; ++i) + baseSourceP->inputFileEntries[baseSourceP->numInputFileEntries++] = + addedSourceP->inputFileEntries[i]; + + free(addedSourceP); + /* Note the space allocated for the *addedSourceP input file + entries themselves is still allocated, and used by + *baseSourceP. + */ +} + + + +/* Forward declaration for recursively called function */ +static void +ReadInputFileNames(FILE * const fpointer, + const char * const endInput, + struct inputSource * const inputSourceP); + +static void +expandBackTickLine(const char * const input, + struct inputSource * const inputSourceP) { + + FILE *fp; + char cmd[300]; + const char * start; + const char * end; + char cdcmd[110]; + + start = &input[1]; + end = &input[strlen(input)-1]; + + while (*end != '`') { + end--; + } + + end--; + + if (optionSeen[OPTION_INPUT_DIR]) + sprintf(cdcmd,"cd %s;",currentPath); + else + strcpy(cdcmd,""); + + { + char tmp[300]; + strncpy(tmp,start,end-start+1); + sprintf(cmd,"(%s %s)", cdcmd, tmp); + } + + fp = popen(cmd, "r"); + if (fp == NULL) { + pm_error("Unable to resolve backtick entry in input file list. " + "Could not open piped command: '%s'", cmd); + } else { + struct inputSource subInputSource; + ReadInputFileNames(fp, "HOPE-THIS_ISNT_A_FILENAME.xyz5555", + &subInputSource); + + mergeInputSource(inputSourceP, &subInputSource); + } +} + + + +static void +ReadInputFileNames(FILE * const fpointer, + const char * const endInput, + struct inputSource * const inputSourceP) { +/*---------------------------------------------------------------------------- + Read a list of input file names from the parameter file. Add + the information to *inputSourceP. + + If inputSourceP == NULL, read off the list, but ignore it. +-----------------------------------------------------------------------------*/ + char input[256]; + bool endStatementRead; + + /* read param file up through endInput statement. Each line until + endInput is a file specification. + */ + endStatementRead = FALSE; + + while (!endStatementRead) { + char * rc; + rc = fgets(input, sizeof(input), fpointer); + + if (feof(fpointer)) + pm_error("End of file before section end statement '%s'", + endInput); + else if (rc == NULL) + pm_error("Error reading file names from parameter file."); + else { + if (strncmp(input, endInput, strlen(endInput)) == 0) + endStatementRead = TRUE; + else if ((input[0] == '#') || (input[0] == '\n')) { + /* It's a comment or blank line. Ignore it */ + } else if (input[0] == '`' ) { + expandBackTickLine(input, inputSourceP); + } else { + /* get rid of trailing whitespace including newline */ + while (ISSPACE(input[strlen(input)-1])) + input[strlen(input)-1] = '\0'; + if (inputSourceP) + AddInputFiles(inputSourceP, input); + } + } + } +} + + + +static void +initOptionSeen(void) { + + unsigned int index; + + for (index = FIRST_OPTION; index < NUM_OPTIONS; ++index) + optionSeen[index] = FALSE; +} + + + +static void +verifyNoMissingEncodeFramesOption(void) { + if (!optionSeen[OPTION_GOP]) + pm_error("GOP_SIZE option missing"); + if (!optionSeen[OPTION_PATTERN]) + pm_error("PATTERN option missing"); + if (!optionSeen[OPTION_PIXEL]) + pm_error("PIXEL option missing"); + if (!optionSeen[OPTION_PQSCALE]) + pm_error("PQSCALE option missing"); + if (!optionSeen[OPTION_OUTPUT]) + pm_error("OUTPUT option missing"); + if (!optionSeen[OPTION_RANGE]) + pm_error("RANGE option missing"); + if (!optionSeen[OPTION_PSEARCH_ALG]) + pm_error("PSEARCH_ALG option missing"); + if (!optionSeen[OPTION_IQSCALE]) + pm_error("IQSCALE option missing"); + if (!optionSeen[OPTION_INPUT_DIR]) + pm_error("INPUT_DIR option missing"); + if (!optionSeen[OPTION_INPUT_CONVERT]) + pm_error("INPUT_CONVERT option missing"); + if (!optionSeen[OPTION_BQSCALE]) + pm_error("BQSCALE option missing"); + if (!optionSeen[OPTION_BASE_FORMAT]) + pm_error("BASE_FILE_FORMAT option missing"); + if (!optionSeen[OPTION_SPF]) + pm_error("SLICES_PER_FRAME option missing"); + if (!optionSeen[OPTION_BSEARCH_ALG]) + pm_error("BSEARCH_ALG option missing"); + if (!optionSeen[OPTION_REF_FRAME]) + pm_error("REFERENCE_FRAME option missing"); +} + + + +static void +verifyNoMissingCombineGopsOption(void) { + + if (!optionSeen[OPTION_GOP_INPUT]) + pm_error("GOP_INPUT option missing"); + if (!optionSeen[OPTION_YUV_SIZE]) + pm_error("YUV_SIZE option missing"); + if (!optionSeen[OPTION_OUTPUT]) + pm_error("OUTPUT option missing"); +} + + + +static void +verifyNoMissingCombineFramesOption(void) { + + if (!optionSeen[OPTION_GOP]) + pm_error("GOP_SIZE option missing"); + if (!optionSeen[OPTION_YUV_SIZE]) + pm_error("YUV_SIZE option missing"); + if (!optionSeen[OPTION_OUTPUT]) + pm_error("OUTPUT option missing"); +} + + + +static void +verifyNoMissingOption(int const function) { +/*---------------------------------------------------------------------------- + Verify that the parameter file contains every option it is supposed to. + Abort program if not. +-----------------------------------------------------------------------------*/ + switch(function) { + case ENCODE_FRAMES: + verifyNoMissingEncodeFramesOption(); + break; + case COMBINE_GOPS: + verifyNoMissingCombineGopsOption(); + break; + case COMBINE_FRAMES: + verifyNoMissingCombineFramesOption(); + break; + default: + pm_error("Internal error - impossible value for 'function': %d", + function); + } +} + + + +static void +processIqtable(FILE * const fpointer) { + + unsigned int row; + unsigned int col; + char input[256]; + + for (row = 0; row < 8; ++row) { + const char * charPtr; + + fgets(input, 256, fpointer); + charPtr = input; + if (8 != sscanf(charPtr,"%d %d %d %d %d %d %d %d", + &qtable[row*8+0], &qtable[row*8+1], + &qtable[row*8+2], &qtable[row*8+3], + &qtable[row*8+4], &qtable[row*8+5], + &qtable[row*8+6], &qtable[row*8+7])) { + pm_error("Line %d of IQTABLE doesn't have 8 elements!", + row); + } + for (col = 0; col < 8; ++col) { + if ((qtable[row*8+col]<1) || (qtable[row*8+col]>255)) { + pm_message( + "Warning: IQTable Element %1d,%1d (%d) " + "corrected to 1-255.", + row+1, col+1, qtable[row*8+col]); + qtable[row*8+col] = (qtable[row*8+col]<1)?1:255; + } + } + } + + if (qtable[0] != 8) { + pm_message("Warning: IQTable Element 1,1 reset to 8, " + "since it must be 8."); + qtable[0] = 8; + } + customQtable = qtable; +} + + + +static void +setInputSource(int const function, + struct inputSource ** const inputSourcePP, + struct inputSource * const inputSourceP, + struct inputSource * const gopInputSourceP, + struct inputSource * const frameInputSourceP) { +/*---------------------------------------------------------------------------- + Given three the three input sources described by the parameter + file, 'inputSourceP', 'gopInputSourceP', and 'frameInputSourceP', + return as *inputSourcePP the appropriate one of them for the + function 'function', and destroy the other two. +-----------------------------------------------------------------------------*/ + switch (function) { + case ENCODE_FRAMES: + *inputSourcePP = inputSourceP; + DestroyInputSource(gopInputSourceP); + DestroyInputSource(frameInputSourceP); + break; + case COMBINE_GOPS: + *inputSourcePP = gopInputSourceP; + DestroyInputSource(inputSourceP); + DestroyInputSource(frameInputSourceP); + break; + case COMBINE_FRAMES: + *inputSourcePP = frameInputSourceP; + DestroyInputSource(inputSourceP); + DestroyInputSource(gopInputSourceP); + break; + default: + pm_error("INTERNAL ERROR: invalid 'function' %d", function); + } +} + + + +/*=====================* + * EXPORTED PROCEDURES * + *=====================*/ + + + +static void +removeTrailingWhite(const char * const rawLine, + const char ** const editedLineP) { + + char * buffer; + char * p; + + buffer = strdup(rawLine); + + if (!buffer) + pm_error("Unable to get memory to process parameter file"); + + p = buffer + strlen(buffer) - 1; /* Point to last character */ + + /* Position p to just before the trailing white space (might be one + character before beginning of string!) + */ + while (p >= buffer && isspace(*p)) + --p; + + ++p; /* Move to first trailing whitespace character */ + + *p = '\0'; /* Chop off all the trailing whitespace */ + + *editedLineP = buffer; +} + + + +static void +readNiqTable(FILE * const fpointer) { + + unsigned int row; + for (row = 0; row < 8; ++row) { + char input[256]; + unsigned int col; + fgets(input, sizeof(input), fpointer); + if (8 != sscanf(input, "%d %d %d %d %d %d %d %d", + &niqtable[row*8+0], &niqtable[row*8+1], + &niqtable[row*8+2], &niqtable[row*8+3], + &niqtable[row*8+4], &niqtable[row*8+5], + &niqtable[row*8+6], &niqtable[row*8+7])) { + pm_error("Line %d of NIQTABLE doesn't have 8 elements!", + row); + } + for ( col = 0; col < 8; col++ ) { + if ((niqtable[row*8+col]<1) || (niqtable[row*8+col]>255)) { + pm_message( + "Warning: NIQTable Element %1d,%1d (%d) " + "corrected to 1-255.", + row + 1, col + 1, niqtable[row * 8 + col]); + niqtable[row * 8 + col] = (niqtable[row*8+col] < 1) ? 1 : 255; + } + } + } + + customNIQtable = niqtable; +} + + + +static void +processRanges(const char * const arg) { + + int numRanges; + int a, b; + + numRanges = sscanf(arg, "%d %d", &a, &b); + + if (numRanges == 2) + SetSearchRange(a, b); + else if (sscanf(arg, "%d [%d]", &a, &b) == 2) + SetSearchRange(a, b); + else + SetSearchRange(a, a); +} + + + +static void +processParamLine(char const input[], + FILE * const fpointer, + bool * const yuvUsedP, + struct inputSource * const inputSourceP, + struct inputSource * const frameInputSourceP, + struct inputSource * const gopInputSourceP, + struct params * const paramP) { + + switch(input[0]) { + case 'A': + if (STRNEQ(input, "ASPECT_RATIO", 12)) { + aspectRatio = GetAspectRatio(SkipSpacesTabs(&input[12])); + optionSeen[OPTION_ASPECT_RATIO] = TRUE; + } + break; + + case 'B': + if (STRNEQ(input, "BQSCALE", 7)) { + SetBQScale(atoi(SkipSpacesTabs(&input[7]))); + optionSeen[OPTION_BQSCALE] = TRUE; + } else if (STRNEQ(input, "BASE_FILE_FORMAT", 16)) { + const char * arg = SkipSpacesTabs(&input[16]); + SetFileFormat(arg); + if (STRNEQ(arg, "YUV", 3) || STREQ(arg, "Y")) + *yuvUsedP = TRUE; + optionSeen[OPTION_BASE_FORMAT] = TRUE; + } else if (STRNEQ(input, "BSEARCH_ALG", 11)) { + SetBSearchAlg(SkipSpacesTabs(&input[11])); + optionSeen[OPTION_BSEARCH_ALG] = TRUE; + } else if (STRNEQ(input, "BIT_RATE", 8)) { + setBitRate(SkipSpacesTabs(&input[8])); + optionSeen[OPTION_BIT_RATE] = TRUE; + } else if (STRNEQ(input, "BUFFER_SIZE", 11)) { + setBufferSize(SkipSpacesTabs(&input[11])); + optionSeen[OPTION_BUFFER_SIZE] = TRUE; + } + break; + + case 'C': + if (STRNEQ(input, "CDL_FILE", 8)) { + strcpy(specificsFile, SkipSpacesTabs(&input[8])); + specificsOn = TRUE; + optionSeen[OPTION_SPECIFICS] = TRUE; + } else if (STRNEQ(input, "CDL_DEFINES", 11)) { + strcpy(specificsDefines, SkipSpacesTabs(&input[11])); + optionSeen[OPTION_DEFS_SPECIFICS] = TRUE; + } + break; + + case 'F': + if (STRNEQ(input, "FRAME_INPUT_DIR", 15)) { + const char * const arg = SkipSpacesTabs(&input[15]); + if (STRNCASEEQ(arg, "stdin", 5)) + SetStdinInput(frameInputSourceP); + + strcpy(currentFramePath, arg); + } else if (STRNEQ(input, "FRAME_INPUT", 11)) { + ReadInputFileNames(fpointer, "FRAME_END_INPUT", + frameInputSourceP->stdinUsed ? + NULL : frameInputSourceP); + optionSeen[OPTION_FRAME_INPUT] = TRUE; + } else if (STRNEQ(input, "FORCE_I_ALIGN", 13)) { + forceIalign = TRUE; + } else if (STRNEQ(input, "FORCE_ENCODE_LAST_FRAME", 23)) { + /* NO-OP. We used to drop trailing B frames by default and you + needed this option to change the last frame to I so you could + encode all the frames. Now we just do that all the time. + Why wouldn't we? + */ + } else if (STRNEQ(input, "FRAME_RATE", 10)) { + frameRate = GetFrameRate(SkipSpacesTabs(&input[10])); + frameRateRounded = (int) VidRateNum[frameRate]; + if ((frameRate % 3) == 1) + frameRateInteger = FALSE; + optionSeen[OPTION_FRAME_RATE] = TRUE; + } + break; + + case 'G': + if (STRNEQ(input, "GOP_SIZE", 8)) { + SetGOPSize(atoi(SkipSpacesTabs(&input[8]))); + optionSeen[OPTION_GOP] = TRUE; + } else if (STRNEQ(input, "GOP_INPUT_DIR", 13)) { + const char * const arg = SkipSpacesTabs(&input[13]); + if (STRNCASEEQ(arg, "stdin", 5)) + SetStdinInput(gopInputSourceP); + + strcpy(currentGOPPath, arg); + } else if (STRNEQ(input, "GOP_INPUT", 9)) { + ReadInputFileNames(fpointer, "GOP_END_INPUT", + gopInputSourceP->stdinUsed ? + NULL : gopInputSourceP); + optionSeen[OPTION_GOP_INPUT] = TRUE; + } else if (STRNEQ(input, "GAMMA", 5)) { + GammaCorrection = TRUE; + sscanf(SkipSpacesTabs(&input[5]), "%f", &GammaValue); + optionSeen[OPTION_GAMMA] = TRUE; + } + break; + + case 'I': + if (STRNEQ(input, "IQSCALE", 7)) { + SetIQScale(atoi(SkipSpacesTabs(&input[7]))); + optionSeen[OPTION_IQSCALE] = TRUE; + } else if (STRNEQ(input, "INPUT_DIR", 9)) { + const char * const arg = SkipSpacesTabs(&input[9]); + if (STRNCASEEQ(arg, "stdin", 5)) + SetStdinInput(inputSourceP); + strcpy(currentPath, arg); + optionSeen[OPTION_INPUT_DIR] = TRUE; + } else if (STRNEQ(input, "INPUT_CONVERT", 13)) { + strcpy(inputConversion, SkipSpacesTabs(&input[13])); + optionSeen[OPTION_INPUT_CONVERT] = TRUE; + } else if (STREQ(input, "INPUT")) { + ReadInputFileNames(fpointer, "END_INPUT", + inputSourceP->stdinUsed ? + NULL : inputSourceP); + optionSeen[OPTION_INPUT] = TRUE; + } else if (STRNEQ(input, "IO_SERVER_CONVERT", 17)) { + strcpy(ioConversion, SkipSpacesTabs(&input[17])); + optionSeen[OPTION_IO_CONVERT] = TRUE; + } else if (STRNEQ(input, "IQTABLE", 7)) { + processIqtable(fpointer); + + optionSeen[OPTION_IQTABLE] = TRUE; + } + break; + + case 'K': + if (STRNEQ(input, "KEEP_TEMP_FILES", 15)) + keepTempFiles = TRUE; + break; + + case 'N': + if (STRNEQ(input, "NIQTABLE", 8)) { + readNiqTable(fpointer); + + optionSeen[OPTION_NIQTABLE] = TRUE; + } + break; + + case 'O': + if (STRNEQ(input, "OUTPUT", 6)) { + const char * const arg = SkipSpacesTabs(&input[6]); + if ( whichGOP == -1 ) + strcpy(outputFileName, arg); + else + sprintf(outputFileName, "%s.gop.%d", arg, whichGOP); + + optionSeen[OPTION_OUTPUT] = TRUE; + } + break; + + case 'P': + if (STRNEQ(input, "PATTERN", 7)) { + SetFramePattern(SkipSpacesTabs(&input[7])); + optionSeen[OPTION_PATTERN] = TRUE; + } else if (STRNEQ(input, "PIXEL", 5)) { + SetPixelSearch(SkipSpacesTabs(&input[5])); + optionSeen[OPTION_PIXEL] = TRUE; + } else if (STRNEQ(input, "PQSCALE", 7)) { + SetPQScale(atoi(SkipSpacesTabs(&input[7]))); + optionSeen[OPTION_PQSCALE] = TRUE; + } else if (STRNEQ(input, "PSEARCH_ALG", 11)) { + SetPSearchAlg(SkipSpacesTabs(&input[11])); + optionSeen[OPTION_PSEARCH_ALG] = TRUE; + } else if (STRNEQ(input, "PARALLEL_TEST_FRAMES", 20)) { + SetParallelPerfect(FALSE); + parallelTestFrames = atoi(SkipSpacesTabs(&input[20])); + } else if (STRNEQ(input, "PARALLEL_TIME_CHUNKS", 20)) { + SetParallelPerfect(FALSE); + parallelTimeChunks = atoi(SkipSpacesTabs(&input[20])); + } else if (STRNEQ(input, "PARALLEL_CHUNK_TAPER", 20)) { + SetParallelPerfect(FALSE); + parallelTimeChunks = -1; + } else if (STRNEQ(input, "PARALLEL_PERFECT", 16)) { + SetParallelPerfect(TRUE); + } else if (STRNEQ(input, "PARALLEL", 8)) { + ReadMachineNames(fpointer); + optionSeen[OPTION_PARALLEL] = TRUE; + } + break; + + case 'R': + if (STRNEQ(input, "RANGE", 5)) { + processRanges(SkipSpacesTabs(&input[5])); + optionSeen[OPTION_RANGE] = TRUE; + } else if (STRNEQ(input, "REFERENCE_FRAME", 15)) { + SetReferenceFrameType(SkipSpacesTabs(&input[15])); + optionSeen[OPTION_REF_FRAME] = TRUE; + } else if (STRNEQ(input, "RSH", 3)) { + SetRemoteShell(SkipSpacesTabs(&input[3])); + } else if (STRNEQ(input, "RESIZE", 6)) { + const char * const arg = SkipSpacesTabs(&input[6]); + sscanf(arg, "%dx%d", &outputWidth, &outputHeight); + outputWidth &= ~(DCTSIZE * 2 - 1); + outputHeight &= ~(DCTSIZE * 2 - 1); + optionSeen[OPTION_RESIZE] = TRUE; + } + break; + + case 'S': + if (STRNEQ(input, "SLICES_PER_FRAME", 16)) { + SetSlicesPerFrame(atoi(SkipSpacesTabs(&input[16]))); + optionSeen[OPTION_SPF] = TRUE; + } else if (STRNEQ(input, "SLAVE_CONVERT", 13)) { + strcpy(slaveConversion, SkipSpacesTabs(&input[13])); + optionSeen[OPTION_SLAVE_CONVERT] = TRUE; + } else if (STRNEQ(input, "SPECIFICS_FILE", 14)) { + strcpy(specificsFile, SkipSpacesTabs(&input[14])); + specificsOn = TRUE; + optionSeen[OPTION_SPECIFICS] = TRUE; + } else if (STRNEQ(input, "SPECIFICS_DEFINES", 16)) { + strcpy(specificsDefines, SkipSpacesTabs(&input[17])); + optionSeen[OPTION_DEFS_SPECIFICS] = TRUE; + } else if (STRNEQ(input, "SEQUENCE_SIZE", 13)) { + mult_seq_headers = atoi(SkipSpacesTabs(&input[13])); + } else if (STRNEQ(input, "SIZE", 4)) { + const char * const arg = SkipSpacesTabs(&input[4]); + sscanf(arg, "%dx%d", &yuvWidth, &yuvHeight); + realWidth = yuvWidth; + realHeight = yuvHeight; + Fsize_Validate(&yuvWidth, &yuvHeight); + optionSeen[OPTION_YUV_SIZE] = TRUE; + } + break; + + case 'T': + if (STRNEQ(input, "TUNE", 4)) { + tuneingOn = TRUE; + ParseTuneParam(SkipSpacesTabs(&input[4])); + } + break; + + case 'U': + if (STRNEQ(input, "USER_DATA", 9)) { + strcpy(userDataFileName, SkipSpacesTabs(&input[9])); + optionSeen[OPTION_USER_DATA] = TRUE; + } + break; + + case 'W': + if (STRNEQ(input, "WARN_UNDERFLOW", 14)) + paramP->warnUnderflow = TRUE; + if (STRNEQ(input, "WARN_OVERFLOW", 13)) + paramP->warnOverflow = TRUE; + break; + + case 'Y': + if (STRNEQ(input, "YUV_SIZE", 8)) { + const char * const arg = SkipSpacesTabs(&input[8]); + sscanf(arg, "%dx%d", &yuvWidth, &yuvHeight); + realWidth = yuvWidth; + realHeight = yuvHeight; + Fsize_Validate(&yuvWidth, &yuvHeight); + optionSeen[OPTION_YUV_SIZE] = TRUE; + } else if (STRNEQ(input, "Y_SIZE", 6)) { + const char * const arg = SkipSpacesTabs(&input[6]); + sscanf(arg, "%dx%d", &yuvWidth, &yuvHeight); + realWidth = yuvWidth; + realHeight = yuvHeight; + Fsize_Validate(&yuvWidth, &yuvHeight); + optionSeen[OPTION_YUV_SIZE] = TRUE; + } else if (STRNEQ(input, "YUV_FORMAT", 10)) { + strcpy(yuvConversion, SkipSpacesTabs(&input[10])); + optionSeen[OPTION_YUV_FORMAT] = TRUE; + } + break; + } +} + + + +/*===========================================================================* + * + * ReadParamFile + * + * read the parameter file + * function is ENCODE_FRAMES, COMBINE_GOPS, or COMBINE_FRAMES, and + * will slightly modify the procedure's behavior as to what it + * is looking for in the parameter file + * + * SIDE EFFECTS: sets parameters accordingly, as well as machine info for + * parallel execution and input file names + * + *===========================================================================*/ +void +ReadParamFile(const char * const fileName, + int const function, + struct params * const paramP) { + + FILE *fpointer; + char buffer[256]; + bool yuvUsed; + struct inputSource * inputSourceP; + /* Contents of INPUT section */ + struct inputSource * frameInputSourceP; + /* Contents of FRAME_INPUT section */ + struct inputSource * gopInputSourceP; + /* Contents of GOP_INPUT section */ + + fpointer = fopen(fileName, "r"); + if (fpointer == NULL) + pm_error("Error: Cannot open parameter file: %s", fileName); + + CreateInputSource(&inputSourceP); + CreateInputSource(&frameInputSourceP); + CreateInputSource(&gopInputSourceP); + + /* should set defaults */ + numMachines = 0; + sprintf(currentPath, "."); + sprintf(currentGOPPath, "."); + sprintf(currentFramePath, "."); + SetRemoteShell("rsh"); + keepTempFiles = FALSE; + paramP->warnOverflow = FALSE; + paramP->warnUnderflow = FALSE; + + initOptionSeen(); + + yuvUsed = FALSE; /* initial value */ + + while (fgets(buffer, 256, fpointer) != NULL) { + const char * input; + + removeTrailingWhite(buffer, &input); + + if (strlen(input) == 0) { + /* Ignore blank line */ + } else if (input[0] == '#') { + /* Ignore comment */ + } else + processParamLine(input, fpointer, &yuvUsed, + inputSourceP, frameInputSourceP, gopInputSourceP, + paramP); + /* May read additional lines from file */ + + strfree(input); + } + + fclose(fpointer); + + setInputSource(function, ¶mP->inputSourceP, + inputSourceP, gopInputSourceP, frameInputSourceP); + + verifyNoMissingOption(function); + + /* error checking */ + + if (!paramP->inputSourceP->stdinUsed && + paramP->inputSourceP->numInputFiles == 0) + pm_error("You have not specified any input. See the " + "INPUT_DIR, INPUT, GOP_INPUT_DIR, GOP_INPUT, " + "FRAME_INPUT_DIR, and FRAME_INPUT options"); + + if (yuvUsed) { + if (!optionSeen[OPTION_YUV_SIZE]) + pm_error("YUV format used but YUV_SIZE not given"); + + if (!optionSeen[OPTION_YUV_FORMAT]) { + strcpy (yuvConversion, "EYUV"); + pm_message("WARNING: YUV format not specified; " + "defaulting to Berkeley YUV (EYUV)"); + } + } + + if (paramP->inputSourceP->stdinUsed && optionSeen[OPTION_PARALLEL] ) + pm_error("stdin reading for parallel execution not enabled yet."); + + if (optionSeen[OPTION_PARALLEL] && !optionSeen[OPTION_YUV_SIZE]) + pm_error("Specify SIZE WxH for parallel encoding"); + + if (optionSeen[OPTION_IO_CONVERT] != optionSeen[OPTION_SLAVE_CONVERT]) + pm_error("Must have either both IO_SERVER_CONVERT and SLAVE_CONVERT " + "or neither"); + + if (optionSeen[OPTION_DEFS_SPECIFICS] && !optionSeen[OPTION_SPECIFICS]) + pm_error("Does not make sense to define Specifics file options, " + "but no specifics file!"); + + SetIOConvert(optionSeen[OPTION_IO_CONVERT]); + + SetResize(optionSeen[OPTION_RESIZE]); + + if (function == ENCODE_FRAMES) { + SetFCode(); + + if (psearchAlg == PSEARCH_TWOLEVEL) + SetPixelSearch("HALF"); + } +} + + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ diff --git a/converter/ppm/ppmtompeg/parse_huff.pl b/converter/ppm/ppmtompeg/parse_huff.pl new file mode 100644 index 00000000..1fc6466c --- /dev/null +++ b/converter/ppm/ppmtompeg/parse_huff.pl @@ -0,0 +1,198 @@ +# +# Copyright (c) 1993 The Regents of the University of California. +# All rights reserved. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation for any purpose, without fee, and without written agreement is +# hereby granted, provided that the above copyright notice and the following +# two paragraphs appear in all copies of this software. +# +# IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR +# DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT +# OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF +# CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +# ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO +# PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +# + +# +# $Header: /n/picasso/users/dwallach/vid2/mpeg_encode/RCS/parse_huff.pl,v 1.3 1993/01/18 10:56:03 dwallach Exp $ +# $Log: parse_huff.pl,v $ +# Revision 1.3 1993/01/18 10:56:03 dwallach +# got RCS headers in huff.c and huff.h working +# +# Revision 1.3 1993/01/18 10:56:03 dwallach +# got RCS headers in huff.c and huff.h working +# +# Revision 1.2 1993/01/18 10:17:29 dwallach +# RCS headers installed, code indented uniformly +# +# Revision 1.2 1993/01/18 10:17:29 dwallach +# RCS headers installed, code indented uniformly +# +# + +# this program's purpose in life is to parse the Huffman tables +# and output C code which is awfully useful for translation + +# typical usage: +# perl parse_huff.pl huffman.table +# ---> generates huff.c and huff.h + +# source format: # is a comment character +# Otherwise, there are three tab-separated fields in a line: +# Run, Level, and VLC Code +# Run and Level are integers, corresponding to the RLE coding. +# The VLC code is what bits to generate, and is expected to be +# composed of 1, 0, space, and 's'. Spaces are ignored, and +# s corresponds to the sign bit. In the output of this program, +# We'll completely right-shift the data, with a 0 for the sign +# bit. The encoder will make appropriate changes before outputing. + + +open(HUFFC, "> huff.c") || die "Can't write huff.c: $!\n"; +open(HUFFH, "> huff.h") || die "Can't write huff.h: $!\n"; + +sub encode { + local($run, $level, $vlc) = @_; + local($result) = 0; + local($tmp); + $vlc =~ s/\s//g; # remove whitespace + + local($bits) = length($vlc); + + foreach (split(//, $vlc)) { + $_ = 0 if $_ eq 's'; + $result = ($result << 1) + $_; + } + + $tmp = sprintf("0x%x", $result); + eval "\$vlc$run[$level] = \$tmp"; + eval "\$bits$run[$level] = \$bits"; +} + +while(<>) { + chop; + s/\s*#.*$//; # remove comments + next if /^\s*$/; # continue if the line is purely whitespace + + ($run, $level, $vlc) = split(/\t/); + &encode($run, $level, $vlc); + + # + # we have the +1's to insure the sizes of C arrays are correct + # + $maxlevel[$run] = $level+1 if $level >= $maxlevel[$run]; + $maxlevel = $level+1 if $level >= $maxlevel; + $maxrun = $run+1 if $run >= $maxrun; +} + +# +# fix the entries that aren't defined +# +for($run = 0; $run < $maxrun; $run++) { + eval "\$vlc$run[0] = '0x0' if \$vlc$run[0] eq ''"; + eval "\$bits$run[0] = '0' if \$bits$run[0] eq ''"; +} + +# +# now, output some C code +# + +printf HUFFC <<'EOF', $maxrun, join(", ", @maxlevel); +/* + * Copyright (c) 1993 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/* + * $Header: /n/picasso/users/dwallach/vid2/mpeg_encode/RCS/parse_huff.pl,v 1.3 1993/01/18 10:56:03 dwallach Exp $ + */ + +/* + * THIS FILE IS MACHINE GENERATED! DO NOT EDIT! + */ +#include "mtypes.h" +#include "huff.h" + +int huff_maxlevel[%s] = { %s }; + +EOF +for($run = 0; $run < $maxrun; $run++) { + printf HUFFC <<EOF, eval "join(', ', \@vlc$run)", eval "join(', ', \@bits$run)"; +uint32 huff_table$run[$maxlevel[$run]] = { %s }; +int huff_bits$run[$maxlevel[$run]] = { %s }; + +EOF +} + +printf HUFFC "uint32 *huff_table[$maxrun] = { "; +for($run = 0; $run < $maxrun; $run++) { + printf HUFFC "%shuff_table$run", ($run)?", ":""; +} +printf HUFFC " };\n"; + +printf HUFFC "int *huff_bits[$maxrun] = { "; +for($run = 0; $run < $maxrun; $run++) { + printf HUFFC "%shuff_bits$run", ($run)?", ":""; +} +printf HUFFC " };\n"; +close HUFFC; + +printf HUFFH <<'EOF', $maxrun, $maxlevel; +/* + * Copyright (c) 1993 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/* + * $Header: /n/picasso/users/dwallach/vid2/mpeg_encode/RCS/parse_huff.pl,v 1.3 1993/01/18 10:56:03 dwallach Exp $ + */ + +/* + * THIS FILE IS MACHINE GENERATED! DO NOT EDIT! + */ +#define HUFF_MAXRUN %s +#define HUFF_MAXLEVEL %s + +extern int huff_maxlevel[]; +extern uint32 *huff_table[]; +extern int *huff_bits[]; +EOF + diff --git a/converter/ppm/ppmtompeg/pframe.c b/converter/ppm/ppmtompeg/pframe.c new file mode 100644 index 00000000..e72fe5d6 --- /dev/null +++ b/converter/ppm/ppmtompeg/pframe.c @@ -0,0 +1,1072 @@ +/*===========================================================================* + * pframe.c + * + * Procedures concerned with generation of P-frames + * + * EXPORTED PROCEDURES: + * GenPFrame + * ResetPFrameStats + * ShowPFrameSummary + * EstimateSecondsPerPFrame + * ComputeHalfPixelData + * SetPQScale + * GetPQScale + * + * NOTE: when motion vectors are passed as arguments, they are passed as + * twice their value. In other words, a motion vector of (3,4) will + * be passed as (6,8). This allows half-pixel motion vectors to be + * passed as integers. This is true throughout the program. + * + *===========================================================================*/ + +/*==============* + * HEADER FILES * + *==============*/ + +#include <assert.h> +#include <sys/param.h> +#include "pm.h" +#include "pm_c_util.h" +#include "all.h" +#include "mtypes.h" +#include "bitio.h" +#include "frames.h" +#include "motion_search.h" +#include "prototypes.h" +#include "block.h" +#include "param.h" +#include "mheaders.h" +#include "fsize.h" +#include "postdct.h" +#include "mpeg.h" +#include "parallel.h" +#include "rate.h" +#include "opts.h" +#include "specifics.h" + +/*==================* + * STATIC VARIABLES * + *==================*/ + +static int32 zeroDiff; +static int numPIBlocks = 0; +static int numPPBlocks = 0; +static int numPSkipped = 0; +static int numPIBits = 0; +static int numPPBits = 0; +static int numFrames = 0; +static int numFrameBits = 0; +static int32 totalTime = 0; +static int qscaleP; +static float totalSNR = 0.0; +static float totalPSNR = 0.0; +extern Block **dct, **dctr, **dctb; +extern dct_data_type **dct_data; + +/*=====================* + * INTERNAL PROCEDURES * + *=====================*/ + +static vector +halfVector(vector const vector) { + struct vector half; + + half.y = vector.y/2; + half.x = vector.x/2; + + return half; +} + +/*===========================================================================* + * + * decide if (0,0) motion is better than the given motion vector + * + * RETURNS: TRUE if (0,0) is better, FALSE if (my,mx) is better + * + * SIDE EFFECTS: none + * + * PRECONDITIONS: The relevant block in 'current' is valid (it has not + * been dct'd). 'zeroDiff' has already been computed + * as the LumMotionError() with (0,0) motion + * + * NOTES: This procedure follows the algorithm described on + * page D-48 of the MPEG-1 specification + * + *===========================================================================*/ +static boolean +ZeroMotionBetter(const LumBlock * const currentBlockP, + MpegFrame * const prev, + int const by, + int const bx, + vector const m) { + + int bestDiff; + int CompareMode; + + /* Junk needed to adapt for TUNEing */ + CompareMode = SearchCompareMode; + SearchCompareMode = DEFAULT_SEARCH; + bestDiff = LumMotionError(currentBlockP, prev, by, bx, m, 0x7fffffff); + SearchCompareMode = CompareMode; + + if ( zeroDiff < 256*3 ) { + if ( 2*bestDiff >= zeroDiff ) { + return TRUE; + } + } else { + if ( 11*bestDiff >= 10*zeroDiff ) { + return TRUE; + } + } + return FALSE; +} + + +/*===========================================================================* + * + * USER-MODIFIABLE + * + * DoIntraCode + * + * decide if intra coding is necessary + * + * RETURNS: TRUE if intra-block coding is better; FALSE if not + * + * SIDE EFFECTS: none + * + * PRECONDITIONS: The relevant block in 'current' is valid (it has not + * been dct'd). + * + * NOTES: This procedure follows the algorithm described on + * page D-49 of the MPEG-1 specification + * + *===========================================================================*/ +static boolean +DoIntraCode(const LumBlock * const currentBlockP, + MpegFrame * const prev, + int const by, + int const bx, + vector const motion) { + + unsigned int y; + int32 sum = 0, vard = 0, varc = 0; + int32 currPixel, prevPixel; + LumBlock motionBlock; + + ComputeMotionLumBlock(prev, by, bx, motion, &motionBlock); + + for (y = 0; y < 16; ++y) { + unsigned int x; + for (x = 0; x < 16; ++x) { + currPixel = currentBlockP->l[y][x]; + prevPixel = motionBlock.l[y][x]; + + sum += currPixel; + varc += currPixel*currPixel; + + vard += SQR(currPixel - prevPixel); + } + } + + vard /= 256; /* divide by 256; assumes mean is close to zero */ + varc = (varc/256) - (sum/256) * (sum/256); + + if (vard <= 64) + return FALSE; + else if (vard < varc) + return FALSE; + else + return TRUE; +} + + + +/*===========================================================================* + * + * USER-MODIFIABLE + * + * ZeroMotionSufficient + * + * decide if zero motion is sufficient without DCT correction + * + * RETURNS: TRUE no DCT required; FALSE otherwise + * + * SIDE EFFECTS: none + * + * PRECONDITIONS: The relevant block in 'current' is raw YCC data + * + *===========================================================================*/ +static boolean +ZeroMotionSufficient(const LumBlock * const currentBlockP, + MpegFrame * const prev, + int const by, + int const bx) { + + LumBlock motionBlock; + int fy, fx; + unsigned int y; + + fy = by * DCTSIZE; + fx = bx * DCTSIZE; + for (y = 0; y < 16; ++y) { + unsigned int x; + for (x = 0; x < 16; ++x) { + motionBlock.l[y][x] = prev->ref_y[fy+y][fx+x]; + } + } + + zeroDiff = LumBlockMAD(currentBlockP, &motionBlock, 0x7fffffff); + + return (zeroDiff <= 256); +} + + + +static void +computeCurrentBlock(MpegFrame * const current, + int const y, + int const x, + LumBlock * const currentBlockP) { + int fy, fx; + int iy; + + BLOCK_TO_FRAME_COORD(y, x, fy, fx); + for ( iy = 0; iy < 16; iy++ ) { + int ix; + for ( ix = 0; ix < 16; ix++ ) { + currentBlockP->l[iy][ix] = + (int16)current->orig_y[fy+iy][fx+ix]; + } + } +} + + + +static void +computeMotionVectors(bool const specificsOn, + bool const IntraPBAllowed, + MpegFrame * const current, + MpegFrame * const prev, + int const mbAddress, + BlockMV ** const infoP, + int const QScale, + const LumBlock * const currentBlockP, + int const y, + int const x, + bool * const useMotionP, + vector * const motionP) { + + bool useCached; + BlockMV * info; + + /* See if we have a cached answer */ + if (specificsOn) { + SpecLookup(current->id, 2, mbAddress, &info, QScale); + if (info != (BlockMV*)NULL) + useCached = TRUE; + else + useCached = FALSE; + } else + useCached = FALSE; + + if (useCached) { + if (info->typ == TYP_SKIP) { + motionP->x = motionP->y = 0; + *useMotionP = TRUE; + } else { /* assume P, since we're a P frame.... */ + motionP->x = info->fx; + motionP->y = info->fy; + *useMotionP = TRUE; + } + } else { + /* see if we should use motion vectors, and if so, what those + * vectors should be + */ + if (ZeroMotionSufficient(currentBlockP, prev, y, x)) { + motionP->x = 0; + motionP->y = 0; + *useMotionP = TRUE; + } else { + vector motion; + motion.y = motion.x = 0; /* initial values */ + PMotionSearch(currentBlockP, prev, y, x, &motion); + if (ZeroMotionBetter(currentBlockP, prev, y, x, motion)) { + motionP->y = 0; + motionP->x = 0; + } else + *motionP = motion; + if (IntraPBAllowed) + *useMotionP = !DoIntraCode(currentBlockP, prev, y, x, motion); + else + *useMotionP = TRUE; + } + } + *infoP = info; +} + + + +static void +calculateForwardDcts(MpegFrame * const current, + int const y, int const x, + Block ** const dct) { + + /* calculate forward dct's */ + if (collect_quant && (collect_quant_detailed & 1)) + fprintf(collect_quant_fp, "l\n"); + + mp_fwd_dct_block2(current->y_blocks[y][x], dct[y][x]); + mp_fwd_dct_block2(current->y_blocks[y][x+1], dct[y][x+1]); + mp_fwd_dct_block2(current->y_blocks[y+1][x], dct[y+1][x]); + mp_fwd_dct_block2(current->y_blocks[y+1][x+1], dct[y+1][x+1]); + + if (collect_quant && (collect_quant_detailed & 1)) + fprintf(collect_quant_fp, "c\n"); + + mp_fwd_dct_block2(current->cb_blocks[y/2][x/2], dctb[y/2][x/2]); + + mp_fwd_dct_block2(current->cr_blocks[y/2][x/2], dctr[y/2][x/2]); +} + + + +static void +computeMotionAndDct(int const lastBlockY, + int const lastBlockX, + bool const specificsOn, + bool const IntraPBAllowed, + MpegFrame * const current, + MpegFrame * const prev, + BlockMV ** const infoP, + int const QScale, + int const searchRangeP, + Block ** const dct, + int * const numPBlocksP, + int * const numIBlocksP, + int ** const pmvHistogram) { +/*---------------------------------------------------------------------------- + Loop through the frame finding motion/not and DCTing +-----------------------------------------------------------------------------*/ + int mbAddress; + int y; + + mbAddress = 0; + + for (y = 0; y < lastBlockY; y += 2) { + int x; + for (x = 0; x < lastBlockX; x += 2) { + LumBlock currentBlock; + vector motion; + bool useMotion; + + computeCurrentBlock(current, y, x, ¤tBlock); + + computeMotionVectors(specificsOn, IntraPBAllowed, + current, prev, mbAddress, infoP, + QScale, ¤tBlock, y, x, + &useMotion, &motion); + + dct_data[y][x].useMotion = useMotion; + + if (useMotion) { + int pattern; + + (*numPBlocksP)++; + + pattern = 63; + ComputeDiffDCTs(current, prev, y, x, motion, &pattern); + + assert(motion.x + searchRangeP + 1 >= 0); + assert(motion.y + searchRangeP + 1 >= 0); + + if (computeMVHist) { + assert(motion.x + searchRangeP + 1 <= 2*searchRangeP + 2); + assert(motion.y + searchRangeP + 1 <= 2*searchRangeP + 2); + ++pmvHistogram[motion.x + searchRangeP + 1] + [motion.y + searchRangeP + 1]; + } + /* Save specs for next loops */ + dct_data[y][x].pattern = pattern; + dct_data[y][x].fmotionX = motion.x; + dct_data[y][x].fmotionY = motion.y; + } else { + /* output I-block inside a P-frame */ + ++*numIBlocksP; + + calculateForwardDcts(current, y, x, dct); + } + ++mbAddress; + } + } +} + + + +/*=====================* + * EXPORTED PROCEDURES * + *=====================*/ + +/*===========================================================================* + * + * GenPFrame + * + * generate a P-frame from previous frame, adding the result to the + * given bit bucket + * + * RETURNS: frame appended to bb + * + *===========================================================================*/ +void +GenPFrame(BitBucket * const bb, + MpegFrame * const current, + MpegFrame * const prev) { + + extern int **pmvHistogram; + FlatBlock fba[6], fb[6]; + Block dec[6]; + int32 y_dc_pred, cr_dc_pred, cb_dc_pred; + int x, y; + vector motion; + vector oldMotion; + int offsetX, offsetY; + vector motionRem; + vector motionQuot; + int pattern; + int mbAddrInc = 1; + int numIBlocks = 0; + int numPBlocks = 0; + int numSkipped = 0; + int numIBits = 0; + int numPBits = 0; + int totalBits; + int totalFrameBits; + int32 startTime, endTime; + int lastBlockX, lastBlockY; + int lastX, lastY; + int mbAddress; + int slicePos; + register int index; + float snr[3], psnr[3]; + int QScale; + BlockMV *info; + int bitstreamMode, newQScale; + int rc_blockStart = 0; + boolean overflowChange = FALSE; + int overflowValue = 0; + + + oldMotion.x = oldMotion.y = 0; + + if (collect_quant) {fprintf(collect_quant_fp, "# P\n");} + if (dct==NULL) AllocDctBlocks(); + numFrames++; + totalFrameBits = bb->cumulativeBits; + startTime = time_elapsed(); + + DBG_PRINT(("Generating pframe\n")); + + QScale = GetPQScale(); + /* bit allocation for rate control purposes */ + bitstreamMode = getRateMode(); + if (bitstreamMode == FIXED_RATE) { + targetRateControl(current); + } + + Mhead_GenPictureHeader(bb, P_FRAME, current->id, fCodeP); + /* Check for Qscale change */ + if (specificsOn) { + /* Set a Qscale for this frame? */ + newQScale = + SpecLookup(current->id, 0, 0 /* junk */, &info /*junk*/, QScale); + if (newQScale != -1) { + QScale = newQScale; + } + /* Set for slice? */ + newQScale = SpecLookup(current->id, 1, 1, &info /*junk*/, QScale); + if (newQScale != -1) { + QScale = newQScale; + } + } + + DBG_PRINT(("Slice Header\n")); + Mhead_GenSliceHeader(bb, 1, QScale, NULL, 0); + + if ( referenceFrame == DECODED_FRAME ) { + Frame_AllocDecoded(current, TRUE); + } else if ( printSNR ) { + Frame_AllocDecoded(current, FALSE); + } + + /* don't do dct on blocks yet */ + Frame_AllocBlocks(current); + BlockifyFrame(current); + + /* for I-blocks */ + y_dc_pred = cr_dc_pred = cb_dc_pred = 128; + + totalBits = bb->cumulativeBits; + + if ( (! pixelFullSearch) && (! prev->halfComputed) ) { + ComputeHalfPixelData(prev); + } + + lastBlockX = Fsize_x>>3; + lastBlockY = Fsize_y>>3; + lastX = lastBlockX-2; + lastY = lastBlockY-2; + + computeMotionAndDct(lastBlockY, lastBlockX, + specificsOn, IntraPBAllowed, current, prev, + &info, QScale, searchRangeP, dct, + &numPBlocks, &numIBlocks, pmvHistogram); + + mbAddress = 0; + for (y = 0; y < lastBlockY; y += 2) { + for (x = 0; x < lastBlockX; x += 2) { + slicePos = (mbAddress % blocksPerSlice); + + if ( (slicePos == 0) && (mbAddress != 0) ) { + if (specificsOn) { + /* Make sure no slice Qscale change */ + newQScale = + SpecLookup(current->id, 1, mbAddress/blocksPerSlice, + &info /*junk*/, QScale); + if (newQScale != -1) QScale = newQScale; + } + + Mhead_GenSliceEnder(bb); + Mhead_GenSliceHeader(bb, 1+(y>>1), QScale, NULL, 0); + + /* reset everything */ + oldMotion.x = oldMotion.y = 0; + y_dc_pred = cr_dc_pred = cb_dc_pred = 128; + + mbAddrInc = 1+(x>>1); + } + + /* Determine if new Qscale needed for Rate Control purposes */ + if (bitstreamMode == FIXED_RATE) { + rc_blockStart = bb->cumulativeBits; + newQScale = needQScaleChange(qscaleP, + current->y_blocks[y][x], + current->y_blocks[y][x+1], + current->y_blocks[y+1][x], + current->y_blocks[y+1][x+1]); + if (newQScale > 0) { + QScale = newQScale; + } + } + + /* Check for Qscale change */ + if (specificsOn) { + newQScale = + SpecLookup(current->id, 2, mbAddress, &info, QScale); + if (newQScale != -1) { + QScale = newQScale; + } + } + + if (! dct_data[y][x].useMotion) { + GEN_I_BLOCK(P_FRAME, current, bb, mbAddrInc, QScale); + mbAddrInc = 1; + + numIBits += (bb->cumulativeBits-totalBits); + totalBits = bb->cumulativeBits; + + /* reset because intra-coded */ + oldMotion.x = oldMotion.y = 0; + + if ( decodeRefFrames ) { + /* need to decode block we just encoded */ + Mpost_UnQuantZigBlock(fb[0], dec[0], QScale, TRUE); + Mpost_UnQuantZigBlock(fb[1], dec[1], QScale, TRUE); + Mpost_UnQuantZigBlock(fb[2], dec[2], QScale, TRUE); + Mpost_UnQuantZigBlock(fb[3], dec[3], QScale, TRUE); + Mpost_UnQuantZigBlock(fb[4], dec[4], QScale, TRUE); + Mpost_UnQuantZigBlock(fb[5], dec[5], QScale, TRUE); + + /* now, reverse the DCT transform */ + for ( index = 0; index < 6; index++ ) { + mpeg_jrevdct((int16 *)dec[index]); + } + + /* now, unblockify */ + BlockToData(current->decoded_y, dec[0], y, x); + BlockToData(current->decoded_y, dec[1], y, x+1); + BlockToData(current->decoded_y, dec[2], y+1, x); + BlockToData(current->decoded_y, dec[3], y+1, x+1); + BlockToData(current->decoded_cb, dec[4], y>>1, x>>1); + BlockToData(current->decoded_cr, dec[5], y>>1, x>>1); + } + } else { + int fCode = fCodeP; + + /* reset because non-intra-coded */ + y_dc_pred = cr_dc_pred = cb_dc_pred = 128; + + pattern = dct_data[y][x].pattern; + motion.x = dct_data[y][x].fmotionX; + motion.y = dct_data[y][x].fmotionY; + +#ifdef BLEAH + ComputeAndPrintPframeMAD(currentBlock, prev, y, x, motion, + mbAddress); +#endif + + if ( pixelFullSearch ) { /* should be even */ + motion.y /= 2; + motion.x /= 2; + } + + /* transform the motion vector into the appropriate values */ + offsetX = motion.x - oldMotion.x; + offsetY = motion.y - oldMotion.y; + /* if ((offsetX+(8*x)) >= (Fsize_x-8)) log(10.0); */ + encodeMotionVector(offsetX, offsetY, &motionQuot, &motionRem, + FORW_F, fCode); + +#ifdef BLEAH + if ( (motion.x != 0) || (motion.y != 0) ) { + fprintf(stdout, "FRAME (y, x) %d, %d (block %d)\n", + y, x, mbAddress); + fprintf(stdout, "motion.x = %d, motion.y = %d\n", + motion.x, motion.y); + fprintf(stdout, + " mxq, mxr = %d, %d myq, myr = %d, %d\n", + motionQuot.x, motionRem.x, + motionQuot.y, motionRem.y); + } +#endif + + oldMotion = motion; + + if ( pixelFullSearch ) { /* reset for use with PMotionSearch */ + motion.y *= 2; + motion.x *= 2; + } + calc_blocks: + /* create flat blocks and update pattern if necessary */ + /* Note DoQuant references QScale, overflowChange, + overflowValue, pattern, and the calc_blocks label */ + DoQuant(0x20, dct[y][x], fba[0]); + DoQuant(0x10, dct[y][x+1], fba[1]); + DoQuant(0x08, dct[y+1][x], fba[2]); + DoQuant(0x04, dct[y+1][x+1], fba[3]); + DoQuant(0x02, dctb[y/2][x/2], fba[4]); + DoQuant(0x01, dctr[y/2][x/2], fba[5]); + + if ( decodeRefFrames) { + for ( index = 0; index < 6; index++ ) { + if ( pattern & (1 << (5-index))) { + Mpost_UnQuantZigBlock(fba[index], dec[index], + QScale, FALSE); + mpeg_jrevdct((int16 *)dec[index]); + } else { + memset((char *)dec[index], 0, sizeof(Block)); + } + } + + /* now add the motion block */ + AddMotionBlock(dec[0], prev->decoded_y, y, x, motion); + AddMotionBlock(dec[1], prev->decoded_y, y, x+1, motion); + AddMotionBlock(dec[2], prev->decoded_y, y+1, x, motion); + AddMotionBlock(dec[3], prev->decoded_y, y+1, x+1, motion); + AddMotionBlock(dec[4], prev->decoded_cb, y/2, x/2, + halfVector(motion)); + AddMotionBlock(dec[5], prev->decoded_cr, y/2, x/2, + halfVector(motion)); + + /* now, unblockify */ + BlockToData(current->decoded_y, dec[0], y, x); + BlockToData(current->decoded_y, dec[1], y, x+1); + BlockToData(current->decoded_y, dec[2], y+1, x); + BlockToData(current->decoded_y, dec[3], y+1, x+1); + BlockToData(current->decoded_cb, dec[4], y/2, x/2); + BlockToData(current->decoded_cr, dec[5], y/2, x/2); + } + + if ((motion.x == 0) && (motion.y == 0)) { + if ( pattern == 0 ) { + /* can only skip if: + * 1) not the last block in frame + * 2) not the last block in slice + * 3) not the first block in slice + */ + + if ( ((y < lastY) || (x < lastX)) && + (slicePos+1 != blocksPerSlice) && + (slicePos != 0) ) { + mbAddrInc++; /* skipped macroblock */ + numSkipped++; + numPBlocks--; + } else { /* first/last macroblock */ + Mhead_GenMBHeader(bb, 2 /* pict_code_type */, + mbAddrInc /* addr_incr */, + QScale /* q_scale */, + fCode /* forw_f_code */, + 1 /* back_f_code */, + motionRem.x /* horiz_forw_r */, + motionRem.y /* vert_forw_r */, + 0 /* horiz_back_r */, + 0 /* vert_back_r */, + 1 /* motion_forw */, + motionQuot.x /* m_horiz_forw */, + motionQuot.y /* m_vert_forw */, + 0 /* motion_back */, + 0 /* m_horiz_back */, + 0 /* m_vert_back */, + 0 /* mb_pattern */, + 0 /* mb_intra */); + mbAddrInc = 1; + } + } else { + DBG_PRINT(("MB Header(%d,%d)\n", x, y)); + Mhead_GenMBHeader(bb, 2 /* pict_code_type */, + mbAddrInc /* addr_incr */, + QScale /* q_scale */, + fCode /* forw_f_code */, + 1 /* back_f_code */, + 0 /* horiz_forw_r */, + 0 /* vert_forw_r */, + 0 /* horiz_back_r */, + 0 /* vert_back_r */, + 0 /* motion_forw */, + 0 /* m_horiz_forw */, + 0 /* m_vert_forw */, + 0 /* motion_back */, + 0 /* m_horiz_back */, + 0 /* m_vert_back */, + pattern /* mb_pattern */, + 0 /* mb_intra */); + mbAddrInc = 1; + } + } else { + /* DBG_PRINT(("MB Header(%d,%d)\n", x, y)); */ + + Mhead_GenMBHeader(bb, 2 /* pict_code_type */, + mbAddrInc /* addr_incr */, + QScale /* q_scale */, + fCode /* forw_f_code */, + 1 /* back_f_code */, + motionRem.x /* horiz_forw_r */, + motionRem.y /* vert_forw_r */, + 0 /* horiz_back_r */, + 0 /* vert_back_r */, + 1 /* motion_forw */, + motionQuot.x /* m_horiz_forw */, + motionQuot.y /* m_vert_forw */, + 0 /* motion_back */, + 0 /* m_horiz_back */, + 0 /* m_vert_back */, + pattern /* mb_pattern */, + 0 /* mb_intra */); + mbAddrInc = 1; + } + + /* now output the difference */ + { + unsigned int x; + for (x = 0; x < 6; ++x) { + if (GET_ITH_BIT(pattern, 5-x)) + Mpost_RLEHuffPBlock(fba[x], bb); + } + } + numPBits += (bb->cumulativeBits-totalBits); + totalBits = bb->cumulativeBits; + } + + if (overflowChange) { + /* undo an overflow-caused Qscale change */ + overflowChange = FALSE; + QScale -= overflowValue; + overflowValue = 0; + } + + mbAddress++; + /* Rate Control */ + if (bitstreamMode == FIXED_RATE) { + incMacroBlockBits( bb->cumulativeBits- rc_blockStart); + rc_blockStart = bb->cumulativeBits; + MB_RateOut(TYPE_PFRAME); + } + } + } + + if ( printSNR ) { + BlockComputeSNR(current,snr,psnr); + totalSNR += snr[0]; + totalPSNR += psnr[0]; + } + +#ifdef BLEAHBLEAH + { + FILE *filePtr; + + filePtr = fopen("PFRAME.yuv", "wb"); + + for ( y = 0; y < Fsize_y; y++ ) + { + for ( x = 0; x < Fsize_x; x++ ) + fprintf(filePtr, "%d ", current->decoded_y[y][x]); + fprintf(filePtr, "\n"); + } + + fclose(filePtr); + } +#endif + + Mhead_GenSliceEnder(bb); + /* Rate Control */ + if (bitstreamMode == FIXED_RATE) { + updateRateControl(TYPE_PFRAME); + } + + /* UPDATE STATISTICS */ + endTime = time_elapsed(); + totalTime += (endTime-startTime); + + if ( showBitRatePerFrame ) { + /* ASSUMES 30 FRAMES PER SECOND */ + fprintf(bitRateFile, "%5d\t%8d\n", current->id, + 30*(bb->cumulativeBits-totalFrameBits)); + } + + if ( frameSummary && (! realQuiet) ) { + fprintf(stdout, "FRAME %d (P): I BLOCKS: %d; " + "P BLOCKS: %d SKIPPED: %d (%ld seconds)\n", + current->id, numIBlocks, numPBlocks, numSkipped, + (long)(endTime-startTime)/TIME_RATE); + if ( printSNR ) { + fprintf(stdout, "FRAME %d: SNR: %.1f\t%.1f\t%.1f\t" + "PSNR: %.1f\t%.1f\t%.1f\n", + current->id, snr[0], snr[1], snr[2], + psnr[0], psnr[1], psnr[2]); + } + } + + numFrameBits += (bb->cumulativeBits-totalFrameBits); + numPIBlocks += numIBlocks; + numPPBlocks += numPBlocks; + numPSkipped += numSkipped; + numPIBits += numIBits; + numPPBits += numPBits; +} + + +/*===========================================================================* + * + * ResetPFrameStats + * + * reset the P-frame statistics + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void + ResetPFrameStats() +{ + numPIBlocks = 0; + numPPBlocks = 0; + numPSkipped = 0; + numPIBits = 0; + numPPBits = 0; + numFrames = 0; + numFrameBits = 0; + totalTime = 0; +} + + +/*===========================================================================* + * + * SetPQScale + * + * set the P-frame Q-scale + * + * RETURNS: nothing + * + * SIDE EFFECTS: qscaleP + * + *===========================================================================*/ +void + SetPQScale(qP) +int qP; +{ + qscaleP = qP; +} + + +/*===========================================================================* + * + * GetPQScale + * + * return the P-frame Q-scale + * + * RETURNS: the P-frame Q-scale + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +int + GetPQScale() +{ + return qscaleP; +} + + +float +PFrameTotalTime(void) { + return (float)totalTime/(float)TIME_RATE; +} + + + +void +ShowPFrameSummary(unsigned int const inputFrameBits, + unsigned int const totalBits, + FILE * const fpointer) { + + if (numFrames > 0) { + + fprintf(fpointer, "-------------------------\n"); + fprintf(fpointer, "*****P FRAME SUMMARY*****\n"); + fprintf(fpointer, "-------------------------\n"); + + if ( numPIBlocks != 0 ) { + fprintf(fpointer, " I Blocks: %5d (%6d bits) (%5d bpb)\n", + numPIBlocks, numPIBits, numPIBits/numPIBlocks); + } else { + fprintf(fpointer, " I Blocks: %5d\n", 0); + } + + if ( numPPBlocks != 0 ) { + fprintf(fpointer, " P Blocks: %5d (%6d bits) (%5d bpb)\n", + numPPBlocks, numPPBits, numPPBits/numPPBlocks); + } else { + fprintf(fpointer, " P Blocks: %5d\n", 0); + } + + fprintf(fpointer, " Skipped: %5d\n", numPSkipped); + + fprintf(fpointer, " Frames: %5d (%6d bits) (%5d bpf) (%2.1f%% of total)\n", + numFrames, numFrameBits, numFrameBits/numFrames, + 100.0*(float)numFrameBits/(float)totalBits); + fprintf(fpointer, " Compression: %3d:1 (%9.4f bpp)\n", + numFrames*inputFrameBits/numFrameBits, + 24.0*(float)numFrameBits/(float)(numFrames*inputFrameBits)); + if ( printSNR ) + fprintf(fpointer, " Avg Y SNR/PSNR: %.1f %.1f\n", + totalSNR/(float)numFrames, totalPSNR/(float)numFrames); + if ( totalTime == 0 ) { + fprintf(fpointer, " Seconds: NONE\n"); + } else { + fprintf(fpointer, " Seconds: %9ld (%9.4f fps) (%9ld pps) (%9ld mps)\n", + (long)(totalTime/TIME_RATE), + (float)((float)(TIME_RATE*numFrames)/(float)totalTime), + (long)((float)TIME_RATE*(float)numFrames*(float)inputFrameBits/(24.0*(float)totalTime)), + (long)((float)TIME_RATE*(float)numFrames*(float)inputFrameBits/(256.0*24.0*(float)totalTime))); + } + } +} + + + +/*===========================================================================* + * + * EstimateSecondsPerPFrame + * + * compute an estimate of the number of seconds required per P-frame + * + * RETURNS: the estimate, in seconds + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +float + EstimateSecondsPerPFrame() +{ + if ( numFrames == 0 ) { + return 10.0; + } else { + return (float)totalTime/((float)TIME_RATE*(float)numFrames); + } +} + + +/*===========================================================================* + * + * ComputeHalfPixelData + * + * compute all half-pixel data required for half-pixel motion vector + * search (luminance only) + * + * RETURNS: frame->halfX, ->halfY, and ->halfBoth modified + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void + ComputeHalfPixelData(frame) +MpegFrame *frame; +{ + register int x, y; + + /* we add 1 before dividing by 2 because .5 is supposed to be rounded up + * (see MPEG-1, page D-31) + */ + + if ( frame->halfX == NULL ) { /* need to allocate memory */ + Frame_AllocHalf(frame); + } + + /* compute halfX */ + for ( y = 0; y < Fsize_y; y++ ) { + for ( x = 0; x < Fsize_x-1; x++ ) { + frame->halfX[y][x] = (frame->ref_y[y][x]+ + frame->ref_y[y][x+1]+1)>>1; + } + } + + /* compute halfY */ + for ( y = 0; y < Fsize_y-1; y++ ) { + for ( x = 0; x < Fsize_x; x++ ) { + frame->halfY[y][x] = (frame->ref_y[y][x]+ + frame->ref_y[y+1][x]+1)>>1; + } + } + + /* compute halfBoth */ + for ( y = 0; y < Fsize_y-1; y++ ) { + for ( x = 0; x < Fsize_x-1; x++ ) { + frame->halfBoth[y][x] = (frame->ref_y[y][x]+ + frame->ref_y[y][x+1]+ + frame->ref_y[y+1][x]+ + frame->ref_y[y+1][x+1]+2)>>2; + } + } + + frame->halfComputed = TRUE; +} + + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ diff --git a/converter/ppm/ppmtompeg/postdct.c b/converter/ppm/ppmtompeg/postdct.c new file mode 100644 index 00000000..56a23de9 --- /dev/null +++ b/converter/ppm/ppmtompeg/postdct.c @@ -0,0 +1,626 @@ +/*===========================================================================* + * postdct.c * + * * + * Procedures concerned with MPEG post-DCT processing: * + * quantization and RLE Huffman encoding * + * * + * EXPORTED PROCEDURES: * + * Mpost_QuantZigBlock * + * Mpost_RLEHuffIBlock * + * Mpost_RLEHuffPBlock * + * Mpost_UnQuantZigBlock * + * * + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/* + * $Header: /n/picasso/project/mm/mpeg/mpeg_dist/mpeg_encode/RCS/postdct.c,v 1.12 1995/06/21 18:26:39 smoot Exp $ + * $Log: postdct.c,v $ + * Revision 1.12 1995/06/21 18:26:39 smoot + * added length estimator for P-blocks + * + * Revision 1.11 1995/04/23 23:22:59 eyhung + * nothing changed + * + * Revision 1.10 1995/04/14 23:10:46 smoot + * Added overflow detection to MPOST_DCT so it will adjust Qscales (above) + * + * Revision 1.9 1995/02/15 23:15:32 smoot + * killed useless asserts + * + * Revision 1.8 1995/02/01 21:48:41 smoot + * assure out is set properly, short circuit 0 revquant + * + * Revision 1.7 1995/01/30 19:56:37 smoot + * Killed a <0 shift + * + * Revision 1.6 1995/01/25 23:07:33 smoot + * Better DBG_PRINTs, multiply/divide instead of shifts + * + * Revision 1.5 1995/01/19 23:09:10 eyhung + * Changed copyrights + * + * Revision 1.4 1995/01/16 08:17:08 eyhung + * added realQuiet + * + * Revision 1.3 1994/11/12 02:11:58 keving + * nothing + * + * Revision 1.2 1994/03/15 00:27:11 keving + * nothing + * + * Revision 1.2 1994/03/15 00:27:11 keving + * nothing + * + * Revision 1.1 1993/12/22 19:19:01 keving + * nothing + * + * Revision 1.11 1993/07/22 22:23:43 keving + * nothing + * + * Revision 1.10 1993/06/30 20:06:09 keving + * nothing + * + * Revision 1.9 1993/06/03 21:08:08 keving + * nothing + * + * Revision 1.8 1993/02/24 18:57:19 keving + * nothing + * + * Revision 1.7 1993/02/23 22:58:36 keving + * nothing + * + * Revision 1.6 1993/02/23 22:54:56 keving + * nothing + * + * Revision 1.5 1993/02/17 23:18:20 dwallach + * checkin prior to keving's joining the project + * + * Revision 1.4 1993/01/18 10:20:02 dwallach + * *** empty log message *** + * + * Revision 1.3 1993/01/18 10:17:29 dwallach + * RCS headers installed, code indented uniformly + * + * Revision 1.3 1993/01/18 10:17:29 dwallach + * RCS headers installed, code indented uniformly + * + */ + + +/*==============* + * HEADER FILES * + *==============*/ + +#include <assert.h> +#include "all.h" +#include "mtypes.h" +#include "bitio.h" +#include "huff.h" +#include "postdct.h" +#include "opts.h" + +/*==================* + * STATIC VARIABLES * + *==================*/ + +/* ZAG[i] is the natural-order position of the i'th element of zigzag order. */ +int ZAG[] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63 +}; + +/* + * possible optimization: reorder the qtable in the correct zigzag order, to + * reduce the number of necessary lookups + * + * this table comes from the MPEG draft, p. D-16, Fig. 2-D.15. + */ +int32 qtable[] = { + 8, 16, 19, 22, 26, 27, 29, 34, + 16, 16, 22, 24, 27, 29, 34, 37, + 19, 22, 26, 27, 29, 34, 34, 38, + 22, 22, 26, 27, 29, 34, 37, 40, + 22, 26, 27, 29, 32, 35, 40, 48, + 26, 27, 29, 32, 35, 40, 48, 58, + 26, 27, 29, 34, 38, 46, 56, 69, + 27, 29, 35, 38, 46, 56, 69, 83 +}; + +int32 niqtable[] = { + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16 +}; + +int32 *customQtable = NULL; +int32 *customNIQtable = NULL; + +/*==================* + * GLOBAL VARIABLES * + *==================*/ + +extern boolean realQuiet; + +/*=====================* + * EXPORTED PROCEDURES * + *=====================*/ + +/*===========================================================================* + * + * Mpost_UnQuantZigBlock + * + * unquantize and zig-zag (decode) a single block + * see section 2.4.4.1 of MPEG standard + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void +Mpost_UnQuantZigBlock(in, out, qscale, iblock) + FlatBlock in; + Block out; + int qscale; + boolean iblock; +{ + register int index; + int start; + int position; + register int qentry; + int level, coeff; + + if ( iblock ) { + /* qtable[0] must be 8 */ + out[0][0] = (int16)(in[0] * 8); + + /* don't need to do anything fancy here, because we saved orig + value, not encoded dc value */ + start = 1; + } else { + start = 0; + } + + for ( index = start; index < DCTSIZE_SQ; index++ ) { + position = ZAG[index]; + level = in[index]; + + if (level == 0) { + ((int16 *)out)[position] = 0; + continue; + } + + + if ( iblock ) { + qentry = qtable[position] * qscale; + coeff = (level*qentry)/8; + if ( (coeff & 1) == 0 ) { + if ( coeff < 0 ) { + coeff++; + } else if ( coeff > 0 ) { + coeff--; + } + } + } else { + qentry = niqtable[position] * qscale; + if ( level == 0 ) { + coeff = 0; + } else if ( level < 0 ) { + coeff = (((2*level)-1)*qentry) / 16; + if ( (coeff & 1) == 0 ) { + coeff++; + } + } else { + coeff = (((2*level)+1)*qentry) >> 4; + if ( (coeff & 1) == 0 ) { + coeff--; + } + } + + if ( coeff > 2047 ) { + coeff = 2047; + } else if ( coeff < -2048 ) { + coeff = -2048; + } + } + + ((int16 *)out)[position] = coeff; + } +} + + +/*===========================================================================* + * + * Mpost_QuantZigBlock + * + * quantize and zigzags a block + * + * RETURNS: MPOST_OVERFLOW if a generated value is outside |255| + * MPOST_ZERO if all coeffs are zero + * MPOST_NON_ZERO otherwisw + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +int +Mpost_QuantZigBlock(in, out, qscale, iblock) + Block in; + FlatBlock out; + register int qscale; + int iblock; +{ + register int i; + register int16 temp; + register int qentry; + register int position; + boolean nonZero = FALSE; + boolean overflow = FALSE; + + DBG_PRINT(("Mpost_QuantZigBlock...\n")); + if (iblock) { + /* + * the DC coefficient is handled specially -- it's not + * sensitive to qscale, but everything else is + */ + temp = ((int16 *) in)[ZAG[0]]; + qentry = qtable[ZAG[0]]; + + if (temp < 0) { + temp = -temp; + temp += (qentry >> 1); + temp /= qentry; + temp = -temp; + } else { + temp += (qentry >> 1); + temp /= qentry; + } + if ( temp != 0 ) { + nonZero = TRUE; + } + out[0] = temp; + + for (i = 1; i < DCTSIZE_SQ; i++) { + position = ZAG[i]; + temp = ((int16 *) in)[position]; + qentry = qtable[position] * qscale; + + /* see 1993 MPEG doc, section D.6.3.4 */ + if (temp < 0) { + temp = -temp; + temp = (temp << 3); /* temp > 0 */ + temp += (qentry >> 1); + temp /= qentry; + temp = -temp; + } else { + temp = (temp << 3); /* temp > 0 */ + temp += (qentry >> 1); + temp /= qentry; + } + + if ( temp != 0 ) { + nonZero = TRUE; + out[i] = temp; + if (temp < -255) { + temp = -255; + overflow = TRUE; + } else if (temp > 255) { + temp = 255; + overflow = TRUE; + } + } else out[i]=0; + } + } else { + for (i = 0; i < DCTSIZE_SQ; i++) { + position = ZAG[i]; + temp = ((int16 *) in)[position]; + + /* multiply by non-intra qtable */ + qentry = qscale * niqtable[position]; + + /* see 1993 MPEG doc, D.6.4.5 */ + temp *= 8; + temp /= qentry; /* truncation toward 0 -- correct */ + + if ( temp != 0 ) { + nonZero = TRUE; + out[i] = temp; + if (temp < -255) { + temp = -255; + overflow = TRUE; + } else if (temp > 255) { + temp = 255; + overflow = TRUE; + } + + } else out[i]=0; + } + } + + if (overflow) return MPOST_OVERFLOW; + if (nonZero) return MPOST_NON_ZERO; + return MPOST_ZERO; +} + + + +/*===========================================================================* + * + * Mpost_RLEHuffIBlock + * + * generate the huffman bits from an I-block + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void +Mpost_RLEHuffIBlock(in, out) + FlatBlock in; + BitBucket *out; +{ + register int i; + register int nzeros = 0; + register int16 cur; + register int16 acur; + register uint32 code; + register int nbits; + + /* + * yes, Virginia, we start at 1. The DC coefficient is handled + * specially, elsewhere. Not here. + */ + for (i = 1; i < DCTSIZE_SQ; i++) { + cur = in[i]; + acur = ABS(cur); + if (cur) { + if ( (nzeros < HUFF_MAXRUN) && (acur < huff_maxlevel[nzeros])) { + /* + * encode using the Huffman tables + */ + + DBG_PRINT(("rle_huff %02d.%02d: Run %02d, Level %4d\n", i, ZAG[i], nzeros, cur)); + code = (huff_table[nzeros])[acur]; + nbits = (huff_bits[nzeros])[acur]; + + if (cur < 0) { + code |= 1; /* the sign bit */ + } + Bitio_Write(out, code, nbits); + } else { + /* + * encode using the escape code + */ + DBG_PRINT(("Escape\n")); + Bitio_Write(out, 0x1, 6); /* ESCAPE */ + DBG_PRINT(("Run Length\n")); + Bitio_Write(out, nzeros, 6); /* Run-Length */ + + /* + * this shouldn't happen, but the other + * choice is to bomb out and dump core... + * Hmmm, seems to happen with small Qtable entries (1) -srs + */ + if (cur < -255) { + cur = -255; + } else if (cur > 255) { + cur = 255; + } + + DBG_PRINT(("Level\n")); + if (acur < 128) { + Bitio_Write(out, cur, 8); + } else { + if (cur < 0) { + Bitio_Write(out, 0x8001 + cur + 255, 16); + } else { + Bitio_Write(out, cur, 16); + } + } + } + nzeros = 0; + } else { + nzeros++; + } + } + DBG_PRINT(("End of block\n")); + Bitio_Write(out, 0x2, 2); /* end of block marker */ +} + + +/*===========================================================================* + * + * Mpost_RLEHuffPBlock + * + * generate the huffman bits from an P-block + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void +Mpost_RLEHuffPBlock(in, out) + FlatBlock in; + BitBucket *out; +{ + register int i; + register int nzeros = 0; + register int16 cur; + register int16 acur; + register uint32 code; + register int nbits; + boolean first_dct = TRUE; + + /* + * yes, Virginia, we start at 0. + */ + for (i = 0; i < DCTSIZE_SQ; i++) { + cur = in[i]; + acur = ABS(cur); + if (cur) { + if ((nzeros < HUFF_MAXRUN) && (acur < huff_maxlevel[nzeros])) { + /* + * encode using the Huffman tables + */ + + DBG_PRINT(("rle_huff %02d.%02d: Run %02d, Level %4d\n", i, ZAG[i], nzeros, cur)); + if ( first_dct && (nzeros == 0) && (acur == 1) ) { + /* actually, only needs = 0x2 */ + code = (cur == 1) ? 0x2 : 0x3; + nbits = 2; + } else { + code = (huff_table[nzeros])[acur]; + nbits = (huff_bits[nzeros])[acur]; + } + + assert(nbits); + + if (cur < 0) { + code |= 1; /* the sign bit */ + } + Bitio_Write(out, code, nbits); + first_dct = FALSE; + } else { + /* + * encode using the escape code + */ + DBG_PRINT(("Escape\n")); + Bitio_Write(out, 0x1, 6); /* ESCAPE */ + DBG_PRINT(("Run Length\n")); + Bitio_Write(out, nzeros, 6); /* Run-Length */ + + /* + * this shouldn't happen, but the other + * choice is to bomb out and dump core... + * Hmmm, seems to happen with small Qtable entries (1) -srs + */ + if (cur < -255) { + cur = -255; + } else if (cur > 255) { + cur = 255; + } + + DBG_PRINT(("Level\n")); + if (acur < 128) { + Bitio_Write(out, cur, 8); + } else { + if (cur < 0) { + Bitio_Write(out, 0x8001 + cur + 255, 16); + } else { + Bitio_Write(out, cur, 16); + } + } + + first_dct = FALSE; + } + nzeros = 0; + } else { + nzeros++; + } + } + + /* actually, should REALLY return FALSE and not use this! */ + + if ( first_dct ) { /* have to give a first_dct even if all 0's */ + fprintf(stderr, "HUFF called with all-zero coefficients\n"); + fprintf(stderr, "exiting...\n"); + exit(1); + } + + DBG_PRINT(("End of block\n")); + Bitio_Write(out, 0x2, 2); /* end of block marker */ +} + + +/*===========================================================================* + * + * CalcRLEHuffLength + * + * count the huffman bits for an P-block + * + * RETURNS: number of bits + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +int +CalcRLEHuffLength(in) + FlatBlock in; +{ + register int i; + register int nzeros = 0; + register int16 cur; + register int16 acur; + register int nbits; + register int countbits=0; + boolean first_dct = TRUE; + + for (i = 0; i < DCTSIZE_SQ; i++) { + cur = in[i]; + acur = ABS(cur); + if (cur) { + if ((nzeros < HUFF_MAXRUN) && (acur < huff_maxlevel[nzeros])) { + /* + * encode using the Huffman tables + */ + + if ( first_dct && (nzeros == 0) && (acur == 1) ) { + nbits = 2; + } else { + nbits = (huff_bits[nzeros])[acur]; + } + countbits += nbits; + first_dct = FALSE; + } else { + countbits += 12; /* ESCAPE + runlength */ + + if (acur < 128) { + countbits += 8; + } else { + countbits += 16; + } + + first_dct = FALSE; + } + nzeros = 0; + } else { + nzeros++; + } + } + + countbits += 2; /* end of block marker */ + return countbits; +} diff --git a/converter/ppm/ppmtompeg/ppmtompeg.c b/converter/ppm/ppmtompeg/ppmtompeg.c new file mode 100644 index 00000000..2296e2cb --- /dev/null +++ b/converter/ppm/ppmtompeg/ppmtompeg.c @@ -0,0 +1,722 @@ +/*===========================================================================* + * main.c + * + * Main procedure + * + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/*==============* + * HEADER FILES * + *==============*/ + +#define _BSD_SOURCE /* Make sure strdup() is in string.h */ + +#include <assert.h> +#include <sys/utsname.h> + +#include "all.h" +#include "mtypes.h" +#include "mpeg.h" +#include "motion_search.h" +#include "prototypes.h" +#include "param.h" +#include "parallel.h" +#include "readframe.h" +#include "combine.h" +#include "frames.h" +#include "jpeg.h" +#include "specifics.h" +#include "opts.h" +#include "frametype.h" +#include "input.h" +#include "gethostname.h" + +#include "pm_c_util.h" +#include "ppm.h" +#include "nstring.h" + +#include <time.h> + +int main _ANSI_ARGS_((int argc, char **argv)); + + +/*==================* + * GLOBAL VARIABLES * + *==================*/ + +boolean showBitRatePerFrame; +boolean frameSummary; + +extern time_t IOtime; +int whichGOP = -1; +boolean ioServer = FALSE; +boolean outputServer = FALSE; +boolean decodeServer = FALSE; +int quietTime = 0; +boolean realQuiet = FALSE; +boolean noFrameSummaryOption = FALSE; +boolean debugSockets = FALSE; +boolean debugMachines = FALSE; +boolean bitRateInfoOption = FALSE; +boolean computeMVHist = FALSE; +int baseFormat; +extern boolean specificsOn; +extern FrameSpecList *fsl; +boolean pureDCT=FALSE; +char encoder_name[1024]; +const char * hostname; + + +/*================================* + * External PROCEDURE prototypes * + *================================*/ + +void init_idctref _ANSI_ARGS_((void)); +void init_fdct _ANSI_ARGS_((void)); + + +struct cmdlineInfo { + bool childProcess; + int function; + const char * masterHostname; + int masterPortNumber; + unsigned int outputFrames; + int maxMachines; + const char * paramFileName; + bool specificFrames; + unsigned int frameStart; + unsigned int frameEnd; +}; + + + +static void +parseArgs(int const argc, + char ** const argv, + struct cmdlineInfo * const cmdlineP) { + + int idx; + + if (argc-1 < 1) + pm_error("You must specify at least one argument: the parameter " + "file name"); + + cmdlineP->function = ENCODE_FRAMES; + cmdlineP->childProcess = FALSE; /* initial assumption */ + cmdlineP->outputFrames = 0; + cmdlineP->maxMachines = MAXINT; + cmdlineP->specificFrames = FALSE; + + /* parse the arguments */ + idx = 1; + while ( idx < argc-1 ) { + if ( argv[idx][0] != '-' ) + pm_error("argument '%s', which must be an option because " + "it is not the last argument, " + "does not start with '-'", argv[idx]); + + if ( strcmp(argv[idx], "-stat") == 0 ) { + if ( idx+1 < argc-1 ) { + SetStatFileName(argv[idx+1]); + idx += 2; + } else { + pm_error("Invalid -stat option"); + } + } else if ( strcmp(argv[idx], "-gop") == 0 ) { + if ((cmdlineP->function != ENCODE_FRAMES) || + (cmdlineP->specificFrames)) + pm_error("Invalid -gop option"); + + if ( idx+1 < argc-1 ) { + whichGOP = atoi(argv[idx+1]); + idx += 2; + } else { + pm_error("Invalid -gop option"); + } + } else if ( strcmp(argv[idx], "-frames") == 0 ) { + if ( (cmdlineP->function != ENCODE_FRAMES) || (whichGOP != -1) ) { + pm_error("invalid -frames option"); + } + + if ( idx+2 < argc-1 ) { + int const frameStart = atoi(argv[idx+1]); + int const frameEnd = atoi(argv[idx+2]); + + if (frameStart > frameEnd) + pm_error("Start frame number %d is greater than end " + "frame number %d", frameStart, frameEnd); + if (frameStart < 0) + pm_error("Start frame number %d is less than zero", + frameStart); + + cmdlineP->specificFrames = TRUE; + cmdlineP->frameStart = frameStart; + cmdlineP->frameEnd = frameEnd; + + idx += 3; + } else + pm_error("-frames needs to be followed by two values"); + } else if (strcmp(argv[idx], "-combine_gops") == 0) { + if ((cmdlineP->function != ENCODE_FRAMES) || (whichGOP != -1) || + (cmdlineP->specificFrames)) { + pm_error("Invalid -combine_gops option"); + } + + cmdlineP->function = COMBINE_GOPS; + idx++; + } else if (strcmp(argv[idx], "-combine_frames") == 0) { + if ((cmdlineP->function != ENCODE_FRAMES) || (whichGOP != -1) || + (cmdlineP->specificFrames)) + pm_error("Invalid -combine_frames option"); + + cmdlineP->function = COMBINE_FRAMES; + idx++; + } else if ( strcmp(argv[idx], "-child") == 0 ) { + if ( idx+7 < argc-1 ) { + int combinePortNumber; + /* This used to be important information, when the child + notified the combine server. Now the master notifies + the combine server after the child notifies the master + it is done. So this value is unused. + */ + cmdlineP->masterHostname = argv[idx+1]; + cmdlineP->masterPortNumber = atoi(argv[idx+2]); + ioPortNumber = atoi(argv[idx+3]); + combinePortNumber = atoi(argv[idx+4]); + decodePortNumber = atoi(argv[idx+5]); + machineNumber = atoi(argv[idx+6]); + remoteIO = atoi(argv[idx+7]); + + IOhostName = cmdlineP->masterHostname; + } else + pm_error("Not enough option values for -child option. " + "Need 7."); + + cmdlineP->childProcess = TRUE; + idx += 8; + } else if ( strcmp(argv[idx], "-io_server") == 0 ) { + if ( idx+2 < argc-1 ) { + cmdlineP->masterHostname = argv[idx+1]; + cmdlineP->masterPortNumber = atoi(argv[idx+2]); + } else { + pm_error("Invalid -io_server option"); + } + + ioServer = TRUE; + idx += 3; + } else if ( strcmp(argv[idx], "-output_server") == 0 ) { + if ( idx+3 < argc-1 ) { + cmdlineP->masterHostname = argv[idx+1]; + cmdlineP->masterPortNumber = atoi(argv[idx+2]); + cmdlineP->outputFrames = atoi(argv[idx+3]); + } else { + pm_error("-output_server option requires 3 option values. " + "You specified %d", argc-1 - idx); + } + + outputServer = TRUE; + idx += 4; + } else if ( strcmp(argv[idx], "-decode_server") == 0 ) { + if ( idx+3 < argc-1 ) { + cmdlineP->masterHostname = argv[idx+1]; + cmdlineP->masterPortNumber = atoi(argv[idx+2]); + cmdlineP->outputFrames = atoi(argv[idx+3]); + } else { + pm_error("Invalid -decode_server option"); + } + + cmdlineP->function = COMBINE_FRAMES; + decodeServer = TRUE; + idx += 4; + } else if ( strcmp(argv[idx], "-nice") == 0 ) { + niceProcesses = TRUE; + idx++; + } else if ( strcmp(argv[idx], "-max_machines") == 0 ) { + if ( idx+1 < argc-1 ) { + cmdlineP->maxMachines = atoi(argv[idx+1]); + } else { + pm_error("Invalid -max_machines option"); + } + + idx += 2; + } else if ( strcmp(argv[idx], "-quiet") == 0 ) { + if ( idx+1 < argc-1 ) { + quietTime = atoi(argv[idx+1]); + } else { + pm_error("Invalid -quiet option"); + } + + idx += 2; + } else if ( strcmp(argv[idx], "-realquiet") == 0 ) { + realQuiet = TRUE; + idx++; + } else if (( strcmp(argv[idx], "-float_dct") == 0 ) || + ( strcmp(argv[idx], "-float-dct") == 0 )) { + pureDCT = TRUE; + init_idctref(); + init_fdct(); + idx++; + } else if ( strcmp(argv[idx], "-no_frame_summary") == 0 ) { + if ( idx < argc-1 ) { + noFrameSummaryOption = TRUE; + } else { + pm_error("Invalid -no_frame_summary option"); + } + + idx++; + } else if ( strcmp(argv[idx], "-snr") == 0 ) { + printSNR = TRUE; + idx++; + } else if ( strcmp(argv[idx], "-mse") == 0 ) { + printSNR = printMSE = TRUE; + idx++; + } else if ( strcmp(argv[idx], "-debug_sockets") == 0 ) { + debugSockets = TRUE; + idx++; + } else if ( strcmp(argv[idx], "-debug_machines") == 0 ) { + debugMachines = TRUE; + idx++; + } else if ( strcmp(argv[idx], "-bit_rate_info") == 0 ) { + if ( idx+1 < argc-1 ) { + bitRateInfoOption = TRUE; + SetBitRateFileName(argv[idx+1]); + idx += 2; + } else { + pm_error("Invalid -bit_rate_info option"); + } + } else if ( strcmp(argv[idx], "-mv_histogram") == 0 ) { + computeMVHist = TRUE; + idx++; + } else { + pm_error("Unrecognized option: '%s'", argv[idx]); + } + } + + cmdlineP->paramFileName = argv[argc-1]; +} + + + +static void +compileTests() { + + /* There was code here (assert() calls) that verified that uint16 + is 16 bits, etc. It caused compiler warnings that said, "Of + course it is!" (actually, "statement has no effect"). There + isn't enough chance that uint16, etc. are defined incorrectly + to warrant those asserts. 2000.05.07 + */ + + if ( (-8 >> 3) != -1 ) { + fprintf(stderr, "ERROR: Right shifts are NOT arithmetic!!!\n"); + fprintf(stderr, "Change >> to multiplies by powers of 2\n"); + exit(1); + } +} + + + +static void +announceJob(enum frameContext const context, + bool const childProcess, + unsigned int const frameStart, + unsigned int const frameEnd, + const char * const outputFileName) { + + if (!realQuiet) { + const char * outputDest; + const char * combineDest; + + if (context == CONTEXT_JUSTFRAMES) + asprintfN(&outputDest, "to individual frame files"); + else + asprintfN(&outputDest, "to file '%s'", outputFileName); + + if (childProcess) + combineDest = strdup("for delivery to combine server"); + else + combineDest = strdup(""); + + pm_message("%s: ENCODING FRAMES %u-%u to %s %s", + hostname, frameStart, frameEnd, outputDest, combineDest); + + strfree(combineDest); + strfree(outputDest); + } +} + + + +static void +encodeSomeFrames(struct inputSource * const inputSourceP, + boolean const childProcess, + enum frameContext const context, + unsigned int const frameStart, + unsigned int const frameEnd, + int32 const qtable[], + int32 const niqtable[], + FILE * const ofp, + const char * const outputFileName, + bool const wantVbvUnderflowWarning, + bool const wantVbvOverflowWarning, + boolean const printStats, + unsigned int * const encodeTimeP) { + + time_t framesTimeStart, framesTimeEnd; + unsigned int inputFrameBits; + unsigned int totalBits; + + announceJob(context, childProcess, frameStart, frameEnd, outputFileName); + + time(&framesTimeStart); + if (printStats) + PrintStartStats(framesTimeStart, context == CONTEXT_JUSTFRAMES, + frameStart, frameEnd, inputSourceP); + + GenMPEGStream(inputSourceP, context, frameStart, frameEnd, + qtable, niqtable, childProcess, ofp, outputFileName, + wantVbvUnderflowWarning, wantVbvOverflowWarning, + &inputFrameBits, &totalBits); + + time(&framesTimeEnd); + + *encodeTimeP = (unsigned int)(framesTimeEnd - framesTimeStart); + + if (!realQuiet) + pm_message("%s: COMPLETED FRAMES %u-%u (%u seconds)", + hostname, frameStart, frameEnd, *encodeTimeP); + + if (printStats) + PrintEndStats(framesTimeStart, framesTimeEnd, + inputFrameBits, totalBits); +} + + + + +static void +encodeFrames(struct inputSource * const inputSourceP, + boolean const childProcess, + const char * const masterHostname, + int const masterPortNumber, + int const whichGOP, + bool const specificFrames, + unsigned int const whichFrameStart, + unsigned int const whichFrameEnd, + int32 const qtable[], + int32 const niqtable[], + FILE * const ofp, + const char * const outputFileName, + bool const wantVbvUnderflowWarning, + bool const wantVbvOverflowWarning) { +/*---------------------------------------------------------------------------- + Encode the stream. If 'specificFrames' is true, then encode frames + 'whichFrameStart' through 'whichFrameEnd' individually. Otherwise, + encode the entire input stream as a complete MPEG stream. + + 'childProcess' means to do it as a child process that is under the + supervision of a master process and is possibly doing only part of + a larger batch. + + (If we had proper modularity, we wouldn't care, but parallel operation + was glued on to this program after it was complete). + + One thing we don't do when running as a child process is print + statistics; our master will do that for the whole job. +----------------------------------------------------------------------------*/ + unsigned int frameStart, frameEnd; + enum frameContext context; + unsigned int lastEncodeTime; + /* How long it took the encoder to do the last set of frames */ + boolean printStats; + /* We want the encoder to print start & end stats */ + + if (whichGOP != -1) { + /* He wants just one particular GOP from the middle of the movie. */ + ComputeGOPFrames(whichGOP, &frameStart, &frameEnd, + inputSourceP->numInputFiles); + context = CONTEXT_GOP; + } else if (specificFrames) { + /* He wants some pure frames from the middle of the movie */ + if (whichFrameStart > whichFrameEnd) + pm_error("You specified a starting frame number (%d) that is " + "greater than the ending frame number (%d) " + "you specified.", whichFrameStart, whichFrameEnd); + if (whichFrameEnd + 1 > inputSourceP->numInputFiles) + pm_error("You specified ending frame number %d, which is " + "beyond the number of input files you supplied (%u)", + whichFrameEnd, inputSourceP->numInputFiles); + + frameStart = whichFrameStart; + frameEnd = whichFrameEnd; + context = CONTEXT_JUSTFRAMES; + } else { + /* He wants the whole movie */ + frameStart = 0; + frameEnd = inputSourceP->numInputFiles - 1; + context = CONTEXT_WHOLESTREAM; + } + + printStats = !childProcess; + + encodeSomeFrames(inputSourceP, childProcess, context, frameStart, frameEnd, + customQtable, customNIQtable, + ofp, outputFileName, + wantVbvUnderflowWarning, wantVbvOverflowWarning, + printStats, + &lastEncodeTime); + + if (childProcess) { + boolean moreWorkToDo; + + /* A child is not capable of generating GOP or stream headers */ + assert(context == CONTEXT_JUSTFRAMES); + + moreWorkToDo = TRUE; /* initial assumption */ + while (moreWorkToDo) { + int nextFrameStart, nextFrameEnd; + + NotifyMasterDone(masterHostname, masterPortNumber, machineNumber, + lastEncodeTime, &moreWorkToDo, + &nextFrameStart, &nextFrameEnd); + if (moreWorkToDo) + encodeSomeFrames(inputSourceP, childProcess, + CONTEXT_JUSTFRAMES, + nextFrameStart, nextFrameEnd, + customQtable, customNIQtable, + NULL, outputFileName, + wantVbvUnderflowWarning, + wantVbvOverflowWarning, + FALSE, + &lastEncodeTime); + } + if (!realQuiet) + pm_message("%s: Child exiting. Master has no more work.", + hostname); + } +} + + + + +static void +runMaster(struct inputSource * const inputSourceP, + const char * const paramFileName, + const char * const outputFileName) { + + if (paramFileName[0] != '/' && paramFileName[0] != '~') + pm_error("For parallel mode, you must " + "use an absolute path for parameter file. " + "You specified '%s'", paramFileName); + else + MasterServer(inputSourceP, paramFileName, outputFileName); +} + + + +static void +getUserFrameFile(void * const handle, + unsigned int const frameNumber, + FILE ** const ifPP) { + + struct inputSource * const inputSourceP = (struct inputSource *) handle; + + if (inputSourceP->stdinUsed) + pm_error("You cannot combine frames from Standard Input."); + /* Because Caller detects end of frames by EOF */ + + if (frameNumber >= inputSourceP->numInputFiles) + *ifPP = NULL; + else { + const char * fileName; + const char * inputFileName; + + GetNthInputFileName(inputSourceP, frameNumber, &inputFileName); + + asprintfN(&fileName, "%s/%s", currentFramePath, inputFileName); + + *ifPP = fopen(fileName, "rb"); + if (*ifPP == NULL) + pm_error("Unable to open file '%s'. Errno = %d (%s)", + fileName, errno, strerror(errno)); + + strfree(inputFileName); + strfree(fileName); + } +} + + + +static void +nullDisposeFile(void * const handle, + unsigned int const frameNumber) { + + +} + + + +static unsigned int +framePoolSize(bool const sequentialInput) { +/*---------------------------------------------------------------------------- + Return the number of frames that our frame memory pool needs to have. + + 'sequentialInput' means we'll be reading from input that comes in frame + number order and we can't go back, so we'll have to have a bigger buffer + of frames than otherwise. +-----------------------------------------------------------------------------*/ + unsigned int numOfFrames; + + numOfFrames = 0; /* initial value */ + + if (sequentialInput) { + unsigned int idx; + unsigned int bcount; + + for ( idx = 0, bcount = 0; idx < strlen(framePattern); idx++) { + + /* counts the maximum number of B frames between two reference + * frames. + */ + + switch( framePattern[idx] ) { + case 'b': + bcount++; + break; + case 'i': + case 'p': + if (bcount > numOfFrames) + numOfFrames = bcount; + bcount = 0; + break; + } + + /* add 2 to hold the forward and past reference frames in addition + * to the maximum number of B's + */ + } + + numOfFrames += 2; + + } else { + /* non-interactive, only 3 frames needed */ + numOfFrames = 3; + } + return numOfFrames; +} + + + +int +main(int argc, char **argv) { + FILE *ofP; + time_t initTimeStart; + struct cmdlineInfo cmdline; + struct params params; + + ppm_init(&argc, argv); + + strcpy(encoder_name, argv[0]); + + compileTests(); + + time(&initTimeStart); + + SetStatFileName(""); + + hostname = GetHostName(); + + parseArgs(argc, argv, &cmdline); + + ReadParamFile(cmdline.paramFileName, cmdline.function, ¶ms); + + /* Jim Boucher's stuff: + if we are using a movie format then break up into frames + */ + if ((!cmdline.childProcess) && (baseFormat == JMOVIE_FILE_TYPE)) + JM2JPEG(params.inputSourceP); + + if (printSNR || (referenceFrame == DECODED_FRAME)) + decodeRefFrames = TRUE; + + showBitRatePerFrame = (bitRateInfoOption && !cmdline.childProcess); + frameSummary = (!noFrameSummaryOption && !cmdline.childProcess); + + numMachines = min(numMachines, cmdline.maxMachines); + + Tune_Init(); + Frame_Init(framePoolSize(params.inputSourceP->stdinUsed)); + + if (specificsOn) + Specifics_Init(); + + ComputeFrameTable(params.inputSourceP->stdinUsed ? + 0 : params.inputSourceP->numInputFiles); + + if (ioServer) { + IoServer(params.inputSourceP, cmdline.masterHostname, + cmdline.masterPortNumber); + return 0; + } else if (outputServer) { + CombineServer(cmdline.outputFrames, + cmdline.masterHostname, cmdline.masterPortNumber, + outputFileName); + } else if (decodeServer) { + DecodeServer(cmdline.outputFrames, outputFileName, + cmdline.masterHostname, cmdline.masterPortNumber); + } else { + if ((!cmdline.specificFrames) && + ((numMachines == 0) || (cmdline.function != ENCODE_FRAMES)) ) { + ofP = fopen(outputFileName, "wb"); + if (ofP == NULL) + pm_error("Could not open output file!"); + } else + ofP = NULL; + + if (cmdline.function == ENCODE_FRAMES) { + if ((numMachines == 0) || (cmdline.specificFrames)) { + encodeFrames(params.inputSourceP, + cmdline.childProcess, + cmdline.masterHostname, cmdline.masterPortNumber, + whichGOP, cmdline.specificFrames, + cmdline.frameStart, cmdline.frameEnd, + customQtable, customNIQtable, + ofP, outputFileName, + params.warnUnderflow, params.warnOverflow); + + } else + runMaster(params.inputSourceP, + cmdline.paramFileName, outputFileName); + } else if (cmdline.function == COMBINE_GOPS) + GOPsToMPEG(params.inputSourceP, outputFileName, ofP); + else if (cmdline.function == COMBINE_FRAMES) + FramesToMPEG(ofP, params.inputSourceP, + &getUserFrameFile, &nullDisposeFile); + } + Frame_Exit(); + + strfree(hostname); + + return 0; +} diff --git a/converter/ppm/ppmtompeg/psearch.c b/converter/ppm/ppmtompeg/psearch.c new file mode 100644 index 00000000..aea1a29b --- /dev/null +++ b/converter/ppm/ppmtompeg/psearch.c @@ -0,0 +1,989 @@ +/*===========================================================================* + * psearch.c + * + * Procedures concerned with the P-frame motion search + * + *===========================================================================*/ + +/*==============* + * HEADER FILES * + *==============*/ + +#include "all.h" +#include "mtypes.h" +#include "frames.h" +#include "motion_search.h" +#include "prototypes.h" +#include "fsize.h" +#include "param.h" + + +/*==================* + * STATIC VARIABLES * + *==================*/ + +/* none */ + + +/*==================* + * GLOBAL VARIABLES * + *==================*/ + +int **pmvHistogram = NULL; /* histogram of P-frame motion vectors */ +int **bbmvHistogram = NULL; /* histogram of B-frame bkwd motion vectors */ +int **bfmvHistogram = NULL; /* histogram of B-frame fwd motion vectors */ +int pixelFullSearch; +int searchRangeP,searchRangeB; +/* The range, in half pixels in each direction, that we are to search + when detecting motion. Specified by RANGE statement in parameter file. + */ +int psearchAlg; +/* specified by parameter file. */ + +/*===============================* + * INTERNAL PROCEDURE prototypes * + *===============================*/ + + +/*=====================* + * EXPORTED PROCEDURES * + *=====================*/ + +/*===========================================================================* + * + * Compute the best P-frame motion vector we can. If it's better than + * *motionP, update *motionP to it. + * + * PRECONDITIONS: The relevant block in 'current' is valid (it has not + * been dct'd). Thus, the data in 'current' can be + * accesed through y_blocks, cr_blocks, and cb_blocks. + * This is not the case for the blocks in 'prev.' + * Therefore, references into 'prev' should be done + * through the struct items ref_y, ref_cr, ref_cb + * + * POSTCONDITIONS: current, prev unchanged. + * Some computation could be saved by requiring + * the dct'd difference to be put into current's block + * elements here, depending on the search technique. + * However, it was decided that it mucks up the code + * organization a little, and the saving in computation + * would be relatively little (if any). + * + * NOTES: the search procedure need not check the (0,0) motion vector + * the calling procedure has a preference toward (0,0) and it + * will check it itself + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +void +PMotionSearch(const LumBlock * const currentBlockP, + MpegFrame * const prev, + int const by, + int const bx, + vector * const motionP) { + + /* CALL SEARCH PROCEDURE */ + + switch(psearchAlg) { + case PSEARCH_SUBSAMPLE: + PSubSampleSearch(currentBlockP, prev, by, bx, motionP, searchRangeP); + break; + case PSEARCH_EXHAUSTIVE: + PLocalSearch(currentBlockP, prev, by, bx, + motionP, INT_MAX, searchRangeP); + break; + case PSEARCH_LOGARITHMIC: + PLogarithmicSearch(currentBlockP, prev, by, bx, motionP, searchRangeP); + break; + case PSEARCH_TWOLEVEL: + PTwoLevelSearch(currentBlockP, prev, by, bx, + motionP, INT_MAX, searchRangeP); + break; + default: + pm_error("IMPOSSIBLE PSEARCH ALG: %d", psearchAlg); + } +} + + + +/*===========================================================================* + * + * SetPixelSearch + * + * set the pixel search type (half or full) + * + * RETURNS: nothing + * + * SIDE EFFECTS: pixelFullSearch + * + *===========================================================================*/ +void +SetPixelSearch(const char * const searchType) { + if ( (strcmp(searchType, "FULL") == 0 ) || + ( strcmp(searchType, "WHOLE") == 0 )) { + pixelFullSearch = TRUE; + } else if ( strcmp(searchType, "HALF") == 0 ) { + pixelFullSearch = FALSE; + } else { + fprintf(stderr, "ERROR: Invalid pixel search type: %s\n", + searchType); + exit(1); + } +} + + +/*===========================================================================* + * + * SetPSearchAlg + * + * set the P-search algorithm + * + * RETURNS: nothing + * + * SIDE EFFECTS: psearchAlg + * + *===========================================================================*/ +void +SetPSearchAlg(const char * const alg) +{ + if ( strcmp(alg, "EXHAUSTIVE") == 0 ) { + psearchAlg = PSEARCH_EXHAUSTIVE; + } else if (strcmp(alg, "SUBSAMPLE") == 0 ) { + psearchAlg = PSEARCH_SUBSAMPLE; + } else if ( strcmp(alg, "LOGARITHMIC") == 0 ) { + psearchAlg = PSEARCH_LOGARITHMIC; + } else if ( strcmp(alg, "TWOLEVEL") == 0 ) { + psearchAlg = PSEARCH_TWOLEVEL; + } else { + fprintf(stderr, "ERROR: Invalid psearch algorithm: %s\n", alg); + exit(1); + } +} + + +/*===========================================================================* + * + * PSearchName + * + * returns a string containing the name of the search algorithm + * + * RETURNS: pointer to the string + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +const char * +PSearchName(void) +{ + const char *retval; + + switch(psearchAlg) { + case PSEARCH_EXHAUSTIVE: + retval = "EXHAUSTIVE";break; + case PSEARCH_SUBSAMPLE: + retval = "SUBSAMPLE";break; + case PSEARCH_LOGARITHMIC: + retval = "LOGARITHMIC";break; + case PSEARCH_TWOLEVEL: + retval = "TWOLEVEL";break; + default: + fprintf(stderr, "ERROR: Illegal PSEARCH ALG: %d\n", psearchAlg); + exit(1); + break; + } + return retval; +} + + +/*===========================================================================* + * + * SetSearchRange + * + * sets the range of the search to the given number of pixels, + * allocate histogram storage + * + *===========================================================================*/ +void +SetSearchRange(int const pixelsP, int const pixelsB) { + + searchRangeP = 2*pixelsP; /* +/- 'pixels' pixels */ + searchRangeB = 2*pixelsB; + + if ( computeMVHist ) { + int const max_search = max(searchRangeP, searchRangeB); + + int index; + + pmvHistogram = (int **) malloc((2*searchRangeP+3)*sizeof(int *)); + bbmvHistogram = (int **) malloc((2*searchRangeB+3)*sizeof(int *)); + bfmvHistogram = (int **) malloc((2*searchRangeB+3)*sizeof(int *)); + for ( index = 0; index < 2*max_search+3; index++ ) { + pmvHistogram[index] = + (int *) calloc(2*searchRangeP+3, sizeof(int)); + bbmvHistogram[index] = + (int *) calloc(2*searchRangeB+3, sizeof(int)); + bfmvHistogram[index] = + (int *) calloc(2*searchRangeB+3, sizeof(int)); + } + } +} + + +/*===========================================================================* + * + * USER-MODIFIABLE + * + * MotionSearchPreComputation + * + * do whatever you want here; this is called once per frame, directly + * after reading + * + * RETURNS: whatever + * + * SIDE EFFECTS: whatever + * + *===========================================================================*/ +void +MotionSearchPreComputation(MpegFrame * const frameP) { + /* do nothing */ +} + + +/*===========================================================================* + * + * PSubSampleSearch + * + * uses the subsampling algorithm to compute the P-frame vector + * + * RETURNS: motion vector + * + * SIDE EFFECTS: none + * + * REFERENCE: Liu and Zaccarin: New Fast Algorithms for the Estimation + * of Block Motion Vectors, IEEE Transactions on Circuits + * and Systems for Video Technology, Vol. 3, No. 2, 1993. + * + *===========================================================================*/ +int +PSubSampleSearch(const LumBlock * const currentBlockP, + MpegFrame * const prev, + int const by, + int const bx, + vector * const motionP, + int const searchRange) { + + int my, mx; + int bestBestDiff; + int stepSize; + int x; + int bestMY[4], bestMX[4], bestDiff[4]; + int leftMY, leftMX; + int rightMY, rightMX; + + stepSize = (pixelFullSearch ? 2 : 1); + + COMPUTE_MOTION_BOUNDARY(by,bx,stepSize,leftMY,leftMX,rightMY,rightMX); + + if ( searchRange < rightMY ) { + rightMY = searchRange; + } + + if ( searchRange < rightMX ) { + rightMX = searchRange; + } + + for ( x = 0; x < 4; x++ ) { + bestMY[x] = 0; + bestMX[x] = 0; + bestDiff[x] = INT_MAX; + } + + /* do A pattern */ + for (my = -searchRange; my < rightMY; my += 2*stepSize) { + if (my >= leftMY) { + for ( mx = -searchRange; mx < rightMX; mx += 2*stepSize ) { + if (mx >= leftMX) { + int diff; + vector m; + m.y = my; m.x = mx; + diff = LumMotionErrorA(currentBlockP, prev, by, bx, m, + bestDiff[0]); + + if (diff < bestDiff[0]) { + bestMY[0] = my; + bestMX[0] = mx; + bestDiff[0] = diff; + } + } + } + } + } + + /* do B pattern */ + for (my = stepSize-searchRange; my < rightMY; my += 2*stepSize) { + if (my >= leftMY) { + for (mx = -searchRange; mx < rightMX; mx += 2*stepSize) { + if (mx >= leftMX) { + int diff; + vector m; + m.y = my; m.x = mx; + diff = LumMotionErrorB(currentBlockP, prev, by, bx, m, + bestDiff[1]); + + if (diff < bestDiff[1]) { + bestMY[1] = my; + bestMX[1] = mx; + bestDiff[1] = diff; + } + } + } + } + } + + /* do C pattern */ + for (my = stepSize-searchRange; my < rightMY; my += 2*stepSize) { + if (my >= leftMY) { + for ( mx = stepSize-searchRange; mx < rightMX; mx += 2*stepSize ) { + if (mx >= leftMX) { + int diff; + vector m; + m.y = my; m.x = mx; + diff = LumMotionErrorC(currentBlockP, prev, by, bx, m, + bestDiff[2]); + + if (diff < bestDiff[2]) { + bestMY[2] = my; + bestMX[2] = mx; + bestDiff[2] = diff; + } + } + } + } + } + + /* do D pattern */ + for (my = -searchRange; my < rightMY; my += 2*stepSize) { + if (my >= leftMY) { + for (mx = stepSize-searchRange; mx < rightMX; mx += 2*stepSize) { + if (mx >= leftMX) { + int diff; + vector m; + m.y = my; m.x = mx; + diff = LumMotionErrorD(currentBlockP, prev, by, bx, m, + bestDiff[3]); + + if (diff < bestDiff[3]) { + bestMY[3] = my; + bestMX[3] = mx; + bestDiff[3] = diff; + } + } + } + } + } + + /* first check old motion */ + if ((motionP->y >= leftMY) && (motionP->y < rightMY) && + (motionP->x >= leftMX) && (motionP->x < rightMX)) { + bestBestDiff = LumMotionError(currentBlockP, prev, by, bx, + *motionP, INT_MAX); + } else + bestBestDiff = INT_MAX; + + /* look at Error of 4 different motion vectors */ + for (x = 0; x < 4; ++x) { + vector m; + m.y = bestMY[x]; + m.x = bestMX[x]; + bestDiff[x] = LumMotionError(currentBlockP, prev, by, bx, m, + bestBestDiff); + + if (bestDiff[x] < bestBestDiff) { + bestBestDiff = bestDiff[x]; + *motionP = m; + } + } + return bestBestDiff; +} + + + +static void +findBestSpaced(int const minMY, + int const minMX, + int const maxMY, + int const maxMX, + int const spacing, + const LumBlock * const currentBlockP, + MpegFrame * const prev, + int const by, + int const bx, + int * const bestDiffP, + vector * const centerP) { +/*---------------------------------------------------------------------------- + Examine every 'spacing'th half-pixel within the rectangle + ('minBoundX', 'minBoundY', 'maxBoundX', 'maxBoundY'), + + If one of the half-pixels examined has a lower "LumMotionError" value + than *bestDiffP, update *bestDiffP to that value and update + *centerP to the location of that half-pixel. +-----------------------------------------------------------------------------*/ + int const minBoundY = MAX(minMY, centerP->y - spacing); + int const minBoundX = MAX(minMX, centerP->x - spacing); + int const maxBoundY = MIN(maxMY, centerP->y + spacing + 1); + int const maxBoundX = MIN(maxMX, centerP->x + spacing + 1); + + int my; + + for (my = minBoundY; my < maxBoundY; my += spacing) { + int mx; + + for (mx = minBoundX; mx < maxBoundX; mx += spacing) { + int diff; + vector m; + + m.y = my; m.x = mx; + + diff = LumMotionError(currentBlockP, prev, by, bx, m, *bestDiffP); + + if (diff < *bestDiffP) { + *centerP = m; + *bestDiffP = diff; + } + } + } +} + + + +/*===========================================================================* + * + * PLogarithmicSearch + * + * uses logarithmic search to compute the P-frame vector + * + * RETURNS: motion vector + * + * SIDE EFFECTS: none + * + * REFERENCE: MPEG-I specification, pages 32-33 + * + *===========================================================================*/ +int +PLogarithmicSearch(const LumBlock * const currentBlockP, + MpegFrame * const prev, + int const by, + int const bx, + vector * const motionP, + int const searchRange) { + + int const stepSize = (pixelFullSearch ? 2 : 1); + + int minMY, minMX, maxMY, maxMX; + int spacing; /* grid spacing */ + vector motion; + /* Distance from (bx,by) (in half-pixels) of the block that is most + like the current block among those that we have examined so far. + (0,0) means we haven't examined any. + */ + int bestDiff; + /* The difference between the current block and the block offset + 'motion' from it. + */ + + COMPUTE_MOTION_BOUNDARY(by, bx, stepSize, minMY, minMX, maxMY, maxMX); + minMX = max(minMX, - searchRange); + minMY = max(minMY, - searchRange); + maxMX = min(maxMX, + searchRange); + maxMY = min(maxMY, + searchRange); + + /* Note: The clipping to 'searchRange' above may seem superfluous because + the basic algorithm would never want to look more than 'searchRange' + pixels away, but with rounding error, it can. + */ + + motion.x = motion.y = 0; + bestDiff = INT_MAX; + + for (spacing = searchRange; spacing >= stepSize;) { + if (stepSize == 2) { /* make sure spacing is even */ + if (spacing == 2) + spacing = 0; + else { + spacing = (spacing+1)/2; + if (spacing % 2 != 0) + --spacing; + } + } else { + if (spacing == 1) { + spacing = 0; + } else + spacing = (spacing + 1) / 2; + } + if (spacing >= stepSize) + findBestSpaced(minMY, minMX, maxMY, maxMX, + spacing, currentBlockP, prev, by, bx, + &bestDiff, &motion); + } + + { + int diff; + /* check old motion -- see if it's better */ + if ((motionP->y >= minMY) && (motionP->y < maxMY) && + (motionP->x >= minMX) && (motionP->x < maxMX)) { + diff = LumMotionError(currentBlockP, prev, by, bx, + *motionP, bestDiff); + } else + diff = INT_MAX; + + if (bestDiff < diff) + *motionP = motion; + else + bestDiff = diff; + } + + return bestDiff; +} + + + +/*===========================================================================* + * + * PLocalSearch + * + * uses local exhaustive search to compute the P-frame vector + * + * RETURNS: motion vector + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +int +PLocalSearch(const LumBlock * const currentBlockP, + MpegFrame * const prev, + int const by, + int const bx, + vector * const motionP, + int const bestSoFar, + int const searchRange) { + + int mx, my; + int bestDiff; + int stepSize; + int leftMY, leftMX; + int rightMY, rightMX; + int distance; + int tempRightMY, tempRightMX; + + stepSize = (pixelFullSearch ? 2 : 1); + + COMPUTE_MOTION_BOUNDARY(by,bx,stepSize,leftMY,leftMX,rightMY,rightMX); + + /* try old motion vector first */ + if (VALID_MOTION(*motionP)) { + bestDiff = LumMotionError(currentBlockP, prev, by, bx, + *motionP, bestSoFar); + + if (bestSoFar < bestDiff) + bestDiff = bestSoFar; + } else { + motionP->y = motionP->x = 0; + bestDiff = bestSoFar; + } + + /* try a spiral pattern */ + for (distance = stepSize; distance <= searchRange; distance += stepSize) { + tempRightMY = MIN(distance, rightMY); + tempRightMX = MIN(distance, rightMX); + + /* do top, bottom */ + for (my = -distance; my < tempRightMY; + my += max(tempRightMY+distance-stepSize, stepSize)) { + if (my >= leftMY) { + for ( mx = -distance; mx < tempRightMX; mx += stepSize ) { + if (mx >= leftMX) { + int diff; + vector m; + + m.y = my; m.x = mx; + diff = LumMotionError(currentBlockP, prev, by, bx, m, + bestDiff); + + if (diff < bestDiff) { + *motionP = m; + bestDiff = diff; + } + } + } + } + } + + /* do left, right */ + for (mx = -distance; mx < tempRightMX; + mx += max(tempRightMX+distance-stepSize, stepSize)) { + + if (mx >= leftMX) { + for (my = -distance+stepSize; my < tempRightMY-stepSize; + my += stepSize) { + if (my >= leftMY) { + int diff; + vector m; + + m.y = my; m.x = mx; + diff = LumMotionError(currentBlockP, prev, by, bx, m, + bestDiff); + + if (diff < bestDiff) { + *motionP = m; + bestDiff = diff; + } + } + } + } + } + } + return bestDiff; +} + + +/*===========================================================================* + * + * PTwoLevelSearch + * + * uses two-level search to compute the P-frame vector + * first does exhaustive full-pixel search, then looks at neighboring + * half-pixel motion vectors + * + * RETURNS: motion vector + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +int +PTwoLevelSearch(const LumBlock * const currentBlockP, + MpegFrame * const prev, + int const by, + int const bx, + vector * const motionP, + int const bestSoFar, + int const searchRange) { + int mx, my; + int loopInc; + int diff, bestDiff; + int leftMY, leftMX; + int rightMY, rightMX; + int distance; + int tempRightMY, tempRightMX; + int xOffset, yOffset; + + /* exhaustive full-pixel search first */ + + COMPUTE_MOTION_BOUNDARY(by,bx,2,leftMY,leftMX,rightMY,rightMX); + + rightMY--; + rightMX--; + + /* convert vector into full-pixel vector */ + if (motionP->y > 0) { + if ((motionP->y % 2) == 1) { + --motionP->y; + } + } else if (((-motionP->y) % 2) == 1) + ++motionP->y; + + if (motionP->x > 0) { + if ((motionP->x % 2) == 1) + --motionP->x; + } else if ((-motionP->x % 2) == 1) + ++motionP->x; + + /* try old motion vector first */ + if (VALID_MOTION(*motionP)) { + bestDiff = LumMotionError(currentBlockP, prev, by, bx, + *motionP, bestSoFar); + + if ( bestSoFar < bestDiff ) { + bestDiff = bestSoFar; + } + } else { + motionP->y = motionP->x = 0; + bestDiff = bestSoFar; + } + + ++rightMY; + ++rightMX; + + /* try a spiral pattern */ + for ( distance = 2; distance <= searchRange; distance += 2 ) { + tempRightMY = MIN(distance, rightMY); + tempRightMX = MIN(distance, rightMX); + + /* do top, bottom */ + loopInc = max(tempRightMY + distance - 2, 2); + for (my = -distance; my < tempRightMY; my += loopInc) { + if (my >= leftMY) { + for (mx = -distance; mx < tempRightMX; mx += 2) { + if (mx >= leftMX) { + vector m; + m.y = my; m.x = mx; + diff = LumMotionError(currentBlockP, prev, by, bx, m, + bestDiff); + + if (diff < bestDiff) { + *motionP = m; + bestDiff = diff; + } + } + } + } + } + + /* do left, right */ + loopInc = max(tempRightMX+distance-2, 2); + for (mx = -distance; mx < tempRightMX; mx += loopInc) { + if (mx >= leftMX) { + for ( my = -distance+2; my < tempRightMY-2; my += 2 ) { + if (my >= leftMY) { + int diff; + vector m; + m.y = my; m.x = mx; + diff = LumMotionError(currentBlockP, prev, by, bx, m, + bestDiff); + + if ( diff < bestDiff ) { + *motionP = m; + bestDiff = diff; + } + } + } + } + } + } + + /* now look at neighboring half-pixels */ + my = motionP->y; + mx = motionP->x; + + --rightMY; + --rightMX; + + for (yOffset = -1; yOffset <= 1; ++yOffset) { + for (xOffset = -1; xOffset <= 1; ++xOffset) { + if ((yOffset != 0) || (xOffset != 0)) { + vector m; + m.y = my+yOffset; m.x = mx+xOffset; + if (VALID_MOTION(m)) { + int diff; + diff = LumMotionError(currentBlockP, prev, by, bx, + m, bestDiff); + if (diff < bestDiff) { + *motionP = m; + bestDiff = diff; + } + } + } + } + } + return bestDiff; +} + + + +void +ShowPMVHistogram(fpointer) + FILE *fpointer; +{ + register int x, y; + int *columnTotals; + int rowTotal; + + columnTotals = (int *) calloc(2*searchRangeP+3, sizeof(int)); + +#ifdef COMPLETE_DISPLAY + fprintf(fpointer, " "); + for ( y = 0; y < 2*searchRange+3; y++ ) { + fprintf(fpointer, "%3d ", y-searchRangeP-1); + } + fprintf(fpointer, "\n"); +#endif + + for ( x = 0; x < 2*searchRangeP+3; x++ ) { +#ifdef COMPLETE_DISPLAY + fprintf(fpointer, "%3d ", x-searchRangeP-1); +#endif + rowTotal = 0; + for ( y = 0; y < 2*searchRangeP+3; y++ ) { + fprintf(fpointer, "%3d ", pmvHistogram[x][y]); + rowTotal += pmvHistogram[x][y]; + columnTotals[y] += pmvHistogram[x][y]; + } +#ifdef COMPLETE_DISPLAY + fprintf(fpointer, "%4d\n", rowTotal); +#else + fprintf(fpointer, "\n"); +#endif + } + +#ifdef COMPLETE_DISPLAY + fprintf(fpointer, "Tot "); + for ( y = 0; y < 2*searchRangeP+3; y++ ) { + fprintf(fpointer, "%3d ", columnTotals[y]); + } +#endif + fprintf(fpointer, "\n"); +} + + +void +ShowBBMVHistogram(fpointer) + FILE *fpointer; +{ + register int x, y; + int *columnTotals; + int rowTotal; + + fprintf(fpointer, "B-frame Backwards:\n"); + + columnTotals = (int *) calloc(2*searchRangeB+3, sizeof(int)); + +#ifdef COMPLETE_DISPLAY + fprintf(fpointer, " "); + for ( y = 0; y < 2*searchRangeB+3; y++ ) { + fprintf(fpointer, "%3d ", y-searchRangeB-1); + } + fprintf(fpointer, "\n"); +#endif + + for ( x = 0; x < 2*searchRangeB+3; x++ ) { +#ifdef COMPLETE_DISPLAY + fprintf(fpointer, "%3d ", x-searchRangeB-1); +#endif + rowTotal = 0; + for ( y = 0; y < 2*searchRangeB+3; y++ ) { + fprintf(fpointer, "%3d ", bbmvHistogram[x][y]); + rowTotal += bbmvHistogram[x][y]; + columnTotals[y] += bbmvHistogram[x][y]; + } +#ifdef COMPLETE_DISPLAY + fprintf(fpointer, "%4d\n", rowTotal); +#else + fprintf(fpointer, "\n"); +#endif + } + +#ifdef COMPLETE_DISPLAY + fprintf(fpointer, "Tot "); + for ( y = 0; y < 2*searchRangeB+3; y++ ) { + fprintf(fpointer, "%3d ", columnTotals[y]); + } +#endif + fprintf(fpointer, "\n"); +} + + +void +ShowBFMVHistogram(fpointer) + FILE *fpointer; +{ + register int x, y; + int *columnTotals; + int rowTotal; + + fprintf(fpointer, "B-frame Forwards:\n"); + + columnTotals = (int *) calloc(2*searchRangeB+3, sizeof(int)); + +#ifdef COMPLETE_DISPLAY + fprintf(fpointer, " "); + for ( y = 0; y < 2*searchRangeB+3; y++ ) { + fprintf(fpointer, "%3d ", y-searchRangeB-1); + } + fprintf(fpointer, "\n"); +#endif + + for ( x = 0; x < 2*searchRangeB+3; x++ ) { +#ifdef COMPLETE_DISPLAY + fprintf(fpointer, "%3d ", x-searchRangeB-1); +#endif + rowTotal = 0; + for ( y = 0; y < 2*searchRangeB+3; y++ ) { + fprintf(fpointer, "%3d ", bfmvHistogram[x][y]); + rowTotal += bfmvHistogram[x][y]; + columnTotals[y] += bfmvHistogram[x][y]; + } +#ifdef COMPLETE_DISPLAY + fprintf(fpointer, "%4d\n", rowTotal); +#else + fprintf(fpointer, "\n"); +#endif + } + +#ifdef COMPLETE_DISPLAY + fprintf(fpointer, "Tot "); + for ( y = 0; y < 2*searchRangeB+3; y++ ) { + fprintf(fpointer, "%3d ", columnTotals[y]); + } +#endif + fprintf(fpointer, "\n"); +} + + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/* + * $Header: /u/smoot/md/mpeg_encode/RCS/psearch.c,v 1.9 1995/01/19 23:09:12 eyhung Exp $ + * $Log: psearch.c,v $ + * Revision 1.9 1995/01/19 23:09:12 eyhung + * Changed copyrights + * + * Revision 1.9 1995/01/19 23:09:12 eyhung + * Changed copyrights + * + * Revision 1.8 1994/12/07 00:40:36 smoot + * Added seperate P and B search ranges + * + * Revision 1.7 1994/11/12 02:09:45 eyhung + * full pixel bug + * fixed on lines 512 and 563 + * + * Revision 1.6 1994/03/15 00:27:11 keving + * nothing + * + * Revision 1.5 1993/12/22 19:19:01 keving + * nothing + * + * Revision 1.4 1993/07/22 22:23:43 keving + * nothing + * + * Revision 1.3 1993/06/30 20:06:09 keving + * nothing + * + * Revision 1.2 1993/06/03 21:08:08 keving + * nothing + * + * Revision 1.1 1993/03/02 18:27:05 keving + * nothing + * + */ + + diff --git a/converter/ppm/ppmtompeg/psocket.c b/converter/ppm/ppmtompeg/psocket.c new file mode 100644 index 00000000..707f1d84 --- /dev/null +++ b/converter/ppm/ppmtompeg/psocket.c @@ -0,0 +1,452 @@ +/*===========================================================================* + psocket.c +============================================================================== + + low level communication facilities for Ppmtompeg parallel operation + + By Bryan Henderson 2004.10.13. Contributed to the public domain by + its author. + +============================================================================*/ + +#define _XOPEN_SOURCE 500 /* Make sure stdio.h contains pclose() */ +/* _ALL_SOURCE is needed on AIX to make the C library include the + socket services (e.g. define struct sockaddr) + + Note that AIX standards.h actually sets feature declaration macros such + as _XOPEN_SOURCE, unless they are already set. +*/ +#define _ALL_SOURCE +#define __EXTENSIONS__ + /* __EXTENSIONS__ is for a broken Sun C library (uname SunOS kosh 5.8 + generic_108528-16 sun4u sparc). When you define _XOPEN_SOURCE, + it's vnode.h and resource.h fail to define some data types that they + need (e.g. timestruct_t). But with __EXTENSIONS__, they declare the + needed types anyway. Our #include <sys/socket.h> causes the broken + header files to get included. + */ + +/* On AIX, pm_config.h includes standards.h, which expects to be included + after feature declaration macros such as _XOPEN_SOURCE. So we include + pm_config.h as late as possible. +*/ + +#include "pm_config.h" /* For POSIX_IS_IMPLIED */ + +#ifdef POSIX_IS_IMPLIED +/* The OpenBSD C library, at least, is broken in that when _XOPEN_SOURCE + is defined, its sys/socket.h refers to types "u_char", etc. but does + not define them. But it is also one of the C libraries where + POSIX is implied so that we don't need to define _XOPEN_SOURCE in order + to get the POSIX routines such as pclose() defined. So we circumvent + the problem by undefining _XOPEN_SOURCE: +*/ +#undef _XOPEN_SOURCE +#endif + +#include <stdarg.h> +#include <netinet/in.h> +#include <unistd.h> +#include <netdb.h> +#include <stdio.h> +#include <errno.h> +#include <assert.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include "pm.h" +#include "pm_c_util.h" +#include "nstring.h" + +#include "gethostname.h" + +#include "psocket.h" + + +/* We use type socklenx_t where we should use socklen_t from the C + library, but older systems don't have socklen_t. And there doesn't + appear to be any way for the preprocessor to know whether it exists + or not. On older systems with no socklen_t, a message length is a + signed integer, but on modern systems, socklen_t is an unsigned + integer. Until we have some kind of build-time check for the existence + of socklen_t, we just use this socklenx_t, which is an unsigned + integer, and accept compiler warnings on older system. + -Bryan 2001.04.22. +*/ +typedef unsigned int socklenx_t; + +#ifndef SOMAXCONN +#define SOMAXCONN 5 +#endif + + + +static void PM_GNU_PRINTF_ATTR(1,2) +errorExit(const char format[], ...) { + + const char * const hostname = GetHostName(); + + va_list args; + + va_start(args, format); + + fprintf(stderr, "%s: FATAL ERROR. ", hostname); + strfree(hostname); + vfprintf(stderr, format, args); + fputc('\n', stderr); + + exit(1); + + va_end(args); +} + + + +static void +unmarshallInt(unsigned char const buffer[], + int * const valueP) { +/*---------------------------------------------------------------------------- + Interpret a number which is formatted for one of our network packets. + + To wit, 32 bit big-endian pure binary. +-----------------------------------------------------------------------------*/ + union { + uint32_t value; + unsigned char bytes[4]; + } converter; + + memcpy(&converter.bytes, buffer, 4); + + /* Note that contrary to what the C data types suggest, ntohl() is + a 32 bit converter, even if a C "long" is bigger than that. + */ + *valueP = ntohl(converter.value); +} + + + +static void +safeRead(int const fd, + unsigned char * const buf, + unsigned int const nbyte) { +/*---------------------------------------------------------------------------- + Safely read from file 'fd'. Keep reading until we get + 'nbyte' bytes. +-----------------------------------------------------------------------------*/ + unsigned int numRead; + + numRead = 0; /* initial value */ + + while (numRead < nbyte) { + int const result = read(fd, &buf[numRead], nbyte-numRead); + + if (result == -1) + errorExit("read (of %u bytes (total %u) ) returned " + "errno %d (%s)", + nbyte-numRead, nbyte, errno, strerror(errno)); + else + numRead += result; + } +} + + + +void +ReadBytes(int const fd, + unsigned char * const buf, + unsigned int const nbyte) { + + safeRead(fd, buf, nbyte); +} + + + +void +ReadInt(int const socketFd, + int * const valueP) { + + unsigned char buffer[4]; + + safeRead(socketFd, buffer, sizeof(buffer)); + + unmarshallInt(buffer, valueP); +} + + + +static void +marshallInt(int const value, + unsigned char (* const bufferP)[]) { +/*---------------------------------------------------------------------------- + Put the number 'value' into the buffer at *bufferP in the form required + for one of our network packets. + + To wit, 32 bit big-endian pure binary. +-----------------------------------------------------------------------------*/ + union { + uint32_t value; + unsigned char bytes[4]; + } converter; + + unsigned char testbuffer[4]; + + /* Note that contrary to what the C data types suggest, htonl() is + a 32 bit converter, even if a C "long" is bigger than that. + */ + converter.value = htonl(value); + + (*bufferP)[0] = 7; + memcpy(testbuffer, &converter.bytes, 4); + memcpy(*bufferP, &converter.bytes, 4); +} + + + +static void +safeWrite(int const fd, + unsigned char * const buf, + unsigned int const nbyte) { +/*---------------------------------------------------------------------------- + Safely write to file 'fd'. Keep writing until we write 'nbyte' + bytes. +-----------------------------------------------------------------------------*/ + unsigned int numWritten; + + numWritten = 0; /* initial value */ + + while (numWritten < nbyte) { + int const result = write(fd, &buf[numWritten], nbyte-numWritten); + + if (result == -1) + errorExit("write (of %u bytes (total %u) ) returned " + "errno %d (%s)", + nbyte-numWritten, nbyte, errno, strerror(errno)); + numWritten += result; + } +} + + + +void +WriteBytes(int const fd, + unsigned char * const buf, + unsigned int const nbyte) { + + safeWrite(fd, buf, nbyte); +} + + + +void +WriteInt(int const socketFd, + int const value) { + + unsigned char buffer[4]; + + marshallInt(value, &buffer); + + safeWrite(socketFd, buffer, sizeof(buffer)); +} + + + +void +ConnectToSocket(const char * const machineName, + int const portNum, + struct hostent ** const hostEnt, + int * const socketFdP, + const char ** const errorP) { +/*---------------------------------------------------------------------------- + Create a socket and connect it to the specified TCP endpoint. + + That endpoint is fundamentally defined by 'machineName' and + 'portNum', but *hostEnt is the address of a host entry that caches + the results of the host name lookup. If *hostEnt is non-null, we + use it. If *hostEnt is NULL, we look up the information and update + **hostEnt. +-----------------------------------------------------------------------------*/ + int rc; + + *errorP = NULL; /* initial value */ + + if ((*hostEnt) == NULL) { + (*hostEnt) = gethostbyname(machineName); + if ((*hostEnt) == NULL) + asprintfN(errorP, "Couldn't get host by name (%s)", machineName); + } + if (!*errorP) { + rc = socket(AF_INET, SOCK_STREAM, 0); + if (rc < 0) + asprintfN(errorP, "socket() failed with errno %d (%s)", + errno, strerror(errno)); + else { + int const socketFd = rc; + + int rc; + unsigned short tempShort; + struct sockaddr_in nameEntry; + + nameEntry.sin_family = AF_INET; + memset((void *) nameEntry.sin_zero, 0, 8); + memcpy((void *) &(nameEntry.sin_addr.s_addr), + (void *) (*hostEnt)->h_addr_list[0], + (size_t) (*hostEnt)->h_length); + tempShort = portNum; + nameEntry.sin_port = htons(tempShort); + + rc = connect(socketFd, (struct sockaddr *) &nameEntry, + sizeof(struct sockaddr)); + + if (rc != 0) + asprintfN(errorP, + "connect() to host '%s', port %d failed with " + "errno %d (%s)", + machineName, portNum, errno, strerror(errno)); + else { + *errorP = NULL; + *socketFdP = socketFd; + } + if (*errorP) + close(socketFd); + } + } +} + + + +static bool +portInUseErrno(int const testErrno) { +/*---------------------------------------------------------------------------- + Return TRUE iff 'testErrno' is what a bind() would return if one requestd + a port number that is unavailable (but other port numbers might be). +-----------------------------------------------------------------------------*/ + bool retval; + + switch (testErrno) { + case EINVAL: + case EADDRINUSE: + case EADDRNOTAVAIL: + retval = TRUE; + break; + default: + retval = FALSE; + } + return retval; +} + + + +static void +bindToUnusedPort(int const socketFd, + unsigned short * const portNumP, + const char ** const errorP) { + + bool foundPort; + unsigned short trialPortNum; + + *errorP = NULL; /* initial value */ + + for (foundPort = FALSE, trialPortNum = 2048; + !foundPort && trialPortNum < 16384 && !*errorP; + ++trialPortNum) { + + struct sockaddr_in nameEntry; + int rc; + + memset((char *) &nameEntry, 0, sizeof(nameEntry)); + nameEntry.sin_family = AF_INET; + nameEntry.sin_port = htons(trialPortNum); + + rc = bind(socketFd, (struct sockaddr *) &nameEntry, + sizeof(struct sockaddr)); + + if (rc == 0) { + foundPort = TRUE; + *portNumP = trialPortNum; + } else if (!portInUseErrno(errno)) + asprintfN(errorP, "bind() of TCP port number %hu failed " + "with errno %d (%s)", + trialPortNum, errno, strerror(errno)); + } + + if (!*errorP && !foundPort) + asprintfN(errorP, "Unable to find a free port. Every TCP port " + "in the range 2048-16383 is in use"); +} + + + +void +CreateListeningSocket(int * const socketP, + int * const portNumP, + const char ** const errorP) { +/*---------------------------------------------------------------------------- + Create a TCP socket and bind it to the first unused port number we + can find. + + Return as *socketP a file handle for the socket (on which Caller can + listen()), and as *portNumP the TCP port number (to which Caller's + partner can connect). +-----------------------------------------------------------------------------*/ + int rc; + + rc = socket(AF_INET, SOCK_STREAM, 0); + if (rc < 0) + asprintfN(errorP, + "Unable to create socket. " + "socket() failed with errno %d (%s)", + errno, strerror(errno)); + else { + int const socketFd = rc; + + unsigned short portNum; + + *socketP = socketFd; + + bindToUnusedPort(socketFd, &portNum, errorP); + if (!*errorP) { + int rc; + + *portNumP = portNum; + + /* would really like to wait for 1+numMachines machines, + but this is max allowable, unfortunately + */ + rc = listen(socketFd, SOMAXCONN); + if (rc != 0) + asprintfN(errorP, "Unable to listen on TCP socket. " + "listen() fails with errno %d (%s)", + errno, strerror(errno)); + } + if (*errorP) + close(socketFd); + } +} + + + +void +AcceptConnection(int const listenSocketFd, + int * const connectSocketFdP, + const char ** const errorP) { + + struct sockaddr otherSocket; + socklenx_t otherSize; + /* This is an ugly dual-meaning variable. As input to accept(), + it is the storage size of 'otherSocket'. As output, it is the + data length of 'otherSocket'. + */ + int rc; + + otherSize = sizeof(otherSocket); + + rc = accept(listenSocketFd, &otherSocket, &otherSize); + + if (rc < 0) + asprintfN(errorP, "accept() failed with errno %d (%s). ", + errno, strerror(errno)); + else { + *connectSocketFdP = rc; + *errorP = NULL; + } +} diff --git a/converter/ppm/ppmtompeg/qtest.c b/converter/ppm/ppmtompeg/qtest.c new file mode 100644 index 00000000..b3d26593 --- /dev/null +++ b/converter/ppm/ppmtompeg/qtest.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/* + * $Header: /n/charlie-brown/project/mm/mpeg/mpeg_dist/mpeg_encode/RCS/qtest.c,v 1.5 1995/01/19 23:09:15 eyhung Exp $ + * $Log: qtest.c,v $ + * Revision 1.5 1995/01/19 23:09:15 eyhung + * Changed copyrights + * + * Revision 1.4 1993/01/18 10:20:02 dwallach + * *** empty log message *** + * + * Revision 1.3 1993/01/18 10:17:29 dwallach + * RCS headers installed, code indented uniformly + * + * Revision 1.3 1993/01/18 10:17:29 dwallach + * RCS headers installed, code indented uniformly + * + */ + +#include <stdio.h> +#include "mtypes.h" +#include "mproto.h" + +main() +{ + Block a; + FlatBlock b; + BitBucket *bb; + int i, j; + + bb = new_bitbucket(); + + for (i = 0; i < 8; i++) + for (j = 0; j < 8; j++) + a[i][j] = rand() % 100; + mp_quant_zig_block(a, b, 1, 1); + for (i = 0; i < 64; i++) + printf("%6d ", b[i]); + printf("\n"); + + mp_rle_huff_block(b, bb); /* intuititve names, huh? */ + + printf("Huffman output is %d bits\n", bb->totalbits); +} diff --git a/converter/ppm/ppmtompeg/rate.c b/converter/ppm/ppmtompeg/rate.c new file mode 100644 index 00000000..3940956c --- /dev/null +++ b/converter/ppm/ppmtompeg/rate.c @@ -0,0 +1,986 @@ +/*============================================================================* + * rate.c * + * * + * Procedures concerned with rate control * + * * + * EXPORTED PROCEDURES: * + * initRatecontrol() * + * targetRateControl() * + * updateRateControl() * + * MB_RateOut() * + * needQScaleChange() * + * incNumBlocks() * + * incQuant() * + * incMacroBlockBits() * + * setPictureRate() * + * setBitRate() * + * getBitRate() * + * setBufferSize() * + * getBufferSize() * + * * + * NOTES: * + * Naming conventions follow those of MPEG-2 draft algorithm (chap. 10) * + *============================================================================*/ + + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/*==============* + * HEADER FILES * + *==============*/ + +#include <assert.h> +#include <sys/types.h> +#include <sys/times.h> + +#include "ppm.h" +#include "nstring.h" + +#include "all.h" +#include "mtypes.h" +#include "bitio.h" +#include "frames.h" +#include "prototypes.h" +#include "param.h" +#include "mheaders.h" +#include "fsize.h" +#include "postdct.h" +#include "mpeg.h" +#include "parallel.h" +#include "dct.h" +#include "rate.h" + + +/*==================* + * GLOBAL VARIABLES * + *==================*/ + +#define MAX_BIT_RATE 104857600 /* 18 digit number in units of 400 */ +#define MAX_BUFFER_SIZE 16760832 /* 10 digit number in units of 16k */ +#define DEFAULT_BUFFER_SIZE 327680 /* maximun for "constrained" bitstream */ +#define DEFAULT_VBV_FULLNESS 3 /* wait till 1/3 full */ +#define DEFAULT_PICT_RATE_CODE 5 /* code for 30 Frames/sec */ +#define DEFAULT_PICT_RATE 30 /* 30 frames per second */ +#define MAX_VBV_DELAY 32768 /* 16 digits */ + + +/* Variables from Parameter File */ + +static int RateControlMode = VARIABLE_RATE; +static int32 buffer_size = DEFAULT_BUFFER_SIZE; +static int32 bit_rate = -1; + +static bool wantVbvUnderflowWarning; +static bool wantVbvOverflowWarning; + +/* Variables for the VBV buffer defined in MPEG specs */ +static unsigned int VBV_remainingDelay; + /* delay in units of 1/90000 seconds */ +static int32 VBV_buffer = 0; /* fullness of the theoretical VBV buffer */ +static int32 bufferFillRate = 0; /* constant rate at which buffer filled */ +static int32 frameDelayIncrement = 0; /* number of "delay" units/Frame */ + +/* Global complexity measure variables */ +static int Xi, Xp, Xb; /* Global complexity measure */ + +static int Si, Sp, Sb; /* Total # bits for last pict of type (Overhead?) */ + +static float Qi, Qp, Qb; /* avg quantizaton for last picture of type */ + +/* Target bit allocations for each type of picture*/ +int Ti, Tp, Tb; + +int current_Tx; /* allocation for current frame */ + +/* Count of number of pictures of each type remaining */ +int GOP_X; +int GOP_I; +int GOP_P; +int GOP_B; + +int Nx = 0; +int Ni = 0; +int Np = 0; +int Nb = 0; + +/* Counters used while encoding frames */ + +int rc_numBlocks = 0; +int rc_totalQuant = 0; +int rc_bitsThisMB; +int rc_totalMBBits; +int rc_totalFrameBits; +int rc_totalOverheadBits = 0; + + +/* Want to print out Macroblock info every Nth MB */ +int RC_MB_SAMPLE_RATE = 0; + +static float Ki = .7; +static float Kp = 1; +static float Kb = 1.4; +static int rc_R; +static int rc_G; + +/* Rate Control variables */ + +/* Virtual buffers for each frame type */ +static int d0_i; /* Initial fullnesses */ +static int d0_p; +static int d0_b; + +static int lastFrameVirtBuf; /* fullness after last frame of this type */ +static int currentVirtBuf; /* fullness during current encoding*/ + +static int MB_cnt = -1; /* Number of MB's in picture */ + +static int rc_Q; /* reference quantization parameter */ + +static int reactionParameter; /* Reaction parameter */ + +/* Adaptive Quantization variables */ +static int act_j; /* spatial activity measure */ +static float N_act; /* Normalized spacial activity */ +static int avg_act; /* average activity value in last picture encoded */ +static int total_act_j; /* Sum of activity values in current frame */ + +static int var_sblk; /* sub-block activity */ +static int P_mean; /* Mean value of pixels in 8x8 sub-block */ + +static int mquant; /* Raw Quantization value */ +static int Qscale; /* Clipped, truncated quantization value */ + + + +/* Output-related variables */ +#ifdef RC_STATS_FILE +static FILE *RC_FILE; +#endif + +static const char * const Frame_header1 = +" Fm # Bit GOP V "; +static const char * const Frame_header2 = +" # type MBs Alloc left Ni Np Nb N_act buff Q_rc Qscale"; +static const char * const Frame_header3 = +"---- - ---- ------ ------- -- -- -- ----- ------ ---- ----"; +static const char * const Frame_trailer1 = +" avg virt % GOP % VBV"; +static const char * const Frame_trailer2 = +" Sx Qx Xx act N_act buffer alloc left left buf delay"; +static const char * const Frame_trailer3 = +"------ --.-- ------- --- --.-- ------- --- ------- --- ------- ------"; + +static const char * const MB_header1 = +"MB# #bits Q mqt Dj Q_j actj N_act totbits b/MB %alloc %done"; +static const char * const MB_header2 = +"--- ----- -- --- ------ --- ----- --.-- ------ ---- --- ---"; + +static char rc_buffer[101]; + +/* EXTERNAL Variables */ +extern char *framePattern; +extern int framePatternLen; + + +/*===============================* + * INTERNAL PROCEDURE prototypes * + *===============================*/ + +int initGOPRateControl _ANSI_ARGS_((void)); +int determineMBCount _ANSI_ARGS_((void)); +void checkBufferFullness _ANSI_ARGS_((int count)); +void checkSpatialActivity _ANSI_ARGS_((Block blk0, Block blk1, Block blk2, Block blk3)); +void incNumBlocks _ANSI_ARGS_((int num)); +void calculateVBVDelay _ANSI_ARGS_((int num)); +int BlockExperiments _ANSI_ARGS_((int16 *OrigBlock, int16 *NewBlock, int control)); + + + +static void +analyzePattern(const char * const framePattern, + int const framePatternLen, + int * const gop_xP, + int * const gop_iP, + int * const gop_pP, + int * const gop_bP, + const char ** const errorP) { + + unsigned int i; + + /* Initialize Pattern info */ + *gop_xP = framePatternLen; + + for (i = 0, *gop_iP = 0, *gop_pP = 0, *gop_bP = 0, *errorP = NULL; + i < framePatternLen && !*errorP; + ++i) { + switch(framePattern[i]) { + case 'i': ++*gop_iP; break; + case 'p': ++*gop_pP; break; + case 'b': ++*gop_bP; break; + default: + asprintfN(errorP, "Bad pattern - not composed of i, p, and b"); + } + } + assert(*gop_xP == *gop_iP + *gop_pP + *gop_bP); +} + + + +/*===========================================================================* + * + * initRateControl + * + * initialize the allocation parameters. + * + * RETURNS: nothing + * + * SIDE EFFECTS: many global variables + * + * NOTES: Get rid of the redundant pattern stuff!! + *===========================================================================*/ +int +initRateControl(bool const wantUnderflowWarning, + bool const wantOverflowWarning) { + int result; + const char * error; + + wantVbvUnderflowWarning = wantUnderflowWarning; + wantVbvOverflowWarning = wantOverflowWarning; + + DBG_PRINT(("Initializing Allocation Data\n")); + +#ifdef RC_STATS_FILE + RC_FILE = fopen("RC_STATS_FILE", "w"); + if ( RC_FILE == NULL) { + DBG_PRINT(("Open of RC file failed, using stderr\n")); + RC_FILE = stderr; + fprintf(RC_FILE, "Open of RC file failed, using stderr\n"); + fflush(RC_FILE); + } +#endif + + VBV_remainingDelay = 0; + + analyzePattern(framePattern, framePatternLen, + &GOP_X, &GOP_I, &GOP_P, &GOP_B, &error); + + if (error) { + pm_message("Unable to set up rate control. Switching to variable. " + "%s", error); + strfree(error); + RateControlMode = VARIABLE_RATE; + return -1; + } + + + /* Initializing GOP bit allocation */ + rc_R = 0; + rc_G = (bit_rate * GOP_X/frameRateRounded); + + /* Initialize the "global complexity measures" */ + Xi = (160 * bit_rate/115); + Xp = (60 * bit_rate/115); + Xb = (42 * bit_rate/115); + + /* Initialize MB counters */ + rc_totalMBBits= rc_bitsThisMB= rc_totalFrameBits=rc_totalOverheadBits = 0; + rc_numBlocks = rc_totalQuant = 0; + + /* init virtual buffers */ + reactionParameter = (2 * bit_rate / frameRateRounded); + d0_i = (10 * reactionParameter / 31); + d0_p = (Kp * d0_i); + d0_b = (Kb * d0_i); + + lastFrameVirtBuf = d0_i; /* start with I Frame */ + rc_Q = lastFrameVirtBuf * 31 / reactionParameter; + + /* init spatial activity measures */ + avg_act = 400; /* Suggested initial value */ + N_act = 1; + + mquant = rc_Q * N_act; + + frameDelayIncrement = (90000 / frameRateRounded); + /* num of "delay" units per frame */ + bufferFillRate = bit_rate / frameRateRounded; + /* VBV buf fills at constant rate */ + VBV_buffer = buffer_size; + DBG_PRINT(("VBV- delay: %d, fill rate: %d, delay/Frame: %d units, " + "buffer size: %d\n", + VBV_remainginDelay, bufferFillRate, frameDelayIncrement, + buffer_size)); + + result = initGOPRateControl(); + + return result; +} + +/*===========================================================================* + * + * initGOPRateControl + * + * (re)-initialize the RC for the a new Group of Pictures. + * New bit allocation, but carry over complexity measures. + * + * RETURNS: nothing + * + * SIDE EFFECTS: many global variables + * + *===========================================================================*/ +int +initGOPRateControl() +{ + DBG_PRINT(("Initializing new GOP\n")); + + Nx = GOP_X; + Ni = GOP_I; + Np = GOP_P; + Nb = GOP_B; + + rc_R += rc_G; + + DBG_PRINT(("bufsize: %d, bitrate: %d, pictrate: %d, GOP bits: %d\n", + buffer_size, bit_rate, frameRateRounded, rc_R)); + DBG_PRINT(("Xi: %d, Xp: %d, Xb: %d Nx: %d, Ni: %d, Np: %d, Nb: %d\n", + Xi, Xp, Xb, Nx,Ni,Np,Nb)); + DBG_PRINT(("d0_i: %d, d0_p: %d, d0_b: %d, avg_act: %d, rc_Q: %d, " + "mquant: %d\n", + d0_i, d0_p, d0_b, avg_act, rc_Q, mquant)); + return 1; +} + + + +/*===========================================================================* + * + * targetRateControl + * + * Determine the target allocation for given picture type, initiates + * variables for rate control process. + * + * RETURNS: nothing. + * + * SIDE EFFECTS: many global variables + * + *===========================================================================*/ +void +targetRateControl(MpegFrame * const frame) { + + float temp1, minimumBits; + float tempX, tempY, tempZ; + int result; + int frameType; + const char *strPtr; + + minimumBits = (bit_rate / (8 * frameRateRounded)); + + /* Check if new GOP */ + if (Nx == 0) { + initGOPRateControl(); + } + + if (MB_cnt < 0) {MB_cnt = determineMBCount();} + + switch (frame->type) { + case TYPE_IFRAME: + frameType = 'I'; + + tempX = ( (Np * Ki * Xp) / (Xi * Kp) ); + tempY = ( (Nb * Ki * Xb) / (Xi*Kb) ); + tempZ = Ni + tempX + tempY; + temp1 = (rc_R / tempZ); + result = (int) (temp1 > minimumBits ? temp1 : minimumBits); + current_Tx = Ti = result; + lastFrameVirtBuf = d0_i; + break; + + case TYPE_PFRAME: + frameType = 'P'; + tempX = ( (Ni * Kp * Xi) / (Ki * Xp) ); + tempY = ( (Nb * Kp * Xb) / (Kb * Xp) ); + tempZ = Np + tempX + tempY; + temp1 = (rc_R/ tempZ); + result = (int) (temp1 > minimumBits ? temp1 : minimumBits); + current_Tx = Tp = result; + lastFrameVirtBuf = d0_p; + break; + + case TYPE_BFRAME: + frameType = 'B'; + tempX = ( (Ni * Kb * Xi) / (Ki * Xb) ); + tempY = ( (Np * Kb * Xp) / (Kp * Xb) ); + tempZ = Nb + tempX + tempY; + temp1 = (rc_R/ tempZ); + result = (int) (temp1 > minimumBits ? temp1 : minimumBits); + current_Tx = Tb = result; + lastFrameVirtBuf = d0_b; + break; + + default: + frameType = 'X'; + } + + N_act = 1; + rc_Q = lastFrameVirtBuf * 31 / reactionParameter; + mquant = rc_Q * N_act; + Qscale = (mquant > 31 ? 31 : mquant); + Qscale = (Qscale < 1 ? 1 : Qscale); + + /* Print headers for Frame info */ + strPtr = Frame_header1; + DBG_PRINT(("%s\n",strPtr)); + strPtr = Frame_header2; + DBG_PRINT(("%s\n",strPtr)); + strPtr = Frame_header3; + DBG_PRINT(("%s\n",strPtr)); + + /* Print Frame info */ + sprintf(rc_buffer, "%4d %1c %4d %6d %7d " + "%2d %2d %2d %2.2f %6d %4d %3d", + frame->id,frameType,MB_cnt,current_Tx,rc_R,Ni,Np,Nb, + N_act, lastFrameVirtBuf, rc_Q, Qscale); + +#ifdef RC_STATS_FILE + fprintf(RC_FILE,"%s\n", rc_buffer); + fflush(RC_FILE); +#endif + DBG_PRINT(("%s\n",rc_buffer)); + + /* Print headers for Macroblock info */ + if (RC_MB_SAMPLE_RATE) { + strPtr = MB_header1; + DBG_PRINT(("%s\n",strPtr)); + strPtr = MB_header2; + DBG_PRINT(("%s\n",strPtr)); + } +} + + + + +static void +updateVBVBuffer(int const frameBits) { +/*---------------------------------------------------------------------------- + Update the VBV buffer after each frame. This theoretical buffer is + being filled at constant rate 'bufferFillRate' from an mpeg stream. + It is emptied as each frame is grabbed by the decoder. Exception is + that the decoder will wait until the "delay" is over. + + We mark the passing of one unit of time, in which 'frameBits' bits of + mpeg data were grabbed from the buffer by the decoder. +-----------------------------------------------------------------------------*/ + if (VBV_remainingDelay) + VBV_remainingDelay -= MIN(frameDelayIncrement, VBV_remainingDelay); + else + VBV_buffer -= frameBits; + + VBV_buffer += bufferFillRate; + if (VBV_buffer < 0 && wantVbvUnderflowWarning) + pm_message("WARNING - VBV buffer underflow (%d)", VBV_buffer); + if (VBV_buffer > buffer_size && wantVbvOverflowWarning) + pm_message("WARNING - VBV buffer overflow (%d > %d)", + VBV_buffer, buffer_size); +} + + + +/*===========================================================================* + * + * updateRateControl + * + * Update the statistics kept, after end of frame. Resets + * various global variables + * + * RETURNS: nothing + * + * SIDE EFFECTS: many global variables + * + *===========================================================================*/ +void +updateRateControl(int const type) { + int totalBits, frameComplexity, pctAllocUsed, pctGOPUsed; + float avgQuant; + const char *strPtr; + + totalBits = rc_totalFrameBits; + avgQuant = ((float) rc_totalQuant / (float) rc_numBlocks); + frameComplexity = totalBits * avgQuant; + pctAllocUsed = (totalBits *100 / current_Tx); + rc_R -= totalBits; + pctGOPUsed = (rc_R *100/ rc_G); + + avg_act = (total_act_j / MB_cnt); + + updateVBVBuffer(totalBits); + + switch (type) { + case TYPE_IFRAME: + Ti = current_Tx; + d0_i = currentVirtBuf; + Ni--; + Si = totalBits; + Qi = avgQuant; + Xi = frameComplexity; + break; + case TYPE_PFRAME: + Tp = current_Tx; + d0_p = currentVirtBuf; + Np--; + Sp = totalBits; + Qp = avgQuant; + Xp = frameComplexity; + break; + case TYPE_BFRAME: + Tb = current_Tx; + d0_b = currentVirtBuf; + Nb--; + Sb = totalBits; + Qb = avgQuant; + Xb = frameComplexity; + break; + } + + + /* Print Frame info */ + strPtr = Frame_trailer1; + DBG_PRINT(("%s\n",strPtr)); + strPtr = Frame_trailer2; + DBG_PRINT(("%s\n",strPtr)); + strPtr = Frame_trailer3; + DBG_PRINT(("%s\n",strPtr)); + + sprintf(rc_buffer, "%6d %2.2f %6d %3d %2.2f %7d " + "%3d %7d %3d %6d %6d", + totalBits, avgQuant, frameComplexity, avg_act, N_act, + currentVirtBuf, pctAllocUsed, rc_R, pctGOPUsed, + VBV_buffer, VBV_remainingDelay); +#ifdef RC_STATS_FILE + fprintf(RC_FILE,"%s\n", rc_buffer); + fflush(RC_FILE); +#endif + DBG_PRINT(("%s\n",rc_buffer)); + + Nx--; + rc_totalMBBits= rc_bitsThisMB= rc_totalFrameBits=rc_totalOverheadBits = 0; + rc_numBlocks = rc_totalQuant = total_act_j = currentVirtBuf = 0; + + DBG_PRINT(("GOP now has %d bits remaining (%3d%%) for %d frames .. , " + "Ni= %d, Np= %d, Nb= %d\n", + rc_R, (rc_R*100/rc_G), (Ni+Np+Nb), Ni, Np, Nb)); + +} + + +/*===========================================================================* + * + * MB_RateOut + * + * Prints out sampling of MB rate control data. Every "nth" block + * stats are printed, with "n" controled by global RC_MB_SAMPLE_RATE + * (NB. "skipped" blocks do not go through this function and thus do not + * show up in the sample ) + * + * RETURNS: nothing + * + * SIDE EFFECTS: none + * + * NOTES: + * + *===========================================================================*/ +void + MB_RateOut(type) +int type; +{ + int totalBits; + int pctUsed, pctDone; + int bitsThisMB; + int bitsPerMB; + + bitsThisMB = rc_bitsThisMB; + totalBits = rc_totalFrameBits; + bitsPerMB = (totalBits / rc_numBlocks); + pctDone = (rc_numBlocks * 100/ MB_cnt); + pctUsed = (totalBits *100/current_Tx); + + sprintf(rc_buffer, "%3d %5d %2d %3d %6d %3d %6d %2.2f %6d %4d %3d %3d\n", + (rc_numBlocks - 1), bitsThisMB, Qscale, mquant, currentVirtBuf, + rc_Q, act_j, N_act, totalBits, bitsPerMB, pctUsed, pctDone); +#ifdef RC_STATS_FILE + fprintf(RC_FILE, "%s", rc_buffer); + fflush(RC_FILE); +#endif + + if ( (RC_MB_SAMPLE_RATE) && ((rc_numBlocks -1) % RC_MB_SAMPLE_RATE)) { + DBG_PRINT(("%s\n", rc_buffer)); + } else { + return; + } +} + + + +/*===========================================================================* + * + * incNumBlocks() + * + * + * RETURNS: nothing + * + * SIDE EFFECTS: rc_numBlocks + * + * NOTES: + * + *===========================================================================*/ +void incNumBlocks(num) + int num; +{ + rc_numBlocks += num; +} + + +/*===========================================================================* + * + * incMacroBlockBits() + * + * Increments the number of Macro Block bits and the total of Frame + * bits by the number passed. + * + * RETURNS: nothing + * + * SIDE EFFECTS: rc_totalMBBits + * + * NOTES: + * + *===========================================================================*/ +void incMacroBlockBits(num) + int num; +{ + rc_bitsThisMB = num; + rc_totalMBBits += num; + rc_totalFrameBits += num; +} + + +/*===========================================================================* + * + * needQScaleChange(current Q scale, 4 luminance blocks) + * + * + * RETURNS: new Qscale + * + * SIDE EFFECTS: + * + *===========================================================================*/ +int needQScaleChange(oldQScale, blk0, blk1, blk2, blk3) + int oldQScale; + Block blk0; + Block blk1; + Block blk2; + Block blk3; +{ + + /* One more MacroBlock seen */ + rc_numBlocks++; /* this notes each block num in MB */ + + checkBufferFullness(oldQScale); + + checkSpatialActivity(blk0, blk1, blk2, blk3); + + mquant = rc_Q * N_act; + Qscale = (mquant > 31 ? 31 : mquant); + Qscale = (Qscale < 1 ? 1 : Qscale); + rc_totalQuant += Qscale; + + if (oldQScale == Qscale) + return -1; + else + return Qscale; +} + + +/*===========================================================================* + * + * determineMBCount() + * + * Determines number of Macro Blocks in frame from the frame sizes + * passed. + * + * RETURNS: nothing + * + * SIDE EFFECTS: sets the count passed + * + *===========================================================================*/ +int + determineMBCount () +{ + int y,x; + + x = (Fsize_x +15)/16; + y = (Fsize_y +15)/16; + return (x * y); +} + + + +/*===========================================================================* + * + * void checkBufferFullness () + * + * Calculates the fullness of the virtual buffer for each + * frame type. Called before encoding each macro block. Along + * with the normalized spatial activity measure (N_act), it + * determine the quantization factor for the next macroblock. + * + * RETURNS: nothing + * + * SIDE EFFECTS: the "currentVirtBuf" variable + * + * NOTES: + * + *===========================================================================*/ +void checkBufferFullness (oldQScale) + int oldQScale; +{ + int temp; + + temp = lastFrameVirtBuf + rc_totalFrameBits; + temp -= (current_Tx * rc_numBlocks / MB_cnt); + currentVirtBuf = temp; + + rc_Q = (currentVirtBuf * 31 / reactionParameter); + return; +} + + +/*===========================================================================* + * + * void checkSpatialActivity() + * + * Calcualtes the spatial activity for the four luminance blocks of the + * macroblock. Along with the normalized reference quantization parameter + * (rc_Q) , it determines the quantization factor for the next macroblock. + * + * RETURNS: nothing + * + * SIDE EFFECTS: the Adaptive quantization variables- act_j, N_act. + * + * NOTES: + * + *===========================================================================*/ +void checkSpatialActivity(blk0, blk1, blk2, blk3) + Block blk0; + Block blk1; + Block blk2; + Block blk3; +{ + int temp; + int16 *blkArray[4]; + int16 *curBlock; + int16 *blk_ptr; + int var[4]; + int i, j; + + + blkArray[0] = (int16 *) blk0; + blkArray[1] = (int16 *) blk1; + blkArray[2] = (int16 *) blk2; + blkArray[3] = (int16 *) blk3; + + + for (i =0; i < 4; i++) { /* Compute the activity in each block */ + curBlock = blkArray[i]; + blk_ptr = curBlock; + P_mean = 0; + /* Find the mean pixel value */ + for (j=0; j < DCTSIZE_SQ; j ++) { + P_mean += *(blk_ptr++); + /* P_mean += curBlock[j]; + if (curBlock[j] != *(blk_ptr++)) { + printf("ARRAY ERROR: block %d\n", j); + } + */ + } + P_mean /= DCTSIZE_SQ; + + /* Now find the variance */ + curBlock = blkArray[i]; + blk_ptr = curBlock; + var[i] = 0; + for (j=0; j < DCTSIZE_SQ; j++) { +#ifdef notdef + if (curBlock[j] != *(blk_ptr++)) { + printf("ARRAY ERROR: block %d\n", j); + } + temp = curBlock[j] - P_mean; +#endif + temp = *(blk_ptr++) - P_mean; + var[i] += (temp * temp); + } + var[i] /= DCTSIZE_SQ; + } + + /* Choose the minimum variance from the 4 blocks and use as the activity */ + var_sblk = var[0]; + for (i=1; i < 4; i++) { + var_sblk = (var_sblk < var[i] ? var_sblk : var[i]); + } + + + act_j = 1 + var_sblk; + total_act_j += act_j; + temp = (2 * act_j + avg_act); + N_act = ( (float) temp / (float) (act_j + 2*avg_act) ); + + return; +} + + + + +/*============================================================================* + * + * getRateMode () + * + * Returns the rate mode- interpreted as either Fixed or Variable + * + * RETURNS: integer + * + * SIDE EFFECTS: none + * + * + *==========================================================================*/ +int getRateMode() +{ + return RateControlMode; +} + + +/*===========================================================================* + * + * setBitRate () + * + * Checks the string parsed from the parameter file. Verifies + * number and sets global values. MPEG standard specifies that bit rate + * be rounded up to nearest 400 bits/sec. + * + * RETURNS: nothing + * + * SIDE EFFECTS: global variables + * + * NOTES: Should this be in the 400-bit units used in sequence header? + * + *===========================================================================*/ +void setBitRate (const char * const charPtr) +{ + int rate, rnd; + + rate = atoi(charPtr); + if (rate > 0) { + RateControlMode = FIXED_RATE; + } else { + printf("Parameter File Error: invalid BIT_RATE: \"%s\", defaults to Variable ratemode\n", + charPtr); + RateControlMode = VARIABLE_RATE; + bit_rate = -1; + } + rnd = (rate % 400); + rate += (rnd ? 400 -rnd : 0); /* round UP to nearest 400 bps */ + rate = (rate > MAX_BIT_RATE ? MAX_BIT_RATE : rate); + bit_rate = rate; + DBG_PRINT(("Bit rate is: %d\n", bit_rate)); +} + + + +/*===========================================================================* + * + * getBitRate () + * + * Returns the bit rate read from the parameter file. This is the + * real rate in bits per second, not in 400 bit units as is written to + * the sequence header. + * + * RETURNS: int (-1 if Variable mode operation) + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +int getBitRate () +{ + return bit_rate; +} + + + + +/*===========================================================================* + * + * setBufferSize () + * + * Checks the string parsed from the parameter file. Verifies + * number and sets global values. + * + * RETURNS: nothing + * + * SIDE EFFECTS: buffer_size global variable. + * + * NOTES: The global is in bits, NOT the 16kb units used in sequence header + * + *===========================================================================*/ +void setBufferSize (const char * const charPtr) +{ + int size; + + size = atoi(charPtr); + size = (size > MAX_BUFFER_SIZE ? MAX_BUFFER_SIZE : size); + if (size > 0) { + size = (16*1024) * ((size + (16*1024 - 1)) / (16*1024)); + buffer_size = size; + } else { + buffer_size = DEFAULT_BUFFER_SIZE; + printf("Parameter File Error: invalid BUFFER_SIZE: \"%s\", defaults to : %d\n", + charPtr, buffer_size); + } + DBG_PRINT(("Buffer size is: %d\n", buffer_size)); +} + + +/*===========================================================================* + * + * getBufferSize () + * + * returns the buffer size read from the parameter file. Size is + * in bits- not in units of 16k as written to the sequence header. + * + * RETURNS: int (or -1 if invalid) + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +int getBufferSize () +{ + return buffer_size; +} + + + diff --git a/converter/ppm/ppmtompeg/readframe.c b/converter/ppm/ppmtompeg/readframe.c new file mode 100644 index 00000000..d1423c1f --- /dev/null +++ b/converter/ppm/ppmtompeg/readframe.c @@ -0,0 +1,1016 @@ +/*===========================================================================* + * readframe.c + * + * procedures to read in frames + * + * EXPORTED PROCEDURES: + * ReadFrame + * SetFileType + * SetFileFormat + * + *===========================================================================*/ + +/* COPYRIGHT INFORMATION IS AT THE END OF THIS FILE */ + + +/*==============* + * HEADER FILES * + *==============*/ + +#define _BSD_SOURCE /* Make sure popen() is in stdio.h */ +#include "all.h" +#include <time.h> +#include <stdio.h> +#include <errno.h> +#include <ctype.h> +#include <string.h> +#include <unistd.h> +#include <assert.h> +#include "ppm.h" +#include "nstring.h" + +#include "mtypes.h" +#include "frames.h" +#include "prototypes.h" +#include "parallel.h" +#include "param.h" +#include "input.h" +#include "fsize.h" +#include "rgbtoycc.h" +#include "jpeg.h" +#include "opts.h" +#include "readframe.h" + + +/*==================* + * STATIC VARIABLES * + *==================*/ + +static int fileType = BASE_FILE_TYPE; +struct YuvLine { + uint8 data[3072]; + uint8 y[1024]; + int8 cr[1024]; + int8 cb[1024]; +}; + + +/*==================* + * Portability * + *==================*/ +#ifdef __OS2__ + #define popen _popen +#endif + + +/*==================* + * Global VARIABLES * + *==================*/ + +extern boolean GammaCorrection; +extern float GammaValue; +extern int outputWidth,outputHeight; +boolean resizeFrame; +const char *CurrFile; + +/*===============================* + * INTERNAL PROCEDURE prototypes * + *===============================*/ + +static void ReadEYUV _ANSI_ARGS_((MpegFrame * mf, FILE *fpointer, + int width, int height)); +static void ReadAYUV _ANSI_ARGS_((MpegFrame * mf, FILE *fpointer, + int width, int height)); +static void SeparateLine _ANSI_ARGS_((FILE *fpointer, struct YuvLine *lineptr, + int width)); +static void ReadY _ANSI_ARGS_((MpegFrame * mf, FILE *fpointer, + int width, int height)); +static void ReadSub4 _ANSI_ARGS_((MpegFrame * mf, FILE *fpointer, + int width, int height)); +static void DoGamma _ANSI_ARGS_((MpegFrame *mf, int width, int height)); + +static void DoKillDim _ANSI_ARGS_((MpegFrame *mf, int w, int h)); + +#define safe_fread(ptr,sz,len,fileptr) \ + if ((safe_read_count=fread(ptr,sz,len,fileptr))!=sz*len) { \ + fprintf(stderr,"Input file too small! (%s)\n",CurrFile); \ + exit(1);} \ + +/*=====================* + * EXPORTED PROCEDURES * + *=====================*/ + + + +void +SetResize(bool const set) { + resizeFrame = set; +} + + + +static void +ReadPNM(MpegFrame * const mpegFrameP, + FILE * const fp) { +/*---------------------------------------------------------------------------- + Read a PPM file containing one movie frame. +-----------------------------------------------------------------------------*/ + int pnmCols, pnmRows; + xelval maxval; + xel ** xels; + + xels = ppm_readppm(fp, &pnmCols, &pnmRows, &maxval); + ERRCHK(mpegFrameP, "ppm_readppm"); + + /* + * if this is the first frame read, set the global frame size + */ + Fsize_Note(mpegFrameP->id, pnmCols, pnmRows); + + PNMtoYUV(mpegFrameP, xels, Fsize_x, Fsize_y, maxval); + ppm_freearray(xels, pnmRows); +} + + + +static void +openFile(struct inputSource * const inputSourceP, + unsigned int const frameNumber, + const char * const conversion, + FILE ** const ifPP) { + + if (inputSourceP->stdinUsed) { + if (fileType == ANY_FILE_TYPE) + pm_error( + "ERROR : You cannot use a converter on frames when " + "you supply them as Standard Input. Either specify " + "INPUT_CONVERTER * in the parameter file or supply the " + "frames in files by specifying a directory with " + "INPUT_DIRECTORY in the parameter file."); + + *ifPP = stdin; + } else { + const char * fileName; + const char * fullFileName; + + GetNthInputFileName(inputSourceP, frameNumber, &fileName); + + asprintfN(&fullFileName, "%s/%s", currentPath, fileName); + + CurrFile = fullFileName; + + if (fileType == ANY_FILE_TYPE) { + char command[1024]; + const char * convertPtr; + char * commandPtr; + const char * charPtr; + + /* replace every occurrence of '*' with fullFileName */ + convertPtr = conversion; + commandPtr = command; + while (*convertPtr != '\0') { + while ((*convertPtr != '\0') && (*convertPtr != '*')) { + *commandPtr = *convertPtr; + ++commandPtr; + ++convertPtr; + } + + if (*convertPtr == '*') { + /* copy fullFileName */ + charPtr = fullFileName; + while (*charPtr != '\0') { + *commandPtr = *charPtr; + ++commandPtr; + ++charPtr; + } + ++convertPtr; /* go past '*' */ + } + } + *commandPtr = '\0'; + + *ifPP = popen(command, "r"); + if (*ifPP == NULL) { + pm_message( + "ERROR: Couldn't execute input conversion command " + "'%s'. errno=%d (%s)", + command, errno, strerror(errno)); + if (ioServer) + pm_error("IO SERVER: EXITING!!!"); + else + pm_error("SLAVE EXITING!!!"); + } + } else { + *ifPP = fopen(fullFileName, "rb"); + if (*ifPP == NULL) + pm_error("Couldn't open input file '%s'", fullFileName); + + if (baseFormat == JMOVIE_FILE_TYPE) + unlink(fullFileName); + } + strfree(fullFileName); + strfree(fileName); + } +} + + + +static void +closeFile(struct inputSource * const inputSourceP, + FILE * const ifP) { + + if (!inputSourceP->stdinUsed) { + if (fileType == ANY_FILE_TYPE) { + int rc; + rc = pclose(ifP); + if (rc != 0) + pm_message("WARNING: pclose() failed with errno %d (%s)", + errno, strerror(errno)); + } else + fclose(ifP); + } +} + + + +static bool +fileIsAtEnd(FILE * const ifP) { + + int c; + bool eof; + + c = getc(ifP); + if (c == EOF) { + if (feof(ifP)) + eof = TRUE; + else + pm_error("File error on getc() to position to image"); + } else { + int rc; + + eof = FALSE; + + rc = ungetc(c, ifP); + if (rc == EOF) + pm_error("File error doing ungetc() to position to image."); + } + return eof; +} + + + +void +ReadFrameFile(MpegFrame * const frameP, + FILE * const ifP, + const char * const conversion, + bool * const eofP) { +/*---------------------------------------------------------------------------- + Read a frame from the file 'ifP'. + + Return *eofP == TRUE iff we encounter EOF before we can get the + frame. +-----------------------------------------------------------------------------*/ + MpegFrame tempFrame; + MpegFrame * framePtr; + + /* To make this code fit Netpbm properly, we should remove handling + of all types except PNM and use pm_nextimage() to handle sensing + of end of stream. + */ + + if (fileIsAtEnd(ifP)) + *eofP = TRUE; + else { + *eofP = FALSE; + + if (resizeFrame) { + tempFrame.inUse = FALSE; + tempFrame.orig_y = NULL; + tempFrame.y_blocks = NULL; + tempFrame.decoded_y = NULL; + tempFrame.halfX = NULL; + framePtr = &tempFrame; + } else + framePtr = frameP; + + switch(baseFormat) { + case YUV_FILE_TYPE: + + /* Encoder YUV */ + if ((strncmp (yuvConversion, "EYUV", 4) == 0) || + (strncmp (yuvConversion, "UCB", 3) == 0)) + + ReadEYUV(framePtr, ifP, realWidth, realHeight); + + else + /* Abekas-type (interlaced) YUV */ + ReadAYUV(framePtr, ifP, realWidth, realHeight); + + break; + case Y_FILE_TYPE: + ReadY(framePtr, ifP, realWidth, realHeight); + break; + case PNM_FILE_TYPE: + ReadPNM(framePtr, ifP); + break; + case SUB4_FILE_TYPE: + ReadSub4(framePtr, ifP, yuvWidth, yuvHeight); + break; + case JPEG_FILE_TYPE: + case JMOVIE_FILE_TYPE: + ReadJPEG(framePtr, ifP); + break; + default: + break; + } + + if (resizeFrame) + Frame_Resize(frameP, &tempFrame, Fsize_x, Fsize_y, + outputWidth, outputHeight); + + if (GammaCorrection) + DoGamma(frameP, Fsize_x, Fsize_y); + + if (kill_dim) + DoKillDim(frameP, Fsize_x, Fsize_y); + + MotionSearchPreComputation(frameP); + } +} + + + +void +ReadFrame(MpegFrame * const frameP, + struct inputSource * const inputSourceP, + unsigned int const frameNumber, + const char * const conversion, + bool * const endOfStreamP) { +/*---------------------------------------------------------------------------- + Read the given frame, performing conversion as necessary. +-----------------------------------------------------------------------------*/ + FILE * ifP; + + openFile(inputSourceP, frameNumber, conversion, &ifP); + + ReadFrameFile(frameP, ifP, conversion, endOfStreamP); + + if (*endOfStreamP && !inputSourceP->stdinUsed) + pm_error("Premature EOF on file containing Frame %u", frameNumber); + + closeFile(inputSourceP, ifP); +} + + + +/*===========================================================================* + * + * SetFileType + * + * set the file type to be either a base type (no conversion), or + * any type (conversion required) + * + * RETURNS: nothing + * + * SIDE EFFECTS: fileType + * + *===========================================================================*/ +void +SetFileType(const char * const conversion) +{ + if ( strcmp(conversion, "*") == 0 ) { + fileType = BASE_FILE_TYPE; + } else { + fileType = ANY_FILE_TYPE; + } +} + + +/*===========================================================================* + * + * SetFileFormat + * + * set the file format (PNM, YUV, JPEG) + * + * RETURNS: nothing + * + * SIDE EFFECTS: baseFormat + * + *===========================================================================*/ +void +SetFileFormat(const char * const format) +{ + if ( strcmp(format, "PPM") == 0 ) { + baseFormat = PNM_FILE_TYPE; + } else if ( strcmp(format, "YUV") == 0 ) { + baseFormat = YUV_FILE_TYPE; + } else if ( strcmp(format, "Y") == 0 ) { + baseFormat = Y_FILE_TYPE; + } else if ( strcmp(format, "PNM") == 0 ) { + baseFormat = PNM_FILE_TYPE; + } else if (( strcmp(format, "JPEG") == 0 ) || ( strcmp(format, "JPG") == 0 )) { + baseFormat = JPEG_FILE_TYPE; + } else if ( strcmp(format, "JMOVIE") == 0 ) { + baseFormat = JMOVIE_FILE_TYPE; + } else if ( strcmp(format, "SUB4") == 0 ) { + baseFormat = SUB4_FILE_TYPE; + } else { + fprintf(stderr, "ERROR: Invalid file format: %s\n", format); + exit(1); + } +} + + + +FILE * +ReadIOConvert(struct inputSource * const inputSourceP, + unsigned int const frameNumber) { +/*---------------------------------------------------------------------------- + Do conversion; return handle to the appropriate file. +-----------------------------------------------------------------------------*/ + FILE *ifp; + char command[1024]; + const char * fullFileName; + char *convertPtr, *commandPtr; + const char * fileName; + + GetNthInputFileName(inputSourceP, frameNumber, &fileName); + + asprintfN(&fullFileName, "%s/%s", currentPath, fileName); + + if ( strcmp(ioConversion, "*") == 0 ) { + char buff[1024]; + ifp = fopen(fullFileName, "rb"); + sprintf(buff,"fopen \"%s\"",fullFileName); + ERRCHK(ifp, buff); + return ifp; + } + + /* replace every occurrence of '*' with fullFileName */ + convertPtr = ioConversion; + commandPtr = command; + while ( *convertPtr != '\0' ) { + while ( (*convertPtr != '\0') && (*convertPtr != '*') ) { + *commandPtr = *convertPtr; + commandPtr++; + convertPtr++; + } + + if ( *convertPtr == '*' ) { + /* copy fullFileName */ + const char * charPtr; + charPtr = fullFileName; + while ( *charPtr != '\0' ) { + *commandPtr = *charPtr; + commandPtr++; + charPtr++; + } + + convertPtr++; /* go past '*' */ + } + } + *commandPtr = '\0'; + + if ( (ifp = popen(command, "r")) == NULL ) { + fprintf(stderr, "ERROR: " + "Couldn't execute input conversion command:\n"); + fprintf(stderr, "\t%s\n", command); + fprintf(stderr, "errno = %d\n", errno); + if ( ioServer ) { + fprintf(stderr, "IO SERVER: EXITING!!!\n"); + } else { + fprintf(stderr, "SLAVE EXITING!!!\n"); + } + exit(1); + } + + strfree(fullFileName); + strfree(fileName); + + return ifp; +} + + + +/*===========================================================================* + * + * ReadEYUV + * + * read a Encoder-YUV file (concatenated Y, U, and V) + * + * RETURNS: mf modified + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +static void +ReadEYUV(mf, fpointer, width, height) + MpegFrame *mf; + FILE *fpointer; + int width; + int height; +{ + register int y; + uint8 junk[4096]; + int safe_read_count; + + Fsize_Note(mf->id, width, height); + + Frame_AllocYCC(mf); + + for (y = 0; y < Fsize_y; y++) { /* Y */ + safe_fread(mf->orig_y[y], 1, Fsize_x, fpointer); + + /* read the leftover stuff on the right side */ + if ( width != Fsize_x ) { + safe_fread(junk, 1, width-Fsize_x, fpointer); + } + } + + /* read the leftover stuff on the bottom */ + for (y = Fsize_y; y < height; y++) { + safe_fread(junk, 1, width, fpointer); + } + + for (y = 0; y < (Fsize_y >> 1); y++) { /* U */ + safe_fread(mf->orig_cb[y], 1, Fsize_x >> 1, fpointer); + + /* read the leftover stuff on the right side */ + if ( width != Fsize_x ) { + safe_fread(junk, 1, (width-Fsize_x)>>1, fpointer); + } + } + + /* read the leftover stuff on the bottom */ + for (y = (Fsize_y >> 1); y < (height >> 1); y++) { + safe_fread(junk, 1, width>>1, fpointer); + } + + for (y = 0; y < (Fsize_y >> 1); y++) { /* V */ + safe_fread(mf->orig_cr[y], 1, Fsize_x >> 1, fpointer); + + /* read the leftover stuff on the right side */ + if ( width != Fsize_x ) { + safe_fread(junk, 1, (width-Fsize_x)>>1, fpointer); + } + } + + /* ignore leftover stuff on the bottom */ +} + +/*===========================================================================* + * + * ReadAYUV + * + * read an Abekas-YUV file + * + * RETURNS: mf modified + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +static void +ReadAYUV(mf, fpointer, width, height) + MpegFrame *mf; + FILE *fpointer; + int width; + int height; +{ + register int x, y; + struct YuvLine line1, line2; + uint8 junk[4096]; + uint8 *cbptr, *crptr; + int safe_read_count; + + Fsize_Note(mf->id, width, height); + + Frame_AllocYCC(mf); + + for (y = 0; y < Fsize_y; y += 2) { + SeparateLine(fpointer, &line1, width); + SeparateLine(fpointer, &line2, width); + + /* Copy the Y values for each line to the frame */ + for (x = 0; x < Fsize_x; x++) { + mf->orig_y[y][x] = line1.y[x]; + mf->orig_y[y+1][x] = line2.y[x]; + } + + cbptr = &(mf->orig_cb[y>>1][0]); + crptr = &(mf->orig_cr[y>>1][0]); + + /* One U and one V for each two pixels horizontal as well */ + /* Toss the second line of Cr/Cb info, averaging was worse, + so just subsample */ + for (x = 0; x < (Fsize_x >> 1); x ++) { + cbptr[x] = line1.cb[x]; + crptr[x] = line1.cr[x]; + + } + } + + /* read the leftover stuff on the bottom */ + for (y = Fsize_y; y < height; y++) { + safe_fread(junk, 1, width<<1, fpointer); + } + +} + +/*===========================================================================* + * + * SeparateLine + * + * Separates one line of pixels into Y, U, and V components + * + * RETURNS: lineptr modified + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +static void +SeparateLine(fpointer, lineptr, width) + FILE *fpointer; + struct YuvLine *lineptr; + int width; +{ + uint8 junk[4096]; + int8 *crptr, *cbptr; + uint8 *yptr; + int num, length; + int safe_read_count; + + + /* Sets the deinterlacing pattern */ + + /* shorthand for UYVY */ + if (strncmp(yuvConversion, "ABEKAS", 6) == 0) { + strcpy(yuvConversion, "UYVY"); + + /* shorthand for YUYV */ + } else if (strncmp(yuvConversion, "PHILLIPS", 8) == 0) { + strcpy(yuvConversion, "YUYV"); + } + + length = strlen (yuvConversion); + + if ((length % 2) != 0) { + fprintf (stderr, "ERROR : YUV_FORMAT must represent two pixels, hence must be even in length.\n"); + exit(1); + } + + /* each line in 4:2:2 chroma format takes 2X bytes to represent X pixels. + * each line in 4:4:4 chroma format takes 3X bytes to represent X pixels. + * Therefore, half of the length of the YUV_FORMAT represents 1 pixel. + */ + safe_fread(lineptr->data, 1, Fsize_x*(length>>1), fpointer); + + /* read the leftover stuff on the right side */ + if ( width != Fsize_x ) { + safe_fread(junk, 1, (width-Fsize_x)*(length>>1), fpointer); + } + + crptr = &(lineptr->cr[0]); + cbptr = &(lineptr->cb[0]); + yptr = &(lineptr->y[0]); + + for (num = 0; num < (Fsize_x*(length>>1)); num++) { + switch (yuvConversion[num % length]) { + case 'U': + case 'u': + *(cbptr++) = (lineptr->data[num]); + break; + case 'V': + case 'v': + *(crptr++) = (lineptr->data[num]); + break; + case 'Y': + case 'y': + *(yptr++) = (lineptr->data[num]); + break; + default: + fprintf(stderr, "ERROR: YUV_FORMAT must be one of the following:\n"); + fprintf(stderr, " ABEKAS\n"); + fprintf(stderr, " EYUV\n"); + fprintf(stderr, " PHILLIPS\n"); + fprintf(stderr, " UCB\n"); + fprintf(stderr, " or any even-length string consisting of the letters U, V, and Y.\n"); + exit(1); + } + + } + +} + + +/*===========================================================================* + * + * ReadY + * + * read a Y file + * + * RETURNS: mf modified + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +static void +ReadY(mf, fpointer, width, height) + MpegFrame *mf; + FILE *fpointer; + int width; + int height; +{ + register int y; + uint8 junk[4096]; + int safe_read_count; + + Fsize_Note(mf->id, width, height); + + Frame_AllocYCC(mf); + + for (y = 0; y < Fsize_y; y++) { /* Y */ + safe_fread(mf->orig_y[y], 1, Fsize_x, fpointer); + + /* read the leftover stuff on the right side */ + if ( width != Fsize_x ) { + safe_fread(junk, 1, width-Fsize_x, fpointer); + } + } + + /* read the leftover stuff on the bottom */ + for (y = Fsize_y; y < height; y++) { + safe_fread(junk, 1, width, fpointer); + } + + for (y = 0 ; y < (Fsize_y >> 1); y++) { + memset(mf->orig_cb[y], 128, (Fsize_x>>1)); + memset(mf->orig_cr[y], 128, (Fsize_x>>1)); + } +} + + +/*===========================================================================* + * + * ReadSub4 + * + * read a YUV file (subsampled even further by 4:1 ratio) + * + * RETURNS: mf modified + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +static void +ReadSub4(mf, fpointer, width, height) + MpegFrame *mf; + FILE *fpointer; + int width; + int height; +{ + register int y; + register int x; + uint8 buffer[1024]; + int safe_read_count; + + Fsize_Note(mf->id, width, height); + + Frame_AllocYCC(mf); + + for (y = 0; y < (height>>1); y++) { /* Y */ + safe_fread(buffer, 1, width>>1, fpointer); + for ( x = 0; x < (width>>1); x++ ) { + mf->orig_y[2*y][2*x] = buffer[x]; + mf->orig_y[2*y][2*x+1] = buffer[x]; + mf->orig_y[2*y+1][2*x] = buffer[x]; + mf->orig_y[2*y+1][2*x+1] = buffer[x]; + } + } + + for (y = 0; y < (height >> 2); y++) { /* U */ + safe_fread(buffer, 1, width>>2, fpointer); + for ( x = 0; x < (width>>2); x++ ) { + mf->orig_cb[2*y][2*x] = buffer[x]; + mf->orig_cb[2*y][2*x+1] = buffer[x]; + mf->orig_cb[2*y+1][2*x] = buffer[x]; + mf->orig_cb[2*y+1][2*x+1] = buffer[x]; + } + } + + for (y = 0; y < (height >> 2); y++) { /* V */ + safe_fread(buffer, 1, width>>2, fpointer); + for ( x = 0; x < (width>>2); x++ ) { + mf->orig_cr[2*y][2*x] = buffer[x]; + mf->orig_cr[2*y][2*x+1] = buffer[x]; + mf->orig_cr[2*y+1][2*x] = buffer[x]; + mf->orig_cr[2*y+1][2*x+1] = buffer[x]; + } + } +} + + +/*=====================* + * INTERNAL PROCEDURES * + *=====================*/ + +/*===========================================================================* + * + * DoGamma + * + * Gamma Correct the Lum values + * + * RETURNS: nothing + * + * SIDE EFFECTS: Raises Y values to power gamma. + * + *===========================================================================*/ +static void +DoGamma(mf, w, h) +MpegFrame *mf; +int w,h; +{ + static int GammaVal[256]; + static boolean init_done=FALSE; + int i,j; + + if (!init_done) { + for(i=0; i<256; i++) + GammaVal[i]=(unsigned char) (pow(((double) i)/255.0,GammaValue)*255.0+0.5); + init_done=TRUE; + } + + for (i=0; i< h; i++) { /* For each line */ + for (j=0; j<w; j++) { /* For each Y value */ + mf->orig_y[i][j] = GammaVal[mf->orig_y[i][j]]; + }} +} + + + + +/*===========================================================================* + * + * DoKillDim + * + * Applies an input filter to small Y values. + * + * RETURNS: nothing + * + * SIDE EFFECTS: Changes Y values: + * + * Output | / + | / + | / + | ! + | / + | ! + | / + | - + | / + | -- + | / + | -- + | / + ------------------------ + ^ kill_dim_break + ^kill_dim_end + kill_dim_slope gives the slope (y = kill_dim_slope * x +0) + from 0 to kill_dim_break + * + *===========================================================================*/ + +static void +DoKillDim(mf, w, h) +MpegFrame *mf; +int w,h; +{ + static boolean init_done=FALSE; + static unsigned char mapper[256]; + register int i,j; + double slope, intercept; + + slope = (kill_dim_end - kill_dim_break*kill_dim_slope)*1.0 / + (kill_dim_end - kill_dim_break); + intercept = kill_dim_end * (1.0-slope); + + if (!init_done) { + for(i=0; i<256; i++) { + if (i >= kill_dim_end) { + mapper[i] = (char) i; + } else if (i >= kill_dim_break) { + mapper[i] = (char) (slope*i + intercept); + } else { /* i <= kill_dim_break */ + mapper[i] = (char) floor(i*kill_dim_slope + 0.49999); + } + } + init_done = TRUE; + } + + for (i=0; i < h; i++) { /* For each line */ + for (j=0; j < w; j++) { /* For each Y value */ + mf->orig_y[i][j] = mapper[mf->orig_y[i][j]]; + }} +} + + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/* + * $Header: /n/picasso/project/mpeg/mpeg_dist/mpeg_encode/RCS/readframe.c,v 1.27 1995/08/14 22:31:40 smoot Exp $ + * $Log: readframe.c,v $ + * Revision 1.27 1995/08/14 22:31:40 smoot + * reads training info from PPms now (needed for piping reads) + * + * Revision 1.26 1995/08/07 21:48:36 smoot + * better error reporting, JPG == JPEG now + * + * Revision 1.25 1995/06/12 20:30:12 smoot + * added popen for OS2 + * + * Revision 1.24 1995/06/08 20:34:36 smoot + * added "b"'s to fopen calls to make MSDOS happy + * + * Revision 1.23 1995/05/03 10:16:01 smoot + * minor compile bug with static f + * + * Revision 1.22 1995/05/02 22:00:12 smoot + * added TUNEing, setting near-black values to black + * + * Revision 1.21 1995/03/27 21:00:01 eyhung + * fixed bug with some long jpeg names + * + * Revision 1.20 1995/02/02 01:05:54 eyhung + * Fixed aAdded error checking for stdin + * + * Revision 1.19 1995/02/01 05:01:12 eyhung + * Removed troubleshooting printf + * + * Revision 1.18 1995/01/31 21:08:16 eyhung + * Improved YUV_FORMAT strings with better algorithm + * + * Revision 1.17 1995/01/27 23:34:09 eyhung + * Removed temporary JPEG files created by JMOVIE input + * + * Revision 1.16 1995/01/27 21:57:43 eyhung + * Added case for reading original JMOVIES + * + * Revision 1.14 1995/01/24 23:47:51 eyhung + * Confusion with Abekas format fixed : all other YUV revisions are wrong + * + * Revision 1.13 1995/01/20 00:02:30 smoot + * added gamma correction + * + * Revision 1.12 1995/01/19 23:09:21 eyhung + * Changed copyrights + * + * Revision 1.11 1995/01/17 22:23:07 aswan + * AbekasYUV chrominance implementation fixed + * + * Revision 1.10 1995/01/17 21:26:25 smoot + * Tore our average on Abekus/Phillips reconstruct + * + * Revision 1.9 1995/01/17 08:22:34 eyhung + * Debugging of ReadAYUV + * + * Revision 1.8 1995/01/16 13:18:24 eyhung + * Interlaced YUV format (e.g. Abekas) capability added (slightly buggy) + * + * Revision 1.7 1995/01/16 06:58:23 eyhung + * Added skeleton of ReadAYUV (for Abekas YUV files) + * + * Revision 1.6 1995/01/13 23:22:23 smoot + * Added ReadY, so we can make black&white movies (how artsy!) + * + * Revision 1.5 1994/12/16 00:20:40 smoot + * Now errors out on too small an input file + * + * Revision 1.4 1994/11/12 02:11:59 keving + * nothing + * + * Revision 1.3 1994/03/15 00:27:11 keving + * nothing + * + * Revision 1.2 1993/12/22 19:19:01 keving + * nothing + * + * Revision 1.1 1993/07/22 22:23:43 keving + * nothing + * + */ diff --git a/converter/ppm/ppmtompeg/rgbtoycc.c b/converter/ppm/ppmtompeg/rgbtoycc.c new file mode 100644 index 00000000..766b8902 --- /dev/null +++ b/converter/ppm/ppmtompeg/rgbtoycc.c @@ -0,0 +1,204 @@ +/*===========================================================================* + * rgbtoycc.c * + * * + * Procedures to convert from RGB space to YUV space * + * * + * EXPORTED PROCEDURES: * + * PNMtoYUV * + * PPMtoYUV * + * * + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/*==============* + * HEADER FILES * + *==============*/ + +#include "pnm.h" +#include "all.h" +#include "frame.h" +#include "rgbtoycc.h" + + +static float *mult299, *mult587, *mult114, *mult16874, *mult33126, + *mult5, *mult41869, *mult08131; /* malloc'ed */ + /* These are tables we use for fast arithmetic */ +static pixval table_maxval = 0; + /* The maxval used to compute the above arrays. Zero means + the above arrays don't exist yet + */ + +static void +compute_mult_tables(const pixval maxval) { + + /* For speed, we do the arithmetic with eight tables that reduce a + bunch of multiplications and divisions to a simple table lookup. + + Because a large maxval could require a significant amount of + table space, we allocate the space dynamically. + + If we had to compute the tables for every frame, it wouldn't be + fast at all, but since all the frames normally have the same + maxval, we only need to compute them once. But just in case, + we check each frame to see if it has a different maxval and + recompute the tables if so. + */ + + if (table_maxval != maxval) { + /* We need to compute or re-compute the multiplication tables */ + if (table_maxval != 0) { + free(mult299); free(mult587); free(mult114); free(mult16874); + free(mult33126); free(mult5); free(mult41869); free(mult08131); + } + table_maxval = maxval; + + mult299 = malloc((table_maxval+1)*sizeof(float)); + mult587 = malloc((table_maxval+1)*sizeof(float)); + mult114 = malloc((table_maxval+1)*sizeof(float)); + mult16874 = malloc((table_maxval+1)*sizeof(float)); + mult33126 = malloc((table_maxval+1)*sizeof(float)); + mult5 = malloc((table_maxval+1)*sizeof(float)); + mult41869 = malloc((table_maxval+1)*sizeof(float)); + mult08131 = malloc((table_maxval+1)*sizeof(float)); + + if (mult299 == NULL || mult587 == NULL || mult114 == NULL || + mult16874 == NULL || mult33126 == NULL || mult5 == NULL || + mult41869 == NULL || mult08131 == NULL) + pm_error("Unable to allocate storage for arithmetic tables.\n" + "We need %d bytes, which is the maxval of the input " + "image, plus 1,\n" + "times the storage size of a floating point value.", + 8 * (table_maxval+1)*sizeof(float)); + + { + int index; + + for (index = 0; index <= table_maxval; index++ ) { + mult299[index] = index*0.29900 * 255 / table_maxval; + mult587[index] = index*0.58700 * 255 / table_maxval; + mult114[index] = index*0.11400 * 255 / table_maxval; + mult16874[index] = -0.16874*index * 255 / table_maxval; + mult33126[index] = -0.33126*index * 255 / table_maxval; + mult5[index] = index*0.50000 * 255 / table_maxval; + mult41869[index] = -0.41869*index * 255 / table_maxval; + mult08131[index] = -0.08131*index * 255 / table_maxval; + } + } + } +} + + +/*=====================* + * EXPORTED PROCEDURES * + *=====================*/ + + +void +PNMtoYUV(MpegFrame * const frameP, + xel ** const xels, + unsigned int const cols, + unsigned int const rows, + xelval const maxval) { +/*---------------------------------------------------------------------------- + Set the raster of the MPEG frame *frameP from the libnetpbm input + 'xels'. Note that the raster information in a MpegFrame is in YUV + form. +-----------------------------------------------------------------------------*/ + int x, y; + uint8 *dy0, *dy1; + uint8 *dcr, *dcb; + pixel *src0, *src1; + + compute_mult_tables(maxval); /* This sets up mult299[], etc. */ + + Frame_AllocYCC(frameP); + + /* + * okay. Now, convert everything into YCbCr space. (the specific + * numbers come from the JPEG source, jccolor.c) The conversion + * equations to be implemented are therefore + * + * Y = 0.29900 * R + 0.58700 * G + 0.11400 * B + * Cb = -0.16874 * R - 0.33126 * G + 0.50000 * B + * Cr = 0.50000 * R - 0.41869 * G - 0.08131 * B + * + * With Y, Cb, and Cr then normalized to the range 0 - 255. + */ + + for (y = 0; y < rows; y += 2) { + for (x = 0, src0 = xels[y], src1 = xels[y + 1], + dy0 = frameP->orig_y[y], dy1 = frameP->orig_y[y + 1], + dcr = frameP->orig_cr[y >> 1], dcb = frameP->orig_cb[y >> 1]; + x < cols; + x += 2, dy0 += 2, dy1 += 2, dcr++, + dcb++, src0 += 2, src1 += 2) { + + *dy0 = (mult299[PPM_GETR(*src0)] + + mult587[PPM_GETG(*src0)] + + mult114[PPM_GETB(*src0)]); + + *dy1 = (mult299[PPM_GETR(*src1)] + + mult587[PPM_GETG(*src1)] + + mult114[PPM_GETB(*src1)]); + + dy0[1] = (mult299[PPM_GETR(src0[1])] + + mult587[PPM_GETG(src0[1])] + + mult114[PPM_GETB(src0[1])]); + + dy1[1] = (mult299[PPM_GETR(src1[1])] + + mult587[PPM_GETG(src1[1])] + + mult114[PPM_GETB(src1[1])]); + + *dcb = ((mult16874[PPM_GETR(*src0)] + + mult33126[PPM_GETG(*src0)] + + mult5[PPM_GETB(*src0)] + + mult16874[PPM_GETR(*src1)] + + mult33126[PPM_GETG(*src1)] + + mult5[PPM_GETB(*src1)] + + mult16874[PPM_GETR(src0[1])] + + mult33126[PPM_GETG(src0[1])] + + mult5[PPM_GETB(src0[1])] + + mult16874[PPM_GETR(src1[1])] + + mult33126[PPM_GETG(src1[1])] + + mult5[PPM_GETB(src1[1])]) / 4) + 128; + + *dcr = ((mult5[PPM_GETR(*src0)] + + mult41869[PPM_GETG(*src0)] + + mult08131[PPM_GETB(*src0)] + + mult5[PPM_GETR(*src1)] + + mult41869[PPM_GETG(*src1)] + + mult08131[PPM_GETB(*src1)] + + mult5[PPM_GETR(src0[1])] + + mult41869[PPM_GETG(src0[1])] + + mult08131[PPM_GETB(src0[1])] + + mult5[PPM_GETR(src1[1])] + + mult41869[PPM_GETG(src1[1])] + + mult08131[PPM_GETB(src1[1])]) / 4) + 128; + + DBG_PRINT(("%3d,%3d: (%3d,%3d,%3d) --> (%3d,%3d,%3d)\n", + x, y, + PPM_GETR(*src0), PPM_GETG(*src0), PPM_GETB(*src0), + *dy0, *dcb, *dcr)); + } + } +} diff --git a/converter/ppm/ppmtompeg/specifics.c b/converter/ppm/ppmtompeg/specifics.c new file mode 100644 index 00000000..c7bbfb5b --- /dev/null +++ b/converter/ppm/ppmtompeg/specifics.c @@ -0,0 +1,688 @@ +/*===========================================================================* + * specifics.c * + * * + * basic procedures to deal with the specifics file * + * * + * EXPORTED PROCEDURES: * + * Specifics_Init * + * Spec_Lookup * + * SpecTypeLookup * + * * + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + + +/*==============* + * HEADER FILES * + *==============*/ + +#include "all.h" +#include "mtypes.h" +#include "frames.h" +#include "frame.h" +#include "fsize.h" +#include "dct.h" +#include "specifics.h" +#include <stdio.h> +#include <string.h> +#include "prototypes.h" + +/*====================* + * System Information * + *====================*/ + +#define CPP_LOC "/lib/cpp" + +/*==================* + * GLOBAL VARIABLES * + *==================*/ + +extern boolean specificsOn; +extern char specificsFile[]; +extern char specificsDefines[]; +FrameSpecList *fsl; + +/*=====================* + * Internal procedures * + *=====================*/ + +void Parse_Specifics_File _ANSI_ARGS_((FILE *fp)); +void Parse_Specifics_File_v1 _ANSI_ARGS_((FILE *fp)); +void Parse_Specifics_File_v2 _ANSI_ARGS_((FILE *fp)); +FrameSpecList *MakeFslEntry _ANSI_ARGS_((void)); +void AddSlc _ANSI_ARGS_((FrameSpecList *c,int snum, int qs)); +Block_Specifics *AddBs _ANSI_ARGS_((FrameSpecList *c,int bnum, + boolean rel, int qs)); +FrameSpecList *MakeFslEntry _ANSI_ARGS_((void)); +#define my_upper(c) (((c>='a') && (c<='z')) ? (c-'a'+'A') : c) +#define CvtType(x) ReallyCvt(my_upper(x)) +#define ReallyCvt(x) (x=='I' ? 1 : (x=='P')?2: ((x=='B')?3:-1)) +#define SkipToSpace(lp) while ((*lp != ' ') && (*lp != '\n') && (*lp != '\0')) lp++ +#define EndString(lp) ((*lp == '\n') || (*lp == '\0')) + +/*============================================================= + * SPEC FILE FORMAT (version 1): + +Specs files are processed with the c preprecoessor, so use C style comments +and #defines if you wish. + +frames, blocks, and slices are numbered from 0. +(sorry) + +In order by frame number, slice number, block number +(if you skip slices it's fine). +Can have specifics for any frame, block, or slice. +Format: +version N + Specify the version of the specifics file format (this is 1) +frame N T M + Sets frame number N to type T and Qscale M + (type T is I,B,P,other, other means unspec. I recomend - ) +slice M Q + Sets slice M (in frame N as defined by a previous frame command) + to qscale Q +block M Q + Sets block M to qscale Q, in frame N previously specified. + +Unspecified frame types are set via the last I frame set, which is forced +to act as the first I of the GOP. +FORCE_ENCODE_LAST_FRAME overrides specifics on the final frame type. +Note that Qscale changes in skipped blocks will be lost! + +Version 2: +frames and slices are the same as above, but Q in blocks can be relative, i.e. ++N or -N. Clipping to 1..31 is done but sequences like +block 1 2 +block 2 -3 +block 3 +3 + +has undefined results (as present block 3 would be Qscale 2). + +In addition motion vectors can be specified: +block M Q skip + Says to skip the block (not really an MV, but....) +block M Q bi fx fy bx by + Sets block M to quality Q. It will be a bidirectional block. + fx/fy is the forward (like a P frame) vector, bx/y is the back +block M Q forw fx fy +block M Q back bx by + Single directional. + +All vectors are specified in HALF PIXEL fixed point units, i.e. +3.5 pixels is 7 +To specify a vector but not touch the q factor, set Q to 0 + +*=============================================================*/ + + +/*=============* + * Local State * + *=============*/ + +static char version = -1; + +/*================================================================ + * + * Specifics_Init + * + * Cpp's and reads in the specifics file. Creates fsl data structure. + * + * Returns: nothing + * + * Modifies: fsl, file specificsFile".cpp" + * + *================================================================ + */ +void Specifics_Init() +{ + char command[1100]; + FILE *specificsFP; + + sprintf(command, "/bin/rm -f %s.cpp", specificsFile); + system(command); + sprintf(command, "%s -P %s %s %s.cpp", + CPP_LOC, specificsDefines, specificsFile, specificsFile); + system(command); + strcat(specificsFile, ".cpp"); + if ((specificsFP = fopen(specificsFile, "r")) == NULL) { + fprintf(stderr, "Error with specifics file, cannot open %s\n", specificsFile); + exit(1); + } + printf("Specifics file: %s\n", specificsFile); + Parse_Specifics_File(specificsFP); + sprintf(command, "/bin/rm -f %s.cpp", specificsFile); + system(command); + +} + + + + +/*================================================================ + * + * Parse_Specifics_File + * + * Read through the file passed in creating the fsl data structure + * There is a primary routine, and helpers for the specific versions. + * + * Returns: Nothing + * + * Modifies: fsl + * + *================================================================ + */ +void Parse_Specifics_File(fp) +FILE *fp; +{ + char line[1024], *lp; + int vers; + + while ((fgets(line, 1023, fp)) != NULL) { + lp = &line[0]; + while ((*lp == ' ') || (*lp == '\t')) lp++; + if (( *lp == '#' ) || (*lp=='\n')) { + continue; + } + + switch (my_upper(*lp)) { + case 'F': case 'S': case 'B': + fprintf(stderr, "Must specify version at beginning of specifics file\n"); + exit(0); + break; + case 'V': + lp += 7; + if (1 != sscanf(lp, "%d", &vers)) { + fprintf(stderr," Improper version line in specs file: %s\n", line); + } else { + switch (vers) { + case 1: + version = vers; + Parse_Specifics_File_v1(fp); + break; + case 2: + version = vers; + Parse_Specifics_File_v2(fp); + break; + default: + fprintf(stderr, "Improper version line in specs file: %s\n", line); + fprintf(stderr, "\tSpecifics file will be IGNORED.\n"); + specificsOn = FALSE; + return; + break; + }} + break; + default: + fprintf(stderr, "Specifics file: What? *%s*\n", line); + break; + }} + +} + +/* Version 1 */ +void Parse_Specifics_File_v1(fp) +FILE *fp; +{ + char line[1024],*lp; + FrameSpecList *current, *new; + char typ; + int fnum,snum, bnum, qs, newqs; + int num_scanned; + + fsl = MakeFslEntry(); + current = fsl; + + while ((fgets(line,1023, fp)) != NULL) { + lp = &line[0]; + while ((*lp == ' ') || (*lp == '\t')) lp++; + if (( *lp == '#' ) || (*lp=='\n')) { + continue; + } + + switch (my_upper(*lp)) { + case 'F': + lp += 6; + sscanf(lp, "%d %c %d", &fnum, &typ, &qs); + if (current->framenum != -1) { + new=MakeFslEntry(); + current->next = new; + current = new; + } + current->framenum = fnum; + current->frametype = CvtType(typ); + if (qs <= 0) qs = -1; + current->qscale = qs; + break; + case 'S': + lp += 6; + sscanf(lp, "%d %d", &snum, &newqs); + if (qs == newqs) break; + qs = newqs; + AddSlc(current, snum, qs); + break; + case 'B': + lp += 6; + num_scanned = sscanf(lp, "%d %d", &bnum, &newqs); + if (qs == newqs) break; + qs = newqs; + AddBs(current, bnum, FALSE, qs); + break; + case 'V': + fprintf(stderr, "Cannot specify version twice! Taking first (%d)\n", version); + break; + default: + fprintf(stderr," What? *%s*\n", line); + break; + }} + +} + +/* Version 2 */ +void Parse_Specifics_File_v2(fp) +FILE *fp; +{ + char line[1024], *lp; + FrameSpecList *current, *new; + char typ; + int fnum, snum, bnum, qs, newqs; + int num_scanned, fx=0, fy=0, sx=0, sy=0; + char kind[100]; + Block_Specifics *new_blk; + boolean relative; + + fsl = MakeFslEntry(); + current = fsl; + + while ((fgets(line,1023,fp))!=NULL) { + lp = &line[0]; + while ((*lp == ' ') || (*lp == '\t')) lp++; + if (( *lp == '#' ) || (*lp=='\n')) { + continue; + } + + switch (my_upper(*lp)) { + case 'F': + lp += 6; + sscanf(lp,"%d %c %d", &fnum, &typ, &qs); + new = MakeFslEntry(); + if (current->framenum != -1) { + current->next = new; + current = new; + } + current->framenum = fnum; + current->frametype = CvtType(typ); + if (qs <= 0) qs = -1; + current->qscale = qs; + break; + case 'S': + lp += 6; + sscanf(lp,"%d %d", &snum, &newqs); + if (qs == newqs) break; + qs = newqs; + AddSlc(current, snum, qs); + break; + case 'B': + lp += 6; + num_scanned = 0; + bnum = atoi(lp); + SkipToSpace(lp); + while ((*lp != '-') && (*lp != '+') && + ((*lp < '0') || (*lp > '9'))) lp++; + relative = ((*lp == '-') || (*lp == '+')); + newqs = atoi(lp); + SkipToSpace(lp); + if (EndString(lp)) { + num_scanned = 2; + } else { + num_scanned = 2+sscanf(lp, "%s %d %d %d %d", kind, &fx, &fy, &sx, &sy); + } + + qs = newqs; + new_blk = AddBs(current, bnum, relative, qs); + if (num_scanned > 2) { + BlockMV *tmp; + tmp = (BlockMV *) malloc(sizeof(BlockMV)); + switch (num_scanned) { + case 7: + tmp->typ = TYP_BOTH; + tmp->fx = fx; + tmp->fy = fy; + tmp->bx = sx; + tmp->by = sy; + new_blk->mv = tmp; + break; + case 3: + tmp->typ = TYP_SKIP; + new_blk->mv = tmp; + break; + case 5: + if (my_upper(kind[0]) == 'B') { + tmp->typ = TYP_BACK; + tmp->bx = fx; + tmp->by = fy; + } else { + tmp->typ = TYP_FORW; + tmp->fx = fx; + tmp->fy = fy; + } + new_blk->mv = tmp; + break; + default: + fprintf(stderr, + "Bug in specifics file! Skipping short/long entry: %s\n",line); + break; + } + } else { + new_blk->mv = (BlockMV *) NULL; + } + + break; + case 'V': + fprintf(stderr, + "Cannot specify version twice! Taking first (%d).\n", + version); + break; + default: + printf("What? *%s*\n",line); + break; + }} + +} + + + + +/*================================================================= + * + * MakeFslEntry + * + * Makes a single entry in for the fsl linked list (makes a frame) + * + * Returns: the new entry + * + * Modifies: nothing + * + *================================================================= + */ +FrameSpecList *MakeFslEntry() +{ + FrameSpecList *fslp; + fslp = (FrameSpecList *) malloc(sizeof(FrameSpecList)); + fslp->framenum = -1; + fslp->slc = (Slice_Specifics *) NULL; + fslp->bs = (Block_Specifics *) NULL; + return fslp; +} + + + + + +/*================================================================ + * + * AddSlc + * + * Adds a slice to framespeclist c with values snum and qs + * + * Returns: nothing + * + * Modifies: fsl's structure + * + *================================================================ + */ +void AddSlc(c, snum, qs) +FrameSpecList *c; +int snum,qs; +{ + Slice_Specifics *new; + static Slice_Specifics *last; + + new = (Slice_Specifics *) malloc(sizeof(Slice_Specifics)); + new->num = snum; + new->qscale = qs; + new->next = (Slice_Specifics *)NULL; + if (c->slc == (Slice_Specifics *)NULL) { + last = new; + c->slc = new; + } else { + last->next = new; + last = new; + } +} + + + + + +/*================================================================ + * + * AddBs + * + * Adds a sliceblock to framespeclist c with values bnum and qs + * + * Returns: pointer to the new block spec + * + * Modifies: fsl's structure + * + *================================================================ + */ +Block_Specifics *AddBs(c,bnum,rel,qs) +FrameSpecList *c; +boolean rel; +int bnum,qs; +{ + Block_Specifics *new; + static Block_Specifics *last; + + new = (Block_Specifics *) malloc(sizeof(Block_Specifics)); + new->num = bnum; + if (qs == 0) rel = TRUE; + new->relative = rel; + new->qscale = qs; + new->next = (Block_Specifics *)NULL; + new->mv = (BlockMV *) NULL; + if (c->bs == (Block_Specifics *)NULL) { + last = new; + c->bs = new; + } else { + last->next = new; + last = new; + } + return new; +} + + + + + + +/*================================================================ + * + * SpecLookup + * + * Find out if there is any changes to be made for the qscale + * at entry fn.num (which is of type typ). Sets info to point to + * motion vector info (if any), else NULL. + * + * Returns: new qscale or -1 + * + * Modifies: *info (well, internal cache can change) + * + *================================================================ + */ + +int SpecLookup(fn,typ,num,info,start_qs) +int fn,typ,num; +BlockMV **info; +int start_qs; +{ + static FrameSpecList *last = (FrameSpecList *) NULL; + Slice_Specifics *sptr=(Slice_Specifics *) NULL; + Block_Specifics *bptr=(Block_Specifics *) NULL; + FrameSpecList *tmp; + boolean found_it; + static int leftovers = 0; /* Used in case of forced movement into 1..31 range */ + + *info = (BlockMV * )NULL; + if (last == (FrameSpecList *) NULL){ + /* No cache, try to find number fn */ + tmp = fsl; + found_it = FALSE; + while (tmp != (FrameSpecList *) NULL) { + if (tmp->framenum == fn) { + found_it = TRUE; + break; + } else tmp = tmp->next; + } + if (!found_it) return -1; + last=tmp; + } else { + if (last->framenum != fn) { /* cache miss! */ + /* first check if it is next */ + if ((last->next != (FrameSpecList *) NULL) && + (last->next->framenum == fn)) { + last = last->next; + } else { + /* if not next, check from the start. + (this allows people to put frames out of order,even + though the spec doesnt allow it.) */ + tmp = fsl; + found_it = FALSE; + while (tmp != (FrameSpecList *) NULL) { + if (tmp->framenum==fn) {found_it = TRUE; break;} + tmp = tmp->next; + } + if (!found_it) return -1; + last = tmp; + } + } + } + /* neither of these should ever be true, unless there is a bug above */ + if (last == (FrameSpecList *) NULL) { + fprintf(stderr, "PROGRAMMER ERROR: last is null!\n"); + return -1; + } + if (last->framenum!=fn) { + fprintf(stderr, "PROGRAMMER ERROR: last has wrong number!\n"); + return -1; /* no data on it */ + } + + switch(typ) { + case 0: /* Frame: num is ignored */ + leftovers = 0; +#ifdef BLEAH + printf("QSchange frame %d to %d\n", fn, last->qscale); +#endif + return last->qscale; + break; + + case 1: /* Slice */ + leftovers = 0; + /* So, any data on slices? */ + if (last->slc == (Slice_Specifics *) NULL) return -1; + for (sptr = last->slc; sptr != (Slice_Specifics *) NULL; sptr = sptr->next) { + if (sptr->num == num) { +#ifdef BLEAH + printf("QSchange Slice %d.%d to %d\n", fn, num, sptr->qscale); +#endif + if (sptr->qscale == 0) return -1; + return sptr->qscale; + } + } + break; + + case 2: /* block */ + /* So, any data on blocks? */ + if (last->bs == (Block_Specifics *) NULL) { + return -1; + } + for (bptr=last->bs; bptr != (Block_Specifics *) NULL; bptr=bptr->next) { + if (bptr->num == num) { + int new_one; +#ifdef BLEAH + printf("QSchange Block %d.%d to %d\n", fn, num, bptr->qscale); +#endif + *info = bptr->mv; + if (bptr->relative) { + if (bptr->qscale == 0) { + /* Do nothing! */ + new_one = start_qs; + } else { + new_one = start_qs + bptr->qscale + leftovers; + if (new_one < 1) { + leftovers = new_one - 1; + new_one = 1; + } else if (new_one > 31) { + leftovers = new_one - 31; + new_one = 31; + } else leftovers = 0; + }} + else { + new_one = bptr->qscale; + leftovers = 0; + } + return new_one; + } + } + break; + default: + fprintf(stderr, "PROGRAMMER ERROR: reached unreachable code in SpecLookup\n"); + break; + } + /* no luck */ + return -1; +} + + +/*================================================================ + * + * SpecTypeLookup + * + * Find out if there is any changes to be made for the type of frame + * at frame fn. + * + * Returns: new type or -1 (unspecified) + * + *================================================================ + */ +int SpecTypeLookup(fn) +int fn; +{ + FrameSpecList *tmp; + + /* try to find number fn */ + tmp = fsl; + do { + if (tmp->framenum == fn) break; + else tmp = tmp->next; + } while (tmp != (FrameSpecList *) NULL); + if (tmp == (FrameSpecList *) NULL) { +#ifdef BLEAH + printf("Frame %d type not specified\n", fn); +#endif + return -1; + } +#ifdef BLEAH + printf("Frame %d type set to %d\n", fn, tmp->frametype); +#endif + return tmp->frametype; +} diff --git a/converter/ppm/ppmtompeg/subsample.c b/converter/ppm/ppmtompeg/subsample.c new file mode 100644 index 00000000..5ec71814 --- /dev/null +++ b/converter/ppm/ppmtompeg/subsample.c @@ -0,0 +1,280 @@ +/*===========================================================================* + * subsample.c * + * * + * Procedures concerned with subsampling * + * * + * EXPORTED PROCEDURES: * + * LumMotionErrorA * + * LumMotionErrorB * + * LumMotionErrorC * + * LumMotionErrorD * + * * + *===========================================================================*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/*==============* + * HEADER FILES * + *==============*/ + +#include "pm_c_util.h" +#include "all.h" +#include "mtypes.h" +#include "frames.h" +#include "bitio.h" +#include "prototypes.h" + + + +static void +computePrevFyFx(MpegFrame * const prevFrame, + int const by, + int const bx, + vector const m, + uint8 *** const prevP, + int * const fyP, + int * const fxP) { + + boolean const xHalf = (ABS(m.x) % 2 == 1); + boolean const yHalf = (ABS(m.y) % 2 == 1); + + MotionToFrameCoord(by, bx, m.y/2, m.x/2, fyP, fxP); + + if (xHalf) { + if (m.x < 0) + --*fxP; + + if (yHalf) { + if (m.y < 0) + --*fyP; + + *prevP = prevFrame->halfBoth; + } else + *prevP = prevFrame->halfX; + } else if (yHalf) { + if (m.y < 0) + --*fyP; + + *prevP = prevFrame->halfY; + } else + *prevP = prevFrame->ref_y; +} + + + +static int32 +evenColDiff(const uint8 * const macross, + const int32 * const currentRow) { + + return 0 + + ABS(macross[ 0] - currentRow[ 0]) + + ABS(macross[ 2] - currentRow[ 2]) + + ABS(macross[ 4] - currentRow[ 4]) + + ABS(macross[ 6] - currentRow[ 6]) + + ABS(macross[ 8] - currentRow[ 8]) + + ABS(macross[10] - currentRow[10]) + + ABS(macross[12] - currentRow[12]) + + ABS(macross[14] - currentRow[14]); +} + + + +static int32 +oddColDiff(const uint8 * const macross, + const int32 * const currentRow) { + + return 0 + + ABS(macross[ 1] - currentRow[ 1]) + + ABS(macross[ 3] - currentRow[ 3]) + + ABS(macross[ 5] - currentRow[ 5]) + + ABS(macross[ 7] - currentRow[ 7]) + + ABS(macross[ 9] - currentRow[ 9]) + + ABS(macross[11] - currentRow[11]) + + ABS(macross[13] - currentRow[13]) + + ABS(macross[15] - currentRow[15]); +} + + + +/*===========================================================================* + * + * LumMotionErrorA + * + * compute the motion error for the A subsampling pattern + * + * RETURNS: the error, or some number greater if it is worse + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +int32 +LumMotionErrorA(const LumBlock * const currentBlockP, + MpegFrame * const prevFrame, + int const by, + int const bx, + vector const m, + int32 const bestSoFar) { + + int32 diff; /* max value of diff is 255*256 = 65280 */ + uint8 ** prev; + int fy, fx; + unsigned int rowNumber; + + computePrevFyFx(prevFrame, by, bx, m, &prev, &fy, &fx); + + diff = 0; /* initial value */ + + for (rowNumber = 0; rowNumber < 16; rowNumber +=2) { + uint8 * const macross = &(prev[fy + rowNumber][fx]); + const int32 * const currentRow = currentBlockP->l[rowNumber]; + + diff += evenColDiff(macross, currentRow); + + if (diff > bestSoFar) + return diff; + } + return diff; +} + + + +/*===========================================================================* + * + * LumMotionErrorB + * + * compute the motion error for the B subsampling pattern + * + * RETURNS: the error, or some number greater if it is worse + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +int32 +LumMotionErrorB(const LumBlock * const currentBlockP, + MpegFrame * const prevFrame, + int const by, + int const bx, + vector const m, + int32 const bestSoFar) { + + int32 diff; /* max value of diff is 255*256 = 65280 */ + uint8 **prev; + int fy, fx; + unsigned int rowNumber; + + computePrevFyFx(prevFrame, by, bx, m, &prev, &fy, &fx); + + diff = 0; /* initial value */ + + for (rowNumber = 0; rowNumber < 16; rowNumber +=2) { + uint8 * const macross = &(prev[fy + rowNumber][fx]); + const int32 * const currentRow = currentBlockP->l[rowNumber]; + + diff += oddColDiff(macross, currentRow); + + if (diff > bestSoFar) + return diff; + } + return diff; +} + + +/*===========================================================================* + * + * LumMotionErrorC + * + * compute the motion error for the C subsampling pattern + * + * RETURNS: the error, or some number greater if it is worse + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +int32 +LumMotionErrorC(const LumBlock * const currentBlockP, + MpegFrame * const prevFrame, + int const by, + int const bx, + vector const m, + int32 const bestSoFar) { + + int32 diff; /* max value of diff is 255*256 = 65280 */ + uint8 **prev; + int fy, fx; + unsigned int rowNumber; + + computePrevFyFx(prevFrame, by, bx, m, &prev, &fy, &fx); + + diff = 0; /* initial value */ + + for (rowNumber = 1; rowNumber < 16; rowNumber +=2) { + uint8 * const macross = &(prev[fy + rowNumber][fx]); + const int32 * const currentRow = currentBlockP->l[rowNumber]; + + diff += evenColDiff(macross, currentRow); + + if (diff > bestSoFar) + return diff; + } + return diff; +} + + +/*===========================================================================* + * + * LumMotionErrorD + * + * compute the motion error for the D subsampling pattern + * + * RETURNS: the error, or some number greater if it is worse + * + * SIDE EFFECTS: none + * + *===========================================================================*/ +int32 +LumMotionErrorD(const LumBlock * const currentBlockP, + MpegFrame * const prevFrame, + int const by, + int const bx, + vector const m, + int32 const bestSoFar) { + + int32 diff; /* max value of diff is 255*256 = 65280 */ + uint8 ** prev; + int fy, fx; + unsigned int rowNumber; + + computePrevFyFx(prevFrame, by, bx, m, &prev, &fy, &fx); + + diff = 0; /* initial value */ + + for (rowNumber = 1; rowNumber < 16; rowNumber +=2) { + uint8 * const macross = &(prev[fy + rowNumber][fx]); + const int32 * const currentRow = currentBlockP->l[rowNumber]; + + diff += oddColDiff(macross, currentRow); + + if (diff > bestSoFar) + return diff; + } + return diff; +} diff --git a/converter/ppm/ppmtompeg/tst/README b/converter/ppm/ppmtompeg/tst/README new file mode 100644 index 00000000..4e35f6ba --- /dev/null +++ b/converter/ppm/ppmtompeg/tst/README @@ -0,0 +1,30 @@ +The test in this directory is derived from that in the Berkeley mpeg +package, but we have trimmed it down for the purposes of the Netpbm +package to save space. + +First of all, the Berkeley package had Berkeley YUV files for sample input +(in the 'ts' directory). But we converted them to JPEG to save a great +deal of space (even when the package is gzipped). + +We modified the ts.param file to work with the JPEG files and updated the +expected results file to correspond to the JPEG input (the expected results +are different after the JPEG conversion because JPEG conversion is lossy). + +We kept some of the other parameter files from the Berkeley package in +case someone can get some use out of them, but did not update them to +use the JPEG files. You'll have to do that yourself. And we removed +the expected results files for them, since you can't generate those +results with the inputs we have supplied. + +Note that JPEG input doesn't really exercise the most standard function +of ppmtompeg. As its name suggests, it's main purpose is to take PPM +frames as input. You can use jpegtoppm to create PPM input and then +modify the parameter files if you want to test that. We have not, however +supplied an expected results file for that. + +The .mpg files are what ppmtojpeg output as the mpeg movie when we +ran it on the jpeg files in the 'ts' directory. + +The .stat files are what ppmtojpeg output when we used the -stat +option in generating the expected results. As part of your test, use +the -stat option and compare yours to the expected results. diff --git a/converter/ppm/ppmtompeg/tst/gop.param b/converter/ppm/ppmtompeg/tst/gop.param new file mode 100644 index 00000000..96900a3d --- /dev/null +++ b/converter/ppm/ppmtompeg/tst/gop.param @@ -0,0 +1,30 @@ +# test suite parameter file + +PATTERN IBPBIBPBPB +OUTPUT /tmp/ts.mpg + +YUV_SIZE 352x240 + +BASE_FILE_FORMAT YUV +INPUT_CONVERT * +GOP_SIZE 10 +SLICES_PER_FRAME 1 + +INPUT_DIR ./tst/ts + +INPUT +stennis.*.yuv [30-39] +stennis.*.yuv [30-39] +END_INPUT + +PIXEL HALF +RANGE 8 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG SIMPLE + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +REFERENCE_FRAME ORIGINAL diff --git a/converter/ppm/ppmtompeg/tst/par3.param b/converter/ppm/ppmtompeg/tst/par3.param new file mode 100644 index 00000000..6ee6586e --- /dev/null +++ b/converter/ppm/ppmtompeg/tst/par3.param @@ -0,0 +1,43 @@ +# speed test parameter file + +PATTERN IBBPBBPBBPBBPBB + +OUTPUT /n/picasso/users/keving/encode/output/flowgard.mpg +GOP_SIZE 30 +SLICES_PER_FRAME 15 + +BASE_FILE_FORMAT YUV +YUV_SIZE 352x240 + +INPUT_CONVERT * + +INPUT_DIR /n/picasso/users/keving/encode/input/flowg + +INPUT +sflowg.*.yuv [0-39] +END_INPUT + +# quality parameters + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +# motion vector search parameters + +PIXEL HALF + +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +REFERENCE_FRAME ORIGINAL + +PARALLEL_TEST_FRAMES 3 +PARALLEL_TIME_CHUNKS 30 + +PARALLEL +bugs-bunny keving ~keving/encode/bin/sun/mpeg_encode +linus keving ~keving/encode/bin/sun/mpeg_encode +END_PARALLEL diff --git a/converter/ppm/ppmtompeg/tst/short.param b/converter/ppm/ppmtompeg/tst/short.param new file mode 100644 index 00000000..9189e5d6 --- /dev/null +++ b/converter/ppm/ppmtompeg/tst/short.param @@ -0,0 +1,31 @@ +# test suite parameter file + +PATTERN IBBBPBBB +OUTPUT /tmp/ts.mpg + +YUV_SIZE 352x240 + +BASE_FILE_FORMAT YUV +INPUT_CONVERT * +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +INPUT_DIR ../test/ts + +INPUT +stennis.*.yuv [30-37] +END_INPUT + +PIXEL HALF +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +REFERENCE_FRAME ORIGINAL + +# FORCE_ENCODE_LAST_FRAME diff --git a/converter/ppm/ppmtompeg/tst/test_all b/converter/ppm/ppmtompeg/tst/test_all new file mode 100755 index 00000000..67b56c9c --- /dev/null +++ b/converter/ppm/ppmtompeg/tst/test_all @@ -0,0 +1,19 @@ +#!/bin/csh -f +cd .. +echo "First we encode three mpegs... (note requires 5MB on /tmp)" +rm -f /tmp/ts{,2,d}.{mpg,stat} +./mpeg_encode -stat /tmp/ts.stat ./tst/ts.param +./mpeg_encode -stat /tmp/ts2.stat ./tst/ts2.param +./mpeg_encode -stat /tmp/tsd.stat ./tst/tsd.param + +cd tst + +echo "Test one - tst/ts.param" +csh diffscript /tmp/ts.stat ts.stat /tmp/ts.mpg ts.mpg + +echo "Test two - tst/ts2.param (different pattern)" +csh diffscript /tmp/ts2.stat ts2.stat /tmp/ts2.mpg ts2.mpg + +echo "Test three - tst/tsd.param (uses decoded frames)" +csh diffscript /tmp/tsd.stat tsd.stat /tmp/tsd.mpg tsd.mpg + diff --git a/converter/ppm/ppmtompeg/tst/ts.mpg b/converter/ppm/ppmtompeg/tst/ts.mpg new file mode 100644 index 00000000..2bc6ae6e --- /dev/null +++ b/converter/ppm/ppmtompeg/tst/ts.mpg Binary files differdiff --git a/converter/ppm/ppmtompeg/tst/ts.param b/converter/ppm/ppmtompeg/tst/ts.param new file mode 100644 index 00000000..23e1092c --- /dev/null +++ b/converter/ppm/ppmtompeg/tst/ts.param @@ -0,0 +1,29 @@ +# test suite parameter file + +PATTERN IBBPBBPBBP +OUTPUT /tmp/ts.mpg + +BASE_FILE_FORMAT JPEG +INPUT_CONVERT * +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +INPUT_DIR ./tst/ts + +INPUT +stennis.*.jpg [30-39] +END_INPUT + +PIXEL HALF +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +REFERENCE_FRAME ORIGINAL +# Default string has the date in it, bad for tests! +USER_DATA /dev/null diff --git a/converter/ppm/ppmtompeg/tst/ts.stat b/converter/ppm/ppmtompeg/tst/ts.stat new file mode 100644 index 00000000..e13be8a3 --- /dev/null +++ b/converter/ppm/ppmtompeg/tst/ts.stat @@ -0,0 +1,52 @@ +MPEG ENCODER STATS (1.5b) +------------------------ +TIME STARTED: Thu Mar 30 19:50:43 2000 +MACHINE: unknown +FIRST FILE: ./tst/ts/stennis.30.jpg +LAST FILE: ./tst/ts/stennis.39.jpg +PATTERN: ibbpbbpbbp +GOP_SIZE: 30 +SLICES PER FRAME: 1 +RANGE: +/-10 +PIXEL SEARCH: HALF +PSEARCH: LOGARITHMIC +BSEARCH: CROSS2 +QSCALE: 8 10 25 +REFERENCE FRAME: ORIGINAL +TIME COMPLETED: Thu Mar 30 19:50:50 2000 +Total time: 7 seconds + +------------------------- +*****I FRAME SUMMARY***** +------------------------- + Blocks: 330 (105385 bits) ( 319 bpb) + Frames: 1 (105488 bits) (105488 bpf) (32.2% of total) + Compression: 19:1 ( 1.2487 bpp) + Seconds: 0 ( 7.6923 fps) ( 649846 pps) ( 2538 mps) +------------------------- +*****P FRAME SUMMARY***** +------------------------- + I Blocks: 110 ( 27261 bits) ( 247 bpb) + P Blocks: 872 (147211 bits) ( 168 bpb) + Skipped: 8 + Frames: 3 (174816 bits) (58272 bpf) (53.3% of total) + Compression: 34:1 ( 0.6898 bpp) + Seconds: 0 ( 3.2967 fps) ( 278505 pps) ( 1087 mps) +------------------------- +*****B FRAME SUMMARY***** +------------------------- + I Blocks: 1 ( 156 bits) ( 156 bpb) + B Blocks: 1979 ( 46497 bits) ( 23 bpb) + B types: 227 ( 21 bpb) forw 484 ( 16 bpb) back 1268 ( 26 bpb) bi + Skipped: 0 + Frames: 6 ( 47328 bits) ( 7888 bpf) (14.4% of total) + Compression: 257:1 ( 0.0934 bpp) + Seconds: 5 ( 1.1952 fps) ( 100972 pps) ( 394 mps) +--------------------------------------------- +Total Compression: 61:1 ( 0.3880 bpp) +Total Frames Per Second: 1.428571 (471 mps) +CPU Time: 1.650165 fps (544 mps) +Total Output Bit Rate (30 fps): 983472 bits/sec +MPEG file created in : /tmp/ts.mpg + + diff --git a/converter/ppm/ppmtompeg/tst/ts/stennis.30.jpg b/converter/ppm/ppmtompeg/tst/ts/stennis.30.jpg new file mode 100644 index 00000000..f70e014e --- /dev/null +++ b/converter/ppm/ppmtompeg/tst/ts/stennis.30.jpg Binary files differdiff --git a/converter/ppm/ppmtompeg/tst/ts/stennis.31.jpg b/converter/ppm/ppmtompeg/tst/ts/stennis.31.jpg new file mode 100644 index 00000000..cc88a285 --- /dev/null +++ b/converter/ppm/ppmtompeg/tst/ts/stennis.31.jpg Binary files differdiff --git a/converter/ppm/ppmtompeg/tst/ts/stennis.32.jpg b/converter/ppm/ppmtompeg/tst/ts/stennis.32.jpg new file mode 100644 index 00000000..221e22c9 --- /dev/null +++ b/converter/ppm/ppmtompeg/tst/ts/stennis.32.jpg Binary files differdiff --git a/converter/ppm/ppmtompeg/tst/ts/stennis.33.jpg b/converter/ppm/ppmtompeg/tst/ts/stennis.33.jpg new file mode 100644 index 00000000..c702a620 --- /dev/null +++ b/converter/ppm/ppmtompeg/tst/ts/stennis.33.jpg Binary files differdiff --git a/converter/ppm/ppmtompeg/tst/ts/stennis.34.jpg b/converter/ppm/ppmtompeg/tst/ts/stennis.34.jpg new file mode 100644 index 00000000..47c86806 --- /dev/null +++ b/converter/ppm/ppmtompeg/tst/ts/stennis.34.jpg Binary files differdiff --git a/converter/ppm/ppmtompeg/tst/ts/stennis.35.jpg b/converter/ppm/ppmtompeg/tst/ts/stennis.35.jpg new file mode 100644 index 00000000..ed21c16b --- /dev/null +++ b/converter/ppm/ppmtompeg/tst/ts/stennis.35.jpg Binary files differdiff --git a/converter/ppm/ppmtompeg/tst/ts/stennis.36.jpg b/converter/ppm/ppmtompeg/tst/ts/stennis.36.jpg new file mode 100644 index 00000000..6fb7506b --- /dev/null +++ b/converter/ppm/ppmtompeg/tst/ts/stennis.36.jpg Binary files differdiff --git a/converter/ppm/ppmtompeg/tst/ts/stennis.37.jpg b/converter/ppm/ppmtompeg/tst/ts/stennis.37.jpg new file mode 100644 index 00000000..561bd3b2 --- /dev/null +++ b/converter/ppm/ppmtompeg/tst/ts/stennis.37.jpg Binary files differdiff --git a/converter/ppm/ppmtompeg/tst/ts/stennis.38.jpg b/converter/ppm/ppmtompeg/tst/ts/stennis.38.jpg new file mode 100644 index 00000000..25ba097e --- /dev/null +++ b/converter/ppm/ppmtompeg/tst/ts/stennis.38.jpg Binary files differdiff --git a/converter/ppm/ppmtompeg/tst/ts/stennis.39.jpg b/converter/ppm/ppmtompeg/tst/ts/stennis.39.jpg new file mode 100644 index 00000000..02b15aff --- /dev/null +++ b/converter/ppm/ppmtompeg/tst/ts/stennis.39.jpg Binary files differdiff --git a/converter/ppm/ppmtompeg/tst/ts2.param b/converter/ppm/ppmtompeg/tst/ts2.param new file mode 100644 index 00000000..f72ba150 --- /dev/null +++ b/converter/ppm/ppmtompeg/tst/ts2.param @@ -0,0 +1,33 @@ +# test suite parameter file + +PATTERN IBBPBBPBB + +OUTPUT /tmp/ts2.mpg + +YUV_SIZE 352x240 + +BASE_FILE_FORMAT YUV +YUV_FORMAT UCB +INPUT_CONVERT * +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +INPUT_DIR ./tst/ts + +INPUT +stennis.*.yuv [30-39] +END_INPUT + +PIXEL HALF +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +REFERENCE_FRAME ORIGINAL +# Default string has the date in it, bad for tests! +USER_DATA /dev/null diff --git a/converter/ppm/ppmtompeg/tst/ts2.stat b/converter/ppm/ppmtompeg/tst/ts2.stat new file mode 100644 index 00000000..5a14ef79 --- /dev/null +++ b/converter/ppm/ppmtompeg/tst/ts2.stat @@ -0,0 +1,52 @@ +MPEG ENCODER STATS (1.5) +------------------------ +TIME STARTED: Mon Jun 26 14:22:56 1995 +MACHINE: odie.CS.Berkeley.EDU +FIRST FILE: ./tst/ts/stennis.30.yuv +LAST FILE: ./tst/ts/stennis.39.yuv +PATTERN: ibbpbbpbb +GOP_SIZE: 30 +SLICES PER FRAME: 1 +RANGE: +/-10 +FULL SEARCH: 0 +PSEARCH: LOGARITHMIC +BSEARCH: CROSS2 +QSCALE: 8 10 25 +REFERENCE FRAME: ORIGINAL +TIME COMPLETED: Mon Jun 26 14:23:11 1995 +Total time: 15 seconds + +------------------------- +*****I FRAME SUMMARY***** +------------------------- + Blocks: 660 (171792 bits) ( 260 bpb) + Frames: 2 (172008 bits) (86004 bpf) (60.0% of total) + Compression: 23:1 ( 1.0180 bpp) + Seconds: 0 ( 2.9851 fps) ( 252179 pps) ( 985 mps) +------------------------- +*****P FRAME SUMMARY***** +------------------------- + I Blocks: 54 ( 13161 bits) ( 243 bpb) + P Blocks: 598 ( 69956 bits) ( 116 bpb) + Skipped: 8 + Frames: 2 ( 83344 bits) (41672 bpf) (29.1% of total) + Compression: 48:1 ( 0.4933 bpp) + Seconds: 1 ( 1.2821 fps) ( 108307 pps) ( 423 mps) +------------------------- +*****B FRAME SUMMARY***** +------------------------- + I Blocks: 5 ( 391 bits) ( 78 bpb) + B Blocks: 1733 ( 29897 bits) ( 17 bpb) + B types: 250 ( 11 bpb) forw 483 ( 12 bpb) back 1000 ( 20 bpb) bi + Skipped: 491 + Frames: 6 ( 30960 bits) ( 5160 bpf) (10.8% of total) + Compression: 392:1 ( 0.0611 bpp) + Seconds: 11 ( 0.5029 fps) ( 42487 pps) ( 165 mps) +--------------------------------------------- +Total Compression: 70:1 ( 0.3392 bpp) +Total Frames Per Second: 0.666667 (220 mps) +CPU Time: 0.706215 fps (233 mps) +Total Output Bit Rate (30 fps): 859608 bits/sec +MPEG file created in : /tmp/ts2.mpg + + diff --git a/converter/ppm/ppmtompeg/tst/ts3.param b/converter/ppm/ppmtompeg/tst/ts3.param new file mode 100644 index 00000000..5f7b9efa --- /dev/null +++ b/converter/ppm/ppmtompeg/tst/ts3.param @@ -0,0 +1,32 @@ +# test suite parameter file + +PATTERN IBPB +OUTPUT /tmp/ts.mpg + +YUV_SIZE 352x240 + +BASE_FILE_FORMAT YUV +YUV_FORMAT UCB +INPUT_CONVERT * +GOP_SIZE 3 +SLICES_PER_FRAME 1 + +INPUT_DIR ./tst/ts + +INPUT +stennis.*.yuv [30-39] +END_INPUT + +PIXEL HALF +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +REFERENCE_FRAME ORIGINAL +# Default string has the date in it, bad for tests! +USER_DATA /dev/null diff --git a/converter/ppm/ppmtompeg/tst/ts4.param b/converter/ppm/ppmtompeg/tst/ts4.param new file mode 100644 index 00000000..c8ba161c --- /dev/null +++ b/converter/ppm/ppmtompeg/tst/ts4.param @@ -0,0 +1,56 @@ +# test suite parameter file + +PATTERN IBBBPBBBBP +OUTPUT /tmp/ts.mpg + +YUV_SIZE 352x240 + +BASE_FILE_FORMAT YUV +YUV_FORMAT UCB +INPUT_CONVERT * +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +INPUT_DIR ./tst/ts + +INPUT +stennis.*.yuv [30-39] +END_INPUT + +PIXEL HALF +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +FRAME_RATE 25.000 + +ASPECT_RATIO 1.0 + +REFERENCE_FRAME ORIGINAL + +IQTABLE + 8 16 19 22 26 27 29 34 + 9 10 11 12 13 14 15 16 + 17 18 19 20 21 22 23 24 + 25 26 27 28 29 30 31 32 + 33 34 35 36 37 38 39 40 + 41 42 43 44 45 46 47 48 + 49 50 51 52 53 54 55 56 + 57 58 59 60 61 62 63 64 + +NIQTABLE + 8 16 19 22 26 27 29 34 + 9 10 11 12 13 14 15 16 + 17 18 19 20 21 22 23 24 + 25 26 27 28 29 30 31 32 + 33 34 35 36 37 38 39 40 + 41 42 43 44 45 46 47 48 + 49 50 51 52 53 54 55 56 + 57 58 59 60 61 62 63 64 +# Default string has the date in it, bad for tests! +USER_DATA /dev/null diff --git a/converter/ppm/ppmtompeg/tst/tsd.param b/converter/ppm/ppmtompeg/tst/tsd.param new file mode 100644 index 00000000..330a7a2a --- /dev/null +++ b/converter/ppm/ppmtompeg/tst/tsd.param @@ -0,0 +1,32 @@ +# test suite parameter file + +PATTERN IBBBPBBBBP +OUTPUT /tmp/tsd.mpg + +YUV_SIZE 352x240 + +BASE_FILE_FORMAT YUV +YUV_FORMAT UCB +INPUT_CONVERT * +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +INPUT_DIR ./tst/ts + +INPUT +stennis.*.yuv [30-39] +END_INPUT + +PIXEL HALF +RANGE 10 + +PSEARCH_ALG LOGARITHMIC +BSEARCH_ALG CROSS2 + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +REFERENCE_FRAME DECODED +# Default string has the date in it, bad for tests! +USER_DATA /dev/null diff --git a/converter/ppm/ppmtompeg/tst/tsd.stat b/converter/ppm/ppmtompeg/tst/tsd.stat new file mode 100644 index 00000000..b130e089 --- /dev/null +++ b/converter/ppm/ppmtompeg/tst/tsd.stat @@ -0,0 +1,52 @@ +MPEG ENCODER STATS (1.5b) +------------------------ +TIME STARTED: Tue Aug 15 12:31:11 1995 +MACHINE: odie.CS.Berkeley.EDU +FIRST FILE: ./tst/ts/stennis.30.yuv +LAST FILE: ./tst/ts/stennis.39.yuv +PATTERN: ibbbpbbbbp +GOP_SIZE: 30 +SLICES PER FRAME: 1 +RANGE: +/-10 +PIXEL SEARCH: HALF +PSEARCH: LOGARITHMIC +BSEARCH: CROSS2 +QSCALE: 8 10 25 +REFERENCE FRAME: DECODED +TIME COMPLETED: Tue Aug 15 12:31:31 1995 +Total time: 20 seconds + +------------------------- +*****I FRAME SUMMARY***** +------------------------- + Blocks: 330 ( 94083 bits) ( 285 bpb) + Frames: 1 ( 94192 bits) (94192 bpf) (37.0% of total) + Compression: 21:1 ( 1.1150 bpp) + Seconds: 0 ( 2.4390 fps) ( 206048 pps) ( 804 mps) +------------------------- +*****P FRAME SUMMARY***** +------------------------- + I Blocks: 97 ( 20894 bits) ( 215 bpb) + P Blocks: 560 ( 83390 bits) ( 148 bpb) + Skipped: 3 + Frames: 2 (104512 bits) (52256 bpf) (41.1% of total) + Compression: 38:1 ( 0.6186 bpp) + Seconds: 1 ( 1.0811 fps) ( 91329 pps) ( 356 mps) +------------------------- +*****B FRAME SUMMARY***** +------------------------- + I Blocks: 4 ( 588 bits) ( 147 bpb) + B Blocks: 2306 ( 54124 bits) ( 23 bpb) + B types: 283 ( 14 bpb) forw 419 ( 16 bpb) back 1604 ( 26 bpb) bi + Skipped: 0 + Frames: 7 ( 55504 bits) ( 7929 bpf) (21.8% of total) + Compression: 255:1 ( 0.0939 bpp) + Seconds: 16 ( 0.4182 fps) ( 35326 pps) ( 137 mps) +--------------------------------------------- +Total Compression: 79:1 ( 0.3011 bpp) +Total Frames Per Second: 0.500000 (165 mps) +CPU Time: 0.526316 fps (173 mps) +Total Output Bit Rate (30 fps): 763200 bits/sec +MPEG file created in : /tmp/tsd.mpg + + diff --git a/converter/ppm/ppmtompeg/tst/tstl.param b/converter/ppm/ppmtompeg/tst/tstl.param new file mode 100644 index 00000000..2b9c1718 --- /dev/null +++ b/converter/ppm/ppmtompeg/tst/tstl.param @@ -0,0 +1,31 @@ +# test suite parameter file + +PATTERN IBBBPBBBBP +OUTPUT /tmp/ts.mpg + +YUV_SIZE 352x240 + +BASE_FILE_FORMAT YUV +INPUT_CONVERT * +GOP_SIZE 30 +SLICES_PER_FRAME 1 + +INPUT_DIR ../test/ts + +INPUT +stennis.*.yuv [30-39] +END_INPUT + +PIXEL HALF +RANGE 10 + +PSEARCH_ALG TWOLEVEL +BSEARCH_ALG CROSS2 + +IQSCALE 8 +PQSCALE 10 +BQSCALE 25 + +REFERENCE_FRAME ORIGINAL +# Default string has the date in it, bad for tests! +USER_DATA /dev/null diff --git a/converter/ppm/ppmtoneo.c b/converter/ppm/ppmtoneo.c new file mode 100644 index 00000000..5703c12a --- /dev/null +++ b/converter/ppm/ppmtoneo.c @@ -0,0 +1,123 @@ +/* ppmtoneo.c - read a portable pixmap and write a Neochrome NEO file +** +** Copyright (C) 2001 by Teemu Hukkanen <tjhukkan@iki.fi> +** based on ppmtopi1 by Steve Belczyk and Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "ppm.h" + +#define COLS 320 +#define ROWS 200 +#define MAXVAL 7 +#define MAXCOLORS 16 + +static short screen[ROWS*COLS/4]; /* simulate the ST's video RAM */ + +int +main(int argc, char *argv[] ) { + + FILE* ifp; + pixel** pixels; + colorhist_vector chv; + colorhash_table cht; + int rows, cols; + int colors; + int i; + int row; + pixval maxval; + + ppm_init( &argc, argv ); + + if ( argc > 2 ) + pm_usage( "[ppmfile]" ); + + if ( argc == 2 ) + ifp = pm_openr( argv[1] ); + else + ifp = stdin; + + pixels = ppm_readppm( ifp, &cols, &rows, &maxval ); + pm_close( ifp ); + if ( (cols > COLS) || (rows > ROWS) ) + pm_error( "image is larger than %dx%d - sorry", COLS, ROWS ); + + pm_message( "computing colormap..." ); + chv = ppm_computecolorhist( pixels, cols, rows, MAXCOLORS, &colors ); + if ( chv == (colorhist_vector) 0 ) { + pm_error( + "too many colors - try doing a 'pnmquant %d'", MAXCOLORS ); + } + pm_message( "%d colors found", colors ); + + /* Write NEO header */ + /* Flag */ + pm_writebigshort( stdout, (short) 0); + + /* resolution */ + pm_writebigshort( stdout, (short) 0 ); /* low resolution */ + + /* palette */ + for ( i = 0; i < 16; ++i ) { + short w; + + if ( i < colors ) { + pixel p; + + p = chv[i].color; + if ( maxval != MAXVAL ) + PPM_DEPTH( p, p, maxval, MAXVAL ); + w = ( (int) PPM_GETR( p ) ) << 8; + w |= ( (int) PPM_GETG( p ) ) << 4; + w |= ( (int) PPM_GETB( p ) ); + } else + w = 0; + pm_writebigshort( stdout, w ); + } + if ( maxval > MAXVAL ) + pm_message( + "maxval is not %d - automatically rescaling colors", MAXVAL ); + + /* Convert color vector to color hash table, for fast lookup. */ + cht = ppm_colorhisttocolorhash( chv, colors ); + ppm_freecolorhist( chv ); + + /* Skip rest of header */ + fseek(stdout, 128, SEEK_SET); + + /* Clear the screen buffer. */ + for ( i = 0; i < ROWS*COLS/4; ++i ) + screen[i] = 0; + + /* Convert. */ + for ( row = 0; row < rows; ++row ) { + int col; + for ( col = 0; col < cols; ++col) { + int color, ind, b, plane; + pixel const p = pixels[row][col]; + + color = ppm_lookupcolor( cht, &p ); + if ( color == -1 ) + pm_error( + "color not found?!? row=%d col=%d r=%d g=%d b=%d", + row, col, PPM_GETR(p), PPM_GETG(p), PPM_GETB(p) ); + ind = 80 * row + ( ( col >> 4 ) << 2 ); + b = 0x8000 >> (col & 0xf); + for ( plane = 0; plane < 4; ++plane ) + if ( color & (1 << plane) ) + screen[ind+plane] |= b; + } + } + + /* And write out the screen buffer. */ + for ( i = 0; i < ROWS*COLS/4; ++i ) + (void) pm_writebigshort( stdout, screen[i] ); + + exit( 0 ); +} diff --git a/converter/ppm/ppmtopcx.c b/converter/ppm/ppmtopcx.c new file mode 100644 index 00000000..bdcfc5c7 --- /dev/null +++ b/converter/ppm/ppmtopcx.c @@ -0,0 +1,906 @@ +/* ppmtopcx.c - convert a portable pixmap to PCX +** +** Copyright (C) 1994 by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de) +** based on ppmtopcx.c by Michael Davidson +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +** +** 11/Dec/94: first version +** 12/Dec/94: added handling of "packed" format (16 colors or less) +*/ +#include <assert.h> + +#include "ppm.h" +#include "shhopt.h" +#include "mallocvar.h" + +#define MAXCOLORS 256 + +#define PCX_MAGIC 0x0a /* PCX magic number */ +#define PCX_256_COLORS 0x0c /* magic number for 256 colors */ +#define PCX_MAXVAL (pixval)255 + + +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char *inputFilespec; /* '-' if stdin */ + unsigned int truecolor; /* -24bit option */ + unsigned int use_8_bit; /* -8bit option */ + unsigned int planes; /* zero means minimum */ + unsigned int packed; + unsigned int verbose; + unsigned int stdpalette; + const char * palette; /* NULL means none */ + int xpos; + int ypos; +}; + + + +struct pcxCmapEntry { + unsigned char r; + unsigned char g; + unsigned char b; +}; + +static struct pcxCmapEntry +pcxCmapEntryFromPixel(pixel const colorPixel) { + + struct pcxCmapEntry retval; + + retval.r = PPM_GETR(colorPixel); + retval.g = PPM_GETG(colorPixel); + retval.b = PPM_GETB(colorPixel); + + return retval; +} + + + +static void +parseCommandLine(int argc, char ** argv, + struct cmdlineInfo * const cmdlineP) { +/*---------------------------------------------------------------------------- + parse program command line described in Unix standard form by argc + and argv. Return the information in the options as *cmdlineP. + + If command line is internally inconsistent (invalid options, etc.), + issue error message to stderr and abort program. + + Note that the strings we return are stored in the storage that + was passed to us as the argv array. We also trash *argv. +-----------------------------------------------------------------------------*/ + optEntry *option_def; + /* Instructions to optParseOptions3 on how to parse our options. + */ + optStruct3 opt; + + unsigned int option_def_index; + + unsigned int planesSpec, xposSpec, yposSpec, paletteSpec; + + MALLOCARRAY(option_def, 100); + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "24bit", OPT_FLAG, NULL, + &cmdlineP->truecolor, 0); + OPTENT3(0, "8bit", OPT_FLAG, NULL, + &cmdlineP->use_8_bit, 0); + OPTENT3(0, "planes", OPT_UINT, &cmdlineP->planes, + &planesSpec, 0); + OPTENT3(0, "packed", OPT_FLAG, NULL, + &cmdlineP->packed, 0); + OPTENT3(0, "verbose", OPT_FLAG, NULL, + &cmdlineP->verbose, 0); + OPTENT3(0, "stdpalette", OPT_FLAG, NULL, + &cmdlineP->stdpalette, 0); + OPTENT3(0, "palette", OPT_STRING, &cmdlineP->palette, + &paletteSpec, 0); + OPTENT3(0, "xpos", OPT_INT, &cmdlineP->xpos, &xposSpec, 0); + OPTENT3(0, "ypos", OPT_INT, &cmdlineP->ypos, &yposSpec, 0); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ + + optParseOptions3( &argc, argv, opt, sizeof(opt), 0 ); + /* Uses and sets argc, argv, and some of *cmdline_p and others. */ + + if (!xposSpec) + cmdlineP->xpos = 0; + else if (cmdlineP->xpos < -32767 || cmdlineP->xpos > 32768) + pm_error("-xpos value (%d) is outside acceptable range " + "(-32767, 32768)", cmdlineP->xpos); + + if (!yposSpec) + cmdlineP->ypos = 0; + else if (cmdlineP->ypos < -32767 || cmdlineP->ypos > 32768) + pm_error("-ypos value (%d) is outside acceptable range " + "(-32767, 32768)", cmdlineP->ypos); + + if (!planesSpec) + cmdlineP->planes = 0; /* 0 means minimum */ + + if (planesSpec) { + if (cmdlineP->planes > 4 || cmdlineP->planes < 1) + pm_error("The only possible numbers of planes are 1-4. " + "You specified %u", cmdlineP->planes); + if (cmdlineP->packed) + pm_error("-planes is meaningless with -packed."); + if (cmdlineP->truecolor) + pm_error("-planes is meaningless with -24bit"); + if (cmdlineP->use_8_bit) + pm_error("-planes is meaningless with -8bit"); + } + + if (paletteSpec && cmdlineP->stdpalette) + pm_error("You can't specify both -palette and -stdpalette"); + + if (!paletteSpec) + cmdlineP->palette = NULL; + + if (cmdlineP->use_8_bit && cmdlineP->truecolor) + pm_error("You cannot specify both -8bit and -truecolor"); + + if (argc-1 < 1) + cmdlineP->inputFilespec = "-"; + else if (argc-1 == 1) + cmdlineP->inputFilespec = argv[1]; + else + pm_error("Program takes at most one argument " + "(input file specification). You specified %d", + argc-1); +} + + + +/* + * Write out a two-byte little-endian word to the PCX file + */ +static void +Putword(int const w, + FILE * const fp) { + + int rc; + + rc = pm_writelittleshort(fp, w); + + if (rc != 0) + pm_error("Error writing integer to output file"); +} + + + +/* + * Write out a byte to the PCX file + */ +static void +Putbyte(int const b, + FILE * const fp) { + + int rc; + + rc = fputc(b & 0xff, fp); + if (rc == EOF) + pm_error("Error writing byte to output file."); +} + + + +static const unsigned char bitmask[] = {1, 2, 4, 8, 16, 32, 64, 128}; + + +static void +extractPlane(unsigned char * const rawrow, + int const cols, + unsigned char * const buf, + int const plane) { +/*---------------------------------------------------------------------------- + From the image row 'rawrow', which is an array of 'cols' palette indices + (as unsigned 8 bit integers), extract plane number 'plane' and return + it at 'buf'. + + E.g. Plane 2 is all the 2nd bits from the palette indices, packed so + that each byte represents 8 columns. +-----------------------------------------------------------------------------*/ + unsigned int const planeMask = 1 << plane; + + unsigned int col; + int cbit; /* Significance of bit representing current column in output */ + unsigned char *cp; /* Ptr to next output byte to fill */ + unsigned char byteUnderConstruction; + + cp = buf; /* initial value */ + + cbit = 7; + byteUnderConstruction = 0x00; + for (col = 0; col < cols; ++col) { + if (rawrow[col] & planeMask) + byteUnderConstruction |= (1 << cbit); + + --cbit; + if (cbit < 0) { + /* We've filled a byte. Output it and start the next */ + *cp++ = byteUnderConstruction; + cbit = 7; + byteUnderConstruction = 0x00; + } + } + if (cbit < 7) + /* A byte was partially built when we ran out of columns (the number + of columns is not a multiple of 8. Output the partial byte. + */ + *cp++ = byteUnderConstruction; +} + + + +static void +PackBits(unsigned char * const rawrow, + int const width, + unsigned char * const buf, + int const bits) { + + int x, i, shift; + + shift = i = -1; + + for (x = 0; x < width; ++x) { + if (shift < 0) { + shift = 8 - bits; + buf[++i] = 0; + } + + buf[i] |= (rawrow[x] << shift); + shift -= bits; + } +} + + + +static void +write_header(FILE * const fp, + int const cols, + int const rows, + int const BitsPerPixel, + int const Planes, + struct pcxCmapEntry const cmap16[], + unsigned int const xPos, + unsigned int const yPos) { + + int i, BytesPerLine; + + Putbyte(PCX_MAGIC, fp); /* .PCX magic number */ + Putbyte(0x05, fp); /* PC Paintbrush version */ + Putbyte(0x01, fp); /* .PCX run length encoding */ + Putbyte(BitsPerPixel, fp); /* bits per pixel */ + + Putword(xPos, fp); /* x1 - image left */ + Putword(yPos, fp); /* y1 - image top */ + Putword(xPos+cols-1, fp); /* x2 - image right */ + Putword(yPos+rows-1, fp); /* y2 - image bottom */ + + Putword(cols, fp); /* horizontal resolution */ + Putword(rows, fp); /* vertical resolution */ + + /* Write out the Color Map for images with 16 colors or less */ + if (cmap16) + for (i = 0; i < 16; ++i) { + Putbyte(cmap16[i].r, fp); + Putbyte(cmap16[i].g, fp); + Putbyte(cmap16[i].b, fp); + } + else { + unsigned int i; + for (i = 0; i < 16; ++i) { + Putbyte(0, fp); + Putbyte(0, fp); + Putbyte(0, fp); + } + } + Putbyte(0, fp); /* reserved byte */ + Putbyte(Planes, fp); /* number of color planes */ + + BytesPerLine = ((cols * BitsPerPixel) + 7) / 8; + Putword(BytesPerLine, fp); /* number of bytes per scanline */ + + Putword(1, fp); /* palette info */ + + { + unsigned int i; + for (i = 0; i < 58; ++i) /* fill to end of header */ + Putbyte(0, fp); + } +} + + + +static void +PCXEncode(FILE * const fp, + const unsigned char * const buf, + int const Size) { + + const unsigned char * const end = buf + Size; + + const unsigned char * currentP; + int previous, count; + + currentP = buf; + previous = *currentP++; + count = 1; + + while (currentP < end) { + unsigned char const c = *currentP++; + if (c == previous && count < 63) + ++count; + else { + if (count > 1 || (previous & 0xc0) == 0xc0) { + count |= 0xc0; + Putbyte ( count , fp ); + } + Putbyte(previous, fp); + previous = c; + count = 1; + } + } + + if (count > 1 || (previous & 0xc0) == 0xc0) { + count |= 0xc0; + Putbyte ( count , fp ); + } + Putbyte(previous, fp); +} + + + +static unsigned int +indexOfColor(colorhash_table const cht, + pixel const color) { +/*---------------------------------------------------------------------------- + Return the index in the palette described by 'cht' of the color 'color'. + + Abort program with error message if the color is not in the palette. +-----------------------------------------------------------------------------*/ + + int const rc = ppm_lookupcolor(cht, &color); + + if (rc < 0) + pm_error("Image contains color which is not " + "in the palette: %u/%u/%u", + PPM_GETR(color), PPM_GETG(color), PPM_GETB(color)); + + return rc; +} + + + +static void +ppmTo16ColorPcx(pixel ** const pixels, + int const cols, + int const rows, + struct pcxCmapEntry const pcxcmap[], + int const colors, + colorhash_table const cht, + bool const packbits, + unsigned int const planesRequested, + unsigned int const xPos, + unsigned int const yPos) { + + int Planes, BytesPerLine, BitsPerPixel; + unsigned char *indexRow; /* malloc'ed */ + /* indexRow[x] is the palette index of the pixel at column x of + the row currently being processed + */ + unsigned char *planesrow; /* malloc'ed */ + /* This is the input for a single row to the compressor */ + int row; + + if (packbits) { + Planes = 1; + if (colors > 4) BitsPerPixel = 4; + else if (colors > 2) BitsPerPixel = 2; + else BitsPerPixel = 1; + } else { + BitsPerPixel = 1; + if (planesRequested) + Planes = planesRequested; + else { + if (colors > 8) Planes = 4; + else if (colors > 4) Planes = 3; + else if (colors > 2) Planes = 2; + else Planes = 1; + } + } + BytesPerLine = ((cols * BitsPerPixel) + 7) / 8; + MALLOCARRAY_NOFAIL(indexRow, cols); + MALLOCARRAY_NOFAIL(planesrow, BytesPerLine); + + write_header(stdout, cols, rows, BitsPerPixel, Planes, pcxcmap, + xPos, yPos); + for (row = 0; row < rows; ++row) { + int col; + for (col = 0; col < cols; ++col) + indexRow[col] = indexOfColor(cht, pixels[row][col]); + + if (packbits) { + PackBits(indexRow, cols, planesrow, BitsPerPixel); + PCXEncode(stdout, planesrow, BytesPerLine); + } else { + unsigned int plane; + for (plane = 0; plane < Planes; ++plane) { + extractPlane(indexRow, cols, planesrow, plane); + PCXEncode(stdout, planesrow, BytesPerLine); + } + } + } + free(planesrow); + free(indexRow); +} + + + +static void +ppmTo256ColorPcx(pixel ** const pixels, + int const cols, + int const rows, + struct pcxCmapEntry const pcxcmap[], + int const colors, + colorhash_table const cht, + unsigned int const xPos, + unsigned int const yPos) { + + int row; + unsigned int i; + unsigned char *rawrow; + + rawrow = (unsigned char *)pm_allocrow(cols, sizeof(unsigned char)); + + /* 8 bits per pixel, 1 plane */ + write_header(stdout, cols, rows, 8, 1, NULL, xPos, yPos); + for (row = 0; row < rows; ++row) { + int col; + for (col = 0; col < cols; ++col) + rawrow[col] = indexOfColor(cht, pixels[row][col]); + PCXEncode(stdout, rawrow, cols); + } + Putbyte(PCX_256_COLORS, stdout); + for (i = 0; i < MAXCOLORS; ++i) { + Putbyte(pcxcmap[i].r, stdout); + Putbyte(pcxcmap[i].g, stdout); + Putbyte(pcxcmap[i].b, stdout); + } + pm_freerow((void*)rawrow); +} + + + +static void +ppmToTruecolorPcx(pixel ** const pixels, + int const cols, + int const rows, + pixval const maxval, + unsigned int const xPos, + unsigned int const yPos) { + + unsigned char *redrow, *greenrow, *bluerow; + int col, row; + + redrow = (unsigned char *)pm_allocrow(cols, sizeof(unsigned char)); + greenrow = (unsigned char *)pm_allocrow(cols, sizeof(unsigned char)); + bluerow = (unsigned char *)pm_allocrow(cols, sizeof(unsigned char)); + + /* 8 bits per pixel, 3 planes */ + write_header(stdout, cols, rows, 8, 3, NULL, xPos, yPos); + for( row = 0; row < rows; row++ ) { + register pixel *pP = pixels[row]; + for( col = 0; col < cols; col++, pP++ ) { + if( maxval != PCX_MAXVAL ) { + redrow[col] = (long)PPM_GETR(*pP) * PCX_MAXVAL / maxval; + greenrow[col] = (long)PPM_GETG(*pP) * PCX_MAXVAL / maxval; + bluerow[col] = (long)PPM_GETB(*pP) * PCX_MAXVAL / maxval; + } + else { + redrow[col] = PPM_GETR(*pP); + greenrow[col] = PPM_GETG(*pP); + bluerow[col] = PPM_GETB(*pP); + } + } + PCXEncode(stdout, redrow, cols); + PCXEncode(stdout, greenrow, cols); + PCXEncode(stdout, bluerow, cols); + } + pm_freerow((void*)bluerow); + pm_freerow((void*)greenrow); + pm_freerow((void*)redrow); +} + + + +static const struct pcxCmapEntry +stdPalette[] = { + { 0, 0, 0 }, + { 0, 0, 170 }, + { 0, 170, 0 }, + { 0, 170, 170 }, + { 170, 0, 0 }, + { 170, 0, 170 }, + { 170, 170, 0 }, + { 170, 170, 170 }, + { 85, 85, 85 }, + { 85, 85, 255 }, + { 85, 255, 85 }, + { 85, 255, 255 }, + { 255, 85, 85 }, + { 255, 85, 255 }, + { 255, 255, 85 }, + { 255, 255, 255 } +}; + + + +static void +putPcxColorInHash(colorhash_table const cht, + pixel const newPcxColor, + unsigned int const newColorIndex, + pixval const maxval) { + + pixel ppmColor; + /* Same color as 'newPcxColor', but at the PPM image's color + resolution: 'maxval' + */ + int rc; + + PPM_DEPTH(ppmColor, newPcxColor, PCX_MAXVAL, maxval); + + rc = ppm_lookupcolor(cht, &ppmColor); + + if (rc == -1) + /* This color is not in the hash yet, so we just add it */ + ppm_addtocolorhash(cht, &ppmColor, newColorIndex); + else { + /* This color is already in the hash. That's because the + subject image has less color resolution than PCX (i.e. + 'maxval' is less than PCX_MAXVAL), and two distinct + colors in the standard palette are indistinguishable at + subject image color resolution. + + So we have to figure out wether color 'newPcxColor' or + 'existingPcxColor' is a better match for 'ppmColor'. + */ + + unsigned int const existingColorIndex = rc; + + pixel idealPcxColor; + pixel existingPcxColor; + + PPM_DEPTH(idealPcxColor, ppmColor, maxval, PCX_MAXVAL); + + PPM_ASSIGN(existingPcxColor, + stdPalette[existingColorIndex].r, + stdPalette[existingColorIndex].g, + stdPalette[existingColorIndex].b); + + if (PPM_DISTANCE(newPcxColor, idealPcxColor) < + PPM_DISTANCE(existingPcxColor, idealPcxColor)) { + /* The new PCX color is a better match. Make it the new + translation of image color 'ppmColor'. + */ + ppm_delfromcolorhash(cht, &ppmColor); + ppm_addtocolorhash(cht, &ppmColor, newColorIndex); + } + } +} + + + +static void +generateStandardPalette(struct pcxCmapEntry ** const pcxcmapP, + pixval const maxval, + colorhash_table * const chtP, + int * const colorsP) { + + unsigned int const stdPaletteSize = 16; + unsigned int colorIndex; + struct pcxCmapEntry * pcxcmap; + colorhash_table cht; + + MALLOCARRAY_NOFAIL(pcxcmap, MAXCOLORS); + + *pcxcmapP = pcxcmap; + + cht = ppm_alloccolorhash(); + + for (colorIndex = 0; colorIndex < stdPaletteSize; ++colorIndex) { + pixel pcxColor; + /* The color of this colormap entry, in PCX resolution */ + + pcxcmap[colorIndex] = stdPalette[colorIndex]; + + PPM_ASSIGN(pcxColor, + stdPalette[colorIndex].r, + stdPalette[colorIndex].g, + stdPalette[colorIndex].b); + + putPcxColorInHash(cht, pcxColor, colorIndex, maxval); + } + + *chtP = cht; + *colorsP = stdPaletteSize; +} + + + +static void +readPpmPalette(const char * const paletteFileName, + pixel (* const ppmPaletteP)[], + unsigned int * const paletteSizeP) { + + FILE * pfP; + pixel ** pixels; + int cols, rows; + pixval maxval; + + pfP = pm_openr(paletteFileName); + + pixels = ppm_readppm(pfP, &cols, &rows, &maxval); + + pm_close(pfP); + + *paletteSizeP = rows * cols; + if (*paletteSizeP > MAXCOLORS) + pm_error("ordered palette image contains %d pixels. Maximum is %d", + *paletteSizeP, MAXCOLORS); + + { + int j; + int row; + j = 0; /* initial value */ + for (row = 0; row < rows; ++row) { + int col; + for (col = 0; col < cols; ++col) + (*ppmPaletteP)[j++] = pixels[row][col]; + } + } + ppm_freearray(pixels, rows); +} + + + +static void +readPaletteFromFile(struct pcxCmapEntry ** const pcxcmapP, + const char * const paletteFileName, + pixval const maxval, + colorhash_table * const chtP, + int * const colorsP) { + + unsigned int colorIndex; + pixel ppmPalette[MAXCOLORS]; + unsigned int paletteSize; + struct pcxCmapEntry * pcxcmap; + colorhash_table cht; + + readPpmPalette(paletteFileName, &ppmPalette, &paletteSize); + + MALLOCARRAY_NOFAIL(pcxcmap, MAXCOLORS); + + *pcxcmapP = pcxcmap; + + cht = ppm_alloccolorhash(); + + for (colorIndex = 0; colorIndex < paletteSize; ++colorIndex) { + pixel pcxColor; + /* The color of this colormap entry, in PCX resolution */ + + pcxcmap[colorIndex] = pcxCmapEntryFromPixel(ppmPalette[colorIndex]); + + PPM_ASSIGN(pcxColor, + ppmPalette[colorIndex].r, + ppmPalette[colorIndex].g, + ppmPalette[colorIndex].b); + + putPcxColorInHash(cht, pcxColor, colorIndex, maxval); + } + + *chtP = cht; + *colorsP = paletteSize; +} + + + +static void +moveBlackToIndex0(colorhist_vector const chv, + int const colors) { +/*---------------------------------------------------------------------------- + If black is in the palette, make it at Index 0. +-----------------------------------------------------------------------------*/ + pixel blackPixel; + unsigned int i; + bool blackPresent; + + PPM_ASSIGN(blackPixel, 0, 0, 0); + + blackPresent = FALSE; /* initial assumption */ + + for (i = 0; i < colors; ++i) + if (PPM_EQUAL(chv[i].color, blackPixel)) + blackPresent = TRUE; + + if (blackPresent) { + /* We use a trick here. ppm_addtocolorhist() always adds to the + beginning of the table and if the color is already elsewhere in + the table, removes it. + */ + int colors2; + colors2 = colors; + ppm_addtocolorhist(chv, &colors2, MAXCOLORS, &blackPixel, 0, 0); + assert(colors2 == colors); + } +} + + + +static void +makePcxColormapFromImage(pixel ** const pixels, + int const cols, + int const rows, + pixval const maxval, + struct pcxCmapEntry ** const pcxcmapP, + colorhash_table * const chtP, + int * const colorsP, + bool * const tooManyColorsP) { +/*---------------------------------------------------------------------------- + Make a colormap (palette) for the PCX header that can be used + for the image described by 'pixels', 'cols', 'rows', and 'maxval'. + + Return it in newly malloc'ed storage and return its address as + *pcxcmapP. + + Also return a lookup hash to relate a color in the image to the + appropriate index in *pcxcmapP. Return that in newly malloc'ed + storage as *chtP. + + Iff there are too many colors to do that (i.e. more than 256), + return *tooManyColorsP == TRUE. +-----------------------------------------------------------------------------*/ + int colors; + colorhist_vector chv; + + pm_message("computing colormap..."); + + chv = ppm_computecolorhist(pixels, cols, rows, MAXCOLORS, &colors); + if (chv == NULL) + *tooManyColorsP = TRUE; + else { + int i; + struct pcxCmapEntry * pcxcmap; + + *tooManyColorsP = FALSE; + + pm_message("%d colors found", colors); + + moveBlackToIndex0(chv, colors); + + MALLOCARRAY_NOFAIL(pcxcmap, MAXCOLORS); + + *pcxcmapP = pcxcmap; + + for (i = 0; i < colors; ++i) { + pixel p; + + PPM_DEPTH(p, chv[i].color, maxval, PCX_MAXVAL); + + pcxcmap[i].r = PPM_GETR(p); + pcxcmap[i].g = PPM_GETG(p); + pcxcmap[i].b = PPM_GETB(p); + } + + /* Fill it out with black */ + for ( ; i < MAXCOLORS; ++i) { + pcxcmap[i].r = 0; + pcxcmap[i].g = 0; + pcxcmap[i].b = 0; + } + + *chtP = ppm_colorhisttocolorhash(chv, colors); + + *colorsP = colors; + + ppm_freecolorhist(chv); + } +} + + + +static void +ppmToPalettePcx(pixel ** const pixels, + int const cols, + int const rows, + pixval const maxval, + unsigned int const xPos, + unsigned int const yPos, + struct pcxCmapEntry const pcxcmap[], + colorhash_table const cht, + int const colors, + bool const packbits, + unsigned int const planes, + bool const use_8_bit) { + + /* convert image */ + if( colors <= 16 && !use_8_bit ) + ppmTo16ColorPcx(pixels, cols, rows, pcxcmap, colors, cht, + packbits, planes, xPos, yPos); + else + ppmTo256ColorPcx(pixels, cols, rows, pcxcmap, colors, cht, + xPos, yPos); +} + + + +int +main(int argc, char *argv[]) { + + struct cmdlineInfo cmdline; + FILE* ifP; + int rows, cols; + pixval maxval; + pixel **pixels; + struct pcxCmapEntry * pcxcmap; + colorhash_table cht; + bool truecolor; + int colors; + + ppm_init(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.inputFilespec); + pixels = ppm_readppm(ifP, &cols, &rows, &maxval); + pm_close(ifP); + + if (cmdline.truecolor) + truecolor = TRUE; + else { + if (cmdline.stdpalette) { + truecolor = FALSE; + generateStandardPalette(&pcxcmap, maxval, &cht, &colors); + } else if (cmdline.palette) { + truecolor = FALSE; + readPaletteFromFile(&pcxcmap, cmdline.palette, maxval, + &cht, &colors); + } else { + bool tooManyColors; + makePcxColormapFromImage(pixels, cols, rows, maxval, + &pcxcmap, &cht, &colors, + &tooManyColors); + + if (tooManyColors) { + pm_message("too many colors - writing a 24bit PCX file"); + pm_message("if you want a non-24bit file, " + " a 'pnmquant %d'", MAXCOLORS); + truecolor = TRUE; + } else + truecolor = FALSE; + } + } + + if (truecolor) + ppmToTruecolorPcx(pixels, cols, rows, maxval, + cmdline.xpos, cmdline.ypos); + else { + ppmToPalettePcx(pixels, cols, rows, maxval, + cmdline.xpos, cmdline.ypos, + pcxcmap, cht, colors, cmdline.packed, + cmdline.planes, cmdline.use_8_bit); + + ppm_freecolorhash(cht); + free(pcxcmap); + } + return 0; +} diff --git a/converter/ppm/ppmtopi1.c b/converter/ppm/ppmtopi1.c new file mode 100644 index 00000000..64f836c7 --- /dev/null +++ b/converter/ppm/ppmtopi1.c @@ -0,0 +1,120 @@ +/* ppmtopi1.c - read a portable pixmap and write a Degas PI1 file +** +** Copyright (C) 1991 by Steve Belczyk and Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "ppm.h" + +#define COLS 320 +#define ROWS 200 +#define MAXVAL 7 +#define MAXCOLORS 16 + +static short screen[ROWS*COLS/4]; /* simulate the ST's video RAM */ + +int +main( argc, argv ) + int argc; + char* argv[]; + { + FILE* ifp; + pixel** pixels; + register pixel *pP; + colorhist_vector chv; + colorhash_table cht; + int rows, cols, row, colors, i; + register int col; + pixval maxval; + + + ppm_init( &argc, argv ); + + if ( argc > 2 ) + pm_usage( "[ppmfile]" ); + + if ( argc == 2 ) + ifp = pm_openr( argv[1] ); + else + ifp = stdin; + + pixels = ppm_readppm( ifp, &cols, &rows, &maxval ); + pm_close( ifp ); + if ( (cols > COLS) || (rows > ROWS) ) + pm_error( "image is larger than %dx%d - sorry", COLS, ROWS ); + + pm_message( "computing colormap..." ); + chv = ppm_computecolorhist( pixels, cols, rows, MAXCOLORS, &colors ); + if ( chv == (colorhist_vector) 0 ) + { + pm_message( + "too many colors - try doing a 'pnmquant %d'", MAXCOLORS ); + exit( 1 ); + } + pm_message( "%d colors found", colors ); + + /* Write PI1 header - resolution and palette. */ + (void) pm_writebigshort( stdout, (short) 0 ); /* low resolution */ + for ( i = 0; i < 16; ++i ) + { + short w; + + if ( i < colors ) + { + pixel p; + + p = chv[i].color; + if ( maxval != MAXVAL ) + PPM_DEPTH( p, p, maxval, MAXVAL ); + w = ( (int) PPM_GETR( p ) ) << 8; + w |= ( (int) PPM_GETG( p ) ) << 4; + w |= ( (int) PPM_GETB( p ) ); + } + else + w = 0; + (void) pm_writebigshort( stdout, w ); + } + if ( maxval > MAXVAL ) + pm_message( + "maxval is not %d - automatically rescaling colors", MAXVAL ); + + /* Convert color vector to color hash table, for fast lookup. */ + cht = ppm_colorhisttocolorhash( chv, colors ); + ppm_freecolorhist( chv ); + + /* Clear the screen buffer. */ + for ( i = 0; i < ROWS*COLS/4; ++i ) + screen[i] = 0; + + /* Convert. */ + for ( row = 0; row < rows; ++row ) + { + for ( col = 0, pP = pixels[row]; col < cols; ++col, ++pP ) + { + register int color, ind, b, plane; + + color = ppm_lookupcolor( cht, pP ); + if ( color == -1 ) + pm_error( + "color not found?!? row=%d col=%d r=%d g=%d b=%d", + row, col, PPM_GETR(*pP), PPM_GETG(*pP), PPM_GETB(*pP) ); + ind = 80 * row + ( ( col >> 4 ) << 2 ); + b = 0x8000 >> (col & 0xf); + for ( plane = 0; plane < 4; ++plane ) + if ( color & (1 << plane) ) + screen[ind+plane] |= b; + } + } + + /* And write out the screen buffer. */ + for ( i = 0; i < ROWS*COLS/4; ++i ) + (void) pm_writebigshort( stdout, screen[i] ); + + exit( 0 ); + } diff --git a/converter/ppm/ppmtopict.c b/converter/ppm/ppmtopict.c new file mode 100644 index 00000000..e2428fb6 --- /dev/null +++ b/converter/ppm/ppmtopict.c @@ -0,0 +1,481 @@ +/* +** ppmtopict.c - read a portable pixmap and produce a Macintosh PICT2 file. +** +** Copyright (C) 1990 by Ken Yap <ken@cs.rochester.edu>. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "ppm.h" + +#define HEADER_SIZE 512 + +#define RUN_THRESH 3 +#define MAX_RUN 128 /* 0xff = 2, 0xfe = 3, etc */ +#define MAX_COUNT 128 /* 0x00 = 1, 0x01 = 2, etc */ + +/* Opcodes */ +#define PICT_NOP 0x00 +#define PICT_clipRgn 0x01 +#define PICT_bkPat 0x02 +#define PICT_txFont 0x03 +#define PICT_txFace 0x04 +#define PICT_txMode 0x05 +#define PICT_spExtra 0x06 +#define PICT_pnSize 0x07 +#define PICT_pnMode 0x08 +#define PICT_pnPat 0x09 +#define PICT_thePat 0x0A +#define PICT_ovSize 0x0B +#define PICT_origin 0x0C +#define PICT_txSize 0x0D +#define PICT_fgColor 0x0E +#define PICT_bkColor 0x0F +#define PICT_txRatio 0x10 +#define PICT_picVersion 0x11 +#define PICT_blPixPat 0x12 +#define PICT_pnPixPat 0x13 +#define PICT_fillPixPat 0x14 +#define PICT_pnLocHFrac 0x15 +#define PICT_chExtra 0x16 +#define PICT_rgbFgCol 0x1A +#define PICT_rgbBkCol 0x1B +#define PICT_hiliteMode 0x1C +#define PICT_hiliteColor 0x1D +#define PICT_defHilite 0x1E +#define PICT_opColor 0x1F +#define PICT_line 0x20 +#define PICT_line_from 0x21 +#define PICT_short_line 0x22 +#define PICT_short_line_from 0x23 +#define PICT_long_text 0x28 +#define PICT_DH_text 0x29 +#define PICT_DV_text 0x2A +#define PICT_DHDV_text 0x2B +#define PICT_frameRect 0x30 +#define PICT_paintRect 0x31 +#define PICT_eraseRect 0x32 +#define PICT_invertRect 0x33 +#define PICT_fillRect 0x34 +#define PICT_frameSameRect 0x38 +#define PICT_paintSameRect 0x39 +#define PICT_eraseSameRect 0x3A +#define PICT_invertSameRect 0x3B +#define PICT_fillSameRect 0x3C +#define PICT_frameRRect 0x40 +#define PICT_paintRRect 0x41 +#define PICT_eraseRRect 0x42 +#define PICT_invertRRect 0x43 +#define PICT_fillRRect 0x44 +#define PICT_frameSameRRect 0x48 +#define PICT_paintSameRRect 0x49 +#define PICT_eraseSameRRect 0x4A +#define PICT_invertSameRRect 0x4B +#define PICT_fillSameRRect 0x4C +#define PICT_frameOval 0x50 +#define PICT_paintOval 0x51 +#define PICT_eraseOval 0x52 +#define PICT_invertOval 0x53 +#define PICT_fillOval 0x54 +#define PICT_frameSameOval 0x58 +#define PICT_paintSameOval 0x59 +#define PICT_eraseSameOval 0x5A +#define PICT_invertSameOval 0x5B +#define PICT_fillSameOval 0x5C +#define PICT_frameArc 0x60 +#define PICT_paintArc 0x61 +#define PICT_eraseArc 0x62 +#define PICT_invertArc 0x63 +#define PICT_fillArc 0x64 +#define PICT_frameSameArc 0x68 +#define PICT_paintSameArc 0x69 +#define PICT_eraseSameArc 0x6A +#define PICT_invertSameArc 0x6B +#define PICT_fillSameArc 0x6C +#define PICT_framePoly 0x70 +#define PICT_paintPoly 0x71 +#define PICT_erasePoly 0x72 +#define PICT_invertPoly 0x73 +#define PICT_fillPoly 0x74 +#define PICT_frameSamePoly 0x78 +#define PICT_paintSamePoly 0x79 +#define PICT_eraseSamePoly 0x7A +#define PICT_invertSamePoly 0x7B +#define PICT_fillSamePoly 0x7C +#define PICT_frameRgn 0x80 +#define PICT_paintRgn 0x81 +#define PICT_eraseRgn 0x82 +#define PICT_invertRgn 0x83 +#define PICT_fillRgn 0x84 +#define PICT_frameSameRgn 0x88 +#define PICT_paintSameRgn 0x89 +#define PICT_eraseSameRgn 0x8A +#define PICT_invertSameRgn 0x8B +#define PICT_fillSameRgn 0x8C +#define PICT_BitsRect 0x90 +#define PICT_BitsRgn 0x91 +#define PICT_PackBitsRect 0x98 +#define PICT_PackBitsRgn 0x99 +#define PICT_shortComment 0xA0 +#define PICT_longComment 0xA1 +#define PICT_EndOfPicture 0xFF +#define PICT_headerOp 0x0C00 + +static void putFill ARGS(( FILE *fd, int n )); +static void putShort ARGS(( FILE *fd, int i )); +static void putLong ARGS(( FILE *fd, long i )); +static void putFixed ARGS(( FILE *fd, int in, int frac )); +static void putRect ARGS(( FILE *fd, int x1, int x2, int y1, int y2 )); +static int putRow ARGS(( FILE *fd, int row, int cols, pixel *rowpixels, char *packed )); + +#define MAXCOLORS 256 +static colorhash_table cht; + +int +main(argc, argv) +int argc; +char *argv[]; +{ + FILE *ifp; + int argn, rows, cols, colors, i, row, oc; + register pixel **pixels; + char *packed; + pixval maxval; + long lmaxval, rval, gval, bval; + colorhist_vector chv; + + + ppm_init( &argc, argv ); + + argn = 1; + if (argn < argc) + { + ifp = pm_openr(argv[1]); + argn++; + } + else + ifp = stdin; + if (argn != argc) + pm_usage("[ppmfile]"); + + pixels = ppm_readppm(ifp, &cols, &rows, &maxval); + if (cols < 8) + pm_error("ppm input too narrow, must be >= 8 pixels wide" ); + lmaxval = (long)maxval; + pm_close(ifp); + + /* Figure out the colormap. */ + pm_message("computing colormap..." ); + chv = ppm_computecolorhist(pixels, cols, rows, MAXCOLORS, &colors); + if (chv == (colorhist_vector) 0) + pm_error("too many colors - try doing a 'pnmquant %d'", MAXCOLORS); + pm_message("%d colors found", colors ); + + /* Make a hash table for fast color lookup. */ + cht = ppm_colorhisttocolorhash(chv, colors); + + /* write the header */ + putFill(stdout, HEADER_SIZE); + + /* write picSize and picFrame */ + putShort(stdout, 0); + putRect(stdout, 0, 0, rows, cols); + + /* write version op and version */ + putShort(stdout, PICT_picVersion); + putShort(stdout, 0x02FF); + putShort(stdout, PICT_headerOp); + putLong(stdout, -1L); + putFixed(stdout, 0, 0); + putFixed(stdout, 0, 0); + putFixed(stdout, cols, 0); + putFixed(stdout, rows, 0); + putFill(stdout, 4); + + /* seems to be needed by many PICT2 programs */ + putShort(stdout, PICT_clipRgn); + putShort(stdout, 10); + putRect(stdout, 0, 0, rows, cols); + + /* write picture */ + putShort(stdout, PICT_PackBitsRect); + putShort(stdout, cols | 0x8000); + putRect(stdout, 0, 0, rows, cols); + putShort(stdout, 0); /* pmVersion */ + putShort(stdout, 0); /* packType */ + putLong(stdout, 0L); /* packSize */ + putFixed(stdout, 72, 0); /* hRes */ + putFixed(stdout, 72, 0); /* vRes */ + putShort(stdout, 0); /* pixelType */ + putShort(stdout, 8); /* pixelSize */ + putShort(stdout, 1); /* cmpCount */ + putShort(stdout, 8); /* cmpSize */ + putLong(stdout, 0L); /* planeBytes */ + putLong(stdout, 0L); /* pmTable */ + putLong(stdout, 0L); /* pmReserved */ + putLong(stdout, 0L); /* ctSeed */ + putShort(stdout, 0); /* ctFlags */ + putShort(stdout, colors-1); /* ctSize */ + + /* Write out the colormap. */ + for (i = 0; i < colors; i++) + { + putShort(stdout, i); + rval = PPM_GETR(chv[i].color); + gval = PPM_GETG(chv[i].color); + bval = PPM_GETB(chv[i].color); + if (lmaxval != 65535L) + { + rval = rval * 65535L / lmaxval; + gval = gval * 65535L / lmaxval; + bval = bval * 65535L / lmaxval; + } + putShort(stdout, (short)rval); + putShort(stdout, (short)gval); + putShort(stdout, (short)bval); + } + + putRect(stdout, 0, 0, rows, cols); /* srcRect */ + putRect(stdout, 0, 0, rows, cols); /* dstRect */ + putShort(stdout, 0); /* mode */ + + /* Finally, write out the data. */ + packed = (char*) malloc((unsigned)(cols+cols/MAX_COUNT+1)); + oc = 0; + for (row = 0; row < rows; row++) + oc += putRow(stdout, row, cols, pixels[row], packed); + + /* if we wrote an odd number of pixdata bytes, pad */ + if (oc & 1) + (void) putc(0, stdout); + putShort(stdout, PICT_EndOfPicture); + + lmaxval = ftell(stdout) - HEADER_SIZE; + if (fseek(stdout, (long)HEADER_SIZE, 0) >= 0) + putShort(stdout, (short)(lmaxval & 0xffff)); + + exit(0); +} + +static void +putFill(fd, n) +FILE *fd; +int n; +{ + register int i; + + for (i = 0; i < n; i++) + (void) putc(0, fd); +} + +static void +putShort(fd, i) +FILE *fd; +int i; +{ + (void) putc((i >> 8) & 0xff, fd); + (void) putc(i & 0xff, fd); +} + +#if __STDC__ +static void +putLong( FILE *fd, long i ) +#else /*__STDC__*/ +static void +putLong(fd, i) +FILE *fd; +long i; +#endif /*__STDC__*/ +{ + (void) putc((int)((i >> 24) & 0xff), fd); + (void) putc(((int)(i >> 16) & 0xff), fd); + (void) putc(((int)(i >> 8) & 0xff), fd); + (void) putc((int)(i & 0xff), fd); +} + +static void +putFixed(fd, in, frac) +FILE *fd; +int in, frac; +{ + putShort(fd, in); + putShort(fd, frac); +} + +static void +putRect(fd, x1, x2, y1, y2) +FILE *fd; +int x1, x2, y1, y2; +{ + putShort(fd, x1); + putShort(fd, x2); + putShort(fd, y1); + putShort(fd, y2); +} + +#define RUNLENGTH +#ifdef RUNLENGTH + +#define runtochar(c) (257-(c)) +#define counttochar(c) ((c)-1) + +static int +putRow(fd, row, cols, rowpixels, packed) +FILE *fd; +int row, cols; +pixel *rowpixels; +char *packed; +{ + register int i; + int packcols, count, run, rep, oc; + register pixel *pP; + pixel lastp; + register char *p; + + run = count = 0; + for (cols--, i = cols, pP = rowpixels + cols, p = packed, lastp = *pP; + i >= 0; i--, lastp = *pP, pP--) + { + if (PPM_EQUAL(lastp, *pP)) + run++; + else if (run < RUN_THRESH) + { + while (run > 0) + { + *p++ = ppm_lookupcolor(cht, &lastp); + run--; + count++; + if (count == MAX_COUNT) + { + *p++ = counttochar(MAX_COUNT); + count -= MAX_COUNT; + } + } + run = 1; + } + else + { + if (count > 0) + *p++ = counttochar(count); + count = 0; + while (run > 0) + { + rep = run > MAX_RUN ? MAX_RUN : run; + *p++ = ppm_lookupcolor(cht, &lastp); + *p++ = runtochar(rep); + run -= rep; + } + run = 1; + } + } + if (run < RUN_THRESH) + { + while (run > 0) + { + *p++ = ppm_lookupcolor(cht, &lastp); + run--; + count++; + if (count == MAX_COUNT) + { + *p++ = counttochar(MAX_COUNT); + count -= MAX_COUNT; + } + } + } + else + { + if (count > 0) + *p++ = counttochar(count); + count = 0; + while (run > 0) + { + rep = run > MAX_RUN ? MAX_RUN : run; + *p++ = ppm_lookupcolor(cht, &lastp); + *p++ = runtochar(rep); + run -= rep; + } + run = 1; + } + if (count > 0) + *p++ = counttochar(count); + + packcols = p - packed; /* how many did we write? */ + if (cols > 250) + { + putShort(fd, packcols); + oc = packcols + 2; + } + else + { + (void) putc(packcols, fd); + oc = packcols + 1; + } + + /* now write out the packed row */ + while(p != packed) + { + --p; + (void) putc(*p, fd); + } + + return (oc); +} + +#else /* RUNLENGTH */ + +/* real dumb putRow with no compression */ +static int +putRow(fd, row, cols, rowpixels, packed) +FILE *fd; +int row, cols; +pixel *rowpixels; +char *packed; +{ + register int i, j, bc, oc; + register pixel *pP; + +#if notdef + bzero(aux, cols); /* aux?? */ +#endif /*notdef*/ + bc = cols + (cols + MAX_COUNT - 1) / MAX_COUNT; + if (bc > 250) + { + putShort(fd, bc); + oc = bc + 2; + } + else + { + (void) putc(bc, fd); + oc = bc + 1; + } + for (i = 0, pP = rowpixels; i < cols;) + { + if (cols - i > MAX_COUNT) + { + (void) putc(MAX_COUNT - 1, fd); + for (j = 0; j < MAX_COUNT; j++) + { + (void) putc(ppm_lookupcolor(cht, pP), fd); + pP++; + } + i += MAX_COUNT; + } + else + { + (void) putc(cols - i - 1, fd); + for (j = 0; j < cols - i; j++) + { + (void) putc(ppm_lookupcolor(cht, pP), fd); + pP++; + } + i = cols; + } + } + return (oc); +} +#endif /* RUNLENGTH */ diff --git a/converter/ppm/ppmtopj.c b/converter/ppm/ppmtopj.c new file mode 100644 index 00000000..5d449f7a --- /dev/null +++ b/converter/ppm/ppmtopj.c @@ -0,0 +1,256 @@ +/* ppmtopj.c - convert a portable pixmap to an HP PainJetXL image +** +** Copyright (C) 1990 by Christos Zoulas (christos@ee.cornell.edu) +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include <string.h> + +#include "nstring.h" +#include "ppm.h" + +static int compress_row ARGS((unsigned char *op, unsigned char *oe, unsigned char *cp)); +/* + * XXX: Only 8.5 x 11 paper + */ +#define WIDTH 8.5 +#define HEIGHT 11.0 +#define DPI 180 +#define XPIX ((int) ((DPI * WIDTH + 7) / 8) << 3) +#define YPIX ((int) ((DPI * HEIGHT + 7) / 8) << 3) + +#define C_RESET "\033E" +#define C_RENDER "\033*t%dJ" +# define C_RENDER_NONE 0 +# define C_RENDER_SNAP 1 +# define C_RENDER_BW 2 +# define C_RENDER_DITHER 3 +# define C_RENDER_DIFFUSE 4 +# define C_RENDER_MONODITHER 5 +# define C_RENDER_MONODIFFUSE 6 +# define C_RENDER_MONO_CL_DITHER 5 +# define C_RENDER_MONO_CL_DIFFUSE 6 +#define C_BACK_SCALE "\033*t%dK" +# define C_BACK_SCALE_LIGHT 0 +# define C_BACK_SCALE_DARK 1 +#define C_GAMMA "\033*t%dI" +#define C_IMAGE_WIDTH "\033*r%dS" +#define C_IMAGE_HEIGHT "\033*r%dT" +#define C_DATA_PLANES "\033*r%dU" +#define C_TRANS_MODE "\033*b%dM" +# define C_TRANS_MODE_STD 0 +# define C_TRANS_MODE_RLE 1 +# define C_TRANS_MODE_TIFF 2 +#define C_SEND_PLANE "\033*b%dV" +#define C_LAST_PLANE "\033*b%dW" +#define C_BEGIN_RASTER "\033*r%dA" +# define C_BEGIN_RASTER_MARGIN 0 +# define C_BEGIN_RASTER_ACTIVE 1 +# define C_BEGIN_RASTER_NOSCALE 0 +# define C_BEGIN_RASTER_SCALE 2 +#define C_END_RASTER "\033*r%dC" +# define C_END_RASTER_UNUSED 0 +#define C_RESOLUTION "\033*t%dR" +# define C_RESOLUTION_90DPI 90 +# define C_RESOLUTION_180DPI 180 +#define C_MOVE_X "\033*p+%dX" +#define C_MOVE_Y "\033*p+%dY" + +static const char * const rmode[] = { + "none", "snap", "bw", "dither", "diffuse", + "monodither", "monodiffuse", "clusterdither", + "monoclusterdither", NULL +}; + +/* + * Run-length encoding for the PaintJet. We have pairs of <instances> + * <value>, where instances goes from 0 (meaning one instance) to 255 + * If we are unlucky we can double the size of the image. + */ +static int +compress_row(op, oe, cp) +unsigned char *op, *oe, *cp; +{ + unsigned char *ce = cp; + while ( op < oe ) { + unsigned char px = *op++; + unsigned char *pr = op; + while ( op < oe && *op == px && op - pr < 255) op++; + *ce++ = op - pr; + *ce++ = px; + } + return ce - cp; +} + +int main(argc, argv) +int argc; +char *argv[]; +{ + pixel **pixels; + FILE *ifp; + int argn, rows, cols, r, c, k, p; + pixval maxval; + unsigned char *obuf, *op, *cbuf; + int render_mode = C_RENDER_NONE; + int back_scale = C_BACK_SCALE_DARK; + int gamma = 0; + int mode = C_TRANS_MODE_STD; + int center = 0; + int xoff = 0, yoff = 0; + /* + * XXX: Someday we could make this command line options. + */ + int posscale = C_BEGIN_RASTER_MARGIN | C_BEGIN_RASTER_NOSCALE; + int resolution = C_RESOLUTION_180DPI; + + const char * const usage = "[-center] [-xpos <pos>] [-ypos <pos>] [-gamma <val>] [-back <dark|lite>] [-rle] [-render <none|snap|bw|dither|diffuse|monodither|monodiffuse|clusterdither|monoclusterdither>] [ppmfile]"; + + + ppm_init( &argc, argv ); + + argn = 1; + while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' ) + { + if ( pm_keymatch(argv[argn],"-render",2) && argn + 1 < argc ) + { + ++argn; + for (r = 0; rmode[r] != NULL; r++) + if (STREQ(rmode[r], argv[argn])) + break; + if (rmode[r] != NULL) + render_mode = r; + else + pm_usage(usage); + } + else if ( pm_keymatch(argv[argn],"-back",2) && argn + 1 < argc ) + { + ++argn; + if (STREQ(argv[argn], "dark")) + back_scale = C_BACK_SCALE_DARK; + else if (STREQ(argv[argn], "lite")) + back_scale = C_BACK_SCALE_LIGHT; + else + pm_usage(usage); + } + else if ( pm_keymatch(argv[argn],"-gamma",2) && argn + 1 < argc ) + { + ++argn; + if ( sscanf( argv[argn], "%d",&gamma ) != 1 ) + pm_usage( usage ); + } + else if ( pm_keymatch(argv[argn],"-xpos",2) && argn + 1 < argc ) + { + ++argn; + if ( sscanf( argv[argn], "%d",&xoff ) != 1 ) + pm_usage( usage ); + } + else if ( pm_keymatch(argv[argn],"-ypos",2) && argn + 1 < argc ) + { + ++argn; + if ( sscanf( argv[argn], "%d",&yoff ) != 1 ) + pm_usage( usage ); + } + else if (pm_keymatch(argv[argn],"-rle",2)) + mode = C_TRANS_MODE_RLE; + else if (pm_keymatch(argv[argn],"-center",2)) + center = 1; + else + pm_usage( usage ); + ++argn; + } + + if ( argn < argc ) + { + ifp = pm_openr( argv[argn] ); + ++argn; + } + else + ifp = stdin; + + if ( argn != argc ) + pm_usage( usage ); + + pixels = ppm_readppm( ifp, &cols, &rows, &maxval ); + + pm_close( ifp ); + obuf = (unsigned char *) pm_allocrow(cols, sizeof(unsigned char)); + cbuf = (unsigned char *) pm_allocrow(cols * 2, sizeof(unsigned char)); + + if (cols > XPIX || rows > YPIX) + pm_message("image too large for page"); + if (center) { + if (xoff || yoff) + pm_error("cannot specify both center and position"); + xoff = (XPIX - cols) / 2; + yoff = (YPIX - rows) / 2; + } + + (void) printf(C_RESET); + /* + * Set the resolution before begin raster otherwise it + * does not work. + */ + (void) printf(C_RESOLUTION, resolution); + (void) printf(C_BEGIN_RASTER, posscale); + if (xoff) + (void) printf(C_MOVE_X, xoff); + if (yoff) + (void) printf(C_MOVE_Y, yoff); + (void) printf(C_TRANS_MODE, mode); + (void) printf(C_RENDER, render_mode); + (void) printf(C_BACK_SCALE, back_scale); + (void) printf(C_GAMMA, gamma); + (void) printf(C_IMAGE_WIDTH, cols); + (void) printf(C_IMAGE_HEIGHT, rows); + (void) printf(C_DATA_PLANES, 3); + + for (r = 0; r < rows; r++) + /* for each primary */ + for (p = 0; p < 3; p++) { + switch (p) { + case 0: + for (c = 0, op = &obuf[-1]; c < cols; c++) { + if ((k = (c & 7)) == 0) + *++op = 0; + if (PPM_GETR(pixels[r][c]) > maxval / 2) + *op |= 1 << (7 - k); + } + break; + case 1: + for (c = 0, op = &obuf[-1]; c < cols; c++) { + if ((k = (c & 7)) == 0) + *++op = 0; + if (PPM_GETG(pixels[r][c]) > maxval / 2) + *op |= 1 << (7 - k); + } + break; + case 2: + for (c = 0, op = &obuf[-1]; c < cols; c++) { + if ((k = (c & 7)) == 0) + *++op = 0; + if (PPM_GETB(pixels[r][c]) > maxval / 2) + *op |= 1 << (7 - k); + } + break; + } + ++op; + if (mode == C_TRANS_MODE_RLE) { + k = compress_row(obuf, op, cbuf); + op = cbuf; + } + else { + k = op - obuf; + op = obuf; + } + (void) printf(p == 2 ? C_LAST_PLANE : C_SEND_PLANE, k); + (void) fwrite(op, 1, k, stdout); + } + (void) printf(C_END_RASTER, C_END_RASTER_UNUSED); + exit(0); +} diff --git a/converter/ppm/ppmtopjxl.c b/converter/ppm/ppmtopjxl.c new file mode 100644 index 00000000..72d299fd --- /dev/null +++ b/converter/ppm/ppmtopjxl.c @@ -0,0 +1,423 @@ +/* ppmtopcl.c - convert portable pixmap into PCL language for HP PaintJet and + * PaintJet XL color printers + * AJCD 12/3/91 + * + * usage: + * ppmtopcl [-nopack] [-gamma <n>] [-presentation] [-dark] + * [-diffuse] [-cluster] [-dither] + * [-xshift <s>] [-yshift <s>] + * [-xshift <s>] [-yshift <s>] + * [-xsize|-width|-xscale <s>] [-ysize|-height|-yscale <s>] + * [ppmfile] + * + */ + +#include <stdio.h> +#include <math.h> +#include <string.h> + +#include "pm_c_util.h" +#include "ppm.h" +#include "nstring.h" + +#define MAXCOLORS 1024 + +const char * const usage="[-nopack] [-gamma <n>] [-presentation] [-dark]\n\ + [-diffuse] [-cluster] [-dither]\n\ + [-xshift <s>] [-yshift <s>]\n\ + [-xshift <s>] [-yshift <s>]\n\ + [-xsize|-width|-xscale <s>] [-ysize|-height|-yscale <s>]\n\ + [ppmfile]"; + +#define PCL_MAXWIDTH 2048 +#define PCL_MAXHEIGHT 32767 +#define PCL_MAXVAL 255 + +static int nopack = 0; +static int dark = 0; +static int diffuse = 0; +static int dither = 0; +static int cluster = 0; +static int xsize = 0; +static int ysize = 0; +static int xshift = 0; +static int yshift = 0; +static int quality = 0; +static double xscale = 0.0; +static double yscale = 0.0; +static double gamma_val = 0.0; + +/* argument types */ +#define DIM 0 +#define REAL 1 +#define BOOL 2 +static const struct options { + const char *name; + int type; + void *value; +} options[] = { + {"-gamma", REAL, &gamma_val }, + {"-presentation", BOOL, &quality }, + {"-width", DIM, &xsize }, + {"-xsize", DIM, &xsize }, + {"-height", DIM, &ysize }, + {"-ysize", DIM, &ysize }, + {"-xscale", REAL, &xscale }, + {"-yscale", REAL, &yscale }, + {"-xshift", DIM, &xshift }, + {"-yshift", DIM, &yshift }, + {"-dark", BOOL, &dark }, + {"-diffuse", BOOL, &diffuse }, + {"-dither", BOOL, &dither }, + {"-cluster", BOOL, &cluster }, + {"-nopack", BOOL, &nopack }, +}; + +#define putword(w) (putchar(((w)>>8) & 255), putchar((w) & 255)) + +static int +bitsperpixel(int v) { + int bpp = 0; + while (v > 0) { /* calculate # bits for value */ + ++bpp; + v>>=1; + } + return (bpp); +} + + + +static char *inrow = NULL; +static char *outrow = NULL; +/* "signed" was commented out below, but it caused warnings on an SGI + compiler, which defaulted to unsigned character. 2001.03.30 BJH */ +static signed char *runcnt = NULL; + +static void +putbits(b, n) { + /* put #n bits in b out, packing into bytes; n=0 flushes bits */ + /* n should never be > 8 */ + + static int out = 0; + static int cnt = 0; + static int num = 0; + static int pack = 0; + if (n) { + int xo = 0; + int xc = 0; + if (cnt+n > 8) { /* overflowing current byte? */ + xc = cnt + n - 8; + xo = (b & ~(-1 << xc)) << (8-xc); + n -= xc; + b >>= xc; + } + cnt += n; + out |= (b & ~(-1 << n)) << (8-cnt); + if (cnt >= 8) { + inrow[num++] = out; + out = xo; + cnt = xc; + } + } else { /* flush row */ + int i; + if (cnt) { + inrow[num++] = out; + out = cnt = 0; + } + for (; num > 0 && inrow[num-1] == 0; num--); /* remove trailing zeros */ + printf("\033*b"); + if (num && !nopack) { /* TIFF 4.0 packbits encoding */ + int start = 0; + int next; + runcnt[start] = 0; + for (i = 1; i < num; i++) { + if (inrow[i] == inrow[i-1]) { + if (runcnt[start] <= 0 && runcnt[start] > -127) + runcnt[start]--; + else + runcnt[start = i] = 0; + } else { + if (runcnt[start] >= 0 && runcnt[start] < 127) + runcnt[start]++; + else + runcnt[start = i] = 0; + } + } + start = 0; + for (i = 0; i < num; i = next) { + int count = runcnt[i]; + int from = i; + if (count >= 0) { /* merge two-byte runs */ + for (;;) { + next = i+1+runcnt[i]; + if(next >= num || runcnt[next] < 0 || + count+runcnt[next]+1 > 127) + break; + count += runcnt[next]+1; + i = next; + } + } + next = i + 1 + ((runcnt[i] < 0) ? -runcnt[i] : runcnt[i]); + if (next < num && count > 0 && + runcnt[next] < 0 && runcnt[next] > -127) { + count--; + next--; + runcnt[next] = runcnt[next+1]-1; + } + outrow[start++] = count; + if (count >= 0) { + while (count-- >= 0) + outrow[start++] = inrow[from++]; + } else + outrow[start++] = inrow[from]; + } + if (start < num) { + num = start; + if (!pack) { + printf("2m"); + pack = 1; + } + } else { + if (pack) { + printf("0m"); + pack = 0; + } + } + } + printf("%dW", num); + for (i = 0; i < num; i++) + putchar(pack ? outrow[i] : inrow[i]); + num = 0; /* new row */ + } +} + +int +main(argc, argv) + int argc; + char *argv[]; +{ + FILE *ifd; + register pixel **pixels, *pixrow; + register int row, col, bpp, i; + int rows, cols; + pixval maxval; + int bpr, bpg, bpb; + int render; + int colors, pclindex; + colorhist_vector chv; + colorhash_table cht; + + ppm_init( &argc, argv ); + + while (argc > 1 && argv[1][0] == '-') { + char *c; + for (i = 0; i < sizeof(options)/sizeof(struct options); i++) { + if (pm_keymatch(argv[1], options[i].name, + MIN(strlen(argv[1]), strlen(options[i].name)))) { + switch (options[i].type) { + case DIM: + if (++argv, --argc == 1) + pm_usage(usage); + for (c = argv[1]; ISDIGIT(*c); c++); + if (c[0] == 'p' && c[1] == 't') /* points */ + *(int *)(options[i].value) = atoi(argv[1])*10; + else if (c[0] == 'd' && c[1] == 'p') /* decipoints */ + *(int *)(options[i].value) = atoi(argv[1]); + else if (c[0] == 'i' && c[1] == 'n') /* inches */ + *(int *)(options[i].value) = atoi(argv[1])*720; + else if (c[0] == 'c' && c[1] == 'm') /* centimetres */ + *(int *)(options[i].value) = atoi(argv[1])*283.46457; + else if (!c[0]) /* dots */ + *(int *)(options[i].value) = atoi(argv[1])*4; + else + pm_error("illegal unit of measure %s", c); + break; + case REAL: + if (++argv, --argc == 1) + pm_usage(usage); + *(double *)(options[i].value) = atof(argv[1]); + break; + case BOOL: + *(int *)(options[i].value) = 1; + break; + } + break; + } + } + if (i >= sizeof(options)/sizeof(struct options)) + pm_usage(usage); + argv++; argc--; + } + if (argc > 2) + pm_usage(usage); + else if (argc == 2) + ifd = pm_openr(argv[1]); + else + ifd = stdin ; + + /* validate arguments */ + if (diffuse+cluster+dither > 1) + pm_error("only one of -diffuse, -dither and -cluster may be used"); + render = diffuse ? 4 : dither ? 3 : cluster ? 7 : 0; + + if (xsize != 0.0 && xscale != 0.0) + pm_error("only one of -xsize and -xscale may be used"); + + if (ysize != 0.0 && yscale != 0.0) + pm_error("only one of -ysize and -yscale may be used"); + + pixels = ppm_readppm( ifd, &cols, &rows, &maxval ); + pm_close( ifd ); + + /* limit checks */ + if (cols > PCL_MAXWIDTH || rows > PCL_MAXHEIGHT) + pm_error("image too large; reduce with ppmscale"); + if (maxval > PCL_MAXVAL) + pm_error("color range too large; reduce with ppmcscale"); + + /* Figure out the colormap. */ + fprintf( stderr, "(Computing colormap..." ); fflush( stderr ); + chv = ppm_computecolorhist( pixels, cols, rows, MAXCOLORS, &colors ); + if ( chv == (colorhist_vector) 0 ) + pm_error("too many colors; reduce with pnmquant"); + fprintf( stderr, " Done. %d colors found.)\n", colors ); + + /* And make a hash table for fast lookup. */ + cht = ppm_colorhisttocolorhash( chv, colors ); + + /* work out color downloading mode */ + pclindex = bitsperpixel(colors); + if (pclindex > 8) /* can't use indexed mode */ + pclindex = 0; + else + switch (pclindex) { /* round up to 1,2,4,8 */ + case 0: /* direct mode (no palette) */ + bpp = bitsperpixel(maxval); /* bits per pixel */ + bpg = bpp; bpb = bpp; + bpp = (bpp*3+7)>>3; /* bytes per pixel now */ + bpr = (bpp<<3)-bpg-bpb; + bpp *= cols; /* bytes per row now */ + break; + case 5: pclindex++; + case 6: pclindex++; + case 3: case 7: pclindex++; + default: + bpp = 8/pclindex; + bpp = (cols+bpp-1)/bpp; /* bytes per row */ + } + + if ((inrow = (char *)malloc((unsigned)bpp)) == NULL || + (outrow = (char *)malloc((unsigned)bpp*2)) == NULL || + (runcnt = (signed char *)malloc((unsigned)bpp)) == NULL) + pm_error("can't allocate space for row"); + + /* set up image details */ + if (xscale != 0.0) + xsize = cols * xscale * 4; + if (yscale != 0.0) + ysize = rows * yscale * 4; + +#ifdef DEBUG + fprintf(stderr, "dark =%d\n", dark); + fprintf(stderr, "diffuse =%d\n", diffuse); + fprintf(stderr, "dither =%d\n", dither); + fprintf(stderr, "cluster =%d\n", cluster); + fprintf(stderr, "quality =%d\n", quality); + fprintf(stderr, "xsize =%d\n", xsize); + fprintf(stderr, "ysize =%d\n", ysize); + fprintf(stderr, "xshift =%d\n", xshift); + fprintf(stderr, "yshift =%d\n", yshift); + fprintf(stderr, "xscale =%lf\n", xscale); + fprintf(stderr, "yscale =%lf\n", yscale); + fprintf(stderr, "gamma =%lf\n", gamma_val); + fprintf(stderr, "pclindex =%d\n", pclindex); + fprintf(stderr, "nopack =%d\n", nopack); +#endif + + /* write PCL header */ +#if 0 + printf("\033&l26A"); /* paper size */ +#endif + printf("\033*r%ds%dT", cols, rows); /* source width, height */ + if (xshift != 0 || yshift != 0) + printf("\033&a%+dh%+dV", xshift, yshift); /* xshift, yshift */ + if (quality) + printf("\033*o%dQ", quality); /* print quality */ + printf("\033*t"); + if (xsize == 0 && ysize == 0) + printf("180r"); /* resolution */ + else { /* destination width, height */ + if (xsize != 0) + printf("%dh", xsize); + if (ysize != 0) + printf("%dv", ysize); + } + if (gamma_val != 0) + printf("%.3fi", gamma_val); /* gamma correction */ + if (dark) + printf("%dk", dark); /* scaling algorithms */ + printf("%dJ", render); /* rendering algorithm */ + printf("\033*v18W"); /* configure image data */ + putchar(0); /* relative colors */ + putchar(pclindex ? 1 : 3); /* index/direct pixel mode */ + putchar(pclindex); /* ignored in direct pixel mode */ + if (pclindex) { + putchar(0); + putchar(0); + putchar(0); + } else { + putchar(bpr); /* bits per red */ + putchar(bpg); /* bits per green */ + putchar(bpb); /* bits per blue */ + } + putword(maxval); /* max red reference */ + putword(maxval); /* max green reference */ + putword(maxval); /* max blue reference */ + putword(0); /* min red reference */ + putword(0); /* min green reference */ + putword(0); /* min blue reference */ + if (pclindex) { /* set palette */ + for (i = 0; i < colors; i++) { + int r, g, b; + r = PPM_GETR( chv[i].color ); + g = PPM_GETG( chv[i].color ); + b = PPM_GETB( chv[i].color ); + if (i == 0) + printf("\033*v"); + if (r) + printf("%da", r); + if (g) + printf("%db", g); + if (b) + printf("%dc", b); + if (i == colors-1) + printf("%dI", i); /* assign color index */ + else + printf("%di", i); /* assign color index */ + } + } + ppm_freecolorhist( chv ); + + /* start raster graphics at CAP */ + printf("\033*r%dA", (xsize != 0 || ysize != 0) ? 3 : 1); + + for (row = 0; row < rows; row++) { + if (pclindex) { /* indexed color mode */ + int out, cnt; + out = cnt = 0; + for (col = 0, pixrow=pixels[row]; col < cols; col++, pixrow++) { + putbits(ppm_lookupcolor( cht, pixrow ), pclindex); + } + putbits(0, 0); /* flush row */ + } else { /* direct color mode */ + for (col = 0, pixrow=pixels[row]; col < cols; col++, pixrow++) { + putbits(PPM_GETR( *pixrow ), bpr); + putbits(PPM_GETG( *pixrow ), bpg); + putbits(PPM_GETB( *pixrow ), bpb); /* don't need to flush */ + } + putbits(0, 0); /* flush row */ + } + } + printf("\033*rC"); /* end raster graphics */ + exit(0); +} diff --git a/converter/ppm/ppmtoppm.c b/converter/ppm/ppmtoppm.c new file mode 100644 index 00000000..500c9856 --- /dev/null +++ b/converter/ppm/ppmtoppm.c @@ -0,0 +1,44 @@ +/*---------------------------------------------------------------------------- + ppmtoppm +------------------------------------------------------------------------------ + Part of the Netpbm package. + + Copy PPM image from Standard Input to Standard Output + + + By Bryan Henderson, San Jose CA 2002.09.07 + + Contributed to the public domain by its author 2002.09.07 +-----------------------------------------------------------------------------*/ + +#include "ppm.h" + +int +main(int argc, char *argv[]) { + int format; + int rows, cols; + pixval maxval; + int row; + pixel* pixelrow; + + ppm_init(&argc, argv); + + if (argc-1 != 0) + pm_error("Program takes no arguments. Input is from Standard Input"); + + ppm_readppminit(stdin, &cols, &rows, &maxval, &format); + + ppm_writeppminit(stdout, cols, rows, maxval, 0); + + pixelrow = ppm_allocrow(cols); + + for (row = 0; row < rows; row++) { + ppm_readppmrow(stdin, pixelrow, cols, maxval, format); + ppm_writeppmrow(stdout, pixelrow, cols, maxval, 0); + } + ppm_freerow(pixelrow); + + pm_close(stdin); + + exit(0); +} diff --git a/converter/ppm/ppmtopuzz.c b/converter/ppm/ppmtopuzz.c new file mode 100644 index 00000000..1277cc20 --- /dev/null +++ b/converter/ppm/ppmtopuzz.c @@ -0,0 +1,96 @@ +/* ppmtopuzz.c - read a portable pixmap and write an X11 "puzzle" file +** +** Copyright (C) 1991 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "ppm.h" + +#define MAXVAL 255 +#define MAXCOLORS 256 + +int +main( argc, argv ) + int argc; + char* argv[]; + { + FILE* ifp; + pixel** pixels; + register pixel* pP; + colorhist_vector chv; + colorhash_table cht; + int rows, cols, row, colors, i; + register int col; + pixval maxval; + + + ppm_init( &argc, argv ); + + if ( argc > 2 ) + pm_usage( "[ppmfile]" ); + + if ( argc == 2 ) + ifp = pm_openr( argv[1] ); + else + ifp = stdin; + + pixels = ppm_readppm( ifp, &cols, &rows, &maxval ); + pm_close( ifp ); + + pm_message( "computing colormap..." ); + chv = ppm_computecolorhist( pixels, cols, rows, MAXCOLORS, &colors ); + if ( chv == (colorhist_vector) 0 ) + { + pm_message( + "too many colors - try doing a 'pnmquant %d'", MAXCOLORS ); + exit( 1 ); + } + pm_message( "%d colors found", colors ); + + /* Write puzzle header. */ + (void) pm_writebiglong( stdout, cols ); + (void) pm_writebiglong( stdout, rows ); + (void) putchar( (unsigned char) colors ); + if ( maxval > MAXVAL ) + pm_message( + "maxval is not %d - automatically rescaling colors", MAXVAL ); + for ( i = 0; i < colors; ++i ) + { + pixel p; + + p = chv[i].color; + if ( maxval != MAXVAL ) + PPM_DEPTH( p, p, maxval, MAXVAL ); + (void) putchar( (unsigned char) PPM_GETR( p ) ); + (void) putchar( (unsigned char) PPM_GETG( p ) ); + (void) putchar( (unsigned char) PPM_GETB( p ) ); + } + + /* Convert color vector to color hash table, for fast lookup. */ + cht = ppm_colorhisttocolorhash( chv, colors ); + ppm_freecolorhist( chv ); + + /* And write out the data. */ + for ( row = 0; row < rows; ++row ) + { + for ( col = 0, pP = pixels[row]; col < cols; ++col, ++pP ) + { + register int color; + + color = ppm_lookupcolor( cht, pP ); + if ( color == -1 ) + pm_error( + "color not found?!? row=%d col=%d r=%d g=%d b=%d", + row, col, PPM_GETR(*pP), PPM_GETG(*pP), PPM_GETB(*pP) ); + (void) putchar( (unsigned char) color ); + } + } + + exit( 0 ); + } diff --git a/converter/ppm/ppmtorgb3.c b/converter/ppm/ppmtorgb3.c new file mode 100644 index 00000000..21d3346a --- /dev/null +++ b/converter/ppm/ppmtorgb3.c @@ -0,0 +1,99 @@ +/* ppmtorgb3.c - separate a portable pixmap into three portable graymaps +** +** Copyright (C) 1991 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include <string.h> +#include "ppm.h" +#include "pgm.h" + +int +main( argc, argv ) + int argc; + char* argv[]; + { + FILE* ifp; + FILE* redfile; + FILE* grnfile; + FILE* blufile; + const char* basename; + char filename[100]; + char* cp; + pixel* pixelrow; + register pixel* pP; + gray* grayrow; + register gray* gP; + int rows, cols, format, row; + register int col; + pixval maxval; + + + ppm_init( &argc, argv ); + + if ( argc > 2 ) + pm_usage( "[ppmfile]" ); + + if ( argc == 2 ) + { + ifp = pm_openr( argv[1] ); + basename = argv[1]; + cp = strrchr( basename, '.' ); + if ( cp != NULL ) + *cp = '\0'; + } + else + { + ifp = stdin; + basename = "noname"; + } + + ppm_readppminit( ifp, &cols, &rows, &maxval, &format ); + pixelrow = ppm_allocrow( cols ); + (void) strcpy( filename, basename ); + (void) strcat( filename, ".red" ); + redfile = pm_openw( filename ); + pgm_writepgminit( redfile, cols, rows, (gray) maxval, 0 ); + (void) strcpy( filename, basename ); + (void) strcat( filename, ".grn" ); + grnfile = pm_openw( filename ); + pgm_writepgminit( grnfile, cols, rows, (gray) maxval, 0 ); + (void) strcpy( filename, basename ); + (void) strcat( filename, ".blu" ); + blufile = pm_openw( filename ); + pgm_writepgminit( blufile, cols, rows, (gray) maxval, 0 ); + grayrow = pgm_allocrow( cols ); + + for ( row = 0; row < rows; ++row ) + { + ppm_readppmrow( ifp, pixelrow, cols, maxval, format ); + + for ( col = 0, pP = pixelrow, gP = grayrow; col < cols; + ++col, ++pP, ++gP ) + *gP = (gray) PPM_GETR( *pP ); + pgm_writepgmrow( redfile, grayrow, cols, maxval, 0 ); + + for ( col = 0, pP = pixelrow, gP = grayrow; col < cols; + ++col, ++pP, ++gP ) + *gP = (gray) PPM_GETG( *pP ); + pgm_writepgmrow( grnfile, grayrow, cols, maxval, 0 ); + + for ( col = 0, pP = pixelrow, gP = grayrow; col < cols; + ++col, ++pP, ++gP ) + *gP = (gray) PPM_GETB( *pP ); + pgm_writepgmrow( blufile, grayrow, cols, maxval, 0 ); + } + + pm_close( ifp ); + pm_close( redfile ); + pm_close( blufile ); + pm_close( grnfile ); + + exit( 0 ); + } diff --git a/converter/ppm/ppmtosixel.c b/converter/ppm/ppmtosixel.c new file mode 100644 index 00000000..5f361e45 --- /dev/null +++ b/converter/ppm/ppmtosixel.c @@ -0,0 +1,215 @@ +/* ppmtosix.c - read a portable pixmap and produce a color sixel file +** +** Copyright (C) 1991 by Rick Vinci. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "ppm.h" + +static void WriteHeader ARGS((void)); +static void WriteColorMap + ARGS((colorhist_vector chv, int colors, pixval maxval)); +static void WriteRawImage ARGS((colorhash_table cht, int rows, int cols)); +static void WritePackedImage ARGS((colorhash_table cht, int rows, int cols)); +static void WriteEnd ARGS((void)); +#define MAXVAL 100 +#define MAXCOLORS 256 + +#define DCS '\220' /* Device Control String */ +#define ST '\234' /* String Terminator */ +#define CSI '\233' /* Control String Introducer */ +#define ESC '\033' /* Escape character */ + +static pixel** pixels; /* stored ppm pixmap input */ +static colorhash_table cht; +int margin; + + + +int +main( argc, argv ) + int argc; + char* argv[]; +{ + FILE* ifp; + int argn, rows, cols, colors; + int raw; + pixval maxval; + colorhist_vector chv; + const char* const usage = "[-raw] [-margin] [ppmfile]"; + + + ppm_init( &argc, argv ); + + argn = 1; + raw = 0; + margin = 0; + + /* Parse args. */ + while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' ) + { + if ( pm_keymatch( argv[argn], "-raw", 2 ) ) + raw = 1; + else if ( pm_keymatch( argv[argn], "-margin", 2 ) ) + margin = 1; + else + pm_usage( usage ); + ++argn; + } + + if ( argn < argc ) + { + ifp = pm_openr( argv[argn] ); + ++argn; + } + else + ifp = stdin; + + if ( argn != argc ) + pm_usage( usage ); + + /* Read in the whole ppmfile. */ + pixels = ppm_readppm( ifp, &cols, &rows, &maxval ); + pm_close( ifp ); + + /* Print a warning if we could lose accuracy when rescaling colors. */ + if ( maxval > MAXVAL ) + pm_message( + "maxval is not %d - automatically rescaling colors", MAXVAL ); + + /* Figure out the colormap. */ + pm_message( "computing colormap..." ); + chv = ppm_computecolorhist( pixels, cols, rows, MAXCOLORS, &colors ); + if ( chv == (colorhist_vector) 0 ) + pm_error( "too many colors - try doing a 'pnmquant %d'", MAXCOLORS ); + pm_message( "%d colors found", colors ); + + /* Make a hash table for fast color lookup. */ + cht = ppm_colorhisttocolorhash( chv, colors ); + + WriteHeader(); + WriteColorMap( chv, colors, maxval ); + if ( raw == 1 ) + WriteRawImage( cht, rows, cols ); + else + WritePackedImage( cht, rows, cols ); + WriteEnd(); + + /* If the program failed, it previously aborted with nonzero completion + code, via various function calls. + */ + return 0; +} + + + +static void +WriteHeader() + { + if ( margin == 1 ) + printf( "%c%d;%ds", CSI, 14, 72 ); + printf( "%c", DCS ); /* start with Device Control String */ + printf( "0;0;8q" ); /* Horizontal Grid Size at 1/90" and graphics On */ + printf( "\"1;1\n" ); /* set aspect ratio 1:1 */ + } + + + +static void +WriteColorMap( colorhist_vector chv, int colors, pixval maxval ) + { + register int colornum; + pixel p; + + for ( colornum = 0; colornum < colors ; ++colornum ) + { + p = chv[colornum].color; + if ( maxval != MAXVAL ) + PPM_DEPTH( p, p, maxval, MAXVAL ); + printf( "#%d;2;%d;%d;%d", colornum, + (int) PPM_GETR( p ), (int) PPM_GETG( p ), (int) PPM_GETB( p ) ); + } + printf( "\n" ); + } + + + +static void +WriteRawImage( cht, rows, cols ) + colorhash_table cht; + int rows, cols; + { + int rownum, colnum, b; + const char* sixel = "@ACGO_"; + register pixel* pP; + + for ( rownum = 0; rownum < rows; ++rownum ) + { + b = rownum % 6; + for ( colnum = 0, pP = pixels[rownum]; colnum < cols; ++colnum, ++pP ) + printf( "#%d%c", ppm_lookupcolor(cht, pP), sixel[b] ); + printf( "$\n" ); /* Carriage Return */ + if ( b == 5 ) + printf( "-\n" ); /* Line Feed (one sixel height) */ + } + } + + + +static void +WritePackedImage( cht, rows, cols ) + colorhash_table cht; + int rows, cols; + { + int rownum, colnum, b, repeat, thiscolor, nextcolor; + const char* sixel = "@ACGO_"; + register pixel* pP; + + for ( rownum = 0; rownum < rows; ++rownum ) + { + b = rownum % 6; + repeat = 1; + for ( colnum = 0, pP = pixels[rownum]; colnum < cols; ++colnum, ++pP ) + { + thiscolor = ppm_lookupcolor(cht, pP); + if ( colnum == cols -1 ) /* last pixel in row */ + if ( repeat == 1 ) + printf( "#%d%c", thiscolor, sixel[b] ); + else + printf( "#%d!%d%c", thiscolor, repeat, sixel[b] ); + else /* not last pixel in row */ + { + nextcolor = ppm_lookupcolor(cht, pP+1); + if ( thiscolor == nextcolor ) + ++repeat; + else + if ( repeat == 1 ) + printf( "#%d%c", thiscolor, sixel[b] ); + else + { + printf( "#%d!%d%c", thiscolor, repeat, sixel[b] ); + repeat = 1; + } + } + } /* end column loop */ + printf( "$\n" ); /* Carriage Return */ + if ( b == 5 ) + printf( "-\n" ); /* Line Feed (one sixel height) */ + } + } + + + +static void +WriteEnd() + { + if ( margin == 1 ) + printf ( "%c%d;%ds", CSI, 1, 80 ); + printf( "%c\n", ST ); + } diff --git a/converter/ppm/ppmtoterm.c b/converter/ppm/ppmtoterm.c new file mode 100644 index 00000000..72105705 --- /dev/null +++ b/converter/ppm/ppmtoterm.c @@ -0,0 +1,173 @@ +/* ppmtoterm.c - convert a portable pixmap into an ISO 6429 (ANSI) color ascii +** text. +** +** Copyright (C) 2002 by Ero Carrera (ecarrera@lightforge.com) +** Partially based on, +** ppmtoyuv.c by Marc Boucher, +** ppmtolj.c by Jonathan Melvin and +** ppmtogif.c by Jef Poskanzer +** +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +** +** 14/Aug/2002: First version. +** +*/ + +#include <string.h> + +#include "ppm.h" +#include "shhopt.h" + + +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char *inputFilespec; /* Filespec of input file */ + unsigned int verbose; +}; + + + +static void +parseCommandLine(int argc, char **argv, + struct cmdlineInfo *cmdlineP) { + optEntry *option_def = malloc(100*sizeof(optEntry)); + /* Instructions to OptParseOptions3 on how to parse our options */ + optStruct3 opt; + + unsigned int option_def_index; + + option_def_index = 0; /* incremented by OPTENTRY */ + OPTENT3(0, "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 0); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ + + optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + switch (argc-1) { + case 0: + cmdlineP->inputFilespec = "-"; + break; + case 1: + cmdlineP->inputFilespec = argv[1]; + break; + case 2: + break; + } +} + + +#define ESC "\x1B\x5B" +#define NUM_COLORS 128 +#define MAX_ANSI_STR_LEN 16 + + +static int __inline__ sqr(const int x) { + return x*x; +} + +/* + Generates some sort of color palette mixing the available + colors as different values of background, foreground & brightness. +*/ +static int +generate_palette(unsigned char rgb[NUM_COLORS][3], + char ansi_code[NUM_COLORS][MAX_ANSI_STR_LEN]) { + int code, col=0, cd2=0; + + memset((void *)rgb, 0, NUM_COLORS*3); + memset((void *)ansi_code, 0, NUM_COLORS*MAX_ANSI_STR_LEN); + + for(col=cd2=0; cd2<8; cd2++) { + unsigned int b; + for(b=0;b<2;b++) { + for(code=0; code<8; code++) { + unsigned int c; + for(c=0;c<3;c++) { + if(code&(1<<c)) { + rgb[col][c]=(192|(b?63:0)); + } + if(cd2&(1<<c)) { + rgb[col][c]|=(128); + } + } + sprintf(ansi_code[col], + ESC"%dm"ESC"3%dm"ESC"4%dm", + b, code, cd2); + col++; + } + } + } + return col; +} + + + +int main(int argc, char **argv) +{ + FILE *ifp; + pixel **pixels; + int rows, row, cols, col, + pal_len, i; + pixval maxval; + struct cmdlineInfo + cmdline; + unsigned char rgb[NUM_COLORS][3]; + char ansi_code[NUM_COLORS][MAX_ANSI_STR_LEN]; + + + ppm_init(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + ifp = pm_openr(cmdline.inputFilespec); + + pixels = ppm_readppm(ifp, &cols, &rows, &maxval); + + pm_close(ifp); + + pal_len=generate_palette(rgb, ansi_code); + + for (row = 0; row < rows; ++row) { + for (col = 0; col < cols; col++) { + pixval const r=(int)PPM_GETR(pixels[row][col])*255/maxval; + pixval const g=(int)PPM_GETG(pixels[row][col])*255/maxval; + pixval const b=(int)PPM_GETB(pixels[row][col])*255/maxval; + int val, dist; + + /* + The following loop calculates the index that + corresponds to the minimum color distance + between the given RGB values and the values + available in the palette. + */ + for(i=0, dist=sqr(255)*3, val=0; i<pal_len; i++) { + pixval const pr=rgb[i][0]; + pixval const pg=rgb[i][1]; + pixval const pb=rgb[i][2]; + unsigned int j; + if( (j=sqr(r-pr)+sqr(b-pb)+sqr(g-pg))<dist ) { + dist=j; + val=i; + } + } + printf("%s%c", ansi_code[val],0xB1); + } + printf(ESC"\x30m\n"); + } + printf(ESC"\x30m"); + + ppm_freearray(pixels, rows); + + exit(0); +} diff --git a/converter/ppm/ppmtowinicon.c b/converter/ppm/ppmtowinicon.c new file mode 100644 index 00000000..bcaa0e08 --- /dev/null +++ b/converter/ppm/ppmtowinicon.c @@ -0,0 +1,881 @@ +/* ppmtowinicon.c - read portable pixmap file(s) and write a MS Windows .ico +** +** Copyright (C) 2000 by Lee Benfield - lee@benf.org +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include <math.h> +#include <string.h> + +#include "winico.h" +#include "ppm.h" +#include "mallocvar.h" +#include "shhopt.h" +#include "nstring.h" + +#define MAJVERSION 0 +#define MINVERSION 3 + +#define MAXCOLORS 256 + +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + unsigned int iconCount; + const char **inputFilespec; /* '-' if stdin; malloc'ed array */ + const char **andpgmFilespec; /* NULL if unspecified; malloc'ed array */ + const char *output; /* '-' if unspecified */ + unsigned int truetransparent; + unsigned int verbose; +}; + + +static bool verbose; + +static int file_offset = 0; /* not actually used, but useful for debug. */ +static FILE * ofp; + +static void +parseCommandLine(int argc, + char ** argv, + struct cmdlineInfo *cmdlineP ) { +/*---------------------------------------------------------------------------- + Parse program command line described in Unix standard form by argc + and argv. Return the information in the options as *cmdlineP. + + If command line is internally inconsistent (invalid options, etc.), + issue error message to stderr and abort program. + + Note that the strings we return are stored in the storage that + was passed to us as the argv array. We also trash *argv. +-----------------------------------------------------------------------------*/ + optEntry *option_def; + /* Instructions to optParseOptions3 on how to parse our options. + */ + optStruct3 opt; + + unsigned int option_def_index; + + unsigned int outputSpec, andpgms; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "output", OPT_STRING, &cmdlineP->output, + &outputSpec, 0); + OPTENT3(0, "andpgms", OPT_FLAG, NULL, + &andpgms, 0); + OPTENT3(0, "truetransparent", OPT_FLAG, NULL, + &cmdlineP->truetransparent, 0); + OPTENT3(0, "verbose", OPT_STRING, NULL, + &cmdlineP->verbose, 0); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ + + optParseOptions3( &argc, argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (!outputSpec) + cmdlineP->output = "-"; + + + if (!andpgms) { + if (argc-1 == 0) { + cmdlineP->iconCount = 1; + MALLOCARRAY_NOFAIL(cmdlineP->inputFilespec, cmdlineP->iconCount); + cmdlineP->inputFilespec[0] = "-"; + } else { + unsigned int iconIndex; + + cmdlineP->iconCount = argc-1; + MALLOCARRAY_NOFAIL(cmdlineP->inputFilespec, cmdlineP->iconCount); + for (iconIndex = 0; iconIndex < cmdlineP->iconCount; ++iconIndex) + cmdlineP->inputFilespec[iconIndex] = argv[iconIndex+1]; + } + { + unsigned int iconIndex; + MALLOCARRAY_NOFAIL(cmdlineP->andpgmFilespec, cmdlineP->iconCount); + for (iconIndex = 0; iconIndex < cmdlineP->iconCount; ++iconIndex) + cmdlineP->andpgmFilespec[iconIndex] = NULL; + } + } else { + if (argc-1 < 2) + pm_error("with -andpgms, you must specify at least two " + "arguments: image file name and and mask file name. " + "You specified %d", argc-1); + else if ((argc-1)/2*2 != (argc-1)) + pm_error("with -andpgms, you must specify an even number of " + "arguments. You specified %d", argc-1); + else { + unsigned int iconIndex; + cmdlineP->iconCount = (argc-1)/2; + MALLOCARRAY_NOFAIL(cmdlineP->inputFilespec, cmdlineP->iconCount); + MALLOCARRAY_NOFAIL(cmdlineP->andpgmFilespec, cmdlineP->iconCount); + for (iconIndex = 0; iconIndex < cmdlineP->iconCount; ++iconIndex) { + cmdlineP->inputFilespec[iconIndex] = argv[1 + iconIndex*2]; + cmdlineP->andpgmFilespec[iconIndex] = argv[2 + iconIndex*2]; + } + } + } + +} + + + +static void +PutByte(int const v) { + + if (putc(v, ofp) == EOF) + pm_error("Unable to write byte to output file."); +} + + + +static void +PutShort(short const v) { + + if (pm_writelittleshort(ofp, v) == -1) + pm_error("Unable to write short integer to output file"); +} + + + +static void +PutLong(long const v) { + + if (pm_writelittlelong(ofp, v) == -1) + pm_error("Unable to write long integer to output file"); +} + + + +/* + * These have no purpose but to wrapper the Byte, Short & Long + * functions. + */ +static void +writeU1 (u1 const v) { + file_offset++; + PutByte(v); +} + +static void +writeU2 (u2 const v) { + file_offset +=2; + PutShort(v); +} + +static void +writeU4 (u4 const v) { + file_offset += 4; + PutLong(v); +} + +static MS_Ico +createIconFile (void) { + MS_Ico MSIconData; + + MALLOCVAR_NOFAIL(MSIconData); + + MSIconData->reserved = 0; + MSIconData->type = 1; + MSIconData->count = 0; + MSIconData->entries = NULL; + return MSIconData; +} + + +/* create andBitmap from pgm */ + +static ICON_bmp +createAndBitmap (gray ** const ba, int const cols, int const rows, + gray const maxval) { + /* + * How wide should the u1 string for each row be? + * each byte is 8 pixels, but must be a multiple of 4 bytes. + */ + ICON_bmp icBitmap; + int xBytes,y,x; + int wt = cols; + u1 ** rowData; + + MALLOCVAR_NOFAIL(icBitmap); + + wt >>= 3; + if (wt & 3) { + wt = (wt & ~3) + 4; + } + xBytes = wt; + MALLOCARRAY_NOFAIL(rowData, rows); + icBitmap->xBytes = xBytes; + icBitmap->data = rowData; + icBitmap->size = xBytes * rows; + for (y=0;y<rows;y++) { + u1 * row; + int byteOn = 0; + int bitOn = 128; + + MALLOCARRAY_NOFAIL(row, xBytes); + + memset (row, 0, xBytes); + rowData[rows-y-1] = row; + /* + * Check there's a bit array, otherwise we're just faking this... + */ + if (ba) { + for (x=0;x<cols;x++) { + /* Black (bit clear) is transparent in PGM alpha maps, + * in ICO bit *set* is transparent. + */ + if (ba[y][x] <= maxval/2) row[byteOn] |= bitOn; + + if (bitOn == 1) { + byteOn++; + bitOn = 128; + } else { + bitOn >>= 1; + } + } + } + } + return icBitmap; +} + + +/* + * Depending on if the image is stored as 1bpp, 4bpp or 8bpp, the + * encoding mechanism is different. + * + * I didn't re-use the code from ppmtobmp since I need to keep the + * bitmaps in memory till I've loaded all ppms. + * + * 8bpp => 1 byte/palette index. + * 4bpp => High Nibble, Low Nibble + * 1bpp => 1 palette value per bit, high bit 1st. + */ +static ICON_bmp +create1Bitmap (pixel ** const pa, int const cols, int const rows, + colorhash_table const cht) { + /* + * How wide should the u1 string for each row be? + * each byte is 8 pixels, but must be a multiple of 4 bytes. + */ + ICON_bmp icBitmap; + int xBytes,y,x; + int wt = cols; + u1 ** rowData; + + MALLOCVAR_NOFAIL(icBitmap); + + wt >>= 3; + if (wt & 3) { + wt = (wt & ~3) + 4; + } + xBytes = wt; + MALLOCARRAY_NOFAIL(rowData, rows); + icBitmap->xBytes = xBytes; + icBitmap->data = rowData; + icBitmap->size = xBytes * rows; + for (y=0;y<rows;y++) { + u1 * row; + int byteOn = 0; + int bitOn = 128; + int value; + + MALLOCARRAY_NOFAIL(row, xBytes); + memset (row, 0, xBytes); + rowData[rows-y-1] = row; + /* + * Check there's a pixel array, otherwise we're just faking this... + */ + if (pa) { + for (x=0;x<cols;x++) { + /* + * So we've got a colorhash_table with two colors in it. + * Which is black?! + * + * Unless the hashing function changes, 0's black. + */ + value = ppm_lookupcolor(cht, &pa[y][x]); + if (!value) { + /* leave black. */ + } else { + row[byteOn] |= bitOn; + } + if (bitOn == 1) { + byteOn++; + bitOn = 128; + } else { + bitOn >>= 1; + } + } + } + } + return icBitmap; +} + + +static ICON_bmp +create4Bitmap (pixel ** const pa, int const cols, int const rows, + colorhash_table const cht) { + /* + * How wide should the u1 string for each row be? + * each byte is 8 pixels, but must be a multiple of 4 bytes. + */ + ICON_bmp icBitmap; + int xBytes,y,x; + int wt = cols; + u1 ** rowData; + + MALLOCVAR_NOFAIL(icBitmap); + + wt >>= 1; + if (wt & 3) { + wt = (wt & ~3) + 4; + } + xBytes = wt; + MALLOCARRAY_NOFAIL(rowData, rows); + icBitmap->xBytes = xBytes; + icBitmap->data = rowData; + icBitmap->size = xBytes * rows; + + for (y=0;y<rows;y++) { + u1 * row; + int byteOn = 0; + int nibble = 1; /* high nibble = 1, low nibble = 0; */ + int value; + + MALLOCARRAY_NOFAIL(row, xBytes); + + memset (row, 0, xBytes); + rowData[rows-y-1] = row; + /* + * Check there's a pixel array, otherwise we're just faking this... + */ + if (pa) { + for (x=0;x<cols;x++) { + value = ppm_lookupcolor(cht, &pa[y][x]); + /* + * Shift it, if we're putting it in the high nibble. + */ + if (nibble) { + value <<= 4; + } + row[byteOn] |= value; + if (nibble) { + nibble = 0; + } else { + nibble = 1; + byteOn++; + } + } + } + } + return icBitmap; +} + + + +static ICON_bmp +create8Bitmap (pixel ** const pa, int const cols, int const rows, + colorhash_table const cht) { + /* + * How wide should the u1 string for each row be? + * each byte is 8 pixels, but must be a multiple of 4 bytes. + */ + ICON_bmp icBitmap; + int xBytes,y,x; + int wt = cols; + u1 ** rowData; + + MALLOCVAR_NOFAIL(icBitmap); + + if (wt & 3) { + wt = (wt & ~3) + 4; + } + xBytes = wt; + MALLOCARRAY_NOFAIL(rowData, rows); + icBitmap->xBytes = xBytes; + icBitmap->data = rowData; + icBitmap->size = xBytes * rows; + + for (y=0;y<rows;y++) { + u1 * row; + + MALLOCARRAY_NOFAIL(row, xBytes); + memset (row, 0, xBytes); + rowData[rows-y-1] = row; + /* + * Check there's a pixel array, otherwise we're just faking this... + */ + if (pa) { + for (x=0;x<cols;x++) { + row[x] = ppm_lookupcolor(cht, &pa[y][x]); + } + } + } + return icBitmap; +} + + + +static IC_InfoHeader +createInfoHeader(IC_Entry const entry, ICON_bmp const xbmp, + ICON_bmp const abmp) { + IC_InfoHeader ih; + + MALLOCVAR_NOFAIL(ih); + + ih->size = 40; + ih->width = entry->width; + ih->height = entry->height * 2; + ih->planes = 1; + ih->bitcount = entry->bitcount; + ih->compression = 0; + ih->imagesize = entry->width * entry->height * 8 / entry->bitcount; + ih->x_pixels_per_m= 0; + ih->y_pixels_per_m= 0; + ih->colors_used = 1 << entry->bitcount; + ih->colors_important = 0; + return ih; +} + + + +static IC_Palette +createCleanPalette(void) { + IC_Palette palette; + int x; + + MALLOCVAR_NOFAIL(palette); + + MALLOCARRAY_NOFAIL(palette->colors, MAXCOLORS); + for (x=0;x<MAXCOLORS;x++ ){ + palette->colors[x] = NULL; + } + return palette; +} + + + +static void +addColorToPalette(IC_Palette const palette, int const i, + int const r, int const g, int const b) { + + MALLOCVAR_NOFAIL(palette->colors[i]); + + palette->colors[i]->red = r; + palette->colors[i]->green = g; + palette->colors[i]->blue = b; + palette->colors[i]->reserved = 0; +} + + + +static ICON_bmp +createBitmap (int const bpp, pixel ** const pa, + int const cols, int const rows, + colorhash_table const cht) { + + ICON_bmp retval; + const int assumed_bpp = (pa == NULL) ? 1 : bpp; + + switch (assumed_bpp) { + case 1: + retval = create1Bitmap (pa,cols,rows,cht); + break; + case 4: + retval = create4Bitmap (pa,cols,rows,cht); + break; + case 8: + default: + retval = create8Bitmap (pa,cols,rows,cht); + break; + } + return retval; +} + + + +static void +makePalette(pixel ** const xorPPMarray, + int const xorCols, + int const xorRows, + pixval const xorMaxval, + IC_Palette * const paletteP, + colorhash_table * const xorChtP, + int * const colorsP, + const char ** const errorP) { + /* + * Figure out the colormap and turn it into the appropriate GIF + * colormap - this code's pretty much straight from ppmtobpm + */ + colorhist_vector xorChv; + unsigned int i; + int colors; + IC_Palette palette = createCleanPalette(); + + if (verbose) pm_message("computing colormap..."); + xorChv = ppm_computecolorhist(xorPPMarray, xorCols, xorRows, MAXCOLORS, + &colors); + if (xorChv == NULL) + asprintfN(errorP, + "image has too many colors - try doing a 'pnmquant %d'", + MAXCOLORS); + else { + *errorP = NULL; + + if (verbose) pm_message("%d colors found", colors); + + if (verbose && (xorMaxval > 255)) + pm_message("maxval is not 255 - automatically rescaling colors"); + for (i = 0; i < colors; ++i) { + if (xorMaxval == 255) { + addColorToPalette(palette,i, + PPM_GETR(xorChv[i].color), + PPM_GETG(xorChv[i].color), + PPM_GETB(xorChv[i].color)); + } else { + addColorToPalette(palette,i, + PPM_GETR(xorChv[i].color) * 255 / xorMaxval, + PPM_GETG(xorChv[i].color) * 255 / xorMaxval, + PPM_GETB(xorChv[i].color) * 255 / xorMaxval); + } + } + + /* And make a hash table for fast lookup. */ + *xorChtP = ppm_colorhisttocolorhash(xorChv, colors); + + ppm_freecolorhist(xorChv); + + *paletteP = palette; + *colorsP = colors; + } +} + + + +static void +getOrFakeAndMap(const char * const andPgmFname, + int const xorCols, + int const xorRows, + gray *** const andPGMarrayP, + pixval * const andMaxvalP, + colorhash_table * const andChtP, + const char ** const errorP) { + + int andRows, andCols; + + if (!andPgmFname) { + /* He's not supplying a bitmap for 'and'. Fake the bitmap. */ + *andPGMarrayP = NULL; + *andMaxvalP = 1; + *andChtP = NULL; + *errorP = NULL; + } else { + FILE * andfile; + andfile = pm_openr(andPgmFname); + *andPGMarrayP = pgm_readpgm(andfile, &andCols, &andRows, andMaxvalP); + pm_close(andfile); + + if ((andCols != xorCols) || (andRows != xorRows)) { + asprintfN(errorP, + "And mask and image have different dimensions " + "(%d x %d vs %d x %d). Aborting.", + andCols, xorCols, andRows, xorRows); + } else + *errorP = NULL; + } +} + + + +static void +blackenTransparentAreas(pixel ** const xorPPMarray, + int const cols, + int const rows, + gray ** const andPGMarray, + pixval const andMaxval) { + + unsigned int row; + + if (verbose) pm_message("Setting transparent pixels to black"); + + for (row = 0; row < rows; ++row) { + unsigned int col; + for (col = 0; col < cols; ++col) { + if (andPGMarray[row][col] < andMaxval) + /* It's not opaque here; make it black */ + PPM_ASSIGN(xorPPMarray[row][col], 0, 0, 0); + } + } +} + + + +static void +addEntryToIcon(MS_Ico const MSIconData, + const char * const xorPpmFname, + const char * const andPgmFname, + bool const trueTransparent) { + + IC_Entry entry; + FILE * xorfile; + pixel ** xorPPMarray; + gray ** andPGMarray; + ICON_bmp xorBitmap; + ICON_bmp andBitmap; + int rows, cols; + int bpp, colors; + int entry_cols; + IC_Palette palette; + colorhash_table xorCht; + colorhash_table andCht; + const char * error; + + pixval xorMaxval; + gray andMaxval; + + MALLOCVAR_NOFAIL(entry); + + /* + * Read the xor PPM. + */ + xorfile = pm_openr(xorPpmFname); + xorPPMarray = ppm_readppm(xorfile, &cols, &rows, &xorMaxval); + pm_close(xorfile); + /* + * Since the entry uses 1 byte to hold the width and height of the icon, the + * image can't be more than 256 x 256. + */ + if (rows > 255 || cols > 255) { + pm_error("Max size for a icon is 255 x 255 (1 byte fields). " + "%s is %d x %d", xorPpmFname, cols, rows); + } + + if (verbose) pm_message("read PPM: %dw x %dh, maxval = %d", + cols, rows, xorMaxval); + + makePalette(xorPPMarray, cols, rows, xorMaxval, + &palette, &xorCht, &colors, &error); + + if (error) + pm_error("Unable to make palette for '%s'. %s", xorPpmFname, error); + /* + * All the icons I found seemed to pad the palette to the max entries + * for that bitdepth. + * + * The spec indicates this isn't neccessary, but I'll follow this behaviour + * just in case. + */ + if (colors < 3) { + bpp = 1; + entry_cols = 2; + } else if (colors < 17) { + bpp = 4; + entry_cols = 16; + } else { + bpp = 8; + entry_cols = 256; + } + + getOrFakeAndMap(andPgmFname, cols, rows, + &andPGMarray, &andMaxval, &andCht, &error); + if (error) + pm_error("Error in and map for '%s'. %s", xorPpmFname, error); + + if (andPGMarray && trueTransparent) + blackenTransparentAreas(xorPPMarray, cols, rows, + andPGMarray, andMaxval); + + xorBitmap = createBitmap(bpp, xorPPMarray, cols, rows, xorCht); + andBitmap = createAndBitmap(andPGMarray, cols, rows, andMaxval); + /* + * Fill in the entry data fields. + */ + entry->width = cols; + entry->height = rows; + entry->color_count = entry_cols; + entry->reserved = 0; + entry->planes = 1; + /* + * all the icons I looked at ignored this value... + */ + entry->bitcount = bpp; + entry->ih = createInfoHeader(entry, xorBitmap, andBitmap); + entry->colors = palette->colors; + entry->size_in_bytes = + xorBitmap->size + andBitmap->size + 40 + (4 * entry->color_count); + if (verbose) + pm_message("entry->size_in_bytes = %d + %d + %d = %d", + xorBitmap->size ,andBitmap->size, + 40, entry->size_in_bytes ); + /* + * We don't know the offset ATM, set to 0 for now. + * Have to calculate this at the end. + */ + entry->file_offset = 0; + entry->xorBitmapOut = xorBitmap->data; + entry->andBitmapOut = andBitmap->data; + entry->xBytesXor = xorBitmap->xBytes; + entry->xBytesAnd = andBitmap->xBytes; + /* + * Add the entry to the entries array. + */ + ++MSIconData->count; + /* Perhaps I should allocate ahead, and take fewer trips to the well. */ + REALLOCARRAY(MSIconData->entries, MSIconData->count); + MSIconData->entries[MSIconData->count-1] = entry; +} + + + +static void +writeIC_Entry (IC_Entry const entry) { + writeU1(entry->width); + writeU1(entry->height); + writeU1(entry->color_count); /* chops 256->0 on its own.. */ + writeU1(entry->reserved); + writeU2(entry->planes); + writeU2(entry->bitcount); + writeU4(entry->size_in_bytes); + writeU4(entry->file_offset); +} + + + +static void +writeIC_InfoHeader (IC_InfoHeader const ih) { + writeU4(ih->size); + writeU4(ih->width); + writeU4(ih->height); + writeU2(ih->planes); + writeU2(ih->bitcount); + writeU4(ih->compression); + writeU4(ih->imagesize); + writeU4(ih->x_pixels_per_m); + writeU4(ih->y_pixels_per_m); + writeU4(ih->colors_used); + writeU4(ih->colors_important); +} + + + +static void +writeIC_Color (IC_Color const col) { + /* Since the ppm might not have as many colors in it as we'd like, + * (2, 16, 256), stick 0 in the gaps. + * + * This means that we lose palette information, but that can't be + * helped. + */ + if (col == NULL) { + writeU1(0); + writeU1(0); + writeU1(0); + writeU1(0); + } else { + writeU1(col->blue); + writeU1(col->green); + writeU1(col->red); + writeU1(col->reserved); + } +} + + + +static void +writeBitmap(u1 ** const bitmap, int const xBytes, int const height) { + int y; + for (y = 0;y<height;y++) { + fwrite (bitmap[y],1,xBytes,ofp); + file_offset += xBytes; + } +} + + + +static void +writeMS_Ico(MS_Ico const MSIconData, + const char * const outFname) { + int x,y; + + ofp = pm_openw(outFname); + + writeU2(MSIconData->reserved); + writeU2(MSIconData->type); + writeU2(MSIconData->count); + for (x=0;x<MSIconData->count;x++) writeIC_Entry(MSIconData->entries[x]); + for (x=0;x<MSIconData->count;x++) { + writeIC_InfoHeader(MSIconData->entries[x]->ih); + for (y=0;y<(MSIconData->entries[x]->color_count);y++) { + writeIC_Color(MSIconData->entries[x]->colors[y]); + } + if (verbose) pm_message("writing xor bitmap"); + writeBitmap(MSIconData->entries[x]->xorBitmapOut, + MSIconData->entries[x]->xBytesXor, + MSIconData->entries[x]->height); + if (verbose) pm_message("writing and bitmap"); + writeBitmap(MSIconData->entries[x]->andBitmapOut, + MSIconData->entries[x]->xBytesAnd, + MSIconData->entries[x]->height); + } + fclose(ofp); +} + + + +int +main(int argc, char ** argv) { + + struct cmdlineInfo cmdline; + + MS_Ico const MSIconDataP = createIconFile(); + unsigned int iconIndex; + unsigned int offset; + + ppm_init (&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + verbose = cmdline.verbose; + + for (iconIndex = 0; iconIndex < cmdline.iconCount; ++iconIndex) { + addEntryToIcon(MSIconDataP, cmdline.inputFilespec[iconIndex], + cmdline.andpgmFilespec[iconIndex], + cmdline.truetransparent); + } + /* + * Now we have to go through and calculate the offsets. + * The first infoheader starts at 6 + count*16 bytes. + */ + offset = (MSIconDataP->count * 16) + 6; + for (iconIndex = 0; iconIndex < MSIconDataP->count; ++iconIndex) { + IC_Entry entry = MSIconDataP->entries[iconIndex]; + entry->file_offset = offset; + /* + * Increase the offset by the size of this offset & data. + * this includes the size of the color data. + */ + offset += entry->size_in_bytes; + } + /* + * And now, we have to actually SAVE the .ico! + */ + writeMS_Ico(MSIconDataP, cmdline.output); + + free(cmdline.inputFilespec); + free(cmdline.andpgmFilespec); + + return 0; +} + diff --git a/converter/ppm/ppmtoxpm.c b/converter/ppm/ppmtoxpm.c new file mode 100644 index 00000000..853ae711 --- /dev/null +++ b/converter/ppm/ppmtoxpm.c @@ -0,0 +1,659 @@ +/* ppmtoxpm.c - read a portable pixmap and produce a (version 3) X11 pixmap +** +** Copyright (C) 1990 by Mark W. Snitily +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +** +** This tool was developed for Schlumberger Technologies, ATE Division, and +** with their permission is being made available to the public with the above +** copyright notice and permission notice. +** +** Upgraded to XPM2 by +** Paul Breslaw, Mecasoft SA, Zurich, Switzerland (paul@mecazh.uu.ch) +** Thu Nov 8 16:01:17 1990 +** +** Upgraded to XPM version 3 by +** Arnaud Le Hors (lehors@mirsa.inria.fr) +** Tue Apr 9 1991 +** +** Rainer Sinkwitz sinkwitz@ifi.unizh.ch - 21 Nov 91: +** - Bug fix, should should malloc space for rgbn[j].name+1 in line 441 +** caused segmentation faults +** +** - lowercase conversion of RGB names def'ed out, +** considered harmful. +** +** Michael Pall (pall@rz.uni-karlsruhe.de) - 29 Nov 93: +** - Use the algorithm from xpm-lib for pixel encoding +** (base 93 not base 28 -> saves a lot of space for colorful xpms) +*/ + +#define _BSD_SOURCE /* Make sure strdup() is in string.h */ +#define _XOPEN_SOURCE 500 /* Make sure strdup() is in string.h */ + +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include "ppm.h" +#include "shhopt.h" +#include "nstring.h" +#include "mallocvar.h" + +/* Max number of entries we will put in the XPM color map + Don't forget the one entry for transparency. + + We don't use this anymore. Ppmtoxpm now has no arbitrary limit on + the number of colors. + + We're assuming this isn't in fact an XPM format requirement, because + we've seen it work with 257, and 257 seems to be common, because it's + the classic 256 colors, plus transparency. The value was 256 for + ages before we added transparency capability to this program in May + 2001. At that time, we started failing with 256 color images. + Some limit was also necessary before then because ppm_computecolorhash() + required us to specify a maximum number of colors. It doesn't anymore. + + If we find out that some XPM processing programs choke on more than + 256 colors, we'll have to readdress this issue. - Bryan. 2001.05.13. +*/ +#define MAXCOLORS 256 + +#define MAXPRINTABLE 92 /* number of printable ascii chars + * minus \ and " for string compat + * and ? to avoid ANSI trigraphs. */ + +static const char * const printable = +" .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjklzxcvbnmMNBVCZ\ +ASDFGHJKLPIUYTREWQ!~^/()_`'][{}|"; + + +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char *inputFilename; + const char *name; + const char *rgb; + const char *alpha_filename; + unsigned int hexonly; + unsigned int verbose; +}; + + + +static void +parseCommandLine(int argc, char ** argv, + struct cmdlineInfo * const cmdlineP) { +/*---------------------------------------------------------------------------- + Note that the file spec array we return is stored in the storage that + was passed to us as the argv array. +-----------------------------------------------------------------------------*/ + optEntry *option_def; + /* Instructions to OptParseOptions3 on how to parse our options. + */ + optStruct3 opt; + + unsigned int option_def_index; + const char * nameOpt; + unsigned int nameSpec; + + MALLOCARRAY(option_def, 100); + + option_def_index = 0; /* incremented by OPTENTRY */ + OPTENT3(0, "alphamask", OPT_STRING, &cmdlineP->alpha_filename, + NULL, 0); + OPTENT3(0, "name", OPT_STRING, &nameOpt, + &nameSpec, 0); + OPTENT3(0, "rgb", OPT_STRING, &cmdlineP->rgb, + NULL, 0); + OPTENT3(0, "hexonly", OPT_FLAG, NULL, + &cmdlineP->hexonly, 0); + OPTENT3(0, "verbose", OPT_FLAG, NULL, + &cmdlineP->verbose, 0); + + /* Set the defaults */ + cmdlineP->alpha_filename = NULL; /* no transparency */ + cmdlineP->rgb = NULL; /* no rgb file specified */ + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We may have parms that are negative numbers */ + + optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (argc-1 == 0) + cmdlineP->inputFilename = "-"; + else if (argc-1 != 1) + pm_error("Program takes zero or one argument (filename). You " + "specified %d", argc-1); + else + cmdlineP->inputFilename = argv[1]; + + /* If output filename not specified, use input filename as default. */ + if (nameSpec) + cmdlineP->name = nameOpt; + else if (STREQ(cmdlineP->inputFilename, "-")) + cmdlineP->name = "noname"; + else { + static char name[80+1]; + char *cp; + + STRSCPY(name, cmdlineP->inputFilename); + cp = strchr(name, '.'); + if (cp) + *cp = '\0'; /* remove extension */ + cmdlineP->name = name; + } +} + + +typedef struct { /* rgb values and ascii names (from + * rgb text file) */ + int r, g, b; /* rgb values, range of 0 -> 65535 */ + char *name; /* color mnemonic of rgb value */ +} rgb_names; + +typedef struct { + /* Information for an XPM color map entry */ + char *cixel; + /* XPM color number, as might appear in the XPM raster */ + const char *rgbname; + /* color name, e.g. "blue" or "#01FF23" */ +} cixel_map; + + + +static char * +genNumstr(unsigned int const input, int const digits) { +/*--------------------------------------------------------------------------- + Given a number and a base (MAXPRINTABLE), this routine prints the + number into a malloc'ed string and returns it. The length of the + string is specified by "digits". It is not printed in decimal or + any other number system you're used to. Rather, each digit is from + the set printable[], which contains MAXPRINTABLE characters; the + character printable[n] has value n. + + The string is printable[0] filled with high order zeroes. + + Example: + Assume: + printable[0] == 'q' + printable[1] == '%' + MAXPRINTABLE == 2 + digits == 5 + input == 3 + Result is the malloc'ed string "qqq%%" +---------------------------------------------------------------------------*/ + char *str, *p; + int d; + unsigned int i; + + /* Allocate memory for printed number. Abort if error. */ + if (!(str = (char *) malloc(digits + 1))) + pm_error("out of memory"); + + i = input; + /* Generate characters starting with least significant digit. */ + p = str + digits; + *p-- = '\0'; /* nul terminate string */ + while (p >= str) { + d = i % MAXPRINTABLE; + i /= MAXPRINTABLE; + *p-- = printable[d]; + } + + if (i != 0) + pm_error("Overflow converting %d to %d digits in base %d", + input, digits, MAXPRINTABLE); + + return str; +} + + + +static unsigned int +xpmMaxvalFromMaxval(pixval const maxval) { + + unsigned int retval; + + /* + * Determine how many hex digits we'll be normalizing to if the rgb + * value doesn't match a color mnemonic. + */ + if (maxval <= 0x000F) + retval = 0x000F; + else if (maxval <= 0x00FF) + retval = 0x00FF; + else if (maxval <= 0x0FFF) + retval = 0x0FFF; + else if (maxval <= 0xFFFF) + retval = 0xFFFF; + else + pm_error("Internal error - impossible maxval %x", maxval); + + return retval; +} + + + +static unsigned int +charsPerPixelForSize(unsigned int const cmapSize) { +/*---------------------------------------------------------------------------- + Return the number of characters it will take to represent a pixel in + an XPM that has a colormap of size 'cmapSize'. Each pixel in an XPM + represents an index into the colormap with a base-92 scheme where each + character is one of 92 printable characters. Ergo, if the colormap + has at most 92 characters, each pixel will be represented by a single + character. If it has more than 92, but at most 92*92, it will take 2, + etc. + + If cmapSize is zero, there's no such thing as an XPM pixel, so we + return an undefined value. +-----------------------------------------------------------------------------*/ + unsigned int charsPerPixel; + + if (cmapSize > 0) { + unsigned int j; + + for (charsPerPixel = 0, j = cmapSize-1; j > 0; ++charsPerPixel) + j /= MAXPRINTABLE; + } + return charsPerPixel; +} + + + +static void +genCmap(colorhist_vector const chv, + int const ncolors, + pixval const maxval, + colorhash_table const colornameHash, + const char * const colornames[], + bool const includeTransparent, + cixel_map ** const cmapP, + unsigned int * const transIndexP, + unsigned int * const cmapSizeP, + unsigned int * const charsPerPixelP) { +/*---------------------------------------------------------------------------- + Generate the XPM color map in cixel_map format (which is just a step + away from the actual text that needs to be written the XPM file). The + color map is defined by 'chv', which contains 'ncolors' colors which + have maxval 'maxval'. + + Output is in newly malloc'ed storage, with its address returned as + *cmapP. We return the number of entries in it as *cmapSizeP. + + This map includes an entry for transparency, whether the raster uses + it or not. We return its index as *transIndexP. + + In the map, identify colors by the names given by 'colornameHash' and + colornames[]. 'colornameHash' maps a color in 'pixel' form to an + index into colornames[]; colornames[] contains the text to identify the + color in the XPM format. The colors in 'colornameHash' have maxval 255. + If a color is not in 'colornameHash', use hexadecimal notation in the + output colormap. + + But if 'colornameHash' is null, don't use color names at all. Just use + hexadecimal notation. + + Return as *charsPerPixel the number of characters, or digits, that + will be needed in the XPM raster to form an index into this color map. +-----------------------------------------------------------------------------*/ + unsigned int const cmapSize = ncolors + (includeTransparent ? 1 : 0); + + cixel_map * cmap; /* malloc'ed */ + unsigned int cmapIndex; + unsigned int charsPerPixel; + unsigned int xpmMaxval; + + MALLOCARRAY(cmap, cmapSize); + if (cmapP == NULL) + pm_error("Out of memory allocating %u bytes for a color map.", + sizeof(cixel_map) * (ncolors+1)); + + xpmMaxval = xpmMaxvalFromMaxval(maxval); + + charsPerPixel = charsPerPixelForSize(cmapSize); + + /* + * Generate the character-pixel string and the rgb name for each + * colormap entry. + */ + for (cmapIndex = 0; cmapIndex < ncolors; ++cmapIndex) { + pixel const color = chv[cmapIndex].color; + + pixel color255; + /* The color, scaled to maxval 255 */ + const char * colorname; /* malloc'ed */ + /* + * The character-pixel string is simply a printed number in base + * MAXPRINTABLE where the digits of the number range from + * printable[0] .. printable[MAXPRINTABLE-1] and the printed length + * of the number is 'charsPerPixel'. + */ + cmap[cmapIndex].cixel = genNumstr(cmapIndex, charsPerPixel); + + PPM_DEPTH(color255, color, maxval, 255); + + if (colornameHash == NULL) + colorname = NULL; + else { + int colornameIndex; + colornameIndex = ppm_lookupcolor(colornameHash, &color255); + if (colornameIndex >= 0) + colorname = strdup(colornames[colornameIndex]); + else + colorname = NULL; + } + if (colorname) + cmap[cmapIndex].rgbname = colorname; + else { + /* Color has no name; represent it in hexadecimal */ + + pixel scaledColor; + const char * hexString; /* malloc'ed */ + + PPM_DEPTH(scaledColor, color, maxval, xpmMaxval); + + asprintfN(&hexString, xpmMaxval == 0x000F ? "#%X%X%X" : + xpmMaxval == 0x00FF ? "#%02X%02X%02X" : + xpmMaxval == 0x0FFF ? "#%03X%03X%03X" : + "#%04X%04X%04X", + PPM_GETR(scaledColor), + PPM_GETG(scaledColor), + PPM_GETB(scaledColor) + ); + + if (hexString == NULL) + pm_error("Unable to allocate storage for hex string"); + cmap[cmapIndex].rgbname = hexString; + } + } + + if (includeTransparent) { + /* Add the special transparency entry to the colormap */ + unsigned int const transIndex = ncolors; + cmap[transIndex].rgbname = strdup("None"); + cmap[transIndex].cixel = genNumstr(transIndex, charsPerPixel); + *transIndexP = transIndex; + } + *cmapP = cmap; + *cmapSizeP = cmapSize; + *charsPerPixelP = charsPerPixel; +} + + + +static void +destroyCmap(cixel_map * const cmap, + unsigned int const cmapSize) { + + int i; + /* Free the real color entries */ + for (i = 0; i < cmapSize; i++) { + strfree(cmap[i].rgbname); + free(cmap[i].cixel); + } + free(cmap); +} + + + +static void +writeXpmFile(FILE * const outfile, + pixel ** const pixels, + gray ** const alpha, + pixval const alphamaxval, + char const name[], + int const cols, + int const rows, + unsigned int const cmapSize, + unsigned int const charsPerPixel, + cixel_map const cmap[], + colorhash_table const cht, + unsigned int const transIndex) { +/*---------------------------------------------------------------------------- + Write the whole XPM file to the open stream 'outfile'. + + 'cmap' is the colormap to be placed in the XPM. 'cmapSize' is the + number of entries in it. 'cht' is a hash table that gives you an + index into 'cmap' given a color. 'transIndex' is the index into cmap + of the transparent color, and is valid only if 'alpha' is non-null + (otherwise, cmap might not contain a transparent color). +-----------------------------------------------------------------------------*/ + /* First the header */ + printf("/* XPM */\n"); + fprintf(outfile, "static char *%s[] = {\n", name); + fprintf(outfile, "/* width height ncolors chars_per_pixel */\n"); + fprintf(outfile, "\"%d %d %d %d\",\n", cols, rows, + cmapSize, charsPerPixel); + + { + int i; + /* Write out the colormap (part of header) */ + fprintf(outfile, "/* colors */\n"); + for (i = 0; i < cmapSize; i++) { + fprintf(outfile, "\"%s c %s\",\n", cmap[i].cixel, cmap[i].rgbname); + } + } + { + int row; + + /* And now the raster */ + fprintf(outfile, "/* pixels */\n"); + for (row = 0; row < rows; row++) { + int col; + fprintf(outfile, "\""); + for (col = 0; col < cols; col++) { + if (alpha && alpha[row][col] <= alphamaxval/2) + /* It's a transparent pixel */ + fprintf(outfile, "%s", cmap[transIndex].cixel); + else + fprintf(outfile, "%s", + cmap[ppm_lookupcolor(cht, + &pixels[row][col])].cixel); + } + fprintf(outfile, "\"%s\n", (row == (rows - 1) ? "" : ",")); + } + } + /* And close up */ + fprintf(outfile, "};\n"); +} + + + +static void +readAlpha(const char filespec[], gray *** const alphaP, + int const cols, int const rows, pixval * const alphamaxvalP) { + + FILE * alpha_file; + int alphacols, alpharows; + + alpha_file = pm_openr(filespec); + *alphaP = pgm_readpgm(alpha_file, &alphacols, &alpharows, alphamaxvalP); + pm_close(alpha_file); + + if (cols != alphacols || rows != alpharows) + pm_error("Alpha mask is not the same dimensions as the " + "image. Image is %d by %d, while mask is %d x %d.", + cols, rows, alphacols, alpharows); +} + + + +static void +computecolorhash(pixel ** const pixels, + gray ** const alpha, + int const cols, + int const rows, + gray const alphaMaxval, + colorhash_table * const chtP, + unsigned int * const ncolorsP, + bool * const transparentSomewhereP) { +/*---------------------------------------------------------------------------- + Compute a colorhash_table with one entry for each color in 'pixels' that + is not mostly transparent according to alpha mask 'alpha' (which has + maxval 'alphaMaxval'). alpha == NULL means all pixels are opaque. + + The value associated with the color in the hash we build is meaningless. + + Return the colorhash_table as *chtP, and the number of colors in it + as *ncolorsP. Return *transparentSomewhereP == TRUE iff the image has + at least one pixel that is mostly transparent. +-----------------------------------------------------------------------------*/ + colorhash_table cht; + int row; + + cht = ppm_alloccolorhash( ); + *ncolorsP = 0; /* initial value */ + *transparentSomewhereP = FALSE; /* initial assumption */ + + /* Go through the entire image, building a hash table of colors. */ + for (row = 0; row < rows; ++row) { + int col; + + for (col = 0; col < cols; ++col) { + if (!alpha || alpha[row][col] > alphaMaxval/2) { + /* It's mostly opaque, so add this color to the hash + if it's not already there. + */ + pixel const color = pixels[row][col]; + int const lookupRc = ppm_lookupcolor(cht, &color); + + if (lookupRc < 0) { + /* It's not in the hash yet, so add it */ + ppm_addtocolorhash(cht, &color, 0); + ++(*ncolorsP); + } + } else + *transparentSomewhereP = TRUE; + } + } + *chtP = cht; +} + + + +static void +computeColormap(pixel ** const pixels, + gray ** const alpha, + int const cols, + int const rows, + gray const alphaMaxval, + colorhist_vector * const chvP, + colorhash_table * const chtP, + unsigned int * const ncolorsP, + bool * const transparentSomewhereP) { +/*---------------------------------------------------------------------------- + Compute the color map for the image 'pixels', which is 'cols' by 'rows', + in Netpbm data structures (a colorhist_vector for index-to-color lookups + and a colorhash_table for color-to-index lookups). + + Exclude pixels that alpha mask 'alpha' (which has maxval + 'alphaMaxval') says are mostly transparent. alpha == NULL means all + pixels are opaque. + + We return as *chvP an array of the colors present in 'pixels', + excluding those that are mostly transparent. We return as + *ncolorsP the number of such colors. We return + *transparentSomewhereP == TRUE iff the image has at least one + pixel that is mostly transparent. +-----------------------------------------------------------------------------*/ + colorhash_table histCht; + + pm_message("(Computing colormap..."); + computecolorhash(pixels, alpha, cols, rows, alphaMaxval, + &histCht, ncolorsP, transparentSomewhereP); + pm_message("...Done. %d colors found.)", *ncolorsP); + + *chvP = ppm_colorhashtocolorhist(histCht, *ncolorsP); + ppm_freecolorhash(histCht); + /* Despite the name, the following generates an index on *chvP, + with which given a color you can quickly find the entry number + in *chvP that contains that color. + */ + *chtP = ppm_colorhisttocolorhash(*chvP, *ncolorsP); +} + + + +int +main(int argc, char *argv[]) { + + FILE *ifp; + int rows, cols; + unsigned int ncolors; + bool transparentSomewhere; + pixval maxval, alphaMaxval; + colorhash_table cht; + colorhist_vector chv; + + colorhash_table colornameHash; + /* Hash table to map colors to their names */ + const char ** colornames; + /* Table of color names; 'colornameHash' yields an index into this + array. + */ + + pixel **pixels; + gray **alpha; + + /* Used for rgb value -> character-pixel string mapping */ + cixel_map *cmap; /* malloc'ed */ + /* The XPM colormap */ + unsigned int cmapSize; + /* Number of entries in 'cmap' */ + unsigned int transIndex; + /* Index into 'cmap' of the transparent color, if there is one */ + + unsigned int charsPerPixel; + + struct cmdlineInfo cmdline; + + ppm_init(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + ifp = pm_openr(cmdline.inputFilename); + pixels = ppm_readppm(ifp, &cols, &rows, &maxval); + pm_close(ifp); + + if (cmdline.alpha_filename) + readAlpha(cmdline.alpha_filename, &alpha, cols, rows, &alphaMaxval); + else + alpha = NULL; + + computeColormap(pixels, alpha, cols, rows, alphaMaxval, + &chv, &cht, &ncolors, &transparentSomewhere); + + if (cmdline.hexonly) + colornameHash = NULL; + else + ppm_readcolornamefile(cmdline.rgb, FALSE, &colornameHash, &colornames); + + /* Now generate the character-pixel colormap table. */ + genCmap(chv, ncolors, maxval, + colornameHash, colornames, transparentSomewhere, + &cmap, &transIndex, &cmapSize, &charsPerPixel); + + writeXpmFile(stdout, pixels, alpha, alphaMaxval, + cmdline.name, cols, rows, cmapSize, + charsPerPixel, cmap, cht, transIndex); + + if (colornameHash) { + ppm_freecolorhash(colornameHash); + ppm_freecolornames(colornames); + } + destroyCmap(cmap, cmapSize); + ppm_freearray(pixels, rows); + if (alpha) pgm_freearray(alpha, rows); + + return 0; +} + diff --git a/converter/ppm/ppmtoyuv.c b/converter/ppm/ppmtoyuv.c new file mode 100644 index 00000000..7d843cc0 --- /dev/null +++ b/converter/ppm/ppmtoyuv.c @@ -0,0 +1,97 @@ +/* ppmtoyuv.c - convert a portable pixmap into an Abekas YUV file +** +** by Marc Boucher +** Internet: marc@PostImage.COM +** +** Based on Example Conversion Program, A60/A64 Digital Video Interface +** Manual, page 69. +** +** Copyright (C) 1991 by DHD PostImage Inc. +** Copyright (C) 1987 by Abekas Video Systems Inc. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "ppm.h" + +int +main(argc, argv) +char **argv; +{ + FILE *ifp; + pixel *pixelrow; + register pixel *pP; + int rows, cols, format, row; + register int col; + pixval maxval; + unsigned long y1, y2=0, u=0, v=0, u0=0, u1, u2, v0=0, v1, v2; + unsigned char *yuvbuf; + + + ppm_init(&argc, argv); + + if (argc > 2) pm_usage("[ppmfile]"); + + if (argc == 2) ifp = pm_openr(argv[1]); + else ifp = stdin; + + ppm_readppminit(ifp, &cols, &rows, &maxval, &format); + + if (cols % 2 != 0) + pm_error("Image must have even number of columns.\n" + "This image is %d columns wide. Try Pnmcut.", cols); + + pixelrow = ((pixel*) pm_allocrow( cols, sizeof(pixel) )); + yuvbuf = (unsigned char *) pm_allocrow( cols, 2 ); + + for (row = 0; row < rows; ++row) { + unsigned char *yuvptr; + + ppm_readppmrow(ifp, pixelrow, cols, maxval, format); + + for (col = 0, pP = pixelrow, yuvptr=yuvbuf; col < cols; col += 2, ++pP) { + pixval r, g, b; + + /* first pixel gives Y and 0.5 of chroma */ + r = PPM_GETR(*pP); + g = PPM_GETG(*pP); + b = PPM_GETB(*pP); + + y1 = 16829 * r + 33039 * g + 6416 * b + (0xffff & y2); + u1 = -4853 * r - 9530 * g + 14383 * b; + v1 = 14386 * r - 12046 * g - 2340 * b; + + pP++; + /* second pixel just yields a Y and 0.25 U, 0.25 V */ + r = PPM_GETR(*pP); + g = PPM_GETG(*pP); + b = PPM_GETB(*pP); + + y2 = 16829 * r + 33039 * g + 6416 * b + (0xffff & y1); + u2 = -2426 * r - 4765 * g + 7191 * b; + v2 = 7193 * r - 6023 * g - 1170 * b; + + /* filter the chroma */ + u = u0 + u1 + u2 + (0xffff & u); + v = v0 + v1 + v2 + (0xffff & v); + + u0 = u2; + v0 = v2; + + *yuvptr++ = (u >> 16) + 128; + *yuvptr++ = (y1 >> 16) + 16; + *yuvptr++ = (v >> 16) + 128; + *yuvptr++ = (y2 >> 16) + 16; + } + fwrite(yuvbuf, cols*2, 1, stdout); + } + + pm_close(ifp); + + exit(0); +} diff --git a/converter/ppm/ppmtoyuvsplit.c b/converter/ppm/ppmtoyuvsplit.c new file mode 100644 index 00000000..2dddebfc --- /dev/null +++ b/converter/ppm/ppmtoyuvsplit.c @@ -0,0 +1,197 @@ +/* ppmtoyuvsplit.c - convert a portable pixmap into 3 raw files: +** - basename.Y : The Luminance chunk at the size of the Image +** - basename.U : The Chrominance chunk U at 1/4 +** - basename.V : The Chrominance chunk V at 1/4 +** The subsampled U and V values are made by arithmetic mean. +** +** If CCIR601 is defined, the produced YUV triples are scaled again +** to fit into the smaller range of values for this standard. +** +** by A.Beck +** Internet: Andre_Beck@IRS.Inf.TU-Dresden.de +** +** Based on ppmtoyuv.c +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +/* Wether to create YUV in JFIF(JPEG) or CCIR.601(MPEG) scale */ +#define CCIR601 + +/* Wether to use pm_close() or fake it -- don't ask me why */ +/* #define ORIGINAL */ + +/* ALPHA Kludge by Franky */ + +#ifdef __alpha +#define myLONG int +#else +#define myLONG long +#endif + +#include <string.h> +#include "ppm.h" + +int +main(argc, argv) +char **argv; +{ + FILE *ifp,*vf,*uf,*yf; + pixel *pixelrow1,*pixelrow2; + register pixel *pP1,*pP2; + int rows, cols, format, row; + register int col; + pixval maxval; + myLONG u,v,y0,y1,y2,y3,u0,u1,u2,u3,v0,v1,v2,v3; + unsigned char *y1buf,*y2buf,*ubuf,*vbuf; + char ufname[256],vfname[256],yfname[256]; + + + ppm_init(&argc, argv); + + if ((argc>3)||(argc<2)) pm_usage("basename [ppmfile]"); + + if (argc == 3) ifp = pm_openr(argv[2]); + else ifp = stdin; + + strcpy(ufname,argv[1]); + strcpy(vfname,argv[1]); + strcpy(yfname,argv[1]); + + strcat(ufname,".U"); + strcat(vfname,".V"); + strcat(yfname,".Y"); + + uf = fopen(ufname,"wb"); + vf = fopen(vfname,"wb"); + yf = fopen(yfname,"wb"); + + if(!(uf && vf && yf)) { + perror("error opening output files"); + exit(0); + } + ppm_readppminit(ifp, &cols, &rows, &maxval, &format); + + if(cols & 1) fprintf(stderr, + "%s: Warning: odd columns count, exceed ignored\n", + argv[0]); + if(rows & 1) fprintf(stderr, + "%s: Warning: odd rows count, exceed ignored\n", + argv[0]); + + pixelrow1 = ((pixel*) pm_allocrow( cols, sizeof(pixel) )); + pixelrow2 = ((pixel*) pm_allocrow( cols, sizeof(pixel) )); + + y1buf = (unsigned char *) pm_allocrow( cols, 1 ); + y2buf = (unsigned char *) pm_allocrow( cols, 1 ); + ubuf = (unsigned char *) pm_allocrow( cols, 1 ); + vbuf = (unsigned char *) pm_allocrow( cols, 1 ); + + for (row = 0; row < (rows & ~1); row += 2) { + unsigned char *y1ptr,*y2ptr,*uptr,*vptr; + + ppm_readppmrow(ifp, pixelrow1, cols, maxval, format); + ppm_readppmrow(ifp, pixelrow2, cols, maxval, format); + + pP1 = pixelrow1; pP2 = pixelrow2; + y1ptr = y1buf; y2ptr = y2buf; vptr = vbuf; uptr = ubuf; + + for (col = 0 ; col < (cols & ~1); col += 2) { + pixval r0,g0,b0,r1,g1,b1,r2,g2,b2,r3,g3,b3; + + /* first pixel */ + r0 = PPM_GETR(*pP1); + g0 = PPM_GETG(*pP1); + b0 = PPM_GETB(*pP1); + pP1++; + /* 2nd pixel */ + r1 = PPM_GETR(*pP1); + g1 = PPM_GETG(*pP1); + b1 = PPM_GETB(*pP1); + pP1++; + /* 3rd pixel */ + r2 = PPM_GETR(*pP2); + g2 = PPM_GETG(*pP2); + b2 = PPM_GETB(*pP2); + pP2++; + /* 4th pixel */ + r3 = PPM_GETR(*pP2); + g3 = PPM_GETG(*pP2); + b3 = PPM_GETB(*pP2); + pP2++; + + +/* The JFIF RGB to YUV Matrix for $00010000 = 1.0 + +[Y] [19595 38469 7471][R] +[U] = [-11056 -21712 32768][G] +[V] [32768 -27440 -5328][B] + +*/ + + y0 = 19595 * r0 + 38469 * g0 + 7471 * b0; + u0 = -11056 * r0 - 21712 * g0 + 32768 * b0; + v0 = 32768 * r0 - 27440 * g0 - 5328 * b0; + + y1 = 19595 * r1 + 38469 * g1 + 7471 * b1; + u1 = -11056 * r1 - 21712 * g1 + 32768 * b1; + v1 = 32768 * r1 - 27440 * g1 - 5328 * b1; + + y2 = 19595 * r2 + 38469 * g2 + 7471 * b2; + u2 = -11056 * r2 - 21712 * g2 + 32768 * b2; + v2 = 32768 * r2 - 27440 * g2 - 5328 * b2; + + y3 = 19595 * r3 + 38469 * g3 + 7471 * b3; + u3 = -11056 * r3 - 21712 * g3 + 32768 * b3; + v3 = 32768 * r3 - 27440 * g3 - 5328 * b3; + + /* mean the chroma for subsampling */ + + u = (u0+u1+u2+u3)>>2; + v = (v0+v1+v2+v3)>>2; + +#ifdef CCIR601 + + y0 = (y0 * 219)/255 + 1048576; + y1 = (y1 * 219)/255 + 1048576; + y2 = (y2 * 219)/255 + 1048576; + y3 = (y3 * 219)/255 + 1048576; + + u = (u * 224)/255 ; + v = (v * 224)/255 ; +#endif + + + *y1ptr++ = (y0 >> 16) ; + *y1ptr++ = (y1 >> 16) ; + *y2ptr++ = (y2 >> 16) ; + *y2ptr++ = (y3 >> 16) ; + + + *uptr++ = (u >> 16)+128 ; + *vptr++ = (v >> 16)+128 ; + + } + fwrite(y1buf, (cols & ~1), 1, yf); + fwrite(y2buf, (cols & ~1), 1, yf); + fwrite(ubuf, cols/2, 1, uf); + fwrite(vbuf, cols/2, 1, vf); + } + +/* I dunno why pm_close sees an error... get rid of it */ + +#ifdef ORIGINAL + pm_close(ifp); +#else + if(ifp != stdin) fclose(ifp); +#endif + fclose(yf); + fclose(uf); + fclose(vf); + exit(0); +} diff --git a/converter/ppm/qrttoppm.c b/converter/ppm/qrttoppm.c new file mode 100644 index 00000000..935463e7 --- /dev/null +++ b/converter/ppm/qrttoppm.c @@ -0,0 +1,69 @@ +/* qrttoppm.c - read a QRT ray-tracer output file and produce a portable pixmap +** +** Copyright (C) 1989 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "ppm.h" + +int +main( argc, argv ) + int argc; + char* argv[]; + { + FILE* ifp; + register pixel* pixelrow; + int rows, cols, row, col; + pixval maxval; + unsigned char* buf; + + + ppm_init( &argc, argv ); + + if ( argc > 2 ) + pm_usage( "[qrtfile]" ); + + if ( argc == 2 ) + ifp = pm_openr( argv[1] ); + else + ifp = stdin; + + /* Read in the QRT file. First the header. */ + cols = getc( ifp ); + cols += getc( ifp ) << 8; + rows = getc( ifp ); + rows += getc( ifp ) << 8; + + if ( cols <= 0 || rows <= 0 ) + pm_error( "invalid size: %d %d", cols, rows ); + maxval = 255; + + ppm_writeppminit( stdout, cols, rows, maxval, 0 ); + pixelrow = ppm_allocrow( cols ); + buf = (unsigned char *) malloc( 3 * cols ); + if ( buf == (unsigned char *) 0 ) + pm_error( "out of memory" ); + + for ( row = 0; row < rows; row++ ) + { + (void) getc( ifp ); /* discard */ + (void) getc( ifp ); /* linenum */ + if ( fread( buf, 3 * cols, 1, ifp ) != 1 ) + pm_error( "EOF / read error" ); + for ( col = 0; col < cols; col++ ) + PPM_ASSIGN( + pixelrow[col], buf[col], buf[cols + col], buf[2 * cols + col] ); + ppm_writeppmrow( stdout, pixelrow, cols, maxval, 0 ); + } + + pm_close( ifp ); + pm_close( stdout ); + + exit( 0 ); + } diff --git a/converter/ppm/rawtoppm.c b/converter/ppm/rawtoppm.c new file mode 100644 index 00000000..8d6c3b2c --- /dev/null +++ b/converter/ppm/rawtoppm.c @@ -0,0 +1,244 @@ +/* rawtoppm.c - convert raw RGB bytes into a portable pixmap +** +** Copyright (C) 1991 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include <math.h> +#include "ppm.h" + +static void dorowskip ARGS(( FILE* ifp, int rowskip )); + +int +main( argc, argv ) + int argc; + char* argv[]; + { + FILE* ifp; + pixel* pixrow; + register pixel* pP; + int argn, headerskip, rowskip, rows, cols, row, i; + register int col; + int order; +#define ORD_RGB 1 +#define ORD_RBG 2 +#define ORD_GRB 3 +#define ORD_GBR 4 +#define ORD_BRG 5 +#define ORD_BGR 6 +int interleave; +#define INT_PIX 1 +#define INT_ROW 2 + int val1, val2, val3; + gray* grow1; + gray* grow2; + gray* grow3; + register gray* g1P; + register gray* g2P; + register gray* g3P; + const char* const usage = "[-headerskip N] [-rowskip N] [-rgb|-rbg|-grb|-gbr|-brg|-bgr] [-interpixel|-interrow] <width> <height> [rawfile]"; + + + ppm_init( &argc, argv ); + + argn = 1; + headerskip = 0; + rowskip = 0; + order = ORD_RGB; + interleave = INT_PIX; + + while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' ) + { + if ( pm_keymatch( argv[argn], "-headerskip", 2 ) ) + { + ++argn; + if ( argn >= argc ) + pm_usage( usage ); + headerskip = atoi( argv[argn] ); + } + else if ( pm_keymatch( argv[argn], "-rowskip", 3 ) ) + { + ++argn; + if ( argn >= argc ) + pm_usage( usage ); + rowskip = atoi( argv[argn] ); + } + else if ( pm_keymatch( argv[argn], "-rgb", 3 ) ) + order = ORD_RGB; + else if ( pm_keymatch( argv[argn], "-rbg", 3 ) ) + order = ORD_RBG; + else if ( pm_keymatch( argv[argn], "-grb", 3 ) ) + order = ORD_GRB; + else if ( pm_keymatch( argv[argn], "-gbr", 3 ) ) + order = ORD_GBR; + else if ( pm_keymatch( argv[argn], "-brg", 3 ) ) + order = ORD_BRG; + else if ( pm_keymatch( argv[argn], "-bgr", 3 ) ) + order = ORD_BGR; + else if ( pm_keymatch( argv[argn], "-interpixel", 7 ) ) + interleave = INT_PIX; + else if ( pm_keymatch( argv[argn], "-interrow", 7 ) ) + interleave = INT_ROW; + else + pm_usage( usage ); + ++argn; + } + + if ( argn + 2 > argc ) + pm_usage( usage ); + + cols = atoi( argv[argn++] ); + rows = atoi( argv[argn++] ); + if ( cols <= 0 || rows <= 0 ) + pm_usage( usage ); + + if ( argn < argc ) + { + ifp = pm_openr( argv[argn] ); + ++argn; + } + else + ifp = stdin; + + if ( argn != argc ) + pm_usage( usage ); + + ppm_writeppminit( stdout, cols, rows, (pixval) 255, 0 ); + pixrow = ppm_allocrow( cols ); + + if ( interleave == INT_ROW ) + { + grow1 = pgm_allocrow( cols ); + grow2 = pgm_allocrow( cols ); + grow3 = pgm_allocrow( cols ); + } + + for ( i = 0; i < headerskip; ++i ) + { + val1 = getc( ifp ); + if ( val1 == EOF ) + pm_error( "EOF / read error" ); + } + + for ( row = 0; row < rows; ++row) + { + switch ( interleave ) + { + case INT_PIX: + for ( col = 0, pP = pixrow; col < cols; ++col, ++pP ) + { + val1 = getc( ifp ); + if ( val1 == EOF ) + pm_error( "EOF / read error" ); + val2 = getc( ifp ); + if ( val2 == EOF ) + pm_error( "EOF / read error" ); + val3 = getc( ifp ); + if ( val3 == EOF ) + pm_error( "EOF / read error" ); + switch ( order ) + { + case ORD_RGB: + PPM_ASSIGN( *pP, val1, val2, val3 ); + break; + case ORD_RBG: + PPM_ASSIGN( *pP, val1, val3, val2 ); + break; + case ORD_GRB: + PPM_ASSIGN( *pP, val2, val1, val3 ); + break; + case ORD_GBR: + PPM_ASSIGN( *pP, val3, val1, val2 ); + break; + case ORD_BRG: + PPM_ASSIGN( *pP, val2, val3, val1 ); + break; + case ORD_BGR: + PPM_ASSIGN( *pP, val3, val2, val1 ); + break; + } + } + dorowskip( ifp, rowskip ); + break; + + case INT_ROW: + for ( col = 0, g1P = grow1; col < cols; ++col, ++g1P ) + { + val1 = getc( ifp ); + if ( val1 == EOF ) + pm_error( "EOF / read error" ); + *g1P = val1; + } + dorowskip( ifp, rowskip ); + for ( col = 0, g2P = grow2; col < cols; ++col, ++g2P ) + { + val2 = getc( ifp ); + if ( val2 == EOF ) + pm_error( "EOF / read error" ); + *g2P = val2; + } + dorowskip( ifp, rowskip ); + for ( col = 0, g3P = grow3; col < cols; ++col, ++g3P ) + { + val3 = getc( ifp ); + if ( val3 == EOF ) + pm_error( "EOF / read error" ); + *g3P = val3; + } + dorowskip( ifp, rowskip ); + for ( col = 0, pP = pixrow, g1P = grow1, g2P = grow2, g3P = grow3; + col < cols; ++col, ++pP, ++g1P, ++g2P, ++g3P ) + { + switch ( order ) + { + case ORD_RGB: + PPM_ASSIGN( *pP, *g1P, *g2P, *g3P ); + break; + case ORD_RBG: + PPM_ASSIGN( *pP, *g1P, *g3P, *g2P ); + break; + case ORD_GRB: + PPM_ASSIGN( *pP, *g2P, *g1P, *g3P ); + break; + case ORD_GBR: + PPM_ASSIGN( *pP, *g3P, *g1P, *g2P ); + break; + case ORD_BRG: + PPM_ASSIGN( *pP, *g2P, *g3P, *g1P ); + break; + case ORD_BGR: + PPM_ASSIGN( *pP, *g3P, *g2P, *g1P ); + break; + } + } + break; + } + ppm_writeppmrow( stdout, pixrow, cols, (pixval) 255, 0 ); + } + + pm_close( ifp ); + pm_close( stdout ); + + exit( 0 ); + } + +static void +dorowskip( ifp, rowskip ) + FILE* ifp; + int rowskip; + { + int i, val; + + for ( i = 0; i < rowskip; ++i ) + { + val = getc( ifp ); + if ( val == EOF ) + pm_error( "EOF / read error" ); + } + } diff --git a/converter/ppm/rgb3toppm.c b/converter/ppm/rgb3toppm.c new file mode 100644 index 00000000..a666553c --- /dev/null +++ b/converter/ppm/rgb3toppm.c @@ -0,0 +1,88 @@ +/* rgb3toppm - combine three portable graymaps into one portable pixmap +** +** Copyright (C) 1991 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "ppm.h" + +int +main( argc, argv ) + int argc; + char* argv[]; + { + FILE* rfd; + FILE* gfd; + FILE* bfd; + gray* rrow; + gray* rP; + gray* grow; + gray* gP; + gray* brow; + gray* bP; + pixel* pixelrow; + register pixel* pP; + int rows, cols, trows, tcols, row, col; + gray rmaxval, gmaxval, bmaxval; + int rformat, gformat, bformat; + pixval pmaxval; + + + ppm_init( &argc, argv ); + + if ( argc != 4 ) + pm_usage( "<red pgmfile> <green pgmfile> <blue pgmfile> " ); + + rfd = pm_openr( argv[1] ); + gfd = pm_openr( argv[2] ); + bfd = pm_openr( argv[3] ); + + pgm_readpgminit( rfd, &cols, &rows, &rmaxval, &rformat ); + pgm_readpgminit( gfd, &tcols, &trows, &gmaxval, &gformat ); + if ( tcols != cols || trows != rows ) + pm_error( "all three graymaps must be the same size" ); + pgm_readpgminit( bfd, &tcols, &trows, &bmaxval, &bformat ); + if ( tcols != cols || trows != rows ) + pm_error( "all three graymaps must be the same size" ); + + pmaxval = rmaxval; + if ( gmaxval > pmaxval ) pmaxval = gmaxval; + if ( bmaxval > pmaxval ) pmaxval = bmaxval; + rrow = pgm_allocrow( cols ); + grow = pgm_allocrow( cols ); + brow = pgm_allocrow( cols ); + + ppm_writeppminit( stdout, cols, rows, pmaxval, 0 ); + pixelrow = ppm_allocrow( cols ); + + for ( row = 0; row < rows; row++ ) + { + pgm_readpgmrow( rfd, rrow, cols, rmaxval, rformat ); + pgm_readpgmrow( gfd, grow, cols, gmaxval, gformat ); + pgm_readpgmrow( bfd, brow, cols, bmaxval, bformat ); + + for ( col = 0, rP = rrow, gP = grow, bP = brow, pP = pixelrow; + col < cols; + ++col, ++rP, ++gP, ++bP, ++pP ) + { + if ( rmaxval != pmaxval ) *rP = (int) *rP * pmaxval / rmaxval; + if ( gmaxval != pmaxval ) *gP = (int) *gP * pmaxval / gmaxval; + if ( bmaxval != pmaxval ) *bP = (int) *bP * pmaxval / bmaxval; + PPM_ASSIGN( *pP, *rP, *gP, *bP ); + } + ppm_writeppmrow( stdout, pixelrow, cols, pmaxval, 0 ); + } + + pm_close( rfd ); + pm_close( gfd ); + pm_close( bfd ); + pm_close( stdout ); + + exit( 0 ); + } diff --git a/converter/ppm/sldtoppm.c b/converter/ppm/sldtoppm.c new file mode 100644 index 00000000..7da6d592 --- /dev/null +++ b/converter/ppm/sldtoppm.c @@ -0,0 +1,650 @@ +/* + + Convert an AutoCAD slide (.sld) file to PPM format + + An AutoCAD slide is a compressed sequence of vectors and filled + polygons. The ppmdraw package is used to scan convert these + geometrical objects into a portable pixmap. + + Author: + John Walker + Autodesk SA + Avenue des Champs-Montants 14b + CH-2074 MARIN + Switzerland + Usenet: kelvin@Autodesk.com + Fax: 038/33 88 15 + Voice: 038/33 76 33 + + Permission to use, copy, modify, and distribute this software and + its documentation for any purpose and without fee is hereby + granted, without any conditions or restrictions. This software is + provided "as is" without express or implied warranty. + +*/ + +#include <string.h> +#include <math.h> + +#include "ppm.h" +#include "ppmdraw.h" +#include "nstring.h" +#ifdef DEBUG +#include <assert.h> +#else +#define assert(x) +#endif + +/* Define a variable type accepting numbers -127 <= n <= 127. But note + that we still expect it to act UNSIGNED. */ + +#define smallint unsigned char /* Small integers */ + +#define TRUE 1 +#define FALSE 0 + +#define EOS '\0' + +/* Screen point */ + +struct spoint { + int x, y; +}; + +/* Screen polygon */ + +struct spolygon { + int npoints, /* Number of points in polygon */ + fill; /* Fill type */ + struct spoint pt[11]; /* Actual points */ +}; + +/* Screen vector */ + +struct svector { + struct spoint f; /* From point */ + struct spoint t; /* To point */ +}; + +static int extend ARGS((smallint ch)); +static int sli ARGS((void)); +static int slib ARGS((void)); +static void vscale ARGS((int *px, int *py)); +static void slider ARGS((void (*slvec) ARGS((struct svector *vec, int color)), + void (*slflood) ARGS((struct spolygon *poly, int color)) )); +static void slidefind ARGS((char *sname, int dironly, int ucasen)); +static void draw ARGS((struct svector *vec, int color)); +static void flood ARGS((struct spolygon *poly, int color)); + +static int ixdots, iydots; /* Screen size in dots */ +static FILE *slfile; /* Slide file descriptor */ +static int blither = FALSE; /* Dump slide file information ? */ +static int info = FALSE; /* Print header information */ +static pixel **pixels; /* Pixel map */ +static int pixcols, pixrows; /* Pixel map size */ +#define pixmaxval 255 /* Largest pixel value */ +static double uscale = -1; /* Uniform scale factor */ +static int sxsize = -1, sysize = -1; /* Scale to X, Y size ? */ + +#include "autocad.h" /* AutoCAD standard color assignments */ + +/* Local variables */ + +struct slhead { + char slh[17]; /* Primate-readable header */ + char sntype; /* Machine type (for number compat) */ + char slevel; /* Format type */ + short sxdots, sydots; /* Display X, Y dots */ + double sdsar; /* Display aspect ratio */ + short shwfill; /* Display hardware fill type */ + char spad; /* Pad to even byte length */ +}; + +static int adjust = FALSE; /* Adjust to correct aspect ratio ? */ +static struct slhead slfrof; /* Slide file header */ +static long xfac, yfac; /* Aspect ratio scale factors */ + +static int sdrawkcab; /* Slide drawing kinematic conversion of + ass-backwards data flag */ + +/* EXTEND -- Turn a smallint into an int with sign extension, whether + or not that happens automatically. */ + +static int extend(smallint ch) +{ + return ((int) ((ch & 0x80) ? (ch | ~0xFF) : ch)); +} + +/* SLI -- Input word from slide file */ + +static int sli() +{ + short wd; + + if (fread(&wd, sizeof wd, 1, slfile) != 1) { + pm_error("error reading slide file"); + } else { + if (sdrawkcab) { + wd = ((wd >> 8) & 0xFF) | (wd << 8); + } + } + return wd; +} + +/* SLIB -- Input byte from slide file */ + +static int slib() +{ + smallint ch = 0; + + if (fread(&ch, sizeof ch, 1, slfile) != 1) { + pm_error("error reading slide file"); + } + return extend(ch); +} + +/* VSCALE -- scale screen coordinates for mismatched display. */ + +static void vscale(px, py) + int *px, *py; +{ + *px = (((unsigned) *px) * xfac) >> 16; + *py = (((unsigned) *py) * yfac) >> 16; +} + +/* SLIDER -- Read slide file. This is called with the name of the + file to be read and function pointers to the routines + which process vectors and polygon fill requests + respectively. +*/ + +static void slider(slvec, slflood) + void (*slvec) ARGS((struct svector *vec, int color)); + void (*slflood) ARGS((struct spolygon *poly, int color)); +{ + int i, rescale; + unsigned char ubfr[4]; /* Utility character buffer */ + int lx, ly; /* Last x and y point */ + int slx, sly; /* Last x and y scaled screen point */ + struct svector vec; /* Screen vector */ + struct spolygon poly; /* Screen polygon */ + unsigned short cw; /* Control word */ + double dsar; /* Screen aspect ratio */ + long ldsar; /* Scaled long DSAR */ + short rtest; /* Value to test byte reversal */ + short btest = 0x1234; /* Value to test byte-reversal */ + static struct slhead slhi = /* Master slide header sample */ + {"AutoCAD Slide\r\n\32", 86,2, 0,0, 0.0, 0}; + int curcolor = 7; /* Current vector color */ + pixel rgbcolor; /* Pixel used to clear pixmap */ + + lx = ly = 32000; + + /* Process the header of the slide file. */ + + sdrawkcab = FALSE; /* Initially guess byte order is OK */ + fread(slfrof.slh, 17, 1, slfile); + fread(&slfrof.sntype, sizeof(char), 1, slfile); + fread(&slfrof.slevel, sizeof(char), 1, slfile); + fread(&slfrof.sxdots, sizeof(short), 1, slfile); + fread(&slfrof.sydots, sizeof(short), 1, slfile); + fread(ubfr, 4, 1, slfile); + fread(&slfrof.shwfill, sizeof(short), 1, slfile); + fread(&rtest, sizeof rtest, 1, slfile); + + /* Verify that slide format is compatible with this program. */ + + if (STREQ(slfrof.slh, slhi.slh)) { + pm_error("this is not an AutoCAD slide file."); + } + + /* Verify that the number format and file level in the header are + compatible. All slides written by versions of AutoCAD released + since September of 1987 are compatible with this format. */ + + if ((slfrof.sntype != slhi.sntype) || (slfrof.slevel != slhi.slevel)) { + pm_error("incompatible slide file format"); + } + + /* Build SDSAR value from long scaled version. */ + + ldsar = 0L; + for (i = 3; i >= 0; i--) { + ldsar = (ldsar << 8) | ubfr[i]; + } + slfrof.sdsar = ((double) ldsar) / 1E7; + + /* Examine the byte order test value. If it's backwards, set the + byte-reversal flag and correct all of the values we've read in + so far. */ + + if (btest != rtest) { + sdrawkcab = TRUE; +#define rshort(x) x = ((x >> 8) & 0xFF) | (x << 8) + rshort(slfrof.sxdots); + rshort(slfrof.sydots); + rshort(slfrof.shwfill); +#undef rshort + } + + /* Dump the header if we're blithering. */ + + if (blither || info) { + pm_message("Slide file type %d, level %d, hwfill type %d.", + slfrof.sntype, slfrof.slevel, slfrof.shwfill); + pm_message("Original screen size %dx%d, aspect ratio %.3f.", + slfrof.sxdots + 1, slfrof.sydots + 1, slfrof.sdsar); + pm_message("Byte order is %s.", + sdrawkcab ? "being reversed" : "the same"); + } + + /* If the display aspect ratio indicates that the pixels on the + sending screen were not square, adjust the size of the + generated bitmap to correct the aspect ratio to square the + pixels. + + We always correct the aspect ratio by adjusting the width of + the image. This guarantees that output from the SHADE command, + which is essentially scan-line data written in vector form, + will not be corrupted. */ + + dsar = ((double) slfrof.sxdots) / slfrof.sydots; + if (fabs(slfrof.sdsar - dsar) > 0.0001) { + if (adjust) { + ixdots = slfrof.sxdots * (slfrof.sdsar / dsar) + 0.5; + iydots = slfrof.sydots; + dsar = ((double) ixdots) / iydots; + } else { + pm_message("Warning - pixels on source screen were non-square."); + pm_message(" Specifying -adjust will correct image width to compensate."); + ixdots = slfrof.sxdots; + iydots = slfrof.sydots; + dsar = slfrof.sdsar; + } + } else { + /* Source pixels were square. */ + ixdots = slfrof.sxdots; + iydots = slfrof.sydots; + dsar = slfrof.sdsar; + adjust = FALSE; /* Mark no adjustment needed */ + } + + /* If there's a uniform scale factor specified, apply it. */ + + if (uscale > 0) { + ixdots = (ixdots * uscale) + 0.5; + iydots = (iydots * uscale) + 0.5; + } + + /* If the image is to be stretched to a given width, set the + output image sizes accordingly. If only a height or width is + given, scale the other direction proportionally to preserve the + aspect ratio. */ + + if (sxsize > 0) { + if (sysize > 0) { + iydots = sysize - 1; + } else { + iydots = ((((long) iydots) * (sxsize - 1)) + + (iydots / 2)) / ixdots; + } + ixdots = sxsize - 1; + } else if (sysize > 0) { + if (sxsize > 0) { + ixdots = sxsize - 1; + } else { + ixdots = ((((long) ixdots) * (sysize - 1)) + + (ixdots / 2)) / iydots; + } + iydots = sysize - 1; + } + + if (adjust) { + pm_message( + "Resized from %dx%d to %dx%d to correct pixel aspect ratio.", + slfrof.sxdots + 1, slfrof.sydots + 1, ixdots + 1, iydots + 1); + } + + /* Allocate image buffer and clear it to black. */ + + pixels = ppm_allocarray(pixcols = ixdots + 1, pixrows = iydots + 1); + PPM_ASSIGN(rgbcolor, 0, 0, 0); + ppmd_filledrectangle(pixels, pixcols, pixrows, pixmaxval, 0, 0, + pixcols, pixrows, PPMD_NULLDRAWPROC, + (char *) &rgbcolor); + + if ((rescale = slfrof.sxdots != ixdots || + slfrof.sydots != iydots || + slfrof.sdsar != dsar) != 0) { + + /* Rescale all coords. so they'll look (more or less) + right on this display. */ + + xfac = (ixdots + 1) * 0x10000L; + xfac /= (long) (slfrof.sxdots + 1); + yfac = (iydots + 1) * 0x10000L; + yfac /= (long) (slfrof.sydots + 1); + if (dsar < slfrof.sdsar) { + yfac = yfac * dsar / slfrof.sdsar; + } else { + xfac = xfac * slfrof.sdsar / dsar; + } + } + + poly.npoints = 0; /* No flood in progress. */ + + while ((cw = sli()) != 0xFC00) { + switch (cw & 0xFF00) { + case 0xFB00: /* Short vector compressed */ + vec.f.x = lx + extend(cw & 0xFF); + vec.f.y = ly + slib(); + vec.t.x = lx + slib(); + vec.t.y = ly + slib(); + lx = vec.f.x; + ly = vec.f.y; + if (rescale) { + vscale(&vec.f.x, &vec.f.y); + vscale(&vec.t.x, &vec.t.y); + } + (*slvec)(&vec, curcolor);/* Draw vector on screen */ + slx = vec.f.x; /* Save scaled point */ + sly = vec.f.y; + break; + + case 0xFC00: /* End of file */ + break; + + case 0xFD00: /* Flood command */ + vec.f.x = sli(); + vec.f.y = sli(); + if ((int) vec.f.y < 0) { /* start or end */ + if (poly.npoints != 0) { /* end? */ + if (poly.npoints > 2 && poly.npoints < 11) { + (*slflood)(&poly, curcolor); + } else { + pm_error("Bad polygon vertex count (%d)", + poly.npoints); + } + poly.npoints = 0; + } else { + poly.fill = -vec.f.y; /* Start */ + } + } else { /* Polygon vertex */ + if (poly.npoints < 10) { + if (rescale) { + vscale(&vec.f.x, &vec.f.y); + } + poly.pt[poly.npoints].x = vec.f.x; + poly.pt[poly.npoints].y = vec.f.y; + } + poly.npoints++; + } + break; + + case 0xFE00: /* Common endpoint compressed */ + vec.f.x = lx + extend(cw & 0xFF); + vec.f.y = ly + slib(); + lx = vec.f.x; + ly = vec.f.y; + vec.t.x = slx; + vec.t.y = sly; + if (rescale) { + vscale(&vec.f.x, &vec.f.y); + } + (*slvec)(&vec, curcolor);/* Draw vector */ + slx = vec.f.x; /* Save scaled point */ + sly = vec.f.y; + break; + + case 0xFF00: /* Change color */ + curcolor = cw & 0xFF; + break; + + default: /* Co-ordinates */ + lx = vec.f.x = cw; + ly = vec.f.y = sli(); + vec.t.x = sli(); + vec.t.y = sli(); + if (rescale) { + vscale(&vec.f.x, &vec.f.y); + vscale(&vec.t.x, &vec.t.y); + } + (*slvec)(&vec, curcolor); + slx = vec.f.x; /* Save scaled point */ + sly = vec.f.y; + break; + } + } +} + +/* SLIDEFIND -- Find a slide in a library or, if DIRONLY is + nonzero, print a directory listing of the library. + If UCASEN is nonzero, the requested slide name is + converted to upper case. */ + +static void slidefind(sname, dironly, ucasen) + char *sname; + int dironly, ucasen; +{ + char uname[32]; + unsigned char libent[36]; + long pos; + + if (dironly) { + pm_message("Slides in library:"); + } else { + int i; + char *ip = sname; + + for (i = 0; i < 31; i++) { + char ch = *ip++; + if (ch == EOS) { + break; + } + if (ucasen && ISLOWER(ch)) { + ch = TOUPPER(ch); + } + uname[i] = ch; + } + uname[i] = EOS; + } + + /* Read slide library header and verify. */ + + if ((fread(libent, 32, 1, slfile) != 1) || + (!STREQ((char *)libent, "AutoCAD Slide Library 1.0\015\012\32"))) { + pm_error("not an AutoCAD slide library file."); + } + pos = 32; + + /* Search for a slide with the requested name. */ + + while (TRUE) { + if ((fread(libent, 36, 1, slfile) != 1) || + (strlen((char *)libent) == 0)) { + if (dironly) { + return; + } + pm_error("slide %s not in library.", sname); + } + pos += 36; + if (dironly) { + pm_message(" %s", libent); + } else if (STREQ((char *)libent, uname)) { + long dpos = (((((libent[35] << 8) | libent[34]) << 8) | + libent[33]) << 8) | libent[32]; + if ((slfile == stdin) || (fseek(slfile, dpos, 0) == -1)) { + dpos -= pos; + + while (dpos-- > 0) { + (void) getc(slfile); + } + } + break; + } + } +} + +/* DRAW -- Draw a vector in the given AutoCAD color. */ + +static void draw(vec, color) + struct svector *vec; + int color; +{ + pixel rgbcolor; + + if (blither) { + pm_message("Vector (%d, %d) - (%d, %d) Color %d", + vec->f.x, vec->f.y, vec->t.x, vec->t.y, color); + } + assert(vec->f.x >= 0 && vec->f.x < pixcols); + assert(vec->f.y >= 0 && vec->f.y < pixrows); + assert(vec->t.x >= 0 && vec->t.x < pixcols); + assert(vec->t.y >= 0 && vec->t.y < pixrows); + PPM_ASSIGN(rgbcolor, + acadcol[color][0], acadcol[color][1], acadcol[color][2]); + ppmd_line(pixels, pixcols, pixrows, pixmaxval, + vec->f.x, iydots - vec->f.y, vec->t.x, iydots - vec->t.y, + PPMD_NULLDRAWPROC, + (char *) &rgbcolor); +} + +/* FLOOD -- Draw a filled polygon. */ + +static void +flood(struct spolygon * const poly, + int const color) { + + int i; + struct fillobj * handle; + pixel rgbcolor; + + handle = ppmd_fill_create(); + + if (blither) { + pm_message("Polygon: %d points, fill type %d, color %d", + poly->npoints, poly->fill, color); + for (i = 0; i < poly->npoints; i++) { + pm_message(" Point %d: (%d, %d)", i + 1, + poly->pt[i].x, poly->pt[i].y); + } + } + + PPM_ASSIGN(rgbcolor, + acadcol[color][0], acadcol[color][1], acadcol[color][2]); + for (i = 0; i < poly->npoints; i++) { + assert(poly->pt[i].x >= 0 && poly->pt[i].x < pixcols); + assert(poly->pt[i].y >= 0 && poly->pt[i].y < pixrows); + ppmd_line(pixels, pixcols, pixrows, pixmaxval, + poly->pt[i].x, iydots - poly->pt[i].y, + poly->pt[(i + 1) % poly->npoints].x, + iydots - poly->pt[(i + 1) % poly->npoints].y, + ppmd_fill_drawproc, handle); + } + ppmd_fill(pixels, pixcols, pixrows, pixmaxval, + handle, PPMD_NULLDRAWPROC, (char *) &rgbcolor); + + ppmd_fill_destroy(handle); +} + +/* Main program. */ + +int main(argc, argv) + int argc; + char *argv[]; +{ + int argn; + const char * const usage = "[-verbose] [-info] [-adjust] [-scale <s>]\n\ + [-dir] [-lib|-Lib <name>]\n\ + [-xsize|-width <x>] [-ysize|-height <y>] [sldfile]"; + int scalespec = FALSE, widspec = FALSE, hgtspec = FALSE, dironly = FALSE, + ucasen; + char *slobber = (char *) 0; /* Slide library item */ + + + ppm_init(&argc, argv); + argn = 1; + + while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0') { + if (pm_keymatch(argv[argn], "-verbose", 2)) { + blither = TRUE; + } else if (pm_keymatch(argv[argn], "-adjust", 2)) { + adjust = TRUE; + } else if (pm_keymatch(argv[argn], "-dir", 2)) { + dironly = TRUE; + } else if (pm_keymatch(argv[argn], "-info", 2)) { + info = TRUE; + } else if (pm_keymatch(argv[argn], "-lib", 2)) { + if (slobber != (char *) 0) { + pm_error("already specified a library item"); + } + ucasen = argv[argn][1] != 'L'; + argn++; + if (argn == argc) { + pm_usage(usage); + } + slobber = argv[argn]; + } else if (pm_keymatch(argv[argn], "-scale", 2)) { + if (scalespec) { + pm_error("already specified a scale factor"); + } + argn++; + if ((argn == argc) || (sscanf(argv[argn], "%lf", &uscale) != 1)) + pm_usage(usage); + if (uscale <= 0.0) { + pm_error("scale factor must be greater than 0"); + } + scalespec = TRUE; + } else if (pm_keymatch(argv[argn], "-xsize", 2) || + pm_keymatch(argv[argn], "-width", 2)) { + if (widspec) { + pm_error("already specified a width/xsize"); + } + argn++; + if ((argn == argc) || (sscanf(argv[argn], "%d", &sxsize) != 1)) + pm_usage(usage); + widspec = TRUE; + } else if (pm_keymatch(argv[argn], "-ysize", 2) || + pm_keymatch(argv[argn], "-height", 2)) { + if (hgtspec) { + pm_error("already specified a height/ysize"); + } + argn++; + if ((argn == argc) || (sscanf(argv[argn], "%d", &sysize) != 1)) + pm_usage(usage); + hgtspec = TRUE; + } else { + pm_usage(usage); + } + argn++; + } + + /* If a file name is specified, open it. Otherwise read from + standard input. */ + + if (argn < argc) { + slfile = pm_openr(argv[argn]); + argn++; + } else { + slfile = stdin; + } + + if (argn != argc) { /* Extra bogus arguments ? */ + pm_usage(usage); + } + + /* If we're extracting an item from a slide library, position the + input stream to the start of the chosen slide. */ + + if (dironly || (slobber != (char *) 0)) { + slidefind(slobber, dironly, ucasen); + } + + if (!dironly) { + slider(draw, flood); + ppm_writeppm(stdout, pixels, pixcols, pixrows, pixmaxval, FALSE); + } + pm_close(slfile); + pm_close(stdout); + exit(0); +} diff --git a/converter/ppm/spctoppm.c b/converter/ppm/spctoppm.c new file mode 100644 index 00000000..3eea7821 --- /dev/null +++ b/converter/ppm/spctoppm.c @@ -0,0 +1,212 @@ +/* spctoppm.c - read a compressed Spectrum file and produce a portable pixmap +** +** Copyright (C) 1991 by Steve Belczyk and Jef Poskanzer +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "ppm.h" + +#define ROWS 200 +#define COLS 320 +#define MAXVAL 7 + +static void DoBitmap ARGS(( FILE* ifp )); +static void DoChar ARGS(( int n, char c )); +static void DoColormap ARGS(( FILE* ifp )); + +static char screen[ROWS*COLS/2]; +static short sscreen[ROWS*COLS/4]; +static pixel pal[ROWS][48]; +static long colormap_length, bitmap_length; + +int +main( argc, argv ) + int argc; + char* argv[]; + { + FILE* ifp; + char c1, c2; + pixel* pixelrow; + register pixel* pP; + int row, col; + + + ppm_init( &argc, argv ); + + /* Check args. */ + if ( argc > 2 ) + pm_usage( "[spcfile]" ); + + if ( argc == 2 ) + ifp = pm_openr( argv[1] ); + else + ifp = stdin; + + /* Check SPC file header. */ + c1 = getc( ifp ); + c2 = getc( ifp ); + + if ( ( c1 != 'S' ) || ( c2 != 'P' ) ) + pm_error( "not a Spectrum picture" ); + + /* Skip reserved bytes. */ + getc( ifp ); + getc( ifp ); + + /* Get length of bitmap data. */ + (void) pm_readbiglong( ifp, &bitmap_length ); + + /* and colormap */ + (void) pm_readbiglong( ifp, &colormap_length ); + + /* Process bitmap. */ + DoBitmap( ifp ); + + /* Process colormap. */ + DoColormap( ifp ); + + pm_close( ifp ); + + /* Write the PPM file. */ + ppm_writeppminit( stdout, COLS, ROWS, (pixval) MAXVAL, 0 ); + pixelrow = ppm_allocrow( COLS ); + + for ( row = 0; row < ROWS; ++row ) + { + for ( col = 0, pP = pixelrow; col < COLS; ++col, ++pP ) + { + int c, ind, b, plane, x1; + + /* Compute pixel value. */ + ind = ( 80 * row ) + ( ( col >> 4 ) << 2 ); + b = 0x8000 >> (col & 0xf); + c = 0; + for ( plane = 0; plane < 4; ++plane ) + if ( b & sscreen[ind+plane] ) + c |= (1 << plane); + + /* Compute palette index. */ + x1 = 10 * c; + if ( c & 1 ) + x1 -= 5; + else + ++x1; + if ( ( col >= x1 ) && ( col < ( x1 + 160 ) ) ) + c += 16; + if ( col >= ( x1 + 160 ) ) + c += 32; + + /* Store the proper color. */ + *pP = pal[row][c]; + } + ppm_writeppmrow( stdout, pixelrow, COLS, (pixval) MAXVAL, 0 ); + } + + pm_close( stdout ); + + exit( 0 ); + } + +static void +DoBitmap( ifp ) + FILE* ifp; + { + int i; + long count, data; + signed char h, c; + + /* Zero out first scan line. */ + for ( i = 0; i < 160; ++i ) + screen[i] = 0; + + /* 'count' counts number of input bytes. */ + count = 0; + + /* 'data' counts just data bytes. */ + data = 0; + + while ( count < bitmap_length ) + { + /* Get next record header. */ + h = getc( ifp ); + ++count; + + if ( ( h >= 0 ) && ( count < bitmap_length ) ) + { + for ( i = 0; i <= h; ++i ) + { + c = getc( ifp ); + ++count; + DoChar( data, c ); + ++data; + } + } + else if ( ( h < 0 ) && ( count < bitmap_length ) ) + { + c = getc( ifp ); + ++count; + + for ( i = 0; i < ( 2 - h ); ++i ) + { + DoChar( data, c ); + ++data; + } + } + } + + /* Convert the char version of the screen to short. */ + for ( i = 0; i < ROWS*COLS/4; ++i ) + sscreen[i] = ( screen[i<<1] << 8 ) + ( 0xff & screen[(i<<1)+1] ); + } + +#if __STDC__ +static void +DoChar( int n, char c ) +#else /*__STDC__*/ +static void +DoChar( n, c ) + int n; + char c; +#endif /*__STDC__*/ + { + int i; + + /* Compute screen index. */ + i = 160 + 2 * ( n / 7960 ) + 8 * ( ( n % 7960 ) / 2 ) + ( n & 1 ); + screen[i] = c; + } + +static void +DoColormap( ifp ) + FILE* ifp; + { + int i, j, b; + short mask; + + /* Clear first three palettes. */ + for ( j = 0; j < 48; ++j ) + PPM_ASSIGN( pal[0][j], 0, 0, 0 ); + + /* Read the palettes. */ + for ( i = 1; i < ROWS; ++i ) + for ( j = 0; j < 3; ++j ) + { + (void) pm_readbigshort( ifp, &mask ); + for ( b = 0; b < 15; ++b ) + if ( mask & ( 1 << b ) ) + { + short k; + (void) pm_readbigshort( ifp, &k ); + PPM_ASSIGN( pal[i][(j*16)+b], + ( k & 0x700 ) >> 8, + ( k & 0x070 ) >> 4, + ( k & 0x007 ) ); + } + } + } diff --git a/converter/ppm/sputoppm.c b/converter/ppm/sputoppm.c new file mode 100644 index 00000000..2671bffa --- /dev/null +++ b/converter/ppm/sputoppm.c @@ -0,0 +1,108 @@ +/* sputoppm.c - read an uncompressed Spectrum file and produce a portable pixmap +** +** Copyright (C) 1991 by Steve Belczyk and Jef Poskanzer +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "ppm.h" + +#define ROWS 200 +#define COLS 320 +#define MAXVAL 7 + +static pixel pal[ROWS][48]; /* Spectrum palettes, three per row */ +static short screen[ROWS*COLS/4]; /* simulates the Atari's video RAM */ + +int +main( argc, argv ) + int argc; + char* argv[]; + { + FILE* ifp; + int i, j; + pixel* pixelrow; + register pixel* pP; + int row, col; + + + ppm_init( &argc, argv ); + + /* Check args. */ + if ( argc > 2 ) + pm_usage( "[spufile]" ); + + if ( argc == 2 ) + ifp = pm_openr( argv[1] ); + else + ifp = stdin; + + /* Read the SPU file */ + + /* Read the screen data. */ + for ( i = 0; i < ROWS*COLS/4; ++i ) + (void) pm_readbigshort( ifp, &screen[i] ); + + /* Clear the first palette line. */ + for ( j = 0; j < 48; ++j ) + PPM_ASSIGN( pal[0][j], 0, 0, 0 ); + + /* Read the palettes. */ + for ( i = 1; i < ROWS; ++i ) + for ( j = 0; j < 48; ++j ) + { + short k; + (void) pm_readbigshort( ifp, &k ); + PPM_ASSIGN( pal[i][j], + ( k & 0x700 ) >> 8, + ( k & 0x070 ) >> 4, + ( k & 0x007 ) ); + } + + pm_close( ifp ); + + /* Ok, get set for writing PPM. */ + ppm_writeppminit( stdout, COLS, ROWS, (pixval) MAXVAL, 0 ); + pixelrow = ppm_allocrow( COLS ); + + /* Now do the conversion. */ + for ( row = 0; row < ROWS; ++row ) + { + for ( col = 0, pP = pixelrow; col < COLS; ++col, ++pP ) + { + int c, ind, b, plane, x1; + + /* Compute pixel value. */ + ind = 80 * row + ( ( col >> 4 ) << 2 ); + b = 0x8000 >> (col & 0xf); + c = 0; + for ( plane = 0; plane < 4; ++plane ) + if ( b & screen[ind+plane] ) + c |= (1 << plane); + + /* Compute palette index. */ + x1 = 10 * c; + if ( c & 1 ) + x1 -= 5; + else + ++x1; + if ( ( col >= x1 ) && ( col < ( x1 + 160 ) ) ) + c += 16; + if ( col >= ( x1 + 160 ) ) + c += 32; + + /* Store the proper color. */ + *pP = pal[row][c]; + } + ppm_writeppmrow( stdout, pixelrow, COLS, (pixval) MAXVAL, 0 ); + } + + pm_close( stdout ); + + exit( 0 ); + } diff --git a/converter/ppm/tgatoppm.c b/converter/ppm/tgatoppm.c new file mode 100644 index 00000000..9f2bc4c0 --- /dev/null +++ b/converter/ppm/tgatoppm.c @@ -0,0 +1,462 @@ +/* tgatoppm.c - read a TrueVision Targa file and write a portable pixmap +** +** Partially based on tga2rast, version 1.0, by Ian MacPhedran. +** +** Copyright (C) 1989 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#define _BSD_SOURCE 1 /* Make sure strdup() is in string.h */ +#define _XOPEN_SOURCE 500 /* Make sure strdup() is in string.h */ + +#include <string.h> +#include "ppm.h" +#include "tga.h" +#include "shhopt.h" +#include "nstring.h" + +#define MAXCOLORS 16384 + +static int mapped, rlencoded; + +static pixel ColorMap[MAXCOLORS]; +static gray AlphaMap[MAXCOLORS]; +static int RLE_count = 0, RLE_flag = 0; + +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char *input_filename; + unsigned int headerdump; + const char *alpha_filename; + unsigned int alpha_stdout; +}; + + + +static void +parseCommandLine(int argc, char ** argv, + struct cmdlineInfo *cmdlineP) { +/*---------------------------------------------------------------------------- + Note that many of the strings that this function returns in the + *cmdlineP structure are actually in the supplied argv array. And + sometimes, one of these strings is actually just a suffix of an entry + in argv! +-----------------------------------------------------------------------------*/ + optEntry *option_def = malloc(100*sizeof(optEntry)); + /* Instructions to OptParseOptions2 on how to parse our options. + */ + optStruct3 opt; + + unsigned int option_def_index; + + unsigned int alpha_spec; + + option_def_index = 0; /* incremented by OPTENTRY */ + OPTENT3(0, "headerdump", OPT_FLAG, NULL, &cmdlineP->headerdump, 0); + OPTENT3(0, "debug", OPT_FLAG, NULL, &cmdlineP->headerdump, 0); + OPTENT3(0, "alphaout", OPT_STRING, &cmdlineP->alpha_filename, + &alpha_spec, 0); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ + + optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + if (argc - 1 == 0) + cmdlineP->input_filename = "="; /* he wants stdin */ + else if (argc - 1 == 1) + cmdlineP->input_filename = strdup(argv[1]); + else + pm_error("Too many arguments. The only argument accepted " + "is the input file specification"); + + if (alpha_spec && + STREQ(cmdlineP->alpha_filename, "-")) + cmdlineP->alpha_stdout = 1; + else + cmdlineP->alpha_stdout = 0; + + if (!alpha_spec) + cmdlineP->alpha_filename = NULL; +} + + + + +static unsigned char +getbyte(FILE * const ifP) { + + unsigned char c; + + if ( fread( (char*) &c, 1, 1, ifP ) != 1 ) + pm_error( "EOF / read error" ); + + return c; +} + + + +static void +get_pixel(FILE * const ifP, pixel * dest, int Size, gray *alpha_p) { + + static pixval Red, Grn, Blu; + static pixval Alpha; + unsigned char j, k; + static unsigned int l; + + /* Check if run length encoded. */ + if ( rlencoded ) + { + if ( RLE_count == 0 ) + { /* Have to restart run. */ + unsigned char i; + i = getbyte( ifP ); + RLE_flag = ( i & 0x80 ); + if ( RLE_flag == 0 ) + /* Stream of unencoded pixels. */ + RLE_count = i + 1; + else + /* Single pixel replicated. */ + RLE_count = i - 127; + /* Decrement count & get pixel. */ + --RLE_count; + } + else + { /* Have already read count & (at least) first pixel. */ + --RLE_count; + if ( RLE_flag != 0 ) + /* Replicated pixels. */ + goto PixEncode; + } + } + /* Read appropriate number of bytes, break into RGB. */ + switch ( Size ) + { + case 8: /* Grayscale, read and triplicate. */ + Red = Grn = Blu = l = getbyte( ifP ); + Alpha = 0; + break; + + case 16: /* 5 bits each of red green and blue. */ + case 15: /* Watch byte order. */ + j = getbyte( ifP ); + k = getbyte( ifP ); + l = ( (unsigned int) k << 8 ) + j; + Red = ( k & 0x7C ) >> 2; + Grn = ( ( k & 0x03 ) << 3 ) + ( ( j & 0xE0 ) >> 5 ); + Blu = j & 0x1F; + Alpha = 0; + break; + + case 32: /* 8 bits each of blue, green, red, and alpha */ + case 24: /* 8 bits each of blue, green, and red. */ + Blu = getbyte( ifP ); + Grn = getbyte( ifP ); + Red = getbyte( ifP ); + if ( Size == 32 ) + Alpha = getbyte( ifP ); + else + Alpha = 0; + l = 0; + break; + + default: + pm_error( "unknown pixel size (#2) - %d", Size ); + } + +PixEncode: + if ( mapped ) { + *dest = ColorMap[l]; + *alpha_p = AlphaMap[l]; + } else { + PPM_ASSIGN( *dest, Red, Grn, Blu ); + *alpha_p = Alpha; + } + } + + + +static void +readtga(FILE * const ifP, struct ImageHeader * tgaP) { + + unsigned char flags; + ImageIDField junk; + + tgaP->IdLength = getbyte( ifP ); + tgaP->CoMapType = getbyte( ifP ); + tgaP->ImgType = getbyte( ifP ); + tgaP->Index_lo = getbyte( ifP ); + tgaP->Index_hi = getbyte( ifP ); + tgaP->Length_lo = getbyte( ifP ); + tgaP->Length_hi = getbyte( ifP ); + tgaP->CoSize = getbyte( ifP ); + tgaP->X_org_lo = getbyte( ifP ); + tgaP->X_org_hi = getbyte( ifP ); + tgaP->Y_org_lo = getbyte( ifP ); + tgaP->Y_org_hi = getbyte( ifP ); + tgaP->Width_lo = getbyte( ifP ); + tgaP->Width_hi = getbyte( ifP ); + tgaP->Height_lo = getbyte( ifP ); + tgaP->Height_hi = getbyte( ifP ); + tgaP->PixelSize = getbyte( ifP ); + flags = getbyte( ifP ); + tgaP->AttBits = flags & 0xf; + tgaP->Rsrvd = ( flags & 0x10 ) >> 4; + tgaP->OrgBit = ( flags & 0x20 ) >> 5; + tgaP->IntrLve = ( flags & 0xc0 ) >> 6; + + if ( tgaP->IdLength != 0 ) + fread( junk, 1, (int) tgaP->IdLength, ifP ); +} + + + +static void +get_map_entry(FILE * const ifP, pixel * Value, int Size, gray * Alpha) { + + unsigned char j, k, r, g, b, a; + + /* Read appropriate number of bytes, break into rgb & put in map. */ + switch ( Size ) + { + case 8: /* Grayscale, read and triplicate. */ + r = g = b = getbyte( ifP ); + a = 0; + break; + + case 16: /* 5 bits each of red green and blue. */ + case 15: /* Watch for byte order. */ + j = getbyte( ifP ); + k = getbyte( ifP ); + r = ( k & 0x7C ) >> 2; + g = ( ( k & 0x03 ) << 3 ) + ( ( j & 0xE0 ) >> 5 ); + b = j & 0x1F; + a = 0; + break; + + case 32: /* 8 bits each of blue, green, red, and alpha */ + case 24: /* 8 bits each of blue green and red. */ + b = getbyte( ifP ); + g = getbyte( ifP ); + r = getbyte( ifP ); + if ( Size == 32 ) + a = getbyte( ifP ); + else + a = 0; + break; + + default: + pm_error( "unknown colormap pixel size (#2) - %d", Size ); + } + PPM_ASSIGN( *Value, r, g, b ); + *Alpha = a; +} + + + +static void +dumpHeader(struct ImageHeader const tga_head) { + const char * imgTypeName; + switch(tga_head.ImgType) { + case TGA_Map: imgTypeName = "TGA_Map"; break; + case TGA_RGB: imgTypeName = "TGA_RGB"; break; + case TGA_Mono: imgTypeName = "TGA_Mono"; break; + case TGA_RLEMap: imgTypeName = "TGA_RLEMap"; break; + case TGA_RLERGB: imgTypeName = "TGA_RLERGB"; break; + case TGA_RLEMono: imgTypeName = "TGA_RLEMono"; break; + case TGA_CompMap: imgTypeName = "TGA_CompMap"; break; + case TGA_CompMap4: imgTypeName = "TGA_CompMap4"; break; + default: imgTypeName = "unknown"; + } + pm_message( "IdLength = %d", (int) tga_head.IdLength ); + pm_message( "CoMapType = %d", (int) tga_head.CoMapType ); + pm_message( "ImgType = %d (%s)", (int) tga_head.ImgType, imgTypeName ); + pm_message( "Index_lo = %d", (int) tga_head.Index_lo ); + pm_message( "Index_hi = %d", (int) tga_head.Index_hi ); + pm_message( "Length_lo = %d", (int) tga_head.Length_lo ); + pm_message( "Length_hi = %d", (int) tga_head.Length_hi ); + pm_message( "CoSize = %d", (int) tga_head.CoSize ); + pm_message( "X_org_lo = %d", (int) tga_head.X_org_lo ); + pm_message( "X_org_hi = %d", (int) tga_head.X_org_hi ); + pm_message( "Y_org_lo = %d", (int) tga_head.Y_org_lo ); + pm_message( "Y_org_hi = %d", (int) tga_head.Y_org_hi ); + pm_message( "Width_lo = %d", (int) tga_head.Width_lo ); + pm_message( "Width_hi = %d", (int) tga_head.Width_hi ); + pm_message( "Height_lo = %d", (int) tga_head.Height_lo ); + pm_message( "Height_hi = %d", (int) tga_head.Height_hi ); + pm_message( "PixelSize = %d", (int) tga_head.PixelSize ); + pm_message( "AttBits = %d", (int) tga_head.AttBits ); + pm_message( "Rsrvd = %d", (int) tga_head.Rsrvd ); + pm_message( "OrgBit = %d", (int) tga_head.OrgBit ); + pm_message( "IntrLve = %d", (int) tga_head.IntrLve ); +} + + + +int +main(int argc, char * argv[]) { + + struct cmdlineInfo cmdline; + struct ImageHeader tga_head; + FILE* ifP; + FILE *imageout_file, *alpha_file; + int rows, cols, row, realrow, truerow, baserow; + int maxval; + pixel** pixels; /* The image array in ppm format */ + gray** alpha; /* The alpha channel array in pgm format */ + + ppm_init(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.input_filename); + + if (cmdline.alpha_stdout) + alpha_file = stdout; + else if (cmdline.alpha_filename == NULL) + alpha_file = NULL; + else + alpha_file = pm_openw(cmdline.alpha_filename); + + if (cmdline.alpha_stdout) + imageout_file = NULL; + else + imageout_file = stdout; + + /* Read the Targa file header. */ + readtga(ifP, &tga_head); + + if (cmdline.headerdump) + dumpHeader(tga_head); + + rows = ((int) tga_head.Height_lo) + ((int) tga_head.Height_hi) * 256; + cols = ((int) tga_head.Width_lo) + ((int) tga_head.Width_hi) * 256; + + switch (tga_head.ImgType) { + case TGA_Map: + case TGA_RGB: + case TGA_Mono: + case TGA_RLEMap: + case TGA_RLERGB: + case TGA_RLEMono: + break; + default: + pm_error("unknown Targa image type %d", tga_head.ImgType); + } + + if (tga_head.ImgType == TGA_Map || + tga_head.ImgType == TGA_RLEMap || + tga_head.ImgType == TGA_CompMap || + tga_head.ImgType == TGA_CompMap4) + { /* Color-mapped image */ + if (tga_head.CoMapType != 1) + pm_error( + "mapped image (type %d) with color map type != 1", + tga_head.ImgType ); + mapped = true; + /* Figure maxval from CoSize. */ + switch (tga_head.CoSize) { + case 8: + case 24: + case 32: + maxval = 255; + break; + + case 15: + case 16: + maxval = 31; + break; + + default: + pm_error( + "unknown colormap pixel size - %d", tga_head.CoSize ); + } + } else { + /* Not colormap, so figure maxval from PixelSize. */ + mapped = false; + switch ( tga_head.PixelSize ) { + case 8: + case 24: + case 32: + maxval = 255; + break; + + case 15: + case 16: + maxval = 31; + break; + + default: + pm_error("unknown pixel size - %d", tga_head.PixelSize); + } + } + + /* If required, read the color map information. */ + if ( tga_head.CoMapType != 0 ) { + unsigned int i; + unsigned int temp1, temp2; + + temp1 = tga_head.Index_lo + tga_head.Index_hi * 256; + temp2 = tga_head.Length_lo + tga_head.Length_hi * 256; + if ((temp1 + temp2 + 1) >= MAXCOLORS) + pm_error("too many colors - %d", (temp1 + temp2 + 1)); + for (i = temp1; i < (temp1 + temp2); ++i) + get_map_entry(ifP, &ColorMap[i], (int) tga_head.CoSize, + &AlphaMap[i]); + } + + /* Check run-length encoding. */ + if (tga_head.ImgType == TGA_RLEMap || + tga_head.ImgType == TGA_RLERGB || + tga_head.ImgType == TGA_RLEMono) + rlencoded = 1; + else + rlencoded = 0; + + /* Read the Targa file body and convert to portable format. */ + pixels = ppm_allocarray( cols, rows ); + alpha = pgm_allocarray( cols, rows ); + truerow = 0; + baserow = 0; + for (row = 0; row < rows; ++row) { + unsigned int col; + + realrow = truerow; + if (tga_head.OrgBit == 0) + realrow = rows - realrow - 1; + + for (col = 0; col < cols; ++col) + get_pixel(ifP, &(pixels[realrow][col]), (int) tga_head.PixelSize, + &(alpha[realrow][col])); + if (tga_head.IntrLve == TGA_IL_Four) + truerow += 4; + else if (tga_head.IntrLve == TGA_IL_Two) + truerow += 2; + else + ++truerow; + if (truerow >= rows) + truerow = ++baserow; + } + pm_close(ifP); + + if (imageout_file) + ppm_writeppm(imageout_file, pixels, cols, rows, (pixval) maxval, 0); + if (alpha_file) + pgm_writepgm(alpha_file, alpha, cols, rows, (pixval) maxval, 0); + if (imageout_file) + pm_close(imageout_file); + if (alpha_file) + pm_close(alpha_file); + + return 0; +} diff --git a/converter/ppm/vidtoppm.c b/converter/ppm/vidtoppm.c new file mode 100644 index 00000000..f3c20404 --- /dev/null +++ b/converter/ppm/vidtoppm.c @@ -0,0 +1,270 @@ +/* Bryan got this from mm.ftp-cs.berkeley.edu from the package + mpeg-encode-1.5b-src under the name vidtoppm.c on March 30, 2000. + The file was dated January 19, 1995. + + This program does not use the netpbm libraries, but generates its + output via the program rawtoppm. If any work is ever done on it + (or, more to the point, any interest ever expressed in it), it + should be converted just to call ppmwrite(), etc. directly. + + There was no attached documentation, but the program appears to + convert from Parallax XVideo JPEG format to a sequence of PPM files. It + does this conversion by putting each frame in a window and then + reading it out of the window, using libXvid. + + Because it requires special libraries and there is no known + requirement for it today, we are not including this program in the + standard netpbm build. But the source code is here in case someone + is interested in it later. + +*/ + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/* gcc -o playone playone.c -lX11 -lXvid -I/n/picasso/project/mm/xvideo/include + */ +#include <stdio.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <XPlxExt.h> + +#include "ppm.h" + +usage (p) +char *p; + +{ + fprintf (stderr, "Usage: %s filename width height start end outbase [quality]\n", p); + exit (1); +} + +static char buffer[300000]; + +Visual * +FindFullColorVisual (dpy, depth) + Display *dpy; + int *depth; +{ + XVisualInfo vinfo; + XVisualInfo *vinfo_ret; + int numitems, maxdepth; + + vinfo.class = TrueColor; + + vinfo_ret = XGetVisualInfo(dpy, VisualClassMask, &vinfo, &numitems); + + if (numitems == 0) return NULL; + + maxdepth = 0; + while(numitems > 0) { + if (vinfo_ret[numitems-1].depth > maxdepth) { + maxdepth = vinfo_ret[numitems-1 ].depth; + } + numitems--; + } + XFree(vinfo_ret); + + if (maxdepth < 24) return NULL; + + if (XMatchVisualInfo(dpy, DefaultScreen(dpy), maxdepth, + TrueColor, &vinfo)) { + *depth = maxdepth; + return vinfo.visual; + } + + return NULL; +} + +Window +CreateFullColorWindow (dpy, x, y, w, h) + Display *dpy; + int x, y, w, h; +{ + int depth; + Visual *visual; + XSetWindowAttributes xswa; + unsigned int mask; + unsigned int class; + int screen; + + screen = XDefaultScreen(dpy); + class = InputOutput; /* Could be InputOnly */ + visual = FindFullColorVisual (dpy, &depth); + if (visual == NULL) { + return 0; + } + mask = CWBackPixel | CWColormap | CWBorderPixel; + xswa.colormap = XCreateColormap(dpy, XRootWindow(dpy, screen), + visual, AllocNone); + xswa.background_pixel = BlackPixel(dpy, DefaultScreen(dpy)); + xswa.border_pixel = WhitePixel(dpy, DefaultScreen(dpy)); + + return XCreateWindow(dpy, RootWindow(dpy, screen), x, y, w, h, + 1, depth, class, visual, mask, &xswa); +} + +main (argc, argv) +int argc; +char **argv; + +{ + char *filename; + Display *dpy; + int screen; + Window root; + XEvent event; + GC gc; + Window win; + XPlxCImage image; + int size; + char *qTable; + FILE *inFile; + FILE *outFile; + extern char *malloc(); + int fd, r1, r2, i, j, r; + int quality; + int start, end; + XImage *ximage; + char *tdata; + char *obase; + char ofname[256]; + int height, width; + char command[256]; + + ppm_init(&argc, argv); + + if ((argc != 7) && (argc != 8))usage (argv[0]); + filename = argv[1]; + + width = atoi(argv[2]); + height = atoi(argv[3]); + + start = atoi(argv[4]); + end = atoi(argv[5]); + + if ((start < 1) || (end < start)) { + perror ("Bad start and end values."); + exit(); + } + + obase = argv[6]; + + quality = 100; + + if (argc > 7) + quality = atoi (argv[7]); + + dpy = XOpenDisplay (NULL); + screen = DefaultScreen(dpy); + root = DefaultRootWindow(dpy); +/* gc = DefaultGC(dpy, screen); */ +/* win = XCreateSimpleWindow (dpy, root, 0, 0, width, height, + 0, NULL, NULL); +*/ + win = CreateFullColorWindow(dpy, 0, 0, width+4, height+4); + gc = XCreateGC(dpy, win, 0, NULL); + + if (!win ) { + perror ("Unable to create window"); + exit(1); + } + + XMapWindow (dpy, win); + XSelectInput (dpy, win, ExposureMask |ButtonPressMask); + + size = MakeQTables(quality, &qTable); + XPlxPutTable(dpy, win, gc, qTable, size, 0); + XPlxPutTable(dpy, win, gc, qTable, size, 1); + XPlxVideoTag (dpy, win, gc, PLX_VIDEO); + + inFile = fopen(filename, "rb"); + if (inFile == NULL) { + perror (filename); + exit (1); + } + fd = fileno(inFile); + wait(2); + + for (i=0; i<start; i++) { + if (read (fd, &image, sizeof(image)) != sizeof(image)) { + perror("End of file."); + exit(); + } + image.data = buffer; + if (read (fd, buffer, image.size) != image.size) { + perror("End of file."); + exit(); + } + } + + for (i=start; i<=end; i++) { + fprintf(stdout, "GRABBING FRAME %d\n", i); + + if (read (fd, &image, sizeof(image)) != sizeof(image)) { + perror("End of file."); + exit(); + } + image.data = buffer; + if (read (fd, buffer, image.size) != image.size) { + perror("End of file."); + exit(); + } + + XPlxPutCImage (dpy, win, gc, &image, 0, 0, image.width, + image.height, 0, 0, width+2, height+2, 1); + + XFlush(dpy); + + ximage = XGetImage(dpy, win, 0, 0, width, height, 0x00ffffff, + ZPixmap); + + if (i == 0) { + fprintf(stderr, "Depth %d\n", ximage->depth); + fprintf(stderr, "Height: %d Width: %d\n", height, width ); + } + tdata = ximage->data; + + + sprintf(ofname, "%s%d.ppm", obase, i); + outFile = fopen("/tmp/foobar", "wb"); + if (!outFile) { + perror("Couldn't open output file."); + } + + for (r=0; r<height; r++) { + for (j=0; j<width; j++) { + fputc(*(tdata+3), outFile); + fputc(*(tdata+2), outFile); + fputc(*(tdata+1), outFile); + tdata += 4; + } + } + + fclose(outFile); + + free(tdata); + + sprintf(command, "rawtoppm %d %d < /tmp/foobar > %s", + width, height, ofname); + system(command); + } +} diff --git a/converter/ppm/winico.h b/converter/ppm/winico.h new file mode 100644 index 00000000..4b8ac38b --- /dev/null +++ b/converter/ppm/winico.h @@ -0,0 +1,88 @@ +#ifndef WINICO_H_INCLUDED +#define WINICO_H_INCLUDED + +/* A specification for the Windows icon format is at (2000.06.08) + + http://www.daubnet.com/formats/ICO.html + +*/ + +typedef unsigned char u1; +typedef unsigned short int u2; +typedef unsigned int u4; + +typedef struct MS_Ico_ * MS_Ico; +typedef struct IC_Entry_ * IC_Entry; +typedef struct IC_InfoHeader_ * IC_InfoHeader; +typedef struct IC_Color_ * IC_Color; +/* Not part of the spec, but useful in constructing the icon. */ +typedef struct IC_Palette_ * IC_Palette; +typedef struct ICON_bmp_ * ICON_bmp; + +struct MS_Ico_ { + u2 reserved; + u2 type; + u2 count; + IC_Entry * entries; +}; + + +struct IC_Entry_ { + u1 width; + u1 height; + /* + * color_count is actually a byte (u1)... but 0 = 256, so I've used a short (u2). + */ + u2 color_count; + u1 reserved; + u2 planes; + u2 bitcount; + u4 size_in_bytes; + u4 file_offset; + IC_InfoHeader ih; + IC_Color * colors; + /* + * Below here, I have useful fields which aren't in the spec, but + * save having to keep stoopid amounts of global data. + */ + u1 * andBitmap; /* Used in reader. */ + u1 * xorBitmap; + int xBytesXor; /* Not used in reading, but saved for writing. */ + int xBytesAnd; /* Not used in reading, but saved for writing. */ + u1 ** andBitmapOut; /* it's just easier to use a 2d array in the code.*/ + u1 ** xorBitmapOut; /* Sorry! :) */ +}; + +struct IC_InfoHeader_ { + u4 size; + u4 width; + u4 height; + u2 planes; + u2 bitcount; + u4 compression; + u4 imagesize; + u4 x_pixels_per_m; + u4 y_pixels_per_m; + u4 colors_used; + u4 colors_important; +}; + +struct IC_Color_ { + u1 red; + u1 green; + u1 blue; + u1 reserved; +}; + +struct IC_Palette_ { + u4 col_amount; + IC_Color * colors; +}; + +struct ICON_bmp_ { + int xBytes; + u4 size; /* just col_amount * height, but save calculating too often. */ + u1 ** data; +}; + +#endif diff --git a/converter/ppm/winico.html b/converter/ppm/winico.html new file mode 100644 index 00000000..6e33874b --- /dev/null +++ b/converter/ppm/winico.html @@ -0,0 +1,701 @@ +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> +<BASE HREF="http://www.oreilly.com/centers/gff/formats/miccur/"><table border=1 width=100%><tr><td><table border=1 bgcolor=#ffffff cellpadding=10 cellspacing=0 width=100% color=#ffffff><tr><td><font face=arial,sans-serif color=black size=-1>This is <b><font color=#0039b6>G</font> <font color=#c41200>o</font> <font color=#f3c518>o</font> <font color=#0039b6>g</font> <font color=#30a72f>l</font> <font color=#c41200>e</font></b>'s <a href="http://www.google.com/help/features.html#cached"><font color=blue>cache</font></a> of <A HREF="http://www.oreilly.com/centers/gff/formats/miccur/"><font color=blue>http://www.oreilly.com/centers/gff/formats/miccur/</font></a>.<br> +<b><font color=#0039b6>G</font> <font color=#c41200>o</font> <font color=#f3c518>o</font> <font color=#0039b6>g</font> <font color=#30a72f>l</font> <font color=#c41200>e</font></b>'s cache is the snapshot that we took of the page as we crawled the web.<br> +The page may have changed since that time. Click here for the <A HREF="http://www.oreilly.com/centers/gff/formats/miccur/"><font color=blue>current page</font></a> without highlighting.<br>To link to or bookmark this page, use the following url: <code>http://www.google.com/search?q=cache:dgpSCZ8_GJwC:www.oreilly.com/centers/gff/formats/miccur/+windows++icon+transparent&hl=en&ie=UTF-8</code></font><br><br><center><font size=-2><i>Google is not affiliated with the authors of this page nor responsible for its content.</i></font></center></td></tr> +<tr><td> +<table border=0 cellpadding=0 cellspacing=0><tr><td><font face=arial,sans-serif color=black size=-1>These search terms have been highlighted: </font></td><td bgcolor=#ffff66><B><font face=arial,sans-serif color=black size=-1>windows </font></B></td><td bgcolor=#A0FFFF><B><font face=arial,sans-serif color=black size=-1>icon </font></B></td><td bgcolor=#99ff99><B><font face=arial,sans-serif color=black size=-1>transparent </font></B></td></tr></table> +</td></tr></table></td></tr></table> +<hr> +<html> +<head> +<title>GFF Format Summary: Microsoft Windows Cursor and Icon</title> +</head> +<body> +<h1><A NAME="SPEC-MICCUR">Microsoft <B style="color:black;background-color:#ffff66">Windows</B> Cursor and <B style="color:black;background-color:#A0FFFF">Icon</B></A></h1> +<p> +<b>Also Known As:</b> CUR, ICO +<p> +<hr> +<p> + +<p> +<table border=0> +<tr valign=top> + <td align=left><b>Type</b></td> + <td align=left>Bitmap</td> +</tr> +<tr valign=top> + <td align=left><b>Colors</b></td> + <td align=left>1-bit and 4-bit</td> +</tr> +<tr valign=top> + <td align=left><b>Compression</b></td> + <td align=left>None</td> +</tr> +<tr valign=top> + <td align=left><b>Maximum Image Size</b></td> + <td align=left>4Gx4G pixels</td> +</tr> +<tr valign=top> + <td align=left><b>Multiple Images Per File</b></td> + <td align=left>Yes</td> +</tr> +<tr valign=top> + <td align=left><b>Numerical Format</b></td> + <td align=left>Little-endian</td> +</tr> +<tr valign=top> + <td align=left><b>Originator</b></td> + <td align=left>Microsoft</td> +</tr> +<tr valign=top> + <td align=left><b>Platform</b></td> + <td align=left>Microsoft <B style="color:black;background-color:#ffff66">Windows</B></td> +</tr> +<tr valign=top> + <td align=left><b>Supporting Applications</b></td> + <td align=left>Microsoft <B style="color:black;background-color:#ffff66">Windows</B></td> +</tr> +<tr valign=top> + <td align=left><b>See Also</b></td> + <td align=left><A HREF="gffse:/format.micbmp">Microsoft Bitmap</A>, <A HREF="gffse:/format.os2bmp">OS/2 Bitmap</A>, <A HREF="gffse:/format.micriff">Microsoft RIFF</A></td> +</tr> +</table> +<p> +<b>Usage</b><br> +The <B style="color:black;background-color:#ffff66">Windows</B> environment uses icons as graphical links to objects +(data files and executable programs). Cursors are used by the pointing device +as graphical indications of the current state of the <B style="color:black;background-color:#ffff66">Windows</B> environment. +<p> +<b>Comments</b><br> +<B style="color:black;background-color:#ffff66">Windows</B> icons and cursors are almost identical in format. +In fact, cursor and <B style="color:black;background-color:#A0FFFF">icon</B> bitmaps can be used interchangeably. +<p> +<hr> +<p> +<A NAME="MICCUR-DMYID.1"><!-- Overview anchor --></A> +<p> +In an object-oriented graphical user interface--as Microsoft +<B style="color:black;background-color:#ffff66">Windows</B> is sometimes described--icons are small bitmaps containing an +iconographic picture that makes an object accessible to the user. The +"objects" made available by icons are: collections of data (files); +executable programs (more files); and peripheral devices (the toys +you have hooked up to your computer). Rather than typing a file or +program name on a command line, applications are started and files +manipulated by clicking on an <B style="color:black;background-color:#A0FFFF">icon</B>, or "dragging and dropping" an +<B style="color:black;background-color:#A0FFFF">icon</B> on to a window or another <B style="color:black;background-color:#A0FFFF">icon</B>. +<p> +A cursor, or pointer as they are also called, is a special type of +<B style="color:black;background-color:#A0FFFF">icon</B> that is used to track the location of the pointing device on the +user interface. The cursor changes its appearance to indicate the +state of the system and the user interface. Such states include that +a search is in progress, an application is starting or stopping, or a +user-initiated action is being processed. The appearance of the +cursor also indicates what action may be performed based on the +current position of the pointer on the display. For example, actions +such as resizing a window or entering text may only be performed at +specific locations within a window. +<p> +Although a wide variety of physical pointing devices (mouse, +joystick, trackball, touch pad, light pen, pointing stick, etc.) are +typically supported by a user interface, the input received by +<B style="color:black;background-color:#ffff66">Windows</B> is identical from all types of pointing devices. +<p> +<P> +<B>Contents:</B><br> +<A HREF="#MICCUR-DMYID.2">File Organization</A><br> +<A HREF="#MICCUR-DMYID.3">File Details</A><br> +<A HREF="#MICCUR-DMYID.4">For Further Information</A><br> +<p> +Icons and cursors themselves are types of resources available in the +<B style="color:black;background-color:#ffff66">Windows</B> environment. When an application makes a request for <B style="color:black;background-color:#ffff66">Windows</B> +to display an <B style="color:black;background-color:#A0FFFF">icon</B> or cursor, <B style="color:black;background-color:#ffff66">Windows</B> must locate the appropriate +file and choose which of the bitmaps stored in the file best fits the +resolution and color depth of the display. +<p> +The <B style="color:black;background-color:#A0FFFF">icon</B> displayed is chosen based on its size and number of colors. +For example, a two-color <B style="color:black;background-color:#A0FFFF">icon</B> 16x16 pixels in size might look good on +a monochrome display at 640x480 resolution, but the same <B style="color:black;background-color:#A0FFFF">icon</B> bitmap +will probably look terrible on a 256-color, 1024x768 display. For +such a high-resolution display, the choice of the 16-color, 64x64 +pixel version of the <B style="color:black;background-color:#A0FFFF">icon</B> would look much better. However, if the +<B style="color:black;background-color:#A0FFFF">icon</B> file only contains one bitmap, then that is the <B style="color:black;background-color:#A0FFFF">icon</B> bitmap that +will be displayed. +<p> +<B style="color:black;background-color:#A0FFFF">Icon</B> and cursor bitmaps are typically very small. Under the 16-bit +<B style="color:black;background-color:#ffff66">Windows</B> environment (Win16) icons are traditionally square (16x16, +32x32, or 64x64 pixels in size) and have 16 or fewer colors. The +32-bit <B style="color:black;background-color:#ffff66">Windows</B> (Win32) environment allows icons to be larger and have +a greater pixel depth, such as 72x72 pixels with 256 colors. Win32 is +also tolerant of rectangular <B style="color:black;background-color:#A0FFFF">icon</B> formats. +<p> +Cursors under <B style="color:black;background-color:#ffff66">Windows</B> 3.<i>x</i> are two-color (1-bit) bitmaps. Under +<B style="color:black;background-color:#ffff66">Windows</B> 4.<i>x</i> cursors with 8, 16, or more colors are possible. Cursors +are also not limited to a specific range of sizes as are icons, +although most cursors are 32x32 pixels in size. +<p> +Both cursor and <B style="color:black;background-color:#A0FFFF">icon</B> data may be stored in separate file formats +(<i>*.ico</i> and <i>*.cur</i>) on disk, or be stored as a <B style="color:black;background-color:#ffff66">Windows</B> +resource file (<i>*.res</i>) and embedded directly into a <B style="color:black;background-color:#ffff66">Windows</B> +executable file (<i>*.exe</i>), Dynamic Link Library (<i>*.dll</i>), +Visual Basic control (<i>*.vbx</i>), or OLE +control (<i>*.ocx</i>). (This article only discusses the ICO and CUR file +formats and will leave the <B style="color:black;background-color:#ffff66">Windows</B> Resource format to a future +article.) +<p> +Simple animations may be created by the sequential display of several +<B style="color:black;background-color:#A0FFFF">icon</B> or cursor bitmaps in a continuous loop. These "flipbook" +animations are not directly supported by the Win16 environment, but +they do appear in some <B style="color:black;background-color:#ffff66">Windows</B> applications. +<p> +A true animated cursor format was defined by Microsoft in 1992 as part +of the RIFF multimedia specification. Animated cursors are the +<i>*.ani</i> files you might have noticed on your hard drive if you +use <B style="color:black;background-color:#ffff66">Windows</B> 95 or <B style="color:black;background-color:#ffff66">Windows</B> NT. They are actually little more than a +collection of ICO files stored in a single ANI file. For more +information on the format of <B style="color:black;background-color:#ffff66">Windows</B> animated icons, see the +<a href="gffse:/format.micriff">Microsoft RIFF</a> article. +<p> +If you are interested in the <B style="color:black;background-color:#A0FFFF">icon</B> and pointer file formats used by +OS/2, then have a look at the <a href="gffse:/format.os2bmp">OS/2 +Bitmap</a> article. OS/2 uses its own flavor of the BMP format to +store <B style="color:black;background-color:#A0FFFF">icon</B> and pointer data. +<p> +It should also be noted that there is no formal specification for +either the <B style="color:black;background-color:#ffff66">Windows </B><B style="color:black;background-color:#A0FFFF">icon</B> or cursor file formats. In fact, there are no +definitions for these file formats in the <B style="color:black;background-color:#ffff66">Windows</B> Software Development +Kit (SDK) header files. What information that can be found must be +scraped up from <B style="color:black;background-color:#ffff66">Windows</B> SDK manuals, Knowledge Base articles, and +several sample applications. See the <a href="#MICCUR-DMYID.4">For +Further Information</a> section in this article for more details on +additional reference material for <B style="color:black;background-color:#ffff66">Windows</B> icons and cursors. +<p> + +<h2><A NAME="MICCUR-DMYID.2">File Organization</A></h2> +<p> +The <B style="color:black;background-color:#ffff66">Windows </B><B style="color:black;background-color:#A0FFFF">icon</B> (ICO) and cursor (CUR) file formats are identical. +Only the interpretation of the file data differs slightly. In fact, +<B style="color:black;background-color:#A0FFFF">icon</B> and cursor files may be used interchangeably by applications that +realize this fact. +<p> +Every ICO and CUR file contains a header, a directory of bitmap +entries, and one or more bitmaps that describes the appearance of an +<B style="color:black;background-color:#A0FFFF">icon</B> or cursor (shown below). +<p> +<table border=1> +<tr valign=center> + <td align=center>Header</td> +</tr> +<tr valign=center> + <td align=center>Bitmap Directory</td> +</tr> +<tr valign=center> + <td align=center>Bitmap 1</td> +</tr> +<tr valign=center> + <td align=center>Bitmap 2</td> +</tr> +<tr valign=center> + <td align=center>...</td> +</tr> +<tr valign=center> + <td align=center>Bitmap N</td> +</tr> +</table> +<p> +The header stores information that is used to determine how many +bitmaps are in the file. ICO and CUR files will contain one or more +uncompressed bitmaps. Each entry in the bitmap directory will contain +information that describes one of the bitmaps stored in the file. The +directory will contain one entry per bitmap. +<p> + +<h3><A NAME="MICCUR-DMYID.3">File Details</A></h3> +<p> +As <B style="color:black;background-color:#ffff66">Windows</B> ICO and CUR files are nearly identical, we will first look +at the ICO format and then discuss the differences between the ICO +and CUR formats. +<p> +The ICO header contains only a <B style="color:black;background-color:#ffff66">Windows</B> resource identification value +and the count of the number of icons stored in the file. The header +is immediately followed by a directory that contains information for +all the icons stored in the ICO file. The directory is followed by +the <B style="color:black;background-color:#A0FFFF">icon</B> bitmaps themselves. +<p> +The following structure illustrates the entire format of an ICO +(or CUR) file: +<pre> +typedef struct _IconFile +{ + WORD Reserved; /* Reserved (always 0) */ + WORD ResourceType; /* Resource ID (always 1) */ + WORD IconCount; /* Number of <B style="color:black;background-color:#A0FFFF">icon</B> bitmaps in file */ + ICONENTRY IconDir[]; /* Directory of <B style="color:black;background-color:#A0FFFF">icon</B> entries */ + ICONDATA IconData[]; /* Listing of ICO bitmaps */ +} ICONFILE; +</pre> +<p> +Reserved is a two-byte value that is always zero in all ICO files. +<p> +ResourceType is the <B style="color:black;background-color:#ffff66">Windows</B> resource identifier type value. For icons +this value is always 1. +<p> +IconCount is the number of icons stored in the ICO file. It is also +the number of elements in the Icons array. +<p> +IconDir is an array of directory entries that describe the icons stored +in the ICO file. There will be one entry per <B style="color:black;background-color:#A0FFFF">icon</B> stored. The number +of entries will also equal the value of the IconCount field. Each +ICONENTRY element has the following format: +<pre> +typedef struct _IconEntry +{ + BYTE Width; /* Width of <B style="color:black;background-color:#A0FFFF">icon</B> in pixels */ + BYTE Height; /* Height of <B style="color:black;background-color:#A0FFFF">icon</B> in pixels */ + BYTE NumColors; /* Maximum number of colors */ + BYTE Reserved; /* Not used (always 0) */ + WORD NumPlanes; /* Not used (always 0) */ + WORD BitsPerPixel; /* Not used (always 0) */ + DWORD DataSize; /* Length of <B style="color:black;background-color:#A0FFFF">icon</B> bitmap in bytes */ + DWORD DataOffset; /* Offset position of <B style="color:black;background-color:#A0FFFF">icon</B> bitmap in file */ +} ICONENTRY; +</pre> +<p> +Width and Height are the size of the <B style="color:black;background-color:#A0FFFF">icon</B> in pixels. Only the values +of 16, 32, and 64 for these fields are accepted by Win16. Other +values are also accepted by Win32. And as square icons are the most +common, both of these fields will typically store the same value +(although 32x16 icons were commonly used for the now antiquated +300x200 CGA display mode). +<p> +NumColors is the maximum number of colors that may appear in the +<B style="color:black;background-color:#A0FFFF">icon</B>. The values 2, 8, and 16 are the only values accepted by Win16. +Other values are accepted by Win32. If the bitmap contains 256 or +more colors the value of NumColors will be 0. +<p> +Reserved is not used and is always zero. This field was probably +included as an element-alignment padding structure. +<p> +NumPlanes and BitsPerPixel are not used and are always 0. Earlier +revisions of the ICO format may have used these fields, but in the +current revision of ICO this information is now stored in the <B style="color:black;background-color:#A0FFFF">icon</B> +data itself. +<p> +DataSize is the length of the <B style="color:black;background-color:#A0FFFF">icon</B> data in bytes for this entry. +This value is the total size of both bitmaps used to render +the <B style="color:black;background-color:#A0FFFF">icon</B> (explained below). +<p> +DataOffset is the location of the <B style="color:black;background-color:#A0FFFF">icon</B> data for this entry. The +offset is measured in bytes from the start of the ICO file. +<p> +Following the <B style="color:black;background-color:#A0FFFF">icon</B> directory is the data for the <B style="color:black;background-color:#A0FFFF">icon</B>(s) themselves +(the IconData array in the ICONFILE structure). Each <B style="color:black;background-color:#A0FFFF">icon</B> stored in +an ICO file is actually an independent file format in itself, and +contains a header, a color palette, <B style="color:black;background-color:#A0FFFF">icon</B> bitmap data, and a display +bit mask. +<p> +The start of each section of <B style="color:black;background-color:#A0FFFF">icon</B> data is specified by the IconOffset +field in each <B style="color:black;background-color:#A0FFFF">icon</B> directory entry. Each section of <B style="color:black;background-color:#A0FFFF">icon</B> data has the +following format: +<pre> +typedef struct _IconData +{ + WIN3XBITMAPHEADER Header; /* Bitmap header data */ + WIN3XPALETTEELEMENT Palette[]; /* Color palette */ + BYTE XorMap[]; /* <B style="color:black;background-color:#A0FFFF">Icon</B> bitmap */ + BYTE AndMap[]; /* Display bit mask */ +} ICONDATA; +</pre> +<p> +Header is a 40-byte <B style="color:black;background-color:#ffff66">Windows</B> 3.<i>x</i> BMP file header structure. Only the +Size, Width, Height, Planes, BitsPerPixel, and SizeOfBitmap fields of +this header are actually used. All other fields in this structure +(Compression, SizeOfBitmap, HorzResolution, VertResolution, +ColorsUsed, and ColorsImportant) are set to zero. Refer to the +<a href="gffse:/format.micbmp">Microsoft <B style="color:black;background-color:#ffff66">Windows</B> Bitmap</a> article +for more information on this header structure. +<p> +Palette is the color palette for the data in the XorMap array. The +BitsPerPixel field of the Header is used to determine the number of +elements in the Palette array (BitsPerPixel >= 1). For two-color +icons there will be two palette entries; for 8- and 16-color icons +there will be 16 entries. Each palette element is a four-byte RGB +structure as described in the <a href="gffse:/format.micbmp">Microsoft +<B style="color:black;background-color:#ffff66">Windows</B> Bitmap</a> article. +<p> +XorMap contains the <B style="color:black;background-color:#A0FFFF">icon's</B> foreground bitmap. The size of the pixels +is indicated by the BitsPerPixel values in the header. Two-color +(monochrome) bitmaps are stored as one bit per pixel; 8- and 16-color +bitmap data is stored as 4 bits per pixel. Each pixel value is +actually an index into the Palette color map. +<p> +AndMap is the <B style="color:black;background-color:#A0FFFF">icon's</B> background bit mask. This is a 1-bit-per-pixel +mask that is the same size (in pixels) as the XorMap. This mask is +used to map the visible area of the <B style="color:black;background-color:#A0FFFF">icon</B> on the screen before the +<B style="color:black;background-color:#A0FFFF">icon</B> is actually displayed. +<p> +At first it may seem redundant to have Height and Width fields in +both the <B style="color:black;background-color:#A0FFFF">icon</B> entry (ICONENTRY) and the <B style="color:black;background-color:#A0FFFF">icon</B> header +(WIN3XBITMAPHEADER) structures. In fact, the Height and Width values +stored in the <B style="color:black;background-color:#A0FFFF">icon</B> header are the combined size of the XorMap and +AndMap bitmaps. The Width values in the two structures will be the +same, but the Height value in the <B style="color:black;background-color:#A0FFFF">icon</B> header will be double that of +the Height value in the <B style="color:black;background-color:#A0FFFF">icon</B> directory entry. The <B style="color:black;background-color:#A0FFFF">icon</B> directory +specifies the actual size of the <B style="color:black;background-color:#A0FFFF">icon</B> as it appears on the display, +and the <B style="color:black;background-color:#A0FFFF">icon</B> header specifies the size of the data used to create the +<B style="color:black;background-color:#A0FFFF">icon</B>. +<p> +Under the <B style="color:black;background-color:#ffff66">Windows</B> environment an <B style="color:black;background-color:#A0FFFF">icon</B> is displayed by first looking +through an ICO file and determining which <B style="color:black;background-color:#A0FFFF">icon</B> bitmap best matches +the number of colors and resolution of the display. The AND bit mask +for the chosen <B style="color:black;background-color:#A0FFFF">icon</B> is bitwise ANDed with the pixels on the display +where the <B style="color:black;background-color:#A0FFFF">icon</B> will appear. This removes the pixels from the display +and leaves a virtual "hole" in the display where the non-<B style="color:black;background-color:#99ff99">transparent</B> +parts of the <B style="color:black;background-color:#A0FFFF">icon</B> will appear. Finally, the XOR map is bitwise XORed +to the same pixels on the display. This operation adds the <B style="color:black;background-color:#A0FFFF">icon's</B> +color to the display. +<p> +Let's look at this process in more detail. Assume we have an ICO +file that contains several "happy face" <B style="color:black;background-color:#A0FFFF">icon</B> bitmaps. The ICO file +actually stores four different bitmap variations of the same happy +face: +<p> +<table border=0> +<tr> + <th align=left>Width</th> + <th align=left>Height</th> + <th align=left>Number of Colors</th> + <th align=left>BitsPerPixel</th> +</tr> +<tr> + <td align=left>8</td> + <td align=left>8</td> + <td align=left>16</td> + <td align=left>4</td> +</tr> +<tr> + <td align=left>16</td> + <td align=left>16</td> + <td align=left>16</td> + <td align=left>4</td> +</tr> +<tr> + <td align=left>32</td> + <td align=left>32</td> + <td align=left>16</td> + <td align=left>4</td> +</tr> +<tr> + <td align=left>48</td> + <td align=left>48</td> + <td align=left>64</td> + <td align=left>8</td> +</tr> +</table> +<p> +When <i>happyfac.ico</i> is loaded, one of the <B style="color:black;background-color:#A0FFFF">icon</B> bitmaps must be chosen +that matches resolution of the display. The ICO reader searches each +ICO directory entry for a "best fit" <B style="color:black;background-color:#A0FFFF">icon</B> bitmap. For a 320x240x16 +display the 8x8, 16-color "happy face" <B style="color:black;background-color:#A0FFFF">icon</B> would probably look best. +The reader then seeks to the offset of the selected <B style="color:black;background-color:#A0FFFF">icon</B> data. +<p> +Next, the AndMap bitmap must be loaded and ANDed to proper location +on the display. The AndMap data follows the XorMap data, so the ICO +reader must skip past the XorMap data to read the AndMap data. It is +important to note that the scan lines of data in the AndMap and +XorMap bitmaps are stored from the bottom up (the origin is in the +lower left-hand corner). That is, the first scan line of the bitmap +is actually the last scan line of the <B style="color:black;background-color:#A0FFFF">icon</B>. +<p> +In the 8x8x16 bitmap chosen, the AndMap contains only eight bytes +of data (8 rows x 8 columns): +<pre> +FF 99 99 E7 66 BD C3 FF +</pre> +<p> +In this form the data looks meaningless. Looking at the AndMap as an +array of bits we can see our happy face more clearly: +<pre> +1 1 1 1 1 1 1 1 +1 0 0 1 1 0 0 1 +1 0 0 1 1 0 0 1 +1 1 1 0 0 1 1 1 +0 1 1 0 0 1 1 0 +1 0 1 1 1 1 0 1 +1 1 0 0 0 0 1 1 +1 1 1 1 1 1 1 1 +</pre> +<p> +If we ANDed this bitmap to a location on the display that was all the +same color we would see this result: +<pre> +C C C C C C C C 1 1 1 1 1 1 1 1 C C C C C C C C +C C C C C C C C 1 0 0 1 1 0 0 1 C 0 0 C C 0 0 C +C C C C C C C C 1 0 0 1 1 0 0 1 C 0 0 C C 0 0 C +C C C C C C C C + 1 1 1 0 0 1 1 1 = C C C 0 0 C C C +C C C C C C C C 0 1 1 0 0 1 1 0 0 C C 0 0 C C 0 +C C C C C C C C 1 0 1 1 1 1 0 1 C 0 C C C C 0 C +C C C C C C C C 1 1 0 0 0 0 1 1 C C 0 0 0 0 C C +C C C C C C C C 1 1 1 1 1 1 1 1 C C C C C C C C + Display AndMap ANDed Display +</pre> +<p> +Each 1 bit in the AndMap preserved a display pixel (the <B style="color:black;background-color:#99ff99">transparent</B> +portion of the <B style="color:black;background-color:#A0FFFF">icon</B>) and each 0 bit removed a display pixel (the +opaque portion of the <B style="color:black;background-color:#A0FFFF">icon</B>). +<p> +Finally, we need to XOR the XorMap <B style="color:black;background-color:#A0FFFF">icon</B> pixel values into the hole we +punched into the display. Remember that the actual pixel color values +are stored in the color palette, and the bitmap data is only an index +map that indicates where the pixel colors should be written. +<p> +In our happy face <B style="color:black;background-color:#A0FFFF">icon</B> each pixel in the bitmap is four bits in size +and packed two pixels per byte. The XorMap bitmap data appears as +such: +<pre> +00 00 00 00 +04 40 04 40 +04 40 04 40 +00 0F F0 00 +90 0F F0 09 +09 00 00 90 +00 99 99 00 +00 00 00 00 +</pre> +<p> +In case you can't see the happy face, the eyes are the value 0x4, +the nose 0xF, and the smile 0x9. We now need to map the bitmap +index values to color values in the palette. Let's assume that +index 0x04 maps to color value 0x1, 0xF to 0xF, and 0x9 to 0x07. +<p> +With the color values determined, we XOR the pixel values to the same +region we applied the AndMap mask. The 0 bits on the display (the +black regions) indicate the non-<B style="color:black;background-color:#99ff99">transparent</B> pixels of the <B style="color:black;background-color:#A0FFFF">icon</B>. It is +the pixel represented by the 0 bits that will have their color +changed to that of the <B style="color:black;background-color:#A0FFFF">icon</B>. The <B style="color:black;background-color:#99ff99">transparent</B> pixels will retain the +original color of the display: +<pre> +C C C C C C C C 0 0 0 0 0 0 0 0 C C C C C C C C +C 0 0 C C 0 0 C 0 1 1 0 0 1 1 0 C 1 1 C C 1 1 C +C 0 0 C C 0 0 C 0 1 1 0 0 1 1 0 C 1 1 C C 1 1 C +C C C 0 0 C C C + 0 0 0 F F 0 0 0 = C C C F F C C C +0 C C 0 0 C C 0 7 0 0 F F 0 0 7 7 C C F F C C 7 +C 0 C C C C 0 C 0 7 0 0 0 0 7 0 C 7 C C C C 7 C +C C 0 0 0 0 C C 0 0 7 7 7 7 0 0 C C 7 7 7 7 C C +C C C C C C C C 0 0 0 0 0 0 0 0 C C C C C C C C + ANDed Display XORed values <B style="color:black;background-color:#A0FFFF">Icon</B> on Display +</pre> +<p> +A monochrome (1-bit) <B style="color:black;background-color:#A0FFFF">icon</B> or cursor will contain only four possible +pixels values: black, white, <B style="color:black;background-color:#99ff99">transparent</B>, and inverted. A <B style="color:black;background-color:#99ff99">transparent</B> +or inverted pixel may be either black or white in color. The color +palette will contain only two colors, which are black (entry zero) +and white (entry one). The <B style="color:black;background-color:#99ff99">transparent</B> color is the original color of +the display pixels. Inverted is the inverse color of the display +pixels. Inverted pixels are responsible for the shadowy or shimmering +effect you may have noticed when some cursors are moved across the +display. +<p> +The possible combined bitmaps values are shown in Table Microsoft +<B style="color:black;background-color:#ffff66">Windows</B> Cursor and <B style="color:black;background-color:#A0FFFF">Icon</B>-1. +<p> +<table border=0> +<caption>Table Microsoft <B style="color:black;background-color:#ffff66">Windows</B> and Cursor <B style="color:black;background-color:#A0FFFF">Icon</B>-1. Monochrome <B style="color:black;background-color:#A0FFFF">icon</B> and cursor mask value combinations</caption> +<tr valign=center> + <th></th> + <th align=left>AndMap Value</th> + <th align=left>XorMap Value</th> + <th align=left>Display Pixel Value</th> + <th align=left>Resulting Color</th> +</tr> +<tr> + <td align=left>Black</td> + <td align=left>0</td> + <td align=left>0</td> + <td align=left>0 or 1</td> + <td align=left>0</td> +</tr> +<tr> + <td align=left>White</td> + <td align=left>0</td> + <td align=left>1</td> + <td align=left>0 or 1</td> + <td align=left>1</td> +</tr> +<tr> + <td align=left><B style="color:black;background-color:#99ff99">Transparent</B></td> + <td align=left>1</td> + <td align=left>0</td> + <td align=left>0</td> + <td align=left>0</td> +</tr> +<tr> + <td></td> + <td align=left>1</td> + <td align=left>0</td> + <td align=left>1</td> + <td align=left>1</td> +</tr> +<tr> + <td align=left>Inverted</td> + <td align=left>1</td> + <td align=left>1</td> + <td align=left>0</td> + <td align=left>1</td> +</tr> +<tr> + <td></td> + <td align=left>1</td> + <td align=left>1</td> + <td align=left>1</td> + <td align=left>0</td> +</tr> +</table> +<p> + +<h3><A NAME="MICCUR-DMYID.3.1">CUR File Format</A></h3> +<p> +Everything we have covered for the ICO format also applies to the +CUR format with only a few exceptions: +<p> +The value of the ResourceType field in the header (ICONFILE) is 2, +indicating the file contains cursor bitmap data. +<p> +The cursor directory entry redefines two unused fields to store hot +spot information and modifies the possible values of two additional +fields: +<pre> +typedef struct _IconEntry +{ + BYTE Width; /* Width of cursor in pixels */ + BYTE Height; /* Height of cursor in pixels */ + BYTE NumColors; /* Maximum number of colors */ + BYTE Reserved; /* Not used (always 0) */ + WORD XHotSpot; /* X location of cursor's hot spot */ + WORD YHotSpot; /* Y location of cursor's hot spot */ + DWORD DataSize; /* Length of cursor bitmap in bytes */ + DWORD DataOffset; /* Offset position of cursor bitmap in file */ +} ICONENTRY; +</pre> +<p> +Width and Height values in the <B style="color:black;background-color:#A0FFFF">icon</B> directory may be any size, +although 32x32 pixels is the most common size for <B style="color:black;background-color:#ffff66">Windows</B> cursors. +<p> +NumColors is always 2 (1-bit, black and white) in a Win16 cursor +file. Win32 cursors have the same color ranges as icons. +<p> +XHotSpot and YHotSpot store the coordinates of the cursor's hot spot. +The X coordinate is relative to the cursor bitmap's left edge; the Y +coordinate is relative to the cursor's top edge. Both coordinates are +measured in pixels. +<p> +The hot spot is a single pixel in size. When the user clicks the +pointing device, the coordinates of the hot spot are sent to <B style="color:black;background-color:#ffff66">Windows</B>. +<B style="color:black;background-color:#ffff66">Windows</B> then performs an action based on where the hot spot is on the +display and on the current state of <B style="color:black;background-color:#ffff66">Windows</B> itself. +<p> +And finally, ICO and CUR files do not contain any type of +identification signature or "magic number". A file reader may assume +that all <i>.ico</i> files are <B style="color:black;background-color:#ffff66">Windows</B> icons and all <i>.cur</i> +files are <B style="color:black;background-color:#ffff66">Windows</B> +cursors, but it would haphazard to do so. If we assume instead that +all ICO files will never contain more than 256 bitmaps (a very safe +assumption) then an ICO file will begin with the byte sequence 00 00 +01 00 XX 00, where XX may be any byte value. Making the same +assumption for cursors, all CUR files will begin with the byte +sequence 00 00 02 00 XX 00. +<p> + +<h2><A NAME="MICCUR-DMYID.4">For Further Information</A></h2> +<p> +The primary sources of ICO and CUR information are the Microsoft +Win16 and Win32 Software Development Kits (SDK) and the Microsoft +Developer Network Library (MSDN) CD-ROMs. +<p> +The Win16 and Win32 SDKs are distributed with the Microsoft Visual +C++ compiler and the MSDN CD-ROMs. The following SDK references and +Knowledge Base articles discuss the ICO, CUR, and DIB bitmap formats: +<p> +<blockquote> +<p> +<i>Microsoft <B style="color:black;background-color:#ffff66">Windows</B> Software Development Kit, Programmers' +Reference, Volume 4: Resources</i> +<p> +Win32 SDK on-line help for the BITMAPINFO structure +<p> +Q81498 SAMPLE: DIBs and Their Uses +<p> +Q94326 SAMPLE: 16 and 32 Bits-Per-Pel Bitmap Formats +<p> +Specs: Icons in Win32 +<p> +Technical Articles: Win32 Binary Resource Formats +<p> +</blockquote> +<p> +You can find the SDK documents on the MSDN Library CD-ROMs. The MSDN +Library is only available by subscription. However, Microsoft has made +the October 1995 MSDN library available at: +<p> +<blockquote> +<p> +<i>ftp://ftp.microsoft.com/developr/MSDN/OctCD/</i> +</blockquote> +<p> +One other MSDN Library file of interest is: +<p> +<blockquote> +MSDN Frequently Asked Questions (FAQ)<br> +PSS ID Number: Q116437, 02-16-1996<br> +<i>ftp://ftp.microsoft.com/developr/MSDN/kb/Q116/4/37.txt</i><br> +<i>http://www.microsoft.com/msdn/msdnfaq.htm</i> +</blockquote> +<p> +The Win32 SDK also contains two sample applications that are necessary +for understanding icons and cursors. They are <i>IconPro</i> and +<i>imagedit</i> (an <B style="color:black;background-color:#A0FFFF">icon</B> and cursor bitmap editor). Both of these +sample applications are distributed with complete source code, but no +compiled binaries. The binaries are only distributed with the <B style="color:black;background-color:#ffff66">Windows</B> +95 and <B style="color:black;background-color:#ffff66">Windows</B> NT Resource Kits. +<p> +You can contact the Microsoft MSDN group at: +<p> +<blockquote> +Microsoft Developers Network<br> +Voice: 206-936-2490<br> +Email: <i>msdn@microsoft.com</i><br> +WWW: <i>http://www.microsoft.com/msdn/</i> +</blockquote> +<p> +And here's the addresses of Microsoft. Try getting what you need from +their FTP and web sites first: +<p> +<blockquote> +Microsoft Corporation<br> +One Microsoft Way<br> +Redmond, WA 98052-6399<br> +FTP: <i>ftp://ftp.microsoft.com/</i><br> +WWW: <i>http://www.microsoft.com/</i><br> +CIS: WINSDK and MSWIN32 forums +</blockquote> +<p> + +<hr> +<p> +<A HREF="gffse:/format.micclip"><img src="../../images/txtpreva.gif"></A> +<A HREF="gffse:/format.micmeta"><img src="../../images/txtnexta.gif"></A> +<A HREF="../book.htm"><img src="../../images/txtupa.gif"></A> +<a href="../bookidx.htm"><img src="../../images/txttoidx.gif"></a> +<br> +<a href="../booktoc.htm"><img src="../../images/btntoc.gif"></a> +<a href="../book/glossary.htm"><img src="../../images/btnglos.gif"></a> +<a href="../main.htm"><img src="../../images/btnmain.gif"></a> +<a href="gffse:/page.formats"><img src="../../images/btnfmt.gif"></a> +<a href="../software.htm"><img src="../../images/btnsoft.gif"></a> +<a href="../internet.htm"><img src="../../images/btninet.gif"></a> +<a href="../book.htm"><img src="../../images/btnbook.gif"></a> +<P> +Copyright © 1996, 1994 O'Reilly & Associates, Inc. All Rights Reserved. +<p> +</body> +</html> diff --git a/converter/ppm/winicontoppm.c b/converter/ppm/winicontoppm.c new file mode 100644 index 00000000..2d9de567 --- /dev/null +++ b/converter/ppm/winicontoppm.c @@ -0,0 +1,899 @@ +/* winicontoppm.c - read a MS Windows .ico file and write portable pixmap(s) +** +** Copyright (C) 2000,2003 by Lee Benfield - lee@benf.org +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +** +** Changes: +** +** 03/2003 - Added 24+32 bpp capability. +*/ + +#define _BSD_SOURCE 1 /* Make sure strdup() is in string.h */ +#define _XOPEN_SOURCE 500 /* Make sure strdup() is in string.h */ + +#include <math.h> +#include <string.h> +#include <assert.h> + +#include "ppm.h" +#include "shhopt.h" +#include "nstring.h" +#include "mallocvar.h" +#include "winico.h" + +#define MAJVERSION 0 +#define MINVERSION 4 + +static int file_offset = 0; /* not actually used, but useful for debug */ +static const char er_read[] = "%s: read error"; +static const char * infname; +static FILE * ifp; + +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char * inputFilespec; + const char * outputFilespec; + unsigned int allicons; + unsigned int bestqual; + unsigned int writeands; + unsigned int multippm; + unsigned int verbose; +}; + + + + +static void +parseCommandLine ( int argc, char ** argv, + struct cmdlineInfo *cmdlineP ) { +/*---------------------------------------------------------------------------- + parse program command line described in Unix standard form by argc + and argv. Return the information in the options as *cmdlineP. + + If command line is internally inconsistent (invalid options, etc.), + issue error message to stderr and abort program. + + Note that the strings we return are stored in the storage that + was passed to us as the argv array. We also trash *argv. +-----------------------------------------------------------------------------*/ + optEntry *option_def = malloc(100*sizeof(optEntry)); + /* Instructions to optParseOptions3 on how to parse our options. + */ + optStruct3 opt; + + unsigned int option_def_index; + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "allicons", OPT_FLAG, NULL, + &cmdlineP->allicons, 0 ); + OPTENT3(0, "bestqual", OPT_FLAG, NULL, + &cmdlineP->bestqual, 0 ); + OPTENT3(0, "writeands", OPT_FLAG, NULL, + &cmdlineP->writeands, 0 ); + OPTENT3(0, "multippm", OPT_FLAG, NULL, + &cmdlineP->multippm, 0 ); + OPTENT3(0, "verbose", OPT_FLAG, NULL, + &cmdlineP->verbose, 0 ); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ + + optParseOptions3( &argc, argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + + if (argc-1 < 1) + cmdlineP->inputFilespec = "-"; + else + cmdlineP->inputFilespec = argv[1]; + + if (argc-1 < 2) { + cmdlineP->outputFilespec = "-"; + + if (cmdlineP->writeands || cmdlineP->allicons) + pm_error("If you specify the -writeands or -allicons option, " + "you must also specify an output file name argument."); + } else + cmdlineP->outputFilespec = argv[2]; + + if (argc-1 > 2) + pm_error("Too many arguments (%d). Input filespec and " + "output filespec are the only possible arguments.", + argc-1); +} + + + + +static int +GetByte(void) { + int v; + + if ((v = getc(ifp)) == EOF) + { + pm_error(er_read, infname); + } + + return v; +} + +static short +GetShort(void) { + short v; + + if (pm_readlittleshort(ifp, &v) == -1) + { + pm_error(er_read, infname); + } + + return v; +} + +static long +GetLong(void) { + long v; + + if (pm_readlittlelong(ifp, &v) == -1) + { + pm_error(er_read, infname); + } + + return v; +} + + + +/* + * These have no purpose but to wrapper the Byte, Short & Long + * functions. + */ +static u1 +readU1 (void) { + file_offset++; + return GetByte(); +} + +static u1 * +readU1String (int length) +{ + + u1 * string; + + MALLOCARRAY(string, length + 1); + if (string == NULL) + pm_error("out of memory"); + + fread(string,sizeof(u1),length,ifp); + string[length] = 0; + file_offset += length * sizeof(u1); + return string; +} + +static u2 +readU2 (void) { + file_offset +=2; + return GetShort(); +} + +static u4 +readU4 (void) { + file_offset += 4; + return GetLong(); +} + +static IC_Entry +readICEntry (void) +{ + IC_Entry entry; + + MALLOCVAR(entry); + + if (entry == NULL) + pm_error("Unable to allcoate memory for IC entry"); + + entry->width = readU1(); + entry->height = readU1(); + entry->color_count = readU1(); + entry->reserved = readU1(); + entry->planes = readU2(); + entry->bitcount = readU2(); + entry->size_in_bytes = readU4(); + entry->file_offset = readU4(); + entry->colors = NULL; + entry->ih = NULL; + entry->xorBitmap = NULL; + entry->andBitmap = NULL; + + return entry; +} + + + +static IC_InfoHeader +readInfoHeader (IC_Entry entry) +{ + IC_InfoHeader ih; + + MALLOCVAR(ih); + + if (ih == NULL) + pm_error("Unable to allocate memory for info header"); + + ih->size = readU4(); + ih->width = readU4(); + ih->height = readU4(); + ih->planes = readU2(); + ih->bitcount = readU2(); + ih->compression = readU4(); + ih->imagesize = readU4(); + ih->x_pixels_per_m = readU4(); + ih->y_pixels_per_m = readU4(); + ih->colors_used = readU4(); + ih->colors_important = readU4(); + + if (!entry->bitcount) entry->bitcount = ih->bitcount; + if (entry->color_count == 0 && + entry->bitcount <= 8) entry->color_count = 256; + if (ih->compression) { + pm_error("Can't handle compressed icons"); + } + return ih; +} + +/* + * I don't know why this isn't the same as the spec, it just <b>isn't</b> + * The colors honestly seem to be stored BGR. Bizarre. + * + * I've checked this in the BMP code for bmptoppm and the gimp. Guess the + * spec I have is just plain wrong. + */ +static IC_Color +readICColor (void) +{ + IC_Color col; + + MALLOCVAR(col); + + if (col == NULL) + pm_error("Unable to allocate memory for color"); + + col->blue = readU1(); + col->green = readU1(); + col->red = readU1(); + col->reserved = readU1(); + return col; +} + + + +/* + * Depending on if the image is stored as 1bpp, 4bpp or 8bpp, the + * encoding mechanism is different. + * + * 8bpp => 1 byte/palette index. + * 4bpp => High Nibble, Low Nibble + * 1bpp => 1 palette value per bit, high bit 1st. + */ +static u1 * +read1Bitmap (int width, int height) +{ + int tmp; + int xBytes; + u1 * bitmap; + int wt = width; + + MALLOCARRAY(bitmap, width * height); + if (bitmap == NULL) + pm_error("out of memory"); + + wt >>= 3; + if (wt & 3) { + wt = (wt & ~3) + 4; + } + xBytes = wt; + for (tmp = 0; tmp<height; tmp++ ) { + int x; + int rowByte = 0; + int xOrVal = 128; + u1 * row = readU1String(xBytes); + for (x = 0; x< width; x++) { + *(bitmap+((height-tmp-1)*width) + (x)) = + (row[rowByte] & xOrVal) / xOrVal; + if (xOrVal == 1) { + xOrVal = 128; + rowByte++; + } else { + xOrVal >>= 1; + } + } + free(row); + } + return bitmap; +} + + + +static u1 * +read4Bitmap (int width, int height) +{ + int tmp; + u1 * bitmap; + + int wt = width; + int xBytes; + + MALLOCARRAY(bitmap, width * height); + if (bitmap == NULL) + pm_error("out of memory"); + + + wt >>= 1; + if (wt & 3) { + wt = (wt & ~3) + 4; + } + xBytes = wt; + for (tmp = 0; tmp<height ; tmp++ ) { + int rowByte = 0; + int bottom = 1; + int x; + u1 * row = readU1String(xBytes); + for (x = 0; x< width; x++) { + /* + * 2 nibbles, 2 values. + */ + if (bottom) { + *(bitmap+((height-tmp-1)*width) + (x)) = + (row[rowByte] & 0xF0) >> 4; + } else { + *(bitmap+((height-tmp-1)*width) + (x)) = (row[rowByte] & 0xF); + rowByte++; + } + bottom = !bottom; + } + free(row); + } + return bitmap; +} + + + +static u1 * +read8Bitmap (int width, int height) +{ + int tmp; + unsigned int xBytes; + unsigned int wt = width; + u1 * bitmap; + + MALLOCARRAY(bitmap, width * height); + if (bitmap == NULL) + pm_error("out of memory"); + + if (wt & 3) { + wt = (wt & ~3) + 4; + } + xBytes = wt; + for (tmp = 0; tmp<height ; tmp++ ) { + int rowByte = 0; + int x; + u1 * row = readU1String(xBytes); + for ( x = 0; x< width; x++) { + *(bitmap+((height-tmp-1)*width) + (x)) = row[rowByte]; + rowByte++; + } + free(row); + } + return bitmap; +} + + + +/* + * Read a true color bitmap. (24/32 bits) + * + * The output routine deplanarizes it for us, we keep it flat here. + */ +static u1 * +readXBitmap (int const width, + int const height, + int const bpp) { + int const bytes = bpp >> 3; + unsigned int const xBytes = width * bytes; + + u1 * bitmap; + /* remember - bmp (dib) stored upside down, so reverse */ + + MALLOCARRAY(bitmap, bytes * width * height); + if (bitmap == NULL) + pm_error("out of memory allocating bitmap array"); + + { + unsigned int i; + u1 * bitcurptr; + + for (i = 0, bitcurptr = &bitmap[bytes * width * (height-1)]; + i < height; + ++i, bitcurptr -= xBytes) { + + u1 * const row = readU1String(xBytes); + memcpy(bitcurptr, row, xBytes); + free(row); + } + } + return bitmap; +} + + + +static MS_Ico +readIconFile (bool const verbose) { + int iter,iter2; + + MS_Ico MSIconData; + + MALLOCVAR(MSIconData); + + /* + * reserved - should equal 0. + */ + MSIconData->reserved = readU2(); + /* + * Type - should equal 1 + */ + MSIconData->type = readU2(); + /* + * count - no of icons in file.. + */ + MSIconData->count = readU2(); + /* + * Allocate "count" array of entries. + */ + if (verbose) + pm_message("Icon file contains %d icons.", MSIconData->count); + + MALLOCARRAY(MSIconData->entries, MSIconData->count); + if (MSIconData->entries == NULL) + pm_error("out of memory"); + /* + * Read in each of the entries + */ + for (iter = 0;iter < MSIconData->count ; iter++ ) { + MSIconData->entries[iter] = readICEntry(); + } + /* After that, we have to read in the infoheader, color map (if + * any) and the actual bit/pix maps for the icons. + */ + if (verbose) + fprintf (stderr,"#\tColors\tBPP\tWidth\tHeight\n"); + for (iter = 0;iter < MSIconData->count ; iter++ ) { + int bpp; + MSIconData->entries[iter]->ih = + readInfoHeader (MSIconData->entries[iter]); + + /* What's the bits per pixel? */ + bpp = MSIconData->entries[iter]->bitcount; + /* Read the palette, if appropriate */ + switch (bpp) { + case 24: + case 32: + /* 24/32 bpp icon has no palette */ + break; + default: + MALLOCARRAY(MSIconData->entries[iter]->colors, + MSIconData->entries[iter]->color_count); + if (MSIconData->entries[iter]->colors == NULL) + pm_error("out of memory"); + + for (iter2 = 0; + iter2 < MSIconData->entries[iter]->color_count ; + iter2++ ) { + MSIconData->entries[iter]->colors[iter2] = readICColor(); + } + break; + } + if (verbose) { + char cols_text[10]; + sprintf (cols_text, "%d", MSIconData->entries[iter]->color_count); + fprintf (stderr, + "%d\t%s\t%d\t%d\t%d\n", iter, + MSIconData->entries[iter]->color_count ? + cols_text : "TRUE", + bpp, MSIconData->entries[iter]->width, + MSIconData->entries[iter]->height); + } + /* Pixels are stored bottom-up, left-to-right. Pixel lines are + * padded with zeros to end on a 32bit (4byte) boundary. Every + * line will have the same number of bytes. Color indices are + * zero based, meaning a pixel color of 0 represents the first + * color table entry, a pixel color of 255 (if there are that + * many) represents the 256th entry. + * + * 24+32 bit (16 is an abomination, which I'll avoid, and expect + * no-one to mind) are stored 1byte/plane with a spare (alpha?) + * byte for 32 bit. + */ + { + /* + * Read XOR Bitmap + */ + switch (bpp) { + case 1: + MSIconData->entries[iter]->xorBitmap = + read1Bitmap(MSIconData->entries[iter]->width, + MSIconData->entries[iter]->height); + break; + case 4: + MSIconData->entries[iter]->xorBitmap = + read4Bitmap(MSIconData->entries[iter]->width, + MSIconData->entries[iter]->height); + break; + case 8: + MSIconData->entries[iter]->xorBitmap = + read8Bitmap(MSIconData->entries[iter]->width, + MSIconData->entries[iter]->height); + break; + case 24: + case 32: + MSIconData->entries[iter]->xorBitmap = + readXBitmap(MSIconData->entries[iter]->width, + MSIconData->entries[iter]->height,bpp); + break; + default: + pm_error("Uncatered bit depth %d",bpp); + } + /* + * Read AND Bitmap + */ + MSIconData->entries[iter]->andBitmap = + read1Bitmap(MSIconData->entries[iter]->width, + MSIconData->entries[iter]->height); + } + + } + return MSIconData; +} + + + +static char * +trimOutputName(const char inputName[]) +{ + /* + * Just trim off the final ".ppm", if there is one, else return as is. + * oh, for =~ ... :) + */ + char * outFile = strdup(inputName); + if (STREQ(outFile + (strlen (outFile) - 4), ".ppm")) { + *(outFile + (strlen (outFile) - 4)) = 0; + } + return outFile; + +} + + + +static int +getBestQualityIcon(MS_Ico MSIconData) +{ + int x,best,best_size,best_bpp,bpp,size; + IC_Entry entry; + + best_size = best_bpp = 0; + for (x = 0; x < MSIconData->count; x++) { + entry = MSIconData->entries[x]; + size = entry->width * entry->height; + bpp = entry->bitcount ? entry->bitcount : entry->ih->bitcount; + if (size > best_size) { + best = x; + best_size = size; + } else if (size == best_size && bpp > best_bpp) { + best = x; + best_bpp = bpp; + } + } + return best; +} + +static void +writeXors(FILE * const multiOutF, + char outputFileBase[], + IC_Entry const entry, + int const entryNum, + bool const multiple, + bool const xor) { +/*---------------------------------------------------------------------------- + Write an "xor" image (i.e. the main image) out. + + 'multiple' means this is one of multiple images that are being written. + 'entryNum' is the sequence number within the winicon file of the image + we are writing. + + 'xor' means to include "xor" in the output file name. + + if 'multiOutF' is non-null, it is the stream descriptor of an open + stream to which we are to write the image. If it is null, + we are to open a file using outputFileBase[] and 'entryNum' and 'xor' + to derive its name, and close it afterward. +-----------------------------------------------------------------------------*/ + FILE * outF; + pixel ** ppm_array; + int row; + int pel_size; + const char *outputFile; + int maxval; + int forcetext; + + if (multiOutF) { + outF = multiOutF; + outputFile = strdup(""); + } else { + if (outputFileBase) { + if (multiple) { + asprintfN(&outputFile, "%s%s_%d.ppm", + outputFileBase,(xor ? "_xor" : ""), entryNum); + } else { + asprintfN(&outputFile, "%s%s.ppm", + outputFileBase,(xor ? "_xor" : "")); + } + } else + outputFile = strdup("-"); + + outF = pm_openw(outputFile); + } + /* + * allocate an array to save the bmp data into. + * note that entry->height will be 1/2 entry->ih->height, + * as the latter adds "and" and "xor" height. + */ + ppm_array = ppm_allocarray(entry->width, entry->height); + for (row=0; row < entry->height; row++) { + u1 * xorRow; + int col; + switch (entry->bitcount) { + case 24: + case 32: + pel_size = entry->bitcount >> 3; + xorRow = entry->xorBitmap + row * entry->width * pel_size; + for (col=0; col < entry->width*pel_size;col+=pel_size) { + PPM_ASSIGN(ppm_array[row][col/pel_size], + xorRow[col+2],xorRow[col+1],xorRow[col]); + } + break; + default: + xorRow = entry->xorBitmap + row * entry->width; + for (col=0; col < entry->width; col++) { + int colorIndex; + IC_Color color; + colorIndex = xorRow[col]; + color = entry->colors[colorIndex]; + PPM_ASSIGN(ppm_array[row][col], + color->red,color->green,color->blue); + } + break; + } + } + + maxval = 255; + forcetext = 0; + + ppm_writeppm(outF,ppm_array,entry->width, entry->height, + (pixval) maxval, forcetext); + ppm_freearray(ppm_array,entry->height); + + strfree(outputFile); + + if (!multiOutF) + pm_close(outF); +} + + + +static void +writeAnds(FILE * const multiOutF, + char outputFileBase[], IC_Entry const entry, int const entryNum, + bool multiple) { +/*---------------------------------------------------------------------------- + Write the "and" image (i.e. the alpha mask) of the image 'IC_Entry' out. + + 'multiple' means this is one of multiple images that are being written. + 'entryNum' is the sequence number within the winicon file of the image + we are writing. + + if 'multiOutF' is non-null, it is the stream descriptor of an open + stream to which we are to write the image. If it is null, + we are to open a file using outputFileBase[] and 'entryNum' and 'xor' + to derive its name, and close it afterward. +-----------------------------------------------------------------------------*/ + FILE * outF; + bit ** pbm_array; + u1 * andRow; + int row; + + if (multiOutF) + outF = multiOutF; + else { + const char *outputFile; + + assert(outputFileBase); + + if (multiple) + asprintfN(&outputFile, "%s_and_%d.pbm", outputFileBase, entryNum); + else + asprintfN(&outputFile, "%s_and.pbm", outputFileBase); + outF = pm_openw(outputFile); + strfree(outputFile); + } + pbm_array = pbm_allocarray(entry->width, entry->height); + for (row=0; row < entry->height; row++) { + int col; + andRow = entry->andBitmap + row * entry->width; + for (col=0; col < entry->width; col++) { + /* Note: black is transparent in a Netpbm alpha mask */ + pbm_array[row][col] = andRow[col] ? PBM_BLACK: PBM_WHITE; + } + } + + pbm_writepbm(outF, pbm_array, entry->width, entry->height, 0); + + pbm_freearray(pbm_array, entry->height); + if (!multiOutF) + pm_close (outF); +} + + + +static void +openMultiXor(char outputFileBase[], + bool const writeands, + FILE ** const multiOutFP) { + + const char *outputFile; + + if (outputFileBase) { + asprintfN(&outputFile, "%s%s.ppm", + outputFileBase, (writeands ? "_xor" : "")); + } else + outputFile = strdup("-"); + + /* + * Open the output file now, it'll stay open the whole time. + */ + *multiOutFP = pm_openw(outputFile); + + strfree(outputFile); +} + + + +static void +openMultiAnd(char outputFileBase[], FILE ** const multiAndOutFP) { + + const char *outputFile; + + assert(outputFileBase); + + asprintfN(&outputFile, "%s_and.pbm", outputFileBase); + + *multiAndOutFP = pm_openw(outputFile); + + strfree(outputFile); +} + +static void free_iconentry(IC_Entry entry) { + int x; + if (entry->colors && entry->color_count) { + for (x=0;x<entry->color_count;x++) free(entry->colors[x]); + free(entry->colors); + } + if (entry->andBitmap) free(entry->andBitmap); + if (entry->xorBitmap) free(entry->xorBitmap); + if (entry->ih) free(entry->ih); + free(entry); +} + +static void free_icondata(MS_Ico MSIconData) +{ + int x; + for (x=0;x<MSIconData->count;x++) { + free_iconentry(MSIconData->entries[x]); + } + free(MSIconData); +} + + +int +main(int argc, char *argv[]) { + + struct cmdlineInfo cmdline; + int startEntry, endEntry; + MS_Ico MSIconData; + char * outputFileBase; + FILE * multiOutF; + FILE * multiAndOutF; + + ppm_init (&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + if (cmdline.bestqual && cmdline.allicons) + pm_message("-bestqual doesn't make sense with -allicons. " + "Ignoring -bestqual."); + + if (STREQ(cmdline.outputFilespec, "-")) + outputFileBase = NULL; + else + outputFileBase = trimOutputName(cmdline.outputFilespec); + + ifp = pm_openr(cmdline.inputFilespec); + + infname = cmdline.inputFilespec; + + MSIconData = readIconFile(cmdline.verbose); + /* + * Now we've read the icon file in (Hopefully! :) + * Go through each of the entries, and write out files of the + * form + * + * fname_0_xor.ppm + * fname_0_and.ppm + * + * (or to stdout, depending on args parsing above). + */ + /* + * If allicons is set, we want everything, if not, just go through once. + */ + startEntry = 0; + if (cmdline.allicons) { + endEntry = MSIconData->count; + } else { + endEntry = 1; + } + /* + * If bestqual is set, find the icon with highest size & bpp. + */ + if (cmdline.bestqual) { + startEntry = getBestQualityIcon(MSIconData); + endEntry = startEntry+1; + } + + if (cmdline.multippm) + openMultiXor(outputFileBase, cmdline.writeands, &multiOutF); + else + multiOutF = NULL; + + if (cmdline.writeands && cmdline.multippm) + openMultiAnd(outputFileBase, &multiAndOutF); + else + multiAndOutF = NULL; + + { + int entryNum; + + for (entryNum = startEntry ; entryNum < endEntry ; entryNum++ ) { + IC_Entry const entry = MSIconData->entries[entryNum]; + + writeXors(multiOutF, outputFileBase, entry, entryNum, + cmdline.allicons, cmdline.writeands); + if (cmdline.writeands) + writeAnds(multiAndOutF, outputFileBase, + entry, entryNum, cmdline.allicons); + } + } + if (multiOutF) + pm_close (multiOutF); + if (multiAndOutF) + pm_close(multiAndOutF); + + /* free up the image data here. */ + free_icondata(MSIconData); + return 0; +} diff --git a/converter/ppm/xim.h b/converter/ppm/xim.h new file mode 100644 index 00000000..ffe60fb6 --- /dev/null +++ b/converter/ppm/xim.h @@ -0,0 +1,125 @@ +#ifndef XIM_H_INCLUDED +#define XIM_H_INCLUDED + +/* xim.h - header file for Xim files +** +** Taken from the X.V11R4 version of XimHeader.h: +** +** Author: Philip R. Thompson +** Address: phils@athena.mit.edu, 9-526 +** Note: size of header should be 1024 (1K) bytes. +** $Header: /mit/phils/X/RCS/XimHeader.h,v 1.7 89/11/09 17:26:54 phils Exp Locker: phils $ +** $Date: 89/11/09 17:26:54 $ +** $Source: /mit/phils/X/RCS/XimHeader.h,v $ +*/ + +#define IMAGE_VERSION 3 +#ifndef _BYTE +typedef unsigned char byte; +#define _BYTE 1 +#endif + +/* External ascii file format. */ +typedef struct ImageHeader { + char file_version[8]; /* header version */ + char header_size[8]; /* Size of file header in bytes */ + char image_width[8]; /* Width of the raster image */ + char image_height[8]; /* Height of the raster imgage */ + char num_colors[8]; /* Actual number of entries in c_map */ + char num_channels[3]; /* 0 or 1 = pixmap, 3 = RG&B buffers */ + char bytes_per_line[5]; /* bytes per scanline */ + char num_pictures[4]; /* Number of pictures in file */ + char bits_per_channel[4]; /* usually 1 or 8 */ + char alpha_channel[4]; /* Alpha channel flag */ + char runlength[4]; /* Runlength encoded flag */ + char author[48]; /* Name of who made it */ + char date[32]; /* Date and time image was made */ + char program[16]; /* Program that created this file */ + char comment[96]; /* other viewing info. for this image */ + unsigned char c_map[256][3]; /* RGB values of the pixmap indices */ +} ImageHeader, XimAsciiHeader; + + +/* Internal binary format. */ +typedef struct Color { + byte pixel, red, grn, blu; +} Color; + +typedef struct XimImage { + int width; /* width of the image in pixels */ + int height; /* height of the image in pixels */ + unsigned datasize; /* size of one channel of data */ + short nchannels; /* number data channels in image */ + short bits_channel; /* usually 1 or 8 */ + short bytes_per_line; /* bytes to hold one scanline */ + byte* data; /* pixmap or red channel data */ + byte* grn_data; /* green channel data */ + byte* blu_data; /* blue channel data */ + byte* other; /* other (alpha) data */ + unsigned alpha_flag :1; /* alpha channel flag */ + unsigned packed_flag:1; /* data packed in one chunk of memory */ + unsigned runlen_flag:1; /* runlength encoded data flag */ + unsigned : 0; /* future flags, word alignment */ + short tpics, npics; /* number of images, total & left in file */ + short ncolors; /* " " colors in the color table */ + Color* colors; /* colortable, one byte per r/g/b & pixel */ + char* author; /* author credit, copyright, etc */ + char* date; /* date image was made, grabbed, etc. */ + char* program; /* program used to make this */ + short ncomments; /* number of comments strings */ + char** comments; /* pointers to null terminated strings */ + char* offset; /* original offset in machine memory */ + float chroma_red[2]; /* x, y image chromacity coords */ + float chroma_grn[2]; + float chroma_blu[2]; + float chroma_wht[2]; + float gamma; /* image storage gamma */ +} XimImage; + +/* Future external ascii variable length header - under review. */ +#if (IMAGE_VERSION == 4) +typedef struct XimAsciiHeader { + char file_version[4]; /* header version */ + char header_size[8]; /* Size of file header (fixed part only) */ + char image_height[8]; /* Height of the raster imgage in pixels */ + char image_width[8]; /* Width of the raster image in pixels */ + char bytes_line[8]; /* Actual # of bytes separating scanlines */ + char bits_channel[4]; /* Bits per channel (usually 1 or 8) */ + char num_channels[4]; /* 1 = pixmap, 3 = RG&B buffers */ + char alpha_channel[2]; /* Alpha channel flag */ + char num_colors[4]; /* Number of entries in c_map (if any) */ + char num_pictures[4]; /* Number of images in file */ + char runlength_flag[2]; /* Runlength encoded flag */ + char future_flags[8]; + char author[48]; /* Name of who made it, from passwd entry */ + char date[32]; /* Unix format date */ + char program[32]; /* Program that created this */ + char gamma[12]; /* image storage gamma */ + char chroma_red[24]; /* image red primary chromaticity coords. */ + char chroma_grn[24]; /* " green " " " */ + char chroma_blu[24]; /* " blue " " " */ + char chroma_wht[24]; /* " white point " " */ + char comment_length[8] /* Total length of comments */ + /* char* comment; Null separated comments */ + /* unsigned char c_map[]; RGB Colortable, (ncolors * 3 bytes) */ +} XimAsciiHeader; +#endif /*IMAGE_VERSION 4*/ + +#ifndef rnd +#define rnd(x) ((int)((float)(x) + 0.5)) /* round a float to an int */ +#endif + +/* Note: +* - All data is in char's in order to maintain easily portability +* across machines, and some human readibility. +* - Images may be stored as pixmaps (8 bits/pixel) or as seperate +* red, green, blue channel data (24+ bits/pixel). +* - An alpha channel is optional and is found after every num_channels +* of data. +* - Pixmaps or RGB (and alpha) channel data are stored respectively +* after the header. +* - If num_channels = 1, a pixmap is assumed and the colormap in the +* header is used. +* - Data size = image_width * image_height. +*/ +#endif diff --git a/converter/ppm/ximtoppm.c b/converter/ppm/ximtoppm.c new file mode 100644 index 00000000..96798707 --- /dev/null +++ b/converter/ppm/ximtoppm.c @@ -0,0 +1,438 @@ +/* ximtoppm.c - read an Xim file and produce a portable pixmap +** +** Copyright (C) 1991 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#define _BSD_SOURCE 1 /* Make sure strdup() is in string.h */ +#define _XOPEN_SOURCE 500 /* Make sure strdup() is in string.h */ + +#include <string.h> +#include "ppm.h" +#include "xim.h" +#include "shhopt.h" +#include "nstring.h" + +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char *input_filename; + const char *alpha_filename; + bool alpha_stdout; +}; + + + +static void +parseCommandLine(int argc, char ** argv, + struct cmdlineInfo *cmdlineP) { +/*---------------------------------------------------------------------------- + Note that many of the strings that this function returns in the + *cmdlineP structure are actually in the supplied argv array. And + sometimes, one of these strings is actually just a suffix of an entry + in argv! +-----------------------------------------------------------------------------*/ + optEntry option_def[100]; + optStruct3 opt; + + unsigned int option_def_index; + + unsigned int alphaoutSpec; + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "alphaout", OPT_STRING, + &cmdlineP->alpha_filename, &alphaoutSpec, 0); + + optParseOptions3(&argc, argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and all of *cmdlineP. */ + + if (!alphaoutSpec) + cmdlineP->alpha_filename = NULL; + + if (argc - 1 == 0) + cmdlineP->input_filename = "-"; /* he wants stdin */ + else if (argc - 1 == 1) + cmdlineP->input_filename = strdup(argv[1]); + else + pm_error("Too many arguments. The only argument accepted " + "is the input file specification"); + + if (cmdlineP->alpha_filename && + STREQ(cmdlineP->alpha_filename, "-")) + cmdlineP->alpha_stdout = TRUE; + else + cmdlineP->alpha_stdout = FALSE; +} + + + +/* The subroutines are excerpted and slightly modified from the + X.V11R4 version of xim_io.c. +*/ + +static int +ReadXimHeader(FILE * const in_fp, + XimImage * const header) { + + int i; + char *cp; + XimAsciiHeader a_head; + + cp = (char *) header; + for (i = 0; i < sizeof(XimImage); ++i ) + *cp++ = 0; + /* Read header and verify image file formats */ + if (fread((char *)&a_head, sizeof(ImageHeader), 1, in_fp) != 1) { + pm_message("ReadXimHeader: unable to read file header" ); + return(0); + } + if (atoi(a_head.header_size) != sizeof(ImageHeader)) { + pm_message("ReadXimHeader: header size mismatch" ); + return(0); + } + if (atoi(a_head.file_version) != IMAGE_VERSION) { + pm_message("ReadXimHeader: incorrect Image_file version" ); + return(0); + } + header->width = atoi(a_head.image_width); + header->height = atoi(a_head.image_height); + header->ncolors = atoi(a_head.num_colors); + header->nchannels = atoi(a_head.num_channels); + header->bytes_per_line = atoi(a_head.bytes_per_line); +/* header->npics = atoi(a_head.num_pictures); +*/ + header->bits_channel = atoi(a_head.bits_per_channel); + header->alpha_flag = atoi(a_head.alpha_channel); + if (strlen(a_head.author)) { + if (!(header->author = calloc((unsigned int)strlen(a_head.author)+1, + 1))) { + pm_message("ReadXimHeader: can't calloc author string" ); + return(0); + } + header->width = atoi(a_head.image_width); + strncpy(header->author, a_head.author, strlen(a_head.author)); + } + if (strlen(a_head.date)) { + if (!(header->date =calloc((unsigned int)strlen(a_head.date)+1,1))){ + pm_message("ReadXimHeader: can't calloc date string" ); + return(0); + } + header->width = atoi(a_head.image_width); + strncpy(header->date, a_head.date, strlen(a_head.date)); + } + if (strlen(a_head.program)) { + if (!(header->program = calloc( + (unsigned int)strlen(a_head.program) + 1, 1))) { + pm_message("ReadXimHeader: can't calloc program string" ); + return(0); + } + header->width = atoi(a_head.image_width); + strncpy(header->program, a_head.program,strlen(a_head.program)); + } + /* Do double checking for bakwards compatibility */ + if (header->npics == 0) + header->npics = 1; + if (header->bits_channel == 0) + header->bits_channel = 8; + else if (header->bits_channel == 24) { + header->nchannels = 3; + header->bits_channel = 8; + } + if ((int)header->bytes_per_line == 0) + header->bytes_per_line = + (header->bits_channel == 1 && header->nchannels == 1) ? + (header->width + 7) / 8 : + header->width; + header->datasize =(unsigned int)header->bytes_per_line * header->height; + if (header->nchannels == 3 && header->bits_channel == 8) + header->ncolors = 0; + else if (header->nchannels == 1 && header->bits_channel == 8) { + header->colors = (Color *)calloc((unsigned int)header->ncolors, + sizeof(Color)); + if (header->colors == NULL) { + pm_message("ReadXimHeader: can't calloc colors" ); + return(0); + } + for (i=0; i < header->ncolors; i++) { + header->colors[i].red = a_head.c_map[i][0]; + header->colors[i].grn = a_head.c_map[i][1]; + header->colors[i].blu = a_head.c_map[i][2]; + } + } + return(1); +} + + + +static int +ReadImageChannel(FILE * const infp, + byte * const buf, + unsigned int * const bufsize, + int const encoded) { + + int i, runlen, nbytes; + unsigned int j; + byte *line; + long marker; + + if (!encoded) + j = fread((char *)buf, 1, (int)*bufsize, infp); + else { + if ((line=(byte *)malloc((unsigned int)BUFSIZ)) == NULL) { + pm_message("ReadImageChannel: can't malloc() fread string" ); + return(0); + } + /* Unrunlength encode data */ + marker = ftell(infp); + j = 0; + while (((nbytes=fread((char *)line, 1, BUFSIZ, infp)) > 0) && + (j < *bufsize)) { + for (i=0; (i < nbytes) && (j < *bufsize); i++) { + runlen = (int)line[i]+1; + i++; + while (runlen--) + buf[j++] = line[i]; + } + marker += i; + } + /* return to the begining of the next image's bufffer */ + if (fseek(infp, marker, 0) == -1) { + pm_message("ReadImageChannel: can't fseek to location in image buffer" ); + return(0); + } + free((char *)line); + } + if (j != *bufsize) { + pm_message("unable to complete channel: %u / %u (%d%%)", + j, *bufsize, (int)(j*100.0 / *bufsize) ); + *bufsize = j; + } + return(1); +} + + + +static int +ReadXimImage(FILE * const in_fp, + XimImage * const xim) { + + if (xim->data) { + free((char *)xim->data); + xim->data = (byte *)0; + } + if (xim->grn_data) { + free((char *)xim->grn_data); + xim->grn_data = (byte *)0; + } + if (xim->blu_data) { + free((char *)xim->blu_data); + xim->blu_data = (byte *)0; + } + if (xim->other) { + free((char *)xim->other); + xim->other = (byte *)0; + } + xim->npics = 0; + if (!(xim->data = (byte *)calloc(xim->datasize, 1))) { + pm_message("ReadXimImage: can't malloc pixmap data" ); + return(0); + } + if (!ReadImageChannel(in_fp, xim->data, &xim->datasize, 0)) { + pm_message("ReadXimImage: end of the images" ); + return(0); + } + if (xim->nchannels == 3) { + xim->grn_data = (byte *)malloc(xim->datasize); + xim->blu_data = (byte *)malloc(xim->datasize); + if (xim->grn_data == NULL || xim->blu_data == NULL) { + pm_message("ReadXimImage: can't malloc rgb channel data" ); + free((char *)xim->data); + if (xim->grn_data) free((char *)xim->grn_data); + if (xim->blu_data) free((char *)xim->blu_data); + xim->data = xim->grn_data = xim->blu_data = (byte*)0; + return(0); + } + if (!ReadImageChannel(in_fp, xim->grn_data, &xim->datasize, 0)) + return(0); + if (!ReadImageChannel(in_fp, xim->blu_data, &xim->datasize, 0)) + return(0); + } + if (xim->nchannels > 3) { + /* In theory, this can be any fourth channel, but the only one we + know about is an Alpha channel, so we'll call it that, even + though we process it generically. + */ + if ((xim->other = (byte *)malloc(xim->datasize)) == NULL) { + pm_message("ReadXimImage: can't malloc alpha data" ); + return(0); + } + if (!ReadImageChannel(in_fp, xim->other, &xim->datasize, 0)) + return(0); + } + xim->npics = 1; + return(1); +} + + + +/*********************************************************************** +* File: xlib.c +* Author: Philip Thompson +* $Date: 89/11/01 10:14:23 $ +* $Revision: 1.14 $ +* Purpose: General xim libray of utililities +* Copyright (c) 1988 Philip R. Thompson +* Computer Resource Laboratory (CRL) +* Dept. of Architecture and Planning +* M.I.T., Rm 9-526 +* Cambridge, MA 02139 +* This software and its documentation may be used, copied, modified, +* and distributed for any purpose without fee, provided: +* -- The above copyright notice appears in all copies. +* -- This disclaimer appears in all source code copies. +* -- The names of M.I.T. and the CRL are not used in advertising +* or publicity pertaining to distribution of the software +* without prior specific written permission from me or CRL. +* I provide this software freely as a public service. It is NOT a +* commercial product, and therefore is not subject to an an implied +* warranty of merchantability or fitness for a particular purpose. I +* provide it as is, without warranty. +* This software is furnished only on the basis that any party who +* receives it indemnifies and holds harmless the parties who furnish +* it against any claims, demands, or liabilities connected with using +* it, furnishing it to others, or providing it to a third party. +* +* Philip R. Thompson (phils@athena.mit.edu) +***********************************************************************/ + +static int +ReadXim(in_fp, xim) + FILE *in_fp; + XimImage *xim; +{ + if (!ReadXimHeader(in_fp, xim)) { + pm_message("can't read xim header" ); + return(0); + } + if (!ReadXimImage(in_fp, xim)) { + pm_message("can't read xim data" ); + return(0); + } + return(1); +} + + + +int +main(int argc, + char *argv[]) { + + struct cmdlineInfo cmdline; + FILE *ifP, *imageout_file, *alpha_file; + XimImage xim; + pixel *pixelrow, colormap[256]; + gray *alpharow; + /* The alpha channel of the row we're currently converting, in + pgm fmt + */ + int rows, cols, row, mapped; + pixval maxval; + bool success; + + ppm_init(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.input_filename); + + if (cmdline.alpha_stdout) + alpha_file = stdout; + else if (cmdline.alpha_filename == NULL) + alpha_file = NULL; + else + alpha_file = pm_openw(cmdline.alpha_filename); + + if (cmdline.alpha_stdout) + imageout_file = NULL; + else + imageout_file = stdout; + + success = ReadXim(ifP, &xim); + if (!success) + pm_error("can't read Xim file"); + + rows = xim.height; + cols = xim.width; + + if (xim.nchannels == 1 && xim.bits_channel == 8) { + int i; + + mapped = true; + maxval = 255; + for (i = 0; i < xim.ncolors; ++i) { + PPM_ASSIGN( + colormap[i], xim.colors[i].red, + xim.colors[i].grn, xim.colors[i].blu ); + /* Should be colormap[xim.colors[i].pixel], but Xim is broken. */ + } + } else if (xim.nchannels == 3 || xim.nchannels == 4) { + mapped = false; + maxval = pm_bitstomaxval(xim.bits_channel); + } else + pm_error( + "unknown Xim file type, nchannels == %d, bits_channel == %d", + xim.nchannels, xim.bits_channel); + + if (imageout_file) + ppm_writeppminit(imageout_file, cols, rows, maxval, 0); + if (alpha_file) + pgm_writepgminit(alpha_file, cols, rows, maxval, 0); + + pixelrow = ppm_allocrow(cols); + alpharow = pgm_allocrow(cols); + + for (row = 0; row < rows; ++row) { + if (mapped) { + byte * const ximrow = xim.data + row * xim.bytes_per_line; + unsigned int col; + + for (col = 0; col < cols; ++col) + pixelrow[col] = colormap[ximrow[col]]; + alpharow[col] = 0; + } else { + byte * const redrow = xim.data + row * xim.bytes_per_line; + byte * const grnrow = xim.grn_data + row * xim.bytes_per_line; + byte * const blurow = xim.blu_data + row * xim.bytes_per_line; + byte * const othrow = xim.other + row * xim.bytes_per_line; + + unsigned int col; + + for (col = 0; col < cols; ++col) { + PPM_ASSIGN(pixelrow[col], + redrow[col], grnrow[col], blurow[col]); + if (xim.nchannels > 3) + alpharow[col] = othrow[col]; + else + alpharow[col] = 0; + } + } + if (imageout_file) + ppm_writeppmrow(imageout_file, pixelrow, cols, maxval, 0); + if (alpha_file) + pgm_writepgmrow(alpha_file, alpharow, cols, maxval, 0); + } + pm_close(ifP); + if (imageout_file) + pm_close(imageout_file); + if (alpha_file) + pm_close(alpha_file); + + return 0; +} diff --git a/converter/ppm/xpmtoppm.README b/converter/ppm/xpmtoppm.README new file mode 100644 index 00000000..b5e254fa --- /dev/null +++ b/converter/ppm/xpmtoppm.README @@ -0,0 +1,69 @@ +PPM Stuff +Convert portable pixmap to X11 Pixmap format (version 3) and vice versa +----------------------------------------------------------------------- + +The program ppmtoxpm is a modified version of one sent out by Mark Snitily +(mark@zok.uucp) and upgraded to XPM version 2 by Paul Breslaw +(paul@mecazh.uu.ch). + +It converts Jeff Poskanzer's (jef@well.sf.ca.us) portable pixmap format +(PBMPlus) into the new X11 pixmap format: XPM version 3 distributed by Arnaud +Le Hors (lehors@mirsa.inria.fr). + +It is built using the PBMPlus libraries in the same way as any of the +ppm utilities in the PBMPlus package. + +Paul Breslaw - Thu Nov 22 09:55:31 MET 1990 +-- +Paul Breslaw, Mecasoft SA, | telephone : 41 1 362 2040 +Guggachstrasse 10, CH-8057 Zurich, | e-mail : paul@mecazh.uu.ch +Switzerland. | mcsun!chx400!mecazh!paul +-- + +The program xpmtoppm is a modified version of the one distributed in the +PBMPlus package by Jeff Poskanzer's which converts XPM version 1 or 3 files +into a portable pixmap format. + +Upgraded to XPM version 3 by + Arnaud LE HORS BULL Research France -- Koala Project + lehors@sa.inria.fr Phone:(33) 93 65 77 71 Fax:(33) 93 65 77 66 + Inria Sophia Antipolis B.P.109 06561 Valbonne Cedex France + + +Installation +----------- +You should copy The ppmtoxpm.c, ppmtoxpm.1 and xpmtoppm.c, xpmtoppm.1 into +your .../pbmplus/ppm directory. + + +Patches +------- +* Rainer Sinkwitz sinkwitz@ifi.unizh.ch - 21 Nov 91: + +xpmtoppm.c: + - Bug fix, no advance of read ptr, would not read + colors like "ac c black" because it would find + the "c" of "ac" and then had problems with "c" + as color. + + - Now understands multword X11 color names + + - Now reads multiple color keys. Takes the color + of the hightest available key. Lines no longer need + to begin with key 'c'. + + - expanded line buffer to from 500 to 2048 for bigger files + +ppmtoxpm.c: + - Bug fix, should should malloc space for rgbn[j].name+1 in line 441 + caused segmentation faults + + - lowercase conversion of RGB names def'ed out, + considered harmful. + +Suggestions: + ppmtoxpm should read /usr/lib/X11/rgb.txt by default. + With the Imakefiles of pbmplus it even gets compiled + with -DRGB_DB=\"/usr/lib/X11/rgb.txt\" + + diff --git a/converter/ppm/xpmtoppm.c b/converter/ppm/xpmtoppm.c new file mode 100644 index 00000000..9dddfd83 --- /dev/null +++ b/converter/ppm/xpmtoppm.c @@ -0,0 +1,832 @@ +/* xpmtoppm.c - read an X11 pixmap file and produce a portable pixmap +** +** Copyright (C) 1991 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +** +** Upgraded to handle XPM version 3 by +** Arnaud Le Hors (lehors@mirsa.inria.fr) +** Tue Apr 9 1991 +** +** Rainer Sinkwitz sinkwitz@ifi.unizh.ch - 21 Nov 91: +** - Bug fix, no advance of read ptr, would not read +** colors like "ac c black" because it would find +** the "c" of "ac" and then had problems with "c" +** as color. +** +** - Now understands multiword X11 color names +** +** - Now reads multiple color keys. Takes the color +** of the hightest available key. Lines no longer need +** to begin with key 'c'. +** +** - expanded line buffer to from 500 to 2048 for bigger files +*/ + +#define _BSD_SOURCE /* Make sure strdup() is in string.h */ +#define _XOPEN_SOURCE 500 /* Make sure strdup() is in string.h */ + +#include <string.h> + +#include "ppm.h" +#include "shhopt.h" +#include "nstring.h" +#include "mallocvar.h" + +#define MAX_LINE (8 * 1024) + /* The maximum size XPM input line we can handle. */ + +/* number of xpmColorKeys */ +#define NKEYS 5 + +const char *xpmColorKeys[] = +{ + "s", /* key #1: symbol */ + "m", /* key #2: mono visual */ + "g4", /* key #3: 4 grays visual */ + "g", /* key #4: gray visual */ + "c", /* key #5: color visual */ +}; + +struct cmdline_info { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + char *input_filespec; /* Filespecs of input files */ + char *alpha_filename; + int alpha_stdout; + int verbose; +}; + + +static int verbose; + + +static void +parse_command_line(int argc, char ** argv, + struct cmdline_info *cmdline_p) { +/*---------------------------------------------------------------------------- + Note that the file spec array we return is stored in the storage that + was passed to us as the argv array. +-----------------------------------------------------------------------------*/ + optStruct *option_def = malloc(100*sizeof(optStruct)); + /* Instructions to OptParseOptions2 on how to parse our options. + */ + optStruct2 opt; + + unsigned int option_def_index; + + option_def_index = 0; /* incremented by OPTENTRY */ + OPTENTRY(0, "alphaout", OPT_STRING, &cmdline_p->alpha_filename, 0); + OPTENTRY(0, "verbose", OPT_FLAG, &cmdline_p->verbose, 0); + + cmdline_p->alpha_filename = NULL; + cmdline_p->verbose = FALSE; + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = TRUE; /* We may have parms that are negative numbers */ + + optParseOptions2(&argc, argv, opt, 0); + /* Uses and sets argc, argv, and some of *cmdline_p and others. */ + + if (argc - 1 == 0) + cmdline_p->input_filespec = NULL; /* he wants stdin */ + else if (argc - 1 == 1) + cmdline_p->input_filespec = strdup(argv[1]); + else + pm_error("Too many arguments. The only argument accepted\n" + "is the input file specification"); + + if (cmdline_p->alpha_filename && + STREQ(cmdline_p->alpha_filename, "-")) + cmdline_p->alpha_stdout = TRUE; + else + cmdline_p->alpha_stdout = FALSE; + +} + + +static char lastInputLine[MAX_LINE+1]; + /* contents of line most recently read from input */ +static bool backup; + /* TRUE means next read should be a reread of the most recently read + line, i.e. lastInputLine, instead of a read from the input file. + */ + + +static void +getline(char * const line, + size_t const size, + FILE * const stream) { +/*---------------------------------------------------------------------------- + Read the next line from the input file 'stream', through the one-line + buffer lastInputLine[]. + + If 'backup' is true, the "next line" is the previously read line, i.e. + the one in that one-line buffer. Otherwise, the "next line" is the next + line from the real file. After reading the backed up line, we reset + 'backup' to false. + + Return the line as a null terminated string in *line, which is an + array of 'size' bytes. + + Exit program if the line doesn't fit in the buffer. +-----------------------------------------------------------------------------*/ + if (size > sizeof(lastInputLine)) + pm_error("INTERNAL ERROR: getline() received 'size' parameter " + "which is out of bounds"); + + if (backup) { + strncpy(line, lastInputLine, size); + backup = FALSE; + } else { + if (fgets(line, size, stream) == NULL) + pm_error("EOF or read error on input file"); + if (strlen(line) == size - 1) + pm_error("Input file has line that is too long (longer than " + "%u bytes).", (unsigned)size - 1); + STRSCPY(lastInputLine, line); + } +} + + + +static unsigned int +getNumber(char * const p, unsigned int const size) { + + unsigned int retval; + char * q; + + retval = 0; + for (q = p; q < p+size; ++q) + retval = (retval << 8) + *q; + + return retval; +} + + + +static void +getword(char * const output, char ** const cursorP) { + + char *t1; + char *t2; + + for (t1=*cursorP; ISSPACE(*t1); t1++); /* skip white space */ + for (t2 = t1; !ISSPACE(*t2) && *t2 != '"' && *t2 != '\0'; t2++); + /* Move to next white space, ", or eol */ + if (t2 > t1) + strncpy(output, t1, t2 - t1); + output[t2 - t1] = '\0'; + *cursorP = t2; +} + + + +static void +addToColorMap(unsigned int const seqNum, + unsigned int const colorNumber, + pixel * const colors, int * const ptab, + char colorspec[], int const isTransparent, + int * const transparentP) { +/*---------------------------------------------------------------------------- + Add the color named by colorspec[] to the colormap contained in + 'colors' and 'ptab', as the color associated with XPM color number + 'colorNumber', which is the seqNum'th color in the XPM color map. + + Iff 'transparent', set *transparentP to the colormap index that + corresponds to this color. +-----------------------------------------------------------------------------*/ + if (ptab == NULL) { + /* Index into table. */ + colors[colorNumber] = ppm_parsecolor(colorspec, + (pixval) PPM_MAXMAXVAL); + if (isTransparent) + *transparentP = colorNumber; + } else { + /* Set up linear search table. */ + colors[seqNum] = ppm_parsecolor(colorspec, + (pixval) PPM_MAXMAXVAL); + ptab[seqNum] = colorNumber; + if (isTransparent) + *transparentP = seqNum; + } +} + + + +static void +interpretXpm3ColorTableLine(char line[], int const seqNum, + int const chars_per_pixel, + pixel * const colors, int * const ptab, + int * const transparentP) { +/*---------------------------------------------------------------------------- + Interpret one line of the color table in the XPM header. 'line' is + the line from the XPM file. It is the seqNum'th color table entry in + the file. The file uses 'chars_per_pixel' characters per pixel. + + Add the information from this color table entry to the color table + 'colors' and, if it isn't NULL, the corresponding lookup shadow table + 'ptab' (see readXpm3ColorTable for a description of these data + structures). + + The line may include values for multiple kinds of color (grayscale, + color, etc.). We take the highest of these (e.g. color over grayscale). + + If a color table entry indicates transparency, set *transparentP + to the colormap index that corresponds to the indicated color. +-----------------------------------------------------------------------------*/ + /* Note: this code seems to allow for multi-word color specifications, + but I'm not aware that such are legal. Ultimately, ppm_parsecolor() + interprets the name, and I believe it only takes single word + color specifications. -Bryan 2001.05.06. + */ + char str2[MAX_LINE+1]; + char *t1; + char *t2; + int endOfEntry; /* boolean */ + + unsigned int curkey, key, highkey; /* current color key */ + unsigned int lastwaskey; + /* The last token we processes was a key, and we have processed + at least one token. + */ + char curbuf[BUFSIZ]; /* current buffer */ + int isTransparent; + + int colorNumber; + /* A color number that appears in the raster */ + /* read the chars */ + t1 = strchr(line, '"'); + if (t1 == NULL) + pm_error("A line that is supposed to be an entry in the color " + "table does not start with a quote. The line is '%s'. " + "It is the %dth entry in the color table.", + line, seqNum); + else + t1++; /* Points now to first color number character */ + + colorNumber = getNumber(t1, chars_per_pixel); + t1 += chars_per_pixel; + + /* + * read color keys and values + */ + curkey = 0; + highkey = 1; + lastwaskey = FALSE; + t2 = t1; + endOfEntry = FALSE; + while ( !endOfEntry ) { + int isKey; /* boolean */ + getword(str2, &t2); + if (strlen(str2) == 0) + endOfEntry = TRUE; + else { + /* See if the word we got is a valid key (and get its key + number if so) + */ + for (key = 1; + key <= NKEYS && !STREQ(xpmColorKeys[key - 1], str2); + key++); + isKey = (key <= NKEYS); + + if (lastwaskey || !isKey) { + /* This word is a color specification (or "none" for + transparent). + */ + if (!curkey) + pm_error("Missing color key token in color table line " + "'%s' before '%s'.", line, str2); + if (!lastwaskey) + strcat(curbuf, " "); /* append space */ + if ( (strncmp(str2, "None", 4) == 0) + || (strncmp(str2, "none", 4) == 0) ) { + /* This entry identifies the transparent color number */ + strcat(curbuf, "#000000"); /* Make it black */ + isTransparent = TRUE; + } else + strcat(curbuf, str2); /* append buf */ + lastwaskey = 0; + } else { + /* This word is a key. So we've seen the last of the + info for the previous key, and we must either put it + in the color map or ignore it if we already have a higher + color form in the colormap for this colormap entry. + */ + if (curkey > highkey) { /* flush string */ + addToColorMap(seqNum, colorNumber, colors, ptab, curbuf, + isTransparent, transparentP); + highkey = curkey; + } + curkey = key; /* set new key */ + curbuf[0] = '\0'; /* reset curbuf */ + isTransparent = FALSE; + lastwaskey = 1; + } + if (*t2 == '"') break; + } + } + /* Put the info for the last key in the line into the colormap (or + ignore it if there's already a higher color form for this colormap + entry in it) + */ + if (curkey > highkey) { + addToColorMap(seqNum, colorNumber, colors, ptab, curbuf, + isTransparent, transparentP); + highkey = curkey; + } + if (highkey == 1) + pm_error("C error scanning color table"); +} + + + +static void +readXpm3Header(FILE * const stream, int * const widthP, int * const heightP, + int * const chars_per_pixelP, int * const ncolorsP, + pixel ** const colorsP, int ** const ptabP, + int * const transparentP) { +/*---------------------------------------------------------------------------- + Read the header of the XPM file on stream 'stream'. Assume the + getline() stream is presently positioned to the beginning of the + file and it is a Version 3 XPM file. Leave the stream positioned + after the header. + + We have two ways to return the colormap, depending on the number of + characters per pixel in the XPM: + + If it is 1 or 2 characters per pixel, we return the colormap as a + Netpbm 'pixel' array *colorsP (in newly malloc'ed storage), such + that if a color in the raster is identified by index N, then + (*colorsP)[N] is that color. So this array is either 256 or 64K + pixels. In this case, we return *ptabP = NULL. + + If it is more than 2 characters per pixel, we return the colormap as + both a Netpbm 'pixel' array *colorsP and a lookup table *ptabP (both + in newly malloc'ed storage). + + If a color in the raster is identified by index N, then for some I, + (*ptabP)[I] is N and (*colorsP)[I] is the color in question. So + you iterate through *ptabP looking for N and then look at the + corresponding entry in *colorsP to get the color. + + Return as *transColorNumberP the value of the XPM color number that + represents a transparent pixel, or -1 if no color number does. +-----------------------------------------------------------------------------*/ + char line[MAX_LINE+1]; + const char * xpm3_signature = "/* XPM */"; + + *widthP = *heightP = *ncolorsP = *chars_per_pixelP = -1; + + /* Read the XPM signature comment */ + getline(line, sizeof(line), stream); + if (strncmp(line, xpm3_signature, strlen(xpm3_signature)) != 0) + pm_error("Apparent XPM 3 file does not start with '/* XPM */'. " + "First line is '%s'", xpm3_signature); + + /* Read the assignment line */ + getline(line, sizeof(line), stream); + if (strncmp(line, "static char", 11) != 0) + pm_error("Cannot find data structure declaration. Expected a " + "line starting with 'static char', but found the line " + "'%s'.", line); + + /* Read the hints line */ + getline(line, sizeof(line), stream); + /* skip the comment line if any */ + if (!strncmp(line, "/*", 2)) { + while (!strstr(line, "*/")) + getline(line, sizeof(line), stream); + getline(line, sizeof(line), stream); + } + if (sscanf(line, "\"%d %d %d %d\",", widthP, heightP, + ncolorsP, chars_per_pixelP) != 4) + pm_error("error scanning hints line"); + + if (verbose == 1) + { + pm_message("Width x Height: %d x %d", *widthP, *heightP); + pm_message("no. of colors: %d", *ncolorsP); + pm_message("chars per pixel: %d", *chars_per_pixelP); + } + + /* Allocate space for color table. */ + if (*chars_per_pixelP <= 2) { + /* Set up direct index (see above) */ + *colorsP = ppm_allocrow(*chars_per_pixelP == 1 ? 256 : 256*256); + *ptabP = NULL; + } else { + /* Set up lookup table (see above) */ + *colorsP = ppm_allocrow(*ncolorsP); + MALLOCARRAY(*ptabP, *ncolorsP); + if (*ptabP == NULL) + pm_error("Unable to allocate memory for %d colors", *ncolorsP); + } + + { + /* Read the color table */ + int seqNum; + /* Sequence number of entry within color table in XPM header */ + + *transparentP = -1; /* initial value */ + + for (seqNum = 0; seqNum < *ncolorsP; seqNum++) { + getline(line, sizeof(line), stream); + /* skip the comment line if any */ + if (!strncmp(line, "/*", 2)) + getline(line, sizeof(line), stream); + + interpretXpm3ColorTableLine(line, seqNum, *chars_per_pixelP, + *colorsP, *ptabP, transparentP); + } + } +} + + +static void +readXpm1Header(FILE * const stream, int * const widthP, int * const heightP, + int * const chars_per_pixelP, int * const ncolorsP, + pixel ** const colorsP, int ** const ptabP) { +/*---------------------------------------------------------------------------- + Read the header of the XPM file on stream 'stream'. Assume the + getline() stream is presently positioned to the beginning of the + file and it is a Version 1 XPM file. Leave the stream positioned + after the header. + + Return the information from the header the same as for readXpm3Header. +-----------------------------------------------------------------------------*/ + char line[MAX_LINE+1], str1[MAX_LINE+1], str2[MAX_LINE+1]; + char *t1; + char *t2; + int format, v; + int i, j; + bool processedStaticChar; + /* We have read up to and interpreted the "static char..." line */ + + *widthP = *heightP = *ncolorsP = *chars_per_pixelP = format = -1; + + /* Read the initial defines. */ + processedStaticChar = FALSE; + while (!processedStaticChar) { + getline(line, sizeof(line), stream); + + if (sscanf(line, "#define %s %d", str1, &v) == 2) { + char *t1; + if ((t1 = strrchr(str1, '_')) == NULL) + t1 = str1; + else + ++t1; + if (STREQ(t1, "format")) + format = v; + else if (STREQ(t1, "width")) + *widthP = v; + else if (STREQ(t1, "height")) + *heightP = v; + else if (STREQ(t1, "ncolors")) + *ncolorsP = v; + else if (STREQ(t1, "pixel")) + *chars_per_pixelP = v; + } else if (!strncmp(line, "static char", 11)) { + if ((t1 = strrchr(line, '_')) == NULL) + t1 = line; + else + ++t1; + processedStaticChar = TRUE; + } + } + /* File is positioned to "static char" line, which is in line[] and + t1 points to position of last "_" in the line, or the beginning of + the line if there is no "_" + */ + if (format == -1) + pm_error("missing or invalid format"); + if (format != 1) + pm_error("can't handle XPM version %d", format); + if (*widthP == -1) + pm_error("missing or invalid width"); + if (*heightP == -1) + pm_error("missing or invalid height"); + if (*ncolorsP == -1) + pm_error("missing or invalid ncolors"); + if (*chars_per_pixelP == -1) + pm_error("missing or invalid *chars_per_pixelP"); + if (*chars_per_pixelP > 2) + pm_message("WARNING: *chars_per_pixelP > 2 uses a lot of memory"); + + /* If there's a monochrome color table, skip it. */ + if (!strncmp(t1, "mono", 4)) { + for (;;) { + getline(line, sizeof(line), stream); + if (!strncmp(line, "static char", 11)) + break; + } + } + /* Allocate space for color table. */ + if (*chars_per_pixelP <= 2) { + /* Up to two chars per pixel, we can use an indexed table. */ + v = 1; + for (i = 0; i < *chars_per_pixelP; ++i) + v *= 256; + *colorsP = ppm_allocrow(v); + *ptabP = NULL; + } else { + /* Over two chars per pixel, we fall back on linear search. */ + *colorsP = ppm_allocrow(*ncolorsP); + MALLOCARRAY(*ptabP, *ncolorsP); + if (*ptabP == NULL) + pm_error("Unable to allocate memory for %d colors", *ncolorsP); + } + + /* Read color table. */ + for (i = 0; i < *ncolorsP; ++i) { + getline(line, sizeof(line), stream); + + if ((t1 = strchr(line, '"')) == NULL) + pm_error("D error scanning color table"); + if ((t2 = strchr(t1 + 1, '"')) == NULL) + pm_error("E error scanning color table"); + if (t2 - t1 - 1 != *chars_per_pixelP) + pm_error("wrong number of chars per pixel in color table"); + strncpy(str1, t1 + 1, t2 - t1 - 1); + str1[t2 - t1 - 1] = '\0'; + + if ((t1 = strchr(t2 + 1, '"')) == NULL) + pm_error("F error scanning color table"); + if ((t2 = strchr(t1 + 1, '"')) == NULL) + pm_error("G error scanning color table"); + strncpy(str2, t1 + 1, t2 - t1 - 1); + str2[t2 - t1 - 1] = '\0'; + + v = 0; + for (j = 0; j < *chars_per_pixelP; ++j) + v = (v << 8) + str1[j]; + if (*chars_per_pixelP <= 2) + /* Index into table. */ + (*colorsP)[v] = ppm_parsecolor(str2, + (pixval) PPM_MAXMAXVAL); + else { + /* Set up linear search table. */ + (*colorsP)[i] = ppm_parsecolor(str2, + (pixval) PPM_MAXMAXVAL); + (*ptabP)[i] = v; + } + } + /* Position to first line of raster (which is the line after + "static char ..."). + */ + for (;;) { + getline(line, sizeof(line), stream); + if (strncmp(line, "static char", 11) == 0) + break; + } +} + + + +static void +interpretXpmLine(char const line[], + int const chars_per_pixel, + int const ncolors, + int * const ptab, + int ** const cursorP, + int * const maxCursor) { +/*---------------------------------------------------------------------------- + Interpret one line of XPM input. The line is in 'line', and its + format is 'chars_per_pixel' characters per pixel. 'ptab' is the + color table that applies to the line, which table has 'ncolors' + colors. + + Put the colormap indexes for the pixels represented in 'line' at + *cursorP, lined up in the order they are in 'line', and return + *cursorP positioned just after the last one. + + If the line doesn't start with a quote (e.g. it is empty), we issue + a warning and just treat the line as one that describes no pixels. + + Stop processing the line either at the end of the line or when + the output would go beyond maxCursor, whichever comes first. +-----------------------------------------------------------------------------*/ + char * lineCursor; + + lineCursor = strchr(line, '"'); /* position to 1st quote in line */ + if (lineCursor == NULL) { + /* We've seen a purported XPM that had a blank line in it. Just + ignoring it was the right thing to do. 05.05.27. + */ + pm_message("WARNING: No opening quotation mark in XPM input " + "line which is supposed to be a line of raster data: " + "'%s'. Ignoring this line.", line); + } else { + ++lineCursor; /* Skip to first character after quote */ + + /* Handle pixels until a close quote, eol, or we've returned all + the pixels Caller wants. + */ + while (*lineCursor && *lineCursor != '"' && *cursorP <= maxCursor) { + int colorNumber; + int i; + colorNumber = 0; /* initial value */ + for (i = 0; i < chars_per_pixel; ++i) + colorNumber = (colorNumber << 8) + *(lineCursor++); + if (ptab == NULL) + /* colormap is indexed directly by XPM color number */ + *(*cursorP)++ = colorNumber; + else { + /* colormap shadows ptab[]. Find this color # in ptab[] */ + int i; + for (i = 0; i < ncolors && ptab[i] != colorNumber; ++i); + if (i < ncolors) + *(*cursorP)++ = i; + else + pm_error("Color number %d is in raster, but not in " + "colormap. Line it's in: '%s'", + colorNumber, line); + } + } + } +} + + + +static void +ReadXPMFile(FILE * const stream, int * const widthP, int * const heightP, + pixel ** const colorsP, int ** const dataP, + int * const transparentP) { +/*---------------------------------------------------------------------------- + Read the XPM file from stream 'stream'. + + Return the dimensions of the image as *widthP and *heightP. + Return the color map as *colorsP, which is an array of *ncolorsP + colors. + + Return the raster in newly malloced storage, an array of *widthP by + *heightP integers, each of which is an index into the colormap + *colorsP (and therefore less than *ncolorsP). Return the address + of the array as *dataP. + + In the colormap, put black for the transparent color, if the XPM + image contains one. +-----------------------------------------------------------------------------*/ + char line[MAX_LINE+1], str1[MAX_LINE+1]; + int totalpixels; + int *cursor; /* cursor into *dataP */ + int *maxcursor; /* value of above cursor for last pixel in image */ + int *ptab; /* colormap - malloc'ed */ + int rc; + int ncolors; + int chars_per_pixel; + + backup = FALSE; + + /* Read the header line */ + getline(line, sizeof(line), stream); + backup = TRUE; /* back up so next read reads this line again */ + + rc = sscanf(line, "/* %s */", str1); + if (rc == 1 && strncmp(str1, "XPM", 3) == 0) { + /* It's an XPM version 3 file */ + readXpm3Header(stream, widthP, heightP, &chars_per_pixel, + &ncolors, colorsP, &ptab, transparentP); + } else { /* try as an XPM version 1 file */ + /* Assume it's an XPM version 1 file */ + readXpm1Header(stream, widthP, heightP, &chars_per_pixel, + &ncolors, colorsP, &ptab); + *transparentP = -1; /* No transparency in version 1 */ + } + totalpixels = *widthP * *heightP; + MALLOCARRAY(*dataP, totalpixels); + if (*dataP == NULL) + pm_error("Could not get %d bytes of memory for image", totalpixels); + cursor = *dataP; + maxcursor = *dataP + totalpixels - 1; + getline(line, sizeof(line), stream); + /* read next line (first line may not always start with comment) */ + while (cursor <= maxcursor) { + if (strncmp(line, "/*", 2) == 0) { + /* It's a comment. Ignore it. */ + } else { + interpretXpmLine(line, chars_per_pixel, + ncolors, ptab, &cursor, maxcursor); + } + if (cursor <= maxcursor) + getline(line, sizeof(line), stream); + } + if (ptab) free(ptab); +} + + + +static void +writeOutput(FILE * const imageout_file, + FILE * const alpha_file, + int const cols, int const rows, + pixel * const colors, int * const data, + int transparent) { +/*---------------------------------------------------------------------------- + Write the image in 'data' to open PPM file stream 'imageout_file', + and the alpha mask for it to open PBM file stream 'alpha_file', + except if either is NULL, skip it. + + 'data' is an array of cols * rows integers, each one being an index + into the colormap 'colors'. + + Where the index 'transparent' occurs in 'data', the pixel is supposed + to be transparent. If 'transparent' < 0, no pixels are transparent. +-----------------------------------------------------------------------------*/ + int row; + pixel *pixrow; + bit * alpharow; + + if (imageout_file) + ppm_writeppminit(imageout_file, cols, rows, PPM_MAXMAXVAL, 0); + if (alpha_file) + pbm_writepbminit(alpha_file, cols, rows, 0); + + pixrow = ppm_allocrow(cols); + alpharow = pbm_allocrow(cols); + + for (row = 0; row < rows; ++row ) { + int col; + int * const datarow = data+(row*cols); + + for (col = 0; col < cols; ++col) { + pixrow[col] = colors[datarow[col]]; + if (datarow[col] == transparent) + alpharow[col] = PBM_BLACK; + else + alpharow[col] = PBM_WHITE; + } + if (imageout_file) + ppm_writeppmrow(imageout_file, + pixrow, cols, (pixval) PPM_MAXMAXVAL, 0); + if (alpha_file) + pbm_writepbmrow(alpha_file, alpharow, cols, 0); + } + ppm_freerow(pixrow); + pbm_freerow(alpharow); + + if (imageout_file) + pm_close(imageout_file); + if (alpha_file) + pm_close(alpha_file); +} + + + +int +main(int argc, char *argv[]) { + + FILE *ifp; + FILE *alpha_file, *imageout_file; + pixel *colormap; + int cols, rows; + int transparent; /* value of 'data' that means transparent */ + int *data; + /* The image as an array of width * height integers, each one + being an index int colormap[]. + */ + + struct cmdline_info cmdline; + + ppm_init(&argc, argv); + + parse_command_line(argc, argv, &cmdline); + + verbose = cmdline.verbose; + + if ( cmdline.input_filespec != NULL ) + ifp = pm_openr( cmdline.input_filespec); + else + ifp = stdin; + + if (cmdline.alpha_stdout) + alpha_file = stdout; + else if (cmdline.alpha_filename == NULL) + alpha_file = NULL; + else { + alpha_file = pm_openw(cmdline.alpha_filename); + } + + if (cmdline.alpha_stdout) + imageout_file = NULL; + else + imageout_file = stdout; + + ReadXPMFile(ifp, &cols, &rows, &colormap, &data, &transparent); + + pm_close(ifp); + + writeOutput(imageout_file, alpha_file, cols, rows, colormap, data, + transparent); + + free(colormap); + + return 0; +} diff --git a/converter/ppm/xvminitoppm.c b/converter/ppm/xvminitoppm.c new file mode 100644 index 00000000..dfc76fcf --- /dev/null +++ b/converter/ppm/xvminitoppm.c @@ -0,0 +1,207 @@ +/*============================================================================= + xvminitoppm +=============================================================================== + Convert XV mini thumbnail image to PPM. + + This replaces the program of the same name by Ingo Wilken + (Ingo.Wilken@informatik.uni-oldenburg.de), 1993. + + Written by Bryan Henderson in April 2006 and contributed to the public + domain. +=============================================================================*/ + +#include <assert.h> +#include <string.h> + +#include "pm_c_util.h" +#include "nstring.h" +#include "ppm.h" + +#define BUFSIZE 256 + + +typedef struct xvPalette { + unsigned int red[256]; + unsigned int grn[256]; + unsigned int blu[256]; +} xvPalette; + + +struct cmdlineInfo { + const char * inputFileName; +}; + + + +static void +parseCommandLine(int const argc, + char * argv[], + struct cmdlineInfo * const cmdlineP) { + + if (argc-1 < 1) + cmdlineP->inputFileName = "-"; + else { + cmdlineP->inputFileName = argv[1]; + + if (argc-1 > 1) + pm_error("Too many arguments: %u. Only argument is optional " + "input file name.", argc-1); + } +} + + + +static void +getline(FILE * const ifP, + char * const buf, + size_t const size) { + + char * rc; + + rc = fgets(buf, size, ifP); + if (rc == NULL) { + if (ferror(ifP)) + pm_perror("read error"); + else + pm_error("unexpected EOF"); + } +} + + + +static void +makeXvPalette(xvPalette * const xvPaletteP) { + + unsigned int paletteIndex; + unsigned int r; + + paletteIndex = 0; + + for (r = 0; r < 8; ++r) { + unsigned int g; + for (g = 0; g < 8; ++g) { + unsigned int b; + for (b = 0; b < 4; ++b) { + xvPaletteP->red[paletteIndex] = (r*255)/7; + xvPaletteP->grn[paletteIndex] = (g*255)/7; + xvPaletteP->blu[paletteIndex] = (b*255)/3; + ++paletteIndex; + } + } + } + +} + + + +static void +readXvHeader(FILE * const ifP, + unsigned int * const colsP, + unsigned int * const rowsP, + unsigned int * const maxvalP) { + + char buf[256]; + unsigned int cols, rows, maxval; + int rc; + bool endOfComments; + + getline(ifP, buf, sizeof(buf)); + + if (!STRNEQ(buf, "P7 332", 6)) + pm_error("Input is not a XV thumbnail picture. It does not " + "begin with the characters 'P7 332'."); + + endOfComments = FALSE; + while (!endOfComments) { + getline(ifP, buf, sizeof(buf)); + if (STRNEQ(buf, "#END_OF_COMMENTS", 16)) + endOfComments = TRUE; + else if (STRNEQ(buf, "#BUILTIN", 8)) + pm_error("This program does not know how to " + "convert builtin XV thumbnail pictures"); + } + getline(ifP, buf, sizeof(buf)); + rc = sscanf(buf, "%u %u %u", &cols, &rows, &maxval); + if (rc != 3) + pm_error("error parsing dimension info '%s'. " + "It does not consist of 3 decimal numbers.", buf); + if (maxval != 255) + pm_error("bogus XV thumbnail maxval %u. Should be 255", maxval); +} + + + +static void +writePpm(FILE * const ifP, + const xvPalette * const xvPaletteP, + unsigned int const cols, + unsigned int const rows, + pixval const maxval, + FILE * const ofP) { +/*---------------------------------------------------------------------------- + Write out the PPM image, from the XV-mini input file ifP, which is + positioned to the raster. + + The raster contains indices into the palette *xvPaletteP. +-----------------------------------------------------------------------------*/ + pixel * pixrow; + unsigned int row; + + pixrow = ppm_allocrow(cols); + + ppm_writeppminit(ofP, cols, rows, maxval, 0); + + for (row = 0; row < rows; ++row) { + unsigned int col; + for (col = 0; col < cols; ++col) { + int byte; + byte = fgetc(ifP); + if (byte == EOF) + pm_error("unexpected EOF"); + else { + unsigned int const paletteIndex = byte; + assert(byte > 0); + + PPM_ASSIGN(pixrow[col], + xvPaletteP->red[paletteIndex], + xvPaletteP->grn[paletteIndex], + xvPaletteP->blu[paletteIndex]); + } + } + ppm_writeppmrow(ofP, pixrow, cols, maxval, 0); + } + + ppm_freerow(pixrow); +} + + + +int +main(int argc, + char * argv[]) { + + struct cmdlineInfo cmdline; + FILE * ifP; + unsigned int cols, rows; + pixval maxval; + xvPalette xvPalette; + + ppm_init(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + ifP = pm_openr(cmdline.inputFileName); + + makeXvPalette(&xvPalette); + + readXvHeader(ifP, &cols, &rows, &maxval); + + writePpm(ifP, &xvPalette, cols, rows, maxval, stdout); + + pm_close(ifP); + + return 0; +} + + + diff --git a/converter/ppm/yuvsplittoppm.c b/converter/ppm/yuvsplittoppm.c new file mode 100644 index 00000000..0f5e19a3 --- /dev/null +++ b/converter/ppm/yuvsplittoppm.c @@ -0,0 +1,251 @@ +/* yuvsplittoppm.c - construct a portable pixmap from 3 raw files: +** - basename.Y : The Luminance chunk at the size of the Image +** - basename.U : The Chrominance chunk U at 1/4 +** - basename.V : The Chrominance chunk V at 1/4 +** The subsampled U and V values are made by arithmetic mean. +** +** If ccir601 is defined, the produced YUV triples have been scaled again +** to fit into the smaller range of values for this standard. +** +** by Marcel Wijkstra <wijkstra@fwi.uva.nl> +** +** Based on ppmtoyuvsplit.c +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include <string.h> +#include "ppm.h" +#include "nstring.h" +#include "shhopt.h" +#include "mallocvar.h" + +/* x must be signed for the following to work correctly */ +#define limit(x) (((x>0xffffff)?0xff0000:((x<=0xffff)?0:x&0xff0000))>>16) + +struct cmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char *filenameBase; + unsigned int width; + unsigned int height; + unsigned int ccir601; + /* Whether to create YUV in JFIF(JPEG) or CCIR.601(MPEG) scale */ +}; + + +static void +parseCommandLine(int argc, + char ** argv, + struct cmdlineInfo *cmdlineP ) { +/*---------------------------------------------------------------------------- + Parse program command line described in Unix standard form by argc + and argv. Return the information in the options as *cmdlineP. + + If command line is internally inconsistent (invalid options, etc.), + issue error message to stderr and abort program. + + Note that the strings we return are stored in the storage that + was passed to us as the argv array. We also trash *argv. +-----------------------------------------------------------------------------*/ + optEntry *option_def = malloc(100*sizeof(optEntry)); + /* Instructions to optParseOptions3 on how to parse our options. + */ + optStruct3 opt; + + unsigned int option_def_index; + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "ccir601", OPT_FLAG, NULL, + &cmdlineP->ccir601, 0); + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ + + optParseOptions3( &argc, argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + + if (argc-1 !=3) + pm_error("You must specify 3 arguments. " + "You specified %d", argc-1); + else { + int width, height; + cmdlineP->filenameBase = argv[1]; + width = atoi(argv[2]); + if (width < 1) + pm_error("Width must be at least 1. You specified %d", width); + height = atoi(argv[3]); + if (height < 1) + pm_error("Height must be at least 1. You specified %d", height); + cmdlineP->width = width; + cmdlineP->height = height; + } +} + + + +static void +computeTwoOutputRows(int const cols, + bool const ccir601, + unsigned char * const y1buf, + unsigned char * const y2buf, + unsigned char * const ubuf, + unsigned char * const vbuf, + pixel * const pixelrow1, + pixel * const pixelrow2) { + + int col; + + for (col = 0; col < cols; col += 2) { + long int r0,g0,b0,r1,g1,b1,r2,g2,b2,r3,g3,b3; + long int u,v,y0,y1,y2,y3,u0,u1,u2,u3,v0,v1,v2,v3; + + y0 = y1buf[col]; + y1 = y1buf[col+1]; + y2 = y2buf[col]; + y3 = y2buf[col+1]; + + u = ubuf[col/2] - 128; + v = vbuf[col/2] - 128; + + if (ccir601) { + y0 = ((y0-16)*255)/219; + y1 = ((y1-16)*255)/219; + y2 = ((y2-16)*255)/219; + y3 = ((y3-16)*255)/219; + + u = (u*255)/224 ; + v = (v*255)/224 ; + } + /* mean the chroma for subsampling */ + + u0=u1=u2=u3=u; + v0=v1=v2=v3=v; + + + /* The inverse of the JFIF RGB to YUV Matrix for $00010000 = 1.0 + + [Y] [65496 0 91880] [R] + [U] = [65533 -22580 -46799] [G] + [V] [65537 116128 -8] [B] + + */ + + r0 = 65536 * y0 + 91880 * v0; + g0 = 65536 * y0 - 22580 * u0 - 46799 * v0; + b0 = 65536 * y0 + 116128 * u0 ; + + r1 = 65536 * y1 + 91880 * v1; + g1 = 65536 * y1 - 22580 * u1 - 46799 * v1; + b1 = 65536 * y1 + 116128 * u1 ; + + r2 = 65536 * y2 + 91880 * v2; + g2 = 65536 * y2 - 22580 * u2 - 46799 * v2; + b2 = 65536 * y2 + 116128 * u2 ; + + r3 = 65536 * y3 + 91880 * v3; + g3 = 65536 * y3 - 22580 * u3 - 46799 * v3; + b3 = 65536 * y3 + 116128 * u3 ; + + r0 = limit(r0); + r1 = limit(r1); + r2 = limit(r2); + r3 = limit(r3); + g0 = limit(g0); + g1 = limit(g1); + g2 = limit(g2); + g3 = limit(g3); + b0 = limit(b0); + b1 = limit(b1); + b2 = limit(b2); + b3 = limit(b3); + + PPM_ASSIGN(pixelrow1[col], (pixval)r0, (pixval)g0, (pixval)b0); + PPM_ASSIGN(pixelrow1[col+1], (pixval)r1, (pixval)g1, (pixval)b1); + PPM_ASSIGN(pixelrow2[col], (pixval)r2, (pixval)g2, (pixval)b2); + PPM_ASSIGN(pixelrow2[col+1], (pixval)r3, (pixval)g3, (pixval)b3); + } +} + + + +int +main(int argc, char **argv) { + + struct cmdlineInfo cmdline; + FILE *vf,*uf,*yf; + int cols, rows; + pixel *pixelrow1,*pixelrow2; + int row; + unsigned char *y1buf,*y2buf,*ubuf,*vbuf; + const char * ufname; + const char * vfname; + const char * yfname; + + ppm_init(&argc, argv); + + parseCommandLine(argc, argv, &cmdline); + + asprintfN(&ufname, "%s.U", cmdline.filenameBase); + asprintfN(&vfname, "%s.V", cmdline.filenameBase); + asprintfN(&yfname, "%s.Y", cmdline.filenameBase); + + uf = pm_openr(ufname); + vf = pm_openr(vfname); + yf = pm_openr(yfname); + + ppm_writeppminit(stdout, cmdline.width, cmdline.height, 255, 0); + + if (cmdline.width % 2 != 0) { + pm_message("Warning: odd width; last column ignored"); + cols = cmdline.width - 1; + } else + cols = cmdline.width; + + if (cmdline.height % 2 != 0) { + pm_message("Warning: odd height; last row ignored"); + rows = cmdline.height - 1; + } else + rows = cmdline.height; + + pixelrow1 = ppm_allocrow(cols); + pixelrow2 = ppm_allocrow(cols); + + MALLOCARRAY_NOFAIL(y1buf, cmdline.width); + MALLOCARRAY_NOFAIL(y2buf, cmdline.width); + MALLOCARRAY_NOFAIL(ubuf, cmdline.width/2); + MALLOCARRAY_NOFAIL(vbuf, cmdline.width/2); + + for (row = 0; row < rows; row += 2) { + fread(y1buf, cmdline.width, 1, yf); + fread(y2buf, cmdline.width, 1, yf); + fread(ubuf, cmdline.width/2, 1, uf); + fread(vbuf, cmdline.width/2, 1, vf); + + computeTwoOutputRows(cols, cmdline.ccir601, + y1buf, y2buf, ubuf, vbuf, + pixelrow1, pixelrow2); + + ppm_writeppmrow(stdout, pixelrow1, cols, (pixval) 255, 0); + ppm_writeppmrow(stdout, pixelrow2, cols, (pixval) 255, 0); + } + pm_close(stdout); + + strfree(yfname); + strfree(vfname); + strfree(ufname); + + pm_close(yf); + pm_close(uf); + pm_close(vf); + + exit(0); +} diff --git a/converter/ppm/yuvtoppm.c b/converter/ppm/yuvtoppm.c new file mode 100644 index 00000000..2b44c65f --- /dev/null +++ b/converter/ppm/yuvtoppm.c @@ -0,0 +1,115 @@ +/* yuvtoppm.c - convert Abekas YUV bytes into a portable pixmap +** +** by Marc Boucher +** Internet: marc@PostImage.COM +** +** Based on Example Conversion Program, A60/A64 Digital Video Interface +** Manual, page 69 +** +** Uses integer arithmetic rather than floating point for better performance +** +** Copyright (C) 1991 by DHD PostImage Inc. +** Copyright (C) 1987 by Abekas Video Systems Inc. +** Copyright (C) 1991 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + +#include "ppm.h" +#include "mallocvar.h" + +/* x must be signed for the following to work correctly */ +#define limit(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16) + +int +main(argc, argv) + char **argv; +{ + FILE *ifp; + pixel *pixrow; + int argn, rows, cols, row; + const char * const usage = "<width> <height> [yuvfile]"; + struct yuv { + /* This is an element of a YUV file. It describes two + side-by-side pixels. + */ + unsigned char u; + unsigned char y1; + unsigned char v; + unsigned char y2; + } *yuvbuf; + + ppm_init(&argc, argv); + + argn = 1; + + if (argn + 2 > argc) + pm_usage(usage); + + cols = atoi(argv[argn++]); + rows = atoi(argv[argn++]); + if (cols <= 0 || rows <= 0) + pm_usage(usage); + + if (argn < argc) { + ifp = pm_openr(argv[argn]); + ++argn; + } else + ifp = stdin; + + if (argn != argc) + pm_usage(usage); + + if (cols % 2 != 0) { + pm_error("Number of columns (%d) is odd. A YUV image must have an " + "even number of columns.", cols); + } + + ppm_writeppminit(stdout, cols, rows, (pixval) 255, 0); + pixrow = ppm_allocrow(cols); + MALLOCARRAY(yuvbuf, (cols+1)/2); + if (yuvbuf == NULL) + pm_error("Unable to allocate YUV buffer for %d columns.", cols); + + for (row = 0; row < rows; ++row) { + int col; + + fread(yuvbuf, cols * 2, 1, ifp); + + for (col = 0; col < cols; col += 2) { + /* Produce two pixels in pixrow[] */ + int y1, u, v, y2, r, g, b; + + u = yuvbuf[col/2].u-128; + + y1 = yuvbuf[col/2].y1 - 16; + if (y1 < 0) y1 = 0; + + v = yuvbuf[col/2].v - 128; + + y2 = yuvbuf[col/2].y2 - 16; + if (y2 < 0) y2 = 0; + + r = 104635 * v; + g = -25690 * u + -53294 * v; + b = 132278 * u; + + y1*=76310; y2*=76310; + + PPM_ASSIGN(pixrow[col], limit(r+y1), limit(g+y1), limit(b+y1)); + PPM_ASSIGN(pixrow[col+1], limit(r+y2), limit(g+y2), limit(b+y2)); + } + ppm_writeppmrow(stdout, pixrow, cols, (pixval) 255, 0); + } + free(yuvbuf); + ppm_freerow(pixrow); + pm_close(ifp); + pm_close(stdout); + + exit(0); +} |