From 6acf68c87ed3e5a4c6c83dd4d204495601790c7c Mon Sep 17 00:00:00 2001 From: Paulchen-Panther Date: Mon, 22 Aug 2016 11:59:07 +0200 Subject: [PATCH] Update X11 Grabber (scaling improvements) (#191) * Add transform Matrix for scaling * Add Image scaling by XRender * Coding Style correction --- include/grabber/X11Grabber.h | 4 + libsrc/grabber/x11/X11Grabber.cpp | 266 ++++++++++++++++++++---------- 2 files changed, 185 insertions(+), 85 deletions(-) diff --git a/include/grabber/X11Grabber.h b/include/grabber/X11Grabber.h index 1aafe1f8..e026bab2 100755 --- a/include/grabber/X11Grabber.h +++ b/include/grabber/X11Grabber.h @@ -72,6 +72,10 @@ private: XRenderPictureAttributes _pictAttr; Picture _srcPicture; Picture _dstPicture; + + XTransform _transform; + int _horizontalDecimation; + int _verticalDecimation; unsigned _screenWidth; unsigned _screenHeight; diff --git a/libsrc/grabber/x11/X11Grabber.cpp b/libsrc/grabber/x11/X11Grabber.cpp index 8fea47b0..e7066e9a 100755 --- a/libsrc/grabber/x11/X11Grabber.cpp +++ b/libsrc/grabber/x11/X11Grabber.cpp @@ -6,29 +6,29 @@ // X11Grabber includes #include -X11Grabber::X11Grabber(bool useXGetImage, int cropLeft, int cropRight, int cropTop, int cropBottom, int horizontalPixelDecimation, int verticalPixelDecimation) : - _imageResampler(), - _useXGetImage(useXGetImage), - _cropLeft(cropLeft), - _cropRight(cropRight), - _cropTop(cropTop), - _cropBottom(cropBottom), - _x11Display(nullptr), - _pixmap(None), - _srcFormat(nullptr), - _dstFormat(nullptr), - _srcPicture(None), - _dstPicture(None), - _screenWidth(0), - _screenHeight(0), - _croppedWidth(0), - _croppedHeight(0), - _image(0,0), - _log(Logger::getInstance("X11GRABBER")) +X11Grabber::X11Grabber(bool useXGetImage, int cropLeft, int cropRight, int cropTop, int cropBottom, int horizontalPixelDecimation, int verticalPixelDecimation) + : _imageResampler() + , _useXGetImage(useXGetImage) + , _cropLeft(cropLeft) + , _cropRight(cropRight) + , _cropTop(cropTop) + , _cropBottom(cropBottom) + , _x11Display(nullptr) + , _pixmap(None) + , _srcFormat(nullptr) + , _dstFormat(nullptr) + , _srcPicture(None) + , _dstPicture(None) + , _horizontalDecimation(horizontalPixelDecimation) + , _verticalDecimation(verticalPixelDecimation) + , _screenWidth(0) + , _screenHeight(0) + , _croppedWidth(0) + , _croppedHeight(0) + , _image(0,0) + , _log(Logger::getInstance("X11GRABBER")) { - _imageResampler.setHorizontalPixelDecimation(horizontalPixelDecimation); - _imageResampler.setVerticalPixelDecimation(verticalPixelDecimation); - _imageResampler.setCropping(0, 0, 0, 0); // cropping is performed by XShmGetImage or XGetImage + _imageResampler.setCropping(0, 0, 0, 0); // cropping is performed by XRender, XShmGetImage or XGetImage memset(&_pictAttr, 0, sizeof(_pictAttr)); _pictAttr.repeat = RepeatNone; } @@ -51,12 +51,14 @@ void X11Grabber::freeResources() { // Cleanup allocated resources of the X11 grab XDestroyImage(_xImage); - if(_XShmAvailable && !_useXGetImage) { + if(_XShmAvailable && !_useXGetImage) + { XShmDetach(_x11Display, &_shminfo); shmdt(_shminfo.shmaddr); shmctl(_shminfo.shmid, IPC_RMID, 0); } - if (_XRenderAvailable && !_useXGetImage) { + if (_XRenderAvailable && !_useXGetImage) + { XRenderFreePicture(_x11Display, _srcPicture); XRenderFreePicture(_x11Display, _dstPicture); XFreePixmap(_x11Display, _pixmap); @@ -65,29 +67,31 @@ void X11Grabber::freeResources() void X11Grabber::setupResources() { - if(_XShmAvailable && !_useXGetImage) { + if(_XShmAvailable && !_useXGetImage) + { _xImage = XShmCreateImage(_x11Display, _windowAttr.visual, _windowAttr.depth, ZPixmap, NULL, &_shminfo, _croppedWidth, _croppedHeight); - _shminfo.shmid = shmget(IPC_PRIVATE, _xImage->bytes_per_line * _xImage->height, IPC_CREAT|0777); _xImage->data = (char*)shmat(_shminfo.shmid,0,0); _shminfo.shmaddr = _xImage->data; _shminfo.readOnly = False; - - XShmAttach(_x11Display, &_shminfo); + XShmAttach(_x11Display, &_shminfo); } - if (_XRenderAvailable && !_useXGetImage) { - if(_XShmPixmapAvailable) { + if (_XRenderAvailable && !_useXGetImage) + { + if(_XShmPixmapAvailable) + { _pixmap = XShmCreatePixmap(_x11Display, _window, _xImage->data, &_shminfo, _croppedWidth, _croppedHeight, _windowAttr.depth); - } else { + } + else + { _pixmap = XCreatePixmap(_x11Display, _window, _croppedWidth, _croppedHeight, _windowAttr.depth); } _srcFormat = XRenderFindVisualFormat(_x11Display, _windowAttr.visual); _dstFormat = XRenderFindVisualFormat(_x11Display, _windowAttr.visual); _srcPicture = XRenderCreatePicture(_x11Display, _window, _srcFormat, CPRepeat, &_pictAttr); _dstPicture = XRenderCreatePicture(_x11Display, _pixmap, _dstFormat, CPRepeat, &_pictAttr); - XRenderSetPictureFilter(_x11Display, _srcPicture, "bilinear", NULL, 0); } } @@ -97,9 +101,12 @@ bool X11Grabber::Setup() if (_x11Display == nullptr) { Error(_log, "Unable to open display"); - if (getenv("DISPLAY")) { + if (getenv("DISPLAY")) + { Error(_log, "%s",getenv("DISPLAY")); - } else { + } + else + { Error(_log, "DISPLAY environment variable not set"); } return false; @@ -113,6 +120,10 @@ bool X11Grabber::Setup() _XShmAvailable = XShmQueryExtension(_x11Display); XShmQueryVersion(_x11Display, &dummy, &dummy, &pixmaps_supported); _XShmPixmapAvailable = pixmaps_supported && XShmPixmapFormat(_x11Display) == ZPixmap; + + // Image scaling is performed by XRender when available, otherwise by ImageResampler + _imageResampler.setHorizontalPixelDecimation(_XRenderAvailable ? 1 : _horizontalDecimation); + _imageResampler.setVerticalPixelDecimation(_XRenderAvailable ? 1 : _verticalDecimation); return true; } @@ -121,32 +132,68 @@ Image & X11Grabber::grab() { updateScreenDimensions(); - if (_XRenderAvailable && !_useXGetImage) { - XRenderComposite( _x11Display, // *dpy, - PictOpSrc, // op, - _srcPicture, // src - None, // mask - _dstPicture, // dst - _cropLeft, // src_x - _cropTop, // src_y - 0, // mask_x - 0, // mask_y - 0, // dst_x - 0, // dst_y - _croppedWidth, // width - _croppedHeight); // height - + if (_XRenderAvailable && !_useXGetImage) + { + double scale_x = static_cast(_windowAttr.width / _horizontalDecimation) / static_cast(_windowAttr.width); + double scale_y = static_cast(_windowAttr.height / _verticalDecimation) / static_cast(_windowAttr.height); + double scale = std::min(scale_y, scale_x); + + _transform = + { + { + { + XDoubleToFixed(1), + XDoubleToFixed(0), + XDoubleToFixed(0) + }, + { + XDoubleToFixed(0), + XDoubleToFixed(1), + XDoubleToFixed(0) + }, + { + XDoubleToFixed(0), + XDoubleToFixed(0), + XDoubleToFixed(scale) + } + } + }; + + XRenderSetPictureTransform (_x11Display, _srcPicture, &_transform); + XRenderSetPictureFilter(_x11Display, _srcPicture, FilterBilinear, NULL, 0); + + XRenderComposite( _x11Display, // dpy + PictOpSrc, // op + _srcPicture, // src + None, // mask + _dstPicture, // dst + _cropLeft / _horizontalDecimation, // src_x _cropLeft + _cropTop / _verticalDecimation, // src_y _cropTop + 0, // mask_x + 0, // mask_y + 0, // dst_x + 0, // dst_y + _croppedWidth, // width + _croppedHeight); // height + XSync(_x11Display, False); - - if (_XShmAvailable) { + + if (!_XShmAvailable) + { XShmGetImage(_x11Display, _pixmap, _xImage, 0, 0, AllPlanes); - } else { + } + else + { _xImage = XGetImage(_x11Display, _pixmap, 0, 0, _croppedWidth, _croppedHeight, AllPlanes, ZPixmap); } - } else { + } + else + { if (_XShmAvailable && !_useXGetImage) { XShmGetImage(_x11Display, _window, _xImage, _cropLeft, _cropTop, AllPlanes); - } else { + } + else + { _xImage = XGetImage(_x11Display, _window, _cropLeft, _cropTop, _croppedWidth, _croppedHeight, AllPlanes, ZPixmap); } } @@ -164,32 +211,68 @@ Image & X11Grabber::grab() int X11Grabber::grabFrame(Image & image) { - if (_XRenderAvailable && !_useXGetImage) { - XRenderComposite( _x11Display, // *dpy, - PictOpSrc, // op, - _srcPicture, // src - None, // mask - _dstPicture, // dst - _cropLeft, // src_x - _cropTop, // src_y - 0, // mask_x - 0, // mask_y - 0, // dst_x - 0, // dst_y - _croppedWidth, // width - _croppedHeight); // height - + if (_XRenderAvailable && !_useXGetImage) + { + double scale_x = static_cast(_windowAttr.width / _horizontalDecimation) / static_cast(_windowAttr.width); + double scale_y = static_cast(_windowAttr.height / _verticalDecimation) / static_cast(_windowAttr.height); + double scale = std::min(scale_y, scale_x); + + _transform = + { + { + { + XDoubleToFixed(1), + XDoubleToFixed(0), + XDoubleToFixed(0) + }, + { + XDoubleToFixed(0), + XDoubleToFixed(1), + XDoubleToFixed(0) + }, + { + XDoubleToFixed(0), + XDoubleToFixed(0), + XDoubleToFixed(scale) + } + } + }; + + XRenderSetPictureTransform (_x11Display, _srcPicture, &_transform); + XRenderSetPictureFilter(_x11Display, _srcPicture, FilterBilinear, NULL, 0); + + XRenderComposite( _x11Display, // dpy + PictOpSrc, // op + _srcPicture, // src + None, // mask + _dstPicture, // dst + _cropLeft / _horizontalDecimation, // src_x _cropLeft + _cropTop / _verticalDecimation, // src_y _cropTop + 0, // mask_x + 0, // mask_y + 0, // dst_x + 0, // dst_y + _croppedWidth, // width + _croppedHeight); // height + XSync(_x11Display, False); - - if (_XShmAvailable) { + + if (!_XShmAvailable) + { XShmGetImage(_x11Display, _pixmap, _xImage, 0, 0, AllPlanes); - } else { + } + else + { _xImage = XGetImage(_x11Display, _pixmap, 0, 0, _croppedWidth, _croppedHeight, AllPlanes, ZPixmap); } - } else { + } + else + { if (_XShmAvailable && !_useXGetImage) { XShmGetImage(_x11Display, _window, _xImage, _cropLeft, _cropTop, AllPlanes); - } else { + } + else + { _xImage = XGetImage(_x11Display, _window, _cropLeft, _cropTop, _croppedWidth, _croppedHeight, AllPlanes, ZPixmap); } } @@ -220,7 +303,8 @@ int X11Grabber::updateScreenDimensions() return 0; } - if (_screenWidth || _screenHeight) { + if (_screenWidth || _screenHeight) + { freeResources(); } @@ -228,17 +312,29 @@ int X11Grabber::updateScreenDimensions() _screenWidth = _windowAttr.width; _screenHeight = _windowAttr.height; - _croppedWidth = (_screenWidth > unsigned(_cropLeft + _cropRight)) - ? (_screenWidth - _cropLeft - _cropRight) - : _screenWidth; - - _croppedHeight = (_screenHeight > unsigned(_cropTop + _cropBottom)) - ? (_screenHeight - _cropTop - _cropBottom) - : _screenHeight; - - if (_XRenderAvailable && !_useXGetImage) { + // Image scaling is performed by XRender when available, otherwise by ImageResampler + if (_XRenderAvailable && !_useXGetImage) + { + _croppedWidth = (_screenWidth > unsigned(_cropLeft + _cropRight)) + ? ((_screenWidth - _cropLeft - _cropRight) / _horizontalDecimation) + : _screenWidth / _horizontalDecimation; + + _croppedHeight = (_screenHeight > unsigned(_cropTop + _cropBottom)) + ? ((_screenHeight - _cropTop - _cropBottom) / _verticalDecimation) + : _screenHeight / _verticalDecimation; + Info(_log, "Using XRender for grabbing"); - } else { + } + else + { + _croppedWidth = (_screenWidth > unsigned(_cropLeft + _cropRight)) + ? (_screenWidth - _cropLeft - _cropRight) + : _screenWidth; + + _croppedHeight = (_screenHeight > unsigned(_cropTop + _cropBottom)) + ? (_screenHeight - _cropTop - _cropBottom) + : _screenHeight; + Info(_log, "Using XGetImage for grabbing"); }