Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions etc/plugins/TVirtualImageUtils/P010_TASImageUtils.C
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
void P010_TASImageUtils()
{
gPluginMgr->AddHandler("TVirtualImageUtils", "*", "TASImageUtils", "ASImageUtils", "TASImageUtils()");
}
1 change: 1 addition & 0 deletions graf2d/asimage/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ endif()
ROOT_STANDARD_LIBRARY_PACKAGE(ASImageUtils
NO_INSTALL_HEADERS
HEADERS
TASImageUtils.h
TASUtils.h
SOURCES
TASUtils.cxx
Expand Down
1 change: 1 addition & 0 deletions graf2d/asimage/inc/LinkDefUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#pragma link off all classes;
#pragma link off all functions;

#pragma link C++ class TASImageUtils;
#pragma link C++ class TASUtils;

#endif
28 changes: 28 additions & 0 deletions graf2d/asimage/inc/TASImageUtils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// For the licensing terms see $ROOTSYS/LICENSE.
// For the list of contributors see $ROOTSYS/README/CREDITS.

#ifndef ROOT_TASImageUtils
#define ROOT_TASImageUtils

#include "TASUtils.h"
#include "TVirtualImageUtils.h"

/// Image utility plugin backed by TASImage.
class TASImageUtils : public TVirtualImageUtils {
public:
TASImageUtils() = default;

Bool_t PaintImage(TImage *image, Drawable_t window, Int_t x, Int_t y, Bool_t opaque) override
{
return TASUtils::PaintImage(image, window, x, y, opaque);
}

Bool_t FillImageFromWindow(TImage *image, Window_t window, Int_t width, Int_t height) override
{
return TASUtils::FillImageFromWindow(image, window, width, height);
}

ClassDefOverride(TASImageUtils, 0) // TASImage implementation of native image operations
};

#endif
6 changes: 5 additions & 1 deletion graf2d/gpad/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@
ROOT_ADD_GTEST(TRatioPlot ratioplot.cxx LIBRARIES Gpad)
ROOT_ADD_GTEST(TPad pdftitle.cxx LIBRARIES Gpad)
ROOT_ADD_GTEST(TPDF pdfurl.cxx LIBRARIES Gpad)
ROOT_ADD_GTEST(TCanvasPainter canvaspainter.cxx LIBRARIES Gpad Hist)
add_dependencies(TCanvasPainter ROOTPadPainter HistPainter)

if(asimage)
ROOT_ADD_GTEST(TASImagePDF tasimage_pdf.cxx LIBRARIES Gpad ASImage ZLIB::ZLIB)
ROOT_ADD_GTEST(TPadPainterImage padpainter_image.cxx LIBRARIES Gpad)
add_dependencies(TPadPainterImage ROOTPadPainter ASImageUtils)
endif()

if(webgui)
ROOT_ADD_GTEST(SaveHTML savehtml.cxx LIBRARIES Gpad)
endif()
endif()
23 changes: 23 additions & 0 deletions graf2d/gpad/test/canvaspainter.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#include "gtest/gtest.h"

#include "TCanvas.h"
#include "TH1D.h"
#include "TROOT.h"
#include "TSystem.h"

#include <string>

TEST(TCanvasPainter, BatchHistogramDoesNotLoadImages)
{
gROOT->SetBatch(kTRUE);
ASSERT_EQ(std::string(gSystem->GetLibraries()).find("libASImage"), std::string::npos);
TCanvas canvas("batch_painter", "batch painter");
ASSERT_NE(canvas.GetCanvasPainter(), nullptr);

TH1D histogram("histogram", "Batch histogram;X;Entries", 10, 0., 10.);
histogram.Fill(5.);
histogram.Draw();
canvas.Update();
EXPECT_FALSE(canvas.IsModified());
EXPECT_EQ(std::string(gSystem->GetLibraries()).find("libASImage"), std::string::npos);
}
91 changes: 91 additions & 0 deletions graf2d/gpad/test/padpainter_image.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#include "gtest/gtest.h"

#include "TCanvas.h"
#include "TImage.h"
#include "TPluginManager.h"
#include "TROOT.h"
#include "TSystem.h"
#include "TVirtualPadPainter.h"
#include "TVirtualX.h"

#include <algorithm>
#include <memory>
#include <string>

namespace {

// Supply pixels and record image painting without requiring a window server.
class ImageBackend : public TVirtualX {
public:
Int_t fCopies = 0;
Int_t fX = 0;
Int_t fY = 0;
UInt_t fWidth = 0;
UInt_t fHeight = 0;

Window_t GetWindowID(Int_t) override { return 1; }
Window_t GetCurrentWindow() const override { return 1; }
GContext_t CreateGC(Drawable_t, GCValues_t *) override { return 1; }
Pixmap_t CreatePixmapFromData(unsigned char *, UInt_t, UInt_t) override { return 1; }

unsigned char *GetColorBits(Drawable_t, Int_t, Int_t, UInt_t width, UInt_t height) override
{
auto pixels = new unsigned char[4 * width * height];
std::fill_n(pixels, 4 * width * height, 255);
return pixels;
}

void CopyArea(Drawable_t, Drawable_t, GContext_t, Int_t, Int_t, UInt_t w, UInt_t h, Int_t x, Int_t y) override
{
++fCopies;
fX = x;
fY = y;
fWidth = w;
fHeight = h;
}
};

class BackendGuard {
TVirtualX *fPrevious = gVirtualX;

public:
explicit BackendGuard(TVirtualX &backend) { gVirtualX = &backend; }
~BackendGuard() { gVirtualX = fPrevious; }
};

} // namespace

TEST(TPadPainterImage, LoadHelperPluginOnDemand)
{
gROOT->SetBatch(kTRUE);
ASSERT_EQ(std::string(gSystem->GetLibraries()).find("libASImage"), std::string::npos);
TCanvas canvas("image_painter", "image painter", 64, 64);
auto painter = canvas.GetPainter();
ASSERT_NE(painter, nullptr);
painter->OnPad(&canvas);

ASSERT_NE(gPluginMgr->FindHandler("TVirtualImageUtils"), nullptr);
EXPECT_EQ(std::string(gSystem->GetLibraries()).find("libASImage"), std::string::npos);

std::unique_ptr<TImage> image(TImage::Create());
ASSERT_NE(image, nullptr);
EXPECT_EQ(std::string(gSystem->GetLibraries()).find("libASImageUtils"), std::string::npos);

ImageBackend backend;
BackendGuard guard(backend);
ASSERT_TRUE(painter->FillImageFromPad(image.get(), &canvas));
EXPECT_TRUE(image->IsValid());
EXPECT_EQ(image->GetWidth(), canvas.GetPadWidth());
EXPECT_EQ(image->GetHeight(), canvas.GetPadHeight());
EXPECT_NE(std::string(gSystem->GetLibraries()).find("libASImageUtils"), std::string::npos);

painter->DrawImage(image.get(), 7, 9);
EXPECT_EQ(backend.fCopies, 1);
EXPECT_EQ(backend.fX, 7);
EXPECT_EQ(backend.fY, 9);
EXPECT_EQ(backend.fWidth, image->GetWidth());
EXPECT_EQ(backend.fHeight, image->GetHeight());

TImage unsupportedImage(1, 1);
EXPECT_FALSE(painter->FillImageFromPad(&unsupportedImage, &canvas));
}
2 changes: 2 additions & 0 deletions graf2d/graf/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ ROOT_STANDARD_LIBRARY_PACKAGE(Graf
TTF.h
TTFhandle.h
TText.h
TVirtualImageUtils.h
TWbox.h
SOURCES
src/TArc.cxx
Expand Down Expand Up @@ -87,6 +88,7 @@ ROOT_STANDARD_LIBRARY_PACKAGE(Graf
src/TTF.cxx
src/TTFhandle.cxx
src/TText.cxx
src/TVirtualImageUtils.cxx
src/TWbox.cxx
DICTIONARY_OPTIONS
-writeEmptyRootPCM
Expand Down
1 change: 1 addition & 0 deletions graf2d/graf/inc/LinkDef.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
#pragma link C++ class TText-;
#pragma link C++ class TTF;
#pragma link C++ class TTFhandle;
#pragma link C++ class TVirtualImageUtils;
#pragma link C++ class TWbox+;

#pragma link C++ enum TImage::EImageFileTypes;
Expand Down
25 changes: 25 additions & 0 deletions graf2d/graf/inc/TVirtualImageUtils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// For the licensing terms see $ROOTSYS/LICENSE.
// For the list of contributors see $ROOTSYS/README/CREDITS.

#ifndef ROOT_TVirtualImageUtils
#define ROOT_TVirtualImageUtils

#include "Rtypes.h"
#include "GuiTypes.h"

class TImage;

/// Interface for image operations on native windows.
class TVirtualImageUtils {
public:
virtual ~TVirtualImageUtils() = default;

virtual Bool_t PaintImage(TImage *image, Drawable_t window, Int_t x, Int_t y, Bool_t opaque) = 0;
virtual Bool_t FillImageFromWindow(TImage *image, Window_t window, Int_t width, Int_t height) = 0;

static TVirtualImageUtils *Create();

ClassDef(TVirtualImageUtils, 0) // Image operations for native windows
};

#endif
18 changes: 18 additions & 0 deletions graf2d/graf/src/TVirtualImageUtils.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// For the licensing terms see $ROOTSYS/LICENSE.
// For the list of contributors see $ROOTSYS/README/CREDITS.

#include "TVirtualImageUtils.h"
#include "TPluginManager.h"

////////////////////////////////////////////////////////////////////////////////
/// Create an image utility plugin. The caller owns the returned object.
/// Returns nullptr if no implementation can be loaded.

TVirtualImageUtils *TVirtualImageUtils::Create()
{
auto handler = gPluginMgr->FindHandler("TVirtualImageUtils");
if (!handler || handler->LoadPlugin() == -1)
return nullptr;

return reinterpret_cast<TVirtualImageUtils *>(handler->ExecPlugin(0));
}
6 changes: 0 additions & 6 deletions graf2d/padpainter/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,4 @@ ROOT_STANDARD_LIBRARY_PACKAGE(ROOTPadPainter
)


if(asimage)
target_link_libraries(ROOTPadPainter PUBLIC ASImageUtils)
target_compile_definitions(ROOTPadPainter PRIVATE USE_ASIMAGEUTILS)
endif()


ROOT_INSTALL_HEADERS()
53 changes: 25 additions & 28 deletions graf2d/padpainter/src/TPadPainter.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,17 @@
#include "TROOT.h"
#include "TMath.h"
#include "TPad.h"

#ifdef USE_ASIMAGEUTILS
#include "TASUtils.h"
#endif
#include "TVirtualImageUtils.h"

namespace {

// Image helpers must not become a load-time dependency of ordinary pad painting.
TVirtualImageUtils *GetImageUtils()
{
static const std::unique_ptr<TVirtualImageUtils> imageUtils(TVirtualImageUtils::Create());
return imageUtils.get();
}

//Typedef is fine, but let's pretend we look cool and modern:
using size_type = std::vector<TPoint>::size_type;

Expand Down Expand Up @@ -232,19 +236,13 @@ void TPadPainter::DrawImage(TImage *img, Int_t x, Int_t y, Int_t flags)
{
Window_t wid = gVirtualX->GetWindowID(fPad->GetPixmapID());

// use old API to draw image on gVirtualX window
// img->PaintImage(wid, x, y, 0, 0, 0, 0, flags ? "" : "opaque");
#ifdef USE_ASIMAGEUTILS
// new API invokes
TASUtils::PaintImage(img, wid, x, y, !flags);
#else
::Error("TPadPainter::DrawImage", "TASImage not compiled, image painting is not possible");
(void) wid;
(void) img;
(void) x;
(void) y;
(void) flags;
#endif
auto imageUtils = GetImageUtils();
if (!imageUtils) {
::Error("TPadPainter::DrawImage", "Cannot load image utility plugin");
return;
}

imageUtils->PaintImage(img, wid, x, y, !flags);
}

////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -589,29 +587,28 @@ TVirtualPadPainter::ESaveImageResult TPadPainter::SaveAsImage(TVirtualPad *pad,

////////////////////////////////////////////////////////////////////////////////
/// Fill image object with the pixel data from the pad
/// Use TASUtils::FillImageFromWindow method which provides native X11 handling
/// Use the image utility plugin, which provides native X11 handling.

Bool_t TPadPainter::FillImageFromPad(TImage *image, TVirtualPad *pad) const
{
if (!image || !pad)
return kFALSE;

#ifdef USE_ASIMAGEUTILS
auto width = pad->GetPadWidth();
auto height = pad->GetPadHeight();
auto imageUtils = GetImageUtils();
if (!imageUtils) {
::Error("TPadPainter::FillImageFromPad", "Cannot load image utility plugin");
return kFALSE;
}

Int_t width = pad->GetPadWidth();
Int_t height = pad->GetPadHeight();

Int_t wid = (pad == pad->GetCanvas()) ? pad->GetCanvasID() : pad->GetPixmapID();
gVirtualX->SelectWindow(wid);

Window_t wd = gVirtualX->GetCurrentWindow();

return TASUtils::FillImageFromWindow(image, wd, width, height);
#else
::Error("TPadPainter::FillImageFromPad", "Cannot work without TASImage class");
(void) image;
(void) pad;
return kFALSE;
#endif
return imageUtils->FillImageFromWindow(image, wd, width, height);
}

//Aux. private functions.
Expand Down
Loading