Cleanup: style, use braces for editors
[blender.git] / source / blender / editors / screen / screendump.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  * Making screendumps.
19  */
20
21 /** \file
22  * \ingroup edscr
23  */
24
25 #include <string.h>
26 #include <errno.h>
27
28 #include "MEM_guardedalloc.h"
29
30 #include "BLI_blenlib.h"
31 #include "BLI_utildefines.h"
32
33 #include "IMB_imbuf_types.h"
34 #include "IMB_imbuf.h"
35
36 #include "DNA_scene_types.h"
37 #include "DNA_screen_types.h"
38 #include "DNA_space_types.h"
39
40 #include "BKE_context.h"
41 #include "BKE_global.h"
42 #include "BKE_image.h"
43 #include "BKE_main.h"
44 #include "BKE_report.h"
45
46 #include "BIF_gl.h"
47
48 #include "RNA_access.h"
49 #include "RNA_define.h"
50
51 #include "UI_interface.h"
52
53 #include "WM_types.h"
54 #include "WM_api.h"
55
56 #include "screen_intern.h"
57
58 typedef struct ScreenshotData {
59   unsigned int *dumprect;
60   int dumpsx, dumpsy;
61   rcti crop;
62
63   ImageFormatData im_format;
64 } ScreenshotData;
65
66 static void screenshot_read_pixels(int x, int y, int w, int h, unsigned char *rect)
67 {
68   int i;
69
70   glReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, rect);
71   glFinish();
72
73   /* clear alpha, it is not set to a meaningful value in opengl */
74   for (i = 0, rect += 3; i < w * h; i++, rect += 4) {
75     *rect = 255;
76   }
77 }
78
79 /* get shot from frontbuffer */
80 static unsigned int *screenshot(bContext *C, int *dumpsx, int *dumpsy)
81 {
82   wmWindow *win = CTX_wm_window(C);
83   int x = 0, y = 0;
84   unsigned int *dumprect = NULL;
85
86   x = 0;
87   y = 0;
88   *dumpsx = WM_window_pixels_x(win);
89   *dumpsy = WM_window_pixels_y(win);
90
91   if (*dumpsx && *dumpsy) {
92
93     dumprect = MEM_mallocN(sizeof(int) * (*dumpsx) * (*dumpsy), "dumprect");
94     glReadBuffer(GL_FRONT);
95     screenshot_read_pixels(x, y, *dumpsx, *dumpsy, (unsigned char *)dumprect);
96     glReadBuffer(GL_BACK);
97   }
98
99   return dumprect;
100 }
101
102 /* call from both exec and invoke */
103 static int screenshot_data_create(bContext *C, wmOperator *op)
104 {
105   unsigned int *dumprect;
106   int dumpsx, dumpsy;
107
108   /* do redraw so we don't show popups/menus */
109   WM_redraw_windows(C);
110
111   dumprect = screenshot(C, &dumpsx, &dumpsy);
112
113   if (dumprect) {
114     ScreenshotData *scd = MEM_callocN(sizeof(ScreenshotData), "screenshot");
115     ScrArea *sa = CTX_wm_area(C);
116
117     scd->dumpsx = dumpsx;
118     scd->dumpsy = dumpsy;
119     scd->dumprect = dumprect;
120     if (sa) {
121       scd->crop = sa->totrct;
122     }
123
124     BKE_imformat_defaults(&scd->im_format);
125
126     op->customdata = scd;
127
128     return true;
129   }
130   else {
131     op->customdata = NULL;
132     return false;
133   }
134 }
135
136 static void screenshot_data_free(wmOperator *op)
137 {
138   ScreenshotData *scd = op->customdata;
139
140   if (scd) {
141     if (scd->dumprect) {
142       MEM_freeN(scd->dumprect);
143     }
144     MEM_freeN(scd);
145     op->customdata = NULL;
146   }
147 }
148
149 static void screenshot_crop(ImBuf *ibuf, rcti crop)
150 {
151   unsigned int *to = ibuf->rect;
152   unsigned int *from = ibuf->rect + crop.ymin * ibuf->x + crop.xmin;
153   int crop_x = BLI_rcti_size_x(&crop);
154   int crop_y = BLI_rcti_size_y(&crop);
155   int y;
156
157   if (crop_x > 0 && crop_y > 0) {
158     for (y = 0; y < crop_y; y++, to += crop_x, from += ibuf->x) {
159       memmove(to, from, sizeof(unsigned int) * crop_x);
160     }
161
162     ibuf->x = crop_x;
163     ibuf->y = crop_y;
164   }
165 }
166
167 static int screenshot_exec(bContext *C, wmOperator *op)
168 {
169   ScreenshotData *scd = op->customdata;
170   bool ok = false;
171
172   if (scd == NULL) {
173     /* when running exec directly */
174     screenshot_data_create(C, op);
175     scd = op->customdata;
176   }
177
178   if (scd) {
179     if (scd->dumprect) {
180       ImBuf *ibuf;
181       char path[FILE_MAX];
182
183       RNA_string_get(op->ptr, "filepath", path);
184       BLI_path_abs(path, BKE_main_blendfile_path_from_global());
185
186       /* operator ensures the extension */
187       ibuf = IMB_allocImBuf(scd->dumpsx, scd->dumpsy, 24, 0);
188       ibuf->rect = scd->dumprect;
189
190       /* crop to show only single editor */
191       if (!RNA_boolean_get(op->ptr, "full")) {
192         screenshot_crop(ibuf, scd->crop);
193       }
194
195       if (scd->im_format.planes == R_IMF_PLANES_BW) {
196         /* bw screenshot? - users will notice if it fails! */
197         IMB_color_to_bw(ibuf);
198       }
199       if (BKE_imbuf_write(ibuf, path, &scd->im_format)) {
200         ok = true;
201       }
202       else {
203         BKE_reportf(op->reports, RPT_ERROR, "Could not write image: %s", strerror(errno));
204       }
205
206       IMB_freeImBuf(ibuf);
207     }
208   }
209
210   screenshot_data_free(op);
211
212   return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
213 }
214
215 static int screenshot_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
216 {
217   if (screenshot_data_create(C, op)) {
218     if (RNA_struct_property_is_set(op->ptr, "filepath")) {
219       return screenshot_exec(C, op);
220     }
221
222     /* extension is added by 'screenshot_check' after */
223     char filepath[FILE_MAX] = "//screen";
224     if (G.relbase_valid) {
225       BLI_strncpy(filepath, BKE_main_blendfile_path_from_global(), sizeof(filepath));
226       BLI_path_extension_replace(filepath, sizeof(filepath), ""); /* strip '.blend' */
227     }
228     RNA_string_set(op->ptr, "filepath", filepath);
229
230     WM_event_add_fileselect(C, op);
231
232     return OPERATOR_RUNNING_MODAL;
233   }
234   return OPERATOR_CANCELLED;
235 }
236
237 static bool screenshot_check(bContext *UNUSED(C), wmOperator *op)
238 {
239   ScreenshotData *scd = op->customdata;
240   return WM_operator_filesel_ensure_ext_imtype(op, &scd->im_format);
241 }
242
243 static void screenshot_cancel(bContext *UNUSED(C), wmOperator *op)
244 {
245   screenshot_data_free(op);
246 }
247
248 static bool screenshot_draw_check_prop(PointerRNA *UNUSED(ptr),
249                                        PropertyRNA *prop,
250                                        void *UNUSED(user_data))
251 {
252   const char *prop_id = RNA_property_identifier(prop);
253
254   return !(STREQ(prop_id, "filepath"));
255 }
256
257 static void screenshot_draw(bContext *UNUSED(C), wmOperator *op)
258 {
259   uiLayout *layout = op->layout;
260   ScreenshotData *scd = op->customdata;
261   PointerRNA ptr;
262
263   /* image template */
264   RNA_pointer_create(NULL, &RNA_ImageFormatSettings, &scd->im_format, &ptr);
265   uiTemplateImageSettings(layout, &ptr, false);
266
267   /* main draw call */
268   RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
269   uiDefAutoButsRNA(
270       layout, &ptr, screenshot_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false);
271 }
272
273 static bool screenshot_poll(bContext *C)
274 {
275   if (G.background) {
276     return false;
277   }
278
279   return WM_operator_winactive(C);
280 }
281
282 void SCREEN_OT_screenshot(wmOperatorType *ot)
283 {
284   /* weak: opname starting with 'save' makes filewindow give save-over */
285   ot->name = "Save Screenshot";
286   ot->idname = "SCREEN_OT_screenshot";
287   ot->description = "Capture a picture of the active area or whole Blender window";
288
289   ot->invoke = screenshot_invoke;
290   ot->check = screenshot_check;
291   ot->exec = screenshot_exec;
292   ot->cancel = screenshot_cancel;
293   ot->ui = screenshot_draw;
294   ot->poll = screenshot_poll;
295
296   ot->flag = 0;
297
298   WM_operator_properties_filesel(ot,
299                                  FILE_TYPE_FOLDER | FILE_TYPE_IMAGE,
300                                  FILE_SPECIAL,
301                                  FILE_SAVE,
302                                  WM_FILESEL_FILEPATH,
303                                  FILE_DEFAULTDISPLAY,
304                                  FILE_SORT_ALPHA);
305   RNA_def_boolean(ot->srna,
306                   "full",
307                   1,
308                   "Full Screen",
309                   "Capture the whole window (otherwise only capture the active area)");
310 }