How to write out a greyscale cairo surface to PNG - c

I have a cairo_surface_t of format CAIRO_FORMAT_A8. I want to write out the surfe as a greyscale image, so every pixel has a single byte value of type uchar.
If I use cairo_surface_write_to_png directly on the CAIRO_FORMAT_A8 surface, all I get is an all-black image. I think this is how cairo internally treats the A8 surface - as alpha values, not as greyscale data. I want a single greyscale image, however.
I'd be enough if somebody count point out how to copy the A8 format to all 3 layers of an RGB24 image.
Any help appreciated!

Untested code below. The idea is to create an ARGB-surface and "copy" the A8 surface there via cairo_mask_surface(). If the colors are "swapped", swap the two cairo_set_source_rgb() calls.
cairo_surface_t *s = YOUR_A8_SURFACE;
cairo_t *cr = cairo_create(s);
cairo_push_group_with_content(cr, CAIRO_CONTENT_COLOR_ALPHA);
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_paint(cr);
cairo_set_source_rgb(cr, 0, 0, 0);
cairo_mask_surface(cr, cairo_get_target(cr), 0, 0);
cairo_surface_write_to_png(cairo_get_group_target(cr), "/tmp/foo.png");
/* If you want to continue using the context:
cairo_pattern_destroy(cairo_pop_group(cr)); */
cairo_destroy(cr);

Related

SDL Texture created from memory is rendering only in black and white

I am trying to port a game from DDraw to SDL2.
The original program loads the images and blits them to a backbuffer then flips it to a primary one.
I am thinking that I could technically shortcut part of the process and just grab the backbuffer in memory and then turn it into a texture and blit that to the screen. This kind of works already the only problem is that the screen is black and white.
here is some code. The variable that is holding the backbuffer is the destmemarea
if (SDL_Init(SDL_INIT_EVERYTHING) != 0) {
SDL_Log("Unable to initialize SDL: %s", SDL_GetError());
}
SDL_Window* window = NULL;
SDL_Texture *bitmapTex = NULL;
SDL_Surface *bitmapSurface = NULL;
SDL_Surface *MySurface = NULL;
SDL_DisplayMode DM;
SDL_GetCurrentDisplayMode(0, &DM);
auto Width = DM.w;
auto Height = DM.h;
window = SDL_CreateWindow("SDL Tutorial ", Width = DM.w - SCREEN_WIDTH, 32, SCREEN_WIDTH *4, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
if (window == NULL)
{
printf("Window could not be created! SDL_Error: %s\n", SDL_GetError());
}
SDL_Renderer * renderer = SDL_CreateRenderer(window, -1, 0);
int w, h;
SDL_GetRendererOutputSize(renderer, &w, &h);
SDL_Surface * image = SDL_CreateRGBSurfaceFrom( destmemarea, 640, 0, 32, 640, 0, 0, 0,0);
SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, image);
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);
SDL_Delay(10000);
SDL_DestroyTexture(texture);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
}
Not sure if this helps but this is what is being used for DDRAW fort he looks...
dd.dwWidth = 768;
dd.lPitch = 768;
dd.dwSize = 108;
dd.dwFlags = DDSD_PIXELFORMAT|DDSD_PITCH|DDSD_WIDTH|DDSD_HEIGHT|DDSD_CAPS;
dd.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY|DDSCAPS_OFFSCREENPLAIN;
dd.dwHeight = 656;
dd.ddpfPixelFormat.dwSize = 32;
So, I'm not 100% sure I understand what you are trying to do, but I have a few assumptions.
You said that you're porting your codebase from DDraw, so I assume that the backbuffer you are mentioning is an internal backbuffer that you are allocating, and in the rest of your application are doing your rendering to it.
If I am correct in this assumption, than your current approach is what you need to do, but need to specify correct parameters to SDL_CreateRGBSurfaceFrom
width and height are... width and height in pixels
depth is the amount of bits in a single pixel. This depends on the rest of your rendering code that writes to your memory buffer. If we assume that you're doing a standard RGBA, where each channel is 8 bits, it would be 32.
pitch is the size in bytes for a single row in your surface - should be equal to width * (depth / 8).
the 4 masks, Rmask, Gmask, Bmask, and Amask describe how each of your depth sized pixels distributes channels. Again, depends on how you render to your memory buffer, and the endianness of your target platform. From the documentation, 2 possible standard layouts:
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
rmask = 0xff000000;
gmask = 0x00ff0000;
bmask = 0x0000ff00;
amask = 0x000000ff;
#else
rmask = 0x000000ff;
gmask = 0x0000ff00;
bmask = 0x00ff0000;
amask = 0xff000000;
#endif
Be sure not to forget to free your surface by calling SDL_FreeSurface()
With all that said... I think you are approaching your problem from the wrong angle.
As I stated in my comment, SDL handles double buffering for you. Instead of having custom code that renders to a buffer in memory, and then trying to create a surface from that memory and rendering it to SDLs backbuffer, and calling present... you should skip the middle man and draw directly to SDLs back buffer.
This is done through the various SDL render functions, of which RenderCopy is a member.
Your render loop should basically do 3 things:
Call SDL_RenderClear()
Loop over every object that you want to present to the screen, and use one of the SDL render functions - in the most common case of an image, that would be SDL_RenderCopy. This would mean, throughout your codebase, load your images, create SDL_Surface and SDL_Texture for them, keep those, and on every frame call SDL_RenderCopy or SDL_RenderCopyEx
Finally, you call SDL_RenderPresent exactly once per frame. This will swap the buffers, and present your image to screen.

Texture do not rotate with OpenGL (C)

I am trying to rotate a texture extracted from a video frame (provided by ffmpeg), I have tried the following code :
glTexSubImage2D(GL_TEXTURE_2D,
0,
0,
0,
textureWidth,
textureHeight,
GL_RGBA,
GL_UNSIGNED_BYTE,
//s_pixels);
pFrameConverted->data[0]);
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glTranslatef(0.5,0.5,0.0);
glRotatef(90,0.0,0.0,1.0);
glTranslatef(-0.5,-0.5,0.0);
glMatrixMode(GL_MODELVIEW);
//glDrawTexiOES(-dPaddingX, -dPaddingY, 0, drawWidth + 2 * dPaddingX, drawHeight + 2 * dPaddingY);
glDrawTexiOES(0, 0, 0, drawWidth, drawHeight);
The image is not rotated, do you see the problem ?
From the GL_OES_draw_texture extension specification:
Note also that s, t, r, and q are computed for each fragment as part of DrawTex rendering. This implies that the texture matrix is ignored and has no effect on the rendered result.
You are trying to transform the texture coordinates using the fixed-function texture matrix, but like point sprites, those coordinates are generated per-fragment rather than per-vertex. Thus, that means that nothing you do to the texture matrix is ever going to affect the output of glDrawTexiOES (...).
Consider using a textured quad instead, those will pass through the traditional vertex processing pipeline.

How to use unsigned short in an opengl shader?

I'm trying to upload a texture with unsigned shorts in a shader but it's not working.
I have tried the following:
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, vbt[1]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 640, 480, 0, GL_RED, GL_UNSIGNED_SHORT, kinect_depth);
glUniform1i(ptexture1, 1);
GLenum ErrorCheckValue = glGetError();
I know I'm binding correctly the texture because I get some results by using
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, vbt[1]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 640, 480, 0,
GL_RG, GL_UNSIGNED_BYTE, kinect_depth);
glUniform1i(ptexture1, 1);
GLenum ErrorCheckValue = glGetError();
In particular, I get part of my values in the red channel. I would like to upload the texture as a unsigned byte or as a float. However I don't manage to get the glTexImage2D call correctly. Also, is it possible to something similar using a depth texture? I would like to do some operations on the depth information I get from a kinect and display it.
Your arguments to glTexImage2D are inconsistent. The 3rd argument (GL_RGB) suggests that you want a 3 component texture, the 7th (GL_RED) suggests a one-component texture. Then your other attempt uses GL_RG, which suggests 2 components.
You need to use an internal texture format that stores unsigned shorts, like GL_RGB16UI.
If you want one component, your call would look like this:
glTexImage2D(GL_TEXTURE_2D, 0, GL_R16UI, 640, 480, 0, GL_RED_INTEGER, GL_UNSIGNED_SHORT, kinect_depth);
If you want three components:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16UI, 640, 480, 0, GL_RGB_INTEGER, GL_UNSIGNED_SHORT, kinect_depth);
You also need to make sure that the types used in your shader for sampling the texture match the type of the data stored in the texture. In this example, since you use a 2D texture containing unsigned integer values, your sampler type should be usampler2D, and you want to store the result of the sampling operation (result of texture() call in the shader) in a variable of type uvec4. (paragraph added based on suggestion by Andon)
Some more background on the format/type arguments of glTexImage2D, since this is a source of fairly frequent misunderstandings:
The 3rd argument (internalFormat) is the format of the data that your OpenGL implementation will store in the texture (or at least the closest possible if the hardware does not support the exact format), and that will be used when you sample from the texture.
The last 3 arguments (format, type, data) belong together. format and type describe what is in data, i.e. they describe the data you pass into the glTexImage2D call.
It is mostly a good idea to keep the two formats matched. Like in this case, the data you pass in is GL_UNSIGNED_SHORT, and the internal format GL_R16UI contains unsigned short values. In OpenGL ES it is required for the internal format to match format/type. Full OpenGL does conversion if necessary, which is undesirable for performance reasons, and also frequently not what you want because the precision of the data in the texture won't be the same as the precision of your original data.

Clear cairo text in gtk_window

I'm have a trouble with a cairo text.
I write some lines in a gtk_window:
cr = gdk_cairo_create(window->window);
cairo_set_source_rgb(cr, 255, 255, 255);
cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size(cr, 14.0);
cairo_move_to(cr, 90.0, 85.0);
cairo_show_text(cr, "Terror");
cairo_set_font_size(cr, 12.0);
cairo_set_source_rgb(cr, 30, 254, 145);
cairo_move_to(cr, 90.0, 105.0);
cairo_show_text(cr, "Underdogs");
cairo_move_to(cr, 90.0, 120.0);
cairo_show_text(cr, "Disziplin");
cairo_destroy(cr);
The problem is that this text should be dynamic, but if I call more than one time the function that writes the text, lines is overlapped.
Is there any method that flushes the previous text?
Thanks!
You have to overwrite the text with the background color :)
If you want to clear your surface to a uniform, opaque color then it is quite straightforward:
/* Set surface to opaque color (r, g, b) */
cairo_set_source_rgb (cr, r, g, b);
cairo_paint (cr);
However, what if you want to clear the surface to something other than an opaque color. Simply modifying the above code to use "cairo_set_source_rgba (cr, 0, 0, 0, 0);" will not work since cairo uses the OVER compositing operator by default, and blending something entirely transparent OVER something else has no effect at all. Instead, you can use the SOURCE operator which copies both color and alpha values directly from the source to the destination instead of blending:
/* Set surface to translucent color (r, g, b, a) */
cairo_set_source_rgba (cr, r, g, b, a);
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
cairo_paint (cr);
Of course, you won't want to forget to set the default CAIRO_OPERATOR_OVER again when you're finished. And the most convenient habit for doing that is to just use cairo_save/cairo_restore around the whole block:
/* Set surface to translucent color (r, g, b, a) without disturbing graphics state. */
cairo_save (cr);
cairo_set_source_rgba (cr, r, g, b, a);
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
cairo_paint (cr);
cairo_restore (cr);
Finally, to clear a surface to all transparent, one could simply use CAIRO_OPERATOR_CLEAR instead of CAIRO_OPERATOR_SOURCE, in which case the call to cairo_set_source_rgba would not be needed at all, (the CLEAR operator always sets the destination to 0 in every channel regardless of what the source pattern contains). But the above approach with CAIRO_OPERATOR_SOURCE is a more general way to clear the surface since it allows for "clearing" to a translucent color such as 50% red rather than just clearing to entirely transparent.
source: https://www.cairographics.org/FAQ/#clear_a_surface

How do I fill the screen with a rectangle in OpenGL ortho mode?

I have a single 640x480 texture that needs to fill the screen. So far, I can make it work with a square texture, but not a rectangular one.
glViewport(0, 0, display->w, display->h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
double aspectRatio = (double)display->w / (double)display->h;
if (display->w <= display->h)
glOrtho(-1, 1, -1 / aspectRatio, 1 / aspectRatio, -1, 1);
else
glOrtho(-1 * aspectRatio, 1 * aspectRatio, -1, 1, -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
What modifications do I need to make so that it will fit any texture to the screen, regardless of its aspect ratio?
This may have some relevance.
Tiling texture bmp file as texture onto a rectangle in OpenGL?
You may wish to consider ARB extension texture rectangle as an alternative approach to (assuming glTexImage2D?) http://glprogramming.com/red/chapter09.html

Resources