Skip to content

Commit 8d49130

Browse files
committed
update from nip4
to get the revised image display widget, with faster and prettier updates
1 parent 034afd8 commit 8d49130

File tree

10 files changed

+355
-457
lines changed

10 files changed

+355
-457
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ master
22

33
- fix a ref leak in tilesource_new_from_file()
44
- much better focus indicator in imagedisplay
5+
- update from nip4 again to get faster and prettier repaints
6+
- better complex image display
57

68
## 4.0.0 31/05/25
79

src/gtkutil.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,3 +537,19 @@ weakref_set(GObject **pointer, GObject *object)
537537
}
538538

539539
#define WEAKREF_SET(A, B) weakref_set((GObject **) &(A), (GObject *) (B));
540+
541+
VipsRect *
542+
rect_graphene_to_vips(graphene_rect_t *graphene, VipsRect *vips)
543+
{
544+
// round out to enclosing int area
545+
graphene_rect_t bounds;
546+
graphene_rect_round_extents(graphene, &bounds);
547+
548+
vips->left = bounds.origin.x;
549+
vips->top = bounds.origin.y;
550+
vips->width = bounds.size.width;
551+
vips->height = bounds.size.height;
552+
553+
return vips;
554+
}
555+

src/gtkutil.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ void action_radio(GSimpleAction *action,
8686

8787
int get_dpi(void);
8888

89+
G_DEFINE_AUTOPTR_CLEANUP_FUNC(VipsRect, g_free);
90+
8991
G_DEFINE_AUTOPTR_CLEANUP_FUNC(cairo_t, cairo_destroy)
9092
G_DEFINE_AUTOPTR_CLEANUP_FUNC(cairo_surface_t, cairo_surface_destroy)
9193

@@ -107,3 +109,5 @@ gboolean value_to_filename(const GValue *value,
107109

108110
void weakref_set(GObject **pointer, GObject *object);
109111
#define WEAKREF_SET(A, B) weakref_set((GObject **) &(A), (GObject *) (B));
112+
113+
VipsRect *rect_graphene_to_vips(graphene_rect_t *graphene, VipsRect *vips);

src/imagedisplay.c

Lines changed: 5 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -353,9 +353,8 @@ imagedisplay_tilecache_changed(Tilecache *tilecache,
353353
Imagedisplay *imagedisplay)
354354
{
355355
#ifdef DEBUG
356-
printf("imagedisplay_tilecache_changed: %d x %d\n",
357-
tilecache->tilesource->image_width,
358-
tilecache->tilesource->image_height);
356+
printf("imagedisplay_tilecache_changed: imagedisplay = %p\n",
357+
imagedisplay);
359358
#endif /*DEBUG*/
360359

361360
imagedisplay->image_rect.width = tilecache->tilesource->image_width;
@@ -527,8 +526,7 @@ imagedisplay_set_property(GObject *object,
527526
break;
528527

529528
case PROP_TILESOURCE:
530-
imagedisplay_set_tilesource(imagedisplay,
531-
g_value_get_object(value));
529+
imagedisplay_set_tilesource(imagedisplay, g_value_get_object(value));
532530
break;
533531

534532
case PROP_BESTFIT:
@@ -652,9 +650,6 @@ imagedisplay_snapshot(GtkWidget *widget, GtkSnapshot *snapshot)
652650
GTK_WIDGET_CLASS(imagedisplay_parent_class)->snapshot(widget, snapshot);
653651

654652
#ifdef HAVE_GTK_SNAPSHOT_SET_SNAP
655-
/* Round tile bounds to the closest pixel edge on all sides to prevent
656-
* seams.
657-
*/
658653
gtk_snapshot_set_snap(snapshot, GSK_RECT_SNAP_ROUND);
659654
#endif /*HAVE_GTK_SNAPSHOT_SET_SNAP*/
660655

@@ -672,7 +667,7 @@ imagedisplay_snapshot(GtkWidget *widget, GtkSnapshot *snapshot)
672667
paint.size.height = imagedisplay->paint_rect.height;
673668

674669
if (imagedisplay->tilecache &&
675-
imagedisplay->tilecache->tiles)
670+
imagedisplay->tilecache->n_levels > 0)
676671
tilecache_snapshot(imagedisplay->tilecache, snapshot,
677672
imagedisplay->scale, imagedisplay->x, imagedisplay->y,
678673
&paint, imagedisplay->debug);
@@ -681,26 +676,6 @@ imagedisplay_snapshot(GtkWidget *widget, GtkSnapshot *snapshot)
681676
imagedisplay_overlay_snapshot(imagedisplay, snapshot);
682677

683678
gtk_snapshot_pop(snapshot);
684-
685-
/* It's unclear how to do this :( maybe we're supposed to get the base
686-
* widget class to do it? Draw it ourselves for now.
687-
if (gtk_widget_has_focus(widget)) {
688-
GskRoundedRect outline;
689-
690-
gsk_rounded_rect_init_from_rect(&outline,
691-
&GRAPHENE_RECT_INIT(
692-
3,
693-
3,
694-
gtk_widget_get_width(widget) - 6,
695-
gtk_widget_get_height(widget) - 6),
696-
5);
697-
698-
gtk_snapshot_append_border(snapshot,
699-
&outline,
700-
(float[4]){ 2, 2, 2, 2 },
701-
(GdkRGBA[4]){ BORDER, BORDER, BORDER, BORDER });
702-
}
703-
*/
704679
}
705680

706681
static void
@@ -891,7 +866,7 @@ imagedisplay_new(Tilesource *tilesource)
891866
}
892867

893868
/* image level0 image coordinates ... this is the coordinate space we
894-
* pass down to tilecache
869+
* pass down to tilecache
895870
*
896871
* gtk screen cods, so the coordinates we use to render tiles
897872
*/

src/tile.c

Lines changed: 68 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,13 @@ tile_dispose(GObject *object)
5151

5252
VIPS_UNREF(tile->texture);
5353
VIPS_FREEF(g_bytes_unref, tile->bytes);
54-
VIPS_UNREF(tile->region);
5554

5655
G_OBJECT_CLASS(tile_parent_class)->dispose(object);
5756
}
5857

5958
static void
6059
tile_init(Tile *tile)
6160
{
62-
tile->time = tile_ticks++;
6361
}
6462

6563
static void
@@ -78,6 +76,15 @@ tile_get_time(void)
7876
return tile_ticks;
7977
}
8078

79+
/* The pixels in the region have changed. We must regenerate the texture on
80+
* next use.
81+
*/
82+
void
83+
tile_invalidate(Tile *tile)
84+
{
85+
tile->valid = FALSE;
86+
}
87+
8188
/* Update the timestamp on a tile.
8289
*/
8390
void
@@ -86,103 +93,84 @@ tile_touch(Tile *tile)
8693
tile->time = tile_ticks++;
8794
}
8895

89-
/* Make a tile on an image. left/top in this image's coordinates (not level0
90-
* coordinates).
96+
/* Make a tile on an image. left/top are in level0 coordinates.
9197
*/
9298
Tile *
93-
tile_new(VipsImage *level, int left, int top, int z)
99+
tile_new(int left, int top, int z)
94100
{
95101
g_autoptr(Tile) tile = g_object_new(TYPE_TILE, NULL);
96102

97-
VipsRect tile_bounds;
98-
VipsRect image_bounds;
99-
100-
tile->region = vips_region_new(level);
101103
tile->z = z;
102-
103-
image_bounds.left = 0;
104-
image_bounds.top = 0;
105-
image_bounds.width = level->Xsize;
106-
image_bounds.height = level->Ysize;
107-
tile_bounds.left = left;
108-
tile_bounds.top = top;
109-
tile_bounds.width = TILE_SIZE;
110-
tile_bounds.height = TILE_SIZE;
111-
vips_rect_intersectrect(&image_bounds, &tile_bounds, &tile_bounds);
112-
if (vips_region_buffer(tile->region, &tile_bounds))
113-
return NULL;
114-
115-
/* Tile bounds in level 0 coordinates.
116-
*/
117-
tile->bounds.left = tile_bounds.left << z;
118-
tile->bounds.top = tile_bounds.top << z;
119-
tile->bounds.width = tile_bounds.width << z;
120-
tile->bounds.height = tile_bounds.height << z;
104+
tile->bounds.left = left >> z;
105+
tile->bounds.top = top >> z;
106+
tile->bounds.width = TILE_SIZE;
107+
tile->bounds.height = TILE_SIZE;
108+
tile->bounds0.left = left;
109+
tile->bounds0.top = top;
110+
tile->bounds0.width = TILE_SIZE << z;
111+
tile->bounds0.height = TILE_SIZE << z;
112+
tile->valid = FALSE;
121113

122114
tile_touch(tile);
123115

124116
return g_steal_pointer(&tile);
125117
}
126118

127-
/* NULL means pixels have not arrived from libvips yet.
119+
/* Set the texture from the pixels in a VipsRegion. The region can be less
120+
* than TILE_SIZE x TILE_SIZE for edge tiles, and can be RGB or RGBA (we
121+
* always make full size RGBA textures).
128122
*/
129-
GdkTexture *
130-
tile_get_texture(Tile *tile)
123+
void
124+
tile_set_texture(Tile *tile, VipsRegion *region)
131125
{
132-
/* This mustn't be a completely empty tile -- there must be either
133-
* fresh, valid pixels, or an old texture.
134-
*/
135-
g_assert(tile->texture ||
136-
tile->valid);
137-
138-
/* The tile is being shown, so it must be useful.
139-
*/
140-
tile_touch(tile);
126+
g_assert(region->valid.left == tile->bounds.left);
127+
g_assert(region->valid.top == tile->bounds.top);
128+
g_assert(region->valid.width <= tile->bounds.width);
129+
g_assert(region->valid.height <= tile->bounds.height);
130+
g_assert(region->im->Bands == 3 || region->im->Bands == 4);
131+
g_assert(region->im->BandFmt == VIPS_FORMAT_UCHAR);
132+
g_assert(region->im->Type == VIPS_INTERPRETATION_sRGB);
133+
134+
// textures are immutable, we we must always reallocate, we can't update
135+
VIPS_FREEF(g_bytes_unref, tile->bytes);
136+
VIPS_UNREF(tile->texture);
141137

142-
/* It's three steps to make the texture:
143-
*
144-
* 1. We must make a copy of the pixel data from libvips, to stop
145-
* it being changed under our feet.
146-
*
147-
* 2. Wrap a GBytes around that copy.
148-
*
149-
* 3. Tag it as a texture that may need upload to the GPU.
150-
*/
151-
if (!tile->texture) {
152-
gpointer copy = g_memdup2(
153-
VIPS_REGION_ADDR(tile->region,
154-
tile->region->valid.left,
155-
tile->region->valid.top),
156-
VIPS_REGION_SIZEOF_LINE(tile->region) *
157-
tile->region->valid.height);
158-
159-
VIPS_FREEF(g_bytes_unref, tile->bytes);
160-
tile->bytes = g_bytes_new_take(
161-
copy,
162-
VIPS_REGION_SIZEOF_LINE(tile->region) *
163-
tile->region->valid.height);
164-
165-
tile->texture = gdk_memory_texture_new(
166-
tile->region->valid.width,
167-
tile->region->valid.height,
168-
tile->region->im->Bands == 4
169-
? GDK_MEMORY_R8G8B8A8
170-
: GDK_MEMORY_R8G8B8,
171-
tile->bytes,
172-
VIPS_REGION_LSKIP(tile->region));
138+
// always a full tile of RGBA pixels
139+
gsize length = TILE_SIZE * TILE_SIZE * 4;
140+
unsigned char *data = g_malloc0(length);
141+
142+
for (int y = 0; y < region->valid.height; y++) {
143+
VipsPel *p =
144+
VIPS_REGION_ADDR(region, region->valid.left, region->valid.top + y);
145+
VipsPel *q = data + 4 * TILE_SIZE * y;
146+
147+
if (region->im->Bands == 4)
148+
memcpy(q, p, VIPS_REGION_SIZEOF_LINE(region));
149+
else
150+
// RGB to RGBA
151+
for (int x = 0; x < region->valid.width; x++) {
152+
q[0] = p[0];
153+
q[1] = p[1];
154+
q[2] = p[2];
155+
q[3] = 255;
156+
157+
q += 4;
158+
p += 3;
159+
}
173160
}
174161

175-
return tile->texture;
162+
tile->bytes = g_bytes_new_take(data, length);
163+
tile->texture = gdk_memory_texture_new(TILE_SIZE, TILE_SIZE,
164+
GDK_MEMORY_R8G8B8A8, tile->bytes, 4 * TILE_SIZE);
165+
166+
tile->valid = TRUE;
167+
tile_touch(tile);
176168
}
177169

178-
/* The pixels in the region have changed. We must regenerate the texture on
179-
* next use.
180-
*/
181-
void
182-
tile_free_texture(Tile *tile)
170+
GdkTexture *
171+
tile_get_texture(Tile *tile)
183172
{
184-
g_assert(tile->valid);
173+
tile_touch(tile);
185174

186-
VIPS_UNREF(tile->texture);
187-
VIPS_FREEF(g_bytes_unref, tile->bytes);
175+
return tile->texture;
188176
}

src/tile.h

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -49,34 +49,21 @@ typedef struct _Tile {
4949
*/
5050
guint time;
5151

52-
/* RGB or RGBA pixels coming in from libvips. A memory region, with
53-
* data copied in from the end of the pipeline.
54-
*/
55-
VipsRegion *region;
56-
5752
/* The z layer the tile sits at.
5853
*/
5954
int z;
6055

61-
/* The tile rect, in level 0 coordinates. region->valid is the rect in
62-
* level z coordinates.
56+
/* The tile rect, in leval coordinates and in level0 coordinates.
6357
*/
6458
VipsRect bounds;
59+
VipsRect bounds0;
6560

66-
/* TRUE if the region contains real pixels from the image. FALSE if
67-
* eg. we're waiting for computation.
61+
/* TRUE if we think the texture is up to date.
6862
*/
6963
gboolean valid;
7064

71-
/* Pixels going out to the scene graph.
72-
*
73-
* bytes and texture won't make a copy of the data, so we must make a
74-
* copy ourselves, in case a later fetch from the same region produces
75-
* invalid data.
76-
*/
7765
GBytes *bytes;
78-
GdkTexture *texture;
79-
66+
GdkTexture *texture;
8067
} Tile;
8168

8269
typedef struct _TileClass {
@@ -88,25 +75,20 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(Tile, g_object_unref)
8875

8976
GType tile_get_type(void);
9077

91-
/* Get the current time.
92-
*/
9378
int tile_get_time(void);
79+
void tile_invalidate(Tile *tile);
80+
void tile_touch(Tile *tile);
9481

95-
/* Update the time on a tile.
82+
/* Make a new tile on the level. left and top are in level0 coordinates.
9683
*/
97-
void tile_touch(Tile *tile);
84+
Tile *tile_new(int left, int top, int z);
9885

99-
/* Make a new tile on the level.
86+
/* Set the texture from the data on a region.
10087
*/
101-
Tile *tile_new(VipsImage *level, int x, int y, int z);
88+
void tile_set_texture(Tile *tile, VipsRegion *region);
10289

10390
/* texture lifetime run by tile ... don't unref.
10491
*/
10592
GdkTexture *tile_get_texture(Tile *tile);
10693

107-
/* Free the texture to force regeneration on next use. Call this if the region
108-
* changes.
109-
*/
110-
void tile_free_texture(Tile *tile);
111-
11294
#endif /*__TILE_H*/

0 commit comments

Comments
 (0)