I am using the routine for getting pixel colour (this one: http://www.markj.net/iphone-uiimage-pixel-color/ ) and am faced with frequent app crashes when using it. The relevant portion of the code:
unsigned char* data = CGBitmapContextGetData (cgctx);
if (data != NULL) {
int offset = (some calculations here);
int alpha = data[offset]; // <<<< crashes here
}
This code is linked to be ran on touchesBegan, touchesEnded and touchesMoved. It appears that the crashes occur during touchesEnded and touchesMoved events only, particularly when I start the touch on the target image, but move it off the boundaries of the image in the process.
Is there any way to check what is the size of the data in the array pointed to by data object? What could be going wrong there?
Edit:
The calculation of offset:
int offset = 4*((w*round(point.y)*x)+round(point.x)*x);
Where point is the point where touch occurs, w is the width if the image, x is the scale of the image (for hi-res images on retina displays).
I don't see anything wrong with the cgctx either. Basically, I am running the code from the link above almost unmodified, the particular code snippet I have problems with is in the function (UIColor*) getPixelColorAtLocation:(CGPoint)point so if you want the details of what the code does, just read the source there.
Edit: another thing is that this never happens in the simulator, but often happens when testing on a device.
Edit: Ideally I'd want to do nothing if the finger is currently not over the image, but have trouble figuring out when that happens. It looks like the relevant methods in SDK only show what view the touch originated in, not where it is now. How can I figure that out?
You didn't show all your work. Your offset calculation is likely returning either a negative number or a number well beyond the end of the buffer. Since CG* APIs often allocate rather large chunks of memory, often memory mapped, it is quite likely that the addresses before and after the allocation are unallocated/unmapped and, thus, access outside of the buffer leads to an immediate crash (as opposed to returning garbage).
Which is good. Easier to debug.
You did provide a clue:
move it off the boundaries of the
image in the process
I'd guess you modified the offset calculation to take the location of the touch. And that location has moved beyond the bounds of the image and, thus, leads to a nonsense offset and a subsequent crash.
Fix that, and your app will stop crashing here.
Does your image exactly occupy the entire bounds of the item being touched? I.e. does the thing handling the touches*: events have a bounds whose width and height are exactly the same as the image?
If not, you need to make sure you are correctly translating the coordinates from whatever is handling the touches to coordinates within the image. Also note that the layout of bytes in an image is heavily dependent on exactly how the image was created and what internal color model it is using.
Related
I am working on the real-time plot application where a stream of data is to be plotted on screen. Earlier using gtkmm2 I had done this using a custom widget (derived from Gtk::Bin) where I have a member function which creates a cairo context and does the plotting.
Now with gtkmm3 I am unable to plot in any method other than on_draw. Here's what my custom draw method body looks like
Gtk::Allocation oAllocation = get_allocation();
Glib::RefPtr <Gdk::Window> refWindow = get_window();
Cairo::RefPtr <Cairo::Context> refContext =
refWindow->create_cairo_context();
refWindow->begin_paint_rect(oAllocation); //added later
refContext->save();
refContext->reset_clip();
refContext->set_source_rgba(1,
1,
1,
1);
refContext->move_to(oAllocation.get_x(),
oAllocation.get_y());
refContext->line_to(oAllocation.get_x()
+ oAllocation.get_width(),
oAllocation.get_y()
+ oAllocation.get_height());
refContext->stroke();
refContext->restore();
refWindow->end_paint();
Initially I derived the class from Gtk::DrawingArea then tried with Gtk::Bin while adding the begin_paint_rect call.
Is it forbidden to draw in any place other than on_draw?
For something like a plot (or anything that is rather complex to draw) I advise to use a buffer; I lost a month of my life because I read that gtkmm3 does buffering so that using "double buffering" isn't needed anymore (as opposed to gtkmm2), but it aint that simple (read: that isn't true).
So, what you should do is just draw to your own surface; and every time you change something call queue_draw_region or queue_draw_area.
Then in on_draw get the list of clip rectangles and copy those from your private surface to the cr that is passed to the on_draw function. Cairo normally does
the exact same thing (or so they claim), copying what you just copied again, to the screen; so you should turn that off (this should be possible I read).
The reason you can't use Cairo's buffering is because it doesn't KEEP that buffer; what you get is some corrupted surface, so you are forced to redraw EVERYTHING inside the clip rectangle list. That wouldn't be too bad if you (your application) was the only one making changes (as per your queue_draw_* calls): then you could set a flag, invalidate the part(s) that needs redrawing and simply postpone the draw until you get to on_draw. But sometimes on_draw is called for other reasons, for example, when you open a menu that goes over your drawing area. I think this is a bug (or a design error) but it is the way it is. The result is that you can't know what you have to redraw EXCEPT by looking at the clip rectangle list; which makes it incredibly hard to just draw a part of your area unless your drawing is made up of many separate rectangles (like, say, a chess board). The only feasible way is to keep a full copy of the image in memory (your private surface) and just copy the clip rectangle list from there when in on_draw.
Is it forbidden to draw in any place other than on_draw?
Basically: Yes.
The idea is that you call gtk_widget_queue_draw() or gtk_widget_queue_draw_area() when you want to cause a redraw.
https://developer.gnome.org/gtk3/stable/GtkWidget.html#gtk-widget-queue-draw
https://developer.gnome.org/gtk3/stable/GtkWidget.html#gtk-widget-queue-draw-area
I am working on a painting program where I draw interactive strokes via an MTKView. If I set the renderPassDescriptor loadAction to 'clear':
renderPassDescriptor?.colorAttachments[0].loadAction = .clear
The frame buffer, as expected, shows the latest contents of renderCommandEncoder?.drawPrimitives, which is this case is the leading edge of the brushstroke.
If I set loadAction to 'load':
renderPassDescriptor?.colorAttachments[0].loadAction = .load
The frame buffer flashes like crazy and shows a patchy trail of what I've just drawn. I now understand that the flashing is likely caused by MTKView's default triple buffering in place. Thus, each time I write to the currentDrawable, I'm likely writing to one of 3 cycling buffers. Please correct me if I'm wrong.
My question is, what do I need to do to draw a clean brushstroke without the frame buffer flashing as it does now? In other words, is there a way to have a master buffer that gets updated with the latest contents of commandEncoder?
You can use a texture of your own as the color attachment of a render pass. You don't have to use the texture of a drawable. In that way, you can use the .load action without getting garbage or weird flashing or whatever. You will have full control over which texture you're rendering to and what its contents are.
After rendering to that texture for a render pass, you then need to blit that to the drawable's texture for display.
The main complication here is that you won't have the benefits of double- or triple-buffering. You'll lose a certain amount of performance, since everything will have to be synced to that one texture's state. I suspect, though, that you don't need that much performance, since this is interactive and only has to keep up with the speed of a human.
manageTiles:(ccTime)dt
{
tiles.position = ccp(tiles.position.x-speed*dt,tiles.position.y);
}
I am moving my batch node but it is causing tears, I think its because dt is .3, but how else do i move it based on time without causing tears. The tears are very very small barley noticeable, but still bothers me.
You mean black lines (gaps) between the batched sprites? Tearing is when you draw to the framebuffer while the screen updates.
Two solutions: move the batchnode instead of individual tiles. If the tiles always stay fixed relative to each other that's the fastest solution. Otherwise cast tiles position to int to ensure they're always at pixel locations, otherwise subpixel rendering will cause those gaps. You may also have to cast the batch node's position to int if you use the first solution.
In apps like iDraft and Penultimate, they perform undos and redos very well without any delay.
I tried many approaches. Currently, my testing app writes raw pixel data directly to a file after each undo using [NSData writeToFile:atomically:] but I am getting 0.6s delay.
Can anyone give some hints on it?
I don’t know iDraft nor Penultimate, but chances are they have a simpler drawing model than you have. When writing a drawing app you can choose between two essential drawing representations: either you track raw pixels, or you track drawing objects like lines, circles and so on. (Or, in other words, you choose between pixel and vector representation.)
When you draw using vectors, you don’t track the individual pixels. Instead you know there should be line between points X and Y of given width, color and other params. And when you are to draw such a representation, you call Quartz to stroke the line. In this case the model (the drawing representation) consists of a few numbers, takes little memory and therefore you can have many versions of a single drawing in a memory, allowing for a quick and convenient undo and redo.
Keep your undo stack in memory. Don't write to disk for every operation. Whether you keep around bitmaps or vectors, your file ops shouldn't be on the critical path for every paint operation you do.
If your data model is full bitmaps, keep just the changed rect for undo/redo.
As previously said, you probably don't need to write the data to disk for every operation, also in a pixel based case, unless you are trying to undo a full screen filter all you need to keep is the data contained within the bounding rectangle of the brush stroke that the user performed.
You can double buffer your drawing, i.e. keep a copy of the image before the draw, draw into the copy, determine the bounding rect of the user operation, copy and retain the appropriate data from the original (with size and location information). On undo you take that copy and paste it over the modified area.
This method extends to redo, on undo take the area that you are going to be overwriting and store it.
I am making a simple iPhone drawing program as a personal side-project.
I capture touches event in a subclassed UIView and render the actual stuff to a seperate CGLayer. After each render, I call [self setNeedsLayout] and in the drawRect: method I draw the CGLayer to the screen context.
This all works great and performs decently for drawing rectangles. However, I just want a simple "freehand" mode like a lot of other iPhone applications have.
The way I thought to do this was to create a CGMutablePath, and simply:
CGMutablePathRef path;
-(void)touchBegan {
path = CGMutablePathCreate();
}
-(void)touchMoved {
CGPathMoveToPoint(path,NULL,x,y);
CGPathAddLineToPoint(path,NULL,x,y);
}
-(void)drawRect:(CGContextRef)context {
CGContextBeginPath(context);
CGContextAddPath(context,path);
CGContextStrokePath(context);
}
However, after drawing for more than 1 second, performance degrades miserably.
I would just draw each line into the off-screen CGLayer, if it were not for variable opacity! The less-than-100% opacity causes dots to be left on the screen connecting the lines. I have looked at CGContextSetBlendingMode() but alas I cannot find an answer.
Can anyone point me in the right direction? Other iPhone apps are able to do this with very good efficiency.
The problem is that with CGStrokePath() the current mutable path gets closed and drawn and a new path is created when you move your finger. So you probably end up with a lot of paths for one touch "session", at least that's what your pseudocode seems to do.
You can try to begin a new mutable path when touches begin, use CGAddLineToPoint() when the touches move und end the path when touches end (much like your pseudocode shows). But in the draw method, you draw a copy of the current mutable path, and the actual mutable path is still being elongated until the touches end, so you only get one path for the whole touch session. After the touches end you can add the path permanently - you can for example put all paths into an array and iterate over them in the draw method.
What SanHolo said - plus you may want to throttle the adding of points, so it only adds a new point no more often than every 10ms, say (you'd need to play with the interval). You can do that with a simple timer.
Also, how are you instructing the view that it needs to redraw itself? You might want to throttle that too - and it could be on a longer interval than the point capturing (e.g. capture points no more than every 10ms, and redraw no more often than every 200ms - again you'd need to play with the numbers).
In both cases you'd need to make sure that, if nothing happens for longer than the interval the last point is captured, or the redraw is requested. That's where the timer comes in.