/* ppmpat.c - make a pixmap ** ** 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. */ #define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */ #define _XOPEN_SOURCE 500 /* Make sure strdup() is in string.h */ /* get M_PI in math.h */ #define _BSD_SOURCE /* Make sure strdup() is in */ #include #include #include #include #include "pm_c_util.h" #include "mallocvar.h" #include "nstring.h" #include "rand.h" #include "shhopt.h" #include "ppm.h" #include "ppmdraw.h" typedef enum { PAT_GINGHAM2, PAT_GINGHAM3, PAT_MADRAS, PAT_TARTAN, PAT_ARGYLE1, PAT_ARGYLE2, PAT_POLES, PAT_SQUIG, PAT_CAMO, PAT_ANTICAMO } Pattern; typedef struct { /*---------------------------------------------------------------------------- An ordered list of colors with a cursor. -----------------------------------------------------------------------------*/ unsigned int count; unsigned int index; /* Current position in the list */ pixel * color; /* Malloced array 'count' in size. */ } ColorTable; struct CmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ Pattern basePattern; unsigned int width; unsigned int height; unsigned int colorSpec; ColorTable colorTable; unsigned int randomseed; unsigned int randomseedSpec; ppmd_drawproc * drawProc; }; static pixel averageTwoColors(pixel const p1, pixel const p2) { pixel p; PPM_ASSIGN(p, (PPM_GETR(p1) + PPM_GETR(p2)) / 2, (PPM_GETG(p1) + PPM_GETG(p2)) / 2, (PPM_GETB(p1) + PPM_GETB(p2)) / 2); return p; } static ppmd_drawproc average_drawproc; static void average_drawproc(pixel ** const pixels, int const cols, int const rows, pixval const maxval, int const col, int const row, const void * const clientdata) { /*---------------------------------------------------------------------------- Reset the pixel's color to the average of the original color and the color indicated by * clientdata. -----------------------------------------------------------------------------*/ if (col >= 0 && col < cols && row >= 0 && row < rows) pixels[row][col] = averageTwoColors(pixels[row][col], *((const pixel*) clientdata)); } static ppmd_drawproc checkerboard_drawproc; static void checkerboard_drawproc(pixel ** const pixels, int const cols, int const rows, pixval const maxval, int const col, int const row, const void * const clientdata) { /*---------------------------------------------------------------------------- If col and row are both even or both odd, do nothing. If one is even and the other is odd, set the pixel's color to that indicated by * clientdata. -----------------------------------------------------------------------------*/ if (col >= 0 && col < cols && row >= 0 && row < rows && row % 2 != col % 2) pixels[row][col] = *((const pixel*) clientdata); } static void validateColorCount(Pattern const basePattern, unsigned int const colorCount) { if (colorCount == 0) pm_error("-color: no colors specified"); switch (basePattern) { case PAT_GINGHAM2: case PAT_ARGYLE1: if (colorCount != 2) pm_error("Wrong number of colors: %u. " "2 colors are required for the specified pattern.", colorCount); break; case PAT_GINGHAM3: case PAT_MADRAS: case PAT_TARTAN: case PAT_ARGYLE2: if (colorCount != 3) pm_error("Wrong number of colors: %u. " "3 colors are required for the specified pattern.", colorCount); break; case PAT_POLES: if (colorCount < 2) pm_error("Too few colors: %u. " "At least 2 colors are required " "for the specified pattern.", colorCount); break; case PAT_SQUIG: case PAT_CAMO: case PAT_ANTICAMO: if (colorCount < 3) pm_error("Wrong number of colors: %u. " "At least 3 colors are required " "for the specified pattern.", colorCount); break; default: pm_error("INTERNAL ERROR."); } } static void parseColorOpt(const char ** const colorText, ColorTable * const colorTableP, Pattern const basePattern) { /*---------------------------------------------------------------------------- String-list argument to -color is a comma-separated array of color names or values, e.g.: "-color=red,white,blue" "-color=rgb:ff/ff/ff,rgb:00/00/00,rgb:80/80/ff" Input: Color name/value string-list: colorText[] Output values: Color array: colorTableP->color[] Number of colors found: colorTableP->colors ----------------------------------------------------------------------------*/ unsigned int colorCount; unsigned int i; pixel * inColor; for (colorCount = 0; colorText[colorCount] != NULL; ++colorCount) ; MALLOCARRAY(inColor, colorCount); if (!inColor) pm_error("Failed to allocate table space for %u colors " "specified by -color", colorCount); for (i = 0; i < colorCount; ++i) inColor[i] = ppm_parsecolor(colorText[i], PPM_MAXMAXVAL); validateColorCount(basePattern, colorCount); colorTableP->count = colorCount; colorTableP->index = 0; /* initial value */ colorTableP->color = inColor; } static void parseCommandLine(int argc, const 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 ** colorText; unsigned int basePatternCount; unsigned int gingham2; unsigned int gingham3; unsigned int madras; unsigned int tartan; unsigned int argyle1; unsigned int argyle2; unsigned int poles; unsigned int squig; unsigned int camo; unsigned int anticamo; unsigned int meshSpec; MALLOCARRAY_NOFAIL(option_def, 100); option_def_index = 0; /* incremented by OPTENTRY */ OPTENT3(0, "gingham2", OPT_FLAG, NULL, &gingham2, 0); OPTENT3(0, "g2", OPT_FLAG, NULL, &gingham2, 0); OPTENT3(0, "gingham3", OPT_FLAG, NULL, &gingham3, 0); OPTENT3(0, "g3", OPT_FLAG, NULL, &gingham3, 0); OPTENT3(0, "madras", OPT_FLAG, NULL, &madras, 0); OPTENT3(0, "tartan", OPT_FLAG, NULL, &tartan, 0); OPTENT3(0, "argyle1", OPT_FLAG, NULL, &argyle1, 0); OPTENT3(0, "argyle2", OPT_FLAG, NULL, &argyle2, 0); OPTENT3(0, "poles", OPT_FLAG, NULL, &poles, 0); OPTENT3(0, "squig", OPT_FLAG, NULL, &squig, 0); OPTENT3(0, "camo", OPT_FLAG, NULL, &camo, 0); OPTENT3(0, "anticamo", OPT_FLAG, NULL, &anticamo, 0); OPTENT3(0, "color", OPT_STRINGLIST, &colorText, &cmdlineP->colorSpec, 0); OPTENT3(0, "randomseed", OPT_UINT, &cmdlineP->randomseed, &cmdlineP->randomseedSpec, 0); OPTENT3(0, "mesh", OPT_FLAG, NULL, &meshSpec, 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 */ pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ free(option_def); basePatternCount = gingham2 + gingham3 + madras + tartan + argyle1 + argyle2 + poles + squig + camo + anticamo; if (basePatternCount < 1) pm_error("You must specify a base pattern option such as -gingham2"); else if (basePatternCount > 1) pm_error("You may not specify more than one base pattern option. " "You specified %u", basePatternCount); else { if (gingham2) cmdlineP->basePattern = PAT_GINGHAM2; else if (gingham3) cmdlineP->basePattern = PAT_GINGHAM3; else if (madras) cmdlineP->basePattern = PAT_MADRAS; else if (tartan) cmdlineP->basePattern = PAT_TARTAN; else if (argyle1) cmdlineP->basePattern = PAT_ARGYLE1; else if (argyle2) cmdlineP->basePattern = PAT_ARGYLE2; else if (poles) cmdlineP->basePattern = PAT_POLES; else if (squig) cmdlineP->basePattern = PAT_SQUIG; else if (camo) cmdlineP->basePattern = PAT_CAMO; else if (anticamo) cmdlineP->basePattern = PAT_ANTICAMO; else assert(false); /* Every possibility is accounted for */ } if (cmdlineP->colorSpec) { parseColorOpt(colorText, &cmdlineP->colorTable, cmdlineP->basePattern); free(colorText); } else cmdlineP->colorTable.count = 0; if (meshSpec) { if (gingham2 + gingham3 + madras + tartan > 0) cmdlineP->drawProc = &checkerboard_drawproc; else pm_message("-mesh ignored (no effect with specified pattern)"); } else cmdlineP->drawProc = &average_drawproc; if (argc-1 != 2) pm_error("You must specify 2 non-option arguments: width and height " "in pixels. You specified %u", argc-1); else { cmdlineP->width = pm_parse_width(argv[1]); cmdlineP->height = pm_parse_height(argv[2]); if (cmdlineP->width < 1) pm_error("Width must be at least 1 pixel"); if (cmdlineP->height < 1) pm_error("Height must be at least 1 pixel"); } } static void freeCmdline(struct CmdlineInfo const cmdline) { if (cmdline.colorSpec) free(cmdline.colorTable.color); } static void validateComputableDimensions(unsigned int const cols, unsigned int const rows) { /* Notes on width and height limits: cols * 3, rows * 3 appear in madras, tartan cols*rows appears in poles cols+rows appears in squig PPMD functions use signed integers for pixel positions (because they allow you to specify points off the canvas). */ if (cols > INT_MAX/4 || rows > INT_MAX/4 || rows > INT_MAX/cols) pm_error("Width and/or height are way too large: %u x %u", cols, rows); } static pixel randomColor(struct pm_randSt * const randStP, pixval const maxval) { pixel p; PPM_ASSIGN(p, pm_rand(randStP) % (maxval + 1), pm_rand(randStP) % (maxval + 1), pm_rand(randStP) % (maxval + 1) ); return p; } static double const DARK_THRESH = 0.25; static pixel randomBrightColor(struct pm_randSt * const randStP, pixval const maxval) { pixel p; do { p = randomColor(randStP, maxval); } while (PPM_LUMIN(p) <= maxval * DARK_THRESH); return p; } static pixel randomDarkColor(struct pm_randSt * const randStP, pixval const maxval) { pixel p; do { p = randomColor(randStP, maxval); } while (PPM_LUMIN(p) > maxval * DARK_THRESH); return p; } static void nextColor(ColorTable * const colorTableP) { /*---------------------------------------------------------------------------- Increment index, return it to 0 if we have used all the colors -----------------------------------------------------------------------------*/ colorTableP->index = (colorTableP->index + 1) % colorTableP->count; } static void nextColorBg(ColorTable * const colorTableP) { /*---------------------------------------------------------------------------- Increment index, return it to 1 if we have used all the colors (color[0] is the background color, it's outside the cycle) -----------------------------------------------------------------------------*/ colorTableP->index = colorTableP->index % (colorTableP->count - 1) + 1; /* Works when index == 0, but no callers rely on this. */ } /*---------------------------------------------------------------------------- Camouflage stuff -----------------------------------------------------------------------------*/ static pixel randomAnticamoColor(struct pm_randSt * const randStP, pixval const maxval) { int v1, v2, v3; pixel p; v1 = (maxval + 1) / 4; v2 = (maxval + 1) / 2; v3 = 3 * v1; switch (pm_rand(randStP) % 15) { case 0: case 1: PPM_ASSIGN(p, pm_rand(randStP) % v1 + v3, pm_rand(randStP) % v2, pm_rand(randStP) % v2); break; case 2: case 3: PPM_ASSIGN(p, pm_rand(randStP) % v2, pm_rand(randStP) % v1 + v3, pm_rand(randStP) % v2); break; case 4: case 5: PPM_ASSIGN(p, pm_rand(randStP) % v2, pm_rand(randStP) % v2, pm_rand(randStP) % v1 + v3); break; case 6: case 7: case 8: PPM_ASSIGN(p, pm_rand(randStP) % v2, pm_rand(randStP) % v1 + v3, pm_rand(randStP) % v1 + v3); break; case 9: case 10: case 11: PPM_ASSIGN(p, pm_rand(randStP) % v1 + v3, pm_rand(randStP) % v2, pm_rand(randStP) % v1 + v3); break; case 12: case 13: case 14: PPM_ASSIGN(p, pm_rand(randStP) % v1 + v3, pm_rand(randStP) % v1 + v3, pm_rand(randStP) % v2); break; } return p; } static pixel randomCamoColor(struct pm_randSt * const randStP, pixval const maxval) { int const v1 = (maxval + 1 ) / 8; int const v2 = (maxval + 1 ) / 4; int const v3 = (maxval + 1 ) / 2; pixel p; switch (pm_rand(randStP) % 10) { case 0: case 1: case 2: /* light brown */ PPM_ASSIGN(p, pm_rand(randStP) % v3 + v3, pm_rand(randStP) % v3 + v2, pm_rand(randStP) % v3 + v2); break; case 3: case 4: case 5: /* dark green */ PPM_ASSIGN(p, pm_rand(randStP) % v2, pm_rand(randStP) % v2 + 3 * v1, pm_rand(randStP) % v2); break; case 6: case 7: /* brown */ PPM_ASSIGN(p, pm_rand(randStP) % v2 + v2, pm_rand(randStP) % v2, pm_rand(randStP) % v2); break; case 8: case 9: /* dark brown */ PPM_ASSIGN(p, pm_rand(randStP) % v1 + v1, pm_rand(randStP) % v1, pm_rand(randStP) % v1); break; } return p; } static float rnduni(struct pm_randSt * const randStP) { return pm_rand(randStP) % 32767 / 32767.0; } static void clearBackgroundCamo(pixel ** const pixels, unsigned int const cols, unsigned int const rows, pixval const maxval, ColorTable * const colorTableP, struct pm_randSt * const randStP, bool const antiflag) { pixel color; if (colorTableP->count > 0) { color = colorTableP->color[0]; } else if (antiflag) color = randomAnticamoColor(randStP, maxval); else color = randomCamoColor(randStP, maxval); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 0, cols, rows, PPMD_NULLDRAWPROC, &color); } static void camoFill(pixel ** const pixels, unsigned int const cols, unsigned int const rows, pixval const maxval, struct fillobj * const fh, ColorTable * const colorTableP, struct pm_randSt * const randStP, bool const antiflag) { pixel color; if (colorTableP->count > 0) { assert(colorTableP->index < colorTableP->count); color = colorTableP->color[colorTableP->index]; nextColorBg(colorTableP); } else if (antiflag) color = randomAnticamoColor(randStP, maxval); else color = randomCamoColor(randStP, maxval); ppmd_fill(pixels, cols, rows, maxval, fh, PPMD_NULLDRAWPROC, &color); } #define BLOBRAD 50 #define MIN_POINTS 7 #define MAX_POINTS 13 #define MIN_ELLIPSE_FACTOR 0.5 #define MAX_ELLIPSE_FACTOR 2.0 #define MIN_POINT_FACTOR 0.5 #define MAX_POINT_FACTOR 2.0 static void computeXsYs(int * const xs, int * const ys, unsigned int const cols, unsigned int const rows, unsigned int const pointCt, struct pm_randSt * const randStP) { unsigned int const cx = pm_rand(randStP) % cols; unsigned int const cy = pm_rand(randStP) % rows; double const a = rnduni(randStP) * (MAX_ELLIPSE_FACTOR - MIN_ELLIPSE_FACTOR) + MIN_ELLIPSE_FACTOR; double const b = rnduni(randStP) * (MAX_ELLIPSE_FACTOR - MIN_ELLIPSE_FACTOR) + MIN_ELLIPSE_FACTOR; double const theta = rnduni(randStP) * 2.0 * M_PI; unsigned int p; for (p = 0; p < pointCt; ++p) { double const c = rnduni(randStP) * (MAX_POINT_FACTOR - MIN_POINT_FACTOR) + MIN_POINT_FACTOR; double const tx = a * sin(p * 2.0 * M_PI / pointCt); double const ty = b * cos(p * 2.0 * M_PI / pointCt); double const tang = atan2(ty, tx) + theta; xs[p] = MAX(0, MIN(cols-1, cx + BLOBRAD * c * sin(tang))); ys[p] = MAX(0, MIN(rows-1, cy + BLOBRAD * c * cos(tang))); } } static void camo(pixel ** const pixels, unsigned int const cols, unsigned int const rows, ColorTable * const colorTableP, struct pm_randSt * const randStP, pixval const maxval, bool const antiflag) { unsigned int const n = (rows * cols) / SQR(BLOBRAD) * 5; unsigned int i; clearBackgroundCamo(pixels, cols, rows, maxval, colorTableP, randStP, antiflag); if (colorTableP->count > 0) { assert(colorTableP->count > 1); colorTableP->index = 1; /* Foreground colors start at 1 */ } for (i = 0; i < n; ++i) { unsigned int const pointCt = pm_rand(randStP) % (MAX_POINTS - MIN_POINTS + 1) + MIN_POINTS; int xs[MAX_POINTS], ys[MAX_POINTS]; int x0, y0; struct fillobj * fh; computeXsYs(xs, ys, cols, rows, pointCt, randStP); x0 = (xs[0] + xs[pointCt - 1]) / 2; y0 = (ys[0] + ys[pointCt - 1]) / 2; fh = ppmd_fill_create(); ppmd_polyspline( pixels, cols, rows, maxval, x0, y0, pointCt, xs, ys, x0, y0, ppmd_fill_drawproc, fh); camoFill(pixels, cols, rows, maxval, fh, colorTableP, randStP, antiflag); ppmd_fill_destroy(fh); } } /*---------------------------------------------------------------------------- Plaid patterns -----------------------------------------------------------------------------*/ static void gingham2(pixel ** const pixels, unsigned int const cols, unsigned int const rows, ColorTable const colorTable, struct pm_randSt * const randStP, ppmd_drawproc * const drawproc, pixval const maxval) { bool const colorSpec = (colorTable.count > 0); pixel const backcolor = colorSpec ? colorTable.color[0] : randomDarkColor(randStP, maxval); pixel const forecolor = colorSpec ? colorTable.color[1] : randomBrightColor(randStP, maxval); unsigned int const colso2 = cols / 2; unsigned int const rowso2 = rows / 2; /* Warp. */ ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 0, colso2, rows, PPMD_NULLDRAWPROC, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, colso2, 0, cols - colso2, rows, PPMD_NULLDRAWPROC, &forecolor); /* Woof. */ ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 0, cols, rowso2, drawproc, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, rowso2, cols, rows - rowso2, drawproc, &forecolor); } static void gingham3(pixel ** const pixels, unsigned int const cols, unsigned int const rows, ColorTable const colorTable, struct pm_randSt * const randStP, ppmd_drawproc * const drawproc, pixval const maxval) { bool const colorSpec = (colorTable.count > 0); pixel const backcolor = colorSpec ? colorTable.color[0] : randomDarkColor(randStP, maxval); pixel const fore1color = colorSpec ? colorTable.color[1] : randomBrightColor(randStP, maxval); pixel const fore2color = colorSpec ? colorTable.color[2] : randomBrightColor(randStP, maxval); unsigned int const colso4 = cols / 4; unsigned int const rowso4 = rows / 4; /* Warp. */ ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 0, colso4, rows, PPMD_NULLDRAWPROC, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, colso4, 0, colso4, rows, PPMD_NULLDRAWPROC, &fore1color); ppmd_filledrectangle( pixels, cols, rows, maxval, 2 * colso4, 0, colso4, rows, PPMD_NULLDRAWPROC, &fore2color); ppmd_filledrectangle( pixels, cols, rows, maxval, 3 * colso4, 0, cols - colso4, rows, PPMD_NULLDRAWPROC, &fore1color); /* Woof. */ ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 0, cols, rowso4, drawproc, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, rowso4, cols, rowso4, drawproc, &fore1color); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 2 * rowso4, cols, rowso4, drawproc, &fore2color); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 3 * rowso4, cols, rows - rowso4, drawproc, &fore1color); } static void madras(pixel ** const pixels, unsigned int const cols, unsigned int const rows, ColorTable const colorTable, struct pm_randSt * const randStP, ppmd_drawproc * const drawproc, pixval const maxval) { bool const colorSpec = (colorTable.count > 0); pixel const backcolor = colorSpec ? colorTable.color[0] : randomDarkColor(randStP, maxval); pixel const fore1color = colorSpec ? colorTable.color[1] : randomBrightColor(randStP, maxval); pixel const fore2color = colorSpec ? colorTable.color[2] : randomBrightColor(randStP, maxval); unsigned int const cols2 = cols * 2 / 44; unsigned int const rows2 = rows * 2 / 44; unsigned int const cols3 = cols * 3 / 44; unsigned int const rows3 = rows * 3 / 44; unsigned int const cols12 = cols - 10 * cols2 - 4 * cols3; unsigned int const rows12 = rows - 10 * rows2 - 4 * rows3; unsigned int const cols6a = cols12 / 2; unsigned int const rows6a = rows12 / 2; unsigned int const cols6b = cols12 - cols6a; unsigned int const rows6b = rows12 - rows6a; /* Warp. */ ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 0, cols2, rows, PPMD_NULLDRAWPROC, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, cols2, 0, cols3, rows, PPMD_NULLDRAWPROC, &fore1color); ppmd_filledrectangle( pixels, cols, rows, maxval, cols2 + cols3, 0, cols2, rows, PPMD_NULLDRAWPROC, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 2 * cols2 + cols3, 0, cols2, rows, PPMD_NULLDRAWPROC, &fore2color); ppmd_filledrectangle( pixels, cols, rows, maxval, 3 * cols2 + cols3, 0, cols2, rows, PPMD_NULLDRAWPROC, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 4 * cols2 + cols3, 0, cols6a, rows, PPMD_NULLDRAWPROC, &fore1color); ppmd_filledrectangle( pixels, cols, rows, maxval, 4 * cols2 + cols3 + cols6a, 0, cols2, rows, PPMD_NULLDRAWPROC, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 5 * cols2 + cols3 + cols6a, 0, cols3, rows, PPMD_NULLDRAWPROC, &fore2color); ppmd_filledrectangle( pixels, cols, rows, maxval, 5 * cols2 + 2 * cols3 + cols6a, 0, cols2, rows, PPMD_NULLDRAWPROC, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 6 * cols2 + 2 * cols3 + cols6a, 0, cols3, rows, PPMD_NULLDRAWPROC, &fore2color); ppmd_filledrectangle( pixels, cols, rows, maxval, 6 * cols2 + 3 * cols3 + cols6a, 0, cols2, rows, PPMD_NULLDRAWPROC, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 7 * cols2 + 3 * cols3 + cols6a, 0, cols6b, rows, PPMD_NULLDRAWPROC, &fore1color); ppmd_filledrectangle( pixels, cols, rows, maxval, 7 * cols2 + 3 * cols3 + cols6a + cols6b, 0, cols2, rows, PPMD_NULLDRAWPROC, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 8 * cols2 + 3 * cols3 + cols6a + cols6b, 0, cols2, rows, PPMD_NULLDRAWPROC, &fore2color); ppmd_filledrectangle( pixels, cols, rows, maxval, 9 * cols2 + 3 * cols3 + cols6a + cols6b, 0, cols2, rows, PPMD_NULLDRAWPROC, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 10 * cols2 + 3 * cols3 + cols6a + cols6b, 0, cols3, rows, PPMD_NULLDRAWPROC, &fore1color); /* Woof. */ ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 0, cols, rows2, drawproc, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, rows2, cols, rows3, drawproc, &fore2color); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, rows2 + rows3, cols, rows2, drawproc, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 2 * rows2 + rows3, cols, rows2, drawproc, &fore1color); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 3 * rows2 + rows3, cols, rows2, drawproc, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 4 * rows2 + rows3, cols, rows6a, drawproc, &fore2color); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 4 * rows2 + rows3 + rows6a, cols, rows2, drawproc, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 5 * rows2 + rows3 + rows6a, cols, rows3, drawproc, &fore1color); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 5 * rows2 + 2 * rows3 + rows6a, cols, rows2, drawproc, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 6 * rows2 + 2 * rows3 + rows6a, cols, rows3, drawproc, &fore1color); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 6 * rows2 + 3 * rows3 + rows6a, cols, rows2, drawproc, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 7 * rows2 + 3 * rows3 + rows6a, cols, rows6b, drawproc, &fore2color); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 7 * rows2 + 3 * rows3 + rows6a + rows6b, cols, rows2, drawproc, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 8 * rows2 + 3 * rows3 + rows6a + rows6b, cols, rows2, drawproc, &fore1color); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 9 * rows2 + 3 * rows3 + rows6a + rows6b, cols, rows2, drawproc, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 10 * rows2 + 3 * rows3 + rows6a + rows6b, cols, rows3, drawproc, &fore2color); } static void tartan(pixel ** const pixels, unsigned int const cols, unsigned int const rows, ColorTable const colorTable, struct pm_randSt * const randStP, ppmd_drawproc * const drawproc, pixval const maxval) { bool const colorSpec = (colorTable.count > 0); pixel const backcolor = colorSpec ? colorTable.color[0] : randomDarkColor(randStP, maxval); pixel const fore1color = colorSpec ? colorTable.color[1] : randomBrightColor(randStP, maxval); pixel const fore2color = colorSpec ? colorTable.color[2] : randomBrightColor(randStP, maxval); unsigned int const cols1 = cols / 22; unsigned int const rows1 = rows / 22; unsigned int const cols3 = cols * 3 / 22; unsigned int const rows3 = rows * 3 / 22; unsigned int const cols10 = cols - 3 * cols1 - 3 * cols3; unsigned int const rows10 = rows - 3 * rows1 - 3 * rows3; unsigned int const cols5a = cols10 / 2; unsigned int const rows5a = rows10 / 2; unsigned int const cols5b = cols10 - cols5a; unsigned int const rows5b = rows10 - rows5a; /* Warp. */ ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 0, cols5a, rows, PPMD_NULLDRAWPROC, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, cols5a, 0, cols1, rows, PPMD_NULLDRAWPROC, &fore1color); ppmd_filledrectangle( pixels, cols, rows, maxval, cols5a + cols1, 0, cols5b, rows, PPMD_NULLDRAWPROC, &backcolor ); ppmd_filledrectangle( pixels, cols, rows, maxval, cols10 + cols1, 0, cols3, rows, PPMD_NULLDRAWPROC, &fore2color); ppmd_filledrectangle( pixels, cols, rows, maxval, cols10 + cols1 + cols3, 0, cols1, rows, PPMD_NULLDRAWPROC, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, cols10 + 2 * cols1 + cols3, 0, cols3, rows, PPMD_NULLDRAWPROC, &fore2color); ppmd_filledrectangle( pixels, cols, rows, maxval, cols10 + 2 * cols1 + 2 * cols3, 0, cols1, rows, PPMD_NULLDRAWPROC, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, cols10 + 3 * cols1 + 2 * cols3, 0, cols3, rows, PPMD_NULLDRAWPROC, &fore2color); /* Woof. */ ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 0, cols, rows5a, drawproc, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, rows5a, cols, rows1, drawproc, &fore1color); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, rows5a + rows1, cols, rows5b, drawproc, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, rows10 + rows1, cols, rows3, drawproc, &fore2color); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, rows10 + rows1 + rows3, cols, rows1, drawproc, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, rows10 + 2 * rows1 + rows3, cols, rows3, drawproc, &fore2color); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, rows10 + 2 * rows1 + 2 * rows3, cols, rows1, drawproc, &backcolor); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, rows10 + 3 * rows1 + 2 * rows3, cols, rows3, drawproc, &fore2color); } static void drawAndFillDiamond(pixel ** const pixels, unsigned int const cols, unsigned int const rows, pixval const maxval, pixel const forecolor) { unsigned int const colso2 = cols / 2; unsigned int const rowso2 = rows / 2; ppmd_pathbuilder * const pathBuilderP = ppmd_pathbuilder_create(); ppmd_pathbuilder_setBegPoint(pathBuilderP, ppmd_makePoint (colso2, 0)); ppmd_pathbuilder_addLineLeg(pathBuilderP, ppmd_makeLineLeg(ppmd_makePoint(cols-1, rowso2))); ppmd_pathbuilder_addLineLeg(pathBuilderP, ppmd_makeLineLeg(ppmd_makePoint(colso2, rows-1))); ppmd_pathbuilder_addLineLeg(pathBuilderP, ppmd_makeLineLeg(ppmd_makePoint(0, rowso2))); ppmd_pathbuilder_addLineLeg(pathBuilderP, ppmd_makeLineLeg(ppmd_makePoint(colso2, 0))); ppmd_fill_path(pixels, cols, rows, maxval, ppmd_pathbuilder_pathP(pathBuilderP), forecolor); } static void argyle(pixel ** const pixels, unsigned int const cols, unsigned int const rows, ColorTable const colorTable, struct pm_randSt * const randStP, pixval const maxval, bool const stripes) { bool const colorSpec = (colorTable.count > 0); pixel const backcolor = colorSpec ? colorTable.color[0] : randomDarkColor(randStP, maxval); pixel const forecolor = colorSpec ? colorTable.color[1] : randomBrightColor(randStP, maxval); /* Fill canvas with background to start */ ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 0, cols, rows, PPMD_NULLDRAWPROC, &backcolor); drawAndFillDiamond(pixels, cols, rows, maxval, forecolor); if (stripes) { /* Connect corners with thin stripes */ pixel const stripecolor = colorSpec ? colorTable.color[2] : randomBrightColor(randStP, maxval); ppmd_line(pixels, cols, rows, maxval, 0, 0, cols-1, rows-1, PPMD_NULLDRAWPROC, (char *) &stripecolor); ppmd_line(pixels, cols, rows, maxval, cols-1, 0, 0, rows-1, PPMD_NULLDRAWPROC, (char *) &stripecolor); } } /*---------------------------------------------------------------------------- Poles stuff -----------------------------------------------------------------------------*/ #define MAXPOLES 500 static void placeAndColorPolesRandomly(int * const xs, int * const ys, pixel * const colors, unsigned int const cols, unsigned int const rows, pixval const maxval, ColorTable * const colorTableP, struct pm_randSt * const randStP, unsigned int const poleCt) { unsigned int i; for (i = 0; i < poleCt; ++i) { xs[i] = pm_rand(randStP) % cols; ys[i] = pm_rand(randStP) % rows; if (colorTableP->count > 0) { colors[i] = colorTableP->color[colorTableP->index]; nextColor(colorTableP); } else colors[i] = randomBrightColor(randStP, maxval); } } static void assignInterpolatedColor(pixel * const resultP, pixel const color1, double const dist1, pixel const color2, double const dist2) { if (dist1 == 0) /* pixel is a pole */ *resultP = color1; else { double const sum = dist1 + dist2; pixval const r = (PPM_GETR(color1)*dist2 + PPM_GETR(color2)*dist1)/sum; pixval const g = (PPM_GETG(color1)*dist2 + PPM_GETG(color2)*dist1)/sum; pixval const b = (PPM_GETB(color1)*dist2 + PPM_GETB(color2)*dist1)/sum; PPM_ASSIGN(*resultP, r, g, b); } } static void poles(pixel ** const pixels, unsigned int const cols, unsigned int const rows, ColorTable * const colorTableP, struct pm_randSt * const randStP, pixval const maxval) { unsigned int const poleCt = MAX(2, MIN(MAXPOLES, cols * rows / 30000)); int xs[MAXPOLES], ys[MAXPOLES]; pixel colors[MAXPOLES]; unsigned int row; placeAndColorPolesRandomly(xs, ys, colors, cols, rows, maxval, colorTableP, randStP, poleCt); /* Interpolate points */ for (row = 0; row < rows; ++row) { unsigned int col; for (col = 0; col < cols; ++col) { double dist1, dist2; pixel color1, color2; unsigned int i; /* Find two closest poles. */ dist1 = dist2 = (SQR((double)cols) + SQR((double)rows)); for (i = 0; i < poleCt; ++i) { double const newdist = (double)((int)col - xs[i]) * ((int)col - xs[i]) + (double)((int)row - ys[i]) * ((int)row - ys[i]); if (newdist < dist1) { dist2 = dist1; color2 = color1; dist1 = newdist; color1 = colors[i]; } else if (newdist < dist2) { dist2 = newdist; color2 = colors[i]; } } assignInterpolatedColor(&pixels[row][col], color1, dist1, color2, dist2); } } } /*---------------------------------------------------------------------------- Squig stuff -----------------------------------------------------------------------------*/ #define SQUIGS 5 #define SQ_POINTS 7 #define SQ_MAXCIRCLE_POINTS 5000 struct Squig { unsigned int circleCt; pixel color[SQ_MAXCIRCLE_POINTS]; ppmd_point off[SQ_MAXCIRCLE_POINTS]; }; typedef struct { struct Squig * squigP; } SqClientData; static void validateSquigAspect(unsigned int const cols, unsigned int const rows) { if (cols / rows >= 25 || rows / cols >= 25) pm_error("Image too narrow. Aspect ratio: %u/%u=%f " "is outside accepted range: 0.04 - 25.0", cols, rows, (float)cols/rows ); } static ppmd_point vectorSum(ppmd_point const a, ppmd_point const b) { return ppmd_makePoint(a.x + b.x, a.y + b.y); } static ppmd_drawprocp sqMeasureCircleDrawproc; static void sqMeasureCircleDrawproc(pixel** const pixels, unsigned int const cols, unsigned int const rows, pixval const maxval, ppmd_point const p, const void * const clientdata) { const SqClientData * const sqClientDataP = clientdata; struct Squig * const squigP = sqClientDataP->squigP; squigP->off[squigP->circleCt++] = p; } static ppmd_drawprocp sqRainbowCircleDrawproc; static void sqRainbowCircleDrawproc(pixel ** const pixels, unsigned int const cols, unsigned int const rows, pixval const maxval, ppmd_point const p, const void * const clientdata) { const SqClientData * const sqClientDataP = clientdata; struct Squig * const squigP = sqClientDataP->squigP; unsigned int i; for (i = 0; i < squigP->circleCt; ++i) ppmd_point_drawprocp( pixels, cols, rows, maxval, vectorSum(p, squigP->off[i]), &squigP->color[i]); } static void chooseSqPoleColors(ColorTable * const colorTableP, pixval const maxval, pixel * const color1P, pixel * const color2P, pixel * const color3P, struct pm_randSt * const randStP) { if (colorTableP->count > 0) { *color1P = colorTableP->color[colorTableP->index]; nextColor(colorTableP); *color2P = colorTableP->color[colorTableP->index]; nextColor(colorTableP); *color3P = colorTableP->color[colorTableP->index]; nextColor(colorTableP); } else { *color1P = randomBrightColor(randStP, maxval); *color2P = randomBrightColor(randStP, maxval); *color3P = randomBrightColor(randStP, maxval); } } static void sqAssignColors(unsigned int const circlecount, pixval const maxval, ColorTable * const colorTableP, pixel * const colors, struct pm_randSt * const randStP) { float const cco3 = (circlecount - 1) / 3.0; pixel rc1; pixel rc2; pixel rc3; unsigned int i; chooseSqPoleColors(colorTableP, maxval, &rc1, &rc2, &rc3, randStP); for (i = 0; i < circlecount; ++i) { if (i < cco3) { float const frac = (float)i/cco3; PPM_ASSIGN(colors[i], (float) PPM_GETR(rc1) + ((float) PPM_GETR(rc2) - (float) PPM_GETR(rc1)) * frac, (float) PPM_GETG(rc1) + ((float) PPM_GETG(rc2) - (float) PPM_GETG(rc1)) * frac, (float) PPM_GETB(rc1) + ((float) PPM_GETB(rc2) - (float) PPM_GETB(rc1)) * frac ); } else if (i < 2.0 * cco3) { float const frac = (float)i/cco3 - 1.0; PPM_ASSIGN(colors[i], (float) PPM_GETR(rc2) + ((float) PPM_GETR(rc3) - (float) PPM_GETR(rc2)) * frac, (float) PPM_GETG(rc2) + ((float) PPM_GETG(rc3) - (float) PPM_GETG(rc2)) * frac, (float) PPM_GETB(rc2) + ((float) PPM_GETB(rc3) - (float) PPM_GETB(rc2)) * frac ); } else { float const frac = (float)i/cco3 - 2.0; PPM_ASSIGN(colors[i], (float) PPM_GETR(rc3) + ((float) PPM_GETR(rc1) - (float) PPM_GETR(rc3)) * frac, (float) PPM_GETG(rc3) + ((float) PPM_GETG(rc1) - (float) PPM_GETG(rc3)) * frac, (float) PPM_GETB(rc3) + ((float) PPM_GETB(rc1) - (float) PPM_GETB(rc3)) * frac ); } } } static void clearBackgroundSquig(pixel ** const pixels, unsigned int const cols, unsigned int const rows, ColorTable * const colorTableP, pixval const maxval) { pixel color; if (colorTableP->count > 0) { color = colorTableP->color[0]; colorTableP->index = 1; } else PPM_ASSIGN(color, 0, 0, 0); ppmd_filledrectangle( pixels, cols, rows, maxval, 0, 0, cols, rows, PPMD_NULLDRAWPROC, &color); } static void chooseWrapAroundPoint(unsigned int const cols, unsigned int const rows, ppmd_point * const pFirstP, ppmd_point * const pLastP, ppmd_point * const p0P, ppmd_point * const p1P, ppmd_point * const p2P, ppmd_point * const p3P, struct pm_randSt * const randStP) { switch (pm_rand(randStP) % 4) { case 0: p1P->x = pm_rand(randStP) % cols; p1P->y = 0; if (p1P->x < cols / 2) pFirstP->x = pm_rand(randStP) % (p1P->x * 2 + 1); else pFirstP->x = cols - 1 - pm_rand(randStP) % ((cols - p1P->x) * 2); pFirstP->y = pm_rand(randStP) % rows; p2P->x = p1P->x; p2P->y = rows - 1; pLastP->x = 2 * p2P->x - pFirstP->x; pLastP->y = p2P->y - pFirstP->y; p0P->x = pLastP->x; p0P->y = pLastP->y - rows; p3P->x = pFirstP->x; p3P->y = pFirstP->y + rows; break; case 1: p2P->x = pm_rand(randStP) % cols; p2P->y = 0; if (p2P->x < cols / 2) pLastP->x = pm_rand(randStP) % (p2P->x * 2 + 1); else pLastP->x = cols - 1 - pm_rand(randStP) % ((cols - p2P->x) * 2); pLastP->y = pm_rand(randStP) % rows; p1P->x = p2P->x; p1P->y = rows - 1; pFirstP->x = 2 * p1P->x - pLastP->x; pFirstP->y = p1P->y - pLastP->y; p0P->x = pLastP->x; p0P->y = pLastP->y + rows; p3P->x = pFirstP->x; p3P->y = pFirstP->y - rows; break; case 2: p1P->x = 0; p1P->y = pm_rand(randStP) % rows; pFirstP->x = pm_rand(randStP) % cols; if (p1P->y < rows / 2) pFirstP->y = pm_rand(randStP) % (p1P->y * 2 + 1); else pFirstP->y = rows - 1 - pm_rand(randStP) % ((rows - p1P->y) * 2); p2P->x = cols - 1; p2P->y = p1P->y; pLastP->x = p2P->x - pFirstP->x; pLastP->y = 2 * p2P->y - pFirstP->y; p0P->x = pLastP->x - cols; p0P->y = pLastP->y; p3P->x = pFirstP->x + cols; p3P->y = pFirstP->y; break; case 3: p2P->x = 0; p2P->y = pm_rand(randStP) % rows; pLastP->x = pm_rand(randStP) % cols; if (p2P->y < rows / 2) pLastP->y = pm_rand(randStP) % (p2P->y * 2 + 1); else pLastP->y = rows - 1 - pm_rand(randStP) % ((rows - p2P->y) * 2); p1P->x = cols - 1; p1P->y = p2P->y; pFirstP->x = p1P->x - pLastP->x; pFirstP->y = 2 * p1P->y - pLastP->y; p0P->x = pLastP->x + cols; p0P->y = pLastP->y; p3P->x = pFirstP->x - cols; p3P->y = pFirstP->y; break; } } static void squig(pixel ** const pixels, unsigned int const cols, unsigned int const rows, ColorTable * const colorTableP, struct pm_randSt * const randStP, pixval const maxval) { int i; validateSquigAspect(cols, rows); clearBackgroundSquig(pixels, cols, rows, colorTableP, maxval); /* Draw the squigs. */ ppmd_setlinetype(PPMD_LINETYPE_NODIAGS); ppmd_setlineclip(0); for (i = SQUIGS; i > 0; --i) { unsigned int const radius = (cols + rows) / 2 / (25 + i * 2); struct Squig squig; SqClientData sqClientData; ppmd_point c[SQ_POINTS]; ppmd_point p0, p1, p2, p3; squig.circleCt = 0; sqClientData.squigP = &squig; ppmd_circlep(pixels, cols, rows, maxval, ppmd_makePoint(0, 0), radius, sqMeasureCircleDrawproc, &sqClientData); sqAssignColors(squig.circleCt, maxval, colorTableP, squig.color, randStP); chooseWrapAroundPoint(cols, rows, &c[0], &c[SQ_POINTS-1], &p0, &p1, &p2, &p3, randStP); { /* Do the middle points */ unsigned int j; for (j = 1; j < SQ_POINTS - 1; ++j) { /* validateSquigAspect() assures that cols - 2 * radius, rows -2 * radius are positive */ c[j].x = (pm_rand(randStP) % (cols - 2 * radius)) + radius; c[j].y = (pm_rand(randStP) % (rows - 2 * radius)) + radius; } } ppmd_linep( pixels, cols, rows, maxval, p0, p1, sqRainbowCircleDrawproc, &sqClientData); ppmd_polysplinep( pixels, cols, rows, maxval, p1, SQ_POINTS, c, p2, sqRainbowCircleDrawproc, &sqClientData); ppmd_linep( pixels, cols, rows, maxval, p2, p3, sqRainbowCircleDrawproc, &sqClientData); } } int main(int argc, const char ** argv) { struct CmdlineInfo cmdline; pixel ** pixels; struct pm_randSt randSt; pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); validateComputableDimensions(cmdline.width, cmdline.height); pm_randinit(&randSt); pm_srand2(&randSt, cmdline.randomseedSpec, cmdline.randomseed); pixels = ppm_allocarray(cmdline.width, cmdline.height); switch (cmdline.basePattern) { case PAT_GINGHAM2: gingham2(pixels, cmdline.width, cmdline.height, cmdline.colorTable, &randSt, cmdline.drawProc, PPM_MAXMAXVAL); break; case PAT_GINGHAM3: gingham3(pixels, cmdline.width, cmdline.height, cmdline.colorTable, &randSt, cmdline.drawProc, PPM_MAXMAXVAL); break; case PAT_MADRAS: madras(pixels, cmdline.width, cmdline.height, cmdline.colorTable, &randSt, cmdline.drawProc, PPM_MAXMAXVAL); break; case PAT_TARTAN: tartan(pixels, cmdline.width, cmdline.height, cmdline.colorTable, &randSt, cmdline.drawProc, PPM_MAXMAXVAL); break; case PAT_ARGYLE1: argyle(pixels, cmdline.width, cmdline.height, cmdline.colorTable, &randSt, PPM_MAXMAXVAL, FALSE); break; case PAT_ARGYLE2: argyle(pixels, cmdline.width, cmdline.height, cmdline.colorTable, &randSt, PPM_MAXMAXVAL, TRUE); break; case PAT_POLES: poles(pixels, cmdline.width, cmdline.height, &cmdline.colorTable, &randSt, PPM_MAXMAXVAL); break; case PAT_SQUIG: squig(pixels, cmdline.width, cmdline.height, &cmdline.colorTable, &randSt, PPM_MAXMAXVAL); break; case PAT_CAMO: camo(pixels, cmdline.width, cmdline.height, &cmdline.colorTable, &randSt, PPM_MAXMAXVAL, 0); break; case PAT_ANTICAMO: camo(pixels, cmdline.width, cmdline.height, &cmdline.colorTable, &randSt, PPM_MAXMAXVAL, 1); break; default: pm_error("can't happen!"); } pm_randterm(&randSt); ppm_writeppm(stdout, pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL, 0); ppm_freearray(pixels, cmdline.height); freeCmdline(cmdline); return 0; }