New function to do proper scrolling of the image view. (drag_to): Use

2000-04-21  Federico Mena Quintero  <federico@helixcode.com>

	* image-view.c (scroll_to): New function to do proper scrolling of
	the image view.
	(drag_to): Use scroll_to().
	(adjustment_changed_cb): Use scroll_to().

	* uta.c (uta_copy_area): New function to do the equivalent of a
	BitBlt on a microtile array.  It is not perfect; see the FIXME for
	details.
	(copy_tile): Helper function for uta_copy_area().
	(uta_ensure_size): Create a new uta with the proper size if the
	specified uta is NULL.
	(uta_add_rect): Special-case source tiles that are empty so as not
	to add a bigger region than needed.
This commit is contained in:
Federico Mena Quintero 2000-04-22 05:44:01 +00:00 committed by Federico Mena Quintero
parent 59fbfdcf67
commit 5585c9d681
7 changed files with 1144 additions and 182 deletions

View file

@ -21,6 +21,7 @@
#include <config.h>
#include <math.h>
#include <stdlib.h>
#include <gtk/gtksignal.h>
#include "cursors.h"
#include "image-view.h"
@ -57,6 +58,9 @@ typedef struct {
GtkAdjustment *hadj;
GtkAdjustment *vadj;
/* Current scrolling offsets */
int xofs, yofs;
/* Microtile array for dirty region */
ArtUta *uta;
@ -324,12 +328,12 @@ paint_rectangle (ImageView *view, ArtIRect *rect)
if (scaled_width < width)
xofs = (width - scaled_width) / 2;
else
xofs = -priv->hadj->value;
xofs = -priv->xofs;
if (scaled_height < height)
yofs = (height - scaled_height) / 2;
else
yofs = -priv->vadj->value;
yofs = -priv->yofs;
/* Draw background if necessary, in four steps */
@ -412,6 +416,17 @@ paint_rectangle (ImageView *view, ArtIRect *rect)
d.x0 - xofs, d.y0 - yofs);
gdk_pixbuf_unref (tmp);
#if 0
gdk_draw_line (GTK_WIDGET (view)->window,
GTK_WIDGET (view)->style->black_gc,
d.x0, d.y0,
d.x1 - 1, d.y1 - 1);
gdk_draw_line (GTK_WIDGET (view)->window,
GTK_WIDGET (view)->style->black_gc,
d.x1 - 1, d.y0,
d.x0, d.y1 - 1);
#endif
}
/* Idle handler for the drawing process. We pull a rectangle from the dirty
@ -480,6 +495,119 @@ request_paint_area (ImageView *view, GdkRectangle *area)
}
}
/* Scrolls the view to the specified offsets. Does not perform range checking! */
static void
scroll_to (ImageView *view, int x, int y)
{
ImageViewPrivate *priv;
int xofs, yofs;
GdkWindow *window;
GdkGC *gc;
int width, height;
int src_x, src_y;
int dest_x, dest_y;
int twidth, theight;
GdkEvent *event;
priv = view->priv;
/* Compute offsets and check bounds */
xofs = x - priv->xofs;
yofs = y - priv->yofs;
if (xofs == 0 && yofs == 0)
return;
width = GTK_WIDGET (view)->allocation.width;
height = GTK_WIDGET (view)->allocation.height;
priv->xofs = x;
priv->yofs = y;
if (abs (xofs) >= width || abs (yofs) >= height) {
GdkRectangle area;
area.x = 0;
area.y = 0;
area.width = width;
area.height = height;
request_paint_area (view, &area);
return;
}
window = GTK_WIDGET (view)->window;
/* Ensure that the uta has the full size */
twidth = (width + ART_UTILE_SIZE - 1) >> ART_UTILE_SHIFT;
theight = (height + ART_UTILE_SIZE - 1) >> ART_UTILE_SHIFT;
if (priv->uta)
g_assert (priv->idle_id != 0);
else
priv->idle_id = g_idle_add (paint_iteration_idle, view);
priv->uta = uta_ensure_size (priv->uta, 0, 0, twidth, theight);
/* Copy the uta area and add the scrolled-in region */
src_x = xofs < 0 ? 0 : xofs;
src_y = yofs < 0 ? 0 : yofs;
dest_x = xofs < 0 ? -xofs : 0;
dest_y = yofs < 0 ? -yofs : 0;
uta_copy_area (priv->uta,
src_x, src_y,
dest_x, dest_y,
width - abs (xofs), height - abs (yofs));
if (xofs) {
int xx;
xx = xofs < 0 ? 0 : width - xofs;
uta_add_rect (priv->uta,
xx, 0,
xx + abs (xofs), height);
}
if (yofs) {
int yy;
yy = yofs < 0 ? 0 : height - yofs;
uta_add_rect (priv->uta,
0, yy,
width, yy + abs (yofs));
}
/* Copy the window area */
gc = gdk_gc_new (window);
gdk_gc_set_exposures (gc, TRUE);
gdk_window_copy_area (window,
gc,
dest_x, dest_y,
window,
src_x, src_y,
width - abs (xofs),
height - abs (yofs));
gdk_gc_destroy (gc);
/* Process graphics exposures */
while ((event = gdk_event_get_graphics_expose (window)) != NULL) {
gtk_widget_event (GTK_WIDGET (view), event);
if (event->expose.count == 0) {
gdk_event_free (event);
break;
}
gdk_event_free (event);
}
}
/* Widget methods */
@ -668,8 +796,8 @@ image_view_button_press (GtkWidget *widget, GdkEventButton *event)
priv->drag_anchor_x = event->x;
priv->drag_anchor_y = event->y;
priv->drag_ofs_x = priv->hadj->value;
priv->drag_ofs_y = priv->vadj->value;
priv->drag_ofs_x = priv->xofs;
priv->drag_ofs_y = priv->yofs;
cursor = cursor_get (widget->window, CURSOR_HAND_CLOSED);
gdk_pointer_grab (widget->window,
@ -691,7 +819,6 @@ drag_to (ImageView *view, int x, int y)
{
ImageViewPrivate *priv;
int dx, dy;
GdkRectangle area;
priv = view->priv;
@ -701,6 +828,8 @@ drag_to (ImageView *view, int x, int y)
x = CLAMP (priv->drag_ofs_x + dx, 0, priv->hadj->upper - priv->hadj->page_size);
y = CLAMP (priv->drag_ofs_y + dy, 0, priv->vadj->upper - priv->vadj->page_size);
scroll_to (view, x, y);
gtk_signal_handler_block_by_data (GTK_OBJECT (priv->hadj), view);
gtk_signal_handler_block_by_data (GTK_OBJECT (priv->vadj), view);
@ -712,15 +841,6 @@ drag_to (ImageView *view, int x, int y)
gtk_signal_handler_unblock_by_data (GTK_OBJECT (priv->hadj), view);
gtk_signal_handler_unblock_by_data (GTK_OBJECT (priv->vadj), view);
/* FIXME: use copy_area() for the window and the uta */
area.x = 0;
area.y = 0;
area.width = GTK_WIDGET (view)->allocation.width;
area.height = GTK_WIDGET (view)->allocation.height;
request_paint_area (view, &area);
}
/* Button release handler for the image view */
@ -791,19 +911,11 @@ adjustment_changed_cb (GtkAdjustment *adj, gpointer data)
{
ImageView *view;
ImageViewPrivate *priv;
GdkRectangle area;
view = IMAGE_VIEW (data);
priv = view->priv;
/* FIXME: use copy_area() for the window and the uta */
area.x = 0;
area.y = 0;
area.width = GTK_WIDGET (view)->allocation.width;
area.height = GTK_WIDGET (view)->allocation.height;
request_paint_area (view, &area);
scroll_to (view, priv->hadj->value, priv->vadj->value);
}
/* Set_scroll_adjustments handler for the image view */

View file

@ -33,7 +33,7 @@
* @y1: Top microtile coordinate that must fit in new array.
* @x2: Right microtile coordinate that must fit in new array.
* @y2: Bottom microtile coordinate that must fit in new array.
*
*
* Ensures that the size of a microtile array is big enough to fit the specified
* microtile coordinates. If it is not big enough, the specified @uta will be
* freed and a new one will be returned. Otherwise, the original @uta will be
@ -42,7 +42,7 @@
*
* Note that the specified coordinates must have already been scaled down by the
* ART_UTILE_SHIFT factor.
*
*
* Return value: The same value as @uta if the original microtile array was
* big enough to fit the specified microtile coordinates, or a new array if
* it needed to be grown. In the second case, the original @uta will be
@ -56,10 +56,12 @@ uta_ensure_size (ArtUta *uta, int x1, int y1, int x2, int y2)
int new_ofs, ofs;
int x, y;
g_return_val_if_fail (uta != NULL, NULL);
g_return_val_if_fail (x1 < x2, NULL);
g_return_val_if_fail (y1 < y2, NULL);
if (!uta)
return art_uta_new (x1, y1, x2, y2);
if (x1 >= uta->x0
&& y1 >= uta->y0
&& x2 <= uta->x0 + uta->width
@ -105,10 +107,10 @@ uta_ensure_size (ArtUta *uta, int x1, int y1, int x2, int y2)
* @y1: Top coordinate of rectangle.
* @x2: Right coordinate of rectangle.
* @y2: Bottom coordinate of rectangle.
*
*
* Adds the specified rectangle to a microtile array. The array is
* grown to fit the rectangle if necessary.
*
*
* Return value: The original @uta, or a new microtile array if the original one
* needed to be grown to fit the specified rectangle. In the second case, the
* original @uta will be freed automatically.
@ -162,83 +164,133 @@ uta_add_rect (ArtUta *uta, int x1, int y1, int x2, int y2)
if (rect_y2 - rect_y1 == 1) {
if (rect_x2 - rect_x1 == 1) {
bb = utiles[ofs];
utiles[ofs] = ART_UTA_BBOX_CONS (MIN (ART_UTA_BBOX_X0 (bb), xf1),
MIN (ART_UTA_BBOX_Y0 (bb), yf1),
MAX (ART_UTA_BBOX_X1 (bb), xf2),
MAX (ART_UTA_BBOX_Y1 (bb), yf2));
if (bb == 0)
utiles[ofs] = ART_UTA_BBOX_CONS (
xf1, yf1, xf2, yf2);
else
utiles[ofs] = ART_UTA_BBOX_CONS (
MIN (ART_UTA_BBOX_X0 (bb), xf1),
MIN (ART_UTA_BBOX_Y0 (bb), yf1),
MAX (ART_UTA_BBOX_X1 (bb), xf2),
MAX (ART_UTA_BBOX_Y1 (bb), yf2));
} else {
/* Leftmost tile */
bb = utiles[ofs];
utiles[ofs++] = ART_UTA_BBOX_CONS (MIN (ART_UTA_BBOX_X0 (bb), xf1),
MIN (ART_UTA_BBOX_Y0 (bb), yf1),
ART_UTILE_SIZE,
MAX (ART_UTA_BBOX_Y1 (bb), yf2));
if (bb == 0)
utiles[ofs++] = ART_UTA_BBOX_CONS (
xf1, yf1, ART_UTILE_SIZE, yf2);
else
utiles[ofs++] = ART_UTA_BBOX_CONS (
MIN (ART_UTA_BBOX_X0 (bb), xf1),
MIN (ART_UTA_BBOX_Y0 (bb), yf1),
ART_UTILE_SIZE,
MAX (ART_UTA_BBOX_Y1 (bb), yf2));
/* Tiles in between */
for (x = rect_x1 + 1; x < rect_x2 - 1; x++) {
bb = utiles[ofs];
utiles[ofs++] = ART_UTA_BBOX_CONS (0,
MIN (ART_UTA_BBOX_Y0 (bb), yf1),
ART_UTILE_SIZE,
MAX (ART_UTA_BBOX_Y1 (bb), yf2));
if (bb == 0)
utiles[ofs++] = ART_UTA_BBOX_CONS (
0, yf1, ART_UTILE_SIZE, yf2);
else
utiles[ofs++] = ART_UTA_BBOX_CONS (
0,
MIN (ART_UTA_BBOX_Y0 (bb), yf1),
ART_UTILE_SIZE,
MAX (ART_UTA_BBOX_Y1 (bb), yf2));
}
/* Rightmost tile */
bb = utiles[ofs];
utiles[ofs] = ART_UTA_BBOX_CONS (0,
MIN (ART_UTA_BBOX_Y0 (bb), yf1),
MAX (ART_UTA_BBOX_X1 (bb), xf2),
MAX (ART_UTA_BBOX_Y1 (bb), yf2));
if (bb == 0)
utiles[ofs] = ART_UTA_BBOX_CONS (
0, yf1, xf2, yf2);
else
utiles[ofs] = ART_UTA_BBOX_CONS (
0,
MIN (ART_UTA_BBOX_Y0 (bb), yf1),
MAX (ART_UTA_BBOX_X1 (bb), xf2),
MAX (ART_UTA_BBOX_Y1 (bb), yf2));
}
} else {
if (rect_x2 - rect_x1 == 1) {
/* Topmost tile */
bb = utiles[ofs];
utiles[ofs] = ART_UTA_BBOX_CONS (MIN (ART_UTA_BBOX_X0 (bb), xf1),
MIN (ART_UTA_BBOX_Y0 (bb), yf1),
MAX (ART_UTA_BBOX_X1 (bb), xf2),
ART_UTILE_SIZE);
if (bb == 0)
utiles[ofs] = ART_UTA_BBOX_CONS (
xf1, yf1, xf2, ART_UTILE_SIZE);
else
utiles[ofs] = ART_UTA_BBOX_CONS (
MIN (ART_UTA_BBOX_X0 (bb), xf1),
MIN (ART_UTA_BBOX_Y0 (bb), yf1),
MAX (ART_UTA_BBOX_X1 (bb), xf2),
ART_UTILE_SIZE);
ofs += uta->width;
/* Tiles in between */
for (y = rect_y1 + 1; y < rect_y2 - 1; y++) {
bb = utiles[ofs];
utiles[ofs] = ART_UTA_BBOX_CONS (MIN (ART_UTA_BBOX_X0 (bb), xf1),
0,
MAX (ART_UTA_BBOX_X1 (bb), xf2),
ART_UTILE_SIZE);
if (bb == 0)
utiles[ofs] = ART_UTA_BBOX_CONS (
xf1, 0, xf2, ART_UTILE_SIZE);
else
utiles[ofs] = ART_UTA_BBOX_CONS (
MIN (ART_UTA_BBOX_X0 (bb), xf1),
0,
MAX (ART_UTA_BBOX_X1 (bb), xf2),
ART_UTILE_SIZE);
ofs += uta->width;
}
/* Bottommost tile */
bb = utiles[ofs];
utiles[ofs] = ART_UTA_BBOX_CONS (MIN (ART_UTA_BBOX_X0 (bb), xf1),
0,
MAX (ART_UTA_BBOX_X1 (bb), xf2),
MAX (ART_UTA_BBOX_Y1 (bb), yf2));
if (bb == 0)
utiles[ofs] = ART_UTA_BBOX_CONS (
xf1, 0, xf2, yf2);
else
utiles[ofs] = ART_UTA_BBOX_CONS (
MIN (ART_UTA_BBOX_X0 (bb), xf1),
0,
MAX (ART_UTA_BBOX_X1 (bb), xf2),
MAX (ART_UTA_BBOX_Y1 (bb), yf2));
} else {
/* Top row, leftmost tile */
bb = utiles[ofs];
utiles[ofs++] = ART_UTA_BBOX_CONS (MIN (ART_UTA_BBOX_X0 (bb), xf1),
MIN (ART_UTA_BBOX_Y0 (bb), yf1),
ART_UTILE_SIZE,
ART_UTILE_SIZE);
if (bb == 0)
utiles[ofs++] = ART_UTA_BBOX_CONS (
xf1, yf1, ART_UTILE_SIZE, ART_UTILE_SIZE);
else
utiles[ofs++] = ART_UTA_BBOX_CONS (
MIN (ART_UTA_BBOX_X0 (bb), xf1),
MIN (ART_UTA_BBOX_Y0 (bb), yf1),
ART_UTILE_SIZE,
ART_UTILE_SIZE);
/* Top row, in between */
for (x = rect_x1 + 1; x < rect_x2 - 1; x++) {
bb = utiles[ofs];
utiles[ofs++] = ART_UTA_BBOX_CONS (0,
MIN (ART_UTA_BBOX_Y0 (bb), yf1),
ART_UTILE_SIZE,
ART_UTILE_SIZE);
if (bb == 0)
utiles[ofs++] = ART_UTA_BBOX_CONS (
0, yf1, ART_UTILE_SIZE, ART_UTILE_SIZE);
else
utiles[ofs++] = ART_UTA_BBOX_CONS (
0,
MIN (ART_UTA_BBOX_Y0 (bb), yf1),
ART_UTILE_SIZE,
ART_UTILE_SIZE);
}
/* Top row, rightmost tile */
/* Top row, rightmost tile */
bb = utiles[ofs];
utiles[ofs] = ART_UTA_BBOX_CONS (0,
MIN (ART_UTA_BBOX_Y0 (bb), yf1),
MAX (ART_UTA_BBOX_X1 (bb), xf2),
ART_UTILE_SIZE);
if (bb == 0)
utiles[ofs] = ART_UTA_BBOX_CONS (
0, yf1, xf2, ART_UTILE_SIZE);
else
utiles[ofs] = ART_UTA_BBOX_CONS (
0,
MIN (ART_UTA_BBOX_Y0 (bb), yf1),
MAX (ART_UTA_BBOX_X1 (bb), xf2),
ART_UTILE_SIZE);
ofs += uta->width - (rect_x2 - rect_x1 - 1);
@ -246,10 +298,15 @@ uta_add_rect (ArtUta *uta, int x1, int y1, int x2, int y2)
for (y = rect_y1 + 1; y < rect_y2 - 1; y++) {
/* Leftmost tile */
bb = utiles[ofs];
utiles[ofs++] = ART_UTA_BBOX_CONS (MIN (ART_UTA_BBOX_X0 (bb), xf1),
0,
ART_UTILE_SIZE,
ART_UTILE_SIZE);
if (bb == 0)
utiles[ofs++] = ART_UTA_BBOX_CONS (
xf1, 0, ART_UTILE_SIZE, ART_UTILE_SIZE);
else
utiles[ofs++] = ART_UTA_BBOX_CONS (
MIN (ART_UTA_BBOX_X0 (bb), xf1),
0,
ART_UTILE_SIZE,
ART_UTILE_SIZE);
/* Tiles in between */
bb = ART_UTA_BBOX_CONS (0, 0, ART_UTILE_SIZE, ART_UTILE_SIZE);
@ -258,36 +315,56 @@ uta_add_rect (ArtUta *uta, int x1, int y1, int x2, int y2)
/* Rightmost tile */
bb = utiles[ofs];
utiles[ofs] = ART_UTA_BBOX_CONS (0,
0,
MAX (ART_UTA_BBOX_X1 (bb), xf2),
ART_UTILE_SIZE);
if (bb == 0)
utiles[ofs] = ART_UTA_BBOX_CONS (
0, 0, xf2, ART_UTILE_SIZE);
else
utiles[ofs] = ART_UTA_BBOX_CONS (
0,
0,
MAX (ART_UTA_BBOX_X1 (bb), xf2),
ART_UTILE_SIZE);
ofs += uta->width - (rect_x2 - rect_x1 - 1);
}
/* Bottom row, leftmost tile */
bb = utiles[ofs];
utiles[ofs++] = ART_UTA_BBOX_CONS (MIN (ART_UTA_BBOX_X0 (bb), xf1),
0,
ART_UTILE_SIZE,
MAX (ART_UTA_BBOX_Y1 (bb), yf2));
if (bb == 0)
utiles[ofs++] = ART_UTA_BBOX_CONS (
xf1, 0, ART_UTILE_SIZE, yf2);
else
utiles[ofs++] = ART_UTA_BBOX_CONS (
MIN (ART_UTA_BBOX_X0 (bb), xf1),
0,
ART_UTILE_SIZE,
MAX (ART_UTA_BBOX_Y1 (bb), yf2));
/* Bottom row, tiles in between */
for (x = rect_x1 + 1; x < rect_x2 - 1; x++) {
bb = utiles[ofs];
utiles[ofs++] = ART_UTA_BBOX_CONS (0,
0,
ART_UTILE_SIZE,
MAX (ART_UTA_BBOX_Y1 (bb), yf2));
if (bb == 0)
utiles[ofs++] = ART_UTA_BBOX_CONS (
0, 0, ART_UTILE_SIZE, yf2);
else
utiles[ofs++] = ART_UTA_BBOX_CONS (
0,
0,
ART_UTILE_SIZE,
MAX (ART_UTA_BBOX_Y1 (bb), yf2));
}
/* Bottom row, rightmost tile */
bb = utiles[ofs];
utiles[ofs] = ART_UTA_BBOX_CONS (0,
0,
MAX (ART_UTA_BBOX_X1 (bb), xf2),
MAX (ART_UTA_BBOX_Y1 (bb), yf2));
if (bb == 0)
utiles[ofs] = ART_UTA_BBOX_CONS (
0, 0, xf2, yf2);
else
utiles[ofs] = ART_UTA_BBOX_CONS (
0,
0,
MAX (ART_UTA_BBOX_X1 (bb), xf2),
MAX (ART_UTA_BBOX_Y1 (bb), yf2));
}
}
@ -301,7 +378,7 @@ uta_add_rect (ArtUta *uta, int x1, int y1, int x2, int y2)
* @y1: Top coordinate of rectangle.
* @x2: Right coordinate of rectangle.
* @y2: Bottom coordinate of rectangle.
*
*
* Removes a rectangular region from the specified microtile array. Due to the
* way microtile arrays represent regions, the tiles at the edge of the
* rectangle may not be clipped exactly.
@ -363,7 +440,7 @@ uta_remove_rect (ArtUta *uta, int x1, int y1, int x2, int y2)
ArtUtaBbox bb;
int bb_x1, bb_y1, bb_x2, bb_y2;
int bb_cx1, bb_cy1, bb_cx2, bb_cy2;
bb = utiles[ofs];
bb_x1 = ART_UTA_BBOX_X0 (bb);
bb_y1 = ART_UTA_BBOX_Y0 (bb);
@ -588,3 +665,285 @@ uta_find_first_glom_rect (ArtUta *uta, ArtIRect *rect, int max_width, int max_he
}
#endif
/* Copies a single microtile to another location in the UTA, offsetted by the
* specified distance. A microtile can thus end up being added in a single part
* to another microtile, in two parts to two horizontally or vertically adjacent
* microtiles, or in four parts to a 2x2 square of microtiles.
*
* This is basically a normal BitBlt but with copying-forwards-to-the-destination
* instead of fetching-backwards-from-the-source.
*/
static void
copy_tile (ArtUta *uta, int x, int y, int xofs, int yofs)
{
ArtUtaBbox *utiles;
ArtUtaBbox bb, dbb;
int t_x1, t_y1, t_x2, t_y2;
int d_x1, d_y1, d_x2, d_y2;
int d_tx1, d_ty1;
int d_xf1, d_yf1, d_xf2, d_yf2;
int dofs;
utiles = uta->utiles;
bb = utiles[(y - uta->y0) * uta->width + x - uta->x0];
if (bb == 0)
return;
t_x1 = ART_UTA_BBOX_X0 (bb) + (x << ART_UTILE_SHIFT);
t_y1 = ART_UTA_BBOX_Y0 (bb) + (y << ART_UTILE_SHIFT);
t_x2 = ART_UTA_BBOX_X1 (bb) + (x << ART_UTILE_SHIFT);
t_y2 = ART_UTA_BBOX_Y1 (bb) + (y << ART_UTILE_SHIFT);
d_x1 = t_x1 + xofs;
d_y1 = t_y1 + yofs;
d_x2 = t_x2 + xofs;
d_y2 = t_y2 + yofs;
d_tx1 = d_x1 >> ART_UTILE_SHIFT;
d_ty1 = d_y1 >> ART_UTILE_SHIFT;
dofs = (d_ty1 - uta->y0) * uta->width + d_tx1 - uta->x0;
d_xf1 = d_x1 & (ART_UTILE_SIZE - 1);
d_yf1 = d_y1 & (ART_UTILE_SIZE - 1);
d_xf2 = ((d_x2 - 1) & (ART_UTILE_SIZE - 1)) + 1;
d_yf2 = ((d_y2 - 1) & (ART_UTILE_SIZE - 1)) + 1;
if (d_x2 - d_x1 <= ART_UTILE_SIZE - d_xf1) {
if (d_y2 - d_y1 <= ART_UTILE_SIZE - d_yf1) {
if (d_tx1 >= uta->x0 && d_tx1 < uta->x0 + uta->width
&& d_ty1 >= uta->y0 && d_ty1 < uta->y0 + uta->height) {
dbb = utiles[dofs];
if (dbb == 0)
utiles[dofs] = ART_UTA_BBOX_CONS (
d_xf1, d_yf1, d_xf2, d_yf2);
else
utiles[dofs] = ART_UTA_BBOX_CONS (
MIN (ART_UTA_BBOX_X0 (dbb), d_xf1),
MIN (ART_UTA_BBOX_Y0 (dbb), d_yf1),
MAX (ART_UTA_BBOX_X1 (dbb), d_xf2),
MAX (ART_UTA_BBOX_Y1 (dbb), d_yf2));
}
} else {
/* Top tile */
if (d_tx1 >= uta->x0 && d_tx1 < uta->x0 + uta->width
&& d_ty1 >= uta->y0 && d_ty1 < uta->y0 + uta->height) {
dbb = utiles[dofs];
if (dbb == 0)
utiles[dofs] = ART_UTA_BBOX_CONS (
d_xf1, d_yf1, d_xf2, ART_UTILE_SIZE);
else
utiles[dofs] = ART_UTA_BBOX_CONS (
MIN (ART_UTA_BBOX_X0 (dbb), d_xf1),
MIN (ART_UTA_BBOX_Y0 (dbb), d_yf1),
MAX (ART_UTA_BBOX_X1 (dbb), d_xf2),
ART_UTILE_SIZE);
}
dofs += uta->width;
/* Bottom tile */
if (d_tx1 >= uta->x0 && d_tx1 < uta->x0 + uta->width
&& d_ty1 + 1 >= uta->y0 && d_ty1 + 1 < uta->y0 + uta->height) {
dbb = utiles[dofs];
if (dbb == 0)
utiles[dofs] = ART_UTA_BBOX_CONS (
d_xf1, 0, d_xf2, d_yf2);
else
utiles[dofs] = ART_UTA_BBOX_CONS (
MIN (ART_UTA_BBOX_X0 (dbb), d_xf1),
0,
MAX (ART_UTA_BBOX_X1 (dbb), d_xf2),
MAX (ART_UTA_BBOX_Y1 (dbb), d_yf2));
}
}
} else {
if (d_y2 - d_y1 <= ART_UTILE_SIZE - d_yf1) {
/* Left tile */
if (d_tx1 >= uta->x0 && d_tx1 < uta->x0 + uta->width
&& d_ty1 >= uta->y0 && d_ty1 < uta->y0 + uta->height) {
dbb = utiles[dofs];
if (dbb == 0)
utiles[dofs] = ART_UTA_BBOX_CONS (
d_xf1, d_yf1, ART_UTILE_SIZE, d_yf2);
else
utiles[dofs] = ART_UTA_BBOX_CONS (
MIN (ART_UTA_BBOX_X0 (dbb), d_xf1),
MIN (ART_UTA_BBOX_Y0 (dbb), d_yf1),
ART_UTILE_SIZE,
MAX (ART_UTA_BBOX_Y1 (dbb), d_yf2));
}
dofs++;
/* Right tile */
if (d_tx1 + 1 >= uta->x0 && d_tx1 + 1 < uta->x0 + uta->width
&& d_ty1 >= uta->y0 && d_ty1 < uta->y0 + uta->height) {
dbb = utiles[dofs];
if (dbb == 0)
utiles[dofs] = ART_UTA_BBOX_CONS (
0, d_yf1, d_xf2, d_yf2);
else
utiles[dofs] = ART_UTA_BBOX_CONS (
0,
MIN (ART_UTA_BBOX_Y0 (dbb), d_yf1),
MAX (ART_UTA_BBOX_X1 (dbb), d_xf2),
MAX (ART_UTA_BBOX_Y1 (dbb), d_yf2));
}
} else {
/* Top left tile */
if (d_tx1 >= uta->x0 && d_tx1 < uta->x0 + uta->width
&& d_ty1 >= uta->y0 && d_ty1 < uta->y0 + uta->height) {
dbb = utiles[dofs];
if (dbb == 0)
utiles[dofs] = ART_UTA_BBOX_CONS (
d_xf1, d_yf1, ART_UTILE_SIZE, ART_UTILE_SIZE);
else
utiles[dofs] = ART_UTA_BBOX_CONS (
MIN (ART_UTA_BBOX_X0 (dbb), d_xf1),
MIN (ART_UTA_BBOX_Y0 (dbb), d_yf1),
ART_UTILE_SIZE,
ART_UTILE_SIZE);
}
dofs++;
/* Top right tile */
if (d_tx1 + 1 >= uta->x0 && d_tx1 + 1 < uta->x0 + uta->width
&& d_ty1 >= uta->y0 && d_ty1 < uta->y0 + uta->height) {
dbb = utiles[dofs];
if (dbb == 0)
utiles[dofs] = ART_UTA_BBOX_CONS (
0, d_yf1, d_xf2, ART_UTILE_SIZE);
else
utiles[dofs] = ART_UTA_BBOX_CONS (
0,
MIN (ART_UTA_BBOX_Y0 (dbb), d_yf1),
MAX (ART_UTA_BBOX_X1 (dbb), d_xf2),
ART_UTILE_SIZE);
}
dofs += uta->width - 1;
/* Bottom left tile */
if (d_tx1 >= uta->x0 && d_tx1 < uta->x0 + uta->width
&& d_ty1 + 1 >= uta->y0 && d_ty1 + 1 < uta->y0 + uta->height) {
dbb = utiles[dofs];
if (dbb == 0)
utiles[dofs] = ART_UTA_BBOX_CONS (
d_xf1, 0, ART_UTILE_SIZE, d_yf2);
else
utiles[dofs] = ART_UTA_BBOX_CONS (
MIN (ART_UTA_BBOX_X0 (dbb), d_xf1),
0,
ART_UTILE_SIZE,
MAX (ART_UTA_BBOX_Y1 (dbb), d_yf2));
}
dofs++;
/* Bottom right tile */
if (d_tx1 + 1 >= uta->x0 && d_tx1 + 1 < uta->x0 + uta->width
&& d_ty1 + 1 >= uta->y0 && d_ty1 + 1 < uta->y0 + uta->height) {
dbb = utiles[dofs];
if (dbb == 0)
utiles[dofs] = ART_UTA_BBOX_CONS (
0, 0, d_xf2, d_yf2);
else
utiles[dofs] = ART_UTA_BBOX_CONS (
0,
0,
MAX (ART_UTA_BBOX_X1 (dbb), d_xf2),
MAX (ART_UTA_BBOX_Y1 (dbb), d_yf2));
}
}
}
}
/**
* uta_copy_area:
* @uta: A microtile array.
* @src_x: Left coordinate of source rectangle.
* @src_y: Top coordinate of source rectangle.
* @dest_x: Left coordinate of destination.
* @dest_y: Top coordinate of destination.
* @width: Width of region to copy.
* @height: Height of region to copy.
*
* Copies a rectangular region within a microtile array. The array will not be
* expanded if the destination area does not fit within it; rather only the area
* that fits will be copied. The source rectangle must be completely contained
* within the microtile array.
**/
void
uta_copy_area (ArtUta *uta, int src_x, int src_y, int dest_x, int dest_y, int width, int height)
{
ArtUtaBbox *utiles;
int rect_x1, rect_y1, rect_x2, rect_y2;
gboolean top_to_bottom, left_to_right;
int xofs, yofs;
int x, y;
g_return_if_fail (uta != NULL);
g_return_if_fail (width >= 0 && height >= 0);
g_return_if_fail (src_x >= uta->x0 << ART_UTILE_SHIFT);
g_return_if_fail (src_y >= uta->y0 << ART_UTILE_SHIFT);
g_return_if_fail (src_x + width <= (uta->x0 + uta->width) << ART_UTILE_SHIFT);
g_return_if_fail (src_y + height <= (uta->y0 + uta->height) << ART_UTILE_SHIFT);
if ((src_x == dest_x && src_y == dest_y) || width == 0 || height == 0)
return;
/* FIXME: This function is not perfect. It *adds* the copied/offsetted
* area to the original contents of the microtile array, thus growing
* the region more than needed. The effect should be to "overwrite" the
* original contents, just like XCopyArea() does. Care needs to be
* taken when the edges of the rectangle do not fall on microtile
* boundaries, because tiles may need to be "split".
*
* Maybe this will work:
*
* 1. Copy the rectangular array of tiles that form the region to a
* temporary buffer.
*
* 2. uta_remove_rect() the *destination* rectangle from the original
* microtile array.
*
* 3. Copy back the temporary buffer to the original array while
* offsetting it in the same way as copy_tile() does.
*/
utiles = uta->utiles;
rect_x1 = src_x >> ART_UTILE_SHIFT;
rect_y1 = src_y >> ART_UTILE_SHIFT;
rect_x2 = (src_x + width + ART_UTILE_SIZE - 1) >> ART_UTILE_SHIFT;
rect_y2 = (src_y + height + ART_UTILE_SIZE - 1) >> ART_UTILE_SHIFT;
xofs = dest_x - src_x;
yofs = dest_y - src_y;
left_to_right = xofs < 0;
top_to_bottom = yofs < 0;
if (top_to_bottom && left_to_right) {
for (y = rect_y1; y < rect_y2; y++)
for (x = rect_x1; x < rect_x2; x++)
copy_tile (uta, x, y, xofs, yofs);
} else if (top_to_bottom && !left_to_right) {
for (y = rect_y1; y < rect_y2; y++)
for (x = rect_x2 - 1; x >= rect_x1; x--)
copy_tile (uta, x, y, xofs, yofs);
} else if (!top_to_bottom && left_to_right) {
for (y = rect_y2 - 1; y >= rect_y1; y--)
for (x = rect_x1; x < rect_x2; x++)
copy_tile (uta, x, y, xofs, yofs);
} else if (!top_to_bottom && !left_to_right) {
for (y = rect_y2 - 1; y >= rect_y1; y--)
for (x = rect_x2 - 1; x >= rect_x1; x--)
copy_tile (uta, x, y, xofs, yofs);
}
}

View file

@ -35,6 +35,8 @@ void uta_remove_rect (ArtUta *uta, int x1, int y1, int x2, int y2);
void uta_find_first_glom_rect (ArtUta *uta, ArtIRect *rect, int max_width, int max_height);
void uta_copy_area (ArtUta *uta, int src_x, int src_y, int dest_x, int dest_y, int width, int height);
#endif

View file

@ -1,3 +1,19 @@
2000-04-21 Federico Mena Quintero <federico@helixcode.com>
* image-view.c (scroll_to): New function to do proper scrolling of
the image view.
(drag_to): Use scroll_to().
(adjustment_changed_cb): Use scroll_to().
* uta.c (uta_copy_area): New function to do the equivalent of a
BitBlt on a microtile array. It is not perfect; see the FIXME for
details.
(copy_tile): Helper function for uta_copy_area().
(uta_ensure_size): Create a new uta with the proper size if the
specified uta is NULL.
(uta_add_rect): Special-case source tiles that are empty so as not
to add a bigger region than needed.
2000-04-20 Federico Mena Quintero <federico@helixcode.com>
* window.c (window_open_image): Add the image size to the window

View file

@ -21,6 +21,7 @@
#include <config.h>
#include <math.h>
#include <stdlib.h>
#include <gtk/gtksignal.h>
#include "cursors.h"
#include "image-view.h"
@ -57,6 +58,9 @@ typedef struct {
GtkAdjustment *hadj;
GtkAdjustment *vadj;
/* Current scrolling offsets */
int xofs, yofs;
/* Microtile array for dirty region */
ArtUta *uta;
@ -324,12 +328,12 @@ paint_rectangle (ImageView *view, ArtIRect *rect)
if (scaled_width < width)
xofs = (width - scaled_width) / 2;
else
xofs = -priv->hadj->value;
xofs = -priv->xofs;
if (scaled_height < height)
yofs = (height - scaled_height) / 2;
else
yofs = -priv->vadj->value;
yofs = -priv->yofs;
/* Draw background if necessary, in four steps */
@ -412,6 +416,17 @@ paint_rectangle (ImageView *view, ArtIRect *rect)
d.x0 - xofs, d.y0 - yofs);
gdk_pixbuf_unref (tmp);
#if 0
gdk_draw_line (GTK_WIDGET (view)->window,
GTK_WIDGET (view)->style->black_gc,
d.x0, d.y0,
d.x1 - 1, d.y1 - 1);
gdk_draw_line (GTK_WIDGET (view)->window,
GTK_WIDGET (view)->style->black_gc,
d.x1 - 1, d.y0,
d.x0, d.y1 - 1);
#endif
}
/* Idle handler for the drawing process. We pull a rectangle from the dirty
@ -480,6 +495,119 @@ request_paint_area (ImageView *view, GdkRectangle *area)
}
}
/* Scrolls the view to the specified offsets. Does not perform range checking! */
static void
scroll_to (ImageView *view, int x, int y)
{
ImageViewPrivate *priv;
int xofs, yofs;
GdkWindow *window;
GdkGC *gc;
int width, height;
int src_x, src_y;
int dest_x, dest_y;
int twidth, theight;
GdkEvent *event;
priv = view->priv;
/* Compute offsets and check bounds */
xofs = x - priv->xofs;
yofs = y - priv->yofs;
if (xofs == 0 && yofs == 0)
return;
width = GTK_WIDGET (view)->allocation.width;
height = GTK_WIDGET (view)->allocation.height;
priv->xofs = x;
priv->yofs = y;
if (abs (xofs) >= width || abs (yofs) >= height) {
GdkRectangle area;
area.x = 0;
area.y = 0;
area.width = width;
area.height = height;
request_paint_area (view, &area);
return;
}
window = GTK_WIDGET (view)->window;
/* Ensure that the uta has the full size */
twidth = (width + ART_UTILE_SIZE - 1) >> ART_UTILE_SHIFT;
theight = (height + ART_UTILE_SIZE - 1) >> ART_UTILE_SHIFT;
if (priv->uta)
g_assert (priv->idle_id != 0);
else
priv->idle_id = g_idle_add (paint_iteration_idle, view);
priv->uta = uta_ensure_size (priv->uta, 0, 0, twidth, theight);
/* Copy the uta area and add the scrolled-in region */
src_x = xofs < 0 ? 0 : xofs;
src_y = yofs < 0 ? 0 : yofs;
dest_x = xofs < 0 ? -xofs : 0;
dest_y = yofs < 0 ? -yofs : 0;
uta_copy_area (priv->uta,
src_x, src_y,
dest_x, dest_y,
width - abs (xofs), height - abs (yofs));
if (xofs) {
int xx;
xx = xofs < 0 ? 0 : width - xofs;
uta_add_rect (priv->uta,
xx, 0,
xx + abs (xofs), height);
}
if (yofs) {
int yy;
yy = yofs < 0 ? 0 : height - yofs;
uta_add_rect (priv->uta,
0, yy,
width, yy + abs (yofs));
}
/* Copy the window area */
gc = gdk_gc_new (window);
gdk_gc_set_exposures (gc, TRUE);
gdk_window_copy_area (window,
gc,
dest_x, dest_y,
window,
src_x, src_y,
width - abs (xofs),
height - abs (yofs));
gdk_gc_destroy (gc);
/* Process graphics exposures */
while ((event = gdk_event_get_graphics_expose (window)) != NULL) {
gtk_widget_event (GTK_WIDGET (view), event);
if (event->expose.count == 0) {
gdk_event_free (event);
break;
}
gdk_event_free (event);
}
}
/* Widget methods */
@ -668,8 +796,8 @@ image_view_button_press (GtkWidget *widget, GdkEventButton *event)
priv->drag_anchor_x = event->x;
priv->drag_anchor_y = event->y;
priv->drag_ofs_x = priv->hadj->value;
priv->drag_ofs_y = priv->vadj->value;
priv->drag_ofs_x = priv->xofs;
priv->drag_ofs_y = priv->yofs;
cursor = cursor_get (widget->window, CURSOR_HAND_CLOSED);
gdk_pointer_grab (widget->window,
@ -691,7 +819,6 @@ drag_to (ImageView *view, int x, int y)
{
ImageViewPrivate *priv;
int dx, dy;
GdkRectangle area;
priv = view->priv;
@ -701,6 +828,8 @@ drag_to (ImageView *view, int x, int y)
x = CLAMP (priv->drag_ofs_x + dx, 0, priv->hadj->upper - priv->hadj->page_size);
y = CLAMP (priv->drag_ofs_y + dy, 0, priv->vadj->upper - priv->vadj->page_size);
scroll_to (view, x, y);
gtk_signal_handler_block_by_data (GTK_OBJECT (priv->hadj), view);
gtk_signal_handler_block_by_data (GTK_OBJECT (priv->vadj), view);
@ -712,15 +841,6 @@ drag_to (ImageView *view, int x, int y)
gtk_signal_handler_unblock_by_data (GTK_OBJECT (priv->hadj), view);
gtk_signal_handler_unblock_by_data (GTK_OBJECT (priv->vadj), view);
/* FIXME: use copy_area() for the window and the uta */
area.x = 0;
area.y = 0;
area.width = GTK_WIDGET (view)->allocation.width;
area.height = GTK_WIDGET (view)->allocation.height;
request_paint_area (view, &area);
}
/* Button release handler for the image view */
@ -791,19 +911,11 @@ adjustment_changed_cb (GtkAdjustment *adj, gpointer data)
{
ImageView *view;
ImageViewPrivate *priv;
GdkRectangle area;
view = IMAGE_VIEW (data);
priv = view->priv;
/* FIXME: use copy_area() for the window and the uta */
area.x = 0;
area.y = 0;
area.width = GTK_WIDGET (view)->allocation.width;
area.height = GTK_WIDGET (view)->allocation.height;
request_paint_area (view, &area);
scroll_to (view, priv->hadj->value, priv->vadj->value);
}
/* Set_scroll_adjustments handler for the image view */

495
src/uta.c
View file

@ -33,7 +33,7 @@
* @y1: Top microtile coordinate that must fit in new array.
* @x2: Right microtile coordinate that must fit in new array.
* @y2: Bottom microtile coordinate that must fit in new array.
*
*
* Ensures that the size of a microtile array is big enough to fit the specified
* microtile coordinates. If it is not big enough, the specified @uta will be
* freed and a new one will be returned. Otherwise, the original @uta will be
@ -42,7 +42,7 @@
*
* Note that the specified coordinates must have already been scaled down by the
* ART_UTILE_SHIFT factor.
*
*
* Return value: The same value as @uta if the original microtile array was
* big enough to fit the specified microtile coordinates, or a new array if
* it needed to be grown. In the second case, the original @uta will be
@ -56,10 +56,12 @@ uta_ensure_size (ArtUta *uta, int x1, int y1, int x2, int y2)
int new_ofs, ofs;
int x, y;
g_return_val_if_fail (uta != NULL, NULL);
g_return_val_if_fail (x1 < x2, NULL);
g_return_val_if_fail (y1 < y2, NULL);
if (!uta)
return art_uta_new (x1, y1, x2, y2);
if (x1 >= uta->x0
&& y1 >= uta->y0
&& x2 <= uta->x0 + uta->width
@ -105,10 +107,10 @@ uta_ensure_size (ArtUta *uta, int x1, int y1, int x2, int y2)
* @y1: Top coordinate of rectangle.
* @x2: Right coordinate of rectangle.
* @y2: Bottom coordinate of rectangle.
*
*
* Adds the specified rectangle to a microtile array. The array is
* grown to fit the rectangle if necessary.
*
*
* Return value: The original @uta, or a new microtile array if the original one
* needed to be grown to fit the specified rectangle. In the second case, the
* original @uta will be freed automatically.
@ -162,83 +164,133 @@ uta_add_rect (ArtUta *uta, int x1, int y1, int x2, int y2)
if (rect_y2 - rect_y1 == 1) {
if (rect_x2 - rect_x1 == 1) {
bb = utiles[ofs];
utiles[ofs] = ART_UTA_BBOX_CONS (MIN (ART_UTA_BBOX_X0 (bb), xf1),
MIN (ART_UTA_BBOX_Y0 (bb), yf1),
MAX (ART_UTA_BBOX_X1 (bb), xf2),
MAX (ART_UTA_BBOX_Y1 (bb), yf2));
if (bb == 0)
utiles[ofs] = ART_UTA_BBOX_CONS (
xf1, yf1, xf2, yf2);
else
utiles[ofs] = ART_UTA_BBOX_CONS (
MIN (ART_UTA_BBOX_X0 (bb), xf1),
MIN (ART_UTA_BBOX_Y0 (bb), yf1),
MAX (ART_UTA_BBOX_X1 (bb), xf2),
MAX (ART_UTA_BBOX_Y1 (bb), yf2));
} else {
/* Leftmost tile */
bb = utiles[ofs];
utiles[ofs++] = ART_UTA_BBOX_CONS (MIN (ART_UTA_BBOX_X0 (bb), xf1),
MIN (ART_UTA_BBOX_Y0 (bb), yf1),
ART_UTILE_SIZE,
MAX (ART_UTA_BBOX_Y1 (bb), yf2));
if (bb == 0)
utiles[ofs++] = ART_UTA_BBOX_CONS (
xf1, yf1, ART_UTILE_SIZE, yf2);
else
utiles[ofs++] = ART_UTA_BBOX_CONS (
MIN (ART_UTA_BBOX_X0 (bb), xf1),
MIN (ART_UTA_BBOX_Y0 (bb), yf1),
ART_UTILE_SIZE,
MAX (ART_UTA_BBOX_Y1 (bb), yf2));
/* Tiles in between */
for (x = rect_x1 + 1; x < rect_x2 - 1; x++) {
bb = utiles[ofs];
utiles[ofs++] = ART_UTA_BBOX_CONS (0,
MIN (ART_UTA_BBOX_Y0 (bb), yf1),
ART_UTILE_SIZE,
MAX (ART_UTA_BBOX_Y1 (bb), yf2));
if (bb == 0)
utiles[ofs++] = ART_UTA_BBOX_CONS (
0, yf1, ART_UTILE_SIZE, yf2);
else
utiles[ofs++] = ART_UTA_BBOX_CONS (
0,
MIN (ART_UTA_BBOX_Y0 (bb), yf1),
ART_UTILE_SIZE,
MAX (ART_UTA_BBOX_Y1 (bb), yf2));
}
/* Rightmost tile */
bb = utiles[ofs];
utiles[ofs] = ART_UTA_BBOX_CONS (0,
MIN (ART_UTA_BBOX_Y0 (bb), yf1),
MAX (ART_UTA_BBOX_X1 (bb), xf2),
MAX (ART_UTA_BBOX_Y1 (bb), yf2));
if (bb == 0)
utiles[ofs] = ART_UTA_BBOX_CONS (
0, yf1, xf2, yf2);
else
utiles[ofs] = ART_UTA_BBOX_CONS (
0,
MIN (ART_UTA_BBOX_Y0 (bb), yf1),
MAX (ART_UTA_BBOX_X1 (bb), xf2),
MAX (ART_UTA_BBOX_Y1 (bb), yf2));
}
} else {
if (rect_x2 - rect_x1 == 1) {
/* Topmost tile */
bb = utiles[ofs];
utiles[ofs] = ART_UTA_BBOX_CONS (MIN (ART_UTA_BBOX_X0 (bb), xf1),
MIN (ART_UTA_BBOX_Y0 (bb), yf1),
MAX (ART_UTA_BBOX_X1 (bb), xf2),
ART_UTILE_SIZE);
if (bb == 0)
utiles[ofs] = ART_UTA_BBOX_CONS (
xf1, yf1, xf2, ART_UTILE_SIZE);
else
utiles[ofs] = ART_UTA_BBOX_CONS (
MIN (ART_UTA_BBOX_X0 (bb), xf1),
MIN (ART_UTA_BBOX_Y0 (bb), yf1),
MAX (ART_UTA_BBOX_X1 (bb), xf2),
ART_UTILE_SIZE);
ofs += uta->width;
/* Tiles in between */
for (y = rect_y1 + 1; y < rect_y2 - 1; y++) {
bb = utiles[ofs];
utiles[ofs] = ART_UTA_BBOX_CONS (MIN (ART_UTA_BBOX_X0 (bb), xf1),
0,
MAX (ART_UTA_BBOX_X1 (bb), xf2),
ART_UTILE_SIZE);
if (bb == 0)
utiles[ofs] = ART_UTA_BBOX_CONS (
xf1, 0, xf2, ART_UTILE_SIZE);
else
utiles[ofs] = ART_UTA_BBOX_CONS (
MIN (ART_UTA_BBOX_X0 (bb), xf1),
0,
MAX (ART_UTA_BBOX_X1 (bb), xf2),
ART_UTILE_SIZE);
ofs += uta->width;
}
/* Bottommost tile */
bb = utiles[ofs];
utiles[ofs] = ART_UTA_BBOX_CONS (MIN (ART_UTA_BBOX_X0 (bb), xf1),
0,
MAX (ART_UTA_BBOX_X1 (bb), xf2),
MAX (ART_UTA_BBOX_Y1 (bb), yf2));
if (bb == 0)
utiles[ofs] = ART_UTA_BBOX_CONS (
xf1, 0, xf2, yf2);
else
utiles[ofs] = ART_UTA_BBOX_CONS (
MIN (ART_UTA_BBOX_X0 (bb), xf1),
0,
MAX (ART_UTA_BBOX_X1 (bb), xf2),
MAX (ART_UTA_BBOX_Y1 (bb), yf2));
} else {
/* Top row, leftmost tile */
bb = utiles[ofs];
utiles[ofs++] = ART_UTA_BBOX_CONS (MIN (ART_UTA_BBOX_X0 (bb), xf1),
MIN (ART_UTA_BBOX_Y0 (bb), yf1),
ART_UTILE_SIZE,
ART_UTILE_SIZE);
if (bb == 0)
utiles[ofs++] = ART_UTA_BBOX_CONS (
xf1, yf1, ART_UTILE_SIZE, ART_UTILE_SIZE);
else
utiles[ofs++] = ART_UTA_BBOX_CONS (
MIN (ART_UTA_BBOX_X0 (bb), xf1),
MIN (ART_UTA_BBOX_Y0 (bb), yf1),
ART_UTILE_SIZE,
ART_UTILE_SIZE);
/* Top row, in between */
for (x = rect_x1 + 1; x < rect_x2 - 1; x++) {
bb = utiles[ofs];
utiles[ofs++] = ART_UTA_BBOX_CONS (0,
MIN (ART_UTA_BBOX_Y0 (bb), yf1),
ART_UTILE_SIZE,
ART_UTILE_SIZE);
if (bb == 0)
utiles[ofs++] = ART_UTA_BBOX_CONS (
0, yf1, ART_UTILE_SIZE, ART_UTILE_SIZE);
else
utiles[ofs++] = ART_UTA_BBOX_CONS (
0,
MIN (ART_UTA_BBOX_Y0 (bb), yf1),
ART_UTILE_SIZE,
ART_UTILE_SIZE);
}
/* Top row, rightmost tile */
/* Top row, rightmost tile */
bb = utiles[ofs];
utiles[ofs] = ART_UTA_BBOX_CONS (0,
MIN (ART_UTA_BBOX_Y0 (bb), yf1),
MAX (ART_UTA_BBOX_X1 (bb), xf2),
ART_UTILE_SIZE);
if (bb == 0)
utiles[ofs] = ART_UTA_BBOX_CONS (
0, yf1, xf2, ART_UTILE_SIZE);
else
utiles[ofs] = ART_UTA_BBOX_CONS (
0,
MIN (ART_UTA_BBOX_Y0 (bb), yf1),
MAX (ART_UTA_BBOX_X1 (bb), xf2),
ART_UTILE_SIZE);
ofs += uta->width - (rect_x2 - rect_x1 - 1);
@ -246,10 +298,15 @@ uta_add_rect (ArtUta *uta, int x1, int y1, int x2, int y2)
for (y = rect_y1 + 1; y < rect_y2 - 1; y++) {
/* Leftmost tile */
bb = utiles[ofs];
utiles[ofs++] = ART_UTA_BBOX_CONS (MIN (ART_UTA_BBOX_X0 (bb), xf1),
0,
ART_UTILE_SIZE,
ART_UTILE_SIZE);
if (bb == 0)
utiles[ofs++] = ART_UTA_BBOX_CONS (
xf1, 0, ART_UTILE_SIZE, ART_UTILE_SIZE);
else
utiles[ofs++] = ART_UTA_BBOX_CONS (
MIN (ART_UTA_BBOX_X0 (bb), xf1),
0,
ART_UTILE_SIZE,
ART_UTILE_SIZE);
/* Tiles in between */
bb = ART_UTA_BBOX_CONS (0, 0, ART_UTILE_SIZE, ART_UTILE_SIZE);
@ -258,36 +315,56 @@ uta_add_rect (ArtUta *uta, int x1, int y1, int x2, int y2)
/* Rightmost tile */
bb = utiles[ofs];
utiles[ofs] = ART_UTA_BBOX_CONS (0,
0,
MAX (ART_UTA_BBOX_X1 (bb), xf2),
ART_UTILE_SIZE);
if (bb == 0)
utiles[ofs] = ART_UTA_BBOX_CONS (
0, 0, xf2, ART_UTILE_SIZE);
else
utiles[ofs] = ART_UTA_BBOX_CONS (
0,
0,
MAX (ART_UTA_BBOX_X1 (bb), xf2),
ART_UTILE_SIZE);
ofs += uta->width - (rect_x2 - rect_x1 - 1);
}
/* Bottom row, leftmost tile */
bb = utiles[ofs];
utiles[ofs++] = ART_UTA_BBOX_CONS (MIN (ART_UTA_BBOX_X0 (bb), xf1),
0,
ART_UTILE_SIZE,
MAX (ART_UTA_BBOX_Y1 (bb), yf2));
if (bb == 0)
utiles[ofs++] = ART_UTA_BBOX_CONS (
xf1, 0, ART_UTILE_SIZE, yf2);
else
utiles[ofs++] = ART_UTA_BBOX_CONS (
MIN (ART_UTA_BBOX_X0 (bb), xf1),
0,
ART_UTILE_SIZE,
MAX (ART_UTA_BBOX_Y1 (bb), yf2));
/* Bottom row, tiles in between */
for (x = rect_x1 + 1; x < rect_x2 - 1; x++) {
bb = utiles[ofs];
utiles[ofs++] = ART_UTA_BBOX_CONS (0,
0,
ART_UTILE_SIZE,
MAX (ART_UTA_BBOX_Y1 (bb), yf2));
if (bb == 0)
utiles[ofs++] = ART_UTA_BBOX_CONS (
0, 0, ART_UTILE_SIZE, yf2);
else
utiles[ofs++] = ART_UTA_BBOX_CONS (
0,
0,
ART_UTILE_SIZE,
MAX (ART_UTA_BBOX_Y1 (bb), yf2));
}
/* Bottom row, rightmost tile */
bb = utiles[ofs];
utiles[ofs] = ART_UTA_BBOX_CONS (0,
0,
MAX (ART_UTA_BBOX_X1 (bb), xf2),
MAX (ART_UTA_BBOX_Y1 (bb), yf2));
if (bb == 0)
utiles[ofs] = ART_UTA_BBOX_CONS (
0, 0, xf2, yf2);
else
utiles[ofs] = ART_UTA_BBOX_CONS (
0,
0,
MAX (ART_UTA_BBOX_X1 (bb), xf2),
MAX (ART_UTA_BBOX_Y1 (bb), yf2));
}
}
@ -301,7 +378,7 @@ uta_add_rect (ArtUta *uta, int x1, int y1, int x2, int y2)
* @y1: Top coordinate of rectangle.
* @x2: Right coordinate of rectangle.
* @y2: Bottom coordinate of rectangle.
*
*
* Removes a rectangular region from the specified microtile array. Due to the
* way microtile arrays represent regions, the tiles at the edge of the
* rectangle may not be clipped exactly.
@ -363,7 +440,7 @@ uta_remove_rect (ArtUta *uta, int x1, int y1, int x2, int y2)
ArtUtaBbox bb;
int bb_x1, bb_y1, bb_x2, bb_y2;
int bb_cx1, bb_cy1, bb_cx2, bb_cy2;
bb = utiles[ofs];
bb_x1 = ART_UTA_BBOX_X0 (bb);
bb_y1 = ART_UTA_BBOX_Y0 (bb);
@ -588,3 +665,285 @@ uta_find_first_glom_rect (ArtUta *uta, ArtIRect *rect, int max_width, int max_he
}
#endif
/* Copies a single microtile to another location in the UTA, offsetted by the
* specified distance. A microtile can thus end up being added in a single part
* to another microtile, in two parts to two horizontally or vertically adjacent
* microtiles, or in four parts to a 2x2 square of microtiles.
*
* This is basically a normal BitBlt but with copying-forwards-to-the-destination
* instead of fetching-backwards-from-the-source.
*/
static void
copy_tile (ArtUta *uta, int x, int y, int xofs, int yofs)
{
ArtUtaBbox *utiles;
ArtUtaBbox bb, dbb;
int t_x1, t_y1, t_x2, t_y2;
int d_x1, d_y1, d_x2, d_y2;
int d_tx1, d_ty1;
int d_xf1, d_yf1, d_xf2, d_yf2;
int dofs;
utiles = uta->utiles;
bb = utiles[(y - uta->y0) * uta->width + x - uta->x0];
if (bb == 0)
return;
t_x1 = ART_UTA_BBOX_X0 (bb) + (x << ART_UTILE_SHIFT);
t_y1 = ART_UTA_BBOX_Y0 (bb) + (y << ART_UTILE_SHIFT);
t_x2 = ART_UTA_BBOX_X1 (bb) + (x << ART_UTILE_SHIFT);
t_y2 = ART_UTA_BBOX_Y1 (bb) + (y << ART_UTILE_SHIFT);
d_x1 = t_x1 + xofs;
d_y1 = t_y1 + yofs;
d_x2 = t_x2 + xofs;
d_y2 = t_y2 + yofs;
d_tx1 = d_x1 >> ART_UTILE_SHIFT;
d_ty1 = d_y1 >> ART_UTILE_SHIFT;
dofs = (d_ty1 - uta->y0) * uta->width + d_tx1 - uta->x0;
d_xf1 = d_x1 & (ART_UTILE_SIZE - 1);
d_yf1 = d_y1 & (ART_UTILE_SIZE - 1);
d_xf2 = ((d_x2 - 1) & (ART_UTILE_SIZE - 1)) + 1;
d_yf2 = ((d_y2 - 1) & (ART_UTILE_SIZE - 1)) + 1;
if (d_x2 - d_x1 <= ART_UTILE_SIZE - d_xf1) {
if (d_y2 - d_y1 <= ART_UTILE_SIZE - d_yf1) {
if (d_tx1 >= uta->x0 && d_tx1 < uta->x0 + uta->width
&& d_ty1 >= uta->y0 && d_ty1 < uta->y0 + uta->height) {
dbb = utiles[dofs];
if (dbb == 0)
utiles[dofs] = ART_UTA_BBOX_CONS (
d_xf1, d_yf1, d_xf2, d_yf2);
else
utiles[dofs] = ART_UTA_BBOX_CONS (
MIN (ART_UTA_BBOX_X0 (dbb), d_xf1),
MIN (ART_UTA_BBOX_Y0 (dbb), d_yf1),
MAX (ART_UTA_BBOX_X1 (dbb), d_xf2),
MAX (ART_UTA_BBOX_Y1 (dbb), d_yf2));
}
} else {
/* Top tile */
if (d_tx1 >= uta->x0 && d_tx1 < uta->x0 + uta->width
&& d_ty1 >= uta->y0 && d_ty1 < uta->y0 + uta->height) {
dbb = utiles[dofs];
if (dbb == 0)
utiles[dofs] = ART_UTA_BBOX_CONS (
d_xf1, d_yf1, d_xf2, ART_UTILE_SIZE);
else
utiles[dofs] = ART_UTA_BBOX_CONS (
MIN (ART_UTA_BBOX_X0 (dbb), d_xf1),
MIN (ART_UTA_BBOX_Y0 (dbb), d_yf1),
MAX (ART_UTA_BBOX_X1 (dbb), d_xf2),
ART_UTILE_SIZE);
}
dofs += uta->width;
/* Bottom tile */
if (d_tx1 >= uta->x0 && d_tx1 < uta->x0 + uta->width
&& d_ty1 + 1 >= uta->y0 && d_ty1 + 1 < uta->y0 + uta->height) {
dbb = utiles[dofs];
if (dbb == 0)
utiles[dofs] = ART_UTA_BBOX_CONS (
d_xf1, 0, d_xf2, d_yf2);
else
utiles[dofs] = ART_UTA_BBOX_CONS (
MIN (ART_UTA_BBOX_X0 (dbb), d_xf1),
0,
MAX (ART_UTA_BBOX_X1 (dbb), d_xf2),
MAX (ART_UTA_BBOX_Y1 (dbb), d_yf2));
}
}
} else {
if (d_y2 - d_y1 <= ART_UTILE_SIZE - d_yf1) {
/* Left tile */
if (d_tx1 >= uta->x0 && d_tx1 < uta->x0 + uta->width
&& d_ty1 >= uta->y0 && d_ty1 < uta->y0 + uta->height) {
dbb = utiles[dofs];
if (dbb == 0)
utiles[dofs] = ART_UTA_BBOX_CONS (
d_xf1, d_yf1, ART_UTILE_SIZE, d_yf2);
else
utiles[dofs] = ART_UTA_BBOX_CONS (
MIN (ART_UTA_BBOX_X0 (dbb), d_xf1),
MIN (ART_UTA_BBOX_Y0 (dbb), d_yf1),
ART_UTILE_SIZE,
MAX (ART_UTA_BBOX_Y1 (dbb), d_yf2));
}
dofs++;
/* Right tile */
if (d_tx1 + 1 >= uta->x0 && d_tx1 + 1 < uta->x0 + uta->width
&& d_ty1 >= uta->y0 && d_ty1 < uta->y0 + uta->height) {
dbb = utiles[dofs];
if (dbb == 0)
utiles[dofs] = ART_UTA_BBOX_CONS (
0, d_yf1, d_xf2, d_yf2);
else
utiles[dofs] = ART_UTA_BBOX_CONS (
0,
MIN (ART_UTA_BBOX_Y0 (dbb), d_yf1),
MAX (ART_UTA_BBOX_X1 (dbb), d_xf2),
MAX (ART_UTA_BBOX_Y1 (dbb), d_yf2));
}
} else {
/* Top left tile */
if (d_tx1 >= uta->x0 && d_tx1 < uta->x0 + uta->width
&& d_ty1 >= uta->y0 && d_ty1 < uta->y0 + uta->height) {
dbb = utiles[dofs];
if (dbb == 0)
utiles[dofs] = ART_UTA_BBOX_CONS (
d_xf1, d_yf1, ART_UTILE_SIZE, ART_UTILE_SIZE);
else
utiles[dofs] = ART_UTA_BBOX_CONS (
MIN (ART_UTA_BBOX_X0 (dbb), d_xf1),
MIN (ART_UTA_BBOX_Y0 (dbb), d_yf1),
ART_UTILE_SIZE,
ART_UTILE_SIZE);
}
dofs++;
/* Top right tile */
if (d_tx1 + 1 >= uta->x0 && d_tx1 + 1 < uta->x0 + uta->width
&& d_ty1 >= uta->y0 && d_ty1 < uta->y0 + uta->height) {
dbb = utiles[dofs];
if (dbb == 0)
utiles[dofs] = ART_UTA_BBOX_CONS (
0, d_yf1, d_xf2, ART_UTILE_SIZE);
else
utiles[dofs] = ART_UTA_BBOX_CONS (
0,
MIN (ART_UTA_BBOX_Y0 (dbb), d_yf1),
MAX (ART_UTA_BBOX_X1 (dbb), d_xf2),
ART_UTILE_SIZE);
}
dofs += uta->width - 1;
/* Bottom left tile */
if (d_tx1 >= uta->x0 && d_tx1 < uta->x0 + uta->width
&& d_ty1 + 1 >= uta->y0 && d_ty1 + 1 < uta->y0 + uta->height) {
dbb = utiles[dofs];
if (dbb == 0)
utiles[dofs] = ART_UTA_BBOX_CONS (
d_xf1, 0, ART_UTILE_SIZE, d_yf2);
else
utiles[dofs] = ART_UTA_BBOX_CONS (
MIN (ART_UTA_BBOX_X0 (dbb), d_xf1),
0,
ART_UTILE_SIZE,
MAX (ART_UTA_BBOX_Y1 (dbb), d_yf2));
}
dofs++;
/* Bottom right tile */
if (d_tx1 + 1 >= uta->x0 && d_tx1 + 1 < uta->x0 + uta->width
&& d_ty1 + 1 >= uta->y0 && d_ty1 + 1 < uta->y0 + uta->height) {
dbb = utiles[dofs];
if (dbb == 0)
utiles[dofs] = ART_UTA_BBOX_CONS (
0, 0, d_xf2, d_yf2);
else
utiles[dofs] = ART_UTA_BBOX_CONS (
0,
0,
MAX (ART_UTA_BBOX_X1 (dbb), d_xf2),
MAX (ART_UTA_BBOX_Y1 (dbb), d_yf2));
}
}
}
}
/**
* uta_copy_area:
* @uta: A microtile array.
* @src_x: Left coordinate of source rectangle.
* @src_y: Top coordinate of source rectangle.
* @dest_x: Left coordinate of destination.
* @dest_y: Top coordinate of destination.
* @width: Width of region to copy.
* @height: Height of region to copy.
*
* Copies a rectangular region within a microtile array. The array will not be
* expanded if the destination area does not fit within it; rather only the area
* that fits will be copied. The source rectangle must be completely contained
* within the microtile array.
**/
void
uta_copy_area (ArtUta *uta, int src_x, int src_y, int dest_x, int dest_y, int width, int height)
{
ArtUtaBbox *utiles;
int rect_x1, rect_y1, rect_x2, rect_y2;
gboolean top_to_bottom, left_to_right;
int xofs, yofs;
int x, y;
g_return_if_fail (uta != NULL);
g_return_if_fail (width >= 0 && height >= 0);
g_return_if_fail (src_x >= uta->x0 << ART_UTILE_SHIFT);
g_return_if_fail (src_y >= uta->y0 << ART_UTILE_SHIFT);
g_return_if_fail (src_x + width <= (uta->x0 + uta->width) << ART_UTILE_SHIFT);
g_return_if_fail (src_y + height <= (uta->y0 + uta->height) << ART_UTILE_SHIFT);
if ((src_x == dest_x && src_y == dest_y) || width == 0 || height == 0)
return;
/* FIXME: This function is not perfect. It *adds* the copied/offsetted
* area to the original contents of the microtile array, thus growing
* the region more than needed. The effect should be to "overwrite" the
* original contents, just like XCopyArea() does. Care needs to be
* taken when the edges of the rectangle do not fall on microtile
* boundaries, because tiles may need to be "split".
*
* Maybe this will work:
*
* 1. Copy the rectangular array of tiles that form the region to a
* temporary buffer.
*
* 2. uta_remove_rect() the *destination* rectangle from the original
* microtile array.
*
* 3. Copy back the temporary buffer to the original array while
* offsetting it in the same way as copy_tile() does.
*/
utiles = uta->utiles;
rect_x1 = src_x >> ART_UTILE_SHIFT;
rect_y1 = src_y >> ART_UTILE_SHIFT;
rect_x2 = (src_x + width + ART_UTILE_SIZE - 1) >> ART_UTILE_SHIFT;
rect_y2 = (src_y + height + ART_UTILE_SIZE - 1) >> ART_UTILE_SHIFT;
xofs = dest_x - src_x;
yofs = dest_y - src_y;
left_to_right = xofs < 0;
top_to_bottom = yofs < 0;
if (top_to_bottom && left_to_right) {
for (y = rect_y1; y < rect_y2; y++)
for (x = rect_x1; x < rect_x2; x++)
copy_tile (uta, x, y, xofs, yofs);
} else if (top_to_bottom && !left_to_right) {
for (y = rect_y1; y < rect_y2; y++)
for (x = rect_x2 - 1; x >= rect_x1; x--)
copy_tile (uta, x, y, xofs, yofs);
} else if (!top_to_bottom && left_to_right) {
for (y = rect_y2 - 1; y >= rect_y1; y--)
for (x = rect_x1; x < rect_x2; x++)
copy_tile (uta, x, y, xofs, yofs);
} else if (!top_to_bottom && !left_to_right) {
for (y = rect_y2 - 1; y >= rect_y1; y--)
for (x = rect_x2 - 1; x >= rect_x1; x--)
copy_tile (uta, x, y, xofs, yofs);
}
}

View file

@ -35,6 +35,8 @@ void uta_remove_rect (ArtUta *uta, int x1, int y1, int x2, int y2);
void uta_find_first_glom_rect (ArtUta *uta, ArtIRect *rect, int max_width, int max_height);
void uta_copy_area (ArtUta *uta, int src_x, int src_y, int dest_x, int dest_y, int width, int height);
#endif