Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Gimp/app/core/   (Gimp Bildeditor Version 3.2.0©)  Datei vom 14.2.2026 mit Größe 32 kB image not shown  

Quelle  gimpbrush-load.c   Sprache: C

 

/* GIMP - The GNU Image Manipulation Program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * gimpbrush-load.c
 *
 * 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 3 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, see <https://www.gnu.org/licenses/>.
 */


#include "config.h"

#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>

#include "libgimpbase/gimpbase.h"

#include "core-types.h"

#include "gimpbrush.h"
#include "gimpbrush-header.h"
#include "gimpbrush-load.h"
#include "gimpbrush-private.h"
#include "gimppattern-header.h"
#include "gimptempbuf.h"
#include "gimp-utils.h"

#include "gimp-intl.h"

/* stuff from abr2gbr Copyright (C) 2001 Marco Lamberto <lm@sunnyspot.org>  */
/* the above is GPL  see http://the.sunnyspot.org/gimp/  */

typedef struct _AbrHeader               AbrHeader;
typedef struct _AbrBrushHeader          AbrBrushHeader;
typedef struct _AbrSampledBrushHeader   AbrSampledBrushHeader;

struct _AbrHeader
{
  gint16 version;
  gint16 count;
};

struct _AbrBrushHeader
{
  gint16 type;
  gint32 size;
};

struct _AbrSampledBrushHeader
{
  gint32   misc;
  gint16   spacing;
  gchar    antialiasing;
  gint16   bounds[4];
  gint32   bounds_long[4];
  gint16   depth;
  gboolean wide;
};


/*  local function prototypes  */

static GList     * gimp_brush_load_abr_v12       (GDataInputStream  *input,
                                                  AbrHeader         *abr_hdr,
                                                  GFile             *file,
                                                  GError           **error);
static GList     * gimp_brush_load_abr_v6        (GDataInputStream  *input,
                                                  AbrHeader         *abr_hdr,
                                                  GFile             *file,
                                                  GError           **error);
static GimpBrush * gimp_brush_load_abr_brush_v12 (GDataInputStream  *input,
                                                  AbrHeader         *abr_hdr,
                                                  gint               index,
                                                  GFile             *file,
                                                  GError           **error);
static GimpBrush * gimp_brush_load_abr_brush_v6  (GDataInputStream  *input,
                                                  AbrHeader         *abr_hdr,
                                                  gint32             max_offset,
                                                  gint               index,
                                                  GFile             *file,
                                                  GError           **error);
static gboolean    abr_supported                 (AbrHeader         *abr_hdr,
                                                  GError           **error);
static gboolean    abr_reach_8bim_section        (GDataInputStream  *input,
                                                  const gchar       *name,
                                                  GError           **error);

/*  public functions  */

GList *
gimp_brush_load (GimpContext   *context,
                 GFile         *file,
                 GInputStream  *input,
                 GError       **error)
{
  GimpBrush *brush;

  g_return_val_if_fail (G_IS_FILE (file), NULL);
  g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL);
  g_return_val_if_fail (error == NULL || *error == NULL, NULL);

  brush = gimp_brush_load_brush (context, file, input, error);
  if (! brush)
    return NULL;

  return g_list_prepend (NULL, brush);
}

GimpBrush *
gimp_brush_load_brush (GimpContext   *context,
                       GFile         *file,
                       GInputStream  *input,
                       GError       **error)
{
  GimpBrush       *brush;
  gsize            bn_size;
  GimpBrushHeader  header;
  gchar           *name = NULL;
  guchar          *mask;
  gsize            bytes_read;
  gssize           i, size;
  gboolean         success = TRUE;

  g_return_val_if_fail (G_IS_FILE (file), NULL);
  g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL);
  g_return_val_if_fail (error == NULL || *error == NULL, NULL);

  /*  read the header  */
  if (! g_input_stream_read_all (input, &header, sizeof (header),
                                 &bytes_read, NULL, error) ||
      bytes_read != sizeof (header))
    {
      return NULL;
    }

  /*  rearrange the bytes in each unsigned int  */
  header.header_size  = g_ntohl (header.header_size);
  header.version      = g_ntohl (header.version);
  header.width        = g_ntohl (header.width);
  header.height       = g_ntohl (header.height);
  header.bytes        = g_ntohl (header.bytes);
  header.magic_number = g_ntohl (header.magic_number);
  header.spacing      = g_ntohl (header.spacing);

  /*  Check for correct file format */

  if (header.width == 0)
    {
      g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
                   _("Fatal parse error in brush file: Width = 0."));
      return NULL;
    }

  if (header.height == 0)
    {
      g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
                   _("Fatal parse error in brush file: Height = 0."));
      return NULL;
    }

  if (header.bytes == 0)
    {
      g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
                   _("Fatal parse error in brush file: Bytes = 0."));
      return NULL;
    }

  if (header.width  > GIMP_BRUSH_MAX_SIZE ||
      header.height > GIMP_BRUSH_MAX_SIZE ||
      G_MAXSIZE / header.width / header.height / MAX (4, header.bytes) < 1)
    {
      g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
                   _("Fatal parse error in brush file: %dx%d over max size."),
                   header.width, header.height);
      return NULL;
    }

  switch (header.version)
    {
    case 1:
      /*  If this is a version 1 brush, set the fp back 8 bytes  */
      if (! g_seekable_seek (G_SEEKABLE (input), -8, G_SEEK_CUR,
                             NULL, error))
        return NULL;

      header.header_size += 8;
      /*  spacing is not defined in version 1  */
      header.spacing = 25;
      break;

    case 3:  /*  cinepaint brush  */
      if (header.bytes == 18  /* FLOAT16_GRAY_GIMAGE */)
        {
          header.bytes = 2;
        }
      else
        {
          g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
                       _("Fatal parse error in brush file: Unknown depth %d."),
                       header.bytes);
          return NULL;
        }
      /*  fallthrough  */

    case 2:
      if (header.magic_number == GIMP_BRUSH_MAGIC)
        break;

    default:
      g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
                   _("Fatal parse error in brush file: Unknown version %d."),
                   header.version);
      return NULL;
    }

  if (header.header_size < sizeof (GimpBrushHeader))
    {
      g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
                   _("Unsupported brush format"));
      return NULL;
    }

  /*  Read in the brush name  */
  if ((bn_size = (header.header_size - sizeof (header))))
    {
      gchar *utf8;

      if (bn_size > GIMP_BRUSH_MAX_NAME)
        {
          g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
                       _("Invalid header data in '%s': "
                         "Brush name is too long: %lu"),
                       gimp_file_get_utf8_name (file),
                       (gulong) bn_size);
          return NULL;
        }

      name = g_new0 (gchar, bn_size + 1);

      if (! g_input_stream_read_all (input, name, bn_size,
                                     &bytes_read, NULL, error) ||
          bytes_read != bn_size)
        {
          g_free (name);
          return NULL;
        }

      utf8 = gimp_any_to_utf8 (name, bn_size - 1,
                               _("Invalid UTF-8 string in brush file '%s'."),
                               gimp_file_get_utf8_name (file));
      g_free (name);
      name = utf8;
    }

  if (! name)
    name = g_strdup (_("Unnamed"));

  brush = g_object_new (GIMP_TYPE_BRUSH,
                        "name",      name,
                        "mime-type", "image/x-gimp-gbr",
                        NULL);
  g_free (name);

  brush->priv->mask = gimp_temp_buf_new (header.width, header.height,
                                         babl_format ("Y u8"));

  mask = gimp_temp_buf_get_data (brush->priv->mask);
  size = header.width * header.height * header.bytes;

  switch (header.bytes)
    {
    case 1:
      success = (g_input_stream_read_all (input, mask, size,
                                          &bytes_read, NULL, error) &&
                 bytes_read == size);

      /* For backwards-compatibility, check if a pattern follows.
       * The obsolete .gpb format did it this way.
       */

      if (success)
        {
          GimpPatternHeader ph;
          goffset           rewind;

          rewind = g_seekable_tell (G_SEEKABLE (input));

          if (g_input_stream_read_all (input, &ph, sizeof (GimpPatternHeader),
                                       &bytes_read, NULL, NULL) &&
              bytes_read == sizeof (GimpPatternHeader))
            {
              /*  rearrange the bytes in each unsigned int  */
              ph.header_size  = g_ntohl (ph.header_size);
              ph.version      = g_ntohl (ph.version);
              ph.width        = g_ntohl (ph.width);
              ph.height       = g_ntohl (ph.height);
              ph.bytes        = g_ntohl (ph.bytes);
              ph.magic_number = g_ntohl (ph.magic_number);

              if (ph.magic_number == GIMP_PATTERN_MAGIC        &&
                  ph.version      == 1                         &&
                  ph.header_size  > sizeof (GimpPatternHeader) &&
                  ph.bytes        == 3                         &&
                  ph.width        == header.width              &&
                  ph.height       == header.height             &&
                  g_input_stream_skip (input,
                                       ph.header_size -
                                       sizeof (GimpPatternHeader),
                                       NULL, NULL) ==
                  ph.header_size - sizeof (GimpPatternHeader))
                {
                  guchar *pixmap;
                  gssize  pixmap_size;

                  brush->priv->pixmap =
                    gimp_temp_buf_new (header.width, header.height,
                                       babl_format ("R'G'B' u8"));

                  pixmap = gimp_temp_buf_get_data (brush->priv->pixmap);

                  pixmap_size = gimp_temp_buf_get_data_size (brush->priv->pixmap);

                  success = (g_input_stream_read_all (input, pixmap,
                                                      pixmap_size,
                                                      &bytes_read, NULL,
                                                      error) &&
                             bytes_read == pixmap_size);
                }
              else
                {
                  /*  seek back if pattern wasn't found  */
                  success = g_seekable_seek (G_SEEKABLE (input),
                                             rewind, G_SEEK_SET,
                                             NULL, error);
                }
            }
        }
      break;

    case 2:  /*  cinepaint brush, 16 bit floats  */
      {
        guchar buf[8 * 1024];

        for (i = 0; success && i < size;)
          {
            gssize bytes = MIN (size - i, sizeof (buf));

            success = (g_input_stream_read_all (input, buf, bytes,
                                                &bytes_read, NULL, error) &&
                       bytes_read == bytes);

            if (success)
              {
                guint16 *b = (guint16 *) buf;

                i += bytes;

                for (; bytes > 0; bytes -= 2, mask++, b++)
                  {
                    union
                    {
                      guint16 u[2];
                      gfloat  f;
                    } short_float;

#if G_BYTE_ORDER == G_LITTLE_ENDIAN
                    short_float.u[0] = 0;
                    short_float.u[1] = GUINT16_FROM_BE (*b);
#else
                    short_float.u[0] = GUINT16_FROM_BE (*b);
                    short_float.u[1] = 0;
#endif

                    *mask = (guchar) (short_float.f * 255.0 + 0.5);
                  }
              }
          }
      }
      break;

    case 4:
      {
        guchar *pixmap;
        guchar  buf[8 * 1024];

        brush->priv->pixmap = gimp_temp_buf_new (header.width, header.height,
                                                 babl_format ("R'G'B' u8"));
        pixmap = gimp_temp_buf_get_data (brush->priv->pixmap);

        for (i = 0; success && i < size;)
          {
            gssize bytes = MIN (size - i, sizeof (buf));

            success = (g_input_stream_read_all (input, buf, bytes,
                                                &bytes_read, NULL, error) &&
                       bytes_read == bytes);

            if (success)
              {
                guchar *b = buf;

                i += bytes;

                for (; bytes > 0; bytes -= 4, pixmap += 3, mask++, b += 4)
                  {
                    pixmap[0] = b[0];
                    pixmap[1] = b[1];
                    pixmap[2] = b[2];

                    mask[0]   = b[3];
                  }
              }
          }
      }
      break;

    default:
      g_object_unref (brush);
      g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
                   _("Fatal parse error in brush file:\n"
                     "Unsupported brush depth %d\n"
                     "GIMP brushes must be GRAY or RGBA."),
                   header.bytes);
      return NULL;
    }

  if (! success)
    {
      g_object_unref (brush);
      return NULL;
    }

  brush->priv->spacing  = header.spacing;
  brush->priv->x_axis.x = header.width  / 2.0;
  brush->priv->x_axis.y = 0.0;
  brush->priv->y_axis.x = 0.0;
  brush->priv->y_axis.y = header.height / 2.0;

  return brush;
}

GList *
gimp_brush_load_abr (GimpContext   *context,
                     GFile         *file,
                     GInputStream  *input,
                     GError       **error)
{
  GDataInputStream *data_input;
  AbrHeader         abr_hdr;
  GList            *brush_list = NULL;
  GError           *my_error   = NULL;

  g_return_val_if_fail (G_IS_FILE (file), NULL);
  g_return_val_if_fail (G_IS_INPUT_STREAM (input), NULL);
  g_return_val_if_fail (error == NULL || *error == NULL, NULL);

  data_input = g_data_input_stream_new (input);

  g_data_input_stream_set_byte_order (data_input,
                                      G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN);

  if (! gimp_data_input_stream_read_short (data_input, &abr_hdr.version, &my_error))
    goto done;

  /* sub-version for ABR v6 */
  if (! gimp_data_input_stream_read_short (data_input, &abr_hdr.count, &my_error))
    goto done;

  if (abr_supported (&abr_hdr, &my_error))
    {
      switch (abr_hdr.version)
        {
        case 1:
        case 2:
          brush_list = gimp_brush_load_abr_v12 (data_input, &abr_hdr,
                                                file, &my_error);
          break;

        case 10:
        case 6:
          brush_list = gimp_brush_load_abr_v6 (data_input, &abr_hdr,
                                               file, &my_error);
          break;
        }
    }

 done:

  g_object_unref (data_input);

  if (! brush_list)
    {
      if (! my_error)
        g_set_error (&my_error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
                     _("Unable to decode abr format version %d."),
                     abr_hdr.version);
    }

  if (my_error)
    g_propagate_error (error, my_error);

  return g_list_reverse (brush_list);
}


/*  private functions  */

static GList *
gimp_brush_load_abr_v12 (GDataInputStream  *input,
                         AbrHeader         *abr_hdr,
                         GFile             *file,
                         GError           **error)
{
  GList *brush_list = NULL;
  gint   i;

  for (i = 0; i < abr_hdr->count; i++)
    {
      GimpBrush *brush;
      GError    *my_error = NULL;

      brush = gimp_brush_load_abr_brush_v12 (input, abr_hdr, i,
                                             file, &my_error);

      /*  a NULL brush without an error means an unsupported brush
       *  type was encountered, silently skip it and try the next one
       */


      if (brush)
        {
          brush_list = g_list_prepend (brush_list, brush);
        }
      else if (my_error)
        {
          g_propagate_error (error, my_error);
          break;
        }
    }

  return brush_list;
}

static GList *
gimp_brush_load_abr_v6 (GDataInputStream  *input,
                        AbrHeader         *abr_hdr,
                        GFile             *file,
                        GError           **error)
{
  GList   *brush_list = NULL;
  gint32   sample_section_size;
  goffset  sample_section_end;
  gint     i = 1;

  if (! abr_reach_8bim_section (input, "samp", error))
    return brush_list;

  if (! gimp_data_input_stream_read_long (input, &sample_section_size, error))
    return brush_list;

  sample_section_end = (sample_section_size +
                        g_seekable_tell (G_SEEKABLE (input)));

  while (g_seekable_tell (G_SEEKABLE (input)) < sample_section_end)
    {
      GimpBrush *brush;
      GError    *my_error = NULL;

      brush = gimp_brush_load_abr_brush_v6 (input, abr_hdr, sample_section_end,
                                            i, file, &my_error);

      /*  a NULL brush without an error means an unsupported brush
       *  type was encountered, silently skip it and try the next one
       */


      if (brush)
        {
          brush_list = g_list_prepend (brush_list, brush);
        }
      else if (my_error)
        {
          g_propagate_error (error, my_error);
          break;
        }

      i++;
    }

  return brush_list;
}

static GimpBrush *
gimp_brush_load_abr_brush_v12 (GDataInputStream  *input,
                               AbrHeader         *abr_hdr,
                               gint               index,
                               GFile             *file,
                               GError           **error)
{
  GimpBrush      *brush = NULL;
  AbrBrushHeader  abr_brush_hdr;

  if (! gimp_data_input_stream_read_short (input, &abr_brush_hdr.type, error))
    return NULL;

  if (! gimp_data_input_stream_read_long (input, &abr_brush_hdr.size, error))
    return NULL;

  if (abr_brush_hdr.size < 0)
    {
      g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
                   _("Fatal parse error in brush file: "
                     "Brush size value corrupt."));
      return NULL;
    }

  /*  g_print(" + BRUSH\n | << type: %i  block size: %i bytes\n",
   *          abr_brush_hdr.type, abr_brush_hdr.size);
   */


  switch (abr_brush_hdr.type)
    {
    case 1: /* computed brush */
      /* FIXME: support it!
       *
       * We can probably feed the info into the generated brush code
       * and get a usable brush back. It seems to support the same
       * types -akl
       */

      g_printerr ("WARNING: computed brush unsupported, skipping.\n");
      g_seekable_seek (G_SEEKABLE (input), abr_brush_hdr.size,
                       G_SEEK_CUR, NULL, NULL);
      break;

    case 2: /* sampled brush */
      {
        AbrSampledBrushHeader  abr_sampled_brush_hdr;
        gint                   width, height;
        gint                   bytes;
        gint                   size;
        guchar                *mask;
        gint                   i;
        gchar                 *name;
        gchar                 *sample_name = NULL;
        gchar                 *tmp;
        gchar                  compress;

        if (! gimp_data_input_stream_read_long (input, &abr_sampled_brush_hdr.misc, error) ||
            ! gimp_data_input_stream_read_short (input, &abr_sampled_brush_hdr.spacing, error))
          break;

        if (abr_hdr->version == 2)
          {
            if (! gimp_data_input_stream_read_ucs2_text (input, &sample_name, error))
              break;
          }

        if (! gimp_data_input_stream_read_char (input, &abr_sampled_brush_hdr.antialiasing, error))
          break;

        for (i = 0; i < 4; i++)
          {
            if (! gimp_data_input_stream_read_short (input, &abr_sampled_brush_hdr.bounds[i], error))
              break;
          }

        for (i = 0; i < 4; i++)
          {
            if (! gimp_data_input_stream_read_long (input, &abr_sampled_brush_hdr.bounds_long[i], error))
              break;
          }

        if (! gimp_data_input_stream_read_short (input, &abr_sampled_brush_hdr.depth, error))
          break;

        height = (abr_sampled_brush_hdr.bounds_long[2] -
                  abr_sampled_brush_hdr.bounds_long[0]); /* bottom - top */
        width  = (abr_sampled_brush_hdr.bounds_long[3] -
                  abr_sampled_brush_hdr.bounds_long[1]); /* right - left */
        bytes  = abr_sampled_brush_hdr.depth >> 3;

        /* g_print ("width %i  height %i  bytes %i\n", width, height, bytes); */

        if (width  < 1 || width  > GIMP_BRUSH_MAX_SIZE ||
            height < 1 || height > GIMP_BRUSH_MAX_SIZE ||
            bytes  < 1 || bytes  > 1                   ||
            G_MAXSIZE / width / height / bytes < 1)
          {
            g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
                         _("Fatal parse error in brush file: "
                           "Brush dimensions out of range."));
            break;
          }

        abr_sampled_brush_hdr.wide = height > 16384;

        if (abr_sampled_brush_hdr.wide)
          {
            /* FIXME: support wide brushes */

            g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
                         _("Fatal parse error in brush file: "
                           "Wide brushes are not supported."));
            break;
          }

        tmp = g_path_get_basename (gimp_file_get_utf8_name (file));
        if (! sample_name)
          {
            /* build name from filename and index */
            name = g_strdup_printf ("%s-%03d", tmp, index);
          }
        else
          {
            /* build name from filename and sample name */
            name = g_strdup_printf ("%s-%s", tmp, sample_name);
            g_free (sample_name);
          }
        g_free (tmp);

        brush = g_object_new (GIMP_TYPE_BRUSH,
                              "name",      name,
                              /*  FIXME: MIME type!!  */
                              "mime-type", "application/x-photoshop-abr",
                              NULL);

        g_free (name);

        brush->priv->spacing  = abr_sampled_brush_hdr.spacing;
        brush->priv->x_axis.x = width / 2.0;
        brush->priv->x_axis.y = 0.0;
        brush->priv->y_axis.x = 0.0;
        brush->priv->y_axis.y = height / 2.0;
        brush->priv->mask     = gimp_temp_buf_new (width, height,
                                                   babl_format ("Y u8"));

        mask = gimp_temp_buf_get_data (brush->priv->mask);
        size = width * height * bytes;

        if (! gimp_data_input_stream_read_char (input, &compress, error))
          {
            g_object_unref (brush);
            brush = NULL;
            break;
          }

        /* g_print(" | << size: %dx%d %d bit (%d bytes) %s\n",
         *         width, height, abr_sampled_brush_hdr.depth, size,
         *         comppres ? "compressed" : "raw");
         */


        if (! compress)
          {
            gsize bytes_read;

            if (! g_input_stream_read_all (G_INPUT_STREAM (input),
                                           mask, size,
                                           &bytes_read, NULL, error) ||
                bytes_read != size)
              {
                g_object_unref (brush);
                brush = NULL;
                break;
              }
          }
        else
          {
            if (! gimp_data_input_stream_rle_decode (input, (gchar *) mask, size, height, error))
              {
                g_object_unref (brush);
                brush = NULL;
                break;
              }
          }
      }
      break;

    default:
      g_printerr ("WARNING: unknown brush type, skipping.\n");
      g_seekable_seek (G_SEEKABLE (input), abr_brush_hdr.size,
                       G_SEEK_CUR, NULL, NULL);
      break;
    }

  return brush;
}

static GimpBrush *
gimp_brush_load_abr_brush_v6 (GDataInputStream  *input,
                              AbrHeader         *abr_hdr,
                              gint32             max_offset,
                              gint               index,
                              GFile             *file,
                              GError           **error)
{
  GimpBrush *brush = NULL;
  guchar    *mask;

  gint32     brush_size;
  gint32     brush_end;
  goffset    next_brush;

  gint32     top, left, bottom, right;
  gint16     depth;
  gchar      compress;

  gint32     width, height;
  gint32     size;

  gchar     *tmp;
  gchar     *name;
  gboolean   r;

  if (! gimp_data_input_stream_read_long (input, &brush_size, error))
    return NULL;

  if (brush_size < 0)
    {
      g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
                   _("Fatal parse error in brush file: "
                     "Brush size value corrupt."));
      return NULL;
    }

  brush_end = brush_size;

  /* complement to 4 */
  while (brush_end % 4 != 0)
    brush_end++;

  next_brush = (brush_end + g_seekable_tell (G_SEEKABLE (input)));

  if (abr_hdr->count == 1)
    {
      /* discard key and short coordinates and unknown short */
      r = g_seekable_seek (G_SEEKABLE (input), 47, G_SEEK_CUR,
                           NULL, error);
    }
  else
    {
      /* discard key and unknown bytes */
      r = g_seekable_seek (G_SEEKABLE (input), 301, G_SEEK_CUR,
                           NULL, error);
    }

  if (! r)
    {
      g_prefix_error (error,
                      _("Fatal parse error in brush file: "
                        "File appears truncated: "));
      return NULL;
    }

  if (! gimp_data_input_stream_read_long  (input, &top,      error) ||
      ! gimp_data_input_stream_read_long  (input, &left,     error) ||
      ! gimp_data_input_stream_read_long  (input, &bottom,   error) ||
      ! gimp_data_input_stream_read_long  (input, &right,    error) ||
      ! gimp_data_input_stream_read_short (input, &depth,    error) ||
      ! gimp_data_input_stream_read_char  (input, &compress, error))
    return NULL;

  depth = depth >> 3;

  width  = right - left;
  height = bottom - top;
  size   = width * depth * height;

#if 0
  g_printerr ("width %i  height %i  depth %i  compress %i\n",
              width, height, depth, compress);
#endif

  if (width  < 1 || width  > GIMP_BRUSH_MAX_SIZE ||
      height < 1 || height > GIMP_BRUSH_MAX_SIZE ||
      (compress && depth != 1)                   ||
      (! compress && (depth < 1 || depth > 2))   ||
      G_MAXSIZE / width / height / depth < 1)
    {
      g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
                   _("Fatal parse error in brush file: "
                     "Brush dimensions out of range."));
      return NULL;
    }

  if (compress < 0 || compress > 1)
    {
      g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
                   _("Fatal parse error in brush file: "
                     "Unknown compression method."));
      return NULL;
    }

  tmp = g_path_get_basename (gimp_file_get_utf8_name (file));
  name = g_strdup_printf ("%s-%03d", tmp, index);
  g_free (tmp);

  brush = g_object_new (GIMP_TYPE_BRUSH,
                        "name",      name,
                        /*  FIXME: MIME type!!  */
                        "mime-type", "application/x-photoshop-abr",
                        NULL);

  g_free (name);

  brush->priv->spacing  = 25; /* real value needs 8BIMdesc section parser */
  brush->priv->x_axis.x = width / 2.0;
  brush->priv->x_axis.y = 0.0;
  brush->priv->y_axis.x = 0.0;
  brush->priv->y_axis.y = height / 2.0;
  brush->priv->mask     = gimp_temp_buf_new (width, height,
                                             babl_format ("Y u8"));

  mask = gimp_temp_buf_get_data (brush->priv->mask);

  /* data decoding */
  if (! compress)
    {
      /* not compressed - read raw bytes as brush data */
      gsize bytes_read;

      if (depth == 1)
        {
          if (! g_input_stream_read_all (G_INPUT_STREAM (input),
                                         mask, size,
                                         &bytes_read, NULL, error) ||
              bytes_read != size)
            {
              g_object_unref (brush);
              return NULL;
            }
        }
      else if (depth == 2)
        {
          /* TODO: For now, convert to 8 bit representation */
          guchar *mask_f = g_new0 (guchar, width * height * depth);

          if (! g_input_stream_read_all (G_INPUT_STREAM (input),
                                         mask_f, size,
                                         &bytes_read, NULL, error) ||
              bytes_read != size)
            {
              g_object_unref (brush);
              g_free (mask_f);
              return NULL;
            }

          for (gint i = 0; i < size; i += 2)
            {
              guint16 *temp   = (guint16 *) &mask_f[i];
              gfloat   temp_f = (gfloat) GUINT16_FROM_LE (temp[0]);

              temp_f = CLAMP (temp_f / G_MAXUINT16, 0.0, 1.0);

              mask[i / 2] = (guchar) (temp_f * 255);
            }

          g_free (mask_f);
        }
    }
  else
    {
      if (! gimp_data_input_stream_rle_decode (input, (gchar *) mask, size, height, error))
        {
          g_object_unref (brush);
          return NULL;
        }
    }

  if (g_seekable_tell (G_SEEKABLE (input)) <= next_brush)
    g_seekable_seek (G_SEEKABLE (input), next_brush, G_SEEK_SET,
                     NULL, NULL);

  return brush;
}

static gboolean
abr_supported (AbrHeader  *abr_hdr,
               GError    **error)
{
  switch (abr_hdr->version)
    {
    case 1:
    case 2:
      return TRUE;
      break;

    case 10:
    case 6:
      /* in this case, count contains format sub-version */
      if (abr_hdr->count == 1 || abr_hdr->count == 2)
        return TRUE;

      g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
                   _("Fatal parse error in brush file: "
                     "Unable to decode abr format version %d."),

                   /* horrid subversion display, but better than
                    * having yet another translatable string for
                    * this
                    */

                   abr_hdr->version * 10 + abr_hdr->count);
      break;
    }

  return FALSE;
}

static gboolean
abr_reach_8bim_section (GDataInputStream  *input,
                        const gchar       *name,
                        GError           **error)
{
  while (TRUE)
    {
      gchar   tag[4];
      gchar   tagname[5];
      gint32  section_size;
      gsize   bytes_read;

      if (! g_input_stream_read_all (G_INPUT_STREAM (input),
                                     tag, 4,
                                     &bytes_read, NULL, error) ||
          bytes_read != 4)
        return FALSE;

      if (strncmp (tag, "8BIM", 4))
        return FALSE;

      if (! g_input_stream_read_all (G_INPUT_STREAM (input),
                                     tagname, 4,
                                     &bytes_read, NULL, error) ||
          bytes_read != 4)
        return FALSE;

      tagname[4] = '\0';

      if (! strncmp (tagname, name, 4))
        return TRUE;

      if (! gimp_data_input_stream_read_long (input, §ion_size, error))
        return FALSE;

      if (! g_seekable_seek (G_SEEKABLE (input), section_size, G_SEEK_CUR,
                             NULL, error))
        return FALSE;
    }

  return FALSE;
}

Messung
CHZG
83959189

¤ Dauer der Verarbeitung: 0.7 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.