mirror of https://github.com/KLayout/klayout.git
Add PixelBuffer#from_bytes and width/height header to to_bytes
This commit is contained in:
parent
35ec1107b6
commit
2fa7b24345
|
|
@ -126,12 +126,32 @@ static std::vector<char> pixel_buffer_to_png (const tl::PixelBuffer *pb)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the raw ARGB32 pixel data as a byte vector (no encoding overhead)
|
// Returns an 8-byte header (width, height as uint32) followed by the raw ARGB32 pixel data
|
||||||
static std::vector<char> pixel_buffer_to_bytes (const tl::PixelBuffer *pb)
|
static std::vector<char> pixel_buffer_to_bytes (const tl::PixelBuffer *pb)
|
||||||
{
|
{
|
||||||
const char *p = (const char *) pb->data ();
|
uint32_t w = pb->width (), h = pb->height ();
|
||||||
size_t n = (size_t) pb->width () * (size_t) pb->height () * 4;
|
size_t n = (size_t) w * (size_t) h * 4;
|
||||||
return std::vector<char> (p, p + n);
|
std::vector<char> data (8 + n);
|
||||||
|
memcpy (data.data (), &w, 4);
|
||||||
|
memcpy (data.data () + 4, &h, 4);
|
||||||
|
memcpy (data.data () + 8, pb->data (), n);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inverse of to_bytes: reads width/height from the header and checks the length
|
||||||
|
static tl::PixelBuffer pixel_buffer_from_bytes (const std::vector<char> &data)
|
||||||
|
{
|
||||||
|
uint32_t w = 0, h = 0;
|
||||||
|
if (data.size () >= 8) {
|
||||||
|
memcpy (&w, data.data (), 4);
|
||||||
|
memcpy (&h, data.data () + 4, 4);
|
||||||
|
}
|
||||||
|
if (data.size () != 8 + (size_t) w * (size_t) h * 4) {
|
||||||
|
throw tl::Exception (tl::to_string (tr ("Invalid PixelBuffer byte stream: length does not match the width and height in the header")));
|
||||||
|
}
|
||||||
|
tl::PixelBuffer pb (w, h, (const tl::color_t *) (data.data () + 8));
|
||||||
|
pb.set_transparent (true);
|
||||||
|
return pb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -199,10 +219,19 @@ Class<tl::PixelBuffer> decl_PixelBuffer ("lay", "PixelBuffer",
|
||||||
gsi::method_ext ("to_bytes", &pixel_buffer_to_bytes,
|
gsi::method_ext ("to_bytes", &pixel_buffer_to_bytes,
|
||||||
"@brief Converts the pixel buffer to a raw byte stream\n"
|
"@brief Converts the pixel buffer to a raw byte stream\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Returns the raw ARGB32 pixel data with 4 bytes per pixel in row-major order, "
|
"The stream starts with an 8-byte header (width and height as 32-bit unsigned integers) "
|
||||||
"top to bottom. Unlike \\to_png_data this method has zero encoding overhead.\n"
|
"followed by the raw ARGB32 pixel data with 4 bytes per pixel in row-major order, "
|
||||||
|
"top to bottom. Unlike \\to_png_data this method has zero encoding overhead. "
|
||||||
|
"Use \\from_bytes to reconstruct the pixel buffer.\n"
|
||||||
"\n"
|
"\n"
|
||||||
"This method has been added in version 0.30.9."
|
"This method has been added in version 0.30.10."
|
||||||
|
) +
|
||||||
|
gsi::method ("from_bytes", &pixel_buffer_from_bytes, gsi::arg ("data"),
|
||||||
|
"@brief Reconstructs a pixel buffer from a byte stream produced by \\to_bytes\n"
|
||||||
|
"\n"
|
||||||
|
"The width and height are taken from the header and the stream length is checked against them.\n"
|
||||||
|
"\n"
|
||||||
|
"This method has been added in version 0.30.10."
|
||||||
) +
|
) +
|
||||||
gsi::method ("patch", &tl::PixelBuffer::patch, gsi::arg ("other"),
|
gsi::method ("patch", &tl::PixelBuffer::patch, gsi::arg ("other"),
|
||||||
"@brief Patches another pixel buffer into this one\n"
|
"@brief Patches another pixel buffer into this one\n"
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,27 @@ class LAYPixelBufferTests(unittest.TestCase):
|
||||||
pb_copy = pya.PixelBuffer.read_png(tmp)
|
pb_copy = pya.PixelBuffer.read_png(tmp)
|
||||||
self.assertEqual(compare(pb, pb_copy), True)
|
self.assertEqual(compare(pb, pb_copy), True)
|
||||||
|
|
||||||
|
def test_2(self):
|
||||||
|
|
||||||
|
# to_bytes / from_bytes round-trip
|
||||||
|
|
||||||
|
import struct
|
||||||
|
|
||||||
|
pb = pya.PixelBuffer(10, 20)
|
||||||
|
pb.transparent = True
|
||||||
|
pb.fill(0xf0010203)
|
||||||
|
pb.set_pixel(1, 2, 0x80102030)
|
||||||
|
|
||||||
|
data = bytes(pb.to_bytes())
|
||||||
|
self.assertEqual(len(data), 8 + 10 * 20 * 4)
|
||||||
|
self.assertEqual(struct.unpack("=II", data[0:8]), (10, 20)) # width, height header
|
||||||
|
|
||||||
|
self.assertEqual(pb == pya.PixelBuffer.from_bytes(data), True)
|
||||||
|
|
||||||
|
# a mismatched stream is rejected
|
||||||
|
with self.assertRaises(Exception):
|
||||||
|
pya.PixelBuffer.from_bytes(data[:-4])
|
||||||
|
|
||||||
|
|
||||||
# run unit tests
|
# run unit tests
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
||||||
|
|
@ -152,6 +152,32 @@ class LAYPixelBuffer_TestClass < TestBase
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_5
|
||||||
|
|
||||||
|
# to_bytes / from_bytes round-trip
|
||||||
|
|
||||||
|
pb = RBA::PixelBuffer::new(10, 20)
|
||||||
|
pb.transparent = true
|
||||||
|
pb.fill(0xf0010203)
|
||||||
|
pb.set_pixel(1, 2, 0x80102030)
|
||||||
|
|
||||||
|
bytes = pb.to_bytes
|
||||||
|
assert_equal(bytes.size, 8 + 10 * 20 * 4)
|
||||||
|
assert_equal(bytes[0, 8].unpack("VV"), [ 10, 20 ]) # width, height header
|
||||||
|
|
||||||
|
assert_equal(pb == RBA::PixelBuffer.from_bytes(bytes), true)
|
||||||
|
|
||||||
|
# a mismatched stream is rejected
|
||||||
|
error = false
|
||||||
|
begin
|
||||||
|
RBA::PixelBuffer.from_bytes(bytes[0, bytes.size - 4])
|
||||||
|
rescue
|
||||||
|
error = true
|
||||||
|
end
|
||||||
|
assert_equal(error, true)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
def test_11
|
def test_11
|
||||||
|
|
||||||
pb = RBA::BitmapBuffer::new
|
pb = RBA::BitmapBuffer::new
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue