Blame gegl/buffer/gegl-buffer-save.c

Packit Service 2781ba
/* This file is part of GEGL.
Packit Service 2781ba
 *
Packit Service 2781ba
 * This library is free software; you can redistribute it and/or
Packit Service 2781ba
 * modify it under the terms of the GNU Lesser General Public
Packit Service 2781ba
 * License as published by the Free Software Foundation; either
Packit Service 2781ba
 * version 3 of the License, or (at your option) any later version.
Packit Service 2781ba
 *
Packit Service 2781ba
 * This library is distributed in the hope that it will be useful,
Packit Service 2781ba
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 2781ba
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 2781ba
 * Lesser General Public License for more details.
Packit Service 2781ba
 *
Packit Service 2781ba
 * You should have received a copy of the GNU Lesser General Public
Packit Service 2781ba
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
Packit Service 2781ba
 *
Packit Service 2781ba
 * Copyright 2006, 2007 Øyvind Kolås <pippin@gimp.org>
Packit Service 2781ba
 */
Packit Service 2781ba
Packit Service 2781ba
#include "config.h"
Packit Service 2781ba
Packit Service 2781ba
#include <string.h>
Packit Service 2781ba
Packit Service 2781ba
#include <sys/types.h>
Packit Service 2781ba
#include <sys/stat.h>
Packit Service 2781ba
#include <fcntl.h>
Packit Service 2781ba
#include <unistd.h>
Packit Service 2781ba
#include <errno.h>
Packit Service 2781ba
Packit Service 2781ba
#include <glib-object.h>
Packit Service 2781ba
Packit Service 2781ba
#include "gegl-types-internal.h"
Packit Service 2781ba
Packit Service 2781ba
#include "gegl.h"
Packit Service 2781ba
#include "gegl-buffer-types.h"
Packit Service 2781ba
#include "gegl-buffer.h"
Packit Service 2781ba
#include "gegl-buffer-private.h"
Packit Service 2781ba
#include "gegl-debug.h"
Packit Service 2781ba
#include "gegl-tile-storage.h"
Packit Service 2781ba
#include "gegl-tile-backend.h"
Packit Service 2781ba
#include "gegl-tile-handler.h"
Packit Service 2781ba
#include "gegl-tile.h"
Packit Service 2781ba
#include "gegl-tile-handler-cache.h"
Packit Service 2781ba
#include "gegl-tile-handler-log.h"
Packit Service 2781ba
#include "gegl-tile-handler-empty.h"
Packit Service 2781ba
#include "gegl-types-internal.h"
Packit Service 2781ba
#include "gegl-utils.h"
Packit Service 2781ba
#include "gegl-buffer-save.h"
Packit Service 2781ba
#include "gegl-buffer-index.h"
Packit Service 2781ba
Packit Service 2781ba
typedef struct
Packit Service 2781ba
{
Packit Service 2781ba
  GeglBufferHeader header;
Packit Service 2781ba
  GList           *tiles;
Packit Service 2781ba
  gchar           *path;
Packit Service 2781ba
  int             o;
Packit Service 2781ba
Packit Service 2781ba
  gint             tile_size;
Packit Service 2781ba
  gint             offset;
Packit Service 2781ba
  gint             entry_count;
Packit Service 2781ba
  GeglBufferBlock *in_holding; /* we need to write one block added behind
Packit Service 2781ba
                                * to be able to recompute the forward pointing
Packit Service 2781ba
                                * link from one entry to the next.
Packit Service 2781ba
                                */
Packit Service 2781ba
} SaveInfo;
Packit Service 2781ba
Packit Service 2781ba
Packit Service 2781ba
GeglBufferTile *
Packit Service 2781ba
gegl_tile_entry_new (gint x,
Packit Service 2781ba
                     gint y,
Packit Service 2781ba
                     gint z)
Packit Service 2781ba
{
Packit Service 2781ba
  GeglBufferTile *entry = g_malloc0 (sizeof(GeglBufferTile));
Packit Service 2781ba
  entry->block.flags = GEGL_FLAG_TILE;
Packit Service 2781ba
  entry->block.length = sizeof (GeglBufferTile);
Packit Service 2781ba
Packit Service 2781ba
  entry->x = x;
Packit Service 2781ba
  entry->y = y;
Packit Service 2781ba
  entry->z = z;
Packit Service 2781ba
  return entry;
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
void
Packit Service 2781ba
gegl_tile_entry_destroy (GeglBufferTile *entry)
Packit Service 2781ba
{
Packit Service 2781ba
  g_free (entry);
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
static gsize write_block (SaveInfo        *info,
Packit Service 2781ba
                          GeglBufferBlock *block)
Packit Service 2781ba
{
Packit Service 2781ba
   gssize ret = 0;
Packit Service 2781ba
Packit Service 2781ba
   if (info->in_holding)
Packit Service 2781ba
     {
Packit Service 2781ba
       glong allocated_pos = info->offset + info->in_holding->length;
Packit Service 2781ba
       info->in_holding->next = allocated_pos;
Packit Service 2781ba
Packit Service 2781ba
       if (block == NULL)
Packit Service 2781ba
         info->in_holding->next = 0;
Packit Service 2781ba
Packit Service 2781ba
       ret = write (info->o, info->in_holding, info->in_holding->length);
Packit Service 2781ba
	   if (ret == -1)
Packit Service 2781ba
         ret = 0;
Packit Service 2781ba
       info->offset += ret;
Packit Service 2781ba
       g_assert (allocated_pos == info->offset);
Packit Service 2781ba
     }
Packit Service 2781ba
  /* write block should also allocate the block and update the
Packit Service 2781ba
   * previously added blocks next pointer
Packit Service 2781ba
   */
Packit Service 2781ba
   info->in_holding = block;
Packit Service 2781ba
   return ret;
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
static void
Packit Service 2781ba
save_info_destroy (SaveInfo *info)
Packit Service 2781ba
{
Packit Service 2781ba
  if (!info)
Packit Service 2781ba
    return;
Packit Service 2781ba
  if (info->path)
Packit Service 2781ba
    g_free (info->path);
Packit Service 2781ba
  if (info->o != -1)
Packit Service 2781ba
    close (info->o);
Packit Service 2781ba
  if (info->tiles != NULL)
Packit Service 2781ba
    {
Packit Service 2781ba
      GList *iter;
Packit Service 2781ba
      for (iter = info->tiles; iter; iter = iter->next)
Packit Service 2781ba
        gegl_tile_entry_destroy (iter->data);
Packit Service 2781ba
      g_list_free (info->tiles);
Packit Service 2781ba
      info->tiles = NULL;
Packit Service 2781ba
    }
Packit Service 2781ba
  g_slice_free (SaveInfo, info);
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
Packit Service 2781ba
Packit Service 2781ba
static glong z_order (const GeglBufferTile *entry)
Packit Service 2781ba
{
Packit Service 2781ba
  glong value;
Packit Service 2781ba
Packit Service 2781ba
  gint  i;
Packit Service 2781ba
  gint  srcA = entry->x;
Packit Service 2781ba
  gint  srcB = entry->y;
Packit Service 2781ba
  gint  srcC = entry->z;
Packit Service 2781ba
Packit Service 2781ba
  /* interleave the 10 least significant bits of all coordinates,
Packit Service 2781ba
   * this gives us Z-order / morton order of the space and should
Packit Service 2781ba
   * work well as a hash
Packit Service 2781ba
   */
Packit Service 2781ba
  value = 0;
Packit Service 2781ba
  for (i = 20; i >= 0; i--)
Packit Service 2781ba
    {
Packit Service 2781ba
#define ADD_BIT(bit)    do { value |= (((bit) != 0) ? 1 : 0); value <<= 1; \
Packit Service 2781ba
    } \
Packit Service 2781ba
  while (0)
Packit Service 2781ba
      ADD_BIT (srcA & (1 << i));
Packit Service 2781ba
      ADD_BIT (srcB & (1 << i));
Packit Service 2781ba
      ADD_BIT (srcC & (1 << i));
Packit Service 2781ba
#undef ADD_BIT
Packit Service 2781ba
    }
Packit Service 2781ba
  return value;
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
static gint z_order_compare (gconstpointer a,
Packit Service 2781ba
                             gconstpointer b)
Packit Service 2781ba
{
Packit Service 2781ba
  const GeglBufferTile *entryA = a;
Packit Service 2781ba
  const GeglBufferTile *entryB = b;
Packit Service 2781ba
Packit Service 2781ba
  return z_order (entryB) - z_order (entryA);
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
Packit Service 2781ba
void
Packit Service 2781ba
gegl_buffer_header_init (GeglBufferHeader *header,
Packit Service 2781ba
                         gint              tile_width,
Packit Service 2781ba
                         gint              tile_height,
Packit Service 2781ba
                         gint              bpp,
Packit Service 2781ba
                         const Babl*       format)
Packit Service 2781ba
{
Packit Service 2781ba
  memcpy (header->magic, "GEGL", 4);
Packit Service 2781ba
Packit Service 2781ba
  header->flags = GEGL_FLAG_HEADER;
Packit Service 2781ba
  header->tile_width  = tile_width;
Packit Service 2781ba
  header->tile_height = tile_height;
Packit Service 2781ba
  header->bytes_per_pixel = bpp;
Packit Service 2781ba
  {
Packit Service 2781ba
    gchar buf[64] = { 0, };
Packit Service 2781ba
Packit Service 2781ba
    g_snprintf (buf, 64, "%s%c\n%i×%i %ibpp\n%ix%i\n\n\n\n\n\n\n\n\n",
Packit Service 2781ba
          babl_get_name (format), 0,
Packit Service 2781ba
          header->tile_width,
Packit Service 2781ba
          header->tile_height,
Packit Service 2781ba
          header->bytes_per_pixel,
Packit Service 2781ba
          (gint)header->width,
Packit Service 2781ba
          (gint)header->height);
Packit Service 2781ba
    memcpy ((header->description), buf, 64);
Packit Service 2781ba
  }
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
void
Packit Service 2781ba
gegl_buffer_save (GeglBuffer          *buffer,
Packit Service 2781ba
                  const gchar         *path,
Packit Service 2781ba
                  const GeglRectangle *roi)
Packit Service 2781ba
{
Packit Service 2781ba
  SaveInfo *info = g_slice_new0 (SaveInfo);
Packit Service 2781ba
Packit Service 2781ba
  glong prediction = 0;
Packit Service 2781ba
  gint bpp;
Packit Service 2781ba
  gint tile_width;
Packit Service 2781ba
  gint tile_height;
Packit Service 2781ba
Packit Service 2781ba
  GEGL_BUFFER_SANITY;
Packit Service 2781ba
Packit Service 2781ba
  if (! roi)
Packit Service 2781ba
    roi = &buffer->extent;
Packit Service 2781ba
Packit Service 2781ba
  GEGL_NOTE (GEGL_DEBUG_BUFFER_SAVE,
Packit Service 2781ba
             "starting to save buffer %s, roi: %d,%d %dx%d",
Packit Service 2781ba
             path, roi->x, roi->y, roi->width, roi->height);
Packit Service 2781ba
Packit Service 2781ba
  /* a header should follow the same structure as a blockdef with
Packit Service 2781ba
   * respect to the flags and next offsets, thus this is a valid
Packit Service 2781ba
   * cast shortcut.
Packit Service 2781ba
   */
Packit Service 2781ba
Packit Service 2781ba
  info->path = g_strdup (path);
Packit Service 2781ba
Packit Service 2781ba
#ifndef G_OS_WIN32
Packit Service 2781ba
  info->o    = open (info->path, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
Packit Service 2781ba
#else
Packit Service 2781ba
  info->o    = open (info->path, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
Packit Service 2781ba
#endif
Packit Service 2781ba
Packit Service 2781ba
Packit Service 2781ba
  if (info->o == -1)
Packit Service 2781ba
    g_warning ("%s: Could not open '%s': %s", G_STRFUNC, info->path, g_strerror(errno));
Packit Service 2781ba
  tile_width  = buffer->tile_storage->tile_width;
Packit Service 2781ba
  tile_height = buffer->tile_storage->tile_height;
Packit Service 2781ba
  g_object_get (buffer, "px-size", &bpp, NULL);
Packit Service 2781ba
Packit Service 2781ba
  info->header.x           = roi->x;
Packit Service 2781ba
  info->header.y           = roi->y;
Packit Service 2781ba
  info->header.width       = roi->width;
Packit Service 2781ba
  info->header.height      = roi->height;
Packit Service 2781ba
  gegl_buffer_header_init (&info->header,
Packit Service 2781ba
                           tile_width,
Packit Service 2781ba
                           tile_height,
Packit Service 2781ba
                           bpp,
Packit Service 2781ba
                           buffer->tile_storage->format
Packit Service 2781ba
                           );
Packit Service 2781ba
  info->header.next = (prediction += sizeof (GeglBufferHeader));
Packit Service 2781ba
  info->tile_size = tile_width * tile_height * bpp;
Packit Service 2781ba
Packit Service 2781ba
  g_assert (info->tile_size % 16 == 0);
Packit Service 2781ba
Packit Service 2781ba
  GEGL_NOTE (GEGL_DEBUG_BUFFER_SAVE,
Packit Service 2781ba
             "collecting list of tiles to be written");
Packit Service 2781ba
  {
Packit Service 2781ba
    gint z;
Packit Service 2781ba
    gint factor = 1;
Packit Service 2781ba
    int  bufy = roi->y;
Packit Service 2781ba
Packit Service 2781ba
    for (z = 0; z < 1; z++)
Packit Service 2781ba
      {
Packit Service 2781ba
        bufy = roi->y;
Packit Service 2781ba
        while (bufy < roi->y + roi->height)
Packit Service 2781ba
          {
Packit Service 2781ba
            gint tiledy  = roi->y + bufy;
Packit Service 2781ba
            gint offsety = gegl_tile_offset (tiledy, tile_height);
Packit Service 2781ba
            gint bufx    = roi->x;
Packit Service 2781ba
Packit Service 2781ba
            while (bufx < roi->x + roi->width)
Packit Service 2781ba
              {
Packit Service 2781ba
                gint tiledx  = roi->x + bufx;
Packit Service 2781ba
                gint offsetx = gegl_tile_offset (tiledx, tile_width);
Packit Service 2781ba
Packit Service 2781ba
                gint tx = gegl_tile_indice (tiledx / factor, tile_width);
Packit Service 2781ba
                gint ty = gegl_tile_indice (tiledy / factor, tile_height);
Packit Service 2781ba
Packit Service 2781ba
                if (gegl_tile_source_exist (GEGL_TILE_SOURCE (buffer), tx, ty, z))
Packit Service 2781ba
                  {
Packit Service 2781ba
                    GeglBufferTile *entry;
Packit Service 2781ba
Packit Service 2781ba
                    GEGL_NOTE (GEGL_DEBUG_BUFFER_SAVE,
Packit Service 2781ba
                               "Found tile to save, tx, ty, z = %d, %d, %d",
Packit Service 2781ba
                               tx, ty, z);
Packit Service 2781ba
Packit Service 2781ba
                    entry = gegl_tile_entry_new (tx, ty, z);
Packit Service 2781ba
                    info->tiles = g_list_prepend (info->tiles, entry);
Packit Service 2781ba
                    info->entry_count++;
Packit Service 2781ba
                  }
Packit Service 2781ba
                bufx += (tile_width - offsetx) * factor;
Packit Service 2781ba
              }
Packit Service 2781ba
            bufy += (tile_height - offsety) * factor;
Packit Service 2781ba
          }
Packit Service 2781ba
        factor *= 2;
Packit Service 2781ba
      }
Packit Service 2781ba
  GEGL_NOTE (GEGL_DEBUG_BUFFER_SAVE,
Packit Service 2781ba
             "size of list of tiles to be written: %d",
Packit Service 2781ba
             g_list_length (info->tiles));
Packit Service 2781ba
  }
Packit Service 2781ba
Packit Service 2781ba
  /* sort the list of tiles into zorder */
Packit Service 2781ba
  info->tiles = g_list_sort (info->tiles, z_order_compare);
Packit Service 2781ba
Packit Service 2781ba
  /* set the offset in the file each tile will be stored on */
Packit Service 2781ba
  {
Packit Service 2781ba
    GList *iter;
Packit Service 2781ba
    gint   predicted_offset = sizeof (GeglBufferHeader) +
Packit Service 2781ba
                              sizeof (GeglBufferTile) * (info->entry_count);
Packit Service 2781ba
    for (iter = info->tiles; iter; iter = iter->next)
Packit Service 2781ba
      {
Packit Service 2781ba
        GeglBufferTile *entry = iter->data;
Packit Service 2781ba
        entry->block.next = iter->next?
Packit Service 2781ba
                            (prediction += sizeof (GeglBufferTile)):0;
Packit Service 2781ba
        entry->offset = predicted_offset;
Packit Service 2781ba
        predicted_offset += info->tile_size;
Packit Service 2781ba
      }
Packit Service 2781ba
  }
Packit Service 2781ba
Packit Service 2781ba
  /* save the header */
Packit Service 2781ba
  {
Packit Service 2781ba
    ssize_t ret = write (info->o, &info->header, sizeof (GeglBufferHeader));
Packit Service 2781ba
	if (ret != -1)
Packit Service 2781ba
      info->offset += ret;
Packit Service 2781ba
  }
Packit Service 2781ba
  g_assert (info->offset == info->header.next);
Packit Service 2781ba
Packit Service 2781ba
  /* save the index */
Packit Service 2781ba
  {
Packit Service 2781ba
    GList *iter;
Packit Service 2781ba
    for (iter = info->tiles; iter; iter = iter->next)
Packit Service 2781ba
      {
Packit Service 2781ba
        GeglBufferItem *item = iter->data;
Packit Service 2781ba
Packit Service 2781ba
        write_block (info, &item->block);
Packit Service 2781ba
Packit Service 2781ba
      }
Packit Service 2781ba
  }
Packit Service 2781ba
  write_block (info, NULL); /* terminate the index */
Packit Service 2781ba
Packit Service 2781ba
  /* update header to point to start of new index (already done for
Packit Service 2781ba
   * this serial saver, and the header is already written.
Packit Service 2781ba
   */
Packit Service 2781ba
Packit Service 2781ba
  /* save each tile */
Packit Service 2781ba
  {
Packit Service 2781ba
    GList *iter;
Packit Service 2781ba
    gint   i = 0;
Packit Service 2781ba
    for (iter = info->tiles; iter; iter = iter->next)
Packit Service 2781ba
      {
Packit Service 2781ba
        GeglBufferTile *entry = iter->data;
Packit Service 2781ba
        guchar          *data;
Packit Service 2781ba
        GeglTile        *tile;
Packit Service 2781ba
Packit Service 2781ba
        tile = gegl_tile_source_get_tile (GEGL_TILE_SOURCE (buffer),
Packit Service 2781ba
                                          entry->x,
Packit Service 2781ba
                                          entry->y,
Packit Service 2781ba
                                          entry->z);
Packit Service 2781ba
        g_assert (tile);
Packit Service 2781ba
        data = gegl_tile_get_data (tile);
Packit Service 2781ba
        g_assert (data);
Packit Service 2781ba
Packit Service 2781ba
        g_assert (info->offset == entry->offset);
Packit Service 2781ba
        {
Packit Service 2781ba
          ssize_t ret = write (info->o, data, info->tile_size);
Packit Service 2781ba
	      if (ret != -1)
Packit Service 2781ba
            info->offset += ret;
Packit Service 2781ba
        }
Packit Service 2781ba
        gegl_tile_unref (tile);
Packit Service 2781ba
        i++;
Packit Service 2781ba
      }
Packit Service 2781ba
  }
Packit Service 2781ba
  save_info_destroy (info);
Packit Service 2781ba
}