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 = nullptr;
124
125static ASFontManager *gFontManager = nullptr;
126static unsigned long kAllPlanes = ~0;
128
129// default icon paths
130static char *gIconPaths[7] = {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
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 = nullptr;
192 fImage = nullptr;
193}
194
195////////////////////////////////////////////////////////////////////////////////
196/// Set default parameters.
197
199{
200 fImage = nullptr;
201 fScaledImage = nullptr;
202 fMaxValue = 1;
203 fMinValue = 0;
205 fPaintMode = 1;
206 fZoomOffX = 0;
207 fZoomOffY = 0;
208 fZoomWidth = 0;
209 fZoomHeight = 0;
211
212 fGrayImage = nullptr;
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);
300 fScaledImage = fScaledImage ? (TASImage*)img.fScaledImage->Clone("") : nullptr;
301 fGrayImage = fGrayImage ? clone_asimage(img.fGrayImage, SCL_DO_ALL) : nullptr;
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);
330 fScaledImage = fScaledImage ? (TASImage*)img.fScaledImage->Clone("") : nullptr;
331 fGrayImage = fGrayImage ? clone_asimage(img.fGrayImage, SCL_DO_ALL) : nullptr;
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("") : nullptr;
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 = nullptr;
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] = nullptr;
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 nullptr;
407
408 if (!fread(&magic, 1, 1, fp)) {
409 fclose(fp);
410 return nullptr;
411 }
412
413 switch (magic) {
414 case 0x00:
415 {
416 if (!fread(&magic, 1, 1, fp)) {
417 fclose(fp);
418 return nullptr;
419 }
420 if (!fread(&magic, 1, 1, fp)) {
421 fclose(fp);
422 return nullptr;
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 nullptr;
433 }
434
435 if (magic == 0x21)
436 ret = "ps";
437 else if (magic == 0x50)
438 ret = "pdf";
439 break;
440 }
441 case 0x42:
442 ret = "bmp";
443 break;
444 case 0x47:
445 ret = "gif";
446 break;
447 case 0x54:
448 ret = "tga";
449 break;
450 case 0x49:
451 ret = "tiff";
452 break;
453 case 0x89:
454 ret = "png";
455 break;
456 case 0xff:
457 ret = "jpg";
458 break;
459 default:
460 ret = "";
461 }
462
463 fclose(fp);
464 return ret;
465}
466
467////////////////////////////////////////////////////////////////////////////////
468/// Read specified image file.
469/// The file type is determined by the file extension (the type argument is
470/// ignored). It will attempt to append .gz and then .Z to the filename and
471/// find such a file. If the filename ends with extension consisting of digits
472/// only, it will attempt to find the file with this extension stripped
473/// off. On success this extension will be used to load subimage from
474/// the file with that number. Subimage is supported for GIF files
475/// (ICO, BMP, CUR, TIFF, XCF to be supported in future).
476/// For example,
477/// ~~~ {.cpp}
478/// i1 = TImage::Open("anim.gif.0"); // read the first subimage
479/// i4 = TImage::Open("anim.gif.3"); // read the forth subimage
480/// ~~~
481/// It is also possible to put XPM raw string (see also SetImageBuffer) as
482/// the first input parameter ("filename"), such string is returned by
483/// GetImageBuffer method.
484
486{
487 if (!InitVisual()) {
488 Warning("Scale", "Visual not initiated");
489 return;
490 }
491
492 Bool_t xpm = filename && (filename[0] == '/' &&
493 filename[1] == '*') && filename[2] == ' ';
494
495 if (xpm) { // XPM strings in-memory array
497 fName = "XPM_image";
498 return;
499 }
500
501 if (!gIconPaths[0]) {
503 }
504 // suppress the "root : looking for image ..." messages
505 set_output_threshold(0);
506
507 static ASImageImportParams iparams;
508 iparams.flags = 0;
509 iparams.width = 0;
510 iparams.height = 0;
511 iparams.filter = SCL_DO_ALL;
512 iparams.gamma = SCREEN_GAMMA;
513 iparams.gamma_table = NULL;
514 iparams.compression = GetImageCompression();
515 iparams.format = ASA_ASImage;
516 iparams.search_path = gIconPaths;
517 iparams.subimage = 0;
518 iparams.return_animation_delay = -1;
519
520 TString ext;
521 const char *dot;
522 if (filename) dot = strrchr(filename, '.');
523 else dot = nullptr;
524 ASImage *image = nullptr;
525 TString fname = filename;
526
527 if (!dot) {
529 else ext = dot + 1;
530 } else {
531 ext = dot + 1;
532 }
533
534 if (!ext.IsNull() && ext.IsDigit()) { // read subimage
535 iparams.subimage = ext.Atoi();
536 fname = fname(0, fname.Length() - ext.Length() - 1);
537 ext = strrchr(fname.Data(), '.') + 1;
538 }
539
540 image = file2ASImage_extra(fname.Data(), &iparams);
541
542 if (image) { // it's OK
543 goto end;
544 } else { // try to read it via plugin
545 if (ext.IsNull()) {
546 return;
547 }
548 ext.ToLower();
549 ext.Strip();
550 UInt_t w = 0;
551 UInt_t h = 0;
552 unsigned char *bitmap = nullptr;
553
555
556 if (!plug) {
557 TPluginHandler *handler = gROOT->GetPluginManager()->FindHandler("TImagePlugin", ext);
558 if (!handler || ((handler->LoadPlugin() == -1))) {
559 return;
560 }
561 plug = (TImagePlugin*)handler->ExecPlugin(1, ext.Data());
562
563 if (!plug) {
564 return;
565 }
566
567 fgPlugList->Add(plug);
568 }
569
570 if (plug) {
571 if (plug->InheritsFrom(TASImagePlugin::Class())) {
572 image = ((TASImagePlugin*)plug)->File2ASImage(fname.Data());
573 if (image) goto end;
574 }
575 bitmap = plug->ReadFile(fname.Data(), w, h);
576 if (bitmap) {
577 image = bitmap2asimage(bitmap, w, h, 0, nullptr);
578 }
579 if (!image) {
580 return;
581 }
582 }
583 }
584
585end:
586 fName.Form("%s.", gSystem->BaseName(fname.Data()));
587
588 DestroyImage();
589 delete fScaledImage;
590 fScaledImage = nullptr;
591
592 fImage = image;
595 fZoomOffX = 0;
596 fZoomOffY = 0;
597 fZoomWidth = fImage->width;
598 fZoomHeight = fImage->height;
599 fPaintMode = 1;
600}
601
602////////////////////////////////////////////////////////////////////////////////
603/// Write image to specified file.
604///
605/// If there is no file extension or if the file extension is unknown, the
606/// type argument will be used to determine the file type. The quality and
607/// compression is derived from the TAttImage values.
608///
609/// It's possible to write image into an animated GIF file by specifying file
610/// name as "myfile.gif+" or "myfile.gif+NN", where NN is the delay of displaying
611/// subimages during animation in 10ms seconds units. NN is not restricted
612/// to two digits. If NN is omitted the delay between subimages is zero.
613/// For an animation that stops after last subimage is reached, one has to
614/// write the last image as .gif+ (zero delay of last image) or .gif+NN
615/// (NN*10ms delay of last image).
616///
617/// For repeated animation (looping), the last subimage must be specified as:
618/// - "myfile.gif++NN++" if you want an infinite looping gif with NN*10ms
619/// delay of the last image.
620/// - "myfile.gif++" for an infinite loop with zero delay of last image.
621/// - "myfile.gif+NN++RR" if you want a finite looping gif with NN*10ms
622/// delay of the last image and the animation to be stopped after RR
623/// repeats. RR is not restricted to two digits.
624///
625/// A deprecated version for saving the last subimage of a looping gif animation is:
626/// - "myfile.gif++NN" for a finite loop where NN is number of repetitions
627/// and NN*10ms the delay of last image. (No separate control of repeats and delay).
628/// Note: If the file "myfile.gif" already exists, the new frames are appended at
629/// the end of the file. To avoid this, delete it first with gSystem->Unlink(myfile.gif);
630///
631/// The following macro creates animated gif from jpeg images with names
632/// - imageNN.jpg, where 1<= NN <= 10
633/// - The delays are set to 10*10ms.
634/// ~~~ {.cpp}
635/// {
636/// TImage *img = 0;
637/// gSystem->Unlink("anim.gif"); // delete existing file
638///
639/// for (int i = 1; i <= 10; i++) {
640/// delete img; // delete previous image
641///
642/// // Read image data. Image can be in any format, e.g. png, gif, etc.
643/// img = TImage::Open(Form("image%d.jpg", i));
644///
645/// if (i < 10) {
646/// img->WriteImage("anim.gif+10"); // 10 centiseconds delay
647/// } else { // the last image written. "++" stands for infinit animation.
648/// img->WriteImage("anim.gif++10++"); // 10 centiseconds delay of last image
649/// }
650/// }
651/// }
652/// ~~~
653
655{
656 if (!IsValid()) {
657 Error("WriteImage", "no image in memory. Draw something first");
658 return;
659 }
660
661 if (!file || !*file) {
662 Error("WriteImage", "no file name specified");
663 return;
664 }
665
666 const char *s;
667 if ((s = strrchr(file, '.'))) {
668 s++;
670 if (t == kUnknown) {
671 Error("WriteImage", "cannot determine a valid file type");
672 return;
673 }
674 if (t != kUnknown)
675 type = t;
676 }
677
678 if (type == kUnknown) {
679 Error("WriteImage", "not a valid file type was specified");
680 return;
681 }
682
683 UInt_t mytype;
684 MapFileTypes(type, mytype);
685 ASImageFileTypes atype = (ASImageFileTypes)mytype;
686
687 UInt_t aquality;
688 EImageQuality quality = GetImageQuality();
689 MapQuality(quality, aquality);
690
691 static TString fname;
692 fname = file;
693 static ASImageExportParams parms;
694 ASImage *im = fScaledImage ? fScaledImage->fImage : fImage;
695
696 switch (type) {
697 case kXpm:
698 parms.xpm.type = atype;
699 parms.xpm.flags = EXPORT_ALPHA;
700 parms.xpm.dither = 4;
701 parms.xpm.opaque_threshold = 127;
702 parms.xpm.max_colors = 512;
703 break;
704 case kBmp:
705 ASImage2bmp(im, fname.Data(), nullptr);
706 return;
707 case kXcf:
708 ASImage2xcf(im, fname.Data(), nullptr);
709 return;
710 case kPng:
711 parms.png.type = atype;
712 parms.png.flags = EXPORT_ALPHA;
713 parms.png.compression = !GetImageCompression() ? -1 : int(GetImageCompression());
714 break;
715 case kJpeg:
716 parms.jpeg.type = atype;
717 parms.jpeg.flags = 0;
718 parms.jpeg.quality = aquality;
719 break;
720 case kGif:
721 parms.gif.type = atype;
722 parms.gif.flags = EXPORT_ALPHA;
723 parms.gif.dither = 0;
724 parms.gif.opaque_threshold = 0;
725 break;
726 case kAnimGif:
727 {
728 parms.gif.type = atype;
729 parms.gif.flags = EXPORT_ALPHA | EXPORT_APPEND;
730 parms.gif.dither = 0;
731 parms.gif.opaque_threshold = 0;
732 parms.gif.animate_repeats = 0;
733
734 s += 4; // skip "gif+"
735 int delay = 0;
736
737 const TString sufix = s; // we denote as suffix as everything that is after .gif+
738 const UInt_t sLength = sufix.Length();
739
740 if (sufix=="+") {
741 // .gif++ implies that this is the last image of the animation
742 // and that the gif will loop forever (infinite animation)
743 // and that the delay of last image is 0ms (backward compatibility reasons)
744 delay = 0;
745 parms.gif.flags |= EXPORT_ANIMATION_REPEATS;// activate repetition
746 parms.gif.animate_repeats = 0;// 0 is code for looping forever (if EXPORT_ANIMATION_REPEATS is also set)
747 } else if(sufix=="") {
748 // .gif+ implies that this is a subimage of the animation with zero delay
749 // or the last image of an animation that will not repeat.
750 // Last image delay is zero because atoi("")=0.
751 delay = atoi(s);
752 //Nothing else needed here
753 } else if(!sufix.Contains("+")) {
754 // .gif+NN implies that this is a subimage of the animation
755 // with NN*10ms delay (latency) until the next one.
756 // You can also use this option on the last image if you do not want the gif to replay
757 delay = atoi(s);
758 //Nothing else needed here
759 } else if(sLength>1 && sufix.BeginsWith("+") && sufix.CountChar('+')==1) {
760 // .gif++NN implies that this is the last image of the animation
761 // and that it will loop NN number of times (finite animation)
762 // and that the delay of last image is NN*10ms (backward compatibility reasons).
763 delay = atoi(s);// atoi is smart enough to ignore the "+" sign before.
764 parms.gif.flags |= EXPORT_ANIMATION_REPEATS;// activate repetition
765 parms.gif.animate_repeats = atoi(s);// loops only NN times, then it stops. atoi discards + sign.
766 } else if(sLength>3 && sufix.BeginsWith("+") && sufix.EndsWith("++") && !TString(sufix(1,sLength-3)).Contains("+")) {
767 // .gif++NN++ implies that this is the last image of the animation
768 // and that the gif will loop forever (infinite animation)
769 // and that the delay of last image is NN*10ms.
770 // In contrast, .gif++ is an infinite loop but with 0 delay, whereas the option
771 // .gif++NN is a loop repeated NN times (not infinite) with NN*10ms delay
772 // between last and first loop images.
773 delay = atoi(s);// atoi discards the three plus signs
774 parms.gif.flags |= EXPORT_ANIMATION_REPEATS;// activate repetition
775 parms.gif.animate_repeats = 0;// 0 is code for looping forever (if EXPORT_ANIMATION_REPEATS is also set)
776 } else if(sLength>3 && sufix.CountChar('+')==2 && TString(sufix(1,sLength-2)).Contains("++")) {
777 // .gif+NN++RR implies that this is the last image animation
778 // and that the gif will loop RR number of times (finite animation)
779 // and that the delay of last image is NN*10ms.
780 const TString sDelay = sufix(0,sufix.First('+'));
781 const TString sRepeats = sufix(sufix.First('+')+2,sLength-(sufix.First('+')+2));
782 delay = atoi(sDelay);
783 parms.gif.flags |= EXPORT_ANIMATION_REPEATS;// activate repetition
784 parms.gif.animate_repeats = atoi(sRepeats);// loops NN times.
785 } else {
786 Error("WriteImage", "gif suffix %s not yet supported", s);
787 return;
788 }
789
790 parms.gif.animate_delay = delay;
791
792 int i1 = fname.Index("gif+");
793 if (i1 != kNPOS) {
794 fname = fname(0, i1 + 3);
795 }
796 else {
797 Error("WriteImage", "unexpected gif extension structure %s", fname.Data());
798 return;
799 }
800 break;
801 }
802 case kTiff:
803 parms.tiff.type = atype;
804 parms.tiff.flags = EXPORT_ALPHA;
805 parms.tiff.rows_per_strip = 0;
806 parms.tiff.compression_type = aquality <= 50 ? TIFF_COMPRESSION_JPEG :
807 TIFF_COMPRESSION_NONE;
808 parms.tiff.jpeg_quality = 100;
809 parms.tiff.opaque_threshold = 0;
810 break;
811 default:
812 Error("WriteImage", "file type %s not yet supported", s);
813 return;
814 }
815
816 if (!ASImage2file(im, nullptr, fname.Data(), atype, &parms)) {
817 Error("WriteImage", "error writing file %s", file);
818 }
819}
820
821////////////////////////////////////////////////////////////////////////////////
822/// Return file type depending on specified extension.
823/// Protected method.
824
826{
827 TString s(ext);
828 s.Strip();
829 s.ToLower();
830
831 if (s == "xpm")
832 return kXpm;
833 if (s == "png")
834 return kPng;
835 if (s == "jpg" || s == "jpeg")
836 return kJpeg;
837 if (s == "xcf")
838 return kXcf;
839 if (s == "ppm")
840 return kPpm;
841 if (s == "pnm")
842 return kPnm;
843 if (s == "bmp")
844 return kBmp;
845 if (s == "ico")
846 return kIco;
847 if (s == "cur")
848 return kCur;
849 if (s == "gif")
850 return kGif;
851 if (s.Contains("gif+"))
852 return kAnimGif;
853 if (s == "tiff")
854 return kTiff;
855 if (s == "xbm")
856 return kXbm;
857 if (s == "tga")
858 return kTga;
859 if (s == "xml")
860 return kXml;
861
862 return kUnknown;
863}
864
865////////////////////////////////////////////////////////////////////////////////
866/// Map file type to/from AfterImage types.
867/// Protected method.
868
870{
871 if (toas) {
872 switch (type) {
873 case kXpm:
874 astype = ASIT_Xpm; break;
875 case kZCompressedXpm:
876 astype = ASIT_ZCompressedXpm; break;
877 case kGZCompressedXpm:
878 astype = ASIT_GZCompressedXpm; break;
879 case kPng:
880 astype = ASIT_Png; break;
881 case kJpeg:
882 astype = ASIT_Jpeg; break;
883 case kXcf:
884 astype = ASIT_Xcf; break;
885 case kPpm:
886 astype = ASIT_Ppm; break;
887 case kPnm:
888 astype = ASIT_Pnm; break;
889 case kBmp:
890 astype = ASIT_Bmp; break;
891 case kIco:
892 astype = ASIT_Ico; break;
893 case kCur:
894 astype = ASIT_Cur; break;
895 case kGif:
896 astype = ASIT_Gif; break;
897 case kAnimGif:
898 astype = ASIT_Gif; break;
899 case kTiff:
900 astype = ASIT_Tiff; break;
901 case kXbm:
902 astype = ASIT_Xbm; break;
903 case kTga:
904 astype = ASIT_Targa; break;
905 case kXml:
906 astype = ASIT_XMLScript; break;
907 default:
908 astype = ASIT_Unknown;
909 }
910 } else {
911 switch (astype) {
912 case ASIT_Xpm:
913 type = kXpm; break;
914 case ASIT_ZCompressedXpm:
915 type = kZCompressedXpm; break;
916 case ASIT_GZCompressedXpm:
917 type = kGZCompressedXpm; break;
918 case ASIT_Png:
919 type = kPng; break;
920 case ASIT_Jpeg:
921 type = kJpeg; break;
922 case ASIT_Xcf:
923 type = kXcf; break;
924 case ASIT_Ppm:
925 type = kPpm; break;
926 case ASIT_Pnm:
927 type = kPnm; break;
928 case ASIT_Bmp:
929 type = kBmp; break;
930 case ASIT_Ico:
931 type = kIco; break;
932 case ASIT_Cur:
933 type = kCur; break;
934 case ASIT_Gif:
935 type = kGif; break;
936 case ASIT_Tiff:
937 type = kTiff; break;
938 case ASIT_Xbm:
939 type = kXbm; break;
940 case ASIT_XMLScript:
941 type = kXml; break;
942 case ASIT_Targa:
943 type = kTga; break;
944 default:
945 type = kUnknown;
946 }
947 }
948}
949
950////////////////////////////////////////////////////////////////////////////////
951/// Map quality to/from AfterImage quality.
952/// Protected method.
953
954void TASImage::MapQuality(EImageQuality &quality, UInt_t &asquality, Bool_t toas)
955{
956 if (toas) {
957 switch (quality) {
958 case kImgPoor:
959 asquality = 25; break;
960 case kImgFast:
961 asquality = 75; break;
962 case kImgGood:
963 asquality = 50; break;
964 case kImgBest:
965 asquality = 100; break;
966 default:
967 asquality = 0;
968 }
969 } else {
970 quality = kImgDefault;
971 if (asquality > 0 && asquality <= 25)
972 quality = kImgPoor;
973 if (asquality > 26 && asquality <= 50)
974 quality = kImgFast;
975 if (asquality > 51 && asquality <= 75)
976 quality = kImgGood;
977 if (asquality > 76 && asquality <= 100)
978 quality = kImgBest;
979 }
980}
981
982////////////////////////////////////////////////////////////////////////////////
983/// Deletes the old image and creates a new image depending on the values
984/// of imageData. The size of the image is width X height.
985///
986/// The color of each pixel depends on the imageData of the corresponding
987/// pixel. The palette is used to convert an image value into its color.
988/// If palette is not defined (palette = 0) a default palette is used.
989/// Any previously defined zooming is reset.
990
992 TImagePalette *palette)
993{
994 TAttImage::SetPalette(palette);
995
996 if (!InitVisual()) {
997 Warning("SetImage", "Visual not initiated");
998 return;
999 }
1000
1001 DestroyImage();
1002 delete fScaledImage;
1003 fScaledImage = nullptr;
1004
1005 // get min and max value of image
1006 fMinValue = fMaxValue = *imageData;
1007 for (Int_t pixel = 1; pixel < Int_t(width * height); pixel++) {
1008 if (fMinValue > *(imageData + pixel)) fMinValue = *(imageData + pixel);
1009 if (fMaxValue < *(imageData + pixel)) fMaxValue = *(imageData + pixel);
1010 }
1011
1012 // copy ROOT palette to asImage palette
1013 const TImagePalette &pal = GetPalette();
1014
1015 ASVectorPalette asPalette;
1016
1017 asPalette.npoints = pal.fNumPoints;
1018 Int_t col;
1019 for (col = 0; col < 4; col++)
1020 asPalette.channels[col] = new UShort_t[asPalette.npoints];
1021
1022 memcpy(asPalette.channels[0], pal.fColorBlue, pal.fNumPoints * sizeof(UShort_t));
1023 memcpy(asPalette.channels[1], pal.fColorGreen, pal.fNumPoints * sizeof(UShort_t));
1024 memcpy(asPalette.channels[2], pal.fColorRed, pal.fNumPoints * sizeof(UShort_t));
1025 memcpy(asPalette.channels[3], pal.fColorAlpha, pal.fNumPoints * sizeof(UShort_t));
1026
1027 asPalette.points = new Double_t[asPalette.npoints];
1028 for (Int_t point = 0; point < Int_t(asPalette.npoints); point++)
1029 asPalette.points[point] = fMinValue + (fMaxValue - fMinValue) * pal.fPoints[point];
1030
1031 fImage = create_asimage_from_vector(fgVisual, (Double_t*)imageData, width,
1032 height, &asPalette, ASA_ASImage,
1034
1035 delete [] asPalette.points;
1036 for (col = 0; col < 4; col++)
1037 delete [] asPalette.channels[col];
1038
1039 fZoomUpdate = 0;
1040 fZoomOffX = 0;
1041 fZoomOffY = 0;
1042 fZoomWidth = width;
1045}
1046
1047////////////////////////////////////////////////////////////////////////////////
1048/// Delete the old image and creates a new image depending on the values
1049/// of imageData. The size of the image is width X (imageData.fN / width).
1050/// The color of each pixel depends on the imageData of the corresponding
1051/// pixel. The palette is used to convert an image value into its color.
1052/// If palette is not defined (palette = 0) a default palette is used.
1053/// Any previously defined zooming is reset.
1054
1055void TASImage::SetImage(const TArrayD &imageData, UInt_t width, TImagePalette *palette)
1056{
1057 SetImage(imageData.GetArray(), width, imageData.GetSize() / width, palette);
1058}
1059
1060////////////////////////////////////////////////////////////////////////////////
1061/// Delete the old image and creates a new image depending on the values
1062/// of imageData. The size of the image is width X (imageData.fN / width).
1063/// The color of each pixel depends on the imageData of the corresponding
1064/// pixel. The palette is used to convert an image value into its color.
1065/// If palette is not defined (palette = 0) a default palette is used.
1066/// Any previously defined zooming is reset.
1067
1068void TASImage::SetImage(const TVectorD &imageData, UInt_t width, TImagePalette *palette)
1069{
1070 SetImage(imageData.GetMatrixArray(), width,
1071 imageData.GetNoElements() / width, palette);
1072}
1073
1074////////////////////////////////////////////////////////////////////////////////
1075/// Create an image from the given pad, afterwards this image can be
1076/// saved in any of the supported image formats.
1077
1079{
1080 if (!pad) {
1081 Error("FromPad", "pad cannot be 0");
1082 return;
1083 }
1084
1085 if (!InitVisual()) {
1086 Warning("FromPad", "Visual not initiated");
1087 return;
1088 }
1089
1090 SetName(pad->GetName());
1091
1092 DestroyImage();
1093 delete fScaledImage;
1094 fScaledImage = nullptr;
1095
1096 if (gROOT->IsBatch()) { // in batch mode
1097 TVirtualPS *psave = gVirtualPS;
1098 gVirtualPS = (TVirtualPS*)gROOT->ProcessLineFast("new TImageDump()");
1099 gVirtualPS->Open(pad->GetName(), 114); // in memory
1100 gVirtualPS->SetBit(BIT(11)); //kPrintingPS
1101
1103
1104 if (itmp && itmp->fImage) {
1105 itmp->BeginPaint();
1106 }
1107
1108 {
1109 TVirtualPad::TContext ctxt(pad, kFALSE);
1110 pad->Paint();
1111 }
1112
1113 if (itmp && itmp->fImage && (itmp != this)) {
1114 fImage = clone_asimage(itmp->fImage, SCL_DO_ALL);
1115 if (itmp->fImage->alt.argb32) {
1116 UInt_t sz = itmp->fImage->width*itmp->fImage->height;
1117 fImage->alt.argb32 = (ARGB32*)safemalloc(sz*sizeof(ARGB32));
1118 memcpy(fImage->alt.argb32, itmp->fImage->alt.argb32, sz*4);
1119 }
1120 }
1121 delete gVirtualPS;
1122 gVirtualPS = psave;
1123 return;
1124 }
1125
1126 // X11 Synchronization
1127 gVirtualX->Update(1);
1128 if (!gThreadXAR) {
1129 gSystem->Sleep(100);
1131 gSystem->Sleep(10);
1133 }
1134
1135 TVirtualPad *canvas = (TVirtualPad*)pad->GetCanvas();
1136 Int_t wid = (pad == canvas) ? pad->GetCanvasID() : pad->GetPixmapID();
1137 gVirtualX->SelectWindow(wid);
1138
1139 Window_t wd = (Window_t)gVirtualX->GetCurrentWindow();
1140 if (!wd) return;
1141
1142 if (w == 0) w = TMath::Abs(pad->UtoPixel(1.));
1143 if (h == 0) h = pad->VtoPixel(0.);
1144
1145 static int x11 = -1;
1146 if (x11 < 0) x11 = gVirtualX->InheritsFrom("TGX11");
1147
1148 if (x11) { //use built-in optimized version
1149 fImage = pixmap2asimage(fgVisual, wd, x, y, w, h, kAllPlanes, 0, 0);
1150 } else {
1151 unsigned char *bits = gVirtualX->GetColorBits(wd, 0, 0, w, h);
1152
1153 if (!bits) { // error
1154 return;
1155 }
1156 fImage = bitmap2asimage(bits, w, h, 0, nullptr);
1157 delete [] bits;
1158 }
1159}
1160
1161////////////////////////////////////////////////////////////////////////////////
1162/// Draw image.
1163/// Support the following drawing options:
1164/// - "T[x,y[,tint]]" : tile image (use specified offset and tint),
1165/// e.g. "T100,100,#556655"
1166/// with this option the zooming is not possible
1167/// and disabled
1168/// - "N" : display in new canvas (of original image size)
1169/// - "X" : image is drawn expanded to pad size
1170/// - "Z" : image is vectorized and image palette is drawn
1171///
1172/// The default is to display the image in the current gPad.
1173
1175{
1176 if (!fImage) {
1177 Error("Draw", "no image set");
1178 return;
1179 }
1180
1181 TString opt = option;
1182 opt.ToLower();
1183 if (opt.Contains("n") || !gPad || !gPad->IsEditable()) {
1184 Int_t w = -64;
1185 Int_t h = 64;
1186 w = (fImage->width > 64) ? (Int_t)fImage->width : w;
1187 h = (fImage->height > 64) ? (Int_t)fImage->height : h;
1188
1189 Float_t cx = 1./gStyle->GetScreenFactor();
1190 w = Int_t(w*cx) + 4;
1191 h = Int_t(h*cx) + 28;
1192 TString rname = GetName();
1193 rname.ReplaceAll(".", "");
1194 rname += Form("\", \"%s (%d x %d)", rname.Data(), fImage->width, fImage->height);
1195 rname = "new TCanvas(\"" + rname + Form("\", %d, %d);", w, h);
1196 gROOT->ProcessLineFast(rname.Data());
1197 }
1198
1199 if (!opt.Contains("x")) {
1200 Double_t left = gPad->GetLeftMargin();
1201 Double_t right = gPad->GetRightMargin();
1202 Double_t top = gPad->GetTopMargin();
1203 Double_t bottom = gPad->GetBottomMargin();
1204
1205 gPad->Range(-left / (1.0 - left - right),
1206 -bottom / (1.0 - top - bottom),
1207 1 + right / (1.0 - left - right),
1208 1 + top / ( 1.0 - top - bottom));
1209 gPad->RangeAxis(0, 0, 1, 1);
1210 }
1211
1212 TFrame *frame = gPad->GetFrame();
1213 if (frame) {
1214 frame->SetBorderMode(0);
1215 frame->SetFillColor(gPad->GetFillColor());
1216 frame->SetLineColor(gPad->GetFillColor());
1217 frame->Draw();
1218 }
1219
1221}
1222
1223////////////////////////////////////////////////////////////////////////////////
1224/// Draw asimage on drawable.
1225
1227 Int_t xsrc, Int_t ysrc, UInt_t wsrc, UInt_t hsrc,
1228 Option_t *opt)
1229{
1230 if (!im) return;
1231
1232 wsrc = wsrc ? wsrc : im->width;
1233 hsrc = hsrc ? hsrc : im->height;
1234
1235 static int x11 = -1;
1236 if (x11 < 0) x11 = gVirtualX->InheritsFrom("TGX11");
1237
1239
1240 if (x11) {
1241 UInt_t hh = hsrc;
1242 UInt_t ow = wsrc%8;
1243 UInt_t ww = wsrc - ow + (ow ? 8 : 0);
1244
1245 UInt_t bit = 0;
1246 int i = 0;
1247 UInt_t yy = 0;
1248 UInt_t xx = 0;
1249
1250 char *bits = new char[ww*hh]; //an array of bits
1251
1252 ASImageDecoder *imdec = start_image_decoding(fgVisual, im, SCL_DO_ALPHA,
1253 xsrc, ysrc, ww, 0, nullptr);
1254 if (imdec) {
1255 for (yy = 0; yy < hh; yy++) {
1256 imdec->decode_image_scanline(imdec);
1257 CARD32 *a = imdec->buffer.alpha;
1258
1259 for (xx = 0; xx < ww; xx++) {
1260 if (a[xx]) {
1261 SETBIT(bits[i], bit);
1262 } else {
1263 CLRBIT(bits[i], bit);
1264 }
1265 bit++;
1266 if (bit == 8) {
1267 bit = 0;
1268 i++;
1269 }
1270 }
1271 }
1272 }
1273
1274 stop_image_decoding(&imdec);
1275
1276 mask = gVirtualX->CreateBitmap(gVirtualX->GetDefaultRootWindow(),
1277 (const char *)bits, ww, hh);
1278 delete [] bits;
1279 }
1280
1281 GCValues_t gv;
1282 static GContext_t gc = 0;
1283
1285 gv.fClipMask = mask;
1286 gv.fClipXOrigin = x;
1287 gv.fClipYOrigin = y;
1288
1289 if (!gc) {
1290 gc = gVirtualX->CreateGC(gVirtualX->GetDefaultRootWindow(), &gv);
1291 } else {
1292 gVirtualX->ChangeGC(gc, &gv);
1293 }
1294
1295 if (x11 && (!gPad || gPad->GetGLDevice() == -1)) { //use built-in optimized version
1296 asimage2drawable(fgVisual, wid, im, (GC)gc, xsrc, ysrc, x, y, wsrc, hsrc, 1);
1297 } else {
1298 ASImage *img = nullptr;
1299 unsigned char *bits = (unsigned char *)im->alt.argb32;
1300 if (!bits) {
1301 img = tile_asimage(fgVisual, im, xsrc, ysrc, wsrc, hsrc,
1302 0, ASA_ARGB32, 0, ASIMAGE_QUALITY_DEFAULT);
1303 if (img)
1304 bits = (unsigned char *)img->alt.argb32;
1305 }
1306
1307 if (bits) {
1308 TString option(opt);
1309 option.ToLower();
1310
1311 if (gPad && gPad->GetGLDevice() != -1) {
1312 if (TVirtualPadPainter *painter = gPad->GetPainter())
1313 painter->DrawPixels(bits, wsrc, hsrc, x, y, !option.Contains("opaque"));
1314 } else {
1315 Pixmap_t pic = gVirtualX->CreatePixmapFromData(bits, wsrc, hsrc);
1316 if (pic) {
1317 if (!option.Contains("opaque")) {
1318 SETBIT(wsrc,31);
1319 SETBIT(hsrc,31);
1320 }
1321 gVirtualX->CopyArea(pic, wid, gc, 0, 0, wsrc, hsrc, x, y);
1322 gVirtualX->DeletePixmap(pic);
1323 }
1324 }
1325 }
1326
1327 if (img) {
1328 destroy_asimage(&img);
1329 }
1330 }
1331
1332 // free mask pixmap
1333 if (gv.fClipMask != kNone) gVirtualX->DeletePixmap(gv.fClipMask);
1334
1335 gv.fMask = kGCClipMask;
1336 gv.fClipMask = kNone;
1337 if (gc) gVirtualX->ChangeGC(gc, &gv);
1338}
1339
1340////////////////////////////////////////////////////////////////////////////////
1341/// Draw image on the drawable wid (pixmap, window) at x,y position.
1342///
1343/// \param[in] wid : Drawable (pixmap or window) on which image is drawn.
1344/// \param[in] x,y : Window coordinates where image is drawn.
1345/// \param[in] xsrc, ysrc : X and Y coordinates of an image area to be drawn.
1346/// \param[in] wsrc, hsrc : Width and height image area to be drawn.
1347/// \param[in] opt : specific options
1348
1350 UInt_t wsrc, UInt_t hsrc, Option_t *opt)
1351{
1353 xsrc, ysrc, wsrc, hsrc, opt);
1354}
1355
1356////////////////////////////////////////////////////////////////////////////////
1357/// Paint image.
1358/// Support the following drawing options:
1359/// - "T[x,y[,tint]]" : tile image (use specified offset and tint),
1360/// e.g. "T100,100,#556655"
1361/// with this option the zooming is not possible
1362/// and disabled
1363/// - "N" : display in new canvas (of original image size)
1364/// - "X" : image is drawn expanded to pad size
1365/// - "Z" : image is vectorized and image palette is drawn
1366///
1367/// The default is to display the image in the current gPad.
1368
1370{
1371 if (!fImage) {
1372 Error("Paint", "no image set");
1373 return;
1374 }
1375
1376 if (!InitVisual()) {
1377 Warning("Paint", "Visual not initiated");
1378 return;
1379 }
1380
1381 Int_t tile_x = 0, tile_y = 0;
1382 CARD32 tile_tint = 0;
1383 Bool_t tile = kFALSE;
1384 Bool_t expand = kFALSE;
1385
1386 TString opt = option;
1387 opt.ToLower();
1388
1389 if (opt.Contains("t")) {
1390 char stint[64];
1391 if (sscanf(opt.Data() + opt.Index("t"), "t%d,%d,%s", &tile_x, &tile_y,
1392 stint) <= 3) {
1393 tile = kTRUE;
1394 if (parse_argb_color(stint, (CARD32*)&tile_tint) == stint)
1395 tile_tint = 0;
1396 } else {
1397 Error("Paint", "tile option error");
1398 }
1399 } else if (opt.Contains("x")) {
1400 expand = kTRUE;
1402 } else if (opt.Contains("z")) {
1404
1405 if (!fImage->alt.vector) {
1406 Vectorize(256);
1407 }
1408 }
1409
1410 ASImage *image = fImage;
1411
1412 // Get geometry of pad
1413 Int_t to_w = gPad->UtoPixel(1.);
1414 Int_t to_h = gPad->VtoPixel(0.);
1415
1416 // remove the size by the margin of the pad
1417 if (!expand) {
1418 to_h = (Int_t)(to_h * (1.0 - gPad->GetBottomMargin() - gPad->GetTopMargin() ) + 0.5);
1419 to_w = (Int_t)(to_w * (1.0 - gPad->GetLeftMargin() - gPad->GetRightMargin() ) + 0.5);
1420 }
1421
1422 if ((to_w < 25 || to_h < 25) && !expand) {
1423 Error("Paint", "pad too small to display an image");
1424 return;
1425 }
1426
1427 if (GetConstRatio()) {
1428 if ((Double_t)to_w / (Double_t)fZoomWidth <
1429 (Double_t)to_h / (Double_t)fZoomHeight)
1430 to_h = Int_t(Double_t(fZoomHeight) * to_w / fZoomWidth);
1431 else
1432 to_w = Int_t(Double_t(fZoomWidth) * to_h / fZoomHeight);
1433 }
1434 // upper left corner and size of the palette in pixels
1435 Int_t pal_Ax = to_w + gPad->UtoAbsPixel(gPad->GetLeftMargin()) +
1436 (gPad->UtoAbsPixel(gPad->GetRightMargin()) / 10);
1437 Int_t pal_Ay = gPad->YtoAbsPixel(1.0);
1438 Int_t pal_x = to_w + gPad->UtoPixel(gPad->GetLeftMargin()) +
1439 (gPad->UtoPixel(gPad->GetRightMargin()) / 10);
1440 Int_t pal_y = gPad->YtoPixel(1.0);
1441 Int_t pal_w = gPad->UtoPixel(gPad->GetRightMargin()) / 3;
1442 Int_t pal_h = to_h;
1443
1444 ASImage *grad_im = nullptr;
1445
1446 if (fImage->alt.vector && fPaletteEnabled) {
1447 // draw the palette
1448 ASGradient grad;
1449 const TImagePalette &pal = GetPalette();
1450
1451 grad.npoints = pal.fNumPoints;
1452 grad.type = GRADIENT_Top2Bottom;
1453 grad.color = new ARGB32[grad.npoints];
1454 grad.offset = new double[grad.npoints];
1455
1456 for (Int_t pt = 0; pt < grad.npoints; pt++) {
1457 Int_t oldPt = grad.npoints - pt -1;
1458 grad.offset[pt] = 1 - pal.fPoints[oldPt];
1459 grad.color[pt] = (((ARGB32)(pal.fColorBlue[oldPt] & 0xff00)) >> 8) |
1460 (((ARGB32)(pal.fColorGreen[oldPt] & 0xff00)) ) |
1461 (((ARGB32)(pal.fColorRed[oldPt] & 0xff00)) << 8) |
1462 (((ARGB32)(pal.fColorAlpha[oldPt] & 0xff00)) << 16);
1463 }
1464
1465 grad_im = make_gradient(fgVisual, &grad , UInt_t(pal_w),
1466 pal_h, SCL_DO_COLOR,
1467 ASA_ARGB32, GetImageCompression(), GetImageQuality());
1468
1469 delete [] grad.color;
1470 delete [] grad.offset;
1471 }
1472
1473 if (tile) {
1474 delete fScaledImage;
1476 if (!fScaledImage) return;
1477 fScaledImage->fImage = tile_asimage(fgVisual, fImage, tile_x, tile_y,
1478 to_w, to_h, tile_tint, ASA_ASImage,
1480 image = fScaledImage->fImage;
1481
1482 } else if (fZoomUpdate == kZoomOps) {
1483 image = fImage;
1484
1485 } else {
1486 // Scale and zoom image if needed
1487 if (Int_t(fImage->width) != to_w || Int_t(fImage->height) != to_h ||
1488 fImage->width != fZoomWidth || fImage->height != fZoomHeight) {
1489
1490 if (fScaledImage && (Int_t(fScaledImage->GetWidth()) != to_w ||
1491 Int_t(fScaledImage->GetHeight()) != to_h ||
1492 fZoomUpdate)) {
1493
1494 delete fScaledImage;
1495 fScaledImage = nullptr;
1496 }
1497
1498 if (!fScaledImage) {
1500 if (!fScaledImage) return;
1501
1502 if (fZoomWidth && fZoomHeight &&
1503 ((fImage->width != fZoomWidth) || (fImage->height != fZoomHeight))) {
1504 // zoom and scale image
1505 ASImage *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 = nullptr;
1599 // Look for color by name
1600 if ((color = (TColor*)colors->FindObject("Image_PS")) == nullptr)
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, nullptr);
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, nullptr);
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)
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 = nullptr;
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 = nullptr;
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 = nullptr;
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(nullptr, 0, 0, nullptr);
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(nullptr, 0, 0, nullptr);
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 || cmap == 0) {
2233 fgVisual = create_asvisual(nullptr, 0, 0, nullptr);
2234 } else {
2235 fgVisual = create_asvisual_for_id(disp, screen, depth,
2236 XVisualIDFromVisual(vis), cmap, nullptr);
2237 }
2238#endif
2239#else
2240 fgVisual = create_asvisual(nullptr, 0, 0, nullptr);
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) {
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, nullptr, 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, nullptr);
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 = nullptr;
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, nullptr);
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 nullptr;
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 nullptr;
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(nullptr, fImage, SCL_DO_ALL, 0, y,
2442 img->width, height, nullptr)) == nullptr) {
2443 Warning("GetPixels", "Failed to create image decoder");
2444 return nullptr;
2445 }
2446
2447 TArrayL *ret = new TArrayL(width * height);
2448 Int_t r = 0, g = 0, b = 0;
2449 Long_t p = 0;
2450
2451 for (UInt_t k = 0; k < height; k++) {
2452 imdec->decode_image_scanline(imdec);
2453
2454 for (UInt_t i = 0; i < width; ++i) {
2455 if ((r == (Int_t)imdec->buffer.red[i]) &&
2456 (g == (Int_t)imdec->buffer.green[i]) &&
2457 (b == (Int_t)imdec->buffer.blue[i])) {
2458 } else {
2459 r = (Int_t)imdec->buffer.red[i];
2460 g = (Int_t)imdec->buffer.green[i];
2461 b = (Int_t)imdec->buffer.blue[i];
2462 p = (Long_t)TColor::RGB2Pixel(r, g, b);
2463 }
2464 ret->AddAt(p, k*width + i);
2465 }
2466 }
2467
2468 stop_image_decoding(&imdec);
2469 return ret;
2470}
2471
2472////////////////////////////////////////////////////////////////////////////////
2473/// Return a pointer to internal array[width x height] of double values [0,1].
2474/// This array is directly accessible. That allows to manipulate/change the
2475/// image.
2476
2478{
2479 if (!fImage) {
2480 Warning("GetVecArray", "Bad Image");
2481 return nullptr;
2482 }
2483 if (fImage->alt.vector) {
2484 return fImage->alt.vector;
2485 }
2486 // vectorize
2487 return nullptr;
2488}
2489
2490////////////////////////////////////////////////////////////////////////////////
2491/// In case of vectorized image return an associated array of doubles
2492/// otherwise this method creates and returns a 2D array of doubles corresponding to palette.
2493/// If palette is ZERO a color converted to double value [0, 1] according to formula
2494/// ~~~ {.cpp}
2495/// Double_t((r << 16) + (g << 8) + b)/0xFFFFFF
2496/// ~~~
2497/// The returned array must be deleted after usage.
2498
2500{
2501 if (!fImage) {
2502 Warning("GetArray", "Bad Image");
2503 return nullptr;
2504 }
2505
2506 TArrayD *ret;
2507
2508 if (fImage->alt.vector) {
2509 ret = new TArrayD(fImage->width*fImage->height, fImage->alt.vector);
2510 return ret;
2511 }
2512
2513 ASImageDecoder *imdec;
2514
2515 w = w ? w : fImage->width;
2516 h = h ? h : fImage->height;
2517
2518 if ((fImage->width != w) || (fImage->height != h)) {
2519 Scale(w, h);
2520 }
2521
2522 ASImage *img = fScaledImage ? fScaledImage->fImage : fImage;
2523
2524 if ((imdec = start_image_decoding(nullptr, img, SCL_DO_ALL, 0, 0,
2525 img->width, 0, nullptr)) == nullptr) {
2526 Warning("GetArray", "Failed to create image decoder");
2527 return nullptr;
2528 }
2529
2530 ret = new TArrayD(w * h);
2531 CARD32 r = 0, g = 0, b = 0;
2532 Int_t p = 0;
2533 Double_t v = 0;
2534
2535 for (UInt_t k = 0; k < h; k++) {
2536 imdec->decode_image_scanline(imdec);
2537
2538 for (UInt_t i = 0; i < w; ++i) {
2539 if ((r == imdec->buffer.red[i]) &&
2540 (g == imdec->buffer.green[i]) &&
2541 (b == imdec->buffer.blue[i])) {
2542 } else {
2543 r = imdec->buffer.red[i];
2544 g = imdec->buffer.green[i];
2545 b = imdec->buffer.blue[i];
2546 if (palette) p = palette->FindColor(r, g, b);
2547 }
2548 v = palette ? palette->fPoints[p] : Double_t((r << 16) + (g << 8) + b)/0xFFFFFF;
2549 ret->AddAt(v, (h-k-1)*w + i);
2550 }
2551 }
2552
2553 stop_image_decoding(&imdec);
2554 return ret;
2555}
2556
2557////////////////////////////////////////////////////////////////////////////////
2558/// Draw text of size (in pixels for TrueType fonts)
2559/// at position (x, y) with color specified by hex string.
2560///
2561/// - font_name: TrueType font's filename or X font spec or alias.
2562/// 3D style of text is one of the following:
2563/// * 0 plain 2D text,
2564/// * 1 embossed,
2565/// * 2 sunken,
2566/// * 3 shade above,
2567/// * 4 shade below,
2568/// * 5 embossed thick,
2569/// * 6 sunken thick.
2570/// * 7 outline above,
2571/// * 8 ouline below,
2572/// * 9 full ouline.
2573/// - fore_file specifies foreground texture of text.
2574
2576 const char *color, const char *font_name,
2577 EText3DType type, const char *fore_file, Float_t angle)
2578{
2579 UInt_t width = 0, height = 0;
2580 ARGB32 text_color = ARGB32_Black;
2581 ASImage *fore_im = nullptr;
2582 ASImage *text_im = nullptr;
2583 Bool_t ttfont = kFALSE;
2584
2585 if (!InitVisual()) {
2586 Warning("DrawText", "Visual not initiated");
2587 return;
2588 }
2589
2590 TString fn = font_name;
2591 fn.Strip();
2592
2593 // This is for backward compatibility...
2594 if (fn.Last('/') == 0) fn = fn(1, fn.Length() - 1);
2595
2596 const char *ttpath = gEnv->GetValue("Root.TTFontPath",
2598 char *tmpstr = gSystem->Which(ttpath, fn, kReadPermission);
2599 fn = tmpstr;
2600 delete [] tmpstr;
2601
2602 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")) {
2603 ttfont = kTRUE;
2604 }
2605
2606 if (color) {
2607 parse_argb_color(color, &text_color);
2608 }
2609
2610 if (fImage && fImage->alt.argb32 && ttfont) {
2611 DrawTextTTF(x, y, text, size, text_color, fn.Data(), angle);
2612 return;
2613 }
2614
2615 if (!gFontManager) {
2616 gFontManager = create_font_manager(fgVisual->dpy, nullptr, nullptr);
2617 }
2618
2619 if (!gFontManager) {
2620 Warning("DrawText", "cannot create Font Manager");
2621 return;
2622 }
2623
2624 ASFont *font = get_asfont(gFontManager, fn.Data(), 0, size, ASF_GuessWho);
2625
2626 if (!font) {
2627 font = get_asfont(gFontManager, "fixed", 0, size, ASF_GuessWho);
2628 if (!font) {
2629 Warning("DrawText", "cannot find a font %s", font_name);
2630 return;
2631 }
2632 }
2633
2634 get_text_size(text, font, (ASText3DType)type, &width, &height);
2635
2636 if (!fImage) {
2637 fImage = create_asimage(width, height, 0);
2638 fill_asimage(fgVisual, fImage, 0, 0, width, height, 0xFFFFFFFF);
2639 }
2640
2641 text_im = draw_text(text, font, (ASText3DType)type, 0);
2642
2643 ASImage *rimg = fImage;
2644
2645 if (fore_file) {
2646 ASImage *tmp = file2ASImage(fore_file, 0xFFFFFFFF, SCREEN_GAMMA, 0, 0);
2647 if (tmp) {
2648 if ((tmp->width != width) || (tmp->height != height)) {
2649 fore_im = tile_asimage(fgVisual, tmp, 0, 0, width, height, 0,
2650 ASA_ASImage, GetImageCompression(), GetImageQuality());
2651 }
2652 destroy_asimage(&tmp);
2653 } else {
2654 fore_im = tmp;
2655 }
2656 }
2657
2658 if (fore_im) {
2659 move_asimage_channel(fore_im, IC_ALPHA, text_im, IC_ALPHA);
2660 destroy_asimage(&text_im);
2661 } else {
2662 fore_im = text_im ;
2663 }
2664
2665 release_font(font);
2666
2667 if (fore_im) {
2668 ASImage *rendered_im;
2669 ASImageLayer layers[2];
2670
2671 init_image_layers(&(layers[0]), 2);
2672 fore_im->back_color = text_color;
2673 layers[0].im = rimg;
2674 layers[0].dst_x = 0;
2675 layers[0].dst_y = 0;
2676 layers[0].clip_width = rimg->width;
2677 layers[0].clip_height = rimg->height;
2678 layers[0].bevel = nullptr;
2679 layers[1].im = fore_im;
2680 layers[1].dst_x = x;
2681 layers[1].dst_y = y;
2682 layers[1].clip_width = fore_im->width;
2683 layers[1].clip_height = fore_im->height;
2684
2685 rendered_im = merge_layers(fgVisual, &(layers[0]), 2, rimg->width, rimg->height,
2686 ASA_ASImage, GetImageCompression(), GetImageQuality());
2687
2688 destroy_asimage(&fore_im);
2689 DestroyImage();
2690 fImage = rendered_im;
2691 UnZoom();
2692 }
2693}
2694
2695////////////////////////////////////////////////////////////////////////////////
2696/// Merge two images.
2697///
2698/// op is string which specifies overlay operation. Supported operations are:
2699///
2700/// - add - color addition with saturation
2701/// - alphablend - alpha-blending
2702/// - allanon - color values averaging
2703/// - colorize - hue and saturate bottom image same as top image
2704/// - darken - use lowest color value from both images
2705/// - diff - use absolute value of the color difference between two images
2706/// - dissipate - randomly alpha-blend images
2707/// - hue - hue bottom image same as top image
2708/// - lighten - use highest color value from both images
2709/// - overlay - some weird image overlaying(see GIMP)
2710/// - saturate - saturate bottom image same as top image
2711/// - screen - another weird image overlaying(see GIMP)
2712/// - sub - color substraction with saturation
2713/// - tint - tinting image with image
2714/// - value - value bottom image same as top image
2715
2716void TASImage::Merge(const TImage *im, const char *op, Int_t x, Int_t y)
2717{
2718 if (!im) return;
2719
2720 if (!InitVisual()) {
2721 Warning("Merge", "Visual not initiated");
2722 return;
2723 }
2724
2725 ASImage *rendered_im;
2726 ASImageLayer layers[2];
2727
2728 init_image_layers(&(layers[0]), 2);
2729 layers[0].im = fImage;
2730 layers[0].dst_x = 0;
2731 layers[0].dst_y = 0;
2732 layers[0].clip_width = fImage->width;
2733 layers[0].clip_height = fImage->height;
2734 layers[0].bevel = nullptr;
2735 layers[1].im = ((TASImage*)im)->fImage;
2736 layers[1].dst_x = x;
2737 layers[1].dst_y = y;
2738 layers[1].clip_width = im->GetWidth();
2739 layers[1].clip_height = im->GetHeight();
2740 layers[1].merge_scanlines = blend_scanlines_name2func(op ? op : "add");
2741
2742 rendered_im = merge_layers(fgVisual, &(layers[0]), 2, fImage->width, fImage->height,
2743 ASA_ASImage, GetImageCompression(), GetImageQuality());
2744
2745 DestroyImage();
2746 fImage = rendered_im;
2747 UnZoom();
2748}
2749
2750////////////////////////////////////////////////////////////////////////////////
2751/// Perform Gaussian blur of the image (useful for drop shadows).
2752/// - hr - horizontal radius of the blur
2753/// - vr - vertical radius of the blur
2754
2756{
2757 if (!InitVisual()) {
2758 Warning("Blur", "Visual not initiated");
2759 return;
2760 }
2761
2762 if (!fImage) {
2763 fImage = create_asimage(100, 100, 0);
2764
2765 if (!fImage) {
2766 Warning("Blur", "Failed to create image");
2767 return;
2768 }
2769
2770 fill_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height, ARGB32_White);
2771 }
2772
2773 ASImage *rendered_im = blur_asimage_gauss(fgVisual, fImage, hr > 0 ? hr : 3,
2774 vr > 0 ? vr : 3, SCL_DO_ALL,
2775 ASA_ASImage, GetImageCompression(), GetImageQuality());
2776 DestroyImage();
2777 fImage = rendered_im;
2778 UnZoom();
2779}
2780
2781////////////////////////////////////////////////////////////////////////////////
2782/// Clone image.
2783
2784TObject *TASImage::Clone(const char *newname) const
2785{
2786 if (!InitVisual() || !fImage) {
2787 Warning("Clone", "Image not initiated");
2788 return nullptr;
2789 }
2790
2792
2793 if (!im) {
2794 Warning("Clone", "Failed to create image");
2795 return nullptr;
2796 }
2797
2798 im->SetName(newname);
2799
2800 im->fImage = clone_asimage(fImage, SCL_DO_ALL);
2801 im->fMaxValue = fMaxValue;
2802 im->fMinValue = fMinValue;
2803 im->fZoomOffX = fZoomOffX;
2804 im->fZoomOffY = fZoomOffY;
2805 im->fZoomWidth = fZoomWidth;
2808 im->fScaledImage = fScaledImage ? (TASImage*)fScaledImage->Clone("") : nullptr;
2809
2810 if (fImage->alt.argb32) {
2811 UInt_t sz = fImage->width * fImage->height;
2812 im->fImage->alt.argb32 = (ARGB32*)safemalloc(sz*sizeof(ARGB32));
2813 memcpy(im->fImage->alt.argb32, fImage->alt.argb32, sz * sizeof(ARGB32));
2814 }
2815
2816 return im;
2817}
2818
2819////////////////////////////////////////////////////////////////////////////////
2820/// Reduce color-depth of an image and fills vector of "scientific data"
2821/// [0...1]
2822///
2823/// Colors are reduced by allocating color cells to most used colors first,
2824/// and then approximating other colors with those allocated.
2825///
2826/// \param[in] max_colors - maximum size of the colormap.
2827/// \param[in] dither - number of bits to strip off the color data ( 0...7 )
2828/// \param[in] opaque_threshold - alpha channel threshold at which pixel should be treated as opaque
2829
2830Double_t *TASImage::Vectorize(UInt_t max_colors, UInt_t dither, Int_t opaque_threshold)
2831{
2832 if (!InitVisual()) {
2833 Warning("Vectorize", "Visual not initiated");
2834 return nullptr;
2835 }
2836
2837 if (!fImage) {
2838 fImage = create_asimage(100, 100, 0);
2839
2840 if (!fImage) {
2841 Warning("Vectorize", "Failed to create image");
2842 return nullptr;
2843 }
2844
2845 fill_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height, ARGB32_White);
2846 }
2847
2848 ASColormap cmap;
2849 int *res;
2850 UInt_t r=0, g=0, b=0;
2851
2852 dither = dither > 7 ? 7 : dither;
2853
2854 res = colormap_asimage(fImage, &cmap, max_colors, dither, opaque_threshold);
2855
2856 Double_t *vec = new Double_t[fImage->height*fImage->width];
2857 UInt_t v;
2858 Double_t tmp;
2859 fMinValue = 2;
2860 fMaxValue = -1;
2861
2862 for (UInt_t y = 0; y < fImage->height; y++) {
2863 for (UInt_t x = 0; x < fImage->width; x++) {
2864 int i = y*fImage->width + x;
2865 if (res) {
2866 g = INDEX_SHIFT_GREEN(cmap.entries[res[i]].green);
2867 b = INDEX_SHIFT_BLUE(cmap.entries[res[i]].blue);
2868 r = INDEX_SHIFT_RED(cmap.entries[res[i]].red);
2869 }
2870 v = MAKE_INDEXED_COLOR24(r,g,b);
2871 v = (v>>12)&0x0FFF;
2872 tmp = Double_t(v)/0x0FFF;
2873 vec[(fImage->height - y - 1)*fImage->width + x] = tmp;
2874 if (fMinValue > tmp) fMinValue = tmp;
2875 if (fMaxValue < tmp) fMaxValue = tmp;
2876 }
2877 }
2878 TImagePalette *pal = new TImagePalette(cmap.count);
2879
2880 for (UInt_t j = 0; j < cmap.count; j++) {
2881 g = INDEX_SHIFT_GREEN(cmap.entries[j].green);
2882 b = INDEX_SHIFT_BLUE(cmap.entries[j].blue);
2883 r = INDEX_SHIFT_RED(cmap.entries[j].red);
2884 v = MAKE_INDEXED_COLOR24(r,g,b);
2885
2886 v = (v>>12) & 0x0FFF;
2887 pal->fPoints[j] = Double_t(v)/0x0FFF;
2888
2889 pal->fColorRed[j] = cmap.entries[j].red << 8;
2890 pal->fColorGreen[j] = cmap.entries[j].green << 8;
2891 pal->fColorBlue[j] = cmap.entries[j].blue << 8;
2892 pal->fColorAlpha[j] = 0xFF00;
2893 }
2894
2895 destroy_colormap(&cmap, kTRUE);
2896
2897 fPalette = *pal;
2898 fImage->alt.vector = vec;
2899 UnZoom();
2900 // ROOT-7647: res is allocated with `safemalloc` by colormap_asimage
2901 if (res) safefree(res);
2902 return (Double_t*)fImage->alt.vector;
2903}
2904
2905////////////////////////////////////////////////////////////////////////////////
2906/// This function will tile original image to specified size with offsets
2907/// requested, and then it will go though it and adjust hue, saturation and
2908/// value of those pixels that have specific hue, set by affected_hue/
2909/// affected_radius parameters. When affected_radius is greater then 180
2910/// entire image will be adjusted. Note that since grayscale colors have
2911/// no hue - the will not get adjusted. Only saturation and value will be
2912/// adjusted in gray pixels.
2913///
2914/// Hue is measured as an angle on a 360 degree circle, The following is
2915/// relationship of hue values to regular color names :
2916/// - red - 0
2917/// - yellow - 60
2918/// - green - 120
2919/// - cyan - 180
2920/// - blue - 240
2921/// - magenta - 300
2922/// - red - 360
2923///
2924/// All the hue values in parameters will be adjusted to fall within 0-360 range.
2925///
2926/// \param[in] hue hue in degrees in range 0-360. This allows to limit
2927/// impact of color adjustment to affect only limited range of hues.
2928///
2929/// \param[in] radius value in degrees to be used in order to
2930/// calculate the range of affected hues. Range is determined by
2931/// substracting and adding this value from/to affected_hue.
2932///
2933/// \param[in] H value by which to change hues in affected range.
2934/// \param[in] S value by which to change saturation of the pixels in affected hue range.
2935/// \param[in] V value by which to change Value(brightness) of pixels in affected hue range.
2936///
2937/// \param[in] x,y position on infinite surface tiled with original image, of the
2938/// left-top corner of the area to be used for new image.
2939///
2940/// \param[in] width, height size of the area of the original image to be used for new image.
2941/// Default is current width, height of the image.
2942
2945{
2946 if (!InitVisual()) {
2947 Warning("HSV", "Visual not initiated");
2948 return;
2949 }
2950
2951 if (!fImage) {
2952 fImage = create_asimage(width ? width : 20, height ? height : 20, 0);
2953
2954 if (!fImage) {
2955 Warning("HSV", "Failed to create image");
2956 return;
2957 }
2958
2959 x = 0;
2960 y = 0;
2961 fill_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height, ARGB32_White);
2962 }
2963
2964 width = !width ? fImage->width : width;
2965 height = !height ? fImage->height : height;
2966
2967 ASImage *rendered_im = nullptr;
2968
2969 if (H || S || V) {
2970 rendered_im = adjust_asimage_hsv(fgVisual, fImage, x, y, width, height,
2971 hue, radius, H, S, V, ASA_ASImage, 100,
2972 ASIMAGE_QUALITY_TOP);
2973 }
2974 if (!rendered_im) {
2975 Warning("HSV", "Failed to create rendered image");
2976 return;
2977 }
2978
2979 DestroyImage();
2980 fImage = rendered_im;
2981 UnZoom();
2982}
2983
2984////////////////////////////////////////////////////////////////////////////////
2985/// Render multipoint gradient inside rectangle of size (width, height)
2986/// at position (x,y) within the existing image.
2987///
2988/// \param[in] angle Given in degrees. Default is 0. This is the
2989/// direction of the gradient. Currently the only supported
2990/// values are 0, 45, 90, 135, 180, 225, 270, 315. 0 means left
2991/// to right, 90 means top to bottom, etc.
2992///
2993/// \param[in] colors Whitespace-separated list of colors. At least two
2994/// colors are required. Each color in this list will be visited
2995/// in turn, at the intervals given by the offsets attribute.
2996///
2997/// \param[in] offsets Whitespace-separated list of floating point values
2998/// ranging from 0.0 to 1.0. The colors from the colors attribute
2999/// are given these offsets, and the final gradient is rendered
3000/// from the combination of the two. If both colors and offsets
3001/// are given but the number of colors and offsets do not match,
3002/// the minimum of the two will be used, and the other will be
3003/// truncated to match. If offsets are not given, a smooth
3004/// stepping from 0.0 to 1.0 will be used.
3005/// \param[in] x x position coordinate
3006/// \param[in] y y position coordinate
3007/// \param[in] width image width, if 0, it will be read from fImage
3008/// \param[in] height image height, if 0, it will be read from fImage
3009void TASImage::Gradient(UInt_t angle, const char *colors, const char *offsets,
3011{
3012 if (!InitVisual()) {
3013 Warning("Gradient", "Visual not initiated");
3014 return;
3015 }
3016
3017 ASImage *rendered_im = nullptr;
3018 ASGradient gradient;
3019
3020 int reverse = 0, npoints1 = 0, npoints2 = 0;
3021 char *p;
3022 char *pb;
3023 char ch;
3024 TString str = colors;
3025 TString col;
3026
3027 if ((angle > 2 * 180 * 15 / 16) || (angle < 2 * 180 * 1 / 16)) {
3028 gradient.type = GRADIENT_Left2Right;
3029 } else if (angle < 2 * 180 * 3 / 16) {
3030 gradient.type = GRADIENT_TopLeft2BottomRight;
3031 } else if (angle < 2 * 180 * 5 / 16) {
3032 gradient.type = GRADIENT_Top2Bottom;
3033 } else if (angle < 2 * 180 * 7 / 16) {
3034 gradient.type = GRADIENT_BottomLeft2TopRight; reverse = 1;
3035 } else if (angle < 2 * 180 * 9 / 16) {
3036 gradient.type = GRADIENT_Left2Right; reverse = 1;
3037 } else if (angle < 2 * 180 * 11 / 16) {
3038 gradient.type = GRADIENT_TopLeft2BottomRight; reverse = 1;
3039 } else if (angle < 2 * 180 * 13 / 16) {
3040 gradient.type = GRADIENT_Top2Bottom; reverse = 1;
3041 } else {
3042 gradient.type = GRADIENT_BottomLeft2TopRight;
3043 }
3044
3045 for (p = (char*)colors; isspace((int)*p); p++) { }
3046
3047 for (npoints1 = 0; *p; npoints1++) {
3048 if (*p) {
3049 for ( ; *p && !isspace((int)*p); p++) { }
3050 }
3051 for ( ; isspace((int)*p); p++) { }
3052 }
3053 if (offsets) {
3054 for (p = (char*)offsets; isspace((int)*p); p++) { }
3055
3056 for (npoints2 = 0; *p; npoints2++) {
3057 if (*p) {
3058 for ( ; *p && !isspace((int)*p); p++) { }
3059 }
3060 for ( ; isspace((int)*p); p++) { }
3061 }
3062 }
3063 if (npoints1 > 1) {
3064 int i;
3065 if (offsets && (npoints1 > npoints2)) npoints1 = npoints2;
3066
3067 if (!width) {
3068 width = fImage ? fImage->width : 20;
3069 }
3070 if (!height) {
3071 height = fImage ? fImage->height : 20;
3072 }
3073
3074 gradient.color = new ARGB32[npoints1];
3075 gradient.offset = new double[npoints1];
3076
3077 for (p = (char*)colors; isspace((int)*p); p++) { }
3078
3079 for (npoints1 = 0; *p; ) {
3080 pb = p;
3081
3082 if (*p) {
3083 for ( ; *p && !isspace((int)*p); p++) { }
3084 }
3085 for ( ; isspace((int)*p); p++) { }
3086
3087 col = str(pb - colors, p - pb);
3088
3089 if (parse_argb_color(col.Data(), gradient.color + npoints1) != col) {
3090 npoints1++;
3091 } else {
3092 Warning("Gradient", "Failed to parse color [%s] - defaulting to black", pb);
3093 }
3094 }
3095
3096 if (offsets) {
3097 for (p = (char*)offsets; isspace((int)*p); p++) { }
3098
3099 for (npoints2 = 0; *p; ) {
3100 pb = p;
3101
3102 if (*p) {
3103 for ( ; *p && !isspace((int)*p); p++) { }
3104 }
3105 ch = *p; *p = '\0';
3106 gradient.offset[npoints2] = strtod(pb, &pb);
3107
3108 if (pb == p) npoints2++;
3109 *p = ch;
3110 for ( ; isspace((int)*p); p++) { }
3111 }
3112 } else {
3113 for (npoints2 = 0; npoints2 < npoints1; npoints2++) {
3114 gradient.offset[npoints2] = (double)npoints2 / (npoints1 - 1);
3115 }
3116 }
3117 gradient.npoints = npoints1;
3118
3119 if (npoints2 && (gradient.npoints > npoints2)) {
3120 gradient.npoints = npoints2;
3121 }
3122 if (reverse) {
3123 for (i = 0; i < gradient.npoints/2; i++) {
3124 int i2 = gradient.npoints - 1 - i;
3125 ARGB32 c = gradient.color[i];
3126 double o = gradient.offset[i];
3127 gradient.color[i] = gradient.color[i2];
3128 gradient.color[i2] = c;
3129 gradient.offset[i] = gradient.offset[i2];
3130 gradient.offset[i2] = o;
3131 }
3132 for (i = 0; i < gradient.npoints; i++) {
3133 gradient.offset[i] = 1.0 - gradient.offset[i];
3134 }
3135 }
3136 rendered_im = make_gradient(fgVisual, &gradient, width, height, SCL_DO_ALL,
3137 ASA_ASImage, GetImageCompression(), GetImageQuality());
3138
3139 delete [] gradient.color;
3140 delete [] gradient.offset;
3141 }
3142
3143 if (!rendered_im) { // error
3144 Warning("Gradient", "Failed to create gradient image");
3145 return;
3146 }
3147
3148 if (!fImage) {
3149 fImage = rendered_im;
3150 return;
3151 }
3152
3153 ASImageLayer layers[2];
3154
3155 init_image_layers(&(layers[0]), 2);
3156 layers[0].im = fImage;
3157 layers[0].dst_x = 0;
3158 layers[0].dst_y = 0;
3159 layers[0].clip_width = fImage->width;
3160 layers[0].clip_height = fImage->height;
3161 layers[0].bevel = nullptr;
3162 layers[1].im = rendered_im;
3163 layers[1].dst_x = x;
3164 layers[1].dst_y = y;
3165 layers[1].clip_width = width;
3166 layers[1].clip_height = height;
3167 layers[1].merge_scanlines = alphablend_scanlines;
3168
3169 ASImage *merge_im = merge_layers(fgVisual, &(layers[0]), 2, fImage->width, fImage->height,
3170 ASA_ASImage, GetImageCompression(), GetImageQuality());
3171 if (!merge_im) {
3172 Warning("Gradient", "Failed to create merged image");
3173 return;
3174 }
3175
3176 destroy_asimage(&rendered_im);
3177 DestroyImage();
3178 fImage = merge_im;
3179 UnZoom();
3180}
3181
3182////////////////////////////////////////////////////////////////////////////////
3183/// Make component hilite.
3184/// (used internally)
3185
3186static CARD8 MakeComponentHilite(int cmp)
3187{
3188 if (cmp < 51) {
3189 cmp = 51;
3190 }
3191 cmp = (cmp * 12) / 10;
3192
3193 return (cmp > 255) ? 255 : cmp;
3194}
3195
3196////////////////////////////////////////////////////////////////////////////////
3197/// Calculate highlite color.
3198/// (used internally)
3199
3200static ARGB32 GetHilite(ARGB32 background)
3201{
3202 return ((MakeComponentHilite((background>>24) & 0x000000FF) << 24) & 0xFF000000) |
3203 ((MakeComponentHilite((background & 0x00FF0000) >> 16) << 16) & 0x00FF0000) |
3204 ((MakeComponentHilite((background & 0x0000FF00) >> 8) << 8) & 0x0000FF00) |
3205 ((MakeComponentHilite((background & 0x000000FF))) & 0x000000FF);
3206}
3207
3208////////////////////////////////////////////////////////////////////////////////
3209/// Calculate shadow color.
3210/// (used internally)
3211
3212static ARGB32 GetShadow(ARGB32 background)
3213{
3214 return (background >> 1) & 0x7F7F7F7F;
3215}
3216
3217////////////////////////////////////////////////////////////////////////////////
3218/// Get average.
3219/// (used internally)
3220
3221static ARGB32 GetAverage(ARGB32 foreground, ARGB32 background)
3222{
3223 CARD16 a, r, g, b;
3224
3225 a = ARGB32_ALPHA8(foreground) + ARGB32_ALPHA8(background);
3226 a = (a<<3)/10;
3227 r = ARGB32_RED8(foreground) + ARGB32_RED8(background);
3228 r = (r<<3)/10;
3229 g = ARGB32_GREEN8(foreground) + ARGB32_GREEN8(background);
3230 g = (g<<3)/10;
3231 b = ARGB32_BLUE8(foreground) + ARGB32_BLUE8(background);
3232 b = (b<<3)/10;
3233
3234 return MAKE_ARGB32(a, r, g, b);
3235}
3236
3237
3238////////////////////////////////////////////////////////////////////////////////
3239/// Bevel is used to create 3D effect while drawing buttons, or any other
3240/// image that needs to be framed. Bevel is drawn using 2 primary colors:
3241/// one for top and left sides - hi color, and another for bottom and
3242/// right sides - low color. Bevel can be drawn over existing image or
3243/// as newly created, as it is shown in code below:
3244/// ~~~ {.cpp}
3245/// TImage *img = TImage::Create();
3246/// img->Bevel(0, 0, 400, 300, "#dddddd", "#000000", 3);
3247/// ~~~
3248
3250 const char *hi_color, const char *lo_color, UShort_t thick,
3251 Bool_t reverse)
3252{
3253 if (!InitVisual()) {
3254 Warning("Bevel", "Visual not initiated");
3255 return;
3256 }
3257
3258 ASImageBevel bevel;
3259 bevel.type = 0;
3260
3261 ARGB32 hi=ARGB32_White, lo=ARGB32_White;
3262 parse_argb_color(hi_color, &hi);
3263 parse_argb_color(lo_color, &lo);
3264
3265 if (reverse) {
3266 bevel.lo_color = hi;
3267 bevel.lolo_color = GetHilite(hi);
3268 bevel.hi_color = lo;
3269 bevel.hihi_color = GetShadow(lo);
3270 } else {
3271 bevel.hi_color = hi;
3272 bevel.hihi_color = GetHilite(hi);
3273 bevel.lo_color = lo;
3274 bevel.lolo_color = GetShadow(lo);
3275 }
3276 bevel.hilo_color = GetAverage(hi, lo);
3277
3278 int extra_hilite = 2;
3279 bevel.left_outline = bevel.top_outline = bevel.right_outline = bevel.bottom_outline = thick;
3280 bevel.left_inline = bevel.top_inline = bevel.right_inline = bevel.bottom_inline = extra_hilite + 1;
3281
3282 if (bevel.top_outline > 1) {
3283 bevel.top_inline += bevel.top_outline - 1;
3284 }
3285
3286 if (bevel.left_outline > 1) {
3287 bevel.left_inline += bevel.left_outline - 1;
3288 }
3289
3290 if (bevel.right_outline > 1) {
3291 bevel.right_inline += bevel.right_outline - 1;
3292 }
3293
3294 if (bevel.bottom_outline > 1) {
3295 bevel.bottom_inline += bevel.bottom_outline - 1;
3296 }
3297
3298 ASImage *merge_im;
3299 ARGB32 fill = ((hi>>24) != 0xff) || ((lo>>24) != 0xff) ? bevel.hilo_color : (bevel.hilo_color | 0xff000000);
3300
3301 if (!fImage) {
3302 fImage = create_asimage(width ? width : 20, height ? height : 20, 0);
3303
3304 if (!fImage) {
3305 Warning("Bevel", "Failed to create image");
3306 return;
3307 }
3308
3309 x = 0;
3310 y = 0;
3311 fill_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height, fill);
3312 }
3313
3314 width = !width ? fImage->width : width;
3315 height = !height ? fImage->height : height;
3316
3317 ASImageLayer layers[2];
3318 init_image_layers(&(layers[0]), 2);
3319
3320 layers[0].im = fImage;
3321 layers[0].dst_x = 0;
3322 layers[0].dst_y = 0;
3323 layers[0].clip_width = fImage->width;
3324 layers[0].clip_height = fImage->height;
3325 layers[0].bevel = nullptr;
3326
3327 UInt_t w = width - (bevel.left_outline + bevel.right_outline);
3328 UInt_t h = height - (bevel.top_outline + bevel.bottom_outline);
3329 ASImage *bevel_im = create_asimage(w, h, 0);
3330
3331 if (!bevel_im) {
3332 Warning("Bevel", "Failed to create bevel image");
3333 return;
3334 }
3335
3336 layers[1].im = bevel_im;
3337 fill_asimage(fgVisual, bevel_im, 0, 0, w, h, fill);
3338
3339 layers[1].dst_x = x;
3340 layers[1].dst_y = y;
3341 layers[1].clip_width = width;
3342 layers[1].clip_height = height;
3343 layers[1].bevel = &bevel;
3344 layers[1].merge_scanlines = alphablend_scanlines;
3345
3346 merge_im = merge_layers(fgVisual, &(layers[0]), 2, fImage->width, fImage->height,
3347 ASA_ASImage, GetImageCompression(), GetImageQuality());
3348 destroy_asimage(&bevel_im);
3349
3350 if (!merge_im) {
3351 Warning("Bevel", "Failed to image");
3352 return;
3353 }
3354
3355 DestroyImage();
3356 fImage = merge_im;
3357 UnZoom();
3358}
3359
3360
3361////////////////////////////////////////////////////////////////////////////////
3362/// Enlarge image, padding it with specified color on each side in
3363/// accordance with requested geometry.
3364
3365void TASImage::Pad(const char *col, UInt_t l, UInt_t r, UInt_t t, UInt_t b)
3366{
3367 Int_t x, y;
3368 UInt_t w, h;
3369
3370 if (!InitVisual()) {
3371 Warning("Pad", "Visual not initiated");
3372 return;
3373 }
3374
3375 if (!fImage) {
3376 fImage = create_asimage(100, 100, 0);
3377
3378 if (!fImage) {
3379 Warning("Pad", "Failed to create image");
3380 return;
3381 }
3382
3383 fill_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height, ARGB32_White);
3384 }
3385
3386 ARGB32 color = ARGB32_White;
3387 parse_argb_color(col, &color);
3388
3389 x = l;
3390 y = t;
3391 w = l + fImage->width + r;
3392 h = t + fImage->height + b;
3393
3394 ASImage *img = pad_asimage(fgVisual, fImage, x, y, w, h, color,
3395 ASA_ASImage, GetImageCompression(), GetImageQuality());
3396
3397 if (!img) {
3398 Warning("Pad", "Failed to create output image");
3399 return;
3400 }
3401
3402 DestroyImage();
3403 fImage = img;
3404 UnZoom();
3406}
3407
3408
3409////////////////////////////////////////////////////////////////////////////////
3410/// Crop an image.
3411
3413{
3414 if (!InitVisual()) {
3415 Warning("Crop", "Visual not initiated");
3416 return;
3417 }
3418
3419 if (!fImage) {
3420 Warning("Crop", "No image");
3421 return;
3422 }
3423
3424 x = x < 0 ? 0 : x;
3425 y = y < 0 ? 0 : y;
3426
3427 width = x + width > fImage->width ? fImage->width - x : width;
3428 height = y + height > fImage->height ? fImage->height - y : height;
3429
3430 if ((width == fImage->width) && (height == fImage->height)) {
3431 Warning("Crop", "input size larger than image");
3432 return;
3433 }
3434 ASImageDecoder *imdec = start_image_decoding(fgVisual, fImage, SCL_DO_ALL,
3435 x, y, width, height, nullptr);
3436
3437 if (!imdec) {
3438 Warning("Crop", "Failed to start image decoding");
3439 return;
3440 }
3441
3442 ASImage *img = create_asimage(width, height, 0);
3443
3444 if (!img) {
3445 delete [] imdec;
3446 Warning("Crop", "Failed to create image");
3447 return;
3448 }
3449
3450 ASImageOutput *imout = start_image_output(fgVisual, img, ASA_ASImage,
3452
3453 if (!imout) {
3454 Warning("Crop", "Failed to start image output");
3455 destroy_asimage(&img);
3456 if (imdec) delete [] imdec;
3457 return;
3458 }
3459
3460#ifdef HAVE_MMX
3461 mmx_init();
3462#endif
3463
3464 for (UInt_t i = 0; i < height; i++) {
3465 imdec->decode_image_scanline(imdec);
3466 imout->output_image_scanline(imout, &(imdec->buffer), 1);
3467 }
3468
3469 stop_image_decoding(&imdec);
3470 stop_image_output(&imout);
3471
3472#ifdef HAVE_MMX
3473 mmx_off();
3474#endif
3475
3476 DestroyImage();
3477 fImage = img;
3478 UnZoom();
3480}
3481
3482////////////////////////////////////////////////////////////////////////////////
3483/// Append image.
3484///
3485/// option:
3486/// - "+" - appends to the right side
3487/// - "/" - appends to the bottom
3488
3489void TASImage::Append(const TImage *im, const char *option, const char *color )
3490{
3491 if (!im) return;
3492
3493 if (!InitVisual()) {
3494 Warning("Append", "Visual not initiated");
3495 return;
3496 }
3497
3498 if (!fImage) {
3499 fImage = ((TASImage*)im)->fImage;
3500 return;
3501 }
3502
3503 TString opt = option;
3504 opt.Strip();
3505
3506 UInt_t width = fImage->width;
3507 UInt_t height = fImage->height;
3508
3509 if (opt == "+") {
3510 Pad(color, 0, im->GetWidth(), 0, 0);
3511 Merge(im, "alphablend", width, 0);
3512 } else if (opt == "/") {
3513 Pad(color, 0, 0, 0, im->GetHeight());
3514 Merge(im, "alphablend", 0, height);
3515 } else {
3516 return;
3517 }
3518
3519 UnZoom();
3520}
3521
3522////////////////////////////////////////////////////////////////////////////////
3523/// BeginPaint initializes internal array[width x height] of ARGB32 pixel
3524/// values.
3525///
3526/// That provides quick access to image during paint operations.
3527/// To RLE compress image one needs to call EndPaint method when painting
3528/// is over.
3529
3531{
3532 if (!InitVisual()) {
3533 Warning("BeginPaint", "Visual not initiated");
3534 return;
3535 }
3536
3537 if (!fImage) {
3538 return;
3539 }
3540
3541 fPaintMode = mode;
3542
3543 if (!fPaintMode || fImage->alt.argb32) {
3544 return;
3545 }
3546
3547 ASImage *img = tile_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height,
3548 0, ASA_ARGB32, 0, ASIMAGE_QUALITY_DEFAULT);
3549
3550 if (!img) {
3551 Warning("BeginPaint", "Failed to create image");
3552 return;
3553 }
3554
3555 DestroyImage();
3556 fImage = img;
3557}
3558
3559////////////////////////////////////////////////////////////////////////////////
3560/// EndPaint does internal RLE compression of image data.
3561
3563{
3564 if (!fImage) {
3565 Warning("EndPaint", "no image");
3566 return;
3567 }
3568
3569 if (!fImage->alt.argb32) return;
3570
3571 ASImage *img = tile_asimage(fgVisual, fImage, 0, 0, fImage->width, fImage->height,
3572 0, ASA_ASImage, 0, ASIMAGE_QUALITY_DEFAULT);
3573
3574 if (!img) {
3575 Warning("EndPaint", "Failed to create image");
3576 return;
3577 }
3578
3580 DestroyImage();
3581 fImage = img;
3582}
3583
3584////////////////////////////////////////////////////////////////////////////////
3585/// Return a pointer to internal array[width x height] of ARGB32 values
3586/// This array is directly accessible. That allows to manipulate/change the
3587/// image.
3588
3590{
3591 if (!fImage) {
3592 Warning("GetArgbArray", "no image");
3593 return nullptr;
3594 }
3595
3596 ASImage *img = fScaledImage ? fScaledImage->fImage : fImage;
3597 if (!img) return nullptr;
3598
3599 if (!img->alt.argb32) {
3600 if (fScaledImage) {
3602 img = fScaledImage->fImage;
3603 } else {
3604 BeginPaint();
3605 img = fImage;
3606 }
3607 }
3608
3609 return (UInt_t *)img->alt.argb32;
3610}
3611
3612////////////////////////////////////////////////////////////////////////////////
3613/// Return a pointer to an array[width x height] of RGBA32 values.
3614/// This array is created from internal ARGB32 array,
3615/// must be deleted after usage.
3616
3618{
3619 if (!fImage) {
3620 Warning("GetRgbaArray", "no image");
3621 return nullptr;
3622 }
3623
3624 ASImage *img = fScaledImage ? fScaledImage->fImage : fImage;
3625 if (!img) return nullptr;
3626
3627 if (!img->alt.argb32) {
3628 if (fScaledImage) {
3630 img = fScaledImage->fImage;
3631 } else {
3632 BeginPaint();
3633 img = fImage;
3634 }
3635 }
3636
3637 UInt_t i, j;
3638 Int_t y = 0;
3639 Int_t idx = 0;
3640 UInt_t a, rgb, rgba, argb;
3641
3642 UInt_t *ret = new UInt_t[img->width*img->height];
3643
3644 for (i = 0; i < img->height; i++) {
3645 for (j = 0; j < img->width; j++) {
3646 idx = Idx(y + j);
3647 argb = img->alt.argb32[idx];
3648 a = argb >> 24;
3649 rgb = argb & 0x00ffffff;
3650 rgba = (rgb << 8) + a;
3651 ret[idx] = rgba;
3652 }
3653 y += img->width;
3654 }
3655
3656 return ret;
3657}
3658
3659////////////////////////////////////////////////////////////////////////////////
3660/// Return a pointer to scan-line.
3661
3663{
3664 if (!fImage) {
3665 Warning("GetScanline", "no image");
3666 return nullptr;
3667 }
3668
3669 ASImage *img = fScaledImage ? fScaledImage->fImage : fImage;
3670 CARD32 *ret = new CARD32[img->width];
3671
3672 ASImageDecoder *imdec = start_image_decoding(fgVisual, img, SCL_DO_ALL,
3673 0, y, img->width, 1, nullptr);
3674
3675 if (!imdec) {
3676 delete [] ret;
3677 Warning("GetScanline", "Failed to start image decoding");
3678 return nullptr;
3679 }
3680
3681#ifdef HAVE_MMX
3682 mmx_init();
3683#endif
3684
3685 imdec->decode_image_scanline(imdec);
3686 memcpy(imdec->buffer.buffer, ret, img->width*sizeof(CARD32));
3687 stop_image_decoding(&imdec);
3688
3689#ifdef HAVE_MMX
3690 mmx_off();
3691#endif
3692
3693 return (UInt_t*)ret;
3694}
3695
3696
3697//______________________________________________________________________________
3698//
3699// Vector graphics
3700// a couple of macros which can be "assembler accelerated"
3701
3702#if defined(R__GNU) && defined(__i386__) && !defined(__sun)
3703#define _MEMSET_(dst, lng, val) __asm__("movl %0,%%eax \n"\
3704 "movl %1,%%edi \n" \
3705 "movl %2,%%ecx \n" \
3706 "cld \n" \
3707 "rep \n" \
3708 "stosl \n" \
3709 : /* no output registers */ \
3710 :"g" (val), "g" (dst), "g" (lng) \
3711 :"eax","edi","ecx" \
3712 )
3713
3714#else
3715 #define _MEMSET_(dst, lng, val) do {\
3716 for( UInt_t j=0; j < lng; j++) *((dst)+j) = val; } while (0)
3717
3718#endif
3719
3720#define FillSpansInternal(npt, ppt, widths, color) do {\
3721 UInt_t yy = ppt[0].fY*fImage->width;\
3722 for (UInt_t i = 0; i < npt; i++) {\
3723 _MEMSET_(&fImage->alt.argb32[Idx(yy + ppt[i].fX)], widths[i], color);\
3724 yy += ((i+1 < npt) && (ppt[i].fY != ppt[i+1].fY) ? fImage->width : 0);\
3725 }\
3726} while (0)
3727
3728////////////////////////////////////////////////////////////////////////////////
3729/// Fill rectangle of size (width, height) at position (x,y)
3730/// within the existing image with specified color.
3731
3733{
3734
3735 if (!InitVisual()) {
3736 Warning("FillRectangle", "Visual not initiated");
3737 return;
3738 }
3739
3740 if (!fImage) {
3741 Warning("FillRectangle", "no image");
3742 return;
3743 }
3744
3745 if (!fImage->alt.argb32) {
3746 BeginPaint();
3747 }
3748
3749 if (!fImage->alt.argb32) {
3750 Warning("FillRectangle", "Failed to get pixel array");
3751 return;
3752 }
3753
3754 ARGB32 color = (ARGB32)col;
3755
3756 if (width == 0) width = 1;
3757 if (height == 0) height = 1;
3758
3759 if (x < 0) {
3760 width += x;
3761 x = 0;
3762 }
3763 if (y < 0) {
3764 height += y;
3765 y = 0;
3766 }
3767
3768 Bool_t has_alpha = (color & 0xff000000) != 0xff000000;
3769
3770 x = x > (int)fImage->width ? (Int_t)fImage->width : x;
3771 y = y > (int)fImage->height ? (Int_t)fImage->height : y;
3772
3773 width = x + width > fImage->width ? fImage->width - x : width;
3774 height = y + height > fImage->height ? fImage->height - y : height;
3775
3776 if (!fImage->alt.argb32) {
3777 fill_asimage(fgVisual, fImage, x, y, width, height, color);
3778 } else {
3779 int yyy = y*fImage->width;
3780 if (!has_alpha) { // use faster memset
3781 ARGB32 *p0 = fImage->alt.argb32 + yyy + x;
3782 ARGB32 *p = p0;
3783 for (UInt_t i = 0; i < height; i++) {
3784 _MEMSET_(p, width, color);
3785 p += fImage->width;
3786 }
3787 } else {
3788 for (UInt_t i = y; i < y + height; i++) {
3789 int j = x + width;
3790 while (j > x) {
3791 j--;
3792 _alphaBlend(&fImage->alt.argb32[Idx(yyy + j)], &color);
3793 }
3794 yyy += fImage->width;
3795 }
3796 }
3797 }
3798}
3799
3800////////////////////////////////////////////////////////////////////////////////
3801/// Fill rectangle of size (width, height) at position (x,y)
3802/// within the existing image with specified color.
3803///
3804/// To create new image with Fill method the following code can be used:
3805/// ~~~ {.cpp}
3806/// TImage *img = TImage::Create();
3807/// img->Fill("#FF00FF", 0, 0, 400, 300);
3808/// ~~~
3809
3811{
3812 if (!InitVisual()) {
3813 Warning("Fill", "Visual not initiated");
3814 return;
3815 }
3816
3817 ARGB32 color = ARGB32_White;
3818
3819 if (col) {
3820 parse_argb_color(col, &color);
3821 }
3822
3823 if (!fImage) {
3824 fImage = create_asimage(width ? width : 20, height ? height : 20, 0);
3825 x = 0;
3826 y = 0;
3827 }
3828
3830 UnZoom();
3831}
3832
3833////////////////////////////////////////////////////////////////////////////////
3834/// Draw a vertical line.
3835
3837{
3838 ARGB32 color = (ARGB32)col;
3839 UInt_t half = 0;
3840
3841 if (!thick) thick = 1;
3842
3843 if (thick > 1) {
3844 half = thick >> 1;
3845 if (x > half) {
3846 x = x - half;
3847 } else {
3848 x = 0;
3849 thick += (x - half);
3850 }
3851 }
3852
3853 y2 = y2 >= fImage->height ? fImage->height - 1 : y2;
3854 y1 = y1 >= fImage->height ? fImage->height - 1 : y1;
3855 x = x + thick >= fImage->width ? fImage->width - thick - 1 : x;
3856
3857 int yy = y1*fImage->width;
3858 for (UInt_t y = y1; y <= y2; y++) {
3859 for (UInt_t w = 0; w < thick; w++) {
3860 if (x + w < fImage->width) {
3861 _alphaBlend(&fImage->alt.argb32[Idx(yy + (x + w))], &color);
3862 }
3863 }
3864 yy += fImage->width;
3865 }
3866}
3867
3868////////////////////////////////////////////////////////////////////////////////
3869/// Draw an horizontal line.
3870
3872{
3873 ARGB32 color = (ARGB32)col;
3874 UInt_t half = 0;
3875
3876 if (!thick) thick = 1;
3877
3878 if (thick > 1) {
3879 half = thick >> 1;
3880 if (y > half) {
3881 y = y - half;
3882 } else {
3883 y = 0;
3884 thick += (y - half);
3885 }
3886 }
3887
3888 y = y + thick >= fImage->height ? fImage->height - thick - 1 : y;
3889 x2 = x2 >= fImage->width ? fImage->width - 1 : x2;
3890 x1 = x1 >= fImage->width ? fImage->width - 1 : x1;
3891
3892 int yy = y*fImage->width;
3893 for (UInt_t w = 0; w < thick; w++) {
3894 for (UInt_t x = x1; x <= x2; x++) {
3895 if (y + w < fImage->height) {
3896 _alphaBlend(&fImage->alt.argb32[Idx(yy + x)], &color);
3897 }
3898 }
3899 yy += fImage->width;
3900 }
3901}
3902
3903////////////////////////////////////////////////////////////////////////////////
3904/// Draw a line.
3905
3907 const char *col, UInt_t thick)
3908{
3909 ARGB32 color = ARGB32_White;
3910 parse_argb_color(col, &color);
3911 DrawLineInternal(x1, y1, x2, y2, (UInt_t)color, thick);
3912}
3913
3914////////////////////////////////////////////////////////////////////////////////
3915/// Internal line drawing.
3916
3918 UInt_t col, UInt_t thick)
3919{
3920 int dx, dy, d;
3921 int i1, i2;
3922 int x, y, xend, yend;
3923 int xdir, ydir;
3924 int q;
3925 int idx;
3926 int yy;
3927
3928 if (!InitVisual()) {
3929 Warning("DrawLine", "Visual not initiated");
3930 return;
3931 }
3932
3933 if (!fImage) {
3934 Warning("DrawLine", "no image");
3935 return;
3936 }
3937
3938 if (!fImage->alt.argb32) {
3939 BeginPaint();
3940 }
3941
3942 if (!fImage->alt.argb32) {
3943 Warning("DrawLine", "Failed to get pixel array");
3944 return;
3945 }
3946
3947 ARGB32 color = (ARGB32)col;
3948
3949 dx = TMath::Abs(Int_t(x2) - Int_t(x1));
3950 dy = TMath::Abs(Int_t(y2) - Int_t(y1));
3951
3952 if (!dx && !dy) return; // invisible line
3953
3954 if (!dx) {
3955 DrawVLine(x1, y2 > y1 ? y1 : y2,
3956 y2 > y1 ? y2 : y1, color, thick);
3957 return;
3958 }
3959
3960 if (!dy) {
3961 DrawHLine(y1, x2 > x1 ? x1 : x2,
3962 x2 > x1 ? x2 : x1, color, thick);
3963 return;
3964 }
3965
3966 if (thick > 1) {
3967 DrawWideLine(x1, y1, x2, y2, color, thick);
3968 return;
3969 }
3970
3971 if (dy <= dx) {
3972 UInt_t ddy = dy << 1;
3973 i1 = ddy;
3974 i2 = i1 - (dx << 1);
3975 d = i1 - dx;
3976
3977 if (x1 > x2) {
3978 x = x2;
3979 y = y2;
3980 ydir = -1;
3981 xend = x1;
3982 } else {
3983 x = x1;
3984 y = y1;
3985 ydir = 1;
3986 xend = x2;
3987 }
3988
3989 yy = y*fImage->width;
3990 _alphaBlend(&fImage->alt.argb32[Idx(yy + x)], &color);
3991 q = (y2 - y1) * ydir;
3992
3993 if (q > 0) {
3994 while (x < xend) {
3995
3996 idx = Idx(yy + x);
3997 _alphaBlend(&fImage->alt.argb32[idx], &color);
3998 x++;
3999
4000 if (d >= 0) {
4001 yy += fImage->width;
4002 d += i2;
4003 } else {
4004 d += i1;
4005 }
4006 }
4007 } else {
4008 while (x < xend) {
4009 idx = Idx(yy + x);
4010 _alphaBlend(&fImage->alt.argb32[idx], &color);
4011 x++;
4012
4013 if (d >= 0) {
4014 yy -= fImage->width;
4015 d += i2;
4016 } else {
4017 d += i1;
4018 }
4019 }
4020 }
4021 } else {
4022 UInt_t ddx = dx << 1;
4023 i1 = ddx;
4024 i2 = i1 - (dy << 1);
4025 d = i1 - dy;
4026
4027 if (y1 > y2) {
4028 y = y2;
4029 x = x2;
4030 yend = y1;
4031 xdir = -1;
4032 } else {
4033 y = y1;
4034 x = x1;
4035 yend = y2;
4036 xdir = 1;
4037 }
4038
4039 yy = y*fImage->width;
4040 _alphaBlend(&fImage->alt.argb32[Idx(yy + x)], &color);
4041 q = (x2 - x1) * xdir;
4042
4043 if (q > 0) {
4044 while (y < yend) {
4045 idx = Idx(yy + x);
4046 _alphaBlend(&fImage->alt.argb32[idx], &color);
4047 y++;
4048 yy += fImage->width;
4049
4050 if (d >= 0) {
4051 x++;
4052 d += i2;
4053 } else {
4054 d += i1;
4055 }
4056 }
4057 } else {
4058 while (y < yend) {
4059 idx = Idx(yy + x);
4060 _alphaBlend(&fImage->alt.argb32[idx], &color);
4061 y++;
4062 yy += fImage->width;
4063
4064 if (d >= 0) {
4065 x--;
4066 d += i2;
4067 } else {
4068 d += i1;
4069 }
4070 }
4071 }
4072 }
4073}
4074
4075////////////////////////////////////////////////////////////////////////////////
4076/// Draw a rectangle.
4077
4079 const char *col, UInt_t thick)
4080{
4081 if (!InitVisual()) {
4082 Warning("DrawRectangle", "Visual not initiated");
4083 return;
4084 }
4085
4086 if (!fImage) {
4087 w = w ? w : 20;
4088 h = h ? h : 20;
4089 fImage = create_asimage(w, h, 0);
4090 FillRectangle(col, 0, 0, w, h);
4091 return;
4092 }
4093
4094 if (!fImage->alt.argb32) {
4095 BeginPaint();
4096 }
4097
4098 if (!fImage->alt.argb32) {
4099 Warning("DrawRectangle", "Failed to get pixel array");
4100 return;
4101 }
4102
4103 ARGB32 color = ARGB32_White;
4104 parse_argb_color(col, &color);
4105
4106 DrawHLine(y, x, x + w, (UInt_t)color, thick);
4107 DrawVLine(x + w, y, y + h, (UInt_t)color, thick);
4108 DrawHLine(y + h, x, x + w, (UInt_t)color, thick);
4109 DrawVLine(x, y, y + h, (UInt_t)color, thick);
4110 UnZoom();
4111}
4112
4113////////////////////////////////////////////////////////////////////////////////
4114/// Draw a box.
4115
4116void TASImage::DrawBox(Int_t x1, Int_t y1, Int_t x2, Int_t y2, const char *col,
4117 UInt_t thick, Int_t mode)
4118{
4119 Int_t x = TMath::Min(x1, x2);
4120 Int_t y = TMath::Min(y1, y2);
4121 Int_t w = TMath::Abs(x2 - x1);
4122 Int_t h = TMath::Abs(y2 - y1);
4123
4124 ARGB32 color = ARGB32_White;
4125
4126 if (!fImage) {
4127 w = w ? x+w : x+20;
4128 h = h ? y+h : y+20;
4129 fImage = create_asimage(w, h, 0);
4130 FillRectangle(col, 0, 0, w, h);
4131 return;
4132 }
4133
4134 if (x1 == x2) {
4135 parse_argb_color(col, &color);
4136 DrawVLine(x1, y1, y2, color, 1);
4137 return;
4138 }
4139
4140 if (y1 == y2) {
4141 parse_argb_color(col, &color);
4142 DrawHLine(y1, x1, x2, color, 1);
4143 return;
4144 }
4145
4146
4147 switch (mode) {
4148 case TVirtualX::kHollow:
4149 DrawRectangle(x, y, w, h, col, thick);
4150 break;
4151
4152 case TVirtualX::kFilled:
4153 FillRectangle(col, x, y, w, h);
4154 break;
4155
4156 default:
4157 FillRectangle(col, x, y, w, h);
4158 break;
4159 }
4160}
4161
4162////////////////////////////////////////////////////////////////////////////////
4163/// Draw a dashed horizontal line.
4164
4166 const char *pDash, UInt_t col, UInt_t thick)
4167{
4168 UInt_t iDash = 0; // index of current dash
4169 int i = 0;
4170
4171 ARGB32 color = (ARGB32)col;
4172
4173 UInt_t half = 0;
4174
4175 if (thick > 1) {
4176 half = thick >> 1;
4177 if (y > half) {
4178 y = y - half;
4179 } else {
4180 y = 0;
4181 thick += (y - half);
4182 }
4183 }
4184 thick = thick <= 0 ? 1 : thick;
4185
4186 y = y + thick >= fImage->height ? fImage->height - thick - 1 : y;
4187 x2 = x2 >= fImage->width ? fImage->width - 1 : x2;
4188 x1 = x1 >= fImage->width ? fImage->width - 1 : x1;
4189
4190 // switch x1, x2
4191 UInt_t tmp = x1;
4192 x1 = x2 < x1 ? x2 : x1;
4193 x2 = x2 < tmp ? tmp : x2;
4194
4195 for (UInt_t x = x1; x <= x2; x++) {
4196 for (UInt_t w = 0; w < thick; w++) {
4197 if (y + w < fImage->height) {
4198 if ((iDash%2)==0) {
4199 _alphaBlend(&fImage->alt.argb32[Idx((y + w)*fImage->width + x)], &color);
4200 }
4201 }
4202 }
4203 i++;
4204
4205 if (i >= pDash[iDash]) {
4206 iDash++;
4207 i = 0;
4208 }
4209 if (iDash >= nDash) {
4210 iDash = 0;
4211 i = 0;
4212 }
4213 }
4214}
4215
4216////////////////////////////////////////////////////////////////////////////////
4217/// Draw a dashed vertical line.
4218
4220 const char *pDash, UInt_t col, UInt_t thick)
4221{
4222 UInt_t iDash = 0; // index of current dash
4223 int i = 0;
4224
4225 ARGB32 color = (ARGB32)col;
4226
4227 UInt_t half = 0;
4228
4229 if (thick > 1) {
4230 half = thick >> 1;
4231 if (x > half) {
4232 x = x - half;
4233 } else {
4234 x = 0;
4235 thick += (x - half);
4236 }
4237 }
4238 thick = thick <= 0 ? 1 : thick;
4239
4240 y2 = y2 >= fImage->height ? fImage->height - 1 : y2;
4241 y1 = y1 >= fImage->height ? fImage->height - 1 : y1;
4242
4243 // switch x1, x2
4244 UInt_t tmp = y1;
4245 y1 = y2 < y1 ? y2 : y1;
4246 y2 = y2 < tmp ? tmp : y2;
4247
4248 x = x + thick >= fImage->width ? fImage->width - thick - 1 : x;
4249
4250 int yy = y1*fImage->width;
4251 for (UInt_t y = y1; y <= y2; y++) {
4252 for (UInt_t w = 0; w < thick; w++) {
4253 if (x + w < fImage->width) {
4254 if ((iDash%2)==0) {
4255 _alphaBlend(&fImage->alt.argb32[Idx(yy + (x + w))], &color);
4256 }
4257 }
4258 }
4259 i++;
4260
4261 if (i >= pDash[iDash]) {
4262 iDash++;
4263 i = 0;
4264 }
4265 if (iDash >= nDash) {
4266 iDash = 0;
4267 i = 0;
4268 }
4269 yy += fImage->width;
4270 }
4271}
4272
4273////////////////////////////////////////////////////////////////////////////////
4274/// Draw a dashed line with one pixel width.
4275
4277 UInt_t nDash, const char *tDash, UInt_t color)
4278{
4279 int dx, dy, d;
4280 int i, i1, i2;
4281 int x, y, xend, yend;
4282 int xdir, ydir;
4283 int q;
4284 UInt_t iDash = 0; // index of current dash
4285 int yy;
4286 int idx;
4287
4288 dx = TMath::Abs(Int_t(x2) - Int_t(x1));
4289 dy = TMath::Abs(Int_t(y2) - Int_t(y1));
4290
4291 char *pDash = new char[nDash];
4292
4293 if (dy <= dx) {
4294 double ac = TMath::Cos(TMath::ATan2(dy, dx));
4295
4296 for (i = 0; i < (int)nDash; i++) {
4297 pDash[i] = TMath::Nint(tDash[i] * ac);
4298 }
4299
4300 UInt_t ddy = dy << 1;
4301 i1 = ddy;
4302 i2 = i1 - (dx << 1);
4303 d = i1 - dx;
4304 i = 0;
4305
4306 if (x1 > x2) {
4307 x = x2;
4308 y = y2;
4309 ydir = -1;
4310 xend = x1;
4311 } else {
4312 x = x1;
4313 y = y1;
4314 ydir = 1;
4315 xend = x2;
4316 }
4317
4318 yy = y*fImage->width;
4319 _alphaBlend(&fImage->alt.argb32[Idx(y*fImage->width + x)], &color);
4320 q = (y2 - y1) * ydir;
4321
4322 if (q > 0) {
4323 while (x < xend) {
4324 idx = Idx(yy + x);
4325 if ((iDash%2) == 0) {
4326 _alphaBlend(&fImage->alt.argb32[idx], &color);
4327 }
4328 x++;
4329 if (d >= 0) {
4330 yy += fImage->width;
4331 d += i2;
4332 } else {
4333 d += i1;
4334 }
4335
4336 i++;
4337 if (i >= pDash[iDash]) {
4338 iDash++;
4339 i = 0;
4340 }
4341 if (iDash >= nDash) {
4342 iDash = 0;
4343 i = 0;
4344 }
4345 }
4346 } else {
4347 while (x < xend) {
4348 idx = Idx(yy + x);
4349 if ((iDash%2) == 0) {
4350 _alphaBlend(&fImage->alt.argb32[idx], &color);
4351 }
4352 x++;
4353 if (d >= 0) {
4354 yy -= fImage->width;
4355 d += i2;
4356 } else {
4357 d += i1;
4358 }
4359
4360 i++;
4361 if (i >= pDash[iDash]) {
4362 iDash++;
4363 i = 0;
4364 }
4365 if (iDash >= nDash) {
4366 iDash = 0;
4367 i = 0;
4368 }
4369 }
4370 }
4371 } else {
4372 double as = TMath::Sin(TMath::ATan2(dy, dx));
4373
4374 for (i = 0; i < (int)nDash; i++) {
4375 pDash[i] = TMath::Nint(tDash[i] * as);
4376 }
4377
4378 UInt_t ddx = dx << 1;
4379 i1 = ddx;
4380 i2 = i1 - (dy << 1);
4381 d = i1 - dy;
4382 i = 0;
4383
4384 if (y1 > y2) {
4385 y = y2;
4386 x = x2;
4387 yend = y1;
4388 xdir = -1;
4389 } else {
4390 y = y1;
4391 x = x1;
4392 yend = y2;
4393 xdir = 1;
4394 }
4395
4396 yy = y*fImage->width;
4397 _alphaBlend(&fImage->alt.argb32[Idx(y*fImage->width + x)], &color);
4398 q = (x2 - x1) * xdir;
4399
4400 if (q > 0) {
4401 while (y < yend) {
4402 idx = Idx(yy + x);
4403 if ((iDash%2) == 0) {
4404 _alphaBlend(&fImage->alt.argb32[idx], &color);
4405 }
4406 y++;
4407 yy += fImage->width;
4408
4409 if (d >= 0) {
4410 x++;
4411 d += i2;
4412 } else {
4413 d += i1;
4414 }
4415
4416 i++;
4417 if (i >= pDash[iDash]) {
4418 iDash++;
4419 i = 0;
4420 }
4421 if (iDash >= nDash) {
4422 iDash = 0;
4423 i = 0;
4424 }
4425 }
4426 } else {
4427 while (y < yend) {
4428 idx = Idx(yy + x);
4429 if ((iDash%2) == 0) {
4430 _alphaBlend(&fImage->alt.argb32[idx], &color);
4431 }
4432 y++;
4433 yy += fImage->width;
4434
4435 if (d >= 0) {
4436 x--;
4437 d += i2;
4438 } else {
4439 d += i1;
4440 }
4441
4442 i++;
4443 if (i >= pDash[iDash]) {
4444 iDash++;
4445 i = 0;
4446 }
4447 if (iDash >= nDash) {
4448 iDash = 0;
4449 i = 0;
4450 }
4451 }
4452 }
4453 }
4454 delete [] pDash;
4455}
4456
4457////////////////////////////////////////////////////////////////////////////////
4458/// Draw a dashed line with thick pixel width.
4459
4461 UInt_t nDash, const char *tDash, UInt_t color, UInt_t thick)
4462{
4463 int dx, dy;
4464 int i;
4465 double x, y, xend=0, yend=0, x0, y0;
4466 int xdir, ydir;
4467 int q;
4468 UInt_t iDash = 0; // index of current dash
4469
4470 dx = TMath::Abs(Int_t(x2) - Int_t(x1));
4471 dy = TMath::Abs(Int_t(y2) - Int_t(y1));
4472
4473 double *xDash = new double[nDash];
4474 double *yDash = new double[nDash];
4475 double a = TMath::ATan2(dy, dx);
4476 double ac = TMath::Cos(a);
4477 double as = TMath::Sin(a);
4478
4479 for (i = 0; i < (int)nDash; i++) {
4480 xDash[i] = tDash[i] * ac;
4481 yDash[i] = tDash[i] * as;
4482
4483 // dirty trick (must be fixed)
4484 if ((i%2) == 0) {
4485 xDash[i] = xDash[i]/2;
4486 yDash[i] = yDash[i]/2;
4487 } else {
4488 xDash[i] = xDash[i]*2;
4489 yDash[i] = yDash[i]*2;
4490 }
4491 }
4492
4493 if (dy <= dx) {
4494 if (x1 > x2) {
4495 x = x2;
4496 y = y2;
4497 ydir = -1;
4498 xend = x1;
4499 } else {
4500 x = x1;
4501 y = y1;
4502 ydir = 1;
4503 xend = x2;
4504 }
4505
4506 q = (y2 - y1) * ydir;
4507 x0 = x;
4508 y0 = y;
4509 iDash = 0;
4510 yend = y + q;
4511
4512 if (q > 0) {
4513 while ((x < xend) && (y < yend)) {
4514 x += xDash[iDash];
4515 y += yDash[iDash];
4516
4517 if ((iDash%2) == 0) {
4519 TMath::Nint(x), TMath::Nint(y), color, thick);
4520 } else {
4521 x0 = x;
4522 y0 = y;
4523 }
4524
4525 iDash++;
4526
4527 if (iDash >= nDash) {
4528 iDash = 0;
4529 }
4530 }
4531 } else {
4532 while ((x < xend) && (y > yend)) {
4533 x += xDash[iDash];
4534 y -= yDash[iDash];
4535
4536 if ((iDash%2) == 0) {
4538 TMath::Nint(x), TMath::Nint(y), color, thick);
4539 } else {
4540 x0 = x;
4541 y0 = y;
4542 }
4543
4544 iDash++;
4545
4546 if (iDash >= nDash) {
4547 iDash = 0;
4548 }
4549 }
4550 }
4551 } else {
4552
4553 if (y1 > y2) {
4554 y = y2;
4555 x = x2;
4556 yend = y1;
4557 xdir = -1;
4558 } else {
4559 y = y1;
4560 x = x1;
4561 yend = y2;
4562 xdir = 1;
4563 }
4564
4565 q = (x2 - x1) * xdir;
4566 x0 = x;
4567 y0 = y;
4568 iDash = 0;
4569 xend = x + q;
4570
4571 if (q > 0) {
4572 while ((x < xend) && (y < yend)) {
4573 x += xDash[iDash];
4574 y += yDash[iDash];
4575
4576 if ((iDash%2) == 0) {
4578 TMath::Nint(x), TMath::Nint(y), color, thick);
4579 } else {
4580 x0 = x;
4581 y0 = y;
4582 }
4583
4584 iDash++;
4585
4586 if (iDash >= nDash) {
4587 iDash = 0;
4588 }
4589 }
4590 } else {
4591 while ((x > xend) && (y < yend)) {
4592 x -= xDash[iDash];
4593 y += yDash[iDash];
4594
4595 if ((iDash%2) == 0) {
4597 TMath::Nint(x), TMath::Nint(y), color, thick);
4598 } else {
4599 x0 = x;
4600 y0 = y;
4601 }
4602
4603 iDash++;
4604
4605 if (iDash >= nDash) {
4606 iDash = 0;
4607 }
4608 }
4609 }
4610 }
4611 delete [] xDash;
4612 delete [] yDash;
4613}
4614
4615////////////////////////////////////////////////////////////////////////////////
4616/// Draw a dashed line.
4617
4619 const char *pDash, const char *col, UInt_t thick)
4620
4621{
4622 if (!InitVisual()) {
4623 Warning("DrawDashLine", "Visual not initiated");
4624 return;
4625 }
4626
4627 if (!fImage) {
4628 Warning("DrawDashLine", "no image");
4629 return;
4630 }
4631
4632 if (!fImage->alt.argb32) {
4633 BeginPaint();
4634 }
4635
4636 if (!fImage->alt.argb32) {
4637 Warning("DrawDashLine", "Failed to get pixel array");
4638 return;
4639 }
4640
4641 if ((nDash < 2) || !pDash || (nDash%2)) {
4642 Warning("DrawDashLine", "Wrong input parameters n=%d %ld", nDash, (Long_t)sizeof(pDash)-1);
4643 return;
4644 }
4645
4646 ARGB32 color = ARGB32_White;
4647 parse_argb_color(col, &color);
4648
4649 if (x1 == x2) {
4650 DrawDashVLine(x1, y1, y2, nDash, pDash, (UInt_t)color, thick);
4651 } else if (y1 == y2) {
4652 DrawDashHLine(y1, x1, x2, nDash, pDash, (UInt_t)color, thick);
4653 } else {
4654 if (thick < 2) DrawDashZLine(x1, y1, x2, y2, nDash, pDash, (UInt_t)color);
4655 else DrawDashZTLine(x1, y1, x2, y2, nDash, pDash, (UInt_t)color, thick);
4656 }
4657}
4658
4659////////////////////////////////////////////////////////////////////////////////
4660/// Draw a polyline.
4661
4662void TASImage::DrawPolyLine(UInt_t nn, TPoint *xy, const char *col, UInt_t thick,
4664{
4665 ARGB32 color = ARGB32_White;
4666 parse_argb_color(col, &color);
4667
4668 Int_t x0 = xy[0].GetX();
4669 Int_t y0 = xy[0].GetY();
4670 Int_t x = 0;
4671 Int_t y = 0;
4672
4673 for (UInt_t i = 1; i < nn; i++) {
4674 x = (mode == kCoordModePrevious) ? x + xy[i].GetX() : xy[i].GetX();
4675 y = (mode == kCoordModePrevious) ? y + xy[i].GetY() : xy[i].GetY();
4676
4677 DrawLineInternal(x0, y0, x, y, (UInt_t)color, thick);
4678
4679 x0 = x;
4680 y0 = y;
4681 }
4682}
4683
4684////////////////////////////////////////////////////////////////////////////////
4685/// Draw a point at the specified position.
4686
4687void TASImage::PutPixel(Int_t x, Int_t y, const char *col)
4688{
4689 if (!InitVisual()) {
4690 Warning("PutPixel", "Visual not initiated");
4691 return;
4692 }
4693
4694 if (!fImage) {
4695 Warning("PutPixel", "no image");
4696 return;
4697 }
4698
4699 if (!fImage->alt.argb32) {
4700 BeginPaint();
4701 }
4702
4703 if (!fImage->alt.argb32) {
4704 Warning("PutPixel", "Failed to get pixel array");
4705 return;
4706 }
4707
4708 ARGB32 color;
4709 parse_argb_color(col, &color);
4710
4711 if ((x < 0) || (y < 0) || (x >= (int)fImage->width) || (y >= (int)fImage->height)) {
4712 Warning("PutPixel", "Out of range width=%d x=%d, height=%d y=%d",
4713 fImage->width, x, fImage->height, y);
4714 return;
4715 }
4716 _alphaBlend(&fImage->alt.argb32[Idx(y*fImage->width + x)], &color);
4717}
4718
4719////////////////////////////////////////////////////////////////////////////////
4720/// Draw a poly point.
4721
4723{
4724 if (!InitVisual()) {
4725 Warning("PolyPoint", "Visual not initiated");
4726 return;
4727 }
4728
4729 if (!fImage) {
4730 Warning("PolyPoint", "no image");
4731 return;
4732 }
4733
4734 if (!fImage->alt.argb32) {
4735 BeginPaint();
4736 }
4737
4738 if (!fImage->alt.argb32) {
4739 Warning("PolyPoint", "Failed to get pixel array");
4740 return;
4741 }
4742
4743 if (!npt || !ppt) {
4744 Warning("PolyPoint", "No points specified");
4745 return;
4746 }
4747
4748 TPoint *ipt = nullptr;
4749 UInt_t i = 0;
4750 ARGB32 color;
4751 parse_argb_color(col, &color);
4752
4753 //make pointlist origin relative
4754 if (mode == kCoordModePrevious) {
4755 ipt = new TPoint[npt];
4756
4757 for (i = 0; i < npt; i++) {
4758 ipt[i].fX += ppt[i].fX;
4759 ipt[i].fY += ppt[i].fY;
4760 }
4761 }
4762 int x, y;
4763
4764 for (i = 0; i < npt; i++) {
4765 x = ipt ? ipt[i].fX : ppt[i].fX;
4766 y = ipt ? ipt[i].fY : ppt[i].fY;
4767
4768 if ((x < 0) || (y < 0) || (x >= (int)fImage->width) || (y >= (int)fImage->height)) {
4769 continue;
4770 }
4771 _alphaBlend(&fImage->alt.argb32[Idx(y*fImage->width + x)], &color);
4772 }
4773
4774 if (ipt) {
4775 delete [] ipt;
4776 }
4777}
4778
4779////////////////////////////////////////////////////////////////////////////////
4780/// Draw segments.
4781
4782void TASImage::DrawSegments(UInt_t nseg, Segment_t *seg, const char *col, UInt_t thick)
4783{
4784 if (!nseg || !seg) {
4785 Warning("DrawSegments", "Invalid data nseg=%d seg=0x%zx", nseg, (size_t)seg);
4786 return;
4787 }
4788
4789 TPoint pt[2];
4790
4791 for (UInt_t i = 0; i < nseg; i++) {
4792 pt[0].fX = seg->fX1;
4793 pt[1].fX = seg->fX2;
4794 pt[0].fY = seg->fY1;
4795 pt[1].fY = seg->fY2;
4796
4797 DrawPolyLine(2, pt, col, thick, kCoordModeOrigin);
4798 seg++;
4799 }
4800}
4801
4802////////////////////////////////////////////////////////////////////////////////
4803/// Fill spans with specified color or/and stipple.
4804
4805void TASImage::FillSpans(UInt_t npt, TPoint *ppt, UInt_t *widths, const char *col,
4806 const char *stipple, UInt_t w, UInt_t h)
4807{
4808 if (!InitVisual()) {
4809 Warning("FillSpans", "Visual not initiated");
4810 return;
4811 }
4812
4813 if (!fImage) {
4814 Warning("FillSpans", "no image");
4815 return;
4816 }
4817
4818 if (!fImage->alt.argb32) {
4819 BeginPaint();
4820 }
4821
4822 if (!fImage->alt.argb32) {
4823 Warning("FillSpans", "Failed to get pixel array");
4824 return;
4825 }
4826
4827 if (!npt || !ppt || !widths || (stipple && (!w || !h))) {
4828 Warning("FillSpans", "Invalid input data npt=%d ppt=0x%zx col=%s widths=0x%zx stipple=0x%zx w=%d h=%d",
4829 npt, (size_t)ppt, col, (size_t)widths, (size_t)stipple, w, h);
4830 return;
4831 }
4832
4833 ARGB32 color;
4834 parse_argb_color(col, &color);
4835 Int_t idx = 0;
4836 UInt_t x = 0;
4837 UInt_t yy;
4838
4839 for (UInt_t i = 0; i < npt; i++) {
4840 yy = ppt[i].fY*fImage->width;
4841 for (UInt_t j = 0; j < widths[i]; j++) {
4842 if ((ppt[i].fX >= (Int_t)fImage->width) || (ppt[i].fX < 0) ||
4843 (ppt[i].fY >= (Int_t)fImage->height) || (ppt[i].fY < 0)) continue;
4844
4845 x = ppt[i].fX + j;
4846 idx = Idx(yy + x);
4847
4848 if (!stipple) {
4849 _alphaBlend(&fImage->alt.argb32[idx], &color);
4850 } else {
4851 Int_t ii = (ppt[i].fY%h)*w + x%w;
4852
4853 if (stipple[ii >> 3] & (1 << (ii%8))) {
4854 _alphaBlend(&fImage->alt.argb32[idx], &color);
4855 }
4856 }
4857 }
4858 }
4859}
4860
4861////////////////////////////////////////////////////////////////////////////////
4862/// Fill spans with tile image.
4863
4864void TASImage::FillSpans(UInt_t npt, TPoint *ppt, UInt_t *widths, TImage *tile)
4865{
4866 if (!InitVisual()) {
4867 Warning("FillSpans", "Visual not initiated");
4868 return;
4869 }
4870
4871 if (!fImage) {
4872 Warning("FillSpans", "no image");
4873 return;
4874 }
4875
4876 if (!fImage->alt.argb32) {
4877 BeginPaint();
4878 }
4879
4880 if (!fImage->alt.argb32) {
4881 Warning("FillSpans", "Failed to get pixel array");
4882 return;
4883 }
4884
4885 if (!npt || !ppt || !widths || !tile) {
4886 Warning("FillSpans", "Invalid input data npt=%d ppt=0x%zx widths=0x%zx tile=0x%zx",
4887 npt, (size_t)ppt, (size_t)widths, (size_t)tile);
4888 return;
4889 }
4890
4891 Int_t idx = 0;
4892 Int_t ii = 0;
4893 UInt_t x = 0;
4894 UInt_t *arr = tile->GetArgbArray();
4895 if (!arr) return;
4896 UInt_t xx = 0;
4897 UInt_t yy = 0;
4898
4899 for (UInt_t i = 0; i < npt; i++) {
4900 UInt_t yyy = ppt[i].fY*fImage->width;
4901
4902 for (UInt_t j = 0; j < widths[i]; j++) {
4903 if ((ppt[i].fX >= (Int_t)fImage->width) || (ppt[i].fX < 0) ||
4904 (ppt[i].fY >= (Int_t)fImage->height) || (ppt[i].fY < 0)) continue;
4905 x = ppt[i].fX + j;
4906 idx = Idx(yyy + x);
4907 xx = x%tile->GetWidth();
4908 yy = ppt[i].fY%tile->GetHeight();
4909 ii = yy*tile->GetWidth() + xx;
4910 _alphaBlend(&fImage->alt.argb32[idx], &arr[ii]);
4911 }
4912 }
4913}
4914
4915////////////////////////////////////////////////////////////////////////////////
4916/// Crop spans.
4917
4918void TASImage::CropSpans(UInt_t npt, TPoint *ppt, UInt_t *widths)
4919{
4920 if (!InitVisual()) {
4921 Warning("CropSpans", "Visual not initiated");
4922 return;
4923 }
4924
4925 if (!fImage) {
4926 Warning("CropSpans", "no image");
4927 return;
4928 }
4929
4930 if (!fImage->alt.argb32) {
4931 BeginPaint();
4932 }
4933
4934 if (!fImage->alt.argb32) {
4935 Warning("CropSpans", "Failed to get pixel array");
4936 return;
4937 }
4938
4939 if (!npt || !ppt || !widths) {
4940 Warning("CropSpans", "No points specified npt=%d ppt=0x%zx widths=0x%zx", npt, (size_t)ppt, (size_t)widths);
4941 return;
4942 }
4943
4944 int y0 = ppt[0].fY;
4945 int y1 = ppt[npt-1].fY;
4946 UI