📄 pixbuf-renderer.c
字号:
while (work && pr->tile_cache_size + space > tile_max) { ImageTile *needle; needle = work->data; work = work->prev; if (needle != it && ((!needle->qd && !needle->qd2) || !pr_tile_is_visible(pr, needle))) pr_tile_remove(pr, needle); }}static void pr_tile_invalidate_all(PixbufRenderer *pr){ GList *work; work = pr->tiles; while (work) { ImageTile *it; it = work->data; work = work->next; it->render_done = TILE_RENDER_NONE; it->render_todo = TILE_RENDER_ALL; it->blank = FALSE; it->w = MIN(pr->tile_width, pr->width - it->x); it->h = MIN(pr->tile_height, pr->height - it->y); }}static void pr_tile_invalidate_region(PixbufRenderer *pr, gint x, gint y, gint w, gint h){ gint x1, x2; gint y1, y2; GList *work; x1 = (gint)floor(x / pr->tile_width) * pr->tile_width; x2 = (gint)ceil((x + w) / pr->tile_width) * pr->tile_width; y1 = (gint)floor(y / pr->tile_height) * pr->tile_height; y2 = (gint)ceil((y + h) / pr->tile_height) * pr->tile_height; work = pr->tiles; while (work) { ImageTile *it; it = work->data; work = work->next; if (it->x < x2 && it->x + it->w > x1 && it->y < y2 && it->y + it->h > y1) { it->render_done = TILE_RENDER_NONE; it->render_todo = TILE_RENDER_ALL; } }}static ImageTile *pr_tile_get(PixbufRenderer *pr, gint x, gint y, gint only_existing){ GList *work; work = pr->tiles; while (work) { ImageTile *it; it = work->data; if (it->x == x && it->y == y) { pr->tiles = g_list_delete_link(pr->tiles, work); pr->tiles = g_list_prepend(pr->tiles, it); return it; } work = work->next; } if (only_existing) return NULL; return pr_tile_add(pr, x, y);}static void pr_tile_prepare(PixbufRenderer *pr, ImageTile *it){ if (!it->pixmap) { GdkPixmap *pixmap; guint size; pixmap = gdk_pixmap_new(((GtkWidget *)pr)->window, pr->tile_width, pr->tile_height, -1); size = pixmap_calc_size(pixmap); pr_tile_free_space(pr, size, it); it->pixmap = pixmap; it->size += size; pr->tile_cache_size += size; } if ((pr->zoom != 1.0 || pr->source_tiles_enabled || (pr->pixbuf && gdk_pixbuf_get_has_alpha(pr->pixbuf)) ) && !it->pixbuf) { GdkPixbuf *pixbuf; guint size; if (pr->pixbuf) { pixbuf = gdk_pixbuf_new(gdk_pixbuf_get_colorspace(pr->pixbuf), gdk_pixbuf_get_has_alpha(pr->pixbuf), gdk_pixbuf_get_bits_per_sample(pr->pixbuf), pr->tile_width, pr->tile_height); } else { pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, pr->tile_width, pr->tile_height); } size = gdk_pixbuf_get_rowstride(pixbuf) * pr->tile_height; pr_tile_free_space(pr, size, it); it->pixbuf = pixbuf; it->size += size; pr->tile_cache_size += size; }}/* *------------------------------------------------------------------- * drawing *------------------------------------------------------------------- */static void pr_tile_render(PixbufRenderer *pr, ImageTile *it, gint x, gint y, gint w, gint h, gint new_data, gint fast){ GtkWidget *box; gint has_alpha; gint draw = FALSE; if (it->render_todo == TILE_RENDER_NONE && it->pixmap && !new_data) return; if (it->render_done != TILE_RENDER_ALL) { x = 0; y = 0; w = it->w; h = it->h; if (!fast) it->render_done = TILE_RENDER_ALL; } else if (it->render_todo != TILE_RENDER_AREA) { if (!fast) it->render_todo = TILE_RENDER_NONE; return; } if (!fast) it->render_todo = TILE_RENDER_NONE; if (new_data) it->blank = FALSE; pr_tile_prepare(pr, it); has_alpha = (pr->pixbuf && gdk_pixbuf_get_has_alpha(pr->pixbuf)); box = GTK_WIDGET(pr); /* FIXME checker colors for alpha should be configurable, * also should be drawn for blank = TRUE */ if (it->blank) { /* no data, do fast rect fill */ gdk_draw_rectangle(it->pixmap, box->style->black_gc, TRUE, 0, 0, it->w, it->h); } else if (pr->source_tiles_enabled) { draw = pr_source_tile_render(pr, it, x, y, w, h, new_data, fast); } else if (pr->zoom == 1.0 || pr->scale == 1.0) { if (has_alpha) { gdk_pixbuf_composite_color(pr->pixbuf, it->pixbuf, x, y, w, h, (double) 0.0 - it->x, (double) 0.0 - it->y, 1.0, 1.0, GDK_INTERP_NEAREST, 255, it->x + x, it->y + y, PR_ALPHA_CHECK_SIZE, PR_ALPHA_CHECK1, PR_ALPHA_CHECK2); draw = TRUE; } else { /* faster, simple */ gdk_draw_pixbuf(it->pixmap, box->style->fg_gc[GTK_WIDGET_STATE(box)], pr->pixbuf, it->x + x, it->y + y, x, y, w, h, pr->dither_quality, it->x + x, it->y + y); } } else { double scale_x, scale_y; if (pr->image_width == 0 || pr->image_height == 0) return; scale_x = (double)pr->width / pr->image_width; scale_y = (double)pr->height / pr->image_height; /* HACK: The pixbuf scalers get kinda buggy(crash) with extremely * small sizes for anything but GDK_INTERP_NEAREST */ if (pr->width < PR_MIN_SCALE_SIZE || pr->height < PR_MIN_SCALE_SIZE) fast = TRUE; if (!has_alpha) { gdk_pixbuf_scale(pr->pixbuf, it->pixbuf, x, y, w, h, (double) 0.0 - it->x, (double) 0.0 - it->y, scale_x, scale_y, (fast) ? GDK_INTERP_NEAREST : pr->zoom_quality); } else { gdk_pixbuf_composite_color(pr->pixbuf, it->pixbuf, x, y, w, h, (double) 0.0 - it->x, (double) 0.0 - it->y, scale_x, scale_y, (fast) ? GDK_INTERP_NEAREST : pr->zoom_quality, 255, it->x + x, it->y + y, PR_ALPHA_CHECK_SIZE, PR_ALPHA_CHECK1, PR_ALPHA_CHECK2); } draw = TRUE; } if (draw && it->pixbuf && !it->blank) { gdk_draw_pixbuf(it->pixmap, box->style->fg_gc[GTK_WIDGET_STATE(box)], it->pixbuf, x, y, x, y, w, h, pr->dither_quality, it->x + x, it->y + y); }}static void pr_tile_expose(PixbufRenderer *pr, ImageTile *it, gint x, gint y, gint w, gint h, gint new_data, gint fast){ GtkWidget *box; pr_tile_render(pr, it, x, y, w, h, new_data, fast); box = GTK_WIDGET(pr); gdk_draw_drawable(box->window, box->style->fg_gc[GTK_WIDGET_STATE(box)], it->pixmap, x, y, pr->x_offset + (it->x - pr->x_scroll) + x, pr->y_offset + (it->y - pr->y_scroll) + y, w, h); if (pr->overlay_list) { pr_overlay_draw(pr, pr->x_offset + (it->x - pr->x_scroll) + x, pr->y_offset + (it->y - pr->y_scroll) + y, w, h); }}static gint pr_tile_is_visible(PixbufRenderer *pr, ImageTile *it){ return (it->x + it->w >= pr->x_scroll && it->x < pr->x_scroll + pr->vis_width && it->y + it->h >= pr->y_scroll && it->y < pr->y_scroll + pr->vis_height);}/* *------------------------------------------------------------------- * draw queue *------------------------------------------------------------------- */static gint pr_queue_draw_idle_cb(gpointer data){ PixbufRenderer *pr = data; QueueData *qd; gint fast; if ((!pr->pixbuf && !pr->source_tiles_enabled) || (!pr->draw_queue && !pr->draw_queue_2pass) || pr->draw_idle_id == -1) { pr_render_complete_signal(pr); pr->draw_idle_id = -1; return FALSE; } if (pr->draw_queue) { qd = pr->draw_queue->data; fast = (pr->zoom_2pass && pr->zoom_quality != GDK_INTERP_NEAREST && pr->scale != 1.0); } else { if (pr->loading) { /* still loading, wait till done (also drops the higher priority) */ pr->draw_idle_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, pr_queue_draw_idle_cb, pr, NULL); pr->draw_idle_high = FALSE; return FALSE; } qd = pr->draw_queue_2pass->data; fast = FALSE; } if (GTK_WIDGET_REALIZED(pr)) { if (pr_tile_is_visible(pr, qd->it)) { pr_tile_expose(pr, qd->it, qd->x, qd->y, qd->w, qd->h, qd->new_data, fast); } else if (qd->new_data) { /* if new pixel data, and we already have a pixmap, update the tile */ qd->it->blank = FALSE; if (qd->it->pixmap && qd->it->render_done == TILE_RENDER_ALL) { pr_tile_render(pr, qd->it, qd->x, qd->y, qd->w, qd->h, qd->new_data, fast); } } } if (pr->draw_queue) { qd->it->qd = NULL; pr->draw_queue = g_list_remove(pr->draw_queue, qd); if (fast) { if (qd->it->qd2) { pr_queue_merge(qd->it->qd2, qd); g_free(qd); } else { qd->it->qd2 = qd; pr->draw_queue_2pass = g_list_append(pr->draw_queue_2pass, qd); } } else { g_free(qd); } } else { qd->it->qd2 = NULL; pr->draw_queue_2pass = g_list_remove(pr->draw_queue_2pass, qd); g_free(qd); } if (!pr->draw_queue && !pr->draw_queue_2pass) { pr_render_complete_signal(pr); pr->draw_idle_id = -1; return FALSE; } return TRUE;}static void pr_queue_list_free(GList *list){ GList *work; work = list; while (work) { QueueData *qd; qd = work->data; work = work->next; qd->it->qd = NULL; qd->it->qd2 = NULL; g_free(qd); } g_list_free(list);}static void pr_queue_clear(PixbufRenderer *pr){ pr_queue_list_free(pr->draw_queue); pr->draw_queue = NULL; pr_queue_list_free(pr->draw_queue_2pass); pr->draw_queue_2pass = NULL; if (pr->draw_idle_id != -1) g_source_remove(pr->draw_idle_id); pr->draw_idle_id = -1;}static void pr_queue_merge(QueueData *parent, QueueData *qd){ if (parent->x + parent->w < qd->x + qd->w) { parent->w += (qd->x + qd->w) - (parent->x + parent->w); } if (parent->x > qd->x) { parent->w += parent->x - qd->x; parent->x = qd->x; } if (parent->y + parent->h < qd->y + qd->h) { parent->h += (qd->y + qd->h) - (parent->y + parent->h); } if (parent->y > qd->y) { parent->h += parent->y - qd->y; parent->y = qd->y; } parent->new_data |= qd->new_data;}static gint pr_clamp_to_visible(PixbufRenderer *pr, gint *x, gint *y, gint *w, gint *h){ gint nx, ny; gint nw, nh; gint vx, vy; gint vw, vh; vw = pr->vis_width; vh = pr->vis_height; vx = pr->x_scroll; vy = pr->y_scroll; if (*x + *w < vx || *x > vx + vw || *y + *h < vy || *y > vy + vh) return FALSE; /* now clamp it */ nx = CLAMP(*x, vx, vx + vw); nw = CLAMP(*w - (nx - *x), 1, vw); ny = CLAMP(*y, vy, vy + vh); nh = CLAMP(*h - (ny - *y), 1, vh); *x = nx; *y = ny; *w = nw; *h = nh; return TRUE;}static gint pr_queue_to_tiles(PixbufRenderer *pr, gint x, gint y, gint w, gint h, gint clamp, ImageTileRenderType render, gint new_data, gint only_existing){ gint i, j; gint x1, x2; gint y1, y2; if (clamp && !pr_clamp_to_visible(pr, &x, &y, &w, &h)) return FALSE; x1 = (gint)floor(x / pr->tile_width) * pr->tile_width; x2 = (gint)ceil((x + w) / pr->tile_width) * pr->tile_width; y1 = (gint)floor(y / pr->tile_height) * pr->tile_height; y2 = (gint)ceil((y + h) / pr->tile_height) * pr->tile_height; for (j = y1; j <= y2; j += pr->tile_height) { for (i = x1; i <= x2; i += pr->tile_width) { ImageTile *it; it = pr_tile_get(pr, i, j, (only_existing && (i + pr->tile_width < pr->x_scroll || i > pr->x_scroll + pr->vis_width || j + pr->tile_height < pr->y_scroll || j > pr->y_scroll + pr->vis_height))); if (it) { QueueData *qd; if ((render == TILE_RENDER_ALL && it->render_done != TILE_RENDER_ALL) || (render == TILE_RENDER_AREA && it->render_todo != TILE_RENDER_ALL)) { it->render_todo = render; } qd = g_new(QueueData, 1); qd->it = it; qd->new_data = new_data; if (i < x) { qd->x = x - i; } else { qd->x = 0; } qd->w = x + w - i - qd->x; if (qd->x + qd->w > pr->tile_width) qd->w = pr->tile_width - qd->x; if (j < y) { qd->y = y - j; } else { qd->y = 0; } qd->h = y + h - j - qd->y; if (qd->y + qd->h > pr->tile_height) qd->h = pr->tile_height - qd->y; if (qd->w < 1 || qd->h < 1) { g_free(qd); } else if (it->qd) { pr_queue_merge(it->qd, qd); g_free(qd); } else { it->qd = qd; pr->draw_queue = g_list_append(pr->draw_queue, qd); } } } } return TRUE;}static void pr_queue(PixbufRenderer *pr, gint x, gint y, gint w, gint h, gint clamp, ImageTileRenderType render, gint new_data, gint only_existing){ gint nx, ny; nx = CLAMP(x, 0, pr->width - 1); ny = CLAMP(y, 0, pr->height - 1); w -= (nx - x); h -= (ny - y); w = CLAMP(w, 0, pr->width - nx); h = CLAMP(h, 0, pr->height - ny); if (w < 1 || h < 1) return; if (pr_queue_to_tiles(pr, nx, ny, w, h, clamp, render, new_data, only_existing) && ((!pr->draw_queue && !pr->draw_queue_2pass) || pr->draw_idle_id == -1 || !pr->draw_idle_high)) { if (pr->draw_idle_id != -1) g_source_remove(pr->draw_idle_id); pr->draw_idle_id = g_idle_add_full(GDK_PRIORITY_REDRAW, pr_queue_draw_idle_cb, pr, NULL); pr->draw_idle_high = TRUE; }}static void pr_redraw(PixbufRenderer *pr, gint new_data){ pr_queue_clear(pr);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -