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.5KB

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