michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifdef HAVE_NETINET_IN_H michael@0: #include michael@0: #elif defined XP_WIN michael@0: #include michael@0: #endif michael@0: #include michael@0: michael@0: #include "nspr.h" michael@0: #include "YuvStamper.h" michael@0: michael@0: typedef uint32_t UINT4; //Needed for r_crc32() call michael@0: extern "C" { michael@0: #include "r_crc32.h" michael@0: } michael@0: michael@0: namespace mozilla { michael@0: michael@0: #define ON_5 0x20 michael@0: #define ON_4 0x10 michael@0: #define ON_3 0x08 michael@0: #define ON_2 0x04 michael@0: #define ON_1 0x02 michael@0: #define ON_0 0x01 michael@0: michael@0: /* michael@0: 0, 0, 1, 1, 0, 0, michael@0: 0, 1, 0, 0, 1, 0, michael@0: 1, 0, 0, 0, 0, 1, michael@0: 1, 0, 0, 0, 0, 1, michael@0: 1, 0, 0, 0, 0, 1, michael@0: 0, 1, 0, 0, 1, 0, michael@0: 0, 0, 1, 1, 0, 0 michael@0: */ michael@0: static unsigned char DIGIT_0 [] = michael@0: { ON_3 | ON_2, michael@0: ON_4 | ON_1, michael@0: ON_5 | ON_0, michael@0: ON_5 | ON_0, michael@0: ON_5 | ON_0, michael@0: ON_4 | ON_1, michael@0: ON_3 | ON_2 michael@0: }; michael@0: michael@0: /* michael@0: 0, 0, 0, 1, 0, 0, michael@0: 0, 0, 0, 1, 0, 0, michael@0: 0, 0, 0, 1, 0, 0, michael@0: 0, 0, 0, 1, 0, 0, michael@0: 0, 0, 0, 1, 0, 0, michael@0: 0, 0, 0, 1, 0, 0, michael@0: 0, 0, 0, 1, 0, 0, michael@0: */ michael@0: static unsigned char DIGIT_1 [] = michael@0: { ON_2, michael@0: ON_2, michael@0: ON_2, michael@0: ON_2, michael@0: ON_2, michael@0: ON_2, michael@0: ON_2 michael@0: }; michael@0: michael@0: /* michael@0: 1, 1, 1, 1, 1, 0, michael@0: 0, 0, 0, 0, 0, 1, michael@0: 0, 0, 0, 0, 0, 1, michael@0: 0, 1, 1, 1, 1, 0, michael@0: 1, 0, 0, 0, 0, 0, michael@0: 1, 0, 0, 0, 0, 0, michael@0: 0, 1, 1, 1, 1, 1, michael@0: */ michael@0: static unsigned char DIGIT_2 [] = michael@0: { ON_5 | ON_4 | ON_3 | ON_2 | ON_1, michael@0: ON_0, michael@0: ON_0, michael@0: ON_4 | ON_3 | ON_2 | ON_1, michael@0: ON_5, michael@0: ON_5, michael@0: ON_4 | ON_3 | ON_2 | ON_1 | ON_0, michael@0: }; michael@0: michael@0: /* michael@0: 1, 1, 1, 1, 1, 0, michael@0: 0, 0, 0, 0, 0, 1, michael@0: 0, 0, 0, 0, 0, 1, michael@0: 0, 1, 1, 1, 1, 1, michael@0: 0, 0, 0, 0, 0, 1, michael@0: 0, 0, 0, 0, 0, 1, michael@0: 1, 1, 1, 1, 1, 0, michael@0: */ michael@0: static unsigned char DIGIT_3 [] = michael@0: { ON_5 | ON_4 | ON_3 | ON_2 | ON_1, michael@0: ON_0, michael@0: ON_0, michael@0: ON_4 | ON_3 | ON_2 | ON_1 | ON_0, michael@0: ON_0, michael@0: ON_0, michael@0: ON_5 | ON_4 | ON_3 | ON_2 | ON_1, michael@0: }; michael@0: michael@0: /* michael@0: 0, 1, 0, 0, 0, 1, michael@0: 0, 1, 0, 0, 0, 1, michael@0: 0, 1, 0, 0, 0, 1, michael@0: 0, 1, 1, 1, 1, 1, michael@0: 0, 0, 0, 0, 0, 1, michael@0: 0, 0, 0, 0, 0, 1, michael@0: 0, 0, 0, 0, 0, 1 michael@0: */ michael@0: static unsigned char DIGIT_4 [] = michael@0: { ON_4 | ON_0, michael@0: ON_4 | ON_0, michael@0: ON_4 | ON_0, michael@0: ON_4 | ON_3 | ON_2 | ON_1 | ON_0, michael@0: ON_0, michael@0: ON_0, michael@0: ON_0, michael@0: }; michael@0: michael@0: /* michael@0: 0, 1, 1, 1, 1, 1, michael@0: 1, 0, 0, 0, 0, 0, michael@0: 1, 0, 0, 0, 0, 0, michael@0: 0, 1, 1, 1, 1, 0, michael@0: 0, 0, 0, 0, 0, 1, michael@0: 0, 0, 0, 0, 0, 1, michael@0: 1, 1, 1, 1, 1, 0, michael@0: */ michael@0: static unsigned char DIGIT_5 [] = michael@0: { ON_4 | ON_3 | ON_2 | ON_1 | ON_0, michael@0: ON_5, michael@0: ON_5, michael@0: ON_4 | ON_3 | ON_2 | ON_1, michael@0: ON_0, michael@0: ON_0, michael@0: ON_5 | ON_4 | ON_3 | ON_2 | ON_1, michael@0: }; michael@0: michael@0: /* michael@0: 0, 1, 1, 1, 1, 1, michael@0: 1, 0, 0, 0, 0, 0, michael@0: 1, 0, 0, 0, 0, 0, michael@0: 1, 1, 1, 1, 1, 0, michael@0: 1, 0, 0, 0, 0, 1, michael@0: 1, 0, 0, 0, 0, 1, michael@0: 0, 1, 1, 1, 1, 0, michael@0: */ michael@0: static unsigned char DIGIT_6 [] = michael@0: { ON_4 | ON_3 | ON_2 | ON_1 | ON_0, michael@0: ON_5, michael@0: ON_5, michael@0: ON_4 | ON_3 | ON_2 | ON_1, michael@0: ON_5 | ON_0, michael@0: ON_5 | ON_0, michael@0: ON_4 | ON_3 | ON_2 | ON_1, michael@0: }; michael@0: michael@0: /* michael@0: 1, 1, 1, 1, 1, 1, michael@0: 0, 0, 0, 0, 0, 1, michael@0: 0, 0, 0, 0, 1, 0, michael@0: 0, 0, 0, 1, 0, 0, michael@0: 0, 0, 1, 0, 0, 0, michael@0: 0, 1, 0, 0, 0, 0, michael@0: 1, 0, 0, 0, 0, 0 michael@0: */ michael@0: static unsigned char DIGIT_7 [] = michael@0: { ON_5 | ON_4 | ON_3 | ON_2 | ON_1 | ON_0, michael@0: ON_0, michael@0: ON_1, michael@0: ON_2, michael@0: ON_3, michael@0: ON_4, michael@0: ON_5 michael@0: }; michael@0: michael@0: /* michael@0: 0, 1, 1, 1, 1, 1, michael@0: 1, 0, 0, 0, 0, 1, michael@0: 1, 0, 0, 0, 0, 1, michael@0: 0, 1, 1, 1, 1, 0, michael@0: 1, 0, 0, 0, 0, 1, michael@0: 1, 0, 0, 0, 0, 1, michael@0: 0, 1, 1, 1, 1, 0 michael@0: */ michael@0: static unsigned char DIGIT_8 [] = michael@0: { ON_4 | ON_3 | ON_2 | ON_1, michael@0: ON_5 | ON_0, michael@0: ON_5 | ON_0, michael@0: ON_4 | ON_3 | ON_2 | ON_1, michael@0: ON_5 | ON_0, michael@0: ON_5 | ON_0, michael@0: ON_4 | ON_3 | ON_2 | ON_1, michael@0: }; michael@0: michael@0: /* michael@0: 0, 1, 1, 1, 1, 1, michael@0: 1, 0, 0, 0, 0, 1, michael@0: 1, 0, 0, 0, 0, 1, michael@0: 0, 1, 1, 1, 1, 1, michael@0: 0, 0, 0, 0, 0, 1, michael@0: 0, 0, 0, 0, 0, 1, michael@0: 0, 1, 1, 1, 1, 0 michael@0: */ michael@0: static unsigned char DIGIT_9 [] = michael@0: { ON_4 | ON_3 | ON_2 | ON_1 | ON_0, michael@0: ON_5 | ON_0, michael@0: ON_5 | ON_0, michael@0: ON_4 | ON_3 | ON_2 | ON_1 | ON_0, michael@0: ON_0, michael@0: ON_0, michael@0: ON_4 | ON_3 | ON_2 | ON_1, michael@0: }; michael@0: michael@0: static unsigned char *DIGITS[] = { michael@0: DIGIT_0, michael@0: DIGIT_1, michael@0: DIGIT_2, michael@0: DIGIT_3, michael@0: DIGIT_4, michael@0: DIGIT_5, michael@0: DIGIT_6, michael@0: DIGIT_7, michael@0: DIGIT_8, michael@0: DIGIT_9 michael@0: }; michael@0: michael@0: YuvStamper::YuvStamper(unsigned char* pYData, michael@0: uint32_t width, michael@0: uint32_t height, michael@0: uint32_t stride, michael@0: uint32_t x, michael@0: uint32_t y, michael@0: unsigned char symbol_width, michael@0: unsigned char symbol_height): michael@0: pYData(pYData), mStride(stride), michael@0: mWidth(width), mHeight(height), michael@0: mSymbolWidth(symbol_width), mSymbolHeight(symbol_height), michael@0: mCursor(x, y) {} michael@0: michael@0: bool YuvStamper::Encode(uint32_t width, uint32_t height, uint32_t stride, michael@0: unsigned char* pYData, unsigned char* pMsg, size_t msg_len, michael@0: uint32_t x, uint32_t y) michael@0: { michael@0: YuvStamper stamper(pYData, width, height, stride, michael@0: x, y, sBitSize, sBitSize); michael@0: michael@0: // Reserve space for a checksum. michael@0: if (stamper.Capacity() < 8 * (msg_len + sizeof(uint32_t))) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: bool ok = false; michael@0: uint32_t crc; michael@0: unsigned char* pCrc = reinterpret_cast(&crc); michael@0: r_crc32(reinterpret_cast(pMsg), (int)msg_len, &crc); michael@0: crc = htonl(crc); michael@0: michael@0: while (msg_len-- > 0) { michael@0: if (!stamper.Write8(*pMsg++)) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: // Add checksum after the message. michael@0: ok = stamper.Write8(*pCrc++) && michael@0: stamper.Write8(*pCrc++) && michael@0: stamper.Write8(*pCrc++) && michael@0: stamper.Write8(*pCrc++); michael@0: michael@0: return ok; michael@0: } michael@0: michael@0: bool YuvStamper::Decode(uint32_t width, uint32_t height, uint32_t stride, michael@0: unsigned char* pYData, unsigned char* pMsg, size_t msg_len, michael@0: uint32_t x, uint32_t y) michael@0: { michael@0: YuvStamper stamper(pYData, width, height, stride, michael@0: x, y, sBitSize, sBitSize); michael@0: michael@0: unsigned char* ptr = pMsg; michael@0: size_t len = msg_len; michael@0: uint32_t crc, msg_crc; michael@0: unsigned char* pCrc = reinterpret_cast(&crc); michael@0: michael@0: // Account for space reserved for the checksum michael@0: if (stamper.Capacity() < 8 * (len + sizeof(uint32_t))) { michael@0: return false; michael@0: } michael@0: michael@0: while (len-- > 0) { michael@0: if(!stamper.Read8(*ptr++)) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: if (!(stamper.Read8(*pCrc++) && michael@0: stamper.Read8(*pCrc++) && michael@0: stamper.Read8(*pCrc++) && michael@0: stamper.Read8(*pCrc++))) { michael@0: return false; michael@0: } michael@0: michael@0: r_crc32(reinterpret_cast(pMsg), (int)msg_len, &msg_crc); michael@0: return crc == htonl(msg_crc); michael@0: } michael@0: michael@0: inline uint32_t YuvStamper::Capacity() michael@0: { michael@0: // Enforce at least a symbol width and height offset from outer edges. michael@0: if (mCursor.y + mSymbolHeight > mHeight) { michael@0: return 0; michael@0: } michael@0: michael@0: if (mCursor.x + mSymbolWidth > mWidth && !AdvanceCursor()) { michael@0: return 0; michael@0: } michael@0: michael@0: // Normalize frame integral to mSymbolWidth x mSymbolHeight michael@0: uint32_t width = mWidth / mSymbolWidth; michael@0: uint32_t height = mHeight / mSymbolHeight; michael@0: uint32_t x = mCursor.x / mSymbolWidth; michael@0: uint32_t y = mCursor.y / mSymbolHeight; michael@0: michael@0: return (width * height - width * y)- x; michael@0: } michael@0: michael@0: bool YuvStamper::Write8(unsigned char value) michael@0: { michael@0: // Encode MSB to LSB. michael@0: unsigned char mask = 0x80; michael@0: while (mask) { michael@0: if (!WriteBit(!!(value & mask))) { michael@0: return false; michael@0: } michael@0: mask >>= 1; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool YuvStamper::WriteBit(bool one) michael@0: { michael@0: // A bit is mapped to a mSymbolWidth x mSymbolHeight square of luma data points. michael@0: // Don't use ternary op.: https://bugzilla.mozilla.org/show_bug.cgi?id=1001708 michael@0: unsigned char value; michael@0: if (one) michael@0: value = sYOn; michael@0: else michael@0: value = sYOff; michael@0: michael@0: for (uint32_t y = 0; y < mSymbolHeight; y++) { michael@0: for (uint32_t x = 0; x < mSymbolWidth; x++) { michael@0: *(pYData + (mCursor.x + x) + ((mCursor.y + y) * mStride)) = value; michael@0: } michael@0: } michael@0: michael@0: return AdvanceCursor(); michael@0: } michael@0: michael@0: bool YuvStamper::AdvanceCursor() michael@0: { michael@0: mCursor.x += mSymbolWidth; michael@0: if (mCursor.x + mSymbolWidth > mWidth) { michael@0: // move to the start of the next row if possible. michael@0: mCursor.y += mSymbolHeight; michael@0: if (mCursor.y + mSymbolHeight > mHeight) { michael@0: // end of frame, do not advance michael@0: mCursor.y -= mSymbolHeight; michael@0: mCursor.x -= mSymbolWidth; michael@0: return false; michael@0: } else { michael@0: mCursor.x = 0; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool YuvStamper::Read8(unsigned char &value) michael@0: { michael@0: unsigned char octet = 0; michael@0: unsigned char bit = 0; michael@0: michael@0: for (int i = 8; i > 0; --i) { michael@0: if (!ReadBit(bit)) { michael@0: return false; michael@0: } michael@0: octet <<= 1; michael@0: octet |= bit; michael@0: } michael@0: michael@0: value = octet; michael@0: return true; michael@0: } michael@0: michael@0: bool YuvStamper::ReadBit(unsigned char &bit) michael@0: { michael@0: uint32_t sum = 0; michael@0: for (uint32_t y = 0; y < mSymbolHeight; y++) { michael@0: for (uint32_t x = 0; x < mSymbolWidth; x++) { michael@0: sum += *(pYData + mStride * (mCursor.y + y) + mCursor.x + x); michael@0: } michael@0: } michael@0: michael@0: // apply threshold to collected bit square michael@0: bit = (sum > (sBitThreshold * mSymbolWidth * mSymbolHeight)) ? 1 : 0; michael@0: return AdvanceCursor(); michael@0: } michael@0: michael@0: bool YuvStamper::WriteDigits(uint32_t value) michael@0: { michael@0: char buf[20]; michael@0: PR_snprintf(buf, sizeof(buf), "%.5u", value); michael@0: size_t size = strlen(buf); michael@0: michael@0: if (Capacity() < size) { michael@0: return false; michael@0: } michael@0: michael@0: for (size_t i=0; i < size; ++i) { michael@0: if (!WriteDigit(buf[i] - '0')) michael@0: return false; michael@0: if (!AdvanceCursor()) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool YuvStamper::WriteDigit(unsigned char digit) { michael@0: if (digit > sizeof(DIGITS)/sizeof(DIGITS[0])) michael@0: return false; michael@0: michael@0: unsigned char *dig = DIGITS[digit]; michael@0: for (uint32_t row = 0; row < sDigitHeight; ++row) { michael@0: unsigned char mask = 0x01 << (sDigitWidth - 1); michael@0: for (uint32_t col = 0; col < sDigitWidth; ++col, mask >>= 1) { michael@0: if (dig[row] & mask) { michael@0: for (uint32_t xx=0; xx < sPixelSize; ++xx) { michael@0: for (uint32_t yy=0; yy < sPixelSize; ++yy) { michael@0: WritePixel(pYData, michael@0: mCursor.x + (col * sPixelSize) + xx, michael@0: mCursor.y + (row * sPixelSize) + yy); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void YuvStamper::WritePixel(unsigned char *data, uint32_t x, uint32_t y) { michael@0: unsigned char *ptr = &data[y * mStride + x]; michael@0: // Don't use ternary op.: https://bugzilla.mozilla.org/show_bug.cgi?id=1001708 michael@0: if (*ptr > sLumaThreshold) michael@0: *ptr = sLumaMin; michael@0: else michael@0: *ptr = sLumaMax; michael@0: } michael@0: michael@0: } // Namespace mozilla.