UI: Image Rotate #117352
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
/* 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
|
||||
* \{ */
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
Brecht Van Lommel
commented
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
Brecht Van Lommel
commented
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
Brecht Van Lommel
commented
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
Brecht Van Lommel
commented
Float images can have arbitrary number of channels, so this should use 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;
|
||||
|
|
Loading…
Reference in New Issue
This should be an enum as it doesn't actually support more values.