Message ID | 20220322104953.27731-2-kshitij.suri@nutanix.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [v2,1/2] Replacing CONFIG_VNC_PNG with CONFIG_PNG | expand |
Hi, Hope this mail finds you well. I have updated the code as required and would be grateful if you could review and suggest changes that are needed to be implemented. In case no change is required, please do let me know the next steps for the same. Regards, Kshitij Suri On 22/03/22 4:19 pm, Kshitij Suri wrote: > Currently screendump only supports PPM format, which is un-compressed and not > standard. Added a "format" parameter to qemu monitor screendump capabilites > to support PNG image capture using libpng. The param was added in QAPI schema > of screendump present in ui.json along with png_save() function which converts > pixman_image to PNG. HMP command equivalent was also modified to support the > feature. > > Example usage: > { "execute": "screendump", "arguments": { "filename": "/tmp/image", > "format":"png" } } > > Resolves: https://gitlab.com/qemu-project/qemu/-/issues/718 > > Signed-off-by: Kshitij Suri <kshitij.suri@nutanix.com> > --- > diff to v1: > - Removed repeated alpha conversion operation. > - Modified logic to mirror png conversion in vnc-enc-tight.c file. > - Added a new CONFIG_PNG parameter for libpng support. > - Changed input format to enum instead of string. > - Improved error handling. > hmp-commands.hx | 11 ++--- > monitor/hmp-cmds.c | 12 +++++- > qapi/ui.json | 24 +++++++++-- > ui/console.c | 101 +++++++++++++++++++++++++++++++++++++++++++-- > 4 files changed, 136 insertions(+), 12 deletions(-) > > diff --git a/hmp-commands.hx b/hmp-commands.hx > index 8476277aa9..19b7cab595 100644 > --- a/hmp-commands.hx > +++ b/hmp-commands.hx > @@ -244,11 +244,12 @@ ERST > > { > .name = "screendump", > - .args_type = "filename:F,device:s?,head:i?", > - .params = "filename [device [head]]", > - .help = "save screen from head 'head' of display device 'device' " > - "into PPM image 'filename'", > - .cmd = hmp_screendump, > + .args_type = "filename:F,format:s?,device:s?,head:i?", > + .params = "filename [format] [device [head]]", > + .help = "save screen from head 'head' of display device 'device'" > + "in specified format 'format' as image 'filename'." > + "Currently only 'png' and 'ppm' formats are supported.", > + .cmd = hmp_screendump, > .coroutine = true, > }, > > diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c > index 634968498b..2442bfa989 100644 > --- a/monitor/hmp-cmds.c > +++ b/monitor/hmp-cmds.c > @@ -1720,9 +1720,19 @@ hmp_screendump(Monitor *mon, const QDict *qdict) > const char *filename = qdict_get_str(qdict, "filename"); > const char *id = qdict_get_try_str(qdict, "device"); > int64_t head = qdict_get_try_int(qdict, "head", 0); > + const char *input_format = qdict_get_try_str(qdict, "format"); > Error *err = NULL; > + ImageFormat format; > > - qmp_screendump(filename, id != NULL, id, id != NULL, head, &err); > + format = qapi_enum_parse(&ImageFormat_lookup, input_format, > + IMAGE_FORMAT_PPM, &err); > + if (err) { > + goto end; > + } > + > + qmp_screendump(filename, id != NULL, id, id != NULL, head, > + input_format != NULL, format, &err); > +end: > hmp_handle_error(mon, err); > } > > diff --git a/qapi/ui.json b/qapi/ui.json > index 664da9e462..e8060d6b3c 100644 > --- a/qapi/ui.json > +++ b/qapi/ui.json > @@ -157,12 +157,27 @@ > ## > { 'command': 'expire_password', 'boxed': true, 'data': 'ExpirePasswordOptions' } > > +## > +# @ImageFormat: > +# > +# Supported image format types. > +# > +# @png: PNG format > +# > +# @ppm: PPM format > +# > +# Since: 7.0 > +# > +## > +{ 'enum': 'ImageFormat', > + 'data': ['ppm', 'png'] } > + > ## > # @screendump: > # > -# Write a PPM of the VGA screen to a file. > +# Capture the contents of a screen and write it to a file. > # > -# @filename: the path of a new PPM file to store the image > +# @filename: the path of a new file to store the image > # > # @device: ID of the display device that should be dumped. If this parameter > # is missing, the primary display will be used. (Since 2.12) > @@ -171,6 +186,8 @@ > # parameter is missing, head #0 will be used. Also note that the head > # can only be specified in conjunction with the device ID. (Since 2.12) > # > +# @format: image format for screendump is specified. (default: ppm) (Since 7.0) > +# > # Returns: Nothing on success > # > # Since: 0.14 > @@ -183,7 +200,8 @@ > # > ## > { 'command': 'screendump', > - 'data': {'filename': 'str', '*device': 'str', '*head': 'int'}, > + 'data': {'filename': 'str', '*device': 'str', '*head': 'int', > + '*format': 'ImageFormat'}, > 'coroutine': true } > > ## > diff --git a/ui/console.c b/ui/console.c > index da434ce1b2..f42f64d556 100644 > --- a/ui/console.c > +++ b/ui/console.c > @@ -37,6 +37,9 @@ > #include "exec/memory.h" > #include "io/channel-file.h" > #include "qom/object.h" > +#ifdef CONFIG_PNG > +#include <png.h> > +#endif > > #define DEFAULT_BACKSCROLL 512 > #define CONSOLE_CURSOR_PERIOD 500 > @@ -291,6 +294,89 @@ void graphic_hw_invalidate(QemuConsole *con) > } > } > > +#ifdef CONFIG_PNG > +/** > + * png_save: Take a screenshot as PNG > + * > + * Saves screendump as a PNG file > + * > + * Returns true for success or false for error. > + * > + * @fd: File descriptor for PNG file. > + * @image: Image data in pixman format. > + * @errp: Pointer to an error. > + */ > +static bool png_save(int fd, pixman_image_t *image, Error **errp) > +{ > + int width = pixman_image_get_width(image); > + int height = pixman_image_get_height(image); > + g_autofree png_struct *png_ptr = NULL; > + g_autofree png_info *info_ptr = NULL; > + g_autoptr(pixman_image_t) linebuf = > + qemu_pixman_linebuf_create(PIXMAN_a8r8g8b8, width); > + uint8_t *buf = (uint8_t *)pixman_image_get_data(linebuf); > + FILE *f = fdopen(fd, "wb"); > + int y; > + if (!f) { > + error_setg_errno(errp, errno, > + "Failed to create file from file descriptor"); > + return false; > + } > + > + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, > + NULL, NULL); > + if (!png_ptr) { > + error_setg(errp, "PNG creation failed. Unable to write struct"); > + fclose(f); > + return false; > + } > + > + info_ptr = png_create_info_struct(png_ptr); > + > + if (!info_ptr) { > + error_setg(errp, "PNG creation failed. Unable to write info"); > + fclose(f); > + png_destroy_write_struct(&png_ptr, &info_ptr); > + return false; > + } > + > + png_init_io(png_ptr, f); > + > + png_set_IHDR(png_ptr, info_ptr, width, height, 8, > + PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, > + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); > + > + png_write_info(png_ptr, info_ptr); > + > + for (y = 0; y < height; ++y) { > + qemu_pixman_linebuf_fill(linebuf, image, width, 0, y); > + png_write_row(png_ptr, buf); > + } > + qemu_pixman_image_unref(linebuf); > + > + png_write_end(png_ptr, NULL); > + > + png_destroy_write_struct(&png_ptr, &info_ptr); > + > + if (fclose(f) != 0) { > + error_setg_errno(errp, errno, > + "PNG creation failed. Unable to close file"); > + return false; > + } > + > + return true; > +} > + > +#else /* no png support */ > + > +static bool png_save(int fd, pixman_image_t *image, Error **errp) > +{ > + error_setg(errp, "Enable PNG support with libpng for screendump"); > + return false; > +} > + > +#endif /* CONFIG_PNG */ > + > static bool ppm_save(int fd, pixman_image_t *image, Error **errp) > { > int width = pixman_image_get_width(image); > @@ -329,7 +415,8 @@ static void graphic_hw_update_bh(void *con) > /* Safety: coroutine-only, concurrent-coroutine safe, main thread only */ > void coroutine_fn > qmp_screendump(const char *filename, bool has_device, const char *device, > - bool has_head, int64_t head, Error **errp) > + bool has_head, int64_t head, > + bool has_format, ImageFormat format, Error **errp) > { > g_autoptr(pixman_image_t) image = NULL; > QemuConsole *con; > @@ -385,8 +472,16 @@ qmp_screendump(const char *filename, bool has_device, const char *device, > * yields and releases the BQL. It could produce corrupted dump, but > * it should be otherwise safe. > */ > - if (!ppm_save(fd, image, errp)) { > - qemu_unlink(filename); > + if (has_format && format == IMAGE_FORMAT_PNG) { > + /* PNG format specified for screendump */ > + if (!png_save(fd, image, errp)) { > + qemu_unlink(filename); > + } > + } else { > + /* PPM format specified/default for screendump */ > + if (!ppm_save(fd, image, errp)) { > + qemu_unlink(filename); > + } > } > } >
On Tue, Mar 22, 2022 at 10:49:53AM +0000, Kshitij Suri wrote: > Currently screendump only supports PPM format, which is un-compressed and not > standard. Added a "format" parameter to qemu monitor screendump capabilites > to support PNG image capture using libpng. The param was added in QAPI schema > of screendump present in ui.json along with png_save() function which converts > pixman_image to PNG. HMP command equivalent was also modified to support the > feature. > > Example usage: > { "execute": "screendump", "arguments": { "filename": "/tmp/image", > "format":"png" } } > > Resolves: https://gitlab.com/qemu-project/qemu/-/issues/718 > > Signed-off-by: Kshitij Suri <kshitij.suri@nutanix.com> > --- > diff to v1: > - Removed repeated alpha conversion operation. > - Modified logic to mirror png conversion in vnc-enc-tight.c file. > - Added a new CONFIG_PNG parameter for libpng support. > - Changed input format to enum instead of string. > - Improved error handling. > hmp-commands.hx | 11 ++--- > monitor/hmp-cmds.c | 12 +++++- > qapi/ui.json | 24 +++++++++-- > ui/console.c | 101 +++++++++++++++++++++++++++++++++++++++++++-- > 4 files changed, 136 insertions(+), 12 deletions(-) > diff --git a/qapi/ui.json b/qapi/ui.json > index 664da9e462..e8060d6b3c 100644 > --- a/qapi/ui.json > +++ b/qapi/ui.json > @@ -157,12 +157,27 @@ > ## > { 'command': 'expire_password', 'boxed': true, 'data': 'ExpirePasswordOptions' } > > +## > +# @ImageFormat: > +# > +# Supported image format types. > +# > +# @png: PNG format > +# > +# @ppm: PPM format > +# > +# Since: 7.0 This will probably end up being 7.1 at this point. > +# > +## > +{ 'enum': 'ImageFormat', > + 'data': ['ppm', 'png'] } > + > ## > # @screendump: > # > -# Write a PPM of the VGA screen to a file. > +# Capture the contents of a screen and write it to a file. > # > -# @filename: the path of a new PPM file to store the image > +# @filename: the path of a new file to store the image > # > # @device: ID of the display device that should be dumped. If this parameter > # is missing, the primary display will be used. (Since 2.12) > @@ -171,6 +186,8 @@ > # parameter is missing, head #0 will be used. Also note that the head > # can only be specified in conjunction with the device ID. (Since 2.12) > # > +# @format: image format for screendump is specified. (default: ppm) (Since 7.0) Likewise probably 7.1 None the less, Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> With regards, Daniel
On 28/03/22 3:22 pm, Daniel P. Berrangé wrote: > On Tue, Mar 22, 2022 at 10:49:53AM +0000, Kshitij Suri wrote: >> Currently screendump only supports PPM format, which is un-compressed and not >> standard. Added a "format" parameter to qemu monitor screendump capabilites >> to support PNG image capture using libpng. The param was added in QAPI schema >> of screendump present in ui.json along with png_save() function which converts >> pixman_image to PNG. HMP command equivalent was also modified to support the >> feature. >> >> Example usage: >> { "execute": "screendump", "arguments": { "filename": "/tmp/image", >> "format":"png" } } >> >> Resolves: https://urldefense.proofpoint.com/v2/url?u=https-3A__gitlab.com_qemu-2Dproject_qemu_-2D_issues_718&d=DwIDaQ&c=s883GpUCOChKOHiocYtGcg&r=utjv19Ej9Fb0TB7_DX0o3faQ-OAm2ypPniPyqVSoj_w&m=SfkDKyBHSRlW9cvs1qhKamw8p2-qgFH_XQcMVSwM5IPIjGQm0Emsj496xu-fpwaw&s=4EZwj8JQje-qxGhg2-Vv81iA-atUQTCNNBnlk1AW7do&e= >> >> Signed-off-by: Kshitij Suri <kshitij.suri@nutanix.com> >> --- >> diff to v1: >> - Removed repeated alpha conversion operation. >> - Modified logic to mirror png conversion in vnc-enc-tight.c file. >> - Added a new CONFIG_PNG parameter for libpng support. >> - Changed input format to enum instead of string. >> - Improved error handling. >> hmp-commands.hx | 11 ++--- >> monitor/hmp-cmds.c | 12 +++++- >> qapi/ui.json | 24 +++++++++-- >> ui/console.c | 101 +++++++++++++++++++++++++++++++++++++++++++-- >> 4 files changed, 136 insertions(+), 12 deletions(-) > >> diff --git a/qapi/ui.json b/qapi/ui.json >> index 664da9e462..e8060d6b3c 100644 >> --- a/qapi/ui.json >> +++ b/qapi/ui.json >> @@ -157,12 +157,27 @@ >> ## >> { 'command': 'expire_password', 'boxed': true, 'data': 'ExpirePasswordOptions' } >> >> +## >> +# @ImageFormat: >> +# >> +# Supported image format types. >> +# >> +# @png: PNG format >> +# >> +# @ppm: PPM format >> +# >> +# Since: 7.0 > This will probably end up being 7.1 at this point. Oh okay. Will update the version >> +# >> +## >> +{ 'enum': 'ImageFormat', >> + 'data': ['ppm', 'png'] } >> + >> ## >> # @screendump: >> # >> -# Write a PPM of the VGA screen to a file. >> +# Capture the contents of a screen and write it to a file. >> # >> -# @filename: the path of a new PPM file to store the image >> +# @filename: the path of a new file to store the image >> # >> # @device: ID of the display device that should be dumped. If this parameter >> # is missing, the primary display will be used. (Since 2.12) >> @@ -171,6 +186,8 @@ >> # parameter is missing, head #0 will be used. Also note that the head >> # can only be specified in conjunction with the device ID. (Since 2.12) >> # >> +# @format: image format for screendump is specified. (default: ppm) (Since 7.0) > Likewise probably 7.1 > > > None the less, > > Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> > > > > With regards, > Daniel Thank you for your review! Will update the version and send it in the following patch Regards, Kshitij Suri
diff to v1: - Removed repeated alpha conversion operation. - Modified logic to mirror png conversion in vnc-enc-tight.c file. - Added a new CONFIG_PNG parameter for libpng support. - Changed input format to enum instead of string. - Improved error handling. hmp-commands.hx | 11 ++--- monitor/hmp-cmds.c | 12 +++++- qapi/ui.json | 24 +++++++++-- ui/console.c | 101 +++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 136 insertions(+), 12 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index 8476277aa9..19b7cab595 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -244,11 +244,12 @@ ERST { .name = "screendump", - .args_type = "filename:F,device:s?,head:i?", - .params = "filename [device [head]]", - .help = "save screen from head 'head' of display device 'device' " - "into PPM image 'filename'", - .cmd = hmp_screendump, + .args_type = "filename:F,format:s?,device:s?,head:i?", + .params = "filename [format] [device [head]]", + .help = "save screen from head 'head' of display device 'device'" + "in specified format 'format' as image 'filename'." + "Currently only 'png' and 'ppm' formats are supported.", + .cmd = hmp_screendump, .coroutine = true, }, diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index 634968498b..2442bfa989 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -1720,9 +1720,19 @@ hmp_screendump(Monitor *mon, const QDict *qdict) const char *filename = qdict_get_str(qdict, "filename"); const char *id = qdict_get_try_str(qdict, "device"); int64_t head = qdict_get_try_int(qdict, "head", 0); + const char *input_format = qdict_get_try_str(qdict, "format"); Error *err = NULL; + ImageFormat format; - qmp_screendump(filename, id != NULL, id, id != NULL, head, &err); + format = qapi_enum_parse(&ImageFormat_lookup, input_format, + IMAGE_FORMAT_PPM, &err); + if (err) { + goto end; + } + + qmp_screendump(filename, id != NULL, id, id != NULL, head, + input_format != NULL, format, &err); +end: hmp_handle_error(mon, err); } diff --git a/qapi/ui.json b/qapi/ui.json index 664da9e462..e8060d6b3c 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -157,12 +157,27 @@ ## { 'command': 'expire_password', 'boxed': true, 'data': 'ExpirePasswordOptions' } +## +# @ImageFormat: +# +# Supported image format types. +# +# @png: PNG format +# +# @ppm: PPM format +# +# Since: 7.0 +# +## +{ 'enum': 'ImageFormat', + 'data': ['ppm', 'png'] } + ## # @screendump: # -# Write a PPM of the VGA screen to a file. +# Capture the contents of a screen and write it to a file. # -# @filename: the path of a new PPM file to store the image +# @filename: the path of a new file to store the image # # @device: ID of the display device that should be dumped. If this parameter # is missing, the primary display will be used. (Since 2.12) @@ -171,6 +186,8 @@ # parameter is missing, head #0 will be used. Also note that the head # can only be specified in conjunction with the device ID. (Since 2.12) # +# @format: image format for screendump is specified. (default: ppm) (Since 7.0) +# # Returns: Nothing on success # # Since: 0.14 @@ -183,7 +200,8 @@ # ## { 'command': 'screendump', - 'data': {'filename': 'str', '*device': 'str', '*head': 'int'}, + 'data': {'filename': 'str', '*device': 'str', '*head': 'int', + '*format': 'ImageFormat'}, 'coroutine': true } ## diff --git a/ui/console.c b/ui/console.c index da434ce1b2..f42f64d556 100644 --- a/ui/console.c +++ b/ui/console.c @@ -37,6 +37,9 @@ #include "exec/memory.h" #include "io/channel-file.h" #include "qom/object.h" +#ifdef CONFIG_PNG +#include <png.h> +#endif #define DEFAULT_BACKSCROLL 512 #define CONSOLE_CURSOR_PERIOD 500 @@ -291,6 +294,89 @@ void graphic_hw_invalidate(QemuConsole *con) } } +#ifdef CONFIG_PNG +/** + * png_save: Take a screenshot as PNG + * + * Saves screendump as a PNG file + * + * Returns true for success or false for error. + * + * @fd: File descriptor for PNG file. + * @image: Image data in pixman format. + * @errp: Pointer to an error. + */ +static bool png_save(int fd, pixman_image_t *image, Error **errp) +{ + int width = pixman_image_get_width(image); + int height = pixman_image_get_height(image); + g_autofree png_struct *png_ptr = NULL; + g_autofree png_info *info_ptr = NULL; + g_autoptr(pixman_image_t) linebuf = + qemu_pixman_linebuf_create(PIXMAN_a8r8g8b8, width); + uint8_t *buf = (uint8_t *)pixman_image_get_data(linebuf); + FILE *f = fdopen(fd, "wb"); + int y; + if (!f) { + error_setg_errno(errp, errno, + "Failed to create file from file descriptor"); + return false; + } + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, + NULL, NULL); + if (!png_ptr) { + error_setg(errp, "PNG creation failed. Unable to write struct"); + fclose(f); + return false; + } + + info_ptr = png_create_info_struct(png_ptr); + + if (!info_ptr) { + error_setg(errp, "PNG creation failed. Unable to write info"); + fclose(f); + png_destroy_write_struct(&png_ptr, &info_ptr); + return false; + } + + png_init_io(png_ptr, f); + + png_set_IHDR(png_ptr, info_ptr, width, height, 8, + PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + png_write_info(png_ptr, info_ptr); + + for (y = 0; y < height; ++y) { + qemu_pixman_linebuf_fill(linebuf, image, width, 0, y); + png_write_row(png_ptr, buf); + } + qemu_pixman_image_unref(linebuf); + + png_write_end(png_ptr, NULL); + + png_destroy_write_struct(&png_ptr, &info_ptr); + + if (fclose(f) != 0) { + error_setg_errno(errp, errno, + "PNG creation failed. Unable to close file"); + return false; + } + + return true; +} + +#else /* no png support */ + +static bool png_save(int fd, pixman_image_t *image, Error **errp) +{ + error_setg(errp, "Enable PNG support with libpng for screendump"); + return false; +} + +#endif /* CONFIG_PNG */ + static bool ppm_save(int fd, pixman_image_t *image, Error **errp) { int width = pixman_image_get_width(image); @@ -329,7 +415,8 @@ static void graphic_hw_update_bh(void *con) /* Safety: coroutine-only, concurrent-coroutine safe, main thread only */ void coroutine_fn qmp_screendump(const char *filename, bool has_device, const char *device, - bool has_head, int64_t head, Error **errp) + bool has_head, int64_t head, + bool has_format, ImageFormat format, Error **errp) { g_autoptr(pixman_image_t) image = NULL; QemuConsole *con; @@ -385,8 +472,16 @@ qmp_screendump(const char *filename, bool has_device, const char *device, * yields and releases the BQL. It could produce corrupted dump, but * it should be otherwise safe. */ - if (!ppm_save(fd, image, errp)) { - qemu_unlink(filename); + if (has_format && format == IMAGE_FORMAT_PNG) { + /* PNG format specified for screendump */ + if (!png_save(fd, image, errp)) { + qemu_unlink(filename); + } + } else { + /* PPM format specified/default for screendump */ + if (!ppm_save(fd, image, errp)) { + qemu_unlink(filename); + } } }
Currently screendump only supports PPM format, which is un-compressed and not standard. Added a "format" parameter to qemu monitor screendump capabilites to support PNG image capture using libpng. The param was added in QAPI schema of screendump present in ui.json along with png_save() function which converts pixman_image to PNG. HMP command equivalent was also modified to support the feature. Example usage: { "execute": "screendump", "arguments": { "filename": "/tmp/image", "format":"png" } } Resolves: https://gitlab.com/qemu-project/qemu/-/issues/718 Signed-off-by: Kshitij Suri <kshitij.suri@nutanix.com> ---