1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * gimpviewrenderer-frame.c
5 * Copyright (C) 2004 Sven Neumann <sven@gimp.org>
6 *
7 * Contains code taken from eel, the Eazel Extensions Library.
8 *
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
23 #include "config.h"
24
25 #include <gegl.h>
26 #include <gtk/gtk.h>
27
28 #include "libgimpwidgets/gimpwidgets.h"
29
30 #include "widgets-types.h"
31
32 #include "core/gimpviewable.h"
33
34 #include "gimpviewrenderer.h"
35 #include "gimpviewrenderer-frame.h"
36 #include "gimpwidgets-utils.h"
37
38
39 /* utility to stretch a frame to the desired size */
40
41 static void
draw_frame_row(GdkPixbuf * frame_image,gint target_width,gint source_width,gint source_v_position,gint dest_v_position,GdkPixbuf * result_pixbuf,gint left_offset,gint height)42 draw_frame_row (GdkPixbuf *frame_image,
43 gint target_width,
44 gint source_width,
45 gint source_v_position,
46 gint dest_v_position,
47 GdkPixbuf *result_pixbuf,
48 gint left_offset,
49 gint height)
50 {
51 gint remaining_width = target_width;
52 gint h_offset = 0;
53
54 while (remaining_width > 0)
55 {
56 gint slab_width = (remaining_width > source_width ?
57 source_width : remaining_width);
58 gdk_pixbuf_copy_area (frame_image,
59 left_offset, source_v_position,
60 slab_width, height,
61 result_pixbuf,
62 left_offset + h_offset, dest_v_position);
63
64 remaining_width -= slab_width;
65 h_offset += slab_width;
66 }
67 }
68
69 /* utility to draw the middle section of the frame in a loop */
70 static void
draw_frame_column(GdkPixbuf * frame_image,gint target_height,gint source_height,gint source_h_position,gint dest_h_position,GdkPixbuf * result_pixbuf,gint top_offset,int width)71 draw_frame_column (GdkPixbuf *frame_image,
72 gint target_height,
73 gint source_height,
74 gint source_h_position,
75 gint dest_h_position,
76 GdkPixbuf *result_pixbuf,
77 gint top_offset, int width)
78 {
79 gint remaining_height = target_height;
80 gint v_offset = 0;
81
82 while (remaining_height > 0)
83 {
84 gint slab_height = (remaining_height > source_height ?
85 source_height : remaining_height);
86
87 gdk_pixbuf_copy_area (frame_image,
88 source_h_position, top_offset,
89 width, slab_height,
90 result_pixbuf,
91 dest_h_position, top_offset + v_offset);
92
93 remaining_height -= slab_height;
94 v_offset += slab_height;
95 }
96 }
97
98 static GdkPixbuf *
stretch_frame_image(GdkPixbuf * frame_image,gint left_offset,gint top_offset,gint right_offset,gint bottom_offset,gint dest_width,gint dest_height)99 stretch_frame_image (GdkPixbuf *frame_image,
100 gint left_offset,
101 gint top_offset,
102 gint right_offset,
103 gint bottom_offset,
104 gint dest_width,
105 gint dest_height)
106 {
107 GdkPixbuf *pixbuf;
108 gint frame_width, frame_height;
109 gint target_width, target_frame_width;
110 gint target_height, target_frame_height;
111
112 frame_width = gdk_pixbuf_get_width (frame_image);
113 frame_height = gdk_pixbuf_get_height (frame_image );
114
115 pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
116 dest_width, dest_height);
117 gdk_pixbuf_fill (pixbuf, 0);
118
119 target_width = dest_width - left_offset - right_offset;
120 target_height = dest_height - top_offset - bottom_offset;
121
122 target_frame_width = frame_width - left_offset - right_offset;
123 target_frame_height = frame_height - top_offset - bottom_offset;
124
125 left_offset += MIN (target_width / 4, target_frame_width / 4);
126 right_offset += MIN (target_width / 4, target_frame_width / 4);
127 top_offset += MIN (target_height / 4, target_frame_height / 4);
128 bottom_offset += MIN (target_height / 4, target_frame_height / 4);
129
130 target_width = dest_width - left_offset - right_offset;
131 target_height = dest_height - top_offset - bottom_offset;
132
133 target_frame_width = frame_width - left_offset - right_offset;
134 target_frame_height = frame_height - top_offset - bottom_offset;
135
136 /* draw the left top corner and top row */
137 gdk_pixbuf_copy_area (frame_image,
138 0, 0, left_offset, top_offset,
139 pixbuf, 0, 0);
140 draw_frame_row (frame_image, target_width, target_frame_width,
141 0, 0,
142 pixbuf,
143 left_offset, top_offset);
144
145 /* draw the right top corner and left column */
146 gdk_pixbuf_copy_area (frame_image,
147 frame_width - right_offset, 0,
148 right_offset, top_offset,
149
150 pixbuf,
151 dest_width - right_offset, 0);
152 draw_frame_column (frame_image, target_height, target_frame_height, 0, 0,
153 pixbuf, top_offset, left_offset);
154
155 /* draw the bottom right corner and bottom row */
156 gdk_pixbuf_copy_area (frame_image,
157 frame_width - right_offset, frame_height - bottom_offset,
158 right_offset, bottom_offset,
159 pixbuf,
160 dest_width - right_offset, dest_height - bottom_offset);
161 draw_frame_row (frame_image, target_width, target_frame_width,
162 frame_height - bottom_offset, dest_height - bottom_offset,
163 pixbuf, left_offset, bottom_offset);
164
165 /* draw the bottom left corner and the right column */
166 gdk_pixbuf_copy_area (frame_image,
167 0, frame_height - bottom_offset,
168 left_offset, bottom_offset,
169 pixbuf,
170 0, dest_height - bottom_offset);
171 draw_frame_column (frame_image, target_height, target_frame_height,
172 frame_width - right_offset, dest_width - right_offset,
173 pixbuf, top_offset, right_offset);
174
175 return pixbuf;
176 }
177
178 static GdkPixbuf *
gimp_view_renderer_get_frame(GimpViewRenderer * renderer,gint width,gint height)179 gimp_view_renderer_get_frame (GimpViewRenderer *renderer,
180 gint width,
181 gint height)
182 {
183 GimpViewRendererClass *class = GIMP_VIEW_RENDERER_GET_CLASS (renderer);
184
185 return stretch_frame_image (class->frame,
186 class->frame_left,
187 class->frame_top,
188 class->frame_right,
189 class->frame_bottom,
190 width, height);
191 }
192
193 static void
gimp_view_renderer_ensure_frame(GimpViewRenderer * renderer,GtkWidget * widget)194 gimp_view_renderer_ensure_frame (GimpViewRenderer *renderer,
195 GtkWidget *widget)
196 {
197 GimpViewRendererClass *class = GIMP_VIEW_RENDERER_GET_CLASS (renderer);
198
199 if (! class->frame)
200 {
201 class->frame = gimp_widget_load_icon (widget, GIMP_ICON_FRAME, 48);
202
203 /* FIXME: shouldn't be hardcoded */
204 class->frame_left = 2;
205 class->frame_top = 2;
206 class->frame_right = 4;
207 class->frame_bottom = 4;
208 }
209 }
210
211 GdkPixbuf *
gimp_view_renderer_get_frame_pixbuf(GimpViewRenderer * renderer,GtkWidget * widget,gint width,gint height)212 gimp_view_renderer_get_frame_pixbuf (GimpViewRenderer *renderer,
213 GtkWidget *widget,
214 gint width,
215 gint height)
216 {
217 GimpViewRendererClass *class;
218 GdkPixbuf *frame;
219 GdkPixbuf *pixbuf;
220 gint w, h;
221 gint x, y;
222
223 g_return_val_if_fail (GIMP_IS_VIEW_RENDERER (renderer), NULL);
224 g_return_val_if_fail (GIMP_IS_VIEWABLE (renderer->viewable), NULL);
225
226 gimp_view_renderer_ensure_frame (renderer, widget);
227
228 class = GIMP_VIEW_RENDERER_GET_CLASS (renderer);
229
230 w = width - class->frame_left - class->frame_right;
231 h = height - class->frame_top - class->frame_bottom;
232
233 if (w > 12 && h > 12)
234 {
235 pixbuf = gimp_viewable_get_pixbuf (renderer->viewable,
236 renderer->context,
237 w, h);
238 if (!pixbuf)
239 return NULL;
240
241 x = class->frame_left;
242 y = class->frame_top;
243 w = gdk_pixbuf_get_width (pixbuf);
244 h = gdk_pixbuf_get_height (pixbuf);
245
246 frame = gimp_view_renderer_get_frame (renderer,
247 w + x + class->frame_right,
248 h + y + class->frame_bottom);
249 }
250 else
251 {
252 pixbuf = gimp_viewable_get_pixbuf (renderer->viewable,
253 renderer->context,
254 width - 2, height - 2);
255 if (!pixbuf)
256 return NULL;
257
258 /* as fallback, render the preview with a 1 pixel wide black border */
259
260 x = 1;
261 y = 1;
262 w = gdk_pixbuf_get_width (pixbuf);
263 h = gdk_pixbuf_get_height (pixbuf);
264
265 frame = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, w + 2, h + 2);
266 gdk_pixbuf_fill (frame, 0);
267 }
268
269 gdk_pixbuf_copy_area (pixbuf, 0, 0, w, h, frame, x, y);
270
271 return frame;
272 }
273
274
275 /* This API is somewhat weird but GimpThumbBox needs these values so
276 * it can request the GimpImageFile view in the proper size.
277 */
278 void
gimp_view_renderer_get_frame_size(gint * horizontal,gint * vertical)279 gimp_view_renderer_get_frame_size (gint *horizontal,
280 gint *vertical)
281 {
282 GimpViewRendererClass *class;
283
284 class = g_type_class_ref (GIMP_TYPE_VIEW_RENDERER);
285
286 if (horizontal)
287 *horizontal = class->frame_left + class->frame_right;
288
289 if (vertical)
290 *vertical = class->frame_top + class->frame_bottom;
291
292 g_type_class_unref (class);
293 }
294