diff options
author | Vivek Jadhav <vivek.jadhav@ittiam.com> | 2024-03-22 18:33:03 +0530 |
---|---|---|
committer | DichenZhang1 <140119224+DichenZhang1@users.noreply.github.com> | 2024-04-05 05:32:25 -0700 |
commit | 1cdc2a99c72b82dd60462b2fe004d2238f92149f (patch) | |
tree | 5f3894444c0727417577d66f254ceabd90dd87a7 | |
parent | bd8f3c8e260ffb1e85f3ebf09124de0cde97692b (diff) | |
download | libultrahdr-1cdc2a99c72b82dd60462b2fe004d2238f92149f.tar.gz |
Update easy editing methods for all supported color formats
Test: ./ultrahdr_unit_test
Change-Id: Iede30d5ca54722d20a498c3c60d14e987b90ac07
-rw-r--r-- | lib/include/ultrahdr/editorhelper.h | 89 | ||||
-rw-r--r-- | lib/include/ultrahdr/ultrahdrcommon.h | 9 | ||||
-rw-r--r-- | lib/src/editorhelper.cpp | 506 | ||||
-rw-r--r-- | lib/src/ultrahdr_api.cpp | 168 | ||||
-rw-r--r-- | tests/editorhelper_test.cpp | 726 |
5 files changed, 727 insertions, 771 deletions
diff --git a/lib/include/ultrahdr/editorhelper.h b/lib/include/ultrahdr/editorhelper.h index 1dd904a..3a82e0e 100644 --- a/lib/include/ultrahdr/editorhelper.h +++ b/lib/include/ultrahdr/editorhelper.h @@ -17,27 +17,86 @@ #ifndef ULTRAHDR_EDITORHELPER_H #define ULTRAHDR_EDITORHELPER_H -#include "ultrahdr/ultrahdr.h" -#include "ultrahdr/jpegr.h" +#include "ultrahdr_api.h" +#include "ultrahdr/ultrahdrcommon.h" + +// todo: move this to ultrahdr_api.h +/*!\brief List of supported mirror directions */ +typedef enum uhdr_mirror_direction { + UHDR_MIRROR_VERTICAL, /**< flip image over x axis */ + UHDR_MIRROR_HORIZONTAL, /**< flip image over y axis */ +} uhdr_mirror_direction_t; /**< alias for enum uhdr_mirror_direction */ namespace ultrahdr { -typedef enum { - ULTRAHDR_MIRROR_VERTICAL, - ULTRAHDR_MIRROR_HORIZONTAL, -} ultrahdr_mirroring_direction; -status_t crop(jr_uncompressed_ptr const in_img, - int left, int right, int top, int bottom, jr_uncompressed_ptr out_img); +/*!\brief uhdr image effect descriptor */ +typedef struct uhdr_effect_desc { + virtual std::string to_string() = 0; + + virtual ~uhdr_effect_desc() = default; +} uhdr_effect_desc_t; /**< alias for struct uhdr_effect_desc */ + +/*!\brief mirror effect descriptor */ +typedef struct uhdr_mirror_effect : uhdr_effect_desc { + uhdr_mirror_effect(uhdr_mirror_direction_t direction) : m_direction{direction} {} + + std::string to_string() { + return "effect : mirror, metadata : direction - " + ((m_direction == UHDR_MIRROR_HORIZONTAL) + ? std::string{"horizontal"} + : std::string{"vertical"}); + } + + uhdr_mirror_direction_t m_direction; +} uhdr_mirror_effect_t; /**< alias for struct uhdr_mirror_effect */ + +/*!\brief rotate effect descriptor */ +typedef struct uhdr_rotate_effect : uhdr_effect_desc { + uhdr_rotate_effect(int degree) : m_degree{degree} {} + + std::string to_string() { + return "effect : rotate, metadata : degree - " + std::to_string(m_degree); + } + + int m_degree; +} uhdr_rotate_effect_t; /**< alias for struct uhdr_rotate_effect */ + +/*!\brief crop effect descriptor */ +typedef struct uhdr_crop_effect : uhdr_effect_desc { + uhdr_crop_effect(int left, int right, int top, int bottom) + : m_left{left}, m_right{right}, m_top{top}, m_bottom{bottom} {} + + std::string to_string() { + return "effect : crop, metadata : left, right, top, bottom - " + std::to_string(m_left) + " ," + + std::to_string(m_right) + " ," + std::to_string(m_top) + " ," + std::to_string(m_bottom); + } + + int m_left; + int m_right; + int m_top; + int m_bottom; +} uhdr_crop_effect_t; /**< alias for struct uhdr_crop_effect */ + +/*!\brief resize effect descriptor */ +typedef struct uhdr_resize_effect : uhdr_effect_desc { + uhdr_resize_effect(int width, int height) : m_width{width}, m_height{height} {} + + std::string to_string() { + return "effect : resize, metadata : dimensions w, h" + std::to_string(m_width) + " ," + + std::to_string(m_height); + } + + int m_width; + int m_height; +} uhdr_resize_effect_t; /**< alias for struct uhdr_resize_effect */ + +std::unique_ptr<uhdr_raw_image_ext_t> apply_rotate(uhdr_raw_image_t* src, int degree); -status_t mirror(jr_uncompressed_ptr const in_img, - ultrahdr_mirroring_direction mirror_dir, - jr_uncompressed_ptr out_img); +std::unique_ptr<uhdr_raw_image_ext_t> apply_mirror(uhdr_raw_image_t* src, + uhdr_mirror_direction_t direction); -status_t rotate(jr_uncompressed_ptr const in_img, int clockwise_degree, - jr_uncompressed_ptr out_img); +std::unique_ptr<uhdr_raw_image_ext_t> apply_resize(uhdr_raw_image* src, int dst_w, int dst_h); -status_t resize(jr_uncompressed_ptr const in_img, int out_width, int out_height, - jr_uncompressed_ptr out_img); +void apply_crop(uhdr_raw_image_t* src, int left, int top, int wd, int ht); } // namespace ultrahdr diff --git a/lib/include/ultrahdr/ultrahdrcommon.h b/lib/include/ultrahdr/ultrahdrcommon.h index 1ab512a..8bc6d2e 100644 --- a/lib/include/ultrahdr/ultrahdrcommon.h +++ b/lib/include/ultrahdr/ultrahdrcommon.h @@ -19,8 +19,10 @@ //#define LOG_NDEBUG 0 +#include <deque> #include <map> #include <memory> +#include <string> #include <vector> #include "ultrahdr_api.h" @@ -103,6 +105,9 @@ typedef struct uhdr_compressed_image_ext : uhdr_compressed_image_t { std::unique_ptr<ultrahdr::uhdr_memory_block> m_block; } uhdr_compressed_image_ext_t; /**< alias for struct uhdr_compressed_image_ext */ +/*!\brief forward declaration for image effect descriptor */ +typedef struct uhdr_effect_desc uhdr_effect_desc_t; + } // namespace ultrahdr // =============================================================================================== @@ -110,7 +115,9 @@ typedef struct uhdr_compressed_image_ext : uhdr_compressed_image_t { // =============================================================================================== struct uhdr_codec_private { - virtual ~uhdr_codec_private() = default; + std::deque<ultrahdr::uhdr_effect_desc_t*> m_effects; + + virtual ~uhdr_codec_private(); }; struct uhdr_encoder_private : uhdr_codec_private { diff --git a/lib/src/editorhelper.cpp b/lib/src/editorhelper.cpp index cdbd84e..1b33bc3 100644 --- a/lib/src/editorhelper.cpp +++ b/lib/src/editorhelper.cpp @@ -14,352 +14,224 @@ * limitations under the License. */ -#include <algorithm> -#include <cmath> -#include <string.h> - -#include <fstream> -#include <iostream> +#include <cstring> +#include <cstdint> #include "ultrahdr/editorhelper.h" -using namespace std; - namespace ultrahdr { -status_t crop(jr_uncompressed_ptr const in_img, - int left, int right, int top, int bottom, - jr_uncompressed_ptr out_img) { - if (in_img == nullptr || in_img->data == nullptr || - out_img == nullptr || out_img->data == nullptr) { - return ERROR_JPEGR_BAD_PTR; - } - if (left < 0 || right >= in_img->width || top < 0 || bottom >= in_img->height) { - return ERROR_JPEGR_INVALID_CROPPING_PARAMETERS; - } - if (in_img->pixelFormat != ULTRAHDR_PIX_FMT_YUV420 && - in_img->pixelFormat != ULTRAHDR_PIX_FMT_MONOCHROME) { - return ERROR_JPEGR_UNSUPPORTED_FEATURE; - } - - out_img->colorGamut = in_img->colorGamut; - out_img->pixelFormat = in_img->pixelFormat; - - int in_luma_stride = in_img->luma_stride != 0 ? in_img->luma_stride : in_img->width; - out_img->width = right - left + 1; - out_img->height = bottom - top + 1; - out_img->luma_stride = out_img->width; - uint8_t* src = reinterpret_cast<uint8_t*>(in_img->data) + in_luma_stride * top + left; - uint8_t* dest = reinterpret_cast<uint8_t*>(out_img->data); - - for (int i = 0; i < out_img->height; i++) { - memcpy(dest + i * out_img->luma_stride, src + i * in_luma_stride, out_img->width); - } - - if (in_img->pixelFormat == ULTRAHDR_PIX_FMT_MONOCHROME) { - return JPEGR_NO_ERROR; - } - - // Assume input is YUV 420 - int in_chroma_stride = in_img->chroma_stride != 0 ? - in_img->chroma_stride : (in_luma_stride >> 1); - out_img->chroma_stride = out_img->luma_stride / 2; - out_img->chroma_data = reinterpret_cast<uint8_t*>(out_img->data) + - out_img->luma_stride * out_img->height; - src = reinterpret_cast<uint8_t*>(in_img->chroma_data); - if (src == nullptr) { - src = reinterpret_cast<uint8_t*>(in_img->data) + in_luma_stride * in_img->height; - } - src = src + in_chroma_stride * (top / 2) + (left / 2); - dest = reinterpret_cast<uint8_t*>(out_img->chroma_data); - for (int i = 0; i < out_img->height; i++) { - memcpy(dest + i * out_img->chroma_stride, src + i * in_chroma_stride, out_img->width / 2); - } - - return JPEGR_NO_ERROR; -} - -status_t mirror(jr_uncompressed_ptr const in_img, - ultrahdr_mirroring_direction mirror_dir, - jr_uncompressed_ptr out_img) { - if (in_img == nullptr || in_img->data == nullptr || - out_img == nullptr || out_img->data == nullptr) { - return ERROR_JPEGR_BAD_PTR; - } - if (in_img->pixelFormat != ULTRAHDR_PIX_FMT_YUV420 && - in_img->pixelFormat != ULTRAHDR_PIX_FMT_MONOCHROME) { - return ERROR_JPEGR_UNSUPPORTED_FEATURE; - } - out_img->colorGamut = in_img->colorGamut; - out_img->pixelFormat = in_img->pixelFormat; - - int in_luma_stride = in_img->luma_stride != 0 ? in_img->luma_stride : in_img->width; - out_img->width = in_img->width; - out_img->height = in_img->height; - out_img->luma_stride = in_luma_stride; - if (mirror_dir == ULTRAHDR_MIRROR_VERTICAL) { - uint8_t* src = reinterpret_cast<uint8_t*>(in_img->data); - uint8_t* dest = reinterpret_cast<uint8_t*>(out_img->data); - for (int i = 0; i < out_img->height; i++) { - memcpy(dest + (out_img->height - i - 1) * out_img->luma_stride, - src + i * in_luma_stride, - out_img->width); - } - } else { - for (int i = 0; i < out_img->height; i++) { - for (int j = 0; j < out_img->width; j++) { - *(reinterpret_cast<uint8_t*>(out_img->data) + i * out_img->luma_stride + j) = - *(reinterpret_cast<uint8_t*>(in_img->data) + - i * in_luma_stride + (in_img->width - j - 1)); +template <typename T> +void rotate_buffer_clockwise(T* src_buffer, T* dst_buffer, int src_w, int src_h, int src_stride, + int dst_stride, int degree) { + if (degree == 90) { + int dst_w = src_h; + int dst_h = src_w; + for (int i = 0; i < dst_h; i++) { + for (int j = 0; j < dst_w; j++) { + dst_buffer[i * dst_stride + j] = src_buffer[(src_h - j - 1) * src_stride + i]; } } - } - - if (in_img->pixelFormat == ULTRAHDR_PIX_FMT_MONOCHROME) { - return JPEGR_NO_ERROR; - } - - // Assume input is YUV 420 - int in_chroma_stride = in_img->chroma_stride != 0 ? - in_img->chroma_stride : (in_luma_stride >> 1); - out_img->chroma_stride = out_img->luma_stride / 2; - out_img->chroma_data = reinterpret_cast<uint8_t*>(out_img->data) + - out_img->luma_stride * out_img->height; - if (mirror_dir == ULTRAHDR_MIRROR_VERTICAL) { - // U - uint8_t* src = reinterpret_cast<uint8_t*>(in_img->chroma_data); - if (src == nullptr) { - src = reinterpret_cast<uint8_t*>(in_img->data) + in_luma_stride * in_img->height; - } - uint8_t* dest = reinterpret_cast<uint8_t*>(out_img->chroma_data); - for (int i = 0; i < out_img->height / 2; i++) { - memcpy(dest + (out_img->height / 2 - i - 1) * out_img->chroma_stride, - src + i * in_chroma_stride, - out_img->width / 2); - } - // V - src = src + in_chroma_stride * (in_img->height / 2); - dest = dest + out_img->chroma_stride * (out_img->height / 2); - for (int i = 0; i < out_img->height / 2; i++) { - memcpy(dest + (out_img->height / 2 - i - 1) * out_img->chroma_stride, - src + i * in_chroma_stride, - out_img->width / 2); - } - } else { - // U - uint8_t* src = reinterpret_cast<uint8_t*>(in_img->chroma_data); - if (src == nullptr) { - src = reinterpret_cast<uint8_t*>(in_img->data) + in_luma_stride * in_img->height; - } - uint8_t* dest = reinterpret_cast<uint8_t*>(out_img->chroma_data); - for (int i = 0; i < out_img->height / 2; i++) { - for (int j = 0; j < out_img->width / 2; j++) { - *(dest + i * out_img->chroma_stride + j) = - *(src + i * in_chroma_stride + (in_img->width / 2 - j - 1)); + } else if (degree == 180) { + int dst_w = src_w; + int dst_h = src_h; + for (int i = 0; i < dst_h; i++) { + for (int j = 0; j < dst_w; j++) { + dst_buffer[i * dst_stride + j] = src_buffer[(src_h - i - 1) * src_stride + (src_w - j - 1)]; } } - // V - src = src + in_chroma_stride * (in_img->height / 2); - dest = dest + out_img->chroma_stride * (out_img->height / 2); - for (int i = 0; i < out_img->height / 2; i++) { - for (int j = 0; j < out_img->width / 2; j++) { - *(dest + i * out_img->chroma_stride + j) = - *(src + i * in_chroma_stride + (in_img->width / 2 - j - 1)); + } else if (degree == 270) { + int dst_w = src_h; + int dst_h = src_w; + for (int i = 0; i < dst_h; i++) { + for (int j = 0; j < dst_w; j++) { + dst_buffer[i * dst_stride + j] = src_buffer[j * src_stride + (src_w - i - 1)]; } } } - - return JPEGR_NO_ERROR; } -status_t rotate(jr_uncompressed_ptr const in_img, int clockwise_degree, - jr_uncompressed_ptr out_img) { - if (in_img == nullptr || in_img->data == nullptr || - out_img == nullptr || out_img->data == nullptr) { - return ERROR_JPEGR_BAD_PTR; - } - if (clockwise_degree != 90 && clockwise_degree != 180 && clockwise_degree != 270) { - return ERROR_JPEGR_INVALID_CROPPING_PARAMETERS; - } - if (in_img->pixelFormat != ULTRAHDR_PIX_FMT_YUV420 && - in_img->pixelFormat != ULTRAHDR_PIX_FMT_MONOCHROME) { - return ERROR_JPEGR_UNSUPPORTED_FEATURE; - } - - out_img->colorGamut = in_img->colorGamut; - out_img->pixelFormat = in_img->pixelFormat; - - int in_luma_stride = in_img->luma_stride != 0 ? in_img->luma_stride : in_img->width; - if (clockwise_degree == 90) { - out_img->width = in_img->height; - out_img->height = in_img->width; - out_img->luma_stride = out_img->width; - for (int i = 0; i < out_img->height; i++) { - for (int j = 0; j < out_img->width; j++) { - *(reinterpret_cast<uint8_t*>(out_img->data) + i * out_img->luma_stride + j) = - *(reinterpret_cast<uint8_t*>(in_img->data) + - (in_img->height - j - 1) * in_luma_stride + i); - } - } - } else if (clockwise_degree == 180) { - out_img->width = in_img->width; - out_img->height = in_img->height; - out_img->luma_stride = in_luma_stride; - for (int i = 0; i < out_img->height; i++) { - for (int j = 0; j < out_img->width; j++) { - *(reinterpret_cast<uint8_t*>(out_img->data) + i * out_img->luma_stride + j) = - *(reinterpret_cast<uint8_t*>(in_img->data) + - (in_img->height - i - 1) * in_luma_stride + (in_img->width - j - 1)); - } - } - } else if (clockwise_degree == 270) { - out_img->width = in_img->height; - out_img->height = in_img->width; - out_img->luma_stride = out_img->width; - for (int i = 0; i < out_img->height; i++) { - for (int j = 0; j < out_img->width; j++) { - *(reinterpret_cast<uint8_t*>(out_img->data) + i * out_img->luma_stride + j) = - *(reinterpret_cast<uint8_t*>(in_img->data) + - j * in_luma_stride + (in_img->width - i - 1)); +template <typename T> +void mirror_buffer(T* src_buffer, T* dst_buffer, int src_w, int src_h, int src_stride, + int dst_stride, uhdr_mirror_direction_t direction) { + if (direction == UHDR_MIRROR_VERTICAL) { + for (int i = 0; i < src_h; i++) { + memcpy(&dst_buffer[(src_h - i - 1) * dst_stride], &src_buffer[i * src_stride], + src_w * sizeof(T)); + } + } else if (direction == UHDR_MIRROR_HORIZONTAL) { + for (int i = 0; i < src_h; i++) { + for (int j = 0; j < src_w; j++) { + dst_buffer[i * dst_stride + j] = src_buffer[i * src_stride + (src_w - j - 1)]; } } } +} - if (in_img->pixelFormat == ULTRAHDR_PIX_FMT_MONOCHROME) { - return JPEGR_NO_ERROR; +template <typename T> +void resize_buffer(T* src_buffer, T* dst_buffer, int src_w, int src_h, int dst_w, int dst_h, + int src_stride, int dst_stride) { + for (int i = 0; i < dst_h; i++) { + for (int j = 0; j < dst_w; j++) { + dst_buffer[i * dst_stride + j] = + src_buffer[i * (src_h / dst_h) * src_stride + j * (src_w / dst_w)]; + } } +} - // Assume input is YUV 420 - int in_chroma_stride = in_img->chroma_stride != 0 ? - in_img->chroma_stride : (in_luma_stride >> 1); - out_img->chroma_stride = out_img->luma_stride / 2; - out_img->chroma_data = reinterpret_cast<uint8_t*>(out_img->data) + - out_img->luma_stride * out_img->height; - if (clockwise_degree == 90) { - // U - uint8_t* src = reinterpret_cast<uint8_t*>(in_img->chroma_data); - if (src == nullptr) { - src = reinterpret_cast<uint8_t*>(in_img->data) + in_luma_stride * in_img->height; - } - uint8_t* dest = reinterpret_cast<uint8_t*>(out_img->chroma_data); - for (int i = 0; i < out_img->height / 2; i++) { - for (int j = 0; j < out_img->width / 2; j++) { - *(dest + i * out_img->chroma_stride + j) = - *(src + (in_img->height / 2 - j - 1) * in_chroma_stride + i); - } - } - // V - src = src + in_chroma_stride * (in_img->height / 2); - dest = dest + out_img->chroma_stride * (out_img->height / 2); - for (int i = 0; i < out_img->height / 2; i++) { - for (int j = 0; j < out_img->width / 2; j++) { - *(dest + i * out_img->chroma_stride + j) = - *(src + (in_img->height / 2 - j - 1) * in_chroma_stride + i); - } - } - } else if (clockwise_degree == 180) { - // U - uint8_t* src = reinterpret_cast<uint8_t*>(in_img->chroma_data); - if (src == nullptr) { - src = reinterpret_cast<uint8_t*>(in_img->data) + in_luma_stride * in_img->height; - } - uint8_t* dest = reinterpret_cast<uint8_t*>(out_img->chroma_data); - for (int i = 0; i < out_img->height / 2; i++) { - for (int j = 0; j < out_img->width / 2; j++) { - *(dest + i * out_img->chroma_stride + j) = - *(src + (in_img->height / 2 - i - 1) * in_chroma_stride + - (in_img->width / 2 - j - 1)); - } - } - // V - src = src + in_chroma_stride * (in_img->height / 2); - dest = dest + out_img->chroma_stride * (out_img->height / 2); - for (int i = 0; i < out_img->height / 2; i++) { - for (int j = 0; j < out_img->width / 2; j++) { - *(dest + i * out_img->chroma_stride + j) = - *(src + (in_img->height / 2 - i - 1) * in_chroma_stride + - (in_img->width / 2 - j - 1)); - } - } - } else if (clockwise_degree == 270) { - // U - uint8_t* src = reinterpret_cast<uint8_t*>(in_img->chroma_data); - if (src == nullptr) { - src = reinterpret_cast<uint8_t*>(in_img->data) + in_luma_stride * in_img->height; - } - uint8_t* dest = reinterpret_cast<uint8_t*>(out_img->chroma_data); - for (int i = 0; i < out_img->height / 2; i++) { - for (int j = 0; j < out_img->width / 2; j++) { - *(dest + i * out_img->chroma_stride + j) = - *(src + j * in_chroma_stride + (in_img->width / 2 - i - 1)); +std::unique_ptr<uhdr_raw_image_ext_t> apply_rotate(uhdr_raw_image_t* src, int degree) { + std::unique_ptr<uhdr_raw_image_ext_t> dst; + + if (degree == 90 || degree == 270) { + dst = std::make_unique<uhdr_raw_image_ext_t>(src->fmt, src->cg, src->ct, src->range, src->h, + src->w, 1); + } else if (degree == 180) { + dst = std::make_unique<uhdr_raw_image_ext_t>(src->fmt, src->cg, src->ct, src->range, src->w, + src->h, 1); + } else { + return nullptr; + } + + if (src->fmt == UHDR_IMG_FMT_24bppYCbCrP010) { + uint16_t* src_buffer = static_cast<uint16_t*>(src->planes[UHDR_PLANE_Y]); + uint16_t* dst_buffer = static_cast<uint16_t*>(dst->planes[UHDR_PLANE_Y]); + rotate_buffer_clockwise(src_buffer, dst_buffer, src->w, src->h, src->stride[UHDR_PLANE_Y], + dst->stride[UHDR_PLANE_Y], degree); + uint32_t* src_uv_buffer = static_cast<uint32_t*>(src->planes[UHDR_PLANE_UV]); + uint32_t* dst_uv_buffer = static_cast<uint32_t*>(dst->planes[UHDR_PLANE_UV]); + rotate_buffer_clockwise(src_uv_buffer, dst_uv_buffer, src->w / 2, src->h / 2, + src->stride[UHDR_PLANE_UV] / 2, dst->stride[UHDR_PLANE_UV] / 2, degree); + } else if (src->fmt == UHDR_IMG_FMT_12bppYCbCr420 || src->fmt == UHDR_IMG_FMT_8bppYCbCr400) { + uint8_t* src_buffer = static_cast<uint8_t*>(src->planes[UHDR_PLANE_Y]); + uint8_t* dst_buffer = static_cast<uint8_t*>(dst->planes[UHDR_PLANE_Y]); + rotate_buffer_clockwise(src_buffer, dst_buffer, src->w, src->h, src->stride[UHDR_PLANE_Y], + dst->stride[UHDR_PLANE_Y], degree); + if (src->fmt == UHDR_IMG_FMT_12bppYCbCr420) { + for (int i = 1; i < 3; i++) { + src_buffer = static_cast<uint8_t*>(src->planes[i]); + dst_buffer = static_cast<uint8_t*>(dst->planes[i]); + rotate_buffer_clockwise(src_buffer, dst_buffer, src->w / 2, src->h / 2, src->stride[i], + dst->stride[i], degree); } } - // V - src = src + in_chroma_stride * (in_img->height / 2); - dest = dest + out_img->chroma_stride * (out_img->height / 2); - for (int i = 0; i < out_img->height / 2; i++) { - for (int j = 0; j < out_img->width / 2; j++) { - *(dest + i * out_img->chroma_stride + j) = - *(src + j * in_chroma_stride + (in_img->width / 2 - i - 1)); + } else if (src->fmt == UHDR_IMG_FMT_32bppRGBA1010102 || src->fmt == UHDR_IMG_FMT_32bppRGBA8888) { + uint32_t* src_buffer = static_cast<uint32_t*>(src->planes[UHDR_PLANE_PACKED]); + uint32_t* dst_buffer = static_cast<uint32_t*>(dst->planes[UHDR_PLANE_PACKED]); + rotate_buffer_clockwise(src_buffer, dst_buffer, src->w, src->h, src->stride[UHDR_PLANE_PACKED], + dst->stride[UHDR_PLANE_PACKED], degree); + } else if (src->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat) { + uint64_t* src_buffer = static_cast<uint64_t*>(src->planes[UHDR_PLANE_PACKED]); + uint64_t* dst_buffer = static_cast<uint64_t*>(dst->planes[UHDR_PLANE_PACKED]); + rotate_buffer_clockwise(src_buffer, dst_buffer, src->w, src->h, src->stride[UHDR_PLANE_PACKED], + dst->stride[UHDR_PLANE_PACKED], degree); + } + return std::move(dst); +} + +std::unique_ptr<uhdr_raw_image_ext_t> apply_mirror(uhdr_raw_image_t* src, + uhdr_mirror_direction_t direction) { + std::unique_ptr<uhdr_raw_image_ext_t> dst = std::make_unique<uhdr_raw_image_ext_t>( + src->fmt, src->cg, src->ct, src->range, src->w, src->h, 1); + + if (src->fmt == UHDR_IMG_FMT_24bppYCbCrP010) { + uint16_t* src_buffer = static_cast<uint16_t*>(src->planes[UHDR_PLANE_Y]); + uint16_t* dst_buffer = static_cast<uint16_t*>(dst->planes[UHDR_PLANE_Y]); + mirror_buffer(src_buffer, dst_buffer, src->w, src->h, src->stride[UHDR_PLANE_Y], + dst->stride[UHDR_PLANE_Y], direction); + uint32_t* src_uv_buffer = static_cast<uint32_t*>(src->planes[UHDR_PLANE_UV]); + uint32_t* dst_uv_buffer = static_cast<uint32_t*>(dst->planes[UHDR_PLANE_UV]); + mirror_buffer(src_uv_buffer, dst_uv_buffer, src->w / 2, src->h / 2, + src->stride[UHDR_PLANE_UV] / 2, dst->stride[UHDR_PLANE_UV] / 2, direction); + } else if (src->fmt == UHDR_IMG_FMT_12bppYCbCr420 || src->fmt == UHDR_IMG_FMT_8bppYCbCr400) { + uint8_t* src_buffer = static_cast<uint8_t*>(src->planes[UHDR_PLANE_Y]); + uint8_t* dst_buffer = static_cast<uint8_t*>(dst->planes[UHDR_PLANE_Y]); + mirror_buffer(src_buffer, dst_buffer, src->w, src->h, src->stride[UHDR_PLANE_Y], + dst->stride[UHDR_PLANE_Y], direction); + if (src->fmt == UHDR_IMG_FMT_12bppYCbCr420) { + for (int i = 1; i < 3; i++) { + src_buffer = static_cast<uint8_t*>(src->planes[i]); + dst_buffer = static_cast<uint8_t*>(dst->planes[i]); + mirror_buffer(src_buffer, dst_buffer, src->w / 2, src->h / 2, src->stride[i], + dst->stride[i], direction); } } - } - - return JPEGR_NO_ERROR; + } else if (src->fmt == UHDR_IMG_FMT_32bppRGBA1010102 || src->fmt == UHDR_IMG_FMT_32bppRGBA8888) { + uint32_t* src_buffer = static_cast<uint32_t*>(src->planes[UHDR_PLANE_PACKED]); + uint32_t* dst_buffer = static_cast<uint32_t*>(dst->planes[UHDR_PLANE_PACKED]); + mirror_buffer(src_buffer, dst_buffer, src->w, src->h, src->stride[UHDR_PLANE_PACKED], + dst->stride[UHDR_PLANE_PACKED], direction); + } else if (src->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat) { + uint64_t* src_buffer = static_cast<uint64_t*>(src->planes[UHDR_PLANE_PACKED]); + uint64_t* dst_buffer = static_cast<uint64_t*>(dst->planes[UHDR_PLANE_PACKED]); + mirror_buffer(src_buffer, dst_buffer, src->w, src->h, src->stride[UHDR_PLANE_PACKED], + dst->stride[UHDR_PLANE_PACKED], direction); + } + return std::move(dst); } -status_t resize(jr_uncompressed_ptr const in_img, int out_width, int out_height, - jr_uncompressed_ptr out_img) { - if (in_img == nullptr || in_img->data == nullptr || - out_img == nullptr || out_img->data == nullptr) { - return ERROR_JPEGR_BAD_PTR; - } - if (in_img->pixelFormat != ULTRAHDR_PIX_FMT_YUV420 && - in_img->pixelFormat != ULTRAHDR_PIX_FMT_MONOCHROME) { - return ERROR_JPEGR_UNSUPPORTED_FEATURE; - } - - out_img->colorGamut = in_img->colorGamut; - out_img->pixelFormat = in_img->pixelFormat; - - int in_luma_stride = in_img->luma_stride != 0 ? in_img->luma_stride : in_img->width; - out_img->width = out_width; - out_img->height = out_height; - out_img->luma_stride = out_img->width; - - for (int i = 0; i < out_img->height; i++) { - for (int j = 0; j < out_img->width; j++) { - *(reinterpret_cast<uint8_t*>(out_img->data) + i * out_img->luma_stride + j) = - *(reinterpret_cast<uint8_t*>(in_img->data) + - i * in_img->height / out_img->height * in_luma_stride + - j * in_img->width / out_img->width ); +void apply_crop(uhdr_raw_image_t* src, int left, int top, int wd, int ht) { + if (src->fmt == UHDR_IMG_FMT_24bppYCbCrP010) { + uint16_t* src_buffer = static_cast<uint16_t*>(src->planes[UHDR_PLANE_Y]); + src->planes[UHDR_PLANE_Y] = &src_buffer[top * src->stride[UHDR_PLANE_Y] + left]; + uint32_t* src_uv_buffer = static_cast<uint32_t*>(src->planes[UHDR_PLANE_UV]); + src->planes[UHDR_PLANE_UV] = + &src_uv_buffer[(top / 2) * (src->stride[UHDR_PLANE_UV] / 2) + (left / 2)]; + } else if (src->fmt == UHDR_IMG_FMT_12bppYCbCr420 || src->fmt == UHDR_IMG_FMT_8bppYCbCr400) { + uint8_t* src_buffer = static_cast<uint8_t*>(src->planes[UHDR_PLANE_Y]); + src->planes[UHDR_PLANE_Y] = &src_buffer[top * src->stride[UHDR_PLANE_Y] + left]; + if (src->fmt == UHDR_IMG_FMT_12bppYCbCr420) { + for (int i = 1; i < 3; i++) { + src->planes[i] = &src_buffer[(top / 2) * src->stride[i] + (left / 2)]; + } } + } else if (src->fmt == UHDR_IMG_FMT_32bppRGBA1010102 || src->fmt == UHDR_IMG_FMT_32bppRGBA8888) { + uint32_t* src_buffer = static_cast<uint32_t*>(src->planes[UHDR_PLANE_PACKED]); + src->planes[UHDR_PLANE_PACKED] = &src_buffer[top * src->stride[UHDR_PLANE_PACKED] + left]; + } else if (src->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat) { + uint64_t* src_buffer = static_cast<uint64_t*>(src->planes[UHDR_PLANE_PACKED]); + src->planes[UHDR_PLANE_PACKED] = &src_buffer[top * src->stride[UHDR_PLANE_PACKED] + left]; } + src->w = wd; + src->h = ht; +} - if (in_img->pixelFormat == ULTRAHDR_PIX_FMT_MONOCHROME) { - return JPEGR_NO_ERROR; - } - - // Assume input is YUV 420 - int in_chroma_stride = in_img->chroma_stride != 0 ? - in_img->chroma_stride : (in_luma_stride >> 1); - out_img->chroma_stride = out_img->luma_stride / 2; - out_img->chroma_data = reinterpret_cast<uint8_t*>(out_img->data) + - out_img->luma_stride * out_img->height; - uint8_t* src = reinterpret_cast<uint8_t*>(in_img->chroma_data); - if (src == nullptr) { - src = reinterpret_cast<uint8_t*>(in_img->data) + in_luma_stride * in_img->height; - } - uint8_t* dest = reinterpret_cast<uint8_t*>(out_img->chroma_data); - for (int i = 0; i < out_img->height; i++) { - for (int j = 0; j < out_img->width / 2; j++) { - *(dest + i * out_img->chroma_stride + j) = - *(src + i * in_img->height / out_img->height * in_chroma_stride + - j * in_img->width / out_img->width ); +std::unique_ptr<uhdr_raw_image_ext_t> apply_resize(uhdr_raw_image_t* src, int dst_w, int dst_h) { + std::unique_ptr<uhdr_raw_image_ext_t> dst = std::make_unique<uhdr_raw_image_ext_t>( + src->fmt, src->cg, src->ct, src->range, dst_w, dst_h, 1); + + if (src->fmt == UHDR_IMG_FMT_24bppYCbCrP010) { + uint16_t* src_buffer = static_cast<uint16_t*>(src->planes[UHDR_PLANE_Y]); + uint16_t* dst_buffer = static_cast<uint16_t*>(dst->planes[UHDR_PLANE_Y]); + resize_buffer(src_buffer, dst_buffer, src->w, src->h, dst->w, dst->h, src->stride[UHDR_PLANE_Y], + dst->stride[UHDR_PLANE_Y]); + uint32_t* src_uv_buffer = static_cast<uint32_t*>(src->planes[UHDR_PLANE_UV]); + uint32_t* dst_uv_buffer = static_cast<uint32_t*>(dst->planes[UHDR_PLANE_UV]); + resize_buffer(src_uv_buffer, dst_uv_buffer, src->w / 4, src->h / 2, dst->w / 4, dst->h / 2, + src->stride[UHDR_PLANE_UV] / 2, dst->stride[UHDR_PLANE_UV] / 2); + } else if (src->fmt == UHDR_IMG_FMT_12bppYCbCr420 || src->fmt == UHDR_IMG_FMT_8bppYCbCr400) { + uint8_t* src_buffer = static_cast<uint8_t*>(src->planes[UHDR_PLANE_Y]); + uint8_t* dst_buffer = static_cast<uint8_t*>(dst->planes[UHDR_PLANE_Y]); + resize_buffer(src_buffer, dst_buffer, src->w, src->h, dst->w, dst->h, src->stride[UHDR_PLANE_Y], + dst->stride[UHDR_PLANE_Y]); + if (src->fmt == UHDR_IMG_FMT_12bppYCbCr420) { + for (int i = 1; i < 3; i++) { + src_buffer = static_cast<uint8_t*>(src->planes[i]); + dst_buffer = static_cast<uint8_t*>(dst->planes[i]); + resize_buffer(src_buffer, dst_buffer, src->w / 2, src->h / 2, dst->w / 2, dst->h / 2, + src->stride[i], dst->stride[i]); + } } - } - - return JPEGR_NO_ERROR; + } else if (src->fmt == UHDR_IMG_FMT_32bppRGBA1010102 || src->fmt == UHDR_IMG_FMT_32bppRGBA8888) { + uint32_t* src_buffer = static_cast<uint32_t*>(src->planes[UHDR_PLANE_PACKED]); + uint32_t* dst_buffer = static_cast<uint32_t*>(dst->planes[UHDR_PLANE_PACKED]); + resize_buffer(src_buffer, dst_buffer, src->w, src->h, dst->w, dst->h, + src->stride[UHDR_PLANE_PACKED], dst->stride[UHDR_PLANE_PACKED]); + } else if (src->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat) { + uint64_t* src_buffer = static_cast<uint64_t*>(src->planes[UHDR_PLANE_PACKED]); + uint64_t* dst_buffer = static_cast<uint64_t*>(dst->planes[UHDR_PLANE_PACKED]); + resize_buffer(src_buffer, dst_buffer, src->w, src->h, dst->w, dst->h, + src->stride[UHDR_PLANE_PACKED], dst->stride[UHDR_PLANE_PACKED]); + } + return std::move(dst); } - } // namespace ultrahdr diff --git a/lib/src/ultrahdr_api.cpp b/lib/src/ultrahdr_api.cpp index 7d0f7f9..17280f2 100644 --- a/lib/src/ultrahdr_api.cpp +++ b/lib/src/ultrahdr_api.cpp @@ -19,6 +19,7 @@ #include "ultrahdr_api.h" #include "ultrahdr/ultrahdrcommon.h" +#include "ultrahdr/editorhelper.h" #include "ultrahdr/jpegr.h" #include "ultrahdr/jpegrutils.h" @@ -102,8 +103,81 @@ uhdr_compressed_image_ext::uhdr_compressed_image_ext(uhdr_color_gamut_t cg, this->range = range; } +uhdr_error_info_t apply_effects(uhdr_decoder_private* dec) { + for (auto& it : dec->m_effects) { + std::unique_ptr<ultrahdr::uhdr_raw_image_ext_t> disp_img = nullptr; + std::unique_ptr<ultrahdr::uhdr_raw_image_ext_t> gm_img = nullptr; + + if (nullptr != dynamic_cast<uhdr_rotate_effect_t*>(it)) { + int degree = (dynamic_cast<ultrahdr::uhdr_rotate_effect_t*>(it))->m_degree; + disp_img = apply_rotate(dec->m_decoded_img_buffer.get(), degree); + gm_img = apply_rotate(dec->m_gainmap_img_buffer.get(), degree); + } else if (nullptr != dynamic_cast<uhdr_mirror_effect_t*>(it)) { + uhdr_mirror_direction_t direction = (dynamic_cast<uhdr_mirror_effect_t*>(it))->m_direction; + disp_img = apply_mirror(dec->m_decoded_img_buffer.get(), direction); + gm_img = apply_mirror(dec->m_gainmap_img_buffer.get(), direction); + } else if (nullptr != dynamic_cast<uhdr_crop_effect_t*>(it)) { + auto crop_effect = dynamic_cast<uhdr_crop_effect_t*>(it); + uhdr_raw_image_t* disp = dec->m_decoded_img_buffer.get(); + uhdr_raw_image_t* gm = dec->m_gainmap_img_buffer.get(); + int left = (std::max)(0, crop_effect->m_left); + int right = (std::min)((int)disp->w, crop_effect->m_right); + int top = (std::max)(0, crop_effect->m_top); + int bottom = (std::min)((int)disp->h, crop_effect->m_bottom); + int scale_factor = disp->w / gm->w; + + if (right - left <= scale_factor || bottom - top <= scale_factor) { + uhdr_error_info_t status; + status.error_code = UHDR_CODEC_UNKNOWN_ERROR; + status.has_detail = 1; + snprintf(status.detail, sizeof status.detail, + "After crop image dimensions are <= 0, display image dimensions %dx%d, gain map " + "image dimensions %dx%d", + right - left, bottom - top, (right - left) / scale_factor, + (bottom - top) / scale_factor); + return status; + } + apply_crop(disp, left, top, right - left, bottom - top); + apply_crop(gm, left / scale_factor, top / scale_factor, (right - left) / scale_factor, + (bottom - top) / scale_factor); + continue; + } else if (nullptr != dynamic_cast<uhdr_resize_effect_t*>(it)) { + auto resize_effect = dynamic_cast<uhdr_resize_effect_t*>(it); + int dst_w = resize_effect->m_width; + int dst_h = resize_effect->m_height; + if (dst_w == 0 || dst_h == 0) { + uhdr_error_info_t status; + status.error_code = UHDR_CODEC_INVALID_PARAM; + snprintf(status.detail, sizeof status.detail, + "destination width or destination height cannot be zero"); + return status; + } + disp_img = apply_resize(dec->m_decoded_img_buffer.get(), dst_w, dst_h); + gm_img = apply_resize(dec->m_gainmap_img_buffer.get(), dst_w, dst_h); + } + + if (disp_img == nullptr || gm_img == nullptr) { + uhdr_error_info_t status; + status.error_code = UHDR_CODEC_UNKNOWN_ERROR; + status.has_detail = 1; + snprintf(status.detail, sizeof status.detail, + "encountered unknown error while applying effect %s", it->to_string().c_str()); + return status; + } + dec->m_decoded_img_buffer = std::move(disp_img); + dec->m_gainmap_img_buffer = std::move(gm_img); + } + + return g_no_error; +} + } // namespace ultrahdr +uhdr_codec_private::~uhdr_codec_private() { + for (auto it : m_effects) delete it; + m_effects.clear(); +} + ultrahdr::ultrahdr_pixel_format map_pix_fmt_to_internal_pix_fmt(uhdr_img_fmt_t fmt) { switch (fmt) { case UHDR_IMG_FMT_12bppYCbCr420: @@ -682,6 +756,13 @@ uhdr_error_info_t uhdr_encode(uhdr_codec_private_t* enc) { uhdr_error_info_t& status = handle->m_encode_call_status; + if (handle->m_effects.size() != 0) { + status.error_code = UHDR_CODEC_INVALID_PARAM; + status.has_detail = 1; + snprintf(status.detail, sizeof status.detail, "image effects are not currently enabled"); + return status; + } + ultrahdr::status_t internal_status = ultrahdr::JPEGR_NO_ERROR; if (handle->m_output_format == UHDR_CODEC_JPG) { ultrahdr::jpegr_exif_struct exif{}; @@ -836,6 +917,8 @@ void uhdr_reset_encoder(uhdr_codec_private_t* enc) { uhdr_encoder_private* handle = dynamic_cast<uhdr_encoder_private*>(enc); // clear entries and restore defaults + for (auto it : handle->m_effects) delete it; + handle->m_effects.clear(); handle->m_raw_images.clear(); handle->m_compressed_images.clear(); handle->m_quality.clear(); @@ -1249,6 +1332,10 @@ uhdr_error_info_t uhdr_decode(uhdr_codec_private_t* dec) { handle->m_decoded_img_buffer->cg = map_internal_cg_to_cg(dest.colorGamut); } + if (status.error_code == UHDR_CODEC_OK && dec->m_effects.size() != 0) { + status = ultrahdr::apply_effects(handle); + } + return status; } @@ -1283,6 +1370,8 @@ void uhdr_reset_decoder(uhdr_codec_private_t* dec) { uhdr_decoder_private* handle = dynamic_cast<uhdr_decoder_private*>(dec); // clear entries and restore defaults + for (auto it : handle->m_effects) delete it; + handle->m_effects.clear(); handle->m_uhdr_compressed_img.reset(); handle->m_output_fmt = UHDR_IMG_FMT_64bppRGBAHalfFloat; handle->m_output_ct = UHDR_CT_LINEAR; @@ -1308,3 +1397,82 @@ void uhdr_reset_decoder(uhdr_codec_private_t* dec) { handle->m_decode_call_status = g_no_error; } } + +uhdr_error_info_t uhdr_add_effect_mirror(uhdr_codec_private_t* codec, + uhdr_mirror_direction_t direction) { + uhdr_error_info_t status = g_no_error; + + if (codec == nullptr) { + status.error_code = UHDR_CODEC_INVALID_PARAM; + status.has_detail = 1; + snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance"); + return status; + } + + if (direction != UHDR_MIRROR_HORIZONTAL && direction != UHDR_MIRROR_VERTICAL) { + status.error_code = UHDR_CODEC_INVALID_PARAM; + status.has_detail = 1; + snprintf( + status.detail, sizeof status.detail, + "unsupported direction, expects one of {UHDR_MIRROR_HORIZONTAL, UHDR_MIRROR_VERTICAL}"); + return status; + } + + codec->m_effects.push_back(new ultrahdr::uhdr_mirror_effect_t(direction)); + + return status; +} + +uhdr_error_info_t uhdr_add_effect_rotate(uhdr_codec_private_t* codec, int degrees) { + uhdr_error_info_t status = g_no_error; + + if (codec == nullptr) { + status.error_code = UHDR_CODEC_INVALID_PARAM; + status.has_detail = 1; + snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance"); + return status; + } + + if (degrees != 90 && degrees != 180 && degrees != 270) { + status.error_code = UHDR_CODEC_INVALID_PARAM; + status.has_detail = 1; + snprintf(status.detail, sizeof status.detail, + "unsupported degrees, expects one of {90, 180, 270}"); + return status; + } + + codec->m_effects.push_back(new ultrahdr::uhdr_rotate_effect_t(degrees)); + + return status; +} + +uhdr_error_info_t uhdr_add_effect_crop(uhdr_codec_private_t* codec, int left, int right, int top, + int bottom) { + uhdr_error_info_t status = g_no_error; + + if (codec == nullptr) { + status.error_code = UHDR_CODEC_INVALID_PARAM; + status.has_detail = 1; + snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance"); + return status; + } + + codec->m_effects.push_back(new ultrahdr::uhdr_crop_effect_t(left, right, top, bottom)); + + return status; +} + +uhdr_error_info_t uhdr_add_effect_resize(uhdr_codec_private_t* codec, int width, int height) { + uhdr_error_info_t status = g_no_error; + + if (codec == nullptr) { + status.error_code = UHDR_CODEC_INVALID_PARAM; + status.has_detail = 1; + snprintf(status.detail, sizeof status.detail, "received nullptr for uhdr codec instance"); + return status; + } + + codec->m_effects.push_back(new ultrahdr::uhdr_resize_effect_t(width, height)); + + return status; +} diff --git a/tests/editorhelper_test.cpp b/tests/editorhelper_test.cpp index 7994ef4..d08ea7c 100644 --- a/tests/editorhelper_test.cpp +++ b/tests/editorhelper_test.cpp @@ -20,15 +20,71 @@ #include <iostream> #include "ultrahdr/editorhelper.h" -#include "ultrahdr/jpegr.h" -//#define DUMP_OUTPUT +// #define DUMP_OUTPUT + +#define OUTPUT_P010_IMAGE "output.p010" +#define OUTPUT_YUV_IMAGE "output.yuv" +#define OUTPUT_RGBA_IMAGE "output.rgb" #ifdef DUMP_OUTPUT -static bool writeFile(const char* filename, void*& result, int length) { +static bool writeFile(std::string prefixName, uhdr_raw_image_t* img) { + char filename[50]; + + if (img->fmt == UHDR_IMG_FMT_24bppYCbCrP010) { + snprintf(filename, sizeof filename, "%s_%d_%s", prefixName.c_str(), img->fmt, + OUTPUT_P010_IMAGE); + } else if (img->fmt == UHDR_IMG_FMT_12bppYCbCr420 || img->fmt == UHDR_IMG_FMT_8bppYCbCr400) { + snprintf(filename, sizeof filename, "%s_%d_%s", prefixName.c_str(), img->fmt, OUTPUT_YUV_IMAGE); + } else if (img->fmt == UHDR_IMG_FMT_32bppRGBA1010102 || img->fmt == UHDR_IMG_FMT_32bppRGBA8888 || + img->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat) { + snprintf(filename, sizeof filename, "%s_%d_%s", prefixName.c_str(), img->fmt, + OUTPUT_RGBA_IMAGE); + } else { + return false; + } + std::ofstream ofd(filename, std::ios::binary); if (ofd.is_open()) { - ofd.write(static_cast<char*>(result), length); + int bpp = 1; + + if (img->fmt == UHDR_IMG_FMT_24bppYCbCrP010) { + bpp = 2; + } else if (img->fmt == UHDR_IMG_FMT_32bppRGBA8888 || + img->fmt == UHDR_IMG_FMT_32bppRGBA1010102) { + bpp = 4; + } else if (img->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat) { + bpp = 8; + } + + const char* data = static_cast<char*>(img->planes[UHDR_PLANE_Y]); + size_t stride = img->stride[UHDR_PLANE_Y] * bpp; + size_t length = img->w * bpp; + for (int i = 0; i < img->h; i++, data += stride) { + ofd.write(data, length); + } + + if (img->fmt == UHDR_IMG_FMT_24bppYCbCrP010) { + data = static_cast<char*>(img->planes[UHDR_PLANE_UV]); + size_t stride = img->stride[UHDR_PLANE_UV] * bpp; + size_t length = img->w * bpp; + for (int i = 0; i < img->h / 2; i++, data += stride) { + ofd.write(data, length); + } + } else if (img->fmt == UHDR_IMG_FMT_12bppYCbCr420) { + data = static_cast<char*>(img->planes[UHDR_PLANE_U]); + size_t stride = img->stride[UHDR_PLANE_U] * bpp; + size_t length = (img->w / 2) * bpp; + for (int i = 0; i < img->h / 2; i++, data += stride) { + ofd.write(data, length); + } + data = static_cast<char*>(img->planes[UHDR_PLANE_V]); + size_t stride = img->stride[UHDR_PLANE_V] * bpp; + size_t length = (img->w / 2) * bpp; + for (int i = 0; i < img->h / 2; i++, data += stride) { + ofd.write(data, length); + } + } return true; } std::cerr << "unable to write to file : " << filename << std::endl; @@ -38,477 +94,271 @@ static bool writeFile(const char* filename, void*& result, int length) { namespace ultrahdr { -#ifdef __ANDROID__ -#define YUV_IMAGE "/data/local/tmp/minnie-320x240.yu12" -#define GREY_IMAGE "/data/local/tmp/minnie-320x240.y" -#else -#define YUV_IMAGE "./data/minnie-320x240.yu12" -#define GREY_IMAGE "./data/minnie-320x240.y" -#endif -#define IMAGE_WIDTH 320 -#define IMAGE_HEIGHT 240 - -class EditorHelperTest : public testing::Test { - public: - struct Image { - std::unique_ptr<uint8_t[]> buffer; - size_t size; - }; - EditorHelperTest(); - ~EditorHelperTest(); - - protected: - virtual void SetUp(); - virtual void TearDown(); - - Image mYuvImage, mGreyImage; -}; - -EditorHelperTest::EditorHelperTest() {} - -EditorHelperTest::~EditorHelperTest() {} - -static bool loadFile(const char filename[], EditorHelperTest::Image* result) { - std::ifstream ifd(filename, std::ios::binary | std::ios::ate); +static bool loadFile(const char* filename, uhdr_raw_image_t* handle) { + std::ifstream ifd(filename, std::ios::binary); if (ifd.good()) { - int size = ifd.tellg(); - ifd.seekg(0, std::ios::beg); - result->buffer.reset(new uint8_t[size]); - ifd.read(reinterpret_cast<char*>(result->buffer.get()), size); - ifd.close(); - result->size = size; - return true; + if (handle->fmt == UHDR_IMG_FMT_24bppYCbCrP010) { + const int bpp = 2; + ifd.read(static_cast<char*>(handle->planes[UHDR_PLANE_Y]), handle->w * handle->h * bpp); + ifd.read(static_cast<char*>(handle->planes[UHDR_PLANE_UV]), + (handle->w / 2) * (handle->h / 2) * bpp * 2); + return true; + } else if (handle->fmt == UHDR_IMG_FMT_12bppYCbCr420) { + ifd.read(static_cast<char*>(handle->planes[UHDR_PLANE_Y]), handle->w * handle->h); + ifd.read(static_cast<char*>(handle->planes[UHDR_PLANE_U]), (handle->w / 2) * (handle->h / 2)); + ifd.read(static_cast<char*>(handle->planes[UHDR_PLANE_V]), (handle->w / 2) * (handle->h / 2)); + return true; + } else if (handle->fmt == UHDR_IMG_FMT_32bppRGBA8888 || + handle->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat || + handle->fmt == UHDR_IMG_FMT_32bppRGBA1010102) { + int bpp = handle->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat ? 8 : 4; + ifd.read(static_cast<char*>(handle->planes[UHDR_PLANE_PACKED]), handle->w * handle->h * bpp); + return true; + } else if (handle->fmt == UHDR_IMG_FMT_8bppYCbCr400) { + ifd.read(static_cast<char*>(handle->planes[UHDR_PLANE_Y]), handle->w * handle->h); + return true; + } + return false; } + std::cerr << "unable to open file : " << filename << std::endl; return false; } -void EditorHelperTest::SetUp() { - if (!loadFile(YUV_IMAGE, &mYuvImage)) { - FAIL() << "Load file " << YUV_IMAGE << " failed"; - } - if (!loadFile(GREY_IMAGE, &mGreyImage)) { - FAIL() << "Load file " << GREY_IMAGE << " failed"; +void initImageHandle(uhdr_raw_image_t* handle, int width, int height, uhdr_img_fmt_t format) { + handle->fmt = format; + handle->cg = UHDR_CG_DISPLAY_P3; + handle->ct = UHDR_CT_SRGB; + handle->range = UHDR_CR_UNSPECIFIED; + handle->w = width; + handle->h = height; + if (handle->fmt == UHDR_IMG_FMT_24bppYCbCrP010) { + handle->planes[UHDR_PLANE_Y] = malloc(width * height * 2); + handle->planes[UHDR_PLANE_UV] = malloc((width / 2) * (height / 2) * 2 * 2); + handle->planes[UHDR_PLANE_V] = nullptr; + handle->stride[UHDR_PLANE_Y] = width; + handle->stride[UHDR_PLANE_UV] = width; + handle->stride[UHDR_PLANE_V] = 0; + } else if (handle->fmt == UHDR_IMG_FMT_12bppYCbCr420) { + handle->planes[UHDR_PLANE_Y] = malloc(width * height); + handle->planes[UHDR_PLANE_U] = malloc((width / 2) * (height / 2)); + handle->planes[UHDR_PLANE_V] = malloc((width / 2) * (height / 2)); + handle->stride[UHDR_PLANE_Y] = width; + handle->stride[UHDR_PLANE_U] = width / 2; + handle->stride[UHDR_PLANE_V] = width / 2; + } else if (handle->fmt == UHDR_IMG_FMT_32bppRGBA8888 || + handle->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat || + handle->fmt == UHDR_IMG_FMT_32bppRGBA1010102) { + int bpp = handle->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat ? 8 : 4; + handle->planes[UHDR_PLANE_PACKED] = malloc(width * height * bpp); + handle->planes[UHDR_PLANE_U] = nullptr; + handle->planes[UHDR_PLANE_V] = nullptr; + handle->stride[UHDR_PLANE_PACKED] = width; + handle->stride[UHDR_PLANE_U] = 0; + handle->stride[UHDR_PLANE_V] = 0; + } else if (handle->fmt == UHDR_IMG_FMT_8bppYCbCr400) { + handle->planes[UHDR_PLANE_Y] = malloc(width * height); + handle->planes[UHDR_PLANE_U] = nullptr; + handle->planes[UHDR_PLANE_V] = nullptr; + handle->stride[UHDR_PLANE_Y] = width; + handle->stride[UHDR_PLANE_U] = 0; + handle->stride[UHDR_PLANE_V] = 0; } } -void EditorHelperTest::TearDown() {} - -TEST_F(EditorHelperTest, croppingYuvImage) { - jpegr_uncompressed_struct in_img; - jpegr_uncompressed_struct out_img; - const int left = 10; - const int right = 99; - const int top = 20; - const int bottom = 199; - int out_width = right - left + 1; - int out_height = bottom - top + 1; - - in_img.data = mYuvImage.buffer.get(); - in_img.width = IMAGE_WIDTH; - in_img.height = IMAGE_HEIGHT; - in_img.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709; - in_img.pixelFormat = ultrahdr_pixel_format::ULTRAHDR_PIX_FMT_YUV420; - - std::unique_ptr<uint8_t[]> out_img_data; - int outSize = out_width * out_height * 3 / 2; - out_img_data.reset(new uint8_t[outSize]); - out_img.data = out_img_data.get(); - EXPECT_TRUE(crop(&in_img, left, right, top, bottom, &out_img) == JPEGR_NO_ERROR); - EXPECT_TRUE(out_img.width = out_width); - EXPECT_TRUE(out_img.height = out_height); - EXPECT_TRUE(out_img.colorGamut == in_img.colorGamut); - EXPECT_TRUE(out_img.pixelFormat == in_img.pixelFormat); -#ifdef DUMP_OUTPUT - if (!writeFile("cropped.yuv", out_img.data, outSize)) { - std::cerr << "unable to write output file" << std::endl; - } -#endif -} +void compare_planes(void* ref_plane, void* test_plane, int ref_stride, int test_stride, int width, + int height, int bpp) { + uint8_t* ref = (uint8_t*)ref_plane; + uint8_t* test = (uint8_t*)test_plane; + const size_t length = width * bpp; -TEST_F(EditorHelperTest, croppingGreyImage) { - jpegr_uncompressed_struct in_img; - jpegr_uncompressed_struct out_img; - const int left = 10; - const int right = 99; - const int top = 20; - const int bottom = 199; - int out_width = right - left + 1; - int out_height = bottom - top + 1; - - in_img.data = mGreyImage.buffer.get(); - in_img.width = IMAGE_WIDTH; - in_img.height = IMAGE_HEIGHT; - in_img.pixelFormat = ultrahdr_pixel_format::ULTRAHDR_PIX_FMT_MONOCHROME; - - std::unique_ptr<uint8_t[]> out_img_data; - int outSize = out_width * out_height; - out_img_data.reset(new uint8_t[outSize]); - out_img.data = out_img_data.get(); - EXPECT_TRUE(crop(&in_img, left, right, top, bottom, &out_img)== JPEGR_NO_ERROR); - EXPECT_TRUE(out_img.width = out_width); - EXPECT_TRUE(out_img.height = out_height); - EXPECT_TRUE(out_img.pixelFormat == in_img.pixelFormat); -#ifdef DUMP_OUTPUT - if (!writeFile("cropped.y", out_img.data, outSize)) { - std::cerr << "unable to write output file" << std::endl; + for (int i = 0; i < height; i++, ref += (ref_stride * bpp), test += (test_stride * bpp)) { + ASSERT_EQ(0, memcmp(ref, ref, length)); } -#endif -} - -TEST_F(EditorHelperTest, mirroringYuvImageVertical) { - jpegr_uncompressed_struct in_img; - jpegr_uncompressed_struct out_img; - - in_img.data = mYuvImage.buffer.get(); - in_img.width = IMAGE_WIDTH; - in_img.height = IMAGE_HEIGHT; - in_img.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709; - in_img.pixelFormat = ultrahdr_pixel_format::ULTRAHDR_PIX_FMT_YUV420; - - std::unique_ptr<uint8_t[]> out_img_data; - int outSize = IMAGE_WIDTH * IMAGE_HEIGHT * 3 / 2; - out_img_data.reset(new uint8_t[outSize]); - out_img.data = out_img_data.get(); - EXPECT_TRUE(mirror(&in_img, ULTRAHDR_MIRROR_VERTICAL, &out_img)== JPEGR_NO_ERROR); - EXPECT_TRUE(out_img.width = IMAGE_WIDTH); - EXPECT_TRUE(out_img.height = IMAGE_HEIGHT); - EXPECT_TRUE(out_img.colorGamut == in_img.colorGamut); - EXPECT_TRUE(out_img.pixelFormat == in_img.pixelFormat); -#ifdef DUMP_OUTPUT - if (!writeFile("mirrored_vertical.yuv", out_img.data, outSize)) { - std::cerr << "unable to write output file" << std::endl; - } -#endif -} - -TEST_F(EditorHelperTest, mirroringYuvImageHorizontal) { - jpegr_uncompressed_struct in_img; - jpegr_uncompressed_struct out_img; - - in_img.data = mYuvImage.buffer.get(); - in_img.width = IMAGE_WIDTH; - in_img.height = IMAGE_HEIGHT; - in_img.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709; - in_img.pixelFormat = ultrahdr_pixel_format::ULTRAHDR_PIX_FMT_YUV420; - - std::unique_ptr<uint8_t[]> out_img_data; - int outSize = IMAGE_WIDTH * IMAGE_HEIGHT * 3 / 2; - out_img_data.reset(new uint8_t[outSize]); - out_img.data = out_img_data.get(); - EXPECT_TRUE(mirror(&in_img, ULTRAHDR_MIRROR_HORIZONTAL, &out_img)== JPEGR_NO_ERROR); - EXPECT_TRUE(out_img.width = IMAGE_WIDTH); - EXPECT_TRUE(out_img.height = IMAGE_HEIGHT); - EXPECT_TRUE(out_img.colorGamut == in_img.colorGamut); - EXPECT_TRUE(out_img.pixelFormat == in_img.pixelFormat); -#ifdef DUMP_OUTPUT - if (!writeFile("mirrored_horizontal.yuv", out_img.data, outSize)) { - std::cerr << "unable to write output file" << std::endl; - } -#endif -} - -TEST_F(EditorHelperTest, mirroringGreyImageVertical) { - jpegr_uncompressed_struct in_img; - jpegr_uncompressed_struct out_img; - - in_img.data = mGreyImage.buffer.get(); - in_img.width = IMAGE_WIDTH; - in_img.height = IMAGE_HEIGHT; - in_img.pixelFormat = ultrahdr_pixel_format::ULTRAHDR_PIX_FMT_MONOCHROME; - - std::unique_ptr<uint8_t[]> out_img_data; - int outSize = IMAGE_WIDTH * IMAGE_HEIGHT; - out_img_data.reset(new uint8_t[outSize]); - out_img.data = out_img_data.get(); - EXPECT_TRUE(mirror(&in_img, ULTRAHDR_MIRROR_VERTICAL, &out_img)== JPEGR_NO_ERROR); - EXPECT_TRUE(out_img.width = IMAGE_WIDTH); - EXPECT_TRUE(out_img.height = IMAGE_HEIGHT); - EXPECT_TRUE(out_img.pixelFormat == in_img.pixelFormat); -#ifdef DUMP_OUTPUT - if (!writeFile("mirrored_vertical.y", out_img.data, outSize)) { - std::cerr << "unable to write output file" << std::endl; - } -#endif } -TEST_F(EditorHelperTest, mirroringGreyImageHorizontal) { - jpegr_uncompressed_struct in_img; - jpegr_uncompressed_struct out_img; - - in_img.data = mGreyImage.buffer.get(); - in_img.width = IMAGE_WIDTH; - in_img.height = IMAGE_HEIGHT; - in_img.pixelFormat = ultrahdr_pixel_format::ULTRAHDR_PIX_FMT_MONOCHROME; - - std::unique_ptr<uint8_t[]> out_img_data; - int outSize = IMAGE_WIDTH * IMAGE_HEIGHT; - out_img_data.reset(new uint8_t[outSize]); - out_img.data = out_img_data.get(); - EXPECT_TRUE(mirror(&in_img, ULTRAHDR_MIRROR_HORIZONTAL, &out_img)== JPEGR_NO_ERROR); - EXPECT_TRUE(out_img.width = IMAGE_WIDTH); - EXPECT_TRUE(out_img.height = IMAGE_HEIGHT); - EXPECT_TRUE(out_img.pixelFormat == in_img.pixelFormat); -#ifdef DUMP_OUTPUT - if (!writeFile("mirrored_horizontal.y", out_img.data, outSize)) { - std::cerr << "unable to write output file" << std::endl; +void compareImg(uhdr_raw_image_t* ref, uhdr_raw_image_t* test) { + ASSERT_EQ(ref->fmt, test->fmt); + ASSERT_EQ(ref->cg, test->cg); + ASSERT_EQ(ref->ct, test->ct); + ASSERT_EQ(ref->range, test->range); + ASSERT_EQ(ref->w, test->w); + ASSERT_EQ(ref->h, test->h); + int bpp = 1; + if (ref->fmt == UHDR_IMG_FMT_24bppYCbCrP010) { + bpp = 2; + compare_planes(ref->planes[UHDR_PLANE_Y], test->planes[UHDR_PLANE_Y], ref->stride[UHDR_PLANE_Y], + test->stride[UHDR_PLANE_Y], ref->w, ref->h, bpp); + compare_planes(ref->planes[UHDR_PLANE_UV], test->planes[UHDR_PLANE_UV], + ref->stride[UHDR_PLANE_UV], test->stride[UHDR_PLANE_UV], ref->w, ref->h / 2, + bpp); + } else if (ref->fmt == UHDR_IMG_FMT_12bppYCbCr420) { + compare_planes(ref->planes[UHDR_PLANE_Y], test->planes[UHDR_PLANE_Y], ref->stride[UHDR_PLANE_Y], + test->stride[UHDR_PLANE_Y], ref->w, ref->h, bpp); + compare_planes(ref->planes[UHDR_PLANE_U], test->planes[UHDR_PLANE_U], ref->stride[UHDR_PLANE_U], + test->stride[UHDR_PLANE_U], ref->w / 2, ref->h / 2, bpp); + compare_planes(ref->planes[UHDR_PLANE_V], test->planes[UHDR_PLANE_V], ref->stride[UHDR_PLANE_V], + test->stride[UHDR_PLANE_V], ref->w / 2, ref->h / 2, bpp); + } else if (ref->fmt == UHDR_IMG_FMT_32bppRGBA8888 || ref->fmt == UHDR_IMG_FMT_32bppRGBA1010102 || + ref->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat) { + bpp = ref->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat ? 8 : 4; + compare_planes(ref->planes[UHDR_PLANE_PACKED], test->planes[UHDR_PLANE_PACKED], + ref->stride[UHDR_PLANE_PACKED], test->stride[UHDR_PLANE_PACKED], ref->w, ref->h, + bpp); + } else if (ref->fmt == UHDR_IMG_FMT_8bppYCbCr400) { + compare_planes(ref->planes[UHDR_PLANE_Y], test->planes[UHDR_PLANE_Y], ref->stride[UHDR_PLANE_Y], + test->stride[UHDR_PLANE_Y], ref->w, ref->h, bpp); } -#endif } -TEST_F(EditorHelperTest, rotatingYuvImage90) { - jpegr_uncompressed_struct in_img; - jpegr_uncompressed_struct out_img; - - in_img.data = mYuvImage.buffer.get(); - in_img.width = IMAGE_WIDTH; - in_img.height = IMAGE_HEIGHT; - in_img.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709; - in_img.pixelFormat = ultrahdr_pixel_format::ULTRAHDR_PIX_FMT_YUV420; - - std::unique_ptr<uint8_t[]> out_img_data; - int outSize = IMAGE_WIDTH * IMAGE_HEIGHT * 3 / 2; - out_img_data.reset(new uint8_t[outSize]); - out_img.data = out_img_data.get(); - EXPECT_TRUE(rotate(&in_img, 90, &out_img)== JPEGR_NO_ERROR); - EXPECT_TRUE(out_img.width = IMAGE_HEIGHT); - EXPECT_TRUE(out_img.height = IMAGE_WIDTH); - EXPECT_TRUE(out_img.colorGamut == in_img.colorGamut); - EXPECT_TRUE(out_img.pixelFormat == in_img.pixelFormat); -#ifdef DUMP_OUTPUT - if (!writeFile("rotated_90.yuv", out_img.data, outSize)) { - std::cerr << "unable to write output file" << std::endl; +class EditorHelperTest + : public ::testing::TestWithParam<std::tuple<std::string, int, int, uhdr_img_fmt_t>> { + public: + EditorHelperTest() + : filename(std::get<0>(GetParam())), + width(std::get<1>(GetParam())), + height(std::get<2>(GetParam())), + fmt(std::get<3>(GetParam())){}; + + ~EditorHelperTest() { + int count = sizeof img_a.planes / sizeof img_a.planes[0]; + for (int i = 0; i < count; i++) { + if (img_a.planes[i]) { + free(img_a.planes[i]); + } + } } -#endif -} -TEST_F(EditorHelperTest, rotatingYuvImage180) { - jpegr_uncompressed_struct in_img; - jpegr_uncompressed_struct out_img; - - in_img.data = mYuvImage.buffer.get(); - in_img.width = IMAGE_WIDTH; - in_img.height = IMAGE_HEIGHT; - in_img.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709; - in_img.pixelFormat = ultrahdr_pixel_format::ULTRAHDR_PIX_FMT_YUV420; - - std::unique_ptr<uint8_t[]> out_img_data; - int outSize = IMAGE_WIDTH * IMAGE_HEIGHT * 3 / 2; - out_img_data.reset(new uint8_t[outSize]); - out_img.data = out_img_data.get(); - EXPECT_TRUE(rotate(&in_img, 180, &out_img)== JPEGR_NO_ERROR); - EXPECT_TRUE(out_img.width = IMAGE_WIDTH); - EXPECT_TRUE(out_img.height = IMAGE_HEIGHT); - EXPECT_TRUE(out_img.colorGamut == in_img.colorGamut); - EXPECT_TRUE(out_img.pixelFormat == in_img.pixelFormat); -#ifdef DUMP_OUTPUT - if (!writeFile("rotated_180.yuv", out_img.data, outSize)) { - std::cerr << "unable to write output file" << std::endl; - } -#endif -} + std::string filename; + int width; + int height; + uhdr_img_fmt_t fmt; + uhdr_raw_image_t img_a; +}; -TEST_F(EditorHelperTest, rotatingYuvImage270) { - jpegr_uncompressed_struct in_img; - jpegr_uncompressed_struct out_img; - - in_img.data = mYuvImage.buffer.get(); - in_img.width = IMAGE_WIDTH; - in_img.height = IMAGE_HEIGHT; - in_img.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709; - in_img.pixelFormat = ultrahdr_pixel_format::ULTRAHDR_PIX_FMT_YUV420; - - std::unique_ptr<uint8_t[]> out_img_data; - int outSize = IMAGE_WIDTH * IMAGE_HEIGHT * 3 / 2; - out_img_data.reset(new uint8_t[outSize]); - out_img.data = out_img_data.get(); - EXPECT_TRUE(rotate(&in_img, 270, &out_img)== JPEGR_NO_ERROR); - EXPECT_TRUE(out_img.width = IMAGE_HEIGHT); - EXPECT_TRUE(out_img.height = IMAGE_WIDTH); - EXPECT_TRUE(out_img.colorGamut == in_img.colorGamut); - EXPECT_TRUE(out_img.pixelFormat == in_img.pixelFormat); -#ifdef DUMP_OUTPUT - if (!writeFile("rotated_270.yuv", out_img.data, outSize)) { - std::cerr << "unable to write output file" << std::endl; - } -#endif +TEST_P(EditorHelperTest, Rotate) { + initImageHandle(&img_a, width, height, fmt); + ASSERT_TRUE(loadFile(filename.c_str(), &img_a)) << "unable to load file " << filename; + auto dst = apply_rotate(&img_a, 90); + dst = apply_rotate(dst.get(), 90); + dst = apply_rotate(dst.get(), 180); + dst = apply_rotate(dst.get(), 270); + dst = apply_rotate(dst.get(), 90); + dst = apply_rotate(dst.get(), 90); + dst = apply_rotate(dst.get(), 270); + ASSERT_NO_FATAL_FAILURE(compareImg(&img_a, dst.get())); } -TEST_F(EditorHelperTest, rotatingGreyImage90) { - jpegr_uncompressed_struct in_img; - jpegr_uncompressed_struct out_img; - - in_img.data = mGreyImage.buffer.get(); - in_img.width = IMAGE_WIDTH; - in_img.height = IMAGE_HEIGHT; - in_img.pixelFormat = ultrahdr_pixel_format::ULTRAHDR_PIX_FMT_MONOCHROME; - - std::unique_ptr<uint8_t[]> out_img_data; - int outSize = IMAGE_WIDTH * IMAGE_HEIGHT; - out_img_data.reset(new uint8_t[outSize]); - out_img.data = out_img_data.get(); - EXPECT_TRUE(rotate(&in_img, 90, &out_img)== JPEGR_NO_ERROR); - EXPECT_TRUE(out_img.width = IMAGE_HEIGHT); - EXPECT_TRUE(out_img.height = IMAGE_WIDTH); - EXPECT_TRUE(out_img.pixelFormat == in_img.pixelFormat); -#ifdef DUMP_OUTPUT - if (!writeFile("rotated_90.y", out_img.data, outSize)) { - std::cerr << "unable to write output file" << std::endl; - } -#endif +TEST_P(EditorHelperTest, Mirror) { + initImageHandle(&img_a, width, height, fmt); + ASSERT_TRUE(loadFile(filename.c_str(), &img_a)) << "unable to load file " << filename; + auto dst = apply_mirror(&img_a, UHDR_MIRROR_VERTICAL); + dst = apply_mirror(dst.get(), UHDR_MIRROR_VERTICAL); + dst = apply_mirror(dst.get(), UHDR_MIRROR_HORIZONTAL); + dst = apply_mirror(dst.get(), UHDR_MIRROR_HORIZONTAL); + ASSERT_NO_FATAL_FAILURE(compareImg(&img_a, dst.get())); } -TEST_F(EditorHelperTest, rotatingGreyImage180) { - jpegr_uncompressed_struct in_img; - jpegr_uncompressed_struct out_img; - - in_img.data = mGreyImage.buffer.get(); - in_img.width = IMAGE_WIDTH; - in_img.height = IMAGE_HEIGHT; - in_img.pixelFormat = ultrahdr_pixel_format::ULTRAHDR_PIX_FMT_MONOCHROME; - - std::unique_ptr<uint8_t[]> out_img_data; - int outSize = IMAGE_WIDTH * IMAGE_HEIGHT; - out_img_data.reset(new uint8_t[outSize]); - out_img.data = out_img_data.get(); - EXPECT_TRUE(rotate(&in_img, 180, &out_img)== JPEGR_NO_ERROR); - EXPECT_TRUE(out_img.width = IMAGE_WIDTH); - EXPECT_TRUE(out_img.height = IMAGE_HEIGHT); - EXPECT_TRUE(out_img.pixelFormat == in_img.pixelFormat); -#ifdef DUMP_OUTPUT - if (!writeFile("rotated_180.y", out_img.data, outSize)) { - std::cerr << "unable to write output file" << std::endl; - } -#endif +TEST_P(EditorHelperTest, MultipleEffects) { + initImageHandle(&img_a, width, height, fmt); + ASSERT_TRUE(loadFile(filename.c_str(), &img_a)) << "unable to load file " << filename; + auto dst = apply_mirror(&img_a, UHDR_MIRROR_VERTICAL); + dst = apply_rotate(dst.get(), 180); + dst = apply_mirror(dst.get(), UHDR_MIRROR_HORIZONTAL); + ASSERT_NO_FATAL_FAILURE(compareImg(&img_a, dst.get())); + + dst = apply_mirror(dst.get(), UHDR_MIRROR_HORIZONTAL); + dst = apply_rotate(dst.get(), 90); + dst = apply_rotate(dst.get(), 90); + dst = apply_mirror(dst.get(), UHDR_MIRROR_VERTICAL); + ASSERT_NO_FATAL_FAILURE(compareImg(&img_a, dst.get())); + + dst = apply_rotate(dst.get(), 270); + dst = apply_mirror(dst.get(), UHDR_MIRROR_VERTICAL); + dst = apply_rotate(dst.get(), 90); + dst = apply_mirror(dst.get(), UHDR_MIRROR_HORIZONTAL); + ASSERT_NO_FATAL_FAILURE(compareImg(&img_a, dst.get())); + + dst = apply_resize(dst.get(), width / 2, height / 2); + ASSERT_EQ(img_a.fmt, dst->fmt); + ASSERT_EQ(img_a.cg, dst->cg); + ASSERT_EQ(img_a.ct, dst->ct); + ASSERT_EQ(img_a.range, dst->range); + ASSERT_EQ(dst->w, width / 2); + ASSERT_EQ(dst->h, height / 2); + + uhdr_raw_image_ext_t* img_copy = dst.get(); + apply_crop(img_copy, 8, 8, width / 4, height / 4); + ASSERT_EQ(dst->fmt, img_copy->fmt); + ASSERT_EQ(dst->cg, img_copy->cg); + ASSERT_EQ(dst->ct, img_copy->ct); + ASSERT_EQ(dst->range, img_copy->range); + ASSERT_EQ(width / 4, img_copy->w); + ASSERT_EQ(height / 4, img_copy->h); } -TEST_F(EditorHelperTest, rotatingGreyImage270) { - jpegr_uncompressed_struct in_img; - jpegr_uncompressed_struct out_img; - - in_img.data = mGreyImage.buffer.get(); - in_img.width = IMAGE_WIDTH; - in_img.height = IMAGE_HEIGHT; - in_img.pixelFormat = ultrahdr_pixel_format::ULTRAHDR_PIX_FMT_MONOCHROME; - - std::unique_ptr<uint8_t[]> out_img_data; - int outSize = IMAGE_WIDTH * IMAGE_HEIGHT; - out_img_data.reset(new uint8_t[outSize]); - out_img.data = out_img_data.get(); - EXPECT_TRUE(rotate(&in_img, 270, &out_img)== JPEGR_NO_ERROR); - EXPECT_TRUE(out_img.width = IMAGE_HEIGHT); - EXPECT_TRUE(out_img.height = IMAGE_WIDTH); - EXPECT_TRUE(out_img.pixelFormat == in_img.pixelFormat); +TEST_P(EditorHelperTest, Crop) { + initImageHandle(&img_a, width, height, fmt); + ASSERT_TRUE(loadFile(filename.c_str(), &img_a)) << "unable to load file " << filename; + uhdr_raw_image_t img_copy = img_a; + apply_crop(&img_copy, 8, 8, width / 2, height / 2); + + ASSERT_EQ(img_a.fmt, img_copy.fmt); + ASSERT_EQ(img_a.cg, img_copy.cg); + ASSERT_EQ(img_a.ct, img_copy.ct); + ASSERT_EQ(img_a.range, img_copy.range); + ASSERT_EQ(img_copy.w, width / 2); + ASSERT_EQ(img_copy.h, height / 2); #ifdef DUMP_OUTPUT - if (!writeFile("rotated_270.y", out_img.data, outSize)) { + if (!writeFile("cropped", &img_copy)) { std::cerr << "unable to write output file" << std::endl; } #endif } -TEST_F(EditorHelperTest, resizeYuvImageUp) { - jpegr_uncompressed_struct in_img; - jpegr_uncompressed_struct out_img; - - in_img.data = mYuvImage.buffer.get(); - in_img.width = IMAGE_WIDTH; - in_img.height = IMAGE_HEIGHT; - in_img.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709; - in_img.pixelFormat = ultrahdr_pixel_format::ULTRAHDR_PIX_FMT_YUV420; - - std::unique_ptr<uint8_t[]> out_img_data; - int out_width = IMAGE_WIDTH * 3 / 2; - int out_height = IMAGE_HEIGHT * 3 / 2; - int outSize = out_width * out_height * 3 / 2; - out_img_data.reset(new uint8_t[outSize]); - out_img.data = out_img_data.get(); - EXPECT_TRUE(resize(&in_img, out_width, out_height, &out_img)== JPEGR_NO_ERROR); - EXPECT_TRUE(out_img.width = out_width); - EXPECT_TRUE(out_img.height = out_height); - EXPECT_TRUE(out_img.colorGamut == in_img.colorGamut); - EXPECT_TRUE(out_img.pixelFormat == in_img.pixelFormat); +TEST_P(EditorHelperTest, Resize) { + initImageHandle(&img_a, width, height, fmt); + ASSERT_TRUE(loadFile(filename.c_str(), &img_a)) << "unable to load file " << filename; + auto dst = apply_resize(&img_a, width / 2, height / 2); + + ASSERT_EQ(img_a.fmt, dst->fmt); + ASSERT_EQ(img_a.cg, dst->cg); + ASSERT_EQ(img_a.ct, dst->ct); + ASSERT_EQ(img_a.range, dst->range); + ASSERT_EQ(dst->w, width / 2); + ASSERT_EQ(dst->h, height / 2); #ifdef DUMP_OUTPUT - if (!writeFile("resize_up.yuv", out_img.data, outSize)) { + if (!writeFile("resize", dst.get())) { std::cerr << "unable to write output file" << std::endl; } #endif } -TEST_F(EditorHelperTest, resizeYuvImageDown) { - jpegr_uncompressed_struct in_img; - jpegr_uncompressed_struct out_img; - - in_img.data = mYuvImage.buffer.get(); - in_img.width = IMAGE_WIDTH; - in_img.height = IMAGE_HEIGHT; - in_img.colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709; - in_img.pixelFormat = ultrahdr_pixel_format::ULTRAHDR_PIX_FMT_YUV420; - - std::unique_ptr<uint8_t[]> out_img_data; - int out_width = IMAGE_WIDTH * 2 / 3; - int out_height = IMAGE_HEIGHT * 2 / 3; - int outSize = out_width * out_height * 3 / 2; - out_img_data.reset(new uint8_t[outSize]); - out_img.data = out_img_data.get(); - EXPECT_TRUE(resize(&in_img, out_width, out_height, &out_img)== JPEGR_NO_ERROR); - EXPECT_TRUE(out_img.width = out_width); - EXPECT_TRUE(out_img.height = out_height); - EXPECT_TRUE(out_img.pixelFormat == in_img.pixelFormat); -#ifdef DUMP_OUTPUT - if (!writeFile("resize_down.yuv", out_img.data, outSize)) { - std::cerr << "unable to write output file" << std::endl; - } -#endif -} +#ifdef __ANDROID__ +INSTANTIATE_TEST_SUITE_P( + EditorAPIParameterizedTests, EditorHelperTest, + ::testing::Values(std::make_tuple("/data/local/tmp/raw_p010_image.p010", 1280, 720, + UHDR_IMG_FMT_24bppYCbCrP010), + std::make_tuple("/data/local/tmp/raw_yuv420_image.yuv420", 1280, 720, + UHDR_IMG_FMT_12bppYCbCr420), + std::make_tuple("/data/local/tmp/raw_yuv420_image.yuv420", 1280, 720, + UHDR_IMG_FMT_8bppYCbCr400), + std::make_tuple("/data/local/tmp/raw_p010_image.p010", 352, 288, + UHDR_IMG_FMT_32bppRGBA1010102), + std::make_tuple("/data/local/tmp/raw_p010_image.p010", 352, 288, + UHDR_IMG_FMT_64bppRGBAHalfFloat), + std::make_tuple("/data/local/tmp/raw_p010_image.p010", 352, 288, + UHDR_IMG_FMT_32bppRGBA8888))); -TEST_F(EditorHelperTest, resizeGreyImageUp) { - jpegr_uncompressed_struct in_img; - jpegr_uncompressed_struct out_img; - - in_img.data = mGreyImage.buffer.get(); - in_img.width = IMAGE_WIDTH; - in_img.height = IMAGE_HEIGHT; - in_img.pixelFormat = ultrahdr_pixel_format::ULTRAHDR_PIX_FMT_MONOCHROME; - - std::unique_ptr<uint8_t[]> out_img_data; - int out_width = IMAGE_WIDTH * 3 / 2; - int out_height = IMAGE_HEIGHT * 3 / 2; - int outSize = out_width * out_height; - out_img_data.reset(new uint8_t[outSize]); - out_img.data = out_img_data.get(); - EXPECT_TRUE(resize(&in_img, out_width, out_height, &out_img)== JPEGR_NO_ERROR); - EXPECT_TRUE(out_img.width = out_width); - EXPECT_TRUE(out_img.height = out_height); - EXPECT_TRUE(out_img.pixelFormat == in_img.pixelFormat); -#ifdef DUMP_OUTPUT - if (!writeFile("resize_up.y", out_img.data, outSize)) { - std::cerr << "unable to write output file" << std::endl; - } +#else +INSTANTIATE_TEST_SUITE_P( + EditorAPIParameterizedTests, EditorHelperTest, + ::testing::Values( + std::make_tuple("./data/raw_p010_image.p010", 1280, 720, UHDR_IMG_FMT_24bppYCbCrP010), + std::make_tuple("./data/raw_yuv420_image.yuv420", 1280, 720, UHDR_IMG_FMT_12bppYCbCr420), + std::make_tuple("./data/raw_yuv420_image.yuv420", 1280, 720, UHDR_IMG_FMT_8bppYCbCr400), + std::make_tuple("./data/raw_p010_image.p010", 352, 288, UHDR_IMG_FMT_32bppRGBA1010102), + std::make_tuple("./data/raw_p010_image.p010", 352, 288, UHDR_IMG_FMT_64bppRGBAHalfFloat), + std::make_tuple("./data/raw_p010_image.p010", 352, 288, UHDR_IMG_FMT_32bppRGBA8888))); #endif -} -TEST_F(EditorHelperTest, resizeGreyImageDown) { - jpegr_uncompressed_struct in_img; - jpegr_uncompressed_struct out_img; - - in_img.data = mGreyImage.buffer.get(); - in_img.width = IMAGE_WIDTH; - in_img.height = IMAGE_HEIGHT; - in_img.pixelFormat = ultrahdr_pixel_format::ULTRAHDR_PIX_FMT_MONOCHROME; - - std::unique_ptr<uint8_t[]> out_img_data; - int out_width = IMAGE_WIDTH * 2 / 3; - int out_height = IMAGE_HEIGHT * 2 / 3; - int outSize = out_width * out_height; - out_img_data.reset(new uint8_t[outSize]); - out_img.data = out_img_data.get(); - EXPECT_TRUE(resize(&in_img, out_width, out_height, &out_img)== JPEGR_NO_ERROR); - EXPECT_TRUE(out_img.width = out_width); - EXPECT_TRUE(out_img.height = out_height); - EXPECT_TRUE(out_img.pixelFormat == in_img.pixelFormat); -#ifdef DUMP_OUTPUT - if (!writeFile("resize_down.y", out_img.data, outSize)) { - std::cerr << "unable to write output file" << std::endl; - } -#endif -} } // namespace ultrahdr |