1 /*
2 Adapt GIMP plugin to texture synthesis engine.
3 
4 Functions to read and write pixmaps and bytemaps from and to Gimp.
5 
6   Copyright (C) 2010, 2011  Lloyd Konneker
7 
8   This program is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 2 of the License, or
11   (at your option) any later version.
12 
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17 
18   You should have received a copy of the GNU General Public License
19   along with this program; if not, write to the Free Software
20   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21 */
22 
23 // Engine and Gimp types must be included previously
24 
25 
26 /*
27 Blit (copy a sub-rect) of source map to destination map.
28 Here, the entire pixel element of the map is copied.
29 
30 original c++ code, which only copied a byte:
31     for(gint y=0;y<temp_mask.height;y++)
32       for(gint x=0;x<temp_mask.width;x++)
33         mask.at(x+drawable_relative_x, y+drawable_relative_y)[0] =
34           temp_mask.at(x,y)[0]; // Only one channel (byte)
35 */
36 void
blit_map(Map * dest_map,Map * source_map,gint offset_x,gint offset_y)37 blit_map(
38   Map* dest_map,
39   Map* source_map,
40   gint offset_x,
41   gint offset_y
42   )
43 {
44   guint y;
45   guint x;
46 
47   for(y=0;y<source_map->height;y++)
48     for(x=0;x<source_map->width;x++)
49     {
50       Coordinates coords = {x,y};
51       Coordinates dest_coords = {x+offset_x, y+offset_y};
52 
53       *pixmap_index(dest_map, dest_coords) = *pixmap_index(source_map, coords);
54     }
55 }
56 
57 
58 /*
59 Copy some channels of pixmap to GimpDrawable.
60 (Usually just the color and alpha channels, omitting the map channel and other channels.)
61 
62 Unlike the original code:
63 - C instead of C++
64 - the origin is fixed at 0,0
65 - the Pixelel offset is fixed at 0.
66 The count of Pixelels moved is what drawable specifies, might be less than in pixmap.
67 That is, copy a slice of pixmap to drawable.
68 */
69 void
pixmap_to_drawable(Map map,GimpDrawable * drawable,guint pixelel_offset)70 pixmap_to_drawable(
71   Map map,
72   GimpDrawable *drawable,
73   guint pixelel_offset  // Index of starting Pixelel (channel) within Pixel sequence to move
74   )
75 {
76   GimpPixelRgn region;
77   guchar *img;
78 
79   /* Create a new, non-sparse sequence of Pixelels for Gimp. */
80   guint width = map.width;
81   guint height = map.height;
82   /* Count Pixelels to copy, whatever drawable wants, we have optional alpha Pixelel in our Pixel. */
83   guint pixelel_count = drawable->bpp;
84   gint size = width * height * pixelel_count;  // !!! Size of drawable, not the pixmap
85 
86   g_assert( pixelel_offset + pixelel_count <= map.depth ); // Pixmap has more pixelels than offset + count
87 
88   gimp_pixel_rgn_init(&region, drawable, 0,0, width, height, TRUE, TRUE);
89   img = g_malloc(size);
90 
91   {
92   guint i;
93   guint j;
94 
95   for(i=0; i<width*height; i++)  // Iterate over map as a sequence
96     for(j=0; j<pixelel_count; j++)  // Iterate over Pixelels
97       img[i*pixelel_count+j] =
98         g_array_index(map.data, Pixelel, i*map.depth+pixelel_offset+j);
99   }
100 
101   /* Send seq of Pixelels to Gimp. */
102   gimp_pixel_rgn_set_rect(&region, img, 0,0, width, height);
103   g_free(img);
104 }
105 
106 
107 /*
108 Copy SOME channels of GimpDrawable to pixmap, possibly offsetting them in the Pixel.
109 (Usually called many times, for image, then mask, then other drawables,
110 to interleave many drawables into one pixmap.)
111 Copy a sub-rect from the drawable.
112 */
113 static void
pixmap_from_drawable(Map map,GimpDrawable * drawable,gint x,gint y,gint pixelel_offset,guint pixelel_count_to_copy)114 pixmap_from_drawable(
115   Map map,
116   GimpDrawable *drawable,
117   gint x,                       /* origin of rect to copy. */
118   gint y,
119   gint pixelel_offset,          /* Which pixelels to copy to. */
120   guint pixelel_count_to_copy   /* Count of pixels to copy, might omit the alpha. */
121   )
122 {
123   GimpPixelRgn region;
124   guchar *img;
125 
126   /* !!! Note our pixmap is same width, height as drawable, but depths may differ. */
127   guint width = map.width;
128   guint height = map.height;
129   guint drawable_size = width * height * drawable->bpp;
130 
131   g_assert(drawable_size > 0);
132   /* Will fit in our Pixel */
133   g_assert( pixelel_count_to_copy + pixelel_offset <= map.depth );
134   /* Drawable has enough to copy */
135   g_assert( pixelel_count_to_copy <= drawable->bpp );
136 
137   gimp_pixel_rgn_init(&region, drawable, x,y, map.width, map.height, FALSE,FALSE);
138 
139   img = g_malloc(drawable_size);
140 
141   /*
142   Get all the pixelels from drawable into img sequence.
143   Note x1,y1 are in drawable coords i.e. relative to drawable
144   The drawable may be offset from the canvas and other drawables.
145   */
146   gimp_pixel_rgn_get_rect(&region, img, x,y, width,height);
147 
148   {
149   guint i;
150   guint j;
151 
152   /* Copy SOME of the pixels from img sequence to our pixmap, OFFSET them. */
153   for(i=0; i<width*height; i++)
154     for(j=0; j<pixelel_count_to_copy; j++)  /* Count can be different from strides. */
155         g_array_index(map.data, Pixelel, i*map.depth+pixelel_offset+j)  /* Stride is depth of pixmap. */
156           = img[i*drawable->bpp+j];   /* Stride is bpp */
157   }
158 
159   g_free(img);
160 }
161 
162 
163 
164 /*
165 Get drawable and a selection mask for it from GIMP.
166 
167 May 2010 lkk Heavily revised:
168  - to fix handling of selection
169  - C++ -> C
170  - break into separate routines
171  - interleave the mask pixelel into the Pixel
172 
173 Fetch a local copy of a selection channel (or other mask?) from Gimp,
174 or create one if no selection exists or selection exists but does not intersect drawable.
175 */
176 
177 static void
fetch_mask(GimpDrawable * drawable,Map * mask,Pixelel default_mask_value)178 fetch_mask(
179   GimpDrawable *drawable,
180   Map *mask,
181   Pixelel default_mask_value
182   )
183 {
184   gint drawable_relative_x, drawable_relative_y;
185   gint width, height;
186 
187   gboolean is_selection;
188   gboolean is_selection_intersect;
189 
190   new_bytemap(mask, drawable->width, drawable->height);
191 
192   /* Bug: original code did this:
193   has_selection = gimp_drawable_mask_bounds(drawable->drawable_id,&x1,&y1,&x2,&y2);
194   but that returns True if there is a selection that does not intersect.
195   That led to blitting out of bounds of our mask copy.
196   */
197   /*
198   If the corpus is a separate layer that does not intersect selection (for target)
199   select the whole layer (what the user intends.)
200   If user is required to select, it should be checked earlier (in calling plugins.)
201   */
202 
203   {
204   gint x1, y1, x2, y2;
205   is_selection = gimp_drawable_mask_bounds(drawable->drawable_id, &x1, &y1, &x2, &y2);
206   }
207   is_selection_intersect = gimp_drawable_mask_intersect(drawable->drawable_id,
208       &drawable_relative_x, &drawable_relative_y, &width, &height);
209 
210   if ( ! is_selection || ! is_selection_intersect) {
211     set_bytemap(mask, default_mask_value);
212     /* This is confusing enough to users and programmers that it deserves a debug message.
213     On all platforms, this only prints if a console is already open.
214     */
215     g_debug("Drawable without intersecting selection, using entire drawable.");
216   }
217   else /* Is a selection and it intersects drawable. */
218   {
219     Map temp_mask;
220     GimpDrawable *mask_drawable;
221     gint xoff,yoff;
222 
223     /* Build a mask that is unselected where the selection channel doesn't intersect,
224     and having the value of the selection channel where it does intersect.
225     */
226 
227     /* Initially Unselect full mask */
228     set_bytemap(mask, MASK_UNSELECTED);
229 
230     /* Get the selection intersection's bytemap into temp_mask */
231     new_bytemap(&temp_mask, width, height);
232 
233     /* Get Gimp drawable for selection channel.  It is in image coords, i.e. anchored at 0,0 image */
234     mask_drawable = gimp_drawable_get(gimp_image_get_selection(gimp_drawable_get_image(drawable->drawable_id)));
235     gimp_drawable_offsets(drawable->drawable_id, &xoff, &yoff); // Offset of layer in image
236 
237     /*
238     Copy selection intersection bytemap from Gimp to our temp_mask bytemap.
239 
240     Destination is the first channel.  Assert only one channel in the drawable.
241 
242     Since mask_drawable is image size, in image coords, calculate coords of intersection in image coords =
243     (offset of drawable plus drawable relative coords of selection)
244     */
245     g_assert(mask_drawable->bpp == 1);   /* Masks have one channel. */
246     pixmap_from_drawable(temp_mask, mask_drawable,
247       drawable_relative_x+xoff, drawable_relative_y+yoff, MASK_PIXELEL_INDEX, mask_drawable->bpp);
248 
249     gimp_drawable_detach(mask_drawable);
250 
251     /* Blit the selection intersection onto our mask, which is only layer size, not image size. */
252     blit_map(mask, &temp_mask, drawable_relative_x, drawable_relative_y);
253 
254     free_map(&temp_mask);
255     }
256 }
257 
258 
259 
260 void
fetch_image_and_mask(GimpDrawable * drawable,Map * pixmap,guint pixelel_count,Map * mask,Pixelel default_mask_value)261 fetch_image_and_mask(
262   GimpDrawable *drawable, // IN
263   Map *pixmap,            // OUT our color pixmap of drawable
264   guint pixelel_count,    // IN total count mask+image+map Pixelels in our Pixel
265   Map *mask,              // OUT our selection bytemap (only one channel ie byte ie depth)
266   Pixelel default_mask_value  // IN default value for any created mask
267   )
268 {
269 
270   /* Both OUT pixmaps same 2D dimensions.  Depth pixelel_count includes a mask byte. */
271   new_pixmap(pixmap, drawable->width, drawable->height, pixelel_count);
272 
273   /* Get color, alpha channels */
274   pixmap_from_drawable(*pixmap, drawable, 0,0, FIRST_PIXELEL_INDEX, drawable->bpp);
275   fetch_mask(drawable, mask, default_mask_value); /* Get mask channel */
276   interleave_mask(pixmap, mask);  /* Insert mask byte into our Pixels */
277 }
278 
279 
280 
281 
282 /*
283 Create our internal pixmap having color, mask, and map Pixelels in one Pixel.
284 Memory locality improves performance.
285 
286 Each of our pixmap elements (pixel) is a sequence of channel pixelels,
287 e.g.
288   MRGBA if color w alpha w no map,
289   MRGBARGB if color, alpha, and color map (note discard map alpha)
290   MRGBW if color, no alpha, w greyscale map
291 Max of eight pixelels.
292 Note we prepend the mask byte.
293 */
294 void
fetch_image_mask_map(GimpDrawable * image_drawable,Map * pixmap,guint pixelel_count,Map * mask,Pixelel default_mask_value,GimpDrawable * map_drawable,guint map_offset)295 fetch_image_mask_map(
296   GimpDrawable *image_drawable,     // IN image: target or corpus drawable
297   Map          *pixmap,             // OUT our pixmap of drawable
298   guint        pixelel_count,       // IN count channels in image + map
299   Map          *mask,               // OUT our selection bytemap (only one channel ie byte ie depth)
300   Pixelel      default_mask_value,  // IN default value for any created mask
301   GimpDrawable *map_drawable,       // IN map drawable, target or corpus
302   guint        map_offset          // IN index in our Pixel to first map Pixelel
303   )
304 {
305   /* Fetch image.  If no selection mask, create one defaulted to SELECTED.
306   The selection mask distinguishes the target from the context (which is optional.)
307   */
308   fetch_image_and_mask(image_drawable, pixmap, pixelel_count, mask, default_mask_value);
309 
310   /*
311   Append some of the map channels (Pixelels) to our Pixel.  map_offset is the destination Pixelel.
312   !!! Note the alpha channel of the map gets discarded.
313   */
314   if (map_drawable)
315   {
316     /* Count map channels excluding alpha. */
317     guint pixelels_to_copy = map_drawable->bpp;
318     if ( gimp_drawable_has_alpha(map_drawable->drawable_id) )
319       pixelels_to_copy--;
320     pixmap_from_drawable(*pixmap, map_drawable, 0,0, map_offset, pixelels_to_copy);
321   }
322 }
323 
324