You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Image.cxx 8.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. /* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved.
  2. * Copyright (C) 2004-2005 Constantin Kaplinsky. All Rights Reserved.
  3. *
  4. * This is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This software is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this software; if not, write to the Free Software
  16. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
  17. * USA.
  18. */
  19. //
  20. // Image.cxx
  21. //
  22. #ifdef HAVE_CONFIG_H
  23. #include <config.h>
  24. #endif
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include <sys/types.h>
  28. #include <sys/ipc.h>
  29. #include <sys/shm.h>
  30. #include <rfb/LogWriter.h>
  31. #include <x0vncserver/Image.h>
  32. //
  33. // ImageCleanup is used to delete Image instances automatically on
  34. // program shutdown. This is important for shared memory images.
  35. //
  36. #include <list>
  37. class ImageCleanup {
  38. public:
  39. std::list<Image *> images;
  40. ~ImageCleanup()
  41. {
  42. while (!images.empty()) {
  43. delete images.front();
  44. }
  45. }
  46. };
  47. ImageCleanup imageCleanup;
  48. //
  49. // Image class implementation.
  50. //
  51. static rfb::LogWriter vlog("Image");
  52. Image::Image(Display *d)
  53. : xim(NULL), dpy(d)
  54. {
  55. imageCleanup.images.push_back(this);
  56. }
  57. Image::Image(Display *d, int width, int height)
  58. : xim(NULL), dpy(d)
  59. {
  60. imageCleanup.images.push_back(this);
  61. Init(width, height);
  62. }
  63. void Image::Init(int width, int height)
  64. {
  65. Visual* vis = DefaultVisual(dpy, DefaultScreen(dpy));
  66. if (vis->c_class != TrueColor) {
  67. vlog.error("pseudocolour not supported");
  68. exit(1);
  69. }
  70. xim = XCreateImage(dpy, vis, DefaultDepth(dpy, DefaultScreen(dpy)),
  71. ZPixmap, 0, 0, width, height, BitmapPad(dpy), 0);
  72. xim->data = (char *)malloc(xim->bytes_per_line * xim->height);
  73. if (xim->data == NULL) {
  74. vlog.error("malloc() failed");
  75. exit(1);
  76. }
  77. }
  78. Image::~Image()
  79. {
  80. imageCleanup.images.remove(this);
  81. // XDestroyImage will free xim->data if necessary
  82. if (xim != NULL)
  83. XDestroyImage(xim);
  84. }
  85. void Image::get(Window wnd, int x, int y)
  86. {
  87. get(wnd, x, y, xim->width, xim->height);
  88. }
  89. void Image::get(Window wnd, int x, int y, int w, int h,
  90. int dst_x, int dst_y)
  91. {
  92. XGetSubImage(dpy, wnd, x, y, w, h, AllPlanes, ZPixmap, xim, dst_x, dst_y);
  93. }
  94. //
  95. // Copying pixels from one image to another.
  96. //
  97. // FIXME: Use Point and Rect structures?
  98. // FIXME: Too many similar methods?
  99. //
  100. inline
  101. void Image::copyPixels(XImage *src,
  102. int dst_x, int dst_y,
  103. int src_x, int src_y,
  104. int w, int h)
  105. {
  106. const char *srcOffset =
  107. src->data + (src_y * src->bytes_per_line +
  108. src_x * (src->bits_per_pixel / 8));
  109. char *dstOffset =
  110. xim->data + (dst_y * xim->bytes_per_line +
  111. dst_x * (xim->bits_per_pixel / 8));
  112. int rowLength = w * (xim->bits_per_pixel / 8);
  113. for (int i = 0; i < h ; i++) {
  114. memcpy(dstOffset, srcOffset, rowLength);
  115. srcOffset += src->bytes_per_line;
  116. dstOffset += xim->bytes_per_line;
  117. }
  118. }
  119. void Image::updateRect(XImage *src, int dst_x, int dst_y)
  120. {
  121. // Limit width and height at destination image size.
  122. int w = src->width;
  123. if (dst_x + w > xim->width)
  124. w = xim->width - dst_x;
  125. int h = src->height;
  126. if (dst_y + h > xim->height)
  127. h = xim->height - dst_y;
  128. copyPixels(src, dst_x, dst_y, 0, 0, w, h);
  129. }
  130. void Image::updateRect(Image *src, int dst_x, int dst_y)
  131. {
  132. updateRect(src->xim, dst_x, dst_y);
  133. }
  134. void Image::updateRect(XImage *src, int dst_x, int dst_y, int w, int h)
  135. {
  136. // Correct width and height if necessary.
  137. if (w > src->width)
  138. w = src->width;
  139. if (dst_x + w > xim->width)
  140. w = xim->width - dst_x;
  141. if (h > src->height)
  142. h = src->height;
  143. if (dst_y + h > xim->height)
  144. h = xim->height - dst_y;
  145. copyPixels(src, dst_x, dst_y, 0, 0, w, h);
  146. }
  147. void Image::updateRect(Image *src, int dst_x, int dst_y, int w, int h)
  148. {
  149. updateRect(src->xim, dst_x, dst_y, w, h);
  150. }
  151. void Image::updateRect(XImage *src, int dst_x, int dst_y,
  152. int src_x, int src_y, int w, int h)
  153. {
  154. // Correct width and height if necessary.
  155. if (src_x + w > src->width)
  156. w = src->width - src_x;
  157. if (dst_x + w > xim->width)
  158. w = xim->width - dst_x;
  159. if (src_y + h > src->height)
  160. h = src->height - src_y;
  161. if (dst_y + h > xim->height)
  162. h = xim->height - dst_y;
  163. copyPixels(src, dst_x, dst_y, src_x, src_y, w, h);
  164. }
  165. void Image::updateRect(Image *src, int dst_x, int dst_y,
  166. int src_x, int src_y, int w, int h)
  167. {
  168. updateRect(src->xim, dst_x, dst_y, src_x, src_y, w, h);
  169. }
  170. //
  171. // ShmImage class implementation.
  172. //
  173. static bool caughtShmError = false;
  174. static int ShmCreationXErrorHandler(Display* /*dpy*/,
  175. XErrorEvent* /*error*/)
  176. {
  177. caughtShmError = true;
  178. return 0;
  179. }
  180. ShmImage::ShmImage(Display *d)
  181. : Image(d), shminfo(NULL)
  182. {
  183. }
  184. ShmImage::ShmImage(Display *d, int width, int height)
  185. : Image(d), shminfo(NULL)
  186. {
  187. Init(width, height);
  188. }
  189. // FIXME: Remove duplication of cleanup operations.
  190. void ShmImage::Init(int width, int height, const XVisualInfo *vinfo)
  191. {
  192. int major, minor;
  193. Bool pixmaps;
  194. if (!XShmQueryVersion(dpy, &major, &minor, &pixmaps)) {
  195. vlog.error("XShmQueryVersion() failed");
  196. return;
  197. }
  198. Visual *visual;
  199. int depth;
  200. if (vinfo == NULL) {
  201. visual = DefaultVisual(dpy, DefaultScreen(dpy));
  202. depth = DefaultDepth(dpy, DefaultScreen(dpy));
  203. } else {
  204. visual = vinfo->visual;
  205. depth = vinfo->depth;
  206. }
  207. if (visual->c_class != TrueColor) {
  208. vlog.error("pseudocolour not supported");
  209. exit(1);
  210. }
  211. shminfo = new XShmSegmentInfo;
  212. xim = XShmCreateImage(dpy, visual, depth, ZPixmap, 0, shminfo,
  213. width, height);
  214. if (xim == NULL) {
  215. vlog.error("XShmCreateImage() failed");
  216. delete shminfo;
  217. shminfo = NULL;
  218. return;
  219. }
  220. shminfo->shmid = shmget(IPC_PRIVATE,
  221. xim->bytes_per_line * xim->height,
  222. IPC_CREAT|0777);
  223. if (shminfo->shmid == -1) {
  224. perror("shmget");
  225. vlog.error("shmget() failed (%d bytes requested)",
  226. int(xim->bytes_per_line * xim->height));
  227. XDestroyImage(xim);
  228. xim = NULL;
  229. delete shminfo;
  230. shminfo = NULL;
  231. return;
  232. }
  233. shminfo->shmaddr = xim->data = (char *)shmat(shminfo->shmid, 0, 0);
  234. if (shminfo->shmaddr == (char *)-1) {
  235. perror("shmat");
  236. vlog.error("shmat() failed (%d bytes requested)",
  237. int(xim->bytes_per_line * xim->height));
  238. shmctl(shminfo->shmid, IPC_RMID, 0);
  239. XDestroyImage(xim);
  240. xim = NULL;
  241. delete shminfo;
  242. shminfo = NULL;
  243. return;
  244. }
  245. shminfo->readOnly = False;
  246. XErrorHandler oldHdlr = XSetErrorHandler(ShmCreationXErrorHandler);
  247. XShmAttach(dpy, shminfo);
  248. XSync(dpy, False);
  249. XSetErrorHandler(oldHdlr);
  250. if (caughtShmError) {
  251. vlog.error("XShmAttach() failed");
  252. shmdt(shminfo->shmaddr);
  253. shmctl(shminfo->shmid, IPC_RMID, 0);
  254. XDestroyImage(xim);
  255. xim = NULL;
  256. delete shminfo;
  257. shminfo = NULL;
  258. return;
  259. }
  260. }
  261. ShmImage::~ShmImage()
  262. {
  263. // FIXME: Destroy image as described in MIT-SHM documentation.
  264. if (shminfo != NULL) {
  265. shmdt(shminfo->shmaddr);
  266. shmctl(shminfo->shmid, IPC_RMID, 0);
  267. delete shminfo;
  268. }
  269. }
  270. void ShmImage::get(Window wnd, int x, int y)
  271. {
  272. XShmGetImage(dpy, wnd, xim, x, y, AllPlanes);
  273. }
  274. void ShmImage::get(Window wnd, int x, int y, int w, int h,
  275. int dst_x, int dst_y)
  276. {
  277. // XShmGetImage is faster, but can only retrieve the entire
  278. // window. Use it for large reads.
  279. if (x == dst_x && y == dst_y && (long)w * h > (long)xim->width * xim->height / 4) {
  280. XShmGetImage(dpy, wnd, xim, 0, 0, AllPlanes);
  281. } else {
  282. XGetSubImage(dpy, wnd, x, y, w, h, AllPlanes, ZPixmap, xim, dst_x, dst_y);
  283. }
  284. }
  285. //
  286. // ImageFactory class implementation
  287. //
  288. // FIXME: Make ImageFactory always create images of the same class?
  289. //
  290. ImageFactory::ImageFactory(bool allowShm)
  291. : mayUseShm(allowShm)
  292. {
  293. }
  294. ImageFactory::~ImageFactory()
  295. {
  296. }
  297. Image *ImageFactory::newImage(Display *d, int width, int height)
  298. {
  299. Image *image = NULL;
  300. // Now, try to use shared memory image.
  301. if (mayUseShm) {
  302. image = new ShmImage(d, width, height);
  303. if (image->xim != NULL) {
  304. return image;
  305. }
  306. delete image;
  307. vlog.error("Failed to create SHM image, falling back to Xlib image");
  308. }
  309. // Fall back to Xlib image.
  310. image = new Image(d, width, height);
  311. return image;
  312. }