/* * SPDX-License-Identifier: MIT * * Copyright (C) 2013-2024 OpenMV, LLC. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * * Basic drawing functions. */ #include "font.h" #include "imlib.h" #include "omv_gpu.h" #include "unaligned_memcpy.h" void *imlib_compute_row_ptr(const image_t *img, int y) { switch (img->pixfmt) { case PIXFORMAT_BINARY: { return IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, y); } case PIXFORMAT_GRAYSCALE: { return IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, y); } case PIXFORMAT_RGB565: { return IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y); } default: { // This shouldn't happen, at least we return a valid memory block return img->data; } } } inline int imlib_get_pixel_fast(image_t *img, const void *row_ptr, int x) { switch (img->pixfmt) { case PIXFORMAT_BINARY: { return IMAGE_GET_BINARY_PIXEL_FAST((uint32_t *) row_ptr, x); } case PIXFORMAT_GRAYSCALE: { return IMAGE_GET_GRAYSCALE_PIXEL_FAST((uint8_t *) row_ptr, x); } case PIXFORMAT_RGB565: { return IMAGE_GET_RGB565_PIXEL_FAST((uint16_t *) row_ptr, x); } default: { return -1; } } } // Set pixel (handles boundary check and image type check). void imlib_set_pixel(image_t *img, int x, int y, int p) { if ((0 <= x) && (x < img->w) && (0 <= y) && (y < img->h)) { switch (img->pixfmt) { case PIXFORMAT_BINARY: { IMAGE_PUT_BINARY_PIXEL(img, x, y, p); break; } case PIXFORMAT_GRAYSCALE: { IMAGE_PUT_GRAYSCALE_PIXEL(img, x, y, p); break; } case PIXFORMAT_RGB565: { IMAGE_PUT_RGB565_PIXEL(img, x, y, p); break; } default: { break; } } } } // https://stackoverflow.com/questions/1201200/fast-algorithm-for-drawing-filled-circles static void point_fill(image_t *img, int cx, int cy, int r0, int r1, int c) { for (int y = r0; y <= r1; y++) { for (int x = r0; x <= r1; x++) { if (((x * x) + (y * y)) <= (r0 * r0)) { imlib_set_pixel(img, cx + x, cy + y, c); } } } } static void imlib_set_pixel_aa(image_t *img, int x, int y, int err, int c) { if (!((0 <= x) && (x < img->w) && (0 <= y) && (y < img->h))) { return; } switch (img->pixfmt) { case PIXFORMAT_BINARY: { uint32_t *ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, y); int old_c = IMAGE_GET_BINARY_PIXEL_FAST(ptr, x) * 255; int new_c = (((old_c * err) + ((c ? 255 : 0) * (256 - err))) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(ptr, x, new_c); break; } case PIXFORMAT_GRAYSCALE: { uint8_t *ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, y); int old_c = IMAGE_GET_GRAYSCALE_PIXEL_FAST(ptr, x); int new_c = ((old_c * err) + ((c & 0xff) * (256 - err))) >> 8; IMAGE_PUT_GRAYSCALE_PIXEL_FAST(ptr, x, new_c); break; } case PIXFORMAT_RGB565: { uint16_t *ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y); int old_c = IMAGE_GET_RGB565_PIXEL_FAST(ptr, x); int old_c_r5 = COLOR_RGB565_TO_R5(old_c); int old_c_g6 = COLOR_RGB565_TO_G6(old_c); int old_c_b5 = COLOR_RGB565_TO_B5(old_c); int c_r5 = COLOR_RGB565_TO_R5(c); int c_g6 = COLOR_RGB565_TO_G6(c); int c_b5 = COLOR_RGB565_TO_B5(c); int new_c_r5 = ((old_c_r5 * err) + (c_r5 * (256 - err))) >> 8; int new_c_g6 = ((old_c_g6 * err) + (c_g6 * (256 - err))) >> 8; int new_c_b5 = ((old_c_b5 * err) + (c_b5 * (256 - err))) >> 8; int new_c = COLOR_R5_G6_B5_TO_RGB565(new_c_r5, new_c_g6, new_c_b5); IMAGE_PUT_RGB565_PIXEL_FAST(ptr, x, new_c); break; } default: { break; } } } // https://gist.github.com/randvoorhies/807ce6e20840ab5314eb7c547899de68#file-bresenham-js-L381 static void imlib_draw_thin_line(image_t *img, int x0, int y0, int x1, int y1, int c) { const int dx = abs(x1 - x0); const int sx = x0 < x1 ? 1 : -1; const int dy = abs(y1 - y0); const int sy = y0 < y1 ? 1 : -1; int err = dx - dy; int e2, x2; // error value e_xy int ed = dx + dy == 0 ? 1 : fast_floorf(fast_sqrtf(dx * dx + dy * dy)); for (;;) { // pixel loop imlib_set_pixel_aa(img, x0, y0, 256 * abs(err - dx + dy) / ed, c); e2 = err; x2 = x0; if (2 * e2 >= -dx) { // x step if (x0 == x1) { break; } if (e2 + dy < ed) { imlib_set_pixel_aa(img, x0, y0 + sy, 256 * (e2 + dy) / ed, c); } err -= dy; x0 += sx; } if (2 * e2 <= dy) { // y step if (y0 == y1) { break; } if (dx - e2 < ed) { imlib_set_pixel_aa(img, x2 + sx, y0, 256 * (dx - e2) / ed, c); } err += dx; y0 += sy; } } } // https://gist.github.com/randvoorhies/807ce6e20840ab5314eb7c547899de68#file-bresenham-js-L813 void imlib_draw_line(image_t *img, int x0, int y0, int x1, int y1, int c, int th) { line_t line = {x0, y0, x1, y1}; if (!lb_clip_line(&line, 0, 0, img->w, img->h)) { return; } x0 = line.x1; y0 = line.y1; x1 = line.x2; y1 = line.y2; // plot an anti-aliased line of width th pixel const int ex = abs(x1 - x0); const int sx = x0 < x1 ? 1 : -1; const int ey = abs(y1 - y0); const int sy = y0 < y1 ? 1 : -1; int e2 = fast_floorf(fast_sqrtf(ex * ex + ey * ey)); // length if (th <= 1 || e2 == 0) { return imlib_draw_thin_line(img, x0, y0, x1, y1, c); // assert } int dx = ex * 256 / e2; int dy = ey * 256 / e2; th = 256 * (th - 1); // scale values if (dx < dy) { // steep line x1 = (e2 + th / 2) / dy; // start offset int err = x1 * dy - th / 2; // shift error value to offset width err = IM_MAX(err, 0); // prevent negative error on straight line for (x0 -= x1 * sx;; y0 += sy) { x1 = x0; imlib_set_pixel_aa(img, x1, y0, err, c); // aliasing pre-pixel for (e2 = dy - err - th; e2 + dy < 256; e2 += dy) { x1 += sx; imlib_set_pixel(img, x1, y0, c); // pixel on the line } imlib_set_pixel_aa(img, x1 + sx, y0, e2, c); // aliasing post-pixel if (y0 == y1) { break; } err += dx; // y-step if (err > 256) { err -= dy; x0 += sx; } // x-step } } else { // flat line y1 = (e2 + th / 2) / dx; // start offset int err = y1 * dx - th / 2; // shift error value to offset width err = IM_MAX(err, 0); // prevent negative error on straight line for (y0 -= y1 * sy;; x0 += sx) { y1 = y0; imlib_set_pixel_aa(img, x0, y1, err, c); // aliasing pre-pixel for (e2 = dx - err - th; e2 + dx < 256; e2 += dx) { y1 += sy; imlib_set_pixel(img, x0, y1, c); // pixel on the line } imlib_set_pixel_aa(img, x0, y1 + sy, e2, c); // aliasing post-pixel if (x0 == x1) { break; } err += dy; // x-step if (err > 256) { err -= dx; y0 += sy; } // y-step } } } static void xLine(image_t *img, int x1, int x2, int y, int c) { while (x1 <= x2) { imlib_set_pixel(img, x1++, y, c); } } static void yLine(image_t *img, int x, int y1, int y2, int c) { while (y1 <= y2) { imlib_set_pixel(img, x, y1++, c); } } void imlib_draw_rectangle(image_t *img, int rx, int ry, int rw, int rh, int c, int thickness, bool fill) { if (fill) { for (int y = ry, yy = ry + rh; y < yy; y++) { for (int x = rx, xx = rx + rw; x < xx; x++) { imlib_set_pixel(img, x, y, c); } } } else if (thickness > 0) { int thickness0 = (thickness - 0) / 2; int thickness1 = (thickness - 1) / 2; for (int i = rx - thickness0, j = rx + rw + thickness1, k = ry + rh - 1; i < j; i++) { yLine(img, i, ry - thickness0, ry + thickness1, c); yLine(img, i, k - thickness0, k + thickness1, c); } for (int i = ry - thickness0, j = ry + rh + thickness1, k = rx + rw - 1; i < j; i++) { xLine(img, rx - thickness0, rx + thickness1, i, c); xLine(img, k - thickness0, k + thickness1, i, c); } } } // https://gist.github.com/randvoorhies/807ce6e20840ab5314eb7c547899de68#file-bresenham-js-L404 static void imlib_draw_circle_thin(image_t *img, int cx, int cy, int r, int c, bool fill) { int x = r; int y = 0; // II. quadrant from bottom left to top right int err = 2 - (2 * r); // error of 1.step r = 1 - err; for (;;) { int i = 256 * abs(err + (2 * (x + y)) - 2) / r; // get blend value of pixel imlib_set_pixel_aa(img, cx + x, cy - y, i, c); // I. Quadrant imlib_set_pixel_aa(img, cx + y, cy + x, i, c); // II. Quadrant imlib_set_pixel_aa(img, cx - x, cy + y, i, c); // III. Quadrant imlib_set_pixel_aa(img, cx - y, cy - x, i, c); // IV. Quadrant if (fill) { xLine(img, cx, cx + x - 1, cy - y, c); yLine(img, cx + y, cy, cy + x - 1, c); xLine(img, cx - x + 1, cx, cy + y, c); yLine(img, cx - y, cy - x + 1, cy, c); } if (x == 0) { break; } int e2 = err; int x2 = x; // remember values if (err > y) { // x step i = 256 * (err + (2 * x) - 1) / r; // outward pixel if (i < 256) { imlib_set_pixel_aa(img, cx + x, cy - y + 1, i, c); imlib_set_pixel_aa(img, cx + y - 1, cy + x, i, c); imlib_set_pixel_aa(img, cx - x, cy + y - 1, i, c); imlib_set_pixel_aa(img, cx - y + 1, cy - x, i, c); } err -= (--x * 2) - 1; } if (e2 <= x2--) { // y step if (!fill) { i = 256 * (1 - (2 * y) - e2) / r; // inward pixel if (i < 256) { imlib_set_pixel_aa(img, cx + x2, cy - y, i, c); imlib_set_pixel_aa(img, cx + y, cy + x2, i, c); imlib_set_pixel_aa(img, cx - x2, cy + y, i, c); imlib_set_pixel_aa(img, cx - y, cy - x2, i, c); } } err -= (--y * 2) - 1; } } } // https://stackoverflow.com/questions/27755514/circle-with-thickness-drawing-algorithm void imlib_draw_circle(image_t *img, int cx, int cy, int r, int c, int thickness, bool fill) { if ((r == 0) && (fill || (thickness > 0))) { imlib_set_pixel(img, cx, cy, c); } if ((r <= 0) || ((!fill) && (thickness <= 0))) { return; } if (thickness == 1 || fill) { imlib_draw_circle_thin(img, cx, cy, r + (IM_MAX(thickness, 0) / 2), c, fill); } else { int thickness0 = (thickness - 0) / 2; int thickness1 = (thickness - 1) / 2; int xo = r + thickness0; int xi = IM_MAX(r - thickness1, 0); int xi_tmp = xi; int y = 0; int erro = 1 - xo; int erri = 1 - xi; while (xo >= y) { xLine(img, cx + xi, cx + xo, cy + y, c); yLine(img, cx + y, cy + xi, cy + xo, c); xLine(img, cx - xo, cx - xi, cy + y, c); yLine(img, cx - y, cy + xi, cy + xo, c); xLine(img, cx - xo, cx - xi, cy - y, c); yLine(img, cx - y, cy - xo, cy - xi, c); xLine(img, cx + xi, cx + xo, cy - y, c); yLine(img, cx + y, cy - xo, cy - xi, c); y++; if (erro < 0) { erro += 2 * y + 1; } else { xo--; erro += 2 * (y - xo + 1); } if (y > xi_tmp) { xi = y; } else { if (erri < 0) { erri += 2 * y + 1; } else { xi--; erri += 2 * (y - xi + 1); } } } // Anti-alias the outer and inner edges. imlib_draw_circle_thin(img, cx, cy, r + thickness0, c, false); imlib_draw_circle_thin(img, cx, cy, xi_tmp, c, false); } } // https://scratch.mit.edu/projects/50039326/ static void scratch_draw_pixel(image_t *img, int x0, int y0, int dx, int dy, float shear_dx, float shear_dy, int r0, int r1, int c) { point_fill(img, x0 + dx, y0 + dy + fast_floorf((dx * shear_dy) / shear_dx), r0, r1, c); } // https://scratch.mit.edu/projects/50039326/ static void scratch_draw_line(image_t *img, int x0, int y0, int dx, int dy0, int dy1, float shear_dx, float shear_dy, int c) { int y = y0 + fast_floorf((dx * shear_dy) / shear_dx); yLine(img, x0 + dx, y + dy0, y + dy1, c); } // https://scratch.mit.edu/projects/50039326/ static void scratch_draw_sheared_ellipse(image_t *img, int x0, int y0, int width, int height, bool filled, float shear_dx, float shear_dy, int c, int thickness) { int thickness0 = (thickness - 0) / 2; int thickness1 = (thickness - 1) / 2; if (((thickness > 0) || filled) && (shear_dx != 0)) { int a_squared = width * width; int four_a_squared = a_squared * 4; int b_squared = height * height; int four_b_squared = b_squared * 4; int x = 0; int y = height; int sigma = (2 * b_squared) + (a_squared * (1 - (2 * height))); while ((b_squared * x) <= (a_squared * y)) { if (filled) { scratch_draw_line(img, x0, y0, x, -y, y, shear_dx, shear_dy, c); scratch_draw_line(img, x0, y0, -x, -y, y, shear_dx, shear_dy, c); } else { scratch_draw_pixel(img, x0, y0, x, y, shear_dx, shear_dy, -thickness0, thickness1, c); scratch_draw_pixel(img, x0, y0, -x, y, shear_dx, shear_dy, -thickness0, thickness1, c); scratch_draw_pixel(img, x0, y0, x, -y, shear_dx, shear_dy, -thickness0, thickness1, c); scratch_draw_pixel(img, x0, y0, -x, -y, shear_dx, shear_dy, -thickness0, thickness1, c); } if (sigma >= 0) { sigma += four_a_squared * (1 - y); y -= 1; } sigma += b_squared * ((4 * x) + 6); x += 1; } x = width; y = 0; sigma = (2 * a_squared) + (b_squared * (1 - (2 * width))); while ((a_squared * y) <= (b_squared * x)) { if (filled) { scratch_draw_line(img, x0, y0, x, -y, y, shear_dx, shear_dy, c); scratch_draw_line(img, x0, y0, -x, -y, y, shear_dx, shear_dy, c); } else { scratch_draw_pixel(img, x0, y0, x, y, shear_dx, shear_dy, -thickness0, thickness1, c); scratch_draw_pixel(img, x0, y0, -x, y, shear_dx, shear_dy, -thickness0, thickness1, c); scratch_draw_pixel(img, x0, y0, x, -y, shear_dx, shear_dy, -thickness0, thickness1, c); scratch_draw_pixel(img, x0, y0, -x, -y, shear_dx, shear_dy, -thickness0, thickness1, c); } if (sigma >= 0) { sigma += four_b_squared * (1 - x); x -= 1; } sigma += a_squared * ((4 * y) + 6); y += 1; } } } // https://scratch.mit.edu/projects/50039326/ static void scratch_draw_rotated_ellipse(image_t *img, int x, int y, int x_axis, int y_axis, int rotation, bool filled, int c, int thickness) { if ((x_axis > 0) && (y_axis > 0)) { if ((x_axis == y_axis) || (rotation == 0)) { scratch_draw_sheared_ellipse(img, x, y, x_axis / 2, y_axis / 2, filled, 1, 0, c, thickness); } else if (rotation == 90) { scratch_draw_sheared_ellipse(img, x, y, y_axis / 2, x_axis / 2, filled, 1, 0, c, thickness); } else { // Avoid rotations above 90. if (rotation > 90) { rotation -= 90; int temp = x_axis; x_axis = y_axis; y_axis = temp; } // Avoid rotations above 45. if (rotation > 45) { rotation -= 90; int temp = x_axis; x_axis = y_axis; y_axis = temp; } float theta = fast_atanf(IM_DIV(y_axis, x_axis) * (-tanf(IM_DEG2RAD(rotation)))); float shear_dx = (x_axis * cosf(theta) * cosf(IM_DEG2RAD(rotation))) - (y_axis * sinf(theta) * sinf(IM_DEG2RAD(rotation))); float shear_dy = (x_axis * cosf(theta) * sinf(IM_DEG2RAD(rotation))) + (y_axis * sinf(theta) * cosf(IM_DEG2RAD(rotation))); float shear_x_axis = fast_fabsf(shear_dx); float shear_y_axis = IM_DIV((y_axis * x_axis), shear_x_axis); scratch_draw_sheared_ellipse(img, x, y, fast_floorf(shear_x_axis / 2), fast_floorf(shear_y_axis / 2), filled, shear_dx, shear_dy, c, thickness); } } } void imlib_draw_ellipse(image_t *img, int cx, int cy, int rx, int ry, int rotation, int c, int thickness, bool fill) { int r = rotation % 180; if (r < 0) { r += 180; } scratch_draw_rotated_ellipse(img, cx, cy, rx * 2, ry * 2, r, fill, c, thickness); } // char rotation == 0, 90, 180, 360, etc. // string rotation == 0, 90, 180, 360, etc. void imlib_draw_string(image_t *img, int x_off, int y_off, const char *str, int c, float scale, int x_spacing, int y_spacing, bool mono_space, int char_rotation, bool char_hmirror, bool char_vflip, int string_rotation, bool string_hmirror, bool string_vflip) { char_rotation %= 360; if (char_rotation < 0) { char_rotation += 360; } char_rotation = (char_rotation / 90) * 90; string_rotation %= 360; if (string_rotation < 0) { string_rotation += 360; } string_rotation = (string_rotation / 90) * 90; bool char_swap_w_h = (char_rotation == 90) || (char_rotation == 270); bool char_upsidedown = (char_rotation == 180) || (char_rotation == 270); if (string_hmirror) { x_off -= fast_floorf(font[0].w * scale) - 1; } if (string_vflip) { y_off -= fast_floorf(font[0].h * scale) - 1; } int org_x_off = x_off; int org_y_off = y_off; const int anchor = x_off; for (char ch, last = '\0'; (ch = *str); str++, last = ch) { if ((last == '\r') && (ch == '\n')) { // handle "\r\n" strings continue; } if ((ch == '\n') || (ch == '\r')) { // handle '\n' or '\r' strings x_off = anchor; y_off += (string_vflip ? -1 : +1) * (fast_floorf((char_swap_w_h ? font[0].w : font[0].h) * scale) + y_spacing); // newline height == space height continue; } if ((ch < ' ') || (ch > '~')) { // handle unknown characters continue; } const glyph_t *g = &font[ch - ' ']; if (!mono_space) { // Find the first pixel set and offset to that. bool exit = false; if (!char_swap_w_h) { for (int x = 0, xx = g->w; x < xx; x++) { for (int y = 0, yy = g->h; y < yy; y++) { if (g->data[(char_upsidedown ^ char_vflip) ? (g->h - 1 - y) : y] & (1 << ((char_upsidedown ^ char_hmirror ^ string_hmirror) ? x : (g->w - 1 - x)))) { x_off += (string_hmirror ? +1 : -1) * fast_floorf(x * scale); exit = true; break; } } if (exit) { break; } } } else { for (int y = g->h - 1; y >= 0; y--) { for (int x = 0, xx = g->w; x < xx; x++) { if (g->data[(char_upsidedown ^ char_vflip) ? (g->h - 1 - y) : y] & (1 << ((char_upsidedown ^ char_hmirror ^ string_hmirror) ? x : (g->w - 1 - x)))) { x_off += (string_hmirror ? +1 : -1) * fast_floorf((g->h - 1 - y) * scale); exit = true; break; } } if (exit) { break; } } } } for (int y = 0, yy = fast_floorf(g->h * scale); y < yy; y++) { for (int x = 0, xx = fast_floorf(g->w * scale); x < xx; x++) { if (g->data[fast_floorf(y / scale)] & (1 << (g->w - 1 - fast_floorf(x / scale)))) { int16_t x_tmp = x_off + (char_hmirror ? (xx - x - 1) : x), y_tmp = y_off + (char_vflip ? (yy - y - 1) : y); point_rotate(x_tmp, y_tmp, IM_DEG2RAD(char_rotation), x_off + (xx / 2), y_off + (yy / 2), &x_tmp, &y_tmp); point_rotate(x_tmp, y_tmp, IM_DEG2RAD(string_rotation), org_x_off, org_y_off, &x_tmp, &y_tmp); imlib_set_pixel(img, x_tmp, y_tmp, c); } } } if (mono_space) { x_off += (string_hmirror ? -1 : +1) * (fast_floorf((char_swap_w_h ? g->h : g->w) * scale) + x_spacing); } else { // Find the last pixel set and offset to that. bool exit = false; if (!char_swap_w_h) { for (int x = g->w - 1; x >= 0; x--) { for (int y = g->h - 1; y >= 0; y--) { if (g->data[(char_upsidedown ^ char_vflip) ? (g->h - 1 - y) : y] & (1 << ((char_upsidedown ^ char_hmirror ^ string_hmirror) ? x : (g->w - 1 - x)))) { x_off += (string_hmirror ? -1 : +1) * (fast_floorf((x + 2) * scale) + x_spacing); exit = true; break; } } if (exit) { break; } } } else { for (int y = 0, yy = g->h; y < yy; y++) { for (int x = g->w - 1; x >= 0; x--) { if (g->data[(char_upsidedown ^ char_vflip) ? (g->h - 1 - y) : y] & (1 << ((char_upsidedown ^ char_hmirror ^ string_hmirror) ? x : (g->w - 1 - x)))) { x_off += (string_hmirror ? -1 : +1) * (fast_floorf(((g->h - 1 - y) + 2) * scale) + x_spacing); exit = true; break; } } if (exit) { break; } } } if (!exit) { x_off += (string_hmirror ? -1 : +1) * fast_floorf(scale * 3); // space char } } } } void imlib_draw_row_setup(imlib_draw_row_data_t *data) { image_t temp; temp.w = data->dst_img->w; temp.h = data->dst_img->h; temp.pixfmt = data->src_img_pixfmt; // 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->row_buffer = fb_alloc(image_row_size, FB_ALLOC_CACHE_ALIGN); int alpha = data->alpha, max = 256; // To avoid having to divide by 255 scale alpha to 0-256 so we can right shift by 8. alpha = fast_roundf((alpha * 256) / 255.0f); if (data->dst_img->pixfmt == PIXFORMAT_RGB565) { alpha >>= 3; // 5-bit alpha for RGB565 max = 32; } data->smuad_alpha = data->black_background ? alpha : ((alpha << 16) | (max - alpha)); if (data->alpha_palette) { data->smuad_alpha_palette = fb_alloc(256 * sizeof(uint32_t), FB_ALLOC_NO_HINT); for (int i = 0, a = alpha; i < 256; i++) { int new_alpha = fast_roundf((a * data->alpha_palette[i]) / 255.0f); data->smuad_alpha_palette[i] = data->black_background ? new_alpha : ((new_alpha << 16) | (max - new_alpha)); } } else { data->smuad_alpha_palette = NULL; } } void imlib_draw_row_teardown(imlib_draw_row_data_t *data) { if (data->smuad_alpha_palette) { fb_free(); } fb_free(); // data->row_buffer } // Draws (x_end - x_start) pixels. // src width must be equal to dst width. void imlib_draw_row(int x_start, int x_end, int y_row, imlib_draw_row_data_t *data) { #define BLEND_RGB566(src_pixel, dst_pixel, smuad_alpha) \ ({ \ __typeof__ (src_pixel) _src_pixel = (src_pixel); \ __typeof__ (dst_pixel) _dst_pixel = (dst_pixel); \ __typeof__ (smuad_alpha) _smuad_alpha = (smuad_alpha); \ const long mask_r = 0x7c007c00, mask_g = 0x07e007e0, mask_b = 0x001f001f; \ uint32_t rgb = (_src_pixel << 16) | _dst_pixel; \ long rb = ((rgb >> 1) & mask_r) | (rgb & mask_b); \ long g = rgb & mask_g; \ int rb_out = __SMUAD(_smuad_alpha, rb) >> 5; \ int g_out = __SMUAD(_smuad_alpha, g) >> 5; \ ((rb_out << 1) & 0xf800) | (g_out & 0x07e0) | (rb_out & 0x001f); \ }) #define BLEND_RGB566_0(src_pixel, smuad_alpha) \ ({ \ __typeof__ (src_pixel) _src_pixel = (src_pixel); \ __typeof__ (smuad_alpha) _smuad_alpha = (smuad_alpha); \ int rb_out = ((_src_pixel & 0xf81f) * _smuad_alpha) >> 5; \ int g_out = ((_src_pixel & 0x7e0) * _smuad_alpha) >> 5; \ (rb_out & 0xf81f) | (g_out & 0x7e0); \ }) #define COLOR_GRAYSCALE_BINARY_MIN_LSL16 (COLOR_GRAYSCALE_BINARY_MIN << 16) #define COLOR_GRAYSCALE_BINARY_MAX_LSL16 (COLOR_GRAYSCALE_BINARY_MAX << 16) switch (data->dst_img->pixfmt) { case PIXFORMAT_BINARY: { uint32_t *dst32 = data->dst_row_override ? ((uint32_t *) data->dst_row_override) : IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(data->dst_img, y_row); switch (data->src_img_pixfmt) { case PIXFORMAT_BINARY: { uint32_t *src32 = (uint32_t *) data->row_buffer; if (data->smuad_alpha_palette) { const uint32_t *smuad_alpha_palette = data->smuad_alpha_palette; if (!data->color_palette) { uint32_t alpha_pal0 = smuad_alpha_palette[0], alpha_pal255 = smuad_alpha_palette[255]; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = IMAGE_GET_BINARY_PIXEL_FAST(src32, x); long smuad_alpha = pixel ? alpha_pal255 : alpha_pal0; long smuad_pixel = (pixel ? COLOR_GRAYSCALE_BINARY_MAX_LSL16 : COLOR_GRAYSCALE_BINARY_MIN_LSL16) | (IMAGE_GET_BINARY_PIXEL_FAST(dst32, x) ? COLOR_GRAYSCALE_BINARY_MAX : COLOR_GRAYSCALE_BINARY_MIN); pixel = (__SMUAD(smuad_alpha, smuad_pixel) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } else { for (int x = x_start; x < x_end; x++) { int pixel = IMAGE_GET_BINARY_PIXEL_FAST(src32, x); long smuad_alpha = pixel ? alpha_pal255 : alpha_pal0; long smuad_pixel = pixel ? COLOR_GRAYSCALE_BINARY_MAX : COLOR_GRAYSCALE_BINARY_MIN; pixel = ((smuad_alpha * smuad_pixel) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } } else { const uint16_t *color_palette = data->color_palette; uint32_t alpha_pal0 = smuad_alpha_palette[0], alpha_pal255 = smuad_alpha_palette[255]; uint32_t pal0 = color_palette[0], pal255 = color_palette[255]; pal0 = COLOR_RGB565_TO_Y(pal0) << 16; pal255 = COLOR_RGB565_TO_Y(pal255) << 16; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = IMAGE_GET_BINARY_PIXEL_FAST(src32, x); long smuad_alpha = pixel ? alpha_pal255 : alpha_pal0; long smuad_pixel = (pixel ? pal255 : pal0) | (IMAGE_GET_BINARY_PIXEL_FAST(dst32, x) ? COLOR_GRAYSCALE_BINARY_MAX : COLOR_GRAYSCALE_BINARY_MIN); pixel = (__SMUAD(smuad_alpha, smuad_pixel) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } else { for (int x = x_start; x < x_end; x++) { int pixel = IMAGE_GET_BINARY_PIXEL_FAST(src32, x); long smuad_alpha = pixel ? alpha_pal255 : alpha_pal0; long smuad_pixel = pixel ? pal255 : pal0; pixel = ((smuad_alpha * smuad_pixel) >> 24) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } } } else if (data->alpha == 255) { if (!data->color_palette) { for (int x = x_start; x < x_end; x++) { int pixel = IMAGE_GET_BINARY_PIXEL_FAST(src32, x); IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } else { const uint16_t *color_palette = data->color_palette; uint16_t pal0 = color_palette[0], pal255 = color_palette[255]; pal0 = COLOR_RGB565_TO_Y(pal0) > 127; pal255 = COLOR_RGB565_TO_Y(pal255) > 127; switch ((pal0 << 1) | (pal255 << 0)) { case 0: { for (int x = x_start; x < x_end; x++) { IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, 0); } break; } case 1: { for (int x = x_start; x < x_end; x++) { int pixel = IMAGE_GET_BINARY_PIXEL_FAST(src32, x); IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } break; } case 2: { for (int x = x_start; x < x_end; x++) { int pixel = !IMAGE_GET_BINARY_PIXEL_FAST(src32, x); IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } break; } case 3: { for (int x = x_start; x < x_end; x++) { IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, 1); } break; } } } } else { long smuad_alpha = data->smuad_alpha; if (!data->color_palette) { if (!data->black_background) { for (int x = x_start; x < x_end; x++) { long smuad_pixel = (IMAGE_GET_BINARY_PIXEL_FAST(src32, x) ? COLOR_GRAYSCALE_BINARY_MAX_LSL16 : COLOR_GRAYSCALE_BINARY_MIN_LSL16) | (IMAGE_GET_BINARY_PIXEL_FAST(dst32, x) ? COLOR_GRAYSCALE_BINARY_MAX : COLOR_GRAYSCALE_BINARY_MIN); int pixel = (__SMUAD(smuad_alpha, smuad_pixel) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } else { for (int x = x_start; x < x_end; x++) { long smuad_pixel = IMAGE_GET_BINARY_PIXEL_FAST(src32, x) ? COLOR_GRAYSCALE_BINARY_MAX : COLOR_GRAYSCALE_BINARY_MIN; int pixel = ((smuad_alpha * smuad_pixel) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } } else { const uint16_t *color_palette = data->color_palette; uint32_t pal0 = color_palette[0], pal255 = color_palette[255]; pal0 = COLOR_RGB565_TO_Y(pal0) << 16; pal255 = COLOR_RGB565_TO_Y(pal255) << 16; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { long smuad_pixel = (IMAGE_GET_BINARY_PIXEL_FAST(src32, x) ? pal255 : pal0) | (IMAGE_GET_BINARY_PIXEL_FAST(dst32, x) ? COLOR_GRAYSCALE_BINARY_MAX : COLOR_GRAYSCALE_BINARY_MIN); int pixel = (__SMUAD(smuad_alpha, smuad_pixel) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } else { for (int x = x_start; x < x_end; x++) { long smuad_pixel = IMAGE_GET_BINARY_PIXEL_FAST(src32, x) ? pal255 : pal0; int pixel = ((smuad_alpha * smuad_pixel) >> 24) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } } } break; } case PIXFORMAT_GRAYSCALE: { uint8_t *src8 = ((uint8_t *) data->row_buffer) + x_start; if (data->smuad_alpha_palette) { const uint32_t *smuad_alpha_palette = data->smuad_alpha_palette; if (!data->color_palette) { if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src8++; long smuad_alpha = smuad_alpha_palette[pixel]; long smuad_pixel = (pixel << 16) | (IMAGE_GET_BINARY_PIXEL_FAST(dst32, x) ? COLOR_GRAYSCALE_BINARY_MAX : COLOR_GRAYSCALE_BINARY_MIN); pixel = (__SMUAD(smuad_alpha, smuad_pixel) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src8++; long smuad_alpha = smuad_alpha_palette[pixel]; pixel = ((smuad_alpha * pixel) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } } else { const uint16_t *color_palette = data->color_palette; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src8++; long smuad_alpha = smuad_alpha_palette[pixel]; pixel = color_palette[pixel]; pixel = COLOR_RGB565_TO_Y(pixel); long smuad_pixel = (pixel << 16) | (IMAGE_GET_BINARY_PIXEL_FAST(dst32, x) ? COLOR_GRAYSCALE_BINARY_MAX : COLOR_GRAYSCALE_BINARY_MIN); pixel = (__SMUAD(smuad_alpha, smuad_pixel) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src8++; long smuad_alpha = smuad_alpha_palette[pixel]; pixel = color_palette[pixel]; pixel = COLOR_RGB565_TO_Y(pixel); pixel = ((smuad_alpha * pixel) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } } } else if (data->alpha == 255) { if (!data->color_palette) { for (int x = x_start; x < x_end; x++) { int pixel = *src8++ > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } else { const uint16_t *color_palette = data->color_palette; for (int x = x_start; x < x_end; x++) { int pixel = color_palette[*src8++]; pixel = COLOR_RGB565_TO_Y(pixel) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } } else { long smuad_alpha = data->smuad_alpha; if (!data->color_palette) { if (!data->black_background) { for (int x = x_start; x < x_end; x++) { long smuad_pixel = (*src8++ << 16) | (IMAGE_GET_BINARY_PIXEL_FAST(dst32, x) ? COLOR_GRAYSCALE_BINARY_MAX : COLOR_GRAYSCALE_BINARY_MIN); int pixel = (__SMUAD(smuad_alpha, smuad_pixel) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } else { for (int x = x_start; x < x_end; x++) { int pixel = ((smuad_alpha * (*src8++)) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } } else { const uint16_t *color_palette = data->color_palette; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = color_palette[*src8++]; pixel = COLOR_RGB565_TO_Y(pixel); long smuad_pixel = (pixel << 16) | (IMAGE_GET_BINARY_PIXEL_FAST(dst32, x) ? COLOR_GRAYSCALE_BINARY_MAX : COLOR_GRAYSCALE_BINARY_MIN); pixel = (__SMUAD(smuad_alpha, smuad_pixel) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } else { for (int x = x_start; x < x_end; x++) { int pixel = color_palette[*src8++]; pixel = COLOR_RGB565_TO_Y(pixel); pixel = ((smuad_alpha * pixel) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } } } break; } case PIXFORMAT_RGB565: { uint16_t *src16 = ((uint16_t *) data->row_buffer) + x_start; if (data->rgb_channel < 0) { if (data->smuad_alpha_palette) { const uint32_t *smuad_alpha_palette = data->smuad_alpha_palette; if (!data->color_palette) { if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = COLOR_RGB565_TO_Y(pixel); long smuad_alpha = smuad_alpha_palette[pixel]; long smuad_pixel = (pixel << 16) | (IMAGE_GET_BINARY_PIXEL_FAST(dst32, x) ? COLOR_GRAYSCALE_BINARY_MAX : COLOR_GRAYSCALE_BINARY_MIN); pixel = (__SMUAD(smuad_alpha, smuad_pixel) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = COLOR_RGB565_TO_Y(pixel); long smuad_alpha = smuad_alpha_palette[pixel]; pixel = ((smuad_alpha * pixel) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } } else { const uint16_t *color_palette = data->color_palette; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; int pixel_y = COLOR_RGB565_TO_Y(pixel); long smuad_alpha = smuad_alpha_palette[pixel_y]; pixel = color_palette[pixel_y]; long smuad_pixel = (COLOR_RGB565_TO_Y(pixel) << 16) | (IMAGE_GET_BINARY_PIXEL_FAST(dst32, x) ? COLOR_GRAYSCALE_BINARY_MAX : COLOR_GRAYSCALE_BINARY_MIN); pixel = (__SMUAD(smuad_alpha, smuad_pixel) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; int pixel_y = COLOR_RGB565_TO_Y(pixel); long smuad_alpha = smuad_alpha_palette[pixel_y]; pixel = color_palette[pixel_y]; pixel = ((smuad_alpha * COLOR_RGB565_TO_Y(pixel)) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } } } else if (data->alpha == 255) { if (!data->color_palette) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = COLOR_RGB565_TO_Y(pixel) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } else { const uint16_t *color_palette = data->color_palette; for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = color_palette[COLOR_RGB565_TO_Y(pixel)]; pixel = COLOR_RGB565_TO_Y(pixel) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } } else { long smuad_alpha = data->smuad_alpha; if (!data->color_palette) { if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; long smuad_pixel = (COLOR_RGB565_TO_Y(pixel) << 16) | (IMAGE_GET_BINARY_PIXEL_FAST(dst32, x) ? COLOR_GRAYSCALE_BINARY_MAX : COLOR_GRAYSCALE_BINARY_MIN); pixel = (__SMUAD(smuad_alpha, smuad_pixel) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = ((smuad_alpha * COLOR_RGB565_TO_Y(pixel)) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } } else { const uint16_t *color_palette = data->color_palette; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = color_palette[COLOR_RGB565_TO_Y(pixel)]; long smuad_pixel = (COLOR_RGB565_TO_Y(pixel) << 16) | (IMAGE_GET_BINARY_PIXEL_FAST(dst32, x) ? COLOR_GRAYSCALE_BINARY_MAX : COLOR_GRAYSCALE_BINARY_MIN); pixel = (__SMUAD(smuad_alpha, smuad_pixel) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = color_palette[COLOR_RGB565_TO_Y(pixel)]; pixel = ((smuad_alpha * COLOR_RGB565_TO_Y(pixel)) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } } } } else if (data->rgb_channel == 0) { if (data->smuad_alpha_palette) { const uint32_t *smuad_alpha_palette = data->smuad_alpha_palette; if (!data->color_palette) { if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = COLOR_RGB565_TO_R8(pixel); long smuad_alpha = smuad_alpha_palette[pixel]; long smuad_pixel = (pixel << 16) | (IMAGE_GET_BINARY_PIXEL_FAST(dst32, x) ? COLOR_GRAYSCALE_BINARY_MAX : COLOR_GRAYSCALE_BINARY_MIN); pixel = (__SMUAD(smuad_alpha, smuad_pixel) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = COLOR_RGB565_TO_R8(pixel); long smuad_alpha = smuad_alpha_palette[pixel]; pixel = ((smuad_alpha * pixel) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } } else { const uint16_t *color_palette = data->color_palette; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; int pixel_y = COLOR_RGB565_TO_R8(pixel); long smuad_alpha = smuad_alpha_palette[pixel_y]; pixel = color_palette[pixel_y]; long smuad_pixel = (COLOR_RGB565_TO_Y(pixel) << 16) | (IMAGE_GET_BINARY_PIXEL_FAST(dst32, x) ? COLOR_GRAYSCALE_BINARY_MAX : COLOR_GRAYSCALE_BINARY_MIN); pixel = (__SMUAD(smuad_alpha, smuad_pixel) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; int pixel_y = COLOR_RGB565_TO_R8(pixel); long smuad_alpha = smuad_alpha_palette[pixel_y]; pixel = color_palette[pixel_y]; pixel = ((smuad_alpha * COLOR_RGB565_TO_Y(pixel)) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } } } else if (data->alpha == 255) { if (!data->color_palette) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = COLOR_RGB565_TO_R8(pixel) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } else { const uint16_t *color_palette = data->color_palette; for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = color_palette[COLOR_RGB565_TO_R8(pixel)]; pixel = COLOR_RGB565_TO_Y(pixel) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } } else { long smuad_alpha = data->smuad_alpha; if (!data->color_palette) { if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; long smuad_pixel = (COLOR_RGB565_TO_R8(pixel) << 16) | (IMAGE_GET_BINARY_PIXEL_FAST(dst32, x) ? COLOR_GRAYSCALE_BINARY_MAX : COLOR_GRAYSCALE_BINARY_MIN); pixel = (__SMUAD(smuad_alpha, smuad_pixel) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = ((smuad_alpha * COLOR_RGB565_TO_R8(pixel)) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } } else { const uint16_t *color_palette = data->color_palette; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = color_palette[COLOR_RGB565_TO_R8(pixel)]; long smuad_pixel = (COLOR_RGB565_TO_Y(pixel) << 16) | (IMAGE_GET_BINARY_PIXEL_FAST(dst32, x) ? COLOR_GRAYSCALE_BINARY_MAX : COLOR_GRAYSCALE_BINARY_MIN); pixel = (__SMUAD(smuad_alpha, smuad_pixel) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = color_palette[COLOR_RGB565_TO_R8(pixel)]; pixel = ((smuad_alpha * COLOR_RGB565_TO_Y(pixel)) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } } } } else if (data->rgb_channel == 1) { if (data->smuad_alpha_palette) { const uint32_t *smuad_alpha_palette = data->smuad_alpha_palette; if (!data->color_palette) { if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = COLOR_RGB565_TO_G8(pixel); long smuad_alpha = smuad_alpha_palette[pixel]; long smuad_pixel = (pixel << 16) | (IMAGE_GET_BINARY_PIXEL_FAST(dst32, x) ? COLOR_GRAYSCALE_BINARY_MAX : COLOR_GRAYSCALE_BINARY_MIN); pixel = (__SMUAD(smuad_alpha, smuad_pixel) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = COLOR_RGB565_TO_G8(pixel); long smuad_alpha = smuad_alpha_palette[pixel]; pixel = ((smuad_alpha * pixel) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } } else { const uint16_t *color_palette = data->color_palette; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; int pixel_y = COLOR_RGB565_TO_G8(pixel); long smuad_alpha = smuad_alpha_palette[pixel_y]; pixel = color_palette[pixel_y]; long smuad_pixel = (COLOR_RGB565_TO_Y(pixel) << 16) | (IMAGE_GET_BINARY_PIXEL_FAST(dst32, x) ? COLOR_GRAYSCALE_BINARY_MAX : COLOR_GRAYSCALE_BINARY_MIN); pixel = (__SMUAD(smuad_alpha, smuad_pixel) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; int pixel_y = COLOR_RGB565_TO_G8(pixel); long smuad_alpha = smuad_alpha_palette[pixel_y]; pixel = color_palette[pixel_y]; pixel = ((smuad_alpha * COLOR_RGB565_TO_Y(pixel)) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } } } else if (data->alpha == 255) { if (!data->color_palette) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = COLOR_RGB565_TO_G8(pixel) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } else { const uint16_t *color_palette = data->color_palette; for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = color_palette[COLOR_RGB565_TO_G8(pixel)]; pixel = COLOR_RGB565_TO_Y(pixel) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } } else { long smuad_alpha = data->smuad_alpha; if (!data->color_palette) { if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; long smuad_pixel = (COLOR_RGB565_TO_G8(pixel) << 16) | (IMAGE_GET_BINARY_PIXEL_FAST(dst32, x) ? COLOR_GRAYSCALE_BINARY_MAX : COLOR_GRAYSCALE_BINARY_MIN); pixel = (__SMUAD(smuad_alpha, smuad_pixel) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = ((smuad_alpha * COLOR_RGB565_TO_G8(pixel)) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } } else { const uint16_t *color_palette = data->color_palette; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = color_palette[COLOR_RGB565_TO_G8(pixel)]; long smuad_pixel = (COLOR_RGB565_TO_Y(pixel) << 16) | (IMAGE_GET_BINARY_PIXEL_FAST(dst32, x) ? COLOR_GRAYSCALE_BINARY_MAX : COLOR_GRAYSCALE_BINARY_MIN); pixel = (__SMUAD(smuad_alpha, smuad_pixel) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = color_palette[COLOR_RGB565_TO_G8(pixel)]; pixel = ((smuad_alpha * COLOR_RGB565_TO_Y(pixel)) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } } } } else if (data->rgb_channel == 2) { if (data->smuad_alpha_palette) { const uint32_t *smuad_alpha_palette = data->smuad_alpha_palette; if (!data->color_palette) { if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = COLOR_RGB565_TO_B8(pixel); long smuad_alpha = smuad_alpha_palette[pixel]; long smuad_pixel = (pixel << 16) | (IMAGE_GET_BINARY_PIXEL_FAST(dst32, x) ? COLOR_GRAYSCALE_BINARY_MAX : COLOR_GRAYSCALE_BINARY_MIN); pixel = (__SMUAD(smuad_alpha, smuad_pixel) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = COLOR_RGB565_TO_B8(pixel); long smuad_alpha = smuad_alpha_palette[pixel]; pixel = ((smuad_alpha * pixel) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } } else { const uint16_t *color_palette = data->color_palette; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; int pixel_y = COLOR_RGB565_TO_B8(pixel); long smuad_alpha = smuad_alpha_palette[pixel_y]; pixel = color_palette[pixel_y]; long smuad_pixel = (COLOR_RGB565_TO_Y(pixel) << 16) | (IMAGE_GET_BINARY_PIXEL_FAST(dst32, x) ? COLOR_GRAYSCALE_BINARY_MAX : COLOR_GRAYSCALE_BINARY_MIN); pixel = (__SMUAD(smuad_alpha, smuad_pixel) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; int pixel_y = COLOR_RGB565_TO_B8(pixel); long smuad_alpha = smuad_alpha_palette[pixel_y]; pixel = color_palette[pixel_y]; pixel = ((smuad_alpha * COLOR_RGB565_TO_Y(pixel)) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } } } else if (data->alpha == 255) { if (!data->color_palette) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = COLOR_RGB565_TO_B8(pixel) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } else { const uint16_t *color_palette = data->color_palette; for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = color_palette[COLOR_RGB565_TO_B8(pixel)]; pixel = COLOR_RGB565_TO_Y(pixel) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } } else { long smuad_alpha = data->smuad_alpha; if (!data->color_palette) { if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; long smuad_pixel = (COLOR_RGB565_TO_B8(pixel) << 16) | (IMAGE_GET_BINARY_PIXEL_FAST(dst32, x) ? COLOR_GRAYSCALE_BINARY_MAX : COLOR_GRAYSCALE_BINARY_MIN); pixel = (__SMUAD(smuad_alpha, smuad_pixel) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = ((smuad_alpha * COLOR_RGB565_TO_B8(pixel)) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } } else { const uint16_t *color_palette = data->color_palette; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = color_palette[COLOR_RGB565_TO_B8(pixel)]; long smuad_pixel = (COLOR_RGB565_TO_Y(pixel) << 16) | (IMAGE_GET_BINARY_PIXEL_FAST(dst32, x) ? COLOR_GRAYSCALE_BINARY_MAX : COLOR_GRAYSCALE_BINARY_MIN); pixel = (__SMUAD(smuad_alpha, smuad_pixel) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = color_palette[COLOR_RGB565_TO_B8(pixel)]; pixel = ((smuad_alpha * COLOR_RGB565_TO_Y(pixel)) >> 8) > 127; IMAGE_PUT_BINARY_PIXEL_FAST(dst32, x, pixel); } } } } } break; } default: { break; } } break; } case PIXFORMAT_GRAYSCALE: { uint8_t *dst8 = (data->dst_row_override ? ((uint8_t *) data->dst_row_override) : IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(data-> dst_img, y_row)) + x_start; switch (data->src_img_pixfmt) { case PIXFORMAT_BINARY: { uint32_t *src32 = (uint32_t *) data->row_buffer; if (data->smuad_alpha_palette) { const uint32_t *smuad_alpha_palette = data->smuad_alpha_palette; if (!data->color_palette) { uint32_t alpha_pal0 = smuad_alpha_palette[0], alpha_pal255 = smuad_alpha_palette[255]; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = IMAGE_GET_BINARY_PIXEL_FAST(src32, x); long smuad_alpha = pixel ? alpha_pal255 : alpha_pal0; long smuad_pixel = (pixel ? COLOR_GRAYSCALE_BINARY_MAX_LSL16 : COLOR_GRAYSCALE_BINARY_MIN_LSL16) | *dst8; *dst8++ = __SMUAD(smuad_alpha, smuad_pixel) >> 8; } } else { for (int x = x_start; x < x_end; x++) { int pixel = IMAGE_GET_BINARY_PIXEL_FAST(src32, x); long smuad_alpha = pixel ? alpha_pal255 : alpha_pal0; long smuad_pixel = pixel ? COLOR_GRAYSCALE_BINARY_MAX : COLOR_GRAYSCALE_BINARY_MIN; *dst8++ = (smuad_alpha * smuad_pixel) >> 8; } } } else { const uint16_t *color_palette = data->color_palette; uint32_t alpha_pal0 = smuad_alpha_palette[0], alpha_pal255 = smuad_alpha_palette[255]; uint32_t pal0 = color_palette[0], pal255 = color_palette[255]; pal0 = COLOR_RGB565_TO_Y(pal0) << 16; pal255 = COLOR_RGB565_TO_Y(pal255) << 16; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = IMAGE_GET_BINARY_PIXEL_FAST(src32, x); long smuad_alpha = pixel ? alpha_pal255 : alpha_pal0; long smuad_pixel = (pixel ? pal255 : pal0) | *dst8; *dst8++ = __SMUAD(smuad_alpha, smuad_pixel) >> 8; } } else { for (int x = x_start; x < x_end; x++) { int pixel = IMAGE_GET_BINARY_PIXEL_FAST(src32, x); long smuad_alpha = pixel ? alpha_pal255 : alpha_pal0; long smuad_pixel = pixel ? pal255 : pal0; *dst8++ = (smuad_alpha * smuad_pixel) >> 24; } } } } else if (data->alpha == 255) { if (!data->color_palette) { for (int x = x_start; x < x_end; x++) { *dst8++ = IMAGE_GET_BINARY_PIXEL_FAST(src32, x) ? COLOR_GRAYSCALE_BINARY_MAX : COLOR_GRAYSCALE_BINARY_MIN; } } else { const uint16_t *color_palette = data->color_palette; uint16_t pal0 = color_palette[0], pal255 = color_palette[255]; pal0 = COLOR_RGB565_TO_Y(pal0); pal255 = COLOR_RGB565_TO_Y(pal255); for (int x = x_start; x < x_end; x++) { *dst8++ = IMAGE_GET_BINARY_PIXEL_FAST(src32, x) ? pal255 : pal0; } } } else { long smuad_alpha = data->smuad_alpha; if (!data->color_palette) { if (!data->black_background) { for (int x = x_start; x < x_end; x++) { long smuad_pixel = (IMAGE_GET_BINARY_PIXEL_FAST(src32, x) ? COLOR_GRAYSCALE_BINARY_MAX_LSL16 : COLOR_GRAYSCALE_BINARY_MIN_LSL16) | *dst8; *dst8++ = __SMUAD(smuad_alpha, smuad_pixel) >> 8; } } else { for (int x = x_start; x < x_end; x++) { long smuad_pixel = IMAGE_GET_BINARY_PIXEL_FAST(src32, x) ? COLOR_GRAYSCALE_BINARY_MAX : COLOR_GRAYSCALE_BINARY_MIN; *dst8++ = (smuad_alpha * smuad_pixel) >> 8; } } } else { const uint16_t *color_palette = data->color_palette; uint32_t pal0 = color_palette[0], pal255 = color_palette[255]; pal0 = COLOR_RGB565_TO_Y(pal0) << 16; pal255 = COLOR_RGB565_TO_Y(pal255) << 16; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { long smuad_pixel = (IMAGE_GET_BINARY_PIXEL_FAST(src32, x) ? pal255 : pal0) | *dst8; *dst8++ = __SMUAD(smuad_alpha, smuad_pixel) >> 8; } } else { for (int x = x_start; x < x_end; x++) { long smuad_pixel = IMAGE_GET_BINARY_PIXEL_FAST(src32, x) ? pal255 : pal0; *dst8++ = (smuad_alpha * smuad_pixel) >> 24; } } } } break; } case PIXFORMAT_GRAYSCALE: { uint8_t *src8 = ((uint8_t *) data->row_buffer) + x_start; if (data->smuad_alpha_palette) { const uint32_t *smuad_alpha_palette = data->smuad_alpha_palette; if (!data->color_palette) { if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src8++; long smuad_alpha = smuad_alpha_palette[pixel]; long smuad_pixel = (pixel << 16) | *dst8; *dst8++ = __SMUAD(smuad_alpha, smuad_pixel) >> 8; } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src8++; long smuad_alpha = smuad_alpha_palette[pixel]; *dst8++ = (smuad_alpha * pixel) >> 8; } } } else { const uint16_t *color_palette = data->color_palette; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src8++; long smuad_alpha = smuad_alpha_palette[pixel]; pixel = color_palette[pixel]; long smuad_pixel = (COLOR_RGB565_TO_Y(pixel) << 16) | *dst8; *dst8++ = __SMUAD(smuad_alpha, smuad_pixel) >> 8; } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src8++; long smuad_alpha = smuad_alpha_palette[pixel]; pixel = color_palette[pixel]; *dst8++ = (smuad_alpha * COLOR_RGB565_TO_Y(pixel)) >> 8; } } } } else if (data->alpha == 255) { if (!data->color_palette) { unaligned_memcpy(dst8, src8, (x_end - x_start) * sizeof(uint8_t)); } else { const uint16_t *color_palette = data->color_palette; for (int x = x_start; x < x_end; x++) { int pixel = color_palette[*src8++]; *dst8++ = COLOR_RGB565_TO_Y(pixel); } } } else { long smuad_alpha = data->smuad_alpha; if (!data->color_palette) { if (!data->black_background) { for (int x = x_start; x < x_end; x++) { long smuad_pixel = (*src8++ << 16) | *dst8; *dst8++ = __SMUAD(smuad_alpha, smuad_pixel) >> 8; } } else { for (int x = x_start; x < x_end; x++) { *dst8++ = (smuad_alpha * (*src8++)) >> 8; } } } else { const uint16_t *color_palette = data->color_palette; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = color_palette[*src8++]; long smuad_pixel = (COLOR_RGB565_TO_Y(pixel) << 16) | *dst8; *dst8++ = __SMUAD(smuad_alpha, smuad_pixel) >> 8; } } else { for (int x = x_start; x < x_end; x++) { int pixel = color_palette[*src8++]; *dst8++ = (smuad_alpha * COLOR_RGB565_TO_Y(pixel)) >> 8; } } } } break; } case PIXFORMAT_RGB565: { uint16_t *src16 = ((uint16_t *) data->row_buffer) + x_start; if (data->rgb_channel < 0) { if (data->smuad_alpha_palette) { const uint32_t *smuad_alpha_palette = data->smuad_alpha_palette; if (!data->color_palette) { if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; int pixel_y = COLOR_RGB565_TO_Y(pixel); long smuad_alpha = smuad_alpha_palette[pixel_y]; long smuad_pixel = (pixel_y << 16) | *dst8; *dst8++ = __SMUAD(smuad_alpha, smuad_pixel) >> 8; } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; int pixel_y = COLOR_RGB565_TO_Y(pixel); long smuad_alpha = smuad_alpha_palette[pixel_y]; *dst8++ = (smuad_alpha * pixel_y) >> 8; } } } else { const uint16_t *color_palette = data->color_palette; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; int pixel_y = COLOR_RGB565_TO_Y(pixel); long smuad_alpha = smuad_alpha_palette[pixel_y]; pixel = color_palette[pixel_y]; long smuad_pixel = (COLOR_RGB565_TO_Y(pixel) << 16) | *dst8; *dst8++ = __SMUAD(smuad_alpha, smuad_pixel) >> 8; } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; int pixel_y = COLOR_RGB565_TO_Y(pixel); long smuad_alpha = smuad_alpha_palette[pixel_y]; pixel = color_palette[pixel_y]; *dst8++ = (smuad_alpha * COLOR_RGB565_TO_Y(pixel)) >> 8; } } } } else if (data->alpha == 255) { if (!data->color_palette) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; *dst8++ = COLOR_RGB565_TO_Y(pixel); } } else { const uint16_t *color_palette = data->color_palette; for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = color_palette[COLOR_RGB565_TO_Y(pixel)]; *dst8++ = COLOR_RGB565_TO_Y(pixel); } } } else { long smuad_alpha = data->smuad_alpha; if (!data->color_palette) { if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; long smuad_pixel = (COLOR_RGB565_TO_Y(pixel) << 16) | *dst8; *dst8++ = __SMUAD(smuad_alpha, smuad_pixel) >> 8; } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; *dst8++ = (smuad_alpha * COLOR_RGB565_TO_Y(pixel)) >> 8; } } } else { const uint16_t *color_palette = data->color_palette; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = color_palette[COLOR_RGB565_TO_Y(pixel)]; long smuad_pixel = (COLOR_RGB565_TO_Y(pixel) << 16) | *dst8; *dst8++ = __SMUAD(smuad_alpha, smuad_pixel) >> 8; } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = color_palette[COLOR_RGB565_TO_Y(pixel)]; *dst8++ = (smuad_alpha * COLOR_RGB565_TO_Y(pixel)) >> 8; } } } } } else if (data->rgb_channel == 0) { if (data->smuad_alpha_palette) { const uint32_t *smuad_alpha_palette = data->smuad_alpha_palette; if (!data->color_palette) { if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; int pixel_y = COLOR_RGB565_TO_R8(pixel); long smuad_alpha = smuad_alpha_palette[pixel_y]; long smuad_pixel = (pixel_y << 16) | *dst8; *dst8++ = __SMUAD(smuad_alpha, smuad_pixel) >> 8; } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; int pixel_y = COLOR_RGB565_TO_R8(pixel); long smuad_alpha = smuad_alpha_palette[pixel_y]; *dst8++ = (smuad_alpha * pixel_y) >> 8; } } } else { const uint16_t *color_palette = data->color_palette; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; int pixel_y = COLOR_RGB565_TO_R8(pixel); long smuad_alpha = smuad_alpha_palette[pixel_y]; pixel = color_palette[pixel_y]; long smuad_pixel = (COLOR_RGB565_TO_Y(pixel) << 16) | *dst8; *dst8++ = __SMUAD(smuad_alpha, smuad_pixel) >> 8; } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; int pixel_y = COLOR_RGB565_TO_R8(pixel); long smuad_alpha = smuad_alpha_palette[pixel_y]; pixel = color_palette[pixel_y]; *dst8++ = (smuad_alpha * COLOR_RGB565_TO_Y(pixel)) >> 8; } } } } else if (data->alpha == 255) { if (!data->color_palette) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; *dst8++ = COLOR_RGB565_TO_R8(pixel); } } else { const uint16_t *color_palette = data->color_palette; for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = color_palette[COLOR_RGB565_TO_R8(pixel)]; *dst8++ = COLOR_RGB565_TO_Y(pixel); } } } else { long smuad_alpha = data->smuad_alpha; if (!data->color_palette) { if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; long smuad_pixel = (COLOR_RGB565_TO_R8(pixel) << 16) | *dst8; *dst8++ = __SMUAD(smuad_alpha, smuad_pixel) >> 8; } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; *dst8++ = (smuad_alpha * COLOR_RGB565_TO_R8(pixel)) >> 8; } } } else { const uint16_t *color_palette = data->color_palette; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = color_palette[COLOR_RGB565_TO_R8(pixel)]; long smuad_pixel = (COLOR_RGB565_TO_Y(pixel) << 16) | *dst8; *dst8++ = __SMUAD(smuad_alpha, smuad_pixel) >> 8; } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = color_palette[COLOR_RGB565_TO_R8(pixel)]; *dst8++ = (smuad_alpha * COLOR_RGB565_TO_Y(pixel)) >> 8; } } } } } else if (data->rgb_channel == 1) { if (data->smuad_alpha_palette) { const uint32_t *smuad_alpha_palette = data->smuad_alpha_palette; if (!data->color_palette) { if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; int pixel_y = COLOR_RGB565_TO_G8(pixel); long smuad_alpha = smuad_alpha_palette[pixel_y]; long smuad_pixel = (pixel_y << 16) | *dst8; *dst8++ = __SMUAD(smuad_alpha, smuad_pixel) >> 8; } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; int pixel_y = COLOR_RGB565_TO_G8(pixel); long smuad_alpha = smuad_alpha_palette[pixel_y]; *dst8++ = (smuad_alpha * pixel_y) >> 8; } } } else { const uint16_t *color_palette = data->color_palette; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; int pixel_y = COLOR_RGB565_TO_G8(pixel); long smuad_alpha = smuad_alpha_palette[pixel_y]; pixel = color_palette[pixel_y]; long smuad_pixel = (COLOR_RGB565_TO_Y(pixel) << 16) | *dst8; *dst8++ = __SMUAD(smuad_alpha, smuad_pixel) >> 8; } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; int pixel_y = COLOR_RGB565_TO_G8(pixel); long smuad_alpha = smuad_alpha_palette[pixel_y]; pixel = color_palette[pixel_y]; *dst8++ = (smuad_alpha * COLOR_RGB565_TO_Y(pixel)) >> 8; } } } } else if (data->alpha == 255) { if (!data->color_palette) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; *dst8++ = COLOR_RGB565_TO_G8(pixel); } } else { const uint16_t *color_palette = data->color_palette; for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = color_palette[COLOR_RGB565_TO_G8(pixel)]; *dst8++ = COLOR_RGB565_TO_Y(pixel); } } } else { long smuad_alpha = data->smuad_alpha; if (!data->color_palette) { if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; long smuad_pixel = (COLOR_RGB565_TO_G8(pixel) << 16) | *dst8; *dst8++ = __SMUAD(smuad_alpha, smuad_pixel) >> 8; } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; *dst8++ = (smuad_alpha * COLOR_RGB565_TO_G8(pixel)) >> 8; } } } else { const uint16_t *color_palette = data->color_palette; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = color_palette[COLOR_RGB565_TO_G8(pixel)]; long smuad_pixel = (COLOR_RGB565_TO_Y(pixel) << 16) | *dst8; *dst8++ = __SMUAD(smuad_alpha, smuad_pixel) >> 8; } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = color_palette[COLOR_RGB565_TO_G8(pixel)]; *dst8++ = (smuad_alpha * COLOR_RGB565_TO_Y(pixel)) >> 8; } } } } } else if (data->rgb_channel == 2) { if (data->smuad_alpha_palette) { const uint32_t *smuad_alpha_palette = data->smuad_alpha_palette; if (!data->color_palette) { if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; int pixel_y = COLOR_RGB565_TO_B8(pixel); long smuad_alpha = smuad_alpha_palette[pixel_y]; long smuad_pixel = (pixel_y << 16) | *dst8; *dst8++ = __SMUAD(smuad_alpha, smuad_pixel) >> 8; } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; int pixel_y = COLOR_RGB565_TO_B8(pixel); long smuad_alpha = smuad_alpha_palette[pixel_y]; *dst8++ = (smuad_alpha * pixel_y) >> 8; } } } else { const uint16_t *color_palette = data->color_palette; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; int pixel_y = COLOR_RGB565_TO_B8(pixel); long smuad_alpha = smuad_alpha_palette[pixel_y]; pixel = color_palette[pixel_y]; long smuad_pixel = (COLOR_RGB565_TO_Y(pixel) << 16) | *dst8; *dst8++ = __SMUAD(smuad_alpha, smuad_pixel) >> 8; } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; int pixel_y = COLOR_RGB565_TO_B8(pixel); long smuad_alpha = smuad_alpha_palette[pixel_y]; pixel = color_palette[pixel_y]; *dst8++ = (smuad_alpha * COLOR_RGB565_TO_Y(pixel)) >> 8; } } } } else if (data->alpha == 255) { if (!data->color_palette) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; *dst8++ = COLOR_RGB565_TO_B8(pixel); } } else { const uint16_t *color_palette = data->color_palette; for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = color_palette[COLOR_RGB565_TO_B8(pixel)]; *dst8++ = COLOR_RGB565_TO_Y(pixel); } } } else { long smuad_alpha = data->smuad_alpha; if (!data->color_palette) { if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; long smuad_pixel = (COLOR_RGB565_TO_B8(pixel) << 16) | *dst8; *dst8++ = __SMUAD(smuad_alpha, smuad_pixel) >> 8; } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; *dst8++ = (smuad_alpha * COLOR_RGB565_TO_B8(pixel)) >> 8; } } } else { const uint16_t *color_palette = data->color_palette; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = color_palette[COLOR_RGB565_TO_B8(pixel)]; long smuad_pixel = (COLOR_RGB565_TO_Y(pixel) << 16) | *dst8; *dst8++ = __SMUAD(smuad_alpha, smuad_pixel) >> 8; } } else { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = color_palette[COLOR_RGB565_TO_B8(pixel)]; *dst8++ = (smuad_alpha * COLOR_RGB565_TO_Y(pixel)) >> 8; } } } } } break; } default: { break; } } break; } case PIXFORMAT_RGB565: { uint16_t *dst16 = (data->dst_row_override ? ((uint16_t *) data->dst_row_override) : IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(data-> dst_img, y_row)) + x_start; switch (data->src_img_pixfmt) { case PIXFORMAT_BINARY: { uint32_t *src32 = (uint32_t *) data->row_buffer; if (data->smuad_alpha_palette) { const uint32_t *smuad_alpha_palette = data->smuad_alpha_palette; if (!data->color_palette) { uint32_t alpha_pal0 = smuad_alpha_palette[0], alpha_pal255 = smuad_alpha_palette[255]; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = IMAGE_GET_BINARY_PIXEL_FAST(src32, x); long smuad_alpha = pixel ? alpha_pal255 : alpha_pal0; int src_pixel = pixel ? COLOR_RGB565_BINARY_MAX : COLOR_RGB565_BINARY_MIN; int dst_pixel = *dst16; *dst16++ = BLEND_RGB566(src_pixel, dst_pixel, smuad_alpha); } } else { for (int x = x_start; x < x_end; x++) { int pixel = IMAGE_GET_BINARY_PIXEL_FAST(src32, x); long smuad_alpha = pixel ? alpha_pal255 : alpha_pal0; int src_pixel = pixel ? COLOR_RGB565_BINARY_MAX : COLOR_RGB565_BINARY_MIN; *dst16++ = BLEND_RGB566_0(src_pixel, smuad_alpha); } } } else { const uint16_t *color_palette = data->color_palette; uint32_t alpha_pal0 = smuad_alpha_palette[0], alpha_pal255 = smuad_alpha_palette[255]; uint16_t pal0 = color_palette[0], pal255 = color_palette[255]; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int pixel = IMAGE_GET_BINARY_PIXEL_FAST(src32, x); long smuad_alpha = pixel ? alpha_pal255 : alpha_pal0; int src_pixel = pixel ? pal255 : pal0; int dst_pixel = *dst16; *dst16++ = BLEND_RGB566(src_pixel, dst_pixel, smuad_alpha); } } else { for (int x = x_start; x < x_end; x++) { int pixel = IMAGE_GET_BINARY_PIXEL_FAST(src32, x); long smuad_alpha = pixel ? alpha_pal255 : alpha_pal0; int src_pixel = pixel ? pal255 : pal0; *dst16++ = BLEND_RGB566_0(src_pixel, smuad_alpha); } } } } else if (data->alpha == 255) { if (!data->color_palette) { for (int x = x_start; x < x_end; x++) { *dst16++ = IMAGE_GET_BINARY_PIXEL_FAST(src32, x) ? COLOR_RGB565_BINARY_MAX : COLOR_RGB565_BINARY_MIN; } } else { const uint16_t *color_palette = data->color_palette; uint16_t pal0 = color_palette[0], pal255 = color_palette[255]; for (int x = x_start; x < x_end; x++) { *dst16++ = IMAGE_GET_BINARY_PIXEL_FAST(src32, x) ? pal255 : pal0; } } } else { long smuad_alpha = data->smuad_alpha; if (!data->color_palette) { if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int src_pixel = IMAGE_GET_BINARY_PIXEL_FAST(src32, x) ? COLOR_RGB565_BINARY_MAX : COLOR_RGB565_BINARY_MIN; int dst_pixel = *dst16; *dst16++ = BLEND_RGB566(src_pixel, dst_pixel, smuad_alpha); } } else { for (int x = x_start; x < x_end; x++) { int src_pixel = IMAGE_GET_BINARY_PIXEL_FAST(src32, x) ? COLOR_RGB565_BINARY_MAX : COLOR_RGB565_BINARY_MIN; *dst16++ = BLEND_RGB566_0(src_pixel, smuad_alpha); } } } else { const uint16_t *color_palette = data->color_palette; uint16_t pal0 = color_palette[0], pal255 = color_palette[255]; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int src_pixel = IMAGE_GET_BINARY_PIXEL_FAST(src32, x) ? pal255 : pal0; int dst_pixel = *dst16; *dst16++ = BLEND_RGB566(src_pixel, dst_pixel, smuad_alpha); } } else { for (int x = x_start; x < x_end; x++) { int src_pixel = IMAGE_GET_BINARY_PIXEL_FAST(src32, x) ? pal255 : pal0; *dst16++ = BLEND_RGB566_0(src_pixel, smuad_alpha); } } } } break; } case PIXFORMAT_GRAYSCALE: { uint8_t *src8 = ((uint8_t *) data->row_buffer) + x_start; if (data->smuad_alpha_palette) { const uint32_t *smuad_alpha_palette = data->smuad_alpha_palette; if (!data->color_palette) { if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int src_pixel = *src8++; long smuad_alpha = smuad_alpha_palette[src_pixel]; src_pixel = COLOR_Y_TO_RGB565(src_pixel); int dst_pixel = *dst16; *dst16++ = BLEND_RGB566(src_pixel, dst_pixel, smuad_alpha); } } else { for (int x = x_start; x < x_end; x++) { int src_pixel = *src8++; long smuad_alpha = smuad_alpha_palette[src_pixel]; src_pixel = COLOR_Y_TO_RGB565(src_pixel); *dst16++ = BLEND_RGB566_0(src_pixel, smuad_alpha); } } } else { const uint16_t *color_palette = data->color_palette; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int src_pixel = *src8++; long smuad_alpha = smuad_alpha_palette[src_pixel]; src_pixel = color_palette[src_pixel]; int dst_pixel = *dst16; *dst16++ = BLEND_RGB566(src_pixel, dst_pixel, smuad_alpha); } } else { for (int x = x_start; x < x_end; x++) { int src_pixel = *src8++; long smuad_alpha = smuad_alpha_palette[src_pixel]; src_pixel = color_palette[src_pixel]; *dst16++ = BLEND_RGB566_0(src_pixel, smuad_alpha); } } } } else if (data->alpha == 255) { if (!data->color_palette) { for (int x = x_start; x < x_end; x++) { int pixel = *src8++; *dst16++ = COLOR_Y_TO_RGB565(pixel); } } else { const uint16_t *color_palette = data->color_palette; for (int x = x_start; x < x_end; x++) { *dst16++ = color_palette[*src8++]; } } } else { long smuad_alpha = data->smuad_alpha; if (!data->color_palette) { if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int src_pixel = *src8++; src_pixel = COLOR_Y_TO_RGB565(src_pixel); int dst_pixel = *dst16; *dst16++ = BLEND_RGB566(src_pixel, dst_pixel, smuad_alpha); } } else { for (int x = x_start; x < x_end; x++) { int src_pixel = *src8++; src_pixel = COLOR_Y_TO_RGB565(src_pixel); *dst16++ = BLEND_RGB566_0(src_pixel, smuad_alpha); } } } else { const uint16_t *color_palette = data->color_palette; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int src_pixel = color_palette[*src8++]; int dst_pixel = *dst16; *dst16++ = BLEND_RGB566(src_pixel, dst_pixel, smuad_alpha); } } else { for (int x = x_start; x < x_end; x++) { int src_pixel = color_palette[*src8++]; *dst16++ = BLEND_RGB566_0(src_pixel, smuad_alpha); } } } } break; } case PIXFORMAT_RGB565: { uint16_t *src16 = ((uint16_t *) data->row_buffer) + x_start; if (data->rgb_channel < 0) { if (data->smuad_alpha_palette) { const uint32_t *smuad_alpha_palette = data->smuad_alpha_palette; if (!data->color_palette) { if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int src_pixel = *src16++; long smuad_alpha = smuad_alpha_palette[COLOR_RGB565_TO_Y(src_pixel)]; int dst_pixel = *dst16; *dst16++ = BLEND_RGB566(src_pixel, dst_pixel, smuad_alpha); } } else { for (int x = x_start; x < x_end; x++) { int src_pixel = *src16++; long smuad_alpha = smuad_alpha_palette[COLOR_RGB565_TO_Y(src_pixel)]; *dst16++ = BLEND_RGB566_0(src_pixel, smuad_alpha); } } } else { const uint16_t *color_palette = data->color_palette; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int src_pixel = *src16++; int src_pixel_y = COLOR_RGB565_TO_Y(src_pixel); long smuad_alpha = smuad_alpha_palette[src_pixel_y]; src_pixel = color_palette[src_pixel_y]; int dst_pixel = *dst16; *dst16++ = BLEND_RGB566(src_pixel, dst_pixel, smuad_alpha); } } else { for (int x = x_start; x < x_end; x++) { int src_pixel = *src16++; int src_pixel_y = COLOR_RGB565_TO_Y(src_pixel); long smuad_alpha = smuad_alpha_palette[src_pixel_y]; src_pixel = color_palette[src_pixel_y]; *dst16++ = BLEND_RGB566_0(src_pixel, smuad_alpha); } } } } else if (data->alpha == 255) { if (!data->color_palette) { unaligned_memcpy(dst16, src16, (x_end - x_start) * sizeof(uint16_t)); } else { const uint16_t *color_palette = data->color_palette; for (int x = x_start; x < x_end; x++) { int pixel = *src16++; *dst16++ = color_palette[COLOR_RGB565_TO_Y(pixel)]; } } } else { long smuad_alpha = data->smuad_alpha; if (!data->color_palette) { if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int src_pixel = *src16++; int dst_pixel = *dst16; *dst16++ = BLEND_RGB566(src_pixel, dst_pixel, smuad_alpha); } } else { for (int x = x_start; x < x_end; x++) { int src_pixel = *src16++; *dst16++ = BLEND_RGB566_0(src_pixel, smuad_alpha); } } } else { const uint16_t *color_palette = data->color_palette; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int src_pixel = *src16++; src_pixel = color_palette[COLOR_RGB565_TO_Y(src_pixel)]; int dst_pixel = *dst16; *dst16++ = BLEND_RGB566(src_pixel, dst_pixel, smuad_alpha); } } else { for (int x = x_start; x < x_end; x++) { int src_pixel = *src16++; src_pixel = color_palette[COLOR_RGB565_TO_Y(src_pixel)]; *dst16++ = BLEND_RGB566_0(src_pixel, smuad_alpha); } } } } } else if (data->rgb_channel == 0) { if (data->smuad_alpha_palette) { const uint32_t *smuad_alpha_palette = data->smuad_alpha_palette; if (!data->color_palette) { if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int src_pixel = *src16++; int src_pixel_y = COLOR_RGB565_TO_R8(src_pixel); long smuad_alpha = smuad_alpha_palette[src_pixel_y]; src_pixel = COLOR_Y_TO_RGB565(src_pixel_y); int dst_pixel = *dst16; *dst16++ = BLEND_RGB566(src_pixel, dst_pixel, smuad_alpha); } } else { for (int x = x_start; x < x_end; x++) { int src_pixel = *src16++; int src_pixel_y = COLOR_RGB565_TO_R8(src_pixel); long smuad_alpha = smuad_alpha_palette[src_pixel_y]; src_pixel = COLOR_Y_TO_RGB565(src_pixel_y); *dst16++ = BLEND_RGB566_0(src_pixel, smuad_alpha); } } } else { const uint16_t *color_palette = data->color_palette; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int src_pixel = *src16++; int src_pixel_y = COLOR_RGB565_TO_R8(src_pixel); long smuad_alpha = smuad_alpha_palette[src_pixel_y]; src_pixel = color_palette[src_pixel_y]; int dst_pixel = *dst16; *dst16++ = BLEND_RGB566(src_pixel, dst_pixel, smuad_alpha); } } else { for (int x = x_start; x < x_end; x++) { int src_pixel = *src16++; int src_pixel_y = COLOR_RGB565_TO_R8(src_pixel); long smuad_alpha = smuad_alpha_palette[src_pixel_y]; src_pixel = color_palette[src_pixel_y]; *dst16++ = BLEND_RGB566_0(src_pixel, smuad_alpha); } } } } else if (data->alpha == 255) { if (!data->color_palette) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = COLOR_RGB565_TO_R8(pixel); *dst16++ = COLOR_Y_TO_RGB565(pixel); } } else { const uint16_t *color_palette = data->color_palette; for (int x = x_start; x < x_end; x++) { int pixel = *src16++; *dst16++ = color_palette[COLOR_RGB565_TO_R8(pixel)]; } } } else { long smuad_alpha = data->smuad_alpha; if (!data->color_palette) { if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int src_pixel = *src16++; src_pixel = COLOR_RGB565_TO_R8(src_pixel); src_pixel = COLOR_Y_TO_RGB565(src_pixel); int dst_pixel = *dst16; *dst16++ = BLEND_RGB566(src_pixel, dst_pixel, smuad_alpha); } } else { for (int x = x_start; x < x_end; x++) { int src_pixel = *src16++; src_pixel = COLOR_RGB565_TO_R8(src_pixel); src_pixel = COLOR_Y_TO_RGB565(src_pixel); *dst16++ = BLEND_RGB566_0(src_pixel, smuad_alpha); } } } else { const uint16_t *color_palette = data->color_palette; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int src_pixel = *src16++; src_pixel = color_palette[COLOR_RGB565_TO_R8(src_pixel)]; int dst_pixel = *dst16; *dst16++ = BLEND_RGB566(src_pixel, dst_pixel, smuad_alpha); } } else { for (int x = x_start; x < x_end; x++) { int src_pixel = *src16++; src_pixel = color_palette[COLOR_RGB565_TO_R8(src_pixel)]; *dst16++ = BLEND_RGB566_0(src_pixel, smuad_alpha); } } } } } else if (data->rgb_channel == 1) { if (data->smuad_alpha_palette) { const uint32_t *smuad_alpha_palette = data->smuad_alpha_palette; if (!data->color_palette) { if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int src_pixel = *src16++; int src_pixel_y = COLOR_RGB565_TO_G8(src_pixel); long smuad_alpha = smuad_alpha_palette[src_pixel_y]; src_pixel = COLOR_Y_TO_RGB565(src_pixel_y); int dst_pixel = *dst16; *dst16++ = BLEND_RGB566(src_pixel, dst_pixel, smuad_alpha); } } else { for (int x = x_start; x < x_end; x++) { int src_pixel = *src16++; int src_pixel_y = COLOR_RGB565_TO_G8(src_pixel); long smuad_alpha = smuad_alpha_palette[src_pixel_y]; src_pixel = COLOR_Y_TO_RGB565(src_pixel_y); *dst16++ = BLEND_RGB566_0(src_pixel, smuad_alpha); } } } else { const uint16_t *color_palette = data->color_palette; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int src_pixel = *src16++; int src_pixel_y = COLOR_RGB565_TO_G8(src_pixel); long smuad_alpha = smuad_alpha_palette[src_pixel_y]; src_pixel = color_palette[src_pixel_y]; int dst_pixel = *dst16; *dst16++ = BLEND_RGB566(src_pixel, dst_pixel, smuad_alpha); } } else { for (int x = x_start; x < x_end; x++) { int src_pixel = *src16++; int src_pixel_y = COLOR_RGB565_TO_G8(src_pixel); long smuad_alpha = smuad_alpha_palette[src_pixel_y]; src_pixel = color_palette[src_pixel_y]; *dst16++ = BLEND_RGB566_0(src_pixel, smuad_alpha); } } } } else if (data->alpha == 255) { if (!data->color_palette) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = COLOR_RGB565_TO_G8(pixel); *dst16++ = COLOR_Y_TO_RGB565(pixel); } } else { const uint16_t *color_palette = data->color_palette; for (int x = x_start; x < x_end; x++) { int pixel = *src16++; *dst16++ = color_palette[COLOR_RGB565_TO_G8(pixel)]; } } } else { long smuad_alpha = data->smuad_alpha; if (!data->color_palette) { if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int src_pixel = *src16++; src_pixel = COLOR_RGB565_TO_G8(src_pixel); src_pixel = COLOR_Y_TO_RGB565(src_pixel); int dst_pixel = *dst16; *dst16++ = BLEND_RGB566(src_pixel, dst_pixel, smuad_alpha); } } else { for (int x = x_start; x < x_end; x++) { int src_pixel = *src16++; src_pixel = COLOR_RGB565_TO_G8(src_pixel); src_pixel = COLOR_Y_TO_RGB565(src_pixel); *dst16++ = BLEND_RGB566_0(src_pixel, smuad_alpha); } } } else { const uint16_t *color_palette = data->color_palette; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int src_pixel = *src16++; src_pixel = color_palette[COLOR_RGB565_TO_G8(src_pixel)]; int dst_pixel = *dst16; *dst16++ = BLEND_RGB566(src_pixel, dst_pixel, smuad_alpha); } } else { for (int x = x_start; x < x_end; x++) { int src_pixel = *src16++; src_pixel = color_palette[COLOR_RGB565_TO_G8(src_pixel)]; *dst16++ = BLEND_RGB566_0(src_pixel, smuad_alpha); } } } } } else if (data->rgb_channel == 2) { if (data->smuad_alpha_palette) { const uint32_t *smuad_alpha_palette = data->smuad_alpha_palette; if (!data->color_palette) { if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int src_pixel = *src16++; int src_pixel_y = COLOR_RGB565_TO_B8(src_pixel); long smuad_alpha = smuad_alpha_palette[src_pixel_y]; src_pixel = COLOR_Y_TO_RGB565(src_pixel_y); int dst_pixel = *dst16; *dst16++ = BLEND_RGB566(src_pixel, dst_pixel, smuad_alpha); } } else { for (int x = x_start; x < x_end; x++) { int src_pixel = *src16++; int src_pixel_y = COLOR_RGB565_TO_B8(src_pixel); long smuad_alpha = smuad_alpha_palette[src_pixel_y]; src_pixel = COLOR_Y_TO_RGB565(src_pixel_y); *dst16++ = BLEND_RGB566_0(src_pixel, smuad_alpha); } } } else { const uint16_t *color_palette = data->color_palette; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int src_pixel = *src16++; int src_pixel_y = COLOR_RGB565_TO_B8(src_pixel); long smuad_alpha = smuad_alpha_palette[src_pixel_y]; src_pixel = color_palette[src_pixel_y]; int dst_pixel = *dst16; *dst16++ = BLEND_RGB566(src_pixel, dst_pixel, smuad_alpha); } } else { for (int x = x_start; x < x_end; x++) { int src_pixel = *src16++; int src_pixel_y = COLOR_RGB565_TO_B8(src_pixel); long smuad_alpha = smuad_alpha_palette[src_pixel_y]; src_pixel = color_palette[src_pixel_y]; *dst16++ = BLEND_RGB566_0(src_pixel, smuad_alpha); } } } } else if (data->alpha == 255) { if (!data->color_palette) { for (int x = x_start; x < x_end; x++) { int pixel = *src16++; pixel = COLOR_RGB565_TO_B8(pixel); *dst16++ = COLOR_Y_TO_RGB565(pixel); } } else { const uint16_t *color_palette = data->color_palette; for (int x = x_start; x < x_end; x++) { int pixel = *src16++; *dst16++ = color_palette[COLOR_RGB565_TO_B8(pixel)]; } } } else { long smuad_alpha = data->smuad_alpha; if (!data->color_palette) { if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int src_pixel = *src16++; src_pixel = COLOR_RGB565_TO_B8(src_pixel); src_pixel = COLOR_Y_TO_RGB565(src_pixel); int dst_pixel = *dst16; *dst16++ = BLEND_RGB566(src_pixel, dst_pixel, smuad_alpha); } } else { for (int x = x_start; x < x_end; x++) { int src_pixel = *src16++; src_pixel = COLOR_RGB565_TO_B8(src_pixel); src_pixel = COLOR_Y_TO_RGB565(src_pixel); *dst16++ = BLEND_RGB566_0(src_pixel, smuad_alpha); } } } else { const uint16_t *color_palette = data->color_palette; if (!data->black_background) { for (int x = x_start; x < x_end; x++) { int src_pixel = *src16++; src_pixel = color_palette[COLOR_RGB565_TO_B8(src_pixel)]; int dst_pixel = *dst16; *dst16++ = BLEND_RGB566(src_pixel, dst_pixel, smuad_alpha); } } else { for (int x = x_start; x < x_end; x++) { int src_pixel = *src16++; src_pixel = color_palette[COLOR_RGB565_TO_B8(src_pixel)]; *dst16++ = BLEND_RGB566_0(src_pixel, smuad_alpha); } } } } } break; } default: { break; } } break; } // Only bayer copying/cropping is supported. case PIXFORMAT_BAYER_ANY: { uint8_t *dst8 = (data->dst_row_override ? ((uint8_t *) data->dst_row_override) : IMAGE_COMPUTE_BAYER_PIXEL_ROW_PTR(data->dst_img, y_row)) + x_start; uint8_t *src8 = ((uint8_t *) data->row_buffer) + x_start; unaligned_memcpy(dst8, src8, (x_end - x_start) * sizeof(uint8_t)); break; } // Only yuv422 copying/cropping is supported. case PIXFORMAT_YUV_ANY: { uint16_t *dst16 = (data->dst_row_override ? ((uint16_t *) data->dst_row_override) : IMAGE_COMPUTE_YUV_PIXEL_ROW_PTR(data->dst_img, y_row)) + x_start; uint16_t *src16 = ((uint16_t *) data->row_buffer) + x_start; unaligned_memcpy(dst16, src16, (x_end - x_start) * sizeof(uint16_t)); break; } default: { break; } } if (data->callback) { ((imlib_draw_row_callback_t) data->callback) (x_start, x_end, y_row, data); } #undef COLOR_GRAYSCALE_BINARY_MIN_LSL16 #undef COLOR_GRAYSCALE_BINARY_MAX_LSL16 #undef BLEND_RGB566_0 #undef BLEND_RGB566 } static void imlib_draw_image_scale_and_center_helper(image_t *dst_img, int src_img_w, int src_img_h, int *src_width_scaled, int *src_height_scaled, int *dst_x_start, int *dst_y_start, float *x_scale, float *y_scale, image_hint_t *hint) { if (*hint & (IMAGE_HINT_SCALE_ASPECT_KEEP | IMAGE_HINT_SCALE_ASPECT_EXPAND | IMAGE_HINT_SCALE_ASPECT_IGNORE)) { float xs = ((*hint & IMAGE_HINT_TRANSPOSE) ? dst_img->h : dst_img->w) / ((float) src_img_w); float ys = ((*hint & IMAGE_HINT_TRANSPOSE) ? dst_img->w : dst_img->h) / ((float) src_img_h); if (*hint & IMAGE_HINT_SCALE_ASPECT_IGNORE) { *x_scale *= xs; *y_scale *= ys; } else { float scale = (*hint & IMAGE_HINT_SCALE_ASPECT_KEEP) ? IM_MIN(xs, ys) : IM_MAX(xs, ys); *x_scale *= scale; *y_scale *= scale; } *hint &= ~(IMAGE_HINT_SCALE_ASPECT_KEEP | IMAGE_HINT_SCALE_ASPECT_EXPAND | IMAGE_HINT_SCALE_ASPECT_IGNORE); } *src_width_scaled = fast_floorf(fast_fabsf(*x_scale) * src_img_w); *src_height_scaled = fast_floorf(fast_fabsf(*y_scale) * src_img_h); if (*hint & IMAGE_HINT_TRANSPOSE) { int temp = *src_width_scaled; *src_width_scaled = *src_height_scaled; *src_height_scaled = temp; } if (*hint & IMAGE_HINT_CENTER) { *dst_x_start += fast_floorf((dst_img->w - *src_width_scaled) / 2.f); *dst_y_start += fast_floorf((dst_img->h - *src_height_scaled) / 2.f); *hint &= ~IMAGE_HINT_CENTER; } } // False == Image is black, True == rect valid void imlib_draw_image_get_bounds(image_t *dst_img, image_t *src_img, int dst_x_start, int dst_y_start, float x_scale, float y_scale, rectangle_t *roi, int alpha, const uint8_t *alpha_palette, image_hint_t hint, point_t *p0, point_t *p1) { p0->x = -1; int src_img_w = roi ? roi->w : src_img->w; int src_img_h = roi ? roi->h : src_img->h; int src_width_scaled, src_height_scaled; imlib_draw_image_scale_and_center_helper(dst_img, src_img_w, src_img_h, &src_width_scaled, &src_height_scaled, &dst_x_start, &dst_y_start, &x_scale, &y_scale, &hint); if (!alpha) { return; } if (alpha_palette) { int i = 0; while ((i < 256) && (!alpha_palette[i])) { i++; } if (i == 256) { // zero alpha palette return; } } // Clamp start x to image bounds. int src_x_start = 0; if (dst_x_start < 0) { src_x_start -= dst_x_start; // this is an add because dst_x_start is negative dst_x_start = 0; } if (dst_x_start >= dst_img->w) { return; } int src_x_dst_width = src_width_scaled - src_x_start; if (src_x_dst_width <= 0) { return; } // Clamp start y to image bounds. int src_y_start = 0; if (dst_y_start < 0) { src_y_start -= dst_y_start; // this is an add because dst_y_start is negative dst_y_start = 0; } if (dst_y_start >= dst_img->h) { return; } int src_y_dst_height = src_height_scaled - src_y_start; if (src_y_dst_height <= 0) { return; } // Clamp end x to image bounds. int dst_x_end = dst_x_start + src_x_dst_width; if (dst_x_end > dst_img->w) { dst_x_end = dst_img->w; } // Clamp end y to image bounds. int dst_y_end = dst_y_start + src_y_dst_height; if (dst_y_end > dst_img->h) { dst_y_end = dst_img->h; } p0->x = dst_x_start; p1->x = dst_x_end; p0->y = dst_y_start; p1->y = dst_y_end; return; } void imlib_draw_image(image_t *dst_img, image_t *src_img, int dst_x_start, int dst_y_start, float x_scale, float y_scale, rectangle_t *roi, int rgb_channel, int alpha, const uint16_t *color_palette, const uint8_t *alpha_palette, image_hint_t hint, imlib_draw_row_callback_t callback, void *callback_arg, void *dst_row_override) { OMV_PROFILE_START(); int dst_delta_x = 1; // positive direction if (x_scale < 0.f) { // flip X dst_delta_x = -1; x_scale = -x_scale; } if (hint & IMAGE_HINT_HMIRROR) { dst_delta_x = -dst_delta_x; } int dst_delta_y = 1; // positive direction if (y_scale < 0.f) { // flip Y dst_delta_y = -1; y_scale = -y_scale; } if (hint & IMAGE_HINT_VFLIP) { dst_delta_y = -dst_delta_y; } int src_img_w = roi ? roi->w : src_img->w; int w_start = roi ? roi->x : 0, w_start_p_1 = w_start + 1, w_start_p_2 = w_start_p_1 + 1; int w_limit = w_start + src_img_w - 1; int w_limit_m_1 = w_limit - 1; int src_img_h = roi ? roi->h : src_img->h; int h_start = roi ? roi->y : 0, h_start_p_1 = h_start + 1, h_start_p_2 = h_start_p_1 + 1; int h_limit = h_start + src_img_h - 1; int h_limit_m_1 = h_limit - 1; int src_width_scaled, src_height_scaled; imlib_draw_image_scale_and_center_helper(dst_img, src_img_w, src_img_h, &src_width_scaled, &src_height_scaled, &dst_x_start, &dst_y_start, &x_scale, &y_scale, &hint); // Nothing to draw if ((src_width_scaled < 1) || (src_height_scaled < 1)) { return; } // If alpha is 0 then nothing changes. if (alpha == 0) { return; } if (alpha_palette) { int i = 0; while ((i < 256) && (!alpha_palette[i])) { i++; } if (i == 256) { return; // zero alpha palette } } int dst_x_start_backup = dst_x_start; int dst_y_start_backup = dst_y_start; // Clamp start x to image bounds. int src_x_start = 0; if (dst_x_start < 0) { src_x_start -= dst_x_start; // this is an add because dst_x_start is negative dst_x_start = 0; } if (dst_x_start >= dst_img->w) { return; } int src_x_dst_width = src_width_scaled - src_x_start; if (src_x_dst_width <= 0) { return; } // Clamp start y to image bounds. int src_y_start = 0; if (dst_y_start < 0) { src_y_start -= dst_y_start; // this is an add because dst_y_start is negative dst_y_start = 0; } if (dst_y_start >= dst_img->h) { return; } int src_y_dst_height = src_height_scaled - src_y_start; if (src_y_dst_height <= 0) { return; } // Clamp end x to image bounds. int dst_x_end = dst_x_start + src_x_dst_width; if (dst_x_end > dst_img->w) { dst_x_end = dst_img->w; } // Clamp end y to image bounds. int dst_y_end = dst_y_start + src_y_dst_height; if (dst_y_end > dst_img->h) { dst_y_end = dst_img->h; } if (dst_delta_x < 0) { // Since we are drawing backwards we have to slide our drawing offset forward by an amount // limited by the size of the drawing area left. E.g. when we hit the right edge we have // advance the offset to prevent the image from sliding. int allowed_offset_width = src_width_scaled - (dst_x_end - dst_x_start); src_x_start = IM_MIN(dst_x_start, allowed_offset_width); } // Apply roi offset if (roi) { src_x_start += fast_floorf(roi->x * x_scale); } if (dst_delta_y < 0) { // Since we are drawing backwards we have to slide our drawing offset forward by an amount // limited by the size of the drawing area left. E.g. when we hit the bottom edge we have // advance the offset to prevent the image from sliding. int allowed_offset_height = src_height_scaled - (dst_y_end - dst_y_start); src_y_start = IM_MIN(dst_y_start, allowed_offset_height); } // Apply roi offset if (roi) { src_y_start += fast_floorf(roi->y * y_scale); } // For all of the scaling algorithms (nearest neighbor, bilinear, bicubic, and area) // we use a 32-bit fraction instead of a floating point value for iteration. Below, // we calculate an increment which fits in 32-bits. We can then add this value // successively as we loop over the destination pixels and then shift this sum // right by 16 to get the corresponding source pixel. If we want the fractional // position we just have to look at the bottom 16-bits. // // top 16-bits = whole part, bottom 16-bits = fractional part. int dst_x_reset = (dst_delta_x < 0) ? (dst_x_end - 1) : dst_x_start; long src_x_frac = fast_floorf(65536.0f / x_scale); long src_x_frac_size = (src_x_frac + 0xFFFF) >> 16; long src_x_accum_reset = fast_floorf((src_x_start << 16) / x_scale); int dst_y_reset = (dst_delta_y < 0) ? (dst_y_end - 1) : dst_y_start; long src_y_frac = fast_floorf(65536.0f / y_scale); long src_y_frac_size = (src_y_frac + 0xFFFF) >> 16; long src_y_accum_reset = fast_floorf((src_y_start << 16) / y_scale); // Nearest Neighbor if ((src_x_frac == 65536) && (src_y_frac == 65536)) { hint &= ~(IMAGE_HINT_AREA | IMAGE_HINT_BICUBIC | IMAGE_HINT_BILINEAR); } // Nearest Neighbor if ((hint & IMAGE_HINT_AREA) && (x_scale >= 1.f) && (y_scale >= 1.f)) { hint &= ~(IMAGE_HINT_AREA | IMAGE_HINT_BICUBIC | IMAGE_HINT_BILINEAR); } // Cannot interpolate. if ((src_img_w <= 3) || (src_img_h <= 3)) { if (hint & IMAGE_HINT_BICUBIC) { hint |= IMAGE_HINT_BILINEAR; } hint &= ~IMAGE_HINT_BICUBIC; } // Cannot interpolate. if ((src_img_w <= 1) || (src_img_h <= 1)) { hint &= ~(IMAGE_HINT_AREA | IMAGE_HINT_BILINEAR); } // rgb_channel extracted / color_palette applied image image_t new_src_img; if (((hint & IMAGE_HINT_EXTRACT_RGB_CHANNEL_FIRST) && (rgb_channel != -1) && src_img->is_color) || ((hint & IMAGE_HINT_APPLY_COLOR_PALETTE_FIRST) && color_palette)) { 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.pixfmt = color_palette ? PIXFORMAT_RGB565 : PIXFORMAT_GRAYSCALE; new_src_img.data = fb_alloc(image_size(&new_src_img), FB_ALLOC_CACHE_ALIGN); imlib_draw_image(&new_src_img, src_img, 0, 0, 1.f, 1.f, NULL, rgb_channel, 255, color_palette, NULL, 0, NULL, NULL, NULL); src_img = &new_src_img; rgb_channel = -1; color_palette = NULL; } // Best format to convert yuv/bayer/jpeg image to. int new_not_mutable_pixfmt = (rgb_channel != -1) ? PIXFORMAT_RGB565 : (color_palette ? PIXFORMAT_GRAYSCALE : dst_img->pixfmt); bool no_scaling_nearest_neighbor = (dst_delta_x == 1) && (dst_delta_y == 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; // Do we need to convert the image? bool is_bayer_conversion = src_img->is_bayer && !dst_img->is_bayer; bool is_yuv_conversion = src_img->is_yuv && !dst_img->is_yuv; bool is_bayer_yuv_conversion = is_bayer_conversion || is_yuv_conversion; // Is the line length growing which will prevent us from working in-place? bool is_upscaling = src_img_row_bytes < dst_img_row_bytes; #if (OMV_GPU_ENABLE == 1) if (!callback && !is_bayer_yuv_conversion && !src_img->is_compressed && (rgb_channel < 0) && ((dst_img->data != src_img->data) || (!is_upscaling)) && !(hint & (IMAGE_HINT_AREA | IMAGE_HINT_BICUBIC | IMAGE_HINT_TRANSPOSE))) { rectangle_t dst_rect = { .x = dst_x_start, .y = dst_y_start, .w = dst_x_end - dst_rect.x, .h = dst_y_end - dst_rect.y, }; rectangle_t src_rect = { .x = fast_roundf(src_x_start / x_scale), .y = fast_roundf(src_y_start / y_scale), .w = fast_floorf(dst_rect.w / x_scale), .h = fast_floorf(dst_rect.h / y_scale), }; image_hint_t gpu_hints = ((dst_delta_x < 0) ? IMAGE_HINT_HMIRROR : 0) | ((dst_delta_y < 0) ? IMAGE_HINT_VFLIP : 0) | (hint & (IMAGE_HINT_BILINEAR | IMAGE_HINT_BLACK_BACKGROUND)); if (!omv_gpu_draw_image(src_img, &src_rect, dst_img, &dst_rect, alpha, color_palette, alpha_palette, gpu_hints, NULL)) { goto exit_cleanup; } } #endif // Make a deep copy of the source image. if (((dst_img->data == src_img->data) && (is_scaling || is_upscaling || is_bayer_yuv_conversion)) || (is_bayer_yuv_conversion && is_scaling) || src_img->is_compressed) { new_src_img.w = src_img->w; // same width as source image new_src_img.h = src_img->h; // same height as source image if (!src_img->is_mutable) { new_src_img.pixfmt = new_not_mutable_pixfmt; size_t size = image_size(&new_src_img); new_src_img.data = fb_alloc(size, FB_ALLOC_CACHE_ALIGN); switch (new_src_img.pixfmt) { case PIXFORMAT_BINARY: case PIXFORMAT_GRAYSCALE: case PIXFORMAT_RGB565: { if (src_img->is_bayer) { imlib_debayer_image(&new_src_img, src_img); } else if (src_img->is_yuv) { imlib_deyuv_image(&new_src_img, src_img); } else if (src_img->pixfmt == PIXFORMAT_JPEG) { jpeg_decompress(&new_src_img, src_img); } else if (src_img->pixfmt == PIXFORMAT_PNG) { png_decompress(&new_src_img, src_img); } break; } case PIXFORMAT_BAYER_ANY: case PIXFORMAT_YUV_ANY: { memcpy(new_src_img.data, src_img->data, size); break; } default: { if (src_img->pixfmt == PIXFORMAT_PNG) { png_decompress(&new_src_img, src_img); } break; } } } else { new_src_img.pixfmt = src_img->pixfmt; size_t size = image_size(&new_src_img); new_src_img.data = fb_alloc(size, FB_ALLOC_CACHE_ALIGN); memcpy(new_src_img.data, src_img->data, size); } src_img = &new_src_img; } // To improve transpose performance we will split the operation up into chunks that fit in // onchip RAM. These chunks will then be copied to the target buffer in an efficent manner. // However, this doesn't work when the image is being scaled. So, we have to scale the image // first if that is requested. if (hint & IMAGE_HINT_TRANSPOSE) { rectangle_t t_roi = {}; image_t t_src_img; t_src_img.pixfmt = src_img->pixfmt; // Are we scaling? if ((src_x_frac != 65536) || (src_y_frac != 65536)) { t_src_img.w = t_roi.w = src_height_scaled; // was transposed t_src_img.h = t_roi.h = src_width_scaled; // was transposed t_src_img.data = fb_alloc(image_size(&t_src_img), FB_ALLOC_CACHE_ALIGN); imlib_draw_image(&t_src_img, src_img, 0, 0, x_scale, y_scale, roi, -1, 255, NULL, NULL, hint & (IMAGE_HINT_AREA | IMAGE_HINT_BILINEAR | IMAGE_HINT_BICUBIC), NULL, NULL, NULL); } else { memcpy(&t_roi, roi, sizeof(rectangle_t)); t_src_img.w = src_img->w; t_src_img.h = src_img->h; t_src_img.data = src_img->data; } // Allocate a buffer to hold chunks of the transposed image. size_t size = fb_avail(); size = (size & ~(OMV_ALLOC_ALIGNMENT - 1)) - OMV_ALLOC_ALIGNMENT; size = IM_MIN(size, image_size(&t_src_img)); void *data = fb_alloc(size, FB_ALLOC_PREFER_SPEED | FB_ALLOC_CACHE_ALIGN); // line_num stores how many lines we can do at a time with on-chip RAM. image_t temp = {.w = t_roi.w, .h = t_roi.h, .pixfmt = t_src_img.pixfmt}; int line_num = size / image_line_size(&temp); // Work top to bottom transposing as many lines at a time in a chunk of the image. for (int i = t_roi.y; i < t_roi.h; i += line_num) { line_num = IM_MIN(line_num, (t_roi.h - i)); // Make an image that is a slice of the input image. image_t in = {.w = t_src_img.w, .h = line_num, .pixfmt = t_src_img.pixfmt}; in.data = t_src_img.data + (image_line_size(&t_src_img) * ((dst_delta_y < 0) ? (t_roi.h - i - 1) : i)); // Make an image that will hold the transposed output. image_t out = in; out.w = line_num; out.h = t_roi.w; out.data = data; switch (t_src_img.pixfmt) { case PIXFORMAT_BINARY: { for (int y = 0; y < in.h; y++) { int y_2 = (dst_delta_y < 0) ? -y : y; uint32_t *row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR((&in), y_2); if (dst_delta_x < 0) { for (int x = 0; x < t_roi.w; x++) { int pixel = IMAGE_GET_BINARY_PIXEL_FAST(row_ptr, (t_roi.x + (t_roi.w - x - 1))); IMAGE_PUT_BINARY_PIXEL((&out), y, x, pixel); } } else { for (int x = 0; x < t_roi.w; x++) { int pixel = IMAGE_GET_BINARY_PIXEL_FAST(row_ptr, (t_roi.x + x)); IMAGE_PUT_BINARY_PIXEL((&out), y, x, pixel); } } } break; } case PIXFORMAT_GRAYSCALE: { for (int y = 0; y < in.h; y++) { int y_2 = (dst_delta_y < 0) ? -y : y; uint8_t *i_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR((&in), y_2) + t_roi.x; uint8_t *o_row_ptr = ((uint8_t *) out.data) + y; if (dst_delta_x < 0) { for (int x = t_roi.w - 1; x >= 0; x--, o_row_ptr += line_num) { *o_row_ptr = i_row_ptr[x]; } } else { for (int x = 0; x < t_roi.w; x++, o_row_ptr += line_num) { *o_row_ptr = i_row_ptr[x]; } } } break; } case PIXFORMAT_RGB565: { for (int y = 0; y < in.h; y++) { int y_2 = (dst_delta_y < 0) ? -y : y; uint16_t *i_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR((&in), y_2) + t_roi.x; uint16_t *o_row_ptr = ((uint16_t *) out.data) + y; if (dst_delta_x < 0) { for (int x = t_roi.w - 1; x >= 0; x--, o_row_ptr += line_num) { *o_row_ptr = i_row_ptr[x]; } } else { for (int x = 0; x < t_roi.w; x++, o_row_ptr += line_num) { *o_row_ptr = i_row_ptr[x]; } } } break; } default: { break; } } imlib_draw_image(dst_img, &out, dst_x_start_backup + i, dst_y_start_backup, 1.f, 1.f, NULL, rgb_channel, alpha, color_palette, alpha_palette, hint & IMAGE_HINT_BLACK_BACKGROUND, callback, callback_arg, dst_row_override); } fb_free(); // fb_alloc_all if (t_src_img.data != src_img->data) { fb_free(); } goto exit_cleanup; } // Bicbuic and bilinear both shift the image right by (0.5, 0.5) so we have to undo that. if (hint & (IMAGE_HINT_BICUBIC | IMAGE_HINT_BILINEAR)) { src_x_accum_reset -= 0x8000; src_y_accum_reset -= 0x8000; } imlib_draw_row_data_t imlib_draw_row_data; imlib_draw_row_data.dst_img = dst_img; imlib_draw_row_data.src_img_pixfmt = src_img->pixfmt; imlib_draw_row_data.rgb_channel = rgb_channel; imlib_draw_row_data.alpha = alpha; imlib_draw_row_data.color_palette = color_palette; imlib_draw_row_data.alpha_palette = alpha_palette; imlib_draw_row_data.black_background = hint & IMAGE_HINT_BLACK_BACKGROUND; imlib_draw_row_data.callback = callback; imlib_draw_row_data.callback_arg = callback_arg; imlib_draw_row_data.dst_row_override = dst_row_override; imlib_draw_row_setup(&imlib_draw_row_data); // Y loop iteration variables int dst_y = dst_y_reset; long src_y_accum = src_y_accum_reset; int next_src_y_index = src_y_accum >> 16; int y = dst_y_start; bool y_not_done = y < dst_y_end; if (hint & IMAGE_HINT_AREA) { // The area scaling algorithm runs in fast mode if the image is being scaled down by // 1, 2, 3, 4, 5, etc. or slow mode if it's a fractional scale. // // In fast mode area scaling is just the sum of the specified area. No weighting of pixels // is required to get the job done. // // In slow mode we need to weight pixels that lie on the edges of the area scale rectangle. // This prevents making the inner loop of the algorithm tight. // if ((!(src_x_frac & 0xFFFF)) && (!(src_y_frac & 0xFFFF))) { // fast switch (src_img->pixfmt) { case PIXFORMAT_BINARY: { while (y_not_done) { int src_y_index = next_src_y_index; int src_y_index_end = src_y_index + src_y_frac_size; if (src_y_index_end >= h_limit) { src_y_index_end = h_limit + 1; } int height = src_y_index_end - src_y_index; uint32_t *dst_row_ptr = (uint32_t *) imlib_draw_row_data.row_buffer; // 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 src_x_index = next_src_x_index; int src_x_index_end = src_x_index + src_x_frac_size; if (src_x_index_end >= w_limit) { src_x_index_end = w_limit + 1; } int width = src_x_index_end - src_x_index; uint32_t area = width * height; uint32_t acc = 0; for (int i = src_y_index; i < src_y_index_end; i++) { uint32_t *src_row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(src_img, i); for (int j = src_x_index; j < src_x_index_end; j++) { acc += IMAGE_GET_BINARY_PIXEL_FAST(src_row_ptr, j); } } int pixel = (acc + (area >> 1)) / area; IMAGE_PUT_BINARY_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 PIXFORMAT_GRAYSCALE: { while (y_not_done) { int src_y_index = next_src_y_index; int src_y_index_end = src_y_index + src_y_frac_size; if (src_y_index_end >= h_limit) { src_y_index_end = h_limit + 1; } int height = src_y_index_end - src_y_index; uint8_t *dst_row_ptr = (uint8_t *) imlib_draw_row_data.row_buffer; // 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 src_x_index = next_src_x_index; int src_x_index_end = src_x_index + src_x_frac_size; if (src_x_index_end >= w_limit) { src_x_index_end = w_limit + 1; } int width = src_x_index_end - src_x_index; uint32_t area = width * height; uint32_t acc = 0; if (width < 4) { for (int i = src_y_index; i < src_y_index_end; i++) { uint8_t *src_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(src_img, i) + src_x_index; int n = width; #if defined(ARM_MATH_DSP) uint16_t *src_row_ptr16 = (uint16_t *) src_row_ptr; for (; n > 1; n -= 2) { uint16_t pixels = *src_row_ptr16++; acc = __USADA8(pixels, 0, acc); } src_row_ptr = (uint8_t *) src_row_ptr16; #endif for (; n > 0; n -= 1) { acc += *src_row_ptr++; } } } else { for (int i = src_y_index; i < src_y_index_end; i++) { uint8_t *src_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(src_img, i) + src_x_index; int n = width; #if defined(ARM_MATH_DSP) uint32_t *src_row_ptr32 = (uint32_t *) src_row_ptr; for (; n > 3; n -= 4) { uint32_t pixels = *src_row_ptr32++; acc = __USADA8(pixels, 0, acc); } src_row_ptr = (uint8_t *) src_row_ptr32; #endif for (; n > 0; n -= 1) { acc += *src_row_ptr++; } } } int pixel = (acc + (area >> 1)) / area; 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 PIXFORMAT_RGB565: { while (y_not_done) { int src_y_index = next_src_y_index; int src_y_index_end = src_y_index + src_y_frac_size; if (src_y_index_end >= h_limit) { src_y_index_end = h_limit + 1; } int height = src_y_index_end - src_y_index; uint16_t *dst_row_ptr = (uint16_t *) imlib_draw_row_data.row_buffer; // 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 src_x_index = next_src_x_index; int src_x_index_end = src_x_index + src_x_frac_size; if (src_x_index_end >= w_limit) { src_x_index_end = w_limit + 1; } int width = src_x_index_end - src_x_index; uint32_t area = width * height; uint32_t r_acc = 0, g_acc = 0, b_acc = 0; for (int i = src_y_index; i < src_y_index_end; i++) { uint16_t *src_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src_img, i) + src_x_index; int n = width; #if defined(ARM_MATH_DSP) uint32_t *src_row_ptr32 = (uint32_t *) src_row_ptr; for (; n > 1; n -= 2) { uint32_t pixels = *src_row_ptr32++; long r = (pixels >> 11) & 0x1F001F; r_acc = __USADA8(r, 0, r_acc); long g = (pixels >> 5) & 0x3F003F; g_acc = __USADA8(g, 0, g_acc); long b = pixels & 0x1F001F; b_acc = __USADA8(b, 0, b_acc); } src_row_ptr = (uint16_t *) src_row_ptr32; #endif for (; n > 0; n -= 1) { int pixel = *src_row_ptr++; r_acc += COLOR_RGB565_TO_R5(pixel); g_acc += COLOR_RGB565_TO_G6(pixel); b_acc += COLOR_RGB565_TO_B5(pixel); } } r_acc = (r_acc + (area >> 1)) / area; g_acc = (g_acc + (area >> 1)) / area; b_acc = (b_acc + (area >> 1)) / area; int pixel = COLOR_R5_G6_B5_TO_RGB565(r_acc, g_acc, b_acc); 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; } } } else { // slow switch (src_img->pixfmt) { case PIXFORMAT_BINARY: { int t_b_weight_sum = 256 + ((src_y_frac >> 8) & 0xFF); int l_r_weight_sum = 256 + ((src_x_frac >> 8) & 0xFF); while (y_not_done) { int src_y_index = next_src_y_index, src_y_index_p_1 = src_y_index + 1; int src_y_index_end = src_y_index + src_y_frac_size - 1; // inclusive end int t_y_weight = 256 - (((src_y_accum + 255) >> 8) & 0xFF); int b_y_weight = ((src_y_accum + src_y_frac + 255) >> 8) & 0xFF; // Since src_y_index_end is inclusive this should be 256 when there's perfect overlap. if ((!b_y_weight) && (t_y_weight < t_b_weight_sum)) { b_y_weight = 256; } // Handle end being off the edge. if (src_y_index_end > h_limit) { src_y_index_end = h_limit; // Either we don't need end this or we chopped off the last part. if (src_y_index_end == src_y_index) { b_y_weight = 0; } else{ b_y_weight = 256; // max out if we chopped off } } // Handle discontinuities. if ((t_y_weight + b_y_weight) < 256) { t_y_weight += 128; b_y_weight += 128; } // Weights must be balanced. if ((t_y_weight + b_y_weight) > t_b_weight_sum) { b_y_weight -= 1; // It's only ever over by 1. } int y_height_m_2 = src_y_index_end - src_y_index - 1; uint32_t *t_src_row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(src_img, src_y_index); uint32_t *b_src_row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(src_img, src_y_index_end); uint32_t *dst_row_ptr = (uint32_t *) imlib_draw_row_data.row_buffer; // 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 src_x_index = next_src_x_index, src_x_index_p_1 = src_x_index + 1; int src_x_index_end = src_x_index + src_x_frac_size - 1; // inclusive end int l_x_weight = 256 - (((src_x_accum + 255) >> 8) & 0xFF); int r_x_weight = ((src_x_accum + src_x_frac + 255) >> 8) & 0xFF; // Since src_x_index_end is inclusive this should be 256 when there's perfect overlap. if ((!r_x_weight) && (l_x_weight < l_r_weight_sum)) { r_x_weight = 256; } // Handle end being off the edge. if (src_x_index_end > w_limit) { src_x_index_end = w_limit; // Either we don't need end this or we chopped off the last part. if (src_x_index_end == src_x_index) { r_x_weight = 0; } else{ r_x_weight = 256; // max out if we chopped off } } // Handle discontinuities. if ((l_x_weight + r_x_weight) < 256) { l_x_weight += 128; r_x_weight += 128; } // Weights must be balanced. if ((l_x_weight + r_x_weight) > l_r_weight_sum) { r_x_weight -= 1; // It's only ever over by 1. } int x_width_m_2 = src_x_index_end - src_x_index - 1; int t_l_weight = t_y_weight * l_x_weight; int t_r_weight = t_y_weight * r_x_weight; int b_l_weight = b_y_weight * l_x_weight; int b_r_weight = b_y_weight * r_x_weight; uint32_t area = t_l_weight + t_r_weight + b_l_weight + b_r_weight; uint32_t acc = 0; // sum corners acc += IMAGE_GET_BINARY_PIXEL_FAST(t_src_row_ptr, src_x_index) * t_l_weight; acc += IMAGE_GET_BINARY_PIXEL_FAST(t_src_row_ptr, src_x_index_end) * t_r_weight; acc += IMAGE_GET_BINARY_PIXEL_FAST(b_src_row_ptr, src_x_index) * b_l_weight; acc += IMAGE_GET_BINARY_PIXEL_FAST(b_src_row_ptr, src_x_index_end) * b_r_weight; area = (area + 255) >> 8; acc = (acc + 128) >> 8; if (x_width_m_2 > 0) { // sum top/bot area += x_width_m_2 * (t_y_weight + b_y_weight); for (int i = src_x_index_p_1; i < src_x_index_end; i++) { acc += IMAGE_GET_BINARY_PIXEL_FAST(t_src_row_ptr, i) * t_y_weight; acc += IMAGE_GET_BINARY_PIXEL_FAST(b_src_row_ptr, i) * b_y_weight; } } if (y_height_m_2 > 0) { // sum left/right area += y_height_m_2 * (l_x_weight + r_x_weight); for (int i = src_y_index_p_1; i < src_y_index_end; i++) { uint32_t *src_row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(src_img, i); acc += IMAGE_GET_BINARY_PIXEL_FAST(src_row_ptr, src_x_index) * l_x_weight; acc += IMAGE_GET_BINARY_PIXEL_FAST(src_row_ptr, src_x_index_end) * r_x_weight; } } area = (area + 255) >> 8; acc = (acc + 128) >> 8; if ((x_width_m_2 > 0) && (y_height_m_2 > 0)) { // sum middle area += x_width_m_2 * y_height_m_2; for (int i = src_y_index_p_1; i < src_y_index_end; i++) { uint32_t *src_row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(src_img, i); for (int j = src_x_index_p_1; j < src_x_index_end; j++) { acc += IMAGE_GET_BINARY_PIXEL_FAST(src_row_ptr, j); } } } int pixel = (acc + (area >> 1)) / area; IMAGE_PUT_BINARY_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 PIXFORMAT_GRAYSCALE: { int t_b_weight_sum = 256 + ((src_y_frac >> 8) & 0xFF); int l_r_weight_sum = 256 + ((src_x_frac >> 8) & 0xFF); while (y_not_done) { int src_y_index = next_src_y_index, src_y_index_p_1 = src_y_index + 1; int src_y_index_end = src_y_index + src_y_frac_size - 1; // inclusive end int t_y_weight = 256 - (((src_y_accum + 255) >> 8) & 0xFF); int b_y_weight = ((src_y_accum + src_y_frac + 255) >> 8) & 0xFF; // Since src_y_index_end is inclusive this should be 256 when there's perfect overlap. if ((!b_y_weight) && (t_y_weight < t_b_weight_sum)) { b_y_weight = 256; } // Handle end being off the edge. if (src_y_index_end > h_limit) { src_y_index_end = h_limit; // Either we don't need end this or we chopped off the last part. if (src_y_index_end == src_y_index) { b_y_weight = 0; } else{ b_y_weight = 256; // max out if we chopped off } } // Handle discontinuities. if ((t_y_weight + b_y_weight) < t_b_weight_sum) { t_y_weight += 128; b_y_weight += 128; } // Weights must be balanced. if ((t_y_weight + b_y_weight) > t_b_weight_sum) { b_y_weight -= 1; // It's only ever over by 1. } int y_height_m_2 = src_y_index_end - src_y_index - 1; uint8_t *t_src_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(src_img, src_y_index); uint8_t *b_src_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(src_img, src_y_index_end); uint8_t *dst_row_ptr = (uint8_t *) imlib_draw_row_data.row_buffer; // 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 src_x_index = next_src_x_index, src_x_index_p_1 = src_x_index + 1; int src_x_index_end = src_x_index + src_x_frac_size - 1; // inclusive end int l_x_weight = 256 - (((src_x_accum + 255) >> 8) & 0xFF); int r_x_weight = ((src_x_accum + src_x_frac + 255) >> 8) & 0xFF; // Since src_x_index_end is inclusive this should be 256 when there's perfect overlap. if ((!r_x_weight) && (l_x_weight < l_r_weight_sum)) { r_x_weight = 256; } // Handle end being off the edge. if (src_x_index_end > w_limit) { src_x_index_end = w_limit; // Either we don't need end this or we chopped off the last part. if (src_x_index_end == src_x_index) { r_x_weight = 0; } else{ r_x_weight = 256; // max out if we chopped off } } // Handle discontinuities. if ((l_x_weight + r_x_weight) < l_r_weight_sum) { l_x_weight += 128; r_x_weight += 128; } // Weights must be balanced. if ((l_x_weight + r_x_weight) > l_r_weight_sum) { r_x_weight -= 1; // It's only ever over by 1. } int x_width_m_2 = src_x_index_end - src_x_index - 1; int t_l_weight = t_y_weight * l_x_weight; int t_r_weight = t_y_weight * r_x_weight; int b_l_weight = b_y_weight * l_x_weight; int b_r_weight = b_y_weight * r_x_weight; uint32_t area = t_l_weight + t_r_weight + b_l_weight + b_r_weight; uint32_t acc = 0; // sum corners acc += IMAGE_GET_GRAYSCALE_PIXEL_FAST(t_src_row_ptr, src_x_index) * t_l_weight; acc += IMAGE_GET_GRAYSCALE_PIXEL_FAST(t_src_row_ptr, src_x_index_end) * t_r_weight; acc += IMAGE_GET_GRAYSCALE_PIXEL_FAST(b_src_row_ptr, src_x_index) * b_l_weight; acc += IMAGE_GET_GRAYSCALE_PIXEL_FAST(b_src_row_ptr, src_x_index_end) * b_r_weight; area = (area + 255) >> 8; acc = (acc + 128) >> 8; if (x_width_m_2 > 0) { // sum top/bot area += x_width_m_2 * (t_y_weight + b_y_weight); uint8_t *t_src_row_ptr_tmp = t_src_row_ptr + src_x_index_p_1; uint8_t *b_src_row_ptr_tmp = b_src_row_ptr + src_x_index_p_1; for (int i = src_x_index_p_1; i < src_x_index_end; i++) { acc += *t_src_row_ptr_tmp++ *t_y_weight; acc += *b_src_row_ptr_tmp++ *b_y_weight; } } if (y_height_m_2 > 0) { // sum left/right area += y_height_m_2 * (l_x_weight + r_x_weight); for (int i = src_y_index_p_1; i < src_y_index_end; i++) { uint8_t *src_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(src_img, i); acc += IMAGE_GET_GRAYSCALE_PIXEL_FAST(src_row_ptr, src_x_index) * l_x_weight; acc += IMAGE_GET_GRAYSCALE_PIXEL_FAST(src_row_ptr, src_x_index_end) * r_x_weight; } } area = (area + 255) >> 8; acc = (acc + 128) >> 8; if ((x_width_m_2 > 0) && (y_height_m_2 > 0)) { // sum middle area += x_width_m_2 * y_height_m_2; if (x_width_m_2 < 4) { for (int i = src_y_index_p_1; i < src_y_index_end; i++) { uint8_t *src_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(src_img, i) + src_x_index_p_1; int n = x_width_m_2; #if defined(ARM_MATH_DSP) uint16_t *src_row_ptr16 = (uint16_t *) src_row_ptr; for (; n > 1; n -= 2) { uint16_t pixels = *src_row_ptr16++; acc = __USADA8(pixels, 0, acc); } src_row_ptr = (uint8_t *) src_row_ptr16; #endif for (; n > 0; n -= 1) { acc += *src_row_ptr++; } } } else { for (int i = src_y_index_p_1; i < src_y_index_end; i++) { uint8_t *src_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(src_img, i) + src_x_index_p_1; int n = x_width_m_2; #if defined(ARM_MATH_DSP) uint32_t *src_row_ptr32 = (uint32_t *) src_row_ptr; for (; n > 4; n -= 4) { uint32_t pixels = *src_row_ptr32++; acc = __USADA8(pixels, 0, acc); } src_row_ptr = (uint8_t *) src_row_ptr32; #endif for (; n > 0; n -= 1) { acc += *src_row_ptr++; } } } } int pixel = (acc + (area >> 1)) / area; 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 PIXFORMAT_RGB565: { int t_b_weight_sum = 64 + ((src_y_frac >> 10) & 0x3F); int l_r_weight_sum = 64 + ((src_x_frac >> 10) & 0x3F); while (y_not_done) { int src_y_index = next_src_y_index, src_y_index_p_1 = src_y_index + 1; int src_y_index_end = src_y_index + src_y_frac_size - 1; // inclusive end int t_y_weight = 64 - (((src_y_accum + 63) >> 10) & 0x3F); int b_y_weight = ((src_y_accum + src_y_frac + 63) >> 10) & 0x3F; // Since src_y_index_end is inclusive this should be 128 when there's perfect overlap. if ((!b_y_weight) && (t_y_weight < t_b_weight_sum)) { b_y_weight = 64; } // Handle end being off the edge. if (src_y_index_end > h_limit) { src_y_index_end = h_limit; // Either we don't need end this or we chopped off the last part. if (src_y_index_end == src_y_index) { b_y_weight = 0; } else{ b_y_weight = 64; // max out if we chopped off } } // Handle discontinuities. if ((t_y_weight + b_y_weight) < t_b_weight_sum) { t_y_weight += 32; b_y_weight += 32; } // Weights must be balanced. if ((t_y_weight + b_y_weight) > t_b_weight_sum) { b_y_weight -= 1; // It's only ever over by 1. } int y_height_m_2 = src_y_index_end - src_y_index - 1; long smlad_y_weight = (t_y_weight << 16) | b_y_weight; uint16_t *t_src_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src_img, src_y_index); uint16_t *b_src_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src_img, src_y_index_end); uint16_t *dst_row_ptr = (uint16_t *) imlib_draw_row_data.row_buffer; // 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 src_x_index = next_src_x_index, src_x_index_p_1 = src_x_index + 1; int src_x_index_end = src_x_index + src_x_frac_size - 1; // inclusive end int l_x_weight = 64 - (((src_x_accum + 63) >> 10) & 0x3F); int r_x_weight = ((src_x_accum + src_x_frac + 63) >> 10) & 0x3F; // Since src_x_index_end is inclusive this should be 128 when there's perfect overlap. if ((!r_x_weight) && (l_x_weight < l_r_weight_sum)) { r_x_weight = 64; } // Handle end being off the edge. if (src_x_index_end > w_limit) { src_x_index_end = w_limit; // Either we don't need end this or we chopped off the last part. if (src_x_index_end == src_x_index) { r_x_weight = 0; } else{ r_x_weight = 64; // max out if we chopped off } } // Handle discontinuities. if ((l_x_weight + r_x_weight) < l_r_weight_sum) { l_x_weight += 32; r_x_weight += 32; } // Weights must be balanced. if ((l_x_weight + r_x_weight) > l_r_weight_sum) { r_x_weight -= 1; // It's only ever over by 1. } int x_width_m_2 = src_x_index_end - src_x_index - 1; long smlad_x_weight = (l_x_weight << 16) | r_x_weight; long t_smlad_x_weight = smlad_x_weight * t_y_weight; long b_smlad_x_weight = smlad_x_weight * b_y_weight; long t_b_smlad_x_weight_sum = __QADD16(t_smlad_x_weight, b_smlad_x_weight); uint32_t area = __SMUAD(t_b_smlad_x_weight_sum, 0x10001); uint32_t r_acc = 0, g_acc = 0, b_acc = 0; // sum corners int t_l_pixel = IMAGE_GET_RGB565_PIXEL_FAST(t_src_row_ptr, src_x_index); int t_r_pixel = IMAGE_GET_RGB565_PIXEL_FAST(t_src_row_ptr, src_x_index_end); int t_pixels = (t_l_pixel << 16) | t_r_pixel; long t_r = (t_pixels >> 11) & 0x1F001F; r_acc = __SMLAD(t_r, t_smlad_x_weight, r_acc); long t_g = (t_pixels >> 5) & 0x3F003F; g_acc = __SMLAD(t_g, t_smlad_x_weight, g_acc); long t_b = t_pixels & 0x1F001F; b_acc = __SMLAD(t_b, t_smlad_x_weight, b_acc); int b_l_pixel = IMAGE_GET_RGB565_PIXEL_FAST(b_src_row_ptr, src_x_index); int b_r_pixel = IMAGE_GET_RGB565_PIXEL_FAST(b_src_row_ptr, src_x_index_end); int b_pixels = (b_l_pixel << 16) | b_r_pixel; long b_r = (b_pixels >> 11) & 0x1F001F; r_acc = __SMLAD(b_r, b_smlad_x_weight, r_acc); long b_g = (b_pixels >> 5) & 0x3F003F; g_acc = __SMLAD(b_g, b_smlad_x_weight, g_acc); long b_b = b_pixels & 0x1F001F; b_acc = __SMLAD(b_b, b_smlad_x_weight, b_acc); area = (area + 63) >> 6; r_acc = (r_acc + 63) >> 6; g_acc = (g_acc + 63) >> 6; b_acc = (b_acc + 63) >> 6; if (x_width_m_2 > 0) { // sum top/bot area += x_width_m_2 * (t_y_weight + b_y_weight); uint16_t *t_src_row_ptr_tmp = t_src_row_ptr + src_x_index_p_1; uint16_t *b_src_row_ptr_tmp = b_src_row_ptr + src_x_index_p_1; for (int i = src_x_index_p_1; i < src_x_index_end; i++) { int t_y_pixel = *t_src_row_ptr_tmp++; int b_y_pixel = *b_src_row_ptr_tmp++; int pixels = (t_y_pixel << 16) | b_y_pixel; long r = (pixels >> 11) & 0x1F001F; r_acc = __SMLAD(r, smlad_y_weight, r_acc); long g = (pixels >> 5) & 0x3F003F; g_acc = __SMLAD(g, smlad_y_weight, g_acc); long b = pixels & 0x1F001F; b_acc = __SMLAD(b, smlad_y_weight, b_acc); } } if (y_height_m_2 > 0) { // sum left/right area += y_height_m_2 * (l_x_weight + r_x_weight); for (int i = src_y_index_p_1; i < src_y_index_end; i++) { uint16_t *src_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src_img, i); int l_x_pixel = IMAGE_GET_RGB565_PIXEL_FAST(src_row_ptr, src_x_index); int r_x_pixel = IMAGE_GET_RGB565_PIXEL_FAST(src_row_ptr, src_x_index_end); int pixels = (l_x_pixel << 16) | r_x_pixel; long r = (pixels >> 11) & 0x1F001F; r_acc = __SMLAD(r, smlad_x_weight, r_acc); long g = (pixels >> 5) & 0x3F003F; g_acc = __SMLAD(g, smlad_x_weight, g_acc); long b = pixels & 0x1F001F; b_acc = __SMLAD(b, smlad_x_weight, b_acc); } } area = (area + 63) >> 6; r_acc = (r_acc + 63) >> 6; g_acc = (g_acc + 63) >> 6; b_acc = (b_acc + 63) >> 6; if ((x_width_m_2 > 0) && (y_height_m_2 > 0)) { // sum middle area += x_width_m_2 * y_height_m_2; for (int i = src_y_index_p_1; i < src_y_index_end; i++) { uint16_t *src_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src_img, i) + src_x_index_p_1; int n = x_width_m_2; #if defined(ARM_MATH_DSP) uint32_t *src_row_ptr32 = (uint32_t *) src_row_ptr; for (; n > 1; n -= 2) { uint32_t pixels = *src_row_ptr32++; long r = (pixels >> 11) & 0x1F001F; r_acc = __USADA8(r, 0, r_acc); long g = (pixels >> 5) & 0x3F003F; g_acc = __USADA8(g, 0, g_acc); long b = pixels & 0x1F001F; b_acc = __USADA8(b, 0, b_acc); } src_row_ptr = (uint16_t *) src_row_ptr32; #endif for (; n > 0; n -= 1) { int pixel = *src_row_ptr++; r_acc += COLOR_RGB565_TO_R5(pixel); g_acc += COLOR_RGB565_TO_G6(pixel); b_acc += COLOR_RGB565_TO_B5(pixel); } } } r_acc = (r_acc + (area >> 1)) / area; g_acc = (g_acc + (area >> 1)) / area; b_acc = (b_acc + (area >> 1)) / area; int pixel = COLOR_R5_G6_B5_TO_RGB565(r_acc, g_acc, b_acc); 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; } } } } else if (hint & IMAGE_HINT_BICUBIC) { // Implements the traditional bicubic interpolation algorithm which uses // a 4x4 filter block with the current pixel centered at (1,1) (C below). // However, instead of floating point math, it uses integer (fixed point). // The Cortex-M4/M7 has a hardware floating point unit, so doing FP math // doesn't take any extra time, but it does take extra time to convert // the integer pixels to floating point and back to integers again. // So this allows it to execute more quickly in pure integer math. // // +---+---+---+---+ // | x | x | x | x | // +---+---+---+---+ // | x | C | x | x | // +---+---+---+---+ // | x | x | x | x | // +---+---+---+---+ // | x | x | x | x | // +---+---+---+---+ // switch (src_img->pixfmt) { case PIXFORMAT_BINARY: { while (y_not_done) { int src_y_index = next_src_y_index; uint32_t *src_row_ptr_0, *src_row_ptr_1, *src_row_ptr_2, *src_row_ptr_3; // keep row pointers in bounds if (src_y_index < h_start) { src_row_ptr_0 = src_row_ptr_1 = src_row_ptr_2 = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(src_img, h_start); src_row_ptr_3 = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(src_img, h_start_p_1); } else if (src_y_index == h_start) { src_row_ptr_0 = src_row_ptr_1 = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(src_img, 0); src_row_ptr_2 = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(src_img, h_start_p_1); src_row_ptr_3 = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(src_img, h_start_p_2); } else if (src_y_index == h_limit_m_1) { int src_y_index_m_1 = src_y_index - 1; src_row_ptr_0 = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(src_img, src_y_index_m_1); src_row_ptr_1 = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(src_img, h_limit_m_1); src_row_ptr_2 = src_row_ptr_3 = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(src_img, h_limit); } else if (src_y_index >= h_limit) { int src_y_index_m_1 = src_y_index - 1; src_row_ptr_0 = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(src_img, src_y_index_m_1); src_row_ptr_1 = src_row_ptr_2 = src_row_ptr_3 = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(src_img, h_limit); } else { // get 4 neighboring rows int src_y_index_m_1 = src_y_index - 1; int src_y_index_p_1 = src_y_index + 1; int src_y_index_p_2 = src_y_index + 2; src_row_ptr_0 = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(src_img, src_y_index_m_1); src_row_ptr_1 = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(src_img, src_y_index); src_row_ptr_2 = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(src_img, src_y_index_p_1); src_row_ptr_3 = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(src_img, src_y_index_p_2); } do { // Cache the results of getting the source rows // 15-bit fraction to fit a square of it in 32-bits // pre-calculate the ^1, ^2, and ^3 of the fraction int dy = ((src_y_accum >> 1) & 0x7FFF); int dy2 = (dy * dy) >> 15; int dy3 = (dy2 * dy) >> 15; long smuad_dy_dy2 = (dy << 16) | dy2; uint32_t *dst_row_ptr = (uint32_t *) imlib_draw_row_data.row_buffer; // 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 src_x_index = next_src_x_index; int src_x_index_m_1 = src_x_index - 1; int src_x_index_p_1 = src_x_index + 1; int src_x_index_p_2 = src_x_index + 2; int pixel_x_offests[4]; // keep pixels in bounds if (src_x_index < w_start) { pixel_x_offests[0] = pixel_x_offests[1] = pixel_x_offests[2] = w_start; pixel_x_offests[3] = w_start_p_1; } else if (src_x_index == w_start) { pixel_x_offests[0] = pixel_x_offests[1] = w_start; pixel_x_offests[2] = w_start_p_1; pixel_x_offests[3] = w_start_p_2; } else if (src_x_index == w_limit_m_1) { pixel_x_offests[0] = src_x_index_m_1; pixel_x_offests[1] = w_limit_m_1; pixel_x_offests[2] = pixel_x_offests[3] = w_limit; } else if (src_x_index >= w_limit) { pixel_x_offests[0] = src_x_index_m_1; pixel_x_offests[1] = pixel_x_offests[2] = pixel_x_offests[3] = w_limit; } else { // get 4 neighboring rows pixel_x_offests[0] = src_x_index_m_1; pixel_x_offests[1] = src_x_index; pixel_x_offests[2] = src_x_index_p_1; pixel_x_offests[3] = src_x_index_p_2; } int d[4]; for (int z = 0; z < 4; z++) { // bicubic x step (-1 to +2) int pixel_0 = IMAGE_GET_BINARY_PIXEL_FAST(src_row_ptr_0, pixel_x_offests[z]) * 0xFF; // more res int pixel_1 = IMAGE_GET_BINARY_PIXEL_FAST(src_row_ptr_1, pixel_x_offests[z]) * 0xFF; // more res int pixel_2 = IMAGE_GET_BINARY_PIXEL_FAST(src_row_ptr_2, pixel_x_offests[z]) * 0xFF; // more res int pixel_3 = IMAGE_GET_BINARY_PIXEL_FAST(src_row_ptr_3, pixel_x_offests[z]) * 0xFF; // more res int a0 = pixel_2 - pixel_0; int a1 = (pixel_0 << 1) + (pixel_2 << 2) - (5 * pixel_1) - pixel_3; int a2 = (3 * (pixel_1 - pixel_2)) + pixel_3 - pixel_0; long smuad_a0_a1 = __PKHBT(a1, a0, 16); int pixel_1_avg = (pixel_1 << 16) | 0x8000; d[z] = ((int32_t) __SMLAD(smuad_dy_dy2, smuad_a0_a1, (dy3 * a2) + pixel_1_avg)) >> 16; } // for z int d0 = d[0], d1 = d[1], d2 = d[2], d3 = d[3]; int a0 = d2 - d0; int a1 = (d0 << 1) + (d2 << 2) - (5 * d1) - d3; int a2 = (3 * (d1 - d2)) + d3 - d0; long smuad_a0_a1 = __PKHBT(a1, a0, 16); int d1_avg = (d1 << 16) | 0x8000; do { // Cache the results of getting the source pixels // 15-bit fraction to fit a square of it in 32-bits // pre-calculate the ^1, ^2, and ^3 of the fraction int dx = ((src_x_accum >> 1) & 0x7FFF); int dx2 = (dx * dx) >> 15; int dx3 = (dx2 * dx) >> 15; long smuad_dx_dx2 = (dx << 16) | dx2; int pixel = __SMLAD(smuad_dx_dx2, smuad_a0_a1, (dx3 * a2) + d1_avg); // clamp output pixel = __USAT_ASR(pixel, 1, 23); IMAGE_PUT_BINARY_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_not_done && (src_x_index == next_src_x_index)); } // 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_not_done && (src_y_index == next_src_y_index)); } // while y break; } case PIXFORMAT_GRAYSCALE: { while (y_not_done) { int src_y_index = next_src_y_index; uint8_t *src_row_ptr_0, *src_row_ptr_1, *src_row_ptr_2, *src_row_ptr_3; // keep row pointers in bounds if (src_y_index < 0) { src_row_ptr_0 = src_row_ptr_1 = src_row_ptr_2 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(src_img, h_start); src_row_ptr_3 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(src_img, h_start_p_1); } else if (src_y_index == h_start) { src_row_ptr_0 = src_row_ptr_1 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(src_img, h_start); src_row_ptr_2 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(src_img, h_start_p_1); src_row_ptr_3 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(src_img, h_start_p_2); } else if (src_y_index == h_limit_m_1) { int src_y_index_m_1 = src_y_index - 1; src_row_ptr_0 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(src_img, src_y_index_m_1); src_row_ptr_1 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(src_img, h_limit_m_1); src_row_ptr_2 = src_row_ptr_3 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(src_img, h_limit); } else if (src_y_index >= h_limit) { int src_y_index_m_1 = src_y_index - 1; src_row_ptr_0 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(src_img, src_y_index_m_1); src_row_ptr_1 = src_row_ptr_2 = src_row_ptr_3 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(src_img, h_limit); } else { // get 4 neighboring rows int src_y_index_m_1 = src_y_index - 1; int src_y_index_p_1 = src_y_index + 1; int src_y_index_p_2 = src_y_index + 2; src_row_ptr_0 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(src_img, src_y_index_m_1); src_row_ptr_1 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(src_img, src_y_index); src_row_ptr_2 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(src_img, src_y_index_p_1); src_row_ptr_3 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(src_img, src_y_index_p_2); } do { // Cache the results of getting the source rows // 15-bit fraction to fit a square of it in 32-bits // pre-calculate the ^1, ^2, and ^3 of the fraction int dy = ((src_y_accum >> 1) & 0x7FFF); int dy2 = (dy * dy) >> 15; int dy3 = (dy2 * dy) >> 15; long smuad_dy_dy2 = (dy << 16) | dy2; uint8_t *dst_row_ptr = (uint8_t *) imlib_draw_row_data.row_buffer; // 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 src_x_index = next_src_x_index; int src_x_index_m_1 = src_x_index - 1; int src_x_index_p_1 = src_x_index + 1; int src_x_index_p_2 = src_x_index + 2; int pixel_x_offests[4]; // keep pixels in bounds if (src_x_index < w_start) { pixel_x_offests[0] = pixel_x_offests[1] = pixel_x_offests[2] = w_start; pixel_x_offests[3] = w_start_p_1; } else if (src_x_index == w_start) { pixel_x_offests[0] = pixel_x_offests[1] = w_start; pixel_x_offests[2] = w_start_p_1; pixel_x_offests[3] = w_start_p_2; } else if (src_x_index == w_limit_m_1) { pixel_x_offests[0] = src_x_index_m_1; pixel_x_offests[1] = w_limit_m_1; pixel_x_offests[2] = pixel_x_offests[3] = w_limit; } else if (src_x_index >= w_limit) { pixel_x_offests[0] = src_x_index_m_1; pixel_x_offests[1] = pixel_x_offests[2] = pixel_x_offests[3] = w_limit; } else { // get 4 neighboring rows pixel_x_offests[0] = src_x_index_m_1; pixel_x_offests[1] = src_x_index; pixel_x_offests[2] = src_x_index_p_1; pixel_x_offests[3] = src_x_index_p_2; } int d[4]; for (int z = 0; z < 4; z++) { // bicubic x step (-1 to +2) int pixel_0 = IMAGE_GET_GRAYSCALE_PIXEL_FAST(src_row_ptr_0, pixel_x_offests[z]); int pixel_1 = IMAGE_GET_GRAYSCALE_PIXEL_FAST(src_row_ptr_1, pixel_x_offests[z]); int pixel_2 = IMAGE_GET_GRAYSCALE_PIXEL_FAST(src_row_ptr_2, pixel_x_offests[z]); int pixel_3 = IMAGE_GET_GRAYSCALE_PIXEL_FAST(src_row_ptr_3, pixel_x_offests[z]); int a0 = pixel_2 - pixel_0; int a1 = (pixel_0 << 1) + (pixel_2 << 2) - (5 * pixel_1) - pixel_3; int a2 = (3 * (pixel_1 - pixel_2)) + pixel_3 - pixel_0; long smuad_a0_a1 = __PKHBT(a1, a0, 16); int pixel_1_avg = (pixel_1 << 16) | 0x8000; d[z] = ((int32_t) __SMLAD(smuad_dy_dy2, smuad_a0_a1, (dy3 * a2) + pixel_1_avg)) >> 16; } // for z int d0 = d[0], d1 = d[1], d2 = d[2], d3 = d[3]; int a0 = d2 - d0; int a1 = (d0 << 1) + (d2 << 2) - (5 * d1) - d3; int a2 = (3 * (d1 - d2)) + d3 - d0; long smuad_a0_a1 = __PKHBT(a1, a0, 16); int d1_avg = (d1 << 16) | 0x8000; do { // Cache the results of getting the source pixels // 15-bit fraction to fit a square of it in 32-bits // pre-calculate the ^1, ^2, and ^3 of the fraction int dx = ((src_x_accum >> 1) & 0x7FFF); int dx2 = (dx * dx) >> 15; int dx3 = (dx2 * dx) >> 15; long smuad_dx_dx2 = (dx << 16) | dx2; int pixel = __SMLAD(smuad_dx_dx2, smuad_a0_a1, (dx3 * a2) + d1_avg); // clamp output pixel = __USAT_ASR(pixel, 8, 16); 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_not_done && (src_x_index == next_src_x_index)); } // 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_not_done && (src_y_index == next_src_y_index)); } // while y break; } case PIXFORMAT_RGB565: { while (y_not_done) { int src_y_index = next_src_y_index; uint16_t *src_row_ptr_0, *src_row_ptr_1, *src_row_ptr_2, *src_row_ptr_3; // keep row pointers in bounds if (src_y_index < h_start) { src_row_ptr_0 = src_row_ptr_1 = src_row_ptr_2 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src_img, h_start); src_row_ptr_3 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src_img, h_start_p_1); } else if (src_y_index == h_start) { src_row_ptr_0 = src_row_ptr_1 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src_img, h_start); src_row_ptr_2 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src_img, h_start_p_1); src_row_ptr_3 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src_img, h_start_p_2); } else if (src_y_index == h_limit_m_1) { int src_y_index_m_1 = src_y_index - 1; src_row_ptr_0 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src_img, src_y_index_m_1); src_row_ptr_1 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src_img, h_limit_m_1); src_row_ptr_2 = src_row_ptr_3 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src_img, h_limit); } else if (src_y_index >= h_limit) { int src_y_index_m_1 = src_y_index - 1; src_row_ptr_0 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src_img, src_y_index_m_1); src_row_ptr_1 = src_row_ptr_2 = src_row_ptr_3 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src_img, h_limit); } else { // get 4 neighboring rows int src_y_index_m_1 = src_y_index - 1; int src_y_index_p_1 = src_y_index + 1; int src_y_index_p_2 = src_y_index + 2; src_row_ptr_0 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src_img, src_y_index_m_1); src_row_ptr_1 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src_img, src_y_index); src_row_ptr_2 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src_img, src_y_index_p_1); src_row_ptr_3 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src_img, src_y_index_p_2); } do { // Cache the results of getting the source rows // 15-bit fraction to fit a square of it in 32-bits // pre-calculate the ^1, ^2, and ^3 of the fraction int dy = ((src_y_accum >> 1) & 0x7FFF); int dy2 = (dy * dy) >> 15; int dy3 = (dy2 * dy) >> 15; long smuad_dy_dy2 = (dy << 16) | dy2; uint16_t *dst_row_ptr = (uint16_t *) imlib_draw_row_data.row_buffer; // 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 src_x_index = next_src_x_index; int src_x_index_m_1 = src_x_index - 1; int src_x_index_p_1 = src_x_index + 1; #if defined(ARM_MATH_DSP) uint32_t pixel_row_0[2], pixel_row_1[2], pixel_row_2[2], pixel_row_3[2]; // Column 0 = Bits[15:0] // Column 1 = Bits[31:16] if (src_x_index < w_start) { pixel_row_0[0] = (*(src_row_ptr_0 + w_start)) * 0x10001; pixel_row_0[1] = __PKHBT(pixel_row_0[0], *(src_row_ptr_0 + w_start_p_1), 16); pixel_row_1[0] = (*(src_row_ptr_1 + w_start)) * 0x10001; pixel_row_1[1] = __PKHBT(pixel_row_1[0], *(src_row_ptr_1 + w_start_p_1), 16); pixel_row_2[0] = (*(src_row_ptr_2 + w_start)) * 0x10001; pixel_row_2[1] = __PKHBT(pixel_row_2[0], *(src_row_ptr_2 + w_start_p_1), 16); pixel_row_3[0] = (*(src_row_ptr_3 + w_start)) * 0x10001; pixel_row_3[1] = __PKHBT(pixel_row_3[0], *(src_row_ptr_3 + w_start_p_1), 16); } else if (src_x_index == w_start) { pixel_row_0[0] = (*(src_row_ptr_0 + w_start)) * 0x10001; pixel_row_0[1] = *((uint32_t *) (src_row_ptr_0 + w_start_p_1)); pixel_row_1[0] = (*(src_row_ptr_1 + w_start)) * 0x10001; pixel_row_1[1] = *((uint32_t *) (src_row_ptr_1 + w_start_p_1)); pixel_row_2[0] = (*(src_row_ptr_2 + w_start)) * 0x10001; pixel_row_2[1] = *((uint32_t *) (src_row_ptr_2 + w_start_p_1)); pixel_row_3[0] = (*(src_row_ptr_3 + w_start)) * 0x10001; pixel_row_3[1] = *((uint32_t *) (src_row_ptr_3 + w_start_p_1)); } else if (src_x_index == w_limit_m_1) { pixel_row_0[0] = *((uint32_t *) (src_row_ptr_0 + src_x_index_m_1)); pixel_row_0[1] = (*(src_row_ptr_0 + w_limit)) * 0x10001; pixel_row_1[0] = *((uint32_t *) (src_row_ptr_1 + src_x_index_m_1)); pixel_row_1[1] = (*(src_row_ptr_1 + w_limit)) * 0x10001; pixel_row_2[0] = *((uint32_t *) (src_row_ptr_2 + src_x_index_m_1)); pixel_row_2[1] = (*(src_row_ptr_2 + w_limit)) * 0x10001; pixel_row_3[0] = *((uint32_t *) (src_row_ptr_3 + src_x_index_m_1)); pixel_row_3[1] = (*(src_row_ptr_3 + w_limit)) * 0x10001; } else if (src_x_index >= w_limit) { pixel_row_0[0] = *((uint32_t *) (src_row_ptr_0 + src_x_index_m_1)); pixel_row_0[1] = (pixel_row_0[0] >> 16) * 0x10001; pixel_row_1[0] = *((uint32_t *) (src_row_ptr_1 + src_x_index_m_1)); pixel_row_1[1] = (pixel_row_1[0] >> 16) * 0x10001; pixel_row_2[0] = *((uint32_t *) (src_row_ptr_2 + src_x_index_m_1)); pixel_row_2[1] = (pixel_row_2[0] >> 16) * 0x10001; pixel_row_3[0] = *((uint32_t *) (src_row_ptr_3 + src_x_index_m_1)); pixel_row_3[1] = (pixel_row_3[0] >> 16) * 0x10001; } else { // get 4 neighboring rows pixel_row_0[0] = *((uint32_t *) (src_row_ptr_0 + src_x_index_m_1)); pixel_row_0[1] = *((uint32_t *) (src_row_ptr_0 + src_x_index_p_1)); pixel_row_1[0] = *((uint32_t *) (src_row_ptr_1 + src_x_index_m_1)); pixel_row_1[1] = *((uint32_t *) (src_row_ptr_1 + src_x_index_p_1)); pixel_row_2[0] = *((uint32_t *) (src_row_ptr_2 + src_x_index_m_1)); pixel_row_2[1] = *((uint32_t *) (src_row_ptr_2 + src_x_index_p_1)); pixel_row_3[0] = *((uint32_t *) (src_row_ptr_3 + src_x_index_m_1)); pixel_row_3[1] = *((uint32_t *) (src_row_ptr_3 + src_x_index_p_1)); } int r_d[4], g_d[4], b_d[4]; for (int z = 0; z < 2; z++) { // dual bicubic x step (-1 to +2) long r_pixel_row_0 = (pixel_row_0[z] >> 11) & 0x1f001f; long r_pixel_row_1 = (pixel_row_1[z] >> 11) & 0x1f001f; long r_pixel_row_2 = (pixel_row_2[z] >> 11) & 0x1f001f; long r_pixel_row_3 = (pixel_row_3[z] >> 11) & 0x1f001f; uint32_t r_a0_col = __QSUB16(r_pixel_row_2, r_pixel_row_0); uint32_t r_a1_col = __QSUB16(__QSUB16(__QADD16(r_pixel_row_0 << 1, r_pixel_row_2 << 2), r_pixel_row_1 * 5), r_pixel_row_3); uint32_t r_a2_col = __QSUB16(__QADD16(__QSUB16(r_pixel_row_1 * 3, r_pixel_row_2 * 3), r_pixel_row_3), r_pixel_row_0); long r_smuad_a0_a1_0 = __PKHBT(r_a1_col, r_a0_col, 16); long r_pixel_1_avg_0 = (r_pixel_row_1 << 16) | 0x8000; r_d[z * 2] = ((int32_t) __SMLAD(smuad_dy_dy2, r_smuad_a0_a1_0, __SMLAD(dy3, r_a2_col, r_pixel_1_avg_0))) >> 16; long r_smuad_a0_a1_1 = __PKHTB(r_a0_col, r_a1_col, 16); long r_pixel_1_avg_1 = __PKHTB(r_pixel_row_1, 0x8000, 0); r_d[(z * 2) + 1] = ((int32_t) __SMLAD(smuad_dy_dy2, r_smuad_a0_a1_1, __SMLADX(dy3, r_a2_col, r_pixel_1_avg_1))) >> 16; long g_pixel_row_0 = (pixel_row_0[z] >> 5) & 0x3f003f; long g_pixel_row_1 = (pixel_row_1[z] >> 5) & 0x3f003f; long g_pixel_row_2 = (pixel_row_2[z] >> 5) & 0x3f003f; long g_pixel_row_3 = (pixel_row_3[z] >> 5) & 0x3f003f; uint32_t g_a0_col = __QSUB16(g_pixel_row_2, g_pixel_row_0); uint32_t g_a1_col = __QSUB16(__QSUB16(__QADD16(g_pixel_row_0 << 1, g_pixel_row_2 << 2), g_pixel_row_1 * 5), g_pixel_row_3); uint32_t g_a2_col = __QSUB16(__QADD16(__QSUB16(g_pixel_row_1 * 3, g_pixel_row_2 * 3), g_pixel_row_3), g_pixel_row_0); long g_smuad_a0_a1_0 = __PKHBT(g_a1_col, g_a0_col, 16); long g_pixel_1_avg_0 = (g_pixel_row_1 << 16) | 0x8000; g_d[z * 2] = ((int32_t) __SMLAD(smuad_dy_dy2, g_smuad_a0_a1_0, __SMLAD(dy3, g_a2_col, g_pixel_1_avg_0))) >> 16; long g_smuad_a0_a1_1 = __PKHTB(g_a0_col, g_a1_col, 16); long g_pixel_1_avg_1 = __PKHTB(g_pixel_row_1, 0x8000, 0); g_d[(z * 2) + 1] = ((int32_t) __SMLAD(smuad_dy_dy2, g_smuad_a0_a1_1, __SMLADX(dy3, g_a2_col, g_pixel_1_avg_1))) >> 16; long b_pixel_row_0 = pixel_row_0[z] & 0x1f001f; long b_pixel_row_1 = pixel_row_1[z] & 0x1f001f; long b_pixel_row_2 = pixel_row_2[z] & 0x1f001f; long b_pixel_row_3 = pixel_row_3[z] & 0x1f001f; uint32_t b_a0_col = __QSUB16(b_pixel_row_2, b_pixel_row_0); uint32_t b_a1_col = __QSUB16(__QSUB16(__QADD16(b_pixel_row_0 << 1, b_pixel_row_2 << 2), b_pixel_row_1 * 5), b_pixel_row_3); uint32_t b_a2_col = __QSUB16(__QADD16(__QSUB16(b_pixel_row_1 * 3, b_pixel_row_2 * 3), b_pixel_row_3), b_pixel_row_0); long b_smuad_a0_a1_0 = __PKHBT(b_a1_col, b_a0_col, 16); long b_pixel_1_avg_0 = (b_pixel_row_1 << 16) | 0x8000; b_d[z * 2] = ((int32_t) __SMLAD(smuad_dy_dy2, b_smuad_a0_a1_0, __SMLAD(dy3, b_a2_col, b_pixel_1_avg_0))) >> 16; long b_smuad_a0_a1_1 = __PKHTB(b_a0_col, b_a1_col, 16); long b_pixel_1_avg_1 = __PKHTB(b_pixel_row_1, 0x8000, 0); b_d[(z * 2) + 1] = ((int32_t) __SMLAD(smuad_dy_dy2, b_smuad_a0_a1_1, __SMLADX(dy3, b_a2_col, b_pixel_1_avg_1))) >> 16; } // for z #else int src_x_index_p_2 = src_x_index + 2; int pixel_x_offests[4]; // keep pixels in bounds if (src_x_index < w_start) { pixel_x_offests[0] = pixel_x_offests[1] = pixel_x_offests[2] = w_start; pixel_x_offests[3] = w_start_p_1; } else if (src_x_index == 0) { pixel_x_offests[0] = pixel_x_offests[1] = w_start; pixel_x_offests[2] = w_start_p_1; pixel_x_offests[3] = w_start_p_2; } else if (src_x_index == w_limit_m_1) { pixel_x_offests[0] = src_x_index_m_1; pixel_x_offests[1] = w_limit_m_1; pixel_x_offests[2] = pixel_x_offests[3] = w_limit; } else if (src_x_index >= w_limit) { pixel_x_offests[0] = src_x_index_m_1; pixel_x_offests[1] = pixel_x_offests[2] = pixel_x_offests[3] = w_limit; } else { // get 4 neighboring rows pixel_x_offests[0] = src_x_index_m_1; pixel_x_offests[1] = src_x_index; pixel_x_offests[2] = src_x_index_p_1; pixel_x_offests[3] = src_x_index_p_2; } int r_d[4], g_d[4], b_d[4]; for (int z = 0; z < 4; z++) { // bicubic x step (-1 to +2) int pixel_0 = IMAGE_GET_RGB565_PIXEL_FAST(src_row_ptr_0, pixel_x_offests[z]); int pixel_1 = IMAGE_GET_RGB565_PIXEL_FAST(src_row_ptr_1, pixel_x_offests[z]); int pixel_2 = IMAGE_GET_RGB565_PIXEL_FAST(src_row_ptr_2, pixel_x_offests[z]); int pixel_3 = IMAGE_GET_RGB565_PIXEL_FAST(src_row_ptr_3, pixel_x_offests[z]); int r0 = pixel_0 >> 11; int r1 = pixel_1 >> 11; int r2 = pixel_2 >> 11; int r3 = pixel_3 >> 11; int r_a0 = r2 - r0; int r_a1 = (r0 << 1) + (r2 << 2) - (5 * r1) - r3; int r_a2 = (3 * (r1 - r2)) + r3 - r0; long smuad_r_a0_r_a1 = __PKHBT(r_a1, r_a0, 16); int r1_avg = (r1 << 16) | 0x8000; r_d[z] = ((int32_t) __SMLAD(smuad_dy_dy2, smuad_r_a0_r_a1, (dy3 * r_a2) + r1_avg)) >> 16; int g0 = (pixel_0 >> 5) & 0x3F; int g1 = (pixel_1 >> 5) & 0x3F; int g2 = (pixel_2 >> 5) & 0x3F; int g3 = (pixel_3 >> 5) & 0x3F; int g_a0 = g2 - g0; int g_a1 = (g0 << 1) + (g2 << 2) - (5 * g1) - g3; int g_a2 = (3 * (g1 - g2)) + g3 - g0; long smuad_g_a0_g_a1 = __PKHBT(g_a1, g_a0, 16); int g1_avg = (g1 << 16) | 0x8000; g_d[z] = ((int32_t) __SMLAD(smuad_dy_dy2, smuad_g_a0_g_a1, (dy3 * g_a2) + g1_avg)) >> 16; int b0 = pixel_0 & 0x1F; int b1 = pixel_1 & 0x1F; int b2 = pixel_2 & 0x1F; int b3 = pixel_3 & 0x1F; int b_a0 = b2 - b0; int b_a1 = (b0 << 1) + (b2 << 2) - (5 * b1) - b3; int b_a2 = (3 * (b1 - b2)) + b3 - b0; long smuad_b_a0_b_a1 = __PKHBT(b_a1, b_a0, 16); int b1_avg = (b1 << 16) | 0x8000; b_d[z] = ((int32_t) __SMLAD(smuad_dy_dy2, smuad_b_a0_b_a1, (dy3 * b_a2) + b1_avg)) >> 16; } // for z #endif int r_d0 = r_d[0], r_d1 = r_d[1], r_d2 = r_d[2], r_d3 = r_d[3]; int r_a0 = r_d2 - r_d0; int r_a1 = (r_d0 << 1) + (r_d2 << 2) - (5 * r_d1) - r_d3; int r_a2 = (3 * (r_d1 - r_d2)) + r_d3 - r_d0; long smuad_r_a0_r_a1 = __PKHBT(r_a1, r_a0, 16); int r_d1_avg = (r_d1 << 16) | 0x8000; int g_d0 = g_d[0], g_d1 = g_d[1], g_d2 = g_d[2], g_d3 = g_d[3]; int g_a0 = g_d2 - g_d0; int g_a1 = (g_d0 << 1) + (g_d2 << 2) - (5 * g_d1) - g_d3; int g_a2 = (3 * (g_d1 - g_d2)) + g_d3 - g_d0; long smuad_g_a0_g_a1 = __PKHBT(g_a1, g_a0, 16); int g_d1_avg = (g_d1 << 16) | 0x8000; int b_d0 = b_d[0], b_d1 = b_d[1], b_d2 = b_d[2], b_d3 = b_d[3]; int b_a0 = b_d2 - b_d0; int b_a1 = (b_d0 << 1) + (b_d2 << 2) - (5 * b_d1) - b_d3; int b_a2 = (3 * (b_d1 - b_d2)) + b_d3 - b_d0; long smuad_b_a0_b_a1 = __PKHBT(b_a1, b_a0, 16); int b_d1_avg = (b_d1 << 16) | 0x8000; do { // Cache the results of getting the source pixels // 15-bit fraction to fit a square of it in 32-bits // pre-calculate the ^1, ^2, and ^3 of the fraction int dx = ((src_x_accum >> 1) & 0x7FFF); int dx2 = (dx * dx) >> 15; int dx3 = (dx2 * dx) >> 15; long smuad_dx_dx2 = (dx << 16) | dx2; long r_pixel = __SMLAD(smuad_dx_dx2, smuad_r_a0_r_a1, (dx3 * r_a2) + r_d1_avg); // clamp output r_pixel = __USAT_ASR(r_pixel, 5, 16); long g_pixel = __SMLAD(smuad_dx_dx2, smuad_g_a0_g_a1, (dx3 * g_a2) + g_d1_avg); // clamp output g_pixel = __USAT_ASR(g_pixel, 6, 16); long b_pixel = __SMLAD(smuad_dx_dx2, smuad_b_a0_b_a1, (dx3 * b_a2) + b_d1_avg); // clamp output b_pixel = __USAT_ASR(b_pixel, 5, 16); int pixel = COLOR_R5_G6_B5_TO_RGB565(r_pixel, g_pixel, b_pixel); 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_not_done && (src_x_index == next_src_x_index)); } // 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_not_done && (src_y_index == next_src_y_index)); } // while y break; } default: { break; } } } else if (hint & IMAGE_HINT_BILINEAR) { // Implements the traditional bilinear interpolation algorithm which uses // a 2x2 filter block with the current pixel centered at (0,0) (C below). // However, instead of floating point math, it uses integer (fixed point). // The Cortex-M4/M7 has a hardware floating point unit, so doing FP math // doesn't take any extra time, but it does take extra time to convert // the integer pixels to floating point and back to integers again. // So this allows it to execute more quickly in pure integer math. // // +---+---+ // | C | x | // +---+---+ // | x | x | // +---+---+ // switch (src_img->pixfmt) { case PIXFORMAT_BINARY: { while (y_not_done) { int src_y_index = next_src_y_index; uint32_t *src_row_ptr_0, *src_row_ptr_1; // keep row pointers in bounds if (src_y_index < h_start) { src_row_ptr_0 = src_row_ptr_1 = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(src_img, h_start); } else if (src_y_index >= h_limit) { src_row_ptr_0 = src_row_ptr_1 = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(src_img, h_limit); } else { // get 2 neighboring rows int src_y_index_p_1 = src_y_index + 1; src_row_ptr_0 = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(src_img, src_y_index); src_row_ptr_1 = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(src_img, src_y_index_p_1); } do { // Cache the results of getting the source rows uint32_t *src_row_ptr = ((src_y_accum >> 15) & 0x1) ? src_row_ptr_1 : src_row_ptr_0; uint32_t *dst_row_ptr = (uint32_t *) imlib_draw_row_data.row_buffer; // 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 src_x_index = next_src_x_index; int pixel_0, pixel_1; // keep pixels in bounds if (src_x_index < w_start) { pixel_0 = pixel_1 = IMAGE_GET_BINARY_PIXEL_FAST(src_row_ptr, w_start); } else if (src_x_index >= w_limit) { pixel_0 = pixel_1 = IMAGE_GET_BINARY_PIXEL_FAST(src_row_ptr, w_limit); } else { // get 4 neighboring pixels int src_x_index_p_1 = src_x_index + 1; pixel_0 = IMAGE_GET_BINARY_PIXEL_FAST(src_row_ptr, src_x_index); pixel_1 = IMAGE_GET_BINARY_PIXEL_FAST(src_row_ptr, src_x_index_p_1); } do { // Cache the results of getting the source pixels int pixel = ((src_x_accum >> 15) & 0x1) ? pixel_1 : pixel_0; IMAGE_PUT_BINARY_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_not_done && (src_x_index == next_src_x_index)); } // 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_not_done && (src_y_index == next_src_y_index)); } // while y break; } case PIXFORMAT_GRAYSCALE: { while (y_not_done) { int src_y_index = next_src_y_index; uint8_t *src_row_ptr_0, *src_row_ptr_1; // keep row pointers in bounds if (src_y_index < h_start) { src_row_ptr_0 = src_row_ptr_1 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(src_img, h_start); } else if (src_y_index >= h_limit) { src_row_ptr_0 = src_row_ptr_1 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(src_img, h_limit); } else { // get 2 neighboring rows int src_y_index_p_1 = src_y_index + 1; src_row_ptr_0 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(src_img, src_y_index); src_row_ptr_1 = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(src_img, src_y_index_p_1); } do { // Cache the results of getting the source rows // used to mix pixels vertically long smuad_y = (src_y_accum >> 8) & 0xff; smuad_y |= (256 - smuad_y) << 16; uint8_t *dst_row_ptr = (uint8_t *) imlib_draw_row_data.row_buffer; // 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 src_x_index = next_src_x_index; int pixel_00, pixel_10, pixel_01, pixel_11; // keep pixels in bounds if (src_x_index < w_start) { pixel_00 = pixel_10 = src_row_ptr_0[w_start]; pixel_01 = pixel_11 = src_row_ptr_1[w_start]; } else if (src_x_index >= w_limit) { pixel_00 = pixel_10 = src_row_ptr_0[w_limit]; pixel_01 = pixel_11 = src_row_ptr_1[w_limit]; } else { // get 4 neighboring pixels int src_x_index_p_1 = src_x_index + 1; pixel_00 = src_row_ptr_0[src_x_index]; pixel_10 = src_row_ptr_0[src_x_index_p_1]; pixel_01 = src_row_ptr_1[src_x_index]; pixel_11 = src_row_ptr_1[src_x_index_p_1]; } long vertical_avg_0 = (pixel_00 << 16) | pixel_01; int pixel_l = __SMLAD(smuad_y, vertical_avg_0, 128) >> 8; // vertically average long vertical_avg_1 = (pixel_10 << 16) | pixel_11; int pixel_r = __SMLAD(smuad_y, vertical_avg_1, 128) >> 8; // vertically average long horizontal_avg = (pixel_l << 16) | pixel_r; do { // Cache the results of getting the source pixels // used to mix pixels horizontally long smuad_x = (src_x_accum >> 8) & 0xff; smuad_x |= (256 - smuad_x) << 16; int pixel = __SMLAD(smuad_x, horizontal_avg, 128) >> 8; // horizontally average 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_not_done && (src_x_index == next_src_x_index)); } // 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_not_done && (src_y_index == next_src_y_index)); } // while y break; } case PIXFORMAT_RGB565: { while (y_not_done) { int src_y_index = next_src_y_index; uint16_t *src_row_ptr_0, *src_row_ptr_1; // keep row pointers in bounds if (src_y_index < h_start) { src_row_ptr_0 = src_row_ptr_1 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src_img, h_start); } else if (src_y_index >= h_limit) { src_row_ptr_0 = src_row_ptr_1 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src_img, h_limit); } else { // get 2 neighboring rows int src_y_index_p_1 = src_y_index + 1; src_row_ptr_0 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src_img, src_y_index); src_row_ptr_1 = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src_img, src_y_index_p_1); } do { // Cache the results of getting the source rows // used to mix pixels vertically long smuad_y = (src_y_accum >> 11) & 0x1f; smuad_y |= (32 - smuad_y) << 16; uint16_t *dst_row_ptr = (uint16_t *) imlib_draw_row_data.row_buffer; // 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 src_x_index = next_src_x_index; int pixel_00, pixel_10, pixel_01, pixel_11; // keep pixels in bounds if (src_x_index < w_start) { pixel_00 = pixel_10 = src_row_ptr_0[w_start]; pixel_01 = pixel_11 = src_row_ptr_1[w_start]; } else if (src_x_index >= w_limit) { pixel_00 = pixel_10 = src_row_ptr_0[w_limit]; pixel_01 = pixel_11 = src_row_ptr_1[w_limit]; } else { // get 4 neighboring pixels int src_x_index_p_1 = src_x_index + 1; pixel_00 = src_row_ptr_0[src_x_index]; pixel_10 = src_row_ptr_0[src_x_index_p_1]; pixel_01 = src_row_ptr_1[src_x_index]; pixel_11 = src_row_ptr_1[src_x_index_p_1]; } const long mask_r = 0x7c007c00, mask_g = 0x07e007e0, mask_b = 0x001f001f; const long avg_rb = 0x4010, avg_g = 0x200; uint32_t rgb_l = (pixel_00 << 16) | pixel_01; long rb_l = ((rgb_l >> 1) & mask_r) | (rgb_l & mask_b); long g_l = rgb_l & mask_g; int rb_out_l = (__SMLAD(smuad_y, rb_l, avg_rb) >> 5) & 0x7c1f; int g_out_l = (__SMLAD(smuad_y, g_l, avg_g) >> 5) & 0x07e0; uint32_t rgb_r = (pixel_10 << 16) | pixel_11; long rb_r = ((rgb_r >> 1) & mask_r) | (rgb_r & mask_b); long g_r = rgb_r & mask_g; int rb_out_r = (__SMLAD(smuad_y, rb_r, avg_rb) >> 5) & 0x7c1f; int g_out_r = (__SMLAD(smuad_y, g_r, avg_g) >> 5) & 0x07e0; long rb = (rb_out_l << 16) | rb_out_r; long g = (g_out_l << 16) | g_out_r; do { // Cache the results of getting the source pixels // used to mix pixels horizontally long smuad_x = (src_x_accum >> 11) & 0x1f; smuad_x |= (32 - smuad_x) << 16; int rb_out = __SMLAD(smuad_x, rb, avg_rb) >> 5; int g_out = __SMLAD(smuad_x, g, avg_g) >> 5; int pixel = ((rb_out << 1) & 0xf800) | (g_out & 0x07e0) | (rb_out & 0x001f); 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_not_done && (src_x_index == next_src_x_index)); } // 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_not_done && (src_y_index == next_src_y_index)); } // while y break; } default: { break; } } } else if (no_scaling_nearest_neighbor) { // copy if (dst_img->data == src_img->data) { // In-Place switch (src_img->pixfmt) { case PIXFORMAT_BINARY: { while (y_not_done) { uint32_t *src_row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(src_img, next_src_y_index); uint32_t *dst_row_ptr = (uint32_t *) imlib_draw_row_data.row_buffer; // 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_BINARY_PIXEL_FAST(src_row_ptr, next_src_x_index); IMAGE_PUT_BINARY_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 PIXFORMAT_GRAYSCALE: // Re-use grayscale for bayer. case PIXFORMAT_BAYER_ANY: { while (y_not_done) { uint8_t *src_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(src_img, next_src_y_index); uint8_t *dst_row_ptr = (uint8_t *) imlib_draw_row_data.row_buffer; // 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 PIXFORMAT_RGB565: // Re-use RGB565 for yuv. case PIXFORMAT_YUV_ANY: { while (y_not_done) { uint16_t *src_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src_img, next_src_y_index); uint16_t *dst_row_ptr = (uint16_t *) imlib_draw_row_data.row_buffer; // 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; } } } else { // Out-of-Place switch (src_img->pixfmt) { case PIXFORMAT_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_data.row_buffer = 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 PIXFORMAT_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_data.row_buffer = 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 PIXFORMAT_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_data.row_buffer = 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 PIXFORMAT_BAYER_ANY: { while (y_not_done) { switch (new_not_mutable_pixfmt) { case PIXFORMAT_MUTABLE_ANY: { imlib_debayer_line(dst_x_start, dst_x_end, next_src_y_index, imlib_draw_row_data.row_buffer, new_not_mutable_pixfmt, src_img); break; } case PIXFORMAT_BAYER_ANY: { // Bayer images have the same shape as GRAYSCALE. uint8_t *src_row_ptr = IMAGE_COMPUTE_BAYER_PIXEL_ROW_PTR(src_img, next_src_y_index); imlib_draw_row_data.row_buffer = src_row_ptr; break; } default: { break; } } 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 PIXFORMAT_YUV_ANY: { while (y_not_done) { switch (new_not_mutable_pixfmt) { case PIXFORMAT_MUTABLE_ANY: { imlib_deyuv_line(dst_x_start, dst_x_end, next_src_y_index, imlib_draw_row_data.row_buffer, new_not_mutable_pixfmt, src_img); break; } case PIXFORMAT_YUV_ANY: { // YUV images have the same shape as RGB565. uint16_t *src_row_ptr = IMAGE_COMPUTE_YUV_PIXEL_ROW_PTR(src_img, next_src_y_index); imlib_draw_row_data.row_buffer = src_row_ptr; break; } default: { break; } } 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 switch (src_img->pixfmt) { case PIXFORMAT_BINARY: { while (y_not_done) { int src_y_index = next_src_y_index; uint32_t *src_row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(src_img, src_y_index); do { // Cache the results of getting the source row uint32_t *dst_row_ptr = (uint32_t *) imlib_draw_row_data.row_buffer; // 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 src_x_index = next_src_x_index; int pixel = IMAGE_GET_BINARY_PIXEL_FAST(src_row_ptr, src_x_index); do { // Cache the results of getting the source pixel IMAGE_PUT_BINARY_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_not_done && (src_x_index == next_src_x_index)); } // 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_not_done && (src_y_index == next_src_y_index)); } // while y break; } case PIXFORMAT_GRAYSCALE: // Re-use grayscale for bayer. case PIXFORMAT_BAYER_ANY: { while (y_not_done) { int src_y_index = next_src_y_index; uint8_t *src_row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(src_img, src_y_index); do { // Cache the results of getting the source row uint8_t *dst_row_ptr = (uint8_t *) imlib_draw_row_data.row_buffer; // 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 src_x_index = next_src_x_index; int pixel = IMAGE_GET_GRAYSCALE_PIXEL_FAST(src_row_ptr, src_x_index); do { // Cache the results of getting the source pixel 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_not_done && (src_x_index == next_src_x_index)); } // 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_not_done && (src_y_index == next_src_y_index)); } // while y break; } case PIXFORMAT_RGB565: // Re-use RGB565 for yuv. case PIXFORMAT_YUV_ANY: { while (y_not_done) { int src_y_index = next_src_y_index; uint16_t *src_row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(src_img, src_y_index); do { // Cache the results of getting the source row uint16_t *dst_row_ptr = (uint16_t *) imlib_draw_row_data.row_buffer; // 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 src_x_index = next_src_x_index; int pixel = IMAGE_GET_RGB565_PIXEL_FAST(src_row_ptr, src_x_index); do { // Cache the results of getting the source pixel 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_not_done && (src_x_index == next_src_x_index)); } // 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_not_done && (src_y_index == next_src_y_index)); } // while y break; } default: { break; } } } imlib_draw_row_teardown(&imlib_draw_row_data); exit_cleanup: if (&new_src_img == src_img) { fb_free(); } OMV_PROFILE_PRINT(); } #ifdef IMLIB_ENABLE_FLOOD_FILL void imlib_flood_fill(image_t *img, int x, int y, float seed_threshold, float floating_threshold, int c, bool invert, bool clear_background, image_t *mask) { if ((0 <= x) && (x < img->w) && (0 <= y) && (y < img->h)) { image_t out; out.w = img->w; out.h = img->h; out.pixfmt = PIXFORMAT_BINARY; out.data = fb_alloc0(image_size(&out), FB_ALLOC_NO_HINT); if (mask) { for (int y = 0, yy = out.h; y < yy; y++) { uint32_t *row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(&out, y); for (int x = 0, xx = out.w; x < xx; x++) { if (image_get_mask_pixel(mask, x, y)) { IMAGE_SET_BINARY_PIXEL_FAST(row_ptr, x); } } } } int color_seed_threshold = 0; int color_floating_threshold = 0; switch (img->pixfmt) { case PIXFORMAT_BINARY: { color_seed_threshold = fast_floorf(seed_threshold * COLOR_BINARY_MAX); color_floating_threshold = fast_floorf(floating_threshold * COLOR_BINARY_MAX); break; } case PIXFORMAT_GRAYSCALE: { color_seed_threshold = fast_floorf(seed_threshold * COLOR_GRAYSCALE_MAX); color_floating_threshold = fast_floorf(floating_threshold * COLOR_GRAYSCALE_MAX); break; } case PIXFORMAT_RGB565: { color_seed_threshold = COLOR_R5_G6_B5_TO_RGB565(fast_floorf(seed_threshold * COLOR_R5_MAX), fast_floorf(seed_threshold * COLOR_G6_MAX), fast_floorf(seed_threshold * COLOR_B5_MAX)); color_floating_threshold = COLOR_R5_G6_B5_TO_RGB565(fast_floorf(floating_threshold * COLOR_R5_MAX), fast_floorf(floating_threshold * COLOR_G6_MAX), fast_floorf(floating_threshold * COLOR_B5_MAX)); break; } default: { break; } } imlib_flood_fill_int(&out, img, x, y, color_seed_threshold, color_floating_threshold, NULL, NULL); switch (img->pixfmt) { case PIXFORMAT_BINARY: { for (int y = 0, yy = out.h; y < yy; y++) { uint32_t *row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(img, y); uint32_t *out_row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(&out, y); for (int x = 0, xx = out.w; x < xx; x++) { if (IMAGE_GET_BINARY_PIXEL_FAST(out_row_ptr, x) ^ invert) { IMAGE_PUT_BINARY_PIXEL_FAST(row_ptr, x, c); } else if (clear_background) { IMAGE_PUT_BINARY_PIXEL_FAST(row_ptr, x, 0); } } } break; } case PIXFORMAT_GRAYSCALE: { for (int y = 0, yy = out.h; y < yy; y++) { uint8_t *row_ptr = IMAGE_COMPUTE_GRAYSCALE_PIXEL_ROW_PTR(img, y); uint32_t *out_row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(&out, y); for (int x = 0, xx = out.w; x < xx; x++) { if (IMAGE_GET_BINARY_PIXEL_FAST(out_row_ptr, x) ^ invert) { IMAGE_PUT_GRAYSCALE_PIXEL_FAST(row_ptr, x, c); } else if (clear_background) { IMAGE_PUT_GRAYSCALE_PIXEL_FAST(row_ptr, x, 0); } } } break; } case PIXFORMAT_RGB565: { for (int y = 0, yy = out.h; y < yy; y++) { uint16_t *row_ptr = IMAGE_COMPUTE_RGB565_PIXEL_ROW_PTR(img, y); uint32_t *out_row_ptr = IMAGE_COMPUTE_BINARY_PIXEL_ROW_PTR(&out, y); for (int x = 0, xx = out.w; x < xx; x++) { if (IMAGE_GET_BINARY_PIXEL_FAST(out_row_ptr, x) ^ invert) { IMAGE_PUT_RGB565_PIXEL_FAST(row_ptr, x, c); } else if (clear_background) { IMAGE_PUT_RGB565_PIXEL_FAST(row_ptr, x, 0); } } } break; } default: { break; } } fb_free(); } } #endif // IMLIB_ENABLE_FLOOD_FILL