OZ++ Class: Image
/******************************************************************************
 *
 * Copyright (c) 2014 Antillia.com TOSHIYUKI ARAI. ALL RIGHTS RESERVED.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer.
 *
 * 2. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 *      Image.h
 *
 *****************************************************************************/
//2015/05/14 Updated

#pragma once

#include <Xm/Xm.h>
#include <oz++/Exception.h>
#include <oz++/motif/Resource.h>

//Simple Image class to represend XImage structure 
/*
typedef struct _XImage {
    int width, height;        // size of image 
    int xoffset;            // number of pixels offset in X direction 
    int format;            // XYBitmap, XYPixmap, ZPixmap 
    char *data;            // pointer to image data 
    int byte_order;            // data byte order, LSBFirst, MSBFirst 
    int bitmap_unit;        // quant. of scanline 8, 16, 32 
    int bitmap_bit_order;        // LSBFirst, MSBFirst 
    int bitmap_pad;            // 8, 16, 32 either XY or ZPixmap 
    int depth;            // depth of image 
    int bytes_per_line;        // accelerator to next scanline 
    int bits_per_pixel;        // bits per pixel (ZPixmap) 
    unsigned long red_mask;        // bits in z arrangement 
    unsigned long green_mask;
    unsigned long blue_mask;
    XPointer obdata;        // hook for the object routines to hang on 
    struct funcs {            // image manipulation routines 
        struct _XImage *(*create_image)();
        int (*destroy_image)();
        unsigned long (*get_pixel)();
        int (*put_pixel)();
        struct _XImage *(*sub_image)();
        int (*add_pixel)();
    } f;
} XImage;
 */

namespace OZ {

class Image :public Resource {
private:
  Screen* screen;

public:
  Image(Display* display, unsigned int depth,
    int format, int offset, char* data, unsigned int width,
    unsigned int height, int pad, int bytes)
    :Resource(display)
  {
    screen  = XDefaultScreenOfDisplay(display);

    XImage* image = XCreateImage(display,
         DefaultVisualOfScreen(screen),
         depth, format, offset, data,
         width, height, pad, bytes);
    if (image) {
      Status status = initialize(image);
      if (status != 0) { 
        image -> byte_order = getByteOrder();
        set(image);
 
      } else {
       throw IException("Failed to XInitImage");
      }
    } else {
       throw IException("Failed to XCreateImage");
    }
  }

  Image(Display* display, Window window,
       int x, int y, unsigned int width, unsigned int height,
    unsigned long mask, int format)
    :Resource(display)
  {
    screen  = XDefaultScreenOfDisplay(display);
    set(XGetImage(display, window,
        x, y, width, height, mask, format));
  }

  ~Image()
  {
    XDestroyImage(get());
  }

  int getByteOrder () 
  {
    union {
      char c[sizeof(short)];
      short s;
    } order;

    order.s = 1;

    if ((1 == order.c[0])) {
      return LSBFirst;
    } else {
      return MSBFirst;
    }
  }

  Status initialize(XImage* ximage)
  {
     Status status = 0;
     if (ximage) {
       status = XInitImage(ximage);
     }
     return status;
  }

  XImage* get()
  {
    XImage* ximage = (XImage*)Resource::get();
    if (ximage) {
      return ximage;
    } else {
      throw IException("XImage is NULL.");
    }
  }

  /*
   See https://hamberg.no/erlend/posts/2011-01-06-read-current-x-background-to-jpeg.html
  */

  //Your have to delete the returned RGB data after its use.
  unsigned char* getRGBData()
  {
    XImage* image = get();
    int width  = image -> width;
    int height = image -> height;

    size_t bsize = sizeof(char) * 3 * width * height;

    unsigned char* rgb = new unsigned char[bsize];

    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            unsigned long pixel = XGetPixel(image, x, y);
            rgb[ y * width *3 + x * 3 + 0] = (char)(pixel >> 16);
            rgb[ y * width *3 + x * 3 + 1] = (char)((pixel & 0x00ff00)>>8);
            rgb[ y * width *3 + x * 3 + 2] = (char)(pixel & 0x0000ff);
        }
    }
    return rgb;
  }

  //Your have to delete the returned RGB data after its use.
  unsigned char* getBGRData()
  {
    XImage* image = get();
    int width  = image -> width;
    int height = image -> height;

    size_t bsize = sizeof(char) * 3 * width * height;

    unsigned char* rgb = new unsigned char[bsize];

    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            unsigned long pixel = XGetPixel(image, x, y);
            rgb[ y * width *3 + x * 3 + 0] = (char)(pixel & 0x0000ff);
            rgb[ y * width *3 + x * 3 + 1] = (char)((pixel & 0x00ff00)>>8);
            rgb[ y * width *3 + x * 3 + 2] = (char)(pixel >> 16);
        }
    }
    return rgb;
  }
   
  int  getDepth()
  {
    return get()->depth;
  }

  int  getWidth()
  {
    return get()->width;
  }

  int  getHeight()
  {
    return get()->height;
  }

  int getFormat()
  {
     return get()->format;      // XYBitmap, XYPixmap, ZPixmap
  }

  int getImageByteOrder()
  {
     return get()->byte_order;  // data byte order, LSBFirst, MSBFirst
  }

  int getBitmapUnit()
  {   
    return get()->bitmap_unit;   // quant. of scanline 8, 16, 32
  }

  int getBitmapBitOrder()
  {
    return get()->bitmap_bit_order;  // LSBFirst, MSBFirst
  }

  int getBitmapPad()
  {
    return get()->bitmap_pad;        // 8, 16, 32 either XY or ZPixmap
  }

  int getBytesPerLine()
  {
     return get()-> bytes_per_line;  // accelerator to next scanline
  }
  int getBitsPerPixel()
  {
     return get()->bits_per_pixel;    
  }

  char*  getData()
  {
    return get()->data;
  }

  unsigned long getPixel(int x, int y)
  {
    return XGetPixel(get(), x, y);
  }
  
  void putPixel(int x, int y, unsigned long pixel)
  {
    XPutPixel(get(), x, y, pixel);
  }
  
  XImage* subImage(int x, int y, unsigned int subimage_width, 
       unsigned int subimage_height)
  {
    return XSubImage(get(), x, y, subimage_width, subimage_height);
  }
  
  void  addPixel(long value)
  {
    XAddPixel(get(), value);
  }

};

}