UI: Image Rotate #117352

Merged
Harley Acheson merged 10 commits from Harley/blender:ImageRotate into main 2024-01-31 23:36:37 +01:00
6 changed files with 186 additions and 6 deletions

View File

@ -231,7 +231,7 @@ class IMAGE_MT_image(Menu):
layout.menu("IMAGE_MT_image_invert")
layout.operator("image.resize", text="Resize")
layout.menu("IMAGE_MT_image_flip")
layout.menu("IMAGE_MT_image_transform")
if ima and not show_render:
if ima.packed_file:
@ -248,13 +248,17 @@ class IMAGE_MT_image(Menu):
layout.operator("gpencil.image_to_grease_pencil", text="Generate Grease Pencil")
class IMAGE_MT_image_flip(Menu):
bl_label = "Flip"
class IMAGE_MT_image_transform(Menu):
bl_label = "Transform"
def draw(self, _context):
layout = self.layout
layout.operator("image.flip", text="Horizontally").use_flip_x = True
layout.operator("image.flip", text="Vertically").use_flip_y = True
layout.operator("image.flip", text="Flip Horizontally").use_flip_x = True
layout.operator("image.flip", text="Flip Vertically").use_flip_y = True
layout.separator()
layout.operator("image.rotate_orthogonal", text="Rotate 90\u00B0 Clockwise").degrees = '90'
layout.operator("image.rotate_orthogonal", text="Rotate 90\u00B0 Counter-Clockwise").degrees = '270'
layout.operator("image.rotate_orthogonal", text="Rotate 180\u00B0").degrees = '180'
class IMAGE_MT_image_invert(Menu):
@ -1684,7 +1688,7 @@ classes = (
IMAGE_MT_select,
IMAGE_MT_select_linked,
IMAGE_MT_image,
IMAGE_MT_image_flip,
IMAGE_MT_image_transform,
IMAGE_MT_image_invert,
IMAGE_MT_uvs,
IMAGE_MT_uvs_showhide,

View File

@ -69,6 +69,7 @@ void IMAGE_OT_clipboard_copy(wmOperatorType *ot);
void IMAGE_OT_clipboard_paste(wmOperatorType *ot);
void IMAGE_OT_flip(wmOperatorType *ot);
void IMAGE_OT_rotate_orthogonal(wmOperatorType *ot);
void IMAGE_OT_invert(wmOperatorType *ot);
void IMAGE_OT_resize(wmOperatorType *ot);

View File

@ -2841,6 +2841,88 @@ void IMAGE_OT_flip(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Rotate Orthogonal Operator (90, 180, 270)
* \{ */
static int image_rotate_orthogonal_exec(bContext *C, wmOperator *op)
{
Image *ima = image_from_context(C);
ImageUser iuser = image_user_from_context_and_active_tile(C, ima);
ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, nullptr);
SpaceImage *sima = CTX_wm_space_image(C);
const bool is_paint = ((sima != nullptr) && (sima->mode == SI_MODE_PAINT));
if (ibuf == nullptr) {
/* TODO: this should actually never happen, but does for render-results -> cleanup. */
return OPERATOR_CANCELLED;
}
int degrees = RNA_enum_get(op->ptr, "degrees");
ED_image_undo_push_begin_with_image(op->type->name, ima, ibuf, &iuser);
if (is_paint) {
ED_imapaint_clear_partial_redraw();
}
if (!IMB_rotate_orthogonal(ibuf, degrees)) {
BKE_image_release_ibuf(ima, ibuf, nullptr);
return OPERATOR_CANCELLED;
}
ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
BKE_image_mark_dirty(ima, ibuf);
if (ibuf->mipmap[0]) {
ibuf->userflags |= IB_MIPMAP_INVALID;
}
ED_image_undo_push_end();
BKE_image_partial_update_mark_full_update(ima);
WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima);
BKE_image_release_ibuf(ima, ibuf, nullptr);
return OPERATOR_FINISHED;
}
void IMAGE_OT_rotate_orthogonal(wmOperatorType *ot)
{
static const EnumPropertyItem orthogonal_rotation_items[] = {
{90, "90", 0, "90 Degrees", "Rotate 90 degrees clockwise"},
{180, "180", 0, "180 Degrees", "Rotate 180 degrees clockwise"},
{270, "270", 0, "270 Degrees", "Rotate 270 degrees clockwise"},
{0, nullptr, 0, nullptr, nullptr},
};
/* identifiers */
ot->name = "Rotate Image Orthogonal";
ot->idname = "IMAGE_OT_rotate_orthogonal";
ot->description = "Rotate the image";
Harley marked this conversation as resolved

This should be an enum as it doesn't actually support more values.

This should be an enum as it doesn't actually support more values.
/* api callbacks */
ot->exec = image_rotate_orthogonal_exec;
ot->poll = image_from_context_has_data_poll_active_tile;
/* properties */
PropertyRNA *prop;
prop = RNA_def_enum(ot->srna,
"degrees",
orthogonal_rotation_items,
90,
"Degrees",
"Amount of rotation in degrees (90, 180, 270)");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
/* flags */
ot->flag = OPTYPE_REGISTER;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Clipboard Copy Operator
* \{ */

View File

@ -220,6 +220,7 @@ static void image_operatortypes()
WM_operatortype_append(IMAGE_OT_clipboard_paste);
WM_operatortype_append(IMAGE_OT_flip);
WM_operatortype_append(IMAGE_OT_rotate_orthogonal);
WM_operatortype_append(IMAGE_OT_invert);
WM_operatortype_append(IMAGE_OT_resize);

View File

@ -529,6 +529,9 @@ ImBuf *IMB_double_y(ImBuf *ibuf1);
void IMB_flipx(ImBuf *ibuf);
void IMB_flipy(ImBuf *ibuf);
/* Rotate by 90 degree increments. Returns true if the ImBuf is altered. */
bool IMB_rotate_orthogonal(ImBuf *ibuf, int degrees);
/* Pre-multiply alpha. */
void IMB_premultiply_alpha(ImBuf *ibuf);

View File

@ -14,6 +14,95 @@
#include "IMB_imbuf_types.hh"
#include "imbuf.hh"
bool IMB_rotate_orthogonal(ImBuf *ibuf, int degrees)
{
if (!ELEM(degrees, 90, 180, 270)) {
Harley marked this conversation as resolved Outdated

This should not be needed.

This should not be needed.
return false;
}
const int size_x = ibuf->x;
const int size_y = ibuf->y;
Harley marked this conversation as resolved

This shouldn't be needed if only fixed degrees are supported.

This shouldn't be needed if only fixed degrees are supported.
if (ibuf->float_buffer.data) {
float *float_pixels = ibuf->float_buffer.data;
float *orig_float_pixels = static_cast<float *>(MEM_dupallocN(float_pixels));
const int channels = ibuf->channels;
if (degrees == 90) {
SWAP(int, ibuf->x, ibuf->y);
for (int y = 0; y < size_y; y++) {
for (int x = 0; x < size_x; x++) {
const float *source_pixel = &orig_float_pixels[(y * size_x + x) * channels];
memcpy(&float_pixels[(y + ((size_x - x - 1) * size_y)) * channels],
source_pixel,
sizeof(float) * channels);
}
}
}
else if (degrees == 180) {
for (int y = 0; y < size_y; y++) {
for (int x = 0; x < size_x; x++) {
const float *source_pixel = &orig_float_pixels[(y * size_x + x) * channels];
memcpy(&float_pixels[(((size_y - y - 1) * size_x) + (size_x - x - 1)) * channels],
Harley marked this conversation as resolved Outdated

Move this check out of the loop and duplicate the for loops. More efficient that way.

Move this check out of the loop and duplicate the for loops. More efficient that way.
source_pixel,
sizeof(float) * channels);
}
Harley marked this conversation as resolved

Float images can have arbitrary number of channels, so this should use const int channels = ibuf->channels;.

Float images can have arbitrary number of channels, so this should use `const int channels = ibuf->channels;`.
}
}
else if (degrees == 270) {
SWAP(int, ibuf->x, ibuf->y);
for (int y = 0; y < size_y; y++) {
for (int x = 0; x < size_x; x++) {
const float *source_pixel = &orig_float_pixels[(y * size_x + x) * channels];
memcpy(&float_pixels[((size_y - y - 1) + (x * size_y)) * channels],
source_pixel,
sizeof(float) * channels);
}
}
}
MEM_freeN(orig_float_pixels);
if (ibuf->byte_buffer.data) {
IMB_rect_from_float(ibuf);
}
}
else if (ibuf->byte_buffer.data) {
uchar *char_pixels = ibuf->byte_buffer.data;
uchar *orig_char_pixels = static_cast<uchar *>(MEM_dupallocN(char_pixels));
if (degrees == 90) {
SWAP(int, ibuf->x, ibuf->y);
for (int y = 0; y < size_y; y++) {
for (int x = 0; x < size_x; x++) {
const uchar *source_pixel = &orig_char_pixels[(y * size_x + x) * 4];
memcpy(
&char_pixels[(y + ((size_x - x - 1) * size_y)) * 4], source_pixel, sizeof(uchar[4]));
}
}
}
else if (degrees == 180) {
for (int y = 0; y < size_y; y++) {
for (int x = 0; x < size_x; x++) {
const uchar *source_pixel = &orig_char_pixels[(y * size_x + x) * 4];
memcpy(&char_pixels[(((size_y - y - 1) * size_x) + (size_x - x - 1)) * 4],
source_pixel,
sizeof(uchar[4]));
}
}
}
else if (degrees == 270) {
SWAP(int, ibuf->x, ibuf->y);
for (int y = 0; y < size_y; y++) {
for (int x = 0; x < size_x; x++) {
const uchar *source_pixel = &orig_char_pixels[(y * size_x + x) * 4];
memcpy(
&char_pixels[((size_y - y - 1) + (x * size_y)) * 4], source_pixel, sizeof(uchar[4]));
}
}
}
MEM_freeN(orig_char_pixels);
}
return true;
}
void IMB_flipy(ImBuf *ibuf)
{
size_t x_size, y_size;