// Last updated 2010/08/02 16:20 // This is derived from a PHP script at: // http://eclecticdjs.com/mike/tutorials/php/imagemagick/imagickpixeliterator/3D_landscape.php #include <windows.h> #include <wand/magick_wand.h> #include <time.h> void test_wand(LPTSTR lpCmdLine) { MagickWand *image = NULL, *canvas = NULL; DrawingWand *line = NULL; PixelWand *pw = NULL; int w,h; int r,g,b,grey,offset; int start_y,end_y; int x,y; int line_height; char *url,*file; MagickWandGenesis(); // input image // The input image is at: "http://eclecticdjs.com/mike/temp/ball/fract6.jpg" url = "fract6.jpg"; // output image file = "3d_fractal.jpg"; image = NewMagickWand(); pw = NewPixelWand(); MagickReadImage(image,url); // scale it down w = (int) MagickGetImageWidth(image); h = (int) MagickGetImageHeight(image); PixelSetColor(pw,"transparent"); if(MagickShearImage(image,pw,45,0) == MagickFalse) MessageBox(NULL,"B - Shear failed","",MB_OK); w = (int) MagickGetImageWidth(image); h = (int) MagickGetImageHeight(image); // scale it to make it look like it is laying down if(MagickScaleImage(image,w,h/2) == MagickFalse) MessageBox(NULL,"C - Scale failed","",MB_OK); // Get image stats w = (int) MagickGetImageWidth(image); h = (int) MagickGetImageHeight(image); // Make a blank canvas to draw on canvas = NewMagickWand(); // Use a colour from the input image MagickGetImagePixelColor(image,0,0,pw); MagickNewImage(canvas,w,h*2,pw); offset = h; // The original drawing method was to go along each row from top to bottom so that // a line in the "front" (which is one lower down the picture) will be drawn over // one behind it. // The problem with this method is that every line is drawn even if it will be covered // up by a line "in front" of it later on. // The method used here goes up each column from left to right and only draws a line if // it is longer than everything drawn so far in this column and will therefore be visible. // With the new drawing method this takes 13 secs - the previous method took 59 secs // loop through all points in image for(x=0;x<w;x++) { // The PHP version created, used and destroyed the drawingwand inside // the inner loop but it is about 25% faster to do only the DrawLine // inside the loop line = NewDrawingWand(); line_height = 0; for(y=h-1;y>=0;y--) { // get (r,g,b) and grey value if(MagickGetImagePixelColor(image,x,y,pw) == MagickFalse)continue; // 255* adjusts the rgb values to Q8 even if the IM being used is Q16 r = (int) (255*PixelGetRed(pw)); g = (int) (255*PixelGetGreen(pw)); b = (int) (255*PixelGetBlue(pw)); // Calculate grayscale - a divisor of 10-25 seems to work well. // grey = (r+g+b)/25; grey = (r+g+b)/15; // grey = (r+g+b)/10; // Only draw a line if it will show "above" what's already been done if(line_height == 0 || line_height < grey) { DrawSetFillColor(line,pw); DrawSetStrokeColor(line,pw); // Draw the part of the line that is visible start_y = y+offset - line_height; end_y = y-grey+offset; DrawLine(line,x,start_y,x,end_y); line_height = grey; } line_height--; } // Draw the lines on the image MagickDrawImage(canvas,line); DestroyDrawingWand(line); } MagickScaleImage(canvas,w-h,h*2); // write canvas MagickWriteImage(canvas,file); // clean up DestroyMagickWand(canvas); DestroyMagickWand(image); DestroyPixelWand(pw); MagickWandTerminus(); }