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(®ion, 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(®ion, 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(®ion, 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(®ion, 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