//============================================== // copyright : (C) 2003-2005 by Will Stokes //============================================== // 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. //============================================== //Systemwide includes #include #include #include #include //Projectwide includes #include "pointillism.h" #include "blackWhite.h" #include "manipulationOptions.h" //---------------------------------------------- // Inputs: // ------- // QString filename - location of original image on disk // StatusWidget* status - widget for making progress visible to user // // Outputs: // -------- // QImage* returned - constructed image // // Description: // ------------ // This method constructs an image using a pointillism approach using luminance // data from the original image. // // This effect is under heavy development and will be documented // in full when it is complete. //---------------------------------------------- //============================================== void pickRandomPixelWithinBlock( int width, int height, int blockX, int blockY, int BLOCK_SIZE, int &x, int &y ) { int dx = rand() % BLOCK_SIZE; int dy = rand() % BLOCK_SIZE; x = blockX*BLOCK_SIZE + dx; y = blockY*BLOCK_SIZE + dy; if(x < 0) x = 0; if(y < 0) y = 0; if(x >= width ) x = width-1; if(y >= height) y = height-1; } //---------------------------------------------- bool pixelValid( QImage* image, int x, int y ) { return ( x >= 0 && y >= 0 && x < image->width() && x < image->height() ); } //---------------------------------------------- double computeLocalGrayVal( QImage* image, int x, int y ) { int weights[3][3] = { {1,2,1}, {2,4,2}, {1,2,1} }; int divisorSum = 0; double sum = 0; int xp, yp; for(yp = QMAX( y-1, 0); yp < QMIN( image->height()-1, y+1 ); yp++) { uchar* scanLine = image->scanLine(yp); for(xp = QMAX( x-1, 0); xp< QMIN( image->width()-1, x+1 ); xp++) { //compute dx and dy values int dx = xp - x; int dy = yp - y; //compute weight index int weightX = dx+1; int weightY = dy+1; //update sum and divisor count sum+= (weights[weightX][weightY] * qGray( *((QRgb*)scanLine+xp) ) ); divisorSum+= weights[weightX][weightY]; } } //return weighted average return sum/divisorSum; } //---------------------------------------------- void drawDotAt( QImage* image, int x, int y, int ) { //TODO: antialias over grid, for now //just update this pixel value uchar* scanLine = image->scanLine(y); QRgb* rgb = ((QRgb*)scanLine+x); int red = qRed(*rgb); red = (int) (0.6*red); *rgb = qRgb( red, red, red); } //---------------------------------------------- QImage* pointillismEffect( QString filename, ManipulationOptions* ) { //intialize seed using current time srand( unsigned(time(NULL)) ); //load original image and convert to grayscale QImage* originalImage = blackWhiteEffect( filename, NULL ); //construct edited image QImage* editedImage = new QImage( originalImage->width(), originalImage->height(), originalImage->depth() ); //fill with white since we'll be drawing black/color dots on top editedImage->fill( qRgb(255,255,255) ); //break image into BLOCK_SIZE x BLOCK_SIZE blocks. iterate over //each block and pick a random pixel within. Local //average gray value in edited image is > originalImage + thresh //then draw a dot at pixel. continue doing this for each block //and repeat until ??? const int BLOCK_SIZE = 8; //compute image size in blocks int blocksWide = editedImage->width() / BLOCK_SIZE; if(blocksWide*BLOCK_SIZE < editedImage->width()) { blocksWide++; } int blocksTall = editedImage->height() / BLOCK_SIZE; if(blocksTall*BLOCK_SIZE < editedImage->height()) { blocksTall++; } //iterate over image say 100 times, we'll need to fix this outer loop to be smarter? int bx,by,x,y; for(int i=0; i<10; i++) { //iterate over all blocks for(bx=0; bxwidth(), editedImage->height(), bx, by, BLOCK_SIZE, x, y ); double curGrayVal = computeLocalGrayVal( editedImage, x, y ); double goalGrayVal = computeLocalGrayVal( originalImage, x, y ); //too bright -> draw dot if( curGrayVal > goalGrayVal ) { drawDotAt( editedImage, x, y, 5 ); } } } } //free grayscale form of original image delete originalImage; originalImage = NULL; //return pointer to edited image return editedImage; } //==============================================