view src/paranormal-ng/general.c @ 2298:cfe45e6861f7

branch merge
author Tomasz Mon <desowin@gmail.com>
date Sat, 12 Jan 2008 12:15:43 +0100
parents 9084e2e05f4a
children f1b6f1b2cdb3
line wrap: on
line source

/*
 * paranormal: iterated pipeline-driven visualization plugin
 * Copyright (c) 2006, 2007 William Pitcock <nenolod@dereferenced.org>
 * Portions copyright (c) 2001 Jamie Gennis <jgennis@mindspring.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; under version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/* FIXME: what to name this file? */

#include <config.h>

#include "paranormal.h"
#include "actuators.h"
#include "pn_utils.h"
#include "libcalc/calc.h"

/* **************** general_fade **************** */
static struct pn_actuator_option_desc general_fade_opts[] =
{
  { "amount", "The amount by which the color index of each "
    "pixel should be decreased by each frame (MAX 255)",
    OPT_TYPE_INT, { ival: 3 } },
  { NULL }
};

static void
general_fade_exec (const struct pn_actuator_option *opts,
	   gpointer data)
{
  int amt = opts[0].val.ival > 255 || opts[0].val.ival < 0 ? 3 : opts[0].val.ival;
  int i, j;

  for (j=0; j<pn_image_data->height; j++)
    for (i=0; i<pn_image_data->width; i++)
      pn_image_data->surface[0][PN_IMG_INDEX (i, j)] =
	CAPLO (pn_image_data->surface[0][PN_IMG_INDEX (i, j)]
	       - amt, 0);
}

struct pn_actuator_desc builtin_general_fade =
{
  "general_fade", "Fade-out", "Decreases the color index of each pixel",
  0, general_fade_opts,
  NULL, NULL, general_fade_exec
};

/* thanks to feelgood_ICBM @ freenode for this */
#define glError() { GLenum err; for(err = glGetError(); err; err = glGetError()) { fprintf(stderr, "glError: %s caught at %s:%u\n", \
	(GLchar*)gluErrorString(err), __FILE__, __LINE__); } }

/* **************** general_blur **************** */
static void
general_blur_exec (const struct pn_actuator_option *opts,
	   gpointer data)
{
  GLuint texture_id;

  glGenTextures(1, &texture_id);
  glBindTexture(GL_TEXTURE_2D, texture_id);

  glError();

  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 128, 128, 0, GL_RGBA, GL_BYTE, NULL);

  glError();

  glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, -1, -1, 1, 1, 0);

  glError();

  glBindTexture(GL_TEXTURE_2D, 0);

  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glBlendFunc(GL_SRC_ALPHA, GL_ONE);
  glBindTexture(GL_TEXTURE_2D, texture_id);

  glError();

  glBegin(GL_QUADS);

  glColor4f(1.0f, 1.0f, 1.0f, 0.5f);
  glTexCoord2f(0, 1);
  glVertex2i(-1, -1);
  glVertex2i(-1, 1);
  glVertex2i(1, 1);
  glVertex2i(1, -1);

  glEnd();
  glBindTexture(GL_TEXTURE_2D, 0);

  glError();

  glDeleteTextures(1, &texture_id);

  glFlush();

  glError();
}

struct pn_actuator_desc builtin_general_blur = 
{
  "general_blur", "Blur", "A simple radial blur",
  0, NULL,
  NULL, NULL, general_blur_exec
};

/* **************** general_mosaic **************** */
/* FIXME: add a variable radius */
/* FIXME: SPEEEED */
static struct pn_actuator_option_desc general_mosaic_opts[] =
{
  { "radius", "The pixel radius that should be used for the effect.",
    OPT_TYPE_INT, { ival: 6 } },
  { NULL }
};

static void
general_mosaic_exec (const struct pn_actuator_option *opts,
	   gpointer data)
{
  int i,j;
  register guchar *srcptr = pn_image_data->surface[0];
  register guchar *destptr = pn_image_data->surface[1];
  register int sum;
  int radius = opts[0].val.ival > 255 || opts[0].val.ival < 0 ? 6 : opts[0].val.ival;

  for (j=0; j<pn_image_data->height; j += radius)
    for (i=0; i<pn_image_data->width; i += radius)
      {
        int ii = 0, jj = 0;
        guchar bval = 0;

        /* find the brightest colour */
        for (jj = 0; jj < radius && (j + jj < pn_image_data->height); jj++)
          for (ii = 0; ii < radius && (i + ii < pn_image_data->width); ii++)
            {
               guchar val = srcptr[PN_IMG_INDEX(i + ii,  j + jj)];

               if (val > bval)
                 bval = val;
            }

        for (jj = 0; jj < radius && (j + jj < pn_image_data->height); jj++)
          for (ii = 0; ii < radius && (i + ii < pn_image_data->width); ii++)
            {
               destptr[PN_IMG_INDEX(i + ii, j + jj)] = bval;
            }
      }

  pn_swap_surfaces ();
}

struct pn_actuator_desc builtin_general_mosaic = 
{
  "general_mosaic", "Mosaic", "A simple mosaic effect.",
  0, general_mosaic_opts,
  NULL, NULL, general_mosaic_exec
};

/* **************** general_clear **************** */
static void
general_clear_exec (const struct pn_actuator_option *opts,
	   gpointer data)
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}

struct pn_actuator_desc builtin_general_clear =
{
  "general_clear", "Clear Surface", "Clears the surface.",
  0, NULL,
  NULL, NULL, general_clear_exec
};

/* **************** general_setalpha **************** */
static struct pn_actuator_option_desc general_setalpha_opts[] =
{
  { "blend_colour", "The colour which should be used for blending.",
    OPT_TYPE_COLOR, { cval: {255, 255, 255} } },
  { "alpha", "The alpha value.",
    OPT_TYPE_COLOR_INDEX, { ival: 128 } },
  { NULL }
};

static void
general_setalpha_exec (const struct pn_actuator_option *opts,
	   gpointer data)
{
  GLubyte colour[] = { opts[0].val.cval.r, opts[0].val.cval.g, opts[0].val.cval.b,
                       opts[1].val.ival };

  glColor4ubv(colour);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE);
}

struct pn_actuator_desc builtin_general_setalpha =
{
  "general_setalpha", "Change Blend Setting", "Changes the blending setting (alpha transparency mask).",
  0, general_setalpha_opts,
  NULL, NULL, general_setalpha_exec
};

/* **************** general_noop **************** */
static void
general_noop_exec (const struct pn_actuator_option *opts,
	   gpointer data)
{
  return;
}

struct pn_actuator_desc builtin_general_noop =
{
  "general_noop", "Do Nothing", "Does absolutely nothing.",
  0, NULL,
  NULL, NULL, general_noop_exec
};

/* **************** general_invert **************** */
static void
general_invert_exec (const struct pn_actuator_option *opts,
	   gpointer data)
{
  int i, j;

  for (j=0; j < pn_image_data->height; j++)
    for (i=0; i < pn_image_data->width; i++)
      pn_image_data->surface[0][PN_IMG_INDEX (i, j)] =
	255 - pn_image_data->surface[0][PN_IMG_INDEX (i, j)];
}

struct pn_actuator_desc builtin_general_invert =
{
  "general_invert", "Value Invert", "Performs a value invert.",
  0, NULL,
  NULL, NULL, general_invert_exec
};

/* **************** general_replace **************** */
static struct pn_actuator_option_desc general_replace_opts[] =
{
  { "start", "The beginning colour value that should be replaced by the value of out.",
    OPT_TYPE_INT, { ival: 250 } },
  { "end", "The ending colour value that should be replaced by the value of out.",
    OPT_TYPE_INT, { ival: 255 } },
  { "out", "The colour value that in is replaced with.",
    OPT_TYPE_INT, { ival: 0 } },
  { NULL }
};

static void
general_replace_exec (const struct pn_actuator_option *opts,
	   gpointer data)
{
  register int i, j;
  register guchar val;
  guchar begin = opts[0].val.ival > 255 || opts[0].val.ival < 0 ? 250 : opts[0].val.ival;
  guchar end = opts[1].val.ival > 255 || opts[1].val.ival < 0 ? 255 : opts[1].val.ival;
  guchar out = opts[2].val.ival > 255 || opts[2].val.ival < 0 ? 0 : opts[2].val.ival;

  for (j=0; j < pn_image_data->height; j++)
    for (i=0; i < pn_image_data->width; i++)
      {
        val = pn_image_data->surface[0][PN_IMG_INDEX (i, j)];
        if (val >= begin && val <= end)
          pn_image_data->surface[0][PN_IMG_INDEX (i, j)] = out;
      }
}

struct pn_actuator_desc builtin_general_replace =
{
  "general_replace", "Value Replace", "Performs a value replace on a range of values.",
  0, general_replace_opts,
  NULL, NULL, general_replace_exec
};

/* **************** general_swap **************** */
static void
general_swap_exec (const struct pn_actuator_option *opts,
	   gpointer data)
{
  pn_swap_surfaces ();
}

struct pn_actuator_desc builtin_general_swap =
{
  "general_swap", "Swap Surface", "Swaps the surface.",
  0, NULL,
  NULL, NULL, general_swap_exec
};

/* **************** general_copy **************** */
static void
general_copy_exec (const struct pn_actuator_option *opts,
	   gpointer data)
{
  memcpy(pn_image_data->surface[1], pn_image_data->surface[0], 
         (pn_image_data->width * pn_image_data->height));
}

struct pn_actuator_desc builtin_general_copy =
{
  "general_copy", "Copy Surface", "Copies the surface to the other surface.",
  0, NULL,
  NULL, NULL, general_copy_exec
};

/* **************** general_flip **************** */
static struct pn_actuator_option_desc general_flip_opts[] =
{
  { "direction", "Negative is horizontal, positive is vertical.",
    OPT_TYPE_INT, { ival: -1 } },
  { NULL }
};

static void
general_flip_exec (const struct pn_actuator_option *opts,
	   gpointer data)
{
  gint x, y;

  if (opts[0].val.ival < 0)
    {
      for (y = 0; y < pn_image_data->height; y++)
        for (x = 0; x < pn_image_data->width; x++)
          {
             pn_image_data->surface[1][PN_IMG_INDEX(pn_image_data->width - x, y)] = 
               pn_image_data->surface[0][PN_IMG_INDEX(x, y)];
          }
    }
  else
    {
      for (y = 0; y < pn_image_data->height; y++)
        for (x = 0; x < pn_image_data->width; x++)
          {
             pn_image_data->surface[1][PN_IMG_INDEX(x, pn_image_data->height - y)] = 
               pn_image_data->surface[0][PN_IMG_INDEX(x, y)];
          }
    }

    pn_swap_surfaces ();
}

struct pn_actuator_desc builtin_general_flip =
{
  "general_flip", "Flip Surface", "Flips the surface.",
  0, general_flip_opts,
  NULL, NULL, general_flip_exec
};

/* ***************** general_evaluate ***************** */

static struct pn_actuator_option_desc general_evaluate_opts[] =
{
  { "init_script", "Script to run on start.", OPT_TYPE_STRING, {sval: "global_reg0 = 27;"} },
  { "frame_script", "Script to run.", OPT_TYPE_STRING, {sval: "global_reg0 = global_reg0 + 1;"} },
  { NULL }
};

struct pn_evaluate_ctx
{
  expression_t *expr_on_init, *expr_on_frame;
  symbol_dict_t *dict;
  gboolean reset;
};

static void
general_evaluate_init(gpointer *data)
{
  *data = g_new0(struct pn_evaluate_ctx, 1);

  ((struct pn_evaluate_ctx *)*data)->reset = TRUE;
}

static void
general_evaluate_cleanup(gpointer op_data)
{
  struct pn_evaluate_ctx *data = (struct pn_evaluate_ctx *) op_data;

  g_return_if_fail(data != NULL);

  if (data->expr_on_init)
    expr_free(data->expr_on_init);

  if (data->expr_on_frame)
    expr_free(data->expr_on_frame);

  if (data->dict)
    dict_free(data->dict);

  if (data)
    g_free(data);
}

static void
general_evaluate_exec(const struct pn_actuator_option *opts,
                      gpointer op_data)
{
  struct pn_evaluate_ctx *data = (struct pn_evaluate_ctx *) op_data;

  if (data->reset)
    {
       if (data->dict)
         dict_free(data->dict);

       data->dict = dict_new();

       if (opts[0].val.sval != NULL);
         data->expr_on_init = expr_compile_string(opts[0].val.sval, data->dict);

       if (opts[1].val.sval != NULL);
         data->expr_on_frame = expr_compile_string(opts[1].val.sval, data->dict);

       if (data->expr_on_init != NULL)
         expr_execute(data->expr_on_init, data->dict);

       data->reset = FALSE;
    }

  if (data->expr_on_frame != NULL)
    expr_execute(data->expr_on_frame, data->dict);
}

struct pn_actuator_desc builtin_general_evaluate =
{
  "general_evaluate", "Evalulate VM Code",
  "Evaluates arbitrary VM code. Does not draw anything.",
  0, general_evaluate_opts,
  general_evaluate_init, general_evaluate_cleanup, general_evaluate_exec
};