1 /* This file is part of GEGL.
2 *
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 3 of the License, or (at your option) any later version.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
15 *
16 * Copyright 2006, 2007 Øyvind Kolås <pippin@gimp.org>
17 */
18
19 #include "config.h"
20
21 #include <string.h>
22 #include <errno.h>
23
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28
29 #include "gegl-buffer.h"
30 #include "gegl-buffer-private.h"
31 #include "gegl-buffer-index.h"
32 #include "gegl-debug.h"
33
34 #include <glib/gprintf.h>
35 #include <glib/gstdio.h>
36
37 #ifdef G_OS_WIN32
38 #define BINARY_FLAG O_BINARY
39 #else
40 #define BINARY_FLAG 0
41 #endif
42
43 typedef struct
44 {
45 GeglBufferHeader header;
46 GList *tiles;
47 gchar *path;
48 int i;
49 gint tile_size;
50 const Babl *format;
51 goffset offset;
52 goffset next_block;
53 gboolean got_header;
54 } LoadInfo;
55
seekto(LoadInfo * info,gint offset)56 static void seekto(LoadInfo *info, gint offset)
57 {
58 info->offset = offset;
59 GEGL_NOTE (GEGL_DEBUG_BUFFER_LOAD, "seek to %i", offset);
60 if(lseek (info->i, info->offset, SEEK_SET) == -1)
61 {
62 g_warning ("failed seeking");
63 }
64 }
65
66 static void
load_info_destroy(LoadInfo * info)67 load_info_destroy (LoadInfo *info)
68 {
69 if (!info)
70 return;
71 if (info->path)
72 g_free (info->path);
73 if (info->i != -1)
74 close (info->i);
75 if (info->tiles != NULL)
76 {
77 GList *iter;
78 for (iter = info->tiles; iter; iter = iter->next)
79 {
80 g_free (iter->data);
81 }
82 g_list_free (info->tiles);
83 info->tiles = NULL;
84 }
85 g_slice_free (LoadInfo, info);
86 }
87
88 GeglBufferItem *
gegl_buffer_read_header(int i,goffset * offset)89 gegl_buffer_read_header (int i,
90 goffset *offset)
91 {
92 goffset placeholder;
93 GeglBufferItem *ret;
94 if (offset==0)
95 offset = &placeholder;
96
97 if(lseek(i, 0, SEEK_SET) == -1)
98 g_warning ("failed seeking to %i", 0);
99 *offset = 0;
100
101 ret = g_malloc (sizeof (GeglBufferHeader));
102 {
103 ssize_t sz_read = read(i, ret, sizeof(GeglBufferHeader));
104 if (sz_read != -1)
105 *offset += sz_read;
106 }
107
108 GEGL_NOTE (GEGL_DEBUG_BUFFER_LOAD, "read header: tile-width: %i tile-height: %i next:%i %ix%i\n",
109 ret->header.tile_width,
110 ret->header.tile_height,
111 (guint)ret->block.next,
112 ret->header.width,
113 ret->header.height);
114
115 if (!(ret->header.magic[0]=='G' &&
116 ret->header.magic[1]=='E' &&
117 ret->header.magic[2]=='G' &&
118 ret->header.magic[3]=='L'))
119 {
120 g_warning ("Magic is wrong! %s", ret->header.magic);
121 }
122
123 return ret;
124 }
125
126 /* reads a block of data from geglbuffer in open file descriptor fd
127 */
read_block(int fd,goffset * offset)128 static GeglBufferItem *read_block (int fd,
129 goffset *offset)
130 {
131 GeglBufferBlock block;
132 GeglBufferItem *ret;
133 gsize byte_read = 0;
134 gint own_size=0;
135
136 g_assert (offset);
137
138 if (*offset==0)
139 return NULL;
140
141 if(lseek(fd, *offset, SEEK_SET) == -1)
142 g_warning ("failed seeking to %i", (gint)*offset);
143
144 {
145 ssize_t sz_read = read (fd, &block, sizeof (GeglBufferBlock));
146 if(sz_read != -1)
147 byte_read += sz_read;
148 }
149 GEGL_NOTE (GEGL_DEBUG_BUFFER_LOAD, "read block: length:%i next:%i",
150 block.length, (guint)block.next);
151
152 switch (block.flags)
153 {
154 case GEGL_FLAG_TILE:
155 case GEGL_FLAG_FREE_TILE:
156 own_size = sizeof (GeglBufferTile);
157 break;
158 default:
159 g_warning ("skipping unknown type of entry flags=%i", block.flags);
160 break;
161 }
162
163 if (block.length != own_size)
164 {
165 GEGL_NOTE(GEGL_DEBUG_BUFFER_LOAD, "read block of size %i which is different from expected %i only using available expected",
166 block.length, own_size);
167 }
168
169 if (block.length == own_size ||
170 block.length > own_size )
171 {
172 /* we discard any excess information that might have been added in later
173 * versions
174 */
175 ret = g_malloc (own_size);
176 memcpy (ret, &block, sizeof (GeglBufferBlock));
177 {
178 ssize_t sz_read = read (fd, ((gchar*)ret) + sizeof(GeglBufferBlock),
179 own_size - sizeof(GeglBufferBlock));
180 if(sz_read != -1)
181 byte_read += sz_read;
182 }
183 ret->block.length = own_size;
184 }
185 else if (block.length < own_size)
186 {
187 ret = g_malloc (own_size);
188 memcpy (ret, &block, sizeof (GeglBufferBlock));
189 {
190 ssize_t sz_read = read (fd, ret + sizeof(GeglBufferBlock),
191 block.length - sizeof (GeglBufferBlock));
192 if(sz_read != -1)
193 byte_read += sz_read;
194 }
195 ret->block.length = own_size;
196 }
197 else
198 {
199 ret = NULL;
200 g_warning ("skipping block : of flags:%i\n", block.flags);
201 }
202
203 *offset += byte_read;
204 return ret;
205 }
206
207 GList *
gegl_buffer_read_index(int i,goffset * offset)208 gegl_buffer_read_index (int i,
209 goffset *offset)
210 /* load the index */
211 {
212 GList *ret = NULL;
213 GeglBufferItem *item;
214
215 for (item = read_block (i, offset); item; item = read_block (i, offset))
216 {
217 g_assert (item);
218 GEGL_NOTE (GEGL_DEBUG_BUFFER_LOAD,"loaded item: %i, %i, %i offset:%i next:%i", item->tile.x,
219 item->tile.y,
220 item->tile.z,
221 (guint)item->tile.offset,
222 (guint)item->block.next);
223 *offset = item->block.next;
224 ret = g_list_prepend (ret, item);
225 }
226 ret = g_list_reverse (ret);
227 return ret;
228 }
229
230
sanity(void)231 static void sanity(void) { GEGL_BUFFER_SANITY; }
232
233
234 GeglBuffer *
gegl_buffer_open(const gchar * path)235 gegl_buffer_open (const gchar *path)
236 {
237 sanity();
238
239 return g_object_new (GEGL_TYPE_BUFFER,
240 /* FIXME: Currently the buffer must always have a format specified,
241 this format will be used if the path did not point to an
242 existing file. */
243 "format", babl_format ("RGBA float"),
244 "path", path,
245 NULL);
246 }
247
248 GeglBuffer *
gegl_buffer_load(const gchar * path)249 gegl_buffer_load (const gchar *path)
250 {
251 GeglBuffer *ret;
252
253 LoadInfo *info = g_slice_new0 (LoadInfo);
254
255 info->path = g_strdup (path);
256 info->i = g_open (info->path, O_RDONLY|BINARY_FLAG, 0770);
257 GEGL_NOTE (GEGL_DEBUG_BUFFER_LOAD, "starting to load buffer %s", path);
258 if (info->i == -1)
259 {
260 GEGL_NOTE (GEGL_DEBUG_BUFFER_LOAD, "failed top open %s for reading", path);
261 return NULL;
262 }
263
264 {
265 GeglBufferItem *header = gegl_buffer_read_header (info->i, &info->offset);
266 g_assert (header);
267 info->header = header->header;
268 info->offset = info->header.next;
269 g_free (header);
270 }
271
272
273
274 info->tile_size = info->header.tile_width *
275 info->header.tile_height *
276 info->header.bytes_per_pixel;
277 info->format = babl_format (info->header.description);
278
279 ret = g_object_new (GEGL_TYPE_BUFFER,
280 "format", info->format,
281 "tile-width", info->header.tile_width,
282 "tile-height", info->header.tile_height,
283 "height", info->header.height,
284 "width", info->header.width,
285 NULL);
286
287 /* sanity check, should probably report error condition and return safely instead
288 */
289 g_assert (babl_format_get_bytes_per_pixel (info->format) == info->header.bytes_per_pixel);
290
291 info->tiles = gegl_buffer_read_index (info->i, &info->offset);
292
293 /* load each tile */
294 {
295 GList *iter;
296 gint i = 0;
297 for (iter = info->tiles; iter; iter = iter->next)
298 {
299 GeglBufferTile *entry = iter->data;
300 guchar *data;
301 GeglTile *tile;
302
303
304 tile = gegl_tile_source_get_tile (GEGL_TILE_SOURCE (ret),
305 entry->x,
306 entry->y,
307 entry->z);
308
309 if (info->offset != entry->offset)
310 {
311 seekto (info, entry->offset);
312 }
313 /*g_assert (info->offset == entry->offset);*/
314
315
316 g_assert (tile);
317 gegl_tile_lock (tile);
318
319 data = gegl_tile_get_data (tile);
320 g_assert (data);
321
322 {
323 ssize_t sz_read = read (info->i, data, info->tile_size);
324 if(sz_read != -1)
325 info->offset += sz_read;
326 }
327 /*g_assert (info->offset == entry->offset + info->tile_size);*/
328
329 gegl_tile_unlock (tile);
330 gegl_tile_unref (tile);
331 i++;
332 }
333 GEGL_NOTE (GEGL_DEBUG_BUFFER_LOAD, "%i tiles loaded",i);
334 }
335 GEGL_NOTE (GEGL_DEBUG_BUFFER_LOAD, "buffer loaded %s", info->path);
336
337 load_info_destroy (info);
338 return ret;
339 }
340