By Hand
If memory is not a very sparse resource, I consider working in bigger chunks.
Here's some pseudo-code.
class Chunk {
Chunk new(int size) {...}
void setPixel(int x, int y, int value) {...}
int getPixel(int x, int y) {...}
}
class Grid {
Map<int, Map<Chunk>> chunks;
Grid new(int chunkSize) {...}
void setPixel(int x, int y, int value) {
getChunk(x,y).setPixel(x % chunkSize, y % chunkSize, value);//actually the modulo could be right in Chunk::setPixel and getPixel for more safety
}
int getPixel(int x, int y) { /*along the lines of setPixel*/ }
private Chunk getChunk(int x, int y) {
x /= chunkSize;
y /= chunkSize;
Map<Chunk> row = chunks.get(y);
if (row == null) chunks.set(y, row = new Map<Chunk>());
Chunk ret = row.get(x);
if (ret == null) row.set(x, ret = new Chunk(chunkSize));
return ret;
}
}
This implementation is quite naive.
For one, it creates chunks in getPixel (basically it would be fine to simply return 0 or so, if no chunks was defined for that position). Secondly it is based on the assumption, that you have a sufficiently fast and scalable implementation of Map. To my knowledge every decent language has one.
Also you will have to play with the chunk size. For dense bitmaps, a big chunk size is good, for sparse bitmaps a smaller chunk size is better. In fact for very sparse ones, a "chunk size" of 1 is the best, rendering the "chunks" themselves obsolete and reducing the data structure to an int map of an int map of pixels.
Off the shelf
Another solution might be to look at some graphics libraries. They are actually quite good at drawing one 2D buffer into another. That would mean you'd simply allocate a bigger buffer and have the original drawn into it at the according coordinates.
As a general strategy: When having a "dynamically growing memory block", it is a good idea to allocate a multiple of it, once it is used up. This is rather memory intense, but significantly cuts allocation and copying costs. Most vector implementations allocate twice their size, when it's exceeded. So especially if you go with the off-the-shelf solution, don't extend you buffer just by 1 pixel, because only one pixel was requested. Allocated memory is cheap. Reallocating, copying and releasing is expensive.
extendXmethods in order to make thesetPixelandgetPixelones fast? – Peter Taylor Aug 30 '11 at 7:20