transport in rgba order

so if core is compiled with a cairo using rgba the pixels can
be sent without need to reorder in server or client

Signed-off-by: Caolán McNamara <caolan.mcnamara@collabora.com>
Change-Id: Iaf0410f1eaa605b9ce2716625f6c968bca523ccb
This commit is contained in:
Caolán McNamara 2023-06-27 09:23:09 +01:00 committed by Michael Meeks
parent 01165fbe1f
commit 76a5a9baff
5 changed files with 86 additions and 46 deletions

View file

@ -6742,36 +6742,24 @@ L.CanvasTileLayer = L.Layer.extend({
return ctx;
},
_brgatorgba: function(rawDelta) {
_unpremultiply: function(rawDelta) {
var len = rawDelta.byteLength / 4;
var delta32 = new Uint32Array(rawDelta.buffer, rawDelta.byteOffset, len);
var resultu32 = new Uint32Array(len);
var resultu8 = new Uint8ClampedArray(resultu32.buffer, resultu32.byteOffset, resultu32.byteLength);
for (var i32 = 0; i32 < len; ++i32) {
// premultiplied brga -> unpremultiplied rgba
// If the previous input pixel was the same as the current input pixel
// just copy the previous output pixel as the current output pixel
if (i32 > 0 && delta32[i32] === delta32[i32 - 1])
resultu32[i32] = resultu32[i32 - 1];
else
{
// premultiplied rgba -> unpremultiplied rgba
var alpha = delta32[i32] >>> 24;
if (alpha === 255) {
resultu32[i32] = delta32[i32];
}
else if (alpha !== 0) { // dest can remain at ctored 0 if alpha is 0
var i8 = i32 * 4;
var alpha = rawDelta[i8 + 3];
if (alpha === 255) {
resultu8[i8] = rawDelta[i8 + 2];
resultu8[i8 + 1] = rawDelta[i8 + 1];
resultu8[i8 + 2] = rawDelta[i8];
resultu8[i8 + 3] = 255;
}
else if (alpha === 0)
resultu32[i32] = 0;
else // forced to do the math
{
resultu8[i8] = Math.ceil(rawDelta[i8 + 2] * 255 / alpha);
resultu8[i8 + 1] = Math.ceil(rawDelta[i8 + 1] * 255 / alpha);
resultu8[i8 + 2] = Math.ceil(rawDelta[i8] * 255 / alpha);
resultu8[i8 + 3] = alpha;
}
// forced to do the math
resultu8[i8] = Math.ceil(rawDelta[i8] * 255 / alpha);
resultu8[i8 + 1] = Math.ceil(rawDelta[i8 + 1] * 255 / alpha);
resultu8[i8 + 2] = Math.ceil(rawDelta[i8 + 2] * 255 / alpha);
resultu8[i8 + 3] = alpha;
}
}
return resultu8;
@ -6853,7 +6841,7 @@ L.CanvasTileLayer = L.Layer.extend({
{
// FIXME: use zstd to de-compress directly into a Uint8ClampedArray
len = canvas.width * canvas.height * 4;
var pixelArray = this._brgatorgba(delta.subarray(0, len));
var pixelArray = this._unpremultiply(delta.subarray(0, len));
imgData = new ImageData(pixelArray, canvas.width, canvas.height);
if (this._debugDeltas)
@ -6953,7 +6941,7 @@ L.CanvasTileLayer = L.Layer.extend({
span *= 4;
// copy so this is suitably aligned for a Uint32Array view
var tmpu8 = new Uint8Array(delta.subarray(i, i + span));
var pixelData = this._brgatorgba(tmpu8);
var pixelData = this._unpremultiply(tmpu8);
// imgData.data[offset + 1] = 256; // debug - greener start
for (var j = 0; j < span; ++j)
imgData.data[offset++] = pixelData[j];

View file

@ -80,9 +80,9 @@ extern "C"
}
/* Unpremultiplies data and converts native endian ARGB => RGBA bytes */
/* Unpremultiplies data and converts native endian BGRA => RGBA bytes */
static void
unpremultiply_data (png_structp /*png*/, png_row_infop row_info, png_bytep data)
unpremultiply_bgra_data (png_structp /*png*/, png_row_infop row_info, png_bytep data)
{
unsigned int i;
@ -116,6 +116,40 @@ unpremultiply_data (png_structp /*png*/, png_row_infop row_info, png_bytep data)
}
}
/* Unpremultiplies data already in RGBA order*/
static void
unpremultiply_rgba_data (png_structp /*png*/, png_row_infop row_info, png_bytep data)
{
unsigned int i;
for (i = 0; i < row_info->rowbytes; i += 4)
{
uint8_t *b = &data[i];
uint32_t pix;
uint8_t alpha;
std::memcpy (&pix, b, sizeof (uint32_t));
alpha = (pix & 0xff000000) >> 24;
if (alpha == 255)
{
std::memcpy(b, &pix, sizeof (uint32_t));
}
else if (alpha == 0)
{
b[0] = b[1] = b[2] = b[3] = 0;
}
else
{
b[0] = (((pix & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha;
b[1] = (((pix & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha;
b[2] = (((pix & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
b[3] = alpha;
}
}
}
/// This function uses setjmp which may clobbers non-trivial objects.
/// So we can't use logging or create complex C++ objects in this frame.
/// Specifically, logging uses std::string objects, and GCC gives the following:
@ -161,9 +195,14 @@ inline bool impl_encodeSubBufferToPNG(unsigned char* pixmap, size_t startX, size
png_write_info(png_ptr, info_ptr);
if (mode == LOK_TILEMODE_BGRA)
switch (mode)
{
png_set_write_user_transform_fn (png_ptr, unpremultiply_data);
case LOK_TILEMODE_BGRA:
png_set_write_user_transform_fn (png_ptr, unpremultiply_bgra_data);
break;
case LOK_TILEMODE_RGBA:
png_set_write_user_transform_fn (png_ptr, unpremultiply_rgba_data);
break;
}
for (int y = 0; y < height; ++y)

View file

@ -146,7 +146,8 @@ class DeltaGenerator {
// Create a diff from our state to new state in curRow
void diffRowTo(const DeltaBitmapRow &curRow,
const int width, const int curY,
std::vector<uint8_t> &output) const
std::vector<uint8_t> &output,
LibreOfficeKitTileMode mode) const
{
PixIterator oldPixels(*this);
PixIterator curPixels(curRow);
@ -188,7 +189,7 @@ class DeltaGenerator {
copy_row(reinterpret_cast<unsigned char *>(&output[dest]),
(const unsigned char *)(scratch),
diff);
diff, mode);
LOG_TRC("row " << curY << " different " << diff << "pixels");
x += diff;
@ -352,15 +353,26 @@ class DeltaGenerator {
}
static void
copy_row (unsigned char *dest, const unsigned char *srcBytes, unsigned int count)
copy_row (unsigned char *dest, const unsigned char *srcBytes, unsigned int count, LibreOfficeKitTileMode mode)
{
std::memcpy(dest, srcBytes, count * 4);
switch (mode)
{
case LOK_TILEMODE_RGBA:
std::memcpy(dest, srcBytes, count * 4);
break;
case LOK_TILEMODE_BGRA:
std::memcpy(dest, srcBytes, count * 4);
for (size_t j = 0; j < count * 4; j += 4)
std::swap(dest[j], dest[j+2]);
break;
}
}
bool makeDelta(
const DeltaData &prev,
const DeltaData &cur,
std::vector<char>& outStream)
std::vector<char>& outStream,
LibreOfficeKitTileMode mode)
{
// TODO: should we split and compress alpha separately ?
if (prev.getWidth() != cur.getWidth() || prev.getHeight() != cur.getHeight())
@ -430,7 +442,7 @@ class DeltaGenerator {
continue;
// Our row is just that different:
prev.getRow(y).diffRowTo(cur.getRow(y), prev.getWidth(), y, output);
prev.getRow(y).diffRowTo(cur.getRow(y), prev.getWidth(), y, output, mode);
}
LOG_TRC("Created delta of size " << output.size());
if (output.empty())
@ -520,7 +532,8 @@ class DeltaGenerator {
int bufferWidth, int bufferHeight,
const TileLocation &loc,
std::vector<char>& output,
TileWireId wid, bool forceKeyframe)
TileWireId wid, bool forceKeyframe,
LibreOfficeKitTileMode mode)
{
if ((width & 0x1) != 0) // power of two - RGBA
{
@ -557,7 +570,7 @@ class DeltaGenerator {
bool delta = false;
if (!forceKeyframe)
delta = makeDelta(*cacheEntry, *update, output);
delta = makeDelta(*cacheEntry, *update, output, mode);
// no two threads can be working on the same DeltaData.
cacheEntry->replaceAndFree(update);
@ -629,7 +642,7 @@ class DeltaGenerator {
if (!createDelta(pixmap, startX, startY, width, height,
bufferWidth, bufferHeight,
loc, output, wid, forceKeyframe))
loc, output, wid, forceKeyframe, mode))
{
// FIXME: should stream it in =)
size_t maxCompressed = ZSTD_COMPRESSBOUND((size_t)width * height * 4);
@ -655,7 +668,7 @@ class DeltaGenerator {
// FIXME: should we RLE in pixels first ?
for (int y = 0; y < height; ++y)
{
copy_row(fixedupLine, pixmap + ((startY + y) * bufferWidth * 4) + (startX * 4), width);
copy_row(fixedupLine, pixmap + ((startY + y) * bufferWidth * 4) + (startX * 4), width, mode);
ZSTD_inBuffer inb;
inb.src = fixedupLine;

View file

@ -357,7 +357,7 @@ static void doc_paintPartTile(LibreOfficeKitDocument* pThis,
static int doc_getTileMode(LibreOfficeKitDocument* /*pThis*/)
{
return LOK_TILEMODE_BGRA;
return LOK_TILEMODE_RGBA;
}
static void doc_getDocumentSize(LibreOfficeKitDocument* pThis,

View file

@ -399,14 +399,14 @@ void DeltaTests::testDeltaSequence()
LOK_ASSERT(gen.createDelta(
reinterpret_cast<unsigned char *>(&text[0]),
0, 0, width, height, width, height,
TileLocation(1, 2, 3, 0, 1), delta, textWid, false) == false);
TileLocation(1, 2, 3, 0, 1), delta, textWid, false, LOK_TILEMODE_RGBA) == false);
LOK_ASSERT(delta.empty());
// Build a delta between text2 & textWid
LOK_ASSERT(gen.createDelta(
reinterpret_cast<unsigned char *>(&text2[0]),
0, 0, width, height, width, height,
TileLocation(1, 2, 3, 0, 1), delta, text2Wid, false) == true);
TileLocation(1, 2, 3, 0, 1), delta, text2Wid, false, LOK_TILEMODE_RGBA) == true);
LOK_ASSERT(delta.size() > 0);
checkzDelta(delta, "text2 to textWid");
@ -419,7 +419,7 @@ void DeltaTests::testDeltaSequence()
LOK_ASSERT(gen.createDelta(
reinterpret_cast<unsigned char *>(&text[0]),
0, 0, width, height, width, height,
TileLocation(1, 2, 3, 0, 1), two2one, textWid, false) == true);
TileLocation(1, 2, 3, 0, 1), two2one, textWid, false, LOK_TILEMODE_RGBA) == true);
LOK_ASSERT(two2one.size() > 0);
checkzDelta(two2one, "text to text2Wid");
@ -458,14 +458,14 @@ void DeltaTests::testDeltaCopyOutOfBounds()
LOK_ASSERT(gen.createDelta(
reinterpret_cast<unsigned char *>(&text[0]),
0, 0, width, height, width, height,
TileLocation(1, 2, 3, 0, 1), delta, textWid, false) == false);
TileLocation(1, 2, 3, 0, 1), delta, textWid, false, LOK_TILEMODE_RGBA) == false);
LOK_ASSERT(delta.empty());
// Build a delta between the two frames
LOK_ASSERT(gen.createDelta(
reinterpret_cast<unsigned char *>(&text2[0]),
0, 0, width, height, width, height,
TileLocation(1, 2, 3, 0, 1), delta, text2Wid, false) == true);
TileLocation(1, 2, 3, 0, 1), delta, text2Wid, false, LOK_TILEMODE_RGBA) == true);
LOK_ASSERT(delta.size() > 0);
checkzDelta(delta, "copy out of bounds");