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