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 //--------------------------------------------------------------------------------