/*
 * Copyright (c) 1987, 1988, 1989, 1990, 1991 Stanford University
 * Copyright (c) 1991 Silicon Graphics, Inc.
 *
 * Permission to use, copy, modify, distribute, and sell this software and 
 * its documentation for any purpose is hereby granted without fee, provided
 * that (i) the above copyright notices and this permission notice appear in
 * all copies of the software and related documentation, and (ii) the names of
 * Stanford and Silicon Graphics may not be used in any advertising or
 * publicity relating to the software without the specific, prior written
 * permission of Stanford and Silicon Graphics.
 * 
 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
 *
 * IN NO EVENT SHALL STANFORD OR SILICON GRAPHICS BE LIABLE FOR
 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
 * OF THIS SOFTWARE.
 */

/*
 * X11-dependent raster code
 */

#include <stdlib.h>
#include <iostream.h>
#include <string.h>

#include <InterViews/canvas.h>
#include <InterViews/display.h>
#include <InterViews/raster.h>
#include <InterViews/session.h>
#include <IV-X11/Xlib.h>
#include <IV-X11/Xutil.h>
#include <IV-X11/xdisplay.h>
#include <IV-X11/xraster.h>
#include <IV-X11/xwindow.h>
#include <IV-X11/harcolors.h>
#include <OS/math.h>
#include <OS/memory.h>

// ***********************************************************************

static int image_byte_order;

// ***********************************************************************
// bmarsch: 10 Nov 94
// initialization of fs dithering (copied from xv)

static int    tbl1[512],     /* tables used in F-S Dithering */
              tbl3[512],     /* contain i/16, 3i/16, 5i/16, 7i/16, */
              tbl5[512],     /* (i=-256..255) respectively */
              tbl7[512];

static void initDither()
{
  /* initialize Floyd-Steinberg division tables */
  /* make sure rounding is done correctly for negative values! */

  int i;

  for (i = -256; i < 0; i++) {
    tbl1[i+256] = -((8 -i  )/16);
    tbl3[i+256] = -((8 -3*i)/16);
    tbl5[i+256] = -((8 -5*i)/16);
    tbl7[i+256] = -((8 -7*i)/16);
  }

  for (i = 0; i < 256; i++) {
    tbl1[i+256] = (i  +8)/16;
    tbl3[i+256] = (3*i+8)/16;
    tbl5[i+256] = (5*i+8)/16;
    tbl7[i+256] = (7*i+8)/16;
  }
}

// ***********************************************************************
// bmarsch: 14 Nov 94
// allocating colors (copied from xv)

#define N256 255
static unsigned short cmaptab[N256];

static unsigned char rmap[256], gmap[256], bmap[256];

#define NOPIX 0xffffffff    

static void allocateColors(XDisplay* xdpy, 
                           const HarColor* colors, int numcolors,
                           unsigned short* cmaptab,
                           XColormap cmap)
{
  static int done = 0;

  // only allocate colors once!
  if (done) return;
  done = 1;

  XColor defs[N256];
  XColor ctab[N256];
  long ri, gi, bi;

  unsigned long cols[266];

  int nfcols = 0;
  int p2alloc = 0;
  int p3alloc = 0;

  // int rwthistime = 0;  // never used


  /* FIRST PASS COLOR ALLOCATION:  
     for each color in the 'desired colormap', try to get it via
     XAllocColor().  If for any reason it fails, mark that pixel
     'unallocated' and worry about it later.  Repeat. */

  /* attempt to allocate first ncols entries in colormap 
     note: On displays with less than 8 bits per RGB gun, it's quite
     possible that different colors in the original picture will be
     mapped to the same color on the screen.  X does this for you
     silently.  However, this is not-desirable for this application, 
     because when I say 'allocate me 32 colors' I want it to allocate
     32 different colors, not 32 instances of the same 4 shades... */
  
  int i;
  for (i=0; i<numcolors; i++) 
    cols[i] = NOPIX;

  for (i=0; i<numcolors; i++) {
    defs[i].red   = colors[i].r;
    defs[i].green = colors[i].g;
    defs[i].blue  = colors[i].b;
    defs[i].flags = DoRed | DoGreen | DoBlue;
    if (XAllocColor(xdpy, cmap, &defs[i])) {
      cols[i] = defs[i].pixel;
      nfcols++;
    }

  }  /* FIRST PASS */

  if (nfcols < numcolors) {

    /* SECOND PASS COLOR ALLOCATION:
       Allocating 'exact' colors failed.  Now try to allocate 'closest'
       colors.

       Read entire X colormap (or first 256 entries) in from display.
       for each unallocated pixel, find the closest color that actually
       is in the X colormap.  Try to allocate that color (read only).
       If that fails, the THIRD PASS will deal with it */


    /* read entire colormap (or first 256 entries) into 'ctab' */
    int dc = N256;
    for (i=0; i<dc; i++) 
      ctab[i].pixel = (unsigned long) i;

    XQueryColors(xdpy, cmap, ctab, dc);

    for (i=0; i<numcolors ; i++)
      if (cols[i] == NOPIX) {  /* an unallocated pixel */
        int d, mdist, close;

        mdist = 100000;   
        close = -1;
        ri = colors[i].r; 
        gi = colors[i].g; 
        bi = colors[i].b; 
      
        for (int j=0; j<dc; j++) {
          d = Math::abs((int)ri - (int)(ctab[j].red)) +
          Math::abs((int)gi - (int)(ctab[j].green)) +
          Math::abs((int)bi - (int)(ctab[j].blue));
          if (d < mdist) { mdist = d; close = j; }
        }

        if (XAllocColor(xdpy, cmap, &ctab[close])) { 
          memcpy((char*)&defs[i], (char*)&ctab[close], sizeof(XColor));
          cols[i] = ctab[close].pixel; 
          p2alloc++;
        }
      }

    /* THIRD PASS COLOR ALLOCATION:
       We've alloc'ed all the colors we can.  Now, we have to map any
       remaining unalloced pixels into either A) the colors that we DID get
       (noglob), or B) the colors found in the X colormap */

    for (i=0; i<numcolors; i++) {
      if (cols[i] == NOPIX) {  /* an unallocated pixel */
        int d, mdist, close;

        mdist = 100000;
        close = -1;
        ri = colors[i].r; 
        gi = colors[i].g; 
        bi = colors[i].b; 
      
        for (int j=0; j<dc; j++) {
          d = Math::abs((int)ri - (int)(ctab[j].red)) +
          Math::abs((int)gi - (int)(ctab[j].green)) +
          Math::abs((int)bi - (int)(ctab[j].blue));
          if (d<mdist) { 
            mdist=d; close=j; 
          }
        }
        memcpy((char*)&defs[i], (char*)&ctab[close], sizeof(XColor));
        cols[i] = defs[i].pixel;
        p3alloc++;
      }
    }  /* THIRD PASS */

  } // 2nd/3rd pass ...

  for (int j=0; j<N256; j++)
    if (cols[j] != NOPIX) {
      cmaptab[j] = (unsigned short) cols[j];
    }
    else
      cmaptab[j] = 0;

  initDither();
}

#undef NOPIX
#undef N256

// ***********************************************************************

// bmarsch: 20 Dec 94: bit-masking table
static const int mask[] = {
  0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
};

// ***********************************************************************

Raster::Raster(unsigned long ww, unsigned long hh, boolean doDither) {
  unsigned int w = (unsigned int) ww;
  unsigned int h = (unsigned int) hh;
  RasterRep* r = new RasterRep;
  rep_ = r;
  Display* d = Session::instance()->default_display();
  r->display_ = d;
  r->modified_ = false;
  r->pwidth_ = w;
  r->pheight_ = h;
  r->width_ = d->to_coord(w);
  r->height_ = d->to_coord(h);
  r->left_ = 0;
  r->bottom_ = 0;
  r->right_ = r->width_;
  r->top_ = r->height_;
  DisplayRep* dr = r->display_->rep();
  XDisplay* dpy = dr->display_;
  WindowVisual* wvis = dr->default_visual_;

  // bmarsch: 09 Jan 95: create pixmap later, when it's really needed
  r->pixmap_valid_ = false;
//   r->pixmap_ = XCreatePixmap(
//     dpy, dr->root_, w, h, wvis->depth()
//   );
//  r->gc_ = XCreateGC(dpy, r->pixmap_, 0, nil);

  /* bmarsch: 10 Nov 94 */
  // initialize new members
  image_byte_order = ImageByteOrder(dpy);
// if (image_byte_order == LSBFirst)
// cerr << "image_byte_order = LSBFirst" << endl;
// else
// cerr << "image_byte_order = MSBFirst" << endl;
  r->trueColor_ = (dr->default_visual_->depth() == 24);
  r->data_ = new unsigned char[w*h*3];
  if (!r->trueColor_) {
//     const HgColor* hgColors = harmonyColors();
//     int numHgColors = numHarmonyColors();
    allocateColors(dpy, harmony_color, num_harmony_colors, cmaptab, wvis->colormap());
  }
  doDither_ = doDither;

  /* bmarsch: 10 Nov 94 */
  // when using XGetImage, we would have to take bitmap_pad into
  // account, when dithering the image; so we better allocate the
  // image using XCreateImage
  int pad = r->trueColor_ ? 32 : 8;
  r->image_ = XCreateImage(
    dpy,
    wvis->visual(),
    wvis->depth(),
    ZPixmap,
    0,         // offset
    nil,
    w, h,
    pad,
    0
  );

  // bmarsch: 20 Dec 94: transparency
  r->clip_data_ = nil;
  r->clip_valid_ = false;

  // bmarsch: 10 Jan 95: return data array
  r->data_rgba_ = nil;
}

Raster::Raster(const Raster& raster) {
  RasterRep* r = new RasterRep;
  rep_ = r;
  raster.flush();
  RasterRep& rr = *(raster.rep());
  r->display_ = rr.display_;
  r->modified_ = true;
  r->width_ = rr.width_;
  r->height_ = rr.height_;
  r->left_ = rr.left_;
  r->bottom_ = rr.bottom_;
  r->right_ = rr.right_;
  r->top_ = rr.top_;
  r->pwidth_ = rr.pwidth_;
  r->pheight_ = rr.pheight_;
  DisplayRep* dr = r->display_->rep();
  XDisplay* dpy = dr->display_;
  WindowVisual* wvis = dr->default_visual_;
  // bmarsch: 09 Jan 95: create pixmap when its needed
  r->pixmap_valid_ = false;
//   r->pixmap_ = XCreatePixmap(
//     dpy, dr->root_, r->pwidth_, r->pheight_, wvis->depth()
//   );
//   r->gc_ = XCreateGC(dpy, r->pixmap_, 0, nil);
//   XCopyArea(
//     dpy, rr.pixmap_, r->pixmap_, r->gc_,
//     0, 0, r->pwidth_, r->pheight_, 0, 0
//   );

  /* bmarsch: 10 Nov 94 */
  // initialize new members
  r->trueColor_ = rr.trueColor_;
  unsigned int len = r->pwidth_ * r->pheight_ * 3;
  r->data_ = new unsigned char[len];
  Memory::copy(rr.data_, r->data_, len);
  doDither_ = doDither_;

  /* bmarsch: 10 Nov 94 */
  // XCreateImage instead of XGetImage
  r->image_ = XCreateImage(
    dpy,
    wvis->visual(),
    wvis->depth(),
    ZPixmap,
    0,         // offset
    nil,
    r->pwidth_, r->pheight_,
    rr.image_->bitmap_pad,
    0
  );

  // bmarsch: 20 Dec 94
  r->clip_valid_ = rr.clip_valid_;
  if (rr.clip_data_) {
    int len = (r->pwidth_ * r->pheight_) >> 3;
    r->clip_data_ = new char[len];
    Memory::copy(rr.clip_data_, r->clip_data_, len);
    r->clip_ = XCreatePixmap(
      dpy, dr->root_, r->pwidth_, r->pheight_, wvis->depth()
    );
    XCopyArea(dpy, rr.clip_, r->clip_, r->gc_,
              0, 0, r->pwidth_, r->pheight_, 0, 0
    );
    r->bytes_per_line_ = rr.bytes_per_line_;
  }
  else
    r->clip_data_ = nil;

  // bmarsch: 10 Jan 95: return data array
  r->data_rgba_ = nil;
}

Raster::Raster(RasterRep* r) { rep_ = r; }

Raster::~Raster() {
  RasterRep* r = rep();
  XDisplay* dpy = r->display_->rep()->display_;
  if (r->pixmap_valid_) {
    XFreePixmap(dpy, r->pixmap_);
    XFreeGC(dpy, r->gc_);
  }
  XDestroyImage(r->image_);

  // bmarsch: 20 Dec 94: transparency
  delete[] r->clip_data_;
  if (r->clip_valid_)
    XFreePixmap(dpy, r->clip_);

  delete r;
}

Coord Raster::width() const { return rep()->width_; }
Coord Raster::height() const { return rep()->height_; }
unsigned long Raster::pwidth() const { return rep()->pwidth_; }
unsigned long Raster::pheight() const { return rep()->pheight_; }

Coord Raster::left_bearing() const { return -rep()->left_; }
Coord Raster::right_bearing() const { return rep()->right_; }
Coord Raster::ascent() const { return rep()->top_; }
Coord Raster::descent() const { return -rep()->bottom_; }

void Raster::peek(unsigned long x, unsigned long y,
                  ColorIntensity& red, ColorIntensity& green, ColorIntensity& blue,
                  float& alpha) const
{
  unsigned char r, g, b, al;
  peekChr (x, y, r, g, b, al);
  red = float(r) / 0xff;
  green = float(g) / 0xff;
  blue = float(b) / 0xff;
  alpha = float(al) / 0xff;
}

void Raster::peekChr (unsigned long x, unsigned long y,
                      unsigned char& red, unsigned char& green, unsigned char& blue,
                      unsigned char& alpha) const 
{
  RasterRep* r = rep_;
  unsigned int line = r->pheight_ - (unsigned int) y - 1;
  unsigned char* cptr = r->data_ + (line * r->pwidth_ + x) * 3;
  red = *cptr++;
  green = *cptr++;
  blue = *cptr;
  // get alpha channel
  if (r->clip_data_) {
    char* ptr = r->clip_data_ + line * r->bytes_per_line_ + (x >> 3);
    alpha = (*ptr & mask[x % 8]) ? 0xFF : 0;
  }
  else
    alpha = 0xFF;
}

void Raster::poke(unsigned long x, unsigned long y,
                  ColorIntensity r, ColorIntensity g, ColorIntensity b, float alpha)
{
  pokeChr (
    x, y,
    (unsigned char) (r * 0xff),
    (unsigned char) (g * 0xff),
    (unsigned char) (b * 0xff),
    (unsigned char) (alpha * 0xff)
  );
}


void Raster::pokeChr (unsigned long x, unsigned long y,
                      unsigned char red, unsigned char green, unsigned char blue,
                      unsigned char alpha)
{
  // static int i = 0;  // never used
  RasterRep* r = rep_;
//   if (x < 0 || x >= rep_->pwidth_) return;
//   if (y < 0 || y >= rep_->pheight_) return;
// InterViews' raster coordinates: x left-to-right, y bottom-to-top, both running from 0
// X11 and internal array: y top-to-bottom, "row major" order

// cerr << "xraster.C: poke at row " << y << ", column " << x << " rgb ("
//      << (int) red << ", " << (int) green << ", " << (int) blue << ")"
//      << ", alpha = " << int(alpha) << endl;

  unsigned int line = r->pheight_ - (unsigned int) y - 1;
  unsigned char* cptr = r->data_ + (line * r->pwidth_ + x) * 3;
  *cptr++ = red;
  *cptr++ = green;
  *cptr = blue;
  if (!alpha && r->clip_data_) {
    // set alpha channel
    char* ptr = r->clip_data_ + line * r->bytes_per_line_ + (x >> 3);
    *ptr &= ~mask[x % 8];
  }
  r->modified_ = true;
}

void Raster::flush() const {
  RasterRep* r = rep();
  if (r->modified_) {
    DisplayRep* dr = r->display_->rep();
    XDisplay* xdpy = dr->display_;

    // bmarsch 09 Jan 95: create pixmap now
    if (!r->pixmap_valid_) {
      r->pixmap_ = XCreatePixmap(xdpy, dr->root_,
                                 r->pwidth_, r->pheight_,
                                 dr->default_visual_->depth());
      r->gc_ = XCreateGC(xdpy, r->pixmap_, 0, nil);
      r->pixmap_valid_ = true;
    }

    if (r->trueColor_)
      r->image_->data = new char[r->pwidth_*r->pheight_*4];
    else
      r->image_->data = new char[r->pwidth_*r->pheight_];
    ditherImage();

    // bmarsch: 20 Dec 94: transparency
    if (r->clip_data_) {
      if (r->clip_valid_) XFreePixmap(xdpy, r->clip_);
      int width = r->bytes_per_line_ * 8;
      r->clip_ = XCreateBitmapFromData(
        xdpy,
        dr->root_,
        r->clip_data_,
        width, r->pheight_
      );
      r->clip_valid_ = true;
    }

    XPutImage(
      xdpy, r->pixmap_, r->gc_, r->image_,
      0, 0, 0, 0, r->pwidth_, r->pheight_
    );

    // bmarsch: 10 Nov 94: clean up image data
    delete[] r->image_->data;
    r->image_->data = nil;

    r->modified_ = false;
  }
}

// bmarsch: 10 Nov 94
// new methods for dithering

void Raster::ditherImage() const
{
  if (rep_->trueColor_) {
    trueColorDither();   // truecolor visual
  }
  else {
    if (doDither_) {
      fsDither();   // floyd-steinberg dithering
    }
    else {
      noDither();   // no dithering
    }
  }
}

static void LFirstDither(const unsigned char* sptr, char* dptr,
                         unsigned int w, unsigned int h)
{
  unsigned int i = w * h;
    
  while (i--) {
    char r = (char) *sptr++;
    char g = (char) *sptr++;
    char b = (char) *sptr++;
    *dptr++ = b;
    *dptr++ = g;
    *dptr++ = r;
    *dptr++ = 0;
  }
}

static void MFirstDither(const unsigned char* sptr, char* dptr,
                         unsigned int w, unsigned int h)
{
  unsigned int i = w * h;
    
  while (i--) {
    char r = (char) *sptr++;
    char g = (char) *sptr++;
    char b = (char) *sptr++;
    *dptr++ = 0;
    *dptr++ = b;
    *dptr++ = g;
    *dptr++ = r;
  }
}

void Raster::trueColorDither() const
{
  if (image_byte_order == LSBFirst)
    LFirstDither(rep_->data_, rep_->image_->data, rep_->pwidth_, rep_->pheight_);
  else
    MFirstDither(rep_->data_, rep_->image_->data, rep_->pwidth_, rep_->pheight_);
}


void Raster::noDither() const
{  
  unsigned char* sptr = rep_->data_;
  char* dptr = rep_->image_->data;

  for (int i=rep_->pheight_; i>0; i--) {
    for (int j=rep_->pwidth_; j>0; j--) {
      unsigned char r = *sptr++;
      unsigned char g = *sptr++;
      unsigned char b = *sptr++;
      // this maptable only considers 4 bits for each r, g and b
      int ind = map_RGB_harmony[((r & 0xf0) << 4) | (g & 0xf0) | (b >> 4)];
      *dptr++ = (char) cmaptab[ind];
    }
  }
}


#define RANGE(a,b,c) { if (a < b) a = b;  if (a > c) a = c; }

void Raster::fsDither() const
{
  // taken from xv
  /* floyd-steinberg dithering.
   *
   * ----   x    7/16
   * 3/16  5/16  1/16
   *
   */

  int w = rep_->pwidth_;
  int h = rep_->pheight_;
  unsigned char* p24 = rep_->data_;
  unsigned char* pic8 = (unsigned char*) rep_->image_->data;

  /* called after 'pic8' has been alloced, pWIDE,pHIGH set up, mono/1-bit
     checked already */

  unsigned char *pp;
  int  r1, g1, b1;
  int  *thisline, *nextline, *thisptr, *nextptr, *tmpptr;
  int  i, j, val, pwide3;
  int  imax, jmax;

  pp = pic8;  pwide3 = w * 3;  imax = h-1;  jmax = w-1;

  /* load up colormap */
  /* note that 0 and 255 of each color are always in the map; */
  /* intermediate values are evenly spaced. */
  for (i=0; i<256; i++) {
    rmap[i] = harmony_color[i].r >> 8;
    gmap[i] = harmony_color[i].g >> 8;
    bmap[i] = harmony_color[i].b >> 8;
  }


  thisline = (int *) malloc(pwide3 * sizeof(int));
  nextline = (int *) malloc(pwide3 * sizeof(int));
  if (!thisline || !nextline) {
    cerr << "Raster: unable to allocate memory for Floyd-Steinberg dithering" << endl;
    return;
  }

  /* get first line of picture */
  for (j=pwide3, tmpptr=nextline; j; j--) *tmpptr++ = (int) *p24++;

  for (i=0; i<h; i++) {
    tmpptr = thisline;  thisline = nextline;  nextline = tmpptr;   /* swap */

    if (i!=imax)   /* get next line */
      for (j=pwide3, tmpptr=nextline; j; j--)
	*tmpptr++ = (int) *p24++;

    for (j=0, thisptr=thisline, nextptr=nextline; j<w; j++,pp++) {
      r1 = *thisptr++;  g1 = *thisptr++;  b1 = *thisptr++;
      RANGE(r1,0,255);  RANGE(g1,0,255);  RANGE(b1,0,255);  

      /* choose actual pixel value */
      // see comment about colour mapping in noDither (bmarsch will look at this)
      val = map_RGB_harmony[int(r1 & 0xf0) << 4 | int(g1 & 0xf0) | int(b1) >> 4];
      *pp = (char) cmaptab[val];

      /* compute color errors */
      r1 -= rmap[val];
      g1 -= gmap[val];
      b1 -= bmap[val];

      /* Add fractions of errors to adjacent pixels */
      r1 += 256;              /* make positive for table indexing */
      g1 += 256;
      b1 += 256;

      if (j!=jmax) {  /* adjust RIGHT pixel */
	thisptr[0] += tbl7[r1];
	thisptr[1] += tbl7[g1];
	thisptr[2] += tbl7[b1];
      }
      
      if (i!=imax) {	/* do BOTTOM pixel */
	nextptr[0] += tbl5[r1];
	nextptr[1] += tbl5[g1];
	nextptr[2] += tbl5[b1];

	if (j>0) {  /* do BOTTOM LEFT pixel */
	  nextptr[-3] += tbl3[r1];
	  nextptr[-2] += tbl3[g1];
	  nextptr[-1] += tbl3[b1];
	}

	if (j!=jmax) {  /* do BOTTOM RIGHT pixel */
	  nextptr[3] += tbl1[r1];
	  nextptr[4] += tbl1[g1];
	  nextptr[5] += tbl1[b1];
	}
	nextptr += 3;
      }
    }
  }
}

#undef RANGE

// bmarsch: 20 Dec 94: transparency

void Raster::useAlphaTransparency()
{
  RasterRep* r = rep_;

  if (!r->clip_data_) {
    r->bytes_per_line_ = (r->pwidth_ + 7) / 8;
    int len = r->pheight_ * r->bytes_per_line_;
    r->clip_data_ = new char[len];
    memset(r->clip_data_, 0xff, len);
    r->modified_ = true;
  }
}

// bmarsch: 09 Jan 95: return data array
const unsigned char* Raster::getRGBarray()
{
  return rep_->data_;
}

void Raster::freeRGBarray() {}

const unsigned char* Raster::getRGBAarray()
{
  RasterRep* r = rep_;
  int w = r->pwidth_;
  int h = r->pheight_;
  unsigned char* rgb = new unsigned char[w * h * 4];
  unsigned char* dptr = r->data_;
  char* aptr = r->clip_data_;

  for (int i = 0; i < h; i++) {
    int a = 0;
    for (int j = 0; j < w; j++) {
      *rgb++ = *dptr++;  // copy r
      *rgb++ = *dptr++;  // copy g
      *rgb++ = *dptr++;  // copy b
      *rgb++ = (*aptr & mask[a++]) ? 0xFF : 0;  //get alpha
      if (a > 7) {
        a = 0;
        aptr++;
      }
    }
    if (a) aptr++;
  }

  r->data_rgba_ = rgb;
  return rgb;
}

void Raster::freeRGBAarray()
{
  delete[] rep_->data_rgba_;
}
