Bug 970691 - Part2: Restore digit stamping function to YuvStamper. r=jesup
☠☠ backed out by d8014e46546e ☠ ☠
authorPaul Kerr [:pkerr] <paulrkerr@gmail.com>
Wed, 23 Apr 2014 10:03:18 -0700
changeset 180230 83f7aec5a083650f8cba8701c577cfb6502fcfea
parent 180229 94348d189ed5b93f8494f01467d18ca55262a9f3
child 180231 972268fe6a0dc06f3e09d29ba61a5370e6ed7577
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
reviewersjesup
bugs970691
milestone31.0a1
Bug 970691 - Part2: Restore digit stamping function to YuvStamper. r=jesup Refactor digit writing method to use the new internals. Allows digit string to wrap through multiple lines in a small frame.
media/webrtc/signaling/src/media-conduit/YuvStamper.cpp
media/webrtc/signaling/src/media-conduit/YuvStamper.h
--- a/media/webrtc/signaling/src/media-conduit/YuvStamper.cpp
+++ b/media/webrtc/signaling/src/media-conduit/YuvStamper.cpp
@@ -3,40 +3,255 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifdef HAVE_NETINET_IN_H
 #include <netinet/in.h>
 #elif defined XP_WIN
 #include <winsock2.h>
 #endif
 
+#include "nspr.h"
 #include "YuvStamper.h"
 
 typedef uint32_t UINT4; //Needed for r_crc32() call
 extern "C" {
 #include "r_crc32.h"
 }
 
 namespace mozilla {
 
+#define ON_5 0x20
+#define ON_4 0x10
+#define ON_3 0x08
+#define ON_2 0x04
+#define ON_1 0x02
+#define ON_0 0x01
+
+/*
+  0, 0, 1, 1, 0, 0,
+  0, 1, 0, 0, 1, 0,
+  1, 0, 0, 0, 0, 1,
+  1, 0, 0, 0, 0, 1,
+  1, 0, 0, 0, 0, 1,
+  0, 1, 0, 0, 1, 0,
+  0, 0, 1, 1, 0, 0
+*/
+static unsigned char DIGIT_0 [] =
+  { ON_3 | ON_2,
+    ON_4 | ON_1,
+    ON_5 | ON_0,
+    ON_5 | ON_0,
+    ON_5 | ON_0,
+    ON_4 | ON_1,
+    ON_3 | ON_2
+  };
+    
+/*
+  0, 0, 0, 1, 0, 0,
+  0, 0, 0, 1, 0, 0,
+  0, 0, 0, 1, 0, 0,
+  0, 0, 0, 1, 0, 0,
+  0, 0, 0, 1, 0, 0,
+  0, 0, 0, 1, 0, 0,
+  0, 0, 0, 1, 0, 0,
+*/
+static unsigned char DIGIT_1 [] =
+  { ON_2,
+    ON_2,
+    ON_2,
+    ON_2,
+    ON_2,
+    ON_2,
+    ON_2
+  };
+
+/*
+  1, 1, 1, 1, 1, 0,
+  0, 0, 0, 0, 0, 1,
+  0, 0, 0, 0, 0, 1,
+  0, 1, 1, 1, 1, 0,
+  1, 0, 0, 0, 0, 0,
+  1, 0, 0, 0, 0, 0,
+  0, 1, 1, 1, 1, 1,
+*/
+static unsigned char DIGIT_2 [] =
+  { ON_5 | ON_4 | ON_3 | ON_2 | ON_1,
+    ON_0,
+    ON_0,
+    ON_4 | ON_3 | ON_2 | ON_1,
+    ON_5,
+    ON_5,
+    ON_4 | ON_3 | ON_2 | ON_1 | ON_0,
+  };
+
+/*
+  1, 1, 1, 1, 1, 0,
+  0, 0, 0, 0, 0, 1,
+  0, 0, 0, 0, 0, 1,
+  0, 1, 1, 1, 1, 1,
+  0, 0, 0, 0, 0, 1,
+  0, 0, 0, 0, 0, 1,
+  1, 1, 1, 1, 1, 0,
+*/
+static unsigned char DIGIT_3 [] =
+  { ON_5 | ON_4 | ON_3 | ON_2 | ON_1,
+    ON_0,
+    ON_0,
+    ON_4 | ON_3 | ON_2 | ON_1 | ON_0,
+    ON_0,
+    ON_0,
+    ON_5 | ON_4 | ON_3 | ON_2 | ON_1,
+  };
+
+/*
+  0, 1, 0, 0, 0, 1,
+  0, 1, 0, 0, 0, 1,
+  0, 1, 0, 0, 0, 1,
+  0, 1, 1, 1, 1, 1,
+  0, 0, 0, 0, 0, 1,
+  0, 0, 0, 0, 0, 1,
+  0, 0, 0, 0, 0, 1
+*/
+static unsigned char DIGIT_4 [] =
+  { ON_4 | ON_0,
+    ON_4 | ON_0,
+    ON_4 | ON_0,
+    ON_4 | ON_3 | ON_2 | ON_1 | ON_0,
+    ON_0,
+    ON_0,
+    ON_0,
+  };
+
+/*
+  0, 1, 1, 1, 1, 1,
+  1, 0, 0, 0, 0, 0,
+  1, 0, 0, 0, 0, 0,
+  0, 1, 1, 1, 1, 0,
+  0, 0, 0, 0, 0, 1,
+  0, 0, 0, 0, 0, 1,
+  1, 1, 1, 1, 1, 0,
+*/
+static unsigned char DIGIT_5 [] =
+  { ON_4 | ON_3 | ON_2 | ON_1 | ON_0,
+    ON_5,
+    ON_5,
+    ON_4 | ON_3 | ON_2 | ON_1,
+    ON_0,
+    ON_0,
+    ON_5 | ON_4 | ON_3 | ON_2 | ON_1,
+  };
+
+/*
+  0, 1, 1, 1, 1, 1,
+  1, 0, 0, 0, 0, 0,
+  1, 0, 0, 0, 0, 0,
+  1, 1, 1, 1, 1, 0,
+  1, 0, 0, 0, 0, 1,
+  1, 0, 0, 0, 0, 1,
+  0, 1, 1, 1, 1, 0,
+*/
+static unsigned char DIGIT_6 [] =
+  { ON_4 | ON_3 | ON_2 | ON_1 | ON_0,
+    ON_5,
+    ON_5,
+    ON_4 | ON_3 | ON_2 | ON_1,
+    ON_5 | ON_0,
+    ON_5 | ON_0,
+    ON_4 | ON_3 | ON_2 | ON_1,
+  };
+
+/*
+  1, 1, 1, 1, 1, 1,
+  0, 0, 0, 0, 0, 1,
+  0, 0, 0, 0, 1, 0,
+  0, 0, 0, 1, 0, 0,
+  0, 0, 1, 0, 0, 0,
+  0, 1, 0, 0, 0, 0,
+  1, 0, 0, 0, 0, 0
+*/
+static unsigned char DIGIT_7 [] =
+  { ON_5 | ON_4 | ON_3 | ON_2 | ON_1 | ON_0,
+    ON_0,
+    ON_1,
+    ON_2,
+    ON_3,
+    ON_4,
+    ON_5
+  };
+
+/*
+  0, 1, 1, 1, 1, 1,
+  1, 0, 0, 0, 0, 1,
+  1, 0, 0, 0, 0, 1,
+  0, 1, 1, 1, 1, 0,
+  1, 0, 0, 0, 0, 1,
+  1, 0, 0, 0, 0, 1,
+  0, 1, 1, 1, 1, 0
+*/
+static unsigned char DIGIT_8 [] =
+  { ON_4 | ON_3 | ON_2 | ON_1,
+    ON_5 | ON_0,
+    ON_5 | ON_0,
+    ON_4 | ON_3 | ON_2 | ON_1,
+    ON_5 | ON_0,
+    ON_5 | ON_0,
+    ON_4 | ON_3 | ON_2 | ON_1,
+  };
+
+/*
+  0, 1, 1, 1, 1, 1,
+  1, 0, 0, 0, 0, 1,
+  1, 0, 0, 0, 0, 1,
+  0, 1, 1, 1, 1, 1,
+  0, 0, 0, 0, 0, 1,
+  0, 0, 0, 0, 0, 1,
+  0, 1, 1, 1, 1, 0
+*/
+static unsigned char DIGIT_9 [] =
+  { ON_4 | ON_3 | ON_2 | ON_1 | ON_0,
+    ON_5 | ON_0,
+    ON_5 | ON_0,
+    ON_4 | ON_3 | ON_2 | ON_1 | ON_0,
+    ON_0,
+    ON_0,
+    ON_4 | ON_3 | ON_2 | ON_1,
+  };
+
+static unsigned char *DIGITS[] = {
+    DIGIT_0,
+    DIGIT_1,
+    DIGIT_2,
+    DIGIT_3,
+    DIGIT_4,
+    DIGIT_5,
+    DIGIT_6,
+    DIGIT_7,
+    DIGIT_8,
+    DIGIT_9
+};
+
   YuvStamper::YuvStamper(uint8_t* pYData,
 			 uint32_t width,
 			 uint32_t height,
 			 uint32_t stride,
 			 uint32_t x,
-			 uint32_t y):
+			 uint32_t y,
+			 uint8_t symbol_width,
+			 uint8_t symbol_height):
     pYData(pYData), mStride(stride),
     mWidth(width), mHeight(height),
+    mSymbolWidth(symbol_width), mSymbolHeight(symbol_height),
     mCursor(x, y) {}
 
   bool YuvStamper::Encode(uint32_t width, uint32_t height, uint32_t stride,
 			  uint8_t* pYData, uint8_t* pMsg, size_t msg_len,
 			  uint32_t x, uint32_t y)
   {
-    YuvStamper stamper(pYData, width, height, stride, x, y);
+    YuvStamper stamper(pYData, width, height, stride,
+		       x, y, sBitSize, sBitSize);
 
     // Reserve space for a checksum.
     if (stamper.Capacity() < 8 * (msg_len + sizeof(uint32_t)))
     {
       return false;
     }
 
     bool ok = false;
@@ -59,17 +274,19 @@ namespace mozilla {
 
     return ok;
   }
 
   bool YuvStamper::Decode(uint32_t width, uint32_t height, uint32_t stride,
 			  uint8_t* pYData, uint8_t* pMsg, size_t msg_len,
 			  uint32_t x, uint32_t y)
   {
-    YuvStamper stamper(pYData, width, height, stride, x, y);
+    YuvStamper stamper(pYData, width, height, stride,
+		       x, y, sBitSize, sBitSize);
+
     uint8_t* ptr = pMsg;
     size_t len = msg_len;
     uint32_t crc, msg_crc;
     uint8_t* pCrc = reinterpret_cast<uint8_t*>(&crc);
 
     // Account for space reserved for the checksum
     if (stamper.Capacity() < 8 * (len + sizeof(uint32_t))) {
       return false;
@@ -90,29 +307,29 @@ namespace mozilla {
 
     r_crc32(reinterpret_cast<char*>(pMsg), (int)msg_len, &msg_crc);
     return crc == htonl(msg_crc);
   }
 
   inline uint32_t YuvStamper::Capacity()
   {
     // Enforce at least a symbol width and height offset from outer edges.
-    if (mCursor.y + sBitSize > mHeight) {
+    if (mCursor.y + mSymbolHeight > mHeight) {
       return 0;
     }
 
-    if (mCursor.x + sBitSize > mWidth && !AdvanceCursor()) {
+    if (mCursor.x + mSymbolWidth > mWidth && !AdvanceCursor()) {
       return 0;
     }
 
     // Normalize frame integral to sBitSize x sBitSize
-    uint32_t width = mWidth / sBitSize;
-    uint32_t height = mHeight / sBitSize;
-    uint32_t x = mCursor.x / sBitSize;
-    uint32_t y = mCursor.y / sBitSize;
+    uint32_t width = mWidth / mSymbolWidth;
+    uint32_t height = mHeight / mSymbolHeight;
+    uint32_t x = mCursor.x / mSymbolWidth;
+    uint32_t y = mCursor.y / mSymbolHeight;
 
     return (width * height - width * y)- x;
   }
 
   bool YuvStamper::Write8(uint8_t value)
   {
     // Encode MSB to LSB.
     uint8_t mask = 0x80;
@@ -124,45 +341,46 @@ namespace mozilla {
     }
     return true;
   }
 
   bool YuvStamper::WriteBit(bool one)
   {
     // A bit is mapped to a sBitSize x sBitSize square of luma data points.
     uint8_t value = one ? sYOn : sYOff;
-    for (uint32_t y = 0; y < sBitSize; y++) {
-      for (uint32_t x = 0; x < sBitSize; x++) {
+    for (uint32_t y = 0; y < mSymbolHeight; y++) {
+      for (uint32_t x = 0; x < mSymbolWidth; x++) {
 	*(pYData + (mCursor.x + x) + ((mCursor.y + y) * mStride)) = value;
       }
     }
 
     return AdvanceCursor();
   }
 
   bool YuvStamper::AdvanceCursor()
   {
-    mCursor.x += sBitSize;
-    if (mCursor.x + sBitSize > mWidth) {
+    mCursor.x += mSymbolWidth;
+    if (mCursor.x + mSymbolWidth > mWidth) {
       // move to the start of the next row if possible.
-      mCursor.y += sBitSize;
-      if (mCursor.y + sBitSize > mHeight) {
+      mCursor.y += mSymbolHeight;
+      if (mCursor.y + mSymbolHeight > mHeight) {
 	// end of frame, do not advance
-	mCursor.y -= sBitSize;
-	mCursor.x -= sBitSize;
+	mCursor.y -= mSymbolHeight;
+	mCursor.x -= mSymbolWidth;
 	return false;
       } else {
 	mCursor.x = 0;
       }
     }
 
     return true;
   }
 
-  bool YuvStamper::Read8(uint8_t &value) {
+  bool YuvStamper::Read8(uint8_t &value)
+  {
     uint8_t octet = 0;
     uint8_t bit = 0;
 
     for (int i = 8; i > 0; --i) {
       if (!ReadBit(bit)) {
 	return false;
       }
       octet <<= 1;
@@ -171,20 +389,70 @@ namespace mozilla {
 
     value = octet;
     return true;
   }
 
   bool YuvStamper::ReadBit(uint8_t &bit)
   {
     uint32_t sum = 0;
-    for (uint32_t y = 0; y < sBitSize; y++) {
-      for (uint32_t x = 0; x < sBitSize; x++) {
+    for (uint32_t y = 0; y < mSymbolHeight; y++) {
+      for (uint32_t x = 0; x < mSymbolWidth; x++) {
 	sum += *(pYData + mStride * (mCursor.y + y) + mCursor.x + x);
       }
     }
 
     // apply threshold to collected bit square
-    bit = (sum > (sBitThreshold * sBitSize * sBitSize)) ? 1 : 0;
+    bit = (sum > (sBitThreshold * mSymbolWidth * mSymbolHeight)) ? 1 : 0;
     return AdvanceCursor();
   }
 
+  bool YuvStamper::WriteDigits(uint32_t value)
+  {
+    char buf[20];
+    PR_snprintf(buf, sizeof(buf), "%.5u", value);
+    size_t size = strlen(buf);
+
+    if (Capacity() < size) {
+      return false;
+    }
+
+    for (size_t i=0; i < size; ++i) {
+      if (!WriteDigit(buf[i] - '0'))
+	return false;
+      if (!AdvanceCursor()) {
+	return false;
+      }
+    }
+
+    return true;
+  }
+
+  bool YuvStamper::WriteDigit(uint8_t digit) {
+    if (digit > sizeof(DIGITS)/sizeof(DIGITS[0]))
+      return false;
+
+    unsigned char *dig = DIGITS[digit];
+    for (uint32_t row = 0; row < sDigitHeight; ++row) {
+      unsigned char mask = 0x01 << (sDigitWidth - 1);
+      for (uint32_t col = 0; col < sDigitWidth; ++col, mask >>= 1) {
+	if (dig[row] & mask) {
+	  for (uint32_t xx=0; xx < sPixelSize; ++xx) {
+	    for (uint32_t yy=0; yy < sPixelSize; ++yy) {
+	      WritePixel(pYData,
+			 mCursor.x + (col * sPixelSize) + xx,
+			 mCursor.y + (row * sPixelSize) + yy);
+	    }
+	  }
+	}
+      }
+    }
+
+    return true;
+  }
+
+  void YuvStamper::WritePixel(uint8_t *data, uint32_t x, uint32_t y) {
+    uint8_t *ptr = &data[y * mStride + x];
+    *ptr = (*ptr > sLumaThreshold) ? sLumaMin : sLumaMax;
+    return;
+   }
+
 }  // Namespace mozilla.
--- a/media/webrtc/signaling/src/media-conduit/YuvStamper.h
+++ b/media/webrtc/signaling/src/media-conduit/YuvStamper.h
@@ -2,44 +2,73 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 namespace mozilla {
 
 class
 YuvStamper {
 public:
-  YuvStamper(uint8_t* pYData,
-	     uint32_t width, uint32_t height, uint32_t stride,
-	     uint32_t x = 0, uint32_t y = 0);
+  bool WriteDigits(uint32_t value);
+
+  template<typename T>
+  static bool Write(uint32_t width, uint32_t height, uint32_t stride,
+                    uint8_t *pYData, const T& value,
+                    uint32_t x=0, uint32_t y=0)
+  {
+    YuvStamper stamper(pYData, width, height, stride,
+		       x, y,
+		       (sDigitWidth + sInterDigit) * sPixelSize,
+		       (sDigitHeight + sInterLine) * sPixelSize);
+    return stamper.WriteDigits(value);
+  }
+
   static bool Encode(uint32_t width, uint32_t height, uint32_t stride,
 		     uint8_t* pYData, uint8_t* pMsg, size_t msg_len,
-		     uint32_t x, uint32_t y);
+		     uint32_t x = 0, uint32_t y = 0);
 
   static bool Decode(uint32_t width, uint32_t height, uint32_t stride,
 		     uint8_t* pYData, uint8_t* pMsg, size_t msg_len,
-		     uint32_t x, uint32_t y);
+		     uint32_t x = 0, uint32_t y = 0);
 
  private:
+  YuvStamper(uint8_t* pYData,
+	     uint32_t width, uint32_t height, uint32_t stride,
+	     uint32_t x, uint32_t y,
+	     uint8_t symbol_width, uint8_t symbol_height);
+
+  bool WriteDigit(uint8_t digit);
+  void WritePixel(uint8_t* data, uint32_t x, uint32_t y);
+
   uint32_t Capacity();
   bool AdvanceCursor();
   bool WriteBit(bool one);
   bool Write8(uint8_t value);
   bool ReadBit(uint8_t &value);
   bool Read8(uint8_t &bit);
 
+  const static uint8_t sPixelSize = 3;
+  const static uint8_t sDigitWidth = 6;
+  const static uint8_t sDigitHeight = 7;
+  const static uint8_t sInterDigit = 1;
+  const static uint8_t sInterLine = 1;
   const static uint32_t sBitSize = 4;
   const static uint32_t sBitThreshold = 60;
   const static uint8_t sYOn = 0x80;
   const static uint8_t sYOff = 0;
+  const static uint8_t sLumaThreshold = 96;
+  const static uint8_t sLumaMin = 16;
+  const static uint8_t sLumaMax = 235;
 
   uint8_t* pYData;
   uint32_t mStride;
   uint32_t mWidth;
   uint32_t mHeight;
+  uint8_t mSymbolWidth;
+  uint8_t mSymbolHeight;
 
   struct Cursor {
     Cursor(uint32_t x, uint32_t y):
       x(x), y(y) {}
     uint32_t x;
     uint32_t y;
   } mCursor;
 };