À mão
Se a memória não é um recurso muito escasso, considero trabalhar em partes maiores.
Aqui estão alguns pseudo-códigos.
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;
}
}
Essa implementação é bastante ingênua.
Por um lado, ele cria chunks no getPixel (basicamente seria bom simplesmente retornar 0 ou mais, se nenhum chunks fosse definido para essa posição). Em segundo lugar, é baseado no pressuposto de que você possui uma implementação suficientemente rápida e escalável do Map. Que eu saiba, toda linguagem decente tem uma.
Além disso, você terá que jogar com o tamanho do pedaço. Para bitmaps densos, um tamanho grande de fragmento é bom; para bitmaps esparsos, um tamanho menor de fragmento é melhor. De fato, para os muito esparsos, um "tamanho de pedaço" igual a 1 é o melhor, tornando os "pedaços" obsoletos e reduzindo a estrutura de dados a um mapa int de um mapa int de pixels.
Fora da prateleira
Outra solução pode ser procurar em algumas bibliotecas gráficas. Eles são realmente muito bons em desenhar um buffer 2D em outro. Isso significaria que você simplesmente alocaria um buffer maior e teria o original desenhado nas coordenadas correspondentes.
Como estratégia geral: ao ter um "bloco de memória que cresce dinamicamente", é uma boa idéia alocar um múltiplo dele, uma vez usado. Isso é bastante intenso na memória, mas reduz significativamente os custos de alocação e cópia . A maioria das implementações de vetores aloca duas vezes seu tamanho, quando é excedida. Portanto, especialmente se você optar pela solução pronta para uso, não estenda seu buffer apenas em 1 pixel, porque apenas um pixel foi solicitado. A memória alocada é barata. Realocar, copiar e liberar é caro.