~ubuntu-branches/ubuntu/feisty/qimageblitz/feisty-backports

« back to all changes in this revision

Viewing changes to blitz/colors.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jonathan Riddell
  • Date: 2007-08-31 09:50:41 UTC
  • Revision ID: james.westby@ubuntu.com-20070831095041-rlguoifpqerdrs2o
Tags: upstream-0.0.706674
ImportĀ upstreamĀ versionĀ 0.0.706674

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 Copyright (C) 1998, 1999, 2001, 2002, 2004, 2005, 2007
 
3
      Daniel M. Duley <daniel.duley@verizon.net>
 
4
 (C) 2000 Josef Weidendorfer <weidendo@in.tum.de>
 
5
 (C) 1999 Geert Jansen <g.t.jansen@stud.tue.nl>
 
6
 
 
7
Redistribution and use in source and binary forms, with or without
 
8
modification, are permitted provided that the following conditions
 
9
are met:
 
10
 
 
11
1. Redistributions of source code must retain the above copyright
 
12
   notice, this list of conditions and the following disclaimer.
 
13
2. Redistributions in binary form must reproduce the above copyright
 
14
   notice, this list of conditions and the following disclaimer in the
 
15
   documentation and/or other materials provided with the distribution.
 
16
 
 
17
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 
18
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 
19
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 
20
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 
21
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 
22
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 
23
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 
24
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
25
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 
26
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
27
 
 
28
*/
 
29
 
 
30
#include "qimageblitz.h"
 
31
#include <config-processor.h>
 
32
#include "private/inlinehsv.h"
 
33
#include "private/blitz_p.h"
 
34
#include "blitzcpu.h"
 
35
#include <QVector>
 
36
 
 
37
#if defined(__i386__) && ( defined(__GNUC__) || defined(__INTEL_COMPILER) )
 
38
#  if defined(HAVE_MMX )
 
39
#    define USE_MMX_INLINE_ASM
 
40
#  endif
 
41
#endif
 
42
 
 
43
/**
 
44
 * Precalculated increment values for HSV contrast. Saves some expensive
 
45
 * floating point math. These are half the integer results of:
 
46
 *
 
47
 *  alpha*(alpha*(sin(M_PI*(brightness-alpha))+1.0)-brightness)
 
48
 *
 
49
 * where alpha is 0.5+M_EPSILON and brightness is the percentage for values
 
50
 * ranging from 0 to 255, (Qt compatible HSV brightness values). The other
 
51
 * half of the lookup table is the inverse of these values. I am so clever :)
 
52
 * (mosfet)
 
53
 */
 
54
static char contrast_table[128] = {
 
55
    0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6, 7, 7, 8, 8, 9, 9,
 
56
    10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17,
 
57
    17, 18, 18, 19, 19, 19, 20, 20, 21, 21, 21, 22, 22, 22, 23,
 
58
    23, 23, 24, 24, 24, 24, 25, 25, 25, 25, 25, 26, 26, 26, 26,
 
59
    26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
 
60
    27, 26, 26, 26, 26, 26, 26, 26, 25, 25, 25, 25, 24, 24, 24,
 
61
    23, 23, 23, 22, 22, 21, 21, 20, 20, 20, 19, 18, 18, 17, 17,
 
62
    16, 16, 15, 14, 14, 13, 12, 11, 11, 10, 9, 8, 7, 7, 6, 5,
 
63
    4, 3, 2, 1, 0
 
64
};
 
65
 
 
66
bool Blitz::grayscale(QImage &img, bool reduceDepth)
 
67
{
 
68
    if(img.isNull())
 
69
        return(false);
 
70
 
 
71
    if(img.format() == QImage::Format_ARGB32_Premultiplied)
 
72
        img = img.convertToFormat(QImage::Format_ARGB32);
 
73
    else if(img.depth() < 8)
 
74
        img = img.convertToFormat(QImage::Format_Indexed8);
 
75
 
 
76
#ifdef USE_MMX_INLINE_ASM
 
77
#ifdef __GNUC__
 
78
#warning Using MMX grayscale
 
79
#endif
 
80
    if(BlitzCPUInfo::haveExtension(BlitzCPUInfo::IntegerSSE)){
 
81
        MMX::Pack4 packedmult = {{5, 16, 11, 1}};
 
82
        MMX::Pack4 alphamask = {{0x0000, 0x0000, 0x0000, 0xFFFF}};
 
83
        MMX::Pack4 noalphamask = {{0xFFFF, 0xFFFF, 0xFFFF, 0x0000}};
 
84
 
 
85
        if(reduceDepth && !img.hasAlphaChannel()){
 
86
            int y, w = img.width(), h = img.height();
 
87
            QImage buffer(w, h, QImage::Format_Indexed8);
 
88
 
 
89
            QVector<QRgb> cTable(256);
 
90
            unsigned int *src = cTable.data();
 
91
            for(y=0; y < 256; ++y)
 
92
                *src++ = qRgb(y, y, y);
 
93
            buffer.setColorTable(cTable);
 
94
 
 
95
            src = (unsigned int *)img.scanLine(0);
 
96
            unsigned char *end, *dest;
 
97
 
 
98
            __asm__ __volatile__
 
99
                ("pxor %%mm7, %%mm7\n\t"
 
100
                 "movq (%0), %%mm4\n\t"
 
101
                 "movq (%1), %%mm5\n\t"
 
102
                 "movq (%2), %%mm6\n\t"
 
103
                 : : "r"(&packedmult), "r"(&alphamask), "r"(&noalphamask));
 
104
            for(y=0; y < h; ++y){
 
105
                dest = buffer.scanLine(y);
 
106
                end = dest+w;
 
107
                while(dest != end){
 
108
                    __asm__ __volatile__
 
109
                        ("movd (%0), %%mm0\n\t" // load pixel
 
110
                         "punpcklbw %%mm7, %%mm0\n\t" // upgrade to quad
 
111
                         "pand %%mm6, %%mm0\n\t" // zero alpha so we can use pmaddwd
 
112
 
 
113
                         "pmaddwd %%mm4, %%mm0\n\t" // 2 multiplies and sum BG, RA
 
114
                         "pshufw $0xE4, %%mm0, %%mm1\n\t"
 
115
                         "punpckhwd %%mm7, %%mm1\n\t" // sum R
 
116
                         "paddw %%mm1, %%mm0\n\t"
 
117
                         "psrlw $0x05, %%mm0\n\t" // divide by 32
 
118
                         "movd %%mm0, %%eax\n\t"
 
119
                         "movb %%al, (%1)\n\t"
 
120
                         : : "r"(src), "r"(dest) : "%eax");
 
121
                    ++src;
 
122
                    ++dest;
 
123
                }
 
124
            }
 
125
            __asm__ __volatile__ ("emms\n\t" : :);
 
126
            img = buffer;
 
127
        }
 
128
        else{
 
129
            // 8bpp or 32bpp w/ no conversion
 
130
            int count = img.depth() > 8 ? img.width()*img.height() : img.numColors();
 
131
 
 
132
            QVector<QRgb> cTable;
 
133
            if(img.depth() == 8)
 
134
                cTable = img.colorTable();
 
135
 
 
136
            unsigned int *data = (img.depth() > 8) ?
 
137
                (unsigned int *)img.scanLine(0) : cTable.data();
 
138
            unsigned int *end = data+count;
 
139
 
 
140
            __asm__ __volatile__
 
141
                ("pxor %%mm7, %%mm7\n\t"
 
142
                 "movq (%0), %%mm4\n\t"
 
143
                 "movq (%1), %%mm5\n\t"
 
144
                 "movq (%2), %%mm6\n\t"
 
145
                 : : "r"(&packedmult), "r"(&alphamask), "r"(&noalphamask));
 
146
            while(data != end){
 
147
                __asm__ __volatile__
 
148
                    ("movd (%0), %%mm0\n\t" // load pixel
 
149
                     "punpcklbw %%mm7, %%mm0\n\t" // upgrade to quad
 
150
                     "pshufw $0xE4, %%mm0, %%mm2\n\t" // copy for alpha later
 
151
                     "pand %%mm6, %%mm0\n\t" // zero alpha so we can use pmaddwd
 
152
 
 
153
                     "pmaddwd %%mm4, %%mm0\n\t" // 2 multiplies and sum BG, RA
 
154
                     "pshufw $0xE4, %%mm0, %%mm1\n\t"
 
155
                     "punpckhwd %%mm7, %%mm1\n\t" // sum R
 
156
                     "paddw %%mm1, %%mm0\n\t"
 
157
                     "psrlw $0x05, %%mm0\n\t" // divide by 32
 
158
 
 
159
                     "pshufw $0x00, %%mm0, %%mm0\n\t" // copy to all pixels
 
160
                     "pand %%mm6, %%mm0\n\t" // reset original alpha
 
161
                     "pand %%mm5, %%mm2\n\t"
 
162
                     "por %%mm2, %%mm0\n\t"
 
163
 
 
164
                     "packuswb %%mm0, %%mm0\n\t" // pack and write
 
165
                     "movd %%mm0, (%0)\n\t"
 
166
                     : : "r"(data));
 
167
                ++data;
 
168
            }
 
169
            __asm__ __volatile__ ("emms\n\t" : :);
 
170
            if(img.depth() == 8)
 
171
                img.setColorTable(cTable);
 
172
        }
 
173
    }
 
174
    else
 
175
#endif
 
176
    {
 
177
        // Non MMX version
 
178
        if(reduceDepth && img.format() ==  QImage::Format_RGB32){
 
179
            // 32bpp w/ conversion to palette
 
180
            int y, w = img.width(), h = img.height();
 
181
            QImage buffer(w, h, QImage::Format_Indexed8);
 
182
 
 
183
            QVector<QRgb> cTable(256);
 
184
            unsigned int *src = cTable.data();
 
185
            for(y=0; y < 256; ++y)
 
186
                *src++ = qRgb(y, y, y);
 
187
            buffer.setColorTable(cTable);
 
188
 
 
189
            src = (unsigned int *)img.scanLine(0);
 
190
            unsigned char *end, *dest;
 
191
            unsigned int pixel;
 
192
            for(y=0; y < h; ++y){
 
193
                dest = buffer.scanLine(y);
 
194
                end = dest+w;
 
195
                while(dest != end){
 
196
                    pixel = *src++;
 
197
                    *dest++ = qGray(qRed(pixel), qGreen(pixel), qBlue(pixel));
 
198
                }
 
199
            }
 
200
            img = buffer;
 
201
        }
 
202
        else{
 
203
            // 8bpp or 32bpp w/ no conversion
 
204
            int count = img.depth() > 8 ? img.width()*img.height() : img.numColors();
 
205
 
 
206
            QVector<QRgb> cTable;
 
207
            if(img.depth() == 8)
 
208
                cTable = img.colorTable();
 
209
 
 
210
            unsigned int *data = (img.depth() > 8) ?
 
211
                (unsigned int *)img.scanLine(0) : cTable.data();
 
212
            unsigned int *end = data+count;
 
213
            unsigned int pixel;
 
214
            unsigned char c;
 
215
            while(data != end){
 
216
                pixel = *data;
 
217
                c = qGray(qRed(pixel), qGreen(pixel), qBlue(pixel));
 
218
                *data++ = qRgba(c, c, c, qAlpha(pixel));
 
219
            }
 
220
            if(img.depth() == 8)
 
221
                img.setColorTable(cTable);
 
222
        }
 
223
    }
 
224
    return(true);
 
225
}
 
226
 
 
227
bool Blitz::invert(QImage &img, QImage::InvertMode mode)
 
228
{
 
229
    if(img.isNull())
 
230
        return(false);
 
231
 
 
232
#ifdef USE_MMX_INLINE_ASM
 
233
#ifdef __GNUC__
 
234
#warning Using MMX invert
 
235
#endif
 
236
    if(BlitzCPUInfo::haveExtension(BlitzCPUInfo::MMX)){
 
237
        if(img.format() == QImage::Format_ARGB32_Premultiplied)
 
238
            img = img.convertToFormat(QImage::Format_ARGB32);
 
239
        else if(img.depth() < 8)
 
240
            img = img.convertToFormat(QImage::Format_Indexed8);
 
241
 
 
242
        unsigned int mask = (mode == QImage::InvertRgba ? 0xFFFFFFFF :
 
243
                             0x00FFFFFF);
 
244
        MMX::Pack2 packedmask = {{mask, mask}};
 
245
 
 
246
        int remainder, count;
 
247
        unsigned int *data, *end;
 
248
        QVector<QRgb> cTable;
 
249
 
 
250
        if(img.depth() <= 8){
 
251
            cTable = img.colorTable();
 
252
            remainder = img.numColors() % 4;
 
253
            count = img.numColors()-remainder;
 
254
            data = (unsigned int *)cTable.data();
 
255
        }
 
256
        else{
 
257
            remainder = (img.width()*img.height()) % 4;
 
258
            count = (img.width()*img.height())-remainder;
 
259
            data = (unsigned int *)img.scanLine(0);
 
260
        }
 
261
        end = data+count;
 
262
 
 
263
        __asm__ __volatile__ ("movq (%0), %%mm7\n\t" : : "r"(&packedmask));
 
264
        while(data != end){
 
265
            __asm__ __volatile__
 
266
                ("movq (%0), %%mm0\n\t"
 
267
                 "pxor %%mm7, %%mm0\n\t"
 
268
                 "movq %%mm0, (%0)\n\t"
 
269
                 : : "r"(data));
 
270
            data += 2;
 
271
        }
 
272
        end += remainder;
 
273
        while(data != end){
 
274
            __asm__ __volatile__
 
275
                ("movd (%0), %%mm0\n\t"
 
276
                 "pxor %%mm7, %%mm0\n\t"
 
277
                 "movd %%mm0, (%0)\n\t"
 
278
                 : : "r"(data));
 
279
            ++data;
 
280
        }
 
281
        __asm__ __volatile__ ("emms\n\t" : :);
 
282
        if(img.depth() <= 8)
 
283
            img.setColorTable(cTable);
 
284
    }
 
285
    else
 
286
#endif
 
287
    {
 
288
        img.invertPixels(mode);
 
289
    }
 
290
    return(true);
 
291
}
 
292
 
 
293
QImage& Blitz::contrast(QImage &img, bool sharpen, int weight)
 
294
{
 
295
    if(img.isNull())
 
296
        return(img);
 
297
 
 
298
    if(img.format() == QImage::Format_ARGB32_Premultiplied)
 
299
        img = img.convertToFormat(QImage::Format_ARGB32);
 
300
    else if(img.depth() < 8)
 
301
        img = img.convertToFormat(QImage::Format_Indexed8);
 
302
 
 
303
    QVector<QRgb> cTable;
 
304
    if(img.depth() == 8)
 
305
        cTable = img.colorTable();
 
306
 
 
307
    unsigned int *end, *data;
 
308
    int count;
 
309
    if(img.depth() > 8){
 
310
        count = img.width()*img.height();
 
311
        data = (unsigned int *)img.scanLine(0);
 
312
    }
 
313
    else{
 
314
        count = img.numColors();
 
315
        data = (unsigned int *)cTable.data();
 
316
    }
 
317
    end = data+count;
 
318
 
 
319
    InlineHSV hsv;
 
320
    int v;
 
321
    if(sharpen){
 
322
        while(data != end){
 
323
            hsv.convertRGB2HSV(*data);
 
324
            v = hsv.value();
 
325
            if(v > 127){
 
326
                v += contrast_table[v-128]+weight;
 
327
                if(v > 255) v = 255;
 
328
            }
 
329
            else{
 
330
                v -= contrast_table[v]+weight;
 
331
                if(v < 0) v = 0;
 
332
            }
 
333
            hsv.setValue(v);
 
334
            hsv.convertHSV2RGB();
 
335
            *data = qRgba(hsv.red(), hsv.green(), hsv.blue(), qAlpha(*data));
 
336
            ++data;
 
337
        }
 
338
    }
 
339
    else{
 
340
        while(data != end){
 
341
            hsv.convertRGB2HSV(*data);
 
342
            v = hsv.value();
 
343
            if(v > 127){
 
344
                v -= contrast_table[v-128]+weight;
 
345
                if(v < 0) v = 0;
 
346
            }
 
347
            else{
 
348
                v += contrast_table[v]+weight;
 
349
                if(v > 255) v = 255;
 
350
            }
 
351
            hsv.setValue(v);
 
352
            hsv.convertHSV2RGB();
 
353
            *data = qRgba(hsv.red(), hsv.green(), hsv.blue(), qAlpha(*data));
 
354
            ++data;
 
355
        }
 
356
    }
 
357
 
 
358
    if(img.depth() == 8)
 
359
        img.setColorTable(cTable);
 
360
    return(img);
 
361
}
 
362
 
 
363
QImage& Blitz::intensity(QImage &img, float percent)
 
364
{
 
365
    if(img.isNull())
 
366
        return(img);
 
367
 
 
368
    if(img.format() == QImage::Format_ARGB32_Premultiplied)
 
369
        img = img.convertToFormat(QImage::Format_ARGB32);
 
370
    else if(img.depth() < 8)
 
371
        img = img.convertToFormat(QImage::Format_Indexed8);
 
372
 
 
373
    QVector<QRgb> colorTable;
 
374
    int segmentColors, pixels;
 
375
    unsigned int *data;
 
376
 
 
377
    if(img.format() == QImage::Format_Indexed8){
 
378
        segmentColors = pixels = img.numColors();
 
379
        colorTable = img.colorTable();
 
380
        data = colorTable.data();
 
381
    }
 
382
    else{
 
383
        segmentColors = 256;
 
384
        pixels = img.width()*img.height();
 
385
        data = (unsigned int *)img.scanLine(0);
 
386
    }
 
387
 
 
388
    percent = qBound(-1.0f, percent, 1.0f);
 
389
    bool brighten = (percent >= 0);
 
390
    if(percent < 0)
 
391
        percent = -percent;
 
392
 
 
393
#ifdef USE_MMX_INLINE_ASM
 
394
#ifdef __GNUC__
 
395
#warning Using MMX intensity
 
396
#endif
 
397
    if(BlitzCPUInfo::haveExtension(BlitzCPUInfo::IntegerSSE)){
 
398
        unsigned int rem = pixels % 4;
 
399
        pixels -= rem;
 
400
        quint32 *end = ( data + pixels );
 
401
 
 
402
        quint16 p = (quint16)(256.0f*(percent));
 
403
        MMX::Pack4 mult = {{p,p,p,0}};
 
404
 
 
405
        __asm__ __volatile__(
 
406
        "pxor %%mm7, %%mm7\n\t"                // zero mm7 for unpacking
 
407
        "movq  (%0), %%mm6\n\t"                // copy intensity change to mm6
 
408
        : : "r"(&mult), "m"(mult));
 
409
        if (brighten)
 
410
        {
 
411
            while ( data != end ) {
 
412
                __asm__ __volatile__(
 
413
                "movq       (%0), %%mm0\n\t"
 
414
                "movq      8(%0), %%mm4\n\t"   // copy 4 pixels of data to mm0 and mm4
 
415
                "movq      %%mm0, %%mm1\n\t"
 
416
                "movq      %%mm0, %%mm3\n\t"
 
417
                "movq      %%mm4, %%mm5\n\t"   // copy to registers for unpacking
 
418
                "punpcklbw %%mm7, %%mm0\n\t"
 
419
                "punpckhbw %%mm7, %%mm1\n\t"   // unpack the two pixels from mm0
 
420
                "pmullw    %%mm6, %%mm0\n\t"
 
421
                "punpcklbw %%mm7, %%mm4\n\t"
 
422
                "pmullw    %%mm6, %%mm1\n\t"   // multiply by intensity*256
 
423
                "psrlw        $8, %%mm0\n\t"   // divide by 256
 
424
                "pmullw    %%mm6, %%mm4\n\t"
 
425
                "psrlw        $8, %%mm1\n\t"
 
426
                "psrlw        $8, %%mm4\n\t"
 
427
                "packuswb  %%mm1, %%mm0\n\t"   // pack solution into mm0. saturates at 255
 
428
                "movq      %%mm5, %%mm1\n\t"
 
429
 
 
430
                "punpckhbw %%mm7, %%mm1\n\t"   // unpack 4th pixel in mm1
 
431
 
 
432
                "pmullw    %%mm6, %%mm1\n\t"
 
433
                "paddusb   %%mm3, %%mm0\n\t"   // add intesity result to original of mm0
 
434
                "psrlw        $8, %%mm1\n\t"
 
435
                "packuswb  %%mm1, %%mm4\n\t"   // pack upper two pixels into mm4
 
436
 
 
437
                "movq      %%mm0, (%0)\n\t"    // rewrite to memory lower two pixels
 
438
                "paddusb   %%mm5, %%mm4\n\t"
 
439
                "movq      %%mm4, 8(%0)\n\t"   // rewrite upper two pixels
 
440
                : : "r"(data) );
 
441
                data += 4;
 
442
            }
 
443
 
 
444
            end += rem;
 
445
            while ( data != end ) {
 
446
                __asm__ __volatile__(
 
447
                "movd       (%0), %%mm0\n\t"   // repeat above but for
 
448
                "punpcklbw %%mm7, %%mm0\n\t"   // one pixel at a time
 
449
                "movq      %%mm0, %%mm3\n\t"
 
450
                "pmullw    %%mm6, %%mm0\n\t"
 
451
                "psrlw        $8, %%mm0\n\t"
 
452
                "paddw     %%mm3, %%mm0\n\t"
 
453
                "packuswb  %%mm0, %%mm0\n\t"
 
454
                "movd      %%mm0, (%0)\n\t"
 
455
                : : "r"(data) );
 
456
                data++;
 
457
            }
 
458
        }
 
459
        else
 
460
        {
 
461
            while ( data != end ) {
 
462
                __asm__ __volatile__(
 
463
                "movq       (%0), %%mm0\n\t"
 
464
                "movq      8(%0), %%mm4\n\t"
 
465
                "movq      %%mm0, %%mm1\n\t"
 
466
                "movq      %%mm0, %%mm3\n\t"
 
467
 
 
468
                "movq      %%mm4, %%mm5\n\t"
 
469
 
 
470
                "punpcklbw %%mm7, %%mm0\n\t"
 
471
                "punpckhbw %%mm7, %%mm1\n\t"
 
472
                "pmullw    %%mm6, %%mm0\n\t"
 
473
                "punpcklbw %%mm7, %%mm4\n\t"
 
474
                "pmullw    %%mm6, %%mm1\n\t"
 
475
                "psrlw        $8, %%mm0\n\t"
 
476
                "pmullw    %%mm6, %%mm4\n\t"
 
477
                "psrlw        $8, %%mm1\n\t"
 
478
                "psrlw        $8, %%mm4\n\t"
 
479
                "packuswb  %%mm1, %%mm0\n\t"
 
480
                "movq      %%mm5, %%mm1\n\t"
 
481
 
 
482
                "punpckhbw %%mm7, %%mm1\n\t"
 
483
 
 
484
                "pmullw    %%mm6, %%mm1\n\t"
 
485
                "psubusb   %%mm0, %%mm3\n\t"   // subtract darkening amount
 
486
                "psrlw        $8, %%mm1\n\t"
 
487
                "packuswb  %%mm1, %%mm4\n\t"
 
488
 
 
489
                "movq      %%mm3, (%0)\n\t"
 
490
                "psubusb   %%mm4, %%mm5\n\t"   // only change for this version is
 
491
                "movq      %%mm5, 8(%0)\n\t"   // subtraction here as we are darkening image
 
492
                : : "r"(data) );
 
493
                data += 4;
 
494
            }
 
495
 
 
496
            end += rem;
 
497
            while ( data != end ) {
 
498
                __asm__ __volatile__(
 
499
                "movd       (%0), %%mm0\n\t"
 
500
                "punpcklbw %%mm7, %%mm0\n\t"
 
501
                "movq      %%mm0, %%mm3\n\t"
 
502
                "pmullw    %%mm6, %%mm0\n\t"
 
503
                "psrlw        $8, %%mm0\n\t"
 
504
                "psubusw   %%mm0, %%mm3\n\t"
 
505
                "packuswb  %%mm3, %%mm3\n\t"
 
506
                "movd      %%mm3, (%0)\n\t"
 
507
                : : "r"(data) );
 
508
                data++;
 
509
            }
 
510
        }
 
511
        __asm__ __volatile__("emms");          // clear mmx state
 
512
    }
 
513
    else
 
514
#endif // USE_MMX_INLINE_ASM
 
515
    {
 
516
        unsigned char *segmentTable = new unsigned char[segmentColors];
 
517
        if(brighten){
 
518
            for(int i=0; i < segmentColors; ++i)
 
519
                segmentTable[i] = qMin((int)(i*percent), 255);
 
520
 
 
521
            int r, g, b;
 
522
            for(int i=0; i < pixels; ++i){
 
523
                r = qRed(data[i]);
 
524
                g = qGreen(data[i]);
 
525
                b = qBlue(data[i]);
 
526
                data[i] = qRgba(qMin(255, r+segmentTable[r]),
 
527
                                qMin(255, g+segmentTable[g]),
 
528
                                qMin(255, b+segmentTable[b]), qAlpha(data[i]));
 
529
            }
 
530
        }
 
531
        else{
 
532
            for(int i=0; i < segmentColors; ++i)
 
533
                segmentTable[i] = qMax((int)(i*percent), 0);
 
534
 
 
535
            int r, g, b;
 
536
            for(int i=0; i < pixels; ++i){
 
537
                r = qRed(data[i]);
 
538
                g = qGreen(data[i]);
 
539
                b = qBlue(data[i]);
 
540
                data[i] = qRgba(qMax(0, r-segmentTable[r]),
 
541
                                qMax(0, g-segmentTable[g]),
 
542
                                qMax(0, b-segmentTable[b]), qAlpha(data[i]));
 
543
            }
 
544
        }
 
545
        delete[] segmentTable;
 
546
    }
 
547
 
 
548
    if(img.depth() == 8)
 
549
        img.setColorTable(colorTable);
 
550
    return(img);
 
551
}
 
552
 
 
553
QImage& Blitz::channelIntensity(QImage &img, float percent, RGBChannel channel)
 
554
{
 
555
    if(img.isNull() || (channel != Red && channel != Blue &&
 
556
                        channel != Green))
 
557
        return(img);
 
558
    if(img.format() == QImage::Format_ARGB32_Premultiplied)
 
559
        img = img.convertToFormat(QImage::Format_ARGB32);
 
560
    else if(img.depth() < 8)
 
561
        img = img.convertToFormat(QImage::Format_Indexed8);
 
562
 
 
563
    QVector<QRgb> colorTable;
 
564
    int segmentColors, pixels;
 
565
    unsigned int *data;
 
566
 
 
567
    if(img.format() == QImage::Format_Indexed8){
 
568
        segmentColors = pixels = img.numColors();
 
569
        colorTable = img.colorTable();
 
570
        data = colorTable.data();
 
571
    }
 
572
    else{
 
573
        segmentColors = 256;
 
574
        pixels = img.width()*img.height();
 
575
        data = (unsigned int *)img.scanLine(0);
 
576
    }
 
577
 
 
578
    percent = qBound(-1.0f, percent, 1.0f);
 
579
    bool brighten = (percent >= 0);
 
580
    if(percent < 0)
 
581
        percent = -percent;
 
582
 
 
583
    unsigned char *segmentTable = new unsigned char[segmentColors];
 
584
    if(brighten){
 
585
        for(int i=0; i < segmentColors; ++i)
 
586
            segmentTable[i] = qMin((int)(i*percent), 255);
 
587
 
 
588
        int color;
 
589
        if(channel == Red){ // and here ;-)
 
590
            for(int i=0; i < pixels; ++i){
 
591
                color = qRed(data[i]);
 
592
                data[i] = qRgba(qMin(255, color+segmentTable[color]),
 
593
                                qGreen(data[i]), qBlue(data[i]),
 
594
                                qAlpha(data[i]));
 
595
            }
 
596
        }
 
597
        else if(channel == Green){
 
598
            for(int i=0; i < pixels; ++i){
 
599
                color = qGreen(data[i]);
 
600
                data[i] = qRgba(qRed(data[i]),
 
601
                                qMin(255, color+segmentTable[color]),
 
602
                                qBlue(data[i]), qAlpha(data[i]));
 
603
            }
 
604
        }
 
605
        else{
 
606
            for(int i=0; i < pixels; ++i){
 
607
                color = qBlue(data[i]);
 
608
                data[i] = qRgba(qRed(data[i]), qGreen(data[i]),
 
609
                                qMin(255, color+segmentTable[color]),
 
610
                                qAlpha(data[i]));
 
611
            }
 
612
        }
 
613
    }
 
614
    else{
 
615
        for(int i=0; i < segmentColors; ++i)
 
616
            segmentTable[i] = qMax((int)(i*percent), 0);
 
617
 
 
618
        int color;
 
619
        if(channel == Red){
 
620
            for(int i=0; i < pixels; ++i){
 
621
                color = qRed(data[i]);
 
622
                data[i] = qRgba(qMax(0, color-segmentTable[color]),
 
623
                                qGreen(data[i]), qBlue(data[i]),
 
624
                                qAlpha(data[i]));
 
625
            }
 
626
        }
 
627
        else if(channel == Green){
 
628
            for(int i=0; i < pixels; ++i){
 
629
                color = qGreen(data[i]);
 
630
                data[i] = qRgba(qRed(data[i]),
 
631
                                qMax(0, color-segmentTable[color]),
 
632
                                qBlue(data[i]), qAlpha(data[i]));
 
633
            }
 
634
        }
 
635
        else{
 
636
            for(int i=0; i < pixels; ++i){
 
637
                color = qBlue(data[i]);
 
638
                data[i] = qRgba(qRed(data[i]), qGreen(data[i]),
 
639
                                qMax(0, color-segmentTable[color]),
 
640
                                qAlpha(data[i]));
 
641
            }
 
642
        }
 
643
    }
 
644
    delete[] segmentTable;
 
645
    if(img.format() == QImage::Format_Indexed8)
 
646
        img.setColorTable(colorTable);
 
647
    return(img);
 
648
}
 
649
 
 
650
QImage& Blitz::desaturate(QImage &img, float desat)
 
651
{
 
652
    if(img.isNull())
 
653
        return(img);
 
654
    if(img.depth() < 8)
 
655
        img = img.convertToFormat(QImage::Format_Indexed8);
 
656
 
 
657
    desat = qBound(0.0f, desat, 1.0f);
 
658
 
 
659
    unsigned int *data, *end;
 
660
    InlineHSV hsv;
 
661
    if(img.format() == QImage::Format_ARGB32 ||
 
662
       img.format() == QImage::Format_RGB32 ||
 
663
       img.format() == QImage::Format_Indexed8){
 
664
        QVector<QRgb> cTable;
 
665
        if(img.format() == QImage::Format_Indexed8){
 
666
            cTable = img.colorTable();
 
667
            data = (unsigned int *)cTable.data();
 
668
            end = data + img.numColors();
 
669
 
 
670
        }
 
671
        else{
 
672
            data = (unsigned int *)img.scanLine(0);
 
673
            end = data + (img.width()*img.height());
 
674
        }
 
675
        while(data != end){
 
676
            hsv.convertRGB2HSV(*data);
 
677
            hsv.setSaturation((int)(hsv.saturation() * (1.0 - desat)));
 
678
            hsv.convertHSV2RGB();
 
679
            *data = qRgba(hsv.red(), hsv.green(), hsv.blue(), qAlpha(*data));
 
680
            ++data;
 
681
        }
 
682
        if(img.format() == QImage::Format_Indexed8)
 
683
            img.setColorTable(cTable);
 
684
 
 
685
    }
 
686
    else if(img.format() == QImage::Format_ARGB32_Premultiplied){
 
687
        data = (unsigned int *)img.scanLine(0);
 
688
        end = data + (img.width()*img.height());
 
689
        while(data != end){
 
690
            hsv.convertRGB2HSV(BlitzPrivate::convertFromPremult(*data));
 
691
            hsv.setSaturation((int)(hsv.saturation() * (1.0 - desat)));
 
692
            hsv.convertHSV2RGB();
 
693
            *data = BlitzPrivate::convertToPremult(qRgba(hsv.red(), hsv.green(),
 
694
                                                         hsv.blue(), qAlpha(*data)));
 
695
            ++data;
 
696
        }
 
697
    }
 
698
    return(img);
 
699
}
 
700
 
 
701
 
 
702
QImage Blitz::threshold(QImage &img, unsigned char thresholdValue,
 
703
                        RGBChannel channel, unsigned int c_on,
 
704
                        unsigned int c_off)
 
705
{
 
706
    if(img.isNull())
 
707
        return(img);
 
708
    else if(img.depth() < 8)
 
709
        img = img.convertToFormat(QImage::Format_Indexed8);
 
710
 
 
711
    int x, y, w, h;
 
712
    w = img.width(); h = img.height();
 
713
    QImage buffer(img.width(), img.height(), QImage::Format_Indexed8);
 
714
 
 
715
    QVector<QRgb> cTable(2);
 
716
    cTable[0] = c_off; cTable[1] = c_on;
 
717
    buffer.setColorTable(cTable);
 
718
 
 
719
    unsigned char *dest;
 
720
    if(img.format() == QImage::Format_RGB32 ||
 
721
       img.format() == QImage::Format_ARGB32){
 
722
        QRgb *src = (QRgb *)img.scanLine(0);
 
723
        switch(channel){
 
724
        case Grayscale:
 
725
        case All:{
 
726
            for(y=0; y < h; ++y){
 
727
                dest = buffer.scanLine(y);
 
728
                for(x=0; x < w; ++x)
 
729
                    *dest++ = (qGray(*src++) >= thresholdValue) ? 1 : 0;
 
730
            }
 
731
            break;
 
732
        }
 
733
        case Brightness:{
 
734
            for(y=0; y < h; ++y){
 
735
                dest = buffer.scanLine(y);
 
736
                for(x=0; x < w; ++x)
 
737
                    *dest++ = (BlitzPrivate::brightness(*src++) >= thresholdValue) ? 1 : 0;
 
738
            }
 
739
            break;
 
740
        }
 
741
        case Red:{
 
742
            for(y=0; y < h; ++y){
 
743
                dest = buffer.scanLine(y);
 
744
                for(x=0; x < w; ++x)
 
745
                    *dest++ = (qRed(*src++) >= thresholdValue) ? 1 : 0;
 
746
            }
 
747
            break;
 
748
        }
 
749
        case Green:{
 
750
            for(y=0; y < h; ++y){
 
751
                dest = buffer.scanLine(y);
 
752
                for(x=0; x < w; ++x)
 
753
                    *dest++ = (qGreen(*src++) >= thresholdValue) ? 1 : 0;
 
754
            }
 
755
            break;
 
756
        }
 
757
        case Blue:{
 
758
            for(y=0; y < h; ++y){
 
759
                dest = buffer.scanLine(y);
 
760
                for(x=0; x < w; ++x)
 
761
                    *dest++ = (qBlue(*src++) >= thresholdValue) ? 1 : 0;
 
762
            }
 
763
            break;
 
764
        }
 
765
        case Alpha:{
 
766
            for(y=0; y < h; ++y){
 
767
                dest = buffer.scanLine(y);
 
768
                for(x=0; x < w; ++x)
 
769
                    *dest++ = (qAlpha(*src++) >= thresholdValue) ? 1 : 0;
 
770
            }
 
771
            break;
 
772
        }
 
773
        }
 
774
    }
 
775
    else if(img.format() == QImage::Format_ARGB32_Premultiplied){
 
776
        QRgb *src = (QRgb *)img.scanLine(0);
 
777
        switch(channel){
 
778
        case Grayscale:
 
779
        case All:{
 
780
            for(y=0; y < h; ++y){
 
781
                dest = buffer.scanLine(y);
 
782
                for(x=0; x < w; ++x, ++src)
 
783
                    *dest++ = (qGray(BlitzPrivate::convertFromPremult(*src)) >=
 
784
                               thresholdValue) ? 1 : 0;
 
785
            }
 
786
            break;
 
787
        }
 
788
        case Brightness:{
 
789
            for(y=0; y < h; ++y){
 
790
                dest = buffer.scanLine(y);
 
791
                for(x=0; x < w; ++x, ++src)
 
792
                    *dest++ = (BlitzPrivate::brightness(BlitzPrivate::convertFromPremult(*src)) >=
 
793
                               thresholdValue) ? 1 : 0;
 
794
            }
 
795
            break;
 
796
        }
 
797
        case Red:{
 
798
            for(y=0; y < h; ++y){
 
799
                dest = buffer.scanLine(y);
 
800
                for(x=0; x < w; ++x, ++src)
 
801
                    *dest++ = (qRed(BlitzPrivate::convertFromPremult(*src)) >=
 
802
                               thresholdValue) ? 1 : 0;
 
803
            }
 
804
            break;
 
805
        }
 
806
        case Green:{
 
807
            for(y=0; y < h; ++y){
 
808
                dest = buffer.scanLine(y);
 
809
                for(x=0; x < w; ++x, ++src)
 
810
                    *dest++ = (qGreen(BlitzPrivate::convertFromPremult(*src)) >=
 
811
                               thresholdValue) ? 1 : 0;
 
812
            }
 
813
            break;
 
814
        }
 
815
        case Blue:{
 
816
            for(y=0; y < h; ++y){
 
817
                dest = buffer.scanLine(y);
 
818
                for(x=0; x < w; ++x, ++src)
 
819
                    *dest++ = (qBlue(BlitzPrivate::convertFromPremult(*src)) >=
 
820
                               thresholdValue) ? 1 : 0;
 
821
            }
 
822
            break;
 
823
        }
 
824
        case Alpha:{
 
825
            for(y=0; y < h; ++y){
 
826
                dest = buffer.scanLine(y);
 
827
                for(x=0; x < w; ++x, ++src)
 
828
                    *dest++ = (qAlpha(BlitzPrivate::convertFromPremult(*src)) >=
 
829
                               thresholdValue) ? 1 : 0;
 
830
            }
 
831
            break;
 
832
        }
 
833
        }
 
834
    }
 
835
    else{
 
836
        // would be quicker to just threshold the color table, but we return
 
837
        // images w/ only 2 colors for easy conversion to bitmap
 
838
        QVector<QRgb> srcTable(img.colorTable());
 
839
        unsigned char *src;
 
840
        switch(channel){
 
841
        case Grayscale:
 
842
        case All:{
 
843
            for(y=0; y < h; ++y){
 
844
                src = img.scanLine(y); dest = buffer.scanLine(y);
 
845
                for(x=0; x < w; ++x){
 
846
                    *dest++ = (qGray(srcTable[*src++]) >= thresholdValue) ?
 
847
                        1 : 0;
 
848
                }
 
849
            }
 
850
            break;
 
851
        }
 
852
        case Brightness:{
 
853
            for(y=0; y < h; ++y){
 
854
                src = img.scanLine(y); dest = buffer.scanLine(y);
 
855
                for(x=0; x < w; ++x){
 
856
                    *dest++ = (BlitzPrivate::brightness(srcTable[*src++]) >= thresholdValue) ?
 
857
                        1 : 0;
 
858
                }
 
859
            }
 
860
            break;
 
861
        }
 
862
        case Red:{
 
863
            for(y=0; y < h; ++y){
 
864
                src = img.scanLine(y); dest = buffer.scanLine(y);
 
865
                for(x=0; x < w; ++x){
 
866
                    *dest++ = (qRed(srcTable[*src++]) >= thresholdValue) ?
 
867
                        1 : 0;
 
868
                }
 
869
            }
 
870
            break;
 
871
        }
 
872
        case Green:{
 
873
            for(y=0; y < h; ++y){
 
874
                src = img.scanLine(y); dest = buffer.scanLine(y);
 
875
                for(x=0; x < w; ++x){
 
876
                    *dest++ = (qGreen(srcTable[*src++]) >= thresholdValue) ?
 
877
                        1 : 0;
 
878
                }
 
879
            }
 
880
            break;
 
881
        }
 
882
        case Blue:{
 
883
            for(y=0; y < h; ++y){
 
884
                src = img.scanLine(y); dest = buffer.scanLine(y);
 
885
                for(x=0; x < w; ++x){
 
886
                    *dest++ = (qBlue(srcTable[*src++]) >= thresholdValue) ?
 
887
                        1 : 0;
 
888
                }
 
889
            }
 
890
            break;
 
891
        }
 
892
        case Alpha:{
 
893
            for(y=0; y < h; ++y){
 
894
                src = img.scanLine(y); dest = buffer.scanLine(y);
 
895
                for(x=0; x < w; ++x){
 
896
                    *dest++ = (qAlpha(srcTable[*src++]) >= thresholdValue) ?
 
897
                        1 : 0;
 
898
                }
 
899
            }
 
900
            break;
 
901
        }
 
902
        }
 
903
    }
 
904
    return(buffer);
 
905
 
 
906
}
 
907
 
 
908
QImage& Blitz::fade(QImage &img, float val, const QColor &color)
 
909
{
 
910
    if(img.isNull() || img.depth() == 1)
 
911
        return(img);
 
912
 
 
913
    val = qBound(0.0f, val, 1.0f);
 
914
    unsigned char tbl[256];
 
915
    for(int i=0; i < 256; ++i)
 
916
        tbl[i] = (int)(val * i + 0.5);
 
917
 
 
918
    int red = color.red();
 
919
    int green = color.green();
 
920
    int blue = color.blue();
 
921
 
 
922
    int r, g, b;
 
923
    QRgb *data, *end;
 
924
    QVector<QRgb> cTable;
 
925
 
 
926
    if(img.format() == QImage::Format_Indexed8){
 
927
        cTable = img.colorTable();
 
928
        data = (unsigned int *)cTable.data();
 
929
        end = data + img.numColors();
 
930
 
 
931
    }
 
932
    else{
 
933
        data = (unsigned int *)img.scanLine(0);
 
934
        end = data + (img.width()*img.height());
 
935
    }
 
936
 
 
937
    if(img.format() == QImage::Format_ARGB32_Premultiplied){
 
938
        QRgb col;
 
939
        while(data != end){
 
940
            col = BlitzPrivate::convertFromPremult(*data);
 
941
            r = qRed(col); g = qGreen(col); b = qBlue(col);
 
942
            *data++ =
 
943
                BlitzPrivate::convertToPremult(qRgba((r > red) ? r - tbl[r - red] : r + tbl[red - r],
 
944
                                       (g > green) ? g - tbl[g - green] : g + tbl[green - g],
 
945
                                       (b > blue)  ? b - tbl[b - blue] : b + tbl[blue - b],
 
946
                                       qAlpha(col)));
 
947
        }
 
948
    }
 
949
    else{
 
950
        while(data != end){
 
951
            r = qRed(*data); g = qGreen(*data); b = qBlue(*data);
 
952
            *data = qRgba((r > red) ? r - tbl[r - red] : r + tbl[red - r],
 
953
                          (g > green) ? g - tbl[g - green] : g + tbl[green - g],
 
954
                          (b > blue)  ? b - tbl[b - blue] : b + tbl[blue - b],
 
955
                          qAlpha(*data));
 
956
            ++data;
 
957
        }
 
958
    }
 
959
    if(img.format() == QImage::Format_Indexed8)
 
960
        img.setColorTable(cTable);
 
961
    return(img);
 
962
}
 
963
 
 
964
QImage& Blitz::flatten(QImage &img, const QColor &ca, const QColor &cb)
 
965
{
 
966
    if(img.isNull())
 
967
        return(img);
 
968
 
 
969
    if(img.depth() == 1) {
 
970
        img.setColor(0, ca.rgb());
 
971
        img.setColor(1, cb.rgb());
 
972
        return(img);
 
973
    }
 
974
 
 
975
    int r1 = ca.red(); int r2 = cb.red();
 
976
    int g1 = ca.green(); int g2 = cb.green();
 
977
    int b1 = ca.blue(); int b2 = cb.blue();
 
978
    int min = 0, max = 255;
 
979
 
 
980
    QRgb *data, *end;
 
981
    QVector<QRgb> cTable;
 
982
    if(img.format() == QImage::Format_Indexed8){
 
983
        cTable = img.colorTable();
 
984
        data = (unsigned int *)cTable.data();
 
985
        end = data + img.numColors();
 
986
 
 
987
    }
 
988
    else{
 
989
        data = (unsigned int *)img.scanLine(0);
 
990
        end = data + (img.width()*img.height());
 
991
    }
 
992
 
 
993
    // get minimum and maximum graylevel
 
994
    QRgb *ptr = data;
 
995
    int mean;
 
996
 
 
997
    if(img.format() != QImage::Format_ARGB32_Premultiplied){
 
998
        while(ptr != end){
 
999
            mean = (qRed(*ptr) + qGreen(*ptr) + qBlue(*ptr)) / 3;
 
1000
            min = qMin(min, mean);
 
1001
            max = qMax(max, mean);
 
1002
            ++ptr;
 
1003
        }
 
1004
    }
 
1005
    else{
 
1006
        QRgb pixel;
 
1007
        while(ptr != end){
 
1008
            pixel = BlitzPrivate::convertFromPremult(*ptr);
 
1009
            mean = (qRed(pixel) + qGreen(pixel) + qBlue(pixel)) / 3;
 
1010
            min = qMin(min, mean);
 
1011
            max = qMax(max, mean);
 
1012
            ++ptr;
 
1013
        }
 
1014
    }
 
1015
 
 
1016
    // conversion factors
 
1017
    float sr = ((float) r2 - r1) / (max - min);
 
1018
    float sg = ((float) g2 - g1) / (max - min);
 
1019
    float sb = ((float) b2 - b1) / (max - min);
 
1020
 
 
1021
    if(img.format() != QImage::Format_ARGB32_Premultiplied){
 
1022
        while(data != end){
 
1023
            mean = (qRed(*data) + qGreen(*data) + qBlue(*data)) / 3;
 
1024
            *data = qRgba((unsigned char)(sr * (mean - min) + r1 + 0.5),
 
1025
                          (unsigned char)(sg * (mean - min) + g1 + 0.5),
 
1026
                          (unsigned char)(sb * (mean - min) + b1 + 0.5),
 
1027
                          qAlpha(*data));
 
1028
            ++data;
 
1029
        }
 
1030
    }
 
1031
    else{
 
1032
        QRgb pixel;
 
1033
        while(data != end){
 
1034
            pixel = BlitzPrivate::convertFromPremult(*data);
 
1035
            mean = (qRed(pixel) + qGreen(pixel) + qBlue(pixel)) / 3;
 
1036
            *data =
 
1037
                BlitzPrivate::
 
1038
                convertToPremult(qRgba((unsigned char)(sr * (mean - min) + r1 + 0.5),
 
1039
                                       (unsigned char)(sg * (mean - min) + g1 + 0.5),
 
1040
                                       (unsigned char)(sb * (mean - min) + b1 + 0.5),
 
1041
                                       qAlpha(*data)));
 
1042
            ++data;
 
1043
        }
 
1044
    }
 
1045
 
 
1046
    if(img.format() == QImage::Format_Indexed8)
 
1047
        img.setColorTable(cTable);
 
1048
    return(img);
 
1049
}
 
1050
 
 
1051