/* * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. * Copyright (C) 2002 Tim Jansen. All Rights Reserved. * Copyright (C) 1999-2001 Anders Lindstr�m * * * * 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. * * tim@tjansen.de: - removed stuff for krdc * - merged with shm.c and misc.c * - added FillRectangle and Sync methods to draw only on * the image * - added Zoom functionality, based on rotation funcs from * SGE by Anders Lindstr�m) * - added support for softcursor encoding * */ /* * desktop.c - functions to deal with "desktop" window. */ #include #include #include #include #include #include #include "vncviewer.h" static XShmSegmentInfo shminfo; static Bool caughtShmError = False; static Bool needShmCleanup = False; static XShmSegmentInfo zoomshminfo; static Bool caughtZoomShmError = False; static Bool needZoomShmCleanup = False; static Bool gcInited = False; GC gc; GC srcGC, dstGC; /* used for debugging copyrect */ Dimension dpyWidth, dpyHeight; static XImage *image = NULL; Bool useShm = True; static Bool zoomActive = False; static int zoomWidth, zoomHeight; static XImage *zoomImage = NULL; static Bool useZoomShm = True; /* for softcursor */ static char *savedArea = NULL; typedef enum { SOFTCURSOR_UNDER, SOFTCURSOR_PART_UNDER, SOFTCURSOR_UNAFFECTED } SoftCursorState; typedef int Sint32; typedef short Sint16; typedef char Sint8; typedef unsigned int Uint32; typedef unsigned short Uint16; typedef unsigned char Uint8; typedef struct { int w, h; unsigned int pitch; void *pixels; int BytesPerPixel; } Surface; typedef struct { Sint16 x, y; Uint16 w, h; } Rect; static void bgr233cpy(CARD8 *dst, CARD8 *src, int len); static void CopyDataToScreenRaw(char *buf, int x, int y, int width, int height); static void CopyBGR233ToScreen(CARD8 *buf, int x, int y, int width,int height); static void FillRectangleBGR233(CARD8 buf, int x, int y, int width,int height); static int CheckRectangle(int x, int y, int width, int height); static SoftCursorState getSoftCursorState(int x, int y, int width, int height); static void discardCursorSavedArea(void); static void saveCursorSavedArea(void); static void ZoomInit(void); static void transformZoomSrc(int six, int siy, int siw, int sih, int *dix, int *diy, int *diw, int *dih, int srcW, int dstW, int srcH, int dstH); static void transformZoomDst(int *six, int *siy, int *siw, int *sih, int dix, int diy, int diw, int dih, int srcW, int dstW, int srcH, int dstH); static void ZoomSurfaceSrcCoords(int x, int y, int w, int h, int *dix, int *diy, int *diw, int *dih, Surface * src, Surface * dst); static void ZoomSurfaceCoords32(int sx, int sy, int sw, int sh, int dx, int dy, Surface * src, Surface * dst); static void sge_transform(Surface *src, Surface *dst, float xscale, float yscale, Uint16 qx, Uint16 qy); void DesktopInit(Window win) { XGCValues gcv; image = CreateShmImage(); if (!image) { useShm = False; image = XCreateImage(dpy, vis, visdepth, ZPixmap, 0, NULL, si.framebufferWidth, si.framebufferHeight, BitmapPad(dpy), 0); image->data = calloc(image->bytes_per_line * image->height, 1); if (!image->data) { fprintf(stderr,"malloc failed\n"); exit(1); } } gc = XCreateGC(dpy,win,0,NULL); gcv.function = GXxor; gcv.foreground = 0x0f0f0f0f; srcGC = XCreateGC(dpy,win,GCFunction|GCForeground,&gcv); gcv.foreground = 0xf0f0f0f0; dstGC = XCreateGC(dpy,win,GCFunction|GCForeground,&gcv); gcInited = True; } /* * DrawScreenRegionX11Thread * Never call from any other desktop.c function, only for X11 thread */ void DrawScreenRegionX11Thread(Window win, int x, int y, int width, int height) { zoomActive = False; zoomWidth = 0; zoomHeight = 0; if (!image) return; if (useShm) XShmPutImage(dpy, win, gc, image, x, y, x, y, width, height, False); else XPutImage(dpy, win, gc, image, x, y, x, y, width, height); } /* * CheckRectangle */ static int CheckRectangle(int x, int y, int width, int height) { if ((x < 0) || (y < 0)) return 0; if (((x+width) > si.framebufferWidth) || ((y+height) > si.framebufferHeight)) return 0; return 1; } static void bgr233cpy(CARD8 *dst, CARD8 *src, int len) { int i; CARD16 *d16; CARD32 *d32; switch (visbpp) { case 8: for (i = 0; i < len; i++) *(dst++) = (CARD8) BGR233ToPixel[*(src++)]; break; case 16: d16 = (CARD16*) dst; for (i = 0; i < len; i++) *(d16++) = (CARD16) BGR233ToPixel[*(src++)]; break; case 32: d32 = (CARD32*) dst; for (i = 0; i < len; i++) *(d32++) = (CARD32) BGR233ToPixel[*(src++)]; break; default: fprintf(stderr, "Unsupported softcursor depth %d\n", visbpp); } } /* * CopyDataToScreen. */ void CopyDataToScreen(char *buf, int x, int y, int width, int height) { SoftCursorState s; if (!CheckRectangle(x, y, width, height)) return; LockFramebuffer(); s = getSoftCursorState(x, y, width, height); if (s == SOFTCURSOR_PART_UNDER) undrawCursor(); if (!appData.useBGR233) CopyDataToScreenRaw(buf, x, y, width, height); else CopyBGR233ToScreen((CARD8 *)buf, x, y, width, height); if (s != SOFTCURSOR_UNAFFECTED) drawCursor(); UnlockFramebuffer(); SyncScreenRegion(x, y, width, height); } /* * CopyDataToScreenRaw. */ static void CopyDataToScreenRaw(char *buf, int x, int y, int width, int height) { int h; int widthInBytes = width * visbpp / 8; int scrWidthInBytes = image->bytes_per_line; char *scr = (image->data + y * scrWidthInBytes + x * visbpp / 8); for (h = 0; h < height; h++) { memcpy(scr, buf, widthInBytes); buf += widthInBytes; scr += scrWidthInBytes; } } /* * CopyBGR233ToScreen. */ static void CopyBGR233ToScreen(CARD8 *buf, int x, int y, int width, int height) { int p, q; int xoff = 7 - (x & 7); int xcur; int fbwb = si.framebufferWidth / 8; CARD8 *scr1 = ((CARD8 *)image->data) + y * fbwb + x / 8; CARD8 *scrt; CARD8 *scr8 = ((CARD8 *)image->data) + y * si.framebufferWidth + x; CARD16 *scr16 = ((CARD16 *)image->data) + y * si.framebufferWidth + x; CARD32 *scr32 = ((CARD32 *)image->data) + y * si.framebufferWidth + x; switch (visbpp) { /* thanks to Chris Hooper for single bpp support */ case 1: for (q = 0; q < height; q++) { xcur = xoff; scrt = scr1; for (p = 0; p < width; p++) { *scrt = ((*scrt & ~(1 << xcur)) | (BGR233ToPixel[*(buf++)] << xcur)); if (xcur-- == 0) { xcur = 7; scrt++; } } scr1 += fbwb; } break; case 8: for (q = 0; q < height; q++) { for (p = 0; p < width; p++) { *(scr8++) = BGR233ToPixel[*(buf++)]; } scr8 += si.framebufferWidth - width; } break; case 16: for (q = 0; q < height; q++) { for (p = 0; p < width; p++) { *(scr16++) = BGR233ToPixel[*(buf++)]; } scr16 += si.framebufferWidth - width; } break; case 32: for (q = 0; q < height; q++) { for (p = 0; p < width; p++) { *(scr32++) = BGR233ToPixel[*(buf++)]; } scr32 += si.framebufferWidth - width; } break; } } /* * FillRectangle8. */ void FillRectangle8(CARD8 fg, int x, int y, int width, int height) { SoftCursorState s; if (!CheckRectangle(x, y, width, height)) return; s = getSoftCursorState(x, y, width, height); if (s == SOFTCURSOR_PART_UNDER) undrawCursor(); if (!appData.useBGR233) { int h; int widthInBytes = width * visbpp / 8; int scrWidthInBytes = image->bytes_per_line; char *scr = (image->data + y * scrWidthInBytes + x * visbpp / 8); for (h = 0; h < height; h++) { memset(scr, fg, widthInBytes); scr += scrWidthInBytes; } } else { FillRectangleBGR233(fg, x, y, width, height); } if (s != SOFTCURSOR_UNAFFECTED) drawCursor(); } /* * FillRectangleBGR233. */ static void FillRectangleBGR233(CARD8 fg, int x, int y, int width, int height) { int p, q; int xoff = 7 - (x & 7); int xcur; int fbwb = si.framebufferWidth / 8; CARD8 *scr1 = ((CARD8 *)image->data) + y * fbwb + x / 8; CARD8 *scrt; CARD8 *scr8 = ((CARD8 *)image->data) + y * si.framebufferWidth + x; CARD16 *scr16 = ((CARD16 *)image->data) + y * si.framebufferWidth + x; CARD32 *scr32 = ((CARD32 *)image->data) + y * si.framebufferWidth + x; unsigned long fg233 = BGR233ToPixel[fg]; switch (visbpp) { /* thanks to Chris Hooper for single bpp support */ case 1: for (q = 0; q < height; q++) { xcur = xoff; scrt = scr1; for (p = 0; p < width; p++) { *scrt = ((*scrt & ~(1 << xcur)) | (fg233 << xcur)); if (xcur-- == 0) { xcur = 7; scrt++; } } scr1 += fbwb; } break; case 8: for (q = 0; q < height; q++) { for (p = 0; p < width; p++) { *(scr8++) = fg233; } scr8 += si.framebufferWidth - width; } break; case 16: for (q = 0; q < height; q++) { for (p = 0; p < width; p++) { *(scr16++) = fg233; } scr16 += si.framebufferWidth - width; } break; case 32: for (q = 0; q < height; q++) { for (p = 0; p < width; p++) { *(scr32++) = fg233; } scr32 += si.framebufferWidth - width; } break; } } /* * FillRectangle16 */ void FillRectangle16(CARD16 fg, int x, int y, int width, int height) { int i, h; int scrWidthInBytes = image->bytes_per_line; char *scr = (image->data + y * scrWidthInBytes + x * visbpp / 8); CARD16 *scr16; SoftCursorState s; if (!CheckRectangle(x, y, width, height)) return; s = getSoftCursorState(x, y, width, height); if (s == SOFTCURSOR_PART_UNDER) undrawCursor(); for (h = 0; h < height; h++) { scr16 = (CARD16*) scr; for (i = 0; i < width; i++) scr16[i] = fg; scr += scrWidthInBytes; } if (s != SOFTCURSOR_UNAFFECTED) drawCursor(); } /* * FillRectangle32 */ void FillRectangle32(CARD32 fg, int x, int y, int width, int height) { int i, h; int scrWidthInBytes = image->bytes_per_line; SoftCursorState s; char *scr = (image->data + y * scrWidthInBytes + x * visbpp / 8); CARD32 *scr32; if (!CheckRectangle(x, y, width, height)) return; s = getSoftCursorState(x, y, width, height); if (s == SOFTCURSOR_PART_UNDER) undrawCursor(); for (h = 0; h < height; h++) { scr32 = (CARD32*) scr; for (i = 0; i < width; i++) scr32[i] = fg; scr += scrWidthInBytes; } if (s != SOFTCURSOR_UNAFFECTED) drawCursor(); } /* * CopyDataFromScreen. */ void CopyDataFromScreen(char *buf, int x, int y, int width, int height) { int widthInBytes = width * visbpp / 8; int scrWidthInBytes = image->bytes_per_line; char *src = (image->data + y * scrWidthInBytes + x * visbpp / 8); int h; if (!CheckRectangle(x, y, width, height)) return; for (h = 0; h < height; h++) { memcpy(buf, src, widthInBytes); src += scrWidthInBytes; buf += widthInBytes; } } /* * CopyArea */ void CopyArea(int srcX, int srcY, int width, int height, int x, int y) { int widthInBytes = width * visbpp / 8; SoftCursorState sSrc, sDst; LockFramebuffer(); sSrc = getSoftCursorState(srcX, srcY, width, height); sDst = getSoftCursorState(x, y, width, height); if ((sSrc != SOFTCURSOR_UNAFFECTED) || (sDst == SOFTCURSOR_PART_UNDER)) undrawCursor(); if ((srcY+height < y) || (y+height < srcY) || (srcX+width < x) || (x+width < srcX)) { int scrWidthInBytes = image->bytes_per_line; char *src = (image->data + srcY * scrWidthInBytes + srcX * visbpp / 8); char *dst = (image->data + y * scrWidthInBytes + x * visbpp / 8); int h; if (!CheckRectangle(srcX, srcY, width, height)) { UnlockFramebuffer(); return; } if (!CheckRectangle(x, y, width, height)) { UnlockFramebuffer(); return; } for (h = 0; h < height; h++) { memcpy(dst, src, widthInBytes); src += scrWidthInBytes; dst += scrWidthInBytes; } } else { char *buf = malloc(widthInBytes*height); if (!buf) { UnlockFramebuffer(); fprintf(stderr, "Out of memory, CopyArea impossible\n"); return; } CopyDataFromScreen(buf, srcX, srcY, width, height); CopyDataToScreenRaw(buf, x, y, width, height); free(buf); } if ((sSrc != SOFTCURSOR_UNAFFECTED) || (sDst != SOFTCURSOR_UNAFFECTED)) drawCursor(); UnlockFramebuffer(); SyncScreenRegion(x, y, width, height); } void SyncScreenRegion(int x, int y, int width, int height) { int dx, dy, dw, dh; if (zoomActive) { Surface src, dest; src.w = si.framebufferWidth; src.h = si.framebufferHeight; src.pitch = image->bytes_per_line; src.pixels = image->data; src.BytesPerPixel = visbpp / 8; dest.w = zoomWidth; dest.h = zoomHeight; dest.pitch = zoomImage->bytes_per_line; dest.pixels = zoomImage->data; dest.BytesPerPixel = visbpp / 8; ZoomSurfaceSrcCoords(x, y, width, height, &dx, &dy, &dw, &dh, &src, &dest); } else { dx = x; dy = y; dw = width; dh = height; } DrawScreenRegion(dx, dy, dw, dh); } void SyncScreenRegionX11Thread(int x, int y, int width, int height) { int dx, dy, dw, dh; if (zoomActive) { Surface src, dest; src.w = si.framebufferWidth; src.h = si.framebufferHeight; src.pitch = image->bytes_per_line; src.pixels = image->data; src.BytesPerPixel = visbpp / 8; dest.w = zoomWidth; dest.h = zoomHeight; dest.pitch = zoomImage->bytes_per_line; dest.pixels = zoomImage->data; dest.BytesPerPixel = visbpp / 8; ZoomSurfaceSrcCoords(x, y, width, height, &dx, &dy, &dw, &dh, &src, &dest); } else { dx = x; dy = y; dw = width; dh = height; } DrawAnyScreenRegionX11Thread(dx, dy, dw, dh); } /* * ToplevelInitBeforeRealization sets the title, geometry and other resources * on the toplevel window. */ void ToplevelInit() { dpyWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy)); dpyHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy)); } /* * Cleanup - perform shm cleanup operations prior to exiting. */ void Cleanup() { if (useShm || useZoomShm) ShmCleanup(); } void ShmCleanup() { fprintf(stderr,"ShmCleanup called\n"); if (needShmCleanup) { shmdt(shminfo.shmaddr); shmctl(shminfo.shmid, IPC_RMID, 0); needShmCleanup = False; } if (needZoomShmCleanup) { shmdt(zoomshminfo.shmaddr); shmctl(zoomshminfo.shmid, IPC_RMID, 0); needZoomShmCleanup = False; } } static int ShmCreationXErrorHandler(Display *d, XErrorEvent *e) { caughtShmError = True; return 0; } XImage * CreateShmImage() { XImage *_image; XErrorHandler oldXErrorHandler; if (!XShmQueryExtension(dpy)) return NULL; _image = XShmCreateImage(dpy, vis, visdepth, ZPixmap, NULL, &shminfo, si.framebufferWidth, si.framebufferHeight); if (!_image) return NULL; shminfo.shmid = shmget(IPC_PRIVATE, _image->bytes_per_line * _image->height, IPC_CREAT|0777); if (shminfo.shmid == -1) { XDestroyImage(_image); return NULL; } shminfo.shmaddr = _image->data = shmat(shminfo.shmid, 0, 0); if (shminfo.shmaddr == (char *)-1) { XDestroyImage(_image); shmctl(shminfo.shmid, IPC_RMID, 0); return NULL; } shminfo.readOnly = True; oldXErrorHandler = XSetErrorHandler(ShmCreationXErrorHandler); XShmAttach(dpy, &shminfo); XSync(dpy, False); XSetErrorHandler(oldXErrorHandler); if (caughtShmError) { XDestroyImage(_image); shmdt(shminfo.shmaddr); shmctl(shminfo.shmid, IPC_RMID, 0); return NULL; } needShmCleanup = True; fprintf(stderr,"Using shared memory PutImage\n"); return _image; } void undrawCursor() { int x, y, w, h; if ((imageIndex < 0) || !savedArea) return; getBoundingRectCursor(cursorX, cursorY, imageIndex, &x, &y, &w, &h); if ((w < 1) || (h < 1)) return; CopyDataToScreenRaw(savedArea, x, y, w, h); discardCursorSavedArea(); } static void drawCursorImage() { int x, y, w, h, pw, pixelsLeft, processingMask; int skipLeft, skipRight; PointerImage *pi = &pointerImages[imageIndex]; CARD8 *img = (CARD8*) pi->image; CARD8 *imgEnd = &img[pi->len]; CARD8 *fb; /* check whether the source image has ended (image broken) */ #define CHECK_IMG(x) if (&img[x] > imgEnd) goto imgError /* check whether the end of the framebuffer has been reached (last line) */ #define CHECK_END() if ((wl == 0) && (h == 1)) return /* skip x pixels in the source (x must be < pixelsLeft!) */ #define SKIP_IMG(x) if ((x > 0) && !processingMask) { \ CHECK_END(); \ img += pw * x; \ CHECK_IMG(0); \ } /* skip x pixels in source and destination */ #define SKIP_PIXELS(x) { int wl = x; \ while (pixelsLeft <= wl) { \ wl -= pixelsLeft; \ SKIP_IMG(pixelsLeft); \ CHECK_END(); \ pixelsLeft = *(img++); \ CHECK_IMG(0); \ processingMask = processingMask ? 0 : 1; \ } \ pixelsLeft -= wl; \ SKIP_IMG(wl); \ } if (!img) return; x = cursorX - pi->hotX; y = cursorY - pi->hotY; w = pi->w; h = pi->h; if (!rectsIntersect(x, y, w, h, 0, 0, si.framebufferWidth, si.framebufferHeight)) { fprintf(stderr, "intersect abort\n"); return; } pw = myFormat.bitsPerPixel / 8; processingMask = 1; pixelsLeft = *(img++); /* at this point everything is initialized for the macros */ /* skip/clip bottom lines */ if ((y+h) > si.framebufferHeight) h = si.framebufferHeight - y; /* Skip invisible top lines */ while (y < 0) { SKIP_PIXELS(w); y++; h--; } /* calculate left/right clipping */ if (x < 0) { skipLeft = -x; w += x; x = 0; } else skipLeft = 0; if ((x+w) > si.framebufferWidth) { skipRight = (x+w) - si.framebufferWidth; w = si.framebufferWidth - x; } else skipRight = 0; fb = (CARD8*) image->data + y * image->bytes_per_line + x * visbpp / 8; /* Paint the thing */ while (h > 0) { SKIP_PIXELS(skipLeft); { CARD8 *fbx = fb; int wl = w; while (pixelsLeft <= wl) { wl -= pixelsLeft; if ((pixelsLeft > 0) && !processingMask) { int pl = pw * pixelsLeft; CHECK_IMG(pl); if (!appData.useBGR233) memcpy(fbx, img, pl); else bgr233cpy(fbx, img, pixelsLeft); img += pl; } CHECK_END(); fbx += pixelsLeft * visbpp / 8; pixelsLeft = *(img++); CHECK_IMG(0); processingMask = processingMask ? 0 : 1; } pixelsLeft -= wl; if ((wl > 0) && !processingMask) { int pl = pw * wl; CHECK_IMG(pl); if (!appData.useBGR233) memcpy(fbx, img, pl); else bgr233cpy(fbx, img, wl); img += pl; } } SKIP_PIXELS(skipRight); fb += image->bytes_per_line; h--; } return; imgError: fprintf(stderr, "Error in softcursor image %d\n", imageIndex); pointerImages[imageIndex].set = 0; } static void discardCursorSavedArea() { if (savedArea) free(savedArea); savedArea = 0; } static void saveCursorSavedArea() { int x, y, w, h; if (imageIndex < 0) return; getBoundingRectCursor(cursorX, cursorY, imageIndex, &x, &y, &w, &h); if ((w < 1) || (h < 1)) return; discardCursorSavedArea(); savedArea = malloc(h*image->bytes_per_line); if (!savedArea) { fprintf(stderr,"malloc failed, saving cursor not possible\n"); exit(1); } CopyDataFromScreen(savedArea, x, y, w, h); } void drawCursor() { saveCursorSavedArea(); drawCursorImage(); } void getBoundingRectCursor(int cx, int cy, int _imageIndex, int *x, int *y, int *w, int *h) { int nx, ny, nw, nh; if ((_imageIndex < 0) || !pointerImages[_imageIndex].set) { *x = 0; *y = 0; *w = 0; *h = 0; return; } nx = cx - pointerImages[_imageIndex].hotX; ny = cy - pointerImages[_imageIndex].hotY; nw = pointerImages[_imageIndex].w; nh = pointerImages[_imageIndex].h; if (nx < 0) { nw += nx; nx = 0; } if (ny < 0) { nh += ny; ny = 0; } if ((nx+nw) > si.framebufferWidth) nw = si.framebufferWidth - nx; if ((ny+nh) > si.framebufferHeight) nh = si.framebufferHeight - ny; if ((nw <= 0) || (nh <= 0)) { *x = 0; *y = 0; *w = 0; *h = 0; return; } *x = nx; *y = ny; *w = nw; *h = nh; } static SoftCursorState getSoftCursorState(int x, int y, int w, int h) { int cx, cy, cw, ch; if (imageIndex < 0) return SOFTCURSOR_UNAFFECTED; getBoundingRectCursor(cursorX, cursorY, imageIndex, &cx, &cy, &cw, &ch); if ((cw == 0) || (ch == 0)) return SOFTCURSOR_UNAFFECTED; if (!rectsIntersect(x, y, w, h, cx, cy, cw, ch)) return SOFTCURSOR_UNAFFECTED; if (rectContains(x, y, w, h, cx, cy, cw, ch)) return SOFTCURSOR_UNDER; else return SOFTCURSOR_PART_UNDER; } int rectsIntersect(int x, int y, int w, int h, int x2, int y2, int w2, int h2) { if (x2 >= (x+w)) return 0; if (y2 >= (y+h)) return 0; if ((x2+w2) <= x) return 0; if ((y2+h2) <= y) return 0; return 1; } int rectContains(int outX, int outY, int outW, int outH, int inX, int inY, int inW, int inH) { if (inX < outX) return 0; if (inY < outY) return 0; if ((inX+inW) > (outX+outW)) return 0; if ((inY+inH) > (outY+outH)) return 0; return 1; } void rectsJoin(int *nx1, int *ny1, int *nw1, int *nh1, int x2, int y2, int w2, int h2) { int ox, oy, ow, oh; ox = *nx1; oy = *ny1; ow = *nw1; oh = *nh1; if (x2 < ox) { ow += ox - x2; ox = x2; } if (y2 < oy) { oh += oy - y2; oy = y2; } if ((x2+w2) > (ox+ow)) ow = (x2+w2) - ox; if ((y2+h2) > (oy+oh)) oh = (y2+h2) - oy; *nx1 = ox; *ny1 = oy; *nw1 = ow; *nh1 = oh; } XImage * CreateShmZoomImage() { XImage *_image; XErrorHandler oldXErrorHandler; if (!XShmQueryExtension(dpy)) return NULL; _image = XShmCreateImage(dpy, vis, visdepth, ZPixmap, NULL, &zoomshminfo, si.framebufferWidth, si.framebufferHeight); if (!_image) return NULL; zoomshminfo.shmid = shmget(IPC_PRIVATE, _image->bytes_per_line * _image->height, IPC_CREAT|0777); if (zoomshminfo.shmid == -1) { XDestroyImage(_image); return NULL; } zoomshminfo.shmaddr = _image->data = shmat(zoomshminfo.shmid, 0, 0); if (zoomshminfo.shmaddr == (char *)-1) { XDestroyImage(_image); shmctl(zoomshminfo.shmid, IPC_RMID, 0); return NULL; } zoomshminfo.readOnly = True; oldXErrorHandler = XSetErrorHandler(ShmCreationXErrorHandler); XShmAttach(dpy, &zoomshminfo); XSync(dpy, False); XSetErrorHandler(oldXErrorHandler); if (caughtZoomShmError) { XDestroyImage(_image); shmdt(zoomshminfo.shmaddr); shmctl(zoomshminfo.shmid, IPC_RMID, 0); return NULL; } needZoomShmCleanup = True; fprintf(stderr,"Using shared memory PutImage\n"); return _image; } /* * DrawZoomedScreenRegionX11Thread * Never call from any other desktop.c function, only for X11 thread */ void DrawZoomedScreenRegionX11Thread(Window win, int zwidth, int zheight, int x, int y, int width, int height) { if (!image) return; if (zwidth > si.framebufferWidth) zwidth = si.framebufferWidth; if (zheight > si.framebufferHeight) zheight = si.framebufferHeight; if (!zoomActive) { ZoomInit(); zoomActive = True; } if ((zoomWidth != zwidth) || (zoomHeight != zheight)) { Surface src, dest; zoomWidth = zwidth; zoomHeight = zheight; src.w = si.framebufferWidth; src.h = si.framebufferHeight; src.pitch = image->bytes_per_line; src.pixels = image->data; src.BytesPerPixel = visbpp / 8; dest.w = zwidth; dest.h = zheight; dest.pitch = zoomImage->bytes_per_line; dest.pixels = zoomImage->data; dest.BytesPerPixel = visbpp / 8; sge_transform(&src, &dest, (float)dest.w/(float)src.w, (float)dest.h/(float)src.h, 0, 0); if (useZoomShm) XShmPutImage(dpy, win, gc, zoomImage, 0, 0, 0, 0, zwidth, zheight, False); else XPutImage(dpy, win, gc, zoomImage, 0, 0, 0, 0, zwidth, zheight); return; } if (useZoomShm) XShmPutImage(dpy, win, gc, zoomImage, x, y, x, y, width, height, False); else XPutImage(dpy, win, gc, zoomImage, x, y, x, y, width, height); } static void ZoomInit() { if (zoomImage) return; zoomImage = CreateShmZoomImage(); if (!zoomImage) { useZoomShm = False; zoomImage = XCreateImage(dpy, vis, visdepth, ZPixmap, 0, NULL, si.framebufferWidth, si.framebufferHeight, BitmapPad(dpy), 0); zoomImage->data = calloc(zoomImage->bytes_per_line * zoomImage->height, 1); if (!zoomImage->data) { fprintf(stderr,"malloc failed\n"); exit(1); } } } static void transformZoomSrc(int six, int siy, int siw, int sih, int *dix, int *diy, int *diw, int *dih, int srcW, int dstW, int srcH, int dstH) { double sx, sy, sw, sh; double dx, dy, dw, dh; double wq, hq; sx = six; sy = siy; sw = siw; sh = sih; wq = ((double)dstW) / (double) srcW; hq = ((double)dstH) / (double) srcH; dx = sx * wq; dy = sy * hq; dw = sw * wq; dh = sh * hq; *dix = dx; *diy = dy; *diw = dw+(dx-(int)dx)+0.5; *dih = dh+(dy-(int)dy)+0.5; } static void transformZoomDst(int *six, int *siy, int *siw, int *sih, int dix, int diy, int diw, int dih, int srcW, int dstW, int srcH, int dstH) { double sx, sy, sw, sh; double dx, dy, dw, dh; double wq, hq; dx = dix; dy = diy; dw = diw; dh = dih; wq = ((double)dstW) / (double) srcW; hq = ((double)dstH) / (double) srcH; sx = dx / wq; sy = dy / hq; sw = dw / wq; sh = dh / hq; *six = sx; *siy = sy; *siw = sw+(sx-(int)sx)+0.5; *sih = sh+(sy-(int)sy)+0.5; } static void ZoomSurfaceSrcCoords(int six, int siy, int siw, int sih, int *dix, int *diy, int *diw, int *dih, Surface * src, Surface * dst) { int dx, dy, dw, dh; int sx, sy, sw, sh; transformZoomSrc(six, siy, siw, sih, &dx, &dy, &dw, &dh, src->w, dst->w, src->h, dst->h); dx-=2; dy-=2; dw+=4; dh+=4; if (dx < 0) dx = 0; if (dy < 0) dy = 0; if (dx+dw > dst->w) dw = dst->w - dx; if (dy+dh > dst->h) dh = dst->h - dy; transformZoomDst(&sx, &sy, &sw, &sh, dx, dy, dw, dh, src->w, dst->w, src->h, dst->h); if (sx+sw > src->w) sw = src->w - sx; if (sy+sh > src->h) sh = src->h - sy; ZoomSurfaceCoords32(sx, sy, sw, sh, dx, dy, src, dst); *dix = dx; *diy = dy; *diw = dw; *dih = dh; } static void ZoomSurfaceCoords32(int sx, int sy, int sw, int sh, int dx, int dy, Surface * src, Surface * dst) { Surface s2; s2 = *src; s2.pixels = ((char*)s2.pixels) + (sx * s2.BytesPerPixel) + (sy * src->pitch); s2.w = sw; s2.h = sh; sge_transform(&s2, dst, (float)dst->w/(float)src->w, (float)dst->h/(float)src->h, dx, dy); } #define sge_clip_xmin(pnt) 0 #define sge_clip_xmax(pnt) pnt->w #define sge_clip_ymin(pnt) 0 #define sge_clip_ymax(pnt) pnt->h /*================================================================================== // Helper function to sge_transform() // Returns the bounding box //================================================================================== */ static void _calcRect(Surface *src, Surface *dst, float xscale, float yscale, Uint16 qx, Uint16 qy, Sint16 *xmin, Sint16 *ymin, Sint16 *xmax, Sint16 *ymax) { Sint16 x, y, rx, ry; int i; /* Clip to src surface */ Sint16 sxmin = sge_clip_xmin(src); Sint16 sxmax = sge_clip_xmax(src); Sint16 symin = sge_clip_ymin(src); Sint16 symax = sge_clip_ymax(src); Sint16 sx[5]; Sint16 sy[4]; /* We don't really need fixed-point here * but why not? */ Sint32 ictx = (Sint32) (xscale * 8192.0); Sint32 icty = (Sint32) (yscale * 8192.0); sx[0] = sxmin; sx[1] = sxmax; sx[2] = sxmin; sx[3] = sxmax; sy[0] = symin; sy[1] = symax; sy[2] = symax; sy[3] = symin; /* Calculate the four corner points */ for(i=0; i<4; i++){ rx = sx[i]; ry = sy[i]; x = (Sint16)(((ictx*rx) >> 13) + qx); y = (Sint16)(((icty*ry) >> 13) + qy); if(i==0){ *xmax = *xmin = x; *ymax = *ymin = y; }else{ if(x>*xmax) *xmax=x; else if(x<*xmin) *xmin=x; if(y>*ymax) *ymax=y; else if(y<*ymin) *ymin=y; } } /* Better safe than sorry...*/ *xmin -= 1; *ymin -= 1; *xmax += 1; *ymax += 1; /* Clip to dst surface */ if( !dst ) return; if( *xmin < sge_clip_xmin(dst) ) *xmin = sge_clip_xmin(dst); if( *xmax > sge_clip_xmax(dst) ) *xmax = sge_clip_xmax(dst); if( *ymin < sge_clip_ymin(dst) ) *ymin = sge_clip_ymin(dst); if( *ymax > sge_clip_ymax(dst) ) *ymax = sge_clip_ymax(dst); } /*================================================================================== ** Scale by scale and place at position (qx,qy). ** ** ** Developed with the help from Terry Hancock (hancock@earthlink.net) ** **==================================================================================*/ /* First we need some macros to handle different bpp * I'm sorry about this... */ #define TRANSFORM(UintXX, DIV) \ Sint32 src_pitch=src->pitch/DIV; \ Sint32 dst_pitch=dst->pitch/DIV; \ UintXX *src_row = (UintXX *)src->pixels; \ UintXX *dst_row; \ \ for (y=ymin; ypixels + y*dst_pitch; \ \ for (x=xmin; x> 13); /* Convert from fixed-point */ \ ry=(Sint16)(sy >> 13); \ \ /* Make sure the source pixel is actually in the source image. */ \ if( (rx>=sxmin) && (rx=symin) && (rypitch/DIV; \ Sint32 dst_pitch=dst->pitch/DIV; \ UintXX *src_row = (UintXX *)src->pixels; \ UintXX *dst_row; \ UintXX c1, c2, c3, c4;\ Uint32 R, G, B, A=0; \ UintXX Rmask = image->red_mask;\ UintXX Gmask = image->green_mask;\ UintXX Bmask = image->blue_mask;\ UintXX Amask = 0;\ Uint32 wx, wy;\ Uint32 p1, p2, p3, p4;\ \ /* * Interpolation: * We calculate the distances from our point to the four nearest pixels, d1..d4. * d(a,b) = sqrt(a�+b�) ~= 0.707(a+b) (Pythagoras (Taylor) expanded around (0.5;0.5)) * * 1 wx 2 * *-|-* (+ = our point at (x,y)) * | | | (* = the four nearest pixels) * wy --+ | wx = float(x) - int(x) * | | wy = float(y) - int(y) * *---* * 3 4 * d1 = d(wx,wy) d2 = d(1-wx,wy) d3 = d(wx,1-wy) d4 = d(1-wx,1-wy) * We now want to weight each pixels importance - it's vicinity to our point: * w1=d4 w2=d3 w3=d2 w4=d1 (Yes it works... just think a bit about it) * * If the pixels have the colors c1..c4 then our point should have the color * c = (w1*c1 + w2*c2 + w3*c3 + w4*c4)/(w1+w2+w3+w4) (the weighted average) * but w1+w2+w3+w4 = 4*0.707 so we might as well write it as * c = p1*c1 + p2*c2 + p3*c3 + p4*c4 where p1..p4 = (w1..w4)/(4*0.707) * * But p1..p4 are fixed point so we can just divide the fixed point constant! * 8192/(4*0.71) = 2897 and we can skip 0.71 too (the division will cancel it everywhere) * 8192/4 = 2048 * * 020102: I changed the fixed-point representation for the variables in the weighted average * to 24.7 to avoid problems with 32bit colors. Everything else is still 18.13. This * does however not solve the problem with 32bit RGBA colors... */\ \ Sint32 one = 2048>>6; /* 1 in Fixed-point */ \ Sint32 two = 2*2048>>6; /* 2 in Fixed-point */ \ \ for (y=ymin; ypixels + y*dst_pitch; \ \ for (x=xmin; x> 13); /* Convert from fixed-point */ \ ry=(Sint16)(sy >> 13); \ \ /* Make sure the source pixel is actually in the source image. */ \ if( (rx>=sxmin) && (rx+1=symin) && (ry+1>8; /* (float(x) - int(x)) / 4 */ \ wy = (sy & 0x00001FFF) >>8;\ \ p4 = wx+wy;\ p3 = one-wx+wy;\ p2 = wx+one-wy;\ p1 = two-wx-wy;\ \ c1 = *(src_row + ry*src_pitch + rx);\ c2 = *(src_row + ry*src_pitch + rx+1);\ c3 = *(src_row + (ry+1)*src_pitch + rx);\ c4 = *(src_row + (ry+1)*src_pitch + rx+1);\ \ /* Calculate the average */\ R = ((p1*(c1 & Rmask) + p2*(c2 & Rmask) + p3*(c3 & Rmask) + p4*(c4 & Rmask))>>7) & Rmask;\ G = ((p1*(c1 & Gmask) + p2*(c2 & Gmask) + p3*(c3 & Gmask) + p4*(c4 & Gmask))>>7) & Gmask;\ B = ((p1*(c1 & Bmask) + p2*(c2 & Bmask) + p3*(c3 & Bmask) + p4*(c4 & Bmask))>>7) & Bmask;\ if(Amask)\ A = ((p1*(c1 & Amask) + p2*(c2 & Amask) + p3*(c3 & Amask) + p4*(c4 & Amask))>>7) & Amask;\ \ *(dst_row + x) = R | G | B | A;\ } \ sx += ctx; /* Incremental transformations */ \ } \ } void sge_transform(Surface *src, Surface *dst, float xscale, float yscale, Uint16 qx, Uint16 qy) { Sint32 dy, sx, sy; Sint16 x, y, rx, ry; Rect r; Sint32 ctx, cty; Sint16 xmin, xmax, ymin, ymax; Sint16 sxmin, sxmax, symin, symax; Sint32 dx, ctdx; /* Here we use 18.13 fixed point integer math // Sint32 should have 31 usable bits and one for sign // 2^13 = 8192 */ /* Check scales */ Sint32 maxint = (Sint32)(pow(2, sizeof(Sint32)*8 - 1 - 13)); /* 2^(31-13) */ r.x = r.y = r.w = r.h = 0; if( xscale == 0 || yscale == 0) return; if( 8192.0/xscale > maxint ) xscale = (float)(8192.0/maxint); else if( 8192.0/xscale < -maxint ) xscale = (float)(-8192.0/maxint); if( 8192.0/yscale > maxint ) yscale = (float)(8192.0/maxint); else if( 8192.0/yscale < -maxint ) yscale = (float)(-8192.0/maxint); /* Fixed-point equivalents */ ctx = (Sint32)(8192.0/xscale); cty = (Sint32)(8192.0/yscale); /* Compute a bounding rectangle */ xmin=0; xmax=dst->w; ymin=0; ymax=dst->h; _calcRect(src, dst, xscale, yscale, qx, qy, &xmin, &ymin, &xmax, &ymax); /* Clip to src surface */ sxmin = sge_clip_xmin(src); sxmax = sge_clip_xmax(src); symin = sge_clip_ymin(src); symax = sge_clip_ymax(src); /* Some terms in the transform are constant */ dx = xmin - qx; ctdx = ctx*dx; /* Use the correct bpp */ if( src->BytesPerPixel == dst->BytesPerPixel){ switch( src->BytesPerPixel ){ case 1: { /* Assuming 8-bpp */ TRANSFORM(Uint8, 1) } break; case 2: { /* Probably 15-bpp or 16-bpp */ TRANSFORM_AA(Uint16, 2) } break; case 4: { /* Probably 32-bpp */ TRANSFORM_AA(Uint32, 4) } break; } } } void freeDesktopResources() { Cleanup(); if (image) { XDestroyImage(image); } if (zoomImage) { XDestroyImage(zoomImage); } if (savedArea) free(savedArea); if (gcInited) { XFreeGC(dpy, gc); XFreeGC(dpy, srcGC); XFreeGC(dpy, dstGC); } caughtShmError = False; needShmCleanup = False; caughtZoomShmError = False; needZoomShmCleanup = False; gcInited = False; image = NULL; useShm = True; zoomActive = False; zoomImage = NULL; useZoomShm = True; savedArea = NULL; } /* * ColorRectangle32 * Only used for debugging / visualizing output */ /* static void ColorRectangle32(XImage *img, CARD32 fg, int x, int y, int width, int height) { int i, h; int scrWidthInBytes = img->bytes_per_line; char *scr; CARD32 *scr32; if ((!img) || (!img->data)) return; scr = (img->data + y * scrWidthInBytes + x * 4); if (!CheckRectangle(x, y, width, height)) return; for (h = 0; h < height; h++) { scr32 = (CARD32*) scr; for (i = 0; i < width; i++) { CARD32 n = 0; CARD32 p = scr32[i]; if (0xff & fg) n |= ((( 0xff & p)+( 0xff & fg)) >> 2) & 0xff; else n |= (0xff & p); if (0xff00 & fg) n |= ((( 0xff00 & p)+( 0xff00 & fg)) >> 2) & 0xff00; else n |= (0xff00 & p); if (0xff0000 & fg) n |= (((0xff0000 & p)+(0xff0000 & fg)) >> 2) & 0xff0000; else n |= (0xff0000 & p); scr32[i] = n; } scr += scrWidthInBytes; } } */