Commit 3512779a88128808eee4c5ecbeb3ad7679f97a50
1 parent
5c3ff3a7
support for all VNC pixel formats
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1923 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
2 changed files
with
195 additions
and
51 deletions
vnc.c
| ... | ... | @@ -42,6 +42,14 @@ typedef struct VncState VncState; |
| 42 | 42 | |
| 43 | 43 | typedef int VncReadEvent(VncState *vs, char *data, size_t len); |
| 44 | 44 | |
| 45 | +typedef void VncWritePixels(VncState *vs, void *data, int size); | |
| 46 | + | |
| 47 | +typedef void VncSendHextileTile(VncState *vs, | |
| 48 | + int x, int y, int w, int h, | |
| 49 | + uint32_t *last_bg, | |
| 50 | + uint32_t *last_fg, | |
| 51 | + int *has_bg, int *has_fg); | |
| 52 | + | |
| 45 | 53 | struct VncState |
| 46 | 54 | { |
| 47 | 55 | QEMUTimer *timer; |
| ... | ... | @@ -53,12 +61,19 @@ struct VncState |
| 53 | 61 | int height; |
| 54 | 62 | uint64_t dirty_row[768]; |
| 55 | 63 | char *old_data; |
| 56 | - int depth; | |
| 64 | + int depth; /* internal VNC frame buffer byte per pixel */ | |
| 57 | 65 | int has_resize; |
| 58 | 66 | int has_hextile; |
| 59 | 67 | Buffer output; |
| 60 | 68 | Buffer input; |
| 61 | 69 | kbd_layout_t *kbd_layout; |
| 70 | + /* current output mode information */ | |
| 71 | + VncWritePixels *write_pixels; | |
| 72 | + VncSendHextileTile *send_hextile_tile; | |
| 73 | + int pix_bpp, pix_big_endian; | |
| 74 | + int red_shift, red_max, red_shift1; | |
| 75 | + int green_shift, green_max, green_shift1; | |
| 76 | + int blue_shift, blue_max, blue_shift1; | |
| 62 | 77 | |
| 63 | 78 | VncReadEvent *read_handler; |
| 64 | 79 | size_t read_handler_expect; |
| ... | ... | @@ -130,6 +145,66 @@ static void vnc_dpy_resize(DisplayState *ds, int w, int h) |
| 130 | 145 | } |
| 131 | 146 | } |
| 132 | 147 | |
| 148 | +/* fastest code */ | |
| 149 | +static void vnc_write_pixels_copy(VncState *vs, void *pixels, int size) | |
| 150 | +{ | |
| 151 | + vnc_write(vs, pixels, size); | |
| 152 | +} | |
| 153 | + | |
| 154 | +/* slowest but generic code. */ | |
| 155 | +static void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v) | |
| 156 | +{ | |
| 157 | + unsigned int r, g, b; | |
| 158 | + | |
| 159 | + r = (v >> vs->red_shift1) & vs->red_max; | |
| 160 | + g = (v >> vs->green_shift1) & vs->green_max; | |
| 161 | + b = (v >> vs->blue_shift1) & vs->blue_max; | |
| 162 | + v = (r << vs->red_shift) | | |
| 163 | + (g << vs->green_shift) | | |
| 164 | + (b << vs->blue_shift); | |
| 165 | + switch(vs->pix_bpp) { | |
| 166 | + case 1: | |
| 167 | + buf[0] = v; | |
| 168 | + break; | |
| 169 | + case 2: | |
| 170 | + if (vs->pix_big_endian) { | |
| 171 | + buf[0] = v >> 8; | |
| 172 | + buf[1] = v; | |
| 173 | + } else { | |
| 174 | + buf[1] = v >> 8; | |
| 175 | + buf[0] = v; | |
| 176 | + } | |
| 177 | + break; | |
| 178 | + default: | |
| 179 | + case 4: | |
| 180 | + if (vs->pix_big_endian) { | |
| 181 | + buf[0] = v >> 24; | |
| 182 | + buf[1] = v >> 16; | |
| 183 | + buf[2] = v >> 8; | |
| 184 | + buf[3] = v; | |
| 185 | + } else { | |
| 186 | + buf[3] = v >> 24; | |
| 187 | + buf[2] = v >> 16; | |
| 188 | + buf[1] = v >> 8; | |
| 189 | + buf[0] = v; | |
| 190 | + } | |
| 191 | + break; | |
| 192 | + } | |
| 193 | +} | |
| 194 | + | |
| 195 | +static void vnc_write_pixels_generic(VncState *vs, void *pixels1, int size) | |
| 196 | +{ | |
| 197 | + uint32_t *pixels = pixels1; | |
| 198 | + uint8_t buf[4]; | |
| 199 | + int n, i; | |
| 200 | + | |
| 201 | + n = size >> 2; | |
| 202 | + for(i = 0; i < n; i++) { | |
| 203 | + vnc_convert_pixel(vs, buf, pixels[i]); | |
| 204 | + vnc_write(vs, buf, vs->pix_bpp); | |
| 205 | + } | |
| 206 | +} | |
| 207 | + | |
| 133 | 208 | static void send_framebuffer_update_raw(VncState *vs, int x, int y, int w, int h) |
| 134 | 209 | { |
| 135 | 210 | int i; |
| ... | ... | @@ -139,7 +214,7 @@ static void send_framebuffer_update_raw(VncState *vs, int x, int y, int w, int h |
| 139 | 214 | |
| 140 | 215 | row = vs->ds->data + y * vs->ds->linesize + x * vs->depth; |
| 141 | 216 | for (i = 0; i < h; i++) { |
| 142 | - vnc_write(vs, row, w * vs->depth); | |
| 217 | + vs->write_pixels(vs, row, w * vs->depth); | |
| 143 | 218 | row += vs->ds->linesize; |
| 144 | 219 | } |
| 145 | 220 | } |
| ... | ... | @@ -162,35 +237,26 @@ static void hextile_enc_cord(uint8_t *ptr, int x, int y, int w, int h) |
| 162 | 237 | #include "vnchextile.h" |
| 163 | 238 | #undef BPP |
| 164 | 239 | |
| 240 | +#define GENERIC | |
| 241 | +#define BPP 32 | |
| 242 | +#include "vnchextile.h" | |
| 243 | +#undef BPP | |
| 244 | +#undef GENERIC | |
| 245 | + | |
| 165 | 246 | static void send_framebuffer_update_hextile(VncState *vs, int x, int y, int w, int h) |
| 166 | 247 | { |
| 167 | 248 | int i, j; |
| 168 | 249 | int has_fg, has_bg; |
| 169 | 250 | uint32_t last_fg32, last_bg32; |
| 170 | - uint16_t last_fg16, last_bg16; | |
| 171 | - uint8_t last_fg8, last_bg8; | |
| 172 | 251 | |
| 173 | 252 | vnc_framebuffer_update(vs, x, y, w, h, 5); |
| 174 | 253 | |
| 175 | 254 | has_fg = has_bg = 0; |
| 176 | 255 | for (j = y; j < (y + h); j += 16) { |
| 177 | 256 | for (i = x; i < (x + w); i += 16) { |
| 178 | - switch (vs->depth) { | |
| 179 | - case 1: | |
| 180 | - send_hextile_tile_8(vs, i, j, MIN(16, x + w - i), MIN(16, y + h - j), | |
| 181 | - &last_bg8, &last_fg8, &has_bg, &has_fg); | |
| 182 | - break; | |
| 183 | - case 2: | |
| 184 | - send_hextile_tile_16(vs, i, j, MIN(16, x + w - i), MIN(16, y + h - j), | |
| 185 | - &last_bg16, &last_fg16, &has_bg, &has_fg); | |
| 186 | - break; | |
| 187 | - case 4: | |
| 188 | - send_hextile_tile_32(vs, i, j, MIN(16, x + w - i), MIN(16, y + h - j), | |
| 189 | - &last_bg32, &last_fg32, &has_bg, &has_fg); | |
| 190 | - break; | |
| 191 | - default: | |
| 192 | - break; | |
| 193 | - } | |
| 257 | + vs->send_hextile_tile(vs, i, j, | |
| 258 | + MIN(16, x + w - i), MIN(16, y + h - j), | |
| 259 | + &last_bg32, &last_fg32, &has_bg, &has_fg); | |
| 194 | 260 | } |
| 195 | 261 | } |
| 196 | 262 | } |
| ... | ... | @@ -660,30 +726,79 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) |
| 660 | 726 | } |
| 661 | 727 | } |
| 662 | 728 | |
| 729 | +static int compute_nbits(unsigned int val) | |
| 730 | +{ | |
| 731 | + int n; | |
| 732 | + n = 0; | |
| 733 | + while (val != 0) { | |
| 734 | + n++; | |
| 735 | + val >>= 1; | |
| 736 | + } | |
| 737 | + return n; | |
| 738 | +} | |
| 739 | + | |
| 663 | 740 | static void set_pixel_format(VncState *vs, |
| 664 | 741 | int bits_per_pixel, int depth, |
| 665 | 742 | int big_endian_flag, int true_color_flag, |
| 666 | 743 | int red_max, int green_max, int blue_max, |
| 667 | 744 | int red_shift, int green_shift, int blue_shift) |
| 668 | 745 | { |
| 669 | - switch (bits_per_pixel) { | |
| 670 | - case 32: | |
| 671 | - case 24: | |
| 672 | - vs->depth = 4; | |
| 673 | - break; | |
| 674 | - case 16: | |
| 675 | - vs->depth = 2; | |
| 676 | - break; | |
| 677 | - case 8: | |
| 678 | - vs->depth = 1; | |
| 679 | - break; | |
| 680 | - default: | |
| 746 | + int host_big_endian_flag; | |
| 747 | + | |
| 748 | +#ifdef WORDS_BIGENDIAN | |
| 749 | + host_big_endian_flag = 1; | |
| 750 | +#else | |
| 751 | + host_big_endian_flag = 0; | |
| 752 | +#endif | |
| 753 | + if (!true_color_flag) { | |
| 754 | + fail: | |
| 681 | 755 | vnc_client_error(vs); |
| 682 | - break; | |
| 756 | + return; | |
| 757 | + } | |
| 758 | + if (bits_per_pixel == 32 && | |
| 759 | + host_big_endian_flag == big_endian_flag && | |
| 760 | + red_max == 0xff && green_max == 0xff && blue_max == 0xff && | |
| 761 | + red_shift == 16 && green_shift == 8 && blue_shift == 0) { | |
| 762 | + vs->depth = 4; | |
| 763 | + vs->write_pixels = vnc_write_pixels_copy; | |
| 764 | + vs->send_hextile_tile = send_hextile_tile_32; | |
| 765 | + } else | |
| 766 | + if (bits_per_pixel == 16 && | |
| 767 | + host_big_endian_flag == big_endian_flag && | |
| 768 | + red_max == 31 && green_max == 63 && blue_max == 31 && | |
| 769 | + red_shift == 11 && green_shift == 5 && blue_shift == 0) { | |
| 770 | + vs->depth = 2; | |
| 771 | + vs->write_pixels = vnc_write_pixels_copy; | |
| 772 | + vs->send_hextile_tile = send_hextile_tile_16; | |
| 773 | + } else | |
| 774 | + if (bits_per_pixel == 8 && | |
| 775 | + red_max == 7 && green_max == 7 && blue_max == 3 && | |
| 776 | + red_shift == 5 && green_shift == 2 && blue_shift == 0) { | |
| 777 | + vs->depth = 1; | |
| 778 | + vs->write_pixels = vnc_write_pixels_copy; | |
| 779 | + vs->send_hextile_tile = send_hextile_tile_8; | |
| 780 | + } else | |
| 781 | + { | |
| 782 | + /* generic and slower case */ | |
| 783 | + if (bits_per_pixel != 8 && | |
| 784 | + bits_per_pixel != 16 && | |
| 785 | + bits_per_pixel != 32) | |
| 786 | + goto fail; | |
| 787 | + vs->depth = 4; | |
| 788 | + vs->red_shift = red_shift; | |
| 789 | + vs->red_max = red_max; | |
| 790 | + vs->red_shift1 = 24 - compute_nbits(red_max); | |
| 791 | + vs->green_shift = green_shift; | |
| 792 | + vs->green_max = green_max; | |
| 793 | + vs->green_shift1 = 16 - compute_nbits(green_max); | |
| 794 | + vs->blue_shift = blue_shift; | |
| 795 | + vs->blue_max = blue_max; | |
| 796 | + vs->blue_shift1 = 8 - compute_nbits(blue_max); | |
| 797 | + vs->pix_bpp = bits_per_pixel / 8; | |
| 798 | + vs->pix_big_endian = big_endian_flag; | |
| 799 | + vs->write_pixels = vnc_write_pixels_generic; | |
| 800 | + vs->send_hextile_tile = send_hextile_tile_generic; | |
| 683 | 801 | } |
| 684 | - | |
| 685 | - if (!true_color_flag) | |
| 686 | - vnc_client_error(vs); | |
| 687 | 802 | |
| 688 | 803 | vnc_dpy_resize(vs->ds, vs->ds->width, vs->ds->height); |
| 689 | 804 | memset(vs->dirty_row, 0xFF, sizeof(vs->dirty_row)); |
| ... | ... | @@ -774,7 +889,11 @@ static int protocol_client_init(VncState *vs, char *data, size_t len) |
| 774 | 889 | |
| 775 | 890 | vnc_write_u8(vs, vs->depth * 8); /* bits-per-pixel */ |
| 776 | 891 | vnc_write_u8(vs, vs->depth * 8); /* depth */ |
| 892 | +#ifdef WORDS_BIGENDIAN | |
| 893 | + vnc_write_u8(vs, 1); /* big-endian-flag */ | |
| 894 | +#else | |
| 777 | 895 | vnc_write_u8(vs, 0); /* big-endian-flag */ |
| 896 | +#endif | |
| 778 | 897 | vnc_write_u8(vs, 1); /* true-color-flag */ |
| 779 | 898 | if (vs->depth == 4) { |
| 780 | 899 | vnc_write_u16(vs, 0xFF); /* red-max */ |
| ... | ... | @@ -783,6 +902,7 @@ static int protocol_client_init(VncState *vs, char *data, size_t len) |
| 783 | 902 | vnc_write_u8(vs, 16); /* red-shift */ |
| 784 | 903 | vnc_write_u8(vs, 8); /* green-shift */ |
| 785 | 904 | vnc_write_u8(vs, 0); /* blue-shift */ |
| 905 | + vs->send_hextile_tile = send_hextile_tile_32; | |
| 786 | 906 | } else if (vs->depth == 2) { |
| 787 | 907 | vnc_write_u16(vs, 31); /* red-max */ |
| 788 | 908 | vnc_write_u16(vs, 63); /* green-max */ |
| ... | ... | @@ -790,14 +910,18 @@ static int protocol_client_init(VncState *vs, char *data, size_t len) |
| 790 | 910 | vnc_write_u8(vs, 11); /* red-shift */ |
| 791 | 911 | vnc_write_u8(vs, 5); /* green-shift */ |
| 792 | 912 | vnc_write_u8(vs, 0); /* blue-shift */ |
| 913 | + vs->send_hextile_tile = send_hextile_tile_16; | |
| 793 | 914 | } else if (vs->depth == 1) { |
| 794 | - vnc_write_u16(vs, 3); /* red-max */ | |
| 915 | + /* XXX: change QEMU pixel 8 bit pixel format to match the VNC one ? */ | |
| 916 | + vnc_write_u16(vs, 7); /* red-max */ | |
| 795 | 917 | vnc_write_u16(vs, 7); /* green-max */ |
| 796 | 918 | vnc_write_u16(vs, 3); /* blue-max */ |
| 797 | 919 | vnc_write_u8(vs, 5); /* red-shift */ |
| 798 | 920 | vnc_write_u8(vs, 2); /* green-shift */ |
| 799 | 921 | vnc_write_u8(vs, 0); /* blue-shift */ |
| 922 | + vs->send_hextile_tile = send_hextile_tile_8; | |
| 800 | 923 | } |
| 924 | + vs->write_pixels = vnc_write_pixels_copy; | |
| 801 | 925 | |
| 802 | 926 | vnc_write(vs, pad, 3); /* padding */ |
| 803 | 927 | ... | ... |
vnchextile.h
| 1 | 1 | #define CONCAT_I(a, b) a ## b |
| 2 | 2 | #define CONCAT(a, b) CONCAT_I(a, b) |
| 3 | 3 | #define pixel_t CONCAT(uint, CONCAT(BPP, _t)) |
| 4 | - | |
| 5 | -static void CONCAT(send_hextile_tile_, BPP)(VncState *vs, | |
| 6 | - int x, int y, int w, int h, | |
| 7 | - pixel_t *last_bg, pixel_t *last_fg, | |
| 8 | - int *has_bg, int *has_fg) | |
| 4 | +#ifdef GENERIC | |
| 5 | +#define NAME generic | |
| 6 | +#else | |
| 7 | +#define NAME BPP | |
| 8 | +#endif | |
| 9 | + | |
| 10 | +static void CONCAT(send_hextile_tile_, NAME)(VncState *vs, | |
| 11 | + int x, int y, int w, int h, | |
| 12 | + uint32_t *last_bg32, | |
| 13 | + uint32_t *last_fg32, | |
| 14 | + int *has_bg, int *has_fg) | |
| 9 | 15 | { |
| 10 | 16 | char *row = (vs->ds->data + y * vs->ds->linesize + x * vs->depth); |
| 11 | 17 | pixel_t *irow = (pixel_t *)row; |
| 12 | 18 | int j, i; |
| 19 | + pixel_t *last_bg = (pixel_t *)last_bg32; | |
| 20 | + pixel_t *last_fg = (pixel_t *)last_fg32; | |
| 13 | 21 | pixel_t bg = 0; |
| 14 | 22 | pixel_t fg = 0; |
| 15 | 23 | int n_colors = 0; |
| ... | ... | @@ -122,10 +130,15 @@ static void CONCAT(send_hextile_tile_, BPP)(VncState *vs, |
| 122 | 130 | has_color = 1; |
| 123 | 131 | } else if (irow[i] != color) { |
| 124 | 132 | has_color = 0; |
| 125 | - | |
| 133 | +#ifdef GENERIC | |
| 134 | + vnc_convert_pixel(vs, data + n_data, color); | |
| 135 | + n_data += vs->pix_bpp; | |
| 136 | +#else | |
| 126 | 137 | memcpy(data + n_data, &color, sizeof(color)); |
| 127 | - hextile_enc_cord(data + n_data + sizeof(pixel_t), min_x, j, i - min_x, 1); | |
| 128 | - n_data += 2 + sizeof(pixel_t); | |
| 138 | + n_data += sizeof(pixel_t); | |
| 139 | +#endif | |
| 140 | + hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1); | |
| 141 | + n_data += 2; | |
| 129 | 142 | n_subtiles++; |
| 130 | 143 | |
| 131 | 144 | min_x = -1; |
| ... | ... | @@ -137,9 +150,15 @@ static void CONCAT(send_hextile_tile_, BPP)(VncState *vs, |
| 137 | 150 | } |
| 138 | 151 | } |
| 139 | 152 | if (has_color) { |
| 140 | - memcpy(data + n_data, &color, sizeof(color)); | |
| 141 | - hextile_enc_cord(data + n_data + sizeof(pixel_t), min_x, j, i - min_x, 1); | |
| 142 | - n_data += 2 + sizeof(pixel_t); | |
| 153 | +#ifdef GENERIC | |
| 154 | + vnc_convert_pixel(vs, data + n_data, color); | |
| 155 | + n_data += vs->pix_bpp; | |
| 156 | +#else | |
| 157 | + memcpy(data + n_data, &color, sizeof(color)); | |
| 158 | + n_data += sizeof(pixel_t); | |
| 159 | +#endif | |
| 160 | + hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1); | |
| 161 | + n_data += 2; | |
| 143 | 162 | n_subtiles++; |
| 144 | 163 | } |
| 145 | 164 | irow += vs->ds->linesize / sizeof(pixel_t); |
| ... | ... | @@ -169,21 +188,22 @@ static void CONCAT(send_hextile_tile_, BPP)(VncState *vs, |
| 169 | 188 | vnc_write_u8(vs, flags); |
| 170 | 189 | if (n_colors < 4) { |
| 171 | 190 | if (flags & 0x02) |
| 172 | - vnc_write(vs, last_bg, sizeof(pixel_t)); | |
| 191 | + vs->write_pixels(vs, last_bg, sizeof(pixel_t)); | |
| 173 | 192 | if (flags & 0x04) |
| 174 | - vnc_write(vs, last_fg, sizeof(pixel_t)); | |
| 193 | + vs->write_pixels(vs, last_fg, sizeof(pixel_t)); | |
| 175 | 194 | if (n_subtiles) { |
| 176 | 195 | vnc_write_u8(vs, n_subtiles); |
| 177 | 196 | vnc_write(vs, data, n_data); |
| 178 | 197 | } |
| 179 | 198 | } else { |
| 180 | 199 | for (j = 0; j < h; j++) { |
| 181 | - vnc_write(vs, row, w * vs->depth); | |
| 200 | + vs->write_pixels(vs, row, w * vs->depth); | |
| 182 | 201 | row += vs->ds->linesize; |
| 183 | 202 | } |
| 184 | 203 | } |
| 185 | 204 | } |
| 186 | 205 | |
| 206 | +#undef NAME | |
| 187 | 207 | #undef pixel_t |
| 188 | 208 | #undef CONCAT_I |
| 189 | 209 | #undef CONCAT | ... | ... |