From 9153412470cc5d1fb940d4742eebe54f5a02d544 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Wed, 15 Apr 2026 11:25:25 +0200 Subject: [PATCH 01/23] [x11] small fix in TGX11::SetAttText Global gCws context was used Provide short docu for new methods --- graf2d/x11/inc/TGX11.h | 1 + graf2d/x11/src/TGX11.cxx | 43 ++++++++++++++++++++++++++++++++++------ 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/graf2d/x11/inc/TGX11.h b/graf2d/x11/inc/TGX11.h index 6d016d8e3cdf6..18e0af1a4138b 100644 --- a/graf2d/x11/inc/TGX11.h +++ b/graf2d/x11/inc/TGX11.h @@ -197,6 +197,7 @@ friend struct XWindow_t; //---- Methods used for old graphics ----- void SetFillColor(Color_t cindex) override; + Color_t GetFillColor() const override; void SetFillStyle(Style_t style) override; Style_t GetFillStyle() const override; void SetLineColor(Color_t cindex) override; diff --git a/graf2d/x11/src/TGX11.cxx b/graf2d/x11/src/TGX11.cxx index dac0cdc595ce9..18f401ac9cb3b 100644 --- a/graf2d/x11/src/TGX11.cxx +++ b/graf2d/x11/src/TGX11.cxx @@ -2281,6 +2281,17 @@ void TGX11::SetFillColor(Color_t cindex) SetAttFill((WinContext_t) gCws, arg); } +//////////////////////////////////////////////////////////////////////////////// +/// Return current fill color + +Color_t TGX11::GetFillColor() const +{ + // TODO: remove in ROOT7, no longer used in the code + + return gCws ? gCws->fAttFill.GetFillColor() : TAttFill::GetFillColor(); +} + + //////////////////////////////////////////////////////////////////////////////// /// Set fill area style. /// @@ -2299,10 +2310,11 @@ void TGX11::SetFillStyle(Style_t fstyle) //////////////////////////////////////////////////////////////////////////////// /// Return current fill style -/// FIXME: Only as temporary solution while some code analyze current fill style Style_t TGX11::GetFillStyle() const { + // TODO: remove in ROOT7, no longer used in the code + return gCws ? gCws->fAttFill.GetFillStyle() : TAttFill::GetFillStyle(); } @@ -2371,10 +2383,11 @@ void TGX11::SetLineStyle(Style_t lstyle) //////////////////////////////////////////////////////////////////////////////// /// Return current line style -/// FIXME: Only as temporary solution while some code analyze current line style Style_t TGX11::GetLineStyle() const { + // TODO: remove in ROOT7, no loner used in ROOT code + return gCws ? gCws->fAttLine.GetLineStyle() : TAttLine::GetLineStyle(); } @@ -2395,10 +2408,11 @@ void TGX11::SetLineWidth(Width_t width) //////////////////////////////////////////////////////////////////////////////// /// Return current line width -/// FIXME: Only as temporary solution while some code analyze current line wide Width_t TGX11::GetLineWidth() const { + // TODO: remove in ROOT7, no loner used in ROOT code + return gCws ? gCws->fAttLine.GetLineWidth() : TAttLine::GetLineWidth(); } @@ -3234,12 +3248,20 @@ Int_t TGX11::SupportsExtension(const char *ext) const return XQueryExtension((Display*)fDisplay, ext, &major_opcode, &first_event, &first_error); } +//////////////////////////////////////////////////////////////////////////////// +/// Returns window context for specified window id +/// Window context is valid until window not closed or destroyed +/// Provided methods to work with window context like DrawLineW allows to +/// perform actions independently on different windows WinContext_t TGX11::GetWindowContext(Int_t wid) { return (WinContext_t) fWindows[wid].get(); } +//////////////////////////////////////////////////////////////////////////////// +/// Set fill attributes for specified window + void TGX11::SetAttFill(WinContext_t wctxt, const TAttFill &att) { auto ctxt = (XWindow_t *) wctxt; @@ -3289,6 +3311,9 @@ void TGX11::SetAttFill(WinContext_t wctxt, const TAttFill &att) } } +//////////////////////////////////////////////////////////////////////////////// +/// Set line attributes for specified window + void TGX11::SetAttLine(WinContext_t wctxt, const TAttLine &att) { auto ctxt = (XWindow_t *) wctxt; @@ -3348,6 +3373,9 @@ void TGX11::SetAttLine(WinContext_t wctxt, const TAttLine &att) ctxt->fAttLine = att; } +//////////////////////////////////////////////////////////////////////////////// +/// Set marker attributes for specified window + void TGX11::SetAttMarker(WinContext_t wctxt, const TAttMarker &att) { auto ctxt = (XWindow_t *) wctxt; @@ -3842,6 +3870,9 @@ void TGX11::SetAttMarker(WinContext_t wctxt, const TAttMarker &att) } } +//////////////////////////////////////////////////////////////////////////////// +/// Set text attributes for specified window + void TGX11::SetAttText(WinContext_t wctxt, const TAttText &att) { auto ctxt = (XWindow_t *) wctxt; @@ -3910,9 +3941,9 @@ void TGX11::SetAttText(WinContext_t wctxt, const TAttText &att) // use first existing font for (int i = 0; i < kMAXFONT; i++) if (gFont[i].id) { - gCws->textFont = gFont[i].id; - XSetFont((Display*)fDisplay, gCws->fGClist[kGCtext], gCws->textFont->fid); - XSetFont((Display*)fDisplay, gCws->fGClist[kGCinvt], gCws->textFont->fid); + ctxt->textFont = gFont[i].id; + XSetFont((Display*)fDisplay, ctxt->fGClist[kGCtext], ctxt->textFont->fid); + XSetFont((Display*)fDisplay, ctxt->fGClist[kGCinvt], ctxt->textFont->fid); break; } From 9a142b42061622d5343a928dbb44815afbf789bd Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Wed, 15 Apr 2026 12:39:04 +0200 Subject: [PATCH 02/23] [win32] add proper comment to new methods --- graf2d/win32gdk/inc/TGWin32.h | 1 + graf2d/win32gdk/src/TGWin32.cxx | 19 ++++++++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/graf2d/win32gdk/inc/TGWin32.h b/graf2d/win32gdk/inc/TGWin32.h index 43c0a92aebac7..b4e842245f5d2 100644 --- a/graf2d/win32gdk/inc/TGWin32.h +++ b/graf2d/win32gdk/inc/TGWin32.h @@ -184,6 +184,7 @@ class TGWin32 : public TVirtualX { void SetDoubleBufferON() override; void SetDrawMode(EDrawMode mode) override; void SetFillColor(Color_t cindex) override; + Color_t GetFillColor() const override; void SetFillStyle(Style_t style) override; Style_t GetFillStyle() const override; void SetLineColor(Color_t cindex) override; diff --git a/graf2d/win32gdk/src/TGWin32.cxx b/graf2d/win32gdk/src/TGWin32.cxx index 9931b6b7f54c7..e4b83edd69911 100644 --- a/graf2d/win32gdk/src/TGWin32.cxx +++ b/graf2d/win32gdk/src/TGWin32.cxx @@ -3130,6 +3130,16 @@ void TGWin32::SetFillColor(Color_t cindex) SetAttFill((WinContext_t) gCws, arg); } +//////////////////////////////////////////////////////////////////////////////// +/// Return current fill color + +Color_t TGWin32::GetFillColor() const +{ + // TODO: remove in ROOT7, no longer used in ROOT + + return gCws ? gCws->fAttFill.GetFillColor() : TAttFill::GetFillColor(); +} + //////////////////////////////////////////////////////////////////////////////// /// Set fill area style. /// fstyle : compound fill area interior style @@ -3147,10 +3157,11 @@ void TGWin32::SetFillStyle(Style_t fstyle) //////////////////////////////////////////////////////////////////////////////// /// Return current fill style -/// FIXME: Only as temporary solution while some code analyze current fill style Style_t TGWin32::GetFillStyle() const { + // TODO: remove in ROOT7, no longer used in ROOT + return gCws ? gCws->fAttFill.GetFillStyle() : TAttFill::GetFillStyle(); } @@ -3263,10 +3274,11 @@ void TGWin32::SetLineStyle(Style_t lstyle) //////////////////////////////////////////////////////////////////////////////// /// Return current line style -/// FIXME: Only as temporary solution while some code analyze current line style Style_t TGWin32::GetLineStyle() const { + // TODO: remove in ROOT7, no longer used in ROOT + return gCws ? gCws->fAttLine.GetLineStyle() : TAttLine::GetLineStyle(); } @@ -3286,10 +3298,11 @@ void TGWin32::SetLineWidth(Width_t width) //////////////////////////////////////////////////////////////////////////////// /// Return current line width -/// FIXME: Only as temporary solution while some code analyze current line wide Width_t TGWin32::GetLineWidth() const { + // TODO: remove in ROOT7, no longer used in ROOT + return gCws ? gCws->fAttLine.GetLineWidth() : TAttLine::GetLineWidth(); } From c2d25f42ef1454e42c30b4c15719e4737bd7a53f Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Wed, 15 Apr 2026 16:16:59 +0200 Subject: [PATCH 03/23] [virtualx] add SetOpacityW and CopyPixmapW methods Let use window context when calling these methods. These are central methods to emulate pad opacity without real support of alpha channel in X11 --- core/base/inc/TVirtualX.h | 2 ++ core/base/src/TVirtualX.cxx | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/core/base/inc/TVirtualX.h b/core/base/inc/TVirtualX.h index ddbae75d26dbc..de2c1b3c5c99b 100644 --- a/core/base/inc/TVirtualX.h +++ b/core/base/inc/TVirtualX.h @@ -112,6 +112,8 @@ class TVirtualX : public TNamed, public TAttLine, public TAttFill, public TAttTe virtual EDrawMode GetDrawModeW(WinContext_t wctxt); virtual void ClearWindowW(WinContext_t wctxt); virtual void UpdateWindowW(WinContext_t wctxt, Int_t mode); + virtual void SetOpacityW(WinContext_t wctxt, Int_t percent); + virtual void CopyPixmapW(WinContext_t wctxt, Int_t wid, Int_t xpos, Int_t ypos); virtual void DrawBoxW(WinContext_t wctxt, Int_t x1, Int_t y1, Int_t x2, Int_t y2, EBoxMode mode); virtual void DrawFillAreaW(WinContext_t wctxt, Int_t n, TPoint *xy); diff --git a/core/base/src/TVirtualX.cxx b/core/base/src/TVirtualX.cxx index 3c0f39558e328..f4e05e3f07b5f 100644 --- a/core/base/src/TVirtualX.cxx +++ b/core/base/src/TVirtualX.cxx @@ -460,6 +460,22 @@ void TVirtualX::UpdateWindowW(WinContext_t /* wctxt */, Int_t mode) UpdateWindow(mode); } +//////////////////////////////////////////////////////////////////////////////// +/// Set opactity for specified window + +void TVirtualX::SetOpacityW(WinContext_t /* wctxt */, Int_t percent) +{ + SetOpacity(percent); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Copy pixmap to specified window + +void TVirtualX::CopyPixmapW(WinContext_t /* wctxt */, Int_t wid, Int_t xpos, Int_t ypos) +{ + CopyPixmap(wid, xpos, ypos); +} + //////////////////////////////////////////////////////////////////////////////// /// Draw box on specified window From 79e2c0730a969958a74e6fb7bf39416b824474b7 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Wed, 15 Apr 2026 16:17:29 +0200 Subject: [PATCH 04/23] [tgx11] implement SetOpacityW and CopyPixmapW methods --- graf2d/x11/inc/TGX11.h | 2 + graf2d/x11/src/TGX11.cxx | 150 ++++++++++++++++++++++----------------- 2 files changed, 88 insertions(+), 64 deletions(-) diff --git a/graf2d/x11/inc/TGX11.h b/graf2d/x11/inc/TGX11.h index 18e0af1a4138b..dc06dc9532723 100644 --- a/graf2d/x11/inc/TGX11.h +++ b/graf2d/x11/inc/TGX11.h @@ -237,6 +237,8 @@ friend struct XWindow_t; EDrawMode GetDrawModeW(WinContext_t wctxt) override; void ClearWindowW(WinContext_t wctxt) override; void UpdateWindowW(WinContext_t wctxt, Int_t mode) override; + void SetOpacityW(WinContext_t wctxt, Int_t percent) override; + void CopyPixmapW(WinContext_t wctxt, Int_t wid, Int_t xpos, Int_t ypos) override; void DrawBoxW(WinContext_t wctxt, Int_t x1, Int_t y1, Int_t x2, Int_t y2, EBoxMode mode) override; void DrawFillAreaW(WinContext_t wctxt, Int_t n, TPoint *xy) override; diff --git a/graf2d/x11/src/TGX11.cxx b/graf2d/x11/src/TGX11.cxx index 18f401ac9cb3b..49a6ad2bfb401 100644 --- a/graf2d/x11/src/TGX11.cxx +++ b/graf2d/x11/src/TGX11.cxx @@ -472,11 +472,7 @@ void TGX11::CloseWindow() void TGX11::CopyPixmap(int wid, int xpos, int ypos) { - gTws = fWindows[wid].get(); - - XCopyArea((Display*)fDisplay, gTws->fDrawing, gCws->fDrawing, gTws->fGClist[kGCpxmp], 0, 0, gTws->fWidth, - gTws->fHeight, xpos, ypos); - XFlush((Display*)fDisplay); + CopyPixmapW((WinContext_t) gCws, wid, xpos, ypos); } //////////////////////////////////////////////////////////////////////////////// @@ -2460,7 +2456,7 @@ void TGX11::SetMarkerStyle(Style_t markerstyle) } //////////////////////////////////////////////////////////////////////////////// -/// Set opacity of a window. This image manipulation routine works +/// Set opacity of a selected window. This image manipulation routine works /// by adding to a percent amount of neutral to each pixels RGB. /// Since it requires quite some additional color map entries is it /// only supported on displays with more than > 8 color planes (> 256 @@ -2468,64 +2464,7 @@ void TGX11::SetMarkerStyle(Style_t markerstyle) void TGX11::SetOpacity(Int_t percent) { - if (fDepth <= 8) return; - if (percent == 0) return; - // if 100 percent then just make white - - ULong_t *orgcolors = nullptr, *tmpc = nullptr; - Int_t maxcolors = 0, ncolors = 0, ntmpc = 0; - - // save previous allocated colors, delete at end when not used anymore - if (gCws->fNewColors) { - tmpc = gCws->fNewColors; - ntmpc = gCws->fNcolors; - } - - // get pixmap from server as image - XImage *image = XGetImage((Display*)fDisplay, gCws->fDrawing, 0, 0, gCws->fWidth, - gCws->fHeight, AllPlanes, ZPixmap); - if (!image) return; - // collect different image colors - int x, y; - for (y = 0; y < (int) gCws->fHeight; y++) { - for (x = 0; x < (int) gCws->fWidth; x++) { - ULong_t pixel = XGetPixel(image, x, y); - CollectImageColors(pixel, orgcolors, ncolors, maxcolors); - } - } - if (ncolors == 0) { - XDestroyImage(image); - ::operator delete(orgcolors); - return; - } - - // create opaque counter parts - MakeOpaqueColors(percent, orgcolors, ncolors); - - if (gCws->fNewColors) { - // put opaque colors in image - for (y = 0; y < (int) gCws->fHeight; y++) { - for (x = 0; x < (int) gCws->fWidth; x++) { - ULong_t pixel = XGetPixel(image, x, y); - Int_t idx = FindColor(pixel, orgcolors, ncolors); - XPutPixel(image, x, y, gCws->fNewColors[idx]); - } - } - } - - // put image back in pixmap on server - XPutImage((Display*)fDisplay, gCws->fDrawing, gCws->fGClist[kGCpxmp], image, 0, 0, 0, 0, - gCws->fWidth, gCws->fHeight); - XFlush((Display*)fDisplay); - - // clean up - if (tmpc) { - if (fRedDiv == -1) - XFreeColors((Display*)fDisplay, fColormap, tmpc, ntmpc, 0); - delete [] tmpc; - } - XDestroyImage(image); - ::operator delete(orgcolors); + SetOpacityW((WinContext_t) gCws, percent); } //////////////////////////////////////////////////////////////////////////////// @@ -2811,6 +2750,89 @@ void TGX11::UpdateWindowW(WinContext_t wctxt, Int_t mode) } } +//////////////////////////////////////////////////////////////////////////////// +/// Set opacity of a specified window. This image manipulation routine works +/// by adding to a percent amount of neutral to each pixels RGB. +/// Since it requires quite some additional color map entries is it +/// only supported on displays with more than > 8 color planes (> 256 +/// colors). + +void TGX11::SetOpacityW(WinContext_t wctxt, Int_t percent) +{ + if ((fDepth <= 8) || (percent <= 0)) return; + // if 100 percent then just make white + + auto ctxt = (XWindow_t *) wctxt; + + ULong_t *orgcolors = nullptr, *tmpc = nullptr; + Int_t maxcolors = 0, ncolors = 0, ntmpc = 0; + + // save previous allocated colors, delete at end when not used anymore + if (ctxt->fNewColors) { + tmpc = ctxt->fNewColors; + ntmpc = ctxt->fNcolors; + } + + // get pixmap from server as image + XImage *image = XGetImage((Display*)fDisplay, ctxt->fDrawing, 0, 0, ctxt->fWidth, + ctxt->fHeight, AllPlanes, ZPixmap); + if (!image) return; + // collect different image colors + for (unsigned y = 0; y < ctxt->fHeight; y++) { + for (unsigned x = 0; x < ctxt->fWidth; x++) { + ULong_t pixel = XGetPixel(image, x, y); + CollectImageColors(pixel, orgcolors, ncolors, maxcolors); + } + } + if (ncolors == 0) { + XDestroyImage(image); + ::operator delete(orgcolors); + return; + } + + // create opaque counter parts + MakeOpaqueColors(percent, orgcolors, ncolors); + + if (ctxt->fNewColors) { + // put opaque colors in image + for (unsigned y = 0; y < ctxt->fHeight; y++) { + for (unsigned x = 0; x < ctxt->fWidth; x++) { + ULong_t pixel = XGetPixel(image, x, y); + Int_t idx = FindColor(pixel, orgcolors, ncolors); + XPutPixel(image, x, y, ctxt->fNewColors[idx]); + } + } + } + + // put image back in pixmap on server + XPutImage((Display*)fDisplay, ctxt->fDrawing, ctxt->fGClist[kGCpxmp], image, 0, 0, 0, 0, + ctxt->fWidth, ctxt->fHeight); + XFlush((Display*)fDisplay); + + // clean up + if (tmpc) { + if (fRedDiv == -1) + XFreeColors((Display*)fDisplay, fColormap, tmpc, ntmpc, 0); + delete [] tmpc; + } + XDestroyImage(image); + ::operator delete(orgcolors); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Copy the pixmap wid at the position xpos, ypos in the specified window. + +void TGX11::CopyPixmapW(WinContext_t wctxt, Int_t wid, Int_t xpos, Int_t ypos) +{ + auto ctxt = (XWindow_t *) wctxt; + + gTws = fWindows[wid].get(); + + XCopyArea((Display*)fDisplay, gTws->fDrawing, ctxt->fDrawing, gTws->fGClist[kGCpxmp], 0, 0, gTws->fWidth, + gTws->fHeight, xpos, ypos); + XFlush((Display*)fDisplay); +} + //////////////////////////////////////////////////////////////////////////////// /// Set pointer position. /// From 16ebb79930a00c2591500b6014dba7215fad5d8b Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Wed, 15 Apr 2026 16:17:51 +0200 Subject: [PATCH 05/23] [mac] implement SetOpacityW and CopyPixmapW methods --- graf2d/cocoa/inc/TGCocoa.h | 1 + graf2d/cocoa/inc/TGQuartz.h | 1 + graf2d/cocoa/src/TGCocoa.mm | 17 ++++++++++++----- graf2d/cocoa/src/TGQuartz.mm | 4 ++++ 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/graf2d/cocoa/inc/TGCocoa.h b/graf2d/cocoa/inc/TGCocoa.h index 28f67306a3386..123a7135fd07b 100644 --- a/graf2d/cocoa/inc/TGCocoa.h +++ b/graf2d/cocoa/inc/TGCocoa.h @@ -110,6 +110,7 @@ class TGCocoa : public TVirtualX { EDrawMode GetDrawModeW(WinContext_t wctxt) override; void ClearWindowW(WinContext_t wctxt) override; void UpdateWindowW(WinContext_t wctxt, Int_t mode) override; + void CopyPixmapW(WinContext_t wctxt, Int_t wid, Int_t xpos, Int_t ypos) override; //-Functions used by GUI. Window_t CreateWindow(Window_t parent, Int_t x, Int_t y, diff --git a/graf2d/cocoa/inc/TGQuartz.h b/graf2d/cocoa/inc/TGQuartz.h index 8c9e8603c557c..b1bdaec556722 100644 --- a/graf2d/cocoa/inc/TGQuartz.h +++ b/graf2d/cocoa/inc/TGQuartz.h @@ -76,6 +76,7 @@ class TGQuartz : public TGCocoa { Float_t GetTextMagnitude() override; //---- Methods used for new graphics ----- + void SetOpacityW(WinContext_t wctxt, Int_t percent) override; void SetAttFill(WinContext_t wctxt, const TAttFill &att) override; void SetAttLine(WinContext_t wctxt, const TAttLine &att) override; void SetAttMarker(WinContext_t wctxt, const TAttMarker &att) override; diff --git a/graf2d/cocoa/src/TGCocoa.mm b/graf2d/cocoa/src/TGCocoa.mm index 004ea9586eabe..f63285d3b840b 100644 --- a/graf2d/cocoa/src/TGCocoa.mm +++ b/graf2d/cocoa/src/TGCocoa.mm @@ -2430,27 +2430,34 @@ void FixAscii(std::vector &text) //______________________________________________________________________________ void TGCocoa::CopyPixmap(Int_t pixmapID, Int_t x, Int_t y) { - assert(pixmapID > (Int_t)fPimpl->GetRootWindowID() && - "CopyPixmap, parameter 'pixmapID' is not a valid id"); assert(fSelectedDrawable > fPimpl->GetRootWindowID() && "CopyPixmap, fSelectedDrawable is not a valid window id"); + CopyPixmapW((WinContext_t) fPimpl->GetDrawable(fSelectedDrawable), pixmapID, x, y); +} + +//______________________________________________________________________________ +void TGCocoa::CopyPixmapW(WinContext_t wctxt, Int_t pixmapID, Int_t x, Int_t y) +{ + assert(pixmapID > (Int_t)fPimpl->GetRootWindowID() && + "CopyPixmapW, parameter 'pixmapID' is not a valid id"); + NSObject * const source = fPimpl->GetDrawable(pixmapID); assert([source isKindOfClass : [QuartzPixmap class]] && "CopyPixmap, source is not a pixmap"); QuartzPixmap * const pixmap = (QuartzPixmap *)source; - NSObject * const drawable = fPimpl->GetDrawable(fSelectedDrawable); + auto drawable = (NSObject * const) wctxt; NSObject * destination = nil; if (drawable.fIsPixmap) { destination = drawable; } else { - NSObject * const window = fPimpl->GetWindow(fSelectedDrawable); + NSObject * const window = (NSObject * const) drawable; if (window.fBackBuffer) { destination = window.fBackBuffer; } else { - Warning("CopyPixmap", "Operation skipped, since destination" + Warning("CopyPixmapW", "Operation skipped, since destination" " window is not double buffered"); return; } diff --git a/graf2d/cocoa/src/TGQuartz.mm b/graf2d/cocoa/src/TGQuartz.mm index fab43c8649a02..f60389eacbf52 100644 --- a/graf2d/cocoa/src/TGQuartz.mm +++ b/graf2d/cocoa/src/TGQuartz.mm @@ -815,6 +815,10 @@ void ConvertPointsROOTToCocoa(Int_t nPoints, const TPoint *xy, std::vector Date: Wed, 15 Apr 2026 16:18:07 +0200 Subject: [PATCH 06/23] [win32] implement SetOpacityW and CopyPixmapW methods --- graf2d/win32gdk/inc/TGWin32.h | 2 + graf2d/win32gdk/src/TGWin32.cxx | 153 +++++++++++++++++++------------- 2 files changed, 91 insertions(+), 64 deletions(-) diff --git a/graf2d/win32gdk/inc/TGWin32.h b/graf2d/win32gdk/inc/TGWin32.h index b4e842245f5d2..f46002393677f 100644 --- a/graf2d/win32gdk/inc/TGWin32.h +++ b/graf2d/win32gdk/inc/TGWin32.h @@ -218,6 +218,8 @@ class TGWin32 : public TVirtualX { EDrawMode GetDrawModeW(WinContext_t wctxt) override; void ClearWindowW(WinContext_t wctxt) override; void UpdateWindowW(WinContext_t wctxt, Int_t mode) override; + void SetOpacityW(WinContext_t wctxt, Int_t percent) override; + void CopyPixmapW(WinContext_t wctxt, Int_t wid, Int_t xpos, Int_t ypos) override; void DrawBoxW(WinContext_t wctxt, Int_t x1, Int_t y1, Int_t x2, Int_t y2, EBoxMode mode) override; void DrawFillAreaW(WinContext_t wctxt, Int_t n, TPoint *xy) override; diff --git a/graf2d/win32gdk/src/TGWin32.cxx b/graf2d/win32gdk/src/TGWin32.cxx index e4b83edd69911..4647a9b38a06c 100644 --- a/graf2d/win32gdk/src/TGWin32.cxx +++ b/graf2d/win32gdk/src/TGWin32.cxx @@ -1648,13 +1648,7 @@ void TGWin32::CloseWindow() void TGWin32::CopyPixmap(int wid, int xpos, int ypos) { - if (fWindows.count(wid) == 0) - return; - - gTws = fWindows[wid].get(); - gdk_window_copy_area(gCws->drawing, gTws->fGClist[kGCpxmp], xpos, ypos, gTws->drawing, - 0, 0, gTws->width, gTws->height); - GdiFlush(); + CopyPixmapW((WinContext_t) gCws, wid, xpos, ypos); } //////////////////////////////////////////////////////////////////////////////// @@ -4076,7 +4070,7 @@ void TGWin32::SetAttText(WinContext_t wctxt, const TAttText &att) //////////////////////////////////////////////////////////////////////////////// -/// Set opacity of a window. This image manipulation routine works +/// Set opacity of a current window. This image manipulation routine works /// by adding to a percent amount of neutral to each pixels RGB. /// Since it requires quite some additional color map entries is it /// only supported on displays with more than > 8 color planes (> 256 @@ -4084,62 +4078,7 @@ void TGWin32::SetAttText(WinContext_t wctxt, const TAttText &att) void TGWin32::SetOpacity(Int_t percent) { - Int_t depth = gdk_visual_get_best_depth(); - - if (depth <= 8) return; - if (percent == 0) return; - - // if 100 percent then just make white - ULong_t *orgcolors = 0; - ULong_t *tmpc = 0; - Int_t maxcolors = 0, ncolors, ntmpc = 0; - - // save previous allocated colors, delete at end when not used anymore - if (gCws->new_colors) { - tmpc = gCws->new_colors; - ntmpc = gCws->ncolors; - } - // get pixmap from server as image - GdkImage *image = gdk_image_get((GdkDrawable*)gCws->drawing, 0, 0, - gCws->width, gCws->height); - - // collect different image colors - int x, y; - for (y = 0; y < (int) gCws->height; y++) { - for (x = 0; x < (int) gCws->width; x++) { - ULong_t pixel = GetPixelImage((Drawable_t)image, x, y); - CollectImageColors(pixel, orgcolors, ncolors, maxcolors); - } - } - if (ncolors == 0) { - gdk_image_unref(image); - ::operator delete(orgcolors); - return; - } - // create opaque counter parts - MakeOpaqueColors(percent, orgcolors, ncolors); - - // put opaque colors in image - for (y = 0; y < (int) gCws->height; y++) { - for (x = 0; x < (int) gCws->width; x++) { - ULong_t pixel = GetPixelImage((Drawable_t)image, x, y); - Int_t idx = FindColor(pixel, orgcolors, ncolors); - PutPixel((Drawable_t)image, x, y, gCws->new_colors[idx]); - } - } - - // put image back in pixmap on server - gdk_draw_image(gCws->drawing, gCws->fGClist[kGCpxmp], (GdkImage *)image, - 0, 0, 0, 0, gCws->width, gCws->height); - GdiFlush(); - - // clean up - if (tmpc) { - gdk_colors_free((GdkColormap *)fColormap, tmpc, ntmpc, 0); - delete[]tmpc; - } - gdk_image_unref(image); - ::operator delete(orgcolors); + SetOpacityW((WinContext_t) gCws, percent); } //////////////////////////////////////////////////////////////////////////////// @@ -4323,6 +4262,92 @@ void TGWin32::UpdateWindowW(WinContext_t wctxt, Int_t mode) Update(mode); } +//////////////////////////////////////////////////////////////////////////////// +/// Set opacity of a specified window. This image manipulation routine works +/// by adding to a percent amount of neutral to each pixels RGB. +/// Since it requires quite some additional color map entries is it +/// only supported on displays with more than > 8 color planes (> 256 +/// colors) + +void TGWin32::SetOpacityW(WinContext_t wctxt, Int_t percent) +{ + auto ctxt = (XWindow_t *) wctxt; + + Int_t depth = gdk_visual_get_best_depth(); + + if (depth <= 8) return; + if (percent == 0) return; + + // if 100 percent then just make white + ULong_t *orgcolors = 0; + ULong_t *tmpc = 0; + Int_t maxcolors = 0, ncolors, ntmpc = 0; + + // save previous allocated colors, delete at end when not used anymore + if (ctxt->new_colors) { + tmpc = ctxt->new_colors; + ntmpc = ctxt->ncolors; + } + // get pixmap from server as image + GdkImage *image = gdk_image_get((GdkDrawable*)ctxt->drawing, 0, 0, + ctxt->width, ctxt->height); + + // collect different image colors + int x, y; + for (y = 0; y < (int) ctxt->height; y++) { + for (x = 0; x < (int) ctxt->width; x++) { + ULong_t pixel = GetPixelImage((Drawable_t)image, x, y); + CollectImageColors(pixel, orgcolors, ncolors, maxcolors); + } + } + if (ncolors == 0) { + gdk_image_unref(image); + ::operator delete(orgcolors); + return; + } + // create opaque counter parts + MakeOpaqueColors(percent, orgcolors, ncolors); + + // put opaque colors in image + for (y = 0; y < (int) ctxt->height; y++) { + for (x = 0; x < (int) ctxt->width; x++) { + ULong_t pixel = GetPixelImage((Drawable_t)image, x, y); + Int_t idx = FindColor(pixel, orgcolors, ncolors); + PutPixel((Drawable_t)image, x, y, ctxt->new_colors[idx]); + } + } + + // put image back in pixmap on server + gdk_draw_image(ctxt->drawing, ctxt->fGClist[kGCpxmp], (GdkImage *)image, + 0, 0, 0, 0, ctxt->width, ctxt->height); + GdiFlush(); + + // clean up + if (tmpc) { + gdk_colors_free((GdkColormap *)fColormap, tmpc, ntmpc, 0); + delete[]tmpc; + } + gdk_image_unref(image); + ::operator delete(orgcolors); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Copy the pixmap wid at the position xpos, ypos in the specified window. + +void TGWin32::CopyPixmapW(WinContext_t wctxt, Int_t wid, Int_t xpos, Int_t ypos) +{ + if (fWindows.count(wid) == 0) + return; + + auto ctxt = (XWindow_t *) wctxt; + + gTws = fWindows[wid].get(); + gdk_window_copy_area(ctxt->drawing, gTws->fGClist[kGCpxmp], xpos, ypos, gTws->drawing, + 0, 0, gTws->width, gTws->height); + GdiFlush(); +} + + //////////////////////////////////////////////////////////////////////////////// /// Set pointer position. /// ix : New X coordinate of pointer From 6c93063c2545276da76b167b4a1bede1b279f96a Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Wed, 15 Apr 2026 16:21:20 +0200 Subject: [PATCH 07/23] [pad] use new methods to set opacity and copy pixmap --- graf2d/gpad/src/TPad.cxx | 4 ++-- graf2d/gpad/src/TPadPainter.cxx | 6 ++---- graf3d/gl/src/TGLPadPainter.cxx | 5 +++-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/graf2d/gpad/src/TPad.cxx b/graf2d/gpad/src/TPad.cxx index cdc161429fbef..50ef909ec6ae2 100644 --- a/graf2d/gpad/src/TPad.cxx +++ b/graf2d/gpad/src/TPad.cxx @@ -3973,8 +3973,8 @@ void TPad::CopyBackgroundPixmap(Int_t x, Int_t y) { int px, py; XYtoAbsPixel(fX1, fY2, px, py); - if (GetPainter()) - GetPainter()->CopyDrawable(GetPixmapID(), px-x, py-y); + if (auto pp = GetPainter()) + pp->CopyDrawable(GetPixmapID(), px-x, py-y); } //////////////////////////////////////////////////////////////////////////////// diff --git a/graf2d/gpad/src/TPadPainter.cxx b/graf2d/gpad/src/TPadPainter.cxx index 491c00c4bd315..2a65421410fcd 100644 --- a/graf2d/gpad/src/TPadPainter.cxx +++ b/graf2d/gpad/src/TPadPainter.cxx @@ -89,7 +89,7 @@ gVirtualX or from my own member. So! All attributed, _ALL_ go to/from gVirtualX. void TPadPainter::SetOpacity(Int_t percent) { - gVirtualX->SetOpacity(percent); + gVirtualX->SetOpacityW(fWinContext, percent); } //////////////////////////////////////////////////////////////////////////////// @@ -146,10 +146,9 @@ void TPadPainter::ClearDrawable() void TPadPainter::CopyDrawable(Int_t device, Int_t px, Int_t py) { - gVirtualX->CopyPixmap(device, px, py); + gVirtualX->CopyPixmapW(fWinContext, device, px, py); } - //////////////////////////////////////////////////////////////////////////////// /// Close the current gVirtualX pixmap. @@ -160,7 +159,6 @@ void TPadPainter::DestroyDrawable(Int_t device) fWinContext = (WinContext_t) 0; } - //////////////////////////////////////////////////////////////////////////////// /// Select the window in which the graphics will go. diff --git a/graf3d/gl/src/TGLPadPainter.cxx b/graf3d/gl/src/TGLPadPainter.cxx index 3c55cef6354e1..858e0312643c8 100644 --- a/graf3d/gl/src/TGLPadPainter.cxx +++ b/graf3d/gl/src/TGLPadPainter.cxx @@ -71,7 +71,8 @@ TGLPadPainter::TGLPadPainter() void TGLPadPainter::SetOpacity(Int_t percent) { - gVirtualX->SetOpacity(percent); + // does not work this way + gVirtualX->SetOpacityW(fWinContext, percent); } //////////////////////////////////////////////////////////////////////////////// @@ -183,7 +184,7 @@ Bool_t TGLPadPainter::IsCocoa() const void TGLPadPainter::CopyDrawable(Int_t /* device */, Int_t /* px */, Int_t /* py */) { - // gVirtualX->CopyPixmap(device, px, py); + // gVirtualX->CopyPixmapW(fWinContext, device, px, py); } //////////////////////////////////////////////////////////////////////////////// From a00a46fa97b8074eaf6abe02a4ba5cc5a6cbf95a Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Wed, 15 Apr 2026 17:24:07 +0200 Subject: [PATCH 08/23] [pad] move special fill attributes handling to TPad::PaintBorder There are several pad fill attributes which only relevant for pad. It is fill style 4000..4100 or fillcolor 10 for fillstyle 3000..3100 Move all these special attribute only to TPad::PaintBorder to exclude misuse of these attributes in different classes --- graf2d/gpad/src/TPad.cxx | 100 +++++++++++++++------------------------ 1 file changed, 39 insertions(+), 61 deletions(-) diff --git a/graf2d/gpad/src/TPad.cxx b/graf2d/gpad/src/TPad.cxx index 50ef909ec6ae2..a37dc19b74bd9 100644 --- a/graf2d/gpad/src/TPad.cxx +++ b/graf2d/gpad/src/TPad.cxx @@ -744,13 +744,9 @@ void TPad::Clear(Option_t *option) } auto pp = GetPainter(); - // If pad painter uses PS, ClearDrawable() start new page - if (pp) { - if (pp->IsNative()) - pp->ClearDrawable(); - else if (this == GetCanvas()) - pp->NewPage(); - } + // If pad painter uses PS, TPad::Clear() start new page + if (pp && pp->GetPS()) + pp->NewPage(); PaintBorder(GetFillColor(), kTRUE); fCrosshairPos = 0; @@ -3660,23 +3656,44 @@ void TPad::PaintBorder(Color_t color, Bool_t /* tops */) pp->OnPad(this); if (color >= 0) { - TAttLine::Modify(); //Change line attributes only if necessary - TAttFill::Modify(); //Change fill area attributes only if necessary - //With Cocoa we have a transparency. But we also have - //pixmaps, and if you just paint a new content over the old one - //with alpha < 1., you'll be able to see the old content. - if (pp->IsNative() && pp->IsCocoa()) + Bool_t do_paint_box = kTRUE; + + Style_t style = GetFillStyle(); + if (!IsBatch() && (pp->IsCocoa() || (pp->IsNative() && (style > 3000) && (style < 3026)))) pp->ClearDrawable(); - PaintBox(fX1, fY1, fX2, fY2); - } - if (color < 0) + if ((style >= 4000) && (style <= 4100) && pp->IsNative()) { + if (this == fMother) { + style = 1001; + } else if (pp->IsCocoa()) { + auto col = gROOT->GetColor(color); + if (col) + color = TColor::GetColor(col->GetRed(), col->GetGreen(), col->GetBlue(), (style - 4000) / 100.); + style = 1001; + } else { + // copy all pixmaps + do_paint_box = kFALSE; + int px, py; + XYtoAbsPixel(fX1, fY2, px, py); + if (fMother) { + fMother->CopyBackgroundPixmap(px, py); + CopyBackgroundPixmaps(fMother, this, px, py); + } + pp->SetOpacity(style - 4000); + } + } else if ((color == 10) && (style > 3000) && (style < 3100)) + color = 1; + + if (do_paint_box) { + pp->SetAttFill({color, style}); + pp->SetAttLine(*this); + PaintBox(fX1, fY1, fX2, fY2); + } + } else color = -color; - if (IsTransparent()) - return; - if (fBorderMode == 0) + if (IsTransparent() || (fBorderMode == 0)) return; // then paint 3d frame (depending on bordermode) @@ -3802,14 +3819,8 @@ void TPad::PaintModified() fPadPaint = 1; { TContext ctxt(this, kTRUE); - if (IsModified() || IsTransparent()) { - if ((fFillStyle < 3026) && (fFillStyle > 3000)) { - auto pp = GetPainter(); - if (pp && pp->IsNative()) - pp->ClearDrawable(); - } + if (IsModified() || IsTransparent()) PaintBorder(GetFillColor(), kTRUE); - } PaintDate(); @@ -3894,42 +3905,9 @@ void TPad::PaintBox(Double_t x1, Double_t y1, Double_t x2, Double_t y2, Option_t } else if ((style >= 1000) && (style < 2000)) { draw_fill = kTRUE; } else if (style > 3000 && style < 3100) { - if (style < 3026) - pp->DrawBox(x1, y1, x2, y2, TVirtualPadPainter::kFilled); - //special case for TAttFillCanvas on real display - if (att.GetFillColor() == 10) { - // SL: reproduce old sequence of painting calls, can have some side effects on opaque pads - att.SetFillColor(1); - pp->SetAttFill(att); - pp->DrawBox(x1, y1, x2, y2, TVirtualPadPainter::kFilled); - att.SetFillColor(10); - pp->SetAttFill(att); - } + draw_fill = style < 3026; } else if (style >= 4000 && style <= 4100) { - // For style >=4000 we make the window transparent. - // From 4000 to 4100 the window is 100% transparent to 100% opaque - - //ignore this style option when this is the canvas itself - if (this == fMother) { - //It's clear, that virtual X checks a style (4000) and will render a hollow rect! - if (pp->IsCocoa()) { - style0 = style; - att.SetFillStyle(1000); - pp->SetAttFill(att); - } - draw_fill = kTRUE; - } else { - //draw background by blitting all bottom pads - int px, py; - XYtoAbsPixel(fX1, fY2, px, py); - - if (fMother) { - fMother->CopyBackgroundPixmap(px, py); - CopyBackgroundPixmaps(fMother, this, px, py); - } - - pp->SetOpacity(style - 4000); - } + // only used by pad, ignored by all other objects } else if (style > 0) draw_border = kTRUE; From 1e45fe91b5a528375fb44bc6dd0b619efc38b07e Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Thu, 16 Apr 2026 17:15:15 +0200 Subject: [PATCH 09/23] [pad] handle fill styles 4000..4100 in Cocoa and GL On these platforms transparency can be used as is so just transform to painting with transparent colors Special handling of 4000 - do nothing. --- graf2d/gpad/src/TPad.cxx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/graf2d/gpad/src/TPad.cxx b/graf2d/gpad/src/TPad.cxx index a37dc19b74bd9..8a4f8da0ae146 100644 --- a/graf2d/gpad/src/TPad.cxx +++ b/graf2d/gpad/src/TPad.cxx @@ -3666,11 +3666,14 @@ void TPad::PaintBorder(Color_t color, Bool_t /* tops */) if ((style >= 4000) && (style <= 4100) && pp->IsNative()) { if (this == fMother) { style = 1001; - } else if (pp->IsCocoa()) { - auto col = gROOT->GetColor(color); - if (col) + } else if (pp->IsCocoa() || (fCanvas && fCanvas->UseGL())) { + TColor *col = (style == 4000) ? nullptr : gROOT->GetColor(color); + if (!col) { + do_paint_box = kFALSE; + } else { color = TColor::GetColor(col->GetRed(), col->GetGreen(), col->GetBlue(), (style - 4000) / 100.); - style = 1001; + style = 1001; + } } else { // copy all pixmaps do_paint_box = kFALSE; From 728503ed2257f839ddfe971c3cf1a3028cc044de Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Fri, 17 Apr 2026 09:54:34 +0200 Subject: [PATCH 10/23] [padpainterbase] add method to handle fill styles 4000..4100 It will be used internally in derived classes to transform fill attributes in convenient format with color opacity --- graf2d/gpad/CMakeLists.txt | 1 + graf2d/gpad/inc/TPadPainterBase.h | 3 +++ graf2d/gpad/src/TPadPainterBase.cxx | 41 +++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+) create mode 100644 graf2d/gpad/src/TPadPainterBase.cxx diff --git a/graf2d/gpad/CMakeLists.txt b/graf2d/gpad/CMakeLists.txt index e6e8e400fed85..a67b5e0f2af50 100644 --- a/graf2d/gpad/CMakeLists.txt +++ b/graf2d/gpad/CMakeLists.txt @@ -47,6 +47,7 @@ ROOT_STANDARD_LIBRARY_PACKAGE(Gpad src/TGroupButton.cxx src/TInspectCanvas.cxx src/TPad.cxx + src/TPadPainterBase.cxx src/TPadPainter.cxx src/TPadPainterPS.cxx src/TPaveClass.cxx diff --git a/graf2d/gpad/inc/TPadPainterBase.h b/graf2d/gpad/inc/TPadPainterBase.h index 0bb19b56b119d..06809da6542de 100644 --- a/graf2d/gpad/inc/TPadPainterBase.h +++ b/graf2d/gpad/inc/TPadPainterBase.h @@ -24,6 +24,9 @@ class TPadPainterBase : public TVirtualPadPainter { TAttLine fAttLine; ///< current line attributes TAttMarker fAttMarker; ///< current marker attributes TAttText fAttText; ///< current text attributes + Bool_t fFullyTransparent = kFALSE; ///< if transformed fill attributes fully transparent + + TAttFill GetAttFillInternal(Bool_t with_transparency); public: diff --git a/graf2d/gpad/src/TPadPainterBase.cxx b/graf2d/gpad/src/TPadPainterBase.cxx new file mode 100644 index 0000000000000..ca93ae13088d8 --- /dev/null +++ b/graf2d/gpad/src/TPadPainterBase.cxx @@ -0,0 +1,41 @@ +// @(#)root/gpad:$Id$ +// Author: Sergey Linev 17/04/2026 + +/************************************************************************* + * Copyright (C) 1995-2026, Rene Brun and Fons Rademakers. * + * All rights reserved. * + * * + * For the licensing terms see $ROOTSYS/LICENSE. * + * For the list of contributors see $ROOTSYS/README/CREDITS. * + *************************************************************************/ + +#include "TPadPainterBase.h" +#include "TColor.h" + + +/** \class TPadPainterBase +\ingroup gpad + +Extends TVirtualPadPainter interface to simplify work with graphical attributes +*/ + +//////////////////////////////////////////////////////////////////////////////// +/// Returns fill attributes after modification +/// Checks for special fill styles 4000 .. 4100 + +TAttFill TPadPainterBase::GetAttFillInternal(Bool_t with_transparency) +{ + Style_t style = GetAttFill().GetFillStyle(); + Color_t color = GetAttFill().GetFillColor(); + + fFullyTransparent = (style == 4000) || (style == 0); + if (fFullyTransparent) { + style = 0; + } else if ((style > 4000) && (style <= 4100)) { + if ((style < 4100) && with_transparency) + color = TColor::GetColorTransparent(color, (style - 4000) / 100.); + style = 1001; + } + + return { color, style }; +} From 55a05430a3307ba2dc5a9303f7549b0c9409642e Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Fri, 17 Apr 2026 09:17:48 +0200 Subject: [PATCH 11/23] [pspainter] support fill styles 4000..4100 Handle them as transparent colors --- graf2d/gpad/src/TPadPainterPS.cxx | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/graf2d/gpad/src/TPadPainterPS.cxx b/graf2d/gpad/src/TPadPainterPS.cxx index 0b5a10d80b52e..55b134dc76eeb 100644 --- a/graf2d/gpad/src/TPadPainterPS.cxx +++ b/graf2d/gpad/src/TPadPainterPS.cxx @@ -16,12 +16,12 @@ #include "TPadPainterPS.h" #include "TVirtualPS.h" -#include "TVirtualX.h" #include "TCanvas.h" #include "TPoint.h" #include "TError.h" #include "TImage.h" #include "TROOT.h" +#include "TColor.h" #include "TMath.h" #include "TPad.h" @@ -57,7 +57,9 @@ so actual value can be requested without asking of gVirtualPS instance void TPadPainterPS::SetOpacity(Int_t percent) { - fPS->SetFillStyle(4000 + percent); + TAttFill att = GetAttFill(); + att.SetFillStyle(4000 + percent); + SetAttFill(att); } //////////////////////////////////////////////////////////////////////////////// @@ -67,8 +69,9 @@ void TPadPainterPS::SetAttFill(const TAttFill &att) { TPadPainterBase::SetAttFill(att); - fPS->SetFillColor(att.GetFillColor()); - fPS->SetFillStyle(att.GetFillStyle()); + auto fill = GetAttFillInternal(kTRUE); + fPS->SetFillColor(fill.GetFillColor()); + fPS->SetFillStyle(fill.GetFillStyle()); } //////////////////////////////////////////////////////////////////////////////// @@ -213,7 +216,8 @@ void TPadPainterPS::DrawBox(Double_t x1, Double_t y1, Double_t x2, Double_t y2, style0 = fPS->GetFillStyle(); if (style0 > 0) fPS->SetFillStyle(0); - } + } else if (fFullyTransparent) + return; fPS->DrawBox(x1, y1, x2, y2); @@ -231,7 +235,8 @@ void TPadPainterPS::DrawFillArea(Int_t nPoints, const Double_t *xs, const Double return; } - fPS->DrawPS(-nPoints, const_cast(xs), const_cast(ys)); + if (!fFullyTransparent || (fPS->GetLineWidth() > 0)) + fPS->DrawPS(-nPoints, const_cast(xs), const_cast(ys)); } @@ -245,7 +250,8 @@ void TPadPainterPS::DrawFillArea(Int_t nPoints, const Float_t *xs, const Float_t return; } - fPS->DrawPS(-nPoints, const_cast(xs), const_cast(ys)); + if (!fFullyTransparent || (fPS->GetLineWidth() > 0)) + fPS->DrawPS(-nPoints, const_cast(xs), const_cast(ys)); } //////////////////////////////////////////////////////////////////////////////// From 3299b79ba1a0b413718210fe3774858c14e18677 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Fri, 17 Apr 2026 10:14:35 +0200 Subject: [PATCH 12/23] [glpainter] support fill styles 4000..4100 So any object with such styles can use it --- graf3d/gl/inc/TGLPadPainter.h | 1 + graf3d/gl/src/TGLPadPainter.cxx | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/graf3d/gl/inc/TGLPadPainter.h b/graf3d/gl/inc/TGLPadPainter.h index 57426b6e3e27e..88372aa6f492f 100644 --- a/graf3d/gl/inc/TGLPadPainter.h +++ b/graf3d/gl/inc/TGLPadPainter.h @@ -34,6 +34,7 @@ class TGLPadPainter : public TPadPainterBase { Rgl::Pad::GLLimits fLimits; WinContext_t fWinContext; // context of selected drawable + TAttFill fGlFillAtt; // fill attributes used for GL std::vector fVs;//Vertex buffer for tesselator. diff --git a/graf3d/gl/src/TGLPadPainter.cxx b/graf3d/gl/src/TGLPadPainter.cxx index 858e0312643c8..2a39bc9e6d2f4 100644 --- a/graf3d/gl/src/TGLPadPainter.cxx +++ b/graf3d/gl/src/TGLPadPainter.cxx @@ -101,6 +101,8 @@ void TGLPadPainter::SetAttFill(const TAttFill &att) { TPadPainterBase::SetAttFill(att); + fGlFillAtt = GetAttFillInternal(kTRUE); + // TODO: dismiss in the future, gVirtualX attributes not needed in GL if (fWinContext && gVirtualX) gVirtualX->SetAttFill(fWinContext, att); @@ -437,7 +439,7 @@ void TGLPadPainter::DrawBox(Double_t x1, Double_t y1, Double_t x2, Double_t y2, return; } - if (IsGradientFill(GetAttFill().GetFillColor())) { + if (IsGradientFill(fGlFillAtt.GetFillColor())) { Double_t xs[] = {x1, x2, x2, x1}; Double_t ys[] = {y1, y1, y2, y2}; DrawPolygonWithGradient(4, xs, ys); @@ -452,7 +454,7 @@ void TGLPadPainter::DrawBox(Double_t x1, Double_t y1, Double_t x2, Double_t y2, glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glLineWidth(1.f); } else { - const Rgl::Pad::FillAttribSet fillAttribs(fSSet, kFALSE, &GetAttFill());//Set filling parameters. + const Rgl::Pad::FillAttribSet fillAttribs(fSSet, kFALSE, &fGlFillAtt);//Set filling parameters. glRectd(x1, y1, x2, y2); } } @@ -474,15 +476,15 @@ void TGLPadPainter::DrawFillArea(Int_t n, const Double_t *x, const Double_t *y) return; } - if (IsGradientFill(GetAttFill().GetFillColor())) + if (IsGradientFill(fGlFillAtt.GetFillColor())) return DrawPolygonWithGradient(n, x, y); - if (!GetAttFill().GetFillStyle()) { + if (fFullyTransparent) { fIsHollowArea = kTRUE; return DrawPolyLine(n, x, y); } - const Rgl::Pad::FillAttribSet fillAttribs(fSSet, kFALSE, &GetAttFill()); + const Rgl::Pad::FillAttribSet fillAttribs(fSSet, kFALSE, &fGlFillAtt); DrawTesselation(n, x, y); } @@ -494,7 +496,7 @@ void TGLPadPainter::DrawFillArea(Int_t n, const Float_t *x, const Float_t *y) { if (fLocked) return; - if (!GetAttFill().GetFillStyle()) { + if (fFullyTransparent) { fIsHollowArea = kTRUE; return DrawPolyLine(n, x, y); } @@ -506,7 +508,7 @@ void TGLPadPainter::DrawFillArea(Int_t n, const Float_t *x, const Float_t *y) fVs[i * 3 + 1] = y[i]; } - const Rgl::Pad::FillAttribSet fillAttribs(fSSet, kFALSE, &GetAttFill()); + const Rgl::Pad::FillAttribSet fillAttribs(fSSet, kFALSE, &fGlFillAtt); GLUtesselator *t = (GLUtesselator *)fTess.GetTess(); gluBeginPolygon(t); @@ -1082,7 +1084,7 @@ void TGLPadPainter::DrawPolygonWithGradient(Int_t n, const Double_t *x, const Do assert(x != nullptr && "DrawPolygonWithGradient, parameter 'x' is null"); assert(y != nullptr && "DrawPolygonWithGradient, parameter 'y' is null"); - auto grad = dynamic_cast(gROOT->GetColor(GetAttFill().GetFillColor())); + auto grad = dynamic_cast(gROOT->GetColor(fGlFillAtt.GetFillColor())); assert(grad != nullptr && "DrawPolygonWithGradient, the current fill color is not a gradient fill"); if (fLocked) From 729b893ca3a5d36a25b6e725d1cb7b640665f612 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Fri, 17 Apr 2026 10:22:45 +0200 Subject: [PATCH 13/23] [padpainter] support fill styles 4000..4100 In case of Cocoa use normal color opacity --- graf2d/gpad/src/TPadPainter.cxx | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/graf2d/gpad/src/TPadPainter.cxx b/graf2d/gpad/src/TPadPainter.cxx index 2a65421410fcd..8fbffdce21476 100644 --- a/graf2d/gpad/src/TPadPainter.cxx +++ b/graf2d/gpad/src/TPadPainter.cxx @@ -47,7 +47,7 @@ void ConvertPointsAndMergePassX(TVirtualPad *pad, unsigned nPoints, const T *x, void ConvertPointsAndMergeInplacePassY(std::vector &dst); template -void DrawFillAreaAux(TVirtualPad *pad, WinContext_t cont, Int_t nPoints, const T *xs, const T *ys, Bool_t close_path); +void DrawFillAreaAux(TVirtualPad *pad, WinContext_t cont, Int_t nPoints, const T *xs, const T *ys, Bool_t add_first_point); template void DrawPolyLineAux(TVirtualPad *pad, WinContext_t cont, unsigned nPoints, const T *xs, const T *ys); @@ -211,7 +211,9 @@ void TPadPainter::SetAttFill(const TAttFill &att) { TPadPainterBase::SetAttFill(att); - gVirtualX->SetAttFill(fWinContext, att); + TAttFill fill = GetAttFillInternal(IsCocoa()); + + gVirtualX->SetAttFill(fWinContext, fill); } //////////////////////////////////////////////////////////////////////////////// @@ -284,6 +286,9 @@ void TPadPainter::DrawBox(Double_t x1, Double_t y1, Double_t x2, Double_t y2, EB if (fAttLine.GetLineWidth() <= 0 && mode == TVirtualPadPainter::kHollow) return; + if (fFullyTransparent && mode == TVirtualPadPainter::kFilled) + return; + Int_t px1 = fDoubleBuffer ? gPad->XtoPixel(x1) : gPad->XtoAbsPixel(x1); Int_t px2 = fDoubleBuffer ? gPad->XtoPixel(x2) : gPad->XtoAbsPixel(x2); Int_t py1 = fDoubleBuffer ? gPad->YtoPixel(y1) : gPad->YtoAbsPixel(y1); @@ -308,7 +313,8 @@ void TPadPainter::DrawFillArea(Int_t nPoints, const Double_t *xs, const Double_t return; } - DrawFillAreaAux(gPad, fWinContext, nPoints, xs, ys, fAttFill.GetFillStyle() == 0); + // if fully transparent, add first point to draw line + DrawFillAreaAux(gPad, fWinContext, nPoints, xs, ys, fFullyTransparent); } @@ -322,7 +328,8 @@ void TPadPainter::DrawFillArea(Int_t nPoints, const Float_t *xs, const Float_t * return; } - DrawFillAreaAux(gPad, fWinContext, nPoints, xs, ys, fAttFill.GetFillStyle() == 0); + // if fully transparent, add first point to draw line + DrawFillAreaAux(gPad, fWinContext, nPoints, xs, ys, fFullyTransparent); } //////////////////////////////////////////////////////////////////////////////// @@ -788,7 +795,7 @@ void ConvertPointsAndMerge(TVirtualPad *pad, unsigned threshold, unsigned nPoint //////////////////////////////////////////////////////////////////////////////// template -void DrawFillAreaAux(TVirtualPad *pad, WinContext_t cont, Int_t nPoints, const T *xs, const T *ys, Bool_t close_path) +void DrawFillAreaAux(TVirtualPad *pad, WinContext_t cont, Int_t nPoints, const T *xs, const T *ys, Bool_t add_first_point) { std::vector xy; @@ -806,12 +813,12 @@ void DrawFillAreaAux(TVirtualPad *pad, WinContext_t cont, Int_t nPoints, const T else ConvertPointsAndMerge(pad, threshold, nPoints, xs, ys, xy); - //We close the 'polygon' and it'll be rendered as a polyline by gVirtualX. - if (close_path) + //We close the 'polygon' so it can be rendered as a polyline by gVirtualX. + if (add_first_point) xy.push_back(xy.front()); if (xy.size() > 2) - gVirtualX->DrawFillAreaW(cont, xy.size(), &xy[0]); + gVirtualX->DrawFillAreaW(cont, xy.size(), xy.data()); } //////////////////////////////////////////////////////////////////////////////// From 61a067c7a482500dc8e0c4f72415b654e4ea2b0d Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Fri, 17 Apr 2026 10:50:06 +0200 Subject: [PATCH 14/23] [pad] allow fill styles 4000..4100 for all pad painters Before only fill style 4000..4100 was supported by TPad Now make special handling for plain X11, for all other platforms and all other classes like TBox support 4000..4100 fill styles as color transparency --- graf2d/gpad/inc/TPad.h | 3 +- graf2d/gpad/src/TPad.cxx | 60 +++++++++++++++------------------------- 2 files changed, 23 insertions(+), 40 deletions(-) diff --git a/graf2d/gpad/inc/TPad.h b/graf2d/gpad/inc/TPad.h index 528b9c66579bd..b76a43d3b9fb3 100644 --- a/graf2d/gpad/inc/TPad.h +++ b/graf2d/gpad/inc/TPad.h @@ -136,8 +136,7 @@ friend class TWebCanvas; TPad(const TPad &pad) = delete; TPad &operator=(const TPad &rhs) = delete; - void CopyBackgroundPixmap(Int_t x, Int_t y); - void CopyBackgroundPixmaps(TPad *start, TPad *stop, Int_t x, Int_t y); + void CopyBackgroundPixmaps(TPad *stop, Int_t x, Int_t y); void DrawDist(Rectangle_t aBBox, Rectangle_t bBBox, char mode); Bool_t Collide(Int_t i, Int_t j, Int_t w, Int_t h); diff --git a/graf2d/gpad/src/TPad.cxx b/graf2d/gpad/src/TPad.cxx index 8a4f8da0ae146..201d4ba985b52 100644 --- a/graf2d/gpad/src/TPad.cxx +++ b/graf2d/gpad/src/TPad.cxx @@ -3663,26 +3663,18 @@ void TPad::PaintBorder(Color_t color, Bool_t /* tops */) if (!IsBatch() && (pp->IsCocoa() || (pp->IsNative() && (style > 3000) && (style < 3026)))) pp->ClearDrawable(); - if ((style >= 4000) && (style <= 4100) && pp->IsNative()) { + // special only for transparent pads in plain X11; + // Cocoa, GL, Web and PS implement transparency different + if ((style >= 4000) && (style <= 4100) && pp->IsNative() && !pp->IsCocoa() && !pp->GetPS() && !(fCanvas && fCanvas->UseGL()) && !IsWeb() && !IsBatch()) { if (this == fMother) { style = 1001; - } else if (pp->IsCocoa() || (fCanvas && fCanvas->UseGL())) { - TColor *col = (style == 4000) ? nullptr : gROOT->GetColor(color); - if (!col) { - do_paint_box = kFALSE; - } else { - color = TColor::GetColor(col->GetRed(), col->GetGreen(), col->GetBlue(), (style - 4000) / 100.); - style = 1001; - } } else { // copy all pixmaps do_paint_box = kFALSE; - int px, py; - XYtoAbsPixel(fX1, fY2, px, py); - if (fMother) { - fMother->CopyBackgroundPixmap(px, py); - CopyBackgroundPixmaps(fMother, this, px, py); - } + Int_t px, py; + XYtoAbsPixel(GetX1(), GetY2(), px, py); + if (fMother) + fMother->CopyBackgroundPixmaps(this, px, py); pp->SetOpacity(style - 4000); } } else if ((color == 10) && (style > 3000) && (style < 3100)) @@ -3910,7 +3902,8 @@ void TPad::PaintBox(Double_t x1, Double_t y1, Double_t x2, Double_t y2, Option_t } else if (style > 3000 && style < 3100) { draw_fill = style < 3026; } else if (style >= 4000 && style <= 4100) { - // only used by pad, ignored by all other objects + // transparency styles, supported now by all engines + draw_fill = style > 4000; } else if (style > 0) draw_border = kTRUE; @@ -3932,30 +3925,21 @@ void TPad::PaintBox(Double_t x1, Double_t y1, Double_t x2, Double_t y2, Option_t /// Copy pixmaps of pads laying below pad "stop" into pad "stop". This /// gives the effect of pad "stop" being transparent. -void TPad::CopyBackgroundPixmaps(TPad *start, TPad *stop, Int_t x, Int_t y) +void TPad::CopyBackgroundPixmaps(TPad *stop, Int_t x, Int_t y) { - if (!start) return; - TObject *obj; - if (!fPrimitives) fPrimitives = new TList; - TIter next(start->GetListOfPrimitives()); - while ((obj = next())) { - if (obj->InheritsFrom(TPad::Class())) { - if (obj == stop) break; - ((TPad*)obj)->CopyBackgroundPixmap(x, y); - ((TPad*)obj)->CopyBackgroundPixmaps((TPad*)obj, stop, x, y); - } - } -} - -//////////////////////////////////////////////////////////////////////////////// -/// Copy pixmap of this pad as background of the current pad. - -void TPad::CopyBackgroundPixmap(Int_t x, Int_t y) -{ - int px, py; - XYtoAbsPixel(fX1, fY2, px, py); + Int_t px, py; + XYtoAbsPixel(GetX1(), GetY2(), px, py); + /// Copy pixmap of this pad as background of the current pad. if (auto pp = GetPainter()) - pp->CopyDrawable(GetPixmapID(), px-x, py-y); + pp->CopyDrawable(GetPixmapID(), px - x, py - y); + + TIter next(GetListOfPrimitives()); + while (auto obj = next()) { + if (obj == stop) + break; + if (auto pad = dynamic_cast(obj)) + pad->CopyBackgroundPixmaps(stop, x, y); + } } //////////////////////////////////////////////////////////////////////////////// From 65b666b7482bf8c0221a116dc24353de1c6b1587 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Fri, 17 Apr 2026 11:21:37 +0200 Subject: [PATCH 15/23] [pad] adjust PaintBox No need for special handling of TPadPainterPS - all pad painters handle fill style for the box in same way --- graf2d/gpad/src/TPad.cxx | 33 +++++++++------------------------ 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/graf2d/gpad/src/TPad.cxx b/graf2d/gpad/src/TPad.cxx index 201d4ba985b52..f4d1567e15ed0 100644 --- a/graf2d/gpad/src/TPad.cxx +++ b/graf2d/gpad/src/TPad.cxx @@ -3874,50 +3874,35 @@ void TPad::PaintBox(Double_t x1, Double_t y1, Double_t x2, Double_t y2, Option_t pp->OnPad(this); - TAttFill att = pp->GetAttFill(); - - Int_t style0 = -1111, style = att.GetFillStyle(); - Bool_t draw_border = kFALSE, draw_fill = kFALSE; - if (option && *option == 's') { - style0 = style; - att.SetFillStyle(0); - pp->SetAttFill(att); - style = 0; - draw_border = kTRUE; - } else if (option && *option == 'l') + Style_t style = pp->GetAttFill().GetFillStyle(); + Bool_t draw_border = kFALSE, draw_fill = kFALSE, skip_fill = kFALSE; + if (option && *option == 's') + skip_fill = draw_border = kTRUE; + else if (option && *option == 'l') draw_border = kTRUE; if (style >= 3100 && style < 4000) { Double_t xb[4] = {x1, x1, x2, x2}; Double_t yb[4] = {y1, y2, y2, y1}; PaintFillAreaHatches(4, xb, yb, style); - } else if (pp->GetPS()) { - draw_fill = kTRUE; - if (style == 0) - draw_border = kFALSE; - } else if ((style > 0) && (style < 1000)) { + } else if (style >= 0 && style < 1000) { draw_border = kTRUE; - } else if ((style >= 1000) && (style < 2000)) { + } else if (style >= 1000 && style < 2000) { draw_fill = kTRUE; } else if (style > 3000 && style < 3100) { draw_fill = style < 3026; } else if (style >= 4000 && style <= 4100) { - // transparency styles, supported now by all engines + // transparency styles, supported now by all painters draw_fill = style > 4000; } else if (style > 0) draw_border = kTRUE; - if (draw_fill) + if (draw_fill && !skip_fill) pp->DrawBox(x1, y1, x2, y2, TVirtualPadPainter::kFilled); if (draw_border) pp->DrawBox(x1, y1, x2, y2, TVirtualPadPainter::kHollow); - if (style0 != -1111) { - att.SetFillStyle(style0); - pp->SetAttFill(att); - } - Modified(); } From d5542d3c2f5e28b113bb05c633fb45d60c82c0bd Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Fri, 17 Apr 2026 14:27:03 +0200 Subject: [PATCH 16/23] Extend TAttFill docu for supported styles 4000..4100 Now these are valid styles for all kind of objects --- core/base/src/TAttFill.cxx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/core/base/src/TAttFill.cxx b/core/base/src/TAttFill.cxx index 257318a061268..4f418792202e2 100644 --- a/core/base/src/TAttFill.cxx +++ b/core/base/src/TAttFill.cxx @@ -116,16 +116,13 @@ method `GetFillStyle`. - 0 : hollow - 1001 : Solid - - 3000+pattern_number (see below) - - For TPad only: + - 3000 + pattern_number (see below) + - 4000..4100: 100% transparent .. 100% opaque - - 4000 :the window is transparent. - - 4000 to 4100 the window is 100% transparent to 100% opaque. - - The pad transparency is visible in binary outputs files like gif, jpg, png etc .. - but not in vector graphics output files like PS, PDF and SVG. This convention - (fill style > 4000) is kept for backward compatibility. It is better to use - the color transparency instead. +Historically the styles between 4000 and 4100 were introduced to implement pad +transparency on platforms like X11 which does not support alpha channel in color. +Since ROOT 6.40 any objects can use such fill styles. On supported platforms like +Cocoa or GL or PS/PDF/SVG output style will be automatically converted to transparent colors. pattern_number can have any value from 1 to 25 (see table), or any value from 100 to 999. For the latest the numbering convention is the following: @@ -268,6 +265,9 @@ void TAttFill::SetFillColorAlpha(Color_t fcolor, Float_t falpha) fFillColor = TColor::GetColorTransparent(fcolor, falpha); } +//////////////////////////////////////////////////////////////////////////////// +/// Set a fill color. + void TAttFill::SetFillColor(TColorNumber lcolor) { SetFillColor(lcolor.number()); From 9d5e05f12206475809bb21e010c7b298aa7f8235 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Fri, 17 Apr 2026 15:03:15 +0200 Subject: [PATCH 17/23] Adjust transparentpad.C demo Shows styles 4000..4100 which now works similar on all platforms On X11 only emulation mode is working - any solid painting inside pad will ignore pad opacity --- tutorials/visualisation/gl/transparentpad.C | 63 ++++++++++++++------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/tutorials/visualisation/gl/transparentpad.C b/tutorials/visualisation/gl/transparentpad.C index 886cea12490c3..e3d6bccaf95c5 100644 --- a/tutorials/visualisation/gl/transparentpad.C +++ b/tutorials/visualisation/gl/transparentpad.C @@ -1,14 +1,17 @@ /// \file /// \ingroup tutorial_gl /// This macro demonstrates semi-transparent pads. -/// Requires OpenGL or Web-based canvas. +/// +/// One uses fill styles between 4000 and 4100 to configure objects transparency +/// On OpenGL or Mac/Cocoa or Web-based canvas pads will be drawn with transparent colors. +/// On X11 pixmap transformation performed to partially emulate pads transparency +/// Also demonstrated usage of transparent fill styles for stats box /// /// \macro_image(nobatch) /// \macro_code /// /// \authors Timur Pocheptsov, Sergey Linev -// Includes for ACLiC (cling does not need them). #include "TCanvas.h" #include "TStyle.h" #include "TError.h" @@ -19,12 +22,14 @@ void transparentpad(bool gl = true) { gStyle->SetCanvasPreferGL(gl); + // prevent filling of TFrame with solid color + gStyle->SetFrameFillStyle(0); + // 1. Create canvas and check if it support transparent colors - auto c1 = new TCanvas("transparentpad", "transparent pad demo", 10, 10, 900, 500); - if (!c1->UseGL() && !c1->IsWeb()) + auto c1 = new TCanvas("c1", "transparent pad demo", 10, 10, 900, 500); + if (!c1->UseGL() && !c1->IsWeb() && !gVirtualX->InheritsFrom("TGCocoa")) ::Warning("transparentpad", - "You can see the transparency ONLY in a pdf or png output (\"File\"->\"Save As\" ->...)\n" - "To have transparency in a canvas graphics, you need either OpenGL or Web rendering enabled"); + "To have real transparency in a canvas graphics, you need either OpenGL or Mac/Cocoa or Web rendering enabled"); // 2. Some arbitrary histograms. auto h1 = new TH1F("TH1F 1", "TH1F 1", 100, -1.5, 1.5); @@ -38,23 +43,39 @@ void transparentpad(bool gl = true) // 3. Now overlapping transparent pads. auto pad1 = new TPad("transparent pad 1", "transparent pad 1", 0.1, 0.1, 0.7, 0.7); - pad1->SetFillColor(TColor::GetColor((Float_t)1., 0.2, 0.2, 0.25)); // transparent pink, here's the magic! - c1->cd(); - pad1->Draw(); - pad1->cd(); - h1->Draw("lego2"); + pad1->SetFillColor(kPink); + pad1->SetFillStyle(4040); // transparent pink, here's the magic! + c1->Add(pad1); + pad1->Add(h1, "lego2"); auto pad2 = new TPad("transparent pad 2", "transparent pad 2", 0.2, 0.2, 0.8, 0.8); - pad2->SetFillColor(TColor::GetColor((Float_t)0.2, 1., 0.2, 0.25)); // transparent green, here's the magic! - c1->cd(); - pad2->Draw(); - pad2->cd(); - h2->Draw(); + pad2->SetFillColor(kGreen); + pad2->SetFillStyle(4035); // transparent green, here's the magic! + c1->Add(pad2); + pad2->Add(h2); auto pad3 = new TPad("transparent pad 3", "transparent pad 3", 0.3, 0.3, 0.9, 0.9); - pad3->SetFillColor(TColor::GetColor((Float_t)0.2, 1., 1., 0.15)); // transparent blue, here's the magic! - c1->cd(); - pad3->Draw(); - pad3->cd(); - h3->Draw(); + pad3->SetFillColor(kBlue); + pad3->SetFillStyle(4030); // transparent blue, here's the magic! + c1->Add(pad3); + pad3->Add(h3); + + c1->Update(); + + auto stats2 = dynamic_cast(h2->FindObject("stats")); + if (stats2) { + stats2->SetFillColor(kYellow); + stats2->SetFillStyle(4050); // semi-transparent stats box, only with GL + } + + auto stats3 = dynamic_cast(h3->FindObject("stats")); + if (stats3) { + stats3->SetFillColor(kYellow); + stats3->SetFillStyle(4050); // semi-transparent stats box, only with GL + } + + // SVG or PDF or PS image always support transparent colors + // c1->SaveAs("transparentpad.svg"); + + c1->Modified(); } From cf594b4f0ac88ac991013f8db402def09e50eb58 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Fri, 17 Apr 2026 17:49:35 +0200 Subject: [PATCH 18/23] Adjust stressGraphics_web.ref fillpatterns Now sequence of operation vary a bit therefore produced by web canvas code is slightly different --- test/stressGraphics_web.ref | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/stressGraphics_web.ref b/test/stressGraphics_web.ref index de2e6741d1556..891d260693fe3 100644 --- a/test/stressGraphics_web.ref +++ b/test/stressGraphics_web.ref @@ -4,7 +4,7 @@ tpolyline 611 50 3802 50 27412 10485 24438 14774 611 50 hatches 2082 70 7349 100 190000 100000 32444 15000 2082 70 arrows 1915 50 6155 50 55000 20000 40240 20000 1915 50 - patterns 35154 300 82420 850 265000 50000 160000 65000 35154 300 + patterns 49050 300 93717 850 270000 65000 160000 65000 49050 300 crown 1768 50 10343 50 65000 20000 40977 20000 1768 50 piechart 13043 300 23769 300 100000 40000 98889 30000 13077 300 ttext1 2338 50 4816 50 104754 39759 62489 21274 2338 50 From e7c34464f1213c425c0187bb2e8f2988e8a3ed66 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Mon, 20 Apr 2026 08:19:35 +0200 Subject: [PATCH 19/23] [tgx11] use pad fill color to create opacity In previos implementation just white color with provided opacity was add - making effect absolutely "invisible" Now pad fill color is used. And - original colors reduced by transparency. As a result If opacity 90% original image will remain by 10%. This was fully ignored by original algorithms and therefore opacity did not really work --- graf2d/x11/inc/TGX11.h | 2 +- graf2d/x11/src/TGX11.cxx | 28 ++++++++++------------------ 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/graf2d/x11/inc/TGX11.h b/graf2d/x11/inc/TGX11.h index dc06dc9532723..cb81811c73aa6 100644 --- a/graf2d/x11/inc/TGX11.h +++ b/graf2d/x11/inc/TGX11.h @@ -81,7 +81,7 @@ friend struct XWindow_t; void SetInput(Int_t inp); void CollectImageColors(ULong_t pixel, ULong_t *&orgcolors, Int_t &ncolors, Int_t &maxcolors); - void MakeOpaqueColors(Int_t percent, ULong_t *orgcolors, Int_t ncolors); + void MakeOpaqueColors(Int_t percent, ULong_t *orgcolors, Int_t ncolors, const XColor_t &bkgr); Int_t FindColor(ULong_t pixel, ULong_t *orgcolors, Int_t ncolors); void ImgPickPalette(RXImage *image, Int_t &ncol, Int_t *&R, Int_t *&G, Int_t *&B); diff --git a/graf2d/x11/src/TGX11.cxx b/graf2d/x11/src/TGX11.cxx index 49a6ad2bfb401..2bb467d3e5117 100644 --- a/graf2d/x11/src/TGX11.cxx +++ b/graf2d/x11/src/TGX11.cxx @@ -2495,33 +2495,23 @@ void TGX11::CollectImageColors(ULong_t pixel, ULong_t *&orgcolors, Int_t &ncolor /// Get RGB values for orgcolors, add percent neutral to the RGB and /// allocate fNewColors. -void TGX11::MakeOpaqueColors(Int_t percent, ULong_t *orgcolors, Int_t ncolors) +void TGX11::MakeOpaqueColors(Int_t percent, ULong_t *orgcolors, Int_t ncolors, const XColor_t &bkgr) { if (ncolors == 0) return; RXColor *xcol = new RXColor[ncolors]; - int i; - for (i = 0; i < ncolors; i++) { + for (Int_t i = 0; i < ncolors; i++) { xcol[i].pixel = orgcolors[i]; xcol[i].red = xcol[i].green = xcol[i].blue = 0; xcol[i].flags = DoRed | DoGreen | DoBlue; } QueryColors(fColormap, xcol, ncolors); - UShort_t add = percent * kBIGGEST_RGB_VALUE / 100; - - Int_t val; - for (i = 0; i < ncolors; i++) { - val = xcol[i].red + add; - if (val > kBIGGEST_RGB_VALUE) val = kBIGGEST_RGB_VALUE; - xcol[i].red = (UShort_t) val; - val = xcol[i].green + add; - if (val > kBIGGEST_RGB_VALUE) val = kBIGGEST_RGB_VALUE; - xcol[i].green = (UShort_t) val; - val = xcol[i].blue + add; - if (val > kBIGGEST_RGB_VALUE) val = kBIGGEST_RGB_VALUE; - xcol[i].blue = (UShort_t) val; + for (Int_t i = 0; i < ncolors; i++) { + xcol[i].red = (UShort_t) TMath::Min((Int_t) xcol[i].red * (100 - percent) / 100 + bkgr.fRed * percent / 100, kBIGGEST_RGB_VALUE); + xcol[i].green = (UShort_t) TMath::Min((Int_t) xcol[i].green * (100 - percent) / 100 + bkgr.fGreen * percent / 100, kBIGGEST_RGB_VALUE); + xcol[i].blue = (UShort_t) TMath::Min((Int_t) xcol[i].blue * (100 - percent) / 100 + bkgr.fBlue * percent / 100, kBIGGEST_RGB_VALUE); if (!AllocColor(fColormap, &xcol[i])) Warning("MakeOpaqueColors", "failed to allocate color %hd, %hd, %hd", xcol[i].red, xcol[i].green, xcol[i].blue); @@ -2531,7 +2521,7 @@ void TGX11::MakeOpaqueColors(Int_t percent, ULong_t *orgcolors, Int_t ncolors) gCws->fNewColors = new ULong_t[ncolors]; gCws->fNcolors = ncolors; - for (i = 0; i < ncolors; i++) + for (Int_t i = 0; i < ncolors; i++) gCws->fNewColors[i] = xcol[i].pixel; delete [] xcol; @@ -2790,8 +2780,10 @@ void TGX11::SetOpacityW(WinContext_t wctxt, Int_t percent) return; } + XColor_t &bkgr = GetColor(ctxt->fAttFill.GetFillColor()); + // create opaque counter parts - MakeOpaqueColors(percent, orgcolors, ncolors); + MakeOpaqueColors(percent, orgcolors, ncolors, bkgr); if (ctxt->fNewColors) { // put opaque colors in image From 8586f11161ffe411aa93193ed8b5e4141da5c317 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Mon, 20 Apr 2026 08:22:03 +0200 Subject: [PATCH 20/23] [tpad] set fill attributes when create pad opacity Before just white color was used. --- graf2d/gpad/src/TPad.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/graf2d/gpad/src/TPad.cxx b/graf2d/gpad/src/TPad.cxx index f4d1567e15ed0..3a0ff72783443 100644 --- a/graf2d/gpad/src/TPad.cxx +++ b/graf2d/gpad/src/TPad.cxx @@ -3675,6 +3675,7 @@ void TPad::PaintBorder(Color_t color, Bool_t /* tops */) XYtoAbsPixel(GetX1(), GetY2(), px, py); if (fMother) fMother->CopyBackgroundPixmaps(this, px, py); + pp->SetAttFill({color, 1001}); // use fill color producing opacity pp->SetOpacity(style - 4000); } } else if ((color == 10) && (style > 3000) && (style < 3100)) From 6efe71b95ce4274a5bfac0465dafc1f7ce3439fa Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Mon, 20 Apr 2026 09:32:10 +0200 Subject: [PATCH 21/23] [tgx11] remove CollectImageColors and FindColor methods One can use std::vector for same functionality --- graf2d/x11/inc/TGX11.h | 3 -- graf2d/x11/src/TGX11.cxx | 111 ++++++++++++++------------------------- 2 files changed, 38 insertions(+), 76 deletions(-) diff --git a/graf2d/x11/inc/TGX11.h b/graf2d/x11/inc/TGX11.h index cb81811c73aa6..89be3723dc9e6 100644 --- a/graf2d/x11/inc/TGX11.h +++ b/graf2d/x11/inc/TGX11.h @@ -79,10 +79,7 @@ friend struct XWindow_t; UChar_t *image, Drawable_t id); void SetColor(XWindow_t *ctxt, void *gc, Int_t ci); void SetInput(Int_t inp); - void CollectImageColors(ULong_t pixel, ULong_t *&orgcolors, Int_t &ncolors, - Int_t &maxcolors); void MakeOpaqueColors(Int_t percent, ULong_t *orgcolors, Int_t ncolors, const XColor_t &bkgr); - Int_t FindColor(ULong_t pixel, ULong_t *orgcolors, Int_t ncolors); void ImgPickPalette(RXImage *image, Int_t &ncol, Int_t *&R, Int_t *&G, Int_t *&B); //---- Private methods used for GUI ---- diff --git a/graf2d/x11/src/TGX11.cxx b/graf2d/x11/src/TGX11.cxx index 2bb467d3e5117..d431431491f5f 100644 --- a/graf2d/x11/src/TGX11.cxx +++ b/graf2d/x11/src/TGX11.cxx @@ -2467,30 +2467,6 @@ void TGX11::SetOpacity(Int_t percent) SetOpacityW((WinContext_t) gCws, percent); } -//////////////////////////////////////////////////////////////////////////////// -/// Collect in orgcolors all different original image colors. - -void TGX11::CollectImageColors(ULong_t pixel, ULong_t *&orgcolors, Int_t &ncolors, - Int_t &maxcolors) -{ - if (maxcolors == 0) { - ncolors = 0; - maxcolors = 100; - orgcolors = (ULong_t*) ::operator new(maxcolors*sizeof(ULong_t)); - } - - for (int i = 0; i < ncolors; i++) - if (pixel == orgcolors[i]) return; - - if (ncolors >= maxcolors) { - orgcolors = (ULong_t*) TStorage::ReAlloc(orgcolors, - maxcolors*2*sizeof(ULong_t), maxcolors*sizeof(ULong_t)); - maxcolors *= 2; - } - - orgcolors[ncolors++] = pixel; -} - //////////////////////////////////////////////////////////////////////////////// /// Get RGB values for orgcolors, add percent neutral to the RGB and /// allocate fNewColors. @@ -2527,19 +2503,6 @@ void TGX11::MakeOpaqueColors(Int_t percent, ULong_t *orgcolors, Int_t ncolors, c delete [] xcol; } -//////////////////////////////////////////////////////////////////////////////// -/// Returns index in orgcolors (and fNewColors) for pixel. - -Int_t TGX11::FindColor(ULong_t pixel, ULong_t *orgcolors, Int_t ncolors) -{ - for (int i = 0; i < ncolors; i++) - if (pixel == orgcolors[i]) return i; - - Error("FindColor", "did not find color, should never happen!"); - - return 0; -} - //////////////////////////////////////////////////////////////////////////////// /// Set color intensities for given color index. /// @@ -2754,8 +2717,8 @@ void TGX11::SetOpacityW(WinContext_t wctxt, Int_t percent) auto ctxt = (XWindow_t *) wctxt; - ULong_t *orgcolors = nullptr, *tmpc = nullptr; - Int_t maxcolors = 0, ncolors = 0, ntmpc = 0; + ULong_t *tmpc = nullptr; + Int_t ntmpc = 0; // save previous allocated colors, delete at end when not used anymore if (ctxt->fNewColors) { @@ -2768,30 +2731,36 @@ void TGX11::SetOpacityW(WinContext_t wctxt, Int_t percent) ctxt->fHeight, AllPlanes, ZPixmap); if (!image) return; // collect different image colors - for (unsigned y = 0; y < ctxt->fHeight; y++) { - for (unsigned x = 0; x < ctxt->fWidth; x++) { + + std::vector orgcolors; + + for (UInt_t y = 0; y < ctxt->fHeight; y++) { + for (UInt_t x = 0; x < ctxt->fWidth; x++) { ULong_t pixel = XGetPixel(image, x, y); - CollectImageColors(pixel, orgcolors, ncolors, maxcolors); + if (std::find(orgcolors.begin(), orgcolors.end(), pixel) == orgcolors.end()) + orgcolors.emplace_back(pixel); } } - if (ncolors == 0) { + if (orgcolors.empty()) { XDestroyImage(image); - ::operator delete(orgcolors); return; } XColor_t &bkgr = GetColor(ctxt->fAttFill.GetFillColor()); // create opaque counter parts - MakeOpaqueColors(percent, orgcolors, ncolors, bkgr); + MakeOpaqueColors(percent, orgcolors.data(), orgcolors.size(), bkgr); if (ctxt->fNewColors) { // put opaque colors in image - for (unsigned y = 0; y < ctxt->fHeight; y++) { - for (unsigned x = 0; x < ctxt->fWidth; x++) { + for (UInt_t y = 0; y < ctxt->fHeight; y++) { + for (UInt_t x = 0; x < ctxt->fWidth; x++) { ULong_t pixel = XGetPixel(image, x, y); - Int_t idx = FindColor(pixel, orgcolors, ncolors); - XPutPixel(image, x, y, ctxt->fNewColors[idx]); + auto iter = std::find(orgcolors.begin(), orgcolors.end(), pixel); + if (iter != orgcolors.end()) { + auto idx = iter - orgcolors.begin(); + XPutPixel(image, x, y, ctxt->fNewColors[idx]); + } } } } @@ -2808,7 +2777,6 @@ void TGX11::SetOpacityW(WinContext_t wctxt, Int_t percent) delete [] tmpc; } XDestroyImage(image); - ::operator delete(orgcolors); } //////////////////////////////////////////////////////////////////////////////// @@ -2907,54 +2875,51 @@ static void PutByte(Byte_t b) void TGX11::ImgPickPalette(RXImage *image, Int_t &ncol, Int_t *&R, Int_t *&G, Int_t *&B) { - ULong_t *orgcolors = nullptr; - Int_t maxcolors = 0, ncolors = 0; + std::vector orgcolors; // collect different image colors - int x, y; - for (x = 0; x < (int) gCws->fWidth; x++) { - for (y = 0; y < (int) gCws->fHeight; y++) { + for (UInt_t x = 0; x < gCws->fWidth; x++) { + for (UInt_t y = 0; y < gCws->fHeight; y++) { ULong_t pixel = XGetPixel(image, x, y); - CollectImageColors(pixel, orgcolors, ncolors, maxcolors); + if (std::find(orgcolors.begin(), orgcolors.end(), pixel) == orgcolors.end()) + orgcolors.emplace_back(pixel); } } // get RGB values belonging to pixels - RXColor *xcol = new RXColor[ncolors]; + std::vector xcol(orgcolors.size()); - int i; - for (i = 0; i < ncolors; i++) { + for (size_t i = 0; i < orgcolors.size(); i++) { xcol[i].pixel = orgcolors[i]; xcol[i].red = xcol[i].green = xcol[i].blue = 0; xcol[i].flags = DoRed | DoGreen | DoBlue; } - QueryColors(fColormap, xcol, ncolors); + QueryColors(fColormap, xcol.data(), orgcolors.size()); // create RGB arrays and store RGB's for each color and set number of colors // (space must be delete by caller) - R = new Int_t[ncolors]; - G = new Int_t[ncolors]; - B = new Int_t[ncolors]; + R = new Int_t[orgcolors.size()]; + G = new Int_t[orgcolors.size()]; + B = new Int_t[orgcolors.size()]; - for (i = 0; i < ncolors; i++) { + for (size_t i = 0; i < orgcolors.size(); i++) { R[i] = xcol[i].red; G[i] = xcol[i].green; B[i] = xcol[i].blue; } - ncol = ncolors; + ncol = (Int_t) orgcolors.size(); // update image with indices (pixels) into the new RGB colormap - for (x = 0; x < (int) gCws->fWidth; x++) { - for (y = 0; y < (int) gCws->fHeight; y++) { + for (UInt_t x = 0; x < gCws->fWidth; x++) { + for (UInt_t y = 0; y < gCws->fHeight; y++) { ULong_t pixel = XGetPixel(image, x, y); - Int_t idx = FindColor(pixel, orgcolors, ncolors); - XPutPixel(image, x, y, idx); + auto iter = std::find(orgcolors.begin(), orgcolors.end(), pixel); + if (iter != orgcolors.end()) { + auto idx = iter - orgcolors.begin(); + XPutPixel(image, x, y, idx); + } } } - - // cleanup - delete [] xcol; - ::operator delete(orgcolors); } //////////////////////////////////////////////////////////////////////////////// From f272018e3c8c6424492bef75297010f3a56fb1a2 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Mon, 20 Apr 2026 10:04:32 +0200 Subject: [PATCH 22/23] [tgx11] use std::vector for new colors Use std::vector to keep new colors which are created for opacity emulation. Move MakeOpaqueColors directly to SetOpacityW method. Significantly simplify code --- graf2d/x11/inc/TGX11.h | 1 - graf2d/x11/src/TGX11.cxx | 123 +++++++++++++++------------------------ 2 files changed, 46 insertions(+), 78 deletions(-) diff --git a/graf2d/x11/inc/TGX11.h b/graf2d/x11/inc/TGX11.h index 89be3723dc9e6..4646efa97d19e 100644 --- a/graf2d/x11/inc/TGX11.h +++ b/graf2d/x11/inc/TGX11.h @@ -79,7 +79,6 @@ friend struct XWindow_t; UChar_t *image, Drawable_t id); void SetColor(XWindow_t *ctxt, void *gc, Int_t ci); void SetInput(Int_t inp); - void MakeOpaqueColors(Int_t percent, ULong_t *orgcolors, Int_t ncolors, const XColor_t &bkgr); void ImgPickPalette(RXImage *image, Int_t &ncol, Int_t *&R, Int_t *&G, Int_t *&B); //---- Private methods used for GUI ---- diff --git a/graf2d/x11/src/TGX11.cxx b/graf2d/x11/src/TGX11.cxx index d431431491f5f..3efc79be47e34 100644 --- a/graf2d/x11/src/TGX11.cxx +++ b/graf2d/x11/src/TGX11.cxx @@ -103,8 +103,7 @@ struct XWindow_t { Int_t fYclip = 0; ///< y coordinate of the clipping rectangle UInt_t fWclip = 0; ///< width of the clipping rectangle UInt_t fHclip = 0; ///< height of the clipping rectangle - ULong_t *fNewColors = 0; ///< new image colors (after processing) - Int_t fNcolors = 0; ///< number of different colors + std::vector fNewColors; ///< extra image colors created for transparency (after processing) Bool_t fShared = 0; ///< notify when window is shared GC fGClist[kMAXGC]; ///< list of GC object, individual for each window TVirtualX::EDrawMode drawMode = TVirtualX::kCopy; ///< current draw mode @@ -423,11 +422,10 @@ void TGX11::CloseWindow() if (gCws->fBuffer) XFreePixmap((Display*)fDisplay, gCws->fBuffer); - if (gCws->fNewColors) { + if (!gCws->fNewColors.empty()) { if (fRedDiv == -1) - XFreeColors((Display*)fDisplay, fColormap, gCws->fNewColors, gCws->fNcolors, 0); - delete [] gCws->fNewColors; - gCws->fNewColors = nullptr; + XFreeColors((Display*)fDisplay, fColormap, gCws->fNewColors.data(), gCws->fNewColors.size(), 0); + gCws->fNewColors.clear(); } if (!gCws->fShared) { // if not QT window @@ -1442,7 +1440,6 @@ Int_t TGX11::OpenPixmap(unsigned int w, unsigned int h) gCws->fClip = 0; gCws->fWidth = wval; gCws->fHeight = hval; - gCws->fNewColors = nullptr; gCws->fShared = kFALSE; return wid; @@ -1505,7 +1502,6 @@ Int_t TGX11::InitWindow(ULong_t win) gCws->fClip = 0; gCws->fWidth = wval; gCws->fHeight = hval; - gCws->fNewColors = nullptr; gCws->fShared = kFALSE; return wid; @@ -1531,7 +1527,6 @@ Int_t TGX11::AddWindow(ULong_t qwid, UInt_t w, UInt_t h) gCws->fClip = 0; gCws->fWidth = w; gCws->fHeight = h; - gCws->fNewColors = nullptr; gCws->fShared = kTRUE; return wid; @@ -2467,42 +2462,6 @@ void TGX11::SetOpacity(Int_t percent) SetOpacityW((WinContext_t) gCws, percent); } -//////////////////////////////////////////////////////////////////////////////// -/// Get RGB values for orgcolors, add percent neutral to the RGB and -/// allocate fNewColors. - -void TGX11::MakeOpaqueColors(Int_t percent, ULong_t *orgcolors, Int_t ncolors, const XColor_t &bkgr) -{ - if (ncolors == 0) return; - - RXColor *xcol = new RXColor[ncolors]; - - for (Int_t i = 0; i < ncolors; i++) { - xcol[i].pixel = orgcolors[i]; - xcol[i].red = xcol[i].green = xcol[i].blue = 0; - xcol[i].flags = DoRed | DoGreen | DoBlue; - } - QueryColors(fColormap, xcol, ncolors); - - for (Int_t i = 0; i < ncolors; i++) { - xcol[i].red = (UShort_t) TMath::Min((Int_t) xcol[i].red * (100 - percent) / 100 + bkgr.fRed * percent / 100, kBIGGEST_RGB_VALUE); - xcol[i].green = (UShort_t) TMath::Min((Int_t) xcol[i].green * (100 - percent) / 100 + bkgr.fGreen * percent / 100, kBIGGEST_RGB_VALUE); - xcol[i].blue = (UShort_t) TMath::Min((Int_t) xcol[i].blue * (100 - percent) / 100 + bkgr.fBlue * percent / 100, kBIGGEST_RGB_VALUE); - if (!AllocColor(fColormap, &xcol[i])) - Warning("MakeOpaqueColors", "failed to allocate color %hd, %hd, %hd", - xcol[i].red, xcol[i].green, xcol[i].blue); - // assumes that in case of failure xcol[i].pixel is not changed - } - - gCws->fNewColors = new ULong_t[ncolors]; - gCws->fNcolors = ncolors; - - for (Int_t i = 0; i < ncolors; i++) - gCws->fNewColors[i] = xcol[i].pixel; - - delete [] xcol; -} - //////////////////////////////////////////////////////////////////////////////// /// Set color intensities for given color index. /// @@ -2713,27 +2672,17 @@ void TGX11::UpdateWindowW(WinContext_t wctxt, Int_t mode) void TGX11::SetOpacityW(WinContext_t wctxt, Int_t percent) { if ((fDepth <= 8) || (percent <= 0)) return; - // if 100 percent then just make white + if (percent > 100) percent = 100; auto ctxt = (XWindow_t *) wctxt; - ULong_t *tmpc = nullptr; - Int_t ntmpc = 0; - - // save previous allocated colors, delete at end when not used anymore - if (ctxt->fNewColors) { - tmpc = ctxt->fNewColors; - ntmpc = ctxt->fNcolors; - } - // get pixmap from server as image XImage *image = XGetImage((Display*)fDisplay, ctxt->fDrawing, 0, 0, ctxt->fWidth, ctxt->fHeight, AllPlanes, ZPixmap); if (!image) return; - // collect different image colors + // collect different image colors std::vector orgcolors; - for (UInt_t y = 0; y < ctxt->fHeight; y++) { for (UInt_t x = 0; x < ctxt->fWidth; x++) { ULong_t pixel = XGetPixel(image, x, y); @@ -2746,21 +2695,48 @@ void TGX11::SetOpacityW(WinContext_t wctxt, Int_t percent) return; } + // clean up old colors + if (!ctxt->fNewColors.empty()) { + if (fRedDiv == -1) + XFreeColors((Display*)fDisplay, fColormap, ctxt->fNewColors.data(), ctxt->fNewColors.size(), 0); + ctxt->fNewColors.clear(); + } + + std::vector xcol(orgcolors.size()); + + for (std::size_t i = 0; i < orgcolors.size(); i++) { + xcol[i].pixel = orgcolors[i]; + xcol[i].red = xcol[i].green = xcol[i].blue = 0; + xcol[i].flags = DoRed | DoGreen | DoBlue; + } + QueryColors(fColormap, xcol.data(), orgcolors.size()); + + // create new colors mixing: "100-percent" of old color and "percent" of new background color XColor_t &bkgr = GetColor(ctxt->fAttFill.GetFillColor()); - // create opaque counter parts - MakeOpaqueColors(percent, orgcolors.data(), orgcolors.size(), bkgr); - - if (ctxt->fNewColors) { - // put opaque colors in image - for (UInt_t y = 0; y < ctxt->fHeight; y++) { - for (UInt_t x = 0; x < ctxt->fWidth; x++) { - ULong_t pixel = XGetPixel(image, x, y); - auto iter = std::find(orgcolors.begin(), orgcolors.end(), pixel); - if (iter != orgcolors.end()) { - auto idx = iter - orgcolors.begin(); - XPutPixel(image, x, y, ctxt->fNewColors[idx]); - } + for (std::size_t i = 0; i < orgcolors.size(); i++) { + xcol[i].red = (UShort_t) TMath::Min((Int_t) xcol[i].red * (100 - percent) / 100 + bkgr.fRed * percent / 100, kBIGGEST_RGB_VALUE); + xcol[i].green = (UShort_t) TMath::Min((Int_t) xcol[i].green * (100 - percent) / 100 + bkgr.fGreen * percent / 100, kBIGGEST_RGB_VALUE); + xcol[i].blue = (UShort_t) TMath::Min((Int_t) xcol[i].blue * (100 - percent) / 100 + bkgr.fBlue * percent / 100, kBIGGEST_RGB_VALUE); + if (!AllocColor(fColormap, &xcol[i])) + Warning("SetOpacityW", "failed to allocate color %hd, %hd, %hd", + xcol[i].red, xcol[i].green, xcol[i].blue); + // assumes that in case of failure xcol[i].pixel is not changed + } + + ctxt->fNewColors.resize(orgcolors.size()); + + for (std::size_t i = 0; i < orgcolors.size(); i++) + ctxt->fNewColors[i] = xcol[i].pixel; + + // put opaque colors in image + for (UInt_t y = 0; y < ctxt->fHeight; y++) { + for (UInt_t x = 0; x < ctxt->fWidth; x++) { + ULong_t pixel = XGetPixel(image, x, y); + auto iter = std::find(orgcolors.begin(), orgcolors.end(), pixel); + if (iter != orgcolors.end()) { + auto idx = iter - orgcolors.begin(); + XPutPixel(image, x, y, ctxt->fNewColors[idx]); } } } @@ -2770,12 +2746,6 @@ void TGX11::SetOpacityW(WinContext_t wctxt, Int_t percent) ctxt->fWidth, ctxt->fHeight); XFlush((Display*)fDisplay); - // clean up - if (tmpc) { - if (fRedDiv == -1) - XFreeColors((Display*)fDisplay, fColormap, tmpc, ntmpc, 0); - delete [] tmpc; - } XDestroyImage(image); } @@ -3204,7 +3174,6 @@ Int_t TGX11::AddPixmap(ULong_t pixid, UInt_t w, UInt_t h) gCws->fClip = 0; gCws->fWidth = w; gCws->fHeight = h; - gCws->fNewColors = nullptr; gCws->fShared = kFALSE; return wid; From ab9a7c9f9bfa92679ba37d8ca659c3f0cd4c2faf Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Mon, 20 Apr 2026 10:40:15 +0200 Subject: [PATCH 23/23] [tgwin32] use pad fill color to create opacity Same as in TGX11, mix existing and pad fill color with "100-pecent" and "percent" proportion. Use everywhere std::vector to manage colors. Remove several no longer used methods --- graf2d/win32gdk/inc/TGWin32.h | 2 - graf2d/win32gdk/src/TGWin32.cxx | 257 +++++++++++--------------------- 2 files changed, 87 insertions(+), 172 deletions(-) diff --git a/graf2d/win32gdk/inc/TGWin32.h b/graf2d/win32gdk/inc/TGWin32.h index f46002393677f..236192defbcab 100644 --- a/graf2d/win32gdk/inc/TGWin32.h +++ b/graf2d/win32gdk/inc/TGWin32.h @@ -86,8 +86,6 @@ class TGWin32 : public TVirtualX { void RemovePixmap(GdkDrawable *pix); void SetColor(XWindow_t *ctxt, GdkGC *gc, Int_t ci); void SetInput(Int_t inp); - void MakeOpaqueColors(Int_t percent, ULong_t *orgcolors, Int_t ncolors); - Int_t FindColor(ULong_t pixel, ULong_t *orgcolors, Int_t ncolors); void ImgPickPalette(GdkImage *image, Int_t &ncol, Int_t *&R, Int_t *&G, Int_t *&B); //---- Private methods used for GUI ---- diff --git a/graf2d/win32gdk/src/TGWin32.cxx b/graf2d/win32gdk/src/TGWin32.cxx index 4647a9b38a06c..da3810ee89554 100644 --- a/graf2d/win32gdk/src/TGWin32.cxx +++ b/graf2d/win32gdk/src/TGWin32.cxx @@ -141,8 +141,7 @@ struct XWindow_t { Int_t yclip = 0; ///< y coordinate of the clipping rectangle UInt_t wclip = 0; ///< width of the clipping rectangle UInt_t hclip = 0; ///< height of the clipping rectangle - ULong_t *new_colors = nullptr; ///< new image colors (after processing) - Int_t ncolors = 0; ///< number of different colors + std::vector new_colors; ///< new image colors for transparency (after processing) GdkGC *fGClist[kMAXGC]; ///< array of GC objects for concrete window TVirtualX::EDrawMode drawMode = TVirtualX::kCopy; ///< current draw mode TAttLine fAttLine = {-1, -1, -1}; ///< current line attributes @@ -541,32 +540,6 @@ static ULong_t GetPixelImage(Drawable_t id, Int_t x, Int_t y) return pixel; } -//////////////////////////////////////////////////////////////////////////////// -/// Collect in orgcolors all different original image colors. - -static void CollectImageColors(ULong_t pixel, ULong_t * &orgcolors, - Int_t & ncolors, Int_t & maxcolors) -{ - if (maxcolors == 0) { - ncolors = 0; - maxcolors = 100; - orgcolors = (ULong_t*) ::operator new(maxcolors*sizeof(ULong_t)); - } - - for (int i = 0; i < ncolors; i++) { - if (pixel == orgcolors[i]) return; - } - if (ncolors >= maxcolors) { - orgcolors = (ULong_t *) TStorage::ReAlloc(orgcolors, - maxcolors * 2 * - sizeof(ULong_t), - maxcolors * - sizeof(ULong_t)); - maxcolors *= 2; - } - orgcolors[ncolors++] = pixel; -} - //////////////////////////////////////////////////////////////////////////////// /// debug function for printing event mask @@ -944,7 +917,6 @@ Int_t TGWin32::OpenDisplay(const char *dpyName) GdkPixmap *pixmp1, *pixmp2; GdkColor fore, back; GdkColor color; - int i; HWND hDesktop = ::GetDesktopWindow(); if (!IsWindow(hDesktop) || !IsWindowVisible(hDesktop)) @@ -1044,9 +1016,8 @@ Int_t TGWin32::OpenDisplay(const char *dpyName) // Setup color information fRedDiv = fGreenDiv = fBlueDiv = fRedShift = fGreenShift = fBlueShift = -1; - if ( gdk_visual_get_best_type() == GDK_VISUAL_TRUE_COLOR) { - int i; - for (i = 0; i < int(sizeof(fVisual->blue_mask)*kBitsPerByte); i++) { + if (gdk_visual_get_best_type() == GDK_VISUAL_TRUE_COLOR) { + for (int i = 0; i < int(sizeof(fVisual->blue_mask)*kBitsPerByte); i++) { if (fBlueShift == -1 && ((fVisual->blue_mask >> i) & 1)) { fBlueShift = i; } @@ -1055,7 +1026,7 @@ Int_t TGWin32::OpenDisplay(const char *dpyName) break; } } - for (i = 0; i < int(sizeof(fVisual->green_mask)*kBitsPerByte); i++) { + for (int i = 0; i < int(sizeof(fVisual->green_mask)*kBitsPerByte); i++) { if (fGreenShift == -1 && ((fVisual->green_mask >> i) & 1)) { fGreenShift = i; } @@ -1064,7 +1035,7 @@ Int_t TGWin32::OpenDisplay(const char *dpyName) break; } } - for (i = 0; i < int(sizeof(fVisual->red_mask)*kBitsPerByte); i++) { + for (int i = 0; i < int(sizeof(fVisual->red_mask)*kBitsPerByte); i++) { if (fRedShift == -1 && ((fVisual->red_mask >> i) & 1)) { fRedShift = i; } @@ -1611,12 +1582,11 @@ void TGWin32::CloseWindow() if (gCws->buffer) { gdk_pixmap_unref(gCws->buffer); } - if (gCws->new_colors) { + if (!gCws->new_colors.empty()) { gdk_colormap_free_colors((GdkColormap *) fColormap, - (GdkColor *)gCws->new_colors, gCws->ncolors); + (GdkColor *)gCws->new_colors.data(), gCws->new_colors.size()); - delete [] gCws->new_colors; - gCws->new_colors = nullptr; + gCws->new_colors.clear(); } for (int i = 0; i < kMAXGC; i++) @@ -2142,8 +2112,6 @@ Int_t TGWin32::AddWindowHandle() ctxt->drawing = nullptr; ctxt->window = nullptr; ctxt->buffer = nullptr; - ctxt->new_colors = nullptr; - ctxt->ncolors = 0; ctxt->drawMode = TVirtualX::kCopy; @@ -4081,80 +4049,6 @@ void TGWin32::SetOpacity(Int_t percent) SetOpacityW((WinContext_t) gCws, percent); } -//////////////////////////////////////////////////////////////////////////////// -/// Get RGB values for orgcolors, add percent neutral to the RGB and -/// allocate new_colors. - -void TGWin32::MakeOpaqueColors(Int_t percent, ULong_t *orgcolors, Int_t ncolors) -{ - Int_t ret; - if (ncolors <= 0) return; - GdkColor *xcol = new GdkColor[ncolors]; - - int i; - for (i = 0; i < ncolors; i++) { - xcol[i].pixel = orgcolors[i]; - xcol[i].red = xcol[i].green = xcol[i].blue = 0; - } - - GdkColorContext *cc; - cc = gdk_color_context_new(gdk_visual_get_system(), (GdkColormap *)fColormap); - gdk_color_context_query_colors(cc, xcol, ncolors); - gdk_color_context_free(cc); - - UShort_t add = percent * kBIGGEST_RGB_VALUE / 100; - - Int_t val; - for (i = 0; i < ncolors; i++) { - val = xcol[i].red + add; - if (val > kBIGGEST_RGB_VALUE) { - val = kBIGGEST_RGB_VALUE; - } - xcol[i].red = (UShort_t) val; - val = xcol[i].green + add; - if (val > kBIGGEST_RGB_VALUE) { - val = kBIGGEST_RGB_VALUE; - } - xcol[i].green = (UShort_t) val; - val = xcol[i].blue + add; - if (val > kBIGGEST_RGB_VALUE) { - val = kBIGGEST_RGB_VALUE; - } - xcol[i].blue = (UShort_t) val; - - ret = gdk_color_alloc((GdkColormap *)fColormap, &xcol[i]); - - if (!ret) { - Warning("MakeOpaqueColors", - "failed to allocate color %hd, %hd, %hd", xcol[i].red, - xcol[i].green, xcol[i].blue); - // assumes that in case of failure xcol[i].pixel is not changed - } - } - - gCws->new_colors = new ULong_t[ncolors]; - gCws->ncolors = ncolors; - - for (i = 0; i < ncolors; i++) { - gCws->new_colors[i] = xcol[i].pixel; - } - - delete []xcol; -} - -//////////////////////////////////////////////////////////////////////////////// -/// Returns index in orgcolors (and new_colors) for pixel. - -Int_t TGWin32::FindColor(ULong_t pixel, ULong_t * orgcolors, Int_t ncolors) -{ - for (int i = 0; i < ncolors; i++) { - if (pixel == orgcolors[i]) return i; - } - Error("FindColor", "did not find color, should never happen!"); - - return 0; -} - //////////////////////////////////////////////////////////////////////////////// /// Set color intensities for given color index. /// cindex : color index @@ -4271,49 +4165,82 @@ void TGWin32::UpdateWindowW(WinContext_t wctxt, Int_t mode) void TGWin32::SetOpacityW(WinContext_t wctxt, Int_t percent) { - auto ctxt = (XWindow_t *) wctxt; - Int_t depth = gdk_visual_get_best_depth(); - if (depth <= 8) return; - if (percent == 0) return; + if ((depth <= 8) || (percent <= 0)) return; + if (percent > 100) percent = 100; - // if 100 percent then just make white - ULong_t *orgcolors = 0; - ULong_t *tmpc = 0; - Int_t maxcolors = 0, ncolors, ntmpc = 0; + auto ctxt = (XWindow_t *) wctxt; - // save previous allocated colors, delete at end when not used anymore - if (ctxt->new_colors) { - tmpc = ctxt->new_colors; - ntmpc = ctxt->ncolors; - } // get pixmap from server as image GdkImage *image = gdk_image_get((GdkDrawable*)ctxt->drawing, 0, 0, ctxt->width, ctxt->height); + if (!image) return; + + std::vector orgcolors; + // collect different image colors - int x, y; - for (y = 0; y < (int) ctxt->height; y++) { - for (x = 0; x < (int) ctxt->width; x++) { + for (UInt_t y = 0; y < ctxt->height; y++) { + for (UInt_t x = 0; x < ctxt->width; x++) { ULong_t pixel = GetPixelImage((Drawable_t)image, x, y); - CollectImageColors(pixel, orgcolors, ncolors, maxcolors); + if (std::find(orgcolors.begin(), orgcolors.end(), pixel) == orgcolors.end()) + orgcolors.emplace_back(pixel); } } - if (ncolors == 0) { + if (orgcolors.empty()) { gdk_image_unref(image); - ::operator delete(orgcolors); return; } - // create opaque counter parts - MakeOpaqueColors(percent, orgcolors, ncolors); + + if (!ctxt->new_colors.empty()) { + gdk_colors_free((GdkColormap *)fColormap, ctxt->new_colors.data(), ctxt->new_colors.size(), 0); + ctxt->new_colors.clear(); + } + + std::vector xcol(orgcolors.size()); + + for (std::size_t i = 0; i < orgcolors.size(); i++) { + xcol[i].pixel = orgcolors[i]; + xcol[i].red = xcol[i].green = xcol[i].blue = 0; + } + + GdkColorContext *cc = gdk_color_context_new(gdk_visual_get_system(), (GdkColormap *)fColormap); + gdk_color_context_query_colors(cc, xcol.data(), orgcolors.size()); + gdk_color_context_free(cc); + + // create new colors mixing: "100-percent" of old color and "percent" of new background color + XColor_t &bkgr = GetColor(ctxt->fAttFill.GetFillColor()); + + for (std::size_t i = 0; i < orgcolors.size(); i++) { + xcol[i].red = (UShort_t) TMath::Min((Int_t) xcol[i].red * (100 - percent) / 100 + bkgr.color.red * percent / 100, kBIGGEST_RGB_VALUE); + xcol[i].green = (UShort_t) TMath::Min((Int_t) xcol[i].green * (100 - percent) / 100 + bkgr.color.green * percent / 100, kBIGGEST_RGB_VALUE); + xcol[i].blue = (UShort_t) TMath::Min((Int_t) xcol[i].blue * (100 - percent) / 100 + bkgr.color.blue * percent / 100, kBIGGEST_RGB_VALUE); + + auto ret = gdk_color_alloc((GdkColormap *)fColormap, &xcol[i]); + + if (!ret) { + Warning("SetOpacityW", + "failed to allocate color %hd, %hd, %hd", xcol[i].red, + xcol[i].green, xcol[i].blue); + // assumes that in case of failure xcol[i].pixel is not changed + } + } + + ctxt->new_colors.resize(orgcolors.size()); + + for (std::size_t i = 0; i < orgcolors.size(); i++) + ctxt->new_colors[i] = xcol[i].pixel; // put opaque colors in image - for (y = 0; y < (int) ctxt->height; y++) { - for (x = 0; x < (int) ctxt->width; x++) { + for (UInt_t y = 0; y < ctxt->height; y++) { + for (UInt_t x = 0; x < ctxt->width; x++) { ULong_t pixel = GetPixelImage((Drawable_t)image, x, y); - Int_t idx = FindColor(pixel, orgcolors, ncolors); - PutPixel((Drawable_t)image, x, y, ctxt->new_colors[idx]); + auto iter = std::find(orgcolors.begin(), orgcolors.end(), pixel); + if (iter != orgcolors.end()) { + auto idx = iter - orgcolors.begin(); + PutPixel((Drawable_t)image, x, y, ctxt->new_colors[idx]); + } } } @@ -4323,12 +4250,7 @@ void TGWin32::SetOpacityW(WinContext_t wctxt, Int_t percent) GdiFlush(); // clean up - if (tmpc) { - gdk_colors_free((GdkColormap *)fColormap, tmpc, ntmpc, 0); - delete[]tmpc; - } gdk_image_unref(image); - ::operator delete(orgcolors); } //////////////////////////////////////////////////////////////////////////////// @@ -4443,60 +4365,55 @@ static void PutByte(Byte_t b) void TGWin32::ImgPickPalette(GdkImage * image, Int_t & ncol, Int_t * &R, Int_t * &G, Int_t * &B) { - ULong_t *orgcolors = 0; - Int_t maxcolors = 0, ncolors; + std::vector orgcolors; // collect different image colors - int x, y; - for (x = 0; x < (int) gCws->width; x++) { - for (y = 0; y < (int) gCws->height; y++) { + for (UInt_t x = 0; x < (int) gCws->width; x++) { + for (UInt_t y = 0; y < (int) gCws->height; y++) { ULong_t pixel = GetPixelImage((Drawable_t)image, x, y); - CollectImageColors(pixel, orgcolors, ncolors, maxcolors); + if (std::find(orgcolors.begin(), orgcolors.end(), pixel) == orgcolors.end()) + orgcolors.emplace_back(pixel); } } // get RGB values belonging to pixels - GdkColor *xcol = new GdkColor[ncolors]; + std::vector xcol(orgcolors.size()); - int i; - for (i = 0; i < ncolors; i++) { + for (std::size_t i = 0; i < orgcolors.size(); i++) { xcol[i].pixel = orgcolors[i]; -// xcol[i].red = xcol[i].green = xcol[i].blue = 0; xcol[i].red = GetRValue(xcol[i].pixel); xcol[i].green = GetGValue(xcol[i].pixel); xcol[i].blue = GetBValue(xcol[i].pixel); } - GdkColorContext *cc; - cc = gdk_color_context_new(gdk_visual_get_system(), (GdkColormap *)fColormap); - gdk_color_context_query_colors(cc, xcol, ncolors); + GdkColorContext *cc = gdk_color_context_new(gdk_visual_get_system(), (GdkColormap *)fColormap); + gdk_color_context_query_colors(cc, xcol.data(), orgcolors.size()); gdk_color_context_free(cc); // create RGB arrays and store RGB's for each color and set number of colors // (space must be delete by caller) - R = new Int_t[ncolors]; - G = new Int_t[ncolors]; - B = new Int_t[ncolors]; + R = new Int_t[orgcolors.size()]; + G = new Int_t[orgcolors.size()]; + B = new Int_t[orgcolors.size()]; - for (i = 0; i < ncolors; i++) { + for (std::size_t i = 0; i < orgcolors.size(); i++) { R[i] = xcol[i].red; G[i] = xcol[i].green; B[i] = xcol[i].blue; } - ncol = ncolors; + ncol = (Int_t) orgcolors.size(); // update image with indices (pixels) into the new RGB colormap - for (x = 0; x < (int) gCws->width; x++) { - for (y = 0; y < (int) gCws->height; y++) { + for (UInt_t x = 0; x < gCws->width; x++) { + for (UInt_t y = 0; y < gCws->height; y++) { ULong_t pixel = GetPixelImage((Drawable_t)image, x, y); - Int_t idx = FindColor(pixel, orgcolors, ncolors); - PutPixel((Drawable_t)image, x, y, idx); + auto iter = std::find(orgcolors.begin(), orgcolors.end(), pixel); + if (iter != orgcolors.end()) { + auto idx = iter - orgcolors.begin(); + PutPixel((Drawable_t)image, x, y, idx); + } } } - - // cleanup - delete[]xcol; - ::operator delete(orgcolors); } ////////////////////////////////////////////////////////////////////////////////