void GaussianXBlurOperation::executePixel(float output[4], int x, int y, void *data)
{
	float color_accum[4] = {0.0f, 0.0f, 0.0f, 0.0f};
	float multiplier_accum = 0.0f;
	MemoryBuffer *inputBuffer = (MemoryBuffer *)data;
	float *buffer = inputBuffer->getBuffer();
	int bufferwidth = inputBuffer->getWidth();
	int bufferstartx = inputBuffer->getRect()->xmin;
	int bufferstarty = inputBuffer->getRect()->ymin;

	int miny = y;
	int minx = x - this->m_rad;
	int maxx = x + this->m_rad;
	miny = max(miny, inputBuffer->getRect()->ymin);
	minx = max(minx, inputBuffer->getRect()->xmin);
	maxx = min(maxx, inputBuffer->getRect()->xmax - 1);

	int step = getStep();
	int offsetadd = getOffsetAdd();
	int bufferindex = ((minx - bufferstartx) * 4) + ((miny - bufferstarty) * 4 * bufferwidth);
	for (int nx = minx, index = (minx - x) + this->m_rad; nx <= maxx; nx += step, index += step) {
		const float multiplier = this->m_gausstab[index];
		madd_v4_v4fl(color_accum, &buffer[bufferindex], multiplier);
		multiplier_accum += multiplier;
		bufferindex += offsetadd;
	}
	mul_v4_v4fl(output, color_accum, 1.0f / multiplier_accum);
}
void GaussianYBlurOperation::executePixel(float output[4], int x, int y, void *data)
{
	float color_accum[4] = {0.0f, 0.0f, 0.0f, 0.0f};
	float multiplier_accum = 0.0f;
	MemoryBuffer *inputBuffer = (MemoryBuffer *)data;
	float *buffer = inputBuffer->getBuffer();
	int bufferwidth = inputBuffer->getWidth();
	int bufferstartx = inputBuffer->getRect()->xmin;
	int bufferstarty = inputBuffer->getRect()->ymin;

	rcti &rect = *inputBuffer->getRect();
	int xmin = max_ii(x,                    rect.xmin);
	int ymin = max_ii(y - m_filtersize,     rect.ymin);
	int ymax = min_ii(y + m_filtersize + 1, rect.ymax);

	int index;
	int step = getStep();
	const int bufferIndexx = ((xmin - bufferstartx) * 4);
	for (int ny = ymin; ny < ymax; ny += step) {
		index = (ny - y) + this->m_filtersize;
		int bufferindex = bufferIndexx + ((ny - bufferstarty) * 4 * bufferwidth);
		const float multiplier = this->m_gausstab[index];
		madd_v4_v4fl(color_accum, &buffer[bufferindex], multiplier);
		multiplier_accum += multiplier;
	}
	mul_v4_v4fl(output, color_accum, 1.0f / multiplier_accum);
}
void GaussianXBlurOperation::executePixel(float output[4], int x, int y, void *data)
{
	float color_accum[4] = {0.0f, 0.0f, 0.0f, 0.0f};
	float multiplier_accum = 0.0f;
	MemoryBuffer *inputBuffer = (MemoryBuffer *)data;
	float *buffer = inputBuffer->getBuffer();
	int bufferwidth = inputBuffer->getWidth();
	int bufferstartx = inputBuffer->getRect()->xmin;
	int bufferstarty = inputBuffer->getRect()->ymin;

	rcti &rect = *inputBuffer->getRect();
	int xmin = max_ii(x - m_filtersize,     rect.xmin);
	int xmax = min_ii(x + m_filtersize + 1, rect.xmax);
	int ymin = max_ii(y,                    rect.ymin);

	int step = getStep();
	int offsetadd = getOffsetAdd();
	int bufferindex = ((xmin - bufferstartx) * 4) + ((ymin - bufferstarty) * 4 * bufferwidth);

#ifdef __SSE2__
	__m128 accum_r = _mm_load_ps(color_accum);
	for (int nx = xmin, index = (xmin - x) + this->m_filtersize; nx < xmax; nx += step, index += step) {
		__m128 reg_a = _mm_load_ps(&buffer[bufferindex]);
		reg_a = _mm_mul_ps(reg_a, this->m_gausstab_sse[index]);
		accum_r = _mm_add_ps(accum_r, reg_a);
		multiplier_accum += this->m_gausstab[index];
		bufferindex += offsetadd;
	}
	_mm_store_ps(color_accum, accum_r);
#else
	for (int nx = xmin, index = (xmin - x) + this->m_filtersize; nx < xmax; nx += step, index += step) {
		const float multiplier = this->m_gausstab[index];
		madd_v4_v4fl(color_accum, &buffer[bufferindex], multiplier);
		multiplier_accum += multiplier;
		bufferindex += offsetadd;
	}
#endif
	mul_v4_v4fl(output, color_accum, 1.0f / multiplier_accum);
}
Example #4
0
static void colorband_init_from_table_rgba_resample(
        ColorBand *coba,
        const float (*array)[4], const int array_len,
        bool filter_samples)
{
	BLI_assert(array_len >= 2);
	const float eps_2x = ((1.0f / 255.0f) + 1e-6f);
	struct ColorResampleElem *c, *carr = MEM_mallocN(sizeof(*carr) * array_len, __func__);
	int carr_len = array_len;
	c = carr;
	{
		const float step_size = 1.0f / (float)(array_len - 1);
		for (int i = 0; i < array_len; i++, c++) {
			copy_v4_v4(carr[i].rgba, array[i]);
			c->next = c + 1;
			c->prev = c - 1;
			c->pos = i * step_size;
		}
	}
	carr[0].prev = NULL;
	carr[array_len - 1].next = NULL;

	/* -2 to remove endpoints. */
	Heap *heap = BLI_heap_new_ex(array_len - 2);
	c = carr;
	for (int i = 0; i < array_len; i++, c++) {
		float cost = color_sample_remove_cost(c);
		if (cost != -1.0f) {
			c->node = BLI_heap_insert(heap, cost, c);
		}
		else {
			c->node = NULL;
		}
	}

	while ((carr_len > 1 && !BLI_heap_is_empty(heap)) &&
	       ((carr_len >= MAXCOLORBAND) || (BLI_heap_node_value(BLI_heap_top(heap)) <= eps_2x)))
	{
		c = BLI_heap_pop_min(heap);
		struct ColorResampleElem *c_next = c->next, *c_prev = c->prev;
		c_prev->next = c_next;
		c_next->prev = c_prev;
		/* Clear data (not essential, avoid confusion). */
		c->prev = c->next = NULL;
		c->node = NULL;

		/* Update adjacent */
		for (int i = 0; i < 2; i++) {
			struct ColorResampleElem *c_other = i ? c_next : c_prev;
			if (c_other->node != NULL) {
				const float cost = color_sample_remove_cost(c_other);
				if (cost != -1.0) {
					BLI_heap_node_value_update(heap, c_other->node, cost);
				}
				else {
					BLI_heap_remove(heap, c_other->node);
					c_other->node = NULL;
				}
			}
		}
		carr_len -= 1;
	}
	BLI_heap_free(heap, NULL);

	/* First member is never removed. */
	int i = 0;
	BLI_assert(carr_len < MAXCOLORBAND);
	if (filter_samples == false) {
		for (c = carr; c != NULL; c = c->next, i++) {
			copy_v4_v4(&coba->data[i].r, c->rgba);
			coba->data[i].pos = c->pos;
			coba->data[i].cur = i;
		}
	}
	else {
		for (c = carr; c != NULL; c = c->next, i++) {
			const int steps_prev = c->prev ? (c - c->prev) - 1 : 0;
			const int steps_next = c->next ? (c->next - c) - 1 : 0;
			if (steps_prev == 0 && steps_next == 0) {
				copy_v4_v4(&coba->data[i].r, c->rgba);
			}
			else {
				float rgba[4];
				float rgba_accum = 1;
				copy_v4_v4(rgba, c->rgba);

				if (steps_prev) {
					const float step_size = 1.0 / (float)(steps_prev + 1);
					int j = steps_prev;
					for (struct ColorResampleElem *c_other = c - 1; c_other != c->prev; c_other--, j--) {
						const float step_pos = (float)j * step_size;
						BLI_assert(step_pos > 0.0f && step_pos < 1.0f);
						const float f = filter_gauss(step_pos);
						madd_v4_v4fl(rgba, c_other->rgba, f);
						rgba_accum += f;
					}
				}
				if (steps_next) {
					const float step_size = 1.0 / (float)(steps_next + 1);
					int j = steps_next;
					for (struct ColorResampleElem *c_other = c + 1; c_other != c->next; c_other++, j--) {
						const float step_pos = (float)j * step_size;
						BLI_assert(step_pos > 0.0f && step_pos < 1.0f);
						const float f = filter_gauss(step_pos);
						madd_v4_v4fl(rgba, c_other->rgba, f);
						rgba_accum += f;
					}
				}

				mul_v4_v4fl(&coba->data[i].r, rgba, 1.0f / rgba_accum);
			}
			coba->data[i].pos = c->pos;
			coba->data[i].cur = i;
		}
	}
	BLI_assert(i == carr_len);
	coba->tot = i;
	coba->cur = 0;

	MEM_freeN(carr);
}
Example #5
0
void IMB_rectblend(ImBuf *dbuf, ImBuf *obuf, ImBuf *sbuf, unsigned short *dmask, unsigned short *curvemask,
                   unsigned short *texmask, float mask_max,
                   int destx,  int desty, int origx, int origy, int srcx, int srcy, int width, int height,
                   IMB_BlendMode mode, bool accumulate)
{
	unsigned int *drect = NULL, *orect = NULL, *srect = NULL, *dr, *or, *sr;
	float *drectf = NULL, *orectf = NULL, *srectf = NULL, *drf, *orf, *srf;
	unsigned short *cmaskrect = curvemask, *cmr;
	unsigned short *dmaskrect = dmask, *dmr;
	unsigned short *texmaskrect = texmask, *tmr;
	int do_float, do_char, srcskip, destskip, origskip, x;
	IMB_blend_func func = NULL;
	IMB_blend_func_float func_float = NULL;

	if (dbuf == NULL || obuf == NULL) return;

	imb_rectclip3(dbuf, obuf, sbuf, &destx, &desty, &origx, &origy, &srcx, &srcy, &width, &height);

	if (width == 0 || height == 0) return;
	if (sbuf && sbuf->channels != 4) return;
	if (dbuf->channels != 4) return;

	do_char = (sbuf && sbuf->rect && dbuf->rect && obuf->rect);
	do_float = (sbuf && sbuf->rect_float && dbuf->rect_float && obuf->rect_float);

	if (do_char) {
		drect = dbuf->rect + ((size_t)desty) * dbuf->x + destx;
		orect = obuf->rect + ((size_t)origy) * obuf->x + origx;
	}
	if (do_float) {
		drectf = dbuf->rect_float + (((size_t)desty) * dbuf->x + destx) * 4;
		orectf = obuf->rect_float + (((size_t)origy) * obuf->x + origx) * 4;
	}

	if (dmaskrect)
		dmaskrect += ((size_t)origy) * obuf->x + origx;

	destskip = dbuf->x;
	origskip = obuf->x;

	if (sbuf) {
		if (do_char) srect = sbuf->rect + ((size_t)srcy) * sbuf->x + srcx;
		if (do_float) srectf = sbuf->rect_float + (((size_t)srcy) * sbuf->x + srcx) * 4;
		srcskip = sbuf->x;

		if (cmaskrect)
			cmaskrect += ((size_t)srcy) * sbuf->x + srcx;

		if (texmaskrect)
			texmaskrect += ((size_t)srcy) * sbuf->x + srcx;
	}
	else {
		srect = drect;
		srectf = drectf;
		srcskip = destskip;
	}

	if (mode == IMB_BLEND_COPY) {
		/* copy */
		for (; height > 0; height--) {
			if (do_char) {
				memcpy(drect, srect, width * sizeof(int));
				drect += destskip;
				srect += srcskip;
			}

			if (do_float) {
				memcpy(drectf, srectf, width * sizeof(float) * 4);
				drectf += destskip * 4;
				srectf += srcskip * 4;
			}
		}
	}
	else if (mode == IMB_BLEND_COPY_RGB) {
		/* copy rgb only */
		for (; height > 0; height--) {
			if (do_char) {
				dr = drect;
				sr = srect;
				for (x = width; x > 0; x--, dr++, sr++) {
					((char *)dr)[0] = ((char *)sr)[0];
					((char *)dr)[1] = ((char *)sr)[1];
					((char *)dr)[2] = ((char *)sr)[2];
				}
				drect += destskip;
				srect += srcskip;
			}

			if (do_float) {
				drf = drectf;
				srf = srectf;
				for (x = width; x > 0; x--, drf += 4, srf += 4) {
					float map_alpha = (srf[3] == 0.0f) ? drf[3] : drf[3] / srf[3];

					drf[0] = srf[0] * map_alpha;
					drf[1] = srf[1] * map_alpha;
					drf[2] = srf[2] * map_alpha;
				}
				drectf += destskip * 4;
				srectf += srcskip * 4;
			}
		}
	}
	else if (mode == IMB_BLEND_COPY_ALPHA) {
		/* copy alpha only */
		for (; height > 0; height--) {
			if (do_char) {
				dr = drect;
				sr = srect;
				for (x = width; x > 0; x--, dr++, sr++)
					((char *)dr)[3] = ((char *)sr)[3];
				drect += destskip;
				srect += srcskip;
			}

			if (do_float) {
				drf = drectf;
				srf = srectf;
				for (x = width; x > 0; x--, drf += 4, srf += 4)
					drf[3] = srf[3];
				drectf += destskip * 4;
				srectf += srcskip * 4;
			}
		}
	}
	else {
		switch (mode) {
			case IMB_BLEND_MIX:
			case IMB_BLEND_INTERPOLATE:
				func = blend_color_mix_byte;
				func_float = blend_color_mix_float;
				break;
			case IMB_BLEND_ADD:
				func = blend_color_add_byte;
				func_float = blend_color_add_float;
				break;
			case IMB_BLEND_SUB:
				func = blend_color_sub_byte;
				func_float = blend_color_sub_float;
				break;
			case IMB_BLEND_MUL:
				func = blend_color_mul_byte;
				func_float = blend_color_mul_float;
				break;
			case IMB_BLEND_LIGHTEN:
				func = blend_color_lighten_byte;
				func_float = blend_color_lighten_float;
				break;
			case IMB_BLEND_DARKEN:
				func = blend_color_darken_byte;
				func_float = blend_color_darken_float;
				break;
			case IMB_BLEND_ERASE_ALPHA:
				func = blend_color_erase_alpha_byte;
				func_float = blend_color_erase_alpha_float;
				break;
			case IMB_BLEND_ADD_ALPHA:
				func = blend_color_add_alpha_byte;
				func_float = blend_color_add_alpha_float;
				break;
			case IMB_BLEND_OVERLAY:
				func = blend_color_overlay_byte;
				func_float = blend_color_overlay_float;
				break;
			case IMB_BLEND_HARDLIGHT:
				func = blend_color_hardlight_byte;
				func_float = blend_color_hardlight_float;
				break;
			case IMB_BLEND_COLORBURN:
				func = blend_color_burn_byte;
				func_float = blend_color_burn_float;
				break;
			case IMB_BLEND_LINEARBURN:
				func = blend_color_linearburn_byte;
				func_float = blend_color_linearburn_float;
				break;
			case IMB_BLEND_COLORDODGE:
				func = blend_color_dodge_byte;
				func_float = blend_color_dodge_float;
				break;
			case IMB_BLEND_SCREEN:
				func = blend_color_screen_byte;
				func_float = blend_color_screen_float;
				break;
			case IMB_BLEND_SOFTLIGHT:
				func = blend_color_softlight_byte;
				func_float = blend_color_softlight_float;
				break;
			case IMB_BLEND_PINLIGHT:
				func = blend_color_pinlight_byte;
				func_float = blend_color_pinlight_float;
				break;
			case IMB_BLEND_LINEARLIGHT:
				func = blend_color_linearlight_byte;
				func_float = blend_color_linearlight_float;
				break;
			case IMB_BLEND_VIVIDLIGHT:
				func = blend_color_vividlight_byte;
				func_float = blend_color_vividlight_float;
				break;
			case IMB_BLEND_DIFFERENCE:
				func = blend_color_difference_byte;
				func_float = blend_color_difference_float;
				break;
			case IMB_BLEND_EXCLUSION:
				func = blend_color_exclusion_byte;
				func_float = blend_color_exclusion_float;
				break;
			case IMB_BLEND_COLOR:
				func = blend_color_color_byte;
				func_float = blend_color_color_float;
				break;
			case IMB_BLEND_HUE:
				func = blend_color_hue_byte;
				func_float = blend_color_hue_float;
				break;
			case IMB_BLEND_SATURATION:
				func = blend_color_saturation_byte;
				func_float = blend_color_saturation_float;
				break;
			case IMB_BLEND_LUMINOSITY:
				func = blend_color_luminosity_byte;
				func_float = blend_color_luminosity_float;
				break;
			default:
				break;
		}

		/* blend */
		for (; height > 0; height--) {
			if (do_char) {
				dr = drect;
				or = orect;
				sr = srect;

				if (cmaskrect) {
					/* mask accumulation for painting */
					cmr = cmaskrect;
					tmr = texmaskrect;

					/* destination mask present, do max alpha masking */
					if (dmaskrect) {
						dmr = dmaskrect;
						for (x = width; x > 0; x--, dr++, or++, sr++, dmr++, cmr++) {
							unsigned char *src = (unsigned char *)sr;
							float mask_lim = mask_max * (*cmr);

							if (texmaskrect)
								mask_lim *= ((*tmr++) / 65535.0f);

							if (src[3] && mask_lim) {
								float mask;

								if (accumulate)
									mask = *dmr + mask_lim;
								else
									mask = *dmr + mask_lim - (*dmr  * (*cmr / 65535.0f));

								mask = min_ff(mask, 65535.0);

								if (mask > *dmr) {
									unsigned char mask_src[4];

									*dmr = mask;

									mask_src[0] = src[0];
									mask_src[1] = src[1];
									mask_src[2] = src[2];

									if (mode == IMB_BLEND_INTERPOLATE) {
										mask_src[3] = src[3];
										blend_color_interpolate_byte((unsigned char *)dr, (unsigned char *)or, mask_src, mask / 65535.0f);
									}
									else {
										mask_src[3] = divide_round_i(src[3] * mask, 65535);
										func((unsigned char *)dr, (unsigned char *)or, mask_src);
									}
								}
							}
						}
						dmaskrect += origskip;
					}
					/* no destination mask buffer, do regular blend with masktexture if present */
					else {
						for (x = width; x > 0; x--, dr++, or++, sr++, cmr++) {
							unsigned char *src = (unsigned char *)sr;
							float mask = (float)mask_max * ((float)(*cmr));

							if (texmaskrect)
								mask *= ((float)(*tmr++) / 65535.0f);

							mask = min_ff(mask, 65535.0);

							if (src[3] && (mask > 0.0f)) {
								unsigned char mask_src[4];

								mask_src[0] = src[0];
								mask_src[1] = src[1];
								mask_src[2] = src[2];

								if (mode == IMB_BLEND_INTERPOLATE) {
									mask_src[3] = src[3];
									blend_color_interpolate_byte((unsigned char *)dr, (unsigned char *)or, mask_src, mask / 65535.0f);
								}
								else {
									mask_src[3] = divide_round_i(src[3] * mask, 65535);
									func((unsigned char *)dr, (unsigned char *)or, mask_src);
								}
							}
						}
					}

					cmaskrect += srcskip;
					if (texmaskrect)
						texmaskrect += srcskip;
				}
				else {
					/* regular blending */
					for (x = width; x > 0; x--, dr++, or++, sr++) {
						if (((unsigned char *)sr)[3])
							func((unsigned char *)dr, (unsigned char *)or, (unsigned char *)sr);
					}
				}

				drect += destskip;
				orect += origskip;
				srect += srcskip;
			}

			if (do_float) {
				drf = drectf;
				orf = orectf;
				srf = srectf;

				if (cmaskrect) {
					/* mask accumulation for painting */
					cmr = cmaskrect;
					tmr = texmaskrect;

					/* destination mask present, do max alpha masking */
					if (dmaskrect) {
						dmr = dmaskrect;
						for (x = width; x > 0; x--, drf += 4, orf += 4, srf += 4, dmr++, cmr++) {
							float mask_lim = mask_max * (*cmr);

							if (texmaskrect)
								mask_lim *= ((*tmr++) / 65535.0f);

							if (srf[3] && mask_lim) {
								float mask;

								if (accumulate)
									mask = min_ff(*dmr + mask_lim, 65535.0);
								else
									mask = *dmr + mask_lim - (*dmr  * (*cmr / 65535.0f));

								mask = min_ff(mask, 65535.0);

								if (mask > *dmr) {
									*dmr = mask;

									if (mode == IMB_BLEND_INTERPOLATE) {
										blend_color_interpolate_float(drf, orf, srf, mask / 65535.0f);
									}
									else {
										float mask_srf[4];
										mul_v4_v4fl(mask_srf, srf, mask / 65535.0f);
										func_float(drf, orf, mask_srf);
									}
								}
							}
						}
						dmaskrect += origskip;
					}
					/* no destination mask buffer, do regular blend with masktexture if present */
					else {
						for (x = width; x > 0; x--, drf += 4, orf += 4, srf += 4, cmr++) {
							float mask = (float)mask_max * ((float)(*cmr));

							if (texmaskrect)
								mask *= ((float)(*tmr++) / 65535.0f);

							mask = min_ff(mask, 65535.0);

							if (srf[3] && (mask > 0.0f)) {
								if (mode == IMB_BLEND_INTERPOLATE) {
									blend_color_interpolate_float(drf, orf, srf, mask / 65535.0f);
								}
								else {
									float mask_srf[4];
									mul_v4_v4fl(mask_srf, srf, mask / 65535.0f);
									func_float(drf, orf, mask_srf);
								}

							}
						}
					}

					cmaskrect += srcskip;
					if (texmaskrect)
						texmaskrect += srcskip;
				}
				else {
					/* regular blending */
					for (x = width; x > 0; x--, drf += 4, orf += 4, srf += 4) {
						if (srf[3] != 0)
							func_float(drf, orf, srf);
					}
				}

				drectf += destskip * 4;
				orectf += origskip * 4;
				srectf += srcskip * 4;
			}
		}
	}
}
Example #6
0
void IMB_rectblend(ImBuf *dbuf, ImBuf *obuf, ImBuf *sbuf, unsigned short *dmask,
                   unsigned short *smask, unsigned short mask_max,
                   int destx,  int desty, int origx, int origy, int srcx, int srcy, int width, int height,
                   IMB_BlendMode mode, short lock_alpha)
{
	unsigned int *drect = NULL, *orect, *srect = NULL, *dr, *or, *sr;
	float *drectf = NULL, *orectf, *srectf = NULL, *drf, *orf, *srf;
	unsigned short *smaskrect = smask, *smr;
	unsigned short *dmaskrect = dmask, *dmr;
	int do_float, do_char, srcskip, destskip, origskip, x;
	IMB_blend_func func = NULL;
	IMB_blend_func_float func_float = NULL;

	if (dbuf == NULL || obuf == NULL) return;

	imb_rectclip3(dbuf, obuf, sbuf, &destx, &desty, &origx, &origy, &srcx, &srcy, &width, &height);

	if (width == 0 || height == 0) return;
	if (sbuf && sbuf->channels != 4) return;
	if (dbuf->channels != 4) return;

	do_char = (sbuf && sbuf->rect && dbuf->rect && obuf->rect);
	do_float = (sbuf && sbuf->rect_float && dbuf->rect_float && obuf->rect_float);

	if (do_char) {
		drect = dbuf->rect + desty * dbuf->x + destx;
		orect = obuf->rect + origy * obuf->x + origx;
	}
	if (do_float) {
		drectf = dbuf->rect_float + (desty * dbuf->x + destx) * 4;
		orectf = obuf->rect_float + (origy * obuf->x + origx) * 4;
	}

	if (dmaskrect)
		dmaskrect += origy * obuf->x + origx;

	destskip = dbuf->x;
	origskip = obuf->x;

	if (sbuf) {
		if (do_char) srect = sbuf->rect + srcy * sbuf->x + srcx;
		if (do_float) srectf = sbuf->rect_float + (srcy * sbuf->x + srcx) * 4;
		srcskip = sbuf->x;

		if (smaskrect)
			smaskrect += srcy * sbuf->x + srcx;
	}
	else {
		srect = drect;
		srectf = drectf;
		srcskip = destskip;
	}

	if (mode == IMB_BLEND_COPY) {
		/* copy */
		for (; height > 0; height--) {
			if (do_char) {
				memcpy(drect, srect, width * sizeof(int));
				drect += destskip;
				srect += srcskip;
			}

			if (do_float) {
				memcpy(drectf, srectf, width * sizeof(float) * 4);
				drectf += destskip * 4;
				srectf += srcskip * 4;
			}
		}
	}
	else if (mode == IMB_BLEND_COPY_RGB) {
		/* copy rgb only */
		for (; height > 0; height--) {
			if (do_char) {
				dr = drect;
				sr = srect;
				for (x = width; x > 0; x--, dr++, sr++) {
					((char *)dr)[0] = ((char *)sr)[0];
					((char *)dr)[1] = ((char *)sr)[1];
					((char *)dr)[2] = ((char *)sr)[2];
				}
				drect += destskip;
				srect += srcskip;
			}

			if (do_float) {
				drf = drectf;
				srf = srectf;
				for (x = width; x > 0; x--, drf += 4, srf += 4) {
					float map_alpha = (srf[3] == 0.0f)? drf[3] : drf[3] / srf[3];

					drf[0] = srf[0] * map_alpha;
					drf[1] = srf[1] * map_alpha;
					drf[2] = srf[2] * map_alpha;
				}
				drectf += destskip * 4;
				srectf += srcskip * 4;
			}
		}
	}
	else if (mode == IMB_BLEND_COPY_ALPHA) {
		/* copy alpha only */
		for (; height > 0; height--) {
			if (do_char) {
				dr = drect;
				sr = srect;
				for (x = width; x > 0; x--, dr++, sr++)
					((char *)dr)[3] = ((char *)sr)[3];
				drect += destskip;
				srect += srcskip;
			}

			if (do_float) {
				drf = drectf;
				srf = srectf;
				for (x = width; x > 0; x--, drf += 4, srf += 4)
					drf[3] = srf[3];
				drectf += destskip * 4;
				srectf += srcskip * 4;
			}
		}
	}
	else {
		switch (mode) {
			case IMB_BLEND_MIX:
				func = blend_color_mix_byte;
				func_float = blend_color_mix_float;
				break;
			case IMB_BLEND_ADD:
				func = blend_color_add_byte;
				func_float = blend_color_add_float;
				break;
			case IMB_BLEND_SUB:
				func = blend_color_sub_byte;
				func_float = blend_color_sub_float;
				break;
			case IMB_BLEND_MUL:
				func = blend_color_mul_byte;
				func_float = blend_color_mul_float;
				break;
			case IMB_BLEND_LIGHTEN:
				func = blend_color_lighten_byte;
				func_float = blend_color_lighten_float;
				break;
			case IMB_BLEND_DARKEN:
				func = blend_color_darken_byte;
				func_float = blend_color_darken_float;
				break;
			case IMB_BLEND_ERASE_ALPHA:
				func = blend_color_erase_alpha_byte;
				func_float = blend_color_erase_alpha_float;
				break;
			case IMB_BLEND_ADD_ALPHA:
				func = blend_color_add_alpha_byte;
				func_float = blend_color_add_alpha_float;
				break;
			default:
				break;
		}

		/* blend */
		for (; height > 0; height--) {
			if (do_char) {
				dr = drect;
				or = orect;
				sr = srect;

				if (dmaskrect && smaskrect) {
					/* mask accumulation for painting */
					dmr = dmaskrect;
					smr = smaskrect;

					for (x = width; x > 0; x--, dr++, or++, sr++, dmr++, smr++) {
						unsigned char *src = (unsigned char *)sr;
						
						if (!((lock_alpha & IMA_LAYER_LOCK_ALPHA) && (pixel_is_transparent((unsigned char *)or)))) {
							if (src[3] && *smr) {
								unsigned short mask = *dmr + (((mask_max - *dmr) * (*smr)) / 65535);
							
								if (mask > *dmr) {
									unsigned char mask_src[4];

									*dmr = mask;

									mask_src[0] = src[0];
									mask_src[1] = src[1];
									mask_src[2] = src[2];
									mask_src[3] = divide_round_i(src[3] * mask, 65535);

									func((unsigned char *)dr, (unsigned char *)or, mask_src);
								}
							}
						}
					}
					dmaskrect += origskip;
					smaskrect += srcskip;
				}
				else {
					/* regular blending */
					for (x = width; x > 0; x--, dr++, or++, sr++) {
						if (((unsigned char *)sr)[3])
							func((unsigned char *)dr, (unsigned char *)or, (unsigned char *)sr);
					}
				}

				drect += destskip;
				orect += origskip;
				srect += srcskip;
			}

			if (do_float) {
				drf = drectf;
				orf = orectf;
				srf = srectf;

				if (dmaskrect && smaskrect) {
					/* mask accumulation for painting */
					dmr = dmaskrect;
					smr = smaskrect;

					for (x = width; x > 0; x--, drf += 4, orf += 4, srf += 4, dmr++, smr++) {
						if (srf[3] != 0 && *smr) {
							unsigned short mask = *dmr + (((mask_max - *dmr) * (*smr)) / 65535);

							if (mask > *dmr) {
								float mask_srf[4];

								*dmr = mask;
								mul_v4_v4fl(mask_srf, srf, mask * (1.0f / 65535.0f));

								func_float(drf, orf, mask_srf);
							}
						}
					}

					dmaskrect += origskip;
					smaskrect += srcskip;
				}
				else {
					/* regular blending */
					for (x = width; x > 0; x--, drf += 4, orf += 4, srf += 4) {
						if (srf[3] != 0)
							func_float(drf, orf, srf);
					}
				}

				drectf += destskip * 4;
				orectf += origskip * 4;
				srectf += srcskip * 4;
			}
		}
	}
}