Failing to overwrite antialiased pixels in Cairo - cairo

While using Cairo 1.14.6 for display purposes, I found that overwriting the very same path with another color does not necessarily overwrite all pixels, and leaves undesirable artifacts behind.
As evidence of my claim I offer this output from a short self-contained example, the source for which follows further below.
An explanation of the six parts of the image, from left to right:
Original shape stroked in blue.
Original shape overwritten in RGBA white.
Original shape overwritten in RGB white.
Original shape overwritten in RGBA white with CAIRO_OPERATOR_SOURCE mode.
Original shape overwritten in RGBA white with CAIRO_OPERATOR_SOURCE mode and CAIRO_ANTIALIAS_NONE.
Original shape overwritten in RGBA white with CAIRO_OPERATOR_SOURCE mode and CAIRO_ANTIALIAS_BEST.
The image was generated from the following code:
#include "cairo/cairo.h"
#define M_PI 3.14159265358979323846
void draw_shape(cairo_t* cr, int x, int y) {
cairo_arc(cr, 50 + x, 50 + y, 48, -M_PI, -M_PI / 2);
cairo_stroke(cr);
cairo_move_to(cr, x + 2, y + 2);
cairo_line_to(cr, x + 48, y + 48);
cairo_stroke(cr);
}
int main(int argc, char** argv) {
int x = 0;
int y = 0;
cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 300, 50);
cairo_t* cr = cairo_create(surface);
/* Draw a white background and a few shapes to overwrite */
cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
cairo_paint(cr);
cairo_set_source_rgba(cr, 0.0, 0.0, 1.0, 1.0);
draw_shape(cr, x, y); x += 50;
draw_shape(cr, x, y); x += 50;
draw_shape(cr, x, y); x += 50;
draw_shape(cr, x, y); x += 50;
draw_shape(cr, x, y); x += 50;
draw_shape(cr, x, y); x += 50;
x = 50;
/* Leftmost shape is left unchanged for reference */
/* Stroke in RGBA opaque white */
cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
draw_shape(cr, x, y); x += 50;
/* Stroke in RGB white */
cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
draw_shape(cr, x + 0, y); x += 50;
/* Stroke in opaque white without blending */
cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
draw_shape(cr, x, y); x += 50;
/* Stroke in opaque white without blending, with no antialiasing */
cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
draw_shape(cr, x, y); x += 50;
/* Stroke in opaque white without blending, with best antialiasing */
cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_set_antialias(cr, CAIRO_ANTIALIAS_BEST);
draw_shape(cr, x, y); x += 50;
/* Write the results to a file */
cairo_surface_write_to_png(surface, "output.png");
return 0;
}
It doesn't make intuitive sense to me that overwriting the very same shape would not overwrite all of its pixels, especially if I force it into non-blending CAIRO_OPERATOR_SOURCE mode. The results are the same on the framebuffer that constitutes my actual surface, so this is not an issue with the backend.
Cairo is usually so good at what it does that I'm very surprised at this. Is there no way to overwrite an anti-aliased shape exactly in Cairo?

What I was trying to do is apparently not possible. I posted my question to the Cairo mailing list and was offered two options:
Keep a copy of the original pixels before drawing over them: "Anti-aliasing involves blending. If you don’t want anti-aliasing, turn it off." (Link)
Draw at a much higher resolution: "The only real solution is to draw a much higher resolution with coverage rounded to exactly zero or one for each pixel." (Link)
More specifically:
All that is stored in the pixel from the first drawing is what
percentage of the pixel was covered by the shape. It does not remember
exactly what parts of the pixel are covered.
Since antialiasing necessarily involves blending, and since Cairo does not remember what part of a subpixel led to the blending, it has no way of knowing how to undo that blending.

Related

What are the meanings of itextpdf pdfcontentbyte addtemplate's parameters

I am using itextpdf to merge some pdfs to a single one.
What are the meanings of itextpdf pdfcontentbyte addtemplate's parameters,there is no docs to describe them.
public void addTemplate(PdfTemplate template,
double a, double b, double c, double d, double e, double f)
The six values a, b, c, d, e, and f are elements of a matrix that has three rows and three columns.
You can use this matrix to express a transformation in a two-dimentional system.
Carrying out this multiplication results in this:
x' = a * x + c * y + e
y' = b * x + d * y + f
The third column in the matrix is fixed: you're working in two dimensions, so you don't need to calculate a new z coordinate.
When studying analytical geometry in high school, you've probably learned how to apply transformations to objects.
In PDF, we use a slightly different approach: instead of transforming objects, we transform the coordinate system.
The e and the f value can be used for a translation. The a, b, c, and d value can be used for a rotation and/or scaling operation.
By default the Current Transformation Matrix (CTM) is:
With the addTemplate() method, you can add a Form XObject to a canvas and define a position using e, f, e.g:
canvas.addTemplate(template, 36, 36);
This will add template at coordinate x = 36; y = 36.
By introducing a, b, c, and d, you can also rotate and/or scale the template.
Update: as mentioned in the comments, you might want to use the overloaded methods that accept an AffineTransform parameter if you don't like the Algebra of the transformation matrix.
the code below did the trick,thank for the guys who helped me.
FileInputStream pdfInput = new FileInputStream(pdf);
PdfReader pdfReader = new PdfReader(pdfInput);
for (int index = 1; index <= pdfReader.getNumberOfPages(); index++) {
main.newPage();
PdfImportedPage page = pdfWriter.getImportedPage(pdfReader,
index);
Rectangle pagesize = pdfReader.getPageSizeWithRotation(index);
float oWidth = pagesize.getWidth();
float oHeight = pagesize.getHeight();
float scale = getScale(oWidth, oHeight);
float scaledWidth = oWidth * scale;
float scaledHeight = oHeight * scale;
int rotation = pagesize.getRotation();
AffineTransform transform = new AffineTransform(scale, 0, 0, scale, 0, 0);
switch (rotation) {
case 0:
cb.addTemplate(page, transform);
break;
case 90:
AffineTransform rotate90 = new AffineTransform(0, -1f, 1f, 0, 0, scaledHeight);
rotate90.concatenate(transform);
cb.addTemplate(page, rotate90);
break;
case 180:
AffineTransform rotate180 = new AffineTransform(-1f, 0, 0, -1f, scaledWidth,
scaledHeight);
rotate180.concatenate(transform);
cb.addTemplate(page, rotate180);
break;
case 270:
AffineTransform rotate270 = new AffineTransform(0, 1f, -1f, 0, scaledWidth, 0);
rotate270.concatenate(transform);
cb.addTemplate(page, rotate270);
break;
default:
cb.addTemplate(page, scale, 0, 0, scale, 0, 0);
}
}
private static float getScale(float width, float height) {
float scaleX = PageSize.A4.getWidth() / width;
float scaleY = PageSize.A4.getHeight() / height;
return Math.min(scaleX, scaleY);
}

How to add a shading pattern to a custom shape

I have drawn an equilateral triangle as follows using iText
canvas.setColorStroke(BaseColor.BLACK);
int x = start.getX();
int y = start.getY();
canvas.moveTo(x,y);
canvas.lineTo(x + side,y);
canvas.lineTo(x + (side/2), (float)(y+(side*Math.sin(convertToRadian(60)))));
canvas.closePathStroke();
I wish to multi color gradient in this shape i.e. fill it with shading comprising of BaseColor.PINK and BaseColor.BLUE. I just can't find a way to do this with iText ?
I've created an example called ShadedFill that fills the triangle you are drawing using a shading pattern that goes from pink to blue as show in the shaded_fill.pdf:
PdfContentByte canvas = writer.getDirectContent();
float x = 36;
float y = 740;
float side = 70;
PdfShading axial = PdfShading.simpleAxial(writer, x, y,
x + side, y, BaseColor.PINK, BaseColor.BLUE);
PdfShadingPattern shading = new PdfShadingPattern(axial);
canvas.setShadingFill(shading);
canvas.moveTo(x,y);
canvas.lineTo(x + side, y);
canvas.lineTo(x + (side / 2), (float)(y + (side * Math.sin(Math.PI / 3))));
canvas.closePathFillStroke();
As you can see, you need to create a PdfShading object. I created an axial shading that varies from pink to blue from the coordinate (x, y) to the coordinate (x + side, y). With this axial shading, you can create a PdfShadingPattern that can be used as a parameter of the setShadingFill() method to set the fill color for the canvas.
See ShadedFill for the full source code.

Textured layers randomly switch position

In my project I want to create 5 textured layers.
Each layer is made out of 4 textured rectangles each. These four parts of a layer are arranged so that it looks like one big texture.
The layers are partly transparent and are arranged in front of each other to create a three dimensional look.
When I run the project with only one layer enabled, everything looks fine, but as soon as I add a second (or more) layers everything gets chaotic.
Some parts go missing, other parts have a completely wrong z-Coordinate (the value itself looks fine, but the background layer is suddenly the foremost layer). Some parts even shift their x-Coordinate (this one looks good as well on setup, if I use NSLog to output all the square coordinates).
This is my setupVBOs function where I write the object coordinates into the VBOs (I have only one Index VBO because every square is the same, but an array of 5x4 VBOs to hold the coordinates for every single part of the layers.)
- (void)setupVBOs {
glGenBuffers(1, &_indexBufferLayer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indexBufferLayer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(IndicesLayer), IndicesLayer, GL_STATIC_DRAW);
for (int layerNo = 1; layerNo < LAYER_COUNT + 1; ++layerNo)
{
for (int layerPart = 1; layerPart < LAYER_PARTS + 1; ++layerPart)
{
glGenBuffers(1, &_vertexBufferLayer[layerNo][layerPart]);
GLfloat x = -3.0 + (2.0 * (layerPart - 1));
GLfloat z = 0.0 + (50.0 * (layerNo - 1));
NSLog(#"Layer %d, Part %d: x=%f, z=%f", layerNo, layerPart, x, z);
// Alter the Texture slightly to
// remove errors from compression (x-Coord.)
Vertex Vertices[] = {
{{x + 1.0, -1.0, z}, {0.9865, 1.0}},
{{x + 1.0, +1.0, z}, {0.9865, 0}},
{{x - 1.0, +1.0, z}, {0.01, 0}},
{{x - 1.0, -1.0, z}, {0.01, 1.0}}
};
glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferLayer[layerNo][layerPart]);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);
x = z = 0;
}
}
}
This is my render Function where I draw everything and add the textures.
- (void)render: (CADisplayLink*)displayLink {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
CC3GLMatrix *projection = [CC3GLMatrix matrix];
float h = 4.0f * self.frame.size.height / self.frame.size.width;
[projection populateFromFrustumLeft:-1 andRight:1 andBottom:-h/4 andTop:h/4 andNear:2 andFar:500];
CC3GLMatrix *modelView = [CC3GLMatrix matrix];
// Translate the Modelviewmatrix
[modelView populateFromTranslation:CC3VectorMake(_cameraX, _cameraY, -5.0)];
// Rotate the Modelviewmatrix
[modelView rotateBy:CC3VectorMake(_currentRotation, 0, 90)];
[modelView translateByZ:_cameraZoom];
//
// Draw all layers
//
for (int layerNo = 1; layerNo < LAYER_COUNT + 1; layerNo++)
{
GLfloat layerFactor = (LAYER_COUNT + 1 - layerNo) * 22.0;
GLfloat scaleFactor = 100.0 + layerFactor;
[modelView scaleByX:scaleFactor];
[modelView scaleByY:scaleFactor];
for (int layerPart = 1; layerPart < LAYER_PARTS + 1; layerPart++)
{
glUniformMatrix4fv(_modelViewUniform, 1, 0, modelView.glMatrix);
glUniformMatrix4fv(_projectionUniform, 1, 0, projection.glMatrix);
GLuint uniformTexture = glGetUniformLocation(programHandle, "Texture");
// Bind Buffer and Texture
glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferLayer[layerNo][layerPart]);
// Activate Texturing Pipeline and Bind Texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _layers[layerNo][layerPart][0]);
glUniform1i(uniformTexture, 0);
// Vertex Shader calls
glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) 0);
glVertexAttribPointer(_texCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) (sizeof(float) * 3));
glEnableVertexAttribArray(_positionSlot);
glEnableVertexAttribArray(_texCoordSlot);
glDrawElements(GL_TRIANGLES, sizeof(IndicesLayer)/sizeof(IndicesLayer[0]), GL_UNSIGNED_BYTE, 0);
glDisableVertexAttribArray(_texCoordSlot);
glDisableVertexAttribArray(_positionSlot);
}
[modelView scaleByX:1/scaleFactor];
[modelView scaleByY:1/scaleFactor];
}
[_context presentRenderbuffer:GL_RENDERBUFFER];
}
Use:
glenable(GL_TEXTURE_2D)
And also:
glActiveTexture — select active texture unit

middlePoint in CGContextAddArc?

i am drawing Arc through CGCOntext.I want to draw a string in the center Point of Arc.how can i fond the center point in the Arc which has been drawn through CGContext.
CGContextSetAlpha(ctx, 0.5);
CGContextSetRGBFillColor(ctx, color.red, color.green, color.blue, color.alpha );
CGContextMoveToPoint(ctx, cX, cY);
CGContextAddArc(ctx, cX, cY, radious+10, (startDeg-90)*M_PI/180.0, (endDeg-90)*M_PI/180.0, 0);
CGContextClosePath(ctx);
CGContextFillPath(ctx);
The 2nd and 3rd arguments to CGContextAddArc are the x and y coordinates for the center of the arc. Therefore, in this code, the center is at the point (cX,cY).
Edit
This code will give the coordinates for the point directly between the starting and ending points of the arc as x and y.
CGFloat x = cX + (radious+10) * (cos((startDeg-90)*M_PI/180.0) + cos((endDeg-90)*M_PI/180.0)) / 2;
CGFloat y = cY + (radious+10) * (sin((startDeg-90)*M_PI/180.0) + sin((endDeg-90)*M_PI/180.0)) / 2;

OpenGL ES 2.0 (specifically for the iphone) rendering is slightly off. Best guess is it's a projection matrix problem

So I bought O'reilly's Iphone 3D programming and found what I believe to be a bug in there code. However I can't figure out what the problem is, and unless I do I can't move forward with my own code.
I will paste what I consider to be the appropriate code into this post but luckily all the code is available online at:
http://examples.oreilly.com/9780596804831/HelloCone/
The problem I am having is with their OpenGL ES 2.0 renderer, it does not show up in their ES 1.1 renderer.
So what I have been noticing is that the cone does not render exactly in the correct position. To test this I changed the ModelViewMatrix to render exactly on the FrustumNear plane. So the cone should appear cut completely in two. When I do this with the ES 1.1 render this is the case, when I do the same in OpenGL ES 2.0 however it is not. The cone is for the most part there, but slightly shaved off. Meaning it is not landing exactly on the fustrum's near face.
Here is the initialization code where the projection matrix is created and set up:
void RenderingEngine2::Initialize(int width, int height)
{
const float coneRadius = 0.5f;
const float coneHeight = 1.0f;
const int coneSlices = 40;
{
// Allocate space for the cone vertices.
m_cone.resize((coneSlices + 1) * 2);
// Initialize the vertices of the triangle strip.
vector<Vertex>::iterator vertex = m_cone.begin();
const float dtheta = TwoPi / coneSlices;
for (float theta = 0; vertex != m_cone.end(); theta += dtheta) {
// Grayscale gradient
float brightness = abs(sin(theta));
vec4 color(brightness, brightness, brightness, 1);
// Apex vertex
vertex->Position = vec3(0, 1, 0);
vertex->Color = color;
vertex++;
// Rim vertex
vertex->Position.x = coneRadius * cos(theta);
vertex->Position.y = 1 - coneHeight;
vertex->Position.z = coneRadius * sin(theta);
vertex->Color = color;
vertex++;
}
}
{
// Allocate space for the disk vertices.
m_disk.resize(coneSlices + 2);
// Initialize the center vertex of the triangle fan.
vector<Vertex>::iterator vertex = m_disk.begin();
vertex->Color = vec4(0.75, 0.75, 0.75, 1);
vertex->Position.x = 0;
vertex->Position.y = 1 - coneHeight;
vertex->Position.z = 0;
vertex++;
// Initialize the rim vertices of the triangle fan.
const float dtheta = TwoPi / coneSlices;
for (float theta = 0; vertex != m_disk.end(); theta += dtheta) {
vertex->Color = vec4(0.75, 0.75, 0.75, 1);
vertex->Position.x = coneRadius * cos(theta);
vertex->Position.y = 1 - coneHeight;
vertex->Position.z = coneRadius * sin(theta);
vertex++;
}
}
// Create the depth buffer.
glGenRenderbuffers(1, &m_depthRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, m_depthRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER,
GL_DEPTH_COMPONENT16,
width,
height);
// Create the framebuffer object; attach the depth and color buffers.
glGenFramebuffers(1, &m_framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER,
m_colorRenderbuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER,
GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER,
m_depthRenderbuffer);
// Bind the color buffer for rendering.
glBindRenderbuffer(GL_RENDERBUFFER, m_colorRenderbuffer);
// Set up some GL state.
glViewport(0, 0, width, height);
glEnable(GL_DEPTH_TEST);
// Build the GLSL program.
m_simpleProgram = BuildProgram(SimpleVertexShader, SimpleFragmentShader);
glUseProgram(m_simpleProgram);
// Set the projection matrix.
GLint projectionUniform = glGetUniformLocation(m_simpleProgram, "Projection");
mat4 projectionMatrix = mat4::Frustum(-1.6f, 1.6, -2.4, 2.4, 5, 10);
glUniformMatrix4fv(projectionUniform, 1, 0, projectionMatrix.Pointer());
}
And here is the Render code. As you can see I have changed the ModelVieMatrix to place the cone on the bottom left corner of the near Frustum face.
void RenderingEngine2::Render() const
{
GLuint positionSlot = glGetAttribLocation(m_simpleProgram, "Position");
GLuint colorSlot = glGetAttribLocation(m_simpleProgram, "SourceColor");
glClearColor(0.5f, 0.5f, 0.5f, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnableVertexAttribArray(positionSlot);
glEnableVertexAttribArray(colorSlot);
mat4 rotation(m_animation.Current.ToMatrix());
mat4 translation = mat4::Translate(-1.6, -2.4, -5);
// Set the model-view matrix.
GLint modelviewUniform = glGetUniformLocation(m_simpleProgram, "Modelview");
mat4 modelviewMatrix = rotation * translation;
glUniformMatrix4fv(modelviewUniform, 1, 0, modelviewMatrix.Pointer());
// Draw the cone.
{
GLsizei stride = sizeof(Vertex);
const GLvoid* pCoords = &m_cone[0].Position.x;
const GLvoid* pColors = &m_cone[0].Color.x;
glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, stride, pCoords);
glVertexAttribPointer(colorSlot, 4, GL_FLOAT, GL_FALSE, stride, pColors);
glDrawArrays(GL_TRIANGLE_STRIP, 0, m_cone.size());
}
// Draw the disk that caps off the base of the cone.
{
GLsizei stride = sizeof(Vertex);
const GLvoid* pCoords = &m_disk[0].Position.x;
const GLvoid* pColors = &m_disk[0].Color.x;
glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, stride, pCoords);
glVertexAttribPointer(colorSlot, 4, GL_FLOAT, GL_FALSE, stride, pColors);
glDrawArrays(GL_TRIANGLE_FAN, 0, m_disk.size());
}
glDisableVertexAttribArray(positionSlot);
glDisableVertexAttribArray(colorSlot);
}
Looks like I found the answer to my own question.
The projection matrix in the O'Reilly code is being calculated incorrectly.
In their code they have:
T a = 2 * near / (right - left);
T b = 2 * near / (top - bottom);
T c = (right + left) / (right - left);
T d = (top + bottom) / (top - bottom);
T e = - (far + near) / (far - near);
T f = -2 * far * near / (far - near);
Matrix4 m;
m.x.x = a; m.x.y = 0; m.x.z = 0; m.x.w = 0;
m.y.x = 0; m.y.y = b; m.y.z = 0; m.y.w = 0;
m.z.x = c; m.z.y = d; m.z.z = e; m.z.w = -1;
m.w.x = 0; m.w.y = 0; m.w.z = f; m.w.w = 1;
return m;
However this is not the projection matrix. m.w.w should be 0 not 1.
Matrix4 m;
m.x.x = a; m.x.y = 0; m.x.z = 0; m.x.w = 0;
m.y.x = 0; m.y.y = b; m.y.z = 0; m.y.w = 0;
m.z.x = c; m.z.y = d; m.z.z = e; m.z.w = -1;
m.w.x = 0; m.w.y = 0; m.w.z = f; m.w.w = 0;
return m;