Beispiel #1
0
/**
 * gimp_image_scale_check:
 * @image:      A #GimpImage.
 * @new_width:   The new width.
 * @new_height:  The new height.
 * @max_memsize: The maximum new memory size.
 * @new_memsize: The new memory size.
 *
 * Inventory the layer list in image and check that it may be
 * scaled to @new_height and @new_width without problems.
 *
 * Return value: #GIMP_IMAGE_SCALE_OK if scaling the image will shrink none
 *               of its layers completely away, and the new image size
 *               is smaller than @max_memsize.
 *               #GIMP_IMAGE_SCALE_TOO_SMALL if scaling would remove some
 *               existing layers.
 *               #GIMP_IMAGE_SCALE_TOO_BIG if the new image size would
 *               exceed the maximum specified in the preferences.
 **/
GimpImageScaleCheckType
gimp_image_scale_check (GimpImage *image,
                        gint       new_width,
                        gint       new_height,
                        gint64     max_memsize,
                        gint64    *new_memsize)
{
  GList  *all_layers;
  GList  *list;
  gint64  current_size;
  gint64  undo_size;
  gint64  redo_size;
  gint64  new_size;

  g_return_val_if_fail (GIMP_IS_IMAGE (image), GIMP_IMAGE_SCALE_TOO_SMALL);
  g_return_val_if_fail (new_memsize != NULL, GIMP_IMAGE_SCALE_TOO_SMALL);

  current_size = gimp_object_get_memsize (GIMP_OBJECT (image), NULL);

  new_size = gimp_image_estimate_memsize (image,
                                          gimp_image_get_component_type (image),
                                          new_width, new_height);

  undo_size = gimp_object_get_memsize (GIMP_OBJECT (gimp_image_get_undo_stack (image)), NULL);
  redo_size = gimp_object_get_memsize (GIMP_OBJECT (gimp_image_get_redo_stack (image)), NULL);

  current_size -= undo_size + redo_size;
  new_size     -= undo_size + redo_size;

  GIMP_LOG (IMAGE_SCALE,
            "old_size = %"G_GINT64_FORMAT"  new_size = %"G_GINT64_FORMAT,
            current_size, new_size);

  *new_memsize = new_size;

  if (new_size > current_size && new_size > max_memsize)
    return GIMP_IMAGE_SCALE_TOO_BIG;

  all_layers = gimp_image_get_layer_list (image);

  for (list = all_layers; list; list = g_list_next (list))
    {
      GimpItem *item = list->data;

      /*  group layers are updated automatically  */
      if (gimp_viewable_get_children (GIMP_VIEWABLE (item)))
        continue;

      if (! gimp_item_check_scaling (item, new_width, new_height))
        {
          g_list_free (all_layers);

          return GIMP_IMAGE_SCALE_TOO_SMALL;
        }
    }

  g_list_free (all_layers);

  return GIMP_IMAGE_SCALE_OK;
}
Beispiel #2
0
void
view_use_gegl_cmd_callback (GtkAction *action,
                            gpointer   data)
{
  GimpImage        *image;
  GimpDisplayShell *shell;
  GList            *layers;
  GList            *list;
  gboolean          active;
  return_if_no_image (image, data);
  return_if_no_shell (shell, data);

  active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));

  gimp_image_get_projection (image)->use_gegl = active;

  layers = gimp_image_get_layer_list (image);

  for (list = layers; list; list = g_list_next (list))
    {
      GimpLayer *layer = list->data;

      if (GIMP_IS_GROUP_LAYER (layer))
        gimp_group_layer_get_projection (GIMP_GROUP_LAYER (layer))->use_gegl = active;
    }

  g_list_free (layers);

  gimp_image_invalidate (image, 0, 0,
                         gimp_image_get_width  (image),
                         gimp_image_get_height (image));
  gimp_image_flush (image);
}
GimpLayer *
gimp_image_pick_layer (const GimpImage *image,
                       gint             x,
                       gint             y)
{
  GList *all_layers;
  GList *list;

  g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);

  all_layers = gimp_image_get_layer_list (image);

  for (list = all_layers; list; list = g_list_next (list))
    {
      GimpLayer *layer = list->data;
      gint       off_x, off_y;

      gimp_item_get_offset (GIMP_ITEM (layer), &off_x, &off_y);

      if (gimp_pickable_get_opacity_at (GIMP_PICKABLE (layer),
                                        x - off_x, y - off_y) > 63)
        {
          g_list_free (all_layers);

          return layer;
        }
    }

  g_list_free (all_layers);

  return NULL;
}
Beispiel #4
0
/**
 * gimp_image_item_list_get_list:
 * @image:   An @image.
 * @type:    Which type of items to return.
 * @set:     Set the returned items are part of.
 *
 * This function returns a #GList of #GimpItem<!-- -->s for which the
 * @type and @set criterions match.
 *
 * Return value: The list of items.
 **/
GList *
gimp_image_item_list_get_list (GimpImage        *image,
                               GimpItemTypeMask  type,
                               GimpItemSet       set)
{
  GList *all_items;
  GList *list;
  GList *return_list = NULL;

  g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);

  if (type & GIMP_ITEM_TYPE_LAYERS)
    {
      all_items = gimp_image_get_layer_list (image);

      for (list = all_items; list; list = g_list_next (list))
        {
          GimpItem *item = list->data;

          if (gimp_item_is_in_set (item, set))
            return_list = g_list_prepend (return_list, item);
        }

      g_list_free (all_items);
    }

  if (type & GIMP_ITEM_TYPE_CHANNELS)
    {
      all_items = gimp_image_get_channel_list (image);

      for (list = all_items; list; list = g_list_next (list))
        {
          GimpItem *item = list->data;

          if (gimp_item_is_in_set (item, set))
            return_list = g_list_prepend (return_list, item);
        }

      g_list_free (all_items);
    }

  if (type & GIMP_ITEM_TYPE_VECTORS)
    {
      all_items = gimp_image_get_vectors_list (image);

      for (list = all_items; list; list = g_list_next (list))
        {
          GimpItem *item = list->data;

          if (gimp_item_is_in_set (item, set))
            return_list = g_list_prepend (return_list, item);
        }

      g_list_free (all_items);
    }

  return g_list_reverse (return_list);
}
static void
layer_select_advance (LayerSelect *layer_select,
                      gint         move)
{
  GimpLayer *active_layer;
  GimpLayer *next_layer;
  GList     *layers;
  gint       n_layers;
  gint       index;

  if (move == 0)
    return;

  /*  If there is a floating selection, allow no advancement  */
  if (gimp_image_get_floating_selection (layer_select->image))
    return;

  active_layer = gimp_image_get_active_layer (layer_select->image);

  layers   = gimp_image_get_layer_list (layer_select->image);
  n_layers = g_list_length (layers);

  index = g_list_index (layers, active_layer);
  index += move;

  if (index < 0)
    index = n_layers - 1;
  else if (index >= n_layers)
    index = 0;

  next_layer = g_list_nth_data (layers, index);

  g_list_free (layers);

  if (next_layer && next_layer != active_layer)
    {
      active_layer = gimp_image_set_active_layer (layer_select->image,
                                                  next_layer);

      if (active_layer)
        {
          gimp_view_set_viewable (GIMP_VIEW (layer_select->view),
                                  GIMP_VIEWABLE (active_layer));
          gtk_label_set_text (GTK_LABEL (layer_select->label),
                              gimp_object_get_name (active_layer));
        }
    }
}
GimpTextLayer *
gimp_image_pick_text_layer (const GimpImage *image,
                            gint             x,
                            gint             y)
{
  GList *all_layers;
  GList *list;

  g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);

  all_layers = gimp_image_get_layer_list (image);

  for (list = all_layers; list; list = g_list_next (list))
    {
      GimpLayer *layer = list->data;
      gint       off_x, off_y;

      gimp_item_get_offset (GIMP_ITEM (layer), &off_x, &off_y);

      if (GIMP_IS_TEXT_LAYER (layer) &&
          x >= off_x &&
          y >= off_y &&
          x <  off_x + gimp_item_get_width  (GIMP_ITEM (layer)) &&
          y <  off_y + gimp_item_get_height (GIMP_ITEM (layer)))
        {
          g_list_free (all_layers);

          return GIMP_TEXT_LAYER (layer);
        }
      else if (gimp_pickable_get_opacity_at (GIMP_PICKABLE (layer),
                                             x - off_x, y - off_y) > 63)
        {
          /*  a normal layer covers any possible text layers below,
           *  bail out
           */

          break;
        }
    }

  g_list_free (all_layers);

  return NULL;
}
GimpLayer *
gimp_image_pick_layer_by_bounds (const GimpImage *image,
                                 gint             x,
                                 gint             y)
{
  GList *all_layers;
  GList *list;

  g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);

  all_layers = gimp_image_get_layer_list (image);

  for (list = all_layers; list; list = g_list_next (list))
    {
      GimpLayer *layer = list->data;

      if (gimp_item_get_visible (GIMP_ITEM (layer)))
        {
          gint off_x, off_y;
          gint width, height;

          gimp_item_get_offset (GIMP_ITEM (layer), &off_x, &off_y);
          width  = gimp_item_get_width  (GIMP_ITEM (layer));
          height = gimp_item_get_height (GIMP_ITEM (layer));

          if (x >= off_x        &&
              y >= off_y        &&
              x < off_x + width &&
              y < off_y + height)
            {
              g_list_free (all_layers);

              return layer;
            }
        }
    }

  g_list_free (all_layers);

  return NULL;
}
static void
gimp_image_convert_profile_rgb (GimpImage                *image,
                                GimpColorProfile         *src_profile,
                                GimpColorProfile         *dest_profile,
                                GimpColorRenderingIntent  intent,
                                gboolean                  bpc,
                                GimpProgress             *progress)
{
  GList *layers;
  GList *list;
  gint   n_drawables;
  gint   nth_drawable;

  layers = gimp_image_get_layer_list (image);

  n_drawables = g_list_length (layers);

  for (list = layers, nth_drawable = 0;
       list;
       list = g_list_next (list), nth_drawable++)
    {
      GimpDrawable    *drawable = list->data;
      cmsHPROFILE      src_lcms;
      cmsHPROFILE      dest_lcms;
      const Babl      *iter_format;
      cmsUInt32Number  lcms_format;
      cmsUInt32Number  flags;
      cmsHTRANSFORM    transform;

      if (gimp_viewable_get_children (GIMP_VIEWABLE (drawable)))
        continue;

      src_lcms  = gimp_color_profile_get_lcms_profile (src_profile);
      dest_lcms = gimp_color_profile_get_lcms_profile (dest_profile);

      iter_format =
        gimp_color_profile_get_format (gimp_drawable_get_format (drawable),
                                       &lcms_format);

      flags = cmsFLAGS_NOOPTIMIZE;

      if (bpc)
        flags |= cmsFLAGS_BLACKPOINTCOMPENSATION;

      transform = cmsCreateTransform (src_lcms,  lcms_format,
                                      dest_lcms, lcms_format,
                                      intent, flags);

      if (transform)
        {
          GeglBuffer         *buffer;
          GeglBufferIterator *iter;

          buffer = gimp_drawable_get_buffer (drawable);

          gimp_drawable_push_undo (drawable, NULL, NULL,
                                   0, 0,
                                   gegl_buffer_get_width  (buffer),
                                   gegl_buffer_get_height (buffer));

          iter = gegl_buffer_iterator_new (buffer, NULL, 0,
                                           iter_format,
                                           GEGL_ACCESS_READWRITE,
                                           GEGL_ABYSS_NONE);

          while (gegl_buffer_iterator_next (iter))
            {
              cmsDoTransform (transform,
                              iter->data[0], iter->data[0], iter->length);
            }

          gimp_drawable_update (drawable, 0, 0,
                                gegl_buffer_get_width  (buffer),
                                gegl_buffer_get_height (buffer));

          cmsDeleteTransform (transform);
        }

      if (progress)
        gimp_progress_set_value (progress,
                                 (gdouble) nth_drawable / (gdouble) n_drawables);
    }

  g_list_free (layers);
}
Beispiel #9
0
void
file_import_image (GimpImage    *image,
                   GimpContext  *context,
                   GFile        *file,
                   gboolean      interactive,
                   GimpProgress *progress)
{
  GimpCoreConfig *config;

  g_return_if_fail (GIMP_IS_IMAGE (image));
  g_return_if_fail (GIMP_IS_CONTEXT (context));
  g_return_if_fail (G_IS_FILE (file));
  g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress));

  config = image->gimp->config;

  if (interactive && gimp_image_get_base_type (image) != GIMP_INDEXED)
    {
      if (config->import_promote_float)
        {
          GimpPrecision old_precision = gimp_image_get_precision (image);

          if (old_precision != GIMP_PRECISION_FLOAT_LINEAR)
            {
              gimp_image_convert_precision (image,
                                            GIMP_PRECISION_FLOAT_LINEAR,
                                            GEGL_DITHER_NONE,
                                            GEGL_DITHER_NONE,
                                            GEGL_DITHER_NONE,
                                            progress);

              if (config->import_promote_dither &&
                  old_precision == GIMP_PRECISION_U8_NON_LINEAR)
                {
                  gimp_image_convert_dither_u8 (image, progress);
                }
            }
        }

      if (config->import_add_alpha)
        {
          GList *layers = gimp_image_get_layer_list (image);
          GList *list;

          for (list = layers; list; list = g_list_next (list))
            {
              if (! gimp_viewable_get_children (list->data) &&
                  ! gimp_item_is_text_layer (list->data)    &&
                  ! gimp_drawable_has_alpha (list->data))
                {
                  gimp_layer_add_alpha (list->data);
                }
            }

          g_list_free (layers);
        }
    }

  gimp_image_import_color_profile (image, context, progress, interactive);

  /* Remember the import source */
  gimp_image_set_imported_file (image, file);

  /* We shall treat this file as an Untitled file */
  gimp_image_set_file (image, NULL);
}
Beispiel #10
0
/* some rather complex logic here.  If the user clicks without modifiers,
 * then we start a new list, and use the first object in it as reference.
 * If the user clicks using Shift, or draws a rubber-band box, then
 * we add objects to the list, but do not specify which one should
 * be used as reference.
 */
static void
gimp_align_tool_button_release (GimpTool              *tool,
                                const GimpCoords      *coords,
                                guint32                time,
                                GdkModifierType        state,
                                GimpButtonReleaseType  release_type,
                                GimpDisplay           *display)
{
    GimpAlignTool    *align_tool = GIMP_ALIGN_TOOL (tool);
    GimpAlignOptions *options    = GIMP_ALIGN_TOOL_GET_OPTIONS (tool);
    GimpDisplayShell *shell      = gimp_display_get_shell (display);
    GObject          *object     = NULL;
    GimpImage        *image      = gimp_display_get_image (display);
    GdkModifierType   extend_mask;
    gint              i;

    extend_mask = gimp_get_extend_selection_mask ();

    gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));

    gimp_tool_control_halt (tool->control);

    if (release_type == GIMP_BUTTON_RELEASE_CANCEL)
    {
        align_tool->x2 = align_tool->x1;
        align_tool->y2 = align_tool->y1;

        gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
        return;
    }

    if (! (state & extend_mask)) /* start a new list */
    {
        gimp_align_tool_clear_selected (align_tool);
        align_tool->set_reference = FALSE;
    }

    /* if mouse has moved less than EPSILON pixels since button press,
     * select the nearest thing, otherwise make a rubber-band rectangle
     */
    if (hypot (coords->x - align_tool->x1,
               coords->y - align_tool->y1) < EPSILON)
    {
        GimpVectors *vectors;
        GimpGuide   *guide;
        GimpLayer   *layer;
        gint         snap_distance = display->config->snap_distance;

        if (gimp_draw_tool_on_vectors (GIMP_DRAW_TOOL (tool), display,
                                       coords, snap_distance, snap_distance,
                                       NULL, NULL, NULL, NULL, NULL,
                                       &vectors))
        {
            object = G_OBJECT (vectors);
        }
        else if (gimp_display_shell_get_show_guides (shell) &&
                 (guide = gimp_image_find_guide (image,
                          coords->x, coords->y,
                          FUNSCALEX (shell, snap_distance),
                          FUNSCALEY (shell, snap_distance))))
        {
            object = G_OBJECT (guide);
        }
        else
        {
            if ((layer = gimp_image_pick_layer_by_bounds (image,
                         coords->x, coords->y)))
            {
                object = G_OBJECT (layer);
            }
        }

        if (object)
        {
            if (! g_list_find (align_tool->selected_objects, object))
            {
                align_tool->selected_objects =
                    g_list_append (align_tool->selected_objects, object);

                g_signal_connect (object, "removed",
                                  G_CALLBACK (gimp_align_tool_object_removed),
                                  align_tool);

                /* if an object has been selected using unmodified click,
                 * it should be used as the reference
                 */
                if (! (state & extend_mask))
                    align_tool->set_reference = TRUE;
            }
        }
    }
    else  /* FIXME: look for vectors too */
    {
        gint   X0 = MIN (coords->x, align_tool->x1);
        gint   X1 = MAX (coords->x, align_tool->x1);
        gint   Y0 = MIN (coords->y, align_tool->y1);
        gint   Y1 = MAX (coords->y, align_tool->y1);
        GList *all_layers;
        GList *list;

        all_layers = gimp_image_get_layer_list (image);

        for (list = all_layers; list; list = g_list_next (list))
        {
            GimpLayer *layer = list->data;
            gint       x0, y0, x1, y1;

            if (! gimp_item_get_visible (GIMP_ITEM (layer)))
                continue;

            gimp_item_get_offset (GIMP_ITEM (layer), &x0, &y0);
            x1 = x0 + gimp_item_get_width  (GIMP_ITEM (layer));
            y1 = y0 + gimp_item_get_height (GIMP_ITEM (layer));

            if (x0 < X0 || y0 < Y0 || x1 > X1 || y1 > Y1)
                continue;

            if (g_list_find (align_tool->selected_objects, layer))
                continue;

            align_tool->selected_objects =
                g_list_append (align_tool->selected_objects, layer);

            g_signal_connect (layer, "removed",
                              G_CALLBACK (gimp_align_tool_object_removed),
                              align_tool);
        }

        g_list_free (all_layers);
    }

    for (i = 0; i < ALIGN_OPTIONS_N_BUTTONS; i++)
    {
        if (options->button[i])
            gtk_widget_set_sensitive (options->button[i],
                                      align_tool->selected_objects != NULL);
    }

    align_tool->x2 = align_tool->x1;
    align_tool->y2 = align_tool->y1;

    gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
}
void
gimp_image_convert_precision (GimpImage     *image,
                              GimpPrecision  precision,
                              gint           layer_dither_type,
                              gint           text_layer_dither_type,
                              gint           mask_dither_type,
                              GimpProgress  *progress)
{
  GList       *all_drawables;
  GList       *list;
  const gchar *undo_desc = NULL;
  gint         nth_drawable, n_drawables;

  g_return_if_fail (GIMP_IS_IMAGE (image));
  g_return_if_fail (precision != gimp_image_get_precision (image));
  g_return_if_fail (precision == GIMP_PRECISION_U8_GAMMA ||
                    gimp_image_get_base_type (image) != GIMP_INDEXED);
  g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress));

  all_drawables = g_list_concat (gimp_image_get_layer_list (image),
                                 gimp_image_get_channel_list (image));

  n_drawables = g_list_length (all_drawables) + 1 /* + selection */;

  switch (precision)
    {
    case GIMP_PRECISION_U8_LINEAR:
      undo_desc = C_("undo-type", "Convert Image to 8 bit linear integer");
      break;
    case GIMP_PRECISION_U8_GAMMA:
      undo_desc = C_("undo-type", "Convert Image to 8 bit gamma integer");
      break;
    case GIMP_PRECISION_U16_LINEAR:
      undo_desc = C_("undo-type", "Convert Image to 16 bit linear integer");
      break;
    case GIMP_PRECISION_U16_GAMMA:
      undo_desc = C_("undo-type", "Convert Image to 16 bit gamma integer");
      break;
    case GIMP_PRECISION_U32_LINEAR:
      undo_desc = C_("undo-type", "Convert Image to 32 bit linear integer");
      break;
    case GIMP_PRECISION_U32_GAMMA:
      undo_desc = C_("undo-type", "Convert Image to 32 bit gamma integer");
      break;
    case GIMP_PRECISION_HALF_LINEAR:
      undo_desc = C_("undo-type", "Convert Image to 16 bit linear floating point");
      break;
    case GIMP_PRECISION_HALF_GAMMA:
      undo_desc = C_("undo-type", "Convert Image to 16 bit gamma floating point");
      break;
    case GIMP_PRECISION_FLOAT_LINEAR:
      undo_desc = C_("undo-type", "Convert Image to 32 bit linear floating point");
      break;
    case GIMP_PRECISION_FLOAT_GAMMA:
      undo_desc = C_("undo-type", "Convert Image to 32 bit gamma floating point");
      break;
    case GIMP_PRECISION_DOUBLE_LINEAR:
      undo_desc = C_("undo-type", "Convert Image to 64 bit linear floating point");
      break;
    case GIMP_PRECISION_DOUBLE_GAMMA:
      undo_desc = C_("undo-type", "Convert Image to 64 bit gamma floating point");
      break;
    }

  if (progress)
    gimp_progress_start (progress, FALSE, "%s", undo_desc);

  g_object_freeze_notify (G_OBJECT (image));

  gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_CONVERT,
                               undo_desc);

  /*  Push the image precision to the stack  */
  gimp_image_undo_push_image_precision (image, NULL);

  /*  Set the new precision  */
  g_object_set (image, "precision", precision, NULL);

  for (list = all_drawables, nth_drawable = 0;
       list;
       list = g_list_next (list), nth_drawable++)
    {
      GimpDrawable *drawable = list->data;
      gint          dither_type;

      if (gimp_item_is_text_layer (GIMP_ITEM (drawable)))
        dither_type = text_layer_dither_type;
      else
        dither_type = layer_dither_type;

      gimp_drawable_convert_type (drawable, image,
                                  gimp_drawable_get_base_type (drawable),
                                  precision,
                                  dither_type,
                                  mask_dither_type,
                                  FALSE,
                                  TRUE);

      if (progress)
        gimp_progress_set_value (progress,
                                 (gdouble) nth_drawable / (gdouble) n_drawables);
    }
  g_list_free (all_drawables);

  /*  convert the selection mask  */
  {
    GimpChannel *mask = gimp_image_get_mask (image);
    GeglBuffer  *buffer;

    gimp_image_undo_push_mask_precision (image, NULL, mask);

    buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
                                              gimp_image_get_width  (image),
                                              gimp_image_get_height (image)),
                              gimp_image_get_mask_format (image));

    gegl_buffer_copy (gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)), NULL,
                      GEGL_ABYSS_NONE,
                      buffer, NULL);

    gimp_drawable_set_buffer (GIMP_DRAWABLE (mask), FALSE, NULL, buffer);
    g_object_unref (buffer);

    nth_drawable++;

    if (progress)
      gimp_progress_set_value (progress,
                               (gdouble) nth_drawable / (gdouble) n_drawables);
  }

  gimp_image_undo_group_end (image);

  gimp_image_precision_changed (image);
  g_object_thaw_notify (G_OBJECT (image));

  if (progress)
    gimp_progress_end (progress);
}
void
gimp_image_convert_precision (GimpImage     *image,
                              GimpPrecision  precision,
                              gint           layer_dither_type,
                              gint           text_layer_dither_type,
                              gint           mask_dither_type,
                              GimpProgress  *progress)
{
  GimpColorProfile *old_profile;
  GimpColorProfile *new_profile = NULL;
  const Babl       *old_format;
  const Babl       *new_format;
  GList            *all_drawables;
  GList            *list;
  const gchar      *undo_desc    = NULL;
  GimpProgress     *sub_progress = NULL;
  gint              nth_drawable, n_drawables;

  g_return_if_fail (GIMP_IS_IMAGE (image));
  g_return_if_fail (precision != gimp_image_get_precision (image));
  g_return_if_fail (precision == GIMP_PRECISION_U8_GAMMA ||
                    gimp_image_get_base_type (image) != GIMP_INDEXED);
  g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress));

  all_drawables = g_list_concat (gimp_image_get_layer_list (image),
                                 gimp_image_get_channel_list (image));

  n_drawables = g_list_length (all_drawables) + 1 /* + selection */;

  if (progress)
    sub_progress = gimp_sub_progress_new (progress);

  switch (precision)
    {
    case GIMP_PRECISION_U8_LINEAR:
      undo_desc = C_("undo-type", "Convert Image to 8 bit linear integer");
      break;
    case GIMP_PRECISION_U8_GAMMA:
      undo_desc = C_("undo-type", "Convert Image to 8 bit gamma integer");
      break;
    case GIMP_PRECISION_U16_LINEAR:
      undo_desc = C_("undo-type", "Convert Image to 16 bit linear integer");
      break;
    case GIMP_PRECISION_U16_GAMMA:
      undo_desc = C_("undo-type", "Convert Image to 16 bit gamma integer");
      break;
    case GIMP_PRECISION_U32_LINEAR:
      undo_desc = C_("undo-type", "Convert Image to 32 bit linear integer");
      break;
    case GIMP_PRECISION_U32_GAMMA:
      undo_desc = C_("undo-type", "Convert Image to 32 bit gamma integer");
      break;
    case GIMP_PRECISION_HALF_LINEAR:
      undo_desc = C_("undo-type", "Convert Image to 16 bit linear floating point");
      break;
    case GIMP_PRECISION_HALF_GAMMA:
      undo_desc = C_("undo-type", "Convert Image to 16 bit gamma floating point");
      break;
    case GIMP_PRECISION_FLOAT_LINEAR:
      undo_desc = C_("undo-type", "Convert Image to 32 bit linear floating point");
      break;
    case GIMP_PRECISION_FLOAT_GAMMA:
      undo_desc = C_("undo-type", "Convert Image to 32 bit gamma floating point");
      break;
    case GIMP_PRECISION_DOUBLE_LINEAR:
      undo_desc = C_("undo-type", "Convert Image to 64 bit linear floating point");
      break;
    case GIMP_PRECISION_DOUBLE_GAMMA:
      undo_desc = C_("undo-type", "Convert Image to 64 bit gamma floating point");
      break;
    }

  if (progress)
    gimp_progress_start (progress, FALSE, "%s", undo_desc);

  g_object_freeze_notify (G_OBJECT (image));

  gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_CONVERT,
                               undo_desc);

  /*  Push the image precision to the stack  */
  gimp_image_undo_push_image_precision (image, NULL);

  old_profile = gimp_image_get_color_profile (image);
  old_format  = gimp_image_get_layer_format (image, FALSE);

  /*  Set the new precision  */
  g_object_set (image, "precision", precision, NULL);

  new_format = gimp_image_get_layer_format (image, FALSE);

  if (old_profile &&
      gimp_babl_format_get_linear (old_format) !=
      gimp_babl_format_get_linear (new_format))
    {
      GimpColorProfile *new_profile;

      /* when converting between linear and gamma, we create a new
       * profile using the original profile's chromacities and
       * whitepoint, but a linear/sRGB-gamma TRC.
       */

      if (gimp_babl_format_get_linear (new_format))
        {
          new_profile =
            gimp_color_profile_new_linear_from_color_profile (old_profile);
        }
      else
        {
          new_profile =
            gimp_color_profile_new_srgb_trc_from_color_profile (old_profile);
        }

      /* if a new profile cannot be be generated, convert to the
       * builtin profile, which is better than leaving the user with
       * broken colors
       */
      if (! new_profile)
        {
          new_profile = gimp_image_get_builtin_color_profile (image);
          g_object_ref (new_profile);
        }
    }

  for (list = all_drawables, nth_drawable = 0;
       list;
       list = g_list_next (list), nth_drawable++)
    {
      GimpDrawable *drawable = list->data;
      gint          dither_type;

      if (gimp_item_is_text_layer (GIMP_ITEM (drawable)))
        dither_type = text_layer_dither_type;
      else
        dither_type = layer_dither_type;

      if (sub_progress)
        gimp_sub_progress_set_step (GIMP_SUB_PROGRESS (sub_progress),
                                    nth_drawable, n_drawables);

      gimp_drawable_convert_type (drawable, image,
                                  gimp_drawable_get_base_type (drawable),
                                  precision,
                                  new_profile,
                                  dither_type,
                                  mask_dither_type,
                                  TRUE, sub_progress);
    }

  g_list_free (all_drawables);

  if (old_profile)
    {
      gimp_image_set_color_profile (image, new_profile, NULL);
      g_object_unref (new_profile);
    }

  /*  convert the selection mask  */
  {
    GimpChannel *mask = gimp_image_get_mask (image);
    GeglBuffer  *buffer;

    if (sub_progress)
      gimp_sub_progress_set_step (GIMP_SUB_PROGRESS (sub_progress),
                                  nth_drawable, n_drawables);

    gimp_image_undo_push_mask_precision (image, NULL, mask);

    buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
                                              gimp_image_get_width  (image),
                                              gimp_image_get_height (image)),
                              gimp_image_get_mask_format (image));

    gegl_buffer_copy (gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)), NULL,
                      GEGL_ABYSS_NONE,
                      buffer, NULL);

    gimp_drawable_set_buffer (GIMP_DRAWABLE (mask), FALSE, NULL, buffer);
    g_object_unref (buffer);

    nth_drawable++;
  }

  if (sub_progress)
    gimp_progress_set_value (sub_progress, 1.0);

  gimp_image_undo_group_end (image);

  gimp_image_precision_changed (image);
  g_object_thaw_notify (G_OBJECT (image));

  if (sub_progress)
    g_object_unref (sub_progress);

  if (progress)
    gimp_progress_end (progress);
}
static void
gimp_image_convert_profile_layers (GimpImage                *image,
                                   GimpColorProfile         *src_profile,
                                   GimpColorProfile         *dest_profile,
                                   GimpColorRenderingIntent  intent,
                                   gboolean                  bpc,
                                   GimpProgress             *progress)
{
  GList *layers;
  GList *list;
  gint   n_drawables  = 0;
  gint   nth_drawable = 0;

  layers = gimp_image_get_layer_list (image);

  for (list = layers; list; list = g_list_next (list))
    {
      if (! gimp_viewable_get_children (list->data))
        n_drawables++;
    }

  for (list = layers; list; list = g_list_next (list))
    {
      GimpDrawable *drawable     = list->data;
      GimpProgress *sub_progress = NULL;

      if (gimp_viewable_get_children (GIMP_VIEWABLE (drawable)))
        continue;

      if (progress)
        {
          sub_progress = gimp_sub_progress_new (progress);
          gimp_sub_progress_set_step (GIMP_SUB_PROGRESS (sub_progress),
                                      nth_drawable, n_drawables);
        }

      nth_drawable++;

      gimp_drawable_push_undo (drawable, NULL, NULL,
                               0, 0,
                               gimp_item_get_width  (GIMP_ITEM (drawable)),
                               gimp_item_get_height (GIMP_ITEM (drawable)));

      gimp_gegl_convert_color_profile (gimp_drawable_get_buffer (drawable),
                                       NULL,
                                       src_profile,
                                       gimp_drawable_get_buffer (drawable),
                                       NULL,
                                       dest_profile,
                                       intent, bpc,
                                       sub_progress);

      gimp_drawable_update (drawable, 0, 0,
                            gimp_item_get_width  (GIMP_ITEM (drawable)),
                            gimp_item_get_height (GIMP_ITEM (drawable)));

      if (sub_progress)
        g_object_unref (sub_progress);
    }

  g_list_free (layers);
}
void
gimp_image_scale (GimpImage             *image,
                  gint                   new_width,
                  gint                   new_height,
                  GimpInterpolationType  interpolation_type,
                  GimpProgress          *progress)
{
    GimpProgress *sub_progress;
    GList        *all_layers;
    GList        *all_channels;
    GList        *all_vectors;
    GList        *list;
    gint          old_width;
    gint          old_height;
    gint          offset_x;
    gint          offset_y;
    gdouble       img_scale_w      = 1.0;
    gdouble       img_scale_h      = 1.0;
    gint          progress_steps;
    gint          progress_current = 0;

    g_return_if_fail (GIMP_IS_IMAGE (image));
    g_return_if_fail (new_width > 0 && new_height > 0);
    g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress));

    gimp_set_busy (image->gimp);

    sub_progress = gimp_sub_progress_new (progress);

    all_layers   = gimp_image_get_layer_list (image);
    all_channels = gimp_image_get_channel_list (image);
    all_vectors  = gimp_image_get_vectors_list (image);

    progress_steps = (g_list_length (all_layers)   +
                      g_list_length (all_channels) +
                      g_list_length (all_vectors)  +
                      1 /* selection */);

    g_object_freeze_notify (G_OBJECT (image));

    gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_SCALE,
                                 C_("undo-type", "Scale Image"));

    old_width   = gimp_image_get_width  (image);
    old_height  = gimp_image_get_height (image);
    img_scale_w = (gdouble) new_width  / (gdouble) old_width;
    img_scale_h = (gdouble) new_height / (gdouble) old_height;

    offset_x = (old_width  - new_width)  / 2;
    offset_y = (old_height - new_height) / 2;

    /*  Push the image size to the stack  */
    gimp_image_undo_push_image_size (image,
                                     NULL,
                                     offset_x,
                                     offset_y,
                                     new_width,
                                     new_height);

    /*  Set the new width and height early, so below image item setters
     *  (esp. guides and sample points) don't choke about moving stuff
     *  out of the image
     */
    g_object_set (image,
                  "width",  new_width,
                  "height", new_height,
                  NULL);

    /*  Scale all channels  */
    for (list = all_channels; list; list = g_list_next (list))
    {
        GimpItem *item = list->data;

        gimp_sub_progress_set_step (GIMP_SUB_PROGRESS (sub_progress),
                                    progress_current++, progress_steps);

        gimp_item_scale (item,
                         new_width, new_height, 0, 0,
                         interpolation_type, sub_progress);
    }

    /*  Scale all vectors  */
    for (list = all_vectors; list; list = g_list_next (list))
    {
        GimpItem *item = list->data;

        gimp_sub_progress_set_step (GIMP_SUB_PROGRESS (sub_progress),
                                    progress_current++, progress_steps);

        gimp_item_scale (item,
                         new_width, new_height, 0, 0,
                         interpolation_type, sub_progress);
    }

    /*  Don't forget the selection mask!  */
    gimp_sub_progress_set_step (GIMP_SUB_PROGRESS (sub_progress),
                                progress_current++, progress_steps);

    gimp_item_scale (GIMP_ITEM (gimp_image_get_mask (image)),
                     new_width, new_height, 0, 0,
                     interpolation_type, sub_progress);

    /*  Scale all layers  */
    for (list = all_layers; list; list = g_list_next (list))
    {
        GimpItem *item = list->data;

        gimp_sub_progress_set_step (GIMP_SUB_PROGRESS (sub_progress),
                                    progress_current++, progress_steps);

        /*  group layers are updated automatically  */
        if (gimp_viewable_get_children (GIMP_VIEWABLE (item)))
        {
            gimp_group_layer_suspend_resize (GIMP_GROUP_LAYER (item), FALSE);
            continue;
        }

        if (! gimp_item_scale_by_factors (item,
                                          img_scale_w, img_scale_h,
                                          interpolation_type, sub_progress))
        {
            /* Since 0 < img_scale_w, img_scale_h, failure due to one or more
             * vanishing scaled layer dimensions. Implicit delete implemented
             * here. Upstream warning implemented in resize_check_layer_scaling(),
             * which offers the user the chance to bail out.
             */
            gimp_image_remove_layer (image, GIMP_LAYER (item), TRUE, NULL);
        }
    }

    for (list = all_layers; list; list = g_list_next (list))
        if (gimp_viewable_get_children (list->data))
            gimp_group_layer_resume_resize (list->data, FALSE);


    /*  Scale all Guides  */
    for (list = gimp_image_get_guides (image);
            list;
            list = g_list_next (list))
    {
        GimpGuide *guide    = list->data;
        gint       position = gimp_guide_get_position (guide);

        switch (gimp_guide_get_orientation (guide))
        {
        case GIMP_ORIENTATION_HORIZONTAL:
            gimp_image_move_guide (image, guide,
                                   (position * new_height) / old_height,
                                   TRUE);
            break;

        case GIMP_ORIENTATION_VERTICAL:
            gimp_image_move_guide (image, guide,
                                   (position * new_width) / old_width,
                                   TRUE);
            break;

        default:
            break;
        }
    }

    /*  Scale all sample points  */
    for (list = gimp_image_get_sample_points (image);
            list;
            list = g_list_next (list))
    {
        GimpSamplePoint *sample_point = list->data;

        gimp_image_move_sample_point (image, sample_point,
                                      sample_point->x * new_width  / old_width,
                                      sample_point->y * new_height / old_height,
                                      TRUE);
    }

    gimp_image_undo_group_end (image);

    g_list_free (all_layers);
    g_list_free (all_channels);
    g_list_free (all_vectors);

    g_object_unref (sub_progress);

    gimp_image_size_changed_detailed (image,
                                      -offset_x,
                                      -offset_y,
                                      old_width,
                                      old_height);

    g_object_thaw_notify (G_OBJECT (image));

    gimp_unset_busy (image->gimp);
}
/**
 * gimp_image_scale_check:
 * @image:      A #GimpImage.
 * @new_width:   The new width.
 * @new_height:  The new height.
 * @max_memsize: The maximum new memory size.
 * @new_memsize: The new memory size.
 *
 * Inventory the layer list in image and check that it may be
 * scaled to @new_height and @new_width without problems.
 *
 * Return value: #GIMP_IMAGE_SCALE_OK if scaling the image will shrink none
 *               of its layers completely away, and the new image size
 *               is smaller than @max_memsize.
 *               #GIMP_IMAGE_SCALE_TOO_SMALL if scaling would remove some
 *               existing layers.
 *               #GIMP_IMAGE_SCALE_TOO_BIG if the new image size would
 *               exceed the maximum specified in the preferences.
 **/
GimpImageScaleCheckType
gimp_image_scale_check (const GimpImage *image,
                        gint             new_width,
                        gint             new_height,
                        gint64           max_memsize,
                        gint64          *new_memsize)
{
    GList  *drawables;
    GList  *all_layers;
    GList  *list;
    gint64  current_size;
    gint64  scalable_size;
    gint64  scaled_size;
    gint64  undo_size;
    gint64  redo_size;
    gint64  fixed_size;
    gint64  new_size;

    g_return_val_if_fail (GIMP_IS_IMAGE (image), GIMP_IMAGE_SCALE_TOO_SMALL);
    g_return_val_if_fail (new_memsize != NULL, GIMP_IMAGE_SCALE_TOO_SMALL);

    current_size = gimp_object_get_memsize (GIMP_OBJECT (image), NULL);

    /*  the part of the image's memsize that scales linearly with the image  */
    drawables = gimp_image_item_list_get_list (image, NULL,
                GIMP_ITEM_TYPE_LAYERS |
                GIMP_ITEM_TYPE_CHANNELS,
                GIMP_ITEM_SET_ALL);

    gimp_image_item_list_filter (NULL, drawables, TRUE, FALSE);

    drawables = g_list_prepend (drawables, gimp_image_get_mask (image));

    scalable_size = 0;
    scaled_size   = 0;

    for (list = drawables; list; list = g_list_next (list))
    {
        GimpDrawable *drawable = list->data;
        gdouble       width    = gimp_item_get_width  (GIMP_ITEM (drawable));
        gdouble       height   = gimp_item_get_height (GIMP_ITEM (drawable));

        scalable_size +=
            gimp_drawable_estimate_memsize (drawable,
                                            width, height);

        scaled_size +=
            gimp_drawable_estimate_memsize (drawable,
                                            width * new_width /
                                            gimp_image_get_width (image),
                                            height * new_height /
                                            gimp_image_get_height (image));
    }

    g_list_free (drawables);

    scalable_size +=
        gimp_projection_estimate_memsize (gimp_image_get_base_type (image),
                                          gimp_image_get_precision (image),
                                          gimp_image_get_width (image),
                                          gimp_image_get_height (image));

    scaled_size +=
        gimp_projection_estimate_memsize (gimp_image_get_base_type (image),
                                          gimp_image_get_precision (image),
                                          new_width, new_height);

    GIMP_LOG (IMAGE_SCALE,
              "scalable_size = %"G_GINT64_FORMAT"  scaled_size = %"G_GINT64_FORMAT,
              scalable_size, scaled_size);

    undo_size = gimp_object_get_memsize (GIMP_OBJECT (gimp_image_get_undo_stack (image)), NULL);
    redo_size = gimp_object_get_memsize (GIMP_OBJECT (gimp_image_get_redo_stack (image)), NULL);

    /*  the fixed part of the image's memsize w/o any undo information  */
    fixed_size = current_size - undo_size - redo_size - scalable_size;

    /*  calculate the new size, which is:  */
    new_size = (fixed_size +   /*  the fixed part                */
                scaled_size);  /*  plus the part that scales...  */

    GIMP_LOG (IMAGE_SCALE,
              "old_size = %"G_GINT64_FORMAT"  new_size = %"G_GINT64_FORMAT,
              current_size - undo_size - redo_size, new_size);

    *new_memsize = new_size;

    if (new_size > current_size && new_size > max_memsize)
        return GIMP_IMAGE_SCALE_TOO_BIG;

    all_layers = gimp_image_get_layer_list (image);

    for (list = all_layers; list; list = g_list_next (list))
    {
        GimpItem *item = list->data;

        /*  group layers are updated automatically  */
        if (gimp_viewable_get_children (GIMP_VIEWABLE (item)))
            continue;

        if (! gimp_item_check_scaling (item, new_width, new_height))
        {
            g_list_free (all_layers);

            return GIMP_IMAGE_SCALE_TOO_SMALL;
        }
    }

    g_list_free (all_layers);

    return GIMP_IMAGE_SCALE_OK;
}