A bounding box check isn't O(1), it's O(n) for n shapes, THEN you have to run edge detection, and only then if all your shapes are Polygons. The issue with bounding boxes is that, in all but the most trivial cases, they are expensive to compute. If you add in any Affine Transforms ( rotate, scale, shear ) the task becomes even worse, and worse still is the bounding boxes of Quadratic or Bezier curves.
With a large number of shapes, you can gain some speed using Quadtree's
http://en.wikipedia.org/wiki/Quadtree and other types of spatial indexing. But you have 4 checks per "primitive", not 2 (x > box.left && x < box.right && y > box.top && y < box.bottom), so you need to keep 4 coordinates. For a large number of shapes, this uses memory, more memory if you choose to use a Quadtree or similar spatial indexing.
So, I have found the color indexing option the best, it's not quite O(1), as it's performance depends on the hashmap lookup time. The downside is you only have 167772316 possible color keys, I feel this is acceptable limitation. This could be helped by having multiple "layers" with each layer having it's own full colorspace. Hashmap lookup time should be near optimal
since an analysis of collisions with out keys yeilds about 1100 out of 167772316, which is pretty good. As a side note, HashMap in GWT is not an optimal implementation for this case. The other downside is you have to draw also in the "ghost canvas", but we found our drawing time so fast it was not a concern.
isPointInPath only works while the Path is CURRENT in the Canvas context 2d, which you lose after drawing another shape or context.restore(), so it's only possible to test IMMEDIATELY after you draw the shape, before you do any context.restore(), which means you have to be drawing somewhere while your doing hit testing, probably another ghost canvas. This also only works for "closed shapes", not something like a PolyLine or Arc.