/**
 * ChoX11: XLib replacement for RISC OS
 *
 * Image handling functions
 *
 * Copyright 2003 by Peter Naulls
 * Written by Peter Naulls and Chris Williams
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation. No representations are made about the suitability of this
 * software for any purpose.  It is provided "as is" without express or
 * implied warranty.
 *
 */


#define XLIB_ILLEGAL_ACCESS
#define NeedFunctionPrototypes 1
#define NeedNestedPrototypes   1

#include <X11/Xlib.h>
#include <X11/Xutil.h> 

#include <stdio.h>
#include <string.h>
#include <kernel.h>

#include "chox11.h"

/**
 * Keep track of Images so obdata and data can be freed on XFree
 */
static XImage **Chox11_ImageList = NULL;
static int Chox11_ImageNum = 0, Chox11_ImageMax = 0;


#define Chox11_SpriteSize(width, height, depth) \
           (sizeof(sprite_areainfo) + \
            Sprite_MemorySizeBpp(width, height, depth, sprite_HASNOMASKPAL))


static screen_modeval Chox11Image_SpriteModeForDepth(unsigned int depth) {
  screen_modeval mode;

  mode.sprite_mode.istype   = 1;
  mode.sprite_mode.horz_dpi = 90;
  mode.sprite_mode.vert_dpi = 90;

  switch(depth) {
    case  1: mode.sprite_mode.type = 1; break;
    case  2: mode.sprite_mode.type = 2; break;
    case  4: mode.sprite_mode.type = 3; break;
    case  8: mode.sprite_mode.type = 4; break;
    case 16: mode.sprite_mode.type = 5; break;
    case 32: mode.sprite_mode.type = 6; break;
    default:
      printf("XImage/Pixmap: unsupported depth %d\n", depth);
      mode.sprite_mode.type = 6;
      break;
  }

  return mode;
}

static int Chox11Image_DepthForSpriteMode(screen_modeval *mode) {
  if (mode->sprite_mode.istype == 0) {
    puts("Invalid sprite mode type\n");
    return 32;
  }

  return (1 << (mode->sprite_mode.type - 1));
}


/**
 * Number of bytes used on a given sprite.  This is rounded up to words
 */
static int Chox11Image_SpriteBytesPerLine(int bpp, int width) {
  if (bpp == 32 || bpp == 8) {
    return ((width * (bpp >> 3)) + 3) & ~0x3;

  } else if (bpp == 1) { /* Assume 1 bpp */
    return (((width + 7) / 8) + 3) & ~0x3; 

  } else {
    printf("unsupported bpp = %d\n", bpp);
    abort();
  }
}


#undef XDestroyImage
XDestroyImage(XImage *image)
{
  int item = Chox11_ImageNum;

  printf("XDestroyImage: %p\n", image);

  Fortify_CheckAllMemory();

  while (--item >= 0) {
    if (Chox11_ImageList[item] == image) {

      memmove(Chox11_ImageList + item, Chox11_ImageList + item + 1,
              (Chox11_ImageNum - item - 1) * sizeof(XImage));

      Chox11_ImageNum -= 1;

      XFreePixmap(NULL, (Pixmap)image->obdata);
      image->obdata = NULL;
  Fortify_CheckAllMemory();

      printf("free data: %p\n", image->data);
      free(image->data);
      image->data = NULL;
      break;
    }
  }

  free(image);
} 


/*
 * Free all images at exit to aid looking for memory leaks
 */
void Chox11Image_FreeImages(void) {
  int images = Chox11_ImageNum; 

  while (images) {
    XDestroyImage(Chox11_ImageList[--images]);
  }
}


/**
 * Following two functions are hard-coded for 32 bit image depth
 */
static unsigned long Chox11ZPixmap_XGetPixel32(XImage *image, int x, int y) {
  return image->data[image->xoffset + x * 4 + y * image->bytes_per_line];
}

static Chox11ZPixmap_XPutPixel32(XImage *image, int x, int y, unsigned long pixel) {
  Fortify_CheckAllMemory();
  image->data[image->xoffset + x * 4 + y * image->bytes_per_line] = pixel;
  Fortify_CheckAllMemory();
}

/**
 * Following two functions are hard-coded for 1 bit image depth
 */
static unsigned long Chox11ZPixmap_XGetPixel1(XImage *image, int x, int y) {
  return ((image->data[((image->xoffset + x) >> 3) + y * image->bytes_per_line] & (1 << ((image->xoffset+x)&7)))!=0);
}

static Chox11ZPixmap_XPutPixel1(XImage *image, int x, int y, unsigned long pixel) {
  char *pos = &image->data[((image->xoffset + x) >> 3) + y * image->bytes_per_line];
  int shift = (1 << ( (image->xoffset+x)&7 ) );

  Fortify_CheckAllMemory();
  if (pixel)
    *pos |= shift;
  else
    *pos &=~shift;
  Fortify_CheckAllMemory();
}


/**
 * For unsupported depths
 */
static unsigned long Chox11ZPixmap_XGetPixel(XImage *image, int x, int y) {
  printf("ZGetPixel: XYBitmap format not supported\n");
  return 0;
}

static Chox11ZPixmap_XPutPixel(XImage *image, int x, int y, unsigned long pixel) {
  printf("ZPutPixel: XYBitmap format not supported\n");
}


static unsigned long Chox11XYPixmap_XGetPixel(XImage *image, int x, int y) {
   unsigned char *red_data = image->data + image->xoffset + x + y * image->width;
   unsigned char *green_data = red_data + image->width * image->height;
   unsigned char *blue_data = green_data + image->width * image->height;

   return *red_data + ((int)(*green_data) << 8) + ((int)(*blue_data) << 16);
}


static Chox11XYPixmap_XPutPixel(XImage *image, int x, int y, unsigned long pixel) {
  unsigned char *red_data = image->data + image->xoffset + x + y * image->width;
  unsigned char *green_data = red_data + image->width * image->height;
  unsigned char *blue_data = green_data + image->width * image->height;

  Fortify_CheckAllMemory();
  *red_data = pixel & 0xFF;
  *green_data = (pixel & 0xFF00) >> 8;
  *blue_data = (pixel & 0xFF0000) >> 16;
  Fortify_CheckAllMemory();
}


static unsigned long Chox11XYBitmap_XGetPixel(XImage *image, int x, int y) {
  printf("XGetPixel: XYBitmap format not supported\n");

  return 0;
}


static Chox11XYBitmap_XPutPixel(XImage *image, int x, int y, unsigned long pixel) {
  printf("XSetPixel: XYBitmap format not supported\n");
}


#undef XSubImage
XImage *XSubImage(XImage *image, int x, int y,
                  unsigned int subimage_width,
                  unsigned int subimage_height)
{
  printf("XSubImage: %p %d %d %d %d (not implemented)\n",
         x, y, subimage_width, subimage_height);
  return NULL;                   
}


#undef XAddPixel
XAddPixel(XImage *image, long value)
{
  printf("XAddPixel: %p %u (not implemented)\n", image, value);
}

Status XInitImage(

    XImage*		 image

)
{
  image->f.create_image = XCreateImage;
  image->f.destroy_image = XDestroyImage;
  image->f.sub_image = XSubImage;
  image->f.add_pixel = XAddPixel;

  printf("XImageImage: %p\n", image);

  switch(image->format) {
    case ZPixmap:
      switch(image->bits_per_pixel) {
        case 32:
          image->f.get_pixel = Chox11ZPixmap_XGetPixel32;
          image->f.put_pixel = Chox11ZPixmap_XPutPixel32;
          break;

        case 1:
          image->f.get_pixel = Chox11ZPixmap_XGetPixel1;
          image->f.put_pixel = Chox11ZPixmap_XPutPixel1;
          break;

        default:
          printf("XInitImage - ZPixmap bits per pixel %d: not implemented\n", image->bits_per_pixel);
          image->f.get_pixel = Chox11ZPixmap_XGetPixel;
          image->f.put_pixel = Chox11ZPixmap_XPutPixel;
          break;
       }
       break;

     case XYPixmap:
       image->f.get_pixel = Chox11XYPixmap_XGetPixel;
       image->f.put_pixel = Chox11XYPixmap_XPutPixel;
       break;

     case XYBitmap:
       image->f.get_pixel = Chox11XYBitmap_XGetPixel;
       image->f.put_pixel = Chox11XYBitmap_XPutPixel;
       break;
   }

   printf("XImageImage put: %p\n", image->f.put_pixel);

   /* Check to see if it's already in the list */
   {
     int check = Chox11_ImageNum;

     while (--check >= 0) {
       if (Chox11_ImageList[check] == image) {
         return Success;
       }
     }
   }

   if (!Chox11_ImageList) {
     Chox11_ImageList = malloc(sizeof(XImage *) * 10);
     Chox11_ImageMax = 10;
   }

   if (++Chox11_ImageNum == Chox11_ImageMax) {
     Chox11_ImageMax *= 2;
     Chox11_ImageList = realloc(Chox11_ImageList, sizeof(XImage *) * Chox11_ImageMax);
   }
   Chox11_ImageList[Chox11_ImageNum - 1] = image;

   return Success;
}


XImage *XCreateImage(

    Display*		 display,
    Visual*		 visual,
    unsigned int	 depth,
    int			 format,
    int			 offset,
    char*		 data,
    unsigned int	 width,
    unsigned int	 height,
    int			 bitmap_pad,
    int			 bytes_per_line

) {
  XImage *image = calloc(1, sizeof(XImage));
  int size;
  screen_modeval mode;

/* Drawable is just used for transfer of image to screen/pixmap */
  XDrawable *bitmap = calloc(sizeof(XDrawable) + (size =
                             Chox11_SpriteSize(width, height, depth)), 1);
  /* TODO: handle memory failure */

  printf("XCreateImage: %dx%d, %d bpp\n", width, height, depth);

  bitmap->isWindow = False;

  bitmap->data.image.pixmap.areasize    = size;
  bitmap->data.image.pixmap.numsprites  = 0;
  bitmap->data.image.pixmap.firstoffset = 16;
  bitmap->data.image.pixmap.freeoffset  = 16;
  bitmap->width = width;
  bitmap->height = height;

  mode = Chox11Image_SpriteModeForDepth(depth);

  Sprite_Create(&bitmap->data.image.pixmap, X_SPRITE_NAME, 0, width, height,
                                                          mode);
  image->obdata = (XPointer)bitmap;
  image->data = data;

  image->width = width;
  image->height = height;
  image->format = format;
  image->bitmap_pad = bitmap_pad;
  image->bits_per_pixel = depth;
  image->xoffset = offset;

  image->byte_order = LSBFirst;

  if (bytes_per_line == 0) {
    int bits_per_line = depth * width;
    int last_bits = bits_per_line % bitmap_pad;

    if (last_bits) bits_per_line += bitmap_pad - last_bits;
    image->bytes_per_line = bits_per_line/8;
  } else {
    image->bytes_per_line = bytes_per_line;
  }

  image->bitmap_bit_order = LSBFirst;
  image->bitmap_unit = depth;

  if (format == ZPixmap) {
    image->red_mask =   0x000000ff;
    image->green_mask = 0x0000ff00;
    image->blue_mask =  0x00ff0000;
  } else {
    image->red_mask = 0;
    image->green_mask = 0;
    image->blue_mask = 0;
  }

  XInitImage(image);

  /* Note: The image data is placed into the sprite in XPutImage */

  printf("XCreateImage: Returning image: %p\n", image);

  return image;
}


XImage *XGetImage(

    Display*		 display,
    Drawable		 d,
    int			 x,
    int			 y,
    unsigned int	 width,
    unsigned int	 height,
    unsigned long	 plane_mask,
    int			 format

) {
  XDrawable *draw = (XDrawable *)d;
  XDrawable *bitmap;
  window_handle handle;
  window_info info;
  int screen_x, screen_y, size, plot_x, plot_y;
  sprite_areainfo /**spritedata,*/ *sourcesprite;
  XImage *image;
  sprite ret_sprite;

  printf("XGetImage: d=%x x=%i y=%i width=%i height=%i\n",
         d, x, y, width, height);

  if (Chox11_FindDrawable(draw) == False) {
    printf("XGetImage: Returning BadDrawable!\n");
    return (XImage *)BadDrawable;
  } else {
    printf("XGetImage: Verified drawable in list\n");
  }                             

  /* TODO: handle memory failure */
  image = calloc(1, sizeof(XImage));

   /* Ensure source is up to date */
   Chox11Queue_EmptyFor(draw);

  if (draw->isWindow) {
    int bw = draw->data.window.border_width;

    /* Screen is always fetched as a 32 bpp image */
    image->bits_per_pixel = 32;
    bitmap = calloc(sizeof(XDrawable) + (size =
                            Chox11_SpriteSize(width, height, 32)), 1);

    bitmap->isWindow = False;
    bitmap->width = width;
    bitmap->height = height;
    bitmap->data.image.pixmap.areasize    = size;
    bitmap->data.image.pixmap.numsprites  = 0;
    bitmap->data.image.pixmap.firstoffset = 16;
    bitmap->data.image.pixmap.freeoffset  = 16;

    handle = draw->data.window.handle;
    printf("XGetImage: getting image from window, handle %i\n", handle);
    Window_GetInfo3(handle, &info);

    screen_x = (x << screen_eig.x) - info.block.scroll.x + info.block.screenrect.min.x + bw;
    screen_y = (y << screen_eig.y) - info.block.scroll.y + info.block.screenrect.max.y - bw;
    
    printf("XGetImage: scroll %d,%d min %d,%d\n", info.block.scroll.x,info.block.scroll.y,info.block.screenrect.min.x ,info.block.screenrect.max.y);
    printf("XGetImage: grabbing rectangle %i,%i,%i,%i\n",
           screen_x, screen_y - ((height-1) << screen_eig.y),
               screen_x + ((width - 1) << screen_eig.x), screen_y);

    if (screen_bpp == 32) {
      os_error *err = Sprite_Get(&bitmap->data.image.pixmap, X_SPRITE_NAME, 0,
               screen_x, screen_y - ((height-1) << screen_eig.y),
               screen_x + ((width - 1) << screen_eig.x), screen_y, &ret_sprite);

      if (err != NULL) printf("XGetImage: **SG Failed %s\n", err->errmess);
    } else {
      /* Need to get it from current mode and then convert it */
      sprite_areainfo *temp_sprite = malloc(size = sizeof(sprite_areainfo) +
           Sprite_MemorySizeBpp(width, height, screen_bpp, (screen_bpp == 16) ? sprite_HASNOMASKPAL : sprite_HASPAL));
      os_error *err;

      temp_sprite->areasize    = size;
      temp_sprite->numsprites  = 0;
      temp_sprite->firstoffset = 16;
      temp_sprite->freeoffset  = 16;

      err = Sprite_Get(temp_sprite, X_SPRITE_NAME, 0,
             screen_x, screen_y - ((height-1) << screen_eig.y),
             screen_x + ((width - 1) << screen_eig.x), screen_y, &ret_sprite);

      if (err != NULL) {
        printf("XGetImage: **SG Failed %s\n", err->errmess);
      } else {
        screen_modeval mode = Chox11Image_SpriteModeForDepth(32);

        err = Sprite_Create(&bitmap->data.image.pixmap, X_SPRITE_NAME, 0, width, height, mode);
        if (err != 0) {
          printf("XGetImage: **Sprite_Create Failed %s\n", err->errmess);
        } else {
          sprite_outputstate previous;

          err = Sprite_Redirect(&bitmap->data.image.pixmap, X_SPRITE_NAME, NULL, &previous);
          if (err != 0)
            printf("XGetImage: redirect failed %s\n", err->errmess);
          Sprite_Plot(temp_sprite, X_SPRITE_NAME, 0, 0, 0);
          Sprite_UnRedirect(&previous);
        }
        free(temp_sprite);
      }
    }
  } else {
    sprite_outputstate previous;
    sprite_info info;
    os_error *err;

    /* We have to copy a rectangle of one sprite as a new sprite.
       First we redirect screen output to a target sprite. Then we'll create
       a graphics window to clip to the required size,
       i.e.: the clipping window's co-ords are 0,0,width,height
       Then plot the source sprite at 0 - x, 0 - ( y - height)
       so the required rectangle gets clipped by the graphics window and
       becomes the target sprite. */

    printf("XGetImage: getting image from pixmap\n");

    sourcesprite = &draw->data.image.pixmap;
    Sprite_ReadInfo(sourcesprite, X_SPRITE_NAME, &info);

    image->bits_per_pixel = Chox11Image_DepthForSpriteMode(&info.mode);

    /* Image must match format of pixmap */
    bitmap = calloc(sizeof(XDrawable) + (size =
                            Chox11_SpriteSize(width, height, image->bits_per_pixel)), 1);

    bitmap->isWindow = False;
    bitmap->width = width;
    bitmap->height = height;
    bitmap->data.image.pixmap.areasize    = size;
    bitmap->data.image.pixmap.numsprites  = 0;
    bitmap->data.image.pixmap.firstoffset = 16;
    bitmap->data.image.pixmap.freeoffset  = 16;

    err = Sprite_Create(&bitmap->data.image.pixmap, X_SPRITE_NAME, 0, width, height, info.mode);

    if (err != 0) {
      printf("XGetImage: **Sprite_Create Failed %s\n", err->errmess);

    } else {
      err = Sprite_Redirect(&bitmap->data.image.pixmap, X_SPRITE_NAME, NULL, &previous);

      if (err != 0) {
        printf("XGetImage: redirect failed %s\n", err->errmess);
 
      } else {
 
        /* Set origin to 0,0 */
        VDU(29); VDU(0); VDU(0); VDU(0); VDU(0);
    
        /* Define clipping rectangle as x0,y0 x1,y1 (VDU wants bytes, so BBC) */
        VDU(24);
        VDU(0); VDU(0);
        VDU(0); VDU(0);
        /* Note: width and height are also converted to os units */
        VDU((width << 1) & 0xff);  VDU((width >> (8-1)) & 0xff);
        VDU((height << 1) & 0xff); VDU((height >> (8-1)) & 0xff);
    
        /* Plot source sprite */
        plot_x = 0 - x;
        plot_y = -info.height + (y + height);
        Sprite_Plot(sourcesprite, X_SPRITE_NAME, plot_x, plot_y, 0);

        /* Reset graphics window, end redirection */
        VDU(26);
        Sprite_UnRedirect(&previous);
      }
    }
  }

  printf("XGetImage: Packaging up XImage structure\n");
  /* Package new sprite into XImage structure */
  image->obdata = (XPointer)bitmap;

  image->width = width;
  image->height = height;
  image->format = format;
  image->bitmap_pad = 32;
  image->xoffset = 0;

  image->byte_order = LSBFirst;
  image->bytes_per_line = (image->bits_per_pixel * width)/8;
  if ((image->bits_per_pixel * width) & 31) image->bytes_per_line = (image->bytes_per_line & ~3) + 4;

  image->bitmap_bit_order = LSBFirst;
  image->bitmap_unit = image->bits_per_pixel;

  /* Put sprite data into image */
  {
     char *sprite_data = (char *)&((XDrawable *)(image->obdata))->data.image.pixmap +
                         sizeof(sprite_areainfo) + sizeof(sprite_header);

     switch (format) {
       case ZPixmap:
        {
          char *data = image->data = malloc(image->bytes_per_line * height);
          int line;
          int bpl =  image->bytes_per_line;

          for (line = 0; line < height; line++) {
            memcpy(data, sprite_data, bpl);
            data += bpl;
            sprite_data += bpl;
          }
        }
        break;
        
      case XYPixmap:
        {
           char *data_red = image->data = malloc(3 * width * height);
           char *data_green = image->data + width * height;
           char *data_blue = data_green + width * height;
           int row, col;
           int *sprite_pixel;

           for (row = 0; row < height; row++) {
             sprite_pixel = (int *)sprite_data;

             for (col = 0; col < width; col++) {
               *data_red++ = (sprite_pixel[col] & 0xFF);
               *data_green++ = ((sprite_pixel[col] & 0xFF00) >> 8);
               *data_blue++ = ((sprite_pixel[col] & 0xFF0000) >> 16);
             }
             sprite_data += image->bytes_per_line;
           }
         }
         break;

      default:
         printf("XGetImage: format unsupported\n");
         break;
     }
  }

  XInitImage(image);

  Fortify_CheckAllMemory();
  printf("XGetImage: Returning image at %p %p\n", image, image->data);

  return image;
}


XImage *XGetSubImage(

    Display*		 display,
    Drawable		 d,
    int			 x,
    int			 y,
    unsigned int	 width,
    unsigned int	 height,
    unsigned long	 plane_mask,
    int			 format,
    XImage*		 dest_image,
    int			 dest_x,
    int			 dest_y

);


XPutImage(

    Display*		 display,
    Drawable		 d,
    GC			 gc,
    XImage*		 image,
    int			 src_x,
    int			 src_y,
    int			 dest_x,
    int			 dest_y,
    unsigned int	 width,
    unsigned int	 height  

) {
     char *data = image->data + image->xoffset;
     char *sprite_data = (char *)&((XDrawable *)(image->obdata))->data.image.pixmap +
                         sizeof(sprite_areainfo) + sizeof(sprite_header);

  printf("XPutImage: image = %p data = %p d = %p\n", image, image->data, d); fflush(stdout);
Fortify_CheckAllMemory();

/* Copy data into sprite area for transfer */

  switch (image->format) {
    case ZPixmap:
      {
        int line;
        int bpl = image->bytes_per_line;
        int bpsl = Chox11Image_SpriteBytesPerLine(image->bits_per_pixel, image->width);

        for (line = 0; line < image->height; line++) {
          if (image->bitmap_bit_order == MSBFirst || image->byte_order == MSBFirst) {
            int byte;

            /* There might be a faster way of doing this */
            for (byte = 0; byte < bpl; byte++) {
              int input;

              if (image->byte_order == MSBFirst && image->bitmap_unit == 32)
                input = data[(byte & ~0x3) + (3 - (byte & 0x3))];
              else
                input = data[byte];

              if (image->bitmap_bit_order == MSBFirst) {
                int val = 0, bit;
  
                for (bit = 0; bit < 8; bit++) {
                  if (input & (1 << bit)) {
                    val |= 1 << (7 - bit);
                  }
                }
                sprite_data[byte] = val;

              } else {
                sprite_data[byte] = input;
              }
            }
          } else {
            memcpy(sprite_data, data, bpl);
          }
          sprite_data += bpsl;
          data += bpl;
        }
      }
      break;

    case XYPixmap:
      {
        unsigned char *red_data = (unsigned char *)data;
        unsigned char *green_data = red_data + image->width * image->height;
        unsigned char *blue_data  = green_data + image->width * image->height;

        int row, col;
        int *sprite_pixel;
        int bpsl = Chox11Image_SpriteBytesPerLine(image->bits_per_pixel, image->width);

        for (row = 0; row < image->height; row++) {
          sprite_pixel = (int *)sprite_data;

          for (col = 0; col < image->width; col++) {
            sprite_pixel[col] = *red_data++ + ((int)(*green_data++) << 8) + ((int)(*blue_data++) << 16);
          }
          sprite_data += bpsl;
        }
      }
      break;

    case XYBitmap:
        printf("XPutImage: XYBitmap format not implemented\n");
        break;
  }

Fortify_CheckAllMemory();
  XCopyArea(display, (Drawable)image->obdata, d,
            gc, src_x, src_y, width, height, dest_x, dest_y);
Fortify_CheckAllMemory();
}


extern Pixmap XCreateBitmapFromData(

    Display*		 display,
    Drawable		 d,
    _Xconst char*	 data,
    unsigned int	 width,
    unsigned int	 height

) {
  int size;
  screen_modeval mode;
  XDrawable *pixmap = calloc(sizeof(XDrawable) + (size =
                            Chox11_SpriteSize(width, height, screen_bpp)), 1);
  char *bitmap = (char *)&pixmap->data.image.pixmap +
                   sizeof(sprite_areainfo) + sizeof(sprite_header);

  printf("XCreateBitmapFromData: %d %d\n", width, height);

  pixmap->isWindow = False;
  pixmap->width  = width;
  pixmap->height = height;

  pixmap->data.image.pixmap.areasize    = size;
  pixmap->data.image.pixmap.numsprites  = 0;
  pixmap->data.image.pixmap.firstoffset = 16;
  pixmap->data.image.pixmap.freeoffset  = 16;

  mode = Chox11Image_SpriteModeForDepth(1);

  Sprite_Create(&pixmap->data.image.pixmap, X_SPRITE_NAME, 0, width, height,
                mode);

  {
    int align;
    int x, y;

    width = (width + 7) >> 3;     /* Round to nearest byte */
    align = (width + 3) & ~0x03;  /* Round to nearest word */

    for (y = 0; y < height; y++) {
      for (x = 0; x < width; x++) {
        bitmap[y * align + x] = data[y * width + x];
      }
    }
  }

  Chox11_AddDrawable(pixmap);

  return (Pixmap)pixmap;
}


extern Pixmap XCreatePixmap(

    Display*		 display,
    Drawable		 d,
    unsigned int	 width,
    unsigned int	 height,
    unsigned int	 depth

) {
  int size;
  XDrawable *pixmap = calloc(sizeof(XDrawable) + (size= Chox11_SpriteSize(width, height, depth)),1);
  /* TODO: handle memory failure */

  printf("XCreatePixmap w = %d h = %d depth = %d size = %d\n",
                                          width, height, depth, size);

  pixmap->isWindow = False;
  pixmap->width = width;
  pixmap->height = height;

  /* Initialise sprite area */
  pixmap->data.image.pixmap.areasize     = size;
  pixmap->data.image.pixmap.freeoffset   = 16;
  pixmap->data.image.pixmap.firstoffset  = 16;
  Sprite_InitArea(&pixmap->data.image.pixmap);

  Sprite_Create(&pixmap->data.image.pixmap, X_SPRITE_NAME, 0, width, height,
                Chox11Image_SpriteModeForDepth(depth));

  Chox11_AddDrawable(pixmap);

  printf("Pixmap created: pixmap ptr = %p\n", pixmap); fflush(stdout);
  Fortify_CheckAllMemory();

  return (Pixmap)pixmap;
}


extern Pixmap XCreatePixmapFromBitmapData(

    Display*		 display,
    Drawable		 d,
    char*		 data,
    unsigned int	 width,
    unsigned int	 height,
    unsigned long	 fg,
    unsigned long	 bg,
    unsigned int	 depth

) {  
  unsigned int x, y, pixel;
  int current_bit;
  sprite_outputstate old_state;

  /* Create empty pixmap */
  Pixmap pixmap = XCreatePixmap(display, d, width, height, depth);
  XDrawable *pix = (XDrawable *)pixmap;
  
  printf("XCreatePixmapFromBitmapData: data=%p, w/h=%i/%i drawable=%x "
         "depth=%i, fg=%08X, bg%=%08X, returning rid=%x\n",
         data, width, height, d, depth, fg, bg, pixmap);
            
  /* Redirect graphics ops to the pixmap sprite, plot the pixmap,
     end redirection */
  Sprite_Redirect(&pix->data.image.pixmap, X_SPRITE_NAME, NULL, &old_state);

  current_bit = 0;
  for (y = height; y >  0; y--) {
    for (x = 0; x < width; x++) {
      pixel = *data & (1 << current_bit);
      if ((++current_bit & 7) == 0) {
        data++;
        current_bit = 0;
      }
        
      GFX_SetColour(0, pixel ? fg : bg);
      GFX_PlotPoint(x<<1, (y - 1)<<1);
    }
  }
  /* Set output back to screen */
  Sprite_UnRedirect(&old_state);

  /* Clean up, exit */
  return pixmap;
}


extern XFreePixmap(

    Display*		 display,
    Pixmap		 pixmap

) {
  XDrawable *pix = (XDrawable *)pixmap;

  printf("XFreePixmap: %x\n", pixmap);

  if (Chox11_RemoveDrawable(pix)) {
    if (pix->gcinfo.clip_sprite) free(pix->gcinfo.clip_sprite);
    if (pix->gcinfo.clip_rects != NULL) Chox11ClipRects_Release(pix->gcinfo.clip_rects);
    free(pix);
  }
}


/**
 * Create a sprite to use for clipping from a single bpp depth pixmap
 */
sprite_area Chox11Image_CreateClipSprite(
  Pixmap clip_mask
) {
  XDrawable *pix = (XDrawable *)clip_mask;
  sprite_area mask_area = &(pix->data.image.pixmap);
  sprite_area clip_sprite = NULL;
  int size = (sizeof(sprite_areainfo) +
       Sprite_MemorySizeBpp(pix->width, pix->height, 32, sprite_HASMASK));

  clip_sprite = malloc(size);

  if (clip_sprite != NULL) {
    sprite_outputstate old_state;

    clip_sprite->areasize     = size;
    clip_sprite->freeoffset   = 16;
    clip_sprite->firstoffset  = 16;
    Sprite_InitArea(clip_sprite);

    Sprite_Create(clip_sprite, X_SPRITE_NAME, 0, pix->width, pix->height,
          Chox11Image_SpriteModeForDepth(32));

    Sprite_CreateMask(clip_sprite, X_SPRITE_NAME);

    /* Switch output to mask */
    Sprite_RedirectMask(clip_sprite, X_SPRITE_NAME, NULL, &old_state);

    Sprite_Plot(mask_area, X_SPRITE_NAME, 0, 0, 0);
    Sprite_UnRedirect(&old_state);
  }

  return clip_sprite;
}
