/** * @file lv_draw_sw_gradient.c * */ /********************* * INCLUDES *********************/ #include "lv_draw_sw_gradient.h" #if LV_USE_DRAW_SW #include "../../misc/lv_types.h" #include "../../osal/lv_os.h" /********************* * DEFINES *********************/ #define GRAD_CM(r,g,b) lv_color_make(r,g,b) #define GRAD_CONV(t, x) t = x #undef ALIGN #if defined(LV_ARCH_64) #define ALIGN(X) (((X) + 7) & ~7) #else #define ALIGN(X) (((X) + 3) & ~3) #endif /********************** * STATIC PROTOTYPES **********************/ typedef lv_result_t (*op_cache_t)(lv_grad_t * c, void * ctx); static lv_grad_t * allocate_item(const lv_grad_dsc_t * g, int32_t w, int32_t h); /********************** * STATIC VARIABLE **********************/ /********************** * STATIC FUNCTIONS **********************/ static lv_grad_t * allocate_item(const lv_grad_dsc_t * g, int32_t w, int32_t h) { int32_t size = g->dir == LV_GRAD_DIR_HOR ? w : h; size_t req_size = ALIGN(sizeof(lv_grad_t)) + ALIGN(size * sizeof(lv_color_t)) + ALIGN(size * sizeof(lv_opa_t)); lv_grad_t * item = lv_malloc(req_size); LV_ASSERT_MALLOC(item); if(item == NULL) return NULL; uint8_t * p = (uint8_t *)item; item->color_map = (lv_color_t *)(p + ALIGN(sizeof(*item))); item->opa_map = (lv_opa_t *)(p + ALIGN(sizeof(*item)) + ALIGN(size * sizeof(lv_color_t))); item->size = size; return item; } /********************** * FUNCTIONS **********************/ lv_grad_t * lv_gradient_get(const lv_grad_dsc_t * g, int32_t w, int32_t h) { /* No gradient, no cache */ if(g->dir == LV_GRAD_DIR_NONE) return NULL; /* Step 1: Search cache for the given key */ lv_grad_t * item = allocate_item(g, w, h); if(item == NULL) { LV_LOG_WARN("Failed to allocate item for the gradient"); return item; } /* Step 3: Fill it with the gradient, as expected */ uint32_t i; for(i = 0; i < item->size; i++) { lv_gradient_color_calculate(g, item->size, i, &item->color_map[i], &item->opa_map[i]); } return item; } void LV_ATTRIBUTE_FAST_MEM lv_gradient_color_calculate(const lv_grad_dsc_t * dsc, int32_t range, int32_t frac, lv_grad_color_t * color_out, lv_opa_t * opa_out) { lv_grad_color_t tmp; /*Clip out-of-bounds first*/ int32_t min = (dsc->stops[0].frac * range) >> 8; if(frac <= min) { GRAD_CONV(tmp, dsc->stops[0].color); *color_out = tmp; *opa_out = dsc->stops[0].opa; return; } int32_t max = (dsc->stops[dsc->stops_count - 1].frac * range) >> 8; if(frac >= max) { GRAD_CONV(tmp, dsc->stops[dsc->stops_count - 1].color); *color_out = tmp; *opa_out = dsc->stops[dsc->stops_count - 1].opa; return; } /*Find the 2 closest stop now*/ int32_t d = 0; int32_t found_i = 0; for(uint8_t i = 1; i < dsc->stops_count; i++) { int32_t cur = (dsc->stops[i].frac * range) >> 8; if(frac <= cur) { found_i = i; break; } } LV_ASSERT(found_i != 0); lv_color_t one, two; one = dsc->stops[found_i - 1].color; two = dsc->stops[found_i].color; min = (dsc->stops[found_i - 1].frac * range) >> 8; max = (dsc->stops[found_i].frac * range) >> 8; d = max - min; /*Then interpolate*/ frac -= min; lv_opa_t mix = (frac * 255) / d; lv_opa_t imix = 255 - mix; *color_out = GRAD_CM(LV_UDIV255(two.red * mix + one.red * imix), LV_UDIV255(two.green * mix + one.green * imix), LV_UDIV255(two.blue * mix + one.blue * imix)); *opa_out = LV_UDIV255(dsc->stops[found_i].opa * mix + dsc->stops[found_i - 1].opa * imix); } void lv_gradient_cleanup(lv_grad_t * grad) { lv_free(grad); } #endif /*LV_USE_DRAW_SW*/