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