Allow conditional dependencies between rects when decoding

Some encodings only cause dependencies between rects some of the
time. Make sure we can allow parallel decoding of those rect that
aren't dependent on each other.
diff --git a/common/rfb/DecodeManager.cxx b/common/rfb/DecodeManager.cxx
index 3c1ddb4..ba2b2d2 100644
--- a/common/rfb/DecodeManager.cxx
+++ b/common/rfb/DecodeManager.cxx
@@ -268,6 +268,23 @@
       }
     }
 
+    // For a partially ordered decoder we must ask the decoder for each
+    // pair of rectangles.
+    if (entry->decoder->flags & DecoderPartiallyOrdered) {
+      for (iter2 = manager->workQueue.begin(); iter2 != iter; ++iter2) {
+        if (entry->encoding != (*iter2)->encoding)
+          continue;
+        if (entry->decoder->doRectsConflict(entry->rect,
+                                            entry->bufferStream->data(),
+                                            entry->bufferStream->length(),
+                                            (*iter2)->rect,
+                                            (*iter2)->bufferStream->data(),
+                                            (*iter2)->bufferStream->length(),
+                                            *entry->cp))
+          goto next;
+      }
+    }
+
     // Check overlap with earlier rectangles
     if (!lockedRegion.intersect(entry->affectedRegion).is_empty())
       goto next;
diff --git a/common/rfb/Decoder.cxx b/common/rfb/Decoder.cxx
index df785eb..370e1f9 100644
--- a/common/rfb/Decoder.cxx
+++ b/common/rfb/Decoder.cxx
@@ -44,6 +44,14 @@
   region->reset(rect);
 }
 
+bool Decoder::doRectsConflict(const Rect& rectA, const void* bufferA,
+                              size_t buflenA, const Rect& rectB,
+                              const void* bufferB, size_t buflenB,
+                              const ConnParams& cp)
+{
+  return false;
+}
+
 bool Decoder::supported(int encoding)
 {
   switch (encoding) {
diff --git a/common/rfb/Decoder.h b/common/rfb/Decoder.h
index 9a03eb3..3840b3f 100644
--- a/common/rfb/Decoder.h
+++ b/common/rfb/Decoder.h
@@ -35,6 +35,9 @@
     DecoderPlain = 0,
     // All rects for this decoder must be handled in order
     DecoderOrdered = 1 << 0,
+    // Only some of the rects must be handled in order,
+    // see doesRectsConflict()
+    DecoderPartiallyOrdered = 1 << 1,
   };
 
   class Decoder {
@@ -62,6 +65,17 @@
                                    size_t buflen, const ConnParams& cp,
                                    Region* region);
 
+    // doesRectsConflict() determines if two rectangles must be decoded
+    // in the order they were received. This will only be called if the
+    // DecoderPartiallyOrdered flag has been set.
+    virtual bool doRectsConflict(const Rect& rectA,
+                                 const void* bufferA,
+                                 size_t buflenA,
+                                 const Rect& rectB,
+                                 const void* bufferB,
+                                 size_t buflenB,
+                                 const ConnParams& cp);
+
     // decodeRect() decodes the given rectangle with data from the
     // given buffer, onto the ModifiablePixelBuffer. The PixelFormat of
     // the PixelBuffer might not match the ConnParams and it is up to
diff --git a/common/rfb/TightDecoder.cxx b/common/rfb/TightDecoder.cxx
index ec329e4..86bb006 100644
--- a/common/rfb/TightDecoder.cxx
+++ b/common/rfb/TightDecoder.cxx
@@ -46,7 +46,7 @@
 #include <rfb/tightDecode.h>
 #undef BPP
 
-TightDecoder::TightDecoder() : Decoder(DecoderOrdered)
+TightDecoder::TightDecoder() : Decoder(DecoderPartiallyOrdered)
 {
 }
 
@@ -148,6 +148,39 @@
   }
 }
 
+bool TightDecoder::doRectsConflict(const Rect& rectA,
+                                   const void* bufferA,
+                                   size_t buflenA,
+                                   const Rect& rectB,
+                                   const void* bufferB,
+                                   size_t buflenB,
+                                   const ConnParams& cp)
+{
+  rdr::U8 comp_ctl_a, comp_ctl_b;
+
+  assert(buflenA >= 1);
+  assert(buflenB >= 1);
+
+  comp_ctl_a = *(const rdr::U8*)bufferA;
+  comp_ctl_b = *(const rdr::U8*)bufferB;
+
+  // Resets or use of zlib pose the same problem, so merge them
+  if ((comp_ctl_a & 0x80) == 0x00)
+    comp_ctl_a |= 1 << ((comp_ctl_a >> 4) & 0x03);
+  if ((comp_ctl_b & 0x80) == 0x00)
+    comp_ctl_b |= 1 << ((comp_ctl_b >> 4) & 0x03);
+
+  if (((comp_ctl_a & 0x0f) & (comp_ctl_b & 0x0f)) != 0)
+    return true;
+
+  // We have a shared JpegDecompressor, so one at a time
+  if (((comp_ctl_a >> 4) == tightJpeg) &&
+      ((comp_ctl_b >> 4) == tightJpeg))
+    return true;
+
+  return false;
+}
+
 void TightDecoder::decodeRect(const Rect& r, const void* buffer,
                               size_t buflen, const ConnParams& cp,
                               ModifiablePixelBuffer* pb)
diff --git a/common/rfb/TightDecoder.h b/common/rfb/TightDecoder.h
index 18497d4..a98788f 100644
--- a/common/rfb/TightDecoder.h
+++ b/common/rfb/TightDecoder.h
@@ -33,6 +33,13 @@
     virtual ~TightDecoder();
     virtual void readRect(const Rect& r, rdr::InStream* is,
                           const ConnParams& cp, rdr::OutStream* os);
+    virtual bool doRectsConflict(const Rect& rectA,
+                                 const void* bufferA,
+                                 size_t buflenA,
+                                 const Rect& rectB,
+                                 const void* bufferB,
+                                 size_t buflenB,
+                                 const ConnParams& cp);
     virtual void decodeRect(const Rect& r, const void* buffer,
                             size_t buflen, const ConnParams& cp,
                             ModifiablePixelBuffer* pb);