Pad the array out to a square power of 2, by adding zeroes. Then pyramidize it into a stack, shrinking by a factor of 2 in each dimension each time. At each level, the value of each element is equal to the max of the 4 corresponding elements in the next largest array in the pyramid. This will take an extra 1/3 storage space on top of the padded array size, like mip-maps do.
Write a recursive function that takes a single element of a given array level and checks if the query region covers the bounds covered by it. If not then it checks each region in the next largest array that overlaps the query region, returns the recursively-computed max of each.
Pseudo-code:
int getMax(Rect query_rect, int level, int level_x, int level_y)
{
int level_factor = 1 << level;
// compute the area covered by this array element:
Rect level_rect(level_x * level_factor, level_y * level_factor,
(level_x + 1) * level_factor, (level_y + 1) * level_factor);
// if the regions don't overlap then ignore:
if(!query_rect.intersects(level_rect))
return -1;
// query rect entirely contains this region, return precomputed max:
if(query_rect.contains(level_rect))
return pyramid[level][level_x][level_y];
if(level == 0)
return -1; // ran out of levels
int max = getMax(query_rect, level - 1, level_x * 2, level_y * 2);
max = maxValue(max, getMax(query_rect, level - 1, (level_x * 2) + 1, level_y * 2);
max = maxValue(max, getMax(query_rect, level - 1, level_x * 2, (level_y * 2) + 1);
max = maxValue(max, getMax(query_rect, level - 1, (level_x * 2) + 1, (level_y * 2) + 1);
return max;
}
Call it at the top level (the capstone 1x1 array in the pyramid) initially:
int max = getMax(query_rect, 10, 0, 0);
There will be a minimum size of query rectangle, below which it will be cheaper just to iterate over all the elements. You could adapt this to work with non-square arrays, it will just require some extra tests against the array size at each level when recursing.