Skip to content

Commit

Permalink
Implement ColorMatrix object
Browse files Browse the repository at this point in the history
Only surface.applyColorFX() is currently implemented.  Still missing:
surface.applyColorFX4().  I assume that uses some sort of linear
interpolation... or would that be bilinear?
  • Loading branch information
fatcerberus committed Jun 11, 2015
1 parent 4589f77 commit a14d2b2
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 23 deletions.
139 changes: 120 additions & 19 deletions src/color.c
Expand Up @@ -2,12 +2,15 @@
#include "api.h"
#include "color.h"

static duk_ret_t js_BlendColors (duk_context* ctx);
static duk_ret_t js_BlendColorsWeighted (duk_context* ctx);
static duk_ret_t js_CreateColor (duk_context* ctx);
static duk_ret_t js_new_Color (duk_context* ctx);
static duk_ret_t js_Color_toString (duk_context* ctx);
static duk_ret_t js_Color_clone (duk_context* ctx);
static duk_ret_t js_BlendColors (duk_context* ctx);
static duk_ret_t js_BlendColorsWeighted (duk_context* ctx);
static duk_ret_t js_CreateColor (duk_context* ctx);
static duk_ret_t js_new_Color (duk_context* ctx);
static duk_ret_t js_Color_toString (duk_context* ctx);
static duk_ret_t js_Color_clone (duk_context* ctx);
static duk_ret_t js_CreateColorMatrix (duk_context* ctx);
static duk_ret_t js_new_ColorMatrix (duk_context* ctx);
static duk_ret_t js_ColorMatrix_toString (duk_context* ctx);

color_t
rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t alpha)
Expand All @@ -21,6 +24,19 @@ rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t alpha)
return color;
}

colormatrix_t
colormatrix(uint8_t rn, uint8_t rr, uint8_t rg, uint8_t rb,
uint8_t gn, uint8_t gr, uint8_t gg, uint8_t gb,
uint8_t bn, uint8_t br, uint8_t bg, uint8_t bb)
{
colormatrix_t matrix = {
rn, rr, rg, rb,
gn, gr, gg, gb,
bn, br, bg, bb,
};
return matrix;
}

ALLEGRO_COLOR
nativecolor(color_t color)
{
Expand All @@ -41,17 +57,32 @@ blend_colors(color_t color1, color_t color2, float w1, float w2)
return blend;
}

color_t
transform_pixel(color_t pixel, colormatrix_t matrix)
{
return rgba(
matrix.rn + (matrix.rr * pixel.r + matrix.rg * pixel.g + matrix.rb * pixel.b) / 255,
matrix.gn + (matrix.gr * pixel.r + matrix.gg * pixel.g + matrix.gb * pixel.b) / 255,
matrix.bn + (matrix.br * pixel.r + matrix.bg * pixel.g + matrix.bb * pixel.b) / 255,
pixel.alpha);
}

void
init_color_api(void)
{
register_api_function(g_duk, NULL, "BlendColors", js_BlendColors);
register_api_function(g_duk, NULL, "BlendColorsWeighted", js_BlendColorsWeighted);
register_api_function(g_duk, NULL, "CreateColor", js_CreateColor);

register_api_function(g_duk, NULL, "CreateColorMatrix", js_CreateColorMatrix);

// register Color methods and properties
register_api_ctor(g_duk, "Color", js_new_Color, NULL);
register_api_function(g_duk, "Color", "toString", js_Color_toString);
register_api_function(g_duk, "Color", "clone", js_Color_clone);

// register ColorMatrix methods and properties
register_api_ctor(g_duk, "ColorMatrix", js_new_ColorMatrix, NULL);
register_api_function(g_duk, "ColorMatrix", "toString", js_ColorMatrix_toString);
}

void
Expand Down Expand Up @@ -80,6 +111,27 @@ duk_require_sphere_color(duk_context* ctx, duk_idx_t index)
return color;
}

colormatrix_t
duk_require_sphere_colormatrix(duk_context* ctx, duk_idx_t index)
{
colormatrix_t matrix;

duk_require_sphere_obj(ctx, index, "ColorMatrix");
duk_get_prop_string(ctx, index, "rn"); matrix.rn = fmin(fmax(duk_get_number(ctx, -1), 0), 255); duk_pop(ctx);
duk_get_prop_string(ctx, index, "rr"); matrix.rr = fmin(fmax(duk_get_number(ctx, -1), 0), 255); duk_pop(ctx);
duk_get_prop_string(ctx, index, "rg"); matrix.rg = fmin(fmax(duk_get_number(ctx, -1), 0), 255); duk_pop(ctx);
duk_get_prop_string(ctx, index, "rb"); matrix.rb = fmin(fmax(duk_get_number(ctx, -1), 0), 255); duk_pop(ctx);
duk_get_prop_string(ctx, index, "gn"); matrix.gn = fmin(fmax(duk_get_number(ctx, -1), 0), 255); duk_pop(ctx);
duk_get_prop_string(ctx, index, "gr"); matrix.gr = fmin(fmax(duk_get_number(ctx, -1), 0), 255); duk_pop(ctx);
duk_get_prop_string(ctx, index, "gg"); matrix.gg = fmin(fmax(duk_get_number(ctx, -1), 0), 255); duk_pop(ctx);
duk_get_prop_string(ctx, index, "gb"); matrix.gb = fmin(fmax(duk_get_number(ctx, -1), 0), 255); duk_pop(ctx);
duk_get_prop_string(ctx, index, "bn"); matrix.bn = fmin(fmax(duk_get_number(ctx, -1), 0), 255); duk_pop(ctx);
duk_get_prop_string(ctx, index, "br"); matrix.br = fmin(fmax(duk_get_number(ctx, -1), 0), 255); duk_pop(ctx);
duk_get_prop_string(ctx, index, "bg"); matrix.bg = fmin(fmax(duk_get_number(ctx, -1), 0), 255); duk_pop(ctx);
duk_get_prop_string(ctx, index, "bb"); matrix.bb = fmin(fmax(duk_get_number(ctx, -1), 0), 255); duk_pop(ctx);
return matrix;
}

static duk_ret_t
js_BlendColors(duk_context* ctx)
{
Expand Down Expand Up @@ -107,18 +159,7 @@ js_BlendColorsWeighted(duk_context* ctx)
static duk_ret_t
js_CreateColor(duk_context* ctx)
{
int n_args = duk_get_top(ctx);
int r = duk_require_int(ctx, 0);
int g = duk_require_int(ctx, 1);
int b = duk_require_int(ctx, 2);
int alpha = n_args >= 4 ? duk_require_int(ctx, 3) : 255;

r = fmin(fmax(r, 0), 255);
g = fmin(fmax(g, 0), 255);
b = fmin(fmax(b, 0), 255);
alpha = fmin(fmax(alpha, 0), 255);
duk_push_sphere_color(ctx, rgba(r, g, b, alpha));
return 1;
return js_new_Color(ctx);
}

static duk_ret_t
Expand Down Expand Up @@ -163,3 +204,63 @@ js_Color_clone(duk_context* ctx)
duk_push_sphere_color(ctx, color);
return 1;
}

static duk_ret_t
js_CreateColorMatrix(duk_context* ctx)
{
return js_new_ColorMatrix(ctx);
}

static duk_ret_t
js_new_ColorMatrix(duk_context* ctx)
{
int rn = duk_require_int(ctx, 0);
int rr = duk_require_int(ctx, 1);
int rg = duk_require_int(ctx, 2);
int rb = duk_require_int(ctx, 3);
int gn = duk_require_int(ctx, 4);
int gr = duk_require_int(ctx, 5);
int gg = duk_require_int(ctx, 6);
int gb = duk_require_int(ctx, 7);
int bn = duk_require_int(ctx, 8);
int br = duk_require_int(ctx, 9);
int bg = duk_require_int(ctx, 10);
int bb = duk_require_int(ctx, 11);

// clamp entries to 8-bit [0-255]
rn = fmin(fmax(rn, 0), 255);
rr = fmin(fmax(rr, 0), 255);
rg = fmin(fmax(rg, 0), 255);
rb = fmin(fmax(rb, 0), 255);
gn = fmin(fmax(gn, 0), 255);
gr = fmin(fmax(gr, 0), 255);
gg = fmin(fmax(gg, 0), 255);
gb = fmin(fmax(gb, 0), 255);
bn = fmin(fmax(bn, 0), 255);
br = fmin(fmax(br, 0), 255);
bg = fmin(fmax(bg, 0), 255);
bb = fmin(fmax(bb, 0), 255);

// construct a ColorMatrix object
duk_push_sphere_obj(ctx, "ColorMatrix", NULL);
duk_push_int(ctx, rn); duk_put_prop_string(ctx, -2, "rn");
duk_push_int(ctx, rr); duk_put_prop_string(ctx, -2, "rr");
duk_push_int(ctx, rg); duk_put_prop_string(ctx, -2, "rg");
duk_push_int(ctx, rb); duk_put_prop_string(ctx, -2, "rb");
duk_push_int(ctx, gg); duk_put_prop_string(ctx, -2, "gn");
duk_push_int(ctx, gr); duk_put_prop_string(ctx, -2, "gr");
duk_push_int(ctx, gg); duk_put_prop_string(ctx, -2, "gg");
duk_push_int(ctx, gb); duk_put_prop_string(ctx, -2, "gb");
duk_push_int(ctx, bg); duk_put_prop_string(ctx, -2, "bn");
duk_push_int(ctx, br); duk_put_prop_string(ctx, -2, "br");
duk_push_int(ctx, bg); duk_put_prop_string(ctx, -2, "bg");
duk_push_int(ctx, bb); duk_put_prop_string(ctx, -2, "bb");
return 1;
}

static duk_ret_t
js_ColorMatrix_toString(duk_context* ctx)
{
duk_push_string(ctx, "[object colormatrix]");
return 1;
}
23 changes: 19 additions & 4 deletions src/color.h
@@ -1,15 +1,23 @@
#ifndef MINISPHERE__COLOR_H__INCLUDED
#define MINISPHERE__COLOR_H__INCLUDED

typedef struct color color_t;
typedef struct color color_t;
typedef struct colormatrix colormatrix_t;

extern color_t rgba (uint8_t r, uint8_t g, uint8_t b, uint8_t alpha);
extern ALLEGRO_COLOR nativecolor (color_t color);
extern color_t blend_colors (color_t color1, color_t color2, float w1, float w2);

extern void init_color_api (void);
extern void duk_push_sphere_color (duk_context* ctx, color_t color);
extern color_t duk_require_sphere_color (duk_context* ctx, duk_idx_t index);
extern colormatrix_t colormatrix (uint8_t rn, uint8_t rr, uint8_t rg, uint8_t rb,
uint8_t gn, uint8_t gr, uint8_t gg, uint8_t gb,
uint8_t bn, uint8_t br, uint8_t bg, uint8_t bb);
extern color_t transform_pixel (color_t pixel, colormatrix_t matrix);

extern void init_color_api (void);

extern void duk_push_sphere_color (duk_context* ctx, color_t color);
extern color_t duk_require_sphere_color (duk_context* ctx, duk_idx_t index);
extern colormatrix_t duk_require_sphere_colormatrix (duk_context* ctx, duk_idx_t index);

struct color
{
Expand All @@ -19,4 +27,11 @@ struct color
uint8_t alpha;
};

struct colormatrix
{
uint8_t rn, rr, rg, rb;
uint8_t gn, gr, gg, gb;
uint8_t bn, br, bg, bb;
};

#endif // MINISPHERE__COLOR_H__INCLUDED
19 changes: 19 additions & 0 deletions src/image.c
Expand Up @@ -296,6 +296,25 @@ set_image_pixel(image_t* image, int x, int y, color_t color)
al_set_target_bitmap(old_target);
}

bool
apply_color_matrix(image_t* image, colormatrix_t matrix, int x, int y, int width, int height)
{
image_lock_t* lock;
color_t* pixel;

int i_x, i_y;

if (!(lock = lock_image(image)))
return false;
uncache_pixels(image);
for (i_x = x; i_x < x + width; ++i_x) for (i_y = y; i_y < y + height; ++i_y) {
pixel = &lock->pixels[i_x + i_y * lock->pitch];
*pixel = transform_pixel(*pixel, matrix);
}
unlock_image(image, lock);
return true;
}

bool
apply_image_lookup(image_t* image, int x, int y, int width, int height, uint8_t red_lu[256], uint8_t green_lu[256], uint8_t blue_lu[256], uint8_t alpha_lu[256])
{
Expand Down
1 change: 1 addition & 0 deletions src/image.h
Expand Up @@ -17,6 +17,7 @@ extern int get_image_height (const image_t* image);
extern color_t get_image_pixel (image_t* image, int x, int y);
extern int get_image_width (const image_t* image);
extern void set_image_pixel (image_t* image, int x, int y, color_t color);
extern bool apply_color_matrix (image_t* image, colormatrix_t matrix, int x, int y, int width, int height);
extern bool apply_image_lookup (image_t* image, int x, int y, int width, int height, uint8_t red_lu[256], uint8_t green_lu[256], uint8_t blue_lu[256], uint8_t alpha_lu[256]);
extern void draw_image (image_t* image, int x, int y);
extern void draw_image_masked (image_t* image, color_t mask, int x, int y);
Expand Down
25 changes: 25 additions & 0 deletions src/surface.c
Expand Up @@ -22,6 +22,7 @@ static duk_ret_t js_Surface_getPixel (duk_context* ctx);
static duk_ret_t js_Surface_setAlpha (duk_context* ctx);
static duk_ret_t js_Surface_setBlendMode (duk_context* ctx);
static duk_ret_t js_Surface_setPixel (duk_context* ctx);
static duk_ret_t js_Surface_applyColorFX (duk_context* ctx);
static duk_ret_t js_Surface_applyLookup (duk_context* ctx);
static duk_ret_t js_Surface_blit (duk_context* ctx);
static duk_ret_t js_Surface_blitMaskSurface (duk_context* ctx);
Expand Down Expand Up @@ -73,6 +74,7 @@ init_surface_api(void)
register_api_function(g_duk, "Surface", "setAlpha", js_Surface_setAlpha);
register_api_function(g_duk, "Surface", "setBlendMode", js_Surface_setBlendMode);
register_api_function(g_duk, "Surface", "setPixel", js_Surface_setPixel);
register_api_function(g_duk, "Surface", "applyColorFX", js_Surface_applyColorFX);
register_api_function(g_duk, "Surface", "applyLookup", js_Surface_applyLookup);
register_api_function(g_duk, "Surface", "blit", js_Surface_blit);
register_api_function(g_duk, "Surface", "blitMaskSurface", js_Surface_blitMaskSurface);
Expand Down Expand Up @@ -314,6 +316,27 @@ js_Surface_getPixel(duk_context* ctx)
return 1;
}

static duk_ret_t
js_Surface_applyColorFX(duk_context* ctx)
{
int x = duk_require_int(ctx, 0);
int y = duk_require_int(ctx, 1);
int w = duk_require_int(ctx, 2);
int h = duk_require_int(ctx, 3);
colormatrix_t matrix = duk_require_sphere_colormatrix(ctx, 4);

image_t* image;

duk_push_this(ctx);
image = duk_require_sphere_surface(ctx, -1);
duk_pop(ctx);
if (x < 0 || y < 0 || x + w > get_image_width(image) || y + h > get_image_height(image))
duk_error_ni(ctx, -1, DUK_ERR_RANGE_ERROR, "Surface:applyColorFX(): Specified area extends outside image (%i,%i,%i,%i)", x, y, w, h);
if (!apply_color_matrix(image, matrix, x, y, w, h))
duk_error_ni(ctx, -1, DUK_ERR_ERROR, "Surface:applyColorFX(): Failed to apply color matrix");
return 0;
}

static duk_ret_t
js_Surface_applyLookup(duk_context* ctx)
{
Expand All @@ -331,6 +354,8 @@ js_Surface_applyLookup(duk_context* ctx)
duk_push_this(ctx);
image = duk_require_sphere_surface(ctx, -1);
duk_pop(ctx);
if (x < 0 || y < 0 || x + w > get_image_width(image) || y + h > get_image_height(image))
duk_error_ni(ctx, -1, DUK_ERR_RANGE_ERROR, "Surface:applyColorFX(): Specified area extends outside image (%i,%i,%i,%i)", x, y, w, h);
if (!apply_image_lookup(image, x, y, w, h, red_lu, green_lu, blue_lu, alpha_lu))
duk_error_ni(ctx, -1, DUK_ERR_ERROR, "Surface:applyLookup(): Failed to apply lookup transformation");
return 0;
Expand Down

0 comments on commit a14d2b2

Please sign in to comment.