/* Copyright 2022-2026 Kurt Nienhaus
 *
 * This file is part of VgaGames.
 * VgaGames 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 2 of the License, or
 * (at your option) any later version.
 * VgaGames 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 VgaGames.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include "image.h"

void init_image(void);
void dest_image(void);

struct VG_Image * vg4_image_create_nolist(int, int);
struct VG_Image * vg4_image_load_nolist(const char *);
struct VG_Image * vg4_image_clone_nolist(const struct VG_Image *, const struct VG_Rect *, const struct VG_ImagecopyAttr *);

static void rd2rw(struct VG_Image *);
static struct VG_Image * rectofimg(const struct VG_Image *, const struct VG_Rect *, VG_BOOL);

static struct VG_Image * image_create(int, int);
static struct VG_Image * image_create_doit(int, int, VG_BOOL);
static struct VG_Image * image_load(const char *);
static struct VG_Image * image_load_doit(const char *, VG_BOOL);
static VG_BOOL image_save(const struct VG_Image *, const char *, VG_BOOL);
static VG_BOOL image_tobase64(const struct VG_Image *, VG_BOOL, char **, size_t *);
static void image_destroy(struct VG_Image *);
static void image_destroyall(void);
static void image_destroyall_doit(VG_BOOL);
static void image_getsize(const struct VG_Image *, const struct VG_ImagecopyAttrImage *, int *, int *);
static const char * image_getname(const struct VG_Image *);
static void image_clear(struct VG_Image *);
static void image_fill(struct VG_Image *, int);
static void image_copy(struct VG_Image *, const struct VG_Image *, const struct VG_Position *, const struct VG_ImagecopyAttr *);
static struct VG_Image * image_clone(const struct VG_Image *, const struct VG_Rect *, const struct VG_ImagecopyAttr *);
static struct VG_Image * image_clone_doit(const struct VG_Image *, const struct VG_Rect *, const struct VG_ImagecopyAttr *, VG_BOOL);
static struct VG_Image * image_copy_doit(struct VG_Image *, const struct VG_Image *, const struct VG_Position *, const struct VG_ImagecopyAttr *, VG_BOOL);
static struct VG_PixelColor image_getpoint(struct VG_Image *, int, int);
static void image_draw_point(struct VG_Image *, int, int, int);
static void image_draw_points(struct VG_Image *, const struct VG_Point *, size_t, int);
static void image_draw_line(struct VG_Image *, int, int, int, int, int);
static void image_draw_rect(struct VG_Image *, const struct VG_Rect *, int, VG_BOOL);
static void image_draw_circle(struct VG_Image *, int, int, int, int, VG_BOOL);
static void image_attr_sum(struct VG_ImagecopyAttr *, const struct VG_ImagecopyAttr *, const struct VG_ImagecopyAttr *);
static void image_dump(FILE *);


/* set functions */
void
init_image(void)
{
  vg4data.lists.image.list = SML3_calloc(1, sizeof(*vg4data.lists.image.list));  /* first list-element is unused */
  vg4data.lists.image.list->prev = vg4data.lists.image.list->next = NULL;
  vg4data.lists.image.hash = vg4_hash_create_nolist();
  vg4->image->create = image_create;
  vg4->image->load = image_load;
  vg4->image->save = image_save;
  vg4->image->tobase64 = image_tobase64;
  vg4->image->destroy = image_destroy;
  vg4->image->destroyall = image_destroyall;
  vg4->image->getsize = image_getsize;
  vg4->image->getname = image_getname;
  vg4->image->clear = image_clear;
  vg4->image->fill = image_fill;
  vg4->image->copy = image_copy;
  vg4->image->clone = image_clone;
  vg4->image->getpoint = image_getpoint;
  vg4->image->draw_point = image_draw_point;
  vg4->image->draw_points = image_draw_points;
  vg4->image->draw_line = image_draw_line;
  vg4->image->draw_rect = image_draw_rect;
  vg4->image->draw_circle = image_draw_circle;
  vg4->image->attr_sum = image_attr_sum;
  vg4->image->dump = image_dump;
} /* Ende init_image */

void
dest_image(void)
{
  image_destroyall_doit(VG_TRUE);
  vg4->hash->destroy(vg4data.lists.image.hash);
  vg4data.lists.image.hash = NULL;
  free(vg4data.lists.image.list);  /* first list-element is unused */
  vg4data.lists.image.list = NULL;
} /* Ende dest_image */


/* vg4_image_create_nolist: like image_create(), but without inserting into list */
struct VG_Image *
vg4_image_create_nolist(int width, int height)
{
  return image_create_doit(width, height, VG_FALSE);
} /* Ende vg4_image_create_nolist */


/* vg4_image_load_nolist: like image_load(), but with hidden-inserting into list */
struct VG_Image *
vg4_image_load_nolist(const char *filename)
{
  return image_load_doit(filename, VG_FALSE);
} /* Ende vg4_image_load_nolist */


/* vg4_image_clone_nolist: like image_clone(), but with hidden-inserting into list */
struct VG_Image *
vg4_image_clone_nolist(const struct VG_Image *imgsrc, const struct VG_Rect *rect, const struct VG_ImagecopyAttr *iattr)
{
  return image_clone_doit(imgsrc, rect, iattr, VG_FALSE);
} /* Ende vg4_image_clone_nolist */


/* make an image read-write */
static void
rd2rw(struct VG_Image *imgp)
{
  struct VG_Image **imgpp;
  struct VG_PixelColor *pixels;
  char fname[32];

  pixels = SML3_calloc(imgp->img.w * imgp->img.h, sizeof(*pixels));
  memcpy(pixels, imgp->img.pixels, imgp->img.w * imgp->img.h * sizeof(*pixels));
  imgp->img.pixels = pixels;
  imgp->img.isrdonly = VG_FALSE;
  imgp->img.hasopq = VG_TRUE;
  snprintf(fname, sizeof(fname), "[%dx%d]", imgp->img.w, imgp->img.h);
  imgp->img.fname = SML3_strdup(fname);

  if (imgp->prev != NULL) { imgp->prev->next = imgp->next; }
  if (imgp->next != NULL) { imgp->next->prev = imgp->prev; }
  imgp->prev = imgp->next = NULL;

  for (imgpp = &vg4data.lists.image.list; *imgpp != NULL; imgpp = &(*imgpp)->next) { imgp->prev = *imgpp; }
  *imgpp = imgp;
} /* Ende rd2rw */


/* return new image from a rectangle of the source image, or NULL = rectangle out of image */
static struct VG_Image *
rectofimg(const struct VG_Image *imgp, const struct VG_Rect *rectp, VG_BOOL intolist)
{
  struct VG_Image *imgrect;
  struct VG_PixelColor *srcpixels, *dstpixels;
  struct VG_Rect rect;
  size_t mlen;
  int ypos;

  if (imgp == NULL || rectp == NULL) { return NULL; }

  rect = *rectp;

  if (rect.x < 0) { rect.w += rect.x; rect.x = 0; }
  if (rect.w <= 0) { return NULL; }
  if (rect.x + rect.w > imgp->img.w) { rect.w = imgp->img.w - rect.x; }
  if (rect.w <= 0) { return NULL; }

  if (rect.y < 0) { rect.h += rect.y; rect.y = 0; }
  if (rect.h <= 0) { return NULL; }
  if (rect.y + rect.h > imgp->img.h) { rect.h = imgp->img.h - rect.y; }
  if (rect.h <= 0) { return NULL; }

  if (intolist) {
    imgrect = vg4->image->create(rect.w, rect.h);
  } else {
    imgrect = vg4_image_create_nolist(rect.w, rect.h);
  }
  imgrect->img.isclear = VG_FALSE;

  mlen = rect.w * sizeof(*dstpixels);
  for (ypos = 0; ypos < rect.h; ypos++) {
    srcpixels = &imgp->img.pixels[(rect.y + ypos) * imgp->img.w];
    dstpixels = &imgrect->img.pixels[ypos * imgrect->img.w];
    memcpy(&dstpixels[0], &srcpixels[rect.x], mlen);
  }

  return imgrect;
} /* Ende rectofimg */


/* image_create:
 * create image
 * @param width   width
 * @param height  height
 * @return  image
 */
static struct VG_Image *
image_create(int width, int height)
{
  return image_create_doit(width, height, VG_TRUE);
} /* Ende image_create */


/* image_create_doit: do action */
static struct VG_Image *
image_create_doit(int width, int height, VG_BOOL intolist)
{
  struct VG_Image *imgp, **imgpp;
  int xpos, ypos;
  struct VG_PixelColor pxcol, *ppix;
  char fname[32];

  if (width <= 0) { width = 1; }
  if (height <= 0) { height = 1; }

  /* create image */
  imgp = SML3_calloc(1, sizeof(*imgp));
  imgp->prev = imgp->next = NULL;
  imgp->img.pixels = SML3_calloc(width * height, sizeof(*imgp->img.pixels));
  snprintf(fname, sizeof(fname), "[%dx%d]", width, height);
  imgp->img.fname = SML3_strdup(fname);
  imgp->img.w = width;
  imgp->img.h = height;
  imgp->img.nolist = (intolist ? VG_FALSE : VG_TRUE);
  imgp->img.isrdonly = VG_FALSE;
  imgp->img.hasopq = VG_TRUE;
  imgp->img.isclear = VG_TRUE;

  /* empty image */
  GET_RGBA(VG_COLOR_TRANSPARENT, &pxcol.r, &pxcol.g, &pxcol.b, &pxcol.a);
  for (ypos = 0; ypos < imgp->img.h; ypos++) {
    ppix = &imgp->img.pixels[ypos * imgp->img.w];
    for (xpos = 0; xpos < imgp->img.w; xpos++) {
      *ppix++ = pxcol;
    }
  }

  /* insert image into list */
  if (intolist) {
    for (imgpp = &vg4data.lists.image.list; *imgpp != NULL; imgpp = &(*imgpp)->next) { imgp->prev = *imgpp; }
    *imgpp = imgp;
  }

  return imgp;
} /* Ende image_create_doit */


/* image_load:
 * load image from file
 * @param filename  file to load
 * @return  image or NULL = error
 */
static struct VG_Image *
image_load(const char *filename)
{
  return image_load_doit(filename, VG_TRUE);
} /* Ende image_load */


/* image_load_doit: do action */
static struct VG_Image *
image_load_doit(const char *filename, VG_BOOL intolist)
{
  struct VG_Image *imgp, **imgpp;
  char fname_sha1[40 + 1];

  if (filename == NULL || *filename == '\0') { outerr("loading image: no filename"); return NULL; }

  { void *cksum;
    cksum = SML3_cksum_init(SML3_CKSUM_DIGEST_SHA1);
    SML3_cksum_add(cksum, filename, strlen(filename));
    SML3_cksum_result(cksum, fname_sha1, sizeof(fname_sha1));
  }

  /* get/set head of hash-element */
  imgpp = (struct VG_Image **)vg4->hash->get(vg4data.lists.image.hash, fname_sha1, NULL);
  if (imgpp == NULL) {
    imgp = vg4_image_loadfile(filename);
    if (imgp == NULL) { return NULL; }
    if (imgp->img.fname != NULL) { free(imgp->img.fname); }
    imgp->img.isrdonly = VG_TRUE;
    imgp->img.fname = SML3_strdup(filename);
    vg4->hash->set(vg4data.lists.image.hash, fname_sha1, &imgp, sizeof(imgp));
    imgpp = (struct VG_Image **)vg4->hash->get(vg4data.lists.image.hash, fname_sha1, NULL);
  }

  /* create image read-only */
  imgp = vg4_image_create_nolist(1, 1);
  if (imgp->img.fname != NULL) { free(imgp->img.fname); }
  free(imgp->img.pixels);
  imgp->img = (*imgpp)->img;
  imgp->img.nolist = (intolist ? VG_FALSE : VG_TRUE);

  /* insert image into list */
  for (; *imgpp != NULL; imgpp = &(*imgpp)->next) { imgp->prev = *imgpp; }
  *imgpp = imgp;

  return imgp;
} /* Ende image_load_doit */


/* image_save:
 * save image to file
 * @param imgp      image
 * @param filename  file to save to
 * @param noalpha   if VG_TRUE, set alpha to 255
 * @return  VG_TRUE = OK or VG_FALSE = error
 */
static VG_BOOL
image_save(const struct VG_Image *imgp, const char *filename, VG_BOOL noalpha)
{
  return vg4_image_savefile(imgp, filename, noalpha);
} /* Ende image_save */


/* image_tobase64:
 * convert image to base64 (e.g. for text control-command "img")
 * @param imgp      image
 * @param noalpha   if VG_TRUE, set alpha to 255
 * @param encdata   for returning encoded data (with terminating 0), (must be freed with free())
 * @param encsize   for returning number of bytes in encdata (without terminating 0)
 * @return  VG_TRUE = OK or VG_FALSE = error
 */
static VG_BOOL
image_tobase64(const struct VG_Image *imgp, VG_BOOL noalpha, char **encdata, size_t *encsize)
{
  return vg4_image_tobase64(imgp, noalpha, encdata, encsize);
} /* Ende image_tobase64 */


/* image_destroy:
 * destroy image
 * @param imgp  image
 */
static void
image_destroy(struct VG_Image *imgp)
{
  if (imgp == NULL) { return; }

  if (!imgp->img.isrdonly && imgp->img.pixels != NULL) { free(imgp->img.pixels); }
  if (!imgp->img.isrdonly && imgp->img.fname != NULL) { free(imgp->img.fname); }
  if (imgp->prev != NULL) { imgp->prev->next = imgp->next; }
  if (imgp->next != NULL) { imgp->next->prev = imgp->prev; }

  free(imgp);
} /* Ende image_destroy */


/* image_destroyall:
 * destroy all images
 */
static void
image_destroyall(void)
{
  image_destroyall_doit(VG_FALSE);
} /* Ende image_destroyall */


/* image_destroyall_doit: do action */
static void
image_destroyall_doit(VG_BOOL doall)
{
  struct VG_Image *imgpn, **imgpp;
  void *vpos;
  const char *key;
  VG_BOOL wasall;

  /* free read-only image-list */
  vpos = NULL;
  for (key = vg4->hash->list(vg4data.lists.image.hash, &vpos);
       vpos != NULL;
       key = vg4->hash->list(vg4data.lists.image.hash, &vpos)) {

    /* free key-list */
    imgpp = (struct VG_Image **)vg4->hash->get(vg4data.lists.image.hash, key, NULL);
    for (imgpp = &(*imgpp)->next; *imgpp != NULL;) {
      if (doall || !(*imgpp)->img.nolist) {
        imgpn = (*imgpp)->next;
        free(*imgpp);
        *imgpp = imgpn;
      } else {
        imgpp = &(*imgpp)->next;
      }
    }

    /* free key-head */
    imgpp = (struct VG_Image **)vg4->hash->get(vg4data.lists.image.hash, key, NULL);
    if ((*imgpp)->next == NULL) {
      if ((*imgpp)->img.fname != NULL) { free((*imgpp)->img.fname); }
      if ((*imgpp)->img.pixels != NULL) { free((*imgpp)->img.pixels); }
      free(*imgpp);
      vpos = vg4->hash->remove(vg4data.lists.image.hash, key);
    }
  }

  /* free read-write image-list */
  if (vg4data.lists.image.list == NULL) { return; }
  wasall = VG_TRUE;
  for (imgpp = &vg4data.lists.image.list->next; *imgpp != NULL;) {
    if (doall || !(*imgpp)->img.nolist) {
      imgpn = (*imgpp)->next;
      if ((*imgpp)->img.fname != NULL) { free((*imgpp)->img.fname); }
      if ((*imgpp)->img.pixels != NULL) { free((*imgpp)->img.pixels); }
      free(*imgpp);
      *imgpp = imgpn;
    } else {
      imgpp = &(*imgpp)->next;
      wasall = VG_FALSE;
    }
  }
  if (wasall) { vg4data.lists.image.list->prev = vg4data.lists.image.list->next = NULL; }
} /* Ende image_destroyall_doit */


/* image_getsize:
 * get size of the image
 * @param imgp         image
 * @param iattr_image  image-modifying part of image-copy attributes, or NULL
 * @param width        for returning width if not NULL
 * @param height       for returning height if not NULL
 */
static void
image_getsize(const struct VG_Image *imgp, const struct VG_ImagecopyAttrImage *iattr_image, int *width, int *height)
{
  int wsize, hsize;

  if (width != NULL) { *width = 0; }
  if (height != NULL) { *height = 0; }
  if (imgp == NULL) { return; }

  wsize = imgp->img.w;
  hsize = imgp->img.h;

  if (iattr_image != NULL) {
    if (!iattr_image->zoom_ispercent || iattr_image->zoom_width != 100 || iattr_image->zoom_height != 100) {
      if (iattr_image->zoom_ispercent) {
        wsize = imgp->img.w * iattr_image->zoom_width / 100;
        hsize = imgp->img.h * iattr_image->zoom_height / 100;
      } else {
        wsize = iattr_image->zoom_width;
        hsize = iattr_image->zoom_height;
      }
    }
    if (iattr_image->rotate % 360 != 0) {
      image_modif_rotatesize(&wsize, &hsize, iattr_image->rotate);
    }
  }

  if (width != NULL) { *width = wsize; }
  if (height != NULL) { *height = hsize; }
} /* Ende image_getsize */


/* image_getname:
 * get filename of the image, if any
 * @param imgp  image
 * @return  filename or replacement (size of image)
 */
static const char *
image_getname(const struct VG_Image *imgp)
{
  if (imgp == NULL || imgp->img.fname == NULL) { return ""; }

  return imgp->img.fname;
} /* Ende image_getname */


/* image_clear:
 * clear an image
 * @param imgp   image
 */
static void
image_clear(struct VG_Image *imgp)
{
  vg4->image->fill(imgp, VG_COLOR_TRANSPARENT);
} /* Ende image_clear */


/* image_fill:
 * fill an image with a color
 * @param imgp   image
 * @param color  color to fill with (VG_COLOR_*)
 */
static void
image_fill(struct VG_Image *imgp, int color)
{
  struct VG_PixelColor pxcol, *ppix;
  int xpos, ypos;

  if (imgp == NULL) { return; }
  if (imgp->img.isrdonly) { rd2rw(imgp); }

  GET_RGBA(color, &pxcol.r, &pxcol.g, &pxcol.b, &pxcol.a);
  for (ypos = 0; ypos < imgp->img.h; ypos++) {
    ppix = &imgp->img.pixels[ypos * imgp->img.w];
    for (xpos = 0; xpos < imgp->img.w; xpos++) {
      *ppix++ = pxcol;
    }
  }
  if (pxcol.a == 0) { imgp->img.isclear = VG_TRUE; } else { imgp->img.isclear = VG_FALSE; }
} /* Ende image_fill */


/* image_copy:
 * copy image onto another image
 * @param imgdst  destination image
 * @param imgsrc  source image
 * @param posdst  destination position, or NULL = copy centered
 * @param iattr   image-copy attributes for source image, or NULL
 */
static void
image_copy(struct VG_Image *imgdst,
           const struct VG_Image *imgsrc,
           const struct VG_Position *posdst,
           const struct VG_ImagecopyAttr *iattr)
{
  if (imgdst == NULL) { return; }
  image_copy_doit(imgdst, imgsrc, posdst, iattr, VG_TRUE);
} /* Ende image_copy */


/* image_clone:
 * clone image creating a new one
 * @param imgsrc  source image
 * @param rect    rectangle out of source image, or NULL = whole image
 * @param iattr   image-copy attributes, or NULL
 * @return  cloned image, or NULL = rectangle out of bounds
 */
static struct VG_Image *
image_clone(const struct VG_Image *imgsrc, const struct VG_Rect *rect, const struct VG_ImagecopyAttr *iattr)
{
  return image_clone_doit(imgsrc, rect, iattr, VG_TRUE);
} /* Ende image_clone */


/* image_clone_doit: do action */
static struct VG_Image *
image_clone_doit(const struct VG_Image *imgsrc, const struct VG_Rect *rect, const struct VG_ImagecopyAttr *iattr, VG_BOOL intolist)
{
  struct VG_Image *imgdst;

  if (imgsrc == NULL) { return NULL; }

  if (rect != NULL && (rect->x != 0 || rect->y != 0 || rect->w != imgsrc->img.w || rect->h != imgsrc->img.h)) {
    struct VG_Image *imgrect = rectofimg(imgsrc, rect, intolist);
    if (imgrect == NULL) { return NULL; }
    if (iattr != NULL && (!vg4_attrpixel_is_default(&iattr->pixel) || !vg4_attrimage_is_default(&iattr->image))) {
      imgdst = image_copy_doit(NULL, imgrect, NULL, iattr, intolist);
      vg4->image->destroy(imgrect);
    } else {
      imgdst = imgrect;
    }
  } else {
    imgdst = image_copy_doit(NULL, imgsrc, NULL, iattr, intolist);
  }

  return imgdst;
} /* Ende image_clone_doit */


/* image_copy_doit: do action */
static struct VG_Image *
image_copy_doit(struct VG_Image *imgdst,
                const struct VG_Image *imgsrc,
                const struct VG_Position *posdst,
                const struct VG_ImagecopyAttr *iattr,
                VG_BOOL intolist)
{
  struct VG_Position posb;
  int xpos, ypos, blending;
  struct VG_Rect src_rect, dst_rect;
  struct VG_PixelColor *srcpixels, *dstpixels, pxcol;
  struct VG_Image *imgptrsrc;
  const struct VG_ImagecopyAttrPixel *iattr_pixel;

  if (imgsrc == NULL) { return NULL; }
  if (imgsrc == imgdst) { outerr("cannot copy image to itself"); return NULL; }

  if (imgdst != NULL) {
    if (imgsrc->img.isclear) { return imgdst; }  /* copying changes nothing in imgdst */
    blending = 1;
    if (!imgsrc->img.hasopq) { blending = 0; }  /* blending unnecessary (full copy) */
    if (imgdst != vg4data.backbuf.img && imgdst->img.isclear) { blending = 0; }  /* blending unnecessary (full copy) */
  } else {
    blending = 0;
  }

  if (iattr != NULL) {
    iattr_pixel = &iattr->pixel;
  } else {
    iattr_pixel = NULL;
  }

  if (imgdst != NULL) {
    if (posdst == NULL) {
      posb.pos = VG_POS_CENTERED;
      posb.x = imgdst->img.w / 2;
      posb.y = imgdst->img.h / 2;
      posdst = &posb;
    }
  }

  /* calculate source and destination rectangle */
  if (imgdst != NULL) {
    src_rect.x = src_rect.y = 0;
    src_rect.w = imgsrc->img.w;
    src_rect.h = imgsrc->img.h;
    dst_rect.x = dst_rect.y = 0;
    dst_rect.w = imgdst->img.w;
    dst_rect.h = imgdst->img.h;
    if (!vg4_rect_overlap(posdst, &src_rect, &dst_rect)) { return NULL; }
  }

  /* +++ copy source to destination +++ */

  imgptrsrc = (struct VG_Image *)imgsrc;

  if (iattr != NULL) {
    /* zoom */
    if (!iattr->image.zoom_ispercent || iattr->image.zoom_width != 100 || iattr->image.zoom_height != 100) {
      VG_BOOL smooth;
      struct VG_Image *pxnew;
      int zwidth, zheight;
      if (vg4data.lists.window.scale_nearest) { smooth = VG_FALSE; } else { smooth = VG_TRUE; }
      if (iattr->image.zoom_ispercent) {
        zwidth = imgptrsrc->img.w * iattr->image.zoom_width / 100;
        zheight = imgptrsrc->img.h * iattr->image.zoom_height / 100;
      } else {
        zwidth = iattr->image.zoom_width;
        zheight = iattr->image.zoom_height;
      }
      pxnew = image_modif_zoom(imgptrsrc, zwidth, zheight, smooth);
      if (pxnew != NULL) {
        if (imgptrsrc != imgsrc) { vg4->image->destroy(imgptrsrc); }
        imgptrsrc = pxnew;
      }
    }

    /* rotate and/or flip */
    if (iattr->image.rotate % 360 != 0 || iattr->image.flip != VG_AXE_NONE) {
      VG_BOOL smooth;
      struct VG_Image *pxnew;
      if (vg4data.lists.window.scale_nearest) { smooth = VG_FALSE; } else { smooth = VG_TRUE; }
      pxnew = image_modif_rotflip(imgptrsrc, iattr->image.rotate, iattr->image.flip, smooth);
      if (pxnew != NULL) {
        if (imgptrsrc != imgsrc) { vg4->image->destroy(imgptrsrc); }
        imgptrsrc = pxnew;
      }
    }

    /* calculate again source and destination rectangle */
    if (imgdst != NULL) {
      if (imgptrsrc != imgsrc) {
        src_rect.x = src_rect.y = 0;
        src_rect.w = imgptrsrc->img.w;
        src_rect.h = imgptrsrc->img.h;
        dst_rect.x = dst_rect.y = 0;
        dst_rect.w = imgdst->img.w;
        dst_rect.h = imgdst->img.h;
        if (!vg4_rect_overlap(posdst, &src_rect, &dst_rect)) {
          vg4->image->destroy(imgptrsrc);
          return NULL;
        }
      }
    }
  }

  if (imgdst == NULL) {
    if (intolist) {
      imgdst = vg4->image->create(imgptrsrc->img.w, imgptrsrc->img.h);
    } else {
      imgdst = vg4_image_create_nolist(imgptrsrc->img.w, imgptrsrc->img.h);
    }
    posb.pos = VG_POS_CENTERED;
    posb.x = imgdst->img.w / 2;
    posb.y = imgdst->img.h / 2;
    posdst = &posb;
    src_rect.x = src_rect.y = 0;
    src_rect.w = imgptrsrc->img.w;
    src_rect.h = imgptrsrc->img.h;
    dst_rect.x = dst_rect.y = 0;
    dst_rect.w = imgdst->img.w;
    dst_rect.h = imgdst->img.h;
  } else if (imgptrsrc != imgsrc) {  /* created with opaque pixels, may contain some still */
    blending = 1;
  }

  /* copy source image to destination image */
  if (blending) {
#define PIXMOD_PIXSRC       srcpixels[src_rect.x + xpos]
#define PIXMOD_PIXDST       dstpixels[dst_rect.x + xpos]
#define PIXMOD_IATTR_PIXEL  iattr_pixel
#define PIXMOD_PIXRET       pxcol
    for (ypos = 0; ypos < dst_rect.h; ypos++) {
      srcpixels = &imgptrsrc->img.pixels[(src_rect.y + ypos) * imgptrsrc->img.w];
      dstpixels = &imgdst->img.pixels[(dst_rect.y + ypos) * imgdst->img.w];
      for (xpos = 0; xpos < dst_rect.w; xpos++) {
#include "image/pixmod.h"
        dstpixels[dst_rect.x + xpos] = pxcol;
      }
    }
    imgdst->img.isclear = VG_FALSE;
#undef PIXMOD_PIXRET
#undef PIXMOD_IATTR_PIXEL
#undef PIXMOD_PIXDST
#undef PIXMOD_PIXSRC

  } else {
    if (vg4_attrpixel_is_default(iattr_pixel)) {
      size_t mlen = dst_rect.w * sizeof(*dstpixels);
      for (ypos = 0; ypos < dst_rect.h; ypos++) {
        srcpixels = &imgptrsrc->img.pixels[(src_rect.y + ypos) * imgptrsrc->img.w];
        dstpixels = &imgdst->img.pixels[(dst_rect.y + ypos) * imgdst->img.w];
        memcpy(&dstpixels[dst_rect.x], &srcpixels[src_rect.x], mlen);
      }
      imgdst->img.isclear = imgsrc->img.isclear;
    } else {
#define PIXMOD_PIXSRC       srcpixels[src_rect.x + xpos]
#define PIXMOD_IATTR_PIXEL  iattr_pixel
#define PIXMOD_PIXRET       pxcol
      for (ypos = 0; ypos < dst_rect.h; ypos++) {
        srcpixels = &imgptrsrc->img.pixels[(src_rect.y + ypos) * imgptrsrc->img.w];
        dstpixels = &imgdst->img.pixels[(dst_rect.y + ypos) * imgdst->img.w];
        for (xpos = 0; xpos < dst_rect.w; xpos++) {
#include "image/pixmod.h"
          dstpixels[dst_rect.x + xpos] = pxcol;
        }
      }
      imgdst->img.isclear = VG_FALSE;
#undef PIXMOD_PIXRET
#undef PIXMOD_IATTR_PIXEL
#undef PIXMOD_PIXSRC
    }
  }

  if (imgptrsrc != imgsrc) { vg4->image->destroy(imgptrsrc); }

  return imgdst;
} /* Ende image_copy_doit */


/* image_getpoint:
 * get pixel of an image
 * @param imgp   image
 * @param xp     x-coordinate of the pixel
 * @param yp     y-coordinate of the pixel
 * @return  pixel
 */
static struct VG_PixelColor
image_getpoint(struct VG_Image *imgp, int xp, int yp)
{
  struct VG_PixelColor pxcol, *ppix;

  memset(&pxcol, 0, sizeof(pxcol));

  if (imgp == NULL) { return pxcol; }
  if (xp < 0 || xp >= imgp->img.w || yp < 0 || yp >= imgp->img.h) { return pxcol; }

  ppix = &imgp->img.pixels[yp * imgp->img.w];
  pxcol = ppix[xp];

  return pxcol;
} /* Ende image_getpoint */


/* image_draw_point:
 * draw a pixel onto an image
 * @param imgp   image
 * @param xp     x-coordinate of the pixel
 * @param yp     y-coordinate of the pixel
 * @param color  pixel color (VG_COLOR_*)
 */
static void
image_draw_point(struct VG_Image *imgp, int xp, int yp, int color)
{
  struct VG_PixelColor pxcol, *ppix;

  if (imgp == NULL) { return; }
  if (imgp->img.isrdonly) { rd2rw(imgp); }

  if (xp < 0 || xp >= imgp->img.w || yp < 0 || yp >= imgp->img.h) { return; }

  GET_RGBA(color, &pxcol.r, &pxcol.g, &pxcol.b, &pxcol.a);
  ppix = &imgp->img.pixels[yp * imgp->img.w];
  ppix[xp] = pxcol;
  if (pxcol.a != 0) { imgp->img.isclear = VG_FALSE; }
} /* Ende image_draw_point */


/* image_draw_points:
 * draw pixels onto an image
 * @param imgp    image
 * @param points  array of pixel-positions
 * @param count   number of pixel-positions
 * @param color   pixel color (VG_COLOR_*)
 */
static void
image_draw_points(struct VG_Image *imgp, const struct VG_Point *points, size_t count, int color)
{
  struct VG_PixelColor pxcol, *ppix;
  int ipos;

  if (imgp == NULL) { return; }
  if (imgp->img.isrdonly) { rd2rw(imgp); }

  if (points == NULL || count == 0) { return; }

  GET_RGBA(color, &pxcol.r, &pxcol.g, &pxcol.b, &pxcol.a);

  for (ipos = 0; ipos < (int)count; ipos++) {
    if (points[ipos].x < 0 || points[ipos].x >= imgp->img.w) { continue; }
    if (points[ipos].y < 0 || points[ipos].y >= imgp->img.h) { continue; }
    ppix = &imgp->img.pixels[points[ipos].y * imgp->img.w];
    ppix[points[ipos].x] = pxcol;
  }
  if (pxcol.a != 0) { imgp->img.isclear = VG_FALSE; }
} /* Ende image_draw_points */


/* image_draw_line:
 * draw a line onto an image
 * @param imgp   image
 * @param x1     x-coordinate of the start point
 * @param y1     y-coordinate of the start point
 * @param x2     x-coordinate of the end point
 * @param y2     y-coordinate of the end point
 * @param color  line color (VG_COLOR_*)
 */
static void
image_draw_line(struct VG_Image *imgp, int x1, int y1, int x2, int y2, int color)
{
  struct VG_PixelColor pxcol, *ppix;

  if (imgp == NULL) { return; }
  if (imgp->img.isrdonly) { rd2rw(imgp); }

  GET_RGBA(color, &pxcol.r, &pxcol.g, &pxcol.b, &pxcol.a);

  if (y1 == y2) {  /* horizontal */
    if (y1 < 0 || y1 >= imgp->img.h) { return; }
    if (x2 < x1) { int x0 = x1; x1 = x2; x2 = x0; }
    if (x1 < 0) { x1 = 0; }
    if (x2 >= imgp->img.w) { x2 = imgp->img.w - 1; }
    ppix = &imgp->img.pixels[y1 * imgp->img.w];
    for (; x1 <= x2; x1++) { ppix[x1] = pxcol; }
  } else if (x1 == x2) {  /* vertikal */
    if (x1 < 0 || x1 >= imgp->img.w) { return; }
    if (y2 < y1) { int y0 = y1; y1 = y2; y2 = y0; }
    if (y1 < 0) { y1 = 0; }
    if (y2 >= imgp->img.h) { y2 = imgp->img.h - 1; }
    for (; y1 <= y2; y1++) {
      ppix = &imgp->img.pixels[y1 * imgp->img.w];
      ppix[x1] = pxcol;
    }
  } else if (ABSVAL(x2 - x1) == ABSVAL(y2 - y1)) {  /* diagonal */
    int xp;
    if (y2 < y1) { int i0 = y1; y1 = y2; y2 = i0; i0 = x1; x1 = x2; x2 = i0; }
    if (x2 < x1) { xp = -1; } else { xp = 1; }
    for (; y1 <= y2; y1++, x1 += xp) {
      if (y1 >= 0 && y1 < imgp->img.h && x1 >= 0 && x1 < imgp->img.w) {
        ppix = &imgp->img.pixels[y1 * imgp->img.w];
        ppix[x1] = pxcol;
      }
    }
  } else {  /* Bresenhams line algo */
    int deltax, deltay, anzpos, dpos;
    int dinc1, dinc2, xinc1, xinc2, yinc1, yinc2;
    deltax = ABSVAL(x2 - x1);
    deltay = ABSVAL(y2 - y1);
    if (deltax >= deltay) {
      anzpos = deltax + 1;
      dpos = (deltay * 2) - deltax;
      dinc1 = deltay * 2;
      dinc2 = (deltay - deltax) * 2;
      xinc1 = xinc2 = yinc2 = 1;
      yinc1 = 0;
    } else {
      anzpos = deltay + 1;
      dpos = (deltax * 2) - deltay;
      dinc1 = deltax * 2;
      dinc2 = (deltax - deltay) * 2;
      xinc1 = 0;
      xinc2 = yinc1 = yinc2 = 1;
    }
    if (x1 > x2) { xinc1 = -xinc1; xinc2 = -xinc2; }
    if (y1 > y2) { yinc1 = -yinc1; yinc2 = -yinc2; }
    for (; anzpos > 0; anzpos--) {
      if (y1 >= 0 && y1 < imgp->img.h && x1 >= 0 && x1 < imgp->img.w) {
        ppix = &imgp->img.pixels[y1 * imgp->img.w];
        ppix[x1] = pxcol;
      }
      if (dpos < 0) {
        dpos += dinc1;
        x1 += xinc1;
        y1 += yinc1;
      } else {
        dpos += dinc2;
        x1 += xinc2;
        y1 += yinc2;
      }
    }
  }
  if (pxcol.a != 0) { imgp->img.isclear = VG_FALSE; }
} /* Ende image_draw_line */


/* image_draw_rect:
 * draw a rectangle onto an image
 * @param imgp   image
 * @param rect   rectangle or NULL = whole image
 * @param color  rectangle color (VG_COLOR_*)
 * @param fill   whether to fill rectangle
 */
static void
image_draw_rect(struct VG_Image *imgp, const struct VG_Rect *rect, int color, VG_BOOL fill)
{
  struct VG_PixelColor pxcol, *ppix;
  int xpos, ypos;
  struct VG_Rect rectb, src_rect, dst_rect;
  struct VG_Position posdst;

  if (imgp == NULL) { return; }
  if (imgp->img.isrdonly) { rd2rw(imgp); }

  if (rect == NULL) {
    rectb.x = rectb.y = 0;
    rectb.w = imgp->img.w;
    rectb.h = imgp->img.h;
    rect = &rectb;
  }

  GET_RGBA(color, &pxcol.r, &pxcol.g, &pxcol.b, &pxcol.a);

  posdst.pos = VG_POS_UPPER_LEFT;
  posdst.x = rect->x;
  posdst.y = rect->y;
  src_rect.x = src_rect.y = 0;
  src_rect.w = rect->w;
  src_rect.h = rect->h;
  dst_rect.x = dst_rect.y = 0;
  dst_rect.w = imgp->img.w;
  dst_rect.h = imgp->img.h;
  if (!vg4_rect_overlap(&posdst, &src_rect, &dst_rect)) { return; }

  if (fill) {
    for (ypos = 0; ypos < dst_rect.h; ypos++) {
      ppix = &imgp->img.pixels[(dst_rect.y + ypos) * imgp->img.w];
      for (xpos = 0; xpos < dst_rect.w; xpos++) {
        ppix[dst_rect.x + xpos] = pxcol;
      }
    }
  } else {
    if (src_rect.y == 0) {
      ppix = &imgp->img.pixels[(dst_rect.y + 0) * imgp->img.w];
      for (xpos = 0; xpos < dst_rect.w; xpos++) {
        ppix[dst_rect.x + xpos] = pxcol;
      }
    }
    if (src_rect.y + src_rect.h == rect->h) {
      ppix = &imgp->img.pixels[(dst_rect.y + dst_rect.h - 1) * imgp->img.w];
      for (xpos = 0; xpos < dst_rect.w; xpos++) {
        ppix[dst_rect.x + xpos] = pxcol;
      }
    }
    for (ypos = 0; ypos < dst_rect.h; ypos++) {
      ppix = &imgp->img.pixels[(dst_rect.y + ypos) * imgp->img.w];
      if (src_rect.x == 0) { ppix[dst_rect.x] = pxcol; }
      if (src_rect.x + src_rect.w == rect->w) { ppix[dst_rect.x + dst_rect.w - 1] = pxcol; }
    }
  }
  if (pxcol.a != 0) { imgp->img.isclear = VG_FALSE; }
} /* Ende image_draw_rect */


/* image_draw_circle:
 * draw a circle onto an image
 * @param imgp    image
 * @param xm      x-coordinate of center
 * @param ym      y-coordinate of center
 * @param radius  radius
 * @param color   circle color (VG_COLOR_*)
 * @param fill    whether to fill circle
 */
static void
image_draw_circle(struct VG_Image *imgp, int xm, int ym, int radius, int color, VG_BOOL fill)
{
  struct VG_PixelColor pxcol, *ppix;
  int xpos, ypos, ilen;
  int deltax, deltay, dd;

  if (imgp == NULL) { return; }
  if (imgp->img.isrdonly) { rd2rw(imgp); }

  if (radius < 1) { vg4->image->draw_point(imgp, xm, ym, color); return; }

  GET_RGBA(color, &pxcol.r, &pxcol.g, &pxcol.b, &pxcol.a);

  deltax = radius;
  deltay = 0;
  dd = 1 - radius;

#define DWCIR_PIXEL \
  if (xpos >= 0 && xpos < imgp->img.w && ypos >= 0 && ypos < imgp->img.h) { \
    ppix = &imgp->img.pixels[ypos * imgp->img.w]; \
    ppix[xpos] = pxcol; \
  }

#define DWCIR_LINE \
  if (xpos < 0) { ilen += xpos; xpos = 0; } \
  if (xpos + ilen > imgp->img.w) { ilen = imgp->img.w - xpos; } \
  if (ypos >= 0 && ypos < imgp->img.h && ilen > 0) { \
    ppix = &imgp->img.pixels[ypos * imgp->img.w]; \
    for (; ilen > 0; ilen--) { ppix[xpos + ilen] = pxcol; } \
  }

  xpos = xm; ypos = ym - deltax;
  DWCIR_PIXEL;
  xpos = xm; ypos = ym + deltax;
  DWCIR_PIXEL;

  if (fill) {
    xpos = xm - deltax; ypos = ym; ilen = 2 * deltax + 1; DWCIR_LINE;
  } else {
    xpos = xm - deltax; ypos = ym; DWCIR_PIXEL;
    xpos = xm + deltax; ypos = ym; DWCIR_PIXEL;
  }

  while (deltay < deltax) {
    if (dd < 0) {
      dd += (deltay * 2 + 3);
    } else {
      dd += (deltay * 2 - deltax * 2 + 5);
      deltax--;
    }
    deltay++;

    if (fill) {
      xpos = xm - deltax; ypos = ym - deltay; ilen = 2 * deltax + 1; DWCIR_LINE;
      xpos = xm - deltax; ypos = ym + deltay; ilen = 2 * deltax + 1; DWCIR_LINE;
      xpos = xm - deltay; ypos = ym - deltax; ilen = 2 * deltay + 1; DWCIR_LINE;
      xpos = xm - deltay; ypos = ym + deltax; ilen = 2 * deltay + 1; DWCIR_LINE;
    } else {
      xpos = xm - deltax; ypos = ym - deltay; DWCIR_PIXEL;
      xpos = xm - deltax; ypos = ym + deltay; DWCIR_PIXEL;
      xpos = xm + deltax; ypos = ym - deltay; DWCIR_PIXEL;
      xpos = xm + deltax; ypos = ym + deltay; DWCIR_PIXEL;
      xpos = xm - deltay; ypos = ym - deltax; DWCIR_PIXEL;
      xpos = xm - deltay; ypos = ym + deltax; DWCIR_PIXEL;
      xpos = xm + deltay; ypos = ym - deltax; DWCIR_PIXEL;
      xpos = xm + deltay; ypos = ym + deltax; DWCIR_PIXEL;
    }
  }
  if (pxcol.a != 0) { imgp->img.isclear = VG_FALSE; }
#undef DWCIR_LINE
#undef DWCIR_PIXEL
} /* Ende image_draw_circle */


/* image_attr_sum:
 * sum two image-copy attributes
 * @param iattr_sum   for returning summed image-copy attributes
 * @param iattr1      image-copy attributes to be summed
 * @param iattr2      image-copy attributes to be summed
 */
static void
image_attr_sum(struct VG_ImagecopyAttr *iattr_sum, const struct VG_ImagecopyAttr *iattr1, const struct VG_ImagecopyAttr *iattr2)
{
  if (iattr_sum == NULL) { return; }
  VG_IMAGECOPY_ATTR_DEFAULT(iattr_sum);

  if (iattr1 == NULL && iattr2 == NULL) { return; }
  if (iattr1 == NULL) { *iattr_sum = *iattr2; return; }
  if (iattr2 == NULL) { *iattr_sum = *iattr1; return; }

  /* pixel-modifying attributes */

  if (!vg4_attrpixel_is_default(&iattr1->pixel) && !vg4_attrpixel_is_default(&iattr2->pixel)) {
    iattr_sum->pixel.opaqueness = iattr1->pixel.opaqueness * iattr2->pixel.opaqueness / 100;
    iattr_sum->pixel.brightness = iattr1->pixel.brightness * iattr2->pixel.brightness / 100;

    if (iattr1->pixel.colorize_color >= 0 && iattr1->pixel.colorize_color != VG_COLOR_TRANSPARENT
        && iattr2->pixel.colorize_color >= 0 && iattr2->pixel.colorize_color != VG_COLOR_TRANSPARENT) {
      struct VG_PixelColor rgb1, rgb2;
      GET_RGBA(iattr1->pixel.colorize_color, &rgb1.r, &rgb1.g, &rgb1.b, &rgb1.a);
      GET_RGBA(iattr2->pixel.colorize_color, &rgb2.r, &rgb2.g, &rgb2.b, &rgb2.a);
      iattr_sum->pixel.colorize_color = ((((int)rgb1.r + (int)rgb2.r) / 2) << 16)
                                        | ((((int)rgb1.g + (int)rgb2.g) / 2) << 8)
                                        | (((int)rgb1.b + (int)rgb2.b) / 2);
      iattr_sum->pixel.colorize_percent = (iattr1->pixel.colorize_percent + iattr2->pixel.colorize_percent) / 2;
    } else if (iattr1->pixel.colorize_color >= 0 && iattr1->pixel.colorize_color != VG_COLOR_TRANSPARENT) {
      iattr_sum->pixel.colorize_color = iattr1->pixel.colorize_color;
      iattr_sum->pixel.colorize_percent = iattr1->pixel.colorize_percent;
    } else if (iattr2->pixel.colorize_color >= 0 && iattr2->pixel.colorize_color != VG_COLOR_TRANSPARENT) {
      iattr_sum->pixel.colorize_color = iattr2->pixel.colorize_color;
      iattr_sum->pixel.colorize_percent = iattr2->pixel.colorize_percent;
    } else {
      iattr_sum->pixel.colorize_color = iattr1->pixel.colorize_color;
      iattr_sum->pixel.colorize_percent = iattr1->pixel.colorize_percent;
    }

    if (iattr1->pixel.pixelcolor == VG_PIXELCOLOR_NOP && iattr2->pixel.pixelcolor == VG_PIXELCOLOR_NOP) {
      iattr_sum->pixel.pixelcolor = VG_PIXELCOLOR_NOP;
    } else if (iattr1->pixel.pixelcolor == VG_PIXELCOLOR_NOP) {
      iattr_sum->pixel.pixelcolor = iattr2->pixel.pixelcolor;
    } else if (iattr2->pixel.pixelcolor == VG_PIXELCOLOR_NOP) {
      iattr_sum->pixel.pixelcolor = iattr1->pixel.pixelcolor;
    } else if (iattr1->pixel.pixelcolor != iattr2->pixel.pixelcolor) {  /* collision */
      iattr_sum->pixel.pixelcolor = VG_PIXELCOLOR_NOP;
    } else {
      iattr_sum->pixel.pixelcolor = iattr1->pixel.pixelcolor;
    }

    if (iattr1->pixel.cbf == NULL && iattr2->pixel.cbf == NULL) {
      iattr_sum->pixel.cbf = NULL;
    } else if (iattr1->pixel.cbf == NULL) {
      iattr_sum->pixel.cbf = iattr2->pixel.cbf;
    } else if (iattr2->pixel.cbf == NULL) {
      iattr_sum->pixel.cbf = iattr1->pixel.cbf;
    } else if (iattr1->pixel.cbf != iattr2->pixel.cbf) {  /* collision */
      iattr_sum->pixel.cbf = NULL;
    } else {
      iattr_sum->pixel.cbf = iattr1->pixel.cbf;
    }
  } else if (!vg4_attrpixel_is_default(&iattr1->pixel)) {
    iattr_sum->pixel = iattr1->pixel;
  } else if (!vg4_attrpixel_is_default(&iattr2->pixel)) {
    iattr_sum->pixel = iattr2->pixel;
  }

  /* image-modifying attributes */

  if (iattr1->image.zoom_ispercent && iattr2->image.zoom_ispercent) {
    iattr_sum->image.zoom_width = iattr1->image.zoom_width * iattr2->image.zoom_width / 100;
    iattr_sum->image.zoom_height = iattr1->image.zoom_height * iattr2->image.zoom_height / 100;
  } else if (!iattr1->image.zoom_ispercent && !iattr2->image.zoom_ispercent) {
    iattr_sum->image.zoom_width = (iattr1->image.zoom_width + iattr2->image.zoom_width) / 2;
    iattr_sum->image.zoom_height = (iattr1->image.zoom_height + iattr2->image.zoom_height) / 2;
  } else {  /* collision */
    iattr_sum->image.zoom_width = 100;
    iattr_sum->image.zoom_height = 100;
    iattr_sum->image.zoom_ispercent = VG_TRUE;
  }

  iattr_sum->image.rotate = iattr1->image.rotate + iattr2->image.rotate;

  if (iattr1->image.flip == iattr2->image.flip) {
    iattr_sum->image.flip = VG_AXE_NONE;
  } else if (iattr1->image.flip == VG_AXE_NONE) {
    iattr_sum->image.flip = iattr2->image.flip;
  } else if (iattr2->image.flip == VG_AXE_NONE) {
    iattr_sum->image.flip = iattr1->image.flip;
  } else {
    iattr_sum->image.flip = VG_AXE_NONE;
    iattr_sum->image.rotate += 180;
  }
} /* Ende image_attr_sum */


/* image_dump:
 * dump image entries
 * @param outfp  filepointer to dump to, or NULL = stdout
 */
static void
image_dump(FILE *outfp)
{
  struct VG_Image *imgp0, **imgpp;
  void *vpos;
  const char *key;
  int izahl;

  if (outfp == NULL) { outfp = stdout; }

  fprintf(outfp, "\nDump of image entries\n");
  fprintf(outfp, "=====================\n\n");

  fprintf(outfp, "Readonly:\n");
  vpos = NULL;
  for (key = vg4->hash->list(vg4data.lists.image.hash, &vpos);
       vpos != NULL;
       key = vg4->hash->list(vg4data.lists.image.hash, &vpos)) {
    imgpp = (struct VG_Image **)vg4->hash->get(vg4data.lists.image.hash, key, NULL);
    imgp0 = (*imgpp)->next;
    for (izahl = 0; imgp0 != NULL; imgp0 = imgp0->next) {
      if (!imgp0->img.nolist) { izahl++; }
    }
    imgp0 = *imgpp;
    if (imgp0->img.fname != NULL) {
      fprintf(outfp, "- %s (%d occurrences)\n", imgp0->img.fname, izahl);
    } else {
      fprintf(outfp, "- [%s] (%d occurrences)\n", key, izahl);
    }
    fprintf(outfp, "  width=%d, height=%d, is-readonly=%s, has-opaque-pixels=%s, is-clear=%s\n",
            imgp0->img.w, imgp0->img.h, (imgp0->img.isrdonly ? "yes" : "no"),
            (imgp0->img.hasopq ? "yes" : "no"), (imgp0->img.isclear ? "yes" : "no"));
  }
  fprintf(outfp, "\n");

  fprintf(outfp, "Readwrite:\n");
  if (vg4data.lists.image.list != NULL) {
    for (imgp0 = vg4data.lists.image.list->next; imgp0 != NULL; imgp0 = imgp0->next) {
      if (!imgp0->img.nolist) {
        fprintf(outfp, "- width=%d, height=%d, is-readonly=%s, has-opaque-pixels=%s, is-clear=%s\n",
                imgp0->img.w, imgp0->img.h, (imgp0->img.isrdonly ? "yes" : "no"),
                (imgp0->img.hasopq ? "yes" : "no"), (imgp0->img.isclear ? "yes" : "no"));
      }
    }
  }
  fprintf(outfp, "\n");
} /* Ende image_dump */
