Logo Search packages:      
Sourcecode: pandora version File versions  Download package

pandora_match.c

/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* The GIMP -- an image manipulation program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * 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; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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.
 */

/*  This is a plugin intended to help with making panoramas.
 *        Copyright (C) 2003  Akkana Peck.
 *
 *  To build & install: gimptool --install-strip pandora_match.c
 */

#include "pandora.h"

#define FILELIST_WIDTH   400
#define FILELIST_HEIGHT  250
#define SCALE_WIDTH       60
#define SPIN_WIDTH        60

typedef struct
{
  gint       run;
} Pandora_MatchInterface;

typedef struct
{
  gint32 xdist;
  gint32 ydist;
} Pandora_MatchVals;

static Pandora_MatchVals avals =
{
  /* 50 is probably a reasonable default for real images */
  1,   /* xdist */
  1    /* ydist */
};

/* Declare local functions.
 */
static void      query (void);
static void      run   (MAYBE_CONST gchar        *name,
                                    gint          nparams,
                        MAYBE_CONST GimpParam    *param,
                                    gint         *nreturn_vals,
                                    GimpParam   **return_vals);
static gint32    do_pandora_match (gint32 img,
                                   gint32 xdist,
                                   gint32 ydist);

/*  user interface functions  */
static gint      pandora_match_dialog (void);

static Pandora_MatchInterface aint =
{
  FALSE,        /* run */
};

GimpPlugInInfo PLUG_IN_INFO =
{
  NULL,  /* init_proc  */
  NULL,  /* quit_proc  */
  query, /* query_proc */
  run,   /* run_proc   */
};

MAIN ()

static void
query (void)
{
  static GimpParamDef args[] =
  {
    { GIMP_PDB_INT32,    "run_mode", "Interactive, non-interactive" },
    { GIMP_PDB_IMAGE,    "image",    "Input image" },
    { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
    { GIMP_PDB_INT32,    "xdist",    "Maximum horizontal distance to move" },
    { GIMP_PDB_INT32,    "ydist",    "Maximum vertical distance to move" },
  };
  static GimpParamDef return_vals[] =
  {
    { GIMP_PDB_INT32, "Status",   "Success = 0" },
  }; 

#ifdef IN_GIMP_TREE
  INIT_I18N();
#endif

  gimp_install_procedure ("plug_in_pandora_match",
                          "Match panorama layers to each other",
                          "Help not yet written for this plug-in",
                          "Akkana Peck",
                          "Akkana Peck",
                          "2003",
                          N_("<Image>/Pandora/Match Panorama Layers..."),
                          "RGB*, GRAY*, INDEXED*",
                          GIMP_PLUGIN,
                          G_N_ELEMENTS (args), G_N_ELEMENTS (return_vals),
                          args, return_vals);
}

static void
run (MAYBE_CONST gchar      *name,
                 gint        nparams,
     MAYBE_CONST GimpParam  *param,
                 gint       *nreturn_vals,
                 GimpParam **return_vals)
{
  static GimpParam values[2];
  GimpRunMode  run_mode;
  GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  gint32 image_id;

  run_mode = param[0].data.d_int32;

  *nreturn_vals = 2;
  *return_vals  = values;
  values[0].type          = GIMP_PDB_STATUS;
  values[0].data.d_status = status;
  values[1].type = GIMP_PDB_LAYER;
  values[1].data.d_layer = -1;

  switch (run_mode)
  {
    case GIMP_RUN_INTERACTIVE:
      INIT_I18N_UI();
      /*  Possibly retrieve data  */
      gimp_get_data ("plug_in_pandora_match", &avals);

      /*  First acquire information with a dialog  */
      if (! pandora_match_dialog ())
      return;
      break;

    case GIMP_RUN_NONINTERACTIVE:
      INIT_I18N();
      /*  Make sure all the arguments are there!  */
      if (nparams != 15)
        status = GIMP_PDB_CALLING_ERROR;
      if (status == GIMP_PDB_SUCCESS)
      {
        avals.xdist = param[1].data.d_int32;
        avals.ydist = param[2].data.d_int32;
      }
      break;

    case GIMP_RUN_WITH_LAST_VALS:
      /*  Possibly retrieve data  */
      gimp_get_data ("plug_in_pandora_match", &avals);
      break;

    default:
      break;
    }

  /*  Get the active drawable  */
  image_id = param[1].data.d_image;

  /*  Do it!  */
  if (status == GIMP_PDB_SUCCESS)
  {
    /*  run the effect  */
    values[1].data.d_image = do_pandora_match (image_id,
                                               avals.xdist, avals.ydist);

    /*  If the run mode is interactive, flush the displays  */
    if (run_mode != GIMP_RUN_NONINTERACTIVE)
      gimp_displays_flush ();

    /*  Store avals data  */
    if (run_mode == GIMP_RUN_INTERACTIVE)
      gimp_set_data ("plug_in_pandora_match",
                     &avals, sizeof (Pandora_MatchVals));
  }
  else if (status == GIMP_PDB_SUCCESS)
  {
    status = GIMP_PDB_EXECUTION_ERROR;
  }

  values[0].data.d_status = status;

  //gimp_drawable_detach (drawable);
}

static void
pandora_match_ok_callback (GtkWidget *widget,
                           gpointer   data)
{
  aint.run = TRUE;
  gtk_widget_destroy (GTK_WIDGET (data));
}

/* See http://www.home.unix-ag.org/simon/gimp/guadec2002/gimp-plugin/html/efficientaccess.html
   for some hints.  But we're not using that currently.
*/

static gdouble
CompareRegions (guchar* left, guchar* right,
                gint w, gint h,
                gint bpp)
{
  gdouble fval = 0.0;
  int x, y;
  int rowstride = w * bpp;

  guchar* l = left;
  guchar* r = right;

  for (y = 0; y < h; y++)
  {
    for (x = 0; x < w; x++)
      printf("%x%x%x ", l[x*bpp+0]&0xf, l[x*bpp+1]&0xf, l[x*bpp+2]&0xf);
    printf("\t");
    for (x = 0; x < w; x++)
      printf("%x%x%x ", r[x*bpp+0]&0xf, r[x*bpp+1]&0xf, r[x*bpp+2]&0xf);
    printf("\n");
    for (x = 0; x < w; x++)
    {
      /* Compare l[n] to r[n] */
      fval += fabs (r[0] - l[0]) + fabs (r[1] - l[1]) + fabs (r[2] - l[2]);

      l += bpp;
      r += bpp;
    }

    left  += rowstride;
    right += rowstride;
  }

  printf("Difference: %f\n\n", fval);
  return fval;
}               

#define MAXWIDTH 50
#define MAXHEIGHT 500

static int min(int a, int b)
{
  if (a > b)
    return b;
  return a;
}

static int max(int a, int b)
{
  if (a > b)
    return a;
  return b;
}

static gint32
do_pandora_match (gint32 img, gint32 xdist, gint32 ydist)
{
  int thislayer;
  gint32 *layers;
  gint32 numlayers;
  gint32 ww, hh;
  guchar *buf0;
  guchar *buf1;

  printf("Pandora_match: max distance (%d, %d)\n", xdist, ydist);

  /* Loop over layers */
  layers = gimp_image_get_layers(img, &numlayers);
  if (numlayers < 2)
      return 1;

  /* The layers returned are backwards, so if we want to go left to right
   * (bottom to top) we have to loop backwards through the list:
   */
  for (thislayer = numlayers-2; thislayer >= 0; --thislayer)
  //for (thislayer = 1; thislayer < numlayers; ++thislayer)
  {
    GimpDrawable* draw0 = gimp_drawable_get (layers[thislayer+1]);
    GimpDrawable* draw1 = gimp_drawable_get (layers[thislayer]);
    GimpPixelRgn rgn0, rgn1;

    /* The starting offsets of the input layers */
    gint x0, y0, x1, y1;

    /* The size of the input layers */
    gint w0 = gimp_drawable_width  (layers[thislayer+1]);
    gint h0 = gimp_drawable_height (layers[thislayer+1]);
    gint w1 = gimp_drawable_width  (layers[thislayer]);
    gint h1 = gimp_drawable_height (layers[thislayer]);

    gint overlapw;
    int xoff, yoff;
    int xsav = 0, ysav = 0;
    gdouble minval = G_MAXFLOAT;

    gimp_drawable_offsets (layers[thislayer+1], &x0, &y0);
    gimp_drawable_offsets (layers[thislayer],   &x1, &y1);
    printf("left: (%d, %d) %d x %d\n", x0, y0, w0, h0);
    printf("right: (%d, %d) %d x %d\n", x1, y1, w1, h1);
    printf("Current X overlap: %d\n", x0 + w0 - x1);

    /* The size of the block we'll be comparing each time.
     * In X, that means the size of the overlap, minus a border of 
     * x/ydist on all four sides.
     * In Y, the maximum amount we can overlap is the minimum of
     * the
     Overlap in Y with right shifted uppest:
       top is max(y0, y1-ydist)
       bottom is min(y0+h0, y1-ydist+h1)
     With right shiftest downest:
       top is max(y0, y1+ydist)
       bottom is min(y0+h0, y1+ydist+h1)
     So the safe height is the minimum of these two.
     */
    hh = min( min(y0+h0, y1-ydist+h1) - max(y0, y1-ydist),
              min(y0+h0, y1+ydist+h1) - max(y0, y1+ydist) );

    overlapw = w0 - x1 + x0;

    /* X size is a bit simpler: the size of the overlap minus 2*xdist. */
    ww = overlapw - 2*xdist;
    printf("ww = %d = %d + %d - %d - 2*%d\n", ww, x0, w0, x1, xdist);
    printf("hh = %d\n", hh);

    /* Now, we loop comparing the leftmost column of draw1
     * (minus a bit at the top and bottom)
     * to columns in the overlapping area of draw0.
     * XXX Leftmost column isn't best; eventually we should compare
     * a region, or at least a column in the middle of the overlap.
     */

    /* Initialize the pixel regions we'll use */
    /* Region 0 is the whole overlap region of the left layer (from y=0) */
    gimp_pixel_rgn_init (&rgn0, draw0, x1 - x0, 0, overlapw, h0,
                         FALSE, FALSE);
    /* Region 1 is the overlap region of the right layer (from x=0, y=0) */
    gimp_pixel_rgn_init (&rgn1, draw1, 0, 0, overlapw, hh, FALSE, FALSE);

    /* Make sure they're the same color depth */
    if (rgn0.bpp != rgn1.bpp) {
      printf("Confused: mismatched bpp, %d vs %d!\n", rgn0.bpp, rgn1.bpp);
      return 1;
    }

    /* Since we have to store these regions in memory,
     * put a limit on how big they can get:
     */
    if (hh > MAXHEIGHT) hh = MAXHEIGHT;
    if (ww > MAXWIDTH) ww = MAXWIDTH;
    buf0 = g_malloc(ww * hh * rgn0.bpp);
    buf1 = g_malloc(ww * hh * rgn0.bpp);
    if (!buf0 || !buf1) {
      if (buf0) g_free(buf0);
      if (buf1) g_free(buf1);
      printf("Out of memory!\n");
      return 1;
    }
    printf("Malloced %p and %p at %d\n",
           buf0, buf1, ww * hh * rgn0.bpp);

    /* Virtually move thislayer left and right by xdist */
    for (xoff = -xdist; xoff <= xdist; ++xoff)
    {
      printf("Trying to get rect: (%d, %d) %d x %d\n",
             x1 - x0 + xdist + xoff, 0, ww, hh);
      gimp_pixel_rgn_get_rect (&rgn0, buf0,
                               x1 - x0 + xdist + xoff, 0,
                               ww, hh + 2*ydist);
      printf("Got one rect\n");
      /* Virtually move thislayer up and down by ydist */
      for (yoff = -ydist; yoff <= ydist; ++yoff)
      {
        gdouble cmp;

        printf("xoff = %d, yoff = %d\n", xoff, yoff);

        /* Get the two rects we're going to be comparing. */
#if 0
        printf("Trying to get rect: (%d, %d) %d x %d\n",
               x1 - x0 + xdist + xoff, ydist+yoff,
               ww, hh);
        gimp_pixel_rgn_get_rect (&rgn0, buf0,
                                 x1 - x0 + xdist + xoff, ydist+yoff,
                                 ww, hh);
        printf("Got one rect\n");
#endif
        gimp_pixel_rgn_get_rect (&rgn1, buf1, xdist, ydist, ww, hh);
        printf("Got the regions\n");

        cmp = CompareRegions(buf0, buf1, ww, hh, rgn0.bpp);
        printf ("total for row: %f\n", cmp);
        if (cmp < minval)
        {
          minval = cmp;
          xsav = xoff;
          ysav = yoff;
        }
      }
      printf("Done with y loop\n");
    }
    g_free(buf0);
    g_free(buf1);
    printf("Recommended offset is (%d, %d) at val %f\n", xsav, ysav, minval);
  }

  gimp_displays_flush ();
  aint.run = TRUE;
  return 0;
}

static gint
pandora_match_dialog (void)
{
  GtkWidget *dlg;
  GtkWidget *hbox;
  GtkWidget *logo_box;
  GtkWidget *frame;
  GtkWidget *table;

  GtkObject *scale;

  gimp_ui_init ("pandora_match", TRUE);

#ifdef GIMP_1_2
  dlg = gimp_dialog_new (_("Pandora_Match"), "pandora_match",
                         gimp_standard_help_func, "filters/pandora_match.html",
                         GTK_WIN_POS_MOUSE,
                         FALSE, TRUE, FALSE,

                         GTK_STOCK_CANCEL, gtk_widget_destroy,
                         NULL, 1, NULL, FALSE, TRUE,

                         GTK_STOCK_OK, pandora_match_ok_callback,
                         NULL, NULL, NULL, TRUE, FALSE,

                         NULL);
#else  /* Gimp 1.3 */
  dlg = gimp_dialog_new (_("Pandora_Match"), "pandora_match",
                         NULL, 0,
                         gimp_standard_help_func, "filters/pandora_match.html",

                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,

                         GTK_STOCK_OK, pandora_match_ok_callback,

                         NULL);
#endif

  g_signal_connect (G_OBJECT (dlg), "destroy",
                    G_CALLBACK (gtk_main_quit),
                    NULL);

  hbox = gtk_hbox_new (FALSE, 6);
  gtk_container_set_border_width (GTK_CONTAINER (hbox), 6);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), hbox, TRUE, TRUE, 0);

  logo_box = gtk_vbox_new (FALSE, 0);
  gtk_box_pack_start (GTK_BOX (hbox), logo_box, FALSE, FALSE, 0);

  frame = gtk_frame_new (NULL);
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
  gtk_box_pack_start (GTK_BOX (logo_box), frame, FALSE, FALSE, 0);

#ifdef DO_ICON_HERE_IF_THERE_IS_ONE
  icon_pixbuf = gdk_pixbuf_new_from_inline(-1, pandora_match_icon, FALSE, NULL);

  preview = gtk_image_new_from_pixbuf (icon_pixbuf);

  gtk_container_add (GTK_CONTAINER (frame), preview);
  gtk_widget_show (preview);
#endif /* ! GIMP_1_2 */
  gtk_widget_show (frame);
  gtk_widget_show (logo_box);

  table = gtk_table_new (2, 1, FALSE);
  //gtk_table_set_col_spacings (GTK_TABLE (table2), 4);
  //gtk_table_set_row_spacings (GTK_TABLE (table2), 2);
  gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  gtk_box_pack_start (GTK_BOX (hbox), table, TRUE, TRUE, 0);

  scale = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
                                _("Max X distance:"),
                                SCALE_WIDTH, SPIN_WIDTH,
                                avals.xdist, 1.0, 200.0, 1.0, 5.0, 1,
                                TRUE, 0, 0,
                                NULL, NULL);
  g_signal_connect (G_OBJECT (scale), "value_changed",
                    G_CALLBACK (gimp_int_adjustment_update),
                    &avals.xdist);

  scale = gimp_scale_entry_new (GTK_TABLE (table), 0, 1,
                                _("Max Y distance:"),
                                SCALE_WIDTH, SPIN_WIDTH,
                                avals.ydist, 1.0, 100.0, 1.0, 5.0, 1,
                                TRUE, 0, 0,
                                NULL, NULL);
  g_signal_connect (G_OBJECT (scale), "value_changed",
                    G_CALLBACK (gimp_int_adjustment_update),
                    &avals.ydist);

  gtk_widget_show (table);
  gtk_widget_show (frame);
  gtk_widget_show (hbox);
  gtk_widget_show (dlg);

  gtk_main ();
  gdk_flush ();

  return aint.run;
}


Generated by  Doxygen 1.6.0   Back to index