/* * PiDiP module * Copyright (c) by Yves Degoyon ( ydegoyon@free.fr ) * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * */ /* This object is a color mapper that lets you change the colors within the image */ #include "pdp.h" #include "yuv.h" #include #include struct _rtext { char *x_buf; int x_bufsize; int x_selstart; int x_selend; int x_active; int x_dragfrom; int x_height; int x_drawnwidth; int x_drawnheight; t_text *x_text; t_glist *x_glist; char x_tag[50]; struct _rtext *x_next; }; #define t_rtext struct _rtext extern int rtext_width(t_rtext *x); extern int rtext_height(t_rtext *x); extern t_rtext *glist_findrtext(t_glist *gl, t_text *who); typedef struct _color { t_int on; t_int y,u,v; t_int oy,ou,ov; t_int tolerance; } t_color; #define COLORHEIGHT 5 #define DEFAULT_CAPACITY 10 static char *pdp_cmap_version = "pdp_cmap: a color mapper version 0.1 written by Yves Degoyon (ydegoyon@free.fr)"; typedef struct pdp_cmap_struct { t_object x_obj; t_float x_f; t_int x_packet0; t_int x_dropped; t_int x_vwidth; t_int x_vheight; t_int x_vsize; t_int x_capacity; // number of mapped colors t_int x_current; // current color t_color *x_colors; // color substitution table t_int x_cursor; // show cursor or not t_int x_luminosity; // use luminosity or not t_int x_colorR; // setable r t_int x_colorG; // setable g t_int x_colorB; // setable b t_int x_cursX; // X coordinate of cursor t_int x_cursY; // Y coordinate of cursor short int *x_frame; // keep a copy of current frame for picking color t_outlet *x_pdp_output; // output packets t_canvas *x_canvas; } t_pdp_cmap; static void pdp_cmap_draw_color(t_pdp_cmap *x, t_int r, t_int g, t_int b) { t_int width, height; char color[32]; sprintf( color, "#%.2X%.2X%.2X", r, g, b ); width = rtext_width( glist_findrtext( (t_glist*)x->x_canvas, (t_text *)x ) ); height = rtext_height( glist_findrtext( (t_glist*)x->x_canvas, (t_text *)x ) ); sys_vgui(".x%x.c delete rectangle %xCOLOR\n", x->x_canvas, x ); sys_vgui(".x%x.c create rectangle %d %d %d %d -fill %s -tags %xCOLOR\n", x->x_canvas, x->x_obj.te_xpix+width+5, x->x_obj.te_ypix, x->x_obj.te_xpix+width+height+5, x->x_obj.te_ypix+height, color, x ); } static void pdp_cmap_r(t_pdp_cmap *x, t_floatarg fr ) { if ( ( fr >= 0 ) && ( fr < 255 ) ) { x->x_colorR = (int)fr; x->x_colors[x->x_current].y = (yuv_RGBtoY( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB ))<<7; x->x_colors[x->x_current].u = (yuv_RGBtoU( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB )-128)<<8; x->x_colors[x->x_current].v = (yuv_RGBtoV( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB )-128)<<8; pdp_cmap_draw_color( x, x->x_colorR, x->x_colorG, x->x_colorB ); } } static void pdp_cmap_g(t_pdp_cmap *x, t_floatarg fg ) { if ( ( fg >= 0 ) && ( fg < 255 ) ) { x->x_colorG = (int)fg; x->x_colors[x->x_current].y = (yuv_RGBtoY( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB ))<<7; x->x_colors[x->x_current].u = (yuv_RGBtoU( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB )-128)<<8; x->x_colors[x->x_current].v = (yuv_RGBtoV( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB )-128)<<8; pdp_cmap_draw_color( x, x->x_colorR, x->x_colorG, x->x_colorB ); } } static void pdp_cmap_b(t_pdp_cmap *x, t_floatarg fb ) { if ( ( fb >= 0 ) && ( fb < 255 ) ) { x->x_colorB = (int)fb; x->x_colors[x->x_current].y = (yuv_RGBtoY( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB ))<<7; x->x_colors[x->x_current].u = (yuv_RGBtoU( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB )-128)<<8; x->x_colors[x->x_current].v = (yuv_RGBtoV( (x->x_colorR << 16) + (x->x_colorG << 8) + x->x_colorB )-128)<<8; pdp_cmap_draw_color( x, x->x_colorR, x->x_colorG, x->x_colorB ); } } static void pdp_cmap_cursx(t_pdp_cmap *x, t_floatarg fx ) { if ( ( fx >= 0 ) && ( fx < x->x_vwidth) ) { x->x_cursX = (int)fx; } } static void pdp_cmap_cursy(t_pdp_cmap *x, t_floatarg fy ) { if ( ( fy >= 0 ) && ( fy < x->x_vheight) ) { x->x_cursY = (int)fy; } } static void pdp_cmap_tolerance(t_pdp_cmap *x, t_floatarg ftolerance ) { if ( ftolerance >= 0 ) { x->x_colors[x->x_current].tolerance = (int)ftolerance; } } static void pdp_cmap_luminosity(t_pdp_cmap *x, t_floatarg fluminosity ) { if ( ( fluminosity == 0 ) || ( fluminosity == 1 ) ) { x->x_luminosity = (int)fluminosity; } } static void pdp_cmap_delete(t_pdp_cmap *x, t_floatarg fcolor ) { if ( ( fcolor >= 0 ) && ( fcolor < x->x_capacity ) ) { x->x_colors[(int)fcolor].on = 0; } } static void pdp_cmap_clear(t_pdp_cmap *x) { t_int ci; for ( ci=0; cix_capacity; ci++) { x->x_colors[ci].on = 0; } x->x_current = 0; } static void pdp_cmap_resize(t_pdp_cmap *x, t_floatarg fnewsize ) { t_color *colors; t_int ci, csize; if ( (int) fnewsize<=0 ) return; // allocate new structures colors = (t_color*) getbytes( fnewsize*sizeof(t_color) ); for ( ci=0; cix_capacity ) { post( "pdp_form : new size is too small : texts lost !!" ); csize = fnewsize; } else { csize = x->x_capacity; } // copy all values for ( ci=0; cix_colors[ci], sizeof( t_color ) ); } // free old structures if ( x->x_colors ) freebytes( x->x_colors, x->x_capacity*sizeof(t_color) ); // set new structures x->x_colors = colors; x->x_capacity = fnewsize; x->x_current = 0; } static void pdp_cmap_setcur(t_pdp_cmap *x, t_floatarg fpx, t_floatarg fpy ) { if ( (fpx>=0.0) && (fpx<=1.0) && (fpy>=0.0) && (fpy<=1.0) ) { x->x_cursX = fpx*(t_float)x->x_vwidth; x->x_cursY = fpy*(t_float)x->x_vheight; } } static void pdp_cmap_current(t_pdp_cmap *x, t_floatarg fcurrent ) { if ( ( fcurrent >= 0 ) && ( fcurrent < x->x_capacity ) ) { x->x_current = (int)fcurrent; post( "pdp_cmap : color index set to : %d", x->x_current ); } } static void pdp_cmap_cursor(t_pdp_cmap *x, t_floatarg fcursor ) { if ( ( fcursor == 0 ) || ( fcursor == 1 ) ) { x->x_cursor = (int)fcursor; } } static void pdp_cmap_pick(t_pdp_cmap *x) { t_int y,u,v; if ( x->x_frame && ( x->x_cursX > 0 ) && ( x->x_cursX < x->x_vwidth ) && ( x->x_cursY > 0 ) && ( x->x_cursY < x->x_vheight ) ) { x->x_colors[x->x_current].oy = x->x_frame[ x->x_cursY*x->x_vwidth+x->x_cursX ]; x->x_colors[x->x_current].ov = x->x_frame[ x->x_vsize+((x->x_cursY>>1)*(x->x_vwidth>>1)+(x->x_cursX>>1)) ]; x->x_colors[x->x_current].ou = x->x_frame[ x->x_vsize+(x->x_vsize>>2)+((x->x_cursY>>1)*(x->x_vwidth>>1)+(x->x_cursX>>1)) ]; y = (x->x_colors[x->x_current].oy)>>7; v = (x->x_colors[x->x_current].ov>>8)+128; u = (x->x_colors[x->x_current].ou>>8)+128; x->x_colorR = yuv_YUVtoR( y, u, v ); x->x_colorG = yuv_YUVtoG( y, u, v ); x->x_colorB = yuv_YUVtoB( y, u, v ); pdp_cmap_draw_color( x, x->x_colorR, x->x_colorG, x->x_colorB ); x->x_colors[x->x_current].y = 255; x->x_colors[x->x_current].u = 255; x->x_colors[x->x_current].v = 255; x->x_colors[x->x_current].on = 1; } } static void pdp_cmap_allocate(t_pdp_cmap *x) { x->x_frame = (short int *) getbytes ( ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 ); if ( !x->x_frame ) { post( "pdp_mgrid : severe error : cannot allocate buffer !!! "); return; } } static void pdp_cmap_free_ressources(t_pdp_cmap *x) { if ( x->x_frame ) freebytes ( x->x_frame, ( x->x_vsize + ( x->x_vsize>>1 ) ) << 1 ); } static void pdp_cmap_process_yv12(t_pdp_cmap *x) { t_pdp *header = pdp_packet_header(x->x_packet0); short int *data = (short int *)pdp_packet_data(x->x_packet0); t_int i, ci; t_int px=0, py=0, ppx=0, ppy=0; t_int y=0, u=0, v=0; short int *pfY, *pfU, *pfV; short int *poY, *poU, *poV; t_int diff; /* allocate all ressources */ if ( ( (int)header->info.image.width != x->x_vwidth ) || ( (int)header->info.image.height != x->x_vheight ) ) { pdp_cmap_free_ressources( x ); x->x_vwidth = header->info.image.width; x->x_vheight = header->info.image.height; x->x_vsize = x->x_vwidth*x->x_vheight; pdp_cmap_allocate( x ); post( "pdp_cmap : reallocated buffers" ); } memcpy(x->x_frame, data, (x->x_vsize + (x->x_vsize>>1))<<1 ); // map colors for ( ci=0; cix_capacity; ci++ ) { if ( x->x_colors[ci].on ) { pfY = data; pfV = data+x->x_vsize; pfU = data+x->x_vsize+(x->x_vsize>>2); poY = x->x_frame; poV = x->x_frame+x->x_vsize; poU = x->x_frame+x->x_vsize+(x->x_vsize>>2); for ( py=0; pyx_vheight; py++ ) { for ( px=0; pxx_vwidth; px++ ) { y = *poY; v = *poV; u = *poU; if ( x->x_luminosity ) { diff = (abs(y-x->x_colors[ci].oy)>>7)+(abs(u-x->x_colors[ci].ou)>>8)+(abs(v-x->x_colors[ci].ov)>>8); } else { diff = (abs(u-x->x_colors[ci].ou)>>8)+(abs(v-x->x_colors[ci].ov)>>8); } if ( diff <= x->x_colors[ci].tolerance ) { // change color not luminosity // *pfY = x->x_colors[ci].y; *pfV = x->x_colors[ci].v; *pfU = x->x_colors[ci].u; } pfY++;poY++; if ( (px%2==0) && (py%2==0) ) { pfU++;pfV++; poU++;poV++; } } } } } // draw cursor if ( ( x->x_cursX > 0 ) && ( x->x_cursY > 0 ) && ( x->x_cursor ) ) { for ( px=(x->x_cursX-5); px<=(x->x_cursX+5); px++ ) { if ( ( px > 0 ) && ( px < x->x_vwidth ) ) { if ( ((*(data+x->x_cursY*x->x_vwidth+px))>>7) < 128 ) { *(data+x->x_cursY*x->x_vwidth+px) = 0xff<<7; } else { *(data+x->x_cursY*x->x_vwidth+px) = 0x00<<7; } } } for ( py=(x->x_cursY-5); py<=(x->x_cursY+5); py++ ) { if ( ( py > 0 ) && ( py < x->x_vheight ) ) { if ( ((*(data+py*x->x_vwidth+x->x_cursX))>>7) < 128 ) { *(data+py*x->x_vwidth+x->x_cursX) = 0xff<<7; } else { *(data+py*x->x_vwidth+x->x_cursX) = 0x00<<7; } } } } pdp_packet_pass_if_valid(x->x_pdp_output, &x->x_packet0); return; } static void pdp_cmap_process(t_pdp_cmap *x) { int encoding; t_pdp *header = 0; /* check if image data packets are compatible */ if ( (header = pdp_packet_header(x->x_packet0)) && (PDP_IMAGE == header->type)){ /* pdp_cmap_process inputs and write into active inlet */ switch(pdp_packet_header(x->x_packet0)->info.image.encoding){ case PDP_IMAGE_YV12: pdp_cmap_process_yv12(x); break; case PDP_IMAGE_GREY: // should write something to handle these one day // but i don't use this mode break; default: /* don't know the type, so dont pdp_cmap_process */ break; } } } static void pdp_cmap_input_0(t_pdp_cmap *x, t_symbol *s, t_floatarg f) { /* if this is a register_ro message or register_rw message, register with packet factory */ if (s== gensym("register_rw")) x->x_dropped = pdp_packet_convert_ro_or_drop(&x->x_packet0, (int)f, pdp_gensym("image/YCrCb/*") ); if ((s == gensym("process")) && (-1 != x->x_packet0) && (!x->x_dropped)){ pdp_cmap_process(x); } } static void pdp_cmap_free(t_pdp_cmap *x) { int i; pdp_packet_mark_unused(x->x_packet0); pdp_cmap_free_ressources( x ); } t_class *pdp_cmap_class; void *pdp_cmap_new(void) { t_int ci; t_pdp_cmap *x = (t_pdp_cmap *)pd_new(pdp_cmap_class); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("current")); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("cursx")); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("cursy")); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("R")); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("G")); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("B")); inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("tolerance")); x->x_pdp_output = outlet_new(&x->x_obj, &s_anything); x->x_packet0 = -1; x->x_cursX = -1; x->x_cursY = -1; x->x_cursor = 1; x->x_luminosity = 1; x->x_capacity = DEFAULT_CAPACITY; x->x_current = 0; x->x_colors = (t_color *) getbytes( x->x_capacity*sizeof(t_color) ); for ( ci=0; cix_capacity; ci++) { x->x_colors[ci].on = 0; x->x_colors[ci].tolerance = 10; } x->x_canvas = canvas_getcurrent(); return (void *)x; } #ifdef __cplusplus extern "C" { #endif void pdp_cmap_setup(void) { post( pdp_cmap_version ); pdp_cmap_class = class_new(gensym("pdp_cmap"), (t_newmethod)pdp_cmap_new, (t_method)pdp_cmap_free, sizeof(t_pdp_cmap), 0, A_NULL); class_addmethod(pdp_cmap_class, (t_method)pdp_cmap_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); class_addmethod(pdp_cmap_class, (t_method)pdp_cmap_r, gensym("R"), A_DEFFLOAT, A_NULL); class_addmethod(pdp_cmap_class, (t_method)pdp_cmap_g, gensym("G"), A_DEFFLOAT, A_NULL); class_addmethod(pdp_cmap_class, (t_method)pdp_cmap_b, gensym("B"), A_DEFFLOAT, A_NULL); class_addmethod(pdp_cmap_class, (t_method)pdp_cmap_cursx, gensym("cursx"), A_DEFFLOAT, A_NULL); class_addmethod(pdp_cmap_class, (t_method)pdp_cmap_cursy, gensym("cursy"), A_DEFFLOAT, A_NULL); class_addmethod(pdp_cmap_class, (t_method)pdp_cmap_pick, gensym("pick"), A_NULL); class_addmethod(pdp_cmap_class, (t_method)pdp_cmap_tolerance, gensym("tolerance"), A_DEFFLOAT, A_NULL); class_addmethod(pdp_cmap_class, (t_method)pdp_cmap_cursor, gensym("cursor"), A_DEFFLOAT, A_NULL); class_addmethod(pdp_cmap_class, (t_method)pdp_cmap_luminosity, gensym("luminosity"), A_DEFFLOAT, A_NULL); class_addmethod(pdp_cmap_class, (t_method)pdp_cmap_current, gensym("current"), A_DEFFLOAT, A_NULL); class_addmethod(pdp_cmap_class, (t_method)pdp_cmap_clear, gensym("clear"), A_NULL); class_addmethod(pdp_cmap_class, (t_method)pdp_cmap_delete, gensym("delete"), A_DEFFLOAT, A_NULL); class_addmethod(pdp_cmap_class, (t_method)pdp_cmap_resize, gensym("resize"), A_DEFFLOAT, A_NULL); class_addmethod(pdp_cmap_class, (t_method)pdp_cmap_setcur, gensym("setcur"), A_DEFFLOAT, A_DEFFLOAT, A_NULL); } #ifdef __cplusplus } #endif