blob: 00580e01d407a15615a5379139bd5c632025b68d [file] [log] [blame]
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +00001/* Copyright (C) 2004-2005 Constantin Kaplinsky. All Rights Reserved.
2 *
3 * This is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This software is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16 * USA.
17 */
18//
19// PollingManager.cxx
20//
21
Constantin Kaplinskyce676c62006-02-08 13:36:58 +000022// FIXME: Don't compare pixels already marked as changed.
23// FIXME: Use Image::copyPixels() instead of Image::updateRect()?
24// In that case, note the fact that arguments are not checked.
25
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +000026#include <stdio.h>
27#include <string.h>
28#include <sys/time.h>
29#include <X11/Xlib.h>
30#include <rfb/VNCServer.h>
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +000031#include <rfb/Configuration.h>
Constantin Kaplinskyce676c62006-02-08 13:36:58 +000032#include <rfb/ServerCore.h>
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +000033
34#include <x0vncserver/Image.h>
35#include <x0vncserver/PollingManager.h>
36
Constantin Kaplinskyce676c62006-02-08 13:36:58 +000037BoolParameter PollingManager::pollPointer
38("PollPointer",
39 "Poll area under the pointer with higher priority",
40 true);
41
42IntParameter PollingManager::pollingType
43("PollingType",
44 "Polling algorithm to use (0..3)",
45 3);
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +000046
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +000047const int PollingManager::m_pollingOrder[32] = {
48 0, 16, 8, 24, 4, 20, 12, 28,
49 10, 26, 18, 2, 22, 6, 30, 14,
50 1, 17, 9, 25, 7, 23, 15, 31,
51 19, 3, 27, 11, 29, 13, 5, 21
52};
53
54//
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +000055// Constructor.
56//
57// Note that dpy and image should remain valid during the object
58// lifetime, while factory is used only in the constructor itself.
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +000059//
60
61PollingManager::PollingManager(Display *dpy, Image *image,
62 ImageFactory *factory)
Constantin Kaplinskyce676c62006-02-08 13:36:58 +000063 : m_dpy(dpy), m_server(0), m_image(image), m_pointerPosKnown(false),
64 m_pollingStep(0)
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +000065{
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +000066 // Save width and height of the screen (and the image).
67 m_width = m_image->xim->width;
68 m_height = m_image->xim->height;
69
70 // Compute width and height in 32x32 tiles.
71 m_widthTiles = (m_width + 31) / 32;
72 m_heightTiles = (m_height + 31) / 32;
73
Constantin Kaplinskyce676c62006-02-08 13:36:58 +000074 // Create additional images used in the polling algorithm.
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +000075 // FIXME: verify that these images use the same pixel format as in m_image.
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +000076 m_rowImage = factory->newImage(m_dpy, m_width, 1);
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +000077 m_tileImage = factory->newImage(m_dpy, 32, 32);
Constantin Kaplinskyce676c62006-02-08 13:36:58 +000078 m_areaImage = factory->newImage(m_dpy, 128, 128);
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +000079
Constantin Kaplinskydc54fc12005-10-19 13:57:16 +000080 // FIXME: Extend the comment.
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +000081 // Create a matrix with one byte per each 32x32 tile. It will be
82 // used to limit the rate of updates on continuously-changed screen
83 // areas (like video).
84 int numTiles = m_widthTiles * m_heightTiles;
85 m_statusMatrix = new char[numTiles];
86 memset(m_statusMatrix, 0, numTiles);
Constantin Kaplinskydc54fc12005-10-19 13:57:16 +000087
88 // FIXME: Extend the comment.
89 // Create a matrix with one byte per each 32x32 tile. It will be
90 // used to limit the rate of updates on continuously-changed screen
91 // areas (like video).
92 m_rateMatrix = new char[numTiles];
93 m_videoFlags = new char[numTiles];
94 m_changedFlags = new char[numTiles];
95 memset(m_rateMatrix, 0, numTiles);
96 memset(m_videoFlags, 0, numTiles);
97 memset(m_changedFlags, 0, numTiles);
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +000098}
99
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000100PollingManager::~PollingManager()
101{
Constantin Kaplinskydc54fc12005-10-19 13:57:16 +0000102 delete[] m_changedFlags;
103 delete[] m_videoFlags;
104 delete[] m_rateMatrix;
105
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000106 delete[] m_statusMatrix;
Constantin Kaplinskyce676c62006-02-08 13:36:58 +0000107
108 delete m_areaImage;
109 delete m_tileImage;
110 delete m_rowImage;
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000111}
112
113//
114// Register VNCServer object.
115//
116
117void PollingManager::setVNCServer(VNCServer *s)
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +0000118{
119 m_server = s;
120}
121
122//
Constantin Kaplinskyce676c62006-02-08 13:36:58 +0000123// Update current pointer position which may be used as a hint for
124// polling algorithms.
125//
126
127void PollingManager::setPointerPos(const Point &pos)
128{
129 m_pointerPos = pos;
130 m_pointerPosKnown = true;
131}
132
133//
134// Indicate that current pointer position is unknown.
135// FIXME: Perhaps this should be done automatically after a number of
136// polling cycles if the cursor position have not been changed?
137//
138
139void PollingManager::unsetPointerPos()
140{
141 m_pointerPosKnown = false;
142}
143
144//
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +0000145// DEBUG: a version of poll() measuring time spent in the function.
146//
147
148void PollingManager::pollDebug()
149{
150 struct timeval timeSaved, timeNow;
151 struct timezone tz;
152 timeSaved.tv_sec = 0;
153 timeSaved.tv_usec = 0;
154 gettimeofday(&timeSaved, &tz);
155
156 poll();
157
158 gettimeofday(&timeNow, &tz);
159 int diff = (int)((timeNow.tv_usec - timeSaved.tv_usec + 500) / 1000 +
160 (timeNow.tv_sec - timeSaved.tv_sec) * 1000);
161 if (diff != 0)
162 fprintf(stderr, "DEBUG: poll(): %4d ms\n", diff);
163}
164
165//
166// Search for changed rectangles on the screen.
167//
168
169void PollingManager::poll()
170{
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000171 bool someChanges = false;
172
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000173 switch((int)pollingType) {
174 case 0:
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000175 someChanges = poll_Dumb();
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000176 break;
177 case 1:
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000178 someChanges = poll_Traditional();
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000179 break;
Constantin Kaplinskydc54fc12005-10-19 13:57:16 +0000180 case 2:
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000181 someChanges = poll_SkipCycles();
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000182 break;
Constantin Kaplinskydc54fc12005-10-19 13:57:16 +0000183//case 3:
184 default:
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000185 someChanges = poll_DetectVideo();
Constantin Kaplinskydc54fc12005-10-19 13:57:16 +0000186 break;
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000187 }
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000188
189 if (someChanges)
190 m_server->tryUpdate();
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000191}
192
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000193bool PollingManager::poll_DetectVideo()
Constantin Kaplinskydc54fc12005-10-19 13:57:16 +0000194{
195 if (!m_server)
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000196 return false;
Constantin Kaplinskydc54fc12005-10-19 13:57:16 +0000197
198 const int GRAND_STEP_DIVISOR = 8;
199 const int VIDEO_THRESHOLD_0 = 3;
200 const int VIDEO_THRESHOLD_1 = 5;
201
202 bool grandStep = (m_pollingStep % GRAND_STEP_DIVISOR == 0);
203
Constantin Kaplinskyce676c62006-02-08 13:36:58 +0000204 // FIXME: Save shortcuts in member variables?
Constantin Kaplinskydc54fc12005-10-19 13:57:16 +0000205 int scanLine = m_pollingOrder[m_pollingStep++ % 32];
206 int bytesPerPixel = m_image->xim->bits_per_pixel / 8;
207 int bytesPerLine = m_image->xim->bytes_per_line;
208
209 Rect rect;
210 int nTilesChanged = 0;
211 int idx = 0;
212
213 for (int y = 0; y * 32 < m_height; y++) {
214 int tile_h = (m_height - y * 32 >= 32) ? 32 : m_height - y * 32;
215 if (scanLine >= tile_h)
216 break;
217 int scan_y = y * 32 + scanLine;
218 m_rowImage->get(DefaultRootWindow(m_dpy), 0, scan_y);
219 char *ptr_old = m_image->xim->data + scan_y * bytesPerLine;
220 char *ptr_new = m_rowImage->xim->data;
221 for (int x = 0; x * 32 < m_width; x++) {
222 int tile_w = (m_width - x * 32 >= 32) ? 32 : m_width - x * 32;
223 int nBytes = tile_w * bytesPerPixel;
224
225 char wasChanged = (memcmp(ptr_old, ptr_new, nBytes) != 0);
226 m_rateMatrix[idx] += wasChanged;
227
228 if (grandStep) {
229 if (m_rateMatrix[idx] <= VIDEO_THRESHOLD_0) {
230 m_videoFlags[idx] = 0;
231 } else if (m_rateMatrix[idx] >= VIDEO_THRESHOLD_1) {
232 m_videoFlags[idx] = 1;
233 }
234 m_rateMatrix[idx] = 0;
235 }
236
237 m_changedFlags[idx] |= wasChanged;
238 if ( m_changedFlags[idx] && (!m_videoFlags[idx] || grandStep) ) {
239 if (tile_w == 32 && tile_h == 32) {
240 m_tileImage->get(DefaultRootWindow(m_dpy), x * 32, y * 32);
241 } else {
242 m_tileImage->get(DefaultRootWindow(m_dpy), x * 32, y * 32,
243 tile_w, tile_h);
244 }
245 m_image->updateRect(m_tileImage, x * 32, y * 32);
246 rect.setXYWH(x * 32, y * 32, tile_w, tile_h);
247 m_server->add_changed(rect);
248 nTilesChanged++;
249 m_changedFlags[idx] = 0;
250 }
251
Constantin Kaplinskydc54fc12005-10-19 13:57:16 +0000252 ptr_old += nBytes;
253 ptr_new += nBytes;
254 idx++;
255 }
256 }
257
Constantin Kaplinsky6df69902006-02-03 11:42:00 +0000258 if (grandStep)
259 adjustVideoArea();
260
Constantin Kaplinskyce676c62006-02-08 13:36:58 +0000261 // FIXME: Exclude area near the pointer from the comparisons above.
262 // FIXME: Code duplication.
263 if (pollPointer && m_pointerPosKnown && pollPointerArea())
264 nTilesChanged++;
265
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000266 return (nTilesChanged != 0);
Constantin Kaplinskydc54fc12005-10-19 13:57:16 +0000267}
268
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000269bool PollingManager::poll_SkipCycles()
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000270{
271 if (!m_server)
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000272 return false;
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000273
274 enum {
275 NOT_CHANGED, CHANGED_ONCE, CHANGED_AGAIN
276 };
277
278 bool grandStep = (m_pollingStep % 8 == 0);
279
280 int nTilesChanged = 0;
281 int scanLine = m_pollingOrder[m_pollingStep++ % 32];
282 int bytesPerPixel = m_image->xim->bits_per_pixel / 8;
283 int bytesPerLine = m_image->xim->bytes_per_line;
284 char *pstatus = m_statusMatrix;
285 bool wasChanged;
286 Rect rect;
287
288 for (int y = 0; y * 32 < m_height; y++) {
289 int tile_h = (m_height - y * 32 >= 32) ? 32 : m_height - y * 32;
290 if (scanLine >= tile_h)
291 scanLine %= tile_h;
292 int scan_y = y * 32 + scanLine;
293 m_rowImage->get(DefaultRootWindow(m_dpy), 0, scan_y);
294 char *ptr_old = m_image->xim->data + scan_y * bytesPerLine;
295 char *ptr_new = m_rowImage->xim->data;
296 for (int x = 0; x * 32 < m_width; x++) {
297 int tile_w = (m_width - x * 32 >= 32) ? 32 : m_width - x * 32;
298 int nBytes = tile_w * bytesPerPixel;
299
300 if (grandStep || *pstatus != CHANGED_AGAIN) {
301 wasChanged = (*pstatus == CHANGED_AGAIN) ?
302 true : (memcmp(ptr_old, ptr_new, nBytes) != 0);
303 if (wasChanged) {
304 if (grandStep || *pstatus == NOT_CHANGED) {
305 if (tile_w == 32 && tile_h == 32) {
306 m_tileImage->get(DefaultRootWindow(m_dpy), x * 32, y * 32);
307 } else {
308 m_tileImage->get(DefaultRootWindow(m_dpy), x * 32, y * 32,
309 tile_w, tile_h);
310 }
311 m_image->updateRect(m_tileImage, x * 32, y * 32);
312 rect.setXYWH(x * 32, y * 32, tile_w, tile_h);
313 m_server->add_changed(rect);
314 nTilesChanged++;
315 *pstatus = CHANGED_ONCE;
316 } else {
317 *pstatus = CHANGED_AGAIN;
318 }
319 } else if (grandStep) {
320 *pstatus = NOT_CHANGED;
321 }
322 }
323
324 ptr_old += nBytes;
325 ptr_new += nBytes;
326 pstatus++;
327 }
328 }
329
Constantin Kaplinskyce676c62006-02-08 13:36:58 +0000330 // FIXME: Exclude area near the pointer from the comparisons above.
331 // FIXME: Code duplication.
332 if (pollPointer && m_pointerPosKnown && pollPointerArea())
333 nTilesChanged++;
334
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000335 return (nTilesChanged != 0);
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000336}
337
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000338bool PollingManager::poll_Traditional()
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000339{
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +0000340 if (!m_server)
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000341 return false;
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +0000342
343 int nTilesChanged = 0;
344 int scanLine = m_pollingOrder[m_pollingStep++ % 32];
345 int bytesPerPixel = m_image->xim->bits_per_pixel / 8;
346 int bytesPerLine = m_image->xim->bytes_per_line;
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +0000347 Rect rect;
348
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000349 for (int y = 0; y * 32 < m_height; y++) {
350 int tile_h = (m_height - y * 32 >= 32) ? 32 : m_height - y * 32;
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +0000351 if (scanLine >= tile_h)
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000352 break;
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +0000353 int scan_y = y * 32 + scanLine;
354 m_rowImage->get(DefaultRootWindow(m_dpy), 0, scan_y);
355 char *ptr_old = m_image->xim->data + scan_y * bytesPerLine;
356 char *ptr_new = m_rowImage->xim->data;
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000357 for (int x = 0; x * 32 < m_width; x++) {
358 int tile_w = (m_width - x * 32 >= 32) ? 32 : m_width - x * 32;
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +0000359 int nBytes = tile_w * bytesPerPixel;
360 if (memcmp(ptr_old, ptr_new, nBytes)) {
361 if (tile_w == 32 && tile_h == 32) {
362 m_tileImage->get(DefaultRootWindow(m_dpy), x * 32, y * 32);
363 } else {
364 m_tileImage->get(DefaultRootWindow(m_dpy), x * 32, y * 32,
365 tile_w, tile_h);
366 }
367 m_image->updateRect(m_tileImage, x * 32, y * 32);
368 rect.setXYWH(x * 32, y * 32, tile_w, tile_h);
369 m_server->add_changed(rect);
370 nTilesChanged++;
371 }
372 ptr_old += nBytes;
373 ptr_new += nBytes;
374 }
375 }
376
Constantin Kaplinskyce676c62006-02-08 13:36:58 +0000377 // FIXME: Exclude area near the pointer from the comparisons above.
378 // FIXME: Code duplication.
379 if (pollPointer && m_pointerPosKnown && pollPointerArea())
380 nTilesChanged++;
381
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000382 return (nTilesChanged != 0);
Constantin Kaplinskyaf1891c2005-09-29 06:18:28 +0000383}
384
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000385//
386// Simplest polling method, from the original x0vncserver of VNC4.
387//
388
389bool PollingManager::poll_Dumb()
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000390{
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000391 if (!m_server)
392 return false;
393
394 m_image->get(DefaultRootWindow(m_dpy));
395 Rect rect(0, 0, m_width, m_height);
396 m_server->add_changed(rect);
397
Constantin Kaplinskyce676c62006-02-08 13:36:58 +0000398 // Report that some changes have been detected.
Constantin Kaplinskyc42eab22006-02-01 05:59:21 +0000399 return true;
Constantin Kaplinsky14cd5472005-09-29 17:12:11 +0000400}
Constantin Kaplinsky6df69902006-02-03 11:42:00 +0000401
402//
Constantin Kaplinskyce676c62006-02-08 13:36:58 +0000403// Compute coordinates of the rectangle around the pointer.
Constantin Kaplinsky6df69902006-02-03 11:42:00 +0000404//
405
Constantin Kaplinskyce676c62006-02-08 13:36:58 +0000406void PollingManager::computePointerArea(Rect *r)
407{
408 int x = m_pointerPos.x - 64;
409 int y = m_pointerPos.y - 64;
410 int w = 128;
411 int h = 128;
412 if (x < 0) {
413 w += x; x = 0;
414 }
415 if (x + w > m_width) {
416 w = m_width - x;
417 }
418 if (y < 0) {
419 h += y; y = 0;
420 }
421 if (y + h > m_height) {
422 h = m_height - y;
423 }
424
425 r->setXYWH(x, y, w, h);
426}
427
428//
429// Poll the area under current pointer position. Each pixel of the
430// area should be compared. Using such polling option gives higher
431// priority to screen area under the pointer.
432//
433// ASSUMES: (m_server != NULL && m_pointerPosKnown != false)
434//
435
436bool PollingManager::pollPointerArea()
437{
438 Rect r;
439 computePointerArea(&r);
440
441 // Shortcuts for coordinates.
442 int x = r.tl.x, y = r.tl.y;
443 int w = r.width(), h = r.height();
444
445 // Get new pixels.
446 if (w == 128 && h == 128) {
447 m_areaImage->get(DefaultRootWindow(m_dpy), x, y);
448 } else {
449 m_areaImage->get(DefaultRootWindow(m_dpy), x, y, w, h);
450 }
451
452 // Now, try to minimize the rectangle by cutting out unchanged
453 // borders (at top and bottom).
454 //
455 // FIXME: Perhaps we should work on 32x32 tiles (properly aligned)
456 // to produce a region instead of a rectangle. If there would
457 // be just one universal polling algorithm, it could be
458 // better to integrate pointer area polling into that
459 // algorithm, instead of a separate pollPointerArea()
460 // function.
461
462 // Shortcuts.
463 int bytesPerPixel = m_image->xim->bits_per_pixel / 8;
464 int oldBytesPerLine = m_image->xim->bytes_per_line;
465 int newBytesPerLine = m_areaImage->xim->bytes_per_line;
466 char *oldPtr = m_image->xim->data + y * oldBytesPerLine + x * bytesPerPixel;
467 char *newPtr = m_areaImage->xim->data;
468
469 // Check and cut out unchanged rows at the top.
470 int ty;
471 for (ty = 0; ty < h; ty++) {
472 if (memcmp(oldPtr, newPtr, w * bytesPerPixel) != 0)
473 break;
474 oldPtr += oldBytesPerLine;
475 newPtr += newBytesPerLine;
476 }
477 if (ty == h) {
478 return false; // no changes at all
479 }
480 y += ty; h -= ty;
481
482 // Check and cut out unchanged rows at the bottom.
483 oldPtr = m_image->xim->data + (y+h-1) * oldBytesPerLine + x * bytesPerPixel;
484 newPtr = m_areaImage->xim->data + (ty+h-1) * newBytesPerLine;
485 int by;
486 for (by = 0; by < h - 1; by++) {
487 if (memcmp(oldPtr, newPtr, w * bytesPerPixel) != 0)
488 break;
489 oldPtr -= oldBytesPerLine;
490 newPtr -= newBytesPerLine;
491 }
492 h -= by;
493
494 // Copy pixels.
495 m_image->updateRect(m_areaImage, x, y, 0, ty, w, h);
496
497 // Report updates to the server.
498 Rect rect(x, y, x+w, y+h);
499 m_server->add_changed(rect);
500 return true;
501}
502
503//
504// Make video area pattern more regular.
505//
506// FIXME: Replace the above with a normal comment.
507// FIXME: Is the function efficient enough?
508//
509
Constantin Kaplinsky6df69902006-02-03 11:42:00 +0000510void PollingManager::adjustVideoArea()
511{
512 char newFlags[m_widthTiles * m_heightTiles];
513 char *ptr = newFlags;
514 int x, y;
515
516 // DEBUG:
517 // int nVideoTiles = 0;
518
519 for (y = 0; y < m_heightTiles; y++) {
520 for (x = 0; x < m_widthTiles; x++) {
521
522 // DEBUG:
523 // nVideoTiles += m_videoFlags[y * m_widthTiles + x];
524
525 int weightedSum = 0, n;
526 if (y > 0 && x > 0) {
527 n = (m_videoFlags[ y * m_widthTiles + (x-1)] +
528 m_videoFlags[(y-1) * m_widthTiles + (x-1)] +
529 m_videoFlags[(y-1) * m_widthTiles + x ]);
530 if (n == 3) {
531 *ptr++ = 1;
532 continue;
533 }
534 weightedSum += n;
535 }
536 if (y > 0 && x < m_widthTiles - 1) {
537 n = (m_videoFlags[ y * m_widthTiles + (x+1)] +
538 m_videoFlags[(y-1) * m_widthTiles + (x+1)] +
539 m_videoFlags[(y-1) * m_widthTiles + x ]);
540 if (n == 3) {
541 *ptr++ = 1;
542 continue;
543 }
544 weightedSum += n;
545 }
546 if (y < m_heightTiles - 1 && x > 0) {
547 n = (m_videoFlags[ y * m_widthTiles + (x-1)] +
548 m_videoFlags[(y+1) * m_widthTiles + (x-1)] +
549 m_videoFlags[(y+1) * m_widthTiles + x ]);
550 if (n == 3) {
551 *ptr++ = 1;
552 continue;
553 }
554 weightedSum += n;
555 }
556 if (y < m_heightTiles - 1 && x < m_widthTiles - 1) {
557 n = (m_videoFlags[ y * m_widthTiles + (x+1)] +
558 m_videoFlags[(y+1) * m_widthTiles + (x+1)] +
559 m_videoFlags[(y+1) * m_widthTiles + x ]);
560 if (n == 3) {
561 *ptr++ = 1;
562 continue;
563 }
564 weightedSum += n;
565 }
566 *ptr++ = (weightedSum <= 3) ? 0 : m_videoFlags[y * m_widthTiles + x];
567 }
568 }
569
570 /*
571 /// DEBUG: ------------------------------------------------------
572 if (nVideoTiles) {
573 for (y = 0; y < m_heightTiles; y++) {
574 for (x = 0; x < m_widthTiles; x++) {
575 printf("%c", m_videoFlags[y * m_widthTiles + x] ? '@' : ':');
576 }
577 printf(" ");
578 for (x = 0; x < m_widthTiles; x++) {
579 printf("%c", newFlags[y * m_widthTiles + x] ? '@' : ':');
580 }
581 printf("\n");
582 }
583 printf("\n");
584 }
585 /// -------------------------------------------------------------
586 */
587
588 memcpy(m_videoFlags, newFlags, m_widthTiles * m_heightTiles);
589}