Files
test/source/blender/blenkernel/intern/image_gen.c
Brecht Van Lommel 8f9150871c Fix part of #34233: bad alpha blending for 2D image painting. This is a very
old issue, the formulas here were never quite right, should all work ok now
with byte and float images.

Some differences:

* Colors with zero alpha from the background will never have an influence, so
  you don't get alpha fringes when painting over such areas. This does give
  hard edges when looking at the RGB channels alone, but there's no way to
  avoid that and fringes at the same time, same behavior as other painting apps.

* Add/Subtract/Multiply/Lighten/Darken now leave the alpha channel unchanged
  and work only the RGB channels, again same behavior as many other apps.

* Erase/Add alpha now compensates for premultiplied float images to keep the
  straight RGB colors the same.

Next: fix projection painting.
2013-04-27 12:51:23 +00:00

353 lines
9.1 KiB
C

/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): Matt Ebb, Campbell Barton, Shuvro Sarker
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/blenkernel/intern/image_gen.c
* \ingroup bke
*/
#include <math.h>
#include <stdlib.h>
#include "BLI_math_base.h"
#include "BLI_math_color.h"
#include "BLI_math_vector.h"
#include "BKE_image.h"
#include "BLF_api.h"
void BKE_image_buf_fill_color(unsigned char *rect, float *rect_float, int width, int height, const float color[4])
{
int x, y;
/* blank image */
if (rect_float) {
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
copy_v4_v4(rect_float, color);
rect_float += 4;
}
}
}
if (rect) {
unsigned char ccol[4];
rgba_float_to_uchar(ccol, color);
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
rect[0] = ccol[0];
rect[1] = ccol[1];
rect[2] = ccol[2];
rect[3] = ccol[3];
rect += 4;
}
}
}
}
void BKE_image_buf_fill_checker(unsigned char *rect, float *rect_float, int width, int height)
{
/* these two passes could be combined into one, but it's more readable and
* easy to tweak like this, speed isn't really that much of an issue in this situation... */
int checkerwidth = 32, dark = 1;
int x, y;
unsigned char *rect_orig = rect;
float *rect_float_orig = rect_float;
float h = 0.0, hoffs = 0.0;
float hsv[3] = {0.0f, 0.9f, 0.9f};
float rgb[3];
/* checkers */
for (y = 0; y < height; y++) {
dark = powf(-1.0f, floorf(y / checkerwidth));
for (x = 0; x < width; x++) {
if (x % checkerwidth == 0) dark = -dark;
if (rect_float) {
if (dark > 0) {
rect_float[0] = rect_float[1] = rect_float[2] = 0.25f;
rect_float[3] = 1.0f;
}
else {
rect_float[0] = rect_float[1] = rect_float[2] = 0.58f;
rect_float[3] = 1.0f;
}
rect_float += 4;
}
else {
if (dark > 0) {
rect[0] = rect[1] = rect[2] = 64;
rect[3] = 255;
}
else {
rect[0] = rect[1] = rect[2] = 150;
rect[3] = 255;
}
rect += 4;
}
}
}
rect = rect_orig;
rect_float = rect_float_orig;
/* 2nd pass, colored + */
for (y = 0; y < height; y++) {
hoffs = 0.125f * floorf(y / checkerwidth);
for (x = 0; x < width; x++) {
h = 0.125f * floorf(x / checkerwidth);
if ((fabs((x % checkerwidth) - (checkerwidth / 2)) < 4) &&
(fabs((y % checkerwidth) - (checkerwidth / 2)) < 4))
{
if ((fabs((x % checkerwidth) - (checkerwidth / 2)) < 1) ||
(fabs((y % checkerwidth) - (checkerwidth / 2)) < 1))
{
hsv[0] = fmodf(fabs(h - hoffs), 1.0f);
hsv_to_rgb_v(hsv, rgb);
if (rect) {
rect[0] = (char)(rgb[0] * 255.0f);
rect[1] = (char)(rgb[1] * 255.0f);
rect[2] = (char)(rgb[2] * 255.0f);
rect[3] = 255;
}
if (rect_float) {
rect_float[0] = rgb[0];
rect_float[1] = rgb[1];
rect_float[2] = rgb[2];
rect_float[3] = 1.0f;
}
}
}
if (rect_float) rect_float += 4;
if (rect) rect += 4;
}
}
}
/* Utility functions for BKE_image_buf_fill_checker_color */
#define BLEND_FLOAT(real, add) (real + add <= 1.0f) ? (real + add) : 1.0f
#define BLEND_CHAR(real, add) ((real + (char)(add * 255.0f)) <= 255) ? (real + (char)(add * 255.0f)) : 255
static void checker_board_color_fill(unsigned char *rect, float *rect_float, int width, int height)
{
int hue_step, y, x;
float hsv[3], rgb[3];
hsv[1] = 1.0;
hue_step = power_of_2_max_i(width / 8);
if (hue_step < 8) hue_step = 8;
for (y = 0; y < height; y++) {
hsv[2] = 0.1 + (y * (0.4 / height)); /* use a number lower then 1.0 else its too bright */
for (x = 0; x < width; x++) {
hsv[0] = (float)((double)(x / hue_step) * 1.0 / width * hue_step);
hsv_to_rgb_v(hsv, rgb);
if (rect) {
rect[0] = (char)(rgb[0] * 255.0f);
rect[1] = (char)(rgb[1] * 255.0f);
rect[2] = (char)(rgb[2] * 255.0f);
rect[3] = 255;
rect += 4;
}
if (rect_float) {
rect_float[0] = rgb[0];
rect_float[1] = rgb[1];
rect_float[2] = rgb[2];
rect_float[3] = 1.0f;
rect_float += 4;
}
}
}
}
static void checker_board_color_tint(unsigned char *rect, float *rect_float, int width, int height, int size, float blend)
{
int x, y;
float blend_half = blend * 0.5f;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
if (((y / size) % 2 == 1 && (x / size) % 2 == 1) || ( (y / size) % 2 == 0 && (x / size) % 2 == 0)) {
if (rect) {
rect[0] = (char)BLEND_CHAR(rect[0], blend);
rect[1] = (char)BLEND_CHAR(rect[1], blend);
rect[2] = (char)BLEND_CHAR(rect[2], blend);
rect[3] = 255;
rect += 4;
}
if (rect_float) {
rect_float[0] = BLEND_FLOAT(rect_float[0], blend);
rect_float[1] = BLEND_FLOAT(rect_float[1], blend);
rect_float[2] = BLEND_FLOAT(rect_float[2], blend);
rect_float[3] = 1.0f;
rect_float += 4;
}
}
else {
if (rect) {
rect[0] = (char)BLEND_CHAR(rect[0], blend_half);
rect[1] = (char)BLEND_CHAR(rect[1], blend_half);
rect[2] = (char)BLEND_CHAR(rect[2], blend_half);
rect[3] = 255;
rect += 4;
}
if (rect_float) {
rect_float[0] = BLEND_FLOAT(rect_float[0], blend_half);
rect_float[1] = BLEND_FLOAT(rect_float[1], blend_half);
rect_float[2] = BLEND_FLOAT(rect_float[2], blend_half);
rect_float[3] = 1.0f;
rect_float += 4;
}
}
}
}
}
static void checker_board_grid_fill(unsigned char *rect, float *rect_float, int width, int height, float blend)
{
int x, y;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
if (((y % 32) == 0) || ((x % 32) == 0) || x == 0) {
if (rect) {
rect[0] = BLEND_CHAR(rect[0], blend);
rect[1] = BLEND_CHAR(rect[1], blend);
rect[2] = BLEND_CHAR(rect[2], blend);
rect[3] = 255;
rect += 4;
}
if (rect_float) {
rect_float[0] = BLEND_FLOAT(rect_float[0], blend);
rect_float[1] = BLEND_FLOAT(rect_float[1], blend);
rect_float[2] = BLEND_FLOAT(rect_float[2], blend);
rect_float[3] = 1.0f;
rect_float += 4;
}
}
else {
if (rect_float) rect_float += 4;
if (rect) rect += 4;
}
}
}
}
/* defined in image.c */
static void checker_board_text(unsigned char *rect, float *rect_float, int width, int height, int step, int outline)
{
int x, y;
int pen_x, pen_y;
char text[3] = {'A', '1', '\0'};
const int mono = blf_mono_font;
BLF_size(mono, 54, 72); /* hard coded size! */
/* OCIO_TODO: using NULL as display will assume using sRGB display
* this is correct since currently generated images are assumed to be in sRGB space,
* but this would probably needed to be fixed in some way
*/
BLF_buffer(mono, rect_float, rect, width, height, 4, NULL);
for (y = 0; y < height; y += step) {
text[1] = '1';
for (x = 0; x < width; x += step) {
/* hard coded offset */
pen_x = x + 33;
pen_y = y + 44;
/* terribly crappy outline font! */
BLF_buffer_col(mono, 1.0, 1.0, 1.0, 1.0);
BLF_position(mono, pen_x - outline, pen_y, 0.0);
BLF_draw_buffer(mono, text);
BLF_position(mono, pen_x + outline, pen_y, 0.0);
BLF_draw_buffer(mono, text);
BLF_position(mono, pen_x, pen_y - outline, 0.0);
BLF_draw_buffer(mono, text);
BLF_position(mono, pen_x, pen_y + outline, 0.0);
BLF_draw_buffer(mono, text);
BLF_position(mono, pen_x - outline, pen_y - outline, 0.0);
BLF_draw_buffer(mono, text);
BLF_position(mono, pen_x + outline, pen_y + outline, 0.0);
BLF_draw_buffer(mono, text);
BLF_position(mono, pen_x - outline, pen_y + outline, 0.0);
BLF_draw_buffer(mono, text);
BLF_position(mono, pen_x + outline, pen_y - outline, 0.0);
BLF_draw_buffer(mono, text);
BLF_buffer_col(mono, 0.0, 0.0, 0.0, 1.0);
BLF_position(mono, pen_x, pen_y, 0.0);
BLF_draw_buffer(mono, text);
text[1]++;
}
text[0]++;
}
/* cleanup the buffer. */
BLF_buffer(mono, NULL, NULL, 0, 0, 0, FALSE);
}
void BKE_image_buf_fill_checker_color(unsigned char *rect, float *rect_float, int width, int height)
{
checker_board_color_fill(rect, rect_float, width, height);
checker_board_color_tint(rect, rect_float, width, height, 1, 0.03f);
checker_board_color_tint(rect, rect_float, width, height, 4, 0.05f);
checker_board_color_tint(rect, rect_float, width, height, 32, 0.07f);
checker_board_color_tint(rect, rect_float, width, height, 128, 0.15f);
checker_board_grid_fill(rect, rect_float, width, height, 1.0f / 4.0f);
checker_board_text(rect, rect_float, width, height, 128, 2);
}