I have a CAShapeLayer that takes his shape from path. This creates a layer object which can be manipulated, like move and rotate.
My problem is that I need the layer object to be composed of multiple paths. For example imagine the United States map; there is the main shape and also the Alaska. Both shapes are not connected, but they are the same entity and I need them to be a single object in a single CAShapeLayer, so that when I move the layer both US and Alaska move together.
UIBezierPath* ahPath = [self mydPath];
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = ahPath.CGPath;
...
[self.layer addSublayer:shapeLayer];
In the case you describe I would make a CAShapeLayer representing the US and a separate CAShapeLayer representing a state, then add the state layer to the US layer. Moving the US layer would automatically move the state layer with it. The advantage is that you can color the US and states differently.
Also be aware that a CGPath can contain multiple subpaths, so one CGPath can contain the US and Alaska. Check the Overview in CGPath Reference.
Related
I have written a simple Cocoa app for Mac OS X (10.7) using Xcode 4.2. All the app does is create a window with a scrollable array of sub-Views in it, each representing a page to draw stuff on at a very low level. The sub-View's isFlipped method delivers YES, so the origin of every sub-View is the upper left corner. Using various Core Graphics routines, I'm able to draw lines and fill paths and all that fun PostScripty stuff successfully.
It's drawing glyphs from a given font that's got me confused.
Here's the complete code, cut-n-pasted from the program, for the sub-View's -drawRect: method --
- (void)drawRect:(NSRect)dirtyRect
{
// Start with background color for any part of this view
[[NSColor whiteColor] set];
NSRectFill( dirtyRect );
// Drop down to Core Graphics world, ensuring there's no side-effects
context = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];
CGContextSaveGState(context);
{
//CGFontRef theFont = CGFontCreateWithFontName(CFSTR("American Typewriter"));
//CGContextSetFont(context, theFont);
CGContextSelectFont(context, "American Typewriter", 200, kCGEncodingMacRoman);
CGContextSetFontSize(context, 200);
// Adjust the text transform so the text doesn't draw upside down
CGContextSetTextMatrix(context, CGAffineTransformScale(CGAffineTransformIdentity, 1, -1));
CGContextSetTextDrawingMode(context, kCGTextFillStroke);
CGContextSetRGBFillColor(context, 0.0, .3, 0.8, 1.0);
// Find the center of view's (not dirtyRect's) bounds
// View is 612 x 792 (nominally 8.5" by 11")
CGPoint centerPoint;
CGRect bds = [self bounds];
centerPoint.x = bds.origin.x + bds.size.width / 2;
centerPoint.y = bds.origin.y + bds.size.height / 2;
// Create arrays to hold glyph IDs and the positions at which to draw them.
#define glyphCount 1 // For now, just one glyph
CGGlyph glyphs[glyphCount];
CGPoint positions[glyphCount];
glyphs[0] = 40; // Glyph ID for '#' character in above font
positions[0] = centerPoint;
// Draw above center. This works.
CGContextShowGlyphsAtPoint(context, centerPoint.x, centerPoint.y - 200.0, glyphs, glyphCount);
// Draw at center. This works.
CGContextShowGlyphsAtPoint(context, positions[0].x, positions[0].y, glyphs, glyphCount);
// Draw below center. This fails (draws nothing). Why?
positions[0].y += 200.0;
CGContextShowGlyphsAtPositions(context, glyphs, positions, glyphCount);
}
CGContextRestoreGState(context);
}
What's got me pulling my hair out is that the first two glyph-drawing calls using CGContextShowGlyphsAtPoint() work fine as expected, but the third attempt using CGContextShowGlyphsAtPositions() never draws anything. So there are only two # symbols on the page, rather than three. This difference in behaviors doesn't depend on whether I've previously used CGContextSetFont() or CGContextSelectFont().
There must be some hidden change in state going on, or something very different under the hood w/r/t these two almost identical Core Graphics glyph-drawing routines, but all my experiments so far have not demonstrated what that might be.
Sigh. I just want to efficiently draw an array of glyphs at a corresponding array of positions in a view.
Any ideas what I'm getting wrong?
After much experimentation enabled by being whacked upside the head by Peter Hosey's response (even though some of it isn't quite right, many thanks!), here's the source of my confusion and an explanation I'm pretty sure is correct (well, the code is doing what I expect it to, anyway).
In the usual higher-level PostScript path/drawing model, drawing a character updates the current point (path end) to the position where a next character might appear, leaving the current user-space transform the same. But under the hood, the text matrix transform is translated by the glyph's width (or more accurately by an advance vector) so that the next character to be drawn can start at, or with respect to, a new text origin. The text matrix's scale factors remain unchanged after translation.
So the initial setup call to CGContextSetTextMatrix() to flip the vertical sense of the text matrix is still necessary (if user-space is similarly flipped), because otherwise both glyph-collection drawing routines will draw the glyphs upside-down w/r/t path drawing, no matter where the text drawing starts or which drawing routine is used.
Neither of the two glyph collection drawing routines affects the current path. They are lower-level than that. I found that I could intersperse either routine among path construction calls without affecting a path's position or shape.
In the code posted above, the position data that CGContextShowGlyphsAtPositions() uses to draw the glyph collection are all relative to the user-space point corresponding to the current text matrix's origin, which was translated to the right of the previously drawn '#' glyph. Because I was using such a large font size, position[0] was causing the next '#' glyph to be drawn outside the view's bounds, so it wasn't visible, but it was being drawn.
But there's still some nuances among the two routines. CGContextShowGlyphsAtPositions() can never be used to place glyphs at any absolute user-space position. So how do you tell it where to start? The answer (or at least one answer) is that CGContextShowGlyphsAtPoint() updates the origin of the text matrix to the given user-space point even if there are no glyphs to draw. And CGContextShowGlyphsAtPoint() must translate the text matrix after each glyph it draws, because what would be the point (so to speak) of drawing the entire glyph collection on top of one another.
So one can "move" to a non-path point in user-space using CGContextShowGlyphsAtPoint() with a glyph count of 0, and then one can call CGContextShowGlyphsAtPositions() (any number of times) with a vector of positions each of which will be treated relative to the text matrix's origin (or really, the user-space point corresponding to it) without the text matrix origin being updated at all when CGContextShowGlyphsAtPositions() returns.
Finally, note that the position data provided to CGContextShowGlyphsAtPositions() is in user-space coordinates. A comment in Apple's header file for these routines expressly says so.
One possibility is this, from the CGContextShowGlyphsAtPositions document:
The position of each glyph is specified in text space, and, as a consequence, is transformed through the text matrix to user space.
The text matrix is a separate property of the context, distinct from the graphics state's current transformation matrix.
It doesn't say that about CGContextShowGlyphsAtPoint:
This function displays an array of glyphs at the specified position in the user space.
(Emphasis added to both quotes.)
So, your text matrix is not actually used when you show glyphs from a single point.
But then, when you show glyphs at an array of positions, it is used, and you see the symptom of a wrong matrix. Specifically, your matrix to try to flip the text back the other way is wrong: it flips the coordinate system upside down. You are drawing outside of the view.
(Try setting it to scale by 0.5 instead of -1 and you'll see what I mean.)
My recommendation is to take out your CGContextSetTextMatrix call.
I have got a UITableView with cells that include a layer shadow and their subviews do too. So summed up I have about 5 shadows per cell. I reuse my cells but they change their height occasionally when the content exceeds a certain height (I say this because this could probably cause more draw calls with the 'optimisations' I enlist below).
Now, I have added the following optimisations:
// setting it opaque will tell the GPU not to blend the
// layer (-> less draw calls - only useful when layer completely opaque obviously)
myShadowView.opaque = YES;
// another significant factor was the shadowPath property,
// it significantly smoothed scrolling
myShadowView.layer.shadowOffset = CGSizeMake(0, -1);
myShadowView.layer.shadowOpacity = 0.08;
myShadowView.layer.shadowRadius = 1;
myShadowView.layer.shadowPath = [UIBezierPath bezierPathWithRect:shad.bounds].CGPath;
// the most important change
myShadowView.layer.shouldRasterize = YES;
myShadowView.layer.rasterizationScale = [UIScreen mainScreen].scale;
I am very very happy with the performance -- scrolling is smooth as baby skin -- but the shouldRasterize property comes with drawbacks. When I scroll down, it takes the cell a little while to load the bitmap. Is there any way to pre-render those cells ? Would a table without the reuse identifier get rid of the problem ?
I absolutely need the shadows, but I would not sacrifice performance for them. I hope someody can help.
I'm looking for some advice on how to proceed.
I'm working on a cocoa program (Objective-C) where I want to be able to draw over top of a bitmap image, defining areas that I can use to get information from the underlying image.
As an example, I'd like to create a box (or oval) and be able to get the average pixel value from the underlying image. Ultimately I want to designate a number of such regions where I am sampling the underlying image to provide various statistics.
Currently I'm using an NSImage class to draw my image but I'm not sure how to go about drawing an NSBezierPath over that image. Would I be better off using something other than NSImage?
Do I simply override the NSImage drawRect method so that it draws a series of NSBezierPath objects?
I would like to be able to save these outlined regions as a layer so that they are available in the future.
You can use a CGBitmapContext (for the bitmap), CGImageMasks (for the masking), and CGPaths or CGContext* drawing primitives for the lines and curves.
A complete answer would be quite long, but that gives you a starting point.
I'm writing a 2D RPG using the LWJGL and Java 1.6. By now, I have a 'World' class, which holds an ArrayList of Tile (interface with basic code for every Tile) and a GrassTile class, which makes the use of a Spritesheet.
When using Immediate mode to draw a grid of 64x64 GrassTiles I get around 100 FPS and do this by calling the .draw() method from each tile inside the ArrayList, which binds the spritesheet and draws a certain area of it (with glTexCoord2f()). So I heard it's better to use VBO's, got a basic tutorial and tried to implement them on the .draw() method.
Now there are two issues: I don't know how to bind only a certain area of a texture to a VBO (the whole texture would be simply glBindTexture()) so I tried using them with colours only.
That takes me to second issue: I got only +20 FPS (120 total) which is not really what I expected, so I suppose I'm doing something wrong. Also, I am making a single VBO for each GrassTile while iterating inside the ArrayList. I think that's kind of wrong, because I can simply throw all the tiles inside a single FloatBuffer.
So, how can I draw similar geometry in a better way and how can I bind only a certain area of a Texture to a VBO?
So, how can I draw similar geometry in a better way...
Like #Ian Mallett described; put all your vertex data into a single vertex buffer object. This makes it possible to render your map in one call. If your map get 1000 times bigger you may want to implement a camera solution which only draws the vertices that are being shown on the screen, but that is a question that will arise later if you're planning on a significantly bigger map.
...and how can I bind only a certain area of a Texture to a VBO?
You can only bind a whole texture. You have to point to a certain area of the texture that you want to be mapped.
Every texture coordinate relates to a specific vertex. Every tile relates to four vertices. Common tiles in your game will share the same texture, hence the 'tile map' name. Make use of that. Place all your tile textures in a texture sheet and bind that texture sheet.
For every new 'tile' you create, check whether the area is meant to be air, grass or ground and then point to the part of the texture that corresponds to what you intend.
Let's say your texture area in pixels are 100x100. The ground area is 15x15 from the lower left corner. Follow the logic above explains the example code being shown below:
// The vertexData array simply contains information
// about a tile's four vertices (or six
// vertices if you draw using GL_TRIANGLES).
mVertexBuffer.put(0, vertexData[0]);
mVertexBuffer.put(1, vertex[1]);
mVertexBuffer.put(2, vertex[2]);
mVertexBuffer.put(3, vertex[3]);
mVertexBuffer.put(4, vertex[4]);
mVertexBuffer.put(5, vertex[5]);
mVertexBuffer.put(6, vertex[6]);
mVertexBuffer.put(7, vertex[7]);
mVertexBuffer.put(8, vertex[8]);
mVertexBuffer.put(9, vertex[9]);
mVertexBuffer.put(10, vertex[10]);
mVertexBuffer.put(11, vertex[11]);
if (tileIsGround) {
mTextureCoordBuffer.put(0, 0.0f);
mTextureCoordBuffer.put(1, 0.0f);
mTextureCoordBuffer.put(2, 0.15f);
mTextureCoordBuffer.put(3, 0.0f);
mTextureCoordBuffer.put(4, 0.15f);
mTextureCoordBuffer.put(5, 0.15f);
mTextureCoordBuffer.put(6, 0.15f);
mTextureCoordBuffer.put(7, 0.0f);
} else { /* Other texture coordinates. */ }
You actually wrote the solution. The only difference is that you should upload the texture coordinates data to the GPU.
This is the key:
I am making a single VBO for each GrassTile while iterating inside the ArrayList.
Don't do this. You make a VBO once, and then you update it if necessary. Making textures, VBOs, shaders, is the slowest possible use of OpenGL--no wonder you're getting problematic framerates--you're doing it O(n) times, each frame.
I think that's kind of wrong, because I can['t?] simply throw all the tiles inside a single FloatBuffer.
You only gain performance when you batch draw calls. This means that when you draw your tiles, you should draw all of them at once with one VBO.
//Initialize
Make a single VBO (or two: one for vertex, one for texture
coordinates, whatever--the key point is O(1) VBOs).
Fill your VBO with ALL of your tiles' data.
//Main loop
while (true) {
Draw the VBO with a single draw call,
thus drawing all your tiles all at once.
}
What would be a better parctice, writing the drawing method inside the GameObject class or in the Game class?
GameObject obj = new GameObject();
obj.Draw();
Or
GameObject obj = new GameObject();
DrawGameObject(obj);
It depends on what Game and GameObject are responsible for. Typically, a GameObject would contain all the information about what to draw -- it'd have a model, for instance. But objects aren't independent of each other -- knowing what to draw isn't sufficient to know how to draw it.
So a Game probably knows a lot more than a GameObject. In particular, it might know:
whether or not a particular object should be drawn (e.g. it's occluded based on the current camera)
how to draw a particular object (shadows from other objects; reflections from nearby surfaces; ambient light factors; etc.)
etc.
Of the two options presented, it probably makes more sense to put the Draw method outside the GameObject.