/* * Copyright (C) 2000 heXoNet Support GmbH, D-66424 Homburg. * All Rights Reserved. * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this software; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ /* * December 15th 2001: removed coments, mouse pointer options and some * other stuff * January 10th 2002: improved hint creation (join adjacent hints) * February 20th: use only partial tiles * January 21st 2003: remember last modified scanlines, and scan them and * in every cycle, reduce scanlines to every 35th * January 21st 2003: scan lines around the cursor in every cycle * * Tim Jansen */ #include #include #include #include #include #include #include #include #include #include #include "xupdatescanner.h" /* ../../krfb/libvncserver/rfb.h */ #ifdef Bool #undef Bool #endif #define Bool int #define SCANLINES 35 unsigned int scanlines[SCANLINES] = { 0, 16, 8, 24, 33, 4, 20, 12, 28, 10, 26, 18, 34, 2, 22, 6, 30, 14, 1, 17, 32, 9, 25, 7, 23, 15, 31, 19, 3, 27, 11, 29, 13, 5, 21 }; #define MAX_ADJ_TOLERANCE 8 #define MAX_RECENT_HITS 12 unsigned int recentHitScanlines[MAX_RECENT_HITS]; #define CURSOR_SCANLINES 5 int cursorScanlines[CURSOR_SCANLINES] = { -10, -4, 0, 4, 10 }; XUpdateScanner::XUpdateScanner(Display *_dpy, Window _window, unsigned char *_fb, int _width, int _height, int _bitsPerPixel, int _bytesPerLine, bool useXShm) : dpy(_dpy), window(_window), fb(_fb), width(_width), height(_height), bitsPerPixel(_bitsPerPixel), bytesPerLine(_bytesPerLine), tileWidth(32), tileHeight(32), count (0), scanline(NULL), tile(NULL) { useShm = useXShm && XShmQueryExtension(dpy); if (useShm) { int major, minor; Bool pixmaps; if ((!XShmQueryVersion(dpy, &major, &minor, &pixmaps)) || !pixmaps) useShm = false; } if (useShm) { tile = XShmCreateImage(dpy, DefaultVisual( dpy, 0 ), bitsPerPixel, ZPixmap, NULL, &shminfo_tile, tileWidth, tileHeight); shminfo_tile.shmid = shmget(IPC_PRIVATE, tile->bytes_per_line * tile->height, IPC_CREAT | 0777); shminfo_tile.shmaddr = tile->data = (char *) shmat(shminfo_tile.shmid, 0, 0); shminfo_tile.readOnly = False; XShmAttach(dpy, &shminfo_tile); } else { int tlen = tileWidth*(bitsPerPixel/8); void *data = malloc(tlen*tileHeight); tile = XCreateImage(dpy, DefaultVisual(dpy, 0), bitsPerPixel, ZPixmap, 0, (char*)data, tileWidth, tileHeight, 8, tlen); } tilesX = (width + tileWidth - 1) / tileWidth; tilesY = (height + tileHeight - 1) / tileHeight; tileMap = new bool[tilesX * tilesY]; tileRegionMap = new struct TileChangeRegion[tilesX * tilesY]; unsigned int i; for (i = 0; i < tilesX * tilesY; i++) tileMap[i] = false; if (useShm) { scanline = XShmCreateImage(dpy, DefaultVisual(dpy, 0), bitsPerPixel, ZPixmap, NULL, &shminfo_scanline, width, 1); shminfo_scanline.shmid = shmget(IPC_PRIVATE, scanline->bytes_per_line, IPC_CREAT | 0777); shminfo_scanline.shmaddr = scanline->data = (char *) shmat( shminfo_scanline.shmid, 0, 0 ); shminfo_scanline.readOnly = False; XShmAttach(dpy, &shminfo_scanline); } else { int slen = width*(bitsPerPixel/8); void *data = malloc(slen); scanline = XCreateImage(dpy, DefaultVisual(dpy, 0), bitsPerPixel, ZPixmap, 0, (char*)data, width, 1, 8, slen); } for (int i = 0; i < MAX_RECENT_HITS; i++) recentHitScanlines[i] = i; } XUpdateScanner::~XUpdateScanner() { if (useShm) { XShmDetach(dpy, &shminfo_scanline); XDestroyImage(scanline); shmdt(shminfo_scanline.shmaddr); shmctl(shminfo_scanline.shmid, IPC_RMID, 0); XShmDetach(dpy, &shminfo_tile); XDestroyImage(tile); shmdt(shminfo_tile.shmaddr); shmctl(shminfo_tile.shmid, IPC_RMID, 0); } else { XDestroyImage(scanline); XDestroyImage(tile); } delete [] tileMap; delete [] tileRegionMap; } // returns true if last line changed. this is used to re-scan the tile under // this one because it is likely to be modified but missed by the probe bool XUpdateScanner::copyTile(int x, int y, int tx, int ty) { unsigned int maxWidth = width - x; unsigned int maxHeight = height - y; if (maxWidth > tileWidth) maxWidth = tileWidth; if (maxHeight > tileHeight) maxHeight = tileHeight; if (useShm) { if ((maxWidth == tileWidth) && (maxHeight == tileHeight)) { XShmGetImage(dpy, window, tile, x, y, AllPlanes); } else { XGetSubImage(dpy, window, x, y, maxWidth, maxHeight, AllPlanes, ZPixmap, tile, 0, 0); } } else XGetSubImage(dpy, window, x, y, maxWidth, maxHeight, AllPlanes, ZPixmap, tile, 0, 0); unsigned int line; int pixelsize = bitsPerPixel >> 3; unsigned char *src = (unsigned char*) tile->data; unsigned char *dest = fb + y * bytesPerLine + x * pixelsize; unsigned char *ssrc = src; unsigned char *sdest = dest; int firstLine = maxHeight; for (line = 0; line < maxHeight; line++) { if (memcmp(sdest, ssrc, maxWidth * pixelsize)) { firstLine = line; break; } ssrc += tile->bytes_per_line; sdest += bytesPerLine; } if (firstLine == maxHeight) { tileMap[tx + ty * tilesX] = false; return false; } unsigned char *msrc = src + (tile->bytes_per_line * maxHeight); unsigned char *mdest = dest + (bytesPerLine * maxHeight); int lastLine = firstLine; for (line = maxHeight-1; line > firstLine; line--) { msrc -= tile->bytes_per_line; mdest -= bytesPerLine; if (memcmp(mdest, msrc, maxWidth * pixelsize)) { lastLine = line; break; } } for (line = firstLine; line <= lastLine; line++) { memcpy(sdest, ssrc, maxWidth * pixelsize ); ssrc += tile->bytes_per_line; sdest += bytesPerLine; } struct TileChangeRegion *r = &tileRegionMap[tx + (ty * tilesX)]; r->firstLine = firstLine; r->lastLine = lastLine; return lastLine == (maxHeight-1); } void XUpdateScanner::copyAllTiles() { for (unsigned int y = 0; y < tilesY; y++) { for (unsigned int x = 0; x < tilesX; x++) { if (tileMap[x + y * tilesX]) if (copyTile(x*tileWidth, y*tileHeight, x, y) && ((y+1) < tilesY)) tileMap[x + (y+1) * tilesX] = true; } } } void XUpdateScanner::createHintFromTile(int x, int y, int th, Hint &hint) { unsigned int w = width - x; unsigned int h = height - y; if (w > tileWidth) w = tileWidth; if (h > th) h = th; hint.x = x; hint.y = y; hint.w = w; hint.h = h; } void XUpdateScanner::addTileToHint(int x, int y, int th, Hint &hint) { unsigned int w = width - x; unsigned int h = height - y; if (w > tileWidth) w = tileWidth; if (h > th) h = th; if (hint.x > x) { hint.w += hint.x - x; hint.x = x; } if (hint.y > y) { hint.h += hint.y - y; hint.y = y; } if ((hint.x+hint.w) < (x+w)) { hint.w = (x+w) - hint.x; } if ((hint.y+hint.h) < (y+h)) { hint.h = (y+h) - hint.y; } } static void printStatistics(Hint &hint) { static int snum = 0; static float ssum = 0.0; int oX0 = hint.x & 0xffffffe0; int oY0 = hint.y & 0xffffffe0; int oX2 = (hint.x+hint.w) & 0x1f; int oY2 = (hint.y+hint.h) & 0x1f; int oX3 = (((hint.x+hint.w) | 0x1f) + ((oX2 == 0) ? 0 : 1)) & 0xffffffe0; int oY3 = (((hint.y+hint.h) | 0x1f) + ((oY2 == 0) ? 0 : 1)) & 0xffffffe0; float s0 = hint.w*hint.h; float s1 = (oX3-oX0)*(oY3-oY0); float p = (100*s0/s1); ssum += p; snum++; float avg = ssum / snum; kdDebug() << "avg size: "<< avg <<"%"< &hintList) { if (x0 < 0) return; x0 = -1; assert (hint.w > 0); assert (hint.h > 0); //printStatistics(hint); hintList.append(new Hint(hint)); } void XUpdateScanner::createHints(TQPtrList &hintList) { Hint hint; int x0 = -1; for (int y = 0; y < tilesY; y++) { int x; for (x = 0; x < tilesX; x++) { int idx = x + y * tilesX; if (tileMap[idx]) { int ty = tileRegionMap[idx].firstLine; int th = tileRegionMap[idx].lastLine - ty +1; if (x0 < 0) { createHintFromTile(x * tileWidth, (y * tileHeight) + ty, th, hint); x0 = x; } else { addTileToHint(x * tileWidth, (y * tileHeight) + ty, th, hint); } } else flushHint(x, y, x0, hint, hintList); } flushHint(x, y, x0, hint, hintList); } } void XUpdateScanner::testScanline(int y, bool rememberHits) { if (y < 0) return; if (y >= (int)height) return; int x = 0; bool hit = false; if (useShm) XShmGetImage(dpy, window, scanline, 0, y, AllPlanes); else XGetSubImage(dpy, window, 0, y, width, 1, AllPlanes, ZPixmap, scanline, 0, 0); while (x < width) { int pixelsize = bitsPerPixel >> 3; unsigned char *src = (unsigned char*) scanline->data + x * pixelsize; unsigned char *dest = fb + y * bytesPerLine + x * pixelsize; int w = (x + 32) > width ? (width-x) : 32; if (memcmp(dest, src, w * pixelsize)) { hit = true; tileMap[(x / tileWidth) + (y / tileHeight) * tilesX] = true; } x += 32; } if (!rememberHits) return; for (int i = 1; i < MAX_RECENT_HITS; i++) recentHitScanlines[i-1] = recentHitScanlines[i]; recentHitScanlines[MAX_RECENT_HITS-1] = y; } void XUpdateScanner::searchUpdates(TQPtrList &hintList, int ptrY) { count++; count %= SCANLINES; unsigned int i; unsigned int y; for (i = 0; i < (tilesX * tilesY); i++) { tileMap[i] = false; } // test last scanlines with hits for (i = 0; i < MAX_RECENT_HITS; i++) testScanline(recentHitScanlines[i], true); // test scanlines around the cursor for (i = 0; i < CURSOR_SCANLINES; i++) testScanline(ptrY+cursorScanlines[i], false); // test last/first line of the tiles around the cursor // (assumes tileHeight = 32) testScanline((ptrY&0xffe0)-1, false); testScanline((ptrY|0x1f)+1, false); // test every SCANLINESth scanline y = scanlines[count]; while (y < (int)height) { testScanline(y, true); y += SCANLINES; } copyAllTiles(); createHints(hintList); }