c-在从循环调用的函数中删除开关情况



我有一个名为blend_pixels()的函数,其任务是根据指定的混合模式将单个像素混合到另一个像素上。几乎所有想要绘制任何内容的函数都会调用该函数。

问题是,函数是为每个像素调用的,这意味着它每秒被调用数千万次,并且它包含一个切换用例语句,该语句遍历所有可能的混合模式,直到找到正确的混合模式。

显然,这比调用直接执行所需操作的函数要慢一些,这就是我试图解决的问题。调用blend_pixels()的父函数通常只传递它们自己在调用后作为参数接收的混合模式,所以我不能让它们只调用一个只执行一种混合模式的小函数。但是,对于父函数的每次调用,只需要进行一次选择(父函数每次调用对许多像素进行操作,而在遍历所有必要像素的循环中,对每个像素调用blend_pixels())。

功能如下:

void blend_pixels(lrgb_t *bg, lrgb_t fg, int32_t p, const int mode)
{
    int32_t r, g, b;
    switch (mode)
    {
        case SOLID:
            *bg = fg;
            break;
        case ADD:
            r = (fg.r * p >> 15) + bg->r;   if (r>ONE) bg->r = ONE; else bg->r = r;
            g = (fg.g * p >> 15) + bg->g;   if (g>ONE) bg->g = ONE; else bg->g = g;
            b = (fg.b * p >> 15) + bg->b;   if (b>ONE) bg->b = ONE; else bg->b = b;
            break;
        case SUB:
            r = -(fg.r * p >> 15) + bg->r;  if (r<0) bg->r = 0; else bg->r = r;
            g = -(fg.g * p >> 15) + bg->g;  if (g<0) bg->g = 0; else bg->g = g;
            b = -(fg.b * p >> 15) + bg->b;  if (b<0) bg->b = 0; else bg->b = b;
            break;
        case MUL:
            ... // you get the idea
    }
}

并以这种方式被称为:

void parent_function(lrgb_t *fb, int w, int h, lrgb_t colour, ... int blendingmode)
{
    ...
    for (iy=y0; iy<y1; iy++)
        for (ix=x0; ix<x1; ix++)
        {
            p = some_weighting_formula();
            blend_pixels(&fb[iy*w+ix], colour, p, blendingmode);
        }
}

其本身可能被称为:

parent_function(fb, w, h, orange, ... /*whatever*/, ADD);

"ADD"是枚举中的整数

所以很明显,选择混合算法的任何切换情况都应该在parent_function的循环之外进行。但是怎么做呢?

您可以使用函数指针来完成此操作。

首先为函数指针定义一个typedef:

typedef void (*blend_function)(lrgb_t *, lrgb_t, int32_t);

然后将blend_pixels的每个部分分解为自己的函数,每个函数都有相同的参数和返回类型作为typedef:

void blend_pixels_add(lrgb_t *bg, lrgb_t fg, int32_t p)
...
void blend_pixels_sub(lrgb_t *bg, lrgb_t fg, int32_t p)
...
void blend_pixels_mult(lrgb_t *bg, lrgb_t fg, int32_t p)
...

然后在您的父函数中,您可以分配函数指针类型的变量,并为其分配您想要使用的函数的地址:

void parent_function(lrgb_t *fb, int w, int h, lrgb_t colour, ... int blendingmode)
{
    ...
    blend_function blend;
    switch (blendingmode)
    {
        case ADD:
            blend = blend_pixels_add;
            break;
        case SUB:
            blend = blend_pixels_sub;
            break;
        ...
    }
    for (iy=y0; iy<y1; iy++)
        for (ix=x0; ix<x1; ix++)
        {
            p = some_weighting_formula();
            blend(&fb[iy*w+ix], colour, p);
        }
}

解决您的担忧"并且它包含一个switch-case语句,它将遍历所有可能的混合模式,直到找到正确的混合模式。",这可能不是真正发生的事情。

Switch语句通常被编译成所谓的跳转表。在跳转表中,代码不会遍历所有情况以查找正确的情况,而是将switch()语句的参数用作地址数组中的索引。类似于:

jump_table[SOLID] -> case SOLID address
jump_table[ADD] -> case ADD address
...

因此,在这种实现中,考虑许多值的switch语句应该和手工编码的函数指针解决方案一样快,因为这本质上是编译器构建的。

最新更新