📄 qjpeghandler.cpp
字号:
// this code. (also only scales down and probably won't scale to a larger size)
uchar *in = inData;
uchar *out = outData + outputLine*out_bpl;
for (uint i=0; i<cinfo.output_width; i++) {
out[sWidth * i / cinfo.output_width] = in[i];
}
}
}
(void) jpeg_finish_decompress(&cinfo);
}
#ifndef QT_NO_IMAGE_SMOOTHSCALE
} else if (scaledSize.isValid()) {
jpegSmoothScaler scaler(&cinfo, QString().sprintf("Scale( %d, %d, ScaleFree )",
scaledSize.width(),
scaledSize.height()).toLatin1().data());
*outImage = scaler.scale();
#endif
} else {
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(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(cinfo.output_width, cinfo.output_height, 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()) {
uchar* data = outImage->bits();
int bpl = outImage->bytesPerLine();
while (cinfo.output_scanline < cinfo.output_height) {
uchar *d = data + cinfo.output_scanline * bpl;
(void) jpeg_read_scanlines(&cinfo,
&d,
1);
}
(void) jpeg_finish_decompress(&cinfo);
if (cinfo.output_components == 3) {
// Expand 24->32 bpp.
for (uint j=0; j<cinfo.output_height; j++) {
uchar *in = outImage->scanLine(j) + cinfo.output_width * 3;
QRgb *out = (QRgb*)outImage->scanLine(j);
for (uint i=cinfo.output_width; i--;) {
in-=3;
out[i] = qRgb(in[0], in[1], in[2]);
}
}
} else if (cinfo.out_color_space == JCS_CMYK) {
for (uint j = 0; j < cinfo.output_height; ++j) {
uchar *in = outImage->scanLine(j) + cinfo.output_width * 4;
QRgb *out = (QRgb*)outImage->scanLine(j);
for (uint i = cinfo.output_width; i--; ) {
in-=4;
int k = in[3];
out[i] = qRgb(k * in[0] / 255, k * in[1] / 255, k * in[2] / 255);
}
}
}
if (cinfo.density_unit == 1) {
outImage->setDotsPerMeterX(int(100. * cinfo.X_density / 2.54));
outImage->setDotsPerMeterY(int(100. * cinfo.Y_density / 2.54));
} else if (cinfo.density_unit == 2) {
outImage->setDotsPerMeterX(int(100. * cinfo.X_density));
outImage->setDotsPerMeterY(int(100. * cinfo.Y_density));
}
}
}
}
jpeg_destroy_decompress(&cinfo);
delete iod_src;
return !outImage->isNull();
}
struct my_jpeg_destination_mgr : public jpeg_destination_mgr {
// Nothing dynamic - cannot rely on destruction over longjump
QIODevice *device;
JOCTET buffer[max_buf];
public:
my_jpeg_destination_mgr(QIODevice *);
};
#if defined(Q_C_CALLBACKS)
extern "C" {
#endif
static void qt_init_destination(j_compress_ptr)
{
}
static boolean qt_empty_output_buffer(j_compress_ptr cinfo)
{
my_jpeg_destination_mgr* dest = (my_jpeg_destination_mgr*)cinfo->dest;
int written = dest->device->write((char*)dest->buffer, max_buf);
if (written == -1)
(*cinfo->err->error_exit)((j_common_ptr)cinfo);
dest->next_output_byte = dest->buffer;
dest->free_in_buffer = max_buf;
#if defined(Q_OS_UNIXWARE)
return B_TRUE;
#else
return true;
#endif
}
static void qt_term_destination(j_compress_ptr cinfo)
{
my_jpeg_destination_mgr* dest = (my_jpeg_destination_mgr*)cinfo->dest;
qint64 n = max_buf - dest->free_in_buffer;
qint64 written = dest->device->write((char*)dest->buffer, n);
if (written == -1)
(*cinfo->err->error_exit)((j_common_ptr)cinfo);
}
#if defined(Q_C_CALLBACKS)
}
#endif
inline my_jpeg_destination_mgr::my_jpeg_destination_mgr(QIODevice *device)
{
jpeg_destination_mgr::init_destination = qt_init_destination;
jpeg_destination_mgr::empty_output_buffer = qt_empty_output_buffer;
jpeg_destination_mgr::term_destination = qt_term_destination;
this->device = device;
next_output_byte = buffer;
free_in_buffer = max_buf;
}
static bool write_jpeg_image(const QImage &sourceImage, QIODevice *device, int sourceQuality)
{
bool success = false;
const QImage image = sourceImage;
const QVector<QRgb> cmap = image.colorTable();
struct jpeg_compress_struct cinfo;
JSAMPROW row_pointer[1];
row_pointer[0] = 0;
struct my_jpeg_destination_mgr *iod_dest = new my_jpeg_destination_mgr(device);
struct my_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jerr.error_exit = my_error_exit;
if (!setjmp(jerr.setjmp_buffer)) {
// WARNING:
// this if loop is inside a setjmp/longjmp branch
// do not create C++ temporaries here because the destructor may never be called
// if you allocate memory, make sure that you can free it (row_pointer[0])
jpeg_create_compress(&cinfo);
cinfo.dest = iod_dest;
cinfo.image_width = image.width();
cinfo.image_height = image.height();
bool gray=false;
switch (image.depth()) {
case 1:
case 8:
gray = true;
for (int i = image.numColors(); gray && i--;) {
gray = gray & (qRed(cmap[i]) == qGreen(cmap[i]) &&
qRed(cmap[i]) == qBlue(cmap[i]));
}
cinfo.input_components = gray ? 1 : 3;
cinfo.in_color_space = gray ? JCS_GRAYSCALE : JCS_RGB;
break;
case 32:
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
}
jpeg_set_defaults(&cinfo);
qreal diffInch = qAbs(image.dotsPerMeterX()*2.54/100. - qRound(image.dotsPerMeterX()*2.54/100.))
+ qAbs(image.dotsPerMeterY()*2.54/100. - qRound(image.dotsPerMeterY()*2.54/100.));
qreal diffCm = (qAbs(image.dotsPerMeterX()/100. - qRound(image.dotsPerMeterX()/100.))
+ qAbs(image.dotsPerMeterY()/100. - qRound(image.dotsPerMeterY()/100.)))*2.54;
if (diffInch < diffCm) {
cinfo.density_unit = 1; // dots/inch
cinfo.X_density = qRound(image.dotsPerMeterX()*2.54/100.);
cinfo.Y_density = qRound(image.dotsPerMeterY()*2.54/100.);
} else {
cinfo.density_unit = 2; // dots/cm
cinfo.X_density = (image.dotsPerMeterX()+50) / 100;
cinfo.Y_density = (image.dotsPerMeterY()+50) / 100;
}
int quality = sourceQuality >= 0 ? qMin(sourceQuality,100) : 75;
#if defined(Q_OS_UNIXWARE)
jpeg_set_quality(&cinfo, quality, B_TRUE /* limit to baseline-JPEG values */);
jpeg_start_compress(&cinfo, B_TRUE);
#else
jpeg_set_quality(&cinfo, quality, true /* limit to baseline-JPEG values */);
jpeg_start_compress(&cinfo, true);
#endif
row_pointer[0] = new uchar[cinfo.image_width*cinfo.input_components];
int w = cinfo.image_width;
while (cinfo.next_scanline < cinfo.image_height) {
uchar *row = row_pointer[0];
switch (image.depth()) {
case 1:
if (gray) {
const uchar* data = image.scanLine(cinfo.next_scanline);
if (image.format() == QImage::Format_MonoLSB) {
for (int i=0; i<w; i++) {
bool bit = !!(*(data + (i >> 3)) & (1 << (i & 7)));
row[i] = qRed(cmap[bit]);
}
} else {
for (int i=0; i<w; i++) {
bool bit = !!(*(data + (i >> 3)) & (1 << (7 -(i & 7))));
row[i] = qRed(cmap[bit]);
}
}
} else {
const uchar* data = image.scanLine(cinfo.next_scanline);
if (image.format() == QImage::Format_MonoLSB) {
for (int i=0; i<w; i++) {
bool bit = !!(*(data + (i >> 3)) & (1 << (i & 7)));
*row++ = qRed(cmap[bit]);
*row++ = qGreen(cmap[bit]);
*row++ = qBlue(cmap[bit]);
}
} else {
for (int i=0; i<w; i++) {
bool bit = !!(*(data + (i >> 3)) & (1 << (7 -(i & 7))));
*row++ = qRed(cmap[bit]);
*row++ = qGreen(cmap[bit]);
*row++ = qBlue(cmap[bit]);
}
}
}
break;
case 8:
if (gray) {
const uchar* pix = image.scanLine(cinfo.next_scanline);
for (int i=0; i<w; i++) {
*row = qRed(cmap[*pix]);
++row; ++pix;
}
} else {
const uchar* pix = image.scanLine(cinfo.next_scanline);
for (int i=0; i<w; i++) {
*row++ = qRed(cmap[*pix]);
*row++ = qGreen(cmap[*pix]);
*row++ = qBlue(cmap[*pix]);
++pix;
}
}
break;
case 32: {
QRgb* rgb = (QRgb*)image.scanLine(cinfo.next_scanline);
for (int i=0; i<w; i++) {
*row++ = qRed(*rgb);
*row++ = qGreen(*rgb);
*row++ = qBlue(*rgb);
++rgb;
}
}
}
jpeg_write_scanlines(&cinfo, row_pointer, 1);
}
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
success = true;
} else {
jpeg_destroy_compress(&cinfo);
success = false;
}
delete iod_dest;
delete [] row_pointer[0];
return success;
}
QJpegHandler::QJpegHandler()
{
quality = 75;
}
bool QJpegHandler::canRead() const
{
if (canRead(device())) {
setFormat("jpeg");
return true;
}
return false;
}
bool QJpegHandler::canRead(QIODevice *device)
{
if (!device) {
qWarning("QJpegHandler::canRead() called with no device");
return false;
}
return device->peek(2) == "\xFF\xD8";
}
bool QJpegHandler::read(QImage *image)
{
if (!canRead())
return false;
return read_jpeg_image(device(), image, parameters, scaledSize, quality);
}
bool QJpegHandler::write(const QImage &image)
{
return write_jpeg_image(image, device(), quality);
}
bool QJpegHandler::supportsOption(ImageOption option) const
{
return option == Quality
#ifndef QT_NO_IMAGE_SMOOTHSCALE
|| option == ScaledSize
#endif
|| option == Size;
}
QVariant QJpegHandler::option(ImageOption option) const
{
if (option == Quality) {
return quality;
#ifndef QT_NO_IMAGE_SMOOTHSCALE
} else if (option == ScaledSize) {
return scaledSize;
#endif
} else if (option == Size) {
if (canRead() && !device()->isSequential()) {
qint64 pos = device()->pos();
QImage image;
read_jpeg_image(device(), &image, "GetHeaderInformation", scaledSize, quality);
device()->seek(pos);
return image.size();
}
}
return QVariant();
}
void QJpegHandler::setOption(ImageOption option, const QVariant &value)
{
if (option == Quality)
quality = value.toInt();
#ifndef QT_NO_IMAGE_SMOOTHSCALE
else if ( option == ScaledSize )
scaledSize = value.toSize();
#endif
}
QByteArray QJpegHandler::name() const
{
return "jpeg";
}
QT_END_NAMESPACE
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -