@@ -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
5958static void
6059tile_init(Tile *tile)
6160{
62- tile->time = tile_ticks++;
6361}
6462
6563static 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 */
8390void
@@ -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 */
9298Tile *
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}
0 commit comments