aboutsummaryrefslogtreecommitdiff
path: root/nx-X11/programs/Xserver/hw/nxagent/Image.c
diff options
context:
space:
mode:
Diffstat (limited to 'nx-X11/programs/Xserver/hw/nxagent/Image.c')
-rw-r--r--nx-X11/programs/Xserver/hw/nxagent/Image.c1821
1 files changed, 1821 insertions, 0 deletions
diff --git a/nx-X11/programs/Xserver/hw/nxagent/Image.c b/nx-X11/programs/Xserver/hw/nxagent/Image.c
new file mode 100644
index 000000000..e499b7a11
--- /dev/null
+++ b/nx-X11/programs/Xserver/hw/nxagent/Image.c
@@ -0,0 +1,1821 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */
+/* */
+/* NXAGENT, NX protocol compression and NX extensions to this software */
+/* are copyright of NoMachine. Redistribution and use of the present */
+/* software is allowed according to terms specified in the file LICENSE */
+/* which comes in the source distribution. */
+/* */
+/* Check http://www.nomachine.com/licensing.html for applicability. */
+/* */
+/* NX and NoMachine are trademarks of Medialogic S.p.A. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#include "scrnintstr.h"
+#include "resource.h"
+#include "dixstruct.h"
+#include "../../fb/fb.h"
+
+#include "Agent.h"
+#include "Composite.h"
+#include "Display.h"
+#include "Visual.h"
+#include "Drawable.h"
+#include "Pixmaps.h"
+#include "GCs.h"
+#include "Image.h"
+#include "Events.h"
+#include "Client.h"
+#include "Trap.h"
+#include "Split.h"
+#include "Args.h"
+#include "Screen.h"
+#include "Pixels.h"
+#include "Utils.h"
+
+#include "NXlib.h"
+#include "NXpack.h"
+
+/*
+ * Set here the required log level.
+ */
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+/*
+ * Don't pack the images having a width, a
+ * height or a data size smaller or equal
+ * to these thresholds.
+ */
+
+#define IMAGE_PACK_WIDTH 2
+#define IMAGE_PACK_HEIGHT 2
+#define IMAGE_PACK_LENGTH 512
+
+/*
+ * Compress the image with a lossless encoder
+ * if the percentage of discrete pixels in the
+ * image is below this threshold.
+ */
+
+#define IMAGE_UNIQUE_RATIO 10
+
+/*
+ * Introduce a small delay after each image
+ * operation if the session is down. Value
+ * is in microseconds and is multiplied by
+ * the image data size in kilobytes.
+ */
+
+#define IMAGE_DELAY_IF_DOWN 250
+
+/*
+ * Preferred pack and split parameters we
+ * got from the NX transport.
+ */
+
+int nxagentPackLossless = -1;
+int nxagentPackMethod = -1;
+int nxagentPackQuality = -1;
+int nxagentSplitThreshold = -1;
+
+/*
+ * Set if images can use the alpha channel.
+ */
+
+int nxagentAlphaEnabled = 0;
+int nxagentAlphaCompat = 0;
+
+/*
+ * Used to reformat image when connecting to
+ * displays having different byte order.
+ */
+
+extern void BitOrderInvert(unsigned char *, int);
+extern void TwoByteSwap(unsigned char *, register int);
+extern void FourByteSwap(register unsigned char *, register int);
+
+/*
+ * Store the last visual used to unpack
+ * the images for the given client.
+ */
+
+static VisualID nxagentUnpackVisualId[MAX_CONNECTIONS];
+
+/*
+ * Store the last alpha data set for the
+ * client.
+ */
+
+typedef struct _UnpackAlpha
+{
+ char *data;
+ int size;
+
+} UnpackAlphaRec;
+
+typedef UnpackAlphaRec *UnpackAlphaPtr;
+
+static UnpackAlphaPtr nxagentUnpackAlpha[MAX_CONNECTIONS];
+
+/*
+ * Encode the imade alpha channel by using
+ * a specific encoding, separating it from
+ * the rest of the RGB data.
+ */
+
+static char *nxagentImageAlpha(XImage *ximage);
+static void nxagentSetUnpackAlpha(DrawablePtr pDrawable, XImage *pImage, ClientPtr pClient);
+
+/*
+ * Copy the source data to the destination.
+ */
+
+static char *nxagentImageCopy(XImage *source, XImage *destination);
+
+/*
+ * Return true if the image can be cached.
+ * Don't cache the images packed with the
+ * bitmap method as the encoding is little
+ * more expensive than a copy.
+ */
+
+#define nxagentNeedCache(image, method) \
+\
+ ((method) != PACK_BITMAP_16M_COLORS)
+
+/*
+ * With the bitmap encoding, if the image
+ * is 32 bits-per-pixel the 4th byte is not
+ * transmitted, so we don't need to clean
+ * the image.
+ */
+
+#define nxagentNeedClean(image, method) \
+\
+ ((method) == PACK_RLE_16M_COLORS || \
+ (method) == PACK_RGB_16M_COLORS || \
+ ((method) == PACK_BITMAP_16M_COLORS && \
+ image -> bits_per_pixel != 32))
+
+/*
+ * Collect the image cache statistics.
+ */
+
+typedef struct _ImageStatisticsRec
+{
+ double partialLookups;
+ double partialMatches;
+ double partialEncoded;
+ double partialAdded;
+
+ double totalLookups;
+ double totalMatches;
+ double totalEncoded;
+ double totalAdded;
+
+} ImageStatisticsRec;
+
+ImageStatisticsRec nxagentImageStatistics;
+
+int nxagentImageReformat(char *base, int nbytes, int bpp, int order)
+{
+ /*
+ * This is used whenever we need to swap the image data.
+ * If we got an image from a X server having a different
+ * endianess, we will need to redormat the image to match
+ * our own image-order so that ProcGetImage can return
+ * the expected format to the client.
+ */
+
+ switch (bpp)
+ {
+ case 1:
+ {
+ if (BITMAP_BIT_ORDER != order)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentImageReformat: Bit order invert with size [%d] "
+ "bits per pixel [%d] byte order [%d].\n", nbytes, bpp, order);
+ #endif
+
+ BitOrderInvert((unsigned char *) base, nbytes);
+ }
+
+ #if IMAGE_BYTE_ORDER != BITMAP_BIT_ORDER && BITMAP_SCANLINE_UNIT != 8
+
+ nxagentImageReformat(base, nbytes, BITMAP_SCANLINE_UNIT, order);
+
+ #endif
+
+ break;
+ }
+ case 4:
+ case 8:
+ {
+ break;
+ }
+ case 16:
+ {
+ if (IMAGE_BYTE_ORDER != order)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentImageReformat: Two bytes swap with size [%d] "
+ "bits per pixel [%d] byte order [%d].\n", nbytes, bpp, order);
+ #endif
+
+ TwoByteSwap((unsigned char *) base, nbytes);
+ }
+
+ break;
+ }
+ case 32:
+ {
+ if (IMAGE_BYTE_ORDER != order)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentImageReformat: Four bytes swap with size [%d] "
+ "bits per pixel [%d] byte order [%d].\n", nbytes, bpp, order);
+ #endif
+
+ FourByteSwap((unsigned char *) base, nbytes);
+ }
+
+ break;
+ }
+ }
+
+ return 1;
+}
+
+int nxagentImageLength(int width, int height, int format, int leftPad, int depth)
+{
+ int line = 0;
+
+ if (format == XYBitmap)
+ {
+ line = BitmapBytePad(width + leftPad);
+ }
+ else if (format == XYPixmap)
+ {
+ line = BitmapBytePad(width + leftPad);
+
+ line *= depth;
+ }
+ else if (format == ZPixmap)
+ {
+ line = PixmapBytePad(width, depth);
+ }
+
+ return line * height;
+}
+
+int nxagentImagePad(int width, int format, int leftPad, int depth)
+{
+ int line = 0;
+
+ if (format == XYBitmap)
+ {
+ line = BitmapBytePad(width + leftPad);
+ }
+ else if (format == XYPixmap)
+ {
+ line = BitmapBytePad(width + leftPad);
+ }
+ else if (format == ZPixmap)
+ {
+ line = PixmapBytePad(width, depth);
+ }
+
+ return line;
+}
+
+/*
+ * Only copy the data, not the structure.
+ * The data pointed by the destination is
+ * lost. Used to clone two images that
+ * point to the same data.
+ */
+
+char *nxagentImageCopy(XImage *source, XImage *destination)
+{
+ #ifdef DEBUG
+ fprintf(stderr, "nxagentImageClone: Copying [%d] bytes of data from the source.\n",
+ source -> bytes_per_line * source -> height);
+ #endif
+
+ destination -> data = Xmalloc(source -> bytes_per_line * source -> height);
+
+ if (destination -> data == NULL)
+ {
+ #ifdef PANIC
+ fprintf(stderr, "nxagentImageCopy: PANIC! Cannot allocate memory for the copy.\n");
+ #endif
+ }
+ else
+ {
+ memcpy(destination -> data, source -> data,
+ source -> bytes_per_line * source -> height);
+ }
+
+ return destination -> data;
+}
+
+char *nxagentImageAlpha(XImage *image)
+{
+ char *pData;
+
+ char *pSrcData;
+ char *pDstData;
+
+ int size;
+ int offset;
+
+ /*
+ * Use one byte per pixel.
+ */
+
+ size = (image -> bytes_per_line * image -> height) >> 2;
+
+ pData = Xmalloc(size);
+
+ if (pData == NULL)
+ {
+ return NULL;
+ }
+
+ /*
+ * The image is supposed to be in
+ * server order.
+ */
+
+ offset = (image -> byte_order == MSBFirst) ? 0 : 3;
+
+ pSrcData = image -> data;
+ pDstData = pData;
+
+ while (size-- > 0)
+ {
+ *pDstData++ = *(pSrcData + offset);
+
+ pSrcData += 4;
+ }
+
+ return pData;
+}
+
+/*
+ * Write down the image cache statistics
+ * to the buffer.
+ */
+
+void nxagentImageStatisticsHandler(char **buffer, int type)
+{
+/*
+FIXME: Agent cache statistics have to be implemented.
+*/
+ #ifdef TEST
+ fprintf(stderr, "nxagentImageStatisticsHandler: STATISTICS! Statistics requested to the agent.\n");
+ #endif
+
+ *buffer = NULL;
+}
+
+/*
+ * This should be called only for drawables
+ * having a depth of 32. In the other cases,
+ * it would only generate useless traffic.
+ */
+
+void nxagentSetUnpackAlpha(DrawablePtr pDrawable, XImage *pImage, ClientPtr pClient)
+{
+ int resource = pClient -> index;
+
+ unsigned int size = (pImage -> bytes_per_line * pImage -> height) >> 2;
+
+ char *data = nxagentImageAlpha(pImage);
+
+ if (data == NULL)
+ {
+ #ifdef PANIC
+ fprintf(stderr, "nxagentSetUnpackAlpha: PANIC! Can't allocate data for the alpha channel.\n");
+ #endif
+
+ return;
+ }
+
+ /*
+ * If we are synchronizing the drawable, discard
+ * any unpack alpha stored for the client. The
+ * alpha data, in fact, may be still traveling
+ * and so we either wait until the end of the
+ * split or send a fresh copy.
+ */
+/*
+FIXME: Here the split trap is always set and so the caching of
+ the alpha channel is useless. I remember we set the trap
+ because of the cursor but why is it always set now?
+*/
+ #ifdef DEBUG
+ fprintf(stderr, "nxagentSetUnpackAlpha: Checking alpha channel for client [%d] with trap [%d].\n",
+ resource, nxagentSplitTrap);
+ #endif
+
+ if (nxagentSplitTrap == 1 || nxagentUnpackAlpha[resource] == NULL ||
+ nxagentUnpackAlpha[resource] -> size != size ||
+ memcmp(nxagentUnpackAlpha[resource] -> data, data, size) != 0)
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "nxagentSetUnpackAlpha: Sending alpha channel with width [%d] height [%d] "
+ "bytes per line [%d] size [%d].\n", pImage -> width, pImage -> height,
+ pImage -> bytes_per_line, size);
+ #endif
+
+ /*
+ * Check if we are connected to a newer proxy
+ * version and so can send the alpha data in
+ * compressed form.
+ */
+
+ if (nxagentAlphaCompat == 0)
+ {
+ NXSetUnpackAlpha(nxagentDisplay, resource, PACK_NONE, size, data, size);
+ }
+ else
+ {
+ NXSetUnpackAlphaCompat(nxagentDisplay, resource, size, data);
+ }
+
+ if (nxagentUnpackAlpha[resource] != NULL)
+ {
+ Xfree(nxagentUnpackAlpha[resource] -> data);
+ }
+ else if ((nxagentUnpackAlpha[resource] = Xmalloc(sizeof(UnpackAlphaRec))) == NULL)
+ {
+ #ifdef PANIC
+ fprintf(stderr, "nxagentSetUnpackAlpha: PANIC! Can't allocate data for the alpha structure.\n");
+ #endif
+
+ Xfree(data);
+
+ return;
+ }
+
+ #ifdef DEBUG
+ fprintf(stderr, "nxagentSetUnpackAlpha: Saved alpha channel for client [%d] with size [%d].\n",
+ resource, size);
+ #endif
+
+ nxagentUnpackAlpha[resource] -> size = size;
+ nxagentUnpackAlpha[resource] -> data = data;
+ }
+ else
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "nxagentSetUnpackAlpha: Matched alpha channel for client [%d] with size [%d].\n",
+ resource, size);
+ #endif
+
+ Xfree(data);
+ }
+}
+
+/*
+ * The NX agent's implementation of the
+ * X server's image functions.
+ */
+
+void nxagentPutImage(DrawablePtr pDrawable, GCPtr pGC, int depth,
+ int dstX, int dstY, int dstWidth, int dstHeight,
+ int leftPad, int format, char *data)
+{
+ int length;
+
+ RegionPtr pRegion = NullRegion;
+
+ int resource = 0;
+ int split = 0;
+ int cache = 1;
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentPutImage: Image data at [%p] drawable [%s][%p] geometry [%d,%d,%d,%d].\n",
+ data, (pDrawable -> type == DRAWABLE_PIXMAP ? "Pixmap" : "Window"),
+ (void *) pDrawable, dstX, dstY, dstWidth, dstHeight);
+ #endif
+
+ /*
+ * If the display is down and there is not an
+ * nxagent attached, sleep for a while but
+ * still give a chance to the client to write
+ * to the framebuffer.
+ */
+
+ length = nxagentImageLength(dstWidth, dstHeight, format, leftPad, depth);
+
+ if (nxagentShadowCounter == 0 &&
+ NXDisplayError(nxagentDisplay) == 1)
+ {
+ int us;
+
+ us = IMAGE_DELAY_IF_DOWN * (length / 1024);
+
+ us = (us < 10000 ? 10000 : (us > 1000000 ? 1000000 : us));
+
+ #ifdef DEBUG
+ fprintf(stderr, "nxagentPutImage: Sleeping for [%d] milliseconds with length [%d] and link down.\n",
+ us / 1000, length);
+ #endif
+
+ usleep(us);
+ }
+
+ /*
+ * This is of little use because clients usually write
+ * to windows only after an expose event, and, in the
+ * rare case they use a direct put image to the window
+ * (for a media player it should be a necessity), they
+ * are likely to monitor the visibility of the window.
+ */
+
+ if (nxagentOption(IgnoreVisibility) == 0 && pDrawable -> type == DRAWABLE_WINDOW &&
+ (nxagentWindowIsVisible((WindowPtr) pDrawable) == 0 ||
+ (nxagentDefaultWindowIsVisible() == 0 && nxagentCompositeEnable == 0)))
+ {
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentPutImage: WARNING! Prevented operation on fully obscured "
+ "window at [%p].\n", (void *) pDrawable);
+ #endif
+
+ goto nxagentPutImageEnd;
+ }
+
+ /*
+ * This is more interesting. Check if the operation
+ * will produce a visible result based on the clip
+ * list of the window and the GC.
+ */
+
+ pRegion = nxagentCreateRegion(pDrawable, pGC, dstX, dstY, dstWidth, dstHeight);
+
+ if (REGION_NIL(pRegion) == 1)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentPutImage: WARNING! Prevented operation on fully clipped "
+ "region [%d,%d,%d,%d] for drawable at [%p].\n", pRegion -> extents.x1,
+ pRegion -> extents.y1, pRegion -> extents.x2, pRegion -> extents.y2,
+ (void *) pDrawable);
+ #endif
+
+ goto nxagentPutImageEnd;
+ }
+
+/*
+FIXME: Should use these.
+
+ int framebuffer = 1;
+ int realize = 1;
+*/
+ if (nxagentGCTrap == 1 && nxagentReconnectTrap == 0 &&
+ nxagentFBTrap == 0 && nxagentShmTrap == 0)
+ {
+ if (pDrawable -> type == DRAWABLE_PIXMAP)
+ {
+ fbPutImage(nxagentVirtualDrawable(pDrawable), pGC, depth,
+ dstX, dstY, dstWidth, dstHeight, leftPad, format, data);
+ }
+ else
+ {
+ fbPutImage(pDrawable, pGC, depth,
+ dstX, dstY, dstWidth, dstHeight, leftPad, format, data);
+ }
+
+ goto nxagentPutImageEnd;
+ }
+
+ if (nxagentReconnectTrap == 0 &&
+ nxagentSplitTrap == 0)
+ {
+ if (pDrawable -> type == DRAWABLE_PIXMAP &&
+ nxagentFBTrap == 0 && nxagentShmTrap == 0)
+ {
+ fbPutImage(nxagentVirtualDrawable(pDrawable), pGC, depth,
+ dstX, dstY, dstWidth, dstHeight, leftPad, format, data);
+ }
+ else if (pDrawable -> type == DRAWABLE_WINDOW)
+ {
+ fbPutImage(pDrawable, pGC, depth,
+ dstX, dstY, dstWidth, dstHeight, leftPad, format, data);
+ }
+ }
+
+ /*
+ * We are going to realize the operation
+ * on the real display. Let's check if
+ * the link is down.
+ */
+
+ if (NXDisplayError(nxagentDisplay) == 1)
+ {
+ goto nxagentPutImageEnd;
+ }
+
+ /*
+ * Mark the region as corrupted and skip the operation
+ * if we went out of bandwidth. The drawable will be
+ * synchronized at later time. Don't do that if the
+ * image is likely to be a shape or a clip mask, if we
+ * are here because we are actually synchronizing the
+ * drawable or if the drawable's corrupted region is
+ * over-age.
+ */
+
+ if (NXAGENT_SHOULD_DEFER_PUTIMAGE(pDrawable))
+ {
+ if (pDrawable -> type == DRAWABLE_PIXMAP &&
+ pDrawable -> depth != 1 &&
+ nxagentOption(DeferLevel) >= 1)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentPutImage: WARNING! Prevented operation on region [%d,%d,%d,%d] "
+ "for drawable at [%p] with drawable pixmap.\n", pRegion -> extents.x1,
+ pRegion -> extents.y1, pRegion -> extents.x2, pRegion -> extents.y2,
+ (void *) pDrawable);
+ #endif
+
+ nxagentMarkCorruptedRegion(pDrawable, pRegion);
+
+ goto nxagentPutImageEnd;
+ }
+
+ if (pDrawable -> type == DRAWABLE_WINDOW &&
+ nxagentOption(DeferLevel) >= 2)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentPutImage: WARNING! Prevented operation on region [%d,%d,%d,%d] "
+ "for drawable at [%p] with drawable window.\n", pRegion -> extents.x1,
+ pRegion -> extents.y1, pRegion -> extents.x2, pRegion -> extents.y2,
+ (void *) pDrawable);
+ #endif
+
+ nxagentMarkCorruptedRegion(pDrawable, pRegion);
+
+ goto nxagentPutImageEnd;
+ }
+ }
+ #ifdef TEST
+ else
+ {
+ fprintf(stderr, "nxagentPutImage: Operation on drawable [%p] not skipped with nxagentSplitTrap [%d].\n",
+ (void *) pDrawable, nxagentSplitTrap);
+ }
+ #endif
+
+ /*
+ * Check whether we need to enclose the
+ * image in a split sequence.
+ */
+/*
+FIXME: Should we disable the split with link LAN?
+
+ split = (nxagentOption(Streaming) == 1 &&
+ nxagentOption(LinkType) != LINK_TYPE_NONE &&
+ nxagentOption(LinkType) != LINK_TYPE_LAN
+*/
+ split = (nxagentOption(Streaming) == 1 &&
+ nxagentOption(LinkType) != LINK_TYPE_NONE
+/*
+FIXME: Do we stream the images from GLX or Xv? If we do that,
+ we should also write on the frame buffer, including the
+ images put on windows, to be able to reconstruct the
+ region that is out of sync. Surely we should not try to
+ cache the GLX and Xv images in memory or save them in
+ the image cache on disk.
+*/
+/*
+FIXME: Temporarily stream the GLX data.
+
+ && nxagentGlxTrap == 0
+ && nxagentXvTrap == 0
+*/
+);
+
+ /*
+ * Never split images whose depth
+ * is less than 15.
+ */
+
+ if (split == 1 && (nxagentSplitTrap == 1 || depth < 15))
+ {
+ #ifdef TEST
+
+ if (nxagentSplitTrap == 1 ||
+ nxagentReconnectTrap == 1)
+ {
+ fprintf(stderr, "nxagentPutImage: Not splitting with reconnection [%d] trap [%d] "
+ "depth [%d].\n", nxagentSplitTrap, nxagentReconnectTrap, depth);
+ }
+
+ #endif
+
+ split = 0;
+ }
+ #ifdef TEST
+ else if (split == 1)
+ {
+ fprintf(stderr, "nxagentPutImage: Splitting with reconnection [%d] trap [%d] "
+ "depth [%d].\n", nxagentSplitTrap, nxagentReconnectTrap, depth);
+ }
+ #endif
+
+ #ifdef TEST
+
+ if (split == 1)
+ {
+ fprintf(stderr, "nxagentPutImage: Splitting the image with size [%d] "
+ "link [%d] GLX [%d] Xv [%d].\n", length, nxagentOption(LinkType),
+ nxagentGlxTrap, nxagentXvTrap);
+ }
+ else if (nxagentOption(LinkType) != LINK_TYPE_NONE)
+ {
+ fprintf(stderr, "nxagentPutImage: Not splitting the image with size [%d] "
+ "link [%d] GLX [%d] Xv [%d].\n", length, nxagentOption(LinkType),
+ nxagentGlxTrap, nxagentXvTrap);
+ }
+
+ #endif
+
+ /*
+ * If the image was originated by a GLX
+ * or Xvideo request, temporarily disable
+ * the use of the cache.
+ */
+
+ if (nxagentOption(LinkType) != LINK_TYPE_NONE &&
+ (nxagentGlxTrap == 1 || nxagentXvTrap == 1))
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentPutImage: Disabling the use of the cache with GLX or Xvideo.\n");
+ #endif
+
+ NXSetCacheParameters(nxagentDisplay, 0, 1, 0, 0);
+
+ cache = 0;
+ }
+
+ /*
+ * Enclose the next messages in a split
+ * sequence. The proxy will tell us if
+ * the split took place.
+ */
+
+ if (split == 1)
+ {
+ /*
+ * If the drawable is already being split,
+ * expand the region. Currently drawables
+ * can't have more than a single split
+ * region.
+ */
+
+ if (nxagentSplitResource(pDrawable) != NULL)
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentPutImage: WARNING! Expanding the region with drawable at [%p] streaming.\n",
+ (void *) pDrawable);
+ #endif
+/*
+FIXME: Should probably intersect the region with
+ the region being split to also invalidate
+ the commits.
+*/
+ nxagentMarkCorruptedRegion(pDrawable, pRegion);
+
+ goto nxagentPutImageEnd;
+ }
+ else
+ {
+ /*
+ * Assign a new resource to the drawable.
+ * Will also assign the GC to use for the
+ * operation.
+ */
+
+ resource = nxagentCreateSplit(pDrawable, &pGC);
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentPutImage: Resource [%d] assigned to drawable at [%p].\n",
+ resource, (void *) pDrawable);
+ #endif
+ }
+
+ NXStartSplit(nxagentDisplay, resource, NXSplitModeDefault);
+ }
+
+ nxagentRealizeImage(pDrawable, pGC, depth, dstX, dstY,
+ dstWidth, dstHeight, leftPad, format, data);
+
+ if (split == 1)
+ {
+ NXEndSplit(nxagentDisplay, resource);
+
+ /*
+ * Now we need to check if all the messages went
+ * straight through the output stream or any of
+ * them required a split. If no split will take
+ * place, we will remove the association with the
+ * drawable and release the resource at the time
+ * we will handle the no-split event.
+ */
+
+ split = nxagentWaitSplitEvent(resource);
+
+ if (split == 1)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentPutImage: Marking corrupted region [%d,%d,%d,%d] for drawable at [%p].\n",
+ pRegion -> extents.x1, pRegion -> extents.y1, pRegion -> extents.x2,
+ pRegion -> extents.y2, (void *) pDrawable);
+ #endif
+
+ /*
+ * Marking the corrupted region we will check
+ * if the region intersects the split region,
+ * therefore the split region must be added
+ * later.
+ */
+
+ nxagentMarkCorruptedRegion(pDrawable, pRegion);
+
+ /*
+ * Assign the region to the drawable.
+ */
+
+ nxagentRegionSplit(pDrawable, pRegion);
+
+ pRegion = NullRegion;
+ }
+ }
+
+ /*
+ * The split value could be changed by a
+ * no-split event in the block above, so
+ * here we have to check the value again.
+ */
+
+ if (split == 0)
+ {
+ if (nxagentDrawableStatus(pDrawable) == NotSynchronized)
+ {
+ /*
+ * We just covered the drawable with
+ * a solid image. We can consider the
+ * overlapping region as synchronized.
+ */
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentPutImage: Marking synchronized region [%d,%d,%d,%d] for drawable at [%p].\n",
+ pRegion -> extents.x1, pRegion -> extents.y1, pRegion -> extents.x2,
+ pRegion -> extents.y2, (void *) pDrawable);
+ #endif
+
+ nxagentUnmarkCorruptedRegion(pDrawable, pRegion);
+ }
+ }
+
+nxagentPutImageEnd:
+
+ /*
+ * Check if we disabled caching.
+ */
+
+ if (cache == 0)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentPutImage: Reenabling the use of the cache.\n");
+ #endif
+
+ NXSetCacheParameters(nxagentDisplay, 1, 1, 1, 1);
+ }
+
+ if (pRegion != NullRegion)
+ {
+ nxagentFreeRegion(pDrawable, pRegion);
+ }
+}
+
+void nxagentRealizeImage(DrawablePtr pDrawable, GCPtr pGC, int depth,
+ int x, int y, int w, int h, int leftPad,
+ int format, char *data)
+{
+ int length;
+
+ int bytesPerLine;
+ int numSubImages;
+ int totalHeight;
+
+ /*
+ * NXPutPackedImage is longer than PutPackedImage
+ * so that we subtract the bigger one to be sure.
+ */
+
+ const int subSize = (MAX_REQUEST_SIZE << 2) - sizeof(xNXPutPackedImageReq);
+
+ Visual *pVisual = NULL;
+
+ RegionPtr clipRegion = NullRegion;
+
+ XImage *image = NULL;
+
+
+ if (NXDisplayError(nxagentDisplay) == 1)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentRealizeImage: Returning on display error.\n");
+ #endif
+
+ goto nxagentRealizeImageEnd;
+ }
+
+ /*
+ * Get the visual according to the
+ * drawable and depth.
+ */
+
+ pVisual = nxagentImageVisual(pDrawable, depth);
+
+ if (pVisual == NULL)
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentRealizeImage: WARNING! Visual not found. Using default visual.\n");
+ #endif
+
+ pVisual = nxagentVisuals[nxagentDefaultVisualIndex].visual;
+ }
+
+ /*
+ * Get bytes per line according to format.
+ */
+
+ bytesPerLine = nxagentImagePad(w, format, leftPad, depth);
+
+ if (nxagentOption(Shadow) == 1 && format == ZPixmap &&
+ (nxagentOption(XRatio) != DONT_SCALE ||
+ nxagentOption(YRatio) != DONT_SCALE) &&
+ pDrawable == (DrawablePtr) nxagentShadowPixmapPtr)
+ {
+ int scaledx;
+ int scaledy;
+
+ image = XCreateImage(nxagentDisplay, pVisual, depth, ZPixmap,
+ 0, data, w, h, BitmapPad(nxagentDisplay), bytesPerLine);
+
+ if (image != NULL)
+ {
+ image -> byte_order = IMAGE_BYTE_ORDER;
+
+ image -> bitmap_bit_order = BITMAP_BIT_ORDER;
+
+ nxagentScaleImage(x, y, nxagentOption(XRatio), nxagentOption(YRatio),
+ &image, &scaledx, &scaledy);
+
+ x = scaledx;
+ y = scaledy;
+
+ w = image -> width;
+ h = image -> height;
+
+ data = image -> data;
+
+ /*
+ * Width of image has changed.
+ */
+
+ bytesPerLine = nxagentImagePad(w, format, leftPad, depth);
+ }
+ #ifdef WARNING
+ else
+ {
+ fprintf(stderr, "nxagentRealizeImage: Failed to create XImage for scaling.\n");
+ }
+ #endif
+ }
+
+ if (w == 0 || h == 0)
+ {
+ goto nxagentRealizeImageEnd;
+ }
+
+ totalHeight = h;
+
+ length = bytesPerLine * h;
+
+ h = (subSize < length ? subSize : length) / bytesPerLine;
+
+ numSubImages = totalHeight / h + 1;
+
+ while (numSubImages > 0)
+ {
+ if (pDrawable -> type == DRAWABLE_WINDOW)
+ {
+ clipRegion = nxagentCreateRegion(pDrawable, pGC, x, y, w, h);
+ }
+
+ if (clipRegion == NullRegion || REGION_NIL(clipRegion) == 0)
+ {
+ nxagentPutSubImage(pDrawable, pGC, depth, x, y, w, h,
+ leftPad, format, data, pVisual);
+ }
+ #ifdef TEST
+ else
+ {
+ fprintf(stderr, "nxagentRealizeImage: Skipping the put sub image with geometry "
+ "[%d,%d,%d,%d] on hidden window at [%p].\n", x, y, w, h, (void *) pDrawable);
+ }
+ #endif
+
+ if (clipRegion != NullRegion)
+ {
+ nxagentFreeRegion(pDrawable, clipRegion);
+ }
+
+ y += h;
+
+ data += h * bytesPerLine;
+
+ if (--numSubImages == 1)
+ {
+ h = totalHeight % h;
+
+ if (h == 0)
+ {
+ break;
+ }
+ }
+ }
+
+nxagentRealizeImageEnd:
+
+ if (image != NULL)
+ {
+ XDestroyImage(image);
+ }
+}
+
+void nxagentPutSubImage(DrawablePtr pDrawable, GCPtr pGC, int depth,
+ int x, int y, int w, int h, int leftPad, int format,
+ char *data, Visual *pVisual)
+{
+ NXPackedImage *packedImage = NULL;
+ XImage *plainImage = NULL;
+ unsigned char *packedChecksum = NULL;
+
+ unsigned int packMethod = nxagentPackMethod;
+ unsigned int packQuality = nxagentPackQuality;
+
+ ClientPtr client;
+
+ int lossless = 0;
+ int clean = 0;
+ int pack = 0;
+
+ /*
+ * XCreateImage is the place where the leftPad should be passed.
+ * The image data is received from our client unmodified. In
+ * theory what we would need to do is just creating an appropri-
+ * ate XImage structure based on the incoming data and let Xlib
+ * do the rest. Probably we don't have to pass leftPad again in
+ * the src_x of XPutImage otherwise the src_x would make Xlib
+ * to take into account the xoffset field twice. Unfortunately
+ * passing the leftPad doesn't work.
+ *
+ * plainImage = XCreateImage(nxagentDisplay, pVisual,
+ * depth, format, leftPad, data,
+ * w, h, BitmapPad(nxagentDisplay),
+ * nxagentImagePad(w, format, leftPad, depth));
+ */
+
+ if ((plainImage = XCreateImage(nxagentDisplay, pVisual,
+ depth, format, leftPad, data,
+ w, h, BitmapPad(nxagentDisplay),
+ nxagentImagePad(w, format, leftPad, depth))) == NULL)
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentPutSubImage: WARNING! Failed to create image.\n");
+ #endif
+
+ goto nxagentPutSubImageEnd;
+ }
+
+ #ifdef DEBUG
+ fprintf(stderr, "nxagentPutSubImage: Handling image with geometry [%d,%d] depth [%d].\n",
+ w, h, depth);
+
+ fprintf(stderr, "nxagentPutSubImage: Default pack method is [%d] quality is [%d].\n",
+ packMethod, packQuality);
+ #endif
+
+/*
+FIXME: Should use an unpack resource here.
+*/
+ client = requestingClient;
+
+ if (client == NULL)
+ {
+ client = serverClient;
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentPutSubImage: WARNING! Using the server client with index [%d].\n",
+ client -> index);
+ #endif
+ }
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentPutSubImage: Server order is [%d] client order is [%d].\n",
+ nxagentServerOrder(), nxagentClientOrder(client));
+ #endif
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentPutSubImage: Display image order is [%d] bitmap order is [%d].\n",
+ ImageByteOrder(nxagentDisplay), BitmapBitOrder(nxagentDisplay));
+ #endif
+
+ /*
+ * We got the image data from the X client or
+ * from the frame-buffer with our own endianess.
+ * Byte swap the image data if the display has
+ * a different endianess than our own.
+ */
+
+ if (nxagentImageNormalize(plainImage) != 0)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentPutSubImage: WARNING! Reformatted the image with remote order [%d/%d].\n",
+ plainImage -> byte_order, plainImage -> bitmap_bit_order);
+ #endif
+ }
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentPutSubImage: Image has visual with RGB color masks [%0lx][%0lx][%0lx].\n",
+ pVisual -> red_mask, pVisual -> green_mask, pVisual -> blue_mask);
+ #endif
+
+ /*
+ * Check if the user requested to pack the
+ * image but don't pack it if we are not
+ * connected to a proxy or if the depth is
+ * less than 15 bpp.
+ */
+
+ pack = (nxagentOption(LinkType) != LINK_TYPE_NONE &&
+ packMethod != PACK_NONE && depth > 8 && format == ZPixmap);
+
+ lossless = (packMethod == nxagentPackLossless);
+
+ if (pack == 1 && lossless == 0)
+ {
+ /*
+ * Force the image to be sent as a plain
+ * bitmap if we don't have any lossless
+ * encoder available.
+ */
+
+ if (w <= IMAGE_PACK_WIDTH || h <= IMAGE_PACK_HEIGHT ||
+ nxagentImageLength(w, h, format, leftPad, depth) <=
+ IMAGE_PACK_LENGTH || nxagentLosslessTrap == 1)
+ {
+ if (nxagentPackLossless == PACK_NONE)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentPutSubImage: Disabling pack with lossless method [%d] "
+ "trap [%d] size [%d].\n", nxagentPackLossless, nxagentLosslessTrap,
+ nxagentImageLength(w, h, format, leftPad, depth));
+ #endif
+
+ pack = 0;
+ }
+ else
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentPutSubImage: Lossless encoder with geometry [%d,%d] "
+ "trap [%d] size [%d].\n", w, h, nxagentLosslessTrap,
+ nxagentImageLength(w, h, format, leftPad, depth));
+ #endif
+
+ packMethod = nxagentPackLossless;
+
+ lossless = 1;
+ }
+ }
+ }
+
+ /*
+ * Do we still want to pack the image?
+ */
+
+ if (pack == 1)
+ {
+ /*
+ * Set the geometry and alpha channel
+ * to be used for the unpacked image.
+ */
+
+ if (nxagentUnpackVisualId[client -> index] != pVisual -> visualid)
+ {
+ nxagentUnpackVisualId[client -> index] = pVisual -> visualid;
+
+ #ifdef DEBUG
+ fprintf(stderr, "nxagentPutSubImage: Sending new geometry for client [%d].\n",
+ client -> index);
+ #endif
+
+ NXSetUnpackGeometry(nxagentDisplay, client -> index, pVisual);
+ }
+
+ /*
+ * Check if the image is supposed to carry
+ * the alpha data in the fourth byte and,
+ * if so, send the alpha channel using the
+ * specific encoding.
+ */
+
+ if (plainImage -> depth == 32)
+ {
+ nxagentSetUnpackAlpha(pDrawable, plainImage, client);
+ }
+
+ /*
+ * If the image doesn't come from the XVideo or the
+ * GLX extension try to locate it in the cache. The
+ * case of the lossless trap is also special, as we
+ * want to eventually encode the image again using
+ * a lossless compression.
+ */
+/*
+FIXME: Should try to locate the image anyway, if the lossless
+ trap is set, and if the image was encoded by a lossy
+ compressor, roll back the changes and encode the image
+ again using the preferred method.
+*/
+ if (nxagentNeedCache(plainImage, packMethod) &&
+ nxagentGlxTrap == 0 && nxagentXvTrap == 0 &&
+ nxagentLosslessTrap == 0 && NXImageCacheSize > 0)
+ {
+ /*
+ * Be sure that the padding bits are
+ * cleaned before calculating the MD5
+ * checksum.
+ */
+/*
+FIXME: There should be a callback registered by the agent that
+ provides a statistics report, in text format, telling
+ for example how many images were searched in the cache,
+ how many were found, how many drawables are to be synch-
+ ronized, etc. This statistics report would be included
+ by the proxy in its stat output.
+*/
+ clean = 1;
+
+ NXCleanImage(plainImage);
+
+ /*
+ * Will return a pointer to the image and checksum
+ * taken from the cache, if found. If the image is
+ * not found, the function returns a null image and
+ * a pointer to the calculated checksum. It is up
+ * to the application to free the memory. We will
+ * use the checksum to add the image in the cache.
+ */
+
+ packedImage = NXCacheFindImage(plainImage, &packMethod, &packedChecksum);
+
+ nxagentImageStatistics.partialLookups++;
+ nxagentImageStatistics.totalLookups++;
+
+ if (packedImage != NULL)
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "nxagentPutSubImage: Matched image of geometry [%d,%d] data size [%d] in cache.\n",
+ w, h, packedImage -> xoffset);
+ #endif
+
+ NXPutPackedImage(nxagentDisplay, client -> index, nxagentDrawable(pDrawable),
+ nxagentGC(pGC), packedImage, packMethod, depth,
+ 0, 0, x, y, w, h);
+
+ nxagentImageStatistics.partialMatches++;
+ nxagentImageStatistics.totalMatches++;
+
+ packedChecksum = NULL;
+ packedImage = NULL;
+
+ goto nxagentPutSubImageEnd;
+ }
+ #ifdef DEBUG
+ else
+ {
+ fprintf(stderr, "nxagentPutSubImage: WARNING! Missed image of geometry [%d,%d] in cache.\n",
+ w, h);
+ }
+ #endif
+ }
+
+ /*
+ * If a specific encoder was not mandated,
+ * try to guess if a lossless encoder will
+ * compress better.
+ */
+
+ if (lossless == 0 && nxagentOption(Adaptive) == 1)
+ {
+ int ratio = nxagentUniquePixels(plainImage);
+
+ if (ratio <= IMAGE_UNIQUE_RATIO)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentPutSubImage: Lossless encoder with geometry [%d,%d] ratio [%d%%].\n",
+ w, h, ratio);
+ #endif
+
+ packMethod = nxagentPackLossless;
+
+ lossless = 1;
+ }
+ #ifdef TEST
+ else
+ {
+ fprintf(stderr, "nxagentPutSubImage: Default encoder with geometry [%d,%d] ratio [%d%%].\n",
+ w, h, ratio);
+ }
+ #endif
+ }
+
+ /*
+ * Encode the image using the selected
+ * pack method.
+ */
+
+ if (packMethod == PACK_RLE_16M_COLORS ||
+ packMethod == PACK_RGB_16M_COLORS ||
+ packMethod == PACK_BITMAP_16M_COLORS)
+ {
+ /*
+ * Cleanup the image if we didn't do that yet.
+ * We assume that the JPEG and PNG compression
+ * methods will actually ignore the padding
+ * bytes. In other words, bitmap images prod-
+ * ucing the same visual output should produce
+ * compressed images that are bitwise the same
+ * regardless the padding bits.
+ */
+
+ if (clean == 0)
+ {
+ if (nxagentNeedClean(plainImage, packMethod) == 1)
+ {
+ clean = 1;
+
+ NXCleanImage(plainImage);
+ }
+ }
+
+ switch (packMethod)
+ {
+ /*
+ * If nothing is done by the bitmap encoder,
+ * it saves an allocation and a memory copy
+ * by setting the data field of the packed
+ * image to the original data. We need to
+ * check this at the time we will free the
+ * packed image.
+ */
+
+ case PACK_BITMAP_16M_COLORS:
+ {
+ packedImage = NXEncodeBitmap(plainImage, packMethod, packQuality);
+
+ break;
+ }
+ case PACK_RGB_16M_COLORS:
+ {
+ packedImage = NXEncodeRgb(plainImage, packMethod, packQuality);
+
+ break;
+ }
+ default:
+ {
+ packedImage = NXEncodeRle(plainImage, packMethod, packQuality);
+
+ break;
+ }
+ }
+ }
+ else if (packMethod >= PACK_JPEG_8_COLORS &&
+ packMethod <= PACK_JPEG_16M_COLORS)
+ {
+ packedImage = NXEncodeJpeg(plainImage, packMethod, packQuality);
+ }
+ else if (packMethod >= PACK_PNG_8_COLORS &&
+ packMethod <= PACK_PNG_16M_COLORS)
+ {
+ packedImage = NXEncodePng(plainImage, packMethod, packQuality);
+ }
+ else if (packMethod != PACK_NONE)
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentPutSubImage: WARNING! Ignoring deprecated pack method [%d].\n",
+ packMethod);
+ #endif
+
+ packMethod = PACK_NONE;
+ }
+ }
+
+ /*
+ * If we didn't produce a valid packed
+ * image, send the image as a X bitmap.
+ */
+
+ if (packedImage != NULL)
+ {
+ nxagentImageStatistics.partialEncoded++;
+ nxagentImageStatistics.totalEncoded++;
+
+ #ifdef DEBUG
+ fprintf(stderr, "nxagentPutSubImage: Using packed image with method [%d] quality [%d] "
+ "geometry [%d,%d] data size [%d].\n", packMethod, packQuality,
+ w, h, packedImage -> xoffset);
+ #endif
+
+ NXPutPackedImage(nxagentDisplay, client -> index, nxagentDrawable(pDrawable),
+ nxagentGC(pGC), packedImage, packMethod, depth,
+ 0, 0, x, y, w, h);
+
+ /*
+ * Add the image only if we have a valid
+ * checksum. This is the case only if we
+ * originally tried to find the image in
+ * cache.
+ */
+
+ if (NXImageCacheSize > 0 && packedChecksum != NULL)
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "nxagentPutSubImage: Adding image with geometry [%d,%d] data size [%d] "
+ "to the cache.\n", w, h, packedImage -> xoffset);
+ #endif
+
+ /*
+ * Check if both the plain and the packed
+ * image point to the same data. In this
+ * case we need a copy.
+ */
+
+ if (packedImage -> data == plainImage -> data &&
+ nxagentImageCopy(plainImage, packedImage) == NULL)
+ {
+ goto nxagentPutSubImageEnd;
+ }
+
+ NXCacheAddImage(packedImage, packMethod, packedChecksum);
+
+ nxagentImageStatistics.partialAdded++;
+ nxagentImageStatistics.totalAdded++;
+
+ packedChecksum = NULL;
+ packedImage = NULL;
+ }
+ }
+ else
+ {
+ /*
+ * Clean the image to help the proxy to match
+ * the checksum in its cache. Do that only if
+ * the differential compression is enabled and
+ * if the image is not supposed to carry the
+ * alpha data in the fourth byte of the pixel.
+ */
+/*
+FIXME: If we failed to encode the image by any of the available
+ methods, for example if we couldn't allocate memory, we
+ may need to ripristinate the alpha channel, that in the
+ meanwhile was sent in the unpack alpha message. This can
+ be done here, if the clean flag is true and we are going
+ to send a plain image.
+*/
+ if (clean == 0)
+ {
+ clean = (nxagentOption(LinkType) != LINK_TYPE_NONE &&
+ nxagentOption(LinkType) != LINK_TYPE_LAN && depth != 32);
+
+ if (clean == 1)
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "nxagentPutSubImage: Cleaning the image with link type [%d] and depth [%d].\n",
+ nxagentOption(LinkType), depth);
+ #endif
+
+ NXCleanImage(plainImage);
+ }
+ #ifdef DEBUG
+ else
+ {
+ fprintf(stderr, "nxagentPutSubImage: Not cleaning the image with link type [%d] and depth [%d].\n",
+ nxagentOption(LinkType), depth);
+ }
+ #endif
+ }
+
+ #ifdef DEBUG
+ fprintf(stderr, "nxagentPutSubImage: Calling XPutImage with geometry [%d,%d] and data size [%d].\n",
+ w, h, plainImage -> bytes_per_line * plainImage -> height);
+ #endif
+
+ /*
+ * Passing the leftPad value in src_x doesn't work.
+ *
+ * XPutImage(nxagentDisplay, nxagentDrawable(pDrawable),
+ * nxagentGC(pGC), plainImage, leftPad, 0, x, y, w, h);
+ */
+
+ XPutImage(nxagentDisplay, nxagentDrawable(pDrawable),
+ nxagentGC(pGC), plainImage, 0, 0, x, y, w, h);
+ }
+
+nxagentPutSubImageEnd:
+
+ #ifdef TEST
+ fprintf(stderr, "nxagentPutSubImage: Performed [%.0f] lookups in the cache with [%.0f] matches.\n",
+ nxagentImageStatistics.totalLookups, nxagentImageStatistics.totalMatches);
+
+ fprintf(stderr, "nxagentPutSubImage: Encoded [%.0f] images with [%.0f] added to the cache.\n",
+ nxagentImageStatistics.totalEncoded, nxagentImageStatistics.totalAdded);
+ #endif
+
+ if (packedChecksum != NULL)
+ {
+ Xfree(packedChecksum);
+ }
+
+ if (packedImage != NULL)
+ {
+ if (packedImage -> data != NULL &&
+ packedImage -> data != plainImage -> data)
+ {
+ Xfree(packedImage -> data);
+ }
+
+ Xfree(packedImage);
+ }
+
+ Xfree(plainImage);
+}
+
+void nxagentGetImage(DrawablePtr pDrawable, int x, int y, int w, int h,
+ unsigned int format, unsigned long planeMask, char *data)
+{
+ #ifdef TEST
+ fprintf(stderr, "nxagentGetImage: Called with drawable at [%p] geometry [%d,%d,%d,%d].\n",
+ (void *) pDrawable, x, y, w, h);
+
+ fprintf(stderr, "nxagentGetImage: Format is [%d] plane mask is [%lx].\n",
+ format, planeMask);
+ #endif
+
+ if ((pDrawable)->type == DRAWABLE_PIXMAP)
+ {
+ #ifdef TEST
+ fprintf(stderr, "nxagentGetImage: Going to get image from virtual pixmap at [%p].\n",
+ (void *) nxagentVirtualDrawable(pDrawable));
+ #endif
+
+ fbGetImage(nxagentVirtualDrawable(pDrawable), x, y, w, h, format, planeMask, data);
+ }
+ else
+ {
+ fbGetImage(pDrawable, x, y, w, h, format, planeMask, data);
+ }
+}
+
+/*
+ * We have to reset the visual cache before
+ * connecting to another display, so that a
+ * new unpack geometry can be communicated
+ * to the new proxy.
+ */
+
+void nxagentResetVisualCache()
+{
+ int i;
+
+ for (i = 0; i < MAX_CONNECTIONS; i++)
+ {
+ nxagentUnpackVisualId[i] = None;
+ }
+}
+
+void nxagentResetAlphaCache()
+{
+ int i;
+
+ for (i = 0; i < MAX_CONNECTIONS; i++)
+ {
+ if (nxagentUnpackAlpha[i])
+ {
+ Xfree(nxagentUnpackAlpha[i] -> data);
+
+ Xfree(nxagentUnpackAlpha[i]);
+
+ nxagentUnpackAlpha[i] = NULL;
+ }
+ }
+}
+
+int nxagentScaleImage(int x, int y, unsigned xRatio, unsigned yRatio,
+ XImage **pImage, int *scaledx, int *scaledy)
+{
+ int x1;
+ int x2;
+ int y1;
+ int y2;
+
+ int xx1;
+ int xx2;
+ int yy1;
+ int yy2;
+
+ int newWidth;
+ int newHeight;
+
+ int i;
+ int j;
+ int k;
+ int l;
+
+ unsigned long val;
+
+ XImage *newImage;
+ XImage *image = *pImage;
+
+ #ifdef FAST_GET_PUT_PIXEL
+
+ register char *srcPixel;
+ register char *dstPixel;
+
+ int i;
+
+ #endif
+
+ if (image == NULL)
+ {
+ return 0;
+ }
+
+ x1 = (xRatio * x) >> PRECISION;
+ x2 = (xRatio * (x + image -> width)) >> PRECISION;
+
+ y1 = (yRatio * y) >> PRECISION;
+ y2 = (yRatio * (y + image -> height)) >> PRECISION;
+
+ newWidth = x2 - x1;
+ newHeight = y2 - y1;
+
+ newImage = XCreateImage(nxagentDisplay, NULL, image -> depth, image -> format, 0, NULL,
+ newWidth, newHeight, BitmapPad(nxagentDisplay),
+ PixmapBytePad(newWidth, image -> depth));
+
+ if (newImage == NULL)
+ {
+ #ifdef PANIC
+ fprintf(stderr, "nxagentScaleImage: PANIC! Failed to create the target image.\n");
+ #endif
+
+ return 0;
+ }
+
+ newImage -> red_mask = image -> red_mask;
+ newImage -> green_mask = image -> green_mask;
+ newImage -> blue_mask = image -> blue_mask;
+
+ newImage -> byte_order = IMAGE_BYTE_ORDER;
+ newImage -> bitmap_bit_order = BITMAP_BIT_ORDER;
+
+ newImage -> data = Xmalloc(newImage -> bytes_per_line * newHeight);
+
+ if (newImage -> data == NULL)
+ {
+ Xfree(newImage);
+
+ #ifdef PANIC
+ fprintf(stderr, "nxagentScaleImage: PANIC! Failed to create the target image data.\n");
+ #endif
+
+ return 0;
+ }
+
+ newImage -> width = newWidth;
+ newImage -> height = newHeight;
+
+ for (j = y; j < y + image -> height; j++)
+ {
+ yy1 = (yRatio * j) >> PRECISION;
+ yy2 = (yRatio * (j + 1)) >> PRECISION;
+
+ for (i = x; i < x + image -> width; i++)
+ {
+ #ifndef FAST_GET_PUT_PIXEL
+
+ val = XGetPixel(image, i - x, j - y);
+
+ #else
+
+ srcPixel = &image -> data[(j * image -> bytes_per_line) +
+ ((i * image -> bits_per_pixel) >> 3)];
+
+ dstPixel = (char *) &val;
+
+ val = 0;
+
+ for (i = (image -> bits_per_pixel + 7) >> 3; --i >= 0; )
+ {
+ *dstPixel++ = *srcPixel++;
+ }
+
+ #endif
+
+ xx1 = (xRatio * i) >> PRECISION;
+ xx2 = (xRatio * (i + 1)) >> PRECISION;
+
+ for (l = yy1; l < yy2; l++)
+ {
+ for (k = xx1; k < xx2; k++)
+ {
+ #ifndef FAST_GET_PUT_PIXEL
+
+ XPutPixel(newImage, k - x1, l - y1, val);
+
+ #else
+
+ dstPixel = &newImage -> data[((l - y1) * newImage -> bytes_per_line) +
+ (((k - x1) * newImage -> bits_per_pixel) >> 3)];
+
+ srcPixel = (char *) &val;
+
+ for (i = (newImage -> bits_per_pixel + 7) >> 3; --i >= 0; )
+ {
+ *dstPixel++ = *srcPixel++;
+ }
+
+ #endif
+ }
+ }
+ }
+ }
+
+ if (image -> obdata != NULL)
+ {
+ Xfree((char *) image -> obdata);
+ }
+
+ Xfree((char *) image);
+
+ *pImage = newImage;
+
+ *scaledx = x1;
+ *scaledy = y1;
+
+ return 1;
+}
+
+char *nxagentAllocateImageData(int width, int height, int depth, int *length, int *format)
+{
+ char *data;
+
+ int leftPad;
+
+ leftPad = 0;
+
+ *format = (depth == 1) ? XYPixmap : ZPixmap;
+
+ *length = nxagentImageLength(width, height, *format, leftPad, depth);
+
+ data = NULL;
+
+ if ((data = xalloc(*length)) == NULL)
+ {
+ #ifdef WARNING
+ fprintf(stderr, "nxagentAllocateImageData: WARNING! Failed to allocate [%d] bytes of memory.\n", *length);
+ #endif
+ }
+
+ return data;
+}
+