2.5
[blender-staging.git] / source / blender / editors / space_image / image_ops.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21  * All rights reserved.
22  *
23  * Contributor(s): Blender Foundation, 2002-2009
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 #include <string.h>
29 #include <stdlib.h>
30
31 #include "MEM_guardedalloc.h"
32
33 #include "DNA_image_types.h"
34 #include "DNA_node_types.h"
35 #include "DNA_object_types.h"
36 #include "DNA_packedFile_types.h"
37 #include "DNA_space_types.h"
38 #include "DNA_scene_types.h"
39 #include "DNA_screen_types.h"
40 #include "DNA_userdef_types.h"
41 #include "DNA_windowmanager_types.h"
42
43 #include "BKE_colortools.h"
44 #include "BKE_context.h"
45 #include "BKE_image.h"
46 #include "BKE_global.h"
47 #include "BKE_library.h"
48 #include "BKE_node.h"
49 #include "BKE_packedFile.h"
50 #include "BKE_report.h"
51 #include "BKE_screen.h"
52
53 #include "BLI_arithb.h"
54 #include "BLI_blenlib.h"
55
56 #include "IMB_imbuf.h"
57 #include "IMB_imbuf_types.h"
58
59 #include "RE_pipeline.h"
60
61 #include "RNA_access.h"
62 #include "RNA_define.h"
63 #include "RNA_types.h"
64
65 #include "ED_image.h"
66 #include "ED_screen.h"
67 #include "ED_space_api.h"
68 #include "ED_uvedit.h"
69
70 #include "UI_interface.h"
71 #include "UI_resources.h"
72 #include "UI_view2d.h"
73
74 #include "WM_api.h"
75 #include "WM_types.h"
76
77 #include "image_intern.h"
78
79 /******************** view navigation utilities *********************/
80
81 static void sima_zoom_set(SpaceImage *sima, ARegion *ar, float zoom)
82 {
83         float oldzoom= sima->zoom;
84         int width, height;
85
86         sima->zoom= zoom;
87
88         if (sima->zoom > 0.1f && sima->zoom < 4.0f)
89                 return;
90
91         /* check zoom limits */
92         ED_space_image_size(sima, &width, &height);
93
94         width *= sima->zoom;
95         height *= sima->zoom;
96
97         if((width < 4) && (height < 4))
98                 sima->zoom= oldzoom;
99         else if((ar->winrct.xmax - ar->winrct.xmin) <= sima->zoom)
100                 sima->zoom= oldzoom;
101         else if((ar->winrct.ymax - ar->winrct.ymin) <= sima->zoom)
102                 sima->zoom= oldzoom;
103 }
104
105 static void sima_zoom_set_factor(SpaceImage *sima, ARegion *ar, float zoomfac)
106 {
107         sima_zoom_set(sima, ar, sima->zoom*zoomfac);
108 }
109
110 int space_image_poll(bContext *C)
111 {
112         SpaceLink *slink= CTX_wm_space_data(C);
113
114         return (slink && (slink->spacetype == SPACE_IMAGE));
115 }
116
117 int space_image_main_area_poll(bContext *C)
118 {
119         SpaceLink *slink= CTX_wm_space_data(C);
120         ARegion *ar= CTX_wm_region(C);
121
122         if(slink && (slink->spacetype == SPACE_IMAGE))
123                 return (ar && ar->type->regionid == RGN_TYPE_WINDOW);
124         
125         return 0;
126 }
127
128 /********************** view pan operator *********************/
129
130 typedef struct ViewPanData {
131         float x, y;
132         float xof, yof;
133 } ViewPanData;
134
135 static void view_pan_init(bContext *C, wmOperator *op, wmEvent *event)
136 {
137         SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C);
138         ViewPanData *vpd;
139
140         op->customdata= vpd= MEM_callocN(sizeof(ViewPanData), "ImageViewPanData");
141         WM_cursor_modal(CTX_wm_window(C), BC_NSEW_SCROLLCURSOR);
142
143         vpd->x= event->x;
144         vpd->y= event->y;
145         vpd->xof= sima->xof;
146         vpd->yof= sima->yof;
147
148         WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
149 }
150
151 static void view_pan_exit(bContext *C, wmOperator *op, int cancel)
152 {
153         SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C);
154         ViewPanData *vpd= op->customdata;
155
156         if(cancel) {
157                 sima->xof= vpd->xof;
158                 sima->yof= vpd->yof;
159                 ED_area_tag_redraw(CTX_wm_area(C));
160         }
161
162         WM_cursor_restore(CTX_wm_window(C));
163         MEM_freeN(op->customdata);
164 }
165
166 static int view_pan_exec(bContext *C, wmOperator *op)
167 {
168         SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C);
169         float offset[2];
170
171         RNA_float_get_array(op->ptr, "offset", offset);
172         sima->xof += offset[0];
173         sima->yof += offset[1];
174
175         ED_area_tag_redraw(CTX_wm_area(C));
176
177         /* XXX notifier? */
178 #if 0
179         if(image_preview_active(curarea, NULL, NULL)) {
180                 /* recalculates new preview rect */
181                 scrarea_do_windraw(curarea);
182                 image_preview_event(2);
183         }
184 #endif
185         
186         return OPERATOR_FINISHED;
187 }
188
189 static int view_pan_invoke(bContext *C, wmOperator *op, wmEvent *event)
190 {
191         view_pan_init(C, op, event);
192         return OPERATOR_RUNNING_MODAL;
193 }
194
195 static int view_pan_modal(bContext *C, wmOperator *op, wmEvent *event)
196 {
197         SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C);
198         ViewPanData *vpd= op->customdata;
199         float offset[2];
200
201         switch(event->type) {
202                 case MOUSEMOVE:
203                         sima->xof= vpd->xof;
204                         sima->yof= vpd->yof;
205                         offset[0]= (vpd->x - event->x)/sima->zoom;
206                         offset[1]= (vpd->y - event->y)/sima->zoom;
207                         RNA_float_set_array(op->ptr, "offset", offset);
208                         view_pan_exec(C, op);
209                         break;
210                 case MIDDLEMOUSE:
211                         if(event->val==0) {
212                                 view_pan_exit(C, op, 0);
213                                 return OPERATOR_FINISHED;
214                         }
215                         break;
216         }
217
218         return OPERATOR_RUNNING_MODAL;
219 }
220
221 static int view_pan_cancel(bContext *C, wmOperator *op)
222 {
223         view_pan_exit(C, op, 1);
224         return OPERATOR_CANCELLED;
225 }
226
227 void IMAGE_OT_view_pan(wmOperatorType *ot)
228 {
229         /* identifiers */
230         ot->name= "View Pan";
231         ot->idname= "IMAGE_OT_view_pan";
232         
233         /* api callbacks */
234         ot->exec= view_pan_exec;
235         ot->invoke= view_pan_invoke;
236         ot->modal= view_pan_modal;
237         ot->cancel= view_pan_cancel;
238         ot->poll= space_image_main_area_poll;
239         
240         /* properties */
241         RNA_def_float_vector(ot->srna, "offset", 2, NULL, -FLT_MAX, FLT_MAX,
242                 "Offset", "Offset in floating point units, 1.0 is the width and height of the image.", -FLT_MAX, FLT_MAX);
243 }
244
245 /********************** view zoom operator *********************/
246
247 typedef struct ViewZoomData {
248         float x, y;
249         float zoom;
250 } ViewZoomData;
251
252 static void view_zoom_init(bContext *C, wmOperator *op, wmEvent *event)
253 {
254         SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C);
255         ViewZoomData *vpd;
256
257         op->customdata= vpd= MEM_callocN(sizeof(ViewZoomData), "ImageViewZoomData");
258         WM_cursor_modal(CTX_wm_window(C), BC_NSEW_SCROLLCURSOR);
259
260         vpd->x= event->x;
261         vpd->y= event->y;
262         vpd->zoom= sima->zoom;
263
264         WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
265 }
266
267 static void view_zoom_exit(bContext *C, wmOperator *op, int cancel)
268 {
269         SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C);
270         ViewZoomData *vpd= op->customdata;
271
272         if(cancel) {
273                 sima->zoom= vpd->zoom;
274                 ED_area_tag_redraw(CTX_wm_area(C));
275         }
276
277         WM_cursor_restore(CTX_wm_window(C));
278         MEM_freeN(op->customdata);
279 }
280
281 static int view_zoom_exec(bContext *C, wmOperator *op)
282 {
283         SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C);
284         ARegion *ar= CTX_wm_region(C);
285
286         sima_zoom_set_factor(sima, ar, RNA_float_get(op->ptr, "factor"));
287
288         ED_area_tag_redraw(CTX_wm_area(C));
289
290         /* XXX notifier? */
291 #if 0
292         if(image_preview_active(curarea, NULL, NULL)) {
293                 /* recalculates new preview rect */
294                 scrarea_do_windraw(curarea);
295                 image_preview_event(2);
296         }
297 #endif
298         
299         return OPERATOR_FINISHED;
300 }
301
302 static int view_zoom_invoke(bContext *C, wmOperator *op, wmEvent *event)
303 {
304         view_zoom_init(C, op, event);
305         return OPERATOR_RUNNING_MODAL;
306 }
307
308 static int view_zoom_modal(bContext *C, wmOperator *op, wmEvent *event)
309 {
310         SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C);
311         ARegion *ar= CTX_wm_region(C);
312         ViewZoomData *vpd= op->customdata;
313         float factor;
314
315         switch(event->type) {
316                 case MOUSEMOVE:
317                         factor= 1.0 + (vpd->x-event->x+vpd->y-event->y)/300.0f;
318                         RNA_float_set(op->ptr, "factor", factor);
319                         sima_zoom_set(sima, ar, vpd->zoom*factor);
320                         ED_area_tag_redraw(CTX_wm_area(C));
321                         break;
322                 case MIDDLEMOUSE:
323                         if(event->val==0) {
324                                 view_zoom_exit(C, op, 0);
325                                 return OPERATOR_FINISHED;
326                         }
327                         break;
328         }
329
330         return OPERATOR_RUNNING_MODAL;
331 }
332
333 static int view_zoom_cancel(bContext *C, wmOperator *op)
334 {
335         view_zoom_exit(C, op, 1);
336         return OPERATOR_CANCELLED;
337 }
338
339 void IMAGE_OT_view_zoom(wmOperatorType *ot)
340 {
341         /* identifiers */
342         ot->name= "View Zoom";
343         ot->idname= "IMAGE_OT_view_zoom";
344         
345         /* api callbacks */
346         ot->exec= view_zoom_exec;
347         ot->invoke= view_zoom_invoke;
348         ot->modal= view_zoom_modal;
349         ot->cancel= view_zoom_cancel;
350         ot->poll= space_image_main_area_poll;
351         
352         /* properties */
353         RNA_def_float(ot->srna, "factor", 0.0f, 0.0f, FLT_MAX,
354                 "Factor", "Zoom factor, values higher than 1.0 zoom in, lower values zoom out.", -FLT_MAX, FLT_MAX);
355 }
356
357 /********************** view all operator *********************/
358
359 /* Updates the fields of the View2D member of the SpaceImage struct.
360  * Default behavior is to reset the position of the image and set the zoom to 1
361  * If the image will not fit within the window rectangle, the zoom is adjusted */
362
363 static int view_all_exec(bContext *C, wmOperator *op)
364 {
365         SpaceImage *sima;
366         ARegion *ar;
367         Scene *scene;
368         Object *obedit;
369         Image *ima;
370         ImBuf *ibuf;
371         float aspx, aspy, zoomx, zoomy, w, h;
372         int width, height;
373
374         /* retrieve state */
375         sima= (SpaceImage*)CTX_wm_space_data(C);
376         ar= CTX_wm_region(C);
377         scene= (Scene*)CTX_data_scene(C);
378         obedit= CTX_data_edit_object(C);
379
380         ima= ED_space_image(sima);
381         ibuf= ED_space_image_buffer(sima);
382         ED_space_image_size(sima, &width, &height);
383         ED_space_image_aspect(sima, &aspx, &aspy);
384
385         w= width*aspx;
386         h= height*aspy;
387         
388         /* check if the image will fit in the image with zoom==1 */
389         width = ar->winrct.xmax - ar->winrct.xmin + 1;
390         height = ar->winrct.ymax - ar->winrct.ymin + 1;
391
392         if((w >= width || h >= height) && (width > 0 && height > 0)) {
393                 /* find the zoom value that will fit the image in the image space */
394                 zoomx= width/w;
395                 zoomy= height/h;
396                 sima_zoom_set(sima, ar, 1.0f/power_of_2(1/MIN2(zoomx, zoomy)));
397         }
398         else
399                 sima_zoom_set(sima, ar, 1.0f);
400
401         sima->xof= sima->yof= 0.0f;
402
403         ED_area_tag_redraw(CTX_wm_area(C));
404         
405         return OPERATOR_FINISHED;
406 }
407
408 void IMAGE_OT_view_all(wmOperatorType *ot)
409 {
410         /* identifiers */
411         ot->name= "View All";
412         ot->idname= "IMAGE_OT_view_all";
413         
414         /* api callbacks */
415         ot->exec= view_all_exec;
416         ot->poll= space_image_main_area_poll;
417 }
418
419 /********************** view selected operator *********************/
420
421 static int view_selected_exec(bContext *C, wmOperator *op)
422 {
423         SpaceImage *sima;
424         ARegion *ar;
425         Scene *scene;
426         Object *obedit;
427         Image *ima;
428         ImBuf *ibuf;
429         float size, min[2], max[2], d[2];
430         int width, height;
431
432         /* retrieve state */
433         sima= (SpaceImage*)CTX_wm_space_data(C);
434         ar= CTX_wm_region(C);
435         scene= (Scene*)CTX_data_scene(C);
436         obedit= CTX_data_edit_object(C);
437
438         ima= ED_space_image(sima);
439         ibuf= ED_space_image_buffer(sima);
440         ED_space_image_size(sima, &width, &height);
441
442         /* get bounds */
443         if(!ED_uvedit_minmax(scene, ima, obedit, min, max))
444                 return OPERATOR_CANCELLED;
445
446         /* adjust offset and zoom */
447         sima->xof= (int)(((min[0] + max[0])*0.5f - 0.5f)*width);
448         sima->yof= (int)(((min[1] + max[1])*0.5f - 0.5f)*height);
449
450         d[0]= max[0] - min[0];
451         d[1]= max[1] - min[1];
452         size= 0.5*MAX2(d[0], d[1])*MAX2(width, height)/256.0f;
453         
454         if(size<=0.01) size= 0.01;
455         sima_zoom_set(sima, ar, 0.7/size);
456
457         ED_area_tag_redraw(CTX_wm_area(C));
458         
459         return OPERATOR_FINISHED;
460 }
461
462 void IMAGE_OT_view_selected(wmOperatorType *ot)
463 {
464         /* identifiers */
465         ot->name= "View Center";
466         ot->idname= "IMAGE_OT_view_selected";
467         
468         /* api callbacks */
469         ot->exec= view_selected_exec;
470         ot->poll= ED_operator_uvedit;
471 }
472
473 /********************** view zoom in/out operator *********************/
474
475 static int view_zoom_in_exec(bContext *C, wmOperator *op)
476 {
477         SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C);
478         ARegion *ar= CTX_wm_region(C);
479
480         sima_zoom_set_factor(sima, ar, 1.25f);
481
482         ED_area_tag_redraw(CTX_wm_area(C));
483         
484         return OPERATOR_FINISHED;
485 }
486
487 void IMAGE_OT_view_zoom_in(wmOperatorType *ot)
488 {
489         /* identifiers */
490         ot->name= "View Zoom In";
491         ot->idname= "IMAGE_OT_view_zoom_in";
492         
493         /* api callbacks */
494         ot->exec= view_zoom_in_exec;
495         ot->poll= space_image_main_area_poll;
496 }
497
498 static int view_zoom_out_exec(bContext *C, wmOperator *op)
499 {
500         SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C);
501         ARegion *ar= CTX_wm_region(C);
502
503         sima_zoom_set_factor(sima, ar, 0.8f);
504
505         ED_area_tag_redraw(CTX_wm_area(C));
506         
507         return OPERATOR_FINISHED;
508 }
509
510 void IMAGE_OT_view_zoom_out(wmOperatorType *ot)
511 {
512         /* identifiers */
513         ot->name= "View Zoom Out";
514         ot->idname= "IMAGE_OT_view_zoom_out";
515         
516         /* api callbacks */
517         ot->exec= view_zoom_out_exec;
518         ot->poll= space_image_main_area_poll;
519 }
520
521 /********************** view zoom ratio operator *********************/
522
523 static int view_zoom_ratio_exec(bContext *C, wmOperator *op)
524 {
525         SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C);
526         ARegion *ar= CTX_wm_region(C);
527
528         sima_zoom_set(sima, ar, RNA_float_get(op->ptr, "ratio"));
529         
530         /* ensure pixel exact locations for draw */
531         sima->xof= (int)sima->xof;
532         sima->yof= (int)sima->yof;
533
534         /* XXX notifier? */
535 #if 0
536         if(image_preview_active(curarea, NULL, NULL)) {
537                 /* recalculates new preview rect */
538                 scrarea_do_windraw(curarea);
539                 image_preview_event(2);
540         }
541 #endif
542
543         ED_area_tag_redraw(CTX_wm_area(C));
544         
545         return OPERATOR_FINISHED;
546 }
547
548 void IMAGE_OT_view_zoom_ratio(wmOperatorType *ot)
549 {
550         /* identifiers */
551         ot->name= "View Zoom Ratio";
552         ot->idname= "IMAGE_OT_view_zoom_ratio";
553         
554         /* api callbacks */
555         ot->exec= view_zoom_ratio_exec;
556         ot->poll= space_image_main_area_poll;
557         
558         /* properties */
559         RNA_def_float(ot->srna, "ratio", 0.0f, 0.0f, FLT_MAX,
560                 "Ratio", "Zoom ratio, 1.0 is 1:1, higher is zoomed in, lower is zoomed out.", -FLT_MAX, FLT_MAX);
561 }
562
563 /**************** load/replace/save callbacks ******************/
564
565 static char *filesel_imagetype_string(Image *ima)
566 {
567         char *strp, *str= MEM_callocN(14*32, "menu for filesel");
568         
569         strp= str;
570         str += sprintf(str, "Save Image as: %%t|");
571         str += sprintf(str, "Targa %%x%d|", R_TARGA);
572         str += sprintf(str, "Targa Raw %%x%d|", R_RAWTGA);
573         str += sprintf(str, "PNG %%x%d|", R_PNG);
574         str += sprintf(str, "BMP %%x%d|", R_BMP);
575         str += sprintf(str, "Jpeg %%x%d|", R_JPEG90);
576         str += sprintf(str, "Iris %%x%d|", R_IRIS);
577         if(G.have_libtiff)
578                 str += sprintf(str, "Tiff %%x%d|", R_TIFF);
579         str += sprintf(str, "Radiance HDR %%x%d|", R_RADHDR);
580         str += sprintf(str, "Cineon %%x%d|", R_CINEON);
581         str += sprintf(str, "DPX %%x%d|", R_DPX);
582 #ifdef WITH_OPENEXR
583         str += sprintf(str, "OpenEXR %%x%d|", R_OPENEXR);
584         /* saving sequences of multilayer won't work, they copy buffers  */
585         if(ima->source==IMA_SRC_SEQUENCE && ima->type==IMA_TYPE_MULTILAYER);
586         else str += sprintf(str, "MultiLayer %%x%d|", R_MULTILAYER);
587 #endif  
588         return strp;
589 }
590
591 static void image_filesel(bContext *C, wmOperator *op, const char *path)
592 {
593         RNA_string_set(op->ptr, "filename", path);
594         WM_event_add_fileselect(C, op); 
595 }
596
597 /******************** open image operator ********************/
598
599 static int open_exec(bContext *C, wmOperator *op)
600 {
601         SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C);
602         Scene *scene= CTX_data_scene(C);
603         Object *obedit= CTX_data_edit_object(C);
604         Image *ima= NULL;
605         char *str;
606
607         str= RNA_string_get_alloc(op->ptr, "filename", NULL, 0);
608         ima= BKE_add_image_file(str, scene->r.cfra);
609         MEM_freeN(str);
610
611         if(!ima)
612                 return OPERATOR_CANCELLED;
613
614         BKE_image_signal(ima, &sima->iuser, IMA_SIGNAL_RELOAD);
615         ED_space_image_set(C, sima, scene, obedit, ima);
616
617         return OPERATOR_FINISHED;
618 }
619
620 static int open_invoke(bContext *C, wmOperator *op, wmEvent *event)
621 {
622         SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C);
623         char *path= (sima->image)? sima->image->name: U.textudir;
624
625         if(RNA_property_is_set(op->ptr, "filename"))
626                 return open_exec(C, op);
627         
628         image_filesel(C, op, path);
629
630         return OPERATOR_RUNNING_MODAL;
631 }
632
633 void IMAGE_OT_open(wmOperatorType *ot)
634 {
635         /* identifiers */
636         ot->name= "Open";
637         ot->idname= "IMAGE_OT_open";
638         
639         /* api callbacks */
640         ot->exec= open_exec;
641         ot->invoke= open_invoke;
642         ot->poll= space_image_poll;
643
644         /* flags */
645         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
646
647         /* properties */
648         RNA_def_string_file_path(ot->srna, "filename", "", 0, "Filename", "File path of image to open.");
649 }
650
651 /******************** replace image operator ********************/
652
653 static int replace_exec(bContext *C, wmOperator *op)
654 {
655         SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C);
656         char *str;
657
658         if(!sima->image)
659                 return OPERATOR_CANCELLED;
660         
661         str= RNA_string_get_alloc(op->ptr, "filename", NULL, 0);
662         BLI_strncpy(sima->image->name, str, sizeof(sima->image->name)-1); /* we cant do much if the str is longer then 240 :/ */
663         MEM_freeN(str);
664
665         BKE_image_signal(sima->image, &sima->iuser, IMA_SIGNAL_RELOAD);
666         WM_event_add_notifier(C, NC_IMAGE|NA_EDITED, sima->image);
667
668         return OPERATOR_FINISHED;
669 }
670
671 static int replace_invoke(bContext *C, wmOperator *op, wmEvent *event)
672 {
673         SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C);
674         char *path= (sima->image)? sima->image->name: U.textudir;
675
676         if(!sima->image)
677                 return OPERATOR_CANCELLED;
678
679         if(RNA_property_is_set(op->ptr, "filename"))
680                 return replace_exec(C, op);
681
682         image_filesel(C, op, path);
683
684         return OPERATOR_RUNNING_MODAL;
685 }
686
687 void IMAGE_OT_replace(wmOperatorType *ot)
688 {
689         /* identifiers */
690         ot->name= "Replace";
691         ot->idname= "IMAGE_OT_replace";
692         
693         /* api callbacks */
694         ot->exec= replace_exec;
695         ot->invoke= replace_invoke;
696         ot->poll= space_image_poll;
697
698         /* flags */
699         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
700
701         /* properties */
702         RNA_def_string_file_path(ot->srna, "filename", "", 0, "Filename", "File path of image to replace current image with.");
703 }
704
705 /******************** save image as operator ********************/
706
707 static void save_image_doit(bContext *C, SpaceImage *sima, Scene *scene, wmOperator *op, char *name)
708 {
709         Image *ima= ED_space_image(sima);
710         ImBuf *ibuf= ED_space_image_buffer(sima);
711         int len;
712         char str[FILE_MAXDIR+FILE_MAXFILE];
713
714         if (ibuf) {
715                 BLI_strncpy(str, name, sizeof(str));
716
717                 BLI_convertstringcode(str, G.sce);
718                 BLI_convertstringframe(str, scene->r.cfra);
719                 
720                 
721                 if(scene->r.scemode & R_EXTENSION)  {
722                         BKE_add_image_extension(scene, str, sima->imtypenr);
723                         BKE_add_image_extension(scene, name, sima->imtypenr);
724                 }
725                 
726                 if(1) { // XXX saveover(str)) {
727                         
728                         /* enforce user setting for RGB or RGBA, but skip BW */
729                         if(scene->r.planes==32)
730                                 ibuf->depth= 32;
731                         else if(scene->r.planes==24)
732                                 ibuf->depth= 24;
733                         
734                         WM_cursor_wait(1);
735
736                         if(sima->imtypenr==R_MULTILAYER) {
737                                 RenderResult *rr= BKE_image_get_renderresult(scene, ima);
738                                 if(rr) {
739                                         RE_WriteRenderResult(rr, str, scene->r.quality);
740                                         
741                                         BLI_strncpy(ima->name, name, sizeof(ima->name));
742                                         BLI_strncpy(ibuf->name, str, sizeof(ibuf->name));
743                                         
744                                         /* should be function? nevertheless, saving only happens here */
745                                         for(ibuf= ima->ibufs.first; ibuf; ibuf= ibuf->next)
746                                                 ibuf->userflags &= ~IB_BITMAPDIRTY;
747                                         
748                                 }
749                                 else
750                                         BKE_report(op->reports, RPT_ERROR, "Did not write, no Multilayer Image");
751                         }
752                         else if (BKE_write_ibuf(scene, ibuf, str, sima->imtypenr, scene->r.subimtype, scene->r.quality)) {
753                                 BLI_strncpy(ima->name, name, sizeof(ima->name));
754                                 BLI_strncpy(ibuf->name, str, sizeof(ibuf->name));
755                                 
756                                 ibuf->userflags &= ~IB_BITMAPDIRTY;
757                                 
758                                 /* change type? */
759                                 if( ELEM(ima->source, IMA_SRC_GENERATED, IMA_SRC_VIEWER)) {
760                                         ima->source= IMA_SRC_FILE;
761                                         ima->type= IMA_TYPE_IMAGE;
762                                 }
763                                 if(ima->type==IMA_TYPE_R_RESULT)
764                                         ima->type= IMA_TYPE_IMAGE;
765                                 
766                                 /* name image as how we saved it */
767                                 len= strlen(str);
768                                 while (len > 0 && str[len - 1] != '/' && str[len - 1] != '\\') len--;
769                                 rename_id(&ima->id, str+len);
770                         } 
771                         else
772                                 BKE_reportf(op->reports, RPT_ERROR, "Couldn't write image: %s", str);
773
774                         WM_event_add_notifier(C, NC_IMAGE|NA_EDITED, sima->image);
775
776                         WM_cursor_wait(0);
777                 }
778         }
779 }
780
781 static int save_as_exec(bContext *C, wmOperator *op)
782 {
783         SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C);
784         Scene *scene= CTX_data_scene(C);
785         Image *ima = ED_space_image(sima);
786         char *str;
787
788         if(!ima)
789                 return OPERATOR_CANCELLED;
790
791         str= RNA_string_get_alloc(op->ptr, "filename", NULL, 0);
792         save_image_doit(C, sima, scene, op, str);
793         MEM_freeN(str);
794
795         return OPERATOR_FINISHED;
796 }
797
798 static int save_as_invoke(bContext *C, wmOperator *op, wmEvent *event)
799 {
800         SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C);
801         Image *ima = ED_space_image(sima);
802         ImBuf *ibuf= ED_space_image_buffer(sima);
803         Scene *scene= CTX_data_scene(C);
804
805         if(RNA_property_is_set(op->ptr, "filename"))
806                 return save_as_exec(C, op);
807         
808         if(!ima)
809                 return OPERATOR_CANCELLED;
810
811         /* always opens fileselect */
812         if(ibuf) {
813                 char *strp;
814                 
815                 strp= filesel_imagetype_string(ima);
816                 
817                 /* cant save multilayer sequence, ima->rr isn't valid for a specific frame */
818                 if(ima->rr && !(ima->source==IMA_SRC_SEQUENCE && ima->type==IMA_TYPE_MULTILAYER))
819                         sima->imtypenr= R_MULTILAYER;
820                 else if(ima->type==IMA_TYPE_R_RESULT)
821                         sima->imtypenr= scene->r.imtype;
822                 else
823                         sima->imtypenr= BKE_ftype_to_imtype(ibuf->ftype);
824                 
825                 // XXX activate_fileselect_menu(FILE_SPECIAL, "Save Image", name, strp, &sima->imtypenr, save_image_doit);
826                 // XXX note: we can give default menu enums to operator for this 
827                 
828                 image_filesel(C, op, ima->name);
829
830                 return OPERATOR_RUNNING_MODAL;
831         }
832
833         return OPERATOR_CANCELLED;
834 }
835
836 void IMAGE_OT_save_as(wmOperatorType *ot)
837 {
838         /* identifiers */
839         ot->name= "Save As";
840         ot->idname= "IMAGE_OT_save_as";
841         
842         /* api callbacks */
843         ot->exec= save_as_exec;
844         ot->invoke= save_as_invoke;
845         ot->poll= space_image_poll;
846
847         /* flags */
848         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
849
850         /* properties */
851         RNA_def_string_file_path(ot->srna, "filename", "", 0, "Filename", "File path to save image to.");
852 }
853
854 /******************** save image operator ********************/
855
856 static int save_exec(bContext *C, wmOperator *op)
857 {
858         SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C);
859         Image *ima = ED_space_image(sima);
860         ImBuf *ibuf= ED_space_image_buffer(sima);
861         Scene *scene= CTX_data_scene(C);
862         char name[FILE_MAXDIR+FILE_MAXFILE];
863
864         if(!ima)
865                 return OPERATOR_CANCELLED;
866
867         /* if exists, saves over without fileselect */
868
869         strcpy(name, ima->name);
870
871         if(ibuf) {
872                 if(BLI_exists(ibuf->name)) {
873                         if(BKE_image_get_renderresult(scene, ima)) 
874                                 sima->imtypenr= R_MULTILAYER;
875                         else 
876                                 sima->imtypenr= BKE_ftype_to_imtype(ibuf->ftype);
877                         
878                         save_image_doit(C, sima, scene, op, ibuf->name);
879                 }
880                 else
881                         return save_as_exec(C, op);
882         }
883         
884         return OPERATOR_FINISHED;
885 }
886
887 void IMAGE_OT_save(wmOperatorType *ot)
888 {
889         /* identifiers */
890         ot->name= "Save";
891         ot->idname= "IMAGE_OT_save";
892         
893         /* api callbacks */
894         ot->exec= save_exec;
895         ot->poll= space_image_poll;
896
897         /* flags */
898         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
899 }
900
901 /******************* save sequence operator ********************/
902
903 static int save_sequence_exec(bContext *C, wmOperator *op)
904 {
905         SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C);
906         ImBuf *ibuf;
907         int tot= 0;
908         char di[FILE_MAX], fi[FILE_MAX];
909         
910         if(sima->image==NULL)
911                 return OPERATOR_CANCELLED;
912
913         if(sima->image->source!=IMA_SRC_SEQUENCE) {
914                 BKE_report(op->reports, RPT_ERROR, "Can only save sequence on image sequences.");
915                 return OPERATOR_CANCELLED;
916         }
917
918         if(sima->image->type==IMA_TYPE_MULTILAYER) {
919                 BKE_report(op->reports, RPT_ERROR, "Can't save multilayer sequences.");
920                 return OPERATOR_CANCELLED;
921         }
922         
923         /* get total */
924         for(ibuf= sima->image->ibufs.first; ibuf; ibuf= ibuf->next) 
925                 if(ibuf->userflags & IB_BITMAPDIRTY)
926                         tot++;
927         
928         if(tot==0) {
929                 BKE_report(op->reports, RPT_WARNING, "No images have been changed.");
930                 return OPERATOR_CANCELLED;
931         }
932
933         /* get a filename for menu */
934         for(ibuf= sima->image->ibufs.first; ibuf; ibuf= ibuf->next) 
935                 if(ibuf->userflags & IB_BITMAPDIRTY)
936                         break;
937         
938         BLI_strncpy(di, ibuf->name, FILE_MAX);
939         BLI_splitdirstring(di, fi);
940         
941         BKE_reportf(op->reports, RPT_INFO, "%d Image(s) will be saved in %s", tot, di);
942
943         for(ibuf= sima->image->ibufs.first; ibuf; ibuf= ibuf->next) {
944                 if(ibuf->userflags & IB_BITMAPDIRTY) {
945                         char name[FILE_MAX];
946                         BLI_strncpy(name, ibuf->name, sizeof(name));
947                         
948                         BLI_convertstringcode(name, G.sce);
949
950                         if(0 == IMB_saveiff(ibuf, name, IB_rect | IB_zbuf | IB_zbuffloat)) {
951                                 BKE_reportf(op->reports, RPT_ERROR, "Could not write image %s.", name);
952                                 break;
953                         }
954
955                         printf("Saved: %s\n", ibuf->name);
956                         ibuf->userflags &= ~IB_BITMAPDIRTY;
957                 }
958         }
959
960         return OPERATOR_FINISHED;
961 }
962
963 void IMAGE_OT_save_sequence(wmOperatorType *ot)
964 {
965         /* identifiers */
966         ot->name= "Save Sequence";
967         ot->idname= "IMAGE_OT_save_sequence";
968         
969         /* api callbacks */
970         ot->exec= save_sequence_exec;
971         ot->poll= space_image_poll;
972
973         /* flags */
974         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
975 }
976
977 /******************** reload image operator ********************/
978
979 static int reload_exec(bContext *C, wmOperator *op)
980 {
981         SpaceImage *sima;
982
983         /* retrieve state */
984         sima= (SpaceImage*)CTX_wm_space_data(C);
985
986         if(!sima->image)
987                 return OPERATOR_CANCELLED;
988
989         BKE_image_signal(sima->image, &sima->iuser, IMA_SIGNAL_RELOAD);
990         /* ED_space_image_set(C, sima, scene, obedit, NULL); - do we really need this? */
991
992         // XXX BIF_preview_changed(ID_TE);
993         WM_event_add_notifier(C, NC_IMAGE|NA_EDITED, sima->image);
994         ED_area_tag_redraw(CTX_wm_area(C));
995         
996         return OPERATOR_FINISHED;
997 }
998
999 void IMAGE_OT_reload(wmOperatorType *ot)
1000 {
1001         /* identifiers */
1002         ot->name= "Reload";
1003         ot->idname= "IMAGE_OT_reload";
1004         
1005         /* api callbacks */
1006         ot->exec= reload_exec;
1007         ot->poll= space_image_poll;
1008
1009         /* flags */
1010         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1011 }
1012
1013 /********************** new image operator *********************/
1014
1015 static int new_exec(bContext *C, wmOperator *op)
1016 {
1017         SpaceImage *sima;
1018         Scene *scene;
1019         Object *obedit;
1020         Image *ima;
1021         char name[22];
1022         float color[4];
1023         int width, height, floatbuf, uvtestgrid;
1024
1025         /* retrieve state */
1026         sima= (SpaceImage*)CTX_wm_space_data(C);
1027         scene= (Scene*)CTX_data_scene(C);
1028         obedit= CTX_data_edit_object(C);
1029
1030         RNA_string_get(op->ptr, "name", name);
1031         width= RNA_int_get(op->ptr, "width");
1032         height= RNA_int_get(op->ptr, "height");
1033         floatbuf= RNA_boolean_get(op->ptr, "float");
1034         uvtestgrid= RNA_boolean_get(op->ptr, "uv_test_grid");
1035         RNA_float_get_array(op->ptr, "color", color);
1036         color[3]= RNA_float_get(op->ptr, "alpha");
1037
1038         ima = BKE_add_image_size(width, height, name, floatbuf, uvtestgrid, color);
1039         BKE_image_signal(sima->image, &sima->iuser, IMA_SIGNAL_USER_NEW_IMAGE);
1040         ED_space_image_set(C, sima, scene, obedit, ima);
1041         
1042         return OPERATOR_FINISHED;
1043 }
1044
1045 void IMAGE_OT_new(wmOperatorType *ot)
1046 {
1047         /* identifiers */
1048         ot->name= "New";
1049         ot->idname= "IMAGE_OT_new";
1050         
1051         /* api callbacks */
1052         ot->exec= new_exec;
1053         ot->poll= space_image_poll;
1054
1055         /* flags */
1056         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1057
1058         /* properties */
1059         RNA_def_string(ot->srna, "name", "Untitled", 21, "Name", "Image datablock name.");
1060         RNA_def_int(ot->srna, "width", 1024, 1, INT_MAX, "Width", "Image width.", 1, 16384);
1061         RNA_def_int(ot->srna, "height", 1024, 1, INT_MAX, "Height", "Image height.", 1, 16384);
1062         RNA_def_float_color(ot->srna, "color", 3, NULL, 0.0f, FLT_MAX, "Color", "Default fill color.", 0.0f, 1.0f);
1063         RNA_def_float(ot->srna, "alpha", 1.0f, 0.0f, 1.0f, "Alpha", "Default fill alpha.", 0.0f, 1.0f);
1064         RNA_def_boolean(ot->srna, "uv_test_grid", 0, "UV Test Grid", "Fill the image with a grid for UV map testing.");
1065         RNA_def_boolean(ot->srna, "float", 0, "32 bit Float", "Create image with 32 bit floating point bit depth.");
1066 }
1067
1068 /********************* pack operator *********************/
1069
1070 static int pack_test(bContext *C, wmOperator *op)
1071 {
1072         Image *ima= CTX_data_edit_image(C);
1073         int as_png= RNA_boolean_get(op->ptr, "as_png");
1074
1075         if(!ima)
1076                 return 0;
1077         if(!as_png && ima->packedfile)
1078                 return 0;
1079
1080         if(ima->source==IMA_SRC_SEQUENCE || ima->source==IMA_SRC_MOVIE) {
1081                 BKE_report(op->reports, RPT_ERROR, "Can't pack movie or image sequence.");
1082                 return 0;
1083         }
1084
1085         return 1;
1086 }
1087
1088 static int pack_exec(bContext *C, wmOperator *op)
1089 {
1090         SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C);
1091         Image *ima= ED_space_image(sima);
1092         ImBuf *ibuf= ED_space_image_buffer(sima);
1093         int as_png= RNA_boolean_get(op->ptr, "as_png");
1094
1095         if(!pack_test(C, op))
1096                 return OPERATOR_CANCELLED;
1097         
1098         if(!as_png && (ibuf && (ibuf->userflags & IB_BITMAPDIRTY))) {
1099                 BKE_report(op->reports, RPT_ERROR, "Can't pack edited image from disk, only as internal PNG.");
1100                 return OPERATOR_CANCELLED;
1101         }
1102
1103         if(as_png)
1104                 BKE_image_memorypack(ima);
1105         else
1106                 ima->packedfile= newPackedFile(ima->name);
1107
1108         return OPERATOR_FINISHED;
1109 }
1110
1111 static int pack_invoke(bContext *C, wmOperator *op, wmEvent *event)
1112 {
1113         SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C);
1114         ImBuf *ibuf= ED_space_image_buffer(sima);
1115         uiMenuItem *head;
1116         int as_png= RNA_boolean_get(op->ptr, "as_png");
1117
1118         if(!pack_test(C, op))
1119                 return OPERATOR_CANCELLED;
1120         
1121         if(!as_png && (ibuf && (ibuf->userflags & IB_BITMAPDIRTY))) {
1122                 head= uiPupMenuBegin("OK", ICON_HELP);
1123                 uiMenuItemBooleanO(head, "Can't pack edited image from disk. Pack as internal PNG?", 0, op->idname, "as_png", 1);
1124                 uiPupMenuEnd(C, head);
1125
1126                 return OPERATOR_CANCELLED;
1127         }
1128
1129         return pack_exec(C, op);
1130 }
1131
1132 void IMAGE_OT_pack(wmOperatorType *ot)
1133 {
1134         /* identifiers */
1135         ot->name= "Pack";
1136         ot->idname= "IMAGE_OT_pack";
1137         
1138         /* api callbacks */
1139         ot->exec= pack_exec;
1140         ot->invoke= pack_invoke;
1141         ot->poll= space_image_poll;
1142
1143         /* flags */
1144         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1145
1146         /* properties */
1147         RNA_def_boolean(ot->srna, "as_png", 0, "Pack As PNG", "Pack image as lossless PNG.");
1148 }
1149
1150 /********************* unpack operator *********************/
1151
1152 static int unpack_exec(bContext *C, wmOperator *op)
1153 {
1154         Image *ima= CTX_data_edit_image(C);
1155
1156         if(!ima)
1157                 return OPERATOR_CANCELLED;
1158         if(!ima->packedfile)
1159                 return OPERATOR_CANCELLED;
1160
1161         if(ima->source==IMA_SRC_SEQUENCE || ima->source==IMA_SRC_MOVIE) {
1162                 BKE_report(op->reports, RPT_ERROR, "Can't unpack movie or image sequence.");
1163                 return OPERATOR_CANCELLED;
1164         }
1165
1166         if(G.fileflags & G_AUTOPACK)
1167                 BKE_report(op->reports, RPT_WARNING, "AutoPack is enabled, so image will be packed again on file save.");
1168         
1169         unpackImage(ima, PF_ASK);
1170
1171         return OPERATOR_FINISHED;
1172 }
1173
1174 void IMAGE_OT_unpack(wmOperatorType *ot)
1175 {
1176         /* identifiers */
1177         ot->name= "Unpack";
1178         ot->idname= "IMAGE_OT_unpack";
1179         
1180         /* api callbacks */
1181         ot->exec= unpack_exec;
1182         ot->poll= space_image_poll;
1183
1184         /* flags */
1185         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1186 }
1187
1188 /******************** sample image operator ********************/
1189
1190 typedef struct ImageSampleInfo {
1191         ARegionType *art;
1192         void *draw_handle;
1193         int x, y;
1194
1195         char col[4];
1196         float colf[4];
1197         int z;
1198         float zf;
1199
1200         char *colp;
1201         float *colfp;
1202         int *zp;
1203         float *zfp;
1204
1205         int draw;
1206 } ImageSampleInfo;
1207
1208 static void sample_draw(const bContext *C, ARegion *ar, void *arg_info)
1209 {
1210         SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C);
1211         ImBuf *ibuf= ED_space_image_buffer(sima);
1212         ImageSampleInfo *info= arg_info;
1213
1214         if(ibuf == NULL)
1215                 return;
1216         
1217         draw_image_info(ar, ibuf->channels, info->x, info->y, info->colp,
1218                 info->colfp, info->zp, info->zfp);
1219 }
1220
1221 static void sample_apply(bContext *C, wmOperator *op, wmEvent *event)
1222 {
1223         SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C);
1224         ARegion *ar= CTX_wm_region(C);
1225         ImBuf *ibuf= ED_space_image_buffer(sima);
1226         ImageSampleInfo *info= op->customdata;
1227         float fx, fy;
1228         int x, y;
1229         
1230         if(ibuf == NULL)
1231                 return;
1232
1233         x= event->x - ar->winrct.xmin;
1234         y= event->y - ar->winrct.ymin;
1235         UI_view2d_region_to_view(&ar->v2d, x, y, &fx, &fy);
1236
1237         if(fx>=0.0 && fy>=0.0 && fx<1.0 && fy<1.0) {
1238                 float *fp;
1239                 char *cp;
1240                 int x= (int)(fx*ibuf->x), y= (int)(fy*ibuf->y);
1241
1242                 CLAMP(x, 0, ibuf->x-1);
1243                 CLAMP(y, 0, ibuf->y-1);
1244
1245                 info->x= x;
1246                 info->y= y;
1247                 info->draw= 1;
1248
1249                 info->colp= NULL;
1250                 info->colfp= NULL;
1251                 info->zp= NULL;
1252                 info->zfp= NULL;
1253                 
1254                 if(ibuf->rect) {
1255                         cp= (char *)(ibuf->rect + y*ibuf->x + x);
1256
1257                         info->col[0]= cp[0];
1258                         info->col[1]= cp[1];
1259                         info->col[2]= cp[2];
1260                         info->col[3]= cp[3];
1261                         info->colp= info->col;
1262
1263                         info->colf[0]= (float)cp[0]/255.0f;
1264                         info->colf[1]= (float)cp[1]/255.0f;
1265                         info->colf[2]= (float)cp[2]/255.0f;
1266                         info->colf[3]= (float)cp[3]/255.0f;
1267                         info->colfp= info->colf;
1268                 }
1269                 if(ibuf->rect_float) {
1270                         fp= (ibuf->rect_float + (ibuf->channels)*(y*ibuf->x + x));
1271
1272                         info->colf[0]= fp[0];
1273                         info->colf[1]= fp[1];
1274                         info->colf[2]= fp[2];
1275                         info->colf[3]= fp[4];
1276                         info->colfp= info->colf;
1277                 }
1278
1279                 if(ibuf->zbuf) {
1280                         info->z= ibuf->zbuf[y*ibuf->x + x];
1281                         info->zp= &info->z;
1282                 }
1283                 if(ibuf->zbuf_float) {
1284                         info->zf= ibuf->zbuf_float[y*ibuf->x + x];
1285                         info->zfp= &info->zf;
1286                 }
1287                 
1288                 if(sima->cumap && ibuf->channels==4) {
1289                         /* we reuse this callback for set curves point operators */
1290                         if(RNA_struct_find_property(op->ptr, "point")) {
1291                                 int point= RNA_enum_get(op->ptr, "point");
1292
1293                                 if(point == 1) {
1294                                         curvemapping_set_black_white(sima->cumap, NULL, info->colfp);
1295                                         curvemapping_do_ibuf(sima->cumap, ibuf);
1296                                 }
1297                                 else if(point == 0) {
1298                                         curvemapping_set_black_white(sima->cumap, info->colfp, NULL);
1299                                         curvemapping_do_ibuf(sima->cumap, ibuf);
1300                                 }
1301                         }
1302                 }
1303                                 
1304                 // XXX node curve integration ..
1305 #if 0
1306                 {
1307                         ScrArea *sa, *cur= curarea;
1308                         
1309                         node_curvemap_sample(fp);       /* sends global to node editor */
1310                         for(sa= G.curscreen->areabase.first; sa; sa= sa->next) {
1311                                 if(sa->spacetype==SPACE_NODE) {
1312                                         areawinset(sa->win);
1313                                         scrarea_do_windraw(sa);
1314                                 }
1315                         }
1316                         node_curvemap_sample(NULL);             /* clears global in node editor */
1317                         curarea= cur;
1318                 }
1319 #endif
1320         }
1321         else
1322                 info->draw= 0;
1323
1324         ED_area_tag_redraw(CTX_wm_area(C));
1325 }
1326
1327 static void sample_exit(bContext *C, wmOperator *op)
1328 {
1329         ImageSampleInfo *info= op->customdata;
1330
1331         ED_region_draw_cb_exit(info->art, info->draw_handle);
1332         ED_area_tag_redraw(CTX_wm_area(C));
1333         MEM_freeN(info);
1334 }
1335
1336 static int sample_invoke(bContext *C, wmOperator *op, wmEvent *event)
1337 {
1338         SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C);
1339         ARegion *ar= CTX_wm_region(C);
1340         ImBuf *ibuf= ED_space_image_buffer(sima);
1341         ImageSampleInfo *info;
1342
1343         if(ibuf == NULL)
1344                 return OPERATOR_CANCELLED;
1345         
1346         info= MEM_callocN(sizeof(ImageSampleInfo), "ImageSampleInfo");
1347         info->art= ar->type;
1348         info->draw_handle = ED_region_draw_cb_activate(ar->type, sample_draw, info, REGION_DRAW_POST);
1349         op->customdata= info;
1350
1351         sample_apply(C, op, event);
1352
1353         WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
1354
1355         return OPERATOR_RUNNING_MODAL;
1356 }
1357
1358 static int sample_modal(bContext *C, wmOperator *op, wmEvent *event)
1359 {
1360         switch(event->type) {
1361                 case LEFTMOUSE:
1362                 case RIGHTMOUSE: // XXX hardcoded
1363                         sample_exit(C, op);
1364                         return OPERATOR_CANCELLED;
1365                 case MOUSEMOVE:
1366                         sample_apply(C, op, event);
1367                         break;
1368         }
1369
1370         return OPERATOR_RUNNING_MODAL;
1371 }
1372
1373 static int sample_cancel(bContext *C, wmOperator *op)
1374 {
1375         sample_exit(C, op);
1376         return OPERATOR_CANCELLED;
1377 }
1378
1379 void IMAGE_OT_sample(wmOperatorType *ot)
1380 {
1381         /* identifiers */
1382         ot->name= "Sample";
1383         ot->idname= "IMAGE_OT_sample";
1384         
1385         /* api callbacks */
1386         ot->invoke= sample_invoke;
1387         ot->modal= sample_modal;
1388         ot->cancel= sample_cancel;
1389         ot->poll= space_image_main_area_poll;
1390 }
1391
1392 /******************** set curve point operator ********************/
1393
1394 void IMAGE_OT_set_curves_point(wmOperatorType *ot)
1395 {
1396         static EnumPropertyItem point_items[]= {
1397                 {0, "BLACK_POINT", "Black Point", ""},
1398                 {1, "WHITE_POINT", "White Point", ""},
1399                 {0, NULL, NULL, NULL}};
1400
1401         /* identifiers */
1402         ot->name= "Set Curves Point";
1403         ot->idname= "IMAGE_OT_set_curves_point";
1404
1405         /* flags */
1406         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1407         
1408         /* api callbacks */
1409         ot->invoke= sample_invoke;
1410         ot->modal= sample_modal;
1411         ot->cancel= sample_cancel;
1412         ot->poll= space_image_main_area_poll;
1413
1414         /* properties */
1415         RNA_def_enum(ot->srna, "point", point_items, 0, "Point", "Set black point or white point for curves.");
1416 }
1417
1418 /******************** record composite operator *********************/
1419
1420 typedef struct RecordCompositeData {
1421         wmTimer *timer;
1422         int old_cfra;
1423         int sfra, efra;
1424 } RecordCompositeData;
1425
1426 int record_composite_apply(bContext *C, wmOperator *op)
1427 {
1428         SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C);
1429         RecordCompositeData *rcd= op->customdata;
1430         Scene *scene= CTX_data_scene(C);
1431         ImBuf *ibuf;
1432         
1433         WM_timecursor(CTX_wm_window(C), scene->r.cfra);
1434
1435         // XXX scene->nodetree->test_break= blender_test_break;
1436         // XXX scene->nodetree->test_break= NULL;
1437         
1438         BKE_image_all_free_anim_ibufs(scene->r.cfra);
1439         ntreeCompositTagAnimated(scene->nodetree);
1440         ntreeCompositExecTree(scene->nodetree, &scene->r, scene->r.cfra != rcd->old_cfra);      /* 1 is no previews */
1441
1442         ED_area_tag_redraw(CTX_wm_area(C));
1443         
1444         ibuf= BKE_image_get_ibuf(sima->image, &sima->iuser);
1445         /* save memory in flipbooks */
1446         if(ibuf)
1447                 imb_freerectfloatImBuf(ibuf);
1448         
1449         scene->r.cfra++;
1450
1451         return (scene->r.cfra <= rcd->efra);
1452 }
1453
1454 static int record_composite_init(bContext *C, wmOperator *op)
1455 {
1456         SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C);
1457         Scene *scene= CTX_data_scene(C);
1458         RecordCompositeData *rcd;
1459
1460         if(sima->iuser.frames < 2)
1461                 return 0;
1462         if(scene->nodetree == NULL)
1463                 return 0;
1464         
1465         op->customdata= rcd= MEM_callocN(sizeof(RecordCompositeData), "ImageRecordCompositeData");
1466
1467         rcd->old_cfra= scene->r.cfra;
1468         rcd->sfra= sima->iuser.sfra;
1469         rcd->efra= sima->iuser.sfra + sima->iuser.frames-1;
1470         scene->r.cfra= rcd->sfra;
1471
1472         return 1;
1473 }
1474
1475 static void record_composite_exit(bContext *C, wmOperator *op)
1476 {
1477         Scene *scene= CTX_data_scene(C);
1478         SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C);
1479         RecordCompositeData *rcd= op->customdata;
1480
1481         scene->r.cfra= rcd->old_cfra;
1482
1483         WM_cursor_restore(CTX_wm_window(C));
1484
1485         if(rcd->timer)
1486                 WM_event_remove_window_timer(CTX_wm_window(C), rcd->timer);
1487
1488         WM_event_add_notifier(C, NC_IMAGE|NA_EDITED, sima->image);
1489
1490         // XXX play_anim(0);
1491         // XXX allqueue(REDRAWNODE, 1);
1492
1493         MEM_freeN(rcd);
1494 }
1495
1496 static int record_composite_exec(bContext *C, wmOperator *op)
1497 {
1498         if(!record_composite_init(C, op))
1499                 return OPERATOR_CANCELLED;
1500         
1501         while(record_composite_apply(C, op))
1502                 ;
1503         
1504         record_composite_exit(C, op);
1505         
1506         return OPERATOR_FINISHED;
1507 }
1508
1509 static int record_composite_invoke(bContext *C, wmOperator *op, wmEvent *event)
1510 {
1511         RecordCompositeData *rcd= op->customdata;
1512         
1513         if(!record_composite_init(C, op))
1514                 return OPERATOR_CANCELLED;
1515
1516         rcd= op->customdata;
1517         rcd->timer= WM_event_add_window_timer(CTX_wm_window(C), TIMER, 0.0f);
1518         WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
1519
1520         if(!record_composite_apply(C, op))
1521                 return OPERATOR_FINISHED;
1522
1523         return OPERATOR_RUNNING_MODAL;
1524 }
1525
1526 static int record_composite_modal(bContext *C, wmOperator *op, wmEvent *event)
1527 {
1528         RecordCompositeData *rcd= op->customdata;
1529
1530         switch(event->type) {
1531                 case TIMER:
1532                         if(rcd->timer == event->customdata) {
1533                                 if(!record_composite_apply(C, op)) {
1534                                         record_composite_exit(C, op);
1535                                         return OPERATOR_FINISHED;
1536                                 }
1537                         }
1538                         break;
1539                 case ESCKEY:
1540                         record_composite_exit(C, op);
1541                         return OPERATOR_FINISHED;
1542         }
1543
1544         return OPERATOR_RUNNING_MODAL;
1545 }
1546
1547 static int record_composite_cancel(bContext *C, wmOperator *op)
1548 {
1549         record_composite_exit(C, op);
1550         return OPERATOR_CANCELLED;
1551 }
1552
1553 void IMAGE_OT_record_composite(wmOperatorType *ot)
1554 {
1555         /* identifiers */
1556         ot->name= "Record Composite";
1557         ot->idname= "IMAGE_OT_record_composite";
1558         
1559         /* api callbacks */
1560         ot->exec= record_composite_exec;
1561         ot->invoke= record_composite_invoke;
1562         ot->modal= record_composite_modal;
1563         ot->cancel= record_composite_cancel;
1564         ot->poll= space_image_poll;
1565 }
1566
1567 /******************** TODO ********************/
1568
1569 /* XXX notifier? */
1570 #if 0
1571 /* goes over all ImageUsers, and sets frame numbers if auto-refresh is set */
1572 void BIF_image_update_frame(void)
1573 {
1574         Tex *tex;
1575         
1576         /* texture users */
1577         for(tex= G.main->tex.first; tex; tex= tex->id.next) {
1578                 if(tex->type==TEX_IMAGE && tex->ima)
1579                         if(ELEM(tex->ima->source, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE))
1580                                 if(tex->iuser.flag & IMA_ANIM_ALWAYS)
1581                                         BKE_image_user_calc_imanr(&tex->iuser, scene->r.cfra, 0);
1582                 
1583         }
1584         /* image window, compo node users */
1585         if(G.curscreen) {
1586                 ScrArea *sa;
1587                 for(sa= G.curscreen->areabase.first; sa; sa= sa->next) {
1588                         if(sa->spacetype==SPACE_VIEW3D) {
1589                                 View3D *v3d= sa->spacedata.first;
1590                                 if(v3d->bgpic)
1591                                         if(v3d->bgpic->iuser.flag & IMA_ANIM_ALWAYS)
1592                                                 BKE_image_user_calc_imanr(&v3d->bgpic->iuser, scene->r.cfra, 0);
1593                         }
1594                         else if(sa->spacetype==SPACE_IMAGE) {
1595                                 SpaceImage *sima= sa->spacedata.first;
1596                                 if(sima->iuser.flag & IMA_ANIM_ALWAYS)
1597                                         BKE_image_user_calc_imanr(&sima->iuser, scene->r.cfra, 0);
1598                         }
1599                         else if(sa->spacetype==SPACE_NODE) {
1600                                 SpaceNode *snode= sa->spacedata.first;
1601                                 if((snode->treetype==NTREE_COMPOSIT) && (snode->nodetree)) {
1602                                         bNode *node;
1603                                         for(node= snode->nodetree->nodes.first; node; node= node->next) {
1604                                                 if(node->id && node->type==CMP_NODE_IMAGE) {
1605                                                         Image *ima= (Image *)node->id;
1606                                                         ImageUser *iuser= node->storage;
1607                                                         if(ELEM(ima->source, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE))
1608                                                                 if(iuser->flag & IMA_ANIM_ALWAYS)
1609                                                                         BKE_image_user_calc_imanr(iuser, scene->r.cfra, 0);
1610                                                 }
1611                                         }
1612                                 }
1613                         }
1614                 }
1615         }
1616 }
1617 #endif
1618