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