/* The GIMP -- an image manipulation program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include #include "core-types.h" #include "base/pixel-region.h" #include "base/temp-buf.h" #include "paint-funcs/paint-funcs.h" #include "config/gimpcoreconfig.h" #include "gimp.h" #include "gimpimage.h" #include "gimpimage-preview.h" #include "gimplayer.h" #include "gimplayermask.h" #include "gimplist.h" void gimp_image_get_preview_size (GimpViewable *viewable, gint size, gboolean is_popup, gboolean dot_for_dot, gint *width, gint *height) { GimpImage *gimage; gimage = GIMP_IMAGE (viewable); if (! gimage->gimp->config->layer_previews && ! is_popup) { *width = size; *height = size; return; } gimp_viewable_calc_preview_size (viewable, gimage->width, gimage->height, size, size, dot_for_dot, gimage->xresolution, gimage->yresolution, width, height, NULL); } gboolean gimp_image_get_popup_size (GimpViewable *viewable, gint width, gint height, gboolean dot_for_dot, gint *popup_width, gint *popup_height) { GimpImage *gimage; gimage = GIMP_IMAGE (viewable); if (! gimage->gimp->config->layer_previews) return FALSE; if (gimage->width > width || gimage->height > height) { gboolean scaling_up; gimp_viewable_calc_preview_size (viewable, gimage->width, gimage->height, MIN (width * 2, GIMP_VIEWABLE_MAX_POPUP_SIZE), MIN (height * 2, GIMP_VIEWABLE_MAX_POPUP_SIZE), dot_for_dot, 1.0, 1.0, popup_width, popup_height, &scaling_up); if (scaling_up) { *popup_width = gimage->width; *popup_height = gimage->height; } return TRUE; } return FALSE; } TempBuf * gimp_image_get_preview (GimpViewable *viewable, gint width, gint height) { GimpImage *gimage; gimage = GIMP_IMAGE (viewable); if (! gimage->gimp->config->layer_previews) return NULL; if (gimage->comp_preview_valid && gimage->comp_preview->width == width && gimage->comp_preview->height == height) { /* The easy way */ return gimage->comp_preview; } else { /* The hard way */ if (gimage->comp_preview) temp_buf_free (gimage->comp_preview); /* Actually construct the composite preview from the layer previews! * This might seem ridiculous, but it's actually the best way, given * a number of unsavory alternatives. */ gimage->comp_preview = gimp_image_get_new_preview (viewable, width, height); gimage->comp_preview_valid = TRUE; return gimage->comp_preview; } } TempBuf * gimp_image_get_new_preview (GimpViewable *viewable, gint width, gint height) { GimpImage *gimage; GimpLayer *layer; GimpLayer *floating_sel; PixelRegion src1PR, src2PR, maskPR; PixelRegion *mask; TempBuf *comp; TempBuf *layer_buf; TempBuf *mask_buf; GList *list; GSList *reverse_list = NULL; gdouble ratio; gint x, y, w, h; gint x1, y1, x2, y2; gint bytes; gboolean construct_flag; gboolean visible_components[MAX_CHANNELS] = { TRUE, TRUE, TRUE, TRUE }; gint off_x, off_y; gimage = GIMP_IMAGE (viewable); if (! gimage->gimp->config->layer_previews) return NULL; ratio = (gdouble) width / (gdouble) gimage->width; switch (gimp_image_base_type (gimage)) { case GIMP_RGB: case GIMP_INDEXED: bytes = 4; break; case GIMP_GRAY: bytes = 2; break; default: bytes = 0; g_assert_not_reached (); break; } /* The construction buffer */ comp = temp_buf_new (width, height, bytes, 0, 0, NULL); temp_buf_data_clear (comp); floating_sel = NULL; for (list = GIMP_LIST (gimage->layers)->list; list; list = g_list_next (list)) { layer = (GimpLayer *) list->data; /* only add layers that are visible to the list */ if (gimp_item_get_visible (GIMP_ITEM (layer))) { /* floating selections are added right above the layer * they are attached to */ if (gimp_layer_is_floating_sel (layer)) { floating_sel = layer; } else { if (floating_sel && floating_sel->fs.drawable == GIMP_DRAWABLE (layer)) { reverse_list = g_slist_prepend (reverse_list, floating_sel); } reverse_list = g_slist_prepend (reverse_list, layer); } } } construct_flag = FALSE; for (; reverse_list; reverse_list = g_slist_next (reverse_list)) { layer = (GimpLayer *) reverse_list->data; gimp_item_offsets (GIMP_ITEM (layer), &off_x, &off_y); x = (gint) RINT (ratio * off_x); y = (gint) RINT (ratio * off_y); w = (gint) RINT (ratio * gimp_item_width (GIMP_ITEM (layer))); h = (gint) RINT (ratio * gimp_item_height (GIMP_ITEM (layer))); if (w < 1 || h < 1) continue; x1 = CLAMP (x, 0, width); y1 = CLAMP (y, 0, height); x2 = CLAMP (x + w, 0, width); y2 = CLAMP (y + h, 0, height); src1PR.bytes = comp->bytes; src1PR.x = x1; src1PR.y = y1; src1PR.w = (x2 - x1); src1PR.h = (y2 - y1); src1PR.rowstride = comp->width * src1PR.bytes; src1PR.data = (temp_buf_data (comp) + y1 * src1PR.rowstride + x1 * src1PR.bytes); layer_buf = gimp_viewable_get_preview (GIMP_VIEWABLE (layer), w, h); g_assert (layer_buf); g_assert (layer_buf->bytes <= comp->bytes); src2PR.bytes = layer_buf->bytes; src2PR.x = src1PR.x; src2PR.y = src1PR.y; src2PR.w = src1PR.w; src2PR.h = src1PR.h; src2PR.rowstride = layer_buf->width * src2PR.bytes; src2PR.data = (temp_buf_data (layer_buf) + (y1 - y) * src2PR.rowstride + (x1 - x) * src2PR.bytes); if (layer->mask && layer->mask->apply_mask) { mask_buf = gimp_viewable_get_preview (GIMP_VIEWABLE (layer->mask), w, h); maskPR.bytes = mask_buf->bytes; maskPR.x = src1PR.x; maskPR.y = src1PR.y; maskPR.w = src1PR.w; maskPR.h = src1PR.h; maskPR.rowstride = mask_buf->width * mask_buf->bytes; maskPR.data = (mask_buf_data (mask_buf) + (y1 - y) * maskPR.rowstride + (x1 - x) * maskPR.bytes); mask = &maskPR; } else { mask = NULL; } /* Based on the type of the layer, project the layer onto the * composite preview... * Indexed images are actually already converted to RGB and RGBA, * so just project them as if they were type "intensity" * Send in all TRUE for visible since that info doesn't matter * for previews */ if (gimp_drawable_has_alpha (GIMP_DRAWABLE (layer))) { if (! construct_flag) initial_region (&src2PR, &src1PR, mask, NULL, layer->opacity * 255.999, layer->mode, visible_components, INITIAL_INTENSITY_ALPHA); else combine_regions (&src1PR, &src2PR, &src1PR, mask, NULL, layer->opacity * 255.999, layer->mode, visible_components, COMBINE_INTEN_A_INTEN_A); } else { if (! construct_flag) initial_region (&src2PR, &src1PR, mask, NULL, layer->opacity * 255.999, layer->mode, visible_components, INITIAL_INTENSITY); else combine_regions (&src1PR, &src2PR, &src1PR, mask, NULL, layer->opacity * 255.999, layer->mode, visible_components, COMBINE_INTEN_A_INTEN); } construct_flag = TRUE; } g_slist_free (reverse_list); return comp; }