📄 qjpeghandler.cpp
字号:
b += fraccolleft * qBlue( *xP ) * qAlpha( *xP ) / 255;
} else {
r += fraccolleft * qRed( *xP );
g += fraccolleft * qGreen( *xP );
b += fraccolleft * qBlue( *xP );
}
fraccoltofill -= fraccolleft;
}
}
if ( fraccoltofill > 0 ) {
--xP;
if (as) {
a += fraccolleft * qAlpha( *xP );
r += fraccoltofill * qRed( *xP ) * qAlpha( *xP ) / 255;
g += fraccoltofill * qGreen( *xP ) * qAlpha( *xP ) / 255;
b += fraccoltofill * qBlue( *xP ) * qAlpha( *xP ) / 255;
if ( a ) {
r = r * 255 / a * SCALE;
g = g * 255 / a * SCALE;
b = b * 255 / a * SCALE;
}
} else {
r += fraccoltofill * qRed( *xP );
g += fraccoltofill * qGreen( *xP );
b += fraccoltofill * qBlue( *xP );
}
}
if ( ! needcol ) {
r /= SCALE;
if ( r > maxval ) r = maxval;
g /= SCALE;
if ( g > maxval ) g = maxval;
b /= SCALE;
if ( b > maxval ) b = maxval;
if (as) {
a /= SCALE;
if ( a > maxval ) a = maxval;
*nxP = qRgba( (int)r, (int)g, (int)b, (int)a );
} else {
*nxP = qRgb( (int)r, (int)g, (int)b );
}
}
}
}
if ( d->newrows != d->rows && tempxelrow )// Robust, tempxelrow might be 0 1 day
delete [] tempxelrow;
if ( as ) // Avoid purify complaint
delete [] as;
if ( rs ) // Robust, rs might be 0 one day
delete [] rs;
if ( gs ) // Robust, gs might be 0 one day
delete [] gs;
if ( bs ) // Robust, bs might be 0 one day
delete [] bs;
return dst;
}
class jpegSmoothScaler : public QImageSmoothScaler
{
public:
jpegSmoothScaler(struct jpeg_decompress_struct *info, const char *params):
QImageSmoothScaler(info->output_width, info->output_height, params)
{
cinfo = info;
cols24Bit = scaledWidth() * 3;
cacheHeight = 1;
imageCache = QImage( info->output_width, cacheHeight, QImage::Format_RGB32 );
}
private:
int cols24Bit;
QImage imageCache;
int cacheHeight;
struct jpeg_decompress_struct *cinfo;
QRgb *scanLine(const int line = 0, const QImage *src = 0)
{
QRgb *out;
uchar *in;
Q_UNUSED(line);
Q_UNUSED(src);
uchar* data = imageCache.bits();
jpeg_read_scanlines(cinfo, &data, 1);
out = (QRgb*)imageCache.scanLine(0);
//
// The smooth scale algorithm only works on 32-bit images;
// convert from (8|24) bits to 32.
//
if (cinfo->output_components == 1) {
in = (uchar*)out + scaledWidth();
for (uint i = scaledWidth(); i--; ) {
in--;
out[i] = qRgb(*in, *in, *in);
}
} else {
in = (uchar*)out + cols24Bit;
for (uint i = scaledWidth(); i--; ) {
in -= 3;
out[i] = qRgb(in[0], in[1], in[2]);
}
}
return out;
}
};
#endif
struct my_error_mgr : public jpeg_error_mgr {
jmp_buf setjmp_buffer;
};
#if defined(Q_C_CALLBACKS)
extern "C" {
#endif
static void my_error_exit (j_common_ptr cinfo)
{
my_error_mgr* myerr = (my_error_mgr*) cinfo->err;
char buffer[JMSG_LENGTH_MAX];
(*cinfo->err->format_message)(cinfo, buffer);
qWarning("%s", buffer);
longjmp(myerr->setjmp_buffer, 1);
}
#if defined(Q_C_CALLBACKS)
}
#endif
static const int max_buf = 4096;
struct my_jpeg_source_mgr : public jpeg_source_mgr {
// Nothing dynamic - cannot rely on destruction over longjump
QIODevice *device;
JOCTET buffer[max_buf];
public:
my_jpeg_source_mgr(QIODevice *device);
};
#if defined(Q_C_CALLBACKS)
extern "C" {
#endif
static void qt_init_source(j_decompress_ptr)
{
}
static boolean qt_fill_input_buffer(j_decompress_ptr cinfo)
{
int num_read;
my_jpeg_source_mgr* src = (my_jpeg_source_mgr*)cinfo->src;
src->next_input_byte = src->buffer;
num_read = src->device->read((char*)src->buffer, max_buf);
if (num_read <= 0) {
// Insert a fake EOI marker - as per jpeglib recommendation
src->buffer[0] = (JOCTET) 0xFF;
src->buffer[1] = (JOCTET) JPEG_EOI;
src->bytes_in_buffer = 2;
} else {
src->bytes_in_buffer = num_read;
}
#if defined(Q_OS_UNIXWARE)
return B_TRUE;
#else
return true;
#endif
}
static void qt_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
{
my_jpeg_source_mgr* src = (my_jpeg_source_mgr*)cinfo->src;
// `dumb' implementation from jpeglib
/* Just a dumb implementation for now. Could use fseek() except
* it doesn't work on pipes. Not clear that being smart is worth
* any trouble anyway --- large skips are infrequent.
*/
if (num_bytes > 0) {
while (num_bytes > (long) src->bytes_in_buffer) {
num_bytes -= (long) src->bytes_in_buffer;
(void) qt_fill_input_buffer(cinfo);
/* note we assume that qt_fill_input_buffer will never return false,
* so suspension need not be handled.
*/
}
src->next_input_byte += (size_t) num_bytes;
src->bytes_in_buffer -= (size_t) num_bytes;
}
}
static void qt_term_source(j_decompress_ptr cinfo)
{
my_jpeg_source_mgr* src = (my_jpeg_source_mgr*)cinfo->src;
if (!src->device->isSequential())
src->device->seek(src->device->pos() - src->bytes_in_buffer);
}
#if defined(Q_C_CALLBACKS)
}
#endif
inline my_jpeg_source_mgr::my_jpeg_source_mgr(QIODevice *device)
{
jpeg_source_mgr::init_source = qt_init_source;
jpeg_source_mgr::fill_input_buffer = qt_fill_input_buffer;
jpeg_source_mgr::skip_input_data = qt_skip_input_data;
jpeg_source_mgr::resync_to_restart = jpeg_resync_to_restart;
jpeg_source_mgr::term_source = qt_term_source;
this->device = device;
bytes_in_buffer = 0;
next_input_byte = buffer;
}
static void scaleSize(int &reqW, int &reqH, int imgW, int imgH, Qt::AspectRatioMode mode)
{
if (mode == Qt::IgnoreAspectRatio)
return;
int t1 = imgW * reqH;
int t2 = reqW * imgH;
if ((mode == Qt::KeepAspectRatio && (t1 > t2)) || (mode == Qt::KeepAspectRatioByExpanding && (t1 < t2)))
reqH = t2 / imgW;
else
reqW = t1 / imgH;
}
#define HIGH_QUALITY_THRESHOLD 50
static bool read_jpeg_image(QIODevice *device, QImage *outImage, const QByteArray ¶meters, QSize scaledSize, int inQuality )
{
#ifdef QT_NO_IMAGE_SMOOTHSCALE
Q_UNUSED( scaledSize );
#endif
struct jpeg_decompress_struct cinfo;
struct my_jpeg_source_mgr *iod_src = new my_jpeg_source_mgr(device);
struct my_error_mgr jerr;
jpeg_create_decompress(&cinfo);
cinfo.src = iod_src;
cinfo.err = jpeg_std_error(&jerr);
jerr.error_exit = my_error_exit;
if (!setjmp(jerr.setjmp_buffer)) {
#if defined(Q_OS_UNIXWARE)
(void) jpeg_read_header(&cinfo, B_TRUE);
#else
(void) jpeg_read_header(&cinfo, true);
#endif
// -1 means default quality.
int quality = inQuality;
if (quality < 0)
quality = 75;
QString params = QString::fromLatin1(parameters);
params.simplified();
int sWidth = 0, sHeight = 0;
char sModeStr[1024] = "";
Qt::AspectRatioMode sMode;
#ifndef QT_NO_IMAGE_SMOOTHSCALE
// If high quality not required, shrink image during decompression
if (scaledSize.isValid() && quality < HIGH_QUALITY_THRESHOLD && !params.contains(QLatin1String("GetHeaderInformation")) ) {
cinfo.scale_denom = qMin(cinfo.image_width / scaledSize.width(),
cinfo.image_width / scaledSize.height());
if (cinfo.scale_denom < 2) {
cinfo.scale_denom = 1;
} else if (cinfo.scale_denom < 4) {
cinfo.scale_denom = 2;
} else if (cinfo.scale_denom < 8) {
cinfo.scale_denom = 4;
} else {
cinfo.scale_denom = 8;
}
cinfo.scale_num = 1;
}
#endif
// If high quality not required, use fast decompression
if( quality < HIGH_QUALITY_THRESHOLD ) {
cinfo.dct_method = JDCT_IFAST;
cinfo.do_fancy_upsampling = FALSE;
}
(void) jpeg_start_decompress(&cinfo);
if (params.contains(QLatin1String("GetHeaderInformation"))) {
// Create QImage but don't read the pixels
static uchar dummy[1];
if (cinfo.output_components == 3 || cinfo.output_components == 4) {
if (outImage->size() != QSize(cinfo.output_width, cinfo.output_height)
|| outImage->format() != QImage::Format_RGB32) {
*outImage = QImage(dummy, cinfo.output_width, cinfo.output_height, QImage::Format_RGB32);
}
} else if (cinfo.output_components == 1) {
if (outImage->size() != QSize(cinfo.output_width, cinfo.output_height)
|| outImage->format() != QImage::Format_Indexed8) {
*outImage = QImage(dummy, cinfo.output_width, cinfo.output_height, QImage::Format_Indexed8);
}
} else {
// Unsupported format
return false;
}
} else if (params.contains(QLatin1String("Scale"))) {
#if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(Q_OS_WINCE)
sscanf_s(params.toLatin1().data(), "Scale(%i, %i, %1023s)",
&sWidth, &sHeight, sModeStr, sizeof(sModeStr));
#else
sscanf(params.toLatin1().data(), "Scale(%i, %i, %1023s)",
&sWidth, &sHeight, sModeStr);
#endif
QString sModeQStr(QString::fromLatin1(sModeStr));
if (sModeQStr == QLatin1String("IgnoreAspectRatio")) {
sMode = Qt::IgnoreAspectRatio;
} else if (sModeQStr == QLatin1String("KeepAspectRatio")) {
sMode = Qt::KeepAspectRatio;
} else if (sModeQStr == QLatin1String("KeepAspectRatioByExpanding")) {
sMode = Qt::KeepAspectRatioByExpanding;
} else {
qDebug("read_jpeg_image: invalid aspect ratio mode \"%s\", see QImage::AspectRatioMode documentation", sModeStr);
sMode = Qt::KeepAspectRatio;
}
// qDebug("Parameters ask to scale the image to %i x %i AspectRatioMode: %s", sWidth, sHeight, sModeStr);
scaleSize(sWidth, sHeight, cinfo.output_width, cinfo.output_height, sMode);
// qDebug("Scaling the jpeg to %i x %i", sWidth, sHeight, sModeStr);
if (cinfo.output_components == 3 || cinfo.output_components == 4) {
if (outImage->size() != QSize(sWidth, sHeight) || outImage->format() != QImage::Format_RGB32)
*outImage = QImage(sWidth, sHeight, QImage::Format_RGB32);
} else if (cinfo.output_components == 1) {
if (outImage->size() != QSize(sWidth, sHeight) || outImage->format() != QImage::Format_Indexed8)
*outImage = QImage(sWidth, sHeight, QImage::Format_Indexed8);
outImage->setNumColors(256);
for (int i=0; i<256; i++)
outImage->setColor(i, qRgb(i,i,i));
} else {
// Unsupported format
}
if (outImage->isNull()) {
(void) jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
delete iod_src;
return false;
}
if (!outImage->isNull()) {
QImage tmpImage(cinfo.output_width, 1, QImage::Format_RGB32);
uchar* inData = tmpImage.bits();
uchar* outData = outImage->bits();
int out_bpl = outImage->bytesPerLine();
while (cinfo.output_scanline < cinfo.output_height) {
int outputLine = sHeight * cinfo.output_scanline / cinfo.output_height;
(void) jpeg_read_scanlines(&cinfo, &inData, 1);
if (cinfo.output_components == 3) {
uchar *in = inData;
QRgb *out = (QRgb*)outData + outputLine * out_bpl;
for (uint i=0; i<cinfo.output_width; i++) {
// ### Only scaling down an image works, I don't think scaling up will work at the moment
// ### An idea I have to make this a smooth scale is to progressively add the pixel values up
// When scaling down, multiple values are being over drawn in to the output buffer.
// Instead, a weighting based on the distance the line or pixel is from the output pixel determines
// the weight of it when added to the output buffer. At present it is a non-smooth scale which is
// inefficently implemented, it still uncompresses all the jpeg, an optimization for progressive
// jpegs could be made if scaling by say 50% or some other special cases
out[sWidth * i / cinfo.output_width] = qRgb(in[0], in[1], in[2]);
in += 3;
}
} else {
// ### Need to test the case where the jpeg is grayscale, need some black and white jpegs to test
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -