Merging r49261 through r49263 from trunk into soc-2011-tomato
[blender-staging.git] / source / blender / editors / space_image / image_ops.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * Contributor(s): Blender Foundation, 2002-2009, Xavier Thomas
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/editors/space_image/image_ops.c
27  *  \ingroup spimage
28  */
29
30
31 #include <stddef.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include <errno.h>
35
36 #include "MEM_guardedalloc.h"
37
38 #include "BLI_math.h"
39 #include "BLI_blenlib.h"
40 #include "BLI_utildefines.h"
41
42 #include "DNA_object_types.h"
43 #include "DNA_node_types.h"
44 #include "DNA_packedFile_types.h"
45 #include "DNA_scene_types.h"
46
47 #include "BKE_colortools.h"
48 #include "BKE_context.h"
49 #include "BKE_image.h"
50 #include "BKE_global.h"
51 #include "BKE_library.h"
52 #include "BKE_main.h"
53 #include "BKE_node.h"
54 #include "BKE_packedFile.h"
55 #include "BKE_report.h"
56 #include "BKE_screen.h"
57
58 #include "IMB_colormanagement.h"
59 #include "IMB_imbuf.h"
60 #include "IMB_imbuf_types.h"
61
62 #include "RE_pipeline.h"
63
64 #include "RNA_access.h"
65 #include "RNA_define.h"
66 #include "RNA_enum_types.h"
67
68 #include "ED_image.h"
69 #include "ED_render.h"
70 #include "ED_screen.h"
71 #include "ED_space_api.h"
72 #include "ED_uvedit.h"
73 #include "ED_util.h"
74
75 #include "UI_interface.h"
76 #include "UI_resources.h"
77 #include "UI_view2d.h"
78
79 #include "WM_api.h"
80 #include "WM_types.h"
81
82 #include "image_intern.h"
83
84 /******************** view navigation utilities *********************/
85
86 static void sima_zoom_set(SpaceImage *sima, ARegion *ar, float zoom, float location[2])
87 {
88         float oldzoom = sima->zoom;
89         int width, height;
90
91         sima->zoom = zoom;
92
93         if (sima->zoom < 0.1f || sima->zoom > 4.0f) {
94                 /* check zoom limits */
95                 ED_space_image_get_size(sima, &width, &height);
96
97                 width *= sima->zoom;
98                 height *= sima->zoom;
99
100                 if ((width < 4) && (height < 4))
101                         sima->zoom = oldzoom;
102                 else if ((ar->winrct.xmax - ar->winrct.xmin) <= sima->zoom)
103                         sima->zoom = oldzoom;
104                 else if ((ar->winrct.ymax - ar->winrct.ymin) <= sima->zoom)
105                         sima->zoom = oldzoom;
106         }
107
108         if ((U.uiflag & USER_ZOOM_TO_MOUSEPOS) && location) {
109                 float aspx, aspy, w, h;
110
111                 ED_space_image_get_size(sima, &width, &height);
112                 ED_space_image_get_aspect(sima, &aspx, &aspy);
113
114                 w = width * aspx;
115                 h = height * aspy;
116
117                 sima->xof += ((location[0] - 0.5f) * w - sima->xof) * (sima->zoom - oldzoom) / sima->zoom;
118                 sima->yof += ((location[1] - 0.5f) * h - sima->yof) * (sima->zoom - oldzoom) / sima->zoom;
119         }
120 }
121
122 static void sima_zoom_set_factor(SpaceImage *sima, ARegion *ar, float zoomfac, float location[2])
123 {
124         sima_zoom_set(sima, ar, sima->zoom * zoomfac, location);
125 }
126
127 #if 0 // currently unused
128 static int image_poll(bContext *C)
129 {
130         return (CTX_data_edit_image(C) != NULL);
131 }
132 #endif
133
134 static int space_image_buffer_exists_poll(bContext *C)
135 {
136         SpaceImage *sima = CTX_wm_space_image(C);
137         if (sima && sima->spacetype == SPACE_IMAGE)
138                 if (ED_space_image_has_buffer(sima))
139                         return 1;
140         return 0;
141 }
142
143 static int space_image_file_exists_poll(bContext *C)
144 {
145         if (space_image_buffer_exists_poll(C)) {
146                 Main *bmain = CTX_data_main(C);
147                 SpaceImage *sima = CTX_wm_space_image(C);
148                 ImBuf *ibuf;
149                 void *lock;
150                 int ret = FALSE;
151                 char name[FILE_MAX];
152
153                 ibuf = ED_space_image_acquire_buffer(sima, &lock);
154                 if (ibuf) {
155                         BLI_strncpy(name, ibuf->name, FILE_MAX);
156                         BLI_path_abs(name, bmain->name);
157
158                         if (BLI_exists(name) == FALSE) {
159                                 CTX_wm_operator_poll_msg_set(C, "image file not found");
160                         }
161                         else if (BLI_file_is_writable(name) == FALSE) {
162                                 CTX_wm_operator_poll_msg_set(C, "image path can't be written to");
163                         }
164                         else {
165                                 ret = TRUE;
166                         }
167                 }
168                 ED_space_image_release_buffer(sima, lock);
169
170                 return ret;
171         }
172         return 0;
173 }
174
175 static int space_image_poll(bContext *C)
176 {
177         SpaceImage *sima = CTX_wm_space_image(C);
178         if (sima && sima->spacetype == SPACE_IMAGE && sima->image)
179                 return 1;
180         return 0;
181 }
182
183 int space_image_main_area_poll(bContext *C)
184 {
185         SpaceImage *sima = CTX_wm_space_image(C);
186         // XXX ARegion *ar= CTX_wm_region(C);
187
188         if (sima)
189                 return 1;  // XXX (ar && ar->type->regionid == RGN_TYPE_WINDOW);
190         
191         return 0;
192 }
193
194 /* For IMAGE_OT_curves_point_set to avoid sampling when in uv smooth mode or editmode */
195 int space_image_main_area_not_uv_brush_poll(bContext *C)
196 {
197         SpaceImage *sima = CTX_wm_space_image(C);
198         Scene *scene = CTX_data_scene(C);
199         ToolSettings *toolsettings = scene->toolsettings;
200
201         if (sima && !toolsettings->uvsculpt && !scene->obedit)
202                 return 1;
203
204         return 0;
205 }
206
207 static int space_image_image_sample_poll(bContext *C)
208 {
209         SpaceImage *sima = CTX_wm_space_image(C);
210         Object *obedit = CTX_data_edit_object(C);
211         ToolSettings *toolsettings = CTX_data_scene(C)->toolsettings;
212
213         if (obedit) {
214                 if (ED_space_image_show_uvedit(sima, obedit) && (toolsettings->use_uv_sculpt))
215                         return 0;
216         }
217         else if (sima->mode != SI_MODE_VIEW) {
218                 return 0;
219         }
220
221         return space_image_main_area_poll(C);
222 }
223 /********************** view pan operator *********************/
224
225 typedef struct ViewPanData {
226         float x, y;
227         float xof, yof;
228         int event_type;
229 } ViewPanData;
230
231 static void image_view_pan_init(bContext *C, wmOperator *op, wmEvent *event)
232 {
233         SpaceImage *sima = CTX_wm_space_image(C);
234         ViewPanData *vpd;
235
236         op->customdata = vpd = MEM_callocN(sizeof(ViewPanData), "ImageViewPanData");
237         WM_cursor_modal(CTX_wm_window(C), BC_NSEW_SCROLLCURSOR);
238
239         vpd->x = event->x;
240         vpd->y = event->y;
241         vpd->xof = sima->xof;
242         vpd->yof = sima->yof;
243         vpd->event_type = event->type;
244
245         WM_event_add_modal_handler(C, op);
246 }
247
248 static void image_view_pan_exit(bContext *C, wmOperator *op, int cancel)
249 {
250         SpaceImage *sima = CTX_wm_space_image(C);
251         ViewPanData *vpd = op->customdata;
252
253         if (cancel) {
254                 sima->xof = vpd->xof;
255                 sima->yof = vpd->yof;
256                 ED_region_tag_redraw(CTX_wm_region(C));
257         }
258
259         WM_cursor_restore(CTX_wm_window(C));
260         MEM_freeN(op->customdata);
261 }
262
263 static int image_view_pan_exec(bContext *C, wmOperator *op)
264 {
265         SpaceImage *sima = CTX_wm_space_image(C);
266         float offset[2];
267
268         RNA_float_get_array(op->ptr, "offset", offset);
269         sima->xof += offset[0];
270         sima->yof += offset[1];
271
272         ED_region_tag_redraw(CTX_wm_region(C));
273
274         /* XXX notifier? */
275 #if 0
276         if (image_preview_active(curarea, NULL, NULL)) {
277                 /* recalculates new preview rect */
278                 scrarea_do_windraw(curarea);
279                 image_preview_event(2);
280         }
281 #endif
282         
283         return OPERATOR_FINISHED;
284 }
285
286 static int image_view_pan_invoke(bContext *C, wmOperator *op, wmEvent *event)
287 {
288         if (event->type == MOUSEPAN) {
289                 SpaceImage *sima = CTX_wm_space_image(C);
290                 float offset[2];
291                 
292                 offset[0] = (event->x - event->prevx) / sima->zoom;
293                 offset[1] = (event->y - event->prevy) / sima->zoom;
294                 RNA_float_set_array(op->ptr, "offset", offset);
295
296                 image_view_pan_exec(C, op);
297                 return OPERATOR_FINISHED;
298         }
299         else {
300                 image_view_pan_init(C, op, event);
301                 return OPERATOR_RUNNING_MODAL;
302         }
303 }
304
305 static int image_view_pan_modal(bContext *C, wmOperator *op, wmEvent *event)
306 {
307         SpaceImage *sima = CTX_wm_space_image(C);
308         ViewPanData *vpd = op->customdata;
309         float offset[2];
310
311         switch (event->type) {
312                 case MOUSEMOVE:
313                         sima->xof = vpd->xof;
314                         sima->yof = vpd->yof;
315                         offset[0] = (vpd->x - event->x) / sima->zoom;
316                         offset[1] = (vpd->y - event->y) / sima->zoom;
317                         RNA_float_set_array(op->ptr, "offset", offset);
318                         image_view_pan_exec(C, op);
319                         break;
320                 default:
321                         if (event->type == vpd->event_type && event->val == KM_RELEASE) {
322                                 image_view_pan_exit(C, op, 0);
323                                 return OPERATOR_FINISHED;
324                         }
325                         break;
326         }
327
328         return OPERATOR_RUNNING_MODAL;
329 }
330
331 static int image_view_pan_cancel(bContext *C, wmOperator *op)
332 {
333         image_view_pan_exit(C, op, 1);
334         return OPERATOR_CANCELLED;
335 }
336
337 void IMAGE_OT_view_pan(wmOperatorType *ot)
338 {
339         /* identifiers */
340         ot->name = "View Pan";
341         ot->idname = "IMAGE_OT_view_pan";
342         ot->description = "Pan the view";
343         
344         /* api callbacks */
345         ot->exec = image_view_pan_exec;
346         ot->invoke = image_view_pan_invoke;
347         ot->modal = image_view_pan_modal;
348         ot->cancel = image_view_pan_cancel;
349         ot->poll = space_image_main_area_poll;
350
351         /* flags */
352         ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_POINTER;
353         
354         /* properties */
355         RNA_def_float_vector(ot->srna, "offset", 2, NULL, -FLT_MAX, FLT_MAX,
356                              "Offset", "Offset in floating point units, 1.0 is the width and height of the image", -FLT_MAX, FLT_MAX);
357 }
358
359 /********************** view zoom operator *********************/
360
361 typedef struct ViewZoomData {
362         float x, y;
363         float zoom;
364         int event_type;
365         float location[2];
366 } ViewZoomData;
367
368 static void image_view_zoom_init(bContext *C, wmOperator *op, wmEvent *event)
369 {
370         SpaceImage *sima = CTX_wm_space_image(C);
371         ARegion *ar = CTX_wm_region(C);
372         ViewZoomData *vpd;
373
374         op->customdata = vpd = MEM_callocN(sizeof(ViewZoomData), "ImageViewZoomData");
375         WM_cursor_modal(CTX_wm_window(C), BC_NSEW_SCROLLCURSOR);
376
377         vpd->x = event->x;
378         vpd->y = event->y;
379         vpd->zoom = sima->zoom;
380         vpd->event_type = event->type;
381
382         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &vpd->location[0], &vpd->location[1]);
383
384         WM_event_add_modal_handler(C, op);
385 }
386
387 static void image_view_zoom_exit(bContext *C, wmOperator *op, int cancel)
388 {
389         SpaceImage *sima = CTX_wm_space_image(C);
390         ViewZoomData *vpd = op->customdata;
391
392         if (cancel) {
393                 sima->zoom = vpd->zoom;
394                 ED_region_tag_redraw(CTX_wm_region(C));
395         }
396
397         WM_cursor_restore(CTX_wm_window(C));
398         MEM_freeN(op->customdata);
399 }
400
401 static int image_view_zoom_exec(bContext *C, wmOperator *op)
402 {
403         SpaceImage *sima = CTX_wm_space_image(C);
404         ARegion *ar = CTX_wm_region(C);
405
406         sima_zoom_set_factor(sima, ar, RNA_float_get(op->ptr, "factor"), NULL);
407
408         ED_region_tag_redraw(CTX_wm_region(C));
409
410         /* XXX notifier? */
411 #if 0
412         if (image_preview_active(curarea, NULL, NULL)) {
413                 /* recalculates new preview rect */
414                 scrarea_do_windraw(curarea);
415                 image_preview_event(2);
416         }
417 #endif
418         
419         return OPERATOR_FINISHED;
420 }
421
422 static int image_view_zoom_invoke(bContext *C, wmOperator *op, wmEvent *event)
423 {
424         if (event->type == MOUSEZOOM) {
425                 SpaceImage *sima = CTX_wm_space_image(C);
426                 ARegion *ar = CTX_wm_region(C);
427                 float factor, location[2];
428
429                 UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &location[0], &location[1]);
430
431                 factor = 1.0f + (event->x - event->prevx + event->y - event->prevy) / 300.0f;
432                 RNA_float_set(op->ptr, "factor", factor);
433                 sima_zoom_set(sima, ar, sima->zoom * factor, location);
434                 ED_region_tag_redraw(CTX_wm_region(C));
435                 
436                 return OPERATOR_FINISHED;
437         }
438         else {
439                 image_view_zoom_init(C, op, event);
440                 return OPERATOR_RUNNING_MODAL;
441         }
442 }
443
444 static int image_view_zoom_modal(bContext *C, wmOperator *op, wmEvent *event)
445 {
446         SpaceImage *sima = CTX_wm_space_image(C);
447         ARegion *ar = CTX_wm_region(C);
448         ViewZoomData *vpd = op->customdata;
449         float factor;
450
451         switch (event->type) {
452                 case MOUSEMOVE:
453                         factor = 1.0f + (vpd->x - event->x + vpd->y - event->y) / 300.0f;
454                         RNA_float_set(op->ptr, "factor", factor);
455                         sima_zoom_set(sima, ar, vpd->zoom * factor, vpd->location);
456                         ED_region_tag_redraw(CTX_wm_region(C));
457                         break;
458                 default:
459                         if (event->type == vpd->event_type && event->val == KM_RELEASE) {
460                                 image_view_zoom_exit(C, op, 0);
461                                 return OPERATOR_FINISHED;
462                         }
463                         break;
464         }
465
466         return OPERATOR_RUNNING_MODAL;
467 }
468
469 static int image_view_zoom_cancel(bContext *C, wmOperator *op)
470 {
471         image_view_zoom_exit(C, op, 1);
472         return OPERATOR_CANCELLED;
473 }
474
475 void IMAGE_OT_view_zoom(wmOperatorType *ot)
476 {
477         /* identifiers */
478         ot->name = "View Zoom";
479         ot->idname = "IMAGE_OT_view_zoom";
480         ot->description = "Zoom in/out the image";
481         
482         /* api callbacks */
483         ot->exec = image_view_zoom_exec;
484         ot->invoke = image_view_zoom_invoke;
485         ot->modal = image_view_zoom_modal;
486         ot->cancel = image_view_zoom_cancel;
487         ot->poll = space_image_main_area_poll;
488
489         /* flags */
490         ot->flag = OPTYPE_BLOCKING;
491         
492         /* properties */
493         RNA_def_float(ot->srna, "factor", 0.0f, 0.0f, FLT_MAX,
494                       "Factor", "Zoom factor, values higher than 1.0 zoom in, lower values zoom out", -FLT_MAX, FLT_MAX);
495 }
496
497 /********************** NDOF operator *********************/
498
499 /* Combined pan/zoom from a 3D mouse device.
500  * Z zooms, XY pans
501  * "view" (not "paper") control -- user moves the viewpoint, not the image being viewed
502  * that explains the negative signs in the code below
503  */
504
505 static int image_view_ndof_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
506 {
507         if (event->type != NDOF_MOTION)
508                 return OPERATOR_CANCELLED;
509         else {
510                 SpaceImage *sima = CTX_wm_space_image(C);
511                 ARegion *ar = CTX_wm_region(C);
512
513                 wmNDOFMotionData *ndof = (wmNDOFMotionData *) event->customdata;
514
515                 float dt = ndof->dt;
516                 /* tune these until it feels right */
517                 const float zoom_sensitivity = 0.5f; // 50% per second (I think)
518                 const float pan_sensitivity = 300.f; // screen pixels per second
519
520                 float pan_x = pan_sensitivity * dt * ndof->tvec[0] / sima->zoom;
521                 float pan_y = pan_sensitivity * dt * ndof->tvec[1] / sima->zoom;
522
523                 /* "mouse zoom" factor = 1 + (dx + dy) / 300
524                  * what about "ndof zoom" factor? should behave like this:
525                  * at rest -> factor = 1
526                  * move forward -> factor > 1
527                  * move backward -> factor < 1
528                  */
529                 float zoom_factor = 1.f + zoom_sensitivity * dt * -ndof->tvec[2];
530
531                 if (U.ndof_flag & NDOF_ZOOM_INVERT)
532                         zoom_factor = -zoom_factor;
533
534                 sima_zoom_set_factor(sima, ar, zoom_factor, NULL);
535                 sima->xof += pan_x;
536                 sima->yof += pan_y;
537
538                 ED_region_tag_redraw(ar);       
539
540                 return OPERATOR_FINISHED;
541         }
542 }
543
544 void IMAGE_OT_view_ndof(wmOperatorType *ot)
545 {
546         /* identifiers */
547         ot->name = "NDOF Pan/Zoom";
548         ot->idname = "IMAGE_OT_view_ndof";
549         ot->description = "Use a 3D mouse device to pan/zoom the view";
550         
551         /* api callbacks */
552         ot->invoke = image_view_ndof_invoke;
553 }
554
555 /********************** view all operator *********************/
556
557 /* Updates the fields of the View2D member of the SpaceImage struct.
558  * Default behavior is to reset the position of the image and set the zoom to 1
559  * If the image will not fit within the window rectangle, the zoom is adjusted */
560
561 static int image_view_all_exec(bContext *C, wmOperator *UNUSED(op))
562 {
563         SpaceImage *sima;
564         ARegion *ar;
565         float aspx, aspy, zoomx, zoomy, w, h;
566         int width, height;
567
568         /* retrieve state */
569         sima = CTX_wm_space_image(C);
570         ar = CTX_wm_region(C);
571
572         ED_space_image_get_size(sima, &width, &height);
573         ED_space_image_get_aspect(sima, &aspx, &aspy);
574
575         w = width * aspx;
576         h = height * aspy;
577         
578         /* check if the image will fit in the image with zoom==1 */
579         width = ar->winrct.xmax - ar->winrct.xmin + 1;
580         height = ar->winrct.ymax - ar->winrct.ymin + 1;
581
582         if ((w >= width || h >= height) && (width > 0 && height > 0)) {
583                 /* find the zoom value that will fit the image in the image space */
584                 zoomx = width / w;
585                 zoomy = height / h;
586                 sima_zoom_set(sima, ar, 1.0f / power_of_2(1 / MIN2(zoomx, zoomy)), NULL);
587         }
588         else
589                 sima_zoom_set(sima, ar, 1.0f, NULL);
590
591         sima->xof = sima->yof = 0.0f;
592
593         ED_region_tag_redraw(CTX_wm_region(C));
594         
595         return OPERATOR_FINISHED;
596 }
597
598 void IMAGE_OT_view_all(wmOperatorType *ot)
599 {
600         /* identifiers */
601         ot->name = "View All";
602         ot->idname = "IMAGE_OT_view_all";
603         ot->description = "View the whole picture";
604         
605         /* api callbacks */
606         ot->exec = image_view_all_exec;
607         ot->poll = space_image_main_area_poll;
608 }
609
610 /********************** view selected operator *********************/
611
612 static int image_view_selected_exec(bContext *C, wmOperator *UNUSED(op))
613 {
614         SpaceImage *sima;
615         ARegion *ar;
616         Scene *scene;
617         Object *obedit;
618         Image *ima;
619         float size, min[2], max[2], d[2], aspx, aspy;
620         int width, height;
621
622         /* retrieve state */
623         sima = CTX_wm_space_image(C);
624         ar = CTX_wm_region(C);
625         scene = CTX_data_scene(C);
626         obedit = CTX_data_edit_object(C);
627
628         ima = ED_space_image(sima);
629         ED_space_image_get_size(sima, &width, &height);
630         ED_image_get_aspect(ima, &aspx, &aspy);
631
632         width = width * aspx;
633         height = height * aspy;
634
635         /* get bounds */
636         if (!ED_uvedit_minmax(scene, ima, obedit, min, max))
637                 return OPERATOR_CANCELLED;
638
639         /* adjust offset and zoom */
640         sima->xof = (int)(((min[0] + max[0]) * 0.5f - 0.5f) * width);
641         sima->yof = (int)(((min[1] + max[1]) * 0.5f - 0.5f) * height);
642
643         d[0] = max[0] - min[0];
644         d[1] = max[1] - min[1];
645         size = 0.5f * MAX2(d[0], d[1]) * MAX2(width, height) / 256.0f;
646         
647         if (size <= 0.01f) size = 0.01f;
648         sima_zoom_set(sima, ar, 0.7f / size, NULL);
649
650         ED_region_tag_redraw(CTX_wm_region(C));
651         
652         return OPERATOR_FINISHED;
653 }
654
655 static int image_view_selected_poll(bContext *C)
656 {
657         return (space_image_main_area_poll(C) && ED_operator_uvedit(C));
658 }
659
660 void IMAGE_OT_view_selected(wmOperatorType *ot)
661 {
662         /* identifiers */
663         ot->name = "View Center";
664         ot->idname = "IMAGE_OT_view_selected";
665         ot->description = "View all selected UVs";
666         
667         /* api callbacks */
668         ot->exec = image_view_selected_exec;
669         ot->poll = image_view_selected_poll;
670 }
671
672 /********************** view zoom in/out operator *********************/
673
674 static int image_view_zoom_in_exec(bContext *C, wmOperator *op)
675 {
676         SpaceImage *sima = CTX_wm_space_image(C);
677         ARegion *ar = CTX_wm_region(C);
678         float location[2];
679
680         RNA_float_get_array(op->ptr, "location", location);
681
682         sima_zoom_set_factor(sima, ar, 1.25f, location);
683
684         ED_region_tag_redraw(CTX_wm_region(C));
685         
686         return OPERATOR_FINISHED;
687 }
688
689 static int image_view_zoom_in_invoke(bContext *C, wmOperator *op, wmEvent *event)
690 {
691         ARegion *ar = CTX_wm_region(C);
692         float location[2];
693
694         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &location[0], &location[1]);
695         RNA_float_set_array(op->ptr, "location", location);
696
697         return image_view_zoom_in_exec(C, op);
698 }
699
700 void IMAGE_OT_view_zoom_in(wmOperatorType *ot)
701 {
702         /* identifiers */
703         ot->name = "View Zoom In";
704         ot->idname = "IMAGE_OT_view_zoom_in";
705         ot->description = "Zoom in the image (centered around 2D cursor)";
706         
707         /* api callbacks */
708         ot->invoke = image_view_zoom_in_invoke;
709         ot->exec = image_view_zoom_in_exec;
710         ot->poll = space_image_main_area_poll;
711
712         /* properties */
713         RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX, "Location", "Cursor location in screen coordinates", -10.0f, 10.0f);
714 }
715
716 static int image_view_zoom_out_exec(bContext *C, wmOperator *op)
717 {
718         SpaceImage *sima = CTX_wm_space_image(C);
719         ARegion *ar = CTX_wm_region(C);
720         float location[2];
721
722         RNA_float_get_array(op->ptr, "location", location);
723
724         sima_zoom_set_factor(sima, ar, 0.8f, location);
725
726         ED_region_tag_redraw(CTX_wm_region(C));
727         
728         return OPERATOR_FINISHED;
729 }
730
731 static int image_view_zoom_out_invoke(bContext *C, wmOperator *op, wmEvent *event)
732 {
733         ARegion *ar = CTX_wm_region(C);
734         float location[2];
735
736         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &location[0], &location[1]);
737         RNA_float_set_array(op->ptr, "location", location);
738
739         return image_view_zoom_out_exec(C, op);
740 }
741
742 void IMAGE_OT_view_zoom_out(wmOperatorType *ot)
743 {
744         /* identifiers */
745         ot->name = "View Zoom Out";
746         ot->idname = "IMAGE_OT_view_zoom_out";
747         ot->description = "Zoom out the image (centered around 2D cursor)";
748         
749         /* api callbacks */
750         ot->invoke = image_view_zoom_out_invoke;
751         ot->exec = image_view_zoom_out_exec;
752         ot->poll = space_image_main_area_poll;
753
754         /* properties */
755         RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX, "Location", "Cursor location in screen coordinates", -10.0f, 10.0f);
756 }
757
758 /********************** view zoom ratio operator *********************/
759
760 static int image_view_zoom_ratio_exec(bContext *C, wmOperator *op)
761 {
762         SpaceImage *sima = CTX_wm_space_image(C);
763         ARegion *ar = CTX_wm_region(C);
764
765         sima_zoom_set(sima, ar, RNA_float_get(op->ptr, "ratio"), NULL);
766         
767         /* ensure pixel exact locations for draw */
768         sima->xof = (int)sima->xof;
769         sima->yof = (int)sima->yof;
770
771         /* XXX notifier? */
772 #if 0
773         if (image_preview_active(curarea, NULL, NULL)) {
774                 /* recalculates new preview rect */
775                 scrarea_do_windraw(curarea);
776                 image_preview_event(2);
777         }
778 #endif
779
780         ED_region_tag_redraw(CTX_wm_region(C));
781         
782         return OPERATOR_FINISHED;
783 }
784
785 void IMAGE_OT_view_zoom_ratio(wmOperatorType *ot)
786 {
787         /* identifiers */
788         ot->name = "View Zoom Ratio";
789         ot->idname = "IMAGE_OT_view_zoom_ratio";
790         ot->description = "Set zoom ration of the view";
791         
792         /* api callbacks */
793         ot->exec = image_view_zoom_ratio_exec;
794         ot->poll = space_image_main_area_poll;
795         
796         /* properties */
797         RNA_def_float(ot->srna, "ratio", 0.0f, 0.0f, FLT_MAX,
798                       "Ratio", "Zoom ratio, 1.0 is 1:1, higher is zoomed in, lower is zoomed out", -FLT_MAX, FLT_MAX);
799 }
800
801 /**************** load/replace/save callbacks ******************/
802 static void image_filesel(bContext *C, wmOperator *op, const char *path)
803 {
804         RNA_string_set(op->ptr, "filepath", path);
805         WM_event_add_fileselect(C, op); 
806 }
807
808 /******************** open image operator ********************/
809
810 static void image_open_init(bContext *C, wmOperator *op)
811 {
812         PropertyPointerRNA *pprop;
813
814         op->customdata = pprop = MEM_callocN(sizeof(PropertyPointerRNA), "OpenPropertyPointerRNA");
815         uiIDContextProperty(C, &pprop->ptr, &pprop->prop);
816 }
817
818 static int image_open_cancel(bContext *UNUSED(C), wmOperator *op)
819 {
820         MEM_freeN(op->customdata);
821         op->customdata = NULL;
822         return OPERATOR_CANCELLED;
823 }
824
825 static int image_open_exec(bContext *C, wmOperator *op)
826 {
827         SpaceImage *sima = CTX_wm_space_image(C); /* XXX other space types can call */
828         Scene *scene = CTX_data_scene(C);
829         Object *obedit = CTX_data_edit_object(C);
830         ImageUser *iuser = NULL;
831         PropertyPointerRNA *pprop;
832         PointerRNA idptr;
833         Image *ima = NULL;
834         char str[FILE_MAX];
835
836         RNA_string_get(op->ptr, "filepath", str);
837         /* default to frame 1 if there's no scene in context */
838
839         errno = 0;
840
841         ima = BKE_image_load_exists(str);
842
843         if (!ima) {
844                 if (op->customdata) MEM_freeN(op->customdata);
845                 BKE_reportf(op->reports, RPT_ERROR, "Can't read: \"%s\", %s", str, errno ? strerror(errno) : "Unsupported image format");
846                 return OPERATOR_CANCELLED;
847         }
848         
849         if (!op->customdata)
850                 image_open_init(C, op);
851
852         /* hook into UI */
853         pprop = op->customdata;
854
855         if (pprop->prop) {
856                 /* when creating new ID blocks, use is already 1, but RNA
857                  * pointer se also increases user, so this compensates it */
858                 ima->id.us--;
859
860                 RNA_id_pointer_create(&ima->id, &idptr);
861                 RNA_property_pointer_set(&pprop->ptr, pprop->prop, idptr);
862                 RNA_property_update(C, &pprop->ptr, pprop->prop);
863         }
864         else if (sima) {
865                 ED_space_image_set(sima, scene, obedit, ima);
866                 iuser = &sima->iuser;
867         }
868         else {
869                 Tex *tex = CTX_data_pointer_get_type(C, "texture", &RNA_Texture).data;
870                 if (tex && tex->type == TEX_IMAGE)
871                         iuser = &tex->iuser;
872                 
873         }
874         
875         /* initialize because of new image */
876         if (iuser) {
877                 iuser->sfra = 1;
878                 iuser->offset = 0;
879                 iuser->fie_ima = 2;
880         }
881
882         /* XXX unpackImage frees image buffers */
883         ED_preview_kill_jobs(C);
884         
885         BKE_image_signal(ima, iuser, IMA_SIGNAL_RELOAD);
886         WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima);
887         
888         MEM_freeN(op->customdata);
889
890         return OPERATOR_FINISHED;
891 }
892
893 static int image_open_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
894 {
895         SpaceImage *sima = CTX_wm_space_image(C); /* XXX other space types can call */
896         char *path = U.textudir;
897         Image *ima = NULL;
898
899         if (sima) {
900                 ima = sima->image;
901         }
902
903         if (ima == NULL) {
904                 Tex *tex = CTX_data_pointer_get_type(C, "texture", &RNA_Texture).data;
905                 if (tex && tex->type == TEX_IMAGE)
906                         ima = tex->ima;
907         }
908
909         if (ima == NULL) {
910                 PointerRNA ptr;
911                 PropertyRNA *prop;
912
913                 /* hook into UI */
914                 uiIDContextProperty(C, &ptr, &prop);
915
916                 if (prop) {
917                         PointerRNA oldptr;
918                         Image *oldima;
919
920                         oldptr = RNA_property_pointer_get(&ptr, prop);
921                         oldima = (Image *)oldptr.id.data;
922                         /* unlikely to fail but better avoid strange crash */
923                         if (oldima && GS(oldima->id.name) != ID_IM) {
924                                 ima = oldima;
925                         }
926                 }
927         }
928
929         if (ima)
930                 path = ima->name;
931
932         if (RNA_struct_property_is_set(op->ptr, "filepath"))
933                 return image_open_exec(C, op);
934         
935         image_open_init(C, op);
936
937         image_filesel(C, op, path);
938
939         return OPERATOR_RUNNING_MODAL;
940 }
941
942 /* called by other space types too */
943 void IMAGE_OT_open(wmOperatorType *ot)
944 {
945         /* identifiers */
946         ot->name = "Open Image";
947         ot->description = "Open image";
948         ot->idname = "IMAGE_OT_open";
949         
950         /* api callbacks */
951         ot->exec = image_open_exec;
952         ot->invoke = image_open_invoke;
953         ot->cancel = image_open_cancel;
954
955         /* flags */
956         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
957
958         /* properties */
959         WM_operator_properties_filesel(ot, FOLDERFILE | IMAGEFILE | MOVIEFILE, FILE_SPECIAL, FILE_OPENFILE, WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH, FILE_DEFAULTDISPLAY);
960 }
961
962 /******************** Match movie length operator ********************/
963 static int image_match_len_exec(bContext *C, wmOperator *UNUSED(op))
964 {
965         Scene *scene = CTX_data_scene(C);
966         Image *ima = CTX_data_pointer_get_type(C, "edit_image", &RNA_Image).data;
967         ImageUser *iuser = CTX_data_pointer_get_type(C, "edit_image_user", &RNA_ImageUser).data;
968
969         if (!ima || !iuser) {
970                 /* Try to get a Texture, or a SpaceImage from context... */
971                 SpaceImage *sima = CTX_wm_space_image(C);
972                 Tex *tex = CTX_data_pointer_get_type(C, "texture", &RNA_Texture).data;
973                 if (tex && tex->type == TEX_IMAGE) {
974                         ima = tex->ima;
975                         iuser = &tex->iuser;
976                 }
977                 else if (sima) {
978                         ima = sima->image;
979                         iuser = &sima->iuser;
980                 }
981                 
982         }
983
984         if (!ima || !iuser || !ima->anim)
985                 return OPERATOR_CANCELLED;
986
987         iuser->frames = IMB_anim_get_duration(ima->anim, IMB_TC_RECORD_RUN);
988         BKE_image_user_frame_calc(iuser, scene->r.cfra, 0);
989
990         return OPERATOR_FINISHED;
991 }
992
993 /* called by other space types too */
994 void IMAGE_OT_match_movie_length(wmOperatorType *ot)
995 {
996         /* identifiers */
997         ot->name = "Match Movie Length";
998         ot->description = "Set image's users length to the one of this video";
999         ot->idname = "IMAGE_OT_match_movie_length";
1000         
1001         /* api callbacks */
1002         ot->exec = image_match_len_exec;
1003
1004         /* flags */
1005         ot->flag = OPTYPE_REGISTER /* | OPTYPE_UNDO */; /* Don't think we need undo for that. */
1006 }
1007
1008 /******************** replace image operator ********************/
1009
1010 static int image_replace_exec(bContext *C, wmOperator *op)
1011 {
1012         SpaceImage *sima = CTX_wm_space_image(C);
1013         char str[FILE_MAX];
1014
1015         if (!sima->image)
1016                 return OPERATOR_CANCELLED;
1017         
1018         RNA_string_get(op->ptr, "filepath", str);
1019
1020         /* we cant do much if the str is longer then FILE_MAX :/ */
1021         BLI_strncpy(sima->image->name, str, sizeof(sima->image->name));
1022
1023         if (BLI_testextensie_array(str, imb_ext_movie))
1024                 sima->image->source = IMA_SRC_MOVIE;
1025         else
1026                 sima->image->source = IMA_SRC_FILE;
1027
1028         /* XXX unpackImage frees image buffers */
1029         ED_preview_kill_jobs(C);
1030         
1031         BKE_image_signal(sima->image, &sima->iuser, IMA_SIGNAL_RELOAD);
1032         WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, sima->image);
1033
1034         return OPERATOR_FINISHED;
1035 }
1036
1037 static int image_replace_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1038 {
1039         SpaceImage *sima = CTX_wm_space_image(C);
1040
1041         if (!sima->image)
1042                 return OPERATOR_CANCELLED;
1043
1044         if (RNA_struct_property_is_set(op->ptr, "filepath"))
1045                 return image_replace_exec(C, op);
1046
1047         if (!RNA_struct_property_is_set(op->ptr, "relative_path"))
1048                 RNA_boolean_set(op->ptr, "relative_path", (strncmp(sima->image->name, "//", 2)) == 0);
1049
1050         image_filesel(C, op, sima->image->name);
1051
1052         return OPERATOR_RUNNING_MODAL;
1053 }
1054
1055 void IMAGE_OT_replace(wmOperatorType *ot)
1056 {
1057         /* identifiers */
1058         ot->name = "Replace Image";
1059         ot->idname = "IMAGE_OT_replace";
1060         ot->description = "Replace current image by another one from disk";
1061         
1062         /* api callbacks */
1063         ot->exec = image_replace_exec;
1064         ot->invoke = image_replace_invoke;
1065         ot->poll = space_image_poll;
1066
1067         /* flags */
1068         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1069
1070         /* properties */
1071         WM_operator_properties_filesel(ot, FOLDERFILE | IMAGEFILE | MOVIEFILE, FILE_SPECIAL, FILE_OPENFILE, WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH, FILE_DEFAULTDISPLAY);
1072 }
1073
1074 /******************** save image as operator ********************/
1075
1076 typedef struct {
1077         /* matching scene->r settings */
1078         //short planes, imtype, subimtype, quality;
1079         ImageFormatData im_format;
1080         char filepath[FILE_MAX]; /* keep absolute */
1081 } SaveImageOptions;
1082
1083 static void save_image_options_defaults(SaveImageOptions *simopts)
1084 {
1085         BKE_imformat_defaults(&simopts->im_format);
1086         simopts->filepath[0] = '\0';
1087 }
1088
1089 static char imtype_best_depth(ImBuf *ibuf, const char imtype)
1090 {
1091         const char depth_ok = BKE_imtype_valid_depths(imtype);
1092
1093         if (ibuf->rect_float) {
1094                 if (depth_ok & R_IMF_CHAN_DEPTH_32) return R_IMF_CHAN_DEPTH_32;
1095                 if (depth_ok & R_IMF_CHAN_DEPTH_24) return R_IMF_CHAN_DEPTH_24;
1096                 if (depth_ok & R_IMF_CHAN_DEPTH_16) return R_IMF_CHAN_DEPTH_16;
1097                 if (depth_ok & R_IMF_CHAN_DEPTH_12) return R_IMF_CHAN_DEPTH_12;
1098                 return R_IMF_CHAN_DEPTH_8;
1099         }
1100         else {
1101                 if (depth_ok & R_IMF_CHAN_DEPTH_8) return R_IMF_CHAN_DEPTH_8;
1102                 if (depth_ok & R_IMF_CHAN_DEPTH_12) return R_IMF_CHAN_DEPTH_12;
1103                 if (depth_ok & R_IMF_CHAN_DEPTH_16) return R_IMF_CHAN_DEPTH_16;
1104                 if (depth_ok & R_IMF_CHAN_DEPTH_24) return R_IMF_CHAN_DEPTH_24;
1105                 if (depth_ok & R_IMF_CHAN_DEPTH_32) return R_IMF_CHAN_DEPTH_32;
1106                 return R_IMF_CHAN_DEPTH_8; /* fallback, should not get here */
1107         }
1108 }
1109
1110 static int save_image_options_init(bContext *C, SaveImageOptions *simopts, SpaceImage *sima, Scene *scene, const short guess_path)
1111 {
1112         void *lock;
1113         ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock);
1114
1115         if (ibuf) {
1116                 wmWindow *win = CTX_wm_window(C);
1117                 const ColorManagedViewSettings *view_settings;
1118                 Image *ima = sima->image;
1119                 short is_depth_set = FALSE;
1120
1121                 simopts->im_format.planes = ibuf->planes;
1122
1123                 if (ELEM(ima->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) {
1124                         /* imtype */
1125                         simopts->im_format = scene->r.im_format;
1126                         is_depth_set = TRUE;
1127                 }
1128                 else if (ima->source == IMA_SRC_GENERATED) {
1129                         simopts->im_format.imtype = R_IMF_IMTYPE_PNG;
1130                 }
1131                 else {
1132                         simopts->im_format.imtype = BKE_ftype_to_imtype(ibuf->ftype);
1133                 }
1134                 //simopts->subimtype= scene->r.subimtype; /* XXX - this is lame, we need to make these available too! */
1135                 simopts->im_format.quality = ibuf->ftype & 0xff;
1136
1137                 BLI_strncpy(simopts->filepath, ibuf->name, sizeof(simopts->filepath));
1138
1139                 /* sanitize all settings */
1140
1141                 /* unlikely but just in case */
1142                 if (ELEM3(simopts->im_format.planes, R_IMF_PLANES_BW, R_IMF_PLANES_RGB, R_IMF_PLANES_RGBA) == 0) {
1143                         simopts->im_format.planes = R_IMF_PLANES_RGBA;
1144                 }
1145
1146                 /* depth, account for float buffer and format support */
1147                 if (is_depth_set == FALSE) {
1148                         simopts->im_format.depth = imtype_best_depth(ibuf, simopts->im_format.imtype);
1149                 }
1150
1151                 /* some formats don't use quality so fallback to scenes quality */
1152                 if (simopts->im_format.quality == 0) {
1153                         simopts->im_format.quality = scene->r.im_format.quality;
1154                 }
1155
1156                 /* check for empty path */
1157                 if (guess_path && simopts->filepath[0] == 0) {
1158                         if ( (G.ima[0] == '/') && (G.ima[1] == '/') && (G.ima[2] == '\0') ) {
1159                                 BLI_strncpy(simopts->filepath, "//untitled", FILE_MAX);
1160                         }
1161                         else {
1162                                 BLI_strncpy(simopts->filepath, G.ima, FILE_MAX);
1163                         }
1164                         BLI_path_abs(simopts->filepath, G.main->name);
1165                 }
1166
1167                 /* color management */
1168                 view_settings = IMB_view_settings_get_effective(win, &sima->view_settings);
1169
1170                 BKE_color_managed_display_settings_copy(&simopts->im_format.display_settings, &win->display_settings);
1171                 BKE_color_managed_view_settings_copy(&simopts->im_format.view_settings, view_settings);
1172         }
1173
1174         ED_space_image_release_buffer(sima, lock);
1175
1176         return (ibuf != NULL);
1177 }
1178
1179 static void save_image_options_from_op(SaveImageOptions *simopts, wmOperator *op)
1180 {
1181         if (op->customdata) {
1182                 simopts->im_format = *(ImageFormatData *)op->customdata;
1183         }
1184
1185         if (RNA_struct_property_is_set(op->ptr, "filepath")) {
1186                 RNA_string_get(op->ptr, "filepath", simopts->filepath);
1187                 BLI_path_abs(simopts->filepath, G.main->name);
1188         }
1189 }
1190
1191 static void save_image_options_to_op(SaveImageOptions *simopts, wmOperator *op)
1192 {
1193         if (op->customdata) {
1194                 *(ImageFormatData *)op->customdata = simopts->im_format;
1195         }
1196
1197         RNA_string_set(op->ptr, "filepath", simopts->filepath);
1198 }
1199
1200 static ImBuf *save_image_colormanaged_imbuf_acquire(ImBuf *ibuf, SaveImageOptions *simopts, void **cache_handle)
1201 {
1202         ImageFormatData *imf = &simopts->im_format;
1203         ImBuf *colormanaged_ibuf;
1204         int do_colormanagement;
1205
1206         *cache_handle = NULL;
1207         do_colormanagement = !BKE_imtype_supports_float(imf->imtype);
1208
1209         if (do_colormanagement) {
1210                 unsigned char *display_buffer =
1211                         IMB_display_buffer_acquire(ibuf, &imf->view_settings, &imf->display_settings, cache_handle);
1212
1213                 if (*cache_handle) {
1214                         colormanaged_ibuf = IMB_allocImBuf(ibuf->x, ibuf->y, ibuf->planes, 0);
1215                         colormanaged_ibuf->rect = (unsigned int *) display_buffer;
1216                 }
1217                 else {
1218                         /* no cache handle means color management didn't run transformation
1219                          * or performed transformation to image's byte buffer which doesn't
1220                          * require allocating new image buffer
1221                          */
1222                         colormanaged_ibuf = ibuf;
1223                 }
1224         }
1225         else {
1226                 colormanaged_ibuf = ibuf;
1227         }
1228
1229         return colormanaged_ibuf;
1230 }
1231
1232 /* assumes name is FILE_MAX */
1233 /* ima->name and ibuf->name should end up the same */
1234 static void save_image_doit(bContext *C, SpaceImage *sima, wmOperator *op, SaveImageOptions *simopts, int do_newpath)
1235 {
1236         Image *ima = ED_space_image(sima);
1237         void *lock;
1238         ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock);
1239
1240         if (ibuf) {
1241                 void *cache_handle;
1242                 ImBuf *colormanaged_ibuf;
1243                 const char *relbase = ID_BLEND_PATH(CTX_data_main(C), &ima->id);
1244                 const short relative = (RNA_struct_find_property(op->ptr, "relative_path") && RNA_boolean_get(op->ptr, "relative_path"));
1245                 const short save_copy = (RNA_struct_find_property(op->ptr, "copy") && RNA_boolean_get(op->ptr, "copy"));
1246                 short ok = FALSE;
1247
1248                 /* old global to ensure a 2nd save goes to same dir */
1249                 BLI_strncpy(G.ima, simopts->filepath, sizeof(G.ima));
1250
1251                 WM_cursor_wait(1);
1252
1253                 if (ima->type == IMA_TYPE_R_RESULT) {
1254                         /* enforce user setting for RGB or RGBA, but skip BW */
1255                         if (simopts->im_format.planes == R_IMF_PLANES_RGBA) {
1256                                 ibuf->planes = R_IMF_PLANES_RGBA;
1257                         }
1258                         else if (simopts->im_format.planes == R_IMF_PLANES_RGB) {
1259                                 ibuf->planes = R_IMF_PLANES_RGB;
1260                         }
1261                 }
1262                 else {
1263                         /* TODO, better solution, if a 24bit image is painted onto it may contain alpha */
1264                         if (ibuf->userflags & IB_BITMAPDIRTY) { /* it has been painted onto */
1265                                 /* checks each pixel, not ideal */
1266                                 ibuf->planes = BKE_imbuf_alpha_test(ibuf) ? 32 : 24;
1267                         }
1268                 }
1269
1270                 colormanaged_ibuf = save_image_colormanaged_imbuf_acquire(ibuf, simopts, &cache_handle);
1271
1272                 if (simopts->im_format.imtype == R_IMF_IMTYPE_MULTILAYER) {
1273                         Scene *scene = CTX_data_scene(C);
1274                         RenderResult *rr = BKE_image_acquire_renderresult(scene, ima);
1275                         if (rr) {
1276                                 RE_WriteRenderResult(op->reports, rr, simopts->filepath, simopts->im_format.quality);
1277                                 ok = TRUE;
1278                         }
1279                         else {
1280                                 BKE_report(op->reports, RPT_ERROR, "Did not write, no Multilayer Image");
1281                         }
1282                         BKE_image_release_renderresult(scene, ima);
1283                 }
1284                 else {
1285                         if (BKE_imbuf_write_as(colormanaged_ibuf, simopts->filepath, &simopts->im_format, save_copy)) {
1286                                 ok = TRUE;
1287                         }
1288                 }
1289
1290                 if (ok) {
1291                         if (!save_copy) {
1292                                 if (do_newpath) {
1293                                         BLI_strncpy(ibuf->name, simopts->filepath, sizeof(ibuf->name));
1294                                         BLI_strncpy(ima->name, simopts->filepath, sizeof(ima->name));
1295                                 }
1296
1297                                 ibuf->userflags &= ~IB_BITMAPDIRTY;
1298
1299                                 /* change type? */
1300                                 if (ima->type == IMA_TYPE_R_RESULT) {
1301                                         ima->type = IMA_TYPE_IMAGE;
1302
1303                                         /* workaround to ensure the render result buffer is no longer used
1304                                          * by this image, otherwise can crash when a new render result is
1305                                          * created. */
1306                                         if (ibuf->rect && !(ibuf->mall & IB_rect))
1307                                                 imb_freerectImBuf(ibuf);
1308                                         if (ibuf->rect_float && !(ibuf->mall & IB_rectfloat))
1309                                                 imb_freerectfloatImBuf(ibuf);
1310                                         if (ibuf->zbuf && !(ibuf->mall & IB_zbuf))
1311                                                 IMB_freezbufImBuf(ibuf);
1312                                         if (ibuf->zbuf_float && !(ibuf->mall & IB_zbuffloat))
1313                                                 IMB_freezbuffloatImBuf(ibuf);
1314                                 }
1315                                 if (ELEM(ima->source, IMA_SRC_GENERATED, IMA_SRC_VIEWER)) {
1316                                         ima->source = IMA_SRC_FILE;
1317                                         ima->type = IMA_TYPE_IMAGE;
1318                                 }
1319
1320                                 /* only image path, never ibuf */
1321                                 if (relative) {
1322                                         BLI_path_rel(ima->name, relbase); /* only after saving */
1323                                 }
1324                         }
1325                 }
1326                 else {
1327                         BKE_reportf(op->reports, RPT_ERROR, "Couldn't write image: %s", simopts->filepath);
1328                 }
1329
1330
1331                 WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, sima->image);
1332
1333                 WM_cursor_wait(0);
1334
1335                 if (cache_handle) {
1336                         colormanaged_ibuf->rect = NULL;
1337                         IMB_freeImBuf(colormanaged_ibuf);
1338
1339                         IMB_display_buffer_release(cache_handle);
1340                 }
1341         }
1342
1343         ED_space_image_release_buffer(sima, lock);
1344 }
1345
1346 static void image_save_as_free(wmOperator *op)
1347 {
1348         if (op->customdata) {
1349                 MEM_freeN(op->customdata);
1350                 op->customdata = NULL;
1351         }
1352 }
1353
1354 static int image_save_as_exec(bContext *C, wmOperator *op)
1355 {
1356         SpaceImage *sima = CTX_wm_space_image(C);
1357         SaveImageOptions simopts;
1358
1359         save_image_options_defaults(&simopts);
1360
1361         /* just in case to initialize values,
1362          * these should be set on invoke or by the caller. */
1363         save_image_options_init(C, &simopts, sima, CTX_data_scene(C), 0);
1364
1365         save_image_options_from_op(&simopts, op);
1366
1367         save_image_doit(C, sima, op, &simopts, TRUE);
1368
1369         image_save_as_free(op);
1370         return OPERATOR_FINISHED;
1371 }
1372
1373
1374 static int image_save_as_check(bContext *UNUSED(C), wmOperator *op)
1375 {
1376         ImageFormatData *imf = op->customdata;
1377         return WM_operator_filesel_ensure_ext_imtype(op, imf->imtype);
1378 }
1379
1380 static int image_save_as_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1381 {
1382         SpaceImage *sima = CTX_wm_space_image(C);
1383         Image *ima = ED_space_image(sima);
1384         Scene *scene = CTX_data_scene(C);
1385         SaveImageOptions simopts;
1386
1387         if (RNA_struct_property_is_set(op->ptr, "filepath"))
1388                 return image_save_as_exec(C, op);
1389
1390         save_image_options_defaults(&simopts);
1391
1392         if (save_image_options_init(C, &simopts, sima, scene, TRUE) == 0)
1393                 return OPERATOR_CANCELLED;
1394         save_image_options_to_op(&simopts, op);
1395
1396         /* enable save_copy by default for render results */
1397         if (ELEM(ima->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE) && !RNA_struct_property_is_set(op->ptr, "copy")) {
1398                 RNA_boolean_set(op->ptr, "copy", TRUE);
1399         }
1400
1401         op->customdata = MEM_mallocN(sizeof(simopts.im_format), __func__);
1402         memcpy(op->customdata, &simopts.im_format, sizeof(simopts.im_format));
1403
1404         image_filesel(C, op, simopts.filepath);
1405
1406         return OPERATOR_RUNNING_MODAL;
1407 }
1408
1409 static int image_save_as_cancel(bContext *UNUSED(C), wmOperator *op)
1410 {
1411         image_save_as_free(op);
1412
1413         return OPERATOR_CANCELLED;
1414 }
1415
1416 static int image_save_as_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop)
1417 {
1418         const char *prop_id = RNA_property_identifier(prop);
1419
1420         return !(strcmp(prop_id, "filepath") == 0 ||
1421                  strcmp(prop_id, "directory") == 0 ||
1422                  strcmp(prop_id, "filename") == 0 ||
1423                  /* when saving a copy, relative path has no effect */
1424                  ((strcmp(prop_id, "relative_path") == 0) && RNA_boolean_get(ptr, "copy"))
1425                  );
1426 }
1427
1428 static void image_save_as_draw(bContext *UNUSED(C), wmOperator *op)
1429 {
1430         uiLayout *layout = op->layout;
1431         ImageFormatData *imf = op->customdata;
1432         PointerRNA ptr;
1433
1434         /* image template */
1435         RNA_pointer_create(NULL, &RNA_ImageFormatSettings, imf, &ptr);
1436         uiTemplateImageSettings(layout, &ptr);
1437
1438         /* main draw call */
1439         RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
1440         uiDefAutoButsRNA(layout, &ptr, image_save_as_draw_check_prop, '\0');
1441 }
1442
1443 void IMAGE_OT_save_as(wmOperatorType *ot)
1444 {
1445 //      PropertyRNA *prop;
1446
1447         /* identifiers */
1448         ot->name = "Save As Image";
1449         ot->idname = "IMAGE_OT_save_as";
1450         ot->description = "Save the image with another name and/or settings";
1451         
1452         /* api callbacks */
1453         ot->exec = image_save_as_exec;
1454         ot->check = image_save_as_check;
1455         ot->invoke = image_save_as_invoke;
1456         ot->cancel = image_save_as_cancel;
1457         ot->ui = image_save_as_draw;
1458         ot->poll = space_image_buffer_exists_poll;
1459
1460         /* flags */
1461         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1462
1463         /* properties */
1464         RNA_def_boolean(ot->srna, "copy", 0, "Copy", "Create a new image file without modifying the current image in blender");
1465
1466         WM_operator_properties_filesel(ot, FOLDERFILE | IMAGEFILE | MOVIEFILE, FILE_SPECIAL, FILE_SAVE, WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH, FILE_DEFAULTDISPLAY);
1467 }
1468
1469 /******************** save image operator ********************/
1470
1471 static int image_save_exec(bContext *C, wmOperator *op)
1472 {
1473         SpaceImage *sima = CTX_wm_space_image(C);
1474         Scene *scene = CTX_data_scene(C);
1475         SaveImageOptions simopts;
1476
1477         if (save_image_options_init(C, &simopts, sima, scene, FALSE) == 0)
1478                 return OPERATOR_CANCELLED;
1479         save_image_options_from_op(&simopts, op);
1480
1481         if (BLI_exists(simopts.filepath) && BLI_file_is_writable(simopts.filepath)) {
1482                 save_image_doit(C, sima, op, &simopts, FALSE);
1483         }
1484         else {
1485                 BKE_reportf(op->reports, RPT_ERROR, "Can not save image, path '%s' is not writable", simopts.filepath);
1486                 return OPERATOR_CANCELLED;
1487         }
1488
1489         return OPERATOR_FINISHED;
1490 }
1491
1492 void IMAGE_OT_save(wmOperatorType *ot)
1493 {
1494         /* identifiers */
1495         ot->name = "Save Image";
1496         ot->idname = "IMAGE_OT_save";
1497         ot->description = "Save the image with current name and settings";
1498         
1499         /* api callbacks */
1500         ot->exec = image_save_exec;
1501         ot->poll = space_image_file_exists_poll;
1502
1503         /* flags */
1504         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1505 }
1506
1507 /******************* save sequence operator ********************/
1508
1509 static int image_save_sequence_exec(bContext *C, wmOperator *op)
1510 {
1511         Main *bmain = CTX_data_main(C);
1512         SpaceImage *sima = CTX_wm_space_image(C);
1513         ImBuf *ibuf;
1514         int tot = 0;
1515         char di[FILE_MAX], fi[FILE_MAX];
1516         
1517         if (sima->image == NULL)
1518                 return OPERATOR_CANCELLED;
1519
1520         if (sima->image->source != IMA_SRC_SEQUENCE) {
1521                 BKE_report(op->reports, RPT_ERROR, "Can only save sequence on image sequences");
1522                 return OPERATOR_CANCELLED;
1523         }
1524
1525         if (sima->image->type == IMA_TYPE_MULTILAYER) {
1526                 BKE_report(op->reports, RPT_ERROR, "Can't save multilayer sequences");
1527                 return OPERATOR_CANCELLED;
1528         }
1529         
1530         /* get total */
1531         for (ibuf = sima->image->ibufs.first; ibuf; ibuf = ibuf->next)
1532                 if (ibuf->userflags & IB_BITMAPDIRTY)
1533                         tot++;
1534         
1535         if (tot == 0) {
1536                 BKE_report(op->reports, RPT_WARNING, "No images have been changed");
1537                 return OPERATOR_CANCELLED;
1538         }
1539
1540         /* get a filename for menu */
1541         for (ibuf = sima->image->ibufs.first; ibuf; ibuf = ibuf->next)
1542                 if (ibuf->userflags & IB_BITMAPDIRTY)
1543                         break;
1544         
1545         BLI_strncpy(di, ibuf->name, FILE_MAX);
1546         BLI_splitdirstring(di, fi);
1547         
1548         BKE_reportf(op->reports, RPT_INFO, "%d Image(s) will be saved in %s", tot, di);
1549
1550         for (ibuf = sima->image->ibufs.first; ibuf; ibuf = ibuf->next) {
1551                 if (ibuf->userflags & IB_BITMAPDIRTY) {
1552                         char name[FILE_MAX];
1553                         BLI_strncpy(name, ibuf->name, sizeof(name));
1554                         
1555                         BLI_path_abs(name, bmain->name);
1556
1557                         if (0 == IMB_saveiff(ibuf, name, IB_rect | IB_zbuf | IB_zbuffloat)) {
1558                                 BKE_reportf(op->reports, RPT_ERROR, "Could not write image %s", name);
1559                                 break;
1560                         }
1561
1562                         BKE_reportf(op->reports, RPT_INFO, "Saved: %s\n", ibuf->name);
1563                         ibuf->userflags &= ~IB_BITMAPDIRTY;
1564                 }
1565         }
1566
1567         return OPERATOR_FINISHED;
1568 }
1569
1570 void IMAGE_OT_save_sequence(wmOperatorType *ot)
1571 {
1572         /* identifiers */
1573         ot->name = "Save Sequence";
1574         ot->idname = "IMAGE_OT_save_sequence";
1575         ot->description = "Save a sequence of images";
1576         
1577         /* api callbacks */
1578         ot->exec = image_save_sequence_exec;
1579         ot->poll = space_image_buffer_exists_poll;
1580
1581         /* flags */
1582         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1583 }
1584
1585 /******************** reload image operator ********************/
1586
1587 static int image_reload_exec(bContext *C, wmOperator *UNUSED(op))
1588 {
1589         Image *ima = CTX_data_edit_image(C);
1590         SpaceImage *sima = CTX_wm_space_image(C);
1591
1592         if (!ima)
1593                 return OPERATOR_CANCELLED;
1594
1595         /* XXX unpackImage frees image buffers */
1596         ED_preview_kill_jobs(C);
1597         
1598         // XXX other users?
1599         BKE_image_signal(ima, (sima) ? &sima->iuser : NULL, IMA_SIGNAL_RELOAD);
1600
1601         WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima);
1602         
1603         return OPERATOR_FINISHED;
1604 }
1605
1606 void IMAGE_OT_reload(wmOperatorType *ot)
1607 {
1608         /* identifiers */
1609         ot->name = "Reload Image";
1610         ot->idname = "IMAGE_OT_reload";
1611         ot->description = "Reload current image from disk";
1612         
1613         /* api callbacks */
1614         ot->exec = image_reload_exec;
1615
1616         /* flags */
1617         ot->flag = OPTYPE_REGISTER; /* no undo, image buffer is not handled by undo */
1618 }
1619
1620 /********************** new image operator *********************/
1621
1622 static int image_new_exec(bContext *C, wmOperator *op)
1623 {
1624         SpaceImage *sima;
1625         Scene *scene;
1626         Object *obedit;
1627         Image *ima;
1628         PointerRNA ptr, idptr;
1629         PropertyRNA *prop;
1630         char name[MAX_ID_NAME - 2];
1631         float color[4];
1632         int width, height, floatbuf, uvtestgrid, alpha;
1633
1634         /* retrieve state */
1635         sima = CTX_wm_space_image(C);
1636         scene = CTX_data_scene(C);
1637         obedit = CTX_data_edit_object(C);
1638
1639         RNA_string_get(op->ptr, "name", name);
1640         width = RNA_int_get(op->ptr, "width");
1641         height = RNA_int_get(op->ptr, "height");
1642         floatbuf = RNA_boolean_get(op->ptr, "float");
1643         uvtestgrid = RNA_boolean_get(op->ptr, "uv_test_grid");
1644         RNA_float_get_array(op->ptr, "color", color);
1645         alpha = RNA_boolean_get(op->ptr, "alpha");
1646         
1647         if (!floatbuf && scene->r.color_mgt_flag & R_COLOR_MANAGEMENT)
1648                 linearrgb_to_srgb_v3_v3(color, color);
1649
1650         if (!alpha)
1651                 color[3] = 1.0f;
1652
1653         ima = BKE_image_add_generated(width, height, name, alpha ? 32 : 24, floatbuf, uvtestgrid, color);
1654
1655         if (!ima)
1656                 return OPERATOR_CANCELLED;
1657
1658         /* hook into UI */
1659         uiIDContextProperty(C, &ptr, &prop);
1660
1661         if (prop) {
1662                 /* when creating new ID blocks, use is already 1, but RNA
1663                  * pointer se also increases user, so this compensates it */
1664                 ima->id.us--;
1665
1666                 RNA_id_pointer_create(&ima->id, &idptr);
1667                 RNA_property_pointer_set(&ptr, prop, idptr);
1668                 RNA_property_update(C, &ptr, prop);
1669         }
1670         else if (sima)
1671                 ED_space_image_set(sima, scene, obedit, ima);
1672
1673         // XXX other users?
1674         BKE_image_signal(ima, (sima) ? &sima->iuser : NULL, IMA_SIGNAL_USER_NEW_IMAGE);
1675         
1676         return OPERATOR_FINISHED;
1677 }
1678
1679 /* XXX, Ton is not a fan of OK buttons but using this function to avoid undo/redo bug while in mesh-editmode, - campbell */
1680 static int image_new_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1681 {
1682         return WM_operator_props_dialog_popup(C, op, 300, 100);
1683
1684 }
1685
1686 void IMAGE_OT_new(wmOperatorType *ot)
1687 {
1688         PropertyRNA *prop;
1689         static float default_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
1690         
1691         /* identifiers */
1692         ot->name = "New Image";
1693         ot->description = "Create a new image";
1694         ot->idname = "IMAGE_OT_new";
1695         
1696         /* api callbacks */
1697         ot->exec = image_new_exec;
1698         ot->invoke = image_new_invoke;
1699         
1700         /* flags */
1701         ot->flag = OPTYPE_UNDO;
1702
1703         /* properties */
1704         RNA_def_string(ot->srna, "name", "untitled", MAX_ID_NAME - 2, "Name", "Image datablock name");
1705         RNA_def_int(ot->srna, "width", 1024, 1, INT_MAX, "Width", "Image width", 1, 16384);
1706         RNA_def_int(ot->srna, "height", 1024, 1, INT_MAX, "Height", "Image height", 1, 16384);
1707         prop = RNA_def_float_color(ot->srna, "color", 4, NULL, 0.0f, FLT_MAX, "Color", "Default fill color", 0.0f, 1.0f);
1708         RNA_def_property_float_array_default(prop, default_color);
1709         RNA_def_boolean(ot->srna, "alpha", 1, "Alpha", "Create an image with an alpha channel");
1710         RNA_def_boolean(ot->srna, "uv_test_grid", 0, "UV Test Grid", "Fill the image with a grid for UV map testing");
1711         RNA_def_boolean(ot->srna, "float", 0, "32 bit Float", "Create image with 32 bit floating point bit depth");
1712 }
1713
1714 /********************* invert operators *********************/
1715
1716 static int image_invert_poll(bContext *C)
1717 {
1718         Image *ima = CTX_data_edit_image(C);
1719         ImBuf *ibuf = BKE_image_get_ibuf(ima, NULL);
1720         
1721         if (ibuf != NULL)
1722                 return 1;
1723         return 0;
1724 }
1725
1726 static int image_invert_exec(bContext *C, wmOperator *op)
1727 {
1728         Image *ima = CTX_data_edit_image(C);
1729         ImBuf *ibuf = BKE_image_get_ibuf(ima, NULL);
1730
1731         /* flags indicate if this channel should be inverted */
1732         const short r = RNA_boolean_get(op->ptr, "invert_r");
1733         const short g = RNA_boolean_get(op->ptr, "invert_g");
1734         const short b = RNA_boolean_get(op->ptr, "invert_b");
1735         const short a = RNA_boolean_get(op->ptr, "invert_a");
1736
1737         int i;
1738
1739         if (ibuf == NULL)  /* TODO: this should actually never happen, but does for render-results -> cleanup */
1740                 return OPERATOR_CANCELLED;
1741
1742         /* TODO: make this into an IMB_invert_channels(ibuf,r,g,b,a) method!? */
1743         if (ibuf->rect_float) {
1744                 
1745                 float *fp = (float *) ibuf->rect_float;
1746                 for (i = ibuf->x * ibuf->y; i > 0; i--, fp += 4) {
1747                         if (r) fp[0] = 1.0f - fp[0];
1748                         if (g) fp[1] = 1.0f - fp[1];
1749                         if (b) fp[2] = 1.0f - fp[2];
1750                         if (a) fp[3] = 1.0f - fp[3];
1751                 }
1752
1753                 if (ibuf->rect) {
1754                         IMB_rect_from_float(ibuf);
1755                 }
1756         }
1757         else if (ibuf->rect) {
1758                 
1759                 char *cp = (char *) ibuf->rect;
1760                 for (i = ibuf->x * ibuf->y; i > 0; i--, cp += 4) {
1761                         if (r) cp[0] = 255 - cp[0];
1762                         if (g) cp[1] = 255 - cp[1];
1763                         if (b) cp[2] = 255 - cp[2];
1764                         if (a) cp[3] = 255 - cp[3];
1765                 }
1766         }
1767         else {
1768                 return OPERATOR_CANCELLED;
1769         }
1770
1771         ibuf->userflags |= IB_BITMAPDIRTY;
1772         if (ibuf->mipmap[0])
1773                 ibuf->userflags |= IB_MIPMAP_INVALID;
1774
1775         WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima);
1776         return OPERATOR_FINISHED;
1777 }
1778
1779 void IMAGE_OT_invert(wmOperatorType *ot)
1780 {
1781         /* identifiers */
1782         ot->name = "Invert Channels";
1783         ot->idname = "IMAGE_OT_invert";
1784         ot->description = "Invert image's channels";
1785         
1786         /* api callbacks */
1787         ot->exec = image_invert_exec;
1788         ot->poll = image_invert_poll;
1789         
1790         /* properties */
1791         RNA_def_boolean(ot->srna, "invert_r", 0, "Red", "Invert Red Channel");
1792         RNA_def_boolean(ot->srna, "invert_g", 0, "Green", "Invert Green Channel");
1793         RNA_def_boolean(ot->srna, "invert_b", 0, "Blue", "Invert Blue Channel");
1794         RNA_def_boolean(ot->srna, "invert_a", 0, "Alpha", "Invert Alpha Channel");
1795         
1796         /* flags */
1797         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1798 }
1799
1800 /********************* pack operator *********************/
1801
1802 static int image_pack_test(bContext *C, wmOperator *op)
1803 {
1804         Image *ima = CTX_data_edit_image(C);
1805         int as_png = RNA_boolean_get(op->ptr, "as_png");
1806
1807         if (!ima)
1808                 return 0;
1809         if (!as_png && ima->packedfile)
1810                 return 0;
1811
1812         if (ima->source == IMA_SRC_SEQUENCE || ima->source == IMA_SRC_MOVIE) {
1813                 BKE_report(op->reports, RPT_ERROR, "Packing movies or image sequences not supported");
1814                 return 0;
1815         }
1816
1817         return 1;
1818 }
1819
1820 static int image_pack_exec(bContext *C, wmOperator *op)
1821 {
1822         struct Main *bmain = CTX_data_main(C);
1823         Image *ima = CTX_data_edit_image(C);
1824         ImBuf *ibuf = BKE_image_get_ibuf(ima, NULL);
1825         int as_png = RNA_boolean_get(op->ptr, "as_png");
1826
1827         if (!image_pack_test(C, op))
1828                 return OPERATOR_CANCELLED;
1829         
1830         if (!as_png && (ibuf && (ibuf->userflags & IB_BITMAPDIRTY))) {
1831                 BKE_report(op->reports, RPT_ERROR, "Can't pack edited image from disk, only as internal PNG");
1832                 return OPERATOR_CANCELLED;
1833         }
1834
1835         if (as_png)
1836                 BKE_image_memorypack(ima);
1837         else
1838                 ima->packedfile = newPackedFile(op->reports, ima->name, ID_BLEND_PATH(bmain, &ima->id));
1839
1840         WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima);
1841         
1842         return OPERATOR_FINISHED;
1843 }
1844
1845 static int image_pack_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1846 {
1847         Image *ima = CTX_data_edit_image(C);
1848         ImBuf *ibuf = BKE_image_get_ibuf(ima, NULL);
1849         uiPopupMenu *pup;
1850         uiLayout *layout;
1851         int as_png = RNA_boolean_get(op->ptr, "as_png");
1852
1853         if (!image_pack_test(C, op))
1854                 return OPERATOR_CANCELLED;
1855         
1856         if (!as_png && (ibuf && (ibuf->userflags & IB_BITMAPDIRTY))) {
1857                 pup = uiPupMenuBegin(C, "OK", ICON_QUESTION);
1858                 layout = uiPupMenuLayout(pup);
1859                 uiItemBooleanO(layout, "Can't pack edited image from disk. Pack as internal PNG?", ICON_NONE, op->idname, "as_png", 1);
1860                 uiPupMenuEnd(C, pup);
1861
1862                 return OPERATOR_CANCELLED;
1863         }
1864
1865         return image_pack_exec(C, op);
1866 }
1867
1868 void IMAGE_OT_pack(wmOperatorType *ot)
1869 {
1870         /* identifiers */
1871         ot->name = "Pack Image";
1872         ot->description = "Pack an image as embedded data into the .blend file"; 
1873         ot->idname = "IMAGE_OT_pack";
1874         
1875         /* api callbacks */
1876         ot->exec = image_pack_exec;
1877         ot->invoke = image_pack_invoke;
1878
1879         /* flags */
1880         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1881
1882         /* properties */
1883         RNA_def_boolean(ot->srna, "as_png", 0, "Pack As PNG", "Pack image as lossless PNG");
1884 }
1885
1886 /********************* unpack operator *********************/
1887
1888 static int image_unpack_exec(bContext *C, wmOperator *op)
1889 {
1890         Image *ima = CTX_data_edit_image(C);
1891         int method = RNA_enum_get(op->ptr, "method");
1892
1893         /* find the suppplied image by name */
1894         if (RNA_struct_property_is_set(op->ptr, "id")) {
1895                 char imaname[MAX_ID_NAME - 2];
1896                 RNA_string_get(op->ptr, "id", imaname);
1897                 ima = BLI_findstring(&CTX_data_main(C)->image, imaname, offsetof(ID, name) + 2);
1898                 if (!ima) ima = CTX_data_edit_image(C);
1899         }
1900         
1901         if (!ima || !ima->packedfile)
1902                 return OPERATOR_CANCELLED;
1903
1904         if (ima->source == IMA_SRC_SEQUENCE || ima->source == IMA_SRC_MOVIE) {
1905                 BKE_report(op->reports, RPT_ERROR, "Unpacking movies or image sequences not supported");
1906                 return OPERATOR_CANCELLED;
1907         }
1908
1909         if (G.fileflags & G_AUTOPACK)
1910                 BKE_report(op->reports, RPT_WARNING, "AutoPack is enabled, so image will be packed again on file save");
1911         
1912         /* XXX unpackImage frees image buffers */
1913         ED_preview_kill_jobs(C);
1914         
1915         unpackImage(op->reports, ima, method);
1916         
1917         WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima);
1918
1919         return OPERATOR_FINISHED;
1920 }
1921
1922 static int image_unpack_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1923 {
1924         Image *ima = CTX_data_edit_image(C);
1925
1926         if (RNA_struct_property_is_set(op->ptr, "id"))
1927                 return image_unpack_exec(C, op);
1928                 
1929         if (!ima || !ima->packedfile)
1930                 return OPERATOR_CANCELLED;
1931
1932         if (ima->source == IMA_SRC_SEQUENCE || ima->source == IMA_SRC_MOVIE) {
1933                 BKE_report(op->reports, RPT_ERROR, "Unpacking movies or image sequences not supported");
1934                 return OPERATOR_CANCELLED;
1935         }
1936
1937         if (G.fileflags & G_AUTOPACK)
1938                 BKE_report(op->reports, RPT_WARNING, "AutoPack is enabled, so image will be packed again on file save");
1939
1940         unpack_menu(C, "IMAGE_OT_unpack", ima->id.name + 2, ima->name, "textures", ima->packedfile);
1941
1942         return OPERATOR_FINISHED;
1943 }
1944
1945 void IMAGE_OT_unpack(wmOperatorType *ot)
1946 {
1947         /* identifiers */
1948         ot->name = "Unpack Image";
1949         ot->description = "Save an image packed in the .blend file to disk"; 
1950         ot->idname = "IMAGE_OT_unpack";
1951         
1952         /* api callbacks */
1953         ot->exec = image_unpack_exec;
1954         ot->invoke = image_unpack_invoke;
1955
1956         /* flags */
1957         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1958         
1959         /* properties */
1960         RNA_def_enum(ot->srna, "method", unpack_method_items, PF_USE_LOCAL, "Method", "How to unpack");
1961         RNA_def_string(ot->srna, "id", "", MAX_ID_NAME - 2, "Image Name", "Image datablock name to unpack"); /* XXX, weark!, will fail with library, name collisions */
1962 }
1963
1964 /******************** sample image operator ********************/
1965
1966 typedef struct ImageSampleInfo {
1967         ARegionType *art;
1968         void *draw_handle;
1969         int x, y;
1970         int channels;
1971
1972         unsigned char col[4];
1973         float colf[4];
1974         int z;
1975         float zf;
1976
1977         unsigned char *colp;
1978         float *colfp;
1979         int *zp;
1980         float *zfp;
1981
1982         int draw;
1983 } ImageSampleInfo;
1984
1985 static void image_sample_draw(const bContext *UNUSED(C), ARegion *ar, void *arg_info)
1986 {
1987         ImageSampleInfo *info = arg_info;
1988         if (info->draw) {
1989                 /* no color management needed for images (color_manage=0) */
1990                 ED_image_draw_info(ar, 0, info->channels, info->x, info->y, info->colp, info->colfp, info->zp, info->zfp);
1991         }
1992 }
1993
1994 static void image_sample_apply(bContext *C, wmOperator *op, wmEvent *event)
1995 {
1996         SpaceImage *sima = CTX_wm_space_image(C);
1997         ARegion *ar = CTX_wm_region(C);
1998         void *lock;
1999         ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock);
2000         ImageSampleInfo *info = op->customdata;
2001         float fx, fy;
2002         
2003         if (ibuf == NULL) {
2004                 ED_space_image_release_buffer(sima, lock);
2005                 info->draw = 0;
2006                 return;
2007         }
2008
2009         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fx, &fy);
2010
2011         if (fx >= 0.0f && fy >= 0.0f && fx < 1.0f && fy < 1.0f) {
2012                 float *fp;
2013                 unsigned char *cp;
2014                 int x = (int)(fx * ibuf->x), y = (int)(fy * ibuf->y);
2015
2016                 CLAMP(x, 0, ibuf->x - 1);
2017                 CLAMP(y, 0, ibuf->y - 1);
2018
2019                 info->x = x;
2020                 info->y = y;
2021                 info->draw = 1;
2022                 info->channels = ibuf->channels;
2023
2024                 info->colp = NULL;
2025                 info->colfp = NULL;
2026                 info->zp = NULL;
2027                 info->zfp = NULL;
2028                 
2029                 if (ibuf->rect) {
2030                         cp = (unsigned char *)(ibuf->rect + y * ibuf->x + x);
2031
2032                         info->col[0] = cp[0];
2033                         info->col[1] = cp[1];
2034                         info->col[2] = cp[2];
2035                         info->col[3] = cp[3];
2036                         info->colp = info->col;
2037
2038                         info->colf[0] = (float)cp[0] / 255.0f;
2039                         info->colf[1] = (float)cp[1] / 255.0f;
2040                         info->colf[2] = (float)cp[2] / 255.0f;
2041                         info->colf[3] = (float)cp[3] / 255.0f;
2042                         info->colfp = info->colf;
2043                 }
2044                 if (ibuf->rect_float) {
2045                         fp = (ibuf->rect_float + (ibuf->channels) * (y * ibuf->x + x));
2046
2047                         info->colf[0] = fp[0];
2048                         info->colf[1] = fp[1];
2049                         info->colf[2] = fp[2];
2050                         info->colf[3] = fp[3];
2051                         info->colfp = info->colf;
2052                 }
2053
2054                 if (ibuf->zbuf) {
2055                         info->z = ibuf->zbuf[y * ibuf->x + x];
2056                         info->zp = &info->z;
2057                 }
2058                 if (ibuf->zbuf_float) {
2059                         info->zf = ibuf->zbuf_float[y * ibuf->x + x];
2060                         info->zfp = &info->zf;
2061                 }
2062                 
2063                 if (sima->cumap && ibuf->channels == 4) {
2064                         /* we reuse this callback for set curves point operators */
2065                         if (RNA_struct_find_property(op->ptr, "point")) {
2066                                 int point = RNA_enum_get(op->ptr, "point");
2067
2068                                 if (point == 1) {
2069                                         curvemapping_set_black_white(sima->cumap, NULL, info->colfp);
2070                                         if (ibuf->rect_float)
2071                                                 curvemapping_do_ibuf(sima->cumap, ibuf);
2072                                 }
2073                                 else if (point == 0) {
2074                                         curvemapping_set_black_white(sima->cumap, info->colfp, NULL);
2075                                         if (ibuf->rect_float)
2076                                                 curvemapping_do_ibuf(sima->cumap, ibuf);
2077                                 }
2078                         }
2079                 }
2080                                 
2081                 // XXX node curve integration ..
2082 #if 0
2083                 {
2084                         ScrArea *sa, *cur = curarea;
2085                         
2086                         node_curvemap_sample(fp);   /* sends global to node editor */
2087                         for (sa = G.curscreen->areabase.first; sa; sa = sa->next) {
2088                                 if (sa->spacetype == SPACE_NODE) {
2089                                         areawinset(sa->win);
2090                                         scrarea_do_windraw(sa);
2091                                 }
2092                         }
2093                         node_curvemap_sample(NULL);     /* clears global in node editor */
2094                         curarea = cur;
2095                 }
2096 #endif
2097         }
2098         else {
2099                 info->draw = 0;
2100         }
2101
2102         ED_space_image_release_buffer(sima, lock);
2103         ED_area_tag_redraw(CTX_wm_area(C));
2104 }
2105
2106 static void image_sample_exit(bContext *C, wmOperator *op)
2107 {
2108         ImageSampleInfo *info = op->customdata;
2109
2110         ED_region_draw_cb_exit(info->art, info->draw_handle);
2111         ED_area_tag_redraw(CTX_wm_area(C));
2112         MEM_freeN(info);
2113 }
2114
2115 static int image_sample_invoke(bContext *C, wmOperator *op, wmEvent *event)
2116 {
2117         SpaceImage *sima = CTX_wm_space_image(C);
2118         ARegion *ar = CTX_wm_region(C);
2119         ImageSampleInfo *info;
2120
2121         if (!ED_space_image_has_buffer(sima))
2122                 return OPERATOR_CANCELLED;
2123         
2124         info = MEM_callocN(sizeof(ImageSampleInfo), "ImageSampleInfo");
2125         info->art = ar->type;
2126         info->draw_handle = ED_region_draw_cb_activate(ar->type, image_sample_draw, info, REGION_DRAW_POST_PIXEL);
2127         op->customdata = info;
2128
2129         image_sample_apply(C, op, event);
2130
2131         WM_event_add_modal_handler(C, op);
2132
2133         return OPERATOR_RUNNING_MODAL;
2134 }
2135
2136 static int image_sample_modal(bContext *C, wmOperator *op, wmEvent *event)
2137 {
2138         switch (event->type) {
2139                 case LEFTMOUSE:
2140                 case RIGHTMOUSE: // XXX hardcoded
2141                         image_sample_exit(C, op);
2142                         return OPERATOR_CANCELLED;
2143                 case MOUSEMOVE:
2144                         image_sample_apply(C, op, event);
2145                         break;
2146         }
2147
2148         return OPERATOR_RUNNING_MODAL;
2149 }
2150
2151 static int image_sample_cancel(bContext *C, wmOperator *op)
2152 {
2153         image_sample_exit(C, op);
2154         return OPERATOR_CANCELLED;
2155 }
2156
2157 void IMAGE_OT_sample(wmOperatorType *ot)
2158 {
2159         /* identifiers */
2160         ot->name = "Sample Color";
2161         ot->idname = "IMAGE_OT_sample";
2162         ot->description = "Use mouse to sample a color in current image";
2163         
2164         /* api callbacks */
2165         ot->invoke = image_sample_invoke;
2166         ot->modal = image_sample_modal;
2167         ot->cancel = image_sample_cancel;
2168         ot->poll = space_image_image_sample_poll;
2169
2170         /* flags */
2171         ot->flag = OPTYPE_BLOCKING;
2172 }
2173
2174 /******************** sample line operator ********************/
2175 static int image_sample_line_exec(bContext *C, wmOperator *op)
2176 {
2177         SpaceImage *sima = CTX_wm_space_image(C);
2178         ARegion *ar = CTX_wm_region(C);
2179         Scene *scene = CTX_data_scene(C);
2180
2181         int x_start = RNA_int_get(op->ptr, "xstart");
2182         int y_start = RNA_int_get(op->ptr, "ystart");
2183         int x_end = RNA_int_get(op->ptr, "xend");
2184         int y_end = RNA_int_get(op->ptr, "yend");
2185         
2186         void *lock;
2187         ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock);
2188         Histogram *hist = &sima->sample_line_hist;
2189         
2190         float x1f, y1f, x2f, y2f;
2191         
2192         if (ibuf == NULL) {
2193                 ED_space_image_release_buffer(sima, lock);
2194                 return OPERATOR_CANCELLED;
2195         }
2196         /* hmmmm */
2197         if (ibuf->channels < 3) {
2198                 ED_space_image_release_buffer(sima, lock);
2199                 return OPERATOR_CANCELLED;
2200         }
2201         
2202         UI_view2d_region_to_view(&ar->v2d, x_start, y_start, &x1f, &y1f);
2203         UI_view2d_region_to_view(&ar->v2d, x_end, y_end, &x2f, &y2f);
2204
2205         hist->co[0][0] = x1f;
2206         hist->co[0][1] = y1f;
2207         hist->co[1][0] = x2f;
2208         hist->co[1][1] = y2f;
2209
2210         BKE_histogram_update_sample_line(hist, ibuf, (scene->r.color_mgt_flag & R_COLOR_MANAGEMENT) != 0);
2211         
2212         /* reset y zoom */
2213         hist->ymax = 1.0f;
2214
2215         ED_space_image_release_buffer(sima, lock);
2216         
2217         ED_area_tag_redraw(CTX_wm_area(C));
2218         
2219         return OPERATOR_FINISHED;
2220 }
2221
2222 static int image_sample_line_invoke(bContext *C, wmOperator *op, wmEvent *event)
2223 {
2224         SpaceImage *sima = CTX_wm_space_image(C);
2225
2226         Histogram *hist = &sima->sample_line_hist;
2227         hist->flag &= ~HISTO_FLAG_SAMPLELINE;
2228
2229         if (!ED_space_image_has_buffer(sima))
2230                 return OPERATOR_CANCELLED;
2231         
2232         return WM_gesture_straightline_invoke(C, op, event);
2233 }
2234
2235 void IMAGE_OT_sample_line(wmOperatorType *ot)
2236 {
2237         /* identifiers */
2238         ot->name = "Sample Line";
2239         ot->idname = "IMAGE_OT_sample_line";
2240         ot->description = "Sample a line and show it in Scope panels";
2241         
2242         /* api callbacks */
2243         ot->invoke = image_sample_line_invoke;
2244         ot->modal = WM_gesture_straightline_modal;
2245         ot->exec = image_sample_line_exec;
2246         ot->poll = space_image_main_area_poll;
2247         ot->cancel = WM_gesture_straightline_cancel;
2248         
2249         /* flags */
2250         ot->flag = 0; /* no undo/register since this operates on the space */
2251         
2252         WM_operator_properties_gesture_straightline(ot, CURSOR_EDIT);
2253 }
2254
2255 /******************** set curve point operator ********************/
2256
2257 void IMAGE_OT_curves_point_set(wmOperatorType *ot)
2258 {
2259         static EnumPropertyItem point_items[] = {
2260                 {0, "BLACK_POINT", 0, "Black Point", ""},
2261                 {1, "WHITE_POINT", 0, "White Point", ""},
2262                 {0, NULL, 0, NULL, NULL}
2263         };
2264
2265         /* identifiers */
2266         ot->name = "Set Curves Point";
2267         ot->idname = "IMAGE_OT_curves_point_set";
2268         ot->description = "Set black point or white point for curves";
2269
2270         /* flags */
2271         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2272         
2273         /* api callbacks */
2274         ot->invoke = image_sample_invoke;
2275         ot->modal = image_sample_modal;
2276         ot->cancel = image_sample_cancel;
2277         ot->poll = space_image_main_area_not_uv_brush_poll;
2278
2279         /* properties */
2280         RNA_def_enum(ot->srna, "point", point_items, 0, "Point", "Set black point or white point for curves");
2281 }
2282
2283 /******************** record composite operator *********************/
2284
2285 typedef struct RecordCompositeData {
2286         wmTimer *timer;
2287         int old_cfra;
2288         int sfra, efra;
2289 } RecordCompositeData;
2290
2291 static int image_record_composite_apply(bContext *C, wmOperator *op)
2292 {
2293         SpaceImage *sima = CTX_wm_space_image(C);
2294         RecordCompositeData *rcd = op->customdata;
2295         Scene *scene = CTX_data_scene(C);
2296         ImBuf *ibuf;
2297         
2298         WM_timecursor(CTX_wm_window(C), scene->r.cfra);
2299
2300         // XXX scene->nodetree->test_break= blender_test_break;
2301         // XXX scene->nodetree->test_break= NULL;
2302         
2303         BKE_image_all_free_anim_ibufs(scene->r.cfra);
2304         ntreeCompositTagAnimated(scene->nodetree);
2305         ntreeCompositExecTree(scene->nodetree, &scene->r, 0, scene->r.cfra != rcd->old_cfra);  /* 1 is no previews */
2306
2307         ED_area_tag_redraw(CTX_wm_area(C));
2308         
2309         ibuf = BKE_image_get_ibuf(sima->image, &sima->iuser);
2310         /* save memory in flipbooks */
2311         if (ibuf)
2312                 imb_freerectfloatImBuf(ibuf);
2313         
2314         scene->r.cfra++;
2315
2316         return (scene->r.cfra <= rcd->efra);
2317 }
2318
2319 static int image_record_composite_init(bContext *C, wmOperator *op)
2320 {
2321         SpaceImage *sima = CTX_wm_space_image(C);
2322         Scene *scene = CTX_data_scene(C);
2323         RecordCompositeData *rcd;
2324
2325         if (sima->iuser.frames < 2)
2326                 return 0;
2327         if (scene->nodetree == NULL)
2328                 return 0;
2329         
2330         op->customdata = rcd = MEM_callocN(sizeof(RecordCompositeData), "ImageRecordCompositeData");
2331
2332         rcd->old_cfra = scene->r.cfra;
2333         rcd->sfra = sima->iuser.sfra;
2334         rcd->efra = sima->iuser.sfra + sima->iuser.frames - 1;
2335         scene->r.cfra = rcd->sfra;
2336
2337         return 1;
2338 }
2339
2340 static void image_record_composite_exit(bContext *C, wmOperator *op)
2341 {
2342         Scene *scene = CTX_data_scene(C);
2343         SpaceImage *sima = CTX_wm_space_image(C);
2344         RecordCompositeData *rcd = op->customdata;
2345
2346         scene->r.cfra = rcd->old_cfra;
2347
2348         WM_cursor_restore(CTX_wm_window(C));
2349
2350         if (rcd->timer)
2351                 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), rcd->timer);
2352
2353         WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, sima->image);
2354
2355         // XXX play_anim(0);
2356         // XXX allqueue(REDRAWNODE, 1);
2357
2358         MEM_freeN(rcd);
2359 }
2360
2361 static int image_record_composite_exec(bContext *C, wmOperator *op)
2362 {
2363         if (!image_record_composite_init(C, op))
2364                 return OPERATOR_CANCELLED;
2365         
2366         while (image_record_composite_apply(C, op))
2367                 ;
2368         
2369         image_record_composite_exit(C, op);
2370         
2371         return OPERATOR_FINISHED;
2372 }
2373
2374 static int image_record_composite_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
2375 {
2376         RecordCompositeData *rcd;
2377         
2378         if (!image_record_composite_init(C, op))
2379                 return OPERATOR_CANCELLED;
2380
2381         rcd = op->customdata;
2382         rcd->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.0f);
2383         WM_event_add_modal_handler(C, op);
2384
2385         if (!image_record_composite_apply(C, op))
2386                 return OPERATOR_FINISHED;
2387
2388         return OPERATOR_RUNNING_MODAL;
2389 }
2390
2391 static int image_record_composite_modal(bContext *C, wmOperator *op, wmEvent *event)
2392 {
2393         RecordCompositeData *rcd = op->customdata;
2394
2395         switch (event->type) {
2396                 case TIMER:
2397                         if (rcd->timer == event->customdata) {
2398                                 if (!image_record_composite_apply(C, op)) {
2399                                         image_record_composite_exit(C, op);
2400                                         return OPERATOR_FINISHED;
2401                                 }
2402                         }
2403                         break;
2404                 case ESCKEY:
2405                         image_record_composite_exit(C, op);
2406                         return OPERATOR_FINISHED;
2407         }
2408
2409         return OPERATOR_RUNNING_MODAL;
2410 }
2411
2412 static int image_record_composite_cancel(bContext *C, wmOperator *op)
2413 {
2414         image_record_composite_exit(C, op);
2415         return OPERATOR_CANCELLED;
2416 }
2417
2418 void IMAGE_OT_record_composite(wmOperatorType *ot)
2419 {
2420         /* identifiers */
2421         ot->name = "Record Composite";
2422         ot->idname = "IMAGE_OT_record_composite";
2423         
2424         /* api callbacks */
2425         ot->exec = image_record_composite_exec;
2426         ot->invoke = image_record_composite_invoke;
2427         ot->modal = image_record_composite_modal;
2428         ot->cancel = image_record_composite_cancel;
2429         ot->poll = space_image_buffer_exists_poll;
2430 }
2431
2432 /********************* cycle render slot operator *********************/
2433
2434 static int image_cycle_render_slot_poll(bContext *C)
2435 {
2436         Image *ima = CTX_data_edit_image(C);
2437
2438         return (ima && ima->type == IMA_TYPE_R_RESULT);
2439 }
2440
2441 static int image_cycle_render_slot_exec(bContext *C, wmOperator *op)
2442 {
2443         Image *ima = CTX_data_edit_image(C);
2444         int a, slot, cur = ima->render_slot;
2445         const short use_reverse = RNA_boolean_get(op->ptr, "reverse");
2446
2447         for (a = 1; a < IMA_MAX_RENDER_SLOT; a++) {
2448                 slot = (cur + (use_reverse ? -a : a)) % IMA_MAX_RENDER_SLOT;
2449                 if (slot < 0) slot += IMA_MAX_RENDER_SLOT;
2450
2451                 if (ima->renders[slot] || slot == ima->last_render_slot) {
2452                         ima->render_slot = slot;
2453                         break;
2454                 }
2455         }
2456
2457         if (a == IMA_MAX_RENDER_SLOT)
2458                 ima->render_slot = ((cur == 1) ? 0 : 1);
2459         
2460         WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL);
2461
2462         /* no undo push for browsing existing */
2463         if (ima->renders[ima->render_slot] || ima->render_slot == ima->last_render_slot)
2464                 return OPERATOR_CANCELLED;
2465         
2466         return OPERATOR_FINISHED;
2467 }
2468
2469 void IMAGE_OT_cycle_render_slot(wmOperatorType *ot)
2470 {
2471         /* identifiers */
2472         ot->name = "Cycle Render Slot";
2473         ot->idname = "IMAGE_OT_cycle_render_slot";
2474         ot->description = "Cycle through all non-void render slots";
2475         
2476         /* api callbacks */
2477         ot->exec = image_cycle_render_slot_exec;
2478         ot->poll = image_cycle_render_slot_poll;
2479
2480         /* flags */
2481         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2482
2483         RNA_def_boolean(ot->srna, "reverse", 0, "Cycle in Reverse", "");
2484 }