From 95e942681169504fdf5bb1611c9fbe7ef8e5d8a1 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Mon, 20 Apr 2026 14:21:18 +0200 Subject: [PATCH 01/15] [tgx11] move ImgPickPalette into WriteGIF It is the only place where it was used. Reduce memory allocation, simplify creation of color palette. Also cleanup created image at the end. Moving forward to none-static version of gif handling --- graf2d/x11/inc/TGX11.h | 1 - graf2d/x11/src/TGX11.cxx | 137 +++++++++++++++++---------------------- 2 files changed, 61 insertions(+), 77 deletions(-) diff --git a/graf2d/x11/inc/TGX11.h b/graf2d/x11/inc/TGX11.h index 4646efa97d19e..2825eec94a4a5 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 ImgPickPalette(RXImage *image, Int_t &ncol, Int_t *&R, Int_t *&G, Int_t *&B); //---- Private methods used for GUI ---- void MapGCValues(GCValues_t &gval, ULong_t &xmask, RXGCValues &xgval, Bool_t tox = kTRUE); diff --git a/graf2d/x11/src/TGX11.cxx b/graf2d/x11/src/TGX11.cxx index 3efc79be47e34..fe335d60b985e 100644 --- a/graf2d/x11/src/TGX11.cxx +++ b/graf2d/x11/src/TGX11.cxx @@ -2834,126 +2834,111 @@ static void PutByte(Byte_t b) if (ferror(gOut) == 0) fputc(b, gOut); } + //////////////////////////////////////////////////////////////////////////////// -/// Returns in R G B the ncol colors of the palette used by the image. -/// The image pixels are changed to index values in these R G B arrays. -/// This produces a colormap with only the used colors (so even on displays -/// with more than 8 planes we will be able to create GIF's when the image -/// contains no more than 256 different colors). If it does contain more -/// colors we will have to use GIFquantize to reduce the number of colors. -/// The R G B arrays must be deleted by the caller. +/// Writes the current window into GIF file. Returns 1 in case of success, +/// 0 otherwise. -void TGX11::ImgPickPalette(RXImage *image, Int_t &ncol, Int_t *&R, Int_t *&G, Int_t *&B) +Int_t TGX11::WriteGIF(char *name) { + Byte_t scline[2000], r[256], b[256], g[256]; + + if (gXimage) { + XDestroyImage(gXimage); + gXimage = nullptr; + } + + gXimage = XGetImage((Display*)fDisplay, gCws->fDrawing, 0, 0, + gCws->fWidth, gCws->fHeight, + AllPlanes, ZPixmap); + + if (!gXimage) { + Error("WriteGIF", "Cannot create image for writing GIF. Try in batch mode."); + return 0; + } + + /// Collect R G B colors of the palette used by the image. + /// The image pixels are changed to index values in these R G B arrays. + /// This produces a colormap with only the used colors (so even on displays + /// with more than 8 planes we will be able to create GIF's when the image + /// contains no more than 256 different colors). If it does contain more + /// colors we will have to use GIFquantize to reduce the number of colors. + std::vector orgcolors; // collect different image colors 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); + ULong_t pixel = XGetPixel(gXimage, x, y); if (std::find(orgcolors.begin(), orgcolors.end(), pixel) == orgcolors.end()) orgcolors.emplace_back(pixel); } } + if (orgcolors.size() > 256) { + Error("WriteGIF", "Cannot create GIF of image containing more than 256 colors. Try in batch mode."); + XDestroyImage(gXimage); + gXimage = nullptr; + return 0; + } + // get RGB values belonging to pixels std::vector xcol(orgcolors.size()); - for (size_t i = 0; i < orgcolors.size(); 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].flags = DoRed | DoGreen | DoBlue; } - 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[orgcolors.size()]; - G = new Int_t[orgcolors.size()]; - B = new Int_t[orgcolors.size()]; + QueryColors(fColormap, xcol.data(), orgcolors.size()); - for (size_t i = 0; i < orgcolors.size(); i++) { - R[i] = xcol[i].red; - G[i] = xcol[i].green; - B[i] = xcol[i].blue; + UShort_t maxcol = 0; + for (std::size_t i = 0; i < orgcolors.size(); i++) { + maxcol = TMath::Max(maxcol, xcol[i].red); + maxcol = TMath::Max(maxcol, xcol[i].green); + maxcol = TMath::Max(maxcol, xcol[i].blue); } - ncol = (Int_t) orgcolors.size(); + if (maxcol == 0) + maxcol = 255; // update image with indices (pixels) into the new RGB colormap 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); + ULong_t pixel = XGetPixel(gXimage, x, y); auto iter = std::find(orgcolors.begin(), orgcolors.end(), pixel); if (iter != orgcolors.end()) { auto idx = iter - orgcolors.begin(); - XPutPixel(image, x, y, idx); + XPutPixel(gXimage, x, y, idx); } } } -} - -//////////////////////////////////////////////////////////////////////////////// -/// Writes the current window into GIF file. Returns 1 in case of success, -/// 0 otherwise. - -Int_t TGX11::WriteGIF(char *name) -{ - Byte_t scline[2000], r[256], b[256], g[256]; - Int_t *red, *green, *blue; - Int_t ncol, maxcol, i; - - if (gXimage) { - XDestroyImage(gXimage); - gXimage = nullptr; - } - gXimage = XGetImage((Display*)fDisplay, gCws->fDrawing, 0, 0, - gCws->fWidth, gCws->fHeight, - AllPlanes, ZPixmap); - - ImgPickPalette((RXImage*)gXimage, ncol, red, green, blue); - - if (ncol > 256) { - //GIFquantize(...); - Error("WriteGIF", "Cannot create GIF of image containing more than 256 colors. Try in batch mode."); - delete [] red; - delete [] green; - delete [] blue; - return 0; - } - - maxcol = 0; - for (i = 0; i < ncol; i++) { - if (maxcol < red[i] ) maxcol = red[i]; - if (maxcol < green[i] ) maxcol = green[i]; - if (maxcol < blue[i] ) maxcol = blue[i]; - r[i] = 0; - g[i] = 0; - b[i] = 0; - } - if (maxcol != 0) { - for (i = 0; i < ncol; i++) { - r[i] = red[i] * 255/maxcol; - g[i] = green[i] * 255/maxcol; - b[i] = blue[i] * 255/maxcol; - } + for (std::size_t i = 0; i < orgcolors.size(); i++) { + r[i] = xcol[i].red * 255 / maxcol; + g[i] = xcol[i].green * 255 / maxcol; + b[i] = xcol[i].blue * 255 / maxcol; } gOut = fopen(name, "w+"); + Int_t ret = 0; + if (gOut) { GIFencode(gCws->fWidth, gCws->fHeight, - ncol, r, g, b, scline, ::GetPixel, PutByte); + orgcolors.size(), r, g, b, scline, ::GetPixel, PutByte); fclose(gOut); - i = 1; + ret = 1; } else { Error("WriteGIF","cannot write file: %s",name); - i = 0; + ret = 0; } - delete [] red; - delete [] green; - delete [] blue; - return i; + + // cleanup image at the end + XDestroyImage(gXimage); + gXimage = nullptr; + + return ret; } //////////////////////////////////////////////////////////////////////////////// From 06b8c17e065f0360970a0ba8069deb52e82dd6cb Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Mon, 20 Apr 2026 14:43:42 +0200 Subject: [PATCH 02/15] [tgwin32] move ImgPickPalette into WriteGIF It is the only place where it was used. Reduce memory allocation, simplify creation of color palette. Also cleanup created image at the end. Make it similar to TGX11 --- graf2d/win32gdk/inc/TGWin32.h | 1 - graf2d/win32gdk/src/TGWin32.cxx | 123 +++++++++++++------------------- 2 files changed, 48 insertions(+), 76 deletions(-) diff --git a/graf2d/win32gdk/inc/TGWin32.h b/graf2d/win32gdk/inc/TGWin32.h index 236192defbcab..1d00421a4075b 100644 --- a/graf2d/win32gdk/inc/TGWin32.h +++ b/graf2d/win32gdk/inc/TGWin32.h @@ -86,7 +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 ImgPickPalette(GdkImage *image, Int_t &ncol, Int_t *&R, Int_t *&G, Int_t *&B); //---- Private methods used for GUI ---- void MapGCValues(GCValues_t &gval, ULong_t &xmask, GdkGCValues &xgval, Bool_t tox = kTRUE); diff --git a/graf2d/win32gdk/src/TGWin32.cxx b/graf2d/win32gdk/src/TGWin32.cxx index da3810ee89554..13185c773151d 100644 --- a/graf2d/win32gdk/src/TGWin32.cxx +++ b/graf2d/win32gdk/src/TGWin32.cxx @@ -4354,28 +4354,46 @@ static void PutByte(Byte_t b) } //////////////////////////////////////////////////////////////////////////////// -/// Returns in R G B the ncol colors of the palette used by the image. -/// The image pixels are changed to index values in these R G B arrays. -/// This produces a colormap with only the used colors (so even on displays -/// with more than 8 planes we will be able to create GIF's when the image -/// contains no more than 256 different colors). If it does contain more -/// colors we will have to use GIFquantize to reduce the number of colors. -/// The R G B arrays must be deleted by the caller. +/// Writes the current window into GIF file. -void TGWin32::ImgPickPalette(GdkImage * image, Int_t & ncol, Int_t * &R, - Int_t * &G, Int_t * &B) +Int_t TGWin32::WriteGIF(char *name) { + Byte_t scline[2000], r[256], b[256], g[256]; + + if (gGifImage) { + gdk_image_unref(gGifImage); + } + + gGifImage = gdk_image_get((GdkDrawable*)gCws->drawing, 0, 0, + gCws->width, gCws->height); + if (!gGifImage) + return 0; + + /// Collect R G B of colors of the palette used by the image. + /// The image pixels are changed to index values in these R G B arrays. + /// This produces a colormap with only the used colors (so even on displays + /// with more than 8 planes we will be able to create GIF's when the image + /// contains no more than 256 different colors). If it does contain more + /// colors we will have to use GIFquantize to reduce the number of colors. + std::vector orgcolors; // collect different image colors - 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); + for (UInt_t x = 0; x < gCws->width; x++) { + for (UInt_t y = 0; y < gCws->height; y++) { + ULong_t pixel = GetPixelImage((Drawable_t)gGifImage, x, y); if (std::find(orgcolors.begin(), orgcolors.end(), pixel) == orgcolors.end()) orgcolors.emplace_back(pixel); } } + if (orgcolors.size() > 256) { + Error("WriteGIF", "can not create GIF of image containing more than 256 colors"); + gdk_image_unref(gGifImage); + gGifImage = nullptr; + return 0; + } + // get RGB values belonging to pixels std::vector xcol(orgcolors.size()); @@ -4390,93 +4408,48 @@ void TGWin32::ImgPickPalette(GdkImage * image, Int_t & ncol, Int_t * &R, 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[orgcolors.size()]; - G = new Int_t[orgcolors.size()]; - B = new Int_t[orgcolors.size()]; - + UChar_t maxcol = 0; 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; + maxcol = TMath::Max(maxcol, xcol[i].red); + maxcol = TMath::Max(maxcol, xcol[i].green); + maxcol = TMath::Max(maxcol, xcol[i].blue); } - ncol = (Int_t) orgcolors.size(); + if (maxcol == 0) + maxcol = 255; // update image with indices (pixels) into the new RGB colormap 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); + ULong_t pixel = GetPixelImage((Drawable_t)gGifImage, x, y); 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); + PutPixel((Drawable_t)gGifImage, x, y, idx); } } } -} -//////////////////////////////////////////////////////////////////////////////// -/// Writes the current window into GIF file. - -Int_t TGWin32::WriteGIF(char *name) -{ - Byte_t scline[2000], r[256], b[256], g[256]; - Int_t *R, *G, *B; - Int_t ncol, maxcol, i; - - if (gGifImage) { - gdk_image_unref((GdkImage *)gGifImage); - } - - gGifImage = gdk_image_get((GdkDrawable*)gCws->drawing, 0, 0, - gCws->width, gCws->height); - - ImgPickPalette(gGifImage, ncol, R, G, B); - - if (ncol > 256) { - //GIFquantize(...); - Error("WriteGIF", - "can not create GIF of image containing more than 256 colors"); - delete[]R; - delete[]G; - delete[]B; - return 0; - } - - maxcol = 0; - for (i = 0; i < ncol; i++) { - if (maxcol < R[i]) maxcol = R[i]; - if (maxcol < G[i]) maxcol = G[i]; - if (maxcol < B[i]) maxcol = B[i]; - r[i] = 0; - g[i] = 0; - b[i] = 0; - } - if (maxcol != 0) { - for (i = 0; i < ncol; i++) { - r[i] = R[i] * 255 / maxcol; - g[i] = G[i] * 255 / maxcol; - b[i] = B[i] * 255 / maxcol; - } + for (std::size_t i = 0; i < orgcolors.size(); i++) { + r[i] = xcol[i].red * 255 / maxcol; + g[i] = xcol[i].green * 255 / maxcol; + b[i] = xcol[i].blue * 255 / maxcol; } + Int_t ret = 0; gGifFile = fopen(name, "wb"); if (gGifFile) { GIFencode(gCws->width, gCws->height, - ncol, r, g, b, scline, ::GetPixel, PutByte); + orgcolors.size(), r, g, b, scline, ::GetPixel, PutByte); fclose(gGifFile); - i = 1; + ret = 1; } else { Error("WriteGIF","cannot write file: %s",name); - i = 0; } - delete[]R; - delete[]G; - delete[]B; + gdk_image_unref(gGifImage); + gGifImage = nullptr; - return i; + return ret; } //////////////////////////////////////////////////////////////////////////////// From 930b02c1499d0c52b3c5d16b808fa17b6948768c Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Tue, 21 Apr 2026 07:23:03 +0200 Subject: [PATCH 03/15] [tgx11] convert gifencode.c into c++ Introduce TGifEncode class and convert all C functions to C++ methods, most of static variables to class members. --- graf2d/x11/CMakeLists.txt | 2 +- graf2d/x11/src/TGX11.cxx | 37 ++- graf2d/x11/src/{gifencode.c => gifencode.cxx} | 255 +++++++++--------- graf2d/x11/src/gifencode.h | 60 +++++ 4 files changed, 210 insertions(+), 144 deletions(-) rename graf2d/x11/src/{gifencode.c => gifencode.cxx} (77%) create mode 100644 graf2d/x11/src/gifencode.h diff --git a/graf2d/x11/CMakeLists.txt b/graf2d/x11/CMakeLists.txt index 68620e97cab03..98ca87c3aaa21 100644 --- a/graf2d/x11/CMakeLists.txt +++ b/graf2d/x11/CMakeLists.txt @@ -14,7 +14,7 @@ ROOT_STANDARD_LIBRARY_PACKAGE(GX11 TGX11.h SOURCES src/gifdecode.c - src/gifencode.c + src/gifencode.cxx src/gifquantize.c src/GX11Gui.cxx src/Rotated.cxx diff --git a/graf2d/x11/src/TGX11.cxx b/graf2d/x11/src/TGX11.cxx index fe335d60b985e..3e7a67469458e 100644 --- a/graf2d/x11/src/TGX11.cxx +++ b/graf2d/x11/src/TGX11.cxx @@ -62,6 +62,8 @@ by Olivier Couet (package X11INT). # include #endif +#include "gifencode.h" + extern float XRotVersion(char*, int); extern void XRotSetMagnification(float); extern void XRotSetBoundingBoxPad(int); @@ -2811,8 +2813,8 @@ static XImage *gXimage = nullptr; // image used in WriteGIF and GetPixel extern "C" { int GIFquantize(UInt_t width, UInt_t height, Int_t *ncol, Byte_t *red, Byte_t *green, Byte_t *blue, Byte_t *outputBuf, Byte_t *outputCmap); - long GIFencode(int Width, int Height, Int_t Ncol, Byte_t R[], Byte_t G[], Byte_t B[], Byte_t ScLine[], - void (*get_scline) (int, int, Byte_t *), void (*pb)(Byte_t)); + // long GIFencode(int Width, int Height, Int_t Ncol, Byte_t R[], Byte_t G[], Byte_t B[], Byte_t ScLine[], +// void (*get_scline) (int, int, Byte_t *), void (*pb)(Byte_t)); int GIFdecode(Byte_t *gifArr, Byte_t *pixArr, int *Width, int *Height, int *Ncols, Byte_t *R, Byte_t *G, Byte_t *B); int GIFinfo(Byte_t *gifArr, int *Width, int *Height, int *Ncols); } @@ -2834,6 +2836,19 @@ static void PutByte(Byte_t b) if (ferror(gOut) == 0) fputc(b, gOut); } +class TX11GifEncode : public TGifEncode { + private: + XImage *fXimage = nullptr; + protected: + void get_scline(int y, int width, unsigned char *buf) override + { + for (int i = 0; i < width; i++) + buf[i] = XGetPixel(fXimage, i, y); + } + public: + TX11GifEncode(XImage *image) : fXimage(image) {} +}; + //////////////////////////////////////////////////////////////////////////////// /// Writes the current window into GIF file. Returns 1 in case of success, @@ -2841,7 +2856,7 @@ static void PutByte(Byte_t b) Int_t TGX11::WriteGIF(char *name) { - Byte_t scline[2000], r[256], b[256], g[256]; + unsigned char r[256], b[256], g[256]; if (gXimage) { XDestroyImage(gXimage); @@ -2920,18 +2935,16 @@ Int_t TGX11::WriteGIF(char *name) b[i] = xcol[i].blue * 255 / maxcol; } - gOut = fopen(name, "w+"); - Int_t ret = 0; - if (gOut) { - GIFencode(gCws->fWidth, gCws->fHeight, - orgcolors.size(), r, g, b, scline, ::GetPixel, PutByte); - fclose(gOut); - ret = 1; + TX11GifEncode gif(gXimage); + if (gif.OpenFile(name)) { + auto len = gif.GIFencode(gCws->fWidth, gCws->fHeight, orgcolors.size(), r, g, b); + if (len > 0) + ret = 1; + gif.CloseFile(); } else { - Error("WriteGIF","cannot write file: %s",name); - ret = 0; + Error("WriteGIF","cannot write file: %s", name); } // cleanup image at the end diff --git a/graf2d/x11/src/gifencode.c b/graf2d/x11/src/gifencode.cxx similarity index 77% rename from graf2d/x11/src/gifencode.c rename to graf2d/x11/src/gifencode.cxx index 0e1d133224e22..973943097b7d6 100644 --- a/graf2d/x11/src/gifencode.c +++ b/graf2d/x11/src/gifencode.cxx @@ -1,43 +1,132 @@ /* @(#)root/x11:$Id$ */ -/* Author: E.Chernyaev 19/01/94*/ -#include -#include -#include +/* Author: E.Chernyaev 19/01/94 + * C++ interface: S.Linev 20/04/2026 */ + +#include "gifencode.h" + +#include -#ifdef __STDC__ -#define ARGS(alist) alist -#else -#define ARGS(alist) () -#endif #define BITS 12 /* largest code size */ #define THELIMIT 4096 /* NEVER generate this */ -#define HSIZE 5003 /* hash table size */ #define SHIFT 4 /* shift for hashing */ -#define put_byte(A) (*put_b)((byte)(A)); Nbyte++ -typedef unsigned char byte; +void TGifEncode::put_byte(unsigned char b) +{ + if (fOut && (ferror(fOut) == 0)) { + fputc(b, fOut); + fNbyte++; + } +} + +bool TGifEncode::OpenFile(const char *fname) +{ + fOut = fopen(fname, "w+"); + return fOut != nullptr; +} + +void TGifEncode::CloseFile() +{ + if (fOut) + fclose(fOut); + fOut = nullptr; +} + + +void TGifEncode::char_init() +{ + a_count = 0; + cur_accum = 0; + cur_bits = 0; +} + +void TGifEncode::char_out(unsigned char c) +{ + accum[a_count++] = c; + if (a_count >= 254) + char_flush(); +} + +void TGifEncode::char_flush() +{ + if (a_count == 0) return; + put_byte(a_count); + for (int i=0; i>8) & 0xFF); +} + +/*************************************************************** + * * + * Name: output Date: 02.10.92 * + * * + * Function: output GIF code * + * * + * Input: code - GIF code * + * * + ***************************************************************/ +void TGifEncode::output(int code) +{ + /* O U T P U T C O D E */ + + static unsigned long masks[] = { 0x0000, + 0x0001, 0x0003, 0x0007, 0x000F, + 0x001F, 0x003F, 0x007F, 0x00FF, + 0x01FF, 0x03FF, 0x07FF, 0x0FFF, + 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; + + cur_accum &= masks[cur_bits]; + if (cur_bits > 0) + cur_accum |= ((long)code << cur_bits); + else + cur_accum = code; + cur_bits += CurCodeSize; + while( cur_bits >= 8 ) { + char_out(cur_accum & 0xFF); + cur_accum >>= 8; + cur_bits -= 8; + } -static long HashTab [HSIZE]; /* hash table */ -static int CodeTab [HSIZE]; /* code table */ + /* R E S E T */ + + if (code == ClearCode ) { + memset((char *) HashTab, -1, sizeof(HashTab)); + FreeCode = ClearCode + 2; + CurCodeSize = IniCodeSize; + CurMaxCode = (1 << (IniCodeSize)) - 1; + } -static int BitsPixel, /* number of bits per pixel */ - IniCodeSize, /* initial number of bits per code */ - CurCodeSize, /* current number of bits per code */ - CurMaxCode, /* maximum code, given CurCodeSize */ - ClearCode, /* reset code */ - EOFCode, /* end of file code */ - FreeCode; /* first unused entry */ + /* I N C R E A S E C O D E S I Z E */ + + if (FreeCode > CurMaxCode ) { + CurCodeSize++; + if ( CurCodeSize == BITS ) + CurMaxCode = THELIMIT; + else + CurMaxCode = (1 << (CurCodeSize)) - 1; + } + + /* E N D O F F I L E : write the rest of the buffer */ + + if( code == EOFCode ) { + while( cur_bits > 0 ) { + char_out(cur_accum & 0xff); + cur_accum >>= 8; + cur_bits -= 8; + } + char_flush(); + } +} -static long Nbyte; -static void (*put_b) ARGS((byte)); -static void output ARGS((int)); -static void char_init(); -static void char_out ARGS((int)); -static void char_flush(); -static void put_short ARGS((int)); /*********************************************************************** * * @@ -60,12 +149,14 @@ static void put_short ARGS((int)); * Return: size of GIF * * * ***********************************************************************/ -long GIFencode(int Width, int Height, int Ncol, byte R[], byte G[], byte B[], byte ScLine[], - void(*get_scline) ARGS((int, int, byte *)), void(*pb) ARGS((byte))) +long TGifEncode::GIFencode(int Width, int Height, int Ncol, unsigned char *R, unsigned char *G, unsigned char *B) +// void(*get_scline) ARGS((int, int, byte *)), void(*pb) ARGS((byte))) { long CodeK; int ncol, i, x, y, disp, Code, K; + std::vector ScLine(Width); + /* C H E C K P A R A M E T E R S */ Code = 0; @@ -82,8 +173,7 @@ long GIFencode(int Width, int Height, int Ncol, byte R[], byte G[], byte B[], by /* I N I T I A L I S A T I O N */ - put_b = pb; - Nbyte = 0; + fNbyte = 0; char_init(); /* initialise "char_..." routines */ /* F I N D # O F B I T S P E R P I X E L */ @@ -149,7 +239,7 @@ long GIFencode(int Width, int Height, int Ncol, byte R[], byte G[], byte B[], by FreeCode = ClearCode + 2; output(ClearCode); for (y=0; y 0) - cur_accum |= ((long)code << cur_bits); - else - cur_accum = code; - cur_bits += CurCodeSize; - while( cur_bits >= 8 ) { - char_out( (unsigned int) (cur_accum & 0xFF) ); - cur_accum >>= 8; - cur_bits -= 8; - } - - /* R E S E T */ - - if (code == ClearCode ) { - memset((char *) HashTab, -1, sizeof(HashTab)); - FreeCode = ClearCode + 2; - CurCodeSize = IniCodeSize; - CurMaxCode = (1 << (IniCodeSize)) - 1; - } - - /* I N C R E A S E C O D E S I Z E */ - - if (FreeCode > CurMaxCode ) { - CurCodeSize++; - if ( CurCodeSize == BITS ) - CurMaxCode = THELIMIT; - else - CurMaxCode = (1 << (CurCodeSize)) - 1; - } - - /* E N D O F F I L E : write the rest of the buffer */ - - if( code == EOFCode ) { - while( cur_bits > 0 ) { - char_out( (unsigned int)(cur_accum & 0xff) ); - cur_accum >>= 8; - cur_bits -= 8; - } - char_flush(); - } -} - -static void char_init() -{ - a_count = 0; - cur_accum = 0; - cur_bits = 0; -} - -static void char_out(int c) -{ - accum[a_count++] = c; - if (a_count >= 254) - char_flush(); -} - -static void char_flush() -{ - int i; - - if (a_count == 0) return; - put_byte(a_count); - for (i=0; i>8) & 0xFF); -} diff --git a/graf2d/x11/src/gifencode.h b/graf2d/x11/src/gifencode.h new file mode 100644 index 0000000000000..219f06e438306 --- /dev/null +++ b/graf2d/x11/src/gifencode.h @@ -0,0 +1,60 @@ +/* @(#)root/x11:$Id$ */ +/* Author: S.Linev 20/04/2026 */ +/* C++ interface for gifencode.c */ + + +#ifndef gifencode_h +#define gifencode_h + +#include +#include +#include + + +class TGifEncode { + + private: + + enum { HSIZE = 5003 }; /* hash table size */ + + unsigned long cur_accum = 0; + int cur_bits = 0; + int a_count = 0; + + int BitsPixel = 0; /* number of bits per pixel */ + int IniCodeSize = 0; /* initial number of bits per code */ + int CurCodeSize = 0; /* current number of bits per code */ + int CurMaxCode = 0; /* maximum code, given CurCodeSize */ + int ClearCode = 0; /* reset code */ + int EOFCode = 0; /* end of file code */ + int FreeCode = 0; /* first unused entry */ + + long HashTab [HSIZE]; /* hash table */ + int CodeTab [HSIZE]; /* code table */ + unsigned char accum[256]; + + long fNbyte = 0; + FILE *fOut = nullptr; + + void put_byte(unsigned char b); + void char_init(); + void char_out(unsigned char c); + void char_flush(); + void put_short(int word); + void output(int code); + + protected: + + virtual void get_scline(int y, int width, unsigned char *buf) = 0; + + public: + virtual ~TGifEncode() { CloseFile(); } + + bool OpenFile(const char *fname); + void CloseFile(); + + long GIFencode(int Width, int Height, int Ncol, unsigned char *R, unsigned char *G, unsigned char *B); +}; + + +#endif From 972d650d7e1b69855eb4cca3e854d9559f7cb0f4 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Tue, 21 Apr 2026 07:50:27 +0200 Subject: [PATCH 04/15] [tgx11] do not use static variables and methods for GIF conversion Now everything incapsulated into TX11GifEncode class One can create several images independent from each other --- graf2d/x11/src/TGX11.cxx | 54 ++++++++++------------------------------ 1 file changed, 13 insertions(+), 41 deletions(-) diff --git a/graf2d/x11/src/TGX11.cxx b/graf2d/x11/src/TGX11.cxx index 3e7a67469458e..7205b2009288f 100644 --- a/graf2d/x11/src/TGX11.cxx +++ b/graf2d/x11/src/TGX11.cxx @@ -2807,14 +2807,7 @@ void TGX11::WritePixmap(int wid, unsigned int w, unsigned int h, char *pxname) // Functions for GIFencode() // -static FILE *gOut; // output unit used WriteGIF and PutByte -static XImage *gXimage = nullptr; // image used in WriteGIF and GetPixel - extern "C" { - int GIFquantize(UInt_t width, UInt_t height, Int_t *ncol, Byte_t *red, Byte_t *green, - Byte_t *blue, Byte_t *outputBuf, Byte_t *outputCmap); - // long GIFencode(int Width, int Height, Int_t Ncol, Byte_t R[], Byte_t G[], Byte_t B[], Byte_t ScLine[], -// void (*get_scline) (int, int, Byte_t *), void (*pb)(Byte_t)); int GIFdecode(Byte_t *gifArr, Byte_t *pixArr, int *Width, int *Height, int *Ncols, Byte_t *R, Byte_t *G, Byte_t *B); int GIFinfo(Byte_t *gifArr, int *Width, int *Height, int *Ncols); } @@ -2822,20 +2815,6 @@ extern "C" { //////////////////////////////////////////////////////////////////////////////// /// Get pixels in line y and put in array scline. -static void GetPixel(int y, int width, Byte_t *scline) -{ - for (int i = 0; i < width; i++) - scline[i] = Byte_t(XGetPixel(gXimage, i, y)); -} - -//////////////////////////////////////////////////////////////////////////////// -/// Put byte b in output stream. - -static void PutByte(Byte_t b) -{ - if (ferror(gOut) == 0) fputc(b, gOut); -} - class TX11GifEncode : public TGifEncode { private: XImage *fXimage = nullptr; @@ -2856,18 +2835,11 @@ class TX11GifEncode : public TGifEncode { Int_t TGX11::WriteGIF(char *name) { - unsigned char r[256], b[256], g[256]; + XImage *image = XGetImage((Display*)fDisplay, gCws->fDrawing, 0, 0, + gCws->fWidth, gCws->fHeight, + AllPlanes, ZPixmap); - if (gXimage) { - XDestroyImage(gXimage); - gXimage = nullptr; - } - - gXimage = XGetImage((Display*)fDisplay, gCws->fDrawing, 0, 0, - gCws->fWidth, gCws->fHeight, - AllPlanes, ZPixmap); - - if (!gXimage) { + if (!image) { Error("WriteGIF", "Cannot create image for writing GIF. Try in batch mode."); return 0; } @@ -2884,7 +2856,7 @@ Int_t TGX11::WriteGIF(char *name) // collect different image colors for (UInt_t x = 0; x < gCws->fWidth; x++) { for (UInt_t y = 0; y < gCws->fHeight; y++) { - ULong_t pixel = XGetPixel(gXimage, x, y); + ULong_t pixel = XGetPixel(image, x, y); if (std::find(orgcolors.begin(), orgcolors.end(), pixel) == orgcolors.end()) orgcolors.emplace_back(pixel); } @@ -2892,8 +2864,7 @@ Int_t TGX11::WriteGIF(char *name) if (orgcolors.size() > 256) { Error("WriteGIF", "Cannot create GIF of image containing more than 256 colors. Try in batch mode."); - XDestroyImage(gXimage); - gXimage = nullptr; + XDestroyImage(image); return 0; } @@ -2920,15 +2891,17 @@ Int_t TGX11::WriteGIF(char *name) // update image with indices (pixels) into the new RGB colormap for (UInt_t x = 0; x < gCws->fWidth; x++) { for (UInt_t y = 0; y < gCws->fHeight; y++) { - ULong_t pixel = XGetPixel(gXimage, x, y); + 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(gXimage, x, y, idx); + XPutPixel(image, x, y, idx); } } } + std::vector r(orgcolors.size()), b(orgcolors.size()), g(orgcolors.size()); + for (std::size_t i = 0; i < orgcolors.size(); i++) { r[i] = xcol[i].red * 255 / maxcol; g[i] = xcol[i].green * 255 / maxcol; @@ -2937,9 +2910,9 @@ Int_t TGX11::WriteGIF(char *name) Int_t ret = 0; - TX11GifEncode gif(gXimage); + TX11GifEncode gif(image); if (gif.OpenFile(name)) { - auto len = gif.GIFencode(gCws->fWidth, gCws->fHeight, orgcolors.size(), r, g, b); + auto len = gif.GIFencode(gCws->fWidth, gCws->fHeight, orgcolors.size(), r.data(), g.data(), b.data()); if (len > 0) ret = 1; gif.CloseFile(); @@ -2948,8 +2921,7 @@ Int_t TGX11::WriteGIF(char *name) } // cleanup image at the end - XDestroyImage(gXimage); - gXimage = nullptr; + XDestroyImage(image); return ret; } From b6170a76e7f72373807ef870e3d20431d437a001 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Tue, 21 Apr 2026 08:07:59 +0200 Subject: [PATCH 05/15] [tgx11] convert gifdecode.c into C++ Here much less static variables are involved File reading done in TGX11 classes Code is unused anyway --- graf2d/x11/CMakeLists.txt | 2 +- graf2d/x11/src/TGX11.cxx | 26 ++++------ graf2d/x11/src/{gifdecode.c => gifdecode.cxx} | 52 +++++++++---------- graf2d/x11/src/gifdecode.h | 29 +++++++++++ 4 files changed, 64 insertions(+), 45 deletions(-) rename graf2d/x11/src/{gifdecode.c => gifdecode.cxx} (88%) create mode 100644 graf2d/x11/src/gifdecode.h diff --git a/graf2d/x11/CMakeLists.txt b/graf2d/x11/CMakeLists.txt index 98ca87c3aaa21..a56da6bfab9eb 100644 --- a/graf2d/x11/CMakeLists.txt +++ b/graf2d/x11/CMakeLists.txt @@ -13,7 +13,7 @@ ROOT_STANDARD_LIBRARY_PACKAGE(GX11 HEADERS TGX11.h SOURCES - src/gifdecode.c + src/gifdecode.cxx src/gifencode.cxx src/gifquantize.c src/GX11Gui.cxx diff --git a/graf2d/x11/src/TGX11.cxx b/graf2d/x11/src/TGX11.cxx index 7205b2009288f..adf6009f01d5b 100644 --- a/graf2d/x11/src/TGX11.cxx +++ b/graf2d/x11/src/TGX11.cxx @@ -63,6 +63,7 @@ by Olivier Couet (package X11INT). #endif #include "gifencode.h" +#include "gifdecode.h" extern float XRotVersion(char*, int); extern void XRotSetMagnification(float); @@ -2802,16 +2803,6 @@ void TGX11::WritePixmap(int wid, unsigned int w, unsigned int h, char *pxname) XWriteBitmapFile((Display*)fDisplay, pxname, gTws->fDrawing, wval, hval, -1, -1); } - -// -// Functions for GIFencode() -// - -extern "C" { - int GIFdecode(Byte_t *gifArr, Byte_t *pixArr, int *Width, int *Height, int *Ncols, Byte_t *R, Byte_t *G, Byte_t *B); - int GIFinfo(Byte_t *gifArr, int *Width, int *Height, int *Ncols); -} - //////////////////////////////////////////////////////////////////////////////// /// Get pixels in line y and put in array scline. @@ -3003,14 +2994,13 @@ void TGX11::PutImage(Int_t offset,Int_t itran,Int_t x0,Int_t y0,Int_t nx,Int_t n Pixmap_t TGX11::ReadGIF(int x0, int y0, const char *file, Window_t id) { - FILE *fd; Seek_t filesize = 0; unsigned char *gifArr, *pixArr, red[256], green[256], blue[256], *j1, *j2, icol; int i, j, k, width, height, ncolor, irep, offset; float rr, gg, bb; Pixmap_t pic = 0; - fd = fopen(file, "r"); + FILE *fd = fopen(file, "r"); if (!fd) { Error("ReadGIF", "unable to open GIF file"); return pic; @@ -3041,7 +3031,7 @@ Pixmap_t TGX11::ReadGIF(int x0, int y0, const char *file, Window_t id) } fclose(fd); - irep = GIFinfo(gifArr, &width, &height, &ncolor); + irep = TGifDecode::GIFinfo(gifArr, &width, &height, &ncolor); if (irep != 0) { free(gifArr); return pic; @@ -3053,7 +3043,9 @@ Pixmap_t TGX11::ReadGIF(int x0, int y0, const char *file, Window_t id) return pic; } - irep = GIFdecode(gifArr, pixArr, &width, &height, &ncolor, red, green, blue); + TGifDecode gif; + + irep = gif.GIFdecode(gifArr, pixArr, &width, &height, &ncolor, red, green, blue); if (irep != 0) { free(gifArr); free(pixArr); @@ -3081,7 +3073,8 @@ Pixmap_t TGX11::ReadGIF(int x0, int y0, const char *file, Window_t id) icol = *j1; *j1++ = *j2; *j2++ = icol; } } - if (id) pic = CreatePixmap(id, width, height); + if (id) + pic = CreatePixmap(id, width, height); PutImage(offset,-1,x0,y0,width,height,0,0,width-1,height-1,pixArr,pic); free(gifArr); @@ -3089,7 +3082,8 @@ Pixmap_t TGX11::ReadGIF(int x0, int y0, const char *file, Window_t id) if (pic) return pic; - else if (gCws->fDrawing) + + if (gCws->fDrawing) return (Pixmap_t)gCws->fDrawing; return 0; } diff --git a/graf2d/x11/src/gifdecode.c b/graf2d/x11/src/gifdecode.cxx similarity index 88% rename from graf2d/x11/src/gifdecode.c rename to graf2d/x11/src/gifdecode.cxx index 26af335ae285b..559148c56d629 100644 --- a/graf2d/x11/src/gifdecode.c +++ b/graf2d/x11/src/gifdecode.cxx @@ -1,33 +1,25 @@ /* @(#)root/x11:$Id$ */ -/* Author: Rene Brun 11/06/97*/ -#include -#include +/* Author: Rene Brun 11/06/97 */ +/* C++ interface Sergey Linev 21/04/2026 */ +#include "gifdecode.h" -#define BITS 12 /* largest code size */ -#define TSIZE 4096 /* tables size */ - -typedef unsigned char byte; - -static int Prefix[TSIZE]; /* prefix table */ -static byte Suffix[TSIZE]; /* suffix table */ -static byte OutCode[TSIZE]; /* output stack */ +#include +#include -static byte *ptr1, /* pointer to GIF array */ - *ptr2; /* pointer to PIX array */ -static int CurCodeSize, /* current number of bits per code */ - CurMaxCode; /* maximum code, given CurCodeSize */ -static long CurBit; /* current bit in GIF image data */ +#define BITS 12 /* largest code size */ +#define TSIZE 4096 /* tables size */ /*************************************************************** * * ***************************************************************/ -static int ReadCode() +int TGifDecode::ReadCode() { - static long b3[3], CurByte; - static byte lblk; + long b3[3] = {0, 0, 0}; + long CurByte; + unsigned char lblk; int shift, nbyte; long OldByte; @@ -57,7 +49,7 @@ static int ReadCode() /*************************************************************** * * ***************************************************************/ -static void OutPixel(byte pix) +void TGifDecode::OutPixel(unsigned char pix) { *ptr2++ = pix; } @@ -77,15 +69,15 @@ static void OutPixel(byte pix) * 1 - if error * * * ***************************************************************/ -int GIFinfo(byte *GIFarr, int *Width, int *Height, int *Ncols) +int TGifDecode::GIFinfo(unsigned char *GIFarr, int *Width, int *Height, int *Ncols) { - byte b; + unsigned char b; - ptr1 = GIFarr; + unsigned char *ptr1 = GIFarr; /* R E A D H E A D E R */ - if (strncmp((char *)GIFarr,"GIF87a",6) && strncmp((char *)GIFarr,"GIF89a",6)) + if (strncmp((const char *)GIFarr,"GIF87a",6) && strncmp((const char *)GIFarr,"GIF89a",6)) { fprintf(stderr,"\nGIFinfo: not a GIF\n"); return 1; @@ -146,9 +138,9 @@ int GIFinfo(byte *GIFarr, int *Width, int *Height, int *Ncols) * 1 - if error * * * ***************************************************************/ -int GIFdecode(byte *GIFarr, byte *PIXarr, int *Width, int *Height, int *Ncols, byte *R, byte *G, byte *B) +int TGifDecode::GIFdecode(unsigned char *GIFarr, unsigned char *PIXarr, int *Width, int *Height, int *Ncols, unsigned char *R, unsigned char *G, unsigned char *B) { - byte b, /* working variable */ + unsigned char b, /* working variable */ FinChar; /* final character */ int i, /* working variable for loops */ @@ -165,6 +157,10 @@ int GIFdecode(byte *GIFarr, byte *PIXarr, int *Width, int *Height, int *Ncols, b long Npix; /* number of pixels */ + int Prefix[TSIZE]; /* prefix table */ + unsigned char Suffix[TSIZE]; /* suffix table */ + unsigned char OutCode[TSIZE]; /* output stack */ + ptr1 = GIFarr; ptr2 = PIXarr; OldCode = 0; @@ -271,8 +267,8 @@ int GIFdecode(byte *GIFarr, byte *PIXarr, int *Width, int *Height, int *Ncols, b fprintf(stderr,"\nGIFdecode: corrupted GIF (big output count)\n"); return 1; } - OutCode[OutCount++] = Suffix[CurCode]; - CurCode = Prefix[CurCode]; + OutCode[OutCount++] = Suffix[CurCode]; + CurCode = Prefix[CurCode]; } FinChar = CurCode; OutCode[OutCount++] = FinChar; diff --git a/graf2d/x11/src/gifdecode.h b/graf2d/x11/src/gifdecode.h new file mode 100644 index 0000000000000..ea17f7e52acf4 --- /dev/null +++ b/graf2d/x11/src/gifdecode.h @@ -0,0 +1,29 @@ +/* @(#)root/x11:$Id$ */ +/* Author: S.Linev 20/04/2026 */ +/* C++ interface for gifdecode.c */ + +#ifndef gifdecode_h +#define gifdecode_h + +class TGifDecode { + private: + unsigned char *ptr1 = nullptr; /* pointer to GIF array */ + unsigned char *ptr2 = nullptr; /* pointer to PIX array */ + + int CurCodeSize = 0; /* current number of bits per code */ + int CurMaxCode = 0; /* maximum code, given CurCodeSize */ + long CurBit = 0; /* current bit in GIF image data */ + + int ReadCode(); + void OutPixel(unsigned char pix); + + public: + + static int GIFinfo(unsigned char *GIFarr, int *Width, int *Height, int *Ncols); + + int GIFdecode(unsigned char *GIFarr, unsigned char *PIXarr, int *Width, int *Height, int *Ncols, unsigned char *R, unsigned char *G, unsigned char *B); + +}; + + +#endif \ No newline at end of file From 5ea7d8f5d1f38494ed1430be421e8ec273dda2a7 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Tue, 21 Apr 2026 08:24:55 +0200 Subject: [PATCH 06/15] Remove no longer used gifquantize.c Was designed to store images with more than 256 colors, but was never used --- graf2d/x11/CMakeLists.txt | 1 - graf2d/x11/src/gifquantize.c | 302 ----------------------------------- 2 files changed, 303 deletions(-) delete mode 100644 graf2d/x11/src/gifquantize.c diff --git a/graf2d/x11/CMakeLists.txt b/graf2d/x11/CMakeLists.txt index a56da6bfab9eb..0ab89fdb61c7c 100644 --- a/graf2d/x11/CMakeLists.txt +++ b/graf2d/x11/CMakeLists.txt @@ -15,7 +15,6 @@ ROOT_STANDARD_LIBRARY_PACKAGE(GX11 SOURCES src/gifdecode.cxx src/gifencode.cxx - src/gifquantize.c src/GX11Gui.cxx src/Rotated.cxx src/TGX11.cxx diff --git a/graf2d/x11/src/gifquantize.c b/graf2d/x11/src/gifquantize.c deleted file mode 100644 index beb1585fe885f..0000000000000 --- a/graf2d/x11/src/gifquantize.c +++ /dev/null @@ -1,302 +0,0 @@ -/* @(#)root/x11:$Id$ */ -/* Author: Fons Rademakers 04/11/98*/ -/***************************************************************************** -* Module to quantize high resolution image into lower one. You may want to * -* peek into the following article this code is based on: * -* "Color Image Quantization for frame buffer Display", by Paul Heckbert * -* SIGGRAPH 1982 page 297-307. * -*****************************************************************************/ - -#include -#include - -typedef unsigned char byte; -typedef struct GifColorType { - byte Red, Green, Blue; -} GifColorType; - -#define ABS(x) ((x) > 0 ? (x) : (-(x))) - -#define GIF_ERROR 0 -#define GIF_OK 1 - -/* The colors are stripped to 5 bits per primary color */ -#define COLOR_ARRAY_SIZE 32768 -#define BITS_PER_PRIM_COLOR 5 -#define MAX_PRIM_COLOR 0x1f - - -static int SortRGBAxis; - -typedef struct QuantizedColorType { - byte RGB[3]; - byte NewColorIndex; - long Count; - struct QuantizedColorType *Pnext; -} QuantizedColorType; - -typedef struct NewColorMapType { - byte RGBMin[3], RGBWidth[3]; - unsigned int NumEntries; /* # of QuantizedColorType in linked list below. */ - long Count; /* Total number of pixels in all the entries. */ - QuantizedColorType *QuantizedColors; -} NewColorMapType; - -static int SubdivColorMap(NewColorMapType *NewColorSubdiv, - unsigned int ColorMapSize, - unsigned int *NewColorMapSize); -static int SortCmpRtn(const void *Entry1, const void *Entry2); - - -/****************************************************************************** -* Quantize high resolution image into lower one. Input image consists of a * -* 2D array for each of the RGB colors with size Width by Height. There is no * -* Color map for the input. Output is a quantized image with 2D array of * -* indexes into the output color map. * -* Note input image can be 24 bits at the most (8 for red/green/blue) and * -* the output has 256 colors at the most (256 entries in the color map.). * -* ColorMapSize specifies size of color map up to 256 and will be updated to * -* real size before returning. * -* Also non of the parameter are allocated by this routine. * -* This function returns GIF_OK if successful, GIF_ERROR otherwise. * -******************************************************************************/ -int GIFquantize(unsigned int Width, unsigned int Height, int *ColorMapSize, - byte *RedInput, byte *GreenInput, byte *BlueInput, - byte *OutputBuffer, GifColorType *OutputColorMap) -{ - unsigned int Index, NumOfEntries, newsize; - int i, j, MaxRGBError[3]; - int NewColorMapSize; - long Red, Green, Blue; - NewColorMapType NewColorSubdiv[256]; - QuantizedColorType *ColorArrayEntries, *QuantizedColor; - - if ((ColorArrayEntries = (QuantizedColorType *) - malloc(sizeof(QuantizedColorType) * COLOR_ARRAY_SIZE)) == NULL) { - fprintf(stderr, "QuantizeBuffer: not enough memory\n"); - return GIF_ERROR; - } - - for (i = 0; i < COLOR_ARRAY_SIZE; i++) { - ColorArrayEntries[i].RGB[0]= i >> (2 * BITS_PER_PRIM_COLOR); - ColorArrayEntries[i].RGB[1] = (i >> BITS_PER_PRIM_COLOR) & MAX_PRIM_COLOR; - ColorArrayEntries[i].RGB[2] = i & MAX_PRIM_COLOR; - ColorArrayEntries[i].Count = 0; - } - - /* Sample the colors and their distribution: */ - for (i = 0; i < (int)(Width * Height); i++) { - Index = ((RedInput[i] >> (8 - BITS_PER_PRIM_COLOR)) - << (2 * BITS_PER_PRIM_COLOR)) + - ((GreenInput[i] >> (8 - BITS_PER_PRIM_COLOR)) - << BITS_PER_PRIM_COLOR) + - (BlueInput[i] >> (8 - BITS_PER_PRIM_COLOR)); - ColorArrayEntries[Index].Count++; - } - - /* Put all the colors in the first entry of the color map, and call the */ - /* recursive subdivision process. */ - for (i = 0; i < 256; i++) { - NewColorSubdiv[i].QuantizedColors = NULL; - NewColorSubdiv[i].Count = NewColorSubdiv[i].NumEntries = 0; - for (j = 0; j < 3; j++) { - NewColorSubdiv[i].RGBMin[j] = 0; - NewColorSubdiv[i].RGBWidth[j] = 255; - } - } - - /* Find the non empty entries in the color table and chain them: */ - for (i = 0; i < COLOR_ARRAY_SIZE; i++) - if (ColorArrayEntries[i].Count > 0) break; - QuantizedColor = NewColorSubdiv[0].QuantizedColors = &ColorArrayEntries[i]; - NumOfEntries = 1; - while (++i < COLOR_ARRAY_SIZE) - if (ColorArrayEntries[i].Count > 0) { - QuantizedColor -> Pnext = &ColorArrayEntries[i]; - QuantizedColor = &ColorArrayEntries[i]; - NumOfEntries++; - } - QuantizedColor -> Pnext = NULL; - - NewColorSubdiv[0].NumEntries = NumOfEntries;/* Different sampled colors. */ - NewColorSubdiv[0].Count = ((long) Width) * Height; /* Pixels. */ - newsize = 1; - if (SubdivColorMap(NewColorSubdiv, *ColorMapSize, &newsize) != GIF_OK) { - free((char *) ColorArrayEntries); - return GIF_ERROR; - } - NewColorMapSize = (int)newsize; - if (NewColorMapSize < *ColorMapSize) { - /* And clear rest of color map: */ - for (i = NewColorMapSize; i < *ColorMapSize; i++) - OutputColorMap[i].Red = - OutputColorMap[i].Green = - OutputColorMap[i].Blue = 0; - } - - /* Average the colors in each entry to be the color to be used in the */ - /* output color map, and plug it into the output color map itself. */ - for (i = 0; i < NewColorMapSize; i++) { - if ((j = NewColorSubdiv[i].NumEntries) > 0) { - QuantizedColor = NewColorSubdiv[i].QuantizedColors; - Red = Green = Blue = 0; - while (QuantizedColor) { - QuantizedColor -> NewColorIndex = i; - Red += QuantizedColor -> RGB[0]; - Green += QuantizedColor -> RGB[1]; - Blue += QuantizedColor -> RGB[2]; - QuantizedColor = QuantizedColor -> Pnext; - } - OutputColorMap[i].Red = (Red << (8 - BITS_PER_PRIM_COLOR)) / j; - OutputColorMap[i].Green = (Green << (8 - BITS_PER_PRIM_COLOR)) / j; - OutputColorMap[i].Blue= (Blue << (8 - BITS_PER_PRIM_COLOR)) / j; - } - else - fprintf(stderr, "Null entry in quantized color map - thats weird."); - } - - /* Finally scan the input buffer again and put the mapped index in the */ - /* output buffer. */ - MaxRGBError[0] = MaxRGBError[1] = MaxRGBError[2] = 0; - for (i = 0; i < (int)(Width * Height); i++) { - Index = ((RedInput[i] >> (8 - BITS_PER_PRIM_COLOR)) - << (2 * BITS_PER_PRIM_COLOR)) + - ((GreenInput[i] >> (8 - BITS_PER_PRIM_COLOR)) - << BITS_PER_PRIM_COLOR) + - (BlueInput[i] >> (8 - BITS_PER_PRIM_COLOR)); - Index = ColorArrayEntries[Index].NewColorIndex; - OutputBuffer[i] = Index; - if (MaxRGBError[0] < ABS(OutputColorMap[Index].Red - RedInput[i])) - MaxRGBError[0] = ABS(OutputColorMap[Index].Red - RedInput[i]); - if (MaxRGBError[1] < ABS(OutputColorMap[Index].Green - GreenInput[i])) - MaxRGBError[1] = ABS(OutputColorMap[Index].Green - GreenInput[i]); - if (MaxRGBError[2] < ABS(OutputColorMap[Index].Blue - BlueInput[i])) - MaxRGBError[2] = ABS(OutputColorMap[Index].Blue - BlueInput[i]); - } - -#ifdef DEBUG - fprintf(stderr, - "Quantization L(0) errors: Red = %d, Green = %d, Blue = %d.\n", - MaxRGBError[0], MaxRGBError[1], MaxRGBError[2]); -#endif /* DEBUG */ - - free((char *) ColorArrayEntries); - - *ColorMapSize = NewColorMapSize; - - return GIF_OK; -} - -/****************************************************************************** -* Routine to subdivide the RGB space recursively using median cut in each * -* axes alternatingly until ColorMapSize different cubes exists. * -* The biggest cube in one dimension is subdivide unless it has only one entry.* -* Returns GIF_ERROR if failed, otherwise GIF_OK. * -******************************************************************************/ -static int SubdivColorMap(NewColorMapType *NewColorSubdiv, - unsigned int ColorMapSize, - unsigned int *NewColorMapSize) -{ - int MaxSize; - unsigned int i, j, Index = 0, NumEntries, MinColor, MaxColor; - long Sum, Count; - QuantizedColorType *QuantizedColor, **SortArray; - - while (ColorMapSize > *NewColorMapSize) { - /* Find candidate for subdivision: */ - MaxSize = -1; - for (i = 0; i < *NewColorMapSize; i++) { - for (j = 0; j < 3; j++) { - if (((int) NewColorSubdiv[i].RGBWidth[j]) > MaxSize && - NewColorSubdiv[i].NumEntries > 1) { - MaxSize = NewColorSubdiv[i].RGBWidth[j]; - Index = i; - SortRGBAxis = j; - } - } - } - - if (MaxSize == -1) - return GIF_OK; - - /* Split the entry Index into two along the axis SortRGBAxis: */ - - /* Sort all elements in that entry along the given axis and split at */ - /* the median. */ - if ((SortArray = (QuantizedColorType **) - malloc(sizeof(QuantizedColorType *) * - NewColorSubdiv[Index].NumEntries)) == NULL) - return GIF_ERROR; - for (j = 0, QuantizedColor = NewColorSubdiv[Index].QuantizedColors; - j < NewColorSubdiv[Index].NumEntries && QuantizedColor != NULL; - j++, QuantizedColor = QuantizedColor -> Pnext) - SortArray[j] = QuantizedColor; - qsort(SortArray, NewColorSubdiv[Index].NumEntries, - sizeof(QuantizedColorType *), SortCmpRtn); - - /* Relink the sorted list into one: */ - for (j = 0; j < NewColorSubdiv[Index].NumEntries - 1; j++) - SortArray[j] -> Pnext = SortArray[j + 1]; - SortArray[NewColorSubdiv[Index].NumEntries - 1] -> Pnext = NULL; - NewColorSubdiv[Index].QuantizedColors = QuantizedColor = SortArray[0]; - free((char *) SortArray); - - /* Now simply add the Counts until we have half of the Count: */ - Sum = NewColorSubdiv[Index].Count / 2 - QuantizedColor -> Count; - NumEntries = 1; - Count = QuantizedColor -> Count; - while (QuantizedColor -> Pnext != NULL && - QuantizedColor -> Pnext -> Pnext != NULL && - (Sum -= QuantizedColor -> Pnext -> Count) >= 0) { - QuantizedColor = QuantizedColor -> Pnext; - NumEntries++; - Count += QuantizedColor -> Count; - } - /* Save the values of the last color of the first half, and first */ - /* of the second half so we can update the Bounding Boxes later. */ - /* Also as the colors are quantized and the BBoxes are full 0..255, */ - /* they need to be rescaled. */ - MaxColor = QuantizedColor -> RGB[SortRGBAxis];/* Max. of first half. */ - if (QuantizedColor -> Pnext) MinColor = QuantizedColor -> Pnext -> RGB[SortRGBAxis];/* of second. */ - else MinColor = 0; - MaxColor <<= (8 - BITS_PER_PRIM_COLOR); - MinColor <<= (8 - BITS_PER_PRIM_COLOR); - - /* Partition right here: */ - NewColorSubdiv[*NewColorMapSize].QuantizedColors = - QuantizedColor -> Pnext; - QuantizedColor -> Pnext = NULL; - NewColorSubdiv[*NewColorMapSize].Count = Count; - NewColorSubdiv[Index].Count -= Count; - NewColorSubdiv[*NewColorMapSize].NumEntries = - NewColorSubdiv[Index].NumEntries - NumEntries; - NewColorSubdiv[Index].NumEntries = NumEntries; - for (j = 0; j < 3; j++) { - NewColorSubdiv[*NewColorMapSize].RGBMin[j] = - NewColorSubdiv[Index].RGBMin[j]; - NewColorSubdiv[*NewColorMapSize].RGBWidth[j] = - NewColorSubdiv[Index].RGBWidth[j]; - } - NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] = - NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] + - NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] - - MinColor; - NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] = MinColor; - - NewColorSubdiv[Index].RGBWidth[SortRGBAxis] = - MaxColor - NewColorSubdiv[Index].RGBMin[SortRGBAxis]; - - (*NewColorMapSize)++; - } - - return GIF_OK; -} - -/****************************************************************************** -* Routine called by qsort to compare to entries. * -******************************************************************************/ -static int SortCmpRtn(const void *Entry1, const void *Entry2) -{ - return (* ((QuantizedColorType **) Entry1)) -> RGB[SortRGBAxis] - - (* ((QuantizedColorType **) Entry2)) -> RGB[SortRGBAxis]; -} From d90e6d794a14e7871d2e219b3825c8794dfab45f Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Tue, 21 Apr 2026 09:20:06 +0200 Subject: [PATCH 07/15] Move gifencode/decode to separate directory It is only internal ROOT classes which are used from TGX11 and in the future from TGWin32 --- graf2d/gifencode/Readme.md | 7 +++++++ graf2d/{x11/src => gifencode}/gifdecode.cxx | 0 graf2d/{x11/src => gifencode}/gifdecode.h | 0 graf2d/{x11/src => gifencode}/gifencode.cxx | 0 graf2d/{x11/src => gifencode}/gifencode.h | 0 graf2d/x11/CMakeLists.txt | 6 ++++-- 6 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 graf2d/gifencode/Readme.md rename graf2d/{x11/src => gifencode}/gifdecode.cxx (100%) rename graf2d/{x11/src => gifencode}/gifdecode.h (100%) rename graf2d/{x11/src => gifencode}/gifencode.cxx (100%) rename graf2d/{x11/src => gifencode}/gifencode.h (100%) diff --git a/graf2d/gifencode/Readme.md b/graf2d/gifencode/Readme.md new file mode 100644 index 0000000000000..67a99751b6056 --- /dev/null +++ b/graf2d/gifencode/Readme.md @@ -0,0 +1,7 @@ +# gifencode and gifdecode + +Originally it was plain C code to create GIF images from pixmap +and read GIF images to create new pixmap + +Later C++ interface was provided to exclude static variables usage + diff --git a/graf2d/x11/src/gifdecode.cxx b/graf2d/gifencode/gifdecode.cxx similarity index 100% rename from graf2d/x11/src/gifdecode.cxx rename to graf2d/gifencode/gifdecode.cxx diff --git a/graf2d/x11/src/gifdecode.h b/graf2d/gifencode/gifdecode.h similarity index 100% rename from graf2d/x11/src/gifdecode.h rename to graf2d/gifencode/gifdecode.h diff --git a/graf2d/x11/src/gifencode.cxx b/graf2d/gifencode/gifencode.cxx similarity index 100% rename from graf2d/x11/src/gifencode.cxx rename to graf2d/gifencode/gifencode.cxx diff --git a/graf2d/x11/src/gifencode.h b/graf2d/gifencode/gifencode.h similarity index 100% rename from graf2d/x11/src/gifencode.h rename to graf2d/gifencode/gifencode.h diff --git a/graf2d/x11/CMakeLists.txt b/graf2d/x11/CMakeLists.txt index 0ab89fdb61c7c..987277c3c16e4 100644 --- a/graf2d/x11/CMakeLists.txt +++ b/graf2d/x11/CMakeLists.txt @@ -13,8 +13,8 @@ ROOT_STANDARD_LIBRARY_PACKAGE(GX11 HEADERS TGX11.h SOURCES - src/gifdecode.cxx - src/gifencode.cxx + ../gifencode/gifdecode.cxx + ../gifencode/gifencode.cxx src/GX11Gui.cxx src/Rotated.cxx src/TGX11.cxx @@ -33,6 +33,8 @@ ROOT_STANDARD_LIBRARY_PACKAGE(GX11 ) target_include_directories(GX11 PRIVATE ${X11_INCLUDE_DIR}) +target_include_directories(GX11 PRIVATE ../gifencode) + if(AIX) target_include_directories(GX11 PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/inc) From 0219ee535f9b57d64c650182ff7294f6299c9a33 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Tue, 21 Apr 2026 10:51:27 +0200 Subject: [PATCH 08/15] Fix several warnings in gifdecode.c Some former static variables need to be made class members --- graf2d/gifencode/gifdecode.cxx | 12 +++--------- graf2d/gifencode/gifdecode.h | 10 +++++++--- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/graf2d/gifencode/gifdecode.cxx b/graf2d/gifencode/gifdecode.cxx index 559148c56d629..35b009aa5c6c2 100644 --- a/graf2d/gifencode/gifdecode.cxx +++ b/graf2d/gifencode/gifdecode.cxx @@ -17,22 +17,16 @@ ***************************************************************/ int TGifDecode::ReadCode() { - long b3[3] = {0, 0, 0}; - long CurByte; - unsigned char lblk; - int shift, nbyte; - long OldByte; - if (CurBit == -1) { lblk = 0; CurByte = -1; } CurBit += CurCodeSize; - OldByte = CurByte; + long OldByte = CurByte; CurByte = CurBit/8; - nbyte = CurByte - OldByte; - shift = 17 + (CurBit%8) - CurCodeSize; + int nbyte = CurByte - OldByte; + int shift = 17 + (CurBit%8) - CurCodeSize; while (nbyte-- > 0) { if (lblk == 0) { lblk = *ptr1++; diff --git a/graf2d/gifencode/gifdecode.h b/graf2d/gifencode/gifdecode.h index ea17f7e52acf4..933d6bd6145f4 100644 --- a/graf2d/gifencode/gifdecode.h +++ b/graf2d/gifencode/gifdecode.h @@ -10,9 +10,13 @@ class TGifDecode { unsigned char *ptr1 = nullptr; /* pointer to GIF array */ unsigned char *ptr2 = nullptr; /* pointer to PIX array */ - int CurCodeSize = 0; /* current number of bits per code */ - int CurMaxCode = 0; /* maximum code, given CurCodeSize */ - long CurBit = 0; /* current bit in GIF image data */ + int CurCodeSize = 0; /* current number of bits per code */ + int CurMaxCode = 0; /* maximum code, given CurCodeSize */ + long CurBit = -1; /* current bit in GIF image data */ + + long b3[3] = {0, 0, 0}; + long CurByte = -1; + unsigned char lblk = 0; int ReadCode(); void OutPixel(unsigned char pix); From be0c3de4ee62ebde40108d2556951fa40117d999 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Tue, 21 Apr 2026 11:27:29 +0200 Subject: [PATCH 09/15] [tgx11] improve TGX11::WriteGif Instead rebuilding of XImage just search for pixel index in original image and store index. In fact it is exactly that is required by GIFencode. Re --- graf2d/x11/src/TGX11.cxx | 64 ++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/graf2d/x11/src/TGX11.cxx b/graf2d/x11/src/TGX11.cxx index adf6009f01d5b..93d02ac308e8b 100644 --- a/graf2d/x11/src/TGX11.cxx +++ b/graf2d/x11/src/TGX11.cxx @@ -2804,7 +2804,7 @@ void TGX11::WritePixmap(int wid, unsigned int w, unsigned int h, char *pxname) } //////////////////////////////////////////////////////////////////////////////// -/// Get pixels in line y and put in array scline. +/// Helper class to extract pixel information from XImage for store in the GIF class TX11GifEncode : public TGifEncode { private: @@ -2812,10 +2812,21 @@ class TX11GifEncode : public TGifEncode { protected: void get_scline(int y, int width, unsigned char *buf) override { - for (int i = 0; i < width; i++) - buf[i] = XGetPixel(fXimage, i, y); + for (int x = 0; x < width; x++) { + ULong_t pixel = XGetPixel(fXimage, x, y); + buf[x] = 0; + + auto iter = std::find(orgcolors.begin(), orgcolors.end(), pixel); + if (iter != orgcolors.end()) { + auto idx = iter - orgcolors.begin(); + if (idx < 256) + buf[x] = (unsigned char) idx; + } + } } public: + std::vector orgcolors; // all unique pixels + TX11GifEncode(XImage *image) : fXimage(image) {} }; @@ -2842,36 +2853,38 @@ Int_t TGX11::WriteGIF(char *name) /// contains no more than 256 different colors). If it does contain more /// colors we will have to use GIFquantize to reduce the number of colors. - std::vector orgcolors; + TX11GifEncode gif(image); // collect different image colors 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); - if (std::find(orgcolors.begin(), orgcolors.end(), pixel) == orgcolors.end()) - orgcolors.emplace_back(pixel); + if (std::find(gif.orgcolors.begin(), gif.orgcolors.end(), pixel) == gif.orgcolors.end()) + gif.orgcolors.emplace_back(pixel); } } - if (orgcolors.size() > 256) { + auto ncolors = gif.orgcolors.size(); + + if (ncolors > 256) { Error("WriteGIF", "Cannot create GIF of image containing more than 256 colors. Try in batch mode."); XDestroyImage(image); return 0; } // get RGB values belonging to pixels - std::vector xcol(orgcolors.size()); + std::vector xcol(ncolors); - for (std::size_t i = 0; i < orgcolors.size(); i++) { - xcol[i].pixel = orgcolors[i]; + for (std::size_t i = 0; i < ncolors; i++) { + xcol[i].pixel = gif.orgcolors[i]; xcol[i].red = xcol[i].green = xcol[i].blue = 0; xcol[i].flags = DoRed | DoGreen | DoBlue; } - QueryColors(fColormap, xcol.data(), orgcolors.size()); + QueryColors(fColormap, xcol.data(), ncolors); UShort_t maxcol = 0; - for (std::size_t i = 0; i < orgcolors.size(); i++) { + for (std::size_t i = 0; i < ncolors; i++) { maxcol = TMath::Max(maxcol, xcol[i].red); maxcol = TMath::Max(maxcol, xcol[i].green); maxcol = TMath::Max(maxcol, xcol[i].blue); @@ -2879,36 +2892,23 @@ Int_t TGX11::WriteGIF(char *name) if (maxcol == 0) maxcol = 255; - // update image with indices (pixels) into the new RGB colormap - 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); - auto iter = std::find(orgcolors.begin(), orgcolors.end(), pixel); - if (iter != orgcolors.end()) { - auto idx = iter - orgcolors.begin(); - XPutPixel(image, x, y, idx); - } - } - } + std::vector r(ncolors), b(ncolors), g(ncolors); - std::vector r(orgcolors.size()), b(orgcolors.size()), g(orgcolors.size()); - - for (std::size_t i = 0; i < orgcolors.size(); i++) { - r[i] = xcol[i].red * 255 / maxcol; - g[i] = xcol[i].green * 255 / maxcol; - b[i] = xcol[i].blue * 255 / maxcol; + for (std::size_t i = 0; i < ncolors; i++) { + r[i] = (unsigned char) (xcol[i].red * 255 / maxcol); + g[i] = (unsigned char) (xcol[i].green * 255 / maxcol); + b[i] = (unsigned char) (xcol[i].blue * 255 / maxcol); } Int_t ret = 0; - TX11GifEncode gif(image); if (gif.OpenFile(name)) { - auto len = gif.GIFencode(gCws->fWidth, gCws->fHeight, orgcolors.size(), r.data(), g.data(), b.data()); + auto len = gif.GIFencode(gCws->fWidth, gCws->fHeight, ncolors, r.data(), g.data(), b.data()); if (len > 0) ret = 1; gif.CloseFile(); } else { - Error("WriteGIF","cannot write file: %s", name); + Error("WriteGIF", "cannot write file: %s", name); } // cleanup image at the end From 29d7e4a732e7d9f1fa88e73a27f4e89241f26e97 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Tue, 21 Apr 2026 09:35:26 +0200 Subject: [PATCH 10/15] [win32] use gifdecode/gifencode from central place Delete old duplicated code and invoke new c++ interface Like in TGX11 avoid lot of static variables --- graf2d/win32gdk/CMakeLists.txt | 7 +- graf2d/win32gdk/src/TGWin32.cxx | 159 +++++++--------- graf2d/win32gdk/src/gifdecode.c | 301 ----------------------------- graf2d/win32gdk/src/gifencode.c | 306 ------------------------------ graf2d/win32gdk/src/gifquantize.c | 301 ----------------------------- 5 files changed, 72 insertions(+), 1002 deletions(-) delete mode 100644 graf2d/win32gdk/src/gifdecode.c delete mode 100644 graf2d/win32gdk/src/gifencode.c delete mode 100644 graf2d/win32gdk/src/gifquantize.c diff --git a/graf2d/win32gdk/CMakeLists.txt b/graf2d/win32gdk/CMakeLists.txt index fefd4b9b3387a..66e3967cec63b 100644 --- a/graf2d/win32gdk/CMakeLists.txt +++ b/graf2d/win32gdk/CMakeLists.txt @@ -105,9 +105,8 @@ ROOT_STANDARD_LIBRARY_PACKAGE(Win32gdk TGWin32.h TGWin32GL.h SOURCES - src/gifdecode.c - src/gifencode.c - src/gifquantize.c + ../gifencode/gifdecode.cxx + ../gifencode/gifencode.cxx src/TGWin32.cxx src/TGWin32GL.cxx src/TGWin32ProxyBase.cxx @@ -128,5 +127,7 @@ ROOT_STANDARD_LIBRARY_PACKAGE(Win32gdk FREETYPE ) +target_include_directories(Win32gdk PRIVATE ../gifencode) + add_dependencies(Win32gdk GDKLIB glib) install(FILES ${gdkdlla} ${glibdlla} ${iconvdlla} DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/graf2d/win32gdk/src/TGWin32.cxx b/graf2d/win32gdk/src/TGWin32.cxx index 13185c773151d..058b66da51750 100644 --- a/graf2d/win32gdk/src/TGWin32.cxx +++ b/graf2d/win32gdk/src/TGWin32.cxx @@ -60,6 +60,9 @@ by Olivier Couet (package X11INT). #include "RStipples.h" #include "GuiTypes.h" +#include "gifencode.h" +#include "gifdecode.h" + // DND protocol version #define XDND_PROTOCOL_VERSION 5 #ifndef IDC_HAND @@ -4313,61 +4316,44 @@ void TGWin32::WritePixmap(int wid, unsigned int w, unsigned int h, // XWriteBitmapFile(fDisplay,pxname,(Pixmap)gTws->drawing,wval,hval,-1,-1); } +class TWin32GifEncode : public TGifEncode { + private: + GdkImage *fImage = nullptr; + protected: + void get_scline(int y, int width, unsigned char *buf) override + { + for (int x = 0; x < width; x++) { + ULong_t pixel = GetPixelImage((Drawable_t)fImage, x, y); + buf[x] = 0; + + auto iter = std::find(orgcolors.begin(), orgcolors.end(), pixel); + if (iter != orgcolors.end()) { + auto idx = iter - orgcolors.begin(); + if (idx < 256) + buf[x] = (unsigned char) idx; + } + } + } + public: + std::vector orgcolors; -// -// Functions for GIFencode() -// - -static FILE *gGifFile; // output unit used WriteGIF and PutByte -static GdkImage *gGifImage = 0; // image used in WriteGIF and GetPixel - -extern "C" { - int GIFquantize(UInt_t width, UInt_t height, Int_t * ncol, Byte_t * red, - Byte_t * green, Byte_t * blue, Byte_t * outputBuf, - Byte_t * outputCmap); - long GIFencode(int Width, int Height, Int_t Ncol, Byte_t R[], - Byte_t G[], Byte_t B[], Byte_t ScLine[], - void (*get_scline) (int, int, Byte_t *), - void (*pb) (Byte_t)); - int GIFdecode(Byte_t * GIFarr, Byte_t * PIXarr, int *Width, int *Height, - int *Ncols, Byte_t * R, Byte_t * G, Byte_t * B); - int GIFinfo(Byte_t * GIFarr, int *Width, int *Height, int *Ncols); -} - - -//////////////////////////////////////////////////////////////////////////////// -/// Get pixels in line y and put in array scline. - -static void GetPixel(int y, int width, Byte_t * scline) -{ - for (int i = 0; i < width; i++) { - scline[i] = Byte_t(GetPixelImage((Drawable_t)gGifImage, i, y)); - } -} - -//////////////////////////////////////////////////////////////////////////////// -/// Put byte b in output stream. + TWin32GifEncode(GdkImage *image) : fImage(image) {} +}; -static void PutByte(Byte_t b) -{ - if (ferror(gGifFile) == 0) fputc(b, gGifFile); -} //////////////////////////////////////////////////////////////////////////////// /// Writes the current window into GIF file. Int_t TGWin32::WriteGIF(char *name) { - Byte_t scline[2000], r[256], b[256], g[256]; - - if (gGifImage) { - gdk_image_unref(gGifImage); + GdkImage *image = gdk_image_get((GdkDrawable*)gCws->drawing, 0, 0, + gCws->width, gCws->height); + if (!image) { + Error("WriteGIF", "Cannot create image for writing GIF. Try in batch mode."); + return 0; } - gGifImage = gdk_image_get((GdkDrawable*)gCws->drawing, 0, 0, - gCws->width, gCws->height); - if (!gGifImage) - return 0; + TWin32GifEncode gif(image); /// Collect R G B of colors of the palette used by the image. /// The image pixels are changed to index values in these R G B arrays. @@ -4376,40 +4362,39 @@ Int_t TGWin32::WriteGIF(char *name) /// contains no more than 256 different colors). If it does contain more /// colors we will have to use GIFquantize to reduce the number of colors. - std::vector orgcolors; - // collect different image colors for (UInt_t x = 0; x < gCws->width; x++) { for (UInt_t y = 0; y < gCws->height; y++) { - ULong_t pixel = GetPixelImage((Drawable_t)gGifImage, x, y); - if (std::find(orgcolors.begin(), orgcolors.end(), pixel) == orgcolors.end()) - orgcolors.emplace_back(pixel); + ULong_t pixel = GetPixelImage((Drawable_t)image, x, y); + if (std::find(gif.orgcolors.begin(), gif.orgcolors.end(), pixel) == gif.orgcolors.end()) + gif.orgcolors.emplace_back(pixel); } } - if (orgcolors.size() > 256) { + auto ncolors = gif.orgcolors.size(); + + if (ncolors > 256) { Error("WriteGIF", "can not create GIF of image containing more than 256 colors"); - gdk_image_unref(gGifImage); - gGifImage = nullptr; + gdk_image_unref(image); return 0; } // get RGB values belonging to pixels - std::vector xcol(orgcolors.size()); + std::vector xcol(ncolors); - for (std::size_t i = 0; i < orgcolors.size(); i++) { - xcol[i].pixel = orgcolors[i]; + for (std::size_t i = 0; i < ncolors; i++) { + xcol[i].pixel = gif.orgcolors[i]; xcol[i].red = GetRValue(xcol[i].pixel); xcol[i].green = GetGValue(xcol[i].pixel); xcol[i].blue = GetBValue(xcol[i].pixel); } 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_query_colors(cc, xcol.data(), ncolors); gdk_color_context_free(cc); - UChar_t maxcol = 0; - for (std::size_t i = 0; i < orgcolors.size(); i++) { + UShort_t maxcol = 0; + for (std::size_t i = 0; i < ncolors; i++) { maxcol = TMath::Max(maxcol, xcol[i].red); maxcol = TMath::Max(maxcol, xcol[i].green); maxcol = TMath::Max(maxcol, xcol[i].blue); @@ -4417,37 +4402,25 @@ Int_t TGWin32::WriteGIF(char *name) if (maxcol == 0) maxcol = 255; - // update image with indices (pixels) into the new RGB colormap - for (UInt_t x = 0; x < gCws->width; x++) { - for (UInt_t y = 0; y < gCws->height; y++) { - ULong_t pixel = GetPixelImage((Drawable_t)gGifImage, x, y); - auto iter = std::find(orgcolors.begin(), orgcolors.end(), pixel); - if (iter != orgcolors.end()) { - auto idx = iter - orgcolors.begin(); - PutPixel((Drawable_t)gGifImage, x, y, idx); - } - } - } + std::vector r(ncolors), b(ncolors), g(ncolors); - for (std::size_t i = 0; i < orgcolors.size(); i++) { - r[i] = xcol[i].red * 255 / maxcol; - g[i] = xcol[i].green * 255 / maxcol; - b[i] = xcol[i].blue * 255 / maxcol; + for (std::size_t i = 0; i < ncolors; i++) { + r[i] = (unsigned char) (xcol[i].red * 255 / maxcol); + g[i] = (unsigned char) (xcol[i].green * 255 / maxcol); + b[i] = (unsigned char) (xcol[i].blue * 255 / maxcol); } Int_t ret = 0; - gGifFile = fopen(name, "wb"); - if (gGifFile) { - GIFencode(gCws->width, gCws->height, - orgcolors.size(), r, g, b, scline, ::GetPixel, PutByte); - fclose(gGifFile); - ret = 1; + if (gif.OpenFile(name)) { + auto len = gif.GIFencode(gCws->width, gCws->height, ncolors, r.data(), g.data(), b.data()); + if (len > 0) + ret = 1; + gif.CloseFile(); } else { - Error("WriteGIF","cannot write file: %s",name); + Error("WriteGIF", "cannot write file: %s",name); } - gdk_image_unref(gGifImage); - gGifImage = nullptr; + gdk_image_unref(image); return ret; } @@ -4539,14 +4512,13 @@ void TGWin32::PutImage(Int_t offset, Int_t itran, Int_t x0, Int_t y0, Int_t nx, Pixmap_t TGWin32::ReadGIF(int x0, int y0, const char *file, Window_t id) { - FILE *fd; Seek_t filesize; unsigned char *GIFarr, *PIXarr, R[256], G[256], B[256], *j1, *j2, icol; int i, j, k, width, height, ncolor, irep, offset; float rr, gg, bb; Pixmap_t pic = 0; - fd = fopen(file, "r+b"); + FILE *fd = fopen(file, "r+b"); if (!fd) { Error("ReadGIF", "unable to open GIF file"); return pic; @@ -4570,7 +4542,7 @@ Pixmap_t TGWin32::ReadGIF(int x0, int y0, const char *file, Window_t id) } fclose(fd); - irep = GIFinfo(GIFarr, &width, &height, &ncolor); + irep = TGifDecode::GIFinfo(GIFarr, &width, &height, &ncolor); if (irep != 0) { return pic; } @@ -4580,7 +4552,9 @@ Pixmap_t TGWin32::ReadGIF(int x0, int y0, const char *file, Window_t id) return pic; } - irep = GIFdecode(GIFarr, PIXarr, &width, &height, &ncolor, R, G, B); + TGifDecode gif; + + irep = gif.GIFdecode(GIFarr, PIXarr, &width, &height, &ncolor, R, G, B); if (irep != 0) { return pic; } @@ -4608,12 +4582,15 @@ Pixmap_t TGWin32::ReadGIF(int x0, int y0, const char *file, Window_t id) } } - if (id) pic = CreatePixmap(id, width, height); + if (id) + pic = CreatePixmap(id, width, height); PutImage(offset, -1, x0, y0, width, height, 0, 0, width-1, height-1, PIXarr, pic); - if (pic) return pic; - else if (gCws->drawing) return (Pixmap_t)gCws->drawing; - else return 0; + if (pic) + return pic; + if (gCws->drawing) + return (Pixmap_t)gCws->drawing; + return 0; } //////////////////////////// GWin32Gui ////////////////////////////////////////// diff --git a/graf2d/win32gdk/src/gifdecode.c b/graf2d/win32gdk/src/gifdecode.c deleted file mode 100644 index 73685c90e867d..0000000000000 --- a/graf2d/win32gdk/src/gifdecode.c +++ /dev/null @@ -1,301 +0,0 @@ -/* @(#)root/win32gdk:$Id$ */ -/* Author: Rene Brun 11/06/97*/ -#include -#include - - -#define BITS 12 /* largest code size */ -#define TSIZE 4096 /* tables size */ - -typedef unsigned char byte; - -static int Prefix[TSIZE]; /* prefix table */ -static byte Suffix[TSIZE]; /* suffix table */ -static byte OutCode[TSIZE]; /* output stack */ - -static byte *ptr1, /* pointer to GIF array */ - *ptr2; /* pointer to PIX array */ - -static int CurCodeSize, /* current number of bits per code */ - CurMaxCode; /* maximum code, given CurCodeSize */ - -static long CurBit; /* current bit in GIF image data */ - -/*************************************************************** - * * - ***************************************************************/ -static int ReadCode() -{ - static long b3[3], CurByte; - static byte lblk; - int shift, nbyte; - long OldByte; - - if (CurBit == -1) { - lblk = 0; - CurByte = -1; - } - - CurBit += CurCodeSize; - OldByte = CurByte; - CurByte = CurBit/8; - nbyte = CurByte - OldByte; - shift = 17 + (CurBit%8) - CurCodeSize; - while (nbyte-- > 0) { - if (lblk == 0) { - lblk = *ptr1++; - if (lblk == 0) return -1; - } - b3[0] = b3[1]; - b3[1] = b3[2]; - b3[2] = *ptr1++; - lblk--; - } - return (((b3[0]+0x100*b3[1]+0x10000*b3[2])>>shift) & (CurMaxCode-1)); -} - -/*************************************************************** - * * - ***************************************************************/ -static void OutPixel(byte pix) -{ - *ptr2++ = pix; -} - -/*************************************************************** - * * - * Name: GIFinfo Date: 03.10.94 * - * * - * Function: Get information on GIF image * - * * - * Input: GIFarr[] - compressed image in GIF format * - * * - * Output: Width - image width * - * Height - image height * - * Ncols - number of colors * - * return - 0 - if O.K. * - * 1 - if error * - * * - ***************************************************************/ -int GIFinfo(byte *GIFarr, int *Width, int *Height, int *Ncols) -{ - byte b; - - ptr1 = GIFarr; - - /* R E A D H E A D E R */ - - if (strncmp((char *)GIFarr,"GIF87a",6) && strncmp((char *)GIFarr,"GIF89a",6)) - { - fprintf(stderr,"\nGIFinfo: not a GIF\n"); - return 1; - } - - ptr1 += 6; - - ptr1 += 2; /* screen width ... ignore */ - ptr1 += 2; /* screen height ... ignore */ - - b = *ptr1++; - *Ncols = 1 << ((b & 7) + 1); - if ((b & 0x80) == 0) { /* is there color map? */ - fprintf(stderr,"\nGIFinfo: warning! no color map\n"); - *Ncols = 0; - } - - ++ptr1; /* background color ... ignore */ - b = *ptr1++; /* supposed to be NULL */ - if (b) { - fprintf(stderr,"\nGIFdecode: bad screen descriptor\n"); - return 1; - } - - ptr1 += (*Ncols) * 3; /* skip color map */ - - b = *ptr1++; /* image separator */ - if (b != ',') { - fprintf(stderr,"\nGIFinfo: no image separator\n"); - return 1; - } - - ptr1 += 2; /* left offset ... ignore */ - ptr1 += 2; /* top offset ... ignore */ - b = *ptr1++; /* image width */ - *Width = b + 0x100*(*ptr1++); - b = *ptr1++; /* image height */ - *Height = b + 0x100*(*ptr1++); - return 0; -} - -/*************************************************************** - * * - * Name: GIFdecode Date: 06.10.92 * - * * - * Function: Decode image from GIF array * - * * - * Input: GIFarr[] - compressed image in GIF format * - * * - * Output: PIXarr[] - image (byte per pixel) * - * Width - image width * - * Height - image height * - * Ncols - number of colors * - * R[] - red components * - * G[] - green components * - * B[] - blue components * - * return - 0 - if O.K. * - * 1 - if error * - * * - ***************************************************************/ -int GIFdecode(byte *GIFarr, byte *PIXarr, int *Width, int *Height, int *Ncols, byte *R, byte *G, byte *B) -{ - byte b, /* working variable */ - FinChar; /* final character */ - - int i, /* working variable for loops */ - BitsPixel, /* number of bits per pixel */ - IniCodeSize, /* initial number of bits per code */ - ClearCode, /* reset code */ - EOFCode, /* end of file code */ - FreeCode, /* first unused entry */ - CurCode, /* current code */ - InCode, /* input code */ - OldCode, /* previous code */ - PixMask, /* mask for pixel */ - OutCount; /* output stack counter */ - - long Npix; /* number of pixels */ - - ptr1 = GIFarr; - ptr2 = PIXarr; - OldCode = 0; - FinChar = 0; - - /* R E A D H E A D E R */ - if (strncmp((char *)GIFarr,"GIF87a",6) && strncmp((char *)GIFarr,"GIF89a",6)) - { - fprintf(stderr,"\nGIFinfo: not a GIF\n"); - return 1; - } - - ptr1 += 6; - - ptr1 += 2; /* screen width ... ignore */ - ptr1 += 2; /* screen height ... ignore */ - - b = *ptr1++; - BitsPixel = (b & 7) + 1; /* # of bits per pixel */ - *Ncols = 1 << BitsPixel; - PixMask = (*Ncols) - 1; /* mask for pixel code */ - if ((b & 0x80) == 0) { /* is there color map? */ - fprintf(stderr,"\nGIFdecode: warning! no color map\n"); - *Ncols = 0; - } - - ++ptr1; /* background color ... ignore */ - b = *ptr1++; /* supposed to be NULL */ - if (b) { - fprintf(stderr,"\nGIFdecode: bad screen descriptor\n"); - return 1; - } - - for (i=0; i<(*Ncols); i++) { /* global color map */ - R[i] = *ptr1++; - G[i] = *ptr1++; - B[i] = *ptr1++; - } - - b = *ptr1++; /* image separator */ - if (b != ',') { - fprintf(stderr,"\nGIFdecode: no image separator\n"); - return 1; - } - - ptr1 += 2; /* left offset ... ignore */ - ptr1 += 2; /* top offset ... ignore */ - b = *ptr1++; /* image width */ - *Width = b + 0x100*(*ptr1++); - b = *ptr1++; /* image height */ - *Height = b + 0x100*(*ptr1++); - - b = *ptr1++; /* local colors, interlace */ - if ((b & 0xc0) != 0) { - fprintf(stderr, - "\nGIFdecode: unexpected item (local colors or interlace)\n"); - return 1; - } - - IniCodeSize = *ptr1++; - CurCodeSize = ++IniCodeSize; - CurMaxCode = (1 << IniCodeSize); - ClearCode = (1 << (IniCodeSize - 1)); - EOFCode = ClearCode + 1; - FreeCode = ClearCode + 2; - - /* D E C O D E I M A G E */ - - Npix =(long) (*Width) * (*Height); - OutCount = 0; - CurBit = -1; - CurCode = ReadCode(); - while (Npix > 0) { - - if (CurCode < 0) { - fprintf(stderr,"\nGIFdecode: corrupted GIF (zero block length)\n"); - return 1; - } - - if (CurCode == EOFCode) { - fprintf(stderr,"\nGIFdecode: corrupted GIF (unexpected EOF)\n"); - return 1; - } - - if (CurCode == ClearCode) { /* clear code ... reset */ - - CurCodeSize = IniCodeSize; - CurMaxCode = (1 << IniCodeSize); - FreeCode = ClearCode + 2; - OldCode = CurCode = ReadCode(); - FinChar = CurCode; - OutPixel(FinChar); - Npix--; - - } else { /* image code */ - - InCode = CurCode; - if (CurCode >= FreeCode) { - CurCode = OldCode; - OutCode[OutCount++] = FinChar; - } - while (CurCode > PixMask) { /* build output pixel chain */ - if (OutCount >= TSIZE) { - fprintf(stderr,"\nGIFdecode: corrupted GIF (big output count)\n"); - return 1; - } - OutCode[OutCount++] = Suffix[CurCode]; - CurCode = Prefix[CurCode]; - } - FinChar = CurCode; - OutCode[OutCount++] = FinChar; - - for (i=OutCount-1; i>=0; i--) { /* put out pixel chain */ - OutPixel(OutCode[i]); - Npix--; - } - OutCount = 0; - - Prefix[FreeCode] = OldCode; /* build the tables */ - Suffix[FreeCode] = FinChar; - OldCode = InCode; - - FreeCode++; /* move pointer */ - if (FreeCode >= CurMaxCode) { - if (CurCodeSize < BITS) { - CurCodeSize++; - CurMaxCode *= 2; - } - } - } - CurCode = ReadCode(); - } - return 0; -} diff --git a/graf2d/win32gdk/src/gifencode.c b/graf2d/win32gdk/src/gifencode.c deleted file mode 100644 index 2bb7a437f6f4b..0000000000000 --- a/graf2d/win32gdk/src/gifencode.c +++ /dev/null @@ -1,306 +0,0 @@ -/* @(#)root/win32gdk:$Id$ */ -/* Author: E.Chernyaev 19/01/94*/ -#include -#include -#include - -#ifdef __STDC__ -#define ARGS(alist) alist -#else -#define ARGS(alist) () -#endif - -#define BITS 12 /* largest code size */ -#define THELIMIT 4096 /* NEVER generate this */ -#define HSIZE 5003 /* hash table size */ -#define SHIFT 4 /* shift for hashing */ - -#define put_byte(A) (*put_b)((byte)(A)); Nbyte++ - -typedef unsigned char byte; - -static long HashTab [HSIZE]; /* hash table */ -static int CodeTab [HSIZE]; /* code table */ - -static int BitsPixel, /* number of bits per pixel */ - IniCodeSize, /* initial number of bits per code */ - CurCodeSize, /* current number of bits per code */ - CurMaxCode, /* maximum code, given CurCodeSize */ - ClearCode, /* reset code */ - EOFCode, /* end of file code */ - FreeCode; /* first unused entry */ - -static long Nbyte; -static void (*put_b) ARGS((byte)); - -static void output ARGS((int)); -static void char_init(); -static void char_out ARGS((int)); -static void char_flush(); -static void put_short ARGS((int)); - -/*********************************************************************** - * * - * Name: GIFencode Date: 02.10.92 * - * Author: E.Chernyaev (IHEP/Protvino) Revised: * - * * - * Function: GIF compression of the image * - * * - * Input: Width - image width (must be >= 8) * - * Height - image height (must be >= 8) * - * Ncol - number of colors * - * R[] - red components * - * G[] - green components * - * B[] - blue components * - * ScLine[] - array for scan line (byte per pixel) * - * get_scline - user routine to read scan line: * - * get_scline(y, Width, ScLine) * - * pb - user routine for "put_byte": pb(b) * - * * - * Return: size of GIF * - * * - ***********************************************************************/ -long GIFencode(Width, Height, Ncol, R, G, B, ScLine, get_scline, pb) - int Width, Height, Ncol; - byte R[], G[], B[], ScLine[]; - void (*get_scline) ARGS((int, int, byte *)), (*pb) ARGS((byte)); -{ - long CodeK; - int ncol, i, x, y, disp, Code, K; - - /* C H E C K P A R A M E T E R S */ - - Code = 0; - if (Width <= 0 || Width > 4096 || Height <= 0 || Height > 4096) { - fprintf(stderr, - "\nGIFencode: incorrect image size: %d x %d\n", Width, Height); - return 0; - } - - if (Ncol <= 0 || Ncol > 256) { - fprintf(stderr,"\nGIFencode: wrong number of colors: %d\n", Ncol); - return 0; - } - - /* I N I T I A L I S A T I O N */ - - put_b = pb; - Nbyte = 0; - char_init(); /* initialise "char_..." routines */ - - /* F I N D # O F B I T S P E R P I X E L */ - - BitsPixel = 1; - if (Ncol > 2) BitsPixel = 2; - if (Ncol > 4) BitsPixel = 3; - if (Ncol > 8) BitsPixel = 4; - if (Ncol > 16) BitsPixel = 5; - if (Ncol > 32) BitsPixel = 6; - if (Ncol > 64) BitsPixel = 7; - if (Ncol > 128) BitsPixel = 8; - - ncol = 1 << BitsPixel; - IniCodeSize = BitsPixel; - if (BitsPixel <= 1) IniCodeSize = 2; - - /* W R I T E H E A D E R */ - - put_byte('G'); /* magic number: GIF87a */ - put_byte('I'); - put_byte('F'); - put_byte('8'); - put_byte('7'); - put_byte('a'); - - put_short(Width); /* screen size */ - put_short(Height); - - K = 0x80; /* yes, there is a color map */ - K |= (8-1)<<4; /* OR in the color resolution */ - K |= (BitsPixel - 1); /* OR in the # of bits per pixel */ - put_byte(K); - - put_byte(0); /* background color */ - put_byte(0); /* future expansion byte */ - - for (i=0; i 0) /* try again */ - goto PROBE; - -NOMATCH: - output(Code); /* full code not found */ - Code = K; - - if (FreeCode < THELIMIT) { - CodeTab[i] = FreeCode++; /* code -> hashtable */ - HashTab[i] = CodeK; - } - else - output(ClearCode); - } - } - /* O U T P U T T H E R E S T */ - - output(Code); - output(EOFCode); - put_byte(0); /* zero-length packet (EOF) */ - put_byte(';'); /* GIF file terminator */ - - return (Nbyte); -} - -static unsigned long cur_accum; -static int cur_bits; -static int a_count; -static char accum[256]; -static unsigned long masks[] = { 0x0000, - 0x0001, 0x0003, 0x0007, 0x000F, - 0x001F, 0x003F, 0x007F, 0x00FF, - 0x01FF, 0x03FF, 0x07FF, 0x0FFF, - 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; - -/*************************************************************** - * * - * Name: output Date: 02.10.92 * - * * - * Function: output GIF code * - * * - * Input: code - GIF code * - * * - ***************************************************************/ -static void output(code) - int code; -{ - /* O U T P U T C O D E */ - - cur_accum &= masks[cur_bits]; - if (cur_bits > 0) - cur_accum |= ((long)code << cur_bits); - else - cur_accum = code; - cur_bits += CurCodeSize; - while( cur_bits >= 8 ) { - char_out( (unsigned int) (cur_accum & 0xFF) ); - cur_accum >>= 8; - cur_bits -= 8; - } - - /* R E S E T */ - - if (code == ClearCode ) { - memset((char *) HashTab, -1, sizeof(HashTab)); - FreeCode = ClearCode + 2; - CurCodeSize = IniCodeSize; - CurMaxCode = (1 << (IniCodeSize)) - 1; - } - - /* I N C R E A S E C O D E S I Z E */ - - if (FreeCode > CurMaxCode ) { - CurCodeSize++; - if ( CurCodeSize == BITS ) - CurMaxCode = THELIMIT; - else - CurMaxCode = (1 << (CurCodeSize)) - 1; - } - - /* E N D O F F I L E : write the rest of the buffer */ - - if( code == EOFCode ) { - while( cur_bits > 0 ) { - char_out( (unsigned int)(cur_accum & 0xff) ); - cur_accum >>= 8; - cur_bits -= 8; - } - char_flush(); - } -} - -static void char_init() -{ - a_count = 0; - cur_accum = 0; - cur_bits = 0; -} - -static void char_out(c) - int c; -{ - accum[a_count++] = c; - if (a_count >= 254) - char_flush(); -} - -static void char_flush() -{ - int i; - - if (a_count == 0) return; - put_byte(a_count); - for (i=0; i>8) & 0xFF); -} diff --git a/graf2d/win32gdk/src/gifquantize.c b/graf2d/win32gdk/src/gifquantize.c deleted file mode 100644 index c1c8b175ed48b..0000000000000 --- a/graf2d/win32gdk/src/gifquantize.c +++ /dev/null @@ -1,301 +0,0 @@ -/* @(#)root/win32gdk:$Id$ */ -/* Author: Fons Rademakers 04/11/98*/ -/***************************************************************************** -* Module to quantize high resolution image into lower one. You may want to * -* peek into the following article this code is based on: * -* "Color Image Quantization for frame buffer Display", by Paul Heckbert * -* SIGGRAPH 1982 page 297-307. * -*****************************************************************************/ - -#include -#include - -typedef unsigned char byte; -typedef struct GifColorType { - byte Red, Green, Blue; -} GifColorType; - -#define ABS(x) ((x) > 0 ? (x) : (-(x))) - -#define GIF_ERROR 0 -#define GIF_OK 1 - -/* The colors are stripped to 5 bits per primary color */ -#define COLOR_ARRAY_SIZE 32768 -#define BITS_PER_PRIM_COLOR 5 -#define MAX_PRIM_COLOR 0x1f - - -static int SortRGBAxis; - -typedef struct QuantizedColorType { - byte RGB[3]; - byte NewColorIndex; - long Count; - struct QuantizedColorType *Pnext; -} QuantizedColorType; - -typedef struct NewColorMapType { - byte RGBMin[3], RGBWidth[3]; - unsigned int NumEntries; /* # of QuantizedColorType in linked list below. */ - long Count; /* Total number of pixels in all the entries. */ - QuantizedColorType *QuantizedColors; -} NewColorMapType; - -static int SubdivColorMap(NewColorMapType *NewColorSubdiv, - unsigned int ColorMapSize, - unsigned int *NewColorMapSize); -static int SortCmpRtn(const void *Entry1, const void *Entry2); - - -/****************************************************************************** -* Quantize high resolution image into lower one. Input image consists of a * -* 2D array for each of the RGB colors with size Width by Height. There is no * -* Color map for the input. Output is a quantized image with 2D array of * -* indexes into the output color map. * -* Note input image can be 24 bits at the most (8 for red/green/blue) and * -* the output has 256 colors at the most (256 entries in the color map.). * -* ColorMapSize specifies size of color map up to 256 and will be updated to * -* real size before returning. * -* Also non of the parameter are allocated by this routine. * -* This function returns GIF_OK if successful, GIF_ERROR otherwise. * -******************************************************************************/ -int GIFquantize(unsigned int Width, unsigned int Height, int *ColorMapSize, - byte *RedInput, byte *GreenInput, byte *BlueInput, - byte *OutputBuffer, GifColorType *OutputColorMap) -{ - unsigned int Index, NumOfEntries, newsize; - int i, j, MaxRGBError[3]; - int NewColorMapSize; - long Red, Green, Blue; - NewColorMapType NewColorSubdiv[256]; - QuantizedColorType *ColorArrayEntries, *QuantizedColor; - - if ((ColorArrayEntries = (QuantizedColorType *) - malloc(sizeof(QuantizedColorType) * COLOR_ARRAY_SIZE)) == NULL) { - fprintf(stderr, "QuantizeBuffer: not enough memory\n"); - return GIF_ERROR; - } - - for (i = 0; i < COLOR_ARRAY_SIZE; i++) { - ColorArrayEntries[i].RGB[0]= i >> (2 * BITS_PER_PRIM_COLOR); - ColorArrayEntries[i].RGB[1] = (i >> BITS_PER_PRIM_COLOR) & MAX_PRIM_COLOR; - ColorArrayEntries[i].RGB[2] = i & MAX_PRIM_COLOR; - ColorArrayEntries[i].Count = 0; - } - - /* Sample the colors and their distribution: */ - for (i = 0; i < (int)(Width * Height); i++) { - Index = ((RedInput[i] >> (8 - BITS_PER_PRIM_COLOR)) - << (2 * BITS_PER_PRIM_COLOR)) + - ((GreenInput[i] >> (8 - BITS_PER_PRIM_COLOR)) - << BITS_PER_PRIM_COLOR) + - (BlueInput[i] >> (8 - BITS_PER_PRIM_COLOR)); - ColorArrayEntries[Index].Count++; - } - - /* Put all the colors in the first entry of the color map, and call the */ - /* recursive subdivision process. */ - for (i = 0; i < 256; i++) { - NewColorSubdiv[i].QuantizedColors = NULL; - NewColorSubdiv[i].Count = NewColorSubdiv[i].NumEntries = 0; - for (j = 0; j < 3; j++) { - NewColorSubdiv[i].RGBMin[j] = 0; - NewColorSubdiv[i].RGBWidth[j] = 255; - } - } - - /* Find the non empty entries in the color table and chain them: */ - for (i = 0; i < COLOR_ARRAY_SIZE; i++) - if (ColorArrayEntries[i].Count > 0) break; - QuantizedColor = NewColorSubdiv[0].QuantizedColors = &ColorArrayEntries[i]; - NumOfEntries = 1; - while (++i < COLOR_ARRAY_SIZE) - if (ColorArrayEntries[i].Count > 0) { - QuantizedColor -> Pnext = &ColorArrayEntries[i]; - QuantizedColor = &ColorArrayEntries[i]; - NumOfEntries++; - } - QuantizedColor -> Pnext = NULL; - - NewColorSubdiv[0].NumEntries = NumOfEntries;/* Different sampled colors. */ - NewColorSubdiv[0].Count = ((long) Width) * Height; /* Pixels. */ - newsize = 1; - if (SubdivColorMap(NewColorSubdiv, *ColorMapSize, &newsize) != GIF_OK) { - free((char *) ColorArrayEntries); - return GIF_ERROR; - } - NewColorMapSize = (int)newsize; - if (NewColorMapSize < *ColorMapSize) { - /* And clear rest of color map: */ - for (i = NewColorMapSize; i < *ColorMapSize; i++) - OutputColorMap[i].Red = - OutputColorMap[i].Green = - OutputColorMap[i].Blue = 0; - } - - /* Average the colors in each entry to be the color to be used in the */ - /* output color map, and plug it into the output color map itself. */ - for (i = 0; i < NewColorMapSize; i++) { - if ((j = NewColorSubdiv[i].NumEntries) > 0) { - QuantizedColor = NewColorSubdiv[i].QuantizedColors; - Red = Green = Blue = 0; - while (QuantizedColor) { - QuantizedColor -> NewColorIndex = i; - Red += QuantizedColor -> RGB[0]; - Green += QuantizedColor -> RGB[1]; - Blue += QuantizedColor -> RGB[2]; - QuantizedColor = QuantizedColor -> Pnext; - } - OutputColorMap[i].Red = (byte)((Red << (8 - BITS_PER_PRIM_COLOR)) / j); - OutputColorMap[i].Green = (byte)((Green << (8 - BITS_PER_PRIM_COLOR)) / j); - OutputColorMap[i].Blue= (byte)((Blue << (8 - BITS_PER_PRIM_COLOR)) / j); - } - else - fprintf(stderr, "Null entry in quantized color map - thats weird."); - } - - /* Finally scan the input buffer again and put the mapped index in the */ - /* output buffer. */ - MaxRGBError[0] = MaxRGBError[1] = MaxRGBError[2] = 0; - for (i = 0; i < (int)(Width * Height); i++) { - Index = ((RedInput[i] >> (8 - BITS_PER_PRIM_COLOR)) - << (2 * BITS_PER_PRIM_COLOR)) + - ((GreenInput[i] >> (8 - BITS_PER_PRIM_COLOR)) - << BITS_PER_PRIM_COLOR) + - (BlueInput[i] >> (8 - BITS_PER_PRIM_COLOR)); - Index = ColorArrayEntries[Index].NewColorIndex; - OutputBuffer[i] = Index; - if (MaxRGBError[0] < ABS(OutputColorMap[Index].Red - RedInput[i])) - MaxRGBError[0] = ABS(OutputColorMap[Index].Red - RedInput[i]); - if (MaxRGBError[1] < ABS(OutputColorMap[Index].Green - GreenInput[i])) - MaxRGBError[1] = ABS(OutputColorMap[Index].Green - GreenInput[i]); - if (MaxRGBError[2] < ABS(OutputColorMap[Index].Blue - BlueInput[i])) - MaxRGBError[2] = ABS(OutputColorMap[Index].Blue - BlueInput[i]); - } - -#ifdef DEBUG - fprintf(stderr, - "Quantization L(0) errors: Red = %d, Green = %d, Blue = %d.\n", - MaxRGBError[0], MaxRGBError[1], MaxRGBError[2]); -#endif /* DEBUG */ - - free((char *) ColorArrayEntries); - - *ColorMapSize = NewColorMapSize; - - return GIF_OK; -} - -/****************************************************************************** -* Routine to subdivide the RGB space recursively using median cut in each * -* axes alternatingly until ColorMapSize different cubes exists. * -* The biggest cube in one dimension is subdivide unless it has only one entry.* -* Returns GIF_ERROR if failed, otherwise GIF_OK. * -******************************************************************************/ -static int SubdivColorMap(NewColorMapType *NewColorSubdiv, - unsigned int ColorMapSize, - unsigned int *NewColorMapSize) -{ - int MaxSize; - unsigned int i, j, Index = 0, NumEntries, MinColor, MaxColor; - long Sum, Count; - QuantizedColorType *QuantizedColor, **SortArray; - - while (ColorMapSize > *NewColorMapSize) { - /* Find candidate for subdivision: */ - MaxSize = -1; - for (i = 0; i < *NewColorMapSize; i++) { - for (j = 0; j < 3; j++) { - if (((int) NewColorSubdiv[i].RGBWidth[j]) > MaxSize && - NewColorSubdiv[i].NumEntries > 1) { - MaxSize = NewColorSubdiv[i].RGBWidth[j]; - Index = i; - SortRGBAxis = j; - } - } - } - - if (MaxSize == -1) - return GIF_OK; - - /* Split the entry Index into two along the axis SortRGBAxis: */ - - /* Sort all elements in that entry along the given axis and split at */ - /* the median. */ - if ((SortArray = (QuantizedColorType **) - malloc(sizeof(QuantizedColorType *) * - NewColorSubdiv[Index].NumEntries)) == NULL) - return GIF_ERROR; - for (j = 0, QuantizedColor = NewColorSubdiv[Index].QuantizedColors; - j < NewColorSubdiv[Index].NumEntries && QuantizedColor != NULL; - j++, QuantizedColor = QuantizedColor -> Pnext) - SortArray[j] = QuantizedColor; - qsort(SortArray, NewColorSubdiv[Index].NumEntries, - sizeof(QuantizedColorType *), SortCmpRtn); - - /* Relink the sorted list into one: */ - for (j = 0; j < NewColorSubdiv[Index].NumEntries - 1; j++) - SortArray[j] -> Pnext = SortArray[j + 1]; - SortArray[NewColorSubdiv[Index].NumEntries - 1] -> Pnext = NULL; - NewColorSubdiv[Index].QuantizedColors = QuantizedColor = SortArray[0]; - free((char *) SortArray); - - /* Now simply add the Counts until we have half of the Count: */ - Sum = NewColorSubdiv[Index].Count / 2 - QuantizedColor -> Count; - NumEntries = 1; - Count = QuantizedColor -> Count; - while ((Sum -= QuantizedColor -> Pnext -> Count) >= 0 && - QuantizedColor -> Pnext != NULL && - QuantizedColor -> Pnext -> Pnext != NULL) { - QuantizedColor = QuantizedColor -> Pnext; - NumEntries++; - Count += QuantizedColor -> Count; - } - /* Save the values of the last color of the first half, and first */ - /* of the second half so we can update the Bounding Boxes later. */ - /* Also as the colors are quantized and the BBoxes are full 0..255, */ - /* they need to be rescaled. */ - MaxColor = QuantizedColor -> RGB[SortRGBAxis];/* Max. of first half. */ - MinColor = QuantizedColor -> Pnext -> RGB[SortRGBAxis];/* of second. */ - MaxColor <<= (8 - BITS_PER_PRIM_COLOR); - MinColor <<= (8 - BITS_PER_PRIM_COLOR); - - /* Partition right here: */ - NewColorSubdiv[*NewColorMapSize].QuantizedColors = - QuantizedColor -> Pnext; - QuantizedColor -> Pnext = NULL; - NewColorSubdiv[*NewColorMapSize].Count = Count; - NewColorSubdiv[Index].Count -= Count; - NewColorSubdiv[*NewColorMapSize].NumEntries = - NewColorSubdiv[Index].NumEntries - NumEntries; - NewColorSubdiv[Index].NumEntries = NumEntries; - for (j = 0; j < 3; j++) { - NewColorSubdiv[*NewColorMapSize].RGBMin[j] = - NewColorSubdiv[Index].RGBMin[j]; - NewColorSubdiv[*NewColorMapSize].RGBWidth[j] = - NewColorSubdiv[Index].RGBWidth[j]; - } - NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] = - NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] + - NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] - - MinColor; - NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] = MinColor; - - NewColorSubdiv[Index].RGBWidth[SortRGBAxis] = - MaxColor - NewColorSubdiv[Index].RGBMin[SortRGBAxis]; - - (*NewColorMapSize)++; - } - - return GIF_OK; -} - -/****************************************************************************** -* Routine called by qsort to compare to entries. * -******************************************************************************/ -static int SortCmpRtn(const void *Entry1, const void *Entry2) -{ - return (* ((QuantizedColorType **) Entry1)) -> RGB[SortRGBAxis] - - (* ((QuantizedColorType **) Entry2)) -> RGB[SortRGBAxis]; -} From b18c6f6c4b0e9ccb75fb0db12437b2dc6ee1cbee Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Tue, 21 Apr 2026 12:45:09 +0200 Subject: [PATCH 11/15] Add fopen flag for gifencode It can be differ on Windows and X11 --- graf2d/gifencode/gifencode.cxx | 4 ++-- graf2d/gifencode/gifencode.h | 2 +- graf2d/win32gdk/src/TGWin32.cxx | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/graf2d/gifencode/gifencode.cxx b/graf2d/gifencode/gifencode.cxx index 973943097b7d6..3673e679659ed 100644 --- a/graf2d/gifencode/gifencode.cxx +++ b/graf2d/gifencode/gifencode.cxx @@ -20,9 +20,9 @@ void TGifEncode::put_byte(unsigned char b) } } -bool TGifEncode::OpenFile(const char *fname) +bool TGifEncode::OpenFile(const char *fname, const char *opt) { - fOut = fopen(fname, "w+"); + fOut = fopen(fname, opt); return fOut != nullptr; } diff --git a/graf2d/gifencode/gifencode.h b/graf2d/gifencode/gifencode.h index 219f06e438306..0224f98c66149 100644 --- a/graf2d/gifencode/gifencode.h +++ b/graf2d/gifencode/gifencode.h @@ -50,7 +50,7 @@ class TGifEncode { public: virtual ~TGifEncode() { CloseFile(); } - bool OpenFile(const char *fname); + bool OpenFile(const char *fname, const char *opt = "w+"); void CloseFile(); long GIFencode(int Width, int Height, int Ncol, unsigned char *R, unsigned char *G, unsigned char *B); diff --git a/graf2d/win32gdk/src/TGWin32.cxx b/graf2d/win32gdk/src/TGWin32.cxx index 058b66da51750..e3b54bca06192 100644 --- a/graf2d/win32gdk/src/TGWin32.cxx +++ b/graf2d/win32gdk/src/TGWin32.cxx @@ -4412,7 +4412,7 @@ Int_t TGWin32::WriteGIF(char *name) Int_t ret = 0; - if (gif.OpenFile(name)) { + if (gif.OpenFile(name, "wb")) { auto len = gif.GIFencode(gCws->width, gCws->height, ncolors, r.data(), g.data(), b.data()); if (len > 0) ret = 1; From eba05a3b8bb703e9c06e6a11b5bf599f0babf5ca Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Tue, 21 Apr 2026 13:28:44 +0200 Subject: [PATCH 12/15] [virtualx] introduce WriteGIFW Allows to store GIF image for specified window. Provide implementation for TGX11 and TGWin3 --- core/base/inc/TVirtualX.h | 1 + core/base/src/TVirtualX.cxx | 8 ++++++++ graf2d/win32gdk/inc/TGWin32.h | 1 + graf2d/win32gdk/src/TGWin32.cxx | 28 ++++++++++++++++++++-------- graf2d/x11/inc/TGX11.h | 2 ++ graf2d/x11/src/TGX11.cxx | 27 +++++++++++++++++++-------- 6 files changed, 51 insertions(+), 16 deletions(-) diff --git a/core/base/inc/TVirtualX.h b/core/base/inc/TVirtualX.h index de2c1b3c5c99b..70a0884432bec 100644 --- a/core/base/inc/TVirtualX.h +++ b/core/base/inc/TVirtualX.h @@ -123,6 +123,7 @@ class TVirtualX : public TNamed, public TAttLine, public TAttFill, public TAttTe virtual void DrawPolyMarkerW(WinContext_t wctxt, Int_t n, TPoint *xy); virtual void DrawTextW(WinContext_t wctxt, Int_t x, Int_t y, Float_t angle, Float_t mgn, const char *text, ETextMode mode); virtual void DrawTextW(WinContext_t wctxt, Int_t x, Int_t y, Float_t angle, Float_t mgn, const wchar_t *text, ETextMode mode); + virtual Int_t WriteGIFW(WinContext_t wctxt, const char *name); //---- OpenGL related stuff, required only with R__HAS_COCOA ---- diff --git a/core/base/src/TVirtualX.cxx b/core/base/src/TVirtualX.cxx index f4e05e3f07b5f..1fbf1e8616d37 100644 --- a/core/base/src/TVirtualX.cxx +++ b/core/base/src/TVirtualX.cxx @@ -540,6 +540,14 @@ void TVirtualX::DrawTextW(WinContext_t /* wctxt */, Int_t x, Int_t y, Float_t an DrawText(x, y, angle, mgn, text, mode); } +//////////////////////////////////////////////////////////////////////////////// +/// Save specified window as GIF image + +Int_t TVirtualX::WriteGIFW(WinContext_t /* wctxt */, const char *name) +{ + return WriteGIF((char *) name); +} + //////////////////////////////////////////////////////////////////////////////// /// Executes the command "code" coming from the other threads (Win32) diff --git a/graf2d/win32gdk/inc/TGWin32.h b/graf2d/win32gdk/inc/TGWin32.h index 1d00421a4075b..b7f65541241c2 100644 --- a/graf2d/win32gdk/inc/TGWin32.h +++ b/graf2d/win32gdk/inc/TGWin32.h @@ -217,6 +217,7 @@ class TGWin32 : public TVirtualX { 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; + Int_t WriteGIFW(WinContext_t wctxt, const char *name) 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 e3b54bca06192..24c5b47a0994e 100644 --- a/graf2d/win32gdk/src/TGWin32.cxx +++ b/graf2d/win32gdk/src/TGWin32.cxx @@ -4346,10 +4346,22 @@ class TWin32GifEncode : public TGifEncode { Int_t TGWin32::WriteGIF(char *name) { - GdkImage *image = gdk_image_get((GdkDrawable*)gCws->drawing, 0, 0, - gCws->width, gCws->height); + return WriteGIFW((WinContext_t)gCws, name); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Writes the specified window into GIF file. + +Int_t TGWin32::WriteGIFW(WinContext_t wctxt, const char *name) +{ + auto ctxt = (XWindow_t *) wctxt; + if (!ctxt) + return 0; + + GdkImage *image = gdk_image_get((GdkDrawable*)ctxt->drawing, 0, 0, + ctxt->width, ctxt->height); if (!image) { - Error("WriteGIF", "Cannot create image for writing GIF. Try in batch mode."); + Error("WriteGIFW", "Cannot create image for writing GIF. Try in batch mode."); return 0; } @@ -4363,8 +4375,8 @@ Int_t TGWin32::WriteGIF(char *name) /// colors we will have to use GIFquantize to reduce the number of colors. // collect different image colors - for (UInt_t x = 0; x < gCws->width; x++) { - for (UInt_t y = 0; y < gCws->height; y++) { + for (UInt_t x = 0; x < ctxt->width; x++) { + for (UInt_t y = 0; y < ctxt->height; y++) { ULong_t pixel = GetPixelImage((Drawable_t)image, x, y); if (std::find(gif.orgcolors.begin(), gif.orgcolors.end(), pixel) == gif.orgcolors.end()) gif.orgcolors.emplace_back(pixel); @@ -4374,7 +4386,7 @@ Int_t TGWin32::WriteGIF(char *name) auto ncolors = gif.orgcolors.size(); if (ncolors > 256) { - Error("WriteGIF", "can not create GIF of image containing more than 256 colors"); + Error("WriteGIFW", "can not create GIF of image containing more than 256 colors"); gdk_image_unref(image); return 0; } @@ -4413,12 +4425,12 @@ Int_t TGWin32::WriteGIF(char *name) Int_t ret = 0; if (gif.OpenFile(name, "wb")) { - auto len = gif.GIFencode(gCws->width, gCws->height, ncolors, r.data(), g.data(), b.data()); + auto len = gif.GIFencode(ctxt->width, ctxt->height, ncolors, r.data(), g.data(), b.data()); if (len > 0) ret = 1; gif.CloseFile(); } else { - Error("WriteGIF", "cannot write file: %s",name); + Error("WriteGIFW", "cannot write file: %s",name); } gdk_image_unref(image); diff --git a/graf2d/x11/inc/TGX11.h b/graf2d/x11/inc/TGX11.h index 2825eec94a4a5..f3c8af94f29e1 100644 --- a/graf2d/x11/inc/TGX11.h +++ b/graf2d/x11/inc/TGX11.h @@ -234,6 +234,8 @@ friend struct XWindow_t; 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; + Int_t WriteGIFW(WinContext_t wctxt, const char *name) 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 93d02ac308e8b..d0e67b47b32a9 100644 --- a/graf2d/x11/src/TGX11.cxx +++ b/graf2d/x11/src/TGX11.cxx @@ -2837,12 +2837,23 @@ class TX11GifEncode : public TGifEncode { Int_t TGX11::WriteGIF(char *name) { - XImage *image = XGetImage((Display*)fDisplay, gCws->fDrawing, 0, 0, - gCws->fWidth, gCws->fHeight, + return WriteGIFW((WinContext_t) gCws, name); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Writes the specified window into GIF file. Returns 1 in case of success, +/// 0 otherwise. + +Int_t TGX11::WriteGIFW(WinContext_t wctxt, const char *name) +{ + auto ctxt = (XWindow_t *) wctxt; + + XImage *image = XGetImage((Display*)fDisplay, ctxt->fDrawing, 0, 0, + ctxt->fWidth, ctxt->fHeight, AllPlanes, ZPixmap); if (!image) { - Error("WriteGIF", "Cannot create image for writing GIF. Try in batch mode."); + Error("WriteGIFW", "Cannot create image for writing GIF. Try in batch mode."); return 0; } @@ -2856,8 +2867,8 @@ Int_t TGX11::WriteGIF(char *name) TX11GifEncode gif(image); // collect different image colors - for (UInt_t x = 0; x < gCws->fWidth; x++) { - for (UInt_t y = 0; y < gCws->fHeight; y++) { + for (UInt_t x = 0; x < ctxt->fWidth; x++) { + for (UInt_t y = 0; y < ctxt->fHeight; y++) { ULong_t pixel = XGetPixel(image, x, y); if (std::find(gif.orgcolors.begin(), gif.orgcolors.end(), pixel) == gif.orgcolors.end()) gif.orgcolors.emplace_back(pixel); @@ -2867,7 +2878,7 @@ Int_t TGX11::WriteGIF(char *name) auto ncolors = gif.orgcolors.size(); if (ncolors > 256) { - Error("WriteGIF", "Cannot create GIF of image containing more than 256 colors. Try in batch mode."); + Error("WriteGIFW", "Cannot create GIF of image containing more than 256 colors. Try in batch mode."); XDestroyImage(image); return 0; } @@ -2903,12 +2914,12 @@ Int_t TGX11::WriteGIF(char *name) Int_t ret = 0; if (gif.OpenFile(name)) { - auto len = gif.GIFencode(gCws->fWidth, gCws->fHeight, ncolors, r.data(), g.data(), b.data()); + auto len = gif.GIFencode(ctxt->fWidth, ctxt->fHeight, ncolors, r.data(), g.data(), b.data()); if (len > 0) ret = 1; gif.CloseFile(); } else { - Error("WriteGIF", "cannot write file: %s", name); + Error("WriteGIFW", "cannot write file: %s", name); } // cleanup image at the end From ab3c6b7996927e9db10b6b23ce8a54c92adc66bc Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Tue, 21 Apr 2026 14:18:40 +0200 Subject: [PATCH 13/15] [padpainter] use gVirtualX->WriteGIFW to store pad gif image Explicitely take window id and context for the pad which is specified. Before active context was used - which is not guaranteed to be proper one --- graf2d/gpad/src/TPadPainter.cxx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/graf2d/gpad/src/TPadPainter.cxx b/graf2d/gpad/src/TPadPainter.cxx index 8fbffdce21476..c91e838d5bf91 100644 --- a/graf2d/gpad/src/TPadPainter.cxx +++ b/graf2d/gpad/src/TPadPainter.cxx @@ -572,7 +572,10 @@ void TPadPainter::SaveImage(TVirtualPad *pad, const char *fileName, Int_t type) } if (type == TImage::kGif) { - gVirtualX->WriteGIF((char*)fileName); + Int_t wid = (pad == pad->GetCanvas()) ? pad->GetCanvasID() : pad->GetPixmapID(); + auto ctxt = gVirtualX->GetWindowContext(wid); + // TODO: if fail, one can use TImage functionality instead + gVirtualX->WriteGIFW(ctxt, fileName); } else { const std::unique_ptr img(TImage::Create()); if (img.get()) { From 33f3d412f5b7c13568c180bde442a7e0c6a8793f Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Tue, 21 Apr 2026 17:10:23 +0200 Subject: [PATCH 14/15] [tpad] Fix - paint highlight border if color positive Prevent border painting when highlight specially deactivated --- graf2d/gpad/src/TPad.cxx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/graf2d/gpad/src/TPad.cxx b/graf2d/gpad/src/TPad.cxx index 3a0ff72783443..66cb2d9370da6 100644 --- a/graf2d/gpad/src/TPad.cxx +++ b/graf2d/gpad/src/TPad.cxx @@ -3626,8 +3626,10 @@ void TPad::Paint(Option_t * /*option*/) lnk = lnk->Next(); } - if (fCanvas && (fCanvas->fHilightPadBorder == this)) - PaintBorder(-GetHighLightColor(), kTRUE); + if (fCanvas && (fCanvas->fHilightPadBorder == this)) { + auto col = GetHighLightColor(); + if (col > 0) PaintBorder(-col, kTRUE); + } } fPadPaint = 0; From 6fbfda3abff749eab4ea964da9e7c886b80aeef5 Mon Sep 17 00:00:00 2001 From: Sergey Linev Date: Tue, 21 Apr 2026 17:19:42 +0200 Subject: [PATCH 15/15] [tasimage] fix - create two ASVisual objects Prevent situation when ASVisual recreated many times without real need --- graf2d/asimage/src/TASImage.cxx | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/graf2d/asimage/src/TASImage.cxx b/graf2d/asimage/src/TASImage.cxx index dedeff20eb1f7..01a937db61104 100644 --- a/graf2d/asimage/src/TASImage.cxx +++ b/graf2d/asimage/src/TASImage.cxx @@ -2231,8 +2231,6 @@ Bool_t TASImage::InitVisual() if (fgVisual && (noX == fgBatch)) return kTRUE; - if (fgVisual) - destroy_asvisual(fgVisual, kFALSE); fgVisual = nullptr; fgBatch = false; @@ -2244,16 +2242,27 @@ Bool_t TASImage::InitVisual() Visual *vis = (Visual*) gVirtualX->GetVisual(); Colormap cmap = (Colormap) gVirtualX->GetColormap(); - if (vis && cmap) - fgVisual = create_asvisual_for_id(disp, screen, depth, + static ASVisual *vis_x = nullptr; + + if (vis && cmap && !noX) { + if (!vis_x) + vis_x = create_asvisual_for_id(disp, screen, depth, XVisualIDFromVisual(vis), cmap, nullptr); + fgVisual = vis_x; + } #endif #endif + static ASVisual *vis_batch = nullptr; + if (!fgVisual) { - // create dummy fgVisual for batch mode - fgVisual = create_asvisual(nullptr, 0, 0, nullptr); - fgVisual->dpy = nullptr; // fake (not used) + if (!vis_batch) { + // create dummy visual for batch mode + vis_batch = create_asvisual(nullptr, 0, 0, nullptr); + vis_batch->dpy = nullptr; // fake (not used) + } + + fgVisual = vis_batch; fgBatch = true; }