home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / app / scan_convert.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-12-17  |  8.0 KB  |  352 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995-1999 Spencer Kimball and Peter Mattis
  3.  *
  4.  * This program is free software; you can redistribute it and/or modify
  5.  * it under the terms of the GNU General Public License as published by
  6.  * the Free Software Foundation; either version 2 of the License, or
  7.  * (at your option) any later version.
  8.  *
  9.  * This program is distributed in the hope that it will be useful,
  10.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.  * GNU General Public License for more details.
  13.  *
  14.  * You should have received a copy of the GNU General Public License
  15.  * along with this program; if not, write to the Free Software
  16.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  17.  */
  18.  
  19. #include "config.h"
  20.  
  21. #include <glib.h>
  22.  
  23. #include "gimpimage.h"
  24. #include "scan_convert.h"
  25. #include "libgimp/gimpmath.h"
  26.  
  27. #include <string.h>
  28.  
  29. #ifdef DEBUG
  30. #define TRC(x) printf x
  31. #else
  32. #define TRC(x)
  33. #endif
  34.  
  35.  
  36. /* Reveal our private structure */
  37. struct ScanConverterPrivate
  38. {
  39.     guint    width;
  40.     guint    height;
  41.     GSList **scanlines;        /* array of height*antialias scanlines */
  42.  
  43.     guint    antialias;        /* how much to oversample by */
  44.  
  45.     /* record the first and last points so we can close the curve */
  46.     gboolean got_first;
  47.     ScanConvertPoint first;
  48.     gboolean got_last;
  49.     ScanConvertPoint last;
  50. };
  51.  
  52.  
  53.  
  54. /* Local helper routines to scan convert the polygon */
  55.  
  56. static GSList *
  57. insert_into_sorted_list (GSList *list,
  58.              int     x)
  59. {
  60.   GSList *orig = list;
  61.   GSList *rest;
  62.  
  63.   if (!list)
  64.     return g_slist_prepend (list, GINT_TO_POINTER (x));
  65.  
  66.   while (list)
  67.     {
  68.       rest = g_slist_next (list);
  69.       if (x < GPOINTER_TO_INT (list->data))
  70.     {
  71.       rest = g_slist_prepend (rest, list->data);
  72.       list->next = rest;
  73.       list->data = GINT_TO_POINTER (x);
  74.       return orig;
  75.     }
  76.       else if (!rest)
  77.     {
  78.       g_slist_append (list, GINT_TO_POINTER (x));
  79.       return orig;
  80.     }
  81.       list = g_slist_next (list);
  82.     }
  83.  
  84.   return orig;
  85. }
  86.  
  87.  
  88. static void
  89. convert_segment (ScanConverter *sc,
  90.          int      x1,
  91.          int      y1,
  92.          int      x2,
  93.          int      y2)
  94. {
  95.     int ydiff, y, tmp;
  96.     gint width;
  97.     gint height;
  98.     GSList **scanlines;
  99.     float xinc, xstart;
  100.  
  101.     /* pre-calculate invariant commonly used values */
  102.     width = sc->width * sc->antialias;
  103.     height = sc->height * sc->antialias;
  104.     scanlines = sc->scanlines;    
  105.  
  106.     x1 = CLAMP (x1, 0, width - 1);
  107.     y1 = CLAMP (y1, 0, height - 1);
  108.     x2 = CLAMP (x2, 0, width - 1);
  109.     y2 = CLAMP (y2, 0, height - 1);
  110.  
  111.     if (y1 > y2)
  112.     {
  113.     tmp = y2; y2 = y1; y1 = tmp;
  114.     tmp = x2; x2 = x1; x1 = tmp;
  115.     }
  116.  
  117.     ydiff = (y2 - y1);
  118.  
  119.     if (ydiff)
  120.     {
  121.     xinc = (float) (x2 - x1) / (float) ydiff;
  122.     xstart = x1 + 0.5 * xinc;
  123.     for (y = y1 ; y < y2; y++)
  124.     {
  125.       scanlines[y] = insert_into_sorted_list (scanlines[y], ROUND (xstart));
  126.       xstart += xinc;
  127.     }
  128.     }
  129.     else
  130.     {
  131.     /* horizontal line */
  132.     scanlines[y1] = insert_into_sorted_list (scanlines[y1], ROUND (x1));
  133.     scanlines[y1] = insert_into_sorted_list (scanlines[y1], ROUND (x2));
  134.     }
  135. }
  136.  
  137.  
  138.  
  139. /**************************************************************/
  140. /* Exported functions */
  141.  
  142.  
  143. /* Create a new scan conversion context */
  144. ScanConverter *
  145. scan_converter_new (guint width, guint height, guint antialias)
  146. {
  147.     ScanConverter *sc;
  148.  
  149.     g_return_val_if_fail (width > 0, NULL);
  150.     g_return_val_if_fail (height > 0, NULL);
  151.     g_return_val_if_fail (antialias > 0, NULL);
  152.  
  153.     sc = g_new0 (ScanConverter, 1);
  154.  
  155.     sc->antialias = antialias;
  156.     sc->width  = width;
  157.     sc->height = height;
  158.     sc->scanlines = g_new0 (GSList *, height*antialias);
  159.  
  160.     return sc;
  161. }
  162.  
  163.  
  164. /* Add "npoints" from "pointlist" to the polygon currently being
  165.  * described by "scan_converter".  */
  166. void
  167. scan_converter_add_points (ScanConverter *sc,
  168.                guint npoints,
  169.                ScanConvertPoint *pointlist)
  170. {
  171.     int i;
  172.     guint antialias;
  173.  
  174.     g_return_if_fail (sc != NULL);
  175.     g_return_if_fail (pointlist != NULL);
  176.  
  177.     antialias = sc->antialias;
  178.  
  179.     if (!sc->got_first && npoints > 0)
  180.     {
  181.     sc->got_first = TRUE;
  182.     sc->first = pointlist[0];
  183.     }
  184.  
  185.     /* link from previous point */
  186.     if (sc->got_last && npoints > 0)
  187.     {
  188.     TRC (("|| %g,%g -> %g,%g\n",
  189.           sc->last.x, sc->last.y,
  190.           pointlist[0].x, pointlist[0].y));
  191.     convert_segment (sc,
  192.              (int)sc->last.x * antialias,
  193.              (int)sc->last.y * antialias,
  194.              (int)pointlist[0].x * antialias,
  195.              (int)pointlist[0].y * antialias);
  196.     }
  197.  
  198.     for (i = 0; i < (npoints - 1); i++)
  199.     {
  200.     convert_segment (sc,
  201.              (int) pointlist[i].x * antialias,
  202.              (int) pointlist[i].y * antialias,
  203.              (int) pointlist[i + 1].x * antialias,
  204.              (int) pointlist[i + 1].y * antialias);
  205.     }
  206.  
  207.     TRC (("[] %g,%g -> %g,%g\n",
  208.       pointlist[0].x, pointlist[0].y,
  209.       pointlist[npoints-1].x, pointlist[npoints-1].y));
  210.  
  211.     if (npoints > 0)
  212.     {
  213.     sc->got_last = TRUE;
  214.     sc->last = pointlist[npoints - 1];
  215.     }
  216. }
  217.  
  218.  
  219.  
  220. /* Scan convert the polygon described by the list of points passed to
  221.  * scan_convert_add_points, and return a channel with a bits set if
  222.  * they fall within the polygon defined.  The polygon is filled
  223.  * according to the even-odd rule.  The polygon is closed by
  224.  * joining the final point to the initial point. */
  225. Channel *
  226. scan_converter_to_channel (ScanConverter *sc,
  227.                GimpImage *gimage)
  228. {
  229.     Channel *mask;
  230.     GSList *list;
  231.     PixelRegion maskPR;
  232.     guint widtha;
  233.     guint heighta;
  234.     guint antialias;
  235.     guint antialias2;
  236.     unsigned char *buf, *b;
  237.     int * vals, val;
  238.     int x, x2, w;
  239.     int i, j;
  240.  
  241.     antialias = sc->antialias;
  242.     antialias2 = antialias * antialias;
  243.  
  244.     /*  do we need to close the polygon? */
  245.     if (sc->got_first && sc->got_last &&
  246.     (sc->first.x != sc->last.x || sc->first.y != sc->last.y))
  247.     {
  248.     convert_segment (sc,
  249.              (int) sc->last.x * antialias,
  250.              (int) sc->last.y * antialias,
  251.              (int) sc->first.x * antialias,
  252.              (int) sc->first.y * antialias);
  253.     }
  254.  
  255.     mask = channel_new_mask (gimage, sc->width, sc->height);
  256.  
  257.     buf = g_new0 (unsigned char, sc->width);
  258.     widtha  = sc->width * antialias;
  259.     heighta = sc->height * antialias;
  260.     /* allocate value array  */
  261.     vals = g_new (int, widtha);
  262.  
  263.     /* dump scanlines */
  264.     for(i=0; i<heighta; i++)
  265.     {
  266.     list = sc->scanlines[i];
  267.     TRC (("%03d: ", i));
  268.     while (list)
  269.     {
  270.         TRC (("%3d ", GPOINTER_TO_INT (list->data)));
  271.         list = g_slist_next (list);
  272.     }
  273.     TRC (("\n"));
  274.     }
  275.  
  276.     pixel_region_init (&maskPR, drawable_data (GIMP_DRAWABLE(mask)), 0, 0, 
  277.                drawable_width (GIMP_DRAWABLE(mask)), 
  278.                drawable_height (GIMP_DRAWABLE(mask)), TRUE);
  279.  
  280.     for (i = 0; i < heighta; i++)
  281.     {
  282.     list = sc->scanlines[i];
  283.  
  284.     /*  zero the vals array  */
  285.     if (!(i % antialias))
  286.         memset (vals, 0, widtha * sizeof (int));
  287.  
  288.     while (list)
  289.     {
  290.         x = GPOINTER_TO_INT (list->data);
  291.         list = g_slist_next (list);
  292.         if (!list)
  293.         {
  294.         g_message ("Cannot properly scanline convert polygon!\n");
  295.         }
  296.         else
  297.         {
  298.         /*  bounds checking  */
  299.         x = CLAMP (x, 0, widtha);
  300.         x2 = CLAMP (GPOINTER_TO_INT (list->data), 0, widtha);
  301.  
  302.         w = x2 - x;
  303.  
  304.         if (w > 0)
  305.         {
  306.             if (antialias == 1)
  307.             {
  308.             channel_add_segment (mask, x, i, w, 255);
  309.             }
  310.             else
  311.             {
  312.             for (j = 0; j < w; j++)
  313.                 vals[j + x] += 255;
  314.             }
  315.         }
  316.         list = g_slist_next (list);
  317.         }
  318.     }
  319.  
  320.     if (antialias != 1 && !((i+1) % antialias))
  321.     {
  322.         b = buf;
  323.         for (j = 0; j < widtha; j += antialias)
  324.         {
  325.         val = 0;
  326.         for (x = 0; x < antialias; x++)
  327.             val += vals[j + x];
  328.  
  329.           *b++ = (unsigned char) (val / antialias2);
  330.         }
  331.  
  332.       pixel_region_set_row (&maskPR, 0, (i / antialias), sc->width, buf);
  333.     }
  334.     }
  335.  
  336.     g_free (vals);
  337.     g_free (buf);
  338.  
  339.     return mask;
  340. }
  341.  
  342.  
  343. void
  344. scan_converter_free (ScanConverter *sc)
  345. {
  346.     g_free (sc->scanlines);
  347.     g_free (sc);
  348. }
  349.  
  350.  
  351. /* End of scan_convert.c */
  352.