| /* |
| * translate.c - translate between different pixel formats |
| */ |
| |
| /* |
| * OSXvnc Copyright (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>. |
| * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. |
| * 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. |
| */ |
| |
| #include <rfb/rfb.h> |
| #include <rfb/rfbregion.h> |
| |
| static void PrintPixelFormat(rfbPixelFormat *pf); |
| static rfbBool rfbSetClientColourMapBGR233(rfbClientPtr cl); |
| |
| rfbBool rfbEconomicTranslate = FALSE; |
| |
| /* |
| * Some standard pixel formats. |
| */ |
| |
| static const rfbPixelFormat BGR233Format = { |
| 8, 8, 0, 1, 7, 7, 3, 0, 3, 6, 0, 0 |
| }; |
| |
| |
| /* |
| * Macro to compare pixel formats. |
| */ |
| |
| #define PF_EQ(x,y) \ |
| ((x.bitsPerPixel == y.bitsPerPixel) && \ |
| (x.depth == y.depth) && \ |
| ((x.bigEndian == y.bigEndian) || (x.bitsPerPixel == 8)) && \ |
| (x.trueColour == y.trueColour) && \ |
| (!x.trueColour || ((x.redMax == y.redMax) && \ |
| (x.greenMax == y.greenMax) && \ |
| (x.blueMax == y.blueMax) && \ |
| (x.redShift == y.redShift) && \ |
| (x.greenShift == y.greenShift) && \ |
| (x.blueShift == y.blueShift)))) |
| |
| #define CONCAT2(a,b) a##b |
| #define CONCAT2E(a,b) CONCAT2(a,b) |
| #define CONCAT3(a,b,c) a##b##c |
| #define CONCAT3E(a,b,c) CONCAT3(a,b,c) |
| #define CONCAT4(a,b,c,d) a##b##c##d |
| #define CONCAT4E(a,b,c,d) CONCAT4(a,b,c,d) |
| |
| #undef OUT |
| #undef IN |
| |
| #define OUT 8 |
| #include "tableinitcmtemplate.c" |
| #include "tableinittctemplate.c" |
| #define IN 8 |
| #include "tabletranstemplate.c" |
| #undef IN |
| #define IN 16 |
| #include "tabletranstemplate.c" |
| #undef IN |
| #define IN 32 |
| #include "tabletranstemplate.c" |
| #undef IN |
| #undef OUT |
| |
| #define OUT 16 |
| #include "tableinitcmtemplate.c" |
| #include "tableinittctemplate.c" |
| #define IN 8 |
| #include "tabletranstemplate.c" |
| #undef IN |
| #define IN 16 |
| #include "tabletranstemplate.c" |
| #undef IN |
| #define IN 32 |
| #include "tabletranstemplate.c" |
| #undef IN |
| #undef OUT |
| |
| #define OUT 32 |
| #include "tableinitcmtemplate.c" |
| #include "tableinittctemplate.c" |
| #define IN 8 |
| #include "tabletranstemplate.c" |
| #undef IN |
| #define IN 16 |
| #include "tabletranstemplate.c" |
| #undef IN |
| #define IN 32 |
| #include "tabletranstemplate.c" |
| #undef IN |
| #undef OUT |
| |
| #ifdef LIBVNCSERVER_ALLOW24BPP |
| #define COUNT_OFFSETS 4 |
| #define BPP2OFFSET(bpp) ((bpp)/8-1) |
| #include "tableinit24.c" |
| #define BPP 8 |
| #include "tabletrans24template.c" |
| #undef BPP |
| #define BPP 16 |
| #include "tabletrans24template.c" |
| #undef BPP |
| #define BPP 24 |
| #include "tabletrans24template.c" |
| #undef BPP |
| #define BPP 32 |
| #include "tabletrans24template.c" |
| #undef BPP |
| #else |
| #define COUNT_OFFSETS 3 |
| #define BPP2OFFSET(bpp) ((int)(bpp)/16) |
| #endif |
| |
| typedef void (*rfbInitCMTableFnType)(char **table, rfbPixelFormat *in, |
| rfbPixelFormat *out,rfbColourMap* cm); |
| typedef void (*rfbInitTableFnType)(char **table, rfbPixelFormat *in, |
| rfbPixelFormat *out); |
| |
| static rfbInitCMTableFnType rfbInitColourMapSingleTableFns[COUNT_OFFSETS] = { |
| rfbInitColourMapSingleTable8, |
| rfbInitColourMapSingleTable16, |
| #ifdef LIBVNCSERVER_ALLOW24BPP |
| rfbInitColourMapSingleTable24, |
| #endif |
| rfbInitColourMapSingleTable32 |
| }; |
| |
| static rfbInitTableFnType rfbInitTrueColourSingleTableFns[COUNT_OFFSETS] = { |
| rfbInitTrueColourSingleTable8, |
| rfbInitTrueColourSingleTable16, |
| #ifdef LIBVNCSERVER_ALLOW24BPP |
| rfbInitTrueColourSingleTable24, |
| #endif |
| rfbInitTrueColourSingleTable32 |
| }; |
| |
| static rfbInitTableFnType rfbInitTrueColourRGBTablesFns[COUNT_OFFSETS] = { |
| rfbInitTrueColourRGBTables8, |
| rfbInitTrueColourRGBTables16, |
| #ifdef LIBVNCSERVER_ALLOW24BPP |
| rfbInitTrueColourRGBTables24, |
| #endif |
| rfbInitTrueColourRGBTables32 |
| }; |
| |
| static rfbTranslateFnType rfbTranslateWithSingleTableFns[COUNT_OFFSETS][COUNT_OFFSETS] = { |
| { rfbTranslateWithSingleTable8to8, |
| rfbTranslateWithSingleTable8to16, |
| #ifdef LIBVNCSERVER_ALLOW24BPP |
| rfbTranslateWithSingleTable8to24, |
| #endif |
| rfbTranslateWithSingleTable8to32 }, |
| { rfbTranslateWithSingleTable16to8, |
| rfbTranslateWithSingleTable16to16, |
| #ifdef LIBVNCSERVER_ALLOW24BPP |
| rfbTranslateWithSingleTable16to24, |
| #endif |
| rfbTranslateWithSingleTable16to32 }, |
| #ifdef LIBVNCSERVER_ALLOW24BPP |
| { rfbTranslateWithSingleTable24to8, |
| rfbTranslateWithSingleTable24to16, |
| rfbTranslateWithSingleTable24to24, |
| rfbTranslateWithSingleTable24to32 }, |
| #endif |
| { rfbTranslateWithSingleTable32to8, |
| rfbTranslateWithSingleTable32to16, |
| #ifdef LIBVNCSERVER_ALLOW24BPP |
| rfbTranslateWithSingleTable32to24, |
| #endif |
| rfbTranslateWithSingleTable32to32 } |
| }; |
| |
| static rfbTranslateFnType rfbTranslateWithRGBTablesFns[COUNT_OFFSETS][COUNT_OFFSETS] = { |
| { rfbTranslateWithRGBTables8to8, |
| rfbTranslateWithRGBTables8to16, |
| #ifdef LIBVNCSERVER_ALLOW24BPP |
| rfbTranslateWithRGBTables8to24, |
| #endif |
| rfbTranslateWithRGBTables8to32 }, |
| { rfbTranslateWithRGBTables16to8, |
| rfbTranslateWithRGBTables16to16, |
| #ifdef LIBVNCSERVER_ALLOW24BPP |
| rfbTranslateWithRGBTables16to24, |
| #endif |
| rfbTranslateWithRGBTables16to32 }, |
| #ifdef LIBVNCSERVER_ALLOW24BPP |
| { rfbTranslateWithRGBTables24to8, |
| rfbTranslateWithRGBTables24to16, |
| rfbTranslateWithRGBTables24to24, |
| rfbTranslateWithRGBTables24to32 }, |
| #endif |
| { rfbTranslateWithRGBTables32to8, |
| rfbTranslateWithRGBTables32to16, |
| #ifdef LIBVNCSERVER_ALLOW24BPP |
| rfbTranslateWithRGBTables32to24, |
| #endif |
| rfbTranslateWithRGBTables32to32 } |
| }; |
| |
| |
| |
| /* |
| * rfbTranslateNone is used when no translation is required. |
| */ |
| |
| void |
| rfbTranslateNone(char *table, rfbPixelFormat *in, rfbPixelFormat *out, |
| char *iptr, char *optr, int bytesBetweenInputLines, |
| int width, int height) |
| { |
| int bytesPerOutputLine = width * (out->bitsPerPixel / 8); |
| |
| while (height > 0) { |
| memcpy(optr, iptr, bytesPerOutputLine); |
| iptr += bytesBetweenInputLines; |
| optr += bytesPerOutputLine; |
| height--; |
| } |
| } |
| |
| |
| /* |
| * rfbSetTranslateFunction sets the translation function. |
| */ |
| |
| rfbBool |
| rfbSetTranslateFunction(rfbClientPtr cl) |
| { |
| rfbLog("Pixel format for client %s:\n",cl->host); |
| PrintPixelFormat(&cl->format); |
| |
| /* |
| * Check that bits per pixel values are valid |
| */ |
| |
| if ((cl->screen->serverFormat.bitsPerPixel != 8) && |
| (cl->screen->serverFormat.bitsPerPixel != 16) && |
| #ifdef LIBVNCSERVER_ALLOW24BPP |
| (cl->screen->serverFormat.bitsPerPixel != 24) && |
| #endif |
| (cl->screen->serverFormat.bitsPerPixel != 32)) |
| { |
| rfbErr("%s: server bits per pixel not 8, 16 or 32 (is %d)\n", |
| "rfbSetTranslateFunction", |
| cl->screen->serverFormat.bitsPerPixel); |
| rfbCloseClient(cl); |
| return FALSE; |
| } |
| |
| if ((cl->format.bitsPerPixel != 8) && |
| (cl->format.bitsPerPixel != 16) && |
| #ifdef LIBVNCSERVER_ALLOW24BPP |
| (cl->format.bitsPerPixel != 24) && |
| #endif |
| (cl->format.bitsPerPixel != 32)) |
| { |
| rfbErr("%s: client bits per pixel not 8, 16 or 32\n", |
| "rfbSetTranslateFunction"); |
| rfbCloseClient(cl); |
| return FALSE; |
| } |
| |
| if (!cl->format.trueColour && (cl->format.bitsPerPixel != 8)) { |
| rfbErr("rfbSetTranslateFunction: client has colour map " |
| "but %d-bit - can only cope with 8-bit colour maps\n", |
| cl->format.bitsPerPixel); |
| rfbCloseClient(cl); |
| return FALSE; |
| } |
| |
| /* |
| * bpp is valid, now work out how to translate |
| */ |
| |
| if (!cl->format.trueColour) { |
| /* |
| * truecolour -> colour map |
| * |
| * Set client's colour map to BGR233, then effectively it's |
| * truecolour as well |
| */ |
| |
| if (!rfbSetClientColourMapBGR233(cl)) |
| return FALSE; |
| |
| cl->format = BGR233Format; |
| } |
| |
| /* truecolour -> truecolour */ |
| |
| if (PF_EQ(cl->format,cl->screen->serverFormat)) { |
| |
| /* client & server the same */ |
| |
| rfbLog("no translation needed\n"); |
| cl->translateFn = rfbTranslateNone; |
| return TRUE; |
| } |
| |
| if ((cl->screen->serverFormat.bitsPerPixel < 16) || |
| ((!cl->screen->serverFormat.trueColour || !rfbEconomicTranslate) && |
| (cl->screen->serverFormat.bitsPerPixel == 16))) { |
| |
| /* we can use a single lookup table for <= 16 bpp */ |
| |
| cl->translateFn = rfbTranslateWithSingleTableFns |
| [BPP2OFFSET(cl->screen->serverFormat.bitsPerPixel)] |
| [BPP2OFFSET(cl->format.bitsPerPixel)]; |
| |
| if(cl->screen->serverFormat.trueColour) |
| (*rfbInitTrueColourSingleTableFns |
| [BPP2OFFSET(cl->format.bitsPerPixel)]) (&cl->translateLookupTable, |
| &(cl->screen->serverFormat), &cl->format); |
| else |
| (*rfbInitColourMapSingleTableFns |
| [BPP2OFFSET(cl->format.bitsPerPixel)]) (&cl->translateLookupTable, |
| &(cl->screen->serverFormat), &cl->format,&cl->screen->colourMap); |
| |
| } else { |
| |
| /* otherwise we use three separate tables for red, green and blue */ |
| |
| cl->translateFn = rfbTranslateWithRGBTablesFns |
| [BPP2OFFSET(cl->screen->serverFormat.bitsPerPixel)] |
| [BPP2OFFSET(cl->format.bitsPerPixel)]; |
| |
| (*rfbInitTrueColourRGBTablesFns |
| [BPP2OFFSET(cl->format.bitsPerPixel)]) (&cl->translateLookupTable, |
| &(cl->screen->serverFormat), &cl->format); |
| } |
| |
| return TRUE; |
| } |
| |
| |
| |
| /* |
| * rfbSetClientColourMapBGR233 sets the client's colour map so that it's |
| * just like an 8-bit BGR233 true colour client. |
| */ |
| |
| static rfbBool |
| rfbSetClientColourMapBGR233(rfbClientPtr cl) |
| { |
| char buf[sz_rfbSetColourMapEntriesMsg + 256 * 3 * 2]; |
| rfbSetColourMapEntriesMsg *scme = (rfbSetColourMapEntriesMsg *)buf; |
| uint16_t *rgb = (uint16_t *)(&buf[sz_rfbSetColourMapEntriesMsg]); |
| int i, len; |
| int r, g, b; |
| |
| if (cl->format.bitsPerPixel != 8 ) { |
| rfbErr("%s: client not 8 bits per pixel\n", |
| "rfbSetClientColourMapBGR233"); |
| rfbCloseClient(cl); |
| return FALSE; |
| } |
| |
| scme->type = rfbSetColourMapEntries; |
| |
| scme->firstColour = Swap16IfLE(0); |
| scme->nColours = Swap16IfLE(256); |
| |
| len = sz_rfbSetColourMapEntriesMsg; |
| |
| i = 0; |
| |
| for (b = 0; b < 4; b++) { |
| for (g = 0; g < 8; g++) { |
| for (r = 0; r < 8; r++) { |
| rgb[i++] = Swap16IfLE(r * 65535 / 7); |
| rgb[i++] = Swap16IfLE(g * 65535 / 7); |
| rgb[i++] = Swap16IfLE(b * 65535 / 3); |
| } |
| } |
| } |
| |
| len += 256 * 3 * 2; |
| |
| if (rfbWriteExact(cl, buf, len) < 0) { |
| rfbLogPerror("rfbSetClientColourMapBGR233: write"); |
| rfbCloseClient(cl); |
| return FALSE; |
| } |
| return TRUE; |
| } |
| |
| /* this function is not called very often, so it needn't be |
| efficient. */ |
| |
| /* |
| * rfbSetClientColourMap is called to set the client's colour map. If the |
| * client is a true colour client, we simply update our own translation table |
| * and mark the whole screen as having been modified. |
| */ |
| |
| rfbBool |
| rfbSetClientColourMap(rfbClientPtr cl, int firstColour, int nColours) |
| { |
| if (cl->screen->serverFormat.trueColour || !cl->readyForSetColourMapEntries) { |
| return TRUE; |
| } |
| |
| if (nColours == 0) { |
| nColours = cl->screen->colourMap.count; |
| } |
| |
| if (cl->format.trueColour) { |
| LOCK(cl->updateMutex); |
| (*rfbInitColourMapSingleTableFns |
| [BPP2OFFSET(cl->format.bitsPerPixel)]) (&cl->translateLookupTable, |
| &cl->screen->serverFormat, &cl->format,&cl->screen->colourMap); |
| |
| sraRgnDestroy(cl->modifiedRegion); |
| cl->modifiedRegion = |
| sraRgnCreateRect(0,0,cl->screen->width,cl->screen->height); |
| UNLOCK(cl->updateMutex); |
| |
| return TRUE; |
| } |
| |
| return rfbSendSetColourMapEntries(cl, firstColour, nColours); |
| } |
| |
| |
| /* |
| * rfbSetClientColourMaps sets the colour map for each RFB client. |
| */ |
| |
| void |
| rfbSetClientColourMaps(rfbScreenInfoPtr rfbScreen, int firstColour, int nColours) |
| { |
| rfbClientIteratorPtr i; |
| rfbClientPtr cl; |
| |
| i = rfbGetClientIterator(rfbScreen); |
| while((cl = rfbClientIteratorNext(i))) |
| rfbSetClientColourMap(cl, firstColour, nColours); |
| rfbReleaseClientIterator(i); |
| } |
| |
| static void |
| PrintPixelFormat(rfbPixelFormat *pf) |
| { |
| if (pf->bitsPerPixel == 1) { |
| rfbLog(" 1 bpp, %s sig bit in each byte is leftmost on the screen.\n", |
| (pf->bigEndian ? "most" : "least")); |
| } else { |
| rfbLog(" %d bpp, depth %d%s\n",pf->bitsPerPixel,pf->depth, |
| ((pf->bitsPerPixel == 8) ? "" |
| : (pf->bigEndian ? ", big endian" : ", little endian"))); |
| if (pf->trueColour) { |
| rfbLog(" true colour: max r %d g %d b %d, shift r %d g %d b %d\n", |
| pf->redMax, pf->greenMax, pf->blueMax, |
| pf->redShift, pf->greenShift, pf->blueShift); |
| } else { |
| rfbLog(" uses a colour map (not true colour).\n"); |
| } |
| } |
| } |