1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  */
19 
20 /** \file
21  * \ingroup spimage
22  */
23 
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <stddef.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #ifndef WIN32
30 #  include <unistd.h>
31 #else
32 #  include <io.h>
33 #endif
34 
35 #include "MEM_guardedalloc.h"
36 
37 #include "BLI_blenlib.h"
38 #include "BLI_fileops.h"
39 #include "BLI_fileops_types.h"
40 #include "BLI_ghash.h"
41 #include "BLI_linklist.h"
42 #include "BLI_math.h"
43 #include "BLI_string.h"
44 #include "BLI_utildefines.h"
45 
46 #include "BLT_translation.h"
47 
48 #include "DNA_camera_types.h"
49 #include "DNA_node_types.h"
50 #include "DNA_object_types.h"
51 #include "DNA_packedFile_types.h"
52 #include "DNA_scene_types.h"
53 #include "DNA_screen_types.h"
54 
55 #include "BKE_colortools.h"
56 #include "BKE_context.h"
57 #include "BKE_global.h"
58 #include "BKE_icons.h"
59 #include "BKE_image.h"
60 #include "BKE_image_save.h"
61 #include "BKE_layer.h"
62 #include "BKE_lib_id.h"
63 #include "BKE_main.h"
64 #include "BKE_packedFile.h"
65 #include "BKE_paint.h"
66 #include "BKE_report.h"
67 #include "BKE_scene.h"
68 #include "BKE_screen.h"
69 
70 #include "DEG_depsgraph.h"
71 
72 #include "GPU_immediate.h"
73 #include "GPU_state.h"
74 
75 #include "IMB_colormanagement.h"
76 #include "IMB_imbuf.h"
77 #include "IMB_imbuf_types.h"
78 #include "IMB_moviecache.h"
79 #include "intern/openexr/openexr_multi.h"
80 
81 #include "RE_pipeline.h"
82 
83 #include "RNA_access.h"
84 #include "RNA_define.h"
85 #include "RNA_enum_types.h"
86 
87 #include "ED_image.h"
88 #include "ED_mask.h"
89 #include "ED_paint.h"
90 #include "ED_render.h"
91 #include "ED_screen.h"
92 #include "ED_space_api.h"
93 #include "ED_util.h"
94 #include "ED_util_imbuf.h"
95 #include "ED_uvedit.h"
96 
97 #include "UI_interface.h"
98 #include "UI_resources.h"
99 #include "UI_view2d.h"
100 
101 #include "WM_api.h"
102 #include "WM_types.h"
103 
104 #include "PIL_time.h"
105 
106 #include "RE_engine.h"
107 
108 #include "image_intern.h"
109 
110 /* -------------------------------------------------------------------- */
111 /** \name View Navigation Utilities
112  * \{ */
113 
sima_zoom_set(SpaceImage * sima,ARegion * region,float zoom,const float location[2],const bool zoom_to_pos)114 static void sima_zoom_set(
115     SpaceImage *sima, ARegion *region, float zoom, const float location[2], const bool zoom_to_pos)
116 {
117   float oldzoom = sima->zoom;
118   int width, height;
119 
120   sima->zoom = zoom;
121 
122   if (sima->zoom < 0.1f || sima->zoom > 4.0f) {
123     /* check zoom limits */
124     ED_space_image_get_size(sima, &width, &height);
125 
126     width *= sima->zoom;
127     height *= sima->zoom;
128 
129     if ((width < 4) && (height < 4) && sima->zoom < oldzoom) {
130       sima->zoom = oldzoom;
131     }
132     else if (BLI_rcti_size_x(&region->winrct) <= sima->zoom) {
133       sima->zoom = oldzoom;
134     }
135     else if (BLI_rcti_size_y(&region->winrct) <= sima->zoom) {
136       sima->zoom = oldzoom;
137     }
138   }
139 
140   if (zoom_to_pos && location) {
141     float aspx, aspy, w, h;
142 
143     ED_space_image_get_size(sima, &width, &height);
144     ED_space_image_get_aspect(sima, &aspx, &aspy);
145 
146     w = width * aspx;
147     h = height * aspy;
148 
149     sima->xof += ((location[0] - 0.5f) * w - sima->xof) * (sima->zoom - oldzoom) / sima->zoom;
150     sima->yof += ((location[1] - 0.5f) * h - sima->yof) * (sima->zoom - oldzoom) / sima->zoom;
151   }
152 }
153 
sima_zoom_set_factor(SpaceImage * sima,ARegion * region,float zoomfac,const float location[2],const bool zoom_to_pos)154 static void sima_zoom_set_factor(SpaceImage *sima,
155                                  ARegion *region,
156                                  float zoomfac,
157                                  const float location[2],
158                                  const bool zoom_to_pos)
159 {
160   sima_zoom_set(sima, region, sima->zoom * zoomfac, location, zoom_to_pos);
161 }
162 
163 /**
164  * Fits the view to the bounds exactly, caller should add margin if needed.
165  */
sima_zoom_set_from_bounds(SpaceImage * sima,ARegion * region,const rctf * bounds)166 static void sima_zoom_set_from_bounds(SpaceImage *sima, ARegion *region, const rctf *bounds)
167 {
168   int image_size[2];
169   float aspx, aspy;
170 
171   ED_space_image_get_size(sima, &image_size[0], &image_size[1]);
172   ED_space_image_get_aspect(sima, &aspx, &aspy);
173 
174   image_size[0] = image_size[0] * aspx;
175   image_size[1] = image_size[1] * aspy;
176 
177   /* adjust offset and zoom */
178   sima->xof = roundf((BLI_rctf_cent_x(bounds) - 0.5f) * image_size[0]);
179   sima->yof = roundf((BLI_rctf_cent_y(bounds) - 0.5f) * image_size[1]);
180 
181   float size_xy[2], size;
182   size_xy[0] = BLI_rcti_size_x(&region->winrct) / (BLI_rctf_size_x(bounds) * image_size[0]);
183   size_xy[1] = BLI_rcti_size_y(&region->winrct) / (BLI_rctf_size_y(bounds) * image_size[1]);
184 
185   size = min_ff(size_xy[0], size_xy[1]);
186   CLAMP_MAX(size, 100.0f);
187 
188   sima_zoom_set(sima, region, size, NULL, false);
189 }
190 
image_from_context(const bContext * C)191 static Image *image_from_context(const bContext *C)
192 {
193   /* Edit image is set by templates used throughout the interface, so image
194    * operations work outside the image editor. */
195   Image *ima = CTX_data_pointer_get_type(C, "edit_image", &RNA_Image).data;
196 
197   if (ima) {
198     return ima;
199   }
200 
201   /* Image editor. */
202   SpaceImage *sima = CTX_wm_space_image(C);
203   return (sima) ? sima->image : NULL;
204 }
205 
image_user_from_context(const bContext * C)206 static ImageUser *image_user_from_context(const bContext *C)
207 {
208   /* Edit image user is set by templates used throughout the interface, so
209    * image operations work outside the image editor. */
210   ImageUser *iuser = CTX_data_pointer_get_type(C, "edit_image_user", &RNA_ImageUser).data;
211 
212   if (iuser) {
213     return iuser;
214   }
215 
216   /* Image editor. */
217   SpaceImage *sima = CTX_wm_space_image(C);
218   return (sima) ? &sima->iuser : NULL;
219 }
220 
image_from_context_has_data_poll(bContext * C)221 static bool image_from_context_has_data_poll(bContext *C)
222 {
223   Image *ima = image_from_context(C);
224   ImageUser *iuser = image_user_from_context(C);
225 
226   if (ima == NULL) {
227     return false;
228   }
229 
230   void *lock;
231   ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, &lock);
232   const bool has_buffer = (ibuf && (ibuf->rect || ibuf->rect_float));
233   BKE_image_release_ibuf(ima, ibuf, lock);
234   return has_buffer;
235 }
236 
237 /**
238  * Use this when the image buffer is accessed without the image user.
239  */
image_from_context_has_data_poll_no_image_user(bContext * C)240 static bool image_from_context_has_data_poll_no_image_user(bContext *C)
241 {
242   Image *ima = image_from_context(C);
243 
244   return BKE_image_has_ibuf(ima, NULL);
245 }
246 
image_not_packed_poll(bContext * C)247 static bool image_not_packed_poll(bContext *C)
248 {
249   /* Do not run 'replace' on packed images, it does not give user expected results at all. */
250   Image *ima = image_from_context(C);
251   return (ima && BLI_listbase_is_empty(&ima->packedfiles));
252 }
253 
space_image_main_region_poll(bContext * C)254 bool space_image_main_region_poll(bContext *C)
255 {
256   SpaceImage *sima = CTX_wm_space_image(C);
257   /* XXX ARegion *region = CTX_wm_region(C); */
258 
259   if (sima) {
260     return true; /* XXX (region && region->type->regionid == RGN_TYPE_WINDOW); */
261   }
262   return false;
263 }
264 
265 /* For IMAGE_OT_curves_point_set to avoid sampling when in uv smooth mode or editmode */
space_image_main_area_not_uv_brush_poll(bContext * C)266 static bool space_image_main_area_not_uv_brush_poll(bContext *C)
267 {
268   SpaceImage *sima = CTX_wm_space_image(C);
269   Scene *scene = CTX_data_scene(C);
270   ToolSettings *toolsettings = scene->toolsettings;
271 
272   if (sima && !toolsettings->uvsculpt && (CTX_data_edit_object(C) == NULL)) {
273     return true;
274   }
275 
276   return false;
277 }
278 
279 /** \} */
280 
281 /* -------------------------------------------------------------------- */
282 /** \name View Pan Operator
283  * \{ */
284 
285 typedef struct ViewPanData {
286   float x, y;
287   float xof, yof;
288   int launch_event;
289   bool own_cursor;
290 } ViewPanData;
291 
image_view_pan_init(bContext * C,wmOperator * op,const wmEvent * event)292 static void image_view_pan_init(bContext *C, wmOperator *op, const wmEvent *event)
293 {
294   wmWindow *win = CTX_wm_window(C);
295   SpaceImage *sima = CTX_wm_space_image(C);
296   ViewPanData *vpd;
297 
298   op->customdata = vpd = MEM_callocN(sizeof(ViewPanData), "ImageViewPanData");
299 
300   /* Grab will be set when running from gizmo. */
301   vpd->own_cursor = (win->grabcursor == 0);
302   if (vpd->own_cursor) {
303     WM_cursor_modal_set(win, WM_CURSOR_NSEW_SCROLL);
304   }
305 
306   vpd->x = event->x;
307   vpd->y = event->y;
308   vpd->xof = sima->xof;
309   vpd->yof = sima->yof;
310   vpd->launch_event = WM_userdef_event_type_from_keymap_type(event->type);
311 
312   WM_event_add_modal_handler(C, op);
313 }
314 
image_view_pan_exit(bContext * C,wmOperator * op,bool cancel)315 static void image_view_pan_exit(bContext *C, wmOperator *op, bool cancel)
316 {
317   SpaceImage *sima = CTX_wm_space_image(C);
318   ViewPanData *vpd = op->customdata;
319 
320   if (cancel) {
321     sima->xof = vpd->xof;
322     sima->yof = vpd->yof;
323     ED_region_tag_redraw(CTX_wm_region(C));
324   }
325 
326   if (vpd->own_cursor) {
327     WM_cursor_modal_restore(CTX_wm_window(C));
328   }
329   MEM_freeN(op->customdata);
330 }
331 
image_view_pan_exec(bContext * C,wmOperator * op)332 static int image_view_pan_exec(bContext *C, wmOperator *op)
333 {
334   SpaceImage *sima = CTX_wm_space_image(C);
335   float offset[2];
336 
337   RNA_float_get_array(op->ptr, "offset", offset);
338   sima->xof += offset[0];
339   sima->yof += offset[1];
340 
341   ED_region_tag_redraw(CTX_wm_region(C));
342 
343   return OPERATOR_FINISHED;
344 }
345 
image_view_pan_invoke(bContext * C,wmOperator * op,const wmEvent * event)346 static int image_view_pan_invoke(bContext *C, wmOperator *op, const wmEvent *event)
347 {
348   if (event->type == MOUSEPAN) {
349     SpaceImage *sima = CTX_wm_space_image(C);
350     float offset[2];
351 
352     offset[0] = (event->prevx - event->x) / sima->zoom;
353     offset[1] = (event->prevy - event->y) / sima->zoom;
354     RNA_float_set_array(op->ptr, "offset", offset);
355 
356     image_view_pan_exec(C, op);
357     return OPERATOR_FINISHED;
358   }
359 
360   image_view_pan_init(C, op, event);
361   return OPERATOR_RUNNING_MODAL;
362 }
363 
image_view_pan_modal(bContext * C,wmOperator * op,const wmEvent * event)364 static int image_view_pan_modal(bContext *C, wmOperator *op, const wmEvent *event)
365 {
366   SpaceImage *sima = CTX_wm_space_image(C);
367   ViewPanData *vpd = op->customdata;
368   float offset[2];
369 
370   switch (event->type) {
371     case MOUSEMOVE:
372       sima->xof = vpd->xof;
373       sima->yof = vpd->yof;
374       offset[0] = (vpd->x - event->x) / sima->zoom;
375       offset[1] = (vpd->y - event->y) / sima->zoom;
376       RNA_float_set_array(op->ptr, "offset", offset);
377       image_view_pan_exec(C, op);
378       break;
379     default:
380       if (event->type == vpd->launch_event && event->val == KM_RELEASE) {
381         image_view_pan_exit(C, op, false);
382         return OPERATOR_FINISHED;
383       }
384       break;
385   }
386 
387   return OPERATOR_RUNNING_MODAL;
388 }
389 
image_view_pan_cancel(bContext * C,wmOperator * op)390 static void image_view_pan_cancel(bContext *C, wmOperator *op)
391 {
392   image_view_pan_exit(C, op, true);
393 }
394 
IMAGE_OT_view_pan(wmOperatorType * ot)395 void IMAGE_OT_view_pan(wmOperatorType *ot)
396 {
397   /* identifiers */
398   ot->name = "Pan View";
399   ot->idname = "IMAGE_OT_view_pan";
400   ot->description = "Pan the view";
401 
402   /* api callbacks */
403   ot->exec = image_view_pan_exec;
404   ot->invoke = image_view_pan_invoke;
405   ot->modal = image_view_pan_modal;
406   ot->cancel = image_view_pan_cancel;
407   ot->poll = space_image_main_region_poll;
408 
409   /* flags */
410   ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_XY | OPTYPE_LOCK_BYPASS;
411 
412   /* properties */
413   RNA_def_float_vector(ot->srna,
414                        "offset",
415                        2,
416                        NULL,
417                        -FLT_MAX,
418                        FLT_MAX,
419                        "Offset",
420                        "Offset in floating point units, 1.0 is the width and height of the image",
421                        -FLT_MAX,
422                        FLT_MAX);
423 }
424 
425 /** \} */
426 
427 /* -------------------------------------------------------------------- */
428 /** \name View Zoom Operator
429  * \{ */
430 
431 typedef struct ViewZoomData {
432   float origx, origy;
433   float zoom;
434   int launch_event;
435   float location[2];
436 
437   /* needed for continuous zoom */
438   wmTimer *timer;
439   double timer_lastdraw;
440   bool own_cursor;
441 
442   /* */
443   SpaceImage *sima;
444   ARegion *region;
445 } ViewZoomData;
446 
image_view_zoom_init(bContext * C,wmOperator * op,const wmEvent * event)447 static void image_view_zoom_init(bContext *C, wmOperator *op, const wmEvent *event)
448 {
449   wmWindow *win = CTX_wm_window(C);
450   SpaceImage *sima = CTX_wm_space_image(C);
451   ARegion *region = CTX_wm_region(C);
452   ViewZoomData *vpd;
453 
454   op->customdata = vpd = MEM_callocN(sizeof(ViewZoomData), "ImageViewZoomData");
455 
456   /* Grab will be set when running from gizmo. */
457   vpd->own_cursor = (win->grabcursor == 0);
458   if (vpd->own_cursor) {
459     WM_cursor_modal_set(win, WM_CURSOR_NSEW_SCROLL);
460   }
461 
462   vpd->origx = event->x;
463   vpd->origy = event->y;
464   vpd->zoom = sima->zoom;
465   vpd->launch_event = WM_userdef_event_type_from_keymap_type(event->type);
466 
467   UI_view2d_region_to_view(
468       &region->v2d, event->mval[0], event->mval[1], &vpd->location[0], &vpd->location[1]);
469 
470   if (U.viewzoom == USER_ZOOM_CONT) {
471     /* needs a timer to continue redrawing */
472     vpd->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
473     vpd->timer_lastdraw = PIL_check_seconds_timer();
474   }
475 
476   vpd->sima = sima;
477   vpd->region = region;
478 
479   WM_event_add_modal_handler(C, op);
480 }
481 
image_view_zoom_exit(bContext * C,wmOperator * op,bool cancel)482 static void image_view_zoom_exit(bContext *C, wmOperator *op, bool cancel)
483 {
484   SpaceImage *sima = CTX_wm_space_image(C);
485   ViewZoomData *vpd = op->customdata;
486 
487   if (cancel) {
488     sima->zoom = vpd->zoom;
489     ED_region_tag_redraw(CTX_wm_region(C));
490   }
491 
492   if (vpd->timer) {
493     WM_event_remove_timer(CTX_wm_manager(C), vpd->timer->win, vpd->timer);
494   }
495 
496   if (vpd->own_cursor) {
497     WM_cursor_modal_restore(CTX_wm_window(C));
498   }
499   MEM_freeN(op->customdata);
500 }
501 
image_view_zoom_exec(bContext * C,wmOperator * op)502 static int image_view_zoom_exec(bContext *C, wmOperator *op)
503 {
504   SpaceImage *sima = CTX_wm_space_image(C);
505   ARegion *region = CTX_wm_region(C);
506 
507   sima_zoom_set_factor(sima, region, RNA_float_get(op->ptr, "factor"), NULL, false);
508 
509   ED_region_tag_redraw(region);
510 
511   return OPERATOR_FINISHED;
512 }
513 
514 enum {
515   VIEW_PASS = 0,
516   VIEW_APPLY,
517   VIEW_CONFIRM,
518 };
519 
image_view_zoom_invoke(bContext * C,wmOperator * op,const wmEvent * event)520 static int image_view_zoom_invoke(bContext *C, wmOperator *op, const wmEvent *event)
521 {
522   if (event->type == MOUSEZOOM || event->type == MOUSEPAN) {
523     SpaceImage *sima = CTX_wm_space_image(C);
524     ARegion *region = CTX_wm_region(C);
525     float delta, factor, location[2];
526 
527     UI_view2d_region_to_view(
528         &region->v2d, event->mval[0], event->mval[1], &location[0], &location[1]);
529 
530     delta = event->prevx - event->x + event->prevy - event->y;
531 
532     if (U.uiflag & USER_ZOOM_INVERT) {
533       delta *= -1;
534     }
535 
536     factor = 1.0f + delta / 300.0f;
537     RNA_float_set(op->ptr, "factor", factor);
538     const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init");
539     sima_zoom_set(sima,
540                   region,
541                   sima->zoom * factor,
542                   location,
543                   (use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS)));
544     ED_region_tag_redraw(region);
545 
546     return OPERATOR_FINISHED;
547   }
548 
549   image_view_zoom_init(C, op, event);
550   return OPERATOR_RUNNING_MODAL;
551 }
552 
image_zoom_apply(ViewZoomData * vpd,wmOperator * op,const int x,const int y,const short viewzoom,const short zoom_invert,const bool zoom_to_pos)553 static void image_zoom_apply(ViewZoomData *vpd,
554                              wmOperator *op,
555                              const int x,
556                              const int y,
557                              const short viewzoom,
558                              const short zoom_invert,
559                              const bool zoom_to_pos)
560 {
561   float factor;
562   float delta;
563 
564   if (viewzoom != USER_ZOOM_SCALE) {
565     if (U.uiflag & USER_ZOOM_HORIZ) {
566       delta = (float)(x - vpd->origx);
567     }
568     else {
569       delta = (float)(y - vpd->origy);
570     }
571   }
572   else {
573     delta = x - vpd->origx + y - vpd->origy;
574   }
575 
576   delta /= U.pixelsize;
577 
578   if (zoom_invert) {
579     delta = -delta;
580   }
581 
582   if (viewzoom == USER_ZOOM_CONT) {
583     double time = PIL_check_seconds_timer();
584     float time_step = (float)(time - vpd->timer_lastdraw);
585     float zfac;
586 
587     /* oldstyle zoom */
588     zfac = 1.0f + ((delta / 20.0f) * time_step);
589     vpd->timer_lastdraw = time;
590     /* this is the final zoom, but instead make it into a factor */
591     factor = (vpd->sima->zoom * zfac) / vpd->zoom;
592   }
593   else {
594     factor = 1.0f + delta / 300.0f;
595   }
596 
597   RNA_float_set(op->ptr, "factor", factor);
598   sima_zoom_set(vpd->sima, vpd->region, vpd->zoom * factor, vpd->location, zoom_to_pos);
599   ED_region_tag_redraw(vpd->region);
600 }
601 
image_view_zoom_modal(bContext * C,wmOperator * op,const wmEvent * event)602 static int image_view_zoom_modal(bContext *C, wmOperator *op, const wmEvent *event)
603 {
604   ViewZoomData *vpd = op->customdata;
605   short event_code = VIEW_PASS;
606 
607   /* execute the events */
608   if (event->type == TIMER && event->customdata == vpd->timer) {
609     /* continuous zoom */
610     event_code = VIEW_APPLY;
611   }
612   else if (event->type == MOUSEMOVE) {
613     event_code = VIEW_APPLY;
614   }
615   else if (event->type == vpd->launch_event && event->val == KM_RELEASE) {
616     event_code = VIEW_CONFIRM;
617   }
618 
619   if (event_code == VIEW_APPLY) {
620     const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init");
621     image_zoom_apply(vpd,
622                      op,
623                      event->x,
624                      event->y,
625                      U.viewzoom,
626                      (U.uiflag & USER_ZOOM_INVERT) != 0,
627                      (use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS)));
628   }
629   else if (event_code == VIEW_CONFIRM) {
630     image_view_zoom_exit(C, op, false);
631     return OPERATOR_FINISHED;
632   }
633 
634   return OPERATOR_RUNNING_MODAL;
635 }
636 
image_view_zoom_cancel(bContext * C,wmOperator * op)637 static void image_view_zoom_cancel(bContext *C, wmOperator *op)
638 {
639   image_view_zoom_exit(C, op, true);
640 }
641 
IMAGE_OT_view_zoom(wmOperatorType * ot)642 void IMAGE_OT_view_zoom(wmOperatorType *ot)
643 {
644   PropertyRNA *prop;
645 
646   /* identifiers */
647   ot->name = "Zoom View";
648   ot->idname = "IMAGE_OT_view_zoom";
649   ot->description = "Zoom in/out the image";
650 
651   /* api callbacks */
652   ot->exec = image_view_zoom_exec;
653   ot->invoke = image_view_zoom_invoke;
654   ot->modal = image_view_zoom_modal;
655   ot->cancel = image_view_zoom_cancel;
656   ot->poll = space_image_main_region_poll;
657 
658   /* flags */
659   ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_XY | OPTYPE_LOCK_BYPASS;
660 
661   /* properties */
662   prop = RNA_def_float(ot->srna,
663                        "factor",
664                        0.0f,
665                        -FLT_MAX,
666                        FLT_MAX,
667                        "Factor",
668                        "Zoom factor, values higher than 1.0 zoom in, lower values zoom out",
669                        -FLT_MAX,
670                        FLT_MAX);
671   RNA_def_property_flag(prop, PROP_HIDDEN);
672 
673   WM_operator_properties_use_cursor_init(ot);
674 }
675 
676 #ifdef WITH_INPUT_NDOF
677 
678 /** \} */
679 
680 /* -------------------------------------------------------------------- */
681 /** \name NDOF Operator
682  * \{ */
683 
684 /* Combined pan/zoom from a 3D mouse device.
685  * Z zooms, XY pans
686  * "view" (not "paper") control -- user moves the viewpoint, not the image being viewed
687  * that explains the negative signs in the code below
688  */
689 
image_view_ndof_invoke(bContext * C,wmOperator * UNUSED (op),const wmEvent * event)690 static int image_view_ndof_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
691 {
692   if (event->type != NDOF_MOTION) {
693     return OPERATOR_CANCELLED;
694   }
695 
696   SpaceImage *sima = CTX_wm_space_image(C);
697   ARegion *region = CTX_wm_region(C);
698   float pan_vec[3];
699 
700   const wmNDOFMotionData *ndof = event->customdata;
701   const float speed = NDOF_PIXELS_PER_SECOND;
702 
703   WM_event_ndof_pan_get(ndof, pan_vec, true);
704 
705   mul_v2_fl(pan_vec, (speed * ndof->dt) / sima->zoom);
706   pan_vec[2] *= -ndof->dt;
707 
708   sima_zoom_set_factor(sima, region, 1.0f + pan_vec[2], NULL, false);
709   sima->xof += pan_vec[0];
710   sima->yof += pan_vec[1];
711 
712   ED_region_tag_redraw(region);
713 
714   return OPERATOR_FINISHED;
715 }
716 
IMAGE_OT_view_ndof(wmOperatorType * ot)717 void IMAGE_OT_view_ndof(wmOperatorType *ot)
718 {
719   /* identifiers */
720   ot->name = "NDOF Pan/Zoom";
721   ot->idname = "IMAGE_OT_view_ndof";
722   ot->description = "Use a 3D mouse device to pan/zoom the view";
723 
724   /* api callbacks */
725   ot->invoke = image_view_ndof_invoke;
726   ot->poll = space_image_main_region_poll;
727 
728   /* flags */
729   ot->flag = OPTYPE_LOCK_BYPASS;
730 }
731 
732 /** \} */
733 
734 #endif /* WITH_INPUT_NDOF */
735 
736 /* -------------------------------------------------------------------- */
737 /** \name View All Operator
738  * \{ */
739 
740 /* Updates the fields of the View2D member of the SpaceImage struct.
741  * Default behavior is to reset the position of the image and set the zoom to 1
742  * If the image will not fit within the window rectangle, the zoom is adjusted */
743 
image_view_all_exec(bContext * C,wmOperator * op)744 static int image_view_all_exec(bContext *C, wmOperator *op)
745 {
746   SpaceImage *sima;
747   ARegion *region;
748   float aspx, aspy, zoomx, zoomy, w, h;
749   int width, height;
750   const bool fit_view = RNA_boolean_get(op->ptr, "fit_view");
751 
752   /* retrieve state */
753   sima = CTX_wm_space_image(C);
754   region = CTX_wm_region(C);
755 
756   ED_space_image_get_size(sima, &width, &height);
757   ED_space_image_get_aspect(sima, &aspx, &aspy);
758 
759   w = width * aspx;
760   h = height * aspy;
761 
762   float xof = 0.0f, yof = 0.0f;
763   if ((sima->image == NULL) || (sima->image->source == IMA_SRC_TILED)) {
764     /* Extend the shown area to cover all UDIM tiles. */
765     int x_tiles, y_tiles;
766     if (sima->image == NULL) {
767       x_tiles = sima->tile_grid_shape[0];
768       y_tiles = sima->tile_grid_shape[1];
769     }
770     else {
771       x_tiles = y_tiles = 1;
772       LISTBASE_FOREACH (ImageTile *, tile, &sima->image->tiles) {
773         int tile_x = (tile->tile_number - 1001) % 10;
774         int tile_y = (tile->tile_number - 1001) / 10;
775         x_tiles = max_ii(x_tiles, tile_x + 1);
776         y_tiles = max_ii(y_tiles, tile_y + 1);
777       }
778     }
779     xof = 0.5f * (x_tiles - 1.0f) * w;
780     yof = 0.5f * (y_tiles - 1.0f) * h;
781     w *= x_tiles;
782     h *= y_tiles;
783   }
784 
785   /* check if the image will fit in the image with (zoom == 1) */
786   width = BLI_rcti_size_x(&region->winrct) + 1;
787   height = BLI_rcti_size_y(&region->winrct) + 1;
788 
789   if (fit_view) {
790     const int margin = 5; /* margin from border */
791 
792     zoomx = (float)width / (w + 2 * margin);
793     zoomy = (float)height / (h + 2 * margin);
794 
795     sima_zoom_set(sima, region, min_ff(zoomx, zoomy), NULL, false);
796   }
797   else {
798     if ((w >= width || h >= height) && (width > 0 && height > 0)) {
799       zoomx = (float)width / w;
800       zoomy = (float)height / h;
801 
802       /* find the zoom value that will fit the image in the image space */
803       sima_zoom_set(sima, region, 1.0f / power_of_2(1.0f / min_ff(zoomx, zoomy)), NULL, false);
804     }
805     else {
806       sima_zoom_set(sima, region, 1.0f, NULL, false);
807     }
808   }
809 
810   sima->xof = xof;
811   sima->yof = yof;
812 
813   ED_region_tag_redraw(region);
814 
815   return OPERATOR_FINISHED;
816 }
817 
IMAGE_OT_view_all(wmOperatorType * ot)818 void IMAGE_OT_view_all(wmOperatorType *ot)
819 {
820   PropertyRNA *prop;
821 
822   /* identifiers */
823   ot->name = "Frame All";
824   ot->idname = "IMAGE_OT_view_all";
825   ot->description = "View the entire image";
826 
827   /* api callbacks */
828   ot->exec = image_view_all_exec;
829   ot->poll = space_image_main_region_poll;
830 
831   /* flags */
832   ot->flag = OPTYPE_LOCK_BYPASS;
833 
834   /* properties */
835   prop = RNA_def_boolean(ot->srna, "fit_view", 0, "Fit View", "Fit frame to the viewport");
836   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
837 }
838 
839 /** \} */
840 
841 /* -------------------------------------------------------------------- */
842 /** \name Center View To Cursor Operator
843  * \{ */
844 
view_center_cursor_exec(bContext * C,wmOperator * UNUSED (op))845 static int view_center_cursor_exec(bContext *C, wmOperator *UNUSED(op))
846 {
847   SpaceImage *sima = CTX_wm_space_image(C);
848   ARegion *region = CTX_wm_region(C);
849 
850   ED_image_view_center_to_point(sima, sima->cursor[0], sima->cursor[1]);
851 
852   ED_region_tag_redraw(region);
853 
854   return OPERATOR_FINISHED;
855 }
856 
IMAGE_OT_view_center_cursor(wmOperatorType * ot)857 void IMAGE_OT_view_center_cursor(wmOperatorType *ot)
858 {
859   /* identifiers */
860   ot->name = "Center View to Cursor";
861   ot->description = "Center the view so that the cursor is in the middle of the view";
862   ot->idname = "IMAGE_OT_view_center_cursor";
863 
864   /* api callbacks */
865   ot->exec = view_center_cursor_exec;
866   ot->poll = ED_space_image_cursor_poll;
867 }
868 
869 /** \} */
870 
871 /* -------------------------------------------------------------------- */
872 /** \name Frame Selected Operator
873  * \{ */
874 
image_view_selected_exec(bContext * C,wmOperator * UNUSED (op))875 static int image_view_selected_exec(bContext *C, wmOperator *UNUSED(op))
876 {
877   SpaceImage *sima;
878   ARegion *region;
879   Scene *scene;
880   ViewLayer *view_layer;
881   Object *obedit;
882 
883   /* retrieve state */
884   sima = CTX_wm_space_image(C);
885   region = CTX_wm_region(C);
886   scene = CTX_data_scene(C);
887   view_layer = CTX_data_view_layer(C);
888   obedit = CTX_data_edit_object(C);
889 
890   /* get bounds */
891   float min[2], max[2];
892   if (ED_space_image_show_uvedit(sima, obedit)) {
893     uint objects_len = 0;
894     Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
895         view_layer, ((View3D *)NULL), &objects_len);
896     bool success = ED_uvedit_minmax_multi(scene, objects, objects_len, min, max);
897     MEM_freeN(objects);
898     if (!success) {
899       return OPERATOR_CANCELLED;
900     }
901   }
902   else if (ED_space_image_check_show_maskedit(sima, obedit)) {
903     if (!ED_mask_selected_minmax(C, min, max)) {
904       return OPERATOR_CANCELLED;
905     }
906   }
907   rctf bounds = {
908       .xmin = min[0],
909       .ymin = min[1],
910       .xmax = max[0],
911       .ymax = max[1],
912   };
913 
914   /* add some margin */
915   BLI_rctf_scale(&bounds, 1.4f);
916 
917   sima_zoom_set_from_bounds(sima, region, &bounds);
918 
919   ED_region_tag_redraw(region);
920 
921   return OPERATOR_FINISHED;
922 }
923 
image_view_selected_poll(bContext * C)924 static bool image_view_selected_poll(bContext *C)
925 {
926   return (space_image_main_region_poll(C) && (ED_operator_uvedit(C) || ED_operator_mask(C)));
927 }
928 
IMAGE_OT_view_selected(wmOperatorType * ot)929 void IMAGE_OT_view_selected(wmOperatorType *ot)
930 {
931   /* identifiers */
932   ot->name = "View Center";
933   ot->idname = "IMAGE_OT_view_selected";
934   ot->description = "View all selected UVs";
935 
936   /* api callbacks */
937   ot->exec = image_view_selected_exec;
938   ot->poll = image_view_selected_poll;
939 }
940 
941 /** \} */
942 
943 /* -------------------------------------------------------------------- */
944 /** \name View Zoom In/Out Operator
945  * \{ */
946 
image_view_zoom_in_exec(bContext * C,wmOperator * op)947 static int image_view_zoom_in_exec(bContext *C, wmOperator *op)
948 {
949   SpaceImage *sima = CTX_wm_space_image(C);
950   ARegion *region = CTX_wm_region(C);
951   float location[2];
952 
953   RNA_float_get_array(op->ptr, "location", location);
954 
955   sima_zoom_set_factor(
956       sima, region, powf(2.0f, 1.0f / 3.0f), location, U.uiflag & USER_ZOOM_TO_MOUSEPOS);
957 
958   ED_region_tag_redraw(region);
959 
960   return OPERATOR_FINISHED;
961 }
962 
image_view_zoom_in_invoke(bContext * C,wmOperator * op,const wmEvent * event)963 static int image_view_zoom_in_invoke(bContext *C, wmOperator *op, const wmEvent *event)
964 {
965   ARegion *region = CTX_wm_region(C);
966   float location[2];
967 
968   UI_view2d_region_to_view(
969       &region->v2d, event->mval[0], event->mval[1], &location[0], &location[1]);
970   RNA_float_set_array(op->ptr, "location", location);
971 
972   return image_view_zoom_in_exec(C, op);
973 }
974 
IMAGE_OT_view_zoom_in(wmOperatorType * ot)975 void IMAGE_OT_view_zoom_in(wmOperatorType *ot)
976 {
977   PropertyRNA *prop;
978 
979   /* identifiers */
980   ot->name = "Zoom In";
981   ot->idname = "IMAGE_OT_view_zoom_in";
982   ot->description = "Zoom in the image (centered around 2D cursor)";
983 
984   /* api callbacks */
985   ot->invoke = image_view_zoom_in_invoke;
986   ot->exec = image_view_zoom_in_exec;
987   ot->poll = space_image_main_region_poll;
988 
989   /* flags */
990   ot->flag = OPTYPE_LOCK_BYPASS;
991 
992   /* properties */
993   prop = RNA_def_float_vector(ot->srna,
994                               "location",
995                               2,
996                               NULL,
997                               -FLT_MAX,
998                               FLT_MAX,
999                               "Location",
1000                               "Cursor location in screen coordinates",
1001                               -10.0f,
1002                               10.0f);
1003   RNA_def_property_flag(prop, PROP_HIDDEN);
1004 }
1005 
image_view_zoom_out_exec(bContext * C,wmOperator * op)1006 static int image_view_zoom_out_exec(bContext *C, wmOperator *op)
1007 {
1008   SpaceImage *sima = CTX_wm_space_image(C);
1009   ARegion *region = CTX_wm_region(C);
1010   float location[2];
1011 
1012   RNA_float_get_array(op->ptr, "location", location);
1013 
1014   sima_zoom_set_factor(
1015       sima, region, powf(0.5f, 1.0f / 3.0f), location, U.uiflag & USER_ZOOM_TO_MOUSEPOS);
1016 
1017   ED_region_tag_redraw(region);
1018 
1019   return OPERATOR_FINISHED;
1020 }
1021 
image_view_zoom_out_invoke(bContext * C,wmOperator * op,const wmEvent * event)1022 static int image_view_zoom_out_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1023 {
1024   ARegion *region = CTX_wm_region(C);
1025   float location[2];
1026 
1027   UI_view2d_region_to_view(
1028       &region->v2d, event->mval[0], event->mval[1], &location[0], &location[1]);
1029   RNA_float_set_array(op->ptr, "location", location);
1030 
1031   return image_view_zoom_out_exec(C, op);
1032 }
1033 
IMAGE_OT_view_zoom_out(wmOperatorType * ot)1034 void IMAGE_OT_view_zoom_out(wmOperatorType *ot)
1035 {
1036   PropertyRNA *prop;
1037 
1038   /* identifiers */
1039   ot->name = "Zoom Out";
1040   ot->idname = "IMAGE_OT_view_zoom_out";
1041   ot->description = "Zoom out the image (centered around 2D cursor)";
1042 
1043   /* api callbacks */
1044   ot->invoke = image_view_zoom_out_invoke;
1045   ot->exec = image_view_zoom_out_exec;
1046   ot->poll = space_image_main_region_poll;
1047 
1048   /* flags */
1049   ot->flag = OPTYPE_LOCK_BYPASS;
1050 
1051   /* properties */
1052   prop = RNA_def_float_vector(ot->srna,
1053                               "location",
1054                               2,
1055                               NULL,
1056                               -FLT_MAX,
1057                               FLT_MAX,
1058                               "Location",
1059                               "Cursor location in screen coordinates",
1060                               -10.0f,
1061                               10.0f);
1062   RNA_def_property_flag(prop, PROP_HIDDEN);
1063 }
1064 
1065 /** \} */
1066 
1067 /* -------------------------------------------------------------------- */
1068 /** \name View Zoom Ratio Operator
1069  * \{ */
1070 
image_view_zoom_ratio_exec(bContext * C,wmOperator * op)1071 static int image_view_zoom_ratio_exec(bContext *C, wmOperator *op)
1072 {
1073   SpaceImage *sima = CTX_wm_space_image(C);
1074   ARegion *region = CTX_wm_region(C);
1075 
1076   sima_zoom_set(sima, region, RNA_float_get(op->ptr, "ratio"), NULL, false);
1077 
1078   /* ensure pixel exact locations for draw */
1079   sima->xof = (int)sima->xof;
1080   sima->yof = (int)sima->yof;
1081 
1082   ED_region_tag_redraw(region);
1083 
1084   return OPERATOR_FINISHED;
1085 }
1086 
IMAGE_OT_view_zoom_ratio(wmOperatorType * ot)1087 void IMAGE_OT_view_zoom_ratio(wmOperatorType *ot)
1088 {
1089   /* identifiers */
1090   ot->name = "View Zoom Ratio";
1091   ot->idname = "IMAGE_OT_view_zoom_ratio";
1092   ot->description = "Set zoom ratio of the view";
1093 
1094   /* api callbacks */
1095   ot->exec = image_view_zoom_ratio_exec;
1096   ot->poll = space_image_main_region_poll;
1097 
1098   /* flags */
1099   ot->flag = OPTYPE_LOCK_BYPASS;
1100 
1101   /* properties */
1102   RNA_def_float(ot->srna,
1103                 "ratio",
1104                 0.0f,
1105                 -FLT_MAX,
1106                 FLT_MAX,
1107                 "Ratio",
1108                 "Zoom ratio, 1.0 is 1:1, higher is zoomed in, lower is zoomed out",
1109                 -FLT_MAX,
1110                 FLT_MAX);
1111 }
1112 
1113 /** \} */
1114 
1115 /* -------------------------------------------------------------------- */
1116 /** \name View Border-Zoom Operator
1117  * \{ */
1118 
image_view_zoom_border_exec(bContext * C,wmOperator * op)1119 static int image_view_zoom_border_exec(bContext *C, wmOperator *op)
1120 {
1121   SpaceImage *sima = CTX_wm_space_image(C);
1122   ARegion *region = CTX_wm_region(C);
1123   rctf bounds;
1124   const bool zoom_in = !RNA_boolean_get(op->ptr, "zoom_out");
1125 
1126   WM_operator_properties_border_to_rctf(op, &bounds);
1127 
1128   UI_view2d_region_to_view_rctf(&region->v2d, &bounds, &bounds);
1129 
1130   const struct {
1131     float xof;
1132     float yof;
1133     float zoom;
1134   } sima_view_prev = {
1135       .xof = sima->xof,
1136       .yof = sima->yof,
1137       .zoom = sima->zoom,
1138   };
1139 
1140   sima_zoom_set_from_bounds(sima, region, &bounds);
1141 
1142   /* zoom out */
1143   if (!zoom_in) {
1144     sima->xof = sima_view_prev.xof + (sima->xof - sima_view_prev.xof);
1145     sima->yof = sima_view_prev.yof + (sima->yof - sima_view_prev.yof);
1146     sima->zoom = sima_view_prev.zoom * (sima_view_prev.zoom / sima->zoom);
1147   }
1148 
1149   ED_region_tag_redraw(region);
1150 
1151   return OPERATOR_FINISHED;
1152 }
1153 
IMAGE_OT_view_zoom_border(wmOperatorType * ot)1154 void IMAGE_OT_view_zoom_border(wmOperatorType *ot)
1155 {
1156   /* identifiers */
1157   ot->name = "Zoom to Border";
1158   ot->description = "Zoom in the view to the nearest item contained in the border";
1159   ot->idname = "IMAGE_OT_view_zoom_border";
1160 
1161   /* api callbacks */
1162   ot->invoke = WM_gesture_box_invoke;
1163   ot->exec = image_view_zoom_border_exec;
1164   ot->modal = WM_gesture_box_modal;
1165   ot->cancel = WM_gesture_box_cancel;
1166 
1167   ot->poll = space_image_main_region_poll;
1168 
1169   /* rna */
1170   WM_operator_properties_gesture_box_zoom(ot);
1171 }
1172 
1173 /* load/replace/save callbacks */
image_filesel(bContext * C,wmOperator * op,const char * path)1174 static void image_filesel(bContext *C, wmOperator *op, const char *path)
1175 {
1176   RNA_string_set(op->ptr, "filepath", path);
1177   WM_event_add_fileselect(C, op);
1178 }
1179 
1180 /** \} */
1181 
1182 /* -------------------------------------------------------------------- */
1183 /** \name Open Image Operator
1184  * \{ */
1185 
1186 typedef struct ImageOpenData {
1187   PropertyPointerRNA pprop;
1188   ImageUser *iuser;
1189   ImageFormatData im_format;
1190 } ImageOpenData;
1191 
image_open_init(bContext * C,wmOperator * op)1192 static void image_open_init(bContext *C, wmOperator *op)
1193 {
1194   ImageOpenData *iod;
1195   op->customdata = iod = MEM_callocN(sizeof(ImageOpenData), __func__);
1196   iod->iuser = CTX_data_pointer_get_type(C, "image_user", &RNA_ImageUser).data;
1197   UI_context_active_but_prop_get_templateID(C, &iod->pprop.ptr, &iod->pprop.prop);
1198 }
1199 
image_open_cancel(bContext * UNUSED (C),wmOperator * op)1200 static void image_open_cancel(bContext *UNUSED(C), wmOperator *op)
1201 {
1202   MEM_freeN(op->customdata);
1203   op->customdata = NULL;
1204 }
1205 
image_open_single(Main * bmain,wmOperator * op,ImageFrameRange * range,const char * relbase,bool is_relative_path,bool use_multiview)1206 static Image *image_open_single(Main *bmain,
1207                                 wmOperator *op,
1208                                 ImageFrameRange *range,
1209                                 const char *relbase,
1210                                 bool is_relative_path,
1211                                 bool use_multiview)
1212 {
1213   bool exists = false;
1214   Image *ima = NULL;
1215 
1216   errno = 0;
1217   ima = BKE_image_load_exists_ex(bmain, range->filepath, &exists);
1218 
1219   if (!ima) {
1220     if (op->customdata) {
1221       MEM_freeN(op->customdata);
1222     }
1223     BKE_reportf(op->reports,
1224                 RPT_ERROR,
1225                 "Cannot read '%s': %s",
1226                 range->filepath,
1227                 errno ? strerror(errno) : TIP_("unsupported image format"));
1228     return NULL;
1229   }
1230 
1231   if (!exists) {
1232     /* only image path after save, never ibuf */
1233     if (is_relative_path) {
1234       BLI_path_rel(ima->filepath, relbase);
1235     }
1236 
1237     /* handle multiview images */
1238     if (use_multiview) {
1239       ImageOpenData *iod = op->customdata;
1240       ImageFormatData *imf = &iod->im_format;
1241 
1242       ima->flag |= IMA_USE_VIEWS;
1243       ima->views_format = imf->views_format;
1244       *ima->stereo3d_format = imf->stereo3d_format;
1245     }
1246     else {
1247       ima->flag &= ~IMA_USE_VIEWS;
1248       BKE_image_free_views(ima);
1249     }
1250 
1251     if ((range->length > 1) && (ima->source == IMA_SRC_FILE)) {
1252       if (range->udim_tiles.first && range->offset == 1001) {
1253         ima->source = IMA_SRC_TILED;
1254         LISTBASE_FOREACH (LinkData *, node, &range->udim_tiles) {
1255           BKE_image_add_tile(ima, POINTER_AS_INT(node->data), NULL);
1256         }
1257       }
1258       else {
1259         ima->source = IMA_SRC_SEQUENCE;
1260       }
1261     }
1262   }
1263 
1264   return ima;
1265 }
1266 
image_open_exec(bContext * C,wmOperator * op)1267 static int image_open_exec(bContext *C, wmOperator *op)
1268 {
1269   Main *bmain = CTX_data_main(C);
1270   ScrArea *area = CTX_wm_area(C);
1271   Scene *scene = CTX_data_scene(C);
1272   Object *obedit = CTX_data_edit_object(C);
1273   ImageUser *iuser = NULL;
1274   ImageOpenData *iod = op->customdata;
1275   Image *ima = NULL;
1276   int frame_seq_len = 0;
1277   int frame_ofs = 1;
1278 
1279   const bool is_relative_path = RNA_boolean_get(op->ptr, "relative_path");
1280   const bool use_multiview = RNA_boolean_get(op->ptr, "use_multiview");
1281   const bool use_udim = RNA_boolean_get(op->ptr, "use_udim_detecting");
1282 
1283   if (!op->customdata) {
1284     image_open_init(C, op);
1285   }
1286 
1287   ListBase ranges = ED_image_filesel_detect_sequences(bmain, op, use_udim);
1288   LISTBASE_FOREACH (ImageFrameRange *, range, &ranges) {
1289     Image *ima_range = image_open_single(
1290         bmain, op, range, BKE_main_blendfile_path(bmain), is_relative_path, use_multiview);
1291 
1292     /* take the first image */
1293     if ((ima == NULL) && ima_range) {
1294       ima = ima_range;
1295       frame_seq_len = range->length;
1296       frame_ofs = range->offset;
1297     }
1298 
1299     BLI_freelistN(&range->udim_tiles);
1300   }
1301   BLI_freelistN(&ranges);
1302 
1303   if (ima == NULL) {
1304     return OPERATOR_CANCELLED;
1305   }
1306 
1307   /* hook into UI */
1308   iod = op->customdata;
1309 
1310   if (iod->pprop.prop) {
1311     /* when creating new ID blocks, use is already 1, but RNA
1312      * pointer use also increases user, so this compensates it */
1313     id_us_min(&ima->id);
1314 
1315     PointerRNA imaptr;
1316     RNA_id_pointer_create(&ima->id, &imaptr);
1317     RNA_property_pointer_set(&iod->pprop.ptr, iod->pprop.prop, imaptr, NULL);
1318     RNA_property_update(C, &iod->pprop.ptr, iod->pprop.prop);
1319   }
1320 
1321   if (iod->iuser) {
1322     iuser = iod->iuser;
1323   }
1324   else if (area && area->spacetype == SPACE_IMAGE) {
1325     SpaceImage *sima = area->spacedata.first;
1326     ED_space_image_set(bmain, sima, obedit, ima, false);
1327     iuser = &sima->iuser;
1328   }
1329   else {
1330     Tex *tex = CTX_data_pointer_get_type(C, "texture", &RNA_Texture).data;
1331     if (tex && tex->type == TEX_IMAGE) {
1332       iuser = &tex->iuser;
1333     }
1334 
1335     if (iuser == NULL) {
1336       Camera *cam = CTX_data_pointer_get_type(C, "camera", &RNA_Camera).data;
1337       if (cam) {
1338         LISTBASE_FOREACH (CameraBGImage *, bgpic, &cam->bg_images) {
1339           if (bgpic->ima == ima) {
1340             iuser = &bgpic->iuser;
1341             break;
1342           }
1343         }
1344       }
1345     }
1346   }
1347 
1348   /* initialize because of new image */
1349   if (iuser) {
1350     /* If the sequence was a tiled image, we only have one frame. */
1351     iuser->frames = (ima->source == IMA_SRC_SEQUENCE) ? frame_seq_len : 1;
1352     iuser->sfra = 1;
1353     iuser->framenr = 1;
1354     if (ima->source == IMA_SRC_MOVIE) {
1355       iuser->offset = 0;
1356     }
1357     else {
1358       iuser->offset = frame_ofs - 1;
1359     }
1360     iuser->scene = scene;
1361     BKE_image_init_imageuser(ima, iuser);
1362   }
1363 
1364   /* XXX BKE_packedfile_unpack_image frees image buffers */
1365   ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
1366 
1367   BKE_image_signal(bmain, ima, iuser, IMA_SIGNAL_RELOAD);
1368   WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima);
1369 
1370   MEM_freeN(op->customdata);
1371 
1372   return OPERATOR_FINISHED;
1373 }
1374 
image_open_invoke(bContext * C,wmOperator * op,const wmEvent * UNUSED (event))1375 static int image_open_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
1376 {
1377   SpaceImage *sima = CTX_wm_space_image(C); /* XXX other space types can call */
1378   const char *path = U.textudir;
1379   Image *ima = NULL;
1380   Scene *scene = CTX_data_scene(C);
1381 
1382   if (sima) {
1383     ima = sima->image;
1384   }
1385 
1386   if (ima == NULL) {
1387     Tex *tex = CTX_data_pointer_get_type(C, "texture", &RNA_Texture).data;
1388     if (tex && tex->type == TEX_IMAGE) {
1389       ima = tex->ima;
1390     }
1391   }
1392 
1393   if (ima == NULL) {
1394     PointerRNA ptr;
1395     PropertyRNA *prop;
1396 
1397     /* hook into UI */
1398     UI_context_active_but_prop_get_templateID(C, &ptr, &prop);
1399 
1400     if (prop) {
1401       PointerRNA oldptr;
1402       Image *oldima;
1403 
1404       oldptr = RNA_property_pointer_get(&ptr, prop);
1405       oldima = (Image *)oldptr.owner_id;
1406       /* unlikely to fail but better avoid strange crash */
1407       if (oldima && GS(oldima->id.name) == ID_IM) {
1408         ima = oldima;
1409       }
1410     }
1411   }
1412 
1413   if (ima) {
1414     path = ima->filepath;
1415   }
1416 
1417   if (RNA_struct_property_is_set(op->ptr, "filepath")) {
1418     return image_open_exec(C, op);
1419   }
1420 
1421   image_open_init(C, op);
1422 
1423   /* show multiview save options only if scene has multiviews */
1424   PropertyRNA *prop;
1425   prop = RNA_struct_find_property(op->ptr, "show_multiview");
1426   RNA_property_boolean_set(op->ptr, prop, (scene->r.scemode & R_MULTIVIEW) != 0);
1427 
1428   image_filesel(C, op, path);
1429 
1430   return OPERATOR_RUNNING_MODAL;
1431 }
1432 
image_open_draw_check_prop(PointerRNA * UNUSED (ptr),PropertyRNA * prop,void * UNUSED (user_data))1433 static bool image_open_draw_check_prop(PointerRNA *UNUSED(ptr),
1434                                        PropertyRNA *prop,
1435                                        void *UNUSED(user_data))
1436 {
1437   const char *prop_id = RNA_property_identifier(prop);
1438 
1439   return !(STREQ(prop_id, "filepath") || STREQ(prop_id, "directory") ||
1440            STREQ(prop_id, "filename"));
1441 }
1442 
image_open_draw(bContext * UNUSED (C),wmOperator * op)1443 static void image_open_draw(bContext *UNUSED(C), wmOperator *op)
1444 {
1445   uiLayout *layout = op->layout;
1446   ImageOpenData *iod = op->customdata;
1447   ImageFormatData *imf = &iod->im_format;
1448   PointerRNA imf_ptr, ptr;
1449 
1450   /* main draw call */
1451   RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
1452   uiDefAutoButsRNA(
1453       layout, &ptr, image_open_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false);
1454 
1455   /* image template */
1456   RNA_pointer_create(NULL, &RNA_ImageFormatSettings, imf, &imf_ptr);
1457 
1458   /* multiview template */
1459   if (RNA_boolean_get(op->ptr, "show_multiview")) {
1460     uiTemplateImageFormatViews(layout, &imf_ptr, op->ptr);
1461   }
1462 }
1463 
1464 /* called by other space types too */
IMAGE_OT_open(wmOperatorType * ot)1465 void IMAGE_OT_open(wmOperatorType *ot)
1466 {
1467   /* identifiers */
1468   ot->name = "Open Image";
1469   ot->description = "Open image";
1470   ot->idname = "IMAGE_OT_open";
1471 
1472   /* api callbacks */
1473   ot->exec = image_open_exec;
1474   ot->invoke = image_open_invoke;
1475   ot->cancel = image_open_cancel;
1476   ot->ui = image_open_draw;
1477 
1478   /* flags */
1479   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1480 
1481   /* properties */
1482   WM_operator_properties_filesel(ot,
1483                                  FILE_TYPE_FOLDER | FILE_TYPE_IMAGE | FILE_TYPE_MOVIE,
1484                                  FILE_SPECIAL,
1485                                  FILE_OPENFILE,
1486                                  WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILES |
1487                                      WM_FILESEL_RELPATH,
1488                                  FILE_DEFAULTDISPLAY,
1489                                  FILE_SORT_ALPHA);
1490 
1491   RNA_def_boolean(
1492       ot->srna,
1493       "use_sequence_detection",
1494       true,
1495       "Detect Sequences",
1496       "Automatically detect animated sequences in selected images (based on file names)");
1497   RNA_def_boolean(ot->srna,
1498                   "use_udim_detecting",
1499                   true,
1500                   "Detect UDIMs",
1501                   "Detect selected UDIM files and load all matching tiles");
1502 }
1503 
1504 /** \} */
1505 
1506 /* -------------------------------------------------------------------- */
1507 /** \name Match Movie Length Operator
1508  * \{ */
1509 
image_match_len_exec(bContext * C,wmOperator * UNUSED (op))1510 static int image_match_len_exec(bContext *C, wmOperator *UNUSED(op))
1511 {
1512   Scene *scene = CTX_data_scene(C);
1513   Image *ima = image_from_context(C);
1514   ImageUser *iuser = image_user_from_context(C);
1515 
1516   if (!ima || !iuser) {
1517     /* Try to get a Texture, or a SpaceImage from context... */
1518     Tex *tex = CTX_data_pointer_get_type(C, "texture", &RNA_Texture).data;
1519     if (tex && tex->type == TEX_IMAGE) {
1520       ima = tex->ima;
1521       iuser = &tex->iuser;
1522     }
1523   }
1524 
1525   if (!ima || !iuser || !BKE_image_has_anim(ima)) {
1526     return OPERATOR_CANCELLED;
1527   }
1528 
1529   struct anim *anim = ((ImageAnim *)ima->anims.first)->anim;
1530   if (!anim) {
1531     return OPERATOR_CANCELLED;
1532   }
1533   iuser->frames = IMB_anim_get_duration(anim, IMB_TC_RECORD_RUN);
1534   BKE_image_user_frame_calc(ima, iuser, scene->r.cfra);
1535 
1536   return OPERATOR_FINISHED;
1537 }
1538 
1539 /* called by other space types too */
IMAGE_OT_match_movie_length(wmOperatorType * ot)1540 void IMAGE_OT_match_movie_length(wmOperatorType *ot)
1541 {
1542   /* identifiers */
1543   ot->name = "Match Movie Length";
1544   ot->description = "Set image's user's length to the one of this video";
1545   ot->idname = "IMAGE_OT_match_movie_length";
1546 
1547   /* api callbacks */
1548   ot->exec = image_match_len_exec;
1549 
1550   /* flags */
1551   /* Don't think we need undo for that. */
1552   ot->flag = OPTYPE_REGISTER | OPTYPE_INTERNAL /* | OPTYPE_UNDO */;
1553 }
1554 
1555 /** \} */
1556 
1557 /* -------------------------------------------------------------------- */
1558 /** \name Replace Image Operator
1559  * \{ */
1560 
image_replace_exec(bContext * C,wmOperator * op)1561 static int image_replace_exec(bContext *C, wmOperator *op)
1562 {
1563   Main *bmain = CTX_data_main(C);
1564   SpaceImage *sima = CTX_wm_space_image(C);
1565   char str[FILE_MAX];
1566 
1567   if (!sima->image) {
1568     return OPERATOR_CANCELLED;
1569   }
1570 
1571   RNA_string_get(op->ptr, "filepath", str);
1572 
1573   /* we cant do much if the str is longer than FILE_MAX :/ */
1574   BLI_strncpy(sima->image->filepath, str, sizeof(sima->image->filepath));
1575 
1576   if (sima->image->source == IMA_SRC_GENERATED) {
1577     sima->image->source = IMA_SRC_FILE;
1578     BKE_image_signal(bmain, sima->image, &sima->iuser, IMA_SIGNAL_SRC_CHANGE);
1579   }
1580 
1581   if (BLI_path_extension_check_array(str, imb_ext_movie)) {
1582     sima->image->source = IMA_SRC_MOVIE;
1583   }
1584   else {
1585     sima->image->source = IMA_SRC_FILE;
1586   }
1587 
1588   /* XXX BKE_packedfile_unpack_image frees image buffers */
1589   ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
1590 
1591   BKE_icon_changed(BKE_icon_id_ensure(&sima->image->id));
1592   BKE_image_signal(bmain, sima->image, &sima->iuser, IMA_SIGNAL_RELOAD);
1593   WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, sima->image);
1594 
1595   return OPERATOR_FINISHED;
1596 }
1597 
image_replace_invoke(bContext * C,wmOperator * op,const wmEvent * UNUSED (event))1598 static int image_replace_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
1599 {
1600   SpaceImage *sima = CTX_wm_space_image(C);
1601 
1602   if (!sima->image) {
1603     return OPERATOR_CANCELLED;
1604   }
1605 
1606   if (RNA_struct_property_is_set(op->ptr, "filepath")) {
1607     return image_replace_exec(C, op);
1608   }
1609 
1610   if (!RNA_struct_property_is_set(op->ptr, "relative_path")) {
1611     RNA_boolean_set(op->ptr, "relative_path", BLI_path_is_rel(sima->image->filepath));
1612   }
1613 
1614   image_filesel(C, op, sima->image->filepath);
1615 
1616   return OPERATOR_RUNNING_MODAL;
1617 }
1618 
IMAGE_OT_replace(wmOperatorType * ot)1619 void IMAGE_OT_replace(wmOperatorType *ot)
1620 {
1621   /* identifiers */
1622   ot->name = "Replace Image";
1623   ot->idname = "IMAGE_OT_replace";
1624   ot->description = "Replace current image by another one from disk";
1625 
1626   /* api callbacks */
1627   ot->exec = image_replace_exec;
1628   ot->invoke = image_replace_invoke;
1629   ot->poll = image_not_packed_poll;
1630 
1631   /* flags */
1632   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1633 
1634   /* properties */
1635   WM_operator_properties_filesel(ot,
1636                                  FILE_TYPE_FOLDER | FILE_TYPE_IMAGE | FILE_TYPE_MOVIE,
1637                                  FILE_SPECIAL,
1638                                  FILE_OPENFILE,
1639                                  WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH,
1640                                  FILE_DEFAULTDISPLAY,
1641                                  FILE_SORT_ALPHA);
1642 }
1643 
1644 /** \} */
1645 
1646 /* -------------------------------------------------------------------- */
1647 /** \name Save Image As Operator
1648  * \{ */
1649 
1650 typedef struct ImageSaveData {
1651   ImageUser *iuser;
1652   Image *image;
1653   ImageFormatData im_format;
1654 } ImageSaveData;
1655 
imtype_best_depth(ImBuf * ibuf,const char imtype)1656 static char imtype_best_depth(ImBuf *ibuf, const char imtype)
1657 {
1658   const char depth_ok = BKE_imtype_valid_depths(imtype);
1659 
1660   if (ibuf->rect_float) {
1661     if (depth_ok & R_IMF_CHAN_DEPTH_32) {
1662       return R_IMF_CHAN_DEPTH_32;
1663     }
1664     if (depth_ok & R_IMF_CHAN_DEPTH_24) {
1665       return R_IMF_CHAN_DEPTH_24;
1666     }
1667     if (depth_ok & R_IMF_CHAN_DEPTH_16) {
1668       return R_IMF_CHAN_DEPTH_16;
1669     }
1670     if (depth_ok & R_IMF_CHAN_DEPTH_12) {
1671       return R_IMF_CHAN_DEPTH_12;
1672     }
1673     return R_IMF_CHAN_DEPTH_8;
1674   }
1675 
1676   if (depth_ok & R_IMF_CHAN_DEPTH_8) {
1677     return R_IMF_CHAN_DEPTH_8;
1678   }
1679   if (depth_ok & R_IMF_CHAN_DEPTH_12) {
1680     return R_IMF_CHAN_DEPTH_12;
1681   }
1682   if (depth_ok & R_IMF_CHAN_DEPTH_16) {
1683     return R_IMF_CHAN_DEPTH_16;
1684   }
1685   if (depth_ok & R_IMF_CHAN_DEPTH_24) {
1686     return R_IMF_CHAN_DEPTH_24;
1687   }
1688   if (depth_ok & R_IMF_CHAN_DEPTH_32) {
1689     return R_IMF_CHAN_DEPTH_32;
1690   }
1691   return R_IMF_CHAN_DEPTH_8; /* fallback, should not get here */
1692 }
1693 
image_save_options_init(Main * bmain,ImageSaveOptions * opts,Image * ima,ImageUser * iuser,const bool guess_path,const bool save_as_render)1694 static int image_save_options_init(Main *bmain,
1695                                    ImageSaveOptions *opts,
1696                                    Image *ima,
1697                                    ImageUser *iuser,
1698                                    const bool guess_path,
1699                                    const bool save_as_render)
1700 {
1701   void *lock;
1702   ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, &lock);
1703 
1704   if (ibuf) {
1705     Scene *scene = opts->scene;
1706     bool is_depth_set = false;
1707 
1708     if (ELEM(ima->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) {
1709       /* imtype */
1710       opts->im_format = scene->r.im_format;
1711       is_depth_set = true;
1712       if (!BKE_image_is_multiview(ima)) {
1713         /* In case multiview is disabled,
1714          * render settings would be invalid for render result in this area. */
1715         opts->im_format.stereo3d_format = *ima->stereo3d_format;
1716         opts->im_format.views_format = ima->views_format;
1717       }
1718     }
1719     else {
1720       if (ima->source == IMA_SRC_GENERATED) {
1721         opts->im_format.imtype = R_IMF_IMTYPE_PNG;
1722         opts->im_format.compress = ibuf->foptions.quality;
1723         opts->im_format.planes = ibuf->planes;
1724       }
1725       else {
1726         BKE_imbuf_to_image_format(&opts->im_format, ibuf);
1727       }
1728 
1729       /* use the multiview image settings as the default */
1730       opts->im_format.stereo3d_format = *ima->stereo3d_format;
1731       opts->im_format.views_format = ima->views_format;
1732     }
1733 
1734     BLI_strncpy(opts->filepath, ibuf->name, sizeof(opts->filepath));
1735 
1736     /* sanitize all settings */
1737 
1738     /* unlikely but just in case */
1739     if (ELEM(opts->im_format.planes, R_IMF_PLANES_BW, R_IMF_PLANES_RGB, R_IMF_PLANES_RGBA) == 0) {
1740       opts->im_format.planes = R_IMF_PLANES_RGBA;
1741     }
1742 
1743     /* depth, account for float buffer and format support */
1744     if (is_depth_set == false) {
1745       opts->im_format.depth = imtype_best_depth(ibuf, opts->im_format.imtype);
1746     }
1747 
1748     /* some formats don't use quality so fallback to scenes quality */
1749     if (opts->im_format.quality == 0) {
1750       opts->im_format.quality = scene->r.im_format.quality;
1751     }
1752 
1753     /* check for empty path */
1754     if (guess_path && opts->filepath[0] == 0) {
1755       const bool is_prev_save = !STREQ(G.ima, "//");
1756       if (save_as_render) {
1757         if (is_prev_save) {
1758           BLI_strncpy(opts->filepath, G.ima, sizeof(opts->filepath));
1759         }
1760         else {
1761           BLI_strncpy(opts->filepath, "//untitled", sizeof(opts->filepath));
1762           BLI_path_abs(opts->filepath, BKE_main_blendfile_path(bmain));
1763         }
1764       }
1765       else {
1766         BLI_snprintf(opts->filepath, sizeof(opts->filepath), "//%s", ima->id.name + 2);
1767         BLI_path_make_safe(opts->filepath);
1768         BLI_path_abs(opts->filepath, is_prev_save ? G.ima : BKE_main_blendfile_path(bmain));
1769       }
1770 
1771       /* append UDIM numbering if not present */
1772       if (ima->source == IMA_SRC_TILED &&
1773           (BLI_path_sequence_decode(ima->filepath, NULL, NULL, NULL) != 1001)) {
1774         int len = strlen(opts->filepath);
1775         STR_CONCAT(opts->filepath, len, ".1001");
1776       }
1777     }
1778 
1779     /* color management */
1780     BKE_color_managed_display_settings_copy(&opts->im_format.display_settings,
1781                                             &scene->display_settings);
1782 
1783     BKE_color_managed_view_settings_free(&opts->im_format.view_settings);
1784     BKE_color_managed_view_settings_copy(&opts->im_format.view_settings, &scene->view_settings);
1785   }
1786 
1787   BKE_image_release_ibuf(ima, ibuf, lock);
1788 
1789   return (ibuf != NULL);
1790 }
1791 
image_save_options_from_op(Main * bmain,ImageSaveOptions * opts,wmOperator * op,ImageFormatData * imf)1792 static void image_save_options_from_op(Main *bmain,
1793                                        ImageSaveOptions *opts,
1794                                        wmOperator *op,
1795                                        ImageFormatData *imf)
1796 {
1797   if (imf) {
1798     BKE_color_managed_view_settings_free(&opts->im_format.view_settings);
1799     opts->im_format = *imf;
1800   }
1801 
1802   if (RNA_struct_property_is_set(op->ptr, "filepath")) {
1803     RNA_string_get(op->ptr, "filepath", opts->filepath);
1804     BLI_path_abs(opts->filepath, BKE_main_blendfile_path(bmain));
1805   }
1806 }
1807 
image_save_options_to_op(ImageSaveOptions * opts,wmOperator * op)1808 static void image_save_options_to_op(ImageSaveOptions *opts, wmOperator *op)
1809 {
1810   if (op->customdata) {
1811     ImageSaveData *isd = op->customdata;
1812     BKE_color_managed_view_settings_free(&isd->im_format.view_settings);
1813     isd->im_format = opts->im_format;
1814   }
1815 
1816   RNA_string_set(op->ptr, "filepath", opts->filepath);
1817 }
1818 
save_image_op(Main * bmain,Image * ima,ImageUser * iuser,wmOperator * op,ImageSaveOptions * opts)1819 static bool save_image_op(
1820     Main *bmain, Image *ima, ImageUser *iuser, wmOperator *op, ImageSaveOptions *opts)
1821 {
1822   opts->relative = (RNA_struct_find_property(op->ptr, "relative_path") &&
1823                     RNA_boolean_get(op->ptr, "relative_path"));
1824   opts->save_copy = (RNA_struct_find_property(op->ptr, "copy") &&
1825                      RNA_boolean_get(op->ptr, "copy"));
1826   opts->save_as_render = (RNA_struct_find_property(op->ptr, "save_as_render") &&
1827                           RNA_boolean_get(op->ptr, "save_as_render"));
1828 
1829   WM_cursor_wait(1);
1830 
1831   bool ok = BKE_image_save(op->reports, bmain, ima, iuser, opts);
1832 
1833   WM_cursor_wait(0);
1834 
1835   /* Remember file path for next save. */
1836   BLI_strncpy(G.ima, opts->filepath, sizeof(G.ima));
1837 
1838   WM_main_add_notifier(NC_IMAGE | NA_EDITED, ima);
1839 
1840   return ok;
1841 }
1842 
image_save_as_free(wmOperator * op)1843 static void image_save_as_free(wmOperator *op)
1844 {
1845   if (op->customdata) {
1846     ImageSaveData *isd = op->customdata;
1847     BKE_color_managed_view_settings_free(&isd->im_format.view_settings);
1848 
1849     MEM_freeN(op->customdata);
1850     op->customdata = NULL;
1851   }
1852 }
1853 
image_save_as_exec(bContext * C,wmOperator * op)1854 static int image_save_as_exec(bContext *C, wmOperator *op)
1855 {
1856   Main *bmain = CTX_data_main(C);
1857   Scene *scene = CTX_data_scene(C);
1858   ImageSaveOptions opts;
1859 
1860   Image *image = NULL;
1861   ImageUser *iuser = NULL;
1862   ImageFormatData *imf = NULL;
1863   if (op->customdata) {
1864     ImageSaveData *isd = op->customdata;
1865     image = isd->image;
1866     iuser = isd->iuser;
1867     imf = &isd->im_format;
1868   }
1869   else {
1870     image = image_from_context(C);
1871     iuser = image_user_from_context(C);
1872   }
1873 
1874   BKE_image_save_options_init(&opts, bmain, scene);
1875 
1876   /* just in case to initialize values,
1877    * these should be set on invoke or by the caller. */
1878   image_save_options_init(bmain, &opts, image, iuser, false, false);
1879 
1880   image_save_options_from_op(bmain, &opts, op, imf);
1881   opts.do_newpath = true;
1882 
1883   save_image_op(bmain, image, iuser, op, &opts);
1884 
1885   if (opts.save_copy == false) {
1886     BKE_image_free_packedfiles(image);
1887   }
1888 
1889   image_save_as_free(op);
1890 
1891   return OPERATOR_FINISHED;
1892 }
1893 
image_save_as_check(bContext * UNUSED (C),wmOperator * op)1894 static bool image_save_as_check(bContext *UNUSED(C), wmOperator *op)
1895 {
1896   ImageSaveData *isd = op->customdata;
1897   return WM_operator_filesel_ensure_ext_imtype(op, &isd->im_format);
1898 }
1899 
image_save_as_invoke(bContext * C,wmOperator * op,const wmEvent * UNUSED (event))1900 static int image_save_as_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
1901 {
1902   Main *bmain = CTX_data_main(C);
1903   Image *ima = image_from_context(C);
1904   ImageUser *iuser = image_user_from_context(C);
1905   Scene *scene = CTX_data_scene(C);
1906   ImageSaveOptions opts;
1907   PropertyRNA *prop;
1908   const bool save_as_render = (ima->source == IMA_SRC_VIEWER);
1909 
1910   if (RNA_struct_property_is_set(op->ptr, "filepath")) {
1911     return image_save_as_exec(C, op);
1912   }
1913 
1914   BKE_image_save_options_init(&opts, bmain, scene);
1915 
1916   if (image_save_options_init(bmain, &opts, ima, iuser, true, save_as_render) == 0) {
1917     return OPERATOR_CANCELLED;
1918   }
1919   image_save_options_to_op(&opts, op);
1920 
1921   /* enable save_copy by default for render results */
1922   if (ELEM(ima->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE) &&
1923       !RNA_struct_property_is_set(op->ptr, "copy")) {
1924     RNA_boolean_set(op->ptr, "copy", true);
1925   }
1926 
1927   RNA_boolean_set(op->ptr, "save_as_render", save_as_render);
1928 
1929   ImageSaveData *isd = MEM_callocN(sizeof(*isd), __func__);
1930   isd->image = ima;
1931   isd->iuser = iuser;
1932 
1933   memcpy(&isd->im_format, &opts.im_format, sizeof(opts.im_format));
1934   op->customdata = isd;
1935 
1936   /* show multiview save options only if image has multiviews */
1937   prop = RNA_struct_find_property(op->ptr, "show_multiview");
1938   RNA_property_boolean_set(op->ptr, prop, BKE_image_is_multiview(ima));
1939   prop = RNA_struct_find_property(op->ptr, "use_multiview");
1940   RNA_property_boolean_set(op->ptr, prop, BKE_image_is_multiview(ima));
1941 
1942   image_filesel(C, op, opts.filepath);
1943 
1944   return OPERATOR_RUNNING_MODAL;
1945 }
1946 
image_save_as_cancel(bContext * UNUSED (C),wmOperator * op)1947 static void image_save_as_cancel(bContext *UNUSED(C), wmOperator *op)
1948 {
1949   image_save_as_free(op);
1950 }
1951 
image_save_as_draw_check_prop(PointerRNA * ptr,PropertyRNA * prop,void * UNUSED (user_data))1952 static bool image_save_as_draw_check_prop(PointerRNA *ptr,
1953                                           PropertyRNA *prop,
1954                                           void *UNUSED(user_data))
1955 {
1956   const char *prop_id = RNA_property_identifier(prop);
1957 
1958   return !(STREQ(prop_id, "filepath") || STREQ(prop_id, "directory") ||
1959            STREQ(prop_id, "filename") ||
1960            /* when saving a copy, relative path has no effect */
1961            ((STREQ(prop_id, "relative_path")) && RNA_boolean_get(ptr, "copy")));
1962 }
1963 
image_save_as_draw(bContext * UNUSED (C),wmOperator * op)1964 static void image_save_as_draw(bContext *UNUSED(C), wmOperator *op)
1965 {
1966   uiLayout *layout = op->layout;
1967   ImageSaveData *isd = op->customdata;
1968   PointerRNA imf_ptr, ptr;
1969   const bool is_multiview = RNA_boolean_get(op->ptr, "show_multiview");
1970 
1971   /* image template */
1972   RNA_pointer_create(NULL, &RNA_ImageFormatSettings, &isd->im_format, &imf_ptr);
1973   uiTemplateImageSettings(layout, &imf_ptr, false);
1974 
1975   /* main draw call */
1976   RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
1977   uiDefAutoButsRNA(
1978       layout, &ptr, image_save_as_draw_check_prop, NULL, NULL, UI_BUT_LABEL_ALIGN_NONE, false);
1979 
1980   /* multiview template */
1981   if (is_multiview) {
1982     uiTemplateImageFormatViews(layout, &imf_ptr, op->ptr);
1983   }
1984 }
1985 
image_save_as_poll(bContext * C)1986 static bool image_save_as_poll(bContext *C)
1987 {
1988   if (!image_from_context_has_data_poll(C)) {
1989     return false;
1990   }
1991 
1992   if (G.is_rendering) {
1993     /* no need to NULL check here */
1994     Image *ima = image_from_context(C);
1995 
1996     if (ima->source == IMA_SRC_VIEWER) {
1997       CTX_wm_operator_poll_msg_set(C, "can't save image while rendering");
1998       return false;
1999     }
2000   }
2001 
2002   return true;
2003 }
2004 
IMAGE_OT_save_as(wmOperatorType * ot)2005 void IMAGE_OT_save_as(wmOperatorType *ot)
2006 {
2007   /* identifiers */
2008   ot->name = "Save As Image";
2009   ot->idname = "IMAGE_OT_save_as";
2010   ot->description = "Save the image with another name and/or settings";
2011 
2012   /* api callbacks */
2013   ot->exec = image_save_as_exec;
2014   ot->check = image_save_as_check;
2015   ot->invoke = image_save_as_invoke;
2016   ot->cancel = image_save_as_cancel;
2017   ot->ui = image_save_as_draw;
2018   ot->poll = image_save_as_poll;
2019 
2020   /* flags */
2021   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2022 
2023   /* properties */
2024   RNA_def_boolean(ot->srna,
2025                   "save_as_render",
2026                   0,
2027                   "Save As Render",
2028                   "Apply render part of display transform when saving byte image");
2029   RNA_def_boolean(ot->srna,
2030                   "copy",
2031                   0,
2032                   "Copy",
2033                   "Create a new image file without modifying the current image in blender");
2034 
2035   WM_operator_properties_filesel(ot,
2036                                  FILE_TYPE_FOLDER | FILE_TYPE_IMAGE | FILE_TYPE_MOVIE,
2037                                  FILE_SPECIAL,
2038                                  FILE_SAVE,
2039                                  WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH | WM_FILESEL_SHOW_PROPS,
2040                                  FILE_DEFAULTDISPLAY,
2041                                  FILE_SORT_ALPHA);
2042 }
2043 
2044 /** \} */
2045 
2046 /* -------------------------------------------------------------------- */
2047 /** \name Save Image Operator
2048  * \{ */
2049 
image_file_path_saveable(bContext * C,Image * ima,ImageUser * iuser)2050 static bool image_file_path_saveable(bContext *C, Image *ima, ImageUser *iuser)
2051 {
2052   /* Can always repack images. */
2053   if (BKE_image_has_packedfile(ima)) {
2054     return true;
2055   }
2056 
2057   /* Test for valid filepath. */
2058   void *lock;
2059   ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, &lock);
2060   bool ret = false;
2061 
2062   if (ibuf) {
2063     Main *bmain = CTX_data_main(C);
2064     char name[FILE_MAX];
2065     BLI_strncpy(name, ibuf->name, FILE_MAX);
2066     BLI_path_abs(name, BKE_main_blendfile_path(bmain));
2067 
2068     if (BLI_exists(name) == false) {
2069       CTX_wm_operator_poll_msg_set(C, "image file not found");
2070     }
2071     else if (!BLI_file_is_writable(name)) {
2072       CTX_wm_operator_poll_msg_set(C, "image path can't be written to");
2073     }
2074     else if (!BKE_image_buffer_format_writable(ibuf)) {
2075       CTX_wm_operator_poll_msg_set(C, "image format is read-only");
2076     }
2077     else {
2078       ret = true;
2079     }
2080   }
2081 
2082   BKE_image_release_ibuf(ima, ibuf, lock);
2083   return ret;
2084 }
2085 
image_save_poll(bContext * C)2086 static bool image_save_poll(bContext *C)
2087 {
2088   /* Can't save if there are no pixels. */
2089   if (image_from_context_has_data_poll(C) == false) {
2090     return false;
2091   }
2092 
2093   Image *ima = image_from_context(C);
2094   ImageUser *iuser = image_user_from_context(C);
2095 
2096   /* Images without a filepath will go to save as. */
2097   if (!BKE_image_has_filepath(ima)) {
2098     return true;
2099   }
2100 
2101   /* Check if there is a valid file path and image format we can write. */
2102   return image_file_path_saveable(C, ima, iuser);
2103 }
2104 
image_save_exec(bContext * C,wmOperator * op)2105 static int image_save_exec(bContext *C, wmOperator *op)
2106 {
2107   Main *bmain = CTX_data_main(C);
2108   Image *image = image_from_context(C);
2109   ImageUser *iuser = image_user_from_context(C);
2110   Scene *scene = CTX_data_scene(C);
2111   ImageSaveOptions opts;
2112   bool ok = false;
2113 
2114   if (BKE_image_has_packedfile(image)) {
2115     /* Save packed files to memory. */
2116     BKE_image_memorypack(image);
2117     return OPERATOR_FINISHED;
2118   }
2119 
2120   BKE_image_save_options_init(&opts, bmain, scene);
2121   if (image_save_options_init(bmain, &opts, image, iuser, false, false) == 0) {
2122     return OPERATOR_CANCELLED;
2123   }
2124   image_save_options_from_op(bmain, &opts, op, NULL);
2125 
2126   if (BLI_exists(opts.filepath) && BLI_file_is_writable(opts.filepath)) {
2127     if (save_image_op(bmain, image, iuser, op, &opts)) {
2128       /* report since this can be called from key-shortcuts */
2129       BKE_reportf(op->reports, RPT_INFO, "Saved Image '%s'", opts.filepath);
2130       ok = true;
2131     }
2132   }
2133   else {
2134     BKE_reportf(
2135         op->reports, RPT_ERROR, "Cannot save image, path '%s' is not writable", opts.filepath);
2136   }
2137 
2138   BKE_color_managed_view_settings_free(&opts.im_format.view_settings);
2139 
2140   if (ok) {
2141     return OPERATOR_FINISHED;
2142   }
2143 
2144   return OPERATOR_CANCELLED;
2145 }
2146 
image_save_invoke(bContext * C,wmOperator * op,const wmEvent * UNUSED (event))2147 static int image_save_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
2148 {
2149   Image *ima = image_from_context(C);
2150 
2151   if (!BKE_image_has_packedfile(ima) && !BKE_image_has_filepath(ima)) {
2152     WM_operator_name_call(C, "IMAGE_OT_save_as", WM_OP_INVOKE_DEFAULT, NULL);
2153     return OPERATOR_CANCELLED;
2154   }
2155   return image_save_exec(C, op);
2156 }
2157 
IMAGE_OT_save(wmOperatorType * ot)2158 void IMAGE_OT_save(wmOperatorType *ot)
2159 {
2160   /* identifiers */
2161   ot->name = "Save Image";
2162   ot->idname = "IMAGE_OT_save";
2163   ot->description = "Save the image with current name and settings";
2164 
2165   /* api callbacks */
2166   ot->exec = image_save_exec;
2167   ot->invoke = image_save_invoke;
2168   ot->poll = image_save_poll;
2169 
2170   /* flags */
2171   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2172 }
2173 
2174 /** \} */
2175 
2176 /* -------------------------------------------------------------------- */
2177 /** \name Save Sequence Operator
2178  * \{ */
2179 
image_save_sequence_exec(bContext * C,wmOperator * op)2180 static int image_save_sequence_exec(bContext *C, wmOperator *op)
2181 {
2182   Main *bmain = CTX_data_main(C);
2183   Image *image = image_from_context(C);
2184   ImBuf *ibuf, *first_ibuf = NULL;
2185   int tot = 0;
2186   char di[FILE_MAX];
2187   struct MovieCacheIter *iter;
2188 
2189   if (image == NULL) {
2190     return OPERATOR_CANCELLED;
2191   }
2192 
2193   if (image->source != IMA_SRC_SEQUENCE) {
2194     BKE_report(op->reports, RPT_ERROR, "Can only save sequence on image sequences");
2195     return OPERATOR_CANCELLED;
2196   }
2197 
2198   if (image->type == IMA_TYPE_MULTILAYER) {
2199     BKE_report(op->reports, RPT_ERROR, "Cannot save multilayer sequences");
2200     return OPERATOR_CANCELLED;
2201   }
2202 
2203   /* get total dirty buffers and first dirty buffer which is used for menu */
2204   ibuf = NULL;
2205   if (image->cache != NULL) {
2206     iter = IMB_moviecacheIter_new(image->cache);
2207     while (!IMB_moviecacheIter_done(iter)) {
2208       ibuf = IMB_moviecacheIter_getImBuf(iter);
2209       if (ibuf->userflags & IB_BITMAPDIRTY) {
2210         if (first_ibuf == NULL) {
2211           first_ibuf = ibuf;
2212         }
2213         tot++;
2214       }
2215       IMB_moviecacheIter_step(iter);
2216     }
2217     IMB_moviecacheIter_free(iter);
2218   }
2219 
2220   if (tot == 0) {
2221     BKE_report(op->reports, RPT_WARNING, "No images have been changed");
2222     return OPERATOR_CANCELLED;
2223   }
2224 
2225   /* get a filename for menu */
2226   BLI_split_dir_part(first_ibuf->name, di, sizeof(di));
2227   BKE_reportf(op->reports, RPT_INFO, "%d image(s) will be saved in %s", tot, di);
2228 
2229   iter = IMB_moviecacheIter_new(image->cache);
2230   while (!IMB_moviecacheIter_done(iter)) {
2231     ibuf = IMB_moviecacheIter_getImBuf(iter);
2232 
2233     if (ibuf->userflags & IB_BITMAPDIRTY) {
2234       char name[FILE_MAX];
2235       BLI_strncpy(name, ibuf->name, sizeof(name));
2236 
2237       BLI_path_abs(name, BKE_main_blendfile_path(bmain));
2238 
2239       if (0 == IMB_saveiff(ibuf, name, IB_rect | IB_zbuf | IB_zbuffloat)) {
2240         BKE_reportf(op->reports, RPT_ERROR, "Could not write image: %s", strerror(errno));
2241         break;
2242       }
2243 
2244       BKE_reportf(op->reports, RPT_INFO, "Saved %s", ibuf->name);
2245       ibuf->userflags &= ~IB_BITMAPDIRTY;
2246     }
2247 
2248     IMB_moviecacheIter_step(iter);
2249   }
2250   IMB_moviecacheIter_free(iter);
2251 
2252   return OPERATOR_FINISHED;
2253 }
2254 
IMAGE_OT_save_sequence(wmOperatorType * ot)2255 void IMAGE_OT_save_sequence(wmOperatorType *ot)
2256 {
2257   /* identifiers */
2258   ot->name = "Save Sequence";
2259   ot->idname = "IMAGE_OT_save_sequence";
2260   ot->description = "Save a sequence of images";
2261 
2262   /* api callbacks */
2263   ot->exec = image_save_sequence_exec;
2264   ot->poll = image_from_context_has_data_poll;
2265 
2266   /* flags */
2267   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2268 }
2269 
2270 /** \} */
2271 
2272 /* -------------------------------------------------------------------- */
2273 /** \name Save All Operator
2274  * \{ */
2275 
image_should_be_saved_when_modified(Image * ima)2276 static bool image_should_be_saved_when_modified(Image *ima)
2277 {
2278   return !ELEM(ima->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE);
2279 }
2280 
image_should_be_saved(Image * ima,bool * is_format_writable)2281 static bool image_should_be_saved(Image *ima, bool *is_format_writable)
2282 {
2283   if (BKE_image_is_dirty_writable(ima, is_format_writable) &&
2284       ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_GENERATED, IMA_SRC_TILED)) {
2285     return image_should_be_saved_when_modified(ima);
2286   }
2287   return false;
2288 }
2289 
image_has_valid_path(Image * ima)2290 static bool image_has_valid_path(Image *ima)
2291 {
2292   return strchr(ima->filepath, '\\') || strchr(ima->filepath, '/');
2293 }
2294 
ED_image_should_save_modified(const Main * bmain)2295 bool ED_image_should_save_modified(const Main *bmain)
2296 {
2297   ReportList reports;
2298   BKE_reports_init(&reports, RPT_STORE);
2299 
2300   uint modified_images_count = ED_image_save_all_modified_info(bmain, &reports);
2301   bool should_save = modified_images_count || !BLI_listbase_is_empty(&reports.list);
2302 
2303   BKE_reports_clear(&reports);
2304 
2305   return should_save;
2306 }
2307 
ED_image_save_all_modified_info(const Main * bmain,ReportList * reports)2308 int ED_image_save_all_modified_info(const Main *bmain, ReportList *reports)
2309 {
2310   GSet *unique_paths = BLI_gset_str_new(__func__);
2311 
2312   int num_saveable_images = 0;
2313 
2314   for (Image *ima = bmain->images.first; ima; ima = ima->id.next) {
2315     bool is_format_writable;
2316 
2317     if (image_should_be_saved(ima, &is_format_writable)) {
2318       if (BKE_image_has_packedfile(ima) || (ima->source == IMA_SRC_GENERATED)) {
2319         if (ima->id.lib == NULL) {
2320           num_saveable_images++;
2321         }
2322         else {
2323           BKE_reportf(reports,
2324                       RPT_WARNING,
2325                       "Packed library image can't be saved: \"%s\" from \"%s\"",
2326                       ima->id.name + 2,
2327                       ima->id.lib->filepath);
2328         }
2329       }
2330       else if (!is_format_writable) {
2331         BKE_reportf(reports,
2332                     RPT_WARNING,
2333                     "Image can't be saved, use a different file format: \"%s\"",
2334                     ima->id.name + 2);
2335       }
2336       else {
2337         if (image_has_valid_path(ima)) {
2338           num_saveable_images++;
2339           if (BLI_gset_haskey(unique_paths, ima->filepath)) {
2340             BKE_reportf(reports,
2341                         RPT_WARNING,
2342                         "Multiple images can't be saved to an identical path: \"%s\"",
2343                         ima->filepath);
2344           }
2345           else {
2346             BLI_gset_insert(unique_paths, BLI_strdup(ima->filepath));
2347           }
2348         }
2349         else {
2350           BKE_reportf(reports,
2351                       RPT_WARNING,
2352                       "Image can't be saved, no valid file path: \"%s\"",
2353                       ima->filepath);
2354         }
2355       }
2356     }
2357   }
2358 
2359   BLI_gset_free(unique_paths, MEM_freeN);
2360   return num_saveable_images;
2361 }
2362 
ED_image_save_all_modified(const bContext * C,ReportList * reports)2363 bool ED_image_save_all_modified(const bContext *C, ReportList *reports)
2364 {
2365   Main *bmain = CTX_data_main(C);
2366 
2367   ED_image_save_all_modified_info(bmain, reports);
2368 
2369   bool ok = true;
2370 
2371   for (Image *ima = bmain->images.first; ima; ima = ima->id.next) {
2372     bool is_format_writable;
2373 
2374     if (image_should_be_saved(ima, &is_format_writable)) {
2375       if (BKE_image_has_packedfile(ima) || (ima->source == IMA_SRC_GENERATED)) {
2376         BKE_image_memorypack(ima);
2377       }
2378       else if (is_format_writable) {
2379         if (image_has_valid_path(ima)) {
2380           ImageSaveOptions opts;
2381           Scene *scene = CTX_data_scene(C);
2382           BKE_image_save_options_init(&opts, bmain, scene);
2383           if (image_save_options_init(bmain, &opts, ima, NULL, false, false)) {
2384             bool saved_successfully = BKE_image_save(reports, bmain, ima, NULL, &opts);
2385             ok = ok && saved_successfully;
2386           }
2387         }
2388       }
2389     }
2390   }
2391   return ok;
2392 }
2393 
image_save_all_modified_poll(bContext * C)2394 static bool image_save_all_modified_poll(bContext *C)
2395 {
2396   int num_files = ED_image_save_all_modified_info(CTX_data_main(C), NULL);
2397   return num_files > 0;
2398 }
2399 
image_save_all_modified_exec(bContext * C,wmOperator * op)2400 static int image_save_all_modified_exec(bContext *C, wmOperator *op)
2401 {
2402   ED_image_save_all_modified(C, op->reports);
2403   return OPERATOR_FINISHED;
2404 }
2405 
IMAGE_OT_save_all_modified(wmOperatorType * ot)2406 void IMAGE_OT_save_all_modified(wmOperatorType *ot)
2407 {
2408   /* identifiers */
2409   ot->name = "Save All Modified";
2410   ot->idname = "IMAGE_OT_save_all_modified";
2411   ot->description = "Save all modified images";
2412 
2413   /* api callbacks */
2414   ot->exec = image_save_all_modified_exec;
2415   ot->poll = image_save_all_modified_poll;
2416 
2417   /* flags */
2418   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2419 }
2420 
2421 /** \} */
2422 
2423 /* -------------------------------------------------------------------- */
2424 /** \name Reload Image Operator
2425  * \{ */
2426 
image_reload_exec(bContext * C,wmOperator * UNUSED (op))2427 static int image_reload_exec(bContext *C, wmOperator *UNUSED(op))
2428 {
2429   Main *bmain = CTX_data_main(C);
2430   Image *ima = image_from_context(C);
2431   ImageUser *iuser = image_user_from_context(C);
2432 
2433   if (!ima) {
2434     return OPERATOR_CANCELLED;
2435   }
2436 
2437   /* XXX BKE_packedfile_unpack_image frees image buffers */
2438   ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
2439 
2440   BKE_image_signal(bmain, ima, iuser, IMA_SIGNAL_RELOAD);
2441   DEG_id_tag_update(&ima->id, 0);
2442 
2443   WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima);
2444 
2445   return OPERATOR_FINISHED;
2446 }
2447 
IMAGE_OT_reload(wmOperatorType * ot)2448 void IMAGE_OT_reload(wmOperatorType *ot)
2449 {
2450   /* identifiers */
2451   ot->name = "Reload Image";
2452   ot->idname = "IMAGE_OT_reload";
2453   ot->description = "Reload current image from disk";
2454 
2455   /* api callbacks */
2456   ot->exec = image_reload_exec;
2457 
2458   /* flags */
2459   ot->flag = OPTYPE_REGISTER; /* no undo, image buffer is not handled by undo */
2460 }
2461 
2462 /** \} */
2463 
2464 /* -------------------------------------------------------------------- */
2465 /** \name New Image Operator
2466  * \{ */
2467 
2468 #define IMA_DEF_NAME N_("Untitled")
2469 
2470 enum {
2471   GEN_CONTEXT_NONE = 0,
2472   GEN_CONTEXT_PAINT_CANVAS = 1,
2473   GEN_CONTEXT_PAINT_STENCIL = 2,
2474 };
2475 
2476 typedef struct ImageNewData {
2477   PropertyPointerRNA pprop;
2478 } ImageNewData;
2479 
image_new_init(bContext * C,wmOperator * op)2480 static ImageNewData *image_new_init(bContext *C, wmOperator *op)
2481 {
2482   if (op->customdata) {
2483     return op->customdata;
2484   }
2485 
2486   ImageNewData *data = MEM_callocN(sizeof(ImageNewData), __func__);
2487   UI_context_active_but_prop_get_templateID(C, &data->pprop.ptr, &data->pprop.prop);
2488   op->customdata = data;
2489   return data;
2490 }
2491 
image_new_free(wmOperator * op)2492 static void image_new_free(wmOperator *op)
2493 {
2494   if (op->customdata) {
2495     MEM_freeN(op->customdata);
2496     op->customdata = NULL;
2497   }
2498 }
2499 
image_new_exec(bContext * C,wmOperator * op)2500 static int image_new_exec(bContext *C, wmOperator *op)
2501 {
2502   SpaceImage *sima;
2503   Object *obedit;
2504   Image *ima;
2505   Main *bmain;
2506   PropertyRNA *prop;
2507   char name_buffer[MAX_ID_NAME - 2];
2508   const char *name;
2509   float color[4];
2510   int width, height, floatbuf, gen_type, alpha;
2511   int stereo3d;
2512 
2513   /* retrieve state */
2514   sima = CTX_wm_space_image(C);
2515   obedit = CTX_data_edit_object(C);
2516   bmain = CTX_data_main(C);
2517 
2518   prop = RNA_struct_find_property(op->ptr, "name");
2519   RNA_property_string_get(op->ptr, prop, name_buffer);
2520   if (!RNA_property_is_set(op->ptr, prop)) {
2521     /* Default value, we can translate! */
2522     name = DATA_(name_buffer);
2523   }
2524   else {
2525     name = name_buffer;
2526   }
2527   width = RNA_int_get(op->ptr, "width");
2528   height = RNA_int_get(op->ptr, "height");
2529   floatbuf = RNA_boolean_get(op->ptr, "float");
2530   gen_type = RNA_enum_get(op->ptr, "generated_type");
2531   RNA_float_get_array(op->ptr, "color", color);
2532   alpha = RNA_boolean_get(op->ptr, "alpha");
2533   stereo3d = RNA_boolean_get(op->ptr, "use_stereo_3d");
2534   bool tiled = RNA_boolean_get(op->ptr, "tiled");
2535 
2536   if (!alpha) {
2537     color[3] = 1.0f;
2538   }
2539 
2540   ima = BKE_image_add_generated(bmain,
2541                                 width,
2542                                 height,
2543                                 name,
2544                                 alpha ? 32 : 24,
2545                                 floatbuf,
2546                                 gen_type,
2547                                 color,
2548                                 stereo3d,
2549                                 false,
2550                                 tiled);
2551 
2552   if (!ima) {
2553     image_new_free(op);
2554     return OPERATOR_CANCELLED;
2555   }
2556 
2557   /* hook into UI */
2558   ImageNewData *data = image_new_init(C, op);
2559 
2560   if (data->pprop.prop) {
2561     /* when creating new ID blocks, use is already 1, but RNA
2562      * pointer use also increases user, so this compensates it */
2563     id_us_min(&ima->id);
2564 
2565     PointerRNA imaptr;
2566     RNA_id_pointer_create(&ima->id, &imaptr);
2567     RNA_property_pointer_set(&data->pprop.ptr, data->pprop.prop, imaptr, NULL);
2568     RNA_property_update(C, &data->pprop.ptr, data->pprop.prop);
2569   }
2570   else if (sima) {
2571     ED_space_image_set(bmain, sima, obedit, ima, false);
2572   }
2573 
2574   BKE_image_signal(bmain, ima, (sima) ? &sima->iuser : NULL, IMA_SIGNAL_USER_NEW_IMAGE);
2575 
2576   WM_event_add_notifier(C, NC_IMAGE | NA_ADDED, ima);
2577 
2578   image_new_free(op);
2579 
2580   return OPERATOR_FINISHED;
2581 }
2582 
image_new_invoke(bContext * C,wmOperator * op,const wmEvent * UNUSED (event))2583 static int image_new_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
2584 {
2585   /* Get property in advance, it doesn't work after WM_operator_props_dialog_popup. */
2586   ImageNewData *data;
2587   op->customdata = data = MEM_callocN(sizeof(ImageNewData), __func__);
2588   UI_context_active_but_prop_get_templateID(C, &data->pprop.ptr, &data->pprop.prop);
2589 
2590   /* Better for user feedback. */
2591   RNA_string_set(op->ptr, "name", DATA_(IMA_DEF_NAME));
2592   return WM_operator_props_dialog_popup(C, op, 300);
2593 }
2594 
image_new_draw(bContext * UNUSED (C),wmOperator * op)2595 static void image_new_draw(bContext *UNUSED(C), wmOperator *op)
2596 {
2597   uiLayout *col;
2598   uiLayout *layout = op->layout;
2599   PointerRNA ptr;
2600 #if 0
2601   Scene *scene = CTX_data_scene(C);
2602   const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0;
2603 #endif
2604 
2605   RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
2606 
2607   /* copy of WM_operator_props_dialog_popup() layout */
2608 
2609   uiLayoutSetPropSep(layout, true);
2610   uiLayoutSetPropDecorate(layout, false);
2611 
2612   col = uiLayoutColumn(layout, false);
2613   uiItemR(col, &ptr, "name", 0, NULL, ICON_NONE);
2614   uiItemR(col, &ptr, "width", 0, NULL, ICON_NONE);
2615   uiItemR(col, &ptr, "height", 0, NULL, ICON_NONE);
2616   uiItemR(col, &ptr, "color", 0, NULL, ICON_NONE);
2617   uiItemR(col, &ptr, "alpha", 0, NULL, ICON_NONE);
2618   uiItemR(col, &ptr, "generated_type", 0, NULL, ICON_NONE);
2619   uiItemR(col, &ptr, "float", 0, NULL, ICON_NONE);
2620   uiItemR(col, &ptr, "tiled", 0, NULL, ICON_NONE);
2621 
2622 #if 0
2623   if (is_multiview) {
2624     uiItemL(col[0], "", ICON_NONE);
2625     uiItemR(col[1], &ptr, "use_stereo_3d", 0, NULL, ICON_NONE);
2626   }
2627 #endif
2628 }
2629 
image_new_cancel(bContext * UNUSED (C),wmOperator * op)2630 static void image_new_cancel(bContext *UNUSED(C), wmOperator *op)
2631 {
2632   image_new_free(op);
2633 }
2634 
IMAGE_OT_new(wmOperatorType * ot)2635 void IMAGE_OT_new(wmOperatorType *ot)
2636 {
2637   PropertyRNA *prop;
2638   static float default_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
2639 
2640   /* identifiers */
2641   ot->name = "New Image";
2642   ot->description = "Create a new image";
2643   ot->idname = "IMAGE_OT_new";
2644 
2645   /* api callbacks */
2646   ot->exec = image_new_exec;
2647   ot->invoke = image_new_invoke;
2648   ot->ui = image_new_draw;
2649   ot->cancel = image_new_cancel;
2650 
2651   /* flags */
2652   ot->flag = OPTYPE_UNDO;
2653 
2654   /* properties */
2655   RNA_def_string(ot->srna, "name", IMA_DEF_NAME, MAX_ID_NAME - 2, "Name", "Image data-block name");
2656   prop = RNA_def_int(ot->srna, "width", 1024, 1, INT_MAX, "Width", "Image width", 1, 16384);
2657   RNA_def_property_subtype(prop, PROP_PIXEL);
2658   prop = RNA_def_int(ot->srna, "height", 1024, 1, INT_MAX, "Height", "Image height", 1, 16384);
2659   RNA_def_property_subtype(prop, PROP_PIXEL);
2660   prop = RNA_def_float_color(
2661       ot->srna, "color", 4, NULL, 0.0f, FLT_MAX, "Color", "Default fill color", 0.0f, 1.0f);
2662   RNA_def_property_subtype(prop, PROP_COLOR_GAMMA);
2663   RNA_def_property_float_array_default(prop, default_color);
2664   RNA_def_boolean(ot->srna, "alpha", 1, "Alpha", "Create an image with an alpha channel");
2665   RNA_def_enum(ot->srna,
2666                "generated_type",
2667                rna_enum_image_generated_type_items,
2668                IMA_GENTYPE_BLANK,
2669                "Generated Type",
2670                "Fill the image with a grid for UV map testing");
2671   RNA_def_boolean(
2672       ot->srna, "float", 0, "32 bit Float", "Create image with 32 bit floating point bit depth");
2673   RNA_def_property_flag(prop, PROP_HIDDEN);
2674   prop = RNA_def_boolean(
2675       ot->srna, "use_stereo_3d", 0, "Stereo 3D", "Create an image with left and right views");
2676   RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
2677   prop = RNA_def_boolean(ot->srna, "tiled", 0, "Tiled", "Create a tiled image");
2678   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2679 }
2680 
2681 #undef IMA_DEF_NAME
2682 
2683 /** \} */
2684 
2685 /* -------------------------------------------------------------------- */
2686 /** \name Invert Operators
2687  * \{ */
2688 
image_invert_exec(bContext * C,wmOperator * op)2689 static int image_invert_exec(bContext *C, wmOperator *op)
2690 {
2691   Image *ima = image_from_context(C);
2692   ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
2693   SpaceImage *sima = CTX_wm_space_image(C);
2694   const bool is_paint = ((sima != NULL) && (sima->mode == SI_MODE_PAINT));
2695 
2696   /* flags indicate if this channel should be inverted */
2697   const bool r = RNA_boolean_get(op->ptr, "invert_r");
2698   const bool g = RNA_boolean_get(op->ptr, "invert_g");
2699   const bool b = RNA_boolean_get(op->ptr, "invert_b");
2700   const bool a = RNA_boolean_get(op->ptr, "invert_a");
2701 
2702   size_t i;
2703 
2704   if (ibuf == NULL) {
2705     /* TODO: this should actually never happen, but does for render-results -> cleanup */
2706     return OPERATOR_CANCELLED;
2707   }
2708 
2709   ED_image_undo_push_begin_with_image(op->type->name, ima, ibuf, &sima->iuser);
2710 
2711   if (is_paint) {
2712     ED_imapaint_clear_partial_redraw();
2713   }
2714 
2715   /* TODO: make this into an IMB_invert_channels(ibuf,r,g,b,a) method!? */
2716   if (ibuf->rect_float) {
2717 
2718     float *fp = (float *)ibuf->rect_float;
2719     for (i = ((size_t)ibuf->x) * ibuf->y; i > 0; i--, fp += 4) {
2720       if (r) {
2721         fp[0] = 1.0f - fp[0];
2722       }
2723       if (g) {
2724         fp[1] = 1.0f - fp[1];
2725       }
2726       if (b) {
2727         fp[2] = 1.0f - fp[2];
2728       }
2729       if (a) {
2730         fp[3] = 1.0f - fp[3];
2731       }
2732     }
2733 
2734     if (ibuf->rect) {
2735       IMB_rect_from_float(ibuf);
2736     }
2737   }
2738   else if (ibuf->rect) {
2739 
2740     char *cp = (char *)ibuf->rect;
2741     for (i = ((size_t)ibuf->x) * ibuf->y; i > 0; i--, cp += 4) {
2742       if (r) {
2743         cp[0] = 255 - cp[0];
2744       }
2745       if (g) {
2746         cp[1] = 255 - cp[1];
2747       }
2748       if (b) {
2749         cp[2] = 255 - cp[2];
2750       }
2751       if (a) {
2752         cp[3] = 255 - cp[3];
2753       }
2754     }
2755   }
2756   else {
2757     BKE_image_release_ibuf(ima, ibuf, NULL);
2758     return OPERATOR_CANCELLED;
2759   }
2760 
2761   ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
2762   BKE_image_mark_dirty(ima, ibuf);
2763 
2764   if (ibuf->mipmap[0]) {
2765     ibuf->userflags |= IB_MIPMAP_INVALID;
2766   }
2767 
2768   ED_image_undo_push_end();
2769 
2770   /* force GPU reupload, all image is invalid */
2771   BKE_image_free_gputextures(ima);
2772 
2773   WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima);
2774 
2775   BKE_image_release_ibuf(ima, ibuf, NULL);
2776 
2777   return OPERATOR_FINISHED;
2778 }
2779 
IMAGE_OT_invert(wmOperatorType * ot)2780 void IMAGE_OT_invert(wmOperatorType *ot)
2781 {
2782   PropertyRNA *prop;
2783 
2784   /* identifiers */
2785   ot->name = "Invert Channels";
2786   ot->idname = "IMAGE_OT_invert";
2787   ot->description = "Invert image's channels";
2788 
2789   /* api callbacks */
2790   ot->exec = image_invert_exec;
2791   ot->poll = image_from_context_has_data_poll_no_image_user;
2792 
2793   /* properties */
2794   prop = RNA_def_boolean(ot->srna, "invert_r", 0, "Red", "Invert Red Channel");
2795   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2796   prop = RNA_def_boolean(ot->srna, "invert_g", 0, "Green", "Invert Green Channel");
2797   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2798   prop = RNA_def_boolean(ot->srna, "invert_b", 0, "Blue", "Invert Blue Channel");
2799   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2800   prop = RNA_def_boolean(ot->srna, "invert_a", 0, "Alpha", "Invert Alpha Channel");
2801   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2802 
2803   /* flags */
2804   ot->flag = OPTYPE_REGISTER;
2805 }
2806 
2807 /** \} */
2808 
2809 /* -------------------------------------------------------------------- */
2810 /** \name Scale Operator
2811  * \{ */
2812 
image_scale_invoke(bContext * C,wmOperator * op,const wmEvent * UNUSED (event))2813 static int image_scale_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
2814 {
2815   Image *ima = image_from_context(C);
2816   PropertyRNA *prop = RNA_struct_find_property(op->ptr, "size");
2817   if (!RNA_property_is_set(op->ptr, prop)) {
2818     ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
2819     const int size[2] = {ibuf->x, ibuf->y};
2820     RNA_property_int_set_array(op->ptr, prop, size);
2821     BKE_image_release_ibuf(ima, ibuf, NULL);
2822   }
2823   return WM_operator_props_dialog_popup(C, op, 200);
2824 }
2825 
image_scale_exec(bContext * C,wmOperator * op)2826 static int image_scale_exec(bContext *C, wmOperator *op)
2827 {
2828   Image *ima = image_from_context(C);
2829   ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
2830   SpaceImage *sima = CTX_wm_space_image(C);
2831   const bool is_paint = ((sima != NULL) && (sima->mode == SI_MODE_PAINT));
2832 
2833   if (ibuf == NULL) {
2834     /* TODO: this should actually never happen, but does for render-results -> cleanup */
2835     return OPERATOR_CANCELLED;
2836   }
2837 
2838   if (is_paint) {
2839     ED_imapaint_clear_partial_redraw();
2840   }
2841 
2842   PropertyRNA *prop = RNA_struct_find_property(op->ptr, "size");
2843   int size[2];
2844   if (RNA_property_is_set(op->ptr, prop)) {
2845     RNA_property_int_get_array(op->ptr, prop, size);
2846   }
2847   else {
2848     size[0] = ibuf->x;
2849     size[1] = ibuf->y;
2850     RNA_property_int_set_array(op->ptr, prop, size);
2851   }
2852 
2853   ED_image_undo_push_begin_with_image(op->type->name, ima, ibuf, &sima->iuser);
2854 
2855   ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
2856   IMB_scaleImBuf(ibuf, size[0], size[1]);
2857   BKE_image_release_ibuf(ima, ibuf, NULL);
2858 
2859   ED_image_undo_push_end();
2860 
2861   /* force GPU reupload, all image is invalid */
2862   BKE_image_free_gputextures(ima);
2863 
2864   DEG_id_tag_update(&ima->id, 0);
2865   WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima);
2866 
2867   return OPERATOR_FINISHED;
2868 }
2869 
IMAGE_OT_resize(wmOperatorType * ot)2870 void IMAGE_OT_resize(wmOperatorType *ot)
2871 {
2872   /* identifiers */
2873   ot->name = "Resize Image";
2874   ot->idname = "IMAGE_OT_resize";
2875   ot->description = "Resize the image";
2876 
2877   /* api callbacks */
2878   ot->invoke = image_scale_invoke;
2879   ot->exec = image_scale_exec;
2880   ot->poll = image_from_context_has_data_poll_no_image_user;
2881 
2882   /* properties */
2883   RNA_def_int_vector(ot->srna, "size", 2, NULL, 1, INT_MAX, "Size", "", 1, SHRT_MAX);
2884 
2885   /* flags */
2886   ot->flag = OPTYPE_REGISTER;
2887 }
2888 
2889 /** \} */
2890 
2891 /* -------------------------------------------------------------------- */
2892 /** \name Pack Operator
2893  * \{ */
2894 
image_pack_test(bContext * C,wmOperator * op)2895 static bool image_pack_test(bContext *C, wmOperator *op)
2896 {
2897   Image *ima = image_from_context(C);
2898 
2899   if (!ima) {
2900     return false;
2901   }
2902 
2903   if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE, IMA_SRC_TILED)) {
2904     BKE_report(
2905         op->reports, RPT_ERROR, "Packing movies, image sequences or tiled images not supported");
2906     return false;
2907   }
2908 
2909   return true;
2910 }
2911 
image_pack_exec(bContext * C,wmOperator * op)2912 static int image_pack_exec(bContext *C, wmOperator *op)
2913 {
2914   struct Main *bmain = CTX_data_main(C);
2915   Image *ima = image_from_context(C);
2916 
2917   if (!image_pack_test(C, op)) {
2918     return OPERATOR_CANCELLED;
2919   }
2920 
2921   if (BKE_image_is_dirty(ima)) {
2922     BKE_image_memorypack(ima);
2923   }
2924   else {
2925     BKE_image_packfiles(op->reports, ima, ID_BLEND_PATH(bmain, &ima->id));
2926   }
2927 
2928   WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima);
2929 
2930   return OPERATOR_FINISHED;
2931 }
2932 
IMAGE_OT_pack(wmOperatorType * ot)2933 void IMAGE_OT_pack(wmOperatorType *ot)
2934 {
2935   /* identifiers */
2936   ot->name = "Pack Image";
2937   ot->description = "Pack an image as embedded data into the .blend file";
2938   ot->idname = "IMAGE_OT_pack";
2939 
2940   /* api callbacks */
2941   ot->exec = image_pack_exec;
2942 
2943   /* flags */
2944   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2945 }
2946 
2947 /** \} */
2948 
2949 /* -------------------------------------------------------------------- */
2950 /** \name Unpack Operator
2951  * \{ */
2952 
image_unpack_exec(bContext * C,wmOperator * op)2953 static int image_unpack_exec(bContext *C, wmOperator *op)
2954 {
2955   Main *bmain = CTX_data_main(C);
2956   Image *ima = image_from_context(C);
2957   int method = RNA_enum_get(op->ptr, "method");
2958 
2959   /* find the supplied image by name */
2960   if (RNA_struct_property_is_set(op->ptr, "id")) {
2961     char imaname[MAX_ID_NAME - 2];
2962     RNA_string_get(op->ptr, "id", imaname);
2963     ima = BLI_findstring(&bmain->images, imaname, offsetof(ID, name) + 2);
2964     if (!ima) {
2965       ima = image_from_context(C);
2966     }
2967   }
2968 
2969   if (!ima || !BKE_image_has_packedfile(ima)) {
2970     return OPERATOR_CANCELLED;
2971   }
2972 
2973   if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE, IMA_SRC_TILED)) {
2974     BKE_report(
2975         op->reports, RPT_ERROR, "Unpacking movies, image sequences or tiled images not supported");
2976     return OPERATOR_CANCELLED;
2977   }
2978 
2979   if (G.fileflags & G_FILE_AUTOPACK) {
2980     BKE_report(op->reports,
2981                RPT_WARNING,
2982                "AutoPack is enabled, so image will be packed again on file save");
2983   }
2984 
2985   /* XXX BKE_packedfile_unpack_image frees image buffers */
2986   ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
2987 
2988   BKE_packedfile_unpack_image(CTX_data_main(C), op->reports, ima, method);
2989 
2990   WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima);
2991 
2992   return OPERATOR_FINISHED;
2993 }
2994 
image_unpack_invoke(bContext * C,wmOperator * op,const wmEvent * UNUSED (event))2995 static int image_unpack_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
2996 {
2997   Image *ima = image_from_context(C);
2998 
2999   if (RNA_struct_property_is_set(op->ptr, "id")) {
3000     return image_unpack_exec(C, op);
3001   }
3002 
3003   if (!ima || !BKE_image_has_packedfile(ima)) {
3004     return OPERATOR_CANCELLED;
3005   }
3006 
3007   if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE, IMA_SRC_TILED)) {
3008     BKE_report(
3009         op->reports, RPT_ERROR, "Unpacking movies, image sequences or tiled images not supported");
3010     return OPERATOR_CANCELLED;
3011   }
3012 
3013   if (G.fileflags & G_FILE_AUTOPACK) {
3014     BKE_report(op->reports,
3015                RPT_WARNING,
3016                "AutoPack is enabled, so image will be packed again on file save");
3017   }
3018 
3019   unpack_menu(C,
3020               "IMAGE_OT_unpack",
3021               ima->id.name + 2,
3022               ima->filepath,
3023               "textures",
3024               BKE_image_has_packedfile(ima) ?
3025                   ((ImagePackedFile *)ima->packedfiles.first)->packedfile :
3026                   NULL);
3027 
3028   return OPERATOR_FINISHED;
3029 }
3030 
IMAGE_OT_unpack(wmOperatorType * ot)3031 void IMAGE_OT_unpack(wmOperatorType *ot)
3032 {
3033   /* identifiers */
3034   ot->name = "Unpack Image";
3035   ot->description = "Save an image packed in the .blend file to disk";
3036   ot->idname = "IMAGE_OT_unpack";
3037 
3038   /* api callbacks */
3039   ot->exec = image_unpack_exec;
3040   ot->invoke = image_unpack_invoke;
3041 
3042   /* flags */
3043   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3044 
3045   /* properties */
3046   RNA_def_enum(
3047       ot->srna, "method", rna_enum_unpack_method_items, PF_USE_LOCAL, "Method", "How to unpack");
3048   /* XXX, weak!, will fail with library, name collisions */
3049   RNA_def_string(
3050       ot->srna, "id", NULL, MAX_ID_NAME - 2, "Image Name", "Image data-block name to unpack");
3051 }
3052 
3053 /** \} */
3054 
3055 /* -------------------------------------------------------------------- */
3056 /** \name Sample Image Operator
3057  * \{ */
3058 
3059 /* Returns color in linear space, matching ED_space_node_color_sample(). */
ED_space_image_color_sample(SpaceImage * sima,ARegion * region,int mval[2],float r_col[3],bool * r_is_data)3060 bool ED_space_image_color_sample(
3061     SpaceImage *sima, ARegion *region, int mval[2], float r_col[3], bool *r_is_data)
3062 {
3063   if (r_is_data) {
3064     *r_is_data = false;
3065   }
3066   if (sima->image == NULL) {
3067     return false;
3068   }
3069   float uv[2];
3070   UI_view2d_region_to_view(&region->v2d, mval[0], mval[1], &uv[0], &uv[1]);
3071   int tile = BKE_image_get_tile_from_pos(sima->image, uv, uv, NULL);
3072 
3073   void *lock;
3074   ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock, tile);
3075   bool ret = false;
3076 
3077   if (ibuf == NULL) {
3078     ED_space_image_release_buffer(sima, ibuf, lock);
3079     return false;
3080   }
3081 
3082   if (uv[0] >= 0.0f && uv[1] >= 0.0f && uv[0] < 1.0f && uv[1] < 1.0f) {
3083     const float *fp;
3084     uchar *cp;
3085     int x = (int)(uv[0] * ibuf->x), y = (int)(uv[1] * ibuf->y);
3086 
3087     CLAMP(x, 0, ibuf->x - 1);
3088     CLAMP(y, 0, ibuf->y - 1);
3089 
3090     if (ibuf->rect_float) {
3091       fp = (ibuf->rect_float + (ibuf->channels) * (y * ibuf->x + x));
3092       copy_v3_v3(r_col, fp);
3093       ret = true;
3094     }
3095     else if (ibuf->rect) {
3096       cp = (uchar *)(ibuf->rect + y * ibuf->x + x);
3097       rgb_uchar_to_float(r_col, cp);
3098       IMB_colormanagement_colorspace_to_scene_linear_v3(r_col, ibuf->rect_colorspace);
3099       ret = true;
3100     }
3101   }
3102 
3103   if (r_is_data) {
3104     *r_is_data = (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA) != 0;
3105   }
3106 
3107   ED_space_image_release_buffer(sima, ibuf, lock);
3108   return ret;
3109 }
3110 
IMAGE_OT_sample(wmOperatorType * ot)3111 void IMAGE_OT_sample(wmOperatorType *ot)
3112 {
3113   /* identifiers */
3114   ot->name = "Sample Color";
3115   ot->idname = "IMAGE_OT_sample";
3116   ot->description = "Use mouse to sample a color in current image";
3117 
3118   /* api callbacks */
3119   ot->invoke = ED_imbuf_sample_invoke;
3120   ot->modal = ED_imbuf_sample_modal;
3121   ot->cancel = ED_imbuf_sample_cancel;
3122   ot->poll = ED_imbuf_sample_poll;
3123 
3124   /* flags */
3125   ot->flag = OPTYPE_BLOCKING;
3126 
3127   PropertyRNA *prop;
3128   prop = RNA_def_int(ot->srna, "size", 1, 1, 128, "Sample Size", "", 1, 64);
3129   RNA_def_property_subtype(prop, PROP_PIXEL);
3130   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
3131 }
3132 
3133 /** \} */
3134 
3135 /* -------------------------------------------------------------------- */
3136 /** \name Sample Line Operator
3137  * \{ */
3138 
image_sample_line_exec(bContext * C,wmOperator * op)3139 static int image_sample_line_exec(bContext *C, wmOperator *op)
3140 {
3141   SpaceImage *sima = CTX_wm_space_image(C);
3142   ARegion *region = CTX_wm_region(C);
3143   Scene *scene = CTX_data_scene(C);
3144   Image *ima = ED_space_image(sima);
3145 
3146   int x_start = RNA_int_get(op->ptr, "xstart");
3147   int y_start = RNA_int_get(op->ptr, "ystart");
3148   int x_end = RNA_int_get(op->ptr, "xend");
3149   int y_end = RNA_int_get(op->ptr, "yend");
3150 
3151   float uv1[2], uv2[2], ofs[2];
3152   UI_view2d_region_to_view(&region->v2d, x_start, y_start, &uv1[0], &uv1[1]);
3153   UI_view2d_region_to_view(&region->v2d, x_end, y_end, &uv2[0], &uv2[1]);
3154 
3155   /* If the image has tiles, shift the positions accordingly. */
3156   int tile = BKE_image_get_tile_from_pos(ima, uv1, uv1, ofs);
3157   sub_v2_v2(uv2, ofs);
3158 
3159   void *lock;
3160   ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock, tile);
3161   Histogram *hist = &sima->sample_line_hist;
3162 
3163   if (ibuf == NULL) {
3164     ED_space_image_release_buffer(sima, ibuf, lock);
3165     return OPERATOR_CANCELLED;
3166   }
3167   /* hmmmm */
3168   if (ibuf->channels < 3) {
3169     ED_space_image_release_buffer(sima, ibuf, lock);
3170     return OPERATOR_CANCELLED;
3171   }
3172 
3173   copy_v2_v2(hist->co[0], uv1);
3174   copy_v2_v2(hist->co[1], uv2);
3175 
3176   /* enable line drawing */
3177   hist->flag |= HISTO_FLAG_SAMPLELINE;
3178 
3179   BKE_histogram_update_sample_line(hist, ibuf, &scene->view_settings, &scene->display_settings);
3180 
3181   /* reset y zoom */
3182   hist->ymax = 1.0f;
3183 
3184   ED_space_image_release_buffer(sima, ibuf, lock);
3185 
3186   ED_area_tag_redraw(CTX_wm_area(C));
3187 
3188   return OPERATOR_FINISHED;
3189 }
3190 
image_sample_line_invoke(bContext * C,wmOperator * op,const wmEvent * event)3191 static int image_sample_line_invoke(bContext *C, wmOperator *op, const wmEvent *event)
3192 {
3193   SpaceImage *sima = CTX_wm_space_image(C);
3194 
3195   Histogram *hist = &sima->sample_line_hist;
3196   hist->flag &= ~HISTO_FLAG_SAMPLELINE;
3197 
3198   if (!ED_space_image_has_buffer(sima)) {
3199     return OPERATOR_CANCELLED;
3200   }
3201 
3202   return WM_gesture_straightline_invoke(C, op, event);
3203 }
3204 
IMAGE_OT_sample_line(wmOperatorType * ot)3205 void IMAGE_OT_sample_line(wmOperatorType *ot)
3206 {
3207   /* identifiers */
3208   ot->name = "Sample Line";
3209   ot->idname = "IMAGE_OT_sample_line";
3210   ot->description = "Sample a line and show it in Scope panels";
3211 
3212   /* api callbacks */
3213   ot->invoke = image_sample_line_invoke;
3214   ot->modal = WM_gesture_straightline_modal;
3215   ot->exec = image_sample_line_exec;
3216   ot->poll = space_image_main_region_poll;
3217   ot->cancel = WM_gesture_straightline_cancel;
3218 
3219   /* flags */
3220   ot->flag = 0; /* no undo/register since this operates on the space */
3221 
3222   WM_operator_properties_gesture_straightline(ot, WM_CURSOR_EDIT);
3223 }
3224 
3225 /** \} */
3226 
3227 /* -------------------------------------------------------------------- */
3228 /** \name Set Curve Point Operator
3229  * \{ */
3230 
IMAGE_OT_curves_point_set(wmOperatorType * ot)3231 void IMAGE_OT_curves_point_set(wmOperatorType *ot)
3232 {
3233   static const EnumPropertyItem point_items[] = {
3234       {0, "BLACK_POINT", 0, "Black Point", ""},
3235       {1, "WHITE_POINT", 0, "White Point", ""},
3236       {0, NULL, 0, NULL, NULL},
3237   };
3238 
3239   /* identifiers */
3240   ot->name = "Set Curves Point";
3241   ot->idname = "IMAGE_OT_curves_point_set";
3242   ot->description = "Set black point or white point for curves";
3243 
3244   /* flags */
3245   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3246 
3247   /* api callbacks */
3248   ot->invoke = ED_imbuf_sample_invoke;
3249   ot->modal = ED_imbuf_sample_modal;
3250   ot->cancel = ED_imbuf_sample_cancel;
3251   ot->poll = space_image_main_area_not_uv_brush_poll;
3252 
3253   /* properties */
3254   RNA_def_enum(
3255       ot->srna, "point", point_items, 0, "Point", "Set black point or white point for curves");
3256 
3257   PropertyRNA *prop;
3258   prop = RNA_def_int(ot->srna, "size", 1, 1, 128, "Sample Size", "", 1, 64);
3259   RNA_def_property_subtype(prop, PROP_PIXEL);
3260   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
3261 }
3262 
3263 /** \} */
3264 
3265 /* -------------------------------------------------------------------- */
3266 /** \name Cycle Render Slot Operator
3267  * \{ */
3268 
image_cycle_render_slot_poll(bContext * C)3269 static bool image_cycle_render_slot_poll(bContext *C)
3270 {
3271   Image *ima = image_from_context(C);
3272 
3273   return (ima && ima->type == IMA_TYPE_R_RESULT);
3274 }
3275 
image_cycle_render_slot_exec(bContext * C,wmOperator * op)3276 static int image_cycle_render_slot_exec(bContext *C, wmOperator *op)
3277 {
3278   Image *ima = image_from_context(C);
3279   const int direction = RNA_boolean_get(op->ptr, "reverse") ? -1 : 1;
3280 
3281   if (!ED_image_slot_cycle(ima, direction)) {
3282     return OPERATOR_CANCELLED;
3283   }
3284 
3285   WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL);
3286 
3287   /* no undo push for browsing existing */
3288   RenderSlot *slot = BKE_image_get_renderslot(ima, ima->render_slot);
3289   if ((slot && slot->render) || ima->render_slot == ima->last_render_slot) {
3290     return OPERATOR_CANCELLED;
3291   }
3292 
3293   return OPERATOR_FINISHED;
3294 }
3295 
IMAGE_OT_cycle_render_slot(wmOperatorType * ot)3296 void IMAGE_OT_cycle_render_slot(wmOperatorType *ot)
3297 {
3298   /* identifiers */
3299   ot->name = "Cycle Render Slot";
3300   ot->idname = "IMAGE_OT_cycle_render_slot";
3301   ot->description = "Cycle through all non-void render slots";
3302 
3303   /* api callbacks */
3304   ot->exec = image_cycle_render_slot_exec;
3305   ot->poll = image_cycle_render_slot_poll;
3306 
3307   /* flags */
3308   ot->flag = OPTYPE_REGISTER;
3309 
3310   RNA_def_boolean(ot->srna, "reverse", 0, "Cycle in Reverse", "");
3311 }
3312 
3313 /** \} */
3314 
3315 /* -------------------------------------------------------------------- */
3316 /** \name Clear Render Slot Operator
3317  * \{ */
3318 
image_clear_render_slot_exec(bContext * C,wmOperator * UNUSED (op))3319 static int image_clear_render_slot_exec(bContext *C, wmOperator *UNUSED(op))
3320 {
3321   SpaceImage *sima = CTX_wm_space_image(C);
3322   Image *ima = image_from_context(C);
3323 
3324   if (!BKE_image_clear_renderslot(ima, &sima->iuser, ima->render_slot)) {
3325     return OPERATOR_CANCELLED;
3326   }
3327 
3328   WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL);
3329 
3330   return OPERATOR_FINISHED;
3331 }
3332 
IMAGE_OT_clear_render_slot(wmOperatorType * ot)3333 void IMAGE_OT_clear_render_slot(wmOperatorType *ot)
3334 {
3335   /* identifiers */
3336   ot->name = "Clear Render Slot";
3337   ot->idname = "IMAGE_OT_clear_render_slot";
3338   ot->description = "Clear the currently selected render slot";
3339 
3340   /* api callbacks */
3341   ot->exec = image_clear_render_slot_exec;
3342   ot->poll = image_cycle_render_slot_poll;
3343 
3344   /* flags */
3345   ot->flag = OPTYPE_REGISTER;
3346 }
3347 
3348 /** \} */
3349 
3350 /* -------------------------------------------------------------------- */
3351 /** \name Add Render Slot Operator
3352  * \{ */
3353 
image_add_render_slot_exec(bContext * C,wmOperator * UNUSED (op))3354 static int image_add_render_slot_exec(bContext *C, wmOperator *UNUSED(op))
3355 {
3356   Image *ima = image_from_context(C);
3357 
3358   RenderSlot *slot = BKE_image_add_renderslot(ima, NULL);
3359   ima->render_slot = BLI_findindex(&ima->renderslots, slot);
3360 
3361   WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL);
3362 
3363   return OPERATOR_FINISHED;
3364 }
3365 
IMAGE_OT_add_render_slot(wmOperatorType * ot)3366 void IMAGE_OT_add_render_slot(wmOperatorType *ot)
3367 {
3368   /* identifiers */
3369   ot->name = "Add Render Slot";
3370   ot->idname = "IMAGE_OT_add_render_slot";
3371   ot->description = "Add a new render slot";
3372 
3373   /* api callbacks */
3374   ot->exec = image_add_render_slot_exec;
3375   ot->poll = image_cycle_render_slot_poll;
3376 
3377   /* flags */
3378   ot->flag = OPTYPE_REGISTER;
3379 }
3380 
3381 /** \} */
3382 
3383 /* -------------------------------------------------------------------- */
3384 /** \name Remove Render Slot Operator
3385  * \{ */
3386 
image_remove_render_slot_exec(bContext * C,wmOperator * UNUSED (op))3387 static int image_remove_render_slot_exec(bContext *C, wmOperator *UNUSED(op))
3388 {
3389   SpaceImage *sima = CTX_wm_space_image(C);
3390   Image *ima = image_from_context(C);
3391 
3392   if (!BKE_image_remove_renderslot(ima, &sima->iuser, ima->render_slot)) {
3393     return OPERATOR_CANCELLED;
3394   }
3395 
3396   WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL);
3397 
3398   return OPERATOR_FINISHED;
3399 }
3400 
IMAGE_OT_remove_render_slot(wmOperatorType * ot)3401 void IMAGE_OT_remove_render_slot(wmOperatorType *ot)
3402 {
3403   /* identifiers */
3404   ot->name = "Remove Render Slot";
3405   ot->idname = "IMAGE_OT_remove_render_slot";
3406   ot->description = "Remove the current render slot";
3407 
3408   /* api callbacks */
3409   ot->exec = image_remove_render_slot_exec;
3410   ot->poll = image_cycle_render_slot_poll;
3411 
3412   /* flags */
3413   ot->flag = OPTYPE_REGISTER;
3414 }
3415 
3416 /** \} */
3417 
3418 /* -------------------------------------------------------------------- */
3419 /** \name Change Frame Operator
3420  * \{ */
3421 
change_frame_poll(bContext * C)3422 static bool change_frame_poll(bContext *C)
3423 {
3424   /* prevent changes during render */
3425   if (G.is_rendering) {
3426     return 0;
3427   }
3428 
3429   return space_image_main_region_poll(C);
3430 }
3431 
change_frame_apply(bContext * C,wmOperator * op)3432 static void change_frame_apply(bContext *C, wmOperator *op)
3433 {
3434   Scene *scene = CTX_data_scene(C);
3435 
3436   /* set the new frame number */
3437   CFRA = RNA_int_get(op->ptr, "frame");
3438   FRAMENUMBER_MIN_CLAMP(CFRA);
3439   SUBFRA = 0.0f;
3440 
3441   /* do updates */
3442   DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_SEEK);
3443   WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
3444 }
3445 
change_frame_exec(bContext * C,wmOperator * op)3446 static int change_frame_exec(bContext *C, wmOperator *op)
3447 {
3448   change_frame_apply(C, op);
3449 
3450   return OPERATOR_FINISHED;
3451 }
3452 
frame_from_event(bContext * C,const wmEvent * event)3453 static int frame_from_event(bContext *C, const wmEvent *event)
3454 {
3455   ARegion *region = CTX_wm_region(C);
3456   Scene *scene = CTX_data_scene(C);
3457   int framenr = 0;
3458 
3459   if (region->regiontype == RGN_TYPE_WINDOW) {
3460     float sfra = SFRA, efra = EFRA, framelen = region->winx / (efra - sfra + 1);
3461 
3462     framenr = sfra + event->mval[0] / framelen;
3463   }
3464   else {
3465     float viewx, viewy;
3466 
3467     UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &viewx, &viewy);
3468 
3469     framenr = round_fl_to_int(viewx);
3470   }
3471 
3472   return framenr;
3473 }
3474 
change_frame_invoke(bContext * C,wmOperator * op,const wmEvent * event)3475 static int change_frame_invoke(bContext *C, wmOperator *op, const wmEvent *event)
3476 {
3477   ARegion *region = CTX_wm_region(C);
3478 
3479   if (region->regiontype == RGN_TYPE_WINDOW) {
3480     SpaceImage *sima = CTX_wm_space_image(C);
3481 
3482     /* Local coordinate visible rect inside region, to accommodate overlapping ui. */
3483     const rcti *rect_visible = ED_region_visible_rect(region);
3484     const int region_bottom = rect_visible->ymin;
3485 
3486     if (event->mval[1] > (region_bottom + 16 * UI_DPI_FAC) || !ED_space_image_show_cache(sima)) {
3487       return OPERATOR_PASS_THROUGH;
3488     }
3489   }
3490 
3491   RNA_int_set(op->ptr, "frame", frame_from_event(C, event));
3492 
3493   change_frame_apply(C, op);
3494 
3495   /* add temp handler */
3496   WM_event_add_modal_handler(C, op);
3497 
3498   return OPERATOR_RUNNING_MODAL;
3499 }
3500 
change_frame_modal(bContext * C,wmOperator * op,const wmEvent * event)3501 static int change_frame_modal(bContext *C, wmOperator *op, const wmEvent *event)
3502 {
3503   switch (event->type) {
3504     case EVT_ESCKEY:
3505       return OPERATOR_FINISHED;
3506 
3507     case MOUSEMOVE:
3508       RNA_int_set(op->ptr, "frame", frame_from_event(C, event));
3509       change_frame_apply(C, op);
3510       break;
3511 
3512     case LEFTMOUSE:
3513     case RIGHTMOUSE:
3514       if (event->val == KM_RELEASE) {
3515         return OPERATOR_FINISHED;
3516       }
3517       break;
3518   }
3519 
3520   return OPERATOR_RUNNING_MODAL;
3521 }
3522 
IMAGE_OT_change_frame(wmOperatorType * ot)3523 void IMAGE_OT_change_frame(wmOperatorType *ot)
3524 {
3525   /* identifiers */
3526   ot->name = "Change Frame";
3527   ot->idname = "IMAGE_OT_change_frame";
3528   ot->description = "Interactively change the current frame number";
3529 
3530   /* api callbacks */
3531   ot->exec = change_frame_exec;
3532   ot->invoke = change_frame_invoke;
3533   ot->modal = change_frame_modal;
3534   ot->poll = change_frame_poll;
3535 
3536   /* flags */
3537   ot->flag = OPTYPE_BLOCKING | OPTYPE_UNDO;
3538 
3539   /* rna */
3540   RNA_def_int(ot->srna, "frame", 0, MINAFRAME, MAXFRAME, "Frame", "", MINAFRAME, MAXFRAME);
3541 }
3542 
3543 /* Reload cached render results... */
3544 /* goes over all scenes, reads render layers */
image_read_viewlayers_exec(bContext * C,wmOperator * UNUSED (op))3545 static int image_read_viewlayers_exec(bContext *C, wmOperator *UNUSED(op))
3546 {
3547   Main *bmain = CTX_data_main(C);
3548   Scene *scene = CTX_data_scene(C);
3549   SpaceImage *sima = CTX_wm_space_image(C);
3550   Image *ima;
3551 
3552   ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_R_RESULT, "Render Result");
3553   if (sima->image == NULL) {
3554     ED_space_image_set(bmain, sima, NULL, ima, false);
3555   }
3556 
3557   RE_ReadRenderResult(scene, scene);
3558 
3559   WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima);
3560   return OPERATOR_FINISHED;
3561 }
3562 
IMAGE_OT_read_viewlayers(wmOperatorType * ot)3563 void IMAGE_OT_read_viewlayers(wmOperatorType *ot)
3564 {
3565   ot->name = "Open Cached Render";
3566   ot->idname = "IMAGE_OT_read_viewlayers";
3567   ot->description = "Read all the current scene's view layers from cache, as needed";
3568 
3569   ot->poll = space_image_main_region_poll;
3570   ot->exec = image_read_viewlayers_exec;
3571 
3572   /* flags */
3573   ot->flag = 0;
3574 }
3575 
3576 /** \} */
3577 
3578 /* -------------------------------------------------------------------- */
3579 /** \name Render Border Operator
3580  * \{ */
3581 
render_border_exec(bContext * C,wmOperator * op)3582 static int render_border_exec(bContext *C, wmOperator *op)
3583 {
3584   ARegion *region = CTX_wm_region(C);
3585   Scene *scene = CTX_data_scene(C);
3586   Render *re = RE_GetSceneRender(scene);
3587   RenderData *rd;
3588   rctf border;
3589 
3590   if (re == NULL) {
3591     /* Shouldn't happen, but better be safe close to the release. */
3592     return OPERATOR_CANCELLED;
3593   }
3594 
3595   rd = RE_engine_get_render_data(re);
3596   if ((rd->mode & (R_BORDER | R_CROP)) == (R_BORDER | R_CROP)) {
3597     BKE_report(op->reports, RPT_INFO, "Can not set border from a cropped render");
3598     return OPERATOR_CANCELLED;
3599   }
3600 
3601   /* get rectangle from operator */
3602   WM_operator_properties_border_to_rctf(op, &border);
3603   UI_view2d_region_to_view_rctf(&region->v2d, &border, &border);
3604 
3605   /* actually set border */
3606   CLAMP(border.xmin, 0.0f, 1.0f);
3607   CLAMP(border.ymin, 0.0f, 1.0f);
3608   CLAMP(border.xmax, 0.0f, 1.0f);
3609   CLAMP(border.ymax, 0.0f, 1.0f);
3610   scene->r.border = border;
3611 
3612   /* drawing a border surrounding the entire camera view switches off border rendering
3613    * or the border covers no pixels */
3614   if ((border.xmin <= 0.0f && border.xmax >= 1.0f && border.ymin <= 0.0f && border.ymax >= 1.0f) ||
3615       (border.xmin == border.xmax || border.ymin == border.ymax)) {
3616     scene->r.mode &= ~R_BORDER;
3617   }
3618   else {
3619     scene->r.mode |= R_BORDER;
3620   }
3621 
3622   DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
3623   WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, NULL);
3624 
3625   return OPERATOR_FINISHED;
3626 }
3627 
IMAGE_OT_render_border(wmOperatorType * ot)3628 void IMAGE_OT_render_border(wmOperatorType *ot)
3629 {
3630   /* identifiers */
3631   ot->name = "Render Region";
3632   ot->description = "Set the boundaries of the render region and enable render region";
3633   ot->idname = "IMAGE_OT_render_border";
3634 
3635   /* api callbacks */
3636   ot->invoke = WM_gesture_box_invoke;
3637   ot->exec = render_border_exec;
3638   ot->modal = WM_gesture_box_modal;
3639   ot->cancel = WM_gesture_box_cancel;
3640   ot->poll = image_cycle_render_slot_poll;
3641 
3642   /* flags */
3643   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3644 
3645   /* rna */
3646   WM_operator_properties_border(ot);
3647 }
3648 
3649 /** \} */
3650 
3651 /* -------------------------------------------------------------------- */
3652 /** \name Clear Render Border Operator
3653  * \{ */
3654 
clear_render_border_exec(bContext * C,wmOperator * UNUSED (op))3655 static int clear_render_border_exec(bContext *C, wmOperator *UNUSED(op))
3656 {
3657   Scene *scene = CTX_data_scene(C);
3658   scene->r.mode &= ~R_BORDER;
3659   WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, NULL);
3660   BLI_rctf_init(&scene->r.border, 0.0f, 1.0f, 0.0f, 1.0f);
3661   return OPERATOR_FINISHED;
3662 }
3663 
IMAGE_OT_clear_render_border(wmOperatorType * ot)3664 void IMAGE_OT_clear_render_border(wmOperatorType *ot)
3665 {
3666   /* identifiers */
3667   ot->name = "Clear Render Region";
3668   ot->description = "Clear the boundaries of the render region and disable render region";
3669   ot->idname = "IMAGE_OT_clear_render_border";
3670 
3671   /* api callbacks */
3672   ot->exec = clear_render_border_exec;
3673   ot->poll = image_cycle_render_slot_poll;
3674 
3675   /* flags */
3676   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3677 }
3678 
3679 /** \} */
3680 
3681 /* -------------------------------------------------------------------- */
3682 /** \name Add Tile Operator
3683  * \{ */
3684 
do_fill_tile(PointerRNA * ptr,Image * ima,ImageTile * tile)3685 static bool do_fill_tile(PointerRNA *ptr, Image *ima, ImageTile *tile)
3686 {
3687   float color[4];
3688   RNA_float_get_array(ptr, "color", color);
3689   int gen_type = RNA_enum_get(ptr, "generated_type");
3690   int width = RNA_int_get(ptr, "width");
3691   int height = RNA_int_get(ptr, "height");
3692   bool is_float = RNA_boolean_get(ptr, "float");
3693   int planes = RNA_boolean_get(ptr, "alpha") ? 32 : 24;
3694 
3695   return BKE_image_fill_tile(ima, tile, width, height, color, gen_type, planes, is_float);
3696 }
3697 
draw_fill_tile(PointerRNA * ptr,uiLayout * layout)3698 static void draw_fill_tile(PointerRNA *ptr, uiLayout *layout)
3699 {
3700   uiLayoutSetPropSep(layout, true);
3701   uiLayoutSetPropDecorate(layout, false);
3702 
3703   uiLayout *col = uiLayoutColumn(layout, false);
3704   uiItemR(col, ptr, "color", 0, NULL, ICON_NONE);
3705   uiItemR(col, ptr, "width", 0, NULL, ICON_NONE);
3706   uiItemR(col, ptr, "height", 0, NULL, ICON_NONE);
3707   uiItemR(col, ptr, "alpha", 0, NULL, ICON_NONE);
3708   uiItemR(col, ptr, "generated_type", 0, NULL, ICON_NONE);
3709   uiItemR(col, ptr, "float", 0, NULL, ICON_NONE);
3710 }
3711 
tile_fill_init(PointerRNA * ptr,Image * ima,ImageTile * tile)3712 static void tile_fill_init(PointerRNA *ptr, Image *ima, ImageTile *tile)
3713 {
3714   ImageUser iuser;
3715   BKE_imageuser_default(&iuser);
3716   if (tile != NULL) {
3717     iuser.tile = tile->tile_number;
3718   }
3719 
3720   /* Acquire ibuf to get the default values.
3721    * If the specified tile has no ibuf, try acquiring the main tile instead
3722    * (unless the specified tile already was the main tile).*/
3723   ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL);
3724   if (ibuf == NULL && (tile != NULL) && (tile->tile_number != 1001)) {
3725     ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
3726   }
3727 
3728   if (ibuf != NULL) {
3729     /* Initialize properties from reference tile. */
3730     RNA_int_set(ptr, "width", ibuf->x);
3731     RNA_int_set(ptr, "height", ibuf->y);
3732     RNA_boolean_set(ptr, "float", ibuf->rect_float != NULL);
3733     RNA_boolean_set(ptr, "alpha", ibuf->planes > 24);
3734 
3735     BKE_image_release_ibuf(ima, ibuf, NULL);
3736   }
3737 }
3738 
def_fill_tile(StructOrFunctionRNA * srna)3739 static void def_fill_tile(StructOrFunctionRNA *srna)
3740 {
3741   PropertyRNA *prop;
3742   static float default_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
3743   prop = RNA_def_float_color(
3744       srna, "color", 4, NULL, 0.0f, FLT_MAX, "Color", "Default fill color", 0.0f, 1.0f);
3745   RNA_def_property_subtype(prop, PROP_COLOR_GAMMA);
3746   RNA_def_property_float_array_default(prop, default_color);
3747   RNA_def_enum(srna,
3748                "generated_type",
3749                rna_enum_image_generated_type_items,
3750                IMA_GENTYPE_BLANK,
3751                "Generated Type",
3752                "Fill the image with a grid for UV map testing");
3753   prop = RNA_def_int(srna, "width", 1024, 1, INT_MAX, "Width", "Image width", 1, 16384);
3754   RNA_def_property_subtype(prop, PROP_PIXEL);
3755   prop = RNA_def_int(srna, "height", 1024, 1, INT_MAX, "Height", "Image height", 1, 16384);
3756   RNA_def_property_subtype(prop, PROP_PIXEL);
3757 
3758   /* Only needed when filling the first tile. */
3759   RNA_def_boolean(
3760       srna, "float", 0, "32 bit Float", "Create image with 32 bit floating point bit depth");
3761   RNA_def_boolean(srna, "alpha", 1, "Alpha", "Create an image with an alpha channel");
3762 }
3763 
tile_add_poll(bContext * C)3764 static bool tile_add_poll(bContext *C)
3765 {
3766   Image *ima = CTX_data_edit_image(C);
3767 
3768   return (ima != NULL && ima->source == IMA_SRC_TILED && BKE_image_has_ibuf(ima, NULL));
3769 }
3770 
tile_add_exec(bContext * C,wmOperator * op)3771 static int tile_add_exec(bContext *C, wmOperator *op)
3772 {
3773   Image *ima = CTX_data_edit_image(C);
3774 
3775   int start_tile = RNA_int_get(op->ptr, "number");
3776   int end_tile = start_tile + RNA_int_get(op->ptr, "count");
3777 
3778   if (start_tile < 1001 || end_tile > IMA_UDIM_MAX) {
3779     BKE_report(op->reports, RPT_ERROR, "Invalid UDIM index range was specified");
3780     return OPERATOR_CANCELLED;
3781   }
3782 
3783   bool fill_tile = RNA_boolean_get(op->ptr, "fill");
3784   char *label = RNA_string_get_alloc(op->ptr, "label", NULL, 0);
3785 
3786   bool created_tile = false;
3787   for (int tile_number = start_tile; tile_number < end_tile; tile_number++) {
3788     ImageTile *tile = BKE_image_add_tile(ima, tile_number, label);
3789 
3790     if (tile != NULL) {
3791       ima->active_tile_index = BLI_findindex(&ima->tiles, tile);
3792 
3793       if (fill_tile) {
3794         do_fill_tile(op->ptr, ima, tile);
3795       }
3796 
3797       created_tile = true;
3798     }
3799   }
3800   MEM_freeN(label);
3801 
3802   if (!created_tile) {
3803     return OPERATOR_CANCELLED;
3804   }
3805 
3806   WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL);
3807   return OPERATOR_FINISHED;
3808 }
3809 
tile_add_invoke(bContext * C,wmOperator * op,const wmEvent * UNUSED (event))3810 static int tile_add_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
3811 {
3812   Image *ima = CTX_data_edit_image(C);
3813 
3814   /* Find the first gap in tile numbers or the number after the last if
3815    * no gap exists. */
3816   int next_number = 0;
3817   LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
3818     next_number = tile->tile_number + 1;
3819     if (tile->next == NULL || tile->next->tile_number > next_number) {
3820       break;
3821     }
3822   }
3823 
3824   ImageTile *tile = BLI_findlink(&ima->tiles, ima->active_tile_index);
3825   tile_fill_init(op->ptr, ima, tile);
3826 
3827   RNA_int_set(op->ptr, "number", next_number);
3828   RNA_int_set(op->ptr, "count", 1);
3829   RNA_string_set(op->ptr, "label", "");
3830 
3831   return WM_operator_props_dialog_popup(C, op, 300);
3832 }
3833 
tile_add_draw(bContext * UNUSED (C),wmOperator * op)3834 static void tile_add_draw(bContext *UNUSED(C), wmOperator *op)
3835 {
3836   uiLayout *col;
3837   uiLayout *layout = op->layout;
3838   PointerRNA ptr;
3839 
3840   RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
3841 
3842   uiLayoutSetPropSep(layout, true);
3843   uiLayoutSetPropDecorate(layout, false);
3844 
3845   col = uiLayoutColumn(layout, false);
3846   uiItemR(col, &ptr, "number", 0, NULL, ICON_NONE);
3847   uiItemR(col, &ptr, "count", 0, NULL, ICON_NONE);
3848   uiItemR(col, &ptr, "label", 0, NULL, ICON_NONE);
3849   uiItemR(layout, &ptr, "fill", 0, NULL, ICON_NONE);
3850 
3851   if (RNA_boolean_get(&ptr, "fill")) {
3852     draw_fill_tile(&ptr, layout);
3853   }
3854 }
3855 
IMAGE_OT_tile_add(wmOperatorType * ot)3856 void IMAGE_OT_tile_add(wmOperatorType *ot)
3857 {
3858   /* identifiers */
3859   ot->name = "Add Tile";
3860   ot->description = "Adds a tile to the image";
3861   ot->idname = "IMAGE_OT_tile_add";
3862 
3863   /* api callbacks */
3864   ot->poll = tile_add_poll;
3865   ot->exec = tile_add_exec;
3866   ot->invoke = tile_add_invoke;
3867   ot->ui = tile_add_draw;
3868 
3869   /* flags */
3870   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3871 
3872   RNA_def_int(ot->srna,
3873               "number",
3874               1002,
3875               1001,
3876               IMA_UDIM_MAX,
3877               "Number",
3878               "UDIM number of the tile",
3879               1001,
3880               1099);
3881   RNA_def_int(ot->srna, "count", 1, 1, INT_MAX, "Count", "How many tiles to add", 1, 1000);
3882   RNA_def_string(ot->srna, "label", NULL, 0, "Label", "Optional tile label");
3883   RNA_def_boolean(ot->srna, "fill", true, "Fill", "Fill new tile with a generated image");
3884   def_fill_tile(ot->srna);
3885 }
3886 
3887 /** \} */
3888 
3889 /* -------------------------------------------------------------------- */
3890 /** \name Remove Tile Operator
3891  * \{ */
3892 
tile_remove_poll(bContext * C)3893 static bool tile_remove_poll(bContext *C)
3894 {
3895   Image *ima = CTX_data_edit_image(C);
3896 
3897   return (ima != NULL && ima->source == IMA_SRC_TILED && ima->active_tile_index != 0);
3898 }
3899 
tile_remove_exec(bContext * C,wmOperator * UNUSED (op))3900 static int tile_remove_exec(bContext *C, wmOperator *UNUSED(op))
3901 {
3902   Image *ima = CTX_data_edit_image(C);
3903 
3904   ImageTile *tile = BLI_findlink(&ima->tiles, ima->active_tile_index);
3905   if (!BKE_image_remove_tile(ima, tile)) {
3906     return OPERATOR_CANCELLED;
3907   }
3908 
3909   /* Ensure that the active index is valid. */
3910   ima->active_tile_index = min_ii(ima->active_tile_index, BLI_listbase_count(&ima->tiles) - 1);
3911 
3912   WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL);
3913 
3914   return OPERATOR_FINISHED;
3915 }
3916 
IMAGE_OT_tile_remove(wmOperatorType * ot)3917 void IMAGE_OT_tile_remove(wmOperatorType *ot)
3918 {
3919   /* identifiers */
3920   ot->name = "Remove Tile";
3921   ot->description = "Removes a tile from the image";
3922   ot->idname = "IMAGE_OT_tile_remove";
3923 
3924   /* api callbacks */
3925   ot->poll = tile_remove_poll;
3926   ot->exec = tile_remove_exec;
3927 
3928   /* flags */
3929   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3930 }
3931 
3932 /** \} */
3933 
3934 /* -------------------------------------------------------------------- */
3935 /** \name Fill Tile Operator
3936  * \{ */
3937 
tile_fill_poll(bContext * C)3938 static bool tile_fill_poll(bContext *C)
3939 {
3940   Image *ima = CTX_data_edit_image(C);
3941 
3942   if (ima != NULL && ima->source == IMA_SRC_TILED) {
3943     /* Filling secondary tiles is only allowed if the primary tile exists. */
3944     return (ima->active_tile_index == 0) || BKE_image_has_ibuf(ima, NULL);
3945   }
3946   return false;
3947 }
3948 
tile_fill_exec(bContext * C,wmOperator * op)3949 static int tile_fill_exec(bContext *C, wmOperator *op)
3950 {
3951   Image *ima = CTX_data_edit_image(C);
3952 
3953   ImageTile *tile = BLI_findlink(&ima->tiles, ima->active_tile_index);
3954   if (!do_fill_tile(op->ptr, ima, tile)) {
3955     return OPERATOR_CANCELLED;
3956   }
3957 
3958   WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL);
3959 
3960   return OPERATOR_FINISHED;
3961 }
3962 
tile_fill_invoke(bContext * C,wmOperator * op,const wmEvent * UNUSED (event))3963 static int tile_fill_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
3964 {
3965   tile_fill_init(op->ptr, CTX_data_edit_image(C), NULL);
3966 
3967   return WM_operator_props_dialog_popup(C, op, 300);
3968 }
3969 
tile_fill_draw(bContext * UNUSED (C),wmOperator * op)3970 static void tile_fill_draw(bContext *UNUSED(C), wmOperator *op)
3971 {
3972   PointerRNA ptr;
3973   RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
3974 
3975   draw_fill_tile(&ptr, op->layout);
3976 }
3977 
IMAGE_OT_tile_fill(wmOperatorType * ot)3978 void IMAGE_OT_tile_fill(wmOperatorType *ot)
3979 {
3980   /* identifiers */
3981   ot->name = "Fill Tile";
3982   ot->description = "Fill the current tile with a generated image";
3983   ot->idname = "IMAGE_OT_tile_fill";
3984 
3985   /* api callbacks */
3986   ot->poll = tile_fill_poll;
3987   ot->exec = tile_fill_exec;
3988   ot->invoke = tile_fill_invoke;
3989   ot->ui = tile_fill_draw;
3990 
3991   /* flags */
3992   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3993 
3994   def_fill_tile(ot->srna);
3995 }
3996