/** \file
 * \brief Image RGB Driver
 *
 * See Copyright Notice in cd.h
 */

#include <stdlib.h> 
#include <memory.h> 
#include <math.h> 
#include <stdio.h>
#include <string.h>
#include <assert.h>

#include "cd.h"
#include "cd_private.h"
#include "cd_truetype.h"
#include "sim.h"
#include "cdirgb.h"


struct _cdCtxImage 
{
  int w, h;
  unsigned char* red;     /* red color buffer */
  unsigned char* green;   /* green color buffer */
  unsigned char* blue;    /* blue color buffer */
  unsigned char* alpha;   /* alpha color buffer */
};


struct _cdCtxCanvas 
{
  cdCanvas* canvas;

  int user_image;         /* can not free an user image */

  unsigned char* red;     /* red color buffer */
  unsigned char* green;   /* green color buffer */
  unsigned char* blue;    /* blue color buffer */
  unsigned char* alpha;   /* alpha color buffer */
  unsigned char* clip;    /* clipping buffer */

  unsigned char* clip_region;  /* clipping region used during NewRegion */

  float  rotate_angle;
  int    rotate_center_x,
         rotate_center_y;

  cdCanvas* canvas_dbuffer; /* used by the CD_DBUFFERRGB driver */
};

/*******************/
/* Local functions */
/*******************/

#define _sNormX(_ctxcanvas, _x) (_x < 0? 0: _x < _ctxcanvas->canvas->w? _x: _ctxcanvas->canvas->w-1)
#define _sNormY(_ctxcanvas, _y) (_y < 0? 0: _y < _ctxcanvas->canvas->h? _y: _ctxcanvas->canvas->h-1)

#define RGB_COMPOSE_OVER(_SRC, _SRC_ALPHA, _DST, _TMP_MULTI, _TMP_ALPHA) (unsigned char)(((_SRC_ALPHA)*(_SRC) + (_TMP_MULTI)*(_DST)) / (_TMP_ALPHA))

#define RGBA_WRITE_MODE(_write_mode, _pdst_red, _pdst_green, _pdst_blue, _tmp_red, _tmp_green, _tmp_blue) \
{                                                                                                         \
  switch (_write_mode)                                                                                    \
  {                                                                                                       \
  case CD_REPLACE:                                                                                        \
    *_pdst_red = _tmp_red;                                                                                \
    *_pdst_green = _tmp_green;                                                                            \
    *_pdst_blue = _tmp_blue;                                                                              \
    break;                                                                                                \
  case CD_XOR:                                                                                            \
    *_pdst_red ^= _tmp_red;                                                                               \
    *_pdst_green ^= _tmp_green;                                                                           \
    *_pdst_blue ^= _tmp_blue;                                                                             \
    break;                                                                                                \
  case CD_NOT_XOR:                                                                                        \
    *_pdst_red = (unsigned char)~(_tmp_red ^ *_pdst_red);                                                 \
    *_pdst_green = (unsigned char)~(_tmp_green ^ *_pdst_green);                                           \
    *_pdst_blue = (unsigned char)~(_tmp_blue ^ *_pdst_blue);                                              \
    break;                                                                                                \
  }                                                                                                       \
}

#define RGBA_COLOR_COMBINE(_ctxcanvas, _pdst_red, _pdst_green, _pdst_blue, _pdst_alpha, _src_red, _src_green, _src_blue, _src_alpha) \
{                                                                                                                        \
  unsigned char _tmp_red = 0, _tmp_green = 0, _tmp_blue = 0;                                                             \
                                                                                                                         \
  if (_pdst_alpha)   /* destiny has alpha */                                                                             \
  {                                                                                                                      \
    if (_src_alpha != 255)   /* some transparency */                                                                     \
    {                                                                                                                    \
      if (_src_alpha != 0) /* source not full transparent */                                                             \
      {                                                                                                                  \
        if (*_pdst_alpha == 0) /* destiny full transparent */                                                            \
        {                                                                                                                \
          _tmp_red = _src_red;                                                                                           \
          _tmp_green = _src_green;                                                                                       \
          _tmp_blue = _src_blue;                                                                                         \
          *_pdst_alpha = _src_alpha;                                                                                     \
          RGBA_WRITE_MODE(CD_REPLACE, _pdst_red, _pdst_green, _pdst_blue,                                                \
                                      _tmp_red, _tmp_green, _tmp_blue);                                                  \
        }                                                                                                                \
        else if (*_pdst_alpha == 255) /* destiny opaque */                                                               \
        {                                                                                                                \
          _tmp_red = CD_ALPHA_BLEND(_src_red, *_pdst_red, _src_alpha);                                                   \
          _tmp_green = CD_ALPHA_BLEND(_src_green, *_pdst_green, _src_alpha);                                             \
          _tmp_blue = CD_ALPHA_BLEND(_src_blue, *_pdst_blue, _src_alpha);                                                \
          /* *_pdst_alpha is not changed */                                                                              \
          RGBA_WRITE_MODE(CD_REPLACE, _pdst_red, _pdst_green, _pdst_blue,                                                \
                                      _tmp_red, _tmp_green, _tmp_blue);                                                  \
        }                                                                                                                \
        else /* (0<*_pdst_alpha<255 && 0<_src_alpha<255) destiny and source are semi-transparent */                      \
        {                                                                                                                \
          /* Closed Compositing SRC over DST  (see smith95a.pdf)        */                                               \
          /* Colors NOT Premultiplied by Alpha                          */                                               \
          /* DST = SRC * SRC_ALPHA + DST * DST_ALPHA * (1 - SRC_ALPHA)  */                                               \
          /* DST_ALPHA = SRC_ALPHA + DST_ALPHA * (1 - SRC_ALPHA)        */                                               \
          /* DST /= DST_ALPHA */                                                                                         \
          int _tmp_multi = *_pdst_alpha * (255 - _src_alpha);                                                            \
          int _tmp_src_alpha = _src_alpha*255;                                                                           \
          int _tmp_alpha = _tmp_src_alpha + _tmp_multi;                                                                  \
          _tmp_red = RGB_COMPOSE_OVER(_src_red, _tmp_src_alpha, *_pdst_red, _tmp_multi, _tmp_alpha);                     \
          _tmp_green = RGB_COMPOSE_OVER(_src_green, _tmp_src_alpha, *_pdst_green, _tmp_multi, _tmp_alpha);               \
          _tmp_blue = RGB_COMPOSE_OVER(_src_blue, _tmp_src_alpha, *_pdst_blue, _tmp_multi, _tmp_alpha);                  \
          *_pdst_alpha = (unsigned char)(_tmp_alpha / 255);                                                              \
          RGBA_WRITE_MODE(CD_REPLACE, _pdst_red, _pdst_green, _pdst_blue,                                                \
                                      _tmp_red, _tmp_green, _tmp_blue);                                                  \
        }                                                                                                                \
      }                                                                                                                  \
      else  /* (_src_alpha == 0) source full transparent */                                                              \
      {                                                                                                                  \
        _tmp_red = *_pdst_red;                                                                                           \
        _tmp_green = *_pdst_green;                                                                                       \
        _tmp_blue = *_pdst_blue;                                                                                         \
        RGBA_WRITE_MODE(CD_REPLACE, _pdst_red, _pdst_green, _pdst_blue,                                                  \
                                    _tmp_red, _tmp_green, _tmp_blue);                                                    \
        /* *_pdst_alpha is not changed */                                                                                \
      }                                                                                                                  \
    }                                                                                                                    \
    else  /* (_src_alpha == 255) source has no alpha = opaque */                                                         \
    {                                                                                                                    \
      _tmp_red = _src_red;                                                                                               \
      _tmp_green = _src_green;                                                                                           \
      _tmp_blue = _src_blue;                                                                                             \
      *_pdst_alpha = (unsigned char)255;   /* set destiny as opaque */                                                   \
      RGBA_WRITE_MODE(_ctxcanvas->canvas->write_mode, _pdst_red, _pdst_green, _pdst_blue,                                \
                                                      _tmp_red, _tmp_green, _tmp_blue);                                  \
    }                                                                                                                    \
  }                                                                                                                      \
  else /* destiny does NOT have alpha */                                                                                 \
  {                                                                                                                      \
    if (_src_alpha != 255) /* source has some transparency */                                                            \
    {                                                                                                                    \
      if (_src_alpha != 0) /* source semi-transparent */                                                                 \
      {                                                                                                                  \
        _tmp_red = CD_ALPHA_BLEND(_src_red, *_pdst_red, _src_alpha);                                                     \
        _tmp_green = CD_ALPHA_BLEND(_src_green, *_pdst_green, _src_alpha);                                               \
        _tmp_blue = CD_ALPHA_BLEND(_src_blue, *_pdst_blue, _src_alpha);                                                  \
        RGBA_WRITE_MODE(CD_REPLACE, _pdst_red, _pdst_green, _pdst_blue,                                                  \
                                    _tmp_red, _tmp_green, _tmp_blue);                                                    \
      }                                                                                                                  \
      else  /* (_src_alpha == 0) source full transparent */                                                              \
      {                                                                                                                  \
        _tmp_red = *_pdst_red;                                                                                           \
        _tmp_green = *_pdst_green;                                                                                       \
        _tmp_blue = *_pdst_blue;                                                                                         \
        RGBA_WRITE_MODE(CD_REPLACE, _pdst_red, _pdst_green, _pdst_blue,                                                  \
                                    _tmp_red, _tmp_green, _tmp_blue);                                                    \
      }                                                                                                                  \
    }                                                                                                                    \
    else  /* (_src_alpha == 255) source has no alpha = opaque */                                                         \
    {                                                                                                                    \
      _tmp_red = _src_red;                                                                                               \
      _tmp_green = _src_green;                                                                                           \
      _tmp_blue = _src_blue;                                                                                             \
      RGBA_WRITE_MODE(_ctxcanvas->canvas->write_mode, _pdst_red, _pdst_green, _pdst_blue,                                \
                                                      _tmp_red, _tmp_green, _tmp_blue);                                  \
    }                                                                                                                    \
  }                                                                                                                      \
}

static void sCombineRGBColor(cdCtxCanvas* ctxcanvas, int offset, long color)
{
  unsigned char *dr = ctxcanvas->red + offset;
  unsigned char *dg = ctxcanvas->green + offset;
  unsigned char *db = ctxcanvas->blue + offset;
  unsigned char *da = ctxcanvas->alpha? ctxcanvas->alpha + offset: NULL;
  unsigned char *clip = ctxcanvas->clip + offset;

  unsigned char sr = cdRed(color);
  unsigned char sg = cdGreen(color);
  unsigned char sb = cdBlue(color); 
  unsigned char sa = cdAlpha(color);

  if (*clip)
    RGBA_COLOR_COMBINE(ctxcanvas, dr, dg, db, da, sr, sg, sb, sa);
}

static void sCombineRGB(cdCtxCanvas* ctxcanvas, int offset, unsigned char sr, unsigned char sg, unsigned char sb, unsigned char sa)
{
  unsigned char *dr = ctxcanvas->red + offset;
  unsigned char *dg = ctxcanvas->green + offset;
  unsigned char *db = ctxcanvas->blue + offset;
  unsigned char *da = ctxcanvas->alpha? ctxcanvas->alpha + offset: NULL;
  unsigned char *clip = ctxcanvas->clip + offset;

  if (*clip)
    RGBA_COLOR_COMBINE(ctxcanvas, dr, dg, db, da, sr, sg, sb, sa);
}

static void sCombineRGBLine(cdCtxCanvas* ctxcanvas, int offset, const unsigned char *sr, const unsigned char *sg, const unsigned char *sb, int size)
{
  int c;
  unsigned char *dr = ctxcanvas->red + offset;
  unsigned char *dg = ctxcanvas->green + offset;
  unsigned char *db = ctxcanvas->blue + offset;
  unsigned char *da = ctxcanvas->alpha? ctxcanvas->alpha + offset: NULL;
  unsigned char *clip = ctxcanvas->clip + offset;
  unsigned char src_a = 255;

  if (size > 0)
  {
    for (c = 0; c < size; c++)
    {
      if (*clip)
        RGBA_COLOR_COMBINE(ctxcanvas, dr, dg, db, da, *sr, *sg, *sb, src_a);
      dr++; dg++; db++; clip++;
      sr++; sg++; sb++;
      if (da) da++;
    }
  }
  else
  {
    size *= -1;
    for (c = 0; c < size; c++)
    {
      if (*clip)
        RGBA_COLOR_COMBINE(ctxcanvas, dr, dg, db, da, *sr, *sg, *sb, src_a);
      dr--; dg--; db--; clip--;
      sr--; sg--; sb--;
      if (da) da--;
    }
  }
}

static void sCombineRGBALine(cdCtxCanvas* ctxcanvas, int offset, const unsigned char *sr, const unsigned char *sg, const unsigned char *sb, const unsigned char *sa, int size)
{
  int c;
  unsigned char *dr = ctxcanvas->red + offset;
  unsigned char *dg = ctxcanvas->green + offset;
  unsigned char *db = ctxcanvas->blue + offset;
  unsigned char *da = ctxcanvas->alpha? ctxcanvas->alpha + offset: NULL;
  unsigned char *clip = ctxcanvas->clip + offset;

  if (size > 0)
  {
    for (c = 0; c < size; c++)
    {
      if (*clip)
        RGBA_COLOR_COMBINE(ctxcanvas, dr, dg, db, da, *sr, *sg, *sb, *sa);
      dr++; dg++; db++; clip++;
      sr++; sg++; sb++; sa++; 
      if (da) da++;
    }
  }
  else
  {
    size *= -1;
    for (c = 0; c < size; c++)
    {
      if (*clip)
        RGBA_COLOR_COMBINE(ctxcanvas, dr, dg, db, da, *sr, *sg, *sb, *sa);
      dr--; dg--; db--; clip--;
      sr--; sg--; sb--; sa--; 
      if (da) da--;
    }
  }
}

static void irgbSolidLine(cdCanvas* canvas, int xmin, int y, int xmax)
{
  int x;
  unsigned long offset = y * canvas->w;

  if (y < 0)
    return;

  if (y > (canvas->h-1))
    return;

  if (xmin < 0)  /* Arruma limites de acordo com o retangulo de clip */
    xmin = 0;    /* so clipa em x                                    */
  if (xmax > (canvas->w-1))
    xmax = (canvas->w-1);

  for (x = xmin; x <= xmax; x++)
    sCombineRGBColor(canvas->ctxcanvas, offset + x, canvas->foreground);
}

static void irgbPatternLine(cdCanvas* canvas, int xmin, int xmax, int y, int pw, const long *pattern)
{
  int x, i;
  unsigned long offset = y * canvas->w;

  if (y < 0 || y > (canvas->h-1))
    return;
  
  if (xmin < 0)  /* Arruma limites de acordo com o retangulo de clip */
    xmin = 0;    /* so clipa em x                                    */
  if (xmax > (canvas->w-1))
    xmax = (canvas->w-1);

  i = xmin % pw;

  for (x = xmin; x <= xmax; x++,i++)
  {
    if (i == pw) 
      i = 0;

    sCombineRGBColor(canvas->ctxcanvas, offset + x, pattern[i]);
  }
}

static void irgbStippleLine(cdCanvas* canvas, int xmin, int xmax, int y, int pw, const unsigned char *stipple)
{
  int x,i;
  unsigned long offset = y * canvas->w;

  if (y < 0 || y > (canvas->h-1))
    return;

  if (xmin < 0)  /* Arruma limites de acordo com o retangulo de clip */
    xmin = 0;    /* so clipa em x                                    */
  if (xmax > (canvas->w-1))
    xmax = (canvas->w-1);

  i = xmin % pw;

  for (x = xmin; x <= xmax; x++,i++)
  {
    if (i == pw) 
      i = 0;
    if(stipple[i])
      sCombineRGBColor(canvas->ctxcanvas, offset + x, canvas->foreground);
    else if (canvas->back_opacity == CD_OPAQUE)
      sCombineRGBColor(canvas->ctxcanvas, offset + x, canvas->background);
  }
}

static void irgbHatchLine(cdCanvas* canvas, int xmin, int xmax, int y, unsigned char hatch)
{
  int x;
  unsigned long offset = y * canvas->w;
  unsigned char n;
  
  if (y < 0 || y > (canvas->h-1))
    return;

  if (xmin < 0)  /* Arruma limites de acordo com o retangulo de clip */
    xmin = 0;    /* so clipa em x                                    */
  if (xmax > (canvas->w-1))
    xmax = (canvas->w-1);

  n = (unsigned char)(xmin&7);
  simRotateHatchN(hatch, n);

  for (x = xmin; x <= xmax; x++)
  {
    if (hatch & 0x80)
      sCombineRGBColor(canvas->ctxcanvas, offset + x, canvas->foreground);
    else if (canvas->back_opacity == CD_OPAQUE)
      sCombineRGBColor(canvas->ctxcanvas, offset + x, canvas->background);

    _cdRotateHatch(hatch);
  }
}

/********************/
/* driver functions */
/********************/

static void cdkillcanvas(cdCtxCanvas* ctxcanvas)
{
  if (!ctxcanvas->user_image)
    free(ctxcanvas->red);

  if (ctxcanvas->clip_region)
    free(ctxcanvas->clip_region);

  free(ctxcanvas->clip);

  memset(ctxcanvas, 0, sizeof(cdCtxCanvas));
  free(ctxcanvas);
}

unsigned char* cdAlphaImage(cdCanvas* canvas)
{
  cdCtxCanvas* ctxcanvas;
  assert(canvas);
  ctxcanvas = (cdCtxCanvas*)canvas->ctxcanvas;
  return ctxcanvas->alpha;
}

unsigned char* cdRedImage(cdCanvas* canvas)
{
  cdCtxCanvas* ctxcanvas;
  assert(canvas);
  ctxcanvas = (cdCtxCanvas*)canvas->ctxcanvas;
  return ctxcanvas->red;
}

unsigned char* cdGreenImage(cdCanvas* canvas)
{
  cdCtxCanvas* ctxcanvas;
  assert(canvas);
  ctxcanvas = (cdCtxCanvas*)canvas->ctxcanvas;
  return ctxcanvas->green;
}

unsigned char* cdBlueImage(cdCanvas* canvas)
{
  cdCtxCanvas* ctxcanvas;
  assert(canvas);
  ctxcanvas = (cdCtxCanvas*)canvas->ctxcanvas;
  return ctxcanvas->blue;
}

static void cdclear(cdCtxCanvas* ctxcanvas)
{
  int size = ctxcanvas->canvas->w * ctxcanvas->canvas->h; 
  memset(ctxcanvas->red, cdRed(ctxcanvas->canvas->background), size);
  memset(ctxcanvas->green, cdGreen(ctxcanvas->canvas->background), size);
  memset(ctxcanvas->blue, cdBlue(ctxcanvas->canvas->background), size);
  if (ctxcanvas->alpha) memset(ctxcanvas->alpha, cdAlpha(ctxcanvas->canvas->background), size);  /* here is the normal alpha coding */
}

static void irgPostProcessIntersect(unsigned char* clip, int size)
{
  int i;
  for(i = 0; i < size; i++)
  {
    if (*clip == 2)
      *clip = 1;
    else
      *clip = 0;

    clip++;
  }
}

#define _irgSetClipPixel(_clip, _combine_mode)             \
{                                                          \
  switch (_combine_mode)                                   \
  {                                                        \
  case CD_INTERSECT:                                       \
    if (_clip)                                             \
      _clip = 2;  /* fills the intersection                \
                   with a value to be post-processed */    \
    break;                                                 \
  case CD_DIFFERENCE:                                      \
    if (_clip)                                             \
      _clip = 0;  /* clears the intersection */            \
    break;                                                 \
  case CD_NOTINTERSECT: /* XOR */                          \
    if (_clip)                                             \
      _clip = 0;  /* clears the intersection */            \
    else                                                   \
      _clip = 1;  /* fills the region */                   \
    break;                                                 \
  default: /* CD_UNION */                                  \
    _clip = 1;    /* fills the region */                   \
    break;                                                 \
  }                                                        \
}

static void irgbClipTextBitmap(FT_Bitmap* bitmap, int x, int y, int w, unsigned char* clip, int combine_mode)
{
  unsigned char *bitmap_data;
  int width = bitmap->width;
  int height = bitmap->rows;
  int i, j;

  /* avoid spaces */
  if (width == 0 || height == 0)
    return;

  bitmap_data = bitmap->buffer + (height-1)*width;  /* bitmap is top down. */

  clip += y * w + x;

  for (i = 0; i < height; i++)
  {
    for (j = 0; j < width; j++)
    {
      if (bitmap_data[j] == 255)
        _irgSetClipPixel(clip[j], combine_mode);
    }
    clip += w;
    bitmap_data -= width;
  }
}

static void irgbClipText(cdCtxCanvas *ctxcanvas, int x, int y, const char *s, int len)
{
  cdCanvas* canvas = ctxcanvas->canvas;
  cdSimulation* simulation = canvas->simulation;
  FT_Face       face;
  FT_GlyphSlot  slot;
  FT_Matrix     matrix;                 /* transformation matrix */
  FT_Vector     pen;                    /* untransformed origin  */
  FT_Error      error;
  int i = 0;

  if (!simulation->tt_text->face)
    return;

  face = simulation->tt_text->face;
  slot = face->glyph;

  /* move the reference point to the baseline-left */
  simGetPenPos(simulation->canvas, x, y, s, len, &matrix, &pen);

  while(i<len)
  {
    /* set transformation */
    FT_Set_Transform(face, &matrix, &pen);

    /* load glyph image into the slot (erase previous one) */
    error = FT_Load_Char(face, (unsigned char)s[i], FT_LOAD_RENDER);
    if (error) {i++; continue;}  /* ignore errors */

    x = slot->bitmap_left;
    y = slot->bitmap_top-slot->bitmap.rows; /* CD image reference point is at bottom-left */

    /* now, draw to our target surface (convert position) */
    irgbClipTextBitmap(&slot->bitmap, x, y, canvas->w, ctxcanvas->clip_region, canvas->combine_mode);

    /* increment pen position */
    pen.x += slot->advance.x;
    pen.y += slot->advance.y;

    i++;
  }

  if (canvas->combine_mode == CD_INTERSECT)
    irgPostProcessIntersect(ctxcanvas->clip_region, ctxcanvas->canvas->w * ctxcanvas->canvas->h);
}

static void irgbClipFillLine(unsigned char* clip_line, int combine_mode, int x1, int x2, int width)
{
  int x;
  if (x1 < 0) x1 = 0;
  if (x2 > width-1) x2 = width-1;
  for (x = x1; x <= x2; x++)
  {
    _irgSetClipPixel(clip_line[x], combine_mode);
  }
}

static int compare_int(const int* xx1, const int* xx2)
{
  return *xx1 - *xx2;
}

static void irgbClipPoly(cdCtxCanvas* ctxcanvas, unsigned char* clip_region, cdPoint* poly, int n, int combine_mode) 
{
  /***********IMPORTANT: the reference for this function is simPolyFill in "sim_linepolyfill.c",
     if a change is made here, must be reflected there, and vice-versa */
  cdCanvas* canvas = ctxcanvas->canvas;
  unsigned char* clip_line;
  cdPoint* t_poly = NULL;
  int y_max, y_min, i, y, fill_mode, num_lines, 
      xx_count, width, height, *xx, *hh, max_hh, n_seg;
  
  /* alloc maximum number of segments */
  simLineSegment *segments = (simLineSegment *)malloc(n*sizeof(simLineSegment));

  if (canvas->use_matrix)
  {
    t_poly = malloc(sizeof(cdPoint)*n);
    memcpy(t_poly, poly, sizeof(cdPoint)*n);
    poly = t_poly;

    for(i = 0; i < n; i++)
      cdMatrixTransformPoint(canvas->matrix, poly[i].x, poly[i].y, &poly[i].x, &poly[i].y);
  }

  width = canvas->w;
  height = canvas->h;
  fill_mode = canvas->fill_mode;
  
  simPolyMakeSegments(segments, &n_seg, poly, n, &max_hh, &y_max, &y_min);
  
  if (y_min > height-1 || y_max < 0)
  {
    free(segments);
    return;
  }
  
  if (y_min < 0) 
    y_min = 0;

  /* number of horizontal lines */
  if (y_max > height-1)
    num_lines = height-y_min;
  else
    num_lines = y_max-y_min+1;

  /* buffer to store the current horizontal intervals during the fill of an horizontal line */
  xx = (int*)malloc((n+1)*sizeof(int));    /* allocated to the maximum number of possible intervals in one line */
  hh = (int*)malloc((2*max_hh)*sizeof(int));

  /* for all horizontal lines between y_max and y_min */
  for(y = y_max; y >= y_min; y--)
  {
    xx_count = simPolyFindHorizontalIntervals(segments, n_seg, xx, hh, y, height);
    if (xx_count < 2)
      continue;
    
    clip_line = clip_region + y*width;

    /* for all intervals, fill the interval */
    for(i = 0; i < xx_count; i += 2)  /* process only pairs */
    {
      /* fills only pairs of intervals, */          
      irgbClipFillLine(clip_line, combine_mode, xx[i], xx[i+1], width);

      if ((fill_mode == CD_WINDING) &&                     /* NOT EVENODD */
          ((i+2 < xx_count) && (xx[i+1] < xx[i+2])) && /* avoid point intervals */
           simIsPointInPolyWind(poly, n, (xx[i+1]+xx[i+2])/2, y)) /* the next interval is inside the polygon */
      {
        irgbClipFillLine(clip_line, combine_mode, xx[i+1], xx[i+2], width);
      }
    }
  }

  if (t_poly) free(t_poly);
  free(xx);
  free(hh);
  free(segments);

  if (combine_mode == CD_INTERSECT)
    irgPostProcessIntersect(ctxcanvas->clip_region, ctxcanvas->canvas->w * ctxcanvas->canvas->h);
}

static void irgbClipElipse(cdCtxCanvas* ctxcanvas, int xc, int yc, int width, int height, double angle1, double angle2, int sector)
{
  float c, s, sx, sy, x, y, prev_x, prev_y;
  double da;
  int i, p;
  cdPoint* poly;

  /* number of segments of equivalent poligonal for a full ellipse */
  int n = simCalcEllipseNumSegments(ctxcanvas->canvas, xc, yc, width, height);

  /* number of segments for the arc */
  n = cdRound((fabs(angle2-angle1)*n)/360);
  if (n < 1) n = 1;

  poly = (cdPoint*)malloc(sizeof(cdPoint)*(n+2+1));  /* n+1 points +1 center */

  /* converts degrees into radians */
  angle1 *= CD_DEG2RAD;
  angle2 *= CD_DEG2RAD;

  /* generates arc points at origin with axis x and y */

  da = (angle2-angle1)/n;
  c  = (float)cos(da);
  s  = (float)sin(da);
  sx = -(width*s)/height;
  sy = (height*s)/width;

  x = xc +  (width/2.0f)*(float)cos(angle1);
  y = yc + (height/2.0f)*(float)sin(angle1);
  poly[0].x = _cdRound(x);
  poly[0].y = _cdRound(y);
  prev_x = x;
  prev_y = y;
  p = 1;

  for (i = 1; i < n+1; i++) /* n+1 points */
  {
    x = xc +  c*(prev_x-xc) + sx*(prev_y-yc);
    y = yc + sy*(prev_x-xc) +  c*(prev_y-yc);

    poly[p].x = _cdRound(x);
    poly[p].y = _cdRound(y);

    if (poly[p-1].x != poly[p].x || poly[p-1].y != poly[p].y)
      p++;

    prev_x = x;
    prev_y = y;
  }

  if (poly[p-1].x != poly[0].x || poly[p-1].y != poly[0].y)
  {
    if (sector)  /* cdSector */
    {
      /* add center */
      poly[p].x = xc;
      poly[p].y = yc;
    }
    else         /* cdChord */
    {
      /* add initial point */
      poly[p].x = poly[0].x;
      poly[p].y = poly[0].y;
    }
    p++;
  }

  irgbClipPoly(ctxcanvas, ctxcanvas->clip_region, poly, p, ctxcanvas->canvas->combine_mode);

  free(poly);
}

static void irgbClipBox(cdCtxCanvas *ctxcanvas, int xmin, int xmax, int ymin, int ymax)
{
  int combine_mode = ctxcanvas->canvas->combine_mode;
  unsigned char* clip_line;
  int x, y, width;

  if (ctxcanvas->canvas->use_matrix)
  {
    cdPoint poly[4];
    poly[0].x = xmin; poly[0].y = ymin;
    poly[1].x = xmin; poly[1].y = ymax;
    poly[2].x = xmax; poly[2].y = ymax;
    poly[3].x = xmax; poly[3].y = ymin;
    irgbClipPoly(ctxcanvas, ctxcanvas->clip_region, poly, 4, ctxcanvas->canvas->combine_mode);
    return;
  }

  xmin = _sNormX(ctxcanvas, xmin);
  ymin = _sNormY(ctxcanvas, ymin);
  xmax = _sNormX(ctxcanvas, xmax);
  ymax = _sNormY(ctxcanvas, ymax);
  width = ctxcanvas->canvas->w;

  for(y = ymin; y <= ymax; y++)
  {
    clip_line = ctxcanvas->clip_region + y*width;
    for(x = xmin; x <= xmax; x++)
    {
      _irgSetClipPixel(clip_line[x], combine_mode);
    }
  }

  if (combine_mode == CD_INTERSECT)
    irgPostProcessIntersect(ctxcanvas->clip_region, ctxcanvas->canvas->w * ctxcanvas->canvas->h);
}

static void irgbClipArea(cdCtxCanvas* ctxcanvas, int xmin, int xmax, int ymin, int ymax)
{
  unsigned char* clip_line = ctxcanvas->clip; /* set directly to clip */
  int y, xsize, ysize, height, width, xrigth;

  if (ctxcanvas->canvas->use_matrix)
  {
    cdPoint poly[4];
    poly[0].x = xmin; poly[0].y = ymin;
    poly[1].x = xmin; poly[1].y = ymax;
    poly[2].x = xmax; poly[2].y = ymax;
    poly[3].x = xmax; poly[3].y = ymin;
    memset(ctxcanvas->clip, 0, ctxcanvas->canvas->w * ctxcanvas->canvas->h);
    irgbClipPoly(ctxcanvas, ctxcanvas->clip, poly, 4, CD_UNION);
    return;
  }

  xmin = _sNormX(ctxcanvas, xmin);
  ymin = _sNormY(ctxcanvas, ymin);
  xmax = _sNormX(ctxcanvas, xmax);
  ymax = _sNormY(ctxcanvas, ymax);
  xsize = xmax-xmin+1;
  ysize = ymax-ymin+1;
  height = ctxcanvas->canvas->h;
  width = ctxcanvas->canvas->w;
  xrigth = width-(xmax+1);

  for(y = 0; y < ymin; y++)
  {
    memset(clip_line, 0, width);
    clip_line += width;
  }

  for(y = ymin; y <= ymax; y++)
  {
    if (xmin)
      memset(clip_line, 0, xmin);

    memset(clip_line+xmin, 1, xsize);

    if (xrigth)
      memset(clip_line+xmax+1, 0, xrigth);

    clip_line += width;
  }

  for(y = ymax+1; y < height; y++)
  {
    memset(clip_line, 0, width);
    clip_line += width;
  }
}

static int cdclip(cdCtxCanvas* ctxcanvas, int mode)
{
  switch(mode)
  {
  case CD_CLIPAREA: 
    irgbClipArea(ctxcanvas, ctxcanvas->canvas->clip_rect.xmin, 
                            ctxcanvas->canvas->clip_rect.xmax, 
                            ctxcanvas->canvas->clip_rect.ymin, 
                            ctxcanvas->canvas->clip_rect.ymax);
    break;
  case CD_CLIPPOLYGON:
    memset(ctxcanvas->clip, 0, ctxcanvas->canvas->w * ctxcanvas->canvas->h);
    irgbClipPoly(ctxcanvas, ctxcanvas->clip, ctxcanvas->canvas->clip_poly, ctxcanvas->canvas->clip_poly_n, CD_UNION);
    break;
  case CD_CLIPREGION:
    if (ctxcanvas->clip_region)
      memcpy(ctxcanvas->clip, ctxcanvas->clip_region, ctxcanvas->canvas->w * ctxcanvas->canvas->h);
    break;
  default:
    memset(ctxcanvas->clip, 1, ctxcanvas->canvas->w * ctxcanvas->canvas->h);  /* CD_CLIPOFF */
    break;
  }

  return mode;
}

static void cdcliparea(cdCtxCanvas* ctxcanvas, int xmin, int xmax, int ymin, int ymax)
{                
  if (ctxcanvas->canvas->clip_mode == CD_CLIPAREA)
    irgbClipArea(ctxcanvas, xmin, xmax, ymin, ymax);
}

static void cdnewregion(cdCtxCanvas* ctxcanvas)
{
  if (ctxcanvas->clip_region)
    free(ctxcanvas->clip_region);
  ctxcanvas->clip_region = malloc(ctxcanvas->canvas->w * ctxcanvas->canvas->h);
  memset(ctxcanvas->clip_region, 0, ctxcanvas->canvas->w * ctxcanvas->canvas->h);
}

static int cdispointinregion(cdCtxCanvas* ctxcanvas, int x, int y)
{
  if (!ctxcanvas->clip_region)
    return 0;

  if (x >= 0  && y >= 0 && x < ctxcanvas->canvas->w && y < ctxcanvas->canvas->h)
  {
    if (ctxcanvas->clip_region[y*ctxcanvas->canvas->w + x])
      return 1;
  }

  return 0;
}

static void cdoffsetregion(cdCtxCanvas* ctxcanvas, int dx, int dy)
{
  unsigned char* clip_region = ctxcanvas->clip_region;
  int x, y, X, Y, old_X, old_Y, width, height;

  if (!ctxcanvas->clip_region)
    return;

  height = ctxcanvas->canvas->h;
  width = ctxcanvas->canvas->w;

  for (y = 0; y < height; y++)
  {
    if (dy > 0)
      Y = height-1 - y;
    else
      Y = y;
    old_Y = Y - dy;
    for(x = 0; x < width; x++)
    {
      if (dx > 0)
        X = width-1 - x;
      else
        X = x;
      old_X = X - dx;

      if (old_X >= 0  && old_Y >= 0 && old_Y < height && old_X < width)
        clip_region[Y*width + X] = clip_region[old_Y*width + old_X];
      else
        clip_region[Y*width + X] = 0;
    }
  }
}

static void cdgetregionbox(cdCtxCanvas* ctxcanvas, int *xmin, int *xmax, int *ymin, int *ymax)
{
  unsigned char* clip_line = ctxcanvas->clip_region;
  int x, y, width, height;

  if (!ctxcanvas->clip_region)
    return;

  *xmin = ctxcanvas->canvas->w-1;
  *xmax = 0;
  *ymin = ctxcanvas->canvas->h-1;
  *ymax = 0;
  height = ctxcanvas->canvas->h;
  width = ctxcanvas->canvas->w;

  for (y = 0; y < height; y++)
  {
    for(x = 0; x < width; x++)
    {
      if (*clip_line)
      {
        if (x < *xmin)
          *xmin = x;
        if (y < *ymin)
          *ymin = y;
        if (x > *xmax)
          *xmax = x;
        if (y > *ymax)
          *ymax = y;
      }

      clip_line++;
    }
  }
}

static void cdbox(cdCtxCanvas *ctxcanvas, int xmin, int xmax, int ymin, int ymax)
{
  if (ctxcanvas->canvas->new_region)
  {
    irgbClipBox(ctxcanvas, xmin, xmax, ymin, ymax);
    return;
  }

  cdboxSIM(ctxcanvas, xmin, xmax, ymin, ymax);
}

static void cdsector(cdCtxCanvas *ctxcanvas, int xc, int yc, int w, int h, double a1, double a2)
{
  if (ctxcanvas->canvas->new_region)
  {
    irgbClipElipse(ctxcanvas, xc, yc, w, h, a1, a2, 1);
    return;
  }

  cdsectorSIM(ctxcanvas, xc, yc, w, h, a1, a2);
}

static void cdchord(cdCtxCanvas *ctxcanvas, int xc, int yc, int w, int h, double a1, double a2)
{
  if (ctxcanvas->canvas->new_region)
  {
    irgbClipElipse(ctxcanvas, xc, yc, w, h, a1, a2, 0);
    return;
  }

  cdchordSIM(ctxcanvas, xc, yc, w, h, a1, a2);
}

static void cdtext(cdCtxCanvas *ctxcanvas, int x, int y, const char *s, int len)
{
  if (ctxcanvas->canvas->new_region)
  {
    irgbClipText(ctxcanvas, x, y, s, len);
    return;
  }

  cdtextSIM(ctxcanvas, x, y, s, len);
}

static void cdpoly(cdCtxCanvas* ctxcanvas, int mode, cdPoint* poly, int n)
{
  if (ctxcanvas->canvas->new_region)
  {
    irgbClipPoly(ctxcanvas, ctxcanvas->clip_region, poly, n, ctxcanvas->canvas->combine_mode);
    return;
  }

  if (mode == CD_CLIP)
  {
    /* set directly to clip */
    memset(ctxcanvas->clip, 1, ctxcanvas->canvas->w * ctxcanvas->canvas->h);  /* CD_CLIPOFF */
    irgbClipPoly(ctxcanvas, ctxcanvas->clip, poly, n, CD_UNION);
  }
  else
    cdpolySIM(ctxcanvas, mode, poly, n);
}

static void cdgetimagergb(cdCtxCanvas* ctxcanvas, unsigned char *r, unsigned char *g, unsigned char *b, int x, int y, int w, int h)
{
  int dst_offset, src_offset, l, xsize, ysize, xpos, ypos;
  unsigned char *src_red, *src_green, *src_blue;

  if (x >= ctxcanvas->canvas->w || y >= ctxcanvas->canvas->h || 
      x + w < 0 || y + h < 0)
    return;

  /* ajusta parametros de entrada */
  xpos = _sNormX(ctxcanvas, x);
  ypos = _sNormY(ctxcanvas, y);

  xsize = w < (ctxcanvas->canvas->w - xpos)? w: ctxcanvas->canvas->w - xpos;
  ysize = h < (ctxcanvas->canvas->h - ypos)? h: ctxcanvas->canvas->h - ypos;

  /* ajusta posicao inicial em source */
  src_offset = xpos + ypos * ctxcanvas->canvas->w;
  src_red = ctxcanvas->red + src_offset;
  src_green = ctxcanvas->green + src_offset;
  src_blue = ctxcanvas->blue + src_offset;

  /* offset para source */
  src_offset = ctxcanvas->canvas->w;

  /* ajusta posicao inicial em destine */
  dst_offset = (xpos - x) + (ypos - y) * w;
  r += dst_offset;
  g += dst_offset;
  b += dst_offset;

  for (l = 0; l < ysize; l++)
  {
    memcpy(r, src_red, xsize);
    memcpy(g, src_green, xsize);
    memcpy(b, src_blue, xsize);

    src_red += src_offset;
    src_green += src_offset;
    src_blue += src_offset;

    r += w;
    g += w;
    b += w;
  }
}

static void cdputimagerectrgba_matrix(cdCtxCanvas* ctxcanvas, int iw, int ih, const unsigned char *r, const unsigned char *g, const unsigned char *b, const unsigned char *a, int x, int y, int w, int h, int xmin, int xmax, int ymin, int ymax)
{
  int t_xmin, t_xmax, t_ymin, t_ymax, 
      t_x, t_y, img_topdown = 0, dst_offset;
  float i_x, i_y, xfactor, yfactor;
  unsigned char sr, sg, sb, sa = 255;
  double inv_matrix[6];

  if (h < 0)
  {
    h = -h;
    y -= (h - 1);    /* y is at top-left, move it to bottom-left */
    img_topdown = 1; /* image pointer will start at top-left     */
  }

  /* calculate the destination limits */
  cdImageRGBCalcDstLimits(ctxcanvas->canvas, x, y, w, h, &t_xmin, &t_xmax, &t_ymin, &t_ymax, NULL);

  /* Setup inverse transform */
  cdImageRGBInitInverseTransform(w, h, xmin, xmax, ymin, ymax, &xfactor, &yfactor, ctxcanvas->canvas->matrix, inv_matrix);

  /* for all pixels in the destiny area */
  for(t_y = t_ymin; t_y <= t_ymax; t_y++)
  {
    dst_offset = t_y * ctxcanvas->canvas->w;

    for(t_x = t_xmin; t_x <= t_xmax; t_x++)
    {
      cdImageRGBInverseTransform(t_x, t_y, &i_x, &i_y, xfactor, yfactor, xmin, ymin, x, y, inv_matrix);

      if (i_x > xmin && i_y > ymin && i_x < xmax+1 && i_y < ymax+1)
      {
        if (img_topdown)  /* image is top-bottom */
          i_y = ih-1 - i_y;

        if (t_x == 350 && t_y == 383)
          t_x = 350;

        sr = cdBilinearInterpolation(iw, ih, r, i_x, i_y);
        sg = cdBilinearInterpolation(iw, ih, g, i_x, i_y);
        sb = cdBilinearInterpolation(iw, ih, b, i_x, i_y);
        if (a) sa = cdBilinearInterpolation(iw, ih, a, i_x, i_y);

        if (sr > 210 && sg > 210 && sb > 210)
          sr = sr;

        sCombineRGB(ctxcanvas, t_x + dst_offset, sr, sg, sb, sa);
      }
    }
  }
}

static void cdputimagerectmap_matrix(cdCtxCanvas* ctxcanvas, int iw, int ih, const unsigned char *index, const long int *colors, int x, int y, int w, int h, int xmin, int xmax, int ymin, int ymax)
{
  int t_xmin, t_xmax, t_ymin, t_ymax, 
      t_x, t_y, img_topdown = 0, dst_offset;
  float i_x, i_y, xfactor, yfactor;
  unsigned char si;
  double inv_matrix[6];

  if (h < 0)
  {
    h = -h;
    y -= (h - 1);    /* y is at top-left, move it to bottom-left */
    img_topdown = 1; /* image pointer will start at top-left (undocumented feature)    */
  }

  /* calculate the destination limits */
  cdImageRGBCalcDstLimits(ctxcanvas->canvas, x, y, w, h, &t_xmin, &t_xmax, &t_ymin, &t_ymax, NULL);

  /* Setup inverse transform */
  cdImageRGBInitInverseTransform(w, h, xmin, xmax, ymin, ymax, &xfactor, &yfactor, ctxcanvas->canvas->matrix, inv_matrix);

  /* for all pixels in the destiny area */
  for(t_y = t_ymin; t_y <= t_ymax; t_y++)
  {
    dst_offset = t_y * ctxcanvas->canvas->w;

    for(t_x = t_xmin; t_x <= t_xmax; t_x++)
    {
      cdImageRGBInverseTransform(t_x, t_y, &i_x, &i_y, xfactor, yfactor, xmin, ymin, x, y, inv_matrix);

      if (i_x > xmin && i_y > ymin && i_x < xmax+1 && i_y < ymax+1)
      {
        if (img_topdown)  /* image is top-bottom */
          i_y = ih-1 - i_y;

        si = cdZeroOrderInterpolation(iw, ih, index, i_x, i_y);
        sCombineRGBColor(ctxcanvas, t_x + dst_offset, colors[si]);
      }
    }
  }
}

static void cdputimagerectrgb(cdCtxCanvas* ctxcanvas, int iw, int ih, const unsigned char *r, const unsigned char *g, const unsigned char *b, int x, int y, int w, int h, int xmin, int xmax, int ymin, int ymax)
{
  int l, c, xsize, ysize, xpos, ypos, src_offset, dst_offset, rh, rw, img_topdown = 0;
  const unsigned char *src_red, *src_green, *src_blue;

  if (ctxcanvas->canvas->use_matrix)
  {
    cdputimagerectrgba_matrix(ctxcanvas, iw, ih, r, g, b, NULL, x, y, w, h, xmin, xmax, ymin, ymax);
    return;
  }

  if (h < 0)
  {
    h = -h;
    y -= (h - 1);    /* y is at top-left, move it to bottom-left */
    img_topdown = 1; /* image pointer will start at top-left     */
  }

  /* verifica se esta dentro da area de desenho */
  if (x > (ctxcanvas->canvas->w-1) || y > (ctxcanvas->canvas->h-1) || 
      (x+w) < 0 || (y+h) < 0)
    return;

  xpos = x < 0? 0: x;
  ypos = y < 0? 0: y;

  xsize = (x+w) < (ctxcanvas->canvas->w-1)+1? (x+w) - xpos: (ctxcanvas->canvas->w-1) - xpos + 1;
  ysize = (y+h) < (ctxcanvas->canvas->h-1)+1? (y+h) - ypos: (ctxcanvas->canvas->h-1) - ypos + 1;

  rw = xmax-xmin+1;
  rh = ymax-ymin+1;

  /* testa se tem que fazer zoom */
  if (rw != w || rh != h)
  {
    int* XTab = cdGetZoomTable(w, rw, xmin);
    int* YTab = cdGetZoomTable(h, rh, ymin);

    /* ajusta posicao inicial em destine */
    dst_offset = xpos + ypos * ctxcanvas->canvas->w;

    for(l = 0; l < ysize; l++)
    {
      /* ajusta posicao inicial em source */
      if (img_topdown)
        src_offset = YTab[(ih - 1) - (l + (ypos - y))] * iw;
      else
        src_offset = YTab[l + (ypos - y)] * iw;

      src_red = r + src_offset;
      src_green = g + src_offset;
      src_blue = b + src_offset;

      for(c = 0; c < xsize; c++)
      {
        src_offset = XTab[c + (xpos - x)];
        sCombineRGB(ctxcanvas, c + dst_offset, src_red[src_offset], src_green[src_offset], src_blue[src_offset], 255);
      }

      dst_offset += ctxcanvas->canvas->w;
    }

    free(XTab);
    free(YTab);
  }
  else
  {
    /* ajusta posicao inicial em destine */
    dst_offset = xpos + ypos * ctxcanvas->canvas->w;

    /* ajusta posicao inicial em source */
    if (img_topdown)
      src_offset = (xpos - x + xmin) + ((ih - 1) - (ypos - y + ymin)) * iw;
    else
      src_offset = (xpos - x + xmin) + (ypos - y + ymin) * iw;

    r += src_offset;
    g += src_offset;
    b += src_offset;

    for (l = 0; l < ysize; l++)
    {
      sCombineRGBLine(ctxcanvas, dst_offset, r, g, b, xsize);

      dst_offset += ctxcanvas->canvas->w;

      if (img_topdown)
      {
        r -= iw;
        g -= iw;
        b -= iw;
      }
      else
      {
        r += iw;
        g += iw;
        b += iw;
      }
    }
  }
}

static void cdputimagerectrgba(cdCtxCanvas* ctxcanvas, int iw, int ih, const unsigned char *r, const unsigned char *g, const unsigned char *b, const unsigned char *a, int x, int y, int w, int h, int xmin, int xmax, int ymin, int ymax)
{
  int l, c, xsize, ysize, xpos, ypos, src_offset, dst_offset, rw, rh, img_topdown = 0;
  const unsigned char *src_red, *src_green, *src_blue, *src_alpha;

  if (ctxcanvas->canvas->use_matrix)
  {
    cdputimagerectrgba_matrix(ctxcanvas, iw, ih, r, g, b, a, x, y, w, h, xmin, xmax, ymin, ymax);
    return;
  }

  if (h < 0)
  {
    h = -h;
    y -= (h - 1);    /* y is at top-left, move it to bottom-left */
    img_topdown = 1; /* image pointer will start at top-left     */
  }

  /* verifica se esta dentro da area de desenho */
  if (x > (ctxcanvas->canvas->w-1) || y > (ctxcanvas->canvas->h-1) || 
       (x+w) < 0 || (y+h) < 0)
    return;

  xpos = x < 0? 0: x;
  ypos = y < 0? 0: y;

  xsize = (x+w) < (ctxcanvas->canvas->w-1)+1? (x+w) - xpos: (ctxcanvas->canvas->w-1) - xpos + 1;
  ysize = (y+h) < (ctxcanvas->canvas->h-1)+1? (y+h) - ypos: (ctxcanvas->canvas->h-1) - ypos + 1;

  rw = xmax-xmin+1;
  rh = ymax-ymin+1;

  /* testa se tem que fazer zoom */
  if (rw != w || rh != h)
  {
    int* XTab = cdGetZoomTable(w, rw, xmin);
    int* YTab = cdGetZoomTable(h, rh, ymin);

    /* ajusta posicao inicial em destine */
    dst_offset = xpos + ypos * ctxcanvas->canvas->w;

    for(l = 0; l < ysize; l++)
    {
      /* ajusta posicao inicial em source */
      if (img_topdown)
        src_offset = YTab[(ih - 1) - (l + (ypos - y))] * iw;
      else
        src_offset = YTab[l + (ypos - y)] * iw;

      src_red = r + src_offset;
      src_green = g + src_offset;
      src_blue = b + src_offset;
      src_alpha = a + src_offset;

      for(c = 0; c < xsize; c++)
      {
        src_offset = XTab[c + (xpos - x)];
        sCombineRGB(ctxcanvas, c + dst_offset, src_red[src_offset], src_green[src_offset], src_blue[src_offset], src_alpha[src_offset]);
      }

      dst_offset += ctxcanvas->canvas->w;
    }

    free(XTab);
    free(YTab);
  }
  else
  {
    /* ajusta posicao inicial em destine */
    dst_offset = xpos + ypos * ctxcanvas->canvas->w;

    /* ajusta posicao inicial em source */
    if (img_topdown)
      src_offset = (xpos - x + xmin) + ((ih - 1) - (ypos - y + ymin)) * iw;
    else
      src_offset = (xpos - x + xmin) + (ypos - y + ymin) * iw;

    r += src_offset;
    g += src_offset;
    b += src_offset;
    a += src_offset;

    for (l = 0; l < ysize; l++)
    {
      sCombineRGBALine(ctxcanvas, dst_offset, r, g, b, a, xsize);

      dst_offset += ctxcanvas->canvas->w;

      if (img_topdown)
      {
        r -= iw;
        g -= iw;
        b -= iw;
        a -= iw;
      }
      else
      {
        r += iw;
        g += iw;
        b += iw;
        a += iw;
      }
    }
  }
}

static void cdputimagerectmap(cdCtxCanvas* ctxcanvas, int iw, int ih, const unsigned char *index, const long int *colors, int x, int y, int w, int h, int xmin, int xmax, int ymin, int ymax)
{
  int l, c, xsize, ysize, xpos, ypos, src_offset, dst_offset, rw, rh, pal_size, idx, img_topdown = 0;
  const unsigned char *src_index;

  if (ctxcanvas->canvas->use_matrix)
  {
    cdputimagerectmap_matrix(ctxcanvas, iw, ih, index, colors, x, y, w, h, xmin, xmax, ymin, ymax);
    return;
  }

  if (h < 0)
  {
    h = -h;
    y -= (h - 1);    /* y is at top-left, move it to bottom-left */
    img_topdown = 1; /* image pointer will start at top-left     */
  }

  /* verifica se esta dentro da area de desenho */
  if (x > (ctxcanvas->canvas->w-1) || y > (ctxcanvas->canvas->h-1) || 
       (x+w) < 0 || (y+h) < 0)
    return;

  xpos = x < 0? 0: x;
  ypos = y < 0? 0: y;

  xsize = (x+w) < (ctxcanvas->canvas->w-1)+1? (x+w) - xpos: (ctxcanvas->canvas->w-1) - xpos + 1;
  ysize = (y+h) < (ctxcanvas->canvas->h-1)+1? (y+h) - ypos: (ctxcanvas->canvas->h-1) - ypos + 1;

  rw = xmax-xmin+1;
  rh = ymax-ymin+1;

  /* Como nao sabemos o tamanho da palette a priori, 
  teremos que ver qual o maior indice usado na imagem. */
  pal_size = 0;
  
  for (l=0; l<ih; l++) 
  {
    for (c=0; c<iw; c++) 
    {
      idx = index[l*iw + c];
      if (idx > pal_size)
        pal_size = idx;
    }
  }
  
  pal_size++;

  /* testa se tem que fazer zoom */
  if (rw != w || rh != h)
  {
    int* XTab = cdGetZoomTable(w, rw, xmin);
    int* YTab = cdGetZoomTable(h, rh, ymin);

    /* ajusta posicao inicial em destine */
    dst_offset = xpos + ypos * ctxcanvas->canvas->w;

    for(l = 0; l < ysize; l++)
    {
      /* ajusta posicao inicial em source */
      if (img_topdown)
        src_offset = YTab[(ih - 1) - (l + (ypos - y))] * iw;
      else
        src_offset = YTab[l + (ypos - y)] * iw;

      src_index = index + src_offset;

      for(c = 0; c < xsize; c++)
      {
        src_offset = XTab[c + (xpos - x)];
        idx = src_index[src_offset];
        sCombineRGBColor(ctxcanvas, c + dst_offset, colors[idx]);
      }

      dst_offset += ctxcanvas->canvas->w;
    }

    free(XTab);
    free(YTab);
  }
  else
  {
    /* ajusta posicao inicial em destine */
    dst_offset = xpos + ypos * ctxcanvas->canvas->w;

    /* ajusta posicao inicial em source */
    if (img_topdown)
      src_offset = (xpos - x + xmin) + ((ih - 1) - (ypos - y + ymin)) * iw;
    else
      src_offset = (xpos - x + xmin) + (ypos - y + ymin) * iw;

    index += src_offset;

    for (l = 0; l < ysize; l++)
    {
      for(c = 0; c < xsize; c++)
      {
        idx = index[c];
        sCombineRGBColor(ctxcanvas, c + dst_offset, colors[idx]);
      }

      dst_offset += ctxcanvas->canvas->w;

      if (img_topdown)
        index -= iw;
      else
        index += iw;
    }
  }
}

static void cdpixel(cdCtxCanvas* ctxcanvas, int x, int y, long int color)
{
  int offset;

  if (ctxcanvas->canvas->use_matrix)
    cdMatrixTransformPoint(ctxcanvas->canvas->matrix, x, y, &x, &y);

  offset = ctxcanvas->canvas->w * y + x;

  /* verifica se esta dentro da area de desenho */
  if (x < 0 ||
      x > (ctxcanvas->canvas->w-1) ||
      y < 0 ||
      y > (ctxcanvas->canvas->h-1))
  return;

  sCombineRGBColor(ctxcanvas, offset, color);
}

static cdCtxImage* cdcreateimage(cdCtxCanvas* ctxcanvas, int w, int h)
{
  cdCtxImage* ctximage;
  int size = w * h;
  int num_c = ctxcanvas->alpha? 4: 3;

  ctximage = (cdCtxImage*)malloc(sizeof(cdCtxImage));
  memset(ctximage, 0, sizeof(cdCtxImage));

  ctximage->w = w;
  ctximage->h = h;

  ctximage->red = (unsigned char*) malloc(num_c*size);
  if (!ctximage->red)
  {
    free(ctximage);
    return NULL;
  }

  ctximage->green = ctximage->red + size;
  ctximage->blue = ctximage->red + 2*size;
  if (ctxcanvas->alpha)
    ctximage->alpha = ctximage->red + 3*size;

  memset(ctximage->red, 0xFF, 3*size);  /* white */
  if (ctximage->alpha) memset(ctximage->alpha, 0, size);  /* transparent, this is the normal alpha coding */

  return ctximage;
}

static void cdgetimage(cdCtxCanvas* ctxcanvas, cdCtxImage* ctximage, int x, int y)
{
  unsigned char *r, *g, *b, *a = NULL;
  int w, h, dst_offset, src_offset, l, xsize, ysize, xpos, ypos, do_alpha = 0;
  unsigned char *src_red, *src_green, *src_blue, *src_alpha = NULL;

  w = ctximage->w;
  h = ctximage->h;

  if (x >= ctxcanvas->canvas->w || y >= ctxcanvas->canvas->h || 
      x + w < 0 || y + h < 0)
    return;

  if (ctximage->alpha && ctxcanvas->alpha)
    do_alpha = 1;

  r = ctximage->red;
  g = ctximage->green;
  b = ctximage->blue;
  if (do_alpha) a = ctximage->alpha;

  /* ajusta parametros de entrada */
  xpos = _sNormX(ctxcanvas, x);
  ypos = _sNormY(ctxcanvas, y);

  xsize = w < (ctxcanvas->canvas->w - xpos)? w: ctxcanvas->canvas->w - xpos;
  ysize = h < (ctxcanvas->canvas->h - ypos)? h: ctxcanvas->canvas->h - ypos;

  /* ajusta posicao inicial em source */
  src_offset = xpos + ypos * ctxcanvas->canvas->w;
  src_red = ctxcanvas->red + src_offset;
  src_green = ctxcanvas->green + src_offset;
  src_blue = ctxcanvas->blue + src_offset;
  if (do_alpha) src_alpha = ctxcanvas->alpha + src_offset;

  /* offset para source */
  src_offset = ctxcanvas->canvas->w;

  /* ajusta posicao inicial em destine */
  dst_offset = (xpos - x) + (ypos - y) * w;
  r += dst_offset;
  g += dst_offset;
  b += dst_offset;
  if (do_alpha) a += dst_offset;

  for (l = 0; l < ysize; l++)
  {
    memcpy(r, src_red, xsize);
    memcpy(g, src_green, xsize);
    memcpy(b, src_blue, xsize);
    if (do_alpha) memcpy(a, src_alpha, xsize);

    src_red += src_offset;
    src_green += src_offset;
    src_blue += src_offset;
    if (do_alpha) src_alpha += src_offset;

    r += w;
    g += w;
    b += w;
    if (do_alpha) a += w;
  }
}

static void cdputimagerect(cdCtxCanvas* ctxcanvas, cdCtxImage* ctximage, int x, int y, int xmin, int xmax, int ymin, int ymax)
{
  int iw, ih, w, h;
  unsigned char *r, *g, *b, *a;
  int l, xsize, ysize, xpos, ypos, src_offset, dst_offset;

  iw = ctximage->w;
  ih = ctximage->h;

  r = ctximage->red;
  g = ctximage->green;
  b = ctximage->blue;
  a = ctximage->alpha;

  w = xmax-xmin+1;
  h = ymax-ymin+1;

  /* verifica se esta dentro da area de desenho */
  if (x > (ctxcanvas->canvas->w-1) || y > (ctxcanvas->canvas->h-1) || 
      x + w < 0 || y + h < 0)
    return;

  xpos = x;
  ypos = y;

  if (ypos < 0) ypos = 0;
  if (xpos < 0) xpos = 0;

  xsize = w < ((ctxcanvas->canvas->w-1)+1 - xpos)? w: ((ctxcanvas->canvas->w-1)+1 - xpos);
  ysize = h < ((ctxcanvas->canvas->h-1)+1 - ypos)? h: ((ctxcanvas->canvas->h-1)+1 - ypos);

  /* ajusta posicao inicial em destine */
  dst_offset = xpos + ypos * ctxcanvas->canvas->w;

  /* ajusta posicao inicial em source */
  src_offset = ((xpos - x) + xmin) + ((ypos - y) + ymin) * iw;
  r += src_offset;
  g += src_offset;
  b += src_offset;
  if (a) a += src_offset;

  for (l = 0; l < ysize; l++)
  {
    if (a)
      sCombineRGBALine(ctxcanvas, dst_offset, r, g, b, a, xsize);
    else
      sCombineRGBLine(ctxcanvas, dst_offset, r, g, b, xsize);

    dst_offset += ctxcanvas->canvas->w;

    r += iw;
    g += iw;
    b += iw;
    if (a) a += iw;
  }
}

static void cdkillimage(cdCtxImage* ctximage)
{
  free(ctximage->red);
  memset(ctximage, 0, sizeof(cdCtxImage));
  free(ctximage);
}

static void cdscrollarea(cdCtxCanvas* ctxcanvas, int xmin, int xmax, int ymin, int ymax, int dx, int dy)
{
  int l;
  long src_offset, dst_offset;
  int incx,incy, xsize, ysize;
  int dst_xmin, dst_xmax, dst_ymin, dst_ymax;

  /* corrige valores de entrada */

  xmin = _sNormX(ctxcanvas, xmin);
  ymin = _sNormY(ctxcanvas, ymin);
  xmax = _sNormX(ctxcanvas, xmax);
  ymax = _sNormY(ctxcanvas, ymax);

  dst_xmin = xmin + dx;
  dst_ymin = ymin + dy;
  dst_xmax = xmax + dx;
  dst_ymax = ymax + dy;

  /* verifica se esta dentro da area de desenho */
  if (dst_xmin > (ctxcanvas->canvas->w-1) || dst_ymin > (ctxcanvas->canvas->h-1) || 
      dst_xmax < 0 || dst_ymax < 0)
    return;

  if (dst_ymin < 0) dst_ymin = 0;
  if (dst_xmin < 0) dst_xmin = 0;

  if (dst_ymax > (ctxcanvas->canvas->h-1)) dst_ymax = (ctxcanvas->canvas->h-1);
  if (dst_xmax > (ctxcanvas->canvas->w-1)) dst_xmax = (ctxcanvas->canvas->w-1);

  if (dst_xmin > dst_xmax || dst_ymin > dst_ymax)
    return;

  /* Decide de onde vai comecar a copiar, isto e' necessario pois pode haver 
     uma intersecao entre a imagem original e a nova imagem, assim devo 
     garantir que nao estou colocando um ponto, em cima de um ponto ainda nao
     lido da imagem antiga. */

  xsize = dst_xmax - dst_xmin + 1;
  ysize = dst_ymax - dst_ymin + 1;

  /* sentido de copia da direita para a esquerda ou ao contrario. */
  if (dx < 0)
  {
    incx = 1;
    dst_offset = dst_xmin;
    src_offset = xmin;
  }
  else
  {
    incx = -1;
    dst_offset = dst_xmax;
    src_offset = xmax;
  }

  /* sentido de copia de cima para baixo ou ao contrario. */
  if (dy < 0)
  {
    incy = ctxcanvas->canvas->w;
    dst_offset += dst_ymin * ctxcanvas->canvas->w;
    src_offset += ymin * ctxcanvas->canvas->w;
  }
  else
  {
    incy = -(ctxcanvas->canvas->w);
    dst_offset += dst_ymax * ctxcanvas->canvas->w;
    src_offset += ymax * ctxcanvas->canvas->w;
  }

  xsize *= incx;

  for (l = 0; l < ysize; l++)
  {
    sCombineRGBLine(ctxcanvas, dst_offset, ctxcanvas->red + src_offset, ctxcanvas->green + src_offset, ctxcanvas->blue + src_offset, xsize);
    dst_offset += incy;
    src_offset += incy;
  }
}

static char* get_green_attrib(cdCtxCanvas* ctxcanvas)
{
  return (char*)ctxcanvas->green;
}

static cdAttribute green_attrib =
{
  "GREENIMAGE",
  NULL,
  get_green_attrib
}; 

static char* get_blue_attrib(cdCtxCanvas* ctxcanvas)
{
  return (char*)ctxcanvas->blue;
}

static cdAttribute blue_attrib =
{
  "BLUEIMAGE",
  NULL,
  get_blue_attrib
}; 

static char* get_red_attrib(cdCtxCanvas* ctxcanvas)
{
  return (char*)ctxcanvas->red;
}

static cdAttribute red_attrib =
{
  "REDIMAGE",
  NULL,
  get_red_attrib
}; 

static char* get_alpha_attrib(cdCtxCanvas* ctxcanvas)
{
  return (char*)ctxcanvas->alpha;
}

static cdAttribute alpha_attrib =
{
  "ALPHAIMAGE",
  NULL,
  get_alpha_attrib
}; 

static void set_aa_attrib(cdCtxCanvas* ctxcanvas, char* data)
{
  if (!data || data[0] == '0')
    ctxcanvas->canvas->simulation->antialias = 0;
  else
    ctxcanvas->canvas->simulation->antialias = 1;
}

static char* get_aa_attrib(cdCtxCanvas* ctxcanvas)
{
  if (ctxcanvas->canvas->simulation->antialias)
    return "0";
  else
    return "1";
}

static cdAttribute aa_attrib =
{
  "ANTIALIAS",
  set_aa_attrib,
  get_aa_attrib
}; 

static void set_rotate_attrib(cdCtxCanvas* ctxcanvas, char* data)
{
  if (data)
  {
    sscanf(data, "%g %d %d", &ctxcanvas->rotate_angle,
                             &ctxcanvas->rotate_center_x,
                             &ctxcanvas->rotate_center_y);

    cdCanvasTransformTranslate(ctxcanvas->canvas, ctxcanvas->rotate_center_x, ctxcanvas->rotate_center_y);
    cdCanvasTransformRotate(ctxcanvas->canvas, ctxcanvas->rotate_angle);
    cdCanvasTransformTranslate(ctxcanvas->canvas, -ctxcanvas->rotate_center_x, -ctxcanvas->rotate_center_y);
  }
  else
  {
    ctxcanvas->rotate_angle = 0;
    ctxcanvas->rotate_center_x = 0;
    ctxcanvas->rotate_center_y = 0;

    cdCanvasTransform(ctxcanvas->canvas, NULL);
  }
}

static char* get_rotate_attrib(cdCtxCanvas* ctxcanvas)
{
  static char data[100];

  if (!ctxcanvas->rotate_angle)
    return NULL;

  sprintf(data, "%g %d %d", (double)ctxcanvas->rotate_angle,
                            ctxcanvas->rotate_center_x,
                            ctxcanvas->rotate_center_y);

  return data;
}

static cdAttribute rotate_attrib =
{
  "ROTATE",
  set_rotate_attrib,
  get_rotate_attrib
}; 

static void cdcreatecanvas(cdCanvas* canvas, void *data)
{
  cdCtxCanvas* ctxcanvas;
  int w = 0, h = 0, use_alpha = 0;
  float res = (float)3.78;
  unsigned char *r = NULL, *g = NULL, *b = NULL, *a = NULL;
  char* str_data = (char*)data;
  char* res_ptr = NULL;

  if (strstr(str_data, "-a"))
    use_alpha = 1;

  res_ptr = strstr(str_data, "-r");
  if (res_ptr)
    sscanf(res_ptr+2, "%g", &res);

  /* size and rgb */
#ifdef SunOS_OLD
  if (use_alpha)
    sscanf(str_data, "%dx%d %d %d %d %d", &w, &h, &r, &g, &b, &a);
  else
    sscanf(str_data, "%dx%d %d %d %d", &w, &h, &r, &g, &b);
#else
  if (use_alpha)
    sscanf(str_data, "%dx%d %p %p %p %p", &w, &h, &r, &g, &b, &a);
  else
    sscanf(str_data, "%dx%d %p %p %p", &w, &h, &r, &g, &b);
#endif

  if (w == 0 || h == 0)
    return;

  ctxcanvas = (cdCtxCanvas *)malloc(sizeof(cdCtxCanvas));
  memset(ctxcanvas, 0, sizeof(cdCtxCanvas));

  canvas->w = w;
  canvas->h = h;
  canvas->yres = res;
  canvas->xres = res;
  canvas->w_mm = ((double)w) / res;
  canvas->h_mm = ((double)h) / res;
  if (use_alpha)
    canvas->bpp = 32;
  else
    canvas->bpp = 24;

  if (r && g && b)
  {
    ctxcanvas->user_image = 1;

    ctxcanvas->red = r;
    ctxcanvas->green = g;
    ctxcanvas->blue = b;
    ctxcanvas->alpha = a;
  }
  else
  {
    int size = w * h;
    int num_c = use_alpha? 4: 3;

    ctxcanvas->user_image = 0;

    ctxcanvas->red = (unsigned char*)malloc(num_c*size);
    if (!ctxcanvas->red)
    {
      free(ctxcanvas);
      return;
    }

    ctxcanvas->green = ctxcanvas->red + size;
    ctxcanvas->blue = ctxcanvas->red + 2*size;
    if (use_alpha) 
      ctxcanvas->alpha = ctxcanvas->red + 3*size;

    memset(ctxcanvas->red, 0xFF, 3*size);  /* white */
    if (ctxcanvas->alpha) memset(ctxcanvas->alpha, 0, size);  /* transparent, this is the normal alpha coding */
  }

  ctxcanvas->clip = (unsigned char*)malloc(w*h);
  memset(ctxcanvas->clip, 1, w*h);  /* CD_CLIPOFF */

  canvas->ctxcanvas = ctxcanvas;
  ctxcanvas->canvas = canvas;

  cdSimInitText(canvas->simulation); 
  /* nao preciso inicializar a fonte,
     pois isso sera' feito na inicializacao dos atributos default do driver */

  canvas->simulation->antialias = 1;

  cdRegisterAttribute(canvas, &red_attrib);
  cdRegisterAttribute(canvas, &green_attrib);
  cdRegisterAttribute(canvas, &blue_attrib);
  cdRegisterAttribute(canvas, &alpha_attrib);
  cdRegisterAttribute(canvas, &aa_attrib);
  cdRegisterAttribute(canvas, &rotate_attrib);
}

static void cdinittable(cdCanvas* canvas)
{
  cdSimulation* sim;

  /* initialize function table*/
  canvas->cxClip = cdclip;
  canvas->cxClipArea = cdcliparea;
  canvas->cxNewRegion = cdnewregion;
  canvas->cxIsPointInRegion = cdispointinregion;
  canvas->cxOffsetRegion = cdoffsetregion;
  canvas->cxGetRegionBox = cdgetregionbox;

  canvas->cxPutImageRectRGB = cdputimagerectrgb;
  canvas->cxPutImageRectRGBA = cdputimagerectrgba;
  canvas->cxPutImageRectMap = cdputimagerectmap;
  canvas->cxGetImageRGB = cdgetimagergb;

  canvas->cxCreateImage = cdcreateimage;
  canvas->cxGetImage = cdgetimage; 
  canvas->cxPutImageRect = cdputimagerect; 
  canvas->cxKillImage = cdkillimage;
  canvas->cxScrollArea = cdscrollarea;

  canvas->cxClear = cdclear;
  canvas->cxPixel = cdpixel;

  canvas->cxLine = cdlineSIM;
  canvas->cxRect = cdrectSIM;
  canvas->cxBox = cdbox;
  canvas->cxArc = cdarcSIM;
  canvas->cxSector = cdsector;
  canvas->cxChord = cdchord;
  canvas->cxPoly = cdpoly;
  canvas->cxText = cdtext;

  canvas->cxKillCanvas = cdkillcanvas;

  /* use simulation */
  canvas->cxFont = cdfontSIM;
  canvas->cxGetFontDim = cdgetfontdimSIM;
  canvas->cxGetTextSize = cdgettextsizeSIM;

  sim = canvas->simulation;

  sim->SolidLine   = irgbSolidLine;
  sim->PatternLine = irgbPatternLine; 
  sim->StippleLine = irgbStippleLine; 
  sim->HatchLine   = irgbHatchLine;   
}

static cdContext cdImageRGBContext =
{
  CD_CAP_ALL & ~(CD_CAP_FLUSH | CD_CAP_PLAY | CD_CAP_FPRIMTIVES |
                 CD_CAP_LINECAP | CD_CAP_LINEJOIN | CD_CAP_REGION |
                 CD_CAP_PALETTE | CD_CAP_TEXTORIENTATION),
  0,
  cdcreatecanvas,
  cdinittable,
  NULL,
  NULL,
};
 
cdContext* cdContextImageRGB(void)
{
  return &cdImageRGBContext;
}

static void cdflushDB(cdCtxCanvas *ctxcanvas)
{
  int old_writemode;
  cdCanvas* canvas_dbuffer = ctxcanvas->canvas_dbuffer;

  /* this is done in the canvas_dbuffer context */

  /* Flush can be affected by Origin and Clipping, but not WriteMode */

  old_writemode = cdCanvasWriteMode(canvas_dbuffer, CD_REPLACE);
  cdCanvasPutImageRectRGB(canvas_dbuffer, ctxcanvas->canvas->w, ctxcanvas->canvas->h, ctxcanvas->red, ctxcanvas->green, ctxcanvas->blue, 0, 0, ctxcanvas->canvas->w, ctxcanvas->canvas->h, 0, 0, 0, 0);
  cdCanvasWriteMode(canvas_dbuffer, old_writemode);
}

static void cdcreatecanvasDB(cdCanvas* canvas, cdCanvas* canvas_dbuffer)
{
  char rgbdata[100];
  sprintf(rgbdata, "%dx%d -r%g", canvas_dbuffer->w, canvas_dbuffer->h, canvas_dbuffer->xres);
  cdcreatecanvas(canvas, rgbdata);
  if (canvas->ctxcanvas)
    canvas->ctxcanvas->canvas_dbuffer = canvas_dbuffer;
}

static int cdactivateDB(cdCtxCanvas *ctxcanvas)
{
  cdCanvas* canvas_dbuffer = ctxcanvas->canvas_dbuffer;

  /* this is done in the canvas_dbuffer context */
  /* this will update canvas size */
  cdCanvasActivate(canvas_dbuffer);

  /* check if the size changed */
  if (canvas_dbuffer->w != ctxcanvas->canvas->w ||
      canvas_dbuffer->h != ctxcanvas->canvas->h)
  {
    cdCanvas* canvas = ctxcanvas->canvas;
    /* save the current, if the rebuild fail */
    cdCtxCanvas* old_ctxcanvas = ctxcanvas;

    /* if the image is rebuild, the canvas that uses the image must be also rebuild */

    /* rebuild the image and the canvas */
    canvas->ctxcanvas = NULL;
    cdcreatecanvasDB(canvas, canvas_dbuffer);
    if (!canvas->ctxcanvas)
    {
      canvas->ctxcanvas = old_ctxcanvas;
      return CD_ERROR;
    }

    /* remove the old image and canvas */
    cdkillcanvas(old_ctxcanvas);

    ctxcanvas = canvas->ctxcanvas;

    /* update canvas attributes */
    if (canvas->cxBackground) canvas->cxBackground(ctxcanvas, canvas->background);
    if (canvas->cxForeground) canvas->cxForeground(ctxcanvas, canvas->foreground);
    if (canvas->cxBackOpacity) canvas->cxBackOpacity(ctxcanvas, canvas->back_opacity);
    if (canvas->cxWriteMode) canvas->cxWriteMode(ctxcanvas, canvas->write_mode);
    if (canvas->cxLineStyle) canvas->cxLineStyle(ctxcanvas, canvas->line_style);
    if (canvas->cxLineWidth) canvas->cxLineWidth(ctxcanvas, canvas->line_width);
    if (canvas->cxLineCap) canvas->cxLineCap(ctxcanvas, canvas->line_cap);
    if (canvas->cxLineJoin) canvas->cxLineJoin(ctxcanvas, canvas->line_join);
    if (canvas->cxHatch) canvas->cxHatch(ctxcanvas, canvas->hatch_style);
    if (canvas->stipple && canvas->cxStipple) canvas->cxStipple(ctxcanvas, canvas->stipple_w, canvas->stipple_h, canvas->stipple);
    if (canvas->pattern && canvas->cxPattern) canvas->cxPattern(ctxcanvas, canvas->pattern_w, canvas->pattern_h, canvas->pattern);
    if (canvas->cxInteriorStyle) canvas->cxInteriorStyle(ctxcanvas, canvas->interior_style);
    if (canvas->native_font[0] && canvas->cxNativeFont)
      canvas->cxNativeFont(ctxcanvas, canvas->native_font);
    else if (canvas->cxFont) canvas->cxFont(ctxcanvas, canvas->font_type_face, canvas->font_style, canvas->font_size);
    if (canvas->cxTextAlignment) canvas->cxTextAlignment(ctxcanvas, canvas->text_alignment);
    if (canvas->cxTextOrientation) canvas->cxTextOrientation(ctxcanvas, canvas->text_orientation);
    if (canvas->use_matrix && canvas->cxTransform) canvas->cxTransform(ctxcanvas, canvas->matrix);
    if (canvas->clip_mode == CD_CLIPAREA && canvas->cxClipArea) canvas->cxClipArea(ctxcanvas, canvas->clip_rect.xmin, canvas->clip_rect.xmax, canvas->clip_rect.ymin, canvas->clip_rect.ymax);
    if (canvas->clip_mode == CD_CLIPAREA && canvas->cxFClipArea) canvas->cxFClipArea(ctxcanvas, canvas->clip_frect.xmin, canvas->clip_frect.xmax, canvas->clip_frect.ymin, canvas->clip_frect.ymax);
    if (canvas->clip_mode == CD_CLIPPOLYGON && canvas->clip_poly) canvas->cxPoly(ctxcanvas, CD_CLIP, canvas->clip_poly, canvas->clip_poly_n);
    if (canvas->clip_mode == CD_CLIPPOLYGON && canvas->clip_fpoly) canvas->cxFPoly(ctxcanvas, CD_CLIP, canvas->clip_fpoly, canvas->clip_poly_n);
    if (canvas->clip_mode != CD_CLIPOFF && canvas->cxClip) canvas->cxClip(ctxcanvas, canvas->clip_mode);
  }

  return CD_OK;
}

static void cddeactivateDB(cdCtxCanvas *ctxcanvas)
{
  cdCanvas* canvas_dbuffer = ctxcanvas->canvas_dbuffer;
  /* this is done in the canvas_dbuffer context */
  cdCanvasDeactivate(canvas_dbuffer);
}

static void cdinittableDB(cdCanvas* canvas)
{
  cdinittable(canvas);

  canvas->cxActivate = cdactivateDB;
  canvas->cxDeactivate = cddeactivateDB;

  canvas->cxFlush = cdflushDB;
}

static cdContext cdDBufferRGBContext =
{
  CD_CAP_ALL & ~(CD_CAP_PLAY | CD_CAP_FPRIMTIVES | 
                 CD_CAP_LINECAP | CD_CAP_LINEJOIN | CD_CAP_REGION |
                 CD_CAP_PALETTE | CD_CAP_TEXTORIENTATION),
  0,
  cdcreatecanvasDB,  
  cdinittableDB,
  NULL,             
  NULL, 
};

cdContext* cdContextDBufferRGB(void)
{
  return &cdDBufferRGBContext;
}