Logo ROOT  
Reference Guide
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 "TBuffer.h"
67 #include "TMath.h"
68 #include "TSystem.h"
69 #include "TVirtualX.h"
70 #include "TVirtualPad.h"
71 #include "TArrayD.h"
72 #include "TVectorD.h"
73 #include "TVirtualPS.h"
74 #include "TGaxis.h"
75 #include "TColor.h"
76 #include "TObjArray.h"
77 #include "TArrayL.h"
78 #include "TPoint.h"
79 #include "TFrame.h"
80 #include "TTF.h"
81 #include "TRandom.h"
82 #include <iostream>
83 #include "THashTable.h"
84 #include "TPluginManager.h"
85 #include "TEnv.h"
86 #include "TStyle.h"
87 #include "TText.h"
88 #include "RConfigure.h"
89 #include "TVirtualPadPainter.h"
90 #include "snprintf.h"
91 
92 #ifndef WIN32
93 #ifndef R__HAS_COCOA
94 # include <X11/Xlib.h>
95 #endif
96 #else
97 # include "Windows4root.h"
98 #endif
99 #ifndef WIN32
100 #ifdef R__HAS_COCOA
101 # define X_DISPLAY_MISSING 1
102 #endif
103 # include <afterbase.h>
104 #else
105 # include <win32/config.h>
106 # include <win32/afterbase.h>
107 # define X_DISPLAY_MISSING 1
108 #endif
109 # include <afterimage.h>
110 # include <bmp.h>
111 extern "C" {
112 # include <draw.h>
113 }
114 
115 // auxiliary functions for general polygon filling
116 #include "TASPolyUtils.c"
117 
118 
119 ASVisual *TASImage::fgVisual = 0;
121 
122 static ASFontManager *gFontManager = 0;
123 static unsigned long kAllPlanes = ~0;
125 
126 // default icon paths
127 static char *gIconPaths[7] = {0, 0, 0, 0, 0, 0, 0};
128 
129 // To scale fonts to the same size as the old TT version
130 const Float_t kScale = 0.985;
131 
132 ///////////////////////////// alpha-blending macros ///////////////////////////////
133 
134 #if defined(__GNUC__) && __GNUC__ >= 4 && ((__GNUC_MINOR__ == 2 && __GNUC_PATCHLEVEL__ >= 1) || (__GNUC_MINOR__ >= 3)) && !__INTEL_COMPILER
135 #pragma GCC diagnostic ignored "-Wstrict-aliasing"
136 #endif
137 
138 #ifdef R__BYTESWAP
139 typedef struct {
140  unsigned char b;
141  unsigned char g;
142  unsigned char r;
143  unsigned char a;
144 } __argb32__;
145 #else
146 typedef struct {
147  unsigned char a;
148  unsigned char r;
149  unsigned char g;
150  unsigned char b;
151 } __argb32__;
152 #endif
153 
154 
155 //______________________________________________________________________________
156 #define _alphaBlend(bot, top) {\
157  __argb32__ *T = (__argb32__*)(top);\
158  __argb32__ *B = (__argb32__*)(bot);\
159  int aa = 255-T->a; /* NOLINT */ \
160  if (!aa) {\
161  *bot = *top;\
162  } else { \
163  B->a = ((B->a*aa)>>8) + T->a;\
164  B->r = (B->r*aa + T->r*T->a)>>8;\
165  B->g = (B->g*aa + T->g*T->a)>>8;\
166  B->b = (B->b*aa + T->b*T->a)>>8;\
167  }\
168 }\
169 
170 
173 
174 ////////////////////////////////////////////////////////////////////////////////
175 /// Destroy image.
176 
178 {
179  if (fImage) {
180  destroy_asimage(&fImage);
181  }
182 
183  if (fIsGray && fGrayImage) {
184  destroy_asimage(&fGrayImage);
185  }
186 
187  fIsGray = kFALSE;
188  fGrayImage = 0;
189  fImage = 0;
190 }
191 
192 ////////////////////////////////////////////////////////////////////////////////
193 /// Set default parameters.
194 
196 {
197  fImage = 0;
198  fScaledImage = 0;
199  fMaxValue = 1;
200  fMinValue = 0;
201  fEditable = kFALSE;
202  fPaintMode = 1;
203  fZoomOffX = 0;
204  fZoomOffY = 0;
205  fZoomWidth = 0;
206  fZoomHeight = 0;
208 
209  fGrayImage = 0;
210  fIsGray = kFALSE;
212 
213  if (!fgInit) {
214  set_application_name((char*)(gProgName ? gProgName : "ROOT"));
215  fgInit = kTRUE;
216  }
217 }
218 
219 ////////////////////////////////////////////////////////////////////////////////
220 /// Default image constructor.
221 
223 {
224  SetDefaults();
225 }
226 
227 ////////////////////////////////////////////////////////////////////////////////
228 /// Create an empty image.
229 
231 {
232  SetDefaults();
233  fImage = create_asimage(w ? w : 20, h ? h : 20, 0);
234  UnZoom();
235 }
236 
237 ////////////////////////////////////////////////////////////////////////////////
238 /// Create an image object and read from specified file.
239 /// For more information see description of function ReadImage()
240 /// which is called by this constructor.
241 
243 {
244  SetDefaults();
245  TString fname = file;
246  gSystem->ExpandPathName(fname);
247  ReadImage(fname.Data());
248 }
249 
250 ////////////////////////////////////////////////////////////////////////////////
251 /// Create an image depending on the values of imageData.
252 /// For more information see function SetImage() which is called
253 /// by this constructor.
254 
255 TASImage::TASImage(const char *name, const Double_t *imageData, UInt_t width,
256  UInt_t height, TImagePalette *palette) : TImage(name)
257 {
258  SetDefaults();
259  SetImage(imageData, width, height, palette);
260 }
261 
262 ////////////////////////////////////////////////////////////////////////////////
263 /// Create an image depending on the values of imageData.
264 /// The size of the image is width X (imageData.fN / width).
265 /// For more information see function SetImage() which is called by
266 /// this constructor.
267 
268 TASImage::TASImage(const char *name, const TArrayD &imageData, UInt_t width,
269  TImagePalette *palette) : TImage(name)
270 {
271  SetDefaults();
272  SetImage(imageData, width, palette);
273 }
274 
275 ////////////////////////////////////////////////////////////////////////////////
276 /// Create an image depending on the values of imageData.
277 /// The size of the image is width X (imageData.fN / width).
278 /// For more information see function SetImage() which is called by
279 /// this constructor.
280 
281 TASImage::TASImage(const char *name, const TVectorD &imageData, UInt_t width,
282  TImagePalette *palette) : TImage(name)
283 {
284  SetDefaults();
285  SetImage(imageData, width, palette);
286 }
287 
288 ////////////////////////////////////////////////////////////////////////////////
289 /// Image copy constructor.
290 
292 {
293  SetDefaults();
294 
295  if (img.IsValid()) {
296  fImage = clone_asimage(img.fImage, SCL_DO_ALL);
298  fGrayImage = fGrayImage ? clone_asimage(img.fGrayImage, SCL_DO_ALL) : 0;
299 
300  if (img.fImage->alt.vector) {
301  Int_t size = img.fImage->width * img.fImage->height * sizeof(double);
302  fImage->alt.vector = (double*)malloc(size);
303  memcpy(fImage->alt.vector, img.fImage->alt.vector, size);
304  }
305 
307  fZoomOffX = img.fZoomOffX;
308  fZoomOffY = img.fZoomOffY;
309  fZoomWidth = img.fZoomWidth;
310  fZoomHeight = img.fZoomHeight;
311  fEditable = img.fEditable;
312  fIsGray = img.fIsGray;
313  }
314 }
315 
316 ////////////////////////////////////////////////////////////////////////////////
317 /// Image assignment operator.
318 
320 {
321  if (this != &img && img.IsValid()) {
322  TImage::operator=(img);
323 
324  DestroyImage();
325  delete fScaledImage;
326  fImage = clone_asimage(img.fImage, SCL_DO_ALL);
328  fGrayImage = fGrayImage ? clone_asimage(img.fGrayImage, SCL_DO_ALL) : 0;
329 
330  if (img.fImage->alt.vector) {
331  Int_t size = img.fImage->width * img.fImage->height * sizeof(double);
332  fImage->alt.vector = (double*)malloc(size);
333  memcpy(fImage->alt.vector, img.fImage->alt.vector, size);
334  }
335 
336  fScaledImage = img.fScaledImage ? (TASImage*)img.fScaledImage->Clone("") : 0;
338  fZoomOffX = img.fZoomOffX;
339  fZoomOffY = img.fZoomOffY;
340  fZoomWidth = img.fZoomWidth;
341  fZoomHeight = img.fZoomHeight;
342  fEditable = img.fEditable;
343  fIsGray = img.fIsGray;
344  fPaintMode = 1;
345  }
346 
347  return *this;
348 }
349 
350 ////////////////////////////////////////////////////////////////////////////////
351 /// Image destructor, clean up image and visual.
352 
354 {
355  DestroyImage();
356  delete fScaledImage;
357  fScaledImage = 0;
358 }
359 
360 ////////////////////////////////////////////////////////////////////////////////
361 /// Set icons paths.
362 
363 static void init_icon_paths()
364 {
365  TString icon_path = gEnv->GetValue("Gui.IconPath", "");
366  if (icon_path.IsNull()) {
367  icon_path = "icons";
369 #ifndef R__WIN32
370  icon_path = ".:" + icon_path + ":" + TROOT::GetIconPath() + ":" + EXTRAICONPATH;
371 #else
372  icon_path = ".;" + icon_path + ";" + TROOT::GetIconPath() + ";" + EXTRAICONPATH;
373 #endif
374  }
375 
376  Int_t cnt = 0;
377  Ssiz_t from = 0;
378  TString token;
379 #ifndef R__WIN32
380  const char *delim = ":";
381 #else
382  const char *delim = ";";
383 #endif
384  while (icon_path.Tokenize(token, from, delim) && cnt < 6) {
385  char *path = gSystem->ExpandPathName(token.Data());
386  if (path) {
387  gIconPaths[cnt] = path;
388  cnt++;
389  }
390  }
391  gIconPaths[cnt] = 0;
392 }
393 
394 ////////////////////////////////////////////////////////////////////////////////
395 /// Guess the file type from the first byte of file.
396 
397 const char *TASImage::TypeFromMagicNumber(const char *file)
398 {
399  UChar_t magic;
400  FILE *fp = fopen(file, "rb");
401  const char *ret = "";
402 
403  if (!fp) return 0;
404 
405  if (!fread(&magic, 1, 1, fp)) {
406  fclose(fp);
407  return 0;
408  }
409 
410  switch (magic) {
411  case 0x00:
412  {
413  if (!fread(&magic, 1, 1, fp)) {
414  fclose(fp);
415  return 0;
416  }
417  if (!fread(&magic, 1, 1, fp)) {
418  fclose(fp);
419  return 0;
420  }
421 
422  ret = (magic == 1) ? "ico" : "cur";
423  break;
424  }
425  case 0x25:
426  {
427  if (!fread(&magic, 1, 1, fp)) {
428  fclose(fp);
429  return 0;
430  }
431 
432  if (magic == 0x21) ret = "ps";
433  else if (magic == 0x50) ret = "pdf";
434  break;
435  }
436  case 0x42:
437  ret = "bmp";
438  break;
439  case 0x47:
440  ret = "gif";
441  break;
442  case 0x54:
443  ret = "tga";
444  break;
445  case 0x49:
446  ret = "tiff";
447  break;
448  case 0x89:
449  ret = "png";
450  break;
451  case 0xff:
452  ret = "jpg";
453  break;
454  default:
455  ret = "";
456  }
457 
458  fclose(fp);
459  return ret;
460 }
461 
462 ////////////////////////////////////////////////////////////////////////////////
463 /// Read specified image file.
464 /// The file type is determined by the file extension (the type argument is
465 /// ignored). It will attempt to append .gz and then .Z to the filename and
466 /// find such a file. If the filename ends with extension consisting of digits
467 /// only, it will attempt to find the file with this extension stripped
468 /// off. On success this extension will be used to load subimage from
469 /// the file with that number. Subimage is supported for GIF files
470 /// (ICO, BMP, CUR, TIFF, XCF to be supported in future).
471 /// For example,
472 /// ~~~ {.cpp}
473 /// i1 = TImage::Open("anim.gif.0"); // read the first subimage
474 /// i4 = TImage::Open("anim.gif.3"); // read the forth subimage
475 /// ~~~
476 /// It is also possible to put XPM raw string (see also SetImageBuffer) as
477 /// the first input parameter ("filename"), such string is returned by
478 /// GetImageBuffer method.
479 
480 void TASImage::ReadImage(const char *filename, EImageFileTypes /*type*/)
481 {
482  if (!InitVisual()) {
483  Warning("Scale", "Visual not initiated");
484  return;
485  }
486 
487  Bool_t xpm = filename && (filename[0] == '/' &&
488  filename[1] == '*') && filename[2] == ' ';
489 
490  if (xpm) { // XPM strings in-memory array
491  SetImageBuffer((char**)&filename, TImage::kXpm);
492  fName = "XPM_image";
493  return;
494  }
495 
496  if (!gIconPaths[0]) {
497  init_icon_paths();
498  }
499  // suppress the "root : looking for image ..." messages
500  set_output_threshold(0);
501 
502  static ASImageImportParams iparams;
503  iparams.flags = 0;
504  iparams.width = 0;
505  iparams.height = 0;
506  iparams.filter = SCL_DO_ALL;
507  iparams.gamma = SCREEN_GAMMA;
508  iparams.gamma_table = NULL;
509  iparams.compression = GetImageCompression();
510  iparams.format = ASA_ASImage;
511  iparams.search_path = gIconPaths;
512  iparams.subimage = 0;
513  iparams.return_animation_delay = -1;
514 
515  TString ext;
516  const char *dot;
517  if (filename) dot = strrchr(filename, '.');
518  else dot = 0;
519  ASImage *image = 0;
520  TString fname = filename;
521 
522  if (!dot) {
523  if (filename) ext = TypeFromMagicNumber(filename);
524  else ext = dot + 1;
525  } else {
526  ext = dot + 1;
527  }
528 
529  if (!ext.IsNull() && ext.IsDigit()) { // read subimage
530  iparams.subimage = ext.Atoi();
531  fname = fname(0, fname.Length() - ext.Length() - 1);
532  ext = strrchr(fname.Data(), '.') + 1;
533  }
534 
535  image = file2ASImage_extra(fname.Data(), &iparams);
536 
537  if (image) { // it's OK
538  goto end;
539  } else { // try to read it via plugin
540  if (ext.IsNull()) {
541  return;
542  }
543  ext.ToLower();
544  ext.Strip();
545  UInt_t w = 0;
546  UInt_t h = 0;
547  unsigned char *bitmap = 0;
548 
550 
551  if (!plug) {
552  TPluginHandler *handler = gROOT->GetPluginManager()->FindHandler("TImagePlugin", ext);
553  if (!handler || ((handler->LoadPlugin() == -1))) {
554  return;
555  }
556  plug = (TImagePlugin*)handler->ExecPlugin(1, ext.Data());
557 
558  if (!plug) {
559  return;
560  }
561 
562  fgPlugList->Add(plug);
563  }
564 
565  if (plug) {
566  if (plug->InheritsFrom(TASImagePlugin::Class())) {
567  image = ((TASImagePlugin*)plug)->File2ASImage(fname.Data());
568  if (image) goto end;
569  }
570  bitmap = plug->ReadFile(fname.Data(), w, h);
571  if (bitmap) {
572  image = bitmap2asimage(bitmap, w, h, 0, 0);
573  }
574  if (!image) {
575  return;
576  }
577  }
578  }
579 
580 end:
581  fName.Form("%s.", gSystem->BaseName(fname.Data()));
582 
583  DestroyImage();
584  delete fScaledImage;
585  fScaledImage = 0;
586 
587  fImage = image;
589  fEditable = kFALSE;
590  fZoomOffX = 0;
591  fZoomOffY = 0;
592  fZoomWidth = fImage->width;
593  fZoomHeight = fImage->height;
594  fPaintMode = 1;
595 }
596 
597 ////////////////////////////////////////////////////////////////////////////////
598 /// Write image to specified file.
599 ///
600 /// If there is no file extension or if the file extension is unknown, the
601 /// type argument will be used to determine the file type. The quality and
602 /// compression is derived from the TAttImage values.
603 ///
604 /// It's possible to write image into an animated GIF file by specifying file
605 /// name as "myfile.gif+" or "myfile.gif+NN", where NN is the delay of displaying
606 /// subimages during animation in 10ms seconds units. NN is not restricted
607 /// to two digits. If NN is omitted the delay between subimages is zero.
608 /// For an animation that stops after last subimage is reached, one has to
609 /// write the last image as .gif+ (zero delay of last image) or .gif+NN
610 /// (NN*10ms delay of last image).
611 ///
612 /// For repeated animation (looping), the last subimage must be specified as:
613 /// - "myfile.gif++NN++" if you want an infinite looping gif with NN*10ms
614 /// delay of the last image.
615 /// - "myfile.gif++" for an infinite loop with zero delay of last image.
616 /// - "myfile.gif+NN++RR" if you want a finite looping gif with NN*10ms
617 /// delay of the last image and the animation to be stopped after RR
618 /// repeats. RR is not restricted to two digits.
619 ///
620 /// A deprecated version for saving the last subimage of a looping gif animation is:
621 /// - "myfile.gif++NN" for a finite loop where NN is number of repetitions
622 /// and NN*10ms the delay of last image. (No separate control of repeats and delay).
623 /// Note: If the file "myfile.gif" already exists, the new frames are appended at
624 /// the end of the file. To avoid this, delete it first with gSystem->Unlink(myfile.gif);
625 ///
626 /// The following macro creates animated gif from jpeg images with names
627 /// - imageNN.jpg, where 1<= NN <= 10
628 /// - The delays are set to 10*10ms.
629 /// ~~~ {.cpp}
630 /// {
631 /// TImage *img = 0;
632 /// gSystem->Unlink("anim.gif"); // delete existing file
633 ///
634 /// for (int i = 1; i <= 10; i++) {
635 /// delete img; // delete previous image
636 ///
637 /// // Read image data. Image can be in any format, e.g. png, gif, etc.
638 /// img = TImage::Open(Form("image%d.jpg", i));
639 ///
640 /// if (i < 10) {
641 /// img->WriteImage("anim.gif+10"); // 10 centiseconds delay
642 /// } else { // the last image written. "++" stands for infinit animation.
643 /// img->WriteImage("anim.gif++10++"); // 10 centiseconds delay of last image
644 /// }
645 /// }
646 /// }
647 /// ~~~
648 
650 {
651  if (!IsValid()) {
652  Error("WriteImage", "no image loaded");
653  return;
654  }
655 
656  if (!file || !*file) {
657  Error("WriteImage", "no file name specified");
658  return;
659  }
660 
661  const char *s;
662  if ((s = strrchr(file, '.'))) {
663  s++;
665  if (t == kUnknown) {
666  Error("WriteImage", "cannot determine a valid file type");
667  return;
668  }
669  if (t != kUnknown)
670  type = t;
671  }
672 
673  if (type == kUnknown) {
674  Error("WriteImage", "not a valid file type was specified");
675  return;
676  }
677 
678  UInt_t mytype;
679  MapFileTypes(type, mytype);
680  ASImageFileTypes atype = (ASImageFileTypes)mytype;
681 
682  UInt_t aquality;
683  EImageQuality quality = GetImageQuality();
684  MapQuality(quality, aquality);
685 
686  static TString fname;
687  fname = file;
688  static ASImageExportParams parms;
689  ASImage *im = fScaledImage ? fScaledImage->fImage : fImage;
690 
691  switch (type) {
692  case kXpm:
693  parms.xpm.type = atype;
694  parms.xpm.flags = EXPORT_ALPHA;
695  parms.xpm.dither = 4;
696  parms.xpm.opaque_threshold = 127;
697  parms.xpm.max_colors = 512;
698  break;
699  case kBmp:
700  ASImage2bmp(im, fname.Data(), 0);
701  return;
702  case kXcf:
703  ASImage2xcf(im, fname.Data(), 0);
704  return;
705  case kPng:
706  parms.png.type = atype;
707  parms.png.flags = EXPORT_ALPHA;
708  parms.png.compression = !GetImageCompression() ? -1 : int(GetImageCompression());
709  break;
710  case kJpeg:
711  parms.jpeg.type = atype;
712  parms.jpeg.flags = 0;
713  parms.jpeg.quality = aquality;
714  break;
715  case kGif:
716  parms.gif.type = atype;
717  parms.gif.flags = EXPORT_ALPHA;
718  parms.gif.dither = 0;
719  parms.gif.opaque_threshold = 0;
720  break;
721  case kAnimGif:
722  {
723  parms.gif.type = atype;
724  parms.gif.flags = EXPORT_ALPHA | EXPORT_APPEND;
725  parms.gif.dither = 0;
726  parms.gif.opaque_threshold = 0;
727  parms.gif.animate_repeats = 0;
728 
729  s += 4; // skip "gif+"
730  int delay = 0;
731 
732  const TString sufix = s; // we denote as suffix as everything that is after .gif+
733  const UInt_t sLength = sufix.Length();
734 
735  if (sufix=="+") {
736  // .gif++ implies that this is the last image of the animation
737  // and that the gif will loop forever (infinite animation)
738  // and that the delay of last image is 0ms (backward compatibility reasons)
739  delay = 0;
740  parms.gif.flags |= EXPORT_ANIMATION_REPEATS;// activate repetition
741  parms.gif.animate_repeats = 0;// 0 is code for looping forever (if EXPORT_ANIMATION_REPEATS is also set)
742  } else if(sufix=="") {
743  // .gif+ implies that this is a subimage of the animation with zero delay
744  // or the last image of an animation that will not repeat.
745  // Last image delay is zero because atoi("")=0.
746  delay = atoi(s);
747  //Nothing else needed here
748  } else if(!sufix.Contains("+")) {
749  // .gif+NN implies that this is a subimage of the animation
750  // with NN*10ms delay (latency) until the next one.
751  // You can also use this option on the last image if you do not want the gif to replay
752  delay = atoi(s);
753  //Nothing else needed here
754  } else if(sLength>1 && sufix.BeginsWith("+") && sufix.CountChar('+')==1) {
755  // .gif++NN implies that this is the last image of the animation
756  // and that it will loop NN number of times (finite animation)
757  // and that the delay of last image is NN*10ms (backward compatibility reasons).
758  delay = atoi(s);// atoi is smart enough to ignore the "+" sign before.
759  parms.gif.flags |= EXPORT_ANIMATION_REPEATS;// activate repetition
760  parms.gif.animate_repeats = atoi(s);// loops only NN times, then it stops. atoi discards + sign.
761  } else if(sLength>3 && sufix.BeginsWith("+") && sufix.EndsWith("++") && !TString(sufix(1,sLength-3)).Contains("+")) {
762  // .gif++NN++ implies that this is the last image of the animation
763  // and that the gif will loop forever (infinite animation)
764  // and that the delay of last image is NN*10ms.
765  // In contrast, .gif++ is an infinite loop but with 0 delay, whereas the option
766  // .gif++NN is a loop repeated NN times (not infinite) with NN*10ms delay
767  // between last and first loop images.
768  delay = atoi(s);// atoi discards the three plus signs
769  parms.gif.flags |= EXPORT_ANIMATION_REPEATS;// activate repetition
770  parms.gif.animate_repeats = 0;// 0 is code for looping forever (if EXPORT_ANIMATION_REPEATS is also set)
771  } else if(sLength>3 && sufix.CountChar('+')==2 && TString(sufix(1,sLength-2)).Contains("++")) {
772  // .gif+NN++RR implies that this is the last image animation
773  // and that the gif will loop RR number of times (finite animation)
774  // and that the delay of last image is NN*10ms.
775  const TString sDelay = sufix(0,sufix.First('+'));
776  const TString sRepeats = sufix(sufix.First('+')+2,sLength-(sufix.First('+')+2));
777  delay = atoi(sDelay);
778  parms.gif.flags |= EXPORT_ANIMATION_REPEATS;// activate repetition
779  parms.gif.animate_repeats = atoi(sRepeats);// loops NN times.
780  } else {
781  Error("WriteImage", "gif suffix %s not yet supported", s);
782  return;
783  }
784 
785  parms.gif.animate_delay = delay;
786 
787  int i1 = fname.Index("gif+");
788  if (i1 != kNPOS) {
789  fname = fname(0, i1 + 3);
790  }
791  else {
792  Error("WriteImage", "unexpected gif extension structure %s", fname.Data());
793  return;
794  }
795  break;
796  }
797  case kTiff:
798  parms.tiff.type = atype;
799  parms.tiff.flags = EXPORT_ALPHA;
800  parms.tiff.rows_per_strip = 0;
801  parms.tiff.compression_type = aquality <= 50 ? TIFF_COMPRESSION_JPEG :
802  TIFF_COMPRESSION_NONE;
803  parms.tiff.jpeg_quality = 100;
804  parms.tiff.opaque_threshold = 0;
805  break;
806  default:
807  Error("WriteImage", "file type %s not yet supported", s);
808  return;
809  }
810 
811  if (!ASImage2file(im, 0, fname.Data(), atype, &parms)) {
812  Error("WriteImage", "error writing file %s", file);
813  }
814 }
815 
816 ////////////////////////////////////////////////////////////////////////////////
817 /// Return file type depending on specified extension.
818 /// Protected method.
819 
821 {
822  TString s(ext);
823  s.Strip();
824  s.ToLower();
825 
826  if (s == "xpm")
827  return kXpm;
828  if (s == "png")
829  return kPng;
830  if (s == "jpg" || s == "jpeg")
831  return kJpeg;
832  if (s == "xcf")
833  return kXcf;
834  if (s == "ppm")
835  return kPpm;
836  if (s == "pnm")
837  return kPnm;
838  if (s == "bmp")
839  return kBmp;
840  if (s == "ico")
841  return kIco;
842  if (s == "cur")
843  return kCur;
844  if (s == "gif")
845  return kGif;
846  if (s.Contains("gif+"))
847  return kAnimGif;
848  if (s == "tiff")
849  return kTiff;
850  if (s == "xbm")
851  return kXbm;
852  if (s == "tga")
853  return kTga;
854  if (s == "xml")
855  return kXml;
856 
857  return kUnknown;
858 }
859 
860 ////////////////////////////////////////////////////////////////////////////////
861 /// Map file type to/from AfterImage types.
862 /// Protected method.
863 
865 {
866  if (toas) {
867  switch (type) {
868  case kXpm:
869  astype = ASIT_Xpm; break;
870  case kZCompressedXpm:
871  astype = ASIT_ZCompressedXpm; break;
872  case kGZCompressedXpm:
873  astype = ASIT_GZCompressedXpm; break;
874  case kPng:
875  astype = ASIT_Png; break;
876  case kJpeg:
877  astype = ASIT_Jpeg; break;
878  case kXcf:
879  astype = ASIT_Xcf; break;
880  case kPpm:
881  astype = ASIT_Ppm; break;
882  case kPnm:
883  astype = ASIT_Pnm; break;
884  case kBmp:
885  astype = ASIT_Bmp; break;
886  case kIco:
887  astype = ASIT_Ico; break;
888  case kCur:
889  astype = ASIT_Cur; break;
890  case kGif:
891  astype = ASIT_Gif; break;
892  case kAnimGif:
893  astype = ASIT_Gif; break;
894  case kTiff:
895  astype = ASIT_Tiff; break;
896  case kXbm:
897  astype = ASIT_Xbm; break;
898  case kTga:
899  astype = ASIT_Targa; break;
900  case kXml:
901  astype = ASIT_XMLScript; break;
902  default:
903  astype = ASIT_Unknown;
904  }
905  } else {
906  switch (astype) {
907  case ASIT_Xpm:
908  type = kXpm; break;
909  case ASIT_ZCompressedXpm:
910  type = kZCompressedXpm; break;
911  case ASIT_GZCompressedXpm:
912  type = kGZCompressedXpm; break;
913  case ASIT_Png:
914  type = kPng; break;
915  case ASIT_Jpeg:
916  type = kJpeg; break;
917  case ASIT_Xcf:
918  type = kXcf; break;
919  case ASIT_Ppm:
920  type = kPpm; break;
921  case ASIT_Pnm:
922  type = kPnm; break;
923  case ASIT_Bmp:
924  type = kBmp; break;
925  case ASIT_Ico:
926  type = kIco; break;
927  case ASIT_Cur:
928  type = kCur; break;
929  case ASIT_Gif:
930  type = kGif; break;
931  case ASIT_Tiff:
932  type = kTiff; break;
933  case ASIT_Xbm:
934  type = kXbm; break;
935  case ASIT_XMLScript:
936  type = kXml; break;
937  case ASIT_Targa:
938  type = kTga; break;
939  default:
940  type = kUnknown;
941  }
942  }
943 }
944 
945 ////////////////////////////////////////////////////////////////////////////////
946 /// Map quality to/from AfterImage quality.
947 /// Protected method.
948 
949 void TASImage::MapQuality(EImageQuality &quality, UInt_t &asquality, Bool_t toas)
950 {
951  if (toas) {
952  switch (quality) {
953  case kImgPoor:
954  asquality = 25; break;
955  case kImgFast:
956  asquality = 75; break;
957  case kImgGood:
958  asquality = 50; break;
959  case kImgBest:
960  asquality = 100; break;
961  default:
962  asquality = 0;
963  }
964  } else {
965  quality = kImgDefault;
966  if (asquality > 0 && asquality <= 25)
967  quality = kImgPoor;
968  if (asquality > 26 && asquality <= 50)
969  quality = kImgFast;
970  if (asquality > 51 && asquality <= 75)
971  quality = kImgGood;
972  if (asquality > 76 && asquality <= 100)
973  quality = kImgBest;
974  }
975 }
976 
977 ////////////////////////////////////////////////////////////////////////////////
978 /// Deletes the old image and creates a new image depending on the values
979 /// of imageData. The size of the image is width X height.
980 ///
981 /// The color of each pixel depends on the imageData of the corresponding
982 /// pixel. The palette is used to convert an image value into its color.
983 /// If palette is not defined (palette = 0) a default palette is used.
984 /// Any previously defined zooming is reset.
985 
986 void TASImage::SetImage(const Double_t *imageData, UInt_t width, UInt_t height,
987  TImagePalette *palette)
988 {
989  TAttImage::SetPalette(palette);
990 
991  if (!InitVisual()) {
992  Warning("SetImage", "Visual not initiated");
993  return;
994  }
995 
996  DestroyImage();
997  delete fScaledImage;
998  fScaledImage = 0;
999 
1000  // get min and max value of image
1001  fMinValue = fMaxValue = *imageData;
1002  for (Int_t pixel = 1; pixel < Int_t(width * height); pixel++) {
1003  if (fMinValue > *(imageData + pixel)) fMinValue = *(imageData + pixel);
1004  if (fMaxValue < *(imageData + pixel)) fMaxValue = *(imageData + pixel);
1005  }
1006 
1007  // copy ROOT palette to asImage palette
1008  const TImagePalette &pal = GetPalette();
1009 
1010  ASVectorPalette asPalette;
1011 
1012  asPalette.npoints = pal.fNumPoints;
1013  Int_t col;
1014  for (col = 0; col < 4; col++)
1015  asPalette.channels[col] = new UShort_t[asPalette.npoints];
1016 
1017  memcpy(asPalette.channels[0], pal.fColorBlue, pal.fNumPoints * sizeof(UShort_t));
1018  memcpy(asPalette.channels[1], pal.fColorGreen, pal.fNumPoints * sizeof(UShort_t));
1019  memcpy(asPalette.channels[2], pal.fColorRed, pal.fNumPoints * sizeof(UShort_t));
1020  memcpy(asPalette.channels[3], pal.fColorAlpha, pal.fNumPoints * sizeof(UShort_t));
1021 
1022  asPalette.points = new Double_t[asPalette.npoints];
1023  for (Int_t point = 0; point < Int_t(asPalette.npoints); point++)
1024  asPalette.points[point] = fMinValue + (fMaxValue - fMinValue) * pal.fPoints[point];
1025 
1026  fImage = create_asimage_from_vector(fgVisual, (Double_t*)imageData, width,
1027  height, &asPalette, ASA_ASImage,
1029 
1030  delete [] asPalette.points;
1031  for (col = 0; col < 4; col++)
1032  delete [] asPalette.channels[col];
1033 
1034  fZoomUpdate = 0;
1035  fZoomOffX = 0;
1036  fZoomOffY = 0;
1037  fZoomWidth = width;
1038  fZoomHeight = height;
1040 }
1041 
1042 ////////////////////////////////////////////////////////////////////////////////
1043 /// Delete the old image and creates a new image depending on the values
1044 /// of imageData. The size of the image is width X (imageData.fN / width).
1045 /// The color of each pixel depends on the imageData of the corresponding
1046 /// pixel. The palette is used to convert an image value into its color.
1047 /// If palette is not defined (palette = 0) a default palette is used.
1048 /// Any previously defined zooming is reset.
1049 
1050 void TASImage::SetImage(const TArrayD &imageData, UInt_t width, TImagePalette *palette)
1051 {
1052  SetImage(imageData.GetArray(), width, imageData.GetSize() / width, palette);
1053 }
1054 
1055 ////////////////////////////////////////////////////////////////////////////////
1056 /// Delete the old image and creates a new image depending on the values
1057 /// of imageData. The size of the image is width X (imageData.fN / width).
1058 /// The color of each pixel depends on the imageData of the corresponding
1059 /// pixel. The palette is used to convert an image value into its color.
1060 /// If palette is not defined (palette = 0) a default palette is used.
1061 /// Any previously defined zooming is reset.
1062 
1063 void TASImage::SetImage(const TVectorD &imageData, UInt_t width, TImagePalette *palette)
1064 {
1065  SetImage(imageData.GetMatrixArray(), width,
1066  imageData.GetNoElements() / width, palette);
1067 }
1068 
1069 ////////////////////////////////////////////////////////////////////////////////
1070 /// Create an image from the given pad, afterwards this image can be
1071 /// saved in any of the supported image formats.
1072 
1074 {
1075  if (!pad) {
1076  Error("FromPad", "pad cannot be 0");
1077  return;
1078  }
1079 
1080  if (!InitVisual()) {
1081  Warning("FromPad", "Visual not initiated");
1082  return;
1083  }
1084 
1085  SetName(pad->GetName());
1086 
1087  DestroyImage();
1088  delete fScaledImage;
1089  fScaledImage = 0;
1090 
1091  if (gROOT->IsBatch()) { // in batch mode
1092  TVirtualPS *psave = gVirtualPS;
1093  gVirtualPS = (TVirtualPS*)gROOT->ProcessLineFast("new TImageDump()");
1094  gVirtualPS->Open(pad->GetName(), 114); // in memory
1095  gVirtualPS->SetBit(BIT(11)); //kPrintingPS
1096 
1097  TASImage *itmp = (TASImage*)gVirtualPS->GetStream();
1098 
1099  if (itmp && itmp->fImage) {
1100  itmp->BeginPaint();
1101  }
1102 
1103  TVirtualPad *sav = gPad;
1104  gPad = pad;
1105  pad->Paint();
1106  gPad = sav;
1107 
1108  if (itmp && itmp->fImage && (itmp != this)) {
1109  fImage = clone_asimage(itmp->fImage, SCL_DO_ALL);
1110  if (itmp->fImage->alt.argb32) {
1111  UInt_t sz = itmp->fImage->width*itmp->fImage->height;
1112  fImage->alt.argb32 = (ARGB32*)safemalloc(sz*sizeof(ARGB32));
1113  memcpy(fImage->alt.argb32, itmp->fImage->alt.argb32, sz*4);
1114  }
1115  }
1116  delete gVirtualPS;
1117  gVirtualPS = psave;
1118  return;
1119  }
1120 
1121  // X11 Synchronization
1122  gVirtualX->Update(1);
1123  if (!gThreadXAR) {
1124  gSystem->Sleep(100);
1126  gSystem->Sleep(10);
1128  }
1129 
1130  TVirtualPad *canvas = (TVirtualPad*)pad->GetCanvas();
1131  Int_t wid = (pad == canvas) ? pad->GetCanvasID() : pad->GetPixmapID();
1132  gVirtualX->SelectWindow(wid);
1133 
1134  Window_t wd = (Window_t)gVirtualX->GetCurrentWindow();
1135  if (!wd) return;
1136 
1137  if (w == 0) w = TMath::Abs(pad->UtoPixel(1.));
1138  if (h == 0) h = pad->VtoPixel(0.);
1139 
1140  static int x11 = -1;
1141  if (x11 < 0) x11 = gVirtualX->InheritsFrom("TGX11");
1142 
1143  if (x11) { //use built-in optimized version
1144  fImage = pixmap2asimage(fgVisual, wd, x, y, w, h, kAllPlanes, 0, 0);
1145  } else {
1146  unsigned char *bits = gVirtualX->GetColorBits(wd, 0, 0, w, h);
1147 
1148  if (!bits) { // error
1149  return;
1150  }
1151  fImage = bitmap2asimage(bits, w, h, 0, 0);
1152  delete [] bits;
1153  }
1154 }
1155 
1156 ////////////////////////////////////////////////////////////////////////////////
1157 /// Draw image.
1158 /// Support the following drawing options:
1159 /// - "T[x,y[,tint]]" : tile image (use specified offset and tint),
1160 /// e.g. "T100,100,#556655"
1161 /// with this option the zooming is not possible
1162 /// and disabled
1163 /// - "N" : display in new canvas (of original image size)
1164 /// - "X" : image is drawn expanded to pad size
1165 /// - "Z" : image is vectorized and image palette is drawn
1166 ///
1167 /// The default is to display the image in the current gPad.
1168 
1170 {
1171  if (!fImage) {
1172  Error("Draw", "no image set");
1173  return;
1174  }
1175 
1176  TString opt = option;
1177  opt.ToLower();
1178  if (opt.Contains("n") || !gPad || !gPad->IsEditable()) {
1179  Int_t w = -64;
1180  Int_t h = 64;
1181  w = (fImage->width > 64) ? (Int_t)fImage->width : w;
1182  h = (fImage->height > 64) ? (Int_t)fImage->height : h;
1183 
1184  Float_t cx = 1./gStyle->GetScreenFactor();
1185  w = Int_t(w*cx) + 4;
1186  h = Int_t(h*cx) + 28;
1187  TString rname = GetName();
1188  rname.ReplaceAll(".", "");
1189  rname += Form("\", \"%s (%d x %d)", rname.Data(), fImage->width, fImage->height);
1190  rname = "new TCanvas(\"" + rname + Form("\", %d, %d);", w, h);
1191  gROOT->ProcessLineFast(rname.Data());
1192  }
1193 
1194  if (!opt.Contains("x")) {
1195  Double_t left = gPad->GetLeftMargin();
1196  Double_t right = gPad->GetRightMargin();
1197  Double_t top = gPad->GetTopMargin();
1198  Double_t bottom = gPad->GetBottomMargin();
1199 
1200  gPad->Range(-left / (1.0 - left - right),
1201  -bottom / (1.0 - top - bottom),
1202  1 + right / (1.0 - left - right),
1203  1 + top / ( 1.0 - top - bottom));
1204  gPad->RangeAxis(0, 0, 1, 1);
1205  }
1206 
1207  TFrame *frame = gPad->GetFrame();
1208  if (frame) {
1209  frame->SetBorderMode(0);
1210  frame->SetFillColor(gPad->GetFillColor());
1211  frame->SetLineColor(gPad->GetFillColor());
1212  frame->Draw();
1213  }
1214 
1215  TObject::Draw(option);
1216 }
1217 
1218 ////////////////////////////////////////////////////////////////////////////////
1219 /// Draw asimage on drawable.
1220 
1222  Int_t xsrc, Int_t ysrc, UInt_t wsrc, UInt_t hsrc,
1223  Option_t *opt)
1224 {
1225  if (!im) return;
1226 
1227  wsrc = wsrc ? wsrc : im->width;
1228  hsrc = hsrc ? hsrc : im->height;
1229 
1230  static int x11 = -1;
1231  if (x11 < 0) x11 = gVirtualX->InheritsFrom("TGX11");
1232 
1233  Pixmap_t mask = kNone;
1234 
1235  if (x11) {
1236  UInt_t hh = hsrc;
1237  UInt_t ow = wsrc%8;
1238  UInt_t ww = wsrc - ow + (ow ? 8 : 0);
1239 
1240  UInt_t bit = 0;
1241  int i = 0;
1242  UInt_t yy = 0;
1243  UInt_t xx = 0;
1244 
1245  char *bits = new char[ww*hh]; //an array of bits
1246 
1247  ASImageDecoder *imdec = start_image_decoding(fgVisual, im, SCL_DO_ALPHA,
1248  xsrc, ysrc, ww, 0, 0);
1249  if (imdec) {
1250  for (yy = 0; yy < hh; yy++) {
1251  imdec->decode_image_scanline(imdec);
1252  CARD32 *a = imdec->buffer.alpha;
1253 
1254  for (xx = 0; xx < ww; xx++) {
1255  if (a[xx]) {
1256  SETBIT(bits[i], bit);
1257  } else {
1258  CLRBIT(bits[i], bit);
1259  }
1260  bit++;
1261  if (bit == 8) {
1262  bit = 0;
1263  i++;
1264  }
1265  }
1266  }
1267  }
1268 
1269  stop_image_decoding(&imdec);
1270 
1271  mask = gVirtualX->CreateBitmap(gVirtualX->GetDefaultRootWindow(),
1272  (const char *)bits, ww, hh);
1273  delete [] bits;
1274  }
1275 
1276  GCValues_t gv;
1277  static GContext_t gc = 0;
1278 
1280  gv.fClipMask = mask;
1281  gv.fClipXOrigin = x;
1282  gv.fClipYOrigin = y;
1283 
1284  if (!gc) {
1285  gc = gVirtualX->CreateGC(gVirtualX->GetDefaultRootWindow(), &gv);
1286  } else {
1287  gVirtualX->ChangeGC(gc, &gv);
1288  }
1289 
1290  if (x11 && (!gPad || gPad->GetGLDevice() == -1)) { //use built-in optimized version
1291  asimage2drawable(fgVisual, wid, im, (GC)gc, xsrc, ysrc, x, y, wsrc, hsrc, 1);
1292  } else {
1293  ASImage *img = 0;
1294  unsigned char *bits = (unsigned char *)im->alt.argb32;
1295  if (!bits) {
1296  img = tile_asimage(fgVisual, im, xsrc, ysrc, wsrc, hsrc,
1297  0, ASA_ARGB32, 0, ASIMAGE_QUALITY_DEFAULT);
1298  if (img)
1299  bits = (unsigned char *)img->alt.argb32;
1300  }
1301 
1302  if (bits) {
1303  TString option(opt);
1304  option.ToLower();
1305 
1306  if (gPad && gPad->GetGLDevice() != -1) {
1307  if (TVirtualPadPainter *painter = gPad->GetPainter())
1308  painter->DrawPixels(bits, wsrc, hsrc, x, y, !option.Contains("opaque"));
1309  } else {
1310  Pixmap_t pic = gVirtualX->CreatePixmapFromData(bits, wsrc, hsrc);
1311  if (pic) {
1312  if (!option.Contains("opaque")) {
1313  SETBIT(wsrc,31);
1314  SETBIT(hsrc,31);
1315  }
1316  gVirtualX->CopyArea(pic, wid, gc, 0, 0, wsrc, hsrc, x, y);
1317  gVirtualX->DeletePixmap(pic);
1318  }
1319  }
1320  }
1321 
1322  if (img) {
1323  destroy_asimage(&img);
1324  }
1325  }
1326 
1327  // free mask pixmap
1328  if (gv.fClipMask != kNone) gVirtualX->DeletePixmap(gv.fClipMask);
1329 
1330  gv.fMask = kGCClipMask;
1331  gv.fClipMask = kNone;
1332  if (gc) gVirtualX->ChangeGC(gc, &gv);
1333 }
1334 
1335 ////////////////////////////////////////////////////////////////////////////////
1336 /// Draw image on the drawable wid (pixmap, window) at x,y position.
1337 ///
1338 /// \param[in] wid : Drawable (pixmap or window) on which image is drawn.
1339 /// \param[in] x,y : Window coordinates where image is drawn.
1340 /// \param[in] xsrc, ysrc : X and Y coordinates of an image area to be drawn.
1341 /// \param[in] wsrc, hsrc : Width and height image area to be drawn.
1342 
1344  UInt_t wsrc, UInt_t hsrc, Option_t *opt)
1345 {
1347  xsrc, ysrc, wsrc, hsrc, opt);
1348 }
1349 
1350 ////////////////////////////////////////////////////////////////////////////////
1351 /// Paint image.
1352 /// Support the following drawing options:
1353 /// - "T[x,y[,tint]]" : tile image (use specified offset and tint),
1354 /// e.g. "T100,100,#556655"
1355 /// with this option the zooming is not possible
1356 /// and disabled
1357 /// - "N" : display in new canvas (of original image size)
1358 /// - "X" : image is drawn expanded to pad size
1359 /// - "Z" : image is vectorized and image palette is drawn
1360 ///
1361 /// The default is to display the image in the current gPad.
1362 
1364 {
1365  if (!fImage) {
1366  Error("Paint", "no image set");
1367  return;
1368  }
1369 
1370  if (!InitVisual()) {
1371  Warning("Paint", "Visual not initiated");
1372  return;
1373  }
1374 
1375  Int_t tile_x = 0, tile_y = 0;
1376  CARD32 tile_tint = 0;
1377  Bool_t tile = kFALSE;
1378  Bool_t expand = kFALSE;
1379 
1380  TString opt = option;
1381  opt.ToLower();
1382 
1383  if (opt.Contains("t")) {
1384  char stint[64];
1385  if (sscanf(opt.Data() + opt.Index("t"), "t%d,%d,%s", &tile_x, &tile_y,
1386  stint) <= 3) {
1387  tile = kTRUE;
1388  if (parse_argb_color(stint, (CARD32*)&tile_tint) == stint)
1389  tile_tint = 0;
1390  } else {
1391  Error("Paint", "tile option error");
1392  }
1393  } else if (opt.Contains("x")) {
1394  expand = kTRUE;
1395  fConstRatio = kFALSE;
1396  } else if (opt.Contains("z")) {
1398 
1399  if (!fImage->alt.vector) {
1400  Vectorize(256);
1401  }
1402  }
1403 
1404  ASImage *image = fImage;
1405 
1406  // Get geometry of pad
1407  Int_t to_w = gPad->UtoPixel(1.);
1408  Int_t to_h = gPad->VtoPixel(0.);
1409 
1410  // remove the size by the margin of the pad
1411  if (!expand) {
1412  to_h = (Int_t)(to_h * (1.0 - gPad->GetBottomMargin() - gPad->GetTopMargin() ) + 0.5);
1413  to_w = (Int_t)(to_w * (1.0 - gPad->GetLeftMargin() - gPad->GetRightMargin() ) + 0.5);
1414  }
1415 
1416  if ((to_w < 25 || to_h < 25) && !expand) {
1417  Error("Paint", "pad too small to display an image");
1418  return;
1419  }
1420 
1421  if (GetConstRatio()) {
1422  if ((Double_t)to_w / (Double_t)fZoomWidth <
1423  (Double_t)to_h / (Double_t)fZoomHeight)
1424  to_h = Int_t(Double_t(fZoomHeight) * to_w / fZoomWidth);
1425  else
1426  to_w = Int_t(Double_t(fZoomWidth) * to_h / fZoomHeight);
1427  }
1428  // upper left corner and size of the palette in pixels
1429  Int_t pal_Ax = to_w + gPad->UtoAbsPixel(gPad->GetLeftMargin()) +
1430  (gPad->UtoAbsPixel(gPad->GetRightMargin()) / 10);
1431  Int_t pal_Ay = gPad->YtoAbsPixel(1.0);
1432  Int_t pal_x = to_w + gPad->UtoPixel(gPad->GetLeftMargin()) +
1433  (gPad->UtoPixel(gPad->GetRightMargin()) / 10);
1434  Int_t pal_y = gPad->YtoPixel(1.0);
1435  Int_t pal_w = gPad->UtoPixel(gPad->GetRightMargin()) / 3;
1436  Int_t pal_h = to_h;
1437 
1438  ASImage *grad_im = 0;
1439 
1440  if (fImage->alt.vector && fPaletteEnabled) {
1441  // draw the palette
1442  ASGradient grad;
1443  const TImagePalette &pal = GetPalette();
1444 
1445  grad.npoints = pal.fNumPoints;
1446  grad.type = GRADIENT_Top2Bottom;
1447  grad.color = new ARGB32[grad.npoints];
1448  grad.offset = new double[grad.npoints];
1449 
1450  for (Int_t pt = 0; pt < grad.npoints; pt++) {
1451  Int_t oldPt = grad.npoints - pt -1;
1452  grad.offset[pt] = 1 - pal.fPoints[oldPt];
1453  grad.color[pt] = (((ARGB32)(pal.fColorBlue[oldPt] & 0xff00)) >> 8) |
1454  (((ARGB32)(pal.fColorGreen[oldPt] & 0xff00)) ) |
1455  (((ARGB32)(pal.fColorRed[oldPt] & 0xff00)) << 8) |
1456  (((ARGB32)(pal.fColorAlpha[oldPt] & 0xff00)) << 16);
1457  }
1458 
1459  grad_im = make_gradient(fgVisual, &grad , UInt_t(pal_w),
1460  pal_h, SCL_DO_COLOR,
1461  ASA_ARGB32, GetImageCompression(), GetImageQuality());
1462 
1463  delete [] grad.color;
1464  delete [] grad.offset;
1465  }
1466 
1467  if (tile) {
1468  delete fScaledImage;
1470  if (!fScaledImage) return;
1471  fScaledImage->fImage = tile_asimage(fgVisual, fImage, tile_x, tile_y,
1472  to_w, to_h, tile_tint, ASA_ASImage,
1474  image = fScaledImage->fImage;
1475 
1476  } else if (fZoomUpdate == kZoomOps) {
1477  image = fImage;
1478 
1479  } else {
1480  // Scale and zoom image if needed
1481  if (Int_t(fImage->width) != to_w || Int_t(fImage->height) != to_h ||
1482  fImage->width != fZoomWidth || fImage->height != fZoomHeight) {
1483 
1484  if (fScaledImage && (Int_t(fScaledImage->GetWidth()) != to_w ||
1485  Int_t(fScaledImage->GetHeight()) != to_h ||
1486  fZoomUpdate)) {
1487 
1488  delete fScaledImage;
1489  fScaledImage = 0;
1490  }
1491 
1492  if (!fScaledImage) {
1494  if (!fScaledImage) return;
1495 
1496  if (fZoomWidth && fZoomHeight &&
1497  ((fImage->width != fZoomWidth) || (fImage->height != fZoomHeight))) {
1498  // zoom and scale image
1499  ASImage *tmpImage = 0;
1500 
1501  tmpImage = tile_asimage(fgVisual, fImage, fZoomOffX,
1502  fImage->height - fZoomHeight - fZoomOffY,
1503  fZoomWidth, fZoomHeight, 0, ASA_ASImage,
1505 
1506  if (tmpImage) {
1507  fScaledImage->fImage = scale_asimage(fgVisual, tmpImage, to_w, to_h,
1508  ASA_ASImage, GetImageCompression(),
1509  GetImageQuality());
1510  destroy_asimage(&tmpImage);
1511  }
1512  } else {
1513  // scale image, no zooming
1514  fScaledImage->fImage = scale_asimage(fgVisual, fImage, to_w, to_h,
1515  ASA_ASImage, GetImageCompression(),
1516  GetImageQuality());
1517  }
1518  }
1519  image = fScaledImage->fImage;
1520  }
1521  }
1522  fZoomUpdate = 0;
1523 
1524  if (!image) {
1525  Error("Paint", "image could not be rendered to display");
1526  return;
1527  }
1528 
1529  int tox = expand ? 0 : int(gPad->UtoPixel(1.) * gPad->GetLeftMargin());
1530  int toy = expand ? 0 : int(gPad->VtoPixel(0.) * gPad->GetTopMargin());
1531 
1532  if (!gROOT->IsBatch()) {
1533  Window_t wid = (Window_t)gVirtualX->GetWindowID(gPad->GetPixmapID());
1534  Image2Drawable(fScaledImage ? fScaledImage->fImage : fImage, wid, tox, toy);
1535 
1536  if (grad_im && fPaletteEnabled) {
1537  // draw color bar
1538  Image2Drawable(grad_im, wid, pal_x, pal_y);
1539 
1540  // values of palette
1541  TGaxis axis;
1542  Int_t ndiv = 510;
1543  double min = fMinValue;
1544  double max = fMaxValue;
1545  axis.SetLineColor(0); // draw white ticks
1546  Double_t pal_Xpos = gPad->AbsPixeltoX(pal_Ax + pal_w);
1547  axis.PaintAxis(pal_Xpos, gPad->PixeltoY(pal_Ay + pal_h - 1),
1548  pal_Xpos, gPad->PixeltoY(pal_Ay),
1549  min, max, ndiv, "+LU");
1550  min = fMinValue;
1551  max = fMaxValue;
1552  axis.SetLineColor(1); // draw black ticks
1553  axis.PaintAxis(pal_Xpos, gPad->AbsPixeltoY(pal_Ay + pal_h),
1554  pal_Xpos, gPad->AbsPixeltoY(pal_Ay + 1),
1555  min, max, ndiv, "+L");
1556  }
1557  }
1558 
1559  // loop over pixmap and draw image to PostScript
1560  if (gVirtualPS) {
1561  if (gVirtualPS->InheritsFrom("TImageDump")) { // PostScript is asimage
1562  TImage *dump = (TImage *)gVirtualPS->GetStream();
1563  if (!dump) return;
1564  dump->Merge(fScaledImage ? fScaledImage : this, "alphablend",
1565  gPad->XtoAbsPixel(0), gPad->YtoAbsPixel(1));
1566 
1567  if (grad_im) {
1568  TASImage tgrad;
1569  tgrad.fImage = grad_im;
1570  dump->Merge(&tgrad, "alphablend", pal_Ax, pal_Ay);
1571 
1572  // values of palette
1573  TGaxis axis;
1574  Int_t ndiv = 510;
1575  double min = fMinValue;
1576  double max = fMaxValue;
1577  axis.SetLineColor(1); // draw black ticks
1578  Double_t pal_Xpos = gPad->AbsPixeltoX(pal_Ax + pal_w);
1579  axis.PaintAxis(pal_Xpos, gPad->AbsPixeltoY(pal_Ay + pal_h),
1580  pal_Xpos, gPad->AbsPixeltoY(pal_Ay + 1),
1581  min, max, ndiv, "+L");
1582  }
1583  return;
1584  } else if (gVirtualPS->InheritsFrom("TPDF")) {
1585  Warning("Paint", "PDF not implemented yet");
1586  return;
1587  } else if (gVirtualPS->InheritsFrom("TSVG")) {
1588  Warning("Paint", "SVG not implemented yet");
1589  return;
1590  }
1591 
1592  // get special color cell to be reused during image printing
1593  TObjArray *colors = (TObjArray*) gROOT->GetListOfColors();
1594  TColor *color = 0;
1595  // Look for color by name
1596  if ((color = (TColor*)colors->FindObject("Image_PS")) == 0)
1597  color = new TColor(colors->GetEntries(), 1., 1., 1., "Image_PS");
1598 
1599  gVirtualPS->SetFillColor(color->GetNumber());
1600  gVirtualPS->SetFillStyle(1001);
1601 
1602  Double_t dx = gPad->GetX2()-gPad->GetX1();
1603  Double_t dy = gPad->GetY2()-gPad->GetY1();
1604  Double_t x1,x2,y1,y2;
1605 
1606  if (expand) {
1607  x1 = gPad->GetX1();
1608  x2 = x1+dx/image->width;
1609  y1 = gPad->GetY2();
1610  y2 = y1+dy/image->height;
1611  } else {
1612  x1 = gPad->GetX1()+dx*gPad->GetLeftMargin();
1613  x2 = x1+(dx*(1-gPad->GetRightMargin()-gPad->GetLeftMargin()))/image->width;
1614  y1 = gPad->GetY2()-dy*gPad->GetTopMargin();
1615  y2 = y1+(dy*(1-gPad->GetTopMargin()-gPad->GetBottomMargin()))/image->height;
1616  }
1617 
1618  gVirtualPS->CellArrayBegin(image->width, image->height, x1, x2, y1, y2);
1619 
1620  ASImageDecoder *imdec = start_image_decoding(fgVisual, image, SCL_DO_ALL,
1621  0, 0, image->width, image->height, 0);
1622  if (!imdec) return;
1623  for (Int_t yt = 0; yt < (Int_t)image->height; yt++) {
1624  imdec->decode_image_scanline(imdec);
1625  for (Int_t xt = 0; xt < (Int_t)image->width; xt++)
1626  gVirtualPS->CellArrayFill(imdec->buffer.red[xt],
1627  imdec->buffer.green[xt],
1628  imdec->buffer.blue[xt]);
1629  }
1630  stop_image_decoding(&imdec);
1632 
1633  // print the color bar
1634  if (grad_im) {
1635  Double_t xconv = (gPad->AbsPixeltoX(pal_Ax + pal_w) - gPad->AbsPixeltoX(pal_Ax)) / grad_im->width;
1636  Double_t yconv = (gPad->AbsPixeltoY(pal_Ay - pal_h) - gPad->AbsPixeltoY(pal_Ay)) / grad_im->height;
1637  x1 = gPad->AbsPixeltoX(pal_Ax);
1638  x2 = x1 + xconv;
1639  y2 = gPad->AbsPixeltoY(pal_Ay);
1640  y1 = y2 - yconv;
1641  gVirtualPS->CellArrayBegin(grad_im->width, grad_im->height,
1642  x1, x2, y1, y2);
1643 
1644  imdec = start_image_decoding(fgVisual, grad_im, SCL_DO_ALL,
1645  0, 0, grad_im->width, grad_im->height, 0);
1646  if (imdec) {
1647  for (Int_t yt = 0; yt < (Int_t)grad_im->height; yt++) {
1648  imdec->decode_image_scanline(imdec);
1649  for (Int_t xt = 0; xt < (Int_t)grad_im->width; xt++)
1650  gVirtualPS->CellArrayFill(imdec->buffer.red[xt],
1651  imdec->buffer.green[xt],
1652  imdec->buffer.blue[xt]);
1653  }
1654  }
1655  stop_image_decoding(&imdec);
1657 
1658  // values of palette
1659  TGaxis axis;
1660  Int_t ndiv = 510;
1661  double min = fMinValue;
1662  double max = fMaxValue;
1663  axis.SetLineColor(1); // draw black ticks
1664  Double_t pal_Xpos = gPad->AbsPixeltoX(pal_Ax + pal_w);
1665  axis.PaintAxis(pal_Xpos, gPad->AbsPixeltoY(pal_Ay + pal_h),
1666  pal_Xpos, gPad->AbsPixeltoY(pal_Ay + 1),
1667  min, max, ndiv, "+L");
1668 
1669  }
1670  }
1671 
1672  if (grad_im) {
1673  destroy_asimage(&grad_im);
1674  }
1675 }
1676 
1677 ////////////////////////////////////////////////////////////////////////////////
1678 /// Is the mouse in the image ?
1679 
1681 {
1682  Int_t pxl, pyl, pxt, pyt;
1683 
1684  Int_t px1 = gPad->XtoAbsPixel(0);
1685  Int_t py1 = gPad->YtoAbsPixel(0);
1686  Int_t px2 = gPad->XtoAbsPixel(1);
1687  Int_t py2 = gPad->YtoAbsPixel(1);
1688 
1689  if (px1 < px2) {pxl = px1; pxt = px2;}
1690  else {pxl = px2; pxt = px1;}
1691  if (py1 < py2) {pyl = py1; pyt = py2;}
1692  else {pyl = py2; pyt = py1;}
1693 
1694  if ((px > pxl && px < pxt) && (py > pyl && py < pyt))
1695  return 0;
1696 
1697  return 999999;
1698 }
1699 
1700 ////////////////////////////////////////////////////////////////////////////////
1701 /// Execute mouse events.
1702 
1704 {
1705  static TBox *ZoomBox;
1706 
1707  if (!gPad) return;
1708 
1709  if (IsEditable()) {
1710  gPad->ExecuteEvent(event, px, py);
1711  return;
1712  }
1713 
1714  gPad->SetCursor(kCross);
1715 
1716  static Int_t px1old, py1old, px2old, py2old;
1717  static Int_t px1, py1, px2, py2, pxl, pyl, pxt, pyt;
1718 
1719  if (!IsValid()) return;
1720 
1721  if (event == kButton1Motion || event == kButton1Down ||
1722  event == kButton1Up) {
1723 
1724  // convert to image pixel on screen
1725  Int_t imgX = px - gPad->XtoAbsPixel(0);
1726  Int_t imgY = py - gPad->YtoAbsPixel(1);
1727 
1728  if (imgX < 0) px = px - imgX;
1729  if (imgY < 0) py = py - imgY;
1730 
1731  ASImage *image = fImage;
1733 
1734  if (imgX >= (int)image->width) px = px - imgX + image->width - 1;
1735  if (imgY >= (int)image->height) py = py - imgY + image->height - 1;
1736 
1737  switch (event) {
1738 
1739  case kButton1Down:
1740  px1 = gPad->XtoAbsPixel(gPad->GetX1());
1741  py1 = gPad->YtoAbsPixel(gPad->GetY1());
1742  px2 = gPad->XtoAbsPixel(gPad->GetX2());
1743  py2 = gPad->YtoAbsPixel(gPad->GetY2());
1744  px1old = px; py1old = py;
1745  break;
1746 
1747  case kButton1Motion:
1748  px2old = px;
1749  px2old = TMath::Max(px2old, px1);
1750  px2old = TMath::Min(px2old, px2);
1751  py2old = py;
1752  py2old = TMath::Max(py2old, py2);
1753  py2old = TMath::Min(py2old, py1);
1754  pxl = TMath::Min(px1old, px2old);
1755  pxt = TMath::Max(px1old, px2old);
1756  pyl = TMath::Max(py1old, py2old);
1757  pyt = TMath::Min(py1old, py2old);
1758 
1759  if (ZoomBox) {
1760  ZoomBox->SetX1(gPad->AbsPixeltoX(pxl));
1761  ZoomBox->SetY1(gPad->AbsPixeltoY(pyl));
1762  ZoomBox->SetX2(gPad->AbsPixeltoX(pxt));
1763  ZoomBox->SetY2(gPad->AbsPixeltoY(pyt));
1764  }
1765  else {
1766  ZoomBox = new TBox(pxl, pyl, pxt, pyt);
1767  ZoomBox->SetFillStyle(0);
1768  ZoomBox->Draw("l*");
1769  }
1770  gPad->Modified(kTRUE);
1771  gPad->Update();
1772  break;
1773 
1774  case kButton1Up:
1775  // do nothing if zoom area is too small
1776  if ( TMath::Abs(pxl - pxt) < 5 || TMath::Abs(pyl - pyt) < 5)
1777  return;
1778 
1779  pxl = 0;
1780  pxt = 0;
1781  pyl = 0;
1782  pyt = 0;
1783 
1784  Double_t xfact = (fScaledImage) ? (Double_t)fScaledImage->fImage->width / fZoomWidth : 1;
1785  Double_t yfact = (fScaledImage) ? (Double_t)fScaledImage->fImage->height / fZoomHeight : 1;
1786 
1787  Int_t imgX1 = px1old - gPad->XtoAbsPixel(0);
1788  Int_t imgY1 = py1old - gPad->YtoAbsPixel(1);
1789  Int_t imgX2 = px - gPad->XtoAbsPixel(0);
1790  Int_t imgY2 = py - gPad->YtoAbsPixel(1);
1791 
1792  imgY1 = image->height - 1 - imgY1;
1793  imgY2 = image->height - 1 - imgY2;
1794  imgX1 = (Int_t)(imgX1 / xfact) + fZoomOffX;
1795  imgY1 = (Int_t)(imgY1 / yfact) + fZoomOffY;
1796  imgX2 = (Int_t)(imgX2 / xfact) + fZoomOffX;
1797  imgY2 = (Int_t)(imgY2 / yfact) + fZoomOffY;
1798 
1799  Zoom((imgX1 < imgX2) ? imgX1 : imgX2, (imgY1 < imgY2) ? imgY1 : imgY2,
1800  TMath::Abs(imgX1 - imgX2) + 1, TMath::Abs(imgY1 - imgY2) + 1);
1801 
1802  if (ZoomBox) {
1803  ZoomBox->Delete();
1804  ZoomBox = 0;
1805  }
1806  gPad->Modified(kTRUE);
1807  gPad->Update();
1808  break;
1809  }
1810  }
1811 }
1812 
1813 ////////////////////////////////////////////////////////////////////////////////
1814 /// Get image pixel coordinates and the pixel value at the mouse pointer.
1815 
1817 {
1818  static char info[64];
1819  info[0] = 0;
1820 
1821  if (!IsValid()) return info;
1822 
1823  // convert to image pixel on screen
1824  px -= gPad->XtoAbsPixel(0);
1825  py -= gPad->YtoAbsPixel(1);
1826 
1827  // no info if mouse is outside of image
1828  if (px < 0 || py < 0) return info;
1829 
1830  ASImage *image = fImage;
1831  if (fScaledImage) image = fScaledImage->fImage;
1832  if (px >= (int)image->width || py >= (int)image->height)
1833  return info;
1834 
1835  py = image->height - 1 - py;
1836  // convert to original image size and take zooming into account
1837  if (fScaledImage) {
1838  px = (Int_t)(px / (Double_t)fScaledImage->fImage->width * fZoomWidth ) + fZoomOffX;
1839  py = (Int_t)(py / (Double_t)fScaledImage->fImage->height * fZoomHeight) + fZoomOffY;
1840  }
1841 
1842  if (fImage->alt.vector) {
1843  snprintf(info,64,"x: %d y: %d %.5g",
1844  px, py, fImage->alt.vector[px + py * fImage->width]);
1845  } else {
1846  snprintf(info,64,"x: %d y: %d", px, py);
1847  }
1848 
1849  return info;
1850 }
1851 
1852 ////////////////////////////////////////////////////////////////////////////////
1853 /// Set a new palette to an image.
1854 /// Only images that were created with the SetImage() functions can be
1855 /// modified with this function. The previously used palette is destroyed.
1856 
1858 {
1859  TAttImage::SetPalette(palette);
1860 
1861  if (!InitVisual()) {
1862  Warning("SetPalette", "Visual not initiated");
1863  return;
1864  }
1865 
1866  if (!IsValid()) {
1867  Warning("SetPalette", "Image not valid");
1868  return;
1869  }
1870 
1871  if (fImage->alt.vector == 0)
1872  return;
1873 
1874  // copy ROOT palette to asImage palette
1875  const TImagePalette &pal = GetPalette();
1876 
1877  ASVectorPalette asPalette;
1878  asPalette.npoints = pal.fNumPoints;
1879  asPalette.channels[0] = new CARD16 [asPalette.npoints];
1880  asPalette.channels[1] = new CARD16 [asPalette.npoints];
1881  asPalette.channels[2] = new CARD16 [asPalette.npoints];
1882  asPalette.channels[3] = new CARD16 [asPalette.npoints];
1883  memcpy(asPalette.channels[0], pal.fColorBlue, pal.fNumPoints * sizeof(UShort_t));
1884  memcpy(asPalette.channels[1], pal.fColorGreen, pal.fNumPoints * sizeof(UShort_t));
1885  memcpy(asPalette.channels[2], pal.fColorRed, pal.fNumPoints * sizeof(UShort_t));
1886  memcpy(asPalette.channels[3], pal.fColorAlpha, pal.fNumPoints * sizeof(UShort_t));
1887 
1888  asPalette.points = new double[asPalette.npoints];
1889  for (Int_t point = 0; point < Int_t(asPalette.npoints); point++)
1890  asPalette.points[point] = fMinValue + (fMaxValue - fMinValue) * pal.fPoints[point];
1891 
1892  // use the new palette in this image
1893  colorize_asimage_vector(fgVisual, fImage, &asPalette, ASA_ASImage, GetImageQuality());
1894 
1895  delete [] asPalette.points;
1896  for (Int_t col = 0; col < 4; col++)
1897  delete [] asPalette.channels[col];
1898 
1899 
1900  delete fScaledImage;
1901  fScaledImage = 0;
1902 }
1903 
1904 ////////////////////////////////////////////////////////////////////////////////
1905 /// Scale the original image.
1906 /// The size of the image on the screen does not change because it is defined
1907 /// by the size of the pad.
1908 /// This function can be used to change the size of an image before writing
1909 /// it into a file. The colors of the new pixels are interpolated.
1910 /// An image created with the SetImage() functions cannot be modified with
1911 /// the function SetPalette() any more after a call of this function!
1912 
1913 void TASImage::Scale(UInt_t toWidth, UInt_t toHeight)
1914 {
1915  if (!IsValid()) {
1916  Warning("Scale", "Image not initiated");
1917  return;
1918  }
1919 
1920  if (!InitVisual()) {
1921  Warning("Scale", "Visual not initiated");
1922  return;
1923  }
1924 
1925  if (toWidth < 1)
1926  toWidth = 1;
1927  if (toHeight < 1 )
1928  toHeight = 1;
1929  if (toWidth > 30000)
1930  toWidth = 30000;
1931  if (toHeight > 30000)
1932  toHeight = 30000;
1933 
1934  ASImage *img = scale_asimage(fgVisual, fImage, toWidth, toHeight,
1935  ASA_ASImage, GetImageCompression(),
1936  GetImageQuality());
1937  DestroyImage();
1938  fImage = img;
1939  UnZoom();
1941 }
1942 
1943 ////////////////////////////////////////////////////////////////////////////////
1944 /// Another method of enlarging images where corners remain unchanged,
1945 /// but middle part gets tiled.
1946 
1947 void TASImage::Slice(UInt_t xStart, UInt_t xEnd, UInt_t yStart, UInt_t yEnd,
1948  UInt_t toWidth, UInt_t toHeight)
1949 {
1950  if (!IsValid()) {
1951  Warning("Scale", "Image not initiated");
1952  return;
1953  }
1954 
1955  if (!InitVisual()) {
1956  Warning("Scale", "Visual not initiated");
1957  return;
1958  }
1959 
1960  if (toWidth < 1)
1961  toWidth = 1;
1962  if (toHeight < 1 )
1963  toHeight = 1;
1964  if (toWidth > 30000)
1965  toWidth = 30000;
1966  if (toHeight > 30000)
1967  toHeight = 30000;
1968 
1969  ASImage *img = slice_asimage(fgVisual, fImage, xStart, xEnd,
1970  yStart, yEnd, toWidth, toHeight,
1971  ASA_ASImage, GetImageCompression(),
1972  GetImageQuality());
1973 
1974  DestroyImage();
1975  fImage = img;
1976  UnZoom();
1978 }
1979 
1980 ////////////////////////////////////////////////////////////////////////////////
1981 /// Tile the original image.
1982 
1983 void TASImage::Tile(UInt_t toWidth, UInt_t toHeight)
1984 {
1985  if (!IsValid()) {
1986  Warning("Tile", "Image not initiated");
1987  return;
1988  }
1989 
1990  if (!InitVisual()) {
1991  Warning("Tile", "Visual not initiated");
1992  return;
1993  }
1994 
1995  if (toWidth < 1)
1996  toWidth = 1;
1997  if (toHeight < 1 )
1998  toHeight = 1;
1999  if (toWidth > 30000)
2000  toWidth = 30000;
2001  if (toHeight > 30000)
2002  toHeight = 30000;
2003 
2004  ASImage *img = tile_asimage(fgVisual, fImage, 0, 0, toWidth, toHeight, 0,
2005  ASA_ASImage, GetImageCompression(), GetImageQuality());
2006  DestroyImage();
2007  fImage = img;
2008  UnZoom();
2010 }
2011 
2012 ////////////////////////////////////////////////////////////////////////////////
2013 /// The area of an image displayed in a pad is defined by this function.
2014 /// Note: the size on the screen is defined by the size of the pad.
2015 /// The original image is not modified by this function.
2016 /// If width or height is larger than the original image they are reduced to
2017 /// the width and height of the image.
2018 /// If the off values are too large (off + width > image width) than the off
2019 /// values are decreased. For example: offX = image width - width
2020 /// Note: the parameters are always relative to the original image not to the
2021 /// size of an already zoomed image.
2022 
2023 void TASImage::Zoom(UInt_t offX, UInt_t offY, UInt_t width, UInt_t height)
2024 {
2025  if (!IsValid()) {
2026  Warning("Zoom", "Image not valid");
2027  return;
2028  }
2029  fZoomUpdate = kZoom;
2030 
2031  fZoomWidth = (width == 0) ? 1 : ((width > fImage->width) ? fImage->width : width);
2032  fZoomHeight = (height == 0) ? 1 : ((height > fImage->height) ? fImage->height : height);
2033  fZoomOffX = offX;
2034  if (fZoomOffX + fZoomWidth > fImage->width)
2035  fZoomOffX = fImage->width - fZoomWidth;
2036  fZoomOffY = offY;
2037  if (fZoomOffY + fZoomHeight > fImage->height)
2038  fZoomOffY = fImage->height - fZoomHeight;
2039 }
2040 
2041 ////////////////////////////////////////////////////////////////////////////////
2042 /// Un-zoom the image to original size.
2043 /// UnZoom() - performs undo for Zoom,Crop,Scale actions
2044 
2046 {
2047  if (!IsValid()) {
2048  Warning("UnZoom", "Image not valid");
2049  return;
2050  }
2051  fZoomUpdate = kZoom;
2052  fZoomOffX = 0;
2053  fZoomOffY = 0;
2054  fZoomWidth = fImage->width;
2055  fZoomHeight = fImage->height;
2056 
2057  delete fScaledImage;
2058  fScaledImage = 0;
2059 }
2060 
2061 ////////////////////////////////////////////////////////////////////////////////
2062 /// Flip image in place.
2063 ///
2064 /// Flip is either 90, 180, 270, 180 is default.
2065 /// This function manipulates the original image and destroys the
2066 /// scaled and zoomed image which will be recreated at the next call of
2067 /// the Draw function. If the image is zoomed the zoom - coordinates are
2068 /// now relative to the new image.
2069 /// This function cannot be used for images which were created with the
2070 /// SetImage() functions, because the original pixel values would be
2071 /// destroyed.
2072 
2074 {
2075  if (!IsValid()) {
2076  Warning("Flip", "Image not valid");
2077  return;
2078  }
2079  if (!InitVisual()) {
2080  Warning("Flip", "Visual not initiated");
2081  return;
2082  }
2083 
2084  if (fImage->alt.vector) {
2085  Warning("Flip", "flip does not work for data images");
2086  return;
2087  }
2088 
2089  Int_t rflip = flip/90;
2090 
2091  UInt_t w = fImage->width;
2092  UInt_t h = fImage->height;
2093 
2094  if (rflip & 1) {
2095  w = fImage->height;
2096  h = fImage->width;
2097  }
2098 
2099  ASImage *img = flip_asimage(fgVisual, fImage, 0, 0, w, h, rflip,
2100  ASA_ASImage, GetImageCompression(),
2101  GetImageQuality());
2102  DestroyImage();
2103  fImage = img;
2104  UnZoom();
2105 }
2106 
2107 ////////////////////////////////////////////////////////////////////////////////
2108 /// Mirror image in place.
2109 ///
2110 /// If vert is true mirror in vertical axis, horizontal otherwise.
2111 /// Vertical is default.
2112 /// This function manipulates the original image and destroys the
2113 /// scaled and zoomed image which will be recreated at the next call of
2114 /// the Draw function. If the image is zoomed the zoom - coordinates are
2115 /// now relative to the new image.
2116 /// This function cannot be used for images which were created with the
2117 /// SetImage() functions, because the original pixel values would be
2118 /// destroyed.
2119 
2121 {
2122  if (!IsValid()) {
2123  Warning("Mirror", "Image not valid");
2124  return;
2125  }
2126 
2127  if (!InitVisual()) {
2128  Warning("Mirror", "Visual not initiated");
2129  return;
2130  }
2131 
2132  if (fImage->alt.vector) {
2133  Warning("Mirror", "mirror does not work for data images");
2134  return;
2135  }
2136 
2137  ASImage *img = mirror_asimage(fgVisual, fImage, 0, 0,
2138  fImage->width, fImage->height, vert,
2139  ASA_ASImage, GetImageCompression(),
2140  GetImageQuality());
2141  DestroyImage();
2142  fImage = img;
2143  UnZoom();
2144 }
2145 
2146 ////////////////////////////////////////////////////////////////////////////////
2147 /// Return width of original image not of the displayed image.
2148 /// (Number of image pixels)
2149 
2151 {
2152  return fImage ? fImage->width : 0;
2153 }
2154 
2155 ////////////////////////////////////////////////////////////////////////////////
2156 /// Return height of original image not of the displayed image.
2157 /// (Number of image pixels)
2158 
2160 {
2161  return fImage ? fImage->height : 0;
2162 }
2163 
2164 ////////////////////////////////////////////////////////////////////////////////
2165 /// Return width of the displayed image not of the original image.
2166 /// (Number of screen pixels)
2167 
2169 {
2170  return fScaledImage ? fScaledImage->fImage->width : GetWidth();
2171 }
2172 
2173 ////////////////////////////////////////////////////////////////////////////////
2174 /// Return height of the displayed image not of the original image.
2175 /// (Number of screen pixels)
2176 
2178 {
2179  return fScaledImage ? fScaledImage->fImage->height : GetHeight();
2180 }
2181 
2182 ////////////////////////////////////////////////////////////////////////////////
2183 /// Return the zoom parameters.
2184 /// This is useful when the zoom has been done interactively using the mouse.
2185 
2187 {
2188  x = fZoomOffX;
2189  y = fZoomOffY;
2190  w = fZoomWidth;
2191  h = fZoomHeight;
2192 }
2193 
2194 ////////////////////////////////////////////////////////////////////////////////
2195 /// Static function to initialize the ASVisual.
2196 
2198 {
2199  Display *disp;
2200 
2201  Bool_t inbatch = fgVisual && (fgVisual->dpy == (void*)1); // was in batch
2202  Bool_t noX = gROOT->IsBatch() || gVirtualX->InheritsFrom("TGWin32");
2203 
2204  // was in batch, but switched to gui
2205  if (inbatch && !noX) {
2206  destroy_asvisual(fgVisual, kFALSE);
2207  fgVisual = 0;
2208  }
2209 
2210  if (fgVisual && fgVisual->dpy) { // already initialized
2211  return kTRUE;
2212  }
2213 
2214  // batch or win32 mode
2215  if (!fgVisual && noX) {
2216  disp = 0;
2217  fgVisual = create_asvisual(0, 0, 0, 0);
2218  fgVisual->dpy = (Display*)1; //fake (not used)
2219  return kTRUE;
2220  }
2221 
2222 #ifndef WIN32
2223 #ifdef R__HAS_COCOA
2224  fgVisual = create_asvisual(0, 0, 0, 0);
2225  fgVisual->dpy = (Display*)1; //fake (not used)
2226 #else
2227  disp = (Display*) gVirtualX->GetDisplay();
2228  Int_t screen = gVirtualX->GetScreen();
2229  Int_t depth = gVirtualX->GetDepth();
2230  Visual *vis = (Visual*) gVirtualX->GetVisual();
2231  Colormap cmap = (Colormap) gVirtualX->GetColormap();
2232 
2233  if (vis == 0 || cmap == 0) {
2234  fgVisual = create_asvisual(0, 0, 0, 0);
2235  } else {
2236  fgVisual = create_asvisual_for_id(disp, screen, depth,
2237  XVisualIDFromVisual(vis), cmap, 0);
2238  }
2239 #endif
2240 #else
2241  fgVisual = create_asvisual(0, 0, 0, 0);
2242  fgVisual->dpy = (Display*)1; //fake (not used)
2243 #endif
2244 
2245  return kTRUE;
2246 }
2247 
2248 ////////////////////////////////////////////////////////////////////////////////
2249 /// Start palette editor.
2250 
2252 {
2253  if (!IsValid()) {
2254  Warning("StartPaletteEditor", "Image not valid");
2255  return;
2256  }
2257  if (fImage->alt.vector == 0) {
2258  Warning("StartPaletteEditor", "palette can be modified only for data images");
2259  return;
2260  }
2261 
2262  // Opens a GUI to edit the color palette
2264 }
2265 
2266 ////////////////////////////////////////////////////////////////////////////////
2267 /// Returns image pixmap.
2268 /// The pixmap must deleted by user.
2269 
2271 {
2272  if (!InitVisual()) {
2273  Warning("GetPixmap", "Visual not initiated");
2274  return 0;
2275  }
2276 
2277  Pixmap_t ret;
2278 
2279  ASImage *img = fScaledImage ? fScaledImage->fImage : fImage;
2280 
2281  static int x11 = -1;
2282  if (x11 < 0) x11 = gVirtualX->InheritsFrom("TGX11");
2283 
2284  if (x11) { // use builtin version
2285  ret = (Pixmap_t)asimage2pixmap(fgVisual, gVirtualX->GetDefaultRootWindow(),
2286  img, 0, kTRUE);
2287  } else {
2288  if (!fImage->alt.argb32) {
2289  BeginPaint();
2290  }
2291  ret = gVirtualX->CreatePixmapFromData((unsigned char*)fImage->alt.argb32,
2292  fImage->width, fImage->height);
2293  }
2294 
2295  return ret;
2296 }
2297 
2298 ////////////////////////////////////////////////////////////////////////////////
2299 /// Returns image mask pixmap (alpha channel).
2300 /// The pixmap must deleted by user.
2301 
2303 {
2304  Pixmap_t pxmap = 0;
2305 
2306  if (!InitVisual()) {
2307  Warning("GetMask", "Visual not initiated");
2308  return pxmap;
2309  }
2310 
2311  ASImage *img = fScaledImage ? fScaledImage->fImage : fImage;
2312 
2313  if (!img) {
2314  Warning("GetMask", "No image");
2315  return pxmap;
2316  }
2317 
2318  UInt_t hh = img->height;
2319  UInt_t ow = img->width%8;
2320  UInt_t ww = img->width - ow + (ow ? 8 : 0);
2321 
2322  UInt_t bit = 0;
2323  int i = 0;
2324  UInt_t y = 0;
2325  UInt_t x = 0;
2326 
2327  char *bits = new char[ww*hh]; //an array of bits
2328 
2329  ASImageDecoder *imdec = start_image_decoding(fgVisual, img, SCL_DO_ALPHA,
2330  0, 0, ww, 0, 0);
2331  if (!imdec) {
2332  delete [] bits;
2333  return 0;
2334  }
2335 
2336  for (y = 0; y < hh; y++) {
2337  imdec->decode_image_scanline(imdec);
2338  CARD32 *a = imdec->buffer.alpha;
2339 
2340  for (x = 0; x < ww; x++) {
2341  if (a[x]) {
2342  SETBIT(bits[i], bit);
2343  } else {
2344  CLRBIT(bits[i], bit);
2345  }
2346  bit++;
2347  if (bit == 8) {
2348  bit = 0;
2349  i++;
2350  }
2351  }
2352  }
2353 
2354  stop_image_decoding(&imdec);
2355  pxmap = gVirtualX->CreateBitmap(gVirtualX->GetDefaultRootWindow(), (const char *)bits,
2356  ww, hh);
2357  delete [] bits;
2358  return pxmap;
2359 }
2360 
2361 ////////////////////////////////////////////////////////////////////////////////
2362 /// Create image from pixmap.
2363 
2365 {
2366  if (!InitVisual()) {
2367  Warning("SetImage", "Visual not initiated");
2368  return;
2369  }
2370 
2371  DestroyImage();
2372  delete fScaledImage;
2373  fScaledImage = 0;
2374 
2375  Int_t xy;
2376  UInt_t w, h;
2377  gVirtualX->GetWindowSize(pxm, xy, xy, w, h);
2378 
2379  if (fName.IsNull()) fName.Form("img_%dx%d",w, h);
2380 
2381  static int x11 = -1;
2382  if (x11 < 0) x11 = gVirtualX->InheritsFrom("TGX11");
2383 
2384  if (x11) { //use built-in optimized version
2385  fImage = picture2asimage(fgVisual, pxm, mask, 0, 0, w, h, kAllPlanes, 1, 0);
2386  } else {
2387  unsigned char *bits = gVirtualX->GetColorBits(pxm, 0, 0, w, h);
2388  if (!bits) { // error
2389  return;
2390  }
2391 
2392  // no mask
2393  if (!mask) {
2394  fImage = bitmap2asimage(bits, w, h, 0, 0);
2395  delete [] bits;
2396  return;
2397  }
2398  unsigned char *mask_bits = gVirtualX->GetColorBits(mask, 0, 0, w, h);
2399  fImage = bitmap2asimage(bits, w, h, 0, mask_bits);
2400  delete [] mask_bits;
2401  delete [] bits;
2402  }
2403 }
2404 
2405 ////////////////////////////////////////////////////////////////////////////////
2406 /// Return 2D array of machine dependent pixel values.
2407 
2409 {
2410  if (!fImage) {
2411  Warning("GetPixels", "Wrong Image");
2412  return 0;
2413  }
2414 
2415  ASImage *img = fScaledImage ? fScaledImage->fImage : fImage;
2416  ASImageDecoder *imdec;
2417 
2418  width = !width ? img->width : width;
2419  height = !height ? img->height : height;
2420 
2421  if (x < 0) {
2422  width -= x;
2423  x = 0 ;
2424  }
2425  if (y < 0) {
2426  height -= y;
2427  y = 0;
2428  }
2429 
2430  if ((x >= (int)img->width) || (y >= (int)img->height)) {
2431  return 0;
2432  }
2433 
2434  if ((int)(x + width) > (int)img->width) {
2435  width = img->width - x;
2436  }
2437 
2438  if ((int)(y + height) > (int)img->height) {
2439  height = img->height - y;
2440  }
2441 
2442  if ((imdec = start_image_decoding(0, fImage, SCL_DO_ALL, 0, y,
2443  img->width, height, 0)) == 0) {
2444  Warning("GetPixels", "Failed to create image decoder");
2445  return 0;
2446  }
2447 
2448  TArrayL *ret = new TArrayL(width * height);
2449  Int_t r = 0;
2450  Int_t g = 0;
2451  Int_t b = 0;
2452  Long_t p = 0;
2453 
2454  for (UInt_t k = 0; k < height; k++) {
2455  imdec->decode_image_scanline(imdec);
2456 
2457  for (UInt_t i = 0; i < width; ++i) {
2458  if ((r == (Int_t)imdec->buffer.red[i]) &&
2459  (g == (Int_t)imdec->buffer.green[i]) &&
2460  (b == (Int_t)imdec->buffer.blue[i])) {
2461  } else {
2462  r = (Int_t)imdec->buffer.red[i];
2463  g = (Int_t)imdec->buffer.green[i];
2464  b = (Int_t)imdec->buffer.blue[i];
2465  p = (Long_t)TColor::RGB2Pixel(r, g, b);
2466  }
2467  ret->AddAt(p, k*width + i);
2468  }
2469  }
2470 
2471  stop_image_decoding(&imdec);
2472  return ret;
2473 }
2474 
2475 ////////////////////////////////////////////////////////////////////////////////
2476 /// Return a pointer to internal array[width x height] of double values [0,1].
2477 /// This array is directly accessible. That allows to manipulate/change the
2478 /// image.
2479 
2481 {
2482  if (!fImage) {
2483  Warning("GetVecArray", "Bad Image");
2484  return 0;
2485  }
2486  if (fImage->alt.vector) {
2487  return fImage->alt.vector;
2488  }
2489  // vectorize
2490  return 0;
2491 }
2492 
2493 ////////////////////////////////////////////////////////////////////////////////
2494 /// In case of vectorized image return an associated array of doubles
2495 /// otherwise this method creates and returns a 2D array of doubles corresponding to palette.
2496 /// If palette is ZERO a color converted to double value [0, 1] according to formula
2497 /// ~~~ {.cpp}
2498 /// Double_t((r << 16) + (g << 8) + b)/0xFFFFFF
2499 /// ~~~
2500 /// The returned array must be deleted after usage.
2501 
2503 {
2504  if (!fImage) {
2505  Warning("GetArray", "Bad Image");
2506  return 0;
2507  }
2508 
2509  TArrayD *ret;
2510 
2511  if (fImage->alt.vector) {
2512  ret = new TArrayD(fImage->width*fImage->height, fImage->alt.vector);
2513  return ret;
2514  }
2515 
2516  ASImageDecoder *imdec;
2517 
2518  w = w ? w : fImage->width;
2519  h = h ? h : fImage->height;
2520 
2521  if ((fImage->width != w) || (fImage->height != h)) {
2522  Scale(w, h);
2523  }
2524 
2525  ASImage *img = fScaledImage ? fScaledImage->fImage : fImage;
2526 
2527  if ((imdec = start_image_decoding(0, img, SCL_DO_ALL, 0, 0,
2528  img->width, 0, 0)) == 0) {
2529  Warning("GetArray", "Failed to create image decoder");
2530  return 0;
2531  }
2532 
2533  ret = new TArrayD(w * h);
2534  CARD32 r = 0;
2535  CARD32 g = 0;
2536  CARD32 b = 0;
2537  Int_t p = 0;
2538  Double_t v = 0;
2539 
2540  for (UInt_t k = 0; k < h; k++) {
2541  imdec->decode_image_scanline(imdec);
2542 
2543  for (UInt_t i = 0; i < w; ++i) {
2544  if ((r == imdec->buffer.red[i]) &&
2545  (g == imdec->buffer.green[i]) &&
2546  (b == imdec->buffer.blue[i])) {
2547  } else {
2548  r = imdec->buffer.red[i];
2549  g = imdec->buffer.green[i];
2550  b = imdec->buffer.blue[i];
2551  if (palette) p = palette->FindColor(r, g, b);
2552  }
2553  v = palette ? palette->fPoints[p] : Double_t((r << 16) + (g << 8) + b)/0xFFFFFF;
2554  ret->AddAt(v, (h-k-1)*w + i);
2555  }
2556  }
2557 
2558  stop_image_decoding(&imdec);
2559  return ret;
2560 }
2561 
2562 ////////////////////////////////////////////////////////////////////////////////
2563 /// Draw text of size (in pixels for TrueType fonts)
2564 /// at position (x, y) with color specified by hex string.
2565 ///
2566 /// - font_name: TrueType font's filename or X font spec or alias.
2567 /// 3D style of text is one of the following:
2568 /// * 0 plain 2D text,
2569 /// * 1 embossed,
2570 /// * 2 sunken,
2571 /// * 3 shade above,
2572 /// * 4 shade below,
2573 /// * 5 embossed thick,
2574 /// * 6 sunken thick.
2575 /// * 7 outline above,
2576 /// * 8 ouline below,
2577 /// * 9 full ouline.
2578 /// - fore_file specifies foreground texture of text.
2579 
2581  const char *color, const char *font_name,
2582  EText3DType type, const char *fore_file, Float_t angle)
2583 {
2584  UInt_t width=0, height=0;
2585  ARGB32 text_color = ARGB32_Black;
2586  ASImage *fore_im = 0;
2587  ASImage *text_im = 0;
2588  Bool_t ttfont = kFALSE;
2589 
2590  if (!InitVisual()) {
2591  Warning("DrawText", "Visual not initiated");
2592  return;
2593  }
2594 
2595  TString fn = font_name;
2596  fn.Strip();
2597 
2598  // This is for backward compatibility...
2599  if (fn.Last('/') == 0) fn = fn(1, fn.Length() - 1);
2600 
2601  const char *ttpath = gEnv->GetValue("Root.TTFontPath",
2603  char *tmpstr = gSystem->Which(ttpath, fn, kReadPermission);
2604  fn = tmpstr;
2605  delete [] tmpstr;
2606 
2607  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")) {
2608  ttfont = kTRUE;
2609  }
2610 
2611  if (color) {
2612  parse_argb_color(color, &text_color);
2613  }
2614 
2615  if (fImage && fImage->alt.argb32 && ttfont) {
2616  DrawTextTTF(x, y, text, size, text_color, fn.Data(), angle);
2617  return;
2618  }
2619 
2620  if (!gFontManager) {
2621  gFontManager = create_font_manager(fgVisual->dpy, 0, 0);
2622  }
2623 
2624  if (!gFontManager) {
2625  Warning("DrawText", "cannot create Font Manager");
2626  return;
2627  }
2628 
2629  ASFont *font = get_asfont(gFontManager, fn.Data(), 0, size, ASF_GuessWho);
2630 
2631  if (!font) {
2632  font = get_asfont(gFontManager, "fixed", 0, size, ASF_GuessWho);
2633  if (!font) {
2634  Warning("DrawText", "cannot find a font %s", font_name);
2635  return;
2636  }
2637  }
2638 
2639  get_text_size(text, font, (ASText3DType)type, &width, &height);
2640 
2641  if (!fImage) {
2642  fImage = create_asimage(width, height, 0);
2643  fill_asimage(fgVisual, fImage, 0, 0, width, height, 0xFFFFFFFF);
2644  }
2645 
2646  text_im = draw_text(text, font, (ASText3DType)type, 0);
2647 
2648  ASImage *rimg = fImage;
2649 
2650  if (fore_file) {
2651  ASImage *tmp = file2ASImage(fore_file, 0xFFFFFFFF, SCREEN_GAMMA, 0, 0);
2652  if (tmp) {
2653  if ((tmp->width != width) || (tmp->height != height)) {
2654  fore_im = tile_asimage(fgVisual, tmp, 0, 0, width, height, 0,
2655  ASA_ASImage, GetImageCompression(), GetImageQuality());
2656  }
2657  destroy_asimage(&tmp);
2658  } else {
2659  fore_im = tmp;
2660  }
2661  }
2662 
2663  if (fore_im) {
2664  move_asimage_channel(fore_im, IC_ALPHA, text_im, IC_ALPHA);
2665  destroy_asimage(&text_im);
2666  } else {
2667  fore_im = text_im ;
2668  }
2669 
2670  release_font(font);
2671 
2672  if (fore_im) {
2673  ASImage *rendered_im;
2674  ASImageLayer layers[2];
2675 
2676  init_image_layers(&(layers[0]), 2);
2677  fore_im->back_color = text_color;
2678  layers[0].im = rimg;
2679  layers[0].dst_x = 0;
2680  layers[0].dst_y = 0;
2681  layers[0].clip_width = rimg->width;
2682  layers[0].clip_height = rimg->height;
2683  layers[0].bevel = 0;
2684  layers[1].im = fore_im;
2685  layers[1].dst_x = x;
2686  layers[1].dst_y = y;
2687  layers[1].clip_width = fore_im->width;
2688  layers[1].clip_height = fore_im->height;
2689 
2690  rendered_im = merge_layers(fgVisual, &(layers[0]), 2, rimg->width, rimg->height,
2691  ASA_ASImage, GetImageCompression(), GetImageQuality());
2692 
2693  destroy_asimage(&fore_im);
2694  DestroyImage();
2695  fImage = rendered_im;
2696  UnZoom();
2697  }
2698 }
2699 
2700 ////////////////////////////////////////////////////////////////////////////////
2701 /// Merge two images.
2702 ///
2703 /// op is string which specifies overlay operation. Supported operations are:
2704 ///
2705 /// - add - color addition with saturation
2706 /// - alphablend - alpha-blending
2707 /// - allanon - color values averaging
2708 /// - colorize - hue and saturate bottom image same as top image
2709 /// - darken - use lowest color value from both images
2710 /// - diff - use absolute value of the color difference between two images
2711 /// - dissipate - randomly alpha-blend images
2712 /// - hue - hue bottom image same as top image
2713 /// - lighten - use highest color value from both images
2714 /// - overlay - some weird image overlaying(see GIMP)
2715 /// - saturate - saturate bottom image same as top image
2716 /// - screen - another weird image overlaying(see GIMP)
2717 /// - sub - color substraction with saturation
2718 /// - tint - tinting image with image
2719 /// - value - value bottom image same as top image
2720 
2721 void TASImage::Merge(const TImage *im, const char *op, Int_t x, Int_t y)
2722 {
2723  if (!im) return;
2724 
2725  if (!InitVisual()) {
2726  Warning("Merge", "Visual not initiated");
2727  return;
2728  }
2729 
2730  ASImage *rendered_im;
2731  ASImageLayer layers[2];
2732 
2733  init_image_layers(&(layers[0]), 2);
2734  layers[0].im = fImage;
2735  layers[0].dst_x = 0;
2736  layers[0].dst_y = 0;
2737  layers[0].clip_width = fImage->width;
2738  layers[0].clip_height = fImage->height;
2739  layers[0].bevel = 0;
2740  layers[1].im = ((TASImage*)im)->fImage;
2741  layers[1].dst_x = x;
2742  layers[1].dst_y = y;
2743  layers[1].clip_width = im->GetWidth();
2744  layers[1].clip_height = im->GetHeight();
2745  layers[1].merge_scanlines = blend_scanlines_name2func(op ? op : "add");
2746 
2747  rendered_im = merge_layers(fgVisual, &(layers[0]), 2, fImage->width, fImage->height,
2748  ASA_ASImage, GetImageCompression(), GetImageQuality());
2749 
2750  DestroyImage();
2751  fImage = rendered_im;
2752  UnZoom();
2753 }
2754 
2755 ////////////////////////////////////////////////////////////////////////////////
2756 /// Perform Gaussian blur of the image (useful for drop shadows).
2757 /// - hr - horizontal radius of the blur
2758 /// - vr - vertical radius of the blur
2759 
2761 {
2762  if (!InitVisual()) {
2763  Warning("Blur", "Visual not initiated");
2764  return;
2765  }
2766 
2767  if (!fImage) {
2768  fImage = create_asimage(100, 100, 0);
2769 
2770  if (!fImage) {
2771  Warning("Blur", "Failed to create image");
2772  return;
2773  }
2774 
2775  fill_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height, ARGB32_White);
2776  }
2777 
2778  ASImage *rendered_im = blur_asimage_gauss(fgVisual, fImage, hr > 0 ? hr : 3,
2779  vr > 0 ? vr : 3, SCL_DO_ALL,
2780  ASA_ASImage, GetImageCompression(), GetImageQuality());
2781  DestroyImage();
2782  fImage = rendered_im;
2783  UnZoom();
2784 }
2785 
2786 ////////////////////////////////////////////////////////////////////////////////
2787 /// Clone image.
2788 
2789 TObject *TASImage::Clone(const char *newname) const
2790 {
2791  if (!InitVisual() || !fImage) {
2792  Warning("Clone", "Image not initiated");
2793  return 0;
2794  }
2795 
2796  TASImage *im = (TASImage*)TImage::Create();
2797 
2798  if (!im) {
2799  Warning("Clone", "Failed to create image");
2800  return 0;
2801  }
2802 
2803  im->SetName(newname);
2804 
2805  im->fImage = clone_asimage(fImage, SCL_DO_ALL);
2806  im->fMaxValue = fMaxValue;
2807  im->fMinValue = fMinValue;
2808  im->fZoomOffX = fZoomOffX;
2809  im->fZoomOffY = fZoomOffY;
2810  im->fZoomWidth = fZoomWidth;
2811  im->fZoomHeight = fZoomHeight;
2812  im->fZoomUpdate = fZoomUpdate;
2814 
2815  if (fImage->alt.argb32) {
2816  UInt_t sz = fImage->width * fImage->height;
2817  im->fImage->alt.argb32 = (ARGB32*)safemalloc(sz*sizeof(ARGB32));
2818  memcpy(im->fImage->alt.argb32, fImage->alt.argb32, sz * sizeof(ARGB32));
2819  }
2820 
2821  return im;
2822 }
2823 
2824 ////////////////////////////////////////////////////////////////////////////////
2825 /// Reduce color-depth of an image and fills vector of "scientific data"
2826 /// [0...1]
2827 ///
2828 /// Colors are reduced by allocating color cells to most used colors first,
2829 /// and then approximating other colors with those allocated.
2830 ///
2831 /// \param[in] max_colors - maximum size of the colormap.
2832 /// \param[in] dither - number of bits to strip off the color data ( 0...7 )
2833 /// \param[in] opaque_threshold - alpha channel threshold at which pixel should be treated as opaque
2834 
2835 Double_t *TASImage::Vectorize(UInt_t max_colors, UInt_t dither, Int_t opaque_threshold)
2836 {
2837  if (!InitVisual()) {
2838  Warning("Vectorize", "Visual not initiated");
2839  return 0;
2840  }
2841 
2842  if (!fImage) {
2843  fImage = create_asimage(100, 100, 0);
2844 
2845  if (!fImage) {
2846  Warning("Vectorize", "Failed to create image");
2847  return 0;
2848  }
2849 
2850  fill_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height, ARGB32_White);
2851  }
2852 
2853  ASColormap cmap;
2854  int *res;
2855  UInt_t r=0, g=0, b=0;
2856 
2857  dither = dither > 7 ? 7 : dither;
2858 
2859  res = colormap_asimage(fImage, &cmap, max_colors, dither, opaque_threshold);
2860 
2861  Double_t *vec = new Double_t[fImage->height*fImage->width];
2862  UInt_t v;
2863  Double_t tmp;
2864  fMinValue = 2;
2865  fMaxValue = -1;
2866 
2867  for (UInt_t y = 0; y < fImage->height; y++) {
2868  for (UInt_t x = 0; x < fImage->width; x++) {
2869  int i = y*fImage->width + x;
2870  if (res) {
2871  g = INDEX_SHIFT_GREEN(cmap.entries[res[i]].green);
2872  b = INDEX_SHIFT_BLUE(cmap.entries[res[i]].blue);
2873  r = INDEX_SHIFT_RED(cmap.entries[res[i]].red);
2874  }
2875  v = MAKE_INDEXED_COLOR24(r,g,b);
2876  v = (v>>12)&0x0FFF;
2877  tmp = Double_t(v)/0x0FFF;
2878  vec[(fImage->height - y - 1)*fImage->width + x] = tmp;
2879  if (fMinValue > tmp) fMinValue = tmp;
2880  if (fMaxValue < tmp) fMaxValue = tmp;
2881  }
2882  }
2883  TImagePalette *pal = new TImagePalette(cmap.count);
2884 
2885  for (UInt_t j = 0; j < cmap.count; j++) {
2886  g = INDEX_SHIFT_GREEN(cmap.entries[j].green);
2887  b = INDEX_SHIFT_BLUE(cmap.entries[j].blue);
2888  r = INDEX_SHIFT_RED(cmap.entries[j].red);
2889  v = MAKE_INDEXED_COLOR24(r,g,b);
2890 
2891  v = (v>>12) & 0x0FFF;
2892  pal->fPoints[j] = Double_t(v)/0x0FFF;
2893 
2894  pal->fColorRed[j] = cmap.entries[j].red << 8;
2895  pal->fColorGreen[j] = cmap.entries[j].green << 8;
2896  pal->fColorBlue[j] = cmap.entries[j].blue << 8;
2897  pal->fColorAlpha[j] = 0xFF00;
2898  }
2899 
2900  destroy_colormap(&cmap, kTRUE);
2901 
2902  fPalette = *pal;
2903  fImage->alt.vector = vec;
2904  UnZoom();
2905  // ROOT-7647: res is allocated with `safemalloc` by colormap_asimage
2906  if (res) safefree(res);
2907  return (Double_t*)fImage->alt.vector;
2908 }
2909 
2910 ////////////////////////////////////////////////////////////////////////////////
2911 /// This function will tile original image to specified size with offsets
2912 /// requested, and then it will go though it and adjust hue, saturation and
2913 /// value of those pixels that have specific hue, set by affected_hue/
2914 /// affected_radius parameters. When affected_radius is greater then 180
2915 /// entire image will be adjusted. Note that since grayscale colors have
2916 /// no hue - the will not get adjusted. Only saturation and value will be
2917 /// adjusted in gray pixels.
2918 ///
2919 /// Hue is measured as an angle on a 360 degree circle, The following is
2920 /// relationship of hue values to regular color names :
2921 /// - red - 0
2922 /// - yellow - 60
2923 /// - green - 120
2924 /// - cyan - 180
2925 /// - blue - 240
2926 /// - magenta - 300
2927 /// - red - 360
2928 ///
2929 /// All the hue values in parameters will be adjusted to fall within 0-360 range.
2930 ///
2931 /// \param[in] hue hue in degrees in range 0-360. This allows to limit
2932 /// impact of color adjustment to affect only limited range of hues.
2933 ///
2934 /// \param[in] radius value in degrees to be used in order to
2935 /// calculate the range of affected hues. Range is determined by
2936 /// substracting and adding this value from/to affected_hue.
2937 ///
2938 /// \param[in] H value by which to change hues in affected range.
2939 /// \param[in] S value by which to change saturation of the pixels in affected hue range.
2940 /// \param[in] V value by which to change Value(brightness) of pixels in affected hue range.
2941 ///
2942 /// \param[in] x,y position on infinite surface tiled with original image, of the
2943 /// left-top corner of the area to be used for new image.
2944 ///
2945 /// \param[in] width, height size of the area of the original image to be used for new image.
2946 /// Default is current width, height of the image.
2947 
2948 void TASImage::HSV(UInt_t hue, UInt_t radius, Int_t H, Int_t S, Int_t V,
2949  Int_t x, Int_t y, UInt_t width, UInt_t height)
2950 {
2951  if (!InitVisual()) {
2952  Warning("HSV", "Visual not initiated");
2953  return;
2954  }
2955 
2956  if (!fImage) {
2957  fImage = create_asimage(width ? width : 20, height ? height : 20, 0);
2958 
2959  if (!fImage) {
2960  Warning("HSV", "Failed to create image");
2961  return;
2962  }
2963 
2964  x = 0;
2965  y = 0;
2966  fill_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height, ARGB32_White);
2967  }
2968 
2969  width = !width ? fImage->width : width;
2970  height = !height ? fImage->height : height;
2971 
2972  ASImage *rendered_im = 0;
2973 
2974  if (H || S || V) {
2975  rendered_im = adjust_asimage_hsv(fgVisual, fImage, x, y, width, height,
2976  hue, radius, H, S, V, ASA_ASImage, 100,
2977  ASIMAGE_QUALITY_TOP);
2978  }
2979  if (!rendered_im) {
2980  Warning("HSV", "Failed to create rendered image");
2981  return;
2982  }
2983 
2984  DestroyImage();
2985  fImage = rendered_im;
2986  UnZoom();
2987 }
2988 
2989 ////////////////////////////////////////////////////////////////////////////////
2990 /// Render multipoint gradient inside rectangle of size (width, height)
2991 /// at position (x,y) within the existing image.
2992 ///
2993 /// \param[in] angle Given in degrees. Default is 0. This is the
2994 /// direction of the gradient. Currently the only supported
2995 /// values are 0, 45, 90, 135, 180, 225, 270, 315. 0 means left
2996 /// to right, 90 means top to bottom, etc.
2997 ///
2998 /// \param[in] colors Whitespace-separated list of colors. At least two
2999 /// colors are required. Each color in this list will be visited
3000 /// in turn, at the intervals given by the offsets attribute.
3001 ///
3002 /// \param[in] offsets Whitespace-separated list of floating point values
3003 /// ranging from 0.0 to 1.0. The colors from the colors attribute
3004 /// are given these offsets, and the final gradient is rendered
3005 /// from the combination of the two. If both colors and offsets
3006 /// are given but the number of colors and offsets do not match,
3007 /// the minimum of the two will be used, and the other will be
3008 /// truncated to match. If offsets are not given, a smooth
3009 /// stepping from 0.0 to 1.0 will be used.
3010 
3011 void TASImage::Gradient(UInt_t angle, const char *colors, const char *offsets,
3012  Int_t x, Int_t y, UInt_t width, UInt_t height)
3013 {
3014  if (!InitVisual()) {
3015  Warning("Gradient", "Visual not initiated");
3016  return;
3017  }
3018 
3019  ASImage *rendered_im = 0;
3020  ASGradient gradient;
3021 
3022  int reverse = 0, npoints1 = 0, npoints2 = 0;
3023  char *p;
3024  char *pb;
3025  char ch;
3026  TString str = colors;
3027  TString col;
3028 
3029  if ((angle > 2 * 180 * 15 / 16) || (angle < 2 * 180 * 1 / 16)) {
3030  gradient.type = GRADIENT_Left2Right;
3031  } else if (angle < 2 * 180 * 3 / 16) {
3032  gradient.type = GRADIENT_TopLeft2BottomRight;
3033  } else if (angle < 2 * 180 * 5 / 16) {
3034  gradient.type = GRADIENT_Top2Bottom;
3035  } else if (angle < 2 * 180 * 7 / 16) {
3036  gradient.type = GRADIENT_BottomLeft2TopRight; reverse = 1;
3037  } else if (angle < 2 * 180 * 9 / 16) {
3038  gradient.type = GRADIENT_Left2Right; reverse = 1;
3039  } else if (angle < 2 * 180 * 11 / 16) {
3040  gradient.type = GRADIENT_TopLeft2BottomRight; reverse = 1;
3041  } else if (angle < 2 * 180 * 13 / 16) {
3042  gradient.type = GRADIENT_Top2Bottom; reverse = 1;
3043  } else {
3044  gradient.type = GRADIENT_BottomLeft2TopRight;
3045  }
3046 
3047  for (p = (char*)colors; isspace((int)*p); p++) { }
3048 
3049  for (npoints1 = 0; *p; npoints1++) {
3050  if (*p) {
3051  for ( ; *p && !isspace((int)*p); p++) { }
3052  }
3053  for ( ; isspace((int)*p); p++) { }
3054  }
3055  if (offsets) {
3056  for (p = (char*)offsets; isspace((int)*p); p++) { }
3057 
3058  for (npoints2 = 0; *p; npoints2++) {
3059  if (*p) {
3060  for ( ; *p && !isspace((int)*p); p++) { }
3061  }
3062  for ( ; isspace((int)*p); p++) { }
3063  }
3064  }
3065  if (npoints1 > 1) {
3066  int i;
3067  if (offsets && (npoints1 > npoints2)) npoints1 = npoints2;
3068 
3069  if (!width) {
3070  width = fImage ? fImage->width : 20;
3071  }
3072  if (!height) {
3073  height = fImage ? fImage->height : 20;
3074  }
3075 
3076  gradient.color = new ARGB32[npoints1];
3077  gradient.offset = new double[npoints1];
3078 
3079  for (p = (char*)colors; isspace((int)*p); p++) { }
3080 
3081  for (npoints1 = 0; *p; ) {
3082  pb = p;
3083 
3084  if (*p) {
3085  for ( ; *p && !isspace((int)*p); p++) { }
3086  }
3087  for ( ; isspace((int)*p); p++) { }
3088 
3089  col = str(pb - colors, p - pb);
3090 
3091  if (parse_argb_color(col.Data(), gradient.color + npoints1) != col) {
3092  npoints1++;
3093  } else {
3094  Warning("Gradient", "Failed to parse color [%s] - defaulting to black", pb);
3095  }
3096  }
3097 
3098  if (offsets) {
3099  for (p = (char*)offsets; isspace((int)*p); p++) { }
3100 
3101  for (npoints2 = 0; *p; ) {
3102  pb = p;
3103 
3104  if (*p) {
3105  for ( ; *p && !isspace((int)*p); p++) { }
3106  }
3107  ch = *p; *p = '\0';
3108  gradient.offset[npoints2] = strtod(pb, &pb);
3109 
3110  if (pb == p) npoints2++;
3111  *p = ch;
3112  for ( ; isspace((int)*p); p++) { }
3113  }
3114  } else {
3115  for (npoints2 = 0; npoints2 < npoints1; npoints2++) {
3116  gradient.offset[npoints2] = (double)npoints2 / (npoints1 - 1);
3117  }
3118  }
3119  gradient.npoints = npoints1;
3120 
3121  if (npoints2 && (gradient.npoints > npoints2)) {
3122  gradient.npoints = npoints2;
3123  }
3124  if (reverse) {
3125  for (i = 0; i < gradient.npoints/2; i++) {
3126  int i2 = gradient.npoints - 1 - i;
3127  ARGB32 c = gradient.color[i];
3128  double o = gradient.offset[i];
3129  gradient.color[i] = gradient.color[i2];
3130  gradient.color[i2] = c;
3131  gradient.offset[i] = gradient.offset[i2];
3132  gradient.offset[i2] = o;
3133  }
3134  for (i = 0; i < gradient.npoints; i++) {
3135  gradient.offset[i] = 1.0 - gradient.offset[i];
3136  }
3137  }
3138  rendered_im = make_gradient(fgVisual, &gradient, width, height, SCL_DO_ALL,
3139  ASA_ASImage, GetImageCompression(), GetImageQuality());
3140 
3141  delete [] gradient.color;
3142  delete [] gradient.offset;
3143  }
3144 
3145  if (!rendered_im) { // error
3146  Warning("Gradient", "Failed to create gradient image");
3147  return;
3148  }
3149 
3150  if (!fImage) {
3151  fImage = rendered_im;
3152  return;
3153  }
3154 
3155  ASImageLayer layers[2];
3156 
3157  init_image_layers(&(layers[0]), 2);
3158  layers[0].im = fImage;
3159  layers[0].dst_x = 0;
3160  layers[0].dst_y = 0;
3161  layers[0].clip_width = fImage->width;
3162  layers[0].clip_height = fImage->height;
3163  layers[0].bevel = 0;
3164  layers[1].im = rendered_im;
3165  layers[1].dst_x = x;
3166  layers[1].dst_y = y;
3167  layers[1].clip_width = width;
3168  layers[1].clip_height = height;
3169  layers[1].merge_scanlines = alphablend_scanlines;
3170 
3171  ASImage *merge_im = merge_layers(fgVisual, &(layers[0]), 2, fImage->width, fImage->height,
3172  ASA_ASImage, GetImageCompression(), GetImageQuality());
3173  if (!merge_im) {
3174  Warning("Gradient", "Failed to create merged image");
3175  return;
3176  }
3177 
3178  destroy_asimage(&rendered_im);
3179  DestroyImage();
3180  fImage = merge_im;
3181  UnZoom();
3182 }
3183 
3184 ////////////////////////////////////////////////////////////////////////////////
3185 /// Make component hilite.
3186 /// (used internally)
3187 
3188 static CARD8 MakeComponentHilite(int cmp)
3189 {
3190  if (cmp < 51) {
3191  cmp = 51;
3192  }
3193  cmp = (cmp * 12) / 10;
3194 
3195  return (cmp > 255) ? 255 : cmp;
3196 }
3197 
3198 ////////////////////////////////////////////////////////////////////////////////
3199 /// Calculate highlite color.
3200 /// (used internally)
3201 
3202 static ARGB32 GetHilite(ARGB32 background)
3203 {
3204  return ((MakeComponentHilite((background>>24) & 0x000000FF) << 24) & 0xFF000000) |
3205  ((MakeComponentHilite((background & 0x00FF0000) >> 16) << 16) & 0x00FF0000) |
3206  ((MakeComponentHilite((background & 0x0000FF00) >> 8) << 8) & 0x0000FF00) |
3207  ((MakeComponentHilite((background & 0x000000FF))) & 0x000000FF);
3208 }
3209 
3210 ////////////////////////////////////////////////////////////////////////////////
3211 /// Calculate shadow color.
3212 /// (used internally)
3213 
3214 static ARGB32 GetShadow(ARGB32 background)
3215 {
3216  return (background >> 1) & 0x7F7F7F7F;
3217 }
3218 
3219 ////////////////////////////////////////////////////////////////////////////////
3220 /// Get average.
3221 /// (used internally)
3222 
3223 static ARGB32 GetAverage(ARGB32 foreground, ARGB32 background)
3224 {
3225  CARD16 a, r, g, b;
3226 
3227  a = ARGB32_ALPHA8(foreground) + ARGB32_ALPHA8(background);
3228  a = (a<<3)/10;
3229  r = ARGB32_RED8(foreground) + ARGB32_RED8(background);
3230  r = (r<<3)/10;
3231  g = ARGB32_GREEN8(foreground) + ARGB32_GREEN8(background);
3232  g = (g<<3)/10;
3233  b = ARGB32_BLUE8(foreground) + ARGB32_BLUE8(background);
3234  b = (b<<3)/10;
3235 
3236  return MAKE_ARGB32(a, r, g, b);
3237 }
3238 
3239 
3240 ////////////////////////////////////////////////////////////////////////////////
3241 /// Bevel is used to create 3D effect while drawing buttons, or any other
3242 /// image that needs to be framed. Bevel is drawn using 2 primary colors:
3243 /// one for top and left sides - hi color, and another for bottom and
3244 /// right sides - low color. Bevel can be drawn over existing image or
3245 /// as newly created, as it is shown in code below:
3246 /// ~~~ {.cpp}
3247 /// TImage *img = TImage::Create();
3248 /// img->Bevel(0, 0, 400, 300, "#dddddd", "#000000", 3);
3249 /// ~~~
3250 
3252  const char *hi_color, const char *lo_color, UShort_t thick,
3253  Bool_t reverse)
3254 {
3255  if (!InitVisual()) {
3256  Warning("Bevel", "Visual not initiated");
3257  return;
3258  }
3259 
3260  ASImageBevel bevel;
3261  bevel.type = 0;
3262 
3263  ARGB32 hi=ARGB32_White, lo=ARGB32_White;
3264  parse_argb_color(hi_color, &hi);
3265  parse_argb_color(lo_color, &lo);
3266 
3267  if (reverse) {
3268  bevel.lo_color = hi;
3269  bevel.lolo_color = GetHilite(hi);
3270  bevel.hi_color = lo;
3271  bevel.hihi_color = GetShadow(lo);
3272  } else {
3273  bevel.hi_color = hi;
3274  bevel.hihi_color = GetHilite(hi);
3275  bevel.lo_color = lo;
3276  bevel.lolo_color = GetShadow(lo);
3277  }
3278  bevel.hilo_color = GetAverage(hi, lo);
3279 
3280  int extra_hilite = 2;
3281  bevel.left_outline = bevel.top_outline = bevel.right_outline = bevel.bottom_outline = thick;
3282  bevel.left_inline = bevel.top_inline = bevel.right_inline = bevel.bottom_inline = extra_hilite + 1;
3283 
3284  if (bevel.top_outline > 1) {
3285  bevel.top_inline += bevel.top_outline - 1;
3286  }
3287 
3288  if (bevel.left_outline > 1) {
3289  bevel.left_inline += bevel.left_outline - 1;
3290  }
3291 
3292  if (bevel.right_outline > 1) {
3293  bevel.right_inline += bevel.right_outline - 1;
3294  }
3295 
3296  if (bevel.bottom_outline > 1) {
3297  bevel.bottom_inline += bevel.bottom_outline - 1;
3298  }
3299 
3300  ASImage *merge_im;
3301  ARGB32 fill = ((hi>>24) != 0xff) || ((lo>>24) != 0xff) ? bevel.hilo_color : (bevel.hilo_color | 0xff000000);
3302 
3303  if (!fImage) {
3304  fImage = create_asimage(width ? width : 20, height ? height : 20, 0);
3305 
3306  if (!fImage) {
3307  Warning("Bevel", "Failed to create image");
3308  return;
3309  }
3310 
3311  x = 0;
3312  y = 0;
3313  fill_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height, fill);
3314  }
3315 
3316  width = !width ? fImage->width : width;
3317  height = !height ? fImage->height : height;
3318 
3319  ASImageLayer layers[2];
3320  init_image_layers(&(layers[0]), 2);
3321 
3322  layers[0].im = fImage;
3323  layers[0].dst_x = 0;
3324  layers[0].dst_y = 0;
3325  layers[0].clip_width = fImage->width;
3326  layers[0].clip_height = fImage->height;
3327  layers[0].bevel = 0;
3328 
3329  UInt_t w = width - (bevel.left_outline + bevel.right_outline);
3330  UInt_t h = height - (bevel.top_outline + bevel.bottom_outline);
3331  ASImage *bevel_im = create_asimage(w, h, 0);
3332 
3333  if (!bevel_im) {
3334  Warning("Bevel", "Failed to create bevel image");
3335  return;
3336  }
3337 
3338  layers[1].im = bevel_im;
3339  fill_asimage(fgVisual, bevel_im, 0, 0, w, h, fill);
3340 
3341  layers[1].dst_x = x;
3342  layers[1].dst_y = y;
3343  layers[1].clip_width = width;
3344  layers[1].clip_height = height;
3345  layers[1].bevel = &bevel;
3346  layers[1].merge_scanlines = alphablend_scanlines;
3347 
3348  merge_im = merge_layers(fgVisual, &(layers[0]), 2, fImage->width, fImage->height,
3349  ASA_ASImage, GetImageCompression(), GetImageQuality());
3350  destroy_asimage(&bevel_im);
3351 
3352  if (!merge_im) {
3353  Warning("Bevel", "Failed to image");
3354  return;
3355  }
3356 
3357  DestroyImage();
3358  fImage = merge_im;
3359  UnZoom();
3360 }
3361 
3362 
3363 ////////////////////////////////////////////////////////////////////////////////
3364 /// Enlarge image, padding it with specified color on each side in
3365 /// accordance with requested geometry.
3366 
3367 void TASImage::Pad(const char *col, UInt_t l, UInt_t r, UInt_t t, UInt_t b)
3368 {
3369  Int_t x, y;
3370  UInt_t w, h;
3371 
3372  if (!InitVisual()) {
3373  Warning("Pad", "Visual not initiated");
3374  return;
3375  }
3376 
3377  if (!fImage) {
3378  fImage = create_asimage(100, 100, 0);
3379 
3380  if (!fImage) {
3381  Warning("Pad", "Failed to create image");
3382  return;
3383  }
3384 
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();
3408 }
3409 
3410 
3411 ////////////////////////////////////////////////////////////////////////////////
3412 /// Crop an image.
3413 
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,
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();
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 
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 
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 
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) {
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 
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) {
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 
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 
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 
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 
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 
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 
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 
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  fImage = create_asimage(w, h, 0);
4093  FillRectangle(col, 0, 0, w, h);
4094  return;
4095  }
4096 
4097  if (!fImage->alt.argb32) {
4098  BeginPaint();
4099  }
4100 
4101  if (!fImage->alt.argb32) {
4102  Warning("DrawRectangle", "Failed to get pixel array");
4103  return;
4104  }
4105 
4106  ARGB32 color = ARGB32_White;
4107  parse_argb_color(col, &color);
4108 
4109  DrawHLine(y, x, x + w, (UInt_t)color, thick);
4110  DrawVLine(x + w, y, y + h, (UInt_t)color, thick);
4111  DrawHLine(y + h, x, x + w, (UInt_t)color, thick);
4112  DrawVLine(x, y, y + h, (UInt_t)color, thick);
4113  UnZoom();
4114 }
4115 
4116 ////////////////////////////////////////////////////////////////////////////////
4117 /// Draw a box.
4118 
4119 void TASImage::DrawBox(Int_t x1, Int_t y1, Int_t x2, Int_t y2, const char *col,
4120  UInt_t thick, Int_t mode)
4121 {
4122  Int_t x = TMath::Min(x1, x2);
4123  Int_t y = TMath::Min(y1, y2);
4124  Int_t w = TMath::Abs(x2 - x1);
4125  Int_t h = TMath::Abs(y2 - y1);
4126 
4127  ARGB32 color = ARGB32_White;
4128 
4129  if (!fImage) {
4130  w = w ? x+w : x+20;
4131  h = h ? y+h : y+20;
4132  fImage = create_asimage(w, h, 0);
4133  FillRectangle(col, 0, 0, w, h);
4134  return;
4135  }
4136 
4137  if (x1 == x2) {
4138  parse_argb_color(col, &color);
4139  DrawVLine(x1, y1, y2, color, 1);
4140  return;
4141  }
4142 
4143  if (y1 == y2) {
4144  parse_argb_color(col, &color);
4145  DrawHLine(y1, x1, x2, color, 1);
4146  return;
4147  }
4148 
4149 
4150  switch (mode) {
4151  case TVirtualX::kHollow:
4152  DrawRectangle(x, y, w, h, col, thick);
4153  break;
4154 
4155  case TVirtualX::kFilled:
4156  FillRectangle(col, x, y, w, h);
4157  break;
4158 
4159  default:
4160  FillRectangle(col, x, y, w, h);
4161  break;
4162  }
4163 }
4164 
4165 ////////////////////////////////////////////////////////////////////////////////
4166 /// Draw a dashed horizontal line.
4167 
4169  const char *pDash, UInt_t col, UInt_t thick)
4170 {
4171  UInt_t iDash = 0; // index of current dash
4172  int i = 0;
4173 
4174  ARGB32 color = (ARGB32)col;
4175 
4176  UInt_t half = 0;
4177 
4178  if (thick > 1) {
4179  half = thick >> 1;
4180  if (y > half) {
4181  y = y - half;
4182  } else {
4183  y = 0;
4184  thick += (y - half);
4185  }
4186  }
4187  thick = thick <= 0 ? 1 : thick;
4188 
4189  y = y + thick >= fImage->height ? fImage->height - thick - 1 : y;
4190  x2 = x2 >= fImage->width ? fImage->width - 1 : x2;
4191  x1 = x1 >= fImage->width ? fImage->width - 1 : x1;
4192 
4193  // switch x1, x2
4194  UInt_t tmp = x1;
4195  x1 = x2 < x1 ? x2 : x1;
4196  x2 = x2 < tmp ? tmp : x2;
4197 
4198  for (UInt_t x = x1; x <= x2; x++) {
4199  for (UInt_t w = 0; w < thick; w++) {
4200  if (y + w < fImage->height) {
4201  if ((iDash%2)==0) {
4202  _alphaBlend(&fImage->alt.argb32[Idx((y + w)*fImage->width + x)], &color);
4203  }
4204  }
4205  }
4206  i++;
4207 
4208  if (i >= pDash[iDash]) {
4209  iDash++;
4210  i = 0;
4211  }
4212  if (iDash >= nDash) {
4213  iDash = 0;
4214  i = 0;
4215  }
4216  }
4217 }
4218 
4219 ////////////////////////////////////////////////////////////////////////////////
4220 /// Draw a dashed vertical line.
4221 
4223  const char *pDash, UInt_t col, UInt_t thick)
4224 {
4225  UInt_t iDash = 0; // index of current dash
4226  int i = 0;
4227 
4228  ARGB32 color = (ARGB32)col;
4229 
4230  UInt_t half = 0;
4231 
4232  if (thick > 1) {
4233  half = thick >> 1;
4234  if (x > half) {
4235  x = x - half;
4236  } else {
4237  x = 0;
4238  thick += (x - half);
4239  }
4240  }
4241  thick = thick <= 0 ? 1 : thick;
4242 
4243  y2 = y2 >= fImage->height ? fImage->height - 1 : y2;
4244  y1 = y1 >= fImage->height ? fImage->height - 1 : y1;
4245 
4246  // switch x1, x2
4247  UInt_t tmp = y1;
4248  y1 = y2 < y1 ? y2 : y1;
4249  y2 = y2 < tmp ? tmp : y2;
4250 
4251  x = x + thick >= fImage->width ? fImage->width - thick - 1 : x;
4252 
4253  int yy = y1*fImage->width;
4254  for (UInt_t y = y1; y <= y2; y++) {
4255  for (UInt_t w = 0; w < thick; w++) {
4256  if (x + w < fImage->width) {
4257  if ((iDash%2)==0) {
4258  _alphaBlend(&fImage->alt.argb32[Idx(yy + (x + w))], &color);
4259  }
4260  }
4261  }
4262  i++;
4263 
4264  if (i >= pDash[iDash]) {
4265  iDash++;
4266  i = 0;
4267  }
4268  if (iDash >= nDash) {
4269  iDash = 0;
4270  i = 0;
4271  }
4272  yy += fImage->width;
4273  }
4274 }
4275 
4276 ////////////////////////////////////////////////////////////////////////////////
4277 /// Draw a dashed line with one pixel width.
4278 
4280  UInt_t nDash, const char *tDash, UInt_t color)
4281 {
4282  int dx, dy, d;
4283  int i, i1, i2;
4284  int x, y, xend, yend;
4285  int xdir, ydir;
4286  int q;
4287  UInt_t iDash = 0; // index of current dash
4288  int yy;
4289  int idx;
4290 
4291  dx = TMath::Abs(Int_t(x2) - Int_t(x1));
4292  dy = TMath::Abs(Int_t(y2) - Int_t(y1));
4293 
4294  char *pDash = new char[nDash];
4295 
4296  if (dy <= dx) {
4297  double ac = TMath::Cos(TMath::ATan2(dy, dx));
4298 
4299  for (i = 0; i < (int)nDash; i++) {
4300  pDash[i] = TMath::Nint(tDash[i] * ac);
4301  }
4302 
4303  UInt_t ddy = dy << 1;
4304  i1 = ddy;
4305  i2 = i1 - (dx << 1);
4306  d = i1 - dx;
4307  i = 0;
4308 
4309  if (x1 > x2) {
4310  x = x2;
4311  y = y2;
4312  ydir = -1;
4313  xend = x1;
4314  } else {
4315  x = x1;
4316  y = y1;
4317  ydir = 1;
4318  xend = x2;
4319  }
4320 
4321  yy = y*fImage->width;
4322  _alphaBlend(&fImage->alt.argb32[Idx(y*fImage->width + x)], &color);
4323  q = (y2 - y1) * ydir;
4324 
4325  if (q > 0) {
4326  while (x < xend) {
4327  idx = Idx(yy + x);
4328  if ((iDash%2) == 0) {
4329  _alphaBlend(&fImage->alt.argb32[idx], &color);
4330  }
4331  x++;
4332  if (d >= 0) {
4333  yy += fImage->width;
4334  d += i2;
4335  } else {
4336  d += i1;
4337  }
4338 
4339  i++;
4340  if (i >= pDash[iDash]) {
4341  iDash++;
4342  i = 0;
4343  }
4344  if (iDash >= nDash) {
4345  iDash = 0;
4346  i = 0;
4347  }
4348  }
4349  } else {
4350  while (x < xend) {
4351  idx = Idx(yy + x);
4352  if ((iDash%2) == 0) {
4353  _alphaBlend(&fImage->alt.argb32[idx], &color);
4354  }
4355  x++;
4356  if (d >= 0) {
4357  yy -= fImage->width;
4358  d += i2;
4359  } else {
4360  d += i1;
4361  }
4362 
4363  i++;
4364  if (i >= pDash[iDash]) {
4365  iDash++;
4366  i = 0;
4367  }
4368  if (iDash >= nDash) {
4369  iDash = 0;
4370  i = 0;
4371  }
4372  }
4373  }
4374  } else {
4375  double as = TMath::Sin(TMath::ATan2(dy, dx));
4376 
4377  for (i = 0; i < (int)nDash; i++) {
4378  pDash[i] = TMath::Nint(tDash[i] * as);
4379  }
4380 
4381  UInt_t ddx = dx << 1;
4382  i1 = ddx;
4383  i2 = i1 - (dy << 1);
4384  d = i1 - dy;
4385  i = 0;
4386 
4387  if (y1 > y2) {
4388  y = y2;
4389  x = x2;
4390  yend = y1;
4391  xdir = -1;
4392  } else {
4393  y = y1;
4394  x = x1;
4395  yend = y2;
4396  xdir = 1;
4397  }
4398 
4399  yy = y*fImage->width;
4400  _alphaBlend(&fImage->alt.argb32[Idx(y*fImage->width + x)], &color);
4401  q = (x2 - x1) * xdir;
4402 
4403  if (q > 0) {
4404  while (y < yend) {
4405  idx = Idx(yy + x);
4406  if ((iDash%2) == 0) {
4407  _alphaBlend(&fImage->alt.argb32[idx], &color);
4408  }
4409  y++;
4410  yy += fImage->width;
4411 
4412  if (d >= 0) {
4413  x++;
4414  d += i2;
4415  } else {
4416  d += i1;
4417  }
4418 
4419  i++;
4420  if (i >= pDash[iDash]) {
4421  iDash++;
4422  i = 0;
4423  }
4424  if (iDash >= nDash) {
4425  iDash = 0;
4426  i = 0;
4427  }
4428  }
4429  } else {
4430  while (y < yend) {
4431  idx = Idx(yy + x);
4432  if ((iDash%2) == 0) {
4433  _alphaBlend(&fImage->alt.argb32[idx], &color);
4434  }
4435  y++;
4436  yy += fImage->width;
4437 
4438  if (d >= 0) {
4439  x--;
4440  d += i2;
4441  } else {
4442  d += i1;
4443  }
4444 
4445  i++;
4446  if (i >= pDash[iDash]) {
4447  iDash++;
4448  i = 0;
4449  }
4450  if (iDash >= nDash) {
4451  iDash = 0;
4452  i = 0;
4453  }
4454  }
4455  }
4456  }
4457  delete [] pDash;
4458 }
4459 
4460 ////////////////////////////////////////////////////////////////////////////////
4461 /// Draw a dashed line with thick pixel width.
4462 
4464  UInt_t nDash, const char *tDash, UInt_t color, UInt_t thick)
4465 {
4466  int dx, dy;
4467  int i;
4468  double x, y, xend=0, yend=0, x0, y0;
4469  int xdir, ydir;
4470  int q;
4471  UInt_t iDash = 0; // index of current dash
4472 
4473  dx = TMath::Abs(Int_t(x2) - Int_t(x1));
4474  dy = TMath::Abs(Int_t(y2) - Int_t(y1));
4475 
4476  double *xDash = new double[nDash];
4477  double *yDash = new double[nDash];
4478  double a = TMath::ATan2(dy, dx);
4479  double ac = TMath::Cos(a);
4480  double as = TMath::Sin(a);
4481 
4482  for (i = 0; i < (int)nDash; i++) {
4483  xDash[i] = tDash[i] * ac;
4484  yDash[i] = tDash[i] * as;
4485 
4486  // dirty trick (must be fixed)
4487  if ((i%2) == 0) {
4488  xDash[i] = xDash[i]/2;
4489  yDash[i] = yDash[i]/2;
4490  } else {
4491  xDash[i] = xDash[i]*2;
4492  yDash[i] = yDash[i]*2;
4493  }
4494  }
4495 
4496  if (dy <= dx) {
4497  if (x1 > x2) {
4498  x = x2;
4499  y = y2;
4500  ydir = -1;
4501  xend = x1;
4502  } else {
4503  x = x1;
4504  y = y1;
4505  ydir = 1;
4506  xend = x2;
4507  }
4508 
4509  q = (y2 - y1) * ydir;
4510  x0 = x;
4511  y0 = y;
4512  iDash = 0;
4513  yend = y + q;
4514 
4515  if (q > 0) {
4516  while ((x < xend) && (y < yend)) {
4517  x += xDash[iDash];
4518  y += yDash[iDash];
4519 
4520  if ((iDash%2) == 0) {
4522  TMath::Nint(x), TMath::Nint(y), color, thick);
4523  } else {
4524  x0 = x;
4525  y0 = y;
4526  }
4527 
4528  iDash++;
4529 
4530  if (iDash >= nDash) {
4531  iDash = 0;
4532  }
4533  }
4534  } else {
4535  while ((x < xend) && (y > yend)) {
4536  x += xDash[iDash];
4537  y -= yDash[iDash];
4538 
4539  if ((iDash%2) == 0) {
4541  TMath::Nint(x), TMath::Nint(y), color, thick);
4542  } else {
4543  x0 = x;
4544  y0 = y;
4545  }
4546 
4547  iDash++;
4548 
4549  if (iDash >= nDash) {
4550  iDash = 0;
4551  }
4552  }
4553  }
4554  } else {
4555 
4556  if (y1 > y2) {
4557  y = y2;
4558  x = x2;
4559  yend = y1;
4560  xdir = -1;
4561  } else {
4562  y = y1;
4563  x = x1;
4564  yend = y2;
4565  xdir = 1;
4566  }
4567 
4568  q = (x2 - x1) * xdir;
4569  x0 = x;
4570  y0 = y;
4571  iDash = 0;
4572  xend = x + q;
4573 
4574  if (q > 0) {
4575  while ((x < xend) && (y < yend)) {
4576  x += xDash[iDash];
4577  y += yDash[iDash];
4578 
4579  if ((iDash%2) == 0) {
4581  TMath::Nint(x), TMath::Nint(y), color, thick);
4582  } else {
4583  x0 = x;
4584  y0 = y;
4585  }
4586 
4587  iDash++;
4588 
4589  if (iDash >= nDash) {
4590  iDash = 0;
4591  }
4592  }
4593  } else {
4594  while ((x > xend) && (y < yend)) {
4595  x -= xDash[iDash];
4596  y += yDash[iDash];
4597 
4598  if ((iDash%2) == 0) {
4600  TMath::Nint(x), TMath::Nint(y), color, thick);
4601  } else {
4602  x0 = x;
4603  y0 = y;
4604  }
4605 
4606  iDash++;
4607 
4608  if (iDash >= nDash) {
4609  iDash = 0;
4610  }
4611  }
4612  }
4613  }
4614  delete [] xDash;
4615  delete [] yDash;
4616 }
4617 
4618 ////////////////////////////////////////////////////////////////////////////////
4619 /// Draw a dashed line.
4620 
4622  const char *pDash, const char *col, UInt_t thick)
4623 
4624 {
4625  if (!InitVisual()) {
4626  Warning("DrawDashLine", "Visual not initiated");
4627  return;
4628  }
4629 
4630  if (!fImage) {
4631  Warning("DrawDashLine", "no image");
4632  return;
4633  }
4634 
4635  if (!fImage->alt.argb32) {
4636  BeginPaint();
4637  }
4638 
4639  if (!fImage->alt.argb32) {
4640  Warning("DrawDashLine", "Failed to get pixel array");
4641  return;
4642  }
4643 
4644  if ((nDash < 2) || !pDash || (nDash%2)) {
4645  Warning("DrawDashLine", "Wrong input parameters n=%d %ld", nDash, (Long_t)sizeof(pDash)-1);
4646  return;
4647  }
4648 
4649  ARGB32 color = ARGB32_White;
4650  parse_argb_color(col, &color);
4651 
4652  if (x1 == x2) {
4653  DrawDashVLine(x1, y1, y2, nDash, pDash, (UInt_t)color, thick);
4654  } else if (y1 == y2) {
4655  DrawDashHLine(y1, x1, x2, nDash, pDash, (UInt_t)color, thick);
4656  } else {
4657  if (thick < 2) DrawDashZLine(x1, y1, x2, y2, nDash, pDash, (UInt_t)color);
4658  else DrawDashZTLine(x1, y1, x2, y2, nDash, pDash, (UInt_t)color, thick);
4659  }
4660 }
4661 
4662 ////////////////////////////////////////////////////////////////////////////////
4663 /// Draw a polyline.
4664 
4665 void TASImage::DrawPolyLine(UInt_t nn, TPoint *xy, const char *col, UInt_t thick,
4666  TImage::ECoordMode mode)
4667 {
4668  ARGB32 color = ARGB32_White;
4669  parse_argb_color(col, &color);
4670 
4671  Int_t x0 = xy[0].GetX();
4672  Int_t y0 = xy[0].GetY();
4673  Int_t x = 0;
4674  Int_t y = 0;
4675 
4676  for (UInt_t i = 1; i < nn; i++) {
4677  x = (mode == kCoordModePrevious) ? x + xy[i].GetX() : xy[i].GetX();
4678  y = (mode == kCoordModePrevious) ? y + xy[i].GetY() : xy[i].GetY();
4679 
4680  DrawLineInternal(x0, y0, x, y, (UInt_t)color, thick);
4681 
4682  x0 = x;
4683  y0 = y;
4684  }
4685 }
4686 
4687 ////////////////////////////////////////////////////////////////////////////////
4688 /// Draw a point at the specified position.
4689 
4690 void TASImage::PutPixel(Int_t x, Int_t y, const char *col)
4691 {
4692  if (!InitVisual()) {
4693  Warning("PutPixel", "Visual not initiated");
4694  return;
4695  }
4696 
4697  if (!fImage) {
4698  Warning("PutPixel", "no image");
4699  return;
4700  }
4701 
4702  if (!fImage->alt.argb32) {
4703  BeginPaint();
4704  }
4705 
4706  if (!fImage->alt.argb32) {
4707  Warning("PutPixel", "Failed to get pixel array");
4708  return;
4709  }
4710 
4711  ARGB32 color;
4712  parse_argb_color(col, &color);
4713 
4714  if ((x < 0) || (y < 0) || (x >= (int)fImage->width) || (y >= (int)fImage->height)) {
4715  Warning("PutPixel", "Out of range width=%d x=%d, height=%d y=%d",
4716  fImage->width, x, fImage->height, y);
4717  return;
4718  }
4719  _alphaBlend(&fImage->alt.argb32[Idx(y*fImage->width + x)], &color);
4720 }
4721 
4722 ////////////////////////////////////////////////////////////////////////////////
4723 /// Draw a poly point.
4724 
4725 void TASImage::PolyPoint(UInt_t npt, TPoint *ppt, const char *col, TImage::ECoordMode mode)
4726 {
4727  if (!InitVisual()) {
4728  Warning("PolyPoint", "Visual not initiated");
4729  return;
4730  }
4731 
4732  if (!fImage) {
4733  Warning("PolyPoint", "no image");
4734  return;
4735  }
4736 
4737  if (!fImage->alt.argb32) {
4738  BeginPaint();
4739  }
4740 
4741  if (!fImage->alt.argb32) {
4742  Warning("PolyPoint", "Failed to get pixel array");
4743  return;
4744  }
4745 
4746  if (!npt || !ppt) {
4747  Warning("PolyPoint", "No points specified");
4748  return;
4749  }
4750 
4751  TPoint *ipt = 0;
4752  UInt_t i = 0;
4753  ARGB32 color;
4754  parse_argb_color(col, &color);
4755 
4756  //make pointlist origin relative
4757  if (mode == kCoordModePrevious) {
4758  ipt = new TPoint[npt];
4759 
4760  for (i = 0; i < npt; i++) {
4761  ipt[i].fX += ppt[i].fX;
4762  ipt[i].fY += ppt[i].fY;
4763  }
4764  }
4765  int x, y;
4766 
4767  for (i = 0; i < npt; i++) {
4768  x = ipt ? ipt[i].fX : ppt[i].fX;
4769  y = ipt ? ipt[i].fY : ppt[i].fY;
4770 
4771  if ((x < 0) || (y < 0) || (x >= (int)fImage->width) || (y >= (int)fImage->height)) {
4772  continue;
4773  }
4774  _alphaBlend(&fImage->alt.argb32[Idx(y*fImage->width + x)], &color);
4775  }
4776 
4777  if (ipt) {
4778  delete [] ipt;
4779  }
4780 }
4781 
4782 ////////////////////////////////////////////////////////////////////////////////
4783 /// Draw segments.
4784 
4785 void TASImage::DrawSegments(UInt_t nseg, Segment_t *seg, const char *col, UInt_t thick)
4786 {
4787  if (!nseg || !seg) {
4788  Warning("DrawSegments", "Invalid data nseg=%d seg=0x%lx", nseg, (Long_t)seg);
4789  return;
4790  }
4791 
4792  TPoint pt[2];
4793 
4794  for (UInt_t i = 0; i < nseg; i++) {
4795  pt[0].fX = seg->fX1;
4796  pt[1].fX = seg->fX2;
4797  pt[0].fY = seg->fY1;
4798  pt[1].fY = seg->fY2;
4799 
4800  DrawPolyLine(2, pt, col, thick, kCoordModeOrigin);
4801  seg++;
4802  }
4803 }
4804 
4805 ////////////////////////////////////////////////////////////////////////////////
4806 /// Fill spans with specified color or/and stipple.
4807 
4808 void TASImage::FillSpans(UInt_t npt, TPoint *ppt, UInt_t *widths, const char *col,
4809  const char *stipple, UInt_t w, UInt_t h)
4810 {
4811  if (!InitVisual()) {
4812  Warning("FillSpans", "Visual not initiated");
4813  return;
4814  }
4815 
4816  if (!fImage) {
4817  Warning("FillSpans", "no image");
4818  return;
4819  }
4820 
4821  if (!fImage->alt.argb32) {
4822  BeginPaint();
4823  }
4824 
4825  if (!fImage->alt.argb32) {
4826  Warning("FillSpans", "Failed to get pixel array");
4827  return;
4828  }
4829 
4830  if (!npt || !ppt || !widths || (stipple && (!w || !h))) {
4831  Warning("FillSpans", "Invalid input data npt=%d ppt=0x%lx col=%s widths=0x%lx stipple=0x%lx w=%d h=%d",
4832  npt, (Long_t)ppt, col, (Long_t)widths, (Long_t)stipple, w, h);
4833  return;
4834  }
4835 
4836  ARGB32 color;
4837  parse_argb_color(col, &color);
4838  Int_t idx = 0;
4839  UInt_t x = 0;
4840  UInt_t yy;
4841 
4842  for (UInt_t i = 0; i < npt; i++) {
4843  yy = ppt[i].fY*fImage->width;
4844  for (UInt_t j = 0; j < widths[i]; j++) {
4845  if ((ppt[i].fX >= (Int_t)fImage->width) || (ppt[i].fX < 0) ||
4846  (ppt[i].fY >= (Int_t)fImage->height) || (ppt[i].fY < 0)) continue;
4847 
4848  x = ppt[i].fX + j;
4849  idx = Idx(yy + x);
4850 
4851  if (!stipple) {
4852  _alphaBlend(&fImage->alt.argb32[idx], &color);
4853  } else {
4854  Int_t ii = (ppt[i].fY%h)*w + x%w;
4855 
4856  if (stipple[ii >> 3] & (1 << (ii%8))) {
4857  _alphaBlend(&fImage->alt.argb32[idx], &color);
4858  }
4859  }
4860  }
4861  }
4862 }
4863 
4864 ////////////////////////////////////////////////////////////////////////////////
4865 /// Fill spans with tile image.
4866 
4867 void TASImage::FillSpans(UInt_t npt, TPoint *ppt, UInt_t *widths, TImage *tile)
4868 {
4869  if (!InitVisual()) {
4870  Warning("FillSpans", "Visual not initiated");
4871  return;
4872  }
4873 
4874  if (!fImage) {
4875  Warning("FillSpans", "no image");
4876  return;
4877  }
4878 
4879  if (!fImage->alt.argb32) {
4880  BeginPaint();
4881  }
4882 
4883  if (!fImage->alt.argb32) {
4884  Warning("FillSpans", "Failed to get pixel array");
4885  return;
4886  }
4887 
4888  if (!npt || !ppt || !widths || !tile) {
4889  Warning("FillSpans", "Invalid input data npt=%d ppt=0x%lx widths=0x%lx tile=0x%lx",
4890  npt, (Long_t)ppt, (Long_t)widths, (Long_t)tile);
4891  return;
4892  }
4893 
4894  Int_t idx = 0;
4895  Int_t ii = 0;
4896  UInt_t x = 0;
4897  UInt_t *arr = tile->GetArgbArray();
4898  if (!arr) return;
4899  UInt_t xx = 0;
4900  UInt_t yy = 0;
4901 
4902  for (UInt_t i = 0; i < npt; i++) {
4903  UInt_t yyy = ppt[i].fY*fImage->width;
4904 
4905  for (UInt_t j = 0; j < widths[i]; j++) {
4906  if ((ppt[i].fX >= (Int_t)fImage->width) || (ppt[i].fX < 0) ||
4907  (ppt[i].fY >= (Int_t)fImage->height) || (ppt[i].fY < 0)) continue;
4908  x = ppt[i].fX + j;
4909  idx = Idx(yyy + x);
4910  xx = x%tile->GetWidth();
4911  yy = ppt[i].fY%tile->GetHeight();
4912  ii = yy*tile->GetWidth() + xx;
4913  _alphaBlend(&fImage->alt.argb32[idx], &arr[ii]);
4914  }
4915  }
4916 }
4917 
4918 ////////////////////////////////////////////////////////////////////////////////
4919 /// Crop spans.
4920 
4921 void TASImage::CropSpans(UInt_t npt, TPoint *ppt, UInt_t *widths)
4922 {
4923  if (!InitVisual()) {
4924  Warning("CropSpans", "Visual not initiated");
4925  return;
4926  }
4927 
4928  if (!fImage) {
4929  Warning("CropSpans", "no image");
4930  return;
4931  }
4932 
4933  if (!fImage->alt.argb32) {
4934  BeginPaint();
4935  }
4936 
4937  if (!fImage->alt.argb32) {
4938  Warning("CropSpans", "Failed to get pixel array");
4939  return;
4940  }
4941 
4942  if (!npt || !ppt || !widths) {
4943  Warning("CropSpans", "No points specified npt=%d ppt=0x%lx widths=0x%lx", npt, (Long_t)ppt, (Long_t)widths);
4944  return;
4945  }
4946 
4947  int y0 = ppt[0].fY;
4948  int y1 = ppt[npt-1].fY;
4949  UInt_t y = 0;
4950  UInt_t x = 0;
4951  UInt_t i = 0;
4952  UInt_t idx = 0;
4953  UInt_t sz = fImage->width*fImage->height;
4954  UInt_t yy = y*fImage->width;
4955 
4956  for (y = 0; (int)y < y0; y++) {
4957  for (x = 0; x < fImage->width; x++) {
4958  idx = Idx(yy + x);
4959  if (idx < sz) fImage->alt.argb32[idx] = 0;
4960  }
4961  yy += fImage->width;
4962  }
4963 
4964  for (i = 0; i < npt; i++) {
4965  for (x = 0; (int)x < ppt[i].fX; x++) {
4966  idx = Idx(ppt[i].fY*fImage->width + x);
4967  if (idx < sz) fImage->alt.argb32[idx] = 0;
4968  }
4969  for (x = ppt[i].fX + widths[i] + 1; x < fImage->width; x++) {
4970  idx = Idx(ppt[i].fY*fImage->width + x);
4971  if (idx < sz) fImage->alt.argb32[idx] = 0;
4972  }
4973  }
4974 
4975  yy = y1*fImage->width;
4976  for (y = y1; y < fImage->height; y++) {
4977  for (x = 0; x < fImage->width; x++) {
4978  idx = Idx(yy + x);
4979  if (idx < sz) fImage->alt.argb32[idx] = 0;
4980  }
4981  yy += fImage->width;
4982  }
4983 }
4984 
4985 ////////////////////////////////////////////////////////////////////////////////
4986 /// Copy source region to the destination image. Copy is done according
4987 /// to specified function:
4988 /// ~~~ {.cpp}
4989 /// enum EGraphicsFunction {
4990 /// kGXclear = 0, // 0
4991 /// kGXand, // src AND dst
4992 /// kGXandReverse, // src AND NOT dst
4993 /// kGXcopy, // src (default)
4994 /// kGXandInverted, // NOT src AND dst
4995 /// kGXnoop, // dst
4996 /// kGXxor, // src XOR dst
4997 /// kGXor, // src OR dst
4998 /// kGXnor, // NOT src AND NOT dst
4999 /// kGXequiv, // NOT src XOR dst
5000 /// kGXinvert, // NOT dst
5001 /// kGXorReverse, // src OR NOT dst
5002 /// kGXcopyInverted, // NOT src
5003 /// kGXorInverted, // NOT src OR dst
5004 /// kGXnand, // NOT src OR NOT dst
5005 /// kGXset // 1
5006 /// };
5007 /// ~~~
5008 
5009 void TASImage::CopyArea(TImage *dst, Int_t xsrc, Int_t ysrc, UInt_t w, UInt_t h,
5010  Int_t xdst, Int_t ydst, Int_t gfunc, EColorChan)
5011 {
5012  if (!InitVisual()) {
5013  Warning("CopyArea", "Visual not initiated");
5014  return;
5015  }
5016 
5017  if (!fImage) {
5018  Warning("CopyArea", "no image");
5019  return;
5020  }
5021  if (!dst) return;
5022 
5023  ASImage *out = ((TASImage*)dst)->GetImage();
5024 
5025  int x = 0;
5026  int y = 0;
5027  int idx = 0;
5028  int idx2 = 0;
5029  xsrc = xsrc < 0 ? 0 : xsrc;
5030  ysrc = ysrc < 0 ? 0 : ysrc;
5031 
5032  if ((xsrc >= (int)fImage->width) || (ysrc >= (int)fImage->height)) return;
5033 
5034  w = xsrc + w > fImage->width ? fImage->width - xsrc : w;
5035  h = ysrc + h > fImage->height ? fImage->height - ysrc : h;
5036  UInt_t yy = (ysrc + y)*fImage->width;
5037 
5038  if (!fImage->alt.argb32) {
5039  BeginPaint();
5040  }
5041  if (!out->alt.argb32) {
5042  dst->BeginPaint();
5043  out = ((TASImage*)dst)->GetImage();
5044  }
5045 
5046  if (fImage->alt.argb32 && out->alt.argb32) {
5047  for (y = 0; y < (int)h; y++) {
5048  for (x = 0; x < (int)w; x++) {
5049  idx = Idx(yy + x + xsrc);
5050  if ((x + xdst < 0) || (ydst + y < 0) ||
5051  (x + xdst >= (int)out->width) || (y + ydst >= (int)out->height) ) continue;
5052 
5053  idx2 = Idx((ydst + y)*out->width + x + xdst);
5054 
5055  switch ((EGraphicsFunction)gfunc) {
5056  case kGXclear:
5057  out->alt.argb32[idx2] = 0;
5058  break;
5059  case kGXand:
5060  out->alt.argb32[idx2] &= fImage->alt.argb32[idx];
5061  break;
5062  case kGXandReverse:
5063  out->alt.argb32[idx2] = fImage->alt.argb32[idx] & (~out->alt.argb32[idx2]);
5064  break;
5065  case kGXandInverted:
5066  out->alt.argb32[idx2] &= ~fImage->alt.argb32[idx];
5067  break;
5068  case kGXnoop:
5069  break;
5070  case kGXxor:
5071  out->alt.argb32[idx2] ^= fImage->alt.argb32[idx];
5072  break;
5073  case kGXor:
5074  out->alt.argb32[idx2] |= fImage->alt.argb32[idx];
5075  break;
5076  case kGXnor:
5077  out->alt.argb32[idx2] = (~fImage->alt.argb32[idx]) & (~out->alt.argb32[idx2]);
5078  break;
5079  case kGXequiv:
5080  out->alt.argb32[idx2] ^= ~fImage->alt.argb32[idx];
5081  break;
5082  case kGXinvert:
5083  out->alt.argb32[idx2] = ~out->alt.argb32[idx2];
5084  break;
5085  case kGXorReverse:
5086  out->alt.argb32[idx2] = fImage->alt.argb32[idx] | (~out->alt.argb32[idx2]);
5087  break;
5088  case kGXcopyInverted:
5089  out->alt.argb32[idx2] = ~fImage->alt.argb32[idx];
5090  break;
5091  case kGXorInverted:
5092  out->alt.argb32[idx2] |= ~fImage->alt.argb32[idx];
5093  break;
5094  case kGXnand:
5095  out->alt.argb32[idx2] = (~fImage->alt.argb32[idx]) | (~out->alt.argb32[idx2]);
5096  break;
5097  case kGXset:
5098  out->alt.argb32[idx2] = 0xFFFFFFFF;
5099  break;
5100  case kGXcopy:
5101  default:
5102  out->alt.argb32[idx2] = fImage->alt.argb32[idx];
5103  break;
5104  }
5105  }
5106  yy += fImage->width;
5107  }
5108  }
5109 }
5110 
5111 ////////////////////////////////////////////////////////////////////////////////
5112 /// Draw a cell array.
5113 ///
5114 /// \param[in] x1,y1 : left down corner
5115 /// \param[in] x2,y2 : right up corner
5116 /// \param[in] nx,ny : array size
5117 /// \param[in] ic : array of ARGB32 colors
5118 ///
5119 /// Draw a cell array. The drawing is done with the pixel precision
5120 /// if (X2-X1)/NX (or Y) is not a exact pixel number the position of
5121 /// the top right corner may be wrong.
5122 
5124  Int_t ny, UInt_t *ic)
5125 {
5126  int i, j, ix, iy, w, h;
5127 
5128  ARGB32 color = 0xFFFFFFFF;
5129  ARGB32 icol;
5130 
5131  w = TMath::Max((x2-x1)/(nx),1);
5132  h = TMath::Max((y1-y2)/(ny),1);
5133  ix = x1;
5134 
5135  for (i = 0; i < nx; i++) {
5136  iy = y1 - h;
5137  for (j = 0; j < ny; j++) {
5138  icol = (ARGB32)ic[i + (nx*j)];
5139  if (icol != color) {
5140  color = icol;
5141  }
5142  FillRectangleInternal((UInt_t)color, ix, iy, w, h);
5143  iy = iy - h;
5144  }
5145  ix = ix + w;
5146  }
5147 }
5148 
5149 ////////////////////////////////////////////////////////////////////////////////
5150 /// Return alpha-blended value computed from bottom and top pixel values.
5151 
5153 {
5154  UInt_t ret = bot;
5155 
5156  _alphaBlend(&ret, &top);
5157  return ret;
5158 }
5159 
5160 ////////////////////////////////////////////////////////////////////////////////
5161 /// Return visual.
5162 
5163 const ASVisual *TASImage::GetVisual()
5164 {
5165  return fgVisual;
5166 }
5167 
5168 ////////////////////////////////////////////////////////////////////////////////
5169 /// Get poly bounds along Y.
5170 
5171 static int GetPolyYBounds(TPoint *pts, int n, int *by, int *ty)
5172 {
5173  TPoint *ptMin;
5174  int ymin, ymax;
5175  TPoint *ptsStart = pts;
5176 
5177  ptMin = pts;
5178  ymin = ymax = (pts++)->fY;
5179 
5180  while (--n > 0) {
5181  if (pts->fY < ymin) {
5182  ptMin = pts;
5183  ymin = pts->fY;
5184  }
5185  if (pts->fY > ymax) {
5186  ymax = pts->fY;
5187  }
5188  pts++;
5189  }
5190 
5191  *by = ymin;
5192  *ty = ymax;
5193  return (ptMin - ptsStart);
5194 }
5195 
5196 ////////////////////////////////////////////////////////////////////////////////
5197 /// The code is based on Xserver/mi/mipolycon.c
5198 /// "Copyright 1987, 1998 The Open Group"
5199 
5201  TPoint **outPoint, UInt_t **outWidth)
5202 {
5203  int xl = 0; // x vals of leftedges
5204  int xr = 0; // x vals of right edges
5205  int dl = 0; // decision variables
5206  int dr = 0; // decision variables
5207  int ml = 0; // left edge slope
5208  int m1l = 0; // left edge slope+1
5209  int mr = 0, m1r = 0; // right edge slope and slope+1
5210  int incr1l = 0, incr2l = 0; // left edge error increments
5211  int incr1r = 0, incr2r = 0; // right edge error increments
5212  int dy; // delta y
5213  int y; // current scanline
5214  int left, right; // indices to first endpoints
5215  int i; // loop counter
5216  int nextleft, nextright; // indices to second endpoints
5217  TPoint *ptsOut; // output buffer
5218  UInt_t *width; // output buffer
5219  TPoint *firstPoint=0;
5220  UInt_t *firstWidth=0;
5221  int imin; // index of smallest vertex (in y)
5222  int ymin; // y-extents of polygon
5223  int ymax;
5224  Bool_t ret = kTRUE;
5225 
5226  *nspans = 0;
5227 
5228  if (!InitVisual()) {
5229  Warning("GetPolygonSpans", "Visual not initiated");
5230  return kFALSE;
5231  }
5232 
5233  if (!fImage) {
5234  Warning("GetPolygonSpans", "no image");
5235  return kFALSE;
5236  }
5237 
5238  if (!fImage->alt.argb32) {
5239  BeginPaint();
5240  }
5241 
5242  if (!fImage->alt.argb32) {
5243  Warning("GetPolygonSpans", "Failed to get pixel array");
5244  return kFALSE;
5245  }
5246 
5247  if ((npt < 3) || !ppt) {
5248  Warning("GetPolygonSpans", "No points specified npt=%d ppt=0x%lx", npt, (Long_t)ppt);
5249  return kFALSE;
5250  }
5251 
5252  // find leftx, bottomy, rightx, topy, and the index
5253  // of bottomy. Also translate the points.
5254  imin = GetPolyYBounds(ppt, npt, &ymin, &ymax);
5255 
5256  dy = ymax - ymin + 1;
5257  if ((npt < 3) || (dy < 0)) return kFALSE;
5258 
5259  ptsOut = firstPoint = new TPoint[dy];
5260  width = firstWidth = new UInt_t[dy];
5261  ret = kTRUE;
5262 
5263  nextleft = nextright = imin;
5264  y = ppt[nextleft].fY;
5265 
5266  // loop through all edges of the polygon
5267  do {
5268  // add a left edge if we need to
5269  if (ppt[nextleft].fY == y) {
5270  left = nextleft;
5271 
5272  // find the next edge, considering the end
5273  // conditions of the array.
5274  nextleft++;
5275  if (nextleft >= (int)npt) {
5276  nextleft = 0;
5277  }
5278 
5279  // now compute all of the random information
5280  // needed to run the iterative algorithm.
5281  BRESINITPGON(ppt[nextleft].fY - ppt[left].fY,
5282  ppt[left].fX, ppt[nextleft].fX,
5283  xl, dl, ml, m1l, incr1l, incr2l);
5284  }
5285 
5286  // add a right edge if we need to
5287  if (ppt[nextright].fY == y) {
5288  right = nextright;
5289 
5290  // find the next edge, considering the end
5291  // conditions of the array.
5292  nextright--;
5293  if (nextright < 0) {
5294  nextright = npt-1;
5295  }
5296 
5297  // now compute all of the random information
5298  // needed to run the iterative algorithm.
5299  BRESINITPGON(ppt[nextright].fY - ppt[right].fY,
5300  ppt[right].fX, ppt[nextright].fX,
5301  xr, dr, mr, m1r, incr1r, incr2r);
5302  }
5303 
5304  // generate scans to fill while we still have
5305  // a right edge as well as a left edge.
5306  i = TMath::Min(ppt[nextleft].fY, ppt[nextright].fY) - y;
5307 
5308  // in case of non-convex polygon
5309  if (i < 0) {
5310  delete [] firstWidth;
5311  delete [] firstPoint;
5312  return kTRUE;
5313  }
5314 
5315  while (i-- > 0) {
5316  ptsOut->fY = y;
5317 
5318  // reverse the edges if necessary
5319  if (xl < xr) {
5320  *(width++) = xr - xl;
5321  (ptsOut++)->fX = xl;
5322  } else {
5323  *(width++) = xl - xr;
5324  (ptsOut++)->fX = xr;
5325  }
5326  y++;
5327 
5328  // increment down the edges
5329  BRESINCRPGON(dl, xl, ml, m1l, incr1l, incr2l);
5330  BRESINCRPGON(dr, xr, mr, m1r, incr1r, incr2r);
5331  }
5332  } while (y != ymax);
5333 
5334  *nspans = UInt_t(ptsOut - firstPoint);
5335  *outPoint = firstPoint;
5336  *outWidth = firstWidth;
5337 
5338  return ret;
5339 }
5340 
5341 ////////////////////////////////////////////////////////////////////////////////
5342 /// Fill a convex polygon with background color or bitmap.
5343 /// For non convex polygon one must use DrawFillArea method
5344 
5345 void TASImage::FillPolygon(UInt_t npt, TPoint *ppt, const char *col,
5346  const char *stipple, UInt_t w, UInt_t h)
5347 {
5348  UInt_t nspans = 0;
5349  TPoint *firstPoint = 0; // output buffer
5350  UInt_t *firstWidth = 0; // output buffer
5351 
5352  Bool_t del = GetPolygonSpans(npt, ppt, &nspans, &firstPoint, &firstWidth);
5353  ARGB32 color = ARGB32_White;
5354  parse_argb_color(col, &color);
5355 
5356  if (nspans) {
5357  if (!stipple && ((color & 0xff000000)==0xff000000)) { //no stipple no alpha
5358  FillSpansInternal(nspans, firstPoint, firstWidth, color);
5359  } else {
5360  FillSpans(nspans, firstPoint, firstWidth, col, stipple, w, h);
5361  }
5362 
5363  if (del) {
5364  delete [] firstWidth;
5365  delete [] firstPoint;
5366  }
5367  } else {
5368  if (firstWidth) delete [] firstWidth;
5369  if (firstPoint) delete [] firstPoint;
5370  }
5371 }
5372 
5373 ////////////////////////////////////////////////////////////////////////////////
5374 /// Fill a convex polygon with background image.
5375 /// For non convex polygon one must use DrawFillArea method
5376 
5378 {
5379  UInt_t nspans = 0;
5380  TPoint *firstPoint = 0; // output buffer
5381  UInt_t *firstWidth = 0; // output buffer
5382 
5383  Bool_t del = GetPolygonSpans(npt, ppt, &nspans, &firstPoint, &firstWidth);
5384 
5385  if (nspans) {
5386  FillSpans(nspans, firstPoint, firstWidth, tile);
5387 
5388  if (del) {
5389  delete [] firstWidth;
5390  delete [] firstPoint;
5391  }
5392  } else {
5393  if (firstWidth) delete [] firstWidth;
5394  if (firstPoint) delete [] firstPoint;
5395  }
5396 }
5397 
5398 ////////////////////////////////////////////////////////////////////////////////
5399 /// Crop a convex polygon.
5400 
5402 {
5403  UInt_t nspans = 0;
5404  TPoint *firstPoint = 0;
5405  UInt_t *firstWidth = 0;
5406 
5407  Bool_t del = GetPolygonSpans(npt, ppt, &nspans, &firstPoint, &firstWidth);
5408 
5409  if (nspans) {
5410  CropSpans(nspans, firstPoint, firstWidth);
5411 
5412  if (del) {
5413  delete [] firstWidth;
5414  delete [] firstPoint;
5415  }
5416  } else {
5417  if (firstWidth) delete [] firstWidth;
5418  if (firstPoint) delete [] firstPoint;
5419  }
5420 }
5421 
5422 static const UInt_t NUMPTSTOBUFFER = 512;
5423 
5424 ////////////////////////////////////////////////////////////////////////////////
5425 /// Fill a polygon (any type convex, non-convex).
5426 
5427 void TASImage::DrawFillArea(UInt_t count, TPoint *ptsIn, const char *col,
5428  const char *stipple, UInt_t w, UInt_t h)
5429 {
5430  if (!InitVisual()) {
5431  Warning("DrawFillArea", "Visual not initiated");
5432  return;
5433  }
5434 
5435  if (!fImage) {
5436  Warning("DrawFillArea", "no image");
5437  return;
5438  }
5439 
5440  if (!fImage->alt.argb32) {
5441  BeginPaint();
5442  }
5443 
5444  if (!fImage->alt.argb32) {
5445  Warning("DrawFillArea", "Failed to get pixel array");
5446  return;
5447  }
5448 
5449  if ((count < 3) || !ptsIn) {
5450  Warning("DrawFillArea", "No points specified npt=%d ppt=0x%lx", count, (Long_t)ptsIn);
5451  return;
5452  }
5453 
5454  if (count < 5) {
5455  FillPolygon(count, ptsIn, col, stipple, w, h);
5456  return;
5457  }
5458 
5459  ARGB32 color = ARGB32_White;
5460  parse_argb_color(col, &color);
5461 
5462  EdgeTableEntry *pAET; // the Active Edge Table
5463  int y; // the current scanline
5464  UInt_t nPts = 0; // number of pts in buffer
5465 
5466  ScanLineList *pSLL; // Current ScanLineList
5467  TPoint *ptsOut; // ptr to output buffers
5468  UInt_t *width;
5469  TPoint firstPoint[NUMPTSTOBUFFER]; // the output buffers
5470  UInt_t firstWidth[NUMPTSTOBUFFER];
5471  EdgeTableEntry *pPrevAET; // previous AET entry
5472  EdgeTable ET; // Edge Table header node
5473  EdgeTableEntry AET; // Active ET header node
5474  EdgeTableEntry *pETEs; // Edge Table Entries buff
5475  ScanLineListBlock SLLBlock; // header for ScanLineList
5476  Bool_t del = kTRUE;
5477 
5478  static const UInt_t gEdgeTableEntryCacheSize = 200;
5479  static EdgeTableEntry gEdgeTableEntryCache[gEdgeTableEntryCacheSize];
5480 
5481  if (count < gEdgeTableEntryCacheSize) {
5482  pETEs = (EdgeTableEntry*)&gEdgeTableEntryCache;
5483  del = kFALSE;
5484  } else {
5485  pETEs = new EdgeTableEntry[count];
5486  del = kTRUE;
5487  }
5488 
5489  ET.scanlines.next = nullptr; // to avoid compiler warnings
5490  ET.ymin = ET.ymax = 0; // to avoid compiler warnings
5491 
5492  ptsOut = firstPoint;
5493  width = firstWidth;
5494  CreateETandAET(count, ptsIn, &ET, &AET, pETEs, &SLLBlock);
5495  pSLL = ET.scanlines.next;
5496 
5497  for (y = ET.ymin; y < ET.ymax; y++) {
5498  if (pSLL && y == pSLL->scanline) {
5499  loadAET(&AET, pSLL->edgelist);
5500  pSLL = pSLL->next;
5501  }
5502  pPrevAET = &AET;
5503  pAET = AET.next;
5504 
5505  while (pAET) {
5506  ptsOut->fX = pAET->bres.minor_axis;
5507  ptsOut->fY = y;
5508  ptsOut++;
5509  nPts++;
5510 
5511  *width++ = pAET->next->bres.minor_axis - pAET->bres.minor_axis;
5512 
5513  if (nPts == NUMPTSTOBUFFER) {
5514  if (!stipple && ((color & 0xff000000)==0xff000000)) { //no stipple, no alpha
5515  FillSpansInternal(nPts, firstPoint, firstWidth, color);
5516  } else {
5517  FillSpans(nPts, firstPoint, firstWidth, col, stipple, w, h);
5518  }
5519  ptsOut = firstPoint;
5520  width = firstWidth;
5521  nPts = 0;
5522  }
5523  EVALUATEEDGEEVENODD(pAET, pPrevAET, y)
5524  EVALUATEEDGEEVENODD(pAET, pPrevAET, y)
5525  }
5526  InsertionSort(&AET);
5527  }
5528 
5529  if (nPts) {
5530  if (!stipple && ((color & 0xff000000)==0xff000000)) { //no stipple, no alpha
5531  FillSpansInternal(nPts, firstPoint, firstWidth, color);
5532  } else {
5533  FillSpans(nPts, firstPoint, firstWidth, col, stipple, w, h);
5534  }
5535  }
5536 
5537  if (del) delete [] pETEs;
5538  FreeStorage(SLLBlock.next);
5539 }
5540 
5541 ////////////////////////////////////////////////////////////////////////////////
5542 /// Fill a polygon (any type convex, non-convex).
5543 
5544 void TASImage::DrawFillArea(UInt_t count, TPoint *ptsIn, TImage *tile)
5545 {
5546  if (!InitVisual()) {
5547  Warning("DrawFillArea", "Visual not initiated");
5548  return;
5549  }
5550 
5551  if (!fImage) {
5552  Warning("DrawFillArea", "no image");
5553  return;
5554  }
5555 
5556  if (!fImage->alt.argb32) {
5557  BeginPaint();
5558  }
5559 
5560  if (!fImage->alt.argb32) {
5561  Warning("DrawFillArea", "Failed to get pixel array");
5562  return;
5563  }
5564 
5565  if ((count < 3) || !ptsIn) {
5566  Warning("DrawFillArea", "No points specified npt=%d ppt=0x%lx", count, (Long_t)ptsIn);
5567  return;
5568  }
5569 
5570  if (count < 5) {
5571  FillPolygon(count, ptsIn, tile);
5572  return;
5573  }
5574 
5575  EdgeTableEntry *pAET; // the Active Edge Table
5576  int y; // the current scanline
5577  UInt_t nPts = 0; // number of pts in buffer
5578 
5579  ScanLineList *pSLL; // Current ScanLineList
5580  TPoint *ptsOut; // ptr to output buffers
5581  UInt_t *width;
5582  TPoint firstPoint[NUMPTSTOBUFFER]; // the output buffers
5583  UInt_t firstWidth[NUMPTSTOBUFFER];
5584  EdgeTableEntry *pPrevAET; // previous AET entry
5585  EdgeTable ET; // Edge Table header node
5586  EdgeTableEntry AET; // Active ET header node
5587  EdgeTableEntry *pETEs; // Edge Table Entries buff
5588  ScanLineListBlock SLLBlock; // header for ScanLineList
5589 
5590  pETEs = new EdgeTableEntry[count];
5591 
5592  ET.scanlines.next = nullptr; // to avoid compiler warnings
5593  ET.ymin = ET.ymax = 0; // to avoid compiler warnings
5594