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