SilentEye 0.4.1
|
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 }