Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TASImage.cxx
Go to the documentation of this file.
1 // @(#)root/asimage:$Id: TASImage.cxx,v 1.54 2006/03/13 15:18:56 rdm E
2 // Author: Fons Rademakers, Reiner Rohlfs, Valeriy Onuchin 28/11/2001
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2001, Rene Brun, Fons Rademakers and Reiner Rohlfs *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 /**************************************************************************
13  * Some parts of this source are based on libAfterImage 2.00.00
14  * (http://www.afterstep.org/)
15  *
16  * Copyright (c) 2002 Sasha Vasko <sasha@aftercode.net>
17  * Copyright (c) 1998, 1999 Ethan Fischer <allanon@crystaltokyo.com>
18  *
19  * This program is free software; you can redistribute it and/or modify
20  * it under the terms of the GNU Library General Public License as
21  * published by the Free Software Foundation; either version 2 of the
22  * License, or (at your option) any later version.
23  *
24  * This program is distributed in the hope that it will be useful,
25  * but WITHOUT ANY WARRANTY; without even the implied warranty of
26  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27  * GNU General Public License for more details.
28  *
29  * You should have received a copy of the GNU Library General Public
30  * License along with this program; if not, write to the Free Software
31  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
32  *
33  **************************************************************************/
34 
35 /** \class TASImage
36 \ingroup asimage
37 
38 Image class.
39 
40 TASImage is the concrete interface to the image processing library
41 libAfterImage.
42 
43 It allows reading and writing of images in different formats, several image
44 manipulations (scaling, tiling, merging, etc.) and displaying in pads. The size
45 of the image on the screen does not depend on the original size of the image but
46 on the size of the pad. Therefore it is very easy to resize the image on the
47 screen by resizing the pad.
48 
49 Besides reading an image from a file an image can be defined by a two
50 dimensional array of values. A palette defines the color of each value.
51 
52 The image can be zoomed by defining a rectangle with the mouse. The color
53 palette can be modified with a GUI, just select StartPaletteEditor() from the
54 context menu.
55 
56 Several examples showing how to use this class are available in the
57 ROOT tutorials: `$ROOTSYS/tutorials/image/`
58 */
59 
60 # include <ft2build.h>
61 # include FT_FREETYPE_H
62 # include FT_GLYPH_H
63 #include "TASImage.h"
64 #include "TASImagePlugin.h"
65 #include "TROOT.h"
66 #include "TMath.h"
67 #include "TSystem.h"
68 #include "TVirtualX.h"
69 #include "TVirtualPad.h"
70 #include "TArrayD.h"
71 #include "TVectorD.h"
72 #include "TVirtualPS.h"
73 #include "TGaxis.h"
74 #include "TColor.h"
75 #include "TObjArray.h"
76 #include "TArrayL.h"
77 #include "TPoint.h"
78 #include "TFrame.h"
79 #include "TTF.h"
80 #include "TRandom.h"
81 #include "Riostream.h"
82 #include "THashTable.h"
83 #include "TPluginManager.h"
84 #include "TEnv.h"
85 #include "TStyle.h"
86 #include "TText.h"
87 #include "RConfigure.h"
88 #include "TVirtualPadPainter.h"
89 
90 #ifndef WIN32
91 #ifndef R__HAS_COCOA
92 # include <X11/Xlib.h>
93 #endif
94 #else
95 # include "Windows4root.h"
96 #endif
97 #ifndef WIN32
98 #ifdef R__HAS_COCOA
99 # define X_DISPLAY_MISSING 1
100 #endif
101 # include <afterbase.h>
102 #else
103 # include <win32/config.h>
104 # include <win32/afterbase.h>
105 # define X_DISPLAY_MISSING 1
106 #endif
107 # include <afterimage.h>
108 # include <bmp.h>
109 extern "C" {
110 # include <draw.h>
111 }
112 
113 // auxiliary functions for general polygon filling
114 #include "TASPolyUtils.c"
115 
116 
117 ASVisual *TASImage::fgVisual = 0;
118 Bool_t TASImage::fgInit = kFALSE;
119 
120 static ASFontManager *gFontManager = 0;
121 static unsigned long kAllPlanes = ~0;
122 THashTable *TASImage::fgPlugList = new THashTable(50);
123 
124 // default icon paths
125 static char *gIconPaths[7] = {0, 0, 0, 0, 0, 0, 0};
126 
127 // To scale fonts to the same size as the old TT version
128 const Float_t kScale = 0.985;
129 
130 ///////////////////////////// alpha-blending macros ///////////////////////////////
131 
132 #if defined(__GNUC__) && __GNUC__ >= 4 && ((__GNUC_MINOR__ == 2 && __GNUC_PATCHLEVEL__ >= 1) || (__GNUC_MINOR__ >= 3)) && !__INTEL_COMPILER
133 #pragma GCC diagnostic ignored "-Wstrict-aliasing"
134 #endif
135 
136 #ifdef R__BYTESWAP
137 typedef struct {
138  unsigned char b;
139  unsigned char g;
140  unsigned char r;
141  unsigned char a;
142 } __argb32__;
143 #else
144 typedef struct {
145  unsigned char a;
146  unsigned char r;
147  unsigned char g;
148  unsigned char b;
149 } __argb32__;
150 #endif
151 
152 
153 //______________________________________________________________________________
154 #define _alphaBlend(bot, top) {\
155  __argb32__ *T = (__argb32__*)(top);\
156  __argb32__ *B = (__argb32__*)(bot);\
157  int aa = 255-T->a;\
158  if (!aa) {\
159  *bot = *top;\
160  } else { \
161  B->a = ((B->a*aa)>>8) + T->a;\
162  B->r = (B->r*aa + T->r*T->a)>>8;\
163  B->g = (B->g*aa + T->g*T->a)>>8;\
164  B->b = (B->b*aa + T->b*T->a)>>8;\
165  }\
166 }\
167 
168 
169 ClassImp(TASImage);
170 ClassImp(TASImagePlugin);
171 
172 ////////////////////////////////////////////////////////////////////////////////
173 /// Destroy image.
174 
175 void TASImage::DestroyImage()
176 {
177  if (fImage) {
178  destroy_asimage(&fImage);
179  }
180 
181  if (fIsGray && fGrayImage) {
182  destroy_asimage(&fGrayImage);
183  }
184 
185  fIsGray = kFALSE;
186  fGrayImage = 0;
187  fImage = 0;
188 }
189 
190 ////////////////////////////////////////////////////////////////////////////////
191 /// Set default parameters.
192 
193 void TASImage::SetDefaults()
194 {
195  fImage = 0;
196  fScaledImage = 0;
197  fMaxValue = 1;
198  fMinValue = 0;
199  fEditable = kFALSE;
200  fPaintMode = 1;
201  fZoomOffX = 0;
202  fZoomOffY = 0;
203  fZoomWidth = 0;
204  fZoomHeight = 0;
205  fZoomUpdate = kZoomOps;
206 
207  fGrayImage = 0;
208  fIsGray = kFALSE;
209  fPaletteEnabled = kFALSE;
210 
211  if (!fgInit) {
212  set_application_name((char*)(gProgName ? gProgName : "ROOT"));
213  fgInit = kTRUE;
214  }
215 }
216 
217 ////////////////////////////////////////////////////////////////////////////////
218 /// Default image constructor.
219 
220 TASImage::TASImage()
221 {
222  SetDefaults();
223 }
224 
225 ////////////////////////////////////////////////////////////////////////////////
226 /// Create an empty image.
227 
228 TASImage::TASImage(UInt_t w, UInt_t h) : TImage(w, h)
229 {
230  SetDefaults();
231  fImage = create_asimage(w ? w : 20, h ? h : 20, 0);
232  UnZoom();
233 }
234 
235 ////////////////////////////////////////////////////////////////////////////////
236 /// Create an image object and read from specified file.
237 /// For more information see description of function ReadImage()
238 /// which is called by this constructor.
239 
240 TASImage::TASImage(const char *file, EImageFileTypes) : TImage(file)
241 {
242  SetDefaults();
243  TString fname = file;
244  gSystem->ExpandPathName(fname);
245  ReadImage(fname.Data());
246 }
247 
248 ////////////////////////////////////////////////////////////////////////////////
249 /// Create an image depending on the values of imageData.
250 /// For more information see function SetImage() which is called
251 /// by this constructor.
252 
253 TASImage::TASImage(const char *name, const Double_t *imageData, UInt_t width,
254  UInt_t height, TImagePalette *palette) : TImage(name)
255 {
256  SetDefaults();
257  SetImage(imageData, width, height, palette);
258 }
259 
260 ////////////////////////////////////////////////////////////////////////////////
261 /// Create an image depending on the values of imageData.
262 /// The size of the image is width X (imageData.fN / width).
263 /// For more information see function SetImage() which is called by
264 /// this constructor.
265 
266 TASImage::TASImage(const char *name, const TArrayD &imageData, UInt_t width,
267  TImagePalette *palette) : TImage(name)
268 {
269  SetDefaults();
270  SetImage(imageData, width, palette);
271 }
272 
273 ////////////////////////////////////////////////////////////////////////////////
274 /// Create an image depending on the values of imageData.
275 /// The size of the image is width X (imageData.fN / width).
276 /// For more information see function SetImage() which is called by
277 /// this constructor.
278 
279 TASImage::TASImage(const char *name, const TVectorD &imageData, UInt_t width,
280  TImagePalette *palette) : TImage(name)
281 {
282  SetDefaults();
283  SetImage(imageData, width, palette);
284 }
285 
286 ////////////////////////////////////////////////////////////////////////////////
287 /// Image copy constructor.
288 
289 TASImage::TASImage(const TASImage &img) : TImage(img)
290 {
291  SetDefaults();
292 
293  if (img.IsValid()) {
294  fImage = clone_asimage(img.fImage, SCL_DO_ALL);
295  fScaledImage = fScaledImage ? (TASImage*)img.fScaledImage->Clone("") : 0;
296  fGrayImage = fGrayImage ? clone_asimage(img.fGrayImage, SCL_DO_ALL) : 0;
297 
298  if (img.fImage->alt.vector) {
299  Int_t size = img.fImage->width * img.fImage->height * sizeof(double);
300  fImage->alt.vector = (double*)malloc(size);
301  memcpy(fImage->alt.vector, img.fImage->alt.vector, size);
302  }
303 
304  fZoomUpdate = kNoZoom;
305  fZoomOffX = img.fZoomOffX;
306  fZoomOffY = img.fZoomOffY;
307  fZoomWidth = img.fZoomWidth;
308  fZoomHeight = img.fZoomHeight;
309  fEditable = img.fEditable;
310  fIsGray = img.fIsGray;
311  }
312 }
313 
314 ////////////////////////////////////////////////////////////////////////////////
315 /// Image assignment operator.
316 
317 TASImage &TASImage::operator=(const TASImage &img)
318 {
319  if (this != &img && img.IsValid()) {
320  TImage::operator=(img);
321 
322  DestroyImage();
323  delete fScaledImage;
324  fImage = clone_asimage(img.fImage, SCL_DO_ALL);
325  fScaledImage = fScaledImage ? (TASImage*)img.fScaledImage->Clone("") : 0;
326  fGrayImage = fGrayImage ? clone_asimage(img.fGrayImage, SCL_DO_ALL) : 0;
327 
328  if (img.fImage->alt.vector) {
329  Int_t size = img.fImage->width * img.fImage->height * sizeof(double);
330  fImage->alt.vector = (double*)malloc(size);
331  memcpy(fImage->alt.vector, img.fImage->alt.vector, size);
332  }
333 
334  fScaledImage = img.fScaledImage ? (TASImage*)img.fScaledImage->Clone("") : 0;
335  fZoomUpdate = kNoZoom;
336  fZoomOffX = img.fZoomOffX;
337  fZoomOffY = img.fZoomOffY;
338  fZoomWidth = img.fZoomWidth;
339  fZoomHeight = img.fZoomHeight;
340  fEditable = img.fEditable;
341  fIsGray = img.fIsGray;
342  fPaintMode = 1;
343  }
344 
345  return *this;
346 }
347 
348 ////////////////////////////////////////////////////////////////////////////////
349 /// Image destructor, clean up image and visual.
350 
351 TASImage::~TASImage()
352 {
353  DestroyImage();
354  delete fScaledImage;
355  fScaledImage = 0;
356 }
357 
358 ////////////////////////////////////////////////////////////////////////////////
359 /// Set icons paths.
360 
361 static void init_icon_paths()
362 {
363  TString icon_path = gEnv->GetValue("Gui.IconPath", "");
364  if (icon_path.IsNull()) {
365  icon_path = "icons";
366  gSystem->PrependPathName(gSystem->HomeDirectory(), icon_path);
367 #ifndef R__WIN32
368  icon_path = ".:" + icon_path + ":" + TROOT::GetIconPath() + ":" + EXTRAICONPATH;
369 #else
370  icon_path = ".;" + icon_path + ";" + TROOT::GetIconPath() + ";" + EXTRAICONPATH;
371 #endif
372  }
373 
374  Int_t cnt = 0;
375  Ssiz_t from = 0;
376  TString token;
377 #ifndef R__WIN32
378  const char *delim = ":";
379 #else
380  const char *delim = ";";
381 #endif
382  while (icon_path.Tokenize(token, from, delim) && cnt < 6) {
383  char *path = gSystem->ExpandPathName(token.Data());
384  if (path) {
385  gIconPaths[cnt] = path;
386  cnt++;
387  }
388  }
389  gIconPaths[cnt] = 0;
390 }
391 
392 ////////////////////////////////////////////////////////////////////////////////
393 /// Guess the file type from the first byte of file.
394 
395 const char *TASImage::TypeFromMagicNumber(const char *file)
396 {
397  UChar_t magic;
398  FILE *fp = fopen(file, "rb");
399  const char *ret = "";
400 
401  if (!fp) return 0;
402 
403  if (!fread(&magic, 1, 1, fp)) {
404  fclose(fp);
405  return 0;
406  }
407 
408  switch (magic) {
409  case 0x00:
410  {
411  if (!fread(&magic, 1, 1, fp)) {
412  fclose(fp);
413  return 0;
414  }
415  if (!fread(&magic, 1, 1, fp)) {
416  fclose(fp);
417  return 0;
418  }
419 
420  ret = (magic == 1) ? "ico" : "cur";
421  break;
422  }
423  case 0x25:
424  {
425  if (!fread(&magic, 1, 1, fp)) {
426  fclose(fp);
427  return 0;
428  }
429 
430  if (magic == 0x21) ret = "ps";
431  else if (magic == 0x50) ret = "pdf";
432  break;
433  }
434  case 0x42:
435  ret = "bmp";
436  break;
437  case 0x47:
438  ret = "gif";
439  break;
440  case 0x54:
441  ret = "tga";
442  break;
443  case 0x49:
444  ret = "tiff";
445  break;
446  case 0x89:
447  ret = "png";
448  break;
449  case 0xff:
450  ret = "jpg";
451  break;
452  default:
453  ret = "";
454  }
455 
456  fclose(fp);
457  return ret;
458 }
459 
460 ////////////////////////////////////////////////////////////////////////////////
461 /// Read specified image file.
462 /// The file type is determined by the file extension (the type argument is
463 /// ignored). It will attempt to append .gz and then .Z to the filename and
464 /// find such a file. If the filename ends with extension consisting of digits
465 /// only, it will attempt to find the file with this extension stripped
466 /// off. On success this extension will be used to load subimage from
467 /// the file with that number. Subimage is supported for GIF files
468 /// (ICO, BMP, CUR, TIFF, XCF to be supported in future).
469 /// For example,
470 /// ~~~ {.cpp}
471 /// i1 = TImage::Open("anim.gif.0"); // read the first subimage
472 /// i4 = TImage::Open("anim.gif.3"); // read the forth subimage
473 /// ~~~
474 /// It is also possible to put XPM raw string (see also SetImageBuffer) as
475 /// the first input parameter ("filename"), such string is returned by
476 /// GetImageBuffer method.
477 
478 void TASImage::ReadImage(const char *filename, EImageFileTypes /*type*/)
479 {
480  if (!InitVisual()) {
481  Warning("Scale", "Visual not initiated");
482  return;
483  }
484 
485  Bool_t xpm = filename && (filename[0] == '/' &&
486  filename[1] == '*') && filename[2] == ' ';
487 
488  if (xpm) { // XPM strings in-memory array
489  SetImageBuffer((char**)&filename, TImage::kXpm);
490  fName = "XPM_image";
491  return;
492  }
493 
494  if (!gIconPaths[0]) {
495  init_icon_paths();
496  }
497  // suppress the "root : looking for image ..." messages
498  set_output_threshold(0);
499 
500  static ASImageImportParams iparams;
501  iparams.flags = 0;
502  iparams.width = 0;
503  iparams.height = 0;
504  iparams.filter = SCL_DO_ALL;
505  iparams.gamma = SCREEN_GAMMA;
506  iparams.gamma_table = NULL;
507  iparams.compression = GetImageCompression();
508  iparams.format = ASA_ASImage;
509  iparams.search_path = gIconPaths;
510  iparams.subimage = 0;
511  iparams.return_animation_delay = -1;
512 
513  TString ext;
514  const char *dot;
515  if (filename) dot = strrchr(filename, '.');
516  else dot = 0;
517  ASImage *image = 0;
518  TString fname = filename;
519 
520  if (!dot) {
521  if (filename) ext = TypeFromMagicNumber(filename);
522  else ext = dot + 1;
523  } else {
524  ext = dot + 1;
525  }
526 
527  if (!ext.IsNull() && ext.IsDigit()) { // read subimage
528  iparams.subimage = ext.Atoi();
529  fname = fname(0, fname.Length() - ext.Length() - 1);
530  ext = strrchr(fname.Data(), '.') + 1;
531  }
532 
533  image = file2ASImage_extra(fname.Data(), &iparams);
534 
535  if (image) { // it's OK
536  goto end;
537  } else { // try to read it via plugin
538  if (ext.IsNull()) {
539  return;
540  }
541  ext.ToLower();
542  ext.Strip();
543  UInt_t w = 0;
544  UInt_t h = 0;
545  unsigned char *bitmap = 0;
546 
547  TImagePlugin *plug = (TImagePlugin*)fgPlugList->FindObject(ext.Data());
548 
549  if (!plug) {
550  TPluginHandler *handler = gROOT->GetPluginManager()->FindHandler("TImagePlugin", ext);
551  if (!handler || ((handler->LoadPlugin() == -1))) {
552  return;
553  }
554  plug = (TImagePlugin*)handler->ExecPlugin(1, ext.Data());
555 
556  if (!plug) {
557  return;
558  }
559 
560  fgPlugList->Add(plug);
561  }
562 
563  if (plug) {
564  if (plug->InheritsFrom(TASImagePlugin::Class())) {
565  image = ((TASImagePlugin*)plug)->File2ASImage(fname.Data());
566  if (image) goto end;
567  }
568  bitmap = plug->ReadFile(fname.Data(), w, h);
569  if (bitmap) {
570  image = bitmap2asimage(bitmap, w, h, 0, 0);
571  }
572  if (!image) {
573  return;
574  }
575  }
576  }
577 
578 end:
579  fName.Form("%s.", gSystem->BaseName(fname.Data()));
580 
581  DestroyImage();
582  delete fScaledImage;
583  fScaledImage = 0;
584 
585  fImage = image;
586  fZoomUpdate = kNoZoom;
587  fEditable = kFALSE;
588  fZoomOffX = 0;
589  fZoomOffY = 0;
590  fZoomWidth = fImage->width;
591  fZoomHeight = fImage->height;
592  fPaintMode = 1;
593 }
594 
595 ////////////////////////////////////////////////////////////////////////////////
596 /// Write image to specified file.
597 ///
598 /// If there is no file extension or if the file extension is unknown, the
599 /// type argument will be used to determine the file type. The quality and
600 /// compression is derived from the TAttImage values.
601 ///
602 /// It's possible to write image into an animated GIF file by specifying file
603 /// name as "myfile.gif+" or "myfile.gif+NN", where NN is the delay of displaying
604 /// subimages during animation in 10ms seconds units. NN is not restricted
605 /// to two digits. If NN is omitted the delay between subimages is zero.
606 /// For an animation that stops after last subimage is reached, one has to
607 /// write the last image as .gif+ (zero delay of last image) or .gif+NN
608 /// (NN*10ms delay of last image).
609 ///
610 /// For repeated animation (looping), the last subimage must be specified as:
611 /// - "myfile.gif++NN++" if you want an infinite looping gif with NN*10ms
612 /// delay of the last image.
613 /// - "myfile.gif++" for an infinite loop with zero delay of last image.
614 /// - "myfile.gif+NN++RR" if you want a finite looping gif with NN*10ms
615 /// delay of the last image and the animation to be stopped after RR
616 /// repeats. RR is not restricted to two digits.
617 ///
618 /// A deprecated version for saving the last subimage of a looping gif animation is:
619 /// - "myfile.gif++NN" for a finite loop where NN is number of repetitions
620 /// and NN*10ms the delay of last image. (No separate control of repeats and delay).
621 /// Note: If the file "myfile.gif" already exists, the new frames are appended at
622 /// the end of the file. To avoid this, delete it first with gSystem->Unlink(myfile.gif);
623 ///
624 /// The following macro creates animated gif from jpeg images with names
625 /// - imageNN.jpg, where 1<= NN <= 10
626 /// - The delays are set to 10*10ms.
627 /// ~~~ {.cpp}
628 /// {
629 /// TImage *img = 0;
630 /// gSystem->Unlink("anim.gif"); // delete existing file
631 ///
632 /// for (int i = 1; i <= 10; i++) {
633 /// delete img; // delete previous image
634 ///
635 /// // Read image data. Image can be in any format, e.g. png, gif, etc.
636 /// img = TImage::Open(Form("image%d.jpg", i));
637 ///
638 /// if (i < 10) {
639 /// img->WriteImage("anim.gif+10"); // 10 centiseconds delay
640 /// } else { // the last image written. "++" stands for infinit animation.
641 /// img->WriteImage("anim.gif++10++"); // 10 centiseconds delay of last image
642 /// }
643 /// }
644 /// }
645 /// ~~~
646 
647 void TASImage::WriteImage(const char *file, EImageFileTypes type)
648 {
649  if (!IsValid()) {
650  Error("WriteImage", "no image loaded");
651  return;
652  }
653 
654  if (!file || !*file) {
655  Error("WriteImage", "no file name specified");
656  return;
657  }
658 
659  const char *s;
660  if ((s = strrchr(file, '.'))) {
661  s++;
662  EImageFileTypes t = GetFileType(s);
663  if (t == kUnknown) {
664  Error("WriteImage", "cannot determine a valid file type");
665  return;
666  }
667  if (t != kUnknown)
668  type = t;
669  }
670 
671  if (type == kUnknown) {
672  Error("WriteImage", "not a valid file type was specified");
673  return;
674  }
675 
676  UInt_t mytype;
677  MapFileTypes(type, mytype);
678  ASImageFileTypes atype = (ASImageFileTypes)mytype;
679 
680  UInt_t aquality;
681  EImageQuality quality = GetImageQuality();
682  MapQuality(quality, aquality);
683 
684  static TString fname;
685  fname = file;
686  static ASImageExportParams parms;
687  ASImage *im = fScaledImage ? fScaledImage->fImage : fImage;
688 
689  switch (type) {
690  case kXpm:
691  parms.xpm.type = atype;
692  parms.xpm.flags = EXPORT_ALPHA;
693  parms.xpm.dither = 4;
694  parms.xpm.opaque_threshold = 127;
695  parms.xpm.max_colors = 512;
696  break;
697  case kBmp:
698  ASImage2bmp(im, fname.Data(), 0);
699  return;
700  case kXcf:
701  ASImage2xcf(im, fname.Data(), 0);
702  return;
703  case kPng:
704  parms.png.type = atype;
705  parms.png.flags = EXPORT_ALPHA;
706  parms.png.compression = !GetImageCompression() ? -1 : int(GetImageCompression());
707  break;
708  case kJpeg:
709  parms.jpeg.type = atype;
710  parms.jpeg.flags = 0;
711  parms.jpeg.quality = aquality;
712  break;
713  case kGif:
714  parms.gif.type = atype;
715  parms.gif.flags = EXPORT_ALPHA;
716  parms.gif.dither = 0;
717  parms.gif.opaque_threshold = 0;
718  break;
719  case kAnimGif:
720  {
721  parms.gif.type = atype;
722  parms.gif.flags = EXPORT_ALPHA | EXPORT_APPEND;
723  parms.gif.dither = 0;
724  parms.gif.opaque_threshold = 0;
725  parms.gif.animate_repeats = 0;
726 
727  s += 4; // skip "gif+"
728  int delay = 0;
729 
730  const TString sufix = s; // we denote as suffix as everything that is after .gif+
731  const UInt_t sLength = sufix.Length();
732 
733  if (sufix=="+") {
734  // .gif++ implies that this is the last image of the animation
735  // and that the gif will loop forever (infinite animation)
736  // and that the delay of last image is 0ms (backward compatibility reasons)
737  delay = 0;
738  parms.gif.flags |= EXPORT_ANIMATION_REPEATS;// activate repetition
739  parms.gif.animate_repeats = 0;// 0 is code for looping forever (if EXPORT_ANIMATION_REPEATS is also set)
740  } else if(sufix=="") {
741  // .gif+ implies that this is a subimage of the animation with zero delay
742  // or the last image of an animation that will not repeat.
743  // Last image delay is zero because atoi("")=0.
744  delay = atoi(s);
745  //Nothing else needed here
746  } else if(!sufix.Contains("+")) {
747  // .gif+NN implies that this is a subimage of the animation
748  // with NN*10ms delay (latency) until the next one.
749  // You can also use this option on the last image if you do not want the gif to replay
750  delay = atoi(s);
751  //Nothing else needed here
752  } else if(sLength>1 && sufix.BeginsWith("+") && sufix.CountChar('+')==1) {
753  // .gif++NN implies that this is the last image of the animation
754  // and that it will loop NN number of times (finite animation)
755  // and that the delay of last image is NN*10ms (backward compatibility reasons).
756  delay = atoi(s);// atoi is smart enough to ignore the "+" sign before.
757  parms.gif.flags |= EXPORT_ANIMATION_REPEATS;// activate repetition
758  parms.gif.animate_repeats = atoi(s);// loops only NN times, then it stops. atoi discards + sign.
759  } else if(sLength>3 && sufix.BeginsWith("+") && sufix.EndsWith("++") && !TString(sufix(1,sLength-3)).Contains("+")) {
760  // .gif++NN++ implies that this is the last image of the animation
761  // and that the gif will loop forever (infinite animation)
762  // and that the delay of last image is NN*10ms.
763  // In contrast, .gif++ is an infinite loop but with 0 delay, whereas the option
764  // .gif++NN is a loop repeated NN times (not infinite) with NN*10ms delay
765  // between last and first loop images.
766  delay = atoi(s);// atoi discards the three plus signs
767  parms.gif.flags |= EXPORT_ANIMATION_REPEATS;// activate repetition
768  parms.gif.animate_repeats = 0;// 0 is code for looping forever (if EXPORT_ANIMATION_REPEATS is also set)
769  } else if(sLength>3 && sufix.CountChar('+')==2 && TString(sufix(1,sLength-2)).Contains("++")) {
770  // .gif+NN++RR implies that this is the last image animation
771  // and that the gif will loop RR number of times (finite animation)
772  // and that the delay of last image is NN*10ms.
773  const TString sDelay = sufix(0,sufix.First('+'));
774  const TString sRepeats = sufix(sufix.First('+')+2,sLength-(sufix.First('+')+2));
775  delay = atoi(sDelay);
776  parms.gif.flags |= EXPORT_ANIMATION_REPEATS;// activate repetition
777  parms.gif.animate_repeats = atoi(sRepeats);// loops NN times.
778  } else {
779  Error("WriteImage", "gif suffix %s not yet supported", s);
780  return;
781  }
782 
783  parms.gif.animate_delay = delay;
784 
785  int i1 = fname.Index("gif+");
786  if (i1 != kNPOS) {
787  fname = fname(0, i1 + 3);
788  }
789  else {
790  Error("WriteImage", "unexpected gif extension structure %s", fname.Data());
791  return;
792  }
793  break;
794  }
795  case kTiff:
796  parms.tiff.type = atype;
797  parms.tiff.flags = EXPORT_ALPHA;
798  parms.tiff.rows_per_strip = 0;
799  parms.tiff.compression_type = aquality <= 50 ? TIFF_COMPRESSION_JPEG :
800  TIFF_COMPRESSION_NONE;
801  parms.tiff.jpeg_quality = 100;
802  parms.tiff.opaque_threshold = 0;
803  break;
804  default:
805  Error("WriteImage", "file type %s not yet supported", s);
806  return;
807  }
808 
809  if (!ASImage2file(im, 0, fname.Data(), atype, &parms)) {
810  Error("WriteImage", "error writing file %s", file);
811  }
812 }
813 
814 ////////////////////////////////////////////////////////////////////////////////
815 /// Return file type depending on specified extension.
816 /// Protected method.
817 
818 TImage::EImageFileTypes TASImage::GetFileType(const char *ext)
819 {
820  TString s(ext);
821  s.Strip();
822  s.ToLower();
823 
824  if (s == "xpm")
825  return kXpm;
826  if (s == "png")
827  return kPng;
828  if (s == "jpg" || s == "jpeg")
829  return kJpeg;
830  if (s == "xcf")
831  return kXcf;
832  if (s == "ppm")
833  return kPpm;
834  if (s == "pnm")
835  return kPnm;
836  if (s == "bmp")
837  return kBmp;
838  if (s == "ico")
839  return kIco;
840  if (s == "cur")
841  return kCur;
842  if (s == "gif")
843  return kGif;
844  if (s.Contains("gif+"))
845  return kAnimGif;
846  if (s == "tiff")
847  return kTiff;
848  if (s == "xbm")
849  return kXbm;
850  if (s == "tga")
851  return kTga;
852  if (s == "xml")
853  return kXml;
854 
855  return kUnknown;
856 }
857 
858 ////////////////////////////////////////////////////////////////////////////////
859 /// Map file type to/from AfterImage types.
860 /// Protected method.
861 
862 void TASImage::MapFileTypes(EImageFileTypes &type, UInt_t &astype, Bool_t toas)
863 {
864  if (toas) {
865  switch (type) {
866  case kXpm:
867  astype = ASIT_Xpm; break;
868  case kZCompressedXpm:
869  astype = ASIT_ZCompressedXpm; break;
870  case kGZCompressedXpm:
871  astype = ASIT_GZCompressedXpm; break;
872  case kPng:
873  astype = ASIT_Png; break;
874  case kJpeg:
875  astype = ASIT_Jpeg; break;
876  case kXcf:
877  astype = ASIT_Xcf; break;
878  case kPpm:
879  astype = ASIT_Ppm; break;
880  case kPnm:
881  astype = ASIT_Pnm; break;
882  case kBmp:
883  astype = ASIT_Bmp; break;
884  case kIco:
885  astype = ASIT_Ico; break;
886  case kCur:
887  astype = ASIT_Cur; break;
888  case kGif:
889  astype = ASIT_Gif; break;
890  case kAnimGif:
891  astype = ASIT_Gif; break;
892  case kTiff:
893  astype = ASIT_Tiff; break;
894  case kXbm:
895  astype = ASIT_Xbm; break;
896  case kTga:
897  astype = ASIT_Targa; break;
898  case kXml:
899  astype = ASIT_XMLScript; break;
900  default:
901  astype = ASIT_Unknown;
902  }
903  } else {
904  switch (astype) {
905  case ASIT_Xpm:
906  type = kXpm; break;
907  case ASIT_ZCompressedXpm:
908  type = kZCompressedXpm; break;
909  case ASIT_GZCompressedXpm:
910  type = kGZCompressedXpm; break;
911  case ASIT_Png:
912  type = kPng; break;
913  case ASIT_Jpeg:
914  type = kJpeg; break;
915  case ASIT_Xcf:
916  type = kXcf; break;
917  case ASIT_Ppm:
918  type = kPpm; break;
919  case ASIT_Pnm:
920  type = kPnm; break;
921  case ASIT_Bmp:
922  type = kBmp; break;
923  case ASIT_Ico:
924  type = kIco; break;
925  case ASIT_Cur:
926  type = kCur; break;
927  case ASIT_Gif:
928  type = kGif; break;
929  case ASIT_Tiff:
930  type = kTiff; break;
931  case ASIT_Xbm:
932  type = kXbm; break;
933  case ASIT_XMLScript:
934  type = kXml; break;
935  case ASIT_Targa:
936  type = kTga; break;
937  default:
938  type = kUnknown;
939  }
940  }
941 }
942 
943 ////////////////////////////////////////////////////////////////////////////////
944 /// Map quality to/from AfterImage quality.
945 /// Protected method.
946 
947 void TASImage::MapQuality(EImageQuality &quality, UInt_t &asquality, Bool_t toas)
948 {
949  if (toas) {
950  switch (quality) {
951  case kImgPoor:
952  asquality = 25; break;
953  case kImgFast:
954  asquality = 75; break;
955  case kImgGood:
956  asquality = 50; break;
957  case kImgBest:
958  asquality = 100; break;
959  default:
960  asquality = 0;
961  }
962  } else {
963  quality = kImgDefault;
964  if (asquality > 0 && asquality <= 25)
965  quality = kImgPoor;
966  if (asquality > 26 && asquality <= 50)
967  quality = kImgFast;
968  if (asquality > 51 && asquality <= 75)
969  quality = kImgGood;
970  if (asquality > 76 && asquality <= 100)
971  quality = kImgBest;
972  }
973 }
974 
975 ////////////////////////////////////////////////////////////////////////////////
976 /// Deletes the old image and creates a new image depending on the values
977 /// of imageData. The size of the image is width X height.
978 ///
979 /// The color of each pixel depends on the imageData of the corresponding
980 /// pixel. The palette is used to convert an image value into its color.
981 /// If palette is not defined (palette = 0) a default palette is used.
982 /// Any previously defined zooming is reset.
983 
984 void TASImage::SetImage(const Double_t *imageData, UInt_t width, UInt_t height,
985  TImagePalette *palette)
986 {
987  TAttImage::SetPalette(palette);
988 
989  if (!InitVisual()) {
990  Warning("SetImage", "Visual not initiated");
991  return;
992  }
993 
994  DestroyImage();
995  delete fScaledImage;
996  fScaledImage = 0;
997 
998  // get min and max value of image
999  fMinValue = fMaxValue = *imageData;
1000  for (Int_t pixel = 1; pixel < Int_t(width * height); pixel++) {
1001  if (fMinValue > *(imageData + pixel)) fMinValue = *(imageData + pixel);
1002  if (fMaxValue < *(imageData + pixel)) fMaxValue = *(imageData + pixel);
1003  }
1004 
1005  // copy ROOT palette to asImage palette
1006  const TImagePalette &pal = GetPalette();
1007 
1008  ASVectorPalette asPalette;
1009 
1010  asPalette.npoints = pal.fNumPoints;
1011  Int_t col;
1012  for (col = 0; col < 4; col++)
1013  asPalette.channels[col] = new UShort_t[asPalette.npoints];
1014 
1015  memcpy(asPalette.channels[0], pal.fColorBlue, pal.fNumPoints * sizeof(UShort_t));
1016  memcpy(asPalette.channels[1], pal.fColorGreen, pal.fNumPoints * sizeof(UShort_t));
1017  memcpy(asPalette.channels[2], pal.fColorRed, pal.fNumPoints * sizeof(UShort_t));
1018  memcpy(asPalette.channels[3], pal.fColorAlpha, pal.fNumPoints * sizeof(UShort_t));
1019 
1020  asPalette.points = new Double_t[asPalette.npoints];
1021  for (Int_t point = 0; point < Int_t(asPalette.npoints); point++)
1022  asPalette.points[point] = fMinValue + (fMaxValue - fMinValue) * pal.fPoints[point];
1023 
1024  fImage = create_asimage_from_vector(fgVisual, (Double_t*)imageData, width,
1025  height, &asPalette, ASA_ASImage,
1026  GetImageCompression(), GetImageQuality());
1027 
1028  delete [] asPalette.points;
1029  for (col = 0; col < 4; col++)
1030  delete [] asPalette.channels[col];
1031 
1032  fZoomUpdate = 0;
1033  fZoomOffX = 0;
1034  fZoomOffY = 0;
1035  fZoomWidth = width;
1036  fZoomHeight = height;
1037  fPaletteEnabled = kTRUE;
1038 }
1039 
1040 ////////////////////////////////////////////////////////////////////////////////
1041 /// Delete the old image and creates a new image depending on the values
1042 /// of imageData. The size of the image is width X (imageData.fN / width).
1043 /// The color of each pixel depends on the imageData of the corresponding
1044 /// pixel. The palette is used to convert an image value into its color.
1045 /// If palette is not defined (palette = 0) a default palette is used.
1046 /// Any previously defined zooming is reset.
1047 
1048 void TASImage::SetImage(const TArrayD &imageData, UInt_t width, TImagePalette *palette)
1049 {
1050  SetImage(imageData.GetArray(), width, imageData.GetSize() / width, palette);
1051 }
1052 
1053 ////////////////////////////////////////////////////////////////////////////////
1054 /// Delete the old image and creates a new image depending on the values
1055 /// of imageData. The size of the image is width X (imageData.fN / width).
1056 /// The color of each pixel depends on the imageData of the corresponding
1057 /// pixel. The palette is used to convert an image value into its color.
1058 /// If palette is not defined (palette = 0) a default palette is used.
1059 /// Any previously defined zooming is reset.
1060 
1061 void TASImage::SetImage(const TVectorD &imageData, UInt_t width, TImagePalette *palette)
1062 {
1063  SetImage(imageData.GetMatrixArray(), width,
1064  imageData.GetNoElements() / width, palette);
1065 }
1066 
1067 ////////////////////////////////////////////////////////////////////////////////
1068 /// Create an image from the given pad, afterwards this image can be
1069 /// saved in any of the supported image formats.
1070 
1071 void TASImage::FromPad(TVirtualPad *pad, Int_t x, Int_t y, UInt_t w, UInt_t h)
1072 {
1073  if (!pad) {
1074  Error("FromPad", "pad cannot be 0");
1075  return;
1076  }
1077 
1078  if (!InitVisual()) {
1079  Warning("FromPad", "Visual not initiated");
1080  return;
1081  }
1082 
1083  SetName(pad->GetName());
1084 
1085  DestroyImage();
1086  delete fScaledImage;
1087  fScaledImage = 0;
1088 
1089  if (gROOT->IsBatch()) { // in batch mode
1090  TVirtualPS *psave = gVirtualPS;
1091  gVirtualPS = (TVirtualPS*)gROOT->ProcessLineFast("new TImageDump()");
1092  gVirtualPS->Open(pad->GetName(), 114); // in memory
1093  gVirtualPS->SetBit(BIT(11)); //kPrintingPS
1094 
1095  TASImage *itmp = (TASImage*)gVirtualPS->GetStream();
1096 
1097  if (itmp && itmp->fImage) {
1098  itmp->BeginPaint();
1099  }
1100 
1101  TVirtualPad *sav = gPad;
1102  gPad = pad;
1103  pad->Paint();
1104  gPad = sav;
1105 
1106  if (itmp && itmp->fImage && (itmp != this)) {
1107  fImage = clone_asimage(itmp->fImage, SCL_DO_ALL);
1108  if (itmp->fImage->alt.argb32) {
1109  UInt_t sz = itmp->fImage->width*itmp->fImage->height;
1110  fImage->alt.argb32 = (ARGB32*)safemalloc(sz*sizeof(ARGB32));
1111  memcpy(fImage->alt.argb32, itmp->fImage->alt.argb32, sz*4);
1112  }
1113  }
1114  delete gVirtualPS;
1115  gVirtualPS = psave;
1116  return;
1117  }
1118 
1119  // X11 Synchronization
1120  gVirtualX->Update(1);
1121  if (!gThreadXAR) {
1122  gSystem->Sleep(100);
1123  gSystem->ProcessEvents();
1124  gSystem->Sleep(10);
1125  gSystem->ProcessEvents();
1126  }
1127 
1128  TVirtualPad *canvas = (TVirtualPad*)pad->GetCanvas();
1129  Int_t wid = (pad == canvas) ? pad->GetCanvasID() : pad->GetPixmapID();
1130  gVirtualX->SelectWindow(wid);
1131 
1132  Window_t wd = (Window_t)gVirtualX->GetCurrentWindow();
1133  if (!wd) return;
1134 
1135  if (w == 0) w = TMath::Abs(pad->UtoPixel(1.));
1136  if (h == 0) h = pad->VtoPixel(0.);
1137 
1138  static int x11 = -1;
1139  if (x11 < 0) x11 = gVirtualX->InheritsFrom("TGX11");
1140 
1141  if (x11) { //use built-in optimized version
1142  fImage = pixmap2asimage(fgVisual, wd, x, y, w, h, kAllPlanes, 0, 0);
1143  } else {
1144  unsigned char *bits = gVirtualX->GetColorBits(wd, 0, 0, w, h);
1145 
1146  if (!bits) { // error
1147  return;
1148  }
1149  fImage = bitmap2asimage(bits, w, h, 0, 0);
1150  delete [] bits;
1151  }
1152 }
1153 
1154 ////////////////////////////////////////////////////////////////////////////////
1155 /// Draw image.
1156 /// Support the following drawing options:
1157 /// - "T[x,y[,tint]]" : tile image (use specified offset and tint),
1158 /// e.g. "T100,100,#556655"
1159 /// with this option the zooming is not possible
1160 /// and disabled
1161 /// - "N" : display in new canvas (of original image size)
1162 /// - "X" : image is drawn expanded to pad size
1163 /// - "Z" : image is vectorized and image palette is drawn
1164 ///
1165 /// The default is to display the image in the current gPad.
1166 
1167 void TASImage::Draw(Option_t *option)
1168 {
1169  if (!fImage) {
1170  Error("Draw", "no image set");
1171  return;
1172  }
1173 
1174  TString opt = option;
1175  opt.ToLower();
1176  if (opt.Contains("n") || !gPad || !gPad->IsEditable()) {
1177  Int_t w = -64;
1178  Int_t h = 64;
1179  w = (fImage->width > 64) ? (Int_t)fImage->width : w;
1180  h = (fImage->height > 64) ? (Int_t)fImage->height : h;
1181 
1182  Float_t cx = 1./gStyle->GetScreenFactor();
1183  w = Int_t(w*cx) + 4;
1184  h = Int_t(h*cx) + 28;
1185  TString rname = GetName();
1186  rname.ReplaceAll(".", "");
1187  rname += Form("\", \"%s (%d x %d)", rname.Data(), fImage->width, fImage->height);
1188  rname = "new TCanvas(\"" + rname + Form("\", %d, %d);", w, h);
1189  gROOT->ProcessLineFast(rname.Data());
1190  }
1191 
1192  if (!opt.Contains("x")) {
1193  Double_t left = gPad->GetLeftMargin();
1194  Double_t right = gPad->GetRightMargin();
1195  Double_t top = gPad->GetTopMargin();
1196  Double_t bottom = gPad->GetBottomMargin();
1197 
1198  gPad->Range(-left / (1.0 - left - right),
1199  -bottom / (1.0 - top - bottom),
1200  1 + right / (1.0 - left - right),
1201  1 + top / ( 1.0 - top - bottom));
1202  gPad->RangeAxis(0, 0, 1, 1);
1203  }
1204 
1205  TFrame *frame = gPad->GetFrame();
1206  if (frame) {
1207  frame->SetBorderMode(0);
1208  frame->SetFillColor(gPad->GetFillColor());
1209  frame->SetLineColor(gPad->GetFillColor());
1210  frame->Draw();
1211  }
1212 
1213  TObject::Draw(option);
1214 }
1215 
1216 ////////////////////////////////////////////////////////////////////////////////
1217 /// Draw asimage on drawable.
1218 
1219 void TASImage::Image2Drawable(ASImage *im, Drawable_t wid, Int_t x, Int_t y,
1220  Int_t xsrc, Int_t ysrc, UInt_t wsrc, UInt_t hsrc,
1221  Option_t *opt)
1222 {
1223  if (!im) return;
1224 
1225  wsrc = wsrc ? wsrc : im->width;
1226  hsrc = hsrc ? hsrc : im->height;
1227 
1228  static int x11 = -1;
1229  if (x11 < 0) x11 = gVirtualX->InheritsFrom("TGX11");
1230 
1231  Pixmap_t mask = kNone;
1232 
1233  if (x11) {
1234  UInt_t hh = hsrc;
1235  UInt_t ow = wsrc%8;
1236  UInt_t ww = wsrc - ow + (ow ? 8 : 0);
1237 
1238  UInt_t bit = 0;
1239  int i = 0;
1240  UInt_t yy = 0;
1241  UInt_t xx = 0;
1242 
1243  char *bits = new char[ww*hh]; //an array of bits
1244 
1245  ASImageDecoder *imdec = start_image_decoding(fgVisual, im, SCL_DO_ALPHA,
1246  xsrc, ysrc, ww, 0, 0);
1247  if (imdec) {
1248  for (yy = 0; yy < hh; yy++) {
1249  imdec->decode_image_scanline(imdec);
1250  CARD32 *a = imdec->buffer.alpha;
1251 
1252  for (xx = 0; xx < ww; xx++) {
1253  if (a[xx]) {
1254  SETBIT(bits[i], bit);
1255  } else {
1256  CLRBIT(bits[i], bit);
1257  }
1258  bit++;
1259  if (bit == 8) {
1260  bit = 0;
1261  i++;
1262  }
1263  }
1264  }
1265  }
1266 
1267  stop_image_decoding(&imdec);
1268 
1269  mask = gVirtualX->CreateBitmap(gVirtualX->GetDefaultRootWindow(),
1270  (const char *)bits, ww, hh);
1271  delete [] bits;
1272  }
1273 
1274  GCValues_t gv;
1275  static GContext_t gc = 0;
1276 
1277  gv.fMask = kGCClipMask | kGCClipXOrigin | kGCClipYOrigin;
1278  gv.fClipMask = mask;
1279  gv.fClipXOrigin = x;
1280  gv.fClipYOrigin = y;
1281 
1282  if (!gc) {
1283  gc = gVirtualX->CreateGC(gVirtualX->GetDefaultRootWindow(), &gv);
1284  } else {
1285  gVirtualX->ChangeGC(gc, &gv);
1286  }
1287 
1288  if (x11 && (!gPad || gPad->GetGLDevice() == -1)) { //use built-in optimized version
1289  asimage2drawable(fgVisual, wid, im, (GC)gc, xsrc, ysrc, x, y, wsrc, hsrc, 1);
1290  } else {
1291  ASImage *img = 0;
1292  unsigned char *bits = (unsigned char *)im->alt.argb32;
1293  if (!bits) {
1294  img = tile_asimage(fgVisual, im, xsrc, ysrc, wsrc, hsrc,
1295  0, ASA_ARGB32, 0, ASIMAGE_QUALITY_DEFAULT);
1296  if (img)
1297  bits = (unsigned char *)img->alt.argb32;
1298  }
1299 
1300  if (bits) {
1301  TString option(opt);
1302  option.ToLower();
1303 
1304  if (gPad && gPad->GetGLDevice() != -1) {
1305  if (TVirtualPadPainter *painter = gPad->GetPainter())
1306  painter->DrawPixels(bits, wsrc, hsrc, x, y, !option.Contains("opaque"));
1307  } else {
1308  Pixmap_t pic = gVirtualX->CreatePixmapFromData(bits, wsrc, hsrc);
1309  if (pic) {
1310  if (!option.Contains("opaque")) {
1311  SETBIT(wsrc,31);
1312  SETBIT(hsrc,31);
1313  }
1314  gVirtualX->CopyArea(pic, wid, gc, 0, 0, wsrc, hsrc, x, y);
1315  gVirtualX->DeletePixmap(pic);
1316  }
1317  }
1318  }
1319 
1320  if (img) {
1321  destroy_asimage(&img);
1322  }
1323  }
1324 
1325  // free mask pixmap
1326  if (gv.fClipMask != kNone) gVirtualX->DeletePixmap(gv.fClipMask);
1327 
1328  gv.fMask = kGCClipMask;
1329  gv.fClipMask = kNone;
1330  if (gc) gVirtualX->ChangeGC(gc, &gv);
1331 }
1332 
1333 ////////////////////////////////////////////////////////////////////////////////
1334 /// Draw image on the drawable wid (pixmap, window) at x,y position.
1335 ///
1336 /// \param[in] wid : Drawable (pixmap or window) on which image is drawn.
1337 /// \param[in] x,y : Window coordinates where image is drawn.
1338 /// \param[in] xsrc, ysrc : X and Y coordinates of an image area to be drawn.
1339 /// \param[in] wsrc, hsrc : Width and height image area to be drawn.
1340 
1341 void TASImage::PaintImage(Drawable_t wid, Int_t x, Int_t y, Int_t xsrc, Int_t ysrc,
1342  UInt_t wsrc, UInt_t hsrc, Option_t *opt)
1343 {
1344  Image2Drawable(fScaledImage ? fScaledImage->fImage : fImage, wid, x, y,
1345  xsrc, ysrc, wsrc, hsrc, opt);
1346 }
1347 
1348 ////////////////////////////////////////////////////////////////////////////////
1349 /// Paint image.
1350 /// Support the following drawing options:
1351 /// - "T[x,y[,tint]]" : tile image (use specified offset and tint),
1352 /// e.g. "T100,100,#556655"
1353 /// with this option the zooming is not possible
1354 /// and disabled
1355 /// - "N" : display in new canvas (of original image size)
1356 /// - "X" : image is drawn expanded to pad size
1357 /// - "Z" : image is vectorized and image palette is drawn
1358 ///
1359 /// The default is to display the image in the current gPad.
1360 
1361 void TASImage::Paint(Option_t *option)
1362 {
1363  if (!fImage) {
1364  Error("Paint", "no image set");
1365  return;
1366  }
1367 
1368  if (!InitVisual()) {
1369  Warning("Paint", "Visual not initiated");
1370  return;
1371  }
1372 
1373  Int_t tile_x = 0, tile_y = 0;
1374  CARD32 tile_tint = 0;
1375  Bool_t tile = kFALSE;
1376  Bool_t expand = kFALSE;
1377 
1378  TString opt = option;
1379  opt.ToLower();
1380 
1381  if (opt.Contains("t")) {
1382  char stint[64];
1383  if (sscanf(opt.Data() + opt.Index("t"), "t%d,%d,%s", &tile_x, &tile_y,
1384  stint) <= 3) {
1385  tile = kTRUE;
1386  if (parse_argb_color(stint, (CARD32*)&tile_tint) == stint)
1387  tile_tint = 0;
1388  } else {
1389  Error("Paint", "tile option error");
1390  }
1391  } else if (opt.Contains("x")) {
1392  expand = kTRUE;
1393  fConstRatio = kFALSE;
1394  } else if (opt.Contains("z")) {
1395  fPaletteEnabled = kTRUE;
1396 
1397  if (!fImage->alt.vector) {
1398  Vectorize(256);
1399  }
1400  }
1401 
1402  ASImage *image = fImage;
1403 
1404  // Get geometry of pad
1405  Int_t to_w = gPad->UtoPixel(1.);
1406  Int_t to_h = gPad->VtoPixel(0.);
1407 
1408  // remove the size by the margin of the pad
1409  if (!expand) {
1410  to_h = (Int_t)(to_h * (1.0 - gPad->GetBottomMargin() - gPad->GetTopMargin() ) + 0.5);
1411  to_w = (Int_t)(to_w * (1.0 - gPad->GetLeftMargin() - gPad->GetRightMargin() ) + 0.5);
1412  }
1413 
1414  if ((to_w < 25 || to_h < 25) && !expand) {
1415  Error("Paint", "pad too small to display an image");
1416  return;
1417  }
1418 
1419  if (GetConstRatio()) {
1420  if ((Double_t)to_w / (Double_t)fZoomWidth <
1421  (Double_t)to_h / (Double_t)fZoomHeight)
1422  to_h = Int_t(Double_t(fZoomHeight) * to_w / fZoomWidth);
1423  else
1424  to_w = Int_t(Double_t(fZoomWidth) * to_h / fZoomHeight);
1425  }
1426  // upper left corner and size of the palette in pixels
1427  Int_t pal_Ax = to_w + gPad->UtoAbsPixel(gPad->GetLeftMargin()) +
1428  (gPad->UtoAbsPixel(gPad->GetRightMargin()) / 10);
1429  Int_t pal_Ay = gPad->YtoAbsPixel(1.0);
1430  Int_t pal_x = to_w + gPad->UtoPixel(gPad->GetLeftMargin()) +
1431  (gPad->UtoPixel(gPad->GetRightMargin()) / 10);
1432  Int_t pal_y = gPad->YtoPixel(1.0);
1433  Int_t pal_w = gPad->UtoPixel(gPad->GetRightMargin()) / 3;
1434  Int_t pal_h = to_h;
1435 
1436  ASImage *grad_im = 0;
1437 
1438  if (fImage->alt.vector && fPaletteEnabled) {
1439  // draw the palette
1440  ASGradient grad;
1441  const TImagePalette &pal = GetPalette();
1442 
1443  grad.npoints = pal.fNumPoints;
1444  grad.type = GRADIENT_Top2Bottom;
1445  grad.color = new ARGB32[grad.npoints];
1446  grad.offset = new double[grad.npoints];
1447 
1448  for (Int_t pt = 0; pt < grad.npoints; pt++) {
1449  Int_t oldPt = grad.npoints - pt -1;
1450  grad.offset[pt] = 1 - pal.fPoints[oldPt];
1451  grad.color[pt] = (((ARGB32)(pal.fColorBlue[oldPt] & 0xff00)) >> 8) |
1452  (((ARGB32)(pal.fColorGreen[oldPt] & 0xff00)) ) |
1453  (((ARGB32)(pal.fColorRed[oldPt] & 0xff00)) << 8) |
1454  (((ARGB32)(pal.fColorAlpha[oldPt] & 0xff00)) << 16);
1455  }
1456 
1457  grad_im = make_gradient(fgVisual, &grad , UInt_t(pal_w),
1458  pal_h, SCL_DO_COLOR,
1459  ASA_ARGB32, GetImageCompression(), GetImageQuality());
1460 
1461  delete [] grad.color;
1462  delete [] grad.offset;
1463  }
1464 
1465  if (tile) {
1466  delete fScaledImage;
1467  fScaledImage = (TASImage*)TImage::Create();
1468  if (!fScaledImage) return;
1469  fScaledImage->fImage = tile_asimage(fgVisual, fImage, tile_x, tile_y,
1470  to_w, to_h, tile_tint, ASA_ASImage,
1471  GetImageCompression(), GetImageQuality());
1472  image = fScaledImage->fImage;
1473 
1474  } else if (fZoomUpdate == kZoomOps) {
1475  image = fImage;
1476 
1477  } else {
1478  // Scale and zoom image if needed
1479  if (Int_t(fImage->width) != to_w || Int_t(fImage->height) != to_h ||
1480  fImage->width != fZoomWidth || fImage->height != fZoomHeight) {
1481 
1482  if (fScaledImage && (Int_t(fScaledImage->GetWidth()) != to_w ||
1483  Int_t(fScaledImage->GetHeight()) != to_h ||
1484  fZoomUpdate)) {
1485 
1486  delete fScaledImage;
1487  fScaledImage = 0;
1488  }
1489 
1490  if (!fScaledImage) {
1491  fScaledImage = (TASImage*)TImage::Create();
1492  if (!fScaledImage) return;
1493 
1494  if (fZoomWidth && fZoomHeight &&
1495  ((fImage->width != fZoomWidth) || (fImage->height != fZoomHeight))) {
1496  // zoom and scale image
1497  ASImage *tmpImage = 0;
1498 
1499  tmpImage = tile_asimage(fgVisual, fImage, fZoomOffX,
1500  fImage->height - fZoomHeight - fZoomOffY,
1501  fZoomWidth, fZoomHeight, 0, ASA_ASImage,
1502  GetImageCompression(), GetImageQuality());
1503 
1504  if (tmpImage) {
1505  fScaledImage->fImage = scale_asimage(fgVisual, tmpImage, to_w, to_h,
1506  ASA_ASImage, GetImageCompression(),
1507  GetImageQuality());
1508  destroy_asimage(&tmpImage);
1509  }
1510  } else {
1511  // scale image, no zooming
1512  fScaledImage->fImage = scale_asimage(fgVisual, fImage, to_w, to_h,
1513  ASA_ASImage, GetImageCompression(),
1514  GetImageQuality());
1515  }
1516  }
1517  image = fScaledImage->fImage;
1518  }
1519  }
1520  fZoomUpdate = 0;
1521 
1522  if (!image) {
1523  Error("Paint", "image could not be rendered to display");
1524  return;
1525  }
1526 
1527  int tox = expand ? 0 : int(gPad->UtoPixel(1.) * gPad->GetLeftMargin());
1528  int toy = expand ? 0 : int(gPad->VtoPixel(0.) * gPad->GetTopMargin());
1529 
1530  if (!gROOT->IsBatch()) {
1531  Window_t wid = (Window_t)gVirtualX->GetWindowID(gPad->GetPixmapID());
1532  Image2Drawable(fScaledImage ? fScaledImage->fImage : fImage, wid, tox, toy);
1533 
1534  if (grad_im && fPaletteEnabled) {
1535  // draw color bar
1536  Image2Drawable(grad_im, wid, pal_x, pal_y);
1537 
1538  // values of palette
1539  TGaxis axis;
1540  Int_t ndiv = 510;
1541  double min = fMinValue;
1542  double max = fMaxValue;
1543  axis.SetLineColor(0); // draw white ticks
1544  Double_t pal_Xpos = gPad->AbsPixeltoX(pal_Ax + pal_w);
1545  axis.PaintAxis(pal_Xpos, gPad->PixeltoY(pal_Ay + pal_h - 1),
1546  pal_Xpos, gPad->PixeltoY(pal_Ay),
1547  min, max, ndiv, "+LU");
1548  min = fMinValue;
1549  max = fMaxValue;
1550  axis.SetLineColor(1); // draw black ticks
1551  axis.PaintAxis(pal_Xpos, gPad->AbsPixeltoY(pal_Ay + pal_h),
1552  pal_Xpos, gPad->AbsPixeltoY(pal_Ay + 1),
1553  min, max, ndiv, "+L");
1554  }
1555  }
1556 
1557  // loop over pixmap and draw image to PostScript
1558  if (gVirtualPS) {
1559  if (gVirtualPS->InheritsFrom("TImageDump")) { // PostScript is asimage
1560  TImage *dump = (TImage *)gVirtualPS->GetStream();
1561  if (!dump) return;
1562  dump->Merge(fScaledImage ? fScaledImage : this, "alphablend",
1563  gPad->XtoAbsPixel(0), gPad->YtoAbsPixel(1));
1564 
1565  if (grad_im) {
1566  TASImage tgrad;
1567  tgrad.fImage = grad_im;
1568  dump->Merge(&tgrad, "alphablend", pal_Ax, pal_Ay);
1569 
1570  // values of palette
1571  TGaxis axis;
1572  Int_t ndiv = 510;
1573  double min = fMinValue;
1574  double max = fMaxValue;
1575  axis.SetLineColor(1); // draw black ticks
1576  Double_t pal_Xpos = gPad->AbsPixeltoX(pal_Ax + pal_w);
1577  axis.PaintAxis(pal_Xpos, gPad->AbsPixeltoY(pal_Ay + pal_h),
1578  pal_Xpos, gPad->AbsPixeltoY(pal_Ay + 1),
1579  min, max, ndiv, "+L");
1580  }
1581  return;
1582  } else if (gVirtualPS->InheritsFrom("TPDF")) {
1583  Warning("Paint", "PDF not implemented yet");
1584  return;
1585  } else if (gVirtualPS->InheritsFrom("TSVG")) {
1586  Warning("Paint", "SVG not implemented yet");
1587  return;
1588  }
1589 
1590  // get special color cell to be reused during image printing
1591  TObjArray *colors = (TObjArray*) gROOT->GetListOfColors();
1592  TColor *color = 0;
1593  // Look for color by name
1594  if ((color = (TColor*)colors->FindObject("Image_PS")) == 0)
1595  color = new TColor(colors->GetEntries(), 1., 1., 1., "Image_PS");
1596 
1597  gVirtualPS->SetFillColor(color->GetNumber());
1598  gVirtualPS->SetFillStyle(1001);
1599 
1600  Double_t dx = gPad->GetX2()-gPad->GetX1();
1601  Double_t dy = gPad->GetY2()-gPad->GetY1();
1602  Double_t x1,x2,y1,y2;
1603 
1604  if (expand) {
1605  x1 = gPad->GetX1();
1606  x2 = x1+dx/image->width;
1607  y1 = gPad->GetY2();
1608  y2 = y1+dy/image->height;
1609  } else {
1610  x1 = gPad->GetX1()+dx*gPad->GetLeftMargin();
1611  x2 = x1+(dx*(1-gPad->GetRightMargin()-gPad->GetLeftMargin()))/image->width;
1612  y1 = gPad->GetY2()-dy*gPad->GetTopMargin();
1613  y2 = y1+(dy*(1-gPad->GetTopMargin()-gPad->GetBottomMargin()))/image->height;
1614  }
1615 
1616  gVirtualPS->CellArrayBegin(image->width, image->height, x1, x2, y1, y2);
1617 
1618  ASImageDecoder *imdec = start_image_decoding(fgVisual, image, SCL_DO_ALL,
1619  0, 0, image->width, image->height, 0);
1620  if (!imdec) return;
1621  for (Int_t yt = 0; yt < (Int_t)image->height; yt++) {
1622  imdec->decode_image_scanline(imdec);
1623  for (Int_t xt = 0; xt < (Int_t)image->width; xt++)
1624  gVirtualPS->CellArrayFill(imdec->buffer.red[xt],
1625  imdec->buffer.green[xt],
1626  imdec->buffer.blue[xt]);
1627  }
1628  stop_image_decoding(&imdec);
1629  gVirtualPS->CellArrayEnd();
1630 
1631  // print the color bar
1632  if (grad_im) {
1633  Double_t xconv = (gPad->AbsPixeltoX(pal_Ax + pal_w) - gPad->AbsPixeltoX(pal_Ax)) / grad_im->width;
1634  Double_t yconv = (gPad->AbsPixeltoY(pal_Ay - pal_h) - gPad->AbsPixeltoY(pal_Ay)) / grad_im->height;
1635  x1 = gPad->AbsPixeltoX(pal_Ax);
1636  x2 = x1 + xconv;
1637  y2 = gPad->AbsPixeltoY(pal_Ay);
1638  y1 = y2 - yconv;
1639  gVirtualPS->CellArrayBegin(grad_im->width, grad_im->height,
1640  x1, x2, y1, y2);
1641 
1642  imdec = start_image_decoding(fgVisual, grad_im, SCL_DO_ALL,
1643  0, 0, grad_im->width, grad_im->height, 0);
1644  if (imdec) {
1645  for (Int_t yt = 0; yt < (Int_t)grad_im->height; yt++) {
1646  imdec->decode_image_scanline(imdec);
1647  for (Int_t xt = 0; xt < (Int_t)grad_im->width; xt++)
1648  gVirtualPS->CellArrayFill(imdec->buffer.red[xt],
1649  imdec->buffer.green[xt],
1650  imdec->buffer.blue[xt]);
1651  }
1652  }
1653  stop_image_decoding(&imdec);
1654  gVirtualPS->CellArrayEnd();
1655 
1656  // values of palette
1657  TGaxis axis;
1658  Int_t ndiv = 510;
1659  double min = fMinValue;
1660  double max = fMaxValue;
1661  axis.SetLineColor(1); // draw black ticks
1662  Double_t pal_Xpos = gPad->AbsPixeltoX(pal_Ax + pal_w);
1663  axis.PaintAxis(pal_Xpos, gPad->AbsPixeltoY(pal_Ay + pal_h),
1664  pal_Xpos, gPad->AbsPixeltoY(pal_Ay + 1),
1665  min, max, ndiv, "+L");
1666 
1667  }
1668  }
1669 
1670  if (grad_im) {
1671  destroy_asimage(&grad_im);
1672  }
1673 }
1674 
1675 ////////////////////////////////////////////////////////////////////////////////
1676 /// Is the mouse in the image ?
1677 
1678 Int_t TASImage::DistancetoPrimitive(Int_t px, Int_t py)
1679 {
1680  Int_t pxl, pyl, pxt, pyt;
1681 
1682  Int_t px1 = gPad->XtoAbsPixel(0);
1683  Int_t py1 = gPad->YtoAbsPixel(0);
1684  Int_t px2 = gPad->XtoAbsPixel(1);
1685  Int_t py2 = gPad->YtoAbsPixel(1);
1686 
1687  if (px1 < px2) {pxl = px1; pxt = px2;}
1688  else {pxl = px2; pxt = px1;}
1689  if (py1 < py2) {pyl = py1; pyt = py2;}
1690  else {pyl = py2; pyt = py1;}
1691 
1692  if ((px > pxl && px < pxt) && (py > pyl && py < pyt))
1693  return 0;
1694 
1695  return 999999;
1696 }
1697 
1698 ////////////////////////////////////////////////////////////////////////////////
1699 /// Execute mouse events.
1700 
1701 void TASImage::ExecuteEvent(Int_t event, Int_t px, Int_t py)
1702 {
1703  static TBox *ZoomBox;
1704 
1705  if (!gPad) return;
1706 
1707  if (IsEditable()) {
1708  gPad->ExecuteEvent(event, px, py);
1709  return;
1710  }
1711 
1712  gPad->SetCursor(kCross);
1713 
1714  static Int_t px1old, py1old, px2old, py2old;
1715  static Int_t px1, py1, px2, py2, pxl, pyl, pxt, pyt;
1716 
1717  if (!IsValid()) return;
1718 
1719  if (event == kButton1Motion || event == kButton1Down ||
1720  event == kButton1Up) {
1721 
1722  // convert to image pixel on screen
1723  Int_t imgX = px - gPad->XtoAbsPixel(0);
1724  Int_t imgY = py - gPad->YtoAbsPixel(1);
1725 
1726  if (imgX < 0) px = px - imgX;
1727  if (imgY < 0) py = py - imgY;
1728 
1729  ASImage *image = fImage;
1730  if (fScaledImage) image = fScaledImage->fImage;
1731 
1732  if (imgX >= (int)image->width) px = px - imgX + image->width - 1;
1733  if (imgY >= (int)image->height) py = py - imgY + image->height - 1;
1734 
1735  switch (event) {
1736 
1737  case kButton1Down:
1738  px1 = gPad->XtoAbsPixel(gPad->GetX1());
1739  py1 = gPad->YtoAbsPixel(gPad->GetY1());
1740  px2 = gPad->XtoAbsPixel(gPad->GetX2());
1741  py2 = gPad->YtoAbsPixel(gPad->GetY2());
1742  px1old = px; py1old = py;
1743  break;
1744 
1745  case kButton1Motion:
1746  px2old = px;
1747  px2old = TMath::Max(px2old, px1);
1748  px2old = TMath::Min(px2old, px2);
1749  py2old = py;
1750  py2old = TMath::Max(py2old, py2);
1751  py2old = TMath::Min(py2old, py1);
1752  pxl = TMath::Min(px1old, px2old);
1753  pxt = TMath::Max(px1old, px2old);
1754  pyl = TMath::Max(py1old, py2old);
1755  pyt = TMath::Min(py1old, py2old);
1756 
1757  if (ZoomBox) {
1758  ZoomBox->SetX1(gPad->AbsPixeltoX(pxl));
1759  ZoomBox->SetY1(gPad->AbsPixeltoY(pyl));
1760  ZoomBox->SetX2(gPad->AbsPixeltoX(pxt));
1761  ZoomBox->SetY2(gPad->AbsPixeltoY(pyt));
1762  }
1763  else {
1764  ZoomBox = new TBox(pxl, pyl, pxt, pyt);
1765  ZoomBox->SetFillStyle(0);
1766  ZoomBox->Draw("l*");
1767  }
1768  gPad->Modified(kTRUE);
1769  gPad->Update();
1770  break;
1771 
1772  case kButton1Up:
1773  // do nothing if zoom area is too small
1774  if ( TMath::Abs(pxl - pxt) < 5 || TMath::Abs(pyl - pyt) < 5)
1775  return;
1776 
1777  pxl = 0;
1778  pxt = 0;
1779  pyl = 0;
1780  pyt = 0;
1781 
1782  Double_t xfact = (fScaledImage) ? (Double_t)fScaledImage->fImage->width / fZoomWidth : 1;
1783  Double_t yfact = (fScaledImage) ? (Double_t)fScaledImage->fImage->height / fZoomHeight : 1;
1784 
1785  Int_t imgX1 = px1old - gPad->XtoAbsPixel(0);
1786  Int_t imgY1 = py1old - gPad->YtoAbsPixel(1);
1787  Int_t imgX2 = px - gPad->XtoAbsPixel(0);
1788  Int_t imgY2 = py - gPad->YtoAbsPixel(1);
1789 
1790  imgY1 = image->height - 1 - imgY1;
1791  imgY2 = image->height - 1 - imgY2;
1792  imgX1 = (Int_t)(imgX1 / xfact) + fZoomOffX;
1793  imgY1 = (Int_t)(imgY1 / yfact) + fZoomOffY;
1794  imgX2 = (Int_t)(imgX2 / xfact) + fZoomOffX;
1795  imgY2 = (Int_t)(imgY2 / yfact) + fZoomOffY;
1796 
1797  Zoom((imgX1 < imgX2) ? imgX1 : imgX2, (imgY1 < imgY2) ? imgY1 : imgY2,
1798  TMath::Abs(imgX1 - imgX2) + 1, TMath::Abs(imgY1 - imgY2) + 1);
1799 
1800  if (ZoomBox) {
1801  ZoomBox->Delete();
1802  ZoomBox = 0;
1803  }
1804  gPad->Modified(kTRUE);
1805  gPad->Update();
1806  break;
1807  }
1808  }
1809 }
1810 
1811 ////////////////////////////////////////////////////////////////////////////////
1812 /// Get image pixel coordinates and the pixel value at the mouse pointer.
1813 
1814 char *TASImage::GetObjectInfo(Int_t px, Int_t py) const
1815 {
1816  static char info[64];
1817  info[0] = 0;
1818 
1819  if (!IsValid()) return info;
1820 
1821  // convert to image pixel on screen
1822  px -= gPad->XtoAbsPixel(0);
1823  py -= gPad->YtoAbsPixel(1);
1824 
1825  // no info if mouse is outside of image
1826  if (px < 0 || py < 0) return info;
1827 
1828  ASImage *image = fImage;
1829  if (fScaledImage) image = fScaledImage->fImage;
1830  if (px >= (int)image->width || py >= (int)image->height)
1831  return info;
1832 
1833  py = image->height - 1 - py;
1834  // convert to original image size and take zooming into account
1835  if (fScaledImage) {
1836  px = (Int_t)(px / (Double_t)fScaledImage->fImage->width * fZoomWidth ) + fZoomOffX;
1837  py = (Int_t)(py / (Double_t)fScaledImage->fImage->height * fZoomHeight) + fZoomOffY;
1838  }
1839 
1840  if (fImage->alt.vector) {
1841  snprintf(info,64,"x: %d y: %d %.5g",
1842  px, py, fImage->alt.vector[px + py * fImage->width]);
1843  } else {
1844  snprintf(info,64,"x: %d y: %d", px, py);
1845  }
1846 
1847  return info;
1848 }
1849 
1850 ////////////////////////////////////////////////////////////////////////////////
1851 /// Set a new palette to an image.
1852 /// Only images that were created with the SetImage() functions can be
1853 /// modified with this function. The previously used palette is destroyed.
1854 
1855 void TASImage::SetPalette(const TImagePalette *palette)
1856 {
1857  TAttImage::SetPalette(palette);
1858 
1859  if (!InitVisual()) {
1860  Warning("SetPalette", "Visual not initiated");
1861  return;
1862  }
1863 
1864  if (!IsValid()) {
1865  Warning("SetPalette", "Image not valid");
1866  return;
1867  }
1868 
1869  if (fImage->alt.vector == 0)
1870  return;
1871 
1872  // copy ROOT palette to asImage palette
1873  const TImagePalette &pal = GetPalette();
1874 
1875  ASVectorPalette asPalette;
1876  asPalette.npoints = pal.fNumPoints;
1877  asPalette.channels[0] = new CARD16 [asPalette.npoints];
1878  asPalette.channels[1] = new CARD16 [asPalette.npoints];
1879  asPalette.channels[2] = new CARD16 [asPalette.npoints];
1880  asPalette.channels[3] = new CARD16 [asPalette.npoints];
1881  memcpy(asPalette.channels[0], pal.fColorBlue, pal.fNumPoints * sizeof(UShort_t));
1882  memcpy(asPalette.channels[1], pal.fColorGreen, pal.fNumPoints * sizeof(UShort_t));
1883  memcpy(asPalette.channels[2], pal.fColorRed, pal.fNumPoints * sizeof(UShort_t));
1884  memcpy(asPalette.channels[3], pal.fColorAlpha, pal.fNumPoints * sizeof(UShort_t));
1885 
1886  asPalette.points = new double[asPalette.npoints];
1887  for (Int_t point = 0; point < Int_t(asPalette.npoints); point++)
1888  asPalette.points[point] = fMinValue + (fMaxValue - fMinValue) * pal.fPoints[point];
1889 
1890  // use the new palette in this image
1891  colorize_asimage_vector(fgVisual, fImage, &asPalette, ASA_ASImage, GetImageQuality());
1892 
1893  delete [] asPalette.points;
1894  for (Int_t col = 0; col < 4; col++)
1895  delete [] asPalette.channels[col];
1896 
1897 
1898  delete fScaledImage;
1899  fScaledImage = 0;
1900 }
1901 
1902 ////////////////////////////////////////////////////////////////////////////////
1903 /// Scale the original image.
1904 /// The size of the image on the screen does not change because it is defined
1905 /// by the size of the pad.
1906 /// This function can be used to change the size of an image before writing
1907 /// it into a file. The colors of the new pixels are interpolated.
1908 /// An image created with the SetImage() functions cannot be modified with
1909 /// the function SetPalette() any more after a call of this function!
1910 
1911 void TASImage::Scale(UInt_t toWidth, UInt_t toHeight)
1912 {
1913  if (!IsValid()) {
1914  Warning("Scale", "Image not initiated");
1915  return;
1916  }
1917 
1918  if (!InitVisual()) {
1919  Warning("Scale", "Visual not initiated");
1920  return;
1921  }
1922 
1923  if (toWidth < 1)
1924  toWidth = 1;
1925  if (toHeight < 1 )
1926  toHeight = 1;
1927  if (toWidth > 30000)
1928  toWidth = 30000;
1929  if (toHeight > 30000)
1930  toHeight = 30000;
1931 
1932  ASImage *img = scale_asimage(fgVisual, fImage, toWidth, toHeight,
1933  ASA_ASImage, GetImageCompression(),
1934  GetImageQuality());
1935  DestroyImage();
1936  fImage = img;
1937  UnZoom();
1938  fZoomUpdate = kZoomOps;
1939 }
1940 
1941 ////////////////////////////////////////////////////////////////////////////////
1942 /// Another method of enlarging images where corners remain unchanged,
1943 /// but middle part gets tiled.
1944 
1945 void TASImage::Slice(UInt_t xStart, UInt_t xEnd, UInt_t yStart, UInt_t yEnd,
1946  UInt_t toWidth, UInt_t toHeight)
1947 {
1948  if (!IsValid()) {
1949  Warning("Scale", "Image not initiated");
1950  return;
1951  }
1952 
1953  if (!InitVisual()) {
1954  Warning("Scale", "Visual not initiated");
1955  return;
1956  }
1957 
1958  if (toWidth < 1)
1959  toWidth = 1;
1960  if (toHeight < 1 )
1961  toHeight = 1;
1962  if (toWidth > 30000)
1963  toWidth = 30000;
1964  if (toHeight > 30000)
1965  toHeight = 30000;
1966 
1967  ASImage *img = slice_asimage(fgVisual, fImage, xStart, xEnd,
1968  yStart, yEnd, toWidth, toHeight,
1969  ASA_ASImage, GetImageCompression(),
1970  GetImageQuality());
1971 
1972  DestroyImage();
1973  fImage = img;
1974  UnZoom();
1975  fZoomUpdate = kZoomOps;
1976 }
1977 
1978 ////////////////////////////////////////////////////////////////////////////////
1979 /// Tile the original image.
1980 
1981 void TASImage::Tile(UInt_t toWidth, UInt_t toHeight)
1982 {
1983  if (!IsValid()) {
1984  Warning("Tile", "Image not initiated");
1985  return;
1986  }
1987 
1988  if (!InitVisual()) {
1989  Warning("Tile", "Visual not initiated");
1990  return;
1991  }
1992 
1993  if (toWidth < 1)
1994  toWidth = 1;
1995  if (toHeight < 1 )
1996  toHeight = 1;
1997  if (toWidth > 30000)
1998  toWidth = 30000;
1999  if (toHeight > 30000)
2000  toHeight = 30000;
2001 
2002  ASImage *img = tile_asimage(fgVisual, fImage, 0, 0, toWidth, toHeight, 0,
2003  ASA_ASImage, GetImageCompression(), GetImageQuality());
2004  DestroyImage();
2005  fImage = img;
2006  UnZoom();
2007  fZoomUpdate = kZoomOps;
2008 }
2009 
2010 ////////////////////////////////////////////////////////////////////////////////
2011 /// The area of an image displayed in a pad is defined by this function.
2012 /// Note: the size on the screen is defined by the size of the pad.
2013 /// The original image is not modified by this function.
2014 /// If width or height is larger than the original image they are reduced to
2015 /// the width and height of the image.
2016 /// If the off values are too large (off + width > image width) than the off
2017 /// values are decreased. For example: offX = image width - width
2018 /// Note: the parameters are always relative to the original image not to the
2019 /// size of an already zoomed image.
2020 
2021 void TASImage::Zoom(UInt_t offX, UInt_t offY, UInt_t width, UInt_t height)
2022 {
2023  if (!IsValid()) {
2024  Warning("Zoom", "Image not valid");
2025  return;
2026  }
2027  fZoomUpdate = kZoom;
2028 
2029  fZoomWidth = (width == 0) ? 1 : ((width > fImage->width) ? fImage->width : width);
2030  fZoomHeight = (height == 0) ? 1 : ((height > fImage->height) ? fImage->height : height);
2031  fZoomOffX = offX;
2032  if (fZoomOffX + fZoomWidth > fImage->width)
2033  fZoomOffX = fImage->width - fZoomWidth;
2034  fZoomOffY = offY;
2035  if (fZoomOffY + fZoomHeight > fImage->height)
2036  fZoomOffY = fImage->height - fZoomHeight;
2037 }
2038 
2039 ////////////////////////////////////////////////////////////////////////////////
2040 /// Un-zoom the image to original size.
2041 /// UnZoom() - performs undo for Zoom,Crop,Scale actions
2042 
2043 void TASImage::UnZoom()
2044 {
2045  if (!IsValid()) {
2046  Warning("UnZoom", "Image not valid");
2047  return;
2048  }
2049  fZoomUpdate = kZoom;
2050  fZoomOffX = 0;
2051  fZoomOffY = 0;
2052  fZoomWidth = fImage->width;
2053  fZoomHeight = fImage->height;
2054 
2055  delete fScaledImage;
2056  fScaledImage = 0;
2057 }
2058 
2059 ////////////////////////////////////////////////////////////////////////////////
2060 /// Flip image in place.
2061 ///
2062 /// Flip is either 90, 180, 270, 180 is default.
2063 /// This function manipulates the original image and destroys the
2064 /// scaled and zoomed image which will be recreated at the next call of
2065 /// the Draw function. If the image is zoomed the zoom - coordinates are
2066 /// now relative to the new image.
2067 /// This function cannot be used for images which were created with the
2068 /// SetImage() functions, because the original pixel values would be
2069 /// destroyed.
2070 
2071 void TASImage::Flip(Int_t flip)
2072 {
2073  if (!IsValid()) {
2074  Warning("Flip", "Image not valid");
2075  return;
2076  }
2077  if (!InitVisual()) {
2078  Warning("Flip", "Visual not initiated");
2079  return;
2080  }
2081 
2082  if (fImage->alt.vector) {
2083  Warning("Flip", "flip does not work for data images");
2084  return;
2085  }
2086 
2087  Int_t rflip = flip/90;
2088 
2089  UInt_t w = fImage->width;
2090  UInt_t h = fImage->height;
2091 
2092  if (rflip & 1) {
2093  w = fImage->height;
2094  h = fImage->width;
2095  }
2096 
2097  ASImage *img = flip_asimage(fgVisual, fImage, 0, 0, w, h, rflip,
2098  ASA_ASImage, GetImageCompression(),
2099  GetImageQuality());
2100  DestroyImage();
2101  fImage = img;
2102  UnZoom();
2103 }
2104 
2105 ////////////////////////////////////////////////////////////////////////////////
2106 /// Mirror image in place.
2107 ///
2108 /// If vert is true mirror in vertical axis, horizontal otherwise.
2109 /// Vertical is default.
2110 /// This function manipulates the original image and destroys the
2111 /// scaled and zoomed image which will be recreated at the next call of
2112 /// the Draw function. If the image is zoomed the zoom - coordinates are
2113 /// now relative to the new image.
2114 /// This function cannot be used for images which were created with the
2115 /// SetImage() functions, because the original pixel values would be
2116 /// destroyed.
2117 
2118 void TASImage::Mirror(Bool_t vert)
2119 {
2120  if (!IsValid()) {
2121  Warning("Mirror", "Image not valid");
2122  return;
2123  }
2124 
2125  if (!InitVisual()) {
2126  Warning("Mirror", "Visual not initiated");
2127  return;
2128  }
2129 
2130  if (fImage->alt.vector) {
2131  Warning("Mirror", "mirror does not work for data images");
2132  return;
2133  }
2134 
2135  ASImage *img = mirror_asimage(fgVisual, fImage, 0, 0,
2136  fImage->width, fImage->height, vert,
2137  ASA_ASImage, GetImageCompression(),
2138  GetImageQuality());
2139  DestroyImage();
2140  fImage = img;
2141  UnZoom();
2142 }
2143 
2144 ////////////////////////////////////////////////////////////////////////////////
2145 /// Return width of original image not of the displayed image.
2146 /// (Number of image pixels)
2147 
2148 UInt_t TASImage::GetWidth() const
2149 {
2150  return fImage ? fImage->width : 0;
2151 }
2152 
2153 ////////////////////////////////////////////////////////////////////////////////
2154 /// Return height of original image not of the displayed image.
2155 /// (Number of image pixels)
2156 
2157 UInt_t TASImage::GetHeight() const
2158 {
2159  return fImage ? fImage->height : 0;
2160 }
2161 
2162 ////////////////////////////////////////////////////////////////////////////////
2163 /// Return width of the displayed image not of the original image.
2164 /// (Number of screen pixels)
2165 
2166 UInt_t TASImage::GetScaledWidth() const
2167 {
2168  return fScaledImage ? fScaledImage->fImage->width : GetWidth();
2169 }
2170 
2171 ////////////////////////////////////////////////////////////////////////////////
2172 /// Return height of the displayed image not of the original image.
2173 /// (Number of screen pixels)
2174 
2175 UInt_t TASImage::GetScaledHeight() const
2176 {
2177  return fScaledImage ? fScaledImage->fImage->height : GetHeight();
2178 }
2179 
2180 ////////////////////////////////////////////////////////////////////////////////
2181 /// Return the zoom parameters.
2182 /// This is useful when the zoom has been done interactively using the mouse.
2183 
2184 void TASImage::GetZoomPosition(UInt_t &x, UInt_t &y, UInt_t &w, UInt_t &h) const
2185 {
2186  x = fZoomOffX;
2187  y = fZoomOffY;
2188  w = fZoomWidth;
2189  h = fZoomHeight;
2190 }
2191 
2192 ////////////////////////////////////////////////////////////////////////////////
2193 /// Static function to initialize the ASVisual.
2194 
2195 Bool_t TASImage::InitVisual()
2196 {
2197  Display *disp;
2198 
2199  Bool_t inbatch = fgVisual && (fgVisual->dpy == (void*)1); // was in batch
2200  Bool_t noX = gROOT->IsBatch() || gVirtualX->InheritsFrom("TGWin32");
2201 
2202  // was in batch, but switched to gui
2203  if (inbatch && !noX) {
2204  destroy_asvisual(fgVisual, kFALSE);
2205  fgVisual = 0;
2206  }
2207 
2208  if (fgVisual && fgVisual->dpy) { // already initialized
2209  return kTRUE;
2210  }
2211 
2212  // batch or win32 mode
2213  if (!fgVisual && noX) {
2214  disp = 0;
2215  fgVisual = create_asvisual(0, 0, 0, 0);
2216  fgVisual->dpy = (Display*)1; //fake (not used)
2217  return kTRUE;
2218  }
2219 
2220 #ifndef WIN32
2221 #ifdef R__HAS_COCOA
2222  fgVisual = create_asvisual(0, 0, 0, 0);
2223  fgVisual->dpy = (Display*)1; //fake (not used)
2224 #else
2225  disp = (Display*) gVirtualX->GetDisplay();
2226  Int_t screen = gVirtualX->GetScreen();
2227  Int_t depth = gVirtualX->GetDepth();
2228  Visual *vis = (Visual*) gVirtualX->GetVisual();
2229  Colormap cmap = (Colormap) gVirtualX->GetColormap();
2230 
2231  if (vis == 0 || cmap == 0) {
2232  fgVisual = create_asvisual(0, 0, 0, 0);
2233  } else {
2234  fgVisual = create_asvisual_for_id(disp, screen, depth,
2235  XVisualIDFromVisual(vis), cmap, 0);
2236  }
2237 #endif
2238 #else
2239  fgVisual = create_asvisual(0, 0, 0, 0);
2240  fgVisual->dpy = (Display*)1; //fake (not used)
2241 #endif
2242 
2243  return kTRUE;
2244 }
2245 
2246 ////////////////////////////////////////////////////////////////////////////////
2247 /// Start palette editor.
2248 
2249 void TASImage::StartPaletteEditor()
2250 {
2251  if (!IsValid()) {
2252  Warning("StartPaletteEditor", "Image not valid");
2253  return;
2254  }
2255  if (fImage->alt.vector == 0) {
2256  Warning("StartPaletteEditor", "palette can be modified only for data images");
2257  return;
2258  }
2259 
2260  // Opens a GUI to edit the color palette
2261  TAttImage::StartPaletteEditor();
2262 }
2263 
2264 ////////////////////////////////////////////////////////////////////////////////
2265 /// Returns image pixmap.
2266 /// The pixmap must deleted by user.
2267 
2268 Pixmap_t TASImage::GetPixmap()
2269 {
2270  if (!InitVisual()) {
2271  Warning("GetPixmap", "Visual not initiated");
2272  return 0;
2273  }
2274 
2275  Pixmap_t ret;
2276 
2277  ASImage *img = fScaledImage ? fScaledImage->fImage : fImage;
2278 
2279  static int x11 = -1;
2280  if (x11 < 0) x11 = gVirtualX->InheritsFrom("TGX11");
2281 
2282  if (x11) { // use builtin version
2283  ret = (Pixmap_t)asimage2pixmap(fgVisual, gVirtualX->GetDefaultRootWindow(),
2284  img, 0, kTRUE);
2285  } else {
2286  if (!fImage->alt.argb32) {
2287  BeginPaint();
2288  }
2289  ret = gVirtualX->CreatePixmapFromData((unsigned char*)fImage->alt.argb32,
2290  fImage->width, fImage->height);
2291  }
2292 
2293  return ret;
2294 }
2295 
2296 ////////////////////////////////////////////////////////////////////////////////
2297 /// Returns image mask pixmap (alpha channel).
2298 /// The pixmap must deleted by user.
2299 
2300 Pixmap_t TASImage::GetMask()
2301 {
2302  Pixmap_t pxmap = 0;
2303 
2304  if (!InitVisual()) {
2305  Warning("GetMask", "Visual not initiated");
2306  return pxmap;
2307  }
2308 
2309  ASImage *img = fScaledImage ? fScaledImage->fImage : fImage;
2310 
2311  if (!img) {
2312  Warning("GetMask", "No image");
2313  return pxmap;
2314  }
2315 
2316  UInt_t hh = img->height;
2317  UInt_t ow = img->width%8;
2318  UInt_t ww = img->width - ow + (ow ? 8 : 0);
2319 
2320  UInt_t bit = 0;
2321  int i = 0;
2322  UInt_t y = 0;
2323  UInt_t x = 0;
2324 
2325  char *bits = new char[ww*hh]; //an array of bits
2326 
2327  ASImageDecoder *imdec = start_image_decoding(fgVisual, img, SCL_DO_ALPHA,
2328  0, 0, ww, 0, 0);
2329  if (!imdec) {
2330  delete [] bits;
2331  return 0;
2332  }
2333 
2334  for (y = 0; y < hh; y++) {
2335  imdec->decode_image_scanline(imdec);
2336  CARD32 *a = imdec->buffer.alpha;
2337 
2338  for (x = 0; x < ww; x++) {
2339  if (a[x]) {
2340  SETBIT(bits[i], bit);
2341  } else {
2342  CLRBIT(bits[i], bit);
2343  }
2344  bit++;
2345  if (bit == 8) {
2346  bit = 0;
2347  i++;
2348  }
2349  }
2350  }
2351 
2352  stop_image_decoding(&imdec);
2353  pxmap = gVirtualX->CreateBitmap(gVirtualX->GetDefaultRootWindow(), (const char *)bits,
2354  ww, hh);
2355  delete [] bits;
2356  return pxmap;
2357 }
2358 
2359 ////////////////////////////////////////////////////////////////////////////////
2360 /// Create image from pixmap.
2361 
2362 void TASImage::SetImage(Pixmap_t pxm, Pixmap_t mask)
2363 {
2364  if (!InitVisual()) {
2365  Warning("SetImage", "Visual not initiated");
2366  return;
2367  }
2368 
2369  DestroyImage();
2370  delete fScaledImage;
2371  fScaledImage = 0;
2372 
2373  Int_t xy;
2374  UInt_t w, h;
2375  gVirtualX->GetWindowSize(pxm, xy, xy, w, h);
2376 
2377  if (fName.IsNull()) fName.Form("img_%dx%d",w, h);
2378 
2379  static int x11 = -1;
2380  if (x11 < 0) x11 = gVirtualX->InheritsFrom("TGX11");
2381 
2382  if (x11) { //use built-in optimized version
2383  fImage = picture2asimage(fgVisual, pxm, mask, 0, 0, w, h, kAllPlanes, 1, 0);
2384  } else {
2385  unsigned char *bits = gVirtualX->GetColorBits(pxm, 0, 0, w, h);
2386  if (!bits) { // error
2387  return;
2388  }
2389 
2390  // no mask
2391  if (!mask) {
2392  fImage = bitmap2asimage(bits, w, h, 0, 0);
2393  delete [] bits;
2394  return;
2395  }
2396  unsigned char *mask_bits = gVirtualX->GetColorBits(mask, 0, 0, w, h);
2397  fImage = bitmap2asimage(bits, w, h, 0, mask_bits);
2398  delete [] mask_bits;
2399  delete [] bits;
2400  }
2401 }
2402 
2403 ////////////////////////////////////////////////////////////////////////////////
2404 /// Return 2D array of machine dependent pixel values.
2405 
2406 TArrayL *TASImage::GetPixels(Int_t x, Int_t y, UInt_t width, UInt_t height)
2407 {
2408  if (!fImage) {
2409  Warning("GetPixels", "Wrong Image");
2410  return 0;
2411  }
2412 
2413  ASImage *img = fScaledImage ? fScaledImage->fImage : fImage;
2414  ASImageDecoder *imdec;
2415 
2416  width = !width ? img->width : width;
2417  height = !height ? img->height : height;
2418 
2419  if (x < 0) {
2420  width -= x;
2421  x = 0 ;
2422  }
2423  if (y < 0) {
2424  height -= y;
2425  y = 0;
2426  }
2427 
2428  if ((x >= (int)img->width) || (y >= (int)img->height)) {
2429  return 0;
2430  }
2431 
2432  if ((int)(x + width) > (int)img->width) {
2433  width = img->width - x;
2434  }
2435 
2436  if ((int)(y + height) > (int)img->height) {
2437  height = img->height - y;
2438  }
2439 
2440  if ((imdec = start_image_decoding(0, fImage, SCL_DO_ALL, 0, y,
2441  img->width, height, 0)) == 0) {
2442  Warning("GetPixels", "Failed to create image decoder");
2443  return 0;
2444  }
2445 
2446  TArrayL *ret = new TArrayL(width * height);
2447  Int_t r = 0;
2448  Int_t g = 0;
2449  Int_t b = 0;
2450  Long_t p = 0;
2451 
2452  for (UInt_t k = 0; k < height; k++) {
2453  imdec->decode_image_scanline(imdec);
2454 
2455  for (UInt_t i = 0; i < width; ++i) {
2456  if ((r == (Int_t)imdec->buffer.red[i]) &&
2457  (g == (Int_t)imdec->buffer.green[i]) &&
2458  (b == (Int_t)imdec->buffer.blue[i])) {
2459  } else {
2460  r = (Int_t)imdec->buffer.red[i];
2461  g = (Int_t)imdec->buffer.green[i];
2462  b = (Int_t)imdec->buffer.blue[i];
2463  p = (Long_t)TColor::RGB2Pixel(r, g, b);
2464  }
2465  ret->AddAt(p, k*width + i);
2466  }
2467  }
2468 
2469  stop_image_decoding(&imdec);
2470  return ret;
2471 }
2472 
2473 ////////////////////////////////////////////////////////////////////////////////
2474 /// Return a pointer to internal array[width x height] of double values [0,1].
2475 /// This array is directly accessible. That allows to manipulate/change the
2476 /// image.
2477 
2478 Double_t *TASImage::GetVecArray()
2479 {
2480  if (!fImage) {
2481  Warning("GetVecArray", "Bad Image");
2482  return 0;
2483  }
2484  if (fImage->alt.vector) {
2485  return fImage->alt.vector;
2486  }
2487  // vectorize
2488  return 0;
2489 }
2490 
2491 ////////////////////////////////////////////////////////////////////////////////
2492 /// In case of vectorized image return an associated array of doubles
2493 /// otherwise this method creates and returns a 2D array of doubles corresponding to palette.
2494 /// If palette is ZERO a color converted to double value [0, 1] according to formula
2495 /// ~~~ {.cpp}
2496 /// Double_t((r << 16) + (g << 8) + b)/0xFFFFFF
2497 /// ~~~
2498 /// The returned array must be deleted after usage.
2499 
2500 TArrayD *TASImage::GetArray(UInt_t w, UInt_t h, TImagePalette *palette)
2501 {
2502  if (!fImage) {
2503  Warning("GetArray", "Bad Image");
2504  return 0;
2505  }
2506 
2507  TArrayD *ret;
2508 
2509  if (fImage->alt.vector) {
2510  ret = new TArrayD(fImage->width*fImage->height, fImage->alt.vector);
2511  return ret;
2512  }
2513 
2514  ASImageDecoder *imdec;
2515 
2516  w = w ? w : fImage->width;
2517  h = h ? h : fImage->height;
2518 
2519  if ((fImage->width != w) || (fImage->height != h)) {
2520  Scale(w, h);
2521  }
2522 
2523  ASImage *img = fScaledImage ? fScaledImage->fImage : fImage;
2524 
2525  if ((imdec = start_image_decoding(0, img, SCL_DO_ALL, 0, 0,
2526  img->width, 0, 0)) == 0) {
2527  Warning("GetArray", "Failed to create image decoder");
2528  return 0;
2529  }
2530 
2531  ret = new TArrayD(w * h);
2532  CARD32 r = 0;
2533  CARD32 g = 0;
2534  CARD32 b = 0;
2535  Int_t p = 0;
2536  Double_t v = 0;
2537 
2538  for (UInt_t k = 0; k < h; k++) {
2539  imdec->decode_image_scanline(imdec);
2540 
2541  for (UInt_t i = 0; i < w; ++i) {
2542  if ((r == imdec->buffer.red[i]) &&
2543  (g == imdec->buffer.green[i]) &&
2544  (b == imdec->buffer.blue[i])) {
2545  } else {
2546  r = imdec->buffer.red[i];
2547  g = imdec->buffer.green[i];
2548  b = imdec->buffer.blue[i];
2549  if (palette) p = palette->FindColor(r, g, b);
2550  }
2551  v = palette ? palette->fPoints[p] : Double_t((r << 16) + (g << 8) + b)/0xFFFFFF;
2552  ret->AddAt(v, (h-k-1)*w + i);
2553  }
2554  }
2555 
2556  stop_image_decoding(&imdec);
2557  return ret;
2558 }
2559 
2560 ////////////////////////////////////////////////////////////////////////////////
2561 /// Draw text of size (in pixels for TrueType fonts)
2562 /// at position (x, y) with color specified by hex string.
2563 ///
2564 /// - font_name: TrueType font's filename or X font spec or alias.
2565 /// 3D style of text is one of the following:
2566 /// * 0 plain 2D text,
2567 /// * 1 embossed,
2568 /// * 2 sunken,
2569 /// * 3 shade above,
2570 /// * 4 shade below,
2571 /// * 5 embossed thick,
2572 /// * 6 sunken thick.
2573 /// * 7 outline above,
2574 /// * 8 ouline below,
2575 /// * 9 full ouline.
2576 /// - fore_file specifies foreground texture of text.
2577 
2578 void TASImage::DrawText(Int_t x, Int_t y, const char *text, Int_t size,
2579  const char *color, const char *font_name,
2580  EText3DType type, const char *fore_file, Float_t angle)
2581 {
2582  UInt_t width=0, height=0;
2583  ARGB32 text_color = ARGB32_Black;
2584  ASImage *fore_im = 0;
2585  ASImage *text_im = 0;
2586  Bool_t ttfont = kFALSE;
2587 
2588  if (!InitVisual()) {
2589  Warning("DrawText", "Visual not initiated");
2590  return;
2591  }
2592 
2593  TString fn = font_name;
2594  fn.Strip();
2595 
2596  // This is for backward compatibility...
2597  if (fn.Last('/') == 0) fn = fn(1, fn.Length() - 1);
2598 
2599  const char *ttpath = gEnv->GetValue("Root.TTFontPath",
2600  TROOT::GetTTFFontDir());
2601  char *tmpstr = gSystem->Which(ttpath, fn, kReadPermission);
2602  fn = tmpstr;
2603  delete [] tmpstr;
2604 
2605  if (fn.EndsWith(".pfa") || fn.EndsWith(".PFA") || fn.EndsWith(".pfb") || fn.EndsWith(".PFB") || fn.EndsWith(".ttf") || fn.EndsWith(".TTF") || fn.EndsWith(".otf") || fn.EndsWith(".OTF")) {
2606  ttfont = kTRUE;
2607  }
2608 
2609  if (color) {
2610  parse_argb_color(color, &text_color);
2611  }
2612 
2613  if (fImage && fImage->alt.argb32 && ttfont) {
2614  DrawTextTTF(x, y, text, size, text_color, fn.Data(), angle);
2615  return;
2616  }
2617 
2618  if (!gFontManager) {
2619  gFontManager = create_font_manager(fgVisual->dpy, 0, 0);
2620  }
2621 
2622  if (!gFontManager) {
2623  Warning("DrawText", "cannot create Font Manager");
2624  return;
2625  }
2626 
2627  ASFont *font = get_asfont(gFontManager, fn.Data(), 0, size, ASF_GuessWho);
2628 
2629  if (!font) {
2630  font = get_asfont(gFontManager, "fixed", 0, size, ASF_GuessWho);
2631  if (!font) {
2632  Warning("DrawText", "cannot find a font %s", font_name);
2633  return;
2634  }
2635  }
2636 
2637  get_text_size(text, font, (ASText3DType)type, &width, &height);
2638 
2639  if (!fImage) {
2640  fImage = create_asimage(width, height, 0);
2641  fill_asimage(fgVisual, fImage, 0, 0, width, height, 0xFFFFFFFF);
2642  }
2643 
2644  text_im = draw_text(text, font, (ASText3DType)type, 0);
2645 
2646  ASImage *rimg = fImage;
2647 
2648  if (fore_file) {
2649  ASImage *tmp = file2ASImage(fore_file, 0xFFFFFFFF, SCREEN_GAMMA, 0, 0);
2650  if (tmp) {
2651  if ((tmp->width != width) || (tmp->height != height)) {
2652  fore_im = tile_asimage(fgVisual, tmp, 0, 0, width, height, 0,
2653  ASA_ASImage, GetImageCompression(), GetImageQuality());
2654  }
2655  destroy_asimage(&tmp);
2656  } else {
2657  fore_im = tmp;
2658  }
2659  }
2660 
2661  if (fore_im) {
2662  move_asimage_channel(fore_im, IC_ALPHA, text_im, IC_ALPHA);
2663  destroy_asimage(&text_im);
2664  } else {
2665  fore_im = text_im ;
2666  }
2667 
2668  release_font(font);
2669 
2670  if (fore_im) {
2671  ASImage *rendered_im;
2672  ASImageLayer layers[2];
2673 
2674  init_image_layers(&(layers[0]), 2);
2675  fore_im->back_color = text_color;
2676  layers[0].im = rimg;
2677  layers[0].dst_x = 0;
2678  layers[0].dst_y = 0;
2679  layers[0].clip_width = rimg->width;
2680  layers[0].clip_height = rimg->height;
2681  layers[0].bevel = 0;
2682  layers[1].im = fore_im;
2683  layers[1].dst_x = x;
2684  layers[1].dst_y = y;
2685  layers[1].clip_width = fore_im->width;
2686  layers[1].clip_height = fore_im->height;
2687 
2688  rendered_im = merge_layers(fgVisual, &(layers[0]), 2, rimg->width, rimg->height,
2689  ASA_ASImage, GetImageCompression(), GetImageQuality());
2690 
2691  destroy_asimage(&fore_im);
2692  DestroyImage();
2693  fImage = rendered_im;
2694  UnZoom();
2695  }
2696 }
2697 
2698 ////////////////////////////////////////////////////////////////////////////////
2699 /// Merge two images.
2700 ///
2701 /// op is string which specifies overlay operation. Supported operations are:
2702 ///
2703 /// - add - color addition with saturation
2704 /// - alphablend - alpha-blending
2705 /// - allanon - color values averaging
2706 /// - colorize - hue and saturate bottom image same as top image
2707 /// - darken - use lowest color value from both images
2708 /// - diff - use absolute value of the color difference between two images
2709 /// - dissipate - randomly alpha-blend images
2710 /// - hue - hue bottom image same as top image
2711 /// - lighten - use highest color value from both images
2712 /// - overlay - some weird image overlaying(see GIMP)
2713 /// - saturate - saturate bottom image same as top image
2714 /// - screen - another weird image overlaying(see GIMP)
2715 /// - sub - color substraction with saturation
2716 /// - tint - tinting image with image
2717 /// - value - value bottom image same as top image
2718 
2719 void TASImage::Merge(const TImage *im, const char *op, Int_t x, Int_t y)
2720 {
2721  if (!im) return;
2722 
2723  if (!InitVisual()) {
2724  Warning("Merge", "Visual not initiated");
2725  return;
2726  }
2727 
2728  ASImage *rendered_im;
2729  ASImageLayer layers[2];
2730 
2731  init_image_layers(&(layers[0]), 2);
2732  layers[0].im = fImage;
2733  layers[0].dst_x = 0;
2734  layers[0].dst_y = 0;
2735  layers[0].clip_width = fImage->width;
2736  layers[0].clip_height = fImage->height;
2737  layers[0].bevel = 0;
2738  layers[1].im = ((TASImage*)im)->fImage;
2739  layers[1].dst_x = x;
2740  layers[1].dst_y = y;
2741  layers[1].clip_width = im->GetWidth();
2742  layers[1].clip_height = im->GetHeight();
2743  layers[1].merge_scanlines = blend_scanlines_name2func(op ? op : "add");
2744 
2745  rendered_im = merge_layers(fgVisual, &(layers[0]), 2, fImage->width, fImage->height,
2746  ASA_ASImage, GetImageCompression(), GetImageQuality());
2747 
2748  DestroyImage();
2749  fImage = rendered_im;
2750  UnZoom();
2751 }
2752 
2753 ////////////////////////////////////////////////////////////////////////////////
2754 /// Perform Gaussian blur of the image (useful for drop shadows).
2755 /// - hr - horizontal radius of the blur
2756 /// - vr - vertical radius of the blur
2757 
2758 void TASImage::Blur(Double_t hr, Double_t vr)
2759 {
2760  if (!InitVisual()) {
2761  Warning("Blur", "Visual not initiated");
2762  return;
2763  }
2764 
2765  if (!fImage) {
2766  fImage = create_asimage(100, 100, 0);
2767 
2768  if (!fImage) {
2769  Warning("Blur", "Failed to create image");
2770  return;
2771  }
2772 
2773  fill_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height, ARGB32_White);
2774  }
2775 
2776  ASImage *rendered_im = blur_asimage_gauss(fgVisual, fImage, hr > 0 ? hr : 3,
2777  vr > 0 ? vr : 3, SCL_DO_ALL,
2778  ASA_ASImage, GetImageCompression(), GetImageQuality());
2779  DestroyImage();
2780  fImage = rendered_im;
2781  UnZoom();
2782 }
2783 
2784 ////////////////////////////////////////////////////////////////////////////////
2785 /// Clone image.
2786 
2787 TObject *TASImage::Clone(const char *newname) const
2788 {
2789  if (!InitVisual() || !fImage) {
2790  Warning("Clone", "Image not initiated");
2791  return 0;
2792  }
2793 
2794  TASImage *im = (TASImage*)TImage::Create();
2795 
2796  if (!im) {
2797  Warning("Clone", "Failed to create image");
2798  return 0;
2799  }
2800 
2801  im->SetName(newname);
2802 
2803  im->fImage = clone_asimage(fImage, SCL_DO_ALL);
2804  im->fMaxValue = fMaxValue;
2805  im->fMinValue = fMinValue;
2806  im->fZoomOffX = fZoomOffX;
2807  im->fZoomOffY = fZoomOffY;
2808  im->fZoomWidth = fZoomWidth;
2809  im->fZoomHeight = fZoomHeight;
2810  im->fZoomUpdate = fZoomUpdate;
2811  im->fScaledImage = fScaledImage ? (TASImage*)fScaledImage->Clone("") : 0;
2812 
2813  if (fImage->alt.argb32) {
2814  UInt_t sz = fImage->width * fImage->height;
2815  im->fImage->alt.argb32 = (ARGB32*)safemalloc(sz*sizeof(ARGB32));
2816  memcpy(im->fImage->alt.argb32, fImage->alt.argb32, sz * sizeof(ARGB32));
2817  }
2818 
2819  return im;
2820 }
2821 
2822 ////////////////////////////////////////////////////////////////////////////////
2823 /// Reduce color-depth of an image and fills vector of "scientific data"
2824 /// [0...1]
2825 ///
2826 /// Colors are reduced by allocating color cells to most used colors first,
2827 /// and then approximating other colors with those allocated.
2828 ///
2829 /// \param[in] max_colors - maximum size of the colormap.
2830 /// \param[in] dither - number of bits to strip off the color data ( 0...7 )
2831 /// \param[in] opaque_threshold - alpha channel threshold at which pixel should be treated as opaque
2832 
2833 Double_t *TASImage::Vectorize(UInt_t max_colors, UInt_t dither, Int_t opaque_threshold)
2834 {
2835  if (!InitVisual()) {
2836  Warning("Vectorize", "Visual not initiated");
2837  return 0;
2838  }
2839 
2840  if (!fImage) {
2841  fImage = create_asimage(100, 100, 0);
2842 
2843  if (!fImage) {
2844  Warning("Vectorize", "Failed to create image");
2845  return 0;
2846  }
2847 
2848  fill_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height, ARGB32_White);
2849  }
2850 
2851  ASColormap cmap;
2852  int *res;
2853  UInt_t r=0, g=0, b=0;
2854 
2855  dither = dither > 7 ? 7 : dither;
2856 
2857  res = colormap_asimage(fImage, &cmap, max_colors, dither, opaque_threshold);
2858 
2859  Double_t *vec = new Double_t[fImage->height*fImage->width];
2860  UInt_t v;
2861  Double_t tmp;
2862  fMinValue = 2;
2863  fMaxValue = -1;
2864 
2865  for (UInt_t y = 0; y < fImage->height; y++) {
2866  for (UInt_t x = 0; x < fImage->width; x++) {
2867  int i = y*fImage->width + x;
2868  if (res) {
2869  g = INDEX_SHIFT_GREEN(cmap.entries[res[i]].green);
2870  b = INDEX_SHIFT_BLUE(cmap.entries[res[i]].blue);
2871  r = INDEX_SHIFT_RED(cmap.entries[res[i]].red);
2872  }
2873  v = MAKE_INDEXED_COLOR24(r,g,b);
2874  v = (v>>12)&0x0FFF;
2875  tmp = Double_t(v)/0x0FFF;
2876  vec[(fImage->height - y - 1)*fImage->width + x] = tmp;
2877  if (fMinValue > tmp) fMinValue = tmp;
2878  if (fMaxValue < tmp) fMaxValue = tmp;
2879  }
2880  }
2881  TImagePalette *pal = new TImagePalette(cmap.count);
2882 
2883  for (UInt_t j = 0; j < cmap.count; j++) {
2884  g = INDEX_SHIFT_GREEN(cmap.entries[j].green);
2885  b = INDEX_SHIFT_BLUE(cmap.entries[j].blue);
2886  r = INDEX_SHIFT_RED(cmap.entries[j].red);
2887  v = MAKE_INDEXED_COLOR24(r,g,b);
2888 
2889  v = (v>>12) & 0x0FFF;
2890  pal->fPoints[j] = Double_t(v)/0x0FFF;
2891 
2892  pal->fColorRed[j] = cmap.entries[j].red << 8;
2893  pal->fColorGreen[j] = cmap.entries[j].green << 8;
2894  pal->fColorBlue[j] = cmap.entries[j].blue << 8;
2895  pal->fColorAlpha[j] = 0xFF00;
2896  }
2897 
2898  destroy_colormap(&cmap, kTRUE);
2899 
2900  fPalette = *pal;
2901  fImage->alt.vector = vec;
2902  UnZoom();
2903  // ROOT-7647: res is allocated with `safemalloc` by colormap_asimage
2904  if (res) safefree(res);
2905  return (Double_t*)fImage->alt.vector;
2906 }
2907 
2908 ////////////////////////////////////////////////////////////////////////////////
2909 /// This function will tile original image to specified size with offsets
2910 /// requested, and then it will go though it and adjust hue, saturation and
2911 /// value of those pixels that have specific hue, set by affected_hue/
2912 /// affected_radius parameters. When affected_radius is greater then 180
2913 /// entire image will be adjusted. Note that since grayscale colors have
2914 /// no hue - the will not get adjusted. Only saturation and value will be
2915 /// adjusted in gray pixels.
2916 ///
2917 /// Hue is measured as an angle on a 360 degree circle, The following is
2918 /// relationship of hue values to regular color names :
2919 /// - red - 0
2920 /// - yellow - 60
2921 /// - green - 120
2922 /// - cyan - 180
2923 /// - blue - 240
2924 /// - magenta - 300
2925 /// - red - 360
2926 ///
2927 /// All the hue values in parameters will be adjusted to fall within 0-360 range.
2928 ///
2929 /// \param[in] hue hue in degrees in range 0-360. This allows to limit
2930 /// impact of color adjustment to affect only limited range of hues.
2931 ///
2932 /// \param[in] radius value in degrees to be used in order to
2933 /// calculate the range of affected hues. Range is determined by
2934 /// substracting and adding this value from/to affected_hue.
2935 ///
2936 /// \param[in] H value by which to change hues in affected range.
2937 /// \param[in] S value by which to change saturation of the pixels in affected hue range.
2938 /// \param[in] V value by which to change Value(brightness) of pixels in affected hue range.
2939 ///
2940 /// \param[in] x,y position on infinite surface tiled with original image, of the
2941 /// left-top corner of the area to be used for new image.
2942 ///
2943 /// \param[in] width, height size of the area of the original image to be used for new image.
2944 /// Default is current width, height of the image.
2945 
2946 void TASImage::HSV(UInt_t hue, UInt_t radius, Int_t H, Int_t S, Int_t V,
2947  Int_t x, Int_t y, UInt_t width, UInt_t height)
2948 {
2949  if (!InitVisual()) {
2950  Warning("HSV", "Visual not initiated");
2951  return;
2952  }
2953 
2954  if (!fImage) {
2955  fImage = create_asimage(width ? width : 20, height ? height : 20, 0);
2956 
2957  if (!fImage) {
2958  Warning("HSV", "Failed to create image");
2959  return;
2960  }
2961 
2962  x = 0;
2963  y = 0;
2964  fill_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height, ARGB32_White);
2965  }
2966 
2967  width = !width ? fImage->width : width;
2968  height = !height ? fImage->height : height;
2969 
2970  ASImage *rendered_im = 0;
2971 
2972  if (H || S || V) {
2973  rendered_im = adjust_asimage_hsv(fgVisual, fImage, x, y, width, height,
2974  hue, radius, H, S, V, ASA_ASImage, 100,
2975  ASIMAGE_QUALITY_TOP);
2976  }
2977  if (!rendered_im) {
2978  Warning("HSV", "Failed to create rendered image");
2979  return;
2980  }
2981 
2982  DestroyImage();
2983  fImage = rendered_im;
2984  UnZoom();
2985 }
2986 
2987 ////////////////////////////////////////////////////////////////////////////////
2988 /// Render multipoint gradient inside rectangle of size (width, height)
2989 /// at position (x,y) within the existing image.
2990 ///
2991 /// \param[in] angle Given in degrees. Default is 0. This is the
2992 /// direction of the gradient. Currently the only supported
2993 /// values are 0, 45, 90, 135, 180, 225, 270, 315. 0 means left
2994 /// to right, 90 means top to bottom, etc.
2995 ///
2996 /// \param[in] colors Whitespace-separated list of colors. At least two
2997 /// colors are required. Each color in this list will be visited
2998 /// in turn, at the intervals given by the offsets attribute.
2999 ///
3000 /// \param[in] offsets Whitespace-separated list of floating point values
3001 /// ranging from 0.0 to 1.0. The colors from the colors attribute
3002 /// are given these offsets, and the final gradient is rendered
3003 /// from the combination of the two. If both colors and offsets
3004 /// are given but the number of colors and offsets do not match,
3005 /// the minimum of the two will be used, and the other will be
3006 /// truncated to match. If offsets are not given, a smooth
3007 /// stepping from 0.0 to 1.0 will be used.
3008 
3009 void TASImage::Gradient(UInt_t angle, const char *colors, const char *offsets,
3010  Int_t x, Int_t y, UInt_t width, UInt_t height)
3011 {
3012  if (!InitVisual()) {
3013  Warning("Gradient", "Visual not initiated");
3014  return;
3015  }
3016 
3017  ASImage *rendered_im = 0;
3018  ASGradient gradient;
3019 
3020  int reverse = 0, npoints1 = 0, npoints2 = 0;
3021  char *p;
3022  char *pb;
3023  char ch;
3024  TString str = colors;
3025  TString col;
3026 
3027  if ((angle > 2 * 180 * 15 / 16) || (angle < 2 * 180 * 1 / 16)) {
3028  gradient.type = GRADIENT_Left2Right;
3029  } else if (angle < 2 * 180 * 3 / 16) {
3030  gradient.type = GRADIENT_TopLeft2BottomRight;
3031  } else if (angle < 2 * 180 * 5 / 16) {
3032  gradient.type = GRADIENT_Top2Bottom;
3033  } else if (angle < 2 * 180 * 7 / 16) {
3034  gradient.type = GRADIENT_BottomLeft2TopRight; reverse = 1;
3035  } else if (angle < 2 * 180 * 9 / 16) {
3036  gradient.type = GRADIENT_Left2Right; reverse = 1;
3037  } else if (angle < 2 * 180 * 11 / 16) {
3038  gradient.type = GRADIENT_TopLeft2BottomRight; reverse = 1;
3039  } else if (angle < 2 * 180 * 13 / 16) {
3040  gradient.type = GRADIENT_Top2Bottom; reverse = 1;
3041  } else {
3042  gradient.type = GRADIENT_BottomLeft2TopRight;
3043  }
3044 
3045  for (p = (char*)colors; isspace((int)*p); p++) { }
3046 
3047  for (npoints1 = 0; *p; npoints1++) {
3048  if (*p) {
3049  for ( ; *p && !isspace((int)*p); p++) { }
3050  }
3051  for ( ; isspace((int)*p); p++) { }
3052  }
3053  if (offsets) {
3054  for (p = (char*)offsets; isspace((int)*p); p++) { }
3055 
3056  for (npoints2 = 0; *p; npoints2++) {
3057  if (*p) {
3058  for ( ; *p && !isspace((int)*p); p++) { }
3059  }
3060  for ( ; isspace((int)*p); p++) { }
3061  }
3062  }
3063  if (npoints1 > 1) {
3064  int i;
3065  if (offsets && (npoints1 > npoints2)) npoints1 = npoints2;
3066 
3067  if (!width) {
3068  width = fImage ? fImage->width : 20;
3069  }
3070  if (!height) {
3071  height = fImage ? fImage->height : 20;
3072  }
3073 
3074  gradient.color = new ARGB32[npoints1];
3075  gradient.offset = new double[npoints1];
3076 
3077  for (p = (char*)colors; isspace((int)*p); p++) { }
3078 
3079  for (npoints1 = 0; *p; ) {
3080  pb = p;
3081 
3082  if (*p) {
3083  for ( ; *p && !isspace((int)*p); p++) { }
3084  }
3085  for ( ; isspace((int)*p); p++) { }
3086 
3087  col = str(pb - colors, p - pb);
3088 
3089  if (parse_argb_color(col.Data(), gradient.color + npoints1) != col) {
3090  npoints1++;
3091  } else {
3092  Warning("Gradient", "Failed to parse color [%s] - defaulting to black", pb);
3093  }
3094  }
3095 
3096  if (offsets) {
3097  for (p = (char*)offsets; isspace((int)*p); p++) { }
3098 
3099  for (npoints2 = 0; *p; ) {
3100  pb = p;
3101 
3102  if (*p) {
3103  for ( ; *p && !isspace((int)*p); p++) { }
3104  }
3105  ch = *p; *p = '\0';
3106  gradient.offset[npoints2] = strtod(pb, &pb);
3107 
3108  if (pb == p) npoints2++;
3109  *p = ch;
3110  for ( ; isspace((int)*p); p++) { }
3111  }
3112  } else {
3113  for (npoints2 = 0; npoints2 < npoints1; npoints2++) {
3114  gradient.offset[npoints2] = (double)npoints2 / (npoints1 - 1);
3115  }
3116  }
3117  gradient.npoints = npoints1;
3118 
3119  if (npoints2 && (gradient.npoints > npoints2)) {
3120  gradient.npoints = npoints2;
3121  }
3122  if (reverse) {
3123  for (i = 0; i < gradient.npoints/2; i++) {
3124  int i2 = gradient.npoints - 1 - i;
3125  ARGB32 c = gradient.color[i];
3126  double o = gradient.offset[i];
3127  gradient.color[i] = gradient.color[i2];
3128  gradient.color[i2] = c;
3129  gradient.offset[i] = gradient.offset[i2];
3130  gradient.offset[i2] = o;
3131  }
3132  for (i = 0; i < gradient.npoints; i++) {
3133  gradient.offset[i] = 1.0 - gradient.offset[i];
3134  }
3135  }
3136  rendered_im = make_gradient(fgVisual, &gradient, width, height, SCL_DO_ALL,
3137  ASA_ASImage, GetImageCompression(), GetImageQuality());
3138 
3139  delete [] gradient.color;
3140  delete [] gradient.offset;
3141  }
3142 
3143  if (!rendered_im) { // error
3144  Warning("Gradient", "Failed to create gradient image");
3145  return;
3146  }
3147 
3148  if (!fImage) {
3149  fImage = rendered_im;
3150  return;
3151  }
3152 
3153  ASImageLayer layers[2];
3154 
3155  init_image_layers(&(layers[0]), 2);
3156  layers[0].im = fImage;
3157  layers[0].dst_x = 0;
3158  layers[0].dst_y = 0;
3159  layers[0].clip_width = fImage->width;
3160  layers[0].clip_height = fImage->height;
3161  layers[0].bevel = 0;
3162  layers[1].im = rendered_im;
3163  layers[1].dst_x = x;
3164  layers[1].dst_y = y;
3165  layers[1].clip_width = width;
3166  layers[1].clip_height = height;
3167  layers[1].merge_scanlines = alphablend_scanlines;
3168 
3169  ASImage *merge_im = merge_layers(fgVisual, &(layers[0]), 2, fImage->width, fImage->height,
3170  ASA_ASImage, GetImageCompression(), GetImageQuality());
3171  if (!merge_im) {
3172  Warning("Gradient", "Failed to create merged image");
3173  return;
3174  }
3175 
3176  destroy_asimage(&rendered_im);
3177  DestroyImage();
3178  fImage = merge_im;
3179  UnZoom();
3180 }
3181 
3182 ////////////////////////////////////////////////////////////////////////////////
3183 /// Make component hilite.
3184 /// (used internally)
3185 
3186 static CARD8 MakeComponentHilite(int cmp)
3187 {
3188  if (cmp < 51) {
3189  cmp = 51;
3190  }
3191  cmp = (cmp * 12) / 10;
3192 
3193  return (cmp > 255) ? 255 : cmp;
3194 }
3195 
3196 ////////////////////////////////////////////////////////////////////////////////
3197 /// Calculate highlite color.
3198 /// (used internally)
3199 
3200 static ARGB32 GetHilite(ARGB32 background)
3201 {
3202  return ((MakeComponentHilite((background>>24) & 0x000000FF) << 24) & 0xFF000000) |
3203  ((MakeComponentHilite((background & 0x00FF0000) >> 16) << 16) & 0x00FF0000) |
3204  ((MakeComponentHilite((background & 0x0000FF00) >> 8) << 8) & 0x0000FF00) |
3205  ((MakeComponentHilite((background & 0x000000FF))) & 0x000000FF);
3206 }
3207 
3208 ////////////////////////////////////////////////////////////////////////////////
3209 /// Calculate shadow color.
3210 /// (used internally)
3211 
3212 static ARGB32 GetShadow(ARGB32 background)
3213 {
3214  return (background >> 1) & 0x7F7F7F7F;
3215 }
3216 
3217 ////////////////////////////////////////////////////////////////////////////////
3218 /// Get average.
3219 /// (used internally)
3220 
3221 static ARGB32 GetAverage(ARGB32 foreground, ARGB32 background)
3222 {
3223  CARD16 a, r, g, b;
3224 
3225  a = ARGB32_ALPHA8(foreground) + ARGB32_ALPHA8(background);
3226  a = (a<<3)/10;
3227  r = ARGB32_RED8(foreground) + ARGB32_RED8(background);
3228  r = (r<<3)/10;
3229  g = ARGB32_GREEN8(foreground) + ARGB32_GREEN8(background);
3230  g = (g<<3)/10;
3231  b = ARGB32_BLUE8(foreground) + ARGB32_BLUE8(background);
3232  b = (b<<3)/10;
3233 
3234  return MAKE_ARGB32(a, r, g, b);
3235 }
3236 
3237 
3238 ////////////////////////////////////////////////////////////////////////////////
3239 /// Bevel is used to create 3D effect while drawing buttons, or any other
3240 /// image that needs to be framed. Bevel is drawn using 2 primary colors:
3241 /// one for top and left sides - hi color, and another for bottom and
3242 /// right sides - low color. Bevel can be drawn over existing image or
3243 /// as newly created, as it is shown in code below:
3244 /// ~~~ {.cpp}
3245 /// TImage *img = TImage::Create();
3246 /// img->Bevel(0, 0, 400, 300, "#dddddd", "#000000", 3);
3247 /// ~~~
3248 
3249 void TASImage::Bevel(Int_t x, Int_t y, UInt_t width, UInt_t height,
3250  const char *hi_color, const char *lo_color, UShort_t thick,
3251  Bool_t reverse)
3252 {
3253  if (!InitVisual()) {
3254  Warning("Bevel", "Visual not initiated");
3255  return;
3256  }
3257 
3258  ASImageBevel bevel;
3259  bevel.type = 0;
3260 
3261  ARGB32 hi=ARGB32_White, lo=ARGB32_White;
3262  parse_argb_color(hi_color, &hi);
3263  parse_argb_color(lo_color, &lo);
3264 
3265  if (reverse) {
3266  bevel.lo_color = hi;
3267  bevel.lolo_color = GetHilite(hi);
3268  bevel.hi_color = lo;
3269  bevel.hihi_color = GetShadow(lo);
3270  } else {
3271  bevel.hi_color = hi;
3272  bevel.hihi_color = GetHilite(hi);
3273  bevel.lo_color = lo;
3274  bevel.lolo_color = GetShadow(lo);
3275  }
3276  bevel.hilo_color = GetAverage(hi, lo);
3277 
3278  int extra_hilite = 2;
3279  bevel.left_outline = bevel.top_outline = bevel.right_outline = bevel.bottom_outline = thick;
3280  bevel.left_inline = bevel.top_inline = bevel.right_inline = bevel.bottom_inline = extra_hilite + 1;
3281 
3282  if (bevel.top_outline > 1) {
3283  bevel.top_inline += bevel.top_outline - 1;
3284  }
3285 
3286  if (bevel.left_outline > 1) {
3287  bevel.left_inline += bevel.left_outline - 1;
3288  }
3289 
3290  if (bevel.right_outline > 1) {
3291  bevel.right_inline += bevel.right_outline - 1;
3292  }
3293 
3294  if (bevel.bottom_outline > 1) {
3295  bevel.bottom_inline += bevel.bottom_outline - 1;
3296  }
3297 
3298  ASImage *merge_im;
3299  ARGB32 fill = ((hi>>24) != 0xff) || ((lo>>24) != 0xff) ? bevel.hilo_color : (bevel.hilo_color | 0xff000000);
3300 
3301  if (!fImage) {
3302  fImage = create_asimage(width ? width : 20, height ? height : 20, 0);
3303 
3304  if (!fImage) {
3305  Warning("Bevel", "Failed to create image");
3306  return;
3307  }
3308 
3309  x = 0;
3310  y = 0;
3311  fill_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height, fill);
3312  }
3313 
3314  width = !width ? fImage->width : width;
3315  height = !height ? fImage->height : height;
3316 
3317  ASImageLayer layers[2];
3318  init_image_layers(&(layers[0]), 2);
3319 
3320  layers[0].im = fImage;
3321  layers[0].dst_x = 0;
3322  layers[0].dst_y = 0;
3323  layers[0].clip_width = fImage->width;
3324  layers[0].clip_height = fImage->height;
3325  layers[0].bevel = 0;
3326 
3327  UInt_t w = width - (bevel.left_outline + bevel.right_outline);
3328  UInt_t h = height - (bevel.top_outline + bevel.bottom_outline);
3329  ASImage *bevel_im = create_asimage(w, h, 0);
3330 
3331  if (!bevel_im) {
3332  Warning("Bevel", "Failed to create bevel image");
3333  return;
3334  }
3335 
3336  layers[1].im = bevel_im;
3337  fill_asimage(fgVisual, bevel_im, 0, 0, w, h, fill);
3338 
3339  layers[1].dst_x = x;
3340  layers[1].dst_y = y;
3341  layers[1].clip_width = width;
3342  layers[1].clip_height = height;
3343  layers[1].bevel = &bevel;
3344  layers[1].merge_scanlines = alphablend_scanlines;
3345 
3346  merge_im = merge_layers(fgVisual, &(layers[0]), 2, fImage->width, fImage->height,
3347  ASA_ASImage, GetImageCompression(), GetImageQuality());
3348  destroy_asimage(&bevel_im);
3349 
3350  if (!merge_im) {
3351  Warning("Bevel", "Failed to image");
3352  return;
3353  }
3354 
3355  DestroyImage();
3356  fImage = merge_im;
3357  UnZoom();
3358 }
3359 
3360 
3361 ////////////////////////////////////////////////////////////////////////////////
3362 /// Enlarge image, padding it with specified color on each side in
3363 /// accordance with requested geometry.
3364 
3365 void TASImage::Pad(const char *col, UInt_t l, UInt_t r, UInt_t t, UInt_t b)
3366 {
3367  Int_t x, y;
3368  UInt_t w, h;
3369 
3370  if (!InitVisual()) {
3371  Warning("Pad", "Visual not initiated");
3372  return;
3373  }
3374 
3375  if (!fImage) {
3376  fImage = create_asimage(100, 100, 0);
3377 
3378  if (!fImage) {
3379  Warning("Pad", "Failed to create image");
3380  return;
3381  }
3382 
3383  x = 0;
3384  y = 0;
3385  fill_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height, ARGB32_White);
3386  }
3387 
3388  ARGB32 color = ARGB32_White;
3389  parse_argb_color(col, &color);
3390 
3391  x = l;
3392  y = t;
3393  w = l + fImage->width + r;
3394  h = t + fImage->height + b;
3395 
3396  ASImage *img = pad_asimage(fgVisual, fImage, x, y, w, h, color,
3397  ASA_ASImage, GetImageCompression(), GetImageQuality());
3398 
3399  if (!img) {
3400  Warning("Pad", "Failed to create output image");
3401  return;
3402  }
3403 
3404  DestroyImage();
3405  fImage = img;
3406  UnZoom();
3407  fZoomUpdate = kZoomOps;
3408 }
3409 
3410 
3411 ////////////////////////////////////////////////////////////////////////////////
3412 /// Crop an image.
3413 
3414 void TASImage::Crop(Int_t x, Int_t y, UInt_t width, UInt_t height)
3415 {
3416  if (!InitVisual()) {
3417  Warning("Crop", "Visual not initiated");
3418  return;
3419  }
3420 
3421  if (!fImage) {
3422  Warning("Crop", "No image");
3423  return;
3424  }
3425 
3426  x = x < 0 ? 0 : x;
3427  y = y < 0 ? 0 : y;
3428 
3429  width = x + width > fImage->width ? fImage->width - x : width;
3430  height = y + height > fImage->height ? fImage->height - y : height;
3431 
3432  if ((width == fImage->width) && (height == fImage->height)) {
3433  Warning("Crop", "input size larger than image");
3434  return;
3435  }
3436  ASImageDecoder *imdec = start_image_decoding(fgVisual, fImage, SCL_DO_ALL,
3437  x, y, width, height, 0);
3438 
3439  if (!imdec) {
3440  Warning("Crop", "Failed to start image decoding");
3441  return;
3442  }
3443 
3444  ASImage *img = create_asimage(width, height, 0);
3445 
3446  if (!img) {
3447  delete [] imdec;
3448  Warning("Crop", "Failed to create image");
3449  return;
3450  }
3451 
3452  ASImageOutput *imout = start_image_output(fgVisual, img, ASA_ASImage,
3453  GetImageCompression(), GetImageQuality());
3454 
3455  if (!imout) {
3456  Warning("Crop", "Failed to start image output");
3457  destroy_asimage(&img);
3458  if (imdec) delete [] imdec;
3459  return;
3460  }
3461 
3462 #ifdef HAVE_MMX
3463  mmx_init();
3464 #endif
3465 
3466  for (UInt_t i = 0; i < height; i++) {
3467  imdec->decode_image_scanline(imdec);
3468  imout->output_image_scanline(imout, &(imdec->buffer), 1);
3469  }
3470 
3471  stop_image_decoding(&imdec);
3472  stop_image_output(&imout);
3473 
3474 #ifdef HAVE_MMX
3475  mmx_off();
3476 #endif
3477 
3478  DestroyImage();
3479  fImage = img;
3480  UnZoom();
3481  fZoomUpdate = kZoomOps;
3482 }
3483 
3484 ////////////////////////////////////////////////////////////////////////////////
3485 /// Append image.
3486 ///
3487 /// option:
3488 /// - "+" - appends to the right side
3489 /// - "/" - appends to the bottom
3490 
3491 void TASImage::Append(const TImage *im, const char *option, const char *color )
3492 {
3493  if (!im) return;
3494 
3495  if (!InitVisual()) {
3496  Warning("Append", "Visual not initiated");
3497  return;
3498  }
3499 
3500  if (!fImage) {
3501  fImage = ((TASImage*)im)->fImage;
3502  return;
3503  }
3504 
3505  TString opt = option;
3506  opt.Strip();
3507 
3508  UInt_t width = fImage->width;
3509  UInt_t height = fImage->height;
3510 
3511  if (opt == "+") {
3512  Pad(color, 0, im->GetWidth(), 0, 0);
3513  Merge(im, "alphablend", width, 0);
3514  } else if (opt == "/") {
3515  Pad(color, 0, 0, 0, im->GetHeight());
3516  Merge(im, "alphablend", 0, height);
3517  } else {
3518  return;
3519  }
3520 
3521  UnZoom();
3522 }
3523 
3524 ////////////////////////////////////////////////////////////////////////////////
3525 /// BeginPaint initializes internal array[width x height] of ARGB32 pixel
3526 /// values.
3527 ///
3528 /// That provides quick access to image during paint operations.
3529 /// To RLE compress image one needs to call EndPaint method when painting
3530 /// is over.
3531 
3532 void TASImage::BeginPaint(Bool_t mode)
3533 {
3534  if (!InitVisual()) {
3535  Warning("BeginPaint", "Visual not initiated");
3536  return;
3537  }
3538 
3539  if (!fImage) {
3540  return;
3541  }
3542 
3543  fPaintMode = mode;
3544 
3545  if (!fPaintMode || fImage->alt.argb32) {
3546  return;
3547  }
3548 
3549  ASImage *img = tile_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height,
3550  0, ASA_ARGB32, 0, ASIMAGE_QUALITY_DEFAULT);
3551 
3552  if (!img) {
3553  Warning("BeginPaint", "Failed to create image");
3554  return;
3555  }
3556 
3557  DestroyImage();
3558  fImage = img;
3559 }
3560 
3561 ////////////////////////////////////////////////////////////////////////////////
3562 /// EndPaint does internal RLE compression of image data.
3563 
3564 void TASImage::EndPaint()
3565 {
3566  if (!fImage) {
3567  Warning("EndPaint", "no image");
3568  return;
3569  }
3570 
3571  if (!fImage->alt.argb32) return;
3572 
3573  ASImage *img = tile_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height,
3574  0, ASA_ASImage, 0, ASIMAGE_QUALITY_DEFAULT);
3575 
3576  if (!img) {
3577  Warning("EndPaint", "Failed to create image");
3578  return;
3579  }
3580 
3581  fPaintMode = kFALSE;
3582  DestroyImage();
3583  fImage = img;
3584 }
3585 
3586 ////////////////////////////////////////////////////////////////////////////////
3587 /// Return a pointer to internal array[width x height] of ARGB32 values
3588 /// This array is directly accessible. That allows to manipulate/change the
3589 /// image.
3590 
3591 UInt_t *TASImage::GetArgbArray()
3592 {
3593  if (!fImage) {
3594  Warning("GetArgbArray", "no image");
3595  return 0;
3596  }
3597 
3598  ASImage *img = fScaledImage ? fScaledImage->fImage : fImage;
3599  if (!img) return 0;
3600 
3601  if (!img->alt.argb32) {
3602  if (fScaledImage) {
3603  fScaledImage->BeginPaint();
3604  img = fScaledImage->fImage;
3605  } else {
3606  BeginPaint();
3607  img = fImage;
3608  }
3609  }
3610 
3611  return (UInt_t *)img->alt.argb32;
3612 }
3613 
3614 ////////////////////////////////////////////////////////////////////////////////
3615 /// Return a pointer to an array[width x height] of RGBA32 values.
3616 /// This array is created from internal ARGB32 array,
3617 /// must be deleted after usage.
3618 
3619 UInt_t *TASImage::GetRgbaArray()
3620 {
3621  if (!fImage) {
3622  Warning("GetRgbaArray", "no image");
3623  return 0;
3624  }
3625 
3626  ASImage *img = fScaledImage ? fScaledImage->fImage : fImage;
3627  if (!img) return 0;
3628 
3629  if (!img->alt.argb32) {
3630  if (fScaledImage) {
3631  fScaledImage->BeginPaint();
3632  img = fScaledImage->fImage;
3633  } else {
3634  BeginPaint();
3635  img = fImage;
3636  }
3637  }
3638 
3639  UInt_t i, j;
3640  Int_t y = 0;
3641  Int_t idx = 0;
3642  UInt_t a, rgb, rgba, argb;
3643  y = 0;
3644 
3645  UInt_t *ret = new UInt_t[img->width*img->height];
3646 
3647  for (i = 0; i < img->height; i++) {
3648  for (j = 0; j < img->width; j++) {
3649  idx = Idx(y + j);
3650  argb = img->alt.argb32[idx];
3651  a = argb >> 24;
3652  rgb = argb & 0x00ffffff;
3653  rgba = (rgb << 8) + a;
3654  ret[idx] = rgba;
3655  }
3656  y += img->width;
3657  }
3658 
3659  return ret;
3660 }
3661 
3662 ////////////////////////////////////////////////////////////////////////////////
3663 /// Return a pointer to scan-line.
3664 
3665 UInt_t *TASImage::GetScanline(UInt_t y)
3666 {
3667  if (!fImage) {
3668  Warning("GetScanline", "no image");
3669  return 0;
3670  }
3671 
3672  ASImage *img = fScaledImage ? fScaledImage->fImage : fImage;
3673  CARD32 *ret = new CARD32[img->width];
3674 
3675  ASImageDecoder *imdec = start_image_decoding(fgVisual, img, SCL_DO_ALL,
3676  0, y, img->width, 1, 0);
3677 
3678  if (!imdec) {
3679  delete [] ret;
3680  Warning("GetScanline", "Failed to start image decoding");
3681  return 0;
3682  }
3683 
3684 #ifdef HAVE_MMX
3685  mmx_init();
3686 #endif
3687 
3688  imdec->decode_image_scanline(imdec);
3689  memcpy(imdec->buffer.buffer, ret, img->width*sizeof(CARD32));
3690  stop_image_decoding(&imdec);
3691 
3692 #ifdef HAVE_MMX
3693  mmx_off();
3694 #endif
3695 
3696  return (UInt_t*)ret;
3697 }
3698 
3699 
3700 //______________________________________________________________________________
3701 //
3702 // Vector graphics
3703 // a couple of macros which can be "assembler accelerated"
3704 
3705 #if defined(R__GNU) && defined(__i386__) && !defined(__sun)
3706 #define _MEMSET_(dst, lng, val) __asm__("movl %0,%%eax \n"\
3707  "movl %1,%%edi \n" \
3708  "movl %2,%%ecx \n" \
3709  "cld \n" \
3710  "rep \n" \
3711  "stosl \n" \
3712  : /* no output registers */ \
3713  :"g" (val), "g" (dst), "g" (lng) \
3714  :"eax","edi","ecx" \
3715  )
3716 
3717 #else
3718  #define _MEMSET_(dst, lng, val) do {\
3719  for( UInt_t j=0; j < lng; j++) *((dst)+j) = val; } while (0)
3720 
3721 #endif
3722 
3723 #define FillSpansInternal(npt, ppt, widths, color) do {\
3724  UInt_t yy = ppt[0].fY*fImage->width;\
3725  for (UInt_t i = 0; i < npt; i++) {\
3726  _MEMSET_(&fImage->alt.argb32[Idx(yy + ppt[i].fX)], widths[i], color);\
3727  yy += ((i+1 < npt) && (ppt[i].fY != ppt[i+1].fY) ? fImage->width : 0);\
3728  }\
3729 } while (0)
3730 
3731 ////////////////////////////////////////////////////////////////////////////////
3732 /// Fill rectangle of size (width, height) at position (x,y)
3733 /// within the existing image with specified color.
3734 
3735 void TASImage::FillRectangleInternal(UInt_t col, Int_t x, Int_t y, UInt_t width, UInt_t height)
3736 {
3737 
3738  if (!InitVisual()) {
3739  Warning("FillRectangle", "Visual not initiated");
3740  return;
3741  }
3742 
3743  if (!fImage) {
3744  Warning("FillRectangle", "no image");
3745  return;
3746  }
3747 
3748  if (!fImage->alt.argb32) {
3749  BeginPaint();
3750  }
3751 
3752  if (!fImage->alt.argb32) {
3753  Warning("FillRectangle", "Failed to get pixel array");
3754  return;
3755  }
3756 
3757  ARGB32 color = (ARGB32)col;
3758 
3759  if (width == 0) width = 1;
3760  if (height == 0) height = 1;
3761 
3762  if (x < 0) {
3763  width += x;
3764  x = 0;
3765  }
3766  if (y < 0) {
3767  height += y;
3768  y = 0;
3769  }
3770 
3771  Bool_t has_alpha = (color & 0xff000000) != 0xff000000;
3772 
3773  x = x > (int)fImage->width ? (Int_t)fImage->width : x;
3774  y = y > (int)fImage->height ? (Int_t)fImage->height : y;
3775 
3776  width = x + width > fImage->width ? fImage->width - x : width;
3777  height = y + height > fImage->height ? fImage->height - y : height;
3778 
3779  if (!fImage->alt.argb32) {
3780  fill_asimage(fgVisual, fImage, x, y, width, height, color);
3781  } else {
3782  int yyy = y*fImage->width;
3783  if (!has_alpha) { // use faster memset
3784  ARGB32 *p0 = fImage->alt.argb32 + yyy + x;
3785  ARGB32 *p = p0;
3786  for (UInt_t i = 0; i < height; i++) {
3787  _MEMSET_(p, width, color);
3788  p += fImage->width;
3789  }
3790  } else {
3791  for (UInt_t i = y; i < y + height; i++) {
3792  int j = x + width;
3793  while (j > x) {
3794  j--;
3795  _alphaBlend(&fImage->alt.argb32[Idx(yyy + j)], &color);
3796  }
3797  yyy += fImage->width;
3798  }
3799  }
3800  }
3801 }
3802 
3803 ////////////////////////////////////////////////////////////////////////////////
3804 /// Fill rectangle of size (width, height) at position (x,y)
3805 /// within the existing image with specified color.
3806 ///
3807 /// To create new image with Fill method the following code can be used:
3808 /// ~~~ {.cpp}
3809 /// TImage *img = TImage::Create();
3810 /// img->Fill("#FF00FF", 0, 0, 400, 300);
3811 /// ~~~
3812 
3813 void TASImage::FillRectangle(const char *col, Int_t x, Int_t y, UInt_t width, UInt_t height)
3814 {
3815  if (!InitVisual()) {
3816  Warning("Fill", "Visual not initiated");
3817  return;
3818  }
3819 
3820  ARGB32 color = ARGB32_White;
3821 
3822  if (col) {
3823  parse_argb_color(col, &color);
3824  }
3825 
3826  if (!fImage) {
3827  fImage = create_asimage(width ? width : 20, height ? height : 20, 0);
3828  x = 0;
3829  y = 0;
3830  }
3831 
3832  FillRectangleInternal((UInt_t)color, x, y, width, height);
3833  UnZoom();
3834 }
3835 
3836 ////////////////////////////////////////////////////////////////////////////////
3837 /// Draw a vertical line.
3838 
3839 void TASImage::DrawVLine(UInt_t x, UInt_t y1, UInt_t y2, UInt_t col, UInt_t thick)
3840 {
3841  ARGB32 color = (ARGB32)col;
3842  UInt_t half = 0;
3843 
3844  if (!thick) thick = 1;
3845 
3846  if (thick > 1) {
3847  half = thick >> 1;
3848  if (x > half) {
3849  x = x - half;
3850  } else {
3851  x = 0;
3852  thick += (x - half);
3853  }
3854  }
3855 
3856  y2 = y2 >= fImage->height ? fImage->height - 1 : y2;
3857  y1 = y1 >= fImage->height ? fImage->height - 1 : y1;
3858  x = x + thick >= fImage->width ? fImage->width - thick - 1 : x;
3859 
3860  int yy = y1*fImage->width;
3861  for (UInt_t y = y1; y <= y2; y++) {
3862  for (UInt_t w = 0; w < thick; w++) {
3863  if (x + w < fImage->width) {
3864  _alphaBlend(&fImage->alt.argb32[Idx(yy + (x + w))], &color);
3865  }
3866  }
3867  yy += fImage->width;
3868  }
3869 }
3870 
3871 ////////////////////////////////////////////////////////////////////////////////
3872 /// Draw an horizontal line.
3873 
3874 void TASImage::DrawHLine(UInt_t y, UInt_t x1, UInt_t x2, UInt_t col, UInt_t thick)
3875 {
3876  ARGB32 color = (ARGB32)col;
3877  UInt_t half = 0;
3878 
3879  if (!thick) thick = 1;
3880 
3881  if (thick > 1) {
3882  half = thick >> 1;
3883  if (y > half) {
3884  y = y - half;
3885  } else {
3886  y = 0;
3887  thick += (y - half);
3888  }
3889  }
3890 
3891  y = y + thick >= fImage->height ? fImage->height - thick - 1 : y;
3892  x2 = x2 >= fImage->width ? fImage->width - 1 : x2;
3893  x1 = x1 >= fImage->width ? fImage->width - 1 : x1;
3894 
3895  int yy = y*fImage->width;
3896  for (UInt_t w = 0; w < thick; w++) {
3897  for (UInt_t x = x1; x <= x2; x++) {
3898  if (y + w < fImage->height) {
3899  _alphaBlend(&fImage->alt.argb32[Idx(yy + x)], &color);
3900  }
3901  }
3902  yy += fImage->width;
3903  }
3904 }
3905 
3906 ////////////////////////////////////////////////////////////////////////////////
3907 /// Draw a line.
3908 
3909 void TASImage::DrawLine(UInt_t x1, UInt_t y1, UInt_t x2, UInt_t y2,
3910  const char *col, UInt_t thick)
3911 {
3912  ARGB32 color = ARGB32_White;
3913  parse_argb_color(col, &color);
3914  DrawLineInternal(x1, y1, x2, y2, (UInt_t)color, thick);
3915 }
3916 
3917 ////////////////////////////////////////////////////////////////////////////////
3918 /// Internal line drawing.
3919 
3920 void TASImage::DrawLineInternal(UInt_t x1, UInt_t y1, UInt_t x2, UInt_t y2,
3921  UInt_t col, UInt_t thick)
3922 {
3923  int dx, dy, d;
3924  int i1, i2;
3925  int x, y, xend, yend;
3926  int xdir, ydir;
3927  int q;
3928  int idx;
3929  int yy;
3930 
3931  if (!InitVisual()) {
3932  Warning("DrawLine", "Visual not initiated");
3933  return;
3934  }
3935 
3936  if (!fImage) {
3937  Warning("DrawLine", "no image");
3938  return;
3939  }
3940 
3941  if (!fImage->alt.argb32) {
3942  BeginPaint();
3943  }
3944 
3945  if (!fImage->alt.argb32) {
3946  Warning("DrawLine", "Failed to get pixel array");
3947  return;
3948  }
3949 
3950  ARGB32 color = (ARGB32)col;
3951 
3952  dx = TMath::Abs(Int_t(x2) - Int_t(x1));
3953  dy = TMath::Abs(Int_t(y2) - Int_t(y1));
3954 
3955  if (!dx && !dy) return; // invisible line
3956 
3957  if (!dx) {
3958  DrawVLine(x1, y2 > y1 ? y1 : y2,
3959  y2 > y1 ? y2 : y1, color, thick);
3960  return;
3961  }
3962 
3963  if (!dy) {
3964  DrawHLine(y1, x2 > x1 ? x1 : x2,
3965  x2 > x1 ? x2 : x1, color, thick);
3966  return;
3967  }
3968 
3969  if (thick > 1) {
3970  DrawWideLine(x1, y1, x2, y2, color, thick);
3971  return;
3972  }
3973 
3974  if (dy <= dx) {
3975  UInt_t ddy = dy << 1;
3976  i1 = ddy;
3977  i2 = i1 - (dx << 1);
3978  d = i1 - dx;
3979 
3980  if (x1 > x2) {
3981  x = x2;
3982  y = y2;
3983  ydir = -1;
3984  xend = x1;
3985  } else {
3986  x = x1;
3987  y = y1;
3988  ydir = 1;
3989  xend = x2;
3990  }
3991 
3992  yy = y*fImage->width;
3993  _alphaBlend(&fImage->alt.argb32[Idx(yy + x)], &color);
3994  q = (y2 - y1) * ydir;
3995 
3996  if (q > 0) {
3997  while (x < xend) {
3998 
3999  idx = Idx(yy + x);
4000  _alphaBlend(&fImage->alt.argb32[idx], &color);
4001  x++;
4002 
4003  if (d >= 0) {
4004  yy += fImage->width;
4005  d += i2;
4006  } else {
4007  d += i1;
4008  }
4009  }
4010  } else {
4011  while (x < xend) {
4012  idx = Idx(yy + x);
4013  _alphaBlend(&fImage->alt.argb32[idx], &color);
4014  x++;
4015 
4016  if (d >= 0) {
4017  yy -= fImage->width;
4018  d += i2;
4019  } else {
4020  d += i1;
4021  }
4022  }
4023  }
4024  } else {
4025  UInt_t ddx = dx << 1;
4026  i1 = ddx;
4027  i2 = i1 - (dy << 1);
4028  d = i1 - dy;
4029 
4030  if (y1 > y2) {
4031  y = y2;
4032  x = x2;
4033  yend = y1;
4034  xdir = -1;
4035  } else {
4036  y = y1;
4037  x = x1;
4038  yend = y2;
4039  xdir = 1;
4040  }
4041 
4042  yy = y*fImage->width;
4043  _alphaBlend(&fImage->alt.argb32[Idx(yy + x)], &color);
4044  q = (x2 - x1) * xdir;
4045 
4046  if (q > 0) {
4047  while (y < yend) {
4048  idx = Idx(yy + x);
4049  _alphaBlend(&fImage->alt.argb32[idx], &color);
4050  y++;
4051  yy += fImage->width;
4052 
4053  if (d >= 0) {
4054  x++;
4055  d += i2;
4056  } else {
4057  d += i1;
4058  }
4059  }
4060  } else {
4061  while (y < yend) {
4062  idx = Idx(yy + x);
4063  _alphaBlend(&fImage->alt.argb32[idx], &color);
4064  y++;
4065  yy += fImage->width;
4066 
4067  if (d >= 0) {
4068  x--;
4069  d += i2;
4070  } else {
4071  d += i1;
4072  }
4073  }
4074  }
4075  }
4076 }
4077 
4078 ////////////////////////////////////////////////////////////////////////////////
4079 /// Draw a rectangle.
4080 
4081 void TASImage::DrawRectangle(UInt_t x, UInt_t y, UInt_t w, UInt_t h,
4082  const char *col, UInt_t thick)
4083 {
4084  if (!InitVisual()) {
4085  Warning("DrawRectangle", "Visual not initiated");
4086  return;
4087  }
4088 
4089  if (!fImage) {
4090  w = w ? w : 20;
4091  h = h ? h : 20;
4092  x = 0;
4093  y = 0;
4094  fImage = create_asimage(w, h, 0);
4095  FillRectangle(col, 0, 0, w, h);
4096  return;
4097  }
4098 
4099  if (!fImage->alt.argb32) {
4100  BeginPaint();
4101  }
4102 
4103  if (!fImage->alt.argb32) {
4104  Warning("DrawRectangle", "Failed to get pixel array");
4105  return;
4106  }
4107 
4108  ARGB32 color = ARGB32_White;
4109  parse_argb_color(col, &color);
4110 
4111  DrawHLine(y, x, x + w, (UInt_t)color, thick);
4112  DrawVLine(x + w, y, y + h, (UInt_t)color, thick);
4113  DrawHLine(y + h, x, x + w, (UInt_t)color, thick);
4114  DrawVLine(x, y, y + h, (UInt_t)color, thick);
4115  UnZoom();
4116 }
4117 
4118 ////////////////////////////////////////////////////////////////////////////////
4119 /// Draw a box.
4120 
4121 void TASImage::DrawBox(Int_t x1, Int_t y1, Int_t x2, Int_t y2, const char *col,
4122  UInt_t thick, Int_t mode)
4123 {
4124  Int_t x = TMath::Min(x1, x2);
4125  Int_t y = TMath::Min(y1, y2);
4126  Int_t w = TMath::Abs(x2 - x1);
4127  Int_t h = TMath::Abs(y2 - y1);
4128 
4129  ARGB32 color = ARGB32_White;
4130 
4131  if (!fImage) {
4132  w = w ? x+w : x+20;
4133  h = h ? y+h : y+20;
4134  fImage = create_asimage(w, h, 0);
4135  FillRectangle(col, 0, 0, w, h);
4136  return;
4137  }
4138 
4139  if (x1 == x2) {
4140  parse_argb_color(col, &color);
4141  DrawVLine(x1, y1, y2, color, 1);
4142  return;
4143  }
4144 
4145  if (y1 == y2) {
4146  parse_argb_color(col, &color);
4147  DrawHLine(y1, x1, x2, color, 1);
4148  return;
4149  }
4150 
4151 
4152  switch (mode) {
4153  case TVirtualX::kHollow:
4154  DrawRectangle(x, y, w, h, col, thick);
4155  break;
4156 
4157  case TVirtualX::kFilled:
4158  FillRectangle(col, x, y, w, h);
4159  break;
4160 
4161  default:
4162  FillRectangle(col, x, y, w, h);
4163  break;
4164  }
4165 }
4166 
4167 ////////////////////////////////////////////////////////////////////////////////
4168 /// Draw a dashed horizontal line.
4169 
4170 void TASImage::DrawDashHLine(UInt_t y, UInt_t x1, UInt_t x2, UInt_t nDash,
4171  const char *pDash, UInt_t col, UInt_t thick)
4172 {
4173  UInt_t iDash = 0; // index of current dash
4174  int i = 0;
4175 
4176  ARGB32 color = (ARGB32)col;
4177 
4178  UInt_t half = 0;
4179 
4180  if (thick > 1) {
4181  half = thick >> 1;
4182  if (y > half) {
4183  y = y - half;
4184  } else {
4185  y = 0;
4186  thick += (y - half);
4187  }
4188  }
4189  thick = thick <= 0 ? 1 : thick;
4190 
4191  y = y + thick >= fImage->height ? fImage->height - thick - 1 : y;
4192  x2 = x2 >= fImage->width ? fImage->width - 1 : x2;
4193  x1 = x1 >= fImage->width ? fImage->width - 1 : x1;
4194 
4195  // switch x1, x2
4196  UInt_t tmp = x1;
4197  x1 = x2 < x1 ? x2 : x1;
4198  x2 = x2 < tmp ? tmp : x2;
4199 
4200  for (UInt_t x = x1; x <= x2; x++) {
4201  for (UInt_t w = 0; w < thick; w++) {
4202  if (y + w < fImage->height) {
4203  if ((iDash%2)==0) {
4204  _alphaBlend(&fImage->alt.argb32[Idx((y + w)*fImage->width + x)], &color);
4205  }
4206  }
4207  }
4208  i++;
4209 
4210  if (i >= pDash[iDash]) {
4211  iDash++;
4212  i = 0;
4213  }
4214  if (iDash >= nDash) {
4215  iDash = 0;
4216  i = 0;
4217  }
4218  }
4219 }
4220 
4221 ////////////////////////////////////////////////////////////////////////////////
4222 /// Draw a dashed vertical line.
4223 
4224 void TASImage::DrawDashVLine(UInt_t x, UInt_t y1, UInt_t y2, UInt_t nDash,
4225  const char *pDash, UInt_t col, UInt_t thick)
4226 {
4227  UInt_t iDash = 0; // index of current dash
4228  int i = 0;
4229 
4230  ARGB32 color = (ARGB32)col;
4231 
4232  UInt_t half = 0;
4233 
4234  if (thick > 1) {
4235  half = thick >> 1;
4236  if (x > half) {
4237  x = x - half;
4238  } else {
4239  x = 0;
4240  thick += (x - half);
4241  }
4242  }
4243  thick = thick <= 0 ? 1 : thick;
4244 
4245  y2 = y2 >= fImage->height ? fImage->height - 1 : y2;
4246  y1 = y1 >= fImage->height ? fImage->height - 1 : y1;
4247 
4248  // switch x1, x2
4249  UInt_t tmp = y1;
4250  y1 = y2 < y1 ? y2 : y1;
4251  y2 = y2 < tmp ? tmp : y2;
4252 
4253  x = x + thick >= fImage->width ? fImage->width - thick - 1 : x;
4254 
4255  int yy = y1*fImage->width;
4256  for (UInt_t y = y1; y <= y2; y++) {
4257  for (UInt_t w = 0; w < thick; w++) {
4258  if (x + w < fImage->width) {
4259  if ((iDash%2)==0) {
4260  _alphaBlend(&fImage->alt.argb32[Idx(yy + (x + w))], &color);
4261  }
4262  }
4263  }
4264  i++;
4265 
4266  if (i >= pDash[iDash]) {
4267  iDash++;
4268  i = 0;
4269  }
4270  if (iDash >= nDash) {
4271  iDash = 0;
4272  i = 0;
4273  }
4274  yy += fImage->width;
4275  }
4276 }
4277 
4278 ////////////////////////////////////////////////////////////////////////////////
4279 /// Draw a dashed line with one pixel width.
4280 
4281 void TASImage::DrawDashZLine(UInt_t x1, UInt_t y1, UInt_t x2, UInt_t y2,
4282  UInt_t nDash, const char *tDash, UInt_t color)
4283 {
4284  int dx, dy, d;
4285  int i, i1, i2;
4286  int x, y, xend, yend;
4287  int xdir, ydir;
4288  int q;
4289  UInt_t iDash = 0; // index of current dash
4290  int yy;
4291  int idx;
4292 
4293  dx = TMath::Abs(Int_t(x2) - Int_t(x1));
4294  dy = TMath::Abs(Int_t(y2) - Int_t(y1));
4295 
4296  char *pDash = new char[nDash];
4297 
4298  if (dy <= dx) {
4299  double ac = TMath::Cos(TMath::ATan2(dy, dx));
4300 
4301  for (i = 0; i < (int)nDash; i++) {
4302  pDash[i] = TMath::Nint(tDash[i] * ac);
4303  }
4304 
4305  UInt_t ddy = dy << 1;
4306  i1 = ddy;
4307  i2 = i1 - (dx << 1);
4308  d = i1 - dx;
4309  i = 0;
4310 
4311  if (x1 > x2) {
4312  x = x2;
4313  y = y2;
4314  ydir = -1;
4315  xend = x1;
4316  } else {
4317  x = x1;
4318  y = y1;
4319  ydir = 1;
4320  xend = x2;
4321  }
4322 
4323  yy = y*fImage->width;
4324  _alphaBlend(&fImage->alt.argb32[Idx(y*fImage->width + x)], &color);
4325  q = (y2 - y1) * ydir;
4326 
4327  if (q > 0) {
4328  while (x < xend) {
4329  idx = Idx(yy + x);
4330  if ((iDash%2) == 0) {
4331  _alphaBlend(&fImage->alt.argb32[idx], &color);
4332  }
4333  x++;
4334  if (d >= 0) {
4335  yy += fImage->width;
4336  d += i2;
4337  } else {
4338  d += i1;
4339  }
4340 
4341  i++;
4342  if (i >= pDash[iDash]) {
4343  iDash++;
4344  i = 0;
4345  }
4346  if (iDash >= nDash) {
4347  iDash = 0;
4348  i = 0;
4349  }
4350  }
4351  } else {
4352  while (x < xend) {
4353  idx = Idx(yy + x);
4354  if ((iDash%2) == 0) {
4355  _alphaBlend(&fImage->alt.argb32[idx], &color);
4356  }
4357  x++;
4358  if (d >= 0) {
4359  yy -= fImage->width;
4360  d += i2;
4361  } else {
4362  d += i1;
4363  }
4364 
4365  i++;
4366  if (i >= pDash[iDash]) {
4367  iDash++;
4368  i = 0;
4369  }
4370  if (iDash >= nDash) {
4371  iDash = 0;
4372  i = 0;
4373  }
4374  }
4375  }
4376  } else {
4377  double as = TMath::Sin(TMath::ATan2(dy, dx));
4378 
4379  for (i = 0; i < (int)nDash; i++) {
4380  pDash[i] = TMath::Nint(tDash[i] * as);
4381  }
4382 
4383  UInt_t ddx = dx << 1;
4384  i1 = ddx;
4385  i2 = i1 - (dy << 1);
4386  d = i1 - dy;
4387  i = 0;
4388 
4389  if (y1 > y2) {
4390  y = y2;
4391  x = x2;
4392  yend = y1;
4393  xdir = -1;
4394  } else {
4395  y = y1;
4396  x = x1;
4397  yend = y2;
4398  xdir = 1;
4399  }
4400 
4401  yy = y*fImage->width;
4402  _alphaBlend(&fImage->alt.argb32[Idx(y*fImage->width + x)], &color);
4403  q = (x2 - x1) * xdir;
4404 
4405  if (q > 0) {
4406  while (y < yend) {
4407  idx = Idx(yy + x);
4408  if ((iDash%2) == 0) {
4409  _alphaBlend(&fImage->alt.argb32[idx], &color);
4410  }
4411  y++;
4412  yy += fImage->width;
4413 
4414  if (d >= 0) {
4415  x++;
4416  d += i2;
4417  } else {
4418  d += i1;
4419  }
4420 
4421  i++;
4422  if (i >= pDash[iDash]) {
4423  iDash++;
4424  i = 0;
4425  }
4426  if (iDash >= nDash) {
4427  iDash = 0;
4428  i = 0;
4429  }
4430  }
4431  } else {
4432  while (y < yend) {
4433  idx = Idx(yy + x);
4434  if ((iDash%2) == 0) {
4435  _alphaBlend(&fImage->alt.argb32[idx], &color);
4436  }
4437  y++;
4438  yy += fImage->width;
4439 
4440  if (d >= 0) {
4441  x--;
4442  d += i2;
4443  } else {
4444  d += i1;
4445  }
4446 
4447  i++;
4448  if (i >= pDash[iDash]) {
4449  iDash++;
4450  i = 0;
4451  }
4452  if (iDash >= nDash) {
4453  iDash = 0;
4454  i = 0;
4455  }
4456  }
4457  }
4458  }
4459  delete [] pDash;
4460 }
4461 
4462 ////////////////////////////////////////////////////////////////////////////////
4463 /// Draw a dashed line with thick pixel width.
4464 
4465 void TASImage::DrawDashZTLine(UInt_t x1, UInt_t y1, UInt_t x2, UInt_t y2,
4466  UInt_t nDash, const char *tDash, UInt_t color, UInt_t thick)
4467 {
4468  int dx, dy;
4469  int i;
4470  double x, y, xend=0, yend=0, x0, y0;
4471  int xdir, ydir;
4472  int q;
4473  UInt_t iDash = 0; // index of current dash
4474 
4475  dx = TMath::Abs(Int_t(x2) - Int_t(x1));
4476  dy = TMath::Abs(Int_t(y2) - Int_t(y1));
4477 
4478  double *xDash = new double[nDash];
4479  double *yDash = new double[nDash];
4480  double a = TMath::ATan2(dy, dx);
4481  double ac = TMath::Cos(a);
4482  double as = TMath::Sin(a);
4483 
4484  for (i = 0; i < (int)nDash; i++) {
4485  xDash[i] = tDash[i] * ac;
4486  yDash[i] = tDash[i] * as;
4487 
4488  // dirty trick (must be fixed)
4489  if ((i%2) == 0) {
4490  xDash[i] = xDash[i]/2;
4491  yDash[i] = yDash[i]/2;
4492  } else {
4493  xDash[i] = xDash[i]*2;
4494  yDash[i] = yDash[i]*2;
4495  }
4496  }
4497 
4498  if (dy <= dx) {
4499  if (x1 > x2) {
4500  x = x2;
4501  y = y2;
4502  ydir = -1;
4503  xend = x1;
4504  } else {
4505  x = x1;
4506  y = y1;
4507  ydir = 1;
4508  xend = x2;
4509  }
4510 
4511  q = (y2 - y1) * ydir;
4512  x0 = x;
4513  y0 = y;
4514  iDash = 0;
4515  yend = y + q;
4516 
4517  if (q > 0) {
4518  while ((x < xend) && (y < yend)) {
4519  x += xDash[iDash];
4520  y += yDash[iDash];
4521 
4522  if ((iDash%2) == 0) {
4523  DrawWideLine(TMath::Nint(x0), TMath::Nint(y0),
4524  TMath::Nint(x), TMath::Nint(y), color, thick);
4525  } else {
4526  x0 = x;
4527  y0 = y;
4528  }
4529 
4530  iDash++;
4531 
4532  if (iDash >= nDash) {
4533  iDash = 0;
4534  }
4535  }
4536  } else {
4537  while ((x < xend) && (y > yend)) {
4538  x += xDash[iDash];
4539  y -= yDash[iDash];
4540 
4541  if ((iDash%2) == 0) {
4542  DrawWideLine(TMath::Nint(x0), TMath::Nint(y0),
4543  TMath::Nint(x), TMath::Nint(y), color, thick);
4544  } else {
4545  x0 = x;
4546  y0 = y;
4547  }
4548 
4549  iDash++;
4550 
4551  if (iDash >= nDash) {
4552  iDash = 0;
4553  }
4554  }
4555  }
4556  } else {
4557 
4558  if (y1 > y2) {
4559  y = y2;
4560  x = x2;
4561  yend = y1;
4562  xdir = -1;
4563  } else {
4564  y = y1;
4565  x = x1;
4566  yend = y2;
4567  xdir = 1;
4568  }
4569 
4570  q = (x2 - x1) * xdir;
4571  x0 = x;
4572  y0 = y;
4573  iDash = 0;
4574  xend = x + q;
4575 
4576  if (q > 0) {
4577  while ((x < xend) && (y < yend)) {
4578  x += xDash[iDash];
4579  y += yDash[iDash];
4580 
4581  if ((iDash%2) == 0) {
4582  DrawWideLine(TMath::Nint(x0), TMath::Nint(y0),
4583  TMath::Nint(x), TMath::Nint(y), color, thick);
4584  } else {
4585  x0 = x;
4586  y0 = y;
4587  }
4588 
4589  iDash++;
4590 
4591  if (iDash >= nDash) {
4592  iDash = 0;
4593  }
4594  }
4595  } else {
4596  while ((x > xend) && (y < yend)) {
4597  x -= xDash[iDash];
4598  y += yDash[iDash];
4599 
4600  if ((iDash%2) == 0) {
4601  DrawWideLine(TMath::Nint(x0), TMath::Nint(y0),
4602  TMath::Nint(x), TMath::Nint(y), color, thick);
4603  } else {
4604  x0 = x;
4605  y0 = y;
4606  }
4607 
4608  iDash++;
4609 
4610  if (iDash >= nDash) {
4611  iDash = 0;
4612  }
4613  }
4614  }
4615  }
4616  delete [] xDash;
4617  delete [] yDash;
4618 }
4619 
4620 ////////////////////////////////////////////////////////////////////////////////
4621 /// Draw a dashed line.
4622 
4623 void TASImage::DrawDashLine(UInt_t x1, UInt_t y1, UInt_t x2, UInt_t y2, UInt_t nDash,
4624  const char *pDash, const char *col, UInt_t thick)
4625 
4626 {
4627  if (!InitVisual()) {
4628  Warning("DrawDashLine", "Visual not initiated");
4629  return;
4630  }
4631 
4632  if (!fImage) {
4633  Warning("DrawDashLine", "no image");
4634  return;
4635  }
4636 
4637  if (!fImage->alt.argb32) {
4638  BeginPaint();
4639  }
4640 
4641  if (!fImage->alt.argb32) {
4642  Warning("DrawDashLine", "Failed to get pixel array");
4643  return;
4644  }
4645 
4646  if ((nDash < 2) || !pDash || (nDash%2)) {
4647  Warning("DrawDashLine", "Wrong input parameters n=%d %ld", nDash, (Long_t)sizeof(pDash)-1);
4648  return;
4649  }
4650 
4651  ARGB32 color = ARGB32_White;
4652  parse_argb_color(col, &color);
4653 
4654  if (x1 == x2) {
4655  DrawDashVLine(x1, y1, y2, nDash, pDash, (UInt_t)color, thick);
4656  } else if (y1 == y2) {
4657  DrawDashHLine(y1, x1, x2, nDash, pDash, (UInt_t)color, thick);
4658  } else {
4659  if (thick < 2) DrawDashZLine(x1, y1, x2, y2, nDash, pDash, (UInt_t)color);
4660  else DrawDashZTLine(x1, y1, x2, y2, nDash, pDash, (UInt_t)color, thick);
4661  }
4662 }
4663 
4664 ////////////////////////////////////////////////////////////////////////////////
4665 /// Draw a polyline.
4666 
4667 void TASImage::DrawPolyLine(UInt_t nn, TPoint *xy, const char *col, UInt_t thick,
4668  TImage::ECoordMode mode)
4669 {
4670  ARGB32 color = ARGB32_White;
4671  parse_argb_color(col, &color);
4672 
4673  Int_t x0 = xy[0].GetX();
4674  Int_t y0 = xy[0].GetY();
4675  Int_t x = 0;
4676  Int_t y = 0;
4677 
4678  for (UInt_t i = 1; i < nn; i++) {
4679  x = (mode == kCoordModePrevious) ? x + xy[i].GetX() : xy[i].GetX();
4680  y = (mode == kCoordModePrevious) ? y + xy[i].GetY() : xy[i].GetY();
4681 
4682  DrawLineInternal(x0, y0, x, y, (UInt_t)color, thick);
4683 
4684  x0 = x;
4685  y0 = y;
4686  }
4687 }
4688 
4689 ////////////////////////////////////////////////////////////////////////////////
4690 /// Draw a point at the specified position.
4691 
4692 void TASImage::PutPixel(Int_t x, Int_t y, const char *col)
4693 {
4694  if (!InitVisual()) {
4695  Warning("PutPixel", "Visual not initiated");
4696  return;
4697  }
4698 
4699  if (!fImage) {
4700  Warning("PutPixel", "no image");
4701  return;
4702  }
4703 
4704  if (!fImage->alt.argb32) {
4705  BeginPaint();
4706  }
4707 
4708  if (!fImage->alt.argb32) {
4709  Warning("PutPixel", "Failed to get pixel array");
4710  return;
4711  }
4712 
4713  ARGB32 color;
4714  parse_argb_color(col, &color);
4715 
4716  if ((x < 0) || (y < 0) || (x >= (int)fImage->width) || (y >= (int)fImage->height)) {
4717  Warning("PutPixel", "Out of range width=%d x=%d, height=%d y=%d",
4718  fImage->width, x, fImage->height, y);
4719  return;
4720  }
4721  _alphaBlend(&fImage->alt.argb32[Idx(y*fImage->width + x)], &color);
4722 }
4723 
4724 ////////////////////////////////////////////////////////////////////////////////
4725 /// Draw a poly point.
4726 
4727 void TASImage::PolyPoint(UInt_t npt, TPoint *ppt, const char *col, TImage::ECoordMode mode)
4728 {
4729  if (!InitVisual()) {
4730  Warning("PolyPoint", "Visual not initiated");
4731  return;
4732  }
4733 
4734  if (!fImage) {
4735  Warning("PolyPoint", "no image");
4736  return;
4737  }
4738 
4739  if (!fImage->alt.argb32) {
4740  BeginPaint();
4741  }
4742 
4743  if (!fImage->alt.argb32) {
4744  Warning("PolyPoint", "Failed to get pixel array");
4745  return;
4746  }
4747 
4748  if (!npt || !ppt) {
4749  Warning("PolyPoint", "No points specified");
4750  return;
4751  }
4752 
4753  TPoint *ipt = 0;
4754  UInt_t i = 0;
4755  ARGB32 color;
4756  parse_argb_color(col, &color);
4757 
4758  //make pointlist origin relative
4759  if (mode == kCoordModePrevious) {
4760  ipt = new TPoint[npt];
4761 
4762  for (i = 0; i < npt; i++) {
4763  ipt[i].fX += ppt[i].fX;
4764  ipt[i].fY += ppt[i].fY;
4765  }
4766  }
4767  int x, y;
4768 
4769  for (i = 0; i < npt; i++) {
4770  x = ipt ? ipt[i].fX : ppt[i].fX;
4771  y = ipt ? ipt[i].fY : ppt[i].fY;
4772 
4773  if ((x < 0) || (y < 0) || (x >= (int)fImage->width) || (y >= (int)fImage->height)) {
4774  continue;
4775  }
4776  _alphaBlend(&fImage->alt.argb32[Idx(y*fImage->width + x)], &color);
4777  }
4778 
4779  if (ipt) {
4780  delete [] ipt;
4781  }
4782 }
4783 
4784 ////////////////////////////////////////////////////////////////////////////////
4785 /// Draw segments.
4786 
4787 void TASImage::DrawSegments(UInt_t nseg, Segment_t *seg, const char *col, UInt_t thick)
4788 {
4789  if (!nseg || !seg) {
4790  Warning("DrawSegments", "Invalid data nseg=%d seg=0x%lx", nseg, (Long_t)seg);
4791  return;
4792  }
4793 
4794  TPoint pt[2];
4795 
4796  for (UInt_t i = 0; i < nseg; i++) {
4797  pt[0].fX = seg->fX1;
4798  pt[1].fX = seg->fX2;
4799  pt[0].fY = seg->fY1;
4800  pt[1].fY = seg->fY2;
4801 
4802  DrawPolyLine(2, pt, col, thick, kCoordModeOrigin);
4803  seg++;
4804  }
4805 }
4806 
4807 ////////////////////////////////////////////////////////////////////////////////
4808 /// Fill spans with specified color or/and stipple.
4809 
4810 void TASImage::FillSpans(UInt_t npt, TPoint *ppt, UInt_t *widths, const char *col,
4811  const char *stipple, UInt_t w, UInt_t h)
4812 {
4813  if (!InitVisual()) {
4814  Warning("FillSpans", "Visual not initiated");
4815  return;
4816  }
4817 
4818  if (!fImage) {
4819  Warning("FillSpans", "no image");
4820  return;
4821  }
4822 
4823  if (!fImage->alt.argb32) {
4824  BeginPaint();
4825  }
4826 
4827  if (!fImage->alt.argb32) {
4828  Warning("FillSpans", "Failed to get pixel array");
4829  return;
4830  }
4831 
4832  if (!npt || !ppt || !widths || (stipple && (!w || !h))) {
4833  Warning("FillSpans", "Invalid input data npt=%d ppt=0x%lx col=%s widths=0x%lx stipple=0x%lx w=%d h=%d",
4834  npt, (Long_t)ppt, col, (Long_t)widths, (Long_t)stipple, w, h);
4835  return;
4836  }
4837 
4838  ARGB32 color;
4839  parse_argb_color(col, &color);
4840  Int_t idx = 0;
4841  UInt_t x = 0;
4842  UInt_t yy;
4843 
4844  for (UInt_t i = 0; i < npt; i++) {
4845  yy = ppt[i].fY*fImage->width;
4846  for (UInt_t j = 0; j < widths[i]; j++) {
4847  if ((ppt[i].fX >= (Int_t)fImage->width) || (ppt[i].fX < 0) ||
4848  (ppt[i].fY >= (Int_t)fImage->height) || (ppt[i].fY < 0)) continue;
4849 
4850  x = ppt[i].fX + j;
4851  idx = Idx(yy + x);
4852 
4853  if (!stipple) {
4854  _alphaBlend(&fImage->alt.argb32[idx], &color);
4855  } else {
4856  Int_t ii = (ppt[i].fY%h)*w + x%w;
4857 
4858  if (stipple[ii >> 3] & (1 << (ii%8))) {
4859  _alphaBlend(&fImage->alt.argb32[idx], &color);
4860  }
4861  }
4862  }
4863  }
4864 }
4865 
4866 ////////////////////////////////////////////////////////////////////////////////
4867 /// Fill spans with tile image.
4868 
4869 void TASImage::FillSpans(UInt_t npt, TPoint *ppt, UInt_t *widths, TImage *tile)
4870 {
4871  if (!InitVisual()) {
4872  Warning("FillSpans", "Visual not initiated");
4873  return;
4874  }
4875 
4876  if (!fImage) {
4877  Warning("FillSpans", "no image");
4878  return;
4879  }
4880 
4881  if (!fImage->alt.argb32) {
4882  BeginPaint();
4883  }
4884 
4885  if (!fImage->alt.argb32) {
4886  Warning("FillSpans", "Failed to get pixel array");
4887  return;
4888  }
4889 
4890  if (!npt || !ppt || !widths || !tile) {
4891  Warning("FillSpans", "Invalid input data npt=%d ppt=0x%lx widths=0x%lx tile=0x%lx",
4892  npt, (Long_t)ppt, (Long_t)widths, (Long_t)tile);
4893  return;
4894  }
4895 
4896  Int_t idx = 0;
4897  Int_t ii = 0;
4898  UInt_t x = 0;
4899  UInt_t *arr = tile->GetArgbArray();
4900  if (!arr) return;
4901  UInt_t xx = 0;
4902  UInt_t yy = 0;
4903  UInt_t yyy = 0;
4904 
4905  for (UInt_t i = 0; i < npt; i++) {
4906  yyy = ppt[i].fY*fImage->width;
4907 
4908  for (UInt_t j = 0; j < widths[i]; j++) {
4909  if ((ppt[i].fX >= (Int_t)fImage->width) || (ppt[i].fX < 0) ||
4910  (ppt[i].fY >= (Int_t)fImage->height) || (ppt[i].fY < 0)) continue;
4911  x = ppt[i].fX + j;
4912  idx = Idx(yyy + x);
4913  xx = x%tile->GetWidth();
4914  yy = ppt[i].fY%tile->GetHeight();
4915  ii = yy*tile->GetWidth() + xx;
4916  _alphaBlend(&fImage->alt.argb32[idx], &arr[ii]);
4917  }
4918  yyy += fImage->width;;
4919  }
4920 }
4921 
4922 ////////////////////////////////////////////////////////////////////////////////
4923 /// Crop spans.
4924 
4925 void TASImage::CropSpans(UInt_t npt, TPoint *ppt, UInt_t *widths)
4926 {
4927  if (!InitVisual()) {
4928  Warning("CropSpans", "Visual not initiated");
4929  return;
4930  }
4931 
4932  if (!fImage) {
4933  Warning("CropSpans", "no image");
4934  return;
4935  }
4936 
4937  if (!fImage->alt.argb32) {
4938  BeginPaint();
4939  }
4940 
4941  if (!fImage->alt.argb32) {
4942  Warning("CropSpans", "Failed to get pixel array");
4943  return;
4944  }
4945 
4946  if (!npt || !ppt || !widths) {
4947  Warning("CropSpans", "No points specified npt=%d ppt=0x%lx widths=0x%lx", npt, (Long_t)ppt, (Long_t)widths);
4948  return;
4949  }
4950 
4951  int y0 = ppt[0].fY;
4952  int y1 = ppt[npt-1].fY;
4953  UInt_t y = 0;
4954  UInt_t x = 0;
4955  UInt_t i = 0;
4956  UInt_t idx = 0;
4957  UInt_t sz = fImage->width*fImage->height;
4958  UInt_t yy = y*fImage->width;
4959 
4960  for (y = 0; (int)y < y0; y++) {
4961  for (x = 0; x < fImage->width; x++) {
4962  idx = Idx(yy + x);
4963  if (idx < sz) fImage->alt.argb32[idx] = 0;
4964  }
4965  yy += fImage->width;
4966  }
4967 
4968  for (i = 0; i < npt; i++) {
4969  for (x = 0; (int)x < ppt[i].fX; x++) {
4970  idx = Idx(ppt[i].fY*fImage->width + x);
4971  if (idx < sz) fImage->alt.argb32[idx] = 0;
4972  }
4973  for (x = ppt[i].fX + widths[i] + 1; x < fImage->width; x++) {
4974  idx = Idx(ppt[i].fY*fImage->width + x);
4975  if (idx < sz) fImage->alt.argb32[idx] = 0;
4976  }
4977  }
4978 
4979  yy = y1*fImage->width;
4980  for (y = y1; y < fImage->height; y++) {
4981  for (x = 0; x < fImage->width; x++) {
4982  idx = Idx(yy + x);
4983  if (idx < sz) fImage->alt.argb32[idx] = 0;
4984  }
4985  yy += fImage->width;
4986  }
4987 }
4988 
4989 ////////////////////////////////////////////////////////////////////////////////
4990 /// Copy source region to the destination image. Copy is done according
4991 /// to specified function:
4992 /// ~~~ {.cpp}
4993 /// enum EGraphicsFunction {
4994 /// kGXclear = 0, // 0
4995 /// kGXand, // src AND dst
4996 /// kGXandReverse, // src AND NOT dst
4997 /// kGXcopy, // src (default)
4998 /// kGXandInverted, // NOT src AND dst
4999 /// kGXnoop, // dst
5000 /// kGXxor, // src XOR dst
5001 /// kGXor, // src OR dst
5002 /// kGXnor, // NOT src AND NOT dst
5003 /// kGXequiv, // NOT src XOR dst
5004 /// kGXinvert, // NOT dst
5005 /// kGXorReverse, // src OR NOT dst
5006 /// kGXcopyInverted, // NOT src
5007 /// kGXorInverted, // NOT src OR dst
5008 /// kGXnand, // NOT src OR NOT dst
5009 /// kGXset // 1
5010 /// };
5011 /// ~~~
5012 
5013 void TASImage::CopyArea(TImage *dst, Int_t xsrc, Int_t ysrc, UInt_t w, UInt_t h,
5014  Int_t xdst, Int_t ydst, Int_t gfunc, EColorChan)
5015 {
5016  if (!InitVisual()) {
5017  Warning("CopyArea", "Visual not initiated");
5018  return;
5019  }
5020 
5021  if (!fImage) {
5022  Warning("CopyArea", "no image");
5023  return;
5024  }
5025  if (!dst) return;
5026 
5027  ASImage *out = ((TASImage*)dst)->GetImage();
5028 
5029  int x = 0;
5030  int y = 0;
5031  int idx = 0;
5032  int idx2 = 0;
5033  xsrc = xsrc < 0 ? 0 : xsrc;
5034  ysrc = ysrc < 0 ? 0 : ysrc;
5035 
5036  if ((xsrc >= (int)fImage->width) || (ysrc >= (int)fImage->height)) return;
5037 
5038  w = xsrc + w > fImage->width ? fImage->width - xsrc : w;
5039  h = ysrc + h > fImage->height ? fImage->height - ysrc : h;
5040  UInt_t yy = (ysrc + y)*fImage->width;
5041 
5042  if (!fImage->alt.argb32) {
5043  BeginPaint();
5044  }
5045  if (!out->alt.argb32) {
5046  dst->BeginPaint();
5047  out = ((TASImage*)dst)->GetImage();
5048  }
5049 
5050  if (fImage->alt.argb32 && out->alt.argb32) {
5051  for (y = 0; y < (int)h; y++) {
5052  for (x = 0; x < (int)w; x++) {
5053  idx = Idx(yy + x + xsrc);
5054  if ((x + xdst < 0) || (ydst + y < 0) ||
5055  (x + xdst >= (int)out->width) || (y + ydst >= (int)out->height) ) continue;
5056 
5057  idx2 = Idx((ydst + y)*out->width + x + xdst);
5058 
5059  switch ((EGraphicsFunction)gfunc) {
5060  case kGXclear:
5061  out->alt.argb32[idx2] = 0;
5062  break;
5063  case kGXand:
5064  out->alt.argb32[idx2] &= fImage->alt.argb32[idx];
5065  break;
5066  case kGXandReverse:
5067  out->alt.argb32[idx2] = fImage->alt.argb32[idx] & (~out->alt.argb32[idx2]);
5068  break;
5069  case kGXandInverted:
5070  out->alt.argb32[idx2] &= ~fImage->alt.argb32[idx];
5071  break;
5072  case kGXnoop:
5073  break;
5074  case kGXxor:
5075  out->alt.argb32[idx2] ^= fImage->alt.argb32[idx];
5076  break;
5077  case kGXor:
5078  out->alt.argb32[idx2] |= fImage->alt.argb32[idx];
5079  break;
5080  case kGXnor:
5081  out->alt.argb32[idx2] = (~fImage->alt.argb32[idx]) & (~out->alt.argb32[idx2]);
5082  break;
5083  case kGXequiv:
5084  out->alt.argb32[idx2] ^= ~fImage->alt.argb32[idx];
5085  break;
5086  case kGXinvert:
5087  out->alt.argb32[idx2] = ~out->alt.argb32[idx2];
5088  break;
5089  case kGXorReverse:
5090  out->alt.argb32[idx2] = fImage->alt.argb32[idx] | (~out->alt.argb32[idx2]);
5091  break;
5092  case kGXcopyInverted:
5093  out->alt.argb32[idx2] = ~fImage->alt.argb32[idx];
5094  break;
5095  case kGXorInverted:
5096  out->alt.argb32[idx2] |= ~fImage->alt.argb32[idx];
5097  break;
5098  case kGXnand:
5099  out->alt.argb32[idx2] = (~fImage->alt.argb32[idx]) | (~out->alt.argb32[idx2]);
5100  break;
5101  case kGXset:
5102  out->alt.argb32[idx2] = 0xFFFFFFFF;
5103  break;
5104  case kGXcopy:
5105  default:
5106  out->alt.argb32[idx2] = fImage->alt.argb32[idx];
5107  break;
5108  }
5109  }
5110  yy += fImage->width;
5111  }
5112  }
5113 }
5114 
5115 ////////////////////////////////////////////////////////////////////////////////
5116 /// Draw a cell array.
5117 ///
5118 /// \param[in] x1,y1 : left down corner
5119 /// \param[in] x2,y2 : right up corner
5120 /// \param[in] nx,ny : array size
5121 /// \param[in] ic : array of ARGB32 colors
5122 ///
5123 /// Draw a cell array. The drawing is done with the pixel precision
5124 /// if (X2-X1)/NX (or Y) is not a exact pixel number the position of
5125 /// the top right corner may be wrong.
5126 
5127 void TASImage::DrawCellArray(Int_t x1, Int_t y1, Int_t x2, Int_t y2, Int_t nx,
5128  Int_t ny, UInt_t *ic)
5129 {
5130  int i, j, ix, iy, w, h;
5131 
5132  ARGB32 color = 0xFFFFFFFF;
5133  ARGB32 icol;
5134 
5135  w = TMath::Max((x2-x1)/(nx),1);
5136  h = TMath::Max((y1-y2)/(ny),1);
5137  ix = x1;
5138 
5139  for (i = 0; i < nx; i++) {
5140  iy = y1 - h;
5141  for (j = 0; j < ny; j++) {
5142  icol = (ARGB32)ic[i + (nx*j)];
5143  if (icol != color) {
5144  color = icol;
5145  }
5146  FillRectangleInternal((UInt_t)color, ix, iy, w, h);
5147  iy = iy - h;
5148  }
5149  ix = ix + w;
5150  }
5151 }
5152 
5153 ////////////////////////////////////////////////////////////////////////////////
5154 /// Return alpha-blended value computed from bottom and top pixel values.
5155 
5156 UInt_t TASImage::AlphaBlend(UInt_t bot, UInt_t top)
5157 {
5158  UInt_t ret = bot;
5159 
5160  _alphaBlend(&ret, &top);
5161  return ret;
5162 }
5163 
5164 ////////////////////////////////////////////////////////////////////////////////
5165 /// Return visual.
5166 
5167 const ASVisual *TASImage::GetVisual()
5168 {
5169  return fgVisual;
5170 }
5171 
5172 ////////////////////////////////////////////////////////////////////////////////
5173 /// Get poly bounds along Y.
5174 
5175 static int GetPolyYBounds(TPoint *pts, int n, int *by, int *ty)
5176 {
5177  TPoint *ptMin;
5178  int ymin, ymax;
5179  TPoint *ptsStart = pts;
5180 
5181  ptMin = pts;
5182  ymin = ymax = (pts++)->fY;
5183 
5184  while (--n > 0) {
5185  if (pts->fY < ymin) {
5186  ptMin = pts;
5187  ymin = pts->fY;
5188  }
5189  if (pts->fY > ymax) {
5190  ymax = pts->fY;
5191  }
5192  pts++;
5193  }
5194 
5195  *by = ymin;
5196  *ty = ymax;
5197  return (ptMin - ptsStart);
5198 }
5199 
5200 ////////////////////////////////////////////////////////////////////////////////
5201 /// The code is based on Xserver/mi/mipolycon.c
5202 /// "Copyright 1987, 1998 The Open Group"
5203 
5204 Bool_t TASImage::GetPolygonSpans(UInt_t npt, TPoint *ppt, UInt_t *nspans,
5205  TPoint **outPoint, UInt_t **outWidth)
5206 {
5207  int xl = 0; // x vals of leftedges
5208  int xr = 0; // x vals of right edges
5209  int dl = 0; // decision variables
5210  int dr = 0; // decision variables
5211  int ml = 0; // left edge slope
5212  int m1l = 0; // left edge slope+1
5213  int mr = 0, m1r = 0; // right edge slope and slope+1
5214  int incr1l = 0, incr2l = 0; // left edge error increments
5215  int incr1r = 0, incr2r = 0; // right edge error increments
5216  int dy; // delta y
5217  int y; // current scanline
5218  int left, right; // indices to first endpoints
5219  int i; // loop counter
5220  int nextleft, nextright; // indices to second endpoints
5221  TPoint *ptsOut; // output buffer
5222  UInt_t *width; // output buffer
5223  TPoint *firstPoint=0;
5224  UInt_t *firstWidth=0;
5225  int imin; // index of smallest vertex (in y)
5226  int ymin; // y-extents of polygon
5227  int ymax;
5228  Bool_t ret = kTRUE;
5229 
5230  *nspans = 0;
5231 
5232  if (!InitVisual()) {
5233  Warning("GetPolygonSpans", "Visual not initiated");
5234  return kFALSE;
5235  }
5236 
5237  if (!fImage) {
5238  Warning("GetPolygonSpans", "no image");
5239  return kFALSE;
5240  }
5241 
5242  if (!fImage->alt.argb32) {
5243  BeginPaint();
5244  }
5245 
5246  if (!fImage->alt.argb32) {
5247  Warning("GetPolygonSpans", "Failed to get pixel array");
5248  return kFALSE;
5249  }
5250 
5251  if ((npt < 3) || !ppt) {
5252  Warning("GetPolygonSpans", "No points specified npt=%d ppt=0x%lx", npt, (Long_t)ppt);
5253  return kFALSE;
5254  }
5255 
5256  // find leftx, bottomy, rightx, topy, and the index
5257  // of bottomy. Also translate the points.
5258  imin = GetPolyYBounds(ppt, npt, &ymin, &ymax);
5259 
5260  dy = ymax - ymin + 1;
5261  if ((npt < 3) || (dy < 0)) return kFALSE;
5262 
5263  ptsOut = firstPoint = new TPoint[dy];
5264  width = firstWidth = new UInt_t[dy];
5265  ret = kTRUE;
5266 
5267  nextleft = nextright = imin;
5268  y = ppt[nextleft].fY;
5269 
5270  // loop through all edges of the polygon
5271  do {
5272  // add a left edge if we need to
5273  if (ppt[nextleft].fY == y) {
5274  left = nextleft;
5275 
5276  // find the next edge, considering the end
5277  // conditions of the array.
5278  nextleft++;
5279  if (nextleft >= (int)npt) {
5280  nextleft = 0;
5281  }
5282 
5283  // now compute all of the random information
5284  // needed to run the iterative algorithm.
5285  BRESINITPGON(ppt[nextleft].fY - ppt[left].fY,
5286  ppt[left].fX, ppt[nextleft].fX,
5287  xl, dl, ml, m1l, incr1l, incr2l);
5288  }
5289 
5290  // add a right edge if we need to
5291  if (ppt[nextright].fY == y) {
5292  right = nextright;
5293 
5294  // find the next edge, considering the end
5295  // conditions of the array.
5296  nextright--;
5297  if (nextright < 0) {
5298  nextright = npt-1;
5299  }
5300 
5301  // now compute all of the random information
5302  // needed to run the iterative algorithm.
5303  BRESINITPGON(ppt[nextright].fY - ppt[right].fY,
5304  ppt[right].fX, ppt[nextright].fX,
5305  xr, dr, mr, m1r, incr1r, incr2r);
5306  }
5307 
5308  // generate scans to fill while we still have
5309  // a right edge as well as a left edge.
5310  i = TMath::Min(ppt[nextleft].fY, ppt[nextright].fY) - y;
5311 
5312  // in case of non-convex polygon
5313  if (i < 0) {
5314  delete [] firstWidth;
5315  delete [] firstPoint;
5316  return kTRUE;
5317  }
5318 
5319  while (i-- > 0) {
5320  ptsOut->fY = y;
5321 
5322  // reverse the edges if necessary
5323  if (xl < xr) {
5324  *(width++) = xr - xl;
5325  (ptsOut++)->fX = xl;
5326  } else {
5327  *(width++) = xl - xr;
5328  (ptsOut++)->fX = xr;
5329  }
5330  y++;
5331 
5332  // increment down the edges
5333  BRESINCRPGON(dl, xl, ml, m1l, incr1l, incr2l);
5334  BRESINCRPGON(dr, xr, mr, m1r, incr1r, incr2r);
5335  }
5336  } while (y != ymax);
5337 
5338  *nspans = UInt_t(ptsOut - firstPoint);
5339  *outPoint = firstPoint;
5340  *outWidth = firstWidth;
5341 
5342  return ret;
5343 }
5344 
5345 ////////////////////////////////////////////////////////////////////////////////
5346 /// Fill a convex polygon with background color or bitmap.
5347 /// For non convex polygon one must use DrawFillArea method
5348 
5349 void TASImage::FillPolygon(UInt_t npt, TPoint *ppt, const char *col,
5350  const char *stipple, UInt_t w, UInt_t h)
5351 {
5352  UInt_t nspans = 0;
5353  TPoint *firstPoint = 0; // output buffer
5354  UInt_t *firstWidth = 0; // output buffer
5355 
5356  Bool_t del = GetPolygonSpans(npt, ppt, &nspans, &firstPoint, &firstWidth);
5357  ARGB32 color = ARGB32_White;
5358  parse_argb_color(col, &color);
5359 
5360  if (nspans) {
5361  if (!stipple && ((color & 0xff000000)==0xff000000)) { //no stipple no alpha
5362  FillSpansInternal(nspans, firstPoint, firstWidth, color);
5363  } else {
5364  FillSpans(nspans, firstPoint, firstWidth, col, stipple, w, h);
5365  }
5366 
5367  if (del) {
5368  delete [] firstWidth;
5369  delete [] firstPoint;
5370  }
5371  } else {
5372  if (firstWidth) delete [] firstWidth;
5373  if (firstPoint) delete [] firstPoint;
5374  }
5375 }
5376 
5377 ////////////////////////////////////////////////////////////////////////////////
5378 /// Fill a convex polygon with background image.
5379 /// For non convex polygon one must use DrawFillArea method
5380 
5381 void TASImage::FillPolygon(UInt_t npt, TPoint *ppt, TImage *tile)
5382 {
5383  UInt_t nspans = 0;
5384  TPoint *firstPoint = 0; // output buffer
5385  UInt_t *firstWidth = 0; // output buffer
5386 
5387  Bool_t del = GetPolygonSpans(npt, ppt, &nspans, &firstPoint, &firstWidth);
5388 
5389  if (nspans) {
5390  FillSpans(nspans, firstPoint, firstWidth, tile);
5391 
5392  if (del) {
5393  delete [] firstWidth;
5394  delete [] firstPoint;
5395  }
5396  } else {
5397  if (firstWidth) delete [] firstWidth;
5398  if (firstPoint) delete [] firstPoint;
5399  }
5400 }
5401 
5402 ////////////////////////////////////////////////////////////////////////////////
5403 /// Crop a convex polygon.
5404 
5405 void TASImage::CropPolygon(UInt_t npt, TPoint *ppt)
5406 {
5407  UInt_t nspans = 0;
5408  TPoint *firstPoint = 0;
5409  UInt_t *firstWidth = 0;
5410 
5411  Bool_t del = GetPolygonSpans(npt, ppt, &nspans, &firstPoint, &firstWidth);
5412 
5413  if (nspans) {
5414  CropSpans(nspans, firstPoint, firstWidth);
5415 
5416  if (del) {
5417  delete [] firstWidth;
5418  delete [] firstPoint;
5419  }
5420  } else {
5421  if (firstWidth) delete [] firstWidth;
5422  if (firstPoint) delete [] firstPoint;
5423  }
5424 }
5425 
5426 static const UInt_t NUMPTSTOBUFFER = 512;
5427 
5428 ////////////////////////////////////////////////////////////////////////////////
5429 /// Fill a polygon (any type convex, non-convex).
5430 
5431 void TASImage::DrawFillArea(UInt_t count, TPoint *ptsIn, const char *col,
5432  const char *stipple, UInt_t w, UInt_t h)
5433 {
5434  if (!InitVisual()) {
5435  Warning("DrawFillArea", "Visual not initiated");
5436  return;
5437  }
5438 
5439  if (!fImage) {
5440  Warning("DrawFillArea", "no image");
5441  return;
5442  }
5443 
5444  if (!fImage->alt.argb32) {
5445  BeginPaint();
5446  }
5447 
5448  if (!fImage->alt.argb32) {
5449  Warning("DrawFillArea", "Failed to get pixel array");
5450  return;
5451  }
5452 
5453  if ((count < 3) || !ptsIn) {
5454  Warning("DrawFillArea", "No points specified npt=%d ppt=0x%lx", count, (Long_t)ptsIn);
5455  return;
5456  }
5457 
5458  if (count < 5) {
5459  FillPolygon(count, ptsIn, col, stipple, w, h);
5460  return;
5461  }
5462 
5463  ARGB32 color = ARGB32_White;
5464  parse_argb_color(col, &color);
5465 
5466  EdgeTableEntry *pAET; // the Active Edge Table
5467  int y; // the current scanline
5468  UInt_t nPts = 0; // number of pts in buffer
5469 
5470  ScanLineList *pSLL; // Current ScanLineList
5471  TPoint *ptsOut; // ptr to output buffers
5472  UInt_t *width;
5473  TPoint firstPoint[NUMPTSTOBUFFER]; // the output buffers
5474  UInt_t firstWidth[NUMPTSTOBUFFER];
5475  EdgeTableEntry *pPrevAET; // previous AET entry
5476  EdgeTable ET; // Edge Table header node
5477  EdgeTableEntry AET; // Active ET header node
5478  EdgeTableEntry *pETEs; // Edge Table Entries buff
5479  ScanLineListBlock SLLBlock; // header for ScanLineList
5480  Bool_t del = kTRUE;
5481 
5482  static const UInt_t gEdgeTableEntryCacheSize = 200;
5483  static EdgeTableEntry gEdgeTableEntryCache[gEdgeTableEntryCacheSize];
5484 
5485  if (count < gEdgeTableEntryCacheSize) {
5486  pETEs = (EdgeTableEntry*)&gEdgeTableEntryCache;
5487  del = kFALSE;
5488  } else {
5489  pETEs = new EdgeTableEntry[count];
5490  del = kTRUE;
5491  }
5492 
5493  ptsOut = firstPoint;
5494  width = firstWidth;
5495  CreateETandAET(count, ptsIn, &ET, &AET, pETEs, &SLLBlock);
5496  pSLL = ET.scanlines.next;
5497 
5498  for (y = ET.ymin; y < ET.ymax; y++) {
5499  if (pSLL && y == pSLL->scanline) {
5500  loadAET(&AET, pSLL->edgelist);
5501  pSLL = pSLL->next;
5502  }
5503  pPrevAET = &AET;
5504  pAET = AET.next;
5505 
5506  while (pAET) {
5507  ptsOut->fX = pAET->bres.minor_axis;
5508  ptsOut->fY = y;
5509  ptsOut++;
5510  nPts++;
5511 
5512  *width++ = pAET->next->bres.minor_axis - pAET->bres.minor_axis;
5513 
5514  if (nPts == NUMPTSTOBUFFER) {
5515  if (!stipple && ((color & 0xff000000)==0xff000000)) { //no stipple, no alpha
5516  FillSpansInternal(nPts, firstPoint, firstWidth, color);
5517  } else {
5518  FillSpans(nPts, firstPoint, firstWidth, col, stipple, w, h);
5519  }
5520  ptsOut = firstPoint;
5521  width = firstWidth;
5522  nPts = 0;
5523  }
5524  EVALUATEEDGEEVENODD(pAET, pPrevAET, y)
5525  EVALUATEEDGEEVENODD(pAET, pPrevAET, y)
5526  }
5527  InsertionSort(&AET);
5528  }
5529 
5530  if (nPts) {
5531  if (!stipple && ((color & 0xff000000)==0xff000000)) { //no stipple, no alpha
5532  FillSpansInternal(nPts, firstPoint, firstWidth, color);
5533  } else {
5534  FillSpans(nPts, firstPoint, firstWidth, col, stipple, w, h);
5535  }
5536  }
5537 
5538  if (del) delete [] pETEs;
5539  FreeStorage(SLLBlock.next);
5540 }
5541 
5542 ////////////////////////////////////////////////////////////////////////////////
5543 /// Fill a polygon (any type convex, non-convex).
5544 
5545 void TASImage::DrawFillArea(UInt_t count, TPoint *ptsIn, TImage *tile)
5546 {
5547  if (!InitVisual()) {
5548  Warning("DrawFillArea", "Visual not initiated");
5549  return;
5550  }
5551 
5552  if (!fImage) {
5553  Warning("DrawFillArea", "no image");
5554  return;
5555  }
5556 
5557  if (!fImage->alt.argb32) {
5558  BeginPaint();
5559  }
5560 
5561  if (!fImage->alt.argb32) {
5562  Warning("DrawFillArea", "Failed to get pixel array");
5563  return;
5564  }
5565 
5566  if ((count < 3) || !ptsIn) {
5567  Warning("DrawFillArea", "No points specified npt=%d ppt=0x%lx", count, (Long_t)ptsIn);
5568  return;
5569  }
5570 
5571  if (count < 5) {
5572  FillPolygon(count, ptsIn, tile);
5573  return;
5574  }
5575 
5576  EdgeTableEntry *pAET; // the Active Edge Table
5577  int y; // the current scanline
5578  UInt_t nPts = 0; // number of pts in buffer
5579 
5580  ScanLineList *pSLL; // Current ScanLineList
5581  TPoint *ptsOut; // ptr to output buffers
5582  UInt_t *width;
5583  TPoint firstPoint[NUMPTSTOBUFFER]; // the output buffers
5584  UInt_t firstWidth[NUMPTSTOBUFFER];
5585  EdgeTableEntry *pPrevAET; // previous AET entry
5586  EdgeTable ET; // Edge Table header node
5587  EdgeTableEntry AET; // Active ET header node
5588  EdgeTableEntry *pETEs; // Edge Table Entries buff
5589  ScanLineListBlock SLLBlock; // header for ScanLineList
5590 
5591  pETEs = new EdgeTableEntry[count];
5592 
5593  ptsOut = firstPoint;
5594  width = firstWidth;
5595  CreateETandAET(count, ptsIn, &ET, &AET, pETEs, &SLLBlock);
5596  pSLL = ET.scanlines.next;
5597 
5598  for (y = ET.ymin; y < ET.ymax; y++) {
5599  if (pSLL && y == pSLL->scanline) {
5600  loadAET(&AET, pSLL->edgelist);
5601  pSLL = pSLL->next;
5602  }
5603  pPrevAET = &AET;
5604  pAET = AET.next;
5605 
5606  while (pAET) {
5607  ptsOut->fX = pAET->bres.minor_axis;
5608  ptsOut->fY = y;
5609  ptsOut++;
5610  nPts++;
5611 
5612  *width++ = pAET->next->bres.minor_axis - pAET->bres.minor_axis;
5613 
5614  if (nPts == NUMPTSTOBUFFER) {
5615  FillSpans(nPts, firstPoint, firstWidth, tile);
5616  ptsOut = firstPoint;
5617  width = firstWidth;
5618  nPts = 0;
5619  }
5620  EVALUATEEDGEEVENODD(pAET, pPrevAET, y)
5621  EVALUATEEDGEEVENODD(pAET, pPrevAET, y)
5622  }
5623  InsertionSort(&AET);
5624  }
5625  FillSpans(nPts, firstPoint, firstWidth, tile);
5626 
5627  delete [] pETEs;
5628  FreeStorage(SLLBlock.next);
5629 }
5630 
5631 ////////////////////////////////////////////////////////////////////////////////
5632 /// Create draw context.
5633 
5634 static ASDrawContext *create_draw_context_argb32(ASImage *im, ASDrawTool *brush)
5635 {
5636  ASDrawContext *ctx = new ASDrawContext;
5637 
5638  ctx->canvas_width = im->width;
5639  ctx->canvas_height = im->height;
5640  ctx->canvas = im->alt.argb32;
5641  ctx->scratch_canvas = 0;
5642 
5643  ctx->flags = ASDrawCTX_CanvasIsARGB;
5644  asim_set_custom_brush_colored( ctx, brush);
5645  return ctx;
5646 }
5647 
5648 ////////////////////////////////////////////////////////////////////////////////
5649 /// Destroy asdraw context32.
5650 
5651 static void destroy_asdraw_context32( ASDrawContext *ctx )
5652 {
5653  if (ctx) {
5654  if (ctx->scratch_canvas) free(ctx->scratch_canvas);
5655  delete ctx;
5656  }
5657 }
5658 
5659 static const UInt_t kBrushCacheSize = 20;
5660 static CARD32 gBrushCache[kBrushCacheSize*kBrushCacheSize];
5661 
5662 ////////////////////////////////////////////////////////////////////////////////
5663 /// Draw wide line.
5664 
5665 void TASImage::DrawWideLine(UInt_t x1, UInt_t y1, UInt_t x2, UInt_t y2,
5666  UInt_t color, UInt_t thick)
5667 {
5668  Int_t sz = thick*thick;
5669  CARD32 *matrix;
5670  Bool_t use_cache = thick < kBrushCacheSize;
5671 
5672  if (use_cache) {
5673  matrix = gBrushCache;
5674  } else {
5675  matrix = new CARD32[sz];
5676  }
5677 
5678  for (int i = 0; i < sz; i++) {
5679  matrix[i] = (CARD32)color;
5680  };
5681 
5682  ASDrawTool brush;
5683  brush.matrix = matrix;
5684  brush.width = thick;
5685  brush.height = thick;
5686  brush.center_y = brush.center_x = thick/2;
5687 
5688  // When the first or last point of a wide line is exactly on the
5689  // window limit the line is drawn vertically or horizontally.
5690  // see https://sft.its.cern.ch/jira/browse/ROOT-8021
5691  UInt_t xx1 = x1;
5692  UInt_t yy1 = y1;
5693  UInt_t xx2 = x2;
5694  UInt_t yy2 = y2;
5695  if (xx1 == fImage->width) --xx1;
5696  if (yy1 == fImage->height) --yy1;
5697  if (xx2 == fImage->width) --xx2;
5698  if (yy2 == fImage->height) --yy2;
5699  ASDrawContext *ctx = create_draw_context_argb32(fImage, &brush);
5700  asim_move_to(ctx, xx1, yy1);
5701  asim_line_to(ctx, xx2, yy2);
5702 
5703  if (!use_cache) {
5704  delete [] matrix;
5705  }
5706  destroy_asdraw_context32(ctx);
5707 }
5708 
5709 ////////////////////////////////////////////////////////////////////////////////
5710 /// Draw glyph bitmap.
5711 
5712 void TASImage::DrawGlyph(void *bitmap, UInt_t color, Int_t bx, Int_t by)
5713 {
5714  static UInt_t col[5];
5715  Int_t x, y, yy, y0, xx;
5716  Bool_t has_alpha = (color & 0xff000000) != 0xff000000;
5717 
5718  ULong_t r, g, b;
5719  int idx = 0;
5720  FT_Bitmap *source = (FT_Bitmap*)bitmap;
5721  UChar_t d = 0, *s = source->buffer;
5722 
5723  Int_t dots = Int_t(source->width * source->rows);
5724  r = g = b = 0;
5725  Int_t bxx, byy;
5726 
5727  yy = y0 = by > 0 ? by * fImage->width : 0;
5728  for (y = 0; y < (int) source->rows; y++) {
5729  byy = by + y;
5730  if ((byy >= (int)fImage->height) || (byy <0)) continue;
5731 
5732  for (x = 0; x < (int) source->width; x++) {
5733  bxx = bx + x;
5734  if ((bxx >= (int)fImage->width) || (bxx < 0)) continue;
5735 
5736  idx = Idx(bxx + yy);
5737  r += ((fImage->alt.argb32[idx] & 0xff0000) >> 16);
5738  g += ((fImage->alt.argb32[idx] & 0x00ff00) >> 8);
5739  b += (fImage->alt.argb32[idx] & 0x0000ff);
5740  }
5741  yy += fImage->width;
5742  }
5743  if (dots != 0) {
5744  r /= dots;
5745  g /= dots;
5746  b /= dots;
5747  }
5748 
5749  col[0] = (r << 16) + (g << 8) + b;
5750  col[4] = color;
5751  Int_t col4r = (col[4] & 0xff0000) >> 16;
5752  Int_t col4g = (col[4] & 0x00ff00) >> 8;
5753  Int_t col4b = (col[4] & 0x0000ff);
5754 
5755  // interpolate between fore and background colors
5756  for (x = 3; x > 0; x--) {
5757  xx = 4-x;
5758  Int_t colxr = (col4r*x + r*xx) >> 2;
5759  Int_t colxg = (col4g*x + g*xx) >> 2;
5760  Int_t colxb = (col4b*x + b*xx) >> 2;
5761  col[x] = (colxr << 16) + (colxg << 8) + colxb;
5762  }
5763 
5764  yy = y0;
5765  ARGB32 acolor;
5766 
5767  Int_t clipx1=0, clipx2=0, clipy1=0, clipy2=0;
5768  Bool_t noClip = kTRUE;
5769 
5770  if (gPad) {
5771  Float_t is = gStyle->GetImageScaling();
5772  clipx1 = gPad->XtoAbsPixel(gPad->GetX1())*is;
5773  clipx2 = gPad->XtoAbsPixel(gPad->GetX2())*is;
5774  clipy1 = gPad->YtoAbsPixel(gPad->GetY1())*is;
5775  clipy2 = gPad->YtoAbsPixel(gPad->GetY2())*is;
5776  noClip = kFALSE;
5777  }
5778 
5779  for (y = 0; y < (int) source->rows; y++) {
5780  byy = by + y;
5781 
5782  for (x = 0; x < (int) source->width; x++) {
5783  bxx = bx + x;
5784 
5785  d = *s++ & 0xff;
5786  d = ((d + 10) * 5) >> 8;
5787  if (d > 4) d = 4;
5788 
5789  if (d) {
5790  if ( noClip || ((x < (int) source->width) &&
5791  (bxx < (int)clipx2) && (bxx >= (int)clipx1) &&
5792  (byy >= (int)clipy2) && (byy < (int)clipy1) )) {
5793  idx = Idx(bxx + yy);
5794  acolor = (ARGB32)col[d];
5795  if (has_alpha) {
5796  _alphaBlend(&fImage->alt.argb32[idx], &acolor);
5797  } else {
5798  fImage->alt.argb32[idx] = acolor;
5799  }
5800  }
5801  }
5802  }
5803  yy += fImage->width;
5804  }
5805 }
5806 
5807 ////////////////////////////////////////////////////////////////////////////////
5808 /// Draw text at the pixel position (x,y).
5809 
5810 void TASImage::DrawText(TText *text, Int_t x, Int_t y)
5811 {
5812  if (!text) return;
5813  if (!fImage) return;
5814  if (!gPad) return;
5815 
5816  if (!InitVisual()) {
5817  Warning("DrawText", "Visual not initiated");
5818  return;
5819  }
5820 
5821  if (!fImage->alt.argb32) {
5822  BeginPaint();
5823  }
5824 
5825  if (!TTF::IsInitialized()) TTF::Init();
5826 
5827  // set text font
5828  TTF::SetTextFont(text->GetTextFont());
5829 
5830  Int_t wh = gPad->XtoPixel(gPad->GetX2());
5831  Int_t hh = gPad->YtoPixel(gPad->GetY1());
5832 
5833  // set text size
5834  Float_t ttfsize;
5835  if (wh < hh) {
5836  ttfsize = text->GetTextSize()*wh;
5837  } else {
5838  ttfsize = text->GetTextSize()*hh;
5839  }
5840  TTF::SetTextSize(ttfsize*kScale);
5841 
5842  // set text angle
5843  TTF::SetRotationMatrix(text->GetTextAngle());
5844 
5845  // set text
5846  const wchar_t *wcsTitle = reinterpret_cast<const wchar_t *>(text->GetWcsTitle());
5847  if (wcsTitle != NULL) {
5848  TTF::PrepareString(wcsTitle);
5849  } else {
5850  TTF::PrepareString(text->GetTitle());
5851  }
5852  TTF::LayoutGlyphs();
5853 
5854  // color
5855  TColor *col = gROOT->GetColor(text->GetTextColor());
5856  if (!col) { // no color, make it black
5857  col = gROOT->GetColor(1);
5858  if (!col) return;
5859  }
5860  ARGB32 color = ARGB32_White;
5861  parse_argb_color(col->AsHexString(), &color);
5862 
5863  // Align()
5864  Int_t align = 0;
5865  Int_t txalh = text->GetTextAlign()/10;
5866  Int_t txalv = text->GetTextAlign()%10;
5867 
5868  switch (txalh) {
5869  case 0 :
5870  case 1 :
5871  switch (txalv) { //left
5872  case 1 :
5873  align = 7; //bottom
5874  break;
5875  case 2 :
5876  align = 4; //center
5877  break;
5878  case 3 :
5879  align = 1; //top
5880  break;
5881  }
5882  break;
5883  case 2 :
5884  switch (txalv) { //center
5885  case 1 :
5886  align = 8; //bottom
5887  break;
5888  case 2 :
5889  align = 5; //center
5890  break;
5891  case 3 :
5892  align = 2; //top
5893  break;
5894  }
5895  break;
5896  case 3 :
5897  switch (txalv) { //right
5898  case 1 :
5899  align = 9; //bottom
5900  break;
5901  case 2 :
5902  align = 6; //center
5903  break;
5904  case 3 :
5905  align = 3; //top
5906  break;
5907  }
5908  break;
5909  }
5910 
5911  FT_Vector ftal;
5912 
5913  // vertical alignment
5914  if (align == 1 || align == 2 || align == 3) {
5915  ftal.y = TTF::GetAscent();
5916  } else if (align == 4 || align == 5 || align == 6) {
5917  ftal.y = TTF::GetAscent()/2;
5918  } else {
5919  ftal.y = 0;
5920  }
5921 
5922  // horizontal alignment
5923  if (align == 3 || align == 6 || align == 9) {
5924  ftal.x = TTF::GetWidth();
5925  } else if (align == 2 || align == 5 || align == 8) {
5926  ftal.x = TTF::GetWidth()/2;
5927  } else {
5928  ftal.x = 0;
5929  }
5930 
5931  FT_Vector_Transform(&ftal, TTF::GetRotMatrix());
5932  ftal.x = (ftal.x >> 6);
5933  ftal.y = (ftal.y >> 6);
5934 
5935  TTF::TTGlyph *glyph = TTF::GetGlyphs();
5936 
5937  for (int n = 0; n < TTF::GetNumGlyphs(); n++, glyph++) {
5938  if (FT_Glyph_To_Bitmap(&glyph->fImage, ft_render_mode_normal, 0, 1 )) continue;
5939 
5940  FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyph->fImage;
5941  FT_Bitmap *source = &bitmap->bitmap;
5942 
5943  Int_t bx = x - ftal.x + bitmap->left;
5944  Int_t by = y + ftal.y - bitmap->top;
5945 
5946  DrawGlyph(source, color, bx, by);
5947  }
5948 }
5949 
5950 ////////////////////////////////////////////////////////////////////////////////
5951 /// Draw text using TrueType fonts.
5952 
5953 void TASImage::DrawTextTTF(Int_t x, Int_t y, const char *text, Int_t size,
5954  UInt_t color, const char *font_name, Float_t angle)
5955 {
5956  if (!TTF::IsInitialized()) TTF::Init();
5957 
5958  TTF::SetTextFont(font_name);
5959  TTF::SetTextSize(size);
5960  TTF::SetRotationMatrix(angle);
5961  TTF::PrepareString(text);
5962  TTF::LayoutGlyphs();
5963 
5964  TTF::TTGlyph *glyph = TTF::GetGlyphs();
5965 
5966  // compute the size and position that will contain the text
5967  // Int_t Xoff = 0; if (TTF::GetBox().xMin < 0) Xoff = -TTF::GetBox().xMin;
5968  Int_t Yoff = 0; if (TTF::GetBox().yMin < 0) Yoff = -TTF::GetBox().yMin;
5969  Int_t h = TTF::GetBox().yMax + Yoff;
5970 
5971  for (int n = 0; n < TTF::GetNumGlyphs(); n++, glyph++) {
5972  if (FT_Glyph_To_Bitmap(&glyph->fImage, ft_render_mode_normal, 0, 1 )) continue;
5973 
5974  FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyph->fImage;
5975  FT_Bitmap *source = &bitmap->bitmap;
5976 
5977  Int_t bx = x + bitmap->left;
5978  Int_t by = y + h - bitmap->top;
5979  DrawGlyph(source, color, bx, by);
5980  }
5981 }
5982 
5983 ////////////////////////////////////////////////////////////////////////////////
5984 /// Return in-memory buffer compressed according image type.
5985 /// Buffer must be deallocated after usage.
5986 /// This method can be used for sending images over network.
5987 
5988 void TASImage::GetImageBuffer(char **buffer, int *size, EImageFileTypes type)
5989 {
5990  static ASImageExportParams params;
5991  Bool_t ret = kFALSE;
5992  int isize = 0;
5993  char *ibuff = 0;
5994  ASImage *img = fScaledImage ? fScaledImage->fImage : fImage;
5995 
5996  if (!img) return;
5997 
5998  switch (type) {
5999  case TImage::kXpm:
6000  ret = ASImage2xpmRawBuff(img, (CARD8 **)buffer, size, 0);
6001  break;
6002  default:
6003  ret = ASImage2PNGBuff(img, (CARD8 **)buffer, size, &params);
6004  }
6005 
6006  if (!ret) {
6007  *size = isize;
6008  *buffer = ibuff;
6009  }
6010 }
6011 
6012 ////////////////////////////////////////////////////////////////////////////////
6013 /// Create image from compressed buffer.
6014 /// Supported formats:
6015 ///
6016 /// - PNG - by default
6017 /// - XPM - two options exist:
6018 /// 1. xpm as a single string (raw buffer). Such string
6019 /// is returned by GetImageBuffer method.
6020 /// For example:
6021 /// ~~~ {.cpp}
6022 /// char *buf;
6023 /// int sz;
6024 /// im1->GetImageBuffer(&buf, &int, TImage::kXpm); /*raw buffer*/
6025 /// TImage *im2 = TImage::Create();
6026 /// im2->SetImageBuffer(&buf, TImage::kXpm);
6027 /// ~~~
6028 /// 2. xpm as an array of strings (pre-parsed)
6029 /// ~~~ {.cpp}
6030 /// For example:
6031 /// char *xpm[] = {
6032 /// "64 28 58 1",
6033 /// " c #0A030C",
6034 /// ". c #1C171B"
6035 /// ...
6036 /// TImage *im = TImage::Create();
6037 /// im->SetImageBuffer(xpm, TImage::kXpm);
6038 /// ~~~
6039 
6040 Bool_t TASImage::SetImageBuffer(char **buffer, EImageFileTypes type)
6041 {
6042  DestroyImage();
6043 
6044  static ASImageImportParams params;
6045  params.flags = 0;
6046  params.width = 0;
6047  params.height = 0 ;
6048  params.filter = SCL_DO_ALL;
6049  params.gamma = 0;
6050  params.gamma_table = 0;
6051  params.compression = 0;
6052  params.format = ASA_ASImage;
6053  params.search_path = 0;
6054  params.subimage = 0;
6055 
6056  switch (type) {
6057  case TImage::kXpm:
6058  {
6059  char *ptr = buffer[0];
6060  while (isspace((int)*ptr)) ++ptr;
6061  if (atoi(ptr)) { // pre-parsed and preloaded data
6062  fImage = xpm_data2ASImage((const char**)buffer, &params);
6063  } else {
6064  fImage = xpmRawBuff2ASImage((const char*)*buffer, &params);
6065  }
6066  break;
6067  }
6068  default:
6069  fImage = PNGBuff2ASimage((CARD8 *)*buffer, &params);
6070  break;
6071  }
6072 
6073  if (!fImage) {
6074  return kFALSE;
6075  }
6076 
6077  if (fName.IsNull()) {
6078  fName.Form("img_%dx%d.%d", fImage->width, fImage->height, gRandom->Integer(1000));
6079  }
6080  UnZoom();
6081  return kTRUE;
6082 }
6083 
6084 ////////////////////////////////////////////////////////////////////////////////
6085 /// Create image thumbnail.
6086 
6087 void TASImage::CreateThumbnail()
6088 {
6089  int size;
6090  const int sz = 64;
6091 
6092  if (!fImage) {
6093  return;
6094  }
6095 
6096  if (!InitVisual()) {
6097  return;
6098  }
6099 
6100  static char *buf = 0;
6101  int w, h;
6102  ASImage *img = 0;
6103 
6104  if (fImage->width > fImage->height) {
6105  w = sz;
6106  h = (fImage->height*sz)/fImage->width;
6107  } else {
6108  h = sz;
6109  w = (fImage->width*sz)/fImage->height;
6110  }
6111 
6112  w = w < 8 ? 8 : w;
6113  h = h < 8 ? 8 : h;
6114 
6115  img = scale_asimage(fgVisual, fImage, w, h, ASA_ASImage,
6116  GetImageCompression(), GetImageQuality());
6117  if (!img) {
6118  return;
6119  }
6120 
6121  // contrasting
6122  ASImage *rendered_im;
6123  ASImageLayer layers[2];
6124  init_image_layers(&(layers[0]), 2);
6125  layers[0].im = img;
6126  layers[0].dst_x = 0;
6127  layers[0].dst_y = 0;
6128  layers[0].clip_width = img->width;
6129  layers[0].clip_height = img->height;
6130  layers[0].bevel = 0;
6131  layers[1].im = img;
6132  layers[1].dst_x = 0;
6133  layers[1].dst_y = 0;
6134  layers[1].clip_width = img->width;
6135  layers[1].clip_height = img->height;
6136  layers[1].merge_scanlines = blend_scanlines_name2func("tint");
6137  rendered_im = merge_layers(fgVisual, &(layers[0]), 2, img->width, img->height,
6138  ASA_ASImage, GetImageCompression(), GetImageQuality());
6139  destroy_asimage(&img);
6140  img = rendered_im;
6141 
6142  // pad image
6143  ASImage *padimg = 0;
6144  int d = 0;
6145 
6146  if (w == sz) {
6147  d = (sz - h) >> 1;
6148  padimg = pad_asimage(fgVisual, img, 0, d, sz, sz, 0x00ffffff,
6149  ASA_ASImage, GetImageCompression(), GetImageQuality());
6150  } else {
6151  d = (sz - w) >> 1;
6152  padimg = pad_asimage(fgVisual, img, d, 0, sz, sz, 0x00ffffff,
6153  ASA_ASImage, GetImageCompression(), GetImageQuality());
6154  }
6155 
6156  if (!padimg) {
6157  destroy_asimage(&img);
6158  return;
6159  }
6160 
6161  void *ptr = &buf;
6162  ASImage2xpmRawBuff(padimg, (CARD8 **)ptr, &size, 0);
6163  fTitle = buf;
6164 
6165  destroy_asimage(&padimg);
6166 }
6167 
6168 ////////////////////////////////////////////////////////////////////////////////
6169 /// Streamer for ROOT I/O.
6170 
6171 void TASImage::Streamer(TBuffer &b)
6172 {
6173  Bool_t image_type = 0;
6174  char *buffer = 0;
6175  int size = 0;
6176  int w, h;
6177  UInt_t R__s, R__c;
6178 
6179  if (b.IsReading()) {
6180  Version_t version = b.ReadVersion(&R__s, &R__c);
6181  if (version == 0) { //dumb prototype for schema evolution
6182  return;
6183  }
6184 
6185  if ( version == 1 ) {
6186  Int_t fileVersion = b.GetVersionOwner();
6187  if (fileVersion > 0 && fileVersion < 50000 ) {
6188  TImage::Streamer(b);
6189  b >> fMaxValue;
6190  b >> fMinValue;
6191  b >> fZoomOffX;
6192  b >> fZoomOffY;
6193  b >> fZoomWidth;
6194  b >> fZoomHeight;
6195  if ( fileVersion < 40200 ) {
6196  Bool_t zoomUpdate;
6197  b >> zoomUpdate;
6198  fZoomUpdate = zoomUpdate;
6199  } else {
6200  b >> fZoomUpdate;
6201  b >> fEditable;
6202  Bool_t paintMode;
6203  b >> paintMode;
6204  fPaintMode = paintMode;
6205  }
6206  b.CheckByteCount(R__s, R__c, TASImage::IsA());
6207  return;
6208  }
6209  }
6210 
6211  TNamed::Streamer(b);
6212  b >> image_type;
6213 
6214  if (image_type != 0) { // read PNG compressed image
6215  b >> size;
6216  buffer = new char[size];
6217  b.ReadFastArray(buffer, size);
6218  SetImageBuffer(&buffer, TImage::kPng);
6219  delete [] buffer;
6220  } else { // read vector with palette
6221  TAttImage::Streamer(b);
6222  b >> w;
6223  b >> h;
6224  size = w*h;
6225  Double_t *vec = new Double_t[size];
6226  b.ReadFastArray(vec, size);
6227  SetImage(vec, w, h, &fPalette);
6228  delete [] vec;
6229  }
6230  b.CheckByteCount(R__s, R__c, TASImage::IsA());
6231  } else {
6232  if (!fImage) {
6233  return;
6234  }
6235  R__c = b.WriteVersion(TASImage::IsA(), kTRUE);
6236 
6237  if (fName.IsNull()) {
6238  fName.Form("img_%dx%d.%d", fImage->width, fImage->height, gRandom->Integer(1000));
6239  }
6240  TNamed::Streamer(b);
6241 
6242  image_type = fImage->alt.vector ? 0 : 1;
6243  b << image_type;
6244 
6245  if (image_type != 0) { // write PNG compressed image
6246  GetImageBuffer(&buffer, &size, TImage::kPng);
6247  b << size;
6248  b.WriteFastArray(buffer, size);
6249  delete buffer;
6250  } else { // write vector with palette
6251  TAttImage::Streamer(b);
6252  b << fImage->width;
6253  b << fImage->height;
6254  b.WriteFastArray(fImage->alt.vector, fImage->width*fImage->height);
6255  }
6256  b.SetByteCount(R__c, kTRUE);
6257  }
6258 }
6259 
6260 ////////////////////////////////////////////////////////////////////////////////
6261 /// Browse image.
6262 
6263 void TASImage::Browse(TBrowser *)
6264 {
6265  if (fImage->alt.vector) {
6266  Draw("n");
6267  } else {
6268  Draw("nxxx");
6269  }
6270  CreateThumbnail();
6271 }
6272 
6273 ////////////////////////////////////////////////////////////////////////////////
6274 /// Title is used to keep 32x32 xpm image's thumbnail.
6275 
6276 const char *TASImage::GetTitle() const
6277 {
6278  if (!gDirectory || !gDirectory->IsWritable()) {
6279  return 0;
6280  }
6281 
6282  TASImage *mutble = (TASImage *)this;
6283 
6284  if (fTitle.IsNull()) {
6285  mutble->SetTitle(fName.Data());
6286  }
6287 
6288  return fTitle.Data();
6289 }
6290 
6291 ////////////////////////////////////////////////////////////////////////////////
6292 /// Set a title for an image.
6293 
6294 void TASImage::SetTitle(const char *title)
6295 {
6296  if (fTitle.IsNull()) {
6297  CreateThumbnail();
6298  }
6299 
6300  if (fTitle.IsNull()) {
6301  return;
6302  }
6303 
6304  int start = fTitle.Index("/*") + 3;
6305  int stop = fTitle.Index("*/") - 1;
6306 
6307  if ((start > 0) && (stop - start > 0)) {
6308  fTitle.Replace(start, stop - start, title);
6309  }
6310 }
6311 
6312 ////////////////////////////////////////////////////////////////////////////////
6313 /// Draw a cubic bezier line.
6314 
6315 void TASImage::DrawCubeBezier(Int_t x1, Int_t y1, Int_t x2, Int_t y2,
6316  Int_t x3, Int_t y3, const char *col, UInt_t thick)
6317 {
6318  Int_t sz = thick*thick;
6319  CARD32 *matrix;
6320  Bool_t use_cache = thick < kBrushCacheSize;
6321 
6322  ARGB32 color = ARGB32_White;
6323  parse_argb_color(col, &color);
6324 
6325  if (use_cache) {
6326  matrix = gBrushCache;
6327  } else {
6328  matrix = new CARD32[sz];
6329  }
6330 
6331  for (int i = 0; i < sz; i++) {
6332  matrix[i] = (CARD32)color;
6333  };
6334 
6335  ASDrawTool brush;
6336  brush.matrix = matrix;
6337  brush.width = thick;
6338  brush.height = thick;
6339  brush.center_y = brush.center_x = thick/2;
6340 
6341  ASDrawContext *ctx = 0;
6342 
6343  ctx = create_draw_context_argb32(fImage, &brush);
6344  asim_cube_bezier(ctx, x1, y1, x2, y2, x3, y3);
6345 
6346  if (!use_cache) {
6347  delete [] matrix;
6348  }
6349  destroy_asdraw_context32(ctx);
6350 }
6351 
6352 ////////////////////////////////////////////////////////////////////////////////
6353 /// Draw a straight ellipse.
6354 /// If thick < 0 - draw filled ellipse.
6355 
6356 void TASImage::DrawStraightEllips(Int_t x, Int_t y, Int_t rx, Int_t ry,
6357  const char *col, Int_t thick)
6358 {
6359  thick = !thick ? 1 : thick;
6360  Int_t sz = thick*thick;
6361  CARD32 *matrix;
6362  Bool_t use_cache = (thick > 0) && ((UInt_t)thick < kBrushCacheSize);
6363 
6364  ARGB32 color = ARGB32_White;
6365  parse_argb_color(col, &color);
6366 
6367  if (use_cache) {
6368  matrix = gBrushCache;
6369  } else {
6370  matrix = new CARD32[sz];
6371  }
6372 
6373  for (int i = 0; i < sz; i++) {
6374  matrix[i] = (CARD32)color;
6375  };
6376 
6377  ASDrawTool brush;
6378  brush.matrix = matrix;
6379  brush.width = thick > 0 ? thick : 1;
6380  brush.height = thick > 0 ? thick : 1;
6381  brush.center_y = brush.center_x = thick > 0 ? thick/2 : 0;
6382 
6383  ASDrawContext *ctx = create_draw_context_argb32(fImage, &brush);
6384  asim_straight_ellips(ctx, x, y, rx, ry, thick < 0);
6385 
6386  if (!use_cache) {
6387  delete [] matrix;
6388  }
6389  destroy_asdraw_context32(ctx);
6390 }
6391 
6392 ////////////////////////////////////////////////////////////////////////////////
6393 /// Draw a circle.
6394 /// If thick < 0 - draw filled circle
6395 
6396 void TASImage::DrawCircle(Int_t x, Int_t y, Int_t r, const char *col, Int_t thick)
6397 {
6398  thick = !thick ? 1 : thick;
6399  Int_t sz = thick*thick;
6400  CARD32 *matrix;
6401  Bool_t use_cache = (thick > 0) && ((UInt_t)thick < kBrushCacheSize);
6402 
6403  ARGB32 color = ARGB32_White;
6404  parse_argb_color(col, &color);
6405 
6406 ///matrix = new CARD32[sz];
6407  if (use_cache) {
6408  matrix = gBrushCache;
6409  } else {
6410  matrix = new CARD32[sz];
6411  }
6412 
6413  for (int i = 0; i < sz; i++) {
6414  matrix[i] = (CARD32)color;
6415  }
6416 
6417  ASDrawTool brush;
6418  brush.matrix = matrix;
6419  brush.height = brush.width = thick > 0 ? thick : 1;
6420  brush.center_y = brush.center_x = thick > 0 ? thick/2 : 0;
6421 
6422  ASDrawContext *ctx = create_draw_context_argb32(fImage, &brush);
6423  asim_circle(ctx, x, y, r, thick < 0);
6424 
6425 ///free (matrix);
6426  if (!use_cache) {
6427  delete [] matrix;
6428  }
6429  destroy_asdraw_context32(ctx);
6430 }
6431 
6432 ////////////////////////////////////////////////////////////////////////////////
6433 /// Draw an ellipse.
6434 /// If thick < 0 - draw filled ellips
6435 
6436 void TASImage::DrawEllips(Int_t x, Int_t y, Int_t rx, Int_t ry, Int_t angle,
6437  const char *col, Int_t thick)
6438 {
6439  thick = !thick ? 1 : thick;
6440  Int_t sz = thick*thick;
6441  CARD32 *matrix;
6442  Bool_t use_cache = (thick > 0) && ((UInt_t)thick < kBrushCacheSize);
6443 
6444  ARGB32 color = ARGB32_White;
6445  parse_argb_color(col, &color);
6446 
6447  if (use_cache) {
6448  matrix = gBrushCache;
6449  } else {
6450  matrix = new CARD32[sz];
6451  }
6452 
6453  for (int i = 0; i < sz; i++) {
6454  matrix[i] = (CARD32)color;
6455  };
6456 
6457  ASDrawTool brush;
6458  brush.matrix = matrix;
6459  brush.width = thick > 0 ? thick : 1;
6460  brush.height = thick > 0 ? thick : 1;
6461  brush.center_y = brush.center_x = thick > 0 ? thick/2 : 0;
6462 
6463  ASDrawContext *ctx = create_draw_context_argb32(fImage, &brush);
6464  asim_ellips(ctx, x, y, rx, ry, angle, thick < 0);
6465 
6466  if (!use_cache) {
6467  delete [] matrix;
6468  }
6469  destroy_asdraw_context32(ctx);
6470 }
6471 
6472 ////////////////////////////////////////////////////////////////////////////////
6473 /// Draw an ellipse.
6474 /// If thick < 0 - draw filled ellipse.
6475 
6476 void TASImage::DrawEllips2(Int_t x, Int_t y, Int_t rx, Int_t ry, Int_t angle,
6477  const char *col, Int_t thick)
6478 {
6479  thick = !thick ? 1 : thick;
6480  Int_t sz = thick*thick;
6481  CARD32 *matrix;
6482  Bool_t use_cache = (thick > 0) && ((UInt_t)thick < kBrushCacheSize);
6483 
6484  ARGB32 color = ARGB32_White;
6485  parse_argb_color(col, &color);
6486 
6487  if (use_cache) {
6488  matrix = gBrushCache;
6489  } else {
6490  matrix = new CARD32[sz];
6491  }
6492 
6493  for (int i = 0; i < sz; i++) {
6494  matrix[i] = (CARD32)color;
6495  };
6496 
6497  ASDrawTool brush;
6498  brush.matrix = matrix;
6499  brush.width = thick > 0 ? thick : 1;
6500  brush.height = thick > 0 ? thick : 1;
6501  brush.center_y = brush.center_x = thick > 0 ? thick/2 : 0;
6502 
6503  ASDrawContext *ctx = create_draw_context_argb32(fImage, &brush);
6504  asim_ellips2(ctx, x, y, rx, ry, angle, thick < 0);
6505 
6506  if (!use_cache) {
6507  delete [] matrix;
6508  }
6509  destroy_asdraw_context32(ctx);
6510 }
6511 
6512 ////////////////////////////////////////////////////////////////////////////////
6513 /// Flood fill.
6514 
6515 void TASImage::FloodFill(Int_t /*x*/, Int_t /*y*/, const char * /*col*/,
6516  const char * /*minc*/, const char * /*maxc*/)
6517 {
6518 }
6519 
6520 ////////////////////////////////////////////////////////////////////////////////
6521 /// Convert RGB image to Gray image and vice versa.
6522 
6523 void TASImage::Gray(Bool_t on)
6524 {
6525  if (fIsGray == on) {
6526  return;
6527  }
6528 
6529  if (!IsValid()) {
6530  Warning("Gray", "Image not initiated");
6531  return;
6532  }
6533 
6534  if (!InitVisual()) {
6535  Warning("Gray", "Visual not initiated");
6536  return;
6537  }
6538 
6539  if (!fGrayImage && !on) {
6540  return;
6541  }
6542  ASImage *sav = 0;
6543  delete fScaledImage;
6544  fScaledImage = 0;
6545 
6546  if (fGrayImage) {
6547  sav = fImage;
6548  fImage = fGrayImage;
6549  fGrayImage = sav;
6550  fIsGray = on;
6551  return;
6552  }
6553 
6554  if (!on) return;
6555 
6556  UInt_t l, r, g, b, idx;
6557  int y = 0;
6558  UInt_t i, j;
6559 
6560  if (fImage->alt.argb32) {
6561  fGrayImage = tile_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height,
6562  0, ASA_ARGB32, 0, ASIMAGE_QUALITY_DEFAULT);
6563 
6564  for (i = 0; i < fImage->height; i++) {
6565  for (j = 0; j < fImage->width; j++) {
6566  idx = Idx(y + j);
6567 
6568  r = ((fImage->alt.argb32[idx] & 0xff0000) >> 16);
6569  g = ((fImage->alt.argb32[idx] & 0x00ff00) >> 8);
6570  b = (fImage->alt.argb32[idx] & 0x0000ff);
6571  l = (57*r + 181*g + 18*b)/256;
6572  fGrayImage->alt.argb32[idx] = (l << 16) + (l << 8) + l;
6573  }
6574  y += fImage->width;
6575  }
6576  } else {
6577  fGrayImage = create_asimage(fImage->width, fImage->height, 0);
6578 
6579  ASImageDecoder *imdec = start_image_decoding(fgVisual, fImage, SCL_DO_ALL,
6580  0, 0, fImage->width, fImage->height, 0);
6581 
6582  if (!imdec) {
6583  return;
6584  }
6585 #ifdef HAVE_MMX
6586  mmx_init();
6587 #endif
6588  ASImageOutput *imout = start_image_output(fgVisual, fGrayImage, ASA_ASImage,
6589  GetImageCompression(), GetImageQuality());
6590  if (!imout) {
6591  Warning("ToGray", "Failed to start image output");
6592  delete fScaledImage;
6593  fScaledImage = 0;
6594  delete [] imdec;
6595  return;
6596  }
6597 
6598  CARD32 *aa = imdec->buffer.alpha;
6599  CARD32 *rr = imdec->buffer.red;
6600  CARD32 *gg = imdec->buffer.green;
6601  CARD32 *bb = imdec->buffer.blue;
6602 
6603  ASScanline result;
6604  prepare_scanline(fImage->width, 0, &result, fgVisual->BGR_mode);
6605 
6606  for (i = 0; i < fImage->height; i++) {
6607  imdec->decode_image_scanline(imdec);
6608  result.flags = imdec->buffer.flags;
6609  result.back_color = imdec->buffer.back_color;
6610 
6611  for (j = 0; j < fImage->width; j++) {
6612  l = (57*rr[j] + 181*gg[j]+ 18*bb[j])/256;
6613  result.alpha[j] = aa[j];
6614  result.red[j] = l;
6615  result.green[j] = l;
6616  result.blue[j] = l;
6617  }
6618  imout->output_image_scanline(imout, &result, 1);
6619  }
6620 
6621  stop_image_decoding(&imdec);
6622  stop_image_output(&imout);
6623 #ifdef HAVE_MMX
6624  mmx_off();
6625 #endif
6626  }
6627 
6628  sav = fImage;
6629  fImage = fGrayImage;
6630  fGrayImage = sav;
6631  fIsGray = kTRUE;
6632 }
6633 
6634 ////////////////////////////////////////////////////////////////////////////////
6635 /// Create an image (screenshot) from specified window.
6636 
6637 void TASImage::FromWindow(Drawable_t wid, Int_t x, Int_t y, UInt_t w, UInt_t h)
6638 {
6639  Int_t xy;
6640 
6641  x = x < 0 ? 0 : x;
6642  y = y < 0 ? 0 : y;
6643 
6644  // X11 Synchronization
6645  gVirtualX->Update(1);
6646  if (!gThreadXAR) {
6647  gSystem->Sleep(10);
6648  gSystem->ProcessEvents();
6649  gSystem->Sleep(10);
6650  gSystem->ProcessEvents();
6651  }
6652 
6653  if (!w || !h) {
6654  gVirtualX->GetWindowSize(wid, xy, xy, w, h);
6655  }
6656 
6657  if ((x >= (Int_t)w) || (y >= (Int_t)h)) {
6658  return;
6659  }
6660 
6661  if (!InitVisual()) {
6662  Warning("FromWindow", "Visual not initiated");
6663  return;
6664  }
6665 
6666  DestroyImage();
6667  delete fScaledImage;
6668  fScaledImage = 0;
6669 
6670  static int x11 = -1;
6671  if (x11 < 0) x11 = gVirtualX->InheritsFrom("TGX11");
6672 
6673  if (x11) { //use built-in optimized version
6674  fImage = pixmap2asimage(fgVisual, wid, x, y, w, h, kAllPlanes, 0, 0);
6675  } else {
6676  unsigned char *bits = gVirtualX->GetColorBits(wid, 0, 0, w, h);
6677 
6678  if (!bits) { // error
6679  return;
6680  }
6681  fImage = bitmap2asimage(bits, w, h, 0, 0);
6682  delete [] bits;
6683  }
6684 }
6685 
6686 ////////////////////////////////////////////////////////////////////////////////
6687 /// Creates an image (screenshot) from a RGBA buffer.
6688 
6689 void TASImage::FromGLBuffer(UChar_t* buf, UInt_t w, UInt_t h)
6690 {
6691  DestroyImage();
6692  delete fScaledImage;
6693  fScaledImage = 0;
6694 
6695  UChar_t* xx = new UChar_t[4*w];
6696  for (UInt_t i = 0; i < h/2; ++i) {
6697  memcpy(xx, buf + 4*w*i, 4*w);
6698  memcpy(buf + 4*w*i, buf + 4*w*(h-i-1), 4*w);
6699  memcpy(buf + 4*w*(h-i-1), xx, 4*w);
6700  }
6701  delete [] xx;
6702 
6703  fImage = bitmap2asimage(buf, w, h, 0, 0);
6704 }
6705 
6706 ////////////////////////////////////////////////////////////////////////////////
6707 /// Switch on/off the image palette.
6708 /// That also invokes calling vectorization of image.
6709 
6710 void TASImage::SetPaletteEnabled(Bool_t on)
6711 {
6712  if (!fImage) {
6713  return;
6714  }
6715 
6716  if (!fImage->alt.vector && on) {
6717  Vectorize();
6718  }
6719  fPaletteEnabled = on;
6720 
6721  if (on) {
6722  Double_t left = gPad->GetLeftMargin();
6723  Double_t right = gPad->GetRightMargin();
6724  Double_t top = gPad->GetTopMargin();
6725  Double_t bottom = gPad->GetBottomMargin();
6726 
6727  gPad->Range(-left / (1.0 - left - right),
6728  -bottom / (1.0 - top - bottom),
6729  1 + right / (1.0 - left - right),
6730  1 + top / ( 1.0 - top - bottom));
6731  gPad->RangeAxis(0, 0, 1, 1);
6732  }
6733 
6734 }
6735 
6736 ////////////////////////////////////////////////////////////////////////////////
6737 /// Save a primitive as a C++ statement(s) on output stream "out".
6738 
6739 void TASImage::SavePrimitive(std::ostream &out, Option_t * /*= ""*/)
6740 {
6741  char *buf = 0;
6742  int sz;
6743 
6744  UInt_t w = GetWidth();
6745  UInt_t h = GetHeight();
6746 
6747  if (w > 500) { // workaround CINT limitations
6748  w = 500;
6749  Double_t scale = 500./GetWidth();
6750  h = TMath::Nint(GetHeight()*scale);
6751  Scale(w, h);
6752  }
6753 
6754  GetImageBuffer(&buf, &sz, TImage::kXpm);
6755 
6756  TString name = GetName();
6757  name.ReplaceAll(".", "_");
6758  TString str = buf;
6759  static int ii = 0;
6760  ii++;
6761 
6762  str.ReplaceAll("static", "const");
6763  TString xpm = "xpm_";
6764  xpm += name;
6765  xpm += ii;
6766  str.ReplaceAll("asxpm", xpm.Data());
6767  out << std::endl << str << std::endl << std::endl;
6768 
6769  out << " TImage *";
6770  out << xpm << "_img = TImage::Create();" << std::endl;
6771  out << " " << xpm << "_img->SetImageBuffer( (char **)" << xpm << ", TImage::kXpm);" << std::endl;
6772  out << " " << xpm << "_img->Draw();" << std::endl;
6773 }
6774 
6775 ////////////////////////////////////////////////////////////////////////////////
6776 /// Set an image printing resolution in Dots Per Inch units.
6777 ///
6778 /// \param[in] name - the name of jpeg file.
6779 /// \param[in] set - dpi resolution.
6780 ///
6781 /// Returns kFALSE in case of error.
6782 
6783 Bool_t TASImage::SetJpegDpi(const char *name, UInt_t set)
6784 {
6785  static char buf[32];
6786  FILE *fp = fopen(name, "rb+");
6787 
6788  if (!fp) {
6789  printf("file %s : failed to open\n", name);
6790  return kFALSE;
6791  }
6792 
6793  if (!fread(buf, 1, 20, fp)) {
6794  fclose(fp);
6795  return kFALSE;
6796  }
6797 
6798  char dpi1 = (set & 0xffff) >> 8;
6799  char dpi2 = set & 0xff;
6800 
6801  int i = 0;
6802 
6803  int dpi = 0; // start of dpi data
6804  for (i = 0; i < 20; i++) {
6805  if ((buf[i] == 0x4a) && (buf[i+1] == 0x46) && (buf[i+2] == 0x49) &&
6806  (buf[i+3] == 0x46) && (buf[i+4] == 0x00) ) {
6807  dpi = i + 7;
6808  break;
6809  }
6810  }
6811 
6812  if (i == 20 || dpi+4 >= 20) { // jpeg maker was not found
6813  fclose(fp);
6814  printf("file %s : wrong JPEG format\n", name);
6815  return kFALSE;
6816  }
6817 
6818  buf[dpi] = 1; // format specified in dots per inch
6819 
6820  // set x density in dpi units
6821  buf[dpi + 1] = dpi1;
6822  buf[dpi + 2] = dpi2;
6823 
6824  // set y density in dpi units
6825  buf[dpi + 3] = dpi1;
6826  buf[dpi + 4] = dpi2;
6827 
6828  rewind(fp);
6829  fwrite(buf, 1, 20, fp);
6830  fclose(fp);
6831 
6832  return kTRUE;
6833 }
6834 
6835 ////////////////////////////////////////////////////////////////////////////////
6836 /// Return a valid index in fImage tables to avoid seg-fault by accessing out of
6837 /// indices out of array's ranges.
6838 
6839 Int_t TASImage::Idx(Int_t idx)
6840 {
6841  // The size of arrays like fImage->alt.argb32 is fImage->width*fImage->height
6842  return TMath::Min(idx,(Int_t)(fImage->width*fImage->height));
6843 }
6844