From 4c71ecdba14f6c959d41f0646f7e556880a3afc1 Mon Sep 17 00:00:00 2001 From: Andrew Corcoran Date: Sat, 19 Jul 2008 18:30:57 +0000 Subject: [PATCH] Added noise and mosaic magic tools --- magic/src/blur.c | 22 +++-- magic/src/mosaic.c | 235 ++++++++++++++++++++++++++++++++++++++++++++ magic/src/noise.c | 230 +++++++++++++++++++++++++++++++++++++++++++ magic/src/sharpen.c | 110 +++++++-------------- 4 files changed, 513 insertions(+), 84 deletions(-) create mode 100644 magic/src/mosaic.c create mode 100644 magic/src/noise.c diff --git a/magic/src/blur.c b/magic/src/blur.c index c031cba1b..a0dab7446 100644 --- a/magic/src/blur.c +++ b/magic/src/blur.c @@ -105,23 +105,27 @@ static void do_blur_pixel(void * ptr, int which, Uint8 temp[3]; double blurValue[3]; + for (k =0;k<3;k++){ + blurValue[k] = 0; + } + //5x5 gaussiann weighting window const int weight[5][5] = { {1,4,7,4,1}, {4,16,26,16,4}, {7,26,41,26,7}, {4,16,26,16,4}, {1,4,7,4,1}}; - for (i=-2;i<3;i++){ - for (j=-2;j<3;j++){ - //Add the pixels around the current one wieghted - SDL_GetRGB(api->getpixel(last, x + i, y + j), last->format, &temp[0], &temp[1], &temp[2]); - for (k =0;k<3;k++){ - blurValue[k] += temp[k]* weight[i+2][j+2]; - } - } + for (i=-2;i<3;i++){ + for (j=-2;j<3;j++){ + //Add the pixels around the current one wieghted + SDL_GetRGB(api->getpixel(canvas, x + i, y + j), canvas->format, &temp[0], &temp[1], &temp[2]); + for (k =0;k<3;k++){ + blurValue[k] += temp[k]* weight[i+2][j+2]; } + } + } for (k =0;k<3;k++){ - blurValue[k] /=273; + blurValue[k] /= 273; } api->putpixel(canvas, x, y, SDL_MapRGB(canvas->format, blurValue[0], blurValue[1], blurValue[2])); } diff --git a/magic/src/mosaic.c b/magic/src/mosaic.c new file mode 100644 index 000000000..6caa8a7bf --- /dev/null +++ b/magic/src/mosaic.c @@ -0,0 +1,235 @@ +/* + mosaic.c + + mosaic, Add a mosaic effect to the image using a combination of other tools. + Requires the mosaicAll sharpen and noise tools. + Tux Paint - A simple drawing program for children. + + Copyright (c) 2002-2007 by Bill Kendrick and others; see AUTHORS.txt + bill@newbreedsoftware.com + http://www.tuxpaint.org/ + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + (See COPYING.txt) + + Last updated: June 6, 2008 + $Id$ +*/ + +#include +#include +#include +#include "tp_magic_api.h" +#include "SDL_image.h" +#include "SDL_mixer.h" +#include +#include +#include +#include "noise.c" +#include "sharpen.c" +#include "blur.c" + +#ifndef gettext_noop +#define gettext_noop(String) String +#endif + + +double mosaic_AMOUNT= 30.0; +const int mosaic_RADIUS = 16; + +enum { + TOOL_MOSAIC, + mosaic_NUM_TOOLS +}; + +static Mix_Chunk * mosaic_snd_effect[mosaic_NUM_TOOLS]; + +const char * mosaic_snd_filenames[mosaic_NUM_TOOLS] = { + "flip.wav", +}; +const char * mosaic_icon_filenames[mosaic_NUM_TOOLS] = { + "flip.png", +}; +const char * mosaic_names[mosaic_NUM_TOOLS] = { + gettext_noop("Mosaic"), +}; +const char * mosaic_descs[mosaic_NUM_TOOLS] = { + gettext_noop("Click to add a mosaic effect to the image."), +}; + +Uint32 mosaic_api_version(void) { return(TP_MAGIC_API_VERSION); } + +//Load sounds +int mosaic_init(magic_api * api){ + + int i; + char fname[1024]; + + for (i = 0; i < mosaic_NUM_TOOLS; i++){ + snprintf(fname, sizeof(fname), "%s/sounds/magic/%s", api->data_directory, mosaic_snd_filenames[i]); + mosaic_snd_effect[i] = Mix_LoadWAV(fname); + } + + sharpen_init(api); + + return(1); +} + +int mosaic_get_tool_count(magic_api * api){ + return(mosaic_NUM_TOOLS); +} + +// Load our icons: +SDL_Surface * mosaic_get_icon(magic_api * api, int which){ + char fname[1024]; + snprintf(fname, sizeof(fname), "%simages/magic/%s", api->data_directory, mosaic_icon_filenames[which]); + return(IMG_Load(fname)); +} + +// Return our names, localized: +char * mosaic_get_name(magic_api * api, int which){ + return(strdup(gettext(mosaic_names[which]))); +} + +// Return our descriptions, localized: +char * mosaic_get_description(magic_api * api, int which){ + return(strdup(gettext(mosaic_descs[which]))); +} + +//Do the effect for one pixel +static void do_mosaic_pixel(void * ptr, int which, + SDL_Surface * canvas, SDL_Surface * last, + int x, int y){ + magic_api * api = (magic_api *) ptr; + int i; + for (i=0;i<3;i++){ + do_noise_pixel(ptr, 0, canvas, last, x, y); + } + do_blur_pixel(ptr, 0, canvas, last, x, y); + do_sharpen_pixel(ptr, 1, canvas, canvas, x, y); +} + +// Do the effect for the full image +static void do_mosaic_full(void * ptr, SDL_Surface * canvas, SDL_Surface * last, int which){ + + magic_api * api = (magic_api *) ptr; + + int x,y; + + for (y = 0; y < last->h; y++){ + for (x=0; x < last->w; x++){ + do_mosaic_pixel(ptr, which, canvas, last, x, y); + } + } +} + +//do the effect for the brush +static void do_mosaic_brush(void * ptr, int which, SDL_Surface * canvas, SDL_Surface * last, int x, int y){ + int xx, yy; + magic_api * api = (magic_api *) ptr; + + for (yy = y - mosaic_RADIUS; yy < y + mosaic_RADIUS; yy++) + { + for (xx = x - mosaic_RADIUS; xx < x + mosaic_RADIUS; xx++) + { + if (api->in_circle(xx - x, yy - y, mosaic_RADIUS) && + !api->touched(xx, yy)) + { + do_mosaic_pixel(api, which, canvas, last, xx, yy); + } + } + } +} + +// Affect the canvas on drag: +void mosaic_drag(magic_api * api, int which, SDL_Surface * canvas, + SDL_Surface * last, int ox, int oy, int x, int y, + SDL_Rect * update_rect){ + + api->line((void *) api, which, canvas, last, ox, oy, x, y, 1, do_mosaic_brush); + + api->playsound(mosaic_snd_effect[which], (x * 255) / canvas->w, 255); + + if (ox > x) { int tmp = ox; ox = x; x = tmp; } + if (oy > y) { int tmp = oy; oy = y; y = tmp; } + + update_rect->x = ox - mosaic_RADIUS; + update_rect->y = oy - mosaic_RADIUS; + update_rect->w = (x + mosaic_RADIUS) - update_rect->x; + update_rect->h = (y + mosaic_RADIUS) - update_rect->y; +} + +// Affect the canvas on click: +void mosaic_click(magic_api * api, int which, int mode, + SDL_Surface * canvas, SDL_Surface * last, + int x, int y, SDL_Rect * update_rect){ + if (mode == MODE_PAINT){ + mosaic_drag(api, which, canvas, last, x, y, x, y, update_rect); + } + else{ + update_rect->x = 0; + update_rect->y = 0; + update_rect->w = canvas->w; + update_rect->h = canvas->h; + do_mosaic_full(api, canvas, last, which); + api->playsound(mosaic_snd_effect[which], 128, 255); + } +} + +// Affect the canvas on release: +void mosaic_release(magic_api * api, int which, + SDL_Surface * canvas, SDL_Surface * last, + int x, int y, SDL_Rect * update_rect) +{ +} + +// No setup happened: +void mosaic_shutdown(magic_api * api) +{ + //Clean up sounds + int i; + for(i=0; i +#include +#include +#include "tp_magic_api.h" +#include "SDL_image.h" +#include "SDL_mixer.h" +#include +#include +#include + +#ifndef gettext_noop +#define gettext_noop(String) String +#endif + +const int noise_AMOUNT = 100.0; +const int noise_RADIUS = 16; + +enum { + TOOL_NOISE, + noise_NUM_TOOLS +}; + +static Mix_Chunk * noise_snd_effect[noise_NUM_TOOLS]; + +const char * noise_snd_filenames[noise_NUM_TOOLS] = { + "flip.wav", +}; +const char * noise_icon_filenames[noise_NUM_TOOLS] = { + "flip.png", +}; +const char * noise_names[noise_NUM_TOOLS] = { + gettext_noop("Noise"), +}; +const char * noise_descs[noise_NUM_TOOLS] = { + gettext_noop("Click to add noise to the image."), +}; + +Uint32 noise_api_version(void) { return(TP_MAGIC_API_VERSION); } + +//Load sounds +int noise_init(magic_api * api){ + srand(time(0)); + + int i; + char fname[1024]; + + for (i = 0; i < noise_NUM_TOOLS; i++){ + snprintf(fname, sizeof(fname), "%s/sounds/magic/%s", api->data_directory, noise_snd_filenames[i]); + noise_snd_effect[i] = Mix_LoadWAV(fname); + } + return(1); +} + +int noise_get_tool_count(magic_api * api){ + return(noise_NUM_TOOLS); +} + +// Load our icons: +SDL_Surface * noise_get_icon(magic_api * api, int which){ + char fname[1024]; + snprintf(fname, sizeof(fname), "%simages/magic/%s", api->data_directory, noise_icon_filenames[which]); + return(IMG_Load(fname)); +} + +// Return our names, localized: +char * noise_get_name(magic_api * api, int which){ + return(strdup(gettext(noise_names[which]))); +} + +// Return our descriptions, localized: +char * noise_get_description(magic_api * api, int which){ + return(strdup(gettext(noise_descs[which]))); +} + +//Do the effect for one pixel +static void do_noise_pixel(void * ptr, int which, + SDL_Surface * canvas, SDL_Surface * last, + int x, int y){ + magic_api * api = (magic_api *) ptr; + + Uint8 temp[3]; + double temp2[3]; + + SDL_GetRGB(api->getpixel(canvas,x, y), canvas->format, &temp[0], &temp[1], &temp[2]); + int k; + for (k =0;k<3;k++){ + temp2[k] = clamp(0.0, (int)temp[k] - (rand()%noise_AMOUNT) + noise_AMOUNT/2.0, 255.0); + } + api->putpixel(canvas, x, y, SDL_MapRGB(canvas->format, temp2[0], temp2[1], temp2[2])); + +} + +// Do the effect for the full image +static void do_noise_full(void * ptr,SDL_Surface * canvas, SDL_Surface * last, int which){ + + magic_api * api = (magic_api *) ptr; + + int x,y; + + for (y = 0; y < last->h; y++){ + for (x=0; x < last->w; x++){ + do_noise_pixel(ptr, which, canvas, last, x, y); + } + } +} + +//do the effect for the brush +static void do_noise_brush(void * ptr, int which, SDL_Surface * canvas, SDL_Surface * last, int x, int y){ + int xx, yy; + magic_api * api = (magic_api *) ptr; + + for (yy = y - noise_RADIUS; yy < y + noise_RADIUS; yy++) + { + for (xx = x - noise_RADIUS; xx < x + noise_RADIUS; xx++) + { + if (api->in_circle(xx - x, yy - y, noise_RADIUS) && + !api->touched(xx, yy)) + { + do_noise_pixel(api, which, canvas, last, xx, yy); + } + } + } +} + +// Affect the canvas on drag: +void noise_drag(magic_api * api, int which, SDL_Surface * canvas, + SDL_Surface * last, int ox, int oy, int x, int y, + SDL_Rect * update_rect){ + + api->line((void *) api, which, canvas, last, ox, oy, x, y, 1, do_noise_brush); + + api->playsound(noise_snd_effect[which], (x * 255) / canvas->w, 255); + + if (ox > x) { int tmp = ox; ox = x; x = tmp; } + if (oy > y) { int tmp = oy; oy = y; y = tmp; } + + update_rect->x = ox - noise_RADIUS; + update_rect->y = oy - noise_RADIUS; + update_rect->w = (x + noise_RADIUS) - update_rect->x; + update_rect->h = (y + noise_RADIUS) - update_rect->y; +} + +// Affect the canvas on click: +void noise_click(magic_api * api, int which, int mode, + SDL_Surface * canvas, SDL_Surface * last, + int x, int y, SDL_Rect * update_rect){ + if (mode == MODE_PAINT) + noise_drag(api, which, canvas, last, x, y, x, y, update_rect); + else{ + update_rect->x = 0; + update_rect->y = 0; + update_rect->w = canvas->w; + update_rect->h = canvas->h; + do_noise_full(api, canvas, last, which); + api->playsound(noise_snd_effect[which], 128, 255); + } +} + +// Affect the canvas on release: +void noise_release(magic_api * api, int which, + SDL_Surface * canvas, SDL_Surface * last, + int x, int y, SDL_Rect * update_rect) +{ +} + +// No setup happened: +void noise_shutdown(magic_api * api) +{ + //Clean up sounds + int i; + for(i=0; icanvas_w*api->canvas_h*sizeof(double)); - return(1); } @@ -139,32 +132,53 @@ static int sharpen_grey(Uint8 r1,Uint8 g1,Uint8 b1){ static void do_sharpen_pixel(void * ptr, int which, SDL_Surface * canvas, SDL_Surface * last, int x, int y){ - magic_api * api = (magic_api *) ptr; - Uint8 r1, g1, b1; - //apply normalisation - sharpen_temp[x*(canvas->h-1) + y]= ((sharpen_temp[x*(canvas->h-1) + y]-sharpen_min)/(sharpen_max-sharpen_min))*255.0; + magic_api * api = (magic_api *) ptr; + + Uint8 r1, g1, b1; + int grey; + int i,j; + double sobel_1=0,sobel_2=0; + + //Sobel weighting masks + const int sobel_weights_1[3][3] = { {1,2,1}, + {0,0,0}, + {-1,-2,-1}}; + const int sobel_weights_2[3][3] = { {-1,0,1}, + {-2,0,2}, + {-1,0,1}}; + + sobel_1=0; + sobel_2=0; + for (i=-1;i<2;i++){ + for(j=-1; j<2; j++){ + //No need to check if inside canvas, getpixel does it for us. + SDL_GetRGB(api->getpixel(canvas, x+i, y+j), canvas->format, &r1, &g1, &b1); + grey = sharpen_grey(r1,g1,b1); + sobel_1 += grey * sobel_weights_1[i+1][j+1]; + sobel_2 += grey * sobel_weights_2[i+1][j+1]; + } + } + + double temp = sqrt(sobel_1*sobel_1 + sobel_2*sobel_2); + temp = (temp/1443)*255.0; // set image to white where edge value is below THRESHOLD if (which == TOOL_TRACE){ - if (sharpen_temp[x*(canvas->h-1) + y]putpixel(canvas, x, y, SDL_MapRGB(canvas->format, 255, 255, 255)); } - } //Simply display the edge values - provides a nice black and white silhouette image else if (which == TOOL_SILHOUETTE){ - api->putpixel(canvas, x, y, SDL_MapRGB(canvas->format, sharpen_temp[x*(canvas->h-1) + y], - sharpen_temp[x*(canvas->h-1) + y], - sharpen_temp[x*(canvas->h-1) + y])); + api->putpixel(canvas, x, y, SDL_MapRGB(canvas->format, temp, temp, temp)); } //Add the edge values to the original image, creating a more distinct jump in contrast at edges else if(which == TOOL_SHARPEN){ - SDL_GetRGB(api->getpixel(last, x, y), last->format, &r1, &g1, &b1); - api->putpixel(canvas, x, y, SDL_MapRGB(canvas->format, clamp(0.0, r1 + SHARPEN * sharpen_temp[x*(canvas->h-1) + y], 255.0), - clamp(0.0, g1 + SHARPEN * sharpen_temp[x*(canvas->h-1) + y], 255.0), - clamp(0.0, b1 + SHARPEN * sharpen_temp[x*(canvas->h-1) + y], 255.0))); + SDL_GetRGB(api->getpixel(canvas, x, y), canvas->format, &r1, &g1, &b1); + api->putpixel(canvas, x, y, SDL_MapRGB(canvas->format, clamp(0.0, r1 + SHARPEN * temp, 255.0), + clamp(0.0, g1 + SHARPEN * temp, 255.0), + clamp(0.0, b1 + SHARPEN * temp, 255.0))); } } @@ -251,10 +265,6 @@ void sharpen_shutdown(magic_api * api) Mix_FreeChunk(sharpen_snd_effect[i]); } } - - if (sharpen_temp != NULL){ - free(sharpen_temp); - } } // Record the color from Tux Paint: @@ -270,56 +280,6 @@ int sharpen_requires_colors(magic_api * api, int which) void sharpen_switchin(magic_api * api, int which, int mode, SDL_Surface * canvas) { - int x, y; - int grey; - Uint8 r1, g1, b1; - - //For sobel calculation - int i,j; - int sobel_1,sobel_2; - - //For normalisation - double min=INT_MAX; - double max=0; - - - //Sobel weighting masks - const int sobel_weights_1[3][3] = { {1,2,1}, - {0,0,0}, - {-1,-2,-1}}; - const int sobel_weights_2[3][3] = { {-1,0,1}, - {-2,0,2}, - {-1,0,1}}; - - for (y = 0; y < canvas->h; y++){ - for (x=0; x < canvas->w; x++){ - //Calculate Sobel edge values - - sobel_1=0; - sobel_2=0; - for (i=-1;i<2;i++){ - for(j=-1; j<2; j++){ - //No need to check if inside canvas, getpixel does it for us. - SDL_GetRGB(api->getpixel(canvas, x+i, y+j), canvas->format, &r1, &g1, &b1); - grey = sharpen_grey(r1,g1,b1); - sobel_1 += grey * sobel_weights_1[i+1][j+1]; - sobel_2 += grey * sobel_weights_2[i+1][j+1]; - } - } - - //And store in temp variable - //Cant just write to surface as they may not be 0-255 and surface will clamp them and lose data - sharpen_temp[x*(canvas->h-1) + y] = sqrt(sobel_1*sobel_1 + sobel_2*sobel_2); - - //Calculate normalisation - if (sharpen_temp[x*(canvas->h-1) + y]h-1) + y]; - } - if(sharpen_temp[x*(canvas->h-1) + y]>sharpen_max){ - sharpen_max=sharpen_temp[x*(canvas->h-1) + y]; - } - } - } } void sharpen_switchout(magic_api * api, int which, int mode, SDL_Surface * canvas)