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