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)) {
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],
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;