diff mbox series

[v2] Added parameter to take screenshot with screendump as PNG

Message ID 20220224115908.102285-1-kshitij.suri@nutanix.com (mailing list archive)
State New, archived
Headers show
Series [v2] Added parameter to take screenshot with screendump as PNG | expand

Commit Message

Kshitij Suri Feb. 24, 2022, 11:59 a.m. UTC
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>
---

Comments

Eric Blake Feb. 24, 2022, 4:02 p.m. UTC | #1
On Thu, Feb 24, 2022 at 11:59:08AM +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.
> 

> +++ b/qapi/ui.json
> @@ -73,12 +73,27 @@
>  ##
>  { 'command': 'expire_password', 'data': {'protocol': 'str', 'time': 'str'} }
>  
> +##
> +# @ImageFormat:
> +#
> +# Available list of supported types.
> +#
> +# @png: PNG format
> +#
> +# @ppm: PPM format
> +#
> +# Since: 6.3

The next release is 7.0, not 6.3.

> +#
> +##
> +{ 'enum': 'ImageFormat',
> +  'data': ['ppm', 'png'] }
> +
>  ##
>  # @screendump:
>  #
> -# Write a PPM of the VGA screen to a file.
> +# Write a screenshot of the VGA screen 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)
> @@ -87,6 +102,9 @@
>  #        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. Currently only PNG and
> +#             PPM are supported.

The second sentence is no longer necessary, as the documentation for
the enum type covers that information now.  Missing a '(since 7.0)' tag.

> +#
>  # Returns: Nothing on success
>  #
>  # Since: 0.14
> @@ -99,7 +117,7 @@
>  #
>  ##
>  { 'command': 'screendump',
> -  'data': {'filename': 'str', '*device': 'str', '*head': 'int'},
> +  'data': {'filename': 'str', '*device': 'str', '*head': 'int', '*format': 'ImageFormat'},

Please wrap the long line.
Daniel P. Berrangé Feb. 24, 2022, 4:18 p.m. UTC | #2
On Thu, Feb 24, 2022 at 11:59:08AM +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.
> 
>  hmp-commands.hx    | 11 +++---
>  meson.build        | 13 +++++--
>  meson_options.txt  |  2 +
>  monitor/hmp-cmds.c |  8 +++-
>  qapi/ui.json       | 24 ++++++++++--
>  ui/console.c       | 97 ++++++++++++++++++++++++++++++++++++++++++++--
>  6 files changed, 139 insertions(+), 16 deletions(-)
> 
> diff --git a/hmp-commands.hx b/hmp-commands.hx
> index 70a9136ac2..e43c9954b5 100644
> --- a/hmp-commands.hx
> +++ b/hmp-commands.hx
> @@ -244,17 +244,18 @@ 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'",
> +        .args_type  = "filename:F,device:s?,head:i?,format:f?",
> +        .params     = "filename [device [head]] [format]",
> +        .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,
>      },
>  
>  SRST
>  ``screendump`` *filename*
> -  Save screen into PPM image *filename*.
> +  Save screen as an image *filename*.
>  ERST
>  
>      {
> diff --git a/meson.build b/meson.build
> index 8df40bfac4..fd550c01ec 100644
> --- a/meson.build
> +++ b/meson.build
> @@ -1112,13 +1112,16 @@ if gtkx11.found()
>    x11 = dependency('x11', method: 'pkg-config', required: gtkx11.found(),
>                     kwargs: static_kwargs)
>  endif
> -vnc = not_found
>  png = not_found
> +png = dependency('libpng', required: get_option('png'),
> +                   method: 'pkg-config', kwargs: static_kwargs)
> +vnc = not_found
> +vnc_png = not_found
>  jpeg = not_found
>  sasl = not_found
>  if get_option('vnc').allowed() and have_system
>    vnc = declare_dependency() # dummy dependency
> -  png = dependency('libpng', required: get_option('vnc_png'),
> +  vnc_png = dependency('libpng', required: get_option('vnc_png'),
>                     method: 'pkg-config', kwargs: static_kwargs)
>    jpeg = dependency('libjpeg', required: get_option('vnc_jpeg'),
>                      method: 'pkg-config', kwargs: static_kwargs)
> @@ -1537,9 +1540,10 @@ config_host_data.set('CONFIG_TPM', have_tpm)
>  config_host_data.set('CONFIG_USB_LIBUSB', libusb.found())
>  config_host_data.set('CONFIG_VDE', vde.found())
>  config_host_data.set('CONFIG_VHOST_USER_BLK_SERVER', have_vhost_user_blk_server)
> +config_host_data.set('CONFIG_PNG', png.found())
>  config_host_data.set('CONFIG_VNC', vnc.found())
>  config_host_data.set('CONFIG_VNC_JPEG', jpeg.found())
> -config_host_data.set('CONFIG_VNC_PNG', png.found())
> +config_host_data.set('CONFIG_VNC_PNG', vnc_png.found())


I think we should be removing  CONFIG_VNC_PNG entirely - the VNC
code should just use  CONFIG_PNG.

I'd suggest splitting ths into two patches.  The first patch
updates meson.build to look for libpng unconditionally and
rename to CONFIG_PNG.  The second patch introduces the PNG
support for screendump.

>  config_host_data.set('CONFIG_VNC_SASL', sasl.found())
>  config_host_data.set('CONFIG_VIRTFS', have_virtfs)
>  config_host_data.set('CONFIG_VTE', vte.found())
> @@ -3579,11 +3583,12 @@ summary_info += {'curses support':    curses}
>  summary_info += {'virgl support':     virgl}
>  summary_info += {'curl support':      curl}
>  summary_info += {'Multipath support': mpathpersist}
> +summary_info += {'PNG support':       png}
>  summary_info += {'VNC support':       vnc}
>  if vnc.found()
>    summary_info += {'VNC SASL support':  sasl}
>    summary_info += {'VNC JPEG support':  jpeg}
> -  summary_info += {'VNC PNG support':   png}
> +  summary_info += {'VNC PNG support':   vnc_png}
>  endif
>  if targetos not in ['darwin', 'haiku', 'windows']
>    summary_info += {'OSS support':     oss}
> diff --git a/meson_options.txt b/meson_options.txt
> index 52b11cead4..88148dec6c 100644
> --- a/meson_options.txt
> +++ b/meson_options.txt
> @@ -177,6 +177,8 @@ option('vde', type : 'feature', value : 'auto',
>         description: 'vde network backend support')
>  option('virglrenderer', type : 'feature', value : 'auto',
>         description: 'virgl rendering support')
> +option('png', type : 'feature', value : 'auto',
> +       description: 'PNG support with libpng')
>  option('vnc', type : 'feature', value : 'auto',
>         description: 'VNC server')
>  option('vnc_jpeg', type : 'feature', value : 'auto',


> diff --git a/qapi/ui.json b/qapi/ui.json
> index 9354f4c467..8caeb2d3bb 100644
> --- a/qapi/ui.json
> +++ b/qapi/ui.json
> @@ -73,12 +73,27 @@
>  ##
>  { 'command': 'expire_password', 'data': {'protocol': 'str', 'time': 'str'} }
>  
> +##
> +# @ImageFormat:
> +#
> +# Available list of supported types.
> +#
> +# @png: PNG format
> +#
> +# @ppm: PPM format
> +#
> +# Since: 6.3
> +#
> +##
> +{ 'enum': 'ImageFormat',
> +  'data': ['ppm', 'png'] }
> +
>  ##
>  # @screendump:
>  #
> -# Write a PPM of the VGA screen to a file.
> +# Write a screenshot of the VGA screen 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)
> @@ -87,6 +102,9 @@
>  #        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. Currently only PNG and
> +#             PPM are supported.

Mention that PPM is the default if omitted.

> diff --git a/ui/console.c b/ui/console.c
> index 40eebb6d2c..911092c908 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"

System headers should use  <...> for includes

> +#endif

> +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);
> +    png_structp png_ptr;
> +    png_infop info_ptr;
> +    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(errp, "Failed to create file from file descriptor");
> +        return false;
> +    }

This leaks 'f', as do following error paths.

> +
> +    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");
> +        return false;
> +    }
> +
> +    info_ptr = png_create_info_struct(png_ptr);
> +
> +    if (!info_ptr) {
> +        error_setg(errp, "PNG creation failed. Unable to write info");
> +        return false;

This leaks 'png_ptr' too

> +    }
> +
> +    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(errp, "PNG creation failed. Unable to close file");
> +        return false;
> +    }
> +
> +    return true;
> +}

Regards,
Daniel
Kshitij Suri Feb. 25, 2022, 5:31 a.m. UTC | #3
On 24/02/22 9:32 pm, Eric Blake wrote:
> On Thu, Feb 24, 2022 at 11:59:08AM +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=DwIBAg&c=s883GpUCOChKOHiocYtGcg&r=utjv19Ej9Fb0TB7_DX0o3faQ-OAm2ypPniPyqVSoj_w&m=qNJ3bItOdAVHt5dAMXQLkxPL-RWabpnjdw53Hqk9lMbMhTF5Z2PmCjhWiBIiyiII&s=HjICgN7yF07YFTdi0rumN8vhm3-EwLgjHTuHhZXVc5w&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.
>>
>> +++ b/qapi/ui.json
>> @@ -73,12 +73,27 @@
>>   ##
>>   { 'command': 'expire_password', 'data': {'protocol': 'str', 'time': 'str'} }
>>   
>> +##
>> +# @ImageFormat:
>> +#
>> +# Available list of supported types.
>> +#
>> +# @png: PNG format
>> +#
>> +# @ppm: PPM format
>> +#
>> +# Since: 6.3
> The next release is 7.0, not 6.3.
>
>> +#
>> +##
>> +{ 'enum': 'ImageFormat',
>> +  'data': ['ppm', 'png'] }
>> +
>>   ##
>>   # @screendump:
>>   #
>> -# Write a PPM of the VGA screen to a file.
>> +# Write a screenshot of the VGA screen 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)
>> @@ -87,6 +102,9 @@
>>   #        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. Currently only PNG and
>> +#             PPM are supported.
> The second sentence is no longer necessary, as the documentation for
> the enum type covers that information now.  Missing a '(since 7.0)' tag.
>
>> +#
>>   # Returns: Nothing on success
>>   #
>>   # Since: 0.14
>> @@ -99,7 +117,7 @@
>>   #
>>   ##
>>   { 'command': 'screendump',
>> -  'data': {'filename': 'str', '*device': 'str', '*head': 'int'},
>> +  'data': {'filename': 'str', '*device': 'str', '*head': 'int', '*format': 'ImageFormat'},
> Please wrap the long line.
Thank you for your prompt review! Will update the suggested changes in 
the following patch.

Regards,
Kshitij
>
Kshitij Suri Feb. 25, 2022, 5:56 a.m. UTC | #4
On 24/02/22 9:48 pm, Daniel P. Berrangé wrote:
> On Thu, Feb 24, 2022 at 11:59:08AM +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=DwIBaQ&c=s883GpUCOChKOHiocYtGcg&r=utjv19Ej9Fb0TB7_DX0o3faQ-OAm2ypPniPyqVSoj_w&m=Hu1QTP-aSF7FeXYQcdODcxg2wW1sBEpxaD4jWHYF5kxD2Z6ihhXkxRFOkovubo-f&s=YwpDKYWnYlYBM7aE1jNrISGXxP9nKm5f9Kfotxm5rZ4&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.
>>
>>   hmp-commands.hx    | 11 +++---
>>   meson.build        | 13 +++++--
>>   meson_options.txt  |  2 +
>>   monitor/hmp-cmds.c |  8 +++-
>>   qapi/ui.json       | 24 ++++++++++--
>>   ui/console.c       | 97 ++++++++++++++++++++++++++++++++++++++++++++--
>>   6 files changed, 139 insertions(+), 16 deletions(-)
>>
>> diff --git a/hmp-commands.hx b/hmp-commands.hx
>> index 70a9136ac2..e43c9954b5 100644
>> --- a/hmp-commands.hx
>> +++ b/hmp-commands.hx
>> @@ -244,17 +244,18 @@ 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'",
>> +        .args_type  = "filename:F,device:s?,head:i?,format:f?",
>> +        .params     = "filename [device [head]] [format]",
>> +        .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,
>>       },
>>   
>>   SRST
>>   ``screendump`` *filename*
>> -  Save screen into PPM image *filename*.
>> +  Save screen as an image *filename*.
>>   ERST
>>   
>>       {
>> diff --git a/meson.build b/meson.build
>> index 8df40bfac4..fd550c01ec 100644
>> --- a/meson.build
>> +++ b/meson.build
>> @@ -1112,13 +1112,16 @@ if gtkx11.found()
>>     x11 = dependency('x11', method: 'pkg-config', required: gtkx11.found(),
>>                      kwargs: static_kwargs)
>>   endif
>> -vnc = not_found
>>   png = not_found
>> +png = dependency('libpng', required: get_option('png'),
>> +                   method: 'pkg-config', kwargs: static_kwargs)
>> +vnc = not_found
>> +vnc_png = not_found
>>   jpeg = not_found
>>   sasl = not_found
>>   if get_option('vnc').allowed() and have_system
>>     vnc = declare_dependency() # dummy dependency
>> -  png = dependency('libpng', required: get_option('vnc_png'),
>> +  vnc_png = dependency('libpng', required: get_option('vnc_png'),
>>                      method: 'pkg-config', kwargs: static_kwargs)
>>     jpeg = dependency('libjpeg', required: get_option('vnc_jpeg'),
>>                       method: 'pkg-config', kwargs: static_kwargs)
>> @@ -1537,9 +1540,10 @@ config_host_data.set('CONFIG_TPM', have_tpm)
>>   config_host_data.set('CONFIG_USB_LIBUSB', libusb.found())
>>   config_host_data.set('CONFIG_VDE', vde.found())
>>   config_host_data.set('CONFIG_VHOST_USER_BLK_SERVER', have_vhost_user_blk_server)
>> +config_host_data.set('CONFIG_PNG', png.found())
>>   config_host_data.set('CONFIG_VNC', vnc.found())
>>   config_host_data.set('CONFIG_VNC_JPEG', jpeg.found())
>> -config_host_data.set('CONFIG_VNC_PNG', png.found())
>> +config_host_data.set('CONFIG_VNC_PNG', vnc_png.found())
>
> I think we should be removing  CONFIG_VNC_PNG entirely - the VNC
> code should just use  CONFIG_PNG.
>
> I'd suggest splitting ths into two patches.  The first patch
> updates meson.build to look for libpng unconditionally and
> rename to CONFIG_PNG.  The second patch introduces the PNG
> support for screendump.
Yes can remove entirely with a combination of CONFIG_VNC and CONFIG_PNG 
as follows

#ifdef CONFIG_VNC_PNG -> #if defined(CONFIG_VNC) && defined(CONFIG_PNG)

But won't removing the vnc_png option cause problems in the build 
scripts of users with the current
version? Instead, can we use the new png meson-option to denote PNG 
format for VNC as well while maintaining backward compatibility? The 
change would look like

vnc_png = dependency('libpng', required: get_option('vnc_png'), method: 
'pkg-config', kwargs: static_kwargs)

gets changed to

vnc_png = dependency('libpng', required: get_option('vnc_png') || get_option('png'),
                     method: 'pkg-config', kwargs: static_kwargs)

>>   config_host_data.set('CONFIG_VNC_SASL', sasl.found())
>>   config_host_data.set('CONFIG_VIRTFS', have_virtfs)
>>   config_host_data.set('CONFIG_VTE', vte.found())
>> @@ -3579,11 +3583,12 @@ summary_info += {'curses support':    curses}
>>   summary_info += {'virgl support':     virgl}
>>   summary_info += {'curl support':      curl}
>>   summary_info += {'Multipath support': mpathpersist}
>> +summary_info += {'PNG support':       png}
>>   summary_info += {'VNC support':       vnc}
>>   if vnc.found()
>>     summary_info += {'VNC SASL support':  sasl}
>>     summary_info += {'VNC JPEG support':  jpeg}
>> -  summary_info += {'VNC PNG support':   png}
>> +  summary_info += {'VNC PNG support':   vnc_png}
>>   endif
>>   if targetos not in ['darwin', 'haiku', 'windows']
>>     summary_info += {'OSS support':     oss}
>> diff --git a/meson_options.txt b/meson_options.txt
>> index 52b11cead4..88148dec6c 100644
>> --- a/meson_options.txt
>> +++ b/meson_options.txt
>> @@ -177,6 +177,8 @@ option('vde', type : 'feature', value : 'auto',
>>          description: 'vde network backend support')
>>   option('virglrenderer', type : 'feature', value : 'auto',
>>          description: 'virgl rendering support')
>> +option('png', type : 'feature', value : 'auto',
>> +       description: 'PNG support with libpng')
>>   option('vnc', type : 'feature', value : 'auto',
>>          description: 'VNC server')
>>   option('vnc_jpeg', type : 'feature', value : 'auto',
>
>> diff --git a/qapi/ui.json b/qapi/ui.json
>> index 9354f4c467..8caeb2d3bb 100644
>> --- a/qapi/ui.json
>> +++ b/qapi/ui.json
>> @@ -73,12 +73,27 @@
>>   ##
>>   { 'command': 'expire_password', 'data': {'protocol': 'str', 'time': 'str'} }
>>   
>> +##
>> +# @ImageFormat:
>> +#
>> +# Available list of supported types.
>> +#
>> +# @png: PNG format
>> +#
>> +# @ppm: PPM format
>> +#
>> +# Since: 6.3
>> +#
>> +##
>> +{ 'enum': 'ImageFormat',
>> +  'data': ['ppm', 'png'] }
>> +
>>   ##
>>   # @screendump:
>>   #
>> -# Write a PPM of the VGA screen to a file.
>> +# Write a screenshot of the VGA screen 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)
>> @@ -87,6 +102,9 @@
>>   #        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. Currently only PNG and
>> +#             PPM are supported.
> Mention that PPM is the default if omitted.
>
>> diff --git a/ui/console.c b/ui/console.c
>> index 40eebb6d2c..911092c908 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"
> System headers should use  <...> for includes
>
>> +#endif
>> +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);
>> +    png_structp png_ptr;
>> +    png_infop info_ptr;
>> +    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(errp, "Failed to create file from file descriptor");
>> +        return false;
>> +    }
> This leaks 'f', as do following error paths.
>
>> +
>> +    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");
>> +        return false;
>> +    }
>> +
>> +    info_ptr = png_create_info_struct(png_ptr);
>> +
>> +    if (!info_ptr) {
>> +        error_setg(errp, "PNG creation failed. Unable to write info");
>> +        return false;
> This leaks 'png_ptr' too
>
>> +    }
>> +
>> +    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(errp, "PNG creation failed. Unable to close file");
>> +        return false;
>> +    }
>> +
>> +    return true;
>> +}
> Regards,
> Daniel
Thank you for your review! Will update the error handling and 
documentation in the upcoming patch.

Regards,
Kshitij
Daniel P. Berrangé Feb. 25, 2022, 9:10 a.m. UTC | #5
On Fri, Feb 25, 2022 at 11:26:20AM +0530, Kshitij Suri wrote:
> 
> On 24/02/22 9:48 pm, Daniel P. Berrangé wrote:
> > On Thu, Feb 24, 2022 at 11:59:08AM +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=DwIBaQ&c=s883GpUCOChKOHiocYtGcg&r=utjv19Ej9Fb0TB7_DX0o3faQ-OAm2ypPniPyqVSoj_w&m=Hu1QTP-aSF7FeXYQcdODcxg2wW1sBEpxaD4jWHYF5kxD2Z6ihhXkxRFOkovubo-f&s=YwpDKYWnYlYBM7aE1jNrISGXxP9nKm5f9Kfotxm5rZ4&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.
> > > 
> > >   hmp-commands.hx    | 11 +++---
> > >   meson.build        | 13 +++++--
> > >   meson_options.txt  |  2 +
> > >   monitor/hmp-cmds.c |  8 +++-
> > >   qapi/ui.json       | 24 ++++++++++--
> > >   ui/console.c       | 97 ++++++++++++++++++++++++++++++++++++++++++++--
> > >   6 files changed, 139 insertions(+), 16 deletions(-)
> > > 
> > > diff --git a/hmp-commands.hx b/hmp-commands.hx
> > > index 70a9136ac2..e43c9954b5 100644
> > > --- a/hmp-commands.hx
> > > +++ b/hmp-commands.hx
> > > @@ -244,17 +244,18 @@ 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'",
> > > +        .args_type  = "filename:F,device:s?,head:i?,format:f?",
> > > +        .params     = "filename [device [head]] [format]",
> > > +        .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,
> > >       },
> > >   SRST
> > >   ``screendump`` *filename*
> > > -  Save screen into PPM image *filename*.
> > > +  Save screen as an image *filename*.
> > >   ERST
> > >       {
> > > diff --git a/meson.build b/meson.build
> > > index 8df40bfac4..fd550c01ec 100644
> > > --- a/meson.build
> > > +++ b/meson.build
> > > @@ -1112,13 +1112,16 @@ if gtkx11.found()
> > >     x11 = dependency('x11', method: 'pkg-config', required: gtkx11.found(),
> > >                      kwargs: static_kwargs)
> > >   endif
> > > -vnc = not_found
> > >   png = not_found
> > > +png = dependency('libpng', required: get_option('png'),
> > > +                   method: 'pkg-config', kwargs: static_kwargs)
> > > +vnc = not_found
> > > +vnc_png = not_found
> > >   jpeg = not_found
> > >   sasl = not_found
> > >   if get_option('vnc').allowed() and have_system
> > >     vnc = declare_dependency() # dummy dependency
> > > -  png = dependency('libpng', required: get_option('vnc_png'),
> > > +  vnc_png = dependency('libpng', required: get_option('vnc_png'),
> > >                      method: 'pkg-config', kwargs: static_kwargs)
> > >     jpeg = dependency('libjpeg', required: get_option('vnc_jpeg'),
> > >                       method: 'pkg-config', kwargs: static_kwargs)
> > > @@ -1537,9 +1540,10 @@ config_host_data.set('CONFIG_TPM', have_tpm)
> > >   config_host_data.set('CONFIG_USB_LIBUSB', libusb.found())
> > >   config_host_data.set('CONFIG_VDE', vde.found())
> > >   config_host_data.set('CONFIG_VHOST_USER_BLK_SERVER', have_vhost_user_blk_server)
> > > +config_host_data.set('CONFIG_PNG', png.found())
> > >   config_host_data.set('CONFIG_VNC', vnc.found())
> > >   config_host_data.set('CONFIG_VNC_JPEG', jpeg.found())
> > > -config_host_data.set('CONFIG_VNC_PNG', png.found())
> > > +config_host_data.set('CONFIG_VNC_PNG', vnc_png.found())
> > 
> > I think we should be removing  CONFIG_VNC_PNG entirely - the VNC
> > code should just use  CONFIG_PNG.
> > 
> > I'd suggest splitting ths into two patches.  The first patch
> > updates meson.build to look for libpng unconditionally and
> > rename to CONFIG_PNG.  The second patch introduces the PNG
> > support for screendump.
> Yes can remove entirely with a combination of CONFIG_VNC and CONFIG_PNG as
> follows
> 
> #ifdef CONFIG_VNC_PNG -> #if defined(CONFIG_VNC) && defined(CONFIG_PNG)
> 
> But won't removing the vnc_png option cause problems in the build scripts of
> users with the current
> version? Instead, can we use the new png meson-option to denote PNG format
> for VNC as well while maintaining backward compatibility? The change would
> look like

Configure arguments / meson options are not a stable interface
from QEMU. We can change them at any time.

Regards,
Daniel
Kshitij Suri Feb. 25, 2022, 9:35 a.m. UTC | #6
On 25/02/22 2:40 pm, Daniel P. Berrangé wrote:
> On Fri, Feb 25, 2022 at 11:26:20AM +0530, Kshitij Suri wrote:
>> On 24/02/22 9:48 pm, Daniel P. Berrangé wrote:
>>> On Thu, Feb 24, 2022 at 11:59:08AM +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=DwIBaQ&c=s883GpUCOChKOHiocYtGcg&r=utjv19Ej9Fb0TB7_DX0o3faQ-OAm2ypPniPyqVSoj_w&m=Hu1QTP-aSF7FeXYQcdODcxg2wW1sBEpxaD4jWHYF5kxD2Z6ihhXkxRFOkovubo-f&s=YwpDKYWnYlYBM7aE1jNrISGXxP9nKm5f9Kfotxm5rZ4&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.
>>>>
>>>>    hmp-commands.hx    | 11 +++---
>>>>    meson.build        | 13 +++++--
>>>>    meson_options.txt  |  2 +
>>>>    monitor/hmp-cmds.c |  8 +++-
>>>>    qapi/ui.json       | 24 ++++++++++--
>>>>    ui/console.c       | 97 ++++++++++++++++++++++++++++++++++++++++++++--
>>>>    6 files changed, 139 insertions(+), 16 deletions(-)
>>>>
>>>> diff --git a/hmp-commands.hx b/hmp-commands.hx
>>>> index 70a9136ac2..e43c9954b5 100644
>>>> --- a/hmp-commands.hx
>>>> +++ b/hmp-commands.hx
>>>> @@ -244,17 +244,18 @@ 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'",
>>>> +        .args_type  = "filename:F,device:s?,head:i?,format:f?",
>>>> +        .params     = "filename [device [head]] [format]",
>>>> +        .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,
>>>>        },
>>>>    SRST
>>>>    ``screendump`` *filename*
>>>> -  Save screen into PPM image *filename*.
>>>> +  Save screen as an image *filename*.
>>>>    ERST
>>>>        {
>>>> diff --git a/meson.build b/meson.build
>>>> index 8df40bfac4..fd550c01ec 100644
>>>> --- a/meson.build
>>>> +++ b/meson.build
>>>> @@ -1112,13 +1112,16 @@ if gtkx11.found()
>>>>      x11 = dependency('x11', method: 'pkg-config', required: gtkx11.found(),
>>>>                       kwargs: static_kwargs)
>>>>    endif
>>>> -vnc = not_found
>>>>    png = not_found
>>>> +png = dependency('libpng', required: get_option('png'),
>>>> +                   method: 'pkg-config', kwargs: static_kwargs)
>>>> +vnc = not_found
>>>> +vnc_png = not_found
>>>>    jpeg = not_found
>>>>    sasl = not_found
>>>>    if get_option('vnc').allowed() and have_system
>>>>      vnc = declare_dependency() # dummy dependency
>>>> -  png = dependency('libpng', required: get_option('vnc_png'),
>>>> +  vnc_png = dependency('libpng', required: get_option('vnc_png'),
>>>>                       method: 'pkg-config', kwargs: static_kwargs)
>>>>      jpeg = dependency('libjpeg', required: get_option('vnc_jpeg'),
>>>>                        method: 'pkg-config', kwargs: static_kwargs)
>>>> @@ -1537,9 +1540,10 @@ config_host_data.set('CONFIG_TPM', have_tpm)
>>>>    config_host_data.set('CONFIG_USB_LIBUSB', libusb.found())
>>>>    config_host_data.set('CONFIG_VDE', vde.found())
>>>>    config_host_data.set('CONFIG_VHOST_USER_BLK_SERVER', have_vhost_user_blk_server)
>>>> +config_host_data.set('CONFIG_PNG', png.found())
>>>>    config_host_data.set('CONFIG_VNC', vnc.found())
>>>>    config_host_data.set('CONFIG_VNC_JPEG', jpeg.found())
>>>> -config_host_data.set('CONFIG_VNC_PNG', png.found())
>>>> +config_host_data.set('CONFIG_VNC_PNG', vnc_png.found())
>>> I think we should be removing  CONFIG_VNC_PNG entirely - the VNC
>>> code should just use  CONFIG_PNG.
>>>
>>> I'd suggest splitting ths into two patches.  The first patch
>>> updates meson.build to look for libpng unconditionally and
>>> rename to CONFIG_PNG.  The second patch introduces the PNG
>>> support for screendump.
>> Yes can remove entirely with a combination of CONFIG_VNC and CONFIG_PNG as
>> follows
>>
>> #ifdef CONFIG_VNC_PNG -> #if defined(CONFIG_VNC) && defined(CONFIG_PNG)
>>
>> But won't removing the vnc_png option cause problems in the build scripts of
>> users with the current
>> version? Instead, can we use the new png meson-option to denote PNG format
>> for VNC as well while maintaining backward compatibility? The change would
>> look like
> Configure arguments / meson options are not a stable interface
> from QEMU. We can change them at any time.
Okay thank you for the clarification! Will send an updated two patch 
series ASAP.
>
> Regards,
> Daniel
>
>
Regards,

Kshitij
diff mbox series

Patch

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.

 hmp-commands.hx    | 11 +++---
 meson.build        | 13 +++++--
 meson_options.txt  |  2 +
 monitor/hmp-cmds.c |  8 +++-
 qapi/ui.json       | 24 ++++++++++--
 ui/console.c       | 97 ++++++++++++++++++++++++++++++++++++++++++++--
 6 files changed, 139 insertions(+), 16 deletions(-)

diff --git a/hmp-commands.hx b/hmp-commands.hx
index 70a9136ac2..e43c9954b5 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -244,17 +244,18 @@  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'",
+        .args_type  = "filename:F,device:s?,head:i?,format:f?",
+        .params     = "filename [device [head]] [format]",
+        .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,
     },
 
 SRST
 ``screendump`` *filename*
-  Save screen into PPM image *filename*.
+  Save screen as an image *filename*.
 ERST
 
     {
diff --git a/meson.build b/meson.build
index 8df40bfac4..fd550c01ec 100644
--- a/meson.build
+++ b/meson.build
@@ -1112,13 +1112,16 @@  if gtkx11.found()
   x11 = dependency('x11', method: 'pkg-config', required: gtkx11.found(),
                    kwargs: static_kwargs)
 endif
-vnc = not_found
 png = not_found
+png = dependency('libpng', required: get_option('png'),
+                   method: 'pkg-config', kwargs: static_kwargs)
+vnc = not_found
+vnc_png = not_found
 jpeg = not_found
 sasl = not_found
 if get_option('vnc').allowed() and have_system
   vnc = declare_dependency() # dummy dependency
-  png = dependency('libpng', required: get_option('vnc_png'),
+  vnc_png = dependency('libpng', required: get_option('vnc_png'),
                    method: 'pkg-config', kwargs: static_kwargs)
   jpeg = dependency('libjpeg', required: get_option('vnc_jpeg'),
                     method: 'pkg-config', kwargs: static_kwargs)
@@ -1537,9 +1540,10 @@  config_host_data.set('CONFIG_TPM', have_tpm)
 config_host_data.set('CONFIG_USB_LIBUSB', libusb.found())
 config_host_data.set('CONFIG_VDE', vde.found())
 config_host_data.set('CONFIG_VHOST_USER_BLK_SERVER', have_vhost_user_blk_server)
+config_host_data.set('CONFIG_PNG', png.found())
 config_host_data.set('CONFIG_VNC', vnc.found())
 config_host_data.set('CONFIG_VNC_JPEG', jpeg.found())
-config_host_data.set('CONFIG_VNC_PNG', png.found())
+config_host_data.set('CONFIG_VNC_PNG', vnc_png.found())
 config_host_data.set('CONFIG_VNC_SASL', sasl.found())
 config_host_data.set('CONFIG_VIRTFS', have_virtfs)
 config_host_data.set('CONFIG_VTE', vte.found())
@@ -3579,11 +3583,12 @@  summary_info += {'curses support':    curses}
 summary_info += {'virgl support':     virgl}
 summary_info += {'curl support':      curl}
 summary_info += {'Multipath support': mpathpersist}
+summary_info += {'PNG support':       png}
 summary_info += {'VNC support':       vnc}
 if vnc.found()
   summary_info += {'VNC SASL support':  sasl}
   summary_info += {'VNC JPEG support':  jpeg}
-  summary_info += {'VNC PNG support':   png}
+  summary_info += {'VNC PNG support':   vnc_png}
 endif
 if targetos not in ['darwin', 'haiku', 'windows']
   summary_info += {'OSS support':     oss}
diff --git a/meson_options.txt b/meson_options.txt
index 52b11cead4..88148dec6c 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -177,6 +177,8 @@  option('vde', type : 'feature', value : 'auto',
        description: 'vde network backend support')
 option('virglrenderer', type : 'feature', value : 'auto',
        description: 'virgl rendering support')
+option('png', type : 'feature', value : 'auto',
+       description: 'PNG support with libpng')
 option('vnc', type : 'feature', value : 'auto',
        description: 'VNC server')
 option('vnc_jpeg', type : 'feature', value : 'auto',
diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
index 8c384dc1b2..c300545f34 100644
--- a/monitor/hmp-cmds.c
+++ b/monitor/hmp-cmds.c
@@ -1677,9 +1677,15 @@  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_str(qdict, "format");
     Error *err = NULL;
+    ImageFormat format = IMAGE_FORMAT_PPM;
+    if (input_format != NULL && strcmp(input_format, "png") == 0) {
+        format = IMAGE_FORMAT_PNG;
+    }
 
-    qmp_screendump(filename, id != NULL, id, id != NULL, head, &err);
+    qmp_screendump(filename, id != NULL, id, id != NULL, head,
+                   input_format != NULL, format, &err);
     hmp_handle_error(mon, err);
 }
 
diff --git a/qapi/ui.json b/qapi/ui.json
index 9354f4c467..8caeb2d3bb 100644
--- a/qapi/ui.json
+++ b/qapi/ui.json
@@ -73,12 +73,27 @@ 
 ##
 { 'command': 'expire_password', 'data': {'protocol': 'str', 'time': 'str'} }
 
+##
+# @ImageFormat:
+#
+# Available list of supported types.
+#
+# @png: PNG format
+#
+# @ppm: PPM format
+#
+# Since: 6.3
+#
+##
+{ 'enum': 'ImageFormat',
+  'data': ['ppm', 'png'] }
+
 ##
 # @screendump:
 #
-# Write a PPM of the VGA screen to a file.
+# Write a screenshot of the VGA screen 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)
@@ -87,6 +102,9 @@ 
 #        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. Currently only PNG and
+#             PPM are supported.
+#
 # Returns: Nothing on success
 #
 # Since: 0.14
@@ -99,7 +117,7 @@ 
 #
 ##
 { '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 40eebb6d2c..911092c908 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
@@ -289,6 +292,84 @@  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);
+    png_structp png_ptr;
+    png_infop info_ptr;
+    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(errp, "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");
+        return false;
+    }
+
+    info_ptr = png_create_info_struct(png_ptr);
+
+    if (!info_ptr) {
+        error_setg(errp, "PNG creation failed. Unable to write info");
+        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(errp, "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);
@@ -327,7 +408,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;
@@ -383,8 +465,17 @@  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);
+        }
     }
 }