home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / plug-ins / common / pixelize.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-08-28  |  15.3 KB  |  601 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  *
  4.  * Pixelize plug-in (ported to GIMP v1.0)
  5.  * Copyright (C) 1997 Eiichi Takamori <taka@ma1.seikyou.ne.jp>
  6.  * original pixelize.c for GIMP 0.54 by Tracy Scott
  7.  *
  8.  * This program is free software; you can redistribute it and/or modify
  9.  * it under the terms of the GNU General Public License as published by
  10.  * the Free Software Foundation; either version 2 of the License, or
  11.  * (at your option) any later version.
  12.  *
  13.  * This program is distributed in the hope that it will be useful,
  14.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
  16.  * GNU General Public License for more details.
  17.  *
  18.  * You should have received a copy of the GNU General Public License
  19.  * along with this program; if not, write to the Free Software
  20.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  21.  */
  22.  
  23. /*
  24.  * version 1.04
  25.  * This version requires GIMP v0.99.10 or above.
  26.  *
  27.  * This plug-in "pixelizes" the image.
  28.  *
  29.  *    Eiichi Takamori <taka@ma1.seikyou.ne.jp>
  30.  *    http://ha1.seikyou.ne.jp/home/taka/gimp/
  31.  *
  32.  * Changes from version 1.03 to version 1.04:
  33.  * - Added gtk_rc_parse
  34.  * - Added entry with scale
  35.  * - Fixed bug that large pixelwidth >=64 sometimes caused core dump
  36.  *
  37.  * Changes from gimp-0.99.9/plug-ins/pixelize.c to version 1.03:
  38.  * - Fixed comments and help strings
  39.  * - Fixed `RGB, GRAY' to `RGB*, GRAY*'
  40.  * - Fixed procedure db name `pixelize' to `plug_in_pixelize'
  41.  *
  42.  * Differences from Tracy Scott's original `pixelize' plug-in:
  43.  *
  44.  * - Algorithm is modified to work around with the tile management.
  45.  *   The way of pixelizing is switched by the value of pixelwidth.  If
  46.  *   pixelwidth is greater than (or equal to) tile width, then this
  47.  *   plug-in makes GimpPixelRgn with that width and proceeds. Otherwise,
  48.  *   it makes the region named `PixelArea', whose size is smaller than
  49.  *   tile width and is multiply of pixel width, and acts onto it.
  50.  */
  51.  
  52. /* pixelize filter written for the GIMP
  53.  *  -Tracy Scott
  54.  *
  55.  * This filter acts as a low pass filter on the color components of
  56.  * the provided region
  57.  */
  58.  
  59. #include "config.h"
  60.  
  61. #include <stdio.h>
  62. #include <stdlib.h>
  63.  
  64. #include <gtk/gtk.h>
  65.  
  66. #include <libgimp/gimp.h>
  67. #include <libgimp/gimpui.h>
  68.  
  69. #include "libgimp/stdplugins-intl.h"
  70.  
  71.  
  72. #ifdef RCSID
  73. static char rcsid[] = "$Id: pixelize.c,v 1.16 2000/08/28 00:42:19 mitch Exp $";
  74. #endif
  75.  
  76.  
  77. /* Some useful macros */
  78.  
  79. #define TILE_CACHE_SIZE  16
  80. #define SCALE_WIDTH     125
  81. #define ENTRY_WIDTH      50
  82.  
  83. typedef struct
  84. {
  85.   gint pixelwidth;
  86. } PixelizeValues;
  87.  
  88. typedef struct
  89. {
  90.   gint run;
  91. } PixelizeInterface;
  92.  
  93. typedef struct
  94. {
  95.   gint x, y, w, h;
  96.   gint width;
  97.   guchar *data;
  98. } PixelArea;
  99.  
  100. /* Declare local functions.
  101.  */
  102. static void    query    (void);
  103. static void    run    (gchar     *name,
  104.              gint     nparams,
  105.              GimpParam     *param,
  106.              gint     *nreturn_vals,
  107.              GimpParam     **return_vals);
  108.  
  109. static gint    pixelize_dialog      (void);
  110. static void    pixelize_ok_callback (GtkWidget *widget,
  111.                       gpointer   data);
  112.  
  113. static void    pixelize    (GimpDrawable *drawable);
  114. static void    pixelize_large    (GimpDrawable *drawable, gint pixelwidth);
  115. static void    pixelize_small    (GimpDrawable *drawable, gint pixelwidth,
  116.                  gint tile_width);
  117. static void    pixelize_sub    (gint pixelwidth, gint bpp);
  118.  
  119. /***** Local vars *****/
  120.  
  121. GimpPlugInInfo PLUG_IN_INFO =
  122. {
  123.   NULL,     /* init_proc  */
  124.   NULL,     /* quit_proc  */
  125.   query, /* query_proc */
  126.   run     /* run_proc   */
  127. };
  128.  
  129. static PixelizeValues pvals =
  130. {
  131.   10
  132. };
  133.  
  134. static PixelizeInterface pint =
  135. {
  136.   FALSE        /* run */
  137. };
  138.  
  139. static PixelArea area;
  140.  
  141. /***** Functions *****/
  142.  
  143. MAIN ()
  144.  
  145. static void
  146. query (void)
  147. {
  148.   static GimpParamDef args[]=
  149.   {
  150.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  151.     { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
  152.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
  153.     { GIMP_PDB_INT32, "pixelwidth", "Pixel width     (the decrease in resolution)" }
  154.   };
  155.   static gint nargs = sizeof (args) / sizeof (args[0]);
  156.  
  157.   gimp_install_procedure ("plug_in_pixelize",
  158.               "Pixelize the contents of the specified drawable",
  159.               "Pixelize the contents of the specified drawable with speficied pixelizing width.",
  160.               "Spencer Kimball & Peter Mattis, Tracy Scott, (ported to 1.0 by) Eiichi Takamori",
  161.               "Spencer Kimball & Peter Mattis, Tracy Scott",
  162.               "1995",
  163.               N_("<Image>/Filters/Blur/Pixelize..."),
  164.               "RGB*, GRAY*",
  165.               GIMP_PLUGIN,
  166.               nargs, 0,
  167.               args, NULL);
  168. }
  169.  
  170. static void
  171. run (gchar   *name,
  172.      gint    nparams,
  173.      GimpParam  *param,
  174.      gint    *nreturn_vals,
  175.      GimpParam  **return_vals)
  176. {
  177.   static GimpParam values[1];
  178.   GimpDrawable *drawable;
  179.   GimpRunModeType run_mode;
  180.   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  181.  
  182.   run_mode = param[0].data.d_int32;
  183.  
  184.   *nreturn_vals = 1;
  185.   *return_vals = values;
  186.  
  187.   values[0].type = GIMP_PDB_STATUS;
  188.   values[0].data.d_status = status;
  189.  
  190.   /*  Get the specified drawable  */
  191.   drawable = gimp_drawable_get (param[2].data.d_drawable);
  192.  
  193.   switch (run_mode)
  194.     {
  195.     case GIMP_RUN_INTERACTIVE:
  196.       INIT_I18N_UI();
  197.       /*  Possibly retrieve data  */
  198.       gimp_get_data ("plug_in_pixelize", &pvals);
  199.  
  200.       /*  First acquire information with a dialog  */
  201.       if (! pixelize_dialog ())
  202.     {
  203.       gimp_drawable_detach (drawable);
  204.       return;
  205.     }
  206.       break;
  207.  
  208.     case GIMP_RUN_NONINTERACTIVE:
  209.       INIT_I18N();
  210.       /*  Make sure all the arguments are there!  */
  211.       if (nparams != 4)
  212.     status = GIMP_PDB_CALLING_ERROR;
  213.       if (status == GIMP_PDB_SUCCESS)
  214.     {
  215.       pvals.pixelwidth = (gdouble) param[3].data.d_int32;
  216.     }
  217.       if ((status == GIMP_PDB_SUCCESS) &&
  218.       pvals.pixelwidth <= 0)
  219.     status = GIMP_PDB_CALLING_ERROR;
  220.       break;
  221.  
  222.     case GIMP_RUN_WITH_LAST_VALS:
  223.       INIT_I18N();
  224.       /*  Possibly retrieve data  */
  225.       gimp_get_data ("plug_in_pixelize", &pvals);
  226.       break;
  227.  
  228.     default:
  229.       break;
  230.     }
  231.  
  232.   if (status == GIMP_PDB_SUCCESS)
  233.     {
  234.       /*  Make sure that the drawable is gray or RGB color  */
  235.       if (gimp_drawable_is_rgb (drawable->id) ||
  236.       gimp_drawable_is_gray (drawable->id))
  237.     {
  238.       gimp_progress_init (_("Pixelizing..."));
  239.  
  240.       /*  set the tile cache size  */
  241.       gimp_tile_cache_ntiles (TILE_CACHE_SIZE);
  242.  
  243.       /*  run the pixelize effect  */
  244.       pixelize (drawable);
  245.  
  246.       if (run_mode != GIMP_RUN_NONINTERACTIVE)
  247.         gimp_displays_flush ();
  248.  
  249.       /*  Store data  */
  250.       if (run_mode == GIMP_RUN_INTERACTIVE)
  251.         gimp_set_data ("plug_in_pixelize", &pvals, sizeof (PixelizeValues));
  252.     }
  253.       else
  254.     {
  255.       /* g_message ("pixelize: cannot operate on indexed color images"); */
  256.       status = GIMP_PDB_EXECUTION_ERROR;
  257.     }
  258.     }
  259.  
  260.   values[0].data.d_status = status;
  261.  
  262.   gimp_drawable_detach (drawable);
  263. }
  264.  
  265. static gint
  266. pixelize_dialog (void)
  267. {
  268.   GtkWidget *dlg;
  269.   GtkWidget *frame;
  270.   GtkWidget *table;
  271.   GtkObject *adjustment;
  272.  
  273.   gimp_ui_init ("pixelize", FALSE);
  274.  
  275.   dlg = gimp_dialog_new (_("Pixelize"), "pixelize",
  276.              gimp_standard_help_func, "filters/pixelize.html",
  277.              GTK_WIN_POS_MOUSE,
  278.              FALSE, TRUE, FALSE,
  279.  
  280.              _("OK"), pixelize_ok_callback,
  281.              NULL, NULL, NULL, TRUE, FALSE,
  282.              _("Cancel"), gtk_widget_destroy,
  283.              NULL, 1, NULL, FALSE, TRUE,
  284.  
  285.              NULL);
  286.  
  287.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  288.               GTK_SIGNAL_FUNC (gtk_main_quit),
  289.               NULL);
  290.  
  291.   /*  parameter settings  */
  292.   frame = gtk_frame_new (_("Parameter Settings"));
  293.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  294.   gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
  295.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
  296.  
  297.   table = gtk_table_new (1, 3, FALSE);
  298.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  299.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  300.   gtk_container_add (GTK_CONTAINER (frame), table);
  301.  
  302.   adjustment =
  303.     gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
  304.               _("Pixel Width:"), SCALE_WIDTH, ENTRY_WIDTH,
  305.               pvals.pixelwidth, 1, 64, 1, 8, 0,
  306.               FALSE, 0, GIMP_MAX_IMAGE_SIZE,
  307.               NULL, NULL);
  308.   gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
  309.               GTK_SIGNAL_FUNC (gimp_int_adjustment_update),
  310.               &pvals.pixelwidth);
  311.  
  312.   gtk_widget_show (frame);
  313.   gtk_widget_show (table);
  314.   gtk_widget_show (dlg);
  315.  
  316.   gtk_main ();
  317.   gdk_flush ();
  318.  
  319.   return pint.run;
  320. }
  321.  
  322. /*  Pixelize interface functions  */
  323.  
  324. static void
  325. pixelize_ok_callback (GtkWidget *widget,
  326.               gpointer     data)
  327. {
  328.   pint.run = TRUE;
  329.  
  330.   gtk_widget_destroy (GTK_WIDGET (data));
  331. }
  332.  
  333. /*
  334.   Pixelize Effect
  335.  */
  336.  
  337. static void
  338. pixelize (GimpDrawable *drawable)
  339. {
  340.   gint tile_width;
  341.   gint pixelwidth;
  342.  
  343.   tile_width = gimp_tile_width();
  344.   pixelwidth = pvals.pixelwidth;
  345.  
  346.   if (pixelwidth < 0)
  347.     pixelwidth = - pixelwidth;
  348.   if (pixelwidth < 1)
  349.     pixelwidth = 1;
  350.  
  351.   if (pixelwidth >= tile_width)
  352.     pixelize_large (drawable, pixelwidth);
  353.   else
  354.     pixelize_small (drawable, pixelwidth, tile_width);
  355. }
  356.  
  357. /*
  358.   This function operates on the image when pixelwidth >= tile_width.
  359.   It simply sets the size of GimpPixelRgn as pixelwidth and proceeds.
  360.  */
  361. static void
  362. pixelize_large (GimpDrawable *drawable,
  363.         gint       pixelwidth)
  364. {
  365.   GimpPixelRgn src_rgn, dest_rgn;
  366.   guchar *src_row, *dest_row;
  367.   guchar *src, *dest;
  368.   gulong *average;
  369.   gint row, col, b, bpp;
  370.   gint x, y, x_step, y_step;
  371.   gulong count;
  372.   gint x1, y1, x2, y2;
  373.   gint progress, max_progress;
  374.   gpointer pr;
  375.  
  376.   gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2);
  377.  
  378.   bpp = gimp_drawable_bpp(drawable->id);
  379.   average = g_new(gulong, bpp);
  380.  
  381.   /* Initialize progress */
  382.   progress = 0;
  383.   max_progress = 2 * (x2 - x1) * (y2 - y1);
  384.  
  385.   for (y = y1; y < y2; y += pixelwidth - (y % pixelwidth))
  386.     {
  387.       for (x = x1; x < x2; x += pixelwidth - (x % pixelwidth))
  388.     {
  389.       x_step = pixelwidth - (x % pixelwidth);
  390.       y_step = pixelwidth - (y % pixelwidth);
  391.       x_step = MIN(x_step, x2-x);
  392.       y_step = MIN(y_step, y2-y);
  393.  
  394.       gimp_pixel_rgn_init (&src_rgn, drawable,
  395.                    x, y, x_step, y_step, FALSE, FALSE);
  396.       for (b = 0; b < bpp;    b++)
  397.         average[b] = 0;
  398.       count = 0;
  399.  
  400.       for (pr = gimp_pixel_rgns_register (1, &src_rgn);
  401.            pr != NULL;
  402.            pr = gimp_pixel_rgns_process (pr))
  403.         {
  404.           src_row = src_rgn.data;
  405.           for (row = 0; row < src_rgn.h; row++)
  406.         {
  407.           src = src_row;
  408.           for (col = 0; col < src_rgn.w; col++)
  409.             {
  410.               for(b = 0; b < bpp; b++)
  411.             average[b] += src[b];
  412.               src += src_rgn.bpp;
  413.               count += 1;
  414.             }
  415.           src_row += src_rgn.rowstride;
  416.         }
  417.           /* Update progress */
  418.           progress += src_rgn.w * src_rgn.h;
  419.           gimp_progress_update ((double) progress / (double) max_progress);
  420.         }
  421.  
  422.       if (count > 0)
  423.         {
  424.           for (b = 0; b < bpp; b++)
  425.         average[b] = (guchar) (average[b] / count);
  426.         }
  427.  
  428.       gimp_pixel_rgn_init (&dest_rgn, drawable,
  429.                    x, y, x_step, y_step, TRUE, TRUE);
  430.       for (pr = gimp_pixel_rgns_register (1, &dest_rgn);
  431.            pr != NULL;
  432.            pr = gimp_pixel_rgns_process (pr))
  433.         {
  434.           dest_row = dest_rgn.data;
  435.           for (row = 0; row < dest_rgn.h; row++)
  436.         {
  437.           dest = dest_row;
  438.           for (col = 0; col < dest_rgn.w; col++)
  439.             {
  440.               for (b = 0; b < bpp; b++)
  441.             dest[b] = average[b];
  442.               dest += dest_rgn.bpp;
  443.               count += 1;
  444.             }
  445.           dest_row += dest_rgn.rowstride;
  446.         }
  447.           /* Update progress */
  448.           progress += dest_rgn.w * dest_rgn.h;
  449.           gimp_progress_update ((double) progress / (double) max_progress);
  450.         }
  451.     }
  452.     }
  453.  
  454.   g_free (average);
  455.  
  456.   /*  update the blurred region     */
  457.   gimp_drawable_flush (drawable);
  458.   gimp_drawable_merge_shadow (drawable->id, TRUE);
  459.   gimp_drawable_update (drawable->id, x1, y1, (x2 - x1), (y2 - y1));
  460. }
  461.  
  462.  
  463. /*
  464.    This function operates on PixelArea, whose width and height are
  465.    multiply of pixel width, and less than the tile size (to enhance
  466.    its speed).
  467.  
  468.    If any coordinates of mask boundary is not multiply of pixel width
  469.    (e.g.  x1 % pixelwidth != 0), operates on the region whose width
  470.    or height is the remainder.
  471.  */
  472. static void
  473. pixelize_small (GimpDrawable *drawable,
  474.         gint       pixelwidth,
  475.         gint       tile_width)
  476. {
  477.   GimpPixelRgn src_rgn, dest_rgn;
  478.   gint bpp;
  479.   gint x1, y1, x2, y2;
  480.   gint progress, max_progress;
  481.  
  482.   gimp_drawable_mask_bounds (drawable->id, &x1, &y1, &x2, &y2);
  483.   gimp_pixel_rgn_init (&src_rgn, drawable, x1, y1, x2-x1, y2-y1, FALSE, FALSE);
  484.   gimp_pixel_rgn_init (&dest_rgn, drawable, x1, y1, x2-x1, y2-y1, TRUE, TRUE);
  485.  
  486.   /* Initialize progress */
  487.   progress = 0;
  488.   max_progress = (x2 - x1) * (y2 - y1);
  489.  
  490.   bpp = drawable->bpp;
  491.  
  492.   area.width = (tile_width / pixelwidth) * pixelwidth;
  493.   area.data= g_new(guchar, (glong) bpp * area.width * area.width );
  494.  
  495.   for(area.y = y1; area.y < y2;
  496.        area.y += area.width - (area.y % area.width))
  497.     {
  498.       area.h = area.width - (area.y % area.width);
  499.       area.h = MIN(area.h, y2 - area.y);
  500.       for(area.x = x1; area.x < x2;
  501.        area.x += area.width - (area.x % area.width))
  502.     {
  503.       area.w = area.width - (area.x % area.width);
  504.       area.w = MIN(area.w, x2 - area.x);
  505.  
  506.       gimp_pixel_rgn_get_rect (&src_rgn, area.data,
  507.                    area.x, area.y, area.w, area.h);
  508.  
  509.       pixelize_sub(pixelwidth, bpp);
  510.  
  511.       gimp_pixel_rgn_set_rect (&dest_rgn, area.data,
  512.                    area.x, area.y, area.w, area.h);
  513.  
  514.       /* Update progress */
  515.       progress += area.w * area.h;
  516.       gimp_progress_update ((double) progress / (double) max_progress);
  517.     }
  518.     }
  519.  
  520.   g_free(area.data);
  521.  
  522.   /*  update the pixelized region  */
  523.   gimp_drawable_flush (drawable);
  524.   gimp_drawable_merge_shadow (drawable->id, TRUE);
  525.   gimp_drawable_update (drawable->id, x1, y1, (x2 - x1), (y2 - y1));
  526. }
  527.  
  528. /*
  529.   This function acts on one PixelArea.    Since there were so many
  530.   nested FORs in pixelize_small(), I put a few of them here...
  531.   */
  532.  
  533. static void
  534. pixelize_sub (gint pixelwidth,
  535.           gint bpp)
  536. {
  537.   glong average[4];        /* bpp <= 4 */
  538.   gint    x, y, w, h;
  539.   guchar *buf_row, *buf;
  540.   gint    row, col;
  541.   gint    rowstride;
  542.   gint    count;
  543.   gint    i;
  544.  
  545.   rowstride = area.w * bpp;
  546.  
  547.   for (y = area.y; y < area.y + area.h; y += pixelwidth - (y % pixelwidth))
  548.     {
  549.       h = pixelwidth - (y % pixelwidth);
  550.       h = MIN(h, area.y + area.h - y);
  551.       for (x = area.x; x < area.x + area.w; x += pixelwidth - (x % pixelwidth))
  552.     {
  553.       w = pixelwidth - (x % pixelwidth);
  554.       w = MIN (w, area.x + area.w - x);
  555.  
  556.       for (i = 0; i < bpp; i++)
  557.         average[i] = 0;
  558.       count = 0;
  559.  
  560.       /* Read */
  561.       buf_row = area.data + (y-area.y)*rowstride + (x-area.x)*bpp;
  562.  
  563.       for (row = 0; row < h; row++)
  564.         {
  565.           buf = buf_row;
  566.           for (col = 0; col < w; col++)
  567.         {
  568.           for (i = 0; i < bpp; i++)
  569.             average[i] += buf[i];
  570.           count++;
  571.           buf += bpp;
  572.         }
  573.           buf_row += rowstride;
  574.         }
  575.  
  576.       /* Average */
  577.       if (count > 0)
  578.         {
  579.           for (i = 0; i < bpp; i++)
  580.         average[i] /= count;
  581.         }
  582.  
  583.       /* Write */
  584.       buf_row = area.data + (y-area.y)*rowstride + (x-area.x)*bpp;
  585.  
  586.       for (row = 0; row < h; row++)
  587.         {
  588.           buf = buf_row;
  589.           for (col = 0; col < w; col++)
  590.         {
  591.           for (i = 0; i < bpp; i++)
  592.             buf[i] = average[i];
  593.           count++;
  594.           buf += bpp;
  595.         }
  596.           buf_row += rowstride;
  597.         }
  598.     }
  599.     }
  600. }
  601.