diff --git a/core/base/inc/TVirtualPS.h b/core/base/inc/TVirtualPS.h index 604caedfa7dea..17fb37b3a6c39 100644 --- a/core/base/inc/TVirtualPS.h +++ b/core/base/inc/TVirtualPS.h @@ -27,6 +27,8 @@ #include "TAttText.h" #include "TAttMarker.h" +class TImage; + class TVirtualPS : public TNamed, public TAttLine, public TAttFill, public TAttMarker, public TAttText { private: @@ -54,6 +56,9 @@ class TVirtualPS : public TNamed, public TAttLine, public TAttFill, public TAttM virtual void CellArrayFill(Int_t r, Int_t g, Int_t b) = 0; virtual void CellArrayPng(char * /* buffer */, int /* size */) {} virtual void CellArrayEnd() = 0; + + virtual void DrawImage(TImage * /* img */, Int_t /* x */, Int_t /* y */ , Int_t /* flags */ = 0) {} + virtual void Close(Option_t *opt="") = 0; virtual void DrawBox(Double_t x1, Double_t y1,Double_t x2, Double_t y2) = 0; virtual void DrawFrame(Double_t xl, Double_t yl, Double_t xt, Double_t yt, diff --git a/core/base/inc/TVirtualPadPainter.h b/core/base/inc/TVirtualPadPainter.h index 79d1b22cb6ee6..9ad912fc32831 100644 --- a/core/base/inc/TVirtualPadPainter.h +++ b/core/base/inc/TVirtualPadPainter.h @@ -17,6 +17,7 @@ class TVirtualPad; class TVirtualPS; +class TImage; class TAttFill; class TAttLine; class TAttMarker; @@ -151,6 +152,8 @@ class TVirtualPadPainter { virtual void DrawTextUrl(Double_t x, Double_t y, const char *text, const char *url); + virtual void DrawImage(TImage *img, Int_t x, Int_t y, Int_t flags = 0); + //gif, jpg, png, bmp output. virtual void SaveImage(TVirtualPad *pad, const char *fileName, Int_t type) const = 0; diff --git a/core/base/src/TVirtualPadPainter.cxx b/core/base/src/TVirtualPadPainter.cxx index e3c6795cdd82e..cec650fa652a5 100644 --- a/core/base/src/TVirtualPadPainter.cxx +++ b/core/base/src/TVirtualPadPainter.cxx @@ -212,3 +212,10 @@ void TVirtualPadPainter::SetCursor(Int_t device, ECursor cursor) gVirtualX->SetCursor(device, cursor); } + +//////////////////////////////////////////////////////////////////////////////// +/// Draw image, need to be implemented in correspondent + +void TVirtualPadPainter::DrawImage(TImage *, Int_t, Int_t, Int_t) +{ +} diff --git a/graf2d/asimage/inc/TASImage.h b/graf2d/asimage/inc/TASImage.h index 032e2ee963b2c..f5c7a6eda143a 100644 --- a/graf2d/asimage/inc/TASImage.h +++ b/graf2d/asimage/inc/TASImage.h @@ -206,6 +206,9 @@ class TASImage : public TImage { void SetPaletteEnabled(Bool_t on = kTRUE) override; // *TOGGLE* void SavePrimitive(std::ostream &out, Option_t *option = "") override; + std::vector GetMaskBits(UInt_t &w, UInt_t &h) override; + void DrawX11(Drawable_t wid, void *gc, Int_t x, Int_t y) override; + static const ASVisual *GetVisual(); static UInt_t AlphaBlend(UInt_t bot, UInt_t top); static void Image2Drawable(ASImage *im, Drawable_t wid, Int_t x, Int_t y, Int_t xsrc = 0, Int_t ysrc = 0, UInt_t wsrc = 0, UInt_t hsrc = 0, Option_t *opt = ""); diff --git a/graf2d/asimage/src/TASImage.cxx b/graf2d/asimage/src/TASImage.cxx index f3589b32740b5..6ea0940372606 100644 --- a/graf2d/asimage/src/TASImage.cxx +++ b/graf2d/asimage/src/TASImage.cxx @@ -1341,6 +1341,23 @@ void TASImage::Image2Drawable(ASImage *im, Drawable_t wid, Int_t x, Int_t y, } +//////////////////////////////////////////////////////////////////////////////// +/// Draw image on the drawable wid (pixmap, window) at x,y position. + +void TASImage::DrawX11(Drawable_t wid, void *gc, Int_t x, Int_t y) +{ + ASImage *img = fScaledImage ? fScaledImage->fImage : fImage; + + if (!img || !gc) + return; + + UInt_t hsrc = img->height; + UInt_t ow = img->width % 8; + UInt_t wsrc = img->width - ow + (ow ? 8 : 0); + + asimage2drawable(fgVisual, wid, img, *((GC*)gc), 0, 0, x, y, wsrc, hsrc, 1); +} + //////////////////////////////////////////////////////////////////////////////// /// Draw image on the drawable wid (pixmap, window) at x,y position. /// @@ -1411,6 +1428,9 @@ void TASImage::Paint(Option_t *option) } } + // opaque flag used in some X11 + Int_t flags = opt.Contains("opaque") ? 0 : 1; + ASImage *image = fImage; // Get geometry of pad @@ -1536,173 +1556,42 @@ void TASImage::Paint(Option_t *option) int tox = expand ? 0 : int(gPad->UtoPixel(1.) * gPad->GetLeftMargin()); int toy = expand ? 0 : int(gPad->VtoPixel(0.) * gPad->GetTopMargin()); - auto ps = gPad->GetPainter()->GetPS(); + auto pp = gPad->GetPainter(); - if (!ps) { - Window_t wid = (Window_t)gVirtualX->GetWindowID(gPad->GetPixmapID()); - Image2Drawable(fScaledImage ? fScaledImage->fImage : fImage, wid, tox, toy); + pp->DrawImage(fScaledImage ? fScaledImage : this, tox, toy, flags); - if (grad_im && fPaletteEnabled) { - // draw color bar - Image2Drawable(grad_im, wid, pal_x, pal_y); + if (grad_im && fPaletteEnabled) { + TASImage pimg; + pimg.fImage = grad_im; + grad_im = nullptr; // will be delete with pimg destructor - // values of palette - TGaxis axis; - Int_t ndiv = 510; - double min = fMinValue; - double max = fMaxValue; + // draw color bar + pp->DrawImage(&pimg, pal_x, pal_y, flags); + + // values of palette + TGaxis axis; + Int_t ndiv = 510; + Double_t min = fMinValue; + Double_t max = fMaxValue; + Double_t pal_Xpos = gPad->AbsPixeltoX(pal_Ax + pal_w); + if (!pp->GetPS()) { + // TODO: check why here special drawing for none-PS axis.SetLineColor(0); // draw white ticks - Double_t pal_Xpos = gPad->AbsPixeltoX(pal_Ax + pal_w); axis.PaintAxis(pal_Xpos, gPad->PixeltoY(pal_Ay + pal_h - 1), pal_Xpos, gPad->PixeltoY(pal_Ay), min, max, ndiv, "+LU"); min = fMinValue; max = fMaxValue; - axis.SetLineColor(1); // draw black ticks - axis.PaintAxis(pal_Xpos, gPad->AbsPixeltoY(pal_Ay + pal_h), - pal_Xpos, gPad->AbsPixeltoY(pal_Ay + 1), - min, max, ndiv, "+L"); - } - } else { - // loop over pixmap and draw image to PostScript - - Bool_t paint_as_png = kFALSE; - - if (ps->InheritsFrom("TImageDump")) { // PostScript is asimage - TImage *dump = (TImage *)ps->GetStream(); - if (!dump) return; - dump->Merge(fScaledImage ? fScaledImage : this, "alphablend", - gPad->XtoAbsPixel(0), gPad->YtoAbsPixel(1)); - - if (grad_im) { - TASImage tgrad; - tgrad.fImage = grad_im; - dump->Merge(&tgrad, "alphablend", pal_Ax, pal_Ay); - - // values of palette - TGaxis axis; - Int_t ndiv = 510; - double min = fMinValue; - double max = fMaxValue; - axis.SetLineColor(1); // draw black ticks - Double_t pal_Xpos = gPad->AbsPixeltoX(pal_Ax + pal_w); - axis.PaintAxis(pal_Xpos, gPad->AbsPixeltoY(pal_Ay + pal_h), - pal_Xpos, gPad->AbsPixeltoY(pal_Ay + 1), - min, max, ndiv, "+L"); - } - return; - } else if (ps->InheritsFrom("TSVG")) { - paint_as_png = kTRUE; - } - - Double_t dx = gPad->GetX2() - gPad->GetX1(); - Double_t dy = gPad->GetY2() - gPad->GetY1(); - Double_t x1, x2, y1, y2; - - if (expand) { - x1 = gPad->GetX1(); - x2 = x1+dx/image->width; - y1 = gPad->GetY2(); - y2 = y1+dy/image->height; - } else { - x1 = gPad->GetX1()+dx*gPad->GetLeftMargin(); - x2 = x1+(dx*(1-gPad->GetRightMargin()-gPad->GetLeftMargin()))/image->width; - y1 = gPad->GetY2()-dy*gPad->GetTopMargin(); - y2 = y1+(dy*(1-gPad->GetTopMargin()-gPad->GetBottomMargin()))/image->height; } - // get special color cell to be reused during image printing - ps->SetFillColor(TColor::GetColor((Float_t) 1., (Float_t) 1., (Float_t) 1.)); - ps->SetFillStyle(1001); - - ps->CellArrayBegin(image->width, image->height, x1, x2, y1, y2); - - if (paint_as_png) { - char *buffer = nullptr; - int size = 0; - ASImageExportParams params; - params.png.type = ASIT_Png; - params.png.flags = EXPORT_ALPHA; - params.png.compression = GetImageCompression(); - if (!params.png.compression) - params.png.compression = -1; - if (ASImage2PNGBuff(image, (CARD8 **)&buffer, &size, ¶ms)) { - ps->CellArrayPng(buffer, size); - free(buffer); - } - } else { - auto imdec = start_image_decoding(fgVisual, image, SCL_DO_ALL, - 0, 0, image->width, image->height, nullptr); - if (imdec) - for (Int_t yt = 0; yt < (Int_t)image->height; yt++) { - imdec->decode_image_scanline(imdec); - for (Int_t xt = 0; xt < (Int_t)image->width; xt++) - ps->CellArrayFill(imdec->buffer.red[xt], - imdec->buffer.green[xt], - imdec->buffer.blue[xt]); - } - stop_image_decoding(&imdec); - } - ps->CellArrayEnd(); - - // print the color bar - if (grad_im) { - Double_t xconv = (gPad->AbsPixeltoX(pal_Ax + pal_w) - gPad->AbsPixeltoX(pal_Ax)) / grad_im->width; - Double_t yconv = (gPad->AbsPixeltoY(pal_Ay - pal_h) - gPad->AbsPixeltoY(pal_Ay)) / grad_im->height; - x1 = gPad->AbsPixeltoX(pal_Ax); - x2 = x1 + xconv; - y2 = gPad->AbsPixeltoY(pal_Ay); - y1 = y2 - yconv; - ps->CellArrayBegin(grad_im->width, grad_im->height, - x1, x2, y1, y2); - - if (paint_as_png) { - char *buffer = nullptr; - int size = 0; - ASImageExportParams params; - params.png.type = ASIT_Png; - params.png.flags = EXPORT_ALPHA; - params.png.compression = GetImageCompression(); - if (!params.png.compression) - params.png.compression = -1; - - if (ASImage2PNGBuff(grad_im, (CARD8 **)&buffer, &size, ¶ms)) { - ps->CellArrayPng(buffer, size); - free(buffer); - } - } else { - auto imdec = start_image_decoding(fgVisual, grad_im, SCL_DO_ALL, - 0, 0, grad_im->width, grad_im->height, nullptr); - if (imdec) - for (Int_t yt = 0; yt < (Int_t)grad_im->height; yt++) { - imdec->decode_image_scanline(imdec); - for (Int_t xt = 0; xt < (Int_t)grad_im->width; xt++) - ps->CellArrayFill(imdec->buffer.red[xt], - imdec->buffer.green[xt], - imdec->buffer.blue[xt]); - } - stop_image_decoding(&imdec); - } - ps->CellArrayEnd(); - - // values of palette - TGaxis axis; - Int_t ndiv = 510; - double min = fMinValue; - double max = fMaxValue; - axis.SetLineColor(1); // draw black ticks - Double_t pal_Xpos = gPad->AbsPixeltoX(pal_Ax + pal_w); - // TODO: provide PaintAxisOn method - axis.PaintAxis(pal_Xpos, gPad->AbsPixeltoY(pal_Ay + pal_h), - pal_Xpos, gPad->AbsPixeltoY(pal_Ay + 1), - min, max, ndiv, "+L"); - - } + axis.SetLineColor(1); // draw black ticks + axis.PaintAxis(pal_Xpos, gPad->AbsPixeltoY(pal_Ay + pal_h), + pal_Xpos, gPad->AbsPixeltoY(pal_Ay + 1), + min, max, ndiv, "+L"); } - if (grad_im) { + if (grad_im) destroy_asimage(&grad_im); - } } //////////////////////////////////////////////////////////////////////////////// @@ -2341,35 +2230,35 @@ Pixmap_t TASImage::GetPixmap() } //////////////////////////////////////////////////////////////////////////////// -/// Returns image mask pixmap (alpha channel). -/// The pixmap must deleted by user. +/// Returns image mask pixmap (alpha channel) as vector. -Pixmap_t TASImage::GetMask() +std::vector TASImage::GetMaskBits(UInt_t &ww, UInt_t &hh) { - Pixmap_t pxmap = 0; + std::vector bits; + ww = hh = 0; if (!InitVisual()) { - Warning("GetMask", "Visual not initiated"); - return pxmap; + Warning("GetMaskBits", "Visual not initiated"); + return bits; } ASImage *img = fScaledImage ? fScaledImage->fImage : fImage; if (!img) { - Warning("GetMask", "No image"); - return pxmap; + Warning("GetMaskBits", "No image"); + return bits; } - UInt_t hh = img->height; + hh = img->height; UInt_t ow = img->width%8; - UInt_t ww = img->width - ow + (ow ? 8 : 0); + ww = img->width - ow + (ow ? 8 : 0); ASImageDecoder *imdec = start_image_decoding(fgVisual, img, SCL_DO_ALPHA, 0, 0, ww, 0, nullptr); if (!imdec) - return pxmap; + return bits; - std::vector bits(ww * hh / 8); //an array of bits + bits.resize(ww * hh / 8); //an array of bits UInt_t bit = 0, i = 0; for (UInt_t y = 0; y < hh; y++) { @@ -2390,7 +2279,25 @@ Pixmap_t TASImage::GetMask() } stop_image_decoding(&imdec); - pxmap = gVirtualX->CreateBitmap(gVirtualX->GetDefaultRootWindow(), bits.data(), ww, hh); + + return bits; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Returns image mask pixmap (alpha channel). +/// The pixmap must deleted by user. + +Pixmap_t TASImage::GetMask() +{ + Pixmap_t pxmap = 0; + + UInt_t ww, hh; + + auto bits = GetMaskBits(ww, hh); + + if (bits.size() > 0) + pxmap = gVirtualX->CreateBitmap(gVirtualX->GetDefaultRootWindow(), (const char *) bits.data(), ww, hh); + return pxmap; } @@ -5773,7 +5680,7 @@ void TASImage::DrawTextTTF(Int_t x, Int_t y, const char *text, Int_t size, ttf.PrepareString(text); ttf.LayoutGlyphs(); - if (ttf.ApplyAlignRotate(x, y, 11, GetWidth(), GetHeight())) + if (ttf.ApplyAlignRotate(x, y, 13, GetWidth(), GetHeight())) DrawFTGlyphs(ttf, color, x, y); } @@ -5784,19 +5691,26 @@ void TASImage::DrawTextTTF(Int_t x, Int_t y, const char *text, Int_t size, void TASImage::GetImageBuffer(char **buffer, int *size, EImageFileTypes type) { - static ASImageExportParams params; - Bool_t ret = kFALSE; ASImage *img = fScaledImage ? fScaledImage->fImage : fImage; - if (!img) return; + Bool_t ret = kFALSE; switch (type) { case TImage::kXpm: - ret = ASImage2xpmRawBuff(img, (CARD8 **)buffer, size, nullptr); + if (img) + ret = ASImage2xpmRawBuff(img, (CARD8 **)buffer, size, nullptr); break; - case TImage::kPng: - ret = ASImage2PNGBuff(img, (CARD8 **)buffer, size, ¶ms); + case TImage::kPng: { + ASImageExportParams params; + params.png.type = ASIT_Png; + params.png.flags = EXPORT_ALPHA; + params.png.compression = GetImageCompression(); + if (!params.png.compression) + params.png.compression = -1; + if (img) + ret = ASImage2PNGBuff(img, (CARD8 **)buffer, size, ¶ms); break; + } default: ret = kFALSE; } diff --git a/graf2d/gpad/inc/TPadPainter.h b/graf2d/gpad/inc/TPadPainter.h index 17275ae1d7c39..08aa50a76f0e3 100644 --- a/graf2d/gpad/inc/TPadPainter.h +++ b/graf2d/gpad/inc/TPadPainter.h @@ -79,6 +79,8 @@ class TPadPainter : public TPadPainterBase { void DrawTTFglyphs(Int_t x, Int_t y, TTFhandle &ttf, ETextMode mode) override; + void DrawImage(TImage *img, Int_t x, Int_t y, Int_t flags = 0) override; + //jpg, png, bmp, gif output. void SaveImage(TVirtualPad *pad, const char *fileName, Int_t type) const override; diff --git a/graf2d/gpad/inc/TPadPainterPS.h b/graf2d/gpad/inc/TPadPainterPS.h index 64b7a33c6f1da..aaa2a83316f7e 100644 --- a/graf2d/gpad/inc/TPadPainterPS.h +++ b/graf2d/gpad/inc/TPadPainterPS.h @@ -85,6 +85,8 @@ class TPadPainterPS : public TPadPainterBase { void DrawTextUrl(Double_t x, Double_t y, const char *text, const char *url) override; + void DrawImage(TImage *img, Int_t x, Int_t y, Int_t flags = 0) override; + //jpg, png, bmp, gif output. void SaveImage(TVirtualPad *pad, const char *fileName, Int_t type) const override; diff --git a/graf2d/gpad/src/TPadPainter.cxx b/graf2d/gpad/src/TPadPainter.cxx index f2ebd73efceb0..4203882e00d8a 100644 --- a/graf2d/gpad/src/TPadPainter.cxx +++ b/graf2d/gpad/src/TPadPainter.cxx @@ -213,6 +213,70 @@ Bool_t TPadPainter::HasTTFonts() const return gVirtualX->HasTTFonts(); } +//////////////////////////////////////////////////////////////////////////////// +/// Draw image on the gVirtualX window + +void TPadPainter::DrawImage(TImage *img, Int_t x, Int_t y, Int_t flags) +{ + Int_t width = img->GetWidth(); + Int_t height = img->GetHeight(); + + static int x11 = -1; + if (x11 < 0) + x11 = gVirtualX->InheritsFrom("TGX11"); + + Window_t wid = (Window_t)gVirtualX->GetWindowID(fPad->GetPixmapID()); + + Pixmap_t mask = kNone; + + if (x11) { + UInt_t ww, hh; + auto maskbits = img->GetMaskBits(ww, hh); + if (maskbits.size()) + mask = gVirtualX->CreateBitmap(gVirtualX->GetDefaultRootWindow(), (const char*) maskbits.data(), ww, hh); + } + + GCValues_t gv; + static GContext_t gc = 0; + + gv.fMask = kGCClipMask | kGCClipXOrigin | kGCClipYOrigin; + gv.fClipMask = mask; + gv.fClipXOrigin = x; + gv.fClipYOrigin = y; + + if (!gc) { + gc = gVirtualX->CreateGC(gVirtualX->GetDefaultRootWindow(), &gv); + } else { + gVirtualX->ChangeGC(gc, &gv); + } + + if (x11) { + img->DrawX11(wid, &gc, x, y); + } else { + auto bits = img->GetArgbArray(); + Pixmap_t pic = (Pixmap_t) 0; + if (bits) + pic = gVirtualX->CreatePixmapFromData((unsigned char *)bits, width, height); + if (pic) { + if (flags) { + SETBIT(width,31); + SETBIT(height,31); + } + gVirtualX->CopyArea(pic, wid, gc, 0, 0, width, height, x, y); + gVirtualX->DeletePixmap(pic); + } + } + + // free mask pixmap + if (mask != kNone) + gVirtualX->DeletePixmap(mask); + + gv.fMask = kGCClipMask; + gv.fClipMask = kNone; + if (gc) + gVirtualX->ChangeGC(gc, &gv); +} + //////////////////////////////////////////////////////////////////////////////// ///Noop, for non-gl pad TASImage calls gVirtualX->CopyArea. diff --git a/graf2d/gpad/src/TPadPainterPS.cxx b/graf2d/gpad/src/TPadPainterPS.cxx index ce9165e07b5ff..c36328a912dc7 100644 --- a/graf2d/gpad/src/TPadPainterPS.cxx +++ b/graf2d/gpad/src/TPadPainterPS.cxx @@ -421,6 +421,19 @@ void TPadPainterPS::DrawTextNDC(Double_t u, Double_t v, const char *text, ETextM } +//////////////////////////////////////////////////////////////////////////////// +/// Draw image on the PS output + +void TPadPainterPS::DrawImage(TImage *img, Int_t x, Int_t y, Int_t flags) +{ + // transfer to absolute pixel coordiantes for PS + x += fPad->UtoAbsPixel(0); + y += fPad->VtoAbsPixel(1); + + fPS->DrawImage(img, x, y, flags); +} + + //////////////////////////////////////////////////////////////////////////////// /// Save the image displayed in the canvas pointed by "pad" into a binary file. diff --git a/graf2d/graf/inc/TImage.h b/graf2d/graf/inc/TImage.h index 6d5fd14b30a47..e7e6a1bb7a3e0 100644 --- a/graf2d/graf/inc/TImage.h +++ b/graf2d/graf/inc/TImage.h @@ -15,6 +15,7 @@ #include "TNamed.h" +#include #include "TAttImage.h" #include "GuiTypes.h" #include "TVectorDfwd.h" @@ -244,6 +245,10 @@ friend TImage operator/(const TImage &i1, const TImage &s2); virtual void PaintImage(Drawable_t /*wid*/, Int_t /*x*/, Int_t /*y*/, Int_t /*xsrc*/ = 0, Int_t /*ysrc*/ = 0, UInt_t /*wsrc*/ = 0, UInt_t /*hsrc*/ = 0, Option_t * /*opt*/ = "") { } virtual void FromWindow(Drawable_t /*wid*/, Int_t /*x*/ = 0, Int_t /*y*/ = 0, UInt_t /*w*/ = 0, UInt_t /*h*/ = 0) {} virtual void FromGLBuffer(UChar_t* /*buf*/, UInt_t /*w*/, UInt_t /*h*/) {} + + virtual std::vector GetMaskBits(UInt_t &w, UInt_t &h) { w = h = 0; return {}; } + virtual void DrawX11(Drawable_t /* wid */, void * /* gc */, Int_t /* x */, Int_t /* y */) {} + static EImageFileTypes GetImageFileTypeFromFilename(const char* opt); static TImage *Create(); diff --git a/graf2d/postscript/inc/TImageDump.h b/graf2d/postscript/inc/TImageDump.h index 49df2d8f0561e..9ae6f6ac7b241 100644 --- a/graf2d/postscript/inc/TImageDump.h +++ b/graf2d/postscript/inc/TImageDump.h @@ -50,6 +50,8 @@ class TImageDump : public TVirtualPS { void CellArrayBegin(Int_t W, Int_t H, Double_t x1, Double_t x2, Double_t y1, Double_t y2) override; void CellArrayFill(Int_t r, Int_t g, Int_t b) override; void CellArrayEnd() override; + void DrawImage(TImage *img, Int_t x, Int_t y , Int_t flags = 0) override; + void Close(Option_t *opt = "") override; void DrawBox(Double_t x1, Double_t y1,Double_t x2, Double_t y2) override; void DrawFrame(Double_t xl, Double_t yl, Double_t xt, Double_t yt, diff --git a/graf2d/postscript/inc/TPDF.h b/graf2d/postscript/inc/TPDF.h index 81a68b2d82edd..c00145a560982 100644 --- a/graf2d/postscript/inc/TPDF.h +++ b/graf2d/postscript/inc/TPDF.h @@ -103,6 +103,9 @@ class TPDF : public TVirtualPS { void CellArrayBegin(Int_t W, Int_t H, Double_t x1, Double_t x2, Double_t y1, Double_t y2) override; void CellArrayFill(Int_t r, Int_t g, Int_t b) override; void CellArrayEnd() override; + + void DrawImage(TImage *img, Int_t x, Int_t y, Int_t flags = 0) override; + void Close(Option_t *opt = "") override; Double_t CMtoPDF(Double_t u) { return Int_t(0.5 + 72 * u / 2.54); } void ComputeRect(const char* chars, Double_t fontsize, Double_t a, Double_t b, Double_t c, Double_t d, Double_t e, Double_t f); diff --git a/graf2d/postscript/inc/TPostScript.h b/graf2d/postscript/inc/TPostScript.h index d84e4d6d1bb9a..d36860dbf9fd0 100644 --- a/graf2d/postscript/inc/TPostScript.h +++ b/graf2d/postscript/inc/TPostScript.h @@ -98,6 +98,7 @@ class TPostScript : public TVirtualPS { Double_t y1, Double_t y2) override; void CellArrayFill(Int_t r, Int_t g, Int_t b) override; void CellArrayEnd() override; + void DrawImage(TImage *img, Int_t x, Int_t y, Int_t flags = 0) override; void Close(Option_t *opt="") override; Int_t CMtoPS(Double_t u) {return Int_t(0.5 + 72*u/2.54);} void DrawBox(Double_t x1, Double_t y1, Double_t x2, Double_t y2) override; diff --git a/graf2d/postscript/inc/TSVG.h b/graf2d/postscript/inc/TSVG.h index cee6b15b1f265..5b6a2999f2143 100644 --- a/graf2d/postscript/inc/TSVG.h +++ b/graf2d/postscript/inc/TSVG.h @@ -46,6 +46,9 @@ class TSVG : public TVirtualPS { void CellArrayFill(Int_t r, Int_t g, Int_t b) override; void CellArrayPng(char *buffer, int size) override; void CellArrayEnd() override; + + void DrawImage(TImage *img, Int_t x, Int_t y , Int_t flags = 0) override; + void Close(Option_t *opt="") override; Double_t CMtoSVG(Double_t u) { return 0.5 + 72*u/2.54; } void DrawBox(Double_t x1, Double_t y1,Double_t x2, Double_t y2) override; diff --git a/graf2d/postscript/src/TImageDump.cxx b/graf2d/postscript/src/TImageDump.cxx index a3ffca6fe8a24..2c3e4011713db 100644 --- a/graf2d/postscript/src/TImageDump.cxx +++ b/graf2d/postscript/src/TImageDump.cxx @@ -624,6 +624,16 @@ void TImageDump::CellArrayEnd() fCellArrayY2 = 0; } +//////////////////////////////////////////////////////////////////////////////// +/// Draw image + +void TImageDump::DrawImage(TImage *img, Int_t x, Int_t y, Int_t) +{ + if (fImage) + fImage->Merge(img, "alphablend", x, y); +} + + //////////////////////////////////////////////////////////////////////////////// /// Set color with its R G B components /// diff --git a/graf2d/postscript/src/TPDF.cxx b/graf2d/postscript/src/TPDF.cxx index e1cd9b10d10ac..3d67b69be8d97 100644 --- a/graf2d/postscript/src/TPDF.cxx +++ b/graf2d/postscript/src/TPDF.cxx @@ -64,6 +64,7 @@ the table of contents. #include "TVirtualPad.h" #include "TPoint.h" #include "TPoints.h" +#include "TImage.h" #include "TPDF.h" #include "TStyle.h" #include "TMath.h" @@ -259,6 +260,51 @@ void TPDF::CellArrayEnd() fCellArrayRGB.shrink_to_fit(); } +//////////////////////////////////////////////////////////////////////////////// +/// Draw image in the PDF + +void TPDF::DrawImage(TImage *img, Int_t x, Int_t y, Int_t) +{ + Int_t width = img->GetWidth(); + Int_t height = img->GetHeight(); + + auto x1 = gPad->AbsPixeltoX(x); + auto x2 = gPad->AbsPixeltoX(x + width); + auto y1 = gPad->AbsPixeltoY(y); + auto y2 = gPad->AbsPixeltoY(y + height); + + fCellArrayW = width; + fCellArrayH = height; + + Double_t xLeft = XtoPDF(x1); + Double_t xRight = XtoPDF(x2); + Double_t yTop = YtoPDF(y1); + Double_t yBot = YtoPDF(y2); + + fCellArrayXpdf = xLeft; + fCellArrayYpdfBot = yBot; + fCellArrayWpdf = xRight - xLeft; + fCellArrayHpdf = yTop - yBot; + + auto argb = img->GetArgbArray(); + if (!argb) { + Error("DrawImage", "Fail to access to ARGB values"); + return; + } + + fCellArrayRGB.resize(3 * width * height); + for (Int_t i = 0; i < width * height; ++i) { + UInt_t p = argb[i]; + fCellArrayRGB[i*3] = static_cast((p >> 16) & 0xFF); + fCellArrayRGB[i*3 + 1] = static_cast((p >> 8) & 0xFF); + fCellArrayRGB[i*3 + 2] = static_cast(p & 0xFF); + } + + // use old method, move here once old API is deprecated + CellArrayEnd(); +} + + //////////////////////////////////////////////////////////////////////////////// /// Close a PDF file diff --git a/graf2d/postscript/src/TPostScript.cxx b/graf2d/postscript/src/TPostScript.cxx index d65ca29240fe3..08ea96937c50b 100644 --- a/graf2d/postscript/src/TPostScript.cxx +++ b/graf2d/postscript/src/TPostScript.cxx @@ -244,6 +244,7 @@ To change the color model use `gStyle->SetColorModelPS(c)`. #include "TVirtualPad.h" #include "TPoints.h" #include "TPoint.h" +#include "TImage.h" #include "TPostScript.h" #include "TStyle.h" #include "TMath.h" @@ -621,6 +622,102 @@ void TPostScript::CellArrayEnd() PrintStr(" def DrawCT "); } +//////////////////////////////////////////////////////////////////////////////// +/// Draw image in postscript + +void TPostScript::DrawImage(TImage *img, Int_t x, Int_t y, Int_t) +{ + auto argb = img->GetArgbArray(); + if (!argb) { + Error("DrawImage", "Fail to access ARGB values"); + return; + } + + Int_t width = img->GetWidth(); + Int_t height = img->GetHeight(); + + auto x1 = gPad->AbsPixeltoX(x); + auto x2 = gPad->AbsPixeltoX(x + width); + auto y1 = gPad->AbsPixeltoY(y); + auto y2 = gPad->AbsPixeltoY(y - height); + + Int_t ix1 = XtoPS(x1); + Int_t iy1 = YtoPS(y1); + Int_t ix2 = XtoPS(x2); + Int_t iy2 = YtoPS(y2); + + Float_t wt = (0. + ix2 - ix1) / width; + Float_t ht = (0. + iy2 - iy1) / height; + + fLastCellRed = 300; + fLastCellGreen = 300; + fLastCellBlue = 300; + fNBSameColorCell = 0; + + fNbinCT = 0; + fNbCellW = width; + fNbCellLine = 0; + fMaxLines = 40000/(3*fNbCellW); + + // Define some parameters + PrintStr("@/WT"); WriteReal(wt) ; PrintStr(" def"); // Cells width + PrintStr(" /HT"); WriteReal(ht) ; PrintStr(" def"); // Cells height + PrintStr(" /XS"); WriteInteger(ix1) ; PrintStr(" def"); // X start + PrintStr(" /YY"); WriteInteger(iy1) ; PrintStr(" def"); // Y start + PrintStr(" /NX"); WriteInteger(width) ; PrintStr(" def"); // Number of columns + PrintStr(" /NY"); WriteInteger(fMaxLines); PrintStr(" def"); // Number of lines + + // This PS procedure draws one cell. + PrintStr(" /DrawCell "); + PrintStr( "{WT HT XX YY bf"); + PrintStr( " /NBBD NBBD 1 add def"); + PrintStr( " NBBD NBB eq {exit} if"); + PrintStr( " /XX WT XX add def"); + PrintStr( " IX NX eq "); + PrintStr( "{/YY YY HT sub def"); + PrintStr( " /XX XS def"); + PrintStr( " /IX 0 def} if"); + PrintStr( " /IX IX 1 add def} def"); + + // This PS procedure draws fMaxLines line. It takes care of duplicated + // colors. Values "n" greater than 300 mean than the previous color + // should be duplicated n-300 times. + PrintStr(" /DrawCT "); + PrintStr( "{/NBB NX NY mul def"); + PrintStr( " /XX XS def"); + PrintStr( " /IX 1 def"); + PrintStr( " /NBBD 0 def"); + PrintStr( " /RC 0 def /GC 1 def /BC 2 def"); + PrintStr( " 1 1 NBB "); + PrintStr( "{/NB CT RC get def"); + PrintStr( " NB 301 ge "); + PrintStr( "{/NBL NB 300 sub def"); + PrintStr( " 1 1 NBL "); + PrintStr( "{DrawCell}"); + PrintStr( " for"); + PrintStr( " /RC RC 1 add def"); + PrintStr( " /GC RC 1 add def"); + PrintStr( " /BC RC 2 add def}"); + PrintStr( "{CT RC get 255 div CT GC get 255 div CT BC get 255 div setrgbcolor"); + PrintStr( " DrawCell"); + PrintStr( " /RC RC 3 add def"); + PrintStr( " /GC GC 3 add def"); + PrintStr( " /BC BC 3 add def} ifelse NBBD NBB eq {exit} if} for"); + PrintStr( " /YY YY HT sub def clear} def"); + + PrintStr(" /CT ["); + + // use old API, move here once old is deprecated + for (Int_t i = 0; i < width * height; ++i) { + UInt_t p = argb[i]; + auto r = static_cast((p >> 16) & 0xFF); + auto g = static_cast((p >> 8) & 0xFF); + auto b = static_cast(p & 0xFF); + CellArrayFill(r, g, b); + } + + CellArrayEnd(); +} //////////////////////////////////////////////////////////////////////////////// /// Draw a Box diff --git a/graf2d/postscript/src/TSVG.cxx b/graf2d/postscript/src/TSVG.cxx index 01759e4cda013..435371d594955 100644 --- a/graf2d/postscript/src/TSVG.cxx +++ b/graf2d/postscript/src/TSVG.cxx @@ -26,6 +26,7 @@ #include "TVirtualPad.h" #include "TPoint.h" #include "TPoints.h" +#include "TImage.h" #include "TSVG.h" #include "TStyle.h" #include "TMath.h" @@ -752,6 +753,53 @@ void TSVG::CellArrayEnd() PrintStr("@"); } +//////////////////////////////////////////////////////////////////////////////// +/// Draw image in the SVG +/// Replaces CellArray methods calling + +void TSVG::DrawImage(TImage *img, Int_t x, Int_t y, Int_t) +{ + Int_t width = img->GetWidth(); + Int_t height = img->GetHeight(); + + auto x1 = gPad->AbsPixeltoX(x); + auto x2 = gPad->AbsPixeltoX(x + width); + auto y1 = gPad->AbsPixeltoY(y); + auto y2 = gPad->AbsPixeltoY(y + height); + + Double_t svgx1 = XtoSVG(x1); + Double_t svgx2 = XtoSVG(x2); + Double_t svgy1 = YtoSVG(y1); + Double_t svgy2 = YtoSVG(y2); + + char *buffer = nullptr; + int size = 0; + + img->GetImageBuffer(&buffer, &size, TImage::kPng); + if (!buffer) { + Error("DrawImage", "Fail to get PNG format of the image"); + return; + } + + TString base64 = TBase64::Encode(reinterpret_cast(buffer), size); + free(buffer); + + PrintStr("@@"); + PrintStr(TString::Format("@"); + PrintStr("@"); +} + //////////////////////////////////////////////////////////////////////////////// /// Initialize the SVG file. The main task of the function is to output the /// SVG header file which consist in ``, `<desc>` and `<defs>`. The diff --git a/graf3d/gl/inc/TGLPadPainter.h b/graf3d/gl/inc/TGLPadPainter.h index a0baf240ee660..1087afe223947 100644 --- a/graf3d/gl/inc/TGLPadPainter.h +++ b/graf3d/gl/inc/TGLPadPainter.h @@ -116,6 +116,8 @@ class TGLPadPainter : public TPadPainterBase { void DrawTextNDC(Double_t x, Double_t y, const char *text, ETextMode mode) override; void DrawTextNDC(Double_t, Double_t, const wchar_t *, ETextMode) override; + void DrawImage(TImage *img, Int_t x, Int_t y, Int_t flags = 0) override; + //jpg, png, gif and bmp output. void SaveImage(TVirtualPad *pad, const char *fileName, Int_t type) const override; diff --git a/graf3d/gl/src/TGLPadPainter.cxx b/graf3d/gl/src/TGLPadPainter.cxx index 85ba074511f23..2b3efe96e3aba 100644 --- a/graf3d/gl/src/TGLPadPainter.cxx +++ b/graf3d/gl/src/TGLPadPainter.cxx @@ -22,6 +22,7 @@ #include "TROOT.h" #include "TPad.h" #include "TCanvas.h" +#include "TImage.h" #include "TColorGradient.h" #include "TGLPadPainter.h" @@ -858,6 +859,19 @@ void TGLPadPainter::RestoreViewport() glViewport(fVp[0], fVp[1], fVp[2], fVp[3]); } + +//////////////////////////////////////////////////////////////////////////////// +/// Draw image on the GL window + +void TGLPadPainter::DrawImage(TImage *img, Int_t x, Int_t y, Int_t flags) +{ + Int_t width = img->GetWidth(); + Int_t height = img->GetHeight(); + auto bits = img->GetArgbArray(); + if (bits) + DrawPixels((unsigned char *)bits, width, height, x, y, flags); +} + //////////////////////////////////////////////////////////////////////////////// /// Using TImage save frame-buffer contents as a picture. diff --git a/hist/histpainter/src/THistPainter.cxx b/hist/histpainter/src/THistPainter.cxx index a3758eda1384e..b76ffd7884c7f 100644 --- a/hist/histpainter/src/THistPainter.cxx +++ b/hist/histpainter/src/THistPainter.cxx @@ -58,6 +58,7 @@ #include "TPaletteAxis.h" #include "TCrown.h" #include "TArrow.h" +#include "TVirtualPadPainter.h" #include "TVirtualPadEditor.h" #include "TVirtualX.h" #include "TEnv.h" @@ -5772,11 +5773,14 @@ void THistPainter::PaintColorLevelsFast(Option_t*) pImage->SetImage(buffer.data(), nXPixels, nYPixels, pPalette); delete pPalette; - Window_t wid = static_cast<Window_t>(gVirtualX->GetWindowID(gPad->GetPixmapID())); - pImage->PaintImage(wid, px0, py1, 0, 0, nXPixels, nYPixels); + auto pp = gPad->GetPainter(); + if (pp) + pp->DrawImage(pImage, px0, py1); + delete pImage; - if (Hoption.Zscale) PaintPalette(); + if (Hoption.Zscale) + PaintPalette(); // Reset the maximum and minimum values to their original values // when this function was called. If we don't do this, an initial diff --git a/test/stressGraphics.ref b/test/stressGraphics.ref index a53d01ec1b7b2..501c31bb4d088 100644 --- a/test/stressGraphics.ref +++ b/test/stressGraphics.ref @@ -73,7 +73,7 @@ tgraph2d3 15025 3000 29874 500 42525 11300 33336 3900 14787 4000 ntuple1 293000 5000 383731 7000 55666 30400 46534 6500 298000 7000 quarks 4846 150 14289 200 33239 600 25303 500 4877 150 - timage 1435879 150000 456978 10000 26170 100 365000 15000 1085792 250000 + timage 1435879 150000 456978 10000 26470 100 365000 15000 1085792 250000 zoomtf1 5884 500 16856 200 40706 14000 30026 4000 5884 500 zoomfit 5723 700 15959 200 32236 11000 16916 3200 5670 800 parallelcoord 1027338 160000 532630 50000 29519 3000 71404 7000 1027338 160000 diff --git a/test/stressGraphics_zlibng.ref b/test/stressGraphics_zlibng.ref index 5b43fcffe9490..740a55244e261 100644 --- a/test/stressGraphics_zlibng.ref +++ b/test/stressGraphics_zlibng.ref @@ -73,7 +73,7 @@ tgraph2d3 16675 3000 30066 500 40536 11300 33519 3900 16452 4000 ntuple1 295000 5000 382457 7000 54446 30400 45002 6500 300000 7000 quarks 4759 180 14288 100 32913 600 25421 1000 4790 180 - timage 1442666 150000 463176 10000 26100 100 374206 15000 1102279 250000 + timage 1442666 150000 463176 10000 26400 100 374206 15000 1102279 250000 zoomtf1 6130 500 16747 300 49750 14000 29288 4000 5429 500 zoomfit 6200 700 15885 200 31219 11000 18280 3200 6113 800 parallelcoord 1021126 160000 531255 50000 29893 3000 73438 7000 1027338 160000 diff --git a/test/svg_ref/timage.svg b/test/svg_ref/timage.svg index f28853fec03c6..a34cbeceaa356 100644 --- a/test/svg_ref/timage.svg +++ b/test/svg_ref/timage.svg @@ -11,7 +11,7 @@ timage.svg <path d="M29,247l 2.0, -2.0h 166v -216l 2.0, -2.0v 220h -170z" fill="#a9a9a9"/> <rect x="46" y="49" width="136" height="176" fill="#f2f2f2"/> <rect x="46" y="49" width="136" height="176" fill="none" stroke="#f2f2f2"/> -<g transform="translate(46 49) scale(0.712 0.712)"> +<g transform="translate(45 49) scale(0.712 0.712)"> <image width="191" height="247" href="data:image/png;base64,"></image> </g> <rect x="312" y="82" width="227" height="165" fill="#f2f2f2"/> @@ -19,7 +19,7 @@ timage.svg <path d="M312,247l 2.0, -2.0h 223v -161l 2.0, -2.0v 165h -227z" fill="#a9a9a9"/> <rect x="335" y="99" width="181" height="132" fill="#f2f2f2"/> <rect x="335" y="99" width="181" height="132" fill="none" stroke="#f2f2f2"/> -<g transform="translate(335 99) scale(0.714 0.709)"> +<g transform="translate(335 99) scale(0.712 0.712)"> <image width="254" height="186" href="data:image/png;base64,"></image> </g> <rect x="312" y="302" width="170" height="220" fill="#f2f2f2"/> @@ -35,7 +35,7 @@ timage.svg <path d="M29,522l 2.0, -2.0h 223v -161l 2.0, -2.0v 165h -227z" fill="#a9a9a9"/> <rect x="52" y="374" width="181" height="132" fill="#f2f2f2"/> <rect x="52" y="374" width="181" height="132" fill="none" stroke="#f2f2f2"/> -<g transform="translate(52 374) scale(0.714 0.709)"> +<g transform="translate(51 374) scale(0.712 0.712)"> <image width="254" height="186" href="data:image/png;base64,"></image> </g> </svg>