BMP image into TGImageMap

Hello, I wanted to use the TGImageMap class to show and update an image in a ROOT GUI.

The issue is that the image I get is an array of bytes in my code, which is in BMP format. Something like:

const char* image = "ABCXYZHH22";

I was wondering what is the best way to ‘translate’ this information so that I can feed into the TGImageMap. If possible, I would like to update this image every 1 second by using SetPicture() function without having to delete and re-create the class instance, however it seems disabled.

Thanks in advance for the support!

You might check if one of the TGPicturePool::GetPicture() methods could help to solve your problem. Check also the iconAsXPMData.C tutorial. Otherwise please provide something we can play with.

Thanks for the hints, however that would work only for XPM format, whereas my input data is in BMP format. Also, the GetPicture() function creates a new picture, rather than “replacing” the existing one.

I guess that why I am looking for is an equivalent of the function root/bmp.c at master · root-project/root · GitHub
that, instead of taking as argument the filename, it takes as argument an array of chars (bytes). So to say, that it ‘skips’ the part of opening the file, which would save me valuable time in saving the bytes to disk and reading them again.

In the meanwhile, I am using the following approach in my GUI loop:

//save my BMP bytes to HDD at /tmp/screenshot.bmp
delete picture;//delete previously existing picture
picture = new TASImage("/tmp/screenshot.bmp",TImage::kBmp);
canvas()->cd();
picture->Draw();
canvas()->Modified();
canvas()->Update();

I also found this related bug:

Here a minimum example, using the screenshot.bmp file from the bug report above:

#include <fstream>
#include<string>

void test_asimage()
{
    ifstream file("./screenshot.png");
    std::string image(261174, '\0');
    file.read(&image[0], 261174);
    std::cout << image << std::endl;
}

That variable “image” is the data I have (not the file itself).

I’ll try to find something before Sunday

BTW, why not simply using TGPicture *pic = gClient->GetPicture("./screenshot.png") ?

Thanks! No hurries at all, it’s not urgent.

I can’t do the gClient->GetPicture() because the file screenshot.png does not exist on the first place, I just created it myself for the minimal example. In other words, I did:

string image = GetBytesFromDevice();
ofstream f ("screenshot.png",ios::binary);
f << image;
f.close();
//Only now I can use `TGPicture *pic = gClient->GetPicture("./screenshot.png")`

I get the image bytes from a pulse generator sent over USB. Writing to file and reading again is a bit of an overhead, it makes it slow. Because the image sent by the device changes in ‘real time’, I would like to update it more quickly minimizing read-write operations from HDD.

void test_asimage()
{
   char *buffer = new char[553021];
   ifstream file("./yj.png", ios::binary);
   file.read(buffer, 553021);
   TImage *img = TImage::Create();
   img->SetImageBuffer(&buffer, TImage::kPng);
   const TGPicture *pic = gClient->GetPicturePool()->GetPicture("yj_png",
         img->GetPixmap(), img->GetMask());
   TGMainFrame *main = new TGMainFrame(gClient->GetRoot(), 500, 500);
   TGImageMap *imageMap = new TGImageMap(main, pic);
   main->AddFrame(imageMap);
   main->Resize(main->GetDefaultSize());
   main->MapSubwindows();
   main->MapWindow();
}
1 Like

Thanks so much for the reply and example. I ended up doing this to only update when something changed. I used a TRootEmbeddedCanvas in order not to recreate the TGPicture and TGImageMap on every iteration. On the latter, the SetPicture() function looks disabled.

char* fPicBuffer = nullptr;
TImage* fPicture = TImage::Create();
TRootEmbeddedCanvas* fScreenshot = new TRoot...;
//Update while loop:
{
        const std::string image = GetScreenshotBytesFromDevice();
        bool redraw = false;
        if(!fPicBuffer || strlen(fPicBuffer) != image.size())
        {
            delete[] fPicBuffer;
            fPicBuffer = new char[image.size() + 1];
            redraw = true;
        }
        const bool different = redraw || (image.compare(fPicBuffer) != 0);
        if(different)
        {
             //https://stackoverflow.com/questions/347949/how-to-convert-a-stdstring-to-const-char-or-char
            std::copy(image.begin(), image.end(), fPicBuffer);
            fPicBuffer[image.size()] = '\0'; // don't forget the terminating 0
            fPicture->SetImageBuffer(&fPicBuffer,TImage::kPng);
        }
        if(redraw)
        {
            fScreenshot->GetCanvas()->cd();
            fPicture->Draw();
        }
        if(different)
        {
            fScreenshot->GetCanvas()->Modified();
            fScreenshot->GetCanvas()->Update();
        }
    }

Indeed, I was about to ask if you really need a TGImageMap, because it’s much more efficient to use a TImage in a canvas

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.