SilentEye 0.4.1

modules/seformatbmp/imagebmp.cpp

Go to the documentation of this file.
00001 //  This file is part of SilentEye.
00002 //
00003 //  SilentEye is free software: you can redistribute it and/or modify
00004 //  it under the terms of the GNU General Public License as published by
00005 //  the Free Software Foundation, either version 3 of the License, or
00006 //  (at your option) any later version.
00007 //
00008 //  SilentEye is distributed in the hope that it will be useful,
00009 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00010 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011 //  GNU General Public License for more details.
00012 //
00013 //  You should have received a copy of the GNU General Public License
00014 //  along with SilentEye.  If not, see <http://www.gnu.org/licenses/>.
00015 
00016 #include "imagebmp.h"
00017 #include "moduleexception.h"
00018 #include <math.h>
00019 
00020 namespace SEFormatBMP {
00021 
00022     ImageBMP::ImageBMP()
00023         : Image()
00024     {
00025         m_nbBits = 2;
00026         setColorUsed(true, false, false);
00027         m_headerPosition = TOP;
00028         m_distribution = INLINE;
00029         setObjectName("ImageBMP");
00030         m_logger = new Logger(this);
00031     }
00032 
00033     ImageBMP::ImageBMP(const QString& filePath)
00034         : Image(filePath)
00035     {
00036         m_img = this->toImage();
00037         m_nbBits = 2;
00038         setColorUsed(true, false, false);
00039         m_headerPosition = TOP;
00040         m_distribution = INLINE;
00041         computeNewFileName("bmp");
00042         setObjectName("ImageBMP");
00043         m_logger = new Logger(this);
00044     }
00045 
00046     ImageBMP::ImageBMP(const QPixmap& pixmap, QString filePath)
00047         : Image(pixmap, filePath)
00048     {
00049         m_img = this->toImage();
00050         m_nbBits = 2;
00051         setColorUsed(true, false, false);
00052         m_headerPosition = TOP;
00053         m_distribution = INLINE;
00054         computeNewFileName("bmp");
00055         setObjectName("ImageBMP");
00056         m_logger = new Logger(this);
00057     }
00058 
00059     ImageBMP::ImageBMP(const Image& img)
00060         : Image(img)
00061     {
00062         m_img = this->toImage();
00063         m_nbBits = 2;
00064         setColorUsed(true, false, false);
00065         m_headerPosition = TOP;
00066         m_distribution = INLINE;
00067         computeNewFileName("bmp");
00068         setObjectName("ImageBMP");
00069         m_logger = new Logger(this);
00070     }
00071 
00072     ImageBMP::ImageBMP(Image* img)
00073         : Image(img)
00074     {
00075         m_nbBits = 2;
00076         setColorUsed(true, false, false);
00077         m_headerPosition = TOP;
00078         m_distribution = INLINE;
00079         computeNewFileName("bmp");
00080         setObjectName("ImageBMP");
00081         m_logger = new Logger(this);
00082     }
00083 
00084     ImageBMP::~ImageBMP()
00085     {
00086         delete m_logger;
00087     }
00088 
00089     unsigned short int ImageBMP::nbBits() const{
00090         return m_nbBits;
00091     }
00092 
00093     void ImageBMP::setColorUsed(bool r, bool g, bool b)
00094     {
00095         m_useRed = r;
00096         m_useGreen = g;
00097         m_useBlue = b;
00098         m_nbColorUsed = 0;
00099         if (m_useRed)
00100             m_nbColorUsed++;
00101         if (m_useGreen)
00102             m_nbColorUsed++;
00103         if (m_useBlue)
00104             m_nbColorUsed++;
00105     }
00106 
00107     void ImageBMP::setDistribution(DataDistribution value)
00108     {
00109         m_distribution = value;
00110     }
00111 
00112     void ImageBMP::setHeaderPosition(HeaderPosition value)
00113     {
00114         m_headerPosition = value;
00115     }
00116 
00117     bool ImageBMP::setNbBits(const unsigned short int nb)
00118     {
00119         if(nb>0 && nb<=6)
00120         {
00121             m_nbBits = nb;
00122             return true;
00123         }
00124         else
00125             m_nbBits = 2;
00126 
00127         m_isLoaded = false;
00128         return m_isLoaded;
00129     }
00130 
00131     quint32 ImageBMP::capacity() const
00132     {
00133         int sizeNbPixel = ceil(32.0/(m_nbColorUsed*m_nbBits));
00134         int nbPixelAvailable = (imgWidth() * imgHeight()) - sizeNbPixel;
00135         return floor((m_nbBits * m_nbColorUsed * nbPixelAvailable) / 8.0);
00136     }
00137 
00138     bool ImageBMP::loadData()
00139     {
00140         m_isLoaded = false;
00141         EncodedData dataSize(Data::UINT32);
00142         dataSize.initialize(m_nbBits);
00143 
00144         if (m_data.isNull())
00145             m_data = new EncodedData();
00146         else
00147             m_data->clear();
00148 
00149         m_data->initialize(m_nbBits);
00150         int x=0, y=0;
00151 
00152         QSize imgSize = m_img.size();
00153         unsigned short int andOperator = EncodedData::andOperator(m_nbBits);
00154 
00155         QPoint* headerPos = computeHeaderPosition();
00156         x = headerPos[0].x();
00157         y = headerPos[0].y();
00158 
00159         int nbBitsRead = 0;
00160         while(x<imgSize.width() && y<imgSize.height() && nbBitsRead<32)
00161         {
00162             QRgb pixel = m_img.pixel(x, y);
00163 
00164             int val;
00165             if (m_useRed && nbBitsRead<32) {
00166                 val = (qRed(pixel))&andOperator;
00167                 dataSize.append(val);
00168                 nbBitsRead += m_nbBits;
00169             }
00170             if (m_useGreen && nbBitsRead<32) {
00171                 val = (qGreen(pixel))&andOperator;
00172                 dataSize.append(val);
00173                 nbBitsRead += m_nbBits;
00174             }
00175             if (m_useBlue && nbBitsRead<32) {
00176                 val = (qBlue(pixel))&andOperator;
00177                 dataSize.append(val);
00178                 nbBitsRead += m_nbBits;
00179             }
00180 
00181             // change cursor to the next pixel
00182             if(x==imgSize.width()-1)
00183                 y++;
00184             x=(x+1)%imgSize.width();
00185         }
00186         quint32 nbOctets = dataSize.toUInt32();
00187 
00188         m_logger->debug("loaded size: " + QString::number(nbOctets));
00189 
00190         if(m_headerPosition != TOP)
00191         {
00192             x = 0;
00193             y = 0;
00194         }
00195 
00196         if (nbOctets > capacity())
00197             return false;
00198 
00199         int step = computeDistributionStep(nbOctets);
00200         QPoint newPos = computeNewPosition(QPoint(x, y), step, true);
00201         x = newPos.x();
00202         y = newPos.y();
00203 
00204         m_logger->debug("first loaded pixel(step: " + QString::number(step) + "): " 
00205                         + QString::number(x) + ":" + QString::number(y));
00206 
00207         int nbBits = nbOctets*8;
00208         nbBitsRead = 0;
00209         while(x<imgSize.width() && y<imgSize.height() &&
00210               nbBitsRead < nbBits)
00211         {
00212             if(isBetweenPoint(QPoint(x, y), headerPos[0], headerPos[1]))
00213             {
00214                 // skip header block
00215                 m_logger->debug("skip: " + QString::number(x) + "," + QString::number(y));
00216                 QPoint pos = computeNewPosition(headerPos[1], 1);
00217                 x = pos.x();
00218                 y = pos.y();
00219                 continue;
00220             }
00221             else
00222             {
00223                 QRgb pixel = m_img.pixel(x, y);
00224 
00225                 int val = 0;
00226                 if (m_useRed && nbBitsRead<nbBits) {
00227                     val = (qRed(pixel))&andOperator;
00228                     m_data->append(val);
00229                     nbBitsRead += m_nbBits;
00230                 }
00231                 if (m_useGreen && nbBitsRead<nbBits) {
00232                     val = (qGreen(pixel))&andOperator;
00233                     m_data->append(val);
00234                     nbBitsRead += m_nbBits;
00235                 }
00236                 if (m_useBlue && nbBitsRead<nbBits) {
00237                     val = (qBlue(pixel))&andOperator;
00238                     m_data->append(val);
00239                     nbBitsRead += m_nbBits;
00240                 }
00241             }
00242 
00243             // change cursor to the next pixel
00244             if(nbBitsRead < nbBits)
00245             {
00246                 QPoint newPos = computeNewPosition(QPoint(x, y), step);
00247                 x = newPos.x();
00248                 y = newPos.y();
00249             }
00250         }
00251 
00252         m_logger->debug("last loaded pixel: "
00253                         + QString::number(x) + ":" + QString::number(y));
00254         delete(headerPos);
00255 
00256         m_isLoaded = m_data->size()>0;
00257         return m_isLoaded;
00258     }
00259 
00260     bool ImageBMP::hideData()
00261     {
00262         int x=0, y=0;
00263         QSize imgSize = m_img.size();
00264         m_swap = 255 - EncodedData::andOperator(m_nbBits);
00265 
00266         if (m_data.isNull())
00267             throw ModuleException("Technical error during encoding process",
00268                                   "Cannot insert null data into image");
00269         if (m_img.isNull())
00270             throw ModuleException("Technical error during encoding process",
00271                                   "Cannot insert data into an empty image");
00272 
00273         EncodedData sizeData(m_data->size());
00274         m_logger->debug("Setted size: " + QString::number(sizeData.toUInt32()) 
00275                         + "/" + QString::number(capacity()));
00276 
00277         QPoint* headerPos = computeHeaderPosition();
00278         x = headerPos[0].x();
00279         y = headerPos[0].y();
00280 
00281         int nbColorUsed = 0;
00282         sizeData.initialize(m_nbBits);
00283         while(sizeData.hasNext() && x<imgSize.width() && y<imgSize.height())
00284         {
00285             QRgb pixel = m_img.pixel(x, y);
00286 
00287             int val = sizeData.read();
00288 
00289             if (m_useRed && nbColorUsed == 0) {
00290                 int red = changeColor(qRed(pixel), val);
00291                 m_img.setPixel(x, y, qRgb(red, qGreen(pixel), qBlue(pixel)));
00292                 nbColorUsed++;
00293             } else if (m_useGreen &&
00294                        ((nbColorUsed == 0 && !m_useRed)
00295                         || (m_useRed && nbColorUsed == 1))) {
00296                 int green = changeColor(qGreen(pixel), val);
00297                 m_img.setPixel(x, y, qRgb(qRed(pixel), green, qBlue(pixel)));
00298                 nbColorUsed++;
00299             } else if (m_useBlue) {
00300                 int blue = changeColor(qBlue(pixel), val);
00301                 m_img.setPixel(x, y, qRgb(qRed(pixel), qGreen(pixel), blue));
00302                 nbColorUsed++;
00303             }
00304 
00305             // change cursor to the next pixel
00306             if(nbColorUsed == m_nbColorUsed || !sizeData.hasNext())
00307             {
00308                 nbColorUsed = 0;
00309                 if(x==imgSize.width()-1)
00310                     y++;
00311                 x=(x+1)%imgSize.width();
00312             }
00313         }
00314 
00315         if(m_headerPosition != TOP)
00316         {
00317             x = 0;
00318             y = 0;
00319         }
00320 
00321         int step = computeDistributionStep(m_data->size());
00322         QPoint newPos = computeNewPosition(QPoint(x, y), step, true);
00323         x = newPos.x();
00324         y = newPos.y();
00325 
00326         m_logger->debug("first setted pixel(step: " + QString::number(step) + "): " 
00327                         + QString::number(x) + ":" + QString::number(y));
00328 
00329         m_data->initialize(m_nbBits);
00330         while(m_data->hasNext() && x<imgSize.width() && y<imgSize.height())
00331         {
00332 
00333             if(isBetweenPoint(QPoint(x, y), headerPos[0], headerPos[1]))
00334             {
00335                 // skip header block
00336                 m_logger->debug("skip: " + QString::number(x) + "," + QString::number(y));
00337                 QPoint pos = computeNewPosition(headerPos[1], 1);
00338                 x = pos.x();
00339                 y = pos.y();
00340                 continue;
00341             }
00342             else
00343             {
00344                 QRgb pixel = m_img.pixel(x, y);
00345                 int val = m_data->read();
00346 
00347                 /*  nbRead++;
00348                     QString debug = "compileData[size:" + QString::number(m_data->size())
00349                     + ",nbBits:" + QString::number(m_nbBits) + "]: ";
00350                     debug += "nbRead: " + QString::number(nbRead) + ", ";
00351                     debug += "coord: " + QString::number(x) +":"+ QString::number(y) + ", ";
00352                     debug += "val: " + QString::number(val) + ", ";
00353                     debug += "hasNext: " + QString::number(m_data->hasNext()) + ", "; */
00354 
00355                 if (m_useRed && nbColorUsed == 0) {
00356                     //debug += "use of red, ";
00357                     int red = changeColor(qRed(pixel), val);
00358                     m_img.setPixel(x, y, qRgb(red, qGreen(pixel), qBlue(pixel)));
00359                     nbColorUsed++;
00360                 } else if (m_useGreen &&
00361                            ((nbColorUsed == 0 && !m_useRed)
00362                             || (m_useRed && nbColorUsed == 1))) {
00363                     //debug += "use of green, ";
00364                     int green = changeColor(qGreen(pixel), val);
00365                     m_img.setPixel(x, y, qRgb(qRed(pixel), green, qBlue(pixel)));
00366                     nbColorUsed++;
00367                 } else if (m_useBlue) {
00368                     //debug += "use of blue, ";
00369                     int blue = changeColor(qBlue(pixel), val);
00370                     m_img.setPixel(x, y, qRgb(qRed(pixel), qGreen(pixel), blue));
00371                     nbColorUsed++;
00372                 }
00373                 // m_logger->debug(debug + "nbColor: " + QString::number(nbColorUsed));
00374             }
00375 
00376             // change cursor to the next pixel
00377             if(nbColorUsed == m_nbColorUsed && m_data->hasNext())
00378             {
00379                 nbColorUsed = 0;
00380                 QPoint newPos = computeNewPosition(QPoint(x, y), step);
00381                 x = newPos.x();
00382                 y = newPos.y();
00383             }
00384         }
00385 
00386         delete(headerPos);
00387 
00388         if (x>=imgSize.width() || y>=imgSize.height())
00389         {
00390             m_logger->warning("Data too large (" + QString::number(x) + ":" + QString::number(y) + ")");
00391             return false;
00392         }
00393 
00394         m_logger->debug("last setted pixel: "
00395                         + QString::number(x) + ":" + QString::number(y));
00396         return true;
00397     }
00398 
00399     bool ImageBMP::saveToDir(QString& outputDirPath)
00400     {
00401         if(!hideData())
00402             return false;
00403 
00404         if (!outputDirPath.endsWith('/'))
00405         {
00406             outputDirPath += "/";
00407         }
00408         m_filePath = outputDirPath+m_shortName;
00409 
00410         if(!m_img.isNull())
00411         {
00412             return m_img.save(m_filePath, "BMP", 100);
00413         }
00414         else
00415             m_logger->warning("Cannot save file: image is null.");
00416 
00417         return false;
00418     }
00419 
00420     int ImageBMP::changeColor(int color, const int val)
00421     {
00422         return (color&m_swap)+val;
00423     }
00424 
00425     int ImageBMP::computeDistributionStep(quint32 size)
00426     {
00427         int step = 1;
00428         if(m_distribution == EQUI)
00429         {
00430             int sizeNbPixel = ceil(32.0/(m_nbColorUsed*m_nbBits));
00431             int nbPixelAvailable = (imgWidth() * imgHeight()) - sizeNbPixel;
00432             int nbPixelData = ceil((size*8.0)/(m_nbColorUsed*m_nbBits));
00433 
00434             step = floor(((double)nbPixelAvailable) / nbPixelData);
00435 
00436             if(step <= 0)
00437             {
00438                 m_logger->debug("computed step was 0 => set 1");
00439                 step = 1;
00440             }
00441             else if(step >= EQUI_NB_STEP_MIN)
00442             {
00443 
00444                 int squareLength = floor(sqrt(step));
00445                 if (imgWidth() < imgHeight())
00446                 {
00447                     double ratio = ((double)imgWidth()) / imgHeight();
00448                     ratio *= 1/(ratio*2);
00449                     m_blockWidth = ceil(squareLength*ratio);
00450                     m_blockHeight = ceil(m_blockWidth/ratio);
00451                 }
00452                 else
00453                 {
00454                     double ratio = ((double)imgHeight()) / imgWidth();
00455                     ratio *= 1/(ratio*2);
00456                     m_blockHeight = ceil(squareLength*ratio);
00457                     m_blockWidth = ceil(m_blockHeight/ratio);
00458                 }
00459 
00460                 int nbBlockMaxSquare = floor(floor((double)imgHeight()/(m_blockHeight)) * floor((double)imgWidth()/(m_blockWidth)));
00461                 if(nbBlockMaxSquare < nbPixelData)
00462                 {
00463                     m_logger->error("Distribution step failed ! (" + QString::number(nbPixelData) + "/" + QString::number(nbBlockMaxSquare) + ")");
00464                 }
00465 
00466                 int posX = floor(m_blockWidth/2.0);
00467                 int posY = floor(m_blockHeight/2.0);
00468                 if (posX <= posY)
00469                 {
00470                     m_blockInnerPos = posX;
00471                 }
00472                 else
00473                 {
00474                     m_blockInnerPos = posY;
00475                 }
00476 
00477                 m_logger->debug("BlockWidth: " + QString::number(m_blockWidth) + ", "
00478                                 + "BlockHeight: " + QString::number(m_blockHeight) + ", "
00479                                 + "BlockInnerPos: " + QString::number(m_blockInnerPos) + ", "
00480                                 + "DataNbPixel: " + QString::number(nbPixelData) + ", "
00481                                 + "NbBlockMax: " + QString::number(nbBlockMaxSquare));
00482             }
00483         }
00484         return step;
00485     }
00486 
00487     QPoint* ImageBMP::computeHeaderPosition()
00488     {  
00489         int headNbPixel = ceil(32.0 / (m_nbColorUsed * m_nbBits));
00490 
00491         int headStartX = 0;
00492         int headStartY = 0;
00493 
00494         if(m_headerPosition == BOTTOM)
00495         {
00496             headStartX = (abs(headNbPixel - imgWidth())) % imgWidth();
00497             headStartY = imgHeight() - ceil((headStartX + headNbPixel - 1) / (double)imgWidth());
00498         }
00499         else if(m_headerPosition == SIGNATURE)
00500         {
00501             headStartX = floor(imgWidth() * 0.95);
00502             headStartY = floor(imgHeight() * 0.95);
00503             if (((imgWidth() - headStartX) + (imgHeight() - headStartY)*imgWidth()) < headNbPixel)
00504                 throw ModuleException("Technical error during loading process",
00505                                       "Image too small for use of signature mode (header position)");
00506         }
00507 
00508         int headEndX = (headStartX + headNbPixel - 1) % imgWidth();
00509         int headEndY = headStartY + floor((headStartX + headNbPixel - 1) / (double)imgWidth());
00510 
00511         m_logger->debug("headNbPixel: " + QString::number(headNbPixel)
00512                         + ", headStartX: " + QString::number(headStartX)
00513                         + ", headStartY: " + QString::number(headStartY)
00514                         + ", headEndX: " + QString::number(headEndX)
00515                         + ", headEndY: " + QString::number(headEndY));
00516 
00517         QPoint* tab = new QPoint[2];
00518         tab[0] = QPoint(headStartX, headStartY);
00519         tab[1] = QPoint(headEndX, headEndY);
00520 
00521         return tab;
00522     }
00523 
00524     bool ImageBMP::isBetweenPoint(const QPoint& ref, const QPoint& start, const QPoint& end)
00525     {
00526         if(start.y() == end.y()) // singleline
00527         {
00528             return ref.y() == start.y() && ref.x() >= start.x() && ref.x() <= end.x();
00529         }
00530         else// multiline
00531         {
00532             return (ref.y() == start.y() && ref.x() >= start.x())
00533                     || (ref.y() == end.y() && ref.x() <= end.x())
00534                     || (ref.y() > start.y() && ref.y() < end.y());
00535         }
00536     }
00537 
00538     QPoint ImageBMP::computeNewPosition(const QPoint& oldPos, int step, bool first)
00539     {
00540         QPoint pos(oldPos);
00541         if(first)
00542         {
00543             if (step >= EQUI_NB_STEP_MIN)
00544             {
00545                 pos.setX(m_blockInnerPos);
00546                 pos.setY(m_blockInnerPos);
00547             }
00548             return pos;
00549         }
00550 
00551         int newX = 0;
00552         if(step >= EQUI_NB_STEP_MIN)
00553         {
00554             newX = oldPos.x() + m_blockWidth;
00555         }
00556         else
00557         {
00558             newX = oldPos.x() + step;
00559         }
00560 
00561         if(newX >= imgWidth())
00562         {
00563             if(step >= EQUI_NB_STEP_MIN)
00564             {
00565                 pos.ry() += m_blockHeight;
00566             }
00567             else
00568             {
00569                 pos.ry() += floor(newX / (double)imgWidth());
00570             }
00571         }
00572         pos.setX(newX % imgWidth());
00573 
00574         //m_logger->debug("new position: " + QString::number(pos.x()) + ":" + QString::number(pos.y()));
00575 
00576         return pos;
00577     }
00578 
00579 }