How to set line width on template clipping - itext

I have some PdfTemplate and I want to clip its shape to some path. I know how to do this, but the clipping line is always the same (probably 1 px) and I want to be able to change it. Is there any way to do this? Half-measures, like resizing template will not do the trick.
Piece of code:
PdfTemplate template = contentByte.CreateTemplate(100, 200);
template.MoveTo(0, 0);
template.LineTo(50, 50);
template.LineTo(50, 0);
template.LineTo(0, 50);
template.SetLineWidth(5);
template.Clip();
Image img = Image.getInstance(RESOURCE);
template.Add(img, 0, 0);
SetLineWidth() obviously doesn't work. Both C# and Java answers will help.
Edit: In this scenario we have img in triangle. What if we want to clip this image like this, but without changing coordinates (I would like to set line width on 10):
template.LineTo(45, 45);
template.LineTo(45, 0);
template.LineTo(0, 45);

Problem #1: You never stroke the path, hence it is never drawn. First try this:
PdfTemplate template = contentByte.CreateTemplate(100, 200);
template.MoveTo(0, 0);
template.LineTo(50, 50);
template.LineTo(50, 0);
template.LineTo(0, 50);
template.SetLineWidth(5);
template.Clip();
Image img = Image.getInstance(RESOURCE);
template.Add(img, 0, 0);
template.Stroke();
Problem #2: You are using your clipping path for two different purposes.
To cut out a shape when adding an Image.
To draw the path.
That doesn't look right. I'm not sure if every PDF viewer will actually stroke that path as you clearly use that path to clip content.
I would write this code like this:
PdfTemplate template = contentByte.CreateTemplate(100, 200);
template.MoveTo(0, 0);
template.LineTo(50, 50);
template.LineTo(50, 0);
template.LineTo(0, 50);
template.Clip();
template.NewPath();
Image img = Image.getInstance(RESOURCE);
template.Add(img, 0, 0);
template.MoveTo(0, 0);
template.LineTo(50, 50);
template.LineTo(50, 0);
template.LineTo(0, 50);
template.SetLineWidth(5);
template.Stroke();
The first time, you use the path as a clipping path. It doesn't make sense to define a line width for a clipping path: the path defines the shape that needs to be cut out.
The second time, you use the path to stroke the borders of a shape. The lines that make these borders have a width. Note that you're only drawing three lines. You may want to close the path!
This is also strange:
template.LineTo(45, 45);
template.LineTo(45, 0);
template.LineTo(0, 45);
This doesn't draw a triangle!
These lines should be corrected like this:
template.MoveTo(0, 45);
template.LineTo(45, 45);
template.LineTo(45, 0);
template.LineTo(0, 45);

Related

SpriteKit: SKShapeNode.calculateAccumulatedFrame returns a frame, which is bigger than its content

I create a SKShapeNode rectangle with a fixed size using the following code
SKShapeNode *rect = [SKShapeNode shapeNodeWithRect: CGRectMake(0, 0, 100, 200)];
CGRect accumulatedFrame = rect.calculateAccumulatedFrame;
Debugging the code above, the accumulatedFrame holds the values below:
origin=(x=-0.5, y=-0.5) size=(width=101, height=201)
Why is the calculated, accumulated frame bigger than the intended 100x200 ?
Thanks in advance for any hint :)
Code sample:
SKShapeNode *rect = [SKShapeNode shapeNodeWithRect: CGRectMake(0, 0, 200, 100)];
CGRect accumulatedFrame = rect.calculateAccumulatedFrame;
CGRect frame = rect.frame;
debugger returns origin=(x=-0.5, y=-0.5) size=(width=201, height=101) for the accumulated frame property
debugger returns origin=(x=-0.5, y=-0.5) size=(width=201, height=101) for the frame propery
Using an origin > (0,0) adds only 0,5 to width and height; seems, that sprite-kit returns an accumulated frame, which really contains the node(s) and adds 0.5, so none of the node borders intersects the border of the accumulated frame. Didn't find anything about it in the api reference...
I believe the issue you are dealing with is that if you have a strokeColor defined, that border stroke adds to size of the rectangle.
Try this code :
SKShapeNode *rect = [SKShapeNode shapeNodeWithRect: CGRectMake(0, 0, 200, 100)];
rect.strokeColor = nil;
You have to look at the stroke as being additive, and adding to the dimensions of your shape.

How do I cut a notch out of a CALayer?

I have a set of views in a carousel, each using a CAGradientLayer as a background. The carousel sits over a textured background. I've been asked for the background to poke up in a triangle to show the selected view. I can't just use a triangular image with the background texture, as it won't necessarily match up with the main background. I'd like to cut a notch out of the background of the current view, so that the textured background is visible through the notch.
How should I go about this? Is it possible to make a polygonal layer?
I found I was able to do it using a CAShapeLayer:
CAShapeLayer *mask = [[[CAShapeLayer alloc] init] autorelease];
mask.frame = backgroundLayer.bounds;
mask.fillColor = [[UIColor blackColor] CGColor];
CGFloat width = backgroundLayer.frame.size.width;
CGFloat height = backgroundLayer.frame.size.height;
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, nil, 0, 0);
CGPathAddLineToPoint(path, nil, width, 0);
CGPathAddLineToPoint(path, nil, width, height);
CGPathAddLineToPoint(path, nil, (width/2) + 5, height);
CGPathAddLineToPoint(path, nil, width/2, height - 5);
CGPathAddLineToPoint(path, nil, (width/2) - 5, height);
CGPathAddLineToPoint(path, nil, 0, height);
CGPathAddLineToPoint(path, nil, 0, 0);
CGPathCloseSubpath(path);
mask.path = path;
CGPathRelease(path);
backgroundLayer.mask = mask;
I don't think that we can draw polygonal layers. However, I believe that the expected result can be achieved using two different ways:-
If your images are of the same size then you can use a PNG image with transparent notch in between (or outside as per your desire).
Draw a filled rectangle and transparent triangular polygon. Then you need to intersect both the polygon (rectangle and triangle). The resulted Shape should be placed over the carousel image. The benefit of this method is that you can dynamically change the size and shapes of the polygons if required.
Hope this helps!
Is it possible to make a polygonal layer?
No.
How should I go about this?
Use a mask - a png image that's the same size as your layer but has a bit cut out - this will make the CALayer appear to have a chunk cut out of it. (However, it won't so if you cut-out is big, you might get users trying to touch whatever is behind it, which won't work!).
See the documentation (and search stackoverflow) for more details.

CGImageCreateWithImageInRect causing distortion

I'm using CGImageCreateWithImageInRect to do a magnifying effect, and it works beautifully, except when I get close to the edges of my view. In that case, clipping causes the image to be distorted. Right now I grab a 72x72 chunk of the view, apply a round mask to it, and then draw the masked image, and a circle on top.
When the copied chunk is near the edge of the view, It winds up smaller than 72x72 because of clipping, and then when it's drawn in the magnifying glass it gets stretched out.
When the touch point is close to the left edge, for example, I would like to create an image where the left part is filled with a solid color, and the right half contains part of the view that's being magnified. Then apply the mask to that image and add the overlay on top.
Here's what I'm doing now. imageRef is the image being magnified, mask is a round mask, and overlay is a circle to mark the edges of the magnified region.
CGImageRef subImage = CGImageCreateWithImageInRect(imageRef, CGRectMake(touchPoint.x - 36, touchPoint.y - 36, 72, 72));
CGImageRef xMaskedImage = CGImageCreateWithMask(subImage, mask);
CGContextRef context = UIGraphicsGetCurrentContext();
CGAffineTransform xform = CGAffineTransformMake(1.0, 0.0, 0.0, -1.0, 0.0, 0.0);
CGContextConcatCTM(context, xform);
CGRect area = CGRectMake(touchPoint.x - 84, -touchPoint.y, 170, 170);
CGRect area2 = CGRectMake(touchPoint.x - 80, -touchPoint.y + 4, 160, 160);
CGContextDrawImage(context, area2, xMaskedImage);
CGContextDrawImage(context, area, overlay);
I solved this by using CGBitmapContextCreate() to create a bitmap context. Then I drew the captured area into a smaller area of this context, and created an image from it with CGBitmapContextCreateImage(). That was the missing piece of the puzzle.

Rendering hundreds of "block" images -vs- Partially rendering a "map" of blocks

It seems that none of these solutions eliminate the lag associated with rendering images onto the screen (whether from .png's or using CG).
There is a 28x16 grid of blocks that are each 16x16 pixels, and at some points in the game, about half of them need to change their image because of a state change. Having each block as a subview of the main view causes a lag when half of them need to individually change their image (either from .png's or using CG).
I tried having one "map" view with whose drawRect: method is:
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, rect);
// width and height are defined as the width and height of the grid (28x16 for iPhone)
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
// states[] is an enum instance variable that holds the states of each block in the map (state determines image)
if (states[(x * height) + y] == PXBlockStateEmpty) {
CGContextSetFillColorWithColor(context, [UIColor colorWithRed:153.0/255.0 green:153.0/255.0 blue:153.0/255.0 alpha:0.5].CGColor);
CGContextSetStrokeColorWithColor(context, [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.5].CGColor);
CGContextAddRect(context, CGRectMake(x * 16, y * 16, 16, 16));
CGContextDrawPath(context, kCGPathFillStroke);
} else if (states[(x * height) + y] == PXBlockStateSolid || states[(x * height) + y] == PXBlockStateSolidEdge) {
CGContextSetFillColorWithColor(context, [UIColor colorWithRed:0.0 green:0.0 blue:102.0/255.0 alpha:0.9].CGColor);
CGContextSetStrokeColorWithColor(context, [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.5].CGColor);
CGContextAddRect(context, CGRectMake(x * 16, y * 16, 16, 16));
CGContextDrawPath(context, kCGPathFillStroke);
} else if (states[(x * height) + y] == PXBlockStateBuilding) {
CGContextSetFillColorWithColor(context, [UIColor colorWithRed:51.0/255.0 green:51.0/255.0 blue:51.0/255.0 alpha:0.5].CGColor);
CGContextFillRect(context, CGRectMake(x * 16, y * 16, 16, 16));
}
}
}
So my solution was to call setNeedsDisplayInRect: on the map, passing the frame (a 16x16 rect) of the block whose state changed. Am I using setNeedsDisplayInRect: incorrectly, or is this just an inefficient way to do it?
Both options (one subview vs hundreds of subviews) lag the game for a bit when a lot of the board is filled with a different image, and the second solution in particular lags whenever ANY block's image needs to be updated.
Any ideas? Thank you for your help!
Calling setNeedsDisplayInRect: affects the argument to drawRect: but is otherwise the same as setneedsDisplay. The rectangle drawRect: receives is the union of all dirty rectangles. If you only draw content that intersects that rectangle, leaving other content untouched, you may see a speed improvement. Judging from the code snippet above you draw every tile every time. Since it is a union, you might also track dirty tiles separately and only draw dirty tiles. In that case, call CGContextClearRect for individual tiles instead of clearing the whole context, unless they are all dirty.
CGContextAddRect adds a rectangle to a path that will be drawn. Since you draw the path at every loop without beginning a new path, you redraw areas as the loop progresses. It would probably be faster to use CGContextFillRect and CGContextStrokeRect instead of a path.
Making each tile a separate UIView incurs more overhead than drawing as a whole. You should be able to get better speed from this method once the kinks are worked out.

How to display text using Quartz on the iPhone?

I've been trying to display text using a Quartz context, but no matter what I've tried I simply haven't had luck getting the text to display (I'm able to display all sorts of other Quartz objects though). Anybody knows what I might be doing wrong?
example:
-(void)drawRect:(CGRect)rect
{
// Drawing code
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSelectFont(context, "Arial", 24, kCGEncodingFontSpecific);
CGContextSetTextPosition(context,80,80);
CGContextShowText(context, "hello", 6);
//not even this works
CGContextShowTextAtPoint(context, 1,1, "hello", 6);
}
OK, I got it. First off, change your encoding mode to kCGEncodingMacRoman. Secondly, insert this line underneath it:
CGContextSetTextMatrix(canvas, CGAffineTransformMake(1, 0, 0, -1, 0, 0));
This sets the conversion matrix for text so that it is drawn correctly. If you don't put that line in, your text will be upside down and back to front. No idea why this wasn't the default. Finally, make sure you've set the right fill colour. It's an easy mistake to make if you forget to change from the backdrop colour to the text colour and end up with white-on-white text.
Here is a fragment of code that I'm using.
UIColor *mainTextColor = [UIColor whiteColor];
[mainTextColor set];
drawTextLjust(#"Sample Text", 8, 50, 185, 18, 16);
And:
static void drawTextLjust(NSString* text, CGFloat y, CGFloat left, CGFloat right,
int maxFontSize, int minFontSize) {
CGPoint point = CGPointMake(left, y);
UIFont *font = [UIFont systemFontOfSize:maxFontSize];
[text drawAtPoint:point forWidth:right - left withFont:font
minFontSize:minFontSize actualFontSize:NULL
lineBreakMode:UILineBreakModeTailTruncation
baselineAdjustment:UIBaselineAdjustmentAlignBaselines];
}