最近在项目中实现壁纸预览功能时,遇到了图片无法正常加载的问题。开始策略是在重绘事件中直接加载硬盘中的图片文件,代码如下:
void YourClass::paintEvent(QPaintEvent* event)
{
QPainter p(this);
p.drawPixmap(0, 0, width(), height(), QPixmap("E:/c++proj/testBlog/StockSnap_3C5NLBSRKD.jpg").scaled(width(), height(), Qt::KeepAspectRatio));
}
当图片较大的时候,主机性能较差时,界面显示白屏,无法正常加载图片。效果如下:
之后变选择使用imagereader的方法读取图片:代码如下:
void YourClass::paintEvent(QPaintEvent* event)
{
QPainter p(this);
QImageReader reader;
QImage image;
reader.setDecideFormatFromContent(true);
reader.setScaledSize(QSize(width(), height()));
reader.setFileName("E:/c++proj/testBlog/Image/StockSnap_3C5NLBSRKD.jpg");
if (reader.canRead())
{
if (!reader.read(&image))
{
QImageReader::ImageReaderError error = reader.error();
QString strError = reader.errorString();
printf("last error:%s\n", strError.toStdString().c_str());
return;
}
}
p.drawPixmap(0, 0, width(), height(), QPixmap::fromImage(image)/*.scaled(width(), height(), Qt::KeepAspectRatio)*/);
}
此时可正常显示:
但遇到一个100M的BMP图片无法加载的问题,查阅资料得知加载时较大的图片会因内存不足无法加载,显示一片空白,此时可通过分段读取的方法解决。
具体代码如下:
QBmpLoader.h
#pragma once
#include <QtGui/QImageIOHandler>
struct BMP_FILEHDR { // BMP file header
char bfType[2]; // "BM"
qint32 bfSize; // size of file
qint16 bfReserved1;
qint16 bfReserved2;
qint32 bfOffBits; // pointer to the pixmap bits
};
struct BMP_INFOHDR { // BMP information header
qint32 biSize; // size of this struct
qint32 biWidth; // pixmap width
qint32 biHeight; // pixmap height
qint16 biPlanes; // should be 1
qint16 biBitCount; // number of bits per pixel
qint32 biCompression; // compression method
qint32 biSizeImage; // size of image
qint32 biXPelsPerMeter; // horizontal resolution
qint32 biYPelsPerMeter; // vertical resolution
qint32 biClrUsed; // number of colors used
qint32 biClrImportant; // number of important colors
};
namespace setting
{
class QBmpLoader
{
public:
enum State {
Ready,
ReadHeader,
Error
};
QIODevice* device();
void setDevice(QIODevice *device);
QBmpLoader();
bool setImagePath(QString imagePath);
bool read(QImage *image);
bool read(QImage *image, const QRect& rc);
bool read(QImage *image, const QRect& range
, double dScalX
, double dScalY);
bool isOk() { return state != Error; }
BMP_FILEHDR fileHeader;
BMP_INFOHDR infoHeader;
private:
bool readHeader();
int startpos;
QString imagePath;
State state;
QIODevice *m_device;
};
}
QBmpLoader.cpp
#include "QBmploader.h"
#include <qimage.h>
#include <qvariant.h>
#include <qvector.h>
#include <QFile>
#include <QDebug>
#include <qmath.h>
static void swapPixel01(QImage *image) // 1-bpp: swap 0 and 1 pixels
{
int i;
if (image->depth() == 1 && image->colorCount() == 2) {
register uint *p = (uint *)image->bits();
int nbytes = image->byteCount();
for (i = 0; i<nbytes / 4; i++) {
*p = ~*p;
p++;
}
uchar *p2 = (uchar *)p;
for (i = 0; i<(nbytes & 3); i++) {
*p2 = ~*p2;
p2++;
}
QRgb t = image->color(0); // swap color 0 and 1
image->setColor(0, image->color(1));
image->setColor(1, t);
}
}
/*
QImageIO::defineIOHandler("BMP", "^BM", 0,
read_bmp_image, write_bmp_image);
*/
/*****************************************************************************
BMP (DIB) image read/write functions
*****************************************************************************/
const int BMP_FILEHDR_SIZE = 14; // size of BMP_FILEHDR data
static QDataStream &operator >> (QDataStream &s, BMP_FILEHDR &bf)
{ // read file header
s.readRawData(bf.bfType, 2);
s >> bf.bfSize >> bf.bfReserved1 >> bf.bfReserved2 >> bf.bfOffBits;
return s;
}
static QDataStream &operator<<(QDataStream &s, const BMP_FILEHDR &bf)
{ // write file header
s.writeRawData(bf.bfType, 2);
s << bf.bfSize << bf.bfReserved1 << bf.bfReserved2 << bf.bfOffBits;
return s;
}
const int BMP_OLD = 12; // old Windows/OS2 BMP size
const int BMP_WIN = 40; // Windows BMP v3 size
const int BMP_OS2 = 64; // new OS/2 BMP size
const int BMP_WIN4 = 108; // Windows BMP v4 size
const int BMP_WIN5 = 124; // Windows BMP v5 size
const int BMP_RGB = 0; // no compression
const int BMP_RLE8 = 1; // run-length encoded, 8 bits
const int BMP_RLE4 = 2; // run-length encoded, 4 bits
const int BMP_BITFIELDS = 3; // RGB values encoded in data as bit-fields
static QDataStream &operator >> (QDataStream &s, BMP_INFOHDR &bi)
{
s >> bi.biSize;
if (bi.biSize == BMP_WIN || bi.biSize == BMP_OS2 || bi.biSize == BMP_WIN4 || bi.biSize == BMP_WIN5) {
s >> bi.biWidth >> bi.biHeight >> bi.biPlanes >> bi.biBitCount;
s >> bi.biCompression >> bi.biSizeImage;
s >> bi.biXPelsPerMeter >> bi.biYPelsPerMeter;
s >> bi.biClrUsed >> bi.biClrImportant;
}
else { // probably old Windows format
qint16 w, h;
s >> w >> h >> bi.biPlanes >> bi.biBitCount;
bi.biWidth = w;
bi.biHeight = h;
bi.biCompression = BMP_RGB; // no compression
bi.biSizeImage = 0;
bi.biXPelsPerMeter = bi.biYPelsPerMeter = 0;
bi.biClrUsed = bi.biClrImportant = 0;
}
return s;
}
static QDataStream &operator<<(QDataStream &s, const BMP_INFOHDR &bi)
{
s << bi.biSize;
s << bi.biWidth << bi.biHeight;
s << bi.biPlanes;
s << bi.biBitCount;
s << bi.biCompression;
s << bi.biSizeImage;
s << bi.biXPelsPerMeter << bi.biYPelsPerMeter;
s << bi.biClrUsed << bi.biClrImportant;
return s;
}
static int calc_shift(int mask)
{
int result = 0;
while (mask && !(mask & 1)) {
result++;
mask >>= 1;
}
return result;
}
static bool read_dib_fileheader(QDataStream &s, BMP_FILEHDR &bf)
{
// read BMP file header
s >> bf;
if (s.status() != QDataStream::Ok)
return false;
// check header
if (qstrncmp(bf.bfType, "BM", 2) != 0)
return false;
return true;
}
static bool read_dib_infoheader(QDataStream &s, BMP_INFOHDR &bi)
{
s >> bi; // read BMP info header
if (s.status() != QDataStream::Ok)
return false;
int nbits = bi.biBitCount;
int comp = bi.biCompression;
if (!(nbits == 1 || nbits == 4 || nbits == 8 || nbits == 16 || nbits == 24 || nbits == 32) ||
bi.biPlanes != 1 || comp > BMP_BITFIELDS)
return false; // weird BMP image
if (!(comp == BMP_RGB || (nbits == 4 && comp == BMP_RLE4) ||
(nbits == 8 && comp == BMP_RLE8) || ((nbits == 16 || nbits == 32) && comp == BMP_BITFIELDS)))
return false; // weird compression type
return true;
}
static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, int offset, int startpos, QImage &image)
{
QIODevice* d = s.device();
if (d->atEnd()) // end of stream/file
return false;
#if 0
qDebug("offset...........%d", offset);
qDebug("startpos.........%d", startpos);
qDebug("biSize...........%d", bi.biSize);
qDebug("biWidth..........%d", bi.biWidth);
qDebug("biHeight.........%d", bi.biHeight);
qDebug("biPlanes.........%d", bi.biPlanes);
qDebug("biBitCount.......%d", bi.biBitCount);
qDebug("biCompression....%d", bi.biCompression);
qDebug("biSizeImage......%d", bi.biSizeImage);
qDebug("biXPelsPerMeter..%d", bi.biXPelsPerMeter);
qDebug("biYPelsPerMeter..%d", bi.biYPelsPerMeter);
qDebug("biClrUsed........%d", bi.biClrUsed);
qDebug("biClrImportant...%d", bi.biClrImportant);
#endif
int w = bi.biWidth, h = bi.biHeight, nbits = bi.biBitCount;
int t = bi.biSize, comp = bi.biCompression;
int red_mask = 0;
int green_mask = 0;
int blue_mask = 0;
int red_shift = 0;
int green_shift = 0;
int blue_shift = 0;
int red_scale = 0;
int green_scale = 0;
int blue_scale = 0;
int ncols = 0;
int depth = 0;
QImage::Format format;
switch (nbits) {
case 32:
case 24:
case 16:
depth = 32;
format = QImage::Format_RGB32;
break;
case 8:
case 4:
depth = 8;
format = QImage::Format_Indexed8;
break;
default:
depth = 1;
format = QImage::Format_Mono;
}
if (bi.biHeight < 0)
h = -h; // support images with negative height
if (image.size() != QSize(w, h) || image.format() != format) {
image = QImage(w, h, format);
if (image.isNull()) // could not create image
return false;
}
if (depth != 32) {
ncols = bi.biClrUsed ? bi.biClrUsed : 1 << nbits;
if (ncols > 256) // sanity check - don't run out of mem if color table is broken
return false;
image.setColorCount(ncols);
}
image.setDotsPerMeterX(bi.biXPelsPerMeter);
image.setDotsPerMeterY(bi.biYPelsPerMeter);
if (!d->isSequential())
d->seek(startpos + BMP_FILEHDR_SIZE + (bi.biSize >= BMP_WIN4 ? BMP_WIN : bi.biSize)); // goto start of colormap
if (bi.biSize >= BMP_WIN4 || (comp == BMP_BITFIELDS && (nbits == 16 || nbits == 32))) {
if (d->read((char *)&red_mask, sizeof(red_mask)) != sizeof(red_mask))
return false;
if (d->read((char *)&green_mask, sizeof(green_mask)) != sizeof(green_mask))
return false;
if (d->read((char *)&blue_mask, sizeof(blue_mask)) != sizeof(blue_mask))
return false;
// Read BMP v4+ header
if (bi.biSize >= BMP_WIN4) {
int alpha_mask = 0;
int CSType = 0;
int gamma_red = 0;
int gamma_green = 0;
int gamma_blue = 0;
int endpoints[9];
if (d->read((char *)&alpha_mask, sizeof(alpha_mask)) != sizeof(alpha_mask))
return false;
if (d->read((char *)&CSType, sizeof(CSType)) != sizeof(CSType))
return false;
if (d->read((char *)&endpoints, sizeof(endpoints)) != sizeof(endpoints))
return false;
if (d->read((char *)&gamma_red, sizeof(gamma_red)) != sizeof(gamma_red))
return false;
if (d->read((char *)&gamma_green, sizeof(gamma_green)) != sizeof(gamma_green))
return false;
if (d->read((char *)&gamma_blue, sizeof(gamma_blue)) != sizeof(gamma_blue))
return false;
if (bi.biSize == BMP_WIN5) {
qint32 intent = 0;
qint32 profileData = 0;
qint32 profileSize = 0;
qint32 reserved = 0;
if (d->read((char *)&intent, sizeof(intent)) != sizeof(intent))
return false;
if (d->read((char *)&profileData, sizeof(profileData)) != sizeof(profileData))
return false;
if (d->read((char *)&profileSize, sizeof(profileSize)) != sizeof(profileSize))
return false;
if (d->read((char *)&reserved, sizeof(reserved)) != sizeof(reserved) || reserved != 0)
return false;
}
}
}
if (ncols > 0) { // read color table
uchar rgb[4];
int rgb_len = t == BMP_OLD ? 3 : 4;
for (int i = 0; i<ncols; i++) {
if (d->read((char *)rgb, rgb_len) != rgb_len)
return false;
image.setColor(i, qRgb(rgb[2], rgb[1], rgb[0]));
if (d->atEnd()) // truncated file
return false;
}
}
else if (comp == BMP_BITFIELDS && (nbits == 16 || nbits == 32)) {
red_shift = calc_shift(red_mask);
red_scale = 256 / ((red_mask >> red_shift) + 1);
green_shift = calc_shift(green_mask);
green_scale = 256 / ((green_mask >> green_shift) + 1);
blue_shift = calc_shift(blue_mask);
blue_scale = 256 / ((blue_mask >> blue_shift) + 1);
}
else if (comp == BMP_RGB && (nbits == 24 || nbits == 32)) {
blue_mask = 0x000000ff;
green_mask = 0x0000ff00;
red_mask = 0x00ff0000;
blue_shift = 0;
green_shift = 8;
red_shift = 16;
blue_scale = green_scale = red_scale = 1;
}
else if (comp == BMP_RGB && nbits == 16) {
blue_mask = 0x001f;
green_mask = 0x03e0;
red_mask = 0x7c00;
blue_shift = 0;
green_shift = 2;
red_shift = 7;
red_scale = 1;
green_scale = 1;
blue_scale = 8;
}
// offset can be bogus, be careful
if (offset >= 0 && startpos + offset > d->pos()) {
if (!d->isSequential())
d->seek(startpos + offset); // start of image data
}
int bpl = image.bytesPerLine();
uchar *data = image.bits();
if (nbits == 1) { // 1 bit BMP image
while (--h >= 0) {
if (d->read((char*)(data + h*bpl), bpl) != bpl)
break;
}
if (ncols == 2 && qGray(image.color(0)) < qGray(image.color(1)))
swapPixel01(&image); // pixel 0 is white!
}
else if (nbits == 4) { // 4 bit BMP image
int buflen = ((w + 7) / 8) * 4;
uchar *buf = new uchar[buflen];
if (comp == BMP_RLE4) { // run length compression
int x = 0, y = 0, c, i;
quint8 b;
register uchar *p = data + (h - 1)*bpl;
const uchar *endp = p + w;
while (y < h) {
if (!d->getChar((char *)&b))
break;
if (b == 0) { // escape code
if (!d->getChar((char *)&b) || b == 1) {
y = h; // exit loop
}
else switch (b) {
case 0: // end of line
x = 0;
y++;
p = data + (h - y - 1)*bpl;
break;
case 2: // delta (jump)
{
quint8 tmp;
d->getChar((char *)&tmp);
x += tmp;
d->getChar((char *)&tmp);
y += tmp;
}
// Protection
if ((uint)x >= (uint)w)
x = w - 1;
if ((uint)y >= (uint)h)
y = h - 1;
p = data + (h - y - 1)*bpl + x;
break;
default: // absolute mode
// Protection
if (p + b > endp)
b = endp - p;
i = (c = b) / 2;
while (i--) {
d->getChar((char *)&b);
*p++ = b >> 4;
*p++ = b & 0x0f;
}
if (c & 1) {
unsigned char tmp;
d->getChar((char *)&tmp);
*p++ = tmp >> 4;
}
if ((((c & 3) + 1) & 2) == 2)
d->getChar(0); // align on word boundary
x += c;
}
}
else { // encoded mode
// Protection
if (p + b > endp)
b = endp - p;
i = (c = b) / 2;
d->getChar((char *)&b); // 2 pixels to be repeated
while (i--) {
*p++ = b >> 4;
*p++ = b & 0x0f;
}
if (c & 1)
*p++ = b >> 4;
x += c;
}
}
}
else if (comp == BMP_RGB) { // no compression
memset(data, 0, h*bpl);
while (--h >= 0) {
if (d->read((char*)buf, buflen) != buflen)
break;
register uchar *p = data + h*bpl;
uchar *b = buf;
for (int i = 0; i<w / 2; i++) { // convert nibbles to bytes
*p++ = *b >> 4;
*p++ = *b++ & 0x0f;
}
if (w & 1) // the last nibble
*p = *b >> 4;
}
}
delete[] buf;
}
else if (nbits == 8) { // 8 bit BMP image
if (comp == BMP_RLE8) { // run length compression
int x = 0, y = 0;
quint8 b;
register uchar *p = data + (h - 1)*bpl;
const uchar *endp = p + w;
while (y < h) {
if (!d->getChar((char *)&b))
break;
if (b == 0) { // escape code
if (!d->getChar((char *)&b) || b == 1) {
y = h; // exit loop
}
else switch (b) {
case 0: // end of line
x = 0;
y++;
p = data + (h - y - 1)*bpl;
break;
case 2: // delta (jump)
// Protection
if ((uint)x >= (uint)w)
x = w - 1;
if ((uint)y >= (uint)h)
y = h - 1;
{
quint8 tmp;
d->getChar((char *)&tmp);
x += tmp;
d->getChar((char *)&tmp);
y += tmp;
}
p = data + (h - y - 1)*bpl + x;
break;
default: // absolute mode
// Protection
if (p + b > endp)
b = endp - p;
if (d->read((char *)p, b) != b)
return false;
if ((b & 1) == 1)
d->getChar(0); // align on word boundary
x += b;
p += b;
}
}
else { // encoded mode
// Protection
if (p + b > endp)
b = endp - p;
char tmp;
d->getChar(&tmp);
memset(p, tmp, b); // repeat pixel
x += b;
p += b;
}
}
}
else if (comp == BMP_RGB) { // uncompressed
while (--h >= 0) {
if (d->read((char *)data + h*bpl, bpl) != bpl)
break;
}
}
}
else if (nbits == 16 || nbits == 24 || nbits == 32) { // 16,24,32 bit BMP image
register QRgb *p;
QRgb *end;
uchar *buf24 = new uchar[bpl];
int bpl24 = ((w*nbits + 31) / 32) * 4;
uchar *b;
int c;
while (--h >= 0) {
p = (QRgb *)(data + h*bpl);
end = p + w;
if (d->read((char *)buf24, bpl24) != bpl24)
break;
b = buf24;
while (p < end) {
c = *(uchar*)b | (*(uchar*)(b + 1) << 8);
if (nbits != 16)
c |= *(uchar*)(b + 2) << 16;
*p++ = qRgb(((c & red_mask) >> red_shift) * red_scale,
((c & green_mask) >> green_shift) * green_scale,
((c & blue_mask) >> blue_shift) * blue_scale);
b += nbits / 8;
}
}
delete[] buf24;
}
if (bi.biHeight < 0) {
// Flip the image
uchar *buf = new uchar[bpl];
h = -bi.biHeight;
for (int y = 0; y < h / 2; ++y) {
memcpy(buf, data + y*bpl, bpl);
memcpy(data + y*bpl, data + (h - y - 1)*bpl, bpl);
memcpy(data + (h - y - 1)*bpl, buf, bpl);
}
delete[] buf;
}
return true;
}
static bool read_dib_bodyParitial(QDataStream &s, const BMP_INFOHDR &bi
, int offset, int startpos, QImage &image
, const QRect& rc
, double dScalX
, double dScalY)
{
QIODevice* d = s.device();
// if (d->atEnd()) // end of stream/file
// return false;
#if 0
qDebug("offset...........%d", offset);
qDebug("startpos.........%d", startpos);
qDebug("biSize...........%d", bi.biSize);
qDebug("biWidth..........%d", bi.biWidth);
qDebug("biHeight.........%d", bi.biHeight);
qDebug("biPlanes.........%d", bi.biPlanes);
qDebug("biBitCount.......%d", bi.biBitCount);
qDebug("biCompression....%d", bi.biCompression);
qDebug("biSizeImage......%d", bi.biSizeImage);
qDebug("biXPelsPerMeter..%d", bi.biXPelsPerMeter);
qDebug("biYPelsPerMeter..%d", bi.biYPelsPerMeter);
qDebug("biClrUsed........%d", bi.biClrUsed);
qDebug("biClrImportant...%d", bi.biClrImportant);
#endif
int w = bi.biWidth, h = bi.biHeight, nbits = bi.biBitCount;
int t = bi.biSize, comp = bi.biCompression;
int red_mask = 0;
int green_mask = 0;
int blue_mask = 0;
int red_shift = 0;
int green_shift = 0;
int blue_shift = 0;
int red_scale = 0;
int green_scale = 0;
int blue_scale = 0;
int ncols = 0;
int depth = 0;
QImage::Format format;
switch (nbits) {
case 32:
case 24:
case 16:
depth = 32;
format = QImage::Format_RGB32;
break;
case 8:
case 4:
depth = 8;
format = QImage::Format_Indexed8;
break;
default:
depth = 1;
format = QImage::Format_Mono;
}
if (bi.biHeight < 0)
h = -h; // support images with negative height
// if(rc.bottom()>h||rc.right()>w){
// return false;
// }
int w2 = rc.width();//w2= w;
int h2 = rc.height();//h2= h;
if (rc.bottom()>h) {
h2 = h;
}
if (rc.right()>w) {
w2 = w;
}
if (image.size() != QSize(w2, h2) || image.format() != format) {
image = QImage(w2, h2, format);
if (image.isNull()) // could not create image
return false;
}
if (depth != 32) {
ncols = bi.biClrUsed ? bi.biClrUsed : 1 << nbits;
if (ncols > 256) // sanity check - don't run out of mem if color table is broken
return false;
image.setColorCount(ncols);
}
image.setDotsPerMeterX(bi.biXPelsPerMeter);
image.setDotsPerMeterY(bi.biYPelsPerMeter);
if (!d->isSequential())
d->seek(startpos + BMP_FILEHDR_SIZE + (bi.biSize >= BMP_WIN4 ? BMP_WIN : bi.biSize)); // goto start of colormap
if (bi.biSize >= BMP_WIN4 || (comp == BMP_BITFIELDS && (nbits == 16 || nbits == 32))) {
if (d->read((char *)&red_mask, sizeof(red_mask)) != sizeof(red_mask))
return false;
if (d->read((char *)&green_mask, sizeof(green_mask)) != sizeof(green_mask))
return false;
if (d->read((char *)&blue_mask, sizeof(blue_mask)) != sizeof(blue_mask))
return false;
// Read BMP v4+ header
if (bi.biSize >= BMP_WIN4) {
int alpha_mask = 0;
int CSType = 0;
int gamma_red = 0;
int gamma_green = 0;
int gamma_blue = 0;
int endpoints[9];
if (d->read((char *)&alpha_mask, sizeof(alpha_mask)) != sizeof(alpha_mask))
return false;
if (d->read((char *)&CSType, sizeof(CSType)) != sizeof(CSType))
return false;
if (d->read((char *)&endpoints, sizeof(endpoints)) != sizeof(endpoints))
return false;
if (d->read((char *)&gamma_red, sizeof(gamma_red)) != sizeof(gamma_red))
return false;
if (d->read((char *)&gamma_green, sizeof(gamma_green)) != sizeof(gamma_green))
return false;
if (d->read((char *)&gamma_blue, sizeof(gamma_blue)) != sizeof(gamma_blue))
return false;
if (bi.biSize == BMP_WIN5) {
qint32 intent = 0;
qint32 profileData = 0;
qint32 profileSize = 0;
qint32 reserved = 0;
if (d->read((char *)&intent, sizeof(intent)) != sizeof(intent))
return false;
if (d->read((char *)&profileData, sizeof(profileData)) != sizeof(profileData))
return false;
if (d->read((char *)&profileSize, sizeof(profileSize)) != sizeof(profileSize))
return false;
if (d->read((char *)&reserved, sizeof(reserved)) != sizeof(reserved) || reserved != 0)
return false;
}
}
}
if (ncols > 0) { // read color table
uchar rgb[4];
int rgb_len = t == BMP_OLD ? 3 : 4;
for (int i = 0; i<ncols; i++) {
if (d->read((char *)rgb, rgb_len) != rgb_len)
return false;
image.setColor(i, qRgb(rgb[2], rgb[1], rgb[0]));
if (d->atEnd()) // truncated file
return false;
}
}
else if (comp == BMP_BITFIELDS && (nbits == 16 || nbits == 32)) {
red_shift = calc_shift(red_mask);
red_scale = 256 / ((red_mask >> red_shift) + 1);
green_shift = calc_shift(green_mask);
green_scale = 256 / ((green_mask >> green_shift) + 1);
blue_shift = calc_shift(blue_mask);
blue_scale = 256 / ((blue_mask >> blue_shift) + 1);
}
else if (comp == BMP_RGB && (nbits == 24 || nbits == 32)) {
blue_mask = 0x000000ff;
green_mask = 0x0000ff00;
red_mask = 0x00ff0000;
blue_shift = 0;
green_shift = 8;
red_shift = 16;
blue_scale = green_scale = red_scale = 1;
}
else if (comp == BMP_RGB && nbits == 16) {
blue_mask = 0x001f;
green_mask = 0x03e0;
red_mask = 0x7c00;
blue_shift = 0;
green_shift = 2;
red_shift = 7;
red_scale = 1;
green_scale = 1;
blue_scale = 8;
}
// offset can be bogus, be careful
if (offset >= 0 && startpos + offset > d->pos()) {
if (!d->isSequential())
d->seek(startpos + offset); // start of image data
}
int bpl = image.bytesPerLine();
unsigned char* data = image.bits();
Q_ASSERT(data != NULL);
if (nbits == 1) { // 1 bit BMP image
while (--h >= 0) {
if (d->read((char*)(data + h*bpl), bpl) != bpl)
break;
}
if (ncols == 2 && qGray(image.color(0)) < qGray(image.color(1)))
swapPixel01(&image); // pixel 0 is white!
}
else if (nbits == 4) { // 4 bit BMP image
int buflen = ((w + 7) / 8) * 4;
uchar *buf = new uchar[buflen];
if (comp == BMP_RLE4) { // run length compression
int x = 0, y = 0, c, i;
quint8 b;
register uchar *p = data + (h - 1)*bpl;
const uchar *endp = p + w;
while (y < h) {
if (!d->getChar((char *)&b))
break;
if (b == 0) { // escape code
if (!d->getChar((char *)&b) || b == 1) {
y = h; // exit loop
}
else switch (b) {
case 0: // end of line
x = 0;
y++;
p = data + (h - y - 1)*bpl;
break;
case 2: // delta (jump)
{
quint8 tmp;
d->getChar((char *)&tmp);
x += tmp;
d->getChar((char *)&tmp);
y += tmp;
}
// Protection
if ((uint)x >= (uint)w)
x = w - 1;
if ((uint)y >= (uint)h)
y = h - 1;
p = data + (h - y - 1)*bpl + x;
break;
default: // absolute mode
// Protection
if (p + b > endp)
b = endp - p;
i = (c = b) / 2;
while (i--) {
d->getChar((char *)&b);
*p++ = b >> 4;
*p++ = b & 0x0f;
}
if (c & 1) {
unsigned char tmp;
d->getChar((char *)&tmp);
*p++ = tmp >> 4;
}
if ((((c & 3) + 1) & 2) == 2)
d->getChar(0); // align on word boundary
x += c;
}
}
else { // encoded mode
// Protection
if (p + b > endp)
b = endp - p;
i = (c = b) / 2;
d->getChar((char *)&b); // 2 pixels to be repeated
while (i--) {
*p++ = b >> 4;
*p++ = b & 0x0f;
}
if (c & 1)
*p++ = b >> 4;
x += c;
}
}
}
else if (comp == BMP_RGB) { // no compression
memset(data, 0, h*bpl);
while (--h >= 0) {
if (d->read((char*)buf, buflen) != buflen)
break;
register uchar *p = data + h*bpl;
uchar *b = buf;
for (int i = 0; i<w / 2; i++) { // convert nibbles to bytes
*p++ = *b >> 4;
*p++ = *b++ & 0x0f;
}
if (w & 1) // the last nibble
*p = *b >> 4;
}
}
delete[] buf;
}
else if (nbits == 8) { // 8 bit BMP image
if (comp == BMP_RLE8) { // run length compression
int x = 0, y = 0;
quint8 b;
register uchar *p = data + (h - 1)*bpl;
const uchar *endp = p + w;
while (y < h) {
if (!d->getChar((char *)&b))
break;
if (b == 0) { // escape code
if (!d->getChar((char *)&b) || b == 1) {
y = h; // exit loop
}
else switch (b) {
case 0: // end of line
x = 0;
y++;
p = data + (h - y - 1)*bpl;
break;
case 2: // delta (jump)
// Protection
if ((uint)x >= (uint)w)
x = w - 1;
if ((uint)y >= (uint)h)
y = h - 1;
{
quint8 tmp;
d->getChar((char *)&tmp);
x += tmp;
d->getChar((char *)&tmp);
y += tmp;
}
p = data + (h - y - 1)*bpl + x;
break;
default: // absolute mode
// Protection
if (p + b > endp)
b = endp - p;
if (d->read((char *)p, b) != b)
return false;
if ((b & 1) == 1)
d->getChar(0); // align on word boundary
x += b;
p += b;
}
}
else { // encoded mode
// Protection
if (p + b > endp)
b = endp - p;
char tmp;
d->getChar(&tmp);
memset(p, tmp, b); // repeat pixel
x += b;
p += b;
}
}
}
else if (comp == BMP_RGB) { // uncompressed
qint64 pos = d->pos();
char* temp = new char[w + 1];
if (qFuzzyCompare(dScalX, 1) && qFuzzyCompare(dScalY, 1)) {
d->seek(pos + (h - rc.y() - h2) *w);// bpl
while (--h2 >= 0) {
if (d->read(temp, w) != w)
break;
memcpy((char *)data + h2*bpl, temp + rc.x(), bpl);
}
}
else {
double yp = rc.y()*dScalY;
int xp = rc.x();
while (--h2 >= 0) {
int hp = qRound(h2*dScalY);
d->seek(pos + (h - yp - hp) *w);// bpl
if (d->read(temp, w) != w)
break;
char* p = (char *)data + h2*bpl;
for (int i = 0; i<bpl; ++i) {
int x = qRound((xp + i)*dScalX);
if (x<w) {
*(p + i) = *(temp + x);
}
else {
*(p + i) = *(temp + w - 1);
}
}
}
}
delete[]temp;
}
}
else if (nbits == 16 || nbits == 24 || nbits == 32) { // 16,24,32 bit BMP image
register QRgb *p;
int bpl24 = ((w*nbits + 31) / 32) * 4;
uchar *buf24 = new uchar[bpl24 + 1];
memset(buf24, 0, bpl24 + 1);
uchar *b;
int c;
qint64 pos = d->pos();
QRgb *end;
if (qFuzzyCompare(dScalX, 1) && qFuzzyCompare(dScalY, 1)) {
d->seek(pos + (h - rc.y() - h2) *bpl24);// bpl
while (--h2 >= 0) {
p = (QRgb *)(data + h2*bpl);
end = p + w2;
if (d->read((char *)buf24, bpl24) != bpl24)
break;
b = buf24;
b += rc.x()*nbits / 8;
// p += rc.x();
while (p < end) {
c = *(uchar*)b | (*(uchar*)(b + 1) << 8);
if (nbits != 16)
c |= *(uchar*)(b + 2) << 16;
*p++ = qRgb(((c & red_mask) >> red_shift) * red_scale,
((c & green_mask) >> green_shift) * green_scale,
((c & blue_mask) >> blue_shift) * blue_scale);
b += nbits / 8;
}
}
}
else {
if (0) {
//等距采样法,QT采用此方式,有一个缺点就是:缩小时未被选取的点的信息无法反映在缩小的图像上
//,放大时会出现整个小块区域像素相同,图像不清晰的,特别是缩放比例很大时。
while (--h2 >= 0) {
d->seek(pos + (h - qRound((rc.y() + h2)*dScalX))*bpl24);// bpl
p = (QRgb *)(data + h2*bpl);
end = p + w2;
if (d->read((char *)buf24, bpl24) != bpl24)
break;
int startY = qCeil((rc.y() + h2)*dScalX);
int endY = qFloor((rc.y() + h2 + 1)*dScalX);
// qDebug()<<startY<<","<<endY;
for (int i = 0; i<w2; ++i) {
int offset = qRound((rc.x() + i)*dScalX)*nbits / 8;
offset = offset >= bpl24 ? (bpl24 - 1) : offset;
{
b = buf24 + offset;
c = *(uchar*)b | (*(uchar*)(b + 1) << 8);
if (nbits != 16)
c |= *(uchar*)(b + 2) << 16;
*p++ = qRgb(((c & red_mask) >> red_shift) * red_scale,
((c & green_mask) >> green_shift) * green_scale,
((c & blue_mask) >> blue_shift) * blue_scale);
}
}
}
}
if (0)
{
// 双线性插值采样,解决上面缺点,但是速度会慢,查的数据多了啊
h2 = rc.height();//h2= h;
while (--h2 >= 0) {
int startY = qRound((rc.y() + h2)*dScalX);
int endY = qRound((rc.y() + h2 + 1)*dScalX);
std::vector<std::string> datas;
for (int i = startY; i<endY; ++i) {
d->seek(pos + (h - qRound((rc.y() + h2)*dScalX))*bpl24);
if (d->read((char *)buf24, bpl24) != bpl24)
break;
datas.push_back(std::string((char *)buf24, bpl24));
}
p = (QRgb *)(data + h2*bpl);
for (int i = 0; i<w2; ++i) {
int startX = qRound((rc.x() + i)*dScalX);
int endX = qRound((rc.x() + i + 1)*dScalX);
int total_red = 0;
int total_green = 0;
int total_blue = 0;
int totalCount = 0;
for (int j = startX; j<endX; ++j) {
int offset = j*nbits / 8;
for (int m = 0; m<datas.size(); ++m) {
const std::string& rgbs = datas.at(m);
if ((offset + 1)<rgbs.size()) {
uchar a1 = rgbs.at(offset);
uchar a2 = rgbs.at(offset + 1);
uchar a3 = rgbs.at(2);
c = (uchar)rgbs.at(offset) | ((uchar)rgbs.at(offset + 1) << 8);
if (nbits != 16) {
if ((offset + 2) >= rgbs.size())
break;
c |= (uchar)rgbs.at(offset + 2) << 16;
}
total_red += ((c & red_mask) >> red_shift) * red_scale;
total_green += ((c & green_mask) >> green_shift) * green_scale;
total_blue += ((c & blue_mask) >> blue_shift) * blue_scale;
++totalCount;
}
else {
break;
}
}
}
if (totalCount != 0) {
*p = qRgb(total_red / totalCount,
total_green / totalCount,
total_blue / totalCount);
}
++p;
}
}
}
if (1) {
//折中方案等距采样法,双线性插值采样太慢。
while (--h2 >= 0) {
d->seek(pos + (h - qRound((rc.y() + h2)*dScalX))*bpl24);// bpl
p = (QRgb *)(data + h2*bpl);
end = p + w2;
if (d->read((char *)buf24, bpl24) != bpl24)
break;
int startY = qCeil((rc.y() + h2)*dScalX);
int endY = qFloor((rc.y() + h2 + 1)*dScalX);
// qDebug()<<startY<<","<<endY;
for (int i = 0; i<w2; ++i) {
int startX = qRound((rc.x() + i)*dScalX);
int endX = qRound((rc.x() + i + 1)*dScalX);
int total_red = 0;
int total_green = 0;
int total_blue = 0;
int totalCount = 0;
for (int j = startX; j<endX; ++j) {
int offset = j*nbits / 8;
if (nbits != 16) {
if ((offset + 2) >= bpl24)
break;
}
else {
if ((offset + 1) >= bpl24)
break;
}
b = buf24 + offset;
c = *(uchar*)b | (*(uchar*)(b + 1) << 8);
if (nbits != 16)
c |= *(uchar*)(b + 2) << 16;
total_red += ((c & red_mask) >> red_shift) * red_scale;
total_green += ((c & green_mask) >> green_shift) * green_scale;
total_blue += ((c & blue_mask) >> blue_shift) * blue_scale;
++totalCount;
}
if (totalCount != 0) {
*p = qRgb(total_red / totalCount,
total_green / totalCount,
total_blue / totalCount);
}
++p;
}
}
}
}
delete[] buf24;
}
if (bi.biHeight < 0) {
// Flip the image
uchar *buf = new uchar[bpl];
h = -bi.biHeight;
for (int y = 0; y < h / 2; ++y) {
memcpy(buf, data + y*bpl, bpl);
memcpy(data + y*bpl, data + (h - y - 1)*bpl, bpl);
memcpy(data + (h - y - 1)*bpl, buf, bpl);
}
delete[] buf;
}
return true;
}
// this is also used in qmime_win.cpp
bool qt_write_dib(QDataStream &s, QImage image)
{
int nbits;
int bpl_bmp;
int bpl = image.bytesPerLine();
QIODevice* d = s.device();
if (!d->isWritable())
return false;
if (image.depth() == 8 && image.colorCount() <= 16) {
bpl_bmp = (((bpl + 1) / 2 + 3) / 4) * 4;
nbits = 4;
}
else if (image.depth() == 32) {
bpl_bmp = ((image.width() * 24 + 31) / 32) * 4;
nbits = 24;
#ifdef Q_WS_QWS
}
else if (image.depth() == 1 || image.depth() == 8) {
// Qt for Embedded Linux doesn't word align.
bpl_bmp = ((image.width()*image.depth() + 31) / 32) * 4;
nbits = image.depth();
#endif
}
else {
bpl_bmp = bpl;
nbits = image.depth();
}
BMP_INFOHDR bi;
bi.biSize = BMP_WIN; // build info header
bi.biWidth = image.width();
bi.biHeight = image.height();
bi.biPlanes = 1;
bi.biBitCount = nbits;
bi.biCompression = BMP_RGB;
bi.biSizeImage = bpl_bmp*image.height();
bi.biXPelsPerMeter = image.dotsPerMeterX() ? image.dotsPerMeterX()
: 2834; // 72 dpi default
bi.biYPelsPerMeter = image.dotsPerMeterY() ? image.dotsPerMeterY() : 2834;
bi.biClrUsed = image.colorCount();
bi.biClrImportant = image.colorCount();
s << bi; // write info header
if (s.status() != QDataStream::Ok)
return false;
if (image.depth() != 32) { // write color table
uchar *color_table = new uchar[4 * image.colorCount()];
uchar *rgb = color_table;
QVector<QRgb> c = image.colorTable();
for (int i = 0; i<image.colorCount(); i++) {
*rgb++ = qBlue(c[i]);
*rgb++ = qGreen(c[i]);
*rgb++ = qRed(c[i]);
*rgb++ = 0;
}
if (d->write((char *)color_table, 4 * image.colorCount()) == -1) {
delete[] color_table;
return false;
}
delete[] color_table;
}
if (image.format() == QImage::Format_MonoLSB)
image = image.convertToFormat(QImage::Format_Mono);
int y;
if (nbits == 1 || nbits == 8) { // direct output
#ifdef Q_WS_QWS
// Qt for Embedded Linux doesn't word align.
int pad = bpl_bmp - bpl;
char padding[4];
#endif
for (y = image.height() - 1; y >= 0; y--) {
if (d->write((char*)image.scanLine(y), bpl) == -1)
return false;
#ifdef Q_WS_QWS
if (d->write(padding, pad) == -1)
return false;
#endif
}
return true;
}
uchar *buf = new uchar[bpl_bmp];
uchar *b, *end;
register uchar *p;
memset(buf, 0, bpl_bmp);
for (y = image.height() - 1; y >= 0; y--) { // write the image bits
if (nbits == 4) { // convert 8 -> 4 bits
p = image.scanLine(y);
b = buf;
end = b + image.width() / 2;
while (b < end) {
*b++ = (*p << 4) | (*(p + 1) & 0x0f);
p += 2;
}
if (image.width() & 1)
*b = *p << 4;
}
else { // 32 bits
QRgb *p = (QRgb *)image.scanLine(y);
QRgb *end = p + image.width();
b = buf;
while (p < end) {
*b++ = qBlue(*p);
*b++ = qGreen(*p);
*b++ = qRed(*p);
p++;
}
}
if (bpl_bmp != d->write((char*)buf, bpl_bmp)) {
delete[] buf;
return false;
}
}
delete[] buf;
return true;
}
// this is also used in qmime_win.cpp
bool qt_read_dib(QDataStream &s, QImage &image)
{
BMP_INFOHDR bi;
if (!read_dib_infoheader(s, bi))
return false;
return read_dib_body(s, bi, -1, -BMP_FILEHDR_SIZE, image);
}
QBmpLoader::QBmpLoader()
: state(Error), m_device(NULL)
{
}
void QBmpLoader::setDevice(QIODevice *device)
{
this->m_device = device;
}
QIODevice* QBmpLoader::device() {
return this->m_device;
}
bool QBmpLoader::setImagePath(QString imagePath) {
this->imagePath = imagePath;
QFile* pFile = new QFile(imagePath);
pFile->open(QIODevice::ReadWrite);
this->setDevice(pFile);
return readHeader();
}
bool QBmpLoader::readHeader()
{
state = Error;
QIODevice *d = device();
QDataStream s(d);
startpos = d->pos();
qint64 size = d->size();
// Intel byte order
s.setByteOrder(QDataStream::LittleEndian);
// read BMP file header
if (!read_dib_fileheader(s, fileHeader))
return false;
// read BMP info header
if (!read_dib_infoheader(s, infoHeader))
return false;
state = ReadHeader;
return true;
}
bool QBmpLoader::read(QImage *image)
{
if (state == Error)
return false;
if (!image) {
qWarning("QBmpLoader::read: cannot read into null pointer");
return false;
}
QIODevice *d = device();
QDataStream s(d);
// Intel byte order
s.setByteOrder(QDataStream::LittleEndian);
// read image
if (!read_dib_body(s, infoHeader, fileHeader.bfOffBits, startpos, *image))
return false;
state = Ready;
return true;
}
bool QBmpLoader::read(QImage *image, const QRect& rc
, double dScalX
, double dScalY) {
if (state == Error)
return false;
if (!image) {
qWarning("QBmpLoader::read: cannot read into null pointer");
return false;
}
QIODevice *d = device();
QDataStream s(d);
// Intel byte order
s.setByteOrder(QDataStream::LittleEndian);
QRect rc2 = rc;
if (dScalX<1.0) {
int l = rc2.left();
int w = rc2.width();
rc2.setLeft(l*dScalX);
rc2.setWidth(w*dScalX);
dScalX = 1.0;
}
if (dScalY<1.0) {
int t = rc2.top();
int h = rc2.height();
rc2.setTop(t*dScalY);
rc2.setHeight(h*dScalY);
dScalY = 1.0;
}
// read image
if (!read_dib_bodyParitial(s, infoHeader, fileHeader.bfOffBits, startpos
, *image, rc2
, dScalX, dScalY))
return false;
if (image->size() != rc.size()) {
*image = image->scaled(rc.size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
}
state = Ready;
return true;
}
bool QBmpLoader::read(QImage *image, const QRect& rc) {
return read(image, rc, 1.0, 100);
}
加载图片cpp
QImageReader imageReader(url);
QImage _image = QImage(1920, 1080, QImage::Format_ARGB32);
QSize imageSize = imageReader.size();
int imageWidth = imageSize.width();
int imageHeight = imageSize.height();
int imageWidthAfterScale = 1920;
int imageHeightAfterScale = 1080;
QBmpLoader* m_bmpLoader=new QBmpLoader();
//表示是个大图,用BmpLoader进行加载
if (imageSize.width() * imageSize.height() > 4096 * 2160)
{
m_bmpLoader->setImagePath(url);
int readTimes = imageSize.width() > imageSize.height() ? imageSize.width() / 4096 :
imageSize.height() / 4096;
if (readTimes < 2)
{
readTimes = 2;
}
QImage* totalImage = new QImage(imageWidthAfterScale, imageHeightAfterScale,
QImage::Format_RGB32);
QPainter painter(totalImage);
QImage* image = new QImage(1920, 1080, QImage::Format_RGB32);
for (int i = 0; i < readTimes; ++i)
{
for (int j = 0; j < readTimes; ++j)
{
QRect rect(imageWidth / readTimes*i, imageHeight / readTimes*j, imageWidth /
readTimes, imageHeight / readTimes);
m_bmpLoader->read(image, rect);
painter.drawImage(QRect(imageWidthAfterScale / readTimes*i,
imageHeightAfterScale / readTimes*j, imageWidthAfterScale / readTimes,
imageHeightAfterScale / readTimes), image->scaled(QSize(imageWidthAfterScale
/ readTimes, imageHeightAfterScale / readTimes), Qt::IgnoreAspectRatio,
Qt::SmoothTransformation), QRect(0, 0, imageWidthAfterScale / readTimes,
imageHeightAfterScale / readTimes));
}
}
delete image;
_image = (*totalImage);
painter.end();
delete totalImage;
}
delete m_bmpLoader;
//最终结果便会保存在_image中