/*************************************************************************** ctiffhandler.cpp - description ------------------- begin : Thu Aug 29 2002 copyright : (C) 2002-2006 by Serghei Amelian email : serghei.amelian@gmail.com ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include "ctiffhandler.h" extern QLabel *resolution; static bool cancel, ignoreAll; // ##### Fast smooth-scaling for monocrome images ######## #define GetBWPixel(y, x) \ (*(ptr_to_row[y] + (x >> 3)) & (1 << ((~x) & 7))) ? 0 : 255; static QImage smooth_scale_mono(const QImage &src, int w, int h) { QImage dst(w, h, 8, 256); for(int i = 0; i < 256; i++) dst.setColor(i, qRgb(i, i, i)); int *tx_array = new int[w + 1]; for(int i = 0; i < w + 1; i++) tx_array[i] = i * src.width() / w; int *ty_array = new int[h + 1]; for(int i = 0; i < h + 1; i++) ty_array[i] = i * src.height() / h; const unsigned char **ptr_to_row = new const unsigned char*[src.height()]; for(int i = 0, j = 0; i < src.height(); i++, j++) ptr_to_row[j] = src.scanLine(i); for(int y = 0; y < h; y++) { unsigned char *p = dst.scanLine(y); for(int x = 0; x < w; x++) { int accum = 0; int total = 0; int tx1 = tx_array[x]; int ty1 = ty_array[y]; int tx2 = tx_array[x + 1]; int ty2 = ty_array[y + 1]; register int ty = ty1; do { for(register int tx = tx1; tx < tx2; tx++) { accum += GetBWPixel(ty, tx); //accum += src.pixelIndex(tx, ty) ? 0 : 255; total++; } ty++; } while(ty < ty2); if(total > 0) *p = accum / total; p++; } } delete [] tx_array; delete [] ty_array; delete [] ptr_to_row; return dst; } // ####################################################### void CImageObject::rotate(float degrees, bool absolute) { QWMatrix rm; rm.rotate(degrees); pixmap = pixmap.xForm(rm); if(!absolute) this->degrees += degrees; if(this->degrees >= 360.0) this->degrees = (int)this->degrees % 360; } void CImageObject::scale(float zoom, bool smoothScale) { if(this->zoom == zoom && this->smoothScale == smoothScale && this->degrees == degrees && !pixmap.isNull()) return; float ratio = ((xres && yres) ? xres / yres : 1.); int w = (int)(image.width() * zoom); int h = (int)(image.height() * zoom * ratio); QImage img; if(1. == zoom && 1.0 == ratio) img = image; else if(1. != zoom && smoothScale) if(1 == image.depth()) img = smooth_scale_mono(image, w, h); else img = image.smoothScale(w, h); else img = image.scale(w, h); if(degrees != 0) { QWMatrix rm; rm.rotate(degrees); img = img.xForm(rm); } pixmap.convertFromImage(img); this->zoom = zoom; this->smoothScale = smoothScale; return; } int CImageObject::realHeight() { if(xres != yres) return int(image.height() * xres / yres); return image.height(); } bool CImageObject::save(const QString &filename, const QString &format) { if(xres != yres) return save(filename, format, 1., true); return image.save(filename, format); } bool CImageObject::save(const QString &filename, const QString &format, float zoom, bool smoothScale) { scale(zoom, smoothScale); return pixmap.save(filename, format); } CTIFFHandler::CTIFFHandler(QLabel *status, QWidget *parent, const char *name) : QObject(parent, name), status(status), tif(NULL) { _page = _pages = 0; _zoom = 1.0; _smoothScale = false; buffer.setAutoDelete(true); _currentPage = QPixmap(); _sender = ""; _cidName = ""; _cidNumber = ""; _time = ""; TIFFSetErrorHandler(errorHandler); } CTIFFHandler::~CTIFFHandler() { } int CTIFFHandler::load(const QString &fileName, float zoom) { cancel = ignoreAll = false; _filename = fileName; _zoom = zoom; TIFF *tif; // open tif file if(!(tif = TIFFOpen(fileName.utf8(), "r"))) return -1; // test type of tiff file uint16 bitspersample; TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bitspersample); uint16 samplesPerPixel; TIFFGetField(tif, 277, &samplesPerPixel); // clear previous pixmaps if(_pages) { buffer.clear(); _page = _pages = 0; _currentPage = QPixmap(); } QApplication::setOverrideCursor(waitCursor); // sender char *sender; if(TIFFGetField(tif, TIFFTAG_IMAGEDESCRIPTION, &sender)) { _sender = QString(sender).section('\n', 0, 0); _cidName = QString(sender).section('\n', 1, 1); _cidNumber = QString(sender).section('\n', 2, 2); } else { _sender = QString::null; _cidName = QString::null; _cidNumber = QString::null; } // time char *time; if(TIFFGetField(tif, TIFFTAG_DATETIME, &time)) _time = time; else _time = QString::null; this->tif = tif; // read pages do { status->setText(tr("Loading page ") + QString::number(_pages + 1) + "..."); qApp->processEvents(); getPage(); _pages++; } while(!cancel && TIFFReadDirectory(tif)); if(cancel) { close(); } else { setPage(1); emit update(); } QApplication::restoreOverrideCursor(); return cancel ? -1 : 0; } int CTIFFHandler::getPage() { // get width and height of page uint32 width, height; TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width); TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height); // page resolutions float xres, yres; if(!TIFFGetField(tif, TIFFTAG_XRESOLUTION, &xres)) xres = 0.0; if(!TIFFGetField(tif, TIFFTAG_YRESOLUTION, &yres)) yres = 0.0; // bits / sample uint16 bitsPerSample; if(!TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bitsPerSample)) bitsPerSample = 0; QImage image; if(1 == bitsPerSample) { if(!image.create(width, height, 1, 2, QImage::BigEndian)) return -1; // photometric interpretation short pmi = 0; TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &pmi); image.setColor(0, qRgb(0xFF, 0xFF, 0xFF)); image.setColor(1, qRgb(0x00, 0x00, 0x00)); // read lines uchar **lines = image.jumpTable(); for (uint row = 0; row < height; row++) { if(TIFFReadScanline(tif, lines[row], row, 0) < 0) if(cancel) return -1; } if(pmi) image.invertPixels(); } else { if(!image.create(width, height, 32)) return -1; //TIFFReadRGBAImageOriented(tif, width, height, (uint32*)image.bits(), ORIENTATION_TOPLEFT); // compatibility with old libtiff TIFFReadRGBAImage(tif, width, height, (uint32*)image.bits()); image = image.mirror(); image = image.swapRGB(); } // append image to list buffer.append(new CImageObject(image, xres, yres)); return 0; } void CTIFFHandler::error(const char *strerr, const char *reason) { QMessageBox::critical((QWidget*)this->parent(), tr("QFaxReader Error"), tr("Error: ") + QString(strerr) + ".\nReason: " + QString(reason) + "."); } void CTIFFHandler::close() { if(!tif) return; TIFFClose(tif); tif = NULL; _page = _pages = 0; _currentPage = QPixmap(); buffer.clear(); emit update(); } void CTIFFHandler::setSmoothScale(bool activate) { _smoothScale = activate; if (_pages) scale(_zoom); } bool CTIFFHandler::smoothScale() { return _smoothScale; } void CTIFFHandler::setPage(int page) { _page = page; scale(_zoom); } void CTIFFHandler::rotate(double degrees) { beginProcessing(tr("Rotating image...")); buffer.at(_page - 1)->rotate(degrees); doneProcessing(); } void CTIFFHandler::scale(float zoomFactor) { _zoom = zoomFactor; if(buffer.count()) { beginProcessing(tr("Scaling image...")); buffer.at(_page - 1)->scale(zoomFactor, _smoothScale); doneProcessing(); }; } bool CTIFFHandler::save(const QString &prefix, const QString &format, bool scaled) { QString ext = format == "JPEG" ? QString("jpg") : format.lower(); QApplication::setOverrideCursor(waitCursor); lastTextForStatus = status->text(); int i; CImageObject *img; bool ok = true; for(i = 1, img = buffer.first(); img && ok; i++, img = buffer.next()) { QString filename; filename.sprintf("%s-%03d.%s", prefix.utf8().data(), i, ext.upper().utf8().data()); status->setText(tr("Writing ") + QFileInfo(filename.latin1()).fileName() + "..."); qApp->processEvents(); if(scaled) ok = img->save(filename, format, _zoom, _smoothScale); else ok = img->save(filename, format); } int errNumber = errno; status->setText(lastTextForStatus); QApplication::restoreOverrideCursor(); if(!ok) error(tr("export failed"), strerror(errNumber)); return ok; } QImage CTIFFHandler::image(int page) { if(page >= _pages || page < 0) { error(tr("getting page failed"), tr("Invalid page number")); return 0; } else return buffer.at(page)->getImage(); } void CTIFFHandler::beginProcessing(const QString &textForStatus) { QApplication::setOverrideCursor(waitCursor); lastTextForStatus = status->text(); status->setText(textForStatus); qApp->processEvents(); } void CTIFFHandler::doneProcessing() { _currentPage = buffer.at(_page - 1)->getPixmap(); QString res; if(buffer.at(_page - 1)->yres) res = QString::number((int)buffer.at(_page - 1)->xres) + "x" + QString::number((int)buffer.at(_page - 1)->yres); else res = tr("NA"); resolution->setText(tr("Resolution") + ": " + res); status->setText(lastTextForStatus); QApplication::restoreOverrideCursor(); emit update(); } void CTIFFHandler::errorHandler(const char*, const char *fmt, va_list ap) { if(ignoreAll) return; char buffer[256]; vsnprintf(buffer, sizeof(buffer), fmt, ap); bool wait = (bool)QApplication::overrideCursor(); QApplication::restoreOverrideCursor(); switch(QMessageBox::warning(0, tr("QFaxReader Error"), buffer, tr("Cancel"), tr("Ignore"), tr("Ignore all"))) { case 0: cancel = true; break; case 2: ignoreAll = true; break; } if(wait) QApplication::setOverrideCursor(waitCursor); }