/* SPDX-License-Identifier: GPL-2.0-or-later * Copyright 2015 Blender Foundation. All rights reserved. */ /** \file * \ingroup imbuf */ #include #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" #include "IMB_allocimbuf.h" #include "IMB_colormanagement_intern.h" #include "IMB_filetype.h" #include "IMB_metadata.h" #include "imbuf.h" #include "MEM_guardedalloc.h" #include "BLI_utildefines.h" #include "BLI_math.h" #include "DNA_scene_types.h" #include "DNA_userdef_types.h" /* prototypes */ struct Stereo3DData; static void imb_stereo3d_write_doit(struct Stereo3DData *s3d_data, const struct Stereo3dFormat *s3d); static void imb_stereo3d_read_doit(struct Stereo3DData *s3d_data, const struct Stereo3dFormat *s3d); typedef struct Stereo3DData { struct { float *left, *right, *stereo; } rectf; struct { uchar *left, *right, *stereo; } rect; size_t x, y, channels; bool is_float; } Stereo3DData; /* -------------------------------------------------------------------- */ /** \name Local Functions * \{ */ static void imb_stereo3d_write_anaglyph(const Stereo3DData *s3d, enum eStereo3dAnaglyphType mode) { int x, y; size_t width = s3d->x; size_t height = s3d->y; const size_t channels = s3d->channels; const int stride_from = width; const int stride_to = width; const int anaglyph_encoding[3][3] = { {0, 1, 1}, {1, 0, 1}, {0, 0, 1}, }; int r, g, b; r = anaglyph_encoding[mode][0]; g = anaglyph_encoding[mode][1]; b = anaglyph_encoding[mode][2]; if (s3d->is_float) { float *rect_left = s3d->rectf.left; float *rect_right = s3d->rectf.right; float *rect_to = s3d->rectf.stereo; if (channels == 3) { for (y = 0; y < height; y++) { float *to = rect_to + stride_to * y * 3; float *from[2] = { rect_left + stride_from * y * 3, rect_right + stride_from * y * 3, }; for (x = 0; x < width; x++, from[0] += 3, from[1] += 3, to += 3) { to[0] = from[r][0]; to[1] = from[g][1]; to[2] = from[b][2]; } } } else if (channels == 4) { for (y = 0; y < height; y++) { float *to = rect_to + stride_to * y * 4; float *from[2] = { rect_left + stride_from * y * 4, rect_right + stride_from * y * 4, }; for (x = 0; x < width; x++, from[0] += 4, from[1] += 4, to += 4) { to[0] = from[r][0]; to[1] = from[g][1]; to[2] = from[b][2]; to[3] = MAX2(from[0][3], from[1][3]); } } } } else { uchar *rect_left = s3d->rect.left; uchar *rect_right = s3d->rect.right; uchar *rect_to = s3d->rect.stereo; if (channels == 3) { for (y = 0; y < height; y++) { uchar *to = rect_to + stride_to * y * 3; uchar *from[2] = { rect_left + stride_from * y * 3, rect_right + stride_from * y * 3, }; for (x = 0; x < width; x++, from[0] += 3, from[1] += 3, to += 3) { to[0] = from[r][0]; to[1] = from[g][1]; to[2] = from[b][2]; } } } else if (channels == 4) { for (y = 0; y < height; y++) { uchar *to = rect_to + stride_to * y * 4; uchar *from[2] = { rect_left + stride_from * y * 4, rect_right + stride_from * y * 4, }; for (x = 0; x < width; x++, from[0] += 4, from[1] += 4, to += 4) { to[0] = from[r][0]; to[1] = from[g][1]; to[2] = from[b][2]; to[3] = MAX2(from[0][3], from[1][3]); } } } } } static void imb_stereo3d_write_interlace(const Stereo3DData *s3d, enum eStereo3dInterlaceType mode, const bool swap) { int x, y; size_t width = s3d->x; size_t height = s3d->y; const size_t channels = s3d->channels; const int stride_from = width; const int stride_to = width; if (s3d->is_float) { const float *rect_left = s3d->rectf.left; const float *rect_right = s3d->rectf.right; float *rect_to = s3d->rectf.stereo; switch (mode) { case S3D_INTERLACE_ROW: { char i = (char)swap; for (y = 0; y < height; y++) { float *to = rect_to + stride_to * y * channels; const float *from[2] = { rect_left + stride_from * y * channels, rect_right + stride_from * y * channels, }; memcpy(to, from[i], sizeof(float) * channels * stride_from); i = !i; } break; } case S3D_INTERLACE_COLUMN: { if (channels == 1) { for (y = 0; y < height; y++) { float *to = rect_to + stride_to * y; const float *from[2] = { rect_left + stride_from * y, rect_right + stride_from * y, }; char i = (char)swap; for (x = 0; x < width; x++, from[0] += 1, from[1] += 1, to += 1) { to[0] = from[i][0]; i = !i; } } } else if (channels == 3) { for (y = 0; y < height; y++) { float *to = rect_to + stride_to * y * 3; const float *from[2] = { rect_left + stride_from * y * 3, rect_right + stride_from * y * 3, }; char i = (char)swap; for (x = 0; x < width; x++, from[0] += 3, from[1] += 3, to += 3) { copy_v3_v3(to, from[i]); i = !i; } } } else if (channels == 4) { for (y = 0; y < height; y++) { float *to = rect_to + stride_to * y * channels; const float *from[2] = { rect_left + stride_from * y * channels, rect_right + stride_from * y * channels, }; char i = (char)swap; for (x = 0; x < width; x++, from[0] += 4, from[1] += 4, to += 4) { copy_v4_v4(to, from[i]); i = !i; } } } break; } case S3D_INTERLACE_CHECKERBOARD: { if (channels == 1) { char i = (char)swap; for (y = 0; y < height; y++) { float *to = rect_to + stride_to * y; const float *from[2] = { rect_left + stride_from * y, rect_right + stride_from * y, }; char j = i; for (x = 0; x < width; x++, from[0] += 1, from[1] += 1, to += 1) { to[0] = from[j][0]; j = !j; } i = !i; } } else if (channels == 3) { char i = (char)swap; for (y = 0; y < height; y++) { float *to = rect_to + stride_to * y * 3; const float *from[2] = { rect_left + stride_from * y * 3, rect_right + stride_from * y * 3, }; char j = i; for (x = 0; x < width; x++, from[0] += 3, from[1] += 3, to += 3) { copy_v3_v3(to, from[j]); j = !j; } i = !i; } } else if (channels == 4) { char i = (char)swap; for (y = 0; y < height; y++) { float *to = rect_to + stride_to * y * 4; const float *from[2] = { rect_left + stride_from * y * 4, rect_right + stride_from * y * 4, }; char j = i; for (x = 0; x < width; x++, from[0] += 4, from[1] += 4, to += 4) { copy_v4_v4(to, from[j]); j = !j; } i = !i; } } break; } default: { break; } } } else { const uchar *rect_left = s3d->rect.left; const uchar *rect_right = s3d->rect.right; uchar *rect_to = s3d->rect.stereo; switch (mode) { case S3D_INTERLACE_ROW: { char i = (char)swap; for (y = 0; y < height; y++) { uchar *to = rect_to + stride_to * y * channels; const uchar *from[2] = { rect_left + stride_from * y * channels, rect_right + stride_from * y * channels, }; memcpy(to, from[i], sizeof(uchar) * channels * stride_from); i = !i; } break; } case S3D_INTERLACE_COLUMN: { if (channels == 1) { for (y = 0; y < height; y++) { uchar *to = rect_to + stride_to * y; const uchar *from[2] = { rect_left + stride_from * y, rect_right + stride_from * y, }; char i = (char)swap; for (x = 0; x < width; x++, from[0] += 1, from[1] += 1, to += 1) { to[0] = from[i][0]; i = !i; } } } else if (channels == 3) { for (y = 0; y < height; y++) { uchar *to = rect_to + stride_to * y * 3; const uchar *from[2] = { rect_left + stride_from * y * 3, rect_right + stride_from * y * 3, }; char i = (char)swap; for (x = 0; x < width; x++, from[0] += 3, from[1] += 3, to += 3) { copy_v3_v3_uchar(to, from[i]); i = !i; } } } else if (channels == 4) { for (y = 0; y < height; y++) { uchar *to = rect_to + stride_to * y * 4; const uchar *from[2] = { rect_left + stride_from * y * 4, rect_right + stride_from * y * 4, }; char i = (char)swap; for (x = 0; x < width; x++, from[0] += 4, from[1] += 4, to += 4) { copy_v4_v4_uchar(to, from[i]); i = !i; } } } break; } case S3D_INTERLACE_CHECKERBOARD: { if (channels == 1) { char i = (char)swap; for (y = 0; y < height; y++) { uchar *to = rect_to + stride_to * y; const uchar *from[2] = { rect_left + stride_from * y, rect_right + stride_from * y, }; char j = i; for (x = 0; x < width; x++, from[0] += 1, from[1] += 1, to += 1) { to[0] = from[j][0]; j = !j; } i = !i; } } else if (channels == 3) { char i = (char)swap; for (y = 0; y < height; y++) { uchar *to = rect_to + stride_to * y * 3; const uchar *from[2] = { rect_left + stride_from * y * 3, rect_right + stride_from * y * 3, }; char j = i; for (x = 0; x < width; x++, from[0] += 3, from[1] += 3, to += 3) { copy_v3_v3_uchar(to, from[j]); j = !j; } i = !i; } } else if (channels == 4) { char i = (char)swap; for (y = 0; y < height; y++) { uchar *to = rect_to + stride_to * y * 4; const uchar *from[2] = { rect_left + stride_from * y * 4, rect_right + stride_from * y * 4, }; char j = i; for (x = 0; x < width; x++, from[0] += 4, from[1] += 4, to += 4) { copy_v4_v4_uchar(to, from[j]); j = !j; } i = !i; } } break; } default: { break; } } } } /* stereo3d output (s3d->rectf.stereo) is always unsqueezed */ static void imb_stereo3d_write_sidebyside(const Stereo3DData *s3d, const bool crosseyed) { int y; size_t width = s3d->x; size_t height = s3d->y; const size_t channels = s3d->channels; const int stride_from = width; const int stride_to = width * 2; const int l = (int)crosseyed; const int r = !l; if (s3d->is_float) { const float *rect_left = s3d->rectf.left; const float *rect_right = s3d->rectf.right; float *rect_to = s3d->rectf.stereo; for (y = 0; y < height; y++) { float *to = rect_to + stride_to * y * channels; const float *from[2] = { rect_left + stride_from * y * channels, rect_right + stride_from * y * channels, }; memcpy(to, from[l], sizeof(float) * channels * stride_from); memcpy(to + channels * stride_from, from[r], sizeof(float) * channels * stride_from); } } else { const uchar *rect_left = s3d->rect.left; const uchar *rect_right = s3d->rect.right; uchar *rect_to = s3d->rect.stereo; for (y = 0; y < height; y++) { uchar *to = rect_to + stride_to * y * channels; const uchar *from[2] = { rect_left + stride_from * y * channels, rect_right + stride_from * y * channels, }; memcpy(to, from[l], sizeof(uchar) * channels * stride_from); memcpy(to + channels * stride_from, from[r], sizeof(uchar) * channels * stride_from); } } } /* stereo3d output (s3d->rectf.stereo) is always unsqueezed */ static void imb_stereo3d_write_topbottom(const Stereo3DData *s3d) { int y; size_t width = s3d->x; size_t height = s3d->y; const size_t channels = s3d->channels; const int stride_from = width; const int stride_to = width; if (s3d->is_float) { const float *rect_left = s3d->rectf.left; const float *rect_right = s3d->rectf.right; float *rect_to = s3d->rectf.stereo; for (y = 0; y < height; y++) { float *to = rect_to + stride_to * y * channels; const float *from[2] = { rect_left + stride_from * y * channels, rect_right + stride_from * y * channels, }; memcpy(to, from[1], sizeof(float) * channels * stride_from); memcpy( to + channels * height * stride_from, from[0], sizeof(float) * channels * stride_from); } } else { const uchar *rect_left = s3d->rect.left; const uchar *rect_right = s3d->rect.right; uchar *rect_to = s3d->rect.stereo; for (y = 0; y < height; y++) { uchar *to = rect_to + stride_to * y * channels; const uchar *from[2] = { rect_left + stride_from * y * channels, rect_right + stride_from * y * channels, }; memcpy(to, from[1], sizeof(uchar) * channels * stride_from); memcpy( to + channels * height * stride_from, from[0], sizeof(uchar) * channels * stride_from); } } } /** \} */ /* -------------------------------------------------------------------- */ /** \name Dimension Utils * \{ */ void IMB_stereo3d_write_dimensions(const char mode, const bool is_squeezed, const size_t width, const size_t height, size_t *r_width, size_t *r_height) { switch (mode) { case S3D_DISPLAY_SIDEBYSIDE: { *r_width = is_squeezed ? width : width * 2; *r_height = height; break; } case S3D_DISPLAY_TOPBOTTOM: { *r_width = width; *r_height = is_squeezed ? height : height * 2; break; } case S3D_DISPLAY_ANAGLYPH: case S3D_DISPLAY_INTERLACE: default: { *r_width = width; *r_height = height; break; } } } void IMB_stereo3d_read_dimensions(const char mode, const bool is_squeezed, const size_t width, const size_t height, size_t *r_width, size_t *r_height) { switch (mode) { case S3D_DISPLAY_SIDEBYSIDE: { *r_width = is_squeezed ? width / 2 : width; *r_height = height; break; } case S3D_DISPLAY_TOPBOTTOM: { *r_width = width; *r_height = is_squeezed ? height / 2 : height; break; } case S3D_DISPLAY_ANAGLYPH: case S3D_DISPLAY_INTERLACE: default: { *r_width = width; *r_height = height; break; } } } /** \} */ /* -------------------------------------------------------------------- */ /** \name Un/Squeeze Frame * \{ */ static void imb_stereo3d_squeeze_ImBuf(ImBuf *ibuf, const Stereo3dFormat *s3d, const size_t x, const size_t y) { if (ELEM(s3d->display_mode, S3D_DISPLAY_SIDEBYSIDE, S3D_DISPLAY_TOPBOTTOM) == false) { return; } if ((s3d->flag & S3D_SQUEEZED_FRAME) == 0) { return; } IMB_scaleImBuf_threaded(ibuf, x, y); } static void imb_stereo3d_unsqueeze_ImBuf(ImBuf *ibuf, const Stereo3dFormat *s3d, const size_t x, const size_t y) { if (ELEM(s3d->display_mode, S3D_DISPLAY_SIDEBYSIDE, S3D_DISPLAY_TOPBOTTOM) == false) { return; } if ((s3d->flag & S3D_SQUEEZED_FRAME) == 0) { return; } IMB_scaleImBuf_threaded(ibuf, x, y); } static void imb_stereo3d_squeeze_rectf( float *rectf, const Stereo3dFormat *s3d, const size_t x, const size_t y, const size_t channels) { ImBuf *ibuf; size_t width, height; if (ELEM(s3d->display_mode, S3D_DISPLAY_SIDEBYSIDE, S3D_DISPLAY_TOPBOTTOM) == false) { return; } if ((s3d->flag & S3D_SQUEEZED_FRAME) == 0) { return; } /* creates temporary imbuf to store the rectf */ IMB_stereo3d_write_dimensions(s3d->display_mode, false, x, y, &width, &height); ibuf = IMB_allocImBuf(width, height, channels, IB_rectfloat); IMB_buffer_float_from_float(ibuf->rect_float, rectf, channels, IB_PROFILE_LINEAR_RGB, IB_PROFILE_LINEAR_RGB, false, width, height, width, width); IMB_scaleImBuf_threaded(ibuf, x, y); memcpy(rectf, ibuf->rect_float, x * y * sizeof(float[4])); IMB_freeImBuf(ibuf); } static void imb_stereo3d_squeeze_rect( int *rect, const Stereo3dFormat *s3d, const size_t x, const size_t y, const size_t channels) { ImBuf *ibuf; size_t width, height; if (ELEM(s3d->display_mode, S3D_DISPLAY_SIDEBYSIDE, S3D_DISPLAY_TOPBOTTOM) == false) { return; } if ((s3d->flag & S3D_SQUEEZED_FRAME) == 0) { return; } /* creates temporary imbuf to store the rectf */ IMB_stereo3d_write_dimensions(s3d->display_mode, false, x, y, &width, &height); ibuf = IMB_allocImBuf(width, height, channels, IB_rect); IMB_buffer_byte_from_byte((uchar *)ibuf->rect, (uchar *)rect, IB_PROFILE_SRGB, IB_PROFILE_SRGB, false, width, height, width, width); IMB_scaleImBuf_threaded(ibuf, x, y); memcpy(rect, ibuf->rect, x * y * sizeof(uint)); IMB_freeImBuf(ibuf); } /** \} */ /* -------------------------------------------------------------------- */ /** \name Preparing To Call The Write Functions * \{ */ static void imb_stereo3d_data_init(Stereo3DData *s3d_data, const bool is_float, const size_t x, const size_t y, const size_t channels, int *rect_left, int *rect_right, int *rect_stereo, float *rectf_left, float *rectf_right, float *rectf_stereo) { s3d_data->is_float = is_float; s3d_data->x = x; s3d_data->y = y; s3d_data->channels = channels; s3d_data->rect.left = (uchar *)rect_left; s3d_data->rect.right = (uchar *)rect_right; s3d_data->rect.stereo = (uchar *)rect_stereo; s3d_data->rectf.left = rectf_left; s3d_data->rectf.right = rectf_right; s3d_data->rectf.stereo = rectf_stereo; } int *IMB_stereo3d_from_rect(const ImageFormatData *im_format, const size_t x, const size_t y, const size_t channels, int *rect_left, int *rect_right) { int *rect_result; Stereo3DData s3d_data = {{NULL}}; size_t width, height; const bool is_float = im_format->depth > 8; IMB_stereo3d_write_dimensions( im_format->stereo3d_format.display_mode, false, x, y, &width, &height); rect_result = MEM_mallocN(channels * sizeof(int) * width * height, __func__); imb_stereo3d_data_init( &s3d_data, is_float, x, y, channels, rect_left, rect_right, rect_result, NULL, NULL, NULL); imb_stereo3d_write_doit(&s3d_data, &im_format->stereo3d_format); imb_stereo3d_squeeze_rect(rect_result, &im_format->stereo3d_format, x, y, channels); return rect_result; } float *IMB_stereo3d_from_rectf(const ImageFormatData *im_format, const size_t x, const size_t y, const size_t channels, float *rectf_left, float *rectf_right) { float *rectf_result; Stereo3DData s3d_data = {{NULL}}; size_t width, height; const bool is_float = im_format->depth > 8; IMB_stereo3d_write_dimensions( im_format->stereo3d_format.display_mode, false, x, y, &width, &height); rectf_result = MEM_mallocN(channels * sizeof(float) * width * height, __func__); imb_stereo3d_data_init(&s3d_data, is_float, x, y, channels, NULL, NULL, NULL, rectf_left, rectf_right, rectf_result); imb_stereo3d_write_doit(&s3d_data, &im_format->stereo3d_format); imb_stereo3d_squeeze_rectf(rectf_result, &im_format->stereo3d_format, x, y, channels); return rectf_result; } ImBuf *IMB_stereo3d_ImBuf(const ImageFormatData *im_format, ImBuf *ibuf_left, ImBuf *ibuf_right) { ImBuf *ibuf_stereo = NULL; Stereo3DData s3d_data = {{NULL}}; size_t width, height; const bool is_float = im_format->depth > 8; IMB_stereo3d_write_dimensions( im_format->stereo3d_format.display_mode, false, ibuf_left->x, ibuf_left->y, &width, &height); ibuf_stereo = IMB_allocImBuf(width, height, ibuf_left->planes, 0); if (is_float) { imb_addrectfloatImBuf(ibuf_stereo, ibuf_left->channels); } else { imb_addrectImBuf(ibuf_stereo); } ibuf_stereo->flags = ibuf_left->flags; imb_stereo3d_data_init(&s3d_data, is_float, ibuf_left->x, ibuf_left->y, ibuf_left->channels, (int *)ibuf_left->rect, (int *)ibuf_right->rect, (int *)ibuf_stereo->rect, ibuf_left->rect_float, ibuf_right->rect_float, ibuf_stereo->rect_float); imb_stereo3d_write_doit(&s3d_data, &im_format->stereo3d_format); imb_stereo3d_squeeze_ImBuf(ibuf_stereo, &im_format->stereo3d_format, ibuf_left->x, ibuf_left->y); return ibuf_stereo; } static void imb_stereo3d_write_doit(Stereo3DData *s3d_data, const Stereo3dFormat *s3d) { switch (s3d->display_mode) { case S3D_DISPLAY_ANAGLYPH: imb_stereo3d_write_anaglyph(s3d_data, s3d->anaglyph_type); break; case S3D_DISPLAY_INTERLACE: imb_stereo3d_write_interlace( s3d_data, s3d->interlace_type, (s3d->flag & S3D_INTERLACE_SWAP) != 0); break; case S3D_DISPLAY_SIDEBYSIDE: imb_stereo3d_write_sidebyside(s3d_data, (s3d->flag & S3D_SIDEBYSIDE_CROSSEYED) != 0); break; case S3D_DISPLAY_TOPBOTTOM: imb_stereo3d_write_topbottom(s3d_data); break; default: break; } } /** \} */ /* -------------------------------------------------------------------- */ /** \name Reading Stereo ImBuf's * \{ */ static void imb_stereo3d_read_anaglyph(const Stereo3DData *s3d, enum eStereo3dAnaglyphType mode) { int x, y; size_t width = s3d->x; size_t height = s3d->y; const size_t channels = s3d->channels; const int stride_from = width; const int stride_to = width; const int anaglyph_encoding[3][3] = { {0, 1, 1}, {1, 0, 1}, {0, 0, 1}, }; int r, g, b; r = anaglyph_encoding[mode][0]; g = anaglyph_encoding[mode][1]; b = anaglyph_encoding[mode][2]; if (s3d->is_float) { float *rect_left = s3d->rectf.left; float *rect_right = s3d->rectf.right; float *rect_from = s3d->rectf.stereo; if (channels == 3) { for (y = 0; y < height; y++) { float *from = rect_from + stride_from * y * 3; float *to[2] = { rect_left + stride_to * y * 3, rect_right + stride_to * y * 3, }; for (x = 0; x < width; x++, from += 3, to[0] += 3, to[1] += 3) { to[r][0] = from[0]; to[g][1] = from[1]; to[b][2] = from[2]; } } } else if (channels == 4) { for (y = 0; y < height; y++) { float *from = rect_from + stride_from * y * 4; float *to[2] = { rect_left + stride_to * y * 4, rect_right + stride_to * y * 4, }; for (x = 0; x < width; x++, from += 4, to[0] += 4, to[1] += 4) { to[r][0] = from[0]; to[g][1] = from[1]; to[b][2] = from[2]; to[0][3] = to[1][3] = from[3]; } } } } else { uchar *rect_left = s3d->rect.left; uchar *rect_right = s3d->rect.right; uchar *rect_from = s3d->rect.stereo; if (channels == 3) { for (y = 0; y < height; y++) { uchar *from = rect_from + stride_from * y * 3; uchar *to[2] = { rect_left + stride_to * y * 3, rect_right + stride_to * y * 3, }; for (x = 0; x < width; x++, from += 3, to[0] += 3, to[1] += 3) { to[r][0] = from[0]; to[g][1] = from[1]; to[b][2] = from[2]; } } } else if (channels == 4) { for (y = 0; y < height; y++) { uchar *from = rect_from + stride_from * y * 4; uchar *to[2] = { rect_left + stride_to * y * 4, rect_right + stride_to * y * 4, }; for (x = 0; x < width; x++, from += 4, to[0] += 4, to[1] += 4) { to[r][0] = from[0]; to[g][1] = from[1]; to[b][2] = from[2]; to[0][3] = to[1][3] = from[3]; } } } } } static void imb_stereo3d_read_interlace(const Stereo3DData *s3d, enum eStereo3dInterlaceType mode, const bool swap) { int x, y; size_t width = s3d->x; size_t height = s3d->y; const size_t channels = s3d->channels; const int stride_from = width; const int stride_to = width; if (s3d->is_float) { float *rect_left = s3d->rectf.left; float *rect_right = s3d->rectf.right; const float *rect_from = s3d->rectf.stereo; switch (mode) { case S3D_INTERLACE_ROW: { char i = (char)swap; for (y = 0; y < height; y++) { const float *from = rect_from + stride_from * y * channels; float *to[2] = { rect_left + stride_to * y * channels, rect_right + stride_to * y * channels, }; memcpy(to[i], from, sizeof(float) * channels * stride_to); i = !i; } break; } case S3D_INTERLACE_COLUMN: { if (channels == 1) { for (y = 0; y < height; y++) { const float *from = rect_from + stride_from * y; float *to[2] = { rect_left + stride_to * y, rect_right + stride_to * y, }; char i = (char)swap; for (x = 0; x < width; x++, from += 1, to[0] += 1, to[1] += 1) { to[i][0] = from[0]; i = !i; } } } else if (channels == 3) { for (y = 0; y < height; y++) { const float *from = rect_from + stride_from * y * 3; float *to[2] = { rect_left + stride_to * y * 3, rect_right + stride_to * y * 3, }; char i = (char)swap; for (x = 0; x < width; x++, from += 3, to[0] += 3, to[1] += 3) { copy_v3_v3(to[i], from); i = !i; } } } else if (channels == 4) { for (y = 0; y < height; y++) { const float *from = rect_from + stride_from * y * channels; float *to[2] = { rect_left + stride_to * y * channels, rect_right + stride_to * y * channels, }; char i = (char)swap; for (x = 0; x < width; x++, from += 4, to[0] += 4, to[1] += 4) { copy_v4_v4(to[i], from); i = !i; } } } break; } case S3D_INTERLACE_CHECKERBOARD: { if (channels == 1) { char i = (char)swap; for (y = 0; y < height; y++) { const float *from = rect_from + stride_from * y; float *to[2] = { rect_left + stride_to * y, rect_right + stride_to * y, }; char j = i; for (x = 0; x < width; x++, from += 1, to[0] += 1, to[1] += 1) { to[j][0] = from[0]; j = !j; } i = !i; } } else if (channels == 3) { char i = (char)swap; for (y = 0; y < height; y++) { const float *from = rect_from + stride_from * y * 3; float *to[2] = { rect_left + stride_to * y * 3, rect_right + stride_to * y * 3, }; char j = i; for (x = 0; x < width; x++, from += 3, to[0] += 3, to[1] += 3) { copy_v3_v3(to[j], from); j = !j; } i = !i; } } else if (channels == 4) { char i = (char)swap; for (y = 0; y < height; y++) { const float *from = rect_from + stride_from * y * 4; float *to[2] = { rect_left + stride_to * y * 4, rect_right + stride_to * y * 4, }; char j = i; for (x = 0; x < width; x++, from += 4, to[0] += 4, to[1] += 4) { copy_v4_v4(to[j], from); j = !j; } i = !i; } } break; } default: { break; } } } else { uchar *rect_left = s3d->rect.right; uchar *rect_right = s3d->rect.left; const uchar *rect_from = s3d->rect.stereo; switch (mode) { case S3D_INTERLACE_ROW: { char i = (char)swap; for (y = 0; y < height; y++) { const uchar *from = rect_from + stride_from * y * channels; uchar *to[2] = { rect_left + stride_to * y * channels, rect_right + stride_to * y * channels, }; memcpy(to[i], from, sizeof(uchar) * channels * stride_to); i = !i; } break; } case S3D_INTERLACE_COLUMN: { if (channels == 1) { for (y = 0; y < height; y++) { const uchar *from = rect_from + stride_from * y; uchar *to[2] = { rect_left + stride_to * y, rect_right + stride_to * y, }; char i = (char)swap; for (x = 0; x < width; x++, from += 1, to[0] += 1, to[1] += 1) { to[i][0] = from[0]; i = !i; } } } else if (channels == 3) { for (y = 0; y < height; y++) { const uchar *from = rect_from + stride_from * y * 3; uchar *to[2] = { rect_left + stride_to * y * 3, rect_right + stride_to * y * 3, }; char i = (char)swap; for (x = 0; x < width; x++, from += 3, to[0] += 3, to[1] += 3) { copy_v3_v3_uchar(to[i], from); i = !i; } } } else if (channels == 4) { for (y = 0; y < height; y++) { const uchar *from = rect_from + stride_from * y * 4; uchar *to[2] = { rect_left + stride_to * y * 4, rect_right + stride_to * y * 4, }; char i = (char)swap; for (x = 0; x < width; x++, from += 4, to[0] += 4, to[1] += 4) { copy_v4_v4_uchar(to[i], from); i = !i; } } } break; } case S3D_INTERLACE_CHECKERBOARD: { if (channels == 1) { char i = (char)swap; for (y = 0; y < height; y++) { const uchar *from = rect_from + stride_from * y; uchar *to[2] = { rect_left + stride_to * y, rect_right + stride_to * y, }; char j = i; for (x = 0; x < width; x++, from += 1, to[0] += 1, to[1] += 1) { to[j][0] = from[0]; j = !j; } i = !i; } } else if (channels == 3) { char i = (char)swap; for (y = 0; y < height; y++) { const uchar *from = rect_from + stride_from * y * 3; uchar *to[2] = { rect_left + stride_to * y * 3, rect_right + stride_to * y * 3, }; char j = i; for (x = 0; x < width; x++, from += 3, to[0] += 3, to[1] += 3) { copy_v3_v3_uchar(to[j], from); j = !j; } i = !i; } } else if (channels == 4) { char i = (char)swap; for (y = 0; y < height; y++) { const uchar *from = rect_from + stride_from * y * 4; uchar *to[2] = { rect_left + stride_to * y * 4, rect_right + stride_to * y * 4, }; char j = i; for (x = 0; x < width; x++, from += 4, to[0] += 4, to[1] += 4) { copy_v4_v4_uchar(to[j], from); j = !j; } i = !i; } } break; } default: { break; } } } } /* stereo input (s3d->rectf.stereo) is always unsqueezed */ static void imb_stereo3d_read_sidebyside(const Stereo3DData *s3d, const bool crosseyed) { int y; size_t width = s3d->x; size_t height = s3d->y; const size_t channels = s3d->channels; const int stride_from = width * 2; const int stride_to = width; const int l = (int)crosseyed; const int r = !l; if (s3d->is_float) { float *rect_left = s3d->rectf.left; float *rect_right = s3d->rectf.right; const float *rect_from = s3d->rectf.stereo; for (y = 0; y < height; y++) { const float *from = rect_from + stride_from * y * channels; float *to[2] = { rect_left + stride_to * y * channels, rect_right + stride_to * y * channels, }; memcpy(to[l], from, sizeof(float) * channels * stride_to); memcpy(to[r], from + channels * stride_to, sizeof(float) * channels * stride_to); } } else { uchar *rect_left = s3d->rect.left; uchar *rect_right = s3d->rect.right; const uchar *rect_from = s3d->rect.stereo; /* always RGBA input/output */ for (y = 0; y < height; y++) { const uchar *from = rect_from + stride_from * y * channels; uchar *to[2] = { rect_left + stride_to * y * channels, rect_right + stride_to * y * channels, }; memcpy(to[l], from, sizeof(uchar) * channels * stride_to); memcpy(to[r], from + channels * stride_to, sizeof(uchar) * channels * stride_to); } } } /* stereo input (s3d->rectf.stereo) is always unsqueezed */ static void imb_stereo3d_read_topbottom(const Stereo3DData *s3d) { int y; size_t width = s3d->x; size_t height = s3d->y; const size_t channels = s3d->channels; const int stride_from = width; const int stride_to = width; if (s3d->is_float) { float *rect_left = s3d->rectf.left; float *rect_right = s3d->rectf.right; const float *rect_from = s3d->rectf.stereo; for (y = 0; y < height; y++) { const float *from = rect_from + stride_from * y * channels; float *to[2] = { rect_left + stride_to * y * channels, rect_right + stride_to * y * channels, }; memcpy(to[1], from, sizeof(float) * channels * stride_to); memcpy(to[0], from + channels * height * stride_to, sizeof(float) * channels * stride_to); } } else { uchar *rect_left = s3d->rect.left; uchar *rect_right = s3d->rect.right; const uchar *rect_from = s3d->rect.stereo; for (y = 0; y < height; y++) { const uchar *from = rect_from + stride_from * y * channels; uchar *to[2] = { rect_left + stride_to * y * channels, rect_right + stride_to * y * channels, }; memcpy(to[1], from, sizeof(uchar) * channels * stride_to); memcpy(to[0], from + channels * height * stride_to, sizeof(uchar) * channels * stride_to); } } } /** \} */ /* -------------------------------------------------------------------- */ /** \name Preparing To Call The Read Functions * \{ */ void IMB_ImBufFromStereo3d(const Stereo3dFormat *s3d, ImBuf *ibuf_stereo3d, ImBuf **r_ibuf_left, ImBuf **r_ibuf_right) { Stereo3DData s3d_data = {{NULL}}; ImBuf *ibuf_left, *ibuf_right; size_t width, height; const bool is_float = (ibuf_stereo3d->rect_float != NULL); IMB_stereo3d_read_dimensions(s3d->display_mode, ((s3d->flag & S3D_SQUEEZED_FRAME) == 0), ibuf_stereo3d->x, ibuf_stereo3d->y, &width, &height); ibuf_left = IMB_allocImBuf(width, height, ibuf_stereo3d->planes, 0); ibuf_right = IMB_allocImBuf(width, height, ibuf_stereo3d->planes, 0); if (is_float) { imb_addrectfloatImBuf(ibuf_left, ibuf_stereo3d->channels); imb_addrectfloatImBuf(ibuf_right, ibuf_stereo3d->channels); } else { imb_addrectImBuf(ibuf_left); imb_addrectImBuf(ibuf_right); } ibuf_left->flags = ibuf_stereo3d->flags; ibuf_right->flags = ibuf_stereo3d->flags; /* we always work with unsqueezed formats */ IMB_stereo3d_write_dimensions(s3d->display_mode, ((s3d->flag & S3D_SQUEEZED_FRAME) == 0), ibuf_stereo3d->x, ibuf_stereo3d->y, &width, &height); imb_stereo3d_unsqueeze_ImBuf(ibuf_stereo3d, s3d, width, height); imb_stereo3d_data_init(&s3d_data, is_float, ibuf_left->x, ibuf_left->y, ibuf_left->channels, (int *)ibuf_left->rect, (int *)ibuf_right->rect, (int *)ibuf_stereo3d->rect, ibuf_left->rect_float, ibuf_right->rect_float, ibuf_stereo3d->rect_float); imb_stereo3d_read_doit(&s3d_data, s3d); if (ibuf_stereo3d->flags & (IB_zbuf | IB_zbuffloat)) { if (is_float) { addzbuffloatImBuf(ibuf_left); addzbuffloatImBuf(ibuf_right); } else { addzbufImBuf(ibuf_left); addzbufImBuf(ibuf_right); } imb_stereo3d_data_init(&s3d_data, is_float, ibuf_left->x, ibuf_left->y, 1, (int *)ibuf_left->zbuf, (int *)ibuf_right->zbuf, (int *)ibuf_stereo3d->zbuf, ibuf_left->zbuf_float, ibuf_right->zbuf_float, ibuf_stereo3d->zbuf_float); imb_stereo3d_read_doit(&s3d_data, s3d); } IMB_freeImBuf(ibuf_stereo3d); *r_ibuf_left = ibuf_left; *r_ibuf_right = ibuf_right; } static void imb_stereo3d_read_doit(Stereo3DData *s3d_data, const Stereo3dFormat *s3d) { switch (s3d->display_mode) { case S3D_DISPLAY_ANAGLYPH: imb_stereo3d_read_anaglyph(s3d_data, s3d->anaglyph_type); break; case S3D_DISPLAY_INTERLACE: imb_stereo3d_read_interlace( s3d_data, s3d->interlace_type, (s3d->flag & S3D_INTERLACE_SWAP) != 0); break; case S3D_DISPLAY_SIDEBYSIDE: imb_stereo3d_read_sidebyside(s3d_data, (s3d->flag & S3D_SIDEBYSIDE_CROSSEYED) != 0); break; case S3D_DISPLAY_TOPBOTTOM: imb_stereo3d_read_topbottom(s3d_data); break; default: break; } } /** \} */