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