1.QImage createAlphaMask(Qt::ImageConversionFlags flags = Qt::AutoColor) const;
/*!
Builds and returns a 1-bpp mask from the alpha buffer in this
image. Returns a null image if the image's format is
QImage::Format_RGB32.
The \a flags argument is a bitwise-OR of the
Qt::ImageConversionFlags, and controls the conversion
process. Passing 0 for flags sets all the default options.
The returned image has little-endian bit order (i.e. the image's
format is QImage::Format_MonoLSB), which you can convert to
big-endian (QImage::Format_Mono) using the convertToFormat()
function.
\sa createHeuristicMask(), {QImage#Image Transformations}{Image
Transformations}
*/
QImage QImage::createAlphaMask(Qt::ImageConversionFlags flags) const
{
if (!d || d->format == QImage::Format_RGB32)
return QImage();
if (d->depth == 1) {
// A monochrome pixmap, with alpha channels on those two colors.
// Pretty unlikely, so use less efficient solution.
return convertToFormat(Format_Indexed8, flags).createAlphaMask(flags);
}
QImage mask(d->width, d->height, Format_MonoLSB);
if (!mask.isNull()) {
dither_to_Mono(mask.d, d, flags, true);
copyPhysicalMetadata(mask.d, d);
}
return mask;
}
void dither_to_Mono(QImageData *dst, const QImageData *src,
Qt::ImageConversionFlags flags, bool fromalpha)
{
Q_ASSERT(src->width == dst->width);
Q_ASSERT(src->height == dst->height);
Q_ASSERT(dst->format == QImage::Format_Mono || dst->format == QImage::Format_MonoLSB);
dst->colortable.clear();
dst->colortable.append(0xffffffff);
dst->colortable.append(0xff000000);
enum { Threshold, Ordered, Diffuse } dithermode;
if (fromalpha) {
if ((flags & Qt::AlphaDither_Mask) == Qt::DiffuseAlphaDither)
dithermode = Diffuse;
else if ((flags & Qt::AlphaDither_Mask) == Qt::OrderedAlphaDither)
dithermode = Ordered;
else
dithermode = Threshold;
} else {
if ((flags & Qt::Dither_Mask) == Qt::ThresholdDither)
dithermode = Threshold;
else if ((flags & Qt::Dither_Mask) == Qt::OrderedDither)
dithermode = Ordered;
else
dithermode = Diffuse;
}
int w = src->width;
int h = src->height;
int d = src->depth;
uchar gray[256]; // gray map for 8 bit images
bool use_gray = (d == 8);
if (use_gray) { // make gray map
if (fromalpha) {
// Alpha 0x00 -> 0 pixels (white)
// Alpha 0xFF -> 1 pixels (black)
for (int i = 0; i < src->colortable.size(); i++)
gray[i] = (255 - (src->colortable.at(i) >> 24));
} else {
// Pixel 0x00 -> 1 pixels (black)
// Pixel 0xFF -> 0 pixels (white)
for (int i = 0; i < src->colortable.size(); i++)
gray[i] = qGray(src->colortable.at(i));
}
}
uchar *dst_data = dst->data;
qsizetype dst_bpl = dst->bytes_per_line;
const uchar *src_data = src->data;
qsizetype src_bpl = src->bytes_per_line;
switch (dithermode) {
case Diffuse: {
QScopedArrayPointer lineBuffer(new int[w * 2]);
int *line1 = lineBuffer.data();
int *line2 = lineBuffer.data() + w;
int bmwidth = (w+7)/8;
int *b1, *b2;
int wbytes = w * (d/8);
const uchar *p = src->data;
const uchar *end = p + wbytes;
b2 = line2;
if (use_gray) { // 8 bit image
while (p < end)
*b2++ = gray[*p++];
} else { // 32 bit image
if (fromalpha) {
while (p < end) {
*b2++ = 255 - (*(const uint*)p >> 24);
p += 4;
}
} else {
while (p < end) {
*b2++ = qGray(*(const uint*)p);
p += 4;
}
}
}
for (int y=0; ydata + (y+1)*src->bytes_per_line;
end = p + wbytes;
b2 = line2;
if (use_gray) { // 8 bit image
while (p < end)
*b2++ = gray[*p++];
} else { // 24 bit image
if (fromalpha) {
while (p < end) {
*b2++ = 255 - (*(const uint*)p >> 24);
p += 4;
}
} else {
while (p < end) {
*b2++ = qGray(*(const uint*)p);
p += 4;
}
}
}
}
int err;
uchar *p = dst->data + y*dst->bytes_per_line;
memset(p, 0, bmwidth);
b1 = line1;
b2 = line2;
int bit = 7;
for (int x=1; x<=w; x++) {
if (*b1 < 128) { // black pixel
err = *b1++;
*p |= 1 << bit;
} else { // white pixel
err = *b1++ - 255;
}
if (bit == 0) {
p++;
bit = 7;
} else {
bit--;
}
const int e7 = ((err * 7) + 8) >> 4;
const int e5 = ((err * 5) + 8) >> 4;
const int e3 = ((err * 3) + 8) >> 4;
const int e1 = err - (e7 + e5 + e3);
if (x < w)
*b1 += e7; // spread error to right pixel
if (not_last_line) {
b2[0] += e5; // pixel below
if (x > 1)
b2[-1] += e3; // pixel below left
if (x < w)
b2[1] += e1; // pixel below right
}
b2++;
}
}
} break;
case Ordered: {
memset(dst->data, 0, dst->nbytes);
if (d == 32) {
for (int i=0; i> 24) >= qt_bayer_matrix[j++&15][i&15])
*m |= 1 << bit;
if (bit == 0) {
m++;
bit = 7;
} else {
bit--;
}
}
} else {
while (p < end) {
if ((uint)qGray(*p++) < qt_bayer_matrix[j++&15][i&15])
*m |= 1 << bit;
if (bit == 0) {
m++;
bit = 7;
} else {
bit--;
}
}
}
dst_data += dst_bpl;
src_data += src_bpl;
}
} else if (d == 8) {
for (int i=0; idata, 0, dst->nbytes);
if (d == 32) {
for (int i=0; i> 24) >= 128)
*m |= 1 << bit; // Set mask "on"
if (bit == 0) {
m++;
bit = 7;
} else {
bit--;
}
}
} else {
while (p < end) {
if (qGray(*p++) < 128)
*m |= 1 << bit; // Set pixel "black"
if (bit == 0) {
m++;
bit = 7;
} else {
bit--;
}
}
}
dst_data += dst_bpl;
src_data += src_bpl;
}
} else
if (d == 8) {
for (int i=0; iformat == QImage::Format_MonoLSB) {
// need to swap bit order
uchar *sl = dst->data;
int bpl = (dst->width + 7) * dst->depth / 8;
int pad = dst->bytes_per_line - bpl;
for (int y=0; yheight; ++y) {
for (int x=0; xdpmx = src->dpmx;
dst->dpmy = src->dpmy;
dst->devicePixelRatio = src->devicePixelRatio;
}
主要是通过dither_to_Mono 这个函数对图像数据进行处理。
2. QImage createHeuristicMask(bool clipTight = true) const;
/*!
Creates and returns a 1-bpp heuristic mask for this image.
The function works by selecting a color from one of the corners,
then chipping away pixels of that color starting at all the edges.
The four corners vote for which color is to be masked away. In
case of a draw (this generally means that this function is not
applicable to the image), the result is arbitrary.
The returned image has little-endian bit order (i.e. the image's
format is QImage::Format_MonoLSB), which you can convert to
big-endian (QImage::Format_Mono) using the convertToFormat()
function.
If \a clipTight is true (the default) the mask is just large
enough to cover the pixels; otherwise, the mask is larger than the
data pixels.
Note that this function disregards the alpha buffer.
\sa createAlphaMask(), {QImage#Image Transformations}{Image
Transformations}
*/
QImage QImage::createHeuristicMask(bool clipTight) const
{
if (!d)
return QImage();
if (d->depth != 32) {
QImage img32 = convertToFormat(Format_RGB32);
return img32.createHeuristicMask(clipTight);
}
#define PIX(x,y) (*((const QRgb*)scanLine(y)+x) & 0x00ffffff)
int w = width();
int h = height();
QImage m(w, h, Format_MonoLSB);
QIMAGE_SANITYCHECK_MEMORY(m);
m.setColorCount(2);
m.setColor(0, QColor(Qt::color0).rgba());
m.setColor(1, QColor(Qt::color1).rgba());
m.fill(0xff);
QRgb background = PIX(0,0);
if (background != PIX(w-1,0) &&
background != PIX(0,h-1) &&
background != PIX(w-1,h-1)) {
background = PIX(w-1,0);
if (background != PIX(w-1,h-1) &&
background != PIX(0,h-1) &&
PIX(0,h-1) == PIX(w-1,h-1)) {
background = PIX(w-1,h-1);
}
}
int x,y;
bool done = false;
uchar *ypp, *ypc, *ypn;
while(!done) {
done = true;
ypn = m.scanLine(0);
ypc = nullptr;
for (y = 0; y < h; y++) {
ypp = ypc;
ypc = ypn;
ypn = (y == h-1) ? nullptr : m.scanLine(y+1);
const QRgb *p = (const QRgb *)scanLine(y);
for (x = 0; x < w; x++) {
// slowness here - it's possible to do six of these tests
// together in one go. oh well.
if ((x == 0 || y == 0 || x == w-1 || y == h-1 ||
!(*(ypc + ((x-1) >> 3)) & (1 << ((x-1) & 7))) ||
!(*(ypc + ((x+1) >> 3)) & (1 << ((x+1) & 7))) ||
!(*(ypp + (x >> 3)) & (1 << (x & 7))) ||
!(*(ypn + (x >> 3)) & (1 << (x & 7)))) &&
( (*(ypc + (x >> 3)) & (1 << (x & 7)))) &&
((*p & 0x00ffffff) == background)) {
done = false;
*(ypc + (x >> 3)) &= ~(1 << (x & 7));
}
p++;
}
}
}
if (!clipTight) {
ypn = m.scanLine(0);
ypc = nullptr;
for (y = 0; y < h; y++) {
ypp = ypc;
ypc = ypn;
ypn = (y == h-1) ? nullptr : m.scanLine(y+1);
const QRgb *p = (const QRgb *)scanLine(y);
for (x = 0; x < w; x++) {
if ((*p & 0x00ffffff) != background) {
if (x > 0)
*(ypc + ((x-1) >> 3)) |= (1 << ((x-1) & 7));
if (x < w-1)
*(ypc + ((x+1) >> 3)) |= (1 << ((x+1) & 7));
if (y > 0)
*(ypp + (x >> 3)) |= (1 << (x & 7));
if (y < h-1)
*(ypn + (x >> 3)) |= (1 << (x & 7));
}
p++;
}
}
}
#undef PIX
copyPhysicalMetadata(m.d, d);
return m;
}
3. mirrored()
QImage mirrored(bool horizontally = false, bool vertically = true) const &
{ return mirrored_helper(horizontally, vertically); }
QImage QImage::mirrored_helper(bool horizontal, bool vertical) const
{
if (!d)
return QImage();
if ((d->width <= 1 && d->height <= 1) || (!horizontal && !vertical))
return *this;
// Create result image, copy colormap
QImage result(d->width, d->height, d->format);
QIMAGE_SANITYCHECK_MEMORY(result);
// check if we ran out of of memory..
if (!result.d)
return QImage();
result.d->colortable = d->colortable;
result.d->has_alpha_clut = d->has_alpha_clut;
copyMetadata(result.d, d);
do_mirror(result.d, d, horizontal, vertical);
return result;
}
inline void do_mirror(QImageData *dst, QImageData *src, bool horizontal, bool vertical)
{
Q_ASSERT(src->width == dst->width && src->height == dst->height && src->depth == dst->depth);
int w = src->width;
int h = src->height;
int depth = src->depth;
if (src->depth == 1) {
w = (w + 7) / 8; // byte aligned width
depth = 8;
}
if (vertical && !horizontal) {
// This one is simple and common, so do it a little more optimized
do_flip(dst, src, w, h, depth);
return;
}
int dstX0 = 0, dstXIncr = 1;
int dstY0 = 0, dstYIncr = 1;
if (horizontal) {
// 0 -> w-1, 1 -> w-2, 2 -> w-3, ...
dstX0 = w - 1;
dstXIncr = -1;
}
if (vertical) {
// 0 -> h-1, 1 -> h-2, 2 -> h-3, ...
dstY0 = h - 1;
dstYIncr = -1;
}
switch (depth) {
case 64:
do_mirror_data(dst, src, dstX0, dstY0, dstXIncr, dstYIncr, w, h);
break;
case 32:
do_mirror_data(dst, src, dstX0, dstY0, dstXIncr, dstYIncr, w, h);
break;
case 24:
do_mirror_data(dst, src, dstX0, dstY0, dstXIncr, dstYIncr, w, h);
break;
case 16:
do_mirror_data(dst, src, dstX0, dstY0, dstXIncr, dstYIncr, w, h);
break;
case 8:
do_mirror_data(dst, src, dstX0, dstY0, dstXIncr, dstYIncr, w, h);
break;
default:
Q_ASSERT(false);
break;
}
// The bytes are now all in the correct place. In addition, the bits in the individual
// bytes have to be flipped too when horizontally mirroring a 1 bit-per-pixel image.
if (horizontal && dst->depth == 1) {
Q_ASSERT(dst->format == QImage::Format_Mono || dst->format == QImage::Format_MonoLSB);
const int shift = 8 - (dst->width % 8);
const uchar *bitflip = qt_get_bitflip_array();
for (int y = 0; y < h; ++y) {
uchar *begin = dst->data + y * dst->bytes_per_line;
uchar *end = begin + dst->bytes_per_line;
for (uchar *p = begin; p < end; ++p) {
*p = bitflip[*p];
// When the data is non-byte aligned, an extra bit shift (of the number of
// unused bits at the end) is needed for the entire scanline.
if (shift != 8 && p != begin) {
if (dst->format == QImage::Format_Mono) {
for (int i = 0; i < shift; ++i) {
p[-1] <<= 1;
p[-1] |= (*p & (128 >> i)) >> (7 - i);
}
} else {
for (int i = 0; i < shift; ++i) {
p[-1] >>= 1;
p[-1] |= (*p & (1 << i)) << (7 - i);
}
}
}
}
if (shift != 8) {
if (dst->format == QImage::Format_Mono)
end[-1] <<= shift;
else
end[-1] >>= shift;
}
}
}
}
你可以看到, 通过do_mirror函数进行了镜像操作。