xcf_load_layer (XcfInfo    *info,
                GimpImage  *image,
                GList     **item_path)
{
  GimpLayer         *layer;
  GimpLayerMask     *layer_mask;
  goffset            hierarchy_offset;
  goffset            layer_mask_offset;
  gboolean           apply_mask = TRUE;
  gboolean           edit_mask  = FALSE;
  gboolean           show_mask  = FALSE;
  GList             *selected;
  GList             *linked;
  gboolean           floating;
  guint32            group_layer_flags = 0;
  guint32            text_layer_flags = 0;
  gint               width;
  gint               height;
  gint               type;
  GimpImageBaseType  base_type;
  gboolean           has_alpha;
  const Babl        *format;
  gboolean           is_fs_drawable;
  gchar             *name;

  /* check and see if this is the drawable the floating selection
   *  is attached to. if it is then we'll do the attachment in our caller.
   */
  is_fs_drawable = (info->cp == info->floating_sel_offset);

  /* read in the layer width, height, type and name */
  xcf_read_int32  (info, (guint32 *) &width,  1);
  xcf_read_int32  (info, (guint32 *) &height, 1);
  xcf_read_int32  (info, (guint32 *) &type,   1);
  xcf_read_string (info,             &name,   1);

  GIMP_LOG (XCF, "width=%d, height=%d, type=%d, name='%s'",
            width, height, type, name);

  switch (type)
    {
    case GIMP_RGB_IMAGE:
      base_type = GIMP_RGB;
      has_alpha = FALSE;
      break;

    case GIMP_RGBA_IMAGE:
      base_type = GIMP_RGB;
      has_alpha = TRUE;
      break;

    case GIMP_GRAY_IMAGE:
      base_type = GIMP_GRAY;
      has_alpha = FALSE;
      break;

    case GIMP_GRAYA_IMAGE:
      base_type = GIMP_GRAY;
      has_alpha = TRUE;
      break;

    case GIMP_INDEXED_IMAGE:
      base_type = GIMP_INDEXED;
      has_alpha = FALSE;
      break;

    case GIMP_INDEXEDA_IMAGE:
      base_type = GIMP_INDEXED;
      has_alpha = TRUE;
      break;

    default:
      g_free (name);
      return NULL;
    }

  if (width <= 0 || height <= 0)
    {
      gboolean is_group_layer = FALSE;
      gboolean is_text_layer  = FALSE;
      goffset  saved_pos;

      saved_pos = info->cp;
      /* Load item path and check if this is a group or text layer. */
      xcf_check_layer_props (info, item_path, &is_group_layer, &is_text_layer);
      if ((is_text_layer || is_group_layer) &&
          xcf_seek_pos (info, saved_pos, NULL))
        {
          /* Something is wrong, but leave a chance to the layer because
           * anyway group and text layer depends on their contents.
           */
          width = height = 1;
          g_clear_pointer (item_path, g_list_free);
        }
      else
        {
          g_free (name);
          return NULL;
        }
    }

  if (base_type == GIMP_GRAY)
    {
      /* do not use gimp_image_get_layer_format() because it might
       * be the floating selection of a channel or mask
       */
      format = gimp_image_get_format (image, base_type,
                                      gimp_image_get_precision (image),
                                      has_alpha,
                                      NULL /* we will fix the space later */);
    }
  else
    {
      format = gimp_image_get_layer_format (image, has_alpha);
    }

  /* create a new layer */
  layer = gimp_layer_new (image, width, height,
                          format, name,
                          GIMP_OPACITY_OPAQUE, GIMP_LAYER_MODE_NORMAL);
  g_free (name);
  if (! layer)
    return NULL;

  /* read in the layer properties */
  if (! xcf_load_layer_props (info, image, &layer, item_path,
                              &apply_mask, &edit_mask, &show_mask,
                              &text_layer_flags, &group_layer_flags))
    goto error;

  GIMP_LOG (XCF, "layer props loaded");

  xcf_progress_update (info);

  /* call the evil text layer hack that might change our layer pointer */
  selected = g_list_find (info->selected_layers, layer);
  linked   = g_list_find (info->linked_layers, layer);
  floating = (info->floating_sel == layer);

  if (gimp_text_layer_xcf_load_hack (&layer))
    {
      gimp_text_layer_set_xcf_flags (GIMP_TEXT_LAYER (layer),
                                     text_layer_flags);

      if (selected)
        {
          info->selected_layers = g_list_delete_link (info->selected_layers, selected);
          info->selected_layers = g_list_prepend (info->selected_layers, layer);
        }
      if (linked)
        {
          info->linked_layers = g_list_delete_link (info->linked_layers, linked);
          info->linked_layers = g_list_prepend (info->linked_layers, layer);
        }
      if (floating)
        info->floating_sel = layer;
    }

  /* if this is not the floating selection, we can fix the layer's
   * space already now, the function will do nothing if we already
   * created the layer with the right format
   */
  if (! floating && base_type == GIMP_GRAY)
    gimp_layer_fix_format_space (layer, FALSE, FALSE);

  /* read the hierarchy and layer mask offsets */
  xcf_read_offset (info, &hierarchy_offset,  1);
  xcf_read_offset (info, &layer_mask_offset, 1);

  /* read in the hierarchy (ignore it for group layers, both as an
   * optimization and because the hierarchy's extents don't match
   * the group layer's tiles)
   */
  if (! gimp_viewable_get_children (GIMP_VIEWABLE (layer)))
    {
      if (! xcf_seek_pos (info, hierarchy_offset, NULL))
        goto error;

      GIMP_LOG (XCF, "loading buffer");

      if (! xcf_load_buffer (info,
                             gimp_drawable_get_buffer (GIMP_DRAWABLE (layer))))
        goto error;

      GIMP_LOG (XCF, "buffer loaded");

      xcf_progress_update (info);
    }
  else
    {
      gboolean expanded = group_layer_flags & XCF_GROUP_ITEM_EXPANDED;

      gimp_viewable_set_expanded (GIMP_VIEWABLE (layer), expanded);
    }

  /* read in the layer mask */
  if (layer_mask_offset != 0)
    {
      if (! xcf_seek_pos (info, layer_mask_offset, NULL))
        goto error;

      layer_mask = xcf_load_layer_mask (info, image);
      if (! layer_mask)
        goto error;

      xcf_progress_update (info);

      /* don't add the layer mask yet, that won't work for group
       * layers which update their size automatically; instead
       * attach it so it can be added when all layers are loaded
       */
      g_object_set_data_full (G_OBJECT (layer), "gimp-layer-mask",
                              g_object_ref_sink (layer_mask),
                              (GDestroyNotify) g_object_unref);
      g_object_set_data (G_OBJECT (layer), "gimp-layer-mask-apply",
                         GINT_TO_POINTER (apply_mask));
      g_object_set_data (G_OBJECT (layer), "gimp-layer-mask-edit",
                         GINT_TO_POINTER (edit_mask));
      g_object_set_data (G_OBJECT (layer), "gimp-layer-mask-show",
                         GINT_TO_POINTER (show_mask));
    }

  /* attach the floating selection... */
  if (is_fs_drawable)
    info->floating_sel_drawable = GIMP_DRAWABLE (layer);

  return layer;

 error:
  info->selected_layers = g_list_remove (info->selected_layers, layer);

  if (info->floating_sel == layer)
    info->floating_sel = NULL;

  if (info->floating_sel_drawable == GIMP_DRAWABLE (layer))
    info->floating_sel_drawable = NULL;

  g_object_unref (layer);

  return NULL;
}
xcf_load_layer (XcfInfo    *info,
                GimpImage  *image,
                GList     **item_path)
{
  GimpLayer         *layer;
  GimpLayerMask     *layer_mask;
  goffset            hierarchy_offset;
  goffset            layer_mask_offset;
  gboolean           apply_mask = TRUE;
  gboolean           edit_mask  = FALSE;
  gboolean           show_mask  = FALSE;
  GList             *selected;
  GList             *linked;
  gboolean           floating;
  guint32            group_layer_flags = 0;
  guint32            text_layer_flags = 0;
  gint               width;
  gint               height;
  gint               type;
  GimpImageBaseType  base_type;
  gboolean           has_alpha;
  const Babl        *format;
  gboolean           is_fs_drawable;
  gchar             *name;

   
  is_fs_drawable = (info->cp == info->floating_sel_offset);

   
  xcf_read_int32  (info, (guint32 *) &width,  1);
  xcf_read_int32  (info, (guint32 *) &height, 1);
  xcf_read_int32  (info, (guint32 *) &type,   1);
  xcf_read_string (info,             &name,   1);

  GIMP_LOG (XCF, "width=%d, height=%d, type=%d, name='%s'",
            width, height, type, name);

  switch (type)
    {
    case GIMP_RGB_IMAGE:
      base_type = GIMP_RGB;
      has_alpha = FALSE;
      break;

    case GIMP_RGBA_IMAGE:
      base_type = GIMP_RGB;
      has_alpha = TRUE;
      break;

    case GIMP_GRAY_IMAGE:
      base_type = GIMP_GRAY;
      has_alpha = FALSE;
      break;

    case GIMP_GRAYA_IMAGE:
      base_type = GIMP_GRAY;
      has_alpha = TRUE;
      break;

    case GIMP_INDEXED_IMAGE:
      base_type = GIMP_INDEXED;
      has_alpha = FALSE;
      break;

    case GIMP_INDEXEDA_IMAGE:
      base_type = GIMP_INDEXED;
      has_alpha = TRUE;
      break;

    default:
      g_free (name);
      return NULL;
    }

  if (width <= 0 || height <= 0)
    {
      gboolean is_group_layer = FALSE;
      gboolean is_text_layer  = FALSE;
      goffset  saved_pos;

      saved_pos = info->cp;
       
      xcf_check_layer_props (info, item_path, &is_group_layer, &is_text_layer);
      if ((is_text_layer || is_group_layer) &&
          xcf_seek_pos (info, saved_pos, NULL))
        {
           
          width = height = 1;
          g_clear_pointer (item_path, g_list_free);
        }
      else
        {
          g_free (name);
          return NULL;
        }
    }

  if (base_type == GIMP_GRAY)
    {
       
      format = gimp_image_get_format (image, base_type,
                                      gimp_image_get_precision (image),
                                      has_alpha,
                                      NULL  );
    }
  else
    {
      format = gimp_image_get_layer_format (image, has_alpha);
    }

   
  layer = gimp_layer_new (image, width, height,
                          format, name,
                          GIMP_OPACITY_OPAQUE, GIMP_LAYER_MODE_NORMAL);
  g_free (name);
  if (! layer)
    return NULL;

   
  if (! xcf_load_layer_props (info, image, &layer, item_path,
                              &apply_mask, &edit_mask, &show_mask,
                              &text_layer_flags, &group_layer_flags))
    goto error;

  GIMP_LOG (XCF, "layer props loaded");

  xcf_progress_update (info);

   
  selected = g_list_find (info->selected_layers, layer);
  linked   = g_list_find (info->linked_layers, layer);
  floating = (info->floating_sel == layer);

  if (gimp_text_layer_xcf_load_hack (&layer))
    {
      gimp_text_layer_set_xcf_flags (GIMP_TEXT_LAYER (layer),
                                     text_layer_flags);

      if (selected)
        {
          info->selected_layers = g_list_delete_link (info->selected_layers, selected);
          info->selected_layers = g_list_prepend (info->selected_layers, layer);
        }
      if (linked)
        {
          info->linked_layers = g_list_delete_link (info->linked_layers, linked);
          info->linked_layers = g_list_prepend (info->linked_layers, layer);
        }
      if (floating)
        info->floating_sel = layer;
    }

   
  if (! floating && base_type == GIMP_GRAY)
    gimp_layer_fix_format_space (layer, FALSE, FALSE);

   
  xcf_read_offset (info, &hierarchy_offset,  1);
  xcf_read_offset (info, &layer_mask_offset, 1);

   
  if (! gimp_viewable_get_children (GIMP_VIEWABLE (layer)))
    {
      if (! xcf_seek_pos (info, hierarchy_offset, NULL))
        goto error;

      GIMP_LOG (XCF, "loading buffer");

      if (! xcf_load_buffer (info,
                             gimp_drawable_get_buffer (GIMP_DRAWABLE (layer))))
        goto error;

      GIMP_LOG (XCF, "buffer loaded");

      xcf_progress_update (info);
    }
  else
    {
      gboolean expanded = group_layer_flags & XCF_GROUP_ITEM_EXPANDED;

      gimp_viewable_set_expanded (GIMP_VIEWABLE (layer), expanded);
    }

   
  if (layer_mask_offset != 0)
    {
      if (! xcf_seek_pos (info, layer_mask_offset, NULL))
        goto error;

      layer_mask = xcf_load_layer_mask (info, image);
      if (! layer_mask)
        goto error;

      xcf_progress_update (info);

       
      g_object_set_data_full (G_OBJECT (layer), "gimp-layer-mask",
                              g_object_ref_sink (layer_mask),
                              (GDestroyNotify) g_object_unref);
      g_object_set_data (G_OBJECT (layer), "gimp-layer-mask-apply",
                         GINT_TO_POINTER (apply_mask));
      g_object_set_data (G_OBJECT (layer), "gimp-layer-mask-edit",
                         GINT_TO_POINTER (edit_mask));
      g_object_set_data (G_OBJECT (layer), "gimp-layer-mask-show",
                         GINT_TO_POINTER (show_mask));
    }

   
  if (is_fs_drawable)
    info->floating_sel_drawable = GIMP_DRAWABLE (layer);

  return layer;

 error:
  info->selected_layers = g_list_remove (info->selected_layers, layer);

  if (info->floating_sel == layer)
    info->floating_sel = NULL;

  if (info->floating_sel_drawable == GIMP_DRAWABLE (layer))
    info->floating_sel_drawable = NULL;

  g_object_unref (layer);

  return NULL;
}