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(®ion->winrct) <= sima->zoom) {
133 sima->zoom = oldzoom;
134 }
135 else if (BLI_rcti_size_y(®ion->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(®ion->winrct) / (BLI_rctf_size_x(bounds) * image_size[0]);
183 size_xy[1] = BLI_rcti_size_y(®ion->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 ®ion->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 ®ion->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(®ion->winrct) + 1;
787 height = BLI_rcti_size_y(®ion->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 ®ion->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 ®ion->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(®ion->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(®ion->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(®ion->v2d, x_start, y_start, &uv1[0], &uv1[1]);
3153 UI_view2d_region_to_view(®ion->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(®ion->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(®ion->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