File indexing completed on 2024-05-19 04:22:56

0001 // functions taken from qimageblitz (no longer maintained)
0002 
0003 /*
0004  Copyright (C) 1998, 1999, 2001, 2002, 2004, 2005, 2007
0005       Daniel M. Duley <daniel.duley@verizon.net>
0006  (C) 2004 Zack Rusin <zack@kde.org>
0007  (C) 2000 Josef Weidendorfer <weidendo@in.tum.de>
0008  (C) 1999 Geert Jansen <g.t.jansen@stud.tue.nl>
0009  (C) 1998, 1999 Christian Tibirna <ctibirna@total.net>
0010  (C) 1998, 1999 Dirk Mueller <mueller@kde.org>
0011 
0012 Redistribution and use in source and binary forms, with or without
0013 modification, are permitted provided that the following conditions
0014 are met:
0015 
0016 1. Redistributions of source code must retain the above copyright
0017    notice, this list of conditions and the following disclaimer.
0018 2. Redistributions in binary form must reproduce the above copyright
0019    notice, this list of conditions and the following disclaimer in the
0020    documentation and/or other materials provided with the distribution.
0021 
0022 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
0023 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0024 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
0025 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
0026 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
0027 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0028 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
0029 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0030 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
0031 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0032 
0033 */
0034 
0035 /*
0036  Portions of this software were originally based on ImageMagick's
0037  algorithms. ImageMagick is copyrighted under the following conditions:
0038 
0039 Copyright (C) 2003 ImageMagick Studio, a non-profit organization dedicated to
0040 making software imaging solutions freely available.
0041 
0042 Permission is hereby granted, free of charge, to any person obtaining a copy
0043 of this software and associated documentation files ("ImageMagick"), to deal
0044 in ImageMagick without restriction, including without limitation the rights
0045 to use, copy, modify, merge, publish, distribute, sublicense,  and/or sell
0046 copies of ImageMagick, and to permit persons to whom the ImageMagick is
0047 furnished to do so, subject to the following conditions:
0048 
0049 The above copyright notice and this permission notice shall be included in all
0050 copies or substantial portions of ImageMagick.
0051 
0052 The software is provided "as is", without warranty of any kind, express or
0053 implied, including but not limited to the warranties of merchantability,
0054 fitness for a particular purpose and noninfringement.  In no event shall
0055 ImageMagick Studio be liable for any claim, damages or other liability,
0056 whether in an action of contract, tort or otherwise, arising from, out of or
0057 in connection with ImageMagick or the use or other dealings in ImageMagick.
0058 
0059 Except as contained in this notice, the name of the ImageMagick Studio shall
0060 not be used in advertising or otherwise to promote the sale, use or other
0061 dealings in ImageMagick without prior written authorization from the
0062 ImageMagick Studio.
0063 */
0064 
0065 #include "blitz.h"
0066 
0067 #include <QColor>
0068 #include <cmath>
0069 
0070 #define M_SQ2PI 2.50662827463100024161235523934010416269302368164062
0071 #define M_EPSILON 1.0e-6
0072 
0073 #define CONVOLVE_ACC(weight, pixel) \
0074     r+=((weight))*(qRed((pixel))); g+=((weight))*(qGreen((pixel))); \
0075     b+=((weight))*(qBlue((pixel)));
0076 
0077 //--------------------------------------------------------------------------------
0078 
0079 inline QRgb convertFromPremult(QRgb p)
0080 {
0081     int alpha = qAlpha(p);
0082     return(!alpha ? 0 : qRgba(255*qRed(p)/alpha,
0083                               255*qGreen(p)/alpha,
0084                               255*qBlue(p)/alpha,
0085                               alpha));
0086 }
0087 
0088 //--------------------------------------------------------------------------------
0089 
0090 inline QRgb convertToPremult(QRgb p)
0091 {
0092     unsigned int a = p >> 24;
0093     unsigned int t = (p & 0xff00ff) * a;
0094     t = (t + ((t >> 8) & 0xff00ff) + 0x800080) >> 8;
0095     t &= 0xff00ff;
0096 
0097     p = ((p >> 8) & 0xff) * a;
0098     p = (p + ((p >> 8) & 0xff) + 0x80);
0099     p &= 0xff00;
0100     p |= t | (a << 24);
0101     return(p);
0102 }
0103 
0104 //--------------------------------------------------------------------------------
0105 // These are used as accumulators
0106 
0107 typedef struct
0108 {
0109     quint32 red, green, blue, alpha;
0110 } IntegerPixel;
0111 
0112 typedef struct
0113 {
0114     // Yes, a normal pixel can be used instead but this is easier to read
0115     // and no shifts to get components.
0116     quint8 red, green, blue, alpha;
0117 } CharPixel;
0118 
0119 typedef struct
0120 {
0121     quint32 red, green, blue, alpha;
0122 } HistogramListItem;
0123 
0124 
0125 bool equalize(QImage &img)
0126 {
0127     if(img.isNull()) {
0128         return(false);
0129     }
0130 
0131     HistogramListItem *histogram;
0132     IntegerPixel *map;
0133     IntegerPixel intensity, high, low;
0134     CharPixel *equalize_map;
0135     int i, count;
0136     QRgb pixel, *dest;
0137     unsigned char r, g, b;
0138 
0139     if(img.depth() < 32){
0140         img.convertTo(img.hasAlphaChannel() ? QImage::Format_ARGB32
0141                                             : QImage::Format_RGB32);
0142     }
0143     count = img.width()*img.height();
0144 
0145     map = new IntegerPixel[256];
0146     histogram = new HistogramListItem[256];
0147     equalize_map = new CharPixel[256];
0148 
0149     // form histogram
0150     memset(histogram, 0, 256*sizeof(HistogramListItem));
0151     dest = reinterpret_cast<QRgb *>(img.bits());
0152 
0153     if(img.format() == QImage::Format_ARGB32_Premultiplied){
0154         for(i=0; i < count; ++i, ++dest){
0155             pixel = convertFromPremult(*dest);
0156             histogram[qRed(pixel)].red++;
0157             histogram[qGreen(pixel)].green++;
0158             histogram[qBlue(pixel)].blue++;
0159             histogram[qAlpha(pixel)].alpha++;
0160         }
0161     }
0162     else{
0163         for(i=0; i < count; ++i){
0164             pixel = *dest++;
0165             histogram[qRed(pixel)].red++;
0166             histogram[qGreen(pixel)].green++;
0167             histogram[qBlue(pixel)].blue++;
0168             histogram[qAlpha(pixel)].alpha++;
0169         }
0170     }
0171 
0172     // integrate the histogram to get the equalization map
0173     memset(&intensity, 0, sizeof(IntegerPixel));
0174     for(i=0; i < 256; ++i){
0175         intensity.red += histogram[i].red;
0176         intensity.green += histogram[i].green;
0177         intensity.blue += histogram[i].blue;
0178         map[i] = intensity;
0179     }
0180 
0181     low = map[0];
0182     high = map[255];
0183     memset(equalize_map, 0, 256*sizeof(CharPixel));
0184     for(i=0; i < 256; ++i){
0185         if(high.red != low.red) {
0186             equalize_map[i].red = static_cast<unsigned char>
0187                     ((255*(map[i].red-low.red))/(high.red-low.red));
0188         }
0189         if(high.green != low.green) {
0190             equalize_map[i].green = static_cast<unsigned char>
0191                     ((255*(map[i].green-low.green))/(high.green-low.green));
0192         }
0193         if(high.blue != low.blue) {
0194             equalize_map[i].blue = static_cast<unsigned char>
0195                     ((255*(map[i].blue-low.blue))/(high.blue-low.blue));
0196         }
0197     }
0198 
0199     // stretch the histogram and write
0200     dest = reinterpret_cast<QRgb *>(img.bits());
0201     if(img.format() == QImage::Format_ARGB32_Premultiplied){
0202         for(i=0; i < count; ++i, ++dest){
0203             pixel = convertFromPremult(*dest);
0204             r = static_cast<unsigned char> ((low.red != high.red) ?
0205                                                 equalize_map[qRed(pixel)].red : qRed(pixel));
0206 
0207             g = static_cast<unsigned char> ((low.green != high.green) ?
0208                                                 equalize_map[qGreen(pixel)].green : qGreen(pixel));
0209 
0210             b = static_cast<unsigned char> ((low.blue != high.blue) ?
0211                                                 equalize_map[qBlue(pixel)].blue : qBlue(pixel));
0212 
0213             *dest = convertToPremult(qRgba(r, g, b, qAlpha(pixel)));
0214         }
0215     }
0216     else{
0217         for(i=0; i < count; ++i){
0218             pixel = *dest;
0219             r = static_cast<unsigned char> ((low.red != high.red) ?
0220                                                 equalize_map[qRed(pixel)].red : qRed(pixel));
0221 
0222             g = static_cast<unsigned char> ((low.green != high.green) ?
0223                                                 equalize_map[qGreen(pixel)].green : qGreen(pixel));
0224 
0225             b = static_cast<unsigned char> ((low.blue != high.blue) ?
0226                                                 equalize_map[qBlue(pixel)].blue : qBlue(pixel));
0227 
0228             *dest++ = qRgba(r, g, b, qAlpha(pixel));
0229         }
0230     }
0231 
0232     delete[] histogram;
0233     delete[] map;
0234     delete[] equalize_map;
0235     return(true);
0236 }
0237 
0238 //--------------------------------------------------------------------------------
0239 
0240 QImage Blitz::blur(QImage &img, int radius)
0241 {
0242     if (img.isNull()) {
0243         return (img);
0244     }
0245 
0246     if (img.depth() < 8) {
0247         img.convertTo(QImage::Format_Indexed8);
0248     }
0249 
0250     QVector<QRgb> colorTable;
0251     if (img.format() == QImage::Format_Indexed8) {
0252         colorTable = img.colorTable();
0253     }
0254 
0255     auto width = img.width();
0256     auto height = img.height();
0257 
0258     QImage buffer(width, height, img.hasAlphaChannel() ? QImage::Format_ARGB32 : QImage::Format_RGB32);
0259 
0260     const auto img_format = img.format();
0261 
0262     int *as = new int[width];
0263     int *rs = new int[width];
0264     int *gs = new int[width];
0265     int *bs = new int[width];
0266 
0267     QRgb *p1 , *p2;
0268 
0269     for (auto y = 0; y < height; ++y) {
0270         auto my = y - radius;
0271         auto mh = (radius << 1) + 1;
0272 
0273         if (my < 0) {
0274             mh += my;
0275             my = 0;
0276         }
0277 
0278         if ((my + mh) > height) {
0279             mh = height - my;
0280         }
0281 
0282         p1 = reinterpret_cast<QRgb *>(buffer.scanLine(y));
0283 
0284         memset(as, 0, static_cast<unsigned int>(width) * sizeof(int));
0285         memset(rs, 0, static_cast<unsigned int>(width) * sizeof(int));
0286         memset(gs, 0, static_cast<unsigned int>(width) * sizeof(int));
0287         memset(bs, 0, static_cast<unsigned int>(width) * sizeof(int));
0288 
0289 
0290         switch (img_format) {
0291         case QImage::Format_ARGB32_Premultiplied: {
0292             QRgb pixel;
0293             for (auto i = 0; i < mh; i++) {
0294                 p2 = reinterpret_cast<QRgb *>(img.scanLine(i + my));
0295                 for (auto j = 0; j < width; ++j) {
0296                     p2++;
0297                     pixel = convertFromPremult(*p2);
0298                     as[j] += qAlpha(pixel);
0299                     rs[j] += qRed(pixel) * qRed(pixel);
0300                     gs[j] += qGreen(pixel) * qGreen(pixel);
0301                     bs[j] += qBlue(pixel) * qBlue(pixel);
0302                 }
0303             }
0304             break;
0305         }
0306 
0307         case QImage::Format_Indexed8: {
0308             QRgb pixel;
0309             unsigned char *ptr;
0310             for (auto i = 0; i < mh; ++i) {
0311                 ptr = img.scanLine(i + my);
0312                 for (auto j = 0; j < width; ++j) {
0313                     ptr++;
0314                     pixel = colorTable[*ptr];
0315                     as[j] += qAlpha(pixel);
0316                     rs[j] += qRed(pixel) * qRed(pixel);
0317                     gs[j] += qGreen(pixel) * qGreen(pixel);
0318                     bs[j] += qBlue(pixel) * qBlue(pixel);
0319                 }
0320             }
0321             break;
0322         }
0323 
0324         default: {
0325             for (auto i = 0; i < mh; ++i) {
0326                 p2 = reinterpret_cast<QRgb *>(img.scanLine(i + my));
0327                 for (auto j = 0; j < width; j++) {
0328                     p2++;
0329                     as[j] += qAlpha(*p2);
0330                     rs[j] += qRed(*p2);
0331                     gs[j] += qGreen(*p2);
0332                     bs[j] += qBlue(*p2);
0333                 }
0334             }
0335             break;
0336         }
0337         }
0338 
0339         for (auto i = 0; i < width; ++i) {
0340             auto a{0};
0341             auto r{0};
0342             auto g{0};
0343             auto b{0};
0344 
0345             auto mx = i - radius;
0346             auto mw = (radius << 1) + 1;
0347 
0348             if (mx < 0) {
0349                 mw += mx;
0350                 mx = 0;
0351             }
0352 
0353             if ((mx + mw) > width) {
0354                 mw = width - mx;
0355             }
0356 
0357             for (auto j = mx; j < (mw + mx); ++j) {
0358                 a += as[j];
0359                 r += rs[j];
0360                 g += gs[j];
0361                 b += bs[j];
0362             }
0363 
0364             auto mt = mw * mh;
0365 
0366             a = a / mt;
0367             r = r / mt;
0368             g = g / mt;
0369             b = b / mt;
0370 
0371             *p1++ = qRgba(std::sqrt(r), std::sqrt(g), std::sqrt(b), a);
0372         }
0373     }
0374 
0375     delete[] as;
0376     delete[] rs;
0377     delete[] gs;
0378     delete[] bs;
0379 
0380     return (buffer);
0381 }
0382 
0383 //--------------------------------------------------------------------------------
0384 
0385 int defaultConvolveMatrixSize(float radius, float sigma, bool quality)
0386 {
0387     int i, matrix_size;
0388     float normalize, value;
0389     float sigma2 = sigma*sigma*2.0f;
0390     float sigmaSQ2PI = static_cast<float>(M_SQ2PI) * sigma;
0391     int max = quality ? 65535 : 255;
0392 
0393     if(sigma == 0.0f){
0394         qWarning("Blitz::defaultConvolveMatrixSize(): Zero sigma is invalid!");
0395         return(5);
0396     }
0397 
0398     if(radius > 0.0f) {
0399         return(static_cast<int>(2.0f * std::ceil(radius) + 1.0f));
0400     }
0401 
0402     matrix_size = 5;
0403     do{
0404         normalize = 0.0;
0405         for(i=(-matrix_size/2); i <= (matrix_size/2); ++i) {
0406             normalize += std::exp(-(static_cast<float> (i*i))/sigma2) / sigmaSQ2PI;
0407         }
0408         i = matrix_size/2;
0409         value = std::exp(-(static_cast<float> (i*i))/sigma2) / sigmaSQ2PI / normalize;
0410         matrix_size += 2;
0411     } while(static_cast<int>(max*value) > 0);
0412 
0413     matrix_size-=4;
0414     return(matrix_size);
0415 }
0416 
0417 //--------------------------------------------------------------------------------
0418 
0419 QImage convolve(QImage &img, int matrix_size, float *matrix)
0420 {
0421     int i, x, y, w, h, matrix_x, matrix_y;
0422     int edge = matrix_size/2;
0423     QRgb *dest, *src, *s, **scanblock;
0424     float *m, *normalize_matrix, normalize;
0425 
0426     if(!(matrix_size % 2)){
0427         qWarning("Blitz::convolve(): kernel width must be an odd number!");
0428         return(img);
0429     }
0430 
0431     w = img.width();
0432     h = img.height();
0433     if(w < 3 || h < 3){
0434         qWarning("Blitz::convolve(): Image is too small!");
0435         return(img);
0436     }
0437 
0438     if(img.format() == QImage::Format_ARGB32_Premultiplied) {
0439         img.convertTo(QImage::Format_ARGB32);
0440     } else if (img.depth() < 32) {
0441         img.convertTo(img.hasAlphaChannel() ? QImage::Format_ARGB32
0442                                             : QImage::Format_RGB32);
0443     }
0444     QImage buffer(w, h, img.format());
0445 
0446     scanblock = new QRgb* [matrix_size];
0447     normalize_matrix = new float[matrix_size*matrix_size];
0448 
0449     // create normalized matrix
0450     normalize = 0.0;
0451     for(i=0; i < matrix_size*matrix_size; ++i) {
0452         normalize += matrix[i];
0453     }
0454     if(std::abs(normalize) <=  static_cast<float> (M_EPSILON)) {
0455         normalize = 1.0f;
0456     }
0457     normalize = 1.0f/normalize;
0458     for(i=0; i < matrix_size*matrix_size; ++i){
0459         normalize_matrix[i] = normalize*matrix[i];
0460     }
0461 
0462     // apply
0463 
0464     {
0465         //
0466         //
0467         // Non-MMX version
0468         //
0469         //
0470 
0471         float r, g, b;
0472         for(y=0; y < h; ++y){
0473             src = reinterpret_cast<QRgb *>(img.scanLine(y));
0474             dest = reinterpret_cast<QRgb *>(buffer.scanLine(y));
0475             // Read in scanlines to pixel neighborhood. If the scanline is outside
0476             // the image use the top or bottom edge.
0477             for(x=y-edge, i=0; x <= y+edge; ++i, ++x){
0478                 scanblock[i] = reinterpret_cast<QRgb *>(
0479                     img.scanLine((x < 0) ? 0 : (x > h-1) ? h-1 : x));
0480             }
0481             // Now we are about to start processing scanlines. First handle the
0482             // part where the pixel neighborhood extends off the left edge.
0483             for(x=0; x-edge < 0 ; ++x){
0484                 r = g = b = 0.0;
0485                 m = normalize_matrix;
0486                 for(matrix_y = 0; matrix_y < matrix_size; ++matrix_y){
0487                     s = scanblock[matrix_y];
0488                     matrix_x = -edge;
0489                     while(x+matrix_x < 0){
0490                         CONVOLVE_ACC(*m, *s);
0491                         ++matrix_x; ++m;
0492                     }
0493                     while(matrix_x <= edge){
0494                         CONVOLVE_ACC(*m, *s);
0495                         ++matrix_x; ++m; ++s;
0496                     }
0497                 }
0498                 r = r < 0.0f ? 0.0f : r > 255.0f ? 255.0f : r + 0.5f;
0499                 g = g < 0.0f ? 0.0f : g > 255.0f ? 255.0f : g + 0.5f;
0500                 b = b < 0.0f ? 0.0f : b > 255.0f ? 255.0f : b + 0.5f;
0501                 *dest++ = qRgba(static_cast<unsigned char> (r), static_cast<unsigned char> (g),
0502                                 static_cast<unsigned char> (b), qAlpha(*src++));
0503             }
0504             // Okay, now process the middle part where the entire neighborhood
0505             // is on the image.
0506             for(; x+edge < w; ++x){
0507                 m = normalize_matrix;
0508                 r = g = b = 0.0;
0509                 for(matrix_y = 0; matrix_y < matrix_size; ++matrix_y){
0510                     s = scanblock[matrix_y] + (x-edge);
0511                     for(matrix_x = -edge; matrix_x <= edge; ++matrix_x, ++m, ++s){
0512                         CONVOLVE_ACC(*m, *s);
0513                     }
0514                 }
0515                 r = r < 0.0f ? 0.0f : r > 255.0f ? 255.0f : r + 0.5f;
0516                 g = g < 0.0f ? 0.0f : g > 255.0f ? 255.0f : g + 0.5f;
0517                 b = b < 0.0f ? 0.0f : b > 255.0f ? 255.0f : b + 0.5f;
0518                 *dest++ = qRgba(static_cast<unsigned char> (r), static_cast<unsigned char> (g),
0519                                 static_cast<unsigned char> (b), qAlpha(*src++));
0520             }
0521             // Finally process the right part where the neighborhood extends off
0522             // the right edge of the image
0523             for(; x < w; ++x){
0524                 r = g = b = 0.0;
0525                 m = normalize_matrix;
0526                 for(matrix_y = 0; matrix_y < matrix_size; ++matrix_y){
0527                     s = scanblock[matrix_y];
0528                     s += x-edge;
0529                     matrix_x = -edge;
0530                     while(x+matrix_x < w){
0531                         CONVOLVE_ACC(*m, *s);
0532                         ++matrix_x;
0533                         ++m;
0534                         ++s;
0535                     }
0536                     --s;
0537                     while(matrix_x <= edge){
0538                         CONVOLVE_ACC(*m, *s);
0539                         ++matrix_x;
0540                         ++m;
0541                     }
0542                 }
0543                 r = r < 0.0f ? 0.0f : r > 255.0f ? 255.0f : r + 0.5f;
0544                 g = g < 0.0f ? 0.0f : g > 255.0f ? 255.0f : g + 0.5f;
0545                 b = b < 0.0f ? 0.0f : b > 255.0f ? 255.0f : b + 0.5f;
0546                 *dest++ = qRgba(static_cast<unsigned char> (r), static_cast<unsigned char> (g),
0547                                 static_cast<unsigned char> (b), qAlpha(*src++));
0548             }
0549         }
0550     }
0551 
0552     delete[] scanblock;
0553     delete[] normalize_matrix;
0554     return(buffer);
0555 }
0556 
0557 //--------------------------------------------------------------------------------
0558 
0559 QImage Blitz::gaussianSharpen(QImage &img, float radius, float sigma)
0560 {
0561     if(sigma == 0.0f){
0562         qWarning("Blitz::gaussianSharpen(): Zero sigma is invalid!");
0563         return(img);
0564     }
0565 
0566     int matrix_size = defaultConvolveMatrixSize(radius, sigma, true);
0567     int len = matrix_size*matrix_size;
0568     float alpha, *matrix = new float[len];
0569     float sigma2 = sigma*sigma*2.0f;
0570     float sigmaPI2 = 2.0f*static_cast<float> (M_PI)*sigma*sigma;
0571 
0572     int half = matrix_size/2;
0573     int x, y, i=0, j=half;
0574     float normalize=0.0;
0575     for(y=(-half); y <= half; ++y, --j){
0576         for(x=(-half); x <= half; ++x, ++i){
0577             alpha = std::exp(-(static_cast<float> (x*x+y*y))/sigma2);
0578             matrix[i] = alpha/sigmaPI2;
0579             normalize += matrix[i];
0580         }
0581     }
0582 
0583     matrix[i/2]=(-2.0f)*normalize;
0584     QImage result(convolve(img, matrix_size, matrix));
0585     delete[] matrix;
0586     return(result);
0587 }
0588 
0589 //--------------------------------------------------------------------------------
0590 
0591 QImage Blitz::emboss(QImage &img, float radius, float sigma)
0592 {
0593     if(sigma == 0.0f){
0594         qWarning("Blitz::emboss(): Zero sigma is invalid!");
0595         return(img);
0596     }
0597 
0598     int matrix_size = defaultConvolveMatrixSize(radius, sigma, true);
0599     int len = matrix_size*matrix_size;
0600 
0601     float alpha, *matrix = new float[len];
0602     float sigma2 = sigma*sigma*2.0f;
0603     float sigmaPI2 = 2.0f*static_cast<float> (M_PI)*sigma*sigma;
0604 
0605     int half = matrix_size/2;
0606     int x, y, i=0, j=half;
0607     for(y=(-half); y <= half; ++y, --j){
0608         for(x=(-half); x <= half; ++x, ++i){
0609             alpha = std::exp(-(static_cast<float> (x*x+y*y))/sigma2);
0610             matrix[i]=((x < 0) || (y < 0) ? -8.0f : 8.0f)*alpha/sigmaPI2;
0611             if(x == j) {
0612                 matrix[i]=0.0;
0613             }
0614         }
0615     }
0616     QImage result(convolve(img, matrix_size, matrix));
0617     delete[] matrix;
0618     equalize(result);
0619     return(result);
0620 }
0621 
0622 //--------------------------------------------------------------------------------
0623 
0624 QImage& Blitz::flatten(QImage &img, const QColor &ca, const QColor &cb)
0625 {
0626     if(img.isNull()) {
0627         return(img);
0628     }
0629 
0630     if(img.depth() == 1) {
0631         img.setColor(0, ca.rgb());
0632         img.setColor(1, cb.rgb());
0633         return(img);
0634     }
0635 
0636     int r1 = ca.red(); int r2 = cb.red();
0637     int g1 = ca.green(); int g2 = cb.green();
0638     int b1 = ca.blue(); int b2 = cb.blue();
0639     int min = 0, max = 255;
0640 
0641     QRgb *data, *end;
0642     QVector<QRgb> cTable;
0643     if(img.format() == QImage::Format_Indexed8){
0644         cTable = img.colorTable();
0645         data = static_cast<unsigned int *> (cTable.data());
0646         end = data + img.colorCount();
0647 
0648     }
0649     else{
0650         data = reinterpret_cast<QRgb *>(img.scanLine(0));
0651         end = data + (img.width()*img.height());
0652     }
0653 
0654     // get minimum and maximum graylevel
0655     QRgb *ptr = data;
0656     int mean;
0657 
0658     if(img.format() != QImage::Format_ARGB32_Premultiplied){
0659         while(ptr != end){
0660             mean = (qRed(*ptr) + qGreen(*ptr) + qBlue(*ptr)) / 3;
0661             min = qMin(min, mean);
0662             max = qMax(max, mean);
0663             ++ptr;
0664         }
0665     }
0666     else{
0667         QRgb pixel;
0668         while(ptr != end){
0669             pixel = convertFromPremult(*ptr);
0670             mean = (qRed(pixel) + qGreen(pixel) + qBlue(pixel)) / 3;
0671             min = qMin(min, mean);
0672             max = qMax(max, mean);
0673             ++ptr;
0674         }
0675     }
0676 
0677     // conversion factors
0678     float sr = (static_cast<float> (r2 - r1) / (max - min));
0679     float sg = (static_cast<float> (g2 - g1) / (max - min));
0680     float sb = (static_cast<float> (b2 - b1) / (max - min));
0681 
0682     if(img.format() != QImage::Format_ARGB32_Premultiplied){
0683         while(data != end){
0684             mean = (qRed(*data) + qGreen(*data) + qBlue(*data)) / 3;
0685             *data = qRgba(static_cast<unsigned char> (sr * (mean - min) + r1 + 0.5f),
0686                           static_cast<unsigned char> (sg * (mean - min) + g1 + 0.5f),
0687                           static_cast<unsigned char> (sb * (mean - min) + b1 + 0.5f),
0688                           qAlpha(*data));
0689             ++data;
0690         }
0691     }
0692     else{
0693         QRgb pixel;
0694         while(data != end){
0695             pixel = convertFromPremult(*data);
0696             mean = (qRed(pixel) + qGreen(pixel) + qBlue(pixel)) / 3;
0697             *data =
0698                 convertToPremult(qRgba(static_cast<unsigned char> (sr * (mean - min) + r1 + 0.5f),
0699                                        static_cast<unsigned char> (sg * (mean - min) + g1 + 0.5f),
0700                                        static_cast<unsigned char> (sb * (mean - min) + b1 + 0.5f),
0701                                        qAlpha(*data)));
0702             ++data;
0703         }
0704     }
0705 
0706     if(img.format() == QImage::Format_Indexed8) {
0707         img.setColorTable(cTable);
0708     }
0709     return(img);
0710 }
0711 
0712 //--------------------------------------------------------------------------------