Merge branch 'master' into blender2.8
[blender.git] / source / blender / editors / screen / screendump.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * Contributor(s): Blender Foundation
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  * Making screendumps.
25  */
26
27 /** \file blender/editors/screen/screendump.c
28  *  \ingroup edscr
29  */
30
31
32 #include <string.h>
33 #include <errno.h>
34
35 #include "MEM_guardedalloc.h"
36
37 #include "BLI_blenlib.h"
38 #include "BLI_utildefines.h"
39 #include "BLI_math.h"
40
41 #include "IMB_imbuf_types.h"
42 #include "IMB_imbuf.h"
43
44 #include "DNA_scene_types.h"
45 #include "DNA_screen_types.h"
46 #include "DNA_space_types.h"
47 #include "DNA_userdef_types.h"
48
49 #include "BKE_context.h"
50 #include "BKE_global.h"
51 #include "BKE_main.h"
52 #include "BKE_image.h"
53 #include "BKE_report.h"
54 #include "BKE_writeavi.h"
55
56 #include "BIF_gl.h"
57
58 #include "GPU_immediate.h"
59 #include "GPU_immediate_util.h"
60
61 #include "RNA_access.h"
62 #include "RNA_define.h"
63
64 #include "UI_interface.h"
65
66 #include "WM_types.h"
67 #include "WM_api.h"
68
69 #include "PIL_time.h"
70
71
72 #include "screen_intern.h"
73
74 typedef struct ScreenshotData {
75         unsigned int *dumprect;
76         int dumpsx, dumpsy;
77         rcti crop;
78
79         ImageFormatData im_format;
80 } ScreenshotData;
81
82 static void screenshot_read_pixels(int x, int y, int w, int h, unsigned char *rect)
83 {
84         int i;
85
86         glReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, rect);
87         glFinish();
88
89         /* clear alpha, it is not set to a meaningful value in opengl */
90         for (i = 0, rect += 3; i < w * h; i++, rect += 4)
91                 *rect = 255;
92 }
93
94 /* get shot from frontbuffer */
95 static unsigned int *screenshot(bContext *C, int *dumpsx, int *dumpsy)
96 {
97         wmWindow *win = CTX_wm_window(C);
98         int x = 0, y = 0;
99         unsigned int *dumprect = NULL;
100
101         x = 0;
102         y = 0;
103         *dumpsx = WM_window_pixels_x(win);
104         *dumpsy = WM_window_pixels_y(win);
105
106         if (*dumpsx && *dumpsy) {
107
108                 dumprect = MEM_mallocN(sizeof(int) * (*dumpsx) * (*dumpsy), "dumprect");
109                 glReadBuffer(GL_FRONT);
110                 screenshot_read_pixels(x, y, *dumpsx, *dumpsy, (unsigned char *)dumprect);
111                 glReadBuffer(GL_BACK);
112         }
113
114         return dumprect;
115 }
116
117 /* call from both exec and invoke */
118 static int screenshot_data_create(bContext *C, wmOperator *op)
119 {
120         unsigned int *dumprect;
121         int dumpsx, dumpsy;
122
123         /* do redraw so we don't show popups/menus */
124         WM_redraw_windows(C);
125
126         dumprect = screenshot(C, &dumpsx, &dumpsy);
127
128         if (dumprect) {
129                 ScreenshotData *scd = MEM_callocN(sizeof(ScreenshotData), "screenshot");
130                 ScrArea *sa = CTX_wm_area(C);
131
132                 scd->dumpsx = dumpsx;
133                 scd->dumpsy = dumpsy;
134                 scd->dumprect = dumprect;
135                 if (sa) {
136                         scd->crop = sa->totrct;
137                 }
138
139                 BKE_imformat_defaults(&scd->im_format);
140
141                 op->customdata = scd;
142
143                 return true;
144         }
145         else {
146                 op->customdata = NULL;
147                 return false;
148         }
149 }
150
151 static void screenshot_data_free(wmOperator *op)
152 {
153         ScreenshotData *scd = op->customdata;
154
155         if (scd) {
156                 if (scd->dumprect)
157                         MEM_freeN(scd->dumprect);
158                 MEM_freeN(scd);
159                 op->customdata = NULL;
160         }
161 }
162
163 static void screenshot_crop(ImBuf *ibuf, rcti crop)
164 {
165         unsigned int *to = ibuf->rect;
166         unsigned int *from = ibuf->rect + crop.ymin * ibuf->x + crop.xmin;
167         int crop_x = BLI_rcti_size_x(&crop);
168         int crop_y = BLI_rcti_size_y(&crop);
169         int y;
170
171         if (crop_x > 0 && crop_y > 0) {
172                 for (y = 0; y < crop_y; y++, to += crop_x, from += ibuf->x)
173                         memmove(to, from, sizeof(unsigned int) * crop_x);
174
175                 ibuf->x = crop_x;
176                 ibuf->y = crop_y;
177         }
178 }
179
180 static int screenshot_exec(bContext *C, wmOperator *op)
181 {
182         ScreenshotData *scd = op->customdata;
183         bool ok = false;
184
185         if (scd == NULL) {
186                 /* when running exec directly */
187                 screenshot_data_create(C, op);
188                 scd = op->customdata;
189         }
190
191         if (scd) {
192                 if (scd->dumprect) {
193                         ImBuf *ibuf;
194                         char path[FILE_MAX];
195
196                         RNA_string_get(op->ptr, "filepath", path);
197                         BLI_path_abs(path, G.main->name);
198
199                         /* operator ensures the extension */
200                         ibuf = IMB_allocImBuf(scd->dumpsx, scd->dumpsy, 24, 0);
201                         ibuf->rect = scd->dumprect;
202
203                         /* crop to show only single editor */
204                         if (!RNA_boolean_get(op->ptr, "full"))
205                                 screenshot_crop(ibuf, scd->crop);
206
207                         if (scd->im_format.planes == R_IMF_PLANES_BW) {
208                                 /* bw screenshot? - users will notice if it fails! */
209                                 IMB_color_to_bw(ibuf);
210                         }
211                         if (BKE_imbuf_write(ibuf, path, &scd->im_format)) {
212                                 ok = true;
213                         }
214                         else {
215                                 BKE_reportf(op->reports, RPT_ERROR, "Could not write image: %s", strerror(errno));
216                         }
217
218                         IMB_freeImBuf(ibuf);
219                 }
220         }
221
222         screenshot_data_free(op);
223
224         return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
225 }
226
227 static int screenshot_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
228 {
229         if (screenshot_data_create(C, op)) {
230                 if (RNA_struct_property_is_set(op->ptr, "filepath"))
231                         return screenshot_exec(C, op);
232
233                 /* extension is added by 'screenshot_check' after */
234                 char filepath[FILE_MAX] = "//screen";
235                 if (G.relbase_valid) {
236                         BLI_strncpy(filepath, G.main->name, sizeof(filepath));
237                         BLI_replace_extension(filepath, sizeof(filepath), "");  /* strip '.blend' */
238                 }
239                 RNA_string_set(op->ptr, "filepath", filepath);
240
241                 WM_event_add_fileselect(C, op);
242
243                 return OPERATOR_RUNNING_MODAL;
244         }
245         return OPERATOR_CANCELLED;
246 }
247
248 static bool screenshot_check(bContext *UNUSED(C), wmOperator *op)
249 {
250         ScreenshotData *scd = op->customdata;
251         return WM_operator_filesel_ensure_ext_imtype(op, &scd->im_format);
252 }
253
254 static void screenshot_cancel(bContext *UNUSED(C), wmOperator *op)
255 {
256         screenshot_data_free(op);
257 }
258
259 static bool screenshot_draw_check_prop(PointerRNA *UNUSED(ptr), PropertyRNA *prop)
260 {
261         const char *prop_id = RNA_property_identifier(prop);
262
263         return !(STREQ(prop_id, "filepath"));
264 }
265
266 static void screenshot_draw(bContext *UNUSED(C), wmOperator *op)
267 {
268         uiLayout *layout = op->layout;
269         ScreenshotData *scd = op->customdata;
270         PointerRNA ptr;
271
272         /* image template */
273         RNA_pointer_create(NULL, &RNA_ImageFormatSettings, &scd->im_format, &ptr);
274         uiTemplateImageSettings(layout, &ptr, false);
275
276         /* main draw call */
277         RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
278         uiDefAutoButsRNA(layout, &ptr, screenshot_draw_check_prop, UI_BUT_LABEL_ALIGN_NONE, false);
279 }
280
281 static int screenshot_poll(bContext *C)
282 {
283         if (G.background)
284                 return false;
285
286         return WM_operator_winactive(C);
287 }
288
289 void SCREEN_OT_screenshot(wmOperatorType *ot)
290 {
291         ot->name = "Save Screenshot"; /* weak: opname starting with 'save' makes filewindow give save-over */
292         ot->idname = "SCREEN_OT_screenshot";
293         ot->description = "Capture a picture of the active area or whole Blender window";
294
295         ot->invoke = screenshot_invoke;
296         ot->check = screenshot_check;
297         ot->exec = screenshot_exec;
298         ot->cancel = screenshot_cancel;
299         ot->ui = screenshot_draw;
300         ot->poll = screenshot_poll;
301
302         ot->flag = 0;
303
304         WM_operator_properties_filesel(
305                 ot, FILE_TYPE_FOLDER | FILE_TYPE_IMAGE, FILE_SPECIAL, FILE_SAVE,
306                 WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA);
307         RNA_def_boolean(ot->srna, "full", 1, "Full Screen",
308                         "Capture the whole window (otherwise only capture the active area)");
309 }
310
311 /* *************** screenshot movie job ************************* */
312
313 typedef struct ScreenshotJob {
314         Main *bmain;
315         Scene *scene;
316         wmWindowManager *wm;
317         unsigned int *dumprect;
318         int x, y, dumpsx, dumpsy;
319         const short *stop;
320         const short *do_update;
321         ReportList reports;
322
323         bMovieHandle *movie_handle;
324         void *movie_ctx;
325 } ScreenshotJob;
326
327
328 static void screenshot_freejob(void *sjv)
329 {
330         ScreenshotJob *sj = sjv;
331
332         if (sj->dumprect)
333                 MEM_freeN(sj->dumprect);
334
335         if (sj->movie_handle) {
336                 bMovieHandle *mh = sj->movie_handle;
337                 mh->end_movie(sj->movie_ctx);
338                 mh->context_free(sj->movie_ctx);
339         }
340
341         MEM_freeN(sj);
342 }
343
344
345 /* called before redraw notifiers, copies a new dumprect */
346 static void screenshot_updatejob(void *sjv)
347 {
348         ScreenshotJob *sj = sjv;
349         unsigned int *dumprect;
350
351         if (sj->dumprect == NULL) {
352                 dumprect = MEM_mallocN(sizeof(int) * sj->dumpsx * sj->dumpsy, "dumprect");
353                 screenshot_read_pixels(sj->x, sj->y, sj->dumpsx, sj->dumpsy, (unsigned char *)dumprect);
354
355                 sj->dumprect = dumprect;
356         }
357 }
358
359
360 /* only this runs inside thread */
361 static void screenshot_startjob(void *sjv, short *stop, short *do_update, float *UNUSED(progress))
362 {
363         ScreenshotJob *sj = sjv;
364         RenderData rd = sj->scene->r;
365         bMovieHandle *mh = NULL;
366
367         /* we need this as local variables for renderdata */
368         rd.frs_sec = U.scrcastfps;
369         rd.frs_sec_base = 1.0f;
370
371         if (BKE_imtype_is_movie(rd.im_format.imtype)) {
372                 mh = BKE_movie_handle_get(sj->scene->r.im_format.imtype);
373                 if (mh == NULL) {
374                         printf("Movie format unsupported\n");
375                         return;
376                 }
377                 sj->movie_ctx = mh->context_create();
378                 sj->movie_handle = mh;
379
380                 if (!mh->start_movie(sj->movie_ctx, sj->scene, &rd, sj->dumpsx, sj->dumpsy, &sj->reports, false, "")) {
381                         printf("screencast job stopped\n");
382                         return;
383                 }
384         }
385
386         sj->stop = stop;
387         sj->do_update = do_update;
388
389         *do_update = true; /* wait for opengl rect */
390
391         while (*stop == 0) {
392
393                 if (sj->dumprect) {
394
395                         if (mh) {
396                                 if (mh->append_movie(sj->movie_ctx, &rd, rd.sfra, rd.cfra, (int *)sj->dumprect,
397                                                      sj->dumpsx, sj->dumpsy, "", &sj->reports))
398                                 {
399                                         BKE_reportf(&sj->reports, RPT_INFO, "Appended frame: %d", rd.cfra);
400                                         printf("Appended frame %d\n", rd.cfra);
401                                 }
402                                 else {
403                                         break;
404                                 }
405                         }
406                         else {
407                                 ImBuf *ibuf = IMB_allocImBuf(sj->dumpsx, sj->dumpsy, rd.im_format.planes, 0);
408                                 char name[FILE_MAX];
409                                 int ok;
410
411                                 BKE_image_path_from_imformat(
412                                         name, rd.pic, sj->bmain->name, rd.cfra,
413                                         &rd.im_format, (rd.scemode & R_EXTENSION) != 0, true, NULL);
414
415                                 ibuf->rect = sj->dumprect;
416                                 ok = BKE_imbuf_write(ibuf, name, &rd.im_format);
417
418                                 if (ok == 0) {
419                                         printf("Write error: cannot save %s\n", name);
420                                         BKE_reportf(&sj->reports, RPT_INFO, "Write error: cannot save %s", name);
421                                         break;
422                                 }
423                                 else {
424                                         printf("Saved file: %s\n", name);
425                                         BKE_reportf(&sj->reports, RPT_INFO, "Saved file: %s", name);
426                                 }
427
428                                 /* imbuf knows which rects are not part of ibuf */
429                                 IMB_freeImBuf(ibuf);
430                         }
431
432                         MEM_freeN(sj->dumprect);
433                         sj->dumprect = NULL;
434
435                         *do_update = true;
436
437                         rd.cfra++;
438
439                 }
440                 else
441                         PIL_sleep_ms(U.scrcastwait);
442         }
443
444         if (mh) {
445                 mh->end_movie(sj->movie_ctx);
446                 mh->context_free(sj->movie_ctx);
447                 sj->movie_handle = NULL;
448         }
449
450         BKE_report(&sj->reports, RPT_INFO, "Screencast job stopped");
451 }
452
453 /* Helper callback for drawing the cursor itself */
454 static void screencast_draw_cursor(bContext *UNUSED(C), int x, int y, void *UNUSED(p_ptr))
455 {
456         glEnable(GL_LINE_SMOOTH);
457         glEnable(GL_BLEND);
458
459         Gwn_VertFormat *format = immVertexFormat();
460         unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
461
462         immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
463
464         immUniformColor4ub(0, 0, 0, 32);
465         imm_draw_circle_fill_2d(pos, (float)x, (float)y, 20, 40);
466
467         immUniformColor4ub(255, 255, 255, 128);
468         imm_draw_circle_wire_2d(pos, (float)x, (float)y, 20, 40);
469
470         immUnbindProgram();
471
472         glDisable(GL_BLEND);
473         glDisable(GL_LINE_SMOOTH);
474 }
475
476 /* Turn brush cursor in 3D view on/off */
477 static void screencast_cursor_toggle(wmWindowManager *wm, short enable)
478 {
479         static void *cursor = NULL;
480
481         if (cursor && !enable) {
482                 /* clear cursor */
483                 WM_paint_cursor_end(wm, cursor);
484                 cursor = NULL;
485         }
486         else if (enable) {
487                 /* enable cursor */
488                 cursor = WM_paint_cursor_activate(wm, NULL, screencast_draw_cursor, NULL);
489         }
490 }
491
492 static void screenshot_endjob(void *sjv)
493 {
494         ScreenshotJob *sj = sjv;
495
496         screencast_cursor_toggle(sj->wm, 0);
497 }
498
499
500 static int screencast_exec(bContext *C, wmOperator *op)
501 {
502         wmWindowManager *wm = CTX_wm_manager(C);
503         wmWindow *win = CTX_wm_window(C);
504         bScreen *screen = CTX_wm_screen(C);
505         wmJob *wm_job;
506         ScreenshotJob *sj;
507
508         /* if called again, stop the running job */
509         if (WM_jobs_test(wm, screen, WM_JOB_TYPE_SCREENCAST))
510                 WM_jobs_stop(wm, screen, screenshot_startjob);
511
512         wm_job = WM_jobs_get(wm, win, screen, "Screencast", 0, WM_JOB_TYPE_SCREENCAST);
513         sj = MEM_callocN(sizeof(ScreenshotJob), "screenshot job");
514
515         /* setup sj */
516         if (RNA_boolean_get(op->ptr, "full")) {
517                 sj->x = 0;
518                 sj->y = 0;
519                 sj->dumpsx = WM_window_pixels_x(win);
520                 sj->dumpsy = WM_window_pixels_y(win);
521         }
522         else {
523                 ScrArea *curarea = CTX_wm_area(C);
524                 sj->x = curarea->totrct.xmin;
525                 sj->y = curarea->totrct.ymin;
526                 sj->dumpsx = curarea->totrct.xmax - sj->x;
527                 sj->dumpsy = curarea->totrct.ymax - sj->y;
528         }
529         sj->bmain = CTX_data_main(C);
530         sj->scene = CTX_data_scene(C);
531         sj->wm = wm;
532
533         BKE_reports_init(&sj->reports, RPT_PRINT);
534
535         /* setup job */
536         WM_jobs_customdata_set(wm_job, sj, screenshot_freejob);
537         WM_jobs_timer(wm_job, 0.1, 0, NC_SCREEN | ND_SCREENCAST);
538         WM_jobs_callbacks(wm_job, screenshot_startjob, NULL, screenshot_updatejob, screenshot_endjob);
539
540         WM_jobs_start(sj->wm, wm_job);
541
542         screencast_cursor_toggle(sj->wm, 1);
543
544         WM_event_add_notifier(C, NC_SCREEN | ND_SCREENCAST, screen);
545
546         return OPERATOR_FINISHED;
547 }
548
549 void SCREEN_OT_screencast(wmOperatorType *ot)
550 {
551         ot->name = "Make Screencast";
552         ot->idname = "SCREEN_OT_screencast";
553         ot->description = "Capture a video of the active area or whole Blender window";
554
555         ot->invoke = WM_operator_confirm;
556         ot->exec = screencast_exec;
557         ot->poll = screenshot_poll;  /* shared poll */
558
559         ot->flag = 0;
560
561         RNA_def_property(ot->srna, "filepath", PROP_STRING, PROP_FILEPATH);
562         RNA_def_boolean(ot->srna, "full", 1, "Full Screen",
563                         "Capture the whole window (otherwise only capture the active area)");
564 }