Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Gimp/plug-ins/file-bmp/   (Gimp Bildeditor Version 3.2.0©)  Datei vom 14.2.2026 mit Größe 35 kB image not shown  

Quelle  bmp-load.c   Sprache: C

 

/* bmpread.c    reads any bitmap I could get for testing */
/* Alexander.Schulz@stud.uni-karlsruhe.de                */

/*
 * GIMP - The GNU 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 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 <errno.h>
#include <string.h>

#include <glib/gstdio.h>

#include <libgimp/gimp.h>

#include "bmp.h"
#include "bmp-load.h"

#include "libgimp/stdplugins-intl.h"

/* huffman.h is auto-generated by generate-huffman.c
 * and defines:
 *  - blackroot
 *  - whiteroot
 *  - nodebuffer[]
 * (look for it in the build directory)
 */

struct Huffnode
{
  signed short   left;
  signed short   right;
  unsigned short value  : 13;
  unsigned short leaf   :  1;
  unsigned short makeup :  1;
};
#include "huffman.h"

enum Bmpformat
{
  BMPFMT_RGB,
  BMPFMT_RGB_64,
  BMPFMT_INDEXED,
  BMPFMT_RLE,
  BMPFMT_HUFFMAN
};

struct Fileinfo
{
  FILE         *file;
  gint          width;
  gint          height;
  guchar       *rowbuf;
  guint64       bytes_per_row;
  gint          bpp;
  gint          channels;
  gint          bytesperchannel;
  gint          ncolors;
  guchar        colormap[3 * 256];
  gboolean      gray;
  BitmapChannel masks[4];
  gint          tile_height;
  gint          tile_n;
  /* RLE state */
  gint          xpos;
  gint          file_ypos;
  gboolean      needs_alpha;
  /* Huffman state */
  guint32       bitbuf;
  gint          buflen;
  gint          black;
};

static GimpImage * ReadImage     (struct Fileinfo  *fi,
                                  GFile            *gfile,
                                  gint              compression,
                                  GError          **error);

static gint        load_rgb_64   (struct Fileinfo  *fi,
                                  guchar           *dest);
static gint        load_rgb      (struct Fileinfo  *fi,
                                  guchar           *dest);
static gint        load_indexed  (struct Fileinfo  *fi,
                                  guchar           *dest);
static gint        load_rle      (struct Fileinfo  *fi,
                                  guchar           *dest);
static gint        load_huffman  (struct Fileinfo  *fi,
                                  guchar           *dest);

static gint        huff_decode   (guint32           *bitbuf,
                                  gint              *buflen,
                                  FILE              *file,
                                  gint               black);

static gint        huff_findnode (guint32            bitbuf,
                                  gint               buflen,
                                  gint               black,
                                  gint              *found);

static void        huff_fillbuf  (guint32           *bitbuf,
                                  gint              *buflen,
                                  FILE              *file);

static gint        huff_find_eol (guint32           *bitbuf,
                                  gint              *buflen,
                                  FILE              *file);

static gint        huff_skip_eol (guint32           *bitbuf,
                                  gint              *buflen,
                                  FILE              *file);

static gint32
ToL (const guchar *buffer)
{
  return (buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer[3] << 24);
}

static guint32
ToLn (const guchar *buffer, gint n)
{
  gint    i;
  guint32 result = 0;

  for (i = 0; i < n; i++)
    {
      result |= ((guint32) buffer[i]) << (8 * i);
    }
  return result;
}

static gint16
ToS (const guchar *buffer)
{
  return (buffer[0] | buffer[1] << 8);
}

static gboolean
read_colormap (FILE     *fd,
               guchar   *buffer,
               gint      number,
               gint      size,
               gboolean *gray)
{
  gint i;

  *gray = (number > 2);

  for (i = 0; i < number ; i++)
    {
      guchar rgb[4];

      if (! ReadOK (fd, rgb, size))
        {
          g_message (_("Bad colormap"));
          return FALSE;
        }

      /* BMP colormap entries are in BGR order */

      buffer[3 * i + 0] = rgb[2];
      buffer[3 * i + 1] = rgb[1];
      buffer[3 * i + 2] = rgb[0];

      *gray = ((*gray) && (rgb[0] == rgb[1]) && (rgb[1] == rgb[2]));
    }

  return TRUE;
}

static void
set_default_masks (gushort        biBitCnt,
                   BitmapChannel *masks)
{
  switch (biBitCnt)
    {
    case 24:
    case 32:
      masks[0].mask = 0x00ff0000;
      masks[1].mask = 0x0000ff00;
      masks[2].mask = 0x000000ff;
      masks[3].mask = 0x00000000;
      break;

    case 16:
      /* 5 bits per channel */
      masks[0].mask = 0x00007c00;
      masks[1].mask = 0x000003e0;
      masks[2].mask = 0x0000001f;
      masks[3].mask = 0x00000000;
      break;

    default:
      break;
    }
}

static void
read_masks (guint32       *tmp,
            BitmapChannel *masks)
{
  gint i;

  for (i = 0; i < 4; i++)
    {
      masks[i].mask = tmp[i];
    }
}

static void
digest_masks (BitmapChannel *masks)
{
  gint i;

  for (i = 0; i < 4; i++)
    {
      guint32 mask   = masks[i].mask;
      gint    nbits  = 0;
      gint    offset = 0;

      while (mask && ! (mask & 1))
        {
          mask >>= 1;
          offset++;
        }

      while (mask)
        {
          mask >>= 1;
          nbits++;
        }

      masks[i].shiftin   = offset;
      masks[i].max_value = (gfloat) ((1 << nbits) - 1);
      masks[i].nbits     = nbits;
    }
}

GimpImage *
load_image (GFile *gfile, GError **error)
{
  struct Fileinfo fi;
  BitmapFileHead  bitmap_file_head;
  BitmapHead      bitmap_head;
  guchar          buffer[124];
  gint            maxcolors;
  gint            colorsize;
  GimpImage      *image = NULL;
  gchar           magick[2];

  gimp_progress_init_printf (_("Opening '%s'"), gimp_file_get_utf8_name (gfile));

  memset (&fi, 0, sizeof fi);
  memset (&bitmap_file_head, 0, sizeof bitmap_file_head);
  memset (&bitmap_head, 0, sizeof bitmap_head);

  fi.file = g_fopen (g_file_peek_path (gfile), "rb");

  if (! fi.file)
    {
      g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
                   _("Could not open '%s' for reading: %s"),
                   gimp_file_get_utf8_name (gfile), g_strerror (errno));
      goto out;
    }

  if (! ReadOK (fi.file, magick, 2) ||
      ! (! strncmp (magick, "BA", 2) ||
         ! strncmp (magick, "BM", 2) ||
         ! strncmp (magick, "IC", 2) ||
         ! strncmp (magick, "PT", 2) ||
         ! strncmp (magick, "CI", 2) ||
         ! strncmp (magick, "CP", 2)))
    {
      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                   _("'%s' is not a valid BMP file"), gimp_file_get_utf8_name (gfile));
      goto out;
    }

  while (! strncmp (magick, "BA", 2))
    {
      if (! ReadOK (fi.file, buffer, 12))
        {
          g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                       _("'%s' is not a valid BMP file"),
                       gimp_file_get_utf8_name (gfile));
          goto out;
        }

      if (! ReadOK (fi.file, magick, 2))
        {
          g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                       _("'%s' is not a valid BMP file"),
                       gimp_file_get_utf8_name (gfile));
          goto out;
        }
    }

  if (! ReadOK (fi.file, buffer, 12))
    {
      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                   _("'%s' is not a valid BMP file"), gimp_file_get_utf8_name (gfile));
      goto out;
    }

  /* bring them to the right byteorder. */

  bitmap_file_head.bfSize = ToL (&buffer[0x00]);
  bitmap_file_head.zzHotX = ToS (&buffer[0x04]);
  bitmap_file_head.zzHotY = ToS (&buffer[0x06]);
  bitmap_file_head.bfOffs = ToL (&buffer[0x08]);

  if (ReadOK (fi.file, buffer, 4))
    bitmap_head.biSize = ToL (&buffer[0x00]);

  if (bitmap_head.biSize < 12)
    {
      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                   _("'%s' is not a valid BMP file"), gimp_file_get_utf8_name (gfile));
      goto out;
    }

  memset (buffer, 0, sizeof buffer);
  if (! ReadOK (fi.file, buffer + 4, MIN (bitmap_head.biSize, sizeof buffer) - 4))
    {
      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                   _("Error reading BMP file header from '%s'"),
                   gimp_file_get_utf8_name (gfile));
      goto out;
    }

  /* OS/2 headers store width and height as unsigned, Windows headers as signed.
   * We make no attempt to distinguish between those (which would be possible
   * in some but not all cases) but always err on the Windows/signed side.
   * The only time we'll ever be wrong would be OS/2 1.x files with dimensions
   * larger than 32k (1.x files are from an era when 1024x768 was huge!) or
   * OS/2 2.x images wider or higher than 2 billion pixels -- which is way
   * over GIMP's limit anyway.
   */


  if (bitmap_head.biSize == 12)
    {
      /* BITMAPCOREHEADER and OS21XBITMAPHEADER use 16bit int for
       * width and height and have 3-byte color table entries */

      colorsize = 3;

      bitmap_head.biWidth  = ToS (&buffer[4]);
      bitmap_head.biHeight = ToS (&buffer[6]);
      bitmap_head.biPlanes = ToS (&buffer[8]);
      bitmap_head.biBitCnt = ToS (&buffer[10]);
    }
  else if (bitmap_head.biSize >= 16)
    {
      /* all others use 32bit ints and have 4-byte color table entries */
      colorsize = 4;

      /* BITMAPINFOHEADER / OS22XBITMAPHEADER */
      bitmap_head.biWidth   = ToL (&buffer[4]);
      bitmap_head.biHeight  = ToL (&buffer[8]);
      bitmap_head.biPlanes  = ToS (&buffer[12]);
      bitmap_head.biBitCnt  = ToS (&buffer[14]);
      bitmap_head.biCompr   = ToL (&buffer[16]);
      bitmap_head.biSizeIm  = ToL (&buffer[20]);
      bitmap_head.biXPels   = ToL (&buffer[24]);
      bitmap_head.biYPels   = ToL (&buffer[28]);
      bitmap_head.biClrUsed = ToL (&buffer[32]);
      bitmap_head.biClrImp  = ToL (&buffer[36]);

      /* OS22XBITMAPHEADER might write garbage into mask values, but
       * they will be ignored because there is no OS/2 BITFIELDS bmp.
       * Likewise for the following V4 fields, which would only be used
       * when the header size is larger than any valid OS/2 header.
       */

      bitmap_head.masks[0] = ToL (&buffer[40]);
      bitmap_head.masks[1] = ToL (&buffer[44]);
      bitmap_head.masks[2] = ToL (&buffer[48]);
      bitmap_head.masks[3] = ToL (&buffer[52]);

      /* the remaining fields (BITMAPV4HEADER/BITMAPV5HEADER) are
       * currently not used, but in case color profile support is
       * later added, here they are */


      /* BITMAPV4HEADER */
      bitmap_head.bV4CSType = ToL (&buffer[56]);
      for (int i = 0; i < 9; i++)
        {
          /* endpoints are stored as 2.30 */
          bitmap_head.bV4Endpoints[i] = (gdouble) ToL (&buffer[60 + 4 * i]) / 0x40000000UL;
        }

      /* gamma is stored as 16.16 */
      bitmap_head.bV4GammaRed   = ToL (&buffer[96]) / 65536.0;
      bitmap_head.bV4GammaGreen = ToL (&buffer[100]) / 65536.0;
      bitmap_head.bV4GammaBlue  = ToL (&buffer[104]) / 65536.0;

      /* BITMAPV5HEADER */
      bitmap_head.bV5Intent      = ToL (&buffer[108]);
      bitmap_head.bV5ProfileData = ToL (&buffer[112]);
      bitmap_head.bV5ProfileSize = ToL (&buffer[116]);
      bitmap_head.bV5Reserved    = ToL (&buffer[120]);

      if (bitmap_head.biSize > sizeof buffer)
        {
          /* fast-forward to end of (future) larger headers */
          if (fseek (fi.file, bitmap_head.biSize - sizeof buffer, SEEK_CUR))
            {
              g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                           _("'%s' is not a valid BMP file"),
                           gimp_file_get_utf8_name (gfile));
              goto out;
            }
        }
    }
  else
    {
      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                   _("'%s' is not a valid BMP file"), gimp_file_get_utf8_name (gfile));
      goto out;
    }

  /* identify OS/2 specific compressions and assign our own values */

  if (bitmap_head.biSize <= 64)
    {
      if (bitmap_head.biCompr == BI_BITFIELDS && bitmap_head.biBitCnt == 1)
        {
          /* BCA_HUFFMAN1D */
          bitmap_head.biCompr = BI_OS2_HUFFMAN;
        }
      else if (bitmap_head.biCompr == BI_JPEG && bitmap_head.biBitCnt == 24)
        {
          /* BCA_RLE24 */
          bitmap_head.biCompr = BI_OS2_RLE24;
        }
    }

  if (bitmap_head.biSize <= 40 &&
      (bitmap_head.biCompr == BI_BITFIELDS || bitmap_head.biCompr == BI_ALPHABITFIELDS))
    {
      /* BITMAPINFOHEADER stores masks right after (not as part of) header */

      gint nmasks;

      nmasks = bitmap_head.biCompr == BI_BITFIELDS ? 3 : 4;

      if (! ReadOK (fi.file, buffer, nmasks * sizeof (guint32)))
        {
          g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                       _("Error reading BMP file header from '%s'"),
                       gimp_file_get_utf8_name (gfile));
          goto out;
        }

      for (int i = 0; i < nmasks; i++)
        {
          bitmap_head.masks[i] = ToL (&buffer[4 * i]);
        }
    }

  switch (bitmap_head.biBitCnt)
    {
    case 1:
    case 2:
    case 4:
    case 8:
      switch (bitmap_head.biCompr)
        {
        case BI_RGB:
        case BI_RLE4:
        case BI_RLE8:
        case BI_OS2_HUFFMAN:
          if ((bitmap_head.biCompr == BI_RLE4        && bitmap_head.biBitCnt != 4) ||
              (bitmap_head.biCompr == BI_RLE8        && bitmap_head.biBitCnt != 8) ||
              (bitmap_head.biCompr == BI_OS2_HUFFMAN && bitmap_head.biBitCnt != 1))
            {
              g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                           _("'%s' is not a valid BMP file"),
                           gimp_file_get_utf8_name (gfile));
              goto out;
            }

          maxcolors = (bitmap_file_head.bfOffs - bitmap_head.biSize - 14) / colorsize;
          fi.ncolors = bitmap_head.biClrUsed == 0 ? 1 << bitmap_head.biBitCnt
                                                  : bitmap_head.biClrUsed;
          fi.ncolors = MIN (256, MIN (fi.ncolors, maxcolors));

          if (fi.ncolors < 1 ||
              ! read_colormap (fi.file, fi.colormap, fi.ncolors, colorsize, &fi.gray))
            {
              if (bitmap_head.biCompr == BI_OS2_HUFFMAN)
                {
                  /* It's unclear whether Huffman files require a color map
                   * or if they can be implicitly black & white. We'll create
                   * one if the file didn't contain a color map.
                   */

                  fi.ncolors = 2;
                  for (int c = 0; c < 3; c++)
                    {
                      fi.colormap[0 + c] = 0;
                      fi.colormap[3 + c] = 255;
                    }
                }
              else
                {
                  g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                               _("'%s' is not a valid BMP file"),
                               gimp_file_get_utf8_name (gfile));
                  goto out;
                }
            }
          break;

        default:
          g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                       _("Unsupported compression (%u) in BMP file from '%s'"),
                       bitmap_head.biCompr > 100 ? bitmap_head.biCompr - 100
                                                 : bitmap_head.biCompr,
                       gimp_file_get_utf8_name (gfile));
          goto out;
        }
      break;

    case 16:
    case 24:
    case 32:
      switch (bitmap_head.biCompr)
        {
        case BI_BITFIELDS:
        case BI_ALPHABITFIELDS:
          read_masks (&bitmap_head.masks[0], fi.masks);
          digest_masks (fi.masks);
          break;

        case BI_RGB:
          set_default_masks (bitmap_head.biBitCnt, fi.masks);
          digest_masks (fi.masks);
          break;

        case BI_OS2_RLE24:
          if (bitmap_head.biBitCnt != 24)
            {
              g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                           _("'%s' is not a valid BMP file"),
                           gimp_file_get_utf8_name (gfile));
              goto out;
            }
          break;

        default:
          g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                       _("Unsupported compression (%u) in BMP file from '%s'"),
                       bitmap_head.biCompr > 100 ? bitmap_head.biCompr - 100
                                                 : bitmap_head.biCompr,
                       gimp_file_get_utf8_name (gfile));
          goto out;
        }
      break;

    case 64:
      if (bitmap_head.biCompr != BI_RGB)
        {
          g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                       _("Unsupported compression (%u) in BMP file from '%s'"),
                       bitmap_head.biCompr > 100 ? bitmap_head.biCompr - 100
                                                 : bitmap_head.biCompr,
                       gimp_file_get_utf8_name (gfile));
          goto out;
        }
      break;

    default:
      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                   _("'%s' is not a valid BMP file"), gimp_file_get_utf8_name (gfile));
      goto out;
    }

  /* Sanity checks */

  if (bitmap_head.biPlanes != 1)
    {
      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                   _("'%s' is not a valid BMP file"), gimp_file_get_utf8_name (gfile));
      goto out;
    }

  fi.bytes_per_row = (((guint64) bitmap_head.biWidth * bitmap_head.biBitCnt + 31) / 32) * 4;

  if (fi.bytes_per_row > G_MAXSIZE || bitmap_head.biWidth > GIMP_MAX_IMAGE_SIZE ||
      bitmap_head.biWidth < 1)
    {
      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                   _("Unsupported or invalid image width: %d"),
                   (int) bitmap_head.biWidth);
      goto out;
    }

  if (bitmap_head.biHeight < G_MININT + 1 || /* +1 because |G_MININT| > G_MAXINT. */
      ABS (bitmap_head.biHeight) > GIMP_MAX_IMAGE_SIZE || bitmap_head.biHeight == 0)
    {
      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                   _("Unsupported or invalid image height: %d"),
                   (int) bitmap_head.biHeight);
      goto out;
    }

  if (fseek (fi.file, bitmap_file_head.bfOffs, SEEK_SET))
    {
      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                   _("'%s' is not a valid BMP file"), gimp_file_get_utf8_name (gfile));
      goto out;
    }

  fi.width  = bitmap_head.biWidth;
  fi.height = ABS (bitmap_head.biHeight);
  fi.bpp    = bitmap_head.biBitCnt;

  image = ReadImage (&fi, gfile, bitmap_head.biCompr, error);

  if (! image)
    goto out;

  if (bitmap_head.biXPels > 0 &&
      bitmap_head.biYPels > 0)
    {
      /* Fixed up from scott@asofyet's changes last year, njl195 */
      gdouble xresolution;
      gdouble yresolution;

      /* I don't agree with scott's feeling that Gimp should be trying
       * to "fix" metric resolution translations, in the long term
       * Gimp should be SI (metric) anyway, but we haven't told the
       * Americans that yet
       */


      xresolution = bitmap_head.biXPels * 0.0254;
      yresolution = bitmap_head.biYPels * 0.0254;

      gimp_image_set_resolution (image, xresolution, yresolution);
    }

  if (bitmap_head.biHeight < 0)
    gimp_image_flip (image, GIMP_ORIENTATION_VERTICAL);

out:
  if (fi.file)
    fclose (fi.file);

  return image;
}

static GimpImage *
ReadImage (struct Fileinfo *fi,
           GFile           *gfile,
           gint             compression,
           GError         **error)
{
  gint               ypos;
  GimpImage         *image;
  GimpLayer         *layer;
  GeglBuffer        *buffer;
  gint               i, cur_progress, max_progress;
  GimpImageBaseType  base_type;
  GimpImageType      image_type;
  GimpPrecision      precision_type = GIMP_PRECISION_U8_NON_LINEAR;
  const Babl        *format         = NULL;
  guchar            *dest           = NULL;
  gsize              dest_size      = 0;
  gsize              rowstride;
  gint               maxbits;
  gint               eof    = FALSE;
  enum Bmpformat     bmpfmt = 0;

  switch (fi->bpp)
    {
    case 64:
      base_type           = GIMP_RGB;
      image_type          = GIMP_RGBA_IMAGE;
      fi->channels        = 4;
      format              = babl_format ("RGBA float");
      precision_type      = GIMP_PRECISION_FLOAT_LINEAR;
      bmpfmt              = BMPFMT_RGB_64;
      fi->bytesperchannel = 4;
      break;

    case 32:
    case 24:
    case 16:
      base_type = GIMP_RGB;
      if (fi->masks[3].mask != 0 || compression == BI_OS2_RLE24)
        {
          image_type   = GIMP_RGBA_IMAGE;
          fi->channels = 4;
        }
      else
        {
          image_type   = GIMP_RGB_IMAGE;
          fi->channels = 3;
        }

      for (i = 0, maxbits = 0; i < fi->channels; i++)
        maxbits = MAX (maxbits, fi->masks[i].nbits);

      if (maxbits <= 8 || compression == BI_OS2_RLE24)
        {
          fi->bytesperchannel = 1;
          precision_type      = GIMP_PRECISION_U8_NON_LINEAR;
        }
      else if (maxbits <= 16)
        {
          fi->bytesperchannel = 2;
          precision_type      = GIMP_PRECISION_U16_NON_LINEAR;
        }
      else
        {
          fi->bytesperchannel = 4;
          precision_type      = GIMP_PRECISION_U32_NON_LINEAR;
        }

      if (compression == BI_OS2_RLE24)
        bmpfmt = BMPFMT_RLE;
      else
        bmpfmt = BMPFMT_RGB;

      break;

    case 8:
    case 4:
    case 2:
    case 1:
      if (fi->gray)
        {
          base_type  = GIMP_GRAY;
          image_type = GIMP_GRAY_IMAGE;
        }
      else
        {
          base_type  = GIMP_INDEXED;
          image_type = GIMP_INDEXED_IMAGE;
        }

      fi->channels        = 1;
      fi->bytesperchannel = 1;

      switch (compression)
        {
        case BI_RGB:
          bmpfmt = BMPFMT_INDEXED;
          break;

        case BI_RLE4:
        case BI_RLE8:
          if (fi->gray)
            image_type = GIMP_GRAYA_IMAGE;
          else
            image_type = GIMP_INDEXEDA_IMAGE;

          fi->channels        = 2;
          fi->bytesperchannel = 1;

          bmpfmt = BMPFMT_RLE;
          break;

        case BI_OS2_HUFFMAN:
          bmpfmt = BMPFMT_HUFFMAN;
          break;
        }
      break;

    default:
      g_message (_("Unsupported or invalid bitdepth."));
      return NULL;
    }

  fi->tile_height = MIN (gimp_tile_height (), fi->height);

  if ((guint64) fi->width * fi->tile_height < G_MAXSIZE / (fi->channels * fi->bytesperchannel))
    {
      dest_size  = (gsize) fi->width * fi->tile_height * fi->channels * fi->bytesperchannel;
      dest       = g_try_malloc0 (dest_size);
      fi->rowbuf = g_try_malloc (MAX (4, fi->bytes_per_row));
    }

  if (! (dest && fi->rowbuf))
    {
      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
                   _("Image dimensions too large: width %d x height %d"),
                   fi->width, fi->height);
      g_free (dest);
      g_free (fi->rowbuf);
      return NULL;
    }

  image = gimp_image_new_with_precision (fi->width, fi->height, base_type, precision_type);

  layer = gimp_layer_new (image, _("Background"),
                          fi->width, fi->height,
                          image_type, 100,
                          gimp_image_get_default_new_layer_mode (image));
  gimp_image_insert_layer (image, layer, NULL, 0);
  buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer));

  rowstride = (gsize) fi->width * fi->channels * fi->bytesperchannel;

  cur_progress = 0;
  max_progress = fi->height;

  fi->tile_n = 0;
  fi->file_ypos = fi->height - 1;

  for (ypos = fi->height - 1; ypos >= 0 && ! eof; ypos--)
    {
      gsize offset = (fi->tile_height - fi->tile_n - 1) * rowstride;

      switch (bmpfmt)
        {
        case BMPFMT_RGB:
          if (! load_rgb (fi, dest + offset))
            eof = TRUE;
          break;

        case BMPFMT_RGB_64:
          if (! load_rgb_64 (fi, dest + offset))
            eof = TRUE;
          break;

        case BMPFMT_INDEXED:
          if (! load_indexed (fi, dest + offset))
            eof = TRUE;
          break;

        case BMPFMT_RLE:
          if (fi->file_ypos < ypos)
            break;

          if (! load_rle (fi, dest + offset))
            eof = TRUE;
          break;

        case BMPFMT_HUFFMAN:
          if (! load_huffman (fi, dest + offset))
            eof = TRUE;
          break;
        }

      cur_progress++;
      if ((cur_progress % 5) == 0)
        gimp_progress_update ((gdouble) cur_progress / max_progress);

      if (++fi->tile_n == fi->tile_height || ypos <= 0 || eof)
        {
          /* we are filling the dest buffer backwards, so we need an offset to dest in
           * case the tile_height isn't fully used (when tile_n < tile_height)
           */

          offset = (fi->tile_height - fi->tile_n) * rowstride;

          gegl_buffer_set (buffer, GEGL_RECTANGLE (0, ypos, fi->width, fi->tile_n), 0, format,
                           dest + offset, GEGL_AUTO_ROWSTRIDE);
          memset (dest, 0, dest_size);
          fi->tile_n = 0;
        }
    }

  g_object_unref (buffer);

  g_free (dest);

  if (! fi->gray && fi->bpp <= 8)
    gimp_palette_set_colormap (gimp_image_get_palette (image), babl_format ("R'G'B' u8"),
                               fi->colormap, fi->ncolors * 3);

  gimp_progress_update (1.0);

  if (bmpfmt == BMPFMT_RLE && ! fi->needs_alpha)
    gimp_layer_flatten (layer);

  return image;
}

static gint
load_rgb (struct Fileinfo *fi, guchar *dest)
{
  gint     xpos, i;
  gint32   px;
  gdouble  d;
  guint16 *dest16;
  guint32 *dest32;

  if (! ReadOK (fi->file, fi->rowbuf, fi->bytes_per_row))
    return FALSE;

  dest16 = (guint16 *) dest;
  dest32 = (guint32 *) dest;

  for (xpos = 0; xpos < fi->width; xpos++)
    {
      px = ToLn (&fi->rowbuf[xpos * (fi->bpp / 8)], fi->bpp / 8);
      for (i = 0; i < fi->channels; i++)
        {
          d = ((px & fi->masks[i].mask) >> fi->masks[i].shiftin) / fi->masks[i].max_value;

          if (fi->bytesperchannel == 1)
            *dest++   = d * 0x00ff + 0.5;
          else if (fi->bytesperchannel == 2)
            *dest16++ = d * 0xffffUL + 0.5;
          else
            *dest32++ = d * 0xffffffffUL + 0.5;
        }
    }
  return TRUE;
}

static gint
load_rgb_64 (struct Fileinfo *fi, guchar *dest)
{
  gint    xpos, i;
  gfloat *destflt;

  if (! ReadOK (fi->file, fi->rowbuf, fi->bytes_per_row))
    return FALSE;

  destflt = (gfloat *) dest;

  for (xpos = 0; xpos < fi->width; ++xpos)
    {
      /* Values are stored as BGRA in s2.13 fixed point format;
       * s2.13 has a range of -4.0 to 3.999...
       */

      for (i = 4; i >= 0; i -= 2)
        {
          *destflt++ = ToS (&fi->rowbuf[(xpos * 8) + i]) / 8192.0;
        }

      /* Alpha Channel */
      *destflt++ = ToS (&fi->rowbuf[(xpos * 8) + 6]) / 8192.0;
    }
  return TRUE;
}

static gint
load_indexed (struct Fileinfo *fi, guchar *dest)
{
  gint xpos, val, shift;

  if (! ReadOK (fi->file, fi->rowbuf, fi->bytes_per_row))
    return FALSE;

  for (xpos = 0; xpos < fi->width; xpos++)
    {
      val   = fi->rowbuf[xpos / (8 / fi->bpp)];
      shift = 8 - fi->bpp * ((xpos % (8 / fi->bpp)) + 1);
      *dest = (val & (((1 << fi->bpp) - 1) << shift)) >> shift;
      *dest = MIN (*dest, fi->ncolors - 1);
      if (fi->gray)
        *dest = fi->colormap[*dest * 3];
      dest++;
    }

  return TRUE;
}

static gint
load_rle (struct Fileinfo *fi, guchar *basedest)
{
  gint    shift, i, j;
  guchar *dest;

  /* dest must be (re)calculated inside loop, because RLE can skip pixles */

  while (fi->xpos <= fi->width)
    {
      if (! ReadOK (fi->file, fi->rowbuf, 2))
        {
          g_message (_("The bitmap ends unexpectedly."));
          fi->needs_alpha = TRUE;
          return FALSE;
        }

      if (fi->rowbuf[0] != 0)
        {
          /* encoded run, row_buf[0] == number of repeats */
          if (fi->bpp <= 8)
            {
              for (j = 0; (j < fi->rowbuf[0]) && (fi->xpos < fi->width);)
                {
                  for (i = 1; (i <= 8 / fi->bpp) && (fi->xpos < fi->width) &&
                              (j < fi->rowbuf[0]);
                       i++, fi->xpos++, j++)
                    {
                      shift = 8 - i * fi->bpp;
                      dest  = basedest + fi->xpos * fi->channels;

                      dest[0] = (fi->rowbuf[1] & (((1 << fi->bpp) - 1) << shift)) >> shift;
                      dest[0] = MIN (dest[0], fi->ncolors - 1);
                      if (fi->gray)
                        dest[0] = fi->colormap[dest[0] * 3];
                      dest[1] = 0xff; /* alpha */
                    }
                }
            }
          else
            {
              /* read remaining 2 rgb components */
              if (! ReadOK (fi->file, fi->rowbuf + 2, 2))
                {
                  g_message (_("The bitmap ends unexpectedly."));
                  fi->needs_alpha = TRUE;
                  return FALSE;
                }
              for (i = 0; (i < fi->rowbuf[0]) && (fi->xpos < fi->width); i++, fi->xpos++)
                {
                  dest = basedest + fi->xpos * fi->channels;
                  for (gint c = 0; c < 3; c++)
                    dest[2 - c] = fi->rowbuf[1 + c];
                  dest[3] = 0xff; /* alpha */
                }
            }
        }

      else if (fi->rowbuf[0] == 0 && fi->rowbuf[1] > 2)
        {
          /* literal run */
          gint n = fi->rowbuf[1];

          for (i = 0; i < n && fi->xpos < fi->width; i++, fi->xpos++)
            {
              if (fi->bpp >= 8 || i % 2 == 0) /* RLE4 only needs new byte every 2 pixels */
                {
                  if (! ReadOK (fi->file, fi->rowbuf, fi->bpp <= 8 ? 1 : 3))
                    {
                      g_message (_("The bitmap ends unexpectedly."));
                      fi->needs_alpha = TRUE;
                      return FALSE;
                    }
                }

              dest = basedest + fi->xpos * fi->channels;
              switch (fi->bpp)
                {
                case 8:
                  dest[0] = fi->rowbuf[0];
                  break;
                case 4:
                  dest[0] = (fi->rowbuf[0] >> (4 * ((i + 1) % 2))) & 0x0f;
                  break;
                case 24:
                  for (gint c = 0; c < 3; c++)
                    dest[2 - c] = fi->rowbuf[c];
                  break;
                }
              dest[fi->channels - 1] = 0xff; /* alpha */
            }

          /* pad to 16-bit boundary */
          if ((fi->bpp == 4 ? (n + 1) / 2 : n) % 2)
            {
              if (EOF == getc (fi->file))
                {
                  g_message (_("The bitmap ends unexpectedly."));
                  fi->needs_alpha = TRUE;
                  return FALSE;
                }
            }
        }

      else if ((fi->rowbuf[0] == 0) && (fi->rowbuf[1] == 0))
        /* Line end */
        {
          if (fi->xpos < fi->width)
            fi->needs_alpha = TRUE;

          fi->xpos = 0;
          fi->file_ypos--;
          break;
        }

      else if ((fi->rowbuf[0] == 0) && (fi->rowbuf[1] == 1))
        /* Bitmap end */
        {
          if (fi->xpos < fi->width || fi->file_ypos > 0)
            fi->needs_alpha = TRUE;
          return FALSE;
        }

      else if ((fi->rowbuf[0] == 0) && (fi->rowbuf[1] == 2))
        /* Deltarecord */
        {
          if (! ReadOK (fi->file, fi->rowbuf, 2))
            {
              g_message (_("The bitmap ends unexpectedly."));
              fi->needs_alpha = TRUE;
              return FALSE;
            }
          if (fi->rowbuf[0] >= fi->width - fi->xpos || fi->rowbuf[1] >= fi->file_ypos)
            {
              /* delta points outside image, we cannot reasonably recover */
              fi->needs_alpha = TRUE;
              return FALSE;
            }

          if (fi->rowbuf[0] + fi->rowbuf[1] > 0)
            fi->needs_alpha = TRUE;

          fi->xpos      += fi->rowbuf[0];
          fi->file_ypos -= fi->rowbuf[1];

          if (fi->rowbuf[1] > 0)
            break;
        }
    }
  return TRUE;
}

static gint
load_huffman (struct Fileinfo *fi, guchar *dest)
{
  gint xpos = 0, len, i;

  while (xpos < fi->width)
    {
      huff_fillbuf (&fi->bitbuf, &fi->buflen, fi->file);
      if (fi->buflen == 0)
        return FALSE;

      if ((fi->bitbuf & 0xff000000UL) == 0)
        {
          /* any sequence that starts with 8 or more 0s is considered eol */
          if (! huff_skip_eol (&fi->bitbuf, &fi->buflen, fi->file))
            return FALSE;

          if (xpos == 0) /* ignore eol at beginning of line */
            continue;
        }
      else
        {
          len = huff_decode (&fi->bitbuf, &fi->buflen, fi->file, fi->black);
          if (len >= 0 && len <= fi->width - xpos)
            {
              for (i = 0; i < len; i++, xpos++)
                *dest++ = fi->black;
              fi->black = ! fi->black;
              continue;
            }
          else
            {
              /* Invalid bit sequence, look for next eol to re-sync */
              if (! huff_find_eol (&fi->bitbuf, &fi->buflen, fi->file))
                return FALSE;
              break;
            }
        }
    }
  fi->black = 0; /* every new row starts with white sequence */
  return TRUE;
}

static gint
huff_decode (guint32 *bitbuf, gint *buflen, FILE *file, gint black)
{
  gint idx;
  gint bits_used = 0;
  gint result    = 0;

  do
    {
      huff_fillbuf (bitbuf, buflen, file);
      bits_used = huff_findnode (*bitbuf, *buflen, black, &idx);
      if (idx == -1)
        {
          /* invalid bit sequenct encountered */
          return -1;
        }

      result += nodebuffer[idx].value;
      *bitbuf <<= bits_used;
      *buflen -= bits_used;
    }
  while (nodebuffer[idx].makeup && result < G_MAXINT - 2560);

  return nodebuffer[idx].makeup ? -1 : result;
}

static gint
huff_findnode (guint32 bitbuf, gint buflen, gint black, gint *found)
{
  gint idx;
  gint bits_used = 0;

  idx = black ? blackroot : whiteroot;

  while (idx != -1 && ! nodebuffer[idx].leaf && bits_used < buflen)
    {
      if (bitbuf & 0x80000000UL)
        idx = nodebuffer[idx].right;
      else
        idx = nodebuffer[idx].left;
      bits_used++;
      bitbuf <<= 1;
    }
  *found = idx;
  return idx != -1 ? bits_used : 0;
}

static void
huff_fillbuf (guint32 *bitbuf, gint *buflen, FILE *file)
{
  gint byte;

  while (*buflen <= 24)
    {
      if (EOF == (byte = fgetc (file)))
        return;
      *bitbuf |= (guint32) byte << (24 - *buflen);
      *buflen += 8;
    }
}

static gint
huff_find_eol (guint32 *bitbuf, gint *buflen, FILE *file)
{
  /* look for the next full 12-bit eol sequence,
   * discarding anything else
   */

  huff_fillbuf (bitbuf, buflen, file);
  while (*buflen > 11)
    {
      if ((*bitbuf & 0xffe00000UL) == 0)
        {
          *bitbuf <<= 11;
          *buflen -= 11;
          return huff_skip_eol (bitbuf, buflen, file);
        }
      *bitbuf <<= 1;
      *buflen -= 1;
      if (*buflen < 12)
        huff_fillbuf (bitbuf, buflen, file);
    }
  return FALSE;
}

static gint
huff_skip_eol (guint32 *bitbuf, gint *buflen, FILE *file)
{
  /* skip any number of 0s and the next 1 */

  huff_fillbuf (bitbuf, buflen, file);
  while (*buflen > 0)
    {
      if (*bitbuf == 0)
        {
          *buflen = 0;
          huff_fillbuf (bitbuf, buflen, file);
          continue;
        }
      while ((*bitbuf & 0x80000000UL) == 0)
        {
          *bitbuf <<= 1;
          *buflen -= 1;
        }
      *bitbuf <<= 1;
      *buflen -= 1;
      return TRUE;
    }
  return FALSE;
}

Messung
CHZG
83929389

¤ Dauer der Verarbeitung: 0.21 Sekunden  ¤

*© 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.