PageRenderTime 47ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/apps/desktop/libvncserver/scale.c

http://ftk.googlecode.com/
C | 416 lines | 276 code | 51 blank | 89 comment | 45 complexity | b7b719406c97869bca3a22e4b718a7c5 MD5 | raw file
Possible License(s): LGPL-3.0
  1. /*
  2. * scale.c - deal with server-side scaling.
  3. */
  4. /*
  5. * Copyright (C) 2005 Rohit Kumar, Johannes E. Schindelin
  6. * Copyright (C) 2002 RealVNC Ltd.
  7. * OSXvnc Copyright (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>.
  8. * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge.
  9. * All Rights Reserved.
  10. *
  11. * This is free software; you can redistribute it and/or modify
  12. * it under the terms of the GNU General Public License as published by
  13. * the Free Software Foundation; either version 2 of the License, or
  14. * (at your option) any later version.
  15. *
  16. * This software is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU General Public License
  22. * along with this software; if not, write to the Free Software
  23. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
  24. * USA.
  25. */
  26. #ifdef __STRICT_ANSI__
  27. #define _BSD_SOURCE
  28. #endif
  29. #include <string.h>
  30. #include <rfb/rfb.h>
  31. #include <rfb/rfbregion.h>
  32. #include "private.h"
  33. #ifdef LIBVNCSERVER_HAVE_FCNTL_H
  34. #include <fcntl.h>
  35. #endif
  36. #ifdef WIN32
  37. #define write(sock,buf,len) send(sock,buf,len,0)
  38. #else
  39. #ifdef LIBVNCSERVER_HAVE_UNISTD_H
  40. #include <unistd.h>
  41. #endif
  42. #include <pwd.h>
  43. #ifdef LIBVNCSERVER_HAVE_SYS_SOCKET_H
  44. #include <sys/socket.h>
  45. #endif
  46. #ifdef LIBVNCSERVER_HAVE_NETINET_IN_H
  47. #include <netinet/in.h>
  48. #include <netinet/tcp.h>
  49. #include <arpa/inet.h>
  50. #endif
  51. #endif
  52. #ifdef CORBA
  53. #include <vncserverctrl.h>
  54. #endif
  55. #ifdef DEBUGPROTO
  56. #undef DEBUGPROTO
  57. #define DEBUGPROTO(x) x
  58. #else
  59. #define DEBUGPROTO(x)
  60. #endif
  61. /****************************/
  62. #define CEIL(x) ( (double) ((int) (x)) == (x) ? \
  63. (double) ((int) (x)) : (double) ((int) (x) + 1) )
  64. #define FLOOR(x) ( (double) ((int) (x)) )
  65. int ScaleX(rfbScreenInfoPtr from, rfbScreenInfoPtr to, int x)
  66. {
  67. if ((from==to) || (from==NULL) || (to==NULL)) return x;
  68. return ((int)(((double) x / (double)from->width) * (double)to->width ));
  69. }
  70. int ScaleY(rfbScreenInfoPtr from, rfbScreenInfoPtr to, int y)
  71. {
  72. if ((from==to) || (from==NULL) || (to==NULL)) return y;
  73. return ((int)(((double) y / (double)from->height) * (double)to->height ));
  74. }
  75. /* So, all of the encodings point to the ->screen->frameBuffer,
  76. * We need to change this!
  77. */
  78. void rfbScaledCorrection(rfbScreenInfoPtr from, rfbScreenInfoPtr to, int *x, int *y, int *w, int *h, char *function)
  79. {
  80. double x1,y1,w1,h1, x2, y2, w2, h2;
  81. double scaleW = ((double) to->width) / ((double) from->width);
  82. double scaleH = ((double) to->height) / ((double) from->height);
  83. /*
  84. * rfbLog("rfbScaledCorrection(%p -> %p, %dx%d->%dx%d (%dXx%dY-%dWx%dH)\n",
  85. * from, to, from->width, from->height, to->width, to->height, *x, *y, *w, *h);
  86. */
  87. /* If it's the original framebuffer... */
  88. if (from==to) return;
  89. x1 = ((double) *x) * scaleW;
  90. y1 = ((double) *y) * scaleH;
  91. w1 = ((double) *w) * scaleW;
  92. h1 = ((double) *h) * scaleH;
  93. /*cast from double to int is same as "*x = floor(x1);" */
  94. x2 = FLOOR(x1);
  95. y2 = FLOOR(y1);
  96. /* include into W and H the jitter of scaling X and Y */
  97. w2 = CEIL(w1 + ( x1 - x2 ));
  98. h2 = CEIL(h1 + ( y1 - y2 ));
  99. /*
  100. * rfbLog("%s (%dXx%dY-%dWx%dH -> %fXx%fY-%fWx%fH) {%dWx%dH -> %dWx%dH}\n",
  101. * function, *x, *y, *w, *h, x2, y2, w2, h2,
  102. * from->width, from->height, to->width, to->height);
  103. */
  104. /* simulate ceil() without math library */
  105. *x = (int)x2;
  106. *y = (int)y2;
  107. *w = (int)w2;
  108. *h = (int)h2;
  109. /* Small changes for a thumbnail may be scaled to zero */
  110. if (*w==0) (*w)++;
  111. if (*h==0) (*h)++;
  112. /* scaling from small to big may overstep the size a bit */
  113. if (*x+*w > to->width) *w=to->width - *x;
  114. if (*y+*h > to->height) *h=to->height - *y;
  115. }
  116. void rfbScaledScreenUpdateRect(rfbScreenInfoPtr screen, rfbScreenInfoPtr ptr, int x0, int y0, int w0, int h0)
  117. {
  118. int x,y,w,v,z;
  119. int x1, y1, w1, h1;
  120. int bitsPerPixel, bytesPerPixel, bytesPerLine, areaX, areaY, area2;
  121. unsigned char *srcptr, *dstptr;
  122. /* Nothing to do!!! */
  123. if (screen==ptr) return;
  124. x1 = x0;
  125. y1 = y0;
  126. w1 = w0;
  127. h1 = h0;
  128. rfbScaledCorrection(screen, ptr, &x1, &y1, &w1, &h1, "rfbScaledScreenUpdateRect");
  129. bitsPerPixel = screen->bitsPerPixel;
  130. bytesPerPixel = bitsPerPixel / 8;
  131. bytesPerLine = w1 * bytesPerPixel;
  132. srcptr = (unsigned char *)(screen->frameBuffer +
  133. (y0 * screen->paddedWidthInBytes + x0 * bytesPerPixel));
  134. dstptr = (unsigned char *)(ptr->frameBuffer +
  135. ( y1 * ptr->paddedWidthInBytes + x1 * bytesPerPixel));
  136. /* The area of the source framebuffer for each destination pixel */
  137. areaX = ScaleX(ptr,screen,1);
  138. areaY = ScaleY(ptr,screen,1);
  139. area2 = areaX*areaY;
  140. /* Ensure that we do not go out of bounds */
  141. if ((x1+w1) > (ptr->width))
  142. {
  143. if (x1==0) w1=ptr->width; else x1 = ptr->width - w1;
  144. }
  145. if ((y1+h1) > (ptr->height))
  146. {
  147. if (y1==0) h1=ptr->height; else y1 = ptr->height - h1;
  148. }
  149. /*
  150. * rfbLog("rfbScaledScreenUpdateRect(%dXx%dY-%dWx%dH -> %dXx%dY-%dWx%dH <%dx%d>) {%dWx%dH -> %dWx%dH} 0x%p\n",
  151. * x0, y0, w0, h0, x1, y1, w1, h1, areaX, areaY,
  152. * screen->width, screen->height, ptr->width, ptr->height, ptr->frameBuffer);
  153. */
  154. if (screen->serverFormat.trueColour) { /* Blend neighbouring pixels together */
  155. unsigned char *srcptr2;
  156. unsigned long pixel_value, red, green, blue;
  157. unsigned int redShift = screen->serverFormat.redShift;
  158. unsigned int greenShift = screen->serverFormat.greenShift;
  159. unsigned int blueShift = screen->serverFormat.blueShift;
  160. unsigned long redMax = screen->serverFormat.redMax;
  161. unsigned long greenMax = screen->serverFormat.greenMax;
  162. unsigned long blueMax = screen->serverFormat.blueMax;
  163. /* for each *destination* pixel... */
  164. for (y = 0; y < h1; y++) {
  165. for (x = 0; x < w1; x++) {
  166. red = green = blue = 0;
  167. /* Get the totals for rgb from the source grid... */
  168. for (w = 0; w < areaX; w++) {
  169. for (v = 0; v < areaY; v++) {
  170. srcptr2 = &srcptr[(((x * areaX) + v) * bytesPerPixel) +
  171. (w * screen->paddedWidthInBytes)];
  172. pixel_value = 0;
  173. switch (bytesPerPixel) {
  174. case 4: pixel_value = *((unsigned int *)srcptr2); break;
  175. case 2: pixel_value = *((unsigned short *)srcptr2); break;
  176. case 1: pixel_value = *((unsigned char *)srcptr2); break;
  177. default:
  178. /* fixme: endianess problem? */
  179. for (z = 0; z < bytesPerPixel; z++)
  180. pixel_value += (srcptr2[z] << (8 * z));
  181. break;
  182. }
  183. /*
  184. srcptr2 += bytesPerPixel;
  185. */
  186. red += ((pixel_value >> redShift) & redMax);
  187. green += ((pixel_value >> greenShift) & greenMax);
  188. blue += ((pixel_value >> blueShift) & blueMax);
  189. }
  190. }
  191. /* We now have a total for all of the colors, find the average! */
  192. red /= area2;
  193. green /= area2;
  194. blue /= area2;
  195. /* Stuff the new value back into memory */
  196. pixel_value = ((red & redMax) << redShift) | ((green & greenMax) << greenShift) | ((blue & blueMax) << blueShift);
  197. switch (bytesPerPixel) {
  198. case 4: *((unsigned int *)dstptr) = (unsigned int) pixel_value; break;
  199. case 2: *((unsigned short *)dstptr) = (unsigned short) pixel_value; break;
  200. case 1: *((unsigned char *)dstptr) = (unsigned char) pixel_value; break;
  201. default:
  202. /* fixme: endianess problem? */
  203. for (z = 0; z < bytesPerPixel; z++)
  204. dstptr[z]=(pixel_value >> (8 * z)) & 0xff;
  205. break;
  206. }
  207. dstptr += bytesPerPixel;
  208. }
  209. srcptr += (screen->paddedWidthInBytes * areaY);
  210. dstptr += (ptr->paddedWidthInBytes - bytesPerLine);
  211. }
  212. } else
  213. { /* Not truecolour, so we can't blend. Just use the top-left pixel instead */
  214. for (y = y1; y < (y1+h1); y++) {
  215. for (x = x1; x < (x1+w1); x++)
  216. memcpy (&ptr->frameBuffer[(y *ptr->paddedWidthInBytes) + (x * bytesPerPixel)],
  217. &screen->frameBuffer[(y * areaY * screen->paddedWidthInBytes) + (x *areaX * bytesPerPixel)], bytesPerPixel);
  218. }
  219. }
  220. }
  221. void rfbScaledScreenUpdate(rfbScreenInfoPtr screen, int x1, int y1, int x2, int y2)
  222. {
  223. /* ok, now the task is to update each and every scaled version of the framebuffer
  224. * and we only have to do this for this specific changed rectangle!
  225. */
  226. rfbScreenInfoPtr ptr;
  227. int count=0;
  228. /* We don't point to cl->screen as it is the original */
  229. for (ptr=screen->scaledScreenNext;ptr!=NULL;ptr=ptr->scaledScreenNext)
  230. {
  231. /* Only update if it has active clients... */
  232. if (ptr->scaledScreenRefCount>0)
  233. {
  234. rfbScaledScreenUpdateRect(screen, ptr, x1, y1, x2-x1, y2-y1);
  235. count++;
  236. }
  237. }
  238. }
  239. /* Create a new scaled version of the framebuffer */
  240. rfbScreenInfoPtr rfbScaledScreenAllocate(rfbClientPtr cl, int width, int height)
  241. {
  242. rfbScreenInfoPtr ptr;
  243. ptr = malloc(sizeof(rfbScreenInfo));
  244. if (ptr!=NULL)
  245. {
  246. /* copy *everything* (we don't use most of it, but just in case) */
  247. memcpy(ptr, cl->screen, sizeof(rfbScreenInfo));
  248. ptr->width = width;
  249. ptr->height = height;
  250. ptr->paddedWidthInBytes = (ptr->bitsPerPixel/8)*ptr->width;
  251. /* Need to by multiples of 4 for Sparc systems */
  252. ptr->paddedWidthInBytes += (ptr->paddedWidthInBytes % 4);
  253. /* Reset the reference count to 0! */
  254. ptr->scaledScreenRefCount = 0;
  255. ptr->sizeInBytes = ptr->paddedWidthInBytes * ptr->height;
  256. ptr->serverFormat = cl->screen->serverFormat;
  257. ptr->frameBuffer = malloc(ptr->sizeInBytes);
  258. if (ptr->frameBuffer!=NULL)
  259. {
  260. /* Reset to a known condition: scale the entire framebuffer */
  261. rfbScaledScreenUpdateRect(cl->screen, ptr, 0, 0, cl->screen->width, cl->screen->height);
  262. /* Now, insert into the chain */
  263. LOCK(cl->updateMutex);
  264. ptr->scaledScreenNext = cl->screen->scaledScreenNext;
  265. cl->screen->scaledScreenNext = ptr;
  266. UNLOCK(cl->updateMutex);
  267. }
  268. else
  269. {
  270. /* Failed to malloc the new frameBuffer, cleanup */
  271. free(ptr);
  272. ptr=NULL;
  273. }
  274. }
  275. return ptr;
  276. }
  277. /* Find an active scaled version of the framebuffer
  278. * TODO: implement a refcount per scaled screen to prevent
  279. * unreferenced scaled screens from hanging around
  280. */
  281. rfbScreenInfoPtr rfbScalingFind(rfbClientPtr cl, int width, int height)
  282. {
  283. rfbScreenInfoPtr ptr;
  284. /* include the original in the search (ie: fine 1:1 scaled version of the frameBuffer) */
  285. for (ptr=cl->screen; ptr!=NULL; ptr=ptr->scaledScreenNext)
  286. {
  287. if ((ptr->width==width) && (ptr->height==height))
  288. return ptr;
  289. }
  290. return NULL;
  291. }
  292. /* Future needs "scale to 320x240, as that's the client's screen size */
  293. void rfbScalingSetup(rfbClientPtr cl, int width, int height)
  294. {
  295. rfbScreenInfoPtr ptr;
  296. ptr = rfbScalingFind(cl,width,height);
  297. if (ptr==NULL)
  298. ptr = rfbScaledScreenAllocate(cl,width,height);
  299. /* Now, there is a new screen available (if ptr is not NULL) */
  300. if (ptr!=NULL)
  301. {
  302. /* Update it! */
  303. if (ptr->scaledScreenRefCount<1)
  304. rfbScaledScreenUpdateRect(cl->screen, ptr, 0, 0, cl->screen->width, cl->screen->height);
  305. /*
  306. * rfbLog("Taking one from %dx%d-%d and adding it to %dx%d-%d\n",
  307. * cl->scaledScreen->width, cl->scaledScreen->height,
  308. * cl->scaledScreen->scaledScreenRefCount,
  309. * ptr->width, ptr->height, ptr->scaledScreenRefCount);
  310. */
  311. LOCK(cl->updateMutex);
  312. cl->scaledScreen->scaledScreenRefCount--;
  313. ptr->scaledScreenRefCount++;
  314. cl->scaledScreen=ptr;
  315. cl->newFBSizePending = TRUE;
  316. UNLOCK(cl->updateMutex);
  317. rfbLog("Scaling to %dx%d (refcount=%d)\n",width,height,ptr->scaledScreenRefCount);
  318. }
  319. else
  320. rfbLog("Scaling to %dx%d failed, leaving things alone\n",width,height);
  321. }
  322. int rfbSendNewScaleSize(rfbClientPtr cl)
  323. {
  324. /* if the client supports newFBsize Encoding, use it */
  325. if (cl->useNewFBSize && cl->newFBSizePending)
  326. return FALSE;
  327. LOCK(cl->updateMutex);
  328. cl->newFBSizePending = FALSE;
  329. UNLOCK(cl->updateMutex);
  330. if (cl->PalmVNC==TRUE)
  331. {
  332. rfbPalmVNCReSizeFrameBufferMsg pmsg;
  333. pmsg.type = rfbPalmVNCReSizeFrameBuffer;
  334. pmsg.pad1 = 0;
  335. pmsg.desktop_w = Swap16IfLE(cl->screen->width);
  336. pmsg.desktop_h = Swap16IfLE(cl->screen->height);
  337. pmsg.buffer_w = Swap16IfLE(cl->scaledScreen->width);
  338. pmsg.buffer_h = Swap16IfLE(cl->scaledScreen->height);
  339. pmsg.pad2 = 0;
  340. rfbLog("Sending a response to a PalmVNC style frameuffer resize event (%dx%d)\n", cl->scaledScreen->width, cl->scaledScreen->height);
  341. if (rfbWriteExact(cl, (char *)&pmsg, sz_rfbPalmVNCReSizeFrameBufferMsg) < 0) {
  342. rfbLogPerror("rfbNewClient: write");
  343. rfbCloseClient(cl);
  344. rfbClientConnectionGone(cl);
  345. return FALSE;
  346. }
  347. }
  348. else
  349. {
  350. rfbResizeFrameBufferMsg rmsg;
  351. rmsg.type = rfbResizeFrameBuffer;
  352. rmsg.pad1=0;
  353. rmsg.framebufferWidth = Swap16IfLE(cl->scaledScreen->width);
  354. rmsg.framebufferHeigth = Swap16IfLE(cl->scaledScreen->height);
  355. rfbLog("Sending a response to a UltraVNC style frameuffer resize event (%dx%d)\n", cl->scaledScreen->width, cl->scaledScreen->height);
  356. if (rfbWriteExact(cl, (char *)&rmsg, sz_rfbResizeFrameBufferMsg) < 0) {
  357. rfbLogPerror("rfbNewClient: write");
  358. rfbCloseClient(cl);
  359. rfbClientConnectionGone(cl);
  360. return FALSE;
  361. }
  362. }
  363. return TRUE;
  364. }
  365. /****************************/