Вам нужен один SpaceManager, централизованный агент, который будет управлять распределением областей прямоугольника для данного BufferedImage. Для этого потребуется метод allocateRectangle (int sizeX, int sizeY), который найдет неиспользуемое пространство, пометит его как использованный и возвратит DrawingRectangle, и метод freeRectangle (DrawingRectangle dr), который освободит пространство, когда он больше не используется. .
DrawingRectangle - это ваш собственный класс, который имеет ссылку на BufferedImage и знает в нем свое собственное смещение. Он имеет свои собственные методы get / setRGB, которые просто добавляют свои смещения по X и Y и вызывают BufferedImage get / setRGB. Таким образом, клиенты этой системы просто получают DrawingRectangle и получают к нему доступ, как если бы это был его собственный BufferedImage с требуемым размером.
Хитрость в том, что ваши методы allocateRectangle и freeRectangle должны быть синхронизированы, чтобы процесс выделения одного не прерывается другим потоком, который затем пытается выделить свой собственный. Управлять двумерным пространством сложно, если вы пытаетесь оптимизировать упаковку, но я бы не стал беспокоиться, по крайней мере, сначала. Просто подумайте об этом, так как ряды были прямоугольниками, выровненными сверху. Если следующий новый не помещается в существующую строку, создайте новую строку, которая начинается чуть ниже самого высокого прямоугольника самого нижнего ряда.