If the client and server are using identical pixel formats, then perform Tight decoding directly into the viewer's back buffer, rather than going through the slow fillRect/imageRect routines.


git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@4757 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/common/rfb/tightDecode.h b/common/rfb/tightDecode.h
index 96042cb..4ea18a9 100644
--- a/common/rfb/tightDecode.h
+++ b/common/rfb/tightDecode.h
@@ -1,5 +1,6 @@
 /* Copyright (C) 2000-2003 Constantin Kaplinsky.  All Rights Reserved.
  * Copyright 2004-2005 Cendio AB.
+ * Copyright (C) 2011 D. R. Commander.  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
@@ -42,32 +43,20 @@
 
 #define PIXEL_T rdr::CONCAT2E(U,BPP)
 #define READ_PIXEL CONCAT2E(readOpaque,BPP)
-#define TIGHT_DECODE CONCAT2E(tightDecode,BPP)
+#define TIGHT_DECODE TightDecoder::CONCAT2E(tightDecode,BPP)
+#define DECOMPRESS_JPEG_RECT TightDecoder::CONCAT2E(DecompressJpegRect,BPP)
+#define FILTER_GRADIENT TightDecoder::CONCAT2E(FilterGradient,BPP)
+#define DIRECT_FILL_RECT TightDecoder::CONCAT2E(directFillRect,BPP)
 
 #define TIGHT_MIN_TO_COMPRESS 12
-static bool DecompressJpegRect(const Rect& r, rdr::InStream* is,
-			       PIXEL_T* buf, CMsgHandler* handler);
-static void FilterGradient(const Rect& r, rdr::InStream* is, int dataSize,
-			   PIXEL_T* buf, CMsgHandler* handler);
-#if BPP == 32
-static void FilterGradient24(const Rect& r, rdr::InStream* is, int dataSize,
-			     PIXEL_T* buf, CMsgHandler* handler);
-#endif
 
 // Main function implementing Tight decoder
 
-void TIGHT_DECODE (const Rect& r, rdr::InStream* is,
-                   rdr::ZlibInStream zis[], PIXEL_T* buf
-#ifdef EXTRA_ARGS
-                      , EXTRA_ARGS
-#endif
-                      )
+void TIGHT_DECODE (const Rect& r)
 {
-  rdr::U8 *bytebuf = (rdr::U8*) buf;
   bool cutZeros = false;
-  const rfb::PixelFormat& myFormat = handler->cp.pf();
 #if BPP == 32
-  if (myFormat.is888()) {
+  if (serverpf.is888()) {
     cutZeros = true;
   } 
 #endif
@@ -86,18 +75,20 @@
   if (comp_ctl == rfbTightFill) {
     PIXEL_T pix;
     if (cutZeros) {
+      rdr::U8 bytebuf[3];
       is->readBytes(bytebuf, 3);
-      myFormat.bufferFromRGB((rdr::U8*)&pix, bytebuf, 1, NULL);
+      serverpf.bufferFromRGB((rdr::U8*)&pix, bytebuf, 1, NULL);
     } else {
       pix = is->READ_PIXEL();
     }
-    FILL_RECT(r, pix);
+    if (directDecode) DIRECT_FILL_RECT(r, pix);
+    else FILL_RECT(r, pix);
     return;
   }
 
   // "JPEG" compression type.
   if (comp_ctl == rfbTightJpeg) {
-    DecompressJpegRect(r, is, buf, handler);
+    DECOMPRESS_JPEG_RECT(r);
     return;
   }
 
@@ -122,11 +113,11 @@
         rdr::U8 elem[3];
         for (int i = 0;i < palSize;i++) {
           is->readBytes(elem, 3);
-          myFormat.bufferFromRGB((rdr::U8*)&palette[i], elem, 1, NULL);
+          serverpf.bufferFromRGB((rdr::U8*)&palette[i], elem, 1, NULL);
         }
       } else {
-	for (int i = 0; i < palSize; i++)
-	  palette[i] = is->READ_PIXEL();
+        for (int i = 0; i < palSize; i++)
+          palette[i] = is->READ_PIXEL();
       }
       break;
     case rfbTightFilterGradient: 
@@ -161,78 +152,93 @@
     input = &zis[streamId];
   }
 
+  PIXEL_T *buf;
+  int stride = r.width();
+  if (directDecode) buf = (PIXEL_T *)handler->getRawPixelsRW(r, &stride);
+  else buf = (PIXEL_T *)reader->getImageBuf(r.area());
+
   if (palSize == 0) {
     // Truecolor data
     if (useGradient) {
 #if BPP == 32
       if (cutZeros) {
-	FilterGradient24(r, input, dataSize, buf, handler);
+        FilterGradient24(input, buf, stride, r, dataSize);
       } else 
 #endif
-	{
-	  FilterGradient(r, input, dataSize, buf, handler);
-	}
+      {
+        FILTER_GRADIENT(input, buf, stride, r, dataSize);
+      }
     } else {
+      // Copy
+      int h = r.height();
+      PIXEL_T *ptr = buf;
       if (cutZeros) {
+        int w = r.width(), pad = stride - w;
         rdr::U8 elem[3];
-        for (int i = 0;i < r.area();i++) {
-          input->readBytes(elem, 3);
-          myFormat.bufferFromRGB((rdr::U8*)&buf[i], elem, 1, NULL);
+        while (h > 0) {
+          PIXEL_T *endOfRow = ptr + w;
+          while (ptr < endOfRow) {
+            input->readBytes(elem, 3);
+            serverpf.bufferFromRGB((rdr::U8*)ptr++, elem, 1, NULL);
+          }
+          ptr += pad;
+          h--;
         }
       } else {
-        input->readBytes(buf, dataSize); 
+        while (h > 0) {
+          input->readBytes(ptr, rowSize);
+          ptr += stride;
+          h--;
+        }
       }
     }
   } else {
-    int x, y, b;
+    // Indexed color
+    int x, h = r.height(), w = r.width(), b, pad = stride - w;
     PIXEL_T *ptr = buf;
     rdr::U8 bits;
     if (palSize <= 2) {
       // 2-color palette
-      for (y = 0; y < r.height(); y++) {
-        for (x = 0; x < r.width() / 8; x++) {
+      while (h > 0) {
+        for (x = 0; x < w / 8; x++) {
           bits = input->readU8();
           for (b = 7; b >= 0; b--) {
             *ptr++ = palette[bits >> b & 1];
-	  }
+          }
         }
-        if (r.width() % 8 != 0) {
+        if (w % 8 != 0) {
           bits = input->readU8();
-          for (b = 7; b >= 8 - r.width() % 8; b--) {
+          for (b = 7; b >= 8 - w % 8; b--) {
             *ptr++ = palette[bits >> b & 1];
           }
         }
+        ptr += pad;
+        h--;
       }
     } else {
       // 256-color palette
-      for (y = 0; y < r.height(); y++) {
-        for (x = 0; x < r.width(); x++) {
+      while (h > 0) {
+        PIXEL_T *endOfRow = ptr + w;
+        while (ptr < endOfRow) {
           *ptr++ = palette[input->readU8()];
-	}
+        }
+        ptr += pad;
+        h--;
       }
     }
   }
 
-  IMAGE_RECT(r, buf);
+  if (directDecode) handler->releaseRawPixels(r);
+  else IMAGE_RECT(r, buf);
 
   if (streamId != -1) {
     zis[streamId].reset();
   }
 }
 
-static bool
-DecompressJpegRect(const Rect& r, rdr::InStream* is,
-		   PIXEL_T* buf, CMsgHandler* handler)
+void
+DECOMPRESS_JPEG_RECT(const Rect& r)
 {
-  struct jpeg_decompress_struct cinfo;
-  struct jpeg_error_mgr jerr;
-  int w = r.width();
-  int h = r.height();
-  int pixelsize;
-  rdr::U8 *dstBuf = NULL;
-  bool dstBufIsTemp = false;
-  const rfb::PixelFormat& pf = handler->cp.pf();
-
   // Read length
   int compressedLen = is->readCompactLength();
   if (compressedLen <= 0) {
@@ -246,97 +252,21 @@
   }
   is->readBytes(netbuf, compressedLen);
 
-  // Set up JPEG decompression
-  cinfo.err = jpeg_std_error(&jerr);
-  jpeg_create_decompress(&cinfo);
-  JpegSetSrcManager(&cinfo, (char*)netbuf, compressedLen);
-  jpeg_read_header(&cinfo, TRUE);
+  // We always use direct decoding with JPEG images
+  int stride;
+  rdr::U8 *buf = handler->getRawPixelsRW(r, &stride);
+  jd.decompress(netbuf, compressedLen, buf, stride * clientpf.bpp / 8, r,
+                clientpf);
+  handler->releaseRawPixels(r);
 
-  cinfo.out_color_space = JCS_RGB;
-  pixelsize = 3;
-
-#ifdef JCS_EXTENSIONS
-  // Try to have libjpeg output directly to our native format
-  if (pf.is888()) {
-    int redShift, greenShift, blueShift;
-
-    if(pf.bigEndian) {
-      redShift = 24 - pf.redShift;
-      greenShift = 24 - pf.greenShift;
-      blueShift = 24 - pf.blueShift;
-    } else {
-      redShift = pf.redShift;
-      greenShift = pf.greenShift;
-      blueShift = pf.blueShift;
-    }
-
-    // libjpeg can only handle some "standard" formats
-    if(redShift == 0 && greenShift == 8 && blueShift == 16)
-      cinfo.out_color_space = JCS_EXT_RGBX;
-    if(redShift == 16 && greenShift == 8 && blueShift == 0)
-      cinfo.out_color_space = JCS_EXT_BGRX;
-    if(redShift == 24 && greenShift == 16 && blueShift == 8)
-      cinfo.out_color_space = JCS_EXT_XBGR;
-    if(redShift == 8 && greenShift == 16 && blueShift == 24)
-      cinfo.out_color_space = JCS_EXT_XRGB;
-
-    if (cinfo.out_color_space != JCS_RGB) {
-      dstBuf = (rdr::U8 *)buf;
-      pixelsize = 4;
-    }
-  }
-#endif
-
-  if (cinfo.out_color_space == JCS_RGB) {
-    dstBuf = new rdr::U8[w * h * pixelsize];
-    dstBufIsTemp = true;
-  }
-
-  JSAMPROW *rowPointer = new JSAMPROW[h];
-  for (int dy = 0; dy < h; dy++)
-    rowPointer[dy] = (JSAMPROW)(&dstBuf[dy * w * pixelsize]);
-
-  jpeg_start_decompress(&cinfo);
-  if (cinfo.output_width != (unsigned)r.width() || cinfo.output_height != (unsigned)r.height() ||
-      cinfo.output_components != pixelsize) {
-      jpeg_destroy_decompress(&cinfo);
-      throw Exception("Tight Encoding: Wrong JPEG data received.\n");
-  }
-
-  // Decompress
-  const rfb::PixelFormat& myFormat = handler->cp.pf();
-  while (cinfo.output_scanline < cinfo.output_height) {
-    jpeg_read_scanlines(&cinfo, &rowPointer[cinfo.output_scanline],
-			cinfo.output_height - cinfo.output_scanline);
-    if (jpegError) {
-      break;
-    }
-  }
-
-  delete [] rowPointer;
-
-  if (cinfo.out_color_space == JCS_RGB)
-    myFormat.bufferFromRGB((rdr::U8*)buf, dstBuf, w * h);
-
-  IMAGE_RECT(r, buf);
-
-  if (!jpegError) {
-    jpeg_finish_decompress(&cinfo);
-  }
-
-  jpeg_destroy_decompress(&cinfo);
-
-  if (dstBufIsTemp) delete [] dstBuf;
   delete [] netbuf;
-
-  return !jpegError;
 }
 
 #if BPP == 32
 
-static void
-FilterGradient24(const Rect& r, rdr::InStream* is, int dataSize,
-		 PIXEL_T* buf, CMsgHandler* handler)
+void
+TightDecoder::FilterGradient24(rdr::InStream* is, PIXEL_T* buf, int stride,
+                               const Rect& r, int dataSize)
 {
   int x, y, c;
   static rdr::U8 prevRow[TIGHT_MAX_WIDTH*3];
@@ -354,7 +284,6 @@
   is->readBytes(netbuf, dataSize);
 
   // Set up shortcut variables
-  const rfb::PixelFormat& myFormat = handler->cp.pf();
   int rectHeight = r.height();
   int rectWidth = r.width();
 
@@ -364,21 +293,21 @@
       pix[c] = netbuf[y*rectWidth*3+c] + prevRow[c];
       thisRow[c] = pix[c];
     }
-    myFormat.bufferFromRGB((rdr::U8*)&buf[y*rectWidth], pix, 1, NULL);
+    serverpf.bufferFromRGB((rdr::U8*)&buf[y*stride], pix, 1, NULL);
 
     /* Remaining pixels of a row */
     for (x = 1; x < rectWidth; x++) {
       for (c = 0; c < 3; c++) {
-	est[c] = prevRow[x*3+c] + pix[c] - prevRow[(x-1)*3+c];
-	if (est[c] > 0xff) {
-	  est[c] = 0xff;
-	} else if (est[c] < 0) {
-	  est[c] = 0;
-	}
-	pix[c] = netbuf[(y*rectWidth+x)*3+c] + est[c];
-	thisRow[x*3+c] = pix[c];
+        est[c] = prevRow[x*3+c] + pix[c] - prevRow[(x-1)*3+c];
+        if (est[c] > 0xff) {
+          est[c] = 0xff;
+        } else if (est[c] < 0) {
+          est[c] = 0;
+        }
+        pix[c] = netbuf[(y*rectWidth+x)*3+c] + est[c];
+        thisRow[x*3+c] = pix[c];
       }
-      myFormat.bufferFromRGB((rdr::U8*)&buf[y*rectWidth+x], pix, 1, NULL);
+      serverpf.bufferFromRGB((rdr::U8*)&buf[y*stride+x], pix, 1, NULL);
     }
 
     memcpy(prevRow, thisRow, sizeof(prevRow));
@@ -389,9 +318,9 @@
 
 #endif
 
-static void
-FilterGradient(const Rect& r, rdr::InStream* is, int dataSize,
-	       PIXEL_T* buf, CMsgHandler* handler)
+void
+FILTER_GRADIENT(rdr::InStream* is, PIXEL_T* buf, int stride, const Rect& r,
+                int dataSize)
 {
   int x, y, c;
   static rdr::U8 prevRow[TIGHT_MAX_WIDTH*sizeof(PIXEL_T)];
@@ -409,19 +338,18 @@
   is->readBytes(netbuf, dataSize);
 
   // Set up shortcut variables
-  const rfb::PixelFormat& myFormat = handler->cp.pf();
   int rectHeight = r.height();
   int rectWidth = r.width();
 
   for (y = 0; y < rectHeight; y++) {
     /* First pixel in a row */
-    myFormat.rgbFromBuffer(pix, (rdr::U8*)&netbuf[y*rectWidth], 1, NULL);
+    serverpf.rgbFromBuffer(pix, (rdr::U8*)&netbuf[y*rectWidth], 1, NULL);
     for (c = 0; c < 3; c++)
       pix[c] += prevRow[c];
 
     memcpy(thisRow, pix, sizeof(pix));
 
-    myFormat.bufferFromRGB((rdr::U8*)&buf[y*rectWidth], pix, 1, NULL);
+    serverpf.bufferFromRGB((rdr::U8*)&buf[y*stride], pix, 1, NULL);
 
     /* Remaining pixels of a row */
     for (x = 1; x < rectWidth; x++) {
@@ -434,13 +362,13 @@
         }
       }
 
-      myFormat.rgbFromBuffer(pix, (rdr::U8*)&netbuf[y*rectWidth+x], 1, NULL);
+      serverpf.rgbFromBuffer(pix, (rdr::U8*)&netbuf[y*rectWidth+x], 1, NULL);
       for (c = 0; c < 3; c++)
         pix[c] += est[c];
 
       memcpy(&thisRow[x*3], pix, sizeof(pix));
 
-      myFormat.bufferFromRGB((rdr::U8*)&buf[y*rectWidth+x], pix, 1, NULL);
+      serverpf.bufferFromRGB((rdr::U8*)&buf[y*stride+x], pix, 1, NULL);
     }
 
     memcpy(prevRow, thisRow, sizeof(prevRow));
@@ -449,7 +377,39 @@
   delete [] netbuf;
 }
 
+void
+DIRECT_FILL_RECT(const Rect& r, Pixel pix) {
+
+  int stride;
+  PIXEL_T *buf = (PIXEL_T *)handler->getRawPixelsRW(r, &stride);
+
+  int w = r.width(), h = r.height();
+  PIXEL_T *ptr = buf;
+#if BPP != 8
+  int pad = stride - w;
+#endif
+
+  while (h > 0) {
+#if BPP == 8
+    memset(ptr, pix, w);
+    ptr += stride;
+#else
+    PIXEL_T *endOfRow = ptr + w;
+    while (ptr < endOfRow) {
+      *ptr++ = pix;
+    }
+    ptr += pad;
+#endif
+    h--;
+  }
+
+  handler->releaseRawPixels(r);
+}
+
 #undef TIGHT_MIN_TO_COMPRESS
+#undef DIRECT_FILL_RECT
+#undef FILTER_GRADIENT
+#undef DECOMPRESS_JPEG_RECT
 #undef TIGHT_DECODE
 #undef READ_PIXEL
 #undef PIXEL_T