summaryrefslogtreecommitdiffstats
path: root/unix/tx/TXImage.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'unix/tx/TXImage.cxx')
-rw-r--r--unix/tx/TXImage.cxx353
1 files changed, 353 insertions, 0 deletions
diff --git a/unix/tx/TXImage.cxx b/unix/tx/TXImage.cxx
new file mode 100644
index 00000000..5fa68288
--- /dev/null
+++ b/unix/tx/TXImage.cxx
@@ -0,0 +1,353 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd. 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+//
+// TXImage.cxx
+//
+
+
+#include <stdio.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <list>
+#include <rfb/TransImageGetter.h>
+#include <rfb/Exception.h>
+#include <rfb/LogWriter.h>
+#include "TXWindow.h"
+#include "TXImage.h"
+
+using namespace rfb;
+
+static rfb::LogWriter vlog("TXImage");
+
+TXImage::TXImage(Display* d, int width, int height, Visual* vis_, int depth_)
+ : xim(0), dpy(d), vis(vis_), depth(depth_), tig(0), cube(0)
+{
+#ifdef HAVE_MITSHM
+ shminfo = 0;
+#endif
+ width_ = width;
+ height_ = height;
+ for (int i = 0; i < 256; i++)
+ colourMap[i].r = colourMap[i].g = colourMap[i].b = 0;
+
+ if (!vis)
+ vis = DefaultVisual(dpy,DefaultScreen(dpy));
+ if (!depth)
+ depth = DefaultDepth(dpy,DefaultScreen(dpy));
+
+ createXImage();
+ getNativePixelFormat(vis, depth);
+ colourmap = this;
+ format.bpp = 0; // just make it different to any valid format, so that...
+ setPF(nativePF); // ...setPF() always works
+}
+
+TXImage::~TXImage()
+{
+ if (data != (rdr::U8*)xim->data) delete [] data;
+ destroyXImage();
+ delete tig;
+ delete cube;
+}
+
+void TXImage::resize(int w, int h)
+{
+ if (w == width() && h == height()) return;
+
+ int oldStrideBytes = getStride() * (format.bpp/8);
+ int rowsToCopy = __rfbmin(h, height());
+ int bytesPerRow = __rfbmin(w, width()) * (format.bpp/8);
+ rdr::U8* oldData = 0;
+ bool allocData = false;
+
+ if (data != (rdr::U8*)xim->data) {
+ oldData = (rdr::U8*)data;
+ allocData = true;
+ } else {
+ oldData = new rdr::U8[xim->bytes_per_line * height()];
+ memcpy(oldData, xim->data, xim->bytes_per_line * height());
+ }
+
+ destroyXImage();
+ width_ = w;
+ height_ = h;
+ createXImage();
+
+ if (allocData)
+ data = new rdr::U8[width() * height() * (format.bpp/8)];
+ else
+ data = (rdr::U8*)xim->data;
+
+ int newStrideBytes = getStride() * (format.bpp/8);
+ for (int i = 0; i < rowsToCopy; i++)
+ memcpy((rdr::U8*)data + newStrideBytes * i, oldData + oldStrideBytes * i,
+ bytesPerRow);
+ delete [] oldData;
+}
+
+void TXImage::setPF(const PixelFormat& newPF)
+{
+ if (newPF.equal(format)) return;
+ format = newPF;
+
+ if (data != (rdr::U8*)xim->data) delete [] data;
+ delete tig;
+ tig = 0;
+
+ if (format.equal(nativePF) && format.trueColour) {
+ data = (rdr::U8*)xim->data;
+ } else {
+ data = new rdr::U8[width() * height() * (format.bpp/8)];
+ tig = new TransImageGetter();
+ tig->init(this, nativePF, 0, cube);
+ }
+}
+
+int TXImage::getStride() const
+{
+ if (data == (rdr::U8*)xim->data)
+ return xim->bytes_per_line / (xim->bits_per_pixel / 8);
+ else
+ return width();
+}
+
+void TXImage::put(Window win, GC gc, const rfb::Rect& r)
+{
+ if (r.is_empty()) return;
+ int x = r.tl.x;
+ int y = r.tl.y;
+ int w = r.width();
+ int h = r.height();
+ if (data != (rdr::U8*)xim->data) {
+ rdr::U8* ximDataStart = ((rdr::U8*)xim->data + y * xim->bytes_per_line
+ + x * (xim->bits_per_pixel / 8));
+ tig->getImage(ximDataStart, r,
+ xim->bytes_per_line / (xim->bits_per_pixel / 8));
+ }
+#ifdef HAVE_MITSHM
+ if (usingShm()) {
+ XShmPutImage(dpy, win, gc, xim, x, y, x, y, w, h, False);
+ return;
+ }
+#endif
+ XPutImage(dpy, win, gc, xim, x, y, x, y, w, h);
+}
+
+void TXImage::setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs)
+{
+ for (int i = 0; i < nColours; i++) {
+ colourMap[firstColour+i].r = rgbs[i*3];
+ colourMap[firstColour+i].g = rgbs[i*3+1];
+ colourMap[firstColour+i].b = rgbs[i*3+2];
+ }
+}
+
+void TXImage::updateColourMap()
+{
+ tig->setColourMapEntries(0, 0, 0);
+}
+
+void TXImage::lookup(int index, int* r, int* g, int* b)
+{
+ *r = colourMap[index].r;
+ *g = colourMap[index].g;
+ *b = colourMap[index].b;
+}
+
+#ifdef HAVE_MITSHM
+static bool caughtError = false;
+
+static int XShmAttachErrorHandler(Display *dpy, XErrorEvent *error)
+{
+ caughtError = true;
+ return 0;
+}
+
+class TXImageCleanup {
+public:
+ std::list<TXImage*> images;
+ ~TXImageCleanup() {
+ while (!images.empty())
+ delete images.front();
+ }
+};
+
+static TXImageCleanup imageCleanup;
+#endif
+
+void TXImage::createXImage()
+{
+#ifdef HAVE_MITSHM
+ int major, minor;
+ Bool pixmaps;
+
+ if (XShmQueryVersion(dpy, &major, &minor, &pixmaps)) {
+ shminfo = new XShmSegmentInfo;
+
+ xim = XShmCreateImage(dpy, vis, depth, ZPixmap,
+ 0, shminfo, width(), height());
+
+ if (xim) {
+ shminfo->shmid = shmget(IPC_PRIVATE,
+ xim->bytes_per_line * xim->height,
+ IPC_CREAT|0777);
+
+ if (shminfo->shmid != -1) {
+ shminfo->shmaddr = xim->data = (char*)shmat(shminfo->shmid, 0, 0);
+
+ if (shminfo->shmaddr != (char *)-1) {
+
+ shminfo->readOnly = False;
+
+ XErrorHandler oldHdlr = XSetErrorHandler(XShmAttachErrorHandler);
+ XShmAttach(dpy, shminfo);
+ XSync(dpy, False);
+ XSetErrorHandler(oldHdlr);
+
+ if (!caughtError) {
+ vlog.debug("Using shared memory XImage");
+ imageCleanup.images.push_back(this);
+ return;
+ }
+
+ shmdt(shminfo->shmaddr);
+ } else {
+ vlog.error("shmat failed");
+ perror("shmat");
+ }
+
+ shmctl(shminfo->shmid, IPC_RMID, 0);
+ } else {
+ vlog.error("shmget failed");
+ perror("shmget");
+ }
+
+ XDestroyImage(xim);
+ xim = 0;
+ } else {
+ vlog.error("XShmCreateImage failed");
+ }
+
+ delete shminfo;
+ shminfo = 0;
+ }
+#endif
+
+ xim = XCreateImage(dpy, vis, depth, ZPixmap,
+ 0, 0, width(), height(), BitmapPad(dpy), 0);
+
+ xim->data = (char*)malloc(xim->bytes_per_line * xim->height);
+ if (!xim->data) {
+ vlog.error("malloc failed");
+ exit(1);
+ }
+}
+
+void TXImage::destroyXImage()
+{
+#ifdef HAVE_MITSHM
+ if (shminfo) {
+ vlog.debug("Freeing shared memory XImage");
+ shmdt(shminfo->shmaddr);
+ shmctl(shminfo->shmid, IPC_RMID, 0);
+ delete shminfo;
+ shminfo = 0;
+ imageCleanup.images.remove(this);
+ }
+#endif
+ // XDestroyImage() will free(xim->data) if appropriate
+ if (xim) XDestroyImage(xim);
+ xim = 0;
+}
+
+
+static bool supportedBPP(int bpp) {
+ return (bpp == 8 || bpp == 16 || bpp == 32);
+}
+
+static int depth2bpp(Display* dpy, int depth)
+{
+ int nformats;
+ XPixmapFormatValues* format = XListPixmapFormats(dpy, &nformats);
+
+ int i;
+ for (i = 0; i < nformats; i++)
+ if (format[i].depth == depth) break;
+
+ if (i == nformats || !supportedBPP(format[i].bits_per_pixel))
+ throw rfb::Exception("Error: couldn't find suitable pixmap format");
+
+ int bpp = format[i].bits_per_pixel;
+ XFree(format);
+ return bpp;
+}
+
+void TXImage::getNativePixelFormat(Visual* vis, int depth)
+{
+ cube = 0;
+ nativePF.depth = depth;
+ nativePF.bpp = depth2bpp(dpy, depth);
+ nativePF.bigEndian = (ImageByteOrder(dpy) == MSBFirst);
+ nativePF.trueColour = (vis->c_class == TrueColor);
+
+ vlog.info("Using default colormap and visual, %sdepth %d.",
+ (vis->c_class == TrueColor) ? "TrueColor, " :
+ ((vis->c_class == PseudoColor) ? "PseudoColor, " : ""),
+ depth);
+
+ if (nativePF.trueColour) {
+
+ nativePF.redShift = ffs(vis->red_mask) - 1;
+ nativePF.greenShift = ffs(vis->green_mask) - 1;
+ nativePF.blueShift = ffs(vis->blue_mask) - 1;
+ nativePF.redMax = vis->red_mask >> nativePF.redShift;
+ nativePF.greenMax = vis->green_mask >> nativePF.greenShift;
+ nativePF.blueMax = vis->blue_mask >> nativePF.blueShift;
+
+ } else {
+
+ XColor xc[256];
+ cube = new rfb::ColourCube(6,6,6);
+ int r;
+ for (r = 0; r < cube->nRed; r++) {
+ for (int g = 0; g < cube->nGreen; g++) {
+ for (int b = 0; b < cube->nBlue; b++) {
+ int i = (r * cube->nGreen + g) * cube->nBlue + b;
+ xc[i].red = r * 65535 / (cube->nRed-1);
+ xc[i].green = g * 65535 / (cube->nGreen-1);
+ xc[i].blue = b * 65535 / (cube->nBlue-1);
+ }
+ }
+ }
+
+ TXWindow::getColours(dpy, xc, cube->size());
+
+ for (r = 0; r < cube->nRed; r++) {
+ for (int g = 0; g < cube->nGreen; g++) {
+ for (int b = 0; b < cube->nBlue; b++) {
+ int i = (r * cube->nGreen + g) * cube->nBlue + b;
+ cube->set(r, g, b, xc[i].pixel);
+ }
+ }
+ }
+ }
+}