[jdp] 2009-09-18 - Some C code for image processing. Known to work on Windows, but it should be portable. Not particularly clean or well-commented, but it works. http://jpage-images.1.xpdev-hosted.com/iprocess.dll%|%Here is a Windows binary, 10KB, upx-packed%|% (because I hate it when someone posts some C code and I just want to use it without the struggle of compiling on Windows). The command-line for compiling was `tcc -IC:\Tcl\include -DUSE_TCL_STUBS -DUSE_TK_STUBS -v -shared -o iprocess.dll iprocess.c tclStubLib.c tkStubLib.c` Includes procedures (some of which are arguably misnamed) for: * image::avg - Getting the average brightness for the area of an image ** `image::avg ''image'' ''x'' ''y'' ''width'' ''height''` * image::split - Splitting an image into red, green and blue channels ** `image::split ''image'' ''red'' ''green'' ''blue''` ** The images for red, green and blue must exist already and must be the same size as ''image'' * image::join - Joining red, green, and blue channels into a single image ** `image::join ''red'' ''green'' ''blue'' ''image''` * image::hsv - Converting an image to HSV (hue is stored in the red channel, saturation in green, and value in blue) ** `image::hsv ''image''` * effect::dither - Converts the image to horizontal black-and-white bars which vary in width to create the image ** `effect::dither ''image'' ''step''` ** ''step'' is the maximum width of the white bars * effect::supersaturate - If the value for the red channel is > than 50%, it's set to 100%, else 0%. Repeat for all channels. ** `effect::supersaturate ''image''` * effect::blur ** `effect::blur ''image'' ''coeff''` ** ''coeff'' should be between 0 and 1 (but you can get some interesting results by ignoring this) * effect::crisp ** `effect::crisp ''image'' ''coeff''` ** Again, ''coeff'' should be <1 and >0 (but you can get some interesting results by ignoring this) * effect::shade - Similar to supersaturate, only it has more than two ways to go ** `effect::shade ''image'' ''shades''` ** ''shades'' is the number of different shades to allow for each channel * effect::emboss ** `effect::emboss ''image''` * effect::invert ** `effect::invert ''image''` * effect::grey / effect::gray - convert the image to greyscale. (At the C-level, they refer to the same function) ** `effect::grey ''image''` * combine::diff - Takes two images; the resulting image is the difference between them ** `combine::diff ''image1'' ''image2'' ''out''` ** ''image1'', ''image2'' and ''out'' should all be the same sound and already exist * combine::mix - Mixes two images with the specified weight. ** `combine::mix ''image1'' ''image2'' ''out'' ''weight'' ** See combine::diff. Weight is 0.5 for equal mixing; 1 for all ''image1'', and 0 for all ''image2''. I haven't tried making this less than 0 or greater than 1. I am not liable if you try and your computer blows up. :) * combine::alpha - Takes two images, and uses the red channel of the second for the alpha channel of the first. ** `combine::alpha ''image'' ''alpha'' ''out''` ** All arguments must have the same dimensions and preexist. And now for the code (comments and fixes etc. welcome): ====== /* iprocess.c */ #include /* Macros from getting ints and doubles from Tcl_Objs */ #define GET_INT(n,v) if ( Tcl_GetIntFromObj( interp, objv[n], &v ) == TCL_ERROR ) return TCL_ERROR #define GET_DOUBLE(n,v) if ( Tcl_GetDoubleFromObj( interp, objv[n], &v ) == TCL_ERROR ) return TCL_ERROR /* Math macros */ #define MIN(a,b) ( (a) < (b) ? (a) : (b) ) #define MAX(a,b) ( (a) > (b) ? (a) : (b) ) #define THRESHOLD(n,t) ( (n) > (t) ? 255 : 0 ) /* Macros for repetative tasks */ #define WRITE_OUT(h,b) ( Tk_PhotoPutBlock( interp, (h), &(b), 0, 0, (b).width, (b).height, TK_PHOTO_COMPOSITE_SET ) ) #define CMD_ARGS ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[] #define CHECK_ARGS(n,m) if (objc != (n) + 1) { Tcl_WrongNumArgs( interp, 1, objv, m ); return TCL_ERROR; } /* Macros for getting values from an image block... */ /* ... based x and y */ #define RED(p,x,y) ((p)->pixelPtr[(y)*(p)->pitch + (x)*(p)->pixelSize + (p)->offset[0]] ) #define GREEN(p,x,y) ((p)->pixelPtr[(y)*(p)->pitch + (x)*(p)->pixelSize + (p)->offset[1]]) #define BLUE(p,x,y) ((p)->pixelPtr[(y)*(p)->pitch + (x)*(p)->pixelSize + (p)->offset[2]]) /* ... based on pixel number (faster) */ #define R(i,n) ((i).pixelPtr[(n)+(i).offset[0]]) #define G(i,n) ((i).pixelPtr[(n)+(i).offset[1]]) #define B(i,n) ((i).pixelPtr[(n)+(i).offset[2]]) #define A(i,n) ((i).pixelPtr[(n)+(i).offset[3]]) /* ... based on pixel number and bounds-checked (S stands for safe) */ #define SR(i,n) ((n)<0||(n)>(i).width*(i).height*(i).pixelSize?0:(i).pixelPtr[(n)+(i).offset[0]]) #define SG(i,n) ((n)<0||(n)>(i).width*(i).height*(i).pixelSize?0:(i).pixelPtr[(n)+(i).offset[1]]) #define SB(i,n) ((n)<0||(n)>(i).width*(i).height*(i).pixelSize?0:(i).pixelPtr[(n)+(i).offset[2]]) #define SA(i,n) ((n)<0||(n)>(i).width*(i).height*(i).pixelSize?0:(i).pixelPtr[(n)+(i).offset[3]]) int Diff_Cmd(CMD_ARGS); int Gray_Cmd(CMD_ARGS); int Avg_Cmd(CMD_ARGS); int Split_Cmd(CMD_ARGS); int Join_Cmd(CMD_ARGS); int Dither_Cmd(CMD_ARGS); int Sat_Cmd(CMD_ARGS); int Blur_Cmd(CMD_ARGS); int Crisp_Cmd(CMD_ARGS); int HSV_Cmd(CMD_ARGS); int Shade_Cmd(CMD_ARGS); int Mix_Cmd(CMD_ARGS); int Invert_Cmd(CMD_ARGS); int Emboss_Cmd(CMD_ARGS); int AddAlpha_Cmd(CMD_ARGS); __declspec(dllexport) int Iprocess_Init(Tcl_Interp *interp) { /* initialize stubs */ if ( Tcl_InitStubs( interp, "8.1", 0 ) == NULL ) return TCL_ERROR; if ( Tk_InitStubs( interp, "8.1", 0 ) == NULL ) return TCL_ERROR; /* create commands */ /* misc image operations */ Tcl_CreateObjCommand( interp, "image::avg", Avg_Cmd, NULL, NULL ); Tcl_CreateObjCommand( interp, "image::split", Split_Cmd, NULL, NULL ); Tcl_CreateObjCommand( interp, "image::join", Join_Cmd, NULL, NULL ); Tcl_CreateObjCommand( interp, "image::hsv", HSV_Cmd, NULL, NULL ); /* effects */ Tcl_CreateObjCommand( interp, "effect::dither", Dither_Cmd, NULL, NULL ); Tcl_CreateObjCommand( interp, "effect::supersaturate", Sat_Cmd, NULL, NULL ); Tcl_CreateObjCommand( interp, "effect::blur", Blur_Cmd, NULL, NULL ); Tcl_CreateObjCommand( interp, "effect::crisp", Crisp_Cmd, NULL, NULL ); Tcl_CreateObjCommand( interp, "effect::shade", Shade_Cmd, NULL, NULL ); Tcl_CreateObjCommand( interp, "effect::emboss", Emboss_Cmd, NULL, NULL ); Tcl_CreateObjCommand( interp, "effect::invert", Invert_Cmd, NULL, NULL ); Tcl_CreateObjCommand( interp, "effect::gray", Gray_Cmd, NULL, NULL ); Tcl_CreateObjCommand( interp, "effect::grey", Gray_Cmd, NULL, NULL ); /* combinations */ Tcl_CreateObjCommand( interp, "combine::diff", Diff_Cmd, NULL, NULL ); Tcl_CreateObjCommand( interp, "combine::mix", Mix_Cmd, NULL, NULL ); Tcl_CreateObjCommand( interp, "combine::alpha", AddAlpha_Cmd, NULL, NULL ); Tcl_PkgProvide( interp, "iprocess", "0.1" ); return TCL_OK; } int Split_Cmd(CMD_ARGS) { Tk_PhotoHandle hred, hgreen, hblue; Tk_PhotoImageBlock image, red, green, blue; int isize, k; CHECK_ARGS( 4, "image red green blue" ); Tk_PhotoGetImage( Tk_FindPhoto( interp, Tcl_GetString( objv[1] ) ), &image ); hred = Tk_FindPhoto( interp, Tcl_GetString( objv[2] ) ); hgreen = Tk_FindPhoto( interp, Tcl_GetString( objv[3] ) ); hblue = Tk_FindPhoto( interp, Tcl_GetString( objv[4] ) ); Tk_PhotoGetImage( hred, &red ); Tk_PhotoGetImage( hgreen, &green ); Tk_PhotoGetImage( hblue, &blue ); /* at some point add a helpful error message */ if (!( image.width == red.width && image.width == green.width && image.width == blue.width && image.height == red.height && image.height == green.height && image.height == blue.height && image.pixelSize == red.pixelSize && image.pixelSize == green.pixelSize && image.pixelSize == blue.pixelSize )) { return TCL_ERROR; } isize = image.width * image.height * image.pixelSize; k = 0; while ( k < isize ) { R( red, k ) = R( image, k ); G( red, k ) = R( image, k ); B( red, k ) = R( image, k ); R( green, k ) = G( image, k ); G( green, k ) = G( image, k ); B( green, k ) = G( image, k ); R( blue, k ) = B( image, k ); G( blue, k ) = B( image, k ); B( blue, k ) = B( image, k ); k += image.pixelSize; } WRITE_OUT( hred, red ); WRITE_OUT( hgreen, green ); WRITE_OUT( hblue, blue ); return TCL_OK; } int Join_Cmd(CMD_ARGS) { Tk_PhotoHandle himage, hred, hgreen, hblue; Tk_PhotoImageBlock image, red, green, blue; int isize, k; CHECK_ARGS( 4, "red green blue image" ); himage = Tk_FindPhoto( interp, Tcl_GetString( objv[4] ) ); hred = Tk_FindPhoto( interp, Tcl_GetString( objv[1] ) ); hgreen = Tk_FindPhoto( interp, Tcl_GetString( objv[2] ) ); hblue = Tk_FindPhoto( interp, Tcl_GetString( objv[3] ) ); Tk_PhotoGetImage( himage, &image ); Tk_PhotoGetImage( hred, &red ); Tk_PhotoGetImage( hgreen, &green ); Tk_PhotoGetImage( hblue, &blue ); /* at some point add a helpful error message */ if (!( image.width == red.width && image.width == green.width && image.width == blue.width && image.height == red.height && image.height == green.height && image.height == blue.height && image.pixelSize == red.pixelSize && image.pixelSize == green.pixelSize && image.pixelSize == blue.pixelSize )) { return TCL_ERROR; } isize = image.width * image.height * image.pixelSize; k = 0; while ( k < isize ) { R( image, k ) = R( red, k ); G( image, k ) = G( green, k ); B( image, k ) = B( blue, k ); k += image.pixelSize; } WRITE_OUT( himage, image ); return TCL_OK; } int Invert_Cmd( CMD_ARGS ) { Tk_PhotoHandle himage; Tk_PhotoImageBlock image; int isize, k; CHECK_ARGS( 1, "image" ); himage = Tk_FindPhoto( interp, Tcl_GetString( objv[1] ) ); Tk_PhotoGetImage( himage, &image ); isize = image.width * image.height * image.pixelSize; k = 0; while ( k < isize ) { R( image, k ) = 255 - R( image, k ); G( image, k ) = 255 - G( image, k ); B( image, k ) = 255 - B( image, k ); k += image.pixelSize; } WRITE_OUT( himage, image ); return TCL_OK; } int Gray_Cmd(CMD_ARGS) { Tk_PhotoHandle imagehandle; Tk_PhotoImageBlock image, out; int isize, ki, ko; unsigned short avg; CHECK_ARGS( 1, "image" ); imagehandle = Tk_FindPhoto( interp, Tcl_GetString( objv[1] ) ); Tk_PhotoGetImage( imagehandle, &image ); out.width = image.width; out.height = image.height; out.pitch = out.width; out.pixelSize = 1; out.offset[0] = 0; out.offset[1] = 0; out.offset[2] = 0; if ( ( out.pixelPtr = (unsigned char*) attemptckalloc( out.width * out.height * sizeof( unsigned char ) ) ) == NULL ) { Tcl_SetObjResult( interp, Tcl_NewStringObj( "Memory allocation error", 24 ) ); return TCL_ERROR; } isize = image.width * image.height * image.pixelSize; ki = 0; ko = 0; while ( ki < isize ) { avg = (int)( 0.3 * R( image, ki ) + 0.59 * G( image, ki ) + 0.11 * B( image, ki ) ); R( out, ko ) = avg; ki += image.pixelSize; ko += out.pixelSize; } WRITE_OUT( imagehandle, out ); ckfree( out.pixelPtr ); return TCL_OK; } int Avg_Cmd(CMD_ARGS) { Tk_PhotoHandle imagehandle; Tk_PhotoImageBlock image; int isize, k, pixels, x, y, width, height, cx, cy; unsigned long sum = 0; Tcl_Obj *result; CHECK_ARGS( 5, "image x y width height" ); GET_INT( 2, x ); GET_INT( 3, y ); GET_INT( 4, width ); GET_INT( 5, height ); imagehandle = Tk_FindPhoto( interp, Tcl_GetString( objv[1] ) ); Tk_PhotoGetImage( imagehandle, &image ); MIN( width, image.width - x ); MIN( height, image.height - y ); isize = (height + y) * image.pitch + (width + x) * image.pixelSize; cx = 0; k = y * image.pitch + x * image.pixelSize; pixels = width * height; while ( k < isize ) { sum += R( image, k ); if (cx >= width) { k += image.pitch - width * image.pixelSize; cx = 0; } else { k += image.pixelSize; cx++; } } result = Tcl_NewIntObj( (sum / pixels) % 256 ); Tcl_SetObjResult( interp, result ); return TCL_OK; } int Sat_Cmd(CMD_ARGS) { Tk_PhotoHandle imagehandle; Tk_PhotoImageBlock image; int isize, k; CHECK_ARGS( 1, "image" ); imagehandle = Tk_FindPhoto( interp, Tcl_GetString( objv[1] ) ); Tk_PhotoGetImage( imagehandle, &image ); isize = image.width * image.pixelSize + image.height * image.pitch; k = 0; while ( k < isize ) { R( image, k ) = THRESHOLD( R( image, k ), 128 ); G( image, k ) = THRESHOLD( G( image, k ), 128 ); B( image, k ) = THRESHOLD( B( image, k ), 128 ); k += image.pixelSize; } WRITE_OUT( imagehandle, image ); return TCL_OK; } int HSV_Cmd(CMD_ARGS) { Tk_PhotoHandle imagehandle; Tk_PhotoImageBlock image; int isize, k; double r, g, b, max, min, h, s, v; CHECK_ARGS( 1, "image" ); imagehandle = Tk_FindPhoto( interp, Tcl_GetString( objv[1] ) ); Tk_PhotoGetImage( imagehandle, &image ); isize = image.width * image.pixelSize + image.height * image.pitch; k = 0; while ( k < isize ) { r = R( image, k ) / 255.0; g = G( image, k ) / 255.0; b = B( image, k ) / 255.0; max = MAX( MAX( r, g ), b ); min = MIN( MIN( r, g ), b ); if (max == min) { h = 0; } else if (max == r) { h = 60 * ( g - b ) / ( max - min ) + 360; while (h >= 360) { h -= 360; } } else if (max == g) { h = 60 * ( b - r ) / ( max - min ) + 120; } else { h = 60 * ( r - g ) / ( max - min ) + 240; } if (max == 0) { s = 0; } else { s = ( max - min ) / max; } R( image, k ) = (int)( ( h / 360.0 ) * 255 ); G( image, k ) = (int)( s * 255 ); B( image, k ) = (int)( max * 255 ); k += image.pixelSize; } WRITE_OUT( imagehandle, image ); return TCL_OK; } int Emboss_Cmd(CMD_ARGS) { Tk_PhotoHandle himage; Tk_PhotoImageBlock image, out; int isize, k, c, rval, gval, bval; int r[9], g[9], b[9]; CHECK_ARGS( 1, "image" ); himage = Tk_FindPhoto( interp, Tcl_GetString( objv[1] ) ); Tk_PhotoGetImage( himage, &image ); out.width = image.width; out.height = image.height; out.pitch = image.pitch; out.pixelSize = image.pixelSize; for ( c = 0 ; c < sizeof( image.offset ) / sizeof( int ) ; c++ ) out.offset[c] = image.offset[c]; if ( ( out.pixelPtr = (unsigned char*) attemptckalloc( image.width * image.height * image.pixelSize * (sizeof(unsigned char) + 1) ) ) == NULL ) { exit(1); } isize = image.width * image.pixelSize * image.height; k = 0; while ( k < isize ) { r[0] = SR( image, k - image.pitch - image.pixelSize ); g[0] = SG( image, k - image.pitch - image.pixelSize ); b[0] = SB( image, k - image.pitch - image.pixelSize ); r[1] = SR( image, k - image.pitch ); g[1] = SG( image, k - image.pitch ); b[1] = SB( image, k - image.pitch ); r[2] = SR( image, k - image.pitch + image.pixelSize ); g[2] = SG( image, k - image.pitch + image.pixelSize ); b[2] = SB( image, k - image.pitch + image.pixelSize ); r[3] = SR( image, k - image.pixelSize ); g[3] = SG( image, k - image.pixelSize ); b[3] = SB( image, k - image.pixelSize ); r[4] = R( image, k ); g[4] = G( image, k ); b[4] = B( image, k ); r[5] = SR( image, k + image.pixelSize ); g[5] = SG( image, k + image.pixelSize ); b[5] = SB( image, k + image.pixelSize ); r[6] = SR( image, k + image.pitch - image.pixelSize ); g[6] = SG( image, k + image.pitch - image.pixelSize ); b[6] = SB( image, k + image.pitch - image.pixelSize ); r[7] = SR( image, k + image.pitch ); g[7] = SG( image, k + image.pitch ); b[7] = SB( image, k + image.pitch ); r[8] = SR( image, k + image.pitch + image.pixelSize ); g[8] = SG( image, k + image.pitch + image.pixelSize ); b[8] = SB( image, k + image.pitch + image.pixelSize ); rval = 128 + (-r[0] - r[1] + r[2] - r[3] - r[4] + r[5] + r[6] + r[7] + r[8]); gval = 128 + (-g[0] - g[1] + g[2] - g[3] - g[4] + g[5] + g[6] + g[7] + g[8]); bval = 128 + (-b[0] - b[1] + b[2] - b[3] - b[4] + b[5] + b[6] + b[7] + b[8]); rval = MAX( rval, 0 ); gval = MAX( gval, 0 ); bval = MAX( bval, 0 ); rval = MIN( rval, 255 ); gval = MIN( gval, 255 ); bval = MIN( bval, 255 ); c = 0.3 * rval + 0.59 * gval + 0.11 * bval; R( out, k ) = c; G( out, k ) = c; B( out, k ) = c; A( out, k ) = 255; k += image.pixelSize; } WRITE_OUT( himage, out ); ckfree( out.pixelPtr ); return TCL_OK; } int Dither_Cmd(CMD_ARGS) { /* Image handles and blocks */ Tk_PhotoHandle todith; Tk_PhotoImageBlock blorig, blnew; int step, i, ct, k, isize, osize, dx, c; int *ko, *ki; double intens; /* Check args, get args */ CHECK_ARGS( 2, "image step" ); GET_INT( 2, step ); todith = Tk_FindPhoto ( interp, Tcl_GetString( objv[1] ) ); Tk_PhotoGetImage ( todith, &blorig ); /* fill in blnew */ blnew.width = blorig.width; blnew.height = blorig.height; blnew.pitch = blnew.width; blnew.pixelSize = 1; blnew.offset[0] = 0; blnew.offset[1] = 0; blnew.offset[2] = 0; /* set isize (image size) and osize (output size) in bytes */ isize = blorig.height * blorig.pitch + blorig.width * blorig.pixelSize; osize = blnew.height * blnew.pitch + blnew.width * blnew.pixelSize; /* Allocate memory for new image */ if ( ( blnew.pixelPtr = (unsigned char*) calloc( osize, sizeof( unsigned char ) ) ) == NULL ) { Tcl_SetObjResult( interp, Tcl_NewStringObj( "Memory allocation error", 24 ) ); return TCL_ERROR; } /* Allocate memory for position markers */ if ( ( ki = (int *) calloc( step, sizeof( int ) ) ) == NULL ) { Tcl_SetObjResult( interp, Tcl_NewStringObj( "Memory allocation error", 24 ) ); return TCL_ERROR; } if ( ( ko = (int *) calloc( step, sizeof( int ) ) ) == NULL ) { Tcl_SetObjResult( interp, Tcl_NewStringObj( "Memory allocation error", 24 ) ); return TCL_ERROR; } /* Initialize position markers */ for ( c = 0 ; c < step ; c++ ) { ki[c] = c * blorig.pitch; ko[c] = c * blnew.pitch; } /* ki[c] and ko[c] are the corresponding pixels in the in and out images */ while ( ko[step - 1] < osize && ki[step - 1] < isize ) { for ( dx = 0 ; dx < blorig.width ; dx++ ) { /* calculate the intensity of greyness on a scale of 0.0 to 1.0 */ for ( intens = 0.0, c = 0 ; c < step ; c++ ) intens += 0.3 * R( blorig, ki[c] ) + 0.59 * G( blorig, ki[c] ) + 0.11 * B( blorig, ki[c] ); intens /= 255.0 * step; i = (int)( intens * (step - 2) ) + 2; ct = ( step - i ) / 2; for ( c = ct ; c < ct + i ; c++ ) { R( blnew, ko[c] ) = 255; } for ( c = 0 ; c < step ; c++ ) { ki[c] += blorig.pixelSize; ko[c] += blnew.pixelSize; } } for ( c = 0 ; c < step ; c++ ) { ki[c] += blorig.pitch * step; ko[c] += blnew.pitch * step; } } WRITE_OUT( todith, blnew ); free( blnew.pixelPtr ); free( ki ); free( ko ); return TCL_OK; } int Shade_Cmd(CMD_ARGS) { Tk_PhotoHandle imagehandle; Tk_PhotoImageBlock image; int isize, k, c, rval, gval, bval, shades, shinterval; CHECK_ARGS( 2, "image shades" ); imagehandle = Tk_FindPhoto( interp, Tcl_GetString( objv[1] ) ); Tk_PhotoGetImage( imagehandle, &image ); GET_INT( 2, shades ); shinterval = 255 / shades; isize = image.width * image.pixelSize * image.height; k = 0; while ( k < isize ) { rval = R( image, k ); gval = G( image, k ); bval = B( image, k ); rval = (int)(rval / shinterval) * shinterval; gval = (int)(gval / shinterval) * shinterval; bval = (int)(bval / shinterval) * shinterval; R( image, k ) = rval; G( image, k ) = gval; B( image, k ) = bval; k += image.pixelSize; } WRITE_OUT( imagehandle, image ); return TCL_OK; } int Blur_Cmd(CMD_ARGS) { Tk_PhotoHandle imagehandle; Tk_PhotoImageBlock image, out; int isize, k, c, rval, gval, bval, aval; int r[9], g[9], b[9], a[9]; double coeff; CHECK_ARGS( 2, "image coeff" ); imagehandle = Tk_FindPhoto( interp, Tcl_GetString( objv[1] ) ); Tk_PhotoGetImage( imagehandle, &image ); GET_DOUBLE( 2, coeff ); coeff = MAX( 0, coeff ); coeff = MIN( 255, coeff ); out.width = image.width; out.height = image.height; out.pitch = image.pitch; out.pixelSize = image.pixelSize; for ( c = 0 ; c < sizeof( image.offset ) / sizeof( int ) ; c++ ) out.offset[c] = image.offset[c]; if ( ( out.pixelPtr = (unsigned char*) attemptckalloc( image.width * image.height * image.pixelSize * (sizeof(unsigned char) + 1) ) ) == NULL ) { Tcl_SetObjResult( interp, Tcl_NewStringObj( "Memory allocation error", 24 ) ); return TCL_ERROR; } isize = image.width * image.pixelSize * image.height; k = 0; while ( k < isize ) { for ( c = 0 ; c < 9 ; c++ ) { r[c] = 0; g[c] = 0; b[c] = 0; } r[0] = SR( image, k - image.pitch * 2 - image.pixelSize * 2 ); g[0] = SG( image, k - image.pitch * 2 - image.pixelSize * 2 ); b[0] = SB( image, k - image.pitch * 2 - image.pixelSize * 2 ); a[0] = SA( image, k - image.pitch * 2 - image.pixelSize * 2 ); r[1] = SR( image, k - image.pitch ); g[1] = SG( image, k - image.pitch ); b[1] = SB( image, k - image.pitch ); a[1] = SA( image, k - image.pitch ); r[2] = SR( image, k - image.pitch * 2 + image.pixelSize * 2 ); g[2] = SG( image, k - image.pitch * 2 + image.pixelSize * 2 ); b[2] = SB( image, k - image.pitch * 2 + image.pixelSize * 2 ); a[2] = SA( image, k - image.pitch * 2 + image.pixelSize * 2 ); r[3] = SR( image, k - image.pixelSize ); g[3] = SG( image, k - image.pixelSize ); b[3] = SB( image, k - image.pixelSize ); a[3] = SA( image, k - image.pixelSize ); r[4] = R( image, k ); g[4] = G( image, k ); b[4] = B( image, k ); a[4] = A( image, k ); r[5] = SR( image, k + image.pixelSize ); g[5] = SG( image, k + image.pixelSize ); b[5] = SB( image, k + image.pixelSize ); a[5] = SA( image, k + image.pixelSize ); r[6] = SR( image, k + image.pitch * 2 - image.pixelSize * 2 ); g[6] = SG( image, k + image.pitch * 2 - image.pixelSize * 2 ); b[6] = SB( image, k + image.pitch * 2 - image.pixelSize * 2 ); a[6] = SA( image, k + image.pitch * 2 - image.pixelSize * 2 ); r[7] = SR( image, k + image.pitch ); g[7] = SG( image, k + image.pitch ); b[7] = SB( image, k + image.pitch ); a[7] = SA( image, k + image.pitch ); r[8] = SR( image, k + image.pitch * 2 + image.pixelSize * 2 ); g[8] = SG( image, k + image.pitch * 2 + image.pixelSize * 2 ); b[8] = SB( image, k + image.pitch * 2 + image.pixelSize * 2 ); a[8] = SA( image, k + image.pitch * 2 + image.pixelSize * 2 ); rval = (1 - coeff) * r[4] + coeff/8 * (r[0] + r[1] + r[2] + r[3] + r[5] + r[6] + r[7] + r[8]); gval = (1 - coeff) * g[4] + coeff/8 * (g[0] + g[1] + g[2] + g[3] + g[5] + g[6] + g[7] + g[8]); bval = (1 - coeff) * b[4] + coeff/8 * (b[0] + b[1] + b[2] + b[3] + b[5] + b[6] + b[7] + b[8]); aval = (1 - coeff) * a[4] + coeff/8 * (a[0] + a[1] + a[2] + a[3] + a[5] + a[6] + a[7] + a[8]); rval = MAX( rval, 0 ); gval = MAX( gval, 0 ); bval = MAX( bval, 0 ); aval = MAX( aval, 0 ); rval = MIN( rval, 255 ); gval = MIN( gval, 255 ); bval = MIN( bval, 255 ); aval = MIN( aval, 255 ); R( out, k ) = rval; G( out, k ) = gval; B( out, k ) = bval; A( out, k ) = aval; k += image.pixelSize; } WRITE_OUT( imagehandle, out ); ckfree( out.pixelPtr ); return TCL_OK; } int Crisp_Cmd(CMD_ARGS) { Tk_PhotoHandle imagehandle; Tk_PhotoImageBlock image, out; int isize, k, c, rval, gval, bval, aval; int r[9], g[9], b[9], a[9]; double coeff; CHECK_ARGS( 2, "image coeff" ); imagehandle = Tk_FindPhoto( interp, Tcl_GetString( objv[1] ) ); Tk_PhotoGetImage( imagehandle, &image ); GET_DOUBLE( 2, coeff ); coeff = MAX( 0, coeff ); coeff = MIN( 255, coeff ); out.width = image.width; out.height = image.height; out.pitch = image.pitch; out.pixelSize = image.pixelSize; for ( c = 0 ; c < sizeof( image.offset ) / sizeof( int ) ; c++ ) out.offset[c] = image.offset[c]; if ( ( out.pixelPtr = (unsigned char*) attemptckalloc( image.width * image.height * image.pixelSize * (sizeof(unsigned char) + 1) ) ) == NULL ) { Tcl_SetObjResult( interp, Tcl_NewStringObj( "Memory allocation error", 24 ) ); return TCL_ERROR; } isize = image.width * image.pixelSize * image.height; k = 0; while ( k < isize ) { for ( c = 0 ; c < 9 ; c++ ) { r[c] = 0; g[c] = 0; b[c] = 0; } r[0] = SR( image, k - image.pitch - image.pixelSize ); g[0] = SG( image, k - image.pitch - image.pixelSize ); b[0] = SB( image, k - image.pitch - image.pixelSize ); a[0] = SA( image, k - image.pitch - image.pixelSize ); r[1] = SR( image, k - image.pitch ); g[1] = SG( image, k - image.pitch ); b[1] = SB( image, k - image.pitch ); a[1] = SA( image, k - image.pitch ); r[2] = SR( image, k - image.pitch + image.pixelSize ); g[2] = SG( image, k - image.pitch + image.pixelSize ); b[2] = SB( image, k - image.pitch + image.pixelSize ); a[2] = SA( image, k - image.pitch + image.pixelSize ); r[3] = SR( image, k - image.pixelSize ); g[3] = SG( image, k - image.pixelSize ); b[3] = SB( image, k - image.pixelSize ); a[3] = SA( image, k - image.pixelSize ); r[4] = R( image, k ); g[4] = G( image, k ); b[4] = B( image, k ); a[4] = A( image, k ); r[5] = SR( image, k + image.pixelSize ); g[5] = SG( image, k + image.pixelSize ); b[5] = SB( image, k + image.pixelSize ); a[5] = SA( image, k + image.pixelSize ); r[6] = SR( image, k + image.pitch - image.pixelSize ); g[6] = SG( image, k + image.pitch - image.pixelSize ); b[6] = SB( image, k + image.pitch - image.pixelSize ); a[6] = SA( image, k + image.pitch - image.pixelSize ); r[7] = SR( image, k + image.pitch ); g[7] = SG( image, k + image.pitch ); b[7] = SB( image, k + image.pitch ); a[7] = SA( image, k + image.pitch ); r[8] = SR( image, k + image.pitch + image.pixelSize ); g[8] = SG( image, k + image.pitch + image.pixelSize ); b[8] = SB( image, k + image.pitch + image.pixelSize ); a[8] = SA( image, k + image.pitch + image.pixelSize ); rval = coeff * r[4] - (1 - coeff)/8 * (r[0] + r[1] + r[2] + r[3] + r[5] + r[6] + r[7] + r[8]); gval = coeff * g[4] - (1 - coeff)/8 * (g[0] + g[1] + g[2] + g[3] + g[5] + g[6] + g[7] + g[8]); bval = coeff * b[4] - (1 - coeff)/8 * (b[0] + b[1] + b[2] + b[3] + b[5] + b[6] + b[7] + b[8]); aval = coeff * a[4] - (1 - coeff)/8 * (a[0] + a[1] + a[2] + a[3] + a[5] + a[6] + a[7] + a[8]); rval = MAX( rval, 0 ); gval = MAX( gval, 0 ); bval = MAX( bval, 0 ); aval = MAX( aval, 0 ); rval = MIN( rval, 255 ); gval = MIN( gval, 255 ); bval = MIN( bval, 255 ); aval = MIN( aval, 255 ); R( out, k ) = rval; G( out, k ) = gval; B( out, k ) = bval; A( out, k ) = aval; k += image.pixelSize; } WRITE_OUT( imagehandle, out ); ckfree( out.pixelPtr ); return TCL_OK; } int Diff_Cmd(CMD_ARGS) { Tk_PhotoHandle outhandle; Tk_PhotoImageBlock image1, image2, out; int isize, k; CHECK_ARGS ( 3, "image1 image2 out" ); Tk_PhotoGetImage( Tk_FindPhoto( interp, Tcl_GetString( objv[1] ) ), &image1 ); Tk_PhotoGetImage( Tk_FindPhoto( interp, Tcl_GetString( objv[2] ) ), &image2 ); outhandle = Tk_FindPhoto( interp, Tcl_GetString( objv[3] ) ); Tk_PhotoGetImage( outhandle, &out ); /* at some point add a helpful error message */ if (!( image1.width == image2.width && image1.width == out.width && image1.height == image2.height && image1.height == out.height && image1.pixelSize == image2.pixelSize && image1.pixelSize == out.pixelSize )) { return TCL_ERROR; } isize = image1.width * image1.height * image1.pixelSize; k = 0; while ( k < isize ) { R( out, k ) = abs( R( image1, k ) - R( image2, k ) ); G( out, k ) = abs( G( image1, k ) - G( image2, k ) ); B( out, k ) = abs( B( image1, k ) - B( image2, k ) ); k += image1.pixelSize; } WRITE_OUT( outhandle, out ); return TCL_OK; } int Mix_Cmd(CMD_ARGS) { Tk_PhotoHandle outhandle; Tk_PhotoImageBlock image1, image2, out; int isize, k; double weight, rweight; CHECK_ARGS ( 4, "image1 image2 out weight" ); Tk_PhotoGetImage( Tk_FindPhoto( interp, Tcl_GetString( objv[1] ) ), &image1 ); Tk_PhotoGetImage( Tk_FindPhoto( interp, Tcl_GetString( objv[2] ) ), &image2 ); outhandle = Tk_FindPhoto( interp, Tcl_GetString( objv[3] ) ); Tk_PhotoGetImage( outhandle, &out ); GET_DOUBLE( 4, weight ); weight = MAX( 0, weight ); weight = MIN( 1, weight ); rweight = 1 - weight; /* at some point add a helpful error message */ if (!( image1.width == image2.width && image1.width == out.width && image1.height == image2.height && image1.height == out.height && image1.pixelSize == image2.pixelSize && image1.pixelSize == out.pixelSize )) { return TCL_ERROR; } isize = image1.width * image1.height * image1.pixelSize; k = 0; while ( k < isize ) { R( out, k ) = R( image1, k ) * weight + R( image2, k ) * rweight; G( out, k ) = G( image1, k ) * weight + G( image2, k ) * rweight; B( out, k ) = B( image1, k ) * weight + B( image2, k ) * rweight; k += image1.pixelSize; } WRITE_OUT( outhandle, out ); return TCL_OK; } int AddAlpha_Cmd(CMD_ARGS) { Tk_PhotoHandle outhandle; Tk_PhotoImageBlock image, alpha, out; int isize, k; CHECK_ARGS ( 3, "image alpha out" ); Tk_PhotoGetImage( Tk_FindPhoto( interp, Tcl_GetString( objv[1] ) ), &image ); Tk_PhotoGetImage( Tk_FindPhoto( interp, Tcl_GetString( objv[2] ) ), &alpha ); outhandle = Tk_FindPhoto( interp, Tcl_GetString( objv[3] ) ); /* at some point add a helpful error message */ if (!( image.width == alpha.width && image.height == alpha.height && image.pixelSize == alpha.pixelSize )) { return TCL_ERROR; } out.width = image.width; out.height = image.height; out.pitch = out.width * 4; out.pixelSize = 4; out.offset[0] = 0; out.offset[1] = 1; out.offset[2] = 2; out.offset[3] = 3; /* Allocate memory for new image */ if ( ( out.pixelPtr = (unsigned char*) calloc( out.width * 4 + out.height * out.pitch, sizeof( unsigned char ) ) ) == NULL ) { Tcl_SetObjResult( interp, Tcl_NewStringObj( "Memory allocation error", 24 ) ); return TCL_ERROR; } isize = image.width * image.height * image.pixelSize; k = 0; while ( k < isize ) { R( out, k ) = R( image, k ); G( out, k ) = G( image, k ); B( out, k ) = B( image, k ); A( out, k ) = R( alpha, k ); k += image.pixelSize; } WRITE_OUT( outhandle, out ); return TCL_OK; } ====== <> [Category Image Processing]