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
38Image class.
39
40TASImage is the concrete interface to the image processing library
41libAfterImage.
42
43It allows reading and writing of images in different formats, several image
44manipulations (scaling, tiling, merging, etc.) and displaying in pads. The size
45of the image on the screen does not depend on the original size of the image but
46on the size of the pad. Therefore it is very easy to resize the image on the
47screen by resizing the pad.
48
49Besides reading an image from a file an image can be defined by a two
50dimensional array of values. A palette defines the color of each value.
51
52The image can be zoomed by defining a rectangle with the mouse. The color
53palette can be modified with a GUI, just select StartPaletteEditor() from the
54context menu.
55
56Several examples showing how to use this class are available in the
57ROOT tutorials: `$ROOTSYS/tutorials/image/`
58*/
59
60#include <ft2build.h>
61#include FT_FREETYPE_H
62#include FT_GLYPH_H
63
64#include "TASImage.h"
65#include "TASImagePlugin.h"
66#include "TROOT.h"
67#include "TBuffer.h"
68#include "TMath.h"
69#include "TSystem.h"
70#include "TVirtualX.h"
71#include "TVirtualPad.h"
72#include "TArrayD.h"
73#include "TVectorD.h"
74#include "TVirtualPS.h"
75#include "TGaxis.h"
76#include "TColor.h"
77#include "TObjArray.h"
78#include "TArrayL.h"
79#include "TPoint.h"
80#include "TFrame.h"
81#include "TTF.h"
82#include "TRandom.h"
83#include <iostream>
84#include "THashTable.h"
85#include "TPluginManager.h"
86#include "TEnv.h"
87#include "TStyle.h"
88#include "TText.h"
89#include "RConfigure.h"
90#include "TVirtualPadPainter.h"
91#include "snprintf.h"
92
93#include <memory>
94
95#ifndef WIN32
96#ifndef R__HAS_COCOA
97# include <X11/Xlib.h>
98#endif
99#else
100# include "Windows4root.h"
101#endif
102#ifndef WIN32
103#ifdef R__HAS_COCOA
104# define X_DISPLAY_MISSING 1
105#endif
106# include <afterbase.h>
107#else
108# include <win32/config.h>
109# include <win32/afterbase.h>
110# define X_DISPLAY_MISSING 1
111#endif
112# include <afterimage.h>
113# include <bmp.h>
114extern "C" {
115# include <draw.h>
116}
117
118// auxiliary functions for general polygon filling
119#include "TASPolyUtils.c"
120
121
122ASVisual *TASImage::fgVisual = 0;
124
125static ASFontManager *gFontManager = 0;
126static unsigned long kAllPlanes = ~0;
128
129// default icon paths
130static char *gIconPaths[7] = {0, 0, 0, 0, 0, 0, 0};
131
132// To scale fonts to the same size as the old TT version
133const Float_t kScale = 0.985;
134
135///////////////////////////// alpha-blending macros ///////////////////////////////
136
137#if defined(__GNUC__) && __GNUC__ >= 4 && ((__GNUC_MINOR__ == 2 && __GNUC_PATCHLEVEL__ >= 1) || (__GNUC_MINOR__ >= 3)) && !__INTEL_COMPILER
138#pragma GCC diagnostic ignored "-Wstrict-aliasing"
139#endif
140
141#ifdef R__BYTESWAP
142typedef struct {
143 unsigned char b;
144 unsigned char g;
145 unsigned char r;
146 unsigned char a;
147} __argb32__;
148#else
149typedef struct {
150 unsigned char a;
151 unsigned char r;
152 unsigned char g;
153 unsigned char b;
154} __argb32__;
155#endif
156
157
158//______________________________________________________________________________
159#define _alphaBlend(bot, top) {\
160 __argb32__ *T = (__argb32__*)(top);\
161 __argb32__ *B = (__argb32__*)(bot);\
162 int aa = 255-T->a; /* NOLINT */ \
163 if (!aa) {\
164 *bot = *top;\
165 } else { \
166 B->a = ((B->a*aa)>>8) + T->a;\
167 B->r = (B->r*aa + T->r*T->a)>>8;\
168 B->g = (B->g*aa + T->g*T->a)>>8;\
169 B->b = (B->b*aa + T->b*T->a)>>8;\
170 }\
171}\
172
173
176
177////////////////////////////////////////////////////////////////////////////////
178/// Destroy image.
179
181{
182 if (fImage) {
183 destroy_asimage(&fImage);
184 }
185
186 if (fIsGray && fGrayImage) {
187 destroy_asimage(&fGrayImage);
188 }
189
190 fIsGray = kFALSE;
191 fGrayImage = 0;
192 fImage = 0;
193}
194
195////////////////////////////////////////////////////////////////////////////////
196/// Set default parameters.
197
199{
200 fImage = 0;
201 fScaledImage = 0;
202 fMaxValue = 1;
203 fMinValue = 0;
205 fPaintMode = 1;
206 fZoomOffX = 0;
207 fZoomOffY = 0;
208 fZoomWidth = 0;
209 fZoomHeight = 0;
211
212 fGrayImage = 0;
213 fIsGray = kFALSE;
215
216 if (!fgInit) {
217 set_application_name((char*)(gProgName ? gProgName : "ROOT"));
218 fgInit = kTRUE;
219 }
220}
221
222////////////////////////////////////////////////////////////////////////////////
223/// Default image constructor.
224
226{
227 SetDefaults();
228}
229
230////////////////////////////////////////////////////////////////////////////////
231/// Create an empty image.
232
234{
235 SetDefaults();
236 fImage = create_asimage(w ? w : 20, h ? h : 20, 0);
237 UnZoom();
238}
239
240////////////////////////////////////////////////////////////////////////////////
241/// Create an image object and read from specified file.
242/// For more information see description of function ReadImage()
243/// which is called by this constructor.
244
246{
247 SetDefaults();
248 TString fname = file;
249 gSystem->ExpandPathName(fname);
250 ReadImage(fname.Data());
251}
252
253////////////////////////////////////////////////////////////////////////////////
254/// Create an image depending on the values of imageData.
255/// For more information see function SetImage() which is called
256/// by this constructor.
257
258TASImage::TASImage(const char *name, const Double_t *imageData, UInt_t width,
260{
261 SetDefaults();
262 SetImage(imageData, width, height, palette);
263}
264
265////////////////////////////////////////////////////////////////////////////////
266/// Create an image depending on the values of imageData.
267/// The size of the image is width X (imageData.fN / width).
268/// For more information see function SetImage() which is called by
269/// this constructor.
270
271TASImage::TASImage(const char *name, const TArrayD &imageData, UInt_t width,
272 TImagePalette *palette) : TImage(name)
273{
274 SetDefaults();
275 SetImage(imageData, width, palette);
276}
277
278////////////////////////////////////////////////////////////////////////////////
279/// Create an image depending on the values of imageData.
280/// The size of the image is width X (imageData.fN / width).
281/// For more information see function SetImage() which is called by
282/// this constructor.
283
284TASImage::TASImage(const char *name, const TVectorD &imageData, UInt_t width,
285 TImagePalette *palette) : TImage(name)
286{
287 SetDefaults();
288 SetImage(imageData, width, palette);
289}
290
291////////////////////////////////////////////////////////////////////////////////
292/// Image copy constructor.
293
295{
296 SetDefaults();
297
298 if (img.IsValid()) {
299 fImage = clone_asimage(img.fImage, SCL_DO_ALL);
301 fGrayImage = fGrayImage ? clone_asimage(img.fGrayImage, SCL_DO_ALL) : 0;
302
303 if (img.fImage->alt.vector) {
304 Int_t size = img.fImage->width * img.fImage->height * sizeof(double);
305 fImage->alt.vector = (double*)malloc(size);
306 memcpy(fImage->alt.vector, img.fImage->alt.vector, size);
307 }
308
310 fZoomOffX = img.fZoomOffX;
311 fZoomOffY = img.fZoomOffY;
314 fEditable = img.fEditable;
315 fIsGray = img.fIsGray;
316 }
317}
318
319////////////////////////////////////////////////////////////////////////////////
320/// Image assignment operator.
321
323{
324 if (this != &img && img.IsValid()) {
326
327 DestroyImage();
328 delete fScaledImage;
329 fImage = clone_asimage(img.fImage, SCL_DO_ALL);
331 fGrayImage = fGrayImage ? clone_asimage(img.fGrayImage, SCL_DO_ALL) : 0;
332
333 if (img.fImage->alt.vector) {
334 Int_t size = img.fImage->width * img.fImage->height * sizeof(double);
335 fImage->alt.vector = (double*)malloc(size);
336 memcpy(fImage->alt.vector, img.fImage->alt.vector, size);
337 }
338
339 fScaledImage = img.fScaledImage ? (TASImage*)img.fScaledImage->Clone("") : 0;
341 fZoomOffX = img.fZoomOffX;
342 fZoomOffY = img.fZoomOffY;
345 fEditable = img.fEditable;
346 fIsGray = img.fIsGray;
347 fPaintMode = 1;
348 }
349
350 return *this;
351}
352
353////////////////////////////////////////////////////////////////////////////////
354/// Image destructor, clean up image and visual.
355
357{
358 DestroyImage();
359 delete fScaledImage;
360 fScaledImage = 0;
361}
362
363////////////////////////////////////////////////////////////////////////////////
364/// Set icons paths.
365
366static void init_icon_paths()
367{
368 TString icon_path = gEnv->GetValue("Gui.IconPath", "");
369 if (icon_path.IsNull()) {
370 icon_path = "icons";
372#ifndef R__WIN32
373 icon_path = ".:" + icon_path + ":" + TROOT::GetIconPath() + ":" + EXTRAICONPATH;
374#else
375 icon_path = ".;" + icon_path + ";" + TROOT::GetIconPath() + ";" + EXTRAICONPATH;
376#endif
377 }
378
379 Int_t cnt = 0;
380 Ssiz_t from = 0;
381 TString token;
382#ifndef R__WIN32
383 const char *delim = ":";
384#else
385 const char *delim = ";";
386#endif
387 while (icon_path.Tokenize(token, from, delim) && cnt < 6) {
388 char *path = gSystem->ExpandPathName(token.Data());
389 if (path) {
390 gIconPaths[cnt] = path;
391 cnt++;
392 }
393 }
394 gIconPaths[cnt] = 0;
395}
396
397////////////////////////////////////////////////////////////////////////////////
398/// Guess the file type from the first byte of file.
399
400const char *TASImage::TypeFromMagicNumber(const char *file)
401{
402 UChar_t magic;
403 FILE *fp = fopen(file, "rb");
404 const char *ret = "";
405
406 if (!fp) return 0;
407
408 if (!fread(&magic, 1, 1, fp)) {
409 fclose(fp);
410 return 0;
411 }
412
413 switch (magic) {
414 case 0x00:
415 {
416 if (!fread(&magic, 1, 1, fp)) {
417 fclose(fp);
418 return 0;
419 }
420 if (!fread(&magic, 1, 1, fp)) {
421 fclose(fp);
422 return 0;
423 }
424
425 ret = (magic == 1) ? "ico" : "cur";
426 break;
427 }
428 case 0x25:
429 {
430 if (!fread(&magic, 1, 1, fp)) {
431 fclose(fp);
432 return 0;
433 }
434
435 if (magic == 0x21) ret = "ps";
436 else if (magic == 0x50) ret = "pdf";
437 break;
438 }
439 case 0x42:
440 ret = "bmp";
441 break;
442 case 0x47:
443 ret = "gif";
444 break;
445 case 0x54:
446 ret = "tga";
447 break;
448 case 0x49:
449 ret = "tiff";
450 break;
451 case 0x89:
452 ret = "png";
453 break;
454 case 0xff:
455 ret = "jpg";
456 break;
457 default:
458 ret = "";
459 }
460
461 fclose(fp);
462 return ret;
463}
464
465////////////////////////////////////////////////////////////////////////////////
466/// Read specified image file.
467/// The file type is determined by the file extension (the type argument is
468/// ignored). It will attempt to append .gz and then .Z to the filename and
469/// find such a file. If the filename ends with extension consisting of digits
470/// only, it will attempt to find the file with this extension stripped
471/// off. On success this extension will be used to load subimage from
472/// the file with that number. Subimage is supported for GIF files
473/// (ICO, BMP, CUR, TIFF, XCF to be supported in future).
474/// For example,
475/// ~~~ {.cpp}
476/// i1 = TImage::Open("anim.gif.0"); // read the first subimage
477/// i4 = TImage::Open("anim.gif.3"); // read the forth subimage
478/// ~~~
479/// It is also possible to put XPM raw string (see also SetImageBuffer) as
480/// the first input parameter ("filename"), such string is returned by
481/// GetImageBuffer method.
482
484{
485 if (!InitVisual()) {
486 Warning("Scale", "Visual not initiated");
487 return;
488 }
489
490 Bool_t xpm = filename && (filename[0] == '/' &&
491 filename[1] == '*') && filename[2] == ' ';
492
493 if (xpm) { // XPM strings in-memory array
495 fName = "XPM_image";
496 return;
497 }
498
499 if (!gIconPaths[0]) {
501 }
502 // suppress the "root : looking for image ..." messages
503 set_output_threshold(0);
504
505 static ASImageImportParams iparams;
506 iparams.flags = 0;
507 iparams.width = 0;
508 iparams.height = 0;
509 iparams.filter = SCL_DO_ALL;
510 iparams.gamma = SCREEN_GAMMA;
511 iparams.gamma_table = NULL;
512 iparams.compression = GetImageCompression();
513 iparams.format = ASA_ASImage;
514 iparams.search_path = gIconPaths;
515 iparams.subimage = 0;
516 iparams.return_animation_delay = -1;
517
518 TString ext;
519 const char *dot;
520 if (filename) dot = strrchr(filename, '.');
521 else dot = 0;
522 ASImage *image = 0;
523 TString fname = filename;
524
525 if (!dot) {
527 else ext = dot + 1;
528 } else {
529 ext = dot + 1;
530 }
531
532 if (!ext.IsNull() && ext.IsDigit()) { // read subimage
533 iparams.subimage = ext.Atoi();
534 fname = fname(0, fname.Length() - ext.Length() - 1);
535 ext = strrchr(fname.Data(), '.') + 1;
536 }
537
538 image = file2ASImage_extra(fname.Data(), &iparams);
539
540 if (image) { // it's OK
541 goto end;
542 } else { // try to read it via plugin
543 if (ext.IsNull()) {
544 return;
545 }
546 ext.ToLower();
547 ext.Strip();
548 UInt_t w = 0;
549 UInt_t h = 0;
550 unsigned char *bitmap = 0;
551
553
554 if (!plug) {
555 TPluginHandler *handler = gROOT->GetPluginManager()->FindHandler("TImagePlugin", ext);
556 if (!handler || ((handler->LoadPlugin() == -1))) {
557 return;
558 }
559 plug = (TImagePlugin*)handler->ExecPlugin(1, ext.Data());
560
561 if (!plug) {
562 return;
563 }
564
565 fgPlugList->Add(plug);
566 }
567
568 if (plug) {
569 if (plug->InheritsFrom(TASImagePlugin::Class())) {
570 image = ((TASImagePlugin*)plug)->File2ASImage(fname.Data());
571 if (image) goto end;
572 }
573 bitmap = plug->ReadFile(fname.Data(), w, h);
574 if (bitmap) {
575 image = bitmap2asimage(bitmap, w, h, 0, 0);
576 }
577 if (!image) {
578 return;
579 }
580 }
581 }
582
583end:
584 fName.Form("%s.", gSystem->BaseName(fname.Data()));
585
586 DestroyImage();
587 delete fScaledImage;
588 fScaledImage = 0;
589
590 fImage = image;
593 fZoomOffX = 0;
594 fZoomOffY = 0;
595 fZoomWidth = fImage->width;
596 fZoomHeight = fImage->height;
597 fPaintMode = 1;
598}
599
600////////////////////////////////////////////////////////////////////////////////
601/// Write image to specified file.
602///
603/// If there is no file extension or if the file extension is unknown, the
604/// type argument will be used to determine the file type. The quality and
605/// compression is derived from the TAttImage values.
606///
607/// It's possible to write image into an animated GIF file by specifying file
608/// name as "myfile.gif+" or "myfile.gif+NN", where NN is the delay of displaying
609/// subimages during animation in 10ms seconds units. NN is not restricted
610/// to two digits. If NN is omitted the delay between subimages is zero.
611/// For an animation that stops after last subimage is reached, one has to
612/// write the last image as .gif+ (zero delay of last image) or .gif+NN
613/// (NN*10ms delay of last image).
614///
615/// For repeated animation (looping), the last subimage must be specified as:
616/// - "myfile.gif++NN++" if you want an infinite looping gif with NN*10ms
617/// delay of the last image.
618/// - "myfile.gif++" for an infinite loop with zero delay of last image.
619/// - "myfile.gif+NN++RR" if you want a finite looping gif with NN*10ms
620/// delay of the last image and the animation to be stopped after RR
621/// repeats. RR is not restricted to two digits.
622///
623/// A deprecated version for saving the last subimage of a looping gif animation is:
624/// - "myfile.gif++NN" for a finite loop where NN is number of repetitions
625/// and NN*10ms the delay of last image. (No separate control of repeats and delay).
626/// Note: If the file "myfile.gif" already exists, the new frames are appended at
627/// the end of the file. To avoid this, delete it first with gSystem->Unlink(myfile.gif);
628///
629/// The following macro creates animated gif from jpeg images with names
630/// - imageNN.jpg, where 1<= NN <= 10
631/// - The delays are set to 10*10ms.
632/// ~~~ {.cpp}
633/// {
634/// TImage *img = 0;
635/// gSystem->Unlink("anim.gif"); // delete existing file
636///
637/// for (int i = 1; i <= 10; i++) {
638/// delete img; // delete previous image
639///
640/// // Read image data. Image can be in any format, e.g. png, gif, etc.
641/// img = TImage::Open(Form("image%d.jpg", i));
642///
643/// if (i < 10) {
644/// img->WriteImage("anim.gif+10"); // 10 centiseconds delay
645/// } else { // the last image written. "++" stands for infinit animation.
646/// img->WriteImage("anim.gif++10++"); // 10 centiseconds delay of last image
647/// }
648/// }
649/// }
650/// ~~~
651
653{
654 if (!IsValid()) {
655 Error("WriteImage", "no image loaded");
656 return;
657 }
658
659 if (!file || !*file) {
660 Error("WriteImage", "no file name specified");
661 return;
662 }
663
664 const char *s;
665 if ((s = strrchr(file, '.'))) {
666 s++;
668 if (t == kUnknown) {
669 Error("WriteImage", "cannot determine a valid file type");
670 return;
671 }
672 if (t != kUnknown)
673 type = t;
674 }
675
676 if (type == kUnknown) {
677 Error("WriteImage", "not a valid file type was specified");
678 return;
679 }
680
681 UInt_t mytype;
682 MapFileTypes(type, mytype);
683 ASImageFileTypes atype = (ASImageFileTypes)mytype;
684
685 UInt_t aquality;
686 EImageQuality quality = GetImageQuality();
687 MapQuality(quality, aquality);
688
689 static TString fname;
690 fname = file;
691 static ASImageExportParams parms;
692 ASImage *im = fScaledImage ? fScaledImage->fImage : fImage;
693
694 switch (type) {
695 case kXpm:
696 parms.xpm.type = atype;
697 parms.xpm.flags = EXPORT_ALPHA;
698 parms.xpm.dither = 4;
699 parms.xpm.opaque_threshold = 127;
700 parms.xpm.max_colors = 512;
701 break;
702 case kBmp:
703 ASImage2bmp(im, fname.Data(), 0);
704 return;
705 case kXcf:
706 ASImage2xcf(im, fname.Data(), 0);
707 return;
708 case kPng:
709 parms.png.type = atype;
710 parms.png.flags = EXPORT_ALPHA;
711 parms.png.compression = !GetImageCompression() ? -1 : int(GetImageCompression());
712 break;
713 case kJpeg:
714 parms.jpeg.type = atype;
715 parms.jpeg.flags = 0;
716 parms.jpeg.quality = aquality;
717 break;
718 case kGif:
719 parms.gif.type = atype;
720 parms.gif.flags = EXPORT_ALPHA;
721 parms.gif.dither = 0;
722 parms.gif.opaque_threshold = 0;
723 break;
724 case kAnimGif:
725 {
726 parms.gif.type = atype;
727 parms.gif.flags = EXPORT_ALPHA | EXPORT_APPEND;
728 parms.gif.dither = 0;
729 parms.gif.opaque_threshold = 0;
730 parms.gif.animate_repeats = 0;
731
732 s += 4; // skip "gif+"
733 int delay = 0;
734
735 const TString sufix = s; // we denote as suffix as everything that is after .gif+
736 const UInt_t sLength = sufix.Length();
737
738 if (sufix=="+") {
739 // .gif++ implies that this is the last image of the animation
740 // and that the gif will loop forever (infinite animation)
741 // and that the delay of last image is 0ms (backward compatibility reasons)
742 delay = 0;
743 parms.gif.flags |= EXPORT_ANIMATION_REPEATS;// activate repetition
744 parms.gif.animate_repeats = 0;// 0 is code for looping forever (if EXPORT_ANIMATION_REPEATS is also set)
745 } else if(sufix=="") {
746 // .gif+ implies that this is a subimage of the animation with zero delay
747 // or the last image of an animation that will not repeat.
748 // Last image delay is zero because atoi("")=0.
749 delay = atoi(s);
750 //Nothing else needed here
751 } else if(!sufix.Contains("+")) {
752 // .gif+NN implies that this is a subimage of the animation
753 // with NN*10ms delay (latency) until the next one.
754 // You can also use this option on the last image if you do not want the gif to replay
755 delay = atoi(s);
756 //Nothing else needed here
757 } else if(sLength>1 && sufix.BeginsWith("+") && sufix.CountChar('+')==1) {
758 // .gif++NN implies that this is the last image of the animation
759 // and that it will loop NN number of times (finite animation)
760 // and that the delay of last image is NN*10ms (backward compatibility reasons).
761 delay = atoi(s);// atoi is smart enough to ignore the "+" sign before.
762 parms.gif.flags |= EXPORT_ANIMATION_REPEATS;// activate repetition
763 parms.gif.animate_repeats = atoi(s);// loops only NN times, then it stops. atoi discards + sign.
764 } else if(sLength>3 && sufix.BeginsWith("+") && sufix.EndsWith("++") && !TString(sufix(1,sLength-3)).Contains("+")) {
765 // .gif++NN++ implies that this is the last image of the animation
766 // and that the gif will loop forever (infinite animation)
767 // and that the delay of last image is NN*10ms.
768 // In contrast, .gif++ is an infinite loop but with 0 delay, whereas the option
769 // .gif++NN is a loop repeated NN times (not infinite) with NN*10ms delay
770 // between last and first loop images.
771 delay = atoi(s);// atoi discards the three plus signs
772 parms.gif.flags |= EXPORT_ANIMATION_REPEATS;// activate repetition
773 parms.gif.animate_repeats = 0;// 0 is code for looping forever (if EXPORT_ANIMATION_REPEATS is also set)
774 } else if(sLength>3 && sufix.CountChar('+')==2 && TString(sufix(1,sLength-2)).Contains("++")) {
775 // .gif+NN++RR implies that this is the last image animation
776 // and that the gif will loop RR number of times (finite animation)
777 // and that the delay of last image is NN*10ms.
778 const TString sDelay = sufix(0,sufix.First('+'));
779 const TString sRepeats = sufix(sufix.First('+')+2,sLength-(sufix.First('+')+2));
780 delay = atoi(sDelay);
781 parms.gif.flags |= EXPORT_ANIMATION_REPEATS;// activate repetition
782 parms.gif.animate_repeats = atoi(sRepeats);// loops NN times.
783 } else {
784 Error("WriteImage", "gif suffix %s not yet supported", s);
785 return;
786 }
787
788 parms.gif.animate_delay = delay;
789
790 int i1 = fname.Index("gif+");
791 if (i1 != kNPOS) {
792 fname = fname(0, i1 + 3);
793 }
794 else {
795 Error("WriteImage", "unexpected gif extension structure %s", fname.Data());
796 return;
797 }
798 break;
799 }
800 case kTiff:
801 parms.tiff.type = atype;
802 parms.tiff.flags = EXPORT_ALPHA;
803 parms.tiff.rows_per_strip = 0;
804 parms.tiff.compression_type = aquality <= 50 ? TIFF_COMPRESSION_JPEG :
805 TIFF_COMPRESSION_NONE;
806 parms.tiff.jpeg_quality = 100;
807 parms.tiff.opaque_threshold = 0;
808 break;
809 default:
810 Error("WriteImage", "file type %s not yet supported", s);
811 return;
812 }
813
814 if (!ASImage2file(im, 0, fname.Data(), atype, &parms)) {
815 Error("WriteImage", "error writing file %s", file);
816 }
817}
818
819////////////////////////////////////////////////////////////////////////////////
820/// Return file type depending on specified extension.
821/// Protected method.
822
824{
825 TString s(ext);
826 s.Strip();
827 s.ToLower();
828
829 if (s == "xpm")
830 return kXpm;
831 if (s == "png")
832 return kPng;
833 if (s == "jpg" || s == "jpeg")
834 return kJpeg;
835 if (s == "xcf")
836 return kXcf;
837 if (s == "ppm")
838 return kPpm;
839 if (s == "pnm")
840 return kPnm;
841 if (s == "bmp")
842 return kBmp;
843 if (s == "ico")
844 return kIco;
845 if (s == "cur")
846 return kCur;
847 if (s == "gif")
848 return kGif;
849 if (s.Contains("gif+"))
850 return kAnimGif;
851 if (s == "tiff")
852 return kTiff;
853 if (s == "xbm")
854 return kXbm;
855 if (s == "tga")
856 return kTga;
857 if (s == "xml")
858 return kXml;
859
860 return kUnknown;
861}
862
863////////////////////////////////////////////////////////////////////////////////
864/// Map file type to/from AfterImage types.
865/// Protected method.
866
868{
869 if (toas) {
870 switch (type) {
871 case kXpm:
872 astype = ASIT_Xpm; break;
873 case kZCompressedXpm:
874 astype = ASIT_ZCompressedXpm; break;
875 case kGZCompressedXpm:
876 astype = ASIT_GZCompressedXpm; break;
877 case kPng:
878 astype = ASIT_Png; break;
879 case kJpeg:
880 astype = ASIT_Jpeg; break;
881 case kXcf:
882 astype = ASIT_Xcf; break;
883 case kPpm:
884 astype = ASIT_Ppm; break;
885 case kPnm:
886 astype = ASIT_Pnm; break;
887 case kBmp:
888 astype = ASIT_Bmp; break;
889 case kIco:
890 astype = ASIT_Ico; break;
891 case kCur:
892 astype = ASIT_Cur; break;
893 case kGif:
894 astype = ASIT_Gif; break;
895 case kAnimGif:
896 astype = ASIT_Gif; break;
897 case kTiff:
898 astype = ASIT_Tiff; break;
899 case kXbm:
900 astype = ASIT_Xbm; break;
901 case kTga:
902 astype = ASIT_Targa; break;
903 case kXml:
904 astype = ASIT_XMLScript; break;
905 default:
906 astype = ASIT_Unknown;
907 }
908 } else {
909 switch (astype) {
910 case ASIT_Xpm:
911 type = kXpm; break;
912 case ASIT_ZCompressedXpm:
913 type = kZCompressedXpm; break;
914 case ASIT_GZCompressedXpm:
915 type = kGZCompressedXpm; break;
916 case ASIT_Png:
917 type = kPng; break;
918 case ASIT_Jpeg:
919 type = kJpeg; break;
920 case ASIT_Xcf:
921 type = kXcf; break;
922 case ASIT_Ppm:
923 type = kPpm; break;
924 case ASIT_Pnm:
925 type = kPnm; break;
926 case ASIT_Bmp:
927 type = kBmp; break;
928 case ASIT_Ico:
929 type = kIco; break;
930 case ASIT_Cur:
931 type = kCur; break;
932 case ASIT_Gif:
933 type = kGif; break;
934 case ASIT_Tiff:
935 type = kTiff; break;
936 case ASIT_Xbm:
937 type = kXbm; break;
938 case ASIT_XMLScript:
939 type = kXml; break;
940 case ASIT_Targa:
941 type = kTga; break;
942 default:
943 type = kUnknown;
944 }
945 }
946}
947
948////////////////////////////////////////////////////////////////////////////////
949/// Map quality to/from AfterImage quality.
950/// Protected method.
951
952void TASImage::MapQuality(EImageQuality &quality, UInt_t &asquality, Bool_t toas)
953{
954 if (toas) {
955 switch (quality) {
956 case kImgPoor:
957 asquality = 25; break;
958 case kImgFast:
959 asquality = 75; break;
960 case kImgGood:
961 asquality = 50; break;
962 case kImgBest:
963 asquality = 100; break;
964 default:
965 asquality = 0;
966 }
967 } else {
968 quality = kImgDefault;
969 if (asquality > 0 && asquality <= 25)
970 quality = kImgPoor;
971 if (asquality > 26 && asquality <= 50)
972 quality = kImgFast;
973 if (asquality > 51 && asquality <= 75)
974 quality = kImgGood;
975 if (asquality > 76 && asquality <= 100)
976 quality = kImgBest;
977 }
978}
979
980////////////////////////////////////////////////////////////////////////////////
981/// Deletes the old image and creates a new image depending on the values
982/// of imageData. The size of the image is width X height.
983///
984/// The color of each pixel depends on the imageData of the corresponding
985/// pixel. The palette is used to convert an image value into its color.
986/// If palette is not defined (palette = 0) a default palette is used.
987/// Any previously defined zooming is reset.
988
990 TImagePalette *palette)
991{
992 TAttImage::SetPalette(palette);
993
994 if (!InitVisual()) {
995 Warning("SetImage", "Visual not initiated");
996 return;
997 }
998
999 DestroyImage();
1000 delete fScaledImage;
1001 fScaledImage = 0;
1002
1003 // get min and max value of image
1004 fMinValue = fMaxValue = *imageData;
1005 for (Int_t pixel = 1; pixel < Int_t(width * height); pixel++) {
1006 if (fMinValue > *(imageData + pixel)) fMinValue = *(imageData + pixel);
1007 if (fMaxValue < *(imageData + pixel)) fMaxValue = *(imageData + pixel);
1008 }
1009
1010 // copy ROOT palette to asImage palette
1011 const TImagePalette &pal = GetPalette();
1012
1013 ASVectorPalette asPalette;
1014
1015 asPalette.npoints = pal.fNumPoints;
1016 Int_t col;
1017 for (col = 0; col < 4; col++)
1018 asPalette.channels[col] = new UShort_t[asPalette.npoints];
1019
1020 memcpy(asPalette.channels[0], pal.fColorBlue, pal.fNumPoints * sizeof(UShort_t));
1021 memcpy(asPalette.channels[1], pal.fColorGreen, pal.fNumPoints * sizeof(UShort_t));
1022 memcpy(asPalette.channels[2], pal.fColorRed, pal.fNumPoints * sizeof(UShort_t));
1023 memcpy(asPalette.channels[3], pal.fColorAlpha, pal.fNumPoints * sizeof(UShort_t));
1024
1025 asPalette.points = new Double_t[asPalette.npoints];
1026 for (Int_t point = 0; point < Int_t(asPalette.npoints); point++)
1027 asPalette.points[point] = fMinValue + (fMaxValue - fMinValue) * pal.fPoints[point];
1028
1029 fImage = create_asimage_from_vector(fgVisual, (Double_t*)imageData, width,
1030 height, &asPalette, ASA_ASImage,
1032
1033 delete [] asPalette.points;
1034 for (col = 0; col < 4; col++)
1035 delete [] asPalette.channels[col];
1036
1037 fZoomUpdate = 0;
1038 fZoomOffX = 0;
1039 fZoomOffY = 0;
1040 fZoomWidth = width;
1043}
1044
1045////////////////////////////////////////////////////////////////////////////////
1046/// Delete the old image and creates a new image depending on the values
1047/// of imageData. The size of the image is width X (imageData.fN / width).
1048/// The color of each pixel depends on the imageData of the corresponding
1049/// pixel. The palette is used to convert an image value into its color.
1050/// If palette is not defined (palette = 0) a default palette is used.
1051/// Any previously defined zooming is reset.
1052
1053void TASImage::SetImage(const TArrayD &imageData, UInt_t width, TImagePalette *palette)
1054{
1055 SetImage(imageData.GetArray(), width, imageData.GetSize() / width, palette);
1056}
1057
1058////////////////////////////////////////////////////////////////////////////////
1059/// Delete the old image and creates a new image depending on the values
1060/// of imageData. The size of the image is width X (imageData.fN / width).
1061/// The color of each pixel depends on the imageData of the corresponding
1062/// pixel. The palette is used to convert an image value into its color.
1063/// If palette is not defined (palette = 0) a default palette is used.
1064/// Any previously defined zooming is reset.
1065
1066void TASImage::SetImage(const TVectorD &imageData, UInt_t width, TImagePalette *palette)
1067{
1068 SetImage(imageData.GetMatrixArray(), width,
1069 imageData.GetNoElements() / width, palette);
1070}
1071
1072////////////////////////////////////////////////////////////////////////////////
1073/// Create an image from the given pad, afterwards this image can be
1074/// saved in any of the supported image formats.
1075
1077{
1078 if (!pad) {
1079 Error("FromPad", "pad cannot be 0");
1080 return;
1081 }
1082
1083 if (!InitVisual()) {
1084 Warning("FromPad", "Visual not initiated");
1085 return;
1086 }
1087
1088 SetName(pad->GetName());
1089
1090 DestroyImage();
1091 delete fScaledImage;
1092 fScaledImage = 0;
1093
1094 if (gROOT->IsBatch()) { // in batch mode
1095 TVirtualPS *psave = gVirtualPS;
1096 gVirtualPS = (TVirtualPS*)gROOT->ProcessLineFast("new TImageDump()");
1097 gVirtualPS->Open(pad->GetName(), 114); // in memory
1098 gVirtualPS->SetBit(BIT(11)); //kPrintingPS
1099
1101
1102 if (itmp && itmp->fImage) {
1103 itmp->BeginPaint();
1104 }
1105
1106 TVirtualPad *sav = gPad;
1107 gPad = pad;
1108 pad->Paint();
1109 gPad = sav;
1110
1111 if (itmp && itmp->fImage && (itmp != this)) {
1112 fImage = clone_asimage(itmp->fImage, SCL_DO_ALL);
1113 if (itmp->fImage->alt.argb32) {
1114 UInt_t sz = itmp->fImage->width*itmp->fImage->height;
1115 fImage->alt.argb32 = (ARGB32*)safemalloc(sz*sizeof(ARGB32));
1116 memcpy(fImage->alt.argb32, itmp->fImage->alt.argb32, sz*4);
1117 }
1118 }
1119 delete gVirtualPS;
1120 gVirtualPS = psave;
1121 return;
1122 }
1123
1124 // X11 Synchronization
1125 gVirtualX->Update(1);
1126 if (!gThreadXAR) {
1127 gSystem->Sleep(100);
1129 gSystem->Sleep(10);
1131 }
1132
1133 TVirtualPad *canvas = (TVirtualPad*)pad->GetCanvas();
1134 Int_t wid = (pad == canvas) ? pad->GetCanvasID() : pad->GetPixmapID();
1135 gVirtualX->SelectWindow(wid);
1136
1137 Window_t wd = (Window_t)gVirtualX->GetCurrentWindow();
1138 if (!wd) return;
1139
1140 if (w == 0) w = TMath::Abs(pad->UtoPixel(1.));
1141 if (h == 0) h = pad->VtoPixel(0.);
1142
1143 static int x11 = -1;
1144 if (x11 < 0) x11 = gVirtualX->InheritsFrom("TGX11");
1145
1146 if (x11) { //use built-in optimized version
1147 fImage = pixmap2asimage(fgVisual, wd, x, y, w, h, kAllPlanes, 0, 0);
1148 } else {
1149 unsigned char *bits = gVirtualX->GetColorBits(wd, 0, 0, w, h);
1150
1151 if (!bits) { // error
1152 return;
1153 }
1154 fImage = bitmap2asimage(bits, w, h, 0, 0);
1155 delete [] bits;
1156 }
1157}
1158
1159////////////////////////////////////////////////////////////////////////////////
1160/// Draw image.
1161/// Support the following drawing options:
1162/// - "T[x,y[,tint]]" : tile image (use specified offset and tint),
1163/// e.g. "T100,100,#556655"
1164/// with this option the zooming is not possible
1165/// and disabled
1166/// - "N" : display in new canvas (of original image size)
1167/// - "X" : image is drawn expanded to pad size
1168/// - "Z" : image is vectorized and image palette is drawn
1169///
1170/// The default is to display the image in the current gPad.
1171
1173{
1174 if (!fImage) {
1175 Error("Draw", "no image set");
1176 return;
1177 }
1178
1179 TString opt = option;
1180 opt.ToLower();
1181 if (opt.Contains("n") || !gPad || !gPad->IsEditable()) {
1182 Int_t w = -64;
1183 Int_t h = 64;
1184 w = (fImage->width > 64) ? (Int_t)fImage->width : w;
1185 h = (fImage->height > 64) ? (Int_t)fImage->height : h;
1186
1187 Float_t cx = 1./gStyle->GetScreenFactor();
1188 w = Int_t(w*cx) + 4;
1189 h = Int_t(h*cx) + 28;
1190 TString rname = GetName();
1191 rname.ReplaceAll(".", "");
1192 rname += Form("\", \"%s (%d x %d)", rname.Data(), fImage->width, fImage->height);
1193 rname = "new TCanvas(\"" + rname + Form("\", %d, %d);", w, h);
1194 gROOT->ProcessLineFast(rname.Data());
1195 }
1196
1197 if (!opt.Contains("x")) {
1198 Double_t left = gPad->GetLeftMargin();
1199 Double_t right = gPad->GetRightMargin();
1200 Double_t top = gPad->GetTopMargin();
1201 Double_t bottom = gPad->GetBottomMargin();
1202
1203 gPad->Range(-left / (1.0 - left - right),
1204 -bottom / (1.0 - top - bottom),
1205 1 + right / (1.0 - left - right),
1206 1 + top / ( 1.0 - top - bottom));
1207 gPad->RangeAxis(0, 0, 1, 1);
1208 }
1209
1210 TFrame *frame = gPad->GetFrame();
1211 if (frame) {
1212 frame->SetBorderMode(0);
1213 frame->SetFillColor(gPad->GetFillColor());
1214 frame->SetLineColor(gPad->GetFillColor());
1215 frame->Draw();
1216 }
1217
1219}
1220
1221////////////////////////////////////////////////////////////////////////////////
1222/// Draw asimage on drawable.
1223
1225 Int_t xsrc, Int_t ysrc, UInt_t wsrc, UInt_t hsrc,
1226 Option_t *opt)
1227{
1228 if (!im) return;
1229
1230 wsrc = wsrc ? wsrc : im->width;
1231 hsrc = hsrc ? hsrc : im->height;
1232
1233 static int x11 = -1;
1234 if (x11 < 0) x11 = gVirtualX->InheritsFrom("TGX11");
1235
1237
1238 if (x11) {
1239 UInt_t hh = hsrc;
1240 UInt_t ow = wsrc%8;
1241 UInt_t ww = wsrc - ow + (ow ? 8 : 0);
1242
1243 UInt_t bit = 0;
1244 int i = 0;
1245 UInt_t yy = 0;
1246 UInt_t xx = 0;
1247
1248 char *bits = new char[ww*hh]; //an array of bits
1249
1250 ASImageDecoder *imdec = start_image_decoding(fgVisual, im, SCL_DO_ALPHA,
1251 xsrc, ysrc, ww, 0, 0);
1252 if (imdec) {
1253 for (yy = 0; yy < hh; yy++) {
1254 imdec->decode_image_scanline(imdec);
1255 CARD32 *a = imdec->buffer.alpha;
1256
1257 for (xx = 0; xx < ww; xx++) {
1258 if (a[xx]) {
1259 SETBIT(bits[i], bit);
1260 } else {
1261 CLRBIT(bits[i], bit);
1262 }
1263 bit++;
1264 if (bit == 8) {
1265 bit = 0;
1266 i++;
1267 }
1268 }
1269 }
1270 }
1271
1272 stop_image_decoding(&imdec);
1273
1274 mask = gVirtualX->CreateBitmap(gVirtualX->GetDefaultRootWindow(),
1275 (const char *)bits, ww, hh);
1276 delete [] bits;
1277 }
1278
1279 GCValues_t gv;
1280 static GContext_t gc = 0;
1281
1283 gv.fClipMask = mask;
1284 gv.fClipXOrigin = x;
1285 gv.fClipYOrigin = y;
1286
1287 if (!gc) {
1288 gc = gVirtualX->CreateGC(gVirtualX->GetDefaultRootWindow(), &gv);
1289 } else {
1290 gVirtualX->ChangeGC(gc, &gv);
1291 }
1292
1293 if (x11 && (!gPad || gPad->GetGLDevice() == -1)) { //use built-in optimized version
1294 asimage2drawable(fgVisual, wid, im, (GC)gc, xsrc, ysrc, x, y, wsrc, hsrc, 1);
1295 } else {
1296 ASImage *img = 0;
1297 unsigned char *bits = (unsigned char *)im->alt.argb32;
1298 if (!bits) {
1299 img = tile_asimage(fgVisual, im, xsrc, ysrc, wsrc, hsrc,
1300 0, ASA_ARGB32, 0, ASIMAGE_QUALITY_DEFAULT);
1301 if (img)
1302 bits = (unsigned char *)img->alt.argb32;
1303 }
1304
1305 if (bits) {
1306 TString option(opt);
1307 option.ToLower();
1308
1309 if (gPad && gPad->GetGLDevice() != -1) {
1310 if (TVirtualPadPainter *painter = gPad->GetPainter())
1311 painter->DrawPixels(bits, wsrc, hsrc, x, y, !option.Contains("opaque"));
1312 } else {
1313 Pixmap_t pic = gVirtualX->CreatePixmapFromData(bits, wsrc, hsrc);
1314 if (pic) {
1315 if (!option.Contains("opaque")) {
1316 SETBIT(wsrc,31);
1317 SETBIT(hsrc,31);
1318 }
1319 gVirtualX->CopyArea(pic, wid, gc, 0, 0, wsrc, hsrc, x, y);
1320 gVirtualX->DeletePixmap(pic);
1321 }
1322 }
1323 }
1324
1325 if (img) {
1326 destroy_asimage(&img);
1327 }
1328 }
1329
1330 // free mask pixmap
1331 if (gv.fClipMask != kNone) gVirtualX->DeletePixmap(gv.fClipMask);
1332
1333 gv.fMask = kGCClipMask;
1334 gv.fClipMask = kNone;
1335 if (gc) gVirtualX->ChangeGC(gc, &gv);
1336}
1337
1338////////////////////////////////////////////////////////////////////////////////
1339/// Draw image on the drawable wid (pixmap, window) at x,y position.
1340///
1341/// \param[in] wid : Drawable (pixmap or window) on which image is drawn.
1342/// \param[in] x,y : Window coordinates where image is drawn.
1343/// \param[in] xsrc, ysrc : X and Y coordinates of an image area to be drawn.
1344/// \param[in] wsrc, hsrc : Width and height image area to be drawn.
1345/// \param[in] opt : specific options
1346
1348 UInt_t wsrc, UInt_t hsrc, Option_t *opt)
1349{
1351 xsrc, ysrc, wsrc, hsrc, opt);
1352}
1353
1354////////////////////////////////////////////////////////////////////////////////
1355/// Paint image.
1356/// Support the following drawing options:
1357/// - "T[x,y[,tint]]" : tile image (use specified offset and tint),
1358/// e.g. "T100,100,#556655"
1359/// with this option the zooming is not possible
1360/// and disabled
1361/// - "N" : display in new canvas (of original image size)
1362/// - "X" : image is drawn expanded to pad size
1363/// - "Z" : image is vectorized and image palette is drawn
1364///
1365/// The default is to display the image in the current gPad.
1366
1368{
1369 if (!fImage) {
1370 Error("Paint", "no image set");
1371 return;
1372 }
1373
1374 if (!InitVisual()) {
1375 Warning("Paint", "Visual not initiated");
1376 return;
1377 }
1378
1379 Int_t tile_x = 0, tile_y = 0;
1380 CARD32 tile_tint = 0;
1381 Bool_t tile = kFALSE;
1382 Bool_t expand = kFALSE;
1383
1384 TString opt = option;
1385 opt.ToLower();
1386
1387 if (opt.Contains("t")) {
1388 char stint[64];
1389 if (sscanf(opt.Data() + opt.Index("t"), "t%d,%d,%s", &tile_x, &tile_y,
1390 stint) <= 3) {
1391 tile = kTRUE;
1392 if (parse_argb_color(stint, (CARD32*)&tile_tint) == stint)
1393 tile_tint = 0;
1394 } else {
1395 Error("Paint", "tile option error");
1396 }
1397 } else if (opt.Contains("x")) {
1398 expand = kTRUE;
1400 } else if (opt.Contains("z")) {
1402
1403 if (!fImage->alt.vector) {
1404 Vectorize(256);
1405 }
1406 }
1407
1408 ASImage *image = fImage;
1409
1410 // Get geometry of pad
1411 Int_t to_w = gPad->UtoPixel(1.);
1412 Int_t to_h = gPad->VtoPixel(0.);
1413
1414 // remove the size by the margin of the pad
1415 if (!expand) {
1416 to_h = (Int_t)(to_h * (1.0 - gPad->GetBottomMargin() - gPad->GetTopMargin() ) + 0.5);
1417 to_w = (Int_t)(to_w * (1.0 - gPad->GetLeftMargin() - gPad->GetRightMargin() ) + 0.5);
1418 }
1419
1420 if ((to_w < 25 || to_h < 25) && !expand) {
1421 Error("Paint", "pad too small to display an image");
1422 return;
1423 }
1424
1425 if (GetConstRatio()) {
1426 if ((Double_t)to_w / (Double_t)fZoomWidth <
1427 (Double_t)to_h / (Double_t)fZoomHeight)
1428 to_h = Int_t(Double_t(fZoomHeight) * to_w / fZoomWidth);
1429 else
1430 to_w = Int_t(Double_t(fZoomWidth) * to_h / fZoomHeight);
1431 }
1432 // upper left corner and size of the palette in pixels
1433 Int_t pal_Ax = to_w + gPad->UtoAbsPixel(gPad->GetLeftMargin()) +
1434 (gPad->UtoAbsPixel(gPad->GetRightMargin()) / 10);
1435 Int_t pal_Ay = gPad->YtoAbsPixel(1.0);
1436 Int_t pal_x = to_w + gPad->UtoPixel(gPad->GetLeftMargin()) +
1437 (gPad->UtoPixel(gPad->GetRightMargin()) / 10);
1438 Int_t pal_y = gPad->YtoPixel(1.0);
1439 Int_t pal_w = gPad->UtoPixel(gPad->GetRightMargin()) / 3;
1440 Int_t pal_h = to_h;
1441
1442 ASImage *grad_im = 0;
1443
1444 if (fImage->alt.vector && fPaletteEnabled) {
1445 // draw the palette
1446 ASGradient grad;
1447 const TImagePalette &pal = GetPalette();
1448
1449 grad.npoints = pal.fNumPoints;
1450 grad.type = GRADIENT_Top2Bottom;
1451 grad.color = new ARGB32[grad.npoints];
1452 grad.offset = new double[grad.npoints];
1453
1454 for (Int_t pt = 0; pt < grad.npoints; pt++) {
1455 Int_t oldPt = grad.npoints - pt -1;
1456 grad.offset[pt] = 1 - pal.fPoints[oldPt];
1457 grad.color[pt] = (((ARGB32)(pal.fColorBlue[oldPt] & 0xff00)) >> 8) |
1458 (((ARGB32)(pal.fColorGreen[oldPt] & 0xff00)) ) |
1459 (((ARGB32)(pal.fColorRed[oldPt] & 0xff00)) << 8) |
1460 (((ARGB32)(pal.fColorAlpha[oldPt] & 0xff00)) << 16);
1461 }
1462
1463 grad_im = make_gradient(fgVisual, &grad , UInt_t(pal_w),
1464 pal_h, SCL_DO_COLOR,
1465 ASA_ARGB32, GetImageCompression(), GetImageQuality());
1466
1467 delete [] grad.color;
1468 delete [] grad.offset;
1469 }
1470
1471 if (tile) {
1472 delete fScaledImage;
1474 if (!fScaledImage) return;
1475 fScaledImage->fImage = tile_asimage(fgVisual, fImage, tile_x, tile_y,
1476 to_w, to_h, tile_tint, ASA_ASImage,
1478 image = fScaledImage->fImage;
1479
1480 } else if (fZoomUpdate == kZoomOps) {
1481 image = fImage;
1482
1483 } else {
1484 // Scale and zoom image if needed
1485 if (Int_t(fImage->width) != to_w || Int_t(fImage->height) != to_h ||
1486 fImage->width != fZoomWidth || fImage->height != fZoomHeight) {
1487
1488 if (fScaledImage && (Int_t(fScaledImage->GetWidth()) != to_w ||
1489 Int_t(fScaledImage->GetHeight()) != to_h ||
1490 fZoomUpdate)) {
1491
1492 delete fScaledImage;
1493 fScaledImage = 0;
1494 }
1495
1496 if (!fScaledImage) {
1498 if (!fScaledImage) return;
1499
1500 if (fZoomWidth && fZoomHeight &&
1501 ((fImage->width != fZoomWidth) || (fImage->height != fZoomHeight))) {
1502 // zoom and scale image
1503 ASImage *tmpImage = 0;
1504
1505 tmpImage = tile_asimage(fgVisual, fImage, fZoomOffX,
1506 fImage->height - fZoomHeight - fZoomOffY,
1507 fZoomWidth, fZoomHeight, 0, ASA_ASImage,
1509
1510 if (tmpImage) {
1511 fScaledImage->fImage = scale_asimage(fgVisual, tmpImage, to_w, to_h,
1512 ASA_ASImage, GetImageCompression(),
1513 GetImageQuality());
1514 destroy_asimage(&tmpImage);
1515 }
1516 } else {
1517 // scale image, no zooming
1518 fScaledImage->fImage = scale_asimage(fgVisual, fImage, to_w, to_h,
1519 ASA_ASImage, GetImageCompression(),
1520 GetImageQuality());
1521 }
1522 }
1523 image = fScaledImage->fImage;
1524 }
1525 }
1526 fZoomUpdate = 0;
1527
1528 if (!image) {
1529 Error("Paint", "image could not be rendered to display");
1530 return;
1531 }
1532
1533 int tox = expand ? 0 : int(gPad->UtoPixel(1.) * gPad->GetLeftMargin());
1534 int toy = expand ? 0 : int(gPad->VtoPixel(0.) * gPad->GetTopMargin());
1535
1536 if (!gROOT->IsBatch()) {
1537 Window_t wid = (Window_t)gVirtualX->GetWindowID(gPad->GetPixmapID());
1539
1540 if (grad_im && fPaletteEnabled) {
1541 // draw color bar
1542 Image2Drawable(grad_im, wid, pal_x, pal_y);
1543
1544 // values of palette
1545 TGaxis axis;
1546 Int_t ndiv = 510;
1547 double min = fMinValue;
1548 double max = fMaxValue;
1549 axis.SetLineColor(0); // draw white ticks
1550 Double_t pal_Xpos = gPad->AbsPixeltoX(pal_Ax + pal_w);
1551 axis.PaintAxis(pal_Xpos, gPad->PixeltoY(pal_Ay + pal_h - 1),
1552 pal_Xpos, gPad->PixeltoY(pal_Ay),
1553 min, max, ndiv, "+LU");
1554 min = fMinValue;
1555 max = fMaxValue;
1556 axis.SetLineColor(1); // draw black ticks
1557 axis.PaintAxis(pal_Xpos, gPad->AbsPixeltoY(pal_Ay + pal_h),
1558 pal_Xpos, gPad->AbsPixeltoY(pal_Ay + 1),
1559 min, max, ndiv, "+L");
1560 }
1561 }
1562
1563 // loop over pixmap and draw image to PostScript
1564 if (gVirtualPS) {
1565 if (gVirtualPS->InheritsFrom("TImageDump")) { // PostScript is asimage
1566 TImage *dump = (TImage *)gVirtualPS->GetStream();
1567 if (!dump) return;
1568 dump->Merge(fScaledImage ? fScaledImage : this, "alphablend",
1569 gPad->XtoAbsPixel(0), gPad->YtoAbsPixel(1));
1570
1571 if (grad_im) {
1572 TASImage tgrad;
1573 tgrad.fImage = grad_im;
1574 dump->Merge(&tgrad, "alphablend", pal_Ax, pal_Ay);
1575
1576 // values of palette
1577 TGaxis axis;
1578 Int_t ndiv = 510;
1579 double min = fMinValue;
1580 double max = fMaxValue;
1581 axis.SetLineColor(1); // draw black ticks
1582 Double_t pal_Xpos = gPad->AbsPixeltoX(pal_Ax + pal_w);
1583 axis.PaintAxis(pal_Xpos, gPad->AbsPixeltoY(pal_Ay + pal_h),
1584 pal_Xpos, gPad->AbsPixeltoY(pal_Ay + 1),
1585 min, max, ndiv, "+L");
1586 }
1587 return;
1588 } else if (gVirtualPS->InheritsFrom("TPDF")) {
1589 Warning("Paint", "PDF not implemented yet");
1590 return;
1591 } else if (gVirtualPS->InheritsFrom("TSVG")) {
1592 Warning("Paint", "SVG not implemented yet");
1593 return;
1594 }
1595
1596 // get special color cell to be reused during image printing
1597 TObjArray *colors = (TObjArray*) gROOT->GetListOfColors();
1598 TColor *color = 0;
1599 // Look for color by name
1600 if ((color = (TColor*)colors->FindObject("Image_PS")) == 0)
1601 color = new TColor(colors->GetEntries(), 1., 1., 1., "Image_PS");
1602
1604 gVirtualPS->SetFillStyle(1001);
1605
1606 Double_t dx = gPad->GetX2()-gPad->GetX1();
1607 Double_t dy = gPad->GetY2()-gPad->GetY1();
1608 Double_t x1,x2,y1,y2;
1609
1610 if (expand) {
1611 x1 = gPad->GetX1();
1612 x2 = x1+dx/image->width;
1613 y1 = gPad->GetY2();
1614 y2 = y1+dy/image->height;
1615 } else {
1616 x1 = gPad->GetX1()+dx*gPad->GetLeftMargin();
1617 x2 = x1+(dx*(1-gPad->GetRightMargin()-gPad->GetLeftMargin()))/image->width;
1618 y1 = gPad->GetY2()-dy*gPad->GetTopMargin();
1619 y2 = y1+(dy*(1-gPad->GetTopMargin()-gPad->GetBottomMargin()))/image->height;
1620 }
1621
1622 gVirtualPS->CellArrayBegin(image->width, image->height, x1, x2, y1, y2);
1623
1624 ASImageDecoder *imdec = start_image_decoding(fgVisual, image, SCL_DO_ALL,
1625 0, 0, image->width, image->height, 0);
1626 if (!imdec) return;
1627 for (Int_t yt = 0; yt < (Int_t)image->height; yt++) {
1628 imdec->decode_image_scanline(imdec);
1629 for (Int_t xt = 0; xt < (Int_t)image->width; xt++)
1630 gVirtualPS->CellArrayFill(imdec->buffer.red[xt],
1631 imdec->buffer.green[xt],
1632 imdec->buffer.blue[xt]);
1633 }
1634 stop_image_decoding(&imdec);
1636
1637 // print the color bar
1638 if (grad_im) {
1639 Double_t xconv = (gPad->AbsPixeltoX(pal_Ax + pal_w) - gPad->AbsPixeltoX(pal_Ax)) / grad_im->width;
1640 Double_t yconv = (gPad->AbsPixeltoY(pal_Ay - pal_h) - gPad->AbsPixeltoY(pal_Ay)) / grad_im->height;
1641 x1 = gPad->AbsPixeltoX(pal_Ax);
1642 x2 = x1 + xconv;
1643 y2 = gPad->AbsPixeltoY(pal_Ay);
1644 y1 = y2 - yconv;
1645 gVirtualPS->CellArrayBegin(grad_im->width, grad_im->height,
1646 x1, x2, y1, y2);
1647
1648 imdec = start_image_decoding(fgVisual, grad_im, SCL_DO_ALL,
1649 0, 0, grad_im->width, grad_im->height, 0);
1650 if (imdec) {
1651 for (Int_t yt = 0; yt < (Int_t)grad_im->height; yt++) {
1652 imdec->decode_image_scanline(imdec);
1653 for (Int_t xt = 0; xt < (Int_t)grad_im->width; xt++)
1654 gVirtualPS->CellArrayFill(imdec->buffer.red[xt],
1655 imdec->buffer.green[xt],
1656 imdec->buffer.blue[xt]);
1657 }
1658 }
1659 stop_image_decoding(&imdec);
1661
1662 // values of palette
1663 TGaxis axis;
1664 Int_t ndiv = 510;
1665 double min = fMinValue;
1666 double max = fMaxValue;
1667 axis.SetLineColor(1); // draw black ticks
1668 Double_t pal_Xpos = gPad->AbsPixeltoX(pal_Ax + pal_w);
1669 axis.PaintAxis(pal_Xpos, gPad->AbsPixeltoY(pal_Ay + pal_h),
1670 pal_Xpos, gPad->AbsPixeltoY(pal_Ay + 1),
1671 min, max, ndiv, "+L");
1672
1673 }
1674 }
1675
1676 if (grad_im) {
1677 destroy_asimage(&grad_im);
1678 }
1679}
1680
1681////////////////////////////////////////////////////////////////////////////////
1682/// Is the mouse in the image ?
1683
1685{
1686 Int_t pxl, pyl, pxt, pyt;
1687
1688 Int_t px1 = gPad->XtoAbsPixel(0);
1689 Int_t py1 = gPad->YtoAbsPixel(0);
1690 Int_t px2 = gPad->XtoAbsPixel(1);
1691 Int_t py2 = gPad->YtoAbsPixel(1);
1692
1693 if (px1 < px2) {pxl = px1; pxt = px2;}
1694 else {pxl = px2; pxt = px1;}
1695 if (py1 < py2) {pyl = py1; pyt = py2;}
1696 else {pyl = py2; pyt = py1;}
1697
1698 if ((px > pxl && px < pxt) && (py > pyl && py < pyt))
1699 return 0;
1700
1701 return 999999;
1702}
1703
1704////////////////////////////////////////////////////////////////////////////////
1705/// Execute mouse events.
1706
1708{
1709 static std::unique_ptr<TBox> ZoomBox;
1710
1711 if (!gPad) return;
1712
1713 if (IsEditable()) {
1714 gPad->ExecuteEvent(event, px, py);
1715 return;
1716 }
1717
1718 gPad->SetCursor(kCross);
1719
1720 static Int_t px1old, py1old, px2old, py2old;
1721 static Int_t px1, py1, px2, py2, pxl, pyl, pxt, pyt;
1722
1723 if (!IsValid()) return;
1724
1725 if (event == kButton1Motion || event == kButton1Down ||
1726 event == kButton1Up) {
1727
1728 // convert to image pixel on screen
1729 Int_t imgX = px - gPad->XtoAbsPixel(0);
1730 Int_t imgY = py - gPad->YtoAbsPixel(1);
1731
1732 if (imgX < 0) px = px - imgX;
1733 if (imgY < 0) py = py - imgY;
1734
1735 ASImage *image = fImage;
1737
1738 if (imgX >= (int)image->width) px = px - imgX + image->width - 1;
1739 if (imgY >= (int)image->height) py = py - imgY + image->height - 1;
1740
1741 switch (event) {
1742
1743 case kButton1Down:
1744 px1 = gPad->XtoAbsPixel(gPad->GetX1());
1745 py1 = gPad->YtoAbsPixel(gPad->GetY1());
1746 px2 = gPad->XtoAbsPixel(gPad->GetX2());
1747 py2 = gPad->YtoAbsPixel(gPad->GetY2());
1748 px1old = px; py1old = py;
1749 break;
1750
1751 case kButton1Motion:
1752 px2old = px;
1753 px2old = TMath::Max(px2old, px1);
1754 px2old = TMath::Min(px2old, px2);
1755 py2old = py;
1756 py2old = TMath::Max(py2old, py2);
1757 py2old = TMath::Min(py2old, py1);
1758 pxl = TMath::Min(px1old, px2old);
1759 pxt = TMath::Max(px1old, px2old);
1760 pyl = TMath::Max(py1old, py2old);
1761 pyt = TMath::Min(py1old, py2old);
1762
1763 if (ZoomBox) {
1764 ZoomBox->SetX1(gPad->AbsPixeltoX(pxl));
1765 ZoomBox->SetY1(gPad->AbsPixeltoY(pyl));
1766 ZoomBox->SetX2(gPad->AbsPixeltoX(pxt));
1767 ZoomBox->SetY2(gPad->AbsPixeltoY(pyt));
1768 } else {
1769 ZoomBox = std::make_unique<TBox>(pxl, pyl, pxt, pyt);
1770 ZoomBox->SetFillStyle(0);
1771 ZoomBox->Draw("l*");
1772 }
1773 gPad->Modified(kTRUE);
1774 gPad->Update();
1775 break;
1776
1777 case kButton1Up:
1778 // do nothing if zoom area is too small
1779 if ( TMath::Abs(pxl - pxt) < 5 || TMath::Abs(pyl - pyt) < 5)
1780 return;
1781
1782 pxl = 0;
1783 pxt = 0;
1784 pyl = 0;
1785 pyt = 0;
1786
1787 Double_t xfact = (fScaledImage) ? (Double_t)fScaledImage->fImage->width / fZoomWidth : 1;
1788 Double_t yfact = (fScaledImage) ? (Double_t)fScaledImage->fImage->height / fZoomHeight : 1;
1789
1790 Int_t imgX1 = px1old - gPad->XtoAbsPixel(0);
1791 Int_t imgY1 = py1old - gPad->YtoAbsPixel(1);
1792 Int_t imgX2 = px - gPad->XtoAbsPixel(0);
1793 Int_t imgY2 = py - gPad->YtoAbsPixel(1);
1794
1795 imgY1 = image->height - 1 - imgY1;
1796 imgY2 = image->height - 1 - imgY2;
1797 imgX1 = (Int_t)(imgX1 / xfact) + fZoomOffX;
1798 imgY1 = (Int_t)(imgY1 / yfact) + fZoomOffY;
1799 imgX2 = (Int_t)(imgX2 / xfact) + fZoomOffX;
1800 imgY2 = (Int_t)(imgY2 / yfact) + fZoomOffY;
1801
1802 Zoom((imgX1 < imgX2) ? imgX1 : imgX2, (imgY1 < imgY2) ? imgY1 : imgY2,
1803 TMath::Abs(imgX1 - imgX2) + 1, TMath::Abs(imgY1 - imgY2) + 1);
1804
1805 if (ZoomBox)
1806 ZoomBox.reset();
1807
1808 gPad->Modified(kTRUE);
1809 gPad->Update();
1810 break;
1811 }
1812 }
1813}
1814
1815////////////////////////////////////////////////////////////////////////////////
1816/// Get image pixel coordinates and the pixel value at the mouse pointer.
1817
1819{
1820 static char info[64];
1821 info[0] = 0;
1822
1823 if (!IsValid()) return info;
1824
1825 // convert to image pixel on screen
1826 px -= gPad->XtoAbsPixel(0);
1827 py -= gPad->YtoAbsPixel(1);
1828
1829 // no info if mouse is outside of image
1830 if (px < 0 || py < 0) return info;
1831
1832 ASImage *image = fImage;
1833 if (fScaledImage) image = fScaledImage->fImage;
1834 if (px >= (int)image->width || py >= (int)image->height)
1835 return info;
1836
1837 py = image->height - 1 - py;
1838 // convert to original image size and take zooming into account
1839 if (fScaledImage) {
1840 px = (Int_t)(px / (Double_t)fScaledImage->fImage->width * fZoomWidth ) + fZoomOffX;
1841 py = (Int_t)(py / (Double_t)fScaledImage->fImage->height * fZoomHeight) + fZoomOffY;
1842 }
1843
1844 if (fImage->alt.vector) {
1845 snprintf(info,64,"x: %d y: %d %.5g",
1846 px, py, fImage->alt.vector[px + py * fImage->width]);
1847 } else {
1848 snprintf(info,64,"x: %d y: %d", px, py);
1849 }
1850
1851 return info;
1852}
1853
1854////////////////////////////////////////////////////////////////////////////////
1855/// Set a new palette to an image.
1856/// Only images that were created with the SetImage() functions can be
1857/// modified with this function. The previously used palette is destroyed.
1858
1860{
1861 TAttImage::SetPalette(palette);
1862
1863 if (!InitVisual()) {
1864 Warning("SetPalette", "Visual not initiated");
1865 return;
1866 }
1867
1868 if (!IsValid()) {
1869 Warning("SetPalette", "Image not valid");
1870 return;
1871 }
1872
1873 if (fImage->alt.vector == 0)
1874 return;
1875
1876 // copy ROOT palette to asImage palette
1877 const TImagePalette &pal = GetPalette();
1878
1879 ASVectorPalette asPalette;
1880 asPalette.npoints = pal.fNumPoints;
1881 asPalette.channels[0] = new CARD16 [asPalette.npoints];
1882 asPalette.channels[1] = new CARD16 [asPalette.npoints];
1883 asPalette.channels[2] = new CARD16 [asPalette.npoints];
1884 asPalette.channels[3] = new CARD16 [asPalette.npoints];
1885 memcpy(asPalette.channels[0], pal.fColorBlue, pal.fNumPoints * sizeof(UShort_t));
1886 memcpy(asPalette.channels[1], pal.fColorGreen, pal.fNumPoints * sizeof(UShort_t));
1887 memcpy(asPalette.channels[2], pal.fColorRed, pal.fNumPoints * sizeof(UShort_t));
1888 memcpy(asPalette.channels[3], pal.fColorAlpha, pal.fNumPoints * sizeof(UShort_t));
1889
1890 asPalette.points = new double[asPalette.npoints];
1891 for (Int_t point = 0; point < Int_t(asPalette.npoints); point++)
1892 asPalette.points[point] = fMinValue + (fMaxValue - fMinValue) * pal.fPoints[point];
1893
1894 // use the new palette in this image
1895 colorize_asimage_vector(fgVisual, fImage, &asPalette, ASA_ASImage, GetImageQuality());
1896
1897 delete [] asPalette.points;
1898 for (Int_t col = 0; col < 4; col++)
1899 delete [] asPalette.channels[col];
1900
1901
1902 delete fScaledImage;
1903 fScaledImage = 0;
1904}
1905
1906////////////////////////////////////////////////////////////////////////////////
1907/// Scale the original image.
1908/// The size of the image on the screen does not change because it is defined
1909/// by the size of the pad.
1910/// This function can be used to change the size of an image before writing
1911/// it into a file. The colors of the new pixels are interpolated.
1912/// An image created with the SetImage() functions cannot be modified with
1913/// the function SetPalette() any more after a call of this function!
1914
1915void TASImage::Scale(UInt_t toWidth, UInt_t toHeight)
1916{
1917 if (!IsValid()) {
1918 Warning("Scale", "Image not initiated");
1919 return;
1920 }
1921
1922 if (!InitVisual()) {
1923 Warning("Scale", "Visual not initiated");
1924 return;
1925 }
1926
1927 if (toWidth < 1)
1928 toWidth = 1;
1929 if (toHeight < 1 )
1930 toHeight = 1;
1931 if (toWidth > 30000)
1932 toWidth = 30000;
1933 if (toHeight > 30000)
1934 toHeight = 30000;
1935
1936 ASImage *img = scale_asimage(fgVisual, fImage, toWidth, toHeight,
1937 ASA_ASImage, GetImageCompression(),
1938 GetImageQuality());
1939 DestroyImage();
1940 fImage = img;
1941 UnZoom();
1943}
1944
1945////////////////////////////////////////////////////////////////////////////////
1946/// Another method of enlarging images where corners remain unchanged,
1947/// but middle part gets tiled.
1948
1949void TASImage::Slice(UInt_t xStart, UInt_t xEnd, UInt_t yStart, UInt_t yEnd,
1950 UInt_t toWidth, UInt_t toHeight)
1951{
1952 if (!IsValid()) {
1953 Warning("Scale", "Image not initiated");
1954 return;
1955 }
1956
1957 if (!InitVisual()) {
1958 Warning("Scale", "Visual not initiated");
1959 return;
1960 }
1961
1962 if (toWidth < 1)
1963 toWidth = 1;
1964 if (toHeight < 1 )
1965 toHeight = 1;
1966 if (toWidth > 30000)
1967 toWidth = 30000;
1968 if (toHeight > 30000)
1969 toHeight = 30000;
1970
1971 ASImage *img = slice_asimage(fgVisual, fImage, xStart, xEnd,
1972 yStart, yEnd, toWidth, toHeight,
1973 ASA_ASImage, GetImageCompression(),
1974 GetImageQuality());
1975
1976 DestroyImage();
1977 fImage = img;
1978 UnZoom();
1980}
1981
1982////////////////////////////////////////////////////////////////////////////////
1983/// Tile the original image.
1984
1985void TASImage::Tile(UInt_t toWidth, UInt_t toHeight)
1986{
1987 if (!IsValid()) {
1988 Warning("Tile", "Image not initiated");
1989 return;
1990 }
1991
1992 if (!InitVisual()) {
1993 Warning("Tile", "Visual not initiated");
1994 return;
1995 }
1996
1997 if (toWidth < 1)
1998 toWidth = 1;
1999 if (toHeight < 1 )
2000 toHeight = 1;
2001 if (toWidth > 30000)
2002 toWidth = 30000;
2003 if (toHeight > 30000)
2004 toHeight = 30000;
2005
2006 ASImage *img = tile_asimage(fgVisual, fImage, 0, 0, toWidth, toHeight, 0,
2007 ASA_ASImage, GetImageCompression(), GetImageQuality());
2008 DestroyImage();
2009 fImage = img;
2010 UnZoom();
2012}
2013
2014////////////////////////////////////////////////////////////////////////////////
2015/// The area of an image displayed in a pad is defined by this function.
2016/// Note: the size on the screen is defined by the size of the pad.
2017/// The original image is not modified by this function.
2018/// If width or height is larger than the original image they are reduced to
2019/// the width and height of the image.
2020/// If the off values are too large (off + width > image width) than the off
2021/// values are decreased. For example: offX = image width - width
2022/// Note: the parameters are always relative to the original image not to the
2023/// size of an already zoomed image.
2024
2026{
2027 if (!IsValid()) {
2028 Warning("Zoom", "Image not valid");
2029 return;
2030 }
2032
2033 fZoomWidth = (width == 0) ? 1 : ((width > fImage->width) ? fImage->width : width);
2034 fZoomHeight = (height == 0) ? 1 : ((height > fImage->height) ? fImage->height : height);
2035 fZoomOffX = offX;
2036 if (fZoomOffX + fZoomWidth > fImage->width)
2037 fZoomOffX = fImage->width - fZoomWidth;
2038 fZoomOffY = offY;
2039 if (fZoomOffY + fZoomHeight > fImage->height)
2040 fZoomOffY = fImage->height - fZoomHeight;
2041}
2042
2043////////////////////////////////////////////////////////////////////////////////
2044/// Un-zoom the image to original size.
2045/// UnZoom() - performs undo for Zoom,Crop,Scale actions
2046
2048{
2049 if (!IsValid()) {
2050 Warning("UnZoom", "Image not valid");
2051 return;
2052 }
2054 fZoomOffX = 0;
2055 fZoomOffY = 0;
2056 fZoomWidth = fImage->width;
2057 fZoomHeight = fImage->height;
2058
2059 delete fScaledImage;
2060 fScaledImage = 0;
2061}
2062
2063////////////////////////////////////////////////////////////////////////////////
2064/// Flip image in place.
2065///
2066/// Flip is either 90, 180, 270, 180 is default.
2067/// This function manipulates the original image and destroys the
2068/// scaled and zoomed image which will be recreated at the next call of
2069/// the Draw function. If the image is zoomed the zoom - coordinates are
2070/// now relative to the new image.
2071/// This function cannot be used for images which were created with the
2072/// SetImage() functions, because the original pixel values would be
2073/// destroyed.
2074
2076{
2077 if (!IsValid()) {
2078 Warning("Flip", "Image not valid");
2079 return;
2080 }
2081 if (!InitVisual()) {
2082 Warning("Flip", "Visual not initiated");
2083 return;
2084 }
2085
2086 if (fImage->alt.vector) {
2087 Warning("Flip", "flip does not work for data images");
2088 return;
2089 }
2090
2091 Int_t rflip = flip/90;
2092
2093 UInt_t w = fImage->width;
2094 UInt_t h = fImage->height;
2095
2096 if (rflip & 1) {
2097 w = fImage->height;
2098 h = fImage->width;
2099 }
2100
2101 ASImage *img = flip_asimage(fgVisual, fImage, 0, 0, w, h, rflip,
2102 ASA_ASImage, GetImageCompression(),
2103 GetImageQuality());
2104 DestroyImage();
2105 fImage = img;
2106 UnZoom();
2107}
2108
2109////////////////////////////////////////////////////////////////////////////////
2110/// Mirror image in place.
2111///
2112/// If vert is true mirror in vertical axis, horizontal otherwise.
2113/// Vertical is default.
2114/// This function manipulates the original image and destroys the
2115/// scaled and zoomed image which will be recreated at the next call of
2116/// the Draw function. If the image is zoomed the zoom - coordinates are
2117/// now relative to the new image.
2118/// This function cannot be used for images which were created with the
2119/// SetImage() functions, because the original pixel values would be
2120/// destroyed.
2121
2123{
2124 if (!IsValid()) {
2125 Warning("Mirror", "Image not valid");
2126 return;
2127 }
2128
2129 if (!InitVisual()) {
2130 Warning("Mirror", "Visual not initiated");
2131 return;
2132 }
2133
2134 if (fImage->alt.vector) {
2135 Warning("Mirror", "mirror does not work for data images");
2136 return;
2137 }
2138
2139 ASImage *img = mirror_asimage(fgVisual, fImage, 0, 0,
2140 fImage->width, fImage->height, vert,
2141 ASA_ASImage, GetImageCompression(),
2142 GetImageQuality());
2143 DestroyImage();
2144 fImage = img;
2145 UnZoom();
2146}
2147
2148////////////////////////////////////////////////////////////////////////////////
2149/// Return width of original image not of the displayed image.
2150/// (Number of image pixels)
2151
2153{
2154 return fImage ? fImage->width : 0;
2155}
2156
2157////////////////////////////////////////////////////////////////////////////////
2158/// Return height of original image not of the displayed image.
2159/// (Number of image pixels)
2160
2162{
2163 return fImage ? fImage->height : 0;
2164}
2165
2166////////////////////////////////////////////////////////////////////////////////
2167/// Return width of the displayed image not of the original image.
2168/// (Number of screen pixels)
2169
2171{
2172 return fScaledImage ? fScaledImage->fImage->width : GetWidth();
2173}
2174
2175////////////////////////////////////////////////////////////////////////////////
2176/// Return height of the displayed image not of the original image.
2177/// (Number of screen pixels)
2178
2180{
2181 return fScaledImage ? fScaledImage->fImage->height : GetHeight();
2182}
2183
2184////////////////////////////////////////////////////////////////////////////////
2185/// Return the zoom parameters.
2186/// This is useful when the zoom has been done interactively using the mouse.
2187
2189{
2190 x = fZoomOffX;
2191 y = fZoomOffY;
2192 w = fZoomWidth;
2193 h = fZoomHeight;
2194}
2195
2196////////////////////////////////////////////////////////////////////////////////
2197/// Static function to initialize the ASVisual.
2198
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 fgVisual = create_asvisual(0, 0, 0, 0);
2217 fgVisual->dpy = (Display*)1; //fake (not used)
2218 return kTRUE;
2219 }
2220
2221#ifndef WIN32
2222#ifdef R__HAS_COCOA
2223 fgVisual = create_asvisual(0, 0, 0, 0);
2224 fgVisual->dpy = (Display*)1; //fake (not used)
2225#else
2226 Display *disp = (Display*) gVirtualX->GetDisplay();
2227 Int_t screen = gVirtualX->GetScreen();
2228 Int_t depth = gVirtualX->GetDepth();
2229 Visual *vis = (Visual*) gVirtualX->GetVisual();
2230 Colormap cmap = (Colormap) gVirtualX->GetColormap();
2231
2232 if (vis == 0 || cmap == 0) {
2233 fgVisual = create_asvisual(0, 0, 0, 0);
2234 } else {
2235 fgVisual = create_asvisual_for_id(disp, screen, depth,
2236 XVisualIDFromVisual(vis), cmap, 0);
2237 }
2238#endif
2239#else
2240 fgVisual = create_asvisual(0, 0, 0, 0);
2241 fgVisual->dpy = (Display*)1; //fake (not used)
2242#endif
2243
2244 return kTRUE;
2245}
2246
2247////////////////////////////////////////////////////////////////////////////////
2248/// Start palette editor.
2249
2251{
2252 if (!IsValid()) {
2253 Warning("StartPaletteEditor", "Image not valid");
2254 return;
2255 }
2256 if (fImage->alt.vector == 0) {
2257 Warning("StartPaletteEditor", "palette can be modified only for data images");
2258 return;
2259 }
2260
2261 // Opens a GUI to edit the color palette
2263}
2264
2265////////////////////////////////////////////////////////////////////////////////
2266/// Returns image pixmap.
2267/// The pixmap must deleted by user.
2268
2270{
2271 if (!InitVisual()) {
2272 Warning("GetPixmap", "Visual not initiated");
2273 return 0;
2274 }
2275
2276 Pixmap_t ret;
2277
2278 ASImage *img = fScaledImage ? fScaledImage->fImage : fImage;
2279
2280 static int x11 = -1;
2281 if (x11 < 0) x11 = gVirtualX->InheritsFrom("TGX11");
2282
2283 if (x11) { // use builtin version
2284 ret = (Pixmap_t)asimage2pixmap(fgVisual, gVirtualX->GetDefaultRootWindow(),
2285 img, 0, kTRUE);
2286 } else {
2287 if (!fImage->alt.argb32) {
2288 BeginPaint();
2289 }
2290 ret = gVirtualX->CreatePixmapFromData((unsigned char*)fImage->alt.argb32,
2291 fImage->width, fImage->height);
2292 }
2293
2294 return ret;
2295}
2296
2297////////////////////////////////////////////////////////////////////////////////
2298/// Returns image mask pixmap (alpha channel).
2299/// The pixmap must deleted by user.
2300
2302{
2303 Pixmap_t pxmap = 0;
2304
2305 if (!InitVisual()) {
2306 Warning("GetMask", "Visual not initiated");
2307 return pxmap;
2308 }
2309
2310 ASImage *img = fScaledImage ? fScaledImage->fImage : fImage;
2311
2312 if (!img) {
2313 Warning("GetMask", "No image");
2314 return pxmap;
2315 }
2316
2317 UInt_t hh = img->height;
2318 UInt_t ow = img->width%8;
2319 UInt_t ww = img->width - ow + (ow ? 8 : 0);
2320
2321 UInt_t bit = 0;
2322 int i = 0;
2323 UInt_t y = 0;
2324 UInt_t x = 0;
2325
2326 char *bits = new char[ww*hh]; //an array of bits
2327
2328 ASImageDecoder *imdec = start_image_decoding(fgVisual, img, SCL_DO_ALPHA,
2329 0, 0, ww, 0, 0);
2330 if (!imdec) {
2331 delete [] bits;
2332 return 0;
2333 }
2334
2335 for (y = 0; y < hh; y++) {
2336 imdec->decode_image_scanline(imdec);
2337 CARD32 *a = imdec->buffer.alpha;
2338
2339 for (x = 0; x < ww; x++) {
2340 if (a[x]) {
2341 SETBIT(bits[i], bit);
2342 } else {
2343 CLRBIT(bits[i], bit);
2344 }
2345 bit++;
2346 if (bit == 8) {
2347 bit = 0;
2348 i++;
2349 }
2350 }
2351 }
2352
2353 stop_image_decoding(&imdec);
2354 pxmap = gVirtualX->CreateBitmap(gVirtualX->GetDefaultRootWindow(), (const char *)bits,
2355 ww, hh);
2356 delete [] bits;
2357 return pxmap;
2358}
2359
2360////////////////////////////////////////////////////////////////////////////////
2361/// Create image from pixmap.
2362
2364{
2365 if (!InitVisual()) {
2366 Warning("SetImage", "Visual not initiated");
2367 return;
2368 }
2369
2370 DestroyImage();
2371 delete fScaledImage;
2372 fScaledImage = 0;
2373
2374 Int_t xy;
2375 UInt_t w, h;
2376 gVirtualX->GetWindowSize(pxm, xy, xy, w, h);
2377
2378 if (fName.IsNull()) fName.Form("img_%dx%d",w, h);
2379
2380 static int x11 = -1;
2381 if (x11 < 0) x11 = gVirtualX->InheritsFrom("TGX11");
2382
2383 if (x11) { //use built-in optimized version
2384 fImage = picture2asimage(fgVisual, pxm, mask, 0, 0, w, h, kAllPlanes, 1, 0);
2385 } else {
2386 unsigned char *bits = gVirtualX->GetColorBits(pxm, 0, 0, w, h);
2387 if (!bits) { // error
2388 return;
2389 }
2390
2391 // no mask
2392 if (!mask) {
2393 fImage = bitmap2asimage(bits, w, h, 0, 0);
2394 delete [] bits;
2395 return;
2396 }
2397 unsigned char *mask_bits = gVirtualX->GetColorBits(mask, 0, 0, w, h);
2398 fImage = bitmap2asimage(bits, w, h, 0, mask_bits);
2399 delete [] mask_bits;
2400 delete [] bits;
2401 }
2402}
2403
2404////////////////////////////////////////////////////////////////////////////////
2405/// Return 2D array of machine dependent pixel values.
2406
2408{
2409 if (!fImage) {
2410 Warning("GetPixels", "Wrong Image");
2411 return 0;
2412 }
2413
2414 ASImage *img = fScaledImage ? fScaledImage->fImage : fImage;
2415 ASImageDecoder *imdec;
2416
2417 width = !width ? img->width : width;
2418 height = !height ? img->height : height;
2419
2420 if (x < 0) {
2421 width -= x;
2422 x = 0 ;
2423 }
2424 if (y < 0) {
2425 height -= y;
2426 y = 0;
2427 }
2428
2429 if ((x >= (int)img->width) || (y >= (int)img->height)) {
2430 return 0;
2431 }
2432
2433 if ((int)(x + width) > (int)img->width) {
2434 width = img->width - x;
2435 }
2436
2437 if ((int)(y + height) > (int)img->height) {
2438 height = img->height - y;
2439 }
2440
2441 if ((imdec = start_image_decoding(0, fImage, SCL_DO_ALL, 0, y,
2442 img->width, height, 0)) == 0) {
2443 Warning("GetPixels", "Failed to create image decoder");
2444 return 0;
2445 }
2446
2447 TArrayL *ret = new TArrayL(width * height);
2448 Int_t r = 0;
2449 Int_t g = 0;
2450 Int_t b = 0;
2451 Long_t p = 0;
2452
2453 for (UInt_t k = 0; k < height; k++) {
2454 imdec->decode_image_scanline(imdec);
2455
2456 for (UInt_t i = 0; i < width; ++i) {
2457 if ((r == (Int_t)imdec->buffer.red[i]) &&
2458 (g == (Int_t)imdec->buffer.green[i]) &&
2459 (b == (Int_t)imdec->buffer.blue[i])) {
2460 } else {
2461 r = (Int_t)imdec->buffer.red[i];
2462 g = (Int_t)imdec->buffer.green[i];
2463 b = (Int_t)imdec->buffer.blue[i];
2464 p = (Long_t)TColor::RGB2Pixel(r, g, b);
2465 }
2466 ret->AddAt(p, k*width + i);
2467 }
2468 }
2469
2470 stop_image_decoding(&imdec);
2471 return ret;
2472}
2473
2474////////////////////////////////////////////////////////////////////////////////
2475/// Return a pointer to internal array[width x height] of double values [0,1].
2476/// This array is directly accessible. That allows to manipulate/change the
2477/// image.
2478
2480{
2481 if (!fImage) {
2482 Warning("GetVecArray", "Bad Image");
2483 return 0;
2484 }
2485 if (fImage->alt.vector) {
2486 return fImage->alt.vector;
2487 }
2488 // vectorize
2489 return 0;
2490}
2491
2492////////////////////////////////////////////////////////////////////////////////
2493/// In case of vectorized image return an associated array of doubles
2494/// otherwise this method creates and returns a 2D array of doubles corresponding to palette.
2495/// If palette is ZERO a color converted to double value [0, 1] according to formula
2496/// ~~~ {.cpp}
2497/// Double_t((r << 16) + (g << 8) + b)/0xFFFFFF
2498/// ~~~
2499/// The returned array must be deleted after usage.
2500
2502{
2503 if (!fImage) {
2504 Warning("GetArray", "Bad Image");
2505 return 0;
2506 }
2507
2508 TArrayD *ret;
2509
2510 if (fImage->alt.vector) {
2511 ret = new TArrayD(fImage->width*fImage->height, fImage->alt.vector);
2512 return ret;
2513 }
2514
2515 ASImageDecoder *imdec;
2516
2517 w = w ? w : fImage->width;
2518 h = h ? h : fImage->height;
2519
2520 if ((fImage->width != w) || (fImage->height != h)) {
2521 Scale(w, h);
2522 }
2523
2524 ASImage *img = fScaledImage ? fScaledImage->fImage : fImage;
2525
2526 if ((imdec = start_image_decoding(0, img, SCL_DO_ALL, 0, 0,
2527 img->width, 0, 0)) == 0) {
2528 Warning("GetArray", "Failed to create image decoder");
2529 return 0;
2530 }
2531
2532 ret = new TArrayD(w * h);
2533 CARD32 r = 0;
2534 CARD32 g = 0;
2535 CARD32 b = 0;
2536 Int_t p = 0;
2537 Double_t v = 0;
2538
2539 for (UInt_t k = 0; k < h; k++) {
2540 imdec->decode_image_scanline(imdec);
2541
2542 for (UInt_t i = 0; i < w; ++i) {
2543 if ((r == imdec->buffer.red[i]) &&
2544 (g == imdec->buffer.green[i]) &&
2545 (b == imdec->buffer.blue[i])) {
2546 } else {
2547 r = imdec->buffer.red[i];
2548 g = imdec->buffer.green[i];
2549 b = imdec->buffer.blue[i];
2550 if (palette) p = palette->FindColor(r, g, b);
2551 }
2552 v = palette ? palette->fPoints[p] : Double_t((r << 16) + (g << 8) + b)/0xFFFFFF;
2553 ret->AddAt(v, (h-k-1)*w + i);
2554 }
2555 }
2556
2557 stop_image_decoding(&imdec);
2558 return ret;
2559}
2560
2561////////////////////////////////////////////////////////////////////////////////
2562/// Draw text of size (in pixels for TrueType fonts)
2563/// at position (x, y) with color specified by hex string.
2564///
2565/// - font_name: TrueType font's filename or X font spec or alias.
2566/// 3D style of text is one of the following:
2567/// * 0 plain 2D text,
2568/// * 1 embossed,
2569/// * 2 sunken,
2570/// * 3 shade above,
2571/// * 4 shade below,
2572/// * 5 embossed thick,
2573/// * 6 sunken thick.
2574/// * 7 outline above,
2575/// * 8 ouline below,
2576/// * 9 full ouline.
2577/// - fore_file specifies foreground texture of text.
2578
2580 const char *color, const char *font_name,
2581 EText3DType type, const char *fore_file, Float_t angle)
2582{
2583 UInt_t width=0, height=0;
2584 ARGB32 text_color = ARGB32_Black;
2585 ASImage *fore_im = 0;
2586 ASImage *text_im = 0;
2587 Bool_t ttfont = kFALSE;
2588
2589 if (!InitVisual()) {
2590 Warning("DrawText", "Visual not initiated");
2591 return;
2592 }
2593
2594 TString fn = font_name;
2595 fn.Strip();
2596
2597 // This is for backward compatibility...
2598 if (fn.Last('/') == 0) fn = fn(1, fn.Length() - 1);
2599
2600 const char *ttpath = gEnv->GetValue("Root.TTFontPath",
2602 char *tmpstr = gSystem->Which(ttpath, fn, kReadPermission);
2603 fn = tmpstr;
2604 delete [] tmpstr;
2605
2606 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")) {
2607 ttfont = kTRUE;
2608 }
2609
2610 if (color) {
2611 parse_argb_color(color, &text_color);
2612 }
2613
2614 if (fImage && fImage->alt.argb32 && ttfont) {
2615 DrawTextTTF(x, y, text, size, text_color, fn.Data(), angle);
2616 return;
2617 }
2618
2619 if (!gFontManager) {
2620 gFontManager = create_font_manager(fgVisual->dpy, 0, 0);
2621 }
2622
2623 if (!gFontManager) {
2624 Warning("DrawText", "cannot create Font Manager");
2625 return;
2626 }
2627
2628 ASFont *font = get_asfont(gFontManager, fn.Data(), 0, size, ASF_GuessWho);
2629
2630 if (!font) {
2631 font = get_asfont(gFontManager, "fixed", 0, size, ASF_GuessWho);
2632 if (!font) {
2633 Warning("DrawText", "cannot find a font %s", font_name);
2634 return;
2635 }
2636 }
2637
2638 get_text_size(text, font, (ASText3DType)type, &width, &height);
2639
2640 if (!fImage) {
2641 fImage = create_asimage(width, height, 0);
2642 fill_asimage(fgVisual, fImage, 0, 0, width, height, 0xFFFFFFFF);
2643 }
2644
2645 text_im = draw_text(text, font, (ASText3DType)type, 0);
2646
2647 ASImage *rimg = fImage;
2648
2649 if (fore_file) {
2650 ASImage *tmp = file2ASImage(fore_file, 0xFFFFFFFF, SCREEN_GAMMA, 0, 0);
2651 if (tmp) {
2652 if ((tmp->width != width) || (tmp->height != height)) {
2653 fore_im = tile_asimage(fgVisual, tmp, 0, 0, width, height, 0,
2654 ASA_ASImage, GetImageCompression(), GetImageQuality());
2655 }
2656 destroy_asimage(&tmp);
2657 } else {
2658 fore_im = tmp;
2659 }
2660 }
2661
2662 if (fore_im) {
2663 move_asimage_channel(fore_im, IC_ALPHA, text_im, IC_ALPHA);
2664 destroy_asimage(&text_im);
2665 } else {
2666 fore_im = text_im ;
2667 }
2668
2669 release_font(font);
2670
2671 if (fore_im) {
2672 ASImage *rendered_im;
2673 ASImageLayer layers[2];
2674
2675 init_image_layers(&(layers[0]), 2);
2676 fore_im->back_color = text_color;
2677 layers[0].im = rimg;
2678 layers[0].dst_x = 0;
2679 layers[0].dst_y = 0;
2680 layers[0].clip_width = rimg->width;
2681 layers[0].clip_height = rimg->height;
2682 layers[0].bevel = 0;
2683 layers[1].im = fore_im;
2684 layers[1].dst_x = x;
2685 layers[1].dst_y = y;
2686 layers[1].clip_width = fore_im->width;
2687 layers[1].clip_height = fore_im->height;
2688
2689 rendered_im = merge_layers(fgVisual, &(layers[0]), 2, rimg->width, rimg->height,
2690 ASA_ASImage, GetImageCompression(), GetImageQuality());
2691
2692 destroy_asimage(&fore_im);
2693 DestroyImage();
2694 fImage = rendered_im;
2695 UnZoom();
2696 }
2697}
2698
2699////////////////////////////////////////////////////////////////////////////////
2700/// Merge two images.
2701///
2702/// op is string which specifies overlay operation. Supported operations are:
2703///
2704/// - add - color addition with saturation
2705/// - alphablend - alpha-blending
2706/// - allanon - color values averaging
2707/// - colorize - hue and saturate bottom image same as top image
2708/// - darken - use lowest color value from both images
2709/// - diff - use absolute value of the color difference between two images
2710/// - dissipate - randomly alpha-blend images
2711/// - hue - hue bottom image same as top image
2712/// - lighten - use highest color value from both images
2713/// - overlay - some weird image overlaying(see GIMP)
2714/// - saturate - saturate bottom image same as top image
2715/// - screen - another weird image overlaying(see GIMP)
2716/// - sub - color substraction with saturation
2717/// - tint - tinting image with image
2718/// - value - value bottom image same as top image
2719
2720void TASImage::Merge(const TImage *im, const char *op, Int_t x, Int_t y)
2721{
2722 if (!im) return;
2723
2724 if (!InitVisual()) {
2725 Warning("Merge", "Visual not initiated");
2726 return;
2727 }
2728
2729 ASImage *rendered_im;
2730 ASImageLayer layers[2];
2731
2732 init_image_layers(&(layers[0]), 2);
2733 layers[0].im = fImage;
2734 layers[0].dst_x = 0;
2735 layers[0].dst_y = 0;
2736 layers[0].clip_width = fImage->width;
2737 layers[0].clip_height = fImage->height;
2738 layers[0].bevel = 0;
2739 layers[1].im = ((TASImage*)im)->fImage;
2740 layers[1].dst_x = x;
2741 layers[1].dst_y = y;
2742 layers[1].clip_width = im->GetWidth();
2743 layers[1].clip_height = im->GetHeight();
2744 layers[1].merge_scanlines = blend_scanlines_name2func(op ? op : "add");
2745
2746 rendered_im = merge_layers(fgVisual, &(layers[0]), 2, fImage->width, fImage->height,
2747 ASA_ASImage, GetImageCompression(), GetImageQuality());
2748
2749 DestroyImage();
2750 fImage = rendered_im;
2751 UnZoom();
2752}
2753
2754////////////////////////////////////////////////////////////////////////////////
2755/// Perform Gaussian blur of the image (useful for drop shadows).
2756/// - hr - horizontal radius of the blur
2757/// - vr - vertical radius of the blur
2758
2760{
2761 if (!InitVisual()) {
2762 Warning("Blur", "Visual not initiated");
2763 return;
2764 }
2765
2766 if (!fImage) {
2767 fImage = create_asimage(100, 100, 0);
2768
2769 if (!fImage) {
2770 Warning("Blur", "Failed to create image");
2771 return;
2772 }
2773
2774 fill_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height, ARGB32_White);
2775 }
2776
2777 ASImage *rendered_im = blur_asimage_gauss(fgVisual, fImage, hr > 0 ? hr : 3,
2778 vr > 0 ? vr : 3, SCL_DO_ALL,
2779 ASA_ASImage, GetImageCompression(), GetImageQuality());
2780 DestroyImage();
2781 fImage = rendered_im;
2782 UnZoom();
2783}
2784
2785////////////////////////////////////////////////////////////////////////////////
2786/// Clone image.
2787
2788TObject *TASImage::Clone(const char *newname) const
2789{
2790 if (!InitVisual() || !fImage) {
2791 Warning("Clone", "Image not initiated");
2792 return 0;
2793 }
2794
2796
2797 if (!im) {
2798 Warning("Clone", "Failed to create image");
2799 return 0;
2800 }
2801
2802 im->SetName(newname);
2803
2804 im->fImage = clone_asimage(fImage, SCL_DO_ALL);
2805 im->fMaxValue = fMaxValue;
2806 im->fMinValue = fMinValue;
2807 im->fZoomOffX = fZoomOffX;
2808 im->fZoomOffY = fZoomOffY;
2809 im->fZoomWidth = fZoomWidth;
2813
2814 if (fImage->alt.argb32) {
2815 UInt_t sz = fImage->width * fImage->height;
2816 im->fImage->alt.argb32 = (ARGB32*)safemalloc(sz*sizeof(ARGB32));
2817 memcpy(im->fImage->alt.argb32, fImage->alt.argb32, sz * sizeof(ARGB32));
2818 }
2819
2820 return im;
2821}
2822
2823////////////////////////////////////////////////////////////////////////////////
2824/// Reduce color-depth of an image and fills vector of "scientific data"
2825/// [0...1]
2826///
2827/// Colors are reduced by allocating color cells to most used colors first,
2828/// and then approximating other colors with those allocated.
2829///
2830/// \param[in] max_colors - maximum size of the colormap.
2831/// \param[in] dither - number of bits to strip off the color data ( 0...7 )
2832/// \param[in] opaque_threshold - alpha channel threshold at which pixel should be treated as opaque
2833
2834Double_t *TASImage::Vectorize(UInt_t max_colors, UInt_t dither, Int_t opaque_threshold)
2835{
2836 if (!InitVisual()) {
2837 Warning("Vectorize", "Visual not initiated");
2838 return 0;
2839 }
2840
2841 if (!fImage) {
2842 fImage = create_asimage(100, 100, 0);
2843
2844 if (!fImage) {
2845 Warning("Vectorize", "Failed to create image");
2846 return 0;
2847 }
2848
2849 fill_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height, ARGB32_White);
2850 }
2851
2852 ASColormap cmap;
2853 int *res;
2854 UInt_t r=0, g=0, b=0;
2855
2856 dither = dither > 7 ? 7 : dither;
2857
2858 res = colormap_asimage(fImage, &cmap, max_colors, dither, opaque_threshold);
2859
2860 Double_t *vec = new Double_t[fImage->height*fImage->width];
2861 UInt_t v;
2862 Double_t tmp;
2863 fMinValue = 2;
2864 fMaxValue = -1;
2865
2866 for (UInt_t y = 0; y < fImage->height; y++) {
2867 for (UInt_t x = 0; x < fImage->width; x++) {
2868 int i = y*fImage->width + x;
2869 if (res) {
2870 g = INDEX_SHIFT_GREEN(cmap.entries[res[i]].green);
2871 b = INDEX_SHIFT_BLUE(cmap.entries[res[i]].blue);
2872 r = INDEX_SHIFT_RED(cmap.entries[res[i]].red);
2873 }
2874 v = MAKE_INDEXED_COLOR24(r,g,b);
2875 v = (v>>12)&0x0FFF;
2876 tmp = Double_t(v)/0x0FFF;
2877 vec[(fImage->height - y - 1)*fImage->width + x] = tmp;
2878 if (fMinValue > tmp) fMinValue = tmp;
2879 if (fMaxValue < tmp) fMaxValue = tmp;
2880 }
2881 }
2882 TImagePalette *pal = new TImagePalette(cmap.count);
2883
2884 for (UInt_t j = 0; j < cmap.count; j++) {
2885 g = INDEX_SHIFT_GREEN(cmap.entries[j].green);
2886 b = INDEX_SHIFT_BLUE(cmap.entries[j].blue);
2887 r = INDEX_SHIFT_RED(cmap.entries[j].red);
2888 v = MAKE_INDEXED_COLOR24(r,g,b);
2889
2890 v = (v>>12) & 0x0FFF;
2891 pal->fPoints[j] = Double_t(v)/0x0FFF;
2892
2893 pal->fColorRed[j] = cmap.entries[j].red << 8;
2894 pal->fColorGreen[j] = cmap.entries[j].green << 8;
2895 pal->fColorBlue[j] = cmap.entries[j].blue << 8;
2896 pal->fColorAlpha[j] = 0xFF00;
2897 }
2898
2899 destroy_colormap(&cmap, kTRUE);
2900
2901 fPalette = *pal;
2902 fImage->alt.vector = vec;
2903 UnZoom();
2904 // ROOT-7647: res is allocated with `safemalloc` by colormap_asimage
2905 if (res) safefree(res);
2906 return (Double_t*)fImage->alt.vector;
2907}
2908
2909////////////////////////////////////////////////////////////////////////////////
2910/// This function will tile original image to specified size with offsets
2911/// requested, and then it will go though it and adjust hue, saturation and
2912/// value of those pixels that have specific hue, set by affected_hue/
2913/// affected_radius parameters. When affected_radius is greater then 180
2914/// entire image will be adjusted. Note that since grayscale colors have
2915/// no hue - the will not get adjusted. Only saturation and value will be
2916/// adjusted in gray pixels.
2917///
2918/// Hue is measured as an angle on a 360 degree circle, The following is
2919/// relationship of hue values to regular color names :
2920/// - red - 0
2921/// - yellow - 60
2922/// - green - 120
2923/// - cyan - 180
2924/// - blue - 240
2925/// - magenta - 300
2926/// - red - 360
2927///
2928/// All the hue values in parameters will be adjusted to fall within 0-360 range.
2929///
2930/// \param[in] hue hue in degrees in range 0-360. This allows to limit
2931/// impact of color adjustment to affect only limited range of hues.
2932///
2933/// \param[in] radius value in degrees to be used in order to
2934/// calculate the range of affected hues. Range is determined by
2935/// substracting and adding this value from/to affected_hue.
2936///
2937/// \param[in] H value by which to change hues in affected range.
2938/// \param[in] S value by which to change saturation of the pixels in affected hue range.
2939/// \param[in] V value by which to change Value(brightness) of pixels in affected hue range.
2940///
2941/// \param[in] x,y position on infinite surface tiled with original image, of the
2942/// left-top corner of the area to be used for new image.
2943///
2944/// \param[in] width, height size of the area of the original image to be used for new image.
2945/// Default is current width, height of the image.
2946
2949{
2950 if (!InitVisual()) {
2951 Warning("HSV", "Visual not initiated");
2952 return;
2953 }
2954
2955 if (!fImage) {
2956 fImage = create_asimage(width ? width : 20, height ? height : 20, 0);
2957
2958 if (!fImage) {
2959 Warning("HSV", "Failed to create image");
2960 return;
2961 }
2962
2963 x = 0;
2964 y = 0;
2965 fill_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height, ARGB32_White);
2966 }
2967
2968 width = !width ? fImage->width : width;
2969 height = !height ? fImage->height : height;
2970
2971 ASImage *rendered_im = 0;
2972
2973 if (H || S || V) {
2974 rendered_im = adjust_asimage_hsv(fgVisual, fImage, x, y, width, height,
2975 hue, radius, H, S, V, ASA_ASImage, 100,
2976 ASIMAGE_QUALITY_TOP);
2977 }
2978 if (!rendered_im) {
2979 Warning("HSV", "Failed to create rendered image");
2980 return;
2981 }
2982
2983 DestroyImage();
2984 fImage = rendered_im;
2985 UnZoom();
2986}
2987
2988////////////////////////////////////////////////////////////////////////////////
2989/// Render multipoint gradient inside rectangle of size (width, height)
2990/// at position (x,y) within the existing image.
2991///
2992/// \param[in] angle Given in degrees. Default is 0. This is the
2993/// direction of the gradient. Currently the only supported
2994/// values are 0, 45, 90, 135, 180, 225, 270, 315. 0 means left
2995/// to right, 90 means top to bottom, etc.
2996///
2997/// \param[in] colors Whitespace-separated list of colors. At least two
2998/// colors are required. Each color in this list will be visited
2999/// in turn, at the intervals given by the offsets attribute.
3000///
3001/// \param[in] offsets Whitespace-separated list of floating point values
3002/// ranging from 0.0 to 1.0. The colors from the colors attribute
3003/// are given these offsets, and the final gradient is rendered
3004/// from the combination of the two. If both colors and offsets
3005/// are given but the number of colors and offsets do not match,
3006/// the minimum of the two will be used, and the other will be
3007/// truncated to match. If offsets are not given, a smooth
3008/// stepping from 0.0 to 1.0 will be used.
3009/// \param[in] x x position coordinate
3010/// \param[in] y y position coordinate
3011/// \param[in] width image width, if 0, it will be read from fImage
3012/// \param[in] height image height, if 0, it will be read from fImage
3013void TASImage::Gradient(UInt_t angle, const char *colors, const char *offsets,
3015{
3016 if (!InitVisual()) {
3017 Warning("Gradient", "Visual not initiated");
3018 return;
3019 }
3020
3021 ASImage *rendered_im = 0;
3022 ASGradient gradient;
3023
3024 int reverse = 0, npoints1 = 0, npoints2 = 0;
3025 char *p;
3026 char *pb;
3027 char ch;
3028 TString str = colors;
3029 TString col;
3030
3031 if ((angle > 2 * 180 * 15 / 16) || (angle < 2 * 180 * 1 / 16)) {
3032 gradient.type = GRADIENT_Left2Right;
3033 } else if (angle < 2 * 180 * 3 / 16) {
3034 gradient.type = GRADIENT_TopLeft2BottomRight;
3035 } else if (angle < 2 * 180 * 5 / 16) {
3036 gradient.type = GRADIENT_Top2Bottom;
3037 } else if (angle < 2 * 180 * 7 / 16) {
3038 gradient.type = GRADIENT_BottomLeft2TopRight; reverse = 1;
3039 } else if (angle < 2 * 180 * 9 / 16) {
3040 gradient.type = GRADIENT_Left2Right; reverse = 1;
3041 } else if (angle < 2 * 180 * 11 / 16) {
3042 gradient.type = GRADIENT_TopLeft2BottomRight; reverse = 1;
3043 } else if (angle < 2 * 180 * 13 / 16) {
3044 gradient.type = GRADIENT_Top2Bottom; reverse = 1;
3045 } else {
3046 gradient.type = GRADIENT_BottomLeft2TopRight;
3047 }
3048
3049 for (p = (char*)colors; isspace((int)*p); p++) { }
3050
3051 for (npoints1 = 0; *p; npoints1++) {
3052 if (*p) {
3053 for ( ; *p && !isspace((int)*p); p++) { }
3054 }
3055 for ( ; isspace((int)*p); p++) { }
3056 }
3057 if (offsets) {
3058 for (p = (char*)offsets; isspace((int)*p); p++) { }
3059
3060 for (npoints2 = 0; *p; npoints2++) {
3061 if (*p) {
3062 for ( ; *p && !isspace((int)*p); p++) { }
3063 }
3064 for ( ; isspace((int)*p); p++) { }
3065 }
3066 }
3067 if (npoints1 > 1) {
3068 int i;
3069 if (offsets && (npoints1 > npoints2)) npoints1 = npoints2;
3070
3071 if (!width) {
3072 width = fImage ? fImage->width : 20;
3073 }
3074 if (!height) {
3075 height = fImage ? fImage->height : 20;
3076 }
3077
3078 gradient.color = new ARGB32[npoints1];
3079 gradient.offset = new double[npoints1];
3080
3081 for (p = (char*)colors; isspace((int)*p); p++) { }
3082
3083 for (npoints1 = 0; *p; ) {
3084 pb = p;
3085
3086 if (*p) {
3087 for ( ; *p && !isspace((int)*p); p++) { }
3088 }
3089 for ( ; isspace((int)*p); p++) { }
3090
3091 col = str(pb - colors, p - pb);
3092
3093 if (parse_argb_color(col.Data(), gradient.color + npoints1) != col) {
3094 npoints1++;
3095 } else {
3096 Warning("Gradient", "Failed to parse color [%s] - defaulting to black", pb);
3097 }
3098 }
3099
3100 if (offsets) {
3101 for (p = (char*)offsets; isspace((int)*p); p++) { }
3102
3103 for (npoints2 = 0; *p; ) {
3104 pb = p;
3105
3106 if (*p) {
3107 for ( ; *p && !isspace((int)*p); p++) { }
3108 }
3109 ch = *p; *p = '\0';
3110 gradient.offset[npoints2] = strtod(pb, &pb);
3111
3112 if (pb == p) npoints2++;
3113 *p = ch;
3114 for ( ; isspace((int)*p); p++) { }
3115 }
3116 } else {
3117 for (npoints2 = 0; npoints2 < npoints1; npoints2++) {
3118 gradient.offset[npoints2] = (double)npoints2 / (npoints1 - 1);
3119 }
3120 }
3121 gradient.npoints = npoints1;
3122
3123 if (npoints2 && (gradient.npoints > npoints2)) {
3124 gradient.npoints = npoints2;
3125 }
3126 if (reverse) {
3127 for (i = 0; i < gradient.npoints/2; i++) {
3128 int i2 = gradient.npoints - 1 - i;
3129 ARGB32 c = gradient.color[i];
3130 double o = gradient.offset[i];
3131 gradient.color[i] = gradient.color[i2];
3132 gradient.color[i2] = c;
3133 gradient.offset[i] = gradient.offset[i2];
3134 gradient.offset[i2] = o;
3135 }
3136 for (i = 0; i < gradient.npoints; i++) {
3137 gradient.offset[i] = 1.0 - gradient.offset[i];
3138 }
3139 }
3140 rendered_im = make_gradient(fgVisual, &gradient, width, height, SCL_DO_ALL,
3141 ASA_ASImage, GetImageCompression(), GetImageQuality());
3142
3143 delete [] gradient.color;
3144 delete [] gradient.offset;
3145 }
3146
3147 if (!rendered_im) { // error
3148 Warning("Gradient", "Failed to create gradient image");
3149 return;
3150 }
3151
3152 if (!fImage) {
3153 fImage = rendered_im;
3154 return;
3155 }
3156
3157 ASImageLayer layers[2];
3158
3159 init_image_layers(&(layers[0]), 2);
3160 layers[0].im = fImage;
3161 layers[0].dst_x = 0;
3162 layers[0].dst_y = 0;
3163 layers[0].clip_width = fImage->width;
3164 layers[0].clip_height = fImage->height;
3165 layers[0].bevel = 0;
3166 layers[1].im = rendered_im;
3167 layers[1].dst_x = x;
3168 layers[1].dst_y = y;
3169 layers[1].clip_width = width;
3170 layers[1].clip_height = height;
3171 layers[1].merge_scanlines = alphablend_scanlines;
3172
3173 ASImage *merge_im = merge_layers(fgVisual, &(layers[0]), 2, fImage->width, fImage->height,
3174 ASA_ASImage, GetImageCompression(), GetImageQuality());
3175 if (!merge_im) {
3176 Warning("Gradient", "Failed to create merged image");
3177 return;
3178 }
3179
3180 destroy_asimage(&rendered_im);
3181 DestroyImage();
3182 fImage = merge_im;
3183 UnZoom();
3184}
3185
3186////////////////////////////////////////////////////////////////////////////////
3187/// Make component hilite.
3188/// (used internally)
3189
3190static CARD8 MakeComponentHilite(int cmp)
3191{
3192 if (cmp < 51) {
3193 cmp = 51;
3194 }
3195 cmp = (cmp * 12) / 10;
3196
3197 return (cmp > 255) ? 255 : cmp;
3198}
3199
3200////////////////////////////////////////////////////////////////////////////////
3201/// Calculate highlite color.
3202/// (used internally)
3203
3204static ARGB32 GetHilite(ARGB32 background)
3205{
3206 return ((MakeComponentHilite((background>>24) & 0x000000FF) << 24) & 0xFF000000) |
3207 ((MakeComponentHilite((background & 0x00FF0000) >> 16) << 16) & 0x00FF0000) |
3208 ((MakeComponentHilite((background & 0x0000FF00) >> 8) << 8) & 0x0000FF00) |
3209 ((MakeComponentHilite((background & 0x000000FF))) & 0x000000FF);
3210}
3211
3212////////////////////////////////////////////////////////////////////////////////
3213/// Calculate shadow color.
3214/// (used internally)
3215
3216static ARGB32 GetShadow(ARGB32 background)
3217{
3218 return (background >> 1) & 0x7F7F7F7F;
3219}
3220
3221////////////////////////////////////////////////////////////////////////////////
3222/// Get average.
3223/// (used internally)
3224
3225static ARGB32 GetAverage(ARGB32 foreground, ARGB32 background)
3226{
3227 CARD16 a, r, g, b;
3228
3229 a = ARGB32_ALPHA8(foreground) + ARGB32_ALPHA8(background);
3230 a = (a<<3)/10;
3231 r = ARGB32_RED8(foreground) + ARGB32_RED8(background);
3232 r = (r<<3)/10;
3233 g = ARGB32_GREEN8(foreground) + ARGB32_GREEN8(background);
3234 g = (g<<3)/10;
3235 b = ARGB32_BLUE8(foreground) + ARGB32_BLUE8(background);
3236 b = (b<<3)/10;
3237
3238 return MAKE_ARGB32(a, r, g, b);
3239}
3240
3241
3242////////////////////////////////////////////////////////////////////////////////
3243/// Bevel is used to create 3D effect while drawing buttons, or any other
3244/// image that needs to be framed. Bevel is drawn using 2 primary colors:
3245/// one for top and left sides - hi color, and another for bottom and
3246/// right sides - low color. Bevel can be drawn over existing image or
3247/// as newly created, as it is shown in code below:
3248/// ~~~ {.cpp}
3249/// TImage *img = TImage::Create();
3250/// img->Bevel(0, 0, 400, 300, "#dddddd", "#000000", 3);
3251/// ~~~
3252
3254 const char *hi_color, const char *lo_color, UShort_t thick,
3255 Bool_t reverse)
3256{
3257 if (!InitVisual()) {
3258 Warning("Bevel", "Visual not initiated");
3259 return;
3260 }
3261
3262 ASImageBevel bevel;
3263 bevel.type = 0;
3264
3265 ARGB32 hi=ARGB32_White, lo=ARGB32_White;
3266 parse_argb_color(hi_color, &hi);
3267 parse_argb_color(lo_color, &lo);
3268
3269 if (reverse) {
3270 bevel.lo_color = hi;
3271 bevel.lolo_color = GetHilite(hi);
3272 bevel.hi_color = lo;
3273 bevel.hihi_color = GetShadow(lo);
3274 } else {
3275 bevel.hi_color = hi;
3276 bevel.hihi_color = GetHilite(hi);
3277 bevel.lo_color = lo;
3278 bevel.lolo_color = GetShadow(lo);
3279 }
3280 bevel.hilo_color = GetAverage(hi, lo);
3281
3282 int extra_hilite = 2;
3283 bevel.left_outline = bevel.top_outline = bevel.right_outline = bevel.bottom_outline = thick;
3284 bevel.left_inline = bevel.top_inline = bevel.right_inline = bevel.bottom_inline = extra_hilite + 1;
3285
3286 if (bevel.top_outline > 1) {
3287 bevel.top_inline += bevel.top_outline - 1;
3288 }
3289
3290 if (bevel.left_outline > 1) {
3291 bevel.left_inline += bevel.left_outline - 1;
3292 }
3293
3294 if (bevel.right_outline > 1) {
3295 bevel.right_inline += bevel.right_outline - 1;
3296 }
3297
3298 if (bevel.bottom_outline > 1) {
3299 bevel.bottom_inline += bevel.bottom_outline - 1;
3300 }
3301
3302 ASImage *merge_im;
3303 ARGB32 fill = ((hi>>24) != 0xff) || ((lo>>24) != 0xff) ? bevel.hilo_color : (bevel.hilo_color | 0xff000000);
3304
3305 if (!fImage) {
3306 fImage = create_asimage(width ? width : 20, height ? height : 20, 0);
3307
3308 if (!fImage) {
3309 Warning("Bevel", "Failed to create image");
3310 return;
3311 }
3312
3313 x = 0;
3314 y = 0;
3315 fill_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height, fill);
3316 }
3317
3318 width = !width ? fImage->width : width;
3319 height = !height ? fImage->height : height;
3320
3321 ASImageLayer layers[2];
3322 init_image_layers(&(layers[0]), 2);
3323
3324 layers[0].im = fImage;
3325 layers[0].dst_x = 0;
3326 layers[0].dst_y = 0;
3327 layers[0].clip_width = fImage->width;
3328 layers[0].clip_height = fImage->height;
3329 layers[0].bevel = 0;
3330
3331 UInt_t w = width - (bevel.left_outline + bevel.right_outline);
3332 UInt_t h = height - (bevel.top_outline + bevel.bottom_outline);
3333 ASImage *bevel_im = create_asimage(w, h, 0);
3334
3335 if (!bevel_im) {
3336 Warning("Bevel", "Failed to create bevel image");
3337 return;
3338 }
3339
3340 layers[1].im = bevel_im;
3341 fill_asimage(fgVisual, bevel_im, 0, 0, w, h, fill);
3342
3343 layers[1].dst_x = x;
3344 layers[1].dst_y = y;
3345 layers[1].clip_width = width;
3346 layers[1].clip_height = height;
3347 layers[1].bevel = &bevel;
3348 layers[1].merge_scanlines = alphablend_scanlines;
3349
3350 merge_im = merge_layers(fgVisual, &(layers[0]), 2, fImage->width, fImage->height,
3351 ASA_ASImage, GetImageCompression(), GetImageQuality());
3352 destroy_asimage(&bevel_im);
3353
3354 if (!merge_im) {
3355 Warning("Bevel", "Failed to image");
3356 return;
3357 }
3358
3359 DestroyImage();
3360 fImage = merge_im;
3361 UnZoom();
3362}
3363
3364
3365////////////////////////////////////////////////////////////////////////////////
3366/// Enlarge image, padding it with specified color on each side in
3367/// accordance with requested geometry.
3368
3369void TASImage::Pad(const char *col, UInt_t l, UInt_t r, UInt_t t, UInt_t b)
3370{
3371 Int_t x, y;
3372 UInt_t w, h;
3373
3374 if (!InitVisual()) {
3375 Warning("Pad", "Visual not initiated");
3376 return;
3377 }
3378
3379 if (!fImage) {
3380 fImage = create_asimage(100, 100, 0);
3381
3382 if (!fImage) {
3383 Warning("Pad", "Failed to create image");
3384 return;
3385 }
3386
3387 fill_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height, ARGB32_White);
3388 }
3389
3390 ARGB32 color = ARGB32_White;
3391 parse_argb_color(col, &color);
3392
3393 x = l;
3394 y = t;
3395 w = l + fImage->width + r;
3396 h = t + fImage->height + b;
3397
3398 ASImage *img = pad_asimage(fgVisual, fImage, x, y, w, h, color,
3399 ASA_ASImage, GetImageCompression(), GetImageQuality());
3400
3401 if (!img) {
3402 Warning("Pad", "Failed to create output image");
3403 return;
3404 }
3405
3406 DestroyImage();
3407 fImage = img;
3408 UnZoom();
3410}
3411
3412
3413////////////////////////////////////////////////////////////////////////////////
3414/// Crop an image.
3415
3417{
3418 if (!InitVisual()) {
3419 Warning("Crop", "Visual not initiated");
3420 return;
3421 }
3422
3423 if (!fImage) {
3424 Warning("Crop", "No image");
3425 return;
3426 }
3427
3428 x = x < 0 ? 0 : x;
3429 y = y < 0 ? 0 : y;
3430
3431 width = x + width > fImage->width ? fImage->width - x : width;
3432 height = y + height > fImage->height ? fImage->height - y : height;
3433
3434 if ((width == fImage->width) && (height == fImage->height)) {
3435 Warning("Crop", "input size larger than image");
3436 return;
3437 }
3438 ASImageDecoder *imdec = start_image_decoding(fgVisual, fImage, SCL_DO_ALL,
3439 x, y, width, height, 0);
3440
3441 if (!imdec) {
3442 Warning("Crop", "Failed to start image decoding");
3443 return;
3444 }
3445
3446 ASImage *img = create_asimage(width, height, 0);
3447
3448 if (!img) {
3449 delete [] imdec;
3450 Warning("Crop", "Failed to create image");
3451 return;
3452 }
3453
3454 ASImageOutput *imout = start_image_output(fgVisual, img, ASA_ASImage,
3456
3457 if (!imout) {
3458 Warning("Crop", "Failed to start image output");
3459 destroy_asimage(&img);
3460 if (imdec) delete [] imdec;
3461 return;
3462 }
3463
3464#ifdef HAVE_MMX
3465 mmx_init();
3466#endif
3467
3468 for (UInt_t i = 0; i < height; i++) {
3469 imdec->decode_image_scanline(imdec);
3470 imout->output_image_scanline(imout, &(imdec->buffer), 1);
3471 }
3472
3473 stop_image_decoding(&imdec);
3474 stop_image_output(&imout);
3475
3476#ifdef HAVE_MMX
3477 mmx_off();
3478#endif
3479
3480 DestroyImage();
3481 fImage = img;
3482 UnZoom();
3484}
3485
3486////////////////////////////////////////////////////////////////////////////////
3487/// Append image.
3488///
3489/// option:
3490/// - "+" - appends to the right side
3491/// - "/" - appends to the bottom
3492
3493void TASImage::Append(const TImage *im, const char *option, const char *color )
3494{
3495 if (!im) return;
3496
3497 if (!InitVisual()) {
3498 Warning("Append", "Visual not initiated");
3499 return;
3500 }
3501
3502 if (!fImage) {
3503 fImage = ((TASImage*)im)->fImage;
3504 return;
3505 }
3506
3507 TString opt = option;
3508 opt.Strip();
3509
3510 UInt_t width = fImage->width;
3511 UInt_t height = fImage->height;
3512
3513 if (opt == "+") {
3514 Pad(color, 0, im->GetWidth(), 0, 0);
3515 Merge(im, "alphablend", width, 0);
3516 } else if (opt == "/") {
3517 Pad(color, 0, 0, 0, im->GetHeight());
3518 Merge(im, "alphablend", 0, height);
3519 } else {
3520 return;
3521 }
3522
3523 UnZoom();
3524}
3525
3526////////////////////////////////////////////////////////////////////////////////
3527/// BeginPaint initializes internal array[width x height] of ARGB32 pixel
3528/// values.
3529///
3530/// That provides quick access to image during paint operations.
3531/// To RLE compress image one needs to call EndPaint method when painting
3532/// is over.
3533
3535{
3536 if (!InitVisual()) {
3537 Warning("BeginPaint", "Visual not initiated");
3538 return;
3539 }
3540
3541 if (!fImage) {
3542 return;
3543 }
3544
3545 fPaintMode = mode;
3546
3547 if (!fPaintMode || fImage->alt.argb32) {
3548 return;
3549 }
3550
3551 ASImage *img = tile_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height,
3552 0, ASA_ARGB32, 0, ASIMAGE_QUALITY_DEFAULT);
3553
3554 if (!img) {
3555 Warning("BeginPaint", "Failed to create image");
3556 return;
3557 }
3558
3559 DestroyImage();
3560 fImage = img;
3561}
3562
3563////////////////////////////////////////////////////////////////////////////////
3564/// EndPaint does internal RLE compression of image data.
3565
3567{
3568 if (!fImage) {
3569 Warning("EndPaint", "no image");
3570 return;
3571 }
3572
3573 if (!fImage->alt.argb32) return;
3574
3575 ASImage *img = tile_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height,
3576 0, ASA_ASImage, 0, ASIMAGE_QUALITY_DEFAULT);
3577
3578 if (!img) {
3579 Warning("EndPaint", "Failed to create image");
3580 return;
3581 }
3582
3584 DestroyImage();
3585 fImage = img;
3586}
3587
3588////////////////////////////////////////////////////////////////////////////////
3589/// Return a pointer to internal array[width x height] of ARGB32 values
3590/// This array is directly accessible. That allows to manipulate/change the
3591/// image.
3592
3594{
3595 if (!fImage) {
3596 Warning("GetArgbArray", "no image");
3597 return 0;
3598 }
3599
3600 ASImage *img = fScaledImage ? fScaledImage->fImage : fImage;
3601 if (!img) return 0;
3602
3603 if (!img->alt.argb32) {
3604 if (fScaledImage) {
3606 img = fScaledImage->fImage;
3607 } else {
3608 BeginPaint();
3609 img = fImage;
3610 }
3611 }
3612
3613 return (UInt_t *)img->alt.argb32;
3614}
3615
3616////////////////////////////////////////////////////////////////////////////////
3617/// Return a pointer to an array[width x height] of RGBA32 values.
3618/// This array is created from internal ARGB32 array,
3619/// must be deleted after usage.
3620
3622{
3623 if (!fImage) {
3624 Warning("GetRgbaArray", "no image");
3625 return 0;
3626 }
3627
3628 ASImage *img = fScaledImage ? fScaledImage->fImage : fImage;
3629 if (!img) return 0;
3630
3631 if (!img->alt.argb32) {
3632 if (fScaledImage) {
3634 img = fScaledImage->fImage;
3635 } else {
3636 BeginPaint();
3637 img = fImage;
3638 }
3639 }
3640
3641 UInt_t i, j;
3642 Int_t y = 0;
3643 Int_t idx = 0;
3644 UInt_t a, rgb, rgba, argb;
3645 y = 0;
3646
3647 UInt_t *ret = new UInt_t[img->width*img->height];
3648
3649 for (i = 0; i < img->height; i++) {
3650 for (j = 0; j < img->width; j++) {
3651 idx = Idx(y + j);
3652 argb = img->alt.argb32[idx];
3653 a = argb >> 24;
3654 rgb = argb & 0x00ffffff;
3655 rgba = (rgb << 8) + a;
3656 ret[idx] = rgba;
3657 }
3658 y += img->width;
3659 }
3660
3661 return ret;
3662}
3663
3664////////////////////////////////////////////////////////////////////////////////
3665/// Return a pointer to scan-line.
3666
3668{
3669 if (!fImage) {
3670 Warning("GetScanline", "no image");
3671 return 0;
3672 }
3673
3674 ASImage *img = fScaledImage ? fScaledImage->fImage : fImage;
3675 CARD32 *ret = new CARD32[img->width];
3676
3677 ASImageDecoder *imdec = start_image_decoding(fgVisual, img, SCL_DO_ALL,
3678 0, y, img->width, 1, 0);
3679
3680 if (!imdec) {
3681 delete [] ret;
3682 Warning("GetScanline", "Failed to start image decoding");
3683 return 0;
3684 }
3685
3686#ifdef HAVE_MMX
3687 mmx_init();
3688#endif
3689
3690 imdec->decode_image_scanline(imdec);
3691 memcpy(imdec->buffer.buffer, ret, img->width*sizeof(CARD32));
3692 stop_image_decoding(&imdec);
3693
3694#ifdef HAVE_MMX
3695 mmx_off();
3696#endif
3697
3698 return (UInt_t*)ret;
3699}
3700
3701
3702//______________________________________________________________________________
3703//
3704// Vector graphics
3705// a couple of macros which can be "assembler accelerated"
3706
3707#if defined(R__GNU) && defined(__i386__) && !defined(__sun)
3708#define _MEMSET_(dst, lng, val) __asm__("movl %0,%%eax \n"\
3709 "movl %1,%%edi \n" \
3710 "movl %2,%%ecx \n" \
3711 "cld \n" \
3712 "rep \n" \
3713 "stosl \n" \
3714 : /* no output registers */ \
3715 :"g" (val), "g" (dst), "g" (lng) \
3716 :"eax","edi","ecx" \
3717 )
3718
3719#else
3720 #define _MEMSET_(dst, lng, val) do {\
3721 for( UInt_t j=0; j < lng; j++) *((dst)+j) = val; } while (0)
3722
3723#endif
3724
3725#define FillSpansInternal(npt, ppt, widths, color) do {\
3726 UInt_t yy = ppt[0].fY*fImage->width;\
3727 for (UInt_t i = 0; i < npt; i++) {\
3728 _MEMSET_(&fImage->alt.argb32[Idx(yy + ppt[i].fX)], widths[i], color);\
3729 yy += ((i+1 < npt) && (ppt[i].fY != ppt[i+1].fY) ? fImage->width : 0);\
3730 }\
3731} while (0)
3732
3733////////////////////////////////////////////////////////////////////////////////
3734/// Fill rectangle of size (width, height) at position (x,y)
3735/// within the existing image with specified color.
3736
3738{
3739
3740 if (!InitVisual()) {
3741 Warning("FillRectangle", "Visual not initiated");
3742 return;
3743 }
3744
3745 if (!fImage) {
3746 Warning("FillRectangle", "no image");
3747 return;
3748 }
3749
3750 if (!fImage->alt.argb32) {
3751 BeginPaint();
3752 }
3753
3754 if (!fImage->alt.argb32) {
3755 Warning("FillRectangle", "Failed to get pixel array");
3756 return;
3757 }
3758
3759 ARGB32 color = (ARGB32)col;
3760
3761 if (width == 0) width = 1;
3762 if (height == 0) height = 1;
3763
3764 if (x < 0) {
3765 width += x;
3766 x = 0;
3767 }
3768 if (y < 0) {
3769 height += y;
3770 y = 0;
3771 }
3772
3773 Bool_t has_alpha = (color & 0xff000000) != 0xff000000;
3774
3775 x = x > (int)fImage->width ? (Int_t)fImage->width : x;
3776 y = y > (int)fImage->height ? (Int_t)fImage->height : y;
3777
3778 width = x + width > fImage->width ? fImage->width - x : width;
3779 height = y + height > fImage->height ? fImage->height - y : height;
3780
3781 if (!fImage->alt.argb32) {
3782 fill_asimage(fgVisual, fImage, x, y, width, height, color);
3783 } else {
3784 int yyy = y*fImage->width;
3785 if (!has_alpha) { // use faster memset
3786 ARGB32 *p0 = fImage->alt.argb32 + yyy + x;
3787 ARGB32 *p = p0;
3788 for (UInt_t i = 0; i < height; i++) {
3789 _MEMSET_(p, width, color);
3790 p += fImage->width;
3791 }
3792 } else {
3793 for (UInt_t i = y; i < y + height; i++) {
3794 int j = x + width;
3795 while (j > x) {
3796 j--;
3797 _alphaBlend(&fImage->alt.argb32[Idx(yyy + j)], &color);
3798 }
3799 yyy += fImage->width;
3800 }
3801 }
3802 }
3803}
3804
3805////////////////////////////////////////////////////////////////////////////////
3806/// Fill rectangle of size (width, height) at position (x,y)
3807/// within the existing image with specified color.
3808///
3809/// To create new image with Fill method the following code can be used:
3810/// ~~~ {.cpp}
3811/// TImage *img = TImage::Create();
3812/// img->Fill("#FF00FF", 0, 0, 400, 300);
3813/// ~~~
3814
3816{
3817 if (!InitVisual()) {
3818 Warning("Fill", "Visual not initiated");
3819 return;
3820 }
3821
3822 ARGB32 color = ARGB32_White;
3823
3824 if (col) {
3825 parse_argb_color(col, &color);
3826 }
3827
3828 if (!fImage) {
3829 fImage = create_asimage(width ? width : 20, height ? height : 20, 0);
3830 x = 0;
3831 y = 0;
3832 }
3833
3835 UnZoom();
3836}
3837
3838////////////////////////////////////////////////////////////////////////////////
3839/// Draw a vertical line.
3840
3842{
3843 ARGB32 color = (ARGB32)col;
3844 UInt_t half = 0;
3845
3846 if (!thick) thick = 1;
3847
3848 if (thick > 1) {
3849 half = thick >> 1;
3850 if (x > half) {
3851 x = x - half;
3852 } else {
3853 x = 0;
3854 thick += (x - half);
3855 }
3856 }
3857
3858 y2 = y2 >= fImage->height ? fImage->height - 1 : y2;
3859 y1 = y1 >= fImage->height ? fImage->height - 1 : y1;
3860 x = x + thick >= fImage->width ? fImage->width - thick - 1 : x;
3861
3862 int yy = y1*fImage->width;
3863 for (UInt_t y = y1; y <= y2; y++) {
3864 for (UInt_t w = 0; w < thick; w++) {
3865 if (x + w < fImage->width) {
3866 _alphaBlend(&fImage->alt.argb32[Idx(yy + (x + w))], &color);
3867 }
3868 }
3869 yy += fImage->width;
3870 }
3871}
3872
3873////////////////////////////////////////////////////////////////////////////////
3874/// Draw an horizontal line.
3875
3877{
3878 ARGB32 color = (ARGB32)col;
3879 UInt_t half = 0;
3880
3881 if (!thick) thick = 1;
3882
3883 if (thick > 1) {
3884 half = thick >> 1;
3885 if (y > half) {
3886 y = y - half;
3887 } else {
3888 y = 0;
3889 thick += (y - half);
3890 }
3891 }
3892
3893 y = y + thick >= fImage->height ? fImage->height - thick - 1 : y;
3894 x2 = x2 >= fImage->width ? fImage->width - 1 : x2;
3895 x1 = x1 >= fImage->width ? fImage->width - 1 : x1;
3896
3897 int yy = y*fImage->width;
3898 for (UInt_t w = 0; w < thick; w++) {
3899 for (UInt_t x = x1; x <= x2; x++) {
3900 if (y + w < fImage->height) {
3901 _alphaBlend(&fImage->alt.argb32[Idx(yy + x)], &color);
3902 }
3903 }
3904 yy += fImage->width;
3905 }
3906}
3907
3908////////////////////////////////////////////////////////////////////////////////
3909/// Draw a line.
3910
3912 const char *col, UInt_t thick)
3913{
3914 ARGB32 color = ARGB32_White;
3915 parse_argb_color(col, &color);
3916 DrawLineInternal(x1, y1, x2, y2, (UInt_t)color, thick);
3917}
3918
3919////////////////////////////////////////////////////////////////////////////////
3920/// Internal line drawing.
3921
3923 UInt_t col, UInt_t thick)
3924{
3925 int dx, dy, d;
3926 int i1, i2;
3927 int x, y, xend, yend;
3928 int xdir, ydir;
3929 int q;
3930 int idx;
3931 int yy;
3932
3933 if (!InitVisual()) {
3934 Warning("DrawLine", "Visual not initiated");
3935 return;
3936 }
3937
3938 if (!fImage) {
3939 Warning("DrawLine", "no image");
3940 return;
3941 }
3942
3943 if (!fImage->alt.argb32) {
3944 BeginPaint();
3945 }
3946
3947 if (!fImage->alt.argb32) {
3948 Warning("DrawLine", "Failed to get pixel array");
3949 return;
3950 }
3951
3952 ARGB32 color = (ARGB32)col;
3953
3954 dx = TMath::Abs(Int_t(x2) - Int_t(x1));
3955 dy = TMath::Abs(Int_t(y2) - Int_t(y1));
3956
3957 if (!dx && !dy) return; // invisible line
3958
3959 if (!dx) {
3960 DrawVLine(x1, y2 > y1 ? y1 : y2,
3961 y2 > y1 ? y2 : y1, color, thick);
3962 return;
3963 }
3964
3965 if (!dy) {
3966 DrawHLine(y1, x2 > x1 ? x1 : x2,
3967 x2 > x1 ? x2 : x1, color, thick);
3968 return;
3969 }
3970
3971 if (thick > 1) {
3972 DrawWideLine(x1, y1, x2, y2, color, thick);
3973 return;
3974 }
3975
3976 if (dy <= dx) {
3977 UInt_t ddy = dy << 1;
3978 i1 = ddy;
3979 i2 = i1 - (dx << 1);
3980 d = i1 - dx;
3981
3982 if (x1 > x2) {
3983 x = x2;
3984 y = y2;
3985 ydir = -1;
3986 xend = x1;
3987 } else {
3988 x = x1;
3989 y = y1;
3990 ydir = 1;
3991 xend = x2;
3992 }
3993
3994 yy = y*fImage->width;
3995 _alphaBlend(&fImage->alt.argb32[Idx(yy + x)], &color);
3996 q = (y2 - y1) * ydir;
3997
3998 if (q > 0) {
3999 while (x < xend) {
4000
4001 idx = Idx(yy + x);
4002 _alphaBlend(&fImage->alt.argb32[idx], &color);
4003 x++;
4004
4005 if (d >= 0) {
4006 yy += fImage->width;
4007 d += i2;
4008 } else {
4009 d += i1;
4010 }
4011 }
4012 } else {
4013 while (x < xend) {
4014 idx = Idx(yy + x);
4015 _alphaBlend(&fImage->alt.argb32[idx], &color);
4016 x++;
4017
4018 if (d >= 0) {
4019 yy -= fImage->width;
4020 d += i2;
4021 } else {
4022 d += i1;
4023 }
4024 }
4025 }
4026 } else {
4027 UInt_t ddx = dx << 1;
4028 i1 = ddx;
4029 i2 = i1 - (dy << 1);
4030 d = i1 - dy;
4031
4032 if (y1 > y2) {
4033 y = y2;
4034 x = x2;
4035 yend = y1;
4036 xdir = -1;
4037 } else {
4038 y = y1;
4039 x = x1;
4040 yend = y2;
4041 xdir = 1;
4042 }
4043
4044 yy = y*fImage->width;
4045 _alphaBlend(&fImage->alt.argb32[Idx(yy + x)], &color);
4046 q = (x2 - x1) * xdir;
4047
4048 if (q > 0) {
4049 while (y < yend) {
4050 idx = Idx(yy + x);
4051 _alphaBlend(&fImage->alt.argb32[idx], &color);
4052 y++;
4053 yy += fImage->width;
4054
4055 if (d >= 0) {
4056 x++;
4057 d += i2;
4058 } else {
4059 d += i1;
4060 }
4061 }
4062 } else {
4063 while (y < yend) {
4064 idx = Idx(yy + x);
4065 _alphaBlend(&fImage->alt.argb32[idx], &color);
4066 y++;
4067 yy += fImage->width;
4068
4069 if (d >= 0) {
4070 x--;
4071 d += i2;
4072 } else {
4073 d += i1;
4074 }
4075 }
4076 }
4077 }
4078}
4079
4080////////////////////////////////////////////////////////////////////////////////
4081/// Draw a rectangle.
4082
4084 const char *col, UInt_t thick)
4085{
4086 if (!InitVisual()) {
4087 Warning("DrawRectangle", "Visual not initiated");
4088 return;
4089 }
4090
4091 if (!fImage) {
4092 w = w ? w : 20;
4093 h = h ? h : 20;
4094 fImage = create_asimage(w, h, 0);
4095 FillRectangle(col, 0, 0, w, h);
4096 return;
4097 }
4098
4099 if (!fImage->alt.argb32) {
4100 BeginPaint();
4101 }
4102
4103 if (!fImage->alt.argb32) {
4104 Warning("DrawRectangle", "Failed to get pixel array");
4105 return;
4106 }
4107
4108 ARGB32 color = ARGB32_White;
4109 parse_argb_color(col, &color);
4110
4111 DrawHLine(y, x, x + w, (UInt_t)color, thick);
4112 DrawVLine(x + w, y, y + h, (UInt_t)color, thick);
4113 DrawHLine(y + h, x, x + w, (UInt_t)color, thick);
4114 DrawVLine(x, y, y + h, (UInt_t)color, thick);
4115 UnZoom();
4116}
4117
4118////////////////////////////////////////////////////////////////////////////////
4119/// Draw a box.
4120
4121void TASImage::DrawBox(Int_t x1, Int_t y1, Int_t x2, Int_t y2, const char *col,
4122 UInt_t thick, Int_t mode)
4123{
4124 Int_t x = TMath::Min(x1, x2);
4125 Int_t y = TMath::Min(y1, y2);
4126 Int_t w = TMath::Abs(x2 - x1);
4127 Int_t h = TMath::Abs(y2 - y1);
4128
4129 ARGB32 color = ARGB32_White;
4130
4131 if (!fImage) {
4132 w = w ? x+w : x+20;
4133 h = h ? y+h : y+20;
4134 fImage = create_asimage(w, h, 0);
4135 FillRectangle(col, 0, 0, w, h);
4136 return;
4137 }
4138
4139 if (x1 == x2) {
4140 parse_argb_color(col, &color);
4141 DrawVLine(x1, y1, y2, color, 1);
4142 return;
4143 }
4144
4145 if (y1 == y2) {
4146 parse_argb_color(col, &color);
4147 DrawHLine(y1, x1, x2, color, 1);
4148 return;
4149 }
4150
4151
4152 switch (mode) {
4153 case TVirtualX::kHollow:
4154 DrawRectangle(x, y, w, h, col, thick);
4155 break;
4156
4157 case TVirtualX::kFilled:
4158 FillRectangle(col, x, y, w, h);
4159 break;
4160
4161 default:
4162 FillRectangle(col, x, y, w, h);
4163 break;
4164 }
4165}
4166
4167////////////////////////////////////////////////////////////////////////////////
4168/// Draw a dashed horizontal line.
4169
4171 const char *pDash, UInt_t col, UInt_t thick)
4172{
4173 UInt_t iDash = 0; // index of current dash
4174 int i = 0;
4175
4176 ARGB32 color = (ARGB32)col;
4177
4178 UInt_t half = 0;
4179
4180 if (thick > 1) {
4181 half = thick >> 1;
4182 if (y > half) {
4183 y = y - half;
4184 } else {
4185 y = 0;
4186 thick += (y - half);
4187 }
4188 }
4189 thick = thick <= 0 ? 1 : thick;
4190
4191 y = y + thick >= fImage->height ? fImage->height - thick - 1 : y;
4192 x2 = x2 >= fImage->width ? fImage->width - 1 : x2;
4193 x1 = x1 >= fImage->width ? fImage->width - 1 : x1;
4194
4195 // switch x1, x2
4196 UInt_t tmp = x1;
4197 x1 = x2 < x1 ? x2 : x1;
4198 x2 = x2 < tmp ? tmp : x2;
4199
4200 for (UInt_t x = x1; x <= x2; x++) {
4201 for (UInt_t w = 0; w < thick; w++) {
4202 if (y + w < fImage->height) {
4203 if ((iDash%2)==0) {
4204 _alphaBlend(&fImage->alt.argb32[Idx((y + w)*fImage->width + x)], &color);
4205 }
4206 }
4207 }
4208 i++;
4209
4210 if (i >= pDash[iDash]) {
4211 iDash++;
4212 i = 0;
4213 }
4214 if (iDash >= nDash) {
4215 iDash = 0;
4216 i = 0;
4217 }
4218 }
4219}
4220
4221////////////////////////////////////////////////////////////////////////////////
4222/// Draw a dashed vertical line.
4223
4225 const char *pDash, UInt_t col, UInt_t thick)
4226{
4227 UInt_t iDash = 0; // index of current dash
4228 int i = 0;
4229
4230 ARGB32 color = (ARGB32)col;
4231
4232 UInt_t half = 0;
4233
4234 if (thick > 1) {
4235 half = thick >> 1;
4236 if (x > half) {
4237 x = x - half;
4238 } else {
4239 x = 0;
4240 thick += (x - half);
4241 }
4242 }
4243 thick = thick <= 0 ? 1 : thick;
4244
4245 y2 = y2 >= fImage->height ? fImage->height - 1 : y2;
4246 y1 = y1 >= fImage->height ? fImage->height - 1 : y1;
4247
4248 // switch x1, x2
4249 UInt_t tmp = y1;
4250 y1 = y2 < y1 ? y2 : y1;
4251 y2 = y2 < tmp ? tmp : y2;
4252
4253 x = x + thick >= fImage->width ? fImage->width - thick - 1 : x;
4254
4255 int yy = y1*fImage->width;
4256 for (UInt_t y = y1; y <= y2; y++) {
4257 for (UInt_t w = 0; w < thick; w++) {
4258 if (x + w < fImage->width) {
4259 if ((iDash%2)==0) {
4260 _alphaBlend(&fImage->alt.argb32[Idx(yy + (x + w))], &color);
4261 }
4262 }
4263 }
4264 i++;
4265
4266 if (i >= pDash[iDash]) {
4267 iDash++;
4268 i = 0;
4269 }
4270 if (iDash >= nDash) {
4271 iDash = 0;
4272 i = 0;
4273 }
4274 yy += fImage->width;
4275 }
4276}
4277
4278////////////////////////////////////////////////////////////////////////////////
4279/// Draw a dashed line with one pixel width.
4280
4282 UInt_t nDash, const char *tDash, UInt_t color)
4283{
4284 int dx, dy, d;
4285 int i, i1, i2;
4286 int x, y, xend, yend;
4287 int xdir, ydir;
4288 int q;
4289 UInt_t iDash = 0; // index of current dash
4290 int yy;
4291 int idx;
4292
4293 dx = TMath::Abs(Int_t(x2) - Int_t(x1));
4294 dy = TMath::Abs(Int_t(y2) - Int_t(y1));
4295
4296 char *pDash = new char[nDash];
4297
4298 if (dy <= dx) {
4299 double ac = TMath::Cos(TMath::ATan2(dy, dx));
4300
4301 for (i = 0; i < (int)nDash; i++) {
4302 pDash[i] = TMath::Nint(tDash[i] * ac);
4303 }
4304
4305 UInt_t ddy = dy << 1;
4306 i1 = ddy;
4307 i2 = i1 - (dx << 1);
4308 d = i1 - dx;
4309 i = 0;
4310
4311 if (x1 > x2) {
4312 x = x2;
4313 y = y2;
4314 ydir = -1;
4315 xend = x1;
4316 } else {
4317 x = x1;
4318 y = y1;
4319 ydir = 1;
4320 xend = x2;
4321 }
4322
4323 yy = y*fImage->width;
4324 _alphaBlend(&fImage->alt.argb32[Idx(y*fImage->width + x)], &color);
4325 q = (y2 - y1) * ydir;
4326
4327 if (q > 0) {
4328 while (x < xend) {
4329 idx = Idx(yy + x);
4330 if ((iDash%2) == 0) {
4331 _alphaBlend(&fImage->alt.argb32[idx], &color);
4332 }
4333 x++;
4334 if (d >= 0) {
4335 yy += fImage->width;
4336 d += i2;
4337 } else {
4338 d += i1;
4339 }
4340
4341 i++;
4342 if (i >= pDash[iDash]) {
4343 iDash++;
4344 i = 0;
4345 }
4346 if (iDash >= nDash) {
4347 iDash = 0;
4348 i = 0;
4349 }
4350 }
4351 } else {
4352 while (x < xend) {
4353 idx = Idx(yy + x);
4354 if ((iDash%2) == 0) {
4355 _alphaBlend(&fImage->alt.argb32[idx], &color);
4356 }
4357 x++;
4358 if (d >= 0) {
4359 yy -= fImage->width;
4360 d += i2;
4361 } else {
4362 d += i1;
4363 }
4364
4365 i++;
4366 if (i >= pDash[iDash]) {
4367 iDash++;
4368 i = 0;
4369 }
4370 if (iDash >= nDash) {
4371 iDash = 0;
4372 i = 0;
4373 }
4374 }
4375 }
4376 } else {
4377 double as = TMath::Sin(TMath::ATan2(dy, dx));
4378
4379 for (i = 0; i < (int)nDash; i++) {
4380 pDash[i] = TMath::Nint(tDash[i] * as);
4381 }
4382
4383 UInt_t ddx = dx << 1;
4384 i1 = ddx;
4385 i2 = i1 - (dy << 1);
4386 d = i1 - dy;
4387 i = 0;
4388
4389 if (y1 > y2) {
4390 y = y2;
4391 x = x2;
4392 yend = y1;
4393 xdir = -1;
4394 } else {
4395 y = y1;
4396 x = x1;
4397 yend = y2;
4398 xdir = 1;
4399 }
4400
4401 yy = y*fImage->width;
4402 _alphaBlend(&fImage->alt.argb32[Idx(y*fImage->width + x)], &color);
4403 q = (x2 - x1) * xdir;
4404
4405 if (q > 0) {
4406 while (y < yend) {
4407 idx = Idx(yy + x);
4408 if ((iDash%2) == 0) {
4409 _alphaBlend(&fImage->alt.argb32[idx], &color);
4410 }
4411 y++;
4412 yy += fImage->width;
4413
4414 if (d >= 0) {
4415 x++;
4416 d += i2;
4417 } else {
4418 d += i1;
4419 }
4420
4421 i++;
4422 if (i >= pDash[iDash]) {
4423 iDash++;
4424 i = 0;
4425 }
4426 if (iDash >= nDash) {
4427 iDash = 0;
4428 i = 0;
4429 }
4430 }
4431 } else {
4432 while (y < yend) {
4433 idx = Idx(yy + x);
4434 if ((iDash%2) == 0) {
4435 _alphaBlend(&fImage->alt.argb32[idx], &color);
4436 }
4437 y++;
4438 yy += fImage->width;
4439
4440 if (d >= 0) {
4441 x--;
4442 d += i2;
4443 } else {
4444 d += i1;
4445 }
4446
4447 i++;
4448 if (i >= pDash[iDash]) {
4449 iDash++;
4450 i = 0;
4451 }
4452 if (iDash >= nDash) {
4453 iDash = 0;
4454 i = 0;
4455 }
4456 }
4457 }
4458 }
4459 delete [] pDash;
4460}
4461
4462////////////////////////////////////////////////////////////////////////////////
4463/// Draw a dashed line with thick pixel width.
4464
4466 UInt_t nDash, const char *tDash, UInt_t color, UInt_t thick)
4467{
4468 int dx, dy;
4469 int i;
4470 double x, y, xend=0, yend=0, x0, y0;
4471 int xdir, ydir;
4472 int q;
4473 UInt_t iDash = 0; // index of current dash
4474
4475 dx = TMath::Abs(Int_t(x2) - Int_t(x1));
4476 dy = TMath::Abs(Int_t(y2) - Int_t(y1));
4477
4478 double *xDash = new double[nDash];
4479 double *yDash = new double[nDash];
4480 double a = TMath::ATan2(dy, dx);
4481 double ac = TMath::Cos(a);
4482 double as = TMath::Sin(a);
4483
4484 for (i = 0; i < (int)nDash; i++) {
4485 xDash[i] = tDash[i] * ac;
4486 yDash[i] = tDash[i] * as;
4487
4488 // dirty trick (must be fixed)
4489 if ((i%2) == 0) {
4490 xDash[i] = xDash[i]/2;
4491 yDash[i] = yDash[i]/2;
4492 } else {
4493 xDash[i] = xDash[i]*2;
4494 yDash[i] = yDash[i]*2;
4495 }
4496 }
4497
4498 if (dy <= dx) {
4499 if (x1 > x2) {
4500 x = x2;
4501 y = y2;
4502 ydir = -1;
4503 xend = x1;
4504 } else {
4505 x = x1;
4506 y = y1;
4507 ydir = 1;
4508 xend = x2;
4509 }
4510
4511 q = (y2 - y1) * ydir;
4512 x0 = x;
4513 y0 = y;
4514 iDash = 0;
4515 yend = y + q;
4516
4517 if (q > 0) {
4518 while ((x < xend) && (y < yend)) {
4519 x += xDash[iDash];
4520 y += yDash[iDash];
4521
4522 if ((iDash%2) == 0) {
4524 TMath::Nint(x), TMath::Nint(y), color, thick);
4525 } else {
4526 x0 = x;
4527 y0 = y;
4528 }
4529
4530 iDash++;
4531
4532 if (iDash >= nDash) {
4533 iDash = 0;
4534 }
4535 }
4536 } else {
4537 while ((x < xend) && (y > yend)) {
4538 x += xDash[iDash];
4539 y -= yDash[iDash];
4540
4541 if ((iDash%2) == 0) {
4543 TMath::Nint(x), TMath::Nint(y), color, thick);
4544 } else {
4545 x0 = x;
4546 y0 = y;
4547 }
4548
4549 iDash++;
4550
4551 if (iDash >= nDash) {
4552 iDash = 0;
4553 }
4554 }
4555 }
4556 } else {
4557
4558 if (y1 > y2) {
4559 y = y2;
4560 x = x2;
4561 yend = y1;
4562 xdir = -1;
4563 } else {
4564 y = y1;
4565 x = x1;
4566 yend = y2;
4567 xdir = 1;
4568 }
4569
4570 q = (x2 - x1) * xdir;
4571 x0 = x;
4572 y0 = y;
4573 iDash = 0;
4574 xend = x + q;
4575
4576 if (q > 0) {
4577 while ((x < xend) && (y < yend)) {
4578 x += xDash[iDash];
4579 y += yDash[iDash];
4580
4581 if ((iDash%2) == 0) {
4583 TMath::Nint(x), TMath::Nint(y), color, thick);
4584 } else {
4585 x0 = x;
4586 y0 = y;
4587 }
4588
4589 iDash++;
4590
4591 if (iDash >= nDash) {
4592 iDash = 0;
4593 }
4594 }
4595 } else {
4596 while ((x > xend) && (y < yend)) {
4597 x -= xDash[iDash];
4598 y += yDash[iDash];
4599
4600 if ((iDash%2) == 0) {
4602 TMath::Nint(x), TMath::Nint(y), color, thick);
4603 } else {
4604 x0 = x;
4605 y0 = y;
4606 }
4607
4608 iDash++;
4609
4610 if (iDash >= nDash) {
4611 iDash = 0;
4612 }
4613 }
4614 }
4615 }
4616 delete [] xDash;
4617 delete [] yDash;
4618}
4619
4620////////////////////////////////////////////////////////////////////////////////
4621/// Draw a dashed line.
4622
4624 const char *pDash, const char *col, UInt_t thick)
4625
4626{
4627 if (!InitVisual()) {
4628 Warning("DrawDashLine", "Visual not initiated");
4629 return;
4630 }
4631
4632 if (!fImage) {
4633 Warning("DrawDashLine", "no image");
4634 return;
4635 }
4636
4637 if (!fImage->alt.argb32) {
4638 BeginPaint();
4639 }
4640
4641 if (!fImage->alt.argb32) {
4642 Warning("DrawDashLine", "Failed to get pixel array");
4643 return;
4644 }
4645
4646 if ((nDash < 2) || !pDash || (nDash%2)) {
4647 Warning("DrawDashLine", "Wrong input parameters n=%d %ld", nDash, (Long_t)sizeof(pDash)-1);
4648 return;
4649 }
4650
4651 ARGB32 color = ARGB32_White;
4652 parse_argb_color(col, &color);
4653
4654 if (x1 == x2) {
4655 DrawDashVLine(x1, y1, y2, nDash, pDash, (UInt_t)color, thick);
4656 } else if (y1 == y2) {
4657 DrawDashHLine(y1, x1, x2, nDash, pDash, (UInt_t)color, thick);
4658 } else {
4659 if (thick < 2) DrawDashZLine(x1, y1, x2, y2, nDash, pDash, (UInt_t)color);
4660 else DrawDashZTLine(x1, y1, x2, y2, nDash, pDash, (UInt_t)color, thick);
4661 }
4662}
4663
4664////////////////////////////////////////////////////////////////////////////////
4665/// Draw a polyline.
4666
4667void TASImage::DrawPolyLine(UInt_t nn, TPoint *xy, const char *col, UInt_t thick,
4669{
4670 ARGB32 color = ARGB32_White;
4671 parse_argb_color(col, &color);
4672
4673 Int_t x0 = xy[0].GetX();
4674 Int_t y0 = xy[0].GetY();
4675 Int_t x = 0;
4676 Int_t y = 0;
4677
4678 for (UInt_t i = 1; i < nn; i++) {
4679 x = (mode == kCoordModePrevious) ? x + xy[i].GetX() : xy[i].GetX();
4680 y = (mode == kCoordModePrevious) ? y + xy[i].GetY() : xy[i].GetY();
4681
4682 DrawLineInternal(x0, y0, x, y, (UInt_t)color, thick);
4683
4684 x0 = x;
4685 y0 = y;
4686 }
4687}
4688
4689////////////////////////////////////////////////////////////////////////////////
4690/// Draw a point at the specified position.
4691
4692void TASImage::PutPixel(Int_t x, Int_t y, const char *col)
4693{
4694 if (!InitVisual()) {
4695 Warning("PutPixel", "Visual not initiated");
4696 return;
4697 }
4698
4699 if (!fImage) {
4700 Warning("PutPixel", "no image");
4701 return;
4702 }
4703
4704 if (!fImage->alt.argb32) {
4705 BeginPaint();
4706 }
4707
4708 if (!fImage->alt.argb32) {
4709 Warning("PutPixel", "Failed to get pixel array");
4710 return;
4711 }
4712
4713 ARGB32 color;
4714 parse_argb_color(col, &color);
4715
4716 if ((x < 0) || (y < 0) || (x >= (int)fImage->width) || (y >= (int)fImage->height)) {
4717 Warning("PutPixel", "Out of range width=%d x=%d, height=%d y=%d",
4718 fImage->width, x, fImage->height, y);
4719 return;
4720 }
4721 _alphaBlend(&fImage->alt.argb32[Idx(y*fImage->width + x)], &color);
4722}
4723
4724////////////////////////////////////////////////////////////////////////////////
4725/// Draw a poly point.
4726
4728{
4729 if (!InitVisual()) {
4730 Warning("PolyPoint", "Visual not initiated");
4731 return;
4732 }
4733
4734 if (!fImage) {
4735 Warning("PolyPoint", "no image");
4736 return;
4737 }
4738
4739 if (!fImage->alt.argb32) {
4740 BeginPaint();
4741 }
4742
4743 if (!fImage->alt.argb32) {
4744 Warning("PolyPoint", "Failed to get pixel array");
4745 return;
4746 }
4747
4748 if (!npt || !ppt) {
4749 Warning("PolyPoint", "No points specified");
4750 return;
4751 }
4752
4753 TPoint *ipt = 0;
4754 UInt_t i = 0;
4755 ARGB32 color;
4756 parse_argb_color(col, &color);
4757
4758 //make pointlist origin relative
4759 if (mode == kCoordModePrevious) {
4760 ipt = new TPoint[npt];
4761
4762 for (i = 0; i < npt; i++) {
4763 ipt[i].fX += ppt[i].fX;
4764 ipt[i].fY += ppt[i].fY;
4765 }
4766 }
4767 int x, y;
4768
4769 for (i = 0; i < npt; i++) {
4770 x = ipt ? ipt[i].fX : ppt[i].fX;
4771 y = ipt ? ipt[i].fY : ppt[i].fY;
4772
4773 if ((x < 0) || (y < 0) || (x >= (int)fImage->width) || (y >= (int)fImage->height)) {
4774 continue;
4775 }
4776 _alphaBlend(&fImage->alt.argb32[Idx(y*fImage->width + x)], &color);
4777 }
4778
4779 if (ipt) {
4780 delete [] ipt;
4781 }
4782}
4783
4784////////////////////////////////////////////////////////////////////////////////
4785/// Draw segments.
4786
4787void TASImage::DrawSegments(UInt_t nseg, Segment_t *seg, const char *col, UInt_t thick)
4788{
4789 if (!nseg || !seg) {
4790 Warning("DrawSegments", "Invalid data nseg=%d seg=0x%zx", nseg, (size_t)seg);
4791 return;
4792 }
4793
4794 TPoint pt[2];
4795
4796 for (UInt_t i = 0; i < nseg; i++) {
4797 pt[0].fX = seg->fX1;
4798 pt[1].fX = seg->fX2;
4799 pt[0].fY = seg->fY1;
4800 pt[1].fY = seg->fY2;
4801
4802 DrawPolyLine(2, pt, col, thick, kCoordModeOrigin);
4803 seg++;
4804 }
4805}
4806
4807////////////////////////////////////////////////////////////////////////////////
4808/// Fill spans with specified color or/and stipple.
4809
4810void TASImage::FillSpans(UInt_t npt, TPoint *ppt, UInt_t *widths, const char *col,
4811 const char *stipple, UInt_t w, UInt_t h)
4812{
4813 if (!InitVisual()) {
4814 Warning("FillSpans", "Visual not initiated");
4815 return;
4816 }
4817
4818 if (!fImage) {
4819 Warning("FillSpans", "no image");
4820 return;
4821 }
4822
4823 if (!fImage->alt.argb32) {
4824 BeginPaint();
4825 }
4826
4827 if (!fImage->alt.argb32) {
4828 Warning("FillSpans", "Failed to get pixel array");
4829 return;
4830 }
4831
4832 if (!npt || !ppt || !widths || (stipple && (!w || !h))) {
4833 Warning("FillSpans", "Invalid input data npt=%d ppt=0x%zx col=%s widths=0x%zx stipple=0x%zx w=%d h=%d",
4834 npt, (size_t)ppt, col, (size_t)widths, (size_t)stipple, w, h);
4835 return;
4836 }
4837
4838 ARGB32 color;
4839 parse_argb_color(col, &color);
4840 Int_t idx = 0;
4841 UInt_t x = 0;
4842 UInt_t yy;
4843
4844 for (UInt_t i = 0; i < npt; i++) {
4845 yy = ppt[i].fY*fImage->width;
4846 for (UInt_t j = 0; j < widths[i]; j++) {
4847 if ((ppt[i].fX >= (Int_t)fImage->width) || (ppt[i].fX < 0) ||
4848 (ppt[i].fY >= (Int_t)fImage->height) || (ppt[i].fY < 0)) continue;
4849
4850 x = ppt[i].fX + j;
4851 idx = Idx(yy + x);
4852
4853 if (!stipple) {
4854 _alphaBlend(&fImage->alt.argb32[idx], &color);
4855 } else {
4856 Int_t ii = (ppt[i].fY%h)*w + x%w;
4857
4858 if (stipple[ii >> 3] & (1 << (ii%8))) {
4859 _alphaBlend(&fImage->alt.argb32[idx], &color);
4860 }
4861 }
4862 }
4863 }
4864}
4865
4866////////////////////////////////////////////////////////////////////////////////
4867/// Fill spans with tile image.
4868
4869void TASImage::FillSpans(UInt_t npt, TPoint *ppt, UInt_t *widths, TImage *tile)
4870{
4871 if (!InitVisual()) {
4872 Warning("FillSpans", "Visual not initiated");
4873 return;
4874 }
4875
4876 if (!fImage) {
4877 Warning("FillSpans", "no image");
4878 return;
4879 }
4880
4881 if (!fImage->alt.argb32) {
4882 BeginPaint();
4883 }
4884
4885 if (!fImage->alt.argb32) {
4886 Warning("FillSpans", "Failed to get pixel array");
4887 return;
4888 }
4889
4890 if (!npt || !ppt || !widths || !tile) {
4891 Warning("FillSpans", "Invalid input data npt=%d ppt=0x%zx widths=0x%zx tile=0x%zx",
4892 npt, (size_t)ppt, (size_t)widths, (size_t)tile);
4893 return;
4894 }
4895
4896 Int_t idx = 0;
4897 Int_t ii = 0;
4898 UInt_t x = 0;
4899 UInt_t *arr = tile->GetArgbArray();
4900 if (!arr) return;
4901 UInt_t xx = 0;
4902 UInt_t yy = 0;
4903
4904 for (UInt_t i = 0; i < npt; i++) {
4905 UInt_t yyy = ppt[i].fY*fImage->width;
4906
4907 for (UInt_t j = 0; j < widths[i]; j++) {
4908 if ((ppt[i].fX >= (Int_t)fImage->width) || (ppt[i].fX < 0) ||
4909 (ppt[i].fY >= (Int_t)fImage->height) || (ppt[i].fY < 0)) continue;
4910 x = ppt[i].fX + j;
4911 idx = Idx(yyy + x);
4912 xx = x%tile->GetWidth();
4913 yy = ppt[i].fY%tile->GetHeight();
4914 ii = yy*tile->GetWidth() + xx;
4915 _alphaBlend(&fImage->alt.argb32[idx], &arr[ii]);
4916 }
4917 }
4918}
4919
4920////////////////////////////////////////////////////////////////////////////////
4921/// Crop spans.
4922
4923void TASImage::CropSpans(UInt_t npt, TPoint *ppt, UInt_t *widths)
4924{
4925 if (!InitVisual()) {
4926 Warning("CropSpans", "Visual not initiated");
4927 return;
4928 }
4929
4930 if (!fImage) {
4931 Warning("CropSpans", "no image");
4932 return;
4933 }
4934
4935 if (!fImage->alt.argb32) {
4936 BeginPaint();
4937 }
4938
4939 if (!fImage->alt.argb32) {
4940 Warning("CropSpans", "Failed to get pixel array");
4941 return;
4942 }
4943
4944 if (!npt || !ppt || !widths) {
4945 Warning("CropSpans", "No points specified npt=%d ppt=0x%zx widths=0x%zx", npt, (size_t)ppt, (size_t)widths);
4946 return;
4947 }
4948
4949 int y0 = ppt[0].fY;
4950 int y1 = ppt[npt-1].fY;
4951 UInt_t y = 0;
4952 UInt_t x = 0;
4953 UInt_t i = 0;
4954 UInt_t idx = 0;
4955 UInt_t sz = fImage->width*fImage->height;
4956 UInt_t yy = y*fImage->width;
4957
4958 for (y = 0; (int)y < y0; y++) {
4959 for (x = 0; x < fImage->width; x++) {
4960 idx = Idx(yy + x);
4961