/* 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 "libgimpcolor/gimpcolor.h" #include "core-types.h" #include "base/pixel-region.h" #include "base/tile-manager.h" #include "paint-funcs/paint-funcs.h" #include "gimpimage.h" #include "gimplayer.h" #include "gimplayer-floating-sel.h" #include "gimplayermask.h" #include "gimplist.h" #include "gimpprojection.h" #include "gimpprojection-construct.h" /* local function prototypes */ static void gimp_projection_construct_layers (GimpProjection *proj, gint x, gint y, gint w, gint h); static void gimp_projection_construct_channels (GimpProjection *proj, gint x, gint y, gint w, gint h); static void gimp_projection_initialize (GimpProjection *proj, gint x, gint y, gint w, gint h); static void project_intensity (GimpProjection *proj, GimpLayer *layer, PixelRegion *src, PixelRegion *dest, PixelRegion *mask); static void project_intensity_alpha (GimpProjection *proj, GimpLayer *layer, PixelRegion *src, PixelRegion *dest, PixelRegion *mask); static void project_indexed (GimpProjection *proj, GimpLayer *layer, PixelRegion *src, PixelRegion *dest); static void project_indexed_alpha (GimpProjection *proj, GimpLayer *layer, PixelRegion *src, PixelRegion *dest, PixelRegion *mask); static void project_channel (GimpProjection *proj, GimpChannel *channel, PixelRegion *src, PixelRegion *src2); /* public functions */ void gimp_projection_construct (GimpProjection *proj, gint x, gint y, gint w, gint h) { g_return_if_fail (GIMP_IS_PROJECTION (proj)); #if 0 gint xoff; gint yoff; /* set the construct flag, used to determine if anything * has been written to the gimage raw image yet. */ gimage->construct_flag = FALSE; if (gimage->layers) { gimp_item_offsets (GIMP_ITEM (gimage->layers->data), &xoff, &yoff); } if ((gimage->layers) && /* There's a layer. */ (! g_slist_next (gimage->layers)) && /* It's the only layer. */ (gimp_drawable_has_alpha (GIMP_DRAWABLE (gimage->layers->data))) && /* It's !flat. */ (gimp_item_get_visible (GIMP_ITEM (gimage->layers->data))) && /* It's visible. */ (gimp_item_width (GIMP_ITEM (gimage->layers->data)) == gimage->width) && (gimp_item_height (GIMP_ITEM (gimage->layers->data)) == gimage->height) && /* Covers all. */ (!gimp_drawable_is_indexed (GIMP_DRAWABLE (gimage->layers->data))) && /* Not indexed. */ (((GimpLayer *)(gimage->layers->data))->opacity == GIMP_OPACITY_OPAQUE) /* Opaque */ ) { gint xoff; gint yoff; gimp_item_offsets (GIMP_ITEM (gimage->layers->data), &xoff, &yoff); if ((xoff==0) && (yoff==0)) /* Starts at 0,0 */ { PixelRegion srcPR, destPR; gpointer pr; g_warning("Can use cow-projection hack. Yay!"); pixel_region_init (&srcPR, gimp_drawable_data (GIMP_DRAWABLE (gimage->layers->data)), x, y, w,h, FALSE); pixel_region_init (&destPR, gimp_image_projection (gimage), x, y, w,h, TRUE); for (pr = pixel_regions_register (2, &srcPR, &destPR); pr != NULL; pr = pixel_regions_process (pr)) { tile_manager_map_over_tile (destPR.tiles, destPR.curtile, srcPR.curtile); } gimage->construct_flag = TRUE; gimp_image_construct_channels (gimage, x, y, w, h); return; } } #else proj->construct_flag = FALSE; #endif /* First, determine if the projection image needs to be * initialized--this is the case when there are no visible * layers that cover the entire canvas--either because layers * are offset or only a floating selection is visible */ gimp_projection_initialize (proj, x, y, w, h); /* call functions which process the list of layers and * the list of channels */ gimp_projection_construct_layers (proj, x, y, w, h); gimp_projection_construct_channels (proj, x, y, w, h); } /* private functions */ static void gimp_projection_construct_layers (GimpProjection *proj, gint x, gint y, gint w, gint h) { GimpLayer *layer; gint x1, y1, x2, y2; PixelRegion src1PR, src2PR, maskPR; PixelRegion * mask; GList *list; GList *reverse_list; gint off_x; gint off_y; /* composite the floating selection if it exists */ if ((layer = gimp_image_floating_sel (proj->gimage))) floating_sel_composite (layer, x, y, w, h, FALSE); reverse_list = NULL; for (list = GIMP_LIST (proj->gimage->layers)->list; list; list = g_list_next (list)) { layer = (GimpLayer *) list->data; /* only add layers that are visible and not floating selections * to the list */ if (! gimp_layer_is_floating_sel (layer) && gimp_item_get_visible (GIMP_ITEM (layer))) { reverse_list = g_list_prepend (reverse_list, layer); } } for (list = reverse_list; list; list = g_list_next (list)) { layer = (GimpLayer *) list->data; gimp_item_offsets (GIMP_ITEM (layer), &off_x, &off_y); x1 = CLAMP (off_x, x, x + w); y1 = CLAMP (off_y, y, y + h); x2 = CLAMP (off_x + gimp_item_width (GIMP_ITEM (layer)), x, x + w); y2 = CLAMP (off_y + gimp_item_height (GIMP_ITEM (layer)), y, y + h); /* configure the pixel regions */ pixel_region_init (&src1PR, gimp_projection_get_tiles (proj), x1, y1, (x2 - x1), (y2 - y1), TRUE); /* If we're showing the layer mask instead of the layer... */ if (layer->mask && layer->mask->show_mask) { pixel_region_init (&src2PR, gimp_drawable_data (GIMP_DRAWABLE (layer->mask)), (x1 - off_x), (y1 - off_y), (x2 - x1), (y2 - y1), FALSE); copy_gray_to_region (&src2PR, &src1PR); } /* Otherwise, normal */ else { pixel_region_init (&src2PR, gimp_drawable_data (GIMP_DRAWABLE (layer)), (x1 - off_x), (y1 - off_y), (x2 - x1), (y2 - y1), FALSE); if (layer->mask && layer->mask->apply_mask) { pixel_region_init (&maskPR, gimp_drawable_data (GIMP_DRAWABLE (layer->mask)), (x1 - off_x), (y1 - off_y), (x2 - x1), (y2 - y1), FALSE); mask = &maskPR; } else mask = NULL; /* Based on the type of the layer, project the layer onto the * projection image... */ switch (gimp_drawable_type (GIMP_DRAWABLE (layer))) { case GIMP_RGB_IMAGE: case GIMP_GRAY_IMAGE: /* no mask possible */ project_intensity (proj, layer, &src2PR, &src1PR, mask); break; case GIMP_RGBA_IMAGE: case GIMP_GRAYA_IMAGE: project_intensity_alpha (proj, layer, &src2PR, &src1PR, mask); break; case GIMP_INDEXED_IMAGE: /* no mask possible */ project_indexed (proj, layer, &src2PR, &src1PR); break; case GIMP_INDEXEDA_IMAGE: project_indexed_alpha (proj, layer, &src2PR, &src1PR, mask); break; default: break; } } proj->construct_flag = TRUE; /* something was projected */ } g_list_free (reverse_list); } static void gimp_projection_construct_channels (GimpProjection *proj, gint x, gint y, gint w, gint h) { GimpChannel *channel; PixelRegion src1PR; PixelRegion src2PR; GList *list; GList *reverse_list = NULL; /* reverse the channel list */ for (list = GIMP_LIST (proj->gimage->channels)->list; list; list = g_list_next (list)) { reverse_list = g_list_prepend (reverse_list, list->data); } for (list = reverse_list; list; list = g_list_next (list)) { channel = (GimpChannel *) list->data; if (gimp_item_get_visible (GIMP_ITEM (channel))) { /* configure the pixel regions */ pixel_region_init (&src1PR, gimp_projection_get_tiles (proj), x, y, w, h, TRUE); pixel_region_init (&src2PR, gimp_drawable_data (GIMP_DRAWABLE (channel)), x, y, w, h, FALSE); project_channel (proj, channel, &src1PR, &src2PR); proj->construct_flag = TRUE; } } g_list_free (reverse_list); } static void gimp_projection_initialize (GimpProjection *proj, gint x, gint y, gint w, gint h) { GList *list; gboolean coverage = FALSE; /* this function determines whether a visible layer * provides complete coverage over the image. If not, * the projection is initialized to transparent */ for (list = GIMP_LIST (proj->gimage->layers)->list; list; list = g_list_next (list)) { GimpItem *item = list->data; gint off_x, off_y; gimp_item_offsets (item, &off_x, &off_y); if (gimp_item_get_visible (item) && ! gimp_drawable_has_alpha (GIMP_DRAWABLE (item)) && (off_x <= x) && (off_y <= y) && (off_x + gimp_item_width (item) >= x + w) && (off_y + gimp_item_height (item) >= y + h)) { coverage = TRUE; break; } } if (!coverage) { PixelRegion PR; guchar clear[4] = { 0, 0, 0, 0 }; pixel_region_init (&PR, gimp_projection_get_tiles (proj), x, y, w, h, TRUE); color_region (&PR, clear); } } static void project_intensity (GimpProjection *proj, GimpLayer *layer, PixelRegion *src, PixelRegion *dest, PixelRegion *mask) { if (! proj->construct_flag) initial_region (src, dest, mask, NULL, layer->opacity * 255.999, layer->mode, proj->gimage->visible, INITIAL_INTENSITY); else combine_regions (dest, src, dest, mask, NULL, layer->opacity * 255.999, layer->mode, proj->gimage->visible, COMBINE_INTEN_A_INTEN); } static void project_intensity_alpha (GimpProjection *proj, GimpLayer *layer, PixelRegion *src, PixelRegion *dest, PixelRegion *mask) { if (! proj->construct_flag) initial_region (src, dest, mask, NULL, layer->opacity * 255.999, layer->mode, proj->gimage->visible, INITIAL_INTENSITY_ALPHA); else combine_regions (dest, src, dest, mask, NULL, layer->opacity * 255.999, layer->mode, proj->gimage->visible, COMBINE_INTEN_A_INTEN_A); } static void project_indexed (GimpProjection *proj, GimpLayer *layer, PixelRegion *src, PixelRegion *dest) { g_return_if_fail (proj->gimage->cmap != NULL); if (! proj->construct_flag) initial_region (src, dest, NULL, proj->gimage->cmap, layer->opacity * 255.999, layer->mode, proj->gimage->visible, INITIAL_INDEXED); else g_warning ("%s: unable to project indexed image.", G_STRFUNC); } static void project_indexed_alpha (GimpProjection *proj, GimpLayer *layer, PixelRegion *src, PixelRegion *dest, PixelRegion *mask) { g_return_if_fail (proj->gimage->cmap != NULL); if (! proj->construct_flag) initial_region (src, dest, mask, proj->gimage->cmap, layer->opacity * 255.999, layer->mode, proj->gimage->visible, INITIAL_INDEXED_ALPHA); else combine_regions (dest, src, dest, mask, proj->gimage->cmap, layer->opacity * 255.999, layer->mode, proj->gimage->visible, COMBINE_INTEN_A_INDEXED_A); } static void project_channel (GimpProjection *proj, GimpChannel *channel, PixelRegion *src, PixelRegion *src2) { guchar col[3]; guchar opacity; gint type; gimp_rgba_get_uchar (&channel->color, &col[0], &col[1], &col[2], &opacity); if (! proj->construct_flag) { type = (channel->show_masked) ? INITIAL_CHANNEL_MASK : INITIAL_CHANNEL_SELECTION; initial_region (src2, src, NULL, col, opacity, GIMP_NORMAL_MODE, NULL, type); } else { type = (channel->show_masked) ? COMBINE_INTEN_A_CHANNEL_MASK : COMBINE_INTEN_A_CHANNEL_SELECTION; combine_regions (src, src2, src, NULL, col, opacity, GIMP_NORMAL_MODE, NULL, type); } }