1 /* 2 * translate.c - translate between different pixel formats 3 */ 4 5 /* 6 * OSXvnc Copyright (C) 2001 Dan McGuirk <mcguirk (at) incompleteness.net>. 7 * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge. 8 * All Rights Reserved. 9 * 10 * This is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation; either version 2 of the License, or 13 * (at your option) any later version. 14 * 15 * This software is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this software; if not, write to the Free Software 22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 23 * USA. 24 */ 25 26 #include <rfb/rfb.h> 27 #include <rfb/rfbregion.h> 28 29 static void PrintPixelFormat(rfbPixelFormat *pf); 30 static rfbBool rfbSetClientColourMapBGR233(rfbClientPtr cl); 31 32 rfbBool rfbEconomicTranslate = FALSE; 33 34 /* 35 * Some standard pixel formats. 36 */ 37 38 static const rfbPixelFormat BGR233Format = { 39 8, 8, 0, 1, 7, 7, 3, 0, 3, 6, 0, 0 40 }; 41 42 43 /* 44 * Macro to compare pixel formats. 45 */ 46 47 #define PF_EQ(x,y) \ 48 ((x.bitsPerPixel == y.bitsPerPixel) && \ 49 (x.depth == y.depth) && \ 50 ((x.bigEndian == y.bigEndian) || (x.bitsPerPixel == 8)) && \ 51 (x.trueColour == y.trueColour) && \ 52 (!x.trueColour || ((x.redMax == y.redMax) && \ 53 (x.greenMax == y.greenMax) && \ 54 (x.blueMax == y.blueMax) && \ 55 (x.redShift == y.redShift) && \ 56 (x.greenShift == y.greenShift) && \ 57 (x.blueShift == y.blueShift)))) 58 59 #define CONCAT2(a,b) a##b 60 #define CONCAT2E(a,b) CONCAT2(a,b) 61 #define CONCAT3(a,b,c) a##b##c 62 #define CONCAT3E(a,b,c) CONCAT3(a,b,c) 63 #define CONCAT4(a,b,c,d) a##b##c##d 64 #define CONCAT4E(a,b,c,d) CONCAT4(a,b,c,d) 65 66 #undef OUT 67 #undef IN 68 69 #define OUT 8 70 #include "tableinitcmtemplate.c" 71 #include "tableinittctemplate.c" 72 #define IN 8 73 #include "tabletranstemplate.c" 74 #undef IN 75 #define IN 16 76 #include "tabletranstemplate.c" 77 #undef IN 78 #define IN 32 79 #include "tabletranstemplate.c" 80 #undef IN 81 #undef OUT 82 83 #define OUT 16 84 #include "tableinitcmtemplate.c" 85 #include "tableinittctemplate.c" 86 #define IN 8 87 #include "tabletranstemplate.c" 88 #undef IN 89 #define IN 16 90 #include "tabletranstemplate.c" 91 #undef IN 92 #define IN 32 93 #include "tabletranstemplate.c" 94 #undef IN 95 #undef OUT 96 97 #define OUT 32 98 #include "tableinitcmtemplate.c" 99 #include "tableinittctemplate.c" 100 #define IN 8 101 #include "tabletranstemplate.c" 102 #undef IN 103 #define IN 16 104 #include "tabletranstemplate.c" 105 #undef IN 106 #define IN 32 107 #include "tabletranstemplate.c" 108 #undef IN 109 #undef OUT 110 111 #ifdef LIBVNCSERVER_ALLOW24BPP 112 #define COUNT_OFFSETS 4 113 #define BPP2OFFSET(bpp) ((bpp)/8-1) 114 #include "tableinit24.c" 115 #define BPP 8 116 #include "tabletrans24template.c" 117 #undef BPP 118 #define BPP 16 119 #include "tabletrans24template.c" 120 #undef BPP 121 #define BPP 24 122 #include "tabletrans24template.c" 123 #undef BPP 124 #define BPP 32 125 #include "tabletrans24template.c" 126 #undef BPP 127 #else 128 #define COUNT_OFFSETS 3 129 #define BPP2OFFSET(bpp) ((int)(bpp)/16) 130 #endif 131 132 typedef void (*rfbInitCMTableFnType)(char **table, rfbPixelFormat *in, 133 rfbPixelFormat *out,rfbColourMap* cm); 134 typedef void (*rfbInitTableFnType)(char **table, rfbPixelFormat *in, 135 rfbPixelFormat *out); 136 137 static rfbInitCMTableFnType rfbInitColourMapSingleTableFns[COUNT_OFFSETS] = { 138 rfbInitColourMapSingleTable8, 139 rfbInitColourMapSingleTable16, 140 #ifdef LIBVNCSERVER_ALLOW24BPP 141 rfbInitColourMapSingleTable24, 142 #endif 143 rfbInitColourMapSingleTable32 144 }; 145 146 static rfbInitTableFnType rfbInitTrueColourSingleTableFns[COUNT_OFFSETS] = { 147 rfbInitTrueColourSingleTable8, 148 rfbInitTrueColourSingleTable16, 149 #ifdef LIBVNCSERVER_ALLOW24BPP 150 rfbInitTrueColourSingleTable24, 151 #endif 152 rfbInitTrueColourSingleTable32 153 }; 154 155 static rfbInitTableFnType rfbInitTrueColourRGBTablesFns[COUNT_OFFSETS] = { 156 rfbInitTrueColourRGBTables8, 157 rfbInitTrueColourRGBTables16, 158 #ifdef LIBVNCSERVER_ALLOW24BPP 159 rfbInitTrueColourRGBTables24, 160 #endif 161 rfbInitTrueColourRGBTables32 162 }; 163 164 static rfbTranslateFnType rfbTranslateWithSingleTableFns[COUNT_OFFSETS][COUNT_OFFSETS] = { 165 { rfbTranslateWithSingleTable8to8, 166 rfbTranslateWithSingleTable8to16, 167 #ifdef LIBVNCSERVER_ALLOW24BPP 168 rfbTranslateWithSingleTable8to24, 169 #endif 170 rfbTranslateWithSingleTable8to32 }, 171 { rfbTranslateWithSingleTable16to8, 172 rfbTranslateWithSingleTable16to16, 173 #ifdef LIBVNCSERVER_ALLOW24BPP 174 rfbTranslateWithSingleTable16to24, 175 #endif 176 rfbTranslateWithSingleTable16to32 }, 177 #ifdef LIBVNCSERVER_ALLOW24BPP 178 { rfbTranslateWithSingleTable24to8, 179 rfbTranslateWithSingleTable24to16, 180 rfbTranslateWithSingleTable24to24, 181 rfbTranslateWithSingleTable24to32 }, 182 #endif 183 { rfbTranslateWithSingleTable32to8, 184 rfbTranslateWithSingleTable32to16, 185 #ifdef LIBVNCSERVER_ALLOW24BPP 186 rfbTranslateWithSingleTable32to24, 187 #endif 188 rfbTranslateWithSingleTable32to32 } 189 }; 190 191 static rfbTranslateFnType rfbTranslateWithRGBTablesFns[COUNT_OFFSETS][COUNT_OFFSETS] = { 192 { rfbTranslateWithRGBTables8to8, 193 rfbTranslateWithRGBTables8to16, 194 #ifdef LIBVNCSERVER_ALLOW24BPP 195 rfbTranslateWithRGBTables8to24, 196 #endif 197 rfbTranslateWithRGBTables8to32 }, 198 { rfbTranslateWithRGBTables16to8, 199 rfbTranslateWithRGBTables16to16, 200 #ifdef LIBVNCSERVER_ALLOW24BPP 201 rfbTranslateWithRGBTables16to24, 202 #endif 203 rfbTranslateWithRGBTables16to32 }, 204 #ifdef LIBVNCSERVER_ALLOW24BPP 205 { rfbTranslateWithRGBTables24to8, 206 rfbTranslateWithRGBTables24to16, 207 rfbTranslateWithRGBTables24to24, 208 rfbTranslateWithRGBTables24to32 }, 209 #endif 210 { rfbTranslateWithRGBTables32to8, 211 rfbTranslateWithRGBTables32to16, 212 #ifdef LIBVNCSERVER_ALLOW24BPP 213 rfbTranslateWithRGBTables32to24, 214 #endif 215 rfbTranslateWithRGBTables32to32 } 216 }; 217 218 219 220 /* 221 * rfbTranslateNone is used when no translation is required. 222 */ 223 224 void 225 rfbTranslateNone(char *table, rfbPixelFormat *in, rfbPixelFormat *out, 226 char *iptr, char *optr, int bytesBetweenInputLines, 227 int width, int height) 228 { 229 int bytesPerOutputLine = width * (out->bitsPerPixel / 8); 230 231 while (height > 0) { 232 memcpy(optr, iptr, bytesPerOutputLine); 233 iptr += bytesBetweenInputLines; 234 optr += bytesPerOutputLine; 235 height--; 236 } 237 } 238 239 240 /* 241 * rfbSetTranslateFunction sets the translation function. 242 */ 243 244 rfbBool 245 rfbSetTranslateFunction(rfbClientPtr cl) 246 { 247 rfbLog("Pixel format for client %s:\n",cl->host); 248 PrintPixelFormat(&cl->format); 249 250 /* 251 * Check that bits per pixel values are valid 252 */ 253 254 if ((cl->screen->serverFormat.bitsPerPixel != 8) && 255 (cl->screen->serverFormat.bitsPerPixel != 16) && 256 #ifdef LIBVNCSERVER_ALLOW24BPP 257 (cl->screen->serverFormat.bitsPerPixel != 24) && 258 #endif 259 (cl->screen->serverFormat.bitsPerPixel != 32)) 260 { 261 rfbErr("%s: server bits per pixel not 8, 16 or 32 (is %d)\n", 262 "rfbSetTranslateFunction", 263 cl->screen->serverFormat.bitsPerPixel); 264 rfbCloseClient(cl); 265 return FALSE; 266 } 267 268 if ((cl->format.bitsPerPixel != 8) && 269 (cl->format.bitsPerPixel != 16) && 270 #ifdef LIBVNCSERVER_ALLOW24BPP 271 (cl->format.bitsPerPixel != 24) && 272 #endif 273 (cl->format.bitsPerPixel != 32)) 274 { 275 rfbErr("%s: client bits per pixel not 8, 16 or 32\n", 276 "rfbSetTranslateFunction"); 277 rfbCloseClient(cl); 278 return FALSE; 279 } 280 281 if (!cl->format.trueColour && (cl->format.bitsPerPixel != 8)) { 282 rfbErr("rfbSetTranslateFunction: client has colour map " 283 "but %d-bit - can only cope with 8-bit colour maps\n", 284 cl->format.bitsPerPixel); 285 rfbCloseClient(cl); 286 return FALSE; 287 } 288 289 /* 290 * bpp is valid, now work out how to translate 291 */ 292 293 if (!cl->format.trueColour) { 294 /* 295 * truecolour -> colour map 296 * 297 * Set client's colour map to BGR233, then effectively it's 298 * truecolour as well 299 */ 300 301 if (!rfbSetClientColourMapBGR233(cl)) 302 return FALSE; 303 304 cl->format = BGR233Format; 305 } 306 307 /* truecolour -> truecolour */ 308 309 if (PF_EQ(cl->format,cl->screen->serverFormat)) { 310 311 /* client & server the same */ 312 313 rfbLog("no translation needed\n"); 314 cl->translateFn = rfbTranslateNone; 315 return TRUE; 316 } 317 318 if ((cl->screen->serverFormat.bitsPerPixel < 16) || 319 ((!cl->screen->serverFormat.trueColour || !rfbEconomicTranslate) && 320 (cl->screen->serverFormat.bitsPerPixel == 16))) { 321 322 /* we can use a single lookup table for <= 16 bpp */ 323 324 cl->translateFn = rfbTranslateWithSingleTableFns 325 [BPP2OFFSET(cl->screen->serverFormat.bitsPerPixel)] 326 [BPP2OFFSET(cl->format.bitsPerPixel)]; 327 328 if(cl->screen->serverFormat.trueColour) 329 (*rfbInitTrueColourSingleTableFns 330 [BPP2OFFSET(cl->format.bitsPerPixel)]) (&cl->translateLookupTable, 331 &(cl->screen->serverFormat), &cl->format); 332 else 333 (*rfbInitColourMapSingleTableFns 334 [BPP2OFFSET(cl->format.bitsPerPixel)]) (&cl->translateLookupTable, 335 &(cl->screen->serverFormat), &cl->format,&cl->screen->colourMap); 336 337 } else { 338 339 /* otherwise we use three separate tables for red, green and blue */ 340 341 cl->translateFn = rfbTranslateWithRGBTablesFns 342 [BPP2OFFSET(cl->screen->serverFormat.bitsPerPixel)] 343 [BPP2OFFSET(cl->format.bitsPerPixel)]; 344 345 (*rfbInitTrueColourRGBTablesFns 346 [BPP2OFFSET(cl->format.bitsPerPixel)]) (&cl->translateLookupTable, 347 &(cl->screen->serverFormat), &cl->format); 348 } 349 350 return TRUE; 351 } 352 353 354 355 /* 356 * rfbSetClientColourMapBGR233 sets the client's colour map so that it's 357 * just like an 8-bit BGR233 true colour client. 358 */ 359 360 static rfbBool 361 rfbSetClientColourMapBGR233(rfbClientPtr cl) 362 { 363 char buf[sz_rfbSetColourMapEntriesMsg + 256 * 3 * 2]; 364 rfbSetColourMapEntriesMsg *scme = (rfbSetColourMapEntriesMsg *)buf; 365 uint16_t *rgb = (uint16_t *)(&buf[sz_rfbSetColourMapEntriesMsg]); 366 int i, len; 367 int r, g, b; 368 369 if (cl->format.bitsPerPixel != 8 ) { 370 rfbErr("%s: client not 8 bits per pixel\n", 371 "rfbSetClientColourMapBGR233"); 372 rfbCloseClient(cl); 373 return FALSE; 374 } 375 376 scme->type = rfbSetColourMapEntries; 377 378 scme->firstColour = Swap16IfLE(0); 379 scme->nColours = Swap16IfLE(256); 380 381 len = sz_rfbSetColourMapEntriesMsg; 382 383 i = 0; 384 385 for (b = 0; b < 4; b++) { 386 for (g = 0; g < 8; g++) { 387 for (r = 0; r < 8; r++) { 388 rgb[i++] = Swap16IfLE(r * 65535 / 7); 389 rgb[i++] = Swap16IfLE(g * 65535 / 7); 390 rgb[i++] = Swap16IfLE(b * 65535 / 3); 391 } 392 } 393 } 394 395 len += 256 * 3 * 2; 396 397 if (rfbWriteExact(cl, buf, len) < 0) { 398 rfbLogPerror("rfbSetClientColourMapBGR233: write"); 399 rfbCloseClient(cl); 400 return FALSE; 401 } 402 return TRUE; 403 } 404 405 /* this function is not called very often, so it needn't be 406 efficient. */ 407 408 /* 409 * rfbSetClientColourMap is called to set the client's colour map. If the 410 * client is a true colour client, we simply update our own translation table 411 * and mark the whole screen as having been modified. 412 */ 413 414 rfbBool 415 rfbSetClientColourMap(rfbClientPtr cl, int firstColour, int nColours) 416 { 417 if (cl->screen->serverFormat.trueColour || !cl->readyForSetColourMapEntries) { 418 return TRUE; 419 } 420 421 if (nColours == 0) { 422 nColours = cl->screen->colourMap.count; 423 } 424 425 if (cl->format.trueColour) { 426 LOCK(cl->updateMutex); 427 (*rfbInitColourMapSingleTableFns 428 [BPP2OFFSET(cl->format.bitsPerPixel)]) (&cl->translateLookupTable, 429 &cl->screen->serverFormat, &cl->format,&cl->screen->colourMap); 430 431 sraRgnDestroy(cl->modifiedRegion); 432 cl->modifiedRegion = 433 sraRgnCreateRect(0,0,cl->screen->width,cl->screen->height); 434 UNLOCK(cl->updateMutex); 435 436 return TRUE; 437 } 438 439 return rfbSendSetColourMapEntries(cl, firstColour, nColours); 440 } 441 442 443 /* 444 * rfbSetClientColourMaps sets the colour map for each RFB client. 445 */ 446 447 void 448 rfbSetClientColourMaps(rfbScreenInfoPtr rfbScreen, int firstColour, int nColours) 449 { 450 rfbClientIteratorPtr i; 451 rfbClientPtr cl; 452 453 i = rfbGetClientIterator(rfbScreen); 454 while((cl = rfbClientIteratorNext(i))) 455 rfbSetClientColourMap(cl, firstColour, nColours); 456 rfbReleaseClientIterator(i); 457 } 458 459 static void 460 PrintPixelFormat(rfbPixelFormat *pf) 461 { 462 if (pf->bitsPerPixel == 1) { 463 rfbLog(" 1 bpp, %s sig bit in each byte is leftmost on the screen.\n", 464 (pf->bigEndian ? "most" : "least")); 465 } else { 466 rfbLog(" %d bpp, depth %d%s\n",pf->bitsPerPixel,pf->depth, 467 ((pf->bitsPerPixel == 8) ? "" 468 : (pf->bigEndian ? ", big endian" : ", little endian"))); 469 if (pf->trueColour) { 470 rfbLog(" true colour: max r %d g %d b %d, shift r %d g %d b %d\n", 471 pf->redMax, pf->greenMax, pf->blueMax, 472 pf->redShift, pf->greenShift, pf->blueShift); 473 } else { 474 rfbLog(" uses a colour map (not true colour).\n"); 475 } 476 } 477 } 478