PyGObject: cairo context to pixbuf - cairo

width, height = max(abs(fromo.realx - to.realx),3), max(abs(fromo.realy - to.realy),3)
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
ctx = cairo.Context(surface)
ctx.scale(width, height)
ctx.move_to(0, 0)
ctx.line_to(1, 1)
I have that, i want to convert the cairo context to a pixbuf so i can use it as Gtk.Image. Can I do that?

You can use gtk_image_set_from_surface for that:
gtk.Image.set_from_surface(surface)
or even make your image from the surface:
gtk.Image.new_from_surface(surface)

Related

Uneven borders of ellipse annotation when I try to rotate the PDF

I am trying to draw ellipse on a rotated PDF but the border of the ellipse appears thick and thin in center
Sample Code:
PdfAnnotation annotation = PdfAnnotation.createSquareCircle(stamper.getWriter(), rect, null, false);
annotation.setFlags(PdfAnnotation.FLAGS_PRINT);
annotation.setColor(getColor(annot.getBorderColor()));
// annotation.setBorder(new PdfBorderArray(2, 2, 2));
// annotation.setColor(getColor(annot.getBorderColor()));
annotation.setBorderStyle(new PdfBorderDictionary(3.5f, PdfBorderDictionary.STYLE_SOLID));
PdfContentByte cb = stamper.getOverContent(page);
if ((int) (orientation % 360) == 90 || (int) (orientation % 360) == 270)
{
w = rect.getHeight();
h = rect.getWidth();
}
else
{
w = rect.getWidth();
h = rect.getHeight();
}
PdfAppearance app = cb.createAppearance(w + 3.5f, h + 3.5f);
app.setColorStroke(getColor(annot.getBorderColor()));
app.setLineWidth(3.5);
app.ellipse(rect.getLeft() + 1.5, rect.getBottom() + 1.5, rect.getRight() - 1.5, rect.getTop() - 1.5);
app.stroke();
annotation.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, app);
stamper.addAnnotation(annotation, page);
As already assumed in a comment a feature of iText (for rotated pages it attempts to pretend to the user that he has an upright coordinate system, not a rotated one) gets into your way. This feature unfortunately does only affect the dimensions of the annotation itself, not of the appearances you create for it.
If a page has a 90° or 270° rotation, therefore, you have to use appearance dimensions whose width and height are switched compared to the annotation dimensions you used.
Furthermore, one also has to consider the switched dimensions when drawing the ellipse on the appearance template. The code in the question and in the example code shared via google drive forgot this.
Finally, one shall not override the switched dimensions of the appearance template using PdfAppearance.setBoundingBox. The code in the example code shared via google drive did this.
False Example
If I create an ellipse annotation with appearance on a rotated page like this:
Rectangle rect = new Rectangle(202 + 6f, 300, 200 + 100, 300 + 150);
PdfAnnotation annotation = PdfAnnotation.createSquareCircle(stamper.getWriter(), rect, null, false);
annotation.setFlags(PdfAnnotation.FLAGS_PRINT);
annotation.setColor(BaseColor.RED);
annotation.setBorderStyle(new PdfBorderDictionary(3.5f, PdfBorderDictionary.STYLE_SOLID));
PdfContentByte cb = stamper.getOverContent(1);
PdfAppearance app = cb.createAppearance(rect.getWidth(), rect.getHeight());
app.setColorStroke(BaseColor.RED);
app.setLineWidth(3.5);
app.ellipse( 1.5, 1.5, rect.getWidth() - 1.5, rect.getHeight() - 1.5);
app.stroke();
annotation.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, app);
stamper.addAnnotation(annotation, 1);
(CreateEllipse test testCreateEllipseAppearanceOnRotated)
I indeed get a funny ellipse:
Correct Example
If I create an ellipse annotation with appearance on a rotated page with switched dimensions like this, though:
Rectangle rect = new Rectangle(202 + 6f, 300, 200 + 100, 300 + 150);
PdfAnnotation annotation = PdfAnnotation.createSquareCircle(stamper.getWriter(), rect, null, false);
annotation.setFlags(PdfAnnotation.FLAGS_PRINT);
annotation.setColor(BaseColor.RED);
annotation.setBorderStyle(new PdfBorderDictionary(3.5f, PdfBorderDictionary.STYLE_SOLID));
PdfContentByte cb = stamper.getOverContent(1);
// switched appearance dimensions
PdfAppearance app = cb.createAppearance(rect.getHeight(), rect.getWidth());
app.setColorStroke(BaseColor.RED);
app.setLineWidth(3.5);
// draw ellipse using switched appearance dimensions
app.ellipse( 1.5, 1.5, rect.getHeight() - 1.5, rect.getWidth() - 1.5);
app.stroke();
annotation.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, app);
stamper.addAnnotation(annotation, 1);
(CreateEllipse test testCreateCorrectEllipseAppearanceOnRotated)
I get the expected ellipse:

how to clean cairo's surface area without png image

this will clean all
void surface_clean(Mpaint *mpaint)
{
mpaint->surface=cairo_image_surface_create_from_png ("cxl.cache.png");
}
I want to clean the area in a cairo_surface_t by x, y, width, height.
is there any Interface?
Check CAIRO_OPERATOR_* here [1]. pycairo code:
ctx.set_source_rgba(0.0, 0.0, 0.0, 0.0)
ctx.set_operator(cairo.OPERATOR_CLEAR)
ctx.rectangle(x, y, width, height)
ctx.paint_with_alpha(1.0)
[1] https://cairographics.org/operators/

iOS how to calculate number of pixels/area enclosed by a curve?

I got an arbitrary shaped curve, enclosing some area. I would like to approximate the number of pixels that the curve is enclosing on an iPhone/iPad screen. How can I do so?
A curve is defined as a successive x/y coordinates of points.
A curve is closed.
A curve is drawn by a user's touches (touchesMoved method), and I
have no knowledge of what it looks like
I was thinking of somehow filling the closed curve with color, then calculating the number of pixels of this color in a screenshot of a screen. This means I need to know how to programmatically fill a closed curve with color.
Is there some other way that I'm not thinking of?
Thank you!
Let's do this by creating a Quartz path enclosing your curve. Then we'll create a bitmap context and fill the path in that context. Then we can examine the bitmap and count the pixels that were filled. We'll wrap this all in a convenient function:
static double areaOfCurveWithPoints(const CGPoint *points, size_t count) {
First we need to create the path:
CGPathRef path = createClosedPathWithPoints(points, count);
Then we need to get the bounding box of the path. CGPoint coordinates don't have to be integers, but a bitmap has to have integer dimensions, so we'll get an integral bounding box at least as big as the path's bounding box:
CGRect frame = integralFrameForPath(path);
We also need to decide how wide (in bytes) to make the bitmap:
size_t bytesPerRow = bytesPerRowForWidth(frame.size.width);
Now we can create the bitmap:
CGContextRef gc = createBitmapContextWithFrame(frame, bytesPerRow);
The bitmap is filled with black when it's created. We'll fill the path with white:
CGContextSetFillColorWithColor(gc, [UIColor whiteColor].CGColor);
CGContextAddPath(gc, path);
CGContextFillPath(gc);
Now we're done with the path so we can release it:
CGPathRelease(path);
Next we'll compute the area that was filled:
double area = areaFilledInBitmapContext(gc);
Now we're done with the bitmap context, so we can release it:
CGContextRelease(gc);
Finally, we can return the area we computed:
return area;
}
Well, that was easy! But we have to write all those helper functions. Let's start at the top. Creating the path is trivial:
static CGPathRef createClosedPathWithPoints(const CGPoint *points, size_t count) {
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddLines(path, NULL, points, count);
CGPathCloseSubpath(path);
return path;
}
Getting the integral bounding box of the path is also trivial:
static CGRect integralFrameForPath(CGPathRef path) {
CGRect frame = CGPathGetBoundingBox(path);
return CGRectIntegral(frame);
}
To choose the bytes per row of the bitmap, we could just use width of the path's bounding box. But I think Quartz likes to have bitmaps that are multiples of a nice power of two. I haven't done any testing on this, so you might want to experiment. For now, we'll round up the width to the next smallest multiple of 64:
static size_t bytesPerRowForWidth(CGFloat width) {
static const size_t kFactor = 64;
// Round up to a multiple of kFactor, which must be a power of 2.
return ((size_t)width + (kFactor - 1)) & ~(kFactor - 1);
}
We create the bitmap context with the computed sizes. We also need to translate the origin of the coordinate system. Why? Because the origin of the path's bounding box might not be at (0, 0).
static CGContextRef createBitmapContextWithFrame(CGRect frame, size_t bytesPerRow) {
CGColorSpaceRef grayscale = CGColorSpaceCreateDeviceGray();
CGContextRef gc = CGBitmapContextCreate(NULL, frame.size.width, frame.size.height, 8, bytesPerRow, grayscale, kCGImageAlphaNone);
CGColorSpaceRelease(grayscale);
CGContextTranslateCTM(gc, -frame.origin.x, -frame.origin.x);
return gc;
}
Finally, we need to write the helper that actually counts the filled pixels. We have to decide how we want to count pixels. Each pixel is represented by one unsigned 8-bit integer. A black pixel is 0. A white pixel is 255. The numbers in between are shades of gray. Quartz anti-aliases the edge of the path when it fills it using gray pixels. So we have to decide how to count those gray pixels.
One way is to define a threshold, like 128. Any pixel at or above the threshold counts as filled; the rest count as unfilled.
Another way is to count the gray pixels as partially filled, and add up that partial filling. So two exactly half-filled pixels get combined and count as a single, entirely-filled pixel. Let's do it that way:
static double areaFilledInBitmapContext(gc) {
size_t width = CGBitmapContextGetWidth(gc);
size_t height = CGBitmapContextGetHeight(gc);
size_t stride = CGBitmapContextGetBytesPerRow(gc);
uint8_t *pixels = CGBitmapContextGetData(gc);
uint64_t coverage = 0;
for (size_t y = 0; y < height; ++y) {
for (size_t x = 0; x < width; ++x) {
coverage += pixels[y * stride + x];
}
}
return (double)coverage / UINT8_MAX;
}
You can find all of the code bundled up in this gist.
I would grab the drawing as a CGIMage ...
(CGBitmapContextCreateImage(UIGraphicsGetCurrentContext());
Then, as recommended above use a "Flood Fill" approach to count the pixels.
(Google Flood Fill)

Image resized error: CGBitmapContextCreate: unsupported parameter

I am using the following code (from a blog post) to resize an image
if (inImage.size.width <= inImage.size.height) {
// Portrait
ratio = inImage.size.height / inImage.size.width;
resizedRect = CGRectMake(0, 0, width, width * ratio);
}
else {
// Landscape
ratio = inImage.size.width / inImage.size.height;
resizedRect = CGRectMake(0, 0, height * ratio, height);
}
CGImageRef imageRef = [inImage CGImage];
CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(imageRef);
if (alphaInfo == kCGImageAlphaNone)
alphaInfo = kCGImageAlphaNoneSkipLast;
CGContextRef bitmap = CGBitmapContextCreate(
NULL,
resizedRect.size.width, // width
resizedRect.size.height, // height
CGImageGetBitsPerComponent(imageRef), // really needs to always be 8
4 * resizedRect.size.width, // rowbytes
CGImageGetColorSpace(imageRef),
alphaInfo
);
but for some reason depending on the size I am try to resize to I get the following error generated
CGBitmapContextCreate: unsupported
parameter combination: 8 integer
bits/component; 32 bits/pixel;
3-component colorspace;
kCGImageAlphaNoneSkipFirst; XXX
bytes/row.
where XXX differs depending on which image.
The rect I am creating is propotional to the image, I take a ratio from the width/height (depending on aspect) and multiple that be target width/height.
Here are some examples (X errors, / doesnt), the resize size will be 50xX or Xx50 depending on aspect:
Source 50x50 69x69
430x320 / X
240x320 / /
272x320 / /
480x419 / X
426x320 X X
480x256 X X
Where you wrote thumbRect, did you mean resizedRect? thumbRect does not otherwise occur.
I suspect the problem is that resizedRect.size.width is non integral. Note that it's floating point.
The width and bytesPerRow parameters of CGBitmapContextCreate are declared as integers. When you pass a floating point value, such as here, it gets truncated.
Suppose your resizedRect.size.width is 1.25. Then you will end up passing 1 for the width, and floor(1.25 * 4) == 5 as the bytes per row. That's inconsistent. You always want to pass four times whatever you passed for the width for the bytes per row.
You can also just leave bytesPerRow as 0, by the way. Then the system picks the best bytesPerRow (which is often larger than 4 times the width - it pads out for alignment).

CGPathRef intersection

Is there a way to find out whether two CGPathRefs are intersected or not. In my case all the CGPaths are having closePath.
For example, I am having two paths. One path is the rectangle which is rotated with some angle and the other path is curved path. Two paths origin will be changing frequently. At some point they may intersect. I want to know when they are intersected. Please let me know if you have any solution.
Thanks in advance
Make one path the clipping path, draw the other path, then search for pixels that survived the clipping process:
// initialise and erase context
CGContextAddPath(context, path1);
CGContextClip(context);
// set fill colour to intersection colour
CGContextAddPath(context, path2);
CGContextFillPath(context);
// search for pixels that match intersection colour
This works because clipping = intersecting.
Don't forget that intersection depends on the definition of interiority, of which there are several. This code uses the winding-number fill rule, you might want the even odd rule or something else again. If interiority doesn't keep you up at night, then this code should be fine.
My previous answer involved drawing transparent curves to an RGBA context. This solution is superior to the old one because it is
simpler
uses a quarter of the memory as an 8bit greyscale context suffices
obviates the need for hairy, difficult-to-debug transparency code
Who could ask for more?
I guess you could ask for a complete implementation, ready to cut'n'paste, but that would spoil the fun and obfuscate an otherwise simple answer.
OLDER, HARDER TO UNDERSTAND AND LESS EFFICIENT ANSWER
Draw both CGPathRefs separately at 50% transparency into a zeroed, CGBitmapContextCreate-ed RGBA memory buffer and check for any pixel values > 128. This works on any platform that supports CoreGraphics (i.e. iOS and OSX).
In pseudocode
// zero memory
CGContextRef context;
context = CGBitmapContextCreate(memory, wide, high, 8, wide*4, CGColorSpaceCreateDeviceRGB(), kCGImageAlphaPremultipliedLast);
CGContextSetRGBFillColor(context, 1, 1, 1, 0.5); // now everything you draw will be at 50%
// draw your path 1 to context
// draw your path 2 to context
// for each pixel in memory buffer
if(*p > 128) return true; // curves intersect
else p+= 4; // keep looking
Let the resolution of the rasterised versions be your precision and choose the precision to suit your performance needs.
1) There isn't any CGPath API to do this. But, you can do the math to figure it out. Take a look at this wikipedia article on Bezier curves to see how the curves in CGPath are implemented.
2) This is going to be slow on the iPhone I would expect but you could fill both paths into a buffer in difference colors (say, red and blue, with alpha=0.5) and then iterate through the buffer to find any pixels that occur at intersections. This will be extremely slow.
For iOS, the alpha blend seems to be ignored.
Instead, you can do a color blend, which will achieve the same effect, but doesn't need alpha:
CGContextSetBlendMode(context, kCGBlendModeColorDodge);
CGFloat semiTransparent[] = { .5,.5,.5,1};
Pixels in output Image will be:
RGB = 0,0,0 = (0.0f) ... no path
RGB = 64,64,64 = (0.25f) ... one path, no intersection
RGB = 128,128,128 = (0.5f) ... two paths, intersection found
Complete code for drawing:
-(void) drawFirst:(CGPathRef) first second:(CGPathRef) second into:(CGContextRef)context
{
/** setup the context for DODGE (everything gets lighter if it overlaps) */
CGContextSetBlendMode(context, kCGBlendModeColorDodge);
CGFloat semiTransparent[] = { .5,.5,.5,1};
CGContextSetStrokeColor(context, semiTransparent);
CGContextSetFillColor(context, semiTransparent);
CGContextAddPath(context, first);
CGContextFillPath(context);
CGContextStrokePath(context);
CGContextAddPath(context, second);
CGContextFillPath(context);
CGContextStrokePath(context);
}
Complete code for checking output:
[self drawFirst:YOUR_FIRST_PATH second:YOUR_SECOND_PATH into:context];
// Now we can get a pointer to the image data associated with the bitmap
// context.
BOOL result = FALSE;
unsigned char* data = CGBitmapContextGetData (context);
if (data != NULL) {
for( int i=0; i<width; i++ )
for( int k=0; k<width; k++ )
{
//offset locates the pixel in the data from x,y.
//4 for 4 bytes of data per pixel, w is width of one row of data.
int offset = 4*((width*round(k))+round(i));
int alpha = data[offset];
int red = data[offset+1];
int green = data[offset+2];
int blue = data[offset+3];
if( red > 254 )
{
result = TRUE;
break;
}
}
And, finally, here's a slightly modified code from another SO answer ... complete code for creating an RGB space on iOS 4, iOS 5, that will support the above functions:
- (CGContextRef) createARGBBitmapContextWithFrame:(CGRect) frame
{
/** NB: this requires iOS 4 or above - it uses the auto-allocating behaviour of Apple's method, to reduce a potential memory leak in the original StackOverflow version */
CGContextRef context = NULL;
CGColorSpaceRef colorSpace;
void * bitmapData;
int bitmapByteCount;
int bitmapBytesPerRow;
// Get image width, height. We'll use the entire image.
size_t pixelsWide = frame.size.width;
size_t pixelsHigh = frame.size.height;
// Declare the number of bytes per row. Each pixel in the bitmap in this
// example is represented by 4 bytes; 8 bits each of red, green, blue, and
// alpha.
bitmapBytesPerRow = (pixelsWide * 4);
bitmapByteCount = (bitmapBytesPerRow * pixelsHigh);
// Use the generic RGB color space.
colorSpace = CGColorSpaceCreateDeviceRGB();
if (colorSpace == NULL)
{
fprintf(stderr, "Error allocating color space\n");
return NULL;
}
// Create the bitmap context. We want pre-multiplied ARGB, 8-bits
// per component. Regardless of what the source image format is
// (CMYK, Grayscale, and so on) it will be converted over to the format
// specified here by CGBitmapContextCreate.
context = CGBitmapContextCreate (NULL,
pixelsWide,
pixelsHigh,
8, // bits per component
bitmapBytesPerRow,
colorSpace,
kCGImageAlphaPremultipliedFirst
//kCGImageAlphaFirst
);
if (context == NULL)
{
fprintf (stderr, "Context not created!");
}
// Make sure and release colorspace before returning
CGColorSpaceRelease( colorSpace );
return context;
}