I want to paint a rectangle to select the area according to mouse coordinates. So, the rectangle should be created and deleted every frame (the if statement below executes every frame). I was trying to call these functions, but nothing happened - shapes were painting one above another.
cairo_surface_destroy(surface);
cairo_destroy(cr);
if (xevent.type == MotionNotify && isPressed == true)
{
int tmp_x = xevent.xmotion.x_root;
int tmp_y = xevent.xmotion.y_root;
int scr = DefaultScreen(xdisplay);
cairo_surface_t *surface = cairo_xlib_surface_create(xdisplay, xroot, DefaultVisual(xdisplay, scr), DisplayWidth(xdisplay, scr), DisplayHeight(xdisplay, scr));
cairo_t *cr = cairo_create(surface);
cairo_rectangle(cr, init_x, init_y, tmp_x - init_x, tmp_y - init_y); /* set rectangle */
cairo_set_source_rgba(cr, 0.3, 0.4, 0.6, 0.1); /* set fill color */
cairo_fill(cr);
cairo_destroy(cr);
}
Related
I want to draw the area of one surface above another surface, but nothing appears on the screen. I want to create area selection mechanics for making a screenshot. That means that I should clear the entire surface before next mouse movement in order not to place one selection above another.
void OcctGtkViewer::buttonclick(int *x, int *y, int *width, int *height)
{
bool isPressed = false;
int init_x, init_y, fin_x, fin_y;
int prevInitX, prevInitY, prevFinX, prevFinY;
Display *xdisplay = XOpenDisplay(0);
XEvent xevent;
::Window xroot = gdk_x11_window_get_xid(gtk_widget_get_window((GtkWidget *)myGLArea->gobj()));
int scr = DefaultScreen(xdisplay);
cairo_surface_t *surface = cairo_xlib_surface_create(xdisplay, xroot, DefaultVisual(xdisplay, scr), DisplayWidth(xdisplay, scr), DisplayHeight(xdisplay, scr));
cairo_t *cr = cairo_create(surface);
const char *backgroundImagePath = "/tmp/test.png";
const char *backgroundImagePath2 = "/tmp/test2.png";
cairo_surface_write_to_png(
surface,
backgroundImagePath);
cairo_surface_t *surfaceClearImage = cairo_image_surface_create_from_png(backgroundImagePath);
cairo_set_source_rgba(cr, 0, 0, 0, 0.2);
cairo_rectangle(cr, 0, 0, DisplayWidth(xdisplay, scr), DisplayHeight(xdisplay, scr));
cairo_fill(cr);
cairo_surface_write_to_png(
surface,
backgroundImagePath2);
cairo_surface_t *surfaceOverlay = cairo_image_surface_create_from_png(backgroundImagePath2);
cr = cairo_create(surfaceOverlay);
XGrabPointer(xdisplay, xroot, 1, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
while (1)
{
XNextEvent(xdisplay, &xevent);
if (xevent.type == MotionNotify && isPressed == true)
{
int tmp_x = xevent.xmotion.x;
int tmp_y = xevent.xmotion.y;
cairo_save(cr);
cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
cairo_paint(cr);
cairo_restore(cr);
cairo_set_source_surface(cr, surfaceClearImage, 0, 0);
cairo_rectangle(cr, init_x, init_y, tmp_x - init_x, tmp_y - init_y); // set rectangle
cairo_fill(cr);
XSync(xdisplay, true);
}
}
}
The result I want to achieve:
If I delete these lines of code: `
cairo_surface_t *surfaceOverlay = cairo_image_surface_create_from_png(backgroundImagePath2);
cr = cairo_create(surfaceOverlay);`
I get this result:
On the screen below I have the image with the painted translucent rectangle and with the painted opaque rectangle - which cuts the area from translucent rectangle. Now it works because I redraw the surface and the translucent rectangle each time in the cycle, but working with slow computers this redrawing causes glaring. If I stop redrawing all this stuff, I get this result, but the glaring disappears:
The commented lines in code below cause the result at the picture
I want just to erase the the translucent rectangle without redrawing the entire surface. I want to dynamically select the area (without redrawing the entire surface each time) for making a screenshot like on the Windows 11
void OcctGtkViewer::buttonclick(int *x, int *y, int *width, int *height)
{
cairo_surface_t *surface = cairo_xlib_surface_create(xdisplay, xroot, DefaultVisual(xdisplay, scr), DisplayWidth(xdisplay, scr), DisplayHeight(xdisplay, scr));
cairo_t *cr = cairo_create(surface);
const char *backgroundImagePath = "/tmp/test.png";
cairo_surface_write_to_png(
surface,
backgroundImagePath);
cairo_surface_t *surfaceTmp = cairo_image_surface_create_from_png(backgroundImagePath);
cairo_set_source_rgba(cr, 0, 0, 0, 0.2);
cairo_rectangle(cr, 0, 0, DisplayWidth(xdisplay, scr), DisplayHeight(xdisplay, scr));
cairo_fill(cr);
cairo_surface_t *surfaceTmpp = cairo_image_surface_create_from_png(backgroundImagePath);
printf("select an area\n");
while (1)
{
event.type == MotionNotify && isPressed == true)
{
int tmp_x = xevent.xmotion.x_root;
int tmp_y = xevent.xmotion.y_root;
cairo_set_source_surface(cr, surfaceTmp, 1, 1);
// cairo_paint(cr);
cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD); // The default is CAIRO_FILL_RULE_WINDING.
// cairo_set_source_rgba(cr, 0, 0, 0, 0.2);
// cairo_rectangle(cr, 0, 0, DisplayWidth(xdisplay, scr), DisplayHeight(xdisplay, scr));
cairo_rectangle(cr, init_x, init_y, tmp_x - init_x, tmp_y - init_y); // set rectangle
cairo_fill(cr);
}
}
}
On the screen below I have the image with the painted translucent rectangle and with the painted opaque rectangle. My purpose is to cut the area of the opaque rectangle - delete pixels in the translucent rectangle in order to see the initial image.
cairo_surface_t *surface = cairo_xlib_surface_create(xdisplay, xroot, DefaultVisual(xdisplay, scr), DisplayWidth(xdisplay, scr), DisplayHeight(xdisplay, scr));
cairo_t *cr = cairo_create(surface);
cairo_surface_write_to_png(
surface,
"test.png");
cairo_surface_t *surfaceTmp = cairo_image_surface_create_from_png("./test.png");
if (xevent.type == MotionNotify && isPressed == true)
{
int tmp_x = xevent.xmotion.x_root;
int tmp_y = xevent.xmotion.y_root;
cairo_save(cr);
cairo_set_source_surface(cr, surfaceTmp, 1, 1);
cairo_paint(cr);
cairo_set_source_rgba(cr, 0, 0, 0, 0.2);
cairo_rectangle(cr, 0, 0, DisplayWidth(xdisplay, scr), DisplayHeight(xdisplay, scr));
cairo_fill(cr);
cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
cairo_rectangle(cr, init_x, init_y, tmp_x - init_x, tmp_y - init_y); // set rectangle
cairo_fill(cr);
cairo_restore(cr);
}
Why do I have this black rectangle? I thought that CAIRO_OPERATOR_CLEAR should delete the shape part beneath.
The desired outcome:
Maybe, since cairo's drawings change pixels directly (=not buffered), once you draw something, there remain no underlying original pixels that can be recovered afterwards. If you'd like to hole the rectangle, try the fill rule: CAIRO_FILL_RULE_EVEN_ODD.
cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD); // The default is CAIRO_FILL_RULE_WINDING.
cairo_set_source_rgba(cr, 0, 0, 0, 0.2);
cairo_rectangle(cr, 0, 0, DisplayWidth(xdisplay, scr), DisplayHeight(xdisplay, scr));
//cairo_fill(cr);
//cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
cairo_rectangle(cr, init_x, init_y, tmp_x - init_x, tmp_y - init_y); // set rectangle
cairo_fill(cr);
This is how I draw a rectangle using GDI+.
Graphics g(hdc);
SolidBrush blueColor((Color(255, 74, 134, 232)));
g.FillRectangle(&blueColor, x, y, width, height);
DeleteObject(&blueColor);
Now I want to add some border-radius, how can I do that?
You can implement this function through GraphicsPath::AddArc and related APIs.
Some code that worked for me:
void GetRoundRectPath(GraphicsPath *pPath, Rect r, int dia)
{
// diameter can't exceed width or height
if(dia > r.Width) dia = r.Width;
if(dia > r.Height) dia = r.Height;
// define a corner
Rect Corner(r.X, r.Y, dia, dia);
// begin path
pPath->Reset();
// top left
pPath->AddArc(Corner, 180, 90);
// tweak needed for radius of 10 (dia of 20)
if(dia == 20)
{
Corner.Width += 1;
Corner.Height += 1;
r.Width -=1; r.Height -= 1;
}
// top right
Corner.X += (r.Width - dia - 1);
pPath->AddArc(Corner, 270, 90);
// bottom right
Corner.Y += (r.Height - dia - 1);
pPath->AddArc(Corner, 0, 90);
// bottom left
Corner.X -= (r.Width - dia - 1);
pPath->AddArc(Corner, 90, 90);
// end path
pPath->CloseFigure();
}
void DrawRoundRect(Graphics* pGraphics, Rect r, Color color, int radius, int width)
{
int dia = 2*radius;
// set to pixel mode
int oldPageUnit = pGraphics->SetPageUnit(UnitPixel);
// define the pen
Pen pen(color, 1);
pen.SetAlignment(PenAlignmentCenter);
// get the corner path
GraphicsPath path;
// get path
GetRoundRectPath(&path, r, dia);
// draw the round rect
pGraphics->DrawPath(&pen, &path);
// if width > 1
for(int i=1; i<width; i++)
{
// left stroke
r.Inflate(-1, 0);
// get the path
GetRoundRectPath(&path, r, dia);
// draw the round rect
pGraphics->DrawPath(&pen, &path);
// up stroke
r.Inflate(0, -1);
// get the path
GetRoundRectPath(&path, r, dia);
// draw the round rect
pGraphics->DrawPath(&pen, &path);
}
// restore page unit
pGraphics->SetPageUnit((Unit)oldPageUnit);
}
More references: RoundRect, WINAPI Edit control with custom border
I'm trying to learn MFC from book: MV C++ Windows Application by Example(2008). There is example app. where I can draw rings filled witch chosen color:
void CRingView::OnDraw(CDC* pDC)
{
CRingDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
PointArray& pointArray = pDoc->GetPointArray();
ColorArray& colorArray = pDoc->GetColorArray();
int iSize = static_cast<int>(pointArray.GetSize());
for (int iIndex = 0; iIndex < iSize; iIndex++)
{
CPoint point = pointArray[iIndex];
COLORREF color = colorArray[iIndex];
CPen pen(PS_SOLID, 0, BLACK);
CBrush brush(color);
pDC->Ellipse(point.x - RADIUS, point.y - RADIUS, point.x + RADIUS, point.y + RADIUS);
CPen* pOldPen = pDC->SelectObject(&pen);
CBrush* pOldBrush = pDC->SelectObject(&brush);
}
}
but there is no color change(always white like bg) even if I do:
CBrush brush(BLACK);
So question is: What I'm doing wrong? I'm using Visual Studio 2013 but with new project there shouldn't be any compatibility errors.
And BLACK is:
static const COLORREF BLACK = RGB(0, 0, 0);
The DC draws with whatever brush, pen, font, etc. objects are currently selected. So SelectObject of the pen and the brush should happen before doing the drawing.