/src/libvncserver/src/libvncserver/scale.c
Line | Count | Source |
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 | 7.49k | #define CEIL(x) ( (double) ((int) (x)) == (x) ? \ |
50 | 7.49k | (double) ((int) (x)) : (double) ((int) (x) + 1) ) |
51 | 7.49k | #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 | 88 | { |
66 | 88 | int remainder = value & 3; |
67 | 88 | if (!remainder) return value; |
68 | 0 | return value + 4 - remainder; |
69 | 88 | } |
70 | | |
71 | | int ScaleX(rfbScreenInfoPtr from, rfbScreenInfoPtr to, int x) |
72 | 15.2k | { |
73 | 15.2k | if ((from==to) || (from==NULL) || (to==NULL)) return x; |
74 | 13.5k | return ((int)(((double) x / (double)from->width) * (double)to->width )); |
75 | 15.2k | } |
76 | | |
77 | | int ScaleY(rfbScreenInfoPtr from, rfbScreenInfoPtr to, int y) |
78 | 15.2k | { |
79 | 15.2k | if ((from==to) || (from==NULL) || (to==NULL)) return y; |
80 | 13.5k | return ((int)(((double) y / (double)from->height) * (double)to->height )); |
81 | 15.2k | } |
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 | 6.43k | { |
88 | 6.43k | double x1,y1,w1,h1, x2, y2, w2, h2; |
89 | 6.43k | double scaleW = ((double) to->width) / ((double) from->width); |
90 | 6.43k | 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 | 6.43k | if (from==to) return; |
100 | | |
101 | 3.74k | x1 = ((double) *x) * scaleW; |
102 | 3.74k | y1 = ((double) *y) * scaleH; |
103 | 3.74k | w1 = ((double) *w) * scaleW; |
104 | 3.74k | h1 = ((double) *h) * scaleH; |
105 | | |
106 | | |
107 | | /*cast from double to int is same as "*x = floor(x1);" */ |
108 | 3.74k | x2 = FLOOR(x1); |
109 | 3.74k | y2 = FLOOR(y1); |
110 | | |
111 | | /* include into W and H the jitter of scaling X and Y */ |
112 | 3.74k | w2 = CEIL(w1 + ( x1 - x2 )); |
113 | 3.74k | 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 | 3.74k | *x = (int)x2; |
123 | 3.74k | *y = (int)y2; |
124 | 3.74k | *w = (int)w2; |
125 | 3.74k | *h = (int)h2; |
126 | | |
127 | | /* Small changes for a thumbnail may be scaled to zero */ |
128 | 3.74k | if (*w==0) (*w)++; |
129 | 3.74k | if (*h==0) (*h)++; |
130 | | /* scaling from small to big may overstep the size a bit */ |
131 | 3.74k | if (*x+*w > to->width) *w=to->width - *x; |
132 | 3.74k | if (*y+*h > to->height) *h=to->height - *y; |
133 | 3.74k | } |
134 | | |
135 | | void rfbScaledScreenUpdateRect(rfbScreenInfoPtr screen, rfbScreenInfoPtr ptr, int x0, int y0, int w0, int h0) |
136 | 2.44k | { |
137 | 2.44k | int x,y,w,v,z; |
138 | 2.44k | int x1, y1, w1, h1; |
139 | 2.44k | int bitsPerPixel, bytesPerPixel, bytesPerLine, areaX, areaY, area2; |
140 | 2.44k | unsigned char *srcptr, *dstptr; |
141 | | |
142 | | /* Nothing to do!!! */ |
143 | 2.44k | if (screen==ptr) return; |
144 | | |
145 | 2.14k | x1 = x0; |
146 | 2.14k | y1 = y0; |
147 | 2.14k | w1 = w0; |
148 | 2.14k | h1 = h0; |
149 | | |
150 | 2.14k | rfbScaledCorrection(screen, ptr, &x1, &y1, &w1, &h1, "rfbScaledScreenUpdateRect"); |
151 | 2.14k | x0 = ScaleX(ptr, screen, x1); |
152 | 2.14k | y0 = ScaleY(ptr, screen, y1); |
153 | 2.14k | w0 = ScaleX(ptr, screen, w1); |
154 | 2.14k | h0 = ScaleY(ptr, screen, h1); |
155 | | |
156 | 2.14k | bitsPerPixel = screen->bitsPerPixel; |
157 | 2.14k | bytesPerPixel = bitsPerPixel / 8; |
158 | 2.14k | bytesPerLine = w1 * bytesPerPixel; |
159 | 2.14k | srcptr = (unsigned char *)(screen->frameBuffer + |
160 | 2.14k | (y0 * screen->paddedWidthInBytes + x0 * bytesPerPixel)); |
161 | 2.14k | dstptr = (unsigned char *)(ptr->frameBuffer + |
162 | 2.14k | ( y1 * ptr->paddedWidthInBytes + x1 * bytesPerPixel)); |
163 | | /* The area of the source framebuffer for each destination pixel */ |
164 | 2.14k | areaX = ScaleX(ptr,screen,1); |
165 | 2.14k | areaY = ScaleY(ptr,screen,1); |
166 | 2.14k | area2 = areaX*areaY; |
167 | | |
168 | | |
169 | | /* Ensure that we do not go out of bounds */ |
170 | 2.14k | if ((x1+w1) > (ptr->width)) |
171 | 0 | { |
172 | 0 | if (x1==0) w1=ptr->width; else x1 = ptr->width - w1; |
173 | 0 | } |
174 | 2.14k | if ((y1+h1) > (ptr->height)) |
175 | 0 | { |
176 | 0 | if (y1==0) h1=ptr->height; else y1 = ptr->height - h1; |
177 | 0 | } |
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 | 2.14k | if (screen->serverFormat.trueColour) { /* Blend neighbouring pixels together */ |
185 | 2.14k | unsigned char *srcptr2; |
186 | 2.14k | unsigned long pixel_value, red, green, blue; |
187 | 2.14k | unsigned int redShift = screen->serverFormat.redShift; |
188 | 2.14k | unsigned int greenShift = screen->serverFormat.greenShift; |
189 | 2.14k | unsigned int blueShift = screen->serverFormat.blueShift; |
190 | 2.14k | unsigned long redMax = screen->serverFormat.redMax; |
191 | 2.14k | unsigned long greenMax = screen->serverFormat.greenMax; |
192 | 2.14k | unsigned long blueMax = screen->serverFormat.blueMax; |
193 | | |
194 | | /* for each *destination* pixel... */ |
195 | 38.9k | for (y = 0; y < h1; y++) { |
196 | 1.96M | for (x = 0; x < w1; x++) { |
197 | 1.92M | red = green = blue = 0; |
198 | | /* Get the totals for rgb from the source grid... */ |
199 | 16.5M | for (w = 0; w < areaX; w++) { |
200 | 268M | for (v = 0; v < areaY; v++) { |
201 | 253M | srcptr2 = &srcptr[(((x * areaX) + w) * bytesPerPixel) + |
202 | 253M | (v * screen->paddedWidthInBytes)]; |
203 | 253M | pixel_value = 0; |
204 | | |
205 | | |
206 | 253M | switch (bytesPerPixel) { |
207 | 253M | case 4: pixel_value = *((unsigned int *)srcptr2); break; |
208 | 0 | case 2: pixel_value = *((unsigned short *)srcptr2); break; |
209 | 0 | case 1: pixel_value = *((unsigned char *)srcptr2); break; |
210 | 0 | default: |
211 | | /* fixme: endianness problem? */ |
212 | 0 | for (z = 0; z < bytesPerPixel; z++) |
213 | 0 | pixel_value += ((unsigned long)srcptr2[z] << (8 * z)); |
214 | 0 | break; |
215 | 253M | } |
216 | | /* |
217 | | srcptr2 += bytesPerPixel; |
218 | | */ |
219 | | |
220 | 253M | red += ((pixel_value >> redShift) & redMax); |
221 | 253M | green += ((pixel_value >> greenShift) & greenMax); |
222 | 253M | blue += ((pixel_value >> blueShift) & blueMax); |
223 | | |
224 | 253M | } |
225 | 14.6M | } |
226 | | /* We now have a total for all of the colors, find the average! */ |
227 | 1.92M | red /= area2; |
228 | 1.92M | green /= area2; |
229 | 1.92M | blue /= area2; |
230 | | /* Stuff the new value back into memory */ |
231 | 1.92M | pixel_value = ((red & redMax) << redShift) | ((green & greenMax) << greenShift) | ((blue & blueMax) << blueShift); |
232 | | |
233 | 1.92M | switch (bytesPerPixel) { |
234 | 1.92M | case 4: *((unsigned int *)dstptr) = (unsigned int) pixel_value; break; |
235 | 0 | case 2: *((unsigned short *)dstptr) = (unsigned short) pixel_value; break; |
236 | 0 | case 1: *((unsigned char *)dstptr) = (unsigned char) pixel_value; break; |
237 | 0 | default: |
238 | | /* fixme: endianness problem? */ |
239 | 0 | for (z = 0; z < bytesPerPixel; z++) |
240 | 0 | dstptr[z]=(pixel_value >> (8 * z)) & 0xff; |
241 | 0 | break; |
242 | 1.92M | } |
243 | 1.92M | dstptr += bytesPerPixel; |
244 | 1.92M | } |
245 | 36.8k | srcptr += (screen->paddedWidthInBytes * areaY); |
246 | 36.8k | dstptr += (ptr->paddedWidthInBytes - bytesPerLine); |
247 | 36.8k | } |
248 | 2.14k | } else |
249 | 0 | { /* Not truecolour, so we can't blend. Just use the top-left pixel instead */ |
250 | 0 | for (y = y1; y < (y1+h1); y++) { |
251 | 0 | for (x = x1; x < (x1+w1); x++) |
252 | 0 | memcpy (&ptr->frameBuffer[(y *ptr->paddedWidthInBytes) + (x * bytesPerPixel)], |
253 | 0 | &screen->frameBuffer[(y * areaY * screen->paddedWidthInBytes) + (x *areaX * bytesPerPixel)], bytesPerPixel); |
254 | 0 | } |
255 | 0 | } |
256 | 2.14k | } |
257 | | |
258 | | void rfbScaledScreenUpdate(rfbScreenInfoPtr screen, int x1, int y1, int x2, int y2) |
259 | 0 | { |
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 | 0 | rfbScreenInfoPtr ptr; |
264 | 0 | int count=0; |
265 | | |
266 | | /* We don't point to cl->screen as it is the original */ |
267 | 0 | for (ptr=screen->scaledScreenNext;ptr!=NULL;ptr=ptr->scaledScreenNext) |
268 | 0 | { |
269 | | /* Only update if it has active clients... */ |
270 | 0 | if (ptr->scaledScreenRefCount>0) |
271 | 0 | { |
272 | 0 | rfbScaledScreenUpdateRect(screen, ptr, x1, y1, x2-x1, y2-y1); |
273 | 0 | count++; |
274 | 0 | } |
275 | 0 | } |
276 | 0 | } |
277 | | |
278 | | /* Create a new scaled version of the framebuffer */ |
279 | | rfbScreenInfoPtr rfbScaledScreenAllocate(rfbClientPtr cl, int width, int height) |
280 | 44 | { |
281 | 44 | rfbScreenInfoPtr ptr; |
282 | 44 | ptr = malloc(sizeof(rfbScreenInfo)); |
283 | 44 | if (ptr!=NULL) |
284 | 44 | { |
285 | 44 | int allocSize; |
286 | | |
287 | | /* copy *everything* (we don't use most of it, but just in case) */ |
288 | 44 | 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 | 44 | allocSize = pad4(width * (ptr->bitsPerPixel/8)); /* per protocol, width<2**16 and bpp<256 */ |
295 | 44 | if (height == 0 || allocSize >= SIZE_MAX / height) |
296 | 0 | { |
297 | 0 | free(ptr); |
298 | 0 | return NULL; /* malloc() will allocate an incorrect buffer size - early abort */ |
299 | 0 | } |
300 | | |
301 | | /* Resume copy everything */ |
302 | 44 | ptr->width = width; |
303 | 44 | ptr->height = height; |
304 | 44 | ptr->paddedWidthInBytes = (ptr->bitsPerPixel/8)*ptr->width; |
305 | | |
306 | | /* Need to by multiples of 4 for Sparc systems */ |
307 | 44 | ptr->paddedWidthInBytes = pad4(ptr->paddedWidthInBytes); |
308 | | |
309 | | /* Reset the reference count to 0! */ |
310 | 44 | ptr->scaledScreenRefCount = 0; |
311 | | |
312 | 44 | ptr->sizeInBytes = ptr->paddedWidthInBytes * ptr->height; |
313 | 44 | ptr->serverFormat = cl->screen->serverFormat; |
314 | | |
315 | 44 | ptr->frameBuffer = malloc(ptr->sizeInBytes); |
316 | 44 | if (ptr->frameBuffer!=NULL) |
317 | 44 | { |
318 | | /* Reset to a known condition: scale the entire framebuffer */ |
319 | 44 | rfbScaledScreenUpdateRect(cl->screen, ptr, 0, 0, cl->screen->width, cl->screen->height); |
320 | | /* Now, insert into the chain */ |
321 | 44 | LOCK(cl->updateMutex); |
322 | 44 | ptr->scaledScreenNext = cl->screen->scaledScreenNext; |
323 | 44 | cl->screen->scaledScreenNext = ptr; |
324 | 44 | UNLOCK(cl->updateMutex); |
325 | 44 | } |
326 | 0 | else |
327 | 0 | { |
328 | | /* Failed to malloc the new frameBuffer, cleanup */ |
329 | 0 | free(ptr); |
330 | 0 | ptr=NULL; |
331 | 0 | } |
332 | 44 | } |
333 | 44 | return ptr; |
334 | 44 | } |
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 | 3.61k | { |
342 | 3.61k | rfbScreenInfoPtr ptr; |
343 | | /* include the original in the search (ie: fine 1:1 scaled version of the frameBuffer) */ |
344 | 89.4k | for (ptr=cl->screen; ptr!=NULL; ptr=ptr->scaledScreenNext) |
345 | 89.4k | { |
346 | 89.4k | if ((ptr->width==width) && (ptr->height==height)) |
347 | 3.56k | return ptr; |
348 | 89.4k | } |
349 | 44 | return NULL; |
350 | 3.61k | } |
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 | 3.61k | { |
355 | 3.61k | rfbScreenInfoPtr ptr; |
356 | | |
357 | 3.61k | ptr = rfbScalingFind(cl,width,height); |
358 | 3.61k | if (ptr==NULL) |
359 | 44 | ptr = rfbScaledScreenAllocate(cl,width,height); |
360 | | /* Now, there is a new screen available (if ptr is not NULL) */ |
361 | 3.61k | if (ptr!=NULL) |
362 | 3.61k | { |
363 | | /* Update it! */ |
364 | 3.61k | if (ptr->scaledScreenRefCount<1) |
365 | 2.40k | 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 | 3.61k | LOCK(cl->updateMutex); |
374 | 3.61k | cl->scaledScreen->scaledScreenRefCount--; |
375 | 3.61k | ptr->scaledScreenRefCount++; |
376 | 3.61k | cl->scaledScreen=ptr; |
377 | 3.61k | cl->newFBSizePending = TRUE; |
378 | 3.61k | UNLOCK(cl->updateMutex); |
379 | | |
380 | 3.61k | rfbLog("Scaling to %dx%d (refcount=%d)\n",width,height,ptr->scaledScreenRefCount); |
381 | 3.61k | } |
382 | 0 | else |
383 | 0 | rfbLog("Scaling to %dx%d failed, leaving things alone\n",width,height); |
384 | 3.61k | } |
385 | | |
386 | | int rfbSendNewScaleSize(rfbClientPtr cl) |
387 | 3.61k | { |
388 | | /* if the client supports newFBsize Encoding, use it */ |
389 | 3.61k | if (cl->useNewFBSize && cl->newFBSizePending) |
390 | 220 | return FALSE; |
391 | | |
392 | 3.39k | LOCK(cl->updateMutex); |
393 | 3.39k | cl->newFBSizePending = FALSE; |
394 | 3.39k | UNLOCK(cl->updateMutex); |
395 | | |
396 | 3.39k | if (cl->PalmVNC==TRUE) |
397 | 1.74k | { |
398 | 1.74k | rfbPalmVNCReSizeFrameBufferMsg pmsg; |
399 | 1.74k | pmsg.type = rfbPalmVNCReSizeFrameBuffer; |
400 | 1.74k | pmsg.pad1 = 0; |
401 | 1.74k | pmsg.desktop_w = Swap16IfLE(cl->screen->width); |
402 | 1.74k | pmsg.desktop_h = Swap16IfLE(cl->screen->height); |
403 | 1.74k | pmsg.buffer_w = Swap16IfLE(cl->scaledScreen->width); |
404 | 1.74k | pmsg.buffer_h = Swap16IfLE(cl->scaledScreen->height); |
405 | 1.74k | pmsg.pad2 = 0; |
406 | | |
407 | 1.74k | rfbLog("Sending a response to a PalmVNC style frameuffer resize event (%dx%d)\n", cl->scaledScreen->width, cl->scaledScreen->height); |
408 | 1.74k | LOCK(cl->sendMutex); |
409 | 1.74k | if (rfbWriteExact(cl, (char *)&pmsg, sz_rfbPalmVNCReSizeFrameBufferMsg) < 0) { |
410 | 0 | rfbLogPerror("rfbNewClient: write"); |
411 | 0 | rfbCloseClient(cl); |
412 | 0 | UNLOCK(cl->sendMutex); |
413 | 0 | return FALSE; |
414 | 0 | } |
415 | 1.74k | UNLOCK(cl->sendMutex); |
416 | 1.74k | } |
417 | 1.64k | else |
418 | 1.64k | { |
419 | 1.64k | rfbResizeFrameBufferMsg rmsg; |
420 | 1.64k | rmsg.type = rfbResizeFrameBuffer; |
421 | 1.64k | rmsg.pad1=0; |
422 | 1.64k | rmsg.framebufferWidth = Swap16IfLE(cl->scaledScreen->width); |
423 | 1.64k | rmsg.framebufferHeigth = Swap16IfLE(cl->scaledScreen->height); |
424 | 1.64k | rfbLog("Sending a response to a UltraVNC style frameuffer resize event (%dx%d)\n", cl->scaledScreen->width, cl->scaledScreen->height); |
425 | 1.64k | LOCK(cl->sendMutex); |
426 | 1.64k | if (rfbWriteExact(cl, (char *)&rmsg, sz_rfbResizeFrameBufferMsg) < 0) { |
427 | 0 | rfbLogPerror("rfbNewClient: write"); |
428 | 0 | rfbCloseClient(cl); |
429 | 0 | UNLOCK(cl->sendMutex); |
430 | 0 | return FALSE; |
431 | 0 | } |
432 | 1.64k | UNLOCK(cl->sendMutex); |
433 | 1.64k | } |
434 | 3.39k | return TRUE; |
435 | 3.39k | } |
436 | | /****************************/ |