Cairo image blurred when scaled - c

I have the following cairo code:
cairo_set_source_rgba(cr, 1, 1, 1, 1);
cairo_rectangle(cr, 0, 0, WINDOW_SIZE, WINDOW_SIZE);
cairo_fill(cr);
cairo_scale(cr, 8, 8);
draw_image(cr, "q.png", 5, 5);
And
void draw_image(cairo_t* cr, char* img_name, int x, int y)
{
cairo_translate(cr, x, y);
cairo_surface_t* img = cairo_image_surface_create_from_png(img_name);
cairo_set_source_surface(cr, img, 0, 0);
cairo_paint(cr);
cairo_translate(cr, -x, -y);
}
q.png is a 5x5 image:
But when the program is run, the image is slightly blurred:
I have already tried
cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
but it does not work.
Is there any way to fix this problem?

This is because of how the image is scaled up. Instead of setting a source surface directly, create a pattern out of the surface with cairo_pattern_create_for_surface(), call cairo_pattern_set_filter() on it to set the scaling mode, and then call cairo_set_source() to load the pattern. See the documentation for cairo_filter_t for the scaling modes. CAIRO_FILTER_NEAREST, for example, will give you a normal pixel zoom with no blurring or other transformations.

Related

Simple Stretched Raylib Fullscreen Option?

The code for a simple Raylib program is listed below (based on the Raylib example shapes_logo_raylib). Running the program shows a version of the Raylib logo: a black square outline which fills about a third of the (800x450) window.
It's not hard to make a fullscreen version of the program, with calls such as GetCurrentMonitor(), SetWindowSize(), GetMonitorWidth(), GetMonitorHeight()
SetConfigFlags(FLAG_WINDOW_RESIZABLE), or ToggleFullscreen(). But then, while the black square remains a similar size as before, it occupies (top left) a much smaller proportion of the larger (fullscreen) window. Is there an option to display a larger "stretched" version of the original windowed image on the fullscreen window?
#include "raylib.h"
int main(void)
{
InitWindow(800, 450, "raylib [shapes] example - raylib logo using shapes");
while (!WindowShouldClose())
{
BeginDrawing()
ClearBackground(RAYWHITE);
DrawRectangle(screenWidth/2 - 128, screenHeight/2 - 128, 256, 256, BLACK);
DrawRectangle(screenWidth/2 - 112, screenHeight/2 - 112, 224, 224, RAYWHITE);
DrawText("raylib", screenWidth/2 - 44, screenHeight/2 + 48, 50, BLACK);
EndDrawing();
}
CloseWindow();
return 0;
}
I made the following additions to your code to make it work on any window size. It draws the stuff you want to draw onto a RenderTexture2D and then draws said texture onto the screen. I've only tested it with resizable windows, but it should work in any window mode, including exclusive fullscreen.
In short:
Request render texture using LoadRenderTexture(int, int)
Use BeginTextureMode(RenderTexture2D) and EndTextureMode() to draw onto the texture
Draw the texture using DrawTexturePro(Texture2D, Rectangle, Rectangle, Vector2, float, Color), the first rectangle is the size of the texture, the second the size of the screen. If it looks mirrored, invert the height of the input texture.
Unload the render texture when done.
I added comments to all my additions/changes to highlight what needs to be changed.
#include "raylib.h"
// This was just missing from your code, but I wanted to show a compilable version of the code
int screenWidth = 800;
int screenHeight = 450;
int main(void)
{
InitWindow(800, 450, "raylib [shapes] example - raylib logo using shapes");
// This should use the flag FLAG_FULLSCREEN_MODE which results in a possible ToggleFullscreen() call later on
SetWindowState(FLAG_WINDOW_RESIZABLE);
// Request a texture to render to. The size is the screen size of the raylib example.
RenderTexture2D renderTexture = LoadRenderTexture(screenWidth, screenHeight);
while (!WindowShouldClose())
{
// Instead of using BeginDrawing() we render to the render texture. Everything else stays unchanged
BeginTextureMode(renderTexture);
ClearBackground(RAYWHITE);
DrawRectangle(screenWidth / 2 - 128, screenHeight / 2 - 128, 256, 256, BLACK);
DrawRectangle(screenWidth / 2 - 112, screenHeight / 2 - 112, 224, 224, RAYWHITE);
DrawText("raylib", screenWidth / 2 - 44, screenHeight / 2 + 48, 50, BLACK);
// We need to end the texture mode separately
EndTextureMode();
// Let's draw the texture. The source rect is the size of the texture, the destination rect is of the same size as the screen. For some reason, the texture was flipped vertically, so I had to invert the source rects "height" to flip the UV.
BeginDrawing();
DrawTexturePro(
renderTexture.texture,
Rectangle{ 0, 0, static_cast<float>(renderTexture.texture.width), static_cast<float>(-renderTexture.texture.height) },
Rectangle{ 0, 0, static_cast<float>(GetScreenWidth()), static_cast<float>(GetScreenHeight()) },
Vector2{ 0, 0 },
0,
WHITE);
EndDrawing();
}
// Unload the texture handle again to make a clean exit.
UnloadRenderTexture(renderTexture);
CloseWindow();
return 0;
}
I hope this answers your question.

Multiple sequentual glPushAttrib/glPopAttrib incorrect behaviour

#include <GL/glut.h>
void reshape(int w, int h){
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(-w/2, w - w/2, -h/2, h - h/2);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void display(){
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1, 1, 1);
glPushAttrib(GL_ALL_ATTRIB_BITS);
glColor3f(1, 0, 0);
glPopAttrib();
glRecti(0, 0, 10, 10); // draws white rect
// Commenting the line makes next rect white
// Uncommenting the line makes next rect red
glTranslatef(0, 0, 0);
glPushAttrib(GL_ALL_ATTRIB_BITS);
glColor3f(1, 0, 0);
glPopAttrib();
glRecti(20, 20, 30, 30); // draws white or red rect
glutSwapBuffers();
}
int main (int argc, char * argv[]){
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA);
glutInitWindowSize(800, 600);
glutCreateWindow("OpenGL lesson 1");
glutReshapeFunc(reshape);
glutDisplayFunc(display);
glutMainLoop();
return 0;
}
The code above is full compilable programm that renders two rectangles. The programm produce different results depending on weather line "glTranslatef(0, 0, 0);" commented or not. Is that a bug, or misusage of OpenGL?
Is that a bug, or misusage of OpenGL?
That is just a bug. The spec clearly states that glColor will set the current RGBA color value, which will become the vertex's color the next time a vertex is formed. This would happen by the next glVertex call inside a glBegin/glEnd block. glRect is specified to be equivalent to glBegin(); glVertex() [4x]; glEnd().
The current RGBA color value is part of the GL_CURRENT_BIT attribute group, and is of course included in GL_ALL_ATTRIB_BITS. glTranslate is to only affect the top element of the currenttly selected matrid stack. The correct output for this code are two wihite rectangles, no matter if a glTranslate is there or not.
However, all this stuff is horribly outdated, and deprecated since 2008.

How to zoom in on a point (the math behind it)

I'm working on a fractal graphic. I need to be able to zoom in on a specific point.
Here's what I've got so far. If you keep the mouse in the same position for the whole zoom, it works. But if you zoom part of the way then move the mouse to a new position and try to zoom some more from there, it starts jumping all over.
scale_change = zoom * ((button == SCROLL_DOWN) ? ZOOM_INC : -ZOOM_INC);
zoom += scale_change;
center->x -= (mouse->x - (IMG_SIZE / 2)) * scale_change;
center->y -= (mouse->y - (IMG_SIZE / 2)) * scale_change;
I assume some part of it is over-simplistic? There's some variable I'm not accounting for? It does work if you don't move the mouse, though.
The best approach is probably to use a transform matrix to both scale the image and find out what point in the scaled image your mouse is over so you can transform based on that point.
I know you're working in C, but I created an example in js, because it allows me to demonstrate working code easier. Click on the image and use Z and X to zoom in and out. https://jsfiddle.net/7ekqg8cb/
Most of the code is implementing matrix multiplication, matrix inversion and matrix point transformation. The important part is:
var scaledMouse = transformPoint(mouse);
matrixMultiplication([1, 0, scaledMouse.x,
0, 1, scaledMouse.y,
0, 0, 1]);
var scale = 1.2;
if (direction) {
matrixMultiplication([scale, 0, 0,
0, scale, 0,
0, 0, 1]);
}
else {
matrixMultiplication([1/scale, 0, 0,
0, 1/scale, 0,
0, 0, 1]);
}
matrixMultiplication([1, 0, -scaledMouse.x,
0, 1, -scaledMouse.y,
0, 0, 1]);
transformPoint uses the inverse of the transform matrix to find out where the mouse is relative to the transformed image. Then the transform matrix is translated, scaled and translated back, to scale the transform matrix around that point.

Is it possible to create an XOR pen like DrawFocusRect()?

The Win32 GDI DrawFocusRect(HDC, const RECT*) function draws the dotted outline of a rectangle on the desired devince context. The cool thing about this function is it draws the dots using an XOR function so that when you call it a second time on the same device context and rectangle, it erases itself:
RECT rc = { 0, 0, 100, 100 };
DrawFocusRect(hdc, &rc); // draw rectangle
DrawFocusRect(hdc, &rc); // erase the rectangle we just drew
I want to achieve the same dotted line effect as DrawFocusRect() but I just want a line, not a whole rectangle. I tried doing this by passing a RECT of height 1 to DrawFocusRect() but this doesn't work because it XORs the "bottom line" of the rectange on top of the top line so nothing gets painted.
Can I create a plain HPEN that achieves the same effect as DrawFocusRect() so I can draw just a single line?
As #IInspectable commented, you want to use SetROP2(). The other half of the battle is creating the correct pen. Here is how the whole thing shakes out:
HPEN create_focus_pen()
{
LONG width(1);
SystemParametersInfo(SPI_GETFOCUSBORDERHEIGHT, 0, &width, 0);
LOGBRUSH lb = { }; // initialize to zero
lb.lbColor = 0xffffff; // white
lb.lbStyle = BS_SOLID;
return ExtCreatePen(PS.GEOMETRIC | PS.DOT, width, &lb, 0, 0);
}
void draw_focus_line(HDC hdc, HPEN hpen, POINT from, POINT to)
{
HPEN old_pen = SelectObject(hdc, hpen);
int old_rop = SetROP2(R2_XORPEN);
MoveToEx(hdc, from.x, from.y, nullptr);
LineTo(hdc, to.x, to.y);
SelectObject(hdc, old_pen);
SetROP2(old_rop);
}

OpenGL 2D texture rendering too large, glViewport broken

I've written a small tiling game engine with OpenGL and C, and I can't seem to figure out what the problem is. My main loop looks like this:
void main_game_loop()
{
(poll for events and respond to them)
glClear(GL_COLOR_BUFFER_BIT);
glPushMatrix();
draw_block(WALL, 10, 10);
}
draw_block:
void draw_block(block b, int x, int y)
{
(load b's texture from a hash and store it in GLuint tex)
glPushMatrix();
glTranslatef(x, y, 0);
glBindTexture(GL_TEXTURE_2D, tex);
glBegin(GL_QUADS);
//BLOCK_DIM is 32, the width and height of the texture
glTexCoord2i(0, 0); glVertex3f(0, 0, 0);
glTexCoord2i(1, 0); glVertex3f(BLOCK_DIM, 0, 0);
glTexCoord2i(1, 1); glVertex3f(BLOCK_DIM, BLOCK_DIM, 0);
glTexCoord2i(0, 1); glVertex3f(0, BLOCK_DIM, 0);
glEnd();
glPopMatrix;
}
initialization function: (called before main_game_loop)
void init_gl()
{
glViewport(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glClearColor(0, 0, 0, 0);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
When run, this displays a black screen. However, if I remove the glViewport call, it seemingly displays the texture, but huge and in the corner of the window. Screenshot:
The texture IS being drawn correctly, because if I scale out by a huge factor, I can see the entire image. The y-axis also seems to be flipped from what I used in the gluOrtho2D call (discovered by making events add or subtract from x/y coordinates of the image, subtracting from the y coordinate causes the image to move downward). I'm starting to get frustrated, because this is the simplest possible example I can think of. I'm using SDL, and am passing SDL_OPENGL to SDL_SetVideoMode. What is going on here?
Looks like a problem with glViewport, but just to be sure, did you try clearing the color buffer to purple?
I've always thought of glViewport as a video/windowing function, not actually part of OpenGL itself, because it is the intermediate between the window manager and the OpenGL subsystem, and it uses window coordinates. As such, you should probably look at it along with the other SDL video calls. I suggest updating the question with the full code, or at least with those parts relevant to the video/window subsystem.
Or is it that you omitted to call glViewport after a resize?
You should also try your code without SDL_FULLSCREEN and/or with a smaller window. I usually start with a 512x512 or 640x480 window until I get the viewport and some basic controls right.
the first two parameters of glViewPort specifies the lower left of the view
http://www.opengl.org/sdk/docs/man/xhtml/glViewport.xml
You can try
glViewport(0, SCREEN_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT);
For gluOrtho2D, the parameters are left, right, top, bottom
so I would probably use
gluOrtho2D(0, SCREEN_WIDTH, 0, SCREEN_HEIGHT);

Resources