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