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

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  *
  4.  * gimp_brush_generated module Copyright 1998 Jay Cox <jaycox@earthlink.net>
  5.  *
  6.  * This program is free software; you can redistribute it and/or modify
  7.  * it under the terms of the GNU General Public License as published by
  8.  * the Free Software Foundation; either version 2 of the License, or
  9.  * (at your option) any later version.
  10.  *
  11.  * This program is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program; if not, write to the Free Software
  18.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  19.  */
  20.  
  21. #include "config.h"
  22.  
  23. #include <stdio.h>
  24. #include <string.h>
  25.  
  26. #ifdef HAVE_UNISTD_H
  27. #include <unistd.h>
  28. #endif
  29.  
  30. #include <glib.h>
  31.  
  32. #include "apptypes.h"
  33.  
  34. #include "appenv.h"
  35. #include "gimpbrushgenerated.h"
  36. #include "paint_core.h"
  37. #include "gimprc.h"
  38. #include "gimpbrush.h"
  39.  
  40. #include "libgimp/gimpmath.h"
  41.  
  42. #define OVERSAMPLING 5
  43.  
  44.  
  45. static void gimp_brush_generated_generate (GimpBrushGenerated *brush);
  46.  
  47.  
  48. static GimpObjectClass *parent_class = NULL;
  49.  
  50.  
  51. static void
  52. gimp_brush_generated_destroy (GtkObject *object)
  53. {
  54.   if (GTK_OBJECT_CLASS (parent_class)->destroy)
  55.     (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
  56. }
  57.  
  58. static void
  59. gimp_brush_generated_class_init (GimpBrushGeneratedClass *klass)
  60. {
  61.   GtkObjectClass *object_class;
  62.  
  63.   object_class = GTK_OBJECT_CLASS (klass);
  64.  
  65.   parent_class = gtk_type_class (GIMP_TYPE_BRUSH);
  66.  
  67.   object_class->destroy = gimp_brush_generated_destroy;
  68. }
  69.  
  70. static void
  71. gimp_brush_generated_init (GimpBrushGenerated *brush)
  72. {
  73.   brush->radius       = 5.0;
  74.   brush->hardness     = 0.0;
  75.   brush->angle        = 0.0;
  76.   brush->aspect_ratio = 1.0;
  77.   brush->freeze       = 0;
  78. }
  79.  
  80. guint
  81. gimp_brush_generated_get_type (void)
  82. {
  83.   static GtkType type = 0;
  84.  
  85.   if (!type)
  86.     {
  87.       GtkTypeInfo info =
  88.       {
  89.     "GimpBrushGenerated",
  90.     sizeof (GimpBrushGenerated),
  91.     sizeof (GimpBrushGeneratedClass),
  92.     (GtkClassInitFunc) gimp_brush_generated_class_init,
  93.     (GtkObjectInitFunc) gimp_brush_generated_init,
  94.     /* reserved_1 */ NULL,
  95.     /* reserved_2 */ NULL,
  96.     (GtkClassInitFunc) NULL
  97.       };
  98.  
  99.       type = gtk_type_unique (GIMP_TYPE_BRUSH, &info);
  100.     }
  101.  
  102.   return type;
  103. }
  104.  
  105. GimpBrush *
  106. gimp_brush_generated_new (gfloat radius,
  107.               gfloat hardness,
  108.               gfloat angle,
  109.               gfloat aspect_ratio)
  110. {
  111.   GimpBrushGenerated *brush;
  112.  
  113.   /* set up normal brush data */
  114.   brush =
  115.     GIMP_BRUSH_GENERATED (gtk_type_new (gimp_brush_generated_get_type ()));
  116.  
  117.   GIMP_BRUSH (brush)->name    = g_strdup ("Untitled");
  118.   GIMP_BRUSH (brush)->spacing = 20;
  119.  
  120.   /* set up gimp_brush_generated data */
  121.   brush->radius       = radius;
  122.   brush->hardness     = hardness;
  123.   brush->angle        = angle;
  124.   brush->aspect_ratio = aspect_ratio;
  125.  
  126.   /* render brush mask */
  127.   gimp_brush_generated_generate (brush);
  128.  
  129.   return GIMP_BRUSH (brush);
  130. }
  131.  
  132. GimpBrush *
  133. gimp_brush_generated_load (const gchar *file_name)
  134. {
  135.   GimpBrushGenerated *brush;
  136.   FILE   *fp;
  137.   gchar   string[256];
  138.   gfloat  fl;
  139.   gfloat  version;
  140.  
  141.   if ((fp = fopen (file_name, "rb")) == NULL)
  142.     return NULL;
  143.  
  144.   /* make sure the file we are reading is the right type */
  145.   fgets (string, 255, fp);
  146.   
  147.   if (strncmp (string, "GIMP-VBR", 8) != 0)
  148.     return NULL;
  149.   
  150.   /* make sure we are reading a compatible version */
  151.   fgets (string, 255, fp);
  152.   sscanf (string, "%f", &version);
  153.   g_return_val_if_fail (version < 2.0, NULL);
  154.  
  155.   /* create new brush */
  156.   brush =
  157.     GIMP_BRUSH_GENERATED (gtk_type_new (gimp_brush_generated_get_type ()));
  158.  
  159.   GIMP_BRUSH (brush)->filename = g_strdup (file_name);
  160.  
  161.   gimp_brush_generated_freeze (brush);
  162.  
  163.   /* read name */
  164.   fgets (string, 255, fp);
  165.   if (string[strlen (string) - 1] == '\n')
  166.     string[strlen (string) - 1] = 0;
  167.   GIMP_BRUSH (brush)->name = g_strdup (string);
  168.  
  169.   /* read brush spacing */
  170.   fscanf (fp, "%f", &fl);
  171.   GIMP_BRUSH (brush)->spacing = fl;
  172.  
  173.   /* read brush radius */
  174.   fscanf (fp, "%f", &fl);
  175.   gimp_brush_generated_set_radius (brush, fl);
  176.  
  177.   /* read brush hardness */
  178.   fscanf (fp, "%f", &fl);
  179.   gimp_brush_generated_set_hardness (brush, fl);
  180.  
  181.   /* read brush aspect_ratio */
  182.   fscanf (fp, "%f", &fl);
  183.   gimp_brush_generated_set_aspect_ratio (brush, fl);
  184.  
  185.   /* read brush angle */
  186.   fscanf (fp, "%f", &fl);
  187.   gimp_brush_generated_set_angle (brush, fl);
  188.  
  189.   fclose (fp);
  190.  
  191.   gimp_brush_generated_thaw (brush);
  192.  
  193.   if (stingy_memory_use)
  194.     temp_buf_swap (GIMP_BRUSH (brush)->mask);
  195.  
  196.   return GIMP_BRUSH (brush);
  197. }
  198.  
  199. void
  200. gimp_brush_generated_save (GimpBrushGenerated *brush, 
  201.                const gchar        *file_name)
  202. {
  203.   FILE *fp;
  204.  
  205.   if ((fp = fopen (file_name, "wb")) == NULL)
  206.     {
  207.       g_warning ("Unable to save file %s", file_name);
  208.       return;
  209.     }
  210.  
  211.   /* write magic header */
  212.   fprintf (fp, "GIMP-VBR\n");
  213.  
  214.   /* write version */
  215.   fprintf (fp, "1.0\n");
  216.  
  217.   /* write name */
  218.   fprintf (fp, "%.255s\n", GIMP_BRUSH (brush)->name);
  219.  
  220.   /* write brush spacing */
  221.   fprintf (fp, "%f\n", (float) GIMP_BRUSH (brush)->spacing);
  222.  
  223.   /* write brush radius */
  224.   fprintf (fp, "%f\n", brush->radius);
  225.  
  226.   /* write brush hardness */
  227.   fprintf (fp, "%f\n", brush->hardness);
  228.  
  229.   /* write brush aspect_ratio */
  230.   fprintf (fp, "%f\n", brush->aspect_ratio);
  231.  
  232.   /* write brush angle */
  233.   fprintf (fp, "%f\n", brush->angle);
  234.  
  235.   fclose (fp);
  236. }
  237.  
  238. void
  239. gimp_brush_generated_delete (GimpBrushGenerated *brush)
  240. {
  241.   if (GIMP_BRUSH (brush)->filename)
  242.     {
  243.       unlink (GIMP_BRUSH (brush)->filename);
  244.     }
  245. }
  246.  
  247. void
  248. gimp_brush_generated_freeze (GimpBrushGenerated *brush)
  249. {
  250.   g_return_if_fail (brush != NULL);
  251.   g_return_if_fail (GIMP_IS_BRUSH_GENERATED (brush));
  252.  
  253.   brush->freeze++;
  254. }
  255.  
  256. void
  257. gimp_brush_generated_thaw (GimpBrushGenerated *brush)
  258. {
  259.   g_return_if_fail (brush != NULL);
  260.   g_return_if_fail (GIMP_IS_BRUSH_GENERATED (brush));
  261.   
  262.   if (brush->freeze > 0)
  263.     brush->freeze--;
  264.  
  265.   if (brush->freeze == 0)
  266.     gimp_brush_generated_generate (brush);
  267. }
  268.  
  269. static double
  270. gauss (gdouble f)
  271.   /* this aint' a real gauss function */
  272.   if (f < -.5)
  273.     {
  274.       f = -1.0 - f;
  275.       return (2.0 * f*f);
  276.     }
  277.  
  278.   if (f < .5)
  279.     return (1.0 - 2.0 * f*f);
  280.  
  281.   f = 1.0 -f;
  282.   return (2.0 * f*f);
  283. }
  284.  
  285. void
  286. gimp_brush_generated_generate (GimpBrushGenerated *brush)
  287. {
  288.   register GimpBrush *gbrush = NULL;
  289.   register gint       x, y;
  290.   register guchar    *centerp;
  291.   register gdouble    d;
  292.   register gdouble    exponent;
  293.   register guchar     a;
  294.   register gint       length;
  295.   register guchar    *lookup;
  296.   register gdouble    sum, c, s, tx, ty;
  297.   gdouble buffer[OVERSAMPLING];
  298.   gint    width, height;
  299.  
  300.   g_return_if_fail (brush != NULL);
  301.   g_return_if_fail (GIMP_IS_BRUSH_GENERATED (brush));
  302.   
  303.   if (brush->freeze) /* if we are frozen defer rerendering till later */
  304.     return;
  305.  
  306.   gbrush = GIMP_BRUSH (brush);
  307.  
  308.   if (stingy_memory_use && gbrush->mask)
  309.     temp_buf_unswap (gbrush->mask);
  310.  
  311.   if (gbrush->mask)
  312.     {
  313.       temp_buf_free(gbrush->mask);
  314.     }
  315.  
  316.   /* compute the range of the brush. should do a better job than this? */
  317.   s = sin (gimp_deg_to_rad (brush->angle));
  318.   c = cos (gimp_deg_to_rad (brush->angle));
  319.  
  320.   tx = MAX (fabs (c*ceil (brush->radius) - s*ceil (brush->radius)
  321.           / brush->aspect_ratio), 
  322.         fabs (c*ceil (brush->radius) + s*ceil (brush->radius)
  323.           / brush->aspect_ratio));
  324.   ty = MAX (fabs (s*ceil (brush->radius) + c*ceil (brush->radius)
  325.           / brush->aspect_ratio),
  326.         fabs (s*ceil (brush->radius) - c*ceil (brush->radius)
  327.           / brush->aspect_ratio));
  328.  
  329.   if (brush->radius > tx)
  330.     width = ceil (tx);
  331.   else
  332.     width = ceil (brush->radius);
  333.  
  334.   if (brush->radius > ty)
  335.     height = ceil (ty);
  336.   else
  337.     height = ceil (brush->radius);
  338.  
  339.   /* compute the axis for spacing */
  340.   GIMP_BRUSH (brush)->x_axis.x =        c * brush->radius;
  341.   GIMP_BRUSH (brush)->x_axis.y = -1.0 * s * brush->radius;
  342.  
  343.   GIMP_BRUSH (brush)->y_axis.x = (s * brush->radius / brush->aspect_ratio);
  344.   GIMP_BRUSH (brush)->y_axis.y = (c * brush->radius / brush->aspect_ratio);
  345.   
  346.   gbrush->mask = temp_buf_new (width * 2 + 1,
  347.                    height * 2 + 1,
  348.                    1, width, height, 0);
  349.   centerp = &gbrush->mask->data[height * gbrush->mask->width + width];
  350.  
  351.   if ((1.0 - brush->hardness) < 0.000001)
  352.     exponent = 1000000; 
  353.   else
  354.     exponent = 1/(1.0 - brush->hardness);
  355.  
  356.   /* set up lookup table */
  357.   length = ceil (sqrt (2 * ceil (brush->radius+1) * ceil (brush->radius+1))+1) * OVERSAMPLING;
  358.   lookup = g_malloc (length);
  359.   sum = 0.0;
  360.  
  361.   for (x = 0; x < OVERSAMPLING; x++)
  362.     {
  363.       d = fabs ((x + 0.5) / OVERSAMPLING - 0.5);
  364.       if (d > brush->radius)
  365.     buffer[x] = 0.0;
  366.       else
  367.     /* buffer[x] =  (1.0 - pow (d/brush->radius, exponent)); */
  368.     buffer[x] = gauss (pow (d/brush->radius, exponent));
  369.       sum += buffer[x];
  370.     }
  371.  
  372.   for (x = 0; d < brush->radius || sum > 0.00001; d += 1.0 / OVERSAMPLING)
  373.     {
  374.       sum -= buffer[x % OVERSAMPLING];
  375.       if (d > brush->radius)
  376.     buffer[x % OVERSAMPLING] = 0.0;
  377.       else
  378.     /* buffer[x%OVERSAMPLING] =  (1.0 - pow (d/brush->radius, exponent)); */
  379.     buffer[x % OVERSAMPLING] = gauss (pow (d/brush->radius, exponent));
  380.       sum += buffer[x%OVERSAMPLING];
  381.       lookup[x++] = RINT (sum * (255.0 / OVERSAMPLING));
  382.     }
  383.   while (x < length)
  384.     {
  385.       lookup[x++] = 0;
  386.     }
  387.   /* compute one half and mirror it */
  388.   for (y = 0; y <= height; y++)
  389.     {
  390.       for (x = -width; x <= width; x++)
  391.     {
  392.       tx = c*x - s*y;
  393.       ty = c*y + s*x;
  394.       ty *= brush->aspect_ratio;
  395.       d = sqrt (tx*tx + ty*ty);
  396.       if (d < brush->radius+1)
  397.         a = lookup[(gint) RINT (d * OVERSAMPLING)];
  398.       else
  399.         a = 0;
  400.       centerp[   y*gbrush->mask->width + x] = a;
  401.       centerp[-1*y*gbrush->mask->width - x] = a;
  402.     }
  403.     }
  404.   g_free (lookup);
  405.  
  406.   gtk_signal_emit_by_name (GTK_OBJECT (brush), "dirty");
  407. }
  408.  
  409. gfloat
  410. gimp_brush_generated_set_radius (GimpBrushGenerated *brush,
  411.                  gfloat              radius)
  412. {
  413.   g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1.0);
  414.  
  415.   if (radius < 0.0)
  416.     radius = 0.0;
  417.   else if (radius > 32767.0)
  418.     radius = 32767.0;
  419.  
  420.   if (radius == brush->radius)
  421.     return radius;
  422.  
  423.   brush->radius = radius;
  424.  
  425.   if (!brush->freeze)
  426.     gimp_brush_generated_generate (brush);
  427.  
  428.   return brush->radius;
  429. }
  430.  
  431. gfloat
  432. gimp_brush_generated_set_hardness (GimpBrushGenerated *brush,
  433.                    gfloat              hardness)
  434. {
  435.   g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1.0);
  436.  
  437.   if (hardness < 0.0)
  438.     hardness = 0.0;
  439.   else if (hardness > 1.0)
  440.     hardness = 1.0;
  441.  
  442.   if (brush->hardness == hardness)
  443.     return hardness;
  444.  
  445.   brush->hardness = hardness;
  446.  
  447.   if (!brush->freeze)
  448.     gimp_brush_generated_generate (brush);
  449.  
  450.   return brush->hardness;
  451. }
  452.  
  453. gfloat
  454. gimp_brush_generated_set_angle (GimpBrushGenerated *brush,
  455.                 gfloat              angle)
  456. {
  457.   g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1.0);
  458.  
  459.   if (angle < 0.0)
  460.     angle = -1.0 * fmod (angle, 180.0);
  461.   else if (angle > 180.0)
  462.     angle = fmod (angle, 180.0);
  463.  
  464.   if (brush->angle == angle)
  465.     return angle;
  466.  
  467.   brush->angle = angle;
  468.  
  469.   if (!brush->freeze)
  470.     gimp_brush_generated_generate (brush);
  471.  
  472.   return brush->angle;
  473. }
  474.  
  475. gfloat
  476. gimp_brush_generated_set_aspect_ratio (GimpBrushGenerated *brush,
  477.                        gfloat              ratio)
  478. {
  479.   g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1.0);
  480.  
  481.   if (ratio < 1.0)
  482.     ratio = 1.0;
  483.   else if (ratio > 1000)
  484.     ratio = 1000;
  485.  
  486.   if (brush->aspect_ratio == ratio)
  487.     return ratio;
  488.  
  489.   brush->aspect_ratio = ratio;
  490.  
  491.   if (!brush->freeze)
  492.     gimp_brush_generated_generate(brush);
  493.  
  494.   return brush->aspect_ratio;
  495. }
  496.  
  497. gfloat
  498. gimp_brush_generated_get_radius (const GimpBrushGenerated *brush)
  499. {
  500.   g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1.0);
  501.  
  502.   return brush->radius;
  503. }
  504.  
  505. gfloat
  506. gimp_brush_generated_get_hardness (const GimpBrushGenerated *brush)
  507. {
  508.   g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1.0);
  509.  
  510.   return brush->hardness;
  511. }
  512.  
  513. gfloat
  514. gimp_brush_generated_get_angle (const GimpBrushGenerated *brush)
  515. {
  516.   g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1.0);
  517.  
  518.   return brush->angle;
  519. }
  520.  
  521. gfloat
  522. gimp_brush_generated_get_aspect_ratio (const GimpBrushGenerated *brush)
  523. {
  524.   g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1.0);
  525.  
  526.   return brush->aspect_ratio;
  527. }
  528.