[Libreoffice-commits] online.git: 9 commits - kit/Delta.hpp kit/Kit.cpp loleaflet/src net/Ssl.cpp net/Ssl.hpp test/data test/DeltaTests.cpp test/Makefile.am wsd/LOOLWSD.cpp wsd/TileDesc.hpp
Michael Meeks
michael.meeks at collabora.com
Wed Nov 22 15:55:23 UTC 2017
kit/Delta.hpp | 219 ++++++++++++++++++++++++++++++++++
kit/Kit.cpp | 10 +
loleaflet/src/core/Socket.js | 20 ++-
loleaflet/src/layer/tile/TileLayer.js | 96 ++++++++++++++
net/Ssl.cpp | 10 +
net/Ssl.hpp | 8 -
test/DeltaTests.cpp | 209 ++++++++++++++++++++++++++++++++
test/Makefile.am | 1
test/data/delta-text.png |binary
test/data/delta-text2.png |binary
wsd/LOOLWSD.cpp | 6
wsd/TileDesc.hpp | 1
12 files changed, 568 insertions(+), 12 deletions(-)
New commits:
commit 92379ffbf2a52d3430eb7f693f64850413a1f347
Author: Michael Meeks <michael.meeks at collabora.com>
Date: Wed Nov 22 15:54:42 2017 +0000
Make the WSD SSL cipher list configurable.
Change-Id: If79b9efc9438cf0a2345b8e81385adafef63ce22
diff --git a/net/Ssl.cpp b/net/Ssl.cpp
index 5a9f8280..cd28f53b 100644
--- a/net/Ssl.cpp
+++ b/net/Ssl.cpp
@@ -26,11 +26,14 @@ extern "C"
};
}
+#define DEFAULT_CIPHER_SET "ALL:!RC4:!SEED:!ADH:!LOW:!EXP:!MD5:@STRENGTH"
+
std::unique_ptr<SslContext> SslContext::Instance(nullptr);
SslContext::SslContext(const std::string& certFilePath,
const std::string& keyFilePath,
- const std::string& caFilePath) :
+ const std::string& caFilePath,
+ const std::string& cipherList) :
_ctx(nullptr)
{
const std::vector<char> rand = Util::rng::getBytes(512);
@@ -107,7 +110,10 @@ SslContext::SslContext(const std::string& certFilePath,
}
SSL_CTX_set_verify(_ctx, SSL_VERIFY_NONE, nullptr /*&verifyServerCallback*/);
- SSL_CTX_set_cipher_list(_ctx, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
+ std::string ciphers(cipherList);
+ if (ciphers.empty())
+ ciphers = DEFAULT_CIPHER_SET;
+ SSL_CTX_set_cipher_list(_ctx, ciphers.c_str());
SSL_CTX_set_verify_depth(_ctx, 9);
// The write buffer may re-allocate, and we don't mind partial writes.
diff --git a/net/Ssl.hpp b/net/Ssl.hpp
index b6fc0427..90ba4f44 100644
--- a/net/Ssl.hpp
+++ b/net/Ssl.hpp
@@ -29,10 +29,11 @@ class SslContext
public:
static void initialize(const std::string& certFilePath,
const std::string& keyFilePath,
- const std::string& caFilePath)
+ const std::string& caFilePath,
+ const std::string& cipherList = "")
{
assert (!Instance);
- Instance.reset(new SslContext(certFilePath, keyFilePath, caFilePath));
+ Instance.reset(new SslContext(certFilePath, keyFilePath, caFilePath, cipherList));
}
static void uninitialize();
@@ -47,7 +48,8 @@ public:
private:
SslContext(const std::string& certFilePath,
const std::string& keyFilePath,
- const std::string& caFilePath);
+ const std::string& caFilePath,
+ const std::string& cipherList);
void initDH();
void initECDH();
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index 7ff87b79..102670e7 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -927,11 +927,15 @@ void LOOLWSD::initializeSSL()
const auto ssl_ca_file_path = getPathFromConfig("ssl.ca_file_path");
LOG_INF("SSL CA file: " << ssl_ca_file_path);
+ const auto ssl_cipher_list = getPathFromConfig("ssl.cipher_list");
+ LOG_INF("SSL Cipher list: " << ssl_cipher_list);
+
#if ENABLE_SSL
// Initialize the non-blocking socket SSL.
SslContext::initialize(ssl_cert_file_path,
ssl_key_file_path,
- ssl_ca_file_path);
+ ssl_ca_file_path,
+ ssl_cipher_list);
#endif
}
commit 74f44251b782e5cab42960d1e328102a1538e72f
Author: Michael Meeks <michael.meeks at collabora.com>
Date: Mon Oct 23 20:59:10 2017 +0100
Deltas - collapse multiple rows to a single row.
Change-Id: Ia2a617c2adbbc4e66b7c773c2280ec609aead16e
diff --git a/kit/Delta.hpp b/kit/Delta.hpp
index 82f871bb..e1890370 100644
--- a/kit/Delta.hpp
+++ b/kit/Delta.hpp
@@ -64,6 +64,7 @@ class DeltaGenerator {
// How do the rows look against each other ?
size_t lastMatchOffset = 0;
+ size_t lastCopy = 0;
for (int y = 0; y < prev._height; ++y)
{
// Life is good where rows match:
@@ -78,12 +79,25 @@ class DeltaGenerator {
if (prev._rows[match].identical(cur._rows[y]))
{
// TODO: if offsets are >256 - use 16bits?
+ if (lastCopy > 0)
+ {
+ char cnt = output[lastCopy];
+ if (output[lastCopy + 1] + cnt == (char)(match) &&
+ output[lastCopy + 2] + cnt == (char)(y))
+ {
+ output[lastCopy]++;
+ matched = true;
+ continue;
+ }
+ }
- // hopefully find blocks of this.
lastMatchOffset = match - y;
- output.push_back('c'); // copy-row
+ output.push_back('c'); // copy-row
+ lastCopy = output.size();
+ output.push_back(1); // count
output.push_back(match); // src
- output.push_back(y); // dest
+ output.push_back(y); // dest
+
matched = true;
continue;
}
diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index a1ac27f9..5f0ba440 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -1306,13 +1306,20 @@ L.TileLayer = L.GridLayer.extend({
// FIXME; can we operate directly on the image ?
var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
- var oldData = new Uint8ClampedArray(imgData);
+ var oldData = new Uint8ClampedArray(imgData.data);
+
var delta = img;
var pixSize = canvas.width * canvas.height * 4;
var offset = 0;
console.log('Applying a delta of length ' + delta.length + ' pix size: ' + pixSize + '\nhex: ' + hex2string(delta));
+ // Green-tinge the old-Data ...
+// for (var i = 0; i < pixSize; ++i)
+// {
+// oldData[i*4 + 1] = 128;
+// }
+
// wipe to grey.
// for (var i = 0; i < pixSize * 4; ++i)
// {
@@ -1325,15 +1332,19 @@ L.TileLayer = L.GridLayer.extend({
switch (delta[i])
{
case 99: // 'c': // copy row
- var srcRow = delta[i+1];
- var destRow = delta[i+2]
- i+= 3;
- console.log('copy row ' + srcRow + ' to ' + destRow);
- var src = srcRow * canvas.width * 4;
- var dest = destRow * canvas.width * 4;
- for (var i = 0; i < canvas.width * 4; ++i)
+ var count = delta[i+1];
+ var srcRow = delta[i+2];
+ var destRow = delta[i+3];
+ i+= 4;
+ console.log('copy ' + count + ' row(s) ' + srcRow + ' to ' + destRow);
+ for (var cnt = 0; cnt < count; ++cnt)
{
- imgData.data[dest + i] = oldData[src + i];
+ var src = (srcRow + cnt) * canvas.width * 4;
+ var dest = (destRow + cnt) * canvas.width * 4;
+ for (var j = 0; j < canvas.width * 4; ++j)
+ {
+ imgData.data[dest + j] = oldData[src + j];
+ }
}
break;
case 100: // 'd': // new run
@@ -1351,7 +1362,8 @@ L.TileLayer = L.GridLayer.extend({
imgData.data[offset - 2] = 256; // debug - blue terminator
break;
default:
- console.log('Unknown code ' + delta[i]);
+ console.log('ERROR: Unknown code ' + delta[i] +
+ ' at offset ' + i);
i = delta.length;
break;
}
diff --git a/test/DeltaTests.cpp b/test/DeltaTests.cpp
index 705b7d3e..66868523 100644
--- a/test/DeltaTests.cpp
+++ b/test/DeltaTests.cpp
@@ -84,15 +84,19 @@ std::vector<char> DeltaTests::applyDelta(
{
case 'c': // copy row.
{
- int srcRow = (uint8_t)(delta[i+1]);
- int destRow = (uint8_t)(delta[i+2]);
-
- std::cout << "copy row " << srcRow << " to " << destRow << "\n";
- const char *src = &pixmap[width * srcRow * 4];
- char *dest = &output[width * destRow * 4];
- for (size_t j = 0; j < width * 4; ++j)
- *dest++ = *src++;
- i += 3;
+ int count = (uint8_t)(delta[i+1]);
+ int srcRow = (uint8_t)(delta[i+2]);
+ int destRow = (uint8_t)(delta[i+3]);
+
+// std::cout << "copy " << count <<" row(s) " << srcRow << " to " << destRow << "\n";
+ for (int cnt = 0; cnt < count; ++cnt)
+ {
+ const char *src = &pixmap[width * (srcRow + cnt) * 4];
+ char *dest = &output[width * (destRow + cnt) * 4];
+ for (size_t j = 0; j < width * 4; ++j)
+ *dest++ = *src++;
+ }
+ i += 4;
break;
}
case 'd': // new run
@@ -102,7 +106,7 @@ std::vector<char> DeltaTests::applyDelta(
size_t length = (uint8_t)(delta[i+3]);
i += 4;
- std::cout << "new " << length << " at " << destCol << ", " << destRow << "\n";
+// std::cout << "new " << length << " at " << destCol << ", " << destRow << "\n";
CPPUNIT_ASSERT(length <= width - destCol);
char *dest = &output[width * destRow * 4 + destCol * 4];
commit fa86ba9ec5ecb418bd025c0bc7bbea8456409c07
Author: Michael Meeks <michael.meeks at collabora.com>
Date: Fri Sep 29 18:05:03 2017 +0100
Convert Javascript to row deltas.
Change-Id: I2ec612c2bc047dc36f86c2935178c964f9feae11
diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index 6d1077cb..a1ac27f9 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -1306,6 +1306,7 @@ L.TileLayer = L.GridLayer.extend({
// FIXME; can we operate directly on the image ?
var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
+ var oldData = new Uint8ClampedArray(imgData);
var delta = img;
var pixSize = canvas.width * canvas.height * 4;
var offset = 0;
@@ -1319,38 +1320,43 @@ L.TileLayer = L.GridLayer.extend({
// }
// Apply delta.
- for (var i = 1; i < delta.length &&
- offset < pixSize;)
+ for (var i = 1; i < delta.length;)
{
-// var span = delta[i];
-// var isChangedRun = span & 64;
-// if (span >= 128)
-// {
- // first char is a control code.
- var isChangedRun = delta[i++] & 64;
- span = delta[i++];
- span += delta[i++] * 256;
-// }
- if (isChangedRun) {
- console.log('apply new span of size ' + span + ' at offset ' + i + ' into delta at byte: ' + offset);
+ switch (delta[i])
+ {
+ case 99: // 'c': // copy row
+ var srcRow = delta[i+1];
+ var destRow = delta[i+2]
+ i+= 3;
+ console.log('copy row ' + srcRow + ' to ' + destRow);
+ var src = srcRow * canvas.width * 4;
+ var dest = destRow * canvas.width * 4;
+ for (var i = 0; i < canvas.width * 4; ++i)
+ {
+ imgData.data[dest + i] = oldData[src + i];
+ }
+ break;
+ case 100: // 'd': // new run
+ var destRow = delta[i+1];
+ var destCol = delta[i+2];
+ var span = delta[i+3];
+ var offset = destRow * canvas.width * 4 + destCol * 4;
+ i += 4;
+ console.log('apply new span of size ' + span + ' at pos ' + destCol + ', ' + destRow + ' into delta at byte: ' + offset);
span *= 4;
+ imgData.data[offset + 1] = 256; // debug - greener start
while (span-- > 0) {
imgData.data[offset++] = delta[i++];
}
imgData.data[offset - 2] = 256; // debug - blue terminator
- } else {
- console.log('apply unchanged span of size ' + span + ' at offset ' + i + ' into delta at byte: ' + offset);
- offset += span * 4;
- imgData.data[offset - 3] = 256; // debug - green terminator
+ break;
+ default:
+ console.log('Unknown code ' + delta[i]);
+ i = delta.length;
+ break;
}
}
- while (offset < pixSize) // debug
- {
- imgData.data[offset] = 256; // redden the remaining section.
- imgData.data[offset+3] = 256; // with some alpha
- offset += 4;
- }
ctx.putImageData(imgData, 0, 0);
tile.oldWireId = tile.wireId;
commit 5efb59db50a49374bcf53198f7c0a1e120754cc9
Author: Michael Meeks <michael.meeks at collabora.com>
Date: Thu Sep 28 09:45:46 2017 +0100
Make delta-builder row-based.
Change-Id: Ic59324535c4f412abc4e83774073eb8f57290704
diff --git a/kit/Delta.hpp b/kit/Delta.hpp
index 95f6a1f1..82f871bb 100644
--- a/kit/Delta.hpp
+++ b/kit/Delta.hpp
@@ -21,106 +21,149 @@
/// A quick and dirty delta generator for last tile changes
class DeltaGenerator {
+ struct DeltaBitmapRow {
+ uint64_t _crc;
+ std::vector<uint32_t> _pixels;
+
+ bool identical(const DeltaBitmapRow &other) const
+ {
+ if (_crc != other._crc)
+ return false;
+ return _pixels == other._pixels;
+ }
+ };
+
struct DeltaData {
TileWireId _wid;
- std::shared_ptr<std::vector<uint32_t>> _rawData;
+ int _width;
+ int _height;
+ std::vector<DeltaBitmapRow> _rows;
};
- std::vector<DeltaData> _deltaEntries;
+ std::vector<std::shared_ptr<DeltaData>> _deltaEntries;
bool makeDelta(
- const DeltaData &prevData,
- const DeltaData &curData,
+ const DeltaData &prev,
+ const DeltaData &cur,
std::vector<char>& output)
{
- std::vector<uint32_t> &prev = *prevData._rawData.get();
- std::vector<uint32_t> &cur = *curData._rawData.get();
-
- // FIXME: should we split and compress alpha separately ?
-
- if (prev.size() != cur.size())
+ // TODO: should we split and compress alpha separately ?
+ if (prev._width != cur._width || prev._height != cur._height)
{
- LOG_ERR("mis-sized delta: " << prev.size() << " vs " << cur.size() << "bytes");
+ LOG_ERR("mis-sized delta: " << prev._width << "x" << prev._height << " vs "
+ << cur._width << "x" << cur._height);
return false;
}
output.push_back('D');
- LOG_TRC("building delta of " << prev.size() << "bytes");
- // FIXME: really lame - some RLE might help etc.
- for (size_t i = 0; i < prev.size();)
+ LOG_TRC("building delta of a " << cur._width << "x" << cur._height << " bitmap");
+
+ // row move/copy src/dest is a byte.
+ assert (prev._height <= 256);
+ // column position is a byte.
+ assert (prev._width <= 256);
+
+ // How do the rows look against each other ?
+ size_t lastMatchOffset = 0;
+ for (int y = 0; y < prev._height; ++y)
{
- int sameCount = 0;
- while (i + sameCount < prev.size() &&
- prev[i+sameCount] == cur[i+sameCount])
- {
- ++sameCount;
- }
- if (sameCount > 0)
+ // Life is good where rows match:
+ if (prev._rows[y].identical(cur._rows[y]))
+ continue;
+
+ // Hunt for other rows
+ bool matched = false;
+ for (int yn = 0; yn < prev._height && !matched; ++yn)
{
-#if 0
- if (sameCount < 64)
- output.push_back(sameCount);
- else
-#endif
+ size_t match = (y + lastMatchOffset + yn) % prev._height;
+ if (prev._rows[match].identical(cur._rows[y]))
{
- output.push_back(0x80 | 0x00); // long-same
- output.push_back(sameCount & 0xff);
- output.push_back(sameCount >> 8);
+ // TODO: if offsets are >256 - use 16bits?
+
+ // hopefully find blocks of this.
+ lastMatchOffset = match - y;
+ output.push_back('c'); // copy-row
+ output.push_back(match); // src
+ output.push_back(y); // dest
+ matched = true;
+ continue;
}
- i += sameCount;
- LOG_TRC("identical " << sameCount << "pixels");
}
+ if (matched)
+ continue;
- int diffCount = 0;
- while (i + diffCount < prev.size() &&
- (prev[i+diffCount] != cur[i+diffCount]))
+ // Our row is just that different:
+ const DeltaBitmapRow &curRow = cur._rows[y];
+ const DeltaBitmapRow &prevRow = prev._rows[y];
+ for (int x = 0; x < prev._width;)
{
- ++diffCount;
- }
-
- if (diffCount > 0)
- {
-#if 0
- if (diffCount < 64)
- output.push_back(0x40 & diffCount);
- else
-#endif
+ int same;
+ for (same = 0; same + x < prev._width &&
+ prevRow._pixels[x+same] == curRow._pixels[x+same];)
+ ++same;
+
+ x += same;
+
+ int diff;
+ for (diff = 0; diff + x < prev._width &&
+ (prevRow._pixels[x+diff] == curRow._pixels[x+diff] || diff < 2) &&
+ diff < 254;)
+ ++diff;
+ if (diff > 0)
{
- output.push_back(0x80 | 0x40); // long-diff
- output.push_back(diffCount & 0xff);
- output.push_back(diffCount >> 8);
- }
+ output.push_back('d');
+ output.push_back(y);
+ output.push_back(x);
+ output.push_back(diff);
+
+ size_t dest = output.size();
+ output.resize(dest + diff * 4);
+ memcpy(&output[dest], &curRow._pixels[x], diff * 4);
- size_t dest = output.size();
- output.resize(dest + diffCount * 4);
- memcpy(&output[dest], &cur[i], diffCount * 4);
- LOG_TRC("different " << diffCount << "pixels");
- i += diffCount;
+ LOG_TRC("different " << diff << "pixels");
+ x += diff;
+ }
}
}
+
return true;
}
- std::shared_ptr<std::vector<uint32_t>> dataToVector(
+ std::shared_ptr<DeltaData> dataToDeltaData(
+ TileWireId wid,
unsigned char* pixmap, size_t startX, size_t startY,
int width, int height,
int bufferWidth, int bufferHeight)
{
+ auto data = std::make_shared<DeltaData>();
+ data->_wid = wid;
+
assert (startX + width <= (size_t)bufferWidth);
assert (startY + height <= (size_t)bufferHeight);
- auto vector = std::make_shared<std::vector<uint32_t>>();
- LOG_TRC("Converting data to vector of size "
+ LOG_TRC("Converting pixel data to delta data of size "
<< (width * height * 4) << " width " << width
<< " height " << height);
- vector->resize(width * height);
+ data->_width = width;
+ data->_height = height;
+ data->_rows.resize(height);
for (int y = 0; y < height; ++y)
{
+ DeltaBitmapRow &row = data->_rows[y];
size_t position = ((startY + y) * bufferWidth * 4) + (startX * 4);
- memcpy(&(*vector)[y * width], pixmap + position, width * 4);
+ int32_t *src = reinterpret_cast<int32_t *>(pixmap + position);
+
+ // We get the hash ~for free as we copy - with a cheap hash.
+ uint64_t crc = 0x7fffffff - 1;
+ row._pixels.resize(width);
+ for (int x = 0; x < width; ++x)
+ {
+ crc = (crc << 7) + crc + src[x];
+ row._pixels[x] = src[x];
+ }
}
- return vector;
+ return data;
}
public:
@@ -143,17 +186,15 @@ class DeltaGenerator {
if (_deltaEntries.size() > 6) // FIXME: hard-coded ...
_deltaEntries.erase(_deltaEntries.begin());
- // FIXME: assuming width etc. are all constant & so on.
- DeltaData update;
- update._wid = wid;
- update._rawData = dataToVector(pixmap, startX, startY, width, height,
- bufferWidth, bufferHeight);
+ std::shared_ptr<DeltaData> update =
+ dataToDeltaData(wid, pixmap, startX, startY, width, height,
+ bufferWidth, bufferHeight);
_deltaEntries.push_back(update);
for (auto &old : _deltaEntries)
{
- if (oldWid == old._wid)
- return makeDelta(old, update, output);
+ if (oldWid == old->_wid)
+ return makeDelta(*old, *update, output);
}
return false;
}
diff --git a/test/DeltaTests.cpp b/test/DeltaTests.cpp
index 3532931e..705b7d3e 100644
--- a/test/DeltaTests.cpp
+++ b/test/DeltaTests.cpp
@@ -57,61 +57,85 @@ class DeltaTests : public CPPUNIT_NS::TestFixture
const std::vector<char> &delta);
void assertEqual(const std::vector<char> &a,
- const std::vector<char> &b);
+ const std::vector<char> &b,
+ int width, int height);
};
// Quick hack for debugging
std::vector<char> DeltaTests::applyDelta(
const std::vector<char> &pixmap,
- png_uint_32 /* width */, png_uint_32 /* height */,
+ png_uint_32 width, png_uint_32 height,
const std::vector<char> &delta)
{
CPPUNIT_ASSERT(delta.size() >= 4);
CPPUNIT_ASSERT(delta[0] == 'D');
-// std::cout << "apply delta of size " << delta.size() << "\n";
+ std::cout << "apply delta of size " << delta.size() << "\n";
+ // start with the same state.
std::vector<char> output = pixmap;
- CPPUNIT_ASSERT_EQUAL(output.size(), pixmap.size());
- size_t offset = 0;
- for (size_t i = 1; i < delta.size() &&
- offset < output.size();)
+ CPPUNIT_ASSERT_EQUAL(output.size(), size_t(pixmap.size()));
+ CPPUNIT_ASSERT_EQUAL(output.size(), size_t(width * height * 4));
+
+ size_t offset = 0, i;
+ for (i = 1; i < delta.size() && offset < output.size();)
{
- bool isChangedRun = delta[i++] & 64;
- CPPUNIT_ASSERT(i < delta.size());
- uint32_t span = (unsigned char)delta[i++];
- CPPUNIT_ASSERT(i < delta.size());
- span += ((unsigned char)delta[i++])*256;
- CPPUNIT_ASSERT(i < delta.size() ||
- (i == delta.size() && !isChangedRun));
- span *= 4;
-// std::cout << "span " << span << " offset " << offset << "\n";
- if (isChangedRun)
+ switch (delta[i])
+ {
+ case 'c': // copy row.
+ {
+ int srcRow = (uint8_t)(delta[i+1]);
+ int destRow = (uint8_t)(delta[i+2]);
+
+ std::cout << "copy row " << srcRow << " to " << destRow << "\n";
+ const char *src = &pixmap[width * srcRow * 4];
+ char *dest = &output[width * destRow * 4];
+ for (size_t j = 0; j < width * 4; ++j)
+ *dest++ = *src++;
+ i += 3;
+ break;
+ }
+ case 'd': // new run
{
- CPPUNIT_ASSERT(offset + span <= output.size());
- memcpy(&output[offset], &delta[i], span);
- i += span;
+ int destRow = (uint8_t)(delta[i+1]);
+ int destCol = (uint8_t)(delta[i+2]);
+ size_t length = (uint8_t)(delta[i+3]);
+ i += 4;
+
+ std::cout << "new " << length << " at " << destCol << ", " << destRow << "\n";
+ CPPUNIT_ASSERT(length <= width - destCol);
+
+ char *dest = &output[width * destRow * 4 + destCol * 4];
+ for (size_t j = 0; j < length * 4 && i < delta.size(); ++j)
+ *dest++ = delta[i++];
+ break;
+ }
+ default:
+ std::cout << "Unknown delta code " << delta[i] << "\n";
+ CPPUNIT_ASSERT(false);
+ break;
}
- offset += span;
}
- CPPUNIT_ASSERT_EQUAL(pixmap.size(), output.size());
- CPPUNIT_ASSERT_EQUAL(output.size(), offset);
+ CPPUNIT_ASSERT_EQUAL(delta.size(), i);
return output;
}
void DeltaTests::assertEqual(const std::vector<char> &a,
- const std::vector<char> &b)
+ const std::vector<char> &b,
+ int width, int /* height */)
{
CPPUNIT_ASSERT_EQUAL(a.size(), b.size());
for (size_t i = 0; i < a.size(); ++i)
{
if (a[i] != b[i])
{
- std::cout << "Differences starting at byte " << i;
+ std::cout << "Differences starting at byte " << i << " "
+ << (i/4 % width) << ", " << (i / (width * 4)) << ":\n";
size_t len;
- for (len = 0; a[i+len] != b[i+len] && i + len < a.size(); ++len)
+ for (len = 0; (a[i+len] != b[i+len] || len < 8) && i + len < a.size(); ++len)
{
- std::cout << std::hex << (int)((unsigned char)a[i+len]) << " ";
+ std::cout << std::hex << (int)((unsigned char)a[i+len]) << " != ";
+ std::cout << std::hex << (int)((unsigned char)b[i+len]) << " ";
if (len > 0 && (len % 16 == 0))
std::cout<< "\n";
}
@@ -157,7 +181,7 @@ void DeltaTests::testDeltaSequence()
// Apply it to move to the second frame
std::vector<char> reText2 = applyDelta(text, width, height, delta);
- assertEqual(reText2, text2);
+ assertEqual(reText2, text2, width, height);
// Build a delta between text & text2Wid
std::vector<char> two2one;
@@ -169,7 +193,7 @@ void DeltaTests::testDeltaSequence()
// Apply it to get back to where we started
std::vector<char> reText = applyDelta(text2, width, height, two2one);
- assertEqual(reText, text);
+ assertEqual(reText, text, width, height);
}
void DeltaTests::testRandomDeltas()
commit 42d264eeb00fc14bb89104c7dc8b3bcd53897e30
Author: Michael Meeks <michael.meeks at collabora.com>
Date: Sat Sep 16 17:32:20 2017 +0100
Start of Delta unit-tests.
Change-Id: I1a25f5347c0d7430000146bb585a041d363bcf37
diff --git a/kit/Delta.hpp b/kit/Delta.hpp
index b2b6a13e..95f6a1f1 100644
--- a/kit/Delta.hpp
+++ b/kit/Delta.hpp
@@ -10,8 +10,10 @@
#define INCLUDED_DELTA_HPP
#include <vector>
+#include <assert.h>
+#include <Log.hpp>
-#ifdef TILE_WIRE_ID
+#ifndef TILE_WIRE_ID
# define TILE_WIRE_ID
typedef uint32_t TileWireId;
#endif
@@ -125,7 +127,7 @@ class DeltaGenerator {
DeltaGenerator() {}
/**
- * Creates a delta if possible:
+ * Creates a delta between @oldWid and pixmap if possible:
* if so - returns @true and appends the delta to @output
* stores @pixmap, and other data to accelerate delta
* creation in a limited size cache.
diff --git a/test/DeltaTests.cpp b/test/DeltaTests.cpp
new file mode 100644
index 00000000..3532931e
--- /dev/null
+++ b/test/DeltaTests.cpp
@@ -0,0 +1,181 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * 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/.
+ */
+
+#include "config.h"
+
+#include <cppunit/extensions/HelperMacros.h>
+
+#include "Delta.hpp"
+#include "Util.hpp"
+#include "Png.hpp"
+#include "helpers.hpp"
+
+/// Delta unit-tests.
+class DeltaTests : public CPPUNIT_NS::TestFixture
+{
+ CPPUNIT_TEST_SUITE(DeltaTests);
+
+ CPPUNIT_TEST(testDeltaSequence);
+ CPPUNIT_TEST(testRandomDeltas);
+
+ CPPUNIT_TEST_SUITE_END();
+
+ void testDeltaSequence();
+ void testRandomDeltas();
+
+ std::vector<char> loadPng(const char *relpath,
+ png_uint_32& height,
+ png_uint_32& width,
+ png_uint_32& rowBytes)
+ {
+ std::ifstream file(relpath);
+ std::stringstream buffer;
+ buffer << file.rdbuf();
+ file.close();
+ std::vector<png_bytep> rows =
+ Png::decodePNG(buffer, height, width, rowBytes);
+ std::vector<char> output;
+ for (png_uint_32 y = 0; y < height; ++y)
+ {
+ for (png_uint_32 i = 0; i < width * 4; ++i)
+ {
+ output.push_back(rows[y][i]);
+ }
+ }
+ return output;
+ }
+
+ std::vector<char> applyDelta(
+ const std::vector<char> &pixmap,
+ png_uint_32 width, png_uint_32 height,
+ const std::vector<char> &delta);
+
+ void assertEqual(const std::vector<char> &a,
+ const std::vector<char> &b);
+};
+
+// Quick hack for debugging
+std::vector<char> DeltaTests::applyDelta(
+ const std::vector<char> &pixmap,
+ png_uint_32 /* width */, png_uint_32 /* height */,
+ const std::vector<char> &delta)
+{
+ CPPUNIT_ASSERT(delta.size() >= 4);
+ CPPUNIT_ASSERT(delta[0] == 'D');
+
+// std::cout << "apply delta of size " << delta.size() << "\n";
+
+ std::vector<char> output = pixmap;
+ CPPUNIT_ASSERT_EQUAL(output.size(), pixmap.size());
+ size_t offset = 0;
+ for (size_t i = 1; i < delta.size() &&
+ offset < output.size();)
+ {
+ bool isChangedRun = delta[i++] & 64;
+ CPPUNIT_ASSERT(i < delta.size());
+ uint32_t span = (unsigned char)delta[i++];
+ CPPUNIT_ASSERT(i < delta.size());
+ span += ((unsigned char)delta[i++])*256;
+ CPPUNIT_ASSERT(i < delta.size() ||
+ (i == delta.size() && !isChangedRun));
+ span *= 4;
+// std::cout << "span " << span << " offset " << offset << "\n";
+ if (isChangedRun)
+ {
+ CPPUNIT_ASSERT(offset + span <= output.size());
+ memcpy(&output[offset], &delta[i], span);
+ i += span;
+ }
+ offset += span;
+ }
+ CPPUNIT_ASSERT_EQUAL(pixmap.size(), output.size());
+ CPPUNIT_ASSERT_EQUAL(output.size(), offset);
+ return output;
+}
+
+void DeltaTests::assertEqual(const std::vector<char> &a,
+ const std::vector<char> &b)
+{
+ CPPUNIT_ASSERT_EQUAL(a.size(), b.size());
+ for (size_t i = 0; i < a.size(); ++i)
+ {
+ if (a[i] != b[i])
+ {
+ std::cout << "Differences starting at byte " << i;
+ size_t len;
+ for (len = 0; a[i+len] != b[i+len] && i + len < a.size(); ++len)
+ {
+ std::cout << std::hex << (int)((unsigned char)a[i+len]) << " ";
+ if (len > 0 && (len % 16 == 0))
+ std::cout<< "\n";
+ }
+ std::cout << " size " << len << "\n";
+ CPPUNIT_ASSERT(false);
+ }
+ }
+}
+
+void DeltaTests::testDeltaSequence()
+{
+ DeltaGenerator gen;
+
+ png_uint_32 height, width, rowBytes;
+ const TileWireId textWid = 1;
+ std::vector<char> text =
+ DeltaTests::loadPng("data/delta-text.png",
+ height, width, rowBytes);
+ CPPUNIT_ASSERT(height == 256 && width == 256 && rowBytes == 256*4);
+ CPPUNIT_ASSERT_EQUAL(size_t(256 * 256 * 4), text.size());
+
+ const TileWireId text2Wid = 2;
+ std::vector<char> text2 =
+ DeltaTests::loadPng("data/delta-text2.png",
+ height, width, rowBytes);
+ CPPUNIT_ASSERT(height == 256 && width == 256 && rowBytes == 256*4);
+ CPPUNIT_ASSERT_EQUAL(size_t(256 * 256 * 4), text2.size());
+
+ std::vector<char> delta;
+ // Stash it in the cache
+ CPPUNIT_ASSERT(gen.createDelta(
+ reinterpret_cast<unsigned char *>(&text[0]),
+ 0, 0, width, height, width, height,
+ delta, textWid, 0) == false);
+ CPPUNIT_ASSERT(delta.size() == 0);
+
+ // Build a delta between text2 & textWid
+ CPPUNIT_ASSERT(gen.createDelta(
+ reinterpret_cast<unsigned char *>(&text2[0]),
+ 0, 0, width, height, width, height,
+ delta, text2Wid, textWid) == true);
+ CPPUNIT_ASSERT(delta.size() > 0);
+
+ // Apply it to move to the second frame
+ std::vector<char> reText2 = applyDelta(text, width, height, delta);
+ assertEqual(reText2, text2);
+
+ // Build a delta between text & text2Wid
+ std::vector<char> two2one;
+ CPPUNIT_ASSERT(gen.createDelta(
+ reinterpret_cast<unsigned char *>(&text[0]),
+ 0, 0, width, height, width, height,
+ two2one, textWid, text2Wid) == true);
+ CPPUNIT_ASSERT(two2one.size() > 0);
+
+ // Apply it to get back to where we started
+ std::vector<char> reText = applyDelta(text2, width, height, two2one);
+ assertEqual(reText, text);
+}
+
+void DeltaTests::testRandomDeltas()
+{
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(DeltaTests);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/test/Makefile.am b/test/Makefile.am
index aa54e0cb..98426f72 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -47,6 +47,7 @@ wsd_sources = \
test_base_source = \
TileQueueTests.cpp \
WhiteBoxTests.cpp \
+ DeltaTests.cpp \
$(wsd_sources)
test_all_source = \
diff --git a/test/data/delta-text.png b/test/data/delta-text.png
new file mode 100644
index 00000000..3d48d9bf
Binary files /dev/null and b/test/data/delta-text.png differ
diff --git a/test/data/delta-text2.png b/test/data/delta-text2.png
new file mode 100644
index 00000000..d05b897c
Binary files /dev/null and b/test/data/delta-text2.png differ
commit 78398d4482a5a39c87d7c0ec88fc9d357f73408c
Author: Michael Meeks <michael.meeks at collabora.com>
Date: Sat Sep 16 17:27:31 2017 +0100
Move the Delta generator out into its own file.
Change-Id: I7f7553c292970b1a52879b6d6c14e67172022310
diff --git a/kit/Delta.hpp b/kit/Delta.hpp
new file mode 100644
index 00000000..b2b6a13e
--- /dev/null
+++ b/kit/Delta.hpp
@@ -0,0 +1,162 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * 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/.
+ */
+#ifndef INCLUDED_DELTA_HPP
+#define INCLUDED_DELTA_HPP
+
+#include <vector>
+
+#ifdef TILE_WIRE_ID
+# define TILE_WIRE_ID
+ typedef uint32_t TileWireId;
+#endif
+
+/// A quick and dirty delta generator for last tile changes
+class DeltaGenerator {
+
+ struct DeltaData {
+ TileWireId _wid;
+ std::shared_ptr<std::vector<uint32_t>> _rawData;
+ };
+ std::vector<DeltaData> _deltaEntries;
+
+ bool makeDelta(
+ const DeltaData &prevData,
+ const DeltaData &curData,
+ std::vector<char>& output)
+ {
+ std::vector<uint32_t> &prev = *prevData._rawData.get();
+ std::vector<uint32_t> &cur = *curData._rawData.get();
+
+ // FIXME: should we split and compress alpha separately ?
+
+ if (prev.size() != cur.size())
+ {
+ LOG_ERR("mis-sized delta: " << prev.size() << " vs " << cur.size() << "bytes");
+ return false;
+ }
+
+ output.push_back('D');
+ LOG_TRC("building delta of " << prev.size() << "bytes");
+ // FIXME: really lame - some RLE might help etc.
+ for (size_t i = 0; i < prev.size();)
+ {
+ int sameCount = 0;
+ while (i + sameCount < prev.size() &&
+ prev[i+sameCount] == cur[i+sameCount])
+ {
+ ++sameCount;
+ }
+ if (sameCount > 0)
+ {
+#if 0
+ if (sameCount < 64)
+ output.push_back(sameCount);
+ else
+#endif
+ {
+ output.push_back(0x80 | 0x00); // long-same
+ output.push_back(sameCount & 0xff);
+ output.push_back(sameCount >> 8);
+ }
+ i += sameCount;
+ LOG_TRC("identical " << sameCount << "pixels");
+ }
+
+ int diffCount = 0;
+ while (i + diffCount < prev.size() &&
+ (prev[i+diffCount] != cur[i+diffCount]))
+ {
+ ++diffCount;
+ }
+
+ if (diffCount > 0)
+ {
+#if 0
+ if (diffCount < 64)
+ output.push_back(0x40 & diffCount);
+ else
+#endif
+ {
+ output.push_back(0x80 | 0x40); // long-diff
+ output.push_back(diffCount & 0xff);
+ output.push_back(diffCount >> 8);
+ }
+
+ size_t dest = output.size();
+ output.resize(dest + diffCount * 4);
+ memcpy(&output[dest], &cur[i], diffCount * 4);
+ LOG_TRC("different " << diffCount << "pixels");
+ i += diffCount;
+ }
+ }
+ return true;
+ }
+
+ std::shared_ptr<std::vector<uint32_t>> dataToVector(
+ unsigned char* pixmap, size_t startX, size_t startY,
+ int width, int height,
+ int bufferWidth, int bufferHeight)
+ {
+ assert (startX + width <= (size_t)bufferWidth);
+ assert (startY + height <= (size_t)bufferHeight);
+
+ auto vector = std::make_shared<std::vector<uint32_t>>();
+ LOG_TRC("Converting data to vector of size "
+ << (width * height * 4) << " width " << width
+ << " height " << height);
+
+ vector->resize(width * height);
+ for (int y = 0; y < height; ++y)
+ {
+ size_t position = ((startY + y) * bufferWidth * 4) + (startX * 4);
+ memcpy(&(*vector)[y * width], pixmap + position, width * 4);
+ }
+
+ return vector;
+ }
+
+ public:
+ DeltaGenerator() {}
+
+ /**
+ * Creates a delta if possible:
+ * if so - returns @true and appends the delta to @output
+ * stores @pixmap, and other data to accelerate delta
+ * creation in a limited size cache.
+ */
+ bool createDelta(
+ unsigned char* pixmap, size_t startX, size_t startY,
+ int width, int height,
+ int bufferWidth, int bufferHeight,
+ std::vector<char>& output,
+ TileWireId wid, TileWireId oldWid)
+ {
+ // First store a copy for later:
+ if (_deltaEntries.size() > 6) // FIXME: hard-coded ...
+ _deltaEntries.erase(_deltaEntries.begin());
+
+ // FIXME: assuming width etc. are all constant & so on.
+ DeltaData update;
+ update._wid = wid;
+ update._rawData = dataToVector(pixmap, startX, startY, width, height,
+ bufferWidth, bufferHeight);
+ _deltaEntries.push_back(update);
+
+ for (auto &old : _deltaEntries)
+ {
+ if (oldWid == old._wid)
+ return makeDelta(old, update, output);
+ }
+ return false;
+ }
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index dfdf276e..df2d6ce5 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -66,6 +66,7 @@
#include "Unit.hpp"
#include "UserMessages.hpp"
#include "Util.hpp"
+#include "Delta.hpp"
#include "common/SigUtil.hpp"
#include "common/Seccomp.hpp"
@@ -285,141 +286,6 @@ namespace
#endif
}
-/// A quick and dirty delta generator for last tile changes
-struct DeltaGenerator {
-
- struct DeltaData {
- TileWireId _wid;
- std::shared_ptr<std::vector<uint32_t>> _rawData;
- };
- std::vector<DeltaData> _deltaEntries;
-
- bool makeDelta(
- const DeltaData &prevData,
- const DeltaData &curData,
- std::vector<char>& output)
- {
- std::vector<uint32_t> &prev = *prevData._rawData.get();
- std::vector<uint32_t> &cur = *curData._rawData.get();
-
- // FIXME: should we split and compress alpha separately ?
-
- if (prev.size() != cur.size())
- {
- LOG_ERR("mis-sized delta: " << prev.size() << " vs " << cur.size() << "bytes");
- return false;
- }
-
- output.push_back('D');
- LOG_TRC("building delta of " << prev.size() << "bytes");
- // FIXME: really lame - some RLE might help etc.
- for (size_t i = 0; i < prev.size();)
- {
- int sameCount = 0;
- while (i + sameCount < prev.size() &&
- prev[i+sameCount] == cur[i+sameCount])
- {
- ++sameCount;
- }
- if (sameCount > 0)
- {
-#if 0
- if (sameCount < 64)
- output.push_back(sameCount);
- else
-#endif
- {
- output.push_back(0x80 | 0x00); // long-same
- output.push_back(sameCount & 0xff);
- output.push_back(sameCount >> 8);
- }
- i += sameCount;
- LOG_TRC("identical " << sameCount << "pixels");
- }
-
- int diffCount = 0;
- while (i + diffCount < prev.size() &&
- (prev[i+diffCount] != cur[i+diffCount]))
- {
- ++diffCount;
- }
-
- if (diffCount > 0)
- {
-#if 0
- if (diffCount < 64)
- output.push_back(0x40 & diffCount);
- else
-#endif
- {
- output.push_back(0x80 | 0x40); // long-diff
- output.push_back(diffCount & 0xff);
- output.push_back(diffCount >> 8);
- }
-
- size_t dest = output.size();
- output.resize(dest + diffCount * 4);
- memcpy(&output[dest], &cur[i], diffCount * 4);
- LOG_TRC("different " << diffCount << "pixels");
- i += diffCount;
- }
- }
- return true;
- }
-
- std::shared_ptr<std::vector<uint32_t>> dataToVector(
- unsigned char* pixmap, size_t startX, size_t startY,
- int width, int height,
- int bufferWidth, int bufferHeight)
- {
- assert (startX + width <= (size_t)bufferWidth);
- assert (startY + height <= (size_t)bufferHeight);
-
- auto vector = std::make_shared<std::vector<uint32_t>>();
- LOG_TRC("Converting data to vector of size "
- << (width * height * 4) << " width " << width
- << " height " << height);
-
- vector->resize(width * height);
- for (int y = 0; y < height; ++y)
- {
- size_t position = ((startY + y) * bufferWidth * 4) + (startX * 4);
- memcpy(&(*vector)[y * width], pixmap + position, width * 4);
- }
-
- return vector;
- }
-
- // Creates a bespoke delta file-format ...
- bool createDelta(
- unsigned char* pixmap, size_t startX, size_t startY,
- int width, int height,
- int bufferWidth, int bufferHeight,
- std::vector<char>& output,
- TileWireId wid, TileWireId oldWid)
- {
- // First store a copy for later:
- if (_deltaEntries.size() > 6) // FIXME: hard-coded ...
- _deltaEntries.erase(_deltaEntries.begin());
-
- // FIXME: assuming width etc. are all constant & so on.
- DeltaData update;
- update._wid = wid;
- update._rawData = dataToVector(pixmap, startX, startY, width, height,
- bufferWidth, bufferHeight);
- _deltaEntries.push_back(update);
-
- for (auto &old : _deltaEntries)
- {
- if (oldWid == old._wid)
- return makeDelta(old, update, output);
- }
- return false;
- }
-};
-
-
-
/// A quick & dirty cache of the last few PNGs
/// and their hashes to avoid re-compression
/// wherever possible.
diff --git a/wsd/TileDesc.hpp b/wsd/TileDesc.hpp
index a4c61636..7e738f64 100644
--- a/wsd/TileDesc.hpp
+++ b/wsd/TileDesc.hpp
@@ -20,6 +20,7 @@
#include "Exceptions.hpp"
#include "Protocol.hpp"
+#define TILE_WIRE_ID
typedef uint32_t TileWireId;
typedef uint64_t TileBinaryHash;
commit ed8807a1a5a613f54dfc5a204294e870969254e2
Author: Michael Meeks <michael.meeks at collabora.com>
Date: Tue Sep 12 09:32:30 2017 +0100
Insert pixels from 'new' not 'old'.
Change-Id: I117348885073b740ed8b2df84d805854b2f00767
diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index 5f9c9106..dfdf276e 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -357,7 +357,9 @@ struct DeltaGenerator {
output.push_back(diffCount >> 8);
}
- output.insert(output.end(), (char *)&cur[i], (char *)&cur[i+diffCount]);
+ size_t dest = output.size();
+ output.resize(dest + diffCount * 4);
+ memcpy(&output[dest], &cur[i], diffCount * 4);
LOG_TRC("different " << diffCount << "pixels");
i += diffCount;
}
@@ -401,16 +403,16 @@ struct DeltaGenerator {
_deltaEntries.erase(_deltaEntries.begin());
// FIXME: assuming width etc. are all constant & so on.
- DeltaData reference;
- reference._wid = wid;
- reference._rawData = dataToVector(pixmap, startX, startY, width, height,
- bufferWidth, bufferHeight);
- _deltaEntries.push_back(reference);
+ DeltaData update;
+ update._wid = wid;
+ update._rawData = dataToVector(pixmap, startX, startY, width, height,
+ bufferWidth, bufferHeight);
+ _deltaEntries.push_back(update);
- for (auto &it : _deltaEntries)
+ for (auto &old : _deltaEntries)
{
- if (oldWid == it._wid)
- return makeDelta(reference, it, output);
+ if (oldWid == old._wid)
+ return makeDelta(old, update, output);
}
return false;
}
commit b1124c05a89007bd00f29be3e1a42d7817458048
Author: Michael Meeks <michael.meeks at collabora.com>
Date: Wed Aug 23 17:01:18 2017 +0100
Deltas should be pixel based, add debugging.
Change-Id: I3b47b738ee71d015911e3d77b59b5f3cb34ecd75
diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index 67e7ac8e..5f9c9106 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -290,7 +290,7 @@ struct DeltaGenerator {
struct DeltaData {
TileWireId _wid;
- std::shared_ptr<std::vector<char>> _rawData;
+ std::shared_ptr<std::vector<uint32_t>> _rawData;
};
std::vector<DeltaData> _deltaEntries;
@@ -299,8 +299,10 @@ struct DeltaGenerator {
const DeltaData &curData,
std::vector<char>& output)
{
- std::vector<char> &prev = *prevData._rawData.get();
- std::vector<char> &cur = *curData._rawData.get();
+ std::vector<uint32_t> &prev = *prevData._rawData.get();
+ std::vector<uint32_t> &cur = *curData._rawData.get();
+
+ // FIXME: should we split and compress alpha separately ?
if (prev.size() != cur.size())
{
@@ -311,43 +313,59 @@ struct DeltaGenerator {
output.push_back('D');
LOG_TRC("building delta of " << prev.size() << "bytes");
// FIXME: really lame - some RLE might help etc.
- for (size_t i = 0; i < prev.size(); ++i)
+ for (size_t i = 0; i < prev.size();)
{
int sameCount = 0;
while (i + sameCount < prev.size() &&
prev[i+sameCount] == cur[i+sameCount])
{
- if (sameCount >= 127)
- break;
++sameCount;
}
if (sameCount > 0)
- output.push_back(sameCount);
- i += sameCount;
- LOG_TRC("identical " << sameCount << "bytes");
+ {
+#if 0
+ if (sameCount < 64)
+ output.push_back(sameCount);
+ else
+#endif
+ {
+ output.push_back(0x80 | 0x00); // long-same
+ output.push_back(sameCount & 0xff);
+ output.push_back(sameCount >> 8);
+ }
+ i += sameCount;
+ LOG_TRC("identical " << sameCount << "pixels");
+ }
int diffCount = 0;
while (i + diffCount < prev.size() &&
- (prev[i+diffCount] != cur[i+diffCount] || diffCount < 3))
+ (prev[i+diffCount] != cur[i+diffCount]))
{
- if (diffCount >= 127)
- break;
++diffCount;
}
if (diffCount > 0)
{
- output.push_back(diffCount | 0x80);
- for (int j = 0; j < diffCount; ++j)
- output.push_back(cur[i+j]);
+#if 0
+ if (diffCount < 64)
+ output.push_back(0x40 & diffCount);
+ else
+#endif
+ {
+ output.push_back(0x80 | 0x40); // long-diff
+ output.push_back(diffCount & 0xff);
+ output.push_back(diffCount >> 8);
+ }
+
+ output.insert(output.end(), (char *)&cur[i], (char *)&cur[i+diffCount]);
+ LOG_TRC("different " << diffCount << "pixels");
+ i += diffCount;
}
- LOG_TRC("different " << diffCount << "bytes");
- i += diffCount;
}
return true;
}
- std::shared_ptr<std::vector<char>> dataToVector(
+ std::shared_ptr<std::vector<uint32_t>> dataToVector(
unsigned char* pixmap, size_t startX, size_t startY,
int width, int height,
int bufferWidth, int bufferHeight)
@@ -355,16 +373,16 @@ struct DeltaGenerator {
assert (startX + width <= (size_t)bufferWidth);
assert (startY + height <= (size_t)bufferHeight);
- auto vector = std::make_shared<std::vector<char>>();
+ auto vector = std::make_shared<std::vector<uint32_t>>();
LOG_TRC("Converting data to vector of size "
<< (width * height * 4) << " width " << width
<< " height " << height);
- vector->resize(width * height * 4);
+ vector->resize(width * height);
for (int y = 0; y < height; ++y)
{
size_t position = ((startY + y) * bufferWidth * 4) + (startX * 4);
- memcpy(&(*vector)[y * width * 4], pixmap + position, width * 4);
+ memcpy(&(*vector)[y * width], pixmap + position, width * 4);
}
return vector;
diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index daca0b96..6d1077cb 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -13,6 +13,18 @@ if (typeof String.prototype.startsWith !== 'function') {
};
}
+function hex2string(inData)
+{
+ hexified = [];
+ data = new Uint8Array(inData);
+ for (var i = 0; i < data.length; i++) {
+ hex = data[i].toString(16);
+ paddedHex = ('00' + hex).slice(-2);
+ hexified.push(paddedHex);
+ }
+ return hexified.join('');
+}
+
L.Compatibility = {
clipboardGet: function (event) {
var text = null;
@@ -1283,7 +1295,6 @@ L.TileLayer = L.GridLayer.extend({
}
else if (tile && typeof(img) == 'object') {
// 'Uint8Array' delta
- console.log('hit here with a delta');
var canvas = document.createElement('canvas');
canvas.width = 256;
canvas.height = 256;
@@ -1298,19 +1309,48 @@ L.TileLayer = L.GridLayer.extend({
var delta = img;
var pixSize = canvas.width * canvas.height * 4;
var offset = 0;
+
+ console.log('Applying a delta of length ' + delta.length + ' pix size: ' + pixSize + '\nhex: ' + hex2string(delta));
+
+ // wipe to grey.
+// for (var i = 0; i < pixSize * 4; ++i)
+// {
+// imgData.data[i] = 128;
+// }
+
+ // Apply delta.
for (var i = 1; i < delta.length &&
- offset < pixSize; ++i)
+ offset < pixSize;)
{
- var span = delta[i] & 127;
- if (delta[i] & 128) { // changed run.
+// var span = delta[i];
+// var isChangedRun = span & 64;
+// if (span >= 128)
+// {
+ // first char is a control code.
+ var isChangedRun = delta[i++] & 64;
+ span = delta[i++];
+ span += delta[i++] * 256;
+// }
+ if (isChangedRun) {
console.log('apply new span of size ' + span + ' at offset ' + i + ' into delta at byte: ' + offset);
+ span *= 4;
while (span-- > 0) {
- imgData.data[offset++] = delta[++i];
+ imgData.data[offset++] = delta[i++];
}
+ imgData.data[offset - 2] = 256; // debug - blue terminator
} else {
- offset += span;
+ console.log('apply unchanged span of size ' + span + ' at offset ' + i + ' into delta at byte: ' + offset);
+ offset += span * 4;
+ imgData.data[offset - 3] = 256; // debug - green terminator
}
}
+
+ while (offset < pixSize) // debug
+ {
+ imgData.data[offset] = 256; // redden the remaining section.
+ imgData.data[offset+3] = 256; // with some alpha
+ offset += 4;
+ }
ctx.putImageData(imgData, 0, 0);
tile.oldWireId = tile.wireId;
commit 0bfbbf98510f82eb9a8ff3dc44254bd848458d53
Author: Michael Meeks <michael.meeks at collabora.com>
Date: Fri Jun 30 17:22:12 2017 +0100
Start of delta creator.
Change-Id: Idf186cda4f11e2418d9ff9f435825832c6b10294
diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index 1a7d87c1..67e7ac8e 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -285,6 +285,121 @@ namespace
#endif
}
+/// A quick and dirty delta generator for last tile changes
+struct DeltaGenerator {
+
+ struct DeltaData {
+ TileWireId _wid;
+ std::shared_ptr<std::vector<char>> _rawData;
+ };
+ std::vector<DeltaData> _deltaEntries;
+
+ bool makeDelta(
+ const DeltaData &prevData,
+ const DeltaData &curData,
+ std::vector<char>& output)
+ {
+ std::vector<char> &prev = *prevData._rawData.get();
+ std::vector<char> &cur = *curData._rawData.get();
+
+ if (prev.size() != cur.size())
+ {
+ LOG_ERR("mis-sized delta: " << prev.size() << " vs " << cur.size() << "bytes");
+ return false;
+ }
+
+ output.push_back('D');
+ LOG_TRC("building delta of " << prev.size() << "bytes");
+ // FIXME: really lame - some RLE might help etc.
+ for (size_t i = 0; i < prev.size(); ++i)
+ {
+ int sameCount = 0;
+ while (i + sameCount < prev.size() &&
+ prev[i+sameCount] == cur[i+sameCount])
+ {
+ if (sameCount >= 127)
+ break;
+ ++sameCount;
+ }
+ if (sameCount > 0)
+ output.push_back(sameCount);
+ i += sameCount;
+ LOG_TRC("identical " << sameCount << "bytes");
+
+ int diffCount = 0;
+ while (i + diffCount < prev.size() &&
+ (prev[i+diffCount] != cur[i+diffCount] || diffCount < 3))
+ {
+ if (diffCount >= 127)
+ break;
+ ++diffCount;
+ }
+
+ if (diffCount > 0)
+ {
+ output.push_back(diffCount | 0x80);
+ for (int j = 0; j < diffCount; ++j)
+ output.push_back(cur[i+j]);
+ }
+ LOG_TRC("different " << diffCount << "bytes");
+ i += diffCount;
+ }
+ return true;
+ }
+
+ std::shared_ptr<std::vector<char>> dataToVector(
+ unsigned char* pixmap, size_t startX, size_t startY,
+ int width, int height,
+ int bufferWidth, int bufferHeight)
+ {
+ assert (startX + width <= (size_t)bufferWidth);
+ assert (startY + height <= (size_t)bufferHeight);
+
+ auto vector = std::make_shared<std::vector<char>>();
+ LOG_TRC("Converting data to vector of size "
+ << (width * height * 4) << " width " << width
+ << " height " << height);
+
+ vector->resize(width * height * 4);
+ for (int y = 0; y < height; ++y)
+ {
+ size_t position = ((startY + y) * bufferWidth * 4) + (startX * 4);
+ memcpy(&(*vector)[y * width * 4], pixmap + position, width * 4);
+ }
+
+ return vector;
+ }
+
+ // Creates a bespoke delta file-format ...
+ bool createDelta(
+ unsigned char* pixmap, size_t startX, size_t startY,
+ int width, int height,
+ int bufferWidth, int bufferHeight,
+ std::vector<char>& output,
+ TileWireId wid, TileWireId oldWid)
+ {
+ // First store a copy for later:
+ if (_deltaEntries.size() > 6) // FIXME: hard-coded ...
+ _deltaEntries.erase(_deltaEntries.begin());
+
+ // FIXME: assuming width etc. are all constant & so on.
+ DeltaData reference;
+ reference._wid = wid;
+ reference._rawData = dataToVector(pixmap, startX, startY, width, height,
+ bufferWidth, bufferHeight);
+ _deltaEntries.push_back(reference);
+
+ for (auto &it : _deltaEntries)
+ {
+ if (oldWid == it._wid)
+ return makeDelta(reference, it, output);
+ }
+ return false;
+ }
+};
+
+
+
/// A quick & dirty cache of the last few PNGs
/// and their hashes to avoid re-compression
/// wherever possible.
@@ -310,6 +425,7 @@ class PngCache
size_t _cacheHits;
size_t _cacheTests;
TileWireId _nextId;
+ DeltaGenerator _deltaGen;
std::map< TileBinaryHash, CacheEntry > _cache;
std::map< TileWireId, TileBinaryHash > _wireToHash;
@@ -406,9 +522,15 @@ class PngCache
int width, int height,
int bufferWidth, int bufferHeight,
std::vector<char>& output, LibreOfficeKitTileMode mode,
- TileBinaryHash hash, TileWireId wid, TileWireId /* oldWid */)
+ TileBinaryHash hash, TileWireId wid, TileWireId oldWid)
{
LOG_DBG("PNG cache with hash " << hash << " missed.");
+ if (_deltaGen.createDelta(pixmap, startX, startY, width, height,
+ bufferWidth, bufferHeight,
+ output, wid, oldWid))
+ return true;
+
+ LOG_DBG("Encode a new png for this tile.");
CacheEntry newEntry(bufferWidth * bufferHeight * 1, wid);
if (Png::encodeSubBufferToPNG(pixmap, startX, startY, width, height,
bufferWidth, bufferHeight,
diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index 5adf34b8..36c75624 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -611,6 +611,7 @@ L.Socket = L.Class.extend({
else if (!textMsg.startsWith('tile:') && !textMsg.startsWith('renderfont:') && !textMsg.startsWith('dialogpaint:') && !textMsg.startsWith('dialogchildpaint:')) {
// log the tile msg separately as we need the tile coordinates
L.Log.log(textMsg, L.INCOMING);
+
if (imgBytes !== undefined) {
try {
// if it's not a tile, parse the whole message
@@ -628,12 +629,21 @@ L.Socket = L.Class.extend({
}
else {
var data = imgBytes.subarray(index + 1);
- // read the tile data
- var strBytes = '';
- for (var i = 0; i < data.length; i++) {
- strBytes += String.fromCharCode(data[i]);
+
+ if (data.length > 0 && data[0] == 68 /* D */)
+ {
+ console.log('Socket: got a delta !');
+ var img = data;
+ }
+ else
+ {
+ // read the tile data
+ var strBytes = '';
+ for (var i = 0; i < data.length; i++) {
+ strBytes += String.fromCharCode(data[i]);
+ }
+ var img = 'data:image/png;base64,' + window.btoa(strBytes);
}
- var img = 'data:image/png;base64,' + window.btoa(strBytes);
}
if (textMsg.startsWith('status:')) {
diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index 7ee9c90c..daca0b96 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -1281,6 +1281,44 @@ L.TileLayer = L.GridLayer.extend({
docType: this._docType
});
}
+ else if (tile && typeof(img) == 'object') {
+ // 'Uint8Array' delta
+ console.log('hit here with a delta');
+ var canvas = document.createElement('canvas');
+ canvas.width = 256;
+ canvas.height = 256;
+ var ctx = canvas.getContext('2d');
+
+ oldImg = new Image();
+ oldImg.src = tile.el.src;
+ ctx.drawImage(oldImg, 0, 0);
+
+ // FIXME; can we operate directly on the image ?
+ var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
+ var delta = img;
+ var pixSize = canvas.width * canvas.height * 4;
+ var offset = 0;
+ for (var i = 1; i < delta.length &&
+ offset < pixSize; ++i)
+ {
+ var span = delta[i] & 127;
+ if (delta[i] & 128) { // changed run.
+ console.log('apply new span of size ' + span + ' at offset ' + i + ' into delta at byte: ' + offset);
+ while (span-- > 0) {
+ imgData.data[offset++] = delta[++i];
+ }
+ } else {
+ offset += span;
+ }
+ }
+ ctx.putImageData(imgData, 0, 0);
+
+ tile.oldWireId = tile.wireId;
+ tile.wireId = command.wireId;
+ tile.el.src = canvas.toDataURL('image/png');
+
+ console.log('set new image');
+ }
else if (tile) {
if (command.wireId != undefined) {
tile.oldWireId = command.wireId;
More information about the Libreoffice-commits
mailing list