wxImage and draw raw rgb bytes - c++

I am programming image viewer as school task and I cant use any libraries for reading or manipulating images. First I started with bmp format. I have created class for handle this type of file. As GUI framework I am using wxWidgets.
So I have plain rgb bytes array, prepared for wxImage constructor
wxImage(int width, int height, unsigned char* data, bool static_data = false).
Problem is that when I convert it to wxBitmap and draw to dc it's ignoring rgb values a draw only black picture. I really do not know what could be a problem. This is my code for draw image:
DrawImage(wxDC &dc)
{
BYTE *rgbArray = bmpFile->GetRGB();
wxSize imageSize = wxSize(bmpFile->GetSize().w,bmpFile->GetSize().h);
wxImage image = wxImage(imageSize, &rgbArray);
wxBitmap bitmap = wxBitmap(image);
dc.DrawBitmap(bitmap,5,5, false);
}
And this is on paint event:
void OnPaint(wxPaintEvent& event)
{
wxAutoBufferedPaintDC dc(canvas);
dc.SetBackground( wxBrush(canvas->GetBackgroundColour()));
dc.Clear();
DrawImage(dc);
}
rgbArray is filled with right values, I checked it multiple times.
Thanks for any help :)

Thats because you are probably calling this function, because you are passing a BYTE**.
wxImage (const wxSize &sz, bool clear=true)
to call the other overload, removing the & might help
wxImage image = wxImage(imageSize, rgbArray);
To make the code exception safe, it must be rewritten slightly. I don't know whether bmpFile returns a new buffer or a pointer to its own data buffer. If it doesn't return a new buffer then you must make your own copy because wxImage takes ownership of the buffer. see wxImage
DrawImage(wxDC &dc)
{
wxSize imageSize = wxSize(bmpFile->GetSize().w,bmpFile->GetSize().h);
wxImage image = wxImage(imageSize, bmpFile->GetRGB() );
wxBitmap bitmap = wxBitmap(image);
dc.DrawBitmap(bitmap,5,5, false);
}
or
DrawImage(wxDC &dc)
{
std::unique_ptr<BYTE> rgbData( bmpFile->GetRGB() );
wxSize imageSize = wxSize(bmpFile->GetSize().w,bmpFile->GetSize().h);
wxImage image = wxImage(imageSize, rgbData.release()) );
wxBitmap bitmap = wxBitmap(image);
dc.DrawBitmap(bitmap,5,5, false);
}

Related

wxWidgets: Frame size is not changing in Image panel

I am using wxWidgets 'An Image Panel' code. Here I made only one change.
I want frame size should be equal to image size, My Image size is 762x463, but my frame size is different. frame SetSize function is not working.
wxImagePanel::wxImagePanel(wxFrame* parent, wxString file, wxBitmapType format) :
wxPanel(parent)
{
image.LoadFile(file, format);
}
void wxImagePanel::paintEvent(wxPaintEvent & evt)
{
// depending on your system you may need to look at double-buffered dcs
wxPaintDC dc(this);
render(dc);
}
void wxImagePanel::paintNow()
{
// depending on your system you may need to look at double-buffered dcs
wxClientDC dc(this);
render(dc);
}
void wxImagePanel::render(wxDC& dc)
{
dc.DrawBitmap( image, 0, 0, false );
}
class MyApp: public wxApp
{
wxFrame *frame;
wxImagePanel * drawPane;
public:
bool OnInit()
{
// make sure to call this first
wxInitAllImageHandlers();
wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
wxBitmap image(wxT("properties.png"), wxBITMAP_TYPE_PNG);
frame = new wxFrame(NULL, wxID_ANY, wxT("Hello wxDC"), wxPoint(50,50), wxSize(image.GetWidth(), image.GetHeight())); // 762x463
// then simply create like this
drawPane = new wxImagePanel( frame, wxT("image.jpg"), wxBITMAP_TYPE_JPEG);
sizer->Add(drawPane, 1, wxEXPAND);
frame->SetSizer(sizer);
frame->Show();
return true;
}
};
To make the frame sized to display the image, you need to make 2 changes:
In the constructor wxImagePanel::wxImagePanel, you need to add a line to set a minimum size for the image panel.
wxImagePanel::wxImagePanel(wxFrame* parent, wxString file, wxBitmapType format) :
wxPanel(parent)
{
image.LoadFile(file, format);
SetMinSize(image.GetSize());
...
An alternate, and possibly better, solution would be to override wxWindow::DoGetBestClientSize for your wxImagePanel class and have it return image.GetSize();.
Incidentally, in the code shown above, there is nothing that actually connects the paint handler to the paint event, so you probably also want to add a line like
Bind(wxEVT_PAINT, &wxImagePanel::paintEvent, this);
to the constructor as well.
In the body of MyApp::OnInit, change the line
frame->SetSizer(sizer);
to
frame->SetSizerAndFit(sizer);
Using the SetSizerAndFit method will tell the frame to resize itself to the smallest size necessary to display all of its contents. Since in step 1 the minimum size of the image panel was set to the size of image and the image panel is the only content of the frame, the frame will be sized to fit the image.

How to flip a QImage

What is the right way to flip/mirror a QImage? The following snippet does not work.
//allocate buffer
BYTE* pRgb32Buffer = new BYTE[width*height* 4];
//create paint device
QImage img = QImage(pRgb32Buffer , width, height, getStride(width, pixelFormat), QImage::Format_RGB32);
//do some drawing on image (works!)
QPainter painter(&img);
painter.drawText(10, 50, QString("some text"));
//mirrore image (doesn't mirror the orignal buffer!!!)
img = img.mirrored(false,true);
//doesn't work either
//QImage mirrored = img.mirrored();
//img = mirrored;
//mirrored.detach();
A solution using QImage::mirrored():
memmove(pRgb32Buffer, img.mirrored().bits(),img.byteCount());
I have this working code to mirror a QPixmap onto an QImage.
QPixmap* source = //... Getting my pixmap form somewhere...
QImage target(QSize(source->width(), source->height()), QImage::Format_ARGB32);
QPainter painter(&target);
QTransform transf = painter.transform();
transf.scale(-1, 1);
painter.setTransform(transf);
painter.drawPixmap(-source->width(), 0, *source);
The source contains the mirrored pixmap after that code. You should be able to do the same with QImageand the QPainter::drawImage function as alternative.
Optionally you can save the file if you want (make sure you have the imageformats dll's or it won't write):
QImageWriter writer("c:\\theimage.tiff", "tiff");
writer.setCompression(1);
writer.write(target);

Converting cv::Mat to SDL_Texture

I am trying to play a video with SDL. For that I'm using opencv to load the video, and get the frames. Then I only need to convert those frames as I need them to a SDL_Texture* and I'm ready to draw them on the screen.
That's my problem, I'm converting it to a SDL_Surface* but then the conversion to SDL_Texture is failing and I'm not sure why. Here is my code:
void Cutscene::play()
{
this->onLoop();
this->onRender();
while(!frameMat.empty())
{
this->onLoop();
this->onRender();
}
}
void Cutscene::onLoop()
{
video >> frameMat;
convertCV_MatToSDL_Texture();
}
void Cutscene::onRender()
{
Image::onDraw(GameEngine::getInstance()->getRenderer(), frameTexture);
}
void Cutscene::convertCV_MatToSDL_Texture()
{
IplImage opencvimg2 = (IplImage)frameMat;
IplImage* opencvimg = &opencvimg2;
//Convert to SDL_Surface
frameSurface = SDL_CreateRGBSurfaceFrom((void*)opencvimg->imageData,
opencvimg->width, opencvimg->height,
opencvimg->depth*opencvimg->nChannels,
opencvimg->widthStep,
0xff0000, 0x00ff00, 0x0000ff, 0);
if(frameSurface == NULL)
{
SDL_Log("Couldn't convert Mat to Surface.");
return;
}
//Convert to SDL_Texture
frameTexture = SDL_CreateTextureFromSurface(
GameEngine::getInstance()->getRenderer(), frameSurface);
if(frameTexture == NULL)
{
SDL_Log("Couldn't convert Mat(converted to surface) to Texture."); //<- ERROR!!
return;
}
//cvReleaseImage(&opencvimg);
//MEMORY LEAK?? opencvimg opencvimg2
}
I've used this function SDL_CreateTextureFromSurface in other parts of my project and it works there. So the question is: Do you know what is the problem with the conversion I do in my code? If not, is there a better way to do what I'm trying to do?
I got it to work! I think the only problem was that i had to use &frameMat and not frameMat.
Here is my code if someone might be interested:
SDL_Texture* Cutscene::convertCV_MatToSDL_Texture(const cv::Mat &matImg)
{
IplImage opencvimg2 = (IplImage)matImg;
IplImage* opencvimg = &opencvimg2;
//Convert to SDL_Surface
frameSurface = SDL_CreateRGBSurfaceFrom(
(void*)opencvimg->imageData,
opencvimg->width, opencvimg->height,
opencvimg->depth*opencvimg->nChannels,
opencvimg->widthStep,
0xff0000, 0x00ff00, 0x0000ff, 0);
if(frameSurface == NULL)
{
SDL_Log("Couldn't convert Mat to Surface.");
return NULL;
}
//Convert to SDL_Texture
frameTexture = SDL_CreateTextureFromSurface(
GameEngine::getInstance()->getRenderer(), frameSurface);
if(frameTexture == NULL)
{
SDL_Log("Couldn't convert Mat(converted to surface) to Texture.");
return NULL;
}
else
{
SDL_Log("SUCCESS conversion");
return frameTexture;
}
cvReleaseImage(&opencvimg);
}
Here is another way without IplImage:
cv::Mat m ...;
// I'm using SDL_TEXTUREACCESS_STREAMING because it's for a video player, you should
// pick whatever suits you most: https://wiki.libsdl.org/SDL_TextureAccess
// remember to pick the right SDL_PIXELFORMAT_* !
SDL_Texture* tex = SDL_CreateTexture(
ren, SDL_PIXELFORMAT_BGR24, SDL_TEXTUREACCESS_STREAMING, m.cols,
m.rows);
SDL_UpdateTexture(tex, NULL, (void*)m.data, m.step1());
// do stuff with texture
SDL_RenderClear(...);
SDL_RenderCopy(...);
SDL_RenderPresent(...);
// cleanup (only after you're done displaying. you can repeatedly call UpdateTexture without destroying it)
SDL_DestroyTexture(tex)
I prefer this to the create surface methods because you don't need to free the surface and it is more flexible (you can update the texture easily for example, instead of create/destroy). I will also note that I could not combine these approaches: ie create the texture with SDL_CreateRGBSurfaceFrom and then later update it. That resulted in gray stripes and the image being messed up.

Use a PNG as a background image in MFC window

I developed a small program in MFC that can display shapes and move them around.
I would like to be able to select PNG images that can be used as the background of the the main MFC window.
Currently, the background is set to black in the following code:
...
CBrush brush;
brush.CreateSolidBrush(RGB(0,0,0));
myCDC->FillRect(r,&brush);
...
I've found classes that might allow me to upload Bitmaps - for example [CStatic][1] - but have had no luck finding something for PNGs.
Does anyone know how to do this, or have you ever done something similar?
You can try to use the class Image of Gdiplus
http://msdn.microsoft.com/en-us/library/windows/desktop/ms534462(v=vs.85).aspx
because the png file can have the alpha property, but the bitmap does not have,
so I always use the Image to load the png file.
wchar_t szFile[256] = {0}; // png image file path
Image* m_pImage;
m_pImage = new Image(szFile, FALSE); // Load png file
// drawing png image
CPaintDC dc(this); // 用于绘制的设备上下文
Graphics graphics(dc.m_hDC);
if(m_pImage != NULL && (m_pImage->GetLastStatus() == Gdiplus::Status::Ok))
{
graphics.DrawImage(m_pImage, 0, 0, m_pImage->GetWidth(), m_pImage->GetHeight());
}
// need delete it, if you donot need it
if (m_pImage != NULL)
{
delete m_pImage;
m_pImage = NULL;
}
I think that GDI+ is the best and simple way for display images. It supports jpg, gif, tiff as well as png.
You can just display a image as below code,
void OnPaint(HDC hdc)
{
Graphics G(hdc);
Image I(L"snowman.jpg");
if (I.GetLastStatus() != Ok) {
MessageBox(hWndMain,TEXT("cannot read the image file"), TEXT("error"),MB_OK);
return;
}
G.DrawImage(&I,0,0);
}
Also, you can change image during execution time using FromFile static method.
Image *pI;
pI=Image::FromFile(L"snowman.jpg");
G.DrawImage(pI,0,0);
delete pI;

SDL DisplayFormat does rectangle

Im new to SDL and C++ overall.
However when i do DisplayFormat on an image for faster blitting it makes it an rectangle.
SDL_Surface* tempImage = NULL;
// The image that will be used (optimized)
image = NULL;
image = IMG_Load( filename.c_str() );
if ( tempImage != NULL )
{
// Create optimization
image = SDL_DisplayFormat( tempImage ); // Makes the circle an rectangle
// Free the old image
SDL_FreeSurface( tempImage );
}
Why is that? If i dont do DisplayFormat, the circle remains an circle when blitted.
This is because your display format which you're converting your image to does not support transparent pixels. You must set your video mode to have 32 bits per pixel, like below:
SDL_Init(SDL_INIT_EVERYTHING);
SDL_Surface *window = SDL_SetVideoMode(width, height, 32, flags);
// ...
You also need to change SDL_DisplayFormat to SDL_DisplayFormatAlpha.