diff --git a/pngfb.c b/pngfb.c new file mode 100644 index 0000000..30ed359 --- /dev/null +++ b/pngfb.c @@ -0,0 +1,84 @@ +#include "util.h" + +#include +#include +#include +#include +#include + +int read_png_file(struct vnc_framebuffer* fb, const char *filename) { + int width, height; + png_byte color_type; + png_byte bit_depth; + png_bytep *row_pointers = NULL; + + FILE *fp = fopen(filename, "rb"); + + png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if(!png) abort(); + + png_infop info = png_create_info_struct(png); + if(!info) abort(); + + if(setjmp(png_jmpbuf(png))) abort(); + + png_init_io(png, fp); + + png_read_info(png, info); + + width = png_get_image_width(png, info); + height = png_get_image_height(png, info); + color_type = png_get_color_type(png, info); + bit_depth = png_get_bit_depth(png, info); + + // Read any color_type into 8bit depth, RGBA format. + // See http://www.libpng.org/pub/png/libpng-manual.txt + + if(bit_depth == 16) + png_set_strip_16(png); + + if(color_type == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb(png); + + // PNG_COLOR_TYPE_GRAY_ALPHA is always 8 or 16bit depth. + if(color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) + png_set_expand_gray_1_2_4_to_8(png); + + if(png_get_valid(png, info, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha(png); + + // These color_type don't have an alpha channel then fill it with 0xff. + if(color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_PALETTE) + png_set_filler(png, 0xFF, PNG_FILLER_AFTER); + + if(color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png); + + png_read_update_info(png, info); + + size_t row_bytes = png_get_rowbytes(png, info); + uint8_t *addr = malloc(row_bytes * height); + assert(addr); + + row_pointers = malloc(sizeof(png_bytep) * height); + assert(row_pointers); + + for (int y = 0; y < height; ++y) + row_pointers[y] = addr + y * row_bytes; + + png_read_image(png, row_pointers); + + free(row_pointers); + fclose(fp); + + png_destroy_read_struct(&png, &info, NULL); + + fb->addr = addr; + fb->width = width; + fb->height = height; + + return 0; +} diff --git a/server.c b/server.c index 28ce5c1..b260f6b 100644 --- a/server.c +++ b/server.c @@ -1,5 +1,6 @@ #include "rfb-proto.h" #include "util.h" +#include "zrle.h" #include #include @@ -7,6 +8,7 @@ #include #include #include +#include #define READ_BUFFER_SIZE 4096 #define MSG_BUFFER_SIZE 4096 @@ -36,7 +38,8 @@ struct vnc_client { uv_tcp_t stream_handle; struct vnc_server *server; enum vnc_client_state state; - uint32_t pixfmt; + uint32_t fourcc; + struct rfb_pixel_format pixfmt; enum vnc_encodings encodings; LIST_ENTRY(vnc_client) link; size_t buffer_index; @@ -57,6 +60,7 @@ struct vnc_server { uv_tcp_t tcp_handle; struct vnc_client_list clients; struct vnc_display display; + struct vnc_framebuffer* fb; }; static const char* fourcc_to_string(uint32_t fourcc) @@ -217,9 +221,9 @@ int rfb_pixfmt_from_fourcc(struct rfb_pixel_format *dst, uint32_t src) { bpp_32: dst->bits_per_pixel = 32; dst->depth = 24; - dst->red_max = htons(0xff); - dst->green_max = htons(0xff); - dst->blue_max = htons(0xff); + dst->red_max = 0xff; + dst->green_max = 0xff; + dst->blue_max = 0xff; break; case DRM_FORMAT_RGBA4444: case DRM_FORMAT_RGBX4444: @@ -247,9 +251,9 @@ bpp_32: bpp_16: dst->bits_per_pixel = 16; dst->depth = 12; - dst->red_max = htons(0x7f); - dst->green_max = htons(0x7f); - dst->blue_max = htons(0x7f); + dst->red_max = 0x7f; + dst->green_max = 0x7f; + dst->blue_max = 0x7f; break; default: return -1; @@ -320,18 +324,14 @@ int get_fourcc_depth(uint32_t fourcc) } } -/* Note: Pixel format is in network order */ uint32_t rfb_pixfmt_to_fourcc(const struct rfb_pixel_format *fmt) { if (!fmt->true_colour_flag) return DRM_FORMAT_INVALID; - uint16_t red_max = ntohs(fmt->red_max); - uint16_t green_max = ntohs(fmt->green_max); - uint16_t blue_max = ntohs(fmt->blue_max); - /* Note: The depth value given by the client is ignored */ - int depth = max_values_to_depth(red_max, green_max, blue_max); + int depth = max_values_to_depth(fmt->red_max, fmt->green_max, + fmt->blue_max); if (depth < 0) return DRM_FORMAT_INVALID; @@ -368,7 +368,11 @@ static void send_server_init_message(struct vnc_client *client) msg->height = htons(display->height), msg->name_length = htonl(name_len), memcpy(msg->name_string, display->name, name_len); + rfb_pixfmt_from_fourcc(&msg->pixel_format, display->pixfmt); + msg->pixel_format.red_max = htons(msg->pixel_format.red_max); + msg->pixel_format.green_max = htons(msg->pixel_format.green_max); + msg->pixel_format.blue_max = htons(msg->pixel_format.blue_max); vnc__write((uv_stream_t*)&client->stream_handle, msg, size, NULL); @@ -406,9 +410,15 @@ static int on_client_set_pixel_format(struct vnc_client *client) return 0; } - client->pixfmt = rfb_pixfmt_to_fourcc(fmt); + fmt->red_max = ntohs(fmt->red_max); + fmt->green_max = ntohs(fmt->green_max); + fmt->blue_max = ntohs(fmt->blue_max); - printf("SetPixelFormat: %s\n", fourcc_to_string(client->pixfmt)); + memcpy(&client->pixfmt, fmt, sizeof(client->pixfmt)); + + client->fourcc = rfb_pixfmt_to_fourcc(fmt); + + printf("SetPixelFormat: %s\n", fourcc_to_string(client->fourcc)); return 4 + sizeof(struct rfb_pixel_format); } @@ -453,7 +463,25 @@ static int on_client_fb_update_request(struct vnc_client *client) int height = ntohs(msg->height); printf("framebuffer update: %d, %d. %d %d\n", x, y, width, height); - // TODO + + struct vnc_server *server = client->server; + struct vnc_display *display = &server->display; + struct vnc_framebuffer *fb = server->fb; + + struct rfb_pixel_format server_fmt; + rfb_pixfmt_from_fourcc(&server_fmt, server->display.pixfmt); + + struct pixman_region16 region; + pixman_region_init(®ion); + + pixman_region_union_rect(®ion, ®ion, x, y, width, height); + pixman_region_intersect_rect(®ion, ®ion, 0, 0, display->width, + display->height); + + zrle_encode_frame((uv_stream_t*)&client->stream_handle, &client->pixfmt, + server->fb->addr, &server_fmt, fb->width, fb->height, ®ion); + + pixman_region_fini(®ion); return sizeof(*msg); } @@ -643,13 +671,27 @@ failure: return -1; } -int main() +int read_png_file(struct vnc_framebuffer* fb, const char *filename); + +int main(int argc, char *argv[]) { + if (!argv[1]) { + printf("Missing argument\n"); + return 1; + } + + struct vnc_framebuffer fb; + if (read_png_file(&fb, argv[1]) < 0) { + printf("Failed to read png file\n"); + return 1; + } + struct vnc_server server = { 0 }; + server.fb = &fb; server.display.pixfmt = DRM_FORMAT_RGBX8888; - server.display.width = 1024; - server.display.height = 768; - server.display.name = "Silly VNC"; + server.display.width = fb.width; + server.display.height = fb.height; + server.display.name = argv[1]; vnc_server_init(&server, "127.0.0.1", 5900); diff --git a/util.h b/util.h index 84a61fb..4567c52 100644 --- a/util.h +++ b/util.h @@ -4,6 +4,12 @@ #include #include +struct vnc_framebuffer { + void *addr; + int width; + int height; +}; + struct vnc_write_request { uv_write_t request; uv_write_cb on_done; diff --git a/zrle.c b/zrle.c index 4f57f78..064f671 100644 --- a/zrle.c +++ b/zrle.c @@ -264,23 +264,23 @@ int zrle_encode_frame(uv_stream_t *stream, for (int i = 0; i < n_rects; ++i) { int x = box[i].x1; int y = box[i].y1; - int width = box[i].x2 - x; - int height = box[i].y2 - y; + int box_width = box[i].x2 - x; + int box_height = box[i].y2 - y; struct rfb_server_fb_rect rect = { .encoding = htonl(RFB_ENCODING_ZRLE), - .x = x, - .y = x, - .width = width, - .height = height, + .x = htons(x), + .y = htons(y), + .width = htons(box_width), + .height = htons(box_height), }; - rc = vnc__write(stream, &rect, sizeof(head), NULL); + rc = vnc__write(stream, &rect, sizeof(rect), NULL); if (rc < 0) return -1; rc = zrle_encode_box(stream, dst_fmt, src, src_fmt, x, y, - width, height); + box_width, box_height); if (rc < 0) return -1; } diff --git a/zrle.h b/zrle.h index 826db33..c50a196 100644 --- a/zrle.h +++ b/zrle.h @@ -5,6 +5,7 @@ #include struct rfb_pixel_format; +struct pixman_region16; void pixel32_to_cpixel(uint8_t *restrict dst, const struct rfb_pixel_format* dst_fmt, @@ -12,4 +13,11 @@ void pixel32_to_cpixel(uint8_t *restrict dst, const struct rfb_pixel_format* src_fmt, size_t bytes_per_cpixel, size_t len); +int zrle_encode_frame(uv_stream_t *stream, + const struct rfb_pixel_format *dst_fmt, + uint8_t *src, + const struct rfb_pixel_format *src_fmt, + int width, int height, + struct pixman_region16 *region); + #endif /* _ZRLE_H_ */