// @(#)root/asimage:$Name:  $:$Id: TASImage.cxx,v 1.47 2005/09/04 15:41:19 rdm Exp $
// Author: Fons Rademakers, Reiner Rohlfs, Valeriy Onuchin   28/11/2001

/*************************************************************************
 * Copyright (C) 1995-2001, Rene Brun, Fons Rademakers and Reiner Rohlfs *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

/**************************************************************************
 * Some parts of this source are based on libAfterImage 2.00.00
 *          (http://www.afterstep.org/)
 *
 * Copyright (c) 2002 Sasha Vasko <sasha@aftercode.net>
 * Copyright (c) 1998, 1999 Ethan Fischer <allanon@crystaltokyo.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Library General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
**************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TASImage                                                             //
//                                                                      //
// Interface to image processing library using libAfterImage.           //
// It allows reading and writing of images in different                 //
// formats, several image manipulations (scaling, tiling, merging,      //
// etc.) and displaying in pads.                                        //
// The size of the image on the screen does not depend on the original  //
// size of the image but on the size of the pad. Therefore it is very   //
// easy to resize the image on the screen by resizing the pad.          //
//                                                                      //
// Besides reading an image from a file an image can be defined by a    //
// two dimensional array of values. A palette defines the color of      //
// each value.                                                          //
//                                                                      //
// The image can be zoomed by defining a rectangle with the mouse.      //
// The color palette can be modified with a GUI, just select            //
// StartPaletteEditor() from the context menu.                          //
//                                                                      //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TASImage.h"
#include "TASImagePlugin.h"
#include "TROOT.h"
#include "TSystem.h"
#include "TVirtualX.h"
#include "TCanvas.h"
#include "TArrayD.h"
#include "TVectorD.h"
#include "TVirtualPS.h"
#include "TGaxis.h"
#include "TColor.h"
#include "TObjArray.h"
#include "TArrayL.h"
#include "TPoint.h"
#include "TFrame.h"
#include "TTF.h"
#include "TRandom.h"
#include "Riostream.h"
#include "THashTable.h"
#include "TPluginManager.h"

#ifndef WIN32
#   include <X11/Xlib.h>
#else
#   include "Windows4root.h"
#endif
extern "C" {
#ifndef WIN32
#   include <afterbase.h>
#else
#   include <win32/config.h>
#   include <win32/afterbase.h>
#   define X_DISPLAY_MISSING 1
#endif
#   include <afterimage.h>
#   include <bmp.h>
#   include <draw.h>

    extern Display *dpy;    // defined in afterbase.c
}

// auxilary functions for general polygon filling
#include "TASPolyUtils.c"


ASVisual *TASImage::fgVisual;
Bool_t TASImage::fgInit = kFALSE;

static ASFontManager *gFontManager = 0;
static unsigned long kAllPlanes = ~0;
THashTable *TASImage::fgPlugList = new THashTable(50);

///////////////////////////// alphablending macros ///////////////////////////////
typedef struct {
   unsigned char b;
   unsigned char g;
   unsigned char r;
   unsigned char a;
} __argb32__;

static __argb32__ *t = new __argb32__;
static __argb32__ *b = new __argb32__;

//______________________________________________________________________________
#define _alphaBlend(bot, top) do {\
   t = (__argb32__*)(top);\
   b = (__argb32__*)(bot);\
   int aa = 255-t->a;\
   if (!aa) {\
      *bot = *top;\
      break;\
   }\
   b->a = (b->a*aa)>>8 + t->a;\
   b->r = (b->r*aa + t->r*t->a)>>8;\
   b->g = (b->g*aa + t->g*t->a)>>8;\
   b->b = (b->b*aa + t->b*t->a)>>8;\
} while (0)



ClassImp(TASImage)
ClassImp(TASImagePlugin)

//______________________________________________________________________________
void TASImage::DestroyImage()
{
   // helper class

   if (fImage) {
      destroy_asimage(&fImage);
   }

   if (fIsGray && fGrayImage) {
      destroy_asimage(&fGrayImage);
   }

   fIsGray     = kFALSE;
   fGrayImage  = 0;
   fImage      = 0;
   fPic        = 0;
   fMask       = 0;
}

//______________________________________________________________________________
void TASImage::SetDefaults()
{
   // set default parameters

   fImage         = 0;
   fScaledImage   = 0;
   fEditable      = kFALSE;
   fPaintMode     = 1;
   fZoomOffX      = 0;
   fZoomOffY      = 0;
   fZoomWidth     = 0;
   fZoomHeight    = 0;
   fZoomUpdate    = kZoomOps;
   fPic           = 0;
   fMask          = 0;
   fGrayImage     = 0;
   fIsGray        = kFALSE;
   fPaletteEnabled = kFALSE;

   if (!fgInit) {
      set_application_name((char*)(gProgName ? gProgName : "ROOT"));
      fgInit = kTRUE;
   }
}

//______________________________________________________________________________
TASImage::TASImage()
{
   // Default image ctor.

   SetDefaults();
}

//______________________________________________________________________________
TASImage::TASImage(UInt_t w, UInt_t h) : TImage(w, h)
{
   // create an empty image

   SetDefaults();
   fImage = create_asimage(w ? w : 20, h ? h : 20, 0);
   UnZoom();
}

//______________________________________________________________________________
TASImage::TASImage(const char *file, EImageFileTypes) : TImage(file)
{
   // Create an image object and read from specified file.
   // For more information see description of function ReadImage()
   // which is called by this constructor.

   SetDefaults();
   ReadImage(file);
}

//______________________________________________________________________________
TASImage::TASImage(const char *name, const Double_t *imageData, UInt_t width,
                   UInt_t height, TImagePalette *palette) : TImage(name)
{
   // Creates an image depending on the values of imageData.
   // For more information see function SetImage() which is called
   // by this constructor.

   SetDefaults();
   SetImage(imageData, width, height, palette);
}

//______________________________________________________________________________
TASImage::TASImage(const char *name, const TArrayD &imageData, UInt_t width,
                   TImagePalette *palette) : TImage(name)
{
   // Creates an image depending on the values of imageData. The size
   // of the image is width X (imageData.fN / width).
   // For more information see function SetImage() which is called by
   // this constructor.

   SetDefaults();
   SetImage(imageData, width, palette);
}

//______________________________________________________________________________
TASImage::TASImage(const char *name, const TVectorD &imageData, UInt_t width,
                   TImagePalette *palette) : TImage(name)
{
   // Creates an image depending on the values of imageData. The size
   // of the image is width X (imageData.fN / width).
   // For more information see function SetImage() which is called by
   // this constructor.

   SetDefaults();
   SetImage(imageData, width, palette);
}

//______________________________________________________________________________
TASImage::TASImage(const TASImage &img) : TImage(img)
{
   // Image copy ctor.

   fImage         = 0;
   fPaintMode     = 1;

   if (img.IsValid()) {
      fImage = clone_asimage(img.fImage, SCL_DO_ALL);
      fScaledImage   = fScaledImage ? (TASImage*)img.fScaledImage->Clone("") : 0;
      fGrayImage     = fGrayImage ? clone_asimage(img.fGrayImage, SCL_DO_ALL) : 0;

      if (img.fImage->alt.vector) {
         Int_t size = img.fImage->width * img.fImage->height * sizeof(double);
         fImage->alt.vector = (double*)malloc(size);
         memcpy(fImage->alt.vector, img.fImage->alt.vector, size);
      }

      fZoomUpdate = kNoZoom;
      fZoomOffX   = img.fZoomOffX;
      fZoomOffY   = img.fZoomOffY;
      fZoomWidth  = img.fZoomWidth;
      fZoomHeight = img.fZoomHeight;
      fEditable   = img.fEditable;
      fIsGray     = img.fIsGray;
   }
}

//______________________________________________________________________________
TASImage &TASImage::operator=(const TASImage &img)
{
   // Image assignment operator.

   if (this != &img && img.IsValid()) {
      TImage::operator=(img);

      DestroyImage();
      delete fScaledImage;
      fImage = clone_asimage(img.fImage, SCL_DO_ALL);
      fScaledImage = fScaledImage ? (TASImage*)img.fScaledImage->Clone("") : 0;
      fGrayImage = fGrayImage ? clone_asimage(img.fGrayImage, SCL_DO_ALL) : 0;

      if (img.fImage->alt.vector) {
         Int_t size = img.fImage->width * img.fImage->height * sizeof(double);
         fImage->alt.vector = (double*)malloc(size);
         memcpy(fImage->alt.vector, img.fImage->alt.vector, size);
      }

      fScaledImage = img.fScaledImage ? (TASImage*)img.fScaledImage->Clone("") : 0;
      fZoomUpdate = kNoZoom;
      fZoomOffX   = img.fZoomOffX;
      fZoomOffY   = img.fZoomOffY;
      fZoomWidth  = img.fZoomWidth;
      fZoomHeight = img.fZoomHeight;
      fEditable   = img.fEditable;
      fIsGray     = img.fIsGray;
      fPaintMode  = 1;
   }

   return *this;
}

//______________________________________________________________________________
TASImage::~TASImage()
{
   // Image dtor, clean up image and visual.

   DestroyImage();
   delete fScaledImage;
   fScaledImage = 0;
}

//______________________________________________________________________________
void TASImage::ReadImage(const char *filename, EImageFileTypes /*type*/)
{
   // Read specified image file. The file type is determined by
   // the file extension (the type argument is ignored). It will
   // attempt to append .gz and then .Z to the filename and find such
   // a file. If the filename ends with extension consisting of digits
   // only, it will attempt to find the file with this extension stripped
   // off. On success this extension will be used to load subimage from
   // the file with that number. Subimage is supported only for GIF files.
   //
   // It is also possible to put XPM raw string (see also SetImageBuffer) as
   // the first input parameter ("filename"), such string  is returned by
   // GetImageBuffer method.

   Bool_t xpm = filename && (filename[0] == '/' &&
                filename[1] == '*') && filename[2] == ' ';

   if (xpm) {  // XPM strings in-memory array
      SetImageBuffer((char**)&filename, TImage::kXpm);
      fName = "XPM_image";
      return;
   }

   ASImage *image = file2ASImage(filename, 0, SCREEN_GAMMA, GetImageCompression(), 0);

   if (!image) {  // try to read it via plugin
      TString ext = strrchr(filename, '.') + 1;

      if (ext.IsNull()) {
         return;
      }
      ext.ToLower();
      ext.Strip();
      UInt_t w = 0;
      UInt_t h = 0;
      unsigned char *bitmap = 0;

      TImagePlugin *plug = (TImagePlugin*)fgPlugList->FindObject(ext.Data());

      if (!plug) {
         TPluginHandler *handler = gROOT->GetPluginManager()->FindHandler("TImagePlugin", ext.Data());
         if (!handler || ((handler->LoadPlugin() == -1))) {
            return;
         }
         plug = (TImagePlugin*)handler->ExecPlugin(1, ext.Data());

         if (!plug) {
            return;
         }

         fgPlugList->Add(plug);
      }

      if (plug) {
         if (plug->InheritsFrom(TASImagePlugin::Class())) {
            image = ((TASImagePlugin*)plug)->File2ASImage(filename);
            if (image) goto end;
         }
         bitmap = plug->ReadFile(filename, w, h);
         if (bitmap) {
            image = bitmap2asimage(bitmap, w, h, 0, 0);
         }
         if (!image) {
            return;
         }
      }
   }

end:
   fName.Form("%s.", gSystem->BaseName(filename));

   DestroyImage();
   delete fScaledImage;
   fScaledImage = 0;

   fImage      = image;
   fZoomUpdate = kNoZoom;
   fEditable   = kFALSE;
   fZoomOffX   = 0;
   fZoomOffY   = 0;
   fZoomWidth  = fImage->width;
   fZoomHeight = fImage->height;
   fPaintMode  = 1;
}

//______________________________________________________________________________
void TASImage::WriteImage(const char *file, EImageFileTypes type)
{
   // Write image to specified file. If there is no file extension or
   // if the file extension is unknown, the type argument will be used
   // to determine the file type. The quality and compression is derived from
   // the TAttImage values.
   // The size of the image in the file is independent of the actually
   // displayed size and zooming factor on the screen. This function
   // writes always the original image with its size in the file

   if (!IsValid()) {
      Error("WriteImage", "no image loaded");
      return;
   }

   if (!file || !*file) {
      Error("WriteImage", "no file name specified");
      return;
   }

   const char *s;
   if ((s = strrchr(file, '.'))) {
      s++;
      EImageFileTypes t = GetFileType(s);
      if (t == kUnknown) {
         Error("WriteImage", "cannot determine a valid file type");
         return;
      }
      if (t != kUnknown)
         type = t;
   }

   if (type == kUnknown) {
      Error("WriteImage", "not a valid file type was specified");
      return;
   }

   ASImageFileTypes atype;
   MapFileTypes(type, (UInt_t&)atype);

   UInt_t aquality;
   EImageQuality quality = GetImageQuality();
   MapQuality(quality, aquality);

   ASImageExportParams parms;
   switch (type) {
   case kXpm:
      parms.xpm.type = atype;
      parms.xpm.flags = EXPORT_ALPHA;
      parms.xpm.dither = 4;
      parms.xpm.opaque_threshold = 127;
      parms.xpm.max_colors = 512;
      break;
   case kBmp:
      ASImage2bmp(fScaledImage ? fScaledImage->fImage : fImage, file, 0);
      return;
   case kXcf:
      ASImage2xcf(fScaledImage ? fScaledImage->fImage : fImage, file, 0);
      return;
   case kPng:
      parms.png.type = atype;
      parms.png.flags = EXPORT_ALPHA;
      parms.png.compression = !GetImageCompression() ? -1 : int(GetImageCompression());
      break;
   case kJpeg:
      parms.jpeg.type = atype;
      parms.jpeg.flags = 0;
      parms.jpeg.quality = aquality;
      break;
   case kGif:
      parms.gif.type = atype;
      parms.gif.flags = EXPORT_ALPHA;
      parms.gif.dither = 0;
      parms.gif.opaque_threshold = 0;
      break;
   case kTiff:
      parms.tiff.type = atype;
      parms.tiff.flags = EXPORT_ALPHA;
      parms.tiff.rows_per_strip = 0;
      parms.tiff.compression_type = aquality <= 50 ? TIFF_COMPRESSION_JPEG :
                                                     TIFF_COMPRESSION_NONE;
      parms.tiff.jpeg_quality = 100;
      parms.tiff.opaque_threshold = 0;
      break;
   default:
      Error("WriteImage", "file type %s not yet supported", s);
      return;
   }

   if (!ASImage2file(fScaledImage ? fScaledImage->fImage : fImage, 0, file, atype, &parms))
      Error("WriteImage", "error writing file %s", file);
}

//______________________________________________________________________________
TImage::EImageFileTypes TASImage::GetFileType(const char *ext)
{
   // Return file type depending on specified extension.
   // Protected method.

   TString s(ext);
   s.Strip();
   s.ToLower();

   if (s == "xpm")
      return kXpm;
   if (s == "png")
      return kPng;
   if (s == "jpg" || s == "jpeg")
      return kJpeg;
   if (s == "xcf")
      return kXcf;
   if (s == "ppm")
      return kPpm;
   if (s == "pnm")
      return kPnm;
   if (s == "bmp")
      return kBmp;
   if (s == "ico")
      return kIco;
   if (s == "cur")
      return kCur;
   if (s == "gif")
      return kGif;
   if (s == "tiff")
      return kTiff;
   if (s == "xbm")
      return kXbm;
   if (s == "tga")
      return kTga;
   if (s == "xml")
      return kXml;

   return kUnknown;
}

//______________________________________________________________________________
void TASImage::MapFileTypes(EImageFileTypes &type, UInt_t &astype, Bool_t toas)
{
   // Map file type to/from AfterImage types.
   // Protected method.

   if (toas) {
      switch (type) {
         case kXpm:
            astype = ASIT_Xpm; break;
         case kZCompressedXpm:
            astype = ASIT_ZCompressedXpm; break;
         case kGZCompressedXpm:
            astype = ASIT_GZCompressedXpm; break;
         case kPng:
            astype = ASIT_Png; break;
         case kJpeg:
            astype = ASIT_Jpeg; break;
         case kXcf:
            astype = ASIT_Xcf; break;
         case kPpm:
            astype = ASIT_Ppm; break;
         case kPnm:
            astype = ASIT_Pnm; break;
         case kBmp:
            astype = ASIT_Bmp; break;
         case kIco:
            astype = ASIT_Ico; break;
         case kCur:
            astype = ASIT_Cur; break;
         case kGif:
            astype = ASIT_Gif; break;
         case kTiff:
            astype = ASIT_Tiff; break;
         case kXbm:
            astype = ASIT_Xbm; break;
         case kTga:
            astype = ASIT_Targa; break;
         case kXml:
            astype = ASIT_XMLScript; break;
         default:
            astype = ASIT_Unknown;
      }
   } else {
      switch (astype) {
         case ASIT_Xpm:
            type = kXpm; break;
         case ASIT_ZCompressedXpm:
            type = kZCompressedXpm; break;
         case ASIT_GZCompressedXpm:
            type = kGZCompressedXpm; break;
         case ASIT_Png:
            type = kPng; break;
         case ASIT_Jpeg:
            type = kJpeg; break;
         case ASIT_Xcf:
            type = kXcf; break;
         case ASIT_Ppm:
            type = kPpm; break;
         case ASIT_Pnm:
            type = kPnm; break;
         case ASIT_Bmp:
            type = kBmp; break;
         case ASIT_Ico:
            type = kIco; break;
         case ASIT_Cur:
            type = kCur; break;
         case ASIT_Gif:
            type = kGif; break;
         case ASIT_Tiff:
            type = kTiff; break;
         case ASIT_Xbm:
            type = kXbm; break;
         case ASIT_XMLScript:
            type = kXml; break;
         case ASIT_Targa:
            type = kTga; break;
         default:
            type = kUnknown;
      }
   }
}

//______________________________________________________________________________
void TASImage::MapQuality(EImageQuality &quality, UInt_t &asquality, Bool_t toas)
{
   // Map quality to/from AfterImage quality.
   // Protected method.

   if (toas) {
      switch (quality) {
         case kImgPoor:
            asquality = 25; break;
         case kImgFast:
            asquality = 75; break;
         case kImgGood:
            asquality = 50; break;
         case kImgBest:
            asquality = 100; break;
         default:
            asquality = 0;
      }
   } else {
      quality = kImgDefault;
      if (asquality > 0  && asquality <= 25)
         quality = kImgPoor;
      if (asquality > 26 && asquality <= 50)
         quality = kImgFast;
      if (asquality > 51 && asquality <= 75)
         quality = kImgGood;
      if (asquality > 76 && asquality <= 100)
         quality = kImgBest;
   }
}

//______________________________________________________________________________
void TASImage::SetImage(const Double_t *imageData, UInt_t width, UInt_t height,
                        TImagePalette *palette)
{
   // Deletes the old image and creates a new image depending on the values
   // of imageData. The size of the image is width X height.
   // The color of each pixel depends on the imageData of the corresponding
   // pixel. The palette is used to convert an image value into its color.
   // If palette is not defined (palette = 0) a default palette is used.
   // Any previously defined zooming is reset.

   TAttImage::SetPalette(palette);

   if (!InitVisual()) {
      Warning("SetImage", "Visual not initiated");
      return;
   }

   DestroyImage();
   delete fScaledImage;
   fScaledImage = 0;

   // get min and max value of image
   fMinValue = fMaxValue = *imageData;
   for (Int_t pixel = 1; pixel < Int_t(width * height); pixel++) {
      if (fMinValue > *(imageData + pixel)) fMinValue = *(imageData + pixel);
      if (fMaxValue < *(imageData + pixel)) fMaxValue = *(imageData + pixel);
   }

   // copy ROOT palette to asImage palette
   const TImagePalette &pal = GetPalette();

   ASVectorPalette asPalette;

   asPalette.npoints = pal.fNumPoints;
   Int_t col;
   for (col = 0; col < 4; col++)
      asPalette.channels[col] = new UShort_t[asPalette.npoints];

   memcpy(asPalette.channels[0], pal.fColorBlue,  pal.fNumPoints * sizeof(UShort_t));
   memcpy(asPalette.channels[1], pal.fColorGreen, pal.fNumPoints * sizeof(UShort_t));
   memcpy(asPalette.channels[2], pal.fColorRed,   pal.fNumPoints * sizeof(UShort_t));
   memcpy(asPalette.channels[3], pal.fColorAlpha, pal.fNumPoints * sizeof(UShort_t));

   asPalette.points = new Double_t[asPalette.npoints];
   for (Int_t point = 0; point < Int_t(asPalette.npoints); point++)
      asPalette.points[point] = fMinValue + (fMaxValue - fMinValue) * pal.fPoints[point];

   fImage = create_asimage_from_vector(fgVisual, (Double_t*)imageData, width,
                                       height, &asPalette, ASA_ASImage,
                                       GetImageCompression(), GetImageQuality());

   delete [] asPalette.points;
   for (col = 0; col < 4; col++)
      delete [] asPalette.channels[col];

   fZoomUpdate = 0;
   fZoomOffX   = 0;
   fZoomOffY   = 0;
   fZoomWidth  = width;
   fZoomHeight = height;
   fPaletteEnabled = kTRUE;
}

//______________________________________________________________________________
void TASImage::SetImage(const TArrayD &imageData, UInt_t width, TImagePalette *palette)
{
   // Deletes the old image and creates a new image depending on the values
   // of imageData. The size of the image is width X (imageData.fN / width).
   // The color of each pixel depends on the imageData of the corresponding
   // pixel. The palette is used to convert an image value into its color.
   // If palette is not defined (palette = 0) a default palette is used.
   // Any previously defined zooming is reset.

   SetImage(imageData.GetArray(), width, imageData.GetSize() / width, palette);
}

//______________________________________________________________________________
void TASImage::SetImage(const TVectorD &imageData, UInt_t width, TImagePalette *palette)
{
   // Deletes the old image and creates a new image depending on the values
   // of imageData. The size of the image is width X (imageData.fN / width).
   // The color of each pixel depends on the imageData of the corresponding
   // pixel. The palette is used to convert an image value into its color.
   // If palette is not defined (palette = 0) a default palette is used.
   // Any previously defined zooming is reset.

   SetImage(imageData.GetMatrixArray(), width,
            imageData.GetNoElements() / width, palette);
}

//______________________________________________________________________________
void TASImage::FromPad(TVirtualPad *pad, Int_t x, Int_t y, UInt_t w, UInt_t h)
{
   // Create an image from the given pad, afterwards this image can be
   // saved in any of the supported image formats.

   if (!pad) {
      Error("FromPad", "pad cannot be 0");
      return;
   }

   if (!InitVisual()) {
      Warning("FromPad", "Visual not initiated");
      return;
   }

   SetName(pad->GetName());

   DestroyImage();
   delete fScaledImage;
   fScaledImage = 0;

   if (gROOT->IsBatch()) { // in batch mode
      TVirtualPS *psave = gVirtualPS;
      gVirtualPS = (TVirtualPS*)gROOT->ProcessLineFast("new TImageDump()");
      gVirtualPS->Open(pad->GetName(), 114); // in memory
      gVirtualPS->SetBit(BIT(11)); //kPrintingPS
      pad->Paint();
      TASImage *itmp = (TASImage*)gVirtualPS->GetStream();

      if (itmp && itmp->fImage) {
         fImage = clone_asimage(itmp->fImage, SCL_DO_ALL);

         if (itmp->fImage->alt.argb32) {
            UInt_t sz = itmp->fImage->width*itmp->fImage->height;
            fImage->alt.argb32 = new ARGB32[sz];
            memcpy(fImage->alt.argb32, itmp->fImage->alt.argb32, sz*4);
         }
      }
      delete gVirtualPS;
      gVirtualPS = psave;
      return;
   }

   if (w == 0) {
      w = pad->UtoPixel(1.);
   }

   if (h == 0) {
      h = pad->VtoPixel(0.);
   }

   TCanvas *canvas = pad->GetCanvas();
   Int_t wid = (pad == canvas) ? canvas->GetCanvasID() : pad->GetPixmapID();
   gVirtualX->SelectWindow(wid);
   Window_t wd = gVirtualX->GetCurrentWindow();
   if (!wd) return;

   static int x11 = -1;
   if (x11 < 0) x11 = gVirtualX->InheritsFrom("TGX11");

   if (x11) { //use built-in optimized version
      fImage = pixmap2asimage(fgVisual, wd, x, y, w, h, kAllPlanes, 0, 0);
   } else {
      unsigned char *bits = gVirtualX->GetColorBits(wd, 0, 0, w, h);

      if (!bits) { // error
         return;
      }
      fImage = bitmap2asimage(bits, w, h, 0, 0);
      delete [] bits;
   }
}

//______________________________________________________________________________
void TASImage::Draw(Option_t *option)
{
   // Draw image. Support the following drawing options:
   // "T[x,y[,tint]]" - tile image (use specified offset and tint),
   //                   e.g. "T100,100,#556655"
   //                   with this option the zooming is not possible
   //                   and disabled
   // "N"             - display in new canvas (of original image size)
   // "X"             - image is drawn expanded to pad size
   // "Z"             - image is vectorized and image palette is drawn
   //
   // The default is to display the image in the current gPad.

   if (!fImage) {
      Error("Draw", "no image set");
      return;
   }

   TString opt = option;
   opt.ToLower();
   if (opt.Contains("n") || !gPad || !gPad->IsEditable()) {
      Int_t w = fImage->height < 65 ? -fImage->width : fImage->width;
      Int_t h = fImage->height < 65 ? 64 : fImage->height;
      TString rname = GetName();
      rname.ReplaceAll(".", "");
      new TCanvas(rname.Data(), Form("%s (%d x %d)", rname.Data(),
                  fImage->width, fImage->height), w, h);
   }

   if (!opt.Contains("x")) {
      Double_t left = gPad->GetLeftMargin();
      Double_t right = gPad->GetRightMargin();
      Double_t top = gPad->GetTopMargin();
      Double_t bottom = gPad->GetBottomMargin();

      gPad->Range(-left / (1.0 - left - right),
                  -bottom / (1.0 - top - bottom),
                  1 + right / (1.0 - left - right),
                  1 + top / ( 1.0 - top - bottom));
      gPad->RangeAxis(0, 0, 1, 1);
   }

   TFrame *frame = gPad->GetFrame();
   frame->SetBorderMode(0);
   frame->SetFillColor(gPad->GetFillColor());
   frame->SetLineColor(gPad->GetFillColor());
   frame->Draw();

   TObject::Draw(option);
}

//______________________________________________________________________________
void TASImage::Image2Drawable(ASImage *im, Drawable_t wid, Int_t x, Int_t y)
{
   // draw asimage on drawable

   static GCValues_t gv;
   static GContext_t gc = 0;

   if (!im) {
      return;
   }

   if (!gc) {
      gc = gVirtualX->CreateGC(gVirtualX->GetDefaultRootWindow(), &gv);
   }

   static int x11 = -1;
   if (x11 < 0) x11 = gVirtualX->InheritsFrom("TGX11");

   if (x11) { //use built-in optimized version
      asimage2drawable(fgVisual, wid, im, (GC)gc, 0, 0, x, y, im->width, im->height, 1);
   } else {
      ASImage *img = 0;
      unsigned char *bits = (unsigned char *)im->alt.argb32;
      if (!bits) {
         img = tile_asimage(fgVisual, im, 0, 0, im->width, im->height,
                            0, ASA_ARGB32, 0, ASIMAGE_QUALITY_DEFAULT);
         bits = (unsigned char *)img->alt.argb32;
      }

      Pixmap_t pic = gVirtualX->CreatePixmapFromData(bits, im->width, im->height);
      if (pic) {
         gVirtualX->CopyArea(pic, wid, gc, 0, 0, im->width, im->height, x, y);
      } else {
         return;
      }
      if (img) {
         destroy_asimage(&img);
      }
   }
}
//______________________________________________________________________________
void TASImage::PaintImage(Drawable_t wid, Int_t x, Int_t y)
{
   // draw image on drawable wid (pixmap, window ) at x,y position

   Image2Drawable(fImage, wid, x, y);
}

//______________________________________________________________________________
void TASImage::Paint(Option_t *option)
{
   // Paint image. Support the following drawing options:
   // "T[x,y[,tint]]" - tile image (use specified offset and tint),
   //                   e.g. "T100,100,#556655"
   //                   with this option the zooming is not possible
   //                   and disabled
   // "N"             - display in new canvas (of original image size)
   // "X"             - image is drawn expanded to pad size
   // "Z"             - image is vectorized and image palette is drawn
   //
   // The default is to display the image in the current gPad.

   if (!fImage) {
      Error("Paint", "no image set");
      return;
   }

   if (!InitVisual()) {
      Warning("Paint", "Visual not initiated");
      return;
   }

   Int_t   tile_x = 0, tile_y = 0;
   ULong_t tile_tint = 0;
   Bool_t  tile = kFALSE;
   Bool_t  expand = kFALSE;

   TString opt = option;
   opt.ToLower();

   if (opt.Contains("t")) {
      char stint[64];
      if (sscanf(opt.Data() + opt.Index("t"), "t%d,%d,%s", &tile_x, &tile_y,
                 stint) <= 3) {
         tile = kTRUE;
         if (parse_argb_color(stint, (CARD32*)&tile_tint) == stint)
            tile_tint = 0;
      } else {
         Error("Paint", "tile option error");
      }
   } else if (opt.Contains("x")) {
      expand = kTRUE;
      fConstRatio = kFALSE;
   } else if (opt.Contains("z")) {
      fPaletteEnabled = kTRUE;

      if (!fImage->alt.vector) {
         Vectorize(256);
      }
   }

   ASImage *image = fImage;

   // Get geometry of pad
   Int_t to_w = gPad->UtoPixel(1.);
   Int_t to_h = gPad->VtoPixel(0.);

   // remove the size by the margin of the pad
   if (!expand) {
      to_h  = (Int_t)(to_h * (1.0 - gPad->GetBottomMargin() - gPad->GetTopMargin() ) + 0.5);
      to_w  = (Int_t)(to_w * (1.0 - gPad->GetLeftMargin() - gPad->GetRightMargin() ) + 0.5);
   } else {
      gPad->SetLeftMargin(0);
      gPad->SetTopMargin(0);
   }

   // upper left corner and size of the palette in pixels
   Int_t pal_Ax = gPad->XtoAbsPixel(1.0) + 5;
   Int_t pal_Ay = gPad->YtoAbsPixel(1.0) + 20;
   Int_t pal_x = gPad->XtoPixel(1.0) + 5;
   Int_t pal_y = gPad->YtoPixel(1.0) + 20;
   Int_t pal_w = gPad->UtoPixel(gPad->GetRightMargin()) / 3;
   Int_t pal_h = to_h - 20;

   if ((to_w < 25 || to_h < 25) && !expand) {
      Error("Paint", "pad too small to display an image");
      return;
   }

   if (GetConstRatio()) {
      if ((Double_t)to_w / (Double_t)fZoomWidth <
          (Double_t)to_h / (Double_t)fZoomHeight)
         to_h = Int_t(Double_t(fZoomHeight) * to_w / fZoomWidth);
      else
         to_w = Int_t(Double_t(fZoomWidth) * to_h / fZoomHeight);
   }

   ASImage  *grad_im = 0;

   if (fImage->alt.vector && fPaletteEnabled) {
      // draw the palette
      ASGradient grad;
      const TImagePalette &pal = GetPalette();

      grad.npoints = pal.fNumPoints;
      grad.type    = GRADIENT_Top2Bottom;
      grad.color   = new ARGB32[grad.npoints];
      grad.offset  = new double[grad.npoints];

      for (Int_t pt = 0; pt < grad.npoints; pt++) {
         Int_t oldPt = grad.npoints - pt -1;
         grad.offset[pt] = 1 - pal.fPoints[oldPt];
         grad.color[pt] = (((ARGB32)(pal.fColorBlue[oldPt]  & 0xff00)) >>  8) |
                          (((ARGB32)(pal.fColorGreen[oldPt] & 0xff00))      ) |
                          (((ARGB32)(pal.fColorRed[oldPt]   & 0xff00)) <<  8) |
                          (((ARGB32)(pal.fColorAlpha[oldPt] & 0xff00)) << 16);
      }

      grad_im = make_gradient(fgVisual, &grad , UInt_t(pal_w),
                              pal_h, SCL_DO_COLOR,
                              ASA_ARGB32, GetImageCompression(), GetImageQuality());

      delete [] grad.color;
      delete [] grad.offset;
   }

   if (tile) {
      delete fScaledImage;
      fScaledImage = (TASImage*)TImage::Create();
      fScaledImage->fImage = tile_asimage(fgVisual, fImage, tile_x, tile_y,
                                          to_w, to_h, tile_tint, ASA_ASImage,
                                          GetImageCompression(), GetImageQuality());
      image = fScaledImage->fImage;

   } else if (fZoomUpdate == kZoomOps) {
      image = fImage;

   } else {
      // Scale and zoom image if needed
      if (Int_t(fImage->width) != to_w || Int_t(fImage->height) != to_h ||
          fImage->width != fZoomWidth || fImage->height != fZoomHeight) {

         if (fScaledImage && (Int_t(fScaledImage->GetWidth()) != to_w ||
                Int_t(fScaledImage->GetHeight()) != to_h ||
                fZoomUpdate)) {

            delete fScaledImage;
            fScaledImage = 0;
         }

         if (!fScaledImage) {
            fScaledImage = (TASImage*)TImage::Create();

            if ((fImage->width != fZoomWidth) || (fImage->height != fZoomHeight)) {
               // zoom and scale image
               ASImage *tmpImage = 0;
               tmpImage = tile_asimage(fgVisual, fImage, fZoomOffX,
                                       fImage->height - fZoomHeight - fZoomOffY,
                                       fZoomWidth, fZoomHeight, 0, ASA_ASImage,
                                       GetImageCompression(), GetImageQuality());

               fScaledImage->fImage = scale_asimage(fgVisual, tmpImage, to_w, to_h,
                                                   ASA_ASImage, GetImageCompression(),
                                                   GetImageQuality());
               destroy_asimage(&tmpImage);
            } else {
               // scale image, no zooming
               fScaledImage->fImage = scale_asimage(fgVisual, fImage, to_w, to_h,
                                                    ASA_ASImage, GetImageCompression(),
                                                    GetImageQuality());
            }
         }
         image = fScaledImage->fImage;
      }
   }
   fZoomUpdate = 0;

   if (!image) {
      Error("Paint", "image could not be rendered to display");
      return;
   }

   int tox = expand  ? 0 : int(gPad->UtoPixel(1.) * gPad->GetLeftMargin());
   int toy = expand  ? 0 : int(gPad->VtoPixel(0.) * gPad->GetTopMargin());

   if (!gROOT->IsBatch()) {
      Window_t wid = (Window_t)gVirtualX->GetWindowID(gPad->GetPixmapID());
      Image2Drawable(fScaledImage ? fScaledImage->fImage : fImage, wid, tox, toy);

      if (grad_im && fPaletteEnabled) {
         // draw color bar
         Image2Drawable(grad_im, wid, pal_x, pal_y);

         // values of palette
         TGaxis axis;
         Int_t ndiv = 510;
         double min = fMinValue;
         double max = fMaxValue;
         axis.SetLineColor(0);       // draw white ticks
         Double_t pal_Xpos = gPad->AbsPixeltoX(pal_Ax + pal_w);
         axis.PaintAxis(pal_Xpos, gPad->PixeltoY(pal_Ay + pal_h - 1),
                        pal_Xpos, gPad->PixeltoY(pal_Ay),
                        min, max, ndiv, "+LU");
         min = fMinValue;
         max = fMaxValue;
         axis.SetLineColor(1);       // draw black ticks
         axis.PaintAxis(pal_Xpos, gPad->AbsPixeltoY(pal_Ay + pal_h),
                        pal_Xpos, gPad->AbsPixeltoY(pal_Ay + 1),
                        min, max, ndiv, "+L");
      }
   }

   // loop over pxmap and draw image to PostScript
   if (gVirtualPS) {
      if (gVirtualPS->InheritsFrom("TImageDump")) { // PostScript is asimage
         TImage *dump = (TImage *)gVirtualPS->GetStream();
         dump->Merge(fScaledImage ? fScaledImage : this, "alphablend",
                     gPad->XtoAbsPixel(0), gPad->YtoAbsPixel(1));

         if (grad_im) {
            TASImage tgrad;
            tgrad.fImage = grad_im;
            dump->Merge(&tgrad, "alphablend", pal_Ax, pal_Ay);

            // values of palette
            TGaxis axis;
            Int_t ndiv = 510;
            double min = fMinValue;
            double max = fMaxValue;
            axis.SetLineColor(1);       // draw black ticks
            Double_t pal_Xpos = gPad->AbsPixeltoX(pal_Ax + pal_w);
            axis.PaintAxis(pal_Xpos, gPad->AbsPixeltoY(pal_Ay + pal_h),
                           pal_Xpos, gPad->AbsPixeltoY(pal_Ay + 1),
                           min, max, ndiv, "+L");
         }
         return;
      } else if (gVirtualPS->InheritsFrom("TPDF")) {
         Warning("Paint", "PDF not implemeted yet");
         return;
      }

      // get special color cell to be reused during image printing
      TObjArray *colors = (TObjArray*) gROOT->GetListOfColors();
      TColor *color = 0;
      // Look for color by name
      if ((color = (TColor*)colors->FindObject("Image_PS")) == 0)
         color = new TColor(colors->GetEntries(), 1., 1., 1., "Image_PS");

      gVirtualPS->SetFillColor(color->GetNumber());
      gVirtualPS->SetFillStyle(1001);

      Double_t xconv = (gPad->AbsPixeltoX(to_w) - gPad->AbsPixeltoX(0)) / image->width;
      Double_t yconv = (gPad->AbsPixeltoY(0) - gPad->AbsPixeltoY(to_h)) / image->height;

      Double_t x1 = 0;
      Double_t x2 = 1 * xconv;
      Double_t y1 = 1 - yconv;
      Double_t y2 = 1;

      gVirtualPS->CellArrayBegin(image->width, image->height, x1, x2, y1, y2);

      ASImageDecoder *imdec = start_image_decoding(fgVisual, image, SCL_DO_ALL,
                                                   0, 0, image->width, image->height, 0);
      for (Int_t yt = 0; yt < (Int_t)image->height; yt++) {
         imdec->decode_image_scanline(imdec);
         for (Int_t xt = 0; xt < (Int_t)image->width; xt++)
            gVirtualPS->CellArrayFill(imdec->buffer.red[xt],
                                      imdec->buffer.green[xt],
                                      imdec->buffer.blue[xt]);
      }
      stop_image_decoding(&imdec);
      gVirtualPS->CellArrayEnd();

      // print the color bar
      if (grad_im) {
         xconv = (gPad->AbsPixeltoX(pal_Ax + pal_w) - gPad->AbsPixeltoX(pal_Ax)) / grad_im->width;
         yconv = (gPad->AbsPixeltoY(pal_Ay - pal_h) - gPad->AbsPixeltoY(pal_Ay)) / grad_im->height;
         x1 = gPad->AbsPixeltoX(pal_Ax);
         x2 = x1 + xconv;
         y2 = gPad->AbsPixeltoY(pal_Ay);
         y1 = y2 - yconv;
         gVirtualPS->CellArrayBegin(grad_im->width, grad_im->height,
                                    x1, x2, y1, y2);

         imdec = start_image_decoding(fgVisual, grad_im, SCL_DO_ALL,
                                      0, 0, grad_im->width, grad_im->height, 0);
         for (Int_t yt = 0; yt < (Int_t)grad_im->height; yt++) {
            imdec->decode_image_scanline(imdec);
            for (Int_t xt = 0; xt < (Int_t)grad_im->width; xt++)
               gVirtualPS->CellArrayFill(imdec->buffer.red[xt],
                                         imdec->buffer.green[xt],
                                         imdec->buffer.blue[xt]);
         }
         stop_image_decoding(&imdec);
         gVirtualPS->CellArrayEnd();

         // values of palette
         TGaxis axis;
         Int_t ndiv = 510;
         double min = fMinValue;
         double max = fMaxValue;
         axis.SetLineColor(1);       // draw black ticks
         Double_t pal_Xpos = gPad->AbsPixeltoX(pal_Ax + pal_w);
         axis.PaintAxis(pal_Xpos, gPad->AbsPixeltoY(pal_Ay + pal_h),
                        pal_Xpos, gPad->AbsPixeltoY(pal_Ay + 1),
                        min, max, ndiv, "+L");

      }
   }

   if (grad_im) {
      destroy_asimage(&grad_im);
   }
}

//______________________________________________________________________________
Int_t TASImage::DistancetoPrimitive(Int_t px, Int_t py)
{
   // Is the mouse in the image?

   Int_t pxl, pyl, pxt, pyt;

   Int_t px1 = gPad->XtoAbsPixel(0);
   Int_t py1 = gPad->YtoAbsPixel(0);
   Int_t px2 = gPad->XtoAbsPixel(1);
   Int_t py2 = gPad->YtoAbsPixel(1);

   if (px1 < px2) {pxl = px1; pxt = px2;}
   else           {pxl = px2; pxt = px1;}
   if (py1 < py2) {pyl = py1; pyt = py2;}
   else           {pyl = py2; pyt = py1;}

   if ((px > pxl && px < pxt) && (py > pyl && py < pyt))
      return 0;

   return 999999;
}

//______________________________________________________________________________
void TASImage::ExecuteEvent(Int_t event, Int_t px, Int_t py)
{
   // Execute mouse events.

   if (IsEditable()) {
      gPad->ExecuteEvent(event, px, py);
      return;
   }

   gPad->SetCursor(kCross);

   static Int_t stx, sty;
   static Int_t oldx, oldy;

   if (!IsValid()) return;

   if (event == kButton1Motion || event == kButton1Down  ||
       event == kButton1Up) {

      // convert to image pixel on screen
      Int_t imgX = px - gPad->XtoAbsPixel(0);
      Int_t imgY = py - gPad->YtoAbsPixel(1);

      if (imgX < 0)  px = px - imgX;
      if (imgY < 0)  py = py - imgY;

      ASImage *image = fImage;
      if (fScaledImage) image = fScaledImage->fImage;

      if (imgX >= (int)image->width)  px = px - imgX + image->width - 1;
      if (imgY >= (int)image->height) py = py - imgY + image->height - 1;

      switch (event) {

         case kButton1Down:
            gVirtualX->SetLineColor(-1);

            stx = oldx = px;
            sty = oldy = py;
            break;

         case kButton1Motion:
            gVirtualX->DrawBox(oldx, oldy, stx, sty, TVirtualX::kHollow);
            oldx = px;
            oldy = py;
            gVirtualX->DrawBox(oldx, oldy, stx, sty, TVirtualX::kHollow);
            break;

         case kButton1Up:
            // do nothing if zoom area is too small
            if ( TMath::Abs(stx - px) < 5 || TMath::Abs(sty - py) < 5)
               return;

            Double_t xfact = (fScaledImage) ? (Double_t)fScaledImage->fImage->width  / fZoomWidth  : 1;
            Double_t yfact = (fScaledImage) ? (Double_t)fScaledImage->fImage->height / fZoomHeight : 1;

            Int_t imgX1 = stx - gPad->XtoAbsPixel(0);
            Int_t imgY1 = sty - gPad->YtoAbsPixel(1);
            Int_t imgX2 = px  - gPad->XtoAbsPixel(0);
            Int_t imgY2 = py  - gPad->YtoAbsPixel(1);

            imgY1 = image->height - 1 - imgY1;
            imgY2 = image->height - 1 - imgY2;
            imgX1 = (Int_t)(imgX1 / xfact) + fZoomOffX;
            imgY1 = (Int_t)(imgY1 / yfact) + fZoomOffY;
            imgX2 = (Int_t)(imgX2 / xfact) + fZoomOffX;
            imgY2 = (Int_t)(imgY2 / yfact) + fZoomOffY;

            Zoom((imgX1 < imgX2) ? imgX1 : imgX2, (imgY1 < imgY2) ? imgY1 : imgY2,
                 TMath::Abs(imgX1 - imgX2) + 1, TMath::Abs(imgY1 - imgY2) + 1);

            gVirtualX->SetLineColor(-1);
            gPad->Modified(kTRUE);
            gPad->Update();
            break;
      }
   }
}

//______________________________________________________________________________
char *TASImage::GetObjectInfo(Int_t px, Int_t py) const
{
   // Get image pixel coordinates and the pixel value at the mouse pointer.

   static char info[64];
   info[0] = 0;

   if (!IsValid()) return info;

   // convert to image pixel on screen
   px -= gPad->XtoAbsPixel(0);
   py -= gPad->YtoAbsPixel(1);

   // no info if mouse is outside of image
   if (px < 0 || py < 0)  return info;

   ASImage *image = fImage;
   if (fScaledImage) image = fScaledImage->fImage;
   if (px >= (int)image->width || py >= (int)image->height)
      return info;

   py = image->height - 1 - py;
   // convert to original image size and take zooming into account
   if (fScaledImage) {
      px = (Int_t)(px / (Double_t)fScaledImage->fImage->width  * fZoomWidth ) + fZoomOffX;
      py = (Int_t)(py / (Double_t)fScaledImage->fImage->height * fZoomHeight) + fZoomOffY;
   }

   if (fImage->alt.vector) {
      sprintf(info, "x: %d  y: %d   %.5g",
              px, py, fImage->alt.vector[px + py * fImage->width]);
   } else {
      sprintf(info, "x: %d  y: %d", px, py);
   }

   return info;
}

//______________________________________________________________________________
void TASImage::SetPalette(const TImagePalette *palette)
{
   // Set a new palette to an image. Only images that were created with the
   // SetImage() functions can be modified with this function.
   // The previously used palette is destroyed.

   TAttImage::SetPalette(palette);

   if (!InitVisual()) {
      Warning("SetPalette", "Visual not initiated");
      return;
   }

   if (!IsValid()) {
      Warning("SetPalette", "Image not valid");
      return;
   }

   if (fImage->alt.vector == 0)
      return;

   // copy ROOT palette to asImage palette
   const TImagePalette &pal = GetPalette();

   ASVectorPalette asPalette;
   asPalette.npoints = pal.fNumPoints;
   asPalette.channels[0] = new CARD16 [asPalette.npoints];
   asPalette.channels[1] = new CARD16 [asPalette.npoints];
   asPalette.channels[2] = new CARD16 [asPalette.npoints];
   asPalette.channels[3] = new CARD16 [asPalette.npoints];
   memcpy(asPalette.channels[0], pal.fColorBlue,  pal.fNumPoints * sizeof(UShort_t));
   memcpy(asPalette.channels[1], pal.fColorGreen, pal.fNumPoints * sizeof(UShort_t));
   memcpy(asPalette.channels[2], pal.fColorRed,   pal.fNumPoints * sizeof(UShort_t));
   memcpy(asPalette.channels[3], pal.fColorAlpha, pal.fNumPoints * sizeof(UShort_t));

   asPalette.points = new double[asPalette.npoints];
   for (Int_t point = 0; point < Int_t(asPalette.npoints); point++)
      asPalette.points[point] = fMinValue + (fMaxValue - fMinValue) * pal.fPoints[point];

   // use the new palette in this image
   colorize_asimage_vector(fgVisual, fImage, &asPalette, ASA_ASImage, GetImageQuality());

   delete [] asPalette.points;
   for (Int_t col = 0; col < 4; col++)
      delete [] asPalette.channels[col];


   delete fScaledImage;
   fScaledImage = 0;
}

//______________________________________________________________________________
void TASImage::Scale(UInt_t toWidth, UInt_t toHeight)
{
   // Scales the original image. The size of the image on the screen does not
   // change because it is defined by the size of the pad.
   // This function can be used to change the size of an image before writing
   // it into a file. The colors of the new pixels are interpolated.
   // An image created with the SetImage() functions cannot be modified with
   // the function SetPalette() any more after a call of this function!

   if (!IsValid()) {
      Warning("Scale", "Image not initiated");
      return;
   }

   if (!InitVisual()) {
      Warning("Scale", "Visual not initiated");
      return;
   }

   if (toWidth < 1)
       toWidth = 1;
   if (toHeight < 1 )
      toHeight = 1;
   if (toWidth > 30000)
      toWidth = 30000;
   if (toHeight > 30000)
      toHeight = 30000;

   ASImage *img = scale_asimage(fgVisual, fImage, toWidth, toHeight,
                                ASA_ASImage, GetImageCompression(),
                                GetImageQuality());
   DestroyImage();
   fImage = img;
   UnZoom();
   fZoomUpdate = kZoomOps;
}

//______________________________________________________________________________
void TASImage::Tile(UInt_t toWidth, UInt_t toHeight)
{
   // Tiles the original image.

   if (!IsValid()) {
      Warning("Tile", "Image not initiated");
      return;
   }

   if (!InitVisual()) {
      Warning("Tile", "Visual not initiated");
      return;
   }

   if (toWidth < 1)
       toWidth = 1;
   if (toHeight < 1 )
      toHeight = 1;
   if (toWidth > 30000)
      toWidth = 30000;
   if (toHeight > 30000)
      toHeight = 30000;

   ASImage *img = tile_asimage(fgVisual, fImage, 0, 0, toWidth, toHeight, 0,
                                ASA_ASImage, GetImageCompression(), GetImageQuality());
   DestroyImage();
   fImage = img;
   UnZoom();
   fZoomUpdate = kZoomOps;
}

//______________________________________________________________________________
void TASImage::Zoom(UInt_t offX, UInt_t offY, UInt_t width, UInt_t height)
{
   // The area of an image displayed in a pad is defined by this function.
   // Note: the size on the screen is defined by the size of the pad.
   // The original image is not modified by this function.
   // If width or height is larger than the original image they are reduced to
   // the width and height of the image.
   // If the off values are too large (off + width > image width) than the off
   // values are decreased. For example: offX = image width - width
   // Note: the parameters are always relative to the original image not to the
   // size of an already zoomed image.

   if (!IsValid()) {
      Warning("Zoom", "Image not valid");
      return;
   }
   fZoomUpdate = kZoom;

   fZoomWidth  = (width == 0) ? 1 : ((width > fImage->width) ? fImage->width : width);
   fZoomHeight = (height == 0) ? 1 : ((height > fImage->height) ? fImage->height : height);
   fZoomOffX   = offX;
   if (fZoomOffX + fZoomWidth > fImage->width)
      fZoomOffX = fImage->width - fZoomWidth;
   fZoomOffY   = offY;
   if (fZoomOffY + fZoomHeight > fImage->height)
      fZoomOffY = fImage->height - fZoomHeight;
}

//______________________________________________________________________________
void TASImage::UnZoom()
{
   // Un-zooms the image to original size.
   //
   // UnZoom() - performs undo for Zoom,Crop,Scale actions

   if (!IsValid()) {
      Warning("UnZoom", "Image not valid");
      return;
   }
   fZoomUpdate = kZoom;
   fZoomOffX   = 0;
   fZoomOffY   = 0;
   fZoomWidth  = fImage->width;
   fZoomHeight = fImage->height;

   delete fScaledImage;
   fScaledImage = 0;
}

//______________________________________________________________________________
void TASImage::Flip(Int_t flip)
{
   // Flip image in place. Flip is either 90, 180, 270, 180 is default.
   // This function manipulates the original image and destroys the
   // scaled and zoomed image which will be recreated at the next call of
   // the Draw function. If the image is zoomed the zoom - coordinates are
   // now relative to the new image.
   // This function cannot be used for images which were created with the
   // SetImage() functions, because the original pixel values would be
   // destroyed.

   if (!IsValid()) {
      Warning("Flip", "Image not valid");
      return;
   }
   if (!InitVisual()) {
      Warning("Flip", "Visual not initiated");
      return;
   }

   if (fImage->alt.vector) {
      Warning("Flip", "flip does not work for data images");
      return;
   }

   Int_t rflip = flip/90;

   UInt_t w = fImage->width;
   UInt_t h = fImage->height;

   if (rflip & 1) {
      w = fImage->height;
      h = fImage->width;
   }

   ASImage *img = flip_asimage(fgVisual, fImage, 0, 0, w, h, rflip,
                               ASA_ASImage, GetImageCompression(),
                               GetImageQuality());
   DestroyImage();
   fImage = img;
   UnZoom();
}

//______________________________________________________________________________
void TASImage::Mirror(Bool_t vert)
{
   // Mirror image in place. If vert is true mirror in vertical axis,
   // horizontal otherwise. Vertical is default.
   // This function manipulates the original image and destroys the
   // scaled and zoomed image which will be recreated at the next call of
   // the Draw function. If the image is zoomed the zoom - coordinates are
   // now relative to the new image.
   // This function cannot be used for images which were created with the
   // SetImage() functions, because the original pixel values would be
   // destroyed.

   if (!IsValid()) {
      Warning("Mirror", "Image not valid");
      return;
   }

   if (!InitVisual()) {
      Warning("Mirrow", "Visual not initiated");
      return;
   }

   if (fImage->alt.vector) {
      Warning("Mirror", "mirror does not work for data images");
      return;
   }

   ASImage *img = mirror_asimage(fgVisual, fImage, 0, 0,
                                 fImage->width, fImage->height, vert,
                                 ASA_ASImage, GetImageCompression(),
                                 GetImageQuality());
   DestroyImage();
   fImage = img;
   UnZoom();
}

//______________________________________________________________________________
UInt_t TASImage::GetWidth() const
{
   // Return width of original image not of the displayed image.
   // (Number of image pixels)

   return fImage ? fImage->width : 0;
}

//______________________________________________________________________________
UInt_t TASImage::GetHeight() const
{
   // Return height of original image not of the displayed image.
   // (Number of image pixels)

   return fImage ? fImage->height : 0;
}

//______________________________________________________________________________
UInt_t TASImage::GetScaledWidth() const
{
   // Return width of the displayed image not of the original image.
   // (Number of screen pixels)

   return fScaledImage ? fScaledImage->fImage->width : GetWidth();
}

//______________________________________________________________________________
UInt_t TASImage::GetScaledHeight() const
{
   // Return height of the displayed image not of the original image.
   // (Number of screen pixels)

   return fScaledImage ? fScaledImage->fImage->height : GetHeight();
}

//______________________________________________________________________________
Bool_t TASImage::InitVisual()
{
   // Static function to initialize the ASVisual.

   if (fgVisual) return kTRUE;

   // batch or win32 mode
   if (gROOT->IsBatch() || gVirtualX->InheritsFrom("TGWin32")) {
      dpy = 0;
      fgVisual = create_asvisual( NULL, 0, 0, NULL );
      return kTRUE;
   }

   dpy = (Display*) gVirtualX->GetDisplay();
   Int_t screen  = gVirtualX->GetScreen();
   Int_t depth   = gVirtualX->GetDepth();
   Visual *vis   = (Visual*) gVirtualX->GetVisual();
   Colormap cmap = (Colormap) gVirtualX->GetColormap();
#ifndef WIN32
   fgVisual = create_asvisual_for_id(dpy, screen, depth,
                                     XVisualIDFromVisual(vis), cmap, 0);
#else
   fgVisual = create_asvisual(NULL, 0, 0, NULL);
#endif

   return kTRUE;
}

//______________________________________________________________________________
void TASImage::StartPaletteEditor()
{
   // Start palette editor.

   if (!IsValid()) {
      Warning("StartPaletteEditor", "Image not valid");
      return;
   }
   if (fImage->alt.vector == 0) {
      Warning("StartPaletteEditor", "palette can be modified only for data images");
      return;
   }

   // Opens a GUI to edit the color palette
   TAttImage::StartPaletteEditor();
}

//______________________________________________________________________________
Pixmap_t TASImage::GetPixmap()
{
   // returns image pixmap

   if (fPic) return fPic;

   if (!InitVisual()) {
      Warning("GetPixmap", "Visual not initiated");
      return 0;
   }

   ASImage *img = fScaledImage ? fScaledImage->fImage : fImage;

   static int x11 = -1;
   if (x11 < 0) x11 = gVirtualX->InheritsFrom("TGX11");

   if (x11) {   // use builtin version
      fPic = (Pixmap_t)asimage2pixmap(fgVisual, gVirtualX->GetDefaultRootWindow(),
                                       img, 0, kTRUE);
   } else {
      if (!fImage->alt.argb32) {
         BeginPaint();
      }
      fPic = gVirtualX->CreatePixmapFromData((unsigned char*)fImage->alt.argb32, fImage->width, fImage->height);
   }

   return fPic;
}

//______________________________________________________________________________
Pixmap_t TASImage::GetMask()
{
   // returns image mask pixmap (alpha channel)

   if (fMask) return fMask;

   Pixmap_t pxmap = 0;

   if (!InitVisual()) {
      Warning("GetMask", "Visual not initiated");
      return pxmap;
   }

   ASImage *img = fScaledImage ? fScaledImage->fImage : fImage;

   if (!img) {
      Warning("GetMask", "No image");
      return pxmap;
   }

   UInt_t hh = img->height;
   UInt_t ow = img->width%8;
   UInt_t ww = img->width - ow + (ow ? 8 : 0);

   UInt_t bit = 0;
   int i = 0;
   UInt_t y = 0;
   UInt_t x = 0;

   char *bits = new char[ww*hh]; //an array of bits

   ASImageDecoder *imdec = start_image_decoding(fgVisual, img, SCL_DO_ALPHA,
                                                0, 0, ww, 0, 0);
   if(!imdec) {
      return 0;
   }

   for (y = 0; y < hh; y++) {
      imdec->decode_image_scanline(imdec);
      CARD32 *a = imdec->buffer.alpha;

      for (x = 0; x < ww; x++) {
         if (a[x]) {
            SETBIT(bits[i], bit);
         } else {
            CLRBIT(bits[i], bit);
         }
         bit++;
         if (bit == 8) {
            bit = 0;
            i++;
         }
      }
   }

   stop_image_decoding( &imdec );
   fMask = gVirtualX->CreateBitmap(gVirtualX->GetDefaultRootWindow(), (const char *)bits,
                                   ww, hh);
   delete [] bits;
   return fMask;
}

//______________________________________________________________________________
void TASImage::SetImage(Pixmap_t pxm, Pixmap_t mask)
{
   // create image from pixmap

   if (!InitVisual()) {
      Warning("SetImage", "Visual not initiated");
      return;
   }

   DestroyImage();
   delete fScaledImage;
   fScaledImage = 0;

   Int_t xy;
   UInt_t w, h;
   gVirtualX->GetWindowSize(pxm, xy, xy, w, h);

   if (fName.IsNull()) fName.Form("img_%dx%d",w, h);

   static int x11 = -1;
   if (x11 < 0) x11 = gVirtualX->InheritsFrom("TGX11");

   if (x11) { //use built-in optimized version
      fImage = picture2asimage(fgVisual, pxm, mask, 0, 0, w, h, kAllPlanes, 1, 0);
   } else {
      unsigned char *bits = gVirtualX->GetColorBits(pxm, 0, 0, w, h);
      if (!bits) {   // error
         return;
      }

      // no mask
      if (!mask) {
         fImage = bitmap2asimage(bits, w, h, 0, 0);
         delete [] bits;
         return;
      }
      unsigned char *mask_bits = gVirtualX->GetColorBits(mask, 0, 0, w, h);
      fImage = bitmap2asimage(bits, w, h, 0, mask_bits);
      delete [] mask_bits;
   }
}

//______________________________________________________________________________
TArrayL *TASImage::GetPixels(Int_t x, Int_t y, UInt_t width, UInt_t height)
{
   // returns 2D array of machine dependent pixel values

   if (!fImage) {
      Warning("GetPixels", "Wrong Image");
      return 0;
   }

   ASImage *img =  fScaledImage ? fScaledImage->fImage : fImage;
   ASImageDecoder *imdec;

   width = !width  ? img->width : width;
   height = !height ? img->height : height;

   if (x < 0) {
      width -= x;
      x = 0 ;
   }
   if (y < 0) {
      height -= y;
      y = 0;
   }

   if ((x >= (int)img->width) || (y >= (int)img->height)) {
      return 0;
   }

   if ((int)(x + width) > (int)img->width) {
      width = img->width - x;
   }

   if ((int)(y + height) > (int)img->height) {
      height = img->height - y;
   }

   if ((imdec = start_image_decoding(NULL, fImage, SCL_DO_ALL, 0, y,
                                     img->width, height, NULL)) == NULL) {
      Warning("GetPixels", "Failed to create image decoder");
      return 0;
   }

   TArrayL *ret = new TArrayL(width * height);
   Int_t r = 0;
   Int_t g = 0;
   Int_t b = 0;
   Long_t p = 0;

   for (UInt_t k = 0; k < height; k++) {
      imdec->decode_image_scanline(imdec);

      for (UInt_t i = 0; i < width; ++i)  {
         if ((r == (Int_t)imdec->buffer.red[i]) &&
             (g == (Int_t)imdec->buffer.green[i]) &&
             (b == (Int_t)imdec->buffer.blue[i])) {
         } else {
            r = (Int_t)imdec->buffer.red[i];
            g = (Int_t)imdec->buffer.green[i];
            b = (Int_t)imdec->buffer.blue[i];
            p = (Long_t)TColor::RGB2Pixel(r, g, b);
         }
         ret->AddAt(p, k*width + i);
      }
   }

   stop_image_decoding(&imdec);
   return ret;
}

//______________________________________________________________________________
TArrayD *TASImage::GetArray(UInt_t w, UInt_t h, TImagePalette *palette)
{
   // Converts an image into 2D array of doubles according to palette.
   // If palette is ZERO a color converted to double value [0, 1] according to formula
   //   Double_t((r << 16) + (g << 8) + b)/0xFFFFFF

   if (!fImage) {
      Warning("GetArray", "Wrong Image");
      return 0;
   }

   ASImageDecoder *imdec;

   w = w ? w : fImage->width;
   h = h ? h : fImage->height;

   if ((fImage->width != w) || (fImage->height != h)) {
      Scale(w, h);
   }

   ASImage *img = fScaledImage ? fScaledImage->fImage : fImage;

   if ((imdec = start_image_decoding(NULL, img, SCL_DO_ALL, 0, 0,
                                     img->width, 0, NULL)) == NULL) {
      Warning("GetArray", "Failed to create image decoder");
      return 0;
   }

   TArrayD *ret = new TArrayD(w * h);
   CARD32 r = 0;
   CARD32 g = 0;
   CARD32 b = 0;
   Int_t p = 0;
   Double_t v = 0;

   for (UInt_t k = 0; k < h; k++) {
      imdec->decode_image_scanline(imdec);

      for (UInt_t i = 0; i < w; ++i)  {
         if ((r == imdec->buffer.red[i]) &&
             (g == imdec->buffer.green[i]) &&
             (b == imdec->buffer.blue[i])) {
         } else {
            r = imdec->buffer.red[i];
            g = imdec->buffer.green[i];
            b = imdec->buffer.blue[i];
            if (palette) p = palette->FindColor(r, g, b);
         }
         v = palette ? palette->fPoints[p] : Double_t((r << 16) + (g << 8) + b)/0xFFFFFF;
         ret->AddAt(v, (h-k-1)*w + i);
      }
   }

   stop_image_decoding(&imdec);
   return ret;
}

//______________________________________________________________________________
void TASImage::DrawText(Int_t x, Int_t y, const char *text, Int_t size,
                        const char *color, const char *font_name,
                        EText3DType type, const char *fore_file, Float_t angle)
{
   // Draw text of size (in pixels for TrueType fonts)
   // at position (x, y) with color  specified by hex string.
   //   font_name - TrueType font's filename or X font spec or alias.
   //   3D style of text is one of the following:
   //     0 - plain 2D text, 1 - embossed, 2 - sunken, 3 - shade above,
   //     4 - shade below, 5 - embossed thick, 6 - sunken thick.
   //     7 - ouline above, 8 - ouline below, 9 - full ouline.
   //  fore_file specifies foreground texture of text.

   UInt_t width, height ;
   ARGB32 text_color = ARGB32_Black;
   ASImage *fore_im = 0;
   ASImage *text_im = 0;
   Bool_t ttfont = kFALSE;

   if (!InitVisual()) {
      Warning("DrawText", "Visual not initiated");
      return;
   }
   if (!InitVisual()) {
      Warning("DrawFillArea", "Visual not initiated");
      return;
   }

   TString fn = font_name;
   fn.Strip();

   if (fn.EndsWith(".ttf") || fn.EndsWith(".TTF")) {
      fn = gSystem->ExpandPathName(fn.Data());
      ttfont = kTRUE;
   }

   if (color) {
      parse_argb_color(color, &text_color);
   }

   if (fImage->alt.argb32 && ttfont) {
      DrawTextTTF(x, y, text, size, text_color, fn.Data(), angle);
      return;
   }

   if (!gFontManager) {
      gFontManager = create_font_manager(dpy, 0, 0);
   }

   if (!gFontManager) {
      Warning("DrawText", "cannot create Font Manager");
      return;
   }

   ASFont *font = get_asfont(gFontManager, fn.Data(), 0, size, ASF_GuessWho);

   if (!font) {
      font = get_asfont(gFontManager, "fixed", 0, size, ASF_GuessWho);
      if (!font) {
         Warning("DrawText", "cannot find a font %s", font_name);
         return;
      }
   }

   get_text_size(text, font, (ASText3DType)type, &width, &height);
   text_im = draw_text(text, font, (ASText3DType)type, 0);

   ASImage *rimg = fImage;

   if (fore_file) {
      ASImage *tmp = file2ASImage(fore_file, 0xFFFFFFFF, SCREEN_GAMMA, 0, NULL);
      if (tmp) {
         if ((tmp->width != width) || (tmp->height != height)) {
            fore_im = tile_asimage(fgVisual, tmp, 0, 0, width, height, 0,
                                   ASA_ASImage, GetImageCompression(), GetImageQuality());
         }
         destroy_asimage(&tmp);
      } else {
         fore_im = tmp;
      }
   }

   if (fore_im) {
      move_asimage_channel(fore_im, IC_ALPHA, text_im, IC_ALPHA);
      destroy_asimage(&text_im);
   } else {
      fore_im = text_im ;
   }

   release_font(font);

   if (fore_im) {
      ASImage *rendered_im;
      ASImageLayer layers[2];

      init_image_layers(&(layers[0]), 2);
      fore_im->back_color = text_color;
      layers[0].im = rimg;
      layers[0].dst_x = 0;
      layers[0].dst_y = 0;
      layers[0].clip_width = rimg->width;
      layers[0].clip_height = rimg->height;
      layers[0].bevel = 0;
      layers[1].im = fore_im;
      layers[1].dst_x = x;
      layers[1].dst_y = y;
      layers[1].clip_width = fore_im->width;
      layers[1].clip_height = fore_im->height;

      rendered_im = merge_layers(fgVisual, &(layers[0]), 2, rimg->width, rimg->height,
                                 ASA_ASImage, GetImageCompression(), GetImageQuality());

      destroy_asimage(&fore_im);
      DestroyImage();
      fImage = rendered_im;
      UnZoom();
   }
}

//______________________________________________________________________________
void TASImage::Merge(const TImage *im, const char *op, Int_t x, Int_t y)
{
   // Merge two images.
   //
   // op is string which specifies overlay operation. Supported operations are:
   //    add            - color addition with saturation
   //    alphablend     - alpha-blending
   //    allanon        - color values averaging
   //    colorize       - hue and saturate bottom image same as top image
   //    darken         - use lowest color value from both images
   //    diff           - use absolute value of the color difference between two images
   //    dissipate      - randomly alpha-blend images
   //    hue            - hue bottom image same as top image
   //    lighten        - use highest color value from both images
   //    overlay        - some wierd image overlaying(see GIMP)
   //    saturate       - saturate bottom image same as top image
   //    screen         - another wierd image overlaying(see GIMP)
   //    sub            - color substraction with saturation
   //    tint           - tinting image with image
   //    value          - value bottom image same as top image

   if (!im) return;

   if (!InitVisual()) {
      Warning("Merge", "Visual not initiated");
      return;
   }

   ASImage *rendered_im;
   ASImageLayer layers[2];

   init_image_layers(&(layers[0]), 2);
   layers[0].im = fImage;
   layers[0].dst_x = 0;
   layers[0].dst_y = 0;
   layers[0].clip_width = fImage->width;
   layers[0].clip_height = fImage->height;
   layers[0].bevel = 0;
   layers[1].im = ((TASImage*)im)->fImage;
   layers[1].dst_x = x;
   layers[1].dst_y = y;
   layers[1].clip_width = im->GetWidth();
   layers[1].clip_height = im->GetHeight();
   layers[1].merge_scanlines = blend_scanlines_name2func(op ? op : "add");

   rendered_im = merge_layers(fgVisual, &(layers[0]), 2, fImage->width, fImage->height,
                              ASA_ASImage, GetImageCompression(), GetImageQuality());

   DestroyImage();
   fImage = rendered_im;
   UnZoom();
}

//______________________________________________________________________________
void TASImage::Blur(Double_t hr, Double_t vr)
{
   // Performs Gaussian blurr of the image (usefull for drop shadows)
   //    hr         - horizontal radius of the blurr
   //    vr         - vertical radius of the blurr

   if (!InitVisual()) {
      Warning("Blur", "Visual not initiated");
      return;
   }

   if (!fImage) {
      fImage = create_asimage(100, 100, 0);

      if (!fImage) {
         Warning("Blur", "Failed to create image");
         return;
      }

      fill_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height, ARGB32_White);
   }

   ASImage *rendered_im = blur_asimage_gauss(fgVisual, fImage, hr > 0 ? hr : 3,
                                             vr > 0 ? vr : 3, SCL_DO_ALL,
                                             ASA_ASImage, GetImageCompression(), GetImageQuality());
   DestroyImage();
   fImage = rendered_im;
   UnZoom();
}

//______________________________________________________________________________
TObject *TASImage::Clone(const char *newname) const
{
   // clone image

   if (!InitVisual() || !fImage) {
      Warning("Clone", "Image not initiated");
      return 0;
   }

   TASImage *im = (TASImage*)TImage::Create();

   if (!im) {
      Warning("Clone", "Failed to create image");
      return 0;
   }

   im->SetName(newname);

   im->fImage = clone_asimage(fImage, SCL_DO_ALL);
   im->fMaxValue = fMaxValue;
   im->fMinValue = fMinValue;
   im->fZoomOffX = fZoomOffX;
   im->fZoomOffY = fZoomOffY;
   im->fZoomWidth = fZoomWidth;
   im->fZoomHeight = fZoomHeight;
   im->fZoomUpdate = fZoomUpdate;
   im->fScaledImage = fScaledImage ? (TASImage*)fScaledImage->Clone("") : 0;

   if (fImage->alt.argb32) {
      UInt_t sz = fImage->width * fImage->height;
      im->fImage->alt.argb32 = new ARGB32[sz];
      memcpy(im->fImage->alt.argb32, fImage->alt.argb32, sz * sizeof(ARGB32));
   }

   return im;
}

//______________________________________________________________________________
void TASImage::Vectorize(UInt_t max_colors, UInt_t dither, Int_t opaque_threshold)
{
   // Reduces colordepth of an image and fills vector of "scientific data" [0...1]
   //
   // Colors are reduced by allocating colorcells to most used colors first,
   // and then approximating other colors with those allocated.
   // max_colors       - maximum size of the colormap.
   // dither           - number of bits to strip off the color data ( 0...7 )
   // opaque_threshold - alpha channel threshold at which pixel should be
   //                    treated as opaque

   if (!InitVisual()) {
      Warning("Vectorize", "Visual not initiated");
      return;
   }

   if (!fImage) {
      fImage = create_asimage(100, 100, 0);

      if (!fImage) {
         Warning("Vectorize", "Failed to create image");
         return;
      }

      fill_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height, ARGB32_White);
   }

   ASColormap cmap;
   int *res;
   UInt_t r, g, b;

   dither = dither > 7 ? 7 : dither;

   res = colormap_asimage(fImage, &cmap, max_colors, dither, opaque_threshold);

   Double_t *vec = new Double_t[fImage->height*fImage->width];
   UInt_t v;
   Double_t tmp;
   fMinValue = 2;
   fMaxValue = -1;

   for (UInt_t y = 0; y < fImage->height; y++) {
      for (UInt_t x = 0; x < fImage->width; x++) {
         int i = y*fImage->width + x;
         g = INDEX_SHIFT_GREEN(cmap.entries[res[i]].green);
         b = INDEX_SHIFT_BLUE(cmap.entries[res[i]].blue);
         r = INDEX_SHIFT_RED(cmap.entries[res[i]].red);
         v = MAKE_INDEXED_COLOR24(r,g,b);
         v = (v>>12)&0x0FFF;
         tmp = Double_t(v)/0x0FFF;
         vec[(fImage->height - y - 1)*fImage->width + x] = tmp;
         if (fMinValue > tmp) fMinValue = tmp;
         if (fMaxValue < tmp) fMaxValue = tmp;
      }
   }
   TImagePalette *pal = new TImagePalette(cmap.count);

   for (UInt_t j = 0; j < cmap.count; j++) {
      g = INDEX_SHIFT_GREEN(cmap.entries[j].green);
      b = INDEX_SHIFT_BLUE(cmap.entries[j].blue);
      r = INDEX_SHIFT_RED(cmap.entries[j].red);
      v = MAKE_INDEXED_COLOR24(r,g,b);

      v = (v>>12) & 0x0FFF;
      pal->fPoints[j] = Double_t(v)/0x0FFF;

      pal->fColorRed[j] = cmap.entries[j].red << 8;
      pal->fColorGreen[j] = cmap.entries[j].green << 8;
      pal->fColorBlue[j] = cmap.entries[j].blue << 8;
      pal->fColorAlpha[j] = 0xFF00;
   }

   destroy_colormap(&cmap, kTRUE);

   fPalette = *pal;
   fImage->alt.vector = vec;
   UnZoom();
}

//______________________________________________________________________________
void TASImage::HSV(UInt_t hue, UInt_t radius, Int_t H, Int_t S, Int_t V,
                   Int_t x, Int_t y, UInt_t width, UInt_t height)
{
   // This function will tile original image to specified size with offsets
   // requested, and then it will go though it and adjust hue, saturation and
   // value of those pixels that have specific hue, set by affected_hue/
   // affected_radius parameters. When affected_radius is greater then 180
   // entire image will be adjusted. Note that since grayscale colors have
   // no hue - the will not get adjusted. Only saturation and value will be
   // adjusted in gray pixels.
   // Hue is measured as an angle on a 360 degree circle, The following is
   // relationship of hue values to regular color names :
   // red      - 0
   // yellow   - 60
   // green    - 120
   // cyan     - 180
   // blue     - 240
   // magenta  - 300
   // red      - 360
   //
   // All the hue values in parameters will be adjusted to fall withing 0-360 range.

   // hue - hue in degrees in range 0-360. This allows to limit
   //       impact of color adjustment to affect only limited range of hues.
   //
   // radius - value in degrees to be used in order to
   //          calculate the range of affected hues. Range is determined by
   //          substracting and adding this value from/to affected_hue.
   //
   // H -   value by which to change hues in affected range.
   // S -   value by which to change saturation of the pixels in affected hue range.
   // V -   value by which to change Value(brightness) of pixels in affected hue range.
   //
   // x,y - position on infinite surface tiled with original image, of the
   //       left-top corner of the area to be used for new image.
   //
   // width, height - size of the area of the original image to be used for new image.
   //                 Default is current width, height of the image.

   if (!InitVisual()) {
      Warning("HSV", "Visual not initiated");
      return;
   }

   if (!fImage) {
      fImage = create_asimage(width ? width : 20, height ? height : 20, 0);

      if (!fImage) {
         Warning("HSV", "Failed to create image");
         return;
      }

      x = 0;
      y = 0;
      fill_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height, ARGB32_White);
   }

   width = !width ? fImage->width : width;
   height = !height ? fImage->height : height;

   ASImage *rendered_im = 0;

   if (H || S || V) {
      rendered_im = adjust_asimage_hsv(fgVisual, fImage, x, y, width, height,
                                       hue, radius, H, S, V, ASA_ASImage, 100,
                                       ASIMAGE_QUALITY_TOP);
   }
   if (!rendered_im) {
      Warning("HSV", "Failed to create rendered image");
      return;
   }

   DestroyImage();
   fImage = rendered_im;
   UnZoom();
}

//______________________________________________________________________________
void TASImage::Gradient(UInt_t angle, const char *colors, const char *offsets,
                        Int_t x, Int_t y, UInt_t width, UInt_t height)
{
   // Render multipoint gradient inside rectangle of size (width, height)
   // at position (x,y) within the existing image.
   //
   // angle    Given in degrees.  Default is 0.  This is the
   //          direction of the gradient.  Currently the only supported
   //          values are 0, 45, 90, 135, 180, 225, 270, 315.  0 means left
   //          to right, 90 means top to bottom, etc.
   //
   // colors   Whitespace-separated list of colors.  At least two
   //          colors are required.  Each color in this list will be visited
   //          in turn, at the intervals given by the offsets attribute.
   //
   // offsets  Whitespace-separated list of floating point values
   //          ranging from 0.0 to 1.0.  The colors from the colors attribute
   //          are given these offsets, and the final gradient is rendered
   //          from the combination of the two.  If both colors and offsets
   //          are given but the number of colors and offsets do not match,
   //          the minimum of the two will be used, and the other will be
   //          truncated to match.  If offsets are not given, a smooth
   //          stepping from 0.0 to 1.0 will be used.

   if (!InitVisual()) {
      Warning("Gradient", "Visual not initiated");
      return;
   }

   ASImage *rendered_im = 0;
   ASGradient gradient;

   int reverse = 0, npoints1 = 0, npoints2 = 0;
   char *p;
   char *pb, ch;

   if ((angle > 2 * 180 * 15 / 16) || (angle < 2 * 180 * 1 / 16)) {
      gradient.type = GRADIENT_Left2Right;
   } else if (angle < 2 * 180 * 3 / 16) {
      gradient.type = GRADIENT_TopLeft2BottomRight;
   } else if (angle < 2 * 180 * 5 / 16) {
      gradient.type = GRADIENT_Top2Bottom;
   } else if (angle < 2 * 180 * 7 / 16) {
      gradient.type = GRADIENT_BottomLeft2TopRight; reverse = 1;
   } else if (angle < 2 * 180 * 9 / 16) {
      gradient.type = GRADIENT_Left2Right; reverse = 1;
   } else if (angle < 2 * 180 * 11 / 16) {
      gradient.type = GRADIENT_TopLeft2BottomRight; reverse = 1;
   } else if (angle < 2 * 180 * 13 / 16) {
      gradient.type = GRADIENT_Top2Bottom; reverse = 1;
   } else {
      gradient.type = GRADIENT_BottomLeft2TopRight;
   }

   for (p = (char*)colors; isspace((int)*p); p++);

   for (npoints1 = 0; *p; npoints1++) {
      if (*p) {
         for ( ; *p && !isspace((int)*p); p++);
      }
      for ( ; isspace((int)*p); p++);
   }
   if (offsets) {
      for (p = (char*)offsets; isspace((int)*p); p++);

      for (npoints2 = 0; *p; npoints2++) {
         if (*p) {
            for ( ; *p && !isspace((int)*p); p++);
         }
         for ( ; isspace((int)*p); p++);
      }
   }
   if (npoints1 > 1) {
      int i;
      if (offsets && (npoints1 > npoints2)) npoints1 = npoints2;

      if (!width) {
         width = fImage ? fImage->width : 20;
      }
      if (!height) {
         height = fImage ? fImage->height : 20;
      }

      gradient.color = new ARGB32[npoints1];
      gradient.offset = new double[npoints1];

      for (p = (char*)colors; isspace((int)*p); p++);

      for (npoints1 = 0; *p; ) {
         pb = p;

         if (*p) {
            for ( ; *p && !isspace((int)*p); p++);
         }
         for ( ; isspace((int)*p); p++);

        ch = *p; *p = '\0';
         if (parse_argb_color(pb, gradient.color + npoints1) != pb) {
         npoints1++;
         } else {
            Warning("Gradient", "Failed to parse color [%s] - defaulting to black", pb);
         }
         *p = ch;
      }

      if (offsets) {
         for (p = (char*)offsets; isspace((int)*p); p++);

         for (npoints2 = 0; *p; ) {
            pb = p;

            if (*p) {
               for ( ; *p && !isspace((int)*p); p++);
            }
            ch = *p; *p = '\0';
            gradient.offset[npoints2] = strtod(pb, &pb);

            if (pb == p) npoints2++;
            *p = ch;
            for ( ; isspace((int)*p); p++);
         }
      } else {
         for (npoints2 = 0; npoints2 < npoints1; npoints2++) {
            gradient.offset[npoints2] = (double)npoints2 / (npoints1 - 1);
         }
      }
      gradient.npoints = npoints1;

      if (npoints2 && (gradient.npoints > npoints2)) {
         gradient.npoints = npoints2;
      }
      if (reverse) {
         for (i = 0; i < gradient.npoints/2; i++) {
            int i2 = gradient.npoints - 1 - i;
            ARGB32 c = gradient.color[i];
            double o = gradient.offset[i];
            gradient.color[i] = gradient.color[i2];
            gradient.color[i2] = c;
            gradient.offset[i] = gradient.offset[i2];
            gradient.offset[i2] = o;
         }
         for (i = 0; i < gradient.npoints; i++) {
            gradient.offset[i] = 1.0 - gradient.offset[i];
         }
      }
      rendered_im = make_gradient(fgVisual, &gradient, width, height, SCL_DO_ALL,
                                  ASA_ASImage, GetImageCompression(), GetImageQuality());

      delete [] gradient.color;
      delete [] gradient.offset;
   }

   if (!rendered_im) {  // error
      Warning("Gradient", "Failed to create gradient image");
      return;
   }

   if (!fImage) {
      fImage = rendered_im;
      return;
   }

   ASImageLayer layers[2];

   init_image_layers(&(layers[0]), 2);
   layers[0].im = fImage;
   layers[0].dst_x = 0;
   layers[0].dst_y = 0;
   layers[0].clip_width = fImage->width;
   layers[0].clip_height = fImage->height;
   layers[0].bevel = 0;
   layers[1].im = rendered_im;
   layers[1].dst_x = x;
   layers[1].dst_y = y;
   layers[1].clip_width = width;
   layers[1].clip_height = height;
   layers[1].merge_scanlines = alphablend_scanlines;

   ASImage *merge_im = merge_layers(fgVisual, &(layers[0]), 2, fImage->width, fImage->height,
                                    ASA_ASImage, GetImageCompression(), GetImageQuality());
   if (!merge_im) {
      Warning("Gradient", "Failed to create merged image");
      return;
   }

   destroy_asimage(&rendered_im);
   DestroyImage();
   fImage = merge_im;
   UnZoom();
}

/////////////// auxilary funcs used in TASImage::Bevel method //////////////////
//______________________________________________________________________________
static CARD8 MakeComponentHilite(int cmp)
{
   //

   if (cmp < 51) {
      cmp = 51;
   }
   cmp = (cmp * 12) / 10;

   return (cmp > 255) ? 255 : cmp;
}

//______________________________________________________________________________
static ARGB32 GetHilite(ARGB32 background)
{
   // calculates highlite color

   return ((MakeComponentHilite((background>>24) & 0x000000FF) << 24) & 0xFF000000) |
           ((MakeComponentHilite((background & 0x00FF0000) >> 16) << 16) & 0x00FF0000) |
           ((MakeComponentHilite((background & 0x0000FF00) >> 8) << 8) & 0x0000FF00) |
           ((MakeComponentHilite((background & 0x000000FF))) & 0x000000FF);
}

//______________________________________________________________________________
static ARGB32 GetShadow(ARGB32 background)
{
   // calculates shadow color

   return (background >> 1) & 0x7F7F7F7F;
}

//______________________________________________________________________________
static ARGB32 GetAverage(ARGB32 foreground, ARGB32 background)
{
   //

   CARD16   a, r, g, b;

   a = ARGB32_ALPHA8(foreground) + ARGB32_ALPHA8(background);
   a = (a<<3)/10;
   r = ARGB32_RED8(foreground) + ARGB32_RED8(background);
   r = (r<<3)/10;
   g = ARGB32_GREEN8(foreground) + ARGB32_GREEN8(background);
   g = (g<<3)/10;
   b = ARGB32_BLUE8(foreground) + ARGB32_BLUE8(background);
   b = (b<<3)/10;

   return MAKE_ARGB32(a, r, g, b);
}

//______________________________________________________________________________
void TASImage::Bevel(Int_t x, Int_t y, UInt_t width, UInt_t height,
                     const char *hi_color, const char *lo_color, UShort_t thick,
                     Bool_t reverse)
{
   //  Bevel is used to create 3D effect while drawing buttons, or any other
   // image that needs to be framed. Bevel is drawn using 2 primary colors:
   // one for top and left sides - hi color, and another for bottom and
   // right sides - low color. Bevel can be drawn over exisiting image or
   // as newly created,  as it is shown in code below:
   //
   //  TImage *img = TImage::Create();
   //  img->Bevel(0, 0, 400, 300, "#dddddd", "#000000", 3);
   //

   if (!InitVisual()) {
      Warning("Bevel", "Visual not initiated");
      return;
   }

   ASImageBevel bevel;

   ARGB32 hi, lo;
   parse_argb_color(hi_color, &hi);
   parse_argb_color(lo_color, &lo);

   if (reverse) {
      bevel.lo_color = hi;
      bevel.lolo_color = GetHilite(hi);
      bevel.hi_color = lo;
      bevel.hihi_color = GetShadow(lo);
   } else {
      bevel.hi_color = hi;
      bevel.hihi_color = GetHilite(hi);
      bevel.lo_color = lo;
      bevel.lolo_color = GetShadow(lo);
   }
   bevel.hilo_color = GetAverage(hi, lo);

   int extra_hilite = 2;
   bevel.left_outline = bevel.top_outline = bevel.right_outline = bevel.bottom_outline = thick;
   bevel.left_inline = bevel.top_inline = bevel.right_inline = bevel.bottom_inline = extra_hilite + 1;

   if (bevel.top_outline > 1) {
      bevel.top_inline += bevel.top_outline - 1;
   }

   if (bevel.left_outline > 1) {
      bevel.left_inline += bevel.left_outline - 1;
   }

   if (bevel.right_outline > 1) {
      bevel.right_inline += bevel.right_outline - 1;
   }

   if (bevel.bottom_outline > 1) {
      bevel.bottom_inline += bevel.bottom_outline - 1;
   }

   ASImage *merge_im;
   ARGB32 fill = ((hi>>24) != 0xff) || ((lo>>24) != 0xff) ? bevel.hilo_color : (bevel.hilo_color | 0xff000000);

   if (!fImage) {
      fImage = create_asimage(width ? width : 20, height ? height : 20, 0);

      if (!fImage) {
         Warning("Bevel", "Failed to create image");
         return;
      }

      x = 0;
      y = 0;
      fill_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height, fill);
   }

   width = !width ? fImage->width : width;
   height = !height ? fImage->height : height;

   ASImageLayer layers[2];
   init_image_layers(&(layers[0]), 2);

   layers[0].im = fImage;
   layers[0].dst_x = 0;
   layers[0].dst_y = 0;
   layers[0].clip_width = fImage->width;
   layers[0].clip_height = fImage->height;
   layers[0].bevel = 0;

   UInt_t w = width - (bevel.left_outline + bevel.right_outline);
   UInt_t h = height - (bevel.top_outline + bevel.bottom_outline);
   ASImage *bevel_im = create_asimage(w, h, 0);

   if (!bevel_im) {
      Warning("Bevel", "Failed to create bevel image");
      return;
   }

   layers[1].im = bevel_im;
   fill_asimage(fgVisual, bevel_im, 0, 0, w, h, fill);

   layers[1].dst_x = x;
   layers[1].dst_y = y;
   layers[1].clip_width = width;
   layers[1].clip_height = height;
   layers[1].bevel = &bevel;
   layers[1].merge_scanlines = alphablend_scanlines;

   merge_im = merge_layers(fgVisual, &(layers[0]), 2, fImage->width, fImage->height,
                           ASA_ASImage, GetImageCompression(), GetImageQuality());
   destroy_asimage(&bevel_im);

   if (!merge_im) {
      Warning("Bevel", "Failed to image");
      return;
   }

   DestroyImage();
   fImage = merge_im;
   UnZoom();
}

//______________________________________________________________________________
void TASImage::Pad(const char *col, UInt_t l, UInt_t r, UInt_t t, UInt_t b)
{
   // Enlarges image, padding it with specified color on each side in
   // accordance with requested geometry.

   Int_t x, y;
   UInt_t w, h;

   if (!InitVisual()) {
      Warning("Pad", "Visual not initiated");
      return;
   }

   if (!fImage) {
      fImage = create_asimage(100, 100, 0);

      if (!fImage) {
         Warning("Pad", "Failed to create image");
         return;
      }

      x = 0;
      y = 0;
      fill_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height, ARGB32_White);
   }

   ARGB32 color;
   parse_argb_color(col, &color);

   x = l;
   y = t;
   w = l + fImage->width + r;
   h = t + fImage->height + b;

   ASImage *img = pad_asimage(fgVisual, fImage, x, y, w, h, color,
                              ASA_ASImage, GetImageCompression(), GetImageQuality());

   if (!img) {
      Warning("Pad", "Failed to create output image");
      return;
   }

   DestroyImage();
   fImage = img;
   UnZoom();
   fZoomUpdate = kZoomOps;
}

//______________________________________________________________________________
void TASImage::Crop(Int_t x, Int_t y, UInt_t width, UInt_t height)
{
   // Crops an image

   if (!InitVisual()) {
      Warning("Crop", "Visual not initiated");
      return;
   }

   if (!fImage) {
      Warning("Crop", "No image");
      return;
   }

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

   width = x + width > fImage->width ? fImage->width - x : width;
   height = y + height > fImage->height ? fImage->height - y : height;

   if ((width == fImage->width) && (height == fImage->height)) {
      Warning("Crop", "input size larger than image");
      return;
   }
   ASImageDecoder *imdec = start_image_decoding(fgVisual, fImage, SCL_DO_ALL,
                                                x, y, width, height, NULL);

   if (!imdec) {
      Warning("Crop", "Failed to start image decoding");
      return;
   }

   ASImage *img = create_asimage(width, height, 0);

   if (!img) {
      Warning("Crop", "Failed to create image");
      return;
   }

   ASImageOutput *imout = start_image_output(fgVisual, img, ASA_ASImage,
                                             GetImageCompression(), GetImageQuality());

   if (!imout) {
      Warning("Crop", "Failed to start image output");
      destroy_asimage(&img);
      return;
   }

#ifdef HAVE_MMX
   mmx_init();
#endif

   for (UInt_t i = 0; i < height; i++) {
      imdec->decode_image_scanline(imdec);
      imout->output_image_scanline(imout, &(imdec->buffer), 1);
   }

   stop_image_decoding(&imdec);
   stop_image_output(&imout);

#ifdef HAVE_MMX
   mmx_off();
#endif

   DestroyImage();
   fImage = img;
   UnZoom();
   fZoomUpdate = kZoomOps;
}

//______________________________________________________________________________
void TASImage::Append(const TImage *im, const char *option, const char *color )
{
   // Appends image
   //
   // option:
   //       "+" - appends to the right side
   //       "/" - appends to the bottom

   if (!im) return;

   if (!InitVisual()) {
      Warning("Append", "Visual not initiated");
      return;
   }

   if (!fImage) {
      fImage = ((TASImage*)im)->fImage;
      return;
   }

   TString opt = option;
   opt.Strip();

   UInt_t width = fImage->width;
   UInt_t height = fImage->height;

   if (opt == "+") {
      Pad(color, 0, im->GetWidth(), 0, 0);
      Merge(im, "alphablend", width, 0);
   } else if (opt == "/") {
      Pad(color, 0, 0, 0, im->GetHeight());
      Merge(im, "alphablend", 0, height);
   } else {
      return;
   }

   UnZoom();
}

//______________________________________________________________________________
void TASImage::BeginPaint(Bool_t mode)
{
   // BeginPaint initializes internal array[width x height] of ARGB32 pixel values
   // That provides quick access to image during paint operations.
   // To RLE compress image one needs to call EndPaint method when paintinig is over

   if (!InitVisual()) {
      Warning("BeginPaint", "Visual not initiated");
      return;
   }

   if (!fImage) {
      Warning("BeginPaint", "no image");
      return;
   }

   fPaintMode = mode;

   if (!fPaintMode || fImage->alt.argb32) {
      return;
   }

   ASImage *img = tile_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height,
                               0, ASA_ARGB32, 0, ASIMAGE_QUALITY_DEFAULT);

   if (!img) {
      Warning("BeginPaint", "Failed to create image");
      return;
   }

   DestroyImage();
   fImage = img;
}

//______________________________________________________________________________
void TASImage::EndPaint()
{
   // EndPaint does internal RLE compression of image data

   if (!fImage) {
      Warning("EndPaint", "no image");
      return;
   }

   if (!fImage->alt.argb32) return;

   ASImage *img = tile_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height,
                               0, ASA_ASImage, 0, ASIMAGE_QUALITY_DEFAULT);

   if (!img) {
      Warning("EndPaint", "Failed to create image");
      return;
   }

   fPaintMode = kFALSE;
   DestroyImage();
   fImage = img;
}

//______________________________________________________________________________
UInt_t *TASImage::GetArgbArray()
{
   // Returns a pointer to internal array[width x height] of ARGB32 pixel values
   // This array is directly acessible. That allows to manipulate/change the image

   if (!fImage) {
      Warning("GetArgbArray", "no image");
      return 0;
   }

   if (!fImage->alt.argb32) {
      BeginPaint();
   }

   ASImage *img = fScaledImage ? fScaledImage->fImage : fImage;

   return (UInt_t *)img->alt.argb32;
}

//______________________________________________________________________________
UInt_t *TASImage::GetScanline(UInt_t y)
{
   // return a pointer to scanline

   if (!fImage) {
      Warning("GetScanline", "no image");
      return 0;
   }

   ASImage *img = fScaledImage ? fScaledImage->fImage : fImage;
   CARD32 *ret = new CARD32[img->width];

   ASImageDecoder *imdec = start_image_decoding(fgVisual, img, SCL_DO_ALL,
                                                0, y, img->width, 1, NULL);

   if (!imdec) {
      Warning("GetScanline", "Failed to start image decoding");
      return 0;
   }

#ifdef HAVE_MMX
   mmx_init();
#endif

   imdec->decode_image_scanline(imdec);
   memcpy(imdec->buffer.buffer, ret, img->width*sizeof(CARD32));
   stop_image_decoding(&imdec);

#ifdef HAVE_MMX
   mmx_off();
#endif

   return (UInt_t*)ret;
}

/////////////////////////////// vector graphics ///////////////////////////////
// a couple of macros which can be "assembler accelerated"
#if defined(R__GNU) && defined(__i386__)
#define _MEMSET_(dst, lng, val)   __asm__("movl  %0,%%eax \n"\
                                      "movl  %1,%%edi \n"              \
                                      "movl  %2,%%ecx \n"              \
                                      "cld \n"                         \
                                      "rep \n"                         \
                                      "stosl %%eax,(%%edi) \n"         \
                                      : /* no output registers */      \
                                      :"g" (val), "g" (dst), "g" (lng) \
                                      :"eax","edi","ecx"               \
                                     )

#else
 #define _MEMSET_(dst, lng, val) do {\
 for( UInt_t j=0; j < lng; j++) *((dst)+j) = val; } while (0)

#endif

#define FillSpansInternal(npt, ppt, widths, color) do {\
   UInt_t yy = ppt[0].fY*fImage->width;\
   for (UInt_t i = 0; i < npt; i++) {\
      _MEMSET_(&fImage->alt.argb32[yy + ppt[i].fX], widths[i], color);\
      yy += ((i+1 < npt) && (ppt[i].fY != ppt[i+1].fY) ? fImage->width : 0);\
   }\
} while (0)


//______________________________________________________________________________
void TASImage::FillRectangleInternal(UInt_t col, Int_t x, Int_t y, UInt_t width, UInt_t height)
{
   // Fills rectangle of size (width, height) at position (x,y)
   // within the existing image with specified color.
   //

   ARGB32 color = (ARGB32)col;

   width = !width ? fImage->width : width;
   height = !height ? fImage->height : height;

   if (x < 0) {
      width += x;
      x = 0;
   }
   if (y < 0) {
      height += y;
      y = 0;
   }

   Bool_t has_alpha = (color & 0xff000000) != 0xff000000;

   x = x > (int)fImage->width ? fImage->width : x;
   y = y > (int)fImage->height ? fImage->height : y;

   width = x + width > fImage->width ? fImage->width - x : width;
   height = y + height > fImage->height ? fImage->height - y : height;

   if (!fImage->alt.argb32) {
      fill_asimage(fgVisual, fImage, x, y, width, height, color);
   } else {
      int yyy = y*fImage->width;
      if (!has_alpha) { // use faster memset
         ARGB32 *p0 = fImage->alt.argb32 + yyy + x;
         ARGB32 *p = p0;
         for (UInt_t i = 0; i < height; i++) {
            _MEMSET_(p, width, color);
            p += fImage->width;
         }
      } else {
         for (UInt_t i = y; i < y + height; i++) {
            int j = x + width;
            while (j > x) {
               j--;
               _alphaBlend(&fImage->alt.argb32[yyy + j], &color);
            }
         }
         yyy += fImage->width;
      }
   }
}

//______________________________________________________________________________
void TASImage::FillRectangle(const char *col, Int_t x, Int_t y, UInt_t width, UInt_t height)
{
   // Fills rectangle of size (width, height) at position (x,y)
   // within the existing image with specified color.
   //
   // To create new image with Fill method the following code can be used:
   //
   //  TImage *img = TImage::Create();
   //  img->Fill("#FF00FF", 0, 0, 400, 300);

   if (!InitVisual()) {
      Warning("Fill", "Visual not initiated");
      return;
   }

   ARGB32 color = ARGB32_White;

   if (col) {
      parse_argb_color(col, &color);
   }

   if (!fImage) {
      fImage = create_asimage(width ? width : 20, height ? height : 20, 0);
      x = 0;
      y = 0;
   }

   FillRectangleInternal((UInt_t)color, x, y, width, height);
   UnZoom();
}

//______________________________________________________________________________
void TASImage::DrawVLine(UInt_t x, UInt_t y1, UInt_t y2, UInt_t col, UInt_t thick)
{
   // vertical line

   ARGB32 color = (ARGB32)col;
   UInt_t half = 0;

   if (thick > 1) {
      half = thick >> 1;
      if (x > half) {
         x =  x - half;
      } else {
         x = 0;
         thick += (x - half);
      }
   }

   y2 = y2 >= fImage->height ? fImage->height - 1 : y2;
   y1 = y1 >= fImage->height ? fImage->height - 1 : y1;
   x = x + thick >= fImage->width ? fImage->width - thick - 1 : x;

   int yy = y1*fImage->width;
   for (UInt_t y = y1; y <= y2; y++) {
      for (UInt_t w = 0; w < thick; w++) {
         if (x + w < fImage->width) {
            _alphaBlend(&fImage->alt.argb32[yy + (x + w)], &color);
         }
      }
      yy += fImage->width;
   }
}

//______________________________________________________________________________
void TASImage::DrawHLine(UInt_t y, UInt_t x1, UInt_t x2, UInt_t col, UInt_t thick)
{
   // horizontal line

   ARGB32 color = (ARGB32)col;
   UInt_t half = 0;

   if (thick > 1) {
      half = thick >> 1;
      if (y > half) {
         y =  y - half;
      } else {
         y = 0;
         thick += (y - half);
      }
   }

   y = y + thick >= fImage->height ? fImage->height - thick - 1 : y;
   x2 = x2 >= fImage->width ? fImage->width - 1 : x2;
   x1 = x1 >= fImage->width ? fImage->width - 1 : x1;

   int yy = y*fImage->width;
   for (UInt_t w = 0; w < thick; w++) {
      for (UInt_t x = x1; x <= x2; x++) {
         if (y + w < fImage->height) {
            _alphaBlend(&fImage->alt.argb32[yy + x], &color);
         }
      }
      yy += fImage->width;
   }
}

//______________________________________________________________________________
void TASImage::DrawLine(UInt_t x1,  UInt_t y1, UInt_t x2, UInt_t y2,
                        const char *col, UInt_t thick)

{
   // draw line

   ARGB32 color;
   parse_argb_color(col, &color);
   DrawLineInternal(x1, y1, x2, y2, (UInt_t)color, thick);
}

//______________________________________________________________________________
void TASImage::DrawLineInternal(UInt_t x1, UInt_t y1, UInt_t x2, UInt_t y2,
                                UInt_t col, UInt_t thick)
{
   // internal drawing

   int dx, dy, d;
   int i1, i2;
   int x, y, xend, yend;
   int xdir, ydir;
   int wid, q;
   int idx;
   int yy;

   if (!InitVisual()) {
      Warning("DrawLine", "Visual not initiated");
      return;
   }

   if (!fImage) {
      Warning("DrawLine", "no image");
      return;
   }

   if (!fImage->alt.argb32) {
      BeginPaint();
   }

   if (!fImage->alt.argb32) {
      Warning("DrawLine", "Failed to get pixel array");
      return;
   }

   ARGB32 color = (ARGB32)col;

   dx = TMath::Abs(Int_t(x2) - Int_t(x1));
   dy = TMath::Abs(Int_t(y2) - Int_t(y1));

   if (!dx) {
      DrawVLine(x1, y2 > y1 ? y1 : y2,
                    y2 > y1 ? y2 : y1, color, thick);
      return;
   }

   if (!dy) {
      DrawHLine(y1, x2 > x1 ? x1 : x2,
                    x2 > x1 ? x2 : x1, color, thick);
      return;
   }

   if (thick > 1) {
      DrawWideLine(x1, y1, x2, y2, color, thick);
      return;
   }

   wid = 1;

   if (dy <= dx) {
      UInt_t ddy = dy << 1;
      i1 = ddy;
      i2 = i1 - (dx << 1);
      d = i1 - dx;

      if (x1 > x2) {
         x = x2;
         y = y2;
         ydir = -1;
         xend = x1;
      } else {
         x = x1;
         y = y1;
         ydir = 1;
         xend = x2;
      }

      yy = y*fImage->width;
      _alphaBlend(&fImage->alt.argb32[yy + x], &color);
      q = (y2 - y1) * ydir;

      if (q > 0) {
         while (x < xend) {
            idx = yy + x;
            _alphaBlend(&fImage->alt.argb32[idx], &color);
            x++;

            if (d >= 0) {
               yy += fImage->width;
               d += i2;
            } else {
               d += i1;
            }
         }
      } else {
         while (x < xend) {
            idx = yy + x;
            _alphaBlend(&fImage->alt.argb32[idx], &color);
            x++;

            if (d >= 0) {
               yy -= fImage->width;
               d += i2;
            } else {
               d += i1;
            }
         }
      }
   } else {
      UInt_t ddx = dx << 1;
      i1 = ddx;
      i2 = i1 - (dy << 1);
      d = i1 - dy;

      if (y1 > y2) {
         y = y2;
         x = x2;
         yend = y1;
         xdir = -1;
      } else {
         y = y1;
         x = x1;
         yend = y2;
         xdir = 1;
      }

      yy = y*fImage->width;
      _alphaBlend(&fImage->alt.argb32[yy + x], &color);
      q = (x2 - x1) * xdir;

      if (q > 0) {
         while (y < yend) {
            idx = yy + x;
            _alphaBlend(&fImage->alt.argb32[idx], &color);
            y++;
            yy += fImage->width;

            if (d >= 0) {
               x++;
               d += i2;
            } else {
               d += i1;
            }
         }
      } else {
         while (y < yend) {
            idx = yy + x;
            _alphaBlend(&fImage->alt.argb32[idx], &color);
            y++;
            yy += fImage->width;

            if (d >= 0) {
               x--;
               d += i2;
            } else {
               d += i1;
            }
         }
      }
   }
}

//______________________________________________________________________________
void TASImage::DrawRectangle(UInt_t x, UInt_t y, UInt_t w, UInt_t h,
                             const char *col, UInt_t thick)
{
   // draw rectangle

   if (!InitVisual()) {
      Warning("DrawRectangle", "Visual not initiated");
      return;
   }

   if (!fImage) {
      Warning("DrawRectangle", "no image");
      return;
   }

   if (!fImage->alt.argb32) {
      BeginPaint();
   }

   if (!fImage->alt.argb32) {
      Warning("DrawRectangle", "Failed to get pixel array");
      return;
   }

   ARGB32 color;
   parse_argb_color(col, &color);

   DrawHLine(y, x, x + w, (UInt_t)color, thick);
   DrawVLine(x + w, y, y + h, (UInt_t)color, thick);
   DrawHLine(y + h, x, x + w, (UInt_t)color, thick);
   DrawVLine(x, y, y + h, (UInt_t)color, thick);
}

//______________________________________________________________________________
void TASImage::DrawBox(Int_t x1, Int_t y1, Int_t x2, Int_t y2, const char *col,
                       UInt_t thick, Int_t mode)
{
   // draw box

   Int_t x = TMath::Min(x1, x2);
   Int_t y = TMath::Min(y1, y2);
   Int_t w = TMath::Abs(x2 - x1);
   Int_t h = TMath::Abs(y2 - y1);

   ARGB32 color;

   if (x1 == x2) {
      parse_argb_color(col, &color);
      DrawVLine(x1, y1, y2, color, 1);
      return;
   }

   if (y1 == y2) {
      parse_argb_color(col, &color);
      DrawHLine(y1, x1, x2, color, 1);
      return;
   }


   switch (mode) {
      case TVirtualX::kHollow:
         DrawRectangle(x, y, w, h, col, thick);
         break;

      case TVirtualX::kFilled:
         FillRectangle(col, x, y, w, h);
         break;

      default:
         FillRectangle(col, x, y, w, h);
         break;
   }
}

//______________________________________________________________________________
void TASImage::DrawDashHLine(UInt_t y, UInt_t x1, UInt_t x2, UInt_t nDash,
                             const char *pDash, UInt_t col, UInt_t thick)
{
   // draw dashed horizontal line

   UInt_t iDash = 0;    // index of current dash
   int i = 0;

   ARGB32 color = (ARGB32)col;

   UInt_t half = 0;

   if (thick > 1) {
      half = thick >> 1;
      if (y > half) {
         y =  y - half;
      } else {
         y = 0;
         thick += (y - half);
      }
   }

   y = y + thick >= fImage->height ? fImage->height - thick - 1 : y;
   x2 = x2 >= fImage->width ? fImage->width - 1 : x2;
   x1 = x1 >= fImage->width ? fImage->width - 1 : x1;

   // switch x1, x2
   UInt_t tmp = x1;
   x1 = x2 < x1 ? x2 : x1;
   x2 = x2 < tmp ? tmp : x2;

   int yy = y*fImage->width;
   for (UInt_t w = 0; w < thick; w++) {
      for (UInt_t x = x1; x <= x2; x++) {
         if (y + w < fImage->height) {
            if ((iDash%2)==0) {
               _alphaBlend(&fImage->alt.argb32[yy + x], &color);
            }
         }
         i++;

         if (i >= pDash[iDash]) {
            iDash++;
            i = 0;
         }
         if (iDash >= nDash) {
            iDash = 0;
            i = 0;
         }
      }
      yy += fImage->width;
   }
}

//______________________________________________________________________________
void TASImage::DrawDashVLine(UInt_t x, UInt_t y1, UInt_t y2, UInt_t nDash,
                             const char *pDash, UInt_t col, UInt_t thick)
{
   // draw dashed vertical line

   UInt_t iDash = 0;    // index of current dash
   int i = 0;

   ARGB32 color = (ARGB32)col;

   UInt_t half = 0;

   if (thick > 1) {
      half = thick >> 1;
      if (x > half) {
         x =  x - half;
      } else {
         x = 0;
         thick += (x - half);
      }
   }

   y2 = y2 >= fImage->height ? fImage->height - 1 : y2;
   y1 = y1 >= fImage->height ? fImage->height - 1 : y1;

   // switch x1, x2
   UInt_t tmp = y1;
   y1 = y2 < y1 ? y2 : y1;
   y2 = y2 < tmp ? tmp : y2;

   x = x + thick >= fImage->width ? fImage->width - thick - 1 : x;

   int yy = y1*fImage->width;
   for (UInt_t y = y1; y <= y2; y++) {
      for (UInt_t w = 0; w < thick; w++) {
         if (x + w < fImage->width) {
            if ((iDash%2)==0) {
               _alphaBlend(&fImage->alt.argb32[yy + (x + w)], &color);
            }
         }
      }
      i++;

      if (i >= pDash[iDash]) {
         iDash++;
         i = 0;
      }
      if (iDash >= nDash) {
         iDash = 0;
         i = 0;
      }
      yy += fImage->width;
   }
}

//______________________________________________________________________________
void TASImage::DrawDashZLine(UInt_t x1, UInt_t y1, UInt_t x2, UInt_t y2,
                             UInt_t nDash, const char *tDash, UInt_t color)
{
   // draw dashed line with 1 pixel width

   int dx, dy, d;
   int i, i1, i2;
   int x, y, xend, yend;
   int xdir, ydir;
   int q;
   UInt_t iDash = 0;    // index of current dash
   int yy;
   int idx;

   dx = TMath::Abs(Int_t(x2) - Int_t(x1));
   dy = TMath::Abs(Int_t(y2) - Int_t(y1));

   char *pDash = new char[nDash];

   if (dy <= dx) {
      double ac = TMath::Cos(TMath::ATan2(dy, dx));

      for (i = 0; i < (int)nDash; i++) {
         pDash[i] = int(tDash[i] * ac);
      }

      UInt_t ddy = dy << 1;
      i1 = ddy;
      i2 = i1 - (dx << 1);
      d = i1 - dx;
      i = 0;

      if (x1 > x2) {
         x = x2;
         y = y2;
         ydir = -1;
         xend = x1;
      } else {
         x = x1;
         y = y1;
         ydir = 1;
         xend = x2;
      }

      yy = y*fImage->width;
      _alphaBlend(&fImage->alt.argb32[y*fImage->width + x], &color);
      q = (y2 - y1) * ydir;

      if (q > 0) {
         while (x < xend) {
            idx = yy + x;
            if ((iDash%2) == 0) {
               _alphaBlend(&fImage->alt.argb32[idx], &color);
            }
            x++;
            if (d >= 0) {
               yy += fImage->width;
               d += i2;
            } else {
               d += i1;
            }

            i++;
            if (i >= pDash[iDash]) {
               iDash++;
               i = 0;
            }
            if (iDash >= nDash) {
               iDash = 0;
               i = 0;
            }
         }
      } else {
         while (x < xend) {
            idx = yy + x;
            if ((iDash%2) == 0) {
               _alphaBlend(&fImage->alt.argb32[idx], &color);
            }
            x++;
            if (d >= 0) {
               yy -= fImage->width;
               d += i2;
            } else {
               d += i1;
            }

            i++;
            if (i >= pDash[iDash]) {
               iDash++;
               i = 0;
            }
            if (iDash >= nDash) {
               iDash = 0;
               i = 0;
            }
         }
      }
   } else {
      double as = TMath::Sin(TMath::ATan2(dy, dx));

      for (i = 0; i < (int)nDash; i++) {
         pDash[i] = int(tDash[i] * as);
      }

      UInt_t ddx = dx << 1;
      i1 = ddx;
      i2 = i1 - (dy << 1);
      d = i1 - dy;
      i = 0;

      if (y1 > y2) {
         y = y2;
         x = x2;
         yend = y1;
         xdir = -1;
      } else {
         y = y1;
         x = x1;
         yend = y2;
         xdir = 1;
      }

      yy = y*fImage->width;
      _alphaBlend(&fImage->alt.argb32[y*fImage->width + x], &color);
      q = (x2 - x1) * xdir;

      if (q > 0) {
         while (y < yend) {
            idx = yy + x;
            if ((iDash%2) == 0) {
               _alphaBlend(&fImage->alt.argb32[idx], &color);
            }
            y++;
            yy += fImage->width;

            if (d >= 0) {
               x++;
               d += i2;
            } else {
               d += i1;
            }

            i++;
            if (i >= pDash[iDash]) {
               iDash++;
               i = 0;
            }
            if (iDash >= nDash) {
               iDash = 0;
               i = 0;
            }
         }
      } else {
         while (y < yend) {
            idx = yy + x;
            if ((iDash%2) == 0) {
               _alphaBlend(&fImage->alt.argb32[idx], &color);
            }
            y++;
            yy += fImage->width;

            if (d >= 0) {
               x--;
               d += i2;
            } else {
               d += i1;
            }

            i++;
            if (i >= pDash[iDash]) {
               iDash++;
               i = 0;
            }
            if (iDash >= nDash) {
               iDash = 0;

               i = 0;
            }
         }
      }
   }
   delete [] pDash;
}

//______________________________________________________________________________
void TASImage::DrawDashLine(UInt_t x1,  UInt_t y1, UInt_t x2, UInt_t y2, UInt_t nDash,
                            const char *pDash, const char *col, UInt_t thick)

{
   // draw dashed line

   if (!InitVisual()) {
      Warning("DrawDashLine", "Visual not initiated");
      return;
   }

   if (!fImage) {
      Warning("DrawDashLine", "no image");
      return;
   }

   if (!fImage->alt.argb32) {
      BeginPaint();
   }

   if (!fImage->alt.argb32) {
      Warning("DrawDashLine", "Failed to get pixel array");
      return;
   }

   if ((nDash < 2) || !pDash || (nDash%2)) {
      Warning("DrawDashLine", "Wrong input parameters n=%d %d", nDash, sizeof(pDash)-1);
      return;
   }

   ARGB32 color;
   parse_argb_color(col, &color);

   if (x1 == x2) {
      DrawDashVLine(x1, y1, y2, nDash, pDash, (UInt_t)color, thick);
   } else if (y1 == y2) {
      DrawDashHLine(y1, x1, x2, nDash, pDash, (UInt_t)color, thick);
   } else {
      DrawDashZLine(x1, y1, x2, y2, nDash, pDash, (UInt_t)color);
   }
}

//______________________________________________________________________________
void TASImage::DrawPolyLine(UInt_t nn, TPoint *xy, const char *col, UInt_t thick,
                            TImage::ECoordMode mode)
{
   // draw polyline

   ARGB32 color;
   parse_argb_color(col, &color);

   Int_t x0 = xy[0].GetX();
   Int_t y0 = xy[0].GetY();
   Int_t x = 0;
   Int_t y = 0;

   for (UInt_t i = 1; i < nn; i++) {
      x = (mode == kCoordModePrevious) ? x + xy[i].GetX() : xy[i].GetX();
      y = (mode == kCoordModePrevious) ? y + xy[i].GetY() : xy[i].GetY();

      DrawLineInternal(x0, y0, x, y, (UInt_t)color, thick);

      x0 = x;
      y0 = y;
   }
}

//______________________________________________________________________________
void TASImage::PutPixel(Int_t x, Int_t y, const char *col)
{
   // draw point at specified position

   if (!InitVisual()) {
      Warning("PutPixel", "Visual not initiated");
      return;
   }

   if (!fImage) {
      Warning("PutPixel", "no image");
      return;
   }

   if (!fImage->alt.argb32) {
      BeginPaint();
   }

   if (!fImage->alt.argb32) {
      Warning("PutPixel", "Failed to get pixel array");
      return;
   }

   ARGB32 color;
   parse_argb_color(col, &color);

   if ((x < 0) || (y < 0) || (x >= (int)fImage->width) || (y >= (int)fImage->height)) {
      Warning("PutPixel", "Out of range width=%d x=%d, height=%d y=%d",
               fImage->width, x, fImage->height, y);
      return;
   }
   _alphaBlend(&fImage->alt.argb32[y*fImage->width + x], &color);
}

//______________________________________________________________________________
void TASImage::PolyPoint(UInt_t npt, TPoint *ppt, const char *col, TImage::ECoordMode mode)
{
   // draw poly point

   if (!InitVisual()) {
      Warning("PolyPoint", "Visual not initiated");
      return;
   }

   if (!fImage) {
      Warning("PolyPoint", "no image");
      return;
   }

   if (!fImage->alt.argb32) {
      BeginPaint();
   }

   if (!fImage->alt.argb32) {
      Warning("PolyPoint", "Failed to get pixel array");
      return;
   }

   if (!npt || !ppt) {
      Warning("PolyPoint", "No points specified");
      return;
   }

   TPoint *ipt = 0;
   UInt_t i = 0;
   ARGB32 color;
   parse_argb_color(col, &color);

   //make pointlist origin relative
   if (mode == kCoordModePrevious) {
      ipt = new TPoint[npt];

      for (i = 0; i < npt; i++) {
         ipt[i].fX += ppt[i].fX;
         ipt[i].fY += ppt[i].fY;
      }
   }
   int x, y;

   for (i = 0; i < npt; i++) {
      x = ipt ? ipt[i].fX : ppt[i].fX;
      y = ipt ? ipt[i].fY : ppt[i].fY;

      if ((x < 0) || (y < 0) || (x >= (int)fImage->width) || (y >= (int)fImage->height)) {
         continue;
      }
      _alphaBlend(&fImage->alt.argb32[y*fImage->width + x], &color);
   }

   if (ipt) {
      delete [] ipt;
   }
}

//______________________________________________________________________________
void TASImage::DrawSegments(UInt_t nseg, Segment_t *seg, const char *col, UInt_t thick)
{
   // draw segments

   if (!nseg || !seg) {
      Warning("DrawSegments", "Ivalid data nseg=%d seg=%d", nseg, seg);
      return;
   }

   TPoint pt[2];

   for (UInt_t i = 0; i < nseg; i++) {
      pt[0].fX = seg->fX1;
      pt[1].fX = seg->fX2;
      pt[0].fY = seg->fY1;
      pt[1].fY = seg->fY2;

      DrawPolyLine(2, pt, col, thick, kCoordModeOrigin);
      seg++;
   }
}

//______________________________________________________________________________
void TASImage::FillSpans(UInt_t npt, TPoint *ppt, UInt_t *widths, const char *col,
                         const char *stipple, UInt_t w, UInt_t h)
{
   // fill spans with specified color or/and stipple

   if (!InitVisual()) {
      Warning("FillSpans", "Visual not initiated");
      return;
   }

   if (!fImage) {
      Warning("FillSpans", "no image");
      return;
   }

   if (!fImage->alt.argb32) {
      BeginPaint();
   }

   if (!fImage->alt.argb32) {
      Warning("FillSpans", "Failed to get pixel array");
      return;
   }

   if (!npt || !ppt || !widths || (stipple && (!w || !h))) {
      Warning("FillSpans", "Invalid input data npt=%d ppt=%x col=%s widths=%x stipple=%x w=%d h=%d",
              npt, ppt, col, widths, stipple, w, h);
      return;
   }

   ARGB32 color;
   parse_argb_color(col, &color);
   Int_t idx = 0;
   UInt_t x = 0;
   UInt_t yy;

   for (UInt_t i = 0; i < npt; i++) {
      yy = ppt[i].fY*fImage->width;
      for (UInt_t j = 0; j < widths[i]; j++) {
         if ((ppt[i].fX >= (Int_t)fImage->width) || (ppt[i].fX < 0) ||
             (ppt[i].fY >= (Int_t)fImage->height) || (ppt[i].fY < 0)) continue;

         x = ppt[i].fX + j;
         idx = yy + x;

         if (!stipple) {
            _alphaBlend(&fImage->alt.argb32[idx], &color);
         } else {
            Int_t ii = (ppt[i].fY%h)*w + x%w;

            if (stipple[ii >> 3] & (1 << (ii%8))) {
               _alphaBlend(&fImage->alt.argb32[idx], &color);
            }
         }
      }
   }
}

//______________________________________________________________________________
void TASImage::FillSpans(UInt_t npt, TPoint *ppt, UInt_t *widths, TImage *tile)
{
   // fille spans with tile image

   if (!InitVisual()) {
      Warning("FillSpans", "Visual not initiated");
      return;
   }

   if (!fImage) {
      Warning("FillSpans", "no image");
      return;
   }

   if (!fImage->alt.argb32) {
      BeginPaint();
   }

   if (!fImage->alt.argb32) {
      Warning("FillSpans", "Failed to get pixel array");
      return;
   }

   if (!npt || !ppt || !widths || !tile) {
      Warning("FillSpans", "Invalid input data npt=%d ppt=%x widths=%x tile=%x",
              npt, ppt, widths, tile);
      return;
   }

   Int_t idx = 0;
   Int_t ii = 0;
   UInt_t x = 0;
   UInt_t *arr = tile->GetArgbArray();
   UInt_t xx = 0;
   UInt_t yy = 0;
   UInt_t yyy = 0;

   for (UInt_t i = 0; i < npt; i++) {
      yyy = ppt[i].fY*fImage->width;

      for (UInt_t j = 0; j < widths[i]; j++) {
         if ((ppt[i].fX >= (Int_t)fImage->width) || (ppt[i].fX < 0) ||
             (ppt[i].fY >= (Int_t)fImage->height) || (ppt[i].fY < 0)) continue;
         x = ppt[i].fX + j;
         idx = yyy + x;
         xx = x%tile->GetWidth();
         yy = ppt[i].fY%tile->GetHeight();
         ii = yy*tile->GetWidth() + xx;
         _alphaBlend(&fImage->alt.argb32[idx], &arr[ii]);
      }
      yyy += fImage->width;;
   }
}

//______________________________________________________________________________
void TASImage::CropSpans(UInt_t npt, TPoint *ppt, UInt_t *widths)
{
   //  crop spans

   if (!InitVisual()) {
      Warning("CropSpans", "Visual not initiated");
      return;
   }

   if (!fImage) {
      Warning("CropSpans", "no image");
      return;
   }

   if (!fImage->alt.argb32) {
      BeginPaint();
   }

   if (!fImage->alt.argb32) {
      Warning("CropSpans", "Failed to get pixel array");
      return;
   }

   if (!npt || !ppt || !widths) {
      Warning("CropSpans", "No points specified npt=%d ppt=%x widths=%x", npt, ppt, widths);
      return;
   }

   int y0 = ppt[0].fY;
   int y1 = ppt[npt-1].fY;
   UInt_t y = 0;
   UInt_t x = 0;
   UInt_t i = 0;
   UInt_t idx = 0;
   UInt_t sz = fImage->width*fImage->height;
   UInt_t yy = y*fImage->width;

   for (y = 0; (int)y < y0; y++) {
      for (x = 0; x < fImage->width; x++) {
         idx = yy + x;
         if (idx < sz) fImage->alt.argb32[idx] = 0;
      }
      yy += fImage->width;
   }

   for (i = 0; i < npt; i++) {
      for (x = 0; (int)x < ppt[i].fX; x++) {
         idx = ppt[i].fY*fImage->width + x;
         if (idx < sz) fImage->alt.argb32[idx] = 0;
      }
      for (x = ppt[i].fX + widths[i] + 1; x < fImage->width; x++) {
         idx = ppt[i].fY*fImage->width + x;
         if (idx < sz) fImage->alt.argb32[idx] = 0;
      }
   }

   yy = y1*fImage->width;
   for (y = y1; y < fImage->height; y++) {
      for (x = 0; x < fImage->width; x++) {
         idx = yy + x;
         if (idx < sz) fImage->alt.argb32[idx] = 0;
      }
      yy += fImage->width;
   }
}

//______________________________________________________________________________
void TASImage::CopyArea(TImage *dst, Int_t xsrc, Int_t ysrc, UInt_t w,  UInt_t h,
                        Int_t xdst, Int_t ydst, Int_t gfunc, EColorChan)
{
   // Copy source region to the destination image. Copy is done according
   // to specified function:
   //
   // enum EGraphicsFunction {
   //    kGXclear = 0,               // 0
   //    kGXand,                     // src AND dst
   //    kGXandReverse,              // src AND NOT dst
   //    kGXcopy,                    // src (default)
   //    kGXandInverted,             // NOT src AND dst
   //    kGXnoop,                    // dst
   //    kGXxor,                     // src XOR dst
   //    kGXor,                      // src OR dst
   //    kGXnor,                     // NOT src AND NOT dst
   //    kGXequiv,                   // NOT src XOR dst
   //    kGXinvert,                  // NOT dst
   //    kGXorReverse,               // src OR NOT dst
   //    kGXcopyInverted,            // NOT src
   //    kGXorInverted,              // NOT src OR dst
   //    kGXnand,                    // NOT src OR NOT dst
   //    kGXset                      // 1
   // };

   if (!InitVisual()) {
      Warning("CopyArea", "Visual not initiated");
      return;
   }

   if (!fImage) {
      Warning("CopyArea", "no image");
      return;
   }
   if (!dst) return;

   ASImage *out = ((TASImage*)dst)->GetImage();

   int x = 0;
   int y = 0;
   int idx = 0;
   int idx2 = 0;
   xsrc = xsrc < 0 ? 0 : xsrc;
   ysrc = ysrc < 0 ? 0 : ysrc;

   if ((xsrc >= (int)fImage->width) || (ysrc >= (int)fImage->height)) return;

   w = xsrc + w > fImage->width ? fImage->width - xsrc : w;
   h = ysrc + h > fImage->height ? fImage->height - ysrc : h;
   UInt_t yy = (ysrc + y)*fImage->width;

   if (fImage->alt.argb32 && out->alt.argb32) {
      for (y = 0; y < (int)h; y++) {
         for (x = 0; x < (int)w; x++) {
            idx = yy + x + xsrc;
            if ((x + xdst < 0) || (ydst + y < 0) ||
                (x + xdst >= (int)out->width) || (y + ydst >= (int)out->height) ) continue;

            idx2 = (ydst + y)*out->width + x + xdst;

            switch ((EGraphicsFunction)gfunc) {
               case kGXclear:
                  out->alt.argb32[idx2] = 0;
                  break;
               case kGXand:
                  out->alt.argb32[idx2] &= fImage->alt.argb32[idx];
                  break;
               case kGXandReverse:
                  out->alt.argb32[idx2] = fImage->alt.argb32[idx] & (~out->alt.argb32[idx2]);
                  break;
               case kGXandInverted:
                  out->alt.argb32[idx2] &= ~fImage->alt.argb32[idx];
                  break;
               case kGXnoop:
                  break;
               case kGXxor:
                  out->alt.argb32[idx2] ^= fImage->alt.argb32[idx];
                  break;
               case kGXor:
                  out->alt.argb32[idx2] |= fImage->alt.argb32[idx];
                  break;
               case kGXnor:
                  out->alt.argb32[idx2] = (~fImage->alt.argb32[idx]) & (~out->alt.argb32[idx2]);
                  break;
               case kGXequiv:
                  out->alt.argb32[idx2] ^= ~fImage->alt.argb32[idx];
                  break;
               case kGXinvert:
                  out->alt.argb32[idx2] = ~out->alt.argb32[idx2];
                  break;
               case kGXorReverse:
                  out->alt.argb32[idx2] = fImage->alt.argb32[idx] | (~out->alt.argb32[idx2]);
                  break;
               case kGXcopyInverted:
                  out->alt.argb32[idx2] = ~fImage->alt.argb32[idx];
                  break;
               case kGXorInverted:
                  out->alt.argb32[idx2] |= ~fImage->alt.argb32[idx];
                  break;
               case kGXnand:
                  out->alt.argb32[idx2] = (~fImage->alt.argb32[idx]) | (~out->alt.argb32[idx2]);
                  break;
               case kGXset:
                  out->alt.argb32[idx2] = 0xFFFFFFFF;
                  break;
               case kGXcopy:
               default:
                  out->alt.argb32[idx2] = fImage->alt.argb32[idx];
                  break;
            }
         }
         yy += fImage->width;
      }
   }
}

//______________________________________________________________________________
void TASImage::DrawCellArray(Int_t x1, Int_t y1, Int_t x2, Int_t y2, Int_t nx,
                             Int_t ny, UInt_t *ic)
{
   // Draw a cell array.
   // x1,y1        : left down corner
   // x2,y2        : right up corner
   // nx,ny        : array size
   // ic           : array of ARGB32 colors
   //
   // Draw a cell array. The drawing is done with the pixel presicion
   // if (X2-X1)/NX (or Y) is not a exact pixel number the position of
   // the top rigth corner may be wrong.

   int i, j, ix, iy, w, h;

   ARGB32 color = 0xFFFFFFFF;
   ARGB32 icol;

   w  = TMath::Max((x2-x1)/(nx),1);
   h  = TMath::Max((y1-y2)/(ny),1);
   ix = x1;

   for (i = 0; i < nx; i++) {
      iy = y1 - h;
      for (j = 0; j < ny; j++) {
         icol = (ARGB32)ic[i + (nx*j)];
         if (icol != color) {
            color = icol;
         }
         FillRectangleInternal((UInt_t)color, ix, iy, w, h);
         iy = iy - h;
      }
      ix = ix + w;
   }
}

//______________________________________________________________________________
UInt_t TASImage::AlphaBlend(UInt_t bot, UInt_t top)
{
   // returns alphablended value computed from bottom and top pixel values

   UInt_t ret = bot;

   _alphaBlend(&ret, &top);
   return ret;
}

//______________________________________________________________________________
const ASVisual *TASImage::GetVisual()
{
   // return visual

   return fgVisual;
}

//////////////////////// polygon filling //////////////////////////////
//______________________________________________________________________________
static int GetPolyYBounds(TPoint *pts, int n, int *by, int *ty)
{
   //

   register TPoint *ptMin;
   int ymin, ymax;
   TPoint *ptsStart = pts;

   ptMin = pts;
   ymin = ymax = (pts++)->fY;

   while (--n > 0) {
      if (pts->fY < ymin) {
         ptMin = pts;
         ymin = pts->fY;
      }
      if (pts->fY > ymax) {
         ymax = pts->fY;
      }
      pts++;
    }

    *by = ymin;
    *ty = ymax;
    return (ptMin - ptsStart);
}

//______________________________________________________________________________
Bool_t TASImage::GetPolygonSpans(UInt_t npt, TPoint *ppt, UInt_t *nspans,
                                 TPoint **outPoint, UInt_t **outWidth)
{
   // The code is taken on Xserver/mi/mipolycon.c
   //    "Copyright 1987, 1998  The Open Group"

   int xl = 0;                   // x vals of leftedges
   int xr = 0;                   // x vals of right edges
   int dl = 0;                   // decision variables
   int dr = 0;                   // decision variables
   int ml = 0;                   // left edge slope
   int m1l = 0;                  // left edge slope+1
   int mr = 0, m1r = 0;          // right edge slope and slope+1
   int incr1l = 0, incr2l = 0;   // left edge error increments
   int incr1r = 0, incr2r = 0;   // right edge error increments
   int dy;                       // delta y
   int y;                        // current scanline
   int left, right;              // indices to first endpoints
   int i;                        // loop counter
   int nextleft, nextright;      // indices to second endpoints
   TPoint *ptsOut;               // output buffer
   UInt_t *width;                // output buffer
   TPoint *firstPoint;
   UInt_t *firstWidth;
   int imin;                     // index of smallest vertex (in y)
   int ymin;                     // y-extents of polygon
   int ymax;
   Bool_t  ret = kTRUE;

   *nspans = 0;

   if (!InitVisual()) {
      Warning("GetPolygonSpans", "Visual not initiated");
      return kFALSE;
   }

   if (!fImage) {
      Warning("GetPolygonSpans", "no image");
      return kFALSE;
   }

   if (!fImage->alt.argb32) {
      BeginPaint();
   }

   if (!fImage->alt.argb32) {
      Warning("GetPolygonSpans", "Failed to get pixel array");
      return kFALSE;
   }

   if ((npt < 3) || !ppt) {
      Warning("GetPolygonSpans", "No points specified npt=%d ppt=%x", npt, ppt);
      return kFALSE;
   }

   //  find leftx, bottomy, rightx, topy, and the index
   //  of bottomy. Also translate the points.
   imin = GetPolyYBounds(ppt, npt, &ymin, &ymax);

   dy = ymax - ymin + 1;
   if ((npt < 3) || (dy < 0)) return kFALSE;

   static const Int_t gCachePwSize = 512;
   static TPoint gPointCache[gCachePwSize];
   static UInt_t gWidthCache[gCachePwSize];

   if (dy < gCachePwSize) {
      ptsOut = firstPoint = (TPoint*)&gPointCache;
      width = firstWidth = (UInt_t*)&gWidthCache;
      ret = kFALSE;
   } else {
      ptsOut = firstPoint = new TPoint[dy];
      width = firstWidth = new UInt_t[dy];
      ret = kTRUE;
   }

   nextleft = nextright = imin;
   y = ppt[nextleft].fY;

   //  loop through all edges of the polygon
   do {
      // add a left edge if we need to
      if (ppt[nextleft].fY == y) {
         left = nextleft;

         //  find the next edge, considering the end
         //  conditions of the array.
         nextleft++;
         if (nextleft >= (int)npt) {
            nextleft = 0;
         }

         // now compute all of the random information
         // needed to run the iterative algorithm.
         BRESINITPGON(ppt[nextleft].fY - ppt[left].fY,
                      ppt[left].fX, ppt[nextleft].fX,
                      xl, dl, ml, m1l, incr1l, incr2l);
      }

      // add a right edge if we need to
      if (ppt[nextright].fY == y) {
         right = nextright;

         // find the next edge, considering the end
         // conditions of the array.
         nextright--;
         if (nextright < 0) {
            nextright = npt-1;
         }

         //  now compute all of the random information
         //  needed to run the iterative algorithm.
         BRESINITPGON(ppt[nextright].fY - ppt[right].fY,
                      ppt[right].fX, ppt[nextright].fX,
                      xr, dr, mr, m1r, incr1r, incr2r);
      }

      // generate scans to fill while we still have
      //  a right edge as well as a left edge.
      i = min(ppt[nextleft].fY, ppt[nextright].fY) - y;

      // in case we're called with non-convex polygon
      if (i < 0) {
         delete [] firstWidth;
         delete [] firstPoint;
         firstPoint = 0;
         firstWidth = 0;
         return kFALSE;
      }

      while (i-- > 0)  {
         ptsOut->fY = y;

         // reverse the edges if necessary
         if (xl < xr) {
            *(width++) = xr - xl;
            (ptsOut++)->fX = xl;
         } else {
            *(width++) = xl - xr;
            (ptsOut++)->fX = xr;
         }
         y++;

         // increment down the edges
         BRESINCRPGON(dl, xl, ml, m1l, incr1l, incr2l);
         BRESINCRPGON(dr, xr, mr, m1r, incr1r, incr2r);
      }
   }  while (y != ymax);

   *nspans = UInt_t(ptsOut - firstPoint);
   *outPoint = firstPoint;
   *outWidth = firstWidth;

   return ret;
}

//______________________________________________________________________________
void TASImage::FillPolygon(UInt_t npt, TPoint *ppt, const char *col,
                           const char *stipple, UInt_t w, UInt_t h)
{
   // Fill a convex polygon with background color or bitmap
   // For non convex polygon one must use DrawFillArea method

   UInt_t  nspans = 0;
   TPoint *firstPoint = 0;   // output buffer
   UInt_t *firstWidth = 0;   // output buffer

   Bool_t del = GetPolygonSpans(npt, ppt, &nspans, &firstPoint, &firstWidth);
   ARGB32 color;
   parse_argb_color(col, &color);

   if (nspans) {
      if (!stipple && ((color & 0xff000000)==0xff000000)) { //no stipple no alpha
         FillSpansInternal(nspans, firstPoint, firstWidth, color);
      } else {
         FillSpans(nspans, firstPoint, firstWidth, col, stipple, w, h);
      }

      if (del) {
         delete [] firstWidth;
         delete [] firstPoint;
      }
   }
}

//______________________________________________________________________________
void TASImage::FillPolygon(UInt_t npt, TPoint *ppt, TImage *tile)
{
   // Fill a convex polygon with background image.
   // For non convex polygon one must use DrawFillArea method

   UInt_t  nspans = 0;
   TPoint *firstPoint = 0;   // output buffer
   UInt_t *firstWidth = 0;   // output buffer

   Bool_t del = GetPolygonSpans(npt, ppt, &nspans, &firstPoint, &firstWidth);

   if (nspans) {
      FillSpans(nspans, firstPoint, firstWidth, tile);

      if (del) {
         delete [] firstWidth;
         delete [] firstPoint;
      }
   }
}

//______________________________________________________________________________
void TASImage::CropPolygon(UInt_t npt, TPoint *ppt)
{
   // Crop a convex polygon.

   UInt_t  nspans = 0;
   TPoint *firstPoint = 0;
   UInt_t *firstWidth = 0;

   Bool_t del = GetPolygonSpans(npt, ppt, &nspans, &firstPoint, &firstWidth);

   if (nspans) {
      CropSpans(nspans, firstPoint, firstWidth);

      if (del) {
         delete [] firstWidth;
         delete [] firstPoint;
      }
   }
}

static const UInt_t NUMPTSTOBUFFER = 512;

//______________________________________________________________________________
void TASImage::DrawFillArea(UInt_t count, TPoint *ptsIn, const char *col,
                           const char *stipple, UInt_t w, UInt_t h)
{
   // fill a polygon (any type convex, non-convex)

   if (!InitVisual()) {
      Warning("DrawFillArea", "Visual not initiated");
      return;
   }

   if (!fImage) {
      Warning("DrawFillArea", "no image");
      return;
   }

   if (!fImage->alt.argb32) {
      BeginPaint();
   }

   if (!fImage->alt.argb32) {
      Warning("DrawFillArea", "Failed to get pixel array");
      return;
   }

   if ((count < 3) || !ptsIn) {
      Warning("DrawFillArea", "No points specified npt=%d ppt=%x", count, ptsIn);
      return;
   }

   if (count < 5) {
      FillPolygon(count, ptsIn, col, stipple, w, h);
      return;
   }

   ARGB32 color;
   parse_argb_color(col, &color);

   EdgeTableEntry *pAET;  // the Active Edge Table
   int y;                 // the current scanline
   UInt_t nPts = 0;          // number of pts in buffer

   ScanLineList *pSLL;   // Current ScanLineList
   TPoint *ptsOut;       // ptr to output buffers
   UInt_t *width;
   TPoint firstPoint[NUMPTSTOBUFFER];  // the output buffers
   UInt_t firstWidth[NUMPTSTOBUFFER];
   EdgeTableEntry *pPrevAET;       // previous AET entry
   EdgeTable ET;                   // Edge Table header node
   EdgeTableEntry AET;             // Active ET header node
   EdgeTableEntry *pETEs;          // Edge Table Entries buff
   ScanLineListBlock SLLBlock;     // header for ScanLineList
   Bool_t del = kTRUE;

   static const UInt_t gEdgeTableEntryCacheSize = 200;
   static EdgeTableEntry gEdgeTableEntryCache[gEdgeTableEntryCacheSize];

   if (count < gEdgeTableEntryCacheSize) {
      pETEs = (EdgeTableEntry*)&gEdgeTableEntryCache;
      del = kFALSE;
   } else {
      pETEs = new EdgeTableEntry[count];
      del = kTRUE;
   }

   ptsOut = firstPoint;
   width = firstWidth;
   CreateETandAET(count, ptsIn, &ET, &AET, pETEs, &SLLBlock);
   pSLL = ET.scanlines.next;

   for (y = ET.ymin; y < ET.ymax; y++) {
      if (pSLL && y == pSLL->scanline) {
         loadAET(&AET, pSLL->edgelist);
         pSLL = pSLL->next;
      }
      pPrevAET = &AET;
      pAET = AET.next;

      while (pAET) {
         ptsOut->fX = pAET->bres.minor_axis;
         ptsOut->fY = y;
		   ptsOut++;
         nPts++;

         *width++ = pAET->next->bres.minor_axis - pAET->bres.minor_axis;

         if (nPts == NUMPTSTOBUFFER) {
            if (!stipple && ((color & 0xff000000)==0xff000000)) { //no stipple, no alpha
               FillSpansInternal(nPts, firstPoint, firstWidth, color);
            } else {
               FillSpans(nPts, firstPoint, firstWidth, col, stipple, w, h);
            }
            ptsOut = firstPoint;
            width = firstWidth;
            nPts = 0;
         }
         EVALUATEEDGEEVENODD(pAET, pPrevAET, y)
         EVALUATEEDGEEVENODD(pAET, pPrevAET, y)
      }
      InsertionSort(&AET);
   }

   if (nPts) {
      if (!stipple && ((color & 0xff000000)==0xff000000)) {  //no stipple, no alpha
         FillSpansInternal(nPts, firstPoint, firstWidth, color);
      } else {
         FillSpans(nPts, firstPoint, firstWidth, col, stipple, w, h);
      }
   }

   if (del) delete [] pETEs;
   FreeStorage(SLLBlock.next);
}

//______________________________________________________________________________
void TASImage::DrawFillArea(UInt_t count, TPoint *ptsIn, TImage *tile)
{
   // fill a polygon (any type convex, non-convex)

   if (!InitVisual()) {
      Warning("DrawFillArea", "Visual not initiated");
      return;
   }

   if (!fImage) {
      Warning("DrawFillArea", "no image");
      return;
   }

   if (!fImage->alt.argb32) {
      BeginPaint();
   }

   if (!fImage->alt.argb32) {
      Warning("DrawFillArea", "Failed to get pixel array");
      return;
   }

   if ((count < 3) || !ptsIn) {
      Warning("DrawFillArea", "No points specified npt=%d ppt=%x", count, ptsIn);
      return;
   }

   if (count < 5) {
      FillPolygon(count, ptsIn, tile);
      return;
   }

   EdgeTableEntry *pAET;   // the Active Edge Table
   int y;                  // the current scanline
   UInt_t nPts = 0;       // number of pts in buffer

   ScanLineList *pSLL;    // Current ScanLineList
   TPoint *ptsOut;        // ptr to output buffers
   UInt_t *width;
   TPoint firstPoint[NUMPTSTOBUFFER]; // the output buffers
   UInt_t firstWidth[NUMPTSTOBUFFER];
   EdgeTableEntry *pPrevAET;       // previous AET entry
   EdgeTable ET;                   // Edge Table header node
   EdgeTableEntry AET;             // Active ET header node
   EdgeTableEntry *pETEs;          // Edge Table Entries buff
   ScanLineListBlock SLLBlock;     // header for ScanLineList

   pETEs = new EdgeTableEntry[count];

   ptsOut = firstPoint;
   width = firstWidth;
   CreateETandAET(count, ptsIn, &ET, &AET, pETEs, &SLLBlock);
   pSLL = ET.scanlines.next;

   for (y = ET.ymin; y < ET.ymax; y++) {
      if (pSLL && y == pSLL->scanline) {
         loadAET(&AET, pSLL->edgelist);
         pSLL = pSLL->next;
      }
      pPrevAET = &AET;
      pAET = AET.next;

      while (pAET) {
         ptsOut->fX = pAET->bres.minor_axis;
         ptsOut->fY = y;
		   ptsOut++;
         nPts++;

         *width++ = pAET->next->bres.minor_axis - pAET->bres.minor_axis;

         if (nPts == NUMPTSTOBUFFER) {
            FillSpans(nPts, firstPoint, firstWidth, tile);
            ptsOut = firstPoint;
            width = firstWidth;
            nPts = 0;
         }
         EVALUATEEDGEEVENODD(pAET, pPrevAET, y)
         EVALUATEEDGEEVENODD(pAET, pPrevAET, y)
      }
      InsertionSort(&AET);
   }
   FillSpans(nPts, firstPoint, firstWidth, tile);

   delete [] pETEs;
   FreeStorage(SLLBlock.next);
}

//_____________________________________________________________________________
static void fill_hline_notile_argb32(ASDrawContext *ctx, int x_from, int y,
                                     int x_to, CARD32)
{
   //

   int cw = ctx->canvas_width;

   if (x_to >= 0 && x_from < cw && y >= 0 && y < ctx->canvas_height) {
      CARD32 value = ctx->tool->matrix[0]; //color
      CARD32 *dst = (CARD32 *)(ctx->canvas + y*cw);
      int x1 = x_from;
      int x2 = x_to;

      if (x1 < 0) {
         x1 = 0;
      }
      if (x2 >= cw) {
         x2 = cw - 1;
      }
      while (x1 <= x2) {
         _alphaBlend(&dst[x1], &value);
         ++x1;
      }
   }
}

//_____________________________________________________________________________
static void apply_tool_2D_argb32(ASDrawContext *ctx, int curr_x, int curr_y, CARD32)
{
   //

   CARD32 *src = (CARD32 *)ctx->tool->matrix;
   int corner_x = curr_x - ctx->tool->center_x;
   int corner_y = curr_y - ctx->tool->center_y;
   int tw = ctx->tool->width;
   int th = ctx->tool->height;
   int cw = ctx->canvas_width;
   int ch = ctx->canvas_height;
   int aw = tw;
   int ah = th;
   CARD32 *dst = (CARD32 *)ctx->canvas;
   int x, y;

   if (corner_x+tw <= 0 || corner_x >= cw || corner_y+th <= 0 || corner_y >= ch) {
      return;
	}

   if (corner_y > 0) {
      dst += corner_y*cw;
   } else if (corner_y < 0) {
      ah -= -corner_y;
      src += -corner_y*tw;
   }

   if (corner_x  > 0) {
      dst += corner_x;
   } else if (corner_x < 0) {
      src += -corner_x;
      aw -= -corner_x;
   }

   if (corner_x + tw > cw) {
      aw = cw - corner_x;
   }

   if (corner_y + th > ch) {
      ah = ch - corner_y;
   }

   for (y = 0; y < ah; ++y) {
      for (x = 0; x < aw; ++x) {
         _alphaBlend(&dst[x], &src[x]);
      }
      src += tw;
      dst += cw;
   }
}

//_____________________________________________________________________________
static ASDrawContext *create_draw_context_argb32(ASImage *im, ASDrawTool *brush)
{
   //

   static ASDrawContext *ctx = new ASDrawContext;

	ctx->canvas_width = im->width;
	ctx->canvas_height = im->height;
	ctx->canvas = im->alt.argb32;
   ctx->scratch_canvas = 0;

	ctx->tool = brush;
	ctx->fill_hline_func = fill_hline_notile_argb32;
   ctx->apply_tool_func = apply_tool_2D_argb32;

	return ctx;
}

static const UInt_t kBrushCacheSize = 20;
static CARD32 gBrushCache[kBrushCacheSize*kBrushCacheSize];

//______________________________________________________________________________
void TASImage::DrawWideLine(UInt_t x1, UInt_t y1, UInt_t x2, UInt_t y2,
                            UInt_t color, UInt_t thick)
{
   // draw wide line

   Int_t sz = thick*thick;
   CARD32 *matrix;
   Bool_t use_cache = thick < kBrushCacheSize;

   if (use_cache) {
      matrix = gBrushCache;
   } else {
      matrix = new CARD32[sz];
   }

   for (int i = 0; i < sz; i++) {
      matrix[i] = (CARD32)color;
   };

   static ASDrawTool *brush = new ASDrawTool;
   brush->matrix = matrix;
   brush->width = thick;
   brush->height = thick;
   brush->center_y = brush->center_x = thick/2;

   ASDrawContext *ctx = create_draw_context_argb32(fImage, brush);
   asim_move_to(ctx, x1, y1);
   asim_line_to(ctx, x2, y2);

   if (!use_cache) {
      delete [] matrix;
   }
}

//______________________________________________________________________________
void TASImage::DrawGlyph(void *bitmap, UInt_t color, Int_t bx, Int_t by)
{
   // Draw FT_Bitmap bitmap

   static UInt_t col[5];
   Int_t x, y, yy, y0, xx;

   ULong_t r, g, b;
   int idx = 0;
   FT_Bitmap *source = (FT_Bitmap*)bitmap;
   UChar_t d = 0, *s = source->buffer;

   Int_t dots = Int_t(source->width * source->rows);
   r = g = b = 0;

   yy = y0 = by * fImage->width;
   for (y = 0; y < (int) source->rows; y++) {
      for (x = 0; x < (int) source->width; x++) {
         idx = bx + x + yy;
         r += ((fImage->alt.argb32[idx] & 0xff0000) >> 16);
         g += ((fImage->alt.argb32[idx] & 0x00ff00) >> 8);
         b += (fImage->alt.argb32[idx] & 0x0000ff);
      }
      yy += fImage->width;
   }
   if (dots != 0) {
      r /= dots;
      g /= dots;
      b /= dots;
   }

   col[0] = (r << 16) + (g << 8) + b;
   col[4] = color;
   Int_t col4r = (col[4] & 0xff0000) >> 16;
   Int_t col4g = (col[4] & 0x00ff00) >> 8;
   Int_t col4b = (col[4] & 0x0000ff);

   // interpolate between fore and backgound colors
   for (x = 3; x > 0; x--) {
      xx = 4-x;
      Int_t colxr = (col4r*x + r*xx) >> 2;
      Int_t colxg = (col4g*x + g*xx) >> 2;
      Int_t colxb = (col4b*x + b*xx) >> 2;
      col[x] = (colxr << 16) + (colxg << 8) + colxb;
   }

   yy = y0;
   for (y = 0; y < (int) source->rows; y++) {
      for (x = 0; x < (int) source->width; x++) {
         d = *s++ & 0xff;
         d = ((d + 10) * 5) >> 8;
         if (d > 4) d = 4;

         if (d && x < (int) source->width) {
            idx = (bx + x) + yy;
            fImage->alt.argb32[idx] = (ARGB32)col[d];
         }
      }
      yy += fImage->width;
   }
}

//______________________________________________________________________________
void TASImage::DrawTextTTF(Int_t x, Int_t y, const char *text, Int_t size,
                           UInt_t color, const char *font_name, Float_t angle)
{
   // Draw text using TrueType fonts.

   if (!TTF::IsInitialized()) TTF::Init();

   TTF::SetTextFont(font_name);
   TTF::SetTextSize(size);
   TTF::SetRotationMatrix(angle);
   TTF::PrepareString(text);
   TTF::LayoutGlyphs();

   TTGlyph *glyph = TTF::GetGlyphs();

   // compute the size and position  that will contain the text
   Int_t Xoff = 0; if (TTF::GetBox().xMin < 0) Xoff = -TTF::GetBox().xMin;
   Int_t Yoff = 0; if (TTF::GetBox().yMin < 0) Yoff = -TTF::GetBox().yMin;
   Int_t h    = TTF::GetBox().yMax + Yoff;

   for (int n = 0; n < TTF::GetNumGlyphs(); n++, glyph++) {
      if (FT_Glyph_To_Bitmap(&glyph->fImage, ft_render_mode_normal, 0, 1 )) continue;

      FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyph->fImage;
      FT_Bitmap *source = &bitmap->bitmap;

      Int_t bx = x + bitmap->left;
      Int_t by = y + h - bitmap->top;
      DrawGlyph(source, color, bx, by);
   }
}

/////////////////////////////////////////////////////////////////////////
//_______________________________________________________________________
void TASImage::GetImageBuffer(char **buffer, int *size, EImageFileTypes type)
{
   // Returns in-memory buffer compressed according image type
   // Buffer must be deallocated after usage.
   // This method can be used for sending images over network.

   if (!fImage) return;

   ASImageExportParams params;
   Bool_t ret = kFALSE;
   int   isize = 0;
   char *ibuff = 0;

   switch (type) {
      case TImage::kXpm:
         ret = ASImage2xpmRawBuff(fImage, (CARD8 **)buffer, size, 0);
         break;
      default:
         ret = ASImage2PNGBuff(fImage, (CARD8 **)buffer, size, &params);
   }

   if (!ret) {
      *size = isize;
      *buffer = ibuff;
   }
}

//_______________________________________________________________________
Bool_t TASImage::SetImageBuffer(char **buffer, EImageFileTypes type)
{
   // create image from  compressed buffer
   // Supported formats:
   //
   //    PNG - by default
   //    XPM - two options exist:
   //      1.  xpm as a single string (raw buffer). Such string
   //          is returned by GetImageBuffer method.
   //
   //    For example:
   //       char *buf;
   //       int sz;
   //       im1->GetImageBuffer(&buf, &int, TImage::kXpm); /*raw buffer*/
   //       TImage *im2 = TImage::Create();
   //       im2->SetImageBuffer(&buf, TImage::kXpm);
   //
   //      2.  xpm as an array of strigs (preparsed)
   //
   //    For example:
   //       char *xpm[] = {
   //          "64 28 58 1",
   //          "  c #0A030C",
   //          ". c #1C171B"
   //             ...
   //    TImage *im = TImage::Create();
   //    im->SetImageBuffer(xpm, TImage::kXpm);

   DestroyImage();

   ASImageImportParams params;
   params.flags = 0;
   params.width = 0;
   params.height = 0 ;
   params.filter = SCL_DO_ALL;
   params.gamma = 0;
   params.gamma_table = 0;
   params.compression = 0;
   params.format = ASA_ASImage;
   params.search_path = 0;
   params.subimage = 0;

   switch (type) {
      case TImage::kXpm:
      {
         char *ptr = buffer[0];
         while (isspace((int)*ptr)) ++ptr;
         if (atoi(ptr)) {  // preparsed and preloaded data
            fImage = xpm_data2ASImage((const char**)buffer, &params);
         } else {
            fImage = xpmRawBuff2ASImage((const char*)*buffer, &params);
         }
         break;
      }
      default:
         fImage = PNGBuff2ASimage((CARD8 *)*buffer, &params);
         break;
   }

   if (!fImage) {
      return kFALSE;
   }

   if (fName.IsNull()) {
      fName.Form("img_%dx%d.%d", fImage->width, fImage->height, gRandom->Integer(1000));
   }
   UnZoom();
   return kTRUE;
}

//_______________________________________________________________________
void TASImage::CreateThumbnail()
{
   // creates image thumbnail

   int size;
   const int sz = 64;

   if (!fImage) {
      return;
   }

   if (!InitVisual()) {
      return;
   }

   static char *buf = 0;
   int w, h;
   ASImage *img = 0;

   if (fImage->width > fImage->height) {
      w = sz;
      h = (fImage->height*sz)/fImage->width;
   } else {
      h = sz;
      w = (fImage->width*sz)/fImage->height;
   }

   w = w < 8 ? 8 : w;
   h = h < 8 ? 8 : h;

   img = scale_asimage(fgVisual, fImage, w, h, ASA_ASImage,
                       GetImageCompression(), GetImageQuality());
   if (!img) {
      return;
   }

   // contrasing
   ASImage *rendered_im;
   ASImageLayer layers[2];
   init_image_layers(&(layers[0]), 2);
   layers[0].im = img;
   layers[0].dst_x = 0;
   layers[0].dst_y = 0;
   layers[0].clip_width = img->width;
   layers[0].clip_height = img->height;
   layers[0].bevel = 0;
   layers[1].im = img;
   layers[1].dst_x = 0;
   layers[1].dst_y = 0;
   layers[1].clip_width = img->width;
   layers[1].clip_height = img->height;
   layers[1].merge_scanlines = blend_scanlines_name2func("tint");
   rendered_im = merge_layers(fgVisual, &(layers[0]), 2, img->width, img->height,
                              ASA_ASImage, GetImageCompression(), GetImageQuality());
   destroy_asimage(&img);
   img = rendered_im;

   // pad image
   ASImage *padimg = 0;
   int d = 0;

   if (w == sz) {
      d = (sz - h) >> 1;
      padimg = pad_asimage(fgVisual, img, 0, d, sz, sz, 0x00ffffff,
                           ASA_ASImage, GetImageCompression(), GetImageQuality());
   } else {
      d = (sz - w) >> 1;
      padimg = pad_asimage(fgVisual, img, d, 0, sz, sz, 0x00ffffff,
                           ASA_ASImage, GetImageCompression(), GetImageQuality());
   }

   if (!padimg) {
      destroy_asimage(&img);
      return;
   }

   ASImage2xpmRawBuff(padimg, (CARD8 **)&buf, &size, 0);
   fTitle = buf;

   destroy_asimage(&padimg);
}

//_______________________________________________________________________
void TASImage::Streamer(TBuffer &b)
{
   // streamer for ROOT I/O

   Bool_t image_type = 0;
   char *buffer = 0;
   int size = 0;
   int w, h;
   UInt_t R__s, R__c;

   if (b.IsReading()) {
      Version_t version = b.ReadVersion(&R__s, &R__c);
      if (version == 0) { //dumb prototype for scheema evolution
         return;
      }
      
      if ( version == 1 ) {
         TObject * parent = b.GetParent();
         if ( parent->IsA() == TFile::Class() ) {
            Int_t file_version = ((TFile*)parent)->GetVersion();
            if ( file_version < 50000 ) {
               TImage::Streamer(b);
               b >> fMaxValue;
               b >> fMinValue;
               b >> fZoomOffX;
               b >> fZoomOffY;
               b >> fZoomWidth;
               b >> fZoomHeight;
               if ( file_version < 40200 ) {
                  Bool_t zoomUpdate;
                  b >> zoomUpdate;
                  fZoomUpdate = zoomUpdate;
               } else {
                  b >> fZoomUpdate;
                  b >> fEditable;
                  Bool_t paintMode;
                  b >> paintMode;
                  fPaintMode = paintMode;
               }
               b.CheckByteCount(R__s, R__c, TASImage::IsA());
               return;
            }
         }
      }
      
      TNamed::Streamer(b);
      b >> image_type;

      if (image_type != 0) {     // read PNG compressed image
         b >> size;
         buffer = new char[size];
         b.ReadFastArray(buffer, size);
         SetImageBuffer(&buffer, TImage::kPng);
         delete buffer;
      } else {                   // read vector with palette
         TAttImage::Streamer(b);
         b >> w;
         b >> h;
         size = w*h;
         Double_t *vec = new Double_t[size];
         b.ReadFastArray(vec, size);
         SetImage(vec, w, h, &fPalette);
      }
      b.CheckByteCount(R__s, R__c, TASImage::IsA());
   } else {
      if (!fImage) {
         return;
      }
      R__c = b.WriteVersion(TASImage::IsA(), kTRUE);

      if (fName.IsNull()) {
         fName.Form("img_%dx%d.%d", fImage->width, fImage->height, gRandom->Integer(1000));
      }
      TNamed::Streamer(b);

      image_type = fImage->alt.vector ? 0 : 1;
      b << image_type;

      if (image_type != 0) {     // write PNG compressed image
         GetImageBuffer(&buffer, &size, TImage::kPng);
         b << size;
         b.WriteFastArray(buffer, size);
         delete buffer;
      } else {                   // write vector  with palette
         TAttImage::Streamer(b);
         b << fImage->width;
         b << fImage->height;
         b.WriteFastArray(fImage->alt.vector, fImage->width*fImage->height);
      }
      b.SetByteCount(R__c, kTRUE);
   }
}

//_______________________________________________________________________
void TASImage::Browse(TBrowser *)
{
   // browse image

   if (fImage->alt.vector) {
      Draw("n");
   } else {
      Draw("nxxx");
   }
   CreateThumbnail();
}

//_______________________________________________________________________
const char *TASImage::GetTitle() const
{
   // title is used to keep 32x32 xpm image's thumbnail

   TASImage *mutble = (TASImage *)this;

   if (fTitle.IsNull()) {
      mutble->SetTitle(fName.Data());
   }

   return fTitle.Data();
}

//_______________________________________________________________________
void TASImage::SetTitle(const char *title)
{
   // set a title for an image

   if (fTitle.IsNull()) {
      CreateThumbnail();
   }

   if (fTitle.IsNull()) {
      return;
   }

   int start = fTitle.Index("/*") + 3;
   int stop = fTitle.Index("*/") - 1;

   if ((start > 0) && (stop - start > 0)) {
      fTitle.Replace(start, stop - start, title);
   }
}

//_______________________________________________________________________
void TASImage::DrawCubeBezier(Int_t x1, Int_t y1, Int_t x2, Int_t y2,
                             Int_t x3, Int_t y3, const char *col, UInt_t thick)
{
   // draws cubic bezier line

   Int_t sz = thick*thick;
   CARD32 *matrix;
   Bool_t use_cache = thick < kBrushCacheSize;

   ARGB32 color;
   parse_argb_color(col, &color);

   if (use_cache) {
      matrix = gBrushCache;
   } else {
      matrix = new CARD32[sz];
   }

   for (int i = 0; i < sz; i++) {
      matrix[i] = (CARD32)color;
   };

   static ASDrawTool *brush = new ASDrawTool;
   brush->matrix = matrix;
   brush->width = thick;
   brush->height = thick;
   brush->center_y = brush->center_x = thick/2;

   ASDrawContext *ctx = 0;

   ctx = create_draw_context_argb32(fImage, brush);
   asim_cube_bezier(ctx, x1, y1, x2, y2, x3, y3);

   if (!use_cache) {
      delete [] matrix;
   }
}

//_______________________________________________________________________
void TASImage::DrawStraightEllips(Int_t x, Int_t y, Int_t rx, Int_t ry,
                                  const char *col, UInt_t thick)
{
   // draws straight ellips

   Int_t sz = thick*thick;
   CARD32 *matrix;
   Bool_t use_cache = thick < kBrushCacheSize;

   ARGB32 color;
   parse_argb_color(col, &color);

   if (use_cache) {
      matrix = gBrushCache;
   } else {
      matrix = new CARD32[sz];
   }

   for (int i = 0; i < sz; i++) {
      matrix[i] = (CARD32)color;
   };

   static ASDrawTool *brush = new ASDrawTool;
   brush->matrix = matrix;
   brush->width = thick;
   brush->height = thick;
   brush->center_y = brush->center_x = thick/2;

   ASDrawContext *ctx = create_draw_context_argb32(fImage, brush);
   asim_straight_ellips(ctx, x, y, rx, ry, 0);

   if (!use_cache) {
      delete [] matrix;
   }
}

//_______________________________________________________________________
void TASImage::DrawCircle(Int_t x, Int_t y, Int_t r, const char *col, UInt_t thick)
{
   // draws circle

   thick = !thick ? 1 : thick;
   Int_t sz = thick*thick;
   CARD32 *matrix;
   Bool_t use_cache = thick < kBrushCacheSize;

   ARGB32 color;
   parse_argb_color(col, &color);

   if (use_cache) {
      matrix = gBrushCache;
   } else {
      matrix = new CARD32[sz];
   }

   for (int i = 0; i < sz; i++) {
      matrix[i] = (CARD32)color;
   };

   static ASDrawTool *brush = new ASDrawTool;
   brush->matrix = matrix;
   brush->width = thick;
   brush->height = thick;
   brush->center_y = brush->center_x = thick/2;

   ASDrawContext *ctx = create_draw_context_argb32(fImage, brush);
   asim_circle(ctx, x,  y, r, 0);

   if (!use_cache) {
      delete [] matrix;
   }
}

//_______________________________________________________________________
void TASImage::DrawEllips(Int_t x, Int_t y, Int_t rx, Int_t ry, Int_t angle,
                           const char *col, UInt_t thick)
{
   // draws ellips

   thick = !thick ? 1 : thick;
   Int_t sz = thick*thick;
   CARD32 *matrix;
   Bool_t use_cache = thick < kBrushCacheSize;

   ARGB32 color;
   parse_argb_color(col, &color);

   if (use_cache) {
      matrix = gBrushCache;
   } else {
      matrix = new CARD32[sz];
   }

   for (int i = 0; i < sz; i++) {
      matrix[i] = (CARD32)color;
   };

   static ASDrawTool *brush = new ASDrawTool;
   brush->matrix = matrix;
   brush->width = thick;
   brush->height = thick;
   brush->center_y = brush->center_x = thick/2;

   ASDrawContext *ctx = create_draw_context_argb32(fImage, brush);
   asim_ellips(ctx, x, y, rx, ry, angle, 0);

   if (!use_cache) {
      delete [] matrix;
   }
}

//_______________________________________________________________________
void TASImage::DrawEllips2(Int_t x, Int_t y, Int_t rx, Int_t ry, Int_t angle,
                           const char *col, UInt_t thick)
{
   // draws allips

   thick = !thick ? 1 : thick;
   Int_t sz = thick*thick;
   CARD32 *matrix;
   Bool_t use_cache = thick < kBrushCacheSize;

   ARGB32 color;
   parse_argb_color(col, &color);

   if (use_cache) {
      matrix = gBrushCache;
   } else {
      matrix = new CARD32[sz];
   }

   for (int i = 0; i < sz; i++) {
      matrix[i] = (CARD32)color;
   };

   static ASDrawTool *brush = new ASDrawTool;
   brush->matrix = matrix;
   brush->width = thick;
   brush->height = thick;
   brush->center_y = brush->center_x = thick/2;

   ASDrawContext *ctx = create_draw_context_argb32(fImage, brush);
   asim_ellips2(ctx, x, y, rx, ry, angle, 0);

   if (!use_cache) {
      delete [] matrix;
   }
}

//_______________________________________________________________________
void TASImage::FloodFill(Int_t /*x*/, Int_t /*y*/, const char * /*col*/,
                         const char * /*minc*/, const char * /*maxc*/)
{
   // flood fill


}

//_______________________________________________________________________
void TASImage::Gray(Bool_t on)
{
   // Converts RGB image to Gray image and vice versa.

   if (fIsGray == on) {
      return;
   }

   if (!IsValid()) {
      Warning("Gray", "Image not initiated");
      return;
   }

   if (!InitVisual()) {
      Warning("Gray", "Visual not initiated");
      return;
   }

   if (!fGrayImage && !on) {
      return;
   }
   ASImage *sav = 0;
   delete fScaledImage;
   fScaledImage = 0;

   if (fGrayImage)  {
      sav = fImage;
      fImage = fGrayImage;
      fGrayImage = sav;
      fIsGray = on;
      return;
   }

   if (!on) return;

   UInt_t l, r, g, b, idx;
   int y = 0;
   UInt_t i, j;

   if (fImage->alt.argb32) {
      fGrayImage = tile_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height,
                                0, ASA_ARGB32, 0, ASIMAGE_QUALITY_DEFAULT);

      for (i = 0; i < fImage->height; i++) {
         for (j = 0; j < fImage->width; j++) {
            idx = y + j;

            r = ((fImage->alt.argb32[idx] & 0xff0000) >> 16);
            g = ((fImage->alt.argb32[idx] & 0x00ff00) >> 8);
            b = (fImage->alt.argb32[idx] & 0x0000ff);
            l = (57*r + 181*g + 18*b)/256;
            fGrayImage->alt.argb32[idx] = (l << 16) + (l << 8) + l;
         }
         y += fImage->width;
      }
   } else {
      fGrayImage = create_asimage(fImage->width, fImage->height, 0);

      ASImageDecoder *imdec = start_image_decoding(fgVisual, fImage, SCL_DO_ALL,
                                                   0, 0, fImage->width, fImage->height, 0);

      if (!imdec) {
         return;
      }
#ifdef HAVE_MMX
   mmx_init();
#endif
      ASImageOutput *imout = start_image_output(fgVisual, fGrayImage, ASA_ASImage,
                                                GetImageCompression(), GetImageQuality());
      if (!imout) {
         Warning("ToGray", "Failed to start image output");
         delete fScaledImage;
         fScaledImage = 0;
         return;
      }

      CARD32 *aa = imdec->buffer.alpha;
      CARD32 *rr = imdec->buffer.red;
      CARD32 *gg = imdec->buffer.green;
      CARD32 *bb = imdec->buffer.blue;

      ASScanline result;
      prepare_scanline(fImage->width, 0, &result, fgVisual->BGR_mode);

      for (i = 0; i < fImage->height; i++) {
         imdec->decode_image_scanline(imdec);
         result.flags = imdec->buffer.flags;
         result.back_color = imdec->buffer.back_color;

         for (j = 0; j < fImage->width; j++) {
            l = (57*rr[j] + 181*gg[j]+ 18*bb[j])/256;
            result.alpha[j] = aa[j];
            result.red[j] = l;
            result.green[j] = l;
            result.blue[j] = l;
         }
         imout->output_image_scanline(imout, &result, 1);
      }

      stop_image_decoding(&imdec);
      stop_image_output(&imout);
#ifdef HAVE_MMX
   mmx_off();
#endif
   }

   sav = fImage;
   fImage = fGrayImage;
   fGrayImage = sav;
   fIsGray = kTRUE;
}

//_______________________________________________________________________
void TASImage::FromWindow(Drawable_t wid, Int_t x, Int_t y, UInt_t w, UInt_t h)
{
   // creates an image(screenshot) from  specified window

   Int_t xy;

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

   if (!w || !h) {
      gVirtualX->GetWindowSize(wid, xy, xy, w, h);
   }

   if ((x >= (Int_t)w) || (y >= (Int_t)h)) {
      return;
   }

   if (!InitVisual()) {
      Warning("FromWindow", "Visual not initiated");
      return;
   }

   DestroyImage();
   delete fScaledImage;
   fScaledImage = 0;

   unsigned char *bits = gVirtualX->GetColorBits(wid, x, y, w, h);
   if (!bits) { // error
      return;
   }
   fImage = bitmap2asimage(bits, w, h, 0, 0);
   delete [] bits;
}

//_______________________________________________________________________
void TASImage::SetPaletteEnabled(Bool_t on)
{
   // switch on/off image palette. That also invokes calling vectorizasion of image

   if (!fImage) {
      return;
   }

   if (!fImage->alt.vector && on) {
      Vectorize();
   }
   fPaletteEnabled = on;

   if (on) {
      Double_t left = gPad->GetLeftMargin();
      Double_t right = gPad->GetRightMargin();
      Double_t top = gPad->GetTopMargin();
      Double_t bottom = gPad->GetBottomMargin();

      gPad->Range(-left / (1.0 - left - right),
                  -bottom / (1.0 - top - bottom),
                  1 + right / (1.0 - left - right),
                  1 + top / ( 1.0 - top - bottom));
      gPad->RangeAxis(0, 0, 1, 1);
   }

}

//_______________________________________________________________________
void TASImage::SavePrimitive(ofstream &out, Option_t *)
{
    // Save a primitive as a C++ statement(s) on output stream "out"

   char *buf;
   int sz;
   GetImageBuffer(&buf, &sz, TImage::kXpm);

   TString name = GetName();
   name.ReplaceAll(".", "_");
   TString str = buf;

   str.ReplaceAll("static", "");
   TString xpm = "xpm_";
   xpm += name;
   str.ReplaceAll("asxpm", xpm.Data());
   out << endl << str << endl << endl;
   out << "   TImage *";
   out << name << " = TImage::Create();" << endl;
   out << "   " << name << "->SetImageBuffer((char**)&" << xpm << ", TImage::kXpm);" << endl;
   out << "   " << name << "->Draw();" << endl;
}


ROOT page - Class index - Class Hierarchy - Top of the page

This page has been automatically generated. If you have any comments or suggestions about the page layout send a mail to ROOT support, or contact the developers with any questions or problems regarding ROOT.