Cairo Saved Paths with Transforms Render "Backwards" - cairo

The below code sample is in Lua, intended to be used with Conky. My thought was to compute a complicated path, save it, then render it twice -- once as a filled region, then again as a stroked path, thus creating an outlined region.
The problem is that the saved path seems to render "backwards" compared to a straightforward rendering.
The first section creates a rotated L-shape in green, which appears as expected.
The second section attempts to create the same L-shape as a saved path, then render it in red. The resulting path does not come out rotated, but since I didn't call cairo_rotate() before appending the path, I suppose this makes some sense.
The third section renders a set of progressively rotated L-shapes in cyan, which appears as expected.
The fourth section attempts to do the same thing as the third section, this time using saved paths, and rendering in yellow. But the paths come out going in the wrong direction.
My guess is that the cairo transformation functions apply their transforms to the unstroked path components (if any) accumulated in the cairo_t context. But I don't quite grasp why it would come out "forwards" one way and "backwards" the other way.
What am I misunderstanding about Cairo paths and transforms?
function tests (cr)
local wiggle
local mat_base = cairo_matrix_t:create ()
tolua.takeownership (mat_base)
cairo_set_line_width (cr, 4)
cairo_identity_matrix (cr)
---
-- Direct stroke of L-shaped figure.
cairo_save (cr)
cairo_translate (cr, 128, 128)
cairo_new_path (cr)
cairo_rotate (cr, math.pi / 6)
cairo_move_to (cr, 150, 0)
cairo_line_to (cr, 0, 0)
cairo_line_to (cr, 0, 50)
cairo_set_source_rgb (cr, 0, 1, 0)
cairo_stroke (cr)
cairo_restore (cr)
---
-- Capture L-shaped figure as path, then render.
cairo_save (cr)
cairo_translate (cr, 384, 128)
cairo_new_path (cr)
cairo_rotate (cr, math.pi / 6)
cairo_move_to (cr, 150, 0)
cairo_line_to (cr, 0, 0)
cairo_line_to (cr, 0, 50)
wiggle = cairo_copy_path (cr)
cairo_restore (cr)
cairo_save (cr)
cairo_translate (cr, 384, 128)
cairo_new_path (cr)
cairo_append_path (cr, wiggle)
cairo_set_source_rgb (cr, 1, 0, 0)
cairo_stroke (cr)
cairo_path_destroy (wiggle)
cairo_restore (cr)
---
-- Direct stroke of a set of rotated L-shaped figures.
cairo_save (cr)
cairo_translate (cr, 640, 128)
cairo_get_matrix (cr, mat_base)
cairo_new_path (cr)
for i = 0, 4 do
cairo_set_matrix (cr, mat_base)
cairo_rotate (cr, i * math.pi / 6)
cairo_move_to (cr, 150, 0)
cairo_line_to (cr, 0, 0)
cairo_line_to (cr, 0, 50)
end
cairo_set_source_rgb (cr, 0, 1, 1)
cairo_stroke (cr)
cairo_restore (cr)
---
-- Capture rotated L-shaped figures as path, then render.
cairo_save (cr)
cairo_translate (cr, 896, 128)
cairo_get_matrix (cr, mat_base)
cairo_new_path (cr)
for i = 0, 4 do
cairo_set_matrix (cr, mat_base)
cairo_rotate (cr, i * math.pi / 6)
cairo_move_to (cr, 150, 0)
cairo_line_to (cr, 0, 0)
cairo_line_to (cr, 0, 50)
end
wiggle = cairo_copy_path (cr)
cairo_restore (cr)
cairo_save (cr)
cairo_new_path (cr)
cairo_translate (cr, 896, 128)
cairo_append_path (cr, wiggle)
cairo_set_source_rgb (cr, 1, 1, 0)
cairo_stroke (cr)
cairo_path_destroy (wiggle)
cairo_restore (cr)
--
end

When you call cairo_copy_path, the path is expressed in the current user space. So if you first draw some path and then afterwards call cairo_rotate(), cairo_copy_path(), then the resulting path will include the very same last rotation.
So in the red example, you copy the path before undoing the rotation and hence it appears like there is no rotation.
In the yellow example, you copy the path after all the rotation and thus the first rotations are in a different "direction" than in the blue one.
Try replacing a = cairo_copy_path(cr) with cairo_save(cr) ; cairo_identity_matrix(cr) ; a = cairo_copy_path(cr) ; cairo_restore(cr). That way you get the path in surface coordinates which seems to be more like what you are expecting.

Related

flutter: transform path in CustomPaint with Float64List Matrix4

I was trying to rotate some path and did not find a snippet and matrix4 in general is not well documented. So in case someone else has this problem, this is what I ended up with
First of all, here is a nice read about the math behind it and how to use matrix4.
In my demo I used an arrow tip. Since sequence matters, I paint it at (0,0), then I rotate it around the z-axis and finally I move the tip to the requested spot.
Path _getArrow (Offset offset, double alpha) {
Path a = Path();
a.moveTo(ARROWSIZE, - ARROWSIZE);
a.lineTo(0, 0);
a.lineTo(ARROWSIZE, ARROWSIZE);
final translateM = Float64List.fromList([
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
offset.dx, offset.dy, 0, 1]
);
final rotateM = Float64List.fromList([
cos(alpha), sin(alpha), 0, 0,
-sin(alpha), cos(alpha), 0, 0,
0, 0, 1, 0,
0, 0, 0, 1]
);
final b = a.transform(rotateM);
final c = b.transform(translateM);
return c;
}

Cairo only render to specific color component

I am using Cairo and would like to render one color component at a time. For example, if I render a set of blue rectangles and then render a set of red rectangles, I want where they overlap to be purple rather than red.
Using set_source_rgb(ctx, 0.0, 1.0, 0.0) doesn't work, because it will overwrite the other channels with zeros. Using transparency doesn't work either, as it equally effects all channels. I would like a way to only render to one channel.
Is that possible? Thank you.
Use CAIRO_OPERATOR_ADD instead of CAIRO_OPERATOR_OVER (the default):
#include <cairo.h>
int main() {
cairo_surface_t *s = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 20, 20);
cairo_t *cr = cairo_create(s);
cairo_set_operator(cr, CAIRO_OPERATOR_ADD);
/* Render blue */
cairo_set_source_rgb(cr, 0, 0, 1);
cairo_rectangle(cr, 0, 0, 15, 15);
cairo_fill(cr);
/* Render red */
cairo_set_source_rgb(cr, 1, 0, 0);
cairo_rectangle(cr, 5, 5, 15, 15);
cairo_fill(cr);
cairo_surface_write_to_png(s, "out.png");
cairo_destroy(cr);
cairo_surface_destroy(s);
return 0;
}

Polygons transparency

I need to bind texture on 2 crossed polygons and make them(polygons) invisible (with alpha=0). But textures are transparent with polygons.
Is is possible to make transparent only polygons without their textures?
By this way i bind textute
Gl.glEnable(Gl.GL_BLEND);
Gl.glEnable(Gl.GL_ALPHA_TEST);
Gl.glBlendFunc(Gl.GL_SRC_ALPHA, Gl.GL_ONE_MINUS_SRC_ALPHA);
Gl.glColor4d(255,255,255,0.1);
Gl.glBegin(Gl.GL_QUADS);
Gl.glTexCoord2f(1, 0); Gl.glVertex3d(2, 2, 3);
Gl.glTexCoord2f(0, 0); Gl.glVertex3d(4, 2, 3);
Gl.glTexCoord2f(0, 1); Gl.glVertex3d(4, 4, 3);
Gl.glTexCoord2f(1, 1); Gl.glVertex3d(2, 4, 3);
Gl.glEnd();
Image
I need smth like on the left part of the img.
I found the solution.
Load png!! image
GL.glBindTexture(GL.GL_TEXTURE_2D, this.texture[i]);
Gl.glTexEnvi(Gl.GL_TEXTURE_ENV, Gl.GL_TEXTURE_ENV_MODE, Gl.GL_REPLACE);
Gl.glAlphaFunc(Gl.GL_LESS, 0.2f);
GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
GL.glTexImage2D(GL.GL_TEXTURE_2D, 0, (int)Gl.GL_RGBA, image[i].Width, image[i].Height, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, bitmapdata.Scan0);
And drow object:
Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);
GL.glEnable(GL.GL_BLEND); // Enable Blending
GL.glDisable(GL.GL_DEPTH_TEST);
GL.glBlendFunc(Gl.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
GL.glBindTexture(GL.GL_TEXTURE_2D, texture[0]);
Gl.glBegin(Gl.GL_QUADS);
Gl.glTexCoord2f(1, 0); Gl.glVertex3d(2, 2, 3);
Gl.glTexCoord2f(0, 0); Gl.glVertex3d(4, 2, 3);
Gl.glTexCoord2f(0, 1); Gl.glVertex3d(4, 6, 3);
Gl.glTexCoord2f(1, 1); Gl.glVertex3d(2, 6, 3);
Gl.glEnd();
And you will get your image without any background.

Batching OpenGL sprites

i have been learning OpenGL and have been able to create a successful 2d drawing system using triangle strips. I wrote a particle generator to test batching the geometry and everything works well, i'm able to render 30k+ vertices at 60 fps on an iPhone 5. I use degenerative triangles to connect the particles and draw them at once. What i am trying to accomplish is batch rendering without using degenerative triangles as that would reduce the amount of data being sent to the GPU by 1/3 the amount which would be huge.
I am attempting to use glDrawElements with triangles to draw 2 sprites with the following code
//the vertices for the square (2d)
GLfloat square[] = {
50, 50, //bottom left
100, 50, //bottom right
50, 100, //top left
100, 100, //top right
150, 200, //bottom left
200, 150, //bottom right
150, 200, //top left
200, 200 //top right
};
//texture coords
GLfloat tex[] = {
0,0,
1,0,
0,1,
1,1,
0,0,
1,0,
0,1,
1,1
};
GLubyte indices[] =
{
0,2,3,
0,3,1,
0,2,3,
0,3,1
};
//actual drawing code
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, GL_FALSE, 0, square);
glVertexAttribPointer(ATTRIB_TEX, 2, GL_FLOAT, GL_FALSE, 0, tex);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, image);
glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_BYTE, indices);
This code draws the first image without issue, but the second image is distorted. I have been looking online but have been unable to figure out how to make this work correctly.
First of all, you're currently using the same indices for both quads, so the second image shouldn't even be visible at all.
Other than that your vertex data for the second quad is messed, you have the point (150, 200) two times, the first one should probably be (150, 150).

Cairo Radial Gradient

I'm using a radial gradient in Cairo, but I'm not getting the expected results. The radial gradient I'm getting is much less fuzzy than I'd expect and I can't seem to fiddle with the color stops in order to get the desired results. Here is the code:
cairo_pattern_t *pat;
pat = cairo_pattern_create_radial(100.0, 100.0, 0.0, 100.0, 100.0, 20.0);
cairo_pattern_add_color_stop_rgba(pat, 0, 0, 0, 0, 1);
cairo_pattern_add_color_stop_rgba(pat, 1, 0, 0, 0, 0);
Here is an image of what I'm talking about.
The #cairo IRC channel suggested (Thanks Company!) to use cairo_mask() instead of cairo_paint() to draw the gradient. That results in a squared instead of linear progression.
I did the following in lua. Sorry for the language, but it's easier to prototype something. This maps 1:1 to the C API and shouldn't be hard to translate:
cairo = require("lgi").cairo
s = cairo.ImageSurface(cairo.Format.ARGB32, 200, 100)
c = cairo.Context(s)
c:set_source_rgb(1, 1, 1)
c:paint()
p = cairo.Pattern.create_radial(50, 50, 0, 50, 50, 20)
p:add_color_stop_rgba(0, 0, 0, 0, 1)
p:add_color_stop_rgba(1, 0, 0, 0, 0)
c:save()
c:rectangle(0, 0, 100, 100)
c:clip()
c.source = p
c:paint()
c:restore()
p = cairo.Pattern.create_radial(50, 50, 2, 50, 50, 25)
p:add_color_stop_rgba(0, 0, 0, 0, 1)
p:add_color_stop_rgba(1, 0, 0, 0, 0)
c:translate(100, 0)
c:save()
c:rectangle(0, 0, 100, 100)
c:clip()
c.source = p
c:mask(p)
c:restore()
s:write_to_png("test.png")
To me, the second circle (The one that was cairo_mask()'d with a black source) looks a lot more like what you want: