/* This Source Code Form is subject to the terms of the Mozilla Public * 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/. */ #ifdef HAVE_NETINET_IN_H # include #elif defined XP_WIN # include #endif #include #include "nspr.h" #include "YuvStamper.h" #include "mozilla/Sprintf.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(unsigned char* pYData, uint32_t width, uint32_t height, uint32_t stride, uint32_t x, uint32_t y, unsigned char symbol_width, unsigned char 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, unsigned char* pYData, unsigned char* pMsg, size_t msg_len, uint32_t x, uint32_t 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; uint32_t crc; unsigned char* pCrc = reinterpret_cast(&crc); r_crc32(reinterpret_cast(pMsg), (int)msg_len, &crc); crc = htonl(crc); while (msg_len-- > 0) { if (!stamper.Write8(*pMsg++)) { return false; } } // Add checksum after the message. ok = stamper.Write8(*pCrc++) && stamper.Write8(*pCrc++) && stamper.Write8(*pCrc++) && stamper.Write8(*pCrc++); return ok; } bool YuvStamper::Decode(uint32_t width, uint32_t height, uint32_t stride, unsigned char* pYData, unsigned char* pMsg, size_t msg_len, uint32_t x, uint32_t y) { YuvStamper stamper(pYData, width, height, stride, x, y, sBitSize, sBitSize); unsigned char* ptr = pMsg; size_t len = msg_len; uint32_t crc, msg_crc; unsigned char* pCrc = reinterpret_cast(&crc); // Account for space reserved for the checksum if (stamper.Capacity() < 8 * (len + sizeof(uint32_t))) { return false; } while (len-- > 0) { if (!stamper.Read8(*ptr++)) { return false; } } if (!(stamper.Read8(*pCrc++) && stamper.Read8(*pCrc++) && stamper.Read8(*pCrc++) && stamper.Read8(*pCrc++))) { return false; } r_crc32(reinterpret_cast(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 + mSymbolHeight > mHeight) { return 0; } if (mCursor.x + mSymbolWidth > mWidth && !AdvanceCursor()) { return 0; } // Normalize frame integral to mSymbolWidth x mSymbolHeight 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(unsigned char value) { // Encode MSB to LSB. unsigned char mask = 0x80; while (mask) { if (!WriteBit(!!(value & mask))) { return false; } mask >>= 1; } return true; } bool YuvStamper::WriteBit(bool one) { // A bit is mapped to a mSymbolWidth x mSymbolHeight square of luma data // points. Don't use ternary op.: // https://bugzilla.mozilla.org/show_bug.cgi?id=1001708 unsigned char value; if (one) value = sYOn; else value = sYOff; 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 += mSymbolWidth; if (mCursor.x + mSymbolWidth > mWidth) { // move to the start of the next row if possible. mCursor.y += mSymbolHeight; if (mCursor.y + mSymbolHeight > mHeight) { // end of frame, do not advance mCursor.y -= mSymbolHeight; mCursor.x -= mSymbolWidth; return false; } else { mCursor.x = 0; } } return true; } bool YuvStamper::Read8(unsigned char& value) { unsigned char octet = 0; unsigned char bit = 0; for (int i = 8; i > 0; --i) { if (!ReadBit(bit)) { return false; } octet <<= 1; octet |= bit; } value = octet; return true; } bool YuvStamper::ReadBit(unsigned char& bit) { uint32_t sum = 0; 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 * mSymbolWidth * mSymbolHeight)) ? 1 : 0; return AdvanceCursor(); } bool YuvStamper::WriteDigits(uint32_t value) { char buf[20]; SprintfLiteral(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(unsigned char 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(unsigned char* data, uint32_t x, uint32_t y) { unsigned char* ptr = &data[y * mStride + x]; // Don't use ternary op.: https://bugzilla.mozilla.org/show_bug.cgi?id=1001708 if (*ptr > sLumaThreshold) *ptr = sLumaMin; else *ptr = sLumaMax; } } // namespace mozilla.