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;
}
static void
gimp_image_prop_view_label_set_memsize (GtkWidget  *label,
                                        GimpObject *object)
{
  gchar *str = g_format_size (gimp_object_get_memsize (object, NULL));
  gtk_label_set_text (GTK_LABEL (label), str);
  g_free (str);
}
Beispiel #3
0
static gint64
gimp_undo_stack_get_memsize (GimpObject *object,
                             gint64     *gui_size)
{
  GimpUndoStack *stack   = GIMP_UNDO_STACK (object);
  gint64         memsize = 0;

  memsize += gimp_object_get_memsize (GIMP_OBJECT (stack->undos), gui_size);

  return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
                                                                  gui_size);
}
Beispiel #4
0
static gint64
gimp_item_get_memsize (GimpObject *object,
                       gint64     *gui_size)
{
  GimpItem *item    = GIMP_ITEM (object);
  gint64    memsize = 0;

  memsize += gimp_object_get_memsize (GIMP_OBJECT (item->parasites), gui_size);

  return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
                                                                  gui_size);
}
Beispiel #5
0
static gint64
gimp_text_layer_get_memsize (GimpObject *object,
                             gint64     *gui_size)
{
  GimpTextLayer *text_layer = GIMP_TEXT_LAYER (object);
  gint64         memsize    = 0;

  memsize += gimp_object_get_memsize (GIMP_OBJECT (text_layer->text),
                                      gui_size);

  return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
                                                                  gui_size);
}
Beispiel #6
0
static gint64
gimp_text_undo_get_memsize (GimpObject *object,
                            gint64     *gui_size)
{
  GimpTextUndo *undo    = GIMP_TEXT_UNDO (object);
  gint64        memsize = 0;

  memsize += gimp_g_value_get_memsize (undo->value);
  memsize += gimp_object_get_memsize (GIMP_OBJECT (undo->text), NULL);

  return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
                                                                  gui_size);
}
Beispiel #7
0
void
debug_mem_profile_cmd_callback (GtkAction *action,
                                gpointer   data)
{
  extern gboolean  gimp_debug_memsize;
  Gimp            *gimp;
  return_if_no_gimp (gimp, data);

  gimp_debug_memsize = TRUE;

  gimp_object_get_memsize (GIMP_OBJECT (gimp), NULL);

  gimp_debug_memsize = FALSE;
}
Beispiel #8
0
static gint64
gimp_channel_undo_get_memsize (GimpObject *object,
                               gint64     *gui_size)
{
  GimpItemUndo *item_undo = GIMP_ITEM_UNDO (object);
  gint64        memsize   = 0;

  if (! gimp_item_is_attached (item_undo->item))
    memsize += gimp_object_get_memsize (GIMP_OBJECT (item_undo->item),
                                        gui_size);

  return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
                                                                  gui_size);
}
Beispiel #9
0
static gint64
gimp_vectors_mod_undo_get_memsize (GimpObject *object,
                                   gint64     *gui_size)
{
  GimpVectorsModUndo *vectors_mod_undo = GIMP_VECTORS_MOD_UNDO (object);
  gint64              memsize          = 0;

  if (vectors_mod_undo->vectors)
    memsize += gimp_object_get_memsize (GIMP_OBJECT (vectors_mod_undo->vectors),
                                        gui_size);

  return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
                                                                  gui_size);
}
static gint64
gimp_layer_mask_undo_get_memsize (GimpObject *object,
                                  gint64     *gui_size)
{
  GimpLayerMaskUndo *layer_mask_undo = GIMP_LAYER_MASK_UNDO (object);
  GimpLayer         *layer           = GIMP_LAYER (GIMP_ITEM_UNDO (object)->item);
  gint64             memsize         = 0;

  /* don't use !gimp_item_is_attached() here */
  if (gimp_layer_get_mask (layer) != layer_mask_undo->layer_mask)
    memsize += gimp_object_get_memsize (GIMP_OBJECT (layer_mask_undo->layer_mask),
                                        gui_size);

  return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
                                                                  gui_size);
}
Beispiel #11
0
static gint64
gimp_vectors_get_memsize (GimpObject *object,
                          gint64     *gui_size)
{
  GimpVectors *vectors;
  GList       *list;
  gint64       memsize = 0;

  vectors = GIMP_VECTORS (object);

  for (list = vectors->strokes; list; list = g_list_next (list))
    memsize += (gimp_object_get_memsize (GIMP_OBJECT (list->data), gui_size) +
                sizeof (GList));

  return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
                                                                  gui_size);
}
static gint64
gimp_image_undo_get_memsize (GimpObject *object,
                             gint64     *gui_size)
{
  GimpImageUndo *image_undo = GIMP_IMAGE_UNDO (object);
  gint64         memsize    = 0;

  if (image_undo->colormap)
    memsize += GIMP_IMAGE_COLORMAP_SIZE;

  memsize += gimp_object_get_memsize (GIMP_OBJECT (image_undo->grid),
                                      gui_size);
  memsize += gimp_string_get_memsize (image_undo->parasite_name);
  memsize += gimp_parasite_get_memsize (image_undo->parasite, gui_size);

  return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
                                                                  gui_size);
}
Beispiel #13
0
static gint64
gimp_brush_pipe_get_memsize (GimpObject *object,
                             gint64     *gui_size)
{
  GimpBrushPipe *pipe    = GIMP_BRUSH_PIPE (object);
  gint64         memsize = 0;
  gint           i;

  memsize += pipe->dimension * (sizeof (gint) /* rank   */ +
                                sizeof (gint) /* stride */ +
                                sizeof (PipeSelectModes));

  for (i = 0; i < pipe->n_brushes; i++)
    memsize += gimp_object_get_memsize (GIMP_OBJECT (pipe->brushes[i]),
                                        gui_size);

  return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
                                                                  gui_size);
}
Beispiel #14
0
static gint64
gimp_list_get_memsize (GimpObject *object,
                       gint64     *gui_size)
{
  GimpList *list    = GIMP_LIST (object);
  gint64    memsize = 0;

  memsize += (gimp_container_num_children (GIMP_CONTAINER (list)) *
              sizeof (GList));

  if (gimp_container_policy (GIMP_CONTAINER (list)) ==
      GIMP_CONTAINER_POLICY_STRONG)
    {
      GList *glist;

      for (glist = list->list; glist; glist = g_list_next (glist))
        memsize += gimp_object_get_memsize (GIMP_OBJECT (glist->data), gui_size);
    }

  return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
                                                                  gui_size);
}
Beispiel #15
0
static void
gimp_image_prop_view_label_set_undo (GtkWidget     *label,
                                     GimpUndoStack *stack)
{
  gint steps = gimp_undo_stack_get_depth (stack);

  if (steps > 0)
    {
      GimpObject *object = GIMP_OBJECT (stack);
      gchar      *str;
      gchar       buf[256];

      str = g_format_size (gimp_object_get_memsize (object, NULL));
      g_snprintf (buf, sizeof (buf), "%d (%s)", steps, str);
      g_free (str);

      gtk_label_set_text (GTK_LABEL (label), buf);
    }
  else
    {
      /*  no undo (or redo) steps available  */
      gtk_label_set_text (GTK_LABEL (label), _("None"));
    }
}
/**
 * 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;
}
Beispiel #17
0
static gint64
gimp_get_memsize (GimpObject *object,
                  gint64     *gui_size)
{
  Gimp   *gimp    = GIMP (object);
  gint64  memsize = 0;

  memsize += gimp_g_list_get_memsize (gimp->user_units, 0 /* FIXME */);

  memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->parasites),
                                      gui_size);

  memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->paint_info_list),
                                      gui_size);

  memsize += gimp_g_object_get_memsize (G_OBJECT (gimp->module_db));
  memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->plug_in_manager),
                                      gui_size);

  memsize += gimp_g_list_get_memsize_foreach (gimp->filter_history,
                                              (GimpMemsizeFunc)
                                              gimp_object_get_memsize,
                                              gui_size);

  memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->image_table), 0);
  memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->item_table),  0);

  memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->displays), gui_size);

  memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->global_buffer),
                                      gui_size);

  memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->named_buffers),
                                      gui_size);
  memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->fonts),
                                      gui_size);
  memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->brush_factory),
                                      gui_size);
  memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->dynamics_factory),
                                      gui_size);
  memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->mybrush_factory),
                                      gui_size);
  memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->pattern_factory),
                                      gui_size);
  memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->gradient_factory),
                                      gui_size);
  memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->palette_factory),
                                      gui_size);
  memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->tool_preset_factory),
                                      gui_size);

  memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->tag_cache),
                                      gui_size);

  memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->pdb), gui_size);

  memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->tool_info_list),
                                      gui_size);
  memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->standard_tool_info),
                                      gui_size);
  memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->documents),
                                      gui_size);
  memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->templates),
                                      gui_size);
  memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->image_new_last_template),
                                      gui_size);

  memsize += gimp_g_list_get_memsize (gimp->context_list, 0);

  memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->default_context),
                                      gui_size);
  memsize += gimp_object_get_memsize (GIMP_OBJECT (gimp->user_context),
                                      gui_size);

  return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
                                                                  gui_size);
}
Beispiel #18
0
void
edit_undo_clear_cmd_callback (GtkAction *action,
                              gpointer   data)
{
  GimpImage     *image;
  GimpUndoStack *undo_stack;
  GimpUndoStack *redo_stack;
  GtkWidget     *widget;
  GtkWidget     *dialog;
  gchar         *size;
  gint64         memsize;
  gint64         guisize;
  return_if_no_image (image, data);
  return_if_no_widget (widget, data);

  dialog = gimp_message_dialog_new (_("Clear Undo History"), GIMP_STOCK_WARNING,
                                    widget,
                                    GTK_DIALOG_MODAL |
                                    GTK_DIALOG_DESTROY_WITH_PARENT,
                                    gimp_standard_help_func,
                                    GIMP_HELP_EDIT_UNDO_CLEAR,

                                    GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                                    GTK_STOCK_CLEAR,  GTK_RESPONSE_OK,

                                    NULL);

  gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
                                           GTK_RESPONSE_OK,
                                           GTK_RESPONSE_CANCEL,
                                           -1);

  g_signal_connect_object (gtk_widget_get_toplevel (widget), "unmap",
                           G_CALLBACK (gtk_widget_destroy),
                           dialog, G_CONNECT_SWAPPED);

  g_signal_connect_object (image, "disconnect",
                           G_CALLBACK (gtk_widget_destroy),
                           dialog, G_CONNECT_SWAPPED);

  gimp_message_box_set_primary_text (GIMP_MESSAGE_DIALOG (dialog)->box,
                                     _("Really clear image's undo history?"));

  undo_stack = gimp_image_get_undo_stack (image);
  redo_stack = gimp_image_get_redo_stack (image);

  memsize =  gimp_object_get_memsize (GIMP_OBJECT (undo_stack), &guisize);
  memsize += guisize;
  memsize += gimp_object_get_memsize (GIMP_OBJECT (redo_stack), &guisize);
  memsize += guisize;

  size = g_format_size (memsize);

  gimp_message_box_set_text (GIMP_MESSAGE_DIALOG (dialog)->box,
                             _("Clearing the undo history of this "
                               "image will gain %s of memory."), size);
  g_free (size);

  if (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK)
    {
      gimp_image_undo_disable (image);
      gimp_image_undo_enable (image);
      gimp_image_flush (image);
    }

  gtk_widget_destroy (dialog);
}
Beispiel #19
0
static gboolean
gimp_view_button_press_event (GtkWidget      *widget,
                              GdkEventButton *bevent)
{
    GimpView *view = GIMP_VIEW (widget);

#ifdef DEBUG_MEMSIZE
    if (bevent->type == GDK_BUTTON_PRESS && bevent->button == 2)
    {
        gimp_debug_memsize = TRUE;

        gimp_object_get_memsize (GIMP_OBJECT (view->viewable), NULL);

        gimp_debug_memsize = FALSE;
    }
#endif /* DEBUG_MEMSIZE */

    if (! view->clickable &&
            ! view->show_popup)
        return FALSE;

    if (! gtk_widget_get_realized (widget))
        return FALSE;

    if (bevent->type == GDK_BUTTON_PRESS)
    {
        if (gdk_event_triggers_context_menu ((GdkEvent *) bevent))
        {
            view->press_state = 0;

            g_signal_emit (widget, view_signals[CONTEXT], 0);
        }
        else if (bevent->button == 1)
        {
            gtk_grab_add (widget);

            view->has_grab    = TRUE;
            view->press_state = bevent->state;

            if (view->show_popup && view->viewable)
            {
                gimp_view_popup_show (widget, bevent,
                                      view->renderer->context,
                                      view->viewable,
                                      view->renderer->width,
                                      view->renderer->height,
                                      view->renderer->dot_for_dot);
            }
        }
        else
        {
            view->press_state = 0;

            if (bevent->button == 2)
                gimp_view_popup_show (widget, bevent,
                                      view->renderer->context,
                                      view->viewable,
                                      view->renderer->width,
                                      view->renderer->height,
                                      view->renderer->dot_for_dot);

            return FALSE;
        }
    }
    else if (bevent->type == GDK_2BUTTON_PRESS)
    {
        if (bevent->button == 1)
            g_signal_emit (widget, view_signals[DOUBLE_CLICKED], 0);
    }

    return view->eat_button_events ? TRUE : FALSE;
}
static void
gimp_display_shell_format_title (GimpDisplayShell *shell,
                                 gchar            *title,
                                 gint              title_len,
                                 const gchar      *format)
{
  Gimp      *gimp;
  GimpImage *image;
  gint       num, denom;
  gint       i = 0;

  g_return_if_fail (GIMP_IS_DISPLAY_SHELL (shell));

  image = shell->display->image;
  gimp  = image->gimp;

  gimp_zoom_model_get_fraction (shell->zoom, &num, &denom);

  while (i < title_len && *format)
    {
      switch (*format)
        {
        case '%':
          format++;
          switch (*format)
            {
            case 0:
              /* format string ends within %-sequence, print literal '%' */

            case '%':
              title[i++] = '%';
              break;

            case 'f': /* pruned filename */
              {
                const gchar *uri = gimp_image_get_uri (image);
                gchar       *basename;

                basename = file_utils_uri_display_basename (uri);

                i += print (title, title_len, i, "%s", basename);

                g_free (basename);
              }
              break;

            case 'F': /* full filename */
              {
                gchar *filename;
                const gchar *uri = gimp_image_get_uri (image);

                filename = file_utils_uri_display_name (uri);

                i += print (title, title_len, i, "%s", filename);

                g_free (filename);
              }
              break;

            case 'p': /* PDB id */
              i += print (title, title_len, i, "%d", gimp_image_get_ID (image));
              break;

            case 'i': /* instance */
              i += print (title, title_len, i, "%d", shell->display->instance);
              break;

            case 't': /* type */
              {
                const gchar *image_type_str = NULL;
                gboolean     empty          = gimp_image_is_empty (image);

                switch (gimp_image_base_type (image))
                  {
                  case GIMP_RGB:
                    image_type_str = empty ? _("RGB-empty") : _("RGB");
                    break;
                  case GIMP_GRAY:
                    image_type_str = empty ? _("grayscale-empty") : _("grayscale");
                    break;
                  case GIMP_INDEXED:
                    image_type_str = empty ? _("indexed-empty") : _("indexed");
                    break;
                  default:
                    g_assert_not_reached ();
                    break;
                  }

                i += print (title, title_len, i, "%s", image_type_str);
              }
              break;

            case 's': /* user source zoom factor */
              i += print (title, title_len, i, "%d", denom);
              break;

            case 'd': /* user destination zoom factor */
              i += print (title, title_len, i, "%d", num);
              break;

            case 'z': /* user zoom factor (percentage) */
              {
                gdouble  scale = gimp_zoom_model_get_factor (shell->zoom);

                i += print (title, title_len, i,
                            scale >= 0.15 ? "%.0f" : "%.2f", 100.0 * scale);
              }
              break;

            case 'D': /* dirty flag */
              if (format[1] == 0)
                {
                  /* format string ends within %D-sequence, print literal '%D' */
                  i += print (title, title_len, i, "%%D");
                  break;
                }
              if (image->dirty)
                title[i++] = format[1];
              format++;
              break;

            case 'C': /* clean flag */
              if (format[1] == 0)
                {
                  /* format string ends within %C-sequence, print literal '%C' */
                  i += print (title, title_len, i, "%%C");
                  break;
                }
              if (! image->dirty)
                title[i++] = format[1];
              format++;
              break;

            case 'B': /* dirty flag (long) */
              if (image->dirty)
                i += print (title, title_len, i, "%s", _("(modified)"));
              break;

            case 'A': /* clean flag (long) */
              if (! image->dirty)
                i += print (title, title_len, i, "%s", _("(clean)"));
              break;

            case 'm': /* memory used by image */
              {
                GimpObject *object = GIMP_OBJECT (image);
                gchar      *str;

                str = gimp_memsize_to_string (gimp_object_get_memsize (object,
                                                                       NULL));

                i += print (title, title_len, i, "%s", str);

                g_free (str);
              }
              break;

            case 'l': /* number of layers */
              i += print (title, title_len, i, "%d",
                          gimp_container_num_children (image->layers));
              break;

            case 'L': /* number of layers (long) */
              {
                gint num = gimp_container_num_children (image->layers);

                i += print (title, title_len, i,
                            ngettext ("%d layer", "%d layers", num), num);
              }
              break;

            case 'n': /* active drawable name */
              {
                GimpDrawable *drawable = gimp_image_get_active_drawable (image);

                if (drawable)
                  i += print (title, title_len, i, "%s",
                              gimp_object_get_name (GIMP_OBJECT (drawable)));
                else
                  i += print (title, title_len, i, "%s", _("(none)"));
              }
              break;

            case 'P': /* active drawable PDB id */
              {
                GimpDrawable *drawable = gimp_image_get_active_drawable (image);

                if (drawable)
                  i += print (title, title_len, i, "%d",
                              gimp_item_get_ID (GIMP_ITEM (drawable)));
                else
                  i += print (title, title_len, i, "%s", _("(none)"));
              }
              break;

            case 'W': /* width in real-world units */
              if (shell->unit != GIMP_UNIT_PIXEL)
                {
                  gchar unit_format[8];

                  g_snprintf (unit_format, sizeof (unit_format), "%%.%df",
                              _gimp_unit_get_digits (gimp, shell->unit) + 1);
                  i += print (title, title_len, i, unit_format,
                              (image->width *
                               _gimp_unit_get_factor (gimp, shell->unit) /
                               image->xresolution));
                  break;
                }
              /* else fallthru */
            case 'w': /* width in pixels */
              i += print (title, title_len, i, "%d", image->width);
              break;

            case 'H': /* height in real-world units */
              if (shell->unit != GIMP_UNIT_PIXEL)
                {
                  gchar unit_format[8];

                  g_snprintf (unit_format, sizeof (unit_format), "%%.%df",
                              _gimp_unit_get_digits (gimp, shell->unit) + 1);
                  i += print (title, title_len, i, unit_format,
                              (image->height *
                               _gimp_unit_get_factor (gimp, shell->unit) /
                               image->yresolution));
                  break;
                }
              /* else fallthru */
            case 'h': /* height in pixels */
              i += print (title, title_len, i, "%d", image->height);
              break;

            case 'u': /* unit symbol */
              i += print (title, title_len, i, "%s",
                          _gimp_unit_get_symbol (gimp, shell->unit));
              break;

            case 'U': /* unit abbreviation */
              i += print (title, title_len, i, "%s",
                          _gimp_unit_get_abbreviation (gimp, shell->unit));
              break;

              /* Other cool things to be added:
               * %r = xresolution
               * %R = yresolution
               * %ø = image's fractal dimension
               * %þ = the answer to everything
               */

            default:
              /* format string contains unknown %-sequence, print it literally */
              i += print (title, title_len, i, "%%%c", *format);
              break;
            }
          break;

        default:
          title[i++] = *format;
          break;
        }

      format++;
    }

  title[MIN (i, title_len - 1)] = '\0';
}
static gint
gimp_display_shell_format_title (GimpDisplayShell *shell,
                                 gchar            *title,
                                 gint              title_len,
                                 const gchar      *format)
{
  GimpImage *image;
  gint       num, denom;
  gint       i = 0;

  g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), 0);

  image = gimp_display_get_image (shell->display);

  if (! image)
    {
      title[0] = '\n';
      return 0;
    }

  gimp_zoom_model_get_fraction (shell->zoom, &num, &denom);

  while (i < title_len && *format)
    {
      switch (*format)
        {
        case '%':
          format++;
          switch (*format)
            {
            case 0:
              /* format string ends within %-sequence, print literal '%' */

            case '%':
              title[i++] = '%';
              break;

            case 'f': /* base filename */
              i += print (title, title_len, i, "%s",
                          gimp_image_get_display_name (image));
              break;

            case 'F': /* full filename */
              i += print (title, title_len, i, "%s",
                          gimp_image_get_display_path (image));
              break;

            case 'p': /* PDB id */
              i += print (title, title_len, i, "%d", gimp_image_get_ID (image));
              break;

            case 'i': /* instance */
              i += print (title, title_len, i, "%d",
                          gimp_display_get_instance (shell->display));
              break;

            case 't': /* image type */
              i += print (title, title_len, i, "%s %s",
                          gimp_display_shell_title_image_type (image),
                          gimp_display_shell_title_image_precision (image));
              break;

            case 'T': /* drawable type */
              {
                GimpDrawable *drawable = gimp_image_get_active_drawable (image);
                const Babl   *format   = gimp_drawable_get_format (drawable);

                if (drawable)
                  i += print (title, title_len, i, "%s",
                              gimp_babl_get_description (format));
              }
              break;

            case 's': /* user source zoom factor */
              i += print (title, title_len, i, "%d", denom);
              break;

            case 'd': /* user destination zoom factor */
              i += print (title, title_len, i, "%d", num);
              break;

            case 'z': /* user zoom factor (percentage) */
              {
                gdouble  scale = gimp_zoom_model_get_factor (shell->zoom);

                i += print (title, title_len, i,
                            scale >= 0.15 ? "%.0f" : "%.2f", 100.0 * scale);
              }
              break;

            case 'D': /* dirty flag */
              if (format[1] == 0)
                {
                  /* format string ends within %D-sequence, print literal '%D' */
                  i += print (title, title_len, i, "%%D");
                  break;
                }
              if (gimp_image_is_dirty (image))
                title[i++] = format[1];
              format++;
              break;

            case 'C': /* clean flag */
              if (format[1] == 0)
                {
                  /* format string ends within %C-sequence, print literal '%C' */
                  i += print (title, title_len, i, "%%C");
                  break;
                }
              if (! gimp_image_is_dirty (image))
                title[i++] = format[1];
              format++;
              break;

            case 'B': /* dirty flag (long) */
              if (gimp_image_is_dirty (image))
                i += print (title, title_len, i, "%s", _("(modified)"));
              break;

            case 'A': /* clean flag (long) */
              if (! gimp_image_is_dirty (image))
                i += print (title, title_len, i, "%s", _("(clean)"));
              break;

            case 'm': /* memory used by image */
              {
                GimpObject *object = GIMP_OBJECT (image);
                gchar      *str;

                str = g_format_size (gimp_object_get_memsize (object, NULL));
                i += print (title, title_len, i, "%s", str);
                g_free (str);
              }
              break;

            case 'M': /* image size in megapixels */
              i += print (title, title_len, i, "%.1f",
                          (gdouble) gimp_image_get_width (image) *
                          (gdouble) gimp_image_get_height (image) / 1000000.0);
              break;

            case 'l': /* number of layers */
              i += print (title, title_len, i, "%d",
                          gimp_image_get_n_layers (image));
              break;

            case 'L': /* number of layers (long) */
              {
                gint num = gimp_image_get_n_layers (image);

                i += print (title, title_len, i,
                            ngettext ("%d layer", "%d layers", num), num);
              }
              break;

            case 'n': /* active drawable name */
              {
                GimpDrawable *drawable = gimp_image_get_active_drawable (image);

                if (drawable)
                  {
                    gchar *desc;

                    desc = gimp_viewable_get_description (GIMP_VIEWABLE (drawable),
                                                          NULL);

                    i += print (title, title_len, i, "%s", desc);

                    g_free (desc);
                  }
                else
                  {
                    i += print (title, title_len, i, "%s", _("(none)"));
                  }
              }
              break;

            case 'P': /* active drawable PDB id */
              {
                GimpDrawable *drawable = gimp_image_get_active_drawable (image);

                if (drawable)
                  i += print (title, title_len, i, "%d",
                              gimp_item_get_ID (GIMP_ITEM (drawable)));
                else
                  i += print (title, title_len, i, "%s", _("(none)"));
              }
              break;

            case 'W': /* width in real-world units */
              if (shell->unit != GIMP_UNIT_PIXEL)
                {
                  gdouble xres;
                  gdouble yres;
                  gchar   unit_format[8];

                  gimp_image_get_resolution (image, &xres, &yres);

                  g_snprintf (unit_format, sizeof (unit_format), "%%.%df",
                              gimp_unit_get_digits (shell->unit) + 1);
                  i += print (title, title_len, i, unit_format,
                              gimp_pixels_to_units (gimp_image_get_width (image),
                                                    shell->unit, xres));
                  break;
                }
              /* else fallthru */
            case 'w': /* width in pixels */
              i += print (title, title_len, i, "%d",
                          gimp_image_get_width (image));
              break;

            case 'H': /* height in real-world units */
              if (shell->unit != GIMP_UNIT_PIXEL)
                {
                  gdouble xres;
                  gdouble yres;
                  gchar   unit_format[8];

                  gimp_image_get_resolution (image, &xres, &yres);

                  g_snprintf (unit_format, sizeof (unit_format), "%%.%df",
                              gimp_unit_get_digits (shell->unit) + 1);
                  i += print (title, title_len, i, unit_format,
                              gimp_pixels_to_units (gimp_image_get_height (image),
                                                    shell->unit, yres));
                  break;
                }
              /* else fallthru */
            case 'h': /* height in pixels */
              i += print (title, title_len, i, "%d",
                          gimp_image_get_height (image));
              break;

            case 'u': /* unit symbol */
              i += print (title, title_len, i, "%s",
                          gimp_unit_get_symbol (shell->unit));
              break;

            case 'U': /* unit abbreviation */
              i += print (title, title_len, i, "%s",
                          gimp_unit_get_abbreviation (shell->unit));
              break;

              /* Other cool things to be added:
               * %r = xresolution
               * %R = yresolution
               * %ø = image's fractal dimension
               * %þ = the answer to everything
               */

            default:
              /* format string contains unknown %-sequence, print it literally */
              i += print (title, title_len, i, "%%%c", *format);
              break;
            }
          break;

        default:
          title[i++] = *format;
          break;
        }

      format++;
    }

  title[MIN (i, title_len - 1)] = '\0';

  return i;
}