void
PollingManager::getVideoAreaRect(Rect *result)
{
- int y0 = m_heightTiles, y1 = 0;
- int x0 = m_widthTiles, x1 = 0;
-
- for (int y = 0; y < m_heightTiles; y++) {
- for (int x = 0; x < m_widthTiles; x++) {
- if (!m_videoFlags[y * m_widthTiles + x])
- continue;
- if (y < y0) y0 = y;
- if (y > y1) y1 = y;
- if (x < x0) x0 = x;
- if (x > x1) x1 = x;
+ int *mx_hlen, *mx_vlen;
+ constructLengthMatrices(&mx_hlen, &mx_vlen);
+
+ int full_h = m_heightTiles;
+ int full_w = m_widthTiles;
+ int x, y;
+ Rect max_rect(0, 0, 0, 0);
+ Rect local_rect;
+
+ for (y = 0; y < full_h; y++) {
+ for (x = 0; x < full_w; x++) {
+ int max_w = mx_hlen[y * full_w + x];
+ int max_h = mx_vlen[y * full_w + x];
+ if (max_w > 2 && max_h > 1 && max_h * max_w > (int)max_rect.area()) {
+ local_rect.tl.x = x;
+ local_rect.tl.y = y;
+ findMaxLocalRect(&local_rect, mx_hlen, mx_vlen);
+ if (local_rect.area() > max_rect.area()) {
+ max_rect = local_rect;
+ }
+ }
}
}
- // Limit width and height at 800 and 576 correspondingly.
- if (x1 - x0 > 24)
- x1 = x0 + 24;
- if (y1 - y0 > 17)
- y1 = y0 + 17;
+ destroyLengthMatrices(mx_hlen, mx_vlen);
- result->tl.x = x0 * 32;
- result->tl.y = y0 * 32;
- result->br.x = (x1 + 1) * 32;
- result->br.y = (y1 + 1) * 32;
+ max_rect.tl.x *= 32;
+ max_rect.tl.y *= 32;
+ max_rect.br.x *= 32;
+ max_rect.br.y *= 32;
+ if (max_rect.br.x > m_width)
+ max_rect.br.x = m_width;
+ if (max_rect.br.y > m_height)
+ max_rect.br.y = m_height;
+ *result = max_rect;
- if (x1 >= x0) {
+ if (!result->is_empty()) {
fprintf(stderr, "Video rect %dx%d\tat(%d,%d)\n",
result->width(), result->height(), result->tl.x, result->tl.y);
}
}
+
+void
+PollingManager::constructLengthMatrices(int **pmx_h, int **pmx_v)
+{
+ // Handy shortcuts.
+ int h = m_heightTiles;
+ int w = m_widthTiles;
+
+ // Allocate memory.
+ int *mx_h = new int[h * w];
+ memset(mx_h, 0, h * w * sizeof(int));
+ int *mx_v = new int[h * w];
+ memset(mx_v, 0, h * w * sizeof(int));
+
+ int x, y, len, i;
+
+ // Fill in horizontal length matrix.
+ for (y = 0; y < h; y++) {
+ for (x = 0; x < w; x++) {
+ len = 0;
+ while (x + len < w && m_videoFlags[y * w + x + len]) {
+ len++;
+ }
+ for (i = 0; i < len; i++) {
+ mx_h[y * w + x + i] = len - i;
+ }
+ x += len;
+ }
+ }
+
+ // Fill in vertical length matrix.
+ for (x = 0; x < w; x++) {
+ for (y = 0; y < h; y++) {
+ len = 0;
+ while (y + len < h && m_videoFlags[(y + len) * w + x]) {
+ len++;
+ }
+ for (i = 0; i < len; i++) {
+ mx_v[(y + i) * w + x] = len - i;
+ }
+ y += len;
+ }
+ }
+
+ *pmx_h = mx_h;
+ *pmx_v = mx_v;
+}
+
+void
+PollingManager::destroyLengthMatrices(int *mx_h, int *mx_v)
+{
+ delete[] mx_h;
+ delete[] mx_v;
+}
+
+// NOTE: This function assumes that current tile has non-zero in mx_h[],
+// otherwise we get division by zero.
+void
+PollingManager::findMaxLocalRect(Rect *r, int mx_h[], int mx_v[])
+{
+ int idx = r->tl.y * m_widthTiles + r->tl.x;
+
+ // NOTE: Rectangle's maximum width and height are 25 and 18
+ // (in tiles, where each tile is usually 32x32 pixels).
+ int max_w = mx_h[idx];
+ if (max_w > 25)
+ max_w = 25;
+ int cur_h = 18;
+
+ int best_w = max_w;
+ int best_area = 1 * best_w;
+
+ for (int i = 0; i < max_w; i++) {
+ int h = mx_v[idx + i];
+ if (h < cur_h) {
+ cur_h = h;
+ if (cur_h * max_w <= best_area)
+ break;
+ }
+ if (cur_h * (i + 1) > best_area) {
+ best_w = i + 1;
+ best_area = cur_h * best_w;
+ }
+ }
+
+ r->br.x = r->tl.x + best_w;
+ r->br.y = r->tl.y + best_area / best_w;
+}
+