/* flip - pageflipper for TIFF animations * Copyright (C) 1999 Mark B. Allan (mba@reptilelabour.com) * * "flip" is free software; you can redistribute it and/or use * it and/or modify it under the terms of the "Artistic License" */ #include "TiffFlip.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "define.h" #define SWAP_THRESH 1.5f extern int e_fontS; extern const char* e_fontFace; static char errorMessage[256]; //------------------------------------------------ TiffFlip::TiffFlip(QWidget *parent, const char* name) :QWidget (parent, name) { pixlist = 0; numFrames = 0; maxFrames = 0; frame = 0; width = 0; height = 0; origWidth = 0; origHeight = 0; loadZoom = 1; displayZoom = 1; QObject::connect(&timer, SIGNAL(timeout()), SLOT(LoadController()) ); flipPID = (int)getpid(); QFont font(e_fontFace, e_fontS); memLabel = new QLabel(this); memLabel->setFont(font); memLabel->setAlignment(AlignCenter); int lH = memLabel->sizeHint().height(); int lW = memLabel->sizeHint().height()*8; memLabel->setFixedSize(lW, lH); memLabel->move(5, 5); swapLabel = new QLabel(this); swapLabel->setFont(font); swapLabel->setAlignment(AlignCenter); swapLabel->setFixedSize(lW, lH); swapLabel->move(5, 10+lH); swapThresh = SWAP_THRESH; } //------------------------------------------------ TiffFlip::~TiffFlip() { if (pixlist) { for (int i = 0; i < numFrames; i++) delete pixlist[i]; delete [] pixlist; } } //------------------------------------------------ void TiffFlip::showMemInfo(bool status) { if(status) { memLabel->show(); swapLabel->show(); } else { memLabel->hide(); swapLabel->hide(); } } //------------------------------------------------ void TiffFlip::Reset() { int i; if ( timer.isActive() ) timer.stop(); hide(); if (pixlist) { for (i = 0; i < numFrames; i++) { delete pixlist[i]; } delete [] pixlist; } pixlist = 0; numFrames = 0; maxFrames = 0; frame = 0; width = 0; height = 0; origWidth = 0; origHeight = 0; showMemInfo(true); } //------------------------------------------------ void TiffFlip::SetDisplayZoom(int z) { displayZoom = z; setFixedSize(width*displayZoom, height*displayZoom); m.reset(); m.scale((float)displayZoom, (float)displayZoom); } //------------------------------------------------ int TiffFlip::LoadImages(const char *startfile, int max, int z) { int i; int index; bool ok; QString tmpString(startfile); FILE *check; loadZoom = z; maxFrames = max; dotTif = ".tif"; index = tmpString.findRev((const char*)dotTif); if (index < 0) { dotTif = ".TIF"; index = tmpString.findRev((const char*)dotTif); if (index < 0) { dotTif = ".tiff"; index = tmpString.findRev((const char*)dotTif); if (index < 0) { dotTif = ".TIFF"; index = tmpString.findRev((const char*)dotTif); } } } if (index < 4) { sprintf (errorMessage, "Invalid filename:\n\'%s\'\n\nLooking for something like\n\'file0000.tif\', \'file0000.TIF\', etc.", startfile); QMessageBox::information(this, APP_NAME, errorMessage); return -1; } tmpString.truncate(index); baseString = (const char*)tmpString; index = tmpString.length(); index -= 4; baseString.truncate(index); tmpString.remove(0, index); QString shortName((const char*)baseString); int slash = shortName.findRev('/'); if(slash > 0) shortName.remove(0, slash+1); setCaption(shortName); startFrame = tmpString.toInt(&ok); if (!ok) { sprintf(errorMessage, "Invalid filename:\n\'%s\'\n\nFilename must have a 4 digit sequence number.\n(i.e. filename0000.tif)", startfile); QMessageBox::information(this, APP_NAME, errorMessage); return -1; } numFrames = 0; // Check that the files exist, and figure out max frames to load // There has to be a better way to do this besides opening and closing // all these files. for (i = startFrame; i < (int)startFrame+maxFrames; i++) { tmpString.sprintf("%s%04d%s", (const char*)baseString, i, (const char*)dotTif); check = fopen((const char*)tmpString, "r"); if (!check) { if (i == 0) { warning ("File \'%s\' not found. Sequence aborted.", (const char*)tmpString); return -1; } maxFrames = i-startFrame; break; } else fclose(check); } if ( ! (pixlist = new QPixmap*[maxFrames+1]) ) { OutOfMemory(); return -1; } swapThresh = SWAP_THRESH; doMemCheck = getMemUsage(&begMem, &begSwap); fprintf(stderr, "begin: mem %3.1f%%, swap: %3.1f%%\n", begMem, begSwap); showMemInfo(true); loadFrame = startFrame; timer.start(0); return startFrame; } //-------------------------------------------------- void TiffFlip::LoadController() { if (loadFrame >= startFrame+maxFrames) { LoadFinished(); } else { int frame = loadFrame-startFrame; emit LoadingFrame(frame); LoadSingleImage(); SetFrame(frame); if(frame%10 == 0) checkMem(); loadFrame++; } } //-------------------------------------------------- void TiffFlip::LoadFinished() { showMemInfo(false); timer.stop(); emit Done(startFrame, false); } //-------------------------------------------------- void TiffFlip::abortLoad() { fprintf(stderr, "load aborted.\n"); showMemInfo(false); timer.stop(); numFrames = maxFrames = (loadFrame-startFrame); emit Done(startFrame, true); } //-------------------------------------------------- void TiffFlip::LoadSingleImage() { QString tmpString; QImage image; uint32 w, h; size_t npixels; uint32* raster; uint32 r; uint32 g; uint32 b; //---------------------------------- // do the loading. Most of this code was // lifted from the libtiff docs tmpString.sprintf("%s%04d%s", (const char*)baseString, loadFrame, (const char*)dotTif); TIFF* tif = TIFFOpen((const char*)tmpString, "r"); if (tif) { TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w); TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h); origWidth = w; origHeight = h; npixels = w * h; raster = (uint32*) _TIFFmalloc(npixels * sizeof (uint32)); if (raster != NULL) { if (TIFFReadRGBAImage(tif, w, h, raster, 0)) { switch(loadZoom) { case 0: //-- Load at 200% width = w*2; height = h*2; if ( image.create(width, height, 32) ) { for (uint32 y = 0; y < h; y++) for (uint32 x = 0; x < w; x++) { //if ( image.setPixel( (x*2+0), (h*2-1)-(y*2+0), raster[(y*w)+x]); image.setPixel( (x*2+1), (h*2-1)-(y*2+0), raster[(y*w)+x]); image.setPixel( (x*2+0), (h*2-1)-(y*2+1), raster[(y*w)+x]); image.setPixel( (x*2+1), (h*2-1)-(y*2+1), raster[(y*w)+x]); } } else { OutOfMemory(); return; } break; case 1: //-- Load at 100% width = w; height = h; if (image.create(width, height, 32) ) { for (uint32 y = 0; y < h; y++) for (uint32 x = 0; x < w; x++) image.setPixel(x, (h-1)-y, raster[(y*w)+x]); } else { OutOfMemory(); return; } break; case 2: //-- Load at 50% width = w/2; height = h/2; if ( image.create(width, height, 32) ) { for (uint32 y = 0; y < h; y+=2) { for (uint32 x = 0; x < w; x+=2) { r = TIFFGetR(raster[( (y+0)*w)+(x+0) ]) + TIFFGetR(raster[( (y+0)*w)+(x+1) ]) + TIFFGetR(raster[( (y+1)*w)+(x+0) ]) + TIFFGetR(raster[( (y+1)*w)+(x+1) ]); g = TIFFGetG(raster[( (y+0)*w)+(x+0) ]) + TIFFGetG(raster[( (y+0)*w)+(x+1) ]) + TIFFGetG(raster[( (y+1)*w)+(x+0) ]) + TIFFGetG(raster[( (y+1)*w)+(x+1) ]); b = TIFFGetB(raster[( (y+0)*w)+(x+0) ]) + TIFFGetB(raster[( (y+0)*w)+(x+1) ]) + TIFFGetB(raster[( (y+1)*w)+(x+0) ]) + TIFFGetB(raster[( (y+1)*w)+(x+1) ]); r = r/4; g = g/4; b = b/4; g = g << 8; b = b << 16; image.setPixel(x/2, ((h/2)-1)-(y/2), r|g|b); } } } else { OutOfMemory(); return; } break; case 3: //-- Load at 33% width = w/3; height = h/3; if ( image.create(width, height, 32) ) { for (uint32 y = 0; y < h; y+=3) { for (uint32 x = 0; x < w; x+=3) { r = TIFFGetR(raster[( (y+0)*w)+(x+0) ]) + TIFFGetR(raster[( (y+0)*w)+(x+1) ]) + TIFFGetR(raster[( (y+0)*w)+(x+2) ]) + TIFFGetR(raster[( (y+1)*w)+(x+0) ]) + TIFFGetR(raster[( (y+1)*w)+(x+1) ]) + TIFFGetR(raster[( (y+1)*w)+(x+2) ]) + TIFFGetR(raster[( (y+2)*w)+(x+0) ]) + TIFFGetR(raster[( (y+2)*w)+(x+1) ]) + TIFFGetR(raster[( (y+2)*w)+(x+2) ]); g = TIFFGetG(raster[( (y+0)*w)+(x+0) ]) + TIFFGetG(raster[( (y+0)*w)+(x+1) ]) + TIFFGetG(raster[( (y+0)*w)+(x+2) ]) + TIFFGetG(raster[( (y+1)*w)+(x+0) ]) + TIFFGetG(raster[( (y+1)*w)+(x+1) ]) + TIFFGetG(raster[( (y+1)*w)+(x+2) ]) + TIFFGetG(raster[( (y+2)*w)+(x+0) ]) + TIFFGetG(raster[( (y+2)*w)+(x+1) ]) + TIFFGetG(raster[( (y+2)*w)+(x+2) ]); b = TIFFGetB(raster[( (y+0)*w)+(x+0) ]) + TIFFGetB(raster[( (y+0)*w)+(x+1) ]) + TIFFGetB(raster[( (y+0)*w)+(x+2) ]) + TIFFGetB(raster[( (y+1)*w)+(x+0) ]) + TIFFGetB(raster[( (y+1)*w)+(x+1) ]) + TIFFGetB(raster[( (y+1)*w)+(x+2) ]) + TIFFGetB(raster[( (y+2)*w)+(x+0) ]) + TIFFGetB(raster[( (y+2)*w)+(x+1) ]) + TIFFGetB(raster[( (y+2)*w)+(x+2) ]); r = r/9; g = g/9; b = b/9; g = g << 8; b = b << 16; image.setPixel(x/3, ((h/3)-1)-(y/3), r|g|b); } } } else { OutOfMemory(); return; } break; case 4: //-- Load at 25% width = w/4; height = h/4; if ( image.create(width, height, 32) ) { for (uint32 y = 0; y < h; y+=4) { for (uint32 x = 0; x < w; x+=4) { r = TIFFGetR(raster[( (y+0)*w)+(x+0) ]) + TIFFGetR(raster[( (y+0)*w)+(x+1) ]) + TIFFGetR(raster[( (y+0)*w)+(x+2) ]) + TIFFGetR(raster[( (y+0)*w)+(x+3) ]) + TIFFGetR(raster[( (y+1)*w)+(x+0) ]) + TIFFGetR(raster[( (y+1)*w)+(x+1) ]) + TIFFGetR(raster[( (y+1)*w)+(x+2) ]) + TIFFGetR(raster[( (y+1)*w)+(x+3) ]) + TIFFGetR(raster[( (y+2)*w)+(x+0) ]) + TIFFGetR(raster[( (y+2)*w)+(x+1) ]) + TIFFGetR(raster[( (y+2)*w)+(x+2) ]) + TIFFGetR(raster[( (y+2)*w)+(x+3) ]) + TIFFGetR(raster[( (y+3)*w)+(x+0) ]) + TIFFGetR(raster[( (y+3)*w)+(x+1) ]) + TIFFGetR(raster[( (y+3)*w)+(x+2) ]) + TIFFGetR(raster[( (y+3)*w)+(x+3) ]); g = TIFFGetG(raster[( (y+0)*w)+(x+0) ]) + TIFFGetG(raster[( (y+0)*w)+(x+1) ]) + TIFFGetG(raster[( (y+0)*w)+(x+2) ]) + TIFFGetG(raster[( (y+0)*w)+(x+3) ]) + TIFFGetG(raster[( (y+1)*w)+(x+0) ]) + TIFFGetG(raster[( (y+1)*w)+(x+1) ]) + TIFFGetG(raster[( (y+1)*w)+(x+2) ]) + TIFFGetG(raster[( (y+1)*w)+(x+3) ]) + TIFFGetG(raster[( (y+2)*w)+(x+0) ]) + TIFFGetG(raster[( (y+2)*w)+(x+1) ]) + TIFFGetG(raster[( (y+2)*w)+(x+2) ]) + TIFFGetG(raster[( (y+2)*w)+(x+3) ]) + TIFFGetG(raster[( (y+3)*w)+(x+0) ]) + TIFFGetG(raster[( (y+3)*w)+(x+1) ]) + TIFFGetG(raster[( (y+3)*w)+(x+2) ]) + TIFFGetG(raster[( (y+3)*w)+(x+3) ]); b = TIFFGetB(raster[( (y+0)*w)+(x+0) ]) + TIFFGetB(raster[( (y+0)*w)+(x+1) ]) + TIFFGetB(raster[( (y+0)*w)+(x+2) ]) + TIFFGetB(raster[( (y+0)*w)+(x+3) ]) + TIFFGetB(raster[( (y+1)*w)+(x+0) ]) + TIFFGetB(raster[( (y+1)*w)+(x+1) ]) + TIFFGetB(raster[( (y+1)*w)+(x+2) ]) + TIFFGetB(raster[( (y+1)*w)+(x+3) ]) + TIFFGetB(raster[( (y+2)*w)+(x+0) ]) + TIFFGetB(raster[( (y+2)*w)+(x+1) ]) + TIFFGetB(raster[( (y+2)*w)+(x+2) ]) + TIFFGetB(raster[( (y+2)*w)+(x+3) ]) + TIFFGetB(raster[( (y+3)*w)+(x+0) ]) + TIFFGetB(raster[( (y+3)*w)+(x+1) ]) + TIFFGetB(raster[( (y+3)*w)+(x+2) ]) + TIFFGetB(raster[( (y+3)*w)+(x+3) ]); r = r/16; g = g/16; b = b/16; g = g << 8; b = b << 16; image.setPixel(x/4, ((h/4)-1)-(y/4), r|g|b); } } } else { OutOfMemory(); return; } break; default: //-- If we get a bad zoom factor, go //-- ahead and load at 100% warning ("How the hell did I get to TiffFlip::LoadSingleImage() with a zoom of %d?", loadZoom); width = w; height = h; if ( image.create(width, height, 32) ) { for (uint32 y = 0; y < h; y++) for (uint32 x = 0; x < w; x++) image.setPixel(x, (h-1)-y, raster[(y*w)+x]); break; } else OutOfMemory(); } } else { abortLoad(); sprintf(errorMessage, "TIFFReadRGBAImage(tif, w, h, raster, 0)\nfailed at frame %d.\n\nSequence truncated to %d frames.", loadFrame-startFrame, maxFrames); QMessageBox::warning(this, APP_NAME, errorMessage); } _TIFFfree(raster); } else { OutOfMemory(); return; } TIFFClose(tif); if ( ! (pixlist[loadFrame-startFrame] = new QPixmap) ) { OutOfMemory(); return; } pixlist[loadFrame-startFrame]->setOptimization(QPixmap::NoOptim); // if ( ! pixlist[loadFrame-startFrame]->convertFromImage(image) ) if ( ! pixlist[loadFrame-startFrame]->convertFromImage(image.swapRGB()) ) { OutOfMemory(); return; } image.reset(); numFrames++; frame = loadFrame-startFrame; drawPixmap(); //-------------------------- // if first time in loop, set the size of window // and show it //-------------------------- if (loadFrame == startFrame) { setFixedSize(width*displayZoom, height*displayZoom); show(); } } else { abortLoad(); sprintf (errorMessage, "Error reading TIFF file\n\"%s\"\n\nSequence truncated to %d frames.", (const char*)tmpString, maxFrames); QMessageBox::warning(this, APP_NAME, errorMessage); } //Fix this to take zoom values } //------------------------------------------------ void TiffFlip::OutOfMemory() { timer.stop(); sprintf (errorMessage, "ERROR: Could not allocate enough memory.\n(memory exhausted at frame %d)\n\nLoad Aborted", loadFrame-startFrame); Reset(); // free memory QMessageBox::critical(this, APP_NAME, errorMessage); emit Done(-1, true); } //------------------------------------------------ void TiffFlip::SetFrame(int in) { frame = in; drawPixmap(); } //------------------------------------------------ void TiffFlip::drawPixmap() { static QPixmap tempPix; if (numFrames > 0 && frame >= 0 && frame < numFrames) { if (displayZoom > 1) { tempPix = pixlist[frame]->xForm(m); bitBlt(this, 0, 0, &tempPix, 0, 0, -1, -1); //bitBlt(this, 0, 0, &(pixlist[frame]->xForm(m)), 0, 0, -1, -1); } else bitBlt(this, 0, 0, pixlist[frame], 0, 0, -1, -1); frame ++; if (frame >= numFrames) frame = 0; } } //------------------------------------------------ void TiffFlip::paintEvent(QPaintEvent * ) { drawPixmap(); } //------------------------------------------------ bool TiffFlip::checkMem() { bool retVal = getMemUsage(&curMem, &curSwap); if(retVal) { memString.sprintf("mem: %3.1f%%", curMem); swapString.sprintf("swap: %3.1f%%", curSwap); memLabel->setText(memString); swapLabel->setText(swapString); } if(curSwap > begSwap) { float diff = curSwap-begSwap; if(diff > swapThresh) { timer.stop(); QString mssg; mssg.sprintf( "WARNING:\nswap file useage has increased by %3.1f%%.\n", diff); int but = QMessageBox::warning(this, APP_NAME, mssg, QMessageBox::Abort, QMessageBox::Ignore); if(but == QMessageBox::Ignore) { swapThresh += SWAP_THRESH; timer.start(0); } else { abortLoad(); } } } return retVal; } /** * get percentage of memory and swap used */ //------------------------------------------------ bool TiffFlip::getMemUsage(float* mem, float* swap) { int c = 0; FILE *meminfo; uint swap_total = 16; uint swap_used = 0; uint swap_free = 16; uint mem_total = 16; uint mem_used = 0; uint mem_free = 16; uint mem_share = 0; uint mem_buff = 0; uint mem_cache = 0; meminfo = fopen("/proc/meminfo", "r"); if(meminfo) { char buffer[512]; while(fgets(buffer, 512, meminfo) != NULL) { if( strncmp(buffer, "Swap:", 5) == 0 ) { sscanf(buffer, "Swap: %u %u %u", &swap_total, &swap_used, &swap_free); c++; } else if( strncmp(buffer, "Mem:", 4) == 0 ) { sscanf(buffer, "Mem: %u %u %u %u %u %u", &mem_total, &mem_used, &mem_free, &mem_share, &mem_buff, &mem_cache); c++; } } fclose(meminfo); } float mb = 1024.0*1024.0; float mTotal = (float)mem_total/mb; float mUsed = (float)(mem_used-(mem_share+mem_buff+mem_cache))/mb; *mem = 100.0f*(mUsed/mTotal); float sTotal = (float)swap_total/mb; float sUsed = (float)swap_used/mb; *swap = 100.0f*(sUsed/sTotal); if(c == 2) return true; else return false; } /** * useless. Memory is allocated in X, not this process. Doh. */ //------------------------------------------------ void TiffFlip::checkProcessMemory() { FILE* file = 0; char filename[128]; char statline[1024]; uint vsize; snprintf(filename, 128, "/proc/%d/stat", flipPID); file = fopen(filename, "r"); fgets(statline, 1024, file); fclose(file); statline[1023] = '\0'; sscanf(statline, "%*d %*s %*c %*d " // pid, comm, state, ppid "%*d %*d %*d %*d " // pgrp, session, tty, tpgid "%*u %*u %*u %*u " // flags, minflt, cminflt, majflt "%*u %*d %*d %*d " // cmajflt, utime, stime, cutime "%*d %*d %*d %*u " // cstime, counter, priority, timeout "%*u %*d %u %*u " // itrealvalue, stattime, vsize, rss "%*u %*u %*u %*u " // rlim, startcode, endcode, startstack "%*u %*u %*d %*d " // kstkesp, kstkeip, signal, blocked "%*d %*d %*u", // sigignore, sigcatch, wchan &vsize); }