From 01aad01c492b39ea8b47d23c6acf4217f056cdeb Mon Sep 17 00:00:00 2001 From: "Kwabena W. Agyeman" Date: Fri, 15 Jan 2021 10:29:07 -0800 Subject: [PATCH 1/2] Update draw image to support in place updates --- src/omv/imlib/draw.c | 226 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 183 insertions(+), 43 deletions(-) diff --git a/src/omv/imlib/draw.c b/src/omv/imlib/draw.c index 734c705f9..f890a569a 100644 --- a/src/omv/imlib/draw.c +++ b/src/omv/imlib/draw.c @@ -439,7 +439,14 @@ void imlib_draw_string(image_t *img, int x_off, int y_off, const char *str, int void imlib_draw_row_setup(imlib_draw_row_data_t *data) { - size_t image_row_size = image_size(data->dst_img) / data->dst_img->h; + image_t temp; + temp.w = data->dst_img->w; + temp.h = data->dst_img->h; + temp.bpp = data->src_img_bpp; + + // Image Row Size should be the width of the destination image + // but with the bpp of the source image. + size_t image_row_size = image_size(&temp) / data->dst_img->h; data->toggle = 0; data->row_buffer[0] = fb_alloc(image_row_size, FB_ALLOC_NO_HINT); @@ -2663,6 +2670,29 @@ void imlib_draw_image(image_t *dst_img, image_t *src_img, int dst_x_start, int d color_palette = NULL; } + bool no_scaling_nearest_neighbor = (dst_delta_x == 1) + && (dst_x_start == 0) && (src_x_start == 0) + && (src_x_frac == 65536) && (src_y_frac == 65536); + + // If we are scaling just make a deep copy. + bool is_scaling = (hint & (IMAGE_HINT_AREA | IMAGE_HINT_BICUBIC | IMAGE_HINT_BILINEAR)) + || (!no_scaling_nearest_neighbor); + + // Otherwise, we only have to do a deep copy if the image is growing. + size_t src_img_row_bytes = image_size(src_img) / src_img->h; + size_t dst_img_row_bytes = image_size(dst_img) / dst_img->h; + + // In-place (by definition mutually exclusive to the above)... + if ((dst_img->data == src_img->data) && (is_scaling || (src_img_row_bytes < dst_img_row_bytes))) { + size_t size = image_size(src_img); + new_src_img.w = src_img->w; // same width as source image + new_src_img.h = src_img->h; // same height as source image + new_src_img.bpp = src_img->bpp; + new_src_img.data = fb_alloc(size, FB_ALLOC_NO_HINT); + memcpy(new_src_img.data, src_img->data, size); + src_img = &new_src_img; + } + imlib_draw_row_data_t imlib_draw_row_data; imlib_draw_row_data.dst_img = dst_img; imlib_draw_row_data.src_img_bpp = src_img->bpp; @@ -4360,52 +4390,162 @@ void imlib_draw_image(image_t *dst_img, image_t *src_img, int dst_x_start, int d break; } } - } else if ((dst_delta_x == 1) && (dst_x_start == 0) && (src_x_start == 0) && (src_x_frac == 65536) && (src_y_frac == 65536)) { // copy - switch (src_img->bpp) { - case IMAGE_BPP_BINARY: { - while (y_not_done) { - uint32_t *src_row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(src_img, next_src_y_index); - imlib_draw_row_put_row_buffer(&imlib_draw_row_data, src_row_ptr); - imlib_draw_row(dst_x_start, dst_x_end, dst_y, &imlib_draw_row_data); + } else if (no_scaling_nearest_neighbor) { // copy + if (dst_img->data == src_img->data) { // In-Place + switch (src_img->bpp) { + case IMAGE_BPP_BINARY: { + while (y_not_done) { + uint32_t *src_row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(src_img, next_src_y_index); + // Must be called per loop to get the address of the temp buffer to blend with + uint32_t *dst_row_ptr = (uint32_t *) imlib_draw_row_get_row_buffer(&imlib_draw_row_data); - // Increment offsets - dst_y += dst_delta_y; - src_y_accum += src_y_frac; - next_src_y_index = src_y_accum >> 16; - y_not_done = ++y < dst_y_end; - } // while y - break; - } - case IMAGE_BPP_GRAYSCALE: { - while (y_not_done) { - uint8_t *src_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(src_img, next_src_y_index); - imlib_draw_row_put_row_buffer(&imlib_draw_row_data, src_row_ptr); - imlib_draw_row(dst_x_start, dst_x_end, dst_y, &imlib_draw_row_data); + // X loop iteration variables + int dst_x = dst_x_reset; + long src_x_accum = src_x_accum_reset; + int next_src_x_index = src_x_accum >> 16; + int x = dst_x_start; + bool x_not_done = x < dst_x_end; - // Increment offsets - dst_y += dst_delta_y; - src_y_accum += src_y_frac; - next_src_y_index = src_y_accum >> 16; - y_not_done = ++y < dst_y_end; - } // while y - break; - } - case IMAGE_BPP_RGB565: { - while (y_not_done) { - uint16_t *src_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src_img, next_src_y_index); - imlib_draw_row_put_row_buffer(&imlib_draw_row_data, src_row_ptr); - imlib_draw_row(dst_x_start, dst_x_end, dst_y, &imlib_draw_row_data); + while (x_not_done) { + int pixel = IMAGE_GET_BINARY_PIXEL_FAST(src_row_ptr, next_src_x_index); + IMAGE_PUT_BINARY_PIXEL_FAST(dst_row_ptr, dst_x, pixel); - // Increment offsets - dst_y += dst_delta_y; - src_y_accum += src_y_frac; - next_src_y_index = src_y_accum >> 16; - y_not_done = ++y < dst_y_end; - } // while y - break; + // Increment offsets + dst_x += dst_delta_x; + src_x_accum += src_x_frac; + next_src_x_index = src_x_accum >> 16; + x_not_done = ++x < dst_x_end; + } // while x + + imlib_draw_row(dst_x_start, dst_x_end, dst_y, &imlib_draw_row_data); + + // Increment offsets + dst_y += dst_delta_y; + src_y_accum += src_y_frac; + next_src_y_index = src_y_accum >> 16; + y_not_done = ++y < dst_y_end; + } // while y + break; + } + case IMAGE_BPP_GRAYSCALE: { + while (y_not_done) { + uint8_t *src_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(src_img, next_src_y_index); + // Must be called per loop to get the address of the temp buffer to blend with + uint8_t *dst_row_ptr = (uint8_t *) imlib_draw_row_get_row_buffer(&imlib_draw_row_data); + + // X loop iteration variables + int dst_x = dst_x_reset; + long src_x_accum = src_x_accum_reset; + int next_src_x_index = src_x_accum >> 16; + int x = dst_x_start; + bool x_not_done = x < dst_x_end; + + while (x_not_done) { + int pixel = IMAGE_GET_GRAYSCALE_PIXEL_FAST(src_row_ptr, next_src_x_index); + IMAGE_PUT_GRAYSCALE_PIXEL_FAST(dst_row_ptr, dst_x, pixel); + + // Increment offsets + dst_x += dst_delta_x; + src_x_accum += src_x_frac; + next_src_x_index = src_x_accum >> 16; + x_not_done = ++x < dst_x_end; + } // while x + + imlib_draw_row(dst_x_start, dst_x_end, dst_y, &imlib_draw_row_data); + + // Increment offsets + dst_y += dst_delta_y; + src_y_accum += src_y_frac; + next_src_y_index = src_y_accum >> 16; + y_not_done = ++y < dst_y_end; + } // while y + break; + } + case IMAGE_BPP_RGB565: { + while (y_not_done) { + uint16_t *src_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src_img, next_src_y_index); + // Must be called per loop to get the address of the temp buffer to blend with + uint16_t *dst_row_ptr = (uint16_t *) imlib_draw_row_get_row_buffer(&imlib_draw_row_data); + + // X loop iteration variables + int dst_x = dst_x_reset; + long src_x_accum = src_x_accum_reset; + int next_src_x_index = src_x_accum >> 16; + int x = dst_x_start; + bool x_not_done = x < dst_x_end; + + while (x_not_done) { + int pixel = IMAGE_GET_RGB565_PIXEL_FAST(src_row_ptr, next_src_x_index); + IMAGE_PUT_RGB565_PIXEL_FAST(dst_row_ptr, dst_x, pixel); + + // Increment offsets + dst_x += dst_delta_x; + src_x_accum += src_x_frac; + next_src_x_index = src_x_accum >> 16; + x_not_done = ++x < dst_x_end; + } // while x + + imlib_draw_row(dst_x_start, dst_x_end, dst_y, &imlib_draw_row_data); + + // Increment offsets + dst_y += dst_delta_y; + src_y_accum += src_y_frac; + next_src_y_index = src_y_accum >> 16; + y_not_done = ++y < dst_y_end; + } // while y + break; + } + default: { + break; + } } - default: { - break; + } else { // Out-of-Place + switch (src_img->bpp) { + case IMAGE_BPP_BINARY: { + while (y_not_done) { + uint32_t *src_row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(src_img, next_src_y_index); + imlib_draw_row_put_row_buffer(&imlib_draw_row_data, src_row_ptr); + imlib_draw_row(dst_x_start, dst_x_end, dst_y, &imlib_draw_row_data); + + // Increment offsets + dst_y += dst_delta_y; + src_y_accum += src_y_frac; + next_src_y_index = src_y_accum >> 16; + y_not_done = ++y < dst_y_end; + } // while y + break; + } + case IMAGE_BPP_GRAYSCALE: { + while (y_not_done) { + uint8_t *src_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(src_img, next_src_y_index); + imlib_draw_row_put_row_buffer(&imlib_draw_row_data, src_row_ptr); + imlib_draw_row(dst_x_start, dst_x_end, dst_y, &imlib_draw_row_data); + + // Increment offsets + dst_y += dst_delta_y; + src_y_accum += src_y_frac; + next_src_y_index = src_y_accum >> 16; + y_not_done = ++y < dst_y_end; + } // while y + break; + } + case IMAGE_BPP_RGB565: { + while (y_not_done) { + uint16_t *src_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src_img, next_src_y_index); + imlib_draw_row_put_row_buffer(&imlib_draw_row_data, src_row_ptr); + imlib_draw_row(dst_x_start, dst_x_end, dst_y, &imlib_draw_row_data); + + // Increment offsets + dst_y += dst_delta_y; + src_y_accum += src_y_frac; + next_src_y_index = src_y_accum >> 16; + y_not_done = ++y < dst_y_end; + } // while y + break; + } + default: { + break; + } } } } else { // nearest neighbor From cb2f22e73749033458ad2b861e1f3fe27c2b7544 Mon Sep 17 00:00:00 2001 From: "Kwabena W. Agyeman" Date: Fri, 15 Jan 2021 10:36:26 -0800 Subject: [PATCH 2/2] Centralize and improve to_x/copy/crop --- src/omv/modules/py_image.c | 686 +++++++------------------------------ 1 file changed, 130 insertions(+), 556 deletions(-) diff --git a/src/omv/modules/py_image.c b/src/omv/modules/py_image.c index fe35d0bd5..2dd4301bd 100644 --- a/src/omv/modules/py_image.c +++ b/src/omv/modules/py_image.c @@ -867,435 +867,156 @@ static mp_obj_t py_image_midpoint_pooled(uint n_args, const mp_obj_t *args, mp_m STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_midpoint_pooled_obj, 3, py_image_midpoint_pooled); #endif // IMLIB_ENABLE_MIDPOINT_POOLING +static mp_obj_t py_image_to(int bpp, const uint16_t *default_color_palette, bool copy_to_fb, + uint n_args, const mp_obj_t *args, mp_map_t *kw_args) +{ + image_t *src_img = py_helper_arg_to_image_mutable(args[0]); + + float arg_x_scale = 1.f; + bool got_x_scale = py_helper_keyword_float_maybe(n_args, args, 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_x_scale), &arg_x_scale); + + float arg_y_scale = 1.f; + bool got_y_scale = py_helper_keyword_float_maybe(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_y_scale), &arg_y_scale); + + rectangle_t arg_roi; + py_helper_keyword_rectangle_roi(src_img, n_args, args, 3, kw_args, &arg_roi); + + int arg_rgb_channel = py_helper_keyword_int(n_args, args, 4, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_rgb_channel), -1); + if ((arg_rgb_channel < -1) || (2 < arg_rgb_channel)) { + mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("-1 <= rgb_channel <= 2!")); + } + + int arg_alpha = py_helper_keyword_int(n_args, args, 5, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_alpha), 256); + if ((arg_alpha < 0) || (256 < arg_alpha)) { + mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("0 <= alpha <= 256!")); + } + + const uint16_t *color_palette = py_helper_keyword_color_palette(n_args, args, 6, kw_args, default_color_palette); + const uint8_t *alpha_palette = py_helper_keyword_alpha_palette(n_args, args, 7, kw_args, NULL); + + image_hint_t hint = py_helper_keyword_int(n_args, args, 8, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_hint), 0); + + int arg_x_size; + bool got_x_size = py_helper_keyword_int_maybe(n_args, args, 9, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_x_size), &arg_x_size); + + int arg_y_size; + bool got_y_size = py_helper_keyword_int_maybe(n_args, args, 10, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_y_size), &arg_y_size); + + if (got_x_scale && got_x_size) { + mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Choose either x_scale or x_size not both!")); + } + + if (got_y_scale && got_y_size) { + mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Choose either y_scale or y_size not both!")); + } + + if (got_x_size) { + arg_x_scale = arg_x_size / ((float) arg_roi.w); + } + + if (got_y_size) { + arg_y_scale = arg_y_size / ((float) arg_roi.h); + } + + if ((!got_x_scale) && (!got_x_size) && got_y_size) { + arg_x_scale = arg_y_scale; + } + + if ((!got_y_scale) && (!got_y_size) && got_x_size) { + arg_y_scale = arg_x_scale; + } + + mp_obj_t copy_obj = py_helper_keyword_object(n_args, args, 11, kw_args, + MP_OBJ_NEW_QSTR(copy_to_fb ? MP_QSTR_copy_to_fb : MP_QSTR_copy), NULL); + bool copy = false; + image_t *arg_other = copy_to_fb ? NULL : src_img; + + if (copy_obj) { + if (mp_obj_is_integer(copy_obj)) { + copy = mp_obj_get_int(copy_obj); + } else { + arg_other = py_helper_arg_to_image_mutable(copy_obj); + } + } + + if (copy_to_fb && copy) { + fb_update_jpeg_buffer(); + } + + image_t dst_img; + dst_img.w = fast_floorf(arg_roi.w * arg_x_scale); + dst_img.h = fast_floorf(arg_roi.h * arg_y_scale); + dst_img.bpp = (bpp >= 0) ? bpp : src_img->bpp; + + if (copy) { + if (copy_to_fb) { + py_helper_set_to_framebuffer(&dst_img); + } else { + dst_img.data = xalloc(image_size(&dst_img)); + } + } else if (arg_other) { + bool fb = py_helper_is_equal_to_framebuffer(arg_other); + size_t size = fb ? framebuffer_get_buffer_size() : image_size(arg_other); + PY_ASSERT_TRUE_MSG((image_size(&dst_img) <= size), + "The new image won't fit in the target frame buffer!"); + // DO NOT MODIFY arg_other YET (as it could point to src_img)! + dst_img.data = arg_other->data; + py_helper_update_framebuffer(&dst_img); + } else { + dst_img.data = xalloc(image_size(&dst_img)); + } + + fb_alloc_mark(); + imlib_draw_image(&dst_img, src_img, 0, 0, arg_x_scale, arg_y_scale, &arg_roi, + arg_rgb_channel, arg_alpha, color_palette, alpha_palette, + (hint & (~IMAGE_HINT_CENTER)) | IMAGE_HINT_BLACK_BACKGROUND, NULL, NULL); + fb_alloc_free_till_mark(); + + if (arg_other) { + arg_other->w = dst_img.w; + arg_other->h = dst_img.h; + arg_other->bpp = dst_img.bpp; + } + + return py_image_from_struct(&dst_img); +} + static mp_obj_t py_image_to_bitmap(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { - image_t *arg_img = py_helper_arg_to_image_mutable(args[0]); - bool copy = py_helper_keyword_int(n_args, args, 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_copy), false); - int channel = py_helper_keyword_int(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_rgb_channel), -1); - - image_t out; - out.w = arg_img->w; - out.h = arg_img->h; - out.bpp = IMAGE_BPP_BINARY; - out.data = copy ? xalloc(image_size(&out)) : arg_img->data; - - switch(arg_img->bpp) { - case IMAGE_BPP_BINARY: { - if (copy) memcpy(out.data, arg_img->data, image_size(&out)); - break; - } - case IMAGE_BPP_GRAYSCALE: { - PY_ASSERT_TRUE_MSG((out.w >= (sizeof(uint32_t)/sizeof(uint8_t))) || copy, - "Can't convert to bitmap in place!"); - fb_alloc_mark(); - uint32_t *out_row_ptr = fb_alloc(IMAGE_BINARY_LINE_LEN_BYTES(&out), FB_ALLOC_NO_HINT); - for (int y = 0, yy = out.h; y < yy; y++) { - uint8_t *row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(arg_img, y); - for (int x = 0, xx = out.w; x < xx; x++) { - IMAGE_PUT_BINARY_PIXEL_FAST(out_row_ptr, x, - COLOR_GRAYSCALE_TO_BINARY(IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x))); - } - memcpy(IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(&out, y), - out_row_ptr, IMAGE_BINARY_LINE_LEN_BYTES(&out)); - } - fb_alloc_free_till_mark(); - break; - } - case IMAGE_BPP_RGB565: { - PY_ASSERT_TRUE_MSG((out.w >= (sizeof(uint32_t)/sizeof(uint16_t))) || copy, - "Can't convert to bitmap in place!"); - fb_alloc_mark(); - uint32_t *out_row_ptr = fb_alloc(IMAGE_BINARY_LINE_LEN_BYTES(&out), FB_ALLOC_NO_HINT); - for (int y = 0, yy = out.h; y < yy; y++) { - uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(arg_img, y); - for (int x = 0, xx = out.w; x < xx; x++) { - int pixel = IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x); - switch (channel) { - case 0: { - pixel = COLOR_RGB565_TO_R5(pixel) > (((COLOR_R5_MAX - COLOR_R5_MIN) / 2) + COLOR_R5_MIN); - break; - } - case 1: { - pixel = COLOR_RGB565_TO_G6(pixel) > (((COLOR_G6_MAX - COLOR_G6_MIN) / 2) + COLOR_G6_MIN); - break; - } - case 2: { - pixel = COLOR_RGB565_TO_B5(pixel) > (((COLOR_B5_MAX - COLOR_B5_MIN) / 2) + COLOR_B5_MIN); - break; - } - default: { - pixel = COLOR_RGB565_TO_BINARY(pixel); - break; - } - } - IMAGE_PUT_BINARY_PIXEL_FAST(out_row_ptr, x, pixel); - } - memcpy(IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(&out, y), - out_row_ptr, IMAGE_BINARY_LINE_LEN_BYTES(&out)); - } - fb_alloc_free_till_mark(); - break; - } - default: { - break; - } - } - - if (!copy) { - arg_img->bpp = IMAGE_BPP_BINARY; - py_helper_update_framebuffer(&out); - } - - return py_image_from_struct(&out); + return py_image_to(IMAGE_BPP_BINARY, NULL, false, n_args, args, kw_args); } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_to_bitmap_obj, 1, py_image_to_bitmap); static mp_obj_t py_image_to_grayscale(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { - image_t *arg_img = py_helper_arg_to_image_mutable(args[0]); - bool copy = py_helper_keyword_int(n_args, args, 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_copy), false); - int channel = py_helper_keyword_int(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_rgb_channel), -1); - - image_t out; - out.w = arg_img->w; - out.h = arg_img->h; - out.bpp = IMAGE_BPP_GRAYSCALE; - out.data = copy ? xalloc(image_size(&out)) : arg_img->data; - - switch(arg_img->bpp) { - case IMAGE_BPP_BINARY: { - if (copy || (!py_helper_is_equal_to_framebuffer(&out))) { - PY_ASSERT_TRUE_MSG((out.w == 1) || copy, - "Can't convert to grayscale in place!"); - for (int y = 0, yy = out.h; y < yy; y++) { - uint32_t *row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(arg_img, y); - uint8_t *out_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(&out, y); - for (int x = 0, xx = out.w; x < xx; x++) { - IMAGE_PUT_GRAYSCALE_PIXEL_FAST(out_row_ptr, x, - COLOR_BINARY_TO_GRAYSCALE(IMAGE_GET_BINARY_PIXEL_FAST(row_ptr, x))); - } - } - } else { - image_t temp; - memcpy(&temp, arg_img, sizeof(image_t)); - fb_alloc_mark(); - temp.data = fb_alloc(image_size(&temp), FB_ALLOC_NO_HINT); - memcpy(temp.data, arg_img->data, image_size(&temp)); - py_helper_set_to_framebuffer(&out); - - for (int y = 0, yy = out.h; y < yy; y++) { - uint32_t *row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(&temp, y); - uint8_t *out_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(&out, y); - for (int x = 0, xx = out.w; x < xx; x++) { - IMAGE_PUT_GRAYSCALE_PIXEL_FAST(out_row_ptr, x, - COLOR_BINARY_TO_GRAYSCALE(IMAGE_GET_BINARY_PIXEL_FAST(row_ptr, x))); - } - } - - fb_alloc_free_till_mark(); - } - break; - } - case IMAGE_BPP_GRAYSCALE: { - if (copy) memcpy(out.data, arg_img->data, image_size(&out)); - break; - } - case IMAGE_BPP_RGB565: { - for (int y = 0, yy = out.h; y < yy; y++) { - uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(arg_img, y); - uint8_t *out_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(&out, y); - for (int x = 0, xx = out.w; x < xx; x++) { - int pixel = IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x); - switch (channel) { - case 0: { - pixel = COLOR_RGB565_TO_R8(pixel); - break; - } - case 1: { - pixel = COLOR_RGB565_TO_G8(pixel); - break; - } - case 2: { - pixel = COLOR_RGB565_TO_B8(pixel); - break; - } - default: { - pixel = COLOR_RGB565_TO_GRAYSCALE(pixel); - break; - } - } - IMAGE_PUT_GRAYSCALE_PIXEL_FAST(out_row_ptr, x, pixel); - } - } - break; - } - default: { - break; - } - } - - if (!copy) { - arg_img->bpp = IMAGE_BPP_GRAYSCALE; - py_helper_update_framebuffer(&out); - } - - return py_image_from_struct(&out); + return py_image_to(IMAGE_BPP_GRAYSCALE, NULL, false, n_args, args, kw_args); } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_to_grayscale_obj, 1, py_image_to_grayscale); static mp_obj_t py_image_to_rgb565(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { - image_t *arg_img = py_helper_arg_to_image_mutable(args[0]); - bool copy = py_helper_keyword_int(n_args, args, 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_copy), false); - int channel = py_helper_keyword_int(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_rgb_channel), -1); - - image_t out; - out.w = arg_img->w; - out.h = arg_img->h; - out.bpp = IMAGE_BPP_RGB565; - out.data = copy ? xalloc(image_size(&out)) : arg_img->data; - - switch(arg_img->bpp) { - case IMAGE_BPP_BINARY: { - if (copy || (!py_helper_is_equal_to_framebuffer(&out))) { - PY_ASSERT_TRUE_MSG((out.w == 1) || copy, - "Can't convert to grayscale in place!"); - for (int y = 0, yy = out.h; y < yy; y++) { - uint32_t *row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(arg_img, y); - uint16_t *out_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&out, y); - for (int x = 0, xx = out.w; x < xx; x++) { - IMAGE_PUT_RGB565_PIXEL_FAST(out_row_ptr, x, - imlib_yuv_to_rgb(IMAGE_GET_BINARY_PIXEL_FAST(row_ptr, x) * COLOR_GRAYSCALE_MAX, 0, 0)); - } - } - } else { - image_t temp; - memcpy(&temp, arg_img, sizeof(image_t)); - fb_alloc_mark(); - temp.data = fb_alloc(image_size(&temp), FB_ALLOC_NO_HINT); - memcpy(temp.data, arg_img->data, image_size(&temp)); - py_helper_set_to_framebuffer(&out); - - for (int y = 0, yy = out.h; y < yy; y++) { - uint32_t *row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(&temp, y); - uint16_t *out_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&out, y); - for (int x = 0, xx = out.w; x < xx; x++) { - IMAGE_PUT_RGB565_PIXEL_FAST(out_row_ptr, x, - imlib_yuv_to_rgb(IMAGE_GET_BINARY_PIXEL_FAST(row_ptr, x) * COLOR_GRAYSCALE_MAX, 0, 0)); - } - } - - fb_alloc_free_till_mark(); - } - break; - } - case IMAGE_BPP_GRAYSCALE: { - if (copy || (!py_helper_is_equal_to_framebuffer(&out))) { - PY_ASSERT_TRUE_MSG(copy, - "Can't convert to rgb565 in place!"); - for (int y = 0, yy = out.h; y < yy; y++) { - uint8_t *row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(arg_img, y); - uint16_t *out_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&out, y); - for (int x = 0, xx = out.w; x < xx; x++) { - IMAGE_PUT_RGB565_PIXEL_FAST(out_row_ptr, x, - imlib_yuv_to_rgb(IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x), 0, 0)); - } - } - } else { - image_t temp; - memcpy(&temp, arg_img, sizeof(image_t)); - fb_alloc_mark(); - temp.data = fb_alloc(image_size(&temp), FB_ALLOC_NO_HINT); - memcpy(temp.data, arg_img->data, image_size(&temp)); - py_helper_set_to_framebuffer(&out); - - for (int y = 0, yy = out.h; y < yy; y++) { - uint8_t *row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(&temp, y); - uint16_t *out_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&out, y); - for (int x = 0, xx = out.w; x < xx; x++) { - IMAGE_PUT_RGB565_PIXEL_FAST(out_row_ptr, x, - imlib_yuv_to_rgb(IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x), 0, 0)); - } - } - - fb_alloc_free_till_mark(); - } - break; - } - case IMAGE_BPP_RGB565: { - for (int y = 0, yy = out.h; y < yy; y++) { - uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(arg_img, y); - uint16_t *out_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&out, y); - for (int x = 0, xx = out.w; x < xx; x++) { - int pixel = IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x); - switch (channel) { - case 0: { - pixel = COLOR_R5_G6_B5_TO_RGB565(COLOR_RGB565_TO_R5(pixel), 0, 0); - break; - } - case 1: { - pixel = COLOR_R5_G6_B5_TO_RGB565(0, COLOR_RGB565_TO_G6(pixel), 0); - break; - } - case 2: { - pixel = COLOR_R5_G6_B5_TO_RGB565(0, 0, COLOR_RGB565_TO_B5(pixel)); - break; - } - default: { - break; - } - } - IMAGE_PUT_RGB565_PIXEL_FAST(out_row_ptr, x, pixel); - } - } - break; - } - default: { - break; - } - } - - if (!copy) { - arg_img->bpp = IMAGE_BPP_RGB565; - py_helper_update_framebuffer(&out); - } - - return py_image_from_struct(&out); + return py_image_to(IMAGE_BPP_RGB565, NULL, false, n_args, args, kw_args); } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_to_rgb565_obj, 1, py_image_to_rgb565); static mp_obj_t py_image_to_rainbow(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) { - image_t *arg_img = py_helper_arg_to_image_mutable(args[0]); - bool copy = py_helper_keyword_int(n_args, args, 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_copy), false); - int channel = py_helper_keyword_int(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_rgb_channel), -1); - int palette = py_helper_keyword_int(n_args, args, 3, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_color_palette), COLOR_PALETTE_RAINBOW); - const uint16_t *color_palette = NULL; - - if (palette == COLOR_PALETTE_RAINBOW) { - color_palette = rainbow_table; - } else if (palette == COLOR_PALETTE_IRONBOW) { - color_palette = ironbow_table; - } else { - mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Invalid color palette!")); - } - - image_t out; - out.w = arg_img->w; - out.h = arg_img->h; - out.bpp = IMAGE_BPP_RGB565; - out.data = copy ? xalloc(image_size(&out)) : arg_img->data; - - switch(arg_img->bpp) { - case IMAGE_BPP_BINARY: { - if (copy || (!py_helper_is_equal_to_framebuffer(&out))) { - PY_ASSERT_TRUE_MSG((out.w == 1) || copy, - "Can't convert to rainbow in place!"); - for (int y = 0, yy = out.h; y < yy; y++) { - uint32_t *row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(arg_img, y); - uint16_t *out_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&out, y); - for (int x = 0, xx = out.w; x < xx; x++) { - IMAGE_PUT_RGB565_PIXEL_FAST(out_row_ptr, x, - color_palette[IMAGE_GET_BINARY_PIXEL_FAST(row_ptr, x) * COLOR_GRAYSCALE_MAX]); - } - } - } else { - image_t temp; - memcpy(&temp, arg_img, sizeof(image_t)); - fb_alloc_mark(); - temp.data = fb_alloc(image_size(&temp), FB_ALLOC_NO_HINT); - memcpy(temp.data, arg_img->data, image_size(&temp)); - py_helper_set_to_framebuffer(&out); - - for (int y = 0, yy = out.h; y < yy; y++) { - uint32_t *row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(&temp, y); - uint16_t *out_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&out, y); - for (int x = 0, xx = out.w; x < xx; x++) { - IMAGE_PUT_RGB565_PIXEL_FAST(out_row_ptr, x, - color_palette[IMAGE_GET_BINARY_PIXEL_FAST(row_ptr, x) * COLOR_GRAYSCALE_MAX]); - } - } - - fb_alloc_free_till_mark(); - } - break; - } - case IMAGE_BPP_GRAYSCALE: { - if (copy || (!py_helper_is_equal_to_framebuffer(&out))) { - PY_ASSERT_TRUE_MSG(copy, - "Can't convert to rainbow in place!"); - for (int y = 0, yy = out.h; y < yy; y++) { - uint8_t *row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(arg_img, y); - uint16_t *out_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&out, y); - for (int x = 0, xx = out.w; x < xx; x++) { - IMAGE_PUT_RGB565_PIXEL_FAST(out_row_ptr, x, - color_palette[IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x)]); - } - } - } else { - image_t temp; - memcpy(&temp, arg_img, sizeof(image_t)); - fb_alloc_mark(); - temp.data = fb_alloc(image_size(&temp), FB_ALLOC_NO_HINT); - memcpy(temp.data, arg_img->data, image_size(&temp)); - py_helper_set_to_framebuffer(&out); - - for (int y = 0, yy = out.h; y < yy; y++) { - uint8_t *row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(&temp, y); - uint16_t *out_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&out, y); - for (int x = 0, xx = out.w; x < xx; x++) { - IMAGE_PUT_RGB565_PIXEL_FAST(out_row_ptr, x, - color_palette[IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, x)]); - } - } - - fb_alloc_free_till_mark(); - } - break; - } - case IMAGE_BPP_RGB565: { - for (int y = 0, yy = out.h; y < yy; y++) { - uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(arg_img, y); - uint16_t *out_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&out, y); - for (int x = 0, xx = out.w; x < xx; x++) { - int pixel = IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, x); - switch (channel) { - case 0: { - pixel = color_palette[COLOR_RGB565_TO_R8(pixel)]; - break; - } - case 1: { - pixel = color_palette[COLOR_RGB565_TO_G8(pixel)]; - break; - } - case 2: { - pixel = color_palette[COLOR_RGB565_TO_B8(pixel)]; - break; - } - default: { - pixel = color_palette[COLOR_RGB565_TO_GRAYSCALE(pixel)]; - break; - } - } - IMAGE_PUT_RGB565_PIXEL_FAST(out_row_ptr, x, pixel); - } - } - break; - } - default: { - break; - } - } - - if (!copy) { - arg_img->bpp = IMAGE_BPP_RGB565; - py_helper_update_framebuffer(&out); - } - - return py_image_from_struct(&out); + return py_image_to(IMAGE_BPP_RGB565, rainbow_table, false, n_args, args, kw_args); } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_to_rainbow_obj, 1, py_image_to_rainbow); +static mp_obj_t py_image_copy(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) +{ + return py_image_to(-1, NULL, true, n_args, args, kw_args); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_copy_obj, 1, py_image_copy); + +static mp_obj_t py_image_crop(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) +{ + return py_image_to(-1, NULL, false, n_args, args, kw_args); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_crop_obj, 1, py_image_crop); + static mp_obj_t py_image_jpeg_encode_for_ide(mp_obj_t img_obj) { image_t *arg_img = py_image_cobj(img_obj); @@ -1434,153 +1155,6 @@ static mp_obj_t py_image_compressed_for_ide(uint n_args, const mp_obj_t *args, m } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_compressed_for_ide_obj, 1, py_image_compressed_for_ide); -static mp_obj_t py_image_copy_int(uint n_args, const mp_obj_t *args, mp_map_t *kw_args, bool mode) -{ - // mode == false -> copy behavior - // mode == true -> crop/scale behavior - image_t *arg_img = py_helper_arg_to_image_mutable(args[0]); - - rectangle_t roi; - py_helper_keyword_rectangle_roi(arg_img, n_args, args, 1, kw_args, &roi); - - float arg_x_scale = - py_helper_keyword_float(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_x_scale), 1.0f); - PY_ASSERT_TRUE_MSG((0.0f <= arg_x_scale), "Error: 0.0 <= x_scale!"); - - float arg_y_scale = - py_helper_keyword_float(n_args, args, 3, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_y_scale), 1.0f); - PY_ASSERT_TRUE_MSG((0.0f <= arg_y_scale), "Error: 0.0 <= y_scale!"); - - mp_obj_t copy_to_fb_obj = py_helper_keyword_object(n_args, args, 4, kw_args, - MP_OBJ_NEW_QSTR(mode ? MP_QSTR_copy : MP_QSTR_copy_to_fb), NULL); - bool copy_to_fb = false; - image_t *arg_other = mode ? arg_img : NULL; - - if (copy_to_fb_obj) { - if (mp_obj_is_integer(copy_to_fb_obj)) { - if (!mode) { - copy_to_fb = mp_obj_get_int(copy_to_fb_obj); - } else if (mp_obj_get_int(copy_to_fb_obj)) { - arg_other = NULL; - } - } else { - arg_other = py_helper_arg_to_image_mutable(copy_to_fb_obj); - } - } - - if (copy_to_fb) { - fb_update_jpeg_buffer(); - } - - image_t image; - image.w = fast_floorf(roi.w * arg_x_scale); - PY_ASSERT_TRUE_MSG(image.w >= 1, "Output image width is 0!"); - image.h = fast_floorf(roi.h * arg_y_scale); - PY_ASSERT_TRUE_MSG(image.h >= 1, "Output image height is 0!"); - image.bpp = arg_img->bpp; - image.data = NULL; - - if (copy_to_fb) { - py_helper_set_to_framebuffer(&image); - } else if (arg_other) { - PY_ASSERT_TRUE_MSG((image_size(&image) <= image_size(arg_other)), "The new image won't fit in the target frame buffer!"); - image.data = arg_other->data; - } else { - image.data = xalloc(image_size(&image)); - } - - bool in_place = arg_img->data == image.data; - image_t temp; - - if (in_place) { - memcpy(&temp, arg_img, sizeof(image_t)); - fb_alloc_mark(); - temp.data = fb_alloc(image_size(&temp), FB_ALLOC_NO_HINT); - memcpy(temp.data, arg_img->data, image_size(&temp)); - arg_img = &temp; - if (copy_to_fb) { - py_helper_set_to_framebuffer(&image); - } - } - - float over_xscale = IM_DIV(1.0, arg_x_scale), over_yscale = IM_DIV(1.0f, arg_y_scale); - - switch(arg_img->bpp) { - case IMAGE_BPP_BINARY: { - for (int y = 0, yy = image.h; y < yy; y++) { - uint32_t *row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(arg_img, fast_floorf(y * over_yscale) + roi.y); - uint32_t *row_ptr_2 = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(&image, y); - for (int x = 0, xx = image.w; x < xx; x++) { - IMAGE_PUT_BINARY_PIXEL_FAST(row_ptr_2, x, - IMAGE_GET_BINARY_PIXEL_FAST(row_ptr, fast_floorf(x * over_xscale) + roi.x)); - } - } - break; - } - case IMAGE_BPP_GRAYSCALE: { - for (int y = 0, yy = image.h; y < yy; y++) { - uint8_t *row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(arg_img, fast_floorf(y * over_yscale) + roi.y); - uint8_t *row_ptr_2 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(&image, y); - for (int x = 0, xx = image.w; x < xx; x++) { - IMAGE_PUT_GRAYSCALE_PIXEL_FAST(row_ptr_2, x, - IMAGE_GET_GRAYSCALE_PIXEL_FAST(row_ptr, fast_floorf(x * over_xscale) + roi.x)); - } - } - break; - } - case IMAGE_BPP_RGB565: { - for (int y = 0, yy = image.h; y < yy; y++) { - uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(arg_img, fast_floorf(y * over_yscale) + roi.y); - uint16_t *row_ptr_2 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(&image, y); - for (int x = 0, xx = image.w; x < xx; x++) { - IMAGE_PUT_RGB565_PIXEL_FAST(row_ptr_2, x, - IMAGE_GET_RGB565_PIXEL_FAST(row_ptr, fast_floorf(x * over_xscale) + roi.x)); - } - } - break; - } - default: { - break; - } - } - - if (in_place) { - fb_alloc_free_till_mark(); - } - - py_helper_update_framebuffer(&image); - - if (copy_to_fb) { - image_t *arg_img = py_helper_arg_to_image_mutable(args[0]); - - if (py_helper_is_equal_to_framebuffer(arg_img)) { - arg_img->w = image.w; - arg_img->h = image.h; - arg_img->bpp = image.bpp; - } - } else { - if (arg_other) { - arg_other->w = image.w; - arg_other->h = image.h; - arg_other->bpp = image.bpp; - } - } - - return py_image_from_struct(&image); -} - -static mp_obj_t py_image_copy(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) -{ - return py_image_copy_int(n_args, args, kw_args, false); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_copy_obj, 1, py_image_copy); - -static mp_obj_t py_image_crop(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) -{ - return py_image_copy_int(n_args, args, kw_args, true); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_image_crop_obj, 1, py_image_crop); - #if defined(IMLIB_ENABLE_IMAGE_FILE_IO) static mp_obj_t py_image_save(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) {