#include "TASImage.h"
#include "TASImagePlugin.h"
#include "TROOT.h"
#include "TMath.h"
#include "TSystem.h"
#include "TVirtualX.h"
#include "TVirtualPad.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"
#include "TEnv.h"
#include "TStyle.h"
#include "TText.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;    
}
#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);
static char *gIconPaths[7] = {0, 0, 0, 0, 0, 0, 0};
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()
{
   
   if (fImage) {
      destroy_asimage(&fImage);
   }
   if (fIsGray && fGrayImage) {
      destroy_asimage(&fGrayImage);
   }
   fIsGray     = kFALSE;
   fGrayImage  = 0;
   fImage      = 0;
}
void TASImage::SetDefaults()
{
   
   fImage         = 0;
   fScaledImage   = 0;
   fEditable      = kFALSE;
   fPaintMode     = 1;
   fZoomOffX      = 0;
   fZoomOffY      = 0;
   fZoomWidth     = 0;
   fZoomHeight    = 0;
   fZoomUpdate    = kZoomOps;
   fGrayImage     = 0;
   fIsGray        = kFALSE;
   fPaletteEnabled = kFALSE;
   if (!fgInit) {
      set_application_name((char*)(gProgName ? gProgName : "ROOT"));
      fgInit = kTRUE;
   }
}
TASImage::TASImage()
{
   
   SetDefaults();
}
TASImage::TASImage(UInt_t w, UInt_t h) : TImage(w, h)
{
   
   SetDefaults();
   fImage = create_asimage(w ? w : 20, h ? h : 20, 0);
   UnZoom();
}
TASImage::TASImage(const char *file, EImageFileTypes) : TImage(file)
{
   
   
   
   SetDefaults();
   ReadImage(file);
}
TASImage::TASImage(const char *name, const Double_t *imageData, UInt_t width,
                   UInt_t height, TImagePalette *palette) : TImage(name)
{
   
   
   
   SetDefaults();
   SetImage(imageData, width, height, palette);
}
TASImage::TASImage(const char *name, const TArrayD &imageData, UInt_t width,
                   TImagePalette *palette) : TImage(name)
{
   
   
   
   
   SetDefaults();
   SetImage(imageData, width, palette);
}
TASImage::TASImage(const char *name, const TVectorD &imageData, UInt_t width,
                   TImagePalette *palette) : TImage(name)
{
   
   
   
   
   SetDefaults();
   SetImage(imageData, width, palette);
}
TASImage::TASImage(const TASImage &img) : TImage(img)
{
   
   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)
{
   
   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()
{
   
   DestroyImage();
   delete fScaledImage;
   fScaledImage = 0;
}
static void init_icon_paths()
{
   
   const char *icons = "/icons";
#ifdef R__WIN32
      icons = "\\icons";
#endif
   TString homeIcons = gSystem->HomeDirectory();
   homeIcons += icons;
   TString rootIcons = gSystem->Getenv("ROOTSYS");
   rootIcons += icons;
   TString guiIcons = gEnv->GetValue("Gui.IconPath", "");
   gIconPaths[0] = ".";
   gIconPaths[1] = StrDup(homeIcons.Data());
   gIconPaths[2] = StrDup(rootIcons.Data());
   gIconPaths[3] = StrDup(guiIcons.Data());
#ifdef ROOTICONPATH
   gIconPaths[4] = ROOTICONPATH;
#endif
#ifdef EXTRAICONPATH
   gIconPaths[5] = EXTRAICONPATH;
#endif
   gIconPaths[6] = "";
}
const char *TASImage::TypeFromMagicNumber(const char *file)
{
   
   UChar_t magic;
   FILE *fp = fopen(file, "rb");
   const char *ret = "";
   if (!fp) return 0;
   fread(&magic, 1, 1, fp);
   switch (magic) {
      case 0x00:
      {
         fread(&magic, 1, 1, fp);
         fread(&magic, 1, 1, fp);
         ret = (magic == 1) ? "ico" : "cur";
         break;
      }
      case 0x25:
      {
         fread(&magic, 1, 1, fp);
         if (magic == 0x21) ret = "ps";
         else if (magic == 0x50) ret = "pdf";
         break;
      }
      case 0x42:
         ret = "bmp";
         break;
      case 0x47:
         ret = "gif";
         break;
      case 0x54:
         ret = "tga";
         break;
      case 0x49:
         ret = "tiff";
         break;
      case 0x89:
         ret = "png";
      case 0xff:
         ret = "jpg";
         break;
      default:
         ret = "";
   }
   fclose(fp);
   return ret;
}
void TASImage::ReadImage(const char *filename, EImageFileTypes )
{
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   Bool_t xpm = filename && (filename[0] == '/' &&
                filename[1] == '*') && filename[2] == ' ';
   if (xpm) {  
      SetImageBuffer((char**)&filename, TImage::kXpm);
      fName = "XPM_image";
      return;
   }
   if (!gIconPaths[0]) {
      init_icon_paths();
   }
   static ASImageImportParams iparams;
   iparams.flags = 0;
   iparams.width = 0;
   iparams.height = 0;
   iparams.filter = SCL_DO_ALL;
   iparams.gamma = SCREEN_GAMMA;
   iparams.gamma_table = NULL;
   iparams.compression = GetImageCompression();
   iparams.format = ASA_ASImage;
   iparams.search_path = gIconPaths;
   iparams.subimage = 0;
   iparams.return_animation_delay = -1;
   TString ext;
   const char *dot = strrchr(filename, '.');
   ASImage *image = 0;
   TString fname = filename;
   if (!dot) {
      ext = TypeFromMagicNumber(filename);
   } else {
      ext = dot + 1;
   }
   if (!ext.IsNull() && ext.IsDigit()) { 
      iparams.subimage = ext.Atoi();
      fname = fname(0, fname.Length() - ext.Length() - 1);
      ext = strrchr(fname.Data(), '.') + 1;
   }
   image = file2ASImage_extra(fname.Data(), &iparams);
   if (image) { 
      goto end;
   } else {  
      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(fname.Data());
            if (image) goto end;
         }
         bitmap = plug->ReadFile(fname.Data(), w, h);
         if (bitmap) {
            image = bitmap2asimage(bitmap, w, h, 0, 0);
         }
         if (!image) {
            return;
         }
      }
   }
end:
   fName.Form("%s.", gSystem->BaseName(fname.Data()));
   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)
{
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   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);
   static TString fname;
   fname = file;
   static ASImageExportParams parms;
   ASImage *im = fScaledImage ? fScaledImage->fImage : fImage;
   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(im, fname.Data(), 0);
      return;
   case kXcf:
      ASImage2xcf(im, fname.Data(), 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 kAnimGif:
   {
      parms.gif.type = atype;
      parms.gif.flags = EXPORT_ALPHA | EXPORT_APPEND;
      parms.gif.dither = 0;
      parms.gif.opaque_threshold = 0;
      parms.gif.animate_repeats = 0;
      s += 4; 
      int delay = atoi(s);
      if (delay < 0) {
         delay = 0;
      }
      if (s[0] == '+') { 
         parms.gif.flags |= EXPORT_ANIMATION_REPEATS;
         s++;
         parms.gif.animate_repeats = atoi(s);
      }
      parms.gif.animate_delay = delay;
      int i1 = fname.Index("gif+");
      if (i1 != kNPOS) {
         fname = fname(0, i1 + 3);
      }
      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(im, 0, fname.Data(), atype, &parms)) {
      Error("WriteImage", "error writing file %s", file);
   }
}
TImage::EImageFileTypes TASImage::GetFileType(const char *ext)
{
   
   
   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.Contains("gif+"))
      return kAnimGif;
   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)
{
   
   
   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 kAnimGif:
            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)
{
   
   
   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)
{
   
   
   
   
   
   
   TAttImage::SetPalette(palette);
   if (!InitVisual()) {
      Warning("SetImage", "Visual not initiated");
      return;
   }
   DestroyImage();
   delete fScaledImage;
   fScaledImage = 0;
   
   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);
   }
   
   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)
{
   
   
   
   
   
   
   SetImage(imageData.GetArray(), width, imageData.GetSize() / width, palette);
}
void TASImage::SetImage(const TVectorD &imageData, UInt_t width, TImagePalette *palette)
{
   
   
   
   
   
   
   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)
{
   
   
   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()) { 
      TVirtualPS *psave = gVirtualPS;
      gVirtualPS = (TVirtualPS*)gROOT->ProcessLineFast("new TImageDump()");
      gVirtualPS->Open(pad->GetName(), 114); 
      gVirtualPS->SetBit(BIT(11)); 
      TASImage *itmp = (TASImage*)gVirtualPS->GetStream();
      if (itmp && itmp->fImage) itmp->BeginPaint();
      pad->Paint();
      if (itmp && itmp->fImage && (itmp != this)) {
         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.);
   }
   
   gVirtualX->Update(1);
   gSystem->ProcessEvents();
   gSystem->Sleep(10);
   gSystem->ProcessEvents();
   TVirtualPad *canvas = (TVirtualPad*)pad->GetCanvas();
   Int_t wid = (pad == canvas) ? pad->GetCanvasID() : pad->GetPixmapID();
   gVirtualX->SelectWindow(wid);
   Window_t wd = (Window_t)gVirtualX->GetCurrentWindow();
   if (!wd) return;
   static int x11 = -1;
   if (x11 < 0) x11 = gVirtualX->InheritsFrom("TGX11");
   if (x11) { 
      fImage = pixmap2asimage(fgVisual, wd, x, y, w, h, kAllPlanes, 0, 0);
   } else {
      unsigned char *bits = gVirtualX->GetColorBits(wd, 0, 0, w, h);
      if (!bits) { 
         return;
      }
      fImage = bitmap2asimage(bits, w, h, 0, 0);
      delete [] bits;
   }
}
void TASImage::Draw(Option_t *option)
{
   
   
   
   
   
   
   
   
   
   
   if (!fImage) {
      Error("Draw", "no image set");
      return;
   }
   TString opt = option;
   opt.ToLower();
   if (opt.Contains("n") || !gPad || !gPad->IsEditable()) {
      Int_t w = -64;
      Int_t h = 64;
      w = (fImage->width > 64) ? fImage->width : w;
      h = (fImage->height > 64) ? fImage->height : h;
      Float_t cx = 1./gStyle->GetScreenFactor();
      w = Int_t(w*cx) + 4;
      h = Int_t(h*cx) + 28;
      TString rname = GetName();
      rname.ReplaceAll(".", "");
		rname += Form("\", \"%s (%d x %d)", rname.Data(), fImage->width, fImage->height);
		rname = "new TCanvas(\"" + rname + Form("\", %d, %d);", w, h);
		gROOT->ProcessLineFast(rname.Data());
   }
   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,
                              Int_t xsrc, Int_t ysrc, UInt_t wsrc, UInt_t hsrc,
                              Option_t *opt)
{
   
   if (!im) return;
   wsrc = wsrc ? wsrc : im->width;
   hsrc = hsrc ? hsrc : im->height;
   static int x11 = -1;
   if (x11 < 0) x11 = gVirtualX->InheritsFrom("TGX11");
   Pixmap_t mask = kNone;
   if (x11) {
      UInt_t hh = hsrc;
      UInt_t ow = wsrc%8;
      UInt_t ww = wsrc - ow + (ow ? 8 : 0);
      UInt_t bit = 0;
      int i = 0;
      UInt_t yy = 0;
      UInt_t xx = 0;
      char *bits = new char[ww*hh]; 
      ASImageDecoder *imdec = start_image_decoding(fgVisual, im, SCL_DO_ALPHA,
                                                   xsrc, ysrc, ww, 0, 0);
      if (imdec) {
         for (yy = 0; yy < hh; yy++) {
            imdec->decode_image_scanline(imdec);
            CARD32 *a = imdec->buffer.alpha;
            for (xx = 0; xx < ww; xx++) {
               if (a[xx]) {
                  SETBIT(bits[i], bit);
               } else {
                  CLRBIT(bits[i], bit);
               }
               bit++;
               if (bit == 8) {
                  bit = 0;
                  i++;
               }
            }
         }
      }
      stop_image_decoding(&imdec);
      mask = gVirtualX->CreateBitmap(gVirtualX->GetDefaultRootWindow(),
                                          (const char *)bits, ww, hh);
      delete [] bits;
   }
   GCValues_t gv;
   static GContext_t gc = 0;
   gv.fMask = kGCClipMask | kGCClipXOrigin | kGCClipYOrigin;
   gv.fClipMask = mask;
   gv.fClipXOrigin = x;
   gv.fClipYOrigin = y;
   if (!gc) {
      gc = gVirtualX->CreateGC(gVirtualX->GetDefaultRootWindow(), &gv);
   } else {
      gVirtualX->ChangeGC(gc, &gv);
   }
   if (x11) { 
      asimage2drawable(fgVisual, wid, im, (GC)gc, xsrc, ysrc, x, y, wsrc, hsrc, 1);
   } else {
      ASImage *img = 0;
      unsigned char *bits = (unsigned char *)im->alt.argb32;
      if (!bits) {
         img = tile_asimage(fgVisual, im, xsrc, ysrc, wsrc, hsrc,
                            0, ASA_ARGB32, 0, ASIMAGE_QUALITY_DEFAULT);
         bits = (unsigned char *)img->alt.argb32;
      }
      Pixmap_t pic = gVirtualX->CreatePixmapFromData(bits, wsrc, hsrc);
      if (pic) {
         TString option = opt;
         option.ToLower();
         if (!option.Contains("opaque")) {
            SETBIT(wsrc,31);
            SETBIT(hsrc,31);
         }
         gVirtualX->CopyArea(pic, wid, gc, 0, 0, wsrc, hsrc, x, y);
         gVirtualX->DeletePixmap(pic);
      } else {
         return;
      }
      if (img) {
         destroy_asimage(&img);
      }
   }
   
   if (gv.fClipMask != kNone) gVirtualX->DeletePixmap(gv.fClipMask);
   gv.fMask = kGCClipMask;
   gv.fClipMask = kNone;
   if (gc) gVirtualX->ChangeGC(gc, &gv);
}
void TASImage::PaintImage(Drawable_t wid, Int_t x, Int_t y, Int_t xsrc, Int_t ysrc,
                          UInt_t wsrc, UInt_t hsrc, Option_t *opt)
{
   
   
   
   
   
   
   Image2Drawable(fScaledImage ? fScaledImage->fImage : fImage, wid, x, y,
                  xsrc, ysrc, wsrc, hsrc, opt);
}
void TASImage::Paint(Option_t *option)
{
   
   
   
   
   
   
   
   
   
   
   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;
   
   Int_t to_w = gPad->UtoPixel(1.);
   Int_t to_h = gPad->VtoPixel(0.);
   
   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);
   }
   
   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) {
      
      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 {
      
      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)) {
               
               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 {
               
               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) {
         
         Image2Drawable(grad_im, wid, pal_x, pal_y);
         
         TGaxis axis;
         Int_t ndiv = 510;
         double min = fMinValue;
         double max = fMaxValue;
         axis.SetLineColor(0);       
         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);       
         axis.PaintAxis(pal_Xpos, gPad->AbsPixeltoY(pal_Ay + pal_h),
                        pal_Xpos, gPad->AbsPixeltoY(pal_Ay + 1),
                        min, max, ndiv, "+L");
      }
   }
   
   if (gVirtualPS) {
      if (gVirtualPS->InheritsFrom("TImageDump")) { 
         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);
            
            TGaxis axis;
            Int_t ndiv = 510;
            double min = fMinValue;
            double max = fMaxValue;
            axis.SetLineColor(1);       
            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;
      }
      
      TObjArray *colors = (TObjArray*) gROOT->GetListOfColors();
      TColor *color = 0;
      
      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 dx = gPad->GetX2()-gPad->GetX1();
      Double_t dy = gPad->GetY2()-gPad->GetY1();
      Double_t x1,x2,y1,y2;
      if (expand) {
         x1 = gPad->GetX1();
         x2 = x1+dx/image->width;
         y1 = gPad->GetY2();
         y2 = y1+dy/image->height;
      } else {
         x1 = gPad->GetX1()+dx*gPad->GetLeftMargin();
         x2 = x1+(dx*(1-gPad->GetRightMargin()-gPad->GetLeftMargin()))/image->width;
         y1 = gPad->GetY2()-dy*gPad->GetTopMargin();
         y2 = y1+(dy*(1-gPad->GetTopMargin()-gPad->GetBottomMargin()))/image->height;
      }
      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();
      
      if (grad_im) {
         Double_t xconv = (gPad->AbsPixeltoX(pal_Ax + pal_w) - gPad->AbsPixeltoX(pal_Ax)) / grad_im->width;
         Double_t 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();
         
         TGaxis axis;
         Int_t ndiv = 510;
         double min = fMinValue;
         double max = fMaxValue;
         axis.SetLineColor(1);       
         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)
{
   
   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)
{
   
   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) {
      
      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:
            
            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
{
   
   static char info[64];
   info[0] = 0;
   if (!IsValid()) return info;
   
   px -= gPad->XtoAbsPixel(0);
   py -= gPad->YtoAbsPixel(1);
   
   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;
   
   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)
{
   
   
   
   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;
   
   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];
   
   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)
{
   
   
   
   
   
   
   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::Slice(UInt_t xStart, UInt_t xEnd, UInt_t yStart,  UInt_t yEnd,
                     UInt_t toWidth, UInt_t toHeight)
{
   
   
   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 = slice_asimage(fgVisual, fImage, xStart, xEnd,
                                yStart, yEnd, toWidth, toHeight,
                                ASA_ASImage, GetImageCompression(),
                                GetImageQuality());
   DestroyImage();
   fImage = img;
   UnZoom();
   fZoomUpdate = kZoomOps;
}
void TASImage::Tile(UInt_t toWidth, UInt_t toHeight)
{
   
   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)
{
   
   
   
   
   
   
   
   
   
   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()
{
   
   
   
   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)
{
   
   
   
   
   
   
   
   
   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)
{
   
   
   
   
   
   
   
   
   
   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 fImage ? fImage->width : 0;
}
UInt_t TASImage::GetHeight() const
{
   
   
   return fImage ? fImage->height : 0;
}
UInt_t TASImage::GetScaledWidth() const
{
   
   
   return fScaledImage ? fScaledImage->fImage->width : GetWidth();
}
UInt_t TASImage::GetScaledHeight() const
{
   
   
   return fScaledImage ? fScaledImage->fImage->height : GetHeight();
}
Bool_t TASImage::InitVisual()
{
   
   if (fgVisual && fgVisual->dpy) {
      return kTRUE;
   }
   
   if (!fgVisual && (gROOT->IsBatch() || gVirtualX->InheritsFrom("TGWin32"))) {
      dpy = 0;
      fgVisual = create_asvisual(0, 0, 0, 0);
      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
   if (vis == 0 || cmap == 0) {
      fgVisual = create_asvisual(0, 0, 0, 0);
   } else {
      fgVisual = create_asvisual_for_id(dpy, screen, depth,
                                        XVisualIDFromVisual(vis), cmap, 0);
   }
#else
   fgVisual = create_asvisual(0, 0, 0, 0);
#endif
   return kTRUE;
}
void TASImage::StartPaletteEditor()
{
   
   if (!IsValid()) {
      Warning("StartPaletteEditor", "Image not valid");
      return;
   }
   if (fImage->alt.vector == 0) {
      Warning("StartPaletteEditor", "palette can be modified only for data images");
      return;
   }
   
   TAttImage::StartPaletteEditor();
}
Pixmap_t TASImage::GetPixmap()
{
   
   if (!InitVisual()) {
      Warning("GetPixmap", "Visual not initiated");
      return 0;
   }
   Pixmap_t ret;
   ASImage *img = fScaledImage ? fScaledImage->fImage : fImage;
   static int x11 = -1;
   if (x11 < 0) x11 = gVirtualX->InheritsFrom("TGX11");
   if (x11) {   
      ret = (Pixmap_t)asimage2pixmap(fgVisual, gVirtualX->GetDefaultRootWindow(),
                                       img, 0, kTRUE);
   } else {
      if (!fImage->alt.argb32) {
         BeginPaint();
      }
      ret = gVirtualX->CreatePixmapFromData((unsigned char*)fImage->alt.argb32,
                                             fImage->width, fImage->height);
   }
   return ret;
}
Pixmap_t TASImage::GetMask()
{
   
   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]; 
   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);
   pxmap = gVirtualX->CreateBitmap(gVirtualX->GetDefaultRootWindow(), (const char *)bits,
                                   ww, hh);
   delete [] bits;
   return pxmap;
}
void TASImage::SetImage(Pixmap_t pxm, Pixmap_t mask)
{
   
   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) { 
      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) {   
         return;
      }
      
      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)
{
   
   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(0, fImage, SCL_DO_ALL, 0, y,
                                     img->width, height, 0)) == 0) {
      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)
{
   
   
   
   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(0, img, SCL_DO_ALL, 0, 0,
                                     img->width, 0, 0)) == 0) {
      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)
{
   
   
   
   
   
   
   
   
   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;
   }
   TString fn = font_name;
   fn.Strip();
   char *tmpstr = 0;
   if (fn.EndsWith(".ttf") || fn.EndsWith(".TTF")) {
      tmpstr = gSystem->ExpandPathName(fn.Data());
      fn = tmpstr;
      ttfont = kTRUE;
   }
   delete [] tmpstr;
   if (color) {
      parse_argb_color(color, &text_color);
   }
   if (fImage && 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);
   if (!fImage) {
      fImage = create_asimage(width, height, 0);
      fill_asimage(fgVisual, fImage, 0, 0, width, height, 0xFFFFFFFF);
   }
   text_im = draw_text(text, font, (ASText3DType)type, 0);
   ASImage *rimg = fImage;
   if (fore_file) {
      ASImage *tmp = file2ASImage(fore_file, 0xFFFFFFFF, SCREEN_GAMMA, 0, 0);
      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)
{
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   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)
{
   
   
   
   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
{
   
   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;
}
Double_t *TASImage::Vectorize(UInt_t max_colors, UInt_t dither, Int_t opaque_threshold)
{
   
   
   
   
   
   
   
   
   if (!InitVisual()) {
      Warning("Vectorize", "Visual not initiated");
      return 0;
   }
   if (!fImage) {
      fImage = create_asimage(100, 100, 0);
      if (!fImage) {
         Warning("Vectorize", "Failed to create image");
         return 0;
      }
      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();
   return (Double_t*)fImage->alt.vector;
}
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)
{
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   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)
{
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   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;
   char ch;
   TString str = colors;
   TString col;
   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++);
         col = str(pb - colors, p - pb);
         if (parse_argb_color(col.Data(), gradient.color + npoints1) != col) {
            npoints1++;
         } else {
            Warning("Gradient", "Failed to parse color [%s] - defaulting to black", pb);
         }
      }
      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) {  
      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();
}
static CARD8 MakeComponentHilite(int cmp)
{
   
   if (cmp < 51) {
      cmp = 51;
   }
   cmp = (cmp * 12) / 10;
   return (cmp > 255) ? 255 : cmp;
}
static ARGB32 GetHilite(ARGB32 background)
{
   
   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)
{
   
   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)
{
   
   
   
   
   
   
   
   
   
   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)
{
   
   
   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)
{
   
   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, 0);
   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 )
{
   
   
   
   
   
   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)
{
   
   
   
   if (!InitVisual()) {
      Warning("BeginPaint", "Visual not initiated");
      return;
   }
   if (!fImage) {
      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()
{
   
   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()
{
   
   
   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::GetRgbaArray()
{
   
   
   
   if (!fImage) {
      Warning("GetRgbaArray", "no image");
      return 0;
   }
   if (!fImage->alt.argb32) {
      BeginPaint();
   }
   ASImage *img = fScaledImage ? fScaledImage->fImage : fImage;
   UInt_t i, j;
   Int_t y = 0;
   Int_t idx = 0;
   UInt_t a, rgb, rgba, argb;
   y = 0;
   UInt_t *ret = new UInt_t[img->width*img->height];
   for (i = 0; i < img->height; i++) {
      for (j = 0; j < img->width; j++) {
         idx = y + j;
         argb = img->alt.argb32[idx];
         a = argb >> 24;
         rgb =  argb & 0x00ffffff;
         rgba = (rgb <<  8) + a;
         ret[idx] = rgba;
      }
      y += img->width;
   }
   return ret;
}
UInt_t *TASImage::GetScanline(UInt_t y)
{
   
   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, 0);
   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;
}
#if defined(R__GNU) && defined(__i386__) && !defined(__sun)
#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"         \
                                      :       \
                                      :"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)
{
   
   
   
   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) { 
         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)
{
   
   
   
   
   
   
   
   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)
{
   
   ARGB32 color = (ARGB32)col;
   UInt_t half = 0;
   if (!thick)  thick = 1;
   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)
{
   
   ARGB32 color = (ARGB32)col;
   UInt_t half = 0;
   if (!thick)  thick = 1;
   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)
{
   
   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)
{
   
   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)
{
   
   if (!InitVisual()) {
      Warning("DrawRectangle", "Visual not initiated");
      return;
   }
   if (!fImage) {
      w = w ? w : 20;
      h = h ? h : 20;
      x = 0;
      y = 0;
      fImage = create_asimage(w, h, 0);
      FillRectangle(col, 0, 0, w, h);
      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)
{
   
   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 (!fImage) {
      w = w ? x+w : x+20;
      h = h ? y+h : y+20;
      fImage = create_asimage(w, h, 0);
      FillRectangle(col, 0, 0, w, h);
      return;
   }
   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)
{
   
   UInt_t iDash = 0;    
   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);
      }
   }
   thick = thick <= 0 ? 1 : thick;
   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;
   
   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)
{
   
   UInt_t iDash = 0;    
   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);
      }
   }
   thick = thick <= 0 ? 1 : thick;
   y2 = y2 >= fImage->height ? fImage->height - 1 : y2;
   y1 = y1 >= fImage->height ? fImage->height - 1 : y1;
   
   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)
{
   
   int dx, dy, d;
   int i, i1, i2;
   int x, y, xend, yend;
   int xdir, ydir;
   int q;
   UInt_t iDash = 0;    
   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)
{
   
   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)
{
   
   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)
{
   
   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)
{
   
   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);
   
   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)
{
   
   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)
{
   
   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)
{
   
   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)
{
   
   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)
{
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   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)
{
   
   
   
   
   
   
   
   
   
   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)
{
   
   UInt_t ret = bot;
   _alphaBlend(&ret, &top);
   return ret;
}
const ASVisual *TASImage::GetVisual()
{
   
   return fgVisual;
}
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)
{
   
   
   int xl = 0;                   
   int xr = 0;                   
   int dl = 0;                   
   int dr = 0;                   
   int ml = 0;                   
   int m1l = 0;                  
   int mr = 0, m1r = 0;          
   int incr1l = 0, incr2l = 0;   
   int incr1r = 0, incr2r = 0;   
   int dy;                       
   int y;                        
   int left, right;              
   int i;                        
   int nextleft, nextright;      
   TPoint *ptsOut;               
   UInt_t *width;                
   TPoint *firstPoint;
   UInt_t *firstWidth;
   int imin;                     
   int ymin;                     
   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;
   }
   
   
   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;
   
   do {
      
      if (ppt[nextleft].fY == y) {
         left = nextleft;
         
         
         nextleft++;
         if (nextleft >= (int)npt) {
            nextleft = 0;
         }
         
         
         BRESINITPGON(ppt[nextleft].fY - ppt[left].fY,
                      ppt[left].fX, ppt[nextleft].fX,
                      xl, dl, ml, m1l, incr1l, incr2l);
      }
      
      if (ppt[nextright].fY == y) {
         right = nextright;
         
         
         nextright--;
         if (nextright < 0) {
            nextright = npt-1;
         }
         
         
         BRESINITPGON(ppt[nextright].fY - ppt[right].fY,
                      ppt[right].fX, ppt[nextright].fX,
                      xr, dr, mr, m1r, incr1r, incr2r);
      }
      
      
      i = min(ppt[nextleft].fY, ppt[nextright].fY) - y;
      
      if (i < 0) {
         delete [] firstWidth;
         delete [] firstPoint;
         firstPoint = 0;
         firstWidth = 0;
         return kFALSE;
      }
      while (i-- > 0)  {
         ptsOut->fY = y;
         
         if (xl < xr) {
            *(width++) = xr - xl;
            (ptsOut++)->fX = xl;
         } else {
            *(width++) = xl - xr;
            (ptsOut++)->fX = xr;
         }
         y++;
         
         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)
{
   
   
   UInt_t  nspans = 0;
   TPoint *firstPoint = 0;   
   UInt_t *firstWidth = 0;   
   Bool_t del = GetPolygonSpans(npt, ppt, &nspans, &firstPoint, &firstWidth);
   ARGB32 color;
   parse_argb_color(col, &color);
   if (nspans) {
      if (!stipple && ((color & 0xff000000)==0xff000000)) { 
         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)
{
   
   
   UInt_t  nspans = 0;
   TPoint *firstPoint = 0;   
   UInt_t *firstWidth = 0;   
   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)
{
   
   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)
{
   
   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;  
   int y;                 
   UInt_t nPts = 0;          
   ScanLineList *pSLL;   
   TPoint *ptsOut;       
   UInt_t *width;
   TPoint firstPoint[NUMPTSTOBUFFER];  
   UInt_t firstWidth[NUMPTSTOBUFFER];
   EdgeTableEntry *pPrevAET;       
   EdgeTable ET;                   
   EdgeTableEntry AET;             
   EdgeTableEntry *pETEs;          
   ScanLineListBlock SLLBlock;     
   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)) { 
               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)) {  
         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)
{
   
   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;   
   int y;                  
   UInt_t nPts = 0;       
   ScanLineList *pSLL;    
   TPoint *ptsOut;        
   UInt_t *width;
   TPoint firstPoint[NUMPTSTOBUFFER]; 
   UInt_t firstWidth[NUMPTSTOBUFFER];
   EdgeTableEntry *pPrevAET;       
   EdgeTable ET;                   
   EdgeTableEntry AET;             
   EdgeTableEntry *pETEs;          
   ScanLineListBlock SLLBlock;     
   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]; 
      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_point_argb32(ASDrawContext *ctx, int curr_x, int curr_y, CARD32)
{
   
   int cw = ctx->canvas_width;
   if (curr_x >= 0 && curr_x < cw && curr_y >= 0 && curr_y < ctx->canvas_height)  {
      CARD32 value = ctx->tool->matrix[0]; 
      CARD32 *dst = (CARD32 *)(ctx->canvas);
      dst += curr_y * cw;
      _alphaBlend(&dst[curr_x], &value);
   }
}
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)
{
   
   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;
   if ((ctx->tool->width == 1) && (ctx->tool->height == 1)) {
      ctx->apply_tool_func = apply_tool_point_argb32;
   } else {
      ctx->apply_tool_func = apply_tool_2D_argb32;
   }
   return ctx;
}
static void destroy_asdraw_context32( ASDrawContext *ctx )
{
   
   if (ctx) {
      if (ctx->scratch_canvas) free(ctx->scratch_canvas);
      delete 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)
{
   
   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;
   };
   ASDrawTool brush;
   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;
   }
   destroy_asdraw_context32(ctx);
}
void TASImage::DrawGlyph(void *bitmap, UInt_t color, Int_t bx, Int_t by)
{
   
   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;
   Int_t bxx, byy;
   yy = y0 = by > 0 ? by * fImage->width : 0;
   for (y = 0; y < (int) source->rows; y++) {
      byy = by + y;
      if ((byy >= (int)fImage->height) || (byy <0)) continue;
      for (x = 0; x < (int) source->width; x++) {
         bxx = bx + x;
         if ((bxx >= (int)fImage->width) || (bxx < 0)) continue;
         idx = bxx + 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);
   
   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++) {
      byy = by + y;
      if ((byy >= (int)fImage->height) || (byy <0)) continue;
      for (x = 0; x < (int) source->width; x++) {
         bxx = bx + x;
         
         d = *s++ & 0xff;
         d = ((d + 10) * 5) >> 8;
         if (d > 4) d = 4;
         if (d && (x < (int) source->width) && (bxx < (int)fImage->width) && (bxx >= 0)) {
            idx = bxx + yy;
            fImage->alt.argb32[idx] = (ARGB32)col[d];
         }
      }
      yy += fImage->width;
   }
}
void TASImage::DrawText(TText *text, Int_t x, Int_t y)
{
   
   if (!text) {
      return;
   }
   if (!InitVisual()) {
      Warning("DrawText", "Visual not initiated");
      return;
   }
   if (!fImage) {
      return;
   }
   if (!gPad) {
      return;
   }
   if (!fImage->alt.argb32) {
      BeginPaint();
   }
   if (!TTF::IsInitialized()) TTF::Init();
   
   TTF::SetTextFont(text->GetTextFont());
   Int_t wh = gPad->XtoPixel(gPad->GetX2());
   Int_t hh = gPad->YtoPixel(gPad->GetY1());
   
   Int_t ttfsize;
   Float_t  scale = 1.044;
   if (wh < hh) {
      ttfsize = (Int_t)(text->GetTextSize()*wh*scale);
   } else {
      ttfsize = (Int_t)(text->GetTextSize()*hh*scale);
   }
   TTF::SetTextSize(ttfsize);
   
   TTF::SetRotationMatrix(text->GetTextAngle());
   
   TTF::PrepareString(text->GetTitle());
   TTF::LayoutGlyphs();
   
   TColor *col = gROOT->GetColor(text->GetTextColor());
   if (!col) { 
      col = gROOT->GetColor(1);
   }
   ARGB32 color;
   parse_argb_color(col->AsHexString(), &color);
   
   Int_t align = 0;
   Int_t txalh = text->GetTextAlign()/10;
   Int_t txalv = text->GetTextAlign()%10;
   switch (txalh) {
      case 0 :
      case 1 :
         switch (txalv) {  
            case 1 :
               align = 7;   
               break;
            case 2 :
               align = 4;   
               break;
            case 3 :
               align = 1;   
               break;
         }
         break;
      case 2 :
         switch (txalv) { 
            case 1 :
               align = 8;   
               break;
            case 2 :
               align = 5;   
               break;
            case 3 :
               align = 2;   
               break;
         }
         break;
      case 3 :
         switch (txalv) {  
            case 1 :
               align = 9;   
               break;
            case 2 :
               align = 6;   
               break;
            case 3 :
               align = 3;   
               break;
         }
         break;
   }
   FT_Vector ftal;
   
   if (align == 1 || align == 2 || align == 3) {
      ftal.y = TTF::GetAscent();
   } else if (align == 4 || align == 5 || align == 6) {
      ftal.y = TTF::GetAscent()/2;
   } else {
      ftal.y = 0;
   }
   
   if (align == 3 || align == 6 || align == 9) {
      ftal.x = TTF::GetWidth();
   } else if (align == 2 || align == 5 || align == 8) {
      ftal.x = TTF::GetWidth()/2;
   } else {
      ftal.x = 0;
   }
   FT_Vector_Transform(&ftal, TTF::GetRotMatrix());
   ftal.x = (ftal.x >> 6);
   ftal.y = (ftal.y >> 6);
   TTGlyph *glyph = TTF::GetGlyphs();
   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 - ftal.x + bitmap->left;
      Int_t by = y + ftal.y - bitmap->top;
      DrawGlyph(source, color, bx, by);
   }
}
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)
{
   
   if (!TTF::IsInitialized()) TTF::Init();
   TTF::SetTextFont(font_name);
   TTF::SetTextSize(size);
   TTF::SetRotationMatrix(angle);
   TTF::PrepareString(text);
   TTF::LayoutGlyphs();
   TTGlyph *glyph = TTF::GetGlyphs();
   
   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)
{
   
   
   
   if (!fImage) return;
   static 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, ¶ms);
   }
   if (!ret) {
      *size = isize;
      *buffer = ibuff;
   }
}
Bool_t TASImage::SetImageBuffer(char **buffer, EImageFileTypes type)
{
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   DestroyImage();
   static 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)) {  
            fImage = xpm_data2ASImage((const char**)buffer, ¶ms);
         } else {
            fImage = xpmRawBuff2ASImage((const char*)*buffer, ¶ms);
         }
         break;
      }
      default:
         fImage = PNGBuff2ASimage((CARD8 *)*buffer, ¶ms);
         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()
{
   
   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;
   }
   
   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;
   
   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)
{
   
   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) { 
         return;
      }
      if ( version == 1 ) {
         Int_t fileVersion = b.GetVersionOwner();
         if (fileVersion > 0 && fileVersion < 50000 ) {
            TImage::Streamer(b);
            b >> fMaxValue;
            b >> fMinValue;
            b >> fZoomOffX;
            b >> fZoomOffY;
            b >> fZoomWidth;
            b >> fZoomHeight;
            if ( fileVersion < 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) {     
         b >> size;
         buffer = new char[size];
         b.ReadFastArray(buffer, size);
         SetImageBuffer(&buffer, TImage::kPng);
         delete buffer;
      } else {                   
         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);
         delete vec;
      }
      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) {     
         GetImageBuffer(&buffer, &size, TImage::kPng);
         b << size;
         b.WriteFastArray(buffer, size);
         delete buffer;
      } else {                   
         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 *)
{
   
   if (fImage->alt.vector) {
      Draw("n");
   } else {
      Draw("nxxx");
   }
   CreateThumbnail();
}
const char *TASImage::GetTitle() const
{
   
   if (!gDirectory || !gDirectory->IsWritable()) {
      return 0;
   }
   TASImage *mutble = (TASImage *)this;
   if (fTitle.IsNull()) {
      mutble->SetTitle(fName.Data());
   }
   return fTitle.Data();
}
void TASImage::SetTitle(const char *title)
{
   
   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)
{
   
   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;
   };
   ASDrawTool brush;
   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;
   }
   destroy_asdraw_context32(ctx);
}
void TASImage::DrawStraightEllips(Int_t x, Int_t y, Int_t rx, Int_t ry,
                                  const char *col, Int_t thick)
{
   
   thick = !thick ? 1 : thick;
   Int_t sz = thick*thick;
   CARD32 *matrix;
   Bool_t use_cache = (thick > 0) && ((UInt_t)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;
   };
   ASDrawTool brush;
   brush.matrix = matrix;
   brush.width = thick > 0 ? thick : 1;
   brush.height = thick > 0 ? thick : 1;
   brush.center_y = brush.center_x = thick > 0 ? thick/2 : 0;
   ASDrawContext *ctx = create_draw_context_argb32(fImage, &brush);
   asim_straight_ellips(ctx, x, y, rx, ry, thick < 0);
   if (!use_cache) {
      delete [] matrix;
   }
   destroy_asdraw_context32(ctx);
}
void TASImage::DrawCircle(Int_t x, Int_t y, Int_t r, const char *col, Int_t thick)
{
   
   thick = !thick ? 1 : thick;
   Int_t sz = thick*thick;
   CARD32 *matrix;
   Bool_t use_cache = (thick > 0) && ((UInt_t)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;
   }
   ASDrawTool brush;
   brush.matrix = matrix;
   brush.width = thick > 0 ? thick : 1;
   brush.height = thick > 0 ? thick : 1;
   brush.center_y = brush.center_x = thick > 0 ? thick/2 : 0;
   ASDrawContext *ctx = create_draw_context_argb32(fImage, &brush);
   asim_circle(ctx, x,  y, r, thick < 0);
   if (!use_cache) {
      delete [] matrix;
   }
   destroy_asdraw_context32(ctx);
}
void TASImage::DrawEllips(Int_t x, Int_t y, Int_t rx, Int_t ry, Int_t angle,
                           const char *col, Int_t thick)
{
   
   thick = !thick ? 1 : thick;
   Int_t sz = thick*thick;
   CARD32 *matrix;
   Bool_t use_cache = (thick > 0) && ((UInt_t)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;
   };
   ASDrawTool brush;
   brush.matrix = matrix;
   brush.width = thick > 0 ? thick : 1;
   brush.height = thick > 0 ? thick : 1;
   brush.center_y = brush.center_x = thick > 0 ? thick/2 : 0;
   ASDrawContext *ctx = create_draw_context_argb32(fImage, &brush);
   asim_ellips(ctx, x, y, rx, ry, angle, thick < 0);
   if (!use_cache) {
      delete [] matrix;
   }
   destroy_asdraw_context32(ctx);
}
void TASImage::DrawEllips2(Int_t x, Int_t y, Int_t rx, Int_t ry, Int_t angle,
                           const char *col, Int_t thick)
{
   
   thick = !thick ? 1 : thick;
   Int_t sz = thick*thick;
   CARD32 *matrix;
   Bool_t use_cache = (thick > 0) && ((UInt_t)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;
   };
   ASDrawTool brush;
   brush.matrix = matrix;
   brush.width = thick > 0 ? thick : 1;
   brush.height = thick > 0 ? thick : 1;
   brush.center_y = brush.center_x = thick > 0 ? thick/2 : 0;
   ASDrawContext *ctx = create_draw_context_argb32(fImage, &brush);
   asim_ellips2(ctx, x, y, rx, ry, angle, thick < 0);
   if (!use_cache) {
      delete [] matrix;
   }
   destroy_asdraw_context32(ctx);
}
void TASImage::FloodFill(Int_t , Int_t , const char * ,
                         const char * , const char * )
{
   
}
void TASImage::Gray(Bool_t on)
{
   
   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)
{
   
   Int_t xy;
   x = x < 0 ? 0 : x;
   y = y < 0 ? 0 : y;
   
   gVirtualX->Update(1);
   gSystem->ProcessEvents();
   gSystem->Sleep(10);
   gSystem->ProcessEvents();
   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;
   static int x11 = -1;
   if (x11 < 0) x11 = gVirtualX->InheritsFrom("TGX11");
   if (x11) { 
      fImage = pixmap2asimage(fgVisual, wid, x, y, w, h, kAllPlanes, 0, 0);
   } else {
      unsigned char *bits = gVirtualX->GetColorBits(wid, 0, 0, w, h);
      if (!bits) { 
         return;
      }
      fImage = bitmap2asimage(bits, w, h, 0, 0);
      delete [] bits;
   }
}
void TASImage::SetPaletteEnabled(Bool_t on)
{
   
   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(ostream &out, Option_t * )
{
    
   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;
}
Bool_t TASImage::SetJpegDpi(const char *name, UInt_t set)
{
   
   
   
   
   static char buf[20];
   FILE *fp = fopen(name, "rb+");
   if (!fp) {
      printf("file %s : failed to open\n", name);
      return kFALSE;
   }
   fread(buf, 1, 20, fp);
   char dpi1 = (set & 0xffff) >> 8;
   char dpi2 = set & 0xff;
   int i = 0;
   int dpi = 0; 
   for (i = 0; i < 20; i++) {
      if ((buf[i] == 0x4a) && (buf[i+1] == 0x46) &&  (buf[i+2] == 0x49) &&
          (buf[i+3] == 0x46) && (buf[i+4] == 0x00) ) {
         dpi = i + 7;
         break;
      }
   }
   if (i == 20) { 
      fclose(fp);
      printf("file %s : wrong JPEG format\n", name);
      return kFALSE;
   }
   buf[dpi] = 1;   
   
   buf[dpi + 1] = dpi1;
   buf[dpi + 2] = dpi2;
   
   buf[dpi + 3] = dpi1;
   buf[dpi + 4] = dpi2;
   rewind(fp);
   fwrite(buf, 1, 20, fp);
   fclose(fp);
   return kTRUE;
}
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.