ucrtbased.dll: Access Violation - c++

I am writing an extension (that is compiled as dynamic library), and I get extension error. Unfortunately, I have to draw entire GUI by myself, unfortunately, in pretty raw GDI/WinAPI.
This is my code that crashes:
Slider::Slider(const char* objectId, int height, POINT topLeft, int visibleElements, int totalElements, int elementHeight, EuroScopePlugIn::CRadarScreen* rs)
{
// Member variable initailization
m_ObjectId = objectId;
m_Height = height;
m_TopLeft = topLeft;
m_VisibleElements = visibleElements;
m_ElementHeight = elementHeight;
m_TotalElements = totalElements;
m_Width = 10;
m_pRS = rs;
m_CurrentFirstElement = 0;
m_CurrentLastElement = m_CurrentFirstElement + visibleElements;
}
SliderGrip::SliderGrip(Slider* sliderObject) : Slider(m_ObjectId.c_str(), m_Height, m_TopLeft, m_VisibleElements, m_TotalElements, m_ElementHeight, m_pRS)
{
// Base class member variable initialization
{
m_ObjectId = sliderObject->m_ObjectId;
m_Height = sliderObject->m_Height;
m_TopLeft = sliderObject->m_TopLeft;
m_VisibleElements = sliderObject->m_VisibleElements;
m_ElementHeight = sliderObject->m_ElementHeight;
m_TotalElements = sliderObject->m_TotalElements;
m_Width = sliderObject->m_Width;
m_pRS = sliderObject->m_pRS;
m_CurrentFirstElement = sliderObject->m_CurrentFirstElement;
m_CurrentLastElement = sliderObject->m_CurrentLastElement;
}
m_GripHeight = (m_VisibleElements / m_TotalElements) * m_ElementHeight;
m_GripTopLeft = (CPoint)(m_TopLeft.x, m_TopLeft.y + 10 + (m_CurrentFirstElement * m_ElementHeight));
}
void SliderGrip::RenderGrip(CDC* dc)
{
// localObjectId
std::string localObjectId = "Grip";
// Save DC for later
int sDC = dc->SaveDC();
// Create CRectangle
CRect CRectangle(m_GripTopLeft.x, m_GripTopLeft.y, m_GripTopLeft.x + m_Width, m_GripTopLeft.y + m_GripHeight);
// Grip Color
COLORREF m_GripColor = RGB(0, 0, 0);
// Fill CRectangle with CBrush
CBrush RectangleFill(m_GripColor);
dc->FillRect(&CRectangle, &RectangleFill);
m_pRS->AddScreenObject(CELEMENTS::SCROLLBAR_GRIP, localObjectId.c_str(), CRectangle, 1, "");
// Restore DC
dc->RestoreDC(sDC);
// Cleaning
DeleteObject(RectangleFill);
}
SliderTrack::SliderTrack(Slider* sliderObject) : Slider(m_ObjectId.c_str(), m_Height, m_TopLeft, m_VisibleElements, m_TotalElements, m_ElementHeight, m_pRS)
{
// Base class member variable initialization
{
m_ObjectId = sliderObject->m_ObjectId;
m_Height = sliderObject->m_Height;
m_TopLeft = sliderObject->m_TopLeft;
m_VisibleElements = sliderObject->m_VisibleElements;
m_ElementHeight = sliderObject->m_ElementHeight;
m_TotalElements = sliderObject->m_TotalElements;
m_Width = sliderObject->m_Width;
m_pRS = sliderObject->m_pRS;
m_CurrentFirstElement = sliderObject->m_CurrentFirstElement;
m_CurrentLastElement = sliderObject->m_CurrentLastElement;
}
m_TrackHeight = m_VisibleElements * m_ElementHeight;
m_TrackTopLeft = (CPoint)(m_TopLeft.x, m_TopLeft.y + 10);
}
void SliderTrack::RenderTrack(CDC* dc)
{
// Save DC for later
int sDC = dc->SaveDC();
// CRectangle
CRect CRectangle(m_TrackTopLeft.x, m_TrackTopLeft.y, m_TrackTopLeft.x + m_Width, m_TrackTopLeft.y + m_TrackHeight);
// Color
COLORREF m_TrackColor = RGB(200, 200, 200);
// Fill CRectangle with CBrush
CBrush RectangleFill(m_TrackColor);
dc->FillRect(&CRectangle, &RectangleFill);
// Restore DC
dc->RestoreDC(sDC);
// Cleaning
DeleteObject(RectangleFill);
}
SliderButton::SliderButton() : Slider(m_ObjectId.c_str(), m_Height, m_TopLeft, m_VisibleElements, m_TotalElements, m_ElementHeight, m_pRS)
{
}
[...]
#pragma region Scrollbar
// Scrollbar -> Elements::Slider --- grip, track, buttonup, buttondown
Scrollbar::Scrollbar(const char* name, EuroScopePlugIn::CRadarScreen* rs, POINT topLeft, int height, int visibleElements, int totalElements, int elementHeight)
{
// member Var initialization
m_Name = name;
m_pRS = rs;
m_TopLeft = topLeft;
m_Height = height;
m_Width = 10;
m_VisibleElements = visibleElements;
m_TotalElements = totalElements;
m_ElementHeight = elementHeight;
// initialize objects
sliderObject = new CInterface::Slider(m_Name.c_str(), m_Height, m_TopLeft, m_VisibleElements, m_TotalElements, m_ElementHeight, m_pRS);
gripObject = new CInterface::SliderGrip(sliderObject);
trackObject = new CInterface::SliderTrack(sliderObject);
}
void Scrollbar::AddTotalElements()
{
m_TotalElements++;
}
void Scrollbar::AddTotalElements(int count)
{
m_TotalElements += count;
}
void Scrollbar::RemoveTotalElements()
{
if(m_TotalElements>0)
m_TotalElements--;
}
void Scrollbar::RemoveTotalElements(int Count)
{
if (m_TotalElements > Count)
m_TotalElements -= Count;
}
void Scrollbar::Render(CDC* dc)
{
// for later
int sDC = dc->SaveDC();
gripObject->RenderGrip(dc);
trackObject->RenderTrack(dc);
// restore
dc->RestoreDC(sDC);
}
Scrollbar::~Scrollbar()
{
delete sliderObject;
delete gripObject;
delete trackObject;
}
#pragma endregion Scrollbar
Later on, code is called from main app in this way:
CallingFunction()
{
testScroll = new CInterface::Scrollbar("testScroll", this, xyz, 50, 10, 50, 5);
}
~CallingFunction()
{
delete testScroll;
}
Until now, code doesn't crash, but once I call rendering functions:
OnRender(HDC hDC)
{
CDC dc;
dc.Attach(hDC);
testScroll->Render(&dc);
dc.Detach();
dc.DeleteDC();
}
App crashes on startup with given code error:
Exception thrown at 0x78F6FF5C (ucrtbased.dll) in EuroScope.exe: 0xC0000005: Access violation reading location 0xCDCDCDCD.
I have no clue what's causing this, any ideas? I am roughly sure it may be issue with some pointer and/or passed const char*. While I tried to replace const char* to std::string whenever possible, I got an similar error, but related to vcruntime140.dll...

Related

Gtk smooth panning issue

I am in a project where map and other information are displayed in a Gtk window. There are several map layers, that I draw into a Cairo surface and save in a .png (plotRect() function in code below). That .png is displayed in a Gtk image when there is a Gtk draw signal (draw()).
I now want to accomplish smooth grabbing and panning. When the mouse button is pressed, I want the whole image to be translated within the window area, follow the movements of the mouse. When the button is released the image should be Cairo remade and redrawn with new bounds. During the drag/pan procedure itself there is no need to draw areas that where previously out of the window borders -- it is OK to wait for that to be done when the mouse button is released.
Enclosed you find a simplified version of my code. The main has a loop going until the windows is closed, redrawing the image after each panning. The problem is in the pan() function. After the translation in line 21 I woould expect the draw statement in line 128 to successively draw panned images while moving the mouse, but the visible image is unaffected. Uncommenting line 23 shows that graph->image has really been modified, and I can see that the draw signal of line 25 is invoking the draw() callback function. After button release, the translated image is correctly displayed.
Can anyone please give me some advice?
I'm using gcc, Cairo, Gtk3 and Ubuntu 18.04 on a double-booted MacBook Pro 64 bit i5.
#include <cairo.h>
#include <chrono>
#include <cmath>
#include <gtk/gtk.h>
#include <iostream>
#include <mutex>
#include <thread>
using namespace std;
mutex mtx;
bool gtkMainLoopRunning = false;
#define SLEEP(d) this_thread::sleep_for(chrono::milliseconds(d))
template <class T>
inline T sqr(T x) {
return x * x;
}
//-----------------------------------------------------------------------------
class Graph {
double toplat, leftlon; // upper left corner
double dydlat, dxdlon; // pixels / degree lon/lat
public:
int size; // window x = y
GtkWidget *window;
GtkImage *image;
const char *png = "/tmp/image.png";
cairo_surface_t *surface{};
cairo_t *cr{};
bool closed = false;
bool leftbuttondown = false;
int mousex = 0, mousey = 0;
Graph(const double, const double, const double, const double);
~Graph();
void plotRect(const double, const double, const double, const double);
bool pan();
};
//-----------------------------------------------------------------------------
static gboolean draw(GtkWidget *widget, cairo_t *cr, gpointer data) {
Graph *graph = (Graph *)data;
if (!graph->leftbuttondown) {
mtx.lock();
gtk_image_set_from_file(graph->image, graph->png);
mtx.unlock();
}
return FALSE;
}
//-----------------------------------------------------------------------------
static gboolean clicked(GtkWidget *widget, GdkEventButton *button, gpointer data) {
Graph *graph = (Graph *)data;
if (button->button == 1) {
if (button->type == GDK_BUTTON_PRESS) {
graph->leftbuttondown = true;
} else if (button->type == GDK_BUTTON_RELEASE) {
graph->leftbuttondown = false;
}
}
graph->mousex = button->x;
graph->mousey = button->y;
return FALSE;
}
//-----------------------------------------------------------------------------
Graph::~Graph() {
do {
SLEEP(100);
} while (gtkMainLoopRunning); // wait until gtk main loop has stopped
cairo_destroy(cr);
cairo_surface_destroy(surface);
}
//-----------------------------------------------------------------------------
void destroyWindow(GtkWidget *widget, gpointer data) {
gtk_main_quit();
gtkMainLoopRunning = false;
Graph *graph = (Graph *)data;
graph->closed = true; // signal gtkThread to finish
gtk_widget_destroy((GtkWidget *)graph->image);
gtk_widget_destroy(graph->window);
}
//-----------------------------------------------------------------------------
Graph::Graph(const double minlat, const double minlon, const double maxlat, const double maxlon) {
gtk_init(NULL, NULL);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
size = 800;
gtk_widget_show(window);
image = (GtkImage *)gtk_image_new();
gtk_widget_set_size_request((GtkWidget *)image, size, size);
gtk_container_add(GTK_CONTAINER(window), (GtkWidget *)image);
gtk_widget_show((GtkWidget *)image);
g_signal_connect(image, "draw", G_CALLBACK(draw), this);
g_signal_connect(window, "destroy", G_CALLBACK(destroyWindow), this);
surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, size, size);
cr = cairo_create(surface);
gtk_widget_add_events(window, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_BUTTON1_MOTION_MASK);
g_signal_connect(window, "button-press-event", G_CALLBACK(clicked), this);
g_signal_connect(window, "button-release-event", G_CALLBACK(clicked), this);
g_signal_connect(window, "motion-notify-event", G_CALLBACK(clicked), this);
const double coslat = cos((minlat + maxlat) / 2 * M_PI / 180);
const double extension = max(maxlat - minlat, (maxlon - minlon) * coslat); // [lat degrees]
toplat = (minlat + maxlat + extension) / 2;
leftlon = (minlon + maxlon - extension / coslat) / 2;
dydlat = -size / extension; // [pixels/degree]
dxdlon = size / extension * coslat;
gtkMainLoopRunning = true;
thread(gtk_main).detach();
}
//-----------------------------------------------------------------------------
bool Graph::pan() {
const int sqrSignifPan = sqr(4);
while (!closed) {
if (leftbuttondown) {
int x0 = mousex;
int y0 = mousey;
int dx = 0, dy = 0;
GdkPixbuf *origPixbuf = gdk_pixbuf_new_from_file("/tmp/image.png", NULL);
char *origPixels = (char *)gdk_pixbuf_get_pixels(origPixbuf);
const int rowstride = gdk_pixbuf_get_rowstride(origPixbuf);
const int nChannels = gdk_pixbuf_get_n_channels(origPixbuf);
char *imagePixels = (char *)gdk_pixbuf_get_pixels(gtk_image_get_pixbuf(image));
while (leftbuttondown) {
const int dx0 = dx, dy0 = dy;
dx = mousex - x0, dy = mousey - y0;
if (sqr(dx - dx0) + sqr(dy - dy0) >= sqrSignifPan) {
const int minx = max(0, -dx);
const int nx = max(0, size - abs(dx));
if (nx > 0) {
for (int y = max(0, -dy); y < min(size, size - dy); ++y) {
memcpy(imagePixels + (y + dy) * rowstride + (minx + dx) * nChannels, origPixels + y * rowstride + minx * nChannels, nx * nChannels);
}
// gdk_pixbuf_save(gtk_image_get_pixbuf(image), "/tmp/imagePixbuf.png", "png", NULL, NULL);
gtk_widget_queue_draw((GtkWidget *)image);
SLEEP(10); // pause for drawing
}
}
SLEEP(100);
}
// rescale graph
toplat -= (mousey - y0) / dydlat;
leftlon -= (mousex - x0) / dxdlon;
dxdlon = -dydlat * cos((toplat + size / dydlat / 2) * M_PI / 180);
gtk_widget_queue_draw((GtkWidget *)image);
return true;
}
SLEEP(100);
}
return false;
}
//-----------------------------------------------------------------------------
void Graph::plotRect(const double minlat, const double minlon, const double maxlat, const double maxlon) {
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_paint(cr);
cairo_rectangle(cr, (minlon - leftlon) * dxdlon, (minlat - toplat) * dydlat, (maxlon - minlon) * dxdlon, (maxlat - minlat) * dydlat);
cairo_set_source_rgb(cr, 0, 1, 0);
cairo_fill(cr);
mtx.lock();
remove(png);
cairo_surface_write_to_png(surface, png);
mtx.unlock();
}
//-----------------------------------------------------------------------------
int main() {
const double minlat = 59, minlon = 16, maxlat = 60, maxlon = 18;
Graph *graph = new Graph(minlat - 0.5, minlon - 1, maxlat + 0.5, maxlon + 1);
do {
graph->plotRect(minlat, minlon, maxlat, maxlon);
} while (graph->pan());
delete graph;
}

Getting an error "This was 0x35" in C++ using SDL2

I'm trying to make a simple game engine in C++ and I keep getting a weird error that says "This was 0x35":
I'm using visual studio and SDL2 (although I use it more like SDL). When the error occurs, the program has started to execute and is "Still running" meaning I have to stop it manually. The exception shows up in this function:
void Tab::Draw(int x, int y, SDL_Surface* display, bool setpos = true)
{
if (setpos) {
this->rect->x = x; //This is the line that the exception points to.
this->rect->y = y;
}
SDL_BlitScaled(surf, NULL, display, rect);
}
The variable 'rect' is an SDL_Rect
The variable 'surf' is an SDL_Surface
both variables get initialized here:
Tab::Tab(SDL_Surface* Image, SDL_Rect* Rect) {
surf = Image;
if (Rect != NULL) {
rect = Rect;
}
}
and this is the only code that calls the function Tab::Draw:
void Bar::Draw(SDL_Surface* display)
{
SDL_BlitScaled(image, NULL, display, &rect);
for (int i = 0; i < 20; i++) {
tabs[i]->Draw(0, 0, display, true);
}
}
The bar class is meant to be a line with a bunch of tabs(which I should and will rename to Icon at some point)
Where Bar::Draw gets called:
#include "AppClass.h"
void CApp::OnRender() {
//Draw stuff
SDL_FillRect(window, NULL, SDL_MapRGB(window->format, r, g, b));
main.Draw(window);
ToBlit->Rect.x = mouseX - 32;
ToBlit->Rect.y = mouseY - 32;
ToBlit->Rect.w = 64;
ToBlit->Rect.h = 64;
if (SDL_GetMouseFocus() == display) {
SDL_BlitScaled(ToBlit->Surface, NULL, window, &ToBlit->Rect);
}
//Update
SDL_UpdateWindowSurface(display);
}

Zoom Image at specific location - centered

So I wanted to add a texture to a map, but the problem with it is that I can't quite get why it doesn't get to the correct place while zooming with different zoom sizes.
I'm normally trying to achieve setting the texture position on the background to my position, keeping myself centered into the frame: for example my texture size is 1500x1600 and I'm located at X140, Y590 in that picture ( yes, the coordinates are retrieved correctly as I've checked with the console ), zooming in with some value and scaling the texture and setting it's position to where I'm at.
The code is the following:
if (!areTexturesInit) {
InitiateTextures();
areTexturesInit = true;
}
wxBitmap bit(imageTest);
wxPaintDC dc(this);
double zoomSize = 0.9; // here I'm applying the zooming proportions ( 0.1 - bigger size of the texture, 0.9 - more zoomed in )
this->SetSize(wxSize(386, 386)); // size of the main frame
backgroundSize.x = GetSize().x; // get the size of the main frame
backgroundSize.y = GetSize().y;
middlePoint.x = (backgroundSize.x / 2); // calculate the middle point of the frame
middlePoint.y = (backgroundSize.y / 2);
mapSizeX = 25600 / -zoomSize; // scale vs zoom size
mapSizeY = 25600 / zoomSize;
Vector3 myPosition;
GetPlayerPosition(&myPosition); // gets my location
float TextureCoordinateX = middlePoint.x + (myPosition.x / mapSizeX) * backgroundSize.x;
float TextureCoordinateY = middlePoint.y - (myPosition.y / mapSizeY) * backgroundSize.y;
dc.DrawBitmap(bit, TextureCoordinateX, TextureCoordinateY);
Vector3 myPosOnMap = PositionToMapPosition(myPosition, myPosition); // calculates my position on the map vs mapSizeX and Y & rotates vector
dc.SetPen(wxPen(wxColor(255, 0, 0), 4));
dc.DrawRectangle(wxRect(myPosOnMap.x, myPosOnMap.y, 2, 2)); // draws me on the map with a red square
The problem is that I think I've messed up the zooming part somewhere.
I've attached some demos so you can see what I'm talking about:
"zoomSize" of 0.9:
"zoomSize" of 0.67 - which kind of works, but I need to change it to different zoomSizes, there being the problem:
Panning and zooming a dc is surprisingly complicated. It requires working with 3 separate coordinate systems and it's really easy to accidentally work in the wrong coordinate system.
Here's an example I wrote a while ago that shows how to do the calculations that allow a dc to be pan and zoomed.
It sounds like you're not interested in the pan part, so you can ignore all the stuff that allows a user to set their own pan. However, it's still necessary to to use a pan vector just for the zoom in order to center the zoom at the correct location.
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
// for all others, include the necessary headers (this file is usually all you
// need because it includes almost all "standard" wxWidgets headers)
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include <wx/graphics.h>
#include <wx/dcbuffer.h>
class PanAndZoomCanvas:public wxWindow
{
public:
PanAndZoomCanvas(wxWindow *parent,
wxWindowID id = wxID_ANY,
const wxPoint &pos=wxDefaultPosition,
const wxSize &size=wxDefaultSize,
long style=0,
const wxString &name="PanAndZoomCanvas");
wxRect2DDouble GetUntransformedRect() const;
protected:
void DoDrawCanvas(wxGraphicsContext*);
private:
void OnPaint(wxPaintEvent&);
void OnMouseWheel(wxMouseEvent&);
void OnLeftDown(wxMouseEvent&);
void OnMotion(wxMouseEvent&);
void OnLeftUp(wxMouseEvent&);
void OnCaptureLost(wxMouseCaptureLostEvent&);
void ProcessPan(const wxPoint&,bool);
void FinishPan(bool);
int m_zoomFactor;
wxPoint2DDouble m_panVector;
wxPoint2DDouble m_inProgressPanVector;
wxPoint m_inProgressPanStartPoint;
bool m_panInProgress;
};
PanAndZoomCanvas::PanAndZoomCanvas(wxWindow *parent, wxWindowID id,
const wxPoint &pos, const wxSize &size,
long style, const wxString &name)
:wxWindow(parent, id, pos, size, style, name)
{
Bind(wxEVT_PAINT,&PanAndZoomCanvas::OnPaint,this);
Bind(wxEVT_MOUSEWHEEL,&PanAndZoomCanvas::OnMouseWheel,this);
Bind(wxEVT_LEFT_DOWN,&PanAndZoomCanvas::OnLeftDown,this);
SetBackgroundStyle(wxBG_STYLE_PAINT);
m_zoomFactor = 100;
m_panVector = wxPoint2DDouble(0,0);
m_inProgressPanStartPoint = wxPoint(0,0);
m_inProgressPanVector = wxPoint2DDouble(0,0);
m_panInProgress = false;
}
void PanAndZoomCanvas::DoDrawCanvas(wxGraphicsContext* gc)
{
gc->SetPen(*wxBLACK_PEN);
wxGraphicsPath path = gc->CreatePath();
path.MoveToPoint(100,100);
path.AddLineToPoint(300,100);
path.AddLineToPoint(300,300);
path.CloseSubpath();
gc->StrokePath(path);
}
void PanAndZoomCanvas::OnPaint(wxPaintEvent& WXUNUSED(event))
{
wxAutoBufferedPaintDC dc(this);
dc.Clear();
wxGraphicsContext* gc = wxGraphicsContext::Create(dc);
if ( gc )
{
double a = m_zoomFactor / 100.0;
wxPoint2DDouble totalPan = m_panVector + m_inProgressPanVector;
gc->Translate(-totalPan.m_x, -totalPan.m_y);
gc->Scale(a, a);
DoDrawCanvas(gc);
delete gc;
}
}
void PanAndZoomCanvas::OnMouseWheel(wxMouseEvent& event)
{
if ( m_panInProgress )
{
FinishPan(false);
}
int rot = event.GetWheelRotation();
int delta = event.GetWheelDelta();
int oldZoom = m_zoomFactor;
m_zoomFactor += 10*(rot/delta);
if ( m_zoomFactor<10 )
{
m_zoomFactor = 10;
}
if ( m_zoomFactor>800)
{
m_zoomFactor = 800;
}
double a = oldZoom / 100.0;
double b = m_zoomFactor / 100.0;
// Set the panVector so that the point below the cursor in the new
// scaled/panned cooresponds to the same point that is currently below it.
wxPoint2DDouble uvPoint = event.GetPosition();
wxPoint2DDouble stPoint = uvPoint + m_panVector;
wxPoint2DDouble xypoint = stPoint/a;
wxPoint2DDouble newSTPoint = b * xypoint;
m_panVector = newSTPoint - uvPoint;
Refresh();
}
void PanAndZoomCanvas::ProcessPan(const wxPoint& pt, bool refresh)
{
m_inProgressPanVector = m_inProgressPanStartPoint - pt;
if ( refresh )
{
Refresh();
}
}
void PanAndZoomCanvas::FinishPan(bool refresh)
{
if ( m_panInProgress )
{
SetCursor(wxNullCursor);
if ( HasCapture() )
{
ReleaseMouse();
}
Unbind(wxEVT_LEFT_UP, &PanAndZoomCanvas::OnLeftUp, this);
Unbind(wxEVT_MOTION, &PanAndZoomCanvas::OnMotion, this);
Unbind(wxEVT_MOUSE_CAPTURE_LOST, &PanAndZoomCanvas::OnCaptureLost, this);
m_panVector += m_inProgressPanVector;
m_inProgressPanVector = wxPoint2DDouble(0,0);
m_panInProgress = false;
if ( refresh )
{
Refresh();
}
}
}
wxRect2DDouble PanAndZoomCanvas::GetUntransformedRect() const
{
double a = m_zoomFactor / 100.0;
wxSize sz = GetSize();
wxPoint2DDouble zero = m_panVector/a;
return wxRect2DDouble(zero.m_x, zero.m_y, sz.GetWidth()/a, sz.GetHeight()/a);
}
void PanAndZoomCanvas::OnLeftDown(wxMouseEvent& event)
{
wxCursor cursor(wxCURSOR_HAND);
SetCursor(cursor);
m_inProgressPanStartPoint = event.GetPosition();
m_inProgressPanVector = wxPoint2DDouble(0,0);
m_panInProgress = true;
Bind(wxEVT_LEFT_UP, &PanAndZoomCanvas::OnLeftUp, this);
Bind(wxEVT_MOTION, &PanAndZoomCanvas::OnMotion, this);
Bind(wxEVT_MOUSE_CAPTURE_LOST, &PanAndZoomCanvas::OnCaptureLost, this);
CaptureMouse();
}
void PanAndZoomCanvas::OnMotion(wxMouseEvent& event)
{
ProcessPan(event.GetPosition(), true);
}
void PanAndZoomCanvas::OnLeftUp(wxMouseEvent& event)
{
ProcessPan(event.GetPosition(), false);
FinishPan(true);
}
void PanAndZoomCanvas::OnCaptureLost(wxMouseCaptureLostEvent&)
{
FinishPan(true);
}
class MyFrame : public wxFrame
{
public:
MyFrame(wxWindow* parent, int id = wxID_ANY, wxString title = "Demo",
wxPoint pos = wxDefaultPosition, wxSize size = wxDefaultSize,
int style = wxDEFAULT_FRAME_STYLE );
};
MyFrame::MyFrame( wxWindow* parent, int id, wxString title, wxPoint pos
, wxSize size, int style )
:wxFrame( parent, id, title, pos, size, style )
{
PanAndZoomCanvas* canvas = new PanAndZoomCanvas(this);
}
class myApp : public wxApp
{
public:
virtual bool OnInit()
{
MyFrame* frame = new MyFrame(NULL);
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(myApp);
On windows, this looks like this:

Thread safety for taking screenshots on Windows with C++ Builder

Isn't taking a screenshot on Windows thread-safe?
My following code sometimes takes some shots, but in most cases, the imgScreenshot (which is just a TImage) keeps being just plain white...
Am I missing something?
void __fastcall TCaptureThread::Execute()
{
int CurWidth = 1600;
int CurHeight = 900;
std::unique_ptr<TCanvas> Canvas(new TCanvas);
Canvas->Handle = GetDC(0);
FBMP = new TBitmap; // private class field
FBMP->Width = CurWidth;
FBMP->Height = CurHeight;
FR = Rect(0, 0, CurWidth, CurHeight); // private class field
while(!Terminated)
{
FBMP->Canvas->CopyRect(FR, Canvas, FR);
Synchronize(&UpdatePicture);
Sleep(100);
}
delete FBMP;
FBMP = NULL;
}
void __fastcall TCaptureThread::UpdatePicture()
{
FMainForm->imgScreenshot->Canvas->CopyRect(FR, FBMP->Canvas, FR);
}
Environment is C++ Builder 10.1.2 Berlin
Isn't taking a screenshot on Windows thread-safe?
Not when using VCL wrapper classes that are not thread-safe by default. If you were using plain Win32 API functions directly, then yes, it would be possible to write thread-safe code.
The main reason your code fails is because the VCL is designed to share GDI resources between multiple objects, and the main UI thread frequently frees unused/dormant GDI resources. So your worker thread's TBitmap image data is likely to get destroyed before you can call Synchronize() to copy it to your TImage.
That being said, what you are attempting can be done if you call Lock()/Unlock() on the Canvas objects in your worker thread, eg:
struct CanvasLocker
{
TCanvas *mCanvas;
CanvasLocker(TCanvas *C) : mCanvas(C) { mCanvas->Lock(); }
~CanvasLocker() { mCanvas->Unlock(); }
};
void __fastcall TCaptureThread::Execute()
{
int CurWidth = 1600;
int CurHeight = 900;
std::unique_ptr<TCanvas> Canvas(new TCanvas);
std::unique_ptr<TBitmap> BMP(new TBitmap);
FBMP = BMP.get();
{
CanvasLocker lock(Canvas); // <-- add this!
Canvas->Handle = GetDC(0);
}
{
CanvasLocker lock(BMP->Canvas); // <-- add this!
BMP->Width = CurWidth;
BMP->Height = CurHeight;
}
FR = Rect(0, 0, CurWidth, CurHeight);
while (!Terminated)
{
{
CanvasLocker lock1(Canvas); // <-- add this!
CanvasLocker lock2(BMP->Canvas); // <-- add this!
BMP->Canvas->CopyRect(FR, Canvas.get(), FR);
}
Synchronize(&UpdatePicture);
Sleep(100);
}
}
void __fastcall TCaptureThread::UpdatePicture()
{
CanvasLocker lock1(FBMP->Canvas); // <-- add this!
CanvasLocker lock2(FMainForm->imgScreenshot->Canvas); // <-- add this!
FMainForm->imgScreenshot->Canvas->CopyRect(FR, FBMP->Canvas, FR);
// or: FMainForm->imgScreenshot->Picture->Bitmap->Assign(FBMP);
}
That being said, because TCanvas is lockable, you might be able to get away with removing Synchronize() altogether:
void __fastcall TCaptureThread::Execute()
{
int CurWidth = 1600;
int CurHeight = 900;
std::unique_ptr<TCanvas> Canvas(new TCanvas);
std::unique_ptr<TBitmap> BMP(new TBitmap);
{
CanvasLocker lock(Canvas);
Canvas->Handle = GetDC(0);
}
{
CanvasLocker lock(BMP->Canvas);
BMP->Width = CurWidth;
BMP->Height = CurHeight;
}
TRect r = Rect(0, 0, CurWidth, CurHeight);
while (!Terminated)
{
{
CanvasLocker lock1(BMP->Canvas);
{
CanvasLocker lock2(Canvas);
BMP->Canvas->CopyRect(r, Canvas.get(), r);
}
CanvasLocker lock3(FMainForm->imgScreenshot->Canvas);
FMainForm->imgScreenshot->Canvas->CopyRect(r, BMP->Canvas, r);
// or: FMainForm->imgScreenshot->Picture->Bitmap->Assign(BMP);
}
Sleep(100);
}
}

SDL_surface that contains several images

Suppose I have a SDL_Surface that is just one image.
What if I wanted to make that SDL_Surface have three copies of that image, one below the other?
I came up with this function, but it doesn't show anything:
void ElementView::adjust()
{
int imageHeight = this->img->h;
int desiredHeight = 3*imageHeight;
int repetitions = desiredHeight / imageHeight ;
int remainder = desiredHeight % imageHeight ;
SDL_Surface* newSurf = SDL_CreateRGBSurface(img->flags, img->w, desiredHeight, 32, img->format->Rmask, img->format->Gmask, img->format->Bmask,img->format->Amask);
SDL_Rect rect;
memset(&rect, 0, sizeof(SDL_Rect));
rect.w = this->img->w;
rect.h = this->img->h;
for (int i = 0 ; i < repetitions ; i++)
{
rect.y = i*imageHeight;
SDL_BlitSurface(img,NULL,newSurf,&rect);
}
rect.y += remainder;
SDL_BlitSurface(this->img,NULL,newSurf,&rect);
if (newSurf != NULL) {
SDL_FreeSurface(this->img);
this->img = newSurf;
}
}
I think you should
Create a new surface that is 3 times as long as the initial one
Copy from img to the new surface using code similar to what you have (SDL_BlitSurface), except having the destination as your new surface
SDL_FreeSurface on your original img
Assign your new surface to img
Edit: Here is some sample code, didn't have time to test it though...
void adjust(SDL_Surface** img)
{
SDL_PixelFormat *fmt = (*img)->format;
SDL_Surface* newSurf = SDL_CreateRGBSurface((*img)->flags, (*img)->w, (*img)->h * 3, fmt->BytesPerPixel * 8, fmt->Rmask, fmt->Gmask, fmt->Bmask, fmt->Amask);
SDL_Rect rect;
memset(&rect, 0, sizeof(SDL_Rect));
rect.w = (*img)->w;
rect.h = (*img)->h;
int i = 0;
for (i ; i < 3; i++)
{
SDL_BlitSurface(*img,NULL,newSurf,&rect);
rect.y += (*img)->h;
}
SDL_FreeSurface(*img);
*img = newSurf;
}